欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

解码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;

工程目录,可能有没用的代码,不想删除:

https://download.csdn.net/download/chnim/10675494

相关标签: nalu decode