1. 程式人生 > >Android音視訊-視訊採集(OpenGL ES渲染)

Android音視訊-視訊採集(OpenGL ES渲染)

上面的都是基於Android的高階應用層API來實現的音視訊的採集和編碼,下面我們要開啟攝像頭通過OpenGL ES底層native程式碼來渲染視訊畫面。

簡介

總體的思路是從攝像頭採集到視訊的資料,然後傳遞給底層的OpenGL ES來渲染顯示到上層的SurfaceView上面,專案總體檔案結構如下:


下面的實現列出關鍵的步驟和程式碼,完整的專案程式碼文章末尾列出。

Java上層程式碼實現

這裡主要是Android應用層的Camera的一些配置切換等操作,並且通過native介面呼叫底層的實現程式碼
程式碼簡單類圖:


前面的一些許可權配置等基礎的程式碼就不列出。主要類是LPreviewScheduler.java檔案
  • LPreviewView:SurfaceView繼承封裝類
  • LVideoCamera:封裝操作Camera的一些方法
  • LPreviewScheduler:總體排程器,包含上面兩個類,並且對外提供應用介面

    上面的操作Camera的類LVideoCamera.java是基於Camera來做的,詳細可見以前文章的一些介紹。下面主要列出LPreviewScheduler的程式碼實現。

package com.lyman.camerapreview.preview;

import android.hardware.Camera.CameraInfo;
import android.util.Log;
import android.view.Surface;
import
android.view.SurfaceHolder; public class LPreviewScheduler implements LVideoCamera.LVideoCameraCallback, LPreviewView.LPreviewViewCallback { static { System.loadLibrary("camerapreview"); } private static final String TAG = "LPreviewScheduler"; private LPreviewView mPreviewView; private
LVideoCamera mCamera; public LPreviewScheduler(LPreviewView previewView, LVideoCamera camera) { isStopped = false; this.mPreviewView = previewView; this.mCamera = camera; this.mPreviewView.setCallback(this); this.mCamera.setCallback(this); } public int getNumberOfCameras() { if (null != mCamera) { return mCamera.getNumberOfCameras(); } return -1; } /** 切換攝像頭, 底層會在返回來呼叫configCamera, 之後在啟動預覽 **/ public native void switchCameraFacing(); private boolean isFirst = true; private boolean isSurfaceExsist = false; private boolean isStopped = false; private int defaultCameraFacingId = CameraInfo.CAMERA_FACING_FRONT; @Override public void createSurface(Surface surface, int width, int height){ startPreview(surface, width, height, defaultCameraFacingId); } private void startPreview(Surface surface, int width, int height, final int cameraFacingId){ if (isFirst) { prepareEGLContext(surface, width, height, cameraFacingId); isFirst = false; } else { createWindowSurface(surface); } isSurfaceExsist = true; } public void startPreview(final int cameraFacingId){ try { if(null != mPreviewView){ SurfaceHolder holder = mPreviewView.getHolder(); if(null != holder){ Surface surface = holder.getSurface(); if(null != surface){ startPreview(surface, mPreviewView.getWidth(), mPreviewView.getHeight(), cameraFacingId); } } } } catch (Throwable e) { e.printStackTrace(); } } public native void prepareEGLContext(Surface surface, int width, int height, int cameraFacingId); public native void createWindowSurface(Surface surface); @Override public native void resetRenderSize(int width, int height); @Override public void destroySurface(){ if(isStopped){ this.stopPreview(); } else{ this.destroyWindowSurface(); } isSurfaceExsist = false; } public void stop() { isStopped = true; if(!isSurfaceExsist){ this.stopPreview(); } } private void stopPreview(){ this.destroyEGLContext(); isFirst = true; isSurfaceExsist = false; isStopped = false; } public native void destroyWindowSurface(); public native void destroyEGLContext(); /** * 當Camera捕捉到了新的一幀影象的時候會呼叫這個方法,因為更新紋理必須要在EGLThread中, * 所以配合下updateTexImageFromNative使用 **/ @Override public native void notifyFrameAvailable(); public void onPermissionDismiss(String tip){ Log.i("problem", "onPermissionDismiss : " + tip); } private CameraConfigInfo mConfigInfo; /** 當底層建立好EGLContext之後,回調回來配置Camera,返回Camera的配置資訊,然後在EGLThread執行緒中回調回來繼續做Camera未完的配置以及Preview **/ public CameraConfigInfo configCameraFromNative(int cameraFacingId){ defaultCameraFacingId = cameraFacingId; mConfigInfo = mCamera.configCameraFromNative(cameraFacingId); return mConfigInfo; } /** 當底層EGLThread建立初紋理之後,設定給Camera **/ public void startPreviewFromNative(int textureId) { mCamera.setCameraPreviewTexture(textureId); } /** 當底層EGLThread更新紋理的時候呼叫這個方法 **/ public void updateTexImageFromNative() { mCamera.updateTexImage(); } /** 釋放掉當前的Camera **/ public void releaseCameraFromNative(){ mCamera.releaseCamera(); } }

從開始顯示到停止的過程通過下面的圖串一遍


上層程式碼還是比較簡單的。

Native層程式碼分析

下面主要介紹底層的C++實現程式碼。

配置專案

首先看一下C++檔案的目錄結構:


上面我配置了兩個輸出so庫,一個commontool和一個camerapreview庫,其中camerapreview引用commontool中的實現程式碼。
CmakeLists.txt在專案中的配置略,看其中的配置C++程式碼如下:
cmake_minimum_required(VERSION 3.4.1)
find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )

set(PATH_TO_ROOT ${CMAKE_SOURCE_DIR}/src/main/cpp)

include_directories(${PATH_TO_ROOT}/libcommon/)

file(GLOB FILES_LIB_COMMON "${PATH_TO_ROOT}/libcommon/*.cpp")
file(GLOB FILES_LIB_COMMON_EGL_CORE "${PATH_TO_ROOT}/libcommon/egl_core/*.cpp")
file(GLOB FILES_LIB_COMMON_MSG_Q "${PATH_TO_ROOT}/libcommon/message_queue/*.cpp")
file(GLOB FILES_LIB_COMMON_GL_MEDIA "${PATH_TO_ROOT}/libcommon/opengl_media/*.cpp")
file(GLOB FILES_LIB_COMMON_GL_MEDIA_RENDER "${PATH_TO_ROOT}/libcommon/opengl_media/render/*.cpp")
file(GLOB FILES_LIB_COMMON_GL_MEDIA_TEXTURE "${PATH_TO_ROOT}/libcommon/opengl_media/texture/*.cpp")
file(GLOB FILES_LIB_COMMON_GL_MEDIA_TEXTURE_COPIER "${PATH_TO_ROOT}/libcommon/opengl_media/texture_copier/*.cpp")
file(GLOB FILES_LIB_COMMON_SL_MEDIA "${PATH_TO_ROOT}/libcommon/opensl_media/*.cpp")

add_library( # Sets the name of the library.
             commontool

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ${FILES_LIB_COMMON}
             ${FILES_LIB_COMMON_EGL_CORE}
             ${FILES_LIB_COMMON_MSG_Q}
             ${FILES_LIB_COMMON_GL_MEDIA}
             ${FILES_LIB_COMMON_GL_MEDIA_RENDER}
             ${FILES_LIB_COMMON_GL_MEDIA_TEXTURE}
             ${FILES_LIB_COMMON_GL_MEDIA_TEXTURE_COPIER}
             ${FILES_LIB_COMMON_SL_MEDIA}
             )

# Include libraries needed for renderer lib
target_link_libraries(
                      commontool
                      ${log-lib}
                      android
                      GLESv2
                      EGL
                      OpenSLES)

include_directories(${PATH_TO_ROOT}/camera_preview/)

file(GLOB FILES_LIB_CAMERA_PREVIEW "${PATH_TO_ROOT}/camera_preview/*.cpp")

add_library( # Sets the name of the library.
             camerapreview

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             ${FILES_LIB_CAMERA_PREVIEW}
             ${PATH_TO_ROOT}/LPreviewScheduler.cpp
             )

# Include libraries needed for renderer lib
target_link_libraries(
                      camerapreview
                      commontool)

