Android使用ANativeWindow更新surfaceView内容最简Demo

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配置文件位置

plugins {id 'com.android.application'id 'org.jetbrains.kotlin.android'
}android {compileSdkVersion 34buildToolsVersion "30.0.3"defaultConfig {applicationId "com.example.learnopengl"minSdkVersion 24targetSdkVersion 30versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"externalNativeBuild {cmake {cppFlags ""}}ndk {abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"}}//……externalNativeBuild {cmake {path "CMakeLists.txt"}}sourceSets {main {jniLibs.srcDirs = ['libs']jni.srcDirs = []}}ndkVersion '20.1.5948944'kotlinOptions {jvmTarget = '1.8'}
}

        根CMakeLists配置:

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

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

        子工程opengl_decoder的CMake配置

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

# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.cmake_minimum_required(VERSION 3.4.1)
#project(zbar LANGUAGES C VERSION 2.0.2)
project(opengl_decoder)#SET(zbar_sdk_dir ${CMAKE_SOURCE_DIR}/src/main/cpp/opengl_decoder)
message(AUTHOR_WARNING ${CMAKE_CURRENT_SOURCE_DIR})
message(AUTHOR_WARNING ${opengl_decoder})
add_definitions("-DDYNAMIC_ES3")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.#INCLUDE_DIRECTORIES("/lib")add_library( # Sets the name of the library.opengl_decoder# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).# Associated headers in the same location as their source# file are automatically included.OpenGLNativeRenderJNIBridgeDemo.hOpenGLNativeRenderJNIBridgeDemo.cpp)# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.find_library(log-lib log)
find_library(android-lib android)
find_library(EGL-lib EGL)
#find_library(GLESv2-lib GLESv2)
find_library(GLESv3-lib GLESv3)
find_library(OpenSLES-lib OpenSLES)
find_library(dl-lib dl)
find_library(z-lib z)target_link_libraries(opengl_decoderjnigraphics${log-lib}${android-lib}${EGL-lib}${GLESv3-lib}${OpenSLES-lib}${dl-lib}${z-lib}#数学库:m
)${PROJECT_SOURCE_DIR}/lib/${ANDROID_ABI}/libiconv.so)
message(AUTHOR_WARNING ${PROJECT_SOURCE_DIR})

 

        实际逻辑代码:

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

package com.cjztest.glOffscreenProcess.demo1import android.content.Context
import android.graphics.Color
import android.graphics.PixelFormat
import android.util.AttributeSet
import android.view.SurfaceHolder
import android.view.SurfaceView
import com.opengldecoder.jnibridge.JniBridge
import kotlin.random.Randomclass NativeModifySurfaceView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null
) : SurfaceView(context, attrs), SurfaceHolder.Callback {private lateinit var mSurfaceHolder: SurfaceHolderinner class TestThread : Thread() {override fun run() {while(this@NativeModifySurfaceView.isAttachedToWindow) {JniBridge.drawToSurface(holder.surface, (0xFF000000.toInt()or (Random.nextFloat() * 255f).toInt()or ((Random.nextFloat() * 255f).toInt() shl  8)or ((Random.nextFloat() * 255f).toInt() shl 16)))sleep(16)}}}init {mSurfaceHolder = holdermSurfaceHolder.addCallback(this)mSurfaceHolder.setFormat(PixelFormat.RGBA_8888)isFocusable = truesetFocusableInTouchMode(true)}private var mTestThread: TestThread ?= nulloverride fun surfaceCreated(holder: SurfaceHolder) {if (mTestThread == null) {mTestThread = TestThread()}mTestThread?.start()}override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {}override fun surfaceDestroyed(holder: SurfaceHolder) {}}

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

mSurfaceHolder.setFormat(PixelFormat.RGBA_8888)

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

package com.opengldecoder.jnibridge;import android.graphics.Bitmap;
import android.view.Surface;public class JniBridge {static {System.loadLibrary("opengl_decoder");}public static native void drawToSurface(Surface surface, int color);
}

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

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

//cpp/opengl_decoder/OpenGLNativeRenderJNIBridgeDemo.cppJNIEXPORT void JNICALLJava_com_opengldecoder_jnibridge_JniBridge_drawToSurface(JNIEnv *env, jobject activity,jobject surface, jint color) {ANativeWindow_Buffer nwBuffer;LOGI("ANativeWindow_fromSurface ");ANativeWindow *mANativeWindow = ANativeWindow_fromSurface(env, surface);if (mANativeWindow == NULL) {LOGE("ANativeWindow_fromSurface error");return;}LOGI("ANativeWindow_lock ");if (0 != ANativeWindow_lock(mANativeWindow, &nwBuffer, 0)) {LOGE("ANativeWindow_lock error");return;}LOGI("ANativeWindow_lock nwBuffer->format ");if (nwBuffer.format == WINDOW_FORMAT_RGBA_8888) {LOGI("nwBuffer->format == WINDOW_FORMAT_RGBA_8888 ");for (int i = 0; i < nwBuffer.height * nwBuffer.width; i++) {*((int*)nwBuffer.bits + i) = color;}}LOGI("ANativeWindow_unlockAndPost ");if (0 != ANativeWindow_unlockAndPost(mANativeWindow)) {LOGE("ANativeWindow_unlockAndPost error");return;}ANativeWindow_release(mANativeWindow);LOGI("ANativeWindow_release ");}

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

 

    override fun surfaceCreated(holder: SurfaceHolder) {if (mTestThread == null) {mTestThread = TestThread()}mTestThread?.start()}inner class TestThread : Thread() {override fun run() {while(this@NativeModifySurfaceView.isAttachedToWindow) {JniBridge.drawToSurface(holder.surface, (0xFF000000.toInt()or (Random.nextFloat() * 255f).toInt()or ((Random.nextFloat() * 255f).toInt() shl  8)or ((Random.nextFloat() * 255f).toInt() shl 16)))sleep(16)}}}

        效果:

                截两次不同颜色填充结果

结尾:

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

引用

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/47873.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决:Linux上SVN 1.12版本以上无法直接存储明文密码

问题&#xff1a;今天在Linux机器上安装了SVN&#xff0c;作为客户端使用&#xff0c;首次执行SVN相关操作&#xff0c;输入账号密码信息后&#xff0c;后面再执行SVN相关操作&#xff08;比如"svn update"&#xff09;还是每次都需要输入密码。 回想以前在首次输入…

Python进阶(4)--正则表达式

正则表达式 在Python中&#xff0c;正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是一种强大的文本处理工具&#xff0c;它允许你使用一种特殊的语法来匹配、查找、替换字符串中的文本。 在这之前&#xff0c;还记得之前我们是通过什么方法分割…

[论文笔记] pai-megatron-patch Qwen2-CT 长文本rope改yarn

更改: # Copyright (c) 2024 Alibaba PAI and Nvidia Megatron-LM Team. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License a…

【系统架构设计】数据库系统(二)

数据库系统&#xff08;二&#xff09; 数据库模式与范式数据库设计数据库设计的方法数据库设计的基本步骤 事务管理并发控制故障和恢复 备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库设计 数据库设计的方法 目前已有的数据库设计方法可分…

element UI :el-table横向列内容超出宽度,滚动条不显示问题

是否能解决你问题的前提 **看到这篇文章的解决问题的方案之前&#xff0c;请先回忆你是否在项目中的全局样式或者私有组件中去单独设置过滚动条样式。如果有 请继续往下看&#xff1a;**单独设置过滚动条样式代码实例&#xff1a; ::-webkit-scrollbar {/*滚动条整体样式*/wi…

layui 让table里的下拉框不被遮挡

记录&#xff1a;layui 让table里的下拉框不被遮挡 /* 这个是让table里的下拉框不被遮挡 */ .goods_table .layui-select-title,.goods_table .layui-select-title input{line-height: 28px;height: 28px; }.goods_table .layui-table-cell {overflow: visible !important; }.…

【Django】网上蛋糕项目商城-注册,登录,修改用户信息,退出功能

概念 通过以上多篇文章的讲解&#xff0c;对该项目的功能已经实现了很多&#xff0c;本文将对该项目的用户注册&#xff0c;登录&#xff0c;修改用户信息&#xff0c;以及退出等功能的实现。 注册功能实现 点击head.html头部页面的注册按钮&#xff0c;触发超链接跳转至use…

操作系统发展简史(Unix/Linux 篇 + DOS/Windows 篇)+ Mac 与 Microsoft 之风云争霸

操作系统发展简史&#xff08;Unix/Linux 篇&#xff09; 说到操作系统&#xff0c;大家都不会陌生。我们天天都在接触操作系统 —— 用台式机或笔记本电脑&#xff0c;使用的是 windows 和 macOS 系统&#xff1b;用手机、平板电脑&#xff0c;则是 android&#xff08;安卓&…

来聊聊去中心化Redis集群节点如何完成通信

