FFmpegKit Linux 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 */
32
33#include <stdio.h>
34#include <string.h>
35
36#include "fftools_ffmpeg.h"
37
38#include "libavutil/fifo.h"
39#include "libavutil/intreadwrite.h"
40#include "libavutil/log.h"
41#include "libavutil/mem.h"
42#include "libavutil/timestamp.h"
43
44#include "libavcodec/packet.h"
45
46#include "libavformat/avformat.h"
47#include "libavformat/avio.h"
48
49static void close_all_output_streams(OutputStream *ost, OSTFinished this_stream, OSTFinished others)
50{
51 int i;
52 for (i = 0; i < nb_output_streams; i++) {
54 ost2->finished |= ost == ost2 ? this_stream : others;
55 }
56}
57
58void of_write_packet(OutputFile *of, AVPacket *pkt, OutputStream *ost,
59 int unqueue)
60{
61 AVFormatContext *s = of->ctx;
62 AVStream *st = ost->st;
63 int ret;
64
65 /*
66 * Audio encoders may split the packets -- #frames in != #packets out.
67 * But there is no reordering, so we can limit the number of output packets
68 * by simply dropping them here.
69 * Counting encoded video frames needs to be done separately because of
70 * reordering, see do_video_out().
71 * Do not count the packet when unqueued because it has been counted when queued.
72 */
73 if (!(st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->encoding_needed) && !unqueue) {
74 if (ost->frame_number >= ost->max_frames) {
75 av_packet_unref(pkt);
76 return;
77 }
78 ost->frame_number++;
79 }
80
81 if (!of->header_written) {
82 AVPacket *tmp_pkt;
83 /* the muxer is not initialized yet, buffer the packet */
84 if (!av_fifo_can_write(ost->muxing_queue)) {
85 size_t cur_size = av_fifo_can_read(ost->muxing_queue);
86 unsigned int are_we_over_size =
88 size_t limit = are_we_over_size ? ost->max_muxing_queue_size : SIZE_MAX;
89 size_t new_size = FFMIN(2 * cur_size, limit);
90
91 if (new_size <= cur_size) {
92 av_log(NULL, AV_LOG_ERROR,
93 "Too many packets buffered for output stream %d:%d.\n",
94 ost->file_index, ost->st->index);
95 exit_program(1);
96 }
97 ret = av_fifo_grow2(ost->muxing_queue, new_size - cur_size);
98 if (ret < 0)
99 exit_program(1);
100 }
101 ret = av_packet_make_refcounted(pkt);
102 if (ret < 0)
103 exit_program(1);
104 tmp_pkt = av_packet_alloc();
105 if (!tmp_pkt)
106 exit_program(1);
107 av_packet_move_ref(tmp_pkt, pkt);
108 ost->muxing_queue_data_size += tmp_pkt->size;
109 av_fifo_write(ost->muxing_queue, &tmp_pkt, 1);
110 return;
111 }
112
113 if ((st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && ost->vsync_method == VSYNC_DROP) ||
114 (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && audio_sync_method < 0))
115 pkt->pts = pkt->dts = AV_NOPTS_VALUE;
116
117 if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
118 if (ost->frame_rate.num && ost->is_cfr) {
119 if (pkt->duration > 0)
120 av_log(NULL, AV_LOG_WARNING, "Overriding packet duration by frame rate, this should not happen\n");
121 pkt->duration = av_rescale_q(1, av_inv_q(ost->frame_rate),
122 ost->mux_timebase);
123 }
124 }
125
126 av_packet_rescale_ts(pkt, ost->mux_timebase, ost->st->time_base);
127
128 if (!(s->oformat->flags & AVFMT_NOTIMESTAMPS)) {
129 if (pkt->dts != AV_NOPTS_VALUE &&
130 pkt->pts != AV_NOPTS_VALUE &&
131 pkt->dts > pkt->pts) {
132 av_log(s, AV_LOG_WARNING, "Invalid DTS: %"PRId64" PTS: %"PRId64" in output stream %d:%d, replacing by guess\n",
133 pkt->dts, pkt->pts,
134 ost->file_index, ost->st->index);
135 pkt->pts =
136 pkt->dts = pkt->pts + pkt->dts + ost->last_mux_dts + 1
137 - FFMIN3(pkt->pts, pkt->dts, ost->last_mux_dts + 1)
138 - FFMAX3(pkt->pts, pkt->dts, ost->last_mux_dts + 1);
139 }
140 if ((st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO || st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) &&
141 pkt->dts != AV_NOPTS_VALUE &&
142 ost->last_mux_dts != AV_NOPTS_VALUE) {
143 int64_t max = ost->last_mux_dts + !(s->oformat->flags & AVFMT_TS_NONSTRICT);
144 if (pkt->dts < max) {
145 int loglevel = max - pkt->dts > 2 || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ? AV_LOG_WARNING : AV_LOG_DEBUG;
146 if (exit_on_error)
147 loglevel = AV_LOG_ERROR;
148 av_log(s, loglevel, "Non-monotonous DTS in output stream "
149 "%d:%d; previous: %"PRId64", current: %"PRId64"; ",
150 ost->file_index, ost->st->index, ost->last_mux_dts, pkt->dts);
151 if (exit_on_error) {
152 av_log(NULL, AV_LOG_FATAL, "aborting.\n");
153 exit_program(1);
154 }
155 av_log(s, loglevel, "changing to %"PRId64". This may result "
156 "in incorrect timestamps in the output file.\n",
157 max);
158 if (pkt->pts >= pkt->dts)
159 pkt->pts = FFMAX(pkt->pts, max);
160 pkt->dts = max;
161 }
162 }
163 }
164 ost->last_mux_dts = pkt->dts;
165
166 ost->data_size += pkt->size;
167 ost->packets_written++;
168
169 pkt->stream_index = ost->index;
170
171 if (debug_ts) {
172 av_log(NULL, AV_LOG_INFO, "muxer <- type:%s "
173 "pkt_pts:%s pkt_pts_time:%s pkt_dts:%s pkt_dts_time:%s duration:%s duration_time:%s size:%d\n",
174 av_get_media_type_string(ost->enc_ctx->codec_type),
175 av_ts2str(pkt->pts), av_ts2timestr(pkt->pts, &ost->st->time_base),
176 av_ts2str(pkt->dts), av_ts2timestr(pkt->dts, &ost->st->time_base),
177 av_ts2str(pkt->duration), av_ts2timestr(pkt->duration, &ost->st->time_base),
178 pkt->size
179 );
180 }
181
182 ret = av_interleaved_write_frame(s, pkt);
183 if (ret < 0) {
184 print_error("av_interleaved_write_frame()", ret);
187 }
188}
189
190static int print_sdp(void)
191{
192 char sdp[16384];
193 int i;
194 int j, ret;
195 AVIOContext *sdp_pb;
196 AVFormatContext **avc;
197
198 for (i = 0; i < nb_output_files; i++) {
199 if (!output_files[i]->header_written)
200 return 0;
201 }
202
203 avc = av_malloc_array(nb_output_files, sizeof(*avc));
204 if (!avc)
205 exit_program(1);
206 for (i = 0, j = 0; i < nb_output_files; i++) {
207 if (!strcmp(output_files[i]->ctx->oformat->name, "rtp")) {
208 avc[j] = output_files[i]->ctx;
209 j++;
210 }
211 }
212
213 if (!j) {
214 av_log(NULL, AV_LOG_ERROR, "No output streams in the SDP.\n");
215 ret = AVERROR(EINVAL);
216 goto fail;
217 }
218
219 ret = av_sdp_create(avc, j, sdp, sizeof(sdp));
220 if (ret < 0)
221 goto fail;
222
223 if (!sdp_filename) {
224 printf("SDP:\n%s\n", sdp);
225 fflush(stdout);
226 } else {
227 ret = avio_open2(&sdp_pb, sdp_filename, AVIO_FLAG_WRITE, &int_cb, NULL);
228 if (ret < 0) {
229 av_log(NULL, AV_LOG_ERROR, "Failed to open sdp file '%s'\n", sdp_filename);
230 goto fail;
231 }
232
233 avio_print(sdp_pb, sdp);
234 avio_closep(&sdp_pb);
235 av_freep(&sdp_filename);
236 }
237
238fail:
239 av_freep(&avc);
240 return ret;
241}
242
243/* open the muxer when all the streams are initialized */
245{
246 int ret, i;
247
248 for (i = 0; i < of->ctx->nb_streams; i++) {
249 OutputStream *ost = output_streams[of->ost_index + i];
250 if (!ost->initialized)
251 return 0;
252 }
253
254 ret = avformat_write_header(of->ctx, &of->opts);
255 if (ret < 0) {
256 av_log(NULL, AV_LOG_ERROR,
257 "Could not write header for output file #%d "
258 "(incorrect codec parameters ?): %s\n",
259 of->index, av_err2str(ret));
260 return ret;
261 }
262 //assert_avoptions(of->opts);
263 of->header_written = 1;
264
265 av_dump_format(of->ctx, of->index, of->ctx->url, 1);
267
268 if (sdp_filename || want_sdp) {
269 ret = print_sdp();
270 if (ret < 0) {
271 av_log(NULL, AV_LOG_ERROR, "Error writing the SDP.\n");
272 return ret;
273 }
274 }
275
276 /* flush the muxing queues */
277 for (i = 0; i < of->ctx->nb_streams; i++) {
278 OutputStream *ost = output_streams[of->ost_index + i];
279 AVPacket *pkt;
280
281 /* try to improve muxing time_base (only possible if nothing has been written yet) */
282 if (!av_fifo_can_read(ost->muxing_queue))
283 ost->mux_timebase = ost->st->time_base;
284
285 while (av_fifo_read(ost->muxing_queue, &pkt, 1) >= 0) {
286 ost->muxing_queue_data_size -= pkt->size;
287 of_write_packet(of, pkt, ost, 1);
288 av_packet_free(&pkt);
289 }
290 }
291
292 return 0;
293}
294
296{
297 int ret;
298
299 if (!of->header_written) {
300 av_log(NULL, AV_LOG_ERROR,
301 "Nothing was written into output file %d (%s), because "
302 "at least one of its streams received no packets.\n",
303 of->index, of->ctx->url);
304 return AVERROR(EINVAL);
305 }
306
307 ret = av_write_trailer(of->ctx);
308 if (ret < 0) {
309 av_log(NULL, AV_LOG_ERROR, "Error writing trailer of %s: %s\n", of->ctx->url, av_err2str(ret));
310 return ret;
311 }
312
313 return 0;
314}
315
317{
318 OutputFile *of = *pof;
319 AVFormatContext *s;
320
321 if (!of)
322 return;
323
324 s = of->ctx;
325 if (s && s->oformat && !(s->oformat->flags & AVFMT_NOFILE))
326 avio_closep(&s->pb);
327 avformat_free_context(s);
328 av_dict_free(&of->opts);
329
330 av_freep(pof);
331}
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