From be979cd78c7562d3a3ec2208b7d6f7bf50dd3f80 Mon Sep 17 00:00:00 2001 From: eisneinechse <42617957+eisneinechse@users.noreply.github.com> Date: Thu, 6 Sep 2018 08:28:50 -0700 Subject: [PATCH] Accelerated encode now supported by Windows and Mac. Only tested on Linux though due to absense of hardware/software. Tested to compile on Ubuntu 14.04, 16.04, 18.04, and 18.10 Acceleration only available on systems with ffmpeg 3.2 and up Very early code, work in progress. Issues to be fixed soon: if hardware cannot decode because the size is too big it keeps trying. more interfaces supported like vdpau in Linux error handling user interface Many commented lines of code are still in the source to help people start who may want to help. --- src/FFmpegWriter.cpp | 80 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 18 deletions(-) diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index 24ccec0a..d0a542ae 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -49,7 +49,13 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 return -1; } frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data); + #if defined(__linux__) frames_ctx->format = AV_PIX_FMT_VAAPI; + #elif defined(_WIN32) + frames_ctx->format = AV_PIX_FMT_DXVA2_VLD; + #elif defined(__APPLE__) + frames_ctx->format = AV_PIX_FMT_QSV; + #endif frames_ctx->sw_format = AV_PIX_FMT_NV12; frames_ctx->width = width; frames_ctx->height = height; @@ -70,9 +76,9 @@ static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx, int6 #endif #if IS_FFMPEG_3_2 -#if defined(__linux__) +//#if defined(__linux__) #pragma message "You are compiling with experimental hardware encode" -#endif +//#endif #endif FFmpegWriter::FFmpegWriter(string path) : @@ -171,6 +177,28 @@ void FFmpegWriter::SetVideoOptions(bool has_video, string codec, Fraction fps, i else { new_codec = avcodec_find_encoder_by_name(codec.c_str()); hw_en_on = 0; + hw_en_supported = 0; + } + #elif defined(_WIN32) + if ( (strcmp(codec.c_str(),"h264_dxva2") == 0)) { + new_codec = avcodec_find_encoder_by_name(codec.c_str()); + hw_en_on = 1; + hw_en_supported = 1; + } + else { + new_codec = avcodec_find_encoder_by_name(codec.c_str()); + hw_en_on = 0; + hw_en_supported = 0; + } + #elif defined(__APPLE__) + if ( (strcmp(codec.c_str(),"h264_qsv") == 0)) { + new_codec = avcodec_find_encoder_by_name(codec.c_str()); + hw_en_on = 1; + hw_en_supported = 1; + } + else { + new_codec = avcodec_find_encoder_by_name(codec.c_str()); + hw_en_on = 0; hw_en_supported = 0; } #else // is FFmpeg 3 but not linux @@ -799,14 +827,14 @@ void FFmpegWriter::close_video(AVFormatContext *oc, AVStream *st) AV_FREE_CONTEXT(video_codec); video_codec = NULL; #if IS_FFMPEG_3_2 - #if defined(__linux__) +// #if defined(__linux__) if (hw_en_on && hw_en_supported) { if (hw_device_ctx) { av_buffer_unref(&hw_device_ctx); hw_device_ctx = NULL; } } - #endif +// #endif #endif } @@ -1176,8 +1204,8 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) video_codec->thread_count = min(FF_NUM_PROCESSORS, 16); #if IS_FFMPEG_3_2 - #if defined(__linux__) if (hw_en_on && hw_en_supported) { + #if defined(__linux__) // Use the hw device given in the environment variable HW_EN_DEVICE_SET or the default if not set char *dev_hw = getenv( "HW_EN_DEVICE_SET" ); // Check if it is there and writable @@ -1189,8 +1217,20 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) cerr << "FFmpegWriter::open_video : Codec name: " << info.vcodec.c_str() << " ERROR creating\n"; throw InvalidCodec("Could not create hwdevice", path); } + #elif defined(_WIN32) + if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_DXVA2, + NULL, NULL, 0) < 0) { + cerr << "FFmpegWriter::open_video : Codec name: " << info.vcodec.c_str() << " ERROR creating\n"; + throw InvalidCodec("Could not create hwdevice", path); + } + #elif defined(__APPLE__) + if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_QSV, + NULL, NULL, 0) < 0) { + cerr << "FFmpegWriter::open_video : Codec name: " << info.vcodec.c_str() << " ERROR creating\n"; + throw InvalidCodec("Could not create hwdevice", path); + } + #endif } - #endif #endif /* find the video encoder */ codec = avcodec_find_encoder_by_name(info.vcodec.c_str()); @@ -1208,10 +1248,15 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) av_dict_set(&opts, "strict", "experimental", 0); #if IS_FFMPEG_3_2 - #if defined(__linux__) if (hw_en_on && hw_en_supported) { video_codec->max_b_frames = 0; // At least this GPU doesn't support b-frames + #if defined(__linux__) video_codec->pix_fmt = AV_PIX_FMT_VAAPI; + #elif defined(_WIN32) + video_codec->pix_fmt = AV_PIX_FMT_DXVA2_VLD + #elif defined(__APPLE__) + video_codec->pix_fmt = AV_PIX_FMT_QSV + #endif video_codec->profile = FF_PROFILE_H264_BASELINE | FF_PROFILE_H264_CONSTRAINED; av_opt_set(video_codec->priv_data,"preset","slow",0); av_opt_set(video_codec->priv_data,"tune","zerolatency",0); @@ -1222,7 +1267,6 @@ void FFmpegWriter::open_video(AVFormatContext *oc, AVStream *st) fprintf(stderr, "Failed to set hwframe context.\n"); } } - #endif #endif /* open the codec */ @@ -1675,11 +1719,11 @@ 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 defined(__linux__) +// #if defined(__linux__) if (hw_en_on && hw_en_supported) { frame_final = allocate_avframe(AV_PIX_FMT_NV12, info.width, info.height, &bytes_final, NULL); } else - #endif +// #endif { frame_final = allocate_avframe((AVPixelFormat)(video_st->codecpar->format), info.width, info.height, &bytes_final, NULL); } @@ -1758,7 +1802,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 defined(__linux__) +// #if defined(__linux__) if (hw_en_on && hw_en_supported) { if (!(hw_frame = av_frame_alloc())) { fprintf(stderr, "Error code: av_hwframe_alloc\n"); @@ -1775,7 +1819,7 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame* fra } av_frame_copy_props(hw_frame, frame_final); } - #endif +// #endif #endif /* encode the image */ int got_packet_ptr = 0; @@ -1784,13 +1828,13 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame* fra // Write video packet (latest version of FFmpeg) int frameFinished = 0; int ret; - #if defined(__linux__) +// #if defined(__linux__) #if IS_FFMPEG_3_2 if (hw_en_on && hw_en_supported) { ret = avcodec_send_frame(video_codec, hw_frame); //hw_frame!!! } else #endif - #endif +// #endif ret = avcodec_send_frame(video_codec, frame_final); error_code = ret; if (ret < 0 ) { @@ -1877,14 +1921,14 @@ bool FFmpegWriter::write_video_packet(std::shared_ptr frame, AVFrame* fra // Deallocate packet AV_FREE_PACKET(&pkt); #if IS_FFMPEG_3_2 - #if defined(__linux__) +// #if defined(__linux__) if (hw_en_on && hw_en_supported) { if (hw_frame) { av_frame_free(&hw_frame); hw_frame = NULL; } } - #endif +// #endif #endif } @@ -1907,11 +1951,11 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) { // Init the software scaler from FFMpeg #if IS_FFMPEG_3_2 - #if defined(__linux__) +// #if defined(__linux__) 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, SWS_BILINEAR, NULL, NULL, NULL); } else - #endif +// #endif #endif { 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), SWS_BILINEAR, NULL, NULL, NULL);