FFmpegKit iOS / macOS / tvOS API 5.1
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 ( tanersener gmail com )
4 *
5 * FFmpeg is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * FFmpeg is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with FFmpeg; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * This file is the modified version of ffmpeg_mux.c file living in ffmpeg source code under the fftools folder. We
22 * manually update it each time we depend on a new ffmpeg version. Below you can see the list of changes applied
23 * by us to develop the ffmpeg-kit library.
24 *
25 * ffmpeg-kit changes by Taner Sener
26 *
27 * 09.2022
28 * --------------------------------------------------------
29 * - fftools_ prefix added to fftools headers
30 * - using main_ffmpeg_return_code instead of main_return_code
31 * - printf replaced with av_log statements
32 */
33
34#include <stdio.h>
35#include <string.h>
36
37#include "fftools_ffmpeg.h"
38
39#include "libavutil/fifo.h"
40#include "libavutil/intreadwrite.h"
41#include "libavutil/log.h"
42#include "libavutil/mem.h"
43#include "libavutil/timestamp.h"
44
45#include "libavcodec/packet.h"
46
47#include "libavformat/avformat.h"
48#include "libavformat/avio.h"
49
50static void close_all_output_streams(OutputStream *ost, OSTFinished this_stream, OSTFinished others)
51{
52 int i;
53 for (i = 0; i < nb_output_streams; i++) {
55 ost2->finished |= ost == ost2 ? this_stream : others;
56 }
57}
58
59void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
60 int unqueue)
61{
62 AVFormatContext *s = of->ctx;
63 AVStream *st = ost->st;
64 int ret;
65
66 /*
67 * Audio encoders may split the packets -- #frames in != #packets out.
68 * But there is no reordering, so we can limit the number of output packets
69 * by simply dropping them here.
70 * Counting encoded video frames needs to be done separately because of
71 * reordering, see do_video_out().
72 * Do not count the packet when unqueued because it has been counted when queued.
73 */
74 if (!(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->encoding_needed) && !unqueue) {
75 if (ost->frame_number >= ost->max_frames) {
76 av_packet_unref(pkt);
77 return;
78 }
79 ost->frame_number++;
80 }
81
82 if (!of->header_written) {
83 AVPacket *tmp_pkt;
84 /* the muxer is not initialized yet, buffer the packet */
85 if (!av_fifo_can_write(ost->muxing_queue)) {
86 size_t cur_size = av_fifo_can_read(ost->muxing_queue);
87 unsigned int are_we_over_size =
89 size_t limit = are_we_over_size ? ost->max_muxing_queue_size : SIZE_MAX;
90 size_t new_size = FFMIN(2 * cur_size, limit);
91
92 if (new_size <= cur_size) {
93 av_log(NULL, AV_LOG_ERROR,
94 "Too many packets buffered for output stream %d:%d.\n",
95 ost->file_index, ost->st->index);
96 exit_program(1);
97 }
98 ret = av_fifo_grow2(ost->muxing_queue, new_size - cur_size);
99 if (ret < 0)
100 exit_program(1);
101 }
102 ret = av_packet_make_refcounted(pkt);
103 if (ret < 0)
104 exit_program(1);
105 tmp_pkt = av_packet_alloc();
106 if (!tmp_pkt)
107 exit_program(1);
108 av_packet_move_ref(tmp_pkt, pkt);
109 ost->muxing_queue_data_size += tmp_pkt->size;
110 av_fifo_write(ost->muxing_queue, &tmp_pkt, 1);
111 return;
112 }
113
114 if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) ||
115 (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_sync_method < 0))
116 pkt->pts = pkt->dts = AV_NOPTS_VALUE;
117
118 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
119 if (ost->frame_rate.num && ost->is_cfr) {
120 if (pkt->duration > 0)
121 av_log(NULL, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n");
122 pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate),
123 ost->mux_timebase);
124 }
125 }
126
127 av_packet_rescale_ts(pkt, ost->mux_timebase, ost->st->time_base);
128
129 if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
130 if (pkt->dts != AV_NOPTS_VALUE &&
131 pkt->pts != AV_NOPTS_VALUE &&
132 pkt->dts > pkt->pts) {
133 av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n",
134 pkt->dts, pkt->pts,
135 ost->file_index, ost->st->index);
136 pkt->pts =
137 pkt->dts = pkt->pts + pkt->dts + ost->last_mux_dts + 1
138 - FFMIN3(pkt->pts, pkt->dts, ost->last_mux_dts + 1)
139 - FFMAX3(pkt->pts, pkt->dts, ost->last_mux_dts + 1);
140 }
141 if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) &&
142 pkt->dts != AV_NOPTS_VALUE &&
143 ost->last_mux_dts != AV_NOPTS_VALUE) {
144 int64_t max = ost->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
145 if (pkt->dts < max) {
146 int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG;
147 if (exit_on_error)
148 loglevel = AV_LOG_ERROR;
149 av_log(s, loglevel, "Non-monotonous DTS in output stream "
150 "%d:%d; previous: %"PRId64", current: %"PRId64"; ",
151 ost->file_index, ost->st->index, ost->last_mux_dts, pkt->dts);
152 if (exit_on_error) {
153 av_log(NULL, AV_LOG_FATAL, "aborting.\n");
154 exit_program(1);
155 }
156 av_log(s, loglevel, "changing to %"PRId64". This may result "
157 "in incorrect timestamps in the output file.\n",
158 max);
159 if (pkt->pts >= pkt->dts)
160 pkt->pts = FFMAX(pkt->pts, max);
161 pkt->dts = max;
162 }
163 }
164 }
165 ost->last_mux_dts = pkt->dts;
166
167 ost->data_size += pkt->size;
168 ost->packets_written++;
169
170 pkt->stream_index = ost->index;
171
172 if (debug_ts) {
173 av_log(NULL, AV_LOG_INFO, "muxer <- type:%s "
174 "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n",
175 av_get_media_type_string(ost->enc_ctx->codec_type),
176 av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base),
177 av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base),
178 av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base),
179 pkt->size
180 );
181 }
182
183 ret = av_interleaved_write_frame(s, pkt);
184 if (ret < 0) {
185 print_error("av_interleaved_write_frame()", ret);
188 }
189}
190
191static int print_sdp(void)
192{
193 char sdp[16384];
194 int i;
195 int j, ret;
196 AVIOContext *sdp_pb;
197 AVFormatContext **avc;
198
199 for (i = 0; i < nb_output_files; i++) {
200 if (!output_files[i]->header_written)
201 return 0;
202 }
203
204 avc = av_malloc_array(nb_output_files, sizeof(*avc));
205 if (!avc)
206 exit_program(1);
207 for (i = 0, j = 0; i < nb_output_files; i++) {
208 if (!strcmp(output_files[i]->ctx->oformat->name, "rtp")) {
209 avc[j] = output_files[i]->ctx;
210 j++;
211 }
212 }
213
214 if (!j) {
215 av_log(NULL, AV_LOG_ERROR, "No output streams in the SDP.\n");
216 ret = AVERROR(EINVAL);
217 goto fail;
218 }
219
220 ret = av_sdp_create(avc, j, sdp, sizeof(sdp));
221 if (ret < 0)
222 goto fail;
223
224 if (!sdp_filename) {
225 av_log(NULL, AV_LOG_ERROR, "SDP:\n%s\n", sdp);
226 fflush(stdout);
227 } else {
228 ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL);
229 if (ret < 0) {
230 av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename);
231 goto fail;
232 }
233
234 avio_print(sdp_pb, sdp);
235 avio_closep(&sdp_pb);
236 av_freep(&sdp_filename);
237 }
238
239fail:
240 av_freep(&avc);
241 return ret;
242}
243
244/* open the muxer when all the streams are initialized */
246{
247 int ret, i;
248
249 for (i = 0; i < of->ctx->nb_streams; i++) {
250 OutputStream *ost = output_streams[of->ost_index + i];
251 if (!ost->initialized)
252 return 0;
253 }
254
255 ret = avformat_write_header(of->ctx, &of->opts);
256 if (ret < 0) {
257 av_log(NULL, AV_LOG_ERROR,
258 "Could not write header for output file #%d "
259 "(incorrect codec parameters ?): %s\n",
260 of->index, av_err2str(ret));
261 return ret;
262 }
263 //assert_avoptions(of->opts);
264 of->header_written = 1;
265
266 av_dump_format(of->ctx, of->index, of->ctx->url, 1);
268
269 if (sdp_filename || want_sdp) {
270 ret = print_sdp();
271 if (ret < 0) {
272 av_log(NULL, AV_LOG_ERROR, "Error writing the SDP.\n");
273 return ret;
274 }
275 }
276
277 /* flush the muxing queues */
278 for (i = 0; i < of->ctx->nb_streams; i++) {
279 OutputStream *ost = output_streams[of->ost_index + i];
280 AVPacket *pkt;
281
282 /* try to improve muxing time_base (only possible if nothing has been written yet) */
283 if (!av_fifo_can_read(ost->muxing_queue))
284 ost->mux_timebase = ost->st->time_base;
285
286 while (av_fifo_read(ost->muxing_queue, &pkt, 1) >= 0) {
287 ost->muxing_queue_data_size -= pkt->size;
288 of_write_packet(of, pkt, ost, 1);
289 av_packet_free(&pkt);
290 }
291 }
292
293 return 0;
294}
295
297{
298 int ret;
299
300 if (!of->header_written) {
301 av_log(NULL, AV_LOG_ERROR,
302 "Nothing was written into output file %d (%s), because "
303 "at least one of its streams received no packets.\n",
304 of->index, of->ctx->url);
305 return AVERROR(EINVAL);
306 }
307
308 ret = av_write_trailer(of->ctx);
309 if (ret < 0) {
310 av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s\n", of->ctx->url, av_err2str(ret));
311 return ret;
312 }
313
314 return 0;
315}
316
318{
319 OutputFile *of = *pof;
320 AVFormatContext *s;
321
322 if (!of)
323 return;
324
325 s = of->ctx;
326 if (s && s->oformat && !(s->oformat->flags & AVFMT_NOFILE))
327 avio_closep(&s->pb);
328 avformat_free_context(s);
329 av_dict_free(&of->opts);
330
331 av_freep(pof);
332}
void exit_program(int ret)
void print_error(const char *filename, int err)
__thread const AVIOInterruptCB int_cb
__thread OutputStream ** output_streams
__thread OutputFile ** output_files
__thread int nb_output_streams
__thread int nb_output_files
__thread unsigned nb_output_dumped
__thread int main_ffmpeg_return_code
__thread int want_sdp
__thread char * sdp_filename
OSTFinished
@ ENCODER_FINISHED
@ MUXER_FINISHED
__thread int audio_sync_method
__thread int exit_on_error
@ VSYNC_DROP
__thread int debug_ts
void of_close(OutputFile **pof)
int of_check_init(OutputFile *of)
int of_write_trailer(OutputFile *of)
static void close_all_output_streams(OutputStream *ost, OSTFinished this_stream, OSTFinished others)
static int print_sdp(void)
void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost, int unqueue)
AVFormatContext * ctx
AVDictionary * opts
int max_muxing_queue_size
int64_t last_mux_dts
AVRational mux_timebase
OSTFinished finished
uint64_t packets_written
int64_t max_frames
size_t muxing_queue_data_threshold
enum VideoSyncMethod vsync_method
int64_t frame_number
AVRational frame_rate
AVCodecContext * enc_ctx
uint64_t data_size
AVStream * st
AVFifo * muxing_queue
size_t muxing_queue_data_size