Android 13(T) - Media框架(2)- libmedia

这一节学习有两个目标:
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的地址存储到mNativeContextenv->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)
{// 释放IGraphicBufferProducerdecVideoSurfaceRef(env, thiz);// 删除Java字段mNativeContext存储的指针sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);// 取消Callback注册,释放mediaplayer wrapperif (mp != NULL) {// this prevents native callbacks after the object is releasedmp->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.hqwc.cn/news/344556.html

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

相关文章

高质量训练数据助力大语言模型摆脱数据困境 | 景联文科技

目前&#xff0c;大语言模型的发展已经取得了显著的成果&#xff0c;如OpenAI的GPT系列模型、谷歌的BERT模型、百度的文心一言模型等。这些模型在文本生成、问答系统、对话生成、情感分析、摘要生成等方面都表现出了强大的能力&#xff0c;为自然语言处理领域带来了新的突破。 …

【kafka】记录用-----------1

主题&#xff08;topic&#xff09;&#xff1a;消息的第一次分类 根据人为的划分条件将消息分成不同的主题 主题的划分是人为的根据不同的任务情景去划分 比如&#xff0c;我们有两个主题&#xff0c;一个是"订单"&#xff0c;另一个是"库存"。每个主题代…

JavaScript基础(26)_dom增删改练习

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8"><title>DOM增删改练习</title><link rel"stylesheet" href"../browser_default_style/reset.css"><style>table {borde…

PMP与NPDP证书:哪个更权威?哪个含金量更高?

&#x1f3af;PMP和NPDP都具有权威性&#xff0c;但它们在领域和目标人qun方面略有不同。 1️⃣PMP在项目管理领域有较高的国际认可度 &#x1f48e;PMP是由项目管理协会(PMI)颁发的项目管理专业认证&#xff0c;具有较高的国际认可度。 PMP证书持有者通常具备严谨的项目管理知…

ubuntu20.04网络问题以及解决方案

1.网络图标消失&#xff0c;wired消失&#xff0c;ens33消失 参考&#xff1a;https://blog.51cto.com/u_204222/2465609 https://blog.csdn.net/qq_42265170/article/details/123640669 原始是在虚拟机中切换网络连接方式&#xff08;桥接和NAT&#xff09;&#xff0c; 解决…

C++代码重用:继承与组合的比较

目录 一、简介 继承 组合 二、继承 三、组合 四、案例说明 4.1一个电子商务系统 4.1.1继承方式 在上述代码中&#xff0c;Order类继承自User类。通过继承&#xff0c;Order类获得了User类的成员函数和成员变量&#xff0c;并且可以添加自己的特性。我们重写了displayI…

第七讲 单片机驱动彩色液晶屏 控制RA8889软件:显示文字:Part3.自建字库

单片机驱动TFT彩色液晶屏系列讲座 目录 第一讲 单片机最小系统STM32F103C6T6通过RA8889驱动彩色液晶屏播放视频 第二讲 单片机最小系统STM32F103C6T6控制RA8889驱动彩色液晶屏硬件框架 第三讲 单片机驱动彩色液晶屏 控制RA8889软件:如何初始化 第四讲 单片机驱动彩色液晶屏 控…

OpenCV-19图像的仿射变换

放射变换是图像旋转&#xff0c;缩放&#xff0c;平移的总称&#xff0c;具体的做法是通过一个矩阵和原图片坐标进行计算&#xff0c;得到新的坐标&#xff0c;完成变换&#xff0c;所以关键就是这个矩阵。 一、仿射变换之图像平移 使用API------warpAffine&#xff08;src &…

【LangChain学习之旅】—(7) 调用模型:使用OpenAI API还是微调开源Llama2/ChatGLM?

【LangChain学习之旅】—&#xff08;7&#xff09; 调用模型&#xff1a;使用OpenAI API还是微调开源Llama2/ChatGLM&#xff1f; 大语言模型发展史预训练 微调的模式用 HuggingFace 跑开源模型申请使用 Meta 的 Llama2 模型通过 HuggingFace 调用 LlamaLangChain 和 Hugging…

解决:ModuleNotFoundError: No module named ‘dbutils’

解决&#xff1a;ModuleNotFoundError: No module named ‘dbutils’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named dbutils背景报错问题报错翻译报错位置代码报错原因解决方法方法一&#xff0c;直接安装方法二&#xff0c;手动下载安装方法三&#xff0c;…

强化学习应用(四):基于Q-learning的无人机物流路径规划研究(提供Python代码)

一、Q-learning简介 Q-learning是一种强化学习算法&#xff0c;用于解决基于马尔可夫决策过程&#xff08;MDP&#xff09;的问题。它通过学习一个价值函数来指导智能体在环境中做出决策&#xff0c;以最大化累积奖励。 Q-learning算法的核心思想是通过不断更新一个称为Q值的…

快速搭建前端开发平台利器

JNPF是一款基于springboot、vue.js技术的企业级低代码平台&#xff0c;采用微服务、前后端分离等标准的原生架构&#xff0c;基于可视化业务建模、流程建模、表单建模、报表建模、大屏建模、移动端建模等工具&#xff0c;零代码快速构建业务应用。 官网地址&#xff1a;https:…