Komposisi dan pengeditan media

Artikel ini memperlihatkan kepada Anda cara menggunakan API di namespace Windows.Media.Editing untuk mengembangkan aplikasi dengan cepat yang memungkinkan pengguna membuat komposisi media dari file sumber audio dan video. Fitur kerangka kerja termasuk kemampuan untuk secara terprogram menambahkan beberapa klip video bersama-sama, menambahkan overlay video dan gambar, menambahkan audio latar belakang, dan menerapkan efek audio dan video. Setelah dibuat, komposisi media dapat dirender ke dalam file media datar untuk pemutaran atau berbagi, tetapi komposisi juga dapat diserialisasikan ke dan dideserialisasi dari disk, memungkinkan pengguna memuat dan memodifikasi komposisi yang telah mereka buat sebelumnya. Semua fungsi ini disediakan dalam antarmuka Windows Runtime yang mudah digunakan yang secara dramatis mengurangi jumlah dan kompleksitas kode yang diperlukan untuk melakukan tugas-tugas ini jika dibandingkan dengan API Microsoft Media Foundation tingkat rendah.

Membuat komposisi media baru

Kelas MediaComposition adalah kontainer untuk semua klip media yang membentuk komposisi dan bertanggung jawab untuk merender komposisi akhir, memuat dan menyimpan komposisi ke disk, dan menyediakan aliran pratinjau komposisi sehingga pengguna dapat melihatnya di UI. Untuk menggunakan MediaComposition di aplikasi Anda, sertakan namespace Layanan Windows.Media.Editing serta namespace Windows.Media.Core yang menyediakan API terkait yang akan Anda butuhkan.

using Windows.Media.Editing;
using Windows.Media.Core;
using Windows.Media.Playback;
using System.Threading.Tasks;

Objek MediaComposition akan diakses dari beberapa titik dalam kode Anda, jadi biasanya Anda akan mendeklarasikan variabel anggota untuk menyimpannya.

private MediaComposition composition;

Konstruktor untuk MediaComposition tidak mengambil argumen.

composition = new MediaComposition();

Menambahkan klip media ke komposisi

Komposisi media biasanya berisi satu atau beberapa klip video. Anda dapat menggunakan FileOpenPicker untuk memungkinkan pengguna memilih file video. Setelah file dipilih, buat objek MediaClip baru untuk memuat klip video dengan memanggil MediaClip.CreateFromFileAsync. Kemudian Anda menambahkan klip ke daftar Klip objek MediaComposition.

private async Task PickFileAndAddClip()
{
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeFilter.Add(".mp4");
    Windows.Storage.StorageFile pickedFile = await picker.PickSingleFileAsync();
    if (pickedFile == null)
    {
        ShowErrorMessage("File picking cancelled");
        return;
    }

    // These files could be picked from a location that we won't have access to later
    var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
    storageItemAccessList.Add(pickedFile);

    var clip = await MediaClip.CreateFromFileAsync(pickedFile);
    composition.Clips.Add(clip);

}
  • Klip media muncul di MediaComposition dalam urutan yang sama seperti yang muncul di daftar Klip.

  • MediaClip hanya dapat disertakan dalam komposisi sekali. Mencoba menambahkan MediaClip yang sudah digunakan oleh komposisi akan mengakibatkan kesalahan. Untuk menggunakan kembali klip video beberapa kali dalam komposisi, panggil Kloning untuk membuat objek MediaClip baru yang kemudian dapat ditambahkan ke komposisi.

  • Aplikasi Universal Windows tidak memiliki izin untuk mengakses seluruh sistem file. Properti FutureAccessList dari kelas StorageApplicationPermissions memungkinkan aplikasi Anda menyimpan rekaman file yang telah dipilih oleh pengguna sehingga Anda dapat mempertahankan izin untuk mengakses file. FutureAccessList memiliki maksimal 1000 entri, sehingga aplikasi Anda perlu mengelola daftar untuk memastikannya tidak menjadi penuh. Ini sangat penting jika Anda berencana untuk mendukung pemuatan dan memodifikasi komposisi yang dibuat sebelumnya.

  • MediaComposition mendukung klip video dalam format MP4.

  • Jika file video berisi beberapa trek audio yang disematkan, Anda dapat memilih trek audio mana yang digunakan dalam komposisi dengan mengatur properti SelectedEmbeddedAudioTrackIndex.

  • Buat MediaClip dengan warna tunggal yang mengisi seluruh bingkai dengan memanggil CreateFromColor dan menentukan warna dan durasi untuk klip.

  • Buat MediaClip dari file gambar dengan memanggil CreateFromImageFileAsync dan menentukan file gambar dan durasi untuk klip.

  • Buat MediaClip dari IDirect3DSurface dengan memanggil CreateFromSurface dan menentukan permukaan dan durasi dari klip.

