@@ -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; 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
+ || (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; 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;
- }
- }
+ 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 },