写在文章开头 今天我们来聊点有意思的&#xff0c;关于redis中集群间通信的设计与实现&#xff0c;本文将从源码的角度分析redis集群节点如何利用Gossip协议完成节点间的通信与传播&#xff0c;希望对你有帮助。 Hi&#xff0c;我是 sharkChili &#xff0c;是个不断在硬核技术…

MAVSKD-Java开源库mavsdk_server库macOS平台编译

1.下载源码 2.使用IDEA打开,进行mavsdk_server目录,使用gradle进行编译 3.开始编译时会自动下载依赖 4.下载完成后,会自动编译 5.编译成功 6.成功生成AAR文件

2024算力基础设施安全架构设计与思考(免费下载)

算网安全体系是将数据中心集群、算力枢纽、一体化大数据中心三个层级的安全需求进行工程化解耦&#xff0c;从国家安全角度统筹设计&#xff0c;通过安全 服务化方式&#xff0c;依托威胁情报和指挥协同通道将三层四级安全体系串联贯通&#xff0c;达成一体化大数据安全目标。 …

文件IO(Ubuntu)

文件IO 目的 将数据写入文件中 与标准IO的区别 &#xff08;为什么要学习文件IO&#xff09; 标准IO只能操作普通文件和特殊的管道文件 文件IO能操作几乎所有的的文件 缓存区的目的 标准IO有缓存区 文件IO没有缓存区 根据右图描述 标准IO 文件IO buffer缓存区 有缓存区…

数据库管理的艺术(MySQL):DDL、DML、DQL、DCL及TPL的实战应用(上:数据定义与控制)

文章目录 DDL数据定义语言1、创建数据库2、创建表3、修改表结构4、删除5、数据类型 列的约束主键约束&#xff08;primary key&#xff09;唯一约束&#xff08;unique key&#xff09;非空约束检查约束&#xff08;check&#xff09;外键约束&#xff08;foreign key&#xff…

水域救援装备的详细简介_鼎跃安全

水域救援行动需要救援人员配备全面、专业的装备&#xff0c;以应对各种复杂的水域环境和救援任务。水域救援套装应运而生&#xff0c;它集合了水域救援所需的各类关键装备&#xff0c;为救援人员提供全方位的保护和辅助&#xff0c;确保数援行动的高效与安全。 水域救援头盔&am…

S参数入门

一、说明 S参数全称为散射参数&#xff0c;主要用来作为描述线性无源互联结构的一种行为模型&#xff0c;来源于网络分析方法。网络分析法是一种频域方法&#xff0c;在一组离散的频率点上&#xff0c;通过在输入和输出端口得到的参量完全描述线性时不变系统&#xff08;定义参…

PyTorch 深度学习实践-循环神经网络基础篇

视频指路 参考博客笔记 参考笔记二 文章目录 上课笔记基于RNNCell实现总代码 基于RNN实现总代码 含嵌入层的RNN网络嵌入层的作用含嵌入层的RNN网络架构总代码 其他RNN扩展基本注意力机制自注意力机制&#xff08;Self-Attention&#xff09;自注意力计算多头注意力机制&#xf…

redis笔记和测试

redis是用c语言写的,放不频繁更新的数据&#xff08;用户数据。课程数据&#xff09; Redis 中&#xff0c;"穿透"通常指的是缓存穿透&#xff08;Cache Penetration&#xff09;问题&#xff0c;这是指一种恶意或非法请求直接绕过缓存层&#xff0c;直接访问数据库或…

Nginx(详解)

1. 什么是Nginx&#xff1f; Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&#xff0c;在BSD-like 协议下发行。其特点是占有内存少&#xff0c;并发能力强&#xff0c;事实上nginx的并发能力在同类型的网页服务器中表…

elementUI在手机端使用遇到的问题总结

之前的博客有写过用vue2elementUI封装手机端选择器picker组件&#xff0c;支持单选、多选、远程搜索多选&#xff0c;最终真机调试的时候发现有很多细节样式需要调整。此篇博客记录下我调试过程中遇到的问题和解决方法。 一、手机真机怎么连电脑本地代码调试&#xff1f; 1.确…

Blender4.2版本正式上线,新版本的5个主要功能!

​Blender刚刚推出了备受瞩目的 Blender 4.2 版本&#xff0c;这款软件专为那些在视觉特效、动画制作、游戏开发和可视化设计领域工作的艺术家们量身打造。作为最新的长期稳定更新&#xff0c;Blender 4.2 不仅稳定可靠&#xff0c;还引入了备受期待的“Eevee Next”实时渲染引…