Pratinjau komposisi dalam MediaElement

Untuk memungkinkan pengguna melihat komposisi media, tambahkan MediaPlayerElement ke file XAML yang menentukan UI Anda.

<MediaPlayerElement x:Name="mediaPlayerElement" AutoPlay="False" Margin="5" HorizontalAlignment="Stretch" AreTransportControlsEnabled="True" />

Mendeklarasikan variabel anggota jenis MediaStreamSource.

private MediaStreamSource mediaStreamSource;

Panggil metode GeneratePreviewMediaStreamSource objek MediaComposition untuk membuat MediaStreamSource untuk komposisi. Buat objek MediaSource dengan memanggil metode pabrik CreateFromMediaStreamSource dan tetapkan ke properti Sumber mediaPlayerElement. Sekarang komposisi dapat dilihat di UI.

public void UpdateMediaElementSource()
{

    mediaStreamSource = composition.GeneratePreviewMediaStreamSource(
        (int)mediaPlayerElement.ActualWidth,
        (int)mediaPlayerElement.ActualHeight);

    mediaPlayerElement.Source = MediaSource.CreateFromMediaStreamSource(mediaStreamSource);

}
  • MediaComposition harus berisi setidaknya satu klip media sebelum memanggil GeneratePreviewMediaStreamSource, atau objek yang dikembalikan akan null.

  • Garis waktu MediaElement tidak diperbarui secara otomatis untuk mencerminkan perubahan dalam komposisi. Disarankan agar Anda memanggil GeneratePreviewMediaStreamSource dan mengatur properti Sumber MediaPlayerElementsetiap kali Anda membuat serangkaian perubahan pada komposisi dan ingin memperbarui UI.

Disarankan agar Anda mengatur objek MediaStreamSource dan properti Sumber MediaPlayerElement ke null saat pengguna menavigasi jauh dari halaman untuk merilis sumber daya terkait.

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    mediaPlayerElement.Source = null;
    mediaStreamSource = null;
    base.OnNavigatedFrom(e);

}

Merender komposisi ke file video

Untuk merender komposisi media ke file video datar sehingga dapat dibagikan dan dilihat di perangkat lain, Anda harus menggunakan API dari namespace Windows.Media.Transcoding. Untuk memperbarui UI pada kemajuan operasi asinkron, Anda juga memerlukan API dari namespace Windows.UI.Core.

using Windows.Media.Transcoding;
using Windows.UI.Core;

Setelah mengizinkan pengguna untuk memilih file output dengan FileSavePicker, render komposisi ke file yang dipilih dengan memanggil RenderToFileAsync objek MediaComposition. Sisa kode dalam contoh berikut hanya mengikuti pola penanganan AsyncOperationWithProgress.

private async Task RenderCompositionToFile()
{
    var picker = new Windows.Storage.Pickers.FileSavePicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeChoices.Add("MP4 files", new List<string>() { ".mp4" });
    picker.SuggestedFileName = "RenderedComposition.mp4";

    Windows.Storage.StorageFile file = await picker.PickSaveFileAsync();
    if (file != null)
    {
        // Call RenderToFileAsync
        var saveOperation = composition.RenderToFileAsync(file, MediaTrimmingPreference.Precise);

        saveOperation.Progress = new AsyncOperationProgressHandler<TranscodeFailureReason, double>(async (info, progress) =>
        {
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
            {
                ShowErrorMessage(string.Format("Saving file... Progress: {0:F0}%", progress));
            }));
        });
        saveOperation.Completed = new AsyncOperationWithProgressCompletedHandler<TranscodeFailureReason, double>(async (info, status) =>
        {
            await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(() =>
            {
                try
                {
                    var results = info.GetResults();
                    if (results != TranscodeFailureReason.None || status != AsyncStatus.Completed)
                    {
                        ShowErrorMessage("Saving was unsuccessful");
                    }
                    else
                    {
                        ShowErrorMessage("Trimmed clip saved to file");
                    }
                }
                finally
                {
                        // Update UI whether the operation succeeded or not
                    }

            }));
        });
    }
    else
    {
        ShowErrorMessage("User cancelled the file selection");
    }
}
  • MediaTrimmingPreference memungkinkan Anda untuk memprioritaskan kecepatan operasi transcoding versus presisi pemangkasan klip media yang berdekatan. Transkode cepat menjadi lebih cepat dengan pemangkasan presisi yang lebih rendah, Presisi menyebabkan transkode menjadi lebih lambat tetapi dengan pemangkasan yang lebih tepat.

Memangkas klip video

Pangkas durasi klip video dalam komposisi dengan mengatur properti Objek MediaClip TrimTimeFromStart, properti TrimTimeFromEnd, atau keduanya.

private void TrimClipBeforeCurrentPosition()
{
    var currentClip = composition.Clips.FirstOrDefault(
        mc => mc.StartTimeInComposition <= mediaPlayerElement.MediaPlayer.PlaybackSession.Position &&
        mc.EndTimeInComposition >= mediaPlayerElement.MediaPlayer.PlaybackSession.Position);

    TimeSpan positionFromStart = mediaPlayerElement.MediaPlayer.PlaybackSession.Position - currentClip.StartTimeInComposition;
    currentClip.TrimTimeFromStart = positionFromStart;

}
  • Anda dapat menggunakan UI apa pun yang ingin Anda biarkan pengguna menentukan nilai pemangkasan awal dan akhir. Contoh di atas menggunakan properti Posisi MediaPlaybackSession yang terkait dengan MediaPlayerElement untuk terlebih dahulu menentukan MediaClip mana yang diputar kembali pada posisi saat ini dalam komposisi dengan memeriksa StartTimeInComposition dan EndTimeInComposition. Kemudian properti Posisi dan StartTimeInComposition digunakan lagi untuk menghitung jumlah waktu untuk memangkas dari awal klip. Metode FirstOrDefault adalah metode ekstensi dari namespace System.Linq yang menyederhanakan kode untuk memilih item dari daftar.
  • Properti OriginalDuration dari objek MediaClip memungkinkan Anda mengetahui durasi klip media tanpa kliping apa pun yang diterapkan.
  • Properti TrimmedDuration memungkinkan Anda mengetahui durasi klip media setelah pemangkasan diterapkan.
  • Menentukan nilai pemangkasan yang lebih besar dari durasi asli klip tidak menimbulkan kesalahan. Namun, jika komposisi hanya berisi satu klip dan yang dipangkas menjadi panjang nol dengan menentukan nilai pemangkasan besar, panggilan berikutnya ke GeneratePreviewMediaStreamSource akan mengembalikan null, seolah-olah komposisi tidak memiliki klip.

Menambahkan trek audio latar belakang ke komposisi

Untuk menambahkan trek latar belakang ke komposisi, muat file audio lalu buat objek BackgroundAudioTrack dengan memanggil metode pabrik BackgroundAudioTrack.CreateFromFileAsync. Kemudian, tambahkan BackgroundAudioTrack ke properti BackgroundAudioTracks komposisi.

private async Task AddBackgroundAudioTrack()
{
    // Add background audio
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.MusicLibrary;
    picker.FileTypeFilter.Add(".mp3");
    picker.FileTypeFilter.Add(".wav");
    picker.FileTypeFilter.Add(".flac");
    Windows.Storage.StorageFile audioFile = await picker.PickSingleFileAsync();
    if (audioFile == null)
    {
        ShowErrorMessage("File picking cancelled");
        return;
    }

    // These files could be picked from a location that we won't have access to later
    var storageItemAccessList = Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList;
    storageItemAccessList.Add(audioFile);

    var backgroundTrack = await BackgroundAudioTrack.CreateFromFileAsync(audioFile);

    composition.BackgroundAudioTracks.Add(backgroundTrack);

}
  • MediaComposition mendukung trek audio latar belakang dalam format berikut: MP3, WAV, FLAC

  • Trek audio latar belakang

  • Seperti halnya file video, Anda harus menggunakan kelas StorageApplicationPermissions untuk mempertahankan akses ke file dalam komposisi.

  • Seperti halnya MediaClip, BackgroundAudioTrack hanya dapat disertakan dalam komposisi sekali. Mencoba menambahkan BackgroundAudioTrack yang sudah digunakan oleh komposisi akan mengakibatkan kesalahan. Untuk menggunakan kembali trek audio beberapa kali dalam komposisi, panggil Kloning untuk membuat objek MediaClip baru yang kemudian dapat ditambahkan ke komposisi.

  • Secara default, trek audio latar belakang mulai diputar di awal komposisi. Jika ada beberapa trek latar belakang, semua trek akan mulai diputar di awal komposisi. Untuk menyebabkan trek audio latar belakang dimulai pemutaran di lain waktu, atur properti Tunda ke offset waktu yang diinginkan.

Menambahkan overlay ke komposisi

Overlay memungkinkan Anda untuk menumpuk beberapa lapisan video di atas satu sama lain dalam komposisi. Komposisi dapat berisi beberapa lapisan overlay, yang masing-masing dapat mencakup beberapa overlay. Buat objek MediaOverlay dengan meneruskan MediaClip ke konstruktornya. Atur posisi dan opasitas overlay, lalu buat MediaOverlayLayer baru dan tambahkan MediaOverlay ke daftar Overlay-nya. Terakhir, tambahkan MediaOverlayLayer ke daftar OverlayLayers komposisi.

private void AddOverlay(MediaClip overlayMediaClip, double scale, double left, double top, double opacity)
{
    Windows.Media.MediaProperties.VideoEncodingProperties encodingProperties =
        overlayMediaClip.GetVideoEncodingProperties();

    Rect overlayPosition;

    overlayPosition.Width = (double)encodingProperties.Width * scale;
    overlayPosition.Height = (double)encodingProperties.Height * scale;
    overlayPosition.X = left;
    overlayPosition.Y = top;

    MediaOverlay mediaOverlay = new MediaOverlay(overlayMediaClip);
    mediaOverlay.Position = overlayPosition;
    mediaOverlay.Opacity = opacity;

    MediaOverlayLayer mediaOverlayLayer = new MediaOverlayLayer();
    mediaOverlayLayer.Overlays.Add(mediaOverlay);

    composition.OverlayLayers.Add(mediaOverlayLayer);
}
  • Overlay dalam lapisan diurutkan z berdasarkan urutannya dalam daftar Overlay lapisan yang berisi. Indeks yang lebih tinggi dalam daftar dirender di atas indeks yang lebih rendah. Hal yang sama berlaku untuk lapisan overlay dalam komposisi. Lapisan dengan indeks yang lebih tinggi dalam daftar OverlayLayers komposisi akan dirender di atas indeks yang lebih rendah.

  • Karena overlay ditumpuk di atas satu sama lain alih-alih dimainkan secara berurutan, semua overlay memulai pemutaran di awal komposisi secara default. Untuk menyebabkan overlay dimulai pemutaran di lain waktu, atur properti Tunda ke offset waktu yang diinginkan.

Menambahkan efek ke klip media

Setiap MediaClip dalam komposisi memiliki daftar efek audio dan video tempat beberapa efek dapat ditambahkan. Efeknya harus menerapkan IAudioEffectDefinition dan IVideoEffectDefinition masing-masing. Contoh berikut menggunakan posisi MediaPlayerElement saat ini untuk memilih MediaClip yang saat ini dilihat lalu membuat instans baru VideoStabilizationEffectDefinition dan menambahkannya ke daftar VideoEffectDefinitions klip media.

private void AddVideoEffect()
{
    var currentClip = composition.Clips.FirstOrDefault(
        mc => mc.StartTimeInComposition <= mediaPlayerElement.MediaPlayer.PlaybackSession.Position &&
        mc.EndTimeInComposition >= mediaPlayerElement.MediaPlayer.PlaybackSession.Position);

    VideoStabilizationEffectDefinition videoEffect = new VideoStabilizationEffectDefinition();
    currentClip.VideoEffectDefinitions.Add(videoEffect);
}

Menyimpan komposisi ke file

Komposisi media dapat diserialisasikan ke file yang akan dimodifikasi di lain waktu. Pilih file output lalu panggil metode MediaComposition SaveAsync untuk menyimpan komposisi.

private async Task SaveComposition()
{
    var picker = new Windows.Storage.Pickers.FileSavePicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeChoices.Add("Composition files", new List<string>() { ".cmp" });
    picker.SuggestedFileName = "SavedComposition";

    Windows.Storage.StorageFile compositionFile = await picker.PickSaveFileAsync();
    if (compositionFile == null)
    {
        ShowErrorMessage("User cancelled the file selection");
    }
    else
    {
        var action = composition.SaveAsync(compositionFile);
        action.Completed = (info, status) =>
        {
            if (status != AsyncStatus.Completed)
            {
                ShowErrorMessage("Error saving composition");
            }

        };
    }
}

Memuat komposisi dari file

Komposisi media dapat dideserialisasi dari file untuk memungkinkan pengguna melihat dan memodifikasi komposisi. Pilih file komposisi lalu panggil metode MediaComposition LoadAsync untuk memuat komposisi.

private async Task OpenComposition()
{
    var picker = new Windows.Storage.Pickers.FileOpenPicker();
    picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
    picker.FileTypeFilter.Add(".cmp");

    Windows.Storage.StorageFile compositionFile = await picker.PickSingleFileAsync();
    if (compositionFile == null)
    {
        ShowErrorMessage("File picking cancelled");
    }
    else
    {
        composition = null;
        composition = await MediaComposition.LoadAsync(compositionFile);

        if (composition != null)
        {
            UpdateMediaElementSource();

        }
        else
        {
            ShowErrorMessage("Unable to open composition");
        }
    }
}