1
0
mirror of https://github.com/mpv-player/mpv synced 2025-02-17 13:17:13 +00:00

demux_mkv: add hack to pass along x264 version to decoder

This fixes when resuming certain broken h264 files encoded by x264. See
FFmpeg commit 840b41b2a643fc8f0617c0370125a19c02c6b586 about the x264
bug itself.

Normally, the unregistered user data SEI (that contains the x264 version
string) is informational only. But libavcodec uses it to workaround a
x264 bug, which was recently fixed in both libavcodec and x264. The fact
that both encoder and decoder were buggy is the reason that it was not
found earlier, and there are apparently a lot of files around created by
the broken decoder. If libavcodec sees the SEI, this bug can be worked
around by using the old behavior.

If you resume a file with mpv (i.e. seeking when the file loads),
libavcodec never sees the first video packet. Consequently it has to
assume the file is not broken, and never applies the workaround,
resulting in garbage being played.

Fix this by always feeding the first video packet to the decoder on
init, and then flushing the codec (to avoid that an unwanted image is
output). Flushing the codec does not remove info such as the x264
version. We also abuse the fact that the first avcodec_send_packet()
always pushes the frame into the decoder (so we don't have to trigger
the decoder by requsting an output frame).
This commit is contained in:
wm4 2017-12-26 06:09:26 +01:00 committed by Kevin Mitchell
parent d480b1261b
commit 5e50fe3049
3 changed files with 55 additions and 0 deletions

View File

@ -254,6 +254,7 @@ const struct m_sub_options demux_mkv_conf = {
static void probe_last_timestamp(struct demuxer *demuxer, int64_t start_pos);
static void probe_first_timestamp(struct demuxer *demuxer);
static int read_next_block_into_queue(demuxer_t *demuxer);
static void free_block(struct block_info *block);
#define AAC_SYNC_EXTENSION_TYPE 0x02b7
@ -1878,6 +1879,41 @@ static int demux_mkv_open_sub(demuxer_t *demuxer, mkv_track_t *track)
return 0;
}
static void probe_x264_garbage(demuxer_t *demuxer)
{
mkv_demuxer_t *mkv_d = demuxer->priv;
for (int n = 0; n < mkv_d->num_tracks; n++) {
mkv_track_t *track = mkv_d->tracks[n];
struct sh_stream *sh = track->stream;
if (!sh || sh->type != STREAM_VIDEO)
continue;
if (sh->codec->codec && strcmp(sh->codec->codec, "h264") != 0)
continue;
struct block_info *block = NULL;
// Find first block for this track.
// Restrict reading number of total packets. (Arbitrary to avoid bloat.)
for (int i = 0; i < 100; i++) {
if (i >= mkv_d->num_blocks && read_next_block_into_queue(demuxer) < 1)
break;
if (mkv_d->blocks[i].track == track) {
block = &mkv_d->blocks[i];
break;
}
}
if (!block || block->num_laces < 1)
continue;
sh->codec->first_packet = new_demux_packet_from_buf(block->laces[0]);
talloc_steal(mkv_d, sh->codec->first_packet);
}
}
static int read_ebml_header(demuxer_t *demuxer)
{
stream_t *s = demuxer->stream;
@ -2077,6 +2113,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
probe_first_timestamp(demuxer);
if (mkv_d->opts->probe_duration)
probe_last_timestamp(demuxer, start_pos);
probe_x264_garbage(demuxer);
return 0;
}

View File

@ -76,6 +76,11 @@ struct mp_codec_params {
// Timestamp granularity for converting double<->rational timestamps.
int native_tb_num, native_tb_den;
// Used by an obscure bug workaround mechanism. As an exception to the usual
// rules, demuxers are allowed to set this after adding the sh_stream, but
// only before the demuxer open call returns.
struct demux_packet *first_packet;
// STREAM_AUDIO
int samplerate;
struct mp_chmap channels;

View File

@ -666,6 +666,19 @@ static void init_avctx(struct dec_video *vd)
if (avcodec_open2(avctx, lavc_codec, NULL) < 0)
goto error;
// Sometimes, the first packet contains information required for correct
// decoding of the rest of the stream. The only currently known case is the
// x264 build number (encoded in a SEI element), needed to enable a
// workaround for broken 4:4:4 streams produced by older x264 versions.
if (lavc_codec->id == AV_CODEC_ID_H264 && c->first_packet) {
AVPacket avpkt;
mp_set_av_packet(&avpkt, c->first_packet, &ctx->codec_timebase);
avcodec_send_packet(avctx, &avpkt);
avcodec_receive_frame(avctx, ctx->pic);
av_frame_unref(ctx->pic);
avcodec_flush_buffers(ctx->avctx);
}
return;
error: