Bagikan melalui


Istim metadata berwaktu yang didukung sistem

Artikel ini menjelaskan cara memanfaatkan beberapa format metadata berwaktu yang mungkin disematkan dalam file atau aliran media. Aplikasi UWP dapat mendaftar untuk peristiwa yang dimunculkan oleh alur media selama pemutaran setiap kali isyarat metadata ini ditemui. Dengan menggunakan kelas DataCue, aplikasi dapat menerapkan isyarat metadata kustom mereka sendiri, tetapi artikel ini berfokus pada beberapa standar metadata yang secara otomatis terdeteksi oleh alur media, termasuk:

  • Subtitel berbasis gambar dalam format VobSub
  • Istik ucapan, termasuk batas kata, batas kalimat, dan marka buku Speech Synthesis Markup Language (SSML)
  • Isti bab
  • Komentar M3U yang diperluas
  • Tag ID3
  • Kotak emsg mp4 terfragmentasi

Artikel ini dibangun berdasarkan konsep yang dibahas dalam artikel Item media, daftar putar, dan trek, yang mencakup dasar-dasar bekerja dengan kelas MediaSource, MediaPlaybackItem, dan TimedMetadataTrack dan panduan umum untuk menggunakan metadata berwaktu di aplikasi Anda.

Langkah-langkah implementasi dasar sama untuk semua jenis metadata berwaktu yang dijelaskan dalam artikel ini:

  1. Buat MediaSource lalu MediaPlaybackItem agar konten dapat diputar.
  2. Daftar untuk peristiwa MediaPlaybackItem.TimedMetadataTracksChanged , yang terjadi saat sub-trek item media diselesaikan oleh alur media.
  3. Daftar untuk peristiwa TimedMetadataTrack.CueEntered dan TimedMetadataTrack.CueExited untuk trek metadata berwaktu yang ingin Anda gunakan.
  4. Di penanganan aktivitas CueEntered, perbarui UI Anda berdasarkan metadata yang diteruskan dalam arg peristiwa. Anda dapat memperbarui UI lagi, untuk menghapus teks subtitel saat ini misalnya, dalam peristiwa CueExited .

Dalam artikel ini, menangani setiap jenis metadata ditampilkan sebagai skenario yang berbeda, tetapi dimungkinkan untuk menangani (atau mengabaikan) berbagai jenis metadata menggunakan sebagian besar kode bersama. Anda dapat memeriksa properti TimedMetadataKind dari objek TimedMetadataTrack di beberapa titik dalam proses. Jadi, misalnya, Anda dapat memilih untuk mendaftar untuk peristiwa CueEntered untuk trek metadata yang memiliki nilai TimedMetadataKind.ImageSubtitle, tetapi tidak untuk trek yang memiliki nilai TimedMetadataKind.Speech. Atau sebagai gantinya, Anda dapat mendaftarkan handler untuk semua jenis trek metadata dan kemudian memeriksa nilai TimedMetadataKind di dalam handler CueEntered untuk menentukan tindakan apa yang harus diambil sebagai respons terhadap isjin.

Subtitel berbasis gambar

Dimulai dengan Windows 10, versi 1703, aplikasi UWP dapat mendukung subtitel berbasis gambar eksternal dalam format VobSub. Untuk menggunakan fitur ini, pertama-tama buat objek MediaSource untuk konten media tempat subtitel gambar akan ditampilkan. Selanjutnya, buat objek TimedTextSource dengan memanggil CreateFromUriWithIndex atau CreateFromStreamWithIndex, meneruskan Uri file .sub yang berisi data gambar subtitel dan file .idx yang berisi informasi waktu untuk subtitel. Tambahkan TimedTextSource ke MediaSource dengan menambahkannya ke koleksi ExternalTimedTextSources sumber. Buat MediaPlaybackItem dari 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);

