// Copyright (c) 1998-2005 B2C2, Incorporated.  All Rights Reserved.
//
// THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF B2C2, INCORPORATED.
// The copyright notice above does not evidence any
// actual or intended publication of such source code.
//
// This file is proprietary source code of B2C2, Incorporated. and is released pursuant to and
// subject to the restrictions of the non-disclosure agreement and license contract entered
// into by the parties.
//
//
// B2FilterGraphHandler.cpp: implementation of the CB2FilterGraphHandler class.
//
///////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "B2FilterGraphHandler.h"
#include "B2FilterGraphException.h"

#include <initguid.h>

#include "b2c2_defs.h"
#include "b2c2_guids.h"

#include "ib2c2mpeg2tunerctrl.h"
#include "ib2c2mpeg2datactrl.h"

#include "ib2c2mpeg2timeshiftctrl.h"

#include <Dshow.h>


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif



UINT  __stdcall UpdatePvrState (E_PVR_CALLBACK_STATE ePvrCommand);

UINT  __stdcall ProcessTransportStreamPacket ( WORD wPID, unsigned char* pucTransportPacket );


//#define _AUDIO_ONLY

///////////////////////////////////////////////////////////////////////////////
// Construction/Destruction
///////////////////////////////////////////////////////////////////////////////

CB2FilterGraphHandler::CB2FilterGraphHandler()
: B2C2MPEG2Adapter ("")
{
	m_pIVideoWnd = NULL;

	m_bFilterGraphRendered = FALSE;

	m_bAudioOnly = FALSE;
}

CB2FilterGraphHandler::~CB2FilterGraphHandler()
{
	Release();
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::Initialize()
{
	HRESULT hr;
	hr = B2C2MPEG2Adapter::Initialize();

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( GetLastError(), GetLastErrorText());
	}
}

void CB2FilterGraphHandler::ReleaseVideoWindow()
{
	if (m_pIVideoWnd)
	{
		m_pIVideoWnd->put_Owner((OAHWND) NULL);
		m_pIVideoWnd->put_WindowState(SW_HIDE);
		m_pIVideoWnd->Release();
		m_pIVideoWnd = NULL;
	}
}

// virtual ////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::Release()
{
	// Relase Renderer
	if (m_pIVideoWnd)
	{
		m_pIVideoWnd->put_Owner((OAHWND) NULL);
		m_pIVideoWnd->Release();
		m_pIVideoWnd = NULL;
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::AddPIDsToPin(long * plNumPID, long * plPIDArray, 
										 long lDataPinIndex)
{
	HRESULT					hr;
	IB2C2MPEG2DataCtrl5		*pData = B2C2MPEG2Adapter::GetDataControl();

	ASSERT( pData);
	if (pData == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("Add PIDs to PIN"));
	}

	hr = pData->AddPIDsToPin( plNumPID, plPIDArray, lDataPinIndex);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Add PIDs to PIN"));
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::DeletePIDsFromPin(long * plNumPID, long * plPIDArray, 
											  long lDataPinIndex)
{
	HRESULT					hr;
	IB2C2MPEG2DataCtrl5		*pData = B2C2MPEG2Adapter::GetDataControl();

	ASSERT( pData);
	if (pData == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("Add PIDs to PIN"));
	}

	hr = pData->DeletePIDsFromPin( *plNumPID, plPIDArray, lDataPinIndex);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Delete PIDs from PIN"));
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::SetSatellite(long lFrequencyKHz, long lLnbFrequencyMHz, 
										 long lSymbolRate, 
										 eLNBSelection eLnbSelectionVal,
										 ePolarity ePolarityVal, 
										 eDiseqc eDiseqcVal /*= DISEQC_NONE*/,
										 eFEC eFecVal /*= FEC_AUTO*/)
{
	HRESULT					hr;
	IB2C2MPEG2TunerCtrl3	*pTuner = B2C2MPEG2Adapter::GetTunerControl();

	ASSERT( pTuner);
	if (pTuner == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("Set Satellite"));
	}

	tTunerCapabilities tunerCap;
	long lTunerCapSize = sizeof (tunerCap);

	hr = pTuner->GetTunerCapabilities( &tunerCap, &lTunerCapSize);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( GetLastError(), GetLastErrorText());
	}

	// Check if satellite
	if (tunerCap.eModulation != TUNER_SATELLITE)
	{
		throw new CB2FilterGraphException( (DWORD) (-1), TEXT("Wrong tuner type, set Satellite"));
	}

	// Apply settings
	hr = pTuner->SetFrequencyKHz( lFrequencyKHz);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set Frequency"));
	}

	hr = pTuner->SetSymbolRate( lSymbolRate);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set Symbol Rate"));
	}

	hr = pTuner->SetLnbFrequency( lLnbFrequencyMHz);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set LNB Frequency"));
	}

	hr = pTuner->SetFec( (long) eFecVal);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set FEC"));
	}

	hr = pTuner->SetPolarity( (long) ePolarityVal);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set Polarity"));
	}

	hr = pTuner->SetLnbKHz( (long) eLnbSelectionVal);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set LNB Selection"));
	}

	hr = pTuner->SetDiseqc( (long) eDiseqcVal);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set DISEQC"));
	}

	// Apply the settings

	hr = pTuner->SetTunerStatus();

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set SetTunerStatus"));
	}

	hr = pTuner->CheckLock();

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Check Lock"));
	}
}


