Exempelkod för sekvenserare
Det här avsnittet visar exempelkod för att använda Sequencer Source i Microsoft Media Foundation. Den innehåller följande avsnitt.
- CPlaylist Class
- Skapa en instans av CPlaylist
- Lägga till och ta bort
- hantera sessionshändelser
- Relaterade ämnen
CPlaylist-klass
Klassen CPlaylist
härleds från klassen CPlayer
som visas i självstudien How to Play Media Files with Media Foundation.
const DWORD MAX_PLAYLIST_SEGMENTS = 128;
// For this example, there is a hard-coded limit to the number of playlist
// segments, to avoid using a dynamically sized list.
class CPlaylist : public CPlayer
{
private:
IMFSequencerSource *m_pSequencerSource;
IMFPresentationClock *m_pPresentationClock;
MFTIME m_PresentationTimeOffset;
DWORD m_ActiveSegment;
LONGLONG m_hnsSegmentDuration;
// Table of segment IDs and topology IDs.
struct
{
MFSequencerElementId SegmentID;
TOPOID TopoID;
} m_segments[MAX_PLAYLIST_SEGMENTS];
DWORD m_count;
private:
MFSequencerElementId LastSegment() const
{
return m_segments[ m_count - 1].SegmentID;
}
HRESULT LookupTopoID(TOPOID id, DWORD *pIndex);
HRESULT OnSessionEvent(IMFMediaEvent *pEvent, MediaEventType meType);
HRESULT OnTopologyStatus(IMFMediaEvent *pEvent);
HRESULT OnNewPresentation(IMFMediaEvent *pEvent);
HRESULT AddSegment(PCWSTR pszURL);
HRESULT QueueNextSegment(IMFPresentationDescriptor *pPD);
CPlaylist(HWND hWnd, HRESULT *phr);
~CPlaylist();
HRESULT Initialize();
public:
static HRESULT CreateInstance(HWND hwnd, CPlaylist **ppPlayer);
HRESULT AddToPlaylist(PCWSTR pszURL);
HRESULT DeleteSegment(DWORD index);
HRESULT SkipTo(DWORD index);
DWORD NumSegments() { return m_count; }
DWORD ActiveSegment() const { return m_ActiveSegment; }
HRESULT GetPlaybackTime(MFTIME *phnsPresentation, MFTIME *phnsSegment);
LONGLONG SegmentDuration() { return m_hnsSegmentDuration; }
};
Skapa en instans av CPlaylist
Metoden CPlaylist::CreateInstance
skapar ett nytt CPlaylist
objekt. Internt anropar den här metoden CPlaylist::Initialize
för att initiera objektet. Metoden Initialize
anropar MFCreateSequencerSource för att skapa sekvenskällan. Det anropar också IMFMediaSession::GetClock för att få en pekare till presentationsklockan.
/*static*/
HRESULT CPlaylist::CreateInstance(HWND hwnd, CPlaylist **ppPlayer)
{
*ppPlayer = NULL;
HRESULT hr = S_OK;
CPlaylist *pPlayer = new (std::nothrow) CPlaylist(hwnd, &hr);
if (pPlayer == NULL)
{
return E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = pPlayer->Initialize();
}
if (SUCCEEDED(hr))
{
*ppPlayer = pPlayer;
}
else
{
pPlayer->Release();
}
return hr;
}
HRESULT CPlaylist::Initialize()
{
IMFClock *pClock = NULL;
HRESULT hr = CPlayer::CreateSession();
if (FAILED(hr))
{
goto done;
}
// Create an instance of the sequencer Source.
hr = MFCreateSequencerSource(NULL, &m_pSequencerSource);
if (FAILED(hr))
{
goto done;
}
hr = m_pSequencerSource->QueryInterface(IID_PPV_ARGS(&m_pSource));
if (FAILED(hr))
{
goto done;
}
// Get the presentation clock.
hr = m_pSession->GetClock(&pClock);
if (FAILED(hr))
{
goto done;
}
hr = pClock->QueryInterface(IID_PPV_ARGS(&m_pPresentationClock));
done:
SafeRelease(&pClock);
return hr;
}
CPlaylist::CPlaylist(HWND hWnd, HRESULT *phr)
: CPlayer(NULL, hWnd, phr),
m_pSequencerSource (NULL),
m_pPresentationClock (NULL),
m_PresentationTimeOffset (0),
m_ActiveSegment((DWORD)-1),
m_hnsSegmentDuration(0),
m_count(0)
{
}
CPlaylist::~CPlaylist()
{
SafeRelease(&m_pSequencerSource);
SafeRelease(&m_pPresentationClock);
}
Lägga till och ta bort spellistesegment
Metoden AddSegment
lägger till ett nytt spellistesegment.
Den här metoden utför följande steg:
- Skapar en uppspelningstopologi. Koden för det här steget visas i avsnittet Skapa uppspelningstopologier.
- Anropar IMFSequencerSource::AppendTopology för att lägga till topologin i spellistan.
- I det första segmentet hämtar du värdet för attributet MF_PD_DURATION, som innehåller uppspelningstiden.
- Lagrar segment-ID och topologi-ID i en uppslagstabell.
// Adds a segment to the sequencer.
HRESULT CPlaylist::AddSegment(PCWSTR pszURL)
{
IMFMediaSource *pMediaSource = NULL;
IMFPresentationDescriptor *pPD = NULL;
IMFTopology *pTopology = NULL;
MFSequencerElementId SegmentId;
TOPOID TopologyID = 0;
HRESULT hr = CreateMediaSource(pszURL, &pMediaSource);
if (FAILED(hr))
{
goto done;
}
hr = pMediaSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto done;
}
hr = CreatePlaybackTopology(pMediaSource, pPD, m_hwndVideo, &pTopology);
if (FAILED(hr))
{
goto done;
}
hr = m_pSequencerSource->AppendTopology(
pTopology,
SequencerTopologyFlags_Last,
&SegmentId
);
if (FAILED(hr))
{
goto done;
}
hr = pTopology->GetTopologyID(&TopologyID);
if (FAILED(hr))
{
goto done;
}
if (m_count == 0)
{
// Get the segment duration
m_hnsSegmentDuration = MFGetAttributeUINT64(pPD, MF_PD_DURATION, 0);
}
m_segments[m_count].SegmentID = SegmentId;
m_segments[m_count].TopoID = TopologyID;
m_count++;
done:
SafeRelease(&pMediaSource);
SafeRelease(&pTopology);
SafeRelease(&pPD);
return hr;
}
Metoden AddSegment
är privat för CPlaylist
och anropas från följande offentliga metod:
// Adds a new segment to the playlist.
HRESULT CPlaylist::AddToPlaylist(PCWSTR pszURL)
{
if (NumSegments() >= MAX_PLAYLIST_SEGMENTS)
{
return MF_E_OUT_OF_RANGE;
}
HRESULT hr = S_OK;
IMFPresentationDescriptor *pPD = NULL;
BOOL bFirstSegment = (NumSegments() == 0);
if (!bFirstSegment)
{
// Remove the "last segment" flag from the last segment.
hr = m_pSequencerSource->UpdateTopologyFlags(LastSegment(), 0);
if (FAILED(hr))
{
goto done;
}
}
// Create the topology and add it to the sequencer.
hr = AddSegment(pszURL);
if (FAILED(hr))
{
goto done;
}
// If this is the first segment, queue it on the session.
if (bFirstSegment)
{
hr = m_pSource->CreatePresentationDescriptor(&pPD);
if (FAILED(hr))
{
goto done;
}
hr = QueueNextSegment(pPD);
if (FAILED(hr))
{
goto done;
}
}
if (m_state < Started)
{
m_state = OpenPending;
}
done:
SafeRelease(&pPD);
return hr;
}
När du skapar det första spellistesegmentet måste du köa segmenttopologin på mediesessionen enligt följande:
- Fråga sekvenserarens källa efter IMFMediaSourceTopologyProvider-gränssnittet.
- Skicka presentationsbeskrivningen till IMFMediaSourceTopologyProvider::GetMediaSourceTopology-metoden. Den här metoden hämtar en pekare till segmenttopologin. Observera att den här topologin inte är exakt samma som uppspelningstopologin som du skapade tidigare. I stället är det en modifierad version av topologin. Mer information finns i Om sekvenserarens källa.
- Köa topologin på mediesessionen genom att anropa IMFMediaSession::SetTopology.
Följande kod visar de här stegen. Samma kod används också när spellistan förregistrerar nästa segment.
// Queues the next topology on the session.
HRESULT CPlaylist::QueueNextSegment(IMFPresentationDescriptor *pPD)
{
IMFMediaSourceTopologyProvider *pTopoProvider = NULL;
IMFTopology *pTopology = NULL;
//Get the topology for the presentation descriptor
HRESULT hr = m_pSequencerSource->QueryInterface(IID_PPV_ARGS(&pTopoProvider));
if (FAILED(hr))
{
goto done;
}
hr = pTopoProvider->GetMediaSourceTopology(pPD, &pTopology);
if (FAILED(hr))
{
goto done;
}
//Set the topology on the media session
m_pSession->SetTopology(NULL, pTopology);
done:
SafeRelease(&pTopoProvider);
SafeRelease(&pTopology);
return hr;
}
Om du vill ta bort ett spellistesegment anropar du IMFSequencerSource::D eleteTopology. Segmentet anges med segment-ID. (Det är därför programmet ska cachelagra listan över segment-ID:t.)
// Deletes the corresponding topology from the sequencer source
HRESULT CPlaylist::DeleteSegment(DWORD index)
{
if (index >= m_count)
{
return E_INVALIDARG;
}
if (index == m_ActiveSegment)
{
return E_INVALIDARG;
}
MFSequencerElementId LastSegId = LastSegment();
MFSequencerElementId SegmentID = m_segments[index].SegmentID;
HRESULT hr = m_pSequencerSource->DeleteTopology( SegmentID );
if (FAILED(hr))
{
goto done;
}
//Delete the segment entry from the list.
// Move everything up one slot.
for (DWORD i = index; i < m_count - 1; i++)
{
m_segments[i] = m_segments[i + 1];
}
m_count --;
// Is the deleted topology the last one?
if (LastSegId == SegmentID)
{
//Get the new last segment id
LastSegId = LastSegment();
//set this topology as the last in the sequencer
hr = m_pSequencerSource->UpdateTopologyFlags(LastSegId, SequencerTopologyFlags_Last);
}
done:
return hr;
}
Hantera sessionshändelser
HRESULT CPlaylist::OnTopologyStatus(IMFMediaEvent *pEvent)
{
IMFTopology *pTopology = NULL;
UINT value = 0;
DWORD SegmentIndex;
TOPOID TopoID;
HRESULT hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, &value);
if (FAILED(hr))
{
goto done;
}
switch(value)
{
case MF_TOPOSTATUS_STARTED_SOURCE:
// Get information about the new segment
hr = GetEventObject(pEvent, &pTopology);
if (FAILED(hr))
{
hr = S_OK;
goto done;
}
hr = pTopology->GetTopologyID(&TopoID);
if (FAILED(hr))
{
goto done;
}
hr = LookupTopoID(TopoID, &SegmentIndex);
if (FAILED(hr))
{
goto done;
}
m_ActiveSegment = SegmentIndex;
hr = GetDurationFromTopology(pTopology, &m_hnsSegmentDuration);
break;
case MF_TOPOSTATUS_ENDED:
m_ActiveSegment = (DWORD)-1;
break;
case MF_TOPOSTATUS_READY:
if (m_state == OpenPending)
{
m_state = Stopped;
}
break;
}
done:
SafeRelease(&pTopology);
return hr;
}
HRESULT CPlaylist::LookupTopoID(TOPOID id, DWORD *pIndex)
{
DWORD index;
for (index = 0; index < m_count; index++)
{
if (m_segments[ index ].TopoID == id)
{
break;
}
}
if (index == m_count)
{
return MF_E_NOT_FOUND;
}
*pIndex = index;
return S_OK;
}
HRESULT CPlaylist::OnSessionEvent(IMFMediaEvent *pEvent, MediaEventType meType)
{
if (meType == MESessionNotifyPresentationTime)
{
m_PresentationTimeOffset =
MFGetAttributeUINT64(pEvent, MF_EVENT_PRESENTATION_TIME_OFFSET, 0);
}
return S_OK;
}
HRESULT CPlaylist::OnNewPresentation(IMFMediaEvent *pEvent)
{
IMFPresentationDescriptor *pPD = NULL;
HRESULT hr = GetEventObject(pEvent, &pPD);
if (SUCCEEDED(hr))
{
// Queue the next segment on the media session
hr = QueueNextSegment(pPD);
}
SafeRelease(&pPD);
return hr;
}
// Skips to the specified segment in the sequencer source
HRESULT CPlaylist::SkipTo(DWORD index)
{
if (index >= m_count)
{
return E_INVALIDARG;
}
MFSequencerElementId ID = m_segments[index].SegmentID;
PROPVARIANT var;
HRESULT hr = MFCreateSequencerSegmentOffset(ID, NULL, &var);
if (SUCCEEDED(hr))
{
hr = m_pSession->Start(&MF_TIME_FORMAT_SEGMENT_OFFSET, &var);
PropVariantClear(&var);
}
return hr;
}
HRESULT GetDurationFromTopology(IMFTopology *pTopology, LONGLONG *phnsDuration)
{
*phnsDuration = 0;
IMFCollection *pSourceNodes = NULL;
IMFTopologyNode *pNode = NULL;
IMFPresentationDescriptor *pPD = NULL;
HRESULT hr = pTopology->GetSourceNodeCollection(&pSourceNodes);
if (FAILED(hr))
{
goto done;
}
hr = GetCollectionObject(pSourceNodes, 0, &pNode);
if (FAILED(hr))
{
goto done;
}
hr = pNode->GetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, IID_PPV_ARGS(&pPD));
if (FAILED(hr))
{
goto done;
}
*phnsDuration = MFGetAttributeUINT64(pPD, MF_PD_DURATION, 0);
done:
SafeRelease(&pSourceNodes);
SafeRelease(&pNode);
SafeRelease(&pPD);
return hr;
}
HRESULT CPlaylist::GetPlaybackTime(
MFTIME *phnsPresentation, // Relative to start of presentation.
MFTIME *phnsSegment // Relative to start of segment.
)
{
*phnsPresentation = 0;
*phnsSegment = 0;
HRESULT hr = m_pPresentationClock->GetTime(phnsPresentation);
if (SUCCEEDED(hr))
{
*phnsSegment = (*phnsPresentation) - m_PresentationTimeOffset;
}
return hr;
}
Relaterade ämnen