Commit 86910b15 authored by Philip Langdale's avatar Philip Langdale Committed by Timo Rothenpieler

cuvid: Implement flush to support seeking in media players

Right now, if we attempt to use cuvid in a media player and then
try to seek, the decoder will happily pass out whatever frames were
already in flight before the seek.

There is both the output queue in our code and some number of frames
within the cuvid decoder that need to be accounted for.

cuvid doesn't support flush, so our only choice is to do a brute-force
re-creation of the decoder, which also implies re-creating the parser,
but this is fine.

The only subtlty is that there is sanity check code in decoder
initialisation that wants to make sure the HWContextFrame hasn't already
been initialised. This is a fair check to do at the beginning but not
after a flush, so it has to be made conditional.
Signed-off-by: 's avatarPhilip Langdale <philipl@overt.org>
Signed-off-by: 's avatarTimo Rothenpieler <timo@rothenpieler.org>
parent 1891dfe0
...@@ -46,9 +46,13 @@ typedef struct CuvidContext ...@@ -46,9 +46,13 @@ typedef struct CuvidContext
AVFifoBuffer *frame_queue; AVFifoBuffer *frame_queue;
int internal_error; int internal_error;
int ever_flushed;
cudaVideoCodec codec_type; cudaVideoCodec codec_type;
cudaVideoChromaFormat chroma_format; cudaVideoChromaFormat chroma_format;
CUVIDPARSERPARAMS cuparseinfo;
CUVIDEOFORMATEX cuparse_ext;
} CuvidContext; } CuvidContext;
static int check_cu(AVCodecContext *avctx, CUresult err, const char *func) static int check_cu(AVCodecContext *avctx, CUresult err, const char *func)
...@@ -127,7 +131,7 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form ...@@ -127,7 +131,7 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
return 0; return 0;
} }
if (hwframe_ctx->pool) { if (hwframe_ctx->pool && !ctx->ever_flushed) {
av_log(avctx, AV_LOG_ERROR, "AVHWFramesContext is already initialized\n"); av_log(avctx, AV_LOG_ERROR, "AVHWFramesContext is already initialized\n");
ctx->internal_error = AVERROR(EINVAL); ctx->internal_error = AVERROR(EINVAL);
return 0; return 0;
...@@ -164,14 +168,16 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form ...@@ -164,14 +168,16 @@ static int CUDAAPI cuvid_handle_video_sequence(void *opaque, CUVIDEOFORMAT* form
if (ctx->internal_error < 0) if (ctx->internal_error < 0)
return 0; return 0;
hwframe_ctx->format = AV_PIX_FMT_CUDA; if (!hwframe_ctx->pool) {
hwframe_ctx->sw_format = AV_PIX_FMT_NV12; hwframe_ctx->format = AV_PIX_FMT_CUDA;
hwframe_ctx->width = FFALIGN(avctx->coded_width, 32); hwframe_ctx->sw_format = AV_PIX_FMT_NV12;
hwframe_ctx->height = FFALIGN(avctx->coded_height, 32); hwframe_ctx->width = FFALIGN(avctx->coded_width, 32);
hwframe_ctx->height = FFALIGN(avctx->coded_height, 32);
if ((ctx->internal_error = av_hwframe_ctx_init(ctx->hwframe)) < 0) { if ((ctx->internal_error = av_hwframe_ctx_init(ctx->hwframe)) < 0) {
av_log(avctx, AV_LOG_ERROR, "av_hwframe_ctx_init failed\n"); av_log(avctx, AV_LOG_ERROR, "av_hwframe_ctx_init failed\n");
return 0; return 0;
}
} }
return 1; return 1;
...@@ -461,8 +467,6 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) ...@@ -461,8 +467,6 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
AVCUDADeviceContext *device_hwctx; AVCUDADeviceContext *device_hwctx;
AVHWDeviceContext *device_ctx; AVHWDeviceContext *device_ctx;
AVHWFramesContext *hwframe_ctx; AVHWFramesContext *hwframe_ctx;
CUVIDPARSERPARAMS cuparseinfo;
CUVIDEOFORMATEX cuparse_ext;
CUVIDSOURCEDATAPACKET seq_pkt; CUVIDSOURCEDATAPACKET seq_pkt;
CUdevice device; CUdevice device;
CUcontext cuda_ctx = NULL; CUcontext cuda_ctx = NULL;
...@@ -550,61 +554,61 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) ...@@ -550,61 +554,61 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
} }
} }
memset(&cuparseinfo, 0, sizeof(cuparseinfo)); memset(&ctx->cuparseinfo, 0, sizeof(ctx->cuparseinfo));
memset(&cuparse_ext, 0, sizeof(cuparse_ext)); memset(&ctx->cuparse_ext, 0, sizeof(ctx->cuparse_ext));
memset(&seq_pkt, 0, sizeof(seq_pkt)); memset(&seq_pkt, 0, sizeof(seq_pkt));
cuparseinfo.pExtVideoInfo = &cuparse_ext; ctx->cuparseinfo.pExtVideoInfo = &ctx->cuparse_ext;
switch (avctx->codec->id) { switch (avctx->codec->id) {
#if CONFIG_H263_CUVID_DECODER #if CONFIG_H263_CUVID_DECODER
case AV_CODEC_ID_H263: case AV_CODEC_ID_H263:
cuparseinfo.CodecType = cudaVideoCodec_MPEG4; ctx->cuparseinfo.CodecType = cudaVideoCodec_MPEG4;
break; break;
#endif #endif
#if CONFIG_H264_CUVID_DECODER #if CONFIG_H264_CUVID_DECODER
case AV_CODEC_ID_H264: case AV_CODEC_ID_H264:
cuparseinfo.CodecType = cudaVideoCodec_H264; ctx->cuparseinfo.CodecType = cudaVideoCodec_H264;
break; break;
#endif #endif
#if CONFIG_HEVC_CUVID_DECODER #if CONFIG_HEVC_CUVID_DECODER
case AV_CODEC_ID_HEVC: case AV_CODEC_ID_HEVC:
cuparseinfo.CodecType = cudaVideoCodec_HEVC; ctx->cuparseinfo.CodecType = cudaVideoCodec_HEVC;
break; break;
#endif #endif
#if CONFIG_MJPEG_CUVID_DECODER #if CONFIG_MJPEG_CUVID_DECODER
case AV_CODEC_ID_MJPEG: case AV_CODEC_ID_MJPEG:
cuparseinfo.CodecType = cudaVideoCodec_JPEG; ctx->cuparseinfo.CodecType = cudaVideoCodec_JPEG;
break; break;
#endif #endif
#if CONFIG_MPEG1_CUVID_DECODER #if CONFIG_MPEG1_CUVID_DECODER
case AV_CODEC_ID_MPEG1VIDEO: case AV_CODEC_ID_MPEG1VIDEO:
cuparseinfo.CodecType = cudaVideoCodec_MPEG1; ctx->cuparseinfo.CodecType = cudaVideoCodec_MPEG1;
break; break;
#endif #endif
#if CONFIG_MPEG2_CUVID_DECODER #if CONFIG_MPEG2_CUVID_DECODER
case AV_CODEC_ID_MPEG2VIDEO: case AV_CODEC_ID_MPEG2VIDEO:
cuparseinfo.CodecType = cudaVideoCodec_MPEG2; ctx->cuparseinfo.CodecType = cudaVideoCodec_MPEG2;
break; break;
#endif #endif
#if CONFIG_MPEG4_CUVID_DECODER #if CONFIG_MPEG4_CUVID_DECODER
case AV_CODEC_ID_MPEG4: case AV_CODEC_ID_MPEG4:
cuparseinfo.CodecType = cudaVideoCodec_MPEG4; ctx->cuparseinfo.CodecType = cudaVideoCodec_MPEG4;
break; break;
#endif #endif
#if CONFIG_VP8_CUVID_DECODER #if CONFIG_VP8_CUVID_DECODER
case AV_CODEC_ID_VP8: case AV_CODEC_ID_VP8:
cuparseinfo.CodecType = cudaVideoCodec_VP8; ctx->cuparseinfo.CodecType = cudaVideoCodec_VP8;
break; break;
#endif #endif
#if CONFIG_VP9_CUVID_DECODER #if CONFIG_VP9_CUVID_DECODER
case AV_CODEC_ID_VP9: case AV_CODEC_ID_VP9:
cuparseinfo.CodecType = cudaVideoCodec_VP9; ctx->cuparseinfo.CodecType = cudaVideoCodec_VP9;
break; break;
#endif #endif
#if CONFIG_VC1_CUVID_DECODER #if CONFIG_VC1_CUVID_DECODER
case AV_CODEC_ID_VC1: case AV_CODEC_ID_VC1:
cuparseinfo.CodecType = cudaVideoCodec_VC1; ctx->cuparseinfo.CodecType = cudaVideoCodec_VC1;
break; break;
#endif #endif
default: default:
...@@ -630,38 +634,38 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) ...@@ -630,38 +634,38 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
goto error; goto error;
} }
cuparse_ext.format.seqhdr_data_length = ctx->bsf->par_out->extradata_size; ctx->cuparse_ext.format.seqhdr_data_length = ctx->bsf->par_out->extradata_size;
memcpy(cuparse_ext.raw_seqhdr_data, memcpy(ctx->cuparse_ext.raw_seqhdr_data,
ctx->bsf->par_out->extradata, ctx->bsf->par_out->extradata,
FFMIN(sizeof(cuparse_ext.raw_seqhdr_data), ctx->bsf->par_out->extradata_size)); FFMIN(sizeof(ctx->cuparse_ext.raw_seqhdr_data), ctx->bsf->par_out->extradata_size));
} else if (avctx->extradata_size > 0) { } else if (avctx->extradata_size > 0) {
cuparse_ext.format.seqhdr_data_length = avctx->extradata_size; ctx->cuparse_ext.format.seqhdr_data_length = avctx->extradata_size;
memcpy(cuparse_ext.raw_seqhdr_data, memcpy(ctx->cuparse_ext.raw_seqhdr_data,
avctx->extradata, avctx->extradata,
FFMIN(sizeof(cuparse_ext.raw_seqhdr_data), avctx->extradata_size)); FFMIN(sizeof(ctx->cuparse_ext.raw_seqhdr_data), avctx->extradata_size));
} }
cuparseinfo.ulMaxNumDecodeSurfaces = MAX_FRAME_COUNT; ctx->cuparseinfo.ulMaxNumDecodeSurfaces = MAX_FRAME_COUNT;
cuparseinfo.ulMaxDisplayDelay = 4; ctx->cuparseinfo.ulMaxDisplayDelay = 4;
cuparseinfo.pUserData = avctx; ctx->cuparseinfo.pUserData = avctx;
cuparseinfo.pfnSequenceCallback = cuvid_handle_video_sequence; ctx->cuparseinfo.pfnSequenceCallback = cuvid_handle_video_sequence;
cuparseinfo.pfnDecodePicture = cuvid_handle_picture_decode; ctx->cuparseinfo.pfnDecodePicture = cuvid_handle_picture_decode;
cuparseinfo.pfnDisplayPicture = cuvid_handle_picture_display; ctx->cuparseinfo.pfnDisplayPicture = cuvid_handle_picture_display;
ret = CHECK_CU(cuCtxPushCurrent(cuda_ctx)); ret = CHECK_CU(cuCtxPushCurrent(cuda_ctx));
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = cuvid_test_dummy_decoder(avctx, &cuparseinfo); ret = cuvid_test_dummy_decoder(avctx, &ctx->cuparseinfo);
if (ret < 0) if (ret < 0)
goto error; goto error;
ret = CHECK_CU(cuvidCreateVideoParser(&ctx->cuparser, &cuparseinfo)); ret = CHECK_CU(cuvidCreateVideoParser(&ctx->cuparser, &ctx->cuparseinfo));
if (ret < 0) if (ret < 0)
goto error; goto error;
seq_pkt.payload = cuparse_ext.raw_seqhdr_data; seq_pkt.payload = ctx->cuparse_ext.raw_seqhdr_data;
seq_pkt.payload_size = cuparse_ext.format.seqhdr_data_length; seq_pkt.payload_size = ctx->cuparse_ext.format.seqhdr_data_length;
if (seq_pkt.payload && seq_pkt.payload_size) { if (seq_pkt.payload && seq_pkt.payload_size) {
ret = CHECK_CU(cuvidParseVideoData(ctx->cuparser, &seq_pkt)); ret = CHECK_CU(cuvidParseVideoData(ctx->cuparser, &seq_pkt));
...@@ -673,6 +677,8 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx) ...@@ -673,6 +677,8 @@ static av_cold int cuvid_decode_init(AVCodecContext *avctx)
if (ret < 0) if (ret < 0)
goto error; goto error;
ctx->ever_flushed = 0;
return 0; return 0;
error: error:
...@@ -680,6 +686,51 @@ error: ...@@ -680,6 +686,51 @@ error:
return ret; return ret;
} }
static void cuvid_flush(AVCodecContext *avctx)
{
CuvidContext *ctx = avctx->priv_data;
AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)ctx->hwdevice->data;
AVCUDADeviceContext *device_hwctx = device_ctx->hwctx;
CUcontext dummy, cuda_ctx = device_hwctx->cuda_ctx;
int ret;
ctx->ever_flushed = 1;
ret = CHECK_CU(cuCtxPushCurrent(cuda_ctx));
if (ret < 0)
goto error;
av_fifo_freep(&ctx->frame_queue);
ctx->frame_queue = av_fifo_alloc(MAX_FRAME_COUNT * sizeof(CUVIDPARSERDISPINFO));
if (!ctx->frame_queue) {
av_log(avctx, AV_LOG_ERROR, "Failed to recreate frame queue on flush\n");
return;
}
if (ctx->cudecoder) {
cuvidDestroyDecoder(ctx->cudecoder);
ctx->cudecoder = NULL;
}
if (ctx->cuparser) {
cuvidDestroyVideoParser(ctx->cuparser);
ctx->cuparser = NULL;
}
ret = CHECK_CU(cuvidCreateVideoParser(&ctx->cuparser, &ctx->cuparseinfo));
if (ret < 0)
goto error;
ret = CHECK_CU(cuCtxPopCurrent(&dummy));
if (ret < 0)
goto error;
return;
error:
av_log(avctx, AV_LOG_ERROR, "CUDA reinit on flush failed\n");
}
#define DEFINE_CUVID_CODEC(x, X) \ #define DEFINE_CUVID_CODEC(x, X) \
AVHWAccel ff_##x##_cuvid_hwaccel = { \ AVHWAccel ff_##x##_cuvid_hwaccel = { \
.name = #x "_cuvid", \ .name = #x "_cuvid", \
...@@ -696,6 +747,7 @@ error: ...@@ -696,6 +747,7 @@ error:
.init = cuvid_decode_init, \ .init = cuvid_decode_init, \
.close = cuvid_decode_end, \ .close = cuvid_decode_end, \
.decode = cuvid_decode_frame, \ .decode = cuvid_decode_frame, \
.flush = cuvid_flush, \
.capabilities = AV_CODEC_CAP_DELAY, \ .capabilities = AV_CODEC_CAP_DELAY, \
.pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \ .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, \
AV_PIX_FMT_NV12, \ AV_PIX_FMT_NV12, \
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
#define LIBAVCODEC_VERSION_MAJOR 57 #define LIBAVCODEC_VERSION_MAJOR 57
#define LIBAVCODEC_VERSION_MINOR 55 #define LIBAVCODEC_VERSION_MINOR 55
#define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_MICRO 101
#define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
LIBAVCODEC_VERSION_MINOR, \ LIBAVCODEC_VERSION_MINOR, \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment