History

fftools/ffmpeg_sched: allow decoders to have multiple outputs

Will be useful for multilayer video.

Anton Khirnov committed 2 years ago

Commit 68c198f1 parent aef221b6 files · +100 −41Browse files

Showing the changes to fftools (6 of 6 files). Show all

Unified · Split

6 files
  1. fftools/ffmpeg_dec.c +5 −5
  2. fftools/ffmpeg_demux.c +1 −1
  3. fftools/ffmpeg_filter.c +2 −2
  4. fftools/ffmpeg_mux_init.c +1 −1
  5. fftools/ffmpeg_sched.c +75 −27
  6. fftools/ffmpeg_sched.h +16 −5
fftools/ffmpeg_dec.c+5−5
@@ -578,7 +578,7 @@ static int process_subtitle(DecoderPriv *dp, AVFrame *frame) · 577 unmodified lines
578 if (!subtitle)578 if (!subtitle)
579 return 0;579 return 0;
580 580
581 ret = sch_dec_send(dp->sch, dp->sch_idx, frame);581 ret = sch_dec_send(dp->sch, dp->sch_idx, 0, frame);
582 if (ret < 0)582 if (ret < 0)
583 av_frame_unref(frame);583 av_frame_unref(frame);
584 584
@@ -620,7 +620,7 @@ static int transcode_subtitles(DecoderPriv *dp, const AVPacket *pkt, · 35 unmodified lines
620 frame->time_base = pkt->time_base;620 frame->time_base = pkt->time_base;
621 frame->opaque = (void*)(intptr_t)FRAME_OPAQUE_SUB_HEARTBEAT;621 frame->opaque = (void*)(intptr_t)FRAME_OPAQUE_SUB_HEARTBEAT;
622 622
623 ret = sch_dec_send(dp->sch, dp->sch_idx, frame);623 ret = sch_dec_send(dp->sch, dp->sch_idx, 0, frame);
624 return ret == AVERROR_EOF ? AVERROR_EXIT : ret;624 return ret == AVERROR_EOF ? AVERROR_EXIT : ret;
625 } else if (pkt && (intptr_t)pkt->opaque == PKT_OPAQUE_FIX_SUB_DURATION) {625 } else if (pkt && (intptr_t)pkt->opaque == PKT_OPAQUE_FIX_SUB_DURATION) {
626 return fix_sub_duration_heartbeat(dp, av_rescale_q(pkt->pts, pkt->time_base,626 return fix_sub_duration_heartbeat(dp, av_rescale_q(pkt->pts, pkt->time_base,
@@ -773,7 +773,7 @@ static int packet_decode(DecoderPriv *dp, AVPacket *pkt, AVFrame *frame) · 146 unmodified lines
773 773
774 dp->dec.frames_decoded++;774 dp->dec.frames_decoded++;
775 775
776 ret = sch_dec_send(dp->sch, dp->sch_idx, frame);776 ret = sch_dec_send(dp->sch, dp->sch_idx, 0, frame);
777 if (ret < 0) {777 if (ret < 0) {
778 av_frame_unref(frame);778 av_frame_unref(frame);
779 return ret == AVERROR_EOF ? AVERROR_EXIT : ret;779 return ret == AVERROR_EOF ? AVERROR_EXIT : ret;
@@ -951,7 +951,7 @@ static int decoder_thread(void *arg) · 171 unmodified lines
951 dp->last_frame_pts + dp->last_frame_duration_est;951 dp->last_frame_pts + dp->last_frame_duration_est;
952 dt.frame->time_base = dp->last_frame_tb;952 dt.frame->time_base = dp->last_frame_tb;
953 953
954 ret = sch_dec_send(dp->sch, dp->sch_idx, dt.frame);954 ret = sch_dec_send(dp->sch, dp->sch_idx, 0, dt.frame);
955 if (ret < 0 && ret != AVERROR_EOF) {955 if (ret < 0 && ret != AVERROR_EOF) {
956 av_log(dp, AV_LOG_FATAL,956 av_log(dp, AV_LOG_FATAL,
957 "Error signalling EOF timestamp: %s\n", av_err2str(ret));957 "Error signalling EOF timestamp: %s\n", av_err2str(ret));
@@ -1355,7 +1355,7 @@ int dec_create(const OptionsContext *o, const char *arg, Scheduler *sch) · 397 unmodified lines
1355 return ret;1355 return ret;
1356 enc_idx = ret;1356 enc_idx = ret;
1357 1357
1358 ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC(dp->sch_idx));1358 ret = sch_connect(sch, SCH_ENC(enc_idx), SCH_DEC_IN(dp->sch_idx));
1359 if (ret < 0)1359 if (ret < 0)
1360 return ret;1360 return ret;
1361 1361
fftools/ffmpeg_demux.c+1−1
@@ -954,7 +954,7 @@ static int ist_use(InputStream *ist, int decoding_needed) · 953 unmodified lines
954 ds->sch_idx_dec = ret;954 ds->sch_idx_dec = ret;
955 955
956 ret = sch_connect(d->sch, SCH_DSTREAM(d->f.index, ds->sch_idx_stream),956 ret = sch_connect(d->sch, SCH_DSTREAM(d->f.index, ds->sch_idx_stream),
957 SCH_DEC(ds->sch_idx_dec));957 SCH_DEC_IN(ds->sch_idx_dec));
958 if (ret < 0)958 if (ret < 0)
959 return ret;959 return ret;
960 960
fftools/ffmpeg_filter.c+2−2
@@ -686,7 +686,7 @@ static int ifilter_bind_ist(InputFilter *ifilter, InputStream *ist) · 685 unmodified lines
686 if (dec_idx < 0)686 if (dec_idx < 0)
687 return dec_idx;687 return dec_idx;
688 688
689 ret = sch_connect(fgp->sch, SCH_DEC(dec_idx),689 ret = sch_connect(fgp->sch, SCH_DEC_OUT(dec_idx, 0),
690 SCH_FILTER_IN(fgp->sch_idx, ifp->index));690 SCH_FILTER_IN(fgp->sch_idx, ifp->index));
691 if (ret < 0)691 if (ret < 0)
692 return ret;692 return ret;
@@ -732,7 +732,7 @@ static int ifilter_bind_dec(InputFilterPriv *ifp, Decoder *dec) · 39 unmodified lines
732 if (dec_idx < 0)732 if (dec_idx < 0)
733 return dec_idx;733 return dec_idx;
734 734
735 ret = sch_connect(fgp->sch, SCH_DEC(dec_idx),735 ret = sch_connect(fgp->sch, SCH_DEC_OUT(dec_idx, 0),
736 SCH_FILTER_IN(fgp->sch_idx, ifp->index));736 SCH_FILTER_IN(fgp->sch_idx, ifp->index));
737 if (ret < 0)737 if (ret < 0)
738 return ret;738 return ret;
fftools/ffmpeg_mux_init.c+1−1
@@ -1511,7 +1511,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, · 1510 unmodified lines
1511 ms->sch_idx_src = sched_idx;1511 ms->sch_idx_src = sched_idx;
1512 1512
1513 if (ost->enc) {1513 if (ost->enc) {
1514 ret = sch_connect(mux->sch, SCH_DEC(sched_idx),1514 ret = sch_connect(mux->sch, SCH_DEC_OUT(sched_idx, 0),
1515 SCH_ENC(ms->sch_idx_enc));1515 SCH_ENC(ms->sch_idx_enc));
1516 if (ret < 0)1516 if (ret < 0)
1517 goto fail;1517 goto fail;
fftools/ffmpeg_sched.c+75−27
@@ -71,13 +71,19 @@ typedef struct SchTask { · 70 unmodified lines
71 int thread_running;71 int thread_running;
72} SchTask;72} SchTask;
73 73
74typedef struct SchDecOutput {
75 SchedulerNode *dst;
76 uint8_t *dst_finished;
77 unsigned nb_dst;
78} SchDecOutput;
79
74typedef struct SchDec {80typedef struct SchDec {
75 const AVClass *class;81 const AVClass *class;
76 82
77 SchedulerNode src;83 SchedulerNode src;
78 SchedulerNode *dst;84
79 uint8_t *dst_finished;85 SchDecOutput *outputs;
80 unsigned nb_dst;86 unsigned nb_outputs;
81 87
82 SchTask task;88 SchTask task;
83 // Queue for receiving input packets, one stream.89 // Queue for receiving input packets, one stream.
@@ -513,8 +519,14 @@ void sch_free(Scheduler **psch) · 429 unmodified lines
513 519
514 av_thread_message_queue_free(&dec->queue_end_ts);520 av_thread_message_queue_free(&dec->queue_end_ts);
515 521
516 av_freep(&dec->dst);522 for (unsigned j = 0; j < dec->nb_outputs; j++) {
517 av_freep(&dec->dst_finished);523 SchDecOutput *o = &dec->outputs[j];
524
525 av_freep(&o->dst);
526 av_freep(&o->dst_finished);
527 }
528
529 av_freep(&dec->outputs);
518 530
519 av_frame_free(&dec->send_frame);531 av_frame_free(&dec->send_frame);
520 }532 }
@@ -712,14 +724,28 @@ int sch_add_demux_stream(Scheduler *sch, unsigned demux_idx) · 191 unmodified lines
712 return ret < 0 ? ret : d->nb_streams - 1;724 return ret < 0 ? ret : d->nb_streams - 1;
713}725}
714 726
727int sch_add_dec_output(Scheduler *sch, unsigned dec_idx)
728{
729 SchDec *dec;
730 int ret;
731
732 av_assert0(dec_idx < sch->nb_dec);
733 dec = &sch->dec[dec_idx];
734
735 ret = GROW_ARRAY(dec->outputs, dec->nb_outputs);
736 if (ret < 0)
737 return ret;
738
739 return dec->nb_outputs - 1;
740}
741
715static const AVClass sch_dec_class = {742static const AVClass sch_dec_class = {
716 .class_name = "SchDec",743 .class_name = "SchDec",
717 .version = LIBAVUTIL_VERSION_INT,744 .version = LIBAVUTIL_VERSION_INT,
718 .parent_log_context_offset = offsetof(SchDec, task.func_arg),745 .parent_log_context_offset = offsetof(SchDec, task.func_arg),
719};746};
720 747
721int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx,748int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, int send_end_ts)
722 int send_end_ts)
723{749{
724 const unsigned idx = sch->nb_dec;750 const unsigned idx = sch->nb_dec;
725 751
@@ -739,6 +765,10 @@ int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, · 13 unmodified lines
739 if (!dec->send_frame)765 if (!dec->send_frame)
740 return AVERROR(ENOMEM);766 return AVERROR(ENOMEM);
741 767
768 ret = sch_add_dec_output(sch, idx);
769 if (ret < 0)
770 return ret;
771
742 ret = queue_alloc(&dec->queue, 1, 0, QUEUE_PACKETS);772 ret = queue_alloc(&dec->queue, 1, 0, QUEUE_PACKETS);
743 if (ret < 0)773 if (ret < 0)
744 return ret;774 return ret;
@@ -943,15 +973,19 @@ int sch_connect(Scheduler *sch, SchedulerNode src, SchedulerNode dst) · 198 unmodified lines
943 }973 }
944 case SCH_NODE_TYPE_DEC: {974 case SCH_NODE_TYPE_DEC: {
945 SchDec *dec;975 SchDec *dec;
976 SchDecOutput *o;
946 977
947 av_assert0(src.idx < sch->nb_dec);978 av_assert0(src.idx < sch->nb_dec);
948 dec = &sch->dec[src.idx];979 dec = &sch->dec[src.idx];
949 980
950 ret = GROW_ARRAY(dec->dst, dec->nb_dst);981 av_assert0(src.idx_stream < dec->nb_outputs);
982 o = &dec->outputs[src.idx_stream];
983
984 ret = GROW_ARRAY(o->dst, o->nb_dst);
951 if (ret < 0)985 if (ret < 0)
952 return ret;986 return ret;
953 987
954 dec->dst[dec->nb_dst - 1] = dst;988 o->dst[o->nb_dst - 1] = dst;
955 989
956 // decoded frames go to filters or encoding990 // decoded frames go to filters or encoding
957 switch (dst.type) {991 switch (dst.type) {
@@ -1417,15 +1451,20 @@ static int start_prepare(Scheduler *sch) · 459 unmodified lines
1417 "Decoder not connected to a source\n");1451 "Decoder not connected to a source\n");
1418 return AVERROR(EINVAL);1452 return AVERROR(EINVAL);
1419 }1453 }
1420 if (!dec->nb_dst) {
1421 av_log(dec, AV_LOG_ERROR,
1422 "Decoder not connected to any sink\n");
1423 return AVERROR(EINVAL);
1424 }
1425 1454
1426 dec->dst_finished = av_calloc(dec->nb_dst, sizeof(*dec->dst_finished));1455 for (unsigned j = 0; j < dec->nb_outputs; j++) {
1427 if (!dec->dst_finished)1456 SchDecOutput *o = &dec->outputs[j];
1428 return AVERROR(ENOMEM);1457
1458 if (!o->nb_dst) {
1459 av_log(dec, AV_LOG_ERROR,
1460 "Decoder output %u not connected to any sink\n", j);
1461 return AVERROR(EINVAL);
1462 }
1463
1464 o->dst_finished = av_calloc(o->nb_dst, sizeof(*o->dst_finished));
1465 if (!o->dst_finished)
1466 return AVERROR(ENOMEM);
1467 }
1429 }1468 }
1430 1469
1431 for (unsigned i = 0; i < sch->nb_enc; i++) {1470 for (unsigned i = 0; i < sch->nb_enc; i++) {
@@ -2171,21 +2210,26 @@ finish: · 739 unmodified lines
2171 return AVERROR_EOF;2210 return AVERROR_EOF;
2172}2211}
2173 2212
2174int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame)2213int sch_dec_send(Scheduler *sch, unsigned dec_idx,
2214 unsigned out_idx, AVFrame *frame)
2175{2215{
2176 SchDec *dec;2216 SchDec *dec;
2217 SchDecOutput *o;
2177 int ret;2218 int ret;
2178 unsigned nb_done = 0;2219 unsigned nb_done = 0;
2179 2220
2180 av_assert0(dec_idx < sch->nb_dec);2221 av_assert0(dec_idx < sch->nb_dec);
2181 dec = &sch->dec[dec_idx];2222 dec = &sch->dec[dec_idx];
2182 2223
2183 for (unsigned i = 0; i < dec->nb_dst; i++) {2224 av_assert0(out_idx < dec->nb_outputs);
2184 uint8_t *finished = &dec->dst_finished[i];2225 o = &dec->outputs[out_idx];
2226
2227 for (unsigned i = 0; i < o->nb_dst; i++) {
2228 uint8_t *finished = &o->dst_finished[i];
2185 AVFrame *to_send = frame;2229 AVFrame *to_send = frame;
2186 2230
2187 // sending a frame consumes it, so make a temporary reference if needed2231 // sending a frame consumes it, so make a temporary reference if needed
2188 if (i < dec->nb_dst - 1) {2232 if (i < o->nb_dst - 1) {
2189 to_send = dec->send_frame;2233 to_send = dec->send_frame;
2190 2234
2191 // frame may sometimes contain props only,2235 // frame may sometimes contain props only,
@@ -2196,7 +2240,7 @@ int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame) · 4 unmodified lines
2196 return ret;2240 return ret;
2197 }2241 }
2198 2242
2199 ret = dec_send_to_dst(sch, dec->dst[i], finished, to_send);2243 ret = dec_send_to_dst(sch, o->dst[i], finished, to_send);
2200 if (ret < 0) {2244 if (ret < 0) {
2201 av_frame_unref(to_send);2245 av_frame_unref(to_send);
2202 if (ret == AVERROR_EOF) {2246 if (ret == AVERROR_EOF) {
@@ -2207,7 +2251,7 @@ int sch_dec_send(Scheduler *sch, unsigned dec_idx, AVFrame *frame) · 4 unmodified lines
2207 }2251 }
2208 }2252 }
2209 2253
2210 return (nb_done == dec->nb_dst) ? AVERROR_EOF : 0;2254 return (nb_done == o->nb_dst) ? AVERROR_EOF : 0;
2211}2255}
2212 2256
2213static int dec_done(Scheduler *sch, unsigned dec_idx)2257static int dec_done(Scheduler *sch, unsigned dec_idx)
@@ -2222,10 +2266,14 @@ static int dec_done(Scheduler *sch, unsigned dec_idx) · 8 unmodified lines
2222 if (dec->queue_end_ts)2266 if (dec->queue_end_ts)
2223 av_thread_message_queue_set_err_recv(dec->queue_end_ts, AVERROR_EOF);2267 av_thread_message_queue_set_err_recv(dec->queue_end_ts, AVERROR_EOF);
2224 2268
2225 for (unsigned i = 0; i < dec->nb_dst; i++) {2269 for (unsigned i = 0; i < dec->nb_outputs; i++) {
2226 int err = dec_send_to_dst(sch, dec->dst[i], &dec->dst_finished[i], NULL);2270 SchDecOutput *o = &dec->outputs[i];
2227 if (err < 0 && err != AVERROR_EOF)2271
2228 ret = err_merge(ret, err);2272 for (unsigned j = 0; j < o->nb_dst; j++) {
2273 int err = dec_send_to_dst(sch, o->dst[j], &o->dst_finished[j], NULL);
2274 if (err < 0 && err != AVERROR_EOF)
2275 ret = err_merge(ret, err);
2276 }
2229 }2277 }
2230 2278
2231 return ret;2279 return ret;
fftools/ffmpeg_sched.h+16−5
@@ -114,9 +114,12 @@ typedef int (*SchThreadFunc)(void *arg); · 113 unmodified lines
114#define SCH_MSTREAM(file, stream) \114#define SCH_MSTREAM(file, stream) \
115 (SchedulerNode){ .type = SCH_NODE_TYPE_MUX, \115 (SchedulerNode){ .type = SCH_NODE_TYPE_MUX, \
116 .idx = file, .idx_stream = stream }116 .idx = file, .idx_stream = stream }
117#define SCH_DEC(decoder) \117#define SCH_DEC_IN(decoder) \
118 (SchedulerNode){ .type = SCH_NODE_TYPE_DEC, \118 (SchedulerNode){ .type = SCH_NODE_TYPE_DEC, \
119 .idx = decoder }119 .idx = decoder }
120#define SCH_DEC_OUT(decoder, out_idx) \
121 (SchedulerNode){ .type = SCH_NODE_TYPE_DEC, \
122 .idx = decoder, .idx_stream = out_idx }
120#define SCH_ENC(encoder) \123#define SCH_ENC(encoder) \
121 (SchedulerNode){ .type = SCH_NODE_TYPE_ENC, \124 (SchedulerNode){ .type = SCH_NODE_TYPE_ENC, \
122 .idx = encoder }125 .idx = encoder }
@@ -178,8 +181,15 @@ int sch_add_demux_stream(Scheduler *sch, unsigned demux_idx); · 55 unmodified lines
178 * @retval ">=0" Index of the newly-created decoder.181 * @retval ">=0" Index of the newly-created decoder.
179 * @retval "<0" Error code.182 * @retval "<0" Error code.
180 */183 */
181int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx,184int sch_add_dec(Scheduler *sch, SchThreadFunc func, void *ctx, int send_end_ts);
182 int send_end_ts);185
186/**
187 * Add another output to decoder (e.g. for multiview video).
188 *
189 * @retval ">=0" Index of the newly-added decoder output.
190 * @retval "<0" Error code.
191 */
192int sch_add_dec_output(Scheduler *sch, unsigned dec_idx);
183 193
184/**194/**
185 * Add a filtergraph to the scheduler.195 * Add a filtergraph to the scheduler.
@@ -379,7 +389,8 @@ int sch_dec_receive(Scheduler *sch, unsigned dec_idx, struct AVPacket *pkt); · 193 unmodified lines
379 * @retval AVERROR_EOF all consumers are done, should terminate decoding389 * @retval AVERROR_EOF all consumers are done, should terminate decoding
380 * @retval "another negative error code" other failure390 * @retval "another negative error code" other failure
381 */391 */
382int sch_dec_send(Scheduler *sch, unsigned dec_idx, struct AVFrame *frame);392int sch_dec_send(Scheduler *sch, unsigned dec_idx,
393 unsigned out_idx, struct AVFrame *frame);
383 394
384/**395/**
385 * Called by filtergraph tasks to obtain frames for filtering. Will wait for a396 * Called by filtergraph tasks to obtain frames for filtering. Will wait for a

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t