其中核心互動的類檔案為mv_recording_preview_controller.cpp,它與上層的互動過程已經在上面列出來,下面主要分析這個類的實現。

實現程式碼

類檔案mv_recording_preview_controller.cpp為和上層互動的主要集合類,串聯了其他各個模組的實現。
主要的使用類如下:

  • MVRecordingPreviewHandler:實現了訊息機制,上層所有方法的呼叫通過它轉發到訊息佇列呼叫
  • EGLSurface:通過接受上層的Surface構造顯示檢視
  • render_preview_renderer:渲染檢視邏輯處理類,這個裡面主要是OpenGL ES相關的實現程式碼

底層的實現的程式碼還是在OpenGL ES的渲染部分,裡面的細節實現程式碼還沒有完全弄明白,這裡先串聯出整個的渲染的流程,具體細節日後慢慢了解了再細究。下面是render_preview_renderer.cpp的實現大體流程:

其中渲染模組的OpenGL程式碼沒有完全吃透。待以後補充。

相關推薦

Android視訊-視訊採集OpenGL ES渲染

上面的都是基於Android的高階應用層API來實現的音視訊的採集和編碼,下面我們要開啟攝像頭通過OpenGL ES底層native程式碼來渲染視訊畫面。 簡介 總體的思路是從攝像頭採集到視訊的資料,然後傳遞給底層的OpenGL ES來渲染顯示到上層

Android視訊-視訊採集Camera2功能實現

這一篇文章我們要實現Camera實現的等一些功能。熟悉Camera2API的使用,著重瞭解我們前面沒有深入瞭解的視訊錄製相關的內容。 基本功能實現 切換攝像頭 這個的實現和Camera API的步驟一摸一樣。只是換了一個API而已。Camera是通

Android視訊-視訊採集Camera預覽

Camera的使用我們直接根據官網介紹的使用流程,然後細入每個環節的內容,完全掌握Camera的使用。 我們最終的Demo在最後貼上,最終的Demo顯示效果如下: 建立Camera應用 我們快速的來顯示一個相機預覽的程式碼 宣告相機許可權和

Android 視頻深入 十三 OpenSL ES 制作音樂播放器,能暫停和調整音量附源碼下載

音視頻 OpenSL ES 項目地址https://github.com/979451341/OpenSLAudio OpenSL ES 是基於NDK也就是c語言的底層開發音頻的公開API,通過使用它能夠做到標準化, 高性能,低響應時間的音頻功能實現方法。 這次是使用OpenSL ES來做一個音樂播

android遊戲開發OpenGL ES繪製矩形平面

接觸android將近一年了,以前學的應用開發,現在自學android遊戲開發,把自己學到的分享出來一下,(這也是我的第一篇部落格),不說廢話了,開始正文: GLRender類用於圖形的渲染工作,Util類用於glrender中的資料緩衝。 GLRender類: pack

視訊監控專案含完整程式碼

功能簡介: 採集端: 1.USB攝像頭採集資料(yuyv格式),通過v4l2 API。      2.  資料格式轉換,yuyv->yuv420p. 3.h264編碼壓縮.通過x264編碼庫 4.資料傳輸(tcp)。

FFmpeg In Android - H264碼流解碼/OpenGL ES渲染

主要思路是FFmpeg解碼H264得到一張yuv420p圖片後,傳遞給opengl es在著色器內部做圖片轉換yuv->rgb,然後通過紋理貼圖的方式渲染出來.這種方式的效率更高.核心程式碼如下: #include "common.h" #include "gl_util.h"

師兄分享的視訊好好看2018.12.18

第四天的視訊: 有很多很多的視訊,需要學習!珍惜在學校的時間哇!!!!! 1:選擇結構 1-1:switch語句 如果比較幾個固定的值,我們建議使用switch,而不建議使用if。 需要常量表達式: 總結:在這個過程中,最重要的注意點是注意switch結束的條

師兄分享的視訊好好看2018.12.12

第一部分:計算機基礎知識 1:計算機概述 2:計算機硬體和軟體概述 3:軟體開發和計算機語言概述 4:人機互動 5:常見DOS命令 有一個注意的地方就是目錄的劃分是右斜杆 補充的快捷鍵:如下圖 注意點:在刪

