运行时不一致DXVA硬件视频解码

时间:2015-08-26 05:38:33

标签: c++ ffmpeg h.264 decoding dxva

我目前正在开发一个涉及使用DXVA API和FFmpeg框架实现H264视频流文件的硬件加速解码的项目。

我已经对GPU解码做了一些研究,并基于VLC中的硬件加速实现构建了我的代码。根据我的理解,在FFmpeg中使用DXVA涉及初始化DirectXVideoDecoder并在AVCodecContext中实现多个回调函数。使用FFmpeg函数 avcodec_decode_video2()完成解码过程,并使用 av_read_frame()解析每个帧。解码后的帧存储在图形存储器中,并使用Direct3D显示。

我尝试使用:GetTickCount()函数计算每个进程的时间,并注意到1550帧视频的程序执行时间为35000ms,显示功能占用了90%的时间,解码功能占用了6%的时间。

然而,当我试图评论显示过程并执行仅解码每一帧的代码时,同一视频的总解码时间意外增加至25,000ms,占总数的94%时间。 这是解码功能的代码:

//record start time 
DWORD start_time = ::GetTickCount();
//media file to be loaded
const char *filename = "123.mkv";
//time recording parameters
unsigned frame_read_time_total = 0;
unsigned decode_frame_time_total = 0;
unsigned display_time_total = 0;
unsigned setup_time_total = 0;

/*********************Setup and Initialization Code*******************************/
unsigned setup_time_start = ::GetTickCount();
av_register_all();
av_log_set_level(AV_LOG_DEBUG);
int res;
AVFormatContext *file = NULL;
res = avformat_open_input(&file, filename, NULL, NULL);//´ò¿ªÎļþ
if (res < 0) {
    printf("error %x in avformat_open_input\n", res);
    return 1;
}
res = avformat_find_stream_info(file, NULL);//È¡³öÁ÷ÐÅÏ¢
if (res < 0)
{
    printf("error %x in avformat_find_stream_info\n", res);
    return 1;
}
av_dump_format(file, 0, filename, 0);//ÁгöÊäÈëÎļþµÄÏà¹ØÁ÷ÐÅÏ¢
int i;
int videoindex = -1;
int audioindex = -1;
for (i = 0; i < file->nb_streams; i++){
    if (file->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
        videoindex = i;
    }
    if (file->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){
        audioindex = i;
    }
}
if (videoindex == -1){
    av_log(NULL, AV_LOG_DEBUG, "can't find video stream\n");
    return 0;

}
AVCodec *codec = avcodec_find_decoder(file->streams[videoindex]->codec->codec_id);//¸ù¾ÝÁ÷ÐÅÏ¢ÕÒµ½½âÂëÆ÷
if (!codec){
    printf("decoder not found\n");
    return 1;
}
AVCodecContext *codecctx = file->streams[videoindex]->codec;
screen_width = codecctx->width;
screen_height = codecctx->height;
//Initialize Win API Window 
WNDCLASSEX window;
ZeroMemory(&window, sizeof(window));
window.cbSize = sizeof(window);
window.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
window.lpfnWndProc = (WNDPROC)WindowProcess;
window.lpszClassName = L"D3D";
window.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&window);
HWND hwnd_temp = CreateWindow(L"D3D", L"Player", WS_OVERLAPPEDWINDOW,
    0, 0, screen_width, screen_height, NULL, NULL, NULL, NULL);
if (hwnd_temp == NULL){
    av_log(NULL, AV_LOG_ERROR, "Error: Cannot create window\n");
    system("pause");
}
hwnd.push_back(hwnd_temp);
vlc_va_dxva2_t *dxva = vlc_va_NewDxva2(codecctx->codec_id);
if (NULL == dxva){
    return 0;
}
res = Setup(dxva, &codecctx->hwaccel_context, &codecctx->pix_fmt, screen_width, screen_height);
if (res < 0) {
    printf("error DXVA setup\n", res);
    return 1;
}
//Assign callback function 
codecctx->opaque = dxva;
codecctx->get_format = ffmpeg_GetFormat;
codecctx->get_buffer = ffmpeg_GetFrameBuf;
codecctx->reget_buffer = ffmpeg_ReGetFrameBuf;
codecctx->release_buffer = ffmpeg_ReleaseFrameBuf;
codecctx->thread_count = 1;
res = avcodec_open2(codecctx, codec, NULL);
if (res < 0) {
    printf("error %x in avcodec_open2\n", res);
    return 1;
}
//Initialize Packet
AVPacket pkt = { 0 };
AVFrame *picture = avcodec_alloc_frame();
DWORD wait_for_keyframe = 60;
//initialize frame count
int count = 0;
ShowWindow(hwnd.at(0), SW_SHOWNORMAL);
UpdateWindow(hwnd.at(0));
RECT screen_size;
screen_size.top = 0;
screen_size.bottom = screen_height;
screen_size.left = 0;
screen_size.right = screen_width;

unsigned setup_time_end = ::GetTickCount();
setup_time_total = setup_time_end - setup_time_start;

MSG msg;
ZeroMemory(&msg, sizeof(msg));
while(msg.message!=WM_QUIT)
{
    if (PeekMessage(&msg, NULL, 0,0, PM_REMOVE)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        continue;
    }
        int read_status;
        unsigned read_frame_start = ::GetTickCount();
        read_status = av_read_frame(file, &pkt);
        if (read_status < 0)
        {
            av_free_packet(&pkt);
            goto done;
        }

        unsigned read_frame_end = ::GetTickCount();
        frame_read_time_total += (read_frame_end - read_frame_start);
        int got_picture = 0;
        unsigned decode_start = ::GetTickCount();
        int bytes_used = avcodec_decode_video2(codecctx, picture, &got_picture, &pkt);
        unsigned decode_end = ::GetTickCount();
        decode_frame_time_total += (decode_end - decode_start);
        if (got_picture)
        {
            count++;
            unsigned display_start = ::GetTickCount();
            //display_frame((vlc_va_dxva2_t *)codecctx->opaque, picture, screen_size,0);
            unsigned display_end = ::GetTickCount();
            display_time_total += (display_end - display_start);
        }
        av_free_packet(&pkt);
}
done:
UnregisterClass(L"D3D",0);
printf("Frames = %d\n",count);
unsigned stop_time = ::GetTickCount();
unsigned total_time = stop_time - start_time;
printf("total frame = %d\n", count);
printf("time cost = %d\n", total_time);
printf("total setup time = %d, %f %% total execution time\n", setup_time_total,(float) setup_time_total / total_time * 100);
printf("total frame read time = %d, %f %% total execution time\n", frame_read_time_total, (float)frame_read_time_total / total_time*100);
printf("total frame decode time = %d, %f %% total execution time\n", decode_frame_time_total, (float)decode_frame_time_total / total_time*100);
printf("total display time = %d, %f %% of total execution time\n", display_time_total, (float)display_time_total / total_time*100);

av_free(picture);
av_close_input_file(file);
system("pause");
return 0;

这种奇怪行为的原因是什么?我的猜测是,可能是错误使用:GetTickCount(),或者可能与DXVA硬件加速解码过程有关。对不起,很长的帖子。任何意见和建议表示赞赏。提前谢谢。

1 个答案:

答案 0 :(得分:0)

如果解码过程是异步的,我认为这是正确的行为。我知道Ffmpeg使用线程,但它依赖于编译标志或解码设置。

如果显示过程很长,则解码器解码帧,同时执行显示过程。因此,当您要求渲染时,某些帧已经被解码,并且速度很快。

如果您避开显示过程,则解码过程将占用所有处理器时间。通常,显示过程使用某种时间戳,以便有足够的时间进行解码过程。

PS:根据我对Ffmpeg和Dxva2的了解,您还需要提供directx纹理。