解码H264视频文件实现类似av_parser_parse2功能
程序员文章站
2022-07-05 14:42:24
...
#include "SubsectionDecoder.h"
#include "H264_2_RGB.h"
#include <pthread.h>
#include <iostream>
#include <Windows.h>
#ifdef _WIN32
#pragma comment(lib, "pthreadVC2.lib")
#endif
using namespace SubDecode;
//6000000
#define MX_RTP_BUF (1000*1000*6)
int info4 = 0, info5 = 0;
static int FindStartCode2(unsigned char *Buf)
{
if (Buf[0] != 0 || Buf[1] != 0 || Buf[2] != 1) return 0; //0x000001?
else return 1;
}
static int FindStartCode3(unsigned char *Buf)
{
if (Buf[0] != 0 || Buf[1] != 0 || Buf[2] != 0 || Buf[3] != 1) return 0;//0x00000001?
else return 1;
}
int SubsectionDecoder::GetAnnexbNALU(NALU_t *nalu)
{
int pos = 0;
int StartCodeFound, rewind;
unsigned char *Buf;
m_nIndex = 0;
if ((Buf = (unsigned char*)calloc(nalu->max_size, sizeof(char))) == NULL)
{
printf("GetAnnexbNALU: Could not allocate Buf memory\n");
return -1;
}
nalu->startcodeprefix_len = 3;
//获取数据
memcpy(Buf, m_pMaxBuf, 3);
m_nCurSize -= 3;
m_nIndex += 3;
info4 = FindStartCode2(Buf);
if (info4 != 1)
{
memcpy(Buf + 3, m_pMaxBuf + m_nIndex, 1);
m_nCurSize -= 1;
m_nIndex += 1;
info5 = FindStartCode3(Buf);
if (info5 != 1)
{
//移动内存
memmove(m_pMaxBuf, m_pMaxBuf + m_nIndex, m_nCurSize);
free(Buf);
return -1;
}
else
{
pos = 4;
nalu->startcodeprefix_len = 4;
}
}
else
{
nalu->startcodeprefix_len = 3;
pos = 3;
}
StartCodeFound = 0;
info4 = 0;
info5 = 0;
while (!StartCodeFound)
{
if (m_nCurSize <= 0)
{
nalu->len = (pos - 1) - nalu->startcodeprefix_len;
memcpy(nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
free(Buf);
//移动内存
memmove(m_pMaxBuf, m_pMaxBuf + m_nIndex, m_nCurSize);
return pos - 1;
}
//整理内存
Buf[pos++] = m_pMaxBuf[m_nIndex];//*m_pMaxBuf + m_nIndex;//(m_pMaxBuf)[m_nIndex];
m_nIndex++;
m_nCurSize--;
info5 = FindStartCode3(&Buf[pos - 4]);
if (info5 != 1)
info4 = FindStartCode2(&Buf[pos - 3]);
StartCodeFound = (info4 == 1 || info5 == 1);
}
// Here, we have found another start code (and read length of startcode bytes more than we should
// have. Hence, go back in the file
rewind = (info5 == 1) ? -4 : -3;
//调整空间
m_nCurSize -= rewind;
m_nIndex += rewind;
// Here the Start code, the complete NALU, and the next start code is in the Buf.
// The size of Buf is pos, pos+rewind are the number of bytes excluding the next
// start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code
nalu->len = (pos + rewind) - nalu->startcodeprefix_len;
memcpy(nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//
nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit
free(Buf);
//移动内存
memmove(m_pMaxBuf, m_pMaxBuf + m_nIndex, m_nCurSize);
return (pos + rewind);
}
int SubsectionDecoder::PushStream2Play(unsigned char * pBuf, int nbufsize, void* pUserData)
{
if (m_nCurSize > MX_RTP_BUF)
return -1;
memcpy(m_pMaxBuf + m_nCurSize, pBuf, nbufsize);
m_nCurSize += nbufsize;
if (m_nCurSize < 40 * 1000)
{
if (nbufsize >= 1024)
{
return -2;
}
}
int data_offset = 0;
int nal_num = 0;
//解码用另加
unsigned char *m_pData = (unsigned char *)calloc(1000 * 1000, sizeof(char));
int sizeHeBing = 0;
unsigned char* outputBuf;
unsigned int nOutSize;
int nHeight;
int nWidth;
bool bInit = false;
int data_length;
//while(m_nCurSize > 10000 && GetAnnexbNALU(m_pNalu) > 0)
data_length = GetAnnexbNALU(m_pNalu);
if(data_length > 0)
{
//解码视频并显示
memset(m_pData, 0, 4);
m_pData[3] = 1;
memcpy(m_pData + 4, m_pNalu->buf, m_pNalu->len);
sizeHeBing = m_pNalu->len + 4;
H264_2_RGB(m_pData, sizeHeBing, outputBuf, &nOutSize, &nWidth, &nHeight);
}
free(m_pData);
return 0;
}
SubsectionDecoder::SubsectionDecoder():m_nCurSize(0), m_bdecoding(true)
{
H264_Init(AV_CODEC_ID_H264);
m_pMaxBuf = (unsigned char*)malloc(sizeof(uint8_t)*MX_RTP_BUF);
if (m_pMaxBuf == NULL)
exit(-1);
InitNalu();
}
int SubsectionDecoder::UnInitNalu()
{
if(m_pNalu->buf != NULL)
free(m_pNalu->buf);
if(m_pNalu != NULL)
free(m_pNalu);
return 0;
}
SubsectionDecoder:: ~SubsectionDecoder()
{
m_bdecoding = false;
//pthread_kill(m_pid, 0);
H264_Release();
free(m_pMaxBuf);
UnInitNalu();
//pthread_mutex_destroy(&m_mutex);
}
int SubsectionDecoder::PushStreamData(unsigned char * pBuf, int nbufSize)
{
if (MX_RTP_BUF < (m_nCurSize + nbufSize))
return m_nCurSize;
if (pBuf != NULL && nbufSize > 0)
{
LockMemery();
memcpy(m_pMaxBuf + m_nCurSize, pBuf, nbufSize);
m_nCurSize += nbufSize;
UnLockMemery();
}
return m_nCurSize;
}
void* decodeThread(void* pUserData)
{
SubsectionDecoder *pDecoder = (SubsectionDecoder*)pUserData;
NALU_t *n;
int buffersize = 100000;
FILE *myout = stdout;
n = (NALU_t*)calloc(1, sizeof(NALU_t));
if (n == NULL) {
printf("Alloc NALU Error\n");
return 0;
}
n->max_size = buffersize;
n->buf = (char*)calloc(buffersize, sizeof(char));
if (n->buf == NULL) {
free(n);
printf("AllocNALU: n->buf");
return 0;
}
int data_offset = 0;
int nal_num = 0;
printf("-----+-------- NALU Table ------+---------+\n");
printf(" NUM | POS | IDC | TYPE | LEN |\n");
printf("-----+---------+--------+-------+---------+\n");
//解码用另加
unsigned char *m_pData = (unsigned char *)calloc(1000 * 1000, sizeof(char));
int sizeHeBing = 0;
unsigned char* outputBuf;
unsigned int nOutSize;
int nHeight;
int nWidth;
bool bInit = false;
while (pDecoder->GetRunStatus())
{
int nCurSize = pDecoder->GetCurMemSize();
if (nCurSize <= (4 * 1000))
{
Sleep(1);
continue;
}
int data_lenth;
pDecoder->LockMemery();
data_lenth = pDecoder->GetAnnexbNALU(n);
pDecoder->UnLockMemery();
if (data_lenth > 0)
{
//解码视频并显示
memset(m_pData, 0, 4);
m_pData[3] = 1;
memcpy(m_pData + 4, n->buf, n->len);
sizeHeBing = n->len + 4;
H264_2_RGB(m_pData, sizeHeBing, outputBuf, &nOutSize, &nWidth, &nHeight);
}
}
return NULL;
}
int SubsectionDecoder::InitNalu()
{
int buffersize = 100000;
FILE *myout = stdout;
m_pNalu = (NALU_t*)calloc(1, sizeof(NALU_t));
if (m_pNalu == NULL) {
printf("Alloc NALU Error\n");
return -1;
}
m_pNalu->max_size = buffersize;
m_pNalu->buf = (char*)calloc(buffersize, sizeof(char));
if (m_pNalu->buf == NULL) {
free(m_pNalu);
printf("AllocNALU: n->buf");
return -1;
}
}
int SubsectionDecoder::LockMemery()
{
pthread_mutex_lock(&m_mutex);
return 0;
}
int SubsectionDecoder::UnLockMemery()
{
pthread_mutex_unlock(&m_mutex);
return 0;
}
void* SubsectionDecoder::GetUserData()
{
return m_pUserData;
}
bool SubsectionDecoder::SetOutputBuf(RgbBufOut bufProc, void* pUserData)
{
m_proRgbBuf = bufProc;
if (bufProc == NULL)
return false;
m_pUserData = pUserData;
return true;
#if 0
pthread_mutex_init(&m_mutex, NULL);
if (pthread_create(&m_pid, NULL, decodeThread, this) == 0)
{
std::cout << "create decoder thread success" << std::endl;
pthread_detach(m_pid);
}
else
{
std::cout << "create decoder thread failed" << std::endl;
}
#endif
}
具体的实现类,参考雷神的代码,不是将文件整个读入,改为一点一点的推送buffer
#pragma once
#include "StreamDetector.h"
#include <pthread.h>
namespace SubDecode {
/**
次类用作,读取视频文件,然后批量解码的功能库
------------------------------------------------------------------------
1 2 3 4 5 6 7
void* _memmove(void* dest, const void* src, size_t count)
*/
typedef void(*RgbBufOut)(void* pUserData, unsigned char* pBufOut, int pBufSize, int nWidth, int nHeight, int nLineWidth);
///Nalu类型枚举
typedef enum {
NALU_TYPE_SLICE = 1,
NALU_TYPE_DPA = 2,
NALU_TYPE_DPB = 3,
NALU_TYPE_DPC = 4,
NALU_TYPE_IDR = 5,
NALU_TYPE_SEI = 6,
NALU_TYPE_SPS = 7,
NALU_TYPE_PPS = 8,
NALU_TYPE_AUD = 9,
NALU_TYPE_EOSEQ = 10,
NALU_TYPE_EOSTREAM = 11,
NALU_TYPE_FILL = 12,
} NaluType;
//Nalu属性
typedef enum {
NALU_PRIORITY_DISPOSABLE = 0,
NALU_PRIRITY_LOW = 1,
NALU_PRIORITY_HIGH = 2,
NALU_PRIORITY_HIGHEST = 3
} NaluPriority;
class EXPORT_DLL SubsectionDecoder {
public:
SubsectionDecoder();
int InitNalu();
int UnInitNalu();
virtual ~SubsectionDecoder();
int PushStreamData(unsigned char * pBuf, int nbufSize);
int PushStream2Play(unsigned char * pBuf, int nbufsize, void* pUserData);
bool SetOutputBuf(RgbBufOut bufProc, void* pUserData);
int LockMemery();
int UnLockMemery();
void* GetUserData();
int GetAnnexbNALU(NALU_t *nalu);
int GetCurMemSize() { return m_nCurSize; };
int GetRunStatus() { return m_bdecoding; };
bool SetRunStatus(bool bFlag) { m_bdecoding = bFlag; return m_bdecoding; };
private:
unsigned char* m_pMaxBuf; //当前buffer存放的数据
int m_nCurSize; //当前buffer的大小
RgbBufOut m_proRgbBuf;
pthread_t m_pid; //解码线程
pthread_mutex_t m_mutex; //解码线程锁
NALU_t *m_pNalu; //nalu结构体
void* m_pUserData;
int m_nIndex; //当前吃调的内存
bool m_bdecoding; //正在解码
};
}
调用方法
SubsectionDecoder *pDecoder = new SubsectionDecoder;
FILE * fph264 = fopen("G:\\UbuntuFiles\\bxg.h264", "rb");
//FILE * fph264 = fopen("sample_720p.h264", "rb");
//FILE * fph264 = fopen("bigbuckbunny_480x272.h264", "rb");
if (fph264 == NULL)
{
printf("open file faield\n");
return -1;
}
unsigned char buf[1024] = {};
//pDecoder->SetOutputBuf(RgbBufOutProc, NULL);
while (!feof(fph264))
{
int nLen = fread(buf, 1, 1024, fph264);
pDecoder->PushStream2Play(buf, nLen, nullptr);
Sleep(1);
}
//pDecoder->SetRunStatus(false);
delete pDecoder;
工程目录,可能有没用的代码,不想删除: