26
26
#include <stdatomic.h>
27
27
#include <unistd.h>
28
28
#include <errno.h>
29
- #include <assert.h>
30
29
31
30
#include <pthread.h>
32
31
49
48
#endif
50
49
51
50
51
+ typedef struct {
52
+ pthread_t tid ;
53
+ us_queue_s * queue ;
54
+ us_stream_s * stream ;
55
+ atomic_bool * stop ;
56
+ } _jpeg_context_s ;
57
+
52
58
typedef struct {
53
59
pthread_t tid ;
54
60
us_device_s * dev ;
@@ -66,11 +72,11 @@ typedef struct {
66
72
} _releaser_context_s ;
67
73
68
74
75
+ static void * _jpeg_thread (void * v_ctx );
69
76
static void * _h264_thread (void * v_ctx );
70
77
static void * _releaser_thread (void * v_ctx );
71
78
72
79
static bool _stream_has_any_clients (us_stream_s * stream );
73
- static bool _stream_slowdown (us_stream_s * stream );
74
80
static int _stream_init_loop (us_stream_s * stream );
75
81
static void _stream_expose_frame (us_stream_s * stream , us_frame_s * frame );
76
82
static void _stream_check_suicide (us_stream_s * stream );
@@ -142,6 +148,13 @@ void us_stream_loop(us_stream_s *stream) {
142
148
US_THREAD_CREATE (releasers [index ].tid , _releaser_thread , & releasers [index ]);
143
149
}
144
150
151
+ _jpeg_context_s jpeg_ctx = {
152
+ .queue = us_queue_init (stream -> dev -> run -> n_bufs ),
153
+ .stream = stream ,
154
+ .stop = & threads_stop ,
155
+ };
156
+ US_THREAD_CREATE (jpeg_ctx .tid , _jpeg_thread , & jpeg_ctx );
157
+
145
158
_h264_context_s h264_ctx ;
146
159
if (run -> h264 != NULL ) {
147
160
h264_ctx .dev = stream -> dev ;
@@ -151,94 +164,51 @@ void us_stream_loop(us_stream_s *stream) {
151
164
US_THREAD_CREATE (h264_ctx .tid , _h264_thread , & h264_ctx );
152
165
}
153
166
154
- ldf grab_after = 0 ;
155
- uint fluency_passed = 0 ;
156
167
uint captured_fps_accum = 0 ;
157
168
sll captured_fps_ts = 0 ;
158
169
159
170
US_LOG_INFO ("Capturing ..." );
160
171
172
+ uint slowdown_count = 0 ;
161
173
while (!atomic_load (& run -> stop ) && !atomic_load (& threads_stop )) {
162
174
_stream_check_suicide (stream );
163
-
164
- US_SEP_DEBUG ('-' );
165
- US_LOG_DEBUG ("Waiting for worker ..." );
166
-
167
- us_worker_s * const ready_wr = us_workers_pool_wait (stream -> enc -> run -> pool );
168
- us_encoder_job_s * const ready_job = ready_wr -> job ;
169
-
170
- if (ready_job -> hw != NULL ) {
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 );
173
- ready_job -> hw = NULL ;
174
- if (ready_wr -> job_failed ) {
175
- // pass
176
- } else if (ready_wr -> job_timely ) {
177
- _stream_expose_frame (stream , ready_job -> dest );
178
- US_LOG_PERF ("##### Encoded JPEG exposed; worker=%s, latency=%.3Lf" ,
179
- ready_wr -> name , us_get_now_monotonic () - ready_job -> dest -> grab_ts );
180
- } else {
181
- US_LOG_PERF ("----- Encoded JPEG dropped; worker=%s" , ready_wr -> name );
182
- }
183
- }
184
-
185
- const bool h264_force_key = _stream_slowdown (stream );
186
- if (atomic_load (& run -> stop ) || atomic_load (& threads_stop )) {
187
- goto close ;
175
+ if (stream -> slowdown && slowdown_count > 0 && !_stream_has_any_clients (stream )) {
176
+ usleep (100 * 1000 );
177
+ slowdown_count = (slowdown_count + 1 ) % 10 ;
178
+ continue ;
188
179
}
189
180
190
181
us_hw_buffer_s * hw ;
191
182
const int buf_index = us_device_grab_buffer (stream -> dev , & hw );
192
183
switch (buf_index ) {
193
184
case -3 : continue ; // Broken frame
194
185
case -2 : // Persistent timeout
195
- case -1 : goto close ; // Any error
186
+ case -1 : goto close ; // Error
196
187
}
197
188
assert (buf_index >= 0 );
198
189
190
+ const sll now_sec_ts = us_floor_ms (us_get_now_monotonic ());
191
+ if (now_sec_ts != captured_fps_ts ) {
192
+ US_LOG_PERF_FPS ("A new second has come; captured_fps=%u" , captured_fps_accum );
193
+ atomic_store (& run -> http_captured_fps , captured_fps_accum );
194
+ captured_fps_accum = 0 ;
195
+ captured_fps_ts = now_sec_ts ;
196
+ }
197
+ captured_fps_accum += 1 ;
198
+
199
199
# ifdef WITH_GPIO
200
200
us_gpio_set_stream_online (true);
201
201
# endif
202
202
203
- const ldf now_ts = us_get_now_monotonic ();
204
-
205
- if (now_ts < grab_after ) {
206
- fluency_passed += 1 ;
207
- US_LOG_VERBOSE ("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf" ,
208
- fluency_passed , now_ts , grab_after );
209
- assert (!us_queue_put (releasers [hw -> buf .index ].queue , hw , 0 ));
210
- } else {
211
- int hw_busy = 1 ;
212
- if (run -> h264 != NULL ) {
213
- hw_busy += 1 ;
214
- }
215
- atomic_store (& hw -> busy , hw_busy );
216
-
217
- fluency_passed = 0 ;
218
-
219
- const sll now_sec_ts = us_floor_ms (now_ts );
220
- if (now_sec_ts != captured_fps_ts ) {
221
- US_LOG_PERF_FPS ("A new second has come; captured_fps=%u" , captured_fps_accum );
222
- atomic_store (& run -> http_captured_fps , captured_fps_accum );
223
- captured_fps_accum = 0 ;
224
- captured_fps_ts = now_sec_ts ;
225
- }
226
- captured_fps_accum += 1 ;
227
-
228
- const ldf fluency_delay = us_workers_pool_get_fluency_delay (stream -> enc -> run -> pool , ready_wr );
229
- grab_after = now_ts + fluency_delay ;
230
- US_LOG_VERBOSE ("Fluency: delay=%.03Lf, grab_after=%.03Lf" , fluency_delay , grab_after );
231
-
232
- ready_job -> hw = hw ;
233
- us_workers_pool_assign (stream -> enc -> run -> pool , ready_wr );
234
- US_LOG_DEBUG ("Assigned new frame in buffer=%d to worker=%s" , buf_index , ready_wr -> name );
235
-
236
- _SINK_PUT (raw_sink , & hw -> raw );
237
-
238
- if (run -> h264 != NULL ) {
239
- us_queue_put (h264_ctx .queue , hw , h264_force_key );
240
- }
203
+ us_device_buffer_incref (hw ); // JPEG
204
+ us_queue_put (jpeg_ctx .queue , hw , 0 );
205
+ if (run -> h264 != NULL ) {
206
+ us_device_buffer_incref (hw ); // H264
207
+ us_queue_put (h264_ctx .queue , hw , 0 );
241
208
}
209
+ us_queue_put (releasers [hw -> buf .index ].queue , hw , 0 ); // Plan to release
210
+
211
+ _SINK_PUT (raw_sink , & hw -> raw );
242
212
}
243
213
244
214
close :
@@ -249,6 +219,9 @@ void us_stream_loop(us_stream_s *stream) {
249
219
us_queue_destroy (h264_ctx .queue );
250
220
}
251
221
222
+ US_THREAD_JOIN (jpeg_ctx .tid );
223
+ us_queue_destroy (jpeg_ctx .queue );
224
+
252
225
for (uint index = 0 ; index < n_releasers ; ++ index ) {
253
226
US_THREAD_JOIN (releasers [index ].tid );
254
227
us_queue_destroy (releasers [index ].queue );
@@ -273,14 +246,66 @@ void us_stream_loop_break(us_stream_s *stream) {
273
246
atomic_store (& stream -> run -> stop , true);
274
247
}
275
248
249
+ static void * _jpeg_thread (void * v_ctx ) {
250
+ _jpeg_context_s * ctx = v_ctx ;
251
+ us_stream_s * stream = ctx -> stream ;
252
+
253
+ ldf grab_after = 0 ;
254
+ uint fluency_passed = 0 ;
255
+
256
+ while (!atomic_load (ctx -> stop )) {
257
+ us_worker_s * const ready_wr = us_workers_pool_wait (stream -> enc -> run -> pool );
258
+ us_encoder_job_s * const ready_job = ready_wr -> job ;
259
+
260
+ if (ready_job -> hw != NULL ) {
261
+ us_device_buffer_decref (ready_job -> hw );
262
+ ready_job -> hw = NULL ;
263
+ if (ready_wr -> job_failed ) {
264
+ // pass
265
+ } else if (ready_wr -> job_timely ) {
266
+ _stream_expose_frame (stream , ready_job -> dest );
267
+ US_LOG_PERF ("##### Encoded JPEG exposed; worker=%s, latency=%.3Lf" ,
268
+ ready_wr -> name , us_get_now_monotonic () - ready_job -> dest -> grab_ts );
269
+ } else {
270
+ US_LOG_PERF ("----- Encoded JPEG dropped; worker=%s" , ready_wr -> name );
271
+ }
272
+ }
273
+
274
+ us_hw_buffer_s * hw ;
275
+ if (us_queue_get (ctx -> queue , (void * * )& hw , 0.1 ) < 0 ) {
276
+ continue ;
277
+ }
278
+
279
+ const ldf now_ts = us_get_now_monotonic ();
280
+ if (now_ts < grab_after ) {
281
+ fluency_passed += 1 ;
282
+ US_LOG_VERBOSE ("Passed %u frames for fluency: now=%.03Lf, grab_after=%.03Lf" ,
283
+ fluency_passed , now_ts , grab_after );
284
+ us_device_buffer_decref (hw );
285
+ continue ;
286
+ }
287
+ fluency_passed = 0 ;
288
+
289
+ const ldf fluency_delay = us_workers_pool_get_fluency_delay (stream -> enc -> run -> pool , ready_wr );
290
+ grab_after = now_ts + fluency_delay ;
291
+ US_LOG_VERBOSE ("Fluency: delay=%.03Lf, grab_after=%.03Lf" , fluency_delay , grab_after );
292
+
293
+ ready_job -> hw = hw ;
294
+ us_workers_pool_assign (stream -> enc -> run -> pool , ready_wr );
295
+ US_LOG_DEBUG ("Assigned new frame in buffer=%d to worker=%s" , hw -> buf .index , ready_wr -> name );
296
+ }
297
+ return NULL ;
298
+ }
299
+
276
300
static void * _h264_thread (void * v_ctx ) {
277
301
_h264_context_s * ctx = v_ctx ;
278
302
while (!atomic_load (ctx -> stop )) {
279
303
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 );
304
+ if (us_queue_get (ctx -> queue , (void * * )& hw , 0.1 ) < 0 ) {
305
+ continue ;
283
306
}
307
+ us_h264_stream_process (ctx -> h264 , & hw -> raw , false);
308
+ us_device_buffer_decref (hw );
284
309
}
285
310
return NULL ;
286
311
}
@@ -289,21 +314,23 @@ static void *_releaser_thread(void *v_ctx) {
289
314
_releaser_context_s * ctx = v_ctx ;
290
315
while (!atomic_load (ctx -> stop )) {
291
316
us_hw_buffer_s * hw ;
292
- 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
- }
299
- US_MUTEX_LOCK (* ctx -> mutex );
300
- const int released = us_device_release_buffer (ctx -> dev , hw );
301
- US_MUTEX_UNLOCK (* ctx -> mutex );
302
- if (released < 0 ) {
303
- break ;
317
+ if (us_queue_get (ctx -> queue , (void * * )& hw , 0.1 ) < 0 ) {
318
+ continue ;
319
+ }
320
+ while (atomic_load (& hw -> refs ) > 0 ) {
321
+ if (atomic_load (ctx -> stop )) {
322
+ goto done ;
304
323
}
324
+ usleep (5 * 1000 );
325
+ }
326
+ US_MUTEX_LOCK (* ctx -> mutex );
327
+ const int released = us_device_release_buffer (ctx -> dev , hw );
328
+ US_MUTEX_UNLOCK (* ctx -> mutex );
329
+ if (released < 0 ) {
330
+ goto done ;
305
331
}
306
332
}
333
+ done :
307
334
atomic_store (ctx -> stop , true); // Stop all other guys on error
308
335
return NULL ;
309
336
}
@@ -318,18 +345,6 @@ static bool _stream_has_any_clients(us_stream_s *stream) {
318
345
);
319
346
}
320
347
321
- static bool _stream_slowdown (us_stream_s * stream ) {
322
- if (stream -> slowdown ) {
323
- unsigned count = 0 ;
324
- while (count < 10 && !atomic_load (& stream -> run -> stop ) && !_stream_has_any_clients (stream )) {
325
- usleep (100000 );
326
- ++ count ;
327
- }
328
- return (count >= 10 );
329
- }
330
- return false;
331
- }
332
-
333
348
static int _stream_init_loop (us_stream_s * stream ) {
334
349
us_stream_runtime_s * const run = stream -> run ;
335
350
@@ -448,7 +463,7 @@ static void _stream_expose_frame(us_stream_s *stream, us_frame_s *frame) {
448
463
449
464
static void _stream_check_suicide (us_stream_s * stream ) {
450
465
us_stream_runtime_s * const run = stream -> run ;
451
- if (stream -> exit_on_no_clients <= 0 ) {
466
+ if (stream -> exit_on_no_clients > 0 ) {
452
467
return ;
453
468
}
454
469
const ldf now_ts = us_get_now_monotonic ();
0 commit comments