iOS視訊轉Gif附example code

前言 這篇部落格源於公司中的一個專案需求,實現也比較簡單,程式碼附在文章的最後。 思路 視訊轉Gif的思路非常簡單: 從視訊中抽幀 將抽出來的幀資料拼接成Gif 這兩個步驟都是用的iOS API,所以實現起來也不復雜。 之前搜尋到網上一個實現版本,但是其中

Vue Quill Editor自定義圖片/視訊上傳Element UI + OSS、字型、字型大小、段落等

  近期專案中需要使用富文字編輯器,開始想到的富文字編輯器是百度的UEditor,UEditor功能齊全、外掛多,但是圖片只能上傳到本地伺服器,如果需要上傳到其他伺服器需要改動原始碼,而且是PHP、JSP、ASP、.Net版本,同時UEditor體積過大壓縮包有

ffmreg thinkphp 控制器 獲取音訊視訊詳細資訊獲取時長

原文地址:https://blog.csdn.net/XueDing_/article/details/86139665 在thinkphp控制器裡面獲取頁面提交過來的音訊連結進行獲取時長 第一步安裝ffmpeg FFmpeg下載:http://ffmpeg.zera

基於jQuery的自定義Video視訊播放元件相容IE6~8

之前用過video.js外掛寫過視訊元件,但是自己一般僅僅是播放個視訊,裡面有好多功能用不到,改著不太方便,於是就自己用jQuery封裝了一個外掛,控制器之類的樣式是可以自定義的,支援中英文切換,廢話不多說,下面上圖。 高版本瀏覽器video樣式 注:如果遇到進度

最簡單的基於libVLC的例子:最簡單的基於libVLC的視訊播放器圖形介面版

=====================================================最簡單的基於libVLC的例子文章列表:=====================================================本文記錄使用libVLC

ASP.NET開發學習視訊教程大全共800集

ASP.NET是微軟.NET平臺的支柱之一,被廣泛應用在WEB等網際網路開發領域,因此它的強大性和適應性,可以使它執行在Web應用軟體開發者的幾乎全部的平臺上。這裡整理了最全的ASP.NET開發學習視訊教程,總共八百集,一次看個夠! 視訊教程地址:http://blog.c

RTSP視訊測試地址親測可用

1、rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov 一段動畫片 2、rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp

獲取網頁中的視訊下載地址用headless browser

介紹 前面通過兩篇文章講了怎麼去抓取HTTP的請求包,包括用代理伺服器和抓包的方法。正因為現在的視訊網站的視訊地址都不是直接在html頁面上獲取的,視訊的獲取是通過瀏覽器動態解釋js指令碼,再向視訊伺服器發去視訊請求。所以我們通過獲取瀏覽器產生的HTTP請求來

視訊主觀實驗中一個簡易的視訊測試介面C# Winfrom實現

在做針對視訊的主觀質量測試時,需要讓受試者通過測試介面進行觀看與打分。因此做了一個簡易的測試介面,採集使用者的資訊和打分情況,並匯入SQL資料庫,方便進一步的處理。測試流程分為7步,即圖中對應的7個序號,本篇部落格將對各部分的邏輯和程式碼進行梳理,整體難度不大,由於時間緊迫很

ANDROID 高效能圖形處理 之 二. OPENGL ES

在之前的介紹中我們說到在Android 4.2上使用RenderScript有諸多限制,我們於是嘗試改用OpenGL ES 2.0來實現濾鏡。本文不詳細介紹OpenGL ES的規範以及組成部分,感興趣的同學可以閱讀 《OpenGL -ES Programming

JAVA通過URL連結獲取視訊檔案資訊無需下載檔案

  最近專案碰到一個大坑:APP上需要在獲取視訊列表時就獲取視訊的時長,但早期上傳的時候資料庫都沒有儲存這個資料,所以前段時間新增一個時長欄位,在上傳時手動輸入視訊時長,但是之前庫中有上萬條資料沒這個資訊,如果這樣一條一條手動輸入,人都得瘋掉。所以誰也不提不管這破事,在這之前的視訊時長資訊就讓它空在那。最近領