Vom System unterstützte, zeitbasierte Metadaten-Marker

In diesem Artikel wird beschrieben, wie Sie mehrere Formate von Zeitmetadaten nutzen können, die in Mediendateien oder Datenströme eingebettet werden können. UWP-Apps können für Ereignisse registrieren, die während der Wiedergabe von der Medienpipeline ausgelöst werden, wenn diese Metadatenhinweise gefunden werden. Mithilfe der DataCue-Klasse können Apps ihre eigenen benutzerdefinierten Metadatenhinweise implementieren. Dieser Artikel konzentriert sich jedoch auf mehrere Metadatenstandards, die automatisch von der Medienpipeline erkannt werden, einschließlich:

  • Bildbasierte Untertitel im VobSub-Format
  • Sprachhinweise, einschließlich Wortgrenzen, Satzgrenzen und SSML-Textmarken (Speech Synthesis Markup Language)
  • Kapitelhinweise
  • Erweiterte M3U-Kommentare
  • ID3-Tags
  • Fragmentierte mp4 emsg Boxen

Dieser Artikel baut auf den Konzepten auf, die im Artikel "Medienelemente", "Wiedergabelisten" und "Tracks" erläutert werden, einschließlich der Grundlagen der Arbeit mit den Klassen "MediaSource", "MediaPlaybackItem" und "TimedMetadataTrack" und allgemeinen Anleitungen für die Verwendung von zeitgesteuerten Metadaten in Ihrer App.

Die grundlegenden Implementierungsschritte sind für alle unterschiedlichen Arten von Zeitgebermetadaten identisch, die in diesem Artikel beschrieben werden:

  1. Erstellen Sie eine MediaSource und dann ein MediaPlaybackItem-Element , damit der Inhalt wiedergegeben werden kann.
  2. Registrieren Sie sich für das MediaPlaybackItem.TimedMetadataTracksChanged-Ereignis , das auftritt, wenn die Unterspuren des Medienelements von der Medienpipeline aufgelöst werden.
  3. Registrieren Sie sich für die Ereignisse TimedMetadataTrack.CueEntered und TimedMetadataTrack.CueExited für die zeitbezogene Metadatentitel, die Sie verwenden möchten.
  4. Aktualisieren Sie im CueEntered-Ereignishandler Die Benutzeroberfläche basierend auf den metadaten, die in den Ereignisargumenten übergeben werden. Sie können die Benutzeroberfläche erneut aktualisieren, um beispielsweise den aktuellen Untertiteltext im CueExited-Ereignis zu entfernen.

In diesem Artikel wird die Behandlung der einzelnen Metadatentypen als unterschiedliches Szenario gezeigt, es ist jedoch möglich, verschiedene Metadatentypen mithilfe von hauptsächlich freigegebenem Code zu behandeln (oder zu ignorieren). Sie können die TimedMetadataKind-Eigenschaft des TimedMetadataTrack-Objekts an mehreren Stellen im Prozess überprüfen. Sie können sich z. B. für das CueEntered-Ereignis für Metadatentitel registrieren, die den Wert "TimedMetadataKind.ImageSubtitle", aber nicht für Titel mit dem Wert "TimedMetadataKind.Speech" aufweisen. Alternativ können Sie einen Handler für alle Metadatentiteltypen registrieren und dann den TimedMetadataKind-Wert innerhalb des CueEntered-Handlers überprüfen, um zu bestimmen, welche Aktion als Reaktion auf den Hinweis ausgeführt werden soll.

Bildbasierte Untertitel

Ab Windows 10, Version 1703, können UWP-Apps externe bildbasierte Untertitel im VobSub-Format unterstützen. Um dieses Feature zu verwenden, erstellen Sie zuerst ein MediaSource-Objekt für den Medieninhalt, für den Bilduntertitel angezeigt werden. Erstellen Sie als Nächstes ein TimedTextSource-Objekt, indem Sie CreateFromUriWithIndex oder CreateFromStreamWithIndex aufrufen und den URI der Subdatei übergeben, die die Untertitelbilddaten und die IDX-Datei mit den Anzeigedauerinformationen für die Untertitel enthält. Fügen Sie die TimedTextSource der MediaSource hinzu, indem Sie sie der ExternalTimedTextSources-Auflistung der Quelle hinzufügen. Erstellen Sie ein MediaPlaybackItem aus der MediaSource.

var contentUri = new Uri("http://contoso.com/content.mp4");
var mediaSource = MediaSource.CreateFromUri(contentUri);

var subUri = new Uri("http://contoso.com/content.sub");
var idxUri = new Uri("http://contoso.com/content.idx");
var timedTextSource = TimedTextSource.CreateFromUriWithIndex(subUri, idxUri);
mediaSource.ExternalTimedTextSources.Add(timedTextSource);

var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Registrieren Sie sich für die Metadatenereignisse für Bilduntertitel, indem Sie das im vorherigen Schritt erstellte MediaPlaybackItem-Objekt verwenden. In diesem Beispiel wird die Hilfsmethode RegisterMetadataHandlerForImageSubtitles verwendet, um die Ereignisse zu registrieren. Ein Lambda-Ausdruck wird verwendet, um einen Handler für das TimedMetadataTracksChanged-Ereignis zu implementieren, das auftritt, wenn das System eine Änderung der Metadatentitel erkennt, die einem MediaPlaybackItem zugeordnet sind. In einigen Fällen sind die Metadatenspuren möglicherweise verfügbar, wenn das Wiedergabeelement anfänglich aufgelöst wird, sodass außerhalb des TimedMetadataTracksChanged-Handlers auch die verfügbaren Metadatentitel durchlaufen und RegisterMetadataHandlerForImageSubtitles aufgerufen werden.

mediaPlaybackItem.TimedMetadataTracksChanged += (MediaPlaybackItem sender, IVectorChangedEventArgs args) =>
{
    if (args.CollectionChange == CollectionChange.ItemInserted)
    {
        RegisterMetadataHandlerForImageSubtitles(sender, (int)args.Index);
    }
    else if (args.CollectionChange == CollectionChange.Reset)
    {
        for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
        {
            if (sender.TimedMetadataTracks[index].TimedMetadataKind == TimedMetadataKind.ImageSubtitle)
                RegisterMetadataHandlerForImageSubtitles(sender, index);
        }
    }
};

for (int index = 0; index < mediaPlaybackItem.TimedMetadataTracks.Count; index++)
{
    RegisterMetadataHandlerForImageSubtitles(mediaPlaybackItem, index);
}

Nach der Registrierung für die Metadatenereignisse für Bilduntertitel wird das MediaItem-Objekt einem MediaPlayer für die Wiedergabe in einem MediaPlayerElement zugewiesen.

_mediaPlayer = new MediaPlayer();
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
_mediaPlayer.Source = mediaPlaybackItem;
_mediaPlayer.Play();

Rufen Sie in der Hilfsmethode RegisterMetadataHandlerForImageSubtitles eine Instanz der TimedMetadataTrack-Klasse ab, indem Sie die TimedMetadataTracks-Auflistung des MediaPlaybackItem indizieren. Registrieren Sie sich für das CueEntered-Ereignis und das CueExited-Ereignis. Anschließend müssen Sie SetPresentationMode für die TimedMetadataTracks-Auflistung des Wiedergabeelements aufrufen, um das System anzuweisen, dass die App Metadatenhinweisereignisse für dieses Wiedergabeelement empfangen möchte.

private void RegisterMetadataHandlerForImageSubtitles(MediaPlaybackItem item, int index)
{
    var timedTrack = item.TimedMetadataTracks[index];
    timedTrack.CueEntered += metadata_ImageSubtitleCueEntered;
    timedTrack.CueExited += metadata_ImageSubtitleCueExited;
    item.TimedMetadataTracks.SetPresentationMode((uint)index, TimedMetadataTrackPresentationMode.ApplicationPresented);

}

Im Handler für das CueEntered-Ereignis können Sie die TimedMetadataKind-Eigenschaft des an den Handler übergebenen TimedMetadataTrack-Objekts überprüfen, um festzustellen, ob die Metadaten für Bilduntertitel vorgesehen sind. Dies ist erforderlich, wenn Sie denselben Datenmarkerereignishandler für mehrere Metadatentypen verwenden. Wenn der zugeordnete Metadatentitel vom Typ "TimedMetadataKind.ImageSubtitle" ist, wandeln Sie den datenmarker in der Cue-Eigenschaft des MediaCueEventArgs in ein ImageCue um. Die SoftwareBitmap-Eigenschaft des ImageCue enthält eine SoftwareBitmap-Darstellung des Untertitelbilds. Erstellen Sie eine SoftwareBitmapSource, und rufen Sie SetBitmapAsync auf, um das Bild einem XAML-Image-Steuerelementzuzuweisen. Die Eigenschaften "Extent" und "Position" des ImageCue enthalten Informationen zur Größe und Position des Untertitelbilds.

private async void metadata_ImageSubtitleCueEntered(TimedMetadataTrack timedMetadataTrack, MediaCueEventArgs args)
{
    // Check in case there are different tracks and the handler was used for more tracks 
    if (timedMetadataTrack.TimedMetadataKind == TimedMetadataKind.ImageSubtitle)
    {
        var cue = args.Cue as ImageCue;
        if (cue != null)
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
            {
                var source = new SoftwareBitmapSource();
                await source.SetBitmapAsync(cue.SoftwareBitmap);
                SubtitleImage.Source = source;
                SubtitleImage.Width = cue.Extent.Width;
                SubtitleImage.Height = cue.Extent.Height;
                SubtitleImage.SetValue(Canvas.LeftProperty, cue.Position.X);
                SubtitleImage.SetValue(Canvas.TopProperty, cue.Position.Y);
            });
        }
    }
}

Sprachhinweise

Ab Windows 10, Version 1703, können UWP-Apps registrieren, um Ereignisse als Reaktion auf Wortgrenzen, Satzgrenzen und SSML-Lesezeichen (Speech Synthesis Markup Language) in wiedergegebenen Medien zu empfangen. Auf diese Weise können Sie Audiodatenströme wiedergeben, die mit der SpeechSynthesizer-Klasse generiert wurden, und Ihre Ui basierend auf diesen Ereignissen aktualisieren, z. B. das Anzeigen des Texts des aktuell wiedergegebenen Worts oder Satzes.

Das in diesem Abschnitt gezeigte Beispiel verwendet eine Klassenmemembevariable, um eine Textzeichenfolge zu speichern, die synthetisiert und wiedergegeben wird.

string inputText = "In the lake heading for the mountain, the flea swims";

Erstellen Sie eine neue Instanz der SpeechSynthesizer-Klasse . Legen Sie die Optionen "IncludeWordBoundaryMetadata" und "IncludeSentenceBoundaryMetadata" für den Synthesizer auf "true" fest, um anzugeben, dass die Metadaten im generierten Mediendatenstrom enthalten sein sollen. Rufen Sie SynthesizeTextToStreamAsync auf, um einen Datenstrom zu generieren, der die synthetisierte Sprache und die entsprechenden Metadaten enthält. Erstellen Sie eine MediaSource und ein MediaPlaybackItem aus dem synthetisierten Stream.

var synthesizer = new Windows.Media.SpeechSynthesis.SpeechSynthesizer();

// Enable word marker generation (false by default). 
synthesizer.Options.IncludeWordBoundaryMetadata = true;
synthesizer.Options.IncludeSentenceBoundaryMetadata = true;

var stream = await synthesizer.SynthesizeTextToStreamAsync(inputText);
var mediaSource = MediaSource.CreateFromStream(stream, "");
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Registrieren Sie sich für die Sprachmetadatenereignisse mithilfe des MediaPlaybackItem-Objekts . In diesem Beispiel wird die Hilfsmethode RegisterMetadataHandlerForSpeech verwendet, um die Ereignisse zu registrieren. Ein Lambda-Ausdruck wird verwendet, um einen Handler für das TimedMetadataTracksChanged-Ereignis zu implementieren, das auftritt, wenn das System eine Änderung der Metadatentitel erkennt, die einem MediaPlaybackItem zugeordnet sind. In einigen Fällen sind die Metadatenspuren möglicherweise verfügbar, wenn das Wiedergabeelement anfänglich aufgelöst wird, sodass außerhalb des TimedMetadataTracksChanged-Handlers auch die verfügbaren Metadatentitel durchlaufen und RegisterMetadataHandlerForSpeech aufgerufen werden.

// Since the tracks are added later we will  
// monitor the tracks being added and subscribe to the ones of interest 
mediaPlaybackItem.TimedMetadataTracksChanged += (MediaPlaybackItem sender, IVectorChangedEventArgs args) =>
{
    if (args.CollectionChange == CollectionChange.ItemInserted)
    {
        RegisterMetadataHandlerForSpeech(sender, (int)args.Index);
    }
    else if (args.CollectionChange == CollectionChange.Reset)
    {
        for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
        {
            RegisterMetadataHandlerForSpeech(sender, index);
        }
    }
};

// If tracks were available at source resolution time, itterate through and register: 
for (int index = 0; index < mediaPlaybackItem.TimedMetadataTracks.Count; index++)
{
    RegisterMetadataHandlerForSpeech(mediaPlaybackItem, index);
}

Nach der Registrierung für die Sprachmetadatenereignisse wird das MediaItem einem MediaPlayer für die Wiedergabe in einem MediaPlayerElement zugewiesen.

_mediaPlayer = new MediaPlayer();
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
_mediaPlayer.Source = mediaPlaybackItem;
_mediaPlayer.Play();

Rufen Sie in der RegisterMetadataHandlerForSpeech-Hilfsmethode eine Instanz der TimedMetadataTrack-Klasse ab, indem Sie die TimedMetadataTracks-Auflistung des MediaPlaybackItem indizieren. Registrieren Sie sich für das CueEntered-Ereignis und das CueExited-Ereignis. Anschließend müssen Sie SetPresentationMode für die TimedMetadataTracks-Auflistung des Wiedergabeelements aufrufen, um das System anzuweisen, dass die App Metadatenhinweisereignisse für dieses Wiedergabeelement empfangen möchte.

private void RegisterMetadataHandlerForSpeech(MediaPlaybackItem item, int index)
{
    var timedTrack = item.TimedMetadataTracks[index];
    timedTrack.CueEntered += metadata_SpeechCueEntered;
    timedTrack.CueExited += metadata_SpeechCueExited;
    item.TimedMetadataTracks.SetPresentationMode((uint)index, TimedMetadataTrackPresentationMode.ApplicationPresented);

}

Im Handler für das CueEntered-Ereignis können Sie die TimedMetadataKind-Eigenschaft des an den Handler übergebenen TimedMetadataTrack-Objekts überprüfen, um festzustellen, ob es sich bei den Metadaten um eine Sprache handelt. Dies ist erforderlich, wenn Sie denselben Datenmarkerereignishandler für mehrere Metadatentypen verwenden. Wenn der zugeordnete Metadatentitel vom Typ "TimedMetadataKind.Speech" ist, wandeln Sie den in der Cue-Eigenschaft der MediaCueEventArgs enthaltenen Datenmarker in eine SpeechCue um. Bei Sprachhinweisen wird der Im Metadatentitel enthaltene Sprachhinweistyp durch Überprüfen der Label-Eigenschaft bestimmt. Der Wert dieser Eigenschaft ist "SpeechWord" für Wortgrenzen, "SpeechSentence" für Satzgrenzen oder "SpeechBookmark" für SSML-Lesezeichen. In diesem Beispiel wird auf den Wert "SpeechWord" überprüft, und wenn dieser Wert gefunden wird, werden die StartPositionInput- und EndPositionInput-Eigenschaften der SpeechCue verwendet, um die Position innerhalb des Eingabetexts des aktuell wiedergegebenen Worts zu bestimmen. In diesem Beispiel wird einfach jedes Wort in die Debugausgabe ausgegeben.

private void metadata_SpeechCueEntered(TimedMetadataTrack timedMetadataTrack, MediaCueEventArgs args)
{
    // Check in case there are different tracks and the handler was used for more tracks 
    if (timedMetadataTrack.TimedMetadataKind == TimedMetadataKind.Speech)
    {
        var cue = args.Cue as SpeechCue;
        if (cue != null)
        {
            if (timedMetadataTrack.Label == "SpeechWord")
            {
                // Do something with the cue 
                System.Diagnostics.Debug.WriteLine($"{cue.StartPositionInInput} - {cue.EndPositionInInput}: {inputText.Substring((int)cue.StartPositionInInput, ((int)cue.EndPositionInInput - (int)cue.StartPositionInInput) + 1)}");
            }
        }
    }
}

Kapitelhinweise

Ab Windows 10, Version 1703, können UWP-Apps für Hinweise registrieren, die Kapiteln innerhalb eines Medienelements entsprechen. Um dieses Feature zu verwenden, erstellen Sie ein MediaSource-Objekt für den Medieninhalt, und erstellen Sie dann ein MediaPlaybackItem aus der MediaSource.

