Skip to content

Commit f2f560a

Browse files
committed
h264 encoder in separate thread
1 parent 6a0ee68 commit f2f560a

File tree

4 files changed

+115
-71
lines changed

4 files changed

+115
-71
lines changed

src/libs/device.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "device.h"
2424

2525
#include <stdlib.h>
26+
#include <stdatomic.h>
2627
#include <stddef.h>
2728
#include <string.h>
2829
#include <strings.h>
@@ -365,6 +366,7 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
365366
} while (true);
366367

367368
*hw = &run->hw_bufs[buf.index];
369+
atomic_store(&(*hw)->busy, 0);
368370
(*hw)->raw.dma_fd = (*hw)->dma_fd;
369371
(*hw)->raw.used = buf.bytesused;
370372
(*hw)->raw.width = run->width;
@@ -388,6 +390,7 @@ int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw) {
388390
return -1;
389391
}
390392
hw->grabbed = false;
393+
atomic_store(&hw->busy, 0);
391394
return 0;
392395
}
393396

@@ -831,6 +834,7 @@ static int _device_open_io_method_mmap(us_device_s *dev) {
831834
}
832835

833836
us_hw_buffer_s *hw = &run->hw_bufs[run->n_bufs];
837+
atomic_init(&hw->busy, false);
834838
const uz buf_size = (run->capture_mplane ? buf.m.planes[0].length : buf.length);
835839
const off_t buf_offset = (run->capture_mplane ? buf.m.planes[0].m.mem_offset : buf.m.offset);
836840

src/libs/device.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222

2323
#pragma once
2424

25+
#include <stdatomic.h>
26+
2527
#include <linux/videodev2.h>
2628

2729
#include "types.h"
@@ -46,6 +48,7 @@ typedef struct {
4648
struct v4l2_buffer buf;
4749
int dma_fd;
4850
bool grabbed;
51+
atomic_int busy;
4952
} us_hw_buffer_s;
5053

5154
typedef struct {

src/ustreamer/stream.c

Lines changed: 107 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,30 @@
5050

5151

5252
typedef struct {
53+
pthread_t tid;
54+
us_device_s *dev;
55+
us_queue_s *queue;
56+
us_h264_stream_s *h264;
57+
atomic_bool *stop;
58+
} _h264_context_s;
59+
60+
typedef struct {
61+
pthread_t tid;
5362
us_device_s *dev;
5463
us_queue_s *queue;
5564
pthread_mutex_t *mutex;
5665
atomic_bool *stop;
5766
} _releaser_context_s;
5867

5968

69+
static void *_h264_thread(void *v_ctx);
6070
static void *_releaser_thread(void *v_ctx);
6171

62-
static void _stream_release_buffer(us_stream_s *stream, us_hw_buffer_s *hw);
63-
static bool _stream_is_stopped(us_stream_s *stream);
6472
static bool _stream_has_any_clients(us_stream_s *stream);
6573
static bool _stream_slowdown(us_stream_s *stream);
6674
static int _stream_init_loop(us_stream_s *stream);
6775
static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame);
76+
static void _stream_check_suicide(us_stream_s *stream);
6877

6978

7079
#define _SINK_PUT(x_sink, x_frame) { \
@@ -74,18 +83,10 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame);
7483
} \
7584
}
7685

77-
#define _H264_PUT(x_frame, x_force_key) { \
78-
if (stream->run->h264) { \
79-
us_h264_stream_process(stream->run->h264, x_frame, x_force_key); \
80-
} \
81-
}
82-
8386

