首页 > 编程学习 > Ubuntu18使用FFMPEG实现QSV硬解






前言

由于项目需要,需要在一块I7-8850H上进行H264解码成YUV并显示的功能。由于系统是Ubuntu18,故打算使用QT+FFMPEG来实现。先前的一路软解发现CPU占用率去到了20%以上,我们需要同时进行四路解码,这个占用率是无法接受的,故打算使用FFMPEG进行硬解。由于只有I7的集显,所以只能使用QSV。前面已经完成了环境的安装(具体教程在Ubuntu18上安装QSV+FFMPEG环境  ) ,此文章将展示如何在QT中实现ffmpeg的硬解,使用的库的路径全部基于安装教程里面的路径和版本。此教程假设观看者有一定的C++和QT开发经验。

使用的Qt Creator版本信息如下图


 






一、.pro文件的配置

在.pro文件添加includpath跟lib,之所以添加这么多,是因为有各自库之间有相互引用,添加少了虽然不报错,但是解不了码。

INCLUDEPATH += /opt/intel/mediasdk/include
INCLUDEPATH += /usr/local/include
INCLUDEPATH += /opt/intel/mediasdk/include/mfx
INCLUDEPATH +=/usr/include/x86_64-linux-gnu/LIBS +=  -Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavutil -Llibpostproc -Llibswscale -Llibswresample  -lavdevice
LIBS += -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil  -lm -lxcb -lxcb-shm -lxcb-shape -lxcb-xfixes -pthread -lm
LIBS += -L/usr/local/lib -lva -L/opt/intel/mediasdk/lib -lmfx -lstdc++ -ldl -lm -lbz2 -lz -pthread -lm -lz -L/usr/local/lib -lva
LIBS += -L/opt/intel/mediasdk/lib -lmfx -lstdc++ -ldl -lm -lm -pthread -L/usr/local/lib -lva-drm -lva -L/usr/local/lib -lva-x11 -lva -lm
LIBS += -L/opt/intel/mediasdk/lib -lmfx -lstdc++ -ldl -L/usr/local/lib -lva -lX11





二、具体代码实现

1、创建一个HW_H264Decoder

2、在.h文件里添加ffmpeg的头文件引用

#include <stdio.h>
#include <string>
#include <unistd.h>
extern "C" {
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"
#include "libavutil/buffer.h"
#include "libavutil/error.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_qsv.h"
#include "libavutil/mem.h"
#include "libavutil/imgutils.h"
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}

ffmpeg都是用C写的,连官方demo都是C,所以导致在C++中,include的时候需要用extern "C" 把ffmpeg的头文件引用给包起来,不然编译的时候会报错。

3、在.h文件里定义几个需要用到方法

    /***  初始化** @param width : 视频的宽度* @param height : 视频的高度** @return 错误代码  0:成功***/int init(int width,int height);/***  传入H264数据,并获取解码后的NV12数据  (QSV好像只能解成NV12)** @param h264Data : H264数据* @param size : H264数据的长度* @param nv12Data:存放NV12数据的缓冲** @return NV12数据的长度***/int decodeToNV12(uint8_t *h264Data,int size,unsigned char *nv12Data);/***  释放资源***/void relese();

4、在.h文件里面定义需要用到的类变量(个人习惯,可不在.h里定义)

  //解码需要用到的上下文 AVCodecContext *decoder_ctx = NULL;//解码器对象const AVCodec *decoder;//放H264数据包的对象AVPacket *pkt;//指向GPU内存的帧对象AVFrame *frame = NULL;//指向CPU内存的帧对象AVFrame *sw_frame = NULL;//解码使用的buffer指针AVBufferRef *device_ref = NULL; //存放sps的数组char sps[50] = {0x00,0x00,0x00,0x01,0x67,0x4d,0x00,0x1f,(char)0xe9,(char)0x80,(char)0xa0,0x0b,0x74,(char)0xa4,0x14,0x18,0x18,0x1b,0x42,(char)0x84,(char)0xa7};//存放PPS的数组char pps[50] = {0x00,0x00,0x00,0x01,0x68,(char)0xee,0x06,(char)0xe2};//sps的长度int spsLen=0;//pps的长度int ppsLen=0;

5、在.cpp文件中实现 init方法,实现对ffmpeg解码相关操作的初始化

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) {return AV_PIX_FMT_QSV;}pix_fmts++;}fprintf(stderr, "The QSV pixel format not offered in get_format()\n");return AV_PIX_FMT_NONE;
}int HW_H264Decoder::init(int width,int height){int ret;/* 打开QSV设备 */ret = av_hwdevice_ctx_create(&device_ref, AV_HWDEVICE_TYPE_QSV,"auto", NULL, 0);/* 检查下是否打开成功,失败则直接跳出去 */if (ret < 0) {fprintf(stderr, "Cannot open the hardware device\n");return -1;}/* 打开解码器,这里是使用qsv对H264进行硬解,故使用h264_qsv */decoder = avcodec_find_decoder_by_name("h264_qsv");/* 检查下是否打开成功,失败则直接跳出去 */if (!decoder) {fprintf(stderr, "The QSV decoder is not present in libavcodec\n");return -1;}/* 根据解码器初始化解码器的上下文 */decoder_ctx = avcodec_alloc_context3(decoder);/* 检查下是否初始化打开成功,失败则直接跳出去 */if (!decoder_ctx) {return -1;}/* 设置下解码类型 */decoder_ctx->codec_id = AV_CODEC_ID_H264;/* 开辟解码器使用的缓冲 */decoder_ctx->hw_device_ctx = av_buffer_ref(device_ref);/* 设置下QSV格式  之所以这么写,是为了确定是否有QSV */decoder_ctx->get_format  = get_format;/* 把解码器的上下文跟解码器绑定并初始化 */ret = avcodec_open2(decoder_ctx, decoder, NULL);/* 检查下是否初始化成功,失败则直接跳出去 */if (ret < 0) {fprintf(stderr, "Error opening the decoder: ");return -1;}/* 初始化用到的帧对象 */frame    = av_frame_alloc();sw_frame = av_frame_alloc();/* 初始化存放H264包的对象 */pkt = av_packet_alloc();/* 检查下是否初始化成功,失败则直接跳出去 */if (!frame || !sw_frame || !pkt) {ret = AVERROR(ENOMEM);return -1;}return 0;
}

5、在.cpp文件中实现decode方法,实现对传入H264数据后解码成NV12数据出来

int HW_H264Decoder::decodeToNV12(uint8_t *h264Data, int size, unsigned char *nv12Data){int ret = 0;/* 由于I帧需要在前面家SPSPPS 不然会出现无法解码的情况,故开辟个临时缓冲区 */uint8_t *h264DataTemp = NULL;/* 如果是SPS 则存起来,不喂给解码器,不然会导致解不出东西 */if(h264Data[4] == 0x67){memcpy(sps,h264Data,size);spsLen = size;return 0;}/* 如果是pps 则存起来,不喂给解码器,不然会导致解不出东西 */if(h264Data[4] == 0x68){memcpy(pps,h264Data,size);ppsLen = size;return 0;}/* 判断下是否是I帧  是I帧的话需要在前面加上spspps,不然会导致解不出东西 */if(h264Data[4] == 0x65){/* 现在是I帧,需要加上SPSPPS,故计算下加上spspps之后的长度是多少 */int spsppsIframeLen = size+spsLen+ppsLen;/* 开辟出需要的内存 */h264DataTemp = new uint8_t[spsppsIframeLen];/* 先复制SPS进去 */memcpy(h264DataTemp,sps,spsLen);/* 再复制PPS进去 */memcpy(h264DataTemp+spsLen,pps,ppsLen);/* 最后再把I帧复制进去 */memcpy(h264DataTemp+spsLen+ppsLen,h264Data,size);/* 把pkt对象的数据指针直接指向整理好的加上了spspps的数据 */pkt->data = h264DataTemp;pkt->size = spsppsIframeLen;}else{/* 不是I帧  直接开辟出传进来的H264数据大小的内存就行了 */h264DataTemp = new uint8_t[size];/* 把H264数据复制进去 */memcpy(h264DataTemp,h264Data,size);/* 把pkt对象的数据指针直接指向H264数据 */pkt->data = h264DataTemp;pkt->size = size;}/* 定义解码后的数据的长度 */int outputSize = 0;/* 把H264数据喂给解码器 */ret = avcodec_send_packet(decoder_ctx, pkt);/* 判断下是否喂成功了,失败则直接退出 */if (ret < 0) {fprintf(stderr, "Error during decoding   str:%s  \n",strerror(errno));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;}/* 拿到解完码的数据对象frame了,但是这份数据不在CPU里,需要av_hwframe_transfer_data转到CPU里,然后赋值给sw_frame    */ret = av_hwframe_transfer_data(sw_frame, frame, 0);/* 看看是否转成功了,失败则退出 */if (ret < 0) {fprintf(stderr, "Error transferring the data to system memory\n");return ret;}/* 把NV12数据复制到给定的内存空间,NV12数据在AVFrame中的存储存储方式为->data[0]里面存着Y分量  ->data[1]厘米存着UV分量 */for (i = 0; i < FF_ARRAY_ELEMS(sw_frame->data) && sw_frame->data[i]; i++){/* 把数据全部读出来 */for (j = 0; j < (sw_frame->height >> (i > 0)); j++){memcpy(nv12Data+writeLen,sw_frame->data[i] + j * sw_frame->linesize[i], sw_frame->width);/* 复制出来的长度需要累计,最后返回给调用者,才知道解码出来的数据大小 */writeLen+=sw_frame->width;}}}outputSize =  writeLen;/* 释放一下内存 */av_packet_unref(pkt);delete []h264DataTemp;return outputSize;
}