var contentUri = new Uri("http://contoso.com/content.mp4");
var mediaSource = MediaSource.CreateFromUri(contentUri);
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Registrieren Sie sich für die Kapitelmetadatenereignisse mithilfe des mediaPlaybackItem-Objekts, das im vorherigen Schritt erstellt wurde. In diesem Beispiel wird die Hilfsmethode RegisterMetadataHandlerForChapterCues verwendet, um die Ereignisse zu registrieren. Ein Lambda-Ausdruck wird verwendet, um einen Handler für das TimedMetadataTracksChanged-Ereignis zu implementieren, das auftritt, wenn das System eine Änderung der Metadatentitel erkennt, die einem MediaPlaybackItem zugeordnet sind. In einigen Fällen sind die Metadatenspuren möglicherweise verfügbar, wenn das Wiedergabeelement anfänglich aufgelöst wird, sodass außerhalb des TimedMetadataTracksChanged-Handlers auch die verfügbaren Metadatentitel durchlaufen und RegisterMetadataHandlerForChapterCues aufgerufen werden.

mediaPlaybackItem.TimedMetadataTracksChanged += (MediaPlaybackItem sender, IVectorChangedEventArgs args) =>
{
    if (args.CollectionChange == CollectionChange.ItemInserted)
    {
        RegisterMetadataHandlerForChapterCues(sender, (int)args.Index);
    }
    else if (args.CollectionChange == CollectionChange.Reset)
    {
        for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
        {
            if (sender.TimedMetadataTracks[index].TimedMetadataKind == TimedMetadataKind.ImageSubtitle)
                RegisterMetadataHandlerForChapterCues(sender, index);
        }
    }
};

for (int index = 0; index < mediaPlaybackItem.TimedMetadataTracks.Count; index++)
{
    RegisterMetadataHandlerForChapterCues(mediaPlaybackItem, index);
}

Nach der Registrierung für die Kapitelmetadatenereignisse wird das MediaItem einem MediaPlayer für die Wiedergabe in einem MediaPlayerElement zugewiesen.

_mediaPlayer = new MediaPlayer();
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
_mediaPlayer.Source = mediaPlaybackItem;
_mediaPlayer.Play();

Rufen Sie in der RegisterMetadataHandlerForChapterCues-Hilfsmethode eine Instanz der TimedMetadataTrack-Klasse ab, indem Sie die TimedMetadataTracks-Auflistung des MediaPlaybackItem indizieren. Registrieren Sie sich für das CueEntered-Ereignis und das CueExited-Ereignis. Anschließend müssen Sie SetPresentationMode für die TimedMetadataTracks-Auflistung des Wiedergabeelements aufrufen, um das System anzuweisen, dass die App Metadatenhinweisereignisse für dieses Wiedergabeelement empfangen möchte.

private void RegisterMetadataHandlerForChapterCues(MediaPlaybackItem item, int index)
{
    var timedTrack = item.TimedMetadataTracks[index];
    timedTrack.CueEntered += metadata_ChapterCueEntered;
    timedTrack.CueExited += metadata_ChapterCueExited;
    item.TimedMetadataTracks.SetPresentationMode((uint)index, TimedMetadataTrackPresentationMode.ApplicationPresented);
}

Im Handler für das CueEntered-Ereignis können Sie die TimedMetadataKind-Eigenschaft des an den Handler übergebenen TimedMetadataTrack-Objekts überprüfen, um festzustellen, ob die Metadaten für Kapitelhinweise vorgesehen sind. Dies ist erforderlich, wenn Sie denselben Datenmarkerereignishandler für mehrere Metadatentypen verwenden. Wenn der zugeordnete Metadatentitel vom Typ "TimedMetadataKind.Chapter" ist, wandeln Sie den Datenmarker in die Cue-Eigenschaft des MediaCueEventArgs in ein ChapterCue um. Die Title-Eigenschaft des ChapterCue enthält den Titel des Kapitels, das gerade in der Wiedergabe erreicht wurde.

private async void metadata_ChapterCueEntered(TimedMetadataTrack timedMetadataTrack, MediaCueEventArgs args)
{
    // Check in case there are different tracks and the handler was used for more tracks 
    if (timedMetadataTrack.TimedMetadataKind == TimedMetadataKind.Chapter)
    {
        var cue = args.Cue as ChapterCue;
        if (cue != null)
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                ChapterTitleTextBlock.Text = cue.Title;
            });
        }
    }
}

Suchen Sie nach dem nächsten Kapitel mithilfe von Kapitelhinweisen

Zusätzlich zum Empfangen von Benachrichtigungen, wenn sich das aktuelle Kapitel in einem Wiedergabeelement ändert, können Sie auch Kapitelhinweise verwenden, um das nächste Kapitel innerhalb eines Wiedergabeelements zu suchen. Die unten gezeigte Beispielmethode verwendet als Argumente einen MediaPlayer und ein MediaPlaybackItem, das das aktuell wiedergegebene Medienelement darstellt. Die TimedMetadataTracks-Auflistung wird durchsucht, um festzustellen, ob einer der Titel "TimedMetadataKind" den TimedMetadataTrack-Wert von "TimedMetadataKind.Chapter" aufweist. Wenn ein Kapiteltitel gefunden wird, durchläuft die Methode jeden Hinweis in der Cues-Sammlung des Titels, um den ersten Hinweis zu finden, der eine Starttime größer als die aktuelle Position der Wiedergabesitzung des Media Players ist. Sobald der richtige Hinweis gefunden wurde, wird die Position der Wiedergabesitzung aktualisiert, und der Kapiteltitel wird in der Benutzeroberfläche aktualisiert.

