手繪視頻最終的生成物是視頻文件,前面幾篇主要講的是手繪視頻的創做部分,今天講一下手繪視頻的導出問題。主要以 UWP 爲例,另外會介紹一些 Web 端遇到的問題和解決方法。瀏覽器
如上所述,手繪視頻在創做後,最終會導出爲視頻文件,如 MP4,WMV 等,咱們目前的選擇是 MP4,整個導出大體分爲幾個步驟:服務器
1. 後臺渲染手繪視頻ide
後臺渲染咱們藉助的仍是 Win2D,前面幾篇介紹過,創做繪製過程也是藉助 Win2D 來完成動態渲染的。把須要渲染的元素和指定的時間等屬性傳遞給 Win2D,其餘的由 Win2D 去完成,這裏很少做介紹。oop
2. 按幀率定製截取圖片性能
這個步驟的實現方式較多,咱們使用的是 CanvasBitmap.CreateFromBytes 和 MediaClip.CreateFromSurface 的方式截圖,並把每部分的視頻片斷文件保存下來,看一段示例代碼:url
var img = CanvasBitmap.CreateFromBytes(device, screen.GetPixelBytes(), (int)screen.SizeInPixels.Width, (int)screen.SizeInPixels.Height, screen.Format); var clip = MediaClip.CreateFromSurface(img, span); layerTmp.Overlays.Add(CreateMediaOverlay(clip, size, s - start)); var composition = new MediaComposition(); composition.Clips.Add(MediaClip.CreateFromSurface(bkScreen, TimeSpan.FromMilliseconds(s - start))); composition.OverlayLayers.Add(layerTmp); var mediaPartFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync($"part_{mediafileList.Count}.mp4", CreationCollisionOption.ReplaceExisting); await composition.RenderToFileAsync(mediaPartFile, MediaTrimmingPreference.Fast, MediaEncodingProfile.CreateMp4(quality)); mediafileList.Add(mediaPartFile);
3. 圖片序列生成視頻spa
這一步驟,廣泛來說都是經過 FFMpeg 來實現,FFMpeg 在 C# 語言方面也有不少封裝版本可用。不過咱們在 UWP 中並無使用 FFMpeg,一方面代碼庫體積較大,另外一方面咱們有 MediaComposition 和 MediaClip 可用。插件
咱們使用前面步驟保存下來的視頻片斷,使用 MediaComposition.RenderToFileAsync 方法保存到視頻文件 ××.mp4 中:code
foreach (var mediaPartFile in mediafileList) { var mediaPartClip = await MediaClip.CreateFromFileAsync(mediaPartFile); bkComposition.Clips.Add(mediaPartClip); } var saveOperation = bkComposition.RenderToFileAsync(file, MediaTrimmingPreference.Fast, MediaEncodingProfile.CreateMp4(quality));
4. 處理插入視頻的音軌orm
這一步驟操做相對簡單,由於 MediaOverlay 對聲音的支持很方便,咱們只須要把插入的視頻,按照設定的開始時間和結束時間作裁剪,而後作好指定的旋轉等變換,接下來設置 MediaOvelay.AudioEnabled = true; 就能夠了,若是須要對視頻靜音,就設置爲 false。
var overlay = CreateMediaOverlay(overlayClip, size, effect.StartAt); overlay.AudioEnabled = videoGlygh.IsEnableAudio; layer.Overlays.Add(overlay); bkComposition.OverlayLayers.Add(layer);
5. 處理視頻背景音樂
處理背景音樂也是使用 MediaComposition 的 BackgroundAudioTracks,經過音頻文件來建立 BackgroundAudioTrack。它是一個 iList 類型,也就是說咱們能夠加入多個音軌。看一下簡單的示例代碼:
StorageFile music = await StorageFile.GetFileFromApplicationUriAsync(new Uri(DrawOption.Instance.DefaultMusic.url)); var backgroundTrack = await BackgroundAudioTrack.CreateFromFileAsync(music); bkComposition.BackgroundAudioTracks.Add(backgroundTrack);
這裏須要處理一些特殊狀況,好比手繪視頻中容許音頻文件循環播放,這時咱們須要對音頻文件作一下拼接,簡單的根據視頻時間和音頻時間作一下手動拼接:
int i = 1; while (DrawOption.Instance.MusicLoop && duration.TotalMilliseconds * i < total) { var track = await BackgroundAudioTrack.CreateFromFileAsync(music); track.Delay = TimeSpan.FromMilliseconds(i * duration.TotalMilliseconds); bkComposition.BackgroundAudioTracks.Add(track); ++i; }
到這裏咱們就完成了在 UWP 中導出手繪視頻的工做,而導出時間通常和視頻分辨率,渲染元素的複雜度有很大關聯,目前 720P 視頻的導出時間大概是手繪視頻時長的 2 倍左右。當視頻很長,好比超過 10 分鐘時,導出時間會變得比較長,以前咱們也 fix 過一個 bug,就是圖片大量保存到本地時,本地磁盤 IO 變成了瓶頸,磁盤佔用量也很高,後面針對這個 bug 作了修改,把本地保存文件改成內存中持有,作好 GC 工做。
這樣一來,視頻導出的時間消耗就能夠接受了,同時咱們還有 Web 端平臺,它一樣也具有手繪視頻創做和導出的功能,它的導出功能是在服務器端完成的,服務器是 Linux,它並無 UWP 這麼幸運,它的導出工做運行起來比較緩慢,基本會在視頻長度的 5- 10倍左右,流程以下:
這裏影響導出時間的主要是 PhantomJS 的截圖,它的性能很差,每幀圖片截圖的時間好久,拖慢了總體速度。而目前咱們想到了,除了使用 C++ 從新寫一下截圖的功能,沒有其餘好的辦法,而即便重寫,效率提高也不會太大。
基於這些問題,咱們想到了另外一個解決辦法,在用戶本地,使用瀏覽器插件或本地應用程序,來完成轉換並同步到服務器。下面簡單說說咱們目前嘗試的幾種方案:
1. 傳統的錄屏方案
在咱們考慮把 Web 端視頻生成轉移到本地的第一時間,就想到了這個方案。實現方面相對於用戶直接使用一個 3rdParty 的錄屏軟件,不一樣點就在於咱們能夠獲取用戶選擇了什麼音頻做爲背景音樂,咱們能夠把它上傳到服務器端,展現在‘個人做品’列表裏。流程以下圖:
這種方式實現相對簡單,基本就是 FFMpeg 的使用,可是弊端也很明顯。由於是錄屏,因此錄製過程當中,用戶的瀏覽器不能移動、不能最小化、也不能暫停,並且必須預覽完整的一遍,不可控性很是多,因此很快就被否決了。
2. Web 端結合本地程序方案
這個方案須要 Web 端和本地程序各自作一些事情,簡單來講就是本地程序在本機啓動一個服務,Web 端按照幀率在後臺渲染的 Canvas 裏截取圖片傳給本地程序,本地程序生成視頻,合成音軌,上傳到服務器,流程如圖:
本地程序是一個後臺服務,沒有界面,不須要用戶配合,瀏覽器只要不關閉就能夠完成,用戶不須要進行預覽,這些就是這個方案的優勢。目前這個方案正在開發中,開發完成後,咱們會就這個方案詳細作分享,仍是一種很腦洞的實現方式。
到這裏咱們就講解完畢了,UWP 的視頻導出,Web 端視頻導出的問題,以及目前咱們想到的解決方案,若是你們有更好的辦法,歡迎反饋給咱們,感謝!