|
8 | 8 |
|
9 | 9 | #include "receiver-internal.h" |
10 | 10 |
|
| 11 | +/* ── Video output queue ───────────────────────────────────── */ |
| 12 | + |
| 13 | +static void video_queue_drain_locked(struct irl_source *ctx) |
| 14 | +{ |
| 15 | + while (ctx->video_queue_count > 0) { |
| 16 | + AVFrame *f = ctx->video_queue[ctx->video_queue_head]; |
| 17 | + ctx->video_queue[ctx->video_queue_head] = NULL; |
| 18 | + ctx->video_queue_head = |
| 19 | + (ctx->video_queue_head + 1) % IRL_VIDEO_QUEUE_SIZE; |
| 20 | + ctx->video_queue_count--; |
| 21 | + av_frame_free(&f); |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +void irl_video_queue_push(struct irl_source *ctx, AVFrame *frame, |
| 26 | + int64_t pts_ns) |
| 27 | +{ |
| 28 | + AVFrame *clone = av_frame_alloc(); |
| 29 | + if (!clone) |
| 30 | + return; |
| 31 | + if (av_frame_ref(clone, frame) < 0) { |
| 32 | + av_frame_free(&clone); |
| 33 | + return; |
| 34 | + } |
| 35 | + clone->pts = pts_ns; |
| 36 | + |
| 37 | + pthread_mutex_lock(&ctx->video_queue_lock); |
| 38 | + if (ctx->video_queue_count >= IRL_VIDEO_QUEUE_SIZE) { |
| 39 | + /* Video thread is stalled; keep the freshest frames and |
| 40 | + * never make the receiver (and therefore audio) wait. */ |
| 41 | + AVFrame *oldest = ctx->video_queue[ctx->video_queue_head]; |
| 42 | + ctx->video_queue[ctx->video_queue_head] = NULL; |
| 43 | + ctx->video_queue_head = |
| 44 | + (ctx->video_queue_head + 1) % IRL_VIDEO_QUEUE_SIZE; |
| 45 | + ctx->video_queue_count--; |
| 46 | + ctx->video_queue_drops++; |
| 47 | + av_frame_free(&oldest); |
| 48 | + } |
| 49 | + int tail = (ctx->video_queue_head + ctx->video_queue_count) % |
| 50 | + IRL_VIDEO_QUEUE_SIZE; |
| 51 | + ctx->video_queue[tail] = clone; |
| 52 | + ctx->video_queue_count++; |
| 53 | + pthread_cond_signal(&ctx->video_queue_cond); |
| 54 | + pthread_mutex_unlock(&ctx->video_queue_lock); |
| 55 | +} |
| 56 | + |
| 57 | +void *irl_video_thread(void *data) |
| 58 | +{ |
| 59 | + struct irl_source *ctx = data; |
| 60 | + |
| 61 | + pthread_mutex_lock(&ctx->video_queue_lock); |
| 62 | + while (os_atomic_load_bool(&ctx->thread_active)) { |
| 63 | + if (ctx->video_queue_count == 0) { |
| 64 | + pthread_cond_wait(&ctx->video_queue_cond, |
| 65 | + &ctx->video_queue_lock); |
| 66 | + continue; |
| 67 | + } |
| 68 | + AVFrame *f = ctx->video_queue[ctx->video_queue_head]; |
| 69 | + ctx->video_queue[ctx->video_queue_head] = NULL; |
| 70 | + ctx->video_queue_head = |
| 71 | + (ctx->video_queue_head + 1) % IRL_VIDEO_QUEUE_SIZE; |
| 72 | + ctx->video_queue_count--; |
| 73 | + pthread_mutex_unlock(&ctx->video_queue_lock); |
| 74 | + |
| 75 | + irl_video_output_frame(ctx, f); |
| 76 | + av_frame_free(&f); |
| 77 | + |
| 78 | + pthread_mutex_lock(&ctx->video_queue_lock); |
| 79 | + } |
| 80 | + video_queue_drain_locked(ctx); |
| 81 | + pthread_mutex_unlock(&ctx->video_queue_lock); |
| 82 | + return NULL; |
| 83 | +} |
| 84 | + |
| 85 | +/* ── Decoded frame handling (receiver thread) ─────────────── */ |
| 86 | + |
11 | 87 | static int64_t video_frame_pts(const AVFrame *frame) |
12 | 88 | { |
13 | 89 | if (frame->best_effort_timestamp != AV_NOPTS_VALUE) |
@@ -76,18 +152,22 @@ void irl_handle_video_frame(struct irl_source *ctx, AVFrame *frame) |
76 | 152 | ctx->last_video_width = frame->width; |
77 | 153 | ctx->last_video_height = frame->height; |
78 | 154 |
|
| 155 | + /* Convert PTS to nanoseconds here: the video thread must not |
| 156 | + * touch fmt_ctx, which this thread frees on reconnect while |
| 157 | + * queued frames may still be in flight. */ |
| 158 | + int64_t pts_ns = 0; |
79 | 159 | if (ctx->fmt_ctx && ctx->video_stream_idx >= 0) { |
80 | 160 | AVStream *vs = |
81 | 161 | ctx->fmt_ctx->streams[ctx->video_stream_idx]; |
82 | | - int64_t pts_ns = av_rescale_q(frame->pts, vs->time_base, |
83 | | - (AVRational){1, 1000000000}); |
| 162 | + pts_ns = av_rescale_q(frame->pts, vs->time_base, |
| 163 | + (AVRational){1, 1000000000}); |
84 | 164 | pthread_mutex_lock(&ctx->audio_state_lock); |
85 | 165 | ctx->latest_video_stream_pts_ns = pts_ns; |
86 | 166 | pthread_mutex_unlock(&ctx->audio_state_lock); |
87 | 167 | } |
88 | 168 |
|
89 | | - irl_video_output_frame(ctx, frame); |
| 169 | + irl_video_queue_push(ctx, frame, pts_ns); |
90 | 170 | ctx->total_video_frames++; |
91 | 171 | if (ctx->total_video_frames == 1) |
92 | | - blog(LOG_INFO, "[irl-source] First video frame output"); |
| 172 | + blog(LOG_INFO, "[irl-source] First video frame queued"); |
93 | 173 | } |
0 commit comments