From patchwork Mon Aug 19 19:16:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Andreas_H=C3=A5kon?= X-Patchwork-Id: 14589 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 4D23144904C for ; Mon, 19 Aug 2019 22:16:26 +0300 (EEST) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 274E568A591; Mon, 19 Aug 2019 22:16:26 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-40135.protonmail.ch (mail-40135.protonmail.ch [185.70.40.135]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 296CE689A9C for ; Mon, 19 Aug 2019 22:16:20 +0300 (EEST) Date: Mon, 19 Aug 2019 19:16:15 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=default; t=1566242177; bh=Bp7jsHUha6jYXlpZRP7rAfJUBrD2si6RshdNlq9jnFQ=; h=Date:To:From:Reply-To:Subject:Feedback-ID:From; b=He0xz0hDssfEV3bBWomeTGAs52rP1lK0OF0RTVvIFJfjCNWnJ+jwTX+xmaTQkF/i5 9FXUxShup5W1DdWgbdxgi9jDC4PXbnNrRDueZDCI9tXJ90XSltKDEJKNqSye/wMPq5 iJR626OVULOvSFz0A+579IFgivU/elV2JV9QwNi4= To: FFmpeg development discussions and patches From: =?UTF-8?Q?Andreas_H=C3=A5kon?= Message-ID: Feedback-ID: Mx8CaiV20jk_fqXDN0fFpg3vRaGkb9VCTrYRnZNHwEija3aOdqvFspzl6ODkmHrlSKJSx29p-LzkuvS_96L02A==:Ext:ProtonMail MIME-Version: 1.0 X-Spam-Status: No, score=-1.2 required=7.0 tests=ALL_TRUSTED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.protonmail.ch X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: [FFmpeg-devel] libavformat/mpegtsenc: new interleaved mux mode [v3] X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi, This is the third version of my patch for an "interleaved MPEG-TS muxer". This new version includes all recommendations and rebases the fix of the incorrect PCR with multiple programs (fixed in collaboration with Marton Balint). Supersedes: https://patchwork.ffmpeg.org/patch/13745/ How to check it: (Note: I use for all the tests the file https://samples.ffmpeg.org/HDTV/bshi01.tp ) - By default the current behavior is selected. You can verify that this patch doesn’t alter the original behavior with this simple test: $ ffmpeg-original -i bshi01.tp \ -c:v copy -c:a copy -c:d copy \ -f mpegts -muxrate 22M bshi01-stock.ts $ ffmpeg-patched -i bshi01.tp \ -c:v copy -c:a copy -c:d copy \ -f mpegts -muxrate 22M -mpegts_extra_mux 0 bshi01-new.ts $ cmp -b bshi01-stock.ts bshi01-new.ts So both files are identical. The patch therefore doesn’t introduce any changes in the implementation of the sequential mode. - To check the new interlaced mode you can perform this other test: $ ffmpeg-patched -y -loglevel verbose -i bshi01.tp \ -map "i:0x100" -c:0 copy \ -map "i:0x110" -c:a:0 mp2 -ac:0 2 -ar:0 48000 -ab:0 384k \ -map "i:0x130" -c:2 copy \ -map "i:0x110" -c:3 copy \ -map "i:0x100" -c:4 copy \ -program title=Prog1:st=0:st=1:st=2 \ -program title=Prog2:st=3:st=4 \ -f mpegts -muxrate 44M -mpegts_extra_mux 1 bshi01-mode1.ts $ ffmpeg-patched -y -loglevel verbose -i bshi01.tp \ -map "i:0x100" -c:0 copy \ -map "i:0x110" -c:a:0 mp2 -ac:0 2 -ar:0 48000 -ab:0 384k \ -map "i:0x130" -c:2 copy \ -map "i:0x110" -c:3 copy \ -map "i:0x100" -c:4 copy \ -program title=Prog1:st=0:st=1:st=2 \ -program title=Prog2:st=3:st=4 \ -f mpegts -muxrate 44M -mpegts_extra_mux 0 bshi01-mode0.ts And you can observe: a) The size of the files “bshi01-mode0.ts” and “bshi01-mode1.ts” is almost the same. If you inspect the content, you can verify that the difference is based solely on: a) an small increase in the number of NULL packets in mode 1; b) a few new packets with only PCR and not payload in the first video stream. b) If you demux the three files to elemental streams, then you can check that the content is identical. Using the linux package “tstools” you can do this check: $ ts2es -pid 256 bshi01-mode0.ts bshi01-mode0-256.m2v $ ts2es -pid 260 bshi01-mode0.ts bshi01-mode0-260.m2v $ ts2es -pid 257 bshi01-mode0.ts bshi01-mode0-257.mp2 $ ts2es -pid 259 bshi01-mode0.ts bshi01-mode0-259.aac $ ts2es -pid 256 bshi01-mode1.ts bshi01-mode1-256.m2v $ ts2es -pid 260 bshi01-mode1.ts bshi01-mode1-260.m2v $ ts2es -pid 257 bshi01-mode1.ts bshi01-mode1-257.mp2 $ ts2es -pid 259 bshi01-mode1.ts bshi01-mode1-259.aac c) If you look at the internal content of the files you can verify that the original “bshi01.tp” file has all pids interlaced, but this isn’t true for the file “bshi01-mode0.ts”. However, the file “bshi01-mode1.ts” has an internal structure similar to that of the original file. You can view the content using the well-known tool “DVB Inspector” with the “Grid View” option. These tests confirm the correctness of the implementation of this new multiplexing mode. Regards. A.H. --- From 4636f83ca24e71fb807d48d3713bda6d3254938a Mon Sep 17 00:00:00 2001 From: Andreas Hakon Date: Mon, 19 Aug 2019 20:57:32 +0200 Subject: [PATCH] libavformat/mpegtsenc: interleaved mux mode [v3] This patch implements a new optional "interleaved mux mode" in the MPEGTS muxer. The strategy that implements the current mux (selected by default) is based on writing full PES packages sequentially. This mode can be problematic when using with DTV broadcasts, as some large video PES packets can delay the writing of other elementary streams. The new optional parameter "-mpegts_interleave_mux 1" enables another strategy. Instead of writing all PES packets sequentially, the first TS packet of each PES packet is written when the PES packet is started. But the rest of the PES data will be written later, and interleaved between all the mux streams. This new (optional) behavior has clear advantages when multiplexing multiple programs with several video streams. And although this does not turn the current implementation into a professional muxer, it brings the result closer to what professional equipment does. Example of use: ffmpeg -i INPUT.ts \ -map "i:0x100" -c:v:0 copy \ -map "i:0x110" -c:a:0 copy \ -map "i:0x100" -c:v:1 copy \ -map "i:0x110" -c:a:1 copy \ -program title=Prog1:st=0:st=1 \ -program title=Prog2:st=2:st=3 \ -f mpegts -muxrate 44M -mpegts_interleave_mux 1 OUTPUT.ts Signed-off-by: Andreas Hakon --- libavformat/mpegtsenc.c | 188 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 152 insertions(+), 36 deletions(-) diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c index d4dd4ab..dc1b68f 100644 --- a/libavformat/mpegtsenc.c +++ b/libavformat/mpegtsenc.c @@ -87,6 +87,7 @@ typedef struct MpegTSWrite { int64_t next_pcr; int mux_rate; ///< set to 1 when VBR int pes_payload_size; + int pes_payload_max; int transport_stream_id; int original_network_id; @@ -96,6 +97,7 @@ typedef struct MpegTSWrite { int pmt_start_pid; int start_pid; int m2ts_mode; + int parallel_mux; int pcr_period; #define MPEGTS_FLAG_REEMIT_PAT_PMT 0x01 @@ -117,6 +119,7 @@ typedef struct MpegTSWrite { /* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */ #define DEFAULT_PES_HEADER_FREQ 16 #define DEFAULT_PES_PAYLOAD_SIZE ((DEFAULT_PES_HEADER_FREQ - 1) * 184 + 170) +#define MAX_PES_PAYLOAD 2 * 200 * 1024 // From mpegts.c /* The section length is 12 bits. The first 2 are set to 0, the remaining * 10 bits should not exceed 1021. */ @@ -229,11 +232,18 @@ typedef struct MpegTSWriteStream { int cc; int discontinuity; int payload_size; + int payload_top; + int stream_id; int first_pts_check; ///< first pts check needed int prev_payload_key; int64_t payload_pts; int64_t payload_dts; int payload_flags; + int pes_flags; +#define PES_FLAG_READY 0x01 +#define PES_FLAG_START 0x02 +#define PES_FLAG_PARTIAL 0x04 +#define PES_FLAG_NEEDS_END 0x08 uint8_t *payload; AVFormatContext *amux; @@ -905,7 +915,8 @@ static int mpegts_init(AVFormatContext *s) avpriv_set_pts_info(st, 33, 1, 90000); - ts_st->payload = av_mallocz(ts->pes_payload_size); + ts->pes_payload_max = FFMAX(MAX_PES_PAYLOAD, ts->pes_payload_size); + ts_st->payload = av_mallocz(ts->parallel_mux ? ts->pes_payload_max : ts->pes_payload_size); if (!ts_st->payload) { ret = AVERROR(ENOMEM); goto fail; @@ -940,6 +951,9 @@ static int mpegts_init(AVFormatContext *s) pids[i] = ts_st->pid; ts_st->payload_pts = AV_NOPTS_VALUE; ts_st->payload_dts = AV_NOPTS_VALUE; + ts_st->payload_top = 0; + ts_st->stream_id = -1; + ts_st->pes_flags = 0; ts_st->first_pts_check = 1; ts_st->cc = 15; ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT; @@ -1167,15 +1181,17 @@ static uint8_t *get_ts_payload_start(uint8_t *pkt) /* Add a PES header to the front of the payload, and segment into an integer * number of TS packets. The final TS packet is padded using an oversized * adaptation header to exactly fill the last TS packet. - * NOTE: 'payload' contains a complete PES payload. */ -static void mpegts_write_pes(AVFormatContext *s, AVStream *st, + * NOTE: 'payload' contains a complete PES payload, or a partial chunk when + * the writing of the PES packet has already begun. */ +static int mpegts_write_pes(AVFormatContext *s, AVStream *st, const uint8_t *payload, int payload_size, - int64_t pts, int64_t dts, int key, int stream_id) + int64_t pts, int64_t dts, int key) { MpegTSWriteStream *ts_st = st->priv_data; MpegTSWrite *ts = s->priv_data; uint8_t buf[TS_PACKET_SIZE]; uint8_t *q; + int ret_size = 0; int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags; int afc_len, stuffing_len; int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE); @@ -1186,7 +1202,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, force_pat = 1; } - is_start = 1; + is_start = !!(ts_st->pes_flags & PES_FLAG_START); while (payload_size > 0) { int64_t pcr = -1; /* avoid warning */ @@ -1297,9 +1313,9 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, st->codecpar->codec_id == AV_CODEC_ID_TIMED_ID3) { *q++ = 0xbd; } else if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA) { - *q++ = stream_id != -1 ? stream_id : 0xfc; + *q++ = ts_st->stream_id != -1 ? ts_st->stream_id : 0xfc; - if (stream_id == 0xbd) /* asynchronous KLV */ + if (ts_st->stream_id == 0xbd) /* asynchronous KLV */ pts = dts = AV_NOPTS_VALUE; } else { *q++ = 0xbd; @@ -1400,12 +1416,15 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, * subtitle_stream_id: for DVB subtitle stream shall be identified by the value 0x00 */ *q++ = 0x20; *q++ = 0x00; + /* It's required to add a final mark */ + ts_st->pes_flags |= PES_FLAG_NEEDS_END; } if (is_dvb_teletext) { memset(q, 0xff, pes_header_stuffing_bytes); q += pes_header_stuffing_bytes; } is_start = 0; + ts_st->pes_flags &= ~PES_FLAG_START; } /* header size */ header_len = q - buf; @@ -1436,7 +1455,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, } } - if (is_dvb_subtitle && payload_size == len) { + if ((ts_st->pes_flags & PES_FLAG_NEEDS_END) && payload_size == len) { memcpy(buf + TS_PACKET_SIZE - len, payload, len - 1); buf[TS_PACKET_SIZE - 1] = 0xff; /* end_of_PES_data_field_marker: an 8-bit field with fixed contents 0xff for DVB subtitle */ } else { @@ -1447,8 +1466,12 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st, payload_size -= len; mpegts_prefix_m2ts_header(s); avio_write(s->pb, buf, TS_PACKET_SIZE); + ret_size += len; + if (ts_st->pes_flags & PES_FLAG_PARTIAL) + break; } ts_st->prev_payload_key = key; + return ret_size; } int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt) @@ -1535,6 +1558,83 @@ static int opus_get_packet_samples(AVFormatContext *s, AVPacket *pkt) return duration; } +static inline void mpegts_write_full_pes_stream(AVFormatContext *s, AVStream *st, const uint8_t *payload) +{ + MpegTSWriteStream *ts_st = st->priv_data; + ts_st->pes_flags |= PES_FLAG_START; + ts_st->pes_flags &= ~PES_FLAG_PARTIAL; + mpegts_write_pes(s, st, payload, + ts_st->payload_size, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + ts_st->payload_size = 0; + ts_st->payload_top = 0; + ts_st->pes_flags = 0; +} + +static inline void mpegts_flush_pes_stream(AVFormatContext *s, AVStream *st, const uint8_t *payload) +{ + MpegTSWriteStream *ts_st = st->priv_data; + ts_st->pes_flags &= ~PES_FLAG_PARTIAL; + mpegts_write_pes(s, st, payload + ts_st->payload_top, + ts_st->payload_size - ts_st->payload_top, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + ts_st->payload_size = 0; + ts_st->payload_top = 0; + ts_st->pes_flags = 0; +} + +static inline void mpegts_write_partial_pes_stream(AVFormatContext *s, AVStream *st, const uint8_t *payload) +{ + MpegTSWriteStream *ts_st = st->priv_data; + MpegTSWrite *ts = s->priv_data; + if (ts_st->payload_size > ts->pes_payload_max) { + av_log(s, AV_LOG_WARNING, "PES packet oversized (%d), full sequential writing required\n", ts_st->payload_size); + ts_st->pes_flags &= ~PES_FLAG_PARTIAL; + } else + ts_st->pes_flags |= PES_FLAG_PARTIAL; + ts_st->payload_top += mpegts_write_pes(s, st, payload + ts_st->payload_top, + ts_st->payload_size - ts_st->payload_top, + ts_st->payload_pts, ts_st->payload_dts, + ts_st->payload_flags & AV_PKT_FLAG_KEY); + if (ts_st->payload_size && ts_st->payload_size == ts_st->payload_top) { + ts_st->payload_size = 0; + ts_st->payload_top = 0; + ts_st->pes_flags = 0; + } +} + +static inline void mpegts_start_partial_pes_stream(AVFormatContext *s, AVStream *st, const uint8_t *payload) +{ + MpegTSWriteStream *ts_st = st->priv_data; + ts_st->payload_top = 0; + ts_st->pes_flags |= PES_FLAG_START | PES_FLAG_READY; + mpegts_write_partial_pes_stream(s, st, payload); +} + +static void write_side_pes_streams(AVFormatContext *s, int64_t dts, int64_t delay, int parallel) +{ + int i; + for(i=0; inb_streams; i++) { + AVStream *st2 = s->streams[i]; + MpegTSWriteStream *ts_st2 = st2->priv_data; + if ( ts_st2->payload_size + && (ts_st2->payload_dts == AV_NOPTS_VALUE + || dts - ts_st2->payload_dts > delay/2 + || (ts_st2->pes_flags & PES_FLAG_READY))) { + if (parallel) { + if (!(ts_st2->pes_flags & PES_FLAG_READY)) + mpegts_start_partial_pes_stream(s, st2, ts_st2->payload); + else + mpegts_write_partial_pes_stream(s, st2, ts_st2->payload); + } else { + mpegts_write_full_pes_stream(s, st2, ts_st2->payload); + } + } + } +} + static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) { AVStream *st = s->streams[pkt->stream_index]; @@ -1548,13 +1648,12 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) int opus_samples = 0; int side_data_size; uint8_t *side_data = NULL; - int stream_id = -1; side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_MPEGTS_STREAM_ID, &side_data_size); if (side_data) - stream_id = side_data[0]; + ts_st->stream_id = side_data[0]; if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) { ts->pat_packet_count = ts->pat_packet_period - 1; @@ -1749,52 +1848,66 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt) } if (pkt->dts != AV_NOPTS_VALUE) { - int i; - for(i=0; inb_streams; i++) { - AVStream *st2 = s->streams[i]; - MpegTSWriteStream *ts_st2 = st2->priv_data; - if ( ts_st2->payload_size - && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2)) { - mpegts_write_pes(s, st2, ts_st2->payload, ts_st2->payload_size, - ts_st2->payload_pts, ts_st2->payload_dts, - ts_st2->payload_flags & AV_PKT_FLAG_KEY, stream_id); - ts_st2->payload_size = 0; - } - } + if (!ts->parallel_mux) { + /* In sequential mode first flush PES packets of other streams */ + write_side_pes_streams(s, dts, delay, ts->parallel_mux); + } else while (ts_st->pes_flags & PES_FLAG_READY) + /* In parallel mode first write one PES chunk of every pending streams */ + write_side_pes_streams(s, dts, delay, ts->parallel_mux); } + /* Complete the PES packet if it's time to do it + * (incoming data will go into another PES packet) */ if (ts_st->payload_size && (ts_st->payload_size + size > ts->pes_payload_size || (dts != AV_NOPTS_VALUE && ts_st->payload_dts != AV_NOPTS_VALUE && av_compare_ts(dts - ts_st->payload_dts, st->time_base, s->max_delay, AV_TIME_BASE_Q) >= 0) || ts_st->opus_queued_samples + opus_samples >= 5760 /* 120ms */)) { - mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, - ts_st->payload_pts, ts_st->payload_dts, - ts_st->payload_flags & AV_PKT_FLAG_KEY, stream_id); - ts_st->payload_size = 0; - ts_st->opus_queued_samples = 0; + if (!ts->parallel_mux || ts_st->opus_queued_samples) { + mpegts_write_full_pes_stream(s, st, ts_st->payload); + ts_st->opus_queued_samples = 0; + } else { + mpegts_start_partial_pes_stream(s, st, ts_st->payload); + /* and write the entire pes packet interleaved until end */ + while (ts_st->pes_flags & PES_FLAG_READY) + write_side_pes_streams(s, dts, delay, ts->parallel_mux); + } } + /* Complete the PES packet too with the incoming data when necessary */ if (st->codecpar->codec_type != AVMEDIA_TYPE_AUDIO || size > ts->pes_payload_size) { av_assert0(!ts_st->payload_size); - // for video and subtitle, write a single pes packet - mpegts_write_pes(s, st, buf, size, pts, dts, - pkt->flags & AV_PKT_FLAG_KEY, stream_id); - ts_st->opus_queued_samples = 0; - av_free(data); - return 0; + /* for video and subtitle, a single pes packet is required */ + ts_st->payload_size = size; + ts_st->payload_pts = pts; + ts_st->payload_dts = dts; + ts_st->payload_flags = pkt->flags; + if (!ts->parallel_mux || ts_st->opus_queued_samples) { + mpegts_write_full_pes_stream(s, st, buf); + ts_st->opus_queued_samples = 0; + goto free; + } else { + /* start writing the first part of this pes packet */ + mpegts_start_partial_pes_stream(s, st, buf); + if (ts_st->payload_size == 0) + goto free; + ts_st->payload_size = 0; // note: this value will be set later + } } + /* Start a new PES packet */ if (!ts_st->payload_size) { ts_st->payload_pts = pts; ts_st->payload_dts = dts; ts_st->payload_flags = pkt->flags; } + /* Enqueue new data in the current PES packet */ memcpy(ts_st->payload + ts_st->payload_size, buf, size); ts_st->payload_size += size; ts_st->opus_queued_samples += opus_samples; +free: av_free(data); return 0; @@ -1809,9 +1922,9 @@ static void mpegts_write_flush(AVFormatContext *s) AVStream *st = s->streams[i]; MpegTSWriteStream *ts_st = st->priv_data; if (ts_st->payload_size > 0) { - mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size, - ts_st->payload_pts, ts_st->payload_dts, - ts_st->payload_flags & AV_PKT_FLAG_KEY, -1); + if (!ts_st->pes_flags) + ts_st->pes_flags |= PES_FLAG_START; + mpegts_flush_pes_stream(s, st, ts_st->payload); ts_st->payload_size = 0; ts_st->opus_queued_samples = 0; } @@ -1929,6 +2042,9 @@ static const AVOption options[] = { { "mpegts_m2ts_mode", "Enable m2ts mode.", offsetof(MpegTSWrite, m2ts_mode), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { "mpegts_interleave_mux", "Enable interleaved (non-sequential) muxing mode.", + offsetof(MpegTSWrite, parallel_mux), AV_OPT_TYPE_BOOL, + { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, { "muxrate", NULL, offsetof(MpegTSWrite, mux_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },