Skip to content

Commit 05b9c89

Browse files
authored
support enabling a secondary stream (#44)
1 parent e761304 commit 05b9c89

File tree

13 files changed

+311
-12
lines changed

13 files changed

+311
-12
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
!/subprojects/x264.wrap
77
!/subprojects/libpisp.wrap
88
!/subprojects/openssl.wrap
9+
!/subprojects/libjpeg-turbo.wrap

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
!/subprojects/x264.wrap
77
!/subprojects/libpisp.wrap
88
!/subprojects/openssl.wrap
9+
!/subprojects/libjpeg-turbo.wrap

camera.cpp

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ const char *camera_get_error() {
5858
return errbuf;
5959
}
6060

61+
static long timespec_sub(struct timespec *a, struct timespec *b) {
62+
return ((a->tv_sec * 1000000000L) + a->tv_nsec) -
63+
((b->tv_sec * 1000000000L) + b->tv_nsec);
64+
}
65+
66+
static void timespec_add(struct timespec *a, long nanosecs) {
67+
a->tv_nsec += nanosecs;
68+
69+
while (a->tv_nsec > 1000000000L) {
70+
a->tv_sec++;
71+
a->tv_nsec -= 1000000000L;
72+
}
73+
};
74+
6175
// https://github.com/raspberrypi/rpicam-apps/blob/6de1ab6a899df35f929b2a15c0831780bd8e750e/core/dma_heaps.cpp
6276
static int create_dma_allocator() {
6377
static const char *heap_positions[] = {
@@ -132,14 +146,17 @@ struct CameraPriv {
132146
const parameters_t *params;
133147
camera_frame_cb frame_cb;
134148
camera_error_cb error_cb;
149+
long secondary_deltat;
135150
std::unique_ptr<CameraManager> camera_manager;
136151
std::shared_ptr<Camera> camera;
137152
Stream *video_stream;
153+
Stream *secondary_stream;
138154
std::vector<std::unique_ptr<Request>> requests;
139155
std::mutex ctrls_mutex;
140156
std::unique_ptr<ControlList> ctrls;
141157
std::vector<std::unique_ptr<FrameBuffer>> frame_buffers;
142158
std::map<FrameBuffer *, uint8_t *> mapped_buffers;
159+
struct timespec last_secondary_frame_time;
143160
bool in_error;
144161
};
145162

@@ -194,14 +211,19 @@ bool camera_create(
194211
if (params->mode != NULL) {
195212
stream_roles.push_back(StreamRole::Raw);
196213
}
214+
if (params->secondary_width != 0) {
215+
stream_roles.push_back(StreamRole::Viewfinder);
216+
}
197217

198218
std::unique_ptr<CameraConfiguration> conf = camp->camera->generateConfiguration(stream_roles);
199219
if (conf == NULL) {
200220
set_error("Camera.generateConfiguration() failed");
201221
return false;
202222
}
203223

204-
StreamConfiguration &video_stream_conf = conf->at(0);
224+
int cur_stream = 0;
225+
226+
StreamConfiguration &video_stream_conf = conf->at(cur_stream++);
205227
video_stream_conf.size = Size(params->width, params->height);
206228
video_stream_conf.pixelFormat = formats::YUV420;
207229
video_stream_conf.bufferCount = params->buffer_count;
@@ -212,12 +234,19 @@ bool camera_create(
212234
}
213235

214236
if (params->mode != NULL) {
215-
StreamConfiguration &raw_stream_conf = conf->at(1);
237+
StreamConfiguration &raw_stream_conf = conf->at(cur_stream++);
216238
raw_stream_conf.size = Size(params->mode->width, params->mode->height);
217239
raw_stream_conf.pixelFormat = mode_to_pixel_format(params->mode);
218240
raw_stream_conf.bufferCount = video_stream_conf.bufferCount;
219241
}
220242

243+
if (params->secondary_width != 0) {
244+
StreamConfiguration &secondary_stream_conf = conf->at(cur_stream++);
245+
secondary_stream_conf.size = Size(params->secondary_width, params->secondary_height);
246+
secondary_stream_conf.bufferCount = video_stream_conf.bufferCount;
247+
secondary_stream_conf.pixelFormat = formats::YUV420;
248+
}
249+
221250
conf->orientation = Orientation::Rotate0;
222251
if (params->h_flip) {
223252
conf->orientation = conf->orientation * Transform::HFlip;
@@ -240,6 +269,11 @@ bool camera_create(
240269

241270
camp->video_stream = video_stream_conf.stream();
242271

272+
if (params->secondary_width != 0) {
273+
StreamConfiguration &secondary_stream_conf = conf->at(cur_stream - 1);
274+
camp->secondary_stream = secondary_stream_conf.stream();
275+
}
276+
243277
for (unsigned int i = 0; i < params->buffer_count; i++) {
244278
std::unique_ptr<Request> request = camp->camera->createRequest((uint64_t)camp.get());
245279
if (request == NULL) {
@@ -282,8 +316,7 @@ bool camera_create(
282316
camp->frame_buffers.push_back(std::make_unique<FrameBuffer>(plane));
283317
FrameBuffer *fb = camp->frame_buffers.back().get();
284318

285-
// map buffers of the video stream only
286-
if (stream == video_stream_conf.stream()) {
319+
if (stream == camp->video_stream || stream == camp->secondary_stream) {
287320
camp->mapped_buffers[fb] = (uint8_t*)mmap(NULL, stream_conf.frameSize, PROT_READ | PROT_WRITE, MAP_SHARED, plane[0].fd.get(), 0);
288321
}
289322

@@ -300,6 +333,8 @@ bool camera_create(
300333
camp->params = params;
301334
camp->frame_cb = frame_cb;
302335
camp->error_cb = error_cb;
336+
camp->secondary_deltat = (long)(1000000000.0 / params->secondary_fps);
337+
clock_gettime(CLOCK_MONOTONIC, &camp->last_secondary_frame_time);
303338
*cam = camp.release();
304339

305340
return true;
@@ -328,11 +363,26 @@ static void on_request_complete(Request *request) {
328363

329364
FrameBuffer *buffer = request->buffers().at(camp->video_stream);
330365

366+
uint8_t *secondary_buffer_mapped = NULL;
367+
if (camp->secondary_stream != NULL) {
368+
struct timespec now;
369+
clock_gettime(CLOCK_MONOTONIC, &now);
370+
371+
long diff = timespec_sub(&now, &camp->last_secondary_frame_time);
372+
373+
if (diff >= camp->secondary_deltat) {
374+
timespec_add(&camp->last_secondary_frame_time, camp->secondary_deltat);
375+
FrameBuffer *secondary_buffer = request->buffers().at(camp->secondary_stream);
376+
secondary_buffer_mapped = camp->mapped_buffers.at(secondary_buffer);
377+
}
378+
}
379+
331380
camp->frame_cb(
332381
camp->mapped_buffers.at(buffer),
333382
buffer->planes()[0].fd.get(),
334383
buffer_size(buffer->planes()),
335-
buffer->metadata().timestamp / 1000);
384+
buffer->metadata().timestamp / 1000,
385+
secondary_buffer_mapped);
336386

337387
request->reuse(Request::ReuseFlag::ReuseBuffers);
338388

@@ -350,6 +400,11 @@ int camera_get_stride(camera_t *cam) {
350400
return camp->video_stream->configuration().stride;
351401
}
352402

403+
int camera_get_secondary_stride(camera_t *cam) {
404+
CameraPriv *camp = (CameraPriv *)cam;
405+
return camp->secondary_stream->configuration().stride;
406+
}
407+
353408
int camera_get_colorspace(camera_t *cam) {
354409
CameraPriv *camp = (CameraPriv *)cam;
355410
return get_v4l2_colorspace(camp->video_stream->configuration().colorSpace);

camera.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ typedef void (*camera_frame_cb)(
99
uint8_t *buffer_mapped,
1010
int buffer_fd,
1111
uint64_t buffer_size,
12-
uint64_t timestamp);
12+
uint64_t timestamp,
13+
uint8_t *secondary_buffer_mapped);
1314

1415
typedef void (*camera_error_cb)();
1516

@@ -24,6 +25,7 @@ bool camera_create(
2425
camera_error_cb error_cb,
2526
camera_t **cam);
2627
int camera_get_stride(camera_t *cam);
28+
int camera_get_secondary_stride(camera_t *cam);
2729
int camera_get_colorspace(camera_t *cam);
2830
bool camera_start(camera_t *cam);
2931
void camera_reload_params(camera_t *cam, const parameters_t *params);

encoder_jpeg.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#include <stdbool.h>
2+
#include <stdio.h>
3+
#include <stdarg.h>
4+
#include <stdlib.h>
5+
#include <stdint.h>
6+
#include <string.h>
7+
#include <stdio.h>
8+
#include <pthread.h>
9+
10+
#include <jpeglib.h>
11+
12+
#include "encoder_jpeg.h"
13+
14+
static char errbuf[256];
15+
16+
const char *encoder_jpeg_get_error() {
17+
return errbuf;
18+
}
19+
20+
typedef struct {
21+
int width;
22+
int height;
23+
int quality;
24+
int stride;
25+
pthread_mutex_t mutex;
26+
pthread_cond_t cond;
27+
pthread_t thread;
28+
bool data_queued;
29+
uint8_t *data_buffer;
30+
uint64_t data_timestamp;
31+
encoder_jpeg_output_cb output_cb;
32+
} encoder_jpeg_priv_t;
33+
34+
static void save_as_jpeg(
35+
unsigned int width,
36+
unsigned int height,
37+
unsigned int quality,
38+
unsigned int stride,
39+
uint8_t *in_buf,
40+
uint8_t **out_buf,
41+
unsigned long *out_size) {
42+
struct jpeg_error_mgr jerr;
43+
struct jpeg_compress_struct cinfo;
44+
cinfo.err = jpeg_std_error(&jerr);
45+
jpeg_create_compress(&cinfo);
46+
47+
cinfo.image_width = width;
48+
cinfo.image_height = height;
49+
cinfo.input_components = 3;
50+
cinfo.in_color_space = JCS_YCbCr;
51+
jpeg_set_defaults(&cinfo);
52+
jpeg_set_quality(&cinfo, quality, TRUE);
53+
54+
jpeg_mem_dest(&cinfo, out_buf, out_size);
55+
56+
jpeg_start_compress(&cinfo, TRUE);
57+
58+
JSAMPROW row_pointer[1];
59+
uint8_t *row_buf = malloc(width * 3);
60+
row_pointer[0] = row_buf;
61+
62+
uint8_t *Y = in_buf;
63+
uint8_t *U = Y + stride * height;
64+
uint8_t *V = U + (stride / 2) * (height / 2);
65+
66+
while (cinfo.next_scanline < height) {
67+
for (unsigned int j = 0; j < width; j++) {
68+
int i1 = cinfo.next_scanline*stride + j;
69+
int i2 = cinfo.next_scanline/2*stride/2 + j/2;
70+
71+
row_buf[j*3 + 0] = Y[i1];
72+
row_buf[j*3 + 1] = U[i2];
73+
row_buf[j*3 + 2] = V[i2];
74+
}
75+
76+
jpeg_write_scanlines(&cinfo, row_pointer, 1);
77+
}
78+
79+
free(row_buf);
80+
81+
jpeg_finish_compress(&cinfo);
82+
jpeg_destroy_compress(&cinfo);
83+
}
84+
85+
static void *thread_main(void *userdata) {
86+
encoder_jpeg_priv_t *encp = (encoder_jpeg_priv_t *)userdata;
87+
88+
while (true) {
89+
pthread_mutex_lock(&encp->mutex);
90+
91+
while (!encp->data_queued) {
92+
pthread_cond_wait(&encp->cond, &encp->mutex);
93+
}
94+
95+
uint8_t *buffer = encp->data_buffer;
96+
uint64_t timestamp = encp->data_timestamp;
97+
encp->data_queued = false;
98+
99+
pthread_cond_signal(&encp->cond);
100+
101+
pthread_mutex_unlock(&encp->mutex);
102+
103+
uint8_t *out_buf;
104+
unsigned long out_size = 0;
105+
save_as_jpeg(encp->width, encp->height, encp->quality, encp->stride, buffer, &out_buf, &out_size);
106+
107+
encp->output_cb(out_buf, out_size, timestamp);
108+
109+
free(out_buf);
110+
}
111+
112+
return NULL;
113+
}
114+
115+
bool encoder_jpeg_create(
116+
int width,
117+
int height,
118+
int quality,
119+
int stride,
120+
encoder_jpeg_output_cb output_cb,
121+
encoder_jpeg_t **enc) {
122+
*enc = malloc(sizeof(encoder_jpeg_priv_t));
123+
encoder_jpeg_priv_t *encp = (encoder_jpeg_priv_t *)(*enc);
124+
memset(encp, 0, sizeof(encoder_jpeg_priv_t));
125+
126+
encp->width = width;
127+
encp->height = height;
128+
encp->quality = quality;
129+
encp->stride = stride;
130+
pthread_mutex_init(&encp->mutex, NULL);
131+
pthread_cond_init(&encp->cond, NULL);
132+
pthread_create(&encp->thread, NULL, thread_main, encp);
133+
encp->output_cb = output_cb;
134+
135+
return true;
136+
}
137+
138+
void encoder_jpeg_encode(encoder_jpeg_t *enc, uint8_t *buffer, uint64_t timestamp) {
139+
encoder_jpeg_priv_t *encp = (encoder_jpeg_priv_t *)enc;
140+
141+
pthread_mutex_lock(&encp->mutex);
142+
143+
while (encp->data_queued) {
144+
pthread_cond_wait(&encp->cond, &encp->mutex);
145+
}
146+
147+
encp->data_queued = true;
148+
encp->data_buffer = buffer;
149+
encp->data_timestamp = timestamp;
150+
151+
pthread_cond_signal(&encp->cond);
152+
153+
pthread_mutex_unlock(&encp->mutex);
154+
}

encoder_jpeg.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#ifndef __ENCODER_JPEG_H__
2+
#define __ENCODER_JPEG_H__
3+
4+
#include "parameters.h"
5+
6+
typedef void encoder_jpeg_t;
7+
8+
typedef void (*encoder_jpeg_output_cb)(const uint8_t *mapped, uint64_t size, uint64_t ts);
9+
10+
const char *encoder_jpeg_get_error();
11+
bool encoder_jpeg_create(
12+
int width,
13+
int height,
14+
int quality,
15+
int stride,
16+
encoder_jpeg_output_cb output_cb,
17+
encoder_jpeg_t **enc);
18+
void encoder_jpeg_encode(encoder_jpeg_t *enc, uint8_t *mapped, uint64_t ts);
19+
20+
#endif

0 commit comments

Comments
 (0)