ZZLFileWriter.cpp
资源名称:p2p_vod.rar [点击查看]
上传用户:liguizhu
上传日期:2015-11-01
资源大小:2422k
文件大小:19k
源码类别:
P2P编程
开发平台:
Visual C++
- /*
- * 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=000000000nBlockSize=%dnChannelName=%snResourceHash=%snDataLength=%dnData=",
- 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
- memcpy(newBlock+newBlockSize, sampleBuffer+dataOff, allSize-dataOff);
- newBlockSize += allSize-dataOff;
- }
- return true;
- }
- bool ZZLFileWriter::PutSample(const SampleHeader& header, BYTE* pData) {
- if(header.size > 1024*1024)
- return false;
- bool ret = true;
- EnterCriticalSection(&zzlfile_cs);
- char tmpStr[96];
- _i64toa(header.start, tmpStr, 10);
- _i64toa(header.length+header.start, tmpStr+32, 10);
- DbgLog((LOG_TRACE, 5, TEXT("start: %s end: %s"), tmpStr, tmpStr+32));
- // record first sample time
- if(firstSampleTime == 0)
- firstSampleTime = (time_t)(header.start/10000000);
- // record first keysample time
- //区分单音频和视频的情况
- if (firstKeySampleTime == 0 && TRUE == mbIsSingleAudio && header.start != 0xffffffffffffffff/*非节目起始标志Sample*/)
- {
- firstKeySampleTime = (time_t)(header.start/10000000);
- }
- else if(firstKeySampleTime == 0 && header.bSyncPoint && header.start != 0xffffffffffffffff/*非节目起始标志Sample*/)
- {
- firstKeySampleTime = (time_t)(header.start/10000000);
- }
- if(header.size > sampleBufferSize) {
- SAFE_ARRAYDELETE(sampleBuffer);
- sampleBuffer = new BYTE[header.size];
- sampleBufferSize = header.size;
- }
- // copy sample header into sample buffer
- memcpy(sampleBuffer, &header, sizeof(SampleHeader));
- // copy sample data into sample buffer;
- if(header.size-sizeof(SampleHeader)) {
- assert(pData);
- memcpy(sampleBuffer+sizeof(SampleHeader), pData, header.size-sizeof(SampleHeader));
- }
- #ifndef GENERATE_SMALL_ZZL
- if(header.start > 0 && header.start != 0xffffffffffffffff/*非节目起始标志Sample*/) {
- if(header.bAudioSample) {
- //检查音频时间是否回滚
- if (header.start > mLLMaxAudioTime)
- {
- mLLMaxAudioTime = header.start;
- }
- else
- {
- //char lstr[1024];
- //sprintf(lstr, "这段音频的数据应该已经播放过了,不能进行直播%I64D",header.start);
- MessageBox(NULL,"这段音频的数据应该已经播放过了,不能进行直播", "错误", MB_OK|MB_ICONSTOP);
- return FALSE;
- }
- // calculate total size of audio samples
- audioDataSize += header.size;
- // set start&end time of audio samples
- if(header.start > audioEndTime || header.start < audioStartTime) {
- if(audioStartTime == 0 || header.start < audioStartTime) {
- audioStartTime = header.start;
- }
- if(header.start+header.length > audioEndTime)
- audioEndTime = header.start+header.length;
- }
- assert(audioStartTime <= header.start);
- }
- else {
- //检查视频时间是否回滚
- if (header.start > mLLMaxVideoTime)
- {
- mLLMaxVideoTime = header.start;
- }
- else
- {
- MessageBox(NULL,"这段视频的数据应该已经播放过了,不能进行直播", "错误", MB_OK|MB_ICONSTOP);
- return FALSE;
- }
- // calculate total size of video samples
- videoDataSize += header.size;
- // set start&end time of video samples
- if(header.start > videoEndTime || header.start < videoStartTime) {
- if(videoStartTime == 0 || header.start < videoStartTime) {
- videoStartTime = header.start;
- }
- if(header.start+header.length > videoEndTime)
- videoEndTime = header.start+header.length;
- }
- assert(videoStartTime <= header.start);
- // 检查video sample和audio sample是否时间相差过多,相差过多的节目不适合进行直播!
- if(videoEndTime > 0 && audioEndTime > 0)
- {
- if(videoEndTime > audioEndTime+10*10000000
- || audioEndTime > videoEndTime+10*10000000)
- {
- ::MessageBox(NULL,
- "现在视频和音频已经不同步,相差超过10s,这样压缩出来的节目是不能进行直播的!",
- "错误", MB_OK|MB_ICONSTOP);
- ret = false;
- }
- }
- }
- }
- #endif
- if(ret)
- ret = SaveSample(0, header.size);
- LeaveCriticalSection(&zzlfile_cs);
- return ret;
- }
- bool ZZLFileWriter::SaveBlock(const PBYTE data, const UINT size) {
- if(zzlHandle == INVALID_HANDLE_VALUE)
- return false;
- DWORD writenBytes = 0;
- if(INVALID_SET_FILE_POINTER == SetFilePointer(zzlHandle, dataOffset + blockCount*BLOCK_SIZE, 0, FILE_BEGIN))
- return false;
- if(!WriteFile(zzlHandle, data, size, &writenBytes, NULL))
- return false;
- if(writenBytes != size)
- return false;
- // write min&max keySample to zzl file
- if(firstSampleTime > 0) {
- if(firstSampleTime > endTime || firstSampleTime < startTime) {
- if(startTime == 0 || firstSampleTime < startTime) {
- startTime = firstSampleTime;
- }
- if(firstSampleTime > endTime)
- endTime = firstSampleTime;
- if(INVALID_SET_FILE_POINTER == SetFilePointer(zzlHandle, dataOffset-sizeof(startTime)-sizeof(endTime), 0, FILE_BEGIN))
- return false;
- // write start & end Time
- if(!WriteFile(zzlHandle, &startTime, sizeof(startTime), &writenBytes, NULL))
- return false;
- if(writenBytes != sizeof(startTime))
- return false;
- if(!WriteFile(zzlHandle, &endTime, sizeof(endTime), &writenBytes, NULL))
- return false;
- if(writenBytes != sizeof(endTime))
- return false;
- }
- assert(startTime <= firstSampleTime);
- }
- if(firstKeySampleTime > 0) {
- // write keysample to keysample file
- if(INVALID_SET_FILE_POINTER == SetFilePointer(keySampleHandle, 0, 0, FILE_END)) // seek to end of file
- return false;
- // write blockID
- if(!WriteFile(keySampleHandle, &blockCount, sizeof(blockCount), &writenBytes, NULL))
- return false;
- if(writenBytes != sizeof(blockCount))
- return false;
- // write keysample
- if(!WriteFile(keySampleHandle, &firstKeySampleTime, sizeof(firstKeySampleTime), &writenBytes, NULL))
- return false;
- if(writenBytes != sizeof(firstKeySampleTime))
- return false;
- }
- // write block data to data file
- if(blockCount/BLOCK_OF_DATA_FILE > dataFileCount) {
- dataFileCount = blockCount/BLOCK_OF_DATA_FILE;
- CloseHandle(dataHandle);
- 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;
- }
- if(INVALID_SET_FILE_POINTER == SetFilePointer(dataHandle, (blockCount%BLOCK_OF_DATA_FILE)*BLOCK_SIZE, 0, FILE_BEGIN))
- return false;
- if(!WriteFile(dataHandle, data, size, &writenBytes, NULL))
- return false;
- if(writenBytes != size)
- return false;
- firstKeySampleTime = 0;
- firstSampleTime = 0;
- blockCount++;
- // 将最新的码率统计写入配置文件
- if(!SaveBitRate())
- return false;
- return true;
- }
- double ZZLFileWriter::GetBitRate() {
- // 根据起始Sample和最近一个Sample的时间戳以及数据的大小,分别计算视频音频的码率,然后加起来就是总的码率
- double bitRate = 0.0f;
- if(videoEndTime-videoStartTime > 0)
- bitRate += ((double)videoDataSize)/1024/((videoEndTime-videoStartTime)/10000000);
- if(audioEndTime-audioStartTime > 0)
- bitRate += ((double)audioDataSize)/1024/((audioEndTime-audioStartTime)/10000000);
- return bitRate;
- }
- //设置是否是单音频
- void ZZLFileWriter::SetIsSingleAudio(BOOL abAudio)
- {
- mbIsSingleAudio = abAudio;
- }
- bool ZZLFileWriter::SaveBitRate() {
- if(configHandle == INVALID_HANDLE_VALUE)
- return false;
- DWORD writenBytes = 0;
- char temp[64];
- if(INVALID_SET_FILE_POINTER == SetFilePointer(configHandle, 0, 0, FILE_BEGIN))
- return false;
- sprintf(temp, "BitRate=%.4f", GetBitRate());
- // 注意:配置文件中所留的空间是“BitRate=000000000”,所以要防止超过这个长度
- temp[strlen("BitRate=000000000")-1] = 0;
- // 检查一下,某些情况下,最后码率是0,很奇怪
- if(blockCount > 20) {
- assert(strcmp(temp, "BitRate=000000000") != 0);
- }
- if(!WriteFile(configHandle, temp, strlen(temp), &writenBytes, NULL))
- return false;
- if(writenBytes != strlen(temp))
- return false;
- return true;
- }
- bool ZZLFileWriter::RemoveOldTmpFile(string path) {
- WIN32_FIND_DATA fileData;
- string match = path;
- match.append("\*");
- HANDLE hFind = FindFirstFile(match.data(), &fileData);
- if(hFind == INVALID_HANDLE_VALUE)
- return true;
- bool firstDeleteFile = true;
- while(1) {
- bool shouldDelete = false;
- if(stricmp(fileData.cFileName, "config") == 0) // 配置文件
- shouldDelete = true;
- else if(stricmp(fileData.cFileName, "keysample") == 0) // 关键帧文件
- shouldDelete = true;
- else if(stricmp(fileData.cFileName, (chnlName+".zzl").data()) == 0) // zzl文件
- shouldDelete = true;
- else {
- // 如果是纯数字,说明是数据文件
- int itemp = atoi(fileData.cFileName);
- char stemp[64];
- itoa(itemp, stemp, 10);
- if(itemp >= 0 && stricmp(fileData.cFileName, stemp) == 0)
- shouldDelete = true;
- }
- if(shouldDelete) {
- if(firstDeleteFile) {
- firstDeleteFile = false;
- TCHAR temp[1024];
- //sprintf(temp, "名字是 %s 的节目已经存在于目录 %s ,请问要删除旧的节目数据吗?(可能是被遗忘的有用数据)", chnlName.data(), path.data());
- //int answer = MessageBox(NULL, temp, "警告", MB_YESNO|MB_ICONQUESTION);
- int answer = IDYES;
- if(answer != IDNO) {
- //MessageBox(NULL, "你选择删除旧的节目数据,新的节目开始压缩!", "通知", MB_OK|MB_ICONINFORMATION);
- TraceLog1("现在覆盖旧的节目数据,新的节目开始压缩!n");
- }
- else {
- sprintf(temp, "你选择保存旧的节目数据,此次压缩将被取消,请在转移目录 %s 下的数据之后,重新开始压缩。", path.data());
- MessageBox(NULL, temp, "通知", MB_OK|MB_ICONINFORMATION);
- return false;
- }
- }
- string temp = path+"\";
- temp.append(fileData.cFileName);
- DeleteFile(temp.data());
- }
- if(!FindNextFile(hFind, &fileData)) {
- if(GetLastError() == ERROR_NO_MORE_FILES)
- break;
- else {
- assert(0);
- return true;
- }
- }
- }
- FindClose(hFind);
- return true;
- }