ffmpeg4教程12:intel media sdk(qsv)硬解码的使用方法+qt5 openggl显示AV_PIX_FMT_NV12

讨论群261074724

1.安装intel media sdk 请对于处理器的版本代号

2.参考ffmpeg官方的examples下的 qsvdec.c改造

如果差#include <mfx/mfxvideo.h>头文件需要将 文件下的include 重命名为mfx 考到工程

 

主要加了一个转换城rgb24的窗口显示


#include "pch.h"
#include <iostream>
#include <opencv2/core/utility.hpp> 
#include <opencv2/opencv.hpp>
#include <windows.h>

extern "C" {
#include "libavcodec/avcodec.h" 
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavdevice/avdevice.h"
#include "libavutil/imgutils.h"
#include "libavutil/audio_fifo.h"  
#include "libavutil/time.h"
#include "libavutil/mathematics.h"
#include "libavutil/channel_layout.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavutil/opt.h"  

#include "libavutil/mem.h"
#include "libavutil/buffer.h"
#include "libavutil/error.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_qsv.h"
#define HAVE_STRUCT_TIMESPEC
#include "pthread.h" 
}
 
typedef struct DecodeContext {
    AVBufferRef *hw_device_ref;
} DecodeContext;
static AVPixelFormat get_format(AVCodecContext *avctx, const enum AVPixelFormat *pix_fmts)
{
    while (*pix_fmts != AV_PIX_FMT_NONE) {
        if (*pix_fmts == AV_PIX_FMT_QSV) {
            DecodeContext *decode =(DecodeContext *) avctx->opaque;
            AVHWFramesContext  *frames_ctx;
            AVQSVFramesContext *frames_hwctx;
            int ret;

            /* create a pool of surfaces to be used by the decoder */
            avctx->hw_frames_ctx = av_hwframe_ctx_alloc(decode->hw_device_ref);
            if (!avctx->hw_frames_ctx)
                return AV_PIX_FMT_NONE;
            frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
            frames_hwctx =(AVQSVFramesContext *) frames_ctx->hwctx;

            frames_ctx->format = AV_PIX_FMT_QSV;
            frames_ctx->sw_format = avctx->sw_pix_fmt;
            frames_ctx->width = FFALIGN(avctx->coded_width, 32);
            frames_ctx->height = FFALIGN(avctx->coded_height, 32);
            frames_ctx->initial_pool_size = 32;

            frames_hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET;

            ret = av_hwframe_ctx_init(avctx->hw_frames_ctx);
            if (ret < 0)
                return AV_PIX_FMT_NONE;

            return AV_PIX_FMT_QSV;
        }

        pix_fmts++;
    }

    fprintf(stderr, "The QSV pixel format not offered in get_format()\n");

    return AV_PIX_FMT_NONE;
}
static void Show(HWND hwnd, unsigned char* rgb, int w, int h, bool fill)
{
    HDC hdc = GetDC(hwnd);//获取当前的显示设备上下文

    RECT rect;
    GetClientRect(hwnd, &rect);
    int cxClient = rect.right;
    int cyClient = rect.bottom;

    if (cxClient <= 0 || cyClient <= 0) {
        return;
    }

    HDC  hdcsource = CreateCompatibleDC(NULL);//创建存放图象的显示缓冲
    HBITMAP bitmap = CreateCompatibleBitmap(hdc, cxClient, cyClient);

    SelectObject(hdcsource, bitmap);    //将位图资源装入显示缓冲


    SetStretchBltMode(hdcsource, COLORONCOLOR);

    BITMAPINFO  bmi;
    bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmi.bmiHeader.biWidth = w;
    bmi.bmiHeader.biHeight = -h;
    bmi.bmiHeader.biCompression = BI_RGB;
    bmi.bmiHeader.biBitCount = 24;
    bmi.bmiHeader.biPlanes = 1;
    bmi.bmiHeader.biClrUsed = 0;
    bmi.bmiHeader.biClrImportant = 0;
    bmi.bmiHeader.biSizeImage = 0;


    if (!fill) {

        int des_x = 0;
        int des_y = 0;
        int des_w = 0;
        int des_h = 0;


        if (1.0*cxClient / cyClient > 1.0*w / h) {
            des_h = cyClient;
            des_w = des_h * w / h;
            des_x = (cxClient - des_w) / 2;
            des_y = 0;
        }
        else {
            des_w = cxClient;
            des_h = des_w * h / w;
            des_x = 0;
            des_y = (cyClient - des_h) / 2;
        }


        BitBlt(hdcsource, 0, 0, cxClient, cyClient, hdcsource, 0, 0, SRCCOPY);
        StretchDIBits(hdcsource, des_x, des_y, des_w, des_h, \
            0, 0, w, h, rgb, &bmi, DIB_RGB_COLORS, SRCCOPY);
        BitBlt(hdc, 0, 0, cxClient, cyClient, hdcsource, 0, 0, SRCCOPY);
    }
    else {
        StretchDIBits(hdcsource, 0, 0, rect.right - rect.left, rect.bottom - rect.top, \
            0, 0, w, h, rgb, &bmi, DIB_RGB_COLORS, SRCCOPY);

        BitBlt(hdc, 0, 0, cxClient, cyClient, hdcsource, 0, 0, SRCCOPY);//将图象显示缓冲的内容直接显示到屏幕
    }

    DeleteObject(bitmap);
    DeleteDC(hdcsource);
    ReleaseDC(hwnd, hdc);
}

