Android短視頻SDK轉碼實踐

来源:https://www.cnblogs.com/KSC123/archive/2017/12/25/8110277.html
-Advertisement-
Play Games

轉碼對於普通用戶來說不可見的,但卻是短視頻SDK的一個重要過程。 ...


一. 前言

一些涉及的基本概念:

  • 轉碼:一般指多媒體文件格式的轉換,比如解析度、碼率、封裝格式等;
  • 解復用(demux):從某種封裝中分離出視頻track和音頻track,然後交給後續模塊進行處理;
  • 復用(mux):將視頻壓縮數據(例如H.264)和音頻壓縮數據(例如AAC)合併到某種封裝格式的文件中去。常提到的MP4即是一種封裝;
  • 編碼(encode):通過專門的演算法(例如H.264AAC)來對原始音視頻數據進行壓縮;
  • 解碼(decode):對壓縮後的數據進行解壓縮。

短視頻APP中錄製完成後,為什麼要做轉碼:

  1. 原始視頻文件碼率較大,上傳下載都需要很長時間,不利於傳播;
  2. 編輯時增加特效、轉場效果後,只是在預覽中有效,原始文件並未改變,需要進行一次轉碼來把這些效果合成進最終的文件;
  3. 多段視頻進行編輯前轉碼拼接為一個文件,方便後續的編輯;
  4. 目標格式和源文件格式不一致,比如需要從mp4轉成gif

為什麼不在服務端做轉碼呢?

  1. 短視頻需要加入濾鏡等效果,在移動端轉碼可以充分利用手機的GPU等資源,實現實時添加濾鏡實時看到效果;
  2. 原始視頻碼率較大,上傳下載都需要很長時間。

轉碼的主要流程如下:


  圖1. 轉碼基本流程

其中Audio FilterVideo Filter分別是指音頻和視頻的預處理。

  • 短視頻轉碼的時機:
  1. 多段視頻的導入;
  2. 轉場完的合成;
  3. 編輯完的合成。

二. Demuxer方案的選擇

Demuxer模塊的實現,主要有以下三種方案:

  1. 方案一,使用播放器
    播放器的主要功能是播放,也就是從原始文件/流中提取出音視頻,按照pts完成音視頻的渲染。轉碼並不需要渲染,要求在保持音視頻同步的情況下,儘快把解碼數據重新按要求編碼成新的音視頻包,重新復用成文件。我們也曾經為了實現儘快這個要求,把播放器強行改造成快速播放的模式,但後來遇到了很多問題:

    • 音視頻同步時機的問題,視頻的解碼是慢於音頻的解碼,必然需要實現同步邏輯。player中如果改成快速播放模式,player內部加上音視頻同步的邏輯,改動非常大。如果player不管同步,解碼數據直接上拋給調用層,則需要在短視頻上層做音視頻同步,引入了額外的工作量;
    • 使用硬解碼時,從SurfaceTexture中獲取的timestamp不准。因此最後放棄了這個方案。
  2. 方案二,使用MediaExtractor
    MediaExtractorAndroid系統封裝好的用來分離容器中的視頻track和音頻trackJava類。優點是使用簡單,缺點是支持的格式有限。

  3. 方案三,使用FFmpeg
    使用FFmpegav_read_frame API來做解復用,即實現簡易版的播放器邏輯。

    • 優點:FFmpeg中對視頻格式有大量相容的邏輯,相比MediaExtractor相容性好,增加新的輸入格式的支持會更容易,同時音視頻同步邏輯的控制更簡單;
    • 缺點: 需要引用FFmpeg,相對來說SDK體積較大。

方案二的相容性不如方案三。相比方案一,方案三把音視頻的解復用和解碼都放到了同一個線程,av_read_frame能輸出同步交織的音視頻packet,上層邏輯調用更清晰。
同時短視頻其他功能模塊已經引入了FFmpeg,轉碼模塊引入FFmpeg並不增加包大小,所以選擇了FFmpeg方案。

三. 轉碼的數據傳遞

金山雲多媒體SDK實踐中,Demuxer實際上是在C層做的,但是介面的封裝是在Java層。解碼結構也是一樣。DemuxerDecoder之間如何高效地在JavaC層之間傳遞待解碼的音視頻包?

3.1 AVPacket的傳遞

FFmpegdemuxer模塊解復用出來的為音頻或視頻的AVPacket。最開始的時候我們並沒有在Java層對整個AVPacket的地址指針進行封裝,而是把數據封裝在ByteBuffer和其他的參數中。這樣遇到了很多因為AVPacket中的參數沒有傳遞到解碼模塊導致的問題。

最終我們通過intptr_tC層保存AVPacket的指針,同時在Java層以long類型來保存和傳遞這個指針,解決了這個問題。

3.2 AVFormatContext/AVCodecParams的傳遞

為了實現模塊的復用,我們把DemuxerDecoder分成了兩個模塊。使用FFmpeg來實現時,Decoder模塊可以和Demuxer模塊共用AVFormatContext,通過AVFormatContext來創建AVCodecContext

但是這樣會有一個問題,Demuxer的工作速度會快於Decoder,此時AVFormatContext是由Demuxer來創建的,Demuxer停止的時候會釋放AVFormatContext。如果交給Decoder模塊來釋放,不利於模塊的復用和解耦。最終我們發現在FFmpeg 3.3的版本中,AVCodecParams結構圖中有Decoder所需要的全部信息,可以通過傳遞AVCodecParams來構造AVCodecContext

四. 轉碼提速

轉碼的速度是客戶非常關心的一個點,轉碼時間太長,用戶體驗會非常差。我們花了非常多的精力來對短視頻的轉碼時間進行提速。經驗主要有以下這些點:

4.1 調整視頻軟編編碼參數

轉碼的時間大部分都被視頻的編碼占用了,我們把x264編碼做了調整,在保證畫質影響較小的前提下,節省了30%以上的編碼時間。

4.2 優化GPU數據讀取

使用視頻軟編時,如何從GPU中把數據“下載”到CPU上,我們嘗試了很多中方案,具體的我們會在另一篇文章中詳細解釋。之前的方案是使用ImageReader讀取RGBA數據。優化為用OpenGL ESRGBA轉換為YUVA。讀取數據後從YUVA再轉為I420,下載和格式轉化總耗時,提速了大約40%。

4.3 開啟硬編

硬編的缺點: 在Android平臺上,硬編的相容性較差,同時視頻硬編的壓縮比差於軟編。
硬編的優點是顯而易見的,編碼器速度快,占用的資源也相對較少。

4.4 開啟硬解

經過大量的測試,硬解的相容性相較於硬編會好很多,使用硬解碼,直接使用MediaCodec渲染到texture上,省去手動上傳YUV的步驟,也節省了軟解碼的時間開銷。

4.4.1 硬編解遇到的坑

關於Android的硬編解網上已經有很多例子,官方文檔也比較完善。不過在實現過程中還是會遇到一些意想不到的問題。

  • 圖像質量的問題

在硬編上線後,我們對比畫質發現轉碼圖像質量較差。原因是使用MediaCodec API時,選擇的是MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBRCBR的好處是碼率比較穩定,但是會犧牲畫質,移動直播中選用CBR更合理。短視頻轉碼場景硬編時推薦使用MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_VBRVBR會獲得更好的圖像質量。對於軟編時,我們也嘗試過ABR(也就是VBR),但實際測試下來效果並不能保證。

  • 硬解不相容AVCC/HVCC 碼流格式

H.264碼流主要分Annex-BAVCC兩種格式,H.265碼流主要分為Annex-BHVCC格式。AnnexBAVCC/HVCC的區別在於參數集與幀格式,AnnexB的參數集spsppsNAL的形式存在碼流中(帶內傳輸),以startcode分割NAL
AVCC/HVCC 的參數集存儲在extradata中(帶外傳輸),使用NALU長度(固定位元組,通常為4位元組,從extradata中解析)分隔NAL,通常MP4MKV使用AVCC格式來存儲。
Android的硬解只接受Annex-B格式的碼流,所以在解碼MP4 Demux出的視頻流時,需要解析extradata,取出sps、pps,通過CSD(Codec-Specific Data)來初始化解碼器;並且將AVCC碼流轉換為Annex-B,在ffmpeg中使用h264_mp4toannexb_filterhevc_mp4toannexb做轉換。

  • 硬解時間戳不准確的問題

硬解碼器解碼視頻到Surface,此時通過SurfaceTexture.getTimestamp()獲得時間戳並不准確,某些機型會出現異常。所以還是要使用解碼輸入的時間戳,可將解碼過程由非同步轉為同步,或者將pts存儲到隊列中來實現。

  • 音頻硬編硬解解的速度

MediaCodec的音頻編解碼具體實現和機型有關,許多機型的MediaCodec音頻編解碼工作仍然是軟體方案。經過測試MediaCodec音頻硬編碼較軟編碼有6%左右的提速,但MediaCodec音頻硬解反而比軟解的的速度慢,具體原因有待進一步調查。不過這隻是部分機型的測試結果,更多機型的比較大家可以使用我們demo的轉碼/合成功能進行測試。

4.5 轉碼提速對比

下麵以三星S8為例,短視頻SDK在轉碼速度上的進步,更多機型的對比數據,請移步github wiki查看。

將1分鐘1080p 18Mbps視頻,轉碼成540p 1.2Mbps,不同版本時間開銷大致如下:

機型版本編碼方式第一次合成時長第二次合成時長第三次合成時長平均值
三星S8 V1.0.4 軟編 52s 54s 58s 54.7s
  V1.1.2 軟編 49s 50s 50s 49.7s
  V1.1.2 硬編 35s 36s 38s 36.3s
  V1.4.7 硬編 21.5s 21.9s 22.5s 22.0s

可以看到,使用了硬編、硬解等提速手段後,合成速度由54秒優化到22秒。

五. 模塊化的思考

金山雲短視頻SDK的基礎模塊是基於直播SDK,整體來說,是一套push模式的流水線。


流水線中的每個模塊都很好地實現瞭解耦,單獨模塊完成單一的功能,模塊的復用也非常方便。前置模塊在產生新的音視頻幀後,會立即push給後續模塊,後續模塊需要儘快把前置模塊產生的音視頻幀消化掉,最大程度上保證實時性。為了保證音視頻同步等邏輯,引入了大量同步鎖。在短視頻的開發中,遇到了不少的死鎖和不方便。對於短視頻這種非實時的場景,更多的時候,需要由後續模塊(而非前置模塊)來控制整個流程的進度。
當前處理過程中需要實現暫停,需要在前置模塊加鎖來實現。為了能方便以後的開發,我們會在接下來重新梳理這種push流水線的方式, 實現模塊化的同時,儘量減少同步鎖的使用。

六. 總結

轉碼對於普通用戶來說不可見的,但卻是短視頻SDK的一個重要過程。怎麼樣讓轉碼過程耗時更短,轉碼圖像質量更高,特效添加更靈活,減少我們團隊自身的開發和維護成本,同時也為開發者提供最方便易用的API,一直是金山雲多媒體SDK團隊的目標。
團隊在很用心的開發短視頻SDK,歡迎試用!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、前端MVC概要 1.1、庫與框架的區別 框架是一個軟體的半成品,在全局範圍內給了大的約束。庫是工具,在單點上給我們提供功能。框架是依賴庫的。Vue是框架而jQuery則是庫。 1.2、AMD與CMD 在傳統的非模塊化JavaScript開發中有許多問題:命名衝突、文件依賴、跨環境共用模塊、性能優 ...
  • 閑話不多說,開篇擼代碼,你可以會看到類似如下的結構: See the Pen react props by 糊一笑 (@rynxiao) on CodePen. 當一個組件嵌套了若幹層子組件時,而想要在特定的組件中取得父組件的屬性,就不得不將 一層一層地往下傳,我這裡只是簡單的列舉了3個子組件,而當 ...
  • 1、示例代碼 (註:寫到vue單文件中了) 2、說明 (1)需要transition 標簽包裹。 (2)6個class狀態 (3)效果: ...
  • 本節流程如圖: 現在正式進入打包流程,起步方法為run: 為什麼不介紹compiler對象?因為構造函數中並沒有一個初始化的方法,只是普通的變數聲明,沒啥好講的。 在run方法中,首先是調用了tapable的applyPluginsAsync執行了before-run事件流,該事件流的定義地點如下: ...
  • 例如: !function(){alert('ok')}(); 常見格式:(function() { /* code */ })(); //也等同於此 匿名函數 解釋:包圍函數(function(){})的第一對括弧向腳本返回未命名的函數,隨後一對空括弧立即執行返回的未命名函數,括弧內為匿名函數的參 ...
  • 需求說明:簡單演示ajax提交fromData類型數據。 代碼說明:ajax在傳輸數據的時候基本格式大都是固定的,只需要修改傳輸類型即可。下麵介紹基本的參數提交。 步驟一:建立html或者jsp頁面,引入jquery-3.2.1.min.js(其他版本亦可)。 步驟二:建立文件選擇輸入框,上傳按鈕並 ...
  • 閱讀目錄: 主機安裝 集群搭建 API 操作 API 說明和 etcdctl 命令說明 "etcd" 是 CoreOS 團隊發起的一個開源項目(Go 語言,其實很多這類項目都是 Go 語言實現的,只能說很強大),實現了 分散式鍵值存儲 和 服務發現 ,etcd 和 ZooKeeper/Consul ...
  • 原文地址:http://www.woshipm.com/pmd/160822.html+ 支付寶系統架構概況 典型處理預設 資金處理平臺 財務會計 支付清算 核算中心 交易 柔性事務 支付寶的開源分散式消息中間件–Metamorphosis(MetaQ) Metamorphosis (MetaQ) ... ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...