FFmpegKit iOS / macOS / tvOS API 6.0
Loading...
Searching...
No Matches
fftools_ffmpeg_mux.c
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 * Copyright (c) 2022 Taner Sener
4 * Copyright (c) 2023 ARTHENICA LTD
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * This file is the modified version of ffmpeg_mux.c file living in ffmpeg source code under the fftools folder. We
23 * manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
24 * by us to develop the ffmpeg-kit library.
25 *
26 * ffmpeg-kit changes by ARTHENICA LTD
27 *
28 * 07.2023
29 * --------------------------------------------------------
30 * - FFmpeg 6.0 changes migrated
31 * - fftools header names updated
32 * - want_sdp marked as thread-local
33 * - ms_from_ost migrated from ffmpeg_mux.c and marked as non-static
34 *
35 * ffmpeg-kit changes by Taner Sener
36 *
37 * 09.2022
38 * --------------------------------------------------------
39 * - fftools_ prefix added to fftools headers
40 * - using main_ffmpeg_return_code instead of main_return_code
41 * - printf replaced with av_log statements
42 */
43
44#include <stdatomic.h>
45#include <stdio.h>
46#include <string.h>
47
48#include "fftools_ffmpeg.h"
49#include "fftools_ffmpeg_mux.h"
50#include "fftools_objpool.h"
51#include "fftools_sync_queue.h"
53
54#include "libavutil/fifo.h"
55#include "libavutil/intreadwrite.h"
56#include "libavutil/log.h"
57#include "libavutil/mem.h"
58#include "libavutil/timestamp.h"
59#include "libavutil/thread.h"
60
61#include "libavcodec/packet.h"
62
63#include "libavformat/avformat.h"
64#include "libavformat/avio.h"
65
66__thread int want_sdp = 1;
67
69{
70 return (MuxStream*)ost;
71}
72
74{
75 return (Muxer*)of;
76}
77
78static int64_t filesize(AVIOContext *pb)
79{
80 int64_t ret = -1;
81
82 if (pb) {
83 ret = avio_size(pb);
84 if (ret <= 0) // FIXME improve avio_size() so it works with non seekable output too
85 ret = avio_tell(pb);
86 }
87
88 return ret;
89}
90
91static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
92{
93 MuxStream *ms = ms_from_ost(ost);
94 AVFormatContext *s = mux->fc;
95 AVStream *st = ost->st;
96 int64_t fs;
97 uint64_t frame_num;
98 int ret;
99
100 fs = filesize(s->pb);
101 atomic_store(&mux->last_filesize, fs);
102 if (fs >= mux->limit_filesize) {
103 ret = AVERROR_EOF;
104 goto fail;
105 }
106
107 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP)
108 pkt->pts = pkt->dts = AV_NOPTS_VALUE;
109
110 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
111 if (ost->frame_rate.num && ost->is_cfr) {
112 if (pkt->duration > 0)
113 av_log(ost, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n");
114 pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate),
115 pkt->time_base);
116 }
117 }
118
119 av_packet_rescale_ts(pkt, pkt->time_base, ost->st->time_base);
120 pkt->time_base = ost->st->time_base;
121
122 if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
123 if (pkt->dts != AV_NOPTS_VALUE &&
124 pkt->pts != AV_NOPTS_VALUE &&
125 pkt->dts > pkt->pts) {
126 av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n",
127 pkt->dts, pkt->pts,
128 ost->file_index, ost->st->index);
129 pkt->pts =
130 pkt->dts = pkt->pts + pkt->dts + ms->last_mux_dts + 1
131 - FFMIN3(pkt->pts, pkt->dts, ms->last_mux_dts + 1)
132 - FFMAX3(pkt->pts, pkt->dts, ms->last_mux_dts + 1);
133 }
134 if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) &&
135 pkt->dts != AV_NOPTS_VALUE &&
136 ms->last_mux_dts != AV_NOPTS_VALUE) {
137 int64_t max = ms->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
138 if (pkt->dts < max) {
139 int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG;
140 if (exit_on_error)
141 loglevel = AV_LOG_ERROR;
142 av_log(s, loglevel, "Non-monotonous DTS in output stream "
143 "%d:%d; previous: %"PRId64", current: %"PRId64"; ",
144 ost->file_index, ost->st->index, ms->last_mux_dts, pkt->dts);
145 if (exit_on_error) {
146 ret = AVERROR(EINVAL);
147 goto fail;
148 }
149
150 av_log(s, loglevel, "changing to %"PRId64". This may result "
151 "in incorrect timestamps in the output file.\n",
152 max);
153 if (pkt->pts >= pkt->dts)
154 pkt->pts = FFMAX(pkt->pts, max);
155 pkt->dts = max;
156 }
157 }
158 }
159 ms->last_mux_dts = pkt->dts;
160
161 ost->data_size_mux += pkt->size;
162 frame_num = atomic_fetch_add(&ost->packets_written, 1);
163
164 pkt->stream_index = ost->index;
165
166 if (debug_ts) {
167 av_log(ost, AV_LOG_INFO, "muxer <- type:%s "
168 "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n",
169 av_get_media_type_string(st->codecpar->codec_type),
170 av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base),
171 av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base),
172 av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base),
173 pkt->size
174 );
175 }
176
177 if (ms->stats.io)
178 enc_stats_write(ost, &ms->stats, NULL, pkt, frame_num);
179
180 ret = av_interleaved_write_frame(s, pkt);
181 if (ret < 0) {
182 print_error("av_interleaved_write_frame()", ret);
183 goto fail;
184 }
185
186 return 0;
187fail:
188 av_packet_unref(pkt);
189 return ret;
190}
191
192static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int *stream_eof)
193{
194 OutputFile *of = &mux->of;
195
196 if (ost->sq_idx_mux >= 0) {
197 int ret = sq_send(mux->sq_mux, ost->sq_idx_mux, SQPKT(pkt));
198 if (ret < 0) {
199 if (ret == AVERROR_EOF)
200 *stream_eof = 1;
201
202 return ret;
203 }
204
205 while (1) {
206 ret = sq_receive(mux->sq_mux, -1, SQPKT(mux->sq_pkt));
207 if (ret < 0)
208 return (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) ? 0 : ret;
209
210 ret = write_packet(mux, of->streams[ret],
211 mux->sq_pkt);
212 if (ret < 0)
213 return ret;
214 }
215 } else if (pkt)
216 return write_packet(mux, ost, pkt);
217
218 return 0;
219}
220
222{
223 char name[16];
224 snprintf(name, sizeof(name), "mux%d:%s", of->index, of->format->name);
225 ff_thread_setname(name);
226}
227
228static void *muxer_thread(void *arg)
229{
230 Muxer *mux = arg;
231 OutputFile *of = &mux->of;
232 AVPacket *pkt = NULL;
233 int ret = 0;
234
235 pkt = av_packet_alloc();
236 if (!pkt) {
237 ret = AVERROR(ENOMEM);
238 goto finish;
239 }
240
241 thread_set_name(of);
242
243 while (1) {
244 OutputStream *ost;
245 int stream_idx, stream_eof = 0;
246
247 ret = tq_receive(mux->tq, &stream_idx, pkt);
248 if (stream_idx < 0) {
249 av_log(mux, AV_LOG_VERBOSE, "All streams finished\n");
250 ret = 0;
251 break;
252 }
253
254 ost = of->streams[stream_idx];
255 ret = sync_queue_process(mux, ost, ret < 0 ? NULL : pkt, &stream_eof);
256 av_packet_unref(pkt);
257 if (ret == AVERROR_EOF && stream_eof)
258 tq_receive_finish(mux->tq, stream_idx);
259 else if (ret < 0) {
260 av_log(mux, AV_LOG_ERROR, "Error muxing a packet\n");
261 break;
262 }
263 }
264
265finish:
266 av_packet_free(&pkt);
267
268 for (unsigned int i = 0; i < mux->fc->nb_streams; i++)
269 tq_receive_finish(mux->tq, i);
270
271 av_log(mux, AV_LOG_VERBOSE, "Terminating muxer thread\n");
272
273 return (void*)(intptr_t)ret;
274}
275
276static int thread_submit_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
277{
278 int ret = 0;
279
280 if (!pkt || ost->finished & MUXER_FINISHED)
281 goto finish;
282
283 ret = tq_send(mux->tq, ost->index, pkt);
284 if (ret < 0)
285 goto finish;
286
287 return 0;
288
289finish:
290 if (pkt)
291 av_packet_unref(pkt);
292
293 ost->finished |= MUXER_FINISHED;
294 tq_send_finish(mux->tq, ost->index);
295 return ret == AVERROR_EOF ? 0 : ret;
296}
297
298static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
299{
300 MuxStream *ms = ms_from_ost(ost);
301 AVPacket *tmp_pkt = NULL;
302 int ret;
303
304 if (!av_fifo_can_write(ms->muxing_queue)) {
305 size_t cur_size = av_fifo_can_read(ms->muxing_queue);
306 size_t pkt_size = pkt ? pkt->size : 0;
307 unsigned int are_we_over_size =
309 size_t limit = are_we_over_size ? ms->max_muxing_queue_size : SIZE_MAX;
310 size_t new_size = FFMIN(2 * cur_size, limit);
311
312 if (new_size <= cur_size) {
313 av_log(ost, AV_LOG_ERROR,
314 "Too many packets buffered for output stream %d:%d.\n",
315 ost->file_index, ost->st->index);
316 return AVERROR(ENOSPC);
317 }
318 ret = av_fifo_grow2(ms->muxing_queue, new_size - cur_size);
319 if (ret < 0)
320 return ret;
321 }
322
323 if (pkt) {
324 ret = av_packet_make_refcounted(pkt);
325 if (ret < 0)
326 return ret;
327
328 tmp_pkt = av_packet_alloc();
329 if (!tmp_pkt)
330 return AVERROR(ENOMEM);
331
332 av_packet_move_ref(tmp_pkt, pkt);
333 ms->muxing_queue_data_size += tmp_pkt->size;
334 }
335 av_fifo_write(ms->muxing_queue, &tmp_pkt, 1);
336
337 return 0;
338}
339
340static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost)
341{
342 int ret;
343
344 if (mux->tq) {
345 return thread_submit_packet(mux, ost, pkt);
346 } else {
347 /* the muxer is not initialized yet, buffer the packet */
348 ret = queue_packet(mux, ost, pkt);
349 if (ret < 0) {
350 if (pkt)
351 av_packet_unref(pkt);
352 return ret;
353 }
354 }
355
356 return 0;
357}
358
359void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof)
360{
361 Muxer *mux = mux_from_of(of);
362 MuxStream *ms = ms_from_ost(ost);
363 const char *err_msg;
364 int ret = 0;
365
366 if (!eof && pkt->dts != AV_NOPTS_VALUE)
367 ost->last_mux_dts = av_rescale_q(pkt->dts, pkt->time_base, AV_TIME_BASE_Q);
368
369 /* apply the output bitstream filters */
370 if (ms->bsf_ctx) {
371 int bsf_eof = 0;
372
373 ret = av_bsf_send_packet(ms->bsf_ctx, eof ? NULL : pkt);
374 if (ret < 0) {
375 err_msg = "submitting a packet for bitstream filtering";
376 goto fail;
377 }
378
379 while (!bsf_eof) {
380 ret = av_bsf_receive_packet(ms->bsf_ctx, pkt);
381 if (ret == AVERROR(EAGAIN))
382 return;
383 else if (ret == AVERROR_EOF)
384 bsf_eof = 1;
385 else if (ret < 0) {
386 err_msg = "applying bitstream filters to a packet";
387 goto fail;
388 }
389
390 ret = submit_packet(mux, bsf_eof ? NULL : pkt, ost);
391 if (ret < 0)
392 goto mux_fail;
393 }
394 } else {
395 ret = submit_packet(mux, eof ? NULL : pkt, ost);
396 if (ret < 0)
397 goto mux_fail;
398 }
399
400 return;
401
402mux_fail:
403 err_msg = "submitting a packet to the muxer";
404
405fail:
406 av_log(ost, AV_LOG_ERROR, "Error %s\n", err_msg);
407 if (exit_on_error)
408 exit_program(1);
409
410}
411
412static int thread_stop(Muxer *mux)
413{
414 void *ret;
415
416 if (!mux || !mux->tq)
417 return 0;
418
419 for (unsigned int i = 0; i < mux->fc->nb_streams; i++)
420 tq_send_finish(mux->tq, i);
421
422 pthread_join(mux->thread, &ret);
423
424 tq_free(&mux->tq);
425
426 return (int)(intptr_t)ret;
427}
428
429static void pkt_move(void *dst, void *src)
430{
431 av_packet_move_ref(dst, src);
432}
433
434static int thread_start(Muxer *mux)
435{
436 AVFormatContext *fc = mux->fc;
437 ObjPool *op;
438 int ret;
439
441 if (!op)
442 return AVERROR(ENOMEM);
443
444 mux->tq = tq_alloc(fc->nb_streams, mux->thread_queue_size, op, pkt_move);
445 if (!mux->tq) {
446 objpool_free(&op);
447 return AVERROR(ENOMEM);
448 }
449
450 ret = pthread_create(&mux->thread, NULL, muxer_thread, (void*)mux);
451 if (ret) {
452 tq_free(&mux->tq);
453 return AVERROR(ret);
454 }
455
456 /* flush the muxing queues */
457 for (int i = 0; i < fc->nb_streams; i++) {
458 OutputStream *ost = mux->of.streams[i];
459 MuxStream *ms = ms_from_ost(ost);
460 AVPacket *pkt;
461
462 /* try to improve muxing time_base (only possible if nothing has been written yet) */
463 if (!av_fifo_can_read(ms->muxing_queue))
464 ost->mux_timebase = ost->st->time_base;
465
466 while (av_fifo_read(ms->muxing_queue, &pkt, 1) >= 0) {
467 ret = thread_submit_packet(mux, ost, pkt);
468 if (pkt) {
469 ms->muxing_queue_data_size -= pkt->size;
470 av_packet_free(&pkt);
471 }
472 if (ret < 0)
473 return ret;
474 }
475 }
476
477 return 0;
478}
479
480static int print_sdp(void)
481{
482 char sdp[16384];
483 int i;
484 int j, ret;
485 AVIOContext *sdp_pb;
486 AVFormatContext **avc;
487
488 for (i = 0; i < nb_output_files; i++) {
489 if (!mux_from_of(output_files[i])->header_written)
490 return 0;
491 }
492
493 avc = av_malloc_array(nb_output_files, sizeof(*avc));
494 if (!avc)
495 return AVERROR(ENOMEM);
496 for (i = 0, j = 0; i < nb_output_files; i++) {
497 if (!strcmp(output_files[i]->format->name, "rtp")) {
498 avc[j] = mux_from_of(output_files[i])->fc;
499 j++;
500 }
501 }
502
503 if (!j) {
504 av_log(NULL, AV_LOG_ERROR, "No output streams in the SDP.\n");
505 ret = AVERROR(EINVAL);
506 goto fail;
507 }
508
509 ret = av_sdp_create(avc, j, sdp, sizeof(sdp));
510 if (ret < 0)
511 goto fail;
512
513 if (!sdp_filename) {
514 av_log(NULL, AV_LOG_ERROR, "SDP:\n%s\n", sdp);
515 fflush(stdout);
516 } else {
517 ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL);
518 if (ret < 0) {
519 av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename);
520 goto fail;
521 }
522
523 avio_print(sdp_pb, sdp);
524 avio_closep(&sdp_pb);
525 av_freep(&sdp_filename);
526 }
527
528 // SDP successfully written, allow muxer threads to start
529 ret = 1;
530
531fail:
532 av_freep(&avc);
533 return ret;
534}
535
537{
538 OutputFile *of = &mux->of;
539 AVFormatContext *fc = mux->fc;
540 int ret, i;
541
542 for (i = 0; i < fc->nb_streams; i++) {
543 OutputStream *ost = of->streams[i];
544 if (!ost->initialized)
545 return 0;
546 }
547
548 ret = avformat_write_header(fc, &mux->opts);
549 if (ret < 0) {
550 av_log(mux, AV_LOG_ERROR, "Could not write header (incorrect codec "
551 "parameters ?): %s\n", av_err2str(ret));
552 return ret;
553 }
554 //assert_avoptions(of->opts);
555 mux->header_written = 1;
556
557 av_dump_format(fc, of->index, fc->url, 1);
559
560 if (sdp_filename || want_sdp) {
561 ret = print_sdp();
562 if (ret < 0) {
563 av_log(NULL, AV_LOG_ERROR, "Error writing the SDP.\n");
564 return ret;
565 } else if (ret == 1) {
566 /* SDP is written only after all the muxers are ready, so now we
567 * start ALL the threads */
568 for (i = 0; i < nb_output_files; i++) {
570 if (ret < 0)
571 return ret;
572 }
573 }
574 } else {
575 ret = thread_start(mux_from_of(of));
576 if (ret < 0)
577 return ret;
578 }
579
580 return 0;
581}
582
583static int bsf_init(MuxStream *ms)
584{
585 OutputStream *ost = &ms->ost;
586 AVBSFContext *ctx = ms->bsf_ctx;
587 int ret;
588
589 if (!ctx)
590 return 0;
591
592 ret = avcodec_parameters_copy(ctx->par_in, ost->st->codecpar);
593 if (ret < 0)
594 return ret;
595
596 ctx->time_base_in = ost->st->time_base;
597
598 ret = av_bsf_init(ctx);
599 if (ret < 0) {
600 av_log(ms, AV_LOG_ERROR, "Error initializing bitstream filter: %s\n",
601 ctx->filter->name);
602 return ret;
603 }
604
605 ret = avcodec_parameters_copy(ost->st->codecpar, ctx->par_out);
606 if (ret < 0)
607 return ret;
608 ost->st->time_base = ctx->time_base_out;
609
610 return 0;
611}
612
614{
615 Muxer *mux = mux_from_of(of);
616 MuxStream *ms = ms_from_ost(ost);
617 int ret;
618
619 if (ost->sq_idx_mux >= 0)
620 sq_set_tb(mux->sq_mux, ost->sq_idx_mux, ost->mux_timebase);
621
622 /* initialize bitstream filters for the output stream
623 * needs to be done here, because the codec id for streamcopy is not
624 * known until now */
625 ret = bsf_init(ms);
626 if (ret < 0)
627 return ret;
628
629 ost->initialized = 1;
630
631 return mux_check_init(mux);
632}
633
635{
636 Muxer *mux = mux_from_of(of);
637 AVFormatContext *fc = mux->fc;
638 int ret;
639
640 if (!mux->tq) {
641 av_log(mux, AV_LOG_ERROR,
642 "Nothing was written into output file, because "
643 "at least one of its streams received no packets.\n");
644 return AVERROR(EINVAL);
645 }
646
647 ret = thread_stop(mux);
648 if (ret < 0)
650
651 ret = av_write_trailer(fc);
652 if (ret < 0) {
653 av_log(mux, AV_LOG_ERROR, "Error writing trailer: %s\n", av_err2str(ret));
654 return ret;
655 }
656
657 mux->last_filesize = filesize(fc->pb);
658
659 if (!(of->format->flags & AVFMT_NOFILE)) {
660 ret = avio_closep(&fc->pb);
661 if (ret < 0) {
662 av_log(mux, AV_LOG_ERROR, "Error closing file: %s\n", av_err2str(ret));
663 return ret;
664 }
665 }
666
667 return 0;
668}
669
670static void ost_free(OutputStream **post)
671{
672 OutputStream *ost = *post;
673 MuxStream *ms;
674
675 if (!ost)
676 return;
677 ms = ms_from_ost(ost);
678
679 if (ost->logfile) {
680 if (fclose(ost->logfile))
681 av_log(ms, AV_LOG_ERROR,
682 "Error closing logfile, loss of information possible: %s\n",
683 av_err2str(AVERROR(errno)));
684 ost->logfile = NULL;
685 }
686
687 if (ms->muxing_queue) {
688 AVPacket *pkt;
689 while (av_fifo_read(ms->muxing_queue, &pkt, 1) >= 0)
690 av_packet_free(&pkt);
691 av_fifo_freep2(&ms->muxing_queue);
692 }
693
694 av_bsf_free(&ms->bsf_ctx);
695
696 av_frame_free(&ost->filtered_frame);
697 av_frame_free(&ost->sq_frame);
698 av_frame_free(&ost->last_frame);
699 av_packet_free(&ost->pkt);
700 av_dict_free(&ost->encoder_opts);
701
702 av_freep(&ost->kf.pts);
703 av_expr_free(ost->kf.pexpr);
704
705 av_freep(&ost->avfilter);
706 av_freep(&ost->logfile_prefix);
707 av_freep(&ost->apad);
708
709#if FFMPEG_OPT_MAP_CHANNEL
710 av_freep(&ost->audio_channels_map);
711 ost->audio_channels_mapped = 0;
712#endif
713
714 av_dict_free(&ost->sws_dict);
715 av_dict_free(&ost->swr_opts);
716
717 if (ost->enc_ctx)
718 av_freep(&ost->enc_ctx->stats_in);
719 avcodec_free_context(&ost->enc_ctx);
720
721 for (int i = 0; i < ost->enc_stats_pre.nb_components; i++)
722 av_freep(&ost->enc_stats_pre.components[i].str);
723 av_freep(&ost->enc_stats_pre.components);
724
725 for (int i = 0; i < ost->enc_stats_post.nb_components; i++)
726 av_freep(&ost->enc_stats_post.components[i].str);
727 av_freep(&ost->enc_stats_post.components);
728
729 for (int i = 0; i < ms->stats.nb_components; i++)
730 av_freep(&ms->stats.components[i].str);
731 av_freep(&ms->stats.components);
732
733 av_freep(post);
734}
735
736static void fc_close(AVFormatContext **pfc)
737{
738 AVFormatContext *fc = *pfc;
739
740 if (!fc)
741 return;
742
743 if (!(fc->oformat->flags & AVFMT_NOFILE))
744 avio_closep(&fc->pb);
745 avformat_free_context(fc);
746
747 *pfc = NULL;
748}
749
751{
752 OutputFile *of = *pof;
753 Muxer *mux;
754
755 if (!of)
756 return;
757 mux = mux_from_of(of);
758
759 thread_stop(mux);
760
761 sq_free(&of->sq_encode);
762 sq_free(&mux->sq_mux);
763
764 for (int i = 0; i < of->nb_streams; i++)
765 ost_free(&of->streams[i]);
766 av_freep(&of->streams);
767
768 av_dict_free(&mux->opts);
769
770 av_packet_free(&mux->sq_pkt);
771
772 fc_close(&mux->fc);
773
774 av_freep(pof);
775}
776
778{
779 Muxer *mux = mux_from_of(of);
780 return atomic_load(&mux->last_filesize);
781}
void exit_program(int ret)
void print_error(const char *filename, int err)
__thread const AVIOInterruptCB int_cb
__thread OutputFile ** output_files
__thread int nb_output_files
__thread unsigned nb_output_dumped
__thread int main_ffmpeg_return_code
void enc_stats_write(OutputStream *ost, EncStats *es, const AVFrame *frame, const AVPacket *pkt, uint64_t frame_num)
__thread char * sdp_filename
@ MUXER_FINISHED
__thread int exit_on_error
@ VSYNC_DROP
__thread int debug_ts
static int64_t filesize(AVIOContext *pb)
static void fc_close(AVFormatContext **pfc)
void of_close(OutputFile **pof)
static int bsf_init(MuxStream *ms)
int64_t of_filesize(OutputFile *of)
static Muxer * mux_from_of(OutputFile *of)
void of_output_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int eof)
int of_write_trailer(OutputFile *of)
static void thread_set_name(OutputFile *of)
static int sync_queue_process(Muxer *mux, OutputStream *ost, AVPacket *pkt, int *stream_eof)
int of_stream_init(OutputFile *of, OutputStream *ost)
int mux_check_init(Muxer *mux)
static int queue_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
static int thread_submit_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
static int thread_stop(Muxer *mux)
static void pkt_move(void *dst, void *src)
static int print_sdp(void)
MuxStream * ms_from_ost(OutputStream *ost)
static void ost_free(OutputStream **post)
static int thread_start(Muxer *mux)
__thread int want_sdp
static void * muxer_thread(void *arg)
static int submit_packet(Muxer *mux, AVPacket *pkt, OutputStream *ost)
static int write_packet(Muxer *mux, OutputStream *ost, AVPacket *pkt)
ObjPool * objpool_alloc_packets(void)
void objpool_free(ObjPool **pop)
int sq_send(SyncQueue *sq, unsigned int stream_idx, SyncQueueFrame frame)
void sq_set_tb(SyncQueue *sq, unsigned int stream_idx, AVRational tb)
int sq_receive(SyncQueue *sq, int stream_idx, SyncQueueFrame frame)
void sq_free(SyncQueue **psq)
#define SQPKT(pkt)
void tq_send_finish(ThreadQueue *tq, unsigned int stream_idx)
int tq_send(ThreadQueue *tq, unsigned int stream_idx, void *data)
ThreadQueue * tq_alloc(unsigned int nb_streams, size_t queue_size, ObjPool *obj_pool, void(*obj_move)(void *dst, void *src))
int tq_receive(ThreadQueue *tq, int *stream_idx, void *data)
void tq_free(ThreadQueue **ptq)
void tq_receive_finish(ThreadQueue *tq, unsigned int stream_idx)
AVIOContext * io
EncStatsComponent * components
OutputStream ost
AVBSFContext * bsf_ctx
AVFifo * muxing_queue
size_t muxing_queue_data_size
size_t muxing_queue_data_threshold
AVDictionary * opts
OutputFile of
int thread_queue_size
atomic_int_least64_t last_filesize
AVPacket * sq_pkt
ThreadQueue * tq
SyncQueue * sq_mux
AVFormatContext * fc
pthread_t thread
int64_t limit_filesize
const AVOutputFormat * format
SyncQueue * sq_encode
OutputStream ** streams
uint64_t data_size_mux
AVDictionary * swr_opts
int64_t last_mux_dts
AVRational mux_timebase
OSTFinished finished
int * audio_channels_map
AVPacket * pkt
AVFrame * last_frame
EncStats enc_stats_pre
enum VideoSyncMethod vsync_method
KeyframeForceCtx kf
AVFrame * filtered_frame
AVFrame * sq_frame
AVRational frame_rate
AVCodecContext * enc_ctx
AVDictionary * encoder_opts
AVStream * st
atomic_uint_least64_t packets_written
EncStats enc_stats_post
AVDictionary * sws_dict
char * logfile_prefix