Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero shutter lag #583

Merged
merged 2 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions apps/libcamera_still.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ static void event_loop(LibcameraStillApp &app)
std::this_thread::sleep_for(10ms);
}
}
else if (options->zsl)
app.ConfigureZsl();
else
app.ConfigureViewfinder();
app.StartCamera();
Expand All @@ -209,6 +211,7 @@ static void event_loop(LibcameraStillApp &app)
} af_wait_state = AF_WAIT_NONE;
int af_wait_timeout = 0;

bool want_capture = false;
for (unsigned int count = 0;; count++)
{
LibcameraApp::Msg msg = app.Wait();
Expand All @@ -235,7 +238,7 @@ static void event_loop(LibcameraStillApp &app)
// In viewfinder mode, run until the timeout or keypress. When that happens,
// if the "--autofocus-on-capture" option was set, trigger an AF scan and wait
// for it to complete. Then switch to capture mode if an output was requested.
if (app.ViewfinderStream())
if (app.ViewfinderStream() && !want_capture)
{
LOG(2, "Viewfinder frame " << count);
timelapse_frames++;
Expand All @@ -244,7 +247,6 @@ static void event_loop(LibcameraStillApp &app)
bool timelapse_timed_out = options->timelapse &&
(now - timelapse_time) > options->timelapse.value &&
timelapse_frames >= TIMELAPSE_MIN_FRAMES;
bool want_capture = false;

if (af_wait_state != AF_WAIT_NONE)
{
Expand Down Expand Up @@ -281,43 +283,53 @@ static void event_loop(LibcameraStillApp &app)
keypressed = false;
af_wait_state = AF_WAIT_NONE;
timelapse_time = std::chrono::high_resolution_clock::now();
app.StopCamera();
app.Teardown();
app.ConfigureStill(still_flags);
if (!options->zsl)
{
app.StopCamera();
app.Teardown();
app.ConfigureStill(still_flags);
}
if (options->af_on_capture)
{
libcamera::ControlList cl;
cl.set(libcamera::controls::AfMode, libcamera::controls::AfModeAuto);
cl.set(libcamera::controls::AfTrigger, libcamera::controls::AfTriggerCancel);
app.SetControls(cl);
}
app.StartCamera();
if (!options->zsl)
app.StartCamera();
}
else
app.ShowPreview(completed_request, app.ViewfinderStream());
}
// In still capture mode, save a jpeg. Go back to viewfinder if in timelapse mode,
// otherwise quit.
else if (app.StillStream())
else if (app.StillStream() && want_capture)
{
app.StopCamera();
want_capture = false;
if (!options->zsl)
app.StopCamera();
LOG(1, "Still capture image received");
save_images(app, completed_request);
if (!options->metadata.empty())
save_metadata(options, completed_request->metadata);
timelapse_frames = 0;
if (!options->immediate && (options->timelapse || options->signal || options->keypress))
{
app.Teardown();
app.ConfigureViewfinder();
if (!options->zsl)
{
app.Teardown();
app.ConfigureViewfinder();
}
if (options->af_on_capture && options->afMode_index == -1)
{
libcamera::ControlList cl;
cl.set(libcamera::controls::AfMode, libcamera::controls::AfModeAuto);
cl.set(libcamera::controls::AfTrigger, libcamera::controls::AfTriggerCancel);
app.SetControls(cl);
}
app.StartCamera();
if (!options->zsl)
app.StartCamera();
af_wait_state = AF_WAIT_NONE;
}
else
Expand Down
95 changes: 95 additions & 0 deletions core/libcamera_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,101 @@ void LibcameraApp::ConfigureViewfinder()
LOG(2, "Viewfinder setup complete");
}

void LibcameraApp::ConfigureZsl(unsigned int still_flags)
{
LOG(2, "Configuring ZSL...");

StreamRoles stream_roles = { StreamRole::StillCapture, StreamRole::Viewfinder };
if (!options_->no_raw)
stream_roles.push_back(StreamRole::Raw);

configuration_ = camera_->generateConfiguration(stream_roles);
if (!configuration_)
throw std::runtime_error("failed to generate viewfinder configuration");

// Now we get to override any of the default settings from the options_->
if (still_flags & FLAG_STILL_BGR)
configuration_->at(0).pixelFormat = libcamera::formats::BGR888;
else if (still_flags & FLAG_STILL_RGB)
configuration_->at(0).pixelFormat = libcamera::formats::RGB888;
else
configuration_->at(0).pixelFormat = libcamera::formats::YUV420;
if (options_->buffer_count > 0)
configuration_->at(0).bufferCount = options_->buffer_count;
else
// Use the viewfinder stream buffer count if none has been provided
configuration_->at(0).bufferCount = configuration_->at(1).bufferCount;
if (options_->width)
configuration_->at(0).size.width = options_->width;
if (options_->height)
configuration_->at(0).size.height = options_->height;
configuration_->at(0).colorSpace = libcamera::ColorSpace::Sycc;
configuration_->transform = options_->transform;

post_processor_.AdjustConfig("still", &configuration_->at(0));

if (!options_->no_raw)
{
options_->mode.update(configuration_->at(0).size, options_->framerate);
options_->mode = selectMode(options_->mode);

configuration_->at(2).size = options_->mode.Size();
configuration_->at(2).pixelFormat = mode_to_pixel_format(options_->mode);
configuration_->sensorConfig = libcamera::SensorConfiguration();
configuration_->sensorConfig->outputSize = options_->mode.Size();
configuration_->sensorConfig->bitDepth = options_->mode.bit_depth;
configuration_->at(2).bufferCount = configuration_->at(0).bufferCount;
}

Size size(1280, 960);
auto area = camera_->properties().get(properties::PixelArrayActiveAreas);
if (options_->viewfinder_width && options_->viewfinder_height)
size = Size(options_->viewfinder_width, options_->viewfinder_height);
else if (area)
{
// The idea here is that most sensors will have a 2x2 binned mode that
// we can pick up. If it doesn't, well, you can always specify the size
// you want exactly with the viewfinder_width/height options_->
size = (*area)[0].size() / 2;
// If width and height were given, we might be switching to capture
// afterwards - so try to match the field of view.
if (options_->width && options_->height)
size = size.boundedToAspectRatio(Size(options_->width, options_->height));
size.alignDownTo(2, 2); // YUV420 will want to be even
LOG(2, "Viewfinder size chosen is " << size.toString());
}

// Finally trim the image size to the largest that the preview can handle.
Size max_size;
preview_->MaxImageSize(max_size.width, max_size.height);
if (max_size.width && max_size.height)
{
size.boundTo(max_size.boundedToAspectRatio(size)).alignDownTo(2, 2);
LOG(2, "Final viewfinder size is " << size.toString());
}

// Now we get to override any of the default settings from the options_->
configuration_->at(1).pixelFormat = libcamera::formats::YUV420;
configuration_->at(1).size = size;
configuration_->at(1).bufferCount = configuration_->at(0).bufferCount;

configuration_->transform = options_->transform;

post_processor_.AdjustConfig("viewfinder", &configuration_->at(1));

configureDenoise(options_->denoise == "auto" ? "cdn_hq" : options_->denoise);
setupCapture();

streams_["still"] = configuration_->at(0).stream();
streams_["viewfinder"] = configuration_->at(1).stream();
if (!options_->no_raw)
streams_["raw"] = configuration_->at(2).stream();

post_processor_.Configure();

LOG(2, "ZSL setup complete");
}

void LibcameraApp::ConfigureStill(unsigned int flags)
{
LOG(2, "Configuring still capture...");
Expand Down
1 change: 1 addition & 0 deletions core/libcamera_app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class LibcameraApp
void ConfigureViewfinder();
void ConfigureStill(unsigned int flags = FLAG_STILL_NONE);
void ConfigureVideo(unsigned int flags = FLAG_VIDEO_NONE);
void ConfigureZsl(unsigned int still_flags = FLAG_STILL_NONE);

void Teardown();
void StartCamera();
Expand Down
4 changes: 4 additions & 0 deletions core/still_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct StillOptions : public Options
"Perform first capture immediately, with no preview phase")
("autofocus-on-capture", value<bool>(&af_on_capture)->default_value(false)->implicit_value(true),
"Switch to AfModeAuto and trigger a scan just before capturing a still")
("zsl", value<bool>(&zsl)->default_value(false)->implicit_value(true),
"Switch to AfModeAuto and trigger a scan just before capturing a still")
;
// clang-format on
}
Expand All @@ -67,6 +69,7 @@ struct StillOptions : public Options
bool raw;
std::string latest;
bool immediate;
bool zsl;

virtual bool Parse(int argc, char *argv[]) override
{
Expand Down Expand Up @@ -114,6 +117,7 @@ struct StillOptions : public Options
std::cerr << " latest: " << latest << std::endl;
std::cerr << " immediate " << immediate << std::endl;
std::cerr << " AF on capture: " << af_on_capture << std::endl;
std::cerr << " Zero shutter lag: " << zsl << std::endl;
for (auto &s : exif)
std::cerr << " EXIF: " << s << std::endl;
}
Expand Down