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 <agoracs...@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 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel