Android音视频开发Media FrameWork框架源码解析

目录
  • 一、Media FrameWork背景
  • 二、Media Framework“路线图”
    • 2.1 代理端
    • 2.2 服务端
      • 2.2.1 Source
      • 2.2.2 Decoder
      • 2.2.3 Renderer
      • 2.2.4 Foundation
    • 2.3 OMX端
    • 2.4 Kernel端
  • 三、media播放的流程
  • 四、Media FrameWork源码分析

一、Media FrameWork背景

Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频与视频文件,支持的文件类型包括MPEG4、H.264、MP3、AAC、AMR、JPG 与PNG 等。 Surface Manager (外观管理函数库):管理图形界面的操作与2D、3D 图层的显示。

二、Media Framework“路线图”

我们可以看到用红色框框圈起来的地方。一个是app应用Gallery(也可以为第三方player);另外一个是Media Framework。对,没错,讲了这么多,我们的主角“Media Framework”登场了。让我们来看看它的庐山真面目, 如图所示:

接下来,给大家简单介绍下它。看的顺序是→ ↓ ← ↓ →(肿么都觉得是在打表情符号:-D)

2.1 代理端

这一端做的事情只是将下面复杂的逻辑进行封装(java),然后透过jni调用底下的native层方法来实现具体功能。并且,这些个具体的功能是在服务端实现的,他们分属不同的进程,通过Binder来通信,最终通过调用服务端的方法实现具体的逻辑处理。(有童鞋问:Binder是个什么东东呢? 小弟有时间会讲解的,现在就理解它是一个进程间通信的一种方式就好,求甚解的朋友们可以百度下_)

2.2 服务端

这边的主要任务就是在MediaPlayerFactory中,创建出NuplayerDriver(这个不是底层驱动啦,我们理解为一个抽象出来的NuPlayer的基类就好啦)。 然后Nuplayer中,我们可以看到有三大模块。

2.2.1 Source

这里是为咱们的播放器提供数据源的(解协议,解封装在这里)。

2.2.2 Decoder

这里是解码数据的地方(解码在这里)

2.2.3 Renderer

这里是用来做Display的,里面涉及到A/V同步的问题。

2.2.4 Foundation

这个部分是基础类。在后面的分析当中,我们会知道在NuPlayer中会启动相当多的线程,这些线程如何异步/同步的通信,需要依靠AMessage/ALooper/AHandler来支持

之后, 通过接口类IOMX来通过Binder进程间通信,远程调用具体的decoder来实现解码。

2.3 OMX端

这一端就比较靠近底层了,里面会有各种各样的插件注册其中。它还链接这Codec Driver,这里面就是放的各种具体的解码器啦。

2.4 Kernel端

最后, OMX的具体解码器在启动Kernel层的A/V Codec Driver完成解码操作。

三、media播放的流程

在framework中涉及media播放的流程头文件如下:IMediaPlayer.h mediaplayer.h IMediaPlayerClient.h

其中IMediaPlayer.h 定义了binder通信相关的接口。 定义了:

class BnMediaPlayer: public BnInterface
{
public:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
};
​

IMediaPlayer.cpp 是binder通信接口的实现。

class BpMediaPlayer: public BpInterface; status_t BnMediaPlayer::onTransact();

mediaplayer.h 是定义binder通信的客户端。在mediaplayer.cpp中如下代码获取BpMediaPlayer:

status_t MediaPlayer::setDataSource(
        const char *url, const KeyedVector *headers)
{
    LOGV("setDataSource(%s)", url);
    status_t err = BAD_VALUE;
    if (url != NULL) {
        const sp& service(getMediaPlayerService());
        if (service != 0) {
            sp player(
                    service->create(getpid(), this, url, headers));
            err = setDataSource(player);
        }
    }
    return err;
}
​

服务端在MediaPlayerService中。在MediaPlayerService.h中如下定义:

class Client : public BnMediaPlayer 在MediaPlayerService.cpp中,create函数创建了BnMediaPlayer:

sp MediaPlayerService::create(
​
        pid_t pid, const sp& client, const char* url,
        const KeyedVector *headers)
{
    int32_t connId = android_atomic_inc(&mNextConnId);
    sp c = new Client(this, pid, connId, client);
    LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);
    if (NO_ERROR != c->setDataSource(url, headers))
    {
        c.clear();
        return c;
    }
    wp w = c;
    Mutex::Autolock lock(mLock);
    mClients.add(w);
    return c;
}

再来看一下MediaPlayer这个类的定义:

class MediaPlayer : public BnMediaPlayerClient, public virtual IMediaDeathNotifier{}

很奇怪:在binder通信的客户端又有了一个binder通信的服务端: BnMediaPlayerClient 在IMediaPlayerClient.h 中这个binder通信只有一个接口:

class IMediaPlayerClient: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayerClient);​
    virtual void notify(int msg, int ext1, int ext2) = 0;
};

这个binder通信服务为谁提供呢?在回来看一下MediaPlayerServer中的create函数:

sp MediaPlayerService::create( pid_t pid, const sp& client, const char* url, const KeyedVector *headers)

客户端就在这里。这个binder通信的实质是一个消息回调函数。framework的media框架式一个双向binder通信框架。

以seek接口为例分析一下:

在mediaplayer.cpp 中调用seek 接口:

MediaPlayer (seek)->IMediaPlayer.cpp(bpMediaPlayer.cpp )->IMediaPlayer.cpp(bnMediaPlayer.cpp )

在这里其实已经达到了MediaPlayerServer中的client类。当底层的media 完成seek 以后会抛出来一消息,这个消息通过 const sp& client 通知给MediaPlayer。

在media相关的头文件中还有一个MediaPlayerInterface.h 。这个头文件定义了底层播放器的接口。

四、Media FrameWork源码分析

首先,针对android.media.MediaPlayer进行分析。

里面有很多native代码,我们找到native_setup这个jni调用,就可以找到整个框架的入口。

我们查看

android_media_MediaPlayer_native_setup@framworks/base/media/jni/android_media_MediaPlayer.cpp

`static` `void` `android_media_MediaPlayer_native_setup(
    JNIEnv *env, jobject thiz, jobject weak_this
    )``
    {
        ``  ``
    LOGV(``"native_setup"``);
        ``  ``sp mp = ``new` `MediaPlayer();
        ``  ``
        if` `(mp == NULL) {
        ``    ``
        jniThrowException(
            env, ``"java/lang/RuntimeException"``,
            ``"Out of memory"``);
            ``
            ``return``;
            ``  ``

        }
            ` `  ``
            // create new listener and give it to MediaPlayer
            ``  ``
            sp listener = ``new` `JNIMediaPlayerListener(
                env, thiz, weak_this);
                ``  ``
                mp->setListener(listener);
                ` `  ``
                // Stow our new C++ MediaPlayer in an opaque field in the Java object.
                ``  ``
                setMediaPlayer(env, thiz, mp);
                ``

    }
        `

从这里的这段代码我们可以看到,android在这里实例化了一个变量mp:MediaPlayer。

并且为其设置了一个listener:JNIMediaPlayerListener

在后面我们会看到对mp的调用,现在让我们先看看MediaPlayer是什么东东。

MediaPlayer@framworks/base/include/media/mediaplayer.h MediaPlayer@framworks/base/media/libmedia/mediaplayer.cpp

在这里我们终于找到了MediaPlayer:BnMediaPlayerClient:IMediaPlayerClient

原来他也是对Bind Native的一个封装,而他本身提供了很多方法用于访问,包括start等。下面是start的cpp代码:

status_t MediaPlayer::start()
{
    LOGV("start");
    Mutex::Autolock _l(mLock);
    if (mCurrentState & MEDIA_PLAYER_STARTED)
        return NO_ERROR;
    if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
                    MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
        mPlayer->setLooping(mLoop);
        mPlayer->setVolume(mLeftVolume, mRightVolume);
        mCurrentState = MEDIA_PLAYER_STARTED;
        status_t ret = mPlayer->start();
        if (ret != NO_ERROR) {
            mCurrentState = MEDIA_PLAYER_STATE_ERROR;
        } else {
            if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
                LOGV("playback completed immediately following start()");
            }
        }
        return ret;
    }
    LOGE("start called in state %d", mCurrentState);
    return INVALID_OPERATION;
}

原来这里又调用了mPlayer:sp

从这里我们发现最终的服务,还是由IMediaPlayer这个东西提供的,而IMediaPlayer@framworks/base/include/media/IMediaPlayer.h

实际上是如下定义的一个类,它继承了IInterface@framworks/base/include/binder/IInterface.h这个类(注意虽然名字是Interface,但是它确实是个类!:-))

class IMediaPlayer: public IInterface
{
public:
    DECLARE_META_INTERFACE(MediaPlayer);
    virtual void            disconnect() = 0;
    virtual status_t        setVideoSurface(const sp<ISurface>& surface) = 0;
    virtual status_t        prepareAsync() = 0;
    virtual status_t        start() = 0;
    virtual status_t        stop() = 0;
    virtual status_t        pause() = 0;
    virtual status_t        isPlaying(bool* state) = 0;
    virtual status_t        seekTo(int msec) = 0;
    virtual status_t        getCurrentPosition(int* msec) = 0;
    virtual status_t        getDuration(int* msec) = 0;
    virtual status_t        reset() = 0;
    virtual status_t        setAudioStreamType(int type) = 0;
    virtual status_t        setLooping(int loop) = 0;
    virtual status_t        setVolume(float leftVolume, float rightVolume) = 0;
    virtual status_t        invoke(const Parcel& request, Parcel *reply) = 0;
    virtual status_t        setMetadataFilter(const Parcel& filter) = 0;
    virtual status_t        getMetadata(bool update_only,
                                        bool apply_filter,
                                        Parcel *metadata) = 0;
};

为了弄清楚,在什么地方产生的mPlayer,我转而分析MediaPlayerService@framworks/base/media/libmediaplayerservice/MediaPlayerService.h

其中有如下代码

virtual sp<IMediaRecorder>  createMediaRecorder(pid_t pid);
void    removeMediaRecorderClient(wp<MediaRecorderClient> client);
virtual sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid);
// House keeping for media player clients
virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url);
virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);

原来在这个地方会创建你sp对象。

以上就是Android音视频开发Media FrameWork框架源码解析的详细内容,更多关于Android音视频Media FrameWork的资料请关注我们其它相关文章!

(0)

相关推荐

  • android WakeLock使用方法代码实例

    Android中提供了一个名为WakeLock的类在android.os.PowerManager.WakeLock中,从名字来看WakeLock是唤醒锁的意思,它可以控制屏幕的背光开关,所以在电源管理类. WakeLock实例化方法比较简单,因为是系统的远程服务,通过下面的代码来构造 复制代码 代码如下: PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock

  • Android 内核代码 wake_up源码解析

    目录 内核中通常用法: wake_up 的源码: func 赋值过程 wait_queue_head 和 wait_queue_entry 数据结构 两种等待任务 wait_queue_entry:线程 和 函数 default_wake_function 函数 综上: 内核中通常用法: 内核有个函数 wake_up 和 wake_up_interruptible 通常来说看到这俩函数调用就是唤醒等待队列上的线程. 直到看了epoll的源码,发现并非如此. bool wakeup_conditi

  • Android应用程序保持后台唤醒(使用WakeLock实现)

    在使用一些产品列如微信.QQ之类的,如果有新消息来时,手机屏幕即使在锁屏状态下也会亮起并提示声音,这时用户就知道有新消息来临了.但是,一般情况下手机锁屏后,Android系统为了省电以及减少CPU消耗,在一段时间后会使系统进入休眠状态,这时,Android系统中CPU会保持在一个相对较低的功耗状态.针对前面的例子,收到新消息必定有网络请求,而网络请求是消耗CPU的操作,那么如何在锁屏状态乃至系统进入休眠后,仍然保持系统的网络状态以及通过程序唤醒手机呢?答案就是Android中的WakeLock机

  • 详解Android获取系统内核版本的方法与实现代码

    Android获取系统内核版本的方法 这里主要实现获取Android Linux 内核的版本号,网上关于这类文章不是很多,这里记录下,希望能帮助到大家, 实现代码: public static String getKernelVersion() { String kernelVersion = ""; InputStream inputStream = null; try { inputStream = new FileInputStream("/proc/version&q

  • Android源码解析onResume方法中获取不到View宽高

    目录 前言 问题1.为什么onCreate和onResume中获取不到view的宽高? 问题2.为什么View.post为什么可以获取View宽高? 结论 前言 有一个经典的问题,我们在Activity的onCreate中可以获取View的宽高吗?onResume中呢? 对于这类八股问题,只要看过都能很容易得出答案:不能. 紧跟着追问一个,那为什么View.post为什么可以获取View宽高? 今天来看看这些问题,到底为何? 今日份问题: 为什么onCreate和onResume中获取不到vie

  • Android音视频开发Media FrameWork框架源码解析

    目录 一.Media FrameWork背景 二.Media Framework“路线图” 2.1 代理端 2.2 服务端 2.2.1 Source 2.2.2 Decoder 2.2.3 Renderer 2.2.4 Foundation 2.3 OMX端 2.4 Kernel端 三.media播放的流程 四.Media FrameWork源码分析 一.Media FrameWork背景 Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频与视

  • Laravel框架源码解析之模型Model原理与用法解析

    本文实例讲述了Laravel框架源码解析之模型Model原理与用法.分享给大家供大家参考,具体如下: 前言 提前预祝猿人们国庆快乐,吃好.喝好.玩好,我会在电视上看着你们. 根据单一责任开发原则来讲,在laravel的开发过程中每个表都应建立一个model对外服务和调用.类似于这样 namespace App\Models; use Illuminate\Database\Eloquent\Model; class User extends Model { protected $table =

  • Laravel框架源码解析之入口文件原理分析

    本文实例讲述了Laravel框架源码解析之入口文件原理.分享给大家供大家参考,具体如下: 前言 提升能力的方法并非使用更多工具,而是解刨自己所使用的工具.今天我们从Laravel启动的第一步开始讲起. 入口文件 laravel是单入口框架,所有请求必将经过index.php define('LARAVEL_START', microtime(true)); // 获取启动时间 使用composer是现代PHP的标志 require __DIR__.'/../vendor/autoload.php

  • Laravel框架源码解析之反射的使用详解

    本文实例讲述了Laravel框架源码解析之反射的使用.分享给大家供大家参考,具体如下: 前言 PHP的反射类与实例化对象作用相反,实例化是调用封装类中的方法.成员,而反射类则是拆封类中的所有方法.成员变量,并包括私有方法等.就如"解刨"一样,我们可以调用任何关键字修饰的方法.成员.当然在正常业务中是建议不使用,比较反射类已经摒弃了封装的概念. 本章讲解反射类的使用及Laravel对反射的使用. 反射 反射类是PHP内部类,无需加载即可使用,你可以通过实例化 ReflectionClas

  • Android图片加载利器之Picasso源码解析

    看到了这里,相信大家对Picasso的使用已经比较熟悉了,本篇博客中将从基本的用法着手,逐步的深入了解其设计原理. Picasso的代码量在众多的开源框架中算得上非常少的一个了,一共只有35个class文件,但是麻雀虽小,五脏俱全.好了下面跟随我的脚步,出发了. 基本用法 Picasso.with(this).load(imageUrl).into(imageView); with(this)方法 public static Picasso with(Context context) { if

  • 使用C#开发OPC Server服务器源码解析

    目录 1.需要的DLL 2.添加引用 3.OPC Server 接口开发 5.测试 OPC Server服务器服务器的开发比较繁琐,本示例采用C#提供了一种简单快速实现OPCServer的方法,已经在工程项目中应用,希望对大家有用. 1.需要的DLL 首选将需要dll放置您的开发目录下,本示例放在工程目录下的bin\x86\debug目录下 需要的dll如下图: 2.添加引用 在VS的项目中添加对FKOPCSrvApi的引用 然后在源码文件中添加 using FKOPCSrvApi; 3.OPC

  • Android开发中线程池源码解析

    线程池(英语:thread pool):一种线程使用模式.线程过多会带来调度开销,进而影响缓存局部性和整体性能.而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务.这避免了在处理短时间任务时创建与销毁线程的代价.线程池不仅能够保证内核的充分利用,还能防止过分调度.可用线程数量应该取决于可用的并发处理器.处理器内核.内存.网络sockets等的数量. 例如,线程数一般取cpu数量+2比较合适,线程数过多会导致额外的线程切换开销.----摘自维基百科 我们在Android或者Java开发中

  • Android下拉刷新控件SwipeRefreshLayout源码解析

    SwipeRefreshLayout是Android官方的下拉刷新控件,使用简单,界面美观,不熟悉的朋友可以随便搜索了解一下,这里就不废话了,直接进入正题. 首先给张流程图吧,标出了几个主要方法的作用,可以结合着看一下哈. 这种下拉刷新控件的原理不难,基本就是监听手指的运动,获取手指的坐标,通过计算判断出是哪种操作,然后就是回调相应的接口了.SwipeRefreshLayout是继承自ViewGroup的,根据Android的事件分发机制,触摸事件应该是先传递到ViewGroup,根据onInt

  • Android实现3D推拉门式滑动菜单源码解析

    前言   又看了郭霖大神的一篇博客<Android 3D滑动菜单完全解析,实现推拉门式的立体特效>,是关于自定义控件方面的,因为自己关于自定义控件了解的不过,以前的要求是会用就行,但是后来越发的明白只会用是不够的,出现问题都不知道该怎么分析,所以我才打算把别人博客里的自定义控件的源码给看懂,虽然可能时间花的时间长,但是,绝对是值得的!   因为源码的东西比较多,看完之后发现还存在可以优化的地方,郭神的代码当时是为了例子讲解,所以对这个控件类的封装就没有仔细去做,所以我就进行了封装和优化,是的移

  • Android音视频开发之MediaExtactor使用教程

    目录 前言 MediaExtactor 使用MediaExtactor 加载音视频文件代码 获取轨道代码 提取轨道数据信息 一些源码细节分析 前言 在之前学习如何使用MediaPlayer后,了解到Android系统提供开发者播放多媒体全家桶能力,但对于开发者希望DIY自由度更高的播放器能力也是可以利用Android内部提供组件包自行实现一个播放器的.举例实现一个视频播放这个流程,它大致流程是[多媒体文件解析提取视频文件]-> [视频流解码]-> [解码数据播放渲染到Render].首要需要实

随机推荐