使用SharpAvi将屏幕截图保存到AVI会产生100帧空白视频

时间:2018-03-12 12:01:48

标签: c# gdi+ avi

我的游戏会在每个游戏循环中截取屏幕截图并存储内存。然后用户可以按“打印屏幕”来触发“SaveScreenshot”(参见下面的代码),将每个屏幕截图存储为PNG,并使用SharpAvi将它们编译成AVI。保存图像工作正常,产生~2秒AVI,但播放时不显示任何视频。它只是占位符VLC播放器图标。我认为这非常接近工作,但我无法确定是什么问题。请参阅下面的代码。如果有人有任何想法,我会非常感激!

    private Bitmap GrabScreenshot()
    {
        try
        {
            Bitmap bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
            System.Drawing.Imaging.BitmapData data =
            bmp.LockBits(this.ClientRectangle, System.Drawing.Imaging.ImageLockMode.WriteOnly,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels(0, 0, this.ClientSize.Width, this.ClientSize.Height, PixelFormat.Bgr, PixelType.UnsignedByte,
                data.Scan0);
            bmp.UnlockBits(data);
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            return bmp;
        } catch(Exception ex)
        {
            // occasionally getting GDI generic exception when rotating the image... skip that one.
            return null;
        }
    }

    private void SaveScreenshots()
    {
        var directory = "c:\\helioscreenshots\\";
        var rootFileName = string.Format("{0}_", DateTime.UtcNow.Ticks);

        var writer = new AviWriter(directory + rootFileName + ".avi")
        {
            FramesPerSecond = 30,
            // Emitting AVI v1 index in addition to OpenDML index (AVI v2)
            // improves compatibility with some software, including 
            // standard Windows programs like Media Player and File Explorer
            EmitIndex1 = true
        };

        // returns IAviVideoStream
        var aviStream = writer.AddVideoStream();

        // set standard VGA resolution
        aviStream.Width = this.ClientSize.Width;
        aviStream.Height = this.ClientSize.Height;
        // class SharpAvi.KnownFourCCs.Codecs contains FOURCCs for several well-known codecs
        // Uncompressed is the default value, just set it for clarity
        aviStream.Codec = KnownFourCCs.Codecs.Uncompressed;
        // Uncompressed format requires to also specify bits per pixel
        aviStream.BitsPerPixel = BitsPerPixel.Bpp32;

        var index = 0;
        while (this.Screenshots.Count > 0)
        {
            Bitmap screenshot = this.Screenshots.Dequeue();

            var screenshotBytes = ImageToBytes(screenshot);

            // write data to a frame
            aviStream.WriteFrame(true, // is key frame? (many codecs use concept of key frames, for others - all frames are keys)
                              screenshotBytes, // array with frame data
                              0, // starting index in the array
                              screenshotBytes.Length); // length of the data

            // save it!
            // NOTE: compared jpeg, gif, and png. PNG had smallest file size.
            index++;
            screenshot.Save(directory + rootFileName + index + ".png", System.Drawing.Imaging.ImageFormat.Png);
        }

        // save the AVI!
        writer.Close();
    }

    public static byte[] ImageToBytes(Image img)
    {
        using (var stream = new MemoryStream())
        {
            img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            return stream.ToArray();
        }
    }

1 个答案:

答案 0 :(得分:0)

据我所知,您提供的是 png 编码的字节数组,但流配置为 KnownFourCCs.Codecs.Uncompressed

此外,来自manual

<块引用>

AVI 需要标准 Windows DIB 格式的未压缩数据,即指定位深度的自底向上位图。对于每一帧,将其数据放入字节数组并调用 IAviVideoStream.WriteFrame()

接下来,所有编码器都期望输入特定格式的图像数据。它是 BGR32 自上而下 - 每像素 32 位,蓝色字节在前,不使用 alpha 字节,顶行在前。这是您通常可以从现有图像中获得的格式。 [...] 因此,您只需传递一个未压缩的自上而下的 BGR32

我将按照手册中的说明使用 BitmapLockBits 直接从 Marshal.Copy 检索字节数组。