⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 zzlfilewriter.cpp

📁 mysee网络直播源代码Mysee Lite是Mysee独立研发的网络视频流媒体播放系统。在应有了P2P技术和一系列先进流媒体技术之后
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*
*  Openmysee
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*/
#include "stdafx.h"
#include "zzlfilewriter.h"
#include "md5.h"

ZZLFileWriter::ZZLFileWriter(void) : FILE_VERSION(1.0f),mbIsSingleAudio(FALSE)
{
	zzlHandle = INVALID_HANDLE_VALUE;
	blockCount = 0;
	dataOffset = 0;
	startTime = endTime = 0;

	dataHandle = keySampleHandle = configHandle = INVALID_HANDLE_VALUE;
	firstSampleTime = firstKeySampleTime = 0;
	newBlockSize = 0;
	sampleBuffer = NULL;
	sampleBufferSize = 0;
	dataFileCount = 0;
	audioDataSize = videoDataSize = 0;
	audioStartTime = audioEndTime = videoStartTime = videoEndTime = 0;

	mLLMaxAudioTime = 0;
	mLLMaxVideoTime = 0;

	InitializeCriticalSection(&zzlfile_cs);
}

ZZLFileWriter::~ZZLFileWriter(void)
{
	CloseHandle(zzlHandle);
	CloseHandle(dataHandle);
	CloseHandle(keySampleHandle);
	CloseHandle(configHandle);
	zzlHandle= dataHandle = keySampleHandle = configHandle = INVALID_HANDLE_VALUE;
	SAFE_ARRAYDELETE(sampleBuffer);
	DeleteCriticalSection(&zzlfile_cs);
}

bool ZZLFileWriter::Init(string path, string name) {
	if(path.empty() || name.empty())
		return false;
	if(!chnlName.empty())
		return true;	
	
	//补路径后的斜杠
	string::const_iterator litSavePath = path.end();
	--litSavePath;
	if ('\\' != static_cast<BYTE>(*litSavePath))
	{
		path += "\\"; 
	}

	savePath = path+name; // 再加一层目录,名字就是节目名
	chnlName = name;

	// calculate md5 hashcode of channel name
	char* md5Str = NULL;
	MD5 md5(reinterpret_cast<const unsigned char*>(chnlName.data()), chnlName.size());
	md5Str = md5.hex_digest();
	assert(md5Str);

	// 删除旧的临时文件
	if(!RemoveOldTmpFile(savePath)) {
		return false;	// 不能删除旧的节目数据,此次压缩取消
	}

	for(;;) {
		string bufferFileName = savePath+"\\"+name+".zzl";
		// 创建zzl文件
		if((zzlHandle = CreateFile(bufferFileName.data(), 
			GENERIC_WRITE | GENERIC_READ, 
			FILE_SHARE_READ, 
			NULL, 
			CREATE_ALWAYS, 
			FILE_ATTRIBUTE_NORMAL, 
			NULL)) == INVALID_HANDLE_VALUE)
		{
			int err = GetLastError();
			if(err == ERROR_PATH_NOT_FOUND) { 
				// 创建文件失败,因为有不存在的中间目录,需要首先创建中间目录
				int index = 0;
				string temp;
				do {
					int offset = bufferFileName.find_first_of('\\', index);
					if(offset == -1)
						break;
					temp = bufferFileName.substr(0, offset);
					index = offset+1;
					if(!CreateDirectory(temp.data(), NULL)) {
						int ret = GetLastError();
						if(ret == ERROR_ALREADY_EXISTS || ret == ERROR_ACCESS_DENIED)
							continue;
						break;
					}
				}
				while(1);
				// Create Again
				continue;
			}
			else
				return false;
		}
		break; // succeeded
	}

	DWORD writenBytes = 0;

	// 1. write ZZLD
	char fcc[5];
	strcpy(fcc, "ZZLD");
	if(INVALID_SET_FILE_POINTER == SetFilePointer(zzlHandle, 0, 0, FILE_BEGIN))
		return false;
	if(!WriteFile(zzlHandle, fcc, 4, &writenBytes, NULL))
		return false;
	if(writenBytes != 4)
		return false;

	// 2. write file version
	float version = FILE_VERSION;
	if(!WriteFile(zzlHandle, &version, sizeof(version), &writenBytes, NULL))
		return false;
	if(writenBytes != sizeof(version))
		return false;

	savePath = savePath + "\\" + md5Str;

	if(!CreateDirectory(savePath.data(), NULL)) {
		int ret = GetLastError();
		if(ret != ERROR_ALREADY_EXISTS && ret != ERROR_ACCESS_DENIED)
		{
			return false;
		}		
	}

	keySampleHandle = CreateFile((savePath+"\\keysample").data(), 
			GENERIC_WRITE | GENERIC_READ, 
			FILE_SHARE_READ, 
			NULL, 
			CREATE_ALWAYS, 
			FILE_ATTRIBUTE_NORMAL, 
			NULL);


	if(keySampleHandle == INVALID_HANDLE_VALUE)
		return false;

	configHandle = CreateFile((savePath+"\\config").data(), 
			GENERIC_WRITE | GENERIC_READ, 
			FILE_SHARE_READ, 
			NULL, 
			CREATE_ALWAYS, 
			FILE_ATTRIBUTE_NORMAL, 
			NULL);
	if(configHandle == INVALID_HANDLE_VALUE)
		return false;

	char temp[16];
	itoa(dataFileCount, temp, 10);
	dataHandle = CreateFile((savePath+"\\"+temp).data(), 
			GENERIC_WRITE | GENERIC_READ, 
			FILE_SHARE_READ, 
			NULL, 
			CREATE_ALWAYS, 
			FILE_ATTRIBUTE_NORMAL, 
			NULL);
	if(dataHandle == INVALID_HANDLE_VALUE)
		return false;

	mLLMaxAudioTime = 0;
	mLLMaxVideoTime = 0;

	// 写入节目开始的标志Sample
	SampleHeader header;
	memset(&header, 0, sizeof(SampleHeader));
	header.size = sizeof(SampleHeader);
	header.length = 0xffffffff;
	header.start = 0xffffffffffffffff;
	header.bSyncPoint = 1;
	if(!PutSample(header, NULL))
		return false;

	return true;
}

bool ZZLFileWriter::SetMediaType(const TVMEDIATYPESECTION& audioType, BYTE* audioData, const TVMEDIATYPESECTION& videoType, BYTE* videoData) {
	if(zzlHandle == INVALID_HANDLE_VALUE)
		return false;
	assert(audioData || videoData);
	DWORD writenBytes = 0;

	char temp[1024];
	memset(temp, 0, sizeof(temp));

	char* pTemp = temp;
	// write media type
	memcpy(pTemp, &videoType, sizeof(videoType));
	pTemp += sizeof(videoType);
	if(videoData)
		memcpy(pTemp, videoData, videoType.cbFormat);
	pTemp += videoType.cbFormat;
	memcpy(pTemp, &audioType, sizeof(audioType));
	pTemp += sizeof(audioType);
	if(audioData)
		memcpy(pTemp, audioData, audioType.cbFormat);
	pTemp += audioType.cbFormat;
	// write start & end Time
	memcpy(pTemp, &startTime, sizeof(startTime));
	pTemp += sizeof(startTime);
	memcpy(pTemp, &endTime, sizeof(endTime));
	pTemp += sizeof(endTime);

	if(INVALID_SET_FILE_POINTER == SetFilePointer(zzlHandle, 4+sizeof(float), 0, FILE_BEGIN))
		return false;
	if(!WriteFile(zzlHandle, temp, pTemp-temp, &writenBytes, NULL))
		return false;
	if(writenBytes != pTemp-temp)
		return false;

	dataOffset = 4 + sizeof(float) + pTemp-temp;

	// calculate md5 hashcode of channel name
	char* md5Str = NULL;
	MD5 md5(reinterpret_cast<const unsigned char*>(chnlName.data()), chnlName.size());
	md5Str = md5.hex_digest();
	assert(md5Str);

	char cfgStr[1024];
	memset(cfgStr, 0, sizeof(cfgStr));
	// write config data
	// 注意:此处给BitRate所留的空间是“BitRate=000000000”
	sprintf(cfgStr, "BitRate=000000000\nBlockSize=%d\nChannelName=%s\nResourceHash=%s\nDataLength=%d\nData=", 
		BLOCK_SIZE, chnlName.data(), md5Str, 
		sizeof(audioType) + audioType.cbFormat + sizeof(videoType) + videoType.cbFormat);
	delete [] md5Str;

	int totalLen = strlen(cfgStr);
	// write media type
	memcpy(cfgStr+totalLen, temp, sizeof(videoType)+videoType.cbFormat+sizeof(audioType)+audioType.cbFormat);
	totalLen += sizeof(videoType)+videoType.cbFormat+sizeof(audioType)+audioType.cbFormat;

	if(INVALID_SET_FILE_POINTER == SetFilePointer(configHandle, 0, 0, FILE_BEGIN))
		return false;
	if(!WriteFile(configHandle, cfgStr, totalLen, &writenBytes, NULL))
		return false;
	if(writenBytes != totalLen)
		return false;

	return true;
}

