From e7c1ced0da1590e79a992d4ac8634edd46bc66a2 Mon Sep 17 00:00:00 2001 From: eisneinechse <42617957+eisneinechse@users.noreply.github.com> Date: Sat, 8 Sep 2018 21:17:24 -0700 Subject: [PATCH] Cleanup import video hardware accelerated and first attempt with nvidia cards. Still no error handling when the dimensions of the video are too large --- src/FFmpegReader.cpp | 242 ++++++++++++++++++------------------------- 1 file changed, 98 insertions(+), 144 deletions(-) diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 5ee6a7b2..7d9c27ca 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -34,6 +34,8 @@ using namespace openshot; int hw_de_on = 1; // Is set in UI int hw_de_supported = 0; // Is set by FFmpegReader +AVPixelFormat hw_de_av_pix_fmt = AV_PIX_FMT_NONE; +AVHWDeviceType hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI; FFmpegReader::FFmpegReader(string path) : last_frame(0), is_seeking(0), seeking_pts(0), seeking_frame(0), seek_count(0), @@ -108,112 +110,60 @@ bool AudioLocation::is_near(AudioLocation location, int samples_per_frame, int64 #if IS_FFMPEG_3_2 #pragma message "You are compiling with experimental hardware decode" -#if defined(__linux__) static enum AVPixelFormat get_vaapi_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) { const enum AVPixelFormat *p; for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { - if (*p == AV_PIX_FMT_VAAPI) - return *p; + if (*p == AV_PIX_FMT_VAAPI) { + hw_de_av_pix_fmt = AV_PIX_FMT_VAAPI; + hw_de_av_device_type = AV_HWDEVICE_TYPE_VAAPI; + return *p; + } + if (*p == AV_PIX_FMT_CUDA) { + hw_de_av_pix_fmt = AV_PIX_FMT_CUDA; + hw_de_av_device_type = AV_HWDEVICE_TYPE_CUDA; + return *p; + } } ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (Unable to decode this file using VA-API.)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1); hw_de_supported = 0; return AV_PIX_FMT_NONE; } -int is_hardware_decode_supported(int codecid) -{ - int ret; - switch (codecid) { - case AV_CODEC_ID_H264: - case AV_CODEC_ID_MPEG2VIDEO: - case AV_CODEC_ID_VC1: - case AV_CODEC_ID_WMV1: - case AV_CODEC_ID_WMV2: - case AV_CODEC_ID_WMV3: - ret = 1; - break; - default : - ret = 0; - break; - } - return ret; -} -#endif - -#if defined(_WIN32) -// Works for Windows 64 and Windows 32 -// FIXME Here goes the detection for Windows -// AV_HWDEVICE_TYPE_DXVA2 AV_PIX_FMT_DXVA2_VLD AV_HWDEVICE_TYPE_D3D11VA AV_PIX_FMT_D3D11 - -static enum AVPixelFormat get_dxva2_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) +static enum AVPixelFormat get_dx_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) { const enum AVPixelFormat *p; for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { - if (*p == AV_PIX_FMT_DXVA2_VLD) - return *p; + if (*p == AV_PIX_FMT_DXVA2_VLD) { + hw_de_av_pix_fmt = AV_PIX_FMT_DXVA2_VLD; + hw_de_av_device_type = AV_HWDEVICE_TYPE_DXVA2; + return *p; + } + if (*p == AV_PIX_FMT_D3D11) { + hw_de_av_pix_fmt = AV_PIX_FMT_D3D11; + hw_de_av_device_type = AV_HWDEVICE_TYPE_D3D11VA; + return *p; + } } ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (Unable to decode this file using DXVA2.)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1); hw_de_supported = 0; + hw_de_av_pix_fmt = AV_PIX_FMT_NONE; return AV_PIX_FMT_NONE; } -int is_hardware_decode_supported(int codecid) -{ - /* int ret; - switch (codecid) { - case AV_CODEC_ID_H264: - case AV_CODEC_ID_MPEG2VIDEO: - case AV_CODEC_ID_VC1: - case AV_CODEC_ID_WMV1: - case AV_CODEC_ID_WMV2: - case AV_CODEC_ID_WMV3: - ret = 1; - break; - default : - ret = 0; - break; - } - return ret;*/ - return 0; -} -#endif - -#if defined(__APPLE__) -// FIXME Here goes the detection for Mac -// Constants for MAC: AV_HWDEVICE_TYPE_QSV AV_PIX_FMT_QSV -int is_hardware_decode_supported(int codecid) -{ -/* int ret; - switch (codecid) { - case AV_CODEC_ID_H264: - case AV_CODEC_ID_MPEG2VIDEO: - case AV_CODEC_ID_VC1: - case AV_CODEC_ID_WMV1: - case AV_CODEC_ID_WMV2: - case AV_CODEC_ID_WMV3: - ret = 1; - break; - default : - ret = 0; - break; - } - return ret;*/ - return 0; -} static int get_qsv_format(AVCodecContext *avctx, const enum AVPixelFormat *pix_fmts) { - while (*pix_fmts != AV_PIX_FMT_NONE) { + /* while (*pix_fmts != AV_PIX_FMT_NONE) { if (*pix_fmts == AV_PIX_FMT_QSV) { DecodeContext *decode = avctx->opaque; AVHWFramesContext *frames_ctx; AVQSVFramesContext *frames_hwctx; int ret; - /* create a pool of surfaces to be used by the decoder */ + // create a pool of surfaces to be used by the decoder avctx->hw_frames_ctx = av_hwframe_ctx_alloc(decode->hw_device_ref); if (!avctx->hw_frames_ctx) return AV_PIX_FMT_NONE; @@ -239,10 +189,40 @@ static int get_qsv_format(AVCodecContext *avctx, const enum AVPixelFormat *pix_f } fprintf(stderr, "The QSV pixel format not offered in get_format()\n"); + */ + const enum AVPixelFormat *p; + for (p = pix_fmts; *p != AV_PIX_FMT_NONE; p++) { + if (*p == AV_PIX_FMT_QSV) { + hw_de_av_pix_fmt = AV_PIX_FMT_QSV; + hw_de_av_device_type = AV_HWDEVICE_TYPE_QSV; + return *p; + } + } + ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::ReadStream (Unable to decode this file using QSV.)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1); + hw_de_supported = 0; + hw_de_av_pix_fmt = AV_PIX_FMT_NONE; return AV_PIX_FMT_NONE; } -#endif + +int is_hardware_decode_supported(int codecid) +{ + int ret; + switch (codecid) { + case AV_CODEC_ID_H264: + case AV_CODEC_ID_MPEG2VIDEO: + case AV_CODEC_ID_VC1: + case AV_CODEC_ID_WMV1: + case AV_CODEC_ID_WMV2: + case AV_CODEC_ID_WMV3: + ret = 1; + break; + default : + ret = 0; + break; + } + return ret; +} #endif @@ -303,9 +283,7 @@ void FFmpegReader::Open() AVCodec *pCodec = avcodec_find_decoder(codecId); pCodecCtx = AV_GET_CODEC_CONTEXT(pStream, pCodec); #if IS_FFMPEG_3_2 -// #if defined(__linux__) - hw_de_supported = is_hardware_decode_supported(pCodecCtx->codec_id); -// #endif + hw_de_supported = is_hardware_decode_supported(pCodecCtx->codec_id); #endif // Set number of threads equal to number of processors (not to exceed 16) pCodecCtx->thread_count = min(FF_NUM_PROCESSORS, 16); @@ -319,42 +297,36 @@ void FFmpegReader::Open() av_dict_set(&opts, "strict", "experimental", 0); #if IS_FFMPEG_3_2 -// #if defined(__linux__) - if (hw_de_on && hw_de_supported) { - // Open Hardware Acceleration - // Use the hw device given in the environment variable HW_DE_DEVICE_SET or the default if not set - char *dev_hw = getenv( "HW_DE_DEVICE_SET" ); - // Check if it is there and writable - if( dev_hw != NULL && access( dev_hw, W_OK ) == -1 ) { - dev_hw = NULL; // use default - } - hw_device_ctx = NULL; -// FIXME get_XXX_format -// FIXME AV_HWDEVICE_TYPE_.... -// IMPORTANT: The get_format has different names because even for one plattform -// like Linux there are different modes of access like vaapi and vdpau and these -// should be chosen by the user in the future - #if defined(__linux__) - pCodecCtx->get_format = get_vaapi_format; - if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, dev_hw, NULL, 0) >= 0) { - #endif - #if defined(_WIN32) - pCodecCtx->get_format = get_dxva2_format; - if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_DXVA2, dev_hw, NULL, 0) >= 0) { - #endif - #if defined(__APPLE__) - pCodecCtx->get_format = get_qsv_format; - if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_QSV, dev_hw, NULL, 0) >= 0) { - #endif - if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) { - throw InvalidCodec("Hardware device reference create failed.", path); - } - } - else { - throw InvalidCodec("Hardware device create failed.", path); + if (hw_de_on && hw_de_supported) { + // Open Hardware Acceleration + // Use the hw device given in the environment variable HW_DE_DEVICE_SET or the default if not set + char *dev_hw = getenv( "HW_DE_DEVICE_SET" ); + // Check if it is there and writable + if( dev_hw != NULL && access( dev_hw, W_OK ) == -1 ) { + dev_hw = NULL; // use default + } + hw_device_ctx = NULL; + #if defined(__linux__) + pCodecCtx->get_format = get_vaapi_format; + //if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, dev_hw, NULL, 0) >= 0) { + #endif + #if defined(_WIN32) + pCodecCtx->get_format = get_dx_format; + //if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_DXVA2, dev_hw, NULL, 0) >= 0) { + #endif + #if defined(__APPLE__) + pCodecCtx->get_format = get_qsv_format; + //if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_QSV, dev_hw, NULL, 0) >= 0) { + #endif + if (av_hwdevice_ctx_create(&hw_device_ctx, hw_de_av_device_type, dev_hw, NULL, 0) >= 0) { + if (!(pCodecCtx->hw_device_ctx = av_buffer_ref(hw_device_ctx))) { + throw InvalidCodec("Hardware device reference create failed.", path); } } -// #endif + else { + throw InvalidCodec("Hardware device create failed.", path); + } + } #endif // Open video codec if (avcodec_open2(pCodecCtx, pCodec, &opts) < 0) @@ -446,14 +418,12 @@ void FFmpegReader::Close() avcodec_flush_buffers(pCodecCtx); AV_FREE_CONTEXT(pCodecCtx); #if IS_FFMPEG_3_2 -// #if defined(__linux__) - if (hw_de_on) { - if (hw_device_ctx) { - av_buffer_unref(&hw_device_ctx); - hw_device_ctx = NULL; - } + if (hw_de_on) { + if (hw_device_ctx) { + av_buffer_unref(&hw_device_ctx); + hw_device_ctx = NULL; } -// #endif + } #endif } if (info.has_audio) @@ -949,37 +919,25 @@ bool FFmpegReader::GetAVFrame() } else { AVFrame *next_frame2; -// #if defined(__linux__) if (hw_de_on && hw_de_supported) { next_frame2 = AV_ALLOCATE_FRAME(); } else -// #endif { next_frame2 = next_frame; } pFrame = new AVFrame(); while (ret >= 0) { ret = avcodec_receive_frame(pCodecCtx, next_frame2); - if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { - break; - } + if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { + break; + } if (ret != 0) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (invalid return frame received)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1); } -// #if defined(__linux__) if (hw_de_on && hw_de_supported) { int err; -// FIXME AV_PIX_FMT_VAAPI - #if defined(__linux__) - if (next_frame2->format == AV_PIX_FMT_VAAPI) { - #endif - #if defined(__WIN32__) - if (next_frame2->format == AV_PIX_FMT_DXVA2_VLD) { - #endif - #if defined(__APPLE__) - if (next_frame2->format == AV_PIX_FMT_QSV) { - #endif + if (next_frame2->format == hw_de_av_pix_fmt) { next_frame->format = AV_PIX_FMT_YUV420P; if ((err = av_hwframe_transfer_data(next_frame,next_frame2,0)) < 0) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Failed to transfer data to output frame)", "", -1, "", -1, "", -1, "", -1, "", -1, "", -1); @@ -990,11 +948,9 @@ bool FFmpegReader::GetAVFrame() } } else -// #endif { // No hardware acceleration used -> no copy from GPU memory needed next_frame = next_frame2; } - //} // TODO also handle possible further frames // Use only the first frame like avcodec_decode_video2 if (frameFinished == 0 ) { @@ -1009,11 +965,9 @@ bool FFmpegReader::GetAVFrame() } } } -// #if defined(__linux__) - if (hw_de_on && hw_de_supported) { - AV_FREE_FRAME(&next_frame2); - } -// #endif + if (hw_de_on && hw_de_supported) { + AV_FREE_FRAME(&next_frame2); + } } #else avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet);