Всем известно, что функция avformat_open_input() в ffmpeg может напрямую открывать локальные файлы или сетевые потоки для декодирования.Нам не нужно заботиться об анализе видеоданных, но зашифрованные видео нельзя воспроизвести, поэтому нам нужно обработать исходные данные и упаковать в стандартный поток формата H264, а затем декодировать его. Эта библиотека декодирования поддерживает стандартное декодирование формата H264 и декодирование потока без сегментации NALU, просто отправьте полученный поток UDP непосредственно в функцию декодирования.
Внутри функции H264DecFrame я выполнил обработку кэш-пакетов.У ffmpeg есть функция av_parser_parse2 для реализации этой функции, но при реальном использовании я обнаружил, что существует проблема: общая длина пакета неверна, что приводит к ненормальному декодированному видеоэкрану. , поэтому пакетную часть я написал сам.
Этот API может декодировать только чистые потоки H264 и не может воспроизводить инкапсулированные форматы, такие как MP4, AVI и т. д.
dll скачать: https://download.csdn.net/download/whf227/13108201
Функции 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;
}