首先,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。