📄 zzlfilewriter.cpp
字号:
/*
* 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 + -