[FFmpeg-devel] [PATCH] mpegts: pcr period option for variable bitrate multiplexing
Predrag Filipovic
agoracsinc at gmail.com
Fri Mar 25 17:50:29 CET 2016
Enable proper PCR insertion for VBR multiplexing (muxrate not specified).
Insertion timing is based on video frame keys and frame period, consequently
pcr period precision is limited to +/- one video frame period.
Signed-off-by: Predrag Filipovic <agoracsinc at gmail.com>
---
libavformat/mpegtsenc.c | 80 +++++++++++++++++++++++++++++++++++++------------
1 file changed, 61 insertions(+), 19 deletions(-)
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 7656720..7ed9076 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -105,6 +105,7 @@ typedef struct MpegTSWrite {
int tables_version;
double pat_period;
double sdt_period;
+ int64_t last_pcr_ts;
int64_t last_pat_ts;
int64_t last_sdt_ts;
@@ -903,6 +904,9 @@ static int mpegts_init(AVFormatContext *s)
ts_st = pcr_st->priv_data;
if (ts->mux_rate > 1) {
+ if (ts->pcr_period >= INT_MAX/2) {
+ ts->pcr_period = PCR_RETRANS_TIME;
+ }
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 /
@@ -931,10 +935,19 @@ static int mpegts_init(AVFormatContext *s)
service->pcr_packet_period =
ts_st->user_tb.den / (10 * ts_st->user_tb.num);
}
- if (!service->pcr_packet_period)
+ /* if pcr_period specified, mark pcr_packet_period as NA (=INT_MAX) */
+ if (ts->pcr_period < INT_MAX/2) {
+ service->pcr_packet_period = INT_MAX;
+ } else {
+ if (!service->pcr_packet_period) {
service->pcr_packet_period = 1;
+ } else if (service->pcr_packet_period == INT_MAX) {
+ service->pcr_packet_period--;
+ }
+ }
}
+ ts->last_pcr_ts = AV_NOPTS_VALUE;
ts->last_pat_ts = AV_NOPTS_VALUE;
ts->last_sdt_ts = AV_NOPTS_VALUE;
// The user specified a period, use only it
@@ -1032,10 +1045,9 @@ static void mpegts_insert_null_packet(AVFormatContext *s)
avio_write(s->pb, buf, TS_PACKET_SIZE);
}
-/* Write a single transport stream packet with a PCR and no payload */
-static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
+/* Write a single transport stream packet with a PCR (value in arg) and no payload */
+static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st, int64_t pcr)
{
- MpegTSWrite *ts = s->priv_data;
MpegTSWriteStream *ts_st = st->priv_data;
uint8_t *q;
uint8_t buf[TS_PACKET_SIZE];
@@ -1050,7 +1062,7 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
*q++ = 0x10; /* Adaptation flags: PCR present */
/* PCR coded into 6 bytes */
- q += write_pcr_bits(q, get_pcr(ts, s->pb));
+ q += write_pcr_bits(q, pcr);
/* stuffing bytes */
memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
@@ -1109,6 +1121,9 @@ static uint8_t *get_ts_payload_start(uint8_t *pkt)
* 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. */
+/* PCR insertion for VBR TS is based on video frames time and key frames
+ * which leaves non-video TS with PCR insertion at key frames only.
+ * NOTE: PCR period "precision" for VBR TS is +/- one video frame period. */
static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
const uint8_t *payload, int payload_size,
int64_t pts, int64_t dts, int key)
@@ -1135,26 +1150,53 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
write_pcr = 0;
if (ts_st->pid == ts_st->service->pcr_pid) {
- if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
- ts_st->service->pcr_packet_count++;
+ if (ts->mux_rate > 1 || is_start)
+ if (ts_st->service->pcr_packet_period != INT_MAX) ts_st->service->pcr_packet_count++;
if (ts_st->service->pcr_packet_count >=
- ts_st->service->pcr_packet_period) {
+ ts_st->service->pcr_packet_period) { /* case is NA for VBR TS with specified pcr period*/
ts_st->service->pcr_packet_count = 0;
- write_pcr = 1;
+ if (ts_st->service->pcr_packet_period != INT_MAX) write_pcr = 1;
}
}
+ if (ts->mux_rate > 1) {
+ pcr = get_pcr(ts, s->pb);
+ } else {
+ pcr = (dts - delay) * 300;
+ }
+ if (pcr < 0) {
+ av_log(s, AV_LOG_WARNING, "calculated pcr < 0, TS is invalid\n");
+ pcr = 0;
+ }
+
if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
- (dts - get_pcr(ts, s->pb) / 300) > delay) {
+ (dts - pcr / 300) > delay) {
/* pcr insert gets priority over null packet insert */
if (write_pcr)
- mpegts_insert_pcr_only(s, st);
+ mpegts_insert_pcr_only(s, st, pcr);
else
mpegts_insert_null_packet(s);
/* recalculate write_pcr and possibly retransmit si_info */
continue;
}
+ /* Insert PCR for VBR TS with specified pcr_period based on video frame time */
+ if ( (ts->mux_rate <= 1) && (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
+ && (ts_st->service->pcr_packet_period == INT_MAX) )
+ {
+ if ( (dts != AV_NOPTS_VALUE && ts->last_pcr_ts == AV_NOPTS_VALUE) ||
+ (dts != AV_NOPTS_VALUE && (dts - delay - ts->last_pcr_ts) >= ts->pcr_period*90) )
+ {
+ ts->last_pcr_ts = pcr / 300;
+ ts_st->service->pcr_packet_count = 0;
+ if (ts_st->pid != ts_st->service->pcr_pid) {
+ mpegts_insert_pcr_only(s, st, pcr);
+ continue;
+ }
+ write_pcr = 1;
+ }
+ }
+
/* prepare packet header */
q = buf;
*q++ = 0x47;
@@ -1166,20 +1208,20 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
ts_st->cc = ts_st->cc + 1 & 0xf;
*q++ = 0x10 | ts_st->cc; // payload indicator + CC
if (key && is_start && pts != AV_NOPTS_VALUE) {
- // set Random Access for key frames
- if (ts_st->pid == ts_st->service->pcr_pid)
+ if (ts_st->pid == ts_st->service->pcr_pid) {
write_pcr = 1;
+ if ( (ts->mux_rate <= 1) && (ts_st->service->pcr_packet_period == INT_MAX) ) {
+ ts->last_pcr_ts = pcr / 300;
+ ts_st->service->pcr_packet_count = 0; /* keep track of last_pcr_ts */
+ }
+ }
+ // set Random Access for key frames
set_af_flag(buf, 0x40);
q = get_ts_payload_start(buf);
}
if (write_pcr) {
set_af_flag(buf, 0x10);
q = get_ts_payload_start(buf);
- // add 11, pcr references the last byte of program clock reference base
- if (ts->mux_rate > 1)
- pcr = get_pcr(ts, s->pb);
- else
- pcr = (dts - delay) * 300;
if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
extend_af(buf, write_pcr_bits(q, pcr));
@@ -1833,7 +1875,7 @@ static const AVOption options[] = {
{ .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
{ "pcr_period", "PCR retransmission time",
offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT,
- { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { .i64 = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
{ "pat_period", "PAT/PMT retransmission time limit in seconds",
offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE,
{ .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
--
1.9.1
More information about the ffmpeg-devel
mailing list