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