Message ID | 20170216182938.55393-3-ffmpeg@tmm1.net |
---|---|
State | New |
Headers | show |
On Thu, 16 Feb 2017 10:29:38 -0800 Aman Gupta <ffmpeg@tmm1.net> wrote: > From: Aman Gupta <aman@tmm1.net> > > On some platforms (observed on macOS Sierra with 12" macbook), the VT > decoder will start returning errors when encountering an SPS change in > the h264 bitstream. With this patch, the kVTVideoDecoderBadDataErr > response from the decoder is caught and the decompression session is > recreated with a new avcC. The "bad data" is then fed into the new > decompression session so that it can be decoded correctly. > > I discovered the underlying issue here by running ffmpeg with lldb, > which causes macOS to display debug information from the VT hardware > decoder on stderr. The following errors were shown, which indicated the > need to restart the decoder session with a new SPS/avcC: > > ffmpeg[15127:4094995] GVA error: SPS mismatch ... > ffmpeg[15127:4094995] GVA error: AVF_PushMetaData, first field kAVF_QT0_SPSPPSBoundaryMarker > ffmpeg[15127:4094995] GVA error: pushMetaData, submitNewJobs > ffmpeg[15127:4094995] GVA warning: OutputQueueReadyCallback status = 1, buffer == 0x0 > > Tested with the following sample, which contains an SPS change midstream: > http://tmm1.s3.amazonaws.com/videotoolbox/spschange.ts > --- > libavcodec/videotoolbox.c | 28 ++++++++++++++++++++++++---- > 1 file changed, 24 insertions(+), 4 deletions(-) > > diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c > index 9be7bee..159d98d 100644 > --- a/libavcodec/videotoolbox.c > +++ b/libavcodec/videotoolbox.c > @@ -38,6 +38,9 @@ > > #define VIDEOTOOLBOX_ESDS_EXTRADATA_PADDING 12 > > +static void videotoolbox_stop(AVCodecContext *avctx); > +static int videotoolbox_start(AVCodecContext *avctx); > + > static void videotoolbox_buffer_release(void *opaque, uint8_t *data) > { > CVPixelBufferRef cv_buffer = (CVImageBufferRef)data; > @@ -350,13 +353,25 @@ static int videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame) > int status; > AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; > VTContext *vtctx = avctx->internal->hwaccel_priv_data; > + int retry; > > av_buffer_unref(&frame->buf[0]); > > if (!videotoolbox->session || !vtctx->bitstream) > return AVERROR_INVALIDDATA; > > - status = videotoolbox_session_decode_frame(avctx); > + for (retry = 0; retry < 2; retry++) { Why retry? > + status = videotoolbox_session_decode_frame(avctx); > + > + if (status == kVTVideoDecoderBadDataErr) { > + av_log(avctx, AV_LOG_DEBUG, "vt decoder got bad data error, restarting..\n"); > + videotoolbox_stop(avctx); > + videotoolbox_start(avctx); Wouldn't Bad Things happen if the session failed to create for some reason? > + continue; > + } else { > + break; > + } > + } > > if (status) { > av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%d)\n", status); > @@ -506,7 +521,7 @@ static CMVideoFormatDescriptionRef videotoolbox_format_desc_create(CMVideoCodecT > return cm_fmt_desc; > } > > -static int videotoolbox_default_init(AVCodecContext *avctx) > +static int videotoolbox_start(AVCodecContext *avctx) > { > AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; > OSStatus status; > @@ -587,7 +602,12 @@ static int videotoolbox_default_init(AVCodecContext *avctx) > } > } > > -static void videotoolbox_default_free(AVCodecContext *avctx) > +static int videotoolbox_default_init(AVCodecContext *avctx) > +{ > + return videotoolbox_start(avctx); > +} > + > +static void videotoolbox_stop(AVCodecContext *avctx) > { > AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; > if (!videotoolbox) > @@ -696,7 +716,7 @@ int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext * > void av_videotoolbox_default_free(AVCodecContext *avctx) > { > > - videotoolbox_default_free(avctx); > + videotoolbox_stop(avctx); > av_freep(&avctx->hwaccel_context); > } > #endif /* CONFIG_VIDEOTOOLBOX */
diff --git a/libavcodec/videotoolbox.c b/libavcodec/videotoolbox.c index 9be7bee..159d98d 100644 --- a/libavcodec/videotoolbox.c +++ b/libavcodec/videotoolbox.c @@ -38,6 +38,9 @@ #define VIDEOTOOLBOX_ESDS_EXTRADATA_PADDING 12 +static void videotoolbox_stop(AVCodecContext *avctx); +static int videotoolbox_start(AVCodecContext *avctx); + static void videotoolbox_buffer_release(void *opaque, uint8_t *data) { CVPixelBufferRef cv_buffer = (CVImageBufferRef)data; @@ -350,13 +353,25 @@ static int videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame) int status; AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; VTContext *vtctx = avctx->internal->hwaccel_priv_data; + int retry; av_buffer_unref(&frame->buf[0]); if (!videotoolbox->session || !vtctx->bitstream) return AVERROR_INVALIDDATA; - status = videotoolbox_session_decode_frame(avctx); + for (retry = 0; retry < 2; retry++) { + status = videotoolbox_session_decode_frame(avctx); + + if (status == kVTVideoDecoderBadDataErr) { + av_log(avctx, AV_LOG_DEBUG, "vt decoder got bad data error, restarting..\n"); + videotoolbox_stop(avctx); + videotoolbox_start(avctx); + continue; + } else { + break; + } + } if (status) { av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%d)\n", status); @@ -506,7 +521,7 @@ static CMVideoFormatDescriptionRef videotoolbox_format_desc_create(CMVideoCodecT return cm_fmt_desc; } -static int videotoolbox_default_init(AVCodecContext *avctx) +static int videotoolbox_start(AVCodecContext *avctx) { AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; OSStatus status; @@ -587,7 +602,12 @@ static int videotoolbox_default_init(AVCodecContext *avctx) } } -static void videotoolbox_default_free(AVCodecContext *avctx) +static int videotoolbox_default_init(AVCodecContext *avctx) +{ + return videotoolbox_start(avctx); +} + +static void videotoolbox_stop(AVCodecContext *avctx) { AVVideotoolboxContext *videotoolbox = avctx->hwaccel_context; if (!videotoolbox) @@ -696,7 +716,7 @@ int av_videotoolbox_default_init2(AVCodecContext *avctx, AVVideotoolboxContext * void av_videotoolbox_default_free(AVCodecContext *avctx) { - videotoolbox_default_free(avctx); + videotoolbox_stop(avctx); av_freep(&avctx->hwaccel_context); } #endif /* CONFIG_VIDEOTOOLBOX */
From: Aman Gupta <aman@tmm1.net> On some platforms (observed on macOS Sierra with 12" macbook), the VT decoder will start returning errors when encountering an SPS change in the h264 bitstream. With this patch, the kVTVideoDecoderBadDataErr response from the decoder is caught and the decompression session is recreated with a new avcC. The "bad data" is then fed into the new decompression session so that it can be decoded correctly. I discovered the underlying issue here by running ffmpeg with lldb, which causes macOS to display debug information from the VT hardware decoder on stderr. The following errors were shown, which indicated the need to restart the decoder session with a new SPS/avcC: ffmpeg[15127:4094995] GVA error: SPS mismatch ... ffmpeg[15127:4094995] GVA error: AVF_PushMetaData, first field kAVF_QT0_SPSPPSBoundaryMarker ffmpeg[15127:4094995] GVA error: pushMetaData, submitNewJobs ffmpeg[15127:4094995] GVA warning: OutputQueueReadyCallback status = 1, buffer == 0x0 Tested with the following sample, which contains an SPS change midstream: http://tmm1.s3.amazonaws.com/videotoolbox/spschange.ts --- libavcodec/videotoolbox.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-)