大家都知道ffmpeg的avformat_open_input()函数可以直接打开本地文件或网络流进行解码,我们不用关心分析视频的数据,但是对于加密的视频就无法进行播放,于是就需要对源数据进行处理后,组包成标准的H264格式流,再进行解码。本解码库支持标准的H264格式解码,支持流式解码,不用进行NALU分割,直接将获取到的UDP流送入解码函数即可。
H264DecFrame函数的内部我做了缓存组包处理,ffmpeg有av_parser_parse2函数实现这个功能,但是实际使用中发现有问题,包长度总数不对,导致解码出来的视频画面异常,于是自己写了组包的部分。
本API仅可解码H264裸流,不可以播放封装的格式如MP4、AVI等。
API函数如下:
* 功能描述:创建解码器
* @param width:输入图像宽
* @param height:输入图像高
* @param picFormat:输入图像格式 0:YUV420
* @param decThread:输入 解码线程数
*
* @return true:成功 false:失败
*/
bool H264DecCreate(int width, int height, int picFormat = 0, int decThread = 4);
/**
* 功能描述:销毁解码器
*/
void H264DecDestroy();
/**
* 功能描述:对输入的一段码流进行解码并按帧输出图像
本函数仅支持流式解码,对于以“00 00 01”为 nalu 分隔符的连续、线性 H.264 码流,
用户可从任意起始地址、任意长度配置给解码器解码。
在调用本函数过程中需要注意以下两点:
− 在解码过程中,用户应该将码流分段,并依次配置给解码器。当用户调用此函
数,将一段码流配置给解码器之后,应对函数的参数做如下配置:
pStream=NULL;iStreamLen=0 然后循环调用此函数,直到函数返
回 H264DEC_NEED_MORE_BITS 时才能再次配置一段新的码流。
在上述循环调用的过程中,如果函数返回 H264DEC_OK 则表明有一帧图像
输出,用户必须在循环调用内部及时处理存储在 pDecFrame 中的图像。
在上述循环调用的过程中,如果函数返回 H264DEC_OK 则表明有一帧图像输
出,用户必须在循环调用内部及时处理存储在 pDecFrame 中的图像
* @param pStream:输入 码流起始地址
* @param streamLen: 输入 码流长度
* @param pY: 输出 Y分量
* @param pU: 输出 U分量
* @param pV: 输出 V分量
* @param yStride: 输出 Y分量的Stride
* @param uvStride: 输出 U/V分量的Stride
* @param flag:
*
* @return
*/
int H264DecFrame(unsigned char* pStream, int streamLen, Dec_Frame_S* decFrame, int flag);
调用方法:
struct frameStruct
{
UCHAR* data;
int length;
};
FILE* h264;
int decode(void* para)
{
bool res = false;
res = yhDecoder->H264DecCreate(width,height,0);
dec_frame = new Dec_Frame_S;
dec_frame->pY = new UCHAR[1920*1080];
dec_frame->pU = new UCHAR[1920*1080];
dec_frame->pV = new UCHAR[1920*1080];
dec_frame->yStride = 0;
dec_frame->uvStride = 0;
while (len>0)
{
char data[1024];
h264 = fopen("F:\\Video\\blackissue.264", "rb");
int len = (int)fread(data, 1, 1024, h264);
if(len<=0)
break;
frameStruct* dataFrame = new frameStruct;
dataFrame->data= data;
dataFrame->length =1024;
result = yhDecoder->H264DecFrame(dataFrame->data, dataFrame>length,dec_frame, 0);
delete[] dataFrame->data;
dataFrame->data = NULL;
delete dataFrame;
dataFrame = NULL;
while (H264DEC_NEED_MORE_BITS != result) //满足一帧
{
if (H264DEC_NO_PICTURE == result) /* 解码器中已经没有残留图像 */
{
break;
}
if (H264DEC_OK == result) /* 输出一帧图像 */
{
canShow = true;
//int ret = sdlDisplay->UpdateSDLWindowYUV(dec_frame->pY, dec_frame->pU, dec_frame->pV, width, height, dec_frame->yStride, dec_frame->uvStride);
if (!isUDPStream)
SDL_Delay(10);
}
/* 继续解码剩余H.264码流 */
result = yhDecoder->H264DecFrame(NULL, 0, dec_frame, 0);
}
}
yhDecoder->H264DecDestroy();
//sdlDisplay->CloseSDL();
delete[] effictiveData;
effictiveData = NULL;
delete dec_frame->pY;
dec_frame->pY = NULL;
delete dec_frame->pU;
dec_frame->pU = NULL;
delete dec_frame->pV;
dec_frame->pV = NULL;
delete dec_frame;
dec_frame = nullptr;
return 0;
}