From 734ccb6d6bb1bd35f67645c10cbd8118cd45133b Mon Sep 17 00:00:00 2001 From: Will Storey Date: Tue, 14 Mar 2017 12:00:07 -0700 Subject: [PATCH] Support input with non-monotonic dts This fixes issue #1. It appears there are inputs where the dts fluctuates. This is apparently inputs that are not well formed. However, I discovered that ffmpeg itself allows for this. If it detects this happening, it updates the pts/dts of the packet. To do this, it tracks the last output dts. This ensures the output dts is monotonic even if the input is not. I use essentially the same approach as in ffmpeg. --- videostreamer.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++-- videostreamer.h | 11 +++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/videostreamer.c b/videostreamer.c index 24aa8e2..6ba171e 100644 --- a/videostreamer.c +++ b/videostreamer.c @@ -239,6 +239,8 @@ vs_open_output(const char * const output_format_name, av_dict_free(&opts); + output->last_dts = 0; + return output; } @@ -319,8 +321,7 @@ vs_read_packet(const struct VSInput * input, AVPacket * const pkt, // 1 if we wrote the packet int vs_write_packet(const struct VSInput * const input, - const struct VSOutput * const output, AVPacket * const pkt, - const bool verbose) + struct VSOutput * const output, AVPacket * const pkt, const bool verbose) { if (!input || !output || !pkt) { printf("%s\n", strerror(EINVAL)); @@ -357,6 +358,45 @@ vs_write_packet(const struct VSInput * const input, return -1; } + // It is possible that the input is not well formed. Its dts (decompression + // timestamp) may fluctuate. av_write_frame() says that the dts must be + // strictly increasing. + // + // Packets from such inputs might look like: + // + // in: pts:18750 pts_time:0.208333 dts:18750 dts_time:0.208333 duration:3750 duration_time:0.0416667 stream_index:1 + // in: pts:0 pts_time:0 dts:0 dts_time:0 duration:3750 duration_time:0.0416667 stream_index:1 + // + // dts here is 18750 and then 0. + // + // If we try to write the second packet as is, we'll see this error: + // [mp4 @ 0x10f1ae0] Application provided invalid, non monotonically increasing dts to muxer in stream 1: 18750 >= 0 + // + // This is apparently a fairly common problem. In ffmpeg.c (as of ffmpeg + // 3.2.4 at least) there is logic to rewrite the dts and warn if it happens. + // Let's do the same. Note my logic is a little different here. + if (pkt->dts != AV_NOPTS_VALUE && output->last_dts != AV_NOPTS_VALUE && + pkt->dts < output->last_dts) { + int64_t const next_dts = output->last_dts+1; + + if (verbose) { + printf("Warning: Non-monotonous DTS in input stream. Previous: %" PRId64 " current: %" PRId64 ". changing to %" PRId64 ".\n", + output->last_dts, pkt->dts, next_dts); + } + + // We also apparently (ffmpeg.c does this too) need to update the pts. + // Otherwise we see an error like: + // + // [mp4 @ 0x555e6825ea60] pts (3780) < dts (22531) in stream 0 + + if (pkt->pts != AV_NOPTS_VALUE && pkt->pts >= pkt->dts) { + pkt->pts = FFMAX(pkt->pts, next_dts); + } + + pkt->dts = next_dts; + } + + // Set pts/dts if not set. Otherwise we will receive warnings like // // [mp4 @ 0x55688397bc40] Timestamps are unset in a packet for stream 0. This @@ -388,6 +428,10 @@ vs_write_packet(const struct VSInput * const input, } + // Track last dts we see (see where we use it for why). + output->last_dts = pkt->dts; + + // Write encoded frame (as a packet). // av_interleaved_write_frame() works too, but I don't think it is needed. diff --git a/videostreamer.h b/videostreamer.h index 07a98eb..f3ae483 100644 --- a/videostreamer.h +++ b/videostreamer.h @@ -3,6 +3,7 @@ #include #include +#include struct VSInput { AVFormatContext * format_ctx; @@ -11,6 +12,13 @@ struct VSInput { struct VSOutput { AVFormatContext * format_ctx; + + // Track the last dts we output. We use it to double check that dts is + // monotonic. + // + // I am not sure if it is available anywhere already. I tried + // AVStream->info->last_dts and that is apparently not set. + int64_t last_dts; }; void @@ -37,7 +45,6 @@ vs_read_packet(const struct VSInput *, AVPacket * const, int vs_write_packet(const struct VSInput * const, - const struct VSOutput * const, AVPacket * const, - const bool); + struct VSOutput * const, AVPacket * const, const bool); #endif