From e74d71f5451c6f2be9c546d61fac412f2d55f851 Mon Sep 17 00:00:00 2001 From: "FeRD (Frank Dana)" Date: Mon, 10 Feb 2020 01:50:31 -0500 Subject: [PATCH] FFmpegReader/Writer: limit hwaccel to FFmpeg 3.4+ Add a new #define HAVE_HW_ACCEL, which is only set on FFmpeg 3.4+, and use that to restrict the use of hw-accel features, leaving IS_FFMPEG_3_2 to determine only whether code is compatible with FFmpeg 3.2+. --- include/FFmpegReader.h | 7 ++-- include/FFmpegUtilities.h | 4 +++ src/FFmpegReader.cpp | 41 +++++++++++++---------- src/FFmpegWriter.cpp | 70 +++++++++++++++++++++------------------ 4 files changed, 68 insertions(+), 54 deletions(-) diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index 9faa86a3..cc782cd5 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -98,7 +98,7 @@ namespace openshot { AVFormatContext *pFormatCtx; int i, videoStream, audioStream; AVCodecContext *pCodecCtx, *aCodecCtx; -#if (LIBAVFORMAT_VERSION_MAJOR >= 57) +#if HAVE_HW_ACCEL AVBufferRef *hw_device_ctx = NULL; //PM #endif AVStream *pStream, *aStream; @@ -147,12 +147,11 @@ namespace openshot { int64_t current_video_frame; // can't reliably use PTS of video to determine this int hw_de_supported = 0; // Is set by FFmpegReader -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL AVPixelFormat hw_de_av_pix_fmt = AV_PIX_FMT_NONE; AVHWDeviceType hw_de_av_device_type = AV_HWDEVICE_TYPE_NONE; -#endif - int IsHardwareDecodeSupported(int codecid); +#endif /// Check for the correct frames per second value by scanning the 1st few seconds of video packets. void CheckFPS(); diff --git a/include/FFmpegUtilities.h b/include/FFmpegUtilities.h index c673305e..62d64df1 100644 --- a/include/FFmpegUtilities.h +++ b/include/FFmpegUtilities.h @@ -40,6 +40,10 @@ #ifndef IS_FFMPEG_3_2 #define IS_FFMPEG_3_2 (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 64, 101)) #endif + + #ifndef HAVE_HW_ACCEL + #define HAVE_HW_ACCEL (LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 107, 100)) + #endif // Include the FFmpeg headers extern "C" { diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 9234ecfe..b548fa8f 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -35,13 +35,13 @@ #define ENABLE_VAAPI 0 -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #pragma message "You are compiling with experimental hardware decode" #else #pragma message "You are compiling only with software decode" #endif -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #define MAX_SUPPORTED_WIDTH 1950 #define MAX_SUPPORTED_HEIGHT 1100 @@ -71,14 +71,14 @@ typedef struct VAAPIDecodeContext { enum AVPixelFormat surface_format; int surface_count; } VAAPIDecodeContext; -#endif -#endif +#endif // ENABLE_VAAPI +#endif // HAVE_HW_ACCEL using namespace openshot; int hw_de_on = 0; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL AVPixelFormat hw_de_av_pix_fmt_global = AV_PIX_FMT_NONE; AVHWDeviceType hw_de_av_device_type_global = AV_HWDEVICE_TYPE_NONE; #endif @@ -153,7 +153,7 @@ bool AudioLocation::is_near(AudioLocation location, int samples_per_frame, int64 return false; } -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL // Get hardware pix format static enum AVPixelFormat get_hw_dec_format(AVCodecContext *ctx, const enum AVPixelFormat *pix_fmts) @@ -234,7 +234,7 @@ int FFmpegReader::IsHardwareDecodeSupported(int codecid) } return ret; } -#endif +#endif // HAVE_HW_ACCEL void FFmpegReader::Open() { // Open reader if not already open @@ -287,7 +287,7 @@ void FFmpegReader::Open() { // If hw accel is selected but hardware cannot handle repeat with software decoding do { pCodecCtx = AV_GET_CODEC_CONTEXT(pStream, pCodec); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on && (retry_decode_open==2)) { // Up to here no decision is made if hardware or software decode hw_de_supported = IsHardwareDecodeSupported(pCodecCtx->codec_id); @@ -304,7 +304,7 @@ void FFmpegReader::Open() { // Init options av_dict_set(&opts, "strict", "experimental", 0); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { // Open Hardware Acceleration int i_decoder_hw = 0; @@ -433,13 +433,13 @@ void FFmpegReader::Open() { throw InvalidCodec("Hardware device create failed.", path); } } -#endif +#endif // HAVE_HW_ACCEL // Open video codec if (avcodec_open2(pCodecCtx, pCodec, &opts) < 0) throw InvalidCodec("A video codec was found, but could not be opened.", path); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { AVHWFramesConstraints *constraints = NULL; void *hwconfig = NULL; @@ -449,7 +449,7 @@ void FFmpegReader::Open() { #if ENABLE_VAAPI ((AVVAAPIHWConfig *)hwconfig)->config_id = ((VAAPIDecodeContext *)(pCodecCtx->priv_data))->va_config; constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx,hwconfig); -#endif +#endif // ENABLE_VAAPI if (constraints) { if (pCodecCtx->coded_width < constraints->min_width || pCodecCtx->coded_height < constraints->min_height || @@ -506,7 +506,7 @@ void FFmpegReader::Open() { } #else retry_decode_open = 0; -#endif +#endif // HAVE_HW_ACCEL } while (retry_decode_open); // retry_decode_open // Free options av_dict_free(&opts); @@ -592,14 +592,14 @@ void FFmpegReader::Close() { if (info.has_video) { avcodec_flush_buffers(pCodecCtx); AV_FREE_CONTEXT(pCodecCtx); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_de_on) { if (hw_device_ctx) { av_buffer_unref(&hw_device_ctx); hw_device_ctx = NULL; } } -#endif +#endif // HAVE_HW_ACCEL } if (info.has_audio) { avcodec_flush_buffers(aCodecCtx); @@ -1100,19 +1100,22 @@ bool FFmpegReader::GetAVFrame() { ret = avcodec_send_packet(pCodecCtx, packet); + #if HAVE_HW_ACCEL // Get the format from the variables set in get_hw_dec_format hw_de_av_pix_fmt = hw_de_av_pix_fmt_global; hw_de_av_device_type = hw_de_av_device_type_global; - + #endif // HAVE_HW_ACCEL if (ret < 0 || ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (Packet not sent)"); } else { AVFrame *next_frame2; + #if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { next_frame2 = AV_ALLOCATE_FRAME(); } else + #endif // HAVE_HW_ACCEL { next_frame2 = next_frame; } @@ -1125,6 +1128,7 @@ bool FFmpegReader::GetAVFrame() { if (ret != 0) { ZmqLogger::Instance()->AppendDebugMethod("FFmpegReader::GetAVFrame (invalid return frame received)"); } + #if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { int err; if (next_frame2->format == hw_de_av_pix_fmt) { @@ -1138,6 +1142,7 @@ bool FFmpegReader::GetAVFrame() { } } else + #endif // HAVE_HW_ACCEL { // No hardware acceleration used -> no copy from GPU memory needed next_frame = next_frame2; } @@ -1151,9 +1156,11 @@ bool FFmpegReader::GetAVFrame() { (AVPixelFormat)(pStream->codecpar->format), info.width, info.height); } } + #if HAVE_HW_ACCEL if (hw_de_on && hw_de_supported) { AV_FREE_FRAME(&next_frame2); } + #endif // HAVE_HW_ACCEL } #else avcodec_decode_video2(pCodecCtx, next_frame, &frameFinished, packet); @@ -1169,7 +1176,7 @@ bool FFmpegReader::GetAVFrame() { av_picture_copy((AVPicture *) pFrame, (AVPicture *) next_frame, pCodecCtx->pix_fmt, info.width, info.height); } -#endif +#endif // IS_FFMPEG_3_2 } // deallocate the frame diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 245bd9bd..8d060d77 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -35,7 +35,7 @@ using namespace openshot; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #pragma message "You are compiling with experimental hardware encode" #else #pragma message "You are compiling only with software encode" @@ -44,7 +44,7 @@ using namespace openshot; // Multiplexer parameters temporary storage AVDictionary *mux_dict = NULL; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL int hw_en_on = 1; // Is set in UI int hw_en_supported = 0; // Is set by FFmpegWriter AVPixelFormat hw_en_av_pix_fmt = AV_PIX_FMT_NONE; @@ -81,7 +81,7 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 av_buffer_unref(&hw_frames_ref); return err; } -#endif +#endif // HAVE_HW_ACCEL FFmpegWriter::FFmpegWriter(std::string path) : path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), @@ -171,7 +171,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f if (codec.length() > 0) { AVCodec *new_codec; // Check if the codec selected is a hardware accelerated codec -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL #if defined(__linux__) if (strstr(codec.c_str(), "_vaapi") != NULL) { new_codec = avcodec_find_encoder_by_name(codec.c_str()); @@ -225,7 +225,7 @@ void FFmpegWriter::SetVideoOptions(bool has_video, std::string codec, Fraction f #endif //__linux__ #else // not ffmpeg 3 new_codec = avcodec_find_encoder_by_name(codec.c_str()); -#endif //IS_FFMPEG_3_2 +#endif // HAVE_HW_ACCEL if (new_codec == NULL) throw InvalidCodec("A valid video codec could not be found for this file.", path); else { @@ -392,11 +392,11 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va // This might be better in an extra methods as more options // and way to set quality are possible #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) - #if IS_FFMPEG_3_2 + #if HAVE_HW_ACCEL if (hw_en_on) { av_opt_set_int(c->priv_data, "qp", std::min(std::stoi(value),63), 0); // 0-63 } else - #endif + #endif // HAVE_HW_ACCEL { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) @@ -442,7 +442,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va // This might be better in an extra methods as more options // and way to set quality are possible #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 39, 101) -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on) { double mbs = 15000000.0; if (info.video_bit_rate > 0) { @@ -455,7 +455,7 @@ void FFmpegWriter::SetOption(StreamType stream, std::string name, std::string va } c->bit_rate = (int)(mbs); } else -#endif +#endif // HAVE_HW_ACCEL { switch (c->codec_id) { #if (LIBAVCODEC_VERSION_MAJOR >= 58) @@ -955,16 +955,14 @@ void FFmpegWriter::flush_encoders() { // Close the video codec void FFmpegWriter::close_video(AVFormatContext *oc, AVStream *st) { -#if IS_FFMPEG_3_2 - // #if defined(__linux__) - if (hw_en_on && hw_en_supported) { - if (hw_device_ctx) { - av_buffer_unref(&hw_device_ctx); - hw_device_ctx = NULL; - } +#if HAVE_HW_ACCEL + if (hw_en_on && hw_en_supported) { + if (hw_device_ctx) { + av_buffer_unref(&hw_device_ctx); + hw_device_ctx = NULL; } - // #endif -#endif + } +#endif // HAVE_HW_ACCEL } // Close the audio codec @@ -1342,7 +1340,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { // Set number of threads equal to number of processors (not to exceed 16) video_codec->thread_count = std::min(FF_NUM_PROCESSORS, 16); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { //char *dev_hw = NULL; char adapter[256]; @@ -1385,7 +1383,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { throw InvalidCodec("Could not create hwdevice", path); } } -#endif +#endif // HAVE_HW_ACCEL /* find the video encoder */ codec = avcodec_find_encoder_by_name(info.vcodec.c_str()); @@ -1402,7 +1400,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { AVDictionary *opts = NULL; av_dict_set(&opts, "strict", "experimental", 0); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { video_codec->pix_fmt = hw_en_av_pix_fmt; @@ -1451,7 +1449,7 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) { "width", info.width, "height", info.height, av_err2str(err), -1); } } -#endif +#endif // HAVE_HW_ACCEL /* open the codec */ if (avcodec_open2(video_codec, codec, &opts) < 0) @@ -1900,14 +1898,17 @@ void FFmpegWriter::process_video_packet(std::shared_ptr frame) { frame_source = allocate_avframe(PIX_FMT_RGBA, source_image_width, source_image_height, &bytes_source, (uint8_t *) pixels); #if IS_FFMPEG_3_2 AVFrame *frame_final; + #if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { frame_final = allocate_avframe(AV_PIX_FMT_NV12, info.width, info.height, &bytes_final, NULL); - } else { + } else + #endif // HAVE_HW_ACCEL + { frame_final = allocate_avframe((AVPixelFormat)(video_st->codecpar->format), info.width, info.height, &bytes_final, NULL); } #else AVFrame *frame_final = allocate_avframe(video_codec->pix_fmt, info.width, info.height, &bytes_final, NULL); -#endif +#endif // IS_FFMPEG_3_2 // Fill with data AV_COPY_PICTURE_DATA(frame_source, (uint8_t *) pixels, PIX_FMT_RGBA, source_image_width, source_image_height); @@ -1977,7 +1978,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // Assign the initial AVFrame PTS from the frame counter frame_final->pts = write_video_count; -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { if (!(hw_frame = av_frame_alloc())) { fprintf(stderr, "Error code: av_hwframe_alloc\n"); @@ -1994,7 +1995,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra } av_frame_copy_props(hw_frame, frame_final); } -#endif +#endif // HAVE_HW_ACCEL /* encode the image */ int got_packet_ptr = 0; int error_code = 0; @@ -2003,9 +2004,12 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra int frameFinished = 0; int ret; + #if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { ret = avcodec_send_frame(video_codec, hw_frame); //hw_frame!!! - } else { + } else + #endif // HAVE_HW_ACCEL + { ret = avcodec_send_frame(video_codec, frame_final); } error_code = ret; @@ -2062,8 +2066,8 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // got data back (so encode this frame) got_packet_ptr = 1; } -#endif -#endif +#endif // LIBAVFORMAT_VERSION_MAJOR >= 54 +#endif // IS_FFMPEG_3_2 /* if zero size, it means the image was buffered */ if (error_code == 0 && got_packet_ptr) { @@ -2095,14 +2099,14 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame *fra // Deallocate packet AV_FREE_PACKET(&pkt); -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { if (hw_frame) { av_frame_free(&hw_frame); hw_frame = NULL; } } -#endif +#endif // HAVE_HW_ACCEL } // Success @@ -2125,11 +2129,11 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) { // Init software rescalers vector (many of them, one for each thread) for (int x = 0; x < num_of_rescalers; x++) { // Init the software scaler from FFMpeg -#if IS_FFMPEG_3_2 +#if HAVE_HW_ACCEL if (hw_en_on && hw_en_supported) { img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_PIX_FMT_NV12, scale_mode, NULL, NULL, NULL); } else -#endif +#endif // HAVE_HW_ACCEL { img_convert_ctx = sws_getContext(source_width, source_height, PIX_FMT_RGBA, info.width, info.height, AV_GET_CODEC_PIXEL_FORMAT(video_st, video_st->codec), scale_mode, NULL, NULL, NULL);