📄 buffermgr.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
*
*/
// BufferMgr.cpp: implementation of the BufferMgr class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "BufferMgr.h"
#include "CaptureServer.h"
#include "LogMgr.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
BufferMgr::BufferMgr(CaptureServer* c) : cs(c), blockCount(0), newBlock(0), newBlockSize(0),
blockArray(0), blockSize(0), maxBlockNum(0), maxBlockID(0),
firstKeySample(0), sampleBuffer(0), sampleBufferSize(0) {
sampleStartTime = 0;
bSwitchMedia = false;
// TEST: 检测时光倒退的问题
lastSampleStart = 0;
lastRecvSampleTime = 0;
startseconds = 0;
RemoveOldTmpFile();
bShouldSave = FALSE;
bShouldConnect = FALSE;
char buf[MAX_PATH+1];
DWORD res = GetTempPath(MAX_PATH, buf);
bufferPath = buf;
UINT uni = GetTickCount();
GetTempFileName(bufferPath.data(), "#CD", uni, buf);
bufferPath = buf;
if(!ExCreateFile(hBufferFile, buf, CREATE_ALWAYS))
return;
ExSetFileSize(hBufferFile, 0);
newBlock = new BYTE[BLOCK_SIZE];
maxBlockNum = BUFFER_SPACE / BLOCK_SIZE;
blockArray = new UINT[maxBlockNum];
for(UINT i = 0; i < maxBlockNum; i++)
blockArray[i] = UINT_MAX;
blockSize = new UINT[maxBlockNum];
}
BufferMgr::~BufferMgr() {
CAutoLock lock(&bufferfile_cs);
StopSave();
CloseHandle(hBufferFile);
DeleteFile(bufferPath.data());
SAFE_ARRAYDELETE(blockArray);
SAFE_ARRAYDELETE(blockSize);
SAFE_ARRAYDELETE(newBlock);
SAFE_ARRAYDELETE(sampleBuffer);
}
void BufferMgr::RemoveOldTmpFile() {
WIN32_FIND_DATA fileData;
char buf[MAX_PATH+1];
DWORD res = GetTempPath(MAX_PATH, buf);
string match = buf;
match.append("#CD*.tmp");
HANDLE hFind = FindFirstFile(match.data(), &fileData);
if(hFind == INVALID_HANDLE_VALUE)
return;
while(1) {
string path = buf;
path.append(fileData.cFileName);
DeleteFile(path.data());
if(!FindNextFile(hFind, &fileData)) {
if(GetLastError() == ERROR_NO_MORE_FILES)
break;
else
return;
}
}
FindClose(hFind);
}
void BufferMgr::StartSave() {
bShouldSave = TRUE;
}
void BufferMgr::StopSave() {
bShouldSave = FALSE;
if(blockCount > 0) {
// 将已经存储的数据清除
blockCount = 0;
for(UINT i = 0; i < maxBlockNum; i++)
blockArray[i] = UINT_MAX;
}
maxBlockID = 0;
firstKeySample = FALSE;
}
/*
* block content
* |offset of first keysample(int32)|offset of first sample(int32)|list of samples|last uncomplete sample|
* |sample data| = |header(SampleHeader)|data(...)|
*/
BOOL BufferMgr::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
firstKeySample && // 这个Sample是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(!SaveNewBlock(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 BufferMgr::PutSample(const SampleHeader& header, BYTE* pData, LogMgr* log) {
CAutoLock lock(&bufferfile_cs);
if(bSwitchMedia) {
bSwitchMedia = false;
// 因为切换编码,首先填入“新节目标志”Sample
SampleHeader header;
memset(&header, 0, sizeof(SampleHeader));
header.size = sizeof(SampleHeader);
header.length = 0xffffffff;
header.start = 0xffffffffffffffff;
header.bSyncPoint = 1;
// copy sample header into sample buffer
if(header.size > sampleBufferSize) {
SAFE_ARRAYDELETE(sampleBuffer);
sampleBuffer = new BYTE[header.size];
sampleBufferSize = header.size;
}
memcpy(sampleBuffer, &header, sizeof(SampleHeader));
if(!SaveSample(0, header.size))
return FALSE;
}
// 记录接收到Sample的时间
lastRecvSampleTime = time(NULL);
time_t nowtime;
time(&nowtime);
if(startseconds == 0)
startseconds = nowtime;
if((nowtime - startseconds < TIME4WAIT2STORE))
return TRUE;
bShouldConnect = TRUE;
if(!bShouldSave)
return TRUE;
if(!pData || header.size > 1024*1024)
return FALSE;
char tmpStr[96];
_i64toa(header.start, tmpStr, 10);
_i64toa(header.length+header.start, tmpStr+32, 10);
//log->StatusOut("recv %s sample start: %s end: %s, put at %d", header.bAudioSample?"audio":"video",
// tmpStr, tmpStr+32, newBlockSize);
if(sampleStartTime == 0) {
time_t temp;
time(&temp);
sampleStartTime = (LONGLONG)temp*10000000;
DbgLog((LOG_TRACE, 5, TEXT("此刻 %s!"), ctime(&temp)));
// 校正时间,因为开始录制的时间并不是此刻,而是打在sample中的时间。
sampleStartTime -= header.start;
temp = static_cast<time_t>(sampleStartTime/10000000);
DbgLog((LOG_TRACE, 5, TEXT("实际开始采集的时间 %s!"), ctime(&temp)));
}
if(header.bAudioSample) {
if(header.start+header.length <= lastSampleStart) {
char tmpStr[96];
_i64toa(lastSampleStart, tmpStr, 10);
_i64toa(header.start, tmpStr+32, 10);
_i64toa(header.start+header.length, tmpStr+64, 10);
DbgLog((LOG_TRACE, 5, TEXT("哇!时光倒流,从%s变到了%s->%s!"), tmpStr, tmpStr+32, tmpStr+64));
}
lastSampleStart = header.start;
}
// record time of the frist keysample of current block
if(firstKeySample == FALSE && header.bSyncPoint)
firstKeySample = TRUE;
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));
if(!cs->GetIsAudioOnly()) {
((SampleHeader*)sampleBuffer)->start += sampleStartTime;
}
// copy sample data into sample buffer;
memcpy(sampleBuffer+sizeof(SampleHeader), pData, header.size-sizeof(SampleHeader));
BOOL ret = SaveSample(0, header.size);
return ret;
}
BOOL BufferMgr::SaveNewBlock(BYTE* buf, UINT size) {
if(!buf || size > BLOCK_SIZE)
return FALSE;
BOOL ret = TRUE;
UINT minBlock = UINT_MAX;
UINT minIndex = 0;
for(UINT i = 0; i < maxBlockNum; i++) {
if(blockArray[i] == UINT_MAX) // empty block
break;
if(minBlock > blockArray[i]) {
minBlock = blockArray[i];
minIndex = i;
}
}
if(i == maxBlockNum) {// no empty block found,replace the min block
i = minIndex;
}
if(!ExSetFilePointer(hBufferFile, i*BLOCK_SIZE))
ret = FALSE;
else if(!ExWriteFile(hBufferFile, buf, size))
ret = FALSE;
if(ret) {
blockArray[i] = blockCount;
blockSize[i] = size;
blockCount++; // blockCount-1 is blockID
// 等待下一个Block的first 可以sample
firstKeySample = FALSE;
maxBlockID = max(blockArray[i], maxBlockID);
// 如果当前块有编码类型,则保存当前块的编码类型
if(!currMediaData.IsEmpty()) {
mediaMap.insert(pair<UINT, MediaData>(blockArray[i], currMediaData));
memset(&currMediaData.audioType, 0, sizeof(currMediaData.audioType));
memset(&currMediaData.videoType, 0, sizeof(currMediaData.videoType));
delete [] currMediaData.audioData;
currMediaData.audioData = NULL;
delete [] currMediaData.videoData;
currMediaData.videoData = NULL;
}
}
return ret;
}
BOOL BufferMgr::GetBlock(UINT blockID, BYTE* buf, UINT& size) {
CAutoLock lock(&bufferfile_cs);
if(!buf || blockID > GetPlayingBlock())
return FALSE;
BOOL ret = TRUE;
for(UINT i = 0; i < maxBlockNum; i++) {
if(blockArray[i] == blockID && blockArray[i] != UINT_MAX)
break;
}
if(i == maxBlockNum) // not found
ret = FALSE;
if(ret) {
size = blockSize[i];
if(!ExSetFilePointer(hBufferFile, i*BLOCK_SIZE))
ret = FALSE;
else if(!ExReadFile(hBufferFile, buf, size))
ret = FALSE;
}
return ret;
}
// 获取一个块的编码类型
BOOL BufferMgr::GetMediaData(UINT blockID, MediaData& data) {
map<UINT, MediaData>::const_iterator cit = mediaMap.find(blockID);
if(cit == mediaMap.end())
return FALSE;
if(cit->second.IsEmpty()) {
ASSERT(0);
return FALSE;
}
data = cit->second;
return TRUE;
}
// 标志切换编码的Block
BOOL BufferMgr::AttachMediaDataToCurrentBlock(const TVMEDIATYPESECTION& tv, const BYTE* data, BOOL bAudio, LogMgr* log) {
CAutoLock lock(&bufferfile_cs);
if(bAudio) {
currMediaData.audioType = tv;
currMediaData.audioData = new BYTE[currMediaData.audioType.cbFormat];
memcpy(currMediaData.audioData, data, currMediaData.audioType.cbFormat);
}
else {
currMediaData.videoType = tv;
currMediaData.videoData = new BYTE[currMediaData.videoType.cbFormat];
memcpy(currMediaData.videoData, data, currMediaData.videoType.cbFormat);
}
// 清空当前block,准备开始保存新的编码方式的block
newBlockSize = 0;
bSwitchMedia = true;
log->StatusOut("attach %s data to block %d.", bAudio?"audio":"video", blockCount);
return TRUE;
}
BOOL BufferMgr::ExSetFileSize(HANDLE handle, int size) {
BOOL ret = TRUE;
if(!ExSetFilePointer(handle, size))
ret = FALSE;
else if(0 == SetEndOfFile(handle)) {
ret = FALSE;
}
return ret;
}
int BufferMgr::ExReadFile(HANDLE handle, LPVOID buf, int toBeRead) {
int readCount = 0;
DWORD tmpRead;
while(readCount < toBeRead) {
BOOL success = ReadFile(
handle, // file handler
(char*)buf+readCount, // current buffer position
toBeRead - readCount, // remaining bytes to be read
&tmpRead, // read bytes
NULL); // not overlaped
if(!success)
return FALSE;
if(tmpRead == 0) // no more data
return FALSE;
readCount += tmpRead;
}
return readCount;
}
BOOL BufferMgr::ExWriteFile(HANDLE handle, LPVOID buf, int toBeWrite) {
int writeCount = 0;
DWORD tmpWrite;
while(writeCount < toBeWrite) {
BOOL success = WriteFile(
handle, // file handler
(char*)buf+writeCount, // current buffer position
toBeWrite-writeCount, // remaining bytes to be read
&tmpWrite, // written bytes
NULL); // not overlaped
if(!success) {
return FALSE;
}
writeCount += tmpWrite;
}
return writeCount;
}
BOOL BufferMgr::ExSetFilePointer(HANDLE handle, int offset) {
if(-1 == SetFilePointer(handle,
offset,
NULL,
FILE_BEGIN)) {
return FALSE;
}
return TRUE;
}
BOOL BufferMgr::ExCreateFile(HANDLE& handle,
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile) {
handle = CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
if(handle == INVALID_HANDLE_VALUE) {
return FALSE;
}
return TRUE;
}
BOOL BufferMgr::ExCreateFile(HANDLE& handle, LPCTSTR lpFileName, DWORD dwCreationDisposition) {
handle = CreateFile(lpFileName,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
dwCreationDisposition,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(handle == INVALID_HANDLE_VALUE) {
return FALSE;
}
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -