|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1997.
//
// File: M E D I A . C P P
//
// Contents: Control handlers and implementation for the media
// player device
//
// Notes:
//
// Author: danielwe 6 Nov 1999
//
//----------------------------------------------------------------------------
#include "pch.h"
#pragma hdrstop
#include "media.h"
#include "ncbase.h"
#include "updiagp.h"
#include "ncinet.h"
static const LPSTR c_rgszStates[] = { "Turned Off", "Uninitialized", "Stopped", "Paused", "Playing", };
// Current multimedia variables
Media media = {TurnedOff, NULL, 0, FALSE};
DWORD Val_VolumeUp(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_VolumeUp(DWORD cArgs, ARG *rgArgs) { OnVolumeUpDown(TRUE); return 1; }
DWORD Val_VolumeDown(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_VolumeDown(DWORD cArgs, ARG *rgArgs) { OnVolumeUpDown(FALSE); return 1; }
DWORD Val_SetVolume(DWORD cArgs, ARG *rgArgs) { if (cArgs == 1) { return 1; }
return 0; }
DWORD Do_SetVolume(DWORD cArgs, ARG *rgArgs) { OnSetVolume(_tcstoul(rgArgs[0].szValue, NULL, 10)); return 1; }
DWORD Val_Mute(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_Mute(DWORD cArgs, ARG *rgArgs) { OnMediaMute(); return 1; }
DWORD Val_Power(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_Power(DWORD cArgs, ARG *rgArgs) { if (media.state == TurnedOff) { InitMedia(); } else { DeleteContents(); }
return 1; }
DWORD Val_LoadFile(DWORD cArgs, ARG *rgArgs) { if (cArgs != 1) { return 0; } else { // Check if the file exists
if (GetFileAttributes(rgArgs[0].szValue) == -1) { return 0; } }
return 1; }
DWORD Do_LoadFile(DWORD cArgs, ARG *rgArgs) { OpenMediaFile(rgArgs[0].szValue); return 1; }
DWORD Val_Play(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_Play(DWORD cArgs, ARG *rgArgs) { OnMediaPlay(); return 1; }
DWORD Val_Stop(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_Stop(DWORD cArgs, ARG *rgArgs) { OnMediaStop(); return 1; }
DWORD Val_Pause(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_Pause(DWORD cArgs, ARG *rgArgs) { OnMediaPause(); return 1; }
DWORD Val_SetPos(DWORD cArgs, ARG *rgArgs) { return 0; }
DWORD Do_SetPos(DWORD cArgs, ARG *rgArgs) { return 0; }
DWORD Val_SetTime(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Do_SetTime(DWORD cArgs, ARG *rgArgs) { return 1; }
DWORD Val_Test(DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Testing 1 2 3..."); return 1; }
DWORD Do_Test(DWORD cArgs, ARG *rgArgs) { TraceTag(ttidUpdiag, "Doing a test!"); return 1; }
//
// CanPlay
//
// Return true if we can go to a playing state from our current state
//
BOOL CanPlay() { return(media.state == Stopped || media.state == Paused); }
BOOL CanSetVolume() { return(media.state == Stopped || media.state == Paused || media.state == Playing); }
//
// CanStop
//
// Return true if we can go to a stopped state from our current state
//
BOOL CanStop() { return(media.state == Playing || media.state == Paused); }
//
// CanPause
//
// Return true if we can go to a paused state from our current state
//
BOOL CanPause() { return(media.state == Playing || media.state == Stopped); }
//
// IsInitialized
//
// Return true if we have loaded and initialized a multimedia file
//
BOOL IsInitialized() { return(media.state != Uninitialized); }
VOID SubmitMediaEvent(LPCSTR szEventSource, LPCSTR szProp, LPCSTR szValue) { UPNPSVC * psvc; CHAR szState[256]; CHAR szUri[INTERNET_MAX_URL_LENGTH]; LPCSTR szEvtSource = szEventSource ? szEventSource : g_pdata->szEventSource; LPTSTR pszEvtSource;
UPNP_PROPERTY rgProps[] = { {(LPSTR)szProp, 0, (LPSTR)szValue} };
pszEvtSource = TszFromSz(szEvtSource); if (pszEvtSource) { psvc = PSvcFromId(pszEvtSource); if (psvc) { LPSTR pszEvtUrl;
pszEvtUrl = SzFromTsz(psvc->szEvtUrl); if (pszEvtUrl) { HRESULT hr;
hr = HrGetRequestUriA(pszEvtUrl, INTERNET_MAX_URL_LENGTH, szUri); if (SUCCEEDED(hr)) { if (SubmitUpnpPropertyEvent(szUri, 0, 1, rgProps)) { TraceTag(ttidUpdiag, "Successfully submitted event for %s.", szUri); } else { TraceTag(ttidUpdiag, "Did not submit event for %s! Error %d.", g_pdata->szEventSource, GetLastError()); } }
delete [] pszEvtUrl; } else { TraceTag(ttidUpdiag, "SubmitMediaEvent: SzFromTsz"); } } delete [] pszEvtSource; } else { TraceTag(ttidUpdiag, "SubmitMediaEvent: TszFromSz"); } }
//
// ChangeStateTo
//
VOID ChangeStateTo(State newState) { media.state = newState;
SubmitMediaEvent("xport", "State", c_rgszStates[newState]);
TraceTag(ttidMedia, "Changed state to %d.", newState); }
DWORD WINAPI TimeThreadProc(LPVOID lpvThreadParam) { while (TRUE) { if (WaitForSingleObject(g_hEventCleanup, 1000) == WAIT_OBJECT_0) { break; } else { SYSTEMTIME st; FILETIME ft; CHAR szLocalTime[255]; CHAR szLocalDate[255]; CHAR szBoth[513];
// Stamp the current time into the subscription struct
//
GetSystemTimeAsFileTime(&ft);
// Convert time to local time zone
FileTimeToLocalFileTime(&ft, &ft); FileTimeToSystemTime(&ft, &st);
GetTimeFormatA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP, &st, NULL, szLocalTime, 255); GetDateFormatA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP | DATE_SHORTDATE, &st, NULL, szLocalDate, 255);
wsprintfA(szBoth, "%s %s", szLocalDate, szLocalTime);
SubmitMediaEvent("clock", "DateTime", szBoth); } }
return 0; }
//
// InitMedia
//
// Initialization
//
BOOL InitMedia() { DWORD dwTid;
g_hThreadTime = CreateThread(NULL, 0, TimeThreadProc, NULL, 0, &dwTid);
ChangeStateTo( Uninitialized );
media.pGraph = NULL;
TraceTag(ttidMedia, "Initialized...");
return TRUE; }
//
// CreateFilterGraph
//
BOOL CreateFilterGraph() { IMediaEvent *pME; HRESULT hr;
ASSERT(media.pGraph == NULL);
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (LPVOID *) &media.pGraph); TraceError("CreateFilterGraph: CoCreateInstance()", hr); if (FAILED(hr)) { media.pGraph = NULL; return FALSE; }
TraceTag(ttidMedia, "Created filter graph");
return TRUE;
} // CreateFilterGraph
// Destruction
//
// DeleteContents
//
VOID DeleteContents() { ReleaseObj(media.pGraph); media.pGraph = NULL;
ChangeStateTo(Uninitialized);
TraceTag(ttidMedia, "Deleted contents."); }
//
// RenderFile
//
BOOL RenderFile(LPCTSTR szFileName) { HRESULT hr; WCHAR wPath[MAX_PATH]; CHAR aPath[MAX_PATH];
DeleteContents();
TszToWszBuf(wPath, szFileName, MAX_PATH);
if (!CreateFilterGraph()) { TraceTag(ttidMedia, "Couldn't render"); return FALSE; }
TszToWszBuf(wPath, szFileName, MAX_PATH);
hr = media.pGraph->RenderFile(wPath, NULL); TraceError("RenderFile: RenderFile()", hr);
if (FAILED(hr)) { return FALSE; }
TszToSzBuf(aPath, szFileName, MAX_PATH);
SubmitMediaEvent("app", "File", aPath);
TraceTag(ttidMedia, "Rendered file %s.", szFileName); return TRUE;
} // RenderFile
//
// OpenMediaFile
//
// File..Open has been selected
//
VOID OpenMediaFile(LPCTSTR szFile) { if ( szFile != NULL && RenderFile(szFile)) { TraceTag(ttidMedia, "Opened file %s." , szFile); ChangeStateTo(Stopped); }
} // OpenMediaFile
VOID OnSetVolume(DWORD dwVol) { if (CanSetVolume()) { HRESULT hr; IBasicAudio * pAudio;
hr = media.pGraph->QueryInterface(IID_IBasicAudio, (LPVOID *) &pAudio); TraceError("OnSetVolume: QueryInterface()", hr); if (SUCCEEDED(hr)) { LONG lVol; CHAR szVol[32];
dwVol = max(0, dwVol); dwVol = min(7, dwVol); lVol = (7 - dwVol) * -300;
TraceTag(ttidMedia, "Putting new volume of %d.", lVol); pAudio->put_Volume(lVol);
wsprintfA(szVol, "%ld", lVol); SubmitMediaEvent("app", "Volume", szVol);
ReleaseObj(pAudio); } } }
VOID OnVolumeUpDown(BOOL fUp) { if (CanSetVolume()) { HRESULT hr; IBasicAudio * pAudio;
hr = media.pGraph->QueryInterface(IID_IBasicAudio, (LPVOID *) &pAudio); TraceError("OnVolumeUp: QueryInterface()", hr); if (SUCCEEDED(hr)) { LONG lVol; const LONG lStep = 300;
pAudio->get_Volume(&lVol); TraceTag(ttidMedia, "Current volume is %d.", lVol);
if (fUp) { lVol += lStep; lVol = min(lVol, 0); } else { lVol -= lStep; lVol = max(lVol, -10000); }
if (media.fIsMuted) { media.lOldVol = lVol; TraceTag(ttidMedia, "MUTED: Saving new volume of %d.", lVol); } else { CHAR szVol[32];
TraceTag(ttidMedia, "Putting new volume of %d.", lVol); pAudio->put_Volume(lVol);
wsprintfA(szVol, "%ld", lVol); SubmitMediaEvent("app", "Volume", szVol); }
ReleaseObj(pAudio); } } }
VOID OnMediaMute() { HRESULT hr; IBasicAudio * pAudio;
hr = media.pGraph->QueryInterface(IID_IBasicAudio, (LPVOID *) &pAudio); TraceError("OnVolumeUp: QueryInterface()", hr); if (SUCCEEDED(hr)) { if (media.fIsMuted) { TraceTag(ttidMedia, "Unmuting... Restoring volume of %d.", media.lOldVol); pAudio->put_Volume(media.lOldVol); media.fIsMuted = FALSE; } else { pAudio->get_Volume(&media.lOldVol); TraceTag(ttidMedia, "Muting volume. Was %d.", media.lOldVol); pAudio->put_Volume(-10000); media.fIsMuted = TRUE; }
ReleaseObj(pAudio); } }
//
// OnMediaPlay
//
// There are two possible UI strategies for an application: you either play
// from the start each time you stop, or you play from the current position,
// in which case you have to explicitly rewind at the end. If you want the
// play from current and rewind at end, enable this code, if you want the
// other option, then enable FROM_START in both OnMediaStop and OnAbortStop.
#undef REWIND
#define FROM_START
VOID OnMediaPlay() { if (CanPlay()) { HRESULT hr; IMediaControl *pMC;
// Obtain the interface to our filter graph
hr = media.pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC); TraceError("OnMediaPlay: QueryInterface()", hr);
if (SUCCEEDED(hr)) { #ifdef REWIND
IMediaPosition * pMP; hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaPosition, (VOID**) &pMP); if (SUCCEEDED(hr)) { // start from last position, but rewind if near the end
REFTIME tCurrent, tLength; hr = pMP->lpVtbl->get_Duration(pMP, &tLength); if (SUCCEEDED(hr)) { hr = pMP->lpVtbl->get_CurrentPosition(pMP, &tCurrent); if (SUCCEEDED(hr)) { // within 1sec of end? (or past end?)
if ((tLength - tCurrent) < 1) { pMP->lpVtbl->put_CurrentPosition(pMP, 0); } } } pMP->lpVtbl->Release(pMP); } #endif
// Ask the filter graph to play and release the interface
hr = pMC->Run(); TraceError("OnMediaPlay: Run()", hr);
ReleaseObj(pMC);
if (SUCCEEDED(hr)) { ChangeStateTo(Playing); TraceTag(ttidMedia, "Playing..."); return; } }
TraceTag(ttidMedia, "Can't play!"); } else { TraceTag(ttidMedia, "Not valid state for playing right now: %d.", media.state); }
} // OnMediaPlay
//
// OnMediaPause
//
VOID OnMediaPause() { if (CanPause()) { HRESULT hr; IMediaControl *pMC;
// Obtain the interface to our filter graph
hr = media.pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC); TraceError("OnMediaPause: QueryInterface()", hr);
if (SUCCEEDED(hr)) { // Ask the filter graph to pause and release the interface
hr = pMC->Pause(); TraceError("OnMediaPause: Pause()", hr); ReleaseObj(pMC);
if (SUCCEEDED(hr)) { ChangeStateTo(Paused); return; } } } else { TraceTag(ttidMedia, "Not valid state for pausing right now: %d.", media.state); }
} // OnMediaPause
//
// OnMediaStop
//
// There are two different ways to stop a graph. We can stop and then when we
// are later paused or run continue from the same position. Alternatively the
// graph can be set back to the start of the media when it is stopped to have
// a more CDPLAYER style interface. These are both offered here conditionally
// compiled using the FROM_START definition. The main difference is that in
// the latter case we put the current position to zero while we change states
//
VOID OnMediaStop() { if (CanStop()) { HRESULT hr; IMediaControl *pMC;
// Obtain the interface to our filter graph
hr = media.pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC); TraceError("OnMediaStop: QueryInterface(IID_IMediaControl)", hr); if (SUCCEEDED(hr)) {
#ifdef FROM_START
IMediaPosition * pMP; OAFilterState state;
// Ask the filter graph to pause
hr = pMC->Pause(); TraceError("OnMediaStop: Pause()", hr);
// if we want always to play from the beginning
// then we should seek back to the start here
// (on app or user stop request, and also after EC_COMPLETE).
hr = media.pGraph->QueryInterface(IID_IMediaPosition, (LPVOID *) &pMP); TraceError("OnMediaStop: QueryInterface(IID_IMediaPosition)", hr); if (SUCCEEDED(hr)) { pMP->put_CurrentPosition(0); ReleaseObj(pMP); }
// wait for pause to complete
pMC->GetState(INFINITE, &state); #endif
// now really do the stop
pMC->Stop(); ReleaseObj(pMC); ChangeStateTo(Stopped); return; } } else { TraceTag(ttidMedia, "Not valid state for pausing right now: %d.", media.state); }
} // OnMediaStop
|