[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