Android 13(T) - Media框架(2)- MediaPlayer与native的串接 libmedia

news2024/11/15 13:51:00

这一节学习有两个目标:
1 熟悉Android Media API的源码路径与调用层次
2 从MediaPlayer的创建与销毁了解与native的串接

1、源码路径

Media相关的API位于:frameworks/base/media/java/android/media,里面提供有MediaPlayer MediaCodecList MediaExtractor MediaCodec等常用类型;

JNI函数位于:frameworks/base/media/jni,每一个Java类型都有其对应的JNI函数文件android_media_xxx。譬如MediaPlayer.java,它对应的JNI函数文件名是android_media_MediaPlayer.cpp

JNI函数中会封装有真正的native实现,这些native实现分布在源码的不同位置,用的比较多的位置在libmedia 和 libstagefright下,这里我们先了解libmedia:frameworks/av/media/libmedia

这里将常用API与其底层实现分为两组:

  • MediaPlayer:Android为我们提供的播放器接口,实现了基本的播放器功能与常见Callback事件的串接,使用起来很方便;
  • MediaCodecList MediaExractor MediaCodec:实现播放器需要的基本组件,用这些组件也可以实现强大的播放器,自由度高也会麻烦一些;
    请添加图片描述

我们的目标是了解Media框架,所以这里从简单一点的MediaPlayer入手,看看它是如何调用native实现的。


2、MediaPlayer的创建

相关代码路径:

  • MediaPlayer.java
  • android_media_MediaPlayer.cpp
  • mediaplayer.cpp

在看正式的创建流程前,我们先要注意MediaPlayer.java中的如下代码段:

    static {
        System.loadLibrary("media_jni");
        native_init();
    }

它会加载media_jni.so,并执行native函数android_media_MediaPlayer_native_init。native_init会获取java类中的postEventFromNative方法ID,mNativeContextmNativeSurfaceTexture等字段的ID,获取到的ID会存储在静态变量fields_t中,后期可以通过这些ID找到Java对象中对应的成员,获取其存储的值或者向其中存储值。

static fields_t fields;

fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;IIILjava/lang/Object;)V");
fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");

接下来看MediaPlayer.java中的构造函数:

private MediaPlayer(int sessionId) {
	...
	try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
		native_setup(new WeakReference<MediaPlayer>(this), attributionSourceState.getParcel());
	}
}

核心是调用native_setup方法,需要将自身的弱引用对象作为参数向下传递:

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
                                       jobject jAttributionSource)
{
    Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
    android::content::AttributionSourceState attributionSource;
    attributionSource.readFromParcel(parcel);
    // 创建MediaPlayer native对象
    sp<MediaPlayer> mp = sp<MediaPlayer>::make(attributionSource);
	// 创建并注册Callback对象
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);
	// 将MediaPlayer对象存储到Java对象中
    setMediaPlayer(env, thiz, mp);
}

native_setup干了3件事:

  1. 创建MediaPlayer对象;
  2. 用传下来的弱引用对象创建Listener对象,用于Callback调用;
  3. 将MediaPlayer对象存储到Java对象中;

前两个对象的创建很简单,我们来看看如何存储MediaPlayer native对象的:

static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
{
    Mutex::Autolock l(sLock);
    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
   	// 手动增加强引用计数
    if (player.get()) {
        player->incStrong((void*)setMediaPlayer);
    }
    // 检查mNativeContext中存储的MediaPlayer对象
    if (old != 0) {
        old->decStrong((void*)setMediaPlayer);
    }
    // 将MediaPlayer的地址存储到mNativeContext
    env->SetLongField(thiz, fields.context, (jlong)player.get());
    return old;
}

setMediaPlayer很简单,但是也有需要注意的地方:

  1. 首先要增加MediaPlayer native对象的强引用计数;
  2. 检查mNativeContext中存储的地址是否为NULL,如果不为NULL则需要销毁存储的MediaPlayer对象;
  3. 将新的MediaPlayer对象存储到mNativeContext中。

虽然我们会把MediaPlayer对象存储到Java字段中,但是其引用计数在setMediaPlayer中仍为1,如不增加引用计数,出了当前作用域对象将自动销毁。


3、MediaPlayer的销毁

我平时看代码可能会注重了解播放器的启动、运行以及Callback处理流程,很容易就忽视release的流程,但是release做了哪些事情,按照什么顺序释放资源同样也是很重要的。

MediaPlayer.java的release会调用native函数_release

public void release() {
	...
	_release();
}

_release方法如下,会有4件事需要处理:

static void
android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
{
    // 释放IGraphicBufferProducer
    decVideoSurfaceRef(env, thiz);
    // 删除Java字段mNativeContext存储的指针
    sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
    // 取消Callback注册,释放mediaplayer wrapper
    if (mp != NULL) {
        // this prevents native callbacks after the object is released
        mp->setListener(0);
        mp->disconnect();
    }
}
  1. 释放IGraphicBufferProducer(surface),暂时不去了解;
  2. 删除Java字段mNativeContext存储的地址,将MediaPlayer的强引用计数减一;
  3. 取消Callback注册,释放MediaPlayer wrapper的内容;
  4. 出了当前函数作用域,MediaPlayer对象自动销毁。

到这儿MediaPlayer与native的串接就了解结束了,其他API是如何调用的想必也很容易看懂了。


4、libmedia

从前面几节我们了解到MediaPlayer的native实现位于libmedia中,这里我们来看看libmedia到底包含哪些东西。

libmedia底下的内容大致可以分为四类:

  1. Java类的native实现,例如:mediaplayer.cpp mediarecorder.cpp MediaScanner.cpp
  2. binder service所需要的文件,这类可以分为两小类:
    • aidl文件,编译时直接生成bp/bn文件,例如IMediaExtractorService.aidl
    • 手动实现的bp/bn文件,例如IMediaPlayerService.cpp
  3. binder service中传输对象所需的文件,例如IMediaPlayer.cpp IMediaExtractor.cpp IDataSource.cpp IMediaCodecList.cpp
  4. 其他成员类,例如MediaCodecInfo.cpp MediaCodecBuffer.cpp OMXBuffer.cpp

虽说第二类第三类都是用于binder service,但是他们之间仍有不一样的地方。第二类文件用于提供binder service服务,而第三类文件一般是在service中创建对象,通过binder传给远程使用。

为什么有的binder service用aidl生成相关bp/bn文件,有的却手动实现呢?大致浏览aidl文件可以发现,需要传递的类型已经使用aidl定义过了或者是基础类型;手动编写的文件例如IMediaPlayer.cpp需要传递比较复杂的类型,例如KeyedVector等,如果要用aidl编写会很麻烦。

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

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

相关文章

数据结构一轮复习 之 第二章

一、线性表&#xff08;逻辑结构&#xff09;-顺序表&#xff08;物理结构&#xff09; 操作&#xff1a; 静态分配&#xff1a;ElemType data[Lenght] 动态分配&#xff08;空间可扩充&#xff1a;新开辟一个更大的空间&#xff0c;并移动原数据&#xff09;&#xff1a;Ele…

【机器学习】西瓜书学习心得及课后习题参考答案—第5章神经网络

5.1神经元模型——这是神经网络中最基本的成分。 5.2感知机与多层网络——由简单的感知机循序渐进引出多层前馈神经网络。 5.3误差逆传播算法——BP算法&#xff0c;迄今最成功的神经网络学习算法。算法如下&#xff08;公式参考西瓜书&#xff09; 停止条件与缓解BP过拟合的…

与“云”共舞,联想凌拓的新科技与新突破

伴随着数字经济的高速发展&#xff0c;IT信息技术在数字中国建设中起到的驱动和支撑作用也愈发凸显。特别是2023年人工智能和ChatGPT在全球的持续火爆&#xff0c;更是为整个IT产业注入了澎湃动力。那么面对日新月异的IT信息技术&#xff0c;再结合疫情之后截然不同的经济环境和…

【Redis】内存数据库Redis进阶(Redis持久化)

目录 分布式缓存 Redis 四大问题Redis 持久化RDB (Redis DataBase)RDB执行时机RDB启动方式——save指令save指令相关配置save指令工作原理save配置自动执行 RDB启动方式——bgsave指令bgsave指令相关配置bgsave指令工作原理 RDB三种启动方式对比RDB特殊启动形式RDB优点与缺点 A…

2023年的深度学习入门指南(23) - ChatGLM2

2023年的深度学习入门指南(23) - ChatGLM2 在《在你的电脑上运行大模型》这一节&#xff0c;我们曾经介绍过ChatGLM模型&#xff0c;它是当时最好的中文大模型之一。现在&#xff0c;它又更新到了第二代&#xff0c;即ChatGLM2。 当时&#xff0c;我们的技术储备还不足&#…

selenium 遇到更新chorme驱动

打开浏览器,在地址栏输入chrome://version/便可以查看到谷歌当前的版本号 谷歌浏览器驱动的下载网址 http://chromedriver.storage.googleapis.com/index.htmlhttp://chromedriver.storage.googleapis.com/index.html 解压后把chromedriver.exe 放到python安装的目录下&am…

3.netty和protobuf

1.ChannelGroup可以免遍历由netty提供,覆盖remove方法即可触发删除channel\ 2.群聊私聊 13.群聊私聊简单原理图 3.netty心跳检测机制,客户端对服务器有没有读写(读,写空闲) //IdleStateHandler(3,5,7,TimeUnite.SECONDS)是netty提供的检测状态的处理器,也加到pipeline,读,写,…

Windows下FreeImage库的配置

首先下载FreeImage库&#xff0c;http://freeimage.sourceforge.net/download.html&#xff0c;官网下载如下&#xff1a; 内部下载地址&#xff1a;https://download.csdn.net/download/qq_36314864/88140305 解压后&#xff0c;打开FreeImage.2017.sln&#xff0c;如果是vs…

【 Redis】的乱码问题

问题描述&#xff1a; 使用RedisTemplate存储的数据&#xff0c;在 redis-cli 客户端查看时&#xff0c;key 和 value 都会携带类似\xac\xad\这样的字符串。 原因&#xff1a; 由于默认使用了 jdk 的序列化方式。以下是支持的序列化方式 项目一般都会有缓存&#xff0c;常常…

Python人工智能在气象中怎样应用?

Python是功能强大、免费、开源&#xff0c;实现面向对象的编程语言&#xff0c;在数据处理、科学计算、数学建模、数据挖掘和数据可视化方面具备优异的性能&#xff0c;这些优势使得Python在气象、海洋、地理、气候、水文和生态等地学领域的科研和工程项目中得到广泛应用。可以…

软件安全测试和渗透测试的区别在哪?安全测试报告有什么作用?

软件安全测试和渗透测试在软件开发过程中扮演着不同的角色&#xff0c;同时也有不同的特点和目标。了解这些区别对于软件开发和测试人员来说非常重要。本文将介绍软件安全测试和渗透测试的区别&#xff0c;以及安全测试报告在软件开发和测试过程中的作用。 一、 软件安全测试和…

【《概率图模型原理与应用:第2版》——概率机器学习与人工智能可解释性领域不可多得的著作】

《概率图模型原理与应用&#xff1a;第2版》反映了PGM的理论基础与进展。取材精炼&#xff0c;层次分明&#xff0c;是一-本很好的关于PGM的专业书籍。同时结合了大量的案例分析与代码算例,使得初学者能快速掌握前沿的PGM理论。本书的翻译与出版能进一步推进国内 人工智能算法领…

Vue3 element-plus表单嵌套表格实现动态表单验证

Vue3结合element-plus表单项可以动态添加/删除 部分效果图如下&#xff1a; 另表格有添加和删除按钮&#xff0c;点击提交进行表单验证。 首先data格式必须是对象包裹数组 import { ref, reactive } from vue; import { FormInstance } from element-plus const froms re…

盘点16个.Net开源项目

今天一起盘点下&#xff0c;16个.Net开源项目&#xff0c;有博客、商城、WPF和WinForm控件、企业框架等。&#xff08;点击标题&#xff0c;查看详情&#xff09; 一、一套包含16个开源WPF组件的套件 项目简介 这是基于WPF开发的&#xff0c;为开发人员提供了一组方便使用自…

华为云hcip核心知识笔记(数据库服务规划)

华为云hcip核心知识笔记&#xff08;数据库服务规划&#xff09; 1.云数据接库优势 1.1云数据库优点有&#xff1a; 易用性强&#xff1a;能欧快速部署和运行 高扩展&#xff1a;开放式架构和云计算存储分离 低成本&#xff1a;按需使用&#xff0c;成本更加低廉 2.云数据库r…

nmake编译Qt第三方库出现无法打开包含文件type_traits

最近需要为个人项目ShaderLab添加内嵌的代码编辑窗口功能&#xff0c;支持语法高亮和Intellisense&#xff0c;最初使用了QCodeEditor,发现这个第三方的库对词法分析的实现效果不太行. 代码换行后直接缩进到首行&#xff0c;无法定位到前一句的首行 考虑换QScintilla&#xff…

java对象、数组作为函数的入参和出参

在Java编程中&#xff0c;将对象或者数组作为方法的入参传递&#xff0c;可以在方法中修改对象或者数组的值&#xff0c;回传给调用者&#xff0c;这样入参又承担了出参的角色。 代码示例&#xff1a; 定义一个类&#xff1a; package com.thb;public class Point {private i…

深入解析项目管理中的用户流程图

介绍用户流程图 用户流程图的定义 用户流程图(User Flow Diagram)是一种可视化工具&#xff0c;它描绘了用户在应用或网站上完成任务的过程。这些任务可以是购物、注册账户、查找信息等&#xff0c;任何需要用户交互的动作都可以在用户流程图中找到。 用户流程图的重要性 用…

9.索引签名类型,映射类型,索引查询类型

目录 1 索引签名类型 1.1 对象 1.2 数组 2 映射类型 2.1 映射联合类型 2.2 映射类型无法在接口中使用 2.3 映射对象类型 2.4 泛型工具类型都是基于映射类型实现的 3 索引查询类型 3.1 基本使用 3.2 索引查询多个相当于联合类型 1 索引签名类型 1.1 对…

07. Docker网络通信模式

目录 1、前言 2、基本原理 3、Docker网络配置 3.1、查看网络配置 3.2、4种网络模式 3.3、bridge模式 3.3.1、使用bridge网络 3.3.2、自定义bridge网络 3.4、host模式 3.5、container模式 3.6、none模式 4、小结 1、前言 前面我们介绍了Docker容器的相关内容&#…