Daftar untuk peristiwa metadata subtitel gambar menggunakan objek MediaPlaybackItem yang dibuat pada langkah sebelumnya. Contoh ini menggunakan metode pembantu, RegisterMetadataHandlerForImageSubtitles, untuk mendaftar peristiwa. Ekspresi lambda digunakan untuk mengimplementasikan handler untuk peristiwa TimedMetadataTracksChanged, yang terjadi ketika sistem mendeteksi perubahan dalam trek metadata yang terkait dengan MediaPlaybackItem. Dalam beberapa kasus, trek metadata mungkin tersedia ketika item pemutaran awalnya diselesaikan, jadi di luar handler TimedMetadataTracksChanged , kami juga mengulangi trek metadata yang tersedia dan memanggil RegisterMetadataHandlerForImageSubtitles.

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);
}

Setelah mendaftar untuk peristiwa metadata subtitel gambar, MediaItem ditetapkan ke MediaPlayer untuk pemutaran dalam MediaPlayerElement.

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

Dalam metode pembantu RegisterMetadataHandlerForImageSubtitles, dapatkan instans kelas TimedMetadataTrack dengan mengindeks ke dalam koleksi TimedMetadataTracks dari MediaPlaybackItem. Daftar untuk peristiwa CueEntered dan peristiwa CueExited. Kemudian, Anda harus memanggil SetPresentationMode pada koleksi TimedMetadataTracks item pemutaran, untuk menginstruksikan sistem bahwa aplikasi ingin menerima peristiwa isjin metadata untuk item pemutaran ini.

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);

}

Di handler untuk peristiwa CueEntered, Anda dapat memeriksa properti TimedMetadataKind dari objek TimedMetadataTrack yang diteruskan ke handler untuk melihat apakah metadata adalah untuk subtitel gambar. Ini diperlukan jika Anda menggunakan penanganan aktivitas isian data yang sama untuk beberapa jenis metadata. Jika trek metadata terkait berjenis TimedMetadataKind.ImageSubtitle, berikan isyarat data yang terkandung dalam properti Isyarat mediaCueEventArgs ke ImageCue. Properti SoftwareBitmap dari ImageCue berisi representasi SoftwareBitmap dari gambar subtitel. Buat SoftwareBitmapSource dan panggil SetBitmapAsync untuk menetapkan gambar ke kontrol Gambar XAML. Properti Extent dan Position imageCue memberikan informasi tentang ukuran dan posisi gambar subtitel.

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);
            });
        }
    }
}

Isti bicara

Dimulai dengan Windows 10, versi 1703, aplikasi UWP dapat mendaftar untuk menerima peristiwa sebagai respons terhadap batas kata, batas kalimat, dan marka buku Speech Synthesis Markup Language (SSML) di media yang diputar. Ini memungkinkan Anda untuk memutar aliran audio yang dihasilkan dengan kelas SpeechSynthesizer dan memperbarui UI Anda berdasarkan peristiwa ini, seperti menampilkan teks kata atau kalimat yang sedang diputar.

Contoh yang ditampilkan di bagian ini menggunakan variabel anggota kelas untuk menyimpan string teks yang akan disintesis dan diputar kembali.

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

Buat instans baru kelas SpeechSynthesizer . Atur opsi IncludeWordBoundaryMetadata dan IncludeSentenceBoundaryMetadata untuk synthesizer ke true untuk menentukan bahwa metadata harus disertakan dalam aliran media yang dihasilkan. Panggil SynthesizeTextToStreamAsync untuk menghasilkan aliran yang berisi ucapan yang disintesis dan metadata yang sesuai. Buat MediaSource dan MediaPlaybackItem dari aliran yang disintesis.

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);

Daftar untuk peristiwa metadata ucapan menggunakan objek MediaPlaybackItem . Contoh ini menggunakan metode pembantu, RegisterMetadataHandlerForSpeech, untuk mendaftar peristiwa. Ekspresi lambda digunakan untuk mengimplementasikan handler untuk peristiwa TimedMetadataTracksChanged, yang terjadi ketika sistem mendeteksi perubahan dalam trek metadata yang terkait dengan MediaPlaybackItem. Dalam beberapa kasus, trek metadata mungkin tersedia ketika item pemutaran awalnya diselesaikan, jadi di luar handler TimedMetadataTracksChanged , kami juga mengulangi trek metadata yang tersedia dan memanggil RegisterMetadataHandlerForSpeech.

// 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);
}

Setelah mendaftar untuk peristiwa metadata ucapan, MediaItem ditetapkan ke MediaPlayer untuk pemutaran dalam MediaPlayerElement.

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

Dalam metode pembantu RegisterMetadataHandlerForSpeech, dapatkan instans kelas TimedMetadataTrack dengan mengindeks ke koleksi TimedMetadataTracks dari MediaPlaybackItem. Daftar untuk peristiwa CueEntered dan peristiwa CueExited. Kemudian, Anda harus memanggil SetPresentationMode pada koleksi TimedMetadataTracks item pemutaran, untuk menginstruksikan sistem bahwa aplikasi ingin menerima peristiwa isjin metadata untuk item pemutaran ini.

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);

}

Di handler untuk peristiwa CueEntered, Anda dapat memeriksa propery TimedMetadataKind objek TimedMetadataTrack yang diteruskan ke handler untuk melihat apakah metadata adalah ucapan. Ini diperlukan jika Anda menggunakan penanganan aktivitas isian data yang sama untuk beberapa jenis metadata. Jika trek metadata terkait berjenis TimedMetadataKind.Speech, berikan isjin data yang terkandung dalam properti Isian Dari MediaCueEventArgs ke SpeechCue. Untuk isti ucapan, jenis isti bicara yang disertakan dalam trek metadata ditentukan dengan memeriksa properti Label . Nilai properti ini adalah "SpeechWord" untuk batas kata, "SpeechSentence" untuk batas kalimat, atau "SpeechBookmark" untuk marka buku SSML. Dalam contoh ini, kita memeriksa nilai "SpeechWord", dan jika nilai ini ditemukan, properti StartPositionInput dan EndPositionInput dari SpeechCue digunakan untuk menentukan lokasi dalam teks input kata yang saat ini diputar kembali. Contoh ini hanya menghasilkan setiap kata ke output debug.

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)}");
            }
        }
    }
}

Isti bab

Dimulai dengan Windows 10, versi 1703, aplikasi UWP dapat mendaftar untuk istiadat yang sesuai dengan bab dalam item media. Untuk menggunakan fitur ini, buat objek MediaSource untuk konten media lalu buat MediaPlaybackItem dari MediaSource.

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

Daftar untuk peristiwa metadata bab menggunakan objek MediaPlaybackItem yang dibuat di langkah sebelumnya. Contoh ini menggunakan metode pembantu, RegisterMetadataHandlerForChapterCues, untuk mendaftar peristiwa. Ekspresi lambda digunakan untuk mengimplementasikan handler untuk peristiwa TimedMetadataTracksChanged, yang terjadi ketika sistem mendeteksi perubahan dalam trek metadata yang terkait dengan MediaPlaybackItem. Dalam beberapa kasus, trek metadata mungkin tersedia ketika item pemutaran awalnya diselesaikan, jadi di luar handler TimedMetadataTracksChanged , kami juga mengulangi trek metadata yang tersedia dan memanggil RegisterMetadataHandlerForChapterCues.

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);
}

Setelah mendaftar untuk peristiwa metadata bab, MediaItem ditetapkan ke MediaPlayer untuk pemutaran dalam MediaPlayerElement.

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

Dalam metode pembantu RegisterMetadataHandlerForChapterCues, dapatkan instans kelas TimedMetadataTrack dengan mengindeks ke koleksi TimedMetadataTracks dari MediaPlaybackItem. Daftar untuk peristiwa CueEntered dan peristiwa CueExited. Kemudian, Anda harus memanggil SetPresentationMode pada koleksi TimedMetadataTracks item pemutaran, untuk menginstruksikan sistem bahwa aplikasi ingin menerima peristiwa isjin metadata untuk item pemutaran ini.

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);
}

