热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

AndroidFramework下StageFright框架流程解读

1、StageFright介绍Androidfroyo版本多媒体引擎做了变动,新添加了stagefright框架,并且默认情况android选择stag
1、    StageFright介绍

    Android froyo版本多媒体引擎做了变动,新添加了stagefright框架,并且默认情况android选择stagefright,并没有完全抛弃 opencore,主要是做了一个OMX层,仅仅是对 opencore的omx-component部分做了引用。stagefright是在MediaPlayerService这一层加入的,和 opencore是并列的。Stagefright在 Android中是以shared library的形式存在(libstagefright.so),其中的module -- AwesomePlayer可用来播放video/audio。 AwesomePlayer提供许多API,可以让上层的应用程序(Java/JNI)来调用。

2、    StageFright数据流封装
    2.1》由数据源DataSource生成MediaExtractor。通过MediaExtractor::Create(dataSource)来 实现。Create方法通过两步来生成相应的 MediaExtractor(MediaExtractor.cpp):
    通过dataSource->sniff探测数据类型
    生成相应的Extractor:
    if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4)
            || !strcasecmp(mime, "audio/mp4")) {
        return new MPEG4Extractor(source);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
        return new MP3Extractor(source, meta);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)
            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
        return new AMRExtractor(source);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {
        return new WAVExtractor(source);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {
        return new OggExtractor(source);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
        return new MatroskaExtractor(source);
    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
        return new MPEG2T***tractor(source);
    }

    2.2》把音视频轨道分离,生成mVideoTrackmAudioTrack两个MediaSource。代码如下(AwesomePlayer.cpp):
   if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
        setVideoSource(extractor->getTrack(i));
        haveVideo = true;
   } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
        setAudioSource(extractor->getTrack(i));
        haveAudio = true;
   }

    2.3》得到的这两个MediaSource,只具有parser功能,没有decode功能。还需要对这两个MediaSource做进一步的包装,获取了两个MediaSource(具有parse和decode功能):
mVideoSource = OMXCodec::Create(
            mClient.interface(), mVideoTrack->getFormat(),
            false, // createEncoder
            mVideoTrack,
            NULL, flags);
mAudioSource = OMXCodec::Create(
                mClient.interface(), mAudioTrack->getFormat(),
                false, // createEncoder
                mAudioTrack);
    当调用MediaSource.start()方法后,它的内部就会开始从数据源获取数据并解析,等到缓冲区满后便停止。在AwesomePlayer里就可以调用MediaSource的read方法读取解码后的数据
    对于mVideoSource来说,读取的数据:mVideoSource->read(&mVideoBuffer, &options)交给显示模块进行渲染,mVideoRenderer->render(mVideoBuffer);
    对mAudioSource来说,用mAudioPlayermAudioSource进行封装,然后由mAudioPlayer负责读取数据和播放控制

3、    StageFright的Decode
    经过“数据流的封装”得到的两个MediaSource,其实是两个OMXCodec。AwesomePlayer和mAudioPlayer都是从 MediaSource中得到数据进行播放。AwesomePlayer得到的是最终需要渲染的原始视频数据,而mAudioPlayer读取的是最终需 要播放的原始音频数据。也就是说,从OMXCodec中读到的数据已经是原始数据了。
    OMXCodec是怎么把数据源经过parse、decode两步以后转化成原始数据的。从OMXCodec::Create这个构造方法开始,它的参数:
    IOMX &omx指的是一个OMXNodeInstance对象的实例。
    MetaData &meta这个参数由MediaSource.getFormat获取得到。这个对象的主要成员就是一个 KeyedVector mItems,里面存放了一些代表MediaSource格式信息的名值对。
    bool createEncoder指明这个OMXCodec是编码还是解码。
    MediaSource &source是一个MediaExtractor。
    char *matchComponentName指定一种Codec用于生成这个OMXCodec。
    先使用findMatchingCodecs寻找对应的Codec,找到以后为当前IOMX分配节点并注册事件监听 器:omx->allocateNode(componentName, observer, &node)。最后,把IOMX封装进一个OMXCodec:
sp codec = new OMXCodec(
                    omx, node, quirks,
                    createEncoder, mime, componentName,
                    source);
这样就得到了OMXCodec。
    AwesomePlayer中得到这个OMXCodec后,首先调用mVideoSource->start()进行初始化。 OMXCodec初始化主要是做两件事:
    向OpenMAX发送开始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)
    调用allocateBuffers()分配两个缓冲区,存放在Vector mPortBuffers[2]中,分别用于输入和输出。
    AwesomePlayer开始播放后,通过mVideoSource->read(&mVideoBuffer, &options)读取数据。mVideoSource->read(&mVideoBuffer, &options)具体是调用OMXCodec.read来读取数据。而OMXCodec.read主要分两步来实现数据的读取:
    通过调用drainInputBuffers()对mPortBuffers[kPortIndexInput]进行填充,这一步完成 parse。由OpenMAX从数据源把demux后的数据读取到输入缓冲区,作为OpenMAX的输入。
    通过fillOutputBuffers()对mPortBuffers[kPortIndexOutput]进行填充,这一步完成 decode。由OpenMAX对输入缓冲区中的数据进行解码,然后把解码后可以显示的视频数据输出到输出缓冲区。
    AwesomePlayer通过mVideoRenderer->render(mVideoBuffer)对经过parse和decode 处理的数据进行渲染。一个mVideoRenderer其实就是一个包装了IOMXRenderer的AwesomeRemoteRenderer:
mVideoRenderer = new AwesomeRemoteRenderer(
                mClient.interface()->createRenderer(
                        mISurface, component,
                        (OMX_COLOR_FORMATTYPE)format,
                        decodedWidth, decodedHeight,
                                    mVideoWidth, mVideoHeight,
                        rotationDegrees));

4、    StageFright处理流程
    Audioplayer 为AwesomePlayer的成员,audioplayer通过callback来驱动数据的获取,awesomeplayer则是通过 videoevent来驱动。二者有个共性,就是数据的获取都抽象成mSource->Read()来完成,且read内部把parser和dec 绑在一起。Stagefright AV同步部分,audio完全是callback驱动数据流,video部分在onVideoEvent里会获取audio的时间戳,是传统的AV时间戳 做同步。

    4.1》AwesomePlayer的Video主要有以下几个成员:
    mVideoSource(解码视频)
    mVideoTrack(从多媒体文件中读取视频数据)
    mVideoRenderer(对解码好的视频进行格式转换,android使用的格式为RGB565)
    mISurface(重绘图层)
    mQueue(event事件队列)
    
    4.2》stagefright运行时的Audio部分抽象流程如下:
    设置mUri的路径
    启动mQueue,创建一个线程来运行 threadEntry(命名为TimedEventQueue,这个线程就是event调度器)
    打开mUri所指定的文件的头部,则会根据类型选择不同的分离器(如MPEG4Extractor)
    使用 MPEG4Extractor对MP4进行音视频轨道的分离,并返回MPEG4Source类型的视频轨道给mVideoTrack
    根据 mVideoTrack中的编码类型来选择解码器,avc的编码类型会选择AVCDecoder,并返回给mVideoSource,并设置mVideoSource中的mSource为mVideoTrack
    插入onVideoEvent到Queue中,开始解码播放
    通过mVideoSource对象来读取解析好的视频buffer
如果解析好的buffer还没到AV时间戳同步的时刻,则推迟到下一轮操作
    mVideoRenderer为空,则进行初始化(如果不使用 OMX会将mVideoRenderer设置为AwesomeLocalRenderer)
    通过mVideoRenderer对象将解析好的视频buffer转换成RGB565格式,并发给display模块进行图像绘制
    将onVideoEvent重新插入event调度器来循环

    4.3》数据由源到最终解码后的流程如下:
                URI,FD
                   |
            DataSource
                   |
          MediaExtractor
                    |
    mVideoTrack   mAudioTrack//音视频数据流
                   |
    mVideoSource   mAudioSource//音视频解码器
         |                      |
      mVideoBuffer    mAudioPlayer
说明:
    设置DataSource,数据源可以两种URI和FD。URI可以http://,rtsp://等。FD是一个本地文件描述符,能过FD,可以找到对应的文件。
    由DataSource生成MediaExtractor。通过sp extractor = MediaExtractor::Create(dataSource);来实现。 MediaExtractor::Create(dataSource)会根据不同的数据内容创建不同的数据读取对象。
    通过调用setVideoSource由MediaExtractor分解生成音频数据流(mAudioTrack)和视频数据流(mVideoTrack)。
    onPrepareAsyncEvent()如果DataSource是URL的话,根据地址获取数据,并开始缓冲,直到获取到mVideoTrack和 mAudioTrack。mVideoTrack和mAudioTrack通过调用initVideoDecoder()和 initAudioDecoder()来生成 mVideoSource和mAudioSource这两个音视频解码器。然后调用postBufferingEvent_l()提交事件开启缓冲。
    数据缓冲的执行函数是onBufferingUpdate()。缓冲区有足够的数据可以播放时,调用play_l()开始播放。play_l()中关键是 调用了postVideoEvent_l(),提交了 mVideoEvent。这个事件执行时会调用函数onVideoEvent()。这个函数通过调用 mVideoSource->read(&mVideoBuffer, &options)进行视频解码。音频解码通过mAudioPlayer实现。
    视频解码器解码后通过mVideoSource->read读取一帧帧的数据,放到mVideoBuffer中,最后通过 mVideoRenderer->render(mVideoBuffer)把视频数据发送到显示模块。当需要暂停或停止时,调用 cancelPlayerEvents来提交事件用来停止解码,还可以选择是否继续缓冲数据。

5、    代码标记Log
    依据第4》项StageFright描述的Vide视频播放流程,作Log标记跟踪视频DATA获取、CODEC过程。从AwesomePlayer.cpp中方法着手,步骤如下:
    在修改的/mydroid/frameworks/base/media/libstagefrigh/下,用mm编译,并调试直到生成相应的.so文件。注:允许单模块编译时,需事先在/mydroid下允许. ./build/envsetup.sh文件。
    在/mydroid/目录下make进行整体编译,生成system.img文件。说明:先单模块编译,后再整体编译的好处是,可以缩短调试编译的时间。
    将system.img文件copy到/android-sdk-linux/platforms/android-8/下。注意:事先备份原有的system.img。
    带sdcard启动模拟器,在/android-sdk-linux/tools/下运行./adb shell文件,再运行logcat
    打开Gallery选择视频文件运行,并同步查看log。
    
反馈结果如下:
I/ActivityManager(   61): Starting: Intent { act=android.intent.action.VIEW dat=content://media/external/video/media/5 typ=video/mp4 cmp=com.cooliris.media/.MovieView } from pid 327
I/RenderView(  327): OnPause RenderView com.cooliris.media.RenderView@4054a3b0
E/AwesomePlayer(   34): beginning AwesomePlayer... by jay remarked...
E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer(   34): ending AwesomePlayer... by jay remarked...
E/AwesomePlayer(   34): setting video source now... by jay remarked...
E/AwesomePlayer(   34): setting Video Type... by jay remarked...
E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...
E/AwesomePlayer(   34): beginning initVideoDecoder by jay remarked...
D/MediaPlayer(  327): getMetadata
I/ActivityManager(   61): Displayed com.cooliris.media/.MovieView: +1s761ms
E/AwesomePlayer(   34): beginning AwesomeLocalRenderer init ...by jay remarked...
E/AwesomePlayer(   34): returning open(libstagefrighthw.so) correctly by jay remarked...
E/MemoryHeapBase(   34): error opening /dev/pmem_adsp: No such file or directory
I/SoftwareRenderer(   34): Creating physical memory heap failed, reverting to regular heap.
E/AwesomePlayer(   34): ending AwesomeLocalRenderer init close ...by jay remarked...
E/AwesomePlayer(   34): returning AwesomeLocalRenderer...by jay remarked...
I/CacheService(  327): Starting CacheService


推荐阅读
  • 在Android平台中,播放音频的采样率通常固定为44.1kHz,而录音的采样率则固定为8kHz。为了确保音频设备的正常工作,底层驱动必须预先设定这些固定的采样率。当上层应用提供的采样率与这些预设值不匹配时,需要通过重采样(resample)技术来调整采样率,以保证音频数据的正确处理和传输。本文将详细探讨FFMpeg在音频处理中的基础理论及重采样技术的应用。 ... [详细]
  • 本文详细介绍了 com.apollographql.apollo.api.internal.Optional 类中的 orNull() 方法,并提供了多个实际代码示例,帮助开发者更好地理解和使用该方法。 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • 深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案
    深入剖析Java中SimpleDateFormat在多线程环境下的潜在风险与解决方案 ... [详细]
  • 在Android应用开发中,实现与MySQL数据库的连接是一项重要的技术任务。本文详细介绍了Android连接MySQL数据库的操作流程和技术要点。首先,Android平台提供了SQLiteOpenHelper类作为数据库辅助工具,用于创建或打开数据库。开发者可以通过继承并扩展该类,实现对数据库的初始化和版本管理。此外,文章还探讨了使用第三方库如Retrofit或Volley进行网络请求,以及如何通过JSON格式交换数据,确保与MySQL服务器的高效通信。 ... [详细]
  • PHP 5.5.31 和 PHP 5.6.17 安全更新发布
    PHP 5.5.31 和 PHP 5.6.17 已正式发布,主要包含多个安全修复。强烈建议所有用户尽快升级至最新版本以确保系统安全。 ... [详细]
  • 兆芯X86 CPU架构的演进与现状(国产CPU系列)
    本文详细介绍了兆芯X86 CPU架构的发展历程,从公司成立背景到关键技术授权,再到具体芯片架构的演进,全面解析了兆芯在国产CPU领域的贡献与挑战。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
  • 高端存储技术演进与趋势
    本文探讨了高端存储技术的发展趋势,包括松耦合架构、虚拟化、高性能、高安全性和智能化等方面。同时,分析了全闪存阵列和中端存储集群对高端存储市场的冲击,以及高端存储在不同应用场景中的发展趋势。 ... [详细]
  • MySQL初级篇——字符串、日期时间、流程控制函数的相关应用
    文章目录:1.字符串函数2.日期时间函数2.1获取日期时间2.2日期与时间戳的转换2.3获取年月日、时分秒、星期数、天数等函数2.4时间和秒钟的转换2. ... [详细]
  • Hadoop的文件操作位于包org.apache.hadoop.fs里面,能够进行新建、删除、修改等操作。比较重要的几个类:(1)Configurati ... [详细]
  • JUC(三):深入解析AQS
    本文详细介绍了Java并发工具包中的核心类AQS(AbstractQueuedSynchronizer),包括其基本概念、数据结构、源码分析及核心方法的实现。 ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
author-avatar
mobiledu2502855757
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有