如何快速实现ffmpeg dxva2硬解码

首先,ffmpeg本身支持dxva2硬结解码,但解码器类型有限,并不是所有的都可以硬解。我使用的ffmpeg的版本是3.2,支持dxva2硬件加速的有以下几种文件格式: AV_CODEC_ID_MPEG2VIDEO、AV_CODEC_ID_H264、AV_CODEC_ID_VC1、AV_CODEC_ID_WMV3、AV_CODEC_ID_HEVC、AV_CODEC_ID_VP9

在正常软解代码的基础上,我们需要ffmpeg_dxva2.h和ffmpeg_dxva2.cpp。ffmpeg的源码里有ffmpeg_dxva2.c,前面的.h和.cpp就是从.c以及.c中包含的头文件中提取出来的,具体的下载地址为:http://download.csdn.net/detail/an505479313/9823407


下面说一下代码实现,直接贴代码:



static AVPixelFormat GetHwFormat(AVCodecContext *s, const AVPixelFormat *pix_fmts)
{
    InputStream* ist = (InputStream*)s->opaque;
    ist->active_hwaccel_id = HWACCEL_DXVA2;
    ist->hwaccel_pix_fmt = AV_PIX_FMT_DXVA2_VLD;
    return ist->hwaccel_pix_fmt;
}


int DecodeThread::stream_component_open(VideoState *is, int stream_index)
{
    AVFormatContext *ic = is->ic;
    AVCodecContext *avctx;
    AVCodec *codec = NULL;
    AVDictionary *opts = NULL;
    int ret = -1;
 
 
    if (stream_index < 0 || stream_index >= (int)ic->nb_streams)
        return -1;
    avctx = avcodec_alloc_context3(NULL);
    if (!avctx)
        return AVERROR(ENOMEM);
 
 
    ret = avcodec_parameters_to_context(avctx, ic->streams[stream_index]->codecpar);
    if (ret < 0)
        return -1;
    av_codec_set_pkt_timebase(avctx, ic->streams[stream_index]->time_base);
 
 
    codec = avcodec_find_decoder(avctx->codec_id);
 
 
    if(avctx->codec_id == AV_CODEC_ID_H264)
        avctx->thread_count = 1;
    else
    {
        if (!av_dict_get(opts, "threads", NULL, 0))
            av_dict_set(&opts, "threads", "auto", 0);
    }
    InputStream *ist = new InputStream();
 
 
    switch(avctx->codec_type){
    case AVMEDIA_TYPE_AUDIO:
        is->last_audio_stream = stream_index;
        break;
    case AVMEDIA_TYPE_VIDEO:
        is->last_video_stream = stream_index;
 
 
        ist->hwaccel_id     = HWACCEL_AUTO;
        ist->hwaccel_device = "dxva2";
        ist->dec            = codec;
        ist->dec_ctx        = avctx;
        avctx->coded_width  = avctx->width;
        avctx->coded_height = avctx->height;
 
 
        avctx->opaque = ist;
        dxva2_init(avctx);
        avctx->get_buffer2           = ist->hwaccel_get_buffer;
        avctx->get_format            = GetHwFormat;
        avctx->thread_safe_callbacks = 1;
        break;
    default:
        break;
    }
 
 
    if(!codec)
        return -1;
 
 
    avctx->lowres = lowres;
 
 
    if(avctx->lowres > codec->max_lowres){
        av_log(avctx, AV_LOG_WARNING, "The maximum value for lowres supported by the decoder is %d\n",
               codec->max_lowres);
        avctx->lowres= codec->max_lowres;
    }
 
 
    if(codec->capabilities & CODEC_CAP_DR1)
        avctx->flags |= CODEC_FLAG_EMU_EDGE;
 
 
 
 
 
 
    ret = avcodec_open2(avctx, codec, &opts);
    if (ret != 0) {
        return -1;
    }
 
 
    if (avctx->codec_type == AVMEDIA_TYPE_AUDIO && !is->playWithoutAudio) {
        int audio_hw_buf_size = audio_open(is, avctx->channel_layout, avctx->channels, avctx->sample_rate, &is->audio_src);
        if (audio_hw_buf_size < 0)
        {
            return -1;
        }
        is->audio_hw_buf_size = audio_hw_buf_size;
        is->audio_tgt = is->audio_src;
    }
 
 
    ic->streams[stream_index]->discard = AVDISCARD_DEFAULT;
    switch (avctx->codec_type) {
    case AVMEDIA_TYPE_AUDIO:
    if(!is->playWithoutAudio)
    {
        is->audio_stream = stream_index;
        is->audio_st = ic->streams[stream_index];
        is->audio_buf_size  = 0;
        is->audio_buf_index = 0;
 
 
        is->audio_diff_avg_coef  = exp(log(0.01) / AUDIO_DIFF_AVG_NB);
        is->audio_diff_avg_count = 0;
 
 
        is->audio_diff_threshold = 2.0 * is->audio_hw_buf_size / av_samples_get_buffer_size(NULL, is->audio_tgt.channels, is->audio_tgt.freq, is->audio_tgt.fmt, 1);
 
 
        memset(&is->audio_pkt, 0, sizeof(is->audio_pkt));
        memset(&is->audio_pkt_temp, 0, sizeof(is->audio_pkt_temp));
 
 
        packet_queue_start(&is->audioq);
        SDL_PauseAudioDevice(is->audiodeviceId,0);
        is->audioCtx = avctx;
    }
        break;
    case AVMEDIA_TYPE_VIDEO:
        is->video_stream = stream_index;
        is->video_st = ic->streams[stream_index];
        is->videoCtx = avctx;
        packet_queue_start(&is->videoq);
 
 
        start_event = new QEvent(Video_Event);
        QCoreApplication::postEvent(player,start_event);
        break;
    default:
        break;
    }
    return 0;
}
主要关于硬解的代码如下:

        ist->hwaccel_id     = HWACCEL_AUTO;
        ist->hwaccel_device = "dxva2";
        ist->dec            = codec;
        ist->dec_ctx        = avctx;
        avctx->coded_width  = avctx->width;
        avctx->coded_height = avctx->height;
 
 
        avctx->opaque = ist;
        dxva2_init(avctx);
        avctx->get_buffer2           = ist->hwaccel_get_buffer;
        avctx->get_format            = GetHwFormat;
        avctx->thread_safe_callbacks = 1;
其中ist是结构体InputStream的指针,该结构体存在于ffmpeg_dxva2.h中。

这里需要注意的是,如果要做硬解码,那么多线程解码的选项应该关掉,并设置线程数为1.我这里是为了支持其他不能用硬解的视频。

然后就是正常的打开解码器,使用_video2解码,并用dxva2_retrieve_data_call函数获取AVFrame。

猜你喜欢

转载自blog.csdn.net/an505479313/article/details/70570007