
// VideoPlayerDlg.cpp : implementation file
//

#include "stdafx.h"
#include "VideoPlayerSDK_5_7_0.h"
#include "VideoPlayerDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CVideoPlayerDlg dialog
CVideoPlayerDlg::CVideoPlayerDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CVideoPlayerDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CVideoPlayerDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_PLAYER, m_ListOfPlayers);
	DDX_Control(pDX, IDC_PROGRESS1, m_Progress1);
	DDX_Control(pDX, IDC_PROGRESS2, m_Progress2);
}

BEGIN_MESSAGE_MAP(CVideoPlayerDlg, CDialog)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_TIMER()
	ON_BN_CLICKED(IDC_START, &CVideoPlayerDlg::OnBnClickedStart)
	ON_BN_CLICKED(IDC_STOP, &CVideoPlayerDlg::OnBnClickedStop)
	ON_CBN_SELCHANGE(IDC_PLAYER, &CVideoPlayerDlg::OnCbnSelchangePlayer)
END_MESSAGE_MAP()


// CVideoPlayerDlg message handlers

BOOL CVideoPlayerDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
	if( !InitListOfPlayers() || !InitPlayer(0) )  EndDialog(IDCANCEL);
	m_ListOfPlayers.SetCurSel(0);

	return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CVideoPlayerDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CVideoPlayerDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


void CVideoPlayerDlg::OnCbnSelchangePlayer()
{
	InitPlayer(m_ListOfPlayers.GetCurSel());
}

void CVideoPlayerDlg::OnBnClickedStart()
{
	if( StartPlayback() ) {
		m_ListOfPlayers.EnableWindow(FALSE);
		GetDlgItem(IDC_STOP)->EnableWindow(TRUE);
		GetDlgItem(IDC_STOP)->SetFocus();
		GetDlgItem(IDC_START)->EnableWindow(FALSE);
		CString list;
		for( int i=0; i<m_FileNames.GetCount(); i++ ) {
			CString str;
			str.Format(_T("%d: %s\r\n"), i+1, m_FileNames[i]);
			list += str;
		}
		GetDlgItem(IDC_LIST)->SetWindowText(list);
		m_Progress1.SetPos(0);
		m_Progress2.SetPos(0);
		DrawStatus(_T("... Starting ..."));
	}
}

void CVideoPlayerDlg::OnBnClickedStop()
{
	if( StopPlayback() ) {
		m_ListOfPlayers.EnableWindow(TRUE);
		GetDlgItem(IDC_START)->EnableWindow(TRUE);
		GetDlgItem(IDC_START)->SetFocus();
		GetDlgItem(IDC_STOP)->EnableWindow(FALSE);
		GetDlgItem(IDC_LIST)->SetWindowText(_T(""));
		m_Progress1.SetPos(0);
		m_Progress2.SetPos(0);
		DrawStatus(_T("... Stopped ..."));
	}
}

void CVideoPlayerDlg::OnTimer(UINT_PTR nIDEvent)
{
	static bool bInTimer = false;
	if( !bInTimer ) {
		bInTimer = true;
		TickTimer();
		bInTimer = false;
	}
	CDialog::OnTimer(nIDEvent);
}


