参考了csdn上Win32Project1_ffmpeg_dxva2这个例子,很不错,直接就可以运行。
但是,有几个问题:
1、窗口无法正常缩放,缩放后,图像大小并没有一起缩放
2、H265的编码格式,显示下面有一块绿色。
3、无法从显卡获取YUV420P数据或者NV12数据
3、找了很久网上也没有相关代码实现从显卡获取数据到内存(有些方法相当慢,基本无法使用!)
4、新遇到问题(2019.11),如果编码端采用硬件(GPU)编码后,会生成1088P的图像,你没看错,是1088,不是1080P,这时候解码如果直接显示会在屏幕下边产生8个像素高的绿条。
第2个问题,看了特征,心想应该是对齐方式问题,修改源码:
ffmpeg_dxva2.cpp下的static int dxva2_retrieve_data(AVCodecContext *s, AVFrame *frame)函数:
原函数是这样的:
static int dxva2_create_decoder(AVCodecContext *s)
{
InputStream *ist = (InputStream *)s->opaque;
int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx;
struct dxva_context *dxva_ctx = (dxva_context *)s->hwaccel_context;
GUID *guid_list = NULL;
unsigned guid_count = 0, i, j;
GUID device_guid = GUID_NULL;
D3DFORMAT target_format = D3DFMT_UNKNOWN;
DXVA2_VideoDesc desc = { 0 };
DXVA2_ConfigPictureDecode config;
HRESULT hr;
int surface_alignment;
int ret;
//hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list);
hr = ctx->decoder_service->GetDecoderDeviceGuids(&guid_count, &guid_list);
if (FAILED(hr)) {
av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n");
goto fail;
}
for (i = 0; dxva2_modes[i].guid; i++) {
D3DFORMAT *target_list = NULL;
unsigned target_count = 0;
const dxva2_mode *mode = &dxva2_modes[i];
if (mode->codec != s->codec_id)
continue;
for (j = 0; j < guid_count; j++) {
if (IsEqualGUID(*mode->guid, guid_list[j]))
break;
}
if (j == guid_count)
continue;
//hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list);
hr = ctx->decoder_service->GetDecoderRenderTargets(*mode->guid, &target_count, &target_list);
if (FAILED(hr)) {
continue;
}
for (unsigned j = 0; j < target_count; j++)
{
const D3DFORMAT f = target_list[j];
const d3d_format_t *format = D3dFindFormat(f);
if (format) {
printf("%s is supported for output\n", format->name);
}
else {
printf("%d is supported for output (%4.4s)\n", f, (const char*)&f);
}
}
system("pause");
for (j = 0; j < target_count; j++) {
const D3DFORMAT format = target_list[j];
if (format == MKTAG('N', 'V', '1', '2')) {
target_format = format;
break;
}
}
CoTaskMemFree(target_list);
if (target_format) {
device_guid = *mode->guid;
break;
}
}
CoTaskMemFree(guid_list);
if (IsEqualGUID(device_guid, GUID_NULL)) {
av_log(NULL, loglevel, "No decoder device for codec found\n");
goto fail;
}
desc.SampleWidth = s->coded_width;
desc.SampleHeight = s->coded_height;
desc.Format = target_format;
ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config);
if (ret < 0) {
goto fail;
}
/* decoding MPEG-2 requires additional alignment on some Intel GPUs,
but it causes issues for H.264 on certain AMD GPUs..... */
if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO)
surface_alignment = 32;
/* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure
all coding features have enough room to work with */
else if (s->codec_id == AV_CODEC_ID_HEVC)
surface_alignment = 128;
else
surface_alignment = 16;
/* 4 base work surfaces */
ctx->num_surfaces = 4;
/* add surfaces based on number of possible refs */
if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC)
ctx->num_surfaces += 16;
else if (s->codec_id == AV_CODEC_ID_VP9)
ctx->num_surfaces += 8;
else
ctx->num_surfaces += 2;
/* add extra surfaces for frame threading */
if (s->active_thread_type & FF_THREAD_FRAME)
ctx->num_surfaces += s->thread_count;
ctx->surfaces = (LPDIRECT3DSURFACE9 *)av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces));
ctx->surface_infos = (surface_info *)av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos));
if (!ctx->surfaces || !ctx->surface_infos) {
av_log(NULL, loglevel, "Unable to allocate surface arrays\n");
goto fail;
}
/*hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service,
FFALIGN(s->coded_width, surface_alignment),
FFALIGN(s->coded_height, surface_alignment),
ctx->num_surfaces - 1,
target_format, D3DPOOL_DEFAULT, 0,
DXVA2_VideoDecoderRenderTarget,
ctx->surfaces, NULL);*/
hr = ctx->decoder_service->CreateSurface(FFALIGN(s->coded_width, surface_alignment),
FFALIGN(s->coded_height, surface_alignment),
ctx->num_surfaces - 1,
target_format, D3DPOOL_DEFAULT, 0,
DXVA2_VideoDecoderRenderTarget,
ctx->surfaces, NULL);
if (FAILED(hr)) {
av_log(NULL, loglevel, "Failed to create %d video surfaces\n", ctx->num_surfaces);
goto fail;
}
/*hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid,
&desc, &config, ctx->surfaces,
ctx->num_surfaces, &ctx->decoder);*/
hr = ctx->decoder_service->CreateVideoDecoder(device_guid,
&desc, &config, ctx->surfaces,
ctx->num_surfaces, &ctx->decoder);
if (FAILED(hr)) {
av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n");
goto fail;
}
ctx->decoder_guid = device_guid;
ctx->decoder_config = config;
dxva_ctx->cfg = &ctx->decoder_config;
dxva_ctx->decoder = ctx->decoder;
dxva_ctx->surface = ctx->surfaces;
dxva_ctx->surface_count = ctx->num_surfaces;
if (IsEqualGUID(ctx->decoder_guid, DXVADDI_Intel_ModeH264_E))
dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
return 0;
fail:
dxva2_destroy_decoder(s);
return AVERROR(EINVAL);
}
修改为:
static int dxva2_create_decoder(AVCodecContext *s)
{
InputStream *ist = (InputStream *)s->opaque;
int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR;
DXVA2Context *ctx = (DXVA2Context *)ist->hwaccel_ctx;
struct dxva_context *dxva_ctx = (dxva_context *)s->hwaccel_context;
GUID *guid_list = NULL;
unsigned guid_count = 0, i, j;
GUID device_guid = GUID_NULL;
D3DFORMAT target_format = D3DFMT_UNKNOWN;
DXVA2_VideoDesc desc = { 0 };
DXVA2_ConfigPictureDecode config;
HRESULT hr;
int surface_alignment;
int ret;
//hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list);
hr = ctx->decoder_service->GetDecoderDeviceGuids(&guid_count, &guid_list);
if (FAILED(hr)) {
av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n");
goto fail;
}
for (i = 0; dxva2_modes[i].guid; i++) {
D3DFORMAT *target_list = NULL;
unsigned target_count = 0;
const dxva2_mode *mode = &dxva2_modes[i];
if (mode->codec != s->codec_id)
continue;
for (j = 0; j < guid_count; j++) {
if (IsEqualGUID(*mode->guid, guid_list[j]))
break;
}
if (j == guid_count)
continue;
//hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list);
hr = ctx->decoder_service->GetDecoderRenderTargets(*mode->guid, &target_count, &target_list);
if (FAILED(hr)) {
continue;
}
for (unsigned j = 0; j < target_count; j++)
{
const D3DFORMAT f = target_list[j];
const d3d_format_t *format = D3dFindFormat(f);
if (format) {
printf("%s is supported for output\n", format->name);
}
else {
printf("%d is supported for output (%4.4s)\n", f, (const char*)&f);
}
}
system("pause");
for (j = 0; j < target_count; j++) {
const D3DFORMAT format = target_list[j];
if (format == MKTAG('N', 'V', '1', '2')) {
target_format = format;
break;
}
}
CoTaskMemFree(target_list);
if (target_format) {
device_guid = *mode->guid;
break;
}
}
CoTaskMemFree(guid_list);
if (IsEqualGUID(device_guid, GUID_NULL)) {
av_log(NULL, loglevel, "No decoder device for codec found\n");
goto fail;
}
desc.SampleWidth = s->coded_width;
desc.SampleHeight = s->coded_height;
desc.Format = target_format;
ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config);
if (ret < 0) {
goto fail;
}
/* decoding MPEG-2 requires additional alignment on some Intel GPUs,
but it causes issues for H.264 on certain AMD GPUs..... */
if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO)
surface_alignment = 32;
/* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure
all coding features have enough room to work with */
//else if (s->codec_id == AV_CODEC_ID_HEVC)
// surface_alignment = 128;
// else
surface_alignment = 16;
/* 4 base work surfaces */
ctx->num_surfaces = 4;
/* add surfaces based on number of possible refs */
if (s->codec_id == AV_CODEC_ID_H264 || s->codec_id == AV_CODEC_ID_HEVC)
ctx->num_surfaces += 16;
else if (s->codec_id == AV_CODEC_ID_VP9)
ctx->num_surfaces += 8;
else
ctx->num_surfaces += 2;
/* add extra surfaces for frame threading */
if (s->active_thread_type & FF_THREAD_FRAME)
ctx->num_surfaces += s->thread_count;
ctx->surfaces = (LPDIRECT3DSURFACE9 *)av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces));
ctx->surface_infos = (surface_info *)av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos));
if (!ctx->surfaces || !ctx->surface_infos) {
av_log(NULL, loglevel, "Unable to allocate surface arrays\n");
goto fail;
}
/*hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service,
FFALIGN(s->coded_width, surface_alignment),
FFALIGN(s->coded_height, surface_alignment),
ctx->num_surfaces - 1,
target_format, D3DPOOL_DEFAULT, 0,
DXVA2_VideoDecoderRenderTarget,
ctx->surfaces, NULL);*/
hr = ctx->decoder_service->CreateSurface(FFALIGN(s->coded_width, surface_alignment),
FFALIGN(s->coded_height, surface_alignment),
ctx->num_surfaces - 1,
target_format, D3DPOOL_DEFAULT, 0,
DXVA2_VideoDecoderRenderTarget,
ctx->surfaces, NULL);
if (FAILED(hr)) {
av_log(NULL, loglevel, "Failed to create %d video surfaces\n", ctx->num_surfaces);
goto fail;
}
/*hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid,
&desc, &config, ctx->surfaces,
ctx->num_surfaces, &ctx->decoder);*/
hr = ctx->decoder_service->CreateVideoDecoder(device_guid,
&desc, &config, ctx->surfaces,
ctx->num_surfaces, &ctx->decoder);
if (FAILED(hr)) {
av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n");
goto fail;
}
ctx->decoder_guid = device_guid;
ctx->decoder_config = config;
dxva_ctx->cfg = &ctx->decoder_config;
dxva_ctx->decoder = ctx->decoder;
dxva_ctx->surface = ctx->surfaces;
dxva_ctx->surface_count = ctx->num_surfaces;
if (IsEqualGUID(ctx->decoder_guid, DXVADDI_Intel_ModeH264_E))
dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
return 0;
fail:
dxva2_destroy_decoder(s);
return AVERROR(EINVAL);
}
统一为16位对齐,测试下,可以了。。。
下一篇文章讲第3个问题,我的QQ35744025,windows下播放器已完成,需要合作的或者技术交流的可以加我QQ哦
(播放器支持dxva2硬解码,D3D+OPENGL显示,音频用DSOUND播放,支持快放慢放,支持输出YV12视频和PCM音频数据)