Evgeny Pavlovavcodec: add amfdec.fbfde332 years agoHistory
716 lines · 29.9 KB · C
RawDownload
/* * 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.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t