本文首發於微信公衆號——世界上有意思的事,搬運轉載請註明出處,不然將追究版權責任。交流qq羣:859640274前端
你們很久不見,又有兩個多月沒有發文章了。最近新型肺炎鬧得挺兇,但願你們身體健康。本篇博客是視頻編輯 SDK 解析文章中的第二篇,文章中我會介紹將上一篇文章中解碼出來的視頻幀經過 OpenGL 繪製出來的方式。WsVideoEditor 中的代碼也已經更新了。你們在看文章的時候必定要結合項目中的代碼來看。c++
本文分爲如下章節,讀者可按需閱讀:git
講解 OpenGL 的教程目前有不少,因此這一章筆者不會去教你們如何入門或者使用 OpenGL。本章筆者只會從抽象的角度來和你們討論一下筆者對於 OpenGL 的理解。至於如何入門 OpenGL 則會推薦幾個有用的網站。程序員
如圖1,咱們知道 OpenGL/OpenGL ES 是一個圖形圖像渲染框架,它的規範由Khronos組織制定,各個顯卡廠商在驅動中實現規範,再由各個系統廠商集成到系統中,最終提供各類語言的 API 給開發者使用。github
固然圖形圖像渲染框架不只僅只有 OpenGL 這一種。Apple 的 Metal(不跨平臺)、Google 的 vulkan(跨平臺)、微軟的 DirectX(不跨平臺) 都是 OpenGL 的競品。編程
那麼什麼是圖形圖像渲染框架呢?作 Android、iOS、前端、Flutter 的同窗必定都用過 Canvas,在各自的平臺中 Canvas 就是一個比較上層的圖形圖像渲染框架。後端
如圖2,咱們在使用 Canvas 繪製一個三角形的時候通常有如下步驟,在 OpenGL 中也是相似:api
除了像 Canvas 同樣繪製 2D 圖像,OpenGL 最主要的功能仍是進行 3D 繪製,這就是 Canvas 們沒法企及的地方了。緩存
要了解 OpenGL 是如何工做的,首先咱們得知道:OpenGL 運行在哪裏? 沒錯有些讀者已經知道了:OpenGL 運行在 GPU 上面,至於在 GPU 上運行的好壞我就不贅述了。微信
咱們在平時的開發當中,絕大部分時間都在與內存和 CPU 打交道。忽然讓咱們寫運行在 GPU 上面的程序,我想大部分人都會水土不服,畢竟這是一個思惟上的轉變。大多數教程一上來就是告訴你們如何調用 OpenGL 的 api,而後拼湊出一個程序來,你們也照貓畫虎的敲出代碼,但最終不少人並無理解 OpenGL 是如何運行的,這也是它難學的地方。那麼下面我會經過一張圖來粗略的講講 OpenGL 是如何運行的。
圖3中有一、二、三、四、5 個步驟,這幾個步驟組合起來的代碼就表示繪製一個三角形到屏幕上。可運行的代碼能夠在 learning-opengl這裏找到,圖中的代碼只是關鍵步驟。我這裏也只是講解 OpenGL 的運行方式,更具體的代碼使用還須要讀者去前面的網站中學習。這裏我推薦兩個教程,讓讓你們可以學習 OpenGL 的具體用法,畢竟仰望星空的同時腳踏實地也很是重要:
個人老本行是 Android 開發,因此這一章我會講解視頻編輯SDK在 Android 層的代碼。代碼已經更新 WsVideoEditor,本章需結合代碼食用。另外本章節強依賴 從零開始仿寫一個抖音App——視頻編輯SDK開發(一) 的第三章SDK架構以及運行機制介紹,你們必定要先讀一下。本章會省略不少已知知識。
圖4是編輯 SDK 的架構圖,從中咱們能夠看見 WsMediaPlayer 代理了 Native 的 NativeWSMediaPlayer 的 Java 類。該類具備一個播放器應該有的各類 API,例如 play、pause、seek 等等。其實不少 Android 中的系統類都是以這種形式存在的,例如 Bitmap、Surface、Canvas 等等。說到底 Java 只是 Android 系統方便開發者開發 App 的上層語言,系統中大部分的功能最終都會走到 Native 中去,因此讀者須要習慣這種代碼邏輯。那麼咱們就來看看這個類的運行方式吧。
mPlayer = new WsMediaPlayer()
:會建立一個 NativeWSMediaPlayer 對象,初始化的時候會建立兩個對象
mPlayer.setProject(videoEditorProjectBuilder.build())
:最終走到了 NativeWSMediaPlayer::SetProject
中,這裏只是將 EditorProject 設置給 VideoDecodeService 和 FrameRenderer。mPlayer.loadProject()
:最終走到了 wsvideoeditor::LoadProject 中,這裏的主要邏輯是對每一段視頻使用 FFmpeg 進行解封裝,獲取到各個視頻的時長、長寬、等等信息,而後存入 EditorProject 中以便以後使用。至於 FFmpeg 的使用能夠參見這幾篇文章:從零開始仿寫一個抖音App——音視頻開篇、零開始仿寫一個抖音App——基於FFmpeg的極簡視頻播放器若是把播放視頻比做:一個繪畫者每隔 30ms 就向畫布上繪製一幅連環畫的話。那麼繪畫者就是 WsMediaPlayer,連環畫就是視頻,畫布就是 WsMediaPlayerView。
init()
:建立 WsMediaPlayerView 是調用,初始化一些參數,註冊回調。setPreviewPlayer
:將 WsMediaPlayer 交給 PlayerGLThread,以繪製。onResume()/onPause()
:須要手動在 Activity 中調用,用於啓動/暫停繪製。onSurfaceTextureAvailable
:在 TextureView.draw
的時候被系統調用,表示咱們能夠開始進行繪製了。咱們在這裏就建立了一個 PlayerGLThread,用於在非主線程進行 30ms 的定時循環繪製。同時還獲取了繪製窗口的大小。onSurfaceTextureSizeChanged
:當繪製窗口改變的時候,更新窗口大小,最終會做用在 OpenGL 的繪製窗口上。onSurfaceTextureDestroyed
:資源銷燬。TextureView.draw
調用與 WsMediaPlayer 被設置,這兩個條件同時知足時啓動線程。mPlayer.onAttachedView(mWidth, mHeight)
來向 Native 同步繪製區域的大小。!mFinished && !mRenderPaused
爲 true,那麼調用 mPlayer.drawFrame()
進行繪製。我在從零開始仿寫一個抖音App——視頻編輯SDK開發(一) 的第四章VideoDecodeService解析中講解了如何解碼出視頻幀,在上一章中講解了如何在 Android 層準備好 OpenGL 的渲染環境。這些都爲本章打下了基礎,沒有看過的同窗必定要仔細閱讀啊。一樣本章的代碼已經上傳至WsVideoEditor,請結合代碼食用本章。
圖5是視頻編輯 SDK 的運行機制,本次咱們解析的功能是在 FrameRender 中渲染 VideoDecodeService 提供的視頻幀,也就是視頻播放功能。下面咱們就從第二章中提到的 WsMediaPlayer.draw 方法入手。
current_time_ = current_time = GetRenderPos()
獲取到的時間戳,是我構造的測試代碼。decoded_frames_unit = video_decode_service_->GetRenderFrameAtPtsOrNull(current_time)
來從 VideoDecodeService 中獲取到視頻幀,由於 VideoDecodeService 有一個單獨的線程本身對視頻進行解碼(代碼解析前面提到過)。因此這裏可能出現獲取不到視頻幀的狀況,這也是後續須要完善的地方。frame_renderer_.Render(current_time, std::move(decoded_frames_unit))
來渲染。current_original_frame_texture_ = avframe_rgba_texture_converter_.Convert
來將AVFrame 轉化成 WsTexture。此時視頻幀已經從內存中被拷貝到了顯存中了,WsTexture.gl_texture_ 能夠理解爲顯存中紋理(視頻幀)數據對象的指針。GetWsFinalDrawProgram()->DrawGlTexture
將視頻幀真正的繪製到屏幕上。經過上一小結的介紹,咱們知道了繪製視頻幀的大體流程,可是咱們只是粗略的介紹了整個渲染流程。因此這一節做爲上一節的補充,會簡單介紹一下咱們的 OpenGL 緩存邏輯和繪製邏輯。
又是一篇大幾千字的乾貨出爐,但願這篇文章能讓你滿意,廢話很少說,咱們下篇文章見。
不販賣焦慮,也不標題黨。分享一些這個世界上有意思的事情。題材包括且不限於:科幻、科學、科技、互聯網、程序員、計算機編程。下面是個人微信公衆號:世界上有意思的事,乾貨多多等你來看。