Snapshot API#
Apart from takeRecordingSnapshot provided by the recording component, this API captures camera images directly.
The snapshot API provides both Non-blocking and Blocking methods.
- By creating a
Photographerobject and using thePhotographerObserverinterface, you can continuously generate capture files. - By calling the
takePicturefunction and waiting until the snapshot is complete, you can generate a single capture file.
Header File#
OasisMedia.h
Blocking Functions#
Generates snapshot data using a Blocking method. It waits until the snapshot is complete or a timeout occurs.
configCameras.
left, center, right.
top, vcenter, bottom.
nullptr or an empty string is specified, the OSD text is not displayed.
true, the outline is displayed with the outline_color.
- 0: Success
- -1: Failure
configCameras.
- 0: Success
- -1: Failure
The key-value map required for snapshot generation is as follows:
left, center, right.top, vcenter, bottom.system-font-path value defined during oasis::initialize is used.When generating a JPEG image, the following EXIF tagging parameters can be specified:
Non-Blocking Functions#
The user creates a Photographer object using createPhotographer. After starting it with startPhotographer, photographerGeneratePicture is called whenever a snapshot is required. To finish using the Photographer, call stopPhotographer.
PhotographerObserver Interface#
When calling createPhotographer, a user interface object defining PhotographerObserver as a base class is passed as a parameter. Subsequently, snapshot tasks are executed via callback functions.
class PhotographerObserver : public std::enable_shared_from_this<PhotographerObserver>
{
public:
PhotographerObserver();
virtual ~PhotographerObserver();
virtual void onCameraSensorMetaData(int32_t camera_id, const CameraSensorMetaData &metadata);
virtual void onIspMetaData(int32_t camera_id, const IspMetaData &metadata);
virtual void onCaptureImageAvailable(bool subchannel_image);
virtual void onImageDataCompleted(const key_value_map_t ¶meters, bool subchannel_image, int32_t width, int32_t height, int32_t format, const std::vector<char> &data);
virtual void onImageDataCompletedWithError(const key_value_map_t ¶meters, bool subchannel_image, int32_t error);
};
Note
onCameraSensorMetaData and onIspMetaData apply only to Raspberry Pi.
false, it means the main channel image is ready. If true, it means the subchannel image is ready. Depending on the camera sensor device, a subchannel image may also be generated together.
photographerGeneratePicture.
photographerGeneratePicture.
false, it indicates main channel image data. If true, it is subchannel image data.
kImageTypeJpeg(0).
photographerGeneratePicture.
photographerGeneratePicture.
false, it indicates main channel image data. If true, it is subchannel image data.
Photographer API#
configCameras.
PhotographerObserver.
nullptr on failure.
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
- 0: Success
- -1: Failure
- true: Started state.
- false: Stopped state.
false. Captures the subchannel image if true.
- 0: Success
- -1: Failure
The key-value map below consists of optional items. In addition, OSD-related parameters and EXIF-related parameters can be specified.
Note
photographerSetIspParameters applies only to Raspberry Pi.
Example#
This example captures video from the selected camera device, applies OSD, and generates a JPEG file. If the -a option is selected, a Non-Blocking function is used.
When applying the Non-Blocking function, snapshots are generated twice at 1-second intervals, specifying different OSD text each time.
#include "OasisAPI.h"
#include "OasisLog.h"
#include "OasisMedia.h"
#include "OasisUI.h"
#include "OasisUtil.h"
#include "OasisDisplay.h"
#include <thread>
#include <mutex>
#include <memory>
#include <condition_variable>
#include <signal.h>
#define DLOG_APP 0x00010000
#undef DLOG_FLAGS
#define DLOG_FLAGS (DLOG_FLAGS_DEFAULT|DLOG_APP/**/)
using namespace oasis;
static bool continue_capturing = true;
enum {
kCaptureStateIdle = 0,
kCaptureStateImageAvailable,
kCaptureStateImageDataReady,
kCaptureStateError,
};
static std::list<int32_t> capture_state_q;
static std::mutex capture_mutex;
static std::condition_variable capture_cond;
static std::vector<char> captured_image_data;
void cancel_handler(int signum)
{
continue_capturing = false;
capture_cond.notify_one();
}
class MyPhotographerObserver : public PhotographerObserver
{
public:
MyPhotographerObserver() {}
virtual ~MyPhotographerObserver() {}
virtual void onCameraSensorMetaData(int32_t camera_id, const CameraSensorMetaData &metadata) {}
virtual void onIspMetaData(int32_t camera_id, const IspMetaData &metadata) {}
virtual void onCaptureImageAvailable(bool subchannel_image);
virtual void onImageDataCompleted(const key_value_map_t ¶meters, bool subchannel_image, int32_t width, int32_t height, int32_t format, const std::vector<char> &data);
virtual void onImageDataCompletedWithError(const key_value_map_t ¶meters, bool subchannel_image, int32_t error);
};
void MyPhotographerObserver::onCaptureImageAvailable(bool subchannel_image)
{
fprintf(stdout, ">>> image available\n");
{
std::lock_guard<std::mutex> lock(capture_mutex);
capture_state_q.push_back(kCaptureStateImageAvailable);
}
capture_cond.notify_one();
}
void MyPhotographerObserver::onImageDataCompleted(const key_value_map_t ¶meters, bool subchannel_image, int32_t width, int32_t height, int32_t format, const std::vector<char> &data)
{
fprintf(stdout, ">>>>> image data completed: %dx%d, format %d, data size %zd\n", width, height, format, data.size());
{
std::lock_guard<std::mutex> lock(capture_mutex);
capture_state_q.push_back(kCaptureStateImageDataReady);
captured_image_data = data;
}
capture_cond.notify_one();
}
void MyPhotographerObserver::onImageDataCompletedWithError(const key_value_map_t ¶meters, bool subchannel_image, int32_t error)
{
fprintf(stdout, ">>>>> image data completed with error: %d\n", error);
{
std::lock_guard<std::mutex> lock(capture_mutex);
capture_state_q.push_back(kCaptureStateError);
}
capture_cond.notify_one();
}
void print_usage(const char *prog_name)
{
fprintf(stdout, "USAGE: %s [-a] [-w width] [-h height] [-q quality] [-t wait-timeout in seconds] [-s skip frame count] <camera id> <output-jpeg-file-path>\n", prog_name);
}
int main(int argc, char* argv[])
{
int32_t err, opt;
int32_t camera_id = 0, width = 0, height = 0, wait = 5, quality = 100, skip = 6;
char output_path[PATH_MAX];
bool use_async = false;
while((opt = getopt(argc, argv, "aw:h:q:t:s:")) != -1 ) {
switch ( opt ) {
case 'a':
use_async = true;
break;
case 'w':
if(optarg == nullptr) {
print_usage(argv[0]);
return -1;
}
width = atoi(optarg);
break;
case 'h':
if(optarg == nullptr) {
print_usage(argv[0]);
return -1;
}
height = atoi(optarg);
break;
case 'q':
if(optarg == nullptr) {
print_usage(argv[0]);
return -1;
}
quality = atoi(optarg);
if(quality > 100) quality = 100;
else if(quality < 0) quality = 1;
break;
case 't':
if(optarg == nullptr) {
print_usage(argv[0]);
return -1;
}
wait = atoi(optarg);
break;
case 's':
if(optarg == nullptr) {
print_usage(argv[0]);
return -1;
}
skip = atoi(optarg);
break;
default:
fprintf(stderr, "error: unknown option '%c'\n", optopt);
print_usage(argv[0]);
return -1;
}
}
if(argc-optind < 2) {
fprintf(stderr, "error: invalid or insufficient arguments\n");
print_usage(argv[0]);
return -1;
}
camera_id = atoi(argv[optind]);
strncpy(output_path, argv[optind+1], sizeof(output_path)-1);
TRACE0("camera %d, size %dx%d, quality %d, wait timeout %d, skip %d frames, save to %s\n", camera_id, width, height, quality, wait, skip, output_path);
signal(SIGINT, cancel_handler);
////////////////////////////////////////////////////////////////////////////////////////////
// init
oasis::key_value_map_t parameters;
parameters["offs-disable"] = "1";
if(oasis::initialize(parameters) < 0) {
DLOG(DLOG_ERROR, "Oasis init failed\n");
return -1;
}
////////////////////////////////////////////////////////////////////////////////////////////
// config cameras
parameters.clear();
parameters["source-count"] = "1";
parameters["source1-camera-id"] = "0";
parameters["source1-isp-id"] = "0";
parameters["source1-isp-wdr-mode"] = "0";
parameters["source1-capture-format"] = "YUV420";
parameters["source1-capture-buffers"] = "5";
parameters["source1-fps"] = "30";
parameters["source1-subchannel-rotation"] = "0";
parameters["source1-loc"] = "front";
parameters["source1-capture-resolution"] = "1080p";
parameters["source1-sensor-config"] = "./Resource_tp2863/VIC/0/tp2863_1920x1080_ch0.cfg";
parameters["source1-autoscene-config"] = "./Resource_tp2863/AutoScene/autoscene_conf.cfg";
parameters["source1-resource-dir"] = "./Resource_tp2863/";
configCameras(parameters);
////////////////////////////////////////////////////////////////////////////////////////////
// display setup
parameters.clear();
parameters["memory-type"] = "ion"; //cma
parameters["screen-width"] = "480";
parameters["screen-height"] = "320";
display::setup(parameters);
////////////////////////////////////////////////////////////////////////////////////////////
parameters["camera-id"] = std::to_string(camera_id);
parameters["width"] = std::to_string(width);
parameters["height"] = std::to_string(height);
parameters["quality"] = std::to_string(quality);
parameters["timeout"] = std::to_string(wait*1000);
parameters["drop-frame-count"] = std::to_string(skip);
if(use_async) {
std::shared_ptr<MyPhotographerObserver> observer = std::make_shared<MyPhotographerObserver>();
PhotographerRef photographer = createPhotographer(camera_id, parameters, observer);
if(photographer) {
parameters.clear();
startPhotographer(photographer);
bool is_capturing = false;
int32_t image_count = 0;
do {
int32_t state;
{
std::unique_lock<std::mutex> lock(capture_mutex);
while(continue_capturing && capture_state_q.empty()) {
if(wait > 0) {
std::cv_status status = capture_cond.wait_for(lock, std::chrono::seconds(wait));
if(status == std::cv_status::timeout) {
fprintf(stderr, "wait for image available timeout\n");
continue_capturing = false;
break;
}
} else {
capture_cond.wait(lock);
}
}
if(capture_state_q.empty()) {
continue;
} else {
state = capture_state_q.front();
capture_state_q.pop_front();
}
}
auto triggerSnapshot = [&](int32_t image_index) -> int32_t {
key_value_map_t parameters;
time_t tm;
struct tm tm_t;
std::string osd_string;
time(&tm);
localtime_r(&tm, &tm_t);
osd_string = oasis::format("snapshot<%d> %4d/%02d/%02d %02d:%02d:%02d", image_index, tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
parameters["osd"] = osd_string;
parameters["osd-font-size"] = "26";
parameters["osd-font-face"] = "Consolas";
parameters["osd-text-color"] = "255,255,255";
parameters["osd-use-text-color"] = "1";
parameters["osd-use-bg-color"] = "0";
parameters["osd-bg-color"] = "0,0,0";
parameters["osd-use-outline-color"] = "1";
parameters["osd-outline-color"] = "0,0,0";
parameters["osd-horz-align"] = "left";
parameters["osd-vert-align"] = "bottom";
parameters["osd-font-path"] = "/mnt/sd/consola.ttf";
parameters["osd-use-fixed-size"] = "0";
return photographerGeneratePicture(photographer, parameters, false, nullptr, 0);
};
if(state == kCaptureStateImageAvailable) {
if(is_capturing == false) {
is_capturing = 0 == triggerSnapshot(image_count);
if(!is_capturing) {
fprintf(stderr, "failed to generate picture\n");
break;
} else {
image_count++;
}
}
} else if(state == kCaptureStateImageDataReady) {
if(captured_image_data.empty() == false) {
std::string path = output_path;
size_t dot_pos = path.find_last_of(".");
std::string prefix = dot_pos != std::string::npos ? path.substr(0, dot_pos) : path;
std::string suffix = dot_pos != std::string::npos ? path.substr(dot_pos) : "";
std::string this_image_path = prefix + "_" + std::to_string(image_count) + suffix;
FILE *fp = fopen(this_image_path.c_str(), "wb");
if(fp) {
size_t n, len;
for(n=0; n<captured_image_data.size(); n += len) {
len = fwrite( captured_image_data.data()+n, 1, captured_image_data.size()-n, fp );
if(len == 0) break;
}
fclose(fp);
DLOG0(DLOG_INFO, "%s saved\n", this_image_path.c_str());
}
}
is_capturing = false;
if(image_count == 2) {
break;
} else {
//take another picture
usleep(1000000);
if(is_capturing == false) {
is_capturing = 0 == triggerSnapshot(image_count);
if(!is_capturing) {
fprintf(stderr, "failed to generate picture\n");
break;
} else {
image_count++;
}
}
}
} else if(state == kCaptureStateError) {
is_capturing = false;
break;
}
} while (continue_capturing);
stopPhotographer(photographer);
destroyPhotographer(photographer);
}
} else {
time_t tm;
struct tm tm_t;
std::string osd_string;
time(&tm);
localtime_r(&tm, &tm_t);
osd_string = oasis::format("%4d/%02d/%02d %02d:%02d:%02d 12.0V 12H/11.9V", tm_t.tm_year + 1900, tm_t.tm_mon + 1, tm_t.tm_mday, tm_t.tm_hour, tm_t.tm_min, tm_t.tm_sec);
parameters["osd"] = osd_string;
parameters["osd-font-size"] = "16";
parameters["osd-font-face"] = "Consolas";
parameters["osd-text-color"] = "255,255,255";
parameters["osd-use-text-color"] = "1";
parameters["osd-use-bg-color"] = "0";
parameters["osd-bg-color"] = "0,0,0";
parameters["osd-use-outline-color"] = "1";
parameters["osd-outline-color"] = "0,0,0";
parameters["osd-horz-align"] = "left";
parameters["osd-vert-align"] = "bottom";
parameters["osd-font-path"] = "/mnt/sd/consola.ttf";
parameters["osd-use-fixed-size"] = "0";
err = takePicture(camera_id, parameters, captured_image_data);
if(err == 0) {
DLOG0(DLOG_INFO, "snapshot jpeg data %d bytes returned\n", captured_image_data.size());
FILE *fp = fopen(output_path, "wb");
if(fp) {
size_t n, len;
for(n=0; n<captured_image_data.size(); n += len) {
len = fwrite( captured_image_data.data()+n, 1, captured_image_data.size()-n, fp );
if(len == 0) break;
}
fclose(fp);
DLOG0(DLOG_INFO, "%s saved\n", output_path);
}
} else {
DLOG0(DLOG_ERROR, "error %d\n", err);
}
}
oasis::finalize();
return 0;
}