스냅샷 API¶
녹화 컴포넌트에서 제공하는 takeRecordingShapshot과는 별개로 직접 카메라 이미지를 캡춰합니다.
스냅샷 API는 Non-blocking 방식과 Blocking 방식이 있습니다.
Photographer객체를 생성하고PhotographerObserver인터페이스를 사용하는 방식으로 캡쳐 파일을 계속 생성할 수 있습니다.takePicture함수를 호출하여 스냅샷이 완료될 때까지 기다리는 방식으로 캡춰 파일을 하나만 생성할 수 있습니다.
헤더 파일¶
OasisMedia.h
Blocking 함수¶
Blocking 방식으로 스냅샷 데이터를 생성합니다. 스냅샷이 완료되거나 타임아웃이 발생할 때까지 기다립니다.
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
스냅샷 생성에 필요한 key-value map은 아래와 같습니다.
JPEG 이미지 생성 시 아래 EXIF 태깅 패러미터를 지정할 수 있습니다.
Non-Blocking 함수¶
사용자는 createPhotographer로 Photographer 객체를 생성합니다. 이후 startPhotographer 로 시작한 후, 스냅샷이 필요할 경우 마다 photographerGeneratePicture를 호출합니다. Photographer를 사용을 마치려면, stopPhotographer를 호출합니다.
PhotographerObserver 인터페이스¶
createPhotographer 호출 시에 매개변수로 PhotographerObserver을 base class로 정의한 사용자 인터페이스 객체를 전달합니다. 이후 콜백함수를 통하여 스냅샷 작업을 수행합니다.
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 와 onIspMetaData 는 Rasberry Pi 에만 적용됩니다.
Photographer API¶
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- 0: 성공
- -1: 실패
- true: 시작상태입니다.
- false: 정지상태입니다.
- 0: 성공
- -1: 실패
아래 key-value map 으로 옵션 사항입니다. 이외에 osd 관련 패러미터와 EXIF 관련 패러미터를 지정할 수 있습니다.
Note
photographerSetIspParameters는 Rasberry Pi 에만 적용됩니다.
예제¶
선택한 카메라 장치로 부터 영상을 캡춰하고 OSD를 적용한 다음 JPEG 파일을 생성하는 예입니다. -a 옵션을 선택할 경우, Non-Blocking 함수를 사용합니다.
Non-Blocking 함수를 적용할 경우, 1초 간격으로 각기 다른 OSD 텍스트를 지정하여 2회 스냅샷을 생성합니다.
#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";
//parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
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();
//parameters["jpeg-quality"] = std::to_string(quality);
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;
}