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 <streams.h>
#include <dvdmedia.h>
#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(&amt;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, &amt;mProfile);
BREAK_IF_FAILED(hr);
// If audio input pin is connected...
if (mFilter->IsAudioPinConnected())
{
// Create a new audio stream
hr = mProfile->CreateNewStream(WMMEDIATYPE_Audio, &amt;pConfig);
BREAK_IF_FAILED(hr);
// Config this audio stream according to the audio pin's media type
hr = pConfig->QueryInterface(IID_IWMMediaProps, (void**)&amt;pProps);
BREAK_IF_FAILED(hr);
CMediaType&amt; mt = mFilter->GetAudioMediaType();
WM_MEDIA_TYPE wmMediaType;
ZeroMemory(&amt;wmMediaType, sizeof(WM_MEDIA_TYPE));
CopyMediaType((AM_MEDIA_TYPE *)&amt;wmMediaType, &amt;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(&amt;wmMediaType);
FreeMediaType((AM_MEDIA_TYPE&amt;) 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, &amt;pConfig);
BREAK_IF_FAILED(hr);
// Config this video stream according to the video pin's media type
hr = pConfig->QueryInterface(IID_IWMMediaProps, (void**)&amt;pProps);
BREAK_IF_FAILED(hr);
CMediaType&amt; mt = mFilter->GetVideoMediaType();
WM_MEDIA_TYPE wmMediaType;
ZeroMemory(&amt;wmMediaType, sizeof(WM_MEDIA_TYPE));
CopyMediaType((AM_MEDIA_TYPE *)&amt;wmMediaType, &amt;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(&amt;wmMediaType);//+++
FreeMediaType((AM_MEDIA_TYPE&amt;) 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&amt; 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&amt; 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, &amt;mWriter);
BREAK_IF_FAILED(hr);
hr = mWriter->QueryInterface(IID_IWMWriterAdvanced, (void**)&amt;mIWriterAdvanced);
BREAK_IF_FAILED(hr);
hr = mWriter->QueryInterface(IID_IWMHeaderInfo, (void**)&amt;mIHeaderInfo);
BREAK_IF_FAILED(hr);
// Extra interfaces
mWriter->QueryInterface(IID_IWMWriterAdvanced3, (void**)&amt;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(&amt;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(&amt;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(&amt;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(&amt;rtStart, &amt;rtEnd);
if (FAILED(hr))
{
return hr;
}
{
CAutoLock lockit(&amt;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, &amt;pWmSample);
if (SUCCEEDED(hr))
{
PBYTE pSrc, pDest;
pSample->GetPointer(&amt;pSrc);
pWmSample->GetBuffer(&amt;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(&amt;rtStart, &amt;rtEnd);
if (FAILED(hr))
{
return hr;
}
{
CAutoLock lockit(&amt;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, &amt;pWmSample);
if (SUCCEEDED(hr))
{
PBYTE pSrc, pDest;
pSample->GetPointer(&amt;pSrc);
pWmSample->GetBuffer(&amt;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(&amt;stat, sizeof(stat));
// Get audio's statistics
hr = mIWriterAdvanced->GetStatistics(inStreamNum, &amt;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(&amt;statEx, sizeof(statEx));
hr = mIWriterAdvanced3->GetStatisticsEx(inStreamNum, &amt;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