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