Unity多媒体转换
将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);