private void GoToNextChapter(MediaPlayer player, MediaPlaybackItem item)
{
    // Find the chapters track if one exists
    TimedMetadataTrack chapterTrack = item.TimedMetadataTracks.FirstOrDefault(track => track.TimedMetadataKind == TimedMetadataKind.Chapter);
    if (chapterTrack == null)
    {
        return;
    }

    // Find the first chapter that starts after current playback position
    TimeSpan currentPosition = player.PlaybackSession.Position;
    foreach (ChapterCue cue in chapterTrack.Cues)
    {
        if (cue.StartTime > currentPosition)
        {
            // Change player position to chapter start time
            player.PlaybackSession.Position = cue.StartTime;

            // Display chapter name
            ChapterTitleTextBlock.Text = cue.Title;
            break;
        }
    }
}

Erweiterte M3U-Kommentare

Ab Windows 10, Version 1703, können UWP-Apps für Hinweise registrieren, die Kommentaren in einer erweiterten M3U-Manifestdatei entsprechen. In diesem Beispiel wird adaptiveMediaSource verwendet, um den Medieninhalt wiederzugeben. Weitere Informationen finden Sie unter Adaptives Streaming. Erstellen Sie eine AdaptiveMediaSource für den Inhalt, indem Sie CreateFromUriAsync oder CreateFromStreamAsync aufrufen. Erstellen Sie ein MediaSource-Objekt, indem Sie CreateFromAdaptiveMediaSource aufrufen und dann ein MediaPlaybackItem aus der MediaSource erstellen.

AdaptiveMediaSourceCreationResult result =
    await AdaptiveMediaSource.CreateFromUriAsync(new Uri("http://contoso.com/playlist.m3u"));

if (result.Status != AdaptiveMediaSourceCreationStatus.Success)
{
    // TODO: Handle adaptive media source creation errors.
    return;
}
var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(result.MediaSource);
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Registrieren Sie sich für die M3U-Metadatenereignisse mithilfe des MediaPlaybackItem-Objekts, das im vorherigen Schritt erstellt wurde. In diesem Beispiel wird die Hilfsmethode RegisterMetadataHandlerForEXTM3UCues verwendet, um die Ereignisse zu registrieren. Ein Lambda-Ausdruck wird verwendet, um einen Handler für das TimedMetadataTracksChanged-Ereignis zu implementieren, das auftritt, wenn das System eine Änderung der Metadatentitel erkennt, die einem MediaPlaybackItem zugeordnet sind. In einigen Fällen sind die Metadatenspuren möglicherweise verfügbar, wenn das Wiedergabeelement anfänglich aufgelöst wird, sodass außerhalb des TimedMetadataTracksChanged-Handlers auch die verfügbaren Metadatentitel durchlaufen und RegisterMetadataHandlerForEXTM3UCues aufgerufen werden.

mediaPlaybackItem.TimedMetadataTracksChanged += (MediaPlaybackItem sender, IVectorChangedEventArgs args) =>
{
    if (args.CollectionChange == CollectionChange.ItemInserted)
    {
        RegisterMetadataHandlerForEXTM3UCues(sender, (int)args.Index);
    }
    else if (args.CollectionChange == CollectionChange.Reset)
    {
        for (int index = 0; index < sender.TimedMetadataTracks.Count; index++)
        {
            if (sender.TimedMetadataTracks[index].TimedMetadataKind == TimedMetadataKind.ImageSubtitle)
                RegisterMetadataHandlerForEXTM3UCues(sender, index);
        }
    }
};

for (int index = 0; index < mediaPlaybackItem.TimedMetadataTracks.Count; index++)
{
    RegisterMetadataHandlerForEXTM3UCues(mediaPlaybackItem, index);
}

Nach der Registrierung für die M3U-Metadatenereignisse wird das MediaItem einem MediaPlayer für die Wiedergabe in einem MediaPlayerElement zugewiesen.

_mediaPlayer = new MediaPlayer();
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
_mediaPlayer.Source = mediaPlaybackItem;
_mediaPlayer.Play();

Rufen Sie in der RegisterMetadataHandlerForEXTM3UCues-Hilfsmethode eine Instanz der TimedMetadataTrack-Klasse ab, indem Sie die TimedMetadataTracks-Auflistung des MediaPlaybackItem indizieren. Überprüfen Sie die DispatchType-Eigenschaft der Metadatenspur, die den Wert "EXTM3U" aufweist, wenn der Titel M3U-Kommentare darstellt. Registrieren Sie sich für das CueEntered-Ereignis und das CueExited-Ereignis. Anschließend müssen Sie SetPresentationMode für die TimedMetadataTracks-Auflistung des Wiedergabeelements aufrufen, um das System anzuweisen, dass die App Metadatenhinweisereignisse für dieses Wiedergabeelement empfangen möchte.

private void RegisterMetadataHandlerForEXTM3UCues(MediaPlaybackItem item, int index)
{
    var timedTrack = item.TimedMetadataTracks[index];
    var dispatchType = timedTrack.DispatchType;

    if (String.Equals(dispatchType, "EXTM3U", StringComparison.OrdinalIgnoreCase))
    {
        timedTrack.Label = "EXTM3U comments";
        timedTrack.CueEntered += metadata_EXTM3UCueEntered;
        timedTrack.CueExited += metadata_EXTM3UCueExited;
        item.TimedMetadataTracks.SetPresentationMode((uint)index, TimedMetadataTrackPresentationMode.ApplicationPresented);
    }
}

