[FFmpeg-devel] [PATCH] libavformat/mpegtsenc: new interlaced mux mode
Andriy Gelman
andriy.gelman at gmail.com
Tue Jun 11 16:03:07 EEST 2019
Hello,
On Mon, 10. Jun 17:29, Andreas Håkon wrote:
> Hi,
>
> Here is a list of comments on this patch:
> (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.
>
> - Last but not least, this patch fixes a bug within the current
> implementation. When several programs are used in the same
> TS file, the function “mpegts_init()” incorrectly initializes the
> “service->pcr_pid” when several services are used. The current
> code only sets this value for the last service, and leaves the
> others uninitialized. This patch solves the problem by looping over
> all services to establish the correct values.
>
> Please check this patch so that it can be accepted.
> Regards.
> A.H.
>
> ---
> From aa02575cc11bed0fd2ae2a01368c8673ad48e64b Mon Sep 17 00:00:00 2001
> From: Andreas Hakon <andreas.hakon at protonmail.com>
> Date: Mon, 10 Jun 2019 18:14:56 +0100
> Subject: [PATCH] libavformat/mpegtsenc: new interlaced mux mode
>
> This patch implements a new optional "parallel muxing 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.
Could you go into more detail as to why this causes problems for DTV broadcasts?
> The new optional parameter "-mpegts_extra_mux 1" enables a different 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 interlaced 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.
>
> Signed-off-by: Andreas Hakon <andreas.hakon at protonmail.com>
> ---
> libavformat/mpegtsenc.c | 305 ++++++++++++++++++++++++++++++++++++-----------
> 1 file changed, 233 insertions(+), 72 deletions(-)
>
> diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
> index fc0ea22..93f8d3d 100644
> --- a/libavformat/mpegtsenc.c
> +++ b/libavformat/mpegtsenc.c
> @@ -97,6 +97,7 @@ typedef struct MpegTSWrite {
> int pmt_start_pid;
> int start_pid;
> int m2ts_mode;
> + int parallel_mux;
>
> int reemit_pat_pmt; // backward compatibility
>
> @@ -120,6 +121,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. */
> @@ -227,17 +229,28 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id,
> #define PAT_RETRANS_TIME 100
> #define PCR_RETRANS_TIME 20
>
> +#define PES_START 1 /* 0000 0001 */
> +#define PES_FULL 2 /* 0000 0010 */
> +#define UNUSED_FLAG_3 4 /* 0000 0100 */
> +#define UNUSED_FLAG_4 8 /* 0000 1000 */
> +#define UNUSED_FLAG_5 16 /* 0001 0000 */
> +#define UNUSED_FLAG_6 32 /* 0010 0000 */
> +#define UNUSED_FLAG_7 64 /* 0100 0000 */
Is it relevant to include these?
> +#define PES_NEEDS_END 128 /* 1000 0000 */
> +
> typedef struct MpegTSWriteStream {
> struct MpegTSService *service;
> int pid; /* stream associated pid */
> int cc;
> int discontinuity;
> int payload_size;
> + int payload_top;
> 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;
> uint8_t *payload;
> AVFormatContext *amux;
> AVRational user_tb;
> @@ -866,7 +879,7 @@ static int mpegts_init(AVFormatContext *s)
> ts_st->user_tb = st->time_base;
> avpriv_set_pts_info(st, 33, 1, 90000);
>
> - ts_st->payload = av_mallocz(ts->pes_payload_size);
> + ts_st->payload = av_mallocz(ts->parallel_mux ? MAX_PES_PAYLOAD : ts->pes_payload_size);
Could you clarify why this needs to be changed?
> if (!ts_st->payload) {
> ret = AVERROR(ENOMEM);
> goto fail;
> @@ -910,6 +923,8 @@ 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->pes_flags = 0;
> ts_st->first_pts_check = 1;
> ts_st->cc = 15;
> ts_st->discontinuity = ts->flags & MPEGTS_FLAG_DISCONT;
> @@ -953,46 +968,52 @@ static int mpegts_init(AVFormatContext *s)
>
> av_freep(&pids);
>
> - /* if no video stream, use the first stream as PCR */
> - if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
> - pcr_st = s->streams[0];
> - ts_st = pcr_st->priv_data;
> - service->pcr_pid = ts_st->pid;
> - } else
> - ts_st = pcr_st->priv_data;
> -
> - if (ts->mux_rate > 1) {
> - service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
> - (TS_PACKET_SIZE * 8 * 1000);
> - ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME /
> - (TS_PACKET_SIZE * 8 * 1000);
> - ts->pat_packet_period = (int64_t)ts->mux_rate * PAT_RETRANS_TIME /
> - (TS_PACKET_SIZE * 8 * 1000);
> -
> - if (ts->copyts < 1)
> - ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE);
> - } else {
> - /* Arbitrary values, PAT/PMT will also be written on video key frames */
> - ts->sdt_packet_period = 200;
> - ts->pat_packet_period = 40;
> - if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
> - int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
> - if (!frame_size) {
> - av_log(s, AV_LOG_WARNING, "frame size not set\n");
> - service->pcr_packet_period =
> - pcr_st->codecpar->sample_rate / (10 * 512);
> + for (i = 0; i < ts->nb_services; i++) {
> + service = ts->services[i];
> +
> + /* if no video stream, use the first stream as PCR */
> + if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
> + pcr_st = s->streams[0];
> + ts_st = pcr_st->priv_data;
> + service->pcr_pid = ts_st->pid;
> + } else
> + ts_st = pcr_st->priv_data;
> +
> + if (ts->mux_rate > 1) {
> + service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
> + (TS_PACKET_SIZE * 8 * 1000);
> + ts->sdt_packet_period = (int64_t)ts->mux_rate * SDT_RETRANS_TIME /
> + (TS_PACKET_SIZE * 8 * 1000);
> + ts->pat_packet_period = (int64_t)ts->mux_rate * PAT_RETRANS_TIME /
> + (TS_PACKET_SIZE * 8 * 1000);
> + if (ts->copyts < 1)
> + ts->first_pcr = av_rescale(s->max_delay, PCR_TIME_BASE, AV_TIME_BASE);
> + } else {
> + /* Arbitrary values, PAT/PMT will also be written on video key frames */
> + ts->sdt_packet_period = 200;
> + ts->pat_packet_period = 40;
> + if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
> + int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
> + if (!frame_size) {
> + av_log(s, AV_LOG_WARNING, "frame size not set\n");
> + service->pcr_packet_period =
> + pcr_st->codecpar->sample_rate / (10 * 512);
> + } else {
> + service->pcr_packet_period =
> + pcr_st->codecpar->sample_rate / (10 * frame_size);
> + }
> } else {
> + // max delta PCR 0.1s
> + // TODO: should be avg_frame_rate
> service->pcr_packet_period =
> - pcr_st->codecpar->sample_rate / (10 * frame_size);
> + ts_st->user_tb.den / (10 * ts_st->user_tb.num);
> }
> - } else {
> - // max delta PCR 0.1s
> - // TODO: should be avg_frame_rate
> - service->pcr_packet_period =
> - ts_st->user_tb.den / (10 * ts_st->user_tb.num);
> + if (!service->pcr_packet_period)
> + service->pcr_packet_period = 1;
> }
> - if (!service->pcr_packet_period)
> - service->pcr_packet_period = 1;
> +
> + // output a PCR as soon as possible
> + service->pcr_packet_count = service->pcr_packet_period;
> }
Why do you need the loop over the services here? It seems unrelated unless
I missed something.
>
> ts->last_pat_ts = AV_NOPTS_VALUE;
> @@ -1005,8 +1026,6 @@ static int mpegts_init(AVFormatContext *s)
> ts->sdt_packet_period = INT_MAX;
> }
>
> - // output a PCR as soon as possible
> - service->pcr_packet_count = service->pcr_packet_period;
> ts->pat_packet_count = ts->pat_packet_period - 1;
> ts->sdt_packet_count = ts->sdt_packet_period - 1;
>
> @@ -1172,9 +1191,14 @@ 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,
> - const uint8_t *payload, int payload_size,
> + * NOTE: 'payload' contains a complete PES payload.
> + * NOTE2: When 'mode' < -1 it writes the rest of the payload (without header);
> + * When 'mode' = -1 it writes the entire payload with the header;
> + * when 'mode' = 0 it writes only one TS packet with the header;
> + * when 'mode' > 0 it writes only one TS packet.
Enum for mode would make more sense to me
> + * Note3: It returns the number of writed bytes. */
> +static int mpegts_write_pes(AVFormatContext *s, AVStream *st,
> + const uint8_t *payload, int payload_size, int mode,
> int64_t pts, int64_t dts, int key, int stream_id)
> {
> MpegTSWriteStream *ts_st = st->priv_data;
> @@ -1186,13 +1210,14 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
> int64_t pcr = -1; /* avoid warning */
> int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
> int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
> + int ret_size = 0;
>
> av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO);
> if (ts->flags & MPEGTS_FLAG_PAT_PMT_AT_FRAMES && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
> force_pat = 1;
> }
>
> - is_start = 1;
> + is_start = (mode > 0 || mode < -1) ? 0 : 1;
> while (payload_size > 0) {
> retransmit_si_info(s, force_pat, dts);
> force_pat = 0;
> @@ -1389,6 +1414,8 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
> memset(q, 0xff, pes_header_stuffing_bytes);
> q += pes_header_stuffing_bytes;
> }
> + if (is_dvb_subtitle)
> + ts_st->pes_flags |= PES_NEEDS_END;
> is_start = 0;
> }
> /* header size */
> @@ -1420,7 +1447,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
> }
> }
>
> - if (is_dvb_subtitle && payload_size == len) {
> + if ((ts_st->pes_flags & PES_NEEDS_END) && payload_size == len) {
This seems unrelated to your commit..
If it is, you can remove PES_NEEDS_END
> 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 {
> @@ -1431,8 +1458,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 (mode >= 0)
> + payload_size = 0; // Write one packet only then exit
Why not just break?
> }
> ts_st->prev_payload_key = key;
> + return ret_size;
> }
>
> int ff_check_h264_startcode(AVFormatContext *s, const AVStream *st, const AVPacket *pkt)
> @@ -1519,6 +1550,102 @@ static int opus_get_packet_samples(AVFormatContext *s, AVPacket *pkt)
> return duration;
> }
>
> +static inline int write_full_pes_stream(AVFormatContext *s, AVStream *st,
> + const uint8_t *payload, int payload_size,
> + int64_t pts, int64_t dts, int key, int stream_id)
> +{
> + return mpegts_write_pes(s, st, payload, payload_size, -1, pts, dts, key, stream_id);
> +}
> +
> +static inline int write_flush_pes_stream(AVFormatContext *s, AVStream *st,
> + const uint8_t *payload, int payload_size,
> + int64_t pts, int64_t dts, int key, int stream_id)
> +{
> + return mpegts_write_pes(s, st, payload, payload_size, -2, pts, dts, key, stream_id);
> +}
> +
> +static inline int write_pkt_pes_stream(AVFormatContext *s, AVStream *st,
> + const uint8_t *payload, int payload_size, int mode,
> + int64_t pts, int64_t dts, int key, int stream_id)
> +{
> + return mpegts_write_pes(s, st, payload, payload_size, mode, pts, dts, key, stream_id);
> +}
> +
> +static int write_side_streams(AVFormatContext *s, int64_t dts, int64_t delay, int stream_id, int parallel)
> +{
> + int i;
> + int sum = 0;
> + int check_mode;
> + int write_mode; // -1: Don't write anything
> + // 0: Start the PES packet and write 1 TS packet
> + // 1: Continue writing just 1 TS packet
> + // 2: Write full PES packet
> + int ret;
> + for(i=0; i<s->nb_streams; i++) {
> + AVStream *st2 = s->streams[i];
> + MpegTSWriteStream *ts_st2 = st2->priv_data;
> +
> + check_mode = parallel ? (ts_st2->payload_top > 0) : 0;
> +
> + if (ts_st2->payload_size && !check_mode
> + && (ts_st2->payload_dts == AV_NOPTS_VALUE || dts - ts_st2->payload_dts > delay/2 || (ts_st2->pes_flags & PES_START))) {
> + write_mode = parallel ? 0 : 2;
> + } else if (check_mode) {
> + write_mode = 1;
> + } else {
> + write_mode = -1;
> + }
> +
> + switch (write_mode)
> + {
> + case 2: // SEQUENTIAL MODE
> + ret = write_full_pes_stream(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;
> + sum += ret;
> + break;
> + case 1: // PARALLEL MODE
> + case 0:
> + ret = write_pkt_pes_stream(s, st2, ts_st2->payload + ts_st2->payload_top,
> + ts_st2->payload_size - ts_st2->payload_top, write_mode,
> + ts_st2->payload_pts, ts_st2->payload_dts,
> + ts_st2->payload_flags & AV_PKT_FLAG_KEY, stream_id);
> + ts_st2->payload_top += ret;
> + sum += ret;
> + break;
> + default:
> + continue;
> + }
> +
> + if (ts_st2->payload_size && ts_st2->payload_top == ts_st2->payload_size) {
> + ts_st2->payload_size = 0;
> + ts_st2->payload_top = 0;
> + ts_st2->pes_flags = 0;
> + }
> + }
> + return sum;
> +}
> +
> +/* NOTE: The return value is negative when the onlyone flag generates the exit. */
> +static int write_with_side_streams(AVFormatContext *s, AVStream *st, int payload_top,
> + const uint8_t *payload, int payload_size,
> + int64_t pts, int64_t dts, int64_t delay, int key, int stream_id)
> +{
> + int force = payload_size - payload_top > MAX_PES_PAYLOAD ? 1 : 0;
> + if (force)
> + av_log(s, AV_LOG_WARNING, "PES packet oversized, full sequential writing required\n");
> + if (payload_size && payload_top != payload_size) {
> + do {
> + payload_top += mpegts_write_pes(s, st, payload + payload_top,
> + payload_size - payload_top, payload_top ? 1 : 0,
> + pts, dts, key, stream_id);
> + } while (force && payload_size != payload_top);
> + }
> + write_side_streams(s, dts, delay, stream_id, 1);
> + return payload_top;
> +}
> +
> static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
> {
> AVStream *st = s->streams[pkt->stream_index];
> @@ -1739,53 +1866,77 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
> }
> }
>
> - if (pkt->dts != AV_NOPTS_VALUE) {
> - int i;
> - for(i=0; i<s->nb_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;
> - }
> - }
> + // Write pending PES packets (SEQUENTIAL MODE)
> + if (pkt->dts != AV_NOPTS_VALUE && !ts->parallel_mux) {
> + write_side_streams(s, dts, delay, stream_id, 0);
> }
>
> + // Complete a new PES packet (new incoming data will go into another PES)
> 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;
> + (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 */)) {
> + if (!ts->parallel_mux || ts_st->opus_queued_samples) {
> + write_full_pes_stream(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;
> + } else {
> + ts_st->pes_flags |= PES_START | PES_FULL;
> + }
> + }
> +
> + // Write pending PES packets (PARALLEL MODE)
> + if (pkt->dts != AV_NOPTS_VALUE && ts->parallel_mux && (ts_st->pes_flags & PES_START)) {
> + // Empty previous pending PES packet before starting a new one or write one packet
> + do {
> + write_with_side_streams(s, st, 0,
> + ts_st->payload, 0,
> + ts_st->payload_pts, ts_st->payload_dts, delay,
> + ts_st->payload_flags & AV_PKT_FLAG_KEY, stream_id);
> + } while (ts_st->pes_flags & PES_START);
> }
>
> + // Directly write a new PES packet with the incoming data
> 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;
> + if (!ts->parallel_mux || ts_st->opus_queued_samples) {
> + write_full_pes_stream(s, st, buf, size, pts, dts,
> + pkt->flags & AV_PKT_FLAG_KEY, stream_id);
> + ts_st->opus_queued_samples = 0;
> + goto free;
> + } else {
> + ts_st->payload_top = write_with_side_streams(s, st, 0,
> + buf, size,
> + pts, dts, delay,
> + pkt->flags & AV_PKT_FLAG_KEY, stream_id);
> + if (ts_st->payload_top == size) {
> + ts_st->payload_size = 0;
> + ts_st->payload_top = 0;
> + ts_st->pes_flags = 0;
> + goto free;
> + }
> + ts_st->pes_flags |= PES_START;
> + ts_st->payload_size = 0; // 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 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;
> @@ -1794,13 +1945,20 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
> static void mpegts_write_flush(AVFormatContext *s)
> {
> int i;
> + MpegTSWrite *ts = s->priv_data;
>
> /* flush current packets */
> for (i = 0; i < s->nb_streams; i++) {
> 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,
> + if (!ts->parallel_mux || ts_st->payload_top == 0)
> + write_full_pes_stream(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);
> + else
> + write_flush_pes_stream(s, st, ts_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, -1);
> ts_st->payload_size = 0;
> @@ -1920,6 +2078,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_extra_mux", "Enable 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 },
> --
> 1.7.10.4
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request at ffmpeg.org with subject "unsubscribe".
Andriy
More information about the ffmpeg-devel
mailing list