简体   繁体   English

如何从UWP中的IBuffer或字节数组创建IDirect3DSurface

[英]How to create a IDirect3DSurface from an IBuffer or byte array in UWP

I want to create a video from a few RenderTargetBitmap s in UWP. 我想在UWP中从几个RenderTargetBitmap创建一个视频。 I am doing that by using MediaClips . 我是通过使用MediaClips做到这MediaClips From RenderTargetBitmap i can get an IBuffer or byte array of pixels. RenderTargetBitmap我可以获得IBuffer或像素字节数组。 To create a MediaClip I need either an image file or an IDirect3DSurface . 要创建MediaClip我需要一个图像文件或一个IDirect3DSurface Creating an image just to create a clip is very expensive, so I thought of using IDirect3DSurface . 创建图像只是为了创建一个剪辑是非常昂贵的,所以我想到使用IDirect3DSurface How can I do this? 我怎样才能做到这一点? I have tried this: 我试过这个:

        RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
        await renderTargetBitmap.RenderAsync(RenderedGrid, 100, 100);

        IBuffer pixels = await renderTargetBitmap.GetPixelsAsync();
        var values = Enum.GetValues(typeof(DirectXPixelFormat));
        CanvasBitmap bitmap=null;

        foreach (DirectXPixelFormat format in values)
        {
            try
            {
                videoClip = new MediaComposition();
                bitmap = CanvasBitmap.CreateFromBytes(myWidget.Device, pixels, renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight, format);
                StorageFile video2 = await storageFolder.CreateFileAsync("video2" + ".mp4", CreationCollisionOption.ReplaceExisting);
                MediaClip d = MediaClip.CreateFromSurface(bitmap, DateTime.Now - previousFrame+new TimeSpan(100));
                videoClip.Clips.Add(d);
                await videoClip.RenderToFileAsync(video2);
                break;
            }
            catch(Exception e)
            {

            }
        }

I try all the formats in DirectXPixelFormat but none works. 我尝试使用DirectXPixelFormat所有格式,但都DirectXPixelFormat

I have a CanvasControl named myWidget that is empty. 我有一个CanvasControl名为myWidget是空的。

I create a CanvasBitmap from Ibuffer ( CanvasBitmap implements IDirect3DSurface ) 我从Ibuffer创建一个CanvasBitmapCanvasBitmap实现IDirect3DSurface

Create a Mediaclip from CanvasBitmap 创建MediaclipCanvasBitmap

Add it to MediaComposition . Add it to MediaComposition

Then I try to render to video file.When i try to save to a file it throws an error 然后我尝试渲染到视频文件。当我尝试保存到文件时,它会抛出一个错误

System.Runtime.InteropServices.COMException Stream is not in a state to handle the request. System.Runtime.InteropServices.COMException Stream未处于处理请求的状态。

EDIT: I figured out where the problem is, but not why and not how to fix it. 编辑:我想出问题所在,但不是为什么而不是如何解决它。

            await videoClip.SaveAsync(video2);
            videoClip= await MediaComposition.LoadAsync(video2);
            var x=await videoClip.RenderToFileAsync(video2);

Now with these three lines i can save the video, but using only the third line it throws the error above. 现在使用这三行我可以保存视频,但只使用第三行它会抛出上面的错误。 I cannot make sense of it. 我无法理解它。 Why does saving and loading fix the problem?? 为什么保存和加载修复问题?

The MediaComposition.RenderToFileAsync Method saves the composition to a video file that can be played back with standard media players. MediaComposition.RenderToFileAsync方法将合成保存到可以使用标准媒体播放器播放的视频文件中。 From the error info, it seems the stream content is not correct media data and can not be render into a video file directly. 从错误信息来看,似乎流内容不正确的媒体数据,不能直接渲染成视频文件。

So, to create a video from a few RenderTargetBitmaps in UWP, the way to use an image file should be your choice. 因此,要从UWP中的几个RenderTargetBitmaps创建视频,应该选择使用图像文件的方式。 using MediaClip.CreateFromImageFileAsync method by saving the RenderTargetBitmap into a file then using it to create a video. 使用MediaClip.CreateFromImageFileAsync方法将RenderTargetBitmap保存到文件中,然后使用它创建视频。

private async void CreateVideoByConvertRenderBitmapToFile()
{
    var folder = await ApplicationData.Current.LocalFolder.CreateFolderAsync("Test", 
        CreationCollisionOption.ReplaceExisting);
    var composition = new MediaComposition();
    for (int i = 0; i < 5; i++)
    {
        RenderTargetBitmap render = new RenderTargetBitmap();
        await render.RenderAsync(RenderGrid);
        MyImage.Source = render;
        var pixel = await render.GetPixelsAsync();
        var file = await folder.CreateFileAsync("test.png", CreationCollisionOption.GenerateUniqueName);
        using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite))
        {
            var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
            var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
            encoder.SetPixelData(
                BitmapPixelFormat.Bgra8,
                BitmapAlphaMode.Ignore,
                (uint)render.PixelWidth,
                (uint)render.PixelHeight,
                logicalDpi,
                logicalDpi,
                pixel.ToArray());
            await encoder.FlushAsync();
            stream.Dispose();
            MediaClip clip = await MediaClip.CreateFromImageFileAsync(file, TimeSpan.FromSeconds(3));
            composition.Clips.Add(clip);
            MyText.Text = "First frame >>>" + i;
        }
    }
    var video = await ApplicationData.Current.LocalFolder.CreateFileAsync("test.mp4",
        CreationCollisionOption.ReplaceExisting);
    var action = await composition.RenderToFileAsync(video, MediaTrimmingPreference.Precise);
    await folder.DeleteAsync();
}

Most probable reason is that, CanvasBitmap has an underlying IDirce3DSurface object as well as image data like byte[] or something else , though I am not sure about this. 最可能的原因是, CanvasBitmap有一个底层的IDirce3DSurface对象以及像byte[]或其他东西的图​​像数据,虽然我不确定这一点。

If that's true, then creating a CanvasBitmap from byte[] or IBuffer won't effect the underlying IDirect3DSurface , the image part will be constructed only. 如果这是真的,则创建一个CanvasBitmapbyte[]IBuffer不会影响底层IDirect3DSurface ,图像部分将只构成。 You can see that by saving that image on the disk, it gives no error. 您可以看到,通过将该图像保存在磁盘上,它不会出错。

But I think there's a workaround if you want to skip saving data on the disk : 但是, 如果您想跳过在磁盘上保存数据,我认为有一种解决方法

You can contruct the underlying IDirect3DSurface if you do Offscreen Drawing to a CanvasRenderTarget . 如果对CanvasRenderTarget执行屏幕外 绘制 ,则可以构建基础IDirect3DSurface

So, you can use the CanvasBitmap to construct a CanvasRenderTarget and then use that CanvasRenderTarget to contruct a MediaClip : 因此,您可以使用CanvasBitmap构造CanvasRenderTarget ,然后使用CanvasRenderTarget MediaClip

CanvasRenderTarget rendertarget;
using (CanvasBitmap canvas = CanvasBitmap.CreateFromBytes(CanvasDevice.GetSharedDevice(), pixels, renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight, format))
{
     rendertarget = new CanvasRenderTarget(CanvasDevice.GetSharedDevice(), canvas.SizeInPixels.Width, canvas.SizeInPixels.Height, 96);
     using (CanvasDrawingSession ds = rendertarget.CreateDrawingSession())
     {
         ds.Clear(Colors.Black);
         ds.DrawImage(canvas);
     }
}
MediaClip d = MediaClip.CreateFromSurface(renderTarget, TimeSpan.FromMilliseconds(80));
mc.Clips.Add(m);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM