Skip to content

Commit

Permalink
Improve timestamps handling at recording muxing
Browse files Browse the repository at this point in the history
Specifically:

- Restart recording on timestamping problems
- Reset last_mux_dts[] on media_writer::close()
- consistently use AV_NOPTS_VALUE as "NULL" for last_mux_dts
- don't update last_mux_dts to a bad one which isn't muxed

Reliability regressions were reported on certain setups attributed to
changes in 3.1.2 and 3.1.6.

One scenario in which problems arise is: an HDMI-to-RTSP encoder is
connected as a device to Bluecherry server, and the upstream video
source connected to the encoder is switched off and later back on.

This commit solves the issues for the scenario which we reproduced in
our lab.
  • Loading branch information
andrey-utkin committed Dec 11, 2024
1 parent 252fd3e commit 44f29e3
Showing 1 changed file with 27 additions and 8 deletions.
35 changes: 27 additions & 8 deletions server/media_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ static void bc_avlog(int val, const char *msg)
///////////////////////////////////////////////////////////////

media_writer::media_writer():
last_mux_dts{0, 0}
last_mux_dts{AV_NOPTS_VALUE, AV_NOPTS_VALUE}
{
}

Expand Down Expand Up @@ -78,44 +78,63 @@ bool media_writer::write_packet(const stream_packet &pkt)
static_assert(AVMEDIA_TYPE_VIDEO == 0);
static_assert(AVMEDIA_TYPE_AUDIO == 1);
int64_t &last_mux_dts = this->last_mux_dts[pkt.type];
bool update_last_mux_dts = true;
if (opkt.dts == AV_NOPTS_VALUE) {
// Do nothing.
// In ffmpeg, ffmpeg_mux.c:write_packet() does nothing and
// lavf/mux.c:compute_muxer_pkt_fields() deals with it for now
bc_log(Info, "Got bad dts=NOPTS on stream %d, passing to libavformat to handle", opkt.stream_index);
update_last_mux_dts = false;
} else if (last_mux_dts == AV_NOPTS_VALUE) {
// First packet ever. Initialize last_mux_dts and move on.
} else if (last_mux_dts < opkt.dts) {
// Do nothing. This is normal.
// Monotonically increasing timestamps. This is normal.

const int tolerated_gap_seconds = 1;
int64_t delta_dts = opkt.dts - last_mux_dts;
if (delta_dts > tolerated_gap_seconds * AV_TIME_BASE) {
// Too large a gap, assume discontinuity and close this recording file.
bc_log(Info, "Bad timestamp: too large a gap of %d seconds (%d tolerated), dts=%" PRId64 " while last was %" PRId64 " on stream %d, bailing out, causing the recording file to restart", (int)(delta_dts / AV_TIME_BASE), tolerated_gap_seconds, opkt.dts, last_mux_dts, opkt.stream_index);
return false;
}
} else {
// In this clause we're dealing with incorrect timestamp received from the source.
assert(last_mux_dts >= opkt.dts);
assert(last_mux_dts != AV_NOPTS_VALUE);
bc_log(Info, "Got bad dts=%" PRId64 " while last was %" PRId64 " on stream %d, setting to last+1", opkt.dts, last_mux_dts, opkt.stream_index);
opkt.dts = last_mux_dts + 1;
bc_log(Info, "Got bad dts=%" PRId64 " while last was %" PRId64 " on stream %d, bailing out, causing the recording file to restart", opkt.dts, last_mux_dts, opkt.stream_index);
return false;
}
last_mux_dts = opkt.dts;

bc_log(Debug, "av_interleaved_write_frame: dts=%" PRId64 " pts=%" PRId64 " tb=%d/%d s_i=%d k=%d",
opkt.dts, opkt.pts, out_ctx->streams[opkt.stream_index]->time_base.num,
out_ctx->streams[opkt.stream_index]->time_base.den, opkt.stream_index,
!!(opkt.flags & AV_PKT_FLAG_KEY));

auto last_mux_dts_uncommitted = opkt.dts; // opkt.dts is lost as av_interleaved_write_frame() frees opkt
re = av_interleaved_write_frame(out_ctx, &opkt);
if (re < 0)
{
bc_avlog(re, "Error writing frame to recording");
if (re == AVERROR(EINVAL)) {
bc_log(Error, "Error writing frame to recording. Likely timestamping problem. Ignoring.");
return true;
bc_log(Error, "Error writing frame to recording. Likely timestamping problem.");
} else {
bc_avlog(re, "Error writing frame to recording.");
}
update_last_mux_dts = false;
return false;
}
if (update_last_mux_dts) {
last_mux_dts = last_mux_dts_uncommitted;
}

return true;
}

void media_writer::close()
{
video_st = audio_st = NULL;
for (int i = 0; i < sizeof(last_mux_dts)/sizeof(last_mux_dts[0]); i++) {
last_mux_dts[i] = AV_NOPTS_VALUE;
}

if (out_ctx)
{
Expand Down

0 comments on commit 44f29e3

Please sign in to comment.