Wandeln Sie im Handler für das CueEntered-Ereignis den Datenmarker in die Cue-Eigenschaft des MediaCueEventArgs in ein DataCue um. Stellen Sie sicher, dass dataCue und die Data-Eigenschaft des Markers nicht null sind. Erweiterte EMU-Kommentare werden in Form von UTF-16, little endian, null terminated strings bereitgestellt. Erstellen Sie einen neuen DataReader, um die Daten zu lesen, indem Sie DataReader.FromBuffer aufrufen. Legen Sie die UnicodeEncoding-Eigenschaft des Readers auf Utf16LE fest, um die Daten im richtigen Format zu lesen. Rufen Sie ReadString auf, um die Daten zu lesen, wobei die Hälfte der Länge des Datenfelds angegeben wird, da jedes Zeichen zwei Byte groß ist, und subtrahieren Sie eine, um das nachfolgende NULL-Zeichen zu entfernen. In diesem Beispiel wird der M3U-Kommentar einfach in die Debugausgabe geschrieben.

private void metadata_EXTM3UCueEntered(TimedMetadataTrack timedMetadataTrack, MediaCueEventArgs args)
{
    var dataCue = args.Cue as DataCue;
    if (dataCue != null && dataCue.Data != null)
    {
        // The payload is a UTF-16 Little Endian null-terminated string.
        // It is any comment line in a manifest that is not part of the HLS spec.
        var dr = Windows.Storage.Streams.DataReader.FromBuffer(dataCue.Data);
        dr.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf16LE;
        var m3uComment = dr.ReadString(dataCue.Data.Length / 2 - 1);
        System.Diagnostics.Debug.WriteLine(m3uComment);
    }
}

ID3-Tags

Ab Windows 10, Version 1703, können UWP-Apps für Hinweise registrieren, die ID3-Tags innerhalb von HTTP Live Streaming (HLS)-Inhalten entsprechen. In diesem Beispiel wird adaptiveMediaSource verwendet, um den Medieninhalt wiederzugeben. Weitere Informationen finden Sie unter Adaptives Streaming. Erstellen Sie eine AdaptiveMediaSource für den Inhalt, indem Sie CreateFromUriAsync oder CreateFromStreamAsync aufrufen. Erstellen Sie ein MediaSource-Objekt, indem Sie CreateFromAdaptiveMediaSource aufrufen und dann ein MediaPlaybackItem aus der MediaSource erstellen.

AdaptiveMediaSourceCreationResult result =
    await AdaptiveMediaSource.CreateFromUriAsync(new Uri("http://contoso.com/playlist.m3u"));

if (result.Status != AdaptiveMediaSourceCreationStatus.Success)
{
    // TODO: Handle adaptive media source creation errors.
    return;
}
var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(result.MediaSource);
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Registrieren Sie sich für die ID3-Tag-Ereignisse mithilfe des MediaPlaybackItem-Objekts, das im vorherigen Schritt erstellt wurde. In diesem Beispiel wird die Hilfsmethode RegisterMetadataHandlerForID3Cues verwendet, um die Ereignisse zu registrieren. Ein Lambda-Ausdruck wird verwendet, um einen Handler für das TimedMetadataTracksChanged-Ereignis zu implementieren, das auftritt, wenn das System eine Änderung der Metadatentitel erkennt, die einem MediaPlaybackItem zugeordnet sind. In einigen Fällen sind die Metadatenspuren möglicherweise verfügbar, wenn das Wiedergabeelement anfänglich aufgelöst wird, sodass außerhalb des TimedMetadataTracksChanged-Handlers auch die verfügbaren Metadatentitel durchlaufen und RegisterMetadataHandlerForID3Cues aufgerufen werden.

AdaptiveMediaSourceCreationResult result =
    await AdaptiveMediaSource.CreateFromUriAsync(new Uri("http://contoso.com/playlist.m3u"));

if (result.Status != AdaptiveMediaSourceCreationStatus.Success)
{
    // TODO: Handle adaptive media source creation errors.
    return;
}
var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(result.MediaSource);
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Nach der Registrierung für die ID3-Metadatenereignisse wird das MediaItem einem MediaPlayer für die Wiedergabe in einem MediaPlayerElement zugewiesen.

_mediaPlayer = new MediaPlayer();
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
_mediaPlayer.Source = mediaPlaybackItem;
_mediaPlayer.Play();

Rufen Sie in der RegisterMetadataHandlerForID3Cues-Hilfsmethode eine Instanz der TimedMetadataTrack-Klasse ab, indem Sie die TimedMetadataTracks-Auflistung des MediaPlaybackItem indizieren. Überprüfen Sie die DispatchType-Eigenschaft der Metadatenspur, die einen Wert mit der GUID-Zeichenfolge "15260DFFFF49443320FF4943320000F" enthält, wenn der Titel ID3-Tags darstellt. Registrieren Sie sich für das CueEntered-Ereignis und das CueExited-Ereignis. Anschließend müssen Sie SetPresentationMode für die TimedMetadataTracks-Auflistung des Wiedergabeelements aufrufen, um das System anzuweisen, dass die App Metadatenhinweisereignisse für dieses Wiedergabeelement empfangen möchte.

private void RegisterMetadataHandlerForID3Cues(MediaPlaybackItem item, int index)
{
    var timedTrack = item.TimedMetadataTracks[index];
    var dispatchType = timedTrack.DispatchType;

    if (String.Equals(dispatchType, "15260DFFFF49443320FF49443320000F", StringComparison.OrdinalIgnoreCase))
    {
        timedTrack.Label = "ID3 tags";
        timedTrack.CueEntered += metadata_ID3CueEntered;
        timedTrack.CueExited += metadata_ID3CueExited;
        item.TimedMetadataTracks.SetPresentationMode((uint)index, TimedMetadataTrackPresentationMode.ApplicationPresented);
    }
}

