当前位置:   article > 正文

Android使用ANativeWindow更新surfaceView内容最简Demo_anativewindow 内存 快速刷新

anativewindow 内存 快速刷新

SurfaceView简介

SurfaceView对比View的区别

        安卓的普通VIew,依赖当前ActivityWindowsurface这个surface用于承载view绘制出来所有内容因此任何一个view需要更新需要所有view进行更新即使使用区域依然其他view相应像素进行合成操作因此不适合频繁更新绘制而且更新过程只能UI线程。

        而SurfaceView自己Surface因此可以单独更新内容触发整个view更新在子线程刷新不会阻塞主线程,适用于界面频繁更新、对帧率要求较高的情况因此十分适合于视频渲染。而很多视频渲染库如FFMpeg,或者有时候一些开源OpenGL ES代码Native编写,如果要在折腾到Java层进行显示是非常麻烦的,所以SurfaceViewNative层面更新安卓图像处理输出一个必须技能

SurfaceView的ANativeWindow的获取和使用

        Surface对象不能直接jni native使用因此需要通过Android NDK工具获取本地对象ANativeWindow

        这里借用一下大佬一张图表达SurfaceViewANativeWindow调用过程描述和流程图:

 

● java层将Surface传递给native层

● 获取ANativeWindow对象

● 将显示数据写到ANativeWindow的buffer中,注意需要将显示的数据格式转换成ANativeWindow设置的数据格式

● 释放ANativeWindow