8487
us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc) {
8588
us_stream_runtime_s *run;
8689
US_CALLOC(run, 1);
87-
US_MUTEX_INIT(run->release_mutex);
88-
atomic_init(&run->release_stop, false);
8990
US_RING_INIT_WITH_ITEMS(run->http_jpeg_ring, 4, us_frame_init);
9091
atomic_init(&run->http_has_clients, false);
9192
atomic_init(&run->http_last_request_ts, 0);
@@ -108,7 +109,6 @@ us_stream_s *us_stream_init(us_device_s *dev, us_encoder_s *enc) {
108109
void us_stream_destroy(us_stream_s *stream) {
109110
us_blank_destroy(stream->run->blank);
110111
US_RING_DELETE_WITH_ITEMS(stream->run->http_jpeg_ring, us_frame_destroy);
111-
US_MUTEX_DESTROY(stream->run->release_mutex);
112112
free(stream->run);
113113
free(stream);
114114
}
@@ -126,17 +126,29 @@ void us_stream_loop(us_stream_s *stream) {
126126
}
127127

128128
while (!_stream_init_loop(stream)) {
129+
atomic_bool threads_stop;
130+
atomic_init(&threads_stop, false);
131+
132+
pthread_mutex_t release_mutex;
133+
US_MUTEX_INIT(release_mutex);
129134
const uint n_releasers = stream->dev->run->n_bufs;
130-
US_CALLOC(run->releasers, n_releasers);
135+
_releaser_context_s *releasers;
136+
US_CALLOC(releasers, n_releasers);
131137
for (uint index = 0; index < n_releasers; ++index) {
132-
run->releasers[index].queue = us_queue_init(1);
133-
_releaser_context_s *ctx;
134-
US_CALLOC(ctx, 1);
135-
ctx->dev = stream->dev;
136-
ctx->queue = run->releasers[index].queue;
137-
ctx->mutex = &run->release_mutex;
138-
ctx->stop = &run->release_stop;
139-
US_THREAD_CREATE(run->releasers[index].tid, _releaser_thread, ctx);
138+
releasers[index].dev = stream->dev;
139+
releasers[index].queue = us_queue_init(1);
140+
releasers[index].mutex = &release_mutex;
141+
releasers[index].stop = &threads_stop;
142+
US_THREAD_CREATE(releasers[index].tid, _releaser_thread, &releasers[index]);
143+
}
144+
145+
_h264_context_s h264_ctx;
146+
if (run->h264 != NULL) {
147+
h264_ctx.dev = stream->dev;
148+
h264_ctx.queue = us_queue_init(stream->dev->run->n_bufs);
149+
h264_ctx.h264 = run->h264;
150+
h264_ctx.stop = &threads_stop;
151+
US_THREAD_CREATE(h264_ctx.tid, _h264_thread, &h264_ctx);
140152
}
141153

142154
ldf grab_after = 0;
@@ -146,15 +158,18 @@ void us_stream_loop(us_stream_s *stream) {
146158

147159
US_LOG_INFO("Capturing ...");
148160

149-
while (!_stream_is_stopped(stream) && !atomic_load(&run->release_stop)) {
161+
while (!atomic_load(&run->stop) && !atomic_load(&threads_stop)) {
162+
_stream_check_suicide(stream);
163+
150164
US_SEP_DEBUG('-');
151165
US_LOG_DEBUG("Waiting for worker ...");
152166

153167
us_worker_s *const ready_wr = us_workers_pool_wait(stream->enc->run->pool);
154168
us_encoder_job_s *const ready_job = ready_wr->job;
155169

156170
if (ready_job->hw != NULL) {
157-
_stream_release_buffer(stream, ready_job->hw);
171+
assert(!us_queue_put(releasers[ready_job->hw->buf.index].queue, ready_job->hw, 0));
172+
atomic_fetch_sub(&ready_job->hw->busy, 1);
158173
ready_job->hw = NULL;
159174
if (ready_wr->job_failed) {
160175
// pass
@@ -168,7 +183,7 @@ void us_stream_loop(us_stream_s *stream) {
168183
}
169184

170185
const bool h264_force_key = _stream_slowdown(stream);
171-
if (_stream_is_stopped(stream)) {
186+
if (atomic_load(&run->stop) || atomic_load(&threads_stop)) {
172187
goto close;
173188
}
174189

@@ -191,8 +206,14 @@ void us_stream_loop(us_stream_s *stream) {
191206
fluency_passed += 1;
192207
US_LOG_VERBOSE("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf",
193208
fluency_passed, now_ts, grab_after);
194-
_stream_release_buffer(stream, hw);
209+
assert(!us_queue_put(releasers[hw->buf.index].queue, hw, 0));
195210
} else {
211+
int hw_busy = 1;
212+
if (run->h264 != NULL) {
213+
hw_busy += 1;
214+
}
215+
atomic_store(&hw->busy, hw_busy);
216+
196217
fluency_passed = 0;
197218

198219
const sll now_sec_ts = us_floor_ms(now_ts);
@@ -213,18 +234,29 @@ void us_stream_loop(us_stream_s *stream) {
213234
US_LOG_DEBUG("Assigned new frame in buffer=%d to worker=%s", buf_index, ready_wr->name);
214235

215236
_SINK_PUT(raw_sink, &hw->raw);
216-
_H264_PUT(&hw->raw, h264_force_key);
237+
238+
if (run->h264 != NULL) {
239+
us_queue_put(h264_ctx.queue, hw, h264_force_key);
240+
}
217241
}
218242
}
219243

220244
close:
221-
atomic_store(&run->release_stop, true);
245+
atomic_store(&threads_stop, true);
246+
247+
if (run->h264 != NULL) {
248+
US_THREAD_JOIN(h264_ctx.tid);
249+
us_queue_destroy(h264_ctx.queue);
250+
}
251+
222252
for (uint index = 0; index < n_releasers; ++index) {
223-
US_THREAD_JOIN(run->releasers[index].tid);
224-
us_queue_destroy(run->releasers[index].queue);
253+
US_THREAD_JOIN(releasers[index].tid);
254+
us_queue_destroy(releasers[index].queue);
225255
}
226-
free(run->releasers);
227-
atomic_store(&run->release_stop, false);
256+
free(releasers);
257+
US_MUTEX_DESTROY(release_mutex);
258+
259+
atomic_store(&threads_stop, false);
228260

229261
us_encoder_close(stream->enc);
230262
us_device_close(stream->dev);
@@ -241,11 +273,29 @@ void us_stream_loop_break(us_stream_s *stream) {
241273
atomic_store(&stream->run->stop, true);
242274
}
243275

276+
static void *_h264_thread(void *v_ctx) {
277+
_h264_context_s *ctx = v_ctx;
278+
while (!atomic_load(ctx->stop)) {
279+
us_hw_buffer_s *hw;
280+
if (!us_queue_get(ctx->queue, (void**)&hw, 0.1)) {
281+
us_h264_stream_process(ctx->h264, &hw->raw, false);
282+
atomic_fetch_sub(&hw->busy, 1);
283+
}
284+
}
285+
return NULL;
286+
}
287+
244288
static void *_releaser_thread(void *v_ctx) {
245289
_releaser_context_s *ctx = v_ctx;
246290
while (!atomic_load(ctx->stop)) {
247291
us_hw_buffer_s *hw;
248292
if (!us_queue_get(ctx->queue, (void**)&hw, 0.1)) {
293+
while (atomic_load(&hw->busy) > 0) {
294+
if (atomic_load(ctx->stop)) {
295+
break;
296+
}
297+
usleep(5 * 1000);
298+
}
249299
US_MUTEX_LOCK(*ctx->mutex);
250300
const int released = us_device_release_buffer(ctx->dev, hw);
251301
US_MUTEX_UNLOCK(*ctx->mutex);
@@ -254,36 +304,10 @@ static void *_releaser_thread(void *v_ctx) {
254304
}
255305
}
256306
}
257-
atomic_store(ctx->stop, true); // Stop all other guys
258-
free(ctx);
307+
atomic_store(ctx->stop, true); // Stop all other guys on error
259308
return NULL;
260309
}
261310

262-
static void _stream_release_buffer(us_stream_s *stream, us_hw_buffer_s *hw) {
263-
assert(!us_queue_put(stream->run->releasers[hw->buf.index].queue, hw, 0));
264-
}
265-
266-
static bool _stream_is_stopped(us_stream_s *stream) {
267-
us_stream_runtime_s *const run = stream->run;
268-
const bool stop = atomic_load(&run->stop);
269-
if (stop) {
270-
return true;
271-
}
272-
if (stream->exit_on_no_clients > 0) {
273-
const ldf now_ts = us_get_now_monotonic();
274-
const ull http_last_request_ts = atomic_load(&run->http_last_request_ts); // Seconds
275-
if (_stream_has_any_clients(stream)) {
276-
atomic_store(&run->http_last_request_ts, now_ts);
277-
} else if (http_last_request_ts + stream->exit_on_no_clients < now_ts) {
278-
US_LOG_INFO("No requests or HTTP/sink clients found in last %u seconds, exiting ...",
279-
stream->exit_on_no_clients);
280-
us_process_suicide();
281-
atomic_store(&run->http_last_request_ts, now_ts);
282-
}
283-
}
284-
return false;
285-
}
286-
287311
static bool _stream_has_any_clients(us_stream_s *stream) {
288312
const us_stream_runtime_s *const run = stream->run;
289313
return (
@@ -297,7 +321,7 @@ static bool _stream_has_any_clients(us_stream_s *stream) {
297321
static bool _stream_slowdown(us_stream_s *stream) {
298322
if (stream->slowdown) {
299323
unsigned count = 0;
300-
while (count < 10 && !_stream_is_stopped(stream) && !_stream_has_any_clients(stream)) {
324+
while (count < 10 && !atomic_load(&stream->run->stop) && !_stream_has_any_clients(stream)) {
301325
usleep(100000);
302326
++count;
303327
}
@@ -310,7 +334,9 @@ static int _stream_init_loop(us_stream_s *stream) {
310334
us_stream_runtime_s *const run = stream->run;
311335

312336
int access_errno = 0;
313-
while (!_stream_is_stopped(stream)) {
337+
while (!atomic_load(&stream->run->stop)) {
338+
_stream_check_suicide(stream);
339+
314340
unsigned width = stream->dev->run->width;
315341
unsigned height = stream->dev->run->height;
316342
if (width == 0 || height == 0) {
@@ -323,7 +349,10 @@ static int _stream_init_loop(us_stream_s *stream) {
323349
_stream_expose_frame(stream, NULL);
324350

325351
_SINK_PUT(raw_sink, run->blank->raw);
326-
_H264_PUT(run->blank->raw, false);
352+
353+
if (run->h264 != NULL) {
354+
us_h264_stream_process(run->h264, run->blank->raw, false);
355+
}
327356

328357
if (access(stream->dev->path, R_OK|W_OK) < 0) {
329358
if (access_errno != errno) {
@@ -394,7 +423,7 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame) {
394423

395424
int ri = -1;
396425
while (
397-
!_stream_is_stopped(stream)
426+
!atomic_load(&run->stop)
398427
&& ((ri = us_ring_producer_acquire(run->http_jpeg_ring, 0)) < 0)
399428
) {
400429
US_LOG_ERROR("Can't push JPEG to HTTP ring (no free slots)");
@@ -416,3 +445,20 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame) {
416445

417446
_SINK_PUT(jpeg_sink, (frame != NULL ? frame : run->blank->jpeg));
418447
}
448+
449+
static void _stream_check_suicide(us_stream_s *stream) {
450+
us_stream_runtime_s *const run = stream->run;
451+
if (stream->exit_on_no_clients <= 0) {
452+
return;
453+
}
454+
const ldf now_ts = us_get_now_monotonic();
455+
const ull http_last_request_ts = atomic_load(&run->http_last_request_ts); // Seconds
456+
if (_stream_has_any_clients(stream)) {
457+
atomic_store(&run->http_last_request_ts, now_ts);
458+
} else if (http_last_request_ts + stream->exit_on_no_clients < now_ts) {
459+
US_LOG_INFO("No requests or HTTP/sink clients found in last %u seconds, exiting ...",
460+
stream->exit_on_no_clients);
461+
us_process_suicide();
462+
atomic_store(&run->http_last_request_ts, now_ts);
463+
}
464+
}

src/ustreamer/stream.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,7 @@
3838

3939

4040
typedef struct {
41-
pthread_t tid;
42-
us_queue_s *queue;
43-
} us_stream_releaser_s;
44-
45-
typedef struct {
46-
us_stream_releaser_s *releasers;
47-
pthread_mutex_t release_mutex;
48-
atomic_bool release_stop;
41+
us_h264_stream_s *h264;
4942

5043
us_ring_s *http_jpeg_ring;
5144
atomic_bool http_has_clients;
@@ -55,8 +48,6 @@ typedef struct {
5548
bool last_online;
5649
long double last_as_blank_ts;
5750

58-
us_h264_stream_s *h264;
59-
6051
us_blank_s *blank;
6152

6253
atomic_bool stop;

0 commit comments

Comments
 (0)