基於大華SDK及c++的標準H264裸碼封裝MP4文件+保存.h264文件+實時預覽

最近本人在學習大華相機的二次開發時遇到一個問題:大華SDK開發文檔並沒有說明如何獲取相機H264裸碼流及其如何封裝成mp4文件。在研究了海康的相關代碼之後,才明白如何操作。

main.cpp

#include <iostream>
#include "dhnetsdk.h"
#include "MP4Encoder.h"
using namespace std;
LLONG g_lRealHandle;
MP4Encoder* pMP4;
void CALLBACK RealDataCallBackEx2(LLONG lRealHandle, DWORD dwDataType, BYTE*pBuffer, DWORD dwBufSize, tagVideoFrameParam param, LDWORD dwUser)
{
	// 若多個實時監視使用相同的數據回調函數,則用戶可通過 lRealHandle 進行一一對應
	if (lRealHandle == g_lRealHandle)
	{
		int type = param.frametype;
		switch (type)
		{
		case 0: // I 幀 
			pMP4->getConvertMP4File(pBuffer, dwBufSize, type);
			break;
		case 1: // P 幀
			pMP4->getConvertMP4File(pBuffer, dwBufSize, type);
			break;
		default:
			break;
		}	
	}
}


int main()
{
	BOOL g_bNetSDKInitFlag = FALSE;
	// 初始化 SDK
	g_bNetSDKInitFlag = CLIENT_Init(NULL, 0);
	if (FALSE == g_bNetSDKInitFlag)
	{
		cout << "Initialize client SDK fail;" << endl;
		return 0;
	}
	else
		cout << "Initialize client SDK done; " << endl;

	NET_DEVICEINFO_Ex deviceInfo = { 0 };
	int nError = 0;
	LLONG login_handle = CLIENT_LoginEx2("192.168.1.64", 37777, "admin", "admin", EM_LOGIN_SPEC_CAP_TCP, nullptr, &deviceInfo, &nError);
	/*登錄失敗*/
	if (login_handle != 0)
	{
		cout << "登錄成功" << endl;
		//開啓實時監視
		int nChannelID = 0; // 預覽通道號
		DH_RealPlayType emRealPlayType = DH_RType_Realplay; // 實時監視
		HWND hWnd = GetConsoleWindow();
		g_lRealHandle = CLIENT_RealPlayEx(login_handle, nChannelID, hWnd, emRealPlayType);
		pMP4 = new MP4Encoder;
		if (0 == g_lRealHandle)
		{
			cout << "CLIENT_RealPlayEx: failed! Error code:" << CLIENT_GetLastError() << endl;
			system("pause");
			return 0;
		}
		else
		{
			//DWORD dwFlag = 0x00000001;
			if (FALSE == CLIENT_SetRealDataCallBackEx2(g_lRealHandle, &RealDataCallBackEx2, NULL, REALDATA_FLAG_DATA_WITH_FRAME_INFO))
			{
				cout << "CLIENT_SetRealDataCallBackEx: failed! Error code: " << CLIENT_GetLastError() << endl;
				return 0;
			}
			Sleep(10000);		
		}	
	}
	pMP4->getMP4FileClose();
	delete pMP4;
	//關閉預覽
	if (CLIENT_StopRealPlayEx(g_lRealHandle))
	{
		g_lRealHandle = 0;
	};

	// 退出設備
	if (0 != login_handle)
	{
		if (FALSE == CLIENT_Logout(login_handle))
		{
			cout << "CLIENT_Logout Failed!Last Error \n" << CLIENT_GetLastError() << endl;
		}
		else
		{
			login_handle = 0;
		}
	}
	// 清理初始化資源
	if (TRUE == g_bNetSDKInitFlag)
	{
		CLIENT_Cleanup();
		g_bNetSDKInitFlag = FALSE;
	}
	return 0;
}

MP4Encoder.cpp,通過libmp4V2實現MP4寫封裝生成MP4視頻文件

#include "MP4Encoder.h"

void MP4Encoder::getMP4FileClose()
{
	MP4Close(mFile);
}

bool MP4Encoder::getConvertMP4File(BYTE *pBuffer, DWORD dwBufSize, int dwDataType)
{
	
	CreateMP4File();
	WriteH264Data(mFile, pBuffer, dwBufSize, dwDataType);
	return true;
}

MP4Encoder::MP4Encoder()
{	
	mFile = MP4_INVALID_FILE_HANDLE;
	mVideoId = MP4_INVALID_TRACK_ID;
}


MP4Encoder::~MP4Encoder()
{
	
}


string MP4Encoder::GetSystemTime()
{
	SYSTEMTIME m_time;
	GetLocalTime(&m_time);
	char szDateTime[100] = { 0 };
	sprintf_s(szDateTime, "%02d%02d%02d%02d%02d%02d", m_time.wYear, m_time.wMonth,
		m_time.wDay, m_time.wHour, m_time.wMinute, m_time.wSecond);
	string time(szDateTime);
	return time;
}

MP4FileHandle MP4Encoder::CreateMP4File()
{
	if (mFile == NULL)
	{
		string s = GetSystemTime() + ".mp4";
		const char* FileName = s.c_str();
		mFile = MP4Create(FileName);
		if (mFile == MP4_INVALID_FILE_HANDLE)
		{
			cout << "Open file failed!\n";
			return 0;
		}

		if (mFile == NULL)
		{
			printf("ERROR:Create file failed!");
			return false;
		}	
		// 設置時間片
		MP4SetTimeScale(mFile, VIDEO_TIME_SCALE);
	}	
	return mFile;
}


int MP4Encoder::ReadOneNaluFromBuf(const unsigned char *pBuffer, unsigned int nBufferSize, unsigned int offSet, MP4ENC_NaluUnit &nalu)
{
	unsigned int i = offSet;
	int j = 0;
	while (i<nBufferSize)
	{//尋找第一個00 00 00 01		
		if (pBuffer[i] == 0x00 &&pBuffer[i+1] == 0x00 &&pBuffer[i+2] == 0x00 &&pBuffer[i+3] == 0x01)
		{			
			i = i + 4;
			unsigned int pos = i;
			while (pos<nBufferSize)
			{//尋找最後一個00 00 00 01
				if (pBuffer[pos] == 0x00 &&pBuffer[pos+1] == 0x00 &&pBuffer[pos+2] == 0x00 &&pBuffer[pos+3] == 0x01)
					break;
				pos++;
			}
			pos = pos + 4;
			if (pos == nBufferSize)
				nalu.size = pos - i;
			else
				nalu.size = (pos - 4) - i;
			nalu.type = pBuffer[i] & 0x1f;
			nalu.data = (unsigned char*)&pBuffer[i];
			return (nalu.size + i - offSet);
		}
		i++;
	}
	return 0;
}

int MP4Encoder::WriteH264Data(MP4FileHandle MP4File, const unsigned char* pData, int size,int dwDataType)
{
	if (MP4File == NULL)
	{
		return -1;
	}
	if (pData == NULL)
	{
		return -1;
	}
	MP4ENC_NaluUnit nalu;
	int pos = 0;
	int	len = 0;

	while (len = ReadOneNaluFromBuf(pData, size, pos, nalu))
	{
		if (nalu.type == 0x07) // sps
		{
			if (mVideoId == MP4_INVALID_TRACK_ID)
			{	// 添加h264 track 
				mVideoId = MP4AddH264VideoTrack(
					MP4File,
					VIDEO_TIME_SCALE,       // 視頻每秒的ticks數(如90000)
					VIDEO_TIME_SCALE / 13,  // 視頻的固定的視頻幀的顯示時間,公式爲timeScale(90000)/fps(碼率例如20f)
					VIDEO_WIDTH,			    // 視頻的寬度
					VIDEO_HEIGHT,			// 視頻的高度
					nalu.data[1],			// sps[1] AVCProfileIndication
					nalu.data[2],			// sps[2] profile_compat
					nalu.data[3],			// sps[3] AVCLevelIndication
					3						// 4 bytes length before each NAL unit
					);

				if (mVideoId == MP4_INVALID_TRACK_ID)
				{
					printf("add video track failed.\n");
					system("pause");
					return 0;
				}
				MP4SetVideoProfileLevel(MP4File, 0x01); //  Simple Profile @ Level 3
				MP4AddH264SequenceParameterSet(MP4File, mVideoId, nalu.data, nalu.size);
			}
		}
		else if (nalu.type == 0x08) // pps
		{
			MP4AddH264PictureParameterSet(MP4File, mVideoId, nalu.data, nalu.size);
		}
		else
		{
			int datalen = nalu.size + 4;
			unsigned char *data = new unsigned char[datalen];

			// MP4 Nalu前四個字節表示Nalu長度
			data[0] = nalu.size >> 24;
			data[1] = nalu.size >> 16;
			data[2] = nalu.size >> 8;
			data[3] = nalu.size & 0xff;
			memcpy(data + 4, nalu.data, nalu.size);
			if (dwDataType == 0)
			{
				MP4WriteSample(MP4File, mVideoId, data, datalen, MP4_INVALID_DURATION, 0, 1);
			}
			if (dwDataType == 1)
			{
				MP4WriteSample(MP4File, mVideoId, data, datalen, MP4_INVALID_DURATION, 0, 0);
			}
			delete[] data;
		}

		pos += len;
	}
	return pos;
}




整個項目(VS2015-X64)下載鏈接:https://download.csdn.net/download/qq_35135771/12526926

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章