//+--------------------------------------------------------------------------- // // 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