| @@ -47,6 +47,7 @@ typedef struct CuvidContext | |||||
| int internal_error; | int internal_error; | ||||
| int ever_flushed; | int ever_flushed; | ||||
| int decoder_flushing; | |||||
| cudaVideoCodec codec_type; | cudaVideoCodec codec_type; | ||||
| cudaVideoChromaFormat chroma_format; | cudaVideoChromaFormat chroma_format; | ||||
| @@ -217,20 +218,26 @@ static int CUDAAPI cuvid_handle_picture_display(void *opaque, CUVIDPARSERDISPINF | |||||
| return 1; | return 1; | ||||
| } | } | ||||
| static int cuvid_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) | |||||
| static int cuvid_decode_packet(AVCodecContext *avctx, const AVPacket *avpkt) | |||||
| { | { | ||||
| CuvidContext *ctx = avctx->priv_data; | CuvidContext *ctx = avctx->priv_data; | ||||
| AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)ctx->hwdevice->data; | AVHWDeviceContext *device_ctx = (AVHWDeviceContext*)ctx->hwdevice->data; | ||||
| AVCUDADeviceContext *device_hwctx = device_ctx->hwctx; | AVCUDADeviceContext *device_hwctx = device_ctx->hwctx; | ||||
| CUcontext dummy, cuda_ctx = device_hwctx->cuda_ctx; | CUcontext dummy, cuda_ctx = device_hwctx->cuda_ctx; | ||||
| AVFrame *frame = data; | |||||
| CUVIDSOURCEDATAPACKET cupkt; | CUVIDSOURCEDATAPACKET cupkt; | ||||
| AVPacket filter_packet = { 0 }; | AVPacket filter_packet = { 0 }; | ||||
| AVPacket filtered_packet = { 0 }; | AVPacket filtered_packet = { 0 }; | ||||
| CUdeviceptr mapped_frame = 0; | |||||
| int ret = 0, eret = 0; | |||||
| int ret = 0, eret = 0, is_flush = ctx->decoder_flushing; | |||||
| if (ctx->bsf && avpkt->size) { | |||||
| av_log(avctx, AV_LOG_TRACE, "cuvid_decode_packet\n"); | |||||
| if (is_flush && avpkt && avpkt->size) | |||||
| return AVERROR_EOF; | |||||
| if (av_fifo_size(ctx->frame_queue) / sizeof(CUVIDPARSERDISPINFO) > MAX_FRAME_COUNT - 2 && avpkt && avpkt->size) | |||||
| return AVERROR(EAGAIN); | |||||
| if (ctx->bsf && avpkt && avpkt->size) { | |||||
| if ((ret = av_packet_ref(&filter_packet, avpkt)) < 0) { | if ((ret = av_packet_ref(&filter_packet, avpkt)) < 0) { | ||||
| av_log(avctx, AV_LOG_ERROR, "av_packet_ref failed\n"); | av_log(avctx, AV_LOG_ERROR, "av_packet_ref failed\n"); | ||||
| return ret; | return ret; | ||||
| @@ -258,7 +265,7 @@ static int cuvid_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, | |||||
| memset(&cupkt, 0, sizeof(cupkt)); | memset(&cupkt, 0, sizeof(cupkt)); | ||||
| if (avpkt->size) { | |||||
| if (avpkt && avpkt->size) { | |||||
| cupkt.payload_size = avpkt->size; | cupkt.payload_size = avpkt->size; | ||||
| cupkt.payload = avpkt->data; | cupkt.payload = avpkt->data; | ||||
| @@ -271,15 +278,15 @@ static int cuvid_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, | |||||
| } | } | ||||
| } else { | } else { | ||||
| cupkt.flags = CUVID_PKT_ENDOFSTREAM; | cupkt.flags = CUVID_PKT_ENDOFSTREAM; | ||||
| ctx->decoder_flushing = 1; | |||||
| } | } | ||||
| ret = CHECK_CU(cuvidParseVideoData(ctx->cuparser, &cupkt)); | ret = CHECK_CU(cuvidParseVideoData(ctx->cuparser, &cupkt)); | ||||
| av_packet_unref(&filtered_packet); | av_packet_unref(&filtered_packet); | ||||
| if (ret < 0) { | |||||
| if (ret < 0) | |||||
| goto error; | goto error; | ||||
| } | |||||
| // cuvidParseVideoData doesn't return an error just because stuff failed... | // cuvidParseVideoData doesn't return an error just because stuff failed... | ||||
| if (ctx->internal_error) { | if (ctx->internal_error) { | ||||
| @@ -288,6 +295,40 @@ static int cuvid_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, | |||||
| goto error; | goto error; | ||||
| } | } | ||||
| error: | |||||
| eret = CHECK_CU(cuCtxPopCurrent(&dummy)); | |||||
| if (eret < 0) | |||||
| return eret; | |||||
| else if (ret < 0) | |||||
| return ret; | |||||
| else if (is_flush) | |||||
| return AVERROR_EOF; | |||||
| else | |||||
| return 0; | |||||
| } | |||||
| static int cuvid_output_frame(AVCodecContext *avctx, AVFrame *frame) | |||||
| { | |||||
| 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; | |||||
| CUdeviceptr mapped_frame = 0; | |||||
| int ret = 0, eret = 0; | |||||
| av_log(avctx, AV_LOG_TRACE, "cuvid_output_frame\n"); | |||||
| if (ctx->decoder_flushing) { | |||||
| ret = cuvid_decode_packet(avctx, NULL); | |||||
| if (ret < 0 && ret != AVERROR_EOF) | |||||
| return ret; | |||||
| } | |||||
| ret = CHECK_CU(cuCtxPushCurrent(cuda_ctx)); | |||||
| if (ret < 0) | |||||
| return ret; | |||||
| if (av_fifo_size(ctx->frame_queue)) { | if (av_fifo_size(ctx->frame_queue)) { | ||||
| CUVIDPARSERDISPINFO dispinfo; | CUVIDPARSERDISPINFO dispinfo; | ||||
| CUVIDPROCPARAMS params; | CUVIDPROCPARAMS params; | ||||
| @@ -394,10 +435,10 @@ static int cuvid_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, | |||||
| if (!dispinfo.progressive_frame) | if (!dispinfo.progressive_frame) | ||||
| frame->top_field_first = dispinfo.top_field_first; | frame->top_field_first = dispinfo.top_field_first; | ||||
| *got_frame = 1; | |||||
| } else if (ctx->decoder_flushing) { | |||||
| ret = AVERROR_EOF; | |||||
| } else { | } else { | ||||
| *got_frame = 0; | |||||
| ret = AVERROR(EAGAIN); | |||||
| } | } | ||||
| error: | error: | ||||
| @@ -412,6 +453,32 @@ error: | |||||
| return ret; | return ret; | ||||
| } | } | ||||
| static int cuvid_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt) | |||||
| { | |||||
| CuvidContext *ctx = avctx->priv_data; | |||||
| AVFrame *frame = data; | |||||
| int ret = 0; | |||||
| av_log(avctx, AV_LOG_TRACE, "cuvid_decode_frame\n"); | |||||
| if (!ctx->decoder_flushing) { | |||||
| ret = cuvid_decode_packet(avctx, avpkt); | |||||
| if (ret < 0) | |||||
| return ret; | |||||
| } | |||||
| ret = cuvid_output_frame(avctx, frame); | |||||
| if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | |||||
| *got_frame = 0; | |||||
| } else if (ret < 0) { | |||||
| return ret; | |||||
| } else { | |||||
| *got_frame = 1; | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| static av_cold int cuvid_decode_end(AVCodecContext *avctx) | static av_cold int cuvid_decode_end(AVCodecContext *avctx) | ||||
| { | { | ||||
| CuvidContext *ctx = avctx->priv_data; | CuvidContext *ctx = avctx->priv_data; | ||||
| @@ -756,6 +823,9 @@ static void cuvid_flush(AVCodecContext *avctx) | |||||
| if (ret < 0) | if (ret < 0) | ||||
| goto error; | goto error; | ||||
| ctx->prev_pts = INT64_MIN; | |||||
| ctx->decoder_flushing = 0; | |||||
| return; | return; | ||||
| error: | error: | ||||
| av_log(avctx, AV_LOG_ERROR, "CUDA reinit on flush failed\n"); | av_log(avctx, AV_LOG_ERROR, "CUDA reinit on flush failed\n"); | ||||
| @@ -777,6 +847,8 @@ static void cuvid_flush(AVCodecContext *avctx) | |||||
| .init = cuvid_decode_init, \ | .init = cuvid_decode_init, \ | ||||
| .close = cuvid_decode_end, \ | .close = cuvid_decode_end, \ | ||||
| .decode = cuvid_decode_frame, \ | .decode = cuvid_decode_frame, \ | ||||
| .send_packet = cuvid_decode_packet, \ | |||||
| .receive_frame = cuvid_output_frame, \ | |||||
| .flush = cuvid_flush, \ | .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, \ | ||||