avcodec/videotoolbox: use decode_params to propagate H264 PPS changes and restart on SPS changes
This fixes decoding of H264 video samples with SPS and PPS changes. See for example https://s3.amazonaws.com/tmm1/videotoolbox/spschange.ts, which previously stalled the decoder and failed to produce any new frames after the SPS change. Also see https://s3.amazonaws.com/tmm1/videotoolbox/ppschange.ts, which uses multiple PPS and would previously cause VT decode failures. If the VideoToolbox session needs to be restarted, and videotoolbox_start() fails for some reason (for instance, if the video is interlaced and the decoder is running on iOS), avcodec will return AVERROR_EXTERNAL. This can be used by the API user to switch to another decoder. Signed-off-by: Aman Gupta <aman@tmm1.net>
This commit is contained in:
parent
872add0854
commit
9519983c0f
@ -42,6 +42,9 @@ enum { kCMVideoCodecType_HEVC = 'hvc1' };
|
|||||||
|
|
||||||
#define VIDEOTOOLBOX_ESDS_EXTRADATA_PADDING 12
|
#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)
|
static void videotoolbox_buffer_release(void *opaque, uint8_t *data)
|
||||||
{
|
{
|
||||||
CVPixelBufferRef cv_buffer = (CVImageBufferRef)data;
|
CVPixelBufferRef cv_buffer = (CVImageBufferRef)data;
|
||||||
@ -307,6 +310,27 @@ int ff_videotoolbox_h264_start_frame(AVCodecContext *avctx,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int videotoolbox_h264_decode_params(AVCodecContext *avctx,
|
||||||
|
int type,
|
||||||
|
const uint8_t *buffer,
|
||||||
|
uint32_t size)
|
||||||
|
{
|
||||||
|
VTContext *vtctx = avctx->internal->hwaccel_priv_data;
|
||||||
|
|
||||||
|
if (type == H264_NAL_SPS) {
|
||||||
|
if (!vtctx->sps || vtctx->sps_len != size || memcmp(buffer, vtctx->sps, size) != 0) {
|
||||||
|
vtctx->sps = av_fast_realloc(vtctx->sps, &vtctx->sps_capa, size);
|
||||||
|
if (vtctx->sps)
|
||||||
|
memcpy(vtctx->sps, buffer, size);
|
||||||
|
vtctx->reconfig_needed = true;
|
||||||
|
vtctx->sps_len = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass-through new PPS to the decoder
|
||||||
|
return ff_videotoolbox_h264_decode_slice(avctx, buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
int ff_videotoolbox_h264_decode_slice(AVCodecContext *avctx,
|
int ff_videotoolbox_h264_decode_slice(AVCodecContext *avctx,
|
||||||
const uint8_t *buffer,
|
const uint8_t *buffer,
|
||||||
uint32_t size)
|
uint32_t size)
|
||||||
@ -339,6 +363,7 @@ int ff_videotoolbox_uninit(AVCodecContext *avctx)
|
|||||||
VTContext *vtctx = avctx->internal->hwaccel_priv_data;
|
VTContext *vtctx = avctx->internal->hwaccel_priv_data;
|
||||||
if (vtctx) {
|
if (vtctx) {
|
||||||
av_freep(&vtctx->bitstream);
|
av_freep(&vtctx->bitstream);
|
||||||
|
av_freep(&vtctx->sps);
|
||||||
if (vtctx->frame)
|
if (vtctx->frame)
|
||||||
CVPixelBufferRelease(vtctx->frame);
|
CVPixelBufferRelease(vtctx->frame);
|
||||||
}
|
}
|
||||||
@ -591,17 +616,30 @@ static int videotoolbox_common_end_frame(AVCodecContext *avctx, AVFrame *frame)
|
|||||||
AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx);
|
AVVideotoolboxContext *videotoolbox = videotoolbox_get_context(avctx);
|
||||||
VTContext *vtctx = avctx->internal->hwaccel_priv_data;
|
VTContext *vtctx = avctx->internal->hwaccel_priv_data;
|
||||||
|
|
||||||
if (!videotoolbox->session || !vtctx->bitstream)
|
if (vtctx->reconfig_needed == true) {
|
||||||
|
vtctx->reconfig_needed = false;
|
||||||
|
av_log(avctx, AV_LOG_VERBOSE, "VideoToolbox decoder needs reconfig, restarting..\n");
|
||||||
|
videotoolbox_stop(avctx);
|
||||||
|
if (videotoolbox_start(avctx) != 0) {
|
||||||
|
return AVERROR_EXTERNAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!videotoolbox->session || !vtctx->bitstream || !vtctx->bitstream_size)
|
||||||
return AVERROR_INVALIDDATA;
|
return AVERROR_INVALIDDATA;
|
||||||
|
|
||||||
status = videotoolbox_session_decode_frame(avctx);
|
status = videotoolbox_session_decode_frame(avctx);
|
||||||
if (status != noErr) {
|
if (status != noErr) {
|
||||||
|
if (status == kVTVideoDecoderMalfunctionErr || status == kVTInvalidSessionErr)
|
||||||
|
vtctx->reconfig_needed = true;
|
||||||
av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%s, %d)\n", videotoolbox_error_string(status), (int)status);
|
av_log(avctx, AV_LOG_ERROR, "Failed to decode frame (%s, %d)\n", videotoolbox_error_string(status), (int)status);
|
||||||
return AVERROR_UNKNOWN;
|
return AVERROR_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vtctx->frame)
|
if (!vtctx->frame) {
|
||||||
|
vtctx->reconfig_needed = true;
|
||||||
return AVERROR_UNKNOWN;
|
return AVERROR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
return videotoolbox_buffer_create(avctx, frame);
|
return videotoolbox_buffer_create(avctx, frame);
|
||||||
}
|
}
|
||||||
@ -1018,6 +1056,7 @@ AVHWAccel ff_h264_videotoolbox_hwaccel = {
|
|||||||
.alloc_frame = ff_videotoolbox_alloc_frame,
|
.alloc_frame = ff_videotoolbox_alloc_frame,
|
||||||
.start_frame = ff_videotoolbox_h264_start_frame,
|
.start_frame = ff_videotoolbox_h264_start_frame,
|
||||||
.decode_slice = ff_videotoolbox_h264_decode_slice,
|
.decode_slice = ff_videotoolbox_h264_decode_slice,
|
||||||
|
.decode_params = videotoolbox_h264_decode_params,
|
||||||
.end_frame = videotoolbox_h264_end_frame,
|
.end_frame = videotoolbox_h264_end_frame,
|
||||||
.frame_params = videotoolbox_frame_params,
|
.frame_params = videotoolbox_frame_params,
|
||||||
.init = videotoolbox_common_init,
|
.init = videotoolbox_common_init,
|
||||||
|
@ -38,6 +38,12 @@ typedef struct VTContext {
|
|||||||
// Non-NULL if the new hwaccel API is used. This is only a separate struct
|
// Non-NULL if the new hwaccel API is used. This is only a separate struct
|
||||||
// to ease compatibility with the old API.
|
// to ease compatibility with the old API.
|
||||||
struct AVVideotoolboxContext *vt_ctx;
|
struct AVVideotoolboxContext *vt_ctx;
|
||||||
|
|
||||||
|
// Current H264 parameters (used to trigger decoder restart on SPS changes).
|
||||||
|
uint8_t *sps;
|
||||||
|
uint32_t sps_len;
|
||||||
|
unsigned int sps_capa;
|
||||||
|
bool reconfig_needed;
|
||||||
} VTContext;
|
} VTContext;
|
||||||
|
|
||||||
int ff_videotoolbox_alloc_frame(AVCodecContext *avctx, AVFrame *frame);
|
int ff_videotoolbox_alloc_frame(AVCodecContext *avctx, AVFrame *frame);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user