最简测试Demo

        gradle配置:

        主要是打开NativeBuild指定创建ABI编译目标以及cmake配置文件位置

  1. plugins {
  2. id 'com.android.application'
  3. id 'org.jetbrains.kotlin.android'
  4. }
  5. android {
  6. compileSdkVersion 34
  7. buildToolsVersion "30.0.3"
  8. defaultConfig {
  9. applicationId "com.example.learnopengl"
  10. minSdkVersion 24
  11. targetSdkVersion 30
  12. versionCode 1
  13. versionName "1.0"
  14. testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  15. externalNativeBuild {
  16. cmake {
  17. cppFlags ""
  18. }
  19. }
  20. ndk {
  21. abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
  22. }
  23. }
  24. //……
  25. externalNativeBuild {
  26. cmake {
  27. path "CMakeLists.txt"
  28. }
  29. }
  30. sourceSets {
  31. main {
  32. jniLibs.srcDirs = ['libs']
  33. jni.srcDirs = []
  34. }
  35. }
  36. ndkVersion '20.1.5948944'
  37. kotlinOptions {
  38. jvmTarget = '1.8'
  39. }
  40. }

        根CMakeLists配置:

        这个只是个人配置大家可以根据实际情况修改

  1. # Sets the minimum version of CMake required to build the native
  2. # library. You should either keep the default value or only pass a
  3. # value of 3.4.0 or lower.
  4. cmake_minimum_required(VERSION 3.4.1)
  5. add_compile_options(
  6. -fno-omit-frame-pointer
  7. -fexceptions
  8. -Wall
  9. )
  10. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -mfloat-abi=soft -DANDROID")
  11. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -mfloat-abi=soft -DANDROID")
  12. # 生成中间文件,便于debug
  13. set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -save-temps=obj")
  14. set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps=obj")
  15. # Creates and names a library, sets it as either STATIC
  16. # or SHARED, and provides the relative paths to its source code.
  17. # You can define multiple libraries, and CMake builds it for you.
  18. # Gradle automatically packages shared libraries with your APK.
  19. ADD_SUBDIRECTORY(src/main/cpp/opengl_decoder)

        子工程opengl_decoder的CMake配置

                声明工程文件位置、工程名,和要引入编译名字

  1. # Sets the minimum version of CMake required to build the native
  2. # library. You should either keep the default value or only pass a
  3. # value of 3.4.0 or lower.
  4. cmake_minimum_required(VERSION 3.4.1)
  5. #project(zbar LANGUAGES C VERSION 2.0.2)
  6. project(opengl_decoder)
  7. #SET(zbar_sdk_dir ${CMAKE_SOURCE_DIR}/src/main/cpp/opengl_decoder)
  8. message(AUTHOR_WARNING ${CMAKE_CURRENT_SOURCE_DIR})
  9. message(AUTHOR_WARNING ${opengl_decoder})
  10. add_definitions("-DDYNAMIC_ES3")
  11. # Creates and names a library, sets it as either STATIC
  12. # or SHARED, and provides the relative paths to its source code.
  13. # You can define multiple libraries, and CMake builds it for you.
  14. # Gradle automatically packages shared libraries with your APK.
  15. #INCLUDE_DIRECTORIES("/lib")
  16. add_library( # Sets the name of the library.
  17. opengl_decoder
  18. # Sets the library as a shared library.
  19. SHARED
  20. # Provides a relative path to your source file(s).
  21. # Associated headers in the same location as their source
  22. # file are automatically included.
  23. OpenGLNativeRenderJNIBridgeDemo.h
  24. OpenGLNativeRenderJNIBridgeDemo.cpp
  25. )
  26. # Searches for a specified prebuilt library and stores the path as a
  27. # variable. Because CMake includes system libraries in the search path by
  28. # default, you only need to specify the name of the public NDK library
  29. # you want to add. CMake verifies that the library exists before
  30. # completing its build.
  31. find_library(log-lib log)
  32. find_library(android-lib android)
  33. find_library(EGL-lib EGL)
  34. #find_library(GLESv2-lib GLESv2)
  35. find_library(GLESv3-lib GLESv3)
  36. find_library(OpenSLES-lib OpenSLES)
  37. find_library(dl-lib dl)
  38. find_library(z-lib z)
  39. target_link_libraries(
  40. opengl_decoder
  41. jnigraphics
  42. ${log-lib}
  43. ${android-lib}
  44. ${EGL-lib}
  45. ${GLESv3-lib}
  46. ${OpenSLES-lib}
  47. ${dl-lib}
  48. ${z-lib}
  49. #数学库:
  50. m
  51. )
  52. ${PROJECT_SOURCE_DIR}/lib/${ANDROID_ABI}/libiconv.so)
  53. message(AUTHOR_WARNING ${PROJECT_SOURCE_DIR})

 

        实际逻辑代码:

        1、  先编写一个SurfaceView子类然后编写一个线程TestThread,意图循环输出随机的颜色清屏信号,通过获取SurfaceView的holder内部Surface、以及清屏颜色传入到事先编写的native方法JniBridge.drawToSurface实现

  1. package com.cjztest.glOffscreenProcess.demo1
  2. import android.content.Context
  3. import android.graphics.Color
  4. import android.graphics.PixelFormat
  5. import android.util.AttributeSet
  6. import android.view.SurfaceHolder
  7. import android.view.SurfaceView
  8. import com.opengldecoder.jnibridge.JniBridge
  9. import kotlin.random.Random
  10. class NativeModifySurfaceView @JvmOverloads constructor(
  11. context: Context, attrs: AttributeSet? = null
  12. ) : SurfaceView(context, attrs), SurfaceHolder.Callback {
  13. private lateinit var mSurfaceHolder: SurfaceHolder
  14. inner class TestThread : Thread() {
  15. override fun run() {
  16. while(this@NativeModifySurfaceView.isAttachedToWindow) {
  17. JniBridge.drawToSurface(holder.surface
  18. , (0xFF000000.toInt()
  19. or (Random.nextFloat() * 255f).toInt()
  20. or ((Random.nextFloat() * 255f).toInt() shl 8)
  21. or ((Random.nextFloat() * 255f).toInt() shl 16)))
  22. sleep(16)
  23. }
  24. }
  25. }
  26. init {
  27. mSurfaceHolder = holder
  28. mSurfaceHolder.addCallback(this)
  29. mSurfaceHolder.setFormat(PixelFormat.RGBA_8888)
  30. isFocusable = true
  31. setFocusableInTouchMode(true)
  32. }
  33. private var mTestThread: TestThread ?= null
  34. override fun surfaceCreated(holder: SurfaceHolder) {
  35. if (mTestThread == null) {
  36. mTestThread = TestThread()
  37. }
  38. mTestThread?.start()
  39. }
  40. override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
  41. }
  42. override fun surfaceDestroyed(holder: SurfaceHolder) {
  43. }
  44. }

        其中要注意为了确保其中Surface像素格式RGBA8888,方便进行颜色填充实验初始化通过SurfaceHolder颜色设置RGBA8888了:

mSurfaceHolder.setFormat(PixelFormat.RGBA_8888)

2、  创建JniBridge类,编写JNI方法签名,可以传入surface对象,交由JNIEnv进行处理:

  1. package com.opengldecoder.jnibridge;
  2. import android.graphics.Bitmap;
  3. import android.view.Surface;
  4. public class JniBridge {
  5. static {
  6. System.loadLibrary("opengl_decoder");
  7. }
  8. public static native void drawToSurface(Surface surface, int color);
  9. }

3、  编写JNI方法,获取SurfaceANativeWindow然后对其进行颜色填充步骤使用SurfaceView类似的。过程如下:

通过ANativeWindow_fromSurface获取传入SurfaceANativeWindow对象,再通过ANativeWindow_lock锁定Surface获取它的数据buffer指针,然后传入color值循环便利写入对象最后调用ANativeWindow_release解锁即可看到Surface被刷成指定颜色了。

  1. //cpp/opengl_decoder/OpenGLNativeRenderJNIBridgeDemo.cpp
  2. JNIEXPORT void JNICALL
  3. Java_com_opengldecoder_jnibridge_JniBridge_drawToSurface(JNIEnv *env, jobject activity,
  4. jobject surface, jint color) {
  5. ANativeWindow_Buffer nwBuffer;
  6. LOGI("ANativeWindow_fromSurface ");
  7. ANativeWindow *mANativeWindow = ANativeWindow_fromSurface(env, surface);
  8. if (mANativeWindow == NULL) {
  9. LOGE("ANativeWindow_fromSurface error");
  10. return;
  11. }
  12. LOGI("ANativeWindow_lock ");
  13. if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, 0)) {
  14. LOGE("ANativeWindow_lock error");
  15. return;
  16. }
  17. LOGI("ANativeWindow_lock nwBuffer->format ");
  18. if (nwBuffer.format == WINDOW_FORMAT_RGBA_8888) {
  19. LOGI("nwBuffer->format == WINDOW_FORMAT_RGBA_8888 ");
  20. for (int i = 0; i < nwBuffer.height * nwBuffer.width; i++) {
  21. *((int*)nwBuffer.bits + i) = color;
  22. }
  23. }
  24. LOGI("ANativeWindow_unlockAndPost ");
  25. if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {
  26. LOGE("ANativeWindow_unlockAndPost error");
  27. return;
  28. }
  29. ANativeWindow_release(mANativeWindow);
  30. LOGI("ANativeWindow_release ");
  31. }

4、  让SurfaceHolder的在创建时生成测试线程对象,测试线程将循环合成不同的颜色然后传入刚才到刚才的native函数逻辑中,实现native层面对surface进行内容填充实例的目的:

 

  1. override fun surfaceCreated(holder: SurfaceHolder) {
  2. if (mTestThread == null) {
  3. mTestThread = TestThread()
  4. }
  5. mTestThread?.start()
  6. }
  7. inner class TestThread : Thread() {
  8. override fun run() {
  9. while(this@NativeModifySurfaceView.isAttachedToWindow) {
  10. JniBridge.drawToSurface(holder.surface
  11. , (0xFF000000.toInt()
  12. or (Random.nextFloat() * 255f).toInt()
  13. or ((Random.nextFloat() * 255f).toInt() shl 8)
  14. or ((Random.nextFloat() * 255f).toInt() shl 16)))
  15. sleep(16)
  16. }
  17. }
  18. }

        效果:

                截两次不同颜色填充结果

结尾:

        本文内容主要是展示了如何搭建一个native层面填充Surface内容的环境实际场景可以用于FFMpeg解码内容、OpenGL ES渲染拷贝显示Surface的。

引用

Android的Surface、View、SurfaceView、Window概念整理 | superxlcr's notebook

Android基础--利用ANativeWindow显示视频-腾讯云开发者社区-腾讯云

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/914999
推荐阅读
相关标签
  

闽ICP备14008679号