Wandeln Sie im Handler für das CueEntered-Ereignis den Datenmarker in die Cue-Eigenschaft des MediaCueEventArgs in ein DataCue um. Stellen Sie sicher, dass dataCue und die Data-Eigenschaft des Markers nicht null sind. Erweiterte EMU-Kommentare werden in form rohen Bytes im Transportdatenstrom bereitgestellt (siehe ID3). Erstellen Sie einen neuen DataReader, um die Daten zu lesen, indem Sie DataReader.FromBuffer aufrufen. In diesem Beispiel werden die Headerwerte aus dem ID3-Tag aus den Hinweisdaten gelesen und in die Debugausgabe geschrieben.

private void metadata_ID3CueEntered(TimedMetadataTrack timedMetadataTrack, MediaCueEventArgs args)
{
    var dataCue = args.Cue as DataCue;
    if (dataCue != null && dataCue.Data != null)
    {
        // The payload is the raw ID3 bytes found in a TS stream
        // Ref: http://id3.org/id3v2.4.0-structure
        var dr = Windows.Storage.Streams.DataReader.FromBuffer(dataCue.Data);
        var header_ID3 = dr.ReadString(3);
        var header_version_major = dr.ReadByte();
        var header_version_minor = dr.ReadByte();
        var header_flags = dr.ReadByte();
        var header_tagSize = dr.ReadUInt32();

        System.Diagnostics.Debug.WriteLine($"ID3 tag data: major {header_version_major}, minor: {header_version_minor}");
    }
}

Fragmentierte mp4 emsg Boxen

Ab Windows 10, Version 1703, können UWP-Apps für Hinweise registrieren, die emsg-Feldern in fragmentierten MP4-Streams entsprechen. Ein Beispiel für die Verwendung dieser Art von Metadaten ist die Verwendung von Inhaltsanbietern, um Clientanwendungen zu signalisieren, dass eine Anzeige während des Livestreaming-Inhalts wiedergegeben wird. In diesem Beispiel wird adaptiveMediaSource verwendet, um den Medieninhalt wiederzugeben. Weitere Informationen finden Sie unter Adaptives Streaming. Erstellen Sie eine AdaptiveMediaSource für den Inhalt, indem Sie CreateFromUriAsync oder CreateFromStreamAsync aufrufen. Erstellen Sie ein MediaSource-Objekt, indem Sie CreateFromAdaptiveMediaSource aufrufen und dann ein MediaPlaybackItem aus der MediaSource erstellen.

AdaptiveMediaSourceCreationResult result =
    await AdaptiveMediaSource.CreateFromUriAsync(new Uri("http://contoso.com/playlist.m3u"));

if (result.Status != AdaptiveMediaSourceCreationStatus.Success)
{
    // TODO: Handle adaptive media source creation errors.
    return;
}
var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(result.MediaSource);
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Registrieren Sie sich für die emsg box-Ereignisse mithilfe des MediaPlaybackItem-Objekts, das im vorherigen Schritt erstellt wurde. In diesem Beispiel wird die Hilfsmethode RegisterMetadataHandlerForEmsgCues verwendet, um die Ereignisse zu registrieren. Ein Lambda-Ausdruck wird verwendet, um einen Handler für das TimedMetadataTracksChanged-Ereignis zu implementieren, das auftritt, wenn das System eine Änderung der Metadatentitel erkennt, die einem MediaPlaybackItem zugeordnet sind. In einigen Fällen sind die Metadatenspuren möglicherweise verfügbar, wenn das Wiedergabeelement anfänglich aufgelöst wird. Außerhalb des TimedMetadataTracksChanged-Handlers werden auch die verfügbaren Metadatentitel durchlaufen und RegisterMetadataHandlerForEmsgCues aufgerufen.

AdaptiveMediaSourceCreationResult result =
    await AdaptiveMediaSource.CreateFromUriAsync(new Uri("http://contoso.com/playlist.m3u"));

if (result.Status != AdaptiveMediaSourceCreationStatus.Success)
{
    // TODO: Handle adaptive media source creation errors.
    return;
}
var mediaSource = MediaSource.CreateFromAdaptiveMediaSource(result.MediaSource);
var mediaPlaybackItem = new MediaPlaybackItem(mediaSource);

Nach der Registrierung für die Emsg-Box-Metadatenereignisse wird das MediaItem-Objekt einem MediaPlayer für die Wiedergabe in einem MediaPlayerElement zugewiesen.

_mediaPlayer = new MediaPlayer();
mediaPlayerElement.SetMediaPlayer(_mediaPlayer);
_mediaPlayer.Source = mediaPlaybackItem;
_mediaPlayer.Play();

Rufen Sie in der RegisterMetadataHandlerForEmsgCues-Hilfsmethode eine Instanz der TimedMetadataTrack-Klasse ab, indem Sie die TimedMetadataTracks-Auflistung des MediaPlaybackItem indizieren. Überprüfen Sie die DispatchType-Eigenschaft der Metadatenspur, die den Wert "emsg:mp4" aufweist, wenn der Titel emsg-Felder darstellt. Registrieren Sie sich für das CueEntered-Ereignis und das CueExited-Ereignis. Anschließend müssen Sie SetPresentationMode für die TimedMetadataTracks-Auflistung des Wiedergabeelements aufrufen, um das System anzuweisen, dass die App Metadatenhinweisereignisse für dieses Wiedergabeelement empfangen möchte.

private void RegisterMetadataHandlerForEmsgCues(MediaPlaybackItem item, int index)
{
    var timedTrack = item.TimedMetadataTracks[index];
    var dispatchType = timedTrack.DispatchType;

    if (String.Equals(dispatchType, "emsg:mp4", StringComparison.OrdinalIgnoreCase))
    {
        timedTrack.Label = "mp4 Emsg boxes";
        timedTrack.CueEntered += metadata_EmsgCueEntered;
        timedTrack.CueExited += metadata_EmsgCueExited;
        item.TimedMetadataTracks.SetPresentationMode((uint)index, TimedMetadataTrackPresentationMode.ApplicationPresented);
    }
}