///////////////////////////////////////////////////////////////////////////////

void CB2FilterGraphHandler::SetTerrestiralAtsc(long lFrequencyKHz)
{
	HRESULT					hr;
	IB2C2MPEG2TunerCtrl3	*pTuner = B2C2MPEG2Adapter::GetTunerControl();

	ASSERT( pTuner);
	if (pTuner == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("Set Satellite"));
	}

	tTunerCapabilities tunerCap;
	long lTunerCapSize = sizeof (tunerCap);

	hr = pTuner->GetTunerCapabilities( &tunerCap, &lTunerCapSize);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( GetLastError(), GetLastErrorText());
	}


	// Check if satellite
	if (tunerCap.eModulation != TUNER_ATSC)
	{
		throw new CB2FilterGraphException( (DWORD) (-1), TEXT("Wrong tuner type, set Terrestrial_ATSC"));
	}

	// Apply settings
	hr = pTuner->SetFrequencyKHz( lFrequencyKHz);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set Frequency"));
	}

	// Apply the settings

	hr = pTuner->SetTunerStatus();

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set SetTunerStatus"));
	}

	hr = pTuner->CheckLock();

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Check Lock"));
	}
}


///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::GetTsOutPin(int nPin, IPin **ppTsOutPin /*= NULL*/)
{
	HRESULT hr;
	hr = B2C2MPEG2Adapter::GetTsOutPin( nPin, ppTsOutPin);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( GetLastError(), GetLastErrorText());
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::GetAudioVideoOutPins(IPin **ppPinOutAudio, IPin **ppPinOutVideo /*= NULL*/)
{
	HRESULT hr;
	hr = B2C2MPEG2Adapter::GetAudioVideoOutPins( ppPinOutAudio, ppPinOutVideo);

	if (FAILED(hr))
	{
		throw new CB2FilterGraphException( GetLastError(), GetLastErrorText());
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::SetVideoWindow(HWND hVideoWnd)
{
	// Check if already initialized; got FilterGraph

	ASSERT( m_pFilterGraph);
	if (m_pFilterGraph == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No filter graph; add Video Renderer"));
	}

	// Check if we already got a video Window filter
#if !defined _AUDIO_ONLY
	if (!m_bAudioOnly)
	{
		HRESULT hr;

		if (m_pIVideoWnd == NULL)
		{
			hr = m_pFilterGraph->QueryInterface( IID_IVideoWindow, (VOID **)&m_pIVideoWnd);

			if(FAILED (hr))
			{
				throw new CB2FilterGraphException( hr, TEXT("Get Video Renderer from Filter Graph"));
			}
		}

		ASSERT( m_pIVideoWnd);

		hr = m_pIVideoWnd->put_Owner((OAHWND) hVideoWnd);

		if(FAILED (hr))
		{
			throw new CB2FilterGraphException( hr, TEXT("Set Video Renderer window"));
		}

		hr = m_pIVideoWnd->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);

		if(FAILED (hr))
		{
			throw new CB2FilterGraphException( hr, TEXT("Set Video Renderer window style"));
		}

		UpdateVideoSize(hVideoWnd);
	}
#endif //!defined _AUDIO_ONLY
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::UpdateVideoSize(HWND hVideoWnd)
{
#if !defined _AUDIO_ONLY
	if (!m_bAudioOnly)
	{
		ASSERT( m_pIVideoWnd);
		if (m_pIVideoWnd == NULL)
		{
			throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No video window; update Video Size"));
		}

		HRESULT hr;

		RECT rect;
		GetClientRect(hVideoWnd, &rect);

		hr = m_pIVideoWnd->SetWindowPosition(rect.left, rect.top, rect.right, rect.bottom);

		if(FAILED (hr))
		{
			throw new CB2FilterGraphException( hr, TEXT("Set Video Renderer window position"));
		}
	}
#endif //!defined _AUDIO_ONLY
}


///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::RenderAudioVideoPins()
{
	HRESULT hr = NOERROR;

	// Check if already initialized; got FilterGraph

	ASSERT( m_pFilterGraph);
	if (m_pFilterGraph == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No filter graph; render Demux OUT pins"));
	}

	ASSERT( m_pPinOutAudio);
	if (m_pPinOutAudio == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No Audio OUT pin; render OUT pins"));
	}

#if !defined _AUDIO_ONLY
	if (!m_bAudioOnly)
	{
		ASSERT( m_pPinOutVideo );
		if (m_pPinOutVideo == NULL)
		{
			throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No Video OUT pin; render OUT pins"));
		}
	}
#endif //!defined _AUDIO_ONLY

	// Render Demux audio out pins

	if ( !m_bFilterGraphRendered )
	{

		hr = m_pFilterGraph->Render( m_pPinOutAudio);

		if (FAILED (hr))
		{
			throw new CB2FilterGraphException( hr, TEXT("Render Audio OUT pin"));
		}

		// Render Demux video out pins

#if !defined _AUDIO_ONLY
		if (!m_bAudioOnly)
		{
			hr = m_pFilterGraph->Render( m_pPinOutVideo );

			if (FAILED (hr))
			{
				throw new CB2FilterGraphException( hr, TEXT("Render Video OUT pin"));
			}
		}
#endif //!defined _AUDIO_ONLY

		m_bFilterGraphRendered = TRUE;
	}
}


///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::Run()
{
	HRESULT hr;

	// Check if already initialized; got FilterGraph
	ASSERT( m_pFilterGraph);
	if (m_pFilterGraph == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No filter graph; Run"));
	}

	// Check / create media control
	if (m_pMediaControl == NULL)
	{
		hr = GetMediaControl( NULL);

		if (FAILED (hr))
		{
			throw new CB2FilterGraphException( GetLastError(), GetLastErrorText());
		}
	}

	hr = m_pMediaControl->Run();
	
	if (FAILED (hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Run"));
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::Pause()
{
	// Check if media control created, if not, never started, so don't need to stop
	if (m_pMediaControl == NULL)
	{
		return;
	}

	HRESULT hr = m_pMediaControl->Pause();
	
	if (FAILED (hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Pause"));
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::Stop()
{
	// Check if media control created, if not, never started, so don't need to stop
	if (m_pMediaControl == NULL)
	{
		return;
	}

	HRESULT hr = m_pMediaControl->Stop();
	
	if (FAILED (hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Stop"));
	}
}

///////////////////////////////////////////////////////////////////////////////
void CB2FilterGraphHandler::SetAudioVideoPids(long lAudioPid, long lVideoPID)
{
	// Check if we 'have' a PVR
	if (m_pIB2C2MPEG2AvCtrl == NULL)
	{
		throw new CB2FilterGraphException( B2C2_SDK_E_NOT_INITIALIZED, TEXT("No PVR control interface; SetPvrPids"));
	}

	// Set PIDs
	HRESULT hr = m_pIB2C2MPEG2AvCtrl->SetAudioVideoPIDs( lAudioPid, lVideoPID);

	m_bAudioOnly = (lVideoPID == 0x1FFF) ? TRUE : FALSE;

	if (FAILED (hr))
	{
		throw new CB2FilterGraphException( hr, TEXT("Set Audio/Video Pids"));
	}
}