// VideoPlayerSDK
//
bool CVideoPlayerDlg::InitListOfPlayers()
{
	// create enumerator
	HRESULT hres = m_pIEnumPlayers.CoCreateInstance(CLSID_SLTMEnumVideoPlayer4TE);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return false;
	// check any board exist
	long cnt = 0;
	hres = m_pIEnumPlayers->get_Count(&cnt);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return false;
	if( cnt < 1 ) { // no title engines
		ATLASSERT(cnt>0);
		return false;
	}
	// fill list of engines
	for( int i=0; i<cnt; i++ ) {
		CComBSTR bstr;
		hres = m_pIEnumPlayers->get_Name(i,&bstr);
		ATLASSERT(S_OK==hres);
		if( S_OK != hres )  return false;
		m_ListOfPlayers.AddString(bstr);
	}
	return true;
}
//
bool CVideoPlayerDlg::InitPlayer(int nVPIndex)
{
	m_nState = eNotInited;
	// init player
	HRESULT hres = CreatePlayer(nVPIndex);
	if( S_OK != hres ) {
		m_pIPlayer = NULL;
		return false;
	}
	// all right - start timer
	DrawStatus(_T("Stopped"));
	SetTimer(100,100,NULL);
	return true;
}
//
bool CVideoPlayerDlg::StartPlayback()
{
	HRESULT hres = m_pIPlayer->ResetPrepare();
	if( S_OK != hres )  return false;
	m_FileNames.RemoveAll();
	// select files
	CFileDialog dlg(TRUE, _T(".avi"), NULL, OFN_ALLOWMULTISELECT|OFN_FILEMUSTEXIST, _T("AVI Files (*.avi)|*.avi|"), this);
	CString fileName;
	const int c_cMaxFiles = 100;
	const int c_cbBuffSize = (c_cMaxFiles * (MAX_PATH + 1)) + 1;
	dlg.GetOFN().lpstrFile = fileName.GetBuffer(c_cbBuffSize);
	dlg.GetOFN().nMaxFile = c_cMaxFiles;
	if( IDOK != dlg.DoModal() )  return false;
	// scan names
	int offset=0;  const TCHAR *text = fileName;
	while( offset < c_cbBuffSize ) if( 0 == text[offset++] )  break;
	if( 0==text[offset] ) { // single file
		m_FileNames.Add(text);
	} else { // many files
		CString path = CString(text) + _T("\\");
		while( 0 != text[offset] ) {
			m_FileNames.Add(path + CString(&text[offset]));
			while( offset < c_cbBuffSize ) if( 0 == text[offset++] )  break;
		}
	}
	fileName.ReleaseBuffer();
	if( 0 == m_FileNames.GetCount() )  return false;
	m_Progress1.SetRange(0,m_FileNames.GetCount());
	m_Progress1.SetPos(0);
	m_Progress2.SetRange(0,100);
	m_Progress2.SetPos(0);
	// prepare first movie
	hres = PrepareMovie(0,true);
	if( S_OK != hres )  return false;
	// start it
	hres = StartPreparedMovie(0);
	if( S_OK != hres )  return false;
	// all right
	m_nState = eRunning;
	return true;
}
//
bool CVideoPlayerDlg::StopPlayback()
{
	m_nState = eStopped;
	// stop all - means just show live
	HRESULT hres = m_pIPlayer->ShowLive(0, 5.0/25);
	ATLASSERT(S_OK==hres);
	// reset prepared moveis
	hres = m_pIPlayer->ResetPrepare();
	ATLASSERT(S_OK==hres);
	// and clear playlist
	m_FileNames.RemoveAll();
	return true;
}
//
void CVideoPlayerDlg::TickTimer()
{
	if( NULL != m_pIPlayer &&  eRunning == m_nState ) {
		if( S_OK != ForcePlayback() )  OnBnClickedStop();
	}
}
//
void CVideoPlayerDlg::DrawStatus(CString text)
{
	GetDlgItem(IDC_STATUS)->SetWindowText(text);
}
//
// VideoPlayerSDK
//
const double cCrossfadeDuration = 5.0 / 25.0; // 5 frames
//
HRESULT CVideoPlayerDlg::CreatePlayer(int nVPIndex)
{
	m_pIPlayer = NULL;
	// obtain info about first available title engine
	GUID guid;  long index;
	HRESULT hres = m_pIEnumPlayers->get_type(nVPIndex, &guid);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	hres = m_pIEnumPlayers->get_index(nVPIndex, &index);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	// create player
	CComPtr<ISLTMVideoPlayer4TE> pInitPlayer;
	hres = pInitPlayer.CoCreateInstance(CLSID_SLTMVideoPlayer4TE_OEM);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	// use the player
	hres = pInitPlayer->QueryInterface(&m_pIPlayer);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	// make inital settings - automatically scale movie & images to full screen
	hres = m_pIPlayer->put_AutoScaleMovie(TRUE);
	ATLASSERT(S_OK==hres);
	hres = m_pIPlayer->put_AutoScaleImage(TRUE);
	ATLASSERT(S_OK==hres);
	// init reading fifo by 20 MBytes
	CComQIPtr<ISLTMReadFifo> pIReadFifo(pInitPlayer);
	ATLASSERT(NULL != pIReadFifo);
	hres = pIReadFifo->put_ReadFifoMB(20);
	ATLASSERT(S_OK==hres);
	// init player with 100 MBytes video buffer
	hres = pInitPlayer->Init4TitleCore(guid,index, 100);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	return S_OK;
}
//
HRESULT CVideoPlayerDlg::PrepareMovie(int nFileIndex, bool wait)
{
	// check current state
	SLTMVideoPlayerPrepareState state=SLTMVideoPlayerInTransition;  HRESULT hres=E_PENDING;
	while( E_PENDING==hres || SLTMVideoPlayerInTransition==state ) {
		// wait finish last transition (last command PlayVideo finishing)
		Sleep(10);
		hres = m_pIPlayer->get_PrepareState(&state);
	}
	// prepare the movie
	double duration = 0.0;
	CComBSTR fname(m_FileNames[nFileIndex]);
	hres = m_pIPlayer->PrepareVideo(fname, 0,0, SLTMVPAudioFile_Default, &duration);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	while( wait ) { // wait preparing
		Sleep(100);
		hres = m_pIPlayer->get_PrepareState(&state);
		ATLASSERT(S_OK==hres);
		if( S_OK != hres )  return hres;
		if( SLTMVideoPlayerPreparingVideo==state )  continue;
		else if( SLTMVideoPlayerPreparedVideo==state )  break;
		else {
			ATLASSERT(FALSE);
			return E_FAIL;
		}
	}
	// all done
	nPrepareIndex = nFileIndex;
	m_fPrepareDuration = duration;
	m_nCurFileIndex = nFileIndex;
	return S_OK;
}
//
HRESULT CVideoPlayerDlg::StartPreparedMovie(double start_time)
{
	// play prepared movie with crossfade duration = 5 frames
	HRESULT hres = m_pIPlayer->PlayVideo(start_time, cCrossfadeDuration);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	// all right
	m_fPlayDuration = m_fPrepareDuration;
	nPlayIndex = nPrepareIndex;
	m_fPrepareDuration = 0;
	nPrepareIndex = 0;
	return S_OK;
}
//
HRESULT CVideoPlayerDlg::ForcePlayback()
{
	// obtain rest time
	double fNextTime,fRestTime;
	HRESULT hres = m_pIPlayer->get_RestDuration(&fNextTime, &fRestTime);
	if( E_PENDING==hres ) return S_OK;
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	if( fRestTime <= 0.0  &&  m_nCurFileIndex == m_FileNames.GetCount() ) { // finish playback of the playlist
		return S_FALSE;
	} else if( fRestTime < 1.0  &&  m_fPrepareDuration > 0.001 ) { // start prepared movie
		hres = StartPreparedMovie(fNextTime-cCrossfadeDuration);
		ATLASSERT(S_OK==hres);
		if( S_OK != hres )  return hres;
		return S_OK;
	} else if( m_fPrepareDuration < 0.001 ) { // try prepare next movie
		if( m_nCurFileIndex < m_FileNames.GetCount()-1 ) { // prepare next movie
			hres = PrepareMovie(m_nCurFileIndex+1, false);
			ATLASSERT(S_OK==hres);
			if( S_OK != hres )  return hres;
			return S_OK;
		} else  m_nCurFileIndex = m_FileNames.GetCount(); // no more movies
	}
	// fill state info ...
	CString str = _T("... ");
	//  ... playback state
	SLTMPlaybackState nPlaybackState;
	hres = m_pIPlayer->get_PlaybackState(&nPlaybackState);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	CString szPlayIndex;
	szPlayIndex.Format(_T(" #%d"), nPlayIndex+1);
	switch( nPlaybackState ) {
		case SLTMPlaybackStopped:
			str += _T("Play = None");
			break;
		case SLTMPlaybackPreparing:
			str += _T("Play = Preparing") + szPlayIndex;
			break;
		case SLTMPlaybackPrepared:
			str += _T("Play = Prepared") + szPlayIndex;
			break;
		case SLTMPlaybackPaused:
			str += _T("Play = Paused") + szPlayIndex;
			break;
		case SLTMPlaybackPlaying:
			str += _T("Play = Playing") + szPlayIndex;
			break;
		case SLTMPlaybackStopOnError:
			str += _T("Play = ERROR with") + szPlayIndex;
			break;
	};
	str += _T(" ... ");
	//  ... prepare state
	SLTMVideoPlayerPrepareState nPrepareState;
	hres = m_pIPlayer->get_PrepareState(&nPrepareState);
	ATLASSERT(S_OK==hres);
	if( S_OK != hres )  return hres;
	CString szPrepareIndex;
	szPrepareIndex.Format(_T(" #%d ..."), nPrepareIndex+1);
	switch( nPrepareState ) {
		case SLTMVideoPlayerPreparingVideo:
			str += _T("Next = Load") + szPrepareIndex;
			break;
		case SLTMVideoPlayerPreparedVideo:
			str += _T("Next = Ready") + szPrepareIndex;
			break;
		case SLTMVideoPlayerWasError:
			str += _T("Next = ERROR with") + szPrepareIndex;
			break;
	};
	// update GUI info
	DrawStatus(str);
	m_Progress1.SetPos(m_nCurFileIndex);
	m_Progress2.SetPos((int)(100*(m_fPlayDuration-fRestTime)/m_fPlayDuration));
	return S_OK;
}
//
// VideoPlayerSDK
