3

MediaElement を介してビデオを表示する WinRT アプリにスクリーンショット機能を実装しようとしています。次のコードがあります。MediaElement のサイズのスクリーンショットを保存しますが、画像は空 (完全に黒) です。さまざまな種類のメディア ファイルで試しました。Surface RT で Win Key + Vol Down を実行すると、スクリーン ショットにメディア フレームのコンテンツが含まれますが、次のコードを使用すると、全体が黒くなります :(

private async Task SaveCurrentFrame()
{
 RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
 await renderTargetBitmap.RenderAsync(Player);
 var pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
 MultimediaItem currentItem = (MultimediaItem)this.DefaultViewModel["Group"];
 StorageFolder currentFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
 var saveFile = await currentFolder.CreateFileAsync(currentItem.UniqueId + ".png", CreationCollisionOption.ReplaceExisting);
 if (saveFile == null)
    return;
 // Encode the image to the selected file on disk
 using (var fileStream = await saveFile.OpenAsync(FileAccessMode.ReadWrite))
 {
    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, fileStream);

    encoder.SetPixelData(
        BitmapPixelFormat.Bgra8,
        BitmapAlphaMode.Ignore,
        (uint)renderTargetBitmap.PixelWidth,
        (uint)renderTargetBitmap.PixelHeight,
        DisplayInformation.GetForCurrentView().LogicalDpi,
        DisplayInformation.GetForCurrentView().LogicalDpi,
        pixelBuffer.ToArray());

    await encoder.FlushAsync();
 }
}

ここで、MultimediaItem は、特に文字列である UniqueId プロパティを持つビュー モデル クラスです。

「Player」はメディア要素の名前です。

コードに何か問題がありますか、それともこのアプローチが間違っているので、C++ で塹壕に入る必要がありますか?

PS 私は WinRT API のみに興味があります。

Update 1 RenderTargetBitmap はこれをサポートしていないようですDirectX C++ を使用してそれを行う方法についての指針をいただければ幸いです。これは私にとって大きなタスクなので、何らかの方法で解決し、解決策を報告します。

4

2 に答える 2

2

OK、ボタンを押してMediaElementからスナップショットを作成して機能させることができました。

SetMediaStreamSource メソッドを使用して MediaStreamSource オブジェクトを MediaElement に渡しています。MediaStreamSource には、基本的に新しいフレームが描画されるたびに発生するイベント SampleRequested があります。次に、ブール値を使用して、ビットマップをいつ作成するかを制御します

private async void MediaStream_SampleRequested(MediaStreamSource sender, MediaStreamSourceSampleRequestedEventArgs args)
{
    if (!takeSnapshot)
    {
        return;
    }

    takeSnapshot = false;
    Task.Run(() => DecodeAndSaveVideoFrame(args.Request.Sample));
}

あとは、圧縮された画像をデコードして WriteableBitmap に変換するだけです。画像はYUV形式です(または少なくとも私の場合はそうでした)。を使用してバイト配列を取得できます

byte[] yvuArray = sample.Buffer.ToArray();

次に、この配列からデータを取得し、RGB に変換します。残念ながら、コード全体を投稿することはできませんが、さらにいくつかのヒントを提供します。

YUV to RGB wikiここには、YUV から RGB への変換がどのように機能するかを説明する wiki があります。

ここで、私が適応した(そして完全に機能する)ソリューションのpythonプロジェクトを見つけました。より正確にするには、メソッド NV12Converter がどのように機能するかを分析する必要があります。

最後に、ボタンを押すか他のアクティビティを実行した後、takeSnapshot ブール値を true に変更します:)。

于 2016-03-15T11:04:37.287 に答える
2

はい、可能です - 少しトリッキーですが、うまくいきます。

mediaElementではなく、StorageFileそれ自体を使用します。Windows.Media.Editing名前空間を使用writableBitmapして作成する必要があります。

UWP で動作 (Windows 10)

これは、ファイルの選択とビデオ解像度の取得、画像ライブラリへの画像の保存の完全な例です

        TimeSpan timeOfFrame = new TimeSpan(0, 0, 1);//one sec

        //pick mp4 file
        var picker = new Windows.Storage.Pickers.FileOpenPicker();
        picker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
        picker.FileTypeFilter.Add(".mp4");
        StorageFile pickedFile = await picker.PickSingleFileAsync();
        if (pickedFile == null)
        {
            return;
        }
        ///


        //Get video resolution
        List<string> encodingPropertiesToRetrieve = new List<string>();
        encodingPropertiesToRetrieve.Add("System.Video.FrameHeight");
        encodingPropertiesToRetrieve.Add("System.Video.FrameWidth");
        IDictionary<string, object> encodingProperties = await pickedFile.Properties.RetrievePropertiesAsync(encodingPropertiesToRetrieve);
        uint frameHeight = (uint)encodingProperties["System.Video.FrameHeight"];
        uint frameWidth = (uint)encodingProperties["System.Video.FrameWidth"];
        ///


        //Use Windows.Media.Editing to get ImageStream
        var clip = await MediaClip.CreateFromFileAsync(pickedFile);
        var composition = new MediaComposition();
        composition.Clips.Add(clip);

        var imageStream = await composition.GetThumbnailAsync(timeOfFrame, (int)frameWidth, (int)frameHeight, VideoFramePrecision.NearestFrame);
        ///


        //generate bitmap 
        var writableBitmap = new WriteableBitmap((int)frameWidth, (int)frameHeight);
        writableBitmap.SetSource(imageStream);


        //generate some random name for file in PicturesLibrary
        var saveAsTarget = await KnownFolders.PicturesLibrary.CreateFileAsync("IMG" + Guid.NewGuid().ToString().Substring(0, 4) + ".jpg");


        //get stream from bitmap
        Stream stream = writableBitmap.PixelBuffer.AsStream();
        byte[] pixels = new byte[(uint)stream.Length];
        await stream.ReadAsync(pixels, 0, pixels.Length);

        using (var writeStream = await saveAsTarget.OpenAsync(FileAccessMode.ReadWrite))
        {
            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, writeStream);
            encoder.SetPixelData(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Premultiplied,
                (uint)writableBitmap.PixelWidth,
                (uint)writableBitmap.PixelHeight,
                96,
                96,
                pixels);
            await encoder.FlushAsync();

            using (var outputStream = writeStream.GetOutputStreamAt(0))
            {
                await outputStream.FlushAsync();
            }
        }

ええ...私はこれに多くの時間を費やしました

于 2016-02-12T16:14:29.577 に答える