将GameView视图画面输出成视频或者Gif、序列帧, 在某些特定的场景中还有些作用。 比如使用Timeline制作的过场动画渲染成视频,游戏运行时流式加载, 而不必将相关的资源打包在包体中。Unity官方推出Preview版本的Encorder, 本文将系列探讨。

MediaEncoder

MediaEncoder是Unity内置的编码器,将图像和音频样本编码到音频或电影文件中。构造此类的实例会创建一个编码器,该编码器将创建一个具有指定轨道的音频、视频或音频/视频文件。对每个轨道交替调用 AddFrame() 和 AddSamples() 方法,以便帧和样本保持对每个轨道进行均等填充。

将所有需要的帧和样本都添加到文件中后,调用 Dispose() 以正确结束每个轨道并关闭文件。

using UnityEditor.Media;

public class Recorder
{
    static public void RecordMovie()
    {
        var videoAttr = new VideoTrackAttributes
        {
            frameRate = new MediaRational(50),
            width = 320,
            height = 200,
            includeAlpha = false
        };

        var audioAttr = new AudioTrackAttributes
        {
            sampleRate = new MediaRational(48000),
            channelCount = 2,
            language = "fr"
        };

        int sampleFramesPerVideoFrame = audioAttr.channelCount *
            audioAttr.sampleRate.numerator / videoAttr.frameRate.numerator;

        var encodedFilePath = Path.Combine(Path.GetTempPath(), "my_movie.mp4");

        Texture2D tex = new Texture2D((int)videoAttr.width, (int)videoAttr.height, TextureFormat.RGBA32, false);

        using (var encoder = new MediaEncoder(encodedFilePath, videoAttr, audioAttr))
        using (var audioBuffer = new NativeArray<float>(sampleFramesPerVideoFrame, Allocator.Temp))
        {
            for (int i = 0; i < 100; ++i)
            {
                // Fill 'tex' with the video content to be encoded into the file
                encoder.AddFrame(tex);
                // Fill 'audioBuffer' with the audio content to be encoded 
                encoder.AddSamples(audioBuffer);
            }
        }
    }
}

上述脚本就是创建一个分辨率320x200, 画面采样率50Hz, 音频采样率48K的mp4编码视频。

音频获取

使用AudioRenderer 截取引擎输出音频, 调用Start之后, 进入音频录制模式。在此之后,Unity 将输出静音,直到调用 AudioRenderer.Stop。 因此,如果桌面有其他声音也不会输出, 比如边听歌边录制,歌声也不会录入。

## AsyncGPUReadback 接口

为了更加快速的处理视频(比如Flip), 这部分运算在新版本中放在了GPU中计算, 当然需要Unity2018以及更高的版本, 并且设备需要SystemInfo.supportsAsyncGPUReadback的支持。

IEnumerator Start()
{
    var rt = new RenderTexture(512, 512, 0);
    //Graphics.xxx...
    var req = AsyncGPUReadback.Request(rt);
    yield return new WaitUntil(() => req.done);

    var tex = new Texture2D(512, 512, TextureFormat.RGBA32, false);
    var colorArray = req.GetData<Color32>().ToArray();
    tex.SetPixels32(colorArray);
    tex.Apply();
}

视频编码

Unity Recorder中支持定义了三种视频编码格式, 如下面代码所示:

public enum VideoRecorderOutputFormat
{
    /// <summary>
    /// Output the recording with the H.264 codec in an MP4 container.
    /// </summary>
    MP4,
    /// <summary>
    /// Output the recording with the VP9 codec in a WebM container.
    /// </summary>
    WebM,
    /// <summary>
    /// Output the recording with the ProRes codec in a MOV container.
    /// </summary>
    MOV,
}

H.264/MPEG-4第10部分,或称AVC(Advanced Video Coding,高级视频编码),是一种视频压缩标准,一种被广泛使用的高精度视频的录制、压缩和发布格式。是由ITU-T视频编码专家组(VCEG)和ISO/IEC动态图像专家组(MPEG)联合组成的联合视频组(JVT,Joint Video Team)提出的高度压缩数字视频编解码器标准。这个标准通常被称之为H.264/AVC(或者AVC/H.264或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC)而明确的说明它两方面的开发者。

WebM由Google提出,是一个开放、免费的媒体文件格式。WebM 影片格式其实是以 Matroska(即 MKV)容器格式为基础开发的新容器格式,里面包括了VP8影片轨和 Ogg Vorbis 音轨,其中Google将其拥有的VP8视频编码技术以类似BSD授权开源,Ogg Vorbis 本来就是开放格式。 WebM标准的网络视频更加偏向于开源并且是基于HTML5标准的,WebM 项目旨在为对每个人都开放的网络开发高质量、开放的视频格式,其重点是解决视频服务这一核心的网络用户体验。Google 说 WebM 的格式相当有效率,应该可以在 netbook、tablet、手持式装置等上面顺畅地使用。VP9 是Google提供的开源的免费视频codec,是VP8的后续版本,初始开发时命名为下一代开源视频或者VP-NEXT. VP9的开发始于2011年Q3,试图降低VP8的50%的码率而保持相同的质量

ProRes是 Apple 开发的一种解码器,MOV 封装格式。MAC常用。普通PC安装Quick可以对其解码,要进行编码需要安装相关插件,文末会分享。ProRes 编解码器提供独一无二的多码流实时编辑性能、卓越图像质量和降低的存储率组合。Apple ProRes 编解码器充分利用多核处理,并具有快速、降低分辨率的解码模式。所有 Apple ProRes 编解码器都支持全分辨率的所有帧尺寸(包括 SD、HD、2K、4K 和 5K)。目前 ProRes 有6个版本(由低到高):ProRes Proxy、ProRes 422 LT、ProRes 422、 ProRes 422 HQ、ProRes 4444 及 ProRes 4444 XQ 。

更多的视频格式以及相关的特性, 请参考这篇文章.

实现部分

H.264

这部分使用的编码器是MediaEncoder,其提供了如下方法来提供了视频录制方法:

  • AddFrame 将帧附加到文件的视频轨道。
  • AddSamples 将样本帧附加到指定的视频轨道。
  • Dispose 完成写入所有轨道,并关闭正在写入的文件。

V9 WebM

通过引用Native库(c++, 插件中库的名字fccore.dll/fccore.bundle)来实现编码,然后通过下面wrap接口完成解析:

[DllImport("fccore")]  
static extern fcStream fcCreateFileStream(string path);
[DllImport("fccore")] 
static extern void  fcReleaseStream(fcStream s);
[DllImport("fccore")] 
static extern void  fcGuardBegin();
[DllImport("fccore")] 
static extern void  fcGuardEnd();
[DllImport("fccore")] 
static extern void  fcReleaseDeferredCall(fcDeferredCall dc);

ProRes QuikTime

与WebM类似, Unity没有直接的支持,也是使用外部的Native接口(c++, mac平台是AVFoundationWrapper.bundle)来实现编码,通过下面wrap接口实现:

[DllImport(ProResWrapperInfo.LibraryPath)]
static extern IntPtr Create(string sMetadata, string sFileName, int width, int height, float fps, bool hasAudio, float fAudioSamplingRate, int codecType, bool hasTransparency, int colorDesc);
[DllImport(ProResWrapperInfo.LibraryPath)]
static extern bool AddVideoFrame(IntPtr pEncoder, byte[] pixels);
[DllImport(ProResWrapperInfo.LibraryPath)]
static extern bool AddAudioSamples(IntPtr pEncoder, float[] samples, int numSamples);
[DllImport(ProResWrapperInfo.LibraryPath)]
static extern bool Close(IntPtr pEncoder);

参考