6、在.cpp文件中实现relese方法,销毁释放内存

void HW_H264Decoder::relese(){av_frame_free(&frame);av_frame_free(&sw_frame);avcodec_free_context(&decoder_ctx);av_packet_free(&pkt);av_buffer_unref(&device_ref);;
}

至此,通过FFMPEG实现QSV硬解的相关代码就写完了。

下面是完整的.h跟.cpp文件

hw_h264decoder.h

#ifndef HW_H264DECODER_H
#define HW_H264DECODER_H#include <stdio.h>
#include <string>
#include <unistd.h>
extern "C" {
#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavcodec/avcodec.h"#include "libavutil/buffer.h"
#include "libavutil/error.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_qsv.h"
#include "libavutil/mem.h"
#include "libavutil/imgutils.h"
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>}class HW_H264Decoder
{
public:HW_H264Decoder();~HW_H264Decoder();/***  初始化** @param width : 视频的宽度* @param height : 视频的高度** @return 错误代码  0:成功***/int init(int width,int height);/*************************************************Function:decode   转成NV12再转成RGB32的数据Description:初始化Input:h264Data-H264图像数据Return:错误代码Others:无*************************************************/int decodeToRGB32(uint8_t *h264Data,int size,unsigned char *buf);/***  传入H264数据,并获取解码后的NV12数据  (QSV好像只能解成NV12)** @param h264Data : H264数据* @param size : H264数据的长度* @param nv12Data:存放NV12数据的缓冲** @return NV12数据的长度***/int decodeToNV12(uint8_t *h264Data,int size,unsigned char *nv12Data);/***  释放资源***/void relese();private://解码需要用到的上下文AVCodecContext *decoder_ctx = NULL;//解码器对象const AVCodec *decoder;//放H264数据包的对象AVPacket *pkt;//指向GPU内存的帧对象    指向CPU内存的帧对象  存储RGB32数据的帧对象AVFrame *frame = NULL, *sw_frame = NULL,*frameRGB = NULL;//解码使用的buffer指针AVBufferRef *device_ref = NULL;//输出的图像数据的大小int outImgSize;//转换格式使用的内存unsigned char *out_buffer;//用于视频图像转换的对象struct SwsContext *img_convert_ctx;//sps  这里先设置测试用的数据的SPSchar sps[50] = {0x00,0x00,0x00,0x01,0x67,0x4d,0x00,0x1f,(char)0xe9,(char)0x80,(char)0xa0,0x0b,0x74,(char)0xa4,0x14,0x18,0x18,0x1b,0x42,(char)0x84,(char)0xa7};//pps  这里先设置测试用的数据的ppschar pps[50] = {0x00,0x00,0x00,0x01,0x68,(char)0xee,0x06,(char)0xe2};//拿到的sps的长度int spsLen=0;//拿到的pps的长度int ppsLen=0;};#endif // HW_H264DECODER_H

hw_h264decoder.cpp

#include "hw_h264decoder.h"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) {return AV_PIX_FMT_QSV;}pix_fmts++;}fprintf(stderr, "The QSV pixel format not offered in get_format()\n");return AV_PIX_FMT_NONE;
}HW_H264Decoder::HW_H264Decoder()
{}
HW_H264Decoder::~HW_H264Decoder()
{}int HW_H264Decoder::init(int width,int height){int ret;/* 打开QSV设备 */ret = av_hwdevice_ctx_create(&device_ref, AV_HWDEVICE_TYPE_QSV,"auto", NULL, 0);/* 检查下是否打开成功,失败则直接跳出去 */if (ret < 0) {fprintf(stderr, "Cannot open the hardware device\n");return -1;}/* 打开解码器,这里是使用qsv对H264进行硬解,故使用h264_qsv */decoder = avcodec_find_decoder_by_name("h264_qsv");/* 检查下是否打开成功,失败则直接跳出去 */if (!decoder) {fprintf(stderr, "The QSV decoder is not present in libavcodec\n");return -1;}/* 根据解码器初始化解码器的上下文 */decoder_ctx = avcodec_alloc_context3(decoder);/* 检查下是否初始化打开成功,失败则直接跳出去 */if (!decoder_ctx) {return -1;}/* 设置下解码类型 */decoder_ctx->codec_id = AV_CODEC_ID_H264;/* 开辟解码器使用的缓冲 */decoder_ctx->hw_device_ctx = av_buffer_ref(device_ref);/* 设置下QSV格式  之所以这么写,是为了确定是否有QSV */decoder_ctx->get_format  = get_format;/* 虽然说QSV只有解出NV12  但是这里设置一下好了 */decoder_ctx->pix_fmt = AV_PIX_FMT_NV12;/* 把解码器的上下文跟解码器绑定并初始化 */ret = avcodec_open2(decoder_ctx, decoder, NULL);/* 检查下是否初始化成功,失败则直接跳出去 */if (ret < 0) {fprintf(stderr, "Error opening the decoder: ");return -1;}/* 初始化用到的帧对象 */frame    = av_frame_alloc();sw_frame = av_frame_alloc();frameRGB = av_frame_alloc();/* 初始化存放H264包的对象 */pkt = av_packet_alloc();/* 检查下是否初始化成功,失败则直接跳出去 */if (!frame || !sw_frame || !frameRGB || !pkt) {ret = AVERROR(ENOMEM);return -1;}// 创建动态内存,创建存储RGB32图像数据的空间(av_image_get_buffer_size获取一帧图像需要的大小)outImgSize = av_image_get_buffer_size(AV_PIX_FMT_RGB32, width, height, 1);out_buffer = (unsigned char *)av_malloc(outImgSize);// 存储一帧像素数据缓冲区av_image_fill_arrays(frameRGB->data, frameRGB->linesize, out_buffer,AV_PIX_FMT_RGB32, width,height, SWS_FAST_BILINEAR );// 初始化img_convert_ctx结构  配置转换成RGB32img_convert_ctx = sws_getContext(width,height, decoder_ctx->pix_fmt,width, height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);return 0;
}void HW_H264Decoder::relese(){av_frame_free(&frame);av_frame_free(&sw_frame);av_frame_free(&frameRGB);avcodec_free_context(&decoder_ctx);av_packet_free(&pkt);av_buffer_unref(&device_ref);sws_freeContext(img_convert_ctx);
}int HW_H264Decoder::decodeToRGB32(uint8_t *h264Data,int size,unsigned char *buf){int ret = 0;/* 由于I帧需要在前面家SPSPPS 不然会出现无法解码的情况,故开辟个临时缓冲区 */uint8_t *h264DataTemp = NULL;/* 如果是SPS 则存起来,不喂给解码器,不然会导致解不出东西 */if(h264Data[4] == 0x67){memcpy(sps,h264Data,size);spsLen = size;return 0;}/* 如果是pps 则存起来,不喂给解码器,不然会导致解不出东西 */if(h264Data[4] == 0x68){memcpy(pps,h264Data,size);ppsLen = size;return 0;}/* 判断下是否是I帧  是I帧的话需要在前面加上spspps,不然会导致解不出东西 */if(h264Data[4] == 0x65){/* 现在是I帧,需要加上SPSPPS,故计算下加上spspps之后的长度是多少 */int spsppsIframeLen = size+spsLen+ppsLen;/* 开辟出需要的内存 */h264DataTemp = new uint8_t[spsppsIframeLen];/* 先复制SPS进去 */memcpy(h264DataTemp,sps,spsLen);/* 再复制PPS进去 */memcpy(h264DataTemp+spsLen,pps,ppsLen);/* 最后再把I帧复制进去 */memcpy(h264DataTemp+spsLen+ppsLen,h264Data,size);/* 把pkt对象的数据指针直接指向整理好的加上了spspps的数据 */pkt->data = h264DataTemp;pkt->size = spsppsIframeLen;}else{/* 不是I帧  直接开辟出传进来的H264数据大小的内存就行了 */h264DataTemp = new uint8_t[size];/* 把H264数据复制进去 */memcpy(h264DataTemp,h264Data,size);/* 把pkt对象的数据指针直接指向H264数据 */pkt->data = h264DataTemp;pkt->size = size;}/* 定义解码后的数据的长度 */int outputSize = 0;/* 把H264数据喂给解码器 */ret = avcodec_send_packet(decoder_ctx, pkt);/* 判断下是否喂成功了,失败则直接退出 */if (ret < 0) {fprintf(stderr, "Error during decoding   str:%s  \n",strerror(errno));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;}/* 拿到解完码的数据对象frame了,但是这份数据不在CPU里,需要av_hwframe_transfer_data转到CPU里,然后赋值给sw_frame    */ret = av_hwframe_transfer_data(sw_frame, frame, 0);/* 看看是否转成功了,失败则退出 */if (ret < 0) {fprintf(stderr, "Error transferring the data to system memory\n");return ret;}/* 把拿出来的NV12转成RGB32 */ret = sws_scale(img_convert_ctx,sw_frame->data, sw_frame->linesize, 0, sw_frame->height,frameRGB->data, frameRGB->linesize);/* 把RGB32复制到输出的缓冲 */memcpy(buf,frameRGB->data[0],outImgSize);/* 输出的RGB32的大小 */outputSize = outImgSize;}/* 释放一下内存 */av_packet_unref(pkt);delete []h264DataTemp;return outputSize;
}int HW_H264Decoder::decodeToNV12(uint8_t *h264Data, int size, unsigned char *nv12Data){int ret = 0;/* 由于I帧需要在前面家SPSPPS 不然会出现无法解码的情况,故开辟个临时缓冲区 */uint8_t *h264DataTemp = NULL;/* 如果是SPS 则存起来,不喂给解码器,不然会导致解不出东西 */if(h264Data[4] == 0x67){memcpy(sps,h264Data,size);spsLen = size;return 0;}/* 如果是pps 则存起来,不喂给解码器,不然会导致解不出东西 */if(h264Data[4] == 0x68){memcpy(pps,h264Data,size);ppsLen = size;return 0;}/* 判断下是否是I帧  是I帧的话需要在前面加上spspps,不然会导致解不出东西 */if(h264Data[4] == 0x65){/* 现在是I帧,需要加上SPSPPS,故计算下加上spspps之后的长度是多少 */int spsppsIframeLen = size+spsLen+ppsLen;/* 开辟出需要的内存 */h264DataTemp = new uint8_t[spsppsIframeLen];/* 先复制SPS进去 */memcpy(h264DataTemp,sps,spsLen);/* 再复制PPS进去 */memcpy(h264DataTemp+spsLen,pps,ppsLen);/* 最后再把I帧复制进去 */memcpy(h264DataTemp+spsLen+ppsLen,h264Data,size);/* 把pkt对象的数据指针直接指向整理好的加上了spspps的数据 */pkt->data = h264DataTemp;pkt->size = spsppsIframeLen;}else{/* 不是I帧  直接开辟出传进来的H264数据大小的内存就行了 */h264DataTemp = new uint8_t[size];/* 把H264数据复制进去 */memcpy(h264DataTemp,h264Data,size);/* 把pkt对象的数据指针直接指向H264数据 */pkt->data = h264DataTemp;pkt->size = size;}/* 定义解码后的数据的长度 */int outputSize = 0;/* 把H264数据喂给解码器 */ret = avcodec_send_packet(decoder_ctx, pkt);/* 判断下是否喂成功了,失败则直接退出 */if (ret < 0) {fprintf(stderr, "Error during decoding   str:%s  \n",strerror(errno));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;}/* 拿到解完码的数据对象frame了,但是这份数据不在CPU里,需要av_hwframe_transfer_data转到CPU里,然后赋值给sw_frame    */ret = av_hwframe_transfer_data(sw_frame, frame, 0);/* 看看是否转成功了,失败则退出 */if (ret < 0) {fprintf(stderr, "Error transferring the data to system memory\n");return ret;}/* 把NV12数据复制到给定的内存空间,NV12数据在AVFrame中的存储存储方式为->data[0]里面存着Y分量  ->data[1]厘米存着UV分量 */for (i = 0; i < FF_ARRAY_ELEMS(sw_frame->data) && sw_frame->data[i]; i++){/* 把数据全部读出来 */for (j = 0; j < (sw_frame->height >> (i > 0)); j++){memcpy(nv12Data+outputSize ,sw_frame->data[i] + j * sw_frame->linesize[i], sw_frame->width);/* 复制出来的长度需要累计,最后返回给调用者,才知道解码出来的数据大小 */outputSize +=sw_frame->width;}}}/* 释放一下内存 */av_packet_unref(pkt);delete []h264DataTemp;return outputSize;
}