Dalam handler untuk peristiwa CueEntered, Anda dapat memeriksa propery TimedMetadataKind objek TimedMetadataTrack yang diteruskan ke handler untuk melihat apakah metadata adalah untuk isjin bab. Ini diperlukan jika Anda menggunakan penanganan aktivitas isian data yang sama untuk beberapa jenis metadata. Jika trek metadata terkait berjenis TimedMetadataKind.Chapter, berikan isjin data yang terkandung dalam properti Isian Dari MediaCueEventArgs ke ChapterCue. Properti Judul BabCue berisi judul bab yang baru saja dicapai dalam pemutaran.

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;
            });
        }
    }
}

Cari ke bab berikutnya menggunakan isyarat bab

Selain menerima pemberitahuan ketika bab saat ini berubah dalam item pemutaran, Anda juga dapat menggunakan isyarat bab untuk mencari bab berikutnya dalam item pemutaran. Contoh metode yang ditunjukkan di bawah ini mengambil sebagai argumen MediaPlayer dan MediaPlaybackItem yang mewakili item media yang sedang diputar. Koleksi TimedMetadataTracks dicari untuk melihat apakah salah satu trek memiliki timedMetadataKind propery dari nilai TimedMetadataTrack dari TimedMetadataKind.Chapter. Jika trek bab ditemukan, metode akan mengulangi setiap iseng dalam koleksi Isti trek untuk menemukan iseng pertama yang memiliki StartTime lebih besar dari Posisi sesi pemutaran pemutaran media saat ini. Setelah isyarat yang benar ditemukan, posisi sesi pemutaran diperbarui dan judul bab diperbarui di UI.

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;
        }
    }
}

Komentar M3U yang diperluas

Dimulai dengan Windows 10, versi 1703, aplikasi UWP dapat mendaftar untuk iseng yang sesuai dengan komentar dalam file manifes M3U yang Diperluas. Contoh ini menggunakan AdaptiveMediaSource untuk memutar konten media. Untuk informasi selengkapnya, lihat Streaming Adaptif. Buat AdaptiveMediaSource untuk konten dengan memanggil CreateFromUriAsync atau CreateFromStreamAsync. Buat objek MediaSource dengan memanggil CreateFromAdaptiveMediaSource lalu buat MediaPlaybackItem dari MediaSource.

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);

Daftar untuk peristiwa metadata M3U menggunakan objek MediaPlaybackItem yang dibuat pada langkah sebelumnya. Contoh ini menggunakan metode pembantu, RegisterMetadataHandlerForEXTM3UCues, untuk mendaftar peristiwa. Ekspresi lambda digunakan untuk mengimplementasikan handler untuk peristiwa TimedMetadataTracksChanged, yang terjadi ketika sistem mendeteksi perubahan dalam trek metadata yang terkait dengan MediaPlaybackItem. Dalam beberapa kasus, trek metadata mungkin tersedia ketika item pemutaran awalnya diselesaikan, jadi di luar handler TimedMetadataTracksChanged , kami juga mengulangi trek metadata yang tersedia dan memanggil RegisterMetadataHandlerForEXTM3UCues.

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);
}

Setelah mendaftar untuk peristiwa metadata M3U, MediaItem ditetapkan ke MediaPlayer untuk pemutaran dalam MediaPlayerElement.

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

Dalam metode pembantu RegisterMetadataHandlerForEXTM3UCues, dapatkan instans kelas TimedMetadataTrack dengan mengindeks ke dalam koleksi TimedMetadataTracks dari MediaPlaybackItem. Periksa properti DispatchType dari trek metadata, yang akan memiliki nilai "EXTM3U" jika trek mewakili komentar M3U. Daftar untuk peristiwa CueEntered dan peristiwa CueExited. Kemudian, Anda harus memanggil SetPresentationMode pada koleksi TimedMetadataTracks item pemutaran, untuk menginstruksikan sistem bahwa aplikasi ingin menerima peristiwa isjin metadata untuk item pemutaran ini.

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);
    }
}

