ffmpeg를 사용하여 dxva를 사용하는 방법
FFmpeg은 코드의 DXVA 가속 디코딩을 이용하여 구현되었다. 그러나 통합 DXVA 관련된 관리 코드는 자체 플레이어에서 구현되지 않습니다.
ffmpeg구현 dxva도입 관련 코드를 디코딩
dxva2_h264.c
AVHWAccel ff_h264_dxva2_hwaccel = {
.name = "h264_dxva2",
.type = AVMEDIA_TYPE_VIDEO,
.id = CODEC_ID_H264,
.pix_fmt = PIX_FMT_DXVA2_VLD,
.start_frame = start_frame,
.decode_slice = decode_slice,
.end_frame = end_frame,
.priv_data_size = sizeof(struct dxva2_picture_context),
};
dxva2_mpeg2.c
AVHWAccel ff_mpeg2_dxva2_hwaccel = {
.name = "mpeg2_dxva2",
.type = AVMEDIA_TYPE_VIDEO,
.id = CODEC_ID_MPEG2VIDEO,
.pix_fmt = PIX_FMT_DXVA2_VLD,
.start_frame = start_frame,
.decode_slice = decode_slice,
.end_frame = end_frame,
.priv_data_size = sizeof(struct dxva2_picture_context),
};
dxva2_vc1.c
#if CONFIG_WMV3_DXVA2_HWACCEL
AVHWAccel ff_wmv3_dxva2_hwaccel = {
.name = "wmv3_dxva2",
.type = AVMEDIA_TYPE_VIDEO,
.id = CODEC_ID_WMV3,
.pix_fmt = PIX_FMT_DXVA2_VLD,
.start_frame = start_frame,
.decode_slice = decode_slice,
.end_frame = end_frame,
.priv_data_size = sizeof(struct dxva2_picture_context),
};
#endif
AVHWAccel ff_vc1_dxva2_hwaccel = {
.name = "vc1_dxva2",
.type = AVMEDIA_TYPE_VIDEO,
.id = CODEC_ID_VC1,
.pix_fmt = PIX_FMT_DXVA2_VLD,
.start_frame = start_frame,
.decode_slice = decode_slice,
.end_frame = end_frame,
.priv_data_size = sizeof(struct dxva2_picture_context),
};
위의 코드를 읽고, 당신은 디코더가 외부 할당을 통해, 복제되지 dxva_context하기 위해 사용하는 것을 발견 할 것이다
struct dxva_context {
/**
* DXVA2 decoder object
*/
IDirectXVideoDecoder *decoder;
/**
* DXVA2 configuration used to create the decoder
*/
const DXVA2_ConfigPictureDecode *cfg;
/**
* The number of surface in the surface array
*/
unsigned surface_count;
/**
* The array of Direct3D surfaces used to create the decoder
*/
LPDIRECT3DSURFACE9 *surface;
/**
* A bit field configuring the workarounds needed for using the decoder
*/
uint64_t workaround;
/**
* Private to the FFmpeg AVHWAccel implementation
*/
unsigned report_id;
};
dxva2api.c에서, 트랜스 코딩 환경 변수 상황에 맞는 과제의 실현을 (비는 FFmpeg 소스 코드, 나에 의해 실현).
static int Setup(va_dxva2_t *va, void **hw, const AVCodecContext *avctx)
{
//va_dxva2_t *va = vlc_va_dxva2_Get(external);
unsigned i;
if (va->width == avctx->width&& va->height == avctx->height && va->decoder)
goto ok;
/* */
DxDestroyVideoConversion(va);
DxDestroyVideoDecoder(va);
*hw = NULL;
if (avctx->width <= 0 || avctx->height <= 0)
return -1;
if (DxCreateVideoDecoder(va, va->codec_id, avctx))
return -1;
/* */
va->hw.decoder = va->decoder;
va->hw.cfg = &va->cfg;
va->hw.surface_count = va->surface_count;
va->hw.surface = va->hw_surface;
for (i = 0; i < va->surface_count; i++)
va->hw.surface[i] = va->surface[i].d3d;
/* */
DxCreateVideoConversion(va);
/* */
ok:
*hw = &va->hw;
const d3d_format_t *output = D3dFindFormat(va->output);
//*chroma = output->codec;
return 0;
}
또한 환경 변수에 대해 할당 FFmpeg를 디코딩 하드웨어 솔루션 방식을 규정
if(is->iUseDxva)
{
pCodecCtx->get_buffer = DxGetFrameBuf;
pCodecCtx->reget_buffer = DxReGetFrameBuf;
pCodecCtx->release_buffer = DxReleaseFrameBuf;
pCodecCtx->opaque = NULL;
//하드웨어 솔루션의 필요성 여부
if(pCodecCtx->codec_id == CODEC_ID_MPEG1VIDEO || pCodecCtx->codec_id == CODEC_ID_MPEG2VIDEO ||
//avctx->codec_id == CODEC_ID_MPEG4 ||
pCodecCtx->codec_id == CODEC_ID_H264 ||
pCodecCtx->codec_id == CODEC_ID_VC1 || pCodecCtx->codec_id == CODEC_ID_WMV3)
{
pCodecCtx->get_format = DxGetFormat;
}
D3DXSaveSurfaceToFile = NULL;
hdll = LoadLibrary(TEXT("D3DX9_42.DLL"));
if(hdll)
D3DXSaveSurfaceToFile = (void *)GetProcAddress(hdll,TEXT("D3DXSaveSurfaceToFileA"));
}
FFmpeg 인터페이스 구현와 코드 dxva2 하드웨어 솔루션
/*****************************************************************************
* va.h: Video Acceleration API for avcodec
*****************************************************************************
* Copyright (C) 2012 tuyuandong
*
* Authors: Yuandong Tu <tuyuandong@gmail.com>
*
* This file is part of FFmpeg.
*****************************************************************************/
#include <libavutil/pixfmt.h>
#include <libavutil/pixdesc.h>
#include <libavcodec/avcodec.h>
#include <va.h>
enum PixelFormat DxGetFormat( AVCodecContext *avctx,
const enum PixelFormat *pi_fmt )
{
unsigned int i;
dxva_t *p_va = (dxva_t*)avctx->opaque;
if( p_va != NULL )
dxva_Delete( p_va );
p_va = dxva_New(avctx->codec_id);
if(p_va != NULL )
{
/* Try too look for a supported hw acceleration */
for(i = 0; pi_fmt[i] != PIX_FMT_NONE; i++ )
{
const char *name = av_get_pix_fmt_name(pi_fmt[i]);
av_log(NULL,AV_LOG_DEBUG, "Available decoder output format %d (%s)",
pi_fmt[i], name ? name : "unknown" );
if( p_va->pix_fmt != pi_fmt[i] )
continue;
/* We try to call dxva_Setup when possible to detect errors when
* possible (later is too late) */
if( avctx->width > 0 && avctx->height > 0
&& dxva_Setup(p_va, &avctx->hwaccel_context,avctx) )
{
av_log(NULL,AV_LOG_ERROR, "acceleration setup failure" );
break;
}
//if( p_va->description )
// av_log(NULL,AV_LOG_INFO, "Using %s for hardware decoding.",
// p_va->description );
/* FIXME this will disable direct rendering
* even if a new pixel format is renegotiated
*/
//p_sys->b_direct_rendering = false;
//p_sys->p_va = p_va;
avctx->opaque = p_va;
avctx->draw_horiz_band = NULL;
return pi_fmt[i];
}
av_log(NULL,AV_LOG_ERROR, "acceleration not available" );
dxva_Delete( p_va );
}
avctx->opaque = NULL;
/* Fallback to default behaviour */
return avcodec_default_get_format(avctx, pi_fmt );
}
/*****************************************************************************
* DxGetFrameBuf: callback used by ffmpeg to get a frame buffer.
*****************************************************************************
* It is used for direct rendering as well as to get the right PTS for each
* decoded picture (even in indirect rendering mode).
*****************************************************************************/
int DxGetFrameBuf( struct AVCodecContext *avctx,
AVFrame *pic )
{
dxva_t *p_va = (dxva_t *)avctx->opaque;
//picture_t *p_pic;
/* */
pic->reordered_opaque = avctx->reordered_opaque;
pic->opaque = NULL;
if(p_va)
{
/* hwaccel_context is not present in old ffmpeg version */
if( dxva_Setup(p_va,&avctx->hwaccel_context, avctx) )
{
av_log(NULL,AV_LOG_ERROR, "vlc_va_Setup failed" );
return -1;
}
/* */
pic->type = FF_BUFFER_TYPE_USER;
#if LIBAVCODEC_VERSION_MAJOR < 54
pic->age = 256*256*256*64;
#endif
if(dxva_Get(p_va,pic ) )
{
av_log(NULL,AV_LOG_ERROR, "VaGrabSurface failed" );
return -1;
}
return 0;
}
return avcodec_default_get_buffer(avctx,pic );
}
int DxReGetFrameBuf( struct AVCodecContext *avctx, AVFrame *pic )
{
pic->reordered_opaque = avctx->reordered_opaque;
/* We always use default reget function, it works perfectly fine */
return avcodec_default_reget_buffer(avctx, pic );
}
void DxReleaseFrameBuf( struct AVCodecContext *avctx,
AVFrame *pic )
{
dxva_t *p_va = (dxva_t *)avctx->opaque;
int i;
if(p_va )
{
dxva_Release(p_va, pic );
}
else if( !pic->opaque )
{
/* We can end up here without the AVFrame being allocated by
* avcodec_default_get_buffer() if VA is used and the frame is
* released when the decoder is closed
*/
if( pic->type == FF_BUFFER_TYPE_INTERNAL )
avcodec_default_release_buffer( avctx,pic );
}
else
{
//picture_t *p_pic = (picture_t*)p_ff_pic->opaque;
//decoder_UnlinkPicture( p_dec, p_pic );
av_log(NULL,AV_LOG_ERROR,"%d %s a error is rasied\e\n");
}
for(i = 0; i < 4; i++ )
pic->data[i] = NULL;
}
int DxPictureCopy(struct AVCodecContext *avctx,AVFrame *src, AVFrame* dst)
{
dxva_t *p_va = (dxva_t *)avctx->opaque;
return dxva_Extract(p_va,src,dst);
}
/*****************************************************************************
* OnlyVideo.c: Video Decode Test
*****************************************************************************
* Copyright (C) 2012 tuyuandong
*
* Authors: Yuandong Tu <tuyuandong@gmail.com>
*
* Data 2012-11-28
*
* This file is part of FFmpeg.
*****************************************************************************/
#include <windows.h>
#include <d3d9.h>
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "va.h"
typedef struct VideoState {
int abort_request;
char filename[1024];
int srcWidth,srcHeight;
int iUseDxva;
}VideoState;
char out_path[128];
void help();
DWORD VideoDecodec(LPVOID lpParameter);
int main(int argc, char *argv[])
{
VideoState is;
int i;
if(argc <2 || (argc == 2 && !strcmp(argv[1],"--help")))
{
help();
return 0;
}
memset(&is,0,sizeof(is));
memset(out_path,0,sizeof(out_path));
sprintf(is.filename,"%s",argv[1]);
if(argc > 2 && !strcmp(argv[2],"-dxva"))
is.iUseDxva = 1;
if(argc >4 && !strcmp(argv[3],"-o"))
strcpy(out_path,argv[4]);
if(out_path[0])
CreateDirectory(out_path,NULL);
VideoDecodec(&is);
return 0;
}
void help()
{
printf("**********************************************\n");
printf("Usage:\n");
printf(" OnlyVideo file [options]\n");
printf("\n");
printf("Options: \n");
printf(" -dxva \n");
printf(" -o out_path\n");
printf("\n");
printf("Examples: \n");
printf(" OnlyVideo a.mpg -dxva\n");
printf("**********************************************\n");
}
void console_log(const char *fmt, ...)
{
HANDLE h;
va_list vl;
va_start(vl, fmt);
char buf[1024];
memset(buf,0,1024);
vsprintf(buf,fmt,vl);
h = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
ULONG unuse;
pos.X= 0;
CONSOLE_SCREEN_BUFFER_INFO bInfo;
GetConsoleScreenBufferInfo(h, &bInfo );
pos.Y= bInfo.dwCursorPosition.Y;
WriteConsoleOutputCharacter(h,buf,strlen(buf),pos,&unuse);
va_end(vl);
}
typedef enum _D3DXIMAGE_FILEFORMAT
{
D3DXIFF_BMP = 0,
D3DXIFF_JPG = 1,
D3DXIFF_TGA = 2,
D3DXIFF_PNG = 3,
D3DXIFF_DDS = 4,
D3DXIFF_PPM = 5,
D3DXIFF_DIB = 6,
D3DXIFF_HDR = 7, //high dynamic range formats
D3DXIFF_PFM = 8, //
D3DXIFF_FORCE_DWORD = 0x7fffffff
} D3DXIMAGE_FILEFORMAT;
void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) {
FILE *pFile;
char szFilename[32];
int y;
// Open file
if(out_path[0])
sprintf(szFilename, ".\\%s\\frame%d.ppm",out_path, iFrame);
else
sprintf(szFilename, ".\\frame%d.ppm", iFrame);
pFile=fopen(szFilename, "wb");
if(pFile==NULL)
return;
// Write header
fprintf(pFile, "P6\n%d %d\n255\n", width, height);
// Write pixel data
for(y=0; y<height; y++)
fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);
// Close file
fclose(pFile);
}
DWORD VideoDecodec(LPVOID lpParameter)
{
DWORD (*D3DXSaveSurfaceToFile)(
char* pDestFile,
D3DXIMAGE_FILEFORMAT DestFormat,
LPDIRECT3DSURFACE9 pSrcSurface,
CONST PALETTEENTRY* pSrcPalette,
CONST RECT* pSrcRect);
VideoState* is = lpParameter;
AVFormatContext *pFormatCtx;
int i, videoStream;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame;
AVFrame *pFrameYUV = NULL;
char* pYUVBuf =NULL;
AVPacket pkt;
int frames;
DWORD dwTicks,cost;
DWORD sec;
HINSTANCE hdll = NULL;
// Register all formats and codecs
av_register_all();
// Open video file
if(av_open_input_file(&pFormatCtx, is->filename, NULL, 0, NULL)!=0)
return -1; // Couldn't open file
// Retrieve stream information
//if(av_find_stream_info(pFormatCtx)<0)
// return -1; // Couldn't find stream information
// Dump information about file onto standard error
dump_format(pFormatCtx, 0, is->filename, 0);
// Find the first video stream
videoStream=-1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{
videoStream=i;
break;
}
if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream
pCodecCtx=pFormatCtx->streams[videoStream]->codec;
pCodec=avcodec_find_encoder(CODEC_ID_H264);
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
if(pCodec==NULL) {
fprintf(stderr, "Unsupported codec!\n");
return -1; // Codec not found
}
if(is->iUseDxva)
{
pCodecCtx->get_buffer = DxGetFrameBuf;
pCodecCtx->reget_buffer = DxReGetFrameBuf;
pCodecCtx->release_buffer = DxReleaseFrameBuf;
pCodecCtx->opaque = NULL;
//是否为需要硬解
if(pCodecCtx->codec_id == CODEC_ID_MPEG1VIDEO || pCodecCtx->codec_id == CODEC_ID_MPEG2VIDEO ||
//avctx->codec_id == CODEC_ID_MPEG4 ||
pCodecCtx->codec_id == CODEC_ID_H264 ||
pCodecCtx->codec_id == CODEC_ID_VC1 || pCodecCtx->codec_id == CODEC_ID_WMV3)
{
pCodecCtx->get_format = DxGetFormat;
}
D3DXSaveSurfaceToFile = NULL;
hdll = LoadLibrary(TEXT("D3DX9_42.DLL"));
if(hdll)
D3DXSaveSurfaceToFile = (void *)GetProcAddress(hdll,TEXT("D3DXSaveSurfaceToFileA"));
}
// Open codec
if(avcodec_open(pCodecCtx, pCodec)<0)
return -1; // Could not open codec
// Allocate video frame
pFrame=avcodec_alloc_frame();
pFrameYUV = avcodec_alloc_frame();
if(pFrameYUV && is->iUseDxva)
{
int numBytes=avpicture_get_size(PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height);
pYUVBuf=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));
pFrameYUV->width = pCodecCtx->width;
pFrameYUV->height =pCodecCtx->height;
avpicture_fill((AVPicture *)pFrameYUV, pYUVBuf, PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height);
}
frames=0;
cost = 0;
sec = 0;
while(!is->abort_request && av_read_frame(pFormatCtx, &pkt)>=0) {
// Is this a packet from the video stream?
if(pkt.stream_index==videoStream) {
int got_picture;
// Decode video frame
dwTicks = GetTickCount();
avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,&pkt);
cost += (GetTickCount() - dwTicks);
// Did we get a video frame?
if(got_picture) {
frames++;
if(cost/1000 > sec)
{
float speeds= (frames*1000.0)/cost;
console_log("pic_type[%d] frames[%d] times[%d] speeds[%f fps]\n",
pFrame->format,frames,cost,speeds);
sec ++;
if(pFrame->format == PIX_FMT_DXVA2_VLD )
{
//char filename[256] = {0};
//DWORD ret = 0;
//LPDIRECT3DSURFACE9 d3d = pFrame->data[3];
//sprintf(filename,"e://%d.jpg",frames);
//if(D3DXSaveSurfaceToFile)
// ret = D3DXSaveSurfaceToFile(filename,D3DXIFF_JPG/*jpeg*/,d3d,NULL,NULL);
//filename[128] = 0;
DxPictureCopy(pCodecCtx,pFrame,pFrameYUV);
SaveFrame(pFrameYUV,pFrameYUV->width,pFrameYUV->height,frames);
}
}
}
}
// Free the packet that was allocated by av_read_frame
av_free_packet(&pkt);
}
avcodec_close(pCodecCtx);
//av_close_input_file(pFormatCtx);
if(pYUVBuf)
av_free(pYUVBuf);
if(pFrame)
av_free(pFrame);
if(pFrameYUV)
av_free(pFrameYUV);
if(hdll)
FreeLibrary(hdll);
}
http://download.csdn.net/detail/tttyd/5166123 http://pan.baidu.com/share/link?shareid=703510495&uk=3240542740