Wandeln Sie im Handler für das CueEntered-Ereignis den Datenmarker in die Cue-Eigenschaft des MediaCueEventArgs in ein DataCue um. Stellen Sie sicher, dass das DataCue-Objekt nicht null ist. Die Eigenschaften des Emsg-Felds werden von der Medienpipeline als benutzerdefinierte Eigenschaften in der Properties-Auflistung des DataCue-Objekts bereitgestellt. In diesem Beispiel wird versucht, mehrere verschiedene Eigenschaftswerte mithilfe der TryGetValue-Methode zu extrahieren. Wenn diese Methode NULL zurückgibt, bedeutet dies, dass die angeforderte Eigenschaft nicht im Emsg-Feld vorhanden ist, daher wird stattdessen ein Standardwert festgelegt.

Im nächsten Teil des Beispiels wird das Szenario veranschaulicht, in dem die Anzeigenwiedergabe ausgelöst wird. Dies ist der Fall, wenn die im vorherigen Schritt abgerufene scheme_id_uri-Eigenschaft den Wert "urn:scte:scte:scte35:2013:xml" aufweist. Weitere Informationen findest du unter https://dashif.org/identifiers/event_schemes/. Beachten Sie, dass der Standard empfiehlt, dieses Emsg mehrmals zur Redundanz zu senden, sodass in diesem Beispiel eine Liste der emsg-IDs verwaltet wird, die bereits verarbeitet wurden und nur neue Nachrichten verarbeitet werden. Erstellen Sie einen neuen DataReader, um die Cue-Daten zu lesen, indem Sie DataReader.FromBuffer aufrufen und die Codierung auf UTF-8 festlegen, indem Sie die UnicodeEncoding-Eigenschaft festlegen und dann die Daten lesen. In diesem Beispiel wird die Nachrichtennutzlast in die Debugausgabe geschrieben. Eine echte App würde die Nutzlastdaten verwenden, um die Wiedergabe einer Anzeige zu planen.

private void metadata_EmsgCueEntered(TimedMetadataTrack timedMetadataTrack, MediaCueEventArgs args)
{
    var dataCue = args.Cue as DataCue;
    if (dataCue != null)
    {
        string scheme_id_uri = string.Empty;
        string value = string.Empty;
        UInt32 timescale = (UInt32)TimeSpan.TicksPerSecond;
        UInt32 presentation_time_delta = (UInt32)dataCue.StartTime.Ticks;
        UInt32 event_duration = (UInt32)dataCue.Duration.Ticks;
        UInt32 id = 0;
        Byte[] message_data = null;

        const string scheme_id_uri_key = "emsg:scheme_id_uri";
        object propValue = null;
        dataCue.Properties.TryGetValue(scheme_id_uri_key, out propValue);
        scheme_id_uri = propValue != null ? (string)propValue : "";

        const string value_key = "emsg:value";
        propValue = null;
        dataCue.Properties.TryGetValue(value_key, out propValue);
        value = propValue != null ? (string)propValue : "";

        const string timescale_key = "emsg:timescale";
        propValue = null;
        dataCue.Properties.TryGetValue(timescale_key, out propValue);
        timescale = propValue != null ? (UInt32)propValue : timescale;

        const string presentation_time_delta_key = "emsg:presentation_time_delta";
        propValue = null;
        dataCue.Properties.TryGetValue(presentation_time_delta_key, out propValue);
        presentation_time_delta = propValue != null ? (UInt32)propValue : presentation_time_delta;

        const string event_duration_key = "emsg:event_duration";
        propValue = null;
        dataCue.Properties.TryGetValue(event_duration_key, out propValue);
        event_duration = propValue != null ? (UInt32)propValue : event_duration;

        const string id_key = "emsg:id";
        propValue = null;
        dataCue.Properties.TryGetValue(id_key, out propValue);
        id = propValue != null ? (UInt32)propValue : 0;

        System.Diagnostics.Debug.WriteLine($"Label: {timedMetadataTrack.Label}, Id: {dataCue.Id}, StartTime: {dataCue.StartTime}, Duration: {dataCue.Duration}");
        System.Diagnostics.Debug.WriteLine($"scheme_id_uri: {scheme_id_uri}, value: {value}, timescale: {timescale}, presentation_time_delta: {presentation_time_delta}, event_duration: {event_duration}, id: {id}");

        if (dataCue.Data != null)
        {
            var dr = Windows.Storage.Streams.DataReader.FromBuffer(dataCue.Data);

            // Check if this is a SCTE ad message:
            // Ref:  http://dashif.org/identifiers/event-schemes/
            if (scheme_id_uri.ToLower() == "urn:scte:scte35:2013:xml")
            {
                // SCTE recommends publishing emsg more than once, so we avoid reprocessing the same message id:
                if (!processedAdIds.Contains(id))
                {
                    processedAdIds.Add(id);
                    dr.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
                    var scte35payload = dr.ReadString(dataCue.Data.Length);
                    System.Diagnostics.Debug.WriteLine($", message_data: {scte35payload}");
                    // TODO: ScheduleAdFromScte35Payload(timedMetadataTrack, presentation_time_delta, timescale, event_duration, scte35payload);
                }
                else
                {
                    System.Diagnostics.Debug.WriteLine($"This emsg.Id, {id}, has already been processed.");
                }
            }
            else
            {
                message_data = new byte[dataCue.Data.Length];
                dr.ReadBytes(message_data);
                // TODO: Use the 'emsg' bytes for something useful. 
                System.Diagnostics.Debug.WriteLine($", message_data.Length: {message_data.Length}");
            }
        }
    }
}