Dalam handler untuk peristiwa CueEntered, transmisikan isjin data yang terkandung dalam properti Cue mediaCueEventArgs ke DataCue. Periksa untuk memastikan DataCue dan properti Data istidak tidak null. Komentar EMU yang diperpanjang disediakan dalam bentuk string UTF-16, little endian, null terminated. Buat DataReader baru untuk membaca data iseng dengan memanggil DataReader.FromBuffer. Atur properti UnicodeEncoding pembaca ke Utf16LE untuk membaca data dalam format yang benar. Panggil ReadString untuk membaca data, menentukan setengah dari panjang bidang Data , karena setiap karakter berukuran dua byte, dan kurangi satu untuk menghapus karakter null berikutnya. Dalam contoh ini, komentar M3U hanya ditulis ke output debug.

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);
    }
}

Tag ID3

Dimulai dengan Windows 10, versi 1703, aplikasi UWP dapat mendaftar untuk iseng yang sesuai dengan tag ID3 dalam konten Http Live Streaming (HLS). Contoh ini menggunakan AdaptiveMediaSource untuk memutar konten media. Untuk informasi selengkapnya, lihat Streaming Adaptif. Buat AdaptiveMediaSource untuk konten dengan memanggil CreateFromUriAsync atau CreateFromStreamAsync. Buat objek MediaSource dengan memanggil CreateFromAdaptiveMediaSource lalu buat MediaPlaybackItem dari MediaSource.

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);

Daftar untuk peristiwa tag ID3 menggunakan objek MediaPlaybackItem yang dibuat di langkah sebelumnya. Contoh ini menggunakan metode pembantu, RegisterMetadataHandlerForID3Cues, untuk mendaftar peristiwa. Ekspresi lambda digunakan untuk mengimplementasikan handler untuk peristiwa TimedMetadataTracksChanged, yang terjadi ketika sistem mendeteksi perubahan dalam trek metadata yang terkait dengan MediaPlaybackItem. Dalam beberapa kasus, trek metadata mungkin tersedia ketika item pemutaran awalnya diselesaikan, jadi di luar handler TimedMetadataTracksChanged , kami juga mengulangi trek metadata yang tersedia dan memanggil RegisterMetadataHandlerForID3Cues.

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);

Setelah mendaftar untuk peristiwa metadata ID3, MediaItem ditetapkan ke MediaPlayer untuk pemutaran dalam MediaPlayerElement.

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

Dalam metode pembantu RegisterMetadataHandlerForID3Cues, dapatkan instans kelas TimedMetadataTrack dengan mengindeks ke koleksi TimedMetadataTracks dari MediaPlaybackItem. Periksa properti DispatchType dari trek metadata, yang akan memiliki nilai yang berisi string GUID "15260DFFFF49443320FF4944332000F" jika trek mewakili tag ID3. Daftar untuk peristiwa CueEntered dan peristiwa CueExited. Kemudian, Anda harus memanggil SetPresentationMode pada koleksi TimedMetadataTracks item pemutaran, untuk menginstruksikan sistem bahwa aplikasi ingin menerima peristiwa isjin metadata untuk item pemutaran ini.

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);
    }
}

Dalam handler untuk peristiwa CueEntered, transmisikan isjin data yang terkandung dalam properti Cue mediaCueEventArgs ke DataCue. Periksa untuk memastikan DataCue dan properti Data istidak tidak null. Komentar EMU yang diperluas disediakan dalam byte mentah formulir di aliran transportasi (lihat ID3). Buat DataReader baru untuk membaca data iseng dengan memanggil DataReader.FromBuffer. Dalam contoh ini, nilai header dari tag ID3 dibaca dari data isjinasi dan ditulis ke output debug.

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}");
    }
}

Kotak emsg mp4 terfragmentasi