调用的时候直接先init初始化一下,然后就调用decode把H264数据传进去就可以实现解码了(前三包必须是sps->ssp->I帧),程序结束的时候调用relese就销毁就行了

/* 从文件中读取H264数据并进行解码 解码成功后发到UI线程显示 */
void ShowCH1Thread::run(){int ret;/* 实例化解码类的对象 */HW_H264Decoder mHWh264Decodec;/* 初始化解码类 */ret =  mHWh264Decodec.init(1920,1080);if(ret != 0){printf("init h264 decodec fialed!   %s\n",strerror(errno));}/* 开辟需要用到的内存空间 */char h264Data[4096] = {0x00};uint8_t *grbData = (uint8_t *)malloc(1024*1024*10);uint8_t *H264Cache = (uint8_t*)malloc(1024*1024*1);int cacheindex = 0;/* 打开一个h264文件用来读H264数据进行解码 */int h264File = open("/home/vis/qsvLIb/H264Player/test.h264",O_RDWR);if(!h264Data){printf("open h264 file failed!   %s\n",strerror(errno));}int len = -1;while(true){/* 先读一个直接出来  看看是不是01  主要用来判断NAL标识头 00000001 */len = read(h264File,h264Data,1);if(len > 0){/* 读到01了  长度也大于等于3  那么可能是一个NAL标识头 */if(h264Data[0] == 0x01 && cacheindex >= 3){/* 判断下是不是一个NAL头 */if(H264Cache[cacheindex-1] == 0x00 && H264Cache[cacheindex-2] == 0x00 && H264Cache[cacheindex-3] == 0x00){/* 是NAL头   那么把这帧数据拿去解码 */int h264len = cacheindex-3;if(h264len > 5){ret = mHWh264Decodec.decodeToRGB32(H264Cache,h264len,grbData);/* ret大于0则说明解码成功  ret就是解码出来的数据的长度  发到UI线程进行显示 */if(ret > 0){emit sigShowCH1ToUI(grbData);usleep(40*1000);}}memset(H264Cache,0x00,cacheindex);cacheindex = 3;H264Cache[cacheindex] = h264Data[0];cacheindex++;}else{/* 还没找到头  继续读 */H264Cache[cacheindex] = h264Data[0];cacheindex++;}}else{/* 还没找到头  继续读 */H264Cache[cacheindex] = h264Data[0];cacheindex++;}}else{printf("read h264 file failed!  len=%d %s\n",len,strerror(errno));break;}}/* 释放一下 */mHWh264Decodec.relese();close(h264File);free(h264Data);free(grbData);free(H264Cache);
}

这个实现过程主要是参考了ffmpeg/doc/examples/qsvdec.c文件,实现得比较粗糙,需要的话可以去看看这个文件的内容,可以加深对QSV硬解的理解


本文链接:https://www.ngui.cc/zz/2336569.html
Copyright © 2010-2022 ngui.cc 版权所有 |关于我们| 联系方式| 豫B2-20100000