2025-11-28 00:35:46 +09:00

946 lines
20 KiB
C++

//------------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//------------------------------------------------------------------------------
#include "MFPlayer.h"
#include "Player2.h"
//-----------------------------------------------------------------------------
// CreateInstance
//
// Creates an instance of the MFPlayer2 object.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::CreateInstance(HWND hwndEvent, HWND hwndVideo, MFPlayer2 **ppPlayer)
{
HRESULT hr = S_OK;
MFPlayer2 *pPlayer = new (std::nothrow) MFPlayer2(hwndEvent);
if (pPlayer == NULL)
{
return E_OUTOFMEMORY;
}
hr = pPlayer->Initialize(hwndVideo);
if (SUCCEEDED(hr))
{
*ppPlayer = pPlayer;
(*ppPlayer)->AddRef();
}
SafeRelease(&pPlayer);
return hr;
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
MFPlayer2::MFPlayer2(HWND hwndEvent) :
m_cRef(1),
m_pPlayer(NULL),
m_hwndEvent(hwndEvent),
m_bHasVideo(FALSE),
m_fZoom(1.0f),
m_pVolume(NULL),
m_fRate(1.0f),
m_caps(0)
{
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
MFPlayer2::~MFPlayer2()
{
if (m_pVolume)
{
(void)m_pVolume->EnableNotifications(FALSE);
}
SafeRelease(&m_pPlayer);
SafeRelease(&m_pVolume);
}
//***************************** IUnknown methods *****************************//
//------------------------------------------------------------------------------
// AddRef
//------------------------------------------------------------------------------
ULONG MFPlayer2::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
//------------------------------------------------------------------------------
// Release
//------------------------------------------------------------------------------
ULONG MFPlayer2::Release()
{
ULONG uCount = InterlockedDecrement(&m_cRef);
if (uCount == 0)
{
delete this;
}
return uCount;
}
//------------------------------------------------------------------------------
// QueryInterface
//------------------------------------------------------------------------------
STDMETHODIMP MFPlayer2::QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] =
{
QITABENT(MFPlayer2, IMFPMediaPlayerCallback),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
//********************* IMFPMediaPlayerCallback methods **********************//
//-----------------------------------------------------------------------------
// OnMediaPlayerEvent
//
// Notifies the object of an MFPlay event.
//-----------------------------------------------------------------------------
void MFPlayer2::OnMediaPlayerEvent(MFP_EVENT_HEADER * pEventHeader)
{
if (FAILED(pEventHeader->hrEvent))
{
NotifyError(pEventHeader->hrEvent);
return;
}
switch (pEventHeader->eEventType)
{
case MFP_EVENT_TYPE_MEDIAITEM_CREATED:
OnMediaItemCreated(MFP_GET_MEDIAITEM_CREATED_EVENT(pEventHeader));
break;
case MFP_EVENT_TYPE_MEDIAITEM_SET:
OnMediaItemSet(MFP_GET_MEDIAITEM_SET_EVENT(pEventHeader));
break;
case MFP_EVENT_TYPE_RATE_SET:
OnRateSet(MFP_GET_RATE_SET_EVENT(pEventHeader));
break;
case MFP_EVENT_TYPE_PLAYBACK_ENDED:
case MFP_EVENT_TYPE_STOP:
SetPlaybackRate(1.0f);
break;
}
NotifyState(pEventHeader->eState);
}
//*************************** Other class methods ****************************//
//-----------------------------------------------------------------------------
// OpenURL
//
// Open a media file by URL.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::OpenURL(const WCHAR *sURL)
{
HRESULT hr = S_OK;
if (sURL == NULL)
{
return E_POINTER;
}
if (m_pPlayer == NULL)
{
return E_UNEXPECTED;
}
// Create a new media item for this URL.
hr = m_pPlayer->CreateMediaItemFromURL(sURL, FALSE, 0, NULL);
// The CreateMediaItemFromURL method completes asynchronously. When it does,
// MFPlay sends an MFP_EVENT_TYPE_MEDIAITEM_CREATED event.
return hr;
}
//-----------------------------------------------------------------------------
// Play
//
// Start playback.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::Play()
{
HRESULT hr = S_OK;
if (m_pPlayer)
{
hr = m_pPlayer->Play();
}
return hr;
}
//-----------------------------------------------------------------------------
// Pause
//
// Pause playback.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::Pause()
{
HRESULT hr = S_OK;
if (m_pPlayer)
{
hr = m_pPlayer->Pause();
}
return hr;
}
//-----------------------------------------------------------------------------
// Shutdown
//
// Shutdown the MFPlay object.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::Shutdown()
{
HRESULT hr = S_OK;
if (m_pPlayer)
{
hr = m_pPlayer->Shutdown();
}
return hr;
}
//-----------------------------------------------------------------------------
// GetState
//
// Get the current state of the MFPlay object.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::GetState(MFP_MEDIAPLAYER_STATE *pState)
{
if (pState == NULL)
{
return E_POINTER;
}
HRESULT hr = S_OK;
MFP_MEDIAPLAYER_STATE state = MFP_MEDIAPLAYER_STATE_EMPTY;
if (m_pPlayer)
{
hr = m_pPlayer->GetState(&state);
}
if (SUCCEEDED(hr))
{
*pState = state;
}
return hr;
}
//-----------------------------------------------------------------------------
// GetMetadata
//
// Get a read-only property store that contains metadata for the current
// media file.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::GetMetadata(IPropertyStore **ppProp)
{
if (ppProp == NULL)
{
return E_POINTER;
}
if (m_pPlayer == NULL)
{
return E_FAIL;
}
HRESULT hr = S_OK;
IMFPMediaItem *pItem = NULL;
IPropertyStore *pProp = NULL;
hr = m_pPlayer->GetMediaItem(&pItem);
if (FAILED(hr)) { goto done; }
hr = pItem->GetMetadata(&pProp);
if (FAILED(hr)) { goto done; }
*ppProp = pProp;
(*ppProp)->AddRef();
done:
SafeRelease(&pProp);
SafeRelease(&pItem);
return hr;
}
//-----------------------------------------------------------------------------
// HasVideo
//
// Queries if current media file has a video stream.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::HasVideo(BOOL *pfHasVideo)
{
if (pfHasVideo == NULL)
{
return E_POINTER;
}
*pfHasVideo = m_bHasVideo;
return S_OK;
}
//-----------------------------------------------------------------------------
// SetZoom
//
// Sets the video zoom level.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetZoom(float fZoom)
{
if (fZoom < 1.0f)
{
return E_INVALIDARG;
}
if (m_fZoom == fZoom)
{
return S_OK; // no-op
}
m_fZoom = fZoom;
return SetZoom();
}
//-----------------------------------------------------------------------------
// SetZoom
//
// Sets the video zoom level (using the current cached zoom value).
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetZoom()
{
HRESULT hr = S_OK;
if (m_bHasVideo && m_pPlayer)
{
if (m_fZoom == 1.0f)
{
// For 100% zoom, the normalized rectangle is {0,0,1,1}.
MFVideoNormalizedRect nrcSource = { 0.0f, 0.0f, 1.0f, 1.0f };
hr = m_pPlayer->SetVideoSourceRect(&nrcSource);
}
else
{
// For higher zoom levels, calculate the normalized rectangle.
float fMargin = (0.5f - (0.5f / m_fZoom));
MFVideoNormalizedRect nrcSource = {
fMargin, fMargin, (1.0f - fMargin), (1.0f - fMargin)
};
hr = m_pPlayer->SetVideoSourceRect(&nrcSource);
}
}
return hr;
}
//-----------------------------------------------------------------------------
// UpdateVideo
//
// Call this method to repaint the current video frame or when the video
// window is resized.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::UpdateVideo()
{
HRESULT hr = S_OK;
if (m_pPlayer)
{
hr = m_pPlayer->UpdateVideo();
}
return hr;
}
//-----------------------------------------------------------------------------
// SetVolume
//
// Sets the audio volume.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetVolume(float fLevel)
{
HRESULT hr = S_OK;
if (m_pVolume)
{
hr = m_pVolume->SetVolume(fLevel);
}
else
{
hr = E_FAIL;
}
return hr;
}
//-----------------------------------------------------------------------------
// GetVolume
//
// Gets the audio volume.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::GetVolume(float *pfLevel)
{
HRESULT hr = S_OK;
if (pfLevel == NULL)
{
return E_POINTER;
}
if (m_pVolume)
{
hr = m_pVolume->GetVolume(pfLevel);
}
else
{
hr = E_FAIL;
}
return hr;
}
//-----------------------------------------------------------------------------
// SetMute
//
// Mutes or unmutes the audio.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetMute(BOOL bMute)
{
HRESULT hr = S_OK;
if (m_pVolume)
{
hr = m_pVolume->SetMute(bMute);
}
else
{
hr = E_FAIL;
}
return hr;
}
//-----------------------------------------------------------------------------
// GetMute
//
// Gets the audio mute state.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::GetMute(BOOL *pbMute)
{
HRESULT hr = S_OK;
if (pbMute == NULL)
{
return E_POINTER;
}
if (m_pVolume)
{
hr = m_pVolume->GetMute(pbMute);
}
else
{
hr = E_FAIL;
}
return hr;
}
//-----------------------------------------------------------------------------
// CanSeek
//
// Queries whether the current media file is seekable.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::CanSeek(BOOL *pbCanSeek)
{
*pbCanSeek = ((m_caps & MFP_MEDIAITEM_CAN_SEEK) && !(m_caps & MFP_MEDIAITEM_HAS_SLOW_SEEK));
return S_OK;
}
//-----------------------------------------------------------------------------
// GetDuration
//
// Gets the playback duration.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::GetDuration(MFTIME *phnsDuration)
{
HRESULT hr = E_FAIL;
PROPVARIANT var;
PropVariantInit(&var);
if (m_pPlayer)
{
hr = m_pPlayer->GetDuration(
MFP_POSITIONTYPE_100NS,
&var
);
if (SUCCEEDED(hr))
{
*phnsDuration = var.uhVal.QuadPart;
}
}
PropVariantClear(&var);
return hr;
}
//-----------------------------------------------------------------------------
// GetCurrentPosition
//
// Gets the current playback position.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::GetCurrentPosition(MFTIME *phnsPosition)
{
HRESULT hr = E_FAIL;
PROPVARIANT var;
PropVariantInit(&var);
if (m_pPlayer)
{
hr = m_pPlayer->GetPosition(
MFP_POSITIONTYPE_100NS,
&var
);
if (SUCCEEDED(hr))
{
*phnsPosition = var.hVal.QuadPart;
}
}
PropVariantClear(&var);
return hr;
}
//-----------------------------------------------------------------------------
// SetPosition
//
// Sets the current playback position.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetPosition(MFTIME hnsPosition)
{
HRESULT hr = E_FAIL;
PROPVARIANT var;
PropVariantInit(&var);
if (m_pPlayer)
{
var.vt = VT_I8;
var.hVal.QuadPart = hnsPosition;
hr = m_pPlayer->SetPosition(
MFP_POSITIONTYPE_100NS,
&var
);
}
PropVariantClear(&var);
return hr;
}
//-----------------------------------------------------------------------------
// CanFastForward
//
// Queries whether the current media file supports fast-forward playback.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::CanFastForward(BOOL *pbCanFF)
{
HRESULT hr = S_OK;
BOOL bCanFF = FALSE;
float fSlowest = 0.0f, fFastest = 0.0f;
if (m_pPlayer)
{
// Ask for the supported rates in the forward direction.
hr = m_pPlayer->GetSupportedRates(TRUE, &fSlowest, &fFastest);
}
if (SUCCEEDED(hr) && fFastest > 1.0f)
{
bCanFF = TRUE;
}
*pbCanFF = bCanFF;
return S_OK;
}
//-----------------------------------------------------------------------------
// CanRewind
//
// Queries whether the current media file supports reverse playback.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::CanRewind(BOOL *pbCanRewind)
{
HRESULT hr = S_OK;
BOOL bCanRew = FALSE;
float fSlowest = 0.0f, fFastest = 0.0f;
if (m_pPlayer)
{
// Ask for the supported rates in the reverse direction.
hr = m_pPlayer->GetSupportedRates(FALSE, &fSlowest, &fFastest);
}
if (SUCCEEDED(hr))
{
bCanRew = TRUE;
}
*pbCanRewind = bCanRew;
return S_OK;
}
//-----------------------------------------------------------------------------
// SetPlaybackRate
//
// Sets the playback rate.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetPlaybackRate(float fRate)
{
if (fRate == GetNominalRate())
{
return S_OK; // no-op
}
HRESULT hr = S_OK;
hr = m_pPlayer->SetRate(fRate);
if (SUCCEEDED(hr))
{
m_fRate = fRate;
}
return hr;
}
//------------------------------------------------------------------------------
// FastForward
//
// Switches to fast-forward playback, as follows:
// - If the current rate is < 0 (reverse play), switch to 1x speed.
// - Otherwise, double the current playback rate.
//
// This method is just for convenience; the app could call SetPlaybackRate.
//------------------------------------------------------------------------------
HRESULT MFPlayer2::FastForward()
{
HRESULT hr = S_OK;
float fTarget = GetNominalRate() * 2;
if (fTarget <= 0.0f)
{
fTarget = 1.0f;
}
hr = SetPlaybackRate(fTarget);
return hr;
}
//------------------------------------------------------------------------------
// FastForward
//
// Switches to reverse playback, as follows:
// - If the current rate is > 0 (forward playback), switch to -1x speed.
// - Otherwise, double the current (reverse) playback rate.
//
// This method is for convenience; the app could call SetPlaybackRate.
//------------------------------------------------------------------------------
HRESULT MFPlayer2::Rewind()
{
HRESULT hr = S_OK;
float fTarget = GetNominalRate() * 2;
if (fTarget >= 0.0f)
{
fTarget = -1.0f;
}
hr = SetPlaybackRate(fTarget);
return hr;
}
//-----------------------------------------------------------------------------
// FrameStep
//
// Steps forward one frame.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::FrameStep()
{
HRESULT hr = E_FAIL;
if (m_pPlayer)
{
hr = m_pPlayer->FrameStep();
}
return hr;
}
//-----------------------------------------------------------------------------
// SetEffect
//
// Adds a video or audio effect.
//
// To remove all effects, pass NULL.
//-----------------------------------------------------------------------------
HRESULT MFPlayer2::SetEffect(IMFTransform *pMFT)
{
if (m_pPlayer == NULL)
{
return E_FAIL;
}
HRESULT hr = S_OK;
if (pMFT == NULL)
{
hr = m_pPlayer->RemoveAllEffects();
}
else
{
hr = m_pPlayer->InsertEffect(pMFT, TRUE);
}
return hr;
}
//------------------------------------------------------------------------------
// Initialize
// Creates an instance of the MFPlay player object.
//
// hwndVideo:
// Handle to the video window.
//------------------------------------------------------------------------------
HRESULT MFPlayer2::Initialize(HWND hwndVideo)
{
HRESULT hr = S_OK;
SafeRelease(&m_pPlayer);
hr = MFPCreateMediaPlayer(
NULL,
FALSE, // Start playback automatically?
0, // Flags
this, // Callback pointer
hwndVideo, // Video window
&m_pPlayer
);
if (FAILED(hr))
{
return hr;
}
// Also create the object that manages to audio session.
// This can fail if the machine does not have an audio end-point.
HRESULT hrAudio = CAudioSessionVolume::CreateInstance(
WM_AUDIO_EVENT,
m_hwndEvent,
&m_pVolume
);
if (SUCCEEDED(hrAudio))
{
// Ask for audio session events.
hrAudio = m_pVolume->EnableNotifications(TRUE);
}
if (FAILED(hrAudio))
{
SafeRelease(&m_pVolume);
}
return hr;
}
// MFPlay event handler functions.
//-----------------------------------------------------------------------------
// OnMediaItemCreated
//
// Called when the CreateMediaItem method completes.
//-----------------------------------------------------------------------------
void MFPlayer2::OnMediaItemCreated(MFP_MEDIAITEM_CREATED_EVENT *pEvent)
{
HRESULT hr = S_OK;
IUnknown *pMFT = NULL;
m_bHasVideo = TRUE;
if ((m_pPlayer != NULL) && (pEvent->pMediaItem != NULL))
{
BOOL bHasVideo = FALSE, bIsSelected = FALSE;
hr = pEvent->pMediaItem->HasVideo(&bHasVideo, &bIsSelected);
if (FAILED(hr)) { goto done; }
m_bHasVideo = bHasVideo && bIsSelected;
hr = m_pPlayer->SetMediaItem(pEvent->pMediaItem);
if (FAILED(hr)) { goto done; }
}
done:
if (FAILED(hr))
{
NotifyError(hr);
}
SafeRelease(&pMFT);
}
//-----------------------------------------------------------------------------
// OnMediaItemSet
//
// Called when the SetMediaItem method completes.
//-----------------------------------------------------------------------------
void MFPlayer2::OnMediaItemSet(MFP_MEDIAITEM_SET_EVENT *pEvent)
{
HRESULT hr = S_OK;
hr = pEvent->header.hrEvent;
if (FAILED(hr)) { goto done; }
if (pEvent->pMediaItem)
{
hr = pEvent->pMediaItem->GetCharacteristics(&m_caps);
if (FAILED(hr)) { goto done; }
}
hr = m_pPlayer->Play();
done:
if (FAILED(hr))
{
NotifyError(hr);
}
}
//-----------------------------------------------------------------------------
// OnRateSet
//
// Called when the SetRate method completes.
//-----------------------------------------------------------------------------
void MFPlayer2::OnRateSet(MFP_RATE_SET_EVENT *pEvent)
{
m_fRate = pEvent->flRate;
}