//------------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are covered by the
// Microsoft Limited Public License (Ms-LPL)
// which can be found in the file MS-LPL.txt at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license.
//
// The software is licensed “as-is.”
//
// You must not remove this notice, or any other, from this software.
//
//
//
// Demux code for Windows CE
//
//-------------------------------------------------------------------------
//======================================================================
// Demux code for Windows CE
//======================================================================
/*++
Module Name:
pin_out.cpp
Abstract:
This module contains the output pin code
Revision History:
02-Jul-1999 created
XXXX: for now we use the pin lock in everything
--*/
#include "precomp.h"
#include // for ksmedia.h
#include // for bdamedia.h
#include // for KSDATAFORMAT_TYPE_MPEG2_SECTIONS
#include "mp2demux.h"
#include "tsstats.h"
#include "mp2seek.h"
#include "pin_out.h"
#include "filter.h"
// disable so we can use 'this' in the initializer list
#pragma warning (disable:4355)
CMPEG2DemuxOutputPin::CMPEG2DemuxOutputPin (
IN AM_MEDIA_TYPE * pmt,
IN LPWSTR pszPinName,
IN TCHAR * pObjectName,
IN CMPEG2Demultiplexer * pOwningFilter,
IN CRefCountedCritSec * pcrtFilterLock,
IN CMpeg2DemuxMediaSeekingCore * pSeekingCore,
IN CRefCountedCritSec * pcrtSeekingLock,
IN CMpeg2Stats * pStats,
IN LONG lBatchSize,
IN BOOL fBatchExact,
IN DWORD dwMediaSampleLen,
IN DWORD dwMediaSamplePool,
OUT HRESULT * pHr
) : CBaseOutputPin (pObjectName,
pOwningFilter,
pcrtFilterLock,
pHr,
pszPinName
),
m_MediaType (* pmt),
m_pOutputQueue (NULL),
m_cRefcount (1),
m_pStats (pStats),
m_lQueueSize (DEFAULT_OUTPUT_PIN_BUFFER_POOL),
m_lBatchSize (lBatchSize),
m_fBatchExact (fBatchExact),
m_dwMediaSampleLen (dwMediaSampleLen),
m_dwMediaSamplePool (dwMediaSamplePool),
m_pMpeg2DemuxFilter (pOwningFilter),
m_Seek (this,
m_pMpeg2DemuxFilter,
pSeekingCore,
pcrtSeekingLock
),
m_pcrtSeekingLock (pcrtFilterLock),
m_pSeekingCore (pSeekingCore),
m_lFlushingRef (0),
m_dwThreadPriority(248 + THREAD_PRIORITY_NORMAL)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::CMPEG2DemuxOutputPin ()")) ;
TRACE_CONSTRUCTOR (TEXT ("CMPEG2DemuxOutputPin")) ;
ASSERT (pcrtFilterLock) ;
pcrtFilterLock -> AddRef () ;
ASSERT (m_pStats) ;
m_pStats -> AddRef () ;
ASSERT (m_pcrtSeekingLock) ;
m_pcrtSeekingLock -> AddRef () ;
HKEY hKey;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT,
TEXT("CLSID\\{AFB6C280-2C41-11D3-8A60-0000F81E0E4A}\\Pins\\MPEG-2 Stream\\"),
0, 0, &hKey))
{
DWORD dwType, dwSize, dwValue;
// Use default value if the value isn't defined, or is invalid
dwSize = sizeof (DWORD);
if (ERROR_SUCCESS == RegQueryValueEx (hKey, TEXT("ThreadPriority"), NULL, &dwType, LPBYTE(&dwValue), &dwSize) &&
REG_DWORD == dwType && dwValue < 256)
{
m_dwThreadPriority = dwValue;
}
RegCloseKey (hKey);
}
// don't addref the demux or we'd get circular refs
#ifdef DEBUG
// need to do this because my pin starts with a refcount of 1, whereas all the
// base class assumes starting with 0, and explicitely AddRef'ing to pass out
// the first reference
m_cRef = 1 ;
#endif
TRACE_5 (LOG_DEMUX_PINS, 4, TEXT ("Output pin created (%s)\n\tMedia sample details\n\t\tlen = %u bytes\n\t\tpool = %u\n\t\tbatch = %u\n\t\tbatch exact = %s"), m_pName, m_dwMediaSampleLen, m_dwMediaSamplePool, m_lBatchSize, m_fBatchExact ? TEXT ("TRUE") : TEXT ("FALSE")) ;
}
CMPEG2DemuxOutputPin::~CMPEG2DemuxOutputPin (
void
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::~CMPEG2DemuxOutputPin ()")) ;
TRACE_DESTRUCTOR (TEXT ("CMPEG2DemuxOutputPin")) ;
delete m_pOutputQueue ;
// done with the filter lock
ASSERT (m_pLock) ;
(reinterpret_cast (m_pLock)) -> Release () ;
// done with the seeking lock
m_pcrtSeekingLock -> Release () ;
ASSERT (m_pStats) ;
m_pStats -> Release () ;
}
STDMETHODIMP_(ULONG)
CMPEG2DemuxOutputPin::NonDelegatingAddRef (
)
{
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::NonDelegatingAddRef ()")) ;
#ifdef DEBUG
// Update the debug only variable maintained by the base class
m_cRef++;
ASSERT(m_cRef > 0);
#endif
return InterlockedIncrement (& m_cRefcount) ;
}
STDMETHODIMP_(ULONG)
CMPEG2DemuxOutputPin::NonDelegatingRelease()
{
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::NonDelegatingRelease ()")) ;
#ifdef DEBUG
// Update the debug only variable in CBasePin
m_cRef--;
ASSERT(m_cRef >= 0);
#endif
if (InterlockedDecrement (& m_cRefcount) == 0) {
delete this ;
return 0 ;
}
ASSERT (m_cRefcount > 0) ;
return m_cRefcount ;
}
STDMETHODIMP
CMPEG2DemuxOutputPin::NonDelegatingQueryInterface (
IN REFIID riid,
IN LPVOID * ppv
)
{
HRESULT hr ;
// ------------------------------------------------------------------------
// IMPEG2PIDMap; this interface is used when the filter is processing a
// transport stream
if (riid == IID_IMPEG2PIDMap &&
!(static_cast (m_pFilter) -> IsInPullMode ())) {
// transport interface; set the stream type to see if we return the interface or not
hr = static_cast (m_pFilter) -> SetStreamType (MPEG2_STREAM_TRANSPORT) ;
if (SUCCEEDED (hr)) {
return GetInterface (
(IMPEG2PIDMap *) this,
ppv
) ;
}
else {
// failed to load, for whatever reason; thus we "don't implement"
// the interface
return E_NOINTERFACE ;
}
}
// ------------------------------------------------------------------------
// IMPEG2StreamIdMap; this interface is used when the filter is processing
// a program stream
else if (riid == IID_IMPEG2StreamIdMap &&
!(static_cast (m_pFilter) -> IsInPullMode ())) {
// transport interface; set the stream type to see if we return the interface or not
hr = static_cast (m_pFilter) -> SetStreamType (MPEG2_STREAM_PROGRAM) ;
if (SUCCEEDED (hr)) {
return GetInterface (
(IMPEG2StreamIdMap *) this,
ppv
) ;
}
else {
return E_NOINTERFACE ;
}
}
// ------------------------------------------------------------------------
// IAMPushSource; this is tied to the filter's IReferenceClock, but we
// implement it always, regardless of presence/absence of ref clock in
// in filter because the graph code seems to cache this interface
else if (riid == IID_IAMPushSource &&
static_cast (m_pFilter) -> ImplementPushClock ()) {
return GetInterface (
(IAMPushSource *) this,
ppv
) ;
}
// ------------------------------------------------------------------------
// IMediaSeeking; only implement this if we're seekable
else if (riid == IID_IMediaSeeking &&
static_cast (m_pFilter) -> IsSeekable ()) {
return GetInterface (
(IMediaSeeking *) & m_Seek,
ppv
) ;
}
// ------------------------------------------------------------------------
// default
else {
return CBaseOutputPin::NonDelegatingQueryInterface (riid, ppv) ;
}
}
HRESULT
CMPEG2DemuxOutputPin::CheckMediaType (
IN const CMediaType * pmtCheck
)
{
CAutoLock ca (m_pLock) ;
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::CheckMediaType ()")) ;
return (m_MediaType == * pmtCheck) ? S_OK : S_FALSE ;
}
HRESULT
CMPEG2DemuxOutputPin::GetMediaType (
IN int iPosition,
OUT CMediaType * pmtGet
)
{
CAutoLock ca (m_pLock) ;
ASSERT (pmtGet) ;
O_TRACE_ENTER_2 (TEXT("CMPEG2DemuxOutputPin::GetMediaType (%u, 0x%08x)"), iPosition, pmtGet) ;
// we only support 1 media type in the pin - the one we are created
// with
if (iPosition == 0) {
* pmtGet = m_MediaType ;
return S_OK ;
}
else {
return VFW_S_NO_MORE_ITEMS ;
}
}
HRESULT
CMPEG2DemuxOutputPin::DecideAllocator (
IN IMemInputPin * pPin,
IN IMemAllocator ** ppAlloc
)
{
HRESULT hr ;
ALLOCATOR_PROPERTIES AllocatorProperties ;
O_TRACE_ENTER_2 (TEXT("CMPEG2DemuxOutputPin::DecideAllocator (0x%08x, 0x%08x)"), pPin, ppAlloc) ;
if (m_pMpeg2DemuxFilter -> GetStreamType () == MPEG2_STREAM_TRANSPORT) {
// don't insist if we are transport; if we are still undecided, we
// will be the allocator
hr = CBaseOutputPin::DecideAllocator (pPin, ppAlloc) ;
}
else {
hr = InitAllocator (
ppAlloc
) ;
if (SUCCEEDED (hr)) {
ZeroMemory (& AllocatorProperties, sizeof AllocatorProperties) ;
hr = pPin -> GetAllocatorRequirements (& AllocatorProperties) ;
if (SUCCEEDED (hr) ||
hr == E_NOTIMPL) {
if (AllocatorProperties.cbAlign == 0) {
AllocatorProperties.cbAlign = 1 ;
}
hr = DecideBufferSize ((* ppAlloc), & AllocatorProperties) ;
if (SUCCEEDED (hr)) {
hr = pPin -> NotifyAllocator ((* ppAlloc), FALSE) ;
}
}
}
}
return hr ;
}
HRESULT
CMPEG2DemuxOutputPin::DecideBufferSize (
IN IMemAllocator * pAlloc,
IN ALLOCATOR_PROPERTIES * ppropInputRequest
)
{
CAutoLock ca (m_pLock) ;
ALLOCATOR_PROPERTIES propActual = {0} ;
O_TRACE_ENTER_2 (TEXT("CMPEG2DemuxOutputPin::DecideBufferSize (0x%08x, 0x%08x)"), pAlloc, ppropInputRequest) ;
ASSERT (pAlloc) ;
ASSERT (ppropInputRequest) ;
ppropInputRequest -> cbBuffer = Max (ppropInputRequest -> cbBuffer, m_dwMediaSampleLen) ;
ppropInputRequest -> cBuffers = (ppropInputRequest -> cBuffers > 0 ? ppropInputRequest -> cBuffers : m_dwMediaSamplePool) ;
ppropInputRequest -> cbAlign = 1 ;
ppropInputRequest -> cbPrefix = ppropInputRequest -> cbPrefix ;
// output queue length max
m_lQueueSize = ppropInputRequest -> cBuffers ;
return pAlloc -> SetProperties (ppropInputRequest, & propActual) ;
}
HRESULT
CMPEG2DemuxOutputPin::Active (
)
{
HRESULT hr ;
CAutoLock ca (m_pLock) ;
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::Active ()")) ;
//m_bDiscontinuity = TRUE ;
m_lFlushingRef = 0 ;
if (!IsConnected ()) {
return S_OK ;
}
// call this first
hr = CBaseOutputPin::Active () ;
if (SUCCEEDED (hr)) {
// we're set to go; instantiate our output queue
ASSERT (m_pOutputQueue == NULL) ;
ASSERT (IsConnected ()) ;
// HACKHACKHACKHACKHACKHACKHACK
// this fixes the problem where calling receive multiple on KSP
// seems to lose about half the media samples
if (m_mt.majortype == KSDATAFORMAT_TYPE_MPEG2_SECTIONS &&
m_mt.subtype == MEDIASUBTYPE_None &&
m_mt.formattype == FORMAT_None) {
// don't batch
m_pOutputQueue = new COutputQueue (
GetConnected (), // input pin
& hr, // return value
FALSE, // auto detect
TRUE, // send directly
1, // batch size
FALSE, // exact batch
1, // queue size
m_dwThreadPriority) ;
}
else {
// batch
m_pOutputQueue = new COutputQueue (
GetConnected (), // input pin
& hr, // return value
TRUE, // auto detect
TRUE, // send directly
m_lBatchSize, // batch size
m_fBatchExact, // exact batch
m_lQueueSize, // queue size
m_dwThreadPriority) ;
}
if (m_pOutputQueue == NULL) {
hr = E_OUTOFMEMORY ;
}
}
return hr ;
}
HRESULT
CMPEG2DemuxOutputPin::Inactive (
)
{
HRESULT hr ;
CAutoLock ca (m_pLock) ;
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::Inactive ()")) ;
hr = CBaseOutputPin::Inactive () ;
if (SUCCEEDED (hr)) {
DELETE_RESET (m_pOutputQueue) ;
}
return hr ;
}
HRESULT
CMPEG2DemuxOutputPin::SendSample (
IN IMediaSample * pIMediaSample
)
/*++
the media sample that enters into this method has 1 refcount that
belongs to the buffer source; this method MUST make sure that this
refcount is kept across the method of delivery
--*/
{
HRESULT hr ;
REFERENCE_TIME rtSegmentStart ;
REFERENCE_TIME rtSegmentStop ;
double dSegmentRate ;
//
// should not be holding the mapper lock across the call
//
m_pLock -> Lock () ;
if (m_pOutputQueue) {
// check if we've just been seeked and need to pass a new segment
// notification downstream
if (pIMediaSample -> IsDiscontinuity () == S_OK &&
static_cast (m_pFilter) -> IsSeekable ()) {
// discontinuity follows a seeking call; send a segment
// downstream
#ifdef DEBUG
REFERENCE_TIME rtPTSStart ;
REFERENCE_TIME rtPTSStop ;
hr = pIMediaSample -> GetTime (& rtPTSStart, & rtPTSStop) ;
if (FAILED (hr)) {
TRACE_0 (LOG_TRACE, 3, TEXT ("no timestamp on first media sample following seek")) ;
}
else {
TRACE_1 (LOG_TRACE, 2, TEXT ("new segment start = %I64d"), rtPTSStart) ;
}
#endif
ASSERT (m_pSeekingCore) ;
hr = m_pSeekingCore -> RecvThreadGetSegmentValues (
& rtSegmentStart,
& rtSegmentStop,
& dSegmentRate
) ;
if (SUCCEEDED (hr)) {
// pass the new segment downstream
m_pOutputQueue -> NewSegment (
rtSegmentStart,
rtSegmentStop,
dSegmentRate
) ;
TRACE_4 (
LOG_TRACE,
3,
TEXT ("New Segment returned : start = %-10I64d, stop = %-10I64d, rate = %-2.1f, rtPTSStart = %-10I64d"),
rtSegmentStart,
rtSegmentStop,
dSegmentRate,
rtPTSStart
) ;
}
// don't fail the SendSample() call because we failed to send a
// newsegment downstream
hr = S_OK ;
}
#ifdef DEBUG
REFERENCE_TIME rtPTSStart2 ;
REFERENCE_TIME rtPTSStop2 ;
hr = pIMediaSample -> GetTime (& rtPTSStart2, & rtPTSStop2) ;
if (hr == S_OK) {
TRACE_3 (LOG_TIMING, 5, TEXT ("sending: [%08xh] start = %I64d (%I64d ms)"), this, rtPTSStart2, rtPTSStart2 / 10000) ;
}
#endif
// COutputQueue Release's the media sample's refcount after this
// call completes, so we make sure that we keep 1 count across the
// call
pIMediaSample -> AddRef () ;
hr = m_pOutputQueue -> Receive (pIMediaSample) ;
NE_SPEW (hr, S_OK, TEXT ("m_pOutputQueue -> Receive (pIMediaSample)")) ;
// dshow has an annoying habit of failing a call with a non-FAILED
// HRESULT i.e. high bit is not set; we want to accurately send back
// the success/failure of the above call to the caller of this method,
// so we set it here
hr = (hr == S_OK ? S_OK : (FAILED (hr) ? hr : E_FAIL)) ;
}
else {
// we're not connected; drop it and return a failure
hr = VFW_E_NOT_CONNECTED ;
}
if (SUCCEEDED (hr)) {
ASSERT (m_pStats) ;
m_pStats -> Global_MediaSampleOut () ;
}
m_pLock -> Unlock () ;
return hr ;
}
HRESULT
CMPEG2DemuxOutputPin::DeliverBeginFlush (
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::DeliverBeginFlush ()")) ;
m_pLock -> Lock () ;
if (m_pOutputQueue) {
if (m_lFlushingRef == 0) {
ASSERT (IsConnected ()) ;
m_pOutputQueue -> BeginFlush () ;
TRACE_0 (LOG_TRACE, 1, TEXT ("BeginFlush")) ;
}
m_lFlushingRef++ ;
hr = S_OK ;
}
else if (!IsConnected ()) {
hr = VFW_E_NOT_CONNECTED ;
}
else {
hr = S_OK ;
}
m_pLock -> Unlock () ;
return hr ;
}
HRESULT
CMPEG2DemuxOutputPin::DeliverEndFlush (
)
{
HRESULT hr ;
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::DeliverEndFlush ()")) ;
m_pLock -> Lock () ;
if (m_pOutputQueue) {
ASSERT (m_lFlushingRef > 0) ;
m_lFlushingRef-- ;
if (m_lFlushingRef == 0) {
ASSERT (IsConnected ()) ;
m_pOutputQueue -> EndFlush () ;
TRACE_0 (LOG_TRACE, 1, TEXT ("EndFlush")) ;
}
hr = S_OK ;
}
else if (!IsConnected ()) {
hr = VFW_E_NOT_CONNECTED ;
}
else {
hr = S_OK ;
}
m_pLock -> Unlock () ;
return hr ;
}
HRESULT
CMPEG2DemuxOutputPin::DeliverEndOfStream (
)
{
#ifndef UNDER_CE
HRESULT hr ;
#endif //UNDER_CE
O_TRACE_ENTER_0 (TEXT("CMPEG2DemuxOutputPin::DeliverEndOfStream ()")) ;
if (IsConnected ()) {
if (m_pOutputQueue) {
m_pOutputQueue -> EOS () ;
}
return S_OK ;
}
return VFW_E_NOT_CONNECTED ;
}
HRESULT
CMPEG2DemuxOutputPin::SetMediaType (
IN const CMediaType * pmt
)
{
HRESULT hr ;
hr = CBaseOutputPin::SetMediaType (pmt) ;
if (SUCCEEDED (hr)) {
ASSERT ((* pmt) == m_mt) ;
if (IsAVMediaType (& m_mt)) {
// A/V - needs a bigger buffer pool min
m_dwMediaSamplePool = Max (m_dwMediaSamplePool, MIN_AV_BUFFER_POOL_SIZE) ;
}
}
return hr ;
}