Dimulai dengan Windows 10, versi 1703, aplikasi UWP dapat mendaftar untuk iseng yang sesuai dengan kotak emsg dalam aliran mp4 terfragmentasi. Contoh penggunaan jenis metadata ini adalah bagi penyedia konten untuk memberi sinyal aplikasi klien untuk memutar iklan selama konten streaming langsung. Contoh ini menggunakan AdaptiveMediaSource untuk memutar konten media. Untuk informasi selengkapnya, lihat Streaming Adaptif. Buat AdaptiveMediaSource untuk konten dengan memanggil CreateFromUriAsync atau CreateFromStreamAsync. Buat objek MediaSource dengan memanggil CreateFromAdaptiveMediaSource lalu buat MediaPlaybackItem dari MediaSource.

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);

Daftar untuk peristiwa kotak emsg menggunakan objek MediaPlaybackItem yang dibuat di langkah sebelumnya. Contoh ini menggunakan metode pembantu, RegisterMetadataHandlerForEmsgCues, untuk mendaftar peristiwa. Ekspresi lambda digunakan untuk mengimplementasikan handler untuk peristiwa TimedMetadataTracksChanged, yang terjadi ketika sistem mendeteksi perubahan dalam trek metadata yang terkait dengan MediaPlaybackItem. Dalam beberapa kasus, trek metadata mungkin tersedia ketika item pemutaran awalnya diselesaikan, jadi di luar handler TimedMetadataTracksChanged , kami juga mengulangi trek metadata yang tersedia dan memanggil RegisterMetadataHandlerForEmsgCues.

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);

Setelah mendaftar untuk peristiwa metadata kotak emsg, MediaItem ditetapkan ke MediaPlayer untuk pemutaran dalam MediaPlayerElement.

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

Dalam metode pembantu RegisterMetadataHandlerForEmsgCues, dapatkan instans kelas TimedMetadataTrack dengan mengindeks ke dalam koleksi TimedMetadataTracks dari MediaPlaybackItem. Centang properti DispatchType dari trek metadata, yang akan memiliki nilai "emsg:mp4" jika trek mewakili kotak emsg. Daftar untuk peristiwa CueEntered dan peristiwa CueExited. Kemudian, Anda harus memanggil SetPresentationMode pada koleksi TimedMetadataTracks item pemutaran, untuk menginstruksikan sistem bahwa aplikasi ingin menerima peristiwa isjin metadata untuk item pemutaran ini.

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);
    }
}

Dalam handler untuk peristiwa CueEntered, transmisikan isjin data yang terkandung dalam properti Cue mediaCueEventArgs ke DataCue. Periksa untuk memastikan objek DataCue tidak null. Properti kotak emsg disediakan oleh alur media sebagai properti kustom dalam kumpulan Properti objek DataCue. Contoh ini mencoba mengekstrak beberapa nilai properti yang berbeda menggunakan metode TryGetValue. Jika metode ini mengembalikan null, itu berarti propery yang diminta tidak ada dalam kotak emsg, sehingga nilai default diatur sebagai gantinya.

Bagian berikutnya dari contoh mengilustrasikan skenario di mana pemutaran iklan dipicu, yang merupakan kasus ketika properti scheme_id_uri , yang diperoleh pada langkah sebelumnya, memiliki nilai "urn:scte:scte35:2013:xml". Untuk informasi selengkapnya, lihat https://dashif.org/identifiers/event_schemes/ . Perhatikan bahwa standar merekomendasikan pengiriman emsg ini beberapa kali untuk redundansi, sehingga contoh ini mempertahankan daftar ID emsg yang telah diproses dan hanya memproses pesan baru. Buat DataReader baru untuk membaca data iseng dengan memanggil DataReader.FromBuffer dan atur pengodean ke UTF-8 dengan mengatur properti UnicodeEncoding, lalu baca data. Dalam contoh ini, payload pesan ditulis ke output debug. Aplikasi nyata akan menggunakan data payload untuk menjadwalkan pemutaran iklan.

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}");
            }
        }
    }
}