www.pudn.com > filterasfmuxer_2008_01_23.rar > CASFMuxer.cpp


// 
// CASFMuxer.cpp 
// 
/** 
 ** Copyright (C) 2005 EnjoyView Inc., all rights reserved. 
 **           Your View, Our Passion. Just Enjoy It! 
 ** 
 **            http://spaces.msn.com/members/jemylu 
 ** 
 **/ 
 
/*************************************************************************/ 
 
#include  
#include  
#include "CFilterASFMuxer.h" 
#include "CASFMuxer.h" 
 
//////////////////////////////////////////////////////////////////////////// 
CASFMuxer::CASFMuxer(CFilterASFMuxer * inFilter) 
{ 
	mFilter = inFilter; 
	wcscpy(mDestFile, L""); 
	 
	mProfile = NULL; 
	mWriter  = NULL; 
	mIHeaderInfo      = NULL; 
	mIWriterAdvanced  = NULL; 
	mIWriterAdvanced3 = NULL; 
	mProfileManager   = NULL; 
	WMCreateProfileManager(&mProfileManager); 
 
	mAudioTime = 0; 
	mVideoTime = 0; 
 
#ifdef _DEBUG 
	m_fpDump = fopen("C:\\asfmux.txt", "w"); 
#endif // _DEBUG 
} 
 
CASFMuxer::~CASFMuxer() 
{ 
	SAFE_RELEASE(mProfile); 
	SAFE_RELEASE(mProfileManager); 
	SAFE_RELEASE(mIHeaderInfo); 
	SAFE_RELEASE(mIWriterAdvanced); 
	SAFE_RELEASE(mIWriterAdvanced3); 
	SAFE_RELEASE(mWriter); 
 
#ifdef _DEBUG 
	if (m_fpDump) 
	{ 
		fclose(m_fpDump); 
		m_fpDump = NULL; 
	} 
#endif // _DEBUG 
} 
 
HRESULT CASFMuxer::SetDestFile(LPCOLESTR pFile) 
{ 
#ifdef _DEBUG 
	char szFile[MAX_PATH]; 
	WideCharToMultiByte(CP_ACP, 0, pFile, -1, szFile, MAX_PATH, 0, 0); 
#endif // _DEBUG 
 
	wcscpy(mDestFile, pFile); 
	return NOERROR; 
} 
 