LRESULT CALLBACK WinProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)

    switch (umsg)
    {
     
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, umsg, wparam, lparam);
}  


static int decode_packet(DecodeContext *decode, AVCodecContext *decoder_ctx,
    AVFrame *frame, AVFrame *sw_frame,
    AVPacket *pkt, AVIOContext *output_ctx,HWND hwnd , SwsContext *img_convert_ctx, AVFrame *bgrFrame)
{
    int ret = 0;
    ret = avcodec_send_packet(decoder_ctx, pkt);
    if (ret < 0) {
        fprintf(stderr, "Error during decoding\n");
        return ret;
    }
    while (ret >= 0) {
        int i, j;
        ret = avcodec_receive_frame(decoder_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            break;
        else if (ret < 0) {
            fprintf(stderr, "Error during decoding\n");
            return ret;
        } 
         
        /* AV_PIX_FMT_QSV到 AV_PIX_FMT_NV12*/ 
        ret = av_hwframe_transfer_data(sw_frame, frame, 0);
        if (ret < 0) {
            fprintf(stderr, "Error transferring the data to system memory\n");
            goto fail;
        }    
        sws_scale(img_convert_ctx, (const unsigned char* const*)sw_frame->data, sw_frame->linesize, 0, sw_frame->height, bgrFrame->data, bgrFrame->linesize);
         
        Show(hwnd, bgrFrame->data[0], bgrFrame->width, bgrFrame->height, true);
        
         
    fail:
        av_frame_unref(sw_frame);
        av_frame_unref(frame);
        if (ret < 0)
            return ret;
    }
    return 0;
}

static AVFrame *alloc_picture(enum AVPixelFormat pix_fmt, int width, int height)
{
    AVFrame *picture;
    int ret;

    picture = av_frame_alloc();
    if (!picture)
        return NULL;

    picture->format = pix_fmt;
    picture->width = width;
    picture->height = height;

    /* allocate the buffers for the frame data */
    ret = av_frame_get_buffer(picture, 4);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate frame data.\n");
        return NULL;
    }

    return picture;
}
void *VideoReadThread1(void*p) { 
    HWND hwnd =(HWND) p;
    int ret = 0;
    const char*url = "F:\\source\\ffmpge\\mux\\1.mp4";
    AVFormatContext* pInputFormatCtx = avformat_alloc_context();
    AVStream *video_st = NULL;

    DecodeContext decode = { NULL };
    const AVCodec *decoder;
    AVCodecContext *decoder_ctx = NULL;
    AVPacket pkt = { 0 }; av_init_packet(&pkt);
    if ((ret = avformat_open_input(&pInputFormatCtx, url, NULL, NULL)) < 0) {
        printf("Could not open input file.");
        return 0;
    }
    if ((ret = avformat_find_stream_info(pInputFormatCtx, 0)) < 0) {
        printf("Failed to retrieve input stream information");
        return 0;
    }
     
    /* find the first H.264 video stream */
    for (int i = 0; i < pInputFormatCtx->nb_streams; i++) {
        AVStream *st = pInputFormatCtx->streams[i];
        if (st->codecpar->codec_id == AV_CODEC_ID_H264 && !video_st)
        { 
            video_st = st;
        }
        else
            st->discard = AVDISCARD_ALL;
    }
 
    AVCodecContext *envideocodecCtx = NULL;
    AVIOContext *output_ctx = NULL;

    AVFrame *frame = NULL, *sw_frame = NULL;

    SwsContext *img_convert_ctx = sws_getContext(pInputFormatCtx->streams[video_st->index]->codecpar->width, pInputFormatCtx->streams[video_st->index]->codecpar->height,
        AV_PIX_FMT_NV12, pInputFormatCtx->streams[video_st->index]->codecpar->width, pInputFormatCtx->streams[video_st->index]->codecpar->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);
    AVFrame *bgrFrame = alloc_picture(AV_PIX_FMT_BGR24, pInputFormatCtx->streams[video_st->index]->codecpar->width, pInputFormatCtx->streams[video_st->index]->codecpar->height);
    

    if (!video_st) {
        fprintf(stderr, "No H.264 video stream in the input file\n");
        goto finish;
    }
    /* open the hardware device */
    ret = av_hwdevice_ctx_create(&decode.hw_device_ref, AV_HWDEVICE_TYPE_QSV, "auto", NULL, 0);
    if (ret < 0) {
        fprintf(stderr, "Cannot open the hardware device\n");
        goto finish;
    }
    /* initialize the decoder */
    decoder = avcodec_find_decoder_by_name("h264_qsv");
    if (!decoder) {
        fprintf(stderr, "The QSV decoder is not present in libavcodec\n");
        goto finish;
    }
    decoder_ctx = avcodec_alloc_context3(decoder);
    if (!decoder_ctx) {
        ret = AVERROR(ENOMEM);
        goto finish;
    }
    decoder_ctx->codec_id = AV_CODEC_ID_H264;
    if (video_st->codecpar->extradata_size) {
        decoder_ctx->extradata = (uint8_t *)av_mallocz(video_st->codecpar->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
        if (!decoder_ctx->extradata) {
            ret = AVERROR(ENOMEM);
            goto finish;
        }
        memcpy(decoder_ctx->extradata, video_st->codecpar->extradata,
            video_st->codecpar->extradata_size);
        decoder_ctx->extradata_size = video_st->codecpar->extradata_size;
    }

    decoder_ctx->opaque = &decode;
    decoder_ctx->get_format = get_format;

    ret = avcodec_open2(decoder_ctx, NULL, NULL);
    if (ret < 0) {
        fprintf(stderr, "Error opening the decoder: ");
        goto finish;
    }
    av_dump_format(pInputFormatCtx, 0, url, 0);
     
    frame = av_frame_alloc();
    sw_frame = av_frame_alloc();
    if (!frame || !sw_frame) {
        ret = AVERROR(ENOMEM);
        goto finish;
    }


    time_t tt;//这句返回的只是一个时间cuo
    struct tm t;
    time(&tt);
    localtime_s(&t, &tt);
    printf("%d-%02d-%02d %02d:%02d:%02d\n",
        t.tm_year + 1900,
        t.tm_mon + 1,
        t.tm_mday,
        t.tm_hour,
        t.tm_min,
        t.tm_sec);

    
    /* actual decoding */ 
        while (ret >= 0) {
            ret = av_read_frame(pInputFormatCtx, &pkt);
            if (ret < 0)
                break;
            if (pkt.stream_index == video_st->index)
                ret = decode_packet(&decode, decoder_ctx, frame, sw_frame, &pkt, output_ctx, hwnd, img_convert_ctx, bgrFrame);
            av_packet_unref(&pkt);
        }
        /* flush the decoder */


        time_t tt1;//这句返回的只是一个时间cuo
        struct tm t1;
        time(&tt1);
        localtime_s(&t1, &tt1);
        printf("%d-%02d-%02d %02d:%02d:%02d\n",
            t1.tm_year + 1900,
            t1.tm_mon + 1,
            t1.tm_mday,
            t1.tm_hour,
            t1.tm_min,
            t1.tm_sec);
    finish:
        if (ret < 0) {
            char buf[1024];
            av_strerror(ret, buf, sizeof(buf));
            fprintf(stderr, "%s\n", buf);
        }

        avformat_close_input(&pInputFormatCtx);

        av_frame_free(&frame);
        av_frame_free(&sw_frame);

        avcodec_free_context(&decoder_ctx);

        av_buffer_unref(&decode.hw_device_ref);

        avio_close(output_ctx);
 
    return 0;
}

int main()
{
    HINSTANCE hInstance;
    hInstance = GetModuleHandle(NULL);
    WNDCLASSEX wce = { 0 };
    wce.cbSize = sizeof(wce);
    wce.cbClsExtra = 0;
    wce.cbWndExtra = 0;
    wce.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wce.hCursor = NULL;
    wce.hIcon = NULL;
    wce.hIconSm = NULL;
    wce.hInstance = hInstance;
    wce.lpfnWndProc = WinProc;
    wce.lpszClassName = L"Main";
    wce.lpszMenuName = NULL;
    wce.style = CS_HREDRAW | CS_VREDRAW;
    ATOM nAtom = RegisterClassEx(&wce);
    if (!nAtom)
    {
        MessageBox(NULL, L"RegisterClassEx失败", L"错误", MB_OK);
        return 0;
    }
    const char*  szStr1 = "Main";
    WCHAR wszClassName[256];
    memset(wszClassName, 0, sizeof(wszClassName));
    MultiByteToWideChar(CP_ACP, 0, szStr1, strlen(szStr1) + 1, wszClassName,
        sizeof(wszClassName) / sizeof(wszClassName[0]));
    HWND hwnd = CreateWindow(wszClassName, L"视频播放", WS_OVERLAPPEDWINDOW, 38, 20, 640, 480, NULL, NULL, hInstance, NULL);
    // 显示窗口  
    ShowWindow(hwnd, SW_SHOW);
    // 更新窗口  
    UpdateWindow(hwnd);

    //init

    pthread_t t1;
    pthread_create(&t1, NULL, VideoReadThread1, hwnd);

    // 消息循环  
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    } 
    return 0;

测试结果

88m 1280*720的硬解码 1分41s 内存250m左右
88m 1280*720的软解 1分31s 内存90m左右

如果在qt内可以不用转换之间用openggl显示nv12的 

 

 

讨论群261074724

资源下载链接为: https://pan.quark.cn/s/9ce3e35e0f39 在当今多媒体处理领域,硬件加速技术是提升性能和效率的核心手段之一。Intel Quick Sync Video(QSV)作为Intel处理器集成的硬件加速技术,专为视频编解码及转码设计。本Demo将展示如何借助Intel QSV对H.264视频流进行硬件解码,并通过SDL2库实现高效、低延迟的视频渲染与播放。该项目基于ffmpegqsvdec.c源代码进行修改,用于演示如何利用Intel QSV硬件解码H.264视频。在解码过程中,QSV可充分调用CPU集成的图形单元,将复杂的视频解码任务从CPU转移到硬件层面,从而减轻CPU负担,提高系统资源利用率。同时,借助SDL2库,可将解码后的视频帧渲染到屏幕上,提供流畅的视觉体验。 Intel Quick Sync Video(QSV):QSVIntel处理器的一项特性,利用Intel Media Engine加速视频编解码。Media Engine由Intel Gen Graphics和Media Processing Units组成,共同为视频处理提供硬件加速能力,尤其在处理高清和4K视频时,性能优势明显。 H.264解码:H.264(又称AVC)是一种高效视频编码标准,广泛应用于网络流媒体、蓝光光盘和数字电视等领域。硬件解码H.264意味着视频数据在到达CPU之前,已在GPU或特定硬件单元中完成解析,从而降低CPU计算压力。 ffmpegffmpeg是一个开源的多媒体处理工具,支持多种音视频格式的编码、解码、转换和流处理。在本示例中,它作为底层库,提供与Intel QSV接口交互的API,用于执行H.264的硬件解码。 qsvdec.c:这是ffmpeg源代码的一部分,负责实现QSV解码功能。开发者在此基础上进行修改,以适应特定应
### 配置FFmpeg以支持AV_PIX_FMT_NV12像素格式进行编码 为了使FFmpeg能够处理并转换成`AV_PIX_FMT_NV12`像素格式,通常需要确保编译时包含了必要的硬件加速选项和支持特定的解码器/编码器。对于Android平台上的H.264编码而言,可以利用MediaCodec来实现NV21到I420再到NV12的转换过程。 #### 编译带有NV12支持的FFmpeg库 当针对Android环境构建FFmpeg时,可以通过指定额外的配置参数来启用对某些特性的支持: ```bash ./configure \ --target-os=android \ --arch=armv7-a \ --enable-cross-compile \ --cross-prefix=$NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi- \ --sysroot=$NDK_HOME/platforms/android-21/arch-arm \ --prefix=./android_build \ --disable-static \ --enable-shared \ --enable-small \ --enable-gpl \ --enable-libx264 \ --enable-hwaccel=h264_mediacodec \ --enable-decoder=h264_mediacodec \ --enable-encoder=h264_mediacodec \ --extra-cflags="-Os -fpic $ADDI_CFLAGS" \ --extra-ldflags="$ADDI_LDFLAGS" ``` 上述命令中的关键部分在于启用了`h264_mediacodec`硬件加速以及对应的解码器和编码器[^3]。这有助于提高性能,并允许更好地兼容不同设备上可用的媒体编解码组件。 #### 设置YUV数据源为YUV_420_888(即NV21) 在实际应用开发过程中,如果希望从相机捕获图像帧并将它们作为输入提供给FFmpeg进行进一步处理,则需要注意调整采集的数据格式。由于大多数移动设备默认返回的是NV21格式而非标准定义下的YUV420P或I420,在这种情况下可能需要先执行一次内部格式间的变换操作。 ```java Image image = reader.acquireLatestImage(); if (image != null && image.getFormat() == ImageFormat.YUV_420_888) { ByteBuffer yBuffer = image.getPlanes()[0].getBuffer(); // Y分量 ByteBuffer uBuffer = image.getPlanes()[1].getBuffer(); // U分量 ByteBuffer vBuffer = image.getPlanes()[2].getBuffer(); // V分量 byte[] data = new byte[yBuffer.remaining() + uBuffer.remaining() + vBuffer.remaining()]; int offset = 0; yBuffer.get(data, offset, yBuffer.capacity()); offset += yBuffer.capacity(); // NV21格式下U,V顺序颠倒 vBuffer.get(data, offset, vBuffer.capacity()); offset += vBuffer.capacity(); uBuffer.get(data, offset, uBuffer.capacity()); // 将data传递给FFmpeg或其他视频处理器... } ``` 这段Java代码展示了如何读取来自CameraX API所获得的一张图片,并将其转化为适合后续使用的字节数组形式。这里特别注意到了NV21格式的特点——V平面位于U平面前面这一点。 #### 获取真实的摄像机分辨率 考虑到并非所有的请求都能得到完全匹配的结果,因此建议查询当前活动预览会话的实际尺寸,而不是单纯依赖于应用程序设定的目标大小。通过这种方式可以获得更精确的信息用于初始化编码流程。 ```cpp // C/C++伪代码片段 int width = /* 实际宽度 */; int height = /* 实际高度 */; AVCodecContext *codec_context = avcodec_alloc_context3(codec); if (!codec_context) { fprintf(stderr, "Could not allocate video codec context\n"); exit(1); } codec_context->width = width; codec_context->height = height; codec_context->pix_fmt = AV_PIX_FMT_NV12; // 设定目标像素格式为NV12 ``` 此C/C++代码段说明了怎样根据已知的真实分辨率创建一个新的编解码上下文实例,并指定了所需的输出像素格式为`AV_PIX_FMT_NV12`。
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值