From: Ukn Unknown <4031821+uknunknown@users.noreply.github.com> Date: Mon, 19 May 2025 13:21:52 +0000 (-0700) Subject: Update VAAPI transcoding as recommended by ffmpeg 6.1.1/doc/examples/… (#1792) X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=75119b6e9640992cefe67cc3b45025367df7071e;p=thirdparty%2Ftvheadend.git Update VAAPI transcoding as recommended by ffmpeg 6.1.1/doc/examples/… (#1792) Update VAAPI transcoding as recommended by ffmpeg 6.1.1/doc/examples/vaapi_*.c --- diff --git a/src/libav.h b/src/libav.h index c73b61089..902b305d8 100644 --- a/src/libav.h +++ b/src/libav.h @@ -54,6 +54,13 @@ This list must be updated every time we use a new AV_CODEC_ID #define AV_CODEC_ID_DVB_TELETEXT CODEC_ID_DVB_TELETEXT #endif +// Enable new transcoding starting ffmpeg 4 +#if LIBAVCODEC_VERSION_MAJOR < 58 +#define ENABLE_FFMPEG4_TRANSCODING 0 +#else +#define ENABLE_FFMPEG4_TRANSCODING 1 +#endif + enum AVCodecID streaming_component_type2codec_id(streaming_component_type_t type); streaming_component_type_t codec_id2streaming_component_type(enum AVCodecID id); void libav_set_loglevel(void); diff --git a/src/transcoding/transcode/hwaccels/hwaccels.c b/src/transcoding/transcode/hwaccels/hwaccels.c index 892bbbc2f..57524c44e 100644 --- a/src/transcoding/transcode/hwaccels/hwaccels.c +++ b/src/transcoding/transcode/hwaccels/hwaccels.c @@ -213,13 +213,43 @@ hwaccels_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, si /* encoding ================================================================= */ +#if ENABLE_FFMPEG4_TRANSCODING int +hwaccels_initialize_encoder_from_decoder(const AVCodecContext *iavctx, AVCodecContext *oavctx) +{ + switch (iavctx->pix_fmt) { + case AV_PIX_FMT_VAAPI: + /* we need to ref hw_frames_ctx of decoder to initialize encoder's codec. + Only after we get a decoded frame, can we obtain its hw_frames_ctx */ + oavctx->hw_frames_ctx = av_buffer_ref(iavctx->hw_frames_ctx); + if (!oavctx->hw_frames_ctx) { + return AVERROR(ENOMEM); + } + return 0; + case AV_PIX_FMT_YUV420P: + break; + default: + break; + } + return 0; +} +#endif + +int +#if ENABLE_FFMPEG4_TRANSCODING +hwaccels_encode_setup_context(AVCodecContext *avctx) +#else hwaccels_encode_setup_context(AVCodecContext *avctx, int low_power) +#endif { switch (avctx->pix_fmt) { #if ENABLE_VAAPI case AV_PIX_FMT_VAAPI: +#if ENABLE_FFMPEG4_TRANSCODING + return vaapi_encode_setup_context(avctx); +#else return vaapi_encode_setup_context(avctx, low_power); +#endif #endif default: break; diff --git a/src/transcoding/transcode/hwaccels/hwaccels.h b/src/transcoding/transcode/hwaccels/hwaccels.h index ca129ef88..ed04f6748 100644 --- a/src/transcoding/transcode/hwaccels/hwaccels.h +++ b/src/transcoding/transcode/hwaccels/hwaccels.h @@ -23,7 +23,7 @@ #include "tvheadend.h" - +#include "../internals.h" #include @@ -51,9 +51,17 @@ hwaccels_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, si /* encoding ================================================================= */ +#if ENABLE_FFMPEG4_TRANSCODING +int +hwaccels_initialize_encoder_from_decoder(const AVCodecContext *iavctx, AVCodecContext *oavctx); +#endif int +#if ENABLE_FFMPEG4_TRANSCODING +hwaccels_encode_setup_context(AVCodecContext *avctx); +#else hwaccels_encode_setup_context(AVCodecContext *avctx, int low_power); +#endif void hwaccels_encode_close_context(AVCodecContext *avctx); diff --git a/src/transcoding/transcode/hwaccels/vaapi.c b/src/transcoding/transcode/hwaccels/vaapi.c index 3e8dab19a..711f11560 100644 --- a/src/transcoding/transcode/hwaccels/vaapi.c +++ b/src/transcoding/transcode/hwaccels/vaapi.c @@ -27,7 +27,41 @@ #include +#if ENABLE_FFMPEG4_TRANSCODING +typedef struct tvh_vaapi_context_t { + int width; + int height; + AVBufferRef *hw_device_ref; + AVBufferRef *hw_frame_ref; +} TVHVAContext; + + +static void +tvhva_done() +{ + /* nothing to do */ +} + +/* TVHVAContext ============================================================= */ + +static void +tvhva_context_destroy(TVHVAContext *self) +{ + if (self) { + if (self->hw_device_ref) { + av_buffer_unref(&self->hw_device_ref); + self->hw_device_ref = NULL; + } + if (self->hw_frame_ref) { + av_buffer_unref(&self->hw_frame_ref); + self->hw_frame_ref = NULL; + } + free(self); + self = NULL; + } +} +#else typedef struct tvh_vaapi_device { char *hw_device_name; AVBufferRef *hw_device_ref; @@ -569,6 +603,7 @@ tvhva_context_create(const char *logpref, } return self; } +#endif /* decoding ================================================================= */ @@ -588,6 +623,34 @@ vaapi_decode_setup_context(AVCodecContext *avctx) { TVHContext *ctx = avctx->opaque; +#if ENABLE_FFMPEG4_TRANSCODING + TVHVAContext *self = NULL; + int ret = -1; + + if (!(self = calloc(1, sizeof(TVHVAContext)))) { + tvherror(LS_VAAPI, "Decode: Failed to allocate VAAPI context (TVHVAContext)"); + return AVERROR(ENOMEM); + } + + /* Open VAAPI device and create an AVHWDeviceContext for it*/ + if ((ret = av_hwdevice_ctx_create(&self->hw_device_ref, AV_HWDEVICE_TYPE_VAAPI, ctx->hw_accel_device, NULL, 0)) < 0) { + tvherror(LS_VAAPI, "Decode: Failed to Open VAAPI device and create an AVHWDeviceContext for device: " + "%s with error code: %s", + ctx->hw_accel_device, av_err2str(ret)); + return ret; + } + + /* set hw_frames_ctx for decoder's AVCodecContext */ + avctx->hw_device_ctx = av_buffer_ref(self->hw_device_ref); + if (!avctx->hw_device_ctx) { + tvherror(LS_VAAPI, "Decode: Failed to create a hardware device reference for device: %s.", + ctx->hw_accel_device); + return AVERROR(ENOMEM); + } + ctx->hw_accel_ictx = self; + avctx->get_buffer2 = vaapi_get_buffer2; + avctx->pix_fmt = AV_PIX_FMT_VAAPI; +#else if (!(ctx->hw_accel_ictx = tvhva_context_create("decode", avctx, VAEntrypointVLD))) { return -1; @@ -597,6 +660,7 @@ vaapi_decode_setup_context(AVCodecContext *avctx) #if LIBAVCODEC_VERSION_MAJOR < 60 avctx->thread_safe_callbacks = 0; #endif +#endif return 0; } @@ -607,6 +671,12 @@ vaapi_decode_close_context(AVCodecContext *avctx) { TVHContext *ctx = avctx->opaque; +#if ENABLE_FFMPEG4_TRANSCODING + if (avctx->hw_device_ctx) { + av_buffer_unref(&avctx->hw_device_ctx); + avctx->hw_device_ctx = NULL; + } +#endif tvhva_context_destroy(ctx->hw_accel_ictx); } @@ -645,7 +715,69 @@ vaapi_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, size_ /* encoding ================================================================= */ +#if ENABLE_FFMPEG4_TRANSCODING +// lifted from ffmpeg-6.1.1/doc/examples/vaapi_encode.c line 41 +static int set_hwframe_ctx(AVCodecContext *ctx, AVBufferRef *hw_device_ctx) +{ + AVBufferRef *hw_frames_ref; + AVHWFramesContext *frames_ctx = NULL; + int err = 0; + + if (!(hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx))) { + tvherror(LS_VAAPI, "Encode: Failed to create VAAPI frame context."); + return AVERROR(ENOMEM); + } + frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data); + frames_ctx->format = AV_PIX_FMT_VAAPI; + frames_ctx->sw_format = AV_PIX_FMT_NV12; + frames_ctx->width = ctx->width; + frames_ctx->height = ctx->height; + frames_ctx->initial_pool_size = 20; + if ((err = av_hwframe_ctx_init(hw_frames_ref)) < 0) { + tvherror(LS_VAAPI, "Encode: Failed to initialize VAAPI frame context." + "Error code: %s",av_err2str(err)); + av_buffer_unref(&hw_frames_ref); + return err; + } + ctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref); + if (!ctx->hw_frames_ctx) { + err = AVERROR(ENOMEM); + tvherror(LS_VAAPI, "Encode: Failed to create a hardware device reference." + "Error code: %s",av_err2str(err)); + } + av_buffer_unref(&hw_frames_ref); + return err; +} +#endif + int +#if ENABLE_FFMPEG4_TRANSCODING +vaapi_encode_setup_context(AVCodecContext *avctx) +{ + TVHContext *ctx = avctx->opaque; + TVHVAContext *self = NULL; + int ret = 0; + + if (!(self = calloc(1, sizeof(TVHVAContext)))) { + tvherror(LS_VAAPI, "Encode: Failed to allocate VAAPI context (TVHVAContext)"); + return AVERROR(ENOMEM); + } + + /* Open VAAPI device and create an AVHWDeviceContext for it*/ + if ((ret = av_hwdevice_ctx_create(&self->hw_frame_ref, AV_HWDEVICE_TYPE_VAAPI, NULL, NULL, 0)) < 0) { + tvherror(LS_VAAPI, "Encode: Failed to open VAAPI device and create an AVHWDeviceContext for it." + "Error code: %s",av_err2str(ret)); + return ret; + } + + /* set hw_frames_ctx for encoder's AVCodecContext */ + if ((ret = set_hwframe_ctx(avctx, self->hw_frame_ref)) < 0) { + tvherror(LS_VAAPI, "Encode: Failed to set hwframe context." + "Error code: %s",av_err2str(ret)); + return ret; + } + ctx->hw_device_octx = av_buffer_ref(self->hw_frame_ref); +#else vaapi_encode_setup_context(AVCodecContext *avctx, int low_power) { TVHContext *ctx = avctx->opaque; @@ -668,6 +800,7 @@ vaapi_encode_setup_context(AVCodecContext *avctx, int low_power) return AVERROR(ENOMEM); } tvhva_context_destroy(hwaccel_context); +#endif return 0; } @@ -678,6 +811,16 @@ vaapi_encode_close_context(AVCodecContext *avctx) TVHContext *ctx = avctx->opaque; av_buffer_unref(&ctx->hw_device_octx); ctx->hw_device_octx = NULL; +#if ENABLE_FFMPEG4_TRANSCODING + if (avctx->hw_device_ctx) { + av_buffer_unref(&avctx->hw_device_ctx); + avctx->hw_device_ctx = NULL; + } + if (avctx->hw_frames_ctx) { + av_buffer_unref(&avctx->hw_frames_ctx); + avctx->hw_frames_ctx = NULL; + } +#endif } diff --git a/src/transcoding/transcode/hwaccels/vaapi.h b/src/transcoding/transcode/hwaccels/vaapi.h index 72a2c4bab..57417b052 100644 --- a/src/transcoding/transcode/hwaccels/vaapi.h +++ b/src/transcoding/transcode/hwaccels/vaapi.h @@ -51,7 +51,11 @@ vaapi_get_sharpness_filter(AVCodecContext *avctx, int value, char *filter, size_ /* encoding ================================================================= */ int +#if ENABLE_FFMPEG4_TRANSCODING +vaapi_encode_setup_context(AVCodecContext *avctx); +#else vaapi_encode_setup_context(AVCodecContext *avctx, int low_power); +#endif void vaapi_encode_close_context(AVCodecContext *avctx); diff --git a/src/transcoding/transcode/video.c b/src/transcoding/transcode/video.c index c838fd734..a4b6090c4 100644 --- a/src/transcoding/transcode/video.c +++ b/src/transcoding/transcode/video.c @@ -226,10 +226,37 @@ tvh_video_context_open_encoder(TVHContext *self, AVDictionary **opts) #if ENABLE_HWACCELS self->oavctx->coded_width = self->oavctx->width; self->oavctx->coded_height = self->oavctx->height; +#if ENABLE_FFMPEG4_TRANSCODING + // hwaccel is the user input for Hardware acceleration from Codec parameteres + int hwaccel = -1; + if ((hwaccel = tvh_codec_profile_video_get_hwaccel(self->profile)) < 0) { + return -1; + } + if (_video_filters_hw_pix_fmt(self->oavctx->pix_fmt)){ + // encoder is hw accelerated + if (hwaccel) { + // decoder is hw accelerated + // --> we initialize encoder from decoder as recomanded in: + // ffmpeg-6.1.1/doc/examples/vaapi_transcode.c line 169 + if (hwaccels_initialize_encoder_from_decoder(self->iavctx, self->oavctx)) { + return -1; + } + } + else { + // decoder is sw + // --> we initialize as recommended in: + // ffmpeg-6.1.1/doc/examples/vaapi_encode.c line 145 + if (hwaccels_encode_setup_context(self->oavctx)) { + return -1; + } + } + } +#else if (hwaccels_encode_setup_context(self->oavctx, self->profile->low_power)) { return -1; } -#endif +#endif // from ENABLE_FFMPEG4_TRANSCODING +#endif // from ENABLE_HWACCELS // XXX: is this a safe assumption? if (!self->iavctx->framerate.num) {