HRESULT CASFMuxer::GetDestFile(LPOLESTR * ppFile) 
{ 
    *ppFile = (LPOLESTR) QzTaskMemAlloc(sizeof(WCHAR) * (1+wcslen(mDestFile))); 
 
    if (*ppFile != NULL)  
    { 
		wcscpy(*ppFile, mDestFile); 
		return NOERROR; 
	} 
	return E_OUTOFMEMORY; 
} 
 
 
HRESULT CASFMuxer::CreateProfile(void) 
{ 
	if (!mProfileManager) 
	{ 
		return E_FAIL; 
	} 
 
	HRESULT hr = NOERROR; 
	SAFE_RELEASE(mProfile); 
	IWMStreamConfig * pConfig = NULL; 
	IWMMediaProps *   pProps  = NULL; 
	 
	do  
	{ 
		// Create an empty profile object 
		hr = mProfileManager->CreateEmptyProfile(WMT_VER_9_0, &mProfile); 
		BREAK_IF_FAILED(hr); 
 
		// If audio input pin is connected... 
		if (mFilter->IsAudioPinConnected()) 
		{		 
			// Create a new audio stream 
			hr = mProfile->CreateNewStream(WMMEDIATYPE_Audio, &pConfig); 
			BREAK_IF_FAILED(hr); 
 
			// Config this audio stream according to the audio pin's media type 
			hr = pConfig->QueryInterface(IID_IWMMediaProps, (void**)&pProps); 
			BREAK_IF_FAILED(hr); 
 
			CMediaType& mt = mFilter->GetAudioMediaType(); 
			WM_MEDIA_TYPE wmMediaType; 
			ZeroMemory(&wmMediaType, sizeof(WM_MEDIA_TYPE)); 
			CopyMediaType((AM_MEDIA_TYPE *)&wmMediaType, &mt); 
			wmMediaType.majortype = MEDIATYPE_Audio; 
			if (wmMediaType.pUnk) 
			{ 
				wmMediaType.pUnk->Release(); 
				wmMediaType.pUnk = NULL; 
			}		 
 
			// Verify the bitrate 
			WAVEFORMATEX * pWave = (WAVEFORMATEX *) wmMediaType.pbFormat; 
			DWORD bitrate = 0; 
			if (pWave->nAvgBytesPerSec > 0) 
			{ 
				bitrate = pWave->nAvgBytesPerSec * 8; 
			} 
			else 
			{ 
				bitrate = pWave->nSamplesPerSec * pWave->nChannels * pWave->wBitsPerSample; 
				pWave->nAvgBytesPerSec = bitrate / 8; 
			} 
			hr = pProps->SetMediaType(&wmMediaType); 
			FreeMediaType((AM_MEDIA_TYPE&) wmMediaType); 
			BREAK_IF_FAILED(hr); 
 
			pConfig->SetBitrate(bitrate); 
			pConfig->SetBufferWindow(3000); 
			pConfig->SetStreamNumber(cAudioStreamNum); 
 
			// Add this audio stream to the profile 
			hr = mProfile->AddStream(pConfig); 
			BREAK_IF_FAILED(hr); 
		} 
 
		SAFE_RELEASE(pProps); 
		SAFE_RELEASE(pConfig); 
		// If video input pin is connected... 
		if (mFilter->IsVideoPinConnected()) 
		{ 
			// Create a new video stream 
			 
			hr = mProfile->CreateNewStream(WMMEDIATYPE_Video, &pConfig); 
			BREAK_IF_FAILED(hr); 
 
			// Config this video stream according to the video pin's media type 
			hr = pConfig->QueryInterface(IID_IWMMediaProps, (void**)&pProps); 
			BREAK_IF_FAILED(hr); 
 
			CMediaType& mt = mFilter->GetVideoMediaType(); 
			WM_MEDIA_TYPE wmMediaType; 
			ZeroMemory(&wmMediaType, sizeof(WM_MEDIA_TYPE)); 
			CopyMediaType((AM_MEDIA_TYPE *)&wmMediaType, &mt); 
 
			wmMediaType.majortype = MEDIATYPE_Video; 
 
			//wmMediaType.bFixedSizeSamples = TRUE; 
			//wmMediaType.bTemporalCompression = FALSE;// Harley 2007.07.05 
 
			if (wmMediaType.pUnk) 
			{ 
				wmMediaType.pUnk->Release(); 
				wmMediaType.pUnk = NULL; 
			}			 
			 
			// Get the video bitrate which has been verified! 
			DWORD bitrate = GetVideoBitrate(wmMediaType); 
			hr = pProps->SetMediaType(&wmMediaType);//+++ 
			FreeMediaType((AM_MEDIA_TYPE&) wmMediaType); 
			BREAK_IF_FAILED(hr);// +++  Harley 2007.07.02 temp 
 
			pConfig->SetBitrate(bitrate); 
			pConfig->SetBufferWindow(3000);// +++  Harley 2007.07.02 temp 
			pConfig->SetStreamNumber(cVideoStreamNum);// Harley 2007.07.05 
 
			// Add this video stream to the profile 
			hr = mProfile->AddStream(pConfig); 
			BREAK_IF_FAILED(hr); 
		}		 
 
	} while (FALSE); 
 
	SAFE_RELEASE(pProps); 
	SAFE_RELEASE(pConfig); 
 
	return hr; 
} 
 
// Verify the bitrate member of the media type, guess it if necessary 
DWORD CASFMuxer::GetVideoBitrate(WM_MEDIA_TYPE& inMediaType) 
{ 
	DWORD  bitrate   = 0; 
	double framerate = 25.; 
	if (inMediaType.formattype == FORMAT_VideoInfo) 
	{ 
		VIDEOINFOHEADER * pvi = (VIDEOINFOHEADER *) inMediaType.pbFormat; 
 
		// 1. Verify the frame rate 
		if (pvi->AvgTimePerFrame > 0) 
		{ 
			framerate = double(1. * UNITS / pvi->AvgTimePerFrame); 
		} 
		else 
		{ 
			pvi->AvgTimePerFrame = 400000; // default to 25fps 
		} 
 
		// 修改bitrate判断方式 by charles 2008-1-22 
		// 由于过来的数据是压缩过的数据,已在压缩层作了码流控制,在这再作码流控制,会造成丢包现象。 
		// 在这里,给码流设置一个比较高的,不大可能超过的值,这样就可以正常使用上层的码流控制。 
		bitrate = DWORD(pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight * 
					pvi->bmiHeader.biBitCount * framerate); 
		pvi->dwBitRate = bitrate; 
		// 2. Verify the bitrate 
		//if (pvi->dwBitRate > 0) 
		//{ 
		//	bitrate = pvi->dwBitRate; 
		//} 
		//else 
		//{ 
		//	if (IsRawMediaType(inMediaType)) 
		//	{ 
		//		// Calculate 
		//		bitrate = DWORD(pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight * 
		//			pvi->bmiHeader.biBitCount * framerate); 
		//	} 
		//	else 
		//	{ 
		//		// Guess 
		//		bitrate = DWORD(pvi->bmiHeader.biWidth * pvi->bmiHeader.biHeight * 
		//			framerate * 0.4); 
		//	} 
		//	pvi->dwBitRate = bitrate; 
		//}  
	} 
	else if (inMediaType.formattype == FORMAT_MPEG2Video) 
	{ 
		MPEG2VIDEOINFO * pvi = (MPEG2VIDEOINFO *) inMediaType.pbFormat; 
		if (pvi->hdr.AvgTimePerFrame > 0) 
		{ 
			framerate = double(1. * UNITS / pvi->hdr.AvgTimePerFrame); 
		} 
		else 
		{ 
			pvi->hdr.AvgTimePerFrame = 400000; // default to 25fps 
		} 
 
		if (pvi->hdr.dwBitRate > 0) 
		{ 
			bitrate = pvi->hdr.dwBitRate; 
		} 
		else 
		{ 
			bitrate = DWORD(pvi->hdr.bmiHeader.biWidth * pvi->hdr.bmiHeader.biHeight * 
				framerate * 0.6); 
			pvi->hdr.dwBitRate = bitrate; 
		} 
	} 
	else 
	{ 
		// Unexpected! 
		bitrate = 500000; 
	} 
 
	return bitrate; 
} 
 
BOOL CASFMuxer::IsRawMediaType(WM_MEDIA_TYPE& inMediaType) 
{ 
	if (inMediaType.subtype == MEDIASUBTYPE_RGB32 || 
		inMediaType.subtype == MEDIASUBTYPE_RGB24 || 
		inMediaType.subtype == MEDIASUBTYPE_RGB565 || 
		inMediaType.subtype == MEDIASUBTYPE_RGB555 || 
		inMediaType.subtype == MEDIASUBTYPE_RGB8 || 
		inMediaType.subtype == MEDIASUBTYPE_RGB4 || 
		inMediaType.subtype == MEDIASUBTYPE_RGB1 ||		 
		inMediaType.subtype == MEDIASUBTYPE_IYUV ||  // the same as 'YV12' 
		inMediaType.subtype == MEDIASUBTYPE_UYVY || 
		inMediaType.subtype == MEDIASUBTYPE_YUY2 || 
		inMediaType.subtype == MEDIASUBTYPE_YV12 || 
		inMediaType.subtype == MEDIASUBTYPE_YVU9 || 
		inMediaType.subtype == MEDIASUBTYPE_YVYU || 
		inMediaType.subtype == MEDIASUBTYPE_PCM) 
	{ 
		return TRUE; 
	} 
	return FALSE; 
} 
 
HRESULT CASFMuxer::CreateWriter(void) 
{ 
	if (mWriter) 
	{ 
		return NOERROR; 
	} 
 
	HRESULT hr = NOERROR; 
	do 
	{ 
		hr = WMCreateWriter(NULL, &mWriter); 
		BREAK_IF_FAILED(hr); 
 
		hr = mWriter->QueryInterface(IID_IWMWriterAdvanced, (void**)&mIWriterAdvanced); 
		BREAK_IF_FAILED(hr); 
 
		hr = mWriter->QueryInterface(IID_IWMHeaderInfo, (void**)&mIHeaderInfo); 
		BREAK_IF_FAILED(hr); 
 
		// Extra interfaces 
		mWriter->QueryInterface(IID_IWMWriterAdvanced3, (void**)&mIWriterAdvanced3); 
 
	} while (FALSE); 
 
	return hr; 
} 
 
HRESULT CASFMuxer::ConfigWriter(void) 
{ 
	HRESULT hr = NOERROR; 
 
	do 
	{ 
		hr = mWriter->SetProfile(mProfile); 
		BREAK_IF_FAILED(hr); 
 
		//////////////////////////////////////////////////////////////// 
		// Input settings on the writer 
		DWORD cInputs = 0; 
		hr = mWriter->GetInputCount(&cInputs); 
		BREAK_IF_FAILED(hr); 
 
		// Loop through all of the inputs on the writer. For each input,  
		// set the input properties to NULL. This tells the writer not  
		// to encode the samples it receives. 
		for (DWORD i = 0; i < cInputs; i++) 
		{ 
			hr = mWriter->SetInputProps(i, NULL); 
			BREAK_IF_FAILED(hr); 
		} 
		//////////////////////////////////////////////////////////////// 
 
		// Set the destination file 
		hr = mWriter->SetOutputFilename(mDestFile); 
		BREAK_IF_FAILED(hr); 
 
	} while (FALSE); 
 
	return hr; 
} 
 
// Set specified metadata to the writer 
HRESULT CASFMuxer::WriteHeader(const WCHAR * pwszName, WCHAR * pValue) 
{ 
	WORD streamNum = 0; // file-level attribute 
	WORD cbLength  = WORD(wcslen(pValue) * 2); 
	HRESULT		hr = S_OK; 
 
	if (mIHeaderInfo) 
	{ 
		hr = mIHeaderInfo->SetAttribute(streamNum, pwszName,  
			WMT_TYPE_STRING, (const BYTE*) pValue, cbLength); 
		return hr; 
	} 
	return E_FAIL; 
} 
 
HRESULT CASFMuxer::StartStreaming(void) 
{ 
	CAutoLock lockit(&mSyncMux); 
 
	mAudioTime = 0; 
	mVideoTime = 0; 
 
	HRESULT hr = NOERROR; 
	do  
	{ 
		hr = CreateProfile(); 
		BREAK_IF_FAILED(hr); 
 
		hr = CreateWriter(); 
		BREAK_IF_FAILED(hr); 
 
		hr = ConfigWriter(); 
		BREAK_IF_FAILED(hr); 
 
		// Write some attributes 
		WriteHeader(g_wszWMTitle, L"ASF Clip [264 Encoded]"); 
		WriteHeader(g_wszWMDescription, L"Powered by Harley @_@ [www.snda.com]"); 
 
		// Begin writing... 
		hr = mWriter->BeginWriting(); 
 
	} while (FALSE); 
 
	return hr; 
} 
 
HRESULT CASFMuxer::StopStreaming(void) 
{ 
	CAutoLock lockit(&mSyncMux); 
 
	if (mWriter) 
	{ 
		mWriter->EndWriting(); 
 
#ifdef _DEBUG 
		DbgDumpStatistics(cAudioStreamNum); 
		DbgDumpStatistics(cVideoStreamNum); 
#endif // _DEBUG 
	} 
 
	return NOERROR; 
} 
 
// Process audio samples 
// Attention: sample's timestamp must be valid! 
//  And sync-point, discontinuity... 
HRESULT CASFMuxer::ReceiveAudio(IMediaSample * pSample) 
{ 
#ifdef _DEBUG 
	DWORD threadId = GetCurrentThreadId(); 
#endif 
 
	HRESULT hr = NOERROR; 
	REFERENCE_TIME rtStart, rtEnd; 
	hr = pSample->GetTime(&rtStart, &rtEnd); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	} 
 
	{ 
		CAutoLock lockit(&mSyncMux); 
		mAudioTime = rtStart; 
		DbgLog((LOG_TRACE,0,TEXT("Audio sample arrived, time: %I64u"), mAudioTime)); 
 
		QWORD duration = QWORD(rtEnd - rtStart); 
		LONG dataSize  = pSample->GetActualDataLength(); 
		INSSBuffer * pWmSample = NULL; 
		hr = mWriter->AllocateSample(dataSize, &pWmSample); 
		if (SUCCEEDED(hr)) 
		{ 
			PBYTE pSrc, pDest; 
			pSample->GetPointer(&pSrc); 
			pWmSample->GetBuffer(&pDest); 
			memcpy(pDest, pSrc, dataSize); 
			pWmSample->SetLength(dataSize); 
 
			// Send sample to the writer 
			DWORD flags = 0; 
			if (S_OK == pSample->IsSyncPoint()) 
			{ 
				flags |= WM_SF_CLEANPOINT; 
			} 
			if (S_OK == pSample->IsDiscontinuity()) 
			{ 
				flags |= WM_SF_DISCONTINUITY; 
			} 
 
			hr = mIWriterAdvanced->WriteStreamSample(cAudioStreamNum, rtStart,  
				DWORD(rtStart / 10000), duration, flags, pWmSample); 
			pWmSample->Release(); 
		} 
	} 
 
	// Try to keep sync 
	if (mFilter->IsVideoPinConnected()) 
	{ 
		if (mAudioTime - mVideoTime > UNITS) 
		{ 
			Sleep(1); 
			DbgLog((LOG_TRACE,0,TEXT("Audio Sleep..."))); 
		} 
	} 
 
	return hr; 
} 
 
// Process video samples 
// Attention: sample's timestamp must be valid!  
//  And sync-point, discontinuity... 
HRESULT CASFMuxer::ReceiveVideo(IMediaSample * pSample) 
{ 
#ifdef _DEBUG 
	DWORD threadId = GetCurrentThreadId(); 
#endif 
 
	HRESULT hr = NOERROR; 
	REFERENCE_TIME rtStart, rtEnd; 
	hr = pSample->GetTime(&rtStart, &rtEnd); 
	if (FAILED(hr)) 
	{ 
		return hr; 
	} 
 
	{ 
		CAutoLock lockit(&mSyncMux); 
		mVideoTime = rtStart; 
		DbgLog((LOG_TRACE,0,TEXT("---Video sample arrived, time: %I64u"), mVideoTime)); 
 
		QWORD duration = QWORD(rtEnd - rtStart); 
		LONG dataSize  = pSample->GetActualDataLength(); 
		INSSBuffer * pWmSample = NULL; 
		hr = mWriter->AllocateSample(dataSize, &pWmSample); 
 
	 
		if (SUCCEEDED(hr)) 
		{ 
			PBYTE pSrc, pDest; 
			pSample->GetPointer(&pSrc); 
			pWmSample->GetBuffer(&pDest); 
			memcpy(pDest, pSrc, dataSize); 
			pWmSample->SetLength(dataSize); 
 
			// Send sample to the writer 
			DWORD flags = 0; 
			if (S_OK == pSample->IsSyncPoint()) 
			{ 
				flags |= WM_SF_CLEANPOINT; 
			} 
			if (S_OK == pSample->IsDiscontinuity()) 
			{ 
				flags |= WM_SF_DISCONTINUITY; 
			} 
 
			mIWriterAdvanced->SetLiveSource( TRUE ); 
			// 设置同步公差,单位为毫秒,等超过该时间,WriteStreamSample将放弃同步,丢失当前包 by charles 2008-1-22 
			// 默认是3秒,现改为60秒,可用于非实时压缩,为了不丢包。 
			// 在实时压缩时,则应该让其设小一点,让它可以丢包! 
			DWORD dwTime = 60*1000; 
			mIWriterAdvanced->SetSyncTolerance( dwTime ); 
 
			hr = mIWriterAdvanced->WriteStreamSample(cVideoStreamNum, rtStart,  
				(DWORD)(rtStart / 10000), duration, flags, pWmSample); 
 
			pWmSample->Release(); 
		} 
	} 
 
	// Try to keep sync 
	if (mFilter->IsAudioPinConnected()) 
	{ 
		if (mVideoTime - mAudioTime > UNITS) 
		{ 
			Sleep(1); 
			DbgLog((LOG_TRACE,0,TEXT("Video Sleep..."))); 
		} 
	} 
 
	if(!SUCCEEDED(hr)){ 
		int l = 0; 
	} 
	return hr; 
} 
 
 
 
/////////////////////////// Debug utilities ////////////////////////// 
#ifdef _DEBUG 
void CASFMuxer::DbgDumpStatistics(WORD inStreamNum) 
{ 
	HRESULT hr = NOERROR; 
	if (mIWriterAdvanced) 
	{ 
		WM_WRITER_STATISTICS stat; 
		ZeroMemory(&stat, sizeof(stat)); 
		// Get audio's statistics 
		hr = mIWriterAdvanced->GetStatistics(inStreamNum, &stat); 
		if (SUCCEEDED(hr)) 
		{ 
			fprintf(m_fpDump, "\n--- Writer Statistics (%s) ---\n", 
				(inStreamNum == 1) ? "Audio" : "Video"); 
			fprintf(m_fpDump, "Samples: %I64u\n", stat.qwSampleCount); 
			fprintf(m_fpDump, "Bytes: %I64u\n", stat.qwByteCount); 
			fprintf(m_fpDump, "Dropped Samples: %I64u\n", stat.qwDroppedSampleCount); 
			fprintf(m_fpDump, "Dropped Bytes: %I64u\n", stat.qwDroppedByteCount); 
			fprintf(m_fpDump, "Current  Bitrate: %d\n", stat.dwCurrentBitrate); 
			fprintf(m_fpDump, "Average  Bitrate: %d\n", stat.dwAverageBitrate); 
			fprintf(m_fpDump, "Expected Bitrate: %d\n", stat.dwExpectedBitrate); 
			fprintf(m_fpDump, "Current  Sample Rate: %d (KHz)\n", stat.dwCurrentSampleRate); 
			fprintf(m_fpDump, "Average  Sample Rate: %d (KHz)\n", stat.dwAverageSampleRate); 
			fprintf(m_fpDump, "Expected Sample Rate: %d (KHz)\n", stat.dwExpectedSampleRate); 
		} 
	} 
 
	// Get the extended statistics 
	if (mIWriterAdvanced3) 
	{ 
		WM_WRITER_STATISTICS_EX statEx; 
		ZeroMemory(&statEx, sizeof(statEx)); 
		hr = mIWriterAdvanced3->GetStatisticsEx(inStreamNum, &statEx); 
		if (SUCCEEDED(hr)) 
		{ 
			fprintf(m_fpDump, "--->>> Extended \n"); 
			fprintf(m_fpDump, "Bitrate Plus Overhead: %d\n", statEx.dwBitratePlusOverhead); 
			fprintf(m_fpDump, "Current Sample Drop Rate In Queue: %d\n", statEx.dwCurrentSampleDropRateInQueue); 
			fprintf(m_fpDump, "Current Sample Drop Rate In Codec: %d\n", statEx.dwCurrentSampleDropRateInCodec); 
			fprintf(m_fpDump, "Current Sample Drop Rate In Muxer: %d\n", statEx.dwCurrentSampleDropRateInMultiplexer); 
			fprintf(m_fpDump, "Total Sample Drops In Queue: %d\n", statEx.dwTotalSampleDropsInQueue); 
			fprintf(m_fpDump, "Total Sample Drops In Codec: %d\n", statEx.dwTotalSampleDropsInCodec); 
			fprintf(m_fpDump, "Total Sample Drops In Muxer: %d\n", statEx.dwTotalSampleDropsInMultiplexer); 
		} 
	} 
} 
#endif // _DEBUG