Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Thema wird Beispielcode für die Verwendung der Sequencer Source- in Microsoft Media Foundation gezeigt. Sie enthält die folgenden Abschnitte.
- CPlaylist-Klasse
- Erstellen einer Instanz von CPlaylist-
- Hinzufügen und Entfernen von Wiedergabelistensegmenten
- Behandeln von Sitzungsereignissen
- Verwandte Themen
CPlaylist-Klasse
Die CPlaylist Klasse wird von der CPlayer Klasse abgeleitet, die im Lernprogramm So wird's ausgeführt: Wiedergeben von Mediendateien mit 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; }
};
Erstellen einer Instanz von CPlaylist
Die CPlaylist::CreateInstance-Methode erstellt ein neues CPlaylist-Objekt. Intern ruft diese Methode CPlaylist::Initialize auf, um das Objekt zu initialisieren. Die Initialize-Methode ruft MFCreateSequencerSource- auf, um die Sequenzquelle zu erstellen. Außerdem wird IMFMediaSession::GetClock aufgerufen, um einen Zeiger auf die Präsentationsuhr zu erhalten.
/*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);
}
Hinzufügen und Entfernen von Wiedergabelistensegmenten
Die methode AddSegment fügt ein neues Wiedergabelistensegment hinzu.
Diese Methode führt die folgenden Schritte aus:
- Erstellt eine Wiedergabetopologie. Der Code für diesen Schritt wird im Thema Erstellen von Wiedergabetopologiengezeigt.
- Ruft IMFSequencerSource::AppendTopology auf, um der Wiedergabeliste die Topologie hinzuzufügen.
- Ruft im ersten Segment den Wert des attributs MF_PD_DURATION ab, das die Wiedergabedauer enthält.
- Speichert die Segment-ID und topologie-ID in einer Nachschlagetabelle.
// 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;
}
Die AddSegment-Methode ist privat für CPlaylistund wird von der folgenden öffentlichen Methode aufgerufen:
// 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;
}
Wenn Sie das erste Wiedergabelistensegment erstellen, müssen Sie die Segmenttopologie in der Mediensitzung wie folgt in die Warteschlange stellen:
- Abfragen der Sequenzerquelle für die IMFMediaSourceTopologyProvider Schnittstelle.
- Übergeben Sie den Präsentationsdeskriptor an die IMFMediaSourceTopologyProvider::GetMediaSourceTopology-Methode. Diese Methode ruft einen Zeiger auf die Segmenttopologie ab. Beachten Sie, dass diese Topologie nicht identisch mit der Wiedergabetopologie ist, die Sie zuvor erstellt haben. Stattdessen handelt es sich um eine geänderte Version dieser Topologie. Weitere Informationen finden Sie unter Informationen zur Sequenzerquelle.
- Warteschlangen Sie die Topologie in der Mediensitzung, indem Sie IMFMediaSession::SetTopologyaufrufen.
Der folgende Code zeigt diese Schritte. Dieser Code wird auch verwendet, wenn die Wiedergabeliste das nächste Segment vorrollt.
// 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;
}
Rufen Sie zum Löschen eines Wiedergabelistensegments IMFSequencerSource::D eleteTopology-auf. Das Segment wird durch Segment-ID angegeben. (Aus diesem Grund sollte die Anwendung die Liste der Segment-IDs zwischenspeichern.)
// 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;
}
Behandeln von Sitzungsereignissen
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;
}
Verwandte Themen