/* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/hwcontext_amf.h"#include "libavutil/hwcontext_amf_internal.h"#include "amfdec.h"#include "codec_internal.h"#include "hwconfig.h"#include "libavutil/imgutils.h"#include "libavutil/mem.h"#include "libavutil/time.h"#include "decode.h"#include "libavutil/mastering_display_metadata.h" #if CONFIG_D3D11VA#include "libavutil/hwcontext_d3d11va.h"#endif#if CONFIG_DXVA2#define COBJMACROS#include "libavutil/hwcontext_dxva2.h"#endif #ifdef _WIN32#include "compat/w32dlfcn.h"#else#include <dlfcn.h>#endif//will be in public headers soon#define AMF_VIDEO_DECODER_OUTPUT_FORMAT L"OutputDecodeFormat" const enum AVPixelFormat amf_dec_pix_fmts[] = { AV_PIX_FMT_NV12, AV_PIX_FMT_P010, AV_PIX_FMT_P012, AV_PIX_FMT_AMF_SURFACE, AV_PIX_FMT_NONE}; static const AVCodecHWConfigInternal *const amf_hw_configs[] = { &(const AVCodecHWConfigInternal) { .public = { .pix_fmt = AV_PIX_FMT_AMF_SURFACE, .methods = AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX | AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, .device_type = AV_HWDEVICE_TYPE_AMF, }, .hwaccel = NULL, }, NULL}; static void amf_free_amfsurface(void *opaque, uint8_t *data){ AMFSurface *surface = (AMFSurface*)(data); surface->pVtbl->Release(surface);} static int amf_legacy_driver_no_bitness_detect(AVAMFDeviceContext *amf_device_ctx){ if( AMF_GET_MAJOR_VERSION(amf_device_ctx->version) <= 1 && AMF_GET_MINOR_VERSION(amf_device_ctx->version) <= 4 && AMF_GET_SUBMINOR_VERSION(amf_device_ctx->version) < 36) return 1; return 0;} static int amf_init_decoder(AVCodecContext *avctx){ AMFDecoderContext *ctx = avctx->priv_data; AVHWDeviceContext *hw_device_ctx = (AVHWDeviceContext*)ctx->device_ctx_ref->data; AVAMFDeviceContext *amf_device_ctx = (AVAMFDeviceContext*)hw_device_ctx->hwctx; const wchar_t *codec_id = NULL; AMF_RESULT res; AMFBuffer *buffer; amf_int64 color_profile; int pool_size = 36; ctx->drain = 0; ctx->resolution_changed = 0; switch (avctx->codec->id) { case AV_CODEC_ID_H264: codec_id = AMFVideoDecoderUVD_H264_AVC; break; case AV_CODEC_ID_HEVC: { codec_id = AMFVideoDecoderHW_H265_HEVC; // way-around for older drivers that don't support dynamic butness detection - // define HEVC 10-bit based on container info if(amf_legacy_driver_no_bitness_detect(amf_device_ctx)){ if(avctx->pix_fmt == AV_PIX_FMT_YUV420P10) codec_id = AMFVideoDecoderHW_H265_MAIN10; } } break; case AV_CODEC_ID_AV1: codec_id = AMFVideoDecoderHW_AV1; break; default: break; } AMF_RETURN_IF_FALSE(ctx, codec_id != NULL, AVERROR(EINVAL), "Codec %d is not supported\n", avctx->codec->id); res = amf_device_ctx->factory->pVtbl->CreateComponent(amf_device_ctx->factory, amf_device_ctx->context, codec_id, &ctx->decoder); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_ENCODER_NOT_FOUND, "CreateComponent(%ls) failed with error %d\n", codec_id, res); // Color Metadata /// Color Range (Support for older Drivers) if (avctx->color_range == AVCOL_RANGE_JPEG) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->decoder, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 1); } else if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->decoder, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 0); } color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; switch (avctx->colorspace) { case AVCOL_SPC_SMPTE170M: if (avctx->color_range == AVCOL_RANGE_JPEG) { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; } else { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; } break; case AVCOL_SPC_BT709: if (avctx->color_range == AVCOL_RANGE_JPEG) { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; } else { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; } break; case AVCOL_SPC_BT2020_NCL: case AVCOL_SPC_BT2020_CL: if (avctx->color_range == AVCOL_RANGE_JPEG) { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; } else { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; } break; } if (color_profile != AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_PROFILE, color_profile); if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); if (ctx->timestamp_mode != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_TIMESTAMP_MODE, ctx->timestamp_mode); if (ctx->decoder_mode != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_REORDER_MODE, ctx->decoder_mode); if (ctx->dpb_size != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_DPB_SIZE, ctx->dpb_size); if (ctx->lowlatency != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_LOW_LATENCY, ctx->lowlatency); if (ctx->smart_access_video != -1) { AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_ENABLE_SMART_ACCESS_VIDEO, ctx->smart_access_video != 0); if (res != AMF_OK) { av_log(avctx, AV_LOG_ERROR, "The Smart Access Video is not supported by AMF decoder.\n"); return AVERROR(EINVAL); } else { av_log(avctx, AV_LOG_INFO, "The Smart Access Video (%d) is set.\n", ctx->smart_access_video); // Set low latency mode if Smart Access Video is enabled if (ctx->smart_access_video != 0) { AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_LOW_LATENCY, true); av_log(avctx, AV_LOG_INFO, "The Smart Access Video set low latency mode for decoder.\n"); } } } if (ctx->skip_transfer_sav != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_SKIP_TRANSFER_SMART_ACCESS_VIDEO, ctx->skip_transfer_sav); if (ctx->copy_output != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_SURFACE_COPY, ctx->copy_output); if (avctx->extradata_size) { res = amf_device_ctx->context->pVtbl->AllocBuffer(amf_device_ctx->context, AMF_MEMORY_HOST, avctx->extradata_size, &buffer); if (res == AMF_OK) { memcpy(buffer->pVtbl->GetNative(buffer), avctx->extradata, avctx->extradata_size); AMF_ASSIGN_PROPERTY_INTERFACE(res,ctx->decoder, AMF_VIDEO_DECODER_EXTRADATA, buffer); buffer->pVtbl->Release(buffer); buffer = NULL; } } if (ctx->surface_pool_size == -1) { ctx->surface_pool_size = pool_size; if (avctx->extra_hw_frames > 0) ctx->surface_pool_size += avctx->extra_hw_frames; if (avctx->active_thread_type & FF_THREAD_FRAME) ctx->surface_pool_size += avctx->thread_count; } //at the moment, there is such a restriction in AMF. //when it is possible, I will remove this code if (ctx->surface_pool_size > 100) ctx->surface_pool_size = 100; AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_SURFACE_POOL_SIZE, ctx->surface_pool_size); res = ctx->decoder->pVtbl->Init(ctx->decoder, AMF_SURFACE_UNKNOWN, avctx->width, avctx->height); if (res != AMF_OK) { av_log(avctx, AV_LOG_ERROR, "Decoder initialization failed with error %d\n", res); return AVERROR(EINVAL); } return 0;} static int amf_decode_close(AVCodecContext *avctx){ AMFDecoderContext *ctx = avctx->priv_data; if (ctx->decoder) { ctx->decoder->pVtbl->Terminate(ctx->decoder); ctx->decoder->pVtbl->Release(ctx->decoder); ctx->decoder = NULL; } av_buffer_unref(&ctx->device_ctx_ref); av_packet_free(&ctx->in_pkt); return 0;} static int amf_init_frames_context(AVCodecContext *avctx, int sw_format, int new_width, int new_height){ int ret; AVHWDeviceContext *hwdev_ctx; AVHWFramesContext *hwframes_ctx; AMFDecoderContext *ctx; if (!avctx->hw_frames_ctx || !avctx->hw_device_ctx) return 0; hwdev_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data; hwframes_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; ctx = avctx->priv_data; if (hwdev_ctx->type != AV_HWDEVICE_TYPE_AMF) return 0; hwframes_ctx->width = new_width; hwframes_ctx->height = new_height; hwframes_ctx->format = AV_PIX_FMT_AMF_SURFACE; hwframes_ctx->sw_format = sw_format; hwframes_ctx->initial_pool_size = ctx->surface_pool_size + 8; ret = av_hwframe_ctx_init(avctx->hw_frames_ctx); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error initializing a AMF frame pool\n"); av_buffer_unref(&avctx->hw_frames_ctx); return ret; } return 0;} static int amf_decode_init(AVCodecContext *avctx){ AMFDecoderContext *ctx = avctx->priv_data; int ret; ctx->in_pkt = av_packet_alloc(); if (!ctx->in_pkt) return AVERROR(ENOMEM); if (avctx->hw_device_ctx && !avctx->hw_frames_ctx) { AVHWDeviceContext *hwdev_ctx; hwdev_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data; if (hwdev_ctx->type == AV_HWDEVICE_TYPE_AMF) { ctx->device_ctx_ref = av_buffer_ref(avctx->hw_device_ctx); avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); AMF_GOTO_FAIL_IF_FALSE(avctx, !!avctx->hw_frames_ctx, AVERROR(ENOMEM), "av_hwframe_ctx_alloc failed\n"); } else { ret = av_hwdevice_ctx_create_derived(&ctx->device_ctx_ref, AV_HWDEVICE_TYPE_AMF, avctx->hw_device_ctx, 0); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to create derived AMF device context: %s\n", av_err2str(ret)); } } else { ret = av_hwdevice_ctx_create(&ctx->device_ctx_ref, AV_HWDEVICE_TYPE_AMF, NULL, NULL, 0); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to create hardware device context (AMF) : %s\n", av_err2str(ret)); } if ((ret = amf_init_decoder(avctx)) == 0) { AVHWDeviceContext *hw_device_ctx = (AVHWDeviceContext*)ctx->device_ctx_ref->data; AVAMFDeviceContext *amf_device_ctx = (AVAMFDeviceContext*)hw_device_ctx->hwctx; enum AVPixelFormat surf_pix_fmt = AV_PIX_FMT_NONE; if(amf_legacy_driver_no_bitness_detect(amf_device_ctx)){ // if bitness detection is not supported in legacy driver use format from container switch (avctx->pix_fmt) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: surf_pix_fmt = AV_PIX_FMT_NV12; break; case AV_PIX_FMT_YUV420P10: surf_pix_fmt = AV_PIX_FMT_P010; break; } }else{ AMFVariantStruct format_var = {0}; ret = ctx->decoder->pVtbl->GetProperty(ctx->decoder, AMF_VIDEO_DECODER_OUTPUT_FORMAT, &format_var); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == AMF_OK, AVERROR(EINVAL), "Failed to get output format (AMF) : %d\n", ret); surf_pix_fmt = av_amf_to_av_format(format_var.int64Value); } if(avctx->hw_frames_ctx) { // this values should be set for avcodec_open2 // will be updated after header decoded if not true. if(surf_pix_fmt == AV_PIX_FMT_NONE) surf_pix_fmt = AV_PIX_FMT_NV12; // for older drivers if (!avctx->coded_width) avctx->coded_width = 1280; if (!avctx->coded_height) avctx->coded_height = 720; ret = amf_init_frames_context(avctx, surf_pix_fmt, avctx->coded_width, avctx->coded_height); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to init frames context (AMF) : %s\n", av_err2str(ret)); } else avctx->pix_fmt = surf_pix_fmt; return 0; }fail: amf_decode_close(avctx); return ret;} static AMF_RESULT amf_get_property_buffer(AMFData *object, const wchar_t *name, AMFBuffer **val){ AMF_RESULT res; AMFVariantStruct var; res = AMFVariantInit(&var); if (res == AMF_OK) { res = object->pVtbl->GetProperty(object, name, &var); if (res == AMF_OK) { if (var.type == AMF_VARIANT_INTERFACE) { AMFGuid guid_AMFBuffer = IID_AMFBuffer(); AMFInterface *amf_interface = AMFVariantInterface(&var); res = amf_interface->pVtbl->QueryInterface(amf_interface, &guid_AMFBuffer, (void**)val); } else { res = AMF_INVALID_DATA_TYPE; } } AMFVariantClear(&var); } return res;} static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, AVFrame *frame){ AMFVariantStruct var = {0}; AMFPlane *plane; int i; int ret; int format_amf; if (avctx->hw_device_ctx && ((AVHWDeviceContext*)avctx->hw_device_ctx->data)->type == AV_HWDEVICE_TYPE_AMF) { // prepare frame similar to ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF); ret = ff_decode_frame_props(avctx, frame); if (ret < 0) return ret; avctx->sw_pix_fmt = avctx->pix_fmt; ret = ff_attach_decode_data(frame); if (ret < 0) return ret; frame->width = avctx->width; frame->height = avctx->height; //// frame->buf[0] = av_buffer_create((uint8_t *)surface, sizeof(surface), amf_free_amfsurface, (void*)avctx, AV_BUFFER_FLAG_READONLY); AMF_RETURN_IF_FALSE(avctx, !!frame->buf[0], AVERROR(ENOMEM), "av_buffer_create for amf surface failed."); frame->data[0] = (uint8_t *)surface; frame->format = AV_PIX_FMT_AMF_SURFACE; format_amf = surface->pVtbl->GetFormat(surface); avctx->sw_pix_fmt = av_amf_to_av_format(format_amf); frame->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx); } else { ret = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST); AMF_RETURN_IF_FALSE(avctx, ret == AMF_OK, AVERROR_UNKNOWN, "Convert(amf::AMF_MEMORY_HOST) failed with error %d\n", ret); for (i = 0; i < surface->pVtbl->GetPlanesCount(surface); i++) { plane = surface->pVtbl->GetPlaneAt(surface, i); frame->data[i] = plane->pVtbl->GetNative(plane); frame->linesize[i] = plane->pVtbl->GetHPitch(plane); } frame->buf[0] = av_buffer_create((uint8_t *)surface, sizeof(surface), amf_free_amfsurface, (void*)avctx, AV_BUFFER_FLAG_READONLY); AMF_RETURN_IF_FALSE(avctx, !!frame->buf[0], AVERROR(ENOMEM), "av_buffer_create for amf surface failed."); format_amf = surface->pVtbl->GetFormat(surface); frame->format = av_amf_to_av_format(format_amf); } frame->width = avctx->width; frame->height = avctx->height; frame->pts = surface->pVtbl->GetPts(surface); surface->pVtbl->GetProperty(surface, L"FFMPEG:dts", &var); frame->pkt_dts = var.int64Value; frame->duration = surface->pVtbl->GetDuration(surface); if (frame->duration < 0) frame->duration = 0; frame->color_range = avctx->color_range; frame->colorspace = avctx->colorspace; frame->color_trc = avctx->color_trc; frame->color_primaries = avctx->color_primaries; if (frame->color_trc == AVCOL_TRC_SMPTE2084) { AMFBuffer * hdrmeta_buffer = NULL; ret = amf_get_property_buffer((AMFData *)surface, AMF_VIDEO_DECODER_HDR_METADATA, &hdrmeta_buffer); if (hdrmeta_buffer != NULL) { AMFHDRMetadata * hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer); if (ret != AMF_OK) return ret; if (hdrmeta != NULL) { AVMasteringDisplayMetadata *mastering = av_mastering_display_metadata_create_side_data(frame); const int chroma_den = 50000; const int luma_den = 10000; if (!mastering) return AVERROR(ENOMEM); mastering->display_primaries[0][0] = av_make_q(hdrmeta->redPrimary[0], chroma_den); mastering->display_primaries[0][1] = av_make_q(hdrmeta->redPrimary[1], chroma_den); mastering->display_primaries[1][0] = av_make_q(hdrmeta->greenPrimary[0], chroma_den); mastering->display_primaries[1][1] = av_make_q(hdrmeta->greenPrimary[1], chroma_den); mastering->display_primaries[2][0] = av_make_q(hdrmeta->bluePrimary[0], chroma_den); mastering->display_primaries[2][1] = av_make_q(hdrmeta->bluePrimary[1], chroma_den); mastering->white_point[0] = av_make_q(hdrmeta->whitePoint[0], chroma_den); mastering->white_point[1] = av_make_q(hdrmeta->whitePoint[1], chroma_den); mastering->max_luminance = av_make_q(hdrmeta->maxMasteringLuminance, luma_den); mastering->min_luminance = av_make_q(hdrmeta->maxMasteringLuminance, luma_den); mastering->has_luminance = 1; mastering->has_primaries = 1; if (hdrmeta->maxContentLightLevel) { AVContentLightMetadata *light = av_content_light_metadata_create_side_data(frame); if (!light) return AVERROR(ENOMEM); light->MaxCLL = hdrmeta->maxContentLightLevel; light->MaxFALL = hdrmeta->maxFrameAverageLightLevel; } } } } return 0;} static AMF_RESULT amf_receive_frame(AVCodecContext *avctx, AVFrame *frame){ AMFDecoderContext *ctx = avctx->priv_data; AMF_RESULT ret = AMF_OK; AMFSurface *surface = NULL; AMFData *data_out = NULL; ret = ctx->decoder->pVtbl->QueryOutput(ctx->decoder, &data_out); if (ret != AMF_OK && ret != AMF_REPEAT) { return ret; } if (data_out == NULL) { return AMF_REPEAT; } if (data_out) { AMFGuid guid = IID_AMFSurface(); data_out->pVtbl->QueryInterface(data_out, &guid, (void**)&surface); // query for buffer interface data_out->pVtbl->Release(data_out); data_out = NULL; } ret = amf_amfsurface_to_avframe(avctx, surface, frame); AMF_GOTO_FAIL_IF_FALSE(avctx, ret >= 0, AMF_FAIL, "Failed to convert AMFSurface to AVFrame = %d\n", ret); return AMF_OK;fail: Lines 1–500 of 716. Load more
/* * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "libavutil/hwcontext_amf.h" #include "libavutil/hwcontext_amf_internal.h" #include "amfdec.h" #include "codec_internal.h" #include "hwconfig.h" #include "libavutil/imgutils.h" #include "libavutil/mem.h" #include "libavutil/time.h" #include "decode.h" #include "libavutil/mastering_display_metadata.h" #if CONFIG_D3D11VA #include "libavutil/hwcontext_d3d11va.h" #endif #if CONFIG_DXVA2 #define COBJMACROS #include "libavutil/hwcontext_dxva2.h" #endif #ifdef _WIN32 #include "compat/w32dlfcn.h" #else #include <dlfcn.h> #endif //will be in public headers soon #define AMF_VIDEO_DECODER_OUTPUT_FORMAT L"OutputDecodeFormat" const enum AVPixelFormat amf_dec_pix_fmts[] = { AV_PIX_FMT_NV12, AV_PIX_FMT_P010, AV_PIX_FMT_P012, AV_PIX_FMT_AMF_SURFACE, AV_PIX_FMT_NONE }; static const AVCodecHWConfigInternal *const amf_hw_configs[] = { &(const AVCodecHWConfigInternal) { .public = { .pix_fmt = AV_PIX_FMT_AMF_SURFACE, .methods = AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX | AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, .device_type = AV_HWDEVICE_TYPE_AMF, }, .hwaccel = NULL, }, NULL }; static void amf_free_amfsurface(void *opaque, uint8_t *data) { AMFSurface *surface = (AMFSurface*)(data); surface->pVtbl->Release(surface); } static int amf_legacy_driver_no_bitness_detect(AVAMFDeviceContext *amf_device_ctx) { if( AMF_GET_MAJOR_VERSION(amf_device_ctx->version) <= 1 && AMF_GET_MINOR_VERSION(amf_device_ctx->version) <= 4 && AMF_GET_SUBMINOR_VERSION(amf_device_ctx->version) < 36) return 1; return 0; } static int amf_init_decoder(AVCodecContext *avctx) { AMFDecoderContext *ctx = avctx->priv_data; AVHWDeviceContext *hw_device_ctx = (AVHWDeviceContext*)ctx->device_ctx_ref->data; AVAMFDeviceContext *amf_device_ctx = (AVAMFDeviceContext*)hw_device_ctx->hwctx; const wchar_t *codec_id = NULL; AMF_RESULT res; AMFBuffer *buffer; amf_int64 color_profile; int pool_size = 36; ctx->drain = 0; ctx->resolution_changed = 0; switch (avctx->codec->id) { case AV_CODEC_ID_H264: codec_id = AMFVideoDecoderUVD_H264_AVC; break; case AV_CODEC_ID_HEVC: { codec_id = AMFVideoDecoderHW_H265_HEVC; // way-around for older drivers that don't support dynamic butness detection - // define HEVC 10-bit based on container info if(amf_legacy_driver_no_bitness_detect(amf_device_ctx)){ if(avctx->pix_fmt == AV_PIX_FMT_YUV420P10) codec_id = AMFVideoDecoderHW_H265_MAIN10; } } break; case AV_CODEC_ID_AV1: codec_id = AMFVideoDecoderHW_AV1; break; default: break; } AMF_RETURN_IF_FALSE(ctx, codec_id != NULL, AVERROR(EINVAL), "Codec %d is not supported\n", avctx->codec->id); res = amf_device_ctx->factory->pVtbl->CreateComponent(amf_device_ctx->factory, amf_device_ctx->context, codec_id, &ctx->decoder); AMF_RETURN_IF_FALSE(ctx, res == AMF_OK, AVERROR_ENCODER_NOT_FOUND, "CreateComponent(%ls) failed with error %d\n", codec_id, res); // Color Metadata /// Color Range (Support for older Drivers) if (avctx->color_range == AVCOL_RANGE_JPEG) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->decoder, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 1); } else if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED) { AMF_ASSIGN_PROPERTY_BOOL(res, ctx->decoder, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 0); } color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN; switch (avctx->colorspace) { case AVCOL_SPC_SMPTE170M: if (avctx->color_range == AVCOL_RANGE_JPEG) { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601; } else { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601; } break; case AVCOL_SPC_BT709: if (avctx->color_range == AVCOL_RANGE_JPEG) { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709; } else { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709; } break; case AVCOL_SPC_BT2020_NCL: case AVCOL_SPC_BT2020_CL: if (avctx->color_range == AVCOL_RANGE_JPEG) { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020; } else { color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020; } break; } if (color_profile != AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_PROFILE, color_profile); if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC, (amf_int64)avctx->color_trc); if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_PRIMARIES, (amf_int64)avctx->color_primaries); if (ctx->timestamp_mode != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_TIMESTAMP_MODE, ctx->timestamp_mode); if (ctx->decoder_mode != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_REORDER_MODE, ctx->decoder_mode); if (ctx->dpb_size != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_DPB_SIZE, ctx->dpb_size); if (ctx->lowlatency != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_LOW_LATENCY, ctx->lowlatency); if (ctx->smart_access_video != -1) { AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_ENABLE_SMART_ACCESS_VIDEO, ctx->smart_access_video != 0); if (res != AMF_OK) { av_log(avctx, AV_LOG_ERROR, "The Smart Access Video is not supported by AMF decoder.\n"); return AVERROR(EINVAL); } else { av_log(avctx, AV_LOG_INFO, "The Smart Access Video (%d) is set.\n", ctx->smart_access_video); // Set low latency mode if Smart Access Video is enabled if (ctx->smart_access_video != 0) { AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_LOW_LATENCY, true); av_log(avctx, AV_LOG_INFO, "The Smart Access Video set low latency mode for decoder.\n"); } } } if (ctx->skip_transfer_sav != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_SKIP_TRANSFER_SMART_ACCESS_VIDEO, ctx->skip_transfer_sav); if (ctx->copy_output != -1) AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_SURFACE_COPY, ctx->copy_output); if (avctx->extradata_size) { res = amf_device_ctx->context->pVtbl->AllocBuffer(amf_device_ctx->context, AMF_MEMORY_HOST, avctx->extradata_size, &buffer); if (res == AMF_OK) { memcpy(buffer->pVtbl->GetNative(buffer), avctx->extradata, avctx->extradata_size); AMF_ASSIGN_PROPERTY_INTERFACE(res,ctx->decoder, AMF_VIDEO_DECODER_EXTRADATA, buffer); buffer->pVtbl->Release(buffer); buffer = NULL; } } if (ctx->surface_pool_size == -1) { ctx->surface_pool_size = pool_size; if (avctx->extra_hw_frames > 0) ctx->surface_pool_size += avctx->extra_hw_frames; if (avctx->active_thread_type & FF_THREAD_FRAME) ctx->surface_pool_size += avctx->thread_count; } //at the moment, there is such a restriction in AMF. //when it is possible, I will remove this code if (ctx->surface_pool_size > 100) ctx->surface_pool_size = 100; AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_SURFACE_POOL_SIZE, ctx->surface_pool_size); res = ctx->decoder->pVtbl->Init(ctx->decoder, AMF_SURFACE_UNKNOWN, avctx->width, avctx->height); if (res != AMF_OK) { av_log(avctx, AV_LOG_ERROR, "Decoder initialization failed with error %d\n", res); return AVERROR(EINVAL); } return 0; } static int amf_decode_close(AVCodecContext *avctx) { AMFDecoderContext *ctx = avctx->priv_data; if (ctx->decoder) { ctx->decoder->pVtbl->Terminate(ctx->decoder); ctx->decoder->pVtbl->Release(ctx->decoder); ctx->decoder = NULL; } av_buffer_unref(&ctx->device_ctx_ref); av_packet_free(&ctx->in_pkt); return 0; } static int amf_init_frames_context(AVCodecContext *avctx, int sw_format, int new_width, int new_height) { int ret; AVHWDeviceContext *hwdev_ctx; AVHWFramesContext *hwframes_ctx; AMFDecoderContext *ctx; if (!avctx->hw_frames_ctx || !avctx->hw_device_ctx) return 0; hwdev_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data; hwframes_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data; ctx = avctx->priv_data; if (hwdev_ctx->type != AV_HWDEVICE_TYPE_AMF) return 0; hwframes_ctx->width = new_width; hwframes_ctx->height = new_height; hwframes_ctx->format = AV_PIX_FMT_AMF_SURFACE; hwframes_ctx->sw_format = sw_format; hwframes_ctx->initial_pool_size = ctx->surface_pool_size + 8; ret = av_hwframe_ctx_init(avctx->hw_frames_ctx); if (ret < 0) { av_log(NULL, AV_LOG_ERROR, "Error initializing a AMF frame pool\n"); av_buffer_unref(&avctx->hw_frames_ctx); return ret; } return 0; } static int amf_decode_init(AVCodecContext *avctx) { AMFDecoderContext *ctx = avctx->priv_data; int ret; ctx->in_pkt = av_packet_alloc(); if (!ctx->in_pkt) return AVERROR(ENOMEM); if (avctx->hw_device_ctx && !avctx->hw_frames_ctx) { AVHWDeviceContext *hwdev_ctx; hwdev_ctx = (AVHWDeviceContext*)avctx->hw_device_ctx->data; if (hwdev_ctx->type == AV_HWDEVICE_TYPE_AMF) { ctx->device_ctx_ref = av_buffer_ref(avctx->hw_device_ctx); avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); AMF_GOTO_FAIL_IF_FALSE(avctx, !!avctx->hw_frames_ctx, AVERROR(ENOMEM), "av_hwframe_ctx_alloc failed\n"); } else { ret = av_hwdevice_ctx_create_derived(&ctx->device_ctx_ref, AV_HWDEVICE_TYPE_AMF, avctx->hw_device_ctx, 0); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to create derived AMF device context: %s\n", av_err2str(ret)); } } else { ret = av_hwdevice_ctx_create(&ctx->device_ctx_ref, AV_HWDEVICE_TYPE_AMF, NULL, NULL, 0); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to create hardware device context (AMF) : %s\n", av_err2str(ret)); } if ((ret = amf_init_decoder(avctx)) == 0) { AVHWDeviceContext *hw_device_ctx = (AVHWDeviceContext*)ctx->device_ctx_ref->data; AVAMFDeviceContext *amf_device_ctx = (AVAMFDeviceContext*)hw_device_ctx->hwctx; enum AVPixelFormat surf_pix_fmt = AV_PIX_FMT_NONE; if(amf_legacy_driver_no_bitness_detect(amf_device_ctx)){ // if bitness detection is not supported in legacy driver use format from container switch (avctx->pix_fmt) { case AV_PIX_FMT_YUV420P: case AV_PIX_FMT_YUVJ420P: surf_pix_fmt = AV_PIX_FMT_NV12; break; case AV_PIX_FMT_YUV420P10: surf_pix_fmt = AV_PIX_FMT_P010; break; } }else{ AMFVariantStruct format_var = {0}; ret = ctx->decoder->pVtbl->GetProperty(ctx->decoder, AMF_VIDEO_DECODER_OUTPUT_FORMAT, &format_var); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == AMF_OK, AVERROR(EINVAL), "Failed to get output format (AMF) : %d\n", ret); surf_pix_fmt = av_amf_to_av_format(format_var.int64Value); } if(avctx->hw_frames_ctx) { // this values should be set for avcodec_open2 // will be updated after header decoded if not true. if(surf_pix_fmt == AV_PIX_FMT_NONE) surf_pix_fmt = AV_PIX_FMT_NV12; // for older drivers if (!avctx->coded_width) avctx->coded_width = 1280; if (!avctx->coded_height) avctx->coded_height = 720; ret = amf_init_frames_context(avctx, surf_pix_fmt, avctx->coded_width, avctx->coded_height); AMF_GOTO_FAIL_IF_FALSE(avctx, ret == 0, ret, "Failed to init frames context (AMF) : %s\n", av_err2str(ret)); } else avctx->pix_fmt = surf_pix_fmt; return 0; } fail: amf_decode_close(avctx); return ret; } static AMF_RESULT amf_get_property_buffer(AMFData *object, const wchar_t *name, AMFBuffer **val) { AMF_RESULT res; AMFVariantStruct var; res = AMFVariantInit(&var); if (res == AMF_OK) { res = object->pVtbl->GetProperty(object, name, &var); if (res == AMF_OK) { if (var.type == AMF_VARIANT_INTERFACE) { AMFGuid guid_AMFBuffer = IID_AMFBuffer(); AMFInterface *amf_interface = AMFVariantInterface(&var); res = amf_interface->pVtbl->QueryInterface(amf_interface, &guid_AMFBuffer, (void**)val); } else { res = AMF_INVALID_DATA_TYPE; } } AMFVariantClear(&var); } return res; } static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, AVFrame *frame) { AMFVariantStruct var = {0}; AMFPlane *plane; int i; int ret; int format_amf; if (avctx->hw_device_ctx && ((AVHWDeviceContext*)avctx->hw_device_ctx->data)->type == AV_HWDEVICE_TYPE_AMF) { // prepare frame similar to ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF); ret = ff_decode_frame_props(avctx, frame); if (ret < 0) return ret; avctx->sw_pix_fmt = avctx->pix_fmt; ret = ff_attach_decode_data(frame); if (ret < 0) return ret; frame->width = avctx->width; frame->height = avctx->height; //// frame->buf[0] = av_buffer_create((uint8_t *)surface, sizeof(surface), amf_free_amfsurface, (void*)avctx, AV_BUFFER_FLAG_READONLY); AMF_RETURN_IF_FALSE(avctx, !!frame->buf[0], AVERROR(ENOMEM), "av_buffer_create for amf surface failed."); frame->data[0] = (uint8_t *)surface; frame->format = AV_PIX_FMT_AMF_SURFACE; format_amf = surface->pVtbl->GetFormat(surface); avctx->sw_pix_fmt = av_amf_to_av_format(format_amf); frame->hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx); } else { ret = surface->pVtbl->Convert(surface, AMF_MEMORY_HOST); AMF_RETURN_IF_FALSE(avctx, ret == AMF_OK, AVERROR_UNKNOWN, "Convert(amf::AMF_MEMORY_HOST) failed with error %d\n", ret); for (i = 0; i < surface->pVtbl->GetPlanesCount(surface); i++) { plane = surface->pVtbl->GetPlaneAt(surface, i); frame->data[i] = plane->pVtbl->GetNative(plane); frame->linesize[i] = plane->pVtbl->GetHPitch(plane); } frame->buf[0] = av_buffer_create((uint8_t *)surface, sizeof(surface), amf_free_amfsurface, (void*)avctx, AV_BUFFER_FLAG_READONLY); AMF_RETURN_IF_FALSE(avctx, !!frame->buf[0], AVERROR(ENOMEM), "av_buffer_create for amf surface failed."); format_amf = surface->pVtbl->GetFormat(surface); frame->format = av_amf_to_av_format(format_amf); } frame->width = avctx->width; frame->height = avctx->height; frame->pts = surface->pVtbl->GetPts(surface); surface->pVtbl->GetProperty(surface, L"FFMPEG:dts", &var); frame->pkt_dts = var.int64Value; frame->duration = surface->pVtbl->GetDuration(surface); if (frame->duration < 0) frame->duration = 0; frame->color_range = avctx->color_range; frame->colorspace = avctx->colorspace; frame->color_trc = avctx->color_trc; frame->color_primaries = avctx->color_primaries; if (frame->color_trc == AVCOL_TRC_SMPTE2084) { AMFBuffer * hdrmeta_buffer = NULL; ret = amf_get_property_buffer((AMFData *)surface, AMF_VIDEO_DECODER_HDR_METADATA, &hdrmeta_buffer); if (hdrmeta_buffer != NULL) { AMFHDRMetadata * hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer); if (ret != AMF_OK) return ret; if (hdrmeta != NULL) { AVMasteringDisplayMetadata *mastering = av_mastering_display_metadata_create_side_data(frame); const int chroma_den = 50000; const int luma_den = 10000; if (!mastering) return AVERROR(ENOMEM); mastering->display_primaries[0][0] = av_make_q(hdrmeta->redPrimary[0], chroma_den); mastering->display_primaries[0][1] = av_make_q(hdrmeta->redPrimary[1], chroma_den); mastering->display_primaries[1][0] = av_make_q(hdrmeta->greenPrimary[0], chroma_den); mastering->display_primaries[1][1] = av_make_q(hdrmeta->greenPrimary[1], chroma_den); mastering->display_primaries[2][0] = av_make_q(hdrmeta->bluePrimary[0], chroma_den); mastering->display_primaries[2][1] = av_make_q(hdrmeta->bluePrimary[1], chroma_den); mastering->white_point[0] = av_make_q(hdrmeta->whitePoint[0], chroma_den); mastering->white_point[1] = av_make_q(hdrmeta->whitePoint[1], chroma_den); mastering->max_luminance = av_make_q(hdrmeta->maxMasteringLuminance, luma_den); mastering->min_luminance = av_make_q(hdrmeta->maxMasteringLuminance, luma_den); mastering->has_luminance = 1; mastering->has_primaries = 1; if (hdrmeta->maxContentLightLevel) { AVContentLightMetadata *light = av_content_light_metadata_create_side_data(frame); if (!light) return AVERROR(ENOMEM); light->MaxCLL = hdrmeta->maxContentLightLevel; light->MaxFALL = hdrmeta->maxFrameAverageLightLevel; } } } } return 0; } static AMF_RESULT amf_receive_frame(AVCodecContext *avctx, AVFrame *frame) { AMFDecoderContext *ctx = avctx->priv_data; AMF_RESULT ret = AMF_OK; AMFSurface *surface = NULL; AMFData *data_out = NULL; ret = ctx->decoder->pVtbl->QueryOutput(ctx->decoder, &data_out); if (ret != AMF_OK && ret != AMF_REPEAT) { return ret; } if (data_out == NULL) { return AMF_REPEAT; } if (data_out) { AMFGuid guid = IID_AMFSurface(); data_out->pVtbl->QueryInterface(data_out, &guid, (void**)&surface); // query for buffer interface data_out->pVtbl->Release(data_out); data_out = NULL; } ret = amf_amfsurface_to_avframe(avctx, surface, frame); AMF_GOTO_FAIL_IF_FALSE(avctx, ret >= 0, AMF_FAIL, "Failed to convert AMFSurface to AVFrame = %d\n", ret); return AMF_OK; fail: