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