www.pudn.com > mysee.zip > 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(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(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::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; 
}