/*
 *	block content
 *  |offset of first keysample(int32)|offset of first sample(int32)|list of samples|last uncomplete sample|
 *  |sample data| = |header(SampleHeader)|data(...)|
 */
bool ZZLFileWriter::SaveSample(UINT dataOff, const UINT allSize) {
	// at the start of new block, write the offset of next sample
	if(newBlockSize == 0) {
		// 在开头的4个字节写入first keysample offset
		*(UINT*)newBlock = 0;	// 默认值是0
		// 在此后的4个字节写入first sample offset
		*((UINT*)newBlock+1) = sizeof(int)*2;		// 如果开始保存新的Sample,则first sample offset = 8
		if(dataOff > 0)
			*((UINT*)newBlock+1) += allSize-dataOff;// 如果保存前一个sample剩下的部分,则要加上其剩下的长度
		if(*((UINT*)newBlock+1) >= BLOCK_SIZE)		// 如果此sample剩下的部分长度超过当前Block,则用UINT_MAX表示
			*((UINT*)newBlock+1) = UINT_MAX;

		newBlockSize += sizeof(int)*2;
	}

	if(	*(UINT*)(newBlock) == 0 &&		// 当前Block尚未记录FirstKeySampleOffset
		dataOff == 0 &&					// 一个新的Sample
		firstKeySampleTime &&			// KeySample的时间
		newBlockSize+sizeof(SampleHeader) < BLOCK_SIZE) // SampleHeader刚好保存在当前Block中
	{
		// 在开头的4个字节记录FirstKeySampleOffset, sizeof(UINT)*2是start在SampleHeader中的位置
		*(UINT*)(newBlock) = newBlockSize+sizeof(UINT)*2;
	}

	// 比较剩余数据与剩余空间
	if(allSize-dataOff >= BLOCK_SIZE-newBlockSize) {
		// 剩余数据超过剩余空间,则填满并保存当前Block,并继续存储剩下的数据
		memcpy(newBlock+newBlockSize, sampleBuffer+dataOff, BLOCK_SIZE-newBlockSize);
		dataOff += BLOCK_SIZE-newBlockSize;

		newBlockSize = 0;	// 开始新的Block
		// 保存旧的Block
		if(!SaveBlock(newBlock, BLOCK_SIZE))
			return false;

		// 继续保存剩余的数据
		if(allSize-dataOff > 0)
			return SaveSample(dataOff, allSize);
	}
	else {
		// 剩余数据小于剩余空间,复制并等待下一个Sample

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -