如何快速實現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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章