| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file is part of FFmpeg. | ||
| 3 | * | ||
| 4 | * FFmpeg is free software; you can redistribute it and/or | ||
| 5 | * modify it under the terms of the GNU Lesser General Public | ||
| 6 | * License as published by the Free Software Foundation; either | ||
| 7 | * version 2.1 of the License, or (at your option) any later version. | ||
| 8 | * | ||
| 9 | * FFmpeg is distributed in the hope that it will be useful, | ||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 12 | * Lesser General Public License for more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU Lesser General Public | ||
| 15 | * License along with FFmpeg; if not, write to the Free Software | ||
| 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <stdint.h> | ||
| 20 | #include <string.h> | ||
| 21 | |||
| 22 | #include "libavutil/avassert.h" | ||
| 23 | #include "libavutil/container_fifo.h" | ||
| 24 | #include "libavutil/channel_layout.h" | ||
| 25 | #include "libavutil/cpu.h" | ||
| 26 | #include "libavutil/error.h" | ||
| 27 | #include "libavutil/mathematics.h" | ||
| 28 | #include "libavutil/mem.h" | ||
| 29 | #include "libavutil/samplefmt.h" | ||
| 30 | #include "libavutil/timestamp.h" | ||
| 31 | |||
| 32 | #include "sync_queue.h" | ||
| 33 | |||
| 34 | /* | ||
| 35 | * How this works: | ||
| 36 | * -------------- | ||
| 37 | * time: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 | ||
| 38 | * ------------------------------------------------------------------- | ||
| 39 | * | | | | | | | | | | | | | | | ||
| 40 | * | ┌───┐┌────────┐┌───┐┌─────────────┐ | ||
| 41 | * stream 0| │d=1││ d=2 ││d=1││ d=3 │ | ||
| 42 | * | └───┘└────────┘└───┘└─────────────┘ | ||
| 43 | * ┌───┐ ┌───────────────────────┐ | ||
| 44 | * stream 1│d=1│ │ d=5 │ | ||
| 45 | * └───┘ └───────────────────────┘ | ||
| 46 | * | ┌───┐┌───┐┌───┐┌───┐ | ||
| 47 | * stream 2| │d=1││d=1││d=1││d=1│ <- stream 2 is the head stream of the queue | ||
| 48 | * | └───┘└───┘└───┘└───┘ | ||
| 49 | * ^ ^ | ||
| 50 | * [stream 2 tail] [stream 2 head] | ||
| 51 | * | ||
| 52 | * We have N streams (N=3 in the diagram), each stream is a FIFO. The *tail* of | ||
| 53 | * each FIFO is the frame with smallest end time, the *head* is the frame with | ||
| 54 | * the largest end time. Frames submitted to the queue with sq_send() are placed | ||
| 55 | * after the head, frames returned to the caller with sq_receive() are taken | ||
| 56 | * from the tail. | ||
| 57 | * | ||
| 58 | * The head stream of the whole queue (SyncQueue.head_stream) is the limiting | ||
| 59 | * stream with the *smallest* head timestamp, i.e. the stream whose source lags | ||
| 60 | * furthest behind all other streams. It determines which frames can be output | ||
| 61 | * from the queue. | ||
| 62 | * | ||
| 63 | * In the diagram, the head stream is 2, because it head time is t=5, while | ||
| 64 | * streams 0 and 1 end at t=8 and t=9 respectively. All frames that _end_ at | ||
| 65 | * or before t=5 can be output, i.e. the first 3 frames from stream 0, first | ||
| 66 | * frame from stream 1, and all 4 frames from stream 2. | ||
| 67 | */ | ||
| 68 | |||
| 69 | #define SQPTR(sq, frame) ((sq->type == SYNC_QUEUE_FRAMES) ? \ | ||
| 70 | (void*)frame.f : (void*)frame.p) | ||
| 71 | |||
| 72 | typedef struct SyncQueueStream { | ||
| 73 | AVContainerFifo *fifo; | ||
| 74 | AVRational tb; | ||
| 75 | |||
| 76 | /* number of audio samples in fifo */ | ||
| 77 | uint64_t samples_queued; | ||
| 78 | /* stream head: largest timestamp seen */ | ||
| 79 | int64_t head_ts; | ||
| 80 | int limiting; | ||
| 81 | /* no more frames will be sent for this stream */ | ||
| 82 | int finished; | ||
| 83 | |||
| 84 | uint64_t frames_sent; | ||
| 85 | uint64_t samples_sent; | ||
| 86 | uint64_t frames_max; | ||
| 87 | int frame_samples; | ||
| 88 | } SyncQueueStream; | ||
| 89 | |||
| 90 | struct SyncQueue { | ||
| 91 | enum SyncQueueType type; | ||
| 92 | |||
| 93 | void *logctx; | ||
| 94 | |||
| 95 | /* no more frames will be sent for any stream */ | ||
| 96 | int finished; | ||
| 97 | /* sync head: the stream with the _smallest_ head timestamp | ||
| 98 | * this stream determines which frames can be output */ | ||
| 99 | int head_stream; | ||
| 100 | /* the finished stream with the smallest finish timestamp or -1 */ | ||
| 101 | int head_finished_stream; | ||
| 102 | |||
| 103 | // maximum buffering duration in microseconds | ||
| 104 | int64_t buf_size_us; | ||
| 105 | |||
| 106 | SyncQueueStream *streams; | ||
| 107 | unsigned int nb_streams; | ||
| 108 | |||
| 109 | int have_limiting; | ||
| 110 | |||
| 111 | uintptr_t align_mask; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /** | ||
| 115 | * Compute the end timestamp of a frame. If nb_samples is provided, consider | ||
| 116 | * the frame to have this number of audio samples, otherwise use frame duration. | ||
| 117 | */ | ||
| 118 | 217491 | static int64_t frame_end(const SyncQueue *sq, SyncQueueFrame frame, int nb_samples) | |
| 119 | { | ||
| 120 |
2/2✓ Branch 0 taken 70439 times.
✓ Branch 1 taken 147052 times.
|
217491 | if (nb_samples) { |
| 121 | 70439 | int64_t d = av_rescale_q(nb_samples, (AVRational){ 1, frame.f->sample_rate}, | |
| 122 | 70439 | frame.f->time_base); | |
| 123 | 70439 | return frame.f->pts + d; | |
| 124 | } | ||
| 125 | |||
| 126 | 147052 | return (sq->type == SYNC_QUEUE_PACKETS) ? | |
| 127 |
2/2✓ Branch 0 taken 4456 times.
✓ Branch 1 taken 142596 times.
|
289648 | frame.p->pts + frame.p->duration : |
| 128 | 142596 | frame.f->pts + frame.f->duration; | |
| 129 | } | ||
| 130 | |||
| 131 | 68697 | static int frame_samples(const SyncQueue *sq, SyncQueueFrame frame) | |
| 132 | { | ||
| 133 |
2/2✓ Branch 0 taken 66024 times.
✓ Branch 1 taken 2673 times.
|
68697 | return (sq->type == SYNC_QUEUE_PACKETS) ? 0 : frame.f->nb_samples; |
| 134 | } | ||
| 135 | |||
| 136 | 39944 | static int frame_null(const SyncQueue *sq, SyncQueueFrame frame) | |
| 137 | { | ||
| 138 |
2/2✓ Branch 0 taken 958 times.
✓ Branch 1 taken 38986 times.
|
39944 | return (sq->type == SYNC_QUEUE_PACKETS) ? (frame.p == NULL) : (frame.f == NULL); |
| 139 | } | ||
| 140 | |||
| 141 | 33283 | static void tb_update(const SyncQueue *sq, SyncQueueStream *st, | |
| 142 | const SyncQueueFrame frame) | ||
| 143 | { | ||
| 144 | 66566 | AVRational tb = (sq->type == SYNC_QUEUE_PACKETS) ? | |
| 145 |
2/2✓ Branch 0 taken 923 times.
✓ Branch 1 taken 32360 times.
|
33283 | frame.p->time_base : frame.f->time_base; |
| 146 | |||
| 147 |
2/4✓ Branch 0 taken 33283 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 33283 times.
|
33283 | av_assert0(tb.num > 0 && tb.den > 0); |
| 148 | |||
| 149 |
4/4✓ Branch 0 taken 33167 times.
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 30012 times.
✓ Branch 3 taken 3155 times.
|
33283 | if (tb.num == st->tb.num && tb.den == st->tb.den) |
| 150 | 30012 | return; | |
| 151 | |||
| 152 | // timebase should not change after the first frame | ||
| 153 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3271 times.
|
3271 | av_assert0(!av_container_fifo_can_read(st->fifo)); |
| 154 | |||
| 155 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3270 times.
|
3271 | if (st->head_ts != AV_NOPTS_VALUE) |
| 156 | 1 | st->head_ts = av_rescale_q(st->head_ts, st->tb, tb); | |
| 157 | |||
| 158 | 3271 | st->tb = tb; | |
| 159 | } | ||
| 160 | |||
| 161 | 9727 | static void finish_stream(SyncQueue *sq, unsigned int stream_idx) | |
| 162 | { | ||
| 163 | 9727 | SyncQueueStream *st = &sq->streams[stream_idx]; | |
| 164 | |||
| 165 |
2/2✓ Branch 0 taken 3290 times.
✓ Branch 1 taken 6437 times.
|
9727 | if (!st->finished) |
| 166 | 3290 | av_log(sq->logctx, AV_LOG_DEBUG, | |
| 167 | "sq: finish %u; head ts %s\n", stream_idx, | ||
| 168 | 3290 | av_ts2timestr(st->head_ts, &st->tb)); | |
| 169 | |||
| 170 | 9727 | st->finished = 1; | |
| 171 | |||
| 172 |
4/4✓ Branch 0 taken 9225 times.
✓ Branch 1 taken 502 times.
✓ Branch 2 taken 9201 times.
✓ Branch 3 taken 24 times.
|
9727 | if (st->limiting && st->head_ts != AV_NOPTS_VALUE) { |
| 173 | /* check if this stream is the new finished head */ | ||
| 174 |
4/4✓ Branch 0 taken 6129 times.
✓ Branch 1 taken 3072 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 6128 times.
|
15330 | if (sq->head_finished_stream < 0 || |
| 175 | 6129 | av_compare_ts(st->head_ts, st->tb, | |
| 176 | 6129 | sq->streams[sq->head_finished_stream].head_ts, | |
| 177 | 6129 | sq->streams[sq->head_finished_stream].tb) < 0) { | |
| 178 | 3073 | sq->head_finished_stream = stream_idx; | |
| 179 | } | ||
| 180 | |||
| 181 | /* mark as finished all streams that should no longer receive new frames, | ||
| 182 | * due to them being ahead of some finished stream */ | ||
| 183 | 9201 | st = &sq->streams[sq->head_finished_stream]; | |
| 184 |
2/2✓ Branch 0 taken 9220 times.
✓ Branch 1 taken 9201 times.
|
18421 | for (unsigned int i = 0; i < sq->nb_streams; i++) { |
| 185 | 9220 | SyncQueueStream *st1 = &sq->streams[i]; | |
| 186 |
5/6✓ Branch 0 taken 19 times.
✓ Branch 1 taken 9201 times.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 5 times.
|
9239 | if (st != st1 && st1->head_ts != AV_NOPTS_VALUE && |
| 187 | 19 | av_compare_ts(st->head_ts, st->tb, st1->head_ts, st1->tb) <= 0) { | |
| 188 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
|
14 | if (!st1->finished) |
| 189 | 4 | av_log(sq->logctx, AV_LOG_DEBUG, | |
| 190 | "sq: finish secondary %u; head ts %s\n", i, | ||
| 191 | 4 | av_ts2timestr(st1->head_ts, &st1->tb)); | |
| 192 | |||
| 193 | 14 | st1->finished = 1; | |
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | /* mark the whole queue as finished if all streams are finished */ | ||
| 199 |
2/2✓ Branch 0 taken 10383 times.
✓ Branch 1 taken 9643 times.
|
20026 | for (unsigned int i = 0; i < sq->nb_streams; i++) { |
| 200 |
2/2✓ Branch 0 taken 84 times.
✓ Branch 1 taken 10299 times.
|
10383 | if (!sq->streams[i].finished) |
| 201 | 84 | return; | |
| 202 | } | ||
| 203 | 9643 | sq->finished = 1; | |
| 204 | |||
| 205 | 9643 | av_log(sq->logctx, AV_LOG_DEBUG, "sq: finish queue\n"); | |
| 206 | } | ||
| 207 | |||
| 208 | 12742 | static void queue_head_update(SyncQueue *sq) | |
| 209 | { | ||
| 210 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12742 times.
|
12742 | av_assert0(sq->have_limiting); |
| 211 | |||
| 212 |
2/2✓ Branch 0 taken 3156 times.
✓ Branch 1 taken 9586 times.
|
12742 | if (sq->head_stream < 0) { |
| 213 | 3156 | unsigned first_limiting = UINT_MAX; | |
| 214 | |||
| 215 | /* wait for one timestamp in each stream before determining | ||
| 216 | * the queue head */ | ||
| 217 |
2/2✓ Branch 0 taken 3248 times.
✓ Branch 1 taken 3072 times.
|
6320 | for (unsigned int i = 0; i < sq->nb_streams; i++) { |
| 218 | 3248 | SyncQueueStream *st = &sq->streams[i]; | |
| 219 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3244 times.
|
3248 | if (!st->limiting) |
| 220 | 4 | continue; | |
| 221 |
2/2✓ Branch 0 taken 84 times.
✓ Branch 1 taken 3160 times.
|
3244 | if (st->head_ts == AV_NOPTS_VALUE) |
| 222 | 84 | return; | |
| 223 |
2/2✓ Branch 0 taken 3156 times.
✓ Branch 1 taken 4 times.
|
3160 | if (first_limiting == UINT_MAX) |
| 224 | 3156 | first_limiting = i; | |
| 225 | } | ||
| 226 | |||
| 227 | // placeholder value, correct one will be found below | ||
| 228 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3072 times.
|
3072 | av_assert0(first_limiting < UINT_MAX); |
| 229 | 3072 | sq->head_stream = first_limiting; | |
| 230 | } | ||
| 231 | |||
| 232 |
2/2✓ Branch 0 taken 13034 times.
✓ Branch 1 taken 12658 times.
|
25692 | for (unsigned int i = 0; i < sq->nb_streams; i++) { |
| 233 | 13034 | SyncQueueStream *st_head = &sq->streams[sq->head_stream]; | |
| 234 | 13034 | SyncQueueStream *st_other = &sq->streams[i]; | |
| 235 |
5/6✓ Branch 0 taken 13000 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 13000 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 12993 times.
|
26034 | if (st_other->limiting && st_other->head_ts != AV_NOPTS_VALUE && |
| 236 | 13000 | av_compare_ts(st_other->head_ts, st_other->tb, | |
| 237 | st_head->head_ts, st_head->tb) < 0) | ||
| 238 | 7 | sq->head_stream = i; | |
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | /* update this stream's head timestamp */ | ||
| 243 | 33377 | static void stream_update_ts(SyncQueue *sq, unsigned int stream_idx, int64_t ts) | |
| 244 | { | ||
| 245 | 33377 | SyncQueueStream *st = &sq->streams[stream_idx]; | |
| 246 | |||
| 247 |
2/2✓ Branch 0 taken 33374 times.
✓ Branch 1 taken 3 times.
|
33377 | if (ts == AV_NOPTS_VALUE || |
| 248 |
4/4✓ Branch 0 taken 30106 times.
✓ Branch 1 taken 3268 times.
✓ Branch 2 taken 128 times.
✓ Branch 3 taken 29978 times.
|
33374 | (st->head_ts != AV_NOPTS_VALUE && st->head_ts >= ts)) |
| 249 | 131 | return; | |
| 250 | |||
| 251 | 33246 | st->head_ts = ts; | |
| 252 | |||
| 253 | /* if this stream is now ahead of some finished stream, then | ||
| 254 | * this stream is also finished */ | ||
| 255 |
4/4✓ Branch 0 taken 127 times.
✓ Branch 1 taken 33119 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 124 times.
|
33373 | if (sq->head_finished_stream >= 0 && |
| 256 | 127 | av_compare_ts(sq->streams[sq->head_finished_stream].head_ts, | |
| 257 | 127 | sq->streams[sq->head_finished_stream].tb, | |
| 258 | ts, st->tb) <= 0) | ||
| 259 | 3 | finish_stream(sq, stream_idx); | |
| 260 | |||
| 261 | /* update the overall head timestamp if it could have changed */ | ||
| 262 |
2/2✓ Branch 0 taken 13208 times.
✓ Branch 1 taken 20038 times.
|
33246 | if (st->limiting && |
| 263 |
4/4✓ Branch 0 taken 10052 times.
✓ Branch 1 taken 3156 times.
✓ Branch 2 taken 9586 times.
✓ Branch 3 taken 466 times.
|
13208 | (sq->head_stream < 0 || sq->head_stream == stream_idx)) |
| 264 | 12742 | queue_head_update(sq); | |
| 265 | } | ||
| 266 | |||
| 267 | /* If the queue for the given stream (or all streams when stream_idx=-1) | ||
| 268 | * is overflowing, trigger a fake heartbeat on lagging streams. | ||
| 269 | * | ||
| 270 | * @return 1 if heartbeat triggered, 0 otherwise | ||
| 271 | */ | ||
| 272 | 30394 | static int overflow_heartbeat(SyncQueue *sq, int stream_idx) | |
| 273 | { | ||
| 274 | SyncQueueStream *st; | ||
| 275 | SyncQueueFrame frame; | ||
| 276 | 30394 | int64_t tail_ts = AV_NOPTS_VALUE; | |
| 277 | |||
| 278 | /* if no stream specified, pick the one that is most ahead */ | ||
| 279 |
1/2✓ Branch 0 taken 30394 times.
✗ Branch 1 not taken.
|
30394 | if (stream_idx < 0) { |
| 280 | 30394 | int64_t ts = AV_NOPTS_VALUE; | |
| 281 | |||
| 282 |
2/2✓ Branch 0 taken 36303 times.
✓ Branch 1 taken 30394 times.
|
66697 | for (int i = 0; i < sq->nb_streams; i++) { |
| 283 | 36303 | st = &sq->streams[i]; | |
| 284 |
4/4✓ Branch 0 taken 35827 times.
✓ Branch 1 taken 476 times.
✓ Branch 2 taken 5436 times.
✓ Branch 3 taken 30391 times.
|
36303 | if (st->head_ts != AV_NOPTS_VALUE && |
| 285 |
2/2✓ Branch 0 taken 648 times.
✓ Branch 1 taken 4788 times.
|
5436 | (ts == AV_NOPTS_VALUE || |
| 286 | 5436 | av_compare_ts(ts, sq->streams[stream_idx].tb, | |
| 287 | st->head_ts, st->tb) < 0)) { | ||
| 288 | 31039 | ts = st->head_ts; | |
| 289 | 31039 | stream_idx = i; | |
| 290 | } | ||
| 291 | } | ||
| 292 | /* no stream has a timestamp yet -> nothing to do */ | ||
| 293 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 30391 times.
|
30394 | if (stream_idx < 0) |
| 294 | 3 | return 0; | |
| 295 | } | ||
| 296 | |||
| 297 | 30391 | st = &sq->streams[stream_idx]; | |
| 298 | |||
| 299 | /* get the chosen stream's tail timestamp */ | ||
| 300 |
4/4✓ Branch 0 taken 30391 times.
✓ Branch 1 taken 15429 times.
✓ Branch 2 taken 15429 times.
✓ Branch 3 taken 14962 times.
|
76211 | for (size_t i = 0; tail_ts == AV_NOPTS_VALUE && |
| 301 | 45820 | av_container_fifo_peek(st->fifo, (void**)&frame, i) >= 0; i++) | |
| 302 | 15429 | tail_ts = frame_end(sq, frame, 0); | |
| 303 | |||
| 304 | /* overflow triggers when the tail is over specified duration behind the head */ | ||
| 305 |
4/4✓ Branch 0 taken 15429 times.
✓ Branch 1 taken 14962 times.
✓ Branch 2 taken 1380 times.
✓ Branch 3 taken 14049 times.
|
30391 | if (tail_ts == AV_NOPTS_VALUE || tail_ts >= st->head_ts || |
| 306 |
2/2✓ Branch 0 taken 1286 times.
✓ Branch 1 taken 94 times.
|
1380 | av_rescale_q(st->head_ts - tail_ts, st->tb, AV_TIME_BASE_Q) < sq->buf_size_us) |
| 307 | 30297 | return 0; | |
| 308 | |||
| 309 | /* signal a fake timestamp for all streams that prevent tail_ts from being output */ | ||
| 310 | 94 | tail_ts++; | |
| 311 |
2/2✓ Branch 0 taken 188 times.
✓ Branch 1 taken 94 times.
|
282 | for (unsigned int i = 0; i < sq->nb_streams; i++) { |
| 312 | 188 | const SyncQueueStream *st1 = &sq->streams[i]; | |
| 313 | int64_t ts; | ||
| 314 | |||
| 315 |
3/4✓ Branch 0 taken 94 times.
✓ Branch 1 taken 94 times.
✓ Branch 2 taken 94 times.
✗ Branch 3 not taken.
|
188 | if (st == st1 || st1->finished || |
| 316 |
3/4✓ Branch 0 taken 93 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 93 times.
|
187 | (st1->head_ts != AV_NOPTS_VALUE && |
| 317 | 93 | av_compare_ts(tail_ts, st->tb, st1->head_ts, st1->tb) <= 0)) | |
| 318 | 94 | continue; | |
| 319 | |||
| 320 | 94 | ts = av_rescale_q(tail_ts, st->tb, st1->tb); | |
| 321 |
2/2✓ Branch 0 taken 93 times.
✓ Branch 1 taken 1 times.
|
94 | if (st1->head_ts != AV_NOPTS_VALUE) |
| 322 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
|
93 | ts = FFMAX(st1->head_ts + 1, ts); |
| 323 | |||
| 324 | 94 | av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u overflow heardbeat %s -> %s\n", | |
| 325 | 94 | i, av_ts2timestr(st1->head_ts, &st1->tb), av_ts2timestr(ts, &st1->tb)); | |
| 326 | |||
| 327 | 94 | stream_update_ts(sq, i, ts); | |
| 328 | } | ||
| 329 | |||
| 330 | 94 | return 1; | |
| 331 | } | ||
| 332 | |||
| 333 | 39944 | int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame) | |
| 334 | { | ||
| 335 | SyncQueueStream *st; | ||
| 336 | int64_t ts; | ||
| 337 | int ret, nb_samples; | ||
| 338 | |||
| 339 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39944 times.
|
39944 | av_assert0(stream_idx < sq->nb_streams); |
| 340 | 39944 | st = &sq->streams[stream_idx]; | |
| 341 | |||
| 342 |
2/2✓ Branch 1 taken 6637 times.
✓ Branch 2 taken 33307 times.
|
39944 | if (frame_null(sq, frame)) { |
| 343 | 6637 | av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u EOF\n", stream_idx); | |
| 344 | 6637 | finish_stream(sq, stream_idx); | |
| 345 | 6637 | return 0; | |
| 346 | } | ||
| 347 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 33283 times.
|
33307 | if (st->finished) |
| 348 | 24 | return AVERROR_EOF; | |
| 349 | |||
| 350 | 33283 | tb_update(sq, st, frame); | |
| 351 | |||
| 352 | 33283 | nb_samples = frame_samples(sq, frame); | |
| 353 | // make sure frame duration is consistent with sample count | ||
| 354 |
2/2✓ Branch 0 taken 23943 times.
✓ Branch 1 taken 9340 times.
|
33283 | if (nb_samples) { |
| 355 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23943 times.
|
23943 | av_assert0(frame.f->sample_rate > 0); |
| 356 | 23943 | frame.f->duration = av_rescale_q(nb_samples, (AVRational){ 1, frame.f->sample_rate }, | |
| 357 | 23943 | frame.f->time_base); | |
| 358 | } | ||
| 359 | |||
| 360 | 33283 | ts = frame_end(sq, frame, 0); | |
| 361 | |||
| 362 | 33283 | av_log(sq->logctx, AV_LOG_DEBUG, "sq: send %u ts %s\n", stream_idx, | |
| 363 | 33283 | av_ts2timestr(ts, &st->tb)); | |
| 364 | |||
| 365 |
2/2✓ Branch 0 taken 32360 times.
✓ Branch 1 taken 923 times.
|
33283 | ret = av_container_fifo_write(st->fifo, SQPTR(sq, frame), 0); |
| 366 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33283 times.
|
33283 | if (ret < 0) |
| 367 | ✗ | return ret; | |
| 368 | |||
| 369 | 33283 | stream_update_ts(sq, stream_idx, ts); | |
| 370 | |||
| 371 | 33283 | st->samples_queued += nb_samples; | |
| 372 | 33283 | st->samples_sent += nb_samples; | |
| 373 | |||
| 374 |
2/2✓ Branch 0 taken 19845 times.
✓ Branch 1 taken 13438 times.
|
33283 | if (st->frame_samples) |
| 375 | 19845 | st->frames_sent = st->samples_sent / st->frame_samples; | |
| 376 | else | ||
| 377 | 13438 | st->frames_sent++; | |
| 378 | |||
| 379 |
2/2✓ Branch 0 taken 3064 times.
✓ Branch 1 taken 30219 times.
|
33283 | if (st->frames_sent >= st->frames_max) { |
| 380 | 3064 | av_log(sq->logctx, AV_LOG_DEBUG, "sq: %u frames_max %"PRIu64" reached\n", | |
| 381 | stream_idx, st->frames_max); | ||
| 382 | |||
| 383 | 3064 | finish_stream(sq, stream_idx); | |
| 384 | } | ||
| 385 | |||
| 386 | 33283 | return 0; | |
| 387 | } | ||
| 388 | |||
| 389 | 64792 | static void offset_audio(AVFrame *f, int nb_samples) | |
| 390 | { | ||
| 391 | 64792 | const int planar = av_sample_fmt_is_planar(f->format); | |
| 392 |
2/2✓ Branch 0 taken 29060 times.
✓ Branch 1 taken 35732 times.
|
64792 | const int planes = planar ? f->ch_layout.nb_channels : 1; |
| 393 | 64792 | const int bps = av_get_bytes_per_sample(f->format); | |
| 394 |
2/2✓ Branch 0 taken 35732 times.
✓ Branch 1 taken 29060 times.
|
64792 | const int offset = nb_samples * bps * (planar ? 1 : f->ch_layout.nb_channels); |
| 395 | |||
| 396 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 64792 times.
|
64792 | av_assert0(bps > 0); |
| 397 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 64792 times.
|
64792 | av_assert0(nb_samples < f->nb_samples); |
| 398 | |||
| 399 |
2/2✓ Branch 0 taken 93306 times.
✓ Branch 1 taken 64792 times.
|
158098 | for (int i = 0; i < planes; i++) { |
| 400 | 93306 | f->extended_data[i] += offset; | |
| 401 |
1/2✓ Branch 0 taken 93306 times.
✗ Branch 1 not taken.
|
93306 | if (i < FF_ARRAY_ELEMS(f->data)) |
| 402 | 93306 | f->data[i] = f->extended_data[i]; | |
| 403 | } | ||
| 404 | 64792 | f->linesize[0] -= offset; | |
| 405 | 64792 | f->nb_samples -= nb_samples; | |
| 406 | 64792 | f->duration = av_rescale_q(f->nb_samples, (AVRational){ 1, f->sample_rate }, | |
| 407 | f->time_base); | ||
| 408 | 64792 | f->pts += av_rescale_q(nb_samples, (AVRational){ 1, f->sample_rate }, | |
| 409 | f->time_base); | ||
| 410 | 64792 | } | |
| 411 | |||
| 412 | 56604 | static int frame_is_aligned(const SyncQueue *sq, const AVFrame *frame) | |
| 413 | { | ||
| 414 | // only checks linesize[0], so only works for audio | ||
| 415 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 56604 times.
|
56604 | av_assert0(frame->nb_samples > 0); |
| 416 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 56604 times.
|
56604 | av_assert0(sq->align_mask); |
| 417 | |||
| 418 | // only check data[0], because we always offset all data pointers | ||
| 419 | // by the same offset, so if one is aligned, all are | ||
| 420 |
2/2✓ Branch 0 taken 56056 times.
✓ Branch 1 taken 548 times.
|
56604 | if (!((uintptr_t)frame->data[0] & sq->align_mask) && |
| 421 |
1/2✓ Branch 0 taken 56056 times.
✗ Branch 1 not taken.
|
56056 | !(frame->linesize[0] & sq->align_mask) && |
| 422 |
1/2✓ Branch 0 taken 56056 times.
✗ Branch 1 not taken.
|
56056 | frame->linesize[0] > sq->align_mask) |
| 423 | 56056 | return 1; | |
| 424 | |||
| 425 | 548 | return 0; | |
| 426 | } | ||
| 427 | |||
| 428 | 66073 | static int receive_samples(SyncQueue *sq, SyncQueueStream *st, | |
| 429 | AVFrame *dst, int nb_samples) | ||
| 430 | { | ||
| 431 | SyncQueueFrame src; | ||
| 432 | int ret; | ||
| 433 | |||
| 434 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 66073 times.
|
66073 | av_assert0(st->samples_queued >= nb_samples); |
| 435 | |||
| 436 | 66073 | ret = av_container_fifo_peek(st->fifo, (void**)&src, 0); | |
| 437 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 66073 times.
|
66073 | av_assert0(ret >= 0); |
| 438 | |||
| 439 | // peeked frame has enough samples and its data is aligned | ||
| 440 | // -> we can just make a reference and limit its sample count | ||
| 441 |
4/4✓ Branch 0 taken 52286 times.
✓ Branch 1 taken 13787 times.
✓ Branch 3 taken 51739 times.
✓ Branch 4 taken 547 times.
|
66073 | if (src.f->nb_samples > nb_samples && frame_is_aligned(sq, src.f)) { |
| 442 | 51739 | ret = av_frame_ref(dst, src.f); | |
| 443 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51739 times.
|
51739 | if (ret < 0) |
| 444 | ✗ | return ret; | |
| 445 | |||
| 446 | 51739 | dst->nb_samples = nb_samples; | |
| 447 | 51739 | offset_audio(src.f, nb_samples); | |
| 448 | 51739 | st->samples_queued -= nb_samples; | |
| 449 | |||
| 450 | 51739 | goto finish; | |
| 451 | } | ||
| 452 | |||
| 453 | // otherwise allocate a new frame and copy the data | ||
| 454 | 14334 | ret = av_channel_layout_copy(&dst->ch_layout, &src.f->ch_layout); | |
| 455 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14334 times.
|
14334 | if (ret < 0) |
| 456 | ✗ | return ret; | |
| 457 | |||
| 458 | 14334 | dst->format = src.f->format; | |
| 459 | 14334 | dst->nb_samples = nb_samples; | |
| 460 | |||
| 461 | 14334 | ret = av_frame_get_buffer(dst, 0); | |
| 462 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14334 times.
|
14334 | if (ret < 0) |
| 463 | ✗ | goto fail; | |
| 464 | |||
| 465 | 14334 | ret = av_frame_copy_props(dst, src.f); | |
| 466 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14334 times.
|
14334 | if (ret < 0) |
| 467 | ✗ | goto fail; | |
| 468 | |||
| 469 | 14334 | dst->nb_samples = 0; | |
| 470 |
2/2✓ Branch 0 taken 28577 times.
✓ Branch 1 taken 14334 times.
|
42911 | while (dst->nb_samples < nb_samples) { |
| 471 | int to_copy; | ||
| 472 | |||
| 473 | 28577 | ret = av_container_fifo_peek(st->fifo, (void**)&src, 0); | |
| 474 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28577 times.
|
28577 | av_assert0(ret >= 0); |
| 475 | |||
| 476 | 28577 | to_copy = FFMIN(nb_samples - dst->nb_samples, src.f->nb_samples); | |
| 477 | |||
| 478 | 28577 | av_samples_copy(dst->extended_data, src.f->extended_data, dst->nb_samples, | |
| 479 | 28577 | 0, to_copy, dst->ch_layout.nb_channels, dst->format); | |
| 480 | |||
| 481 |
2/2✓ Branch 0 taken 13053 times.
✓ Branch 1 taken 15524 times.
|
28577 | if (to_copy < src.f->nb_samples) |
| 482 | 13053 | offset_audio(src.f, to_copy); | |
| 483 | else | ||
| 484 | 15524 | av_container_fifo_drain(st->fifo, 1); | |
| 485 | |||
| 486 | 28577 | st->samples_queued -= to_copy; | |
| 487 | |||
| 488 | 28577 | dst->nb_samples += to_copy; | |
| 489 | } | ||
| 490 | |||
| 491 | 14334 | finish: | |
| 492 | 66073 | dst->duration = av_rescale_q(nb_samples, (AVRational){ 1, dst->sample_rate }, | |
| 493 | dst->time_base); | ||
| 494 | |||
| 495 | 66073 | return 0; | |
| 496 | |||
| 497 | ✗ | fail: | |
| 498 | ✗ | av_frame_unref(dst); | |
| 499 | ✗ | return ret; | |
| 500 | } | ||
| 501 | |||
| 502 | 132752 | static int receive_for_stream(SyncQueue *sq, unsigned int stream_idx, | |
| 503 | SyncQueueFrame frame) | ||
| 504 | { | ||
| 505 | 265504 | const SyncQueueStream *st_head = sq->head_stream >= 0 ? | |
| 506 |
2/2✓ Branch 0 taken 33853 times.
✓ Branch 1 taken 98899 times.
|
132752 | &sq->streams[sq->head_stream] : NULL; |
| 507 | SyncQueueStream *st; | ||
| 508 | |||
| 509 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 132752 times.
|
132752 | av_assert0(stream_idx < sq->nb_streams); |
| 510 | 132752 | st = &sq->streams[stream_idx]; | |
| 511 | |||
| 512 |
2/2✓ Branch 1 taken 104425 times.
✓ Branch 2 taken 28327 times.
|
132752 | if (av_container_fifo_can_read(st->fifo) && |
| 513 |
4/4✓ Branch 0 taken 19586 times.
✓ Branch 1 taken 84839 times.
✓ Branch 2 taken 160 times.
✓ Branch 3 taken 19426 times.
|
104425 | (st->frame_samples <= st->samples_queued || st->finished)) { |
| 514 | 84999 | int nb_samples = st->frame_samples; | |
| 515 | SyncQueueFrame peek; | ||
| 516 | int64_t ts; | ||
| 517 | 84999 | int cmp = 1; | |
| 518 | |||
| 519 |
2/2✓ Branch 0 taken 3553 times.
✓ Branch 1 taken 81446 times.
|
84999 | if (st->finished) |
| 520 | 3553 | nb_samples = FFMIN(nb_samples, st->samples_queued); | |
| 521 | |||
| 522 | 84999 | av_container_fifo_peek(st->fifo, (void**)&peek, 0); | |
| 523 | 84999 | ts = frame_end(sq, peek, nb_samples); | |
| 524 | |||
| 525 | /* check if this stream's tail timestamp does not overtake | ||
| 526 | * the overall queue head */ | ||
| 527 |
4/4✓ Branch 0 taken 84996 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 14292 times.
✓ Branch 3 taken 70704 times.
|
84999 | if (ts != AV_NOPTS_VALUE && st_head) |
| 528 | 14292 | cmp = av_compare_ts(ts, st->tb, st_head->head_ts, st_head->tb); | |
| 529 | |||
| 530 | /* We can release frames that do not end after the queue head. | ||
| 531 | * Frames with no timestamps are just passed through with no conditions. | ||
| 532 | * Frames are also passed through when there are no limiting streams. | ||
| 533 | */ | ||
| 534 |
6/6✓ Branch 0 taken 71840 times.
✓ Branch 1 taken 13159 times.
✓ Branch 2 taken 71837 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 70618 times.
✓ Branch 5 taken 1219 times.
|
84999 | if (cmp <= 0 || ts == AV_NOPTS_VALUE || !sq->have_limiting) { |
| 535 |
2/2✓ Branch 0 taken 70390 times.
✓ Branch 1 taken 13390 times.
|
83780 | if (nb_samples && |
| 536 |
4/4✓ Branch 0 taken 4318 times.
✓ Branch 1 taken 66072 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 4317 times.
|
136463 | (nb_samples != peek.f->nb_samples || !frame_is_aligned(sq, peek.f))) { |
| 537 | 66073 | int ret = receive_samples(sq, st, frame.f, nb_samples); | |
| 538 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 66073 times.
|
66073 | if (ret < 0) |
| 539 | ✗ | return ret; | |
| 540 | } else { | ||
| 541 |
2/2✓ Branch 0 taken 16832 times.
✓ Branch 1 taken 875 times.
|
17707 | int ret = av_container_fifo_read(st->fifo, SQPTR(sq, frame), 0); |
| 542 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17707 times.
|
17707 | av_assert0(ret >= 0); |
| 543 | |||
| 544 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 17707 times.
|
17707 | av_assert0(st->samples_queued >= frame_samples(sq, frame)); |
| 545 | 17707 | st->samples_queued -= frame_samples(sq, frame); | |
| 546 | } | ||
| 547 | |||
| 548 |
2/2✓ Branch 0 taken 13159 times.
✓ Branch 1 taken 70621 times.
|
167560 | av_log(sq->logctx, AV_LOG_DEBUG, |
| 549 | "sq: receive %u ts %s queue head %d ts %s\n", stream_idx, | ||
| 550 | 83780 | av_ts2timestr(frame_end(sq, frame, 0), &st->tb), | |
| 551 | sq->head_stream, | ||
| 552 | 13159 | st_head ? av_ts2timestr(st_head->head_ts, &st_head->tb) : "N/A"); | |
| 553 | |||
| 554 | 83780 | return 0; | |
| 555 | } | ||
| 556 | } | ||
| 557 | |||
| 558 |
4/4✓ Branch 0 taken 614 times.
✓ Branch 1 taken 38211 times.
✓ Branch 3 taken 390 times.
✓ Branch 4 taken 224 times.
|
38825 | return (sq->finished || (st->finished && !av_container_fifo_can_read(st->fifo))) ? |
| 559 |
2/2✓ Branch 0 taken 38825 times.
✓ Branch 1 taken 10147 times.
|
87797 | AVERROR_EOF : AVERROR(EAGAIN); |
| 560 | } | ||
| 561 | |||
| 562 | 123794 | static int receive_internal(SyncQueue *sq, int stream_idx, SyncQueueFrame frame) | |
| 563 | { | ||
| 564 | 123794 | int nb_eof = 0; | |
| 565 | int ret; | ||
| 566 | |||
| 567 | /* read a frame for a specific stream */ | ||
| 568 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 123794 times.
|
123794 | if (stream_idx >= 0) { |
| 569 | ✗ | ret = receive_for_stream(sq, stream_idx, frame); | |
| 570 | ✗ | return (ret < 0) ? ret : stream_idx; | |
| 571 | } | ||
| 572 | |||
| 573 | /* read a frame for any stream with available output */ | ||
| 574 |
2/2✓ Branch 0 taken 132752 times.
✓ Branch 1 taken 40014 times.
|
172766 | for (unsigned int i = 0; i < sq->nb_streams; i++) { |
| 575 | 132752 | ret = receive_for_stream(sq, i, frame); | |
| 576 |
4/4✓ Branch 0 taken 122215 times.
✓ Branch 1 taken 10537 times.
✓ Branch 2 taken 38435 times.
✓ Branch 3 taken 83780 times.
|
132752 | if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { |
| 577 | 48972 | nb_eof += (ret == AVERROR_EOF); | |
| 578 | 48972 | continue; | |
| 579 | } | ||
| 580 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 83780 times.
|
83780 | return (ret < 0) ? ret : i; |
| 581 | } | ||
| 582 | |||
| 583 |
2/2✓ Branch 0 taken 9620 times.
✓ Branch 1 taken 30394 times.
|
40014 | return (nb_eof == sq->nb_streams) ? AVERROR_EOF : AVERROR(EAGAIN); |
| 584 | } | ||
| 585 | |||
| 586 | 123700 | int sq_receive(SyncQueue *sq, int stream_idx, SyncQueueFrame frame) | |
| 587 | { | ||
| 588 | 123700 | int ret = receive_internal(sq, stream_idx, frame); | |
| 589 | |||
| 590 | /* try again if the queue overflowed and triggered a fake heartbeat | ||
| 591 | * for lagging streams */ | ||
| 592 |
4/4✓ Branch 0 taken 30394 times.
✓ Branch 1 taken 93306 times.
✓ Branch 3 taken 94 times.
✓ Branch 4 taken 30300 times.
|
123700 | if (ret == AVERROR(EAGAIN) && overflow_heartbeat(sq, stream_idx)) |
| 593 | 94 | ret = receive_internal(sq, stream_idx, frame); | |
| 594 | |||
| 595 | 123700 | return ret; | |
| 596 | } | ||
| 597 | |||
| 598 | 3294 | int sq_add_stream(SyncQueue *sq, int limiting) | |
| 599 | { | ||
| 600 | SyncQueueStream *tmp, *st; | ||
| 601 | |||
| 602 | 3294 | tmp = av_realloc_array(sq->streams, sq->nb_streams + 1, sizeof(*sq->streams)); | |
| 603 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3294 times.
|
3294 | if (!tmp) |
| 604 | ✗ | return AVERROR(ENOMEM); | |
| 605 | 3294 | sq->streams = tmp; | |
| 606 | |||
| 607 | 3294 | st = &sq->streams[sq->nb_streams]; | |
| 608 | 3294 | memset(st, 0, sizeof(*st)); | |
| 609 | |||
| 610 | 6588 | st->fifo = (sq->type == SYNC_QUEUE_FRAMES) ? | |
| 611 |
2/2✓ Branch 0 taken 3248 times.
✓ Branch 1 taken 46 times.
|
3294 | av_container_fifo_alloc_avframe(0) : av_container_fifo_alloc_avpacket(0); |
| 612 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3294 times.
|
3294 | if (!st->fifo) |
| 613 | ✗ | return AVERROR(ENOMEM); | |
| 614 | |||
| 615 | /* we set a valid default, so that a pathological stream that never | ||
| 616 | * receives even a real timebase (and no frames) won't stall all other | ||
| 617 | * streams forever; cf. overflow_heartbeat() */ | ||
| 618 | 3294 | st->tb = (AVRational){ 1, 1 }; | |
| 619 | 3294 | st->head_ts = AV_NOPTS_VALUE; | |
| 620 | 3294 | st->frames_max = UINT64_MAX; | |
| 621 | 3294 | st->limiting = limiting; | |
| 622 | |||
| 623 | 3294 | sq->have_limiting |= limiting; | |
| 624 | |||
| 625 | 3294 | return sq->nb_streams++; | |
| 626 | } | ||
| 627 | |||
| 628 | 3089 | void sq_limit_frames(SyncQueue *sq, unsigned int stream_idx, uint64_t frames) | |
| 629 | { | ||
| 630 | SyncQueueStream *st; | ||
| 631 | |||
| 632 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3089 times.
|
3089 | av_assert0(stream_idx < sq->nb_streams); |
| 633 | 3089 | st = &sq->streams[stream_idx]; | |
| 634 | |||
| 635 | 3089 | st->frames_max = frames; | |
| 636 |
2/2✓ Branch 0 taken 23 times.
✓ Branch 1 taken 3066 times.
|
3089 | if (st->frames_sent >= st->frames_max) |
| 637 | 23 | finish_stream(sq, stream_idx); | |
| 638 | 3089 | } | |
| 639 | |||
| 640 | 174 | void sq_frame_samples(SyncQueue *sq, unsigned int stream_idx, | |
| 641 | int frame_samples) | ||
| 642 | { | ||
| 643 | SyncQueueStream *st; | ||
| 644 | |||
| 645 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 174 times.
|
174 | av_assert0(sq->type == SYNC_QUEUE_FRAMES); |
| 646 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 174 times.
|
174 | av_assert0(stream_idx < sq->nb_streams); |
| 647 | 174 | st = &sq->streams[stream_idx]; | |
| 648 | |||
| 649 | 174 | st->frame_samples = frame_samples; | |
| 650 | |||
| 651 | 174 | sq->align_mask = av_cpu_max_align() - 1; | |
| 652 | 174 | } | |
| 653 | |||
| 654 | 3204 | SyncQueue *sq_alloc(enum SyncQueueType type, int64_t buf_size_us, void *logctx) | |
| 655 | { | ||
| 656 | 3204 | SyncQueue *sq = av_mallocz(sizeof(*sq)); | |
| 657 | |||
| 658 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3204 times.
|
3204 | if (!sq) |
| 659 | ✗ | return NULL; | |
| 660 | |||
| 661 | 3204 | sq->type = type; | |
| 662 | 3204 | sq->buf_size_us = buf_size_us; | |
| 663 | 3204 | sq->logctx = logctx; | |
| 664 | |||
| 665 | 3204 | sq->head_stream = -1; | |
| 666 | 3204 | sq->head_finished_stream = -1; | |
| 667 | |||
| 668 | 3204 | return sq; | |
| 669 | } | ||
| 670 | |||
| 671 | 11646 | void sq_free(SyncQueue **psq) | |
| 672 | { | ||
| 673 | 11646 | SyncQueue *sq = *psq; | |
| 674 | |||
| 675 |
2/2✓ Branch 0 taken 8442 times.
✓ Branch 1 taken 3204 times.
|
11646 | if (!sq) |
| 676 | 8442 | return; | |
| 677 | |||
| 678 |
2/2✓ Branch 0 taken 3294 times.
✓ Branch 1 taken 3204 times.
|
6498 | for (unsigned int i = 0; i < sq->nb_streams; i++) |
| 679 | 3294 | av_container_fifo_free(&sq->streams[i].fifo); | |
| 680 | |||
| 681 | 3204 | av_freep(&sq->streams); | |
| 682 | |||
| 683 | 3204 | av_freep(psq); | |
| 684 | } | ||
| 685 |