Android
源码版本:android-12.0.0_r3
Android
源码来源:https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest
Android
源码编译配置: aosp_crosshatch-userdebug
Google Pixel 3 XL (crosshatch)
SP1A.210812.016.A1
Ubuntu 22.04 LTS
Codeless
表示尽可能少的代码.
Android Open Source Project
的开源代码, 因此本文的内容也可随意分发媒体文件解码流程:
媒体文件 --> |--> 视频流 -> 解码 --| | --> 视频渲染
| |--> 同步 --|
└--> 音频流 -> 解码 --| | --> 音频渲染
媒体文件在 Android 系统回放时设计的进程:
media.extractor -> mediaserver --| --> |media.swcodec
| <-- |
| --> surfaceflinger
| --> audioserver
至此已经掌握音视频所有的媒体文件播放流程了, 为方便理解本文的内容,提供两张图片供参考。
MediaPlayer开始播放时的基本流程:
MediaPlayer开始播放时的数据流向:
加粗的线条为数据在各类对象、进程见的流向。注: 由于水平有限,时序图和类图可能存在小的错误或缺失,请谅解。
MediaPlayer
: 播放器, 泛指Java
层和Native(C++)
层的播放器media.extractor
中
MediaExtractorService
: 实现IMediaExtractorService
接口, 对外提供创建解封装的接口DataSource
: 描述一个数据源头, 一个文件, 一个流等, 通常以IDataSource
向外提供数据源的操作接口,有以有以下几种实现:
DataURISource
FileSource
: 数据来自文件HTTPBase
: 数据来自HTTP
链接NuCachedSource2
MediaExtractor
: 表示一个解封装器, 其通过IMediaExtractor
的实现RemoteMediaExtractor
对外提供服务, 其子类实现为MediaExtractorCUnwrapper
, 这是一个CMediaExtractor
的Wrapper
, 该类其实是MPEG4Extractor
对外的抽象MediaTrack
: 表示一个未解码的流, 通常以IMediaSource
的实现RemoteMediaSource
对外提供接口, 其子类实现为MediaTrackCUnwrapper
, 这是一个CMediaTrack
的Wrapper
, 该类其实是MPEG4Source
对外的抽象mediaserver
进程中
MediaPlayerService
: 播放器服务, 运行在mediaserver
中, 以IMediaPlayerService
接口提供服务MediaPlayerService::Client
: 每个请求播放器服务的进程在MediaPlayerService
中的描述MediaPlayerFactory
: 用于创建播放器实例的工厂类, 它负责通过实现IFactory
接口的工厂类创建播放器NuPlayerFactory
: NuPlayerDriver
的工厂类NuPlayerDriver
: 播放器实例的驱动类, 负责部分同步操作到NuPlayer
异步的转换NuPlayer
: MediaPlayerService
中实际的播放器实例, 负责完成播放相关的大部分异步操作NuPlayer::Source
: 表示一个来源, 来源可能有很多种:
NuPlayer::GenericSource
: 来源是本地文件, 其引用:
TinyCacheSource
: 通过TinyCacheSource
(DataSource
)::mSource
-> CallbackDataSource
(DataSource
)访问IDataSource
接口IMediaSource
: 被成员mVideoSource
和mAudioSource
所引用NuPlayer::StreamingSource
: 来源是一个流NuPlayer::HTTPLiveSource
: 来源是一个HTTP
直播流NuPlayer::RTSPSource
: 来源是一个RTSP
流NuPlayer::RTPSource
: 来源是一个RTP
流NuPlayer::Decoder
: NuPlayer
的解码器对象, 有NuPlayer::Decoder
和NuPlayer::DecoderPassThrough
两种实现, 本例只关注前者MediaCodec
: 实际的解码器接口, 其负责将部分上层的同步操作转换为异步操作, 其引用一个CodecBase
CodecBase
: 表示一个解码器实例, 其引用一个CodecBase
, 通常有ACodec
, CCodec
, MediaFilter
几种实现:
CCodec
: 采用 Codec 2 框架的解码器实现ACodec
: 采用 OMX 框架的解码器实现CodecCallback
: 用于响应解码器的消息, 由``CodecBas`的实现BufferCallback
: 用于响应缓冲区队列的消息, 由BufferChannelBase
的实现回调该接口BufferChannelBase
: 负责处理所有解码相关的输入输出缓冲区队列管理, 其有两个实现
CCodecBufferChannel
: Codec 2 框架实现缓冲区管理的实现ACodecBufferChannel
: OMX 框架实现缓冲区管理的实现MediaCodecBuffer
用于描述一个解码器使用的缓冲区, 其有几种扩展实现
Codec2Buffer
Codec 2 框架下的缓冲区描述, 该类有多种实现:
LocalLinearBuffer
: 本地线性缓存, 可写入DummyContainerBuffer
: 空缓存, 在解码器没有配置Surface
且应用试图获取数据时返回空缓存LinearBlockBuffer
: 可写入的线性块缓存, 一般提供一个写入视图C2WriteView
, 同样引用一个线性数据块C2LinearBlock
(父类为C2Block1D
)ConstLinearBlockBuffer
: 只读的线性块缓存, 一般提供一个读取试图C2ReadView
, 同样引用一个C2Buffer
(子类为LinearBuffer
)作为缓冲区的描述GraphicBlockBuffer
: 图形数据块缓存描述, 一般提供一个视图C2GraphicView
(用于写入), 同样引用一个C2GraphicBlock
(父类为C2Block2D
)GraphicMetadataBuffer
: 图形元数据数据块,ConstGraphicBlockBuffer
: 只读图形数据块, 其提供一个视图C2GraphicView
(用于读取), 同样引用一个C2Buffer
或ABuffer
EncryptedLinearBlockBuffer
: 加密线性数据块SharedMemoryBuffer
: 共享内存方式SecureBuffer
: 安全缓冲区Codec2Client
: 负责通过HIDL
完成到 Codec 2 解码组件库所在进程meida.codec
或media.swcodec
的各种请求Codec2Client::Component
: 负责通过HIDL
完成到 Codec 2 解码组件库所在进程meida.codec
或media.swcodec
的各种请求Codec2Client::Interface
: 为组建接口, 其引用一个远程的IComponentInterface
接口, 它集成自Codec2Client::Configurable
, 意为可配置Codec2Client::Listener
: 负责监听来自组件HIDL
接口的消息, 其有一个实现CCodec::ClientListener
, 负责通知CCodec
Component::HidlListener
: 负责监听来自组件的消息, 其实现了IComponentListener
接口, 有消息产生后通过Codec2Client::Listener
通知CCodec
Renderer
: 渲染器NuPlayerAudioSink
: 音频输出, 其实现: AudioOutput
AudioTrack
: 音频输出流, 通过IAudioTrack
访问audioserver
中的TrackHandle
media.swcodec
中(本文以软解为例)
ComponentStore
: 组件库, 运行与media.codec
和media.swcodec
两个进程, 通过IComponentStore
接口提供 Codec 2 框架的解码器实现android::hardware::media::c1::V1_2::Component
为 CCodec 2 框架解码器组件对于HIDL
的实现, 其通过IComponent
向其它进程的Codec2Client::Component
提供服务, 后端实现为C2Component
C2Component
为 CCodec 2 框架解码器组的实现, 其有多种具体的实现:
V4L2DecodeComponent
: V4L2
解码组件V4L2EncodeComponent
: V4L2
编码组件SimpleC2Component
: Google 实现的软解组件, 简单列出几种实现:
C2SoftAvcDec
C2SoftHevcDec
C2SoftVpxDec
C2SoftFlacDec
C2SoftAacDec
SampleToneMappingFilter
: ??WrappedDecoder
: ??android::hardware::media::c1::V1_2::utils::ComponentInterface
: 组件接口类, 负责描述一个 Codec 2 组件的各种信息, 其通过IComponentInterface
向对端(Codec2Client
)提供查询服务, 该接口类也被Component
所引用, 后端实现为C2ComponentInterface
C2Component::Listener
: 为组件中, 客户端的回调实现, 其持有一个IComponentListener
接口, 用于通知客户端组件的消息, 其有一个实现: Component::Listener
, 持有父类的IComponentListener
接口C2Work
: 表示一个解码工作(播放器中)C2FrameData
: 表示一个帧的数据, 其中有一组C2Buffer
C2Buffer
: 一个缓冲区的描述, 其包含其数据的描述C2BufferData
C2BufferData
: 描述一个缓冲区的数据, 以及它包含的块C2Block[1|2]D
C2Block1D
: 描述一个一维的缓冲区, 有如下实现:
C2LinearBlock
: 可写一个线性缓冲区C2ConstLinearBlock
: 只读线性缓冲区C2CircularBlock
: 环形缓冲区(环形缓冲区是"首尾相接"的线性缓冲区)C2Block2D
: 描述一个二维的缓冲区有如下实现:
C2GraphicBlock
: 描述一个二维图形缓冲区C2ConstGraphicBlock
: 描述一个只读的图形缓冲区(本例不涉及)MediaPlayer
MediaPlayer
(Java)对象有自己的本地方法, 其位于frameworks/base/media/jni/android_media_MediaPlayer.cpp
中, 这些方法均以android_media_MediaPlayer_
开头, 因此"native_init"
对应android_media_MediaPlayer_native_init()
.
MediaPlayer
在构造时会做两件事情:
libmedia_jni.so
并执行native_init()
, 这个步骤只获取MediaPlayer
类相关的一些信息, 并不会初始化 C++ 对象native
方法native_setup()
接下来被调用, 这个步骤负责实例化一个MediaPlayer
(C++)类, 并生成一个集成自MediaPlayerListener
的JNIMediaPlayerListener
用于监听来自播放器的消息. 新创建的MediaPlayer
(C++)对象将被保存在MediaPlayer
(Java)的mNativeContext
中用于后续的下行调用.MediaPlayer
的初始化比较简单, 只有设置数据源之后才能开始 解封装 / 解码 / 渲染 等的工作.数据源是媒体的数据来源, 可能来自一个文件, 可能来自一个网络流. 媒体源是数据源中的一个未解码的流, 例如视频流 / 音频流 / 字幕流等.
在 Android Multimedia中主要以IMediaSource
接口体现(和MediaSource
不同, MediaSource
用于描述一个未编码的媒体流). 该类别通常针对一个具体的类型, 比如一个符合VP9
编码规范的数据源, 从该数据源读取的数据应是编码过的数据.
通常一个媒体文件中会包含很多个部分:
VP9
, H264
, H265
等PCM
, G711
, FLAC
, APE
, AAC
等MP4
, 本文的视频封装以及音视频编码参考信息: Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2mp41
encoder : Lavf58.29.100
Duration: 00:00:01.75, start: 0.000000, bitrate: 291 kb/s
Stream #0:0(eng): Video: vp9 (Profile 0) (vp09 / 0x39307076), yuv420p(tv, progressive), 1080x1920, 216 kb/s, 30.13 fps, 30.13 tbr, 90k tbn, 90k tbc (default)
Metadata:
handler_name : VideoHandle
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, mono, fltp, 73 kb/s (default)
Metadata:
handler_name : SoundHandle
接下来播放器要通过MeidaExtractor
(最终的工作位于media.extractor
中)找到响应的数据源. 那么首先从封装信息中确定音视频Track
数量, 其对应的编码格式, 然后再根据每条具体的Track
构造IMediaSource
(媒体源).
MediaPlayer.setDataSource
的Java部分过程比较复杂, 涉及ContentResolver
, 本文不讨论, 对于本地文件, 其最终配置到底层的android_media_MediaPlayer_setDataSourceFD()
-> MediaPlayer::setDataSource(int fd,...)
, 此时一个问题出现了:
MediaPlayer
是本地播放的么? 并不是, 它请求远程服务完成播放, 执行播放任务的是MediaPlayerService
, 该服务运行在mediaserver
中, 那么mediaserver
中构造的播放器是什么? 接下来一起看看MediaPlayer::setDataSource(int fd,...)
时发生了什么? 首先我们需要了解一个重要的服务: MediaPlayerService
.
MediaPlayerService
mediaserver
创建IMediaPlayerService
接口的MediaPlayerService
实现, 该类将处理来自MediaPlayer
的请求. 在MediaPlayerService
创建时, 会注册多个MediaPlayerFactory::IFactory
实现, 目前主要有两种:
NuPlayerFactory
: 主要的工厂类, 用于创建播放器实例: NuPlayerDriver
, 其更底层的实现是NuPlayer
TestPlayerFactory
: 测试目的, 不关注AwesomePlayer
(被stagefrightplayer
封装), 已经过时了.MediaPlayer
设置数据源之前要先完成实际的播放器实例的创建, 它通过IMediaPlayerService
接口向MediaPlayerService
服务申请创建播放器, 创建播放器后, 本地的MediaPlayer
将通过IMediaPlayer
接口引用服务器为其创建的播放器实例. 显然该Client
实现了BnMediaPlayer
并在创建后返回给应用, 它将作为一个引用传递给MediaPlayer
并作为后续所有的请求的代理, setDataSource()
也在其中.MediaPlayerService
要具备通知MediaPlayer
的能力才行, 后者实现了BnMediaPlayerClient
, 将通过IMediaPlayerClient
在创建Client
时被设置在Client
的mClient
中Client
是也创建了MediaPlayerService::Listener
, 该类是继承自MediaPlayerBase::Listener
, 显然该Listener
将负责从底层的MediaPlayerBase
监听播放时的各种消息, 从这里, 也知道了在MediaPlayerService
中, 负责播放任务的实现是集成自MediaPlayerBase
的, 本例中的继承: MediaPlayerBase
-> MediaPlayerInterface
-> NuPlayerDriver
, Listener
本身持有了Client
的引用, 因此Listener::notify()
将通知到Client::notify()
, 而这时调用上文的MediaPlayerService::Listener
的notify()
将完成通过IMediaPlayerClient
完成对对端MediaPlayer
的通知(见附录: MediaPlayer
将收到的通知类型).NuPlayer
Client
负责响应来自MediaPlayer
的请请求, 现在Client
已经创建, MediaPlayer
该通过IMediaPlayer
接口通过它发起setDataSource()
操作了, 这里分两个步骤:
NuPlayerDriver
NuPlayerDriver
执行setDataSource()
NuPlayerDriver
, 这将在MediaPlayerService::Client
响应createPlayer()
消息时通过MediaPlayerFactory::createPlayer()
静态方法从NuPlayerFactory
构建.NuPlayerDriver
, 但该类会马上创建NuPlayer
类.NuPlayer
后续则会通过MediaPlayerService::Client
-> NuPlayerDriver
响应来自应用中MediaPlayer
的很多事件.NuPlayer
最终完成所有播放请求, 请求的类型很多,我们只讨论传统本地视频文件的播放,关注以下几种类型:kWhatSetDataSource
: 设置数据源头MediaPlayer.setDataSource()
, 支持各种各样的数据源, 例如: URI/文件/数据源等等kWhatPrepare
: 准备播放器MediaPlayer.prepare()
kWhatSetVideoSurface
: 设置视频输出MediaPlayer.setDisplay()
或者MediaPlayer.setSurface()
, 它们的参数不同kWhatStart
: 开始播放MediaPlayer.start()
kWhatSeek
: seek操作MediaPlayer.seekTo()
, 该方法可以设置seek
(跳转到)的方式, seek
时需要的参数:
"seekTimeUs"
: seek
的目标事件, 单位us
"mode"
: seek
的模式(向前/向后/最近等)"needNotify"
: 是否需要通知上层, 如果需要, NuPlayer
将通过父类MediaPlayerInterface
的sendEvent()
方法通知上层.kWhatPause
: 暂停操作kWhatResume
: 恢复播放NuPlayer
不但需要负责完成各种下行的控制, 还要通过AMessage
响应来自底层的一系列消息(见附录).NuPlayer
在创建完成后会保存在NuPlayerDriver
的mPlayer
中, 而NuPlayerDriver
作为MediaPlayerInterface
(父类MediaPlayerBase
)被Client
的mPlayer
引用, 因此总结MediaPlayer
(Java) -> MediaPlayer
(C++) --[binder
]–> [IMediaPlayer
=> MediaPlayerService::Client
] -> NuPlayerDriver
-> NuPlayer
NuPlayer
-> [MediaPlayerBase::Listener
=> MediaPlayerService::Client::Listener
] -> MediaPlayerService::Client
--[binder
]–> [IMediaPlayerClient
=> MediaPlayer
] -> [MediaPlayerListener
=> JNIMediaPlayerListener
] -> MediaPlayer
(Java)NuPlayer::setDataSourceAsync(int fd, ...)
在(NuPlayer::setDataSourceAsync(int fd, ...)
被转换为异步处理)如何处理接下来的工作呢?, 数据类型如果是文件文件描述符, 则创建GenericSource
(实现自:NuPlayer::Source
), 除了该类型, 对于NuPlayer::Source
还有几种主要类型:
StreamingSource
HTTPLiveSource
RTSPSource
RTPSource
GenericSource
GenericSource
创建后直接配置数据源就可以了, 数据源被创建后是否开始解析数据文件呢? 没有, 这部分工作将在MediaPlayer.prepare()
时开始.MediaPlayer.prepare(...)
最终都是通过MediaPlayer::prepare()
完成工作的, 而最后也都是通过MediaPlayer::prepareAsync_l()
--[Binder]–> Client::prepareAsync()
-> NuPlayerDriver::prepareAsync()
-> NuPlayer::prepareAsync()
, 既然是异步, 所以NuPlayer
给自己的异步线程发送了kWhatPrepare
消息, 上文说到, GenericSource
不会开始解析文件, 知道prepare()
开始, 此处NuPlayer
也确实在prepare()
时只调用了GenericSource::prepareAsync()
, 同样GenericSource
通过kWhatPrepareAsync
异步处理这个消息.
MediaPlayerService
中数据源IDataSource
的创建Android 中, 原则上都是通过MediaExtractorService
处理, MediaExtractorService
运行在media.extractor
进程中, 其通过IMediaExtractorService
为其它进程提供服务.
需求方GenericSource
通过IMediaExtractorService::makeIDataSource()
请求创建数据源, 提供了文件描述符, MediaExtractorService
通过工厂类DataSourceFactory
完成从文件描述符到DataSource
的创建, 但DataSource
本身不是继承自IDataSource
接口, 无法为需求方提供服务, 因此DataSource
最终还是要通过RemoteDataSource
, 而RemoteDataSource
继承自BnDataSource
响应后续对端的请求. 对于本地文件DataSourceFactory
创建的DataSource
是FileSource
.
IDataSource
接口通过Binder
从MediaExtractorService
返回给应用后通过TinyCacheSource
(DataSource
)::mSource
-> CallbackDataSource
(DataSource
)::mIDataSource
引用.
MediaExtractorService
中数据源探测前的准备数据源有了, 那么需要从数据源中接封装出媒体流, 它可能是音频/视频/字幕等, 这个过程需要 对数据源中的数据进行解封装, 找到各种媒体数据所对应的流, MediaExtractorFactory::Create()
仍然是本地工作的, 它负责通过IMediaExtractorService::makeExtractor()
向MediaExtractorService
请求创建IMediaExtractor
过程, 对应的服务端实现是:MediaExtractorService::makeExtractor()
.
但是这这个过程中, 上文的TinyCacheDataSource
作为DataSource
通过CreateIDataSourceFromDataSource()
转换成了IDataSource
接口的RemoteDataSource
又发回给MediaExtracotrService
了?
不是的, 在RemoteDataSource::wrap()
不一定创建实现新的IDataSource
的RemoeDataSource
, 如果传入的DataSource
本身及持有IDataSource
, 那就直接返回了, 没有重新创建的必要, 所以返回的仍然是TinyCacheSource
(DataSource
)::mSource
-> CallbackDataSource
(DataSource
)::mIDataSource
所保存的来自MediaExtractor
的IMediaSource
.
请求发给服务端MediaExtractorService
, 又会被如何处理呢? 这里仍然是通过CreateDataSourceFromIDataSource()
创建了本地的DataSource
, 这和上文应用中的操作完全一样? 是的, 完全一样, 最后本地曾经创建过的RemoteDataSource
(IDataSource
接口)也是被MediaExtractorService
本地的TinyCacheSource
(DataSource
)::mSource
-> CallbackDataSource
(DataSource
)::mIDataSource
所引用.
MediaExtractorService
将通过MediaExtractorFactory
的CreateFromService()
方法完成MediaExtractor
的创建, 从名字可以看到创建自服务端, 和上文MediaExtractorFactory::Create()
不一样了.
创建具体的MediaExtractor
之前, 需要从DataSource
中读取一些数据, 然后对读取到的数据机型探测. 在继续之前先了解Extractor
的插件加载.
media.extractor
在启动时, 创建MediaExtractorService
服务, MediaExtractorService
实例化是通过MediaExtractorFactory::LoadExtractors()
装载插件.
MediaExtractorFactory
首先通过RegisterExtractors()
, 它完成单个路径下的所有插件的加载, 例如"/apex/com.android.media/lib64/extractors/"
, 通常情况下形如lib[aac|amr|flac||midi|mkv|mp3|mp4|mpeg2|ogg|wav]extractor.so
, 对于本例, 关注libmp4extractor.so
, 首先从动态库中寻找"GETEXTRACTORDEF"
符号, 因此getDef()
就是GETEXTRACTORDEF
函数, 函数被调用后, 返回一个ExtractorDef
被用于构造ExtractorPlugin
, ExtractorPlugin::def
将在上文提到的sniff()
函数中被获取. 而RegisterExtractor()
为插件的注册. 最终插件的MPEG4Extractor.cpp:Sniff()
函数将保存在:MediaService::gPlugins[...].m_ptr.def.u.v3.sniff
中等待后续被调用.
MediaExtractorFactory::CreateFromService()
通过sniff()
函数主要完成DataSource
中流媒体文件格式的探测工作, 这将调用上文的MPEG4Extractor.cpp:Sniff()
, 如果MPEG4Extractor.cpp:Sniff()
判定为是自己能解析的格式, 则返回MPEG4Extractor.cpp:CreateExtractor()
用于后续接封装器的创建. MediaExtractorFactory::CreateFromService()
中, ((CreatorFunc)creator)(source->wrap(), meta)
将调用该函数. 该函数创建解封装器之前, TinyCacheSource
通过其父类DataSource
被wrap()
成了CDataSource
, 其被DataSourceHelper
引用, 供MPEG4Extractor
创建时使用. MPEG4Extractor
在被构造后, 也通过父类MediaExtractorPluginHelper
的wrap()
包装为CMediaExtractor
给MediaExtractorFactory
进一步封装为MediaExtractorCUnwrapper
(父类MediaExtractor
), 而MediaExtractorCUnwrapper
最终通过RemoteMediaExtractor
包装, 最后作为IMediaExtractor
返回给mediaserver
总结:
TinyDataSource
被设置到了:MediaExtractorService::makeExtractor()
中的extractor->mExtractor->plugin->data->mDataSource->mSource->handle
其中:
extractor
: IMediaExtractor
-> RemoteMediaExtractor
extractor->mExtractor
: MediaExtractor
-> MediaExtractorCUnwrapper
extractor->mExtractor->plugin
: CMediaExtractor
extractor->mExtractor->plugin->data
: void *
-> MediaExtractorPluginHelper
-> MPEG4Extractor
extractor->mExtractor->plugin->data->mDataSource
: DataSourceHelper
extractor->mExtractor->plugin->data->mDataSource->mSource
: CDataSource
extractor->mExtractor->plugin->data->mDataSource->mSource->handle
: DataSource
-> TinyDataSource
MetaData
的获取解封装器IMediaExtractor
返回给MediaPlayerService
后, 可以开始获取元数据了, 包括有多少条Track等等, IMediaExtractor::getMetaData()
负责完成到RemoteMediaExtractor
的请求.
MPEG4Extractor::readMetaData()
比较复杂, 放弃分析, 该函数负责将获取到的元数据保存在MPEG4Extracotr
的mFileMetaData
(类型为AMediaFormat
)成员中, 该信息将在MPEG4Extractor::getMetaData)
函数中通过AMediaFormat_copy()
方法拷贝到调用方MediaExtractorCUnwrapper::getMetaData()
的临时变量format
中.
在MediaExtractorCUnwrapper::getMetaData()
函数中, 获取到的AMediaFormat
需要通过convertMessageToMetaData()
函数转化到MetaData
类型, 此处过程较长, 本文不分析.
MetaData
通过binder
返回给mediaserver
时是通过``MetaDataBase::writeToParcel()完成序列化的, 不文也不分析该过程.
获取元数据后, 获取Track
的数量, 通过接口IMediaExtractor::countTracks()
完成请求, 这里略去.
IMediaSource
Track
的获取通过如下过程: IMediaExtractor::getTrack(size_t index)
--[Binder]–> RemoteMediaExtractor::getTrack()
-> MediaExtractorCUnwrapper::getTrack()
-> MPEG4Extractor::getTrack()
, 此时MPEG4Source
被创建, 其实现是MediaTrackHelper
, 类似的, 它也通过MediaTrackHelper::wrap()
被包装为CMediaTrack
, 由MediaTrackCUnwrapper
引用, 而MediaTrackCUnwrapper
被RemoteMediaSource
引用, RemoteMediaSource
作为IMediaSource
返回给MediaPlayerService
, 该过程和上文返回IMediaExtractor
的过程是一样的.
总结:
MPEG4Source
作为MediaTrackHelper
被设置在: RemoteMediaSource.mTrack->wrapper->data
, 其中:
RemoteMediaSource.mTrack
: MediaTrackCUnwrapper
RemoteMediaSource.mTrack->wrapper
: CMediaTrack
RemoteMediaSource.mTrack->wrapper->data
: MediaTrackHelper
-> MPEG4Source
媒体元也有源数据信息, 标记了该媒体源的编码类型等: 通过接口IMediaExtractor::getTrackMetaData()
完成请求.
最后IMediaSource
被保存到GenericSource
的mVideoSource
或者mAudioSource
(类型为GenericSource::Tracks
)的mSource
成员中, 后续将用于音频/视频流数据的获取.
当GenericSource
的准备工作完成后, 相应的媒体源也已经获取到, 则开始这些媒体源的工作, 这是会创建一个BufferGroup
, 用户缓冲数据等, 调用的顺序: GenericSource::startSources()
--[Binder]-> IMediaSource::startSources()
=> RemoteMediaSource::start()
-> MediaTrackCUnwrapper::start()
-> MediaBufferGroup::MediaBufferGroup()
-> CMediaBufferGroup::CMediaBufferGroup()
, 在媒体源开始后, CMediaBufferGroup
完成对MediaBufferGroupHelper
的创建.
总结:
MPEG4Source.mBufferGroup
: MediaBufferGroupHelper
MPEG4Source.mBufferGroup->mGroup
: CMediaBufferGroup
MPEG4Source.mBufferGroup->mGroup->handle
: MediaBufferGroup
媒体源开始工作后, GenericSource
即刻开始从媒体源读取数据.该读取过程是异步的, GenericSource
给其异步线程发送了kWhatReadBuffer
消息, 异步线程读取数据的调用过程为: GenericSource::onReadBuffer()
-> GenericSource::readBuffer()
-> IMediaSource::readMultiple()
--[Binder]–> BnMediaSource::onTransact()
=> RemoteMediaSource::read()
-> MediaTrackCUnwrapper::read()
-> MPEG4Source::read()
-> MediaBufferGroupHelper::acquire_buffer()
-> MediaBufferGroup::acquire_buffer()
-> MediaBuffer::MediaBuffer()
-> MemoryDealer::allocate()
.
上述过程, MediaBuffer
根据其size
的要求, 自行确定了是否使用共享内存的方式创建, 创建完成后, 数据指针被保存到其自身的mData
成员中, 创建完成后MediaBuffer
被封装到newHelper->mBuffer->handle
中返回给上层
在CMediaBufferGroup::acquire_buffer()
中, newHelper
:
newHelper
: MediaBufferHelper
newHelper->mBuffer
: CMediaBuffer
newHelper->mBuffer->handle
: MediaBufferBase
-> MediaBuffer
对于上述过程的最后一个函数, 也就是MediaBufferGroup::acquire_buffer()
中, 只有for (auto it = mInternal->mBuffers.begin(); it != mInternal->mBuffers.end(); ++it)
没有找到合适的buffer
, 才会申请新的buffer
至此, 可以知道mediaserver
所获取到的数据结构即MediaBufferBase
BnMediaSource::onTransact()
是循环通过RemoteMediaSource::read()
读取到MediaBuffer
的, 读取后判断解析出来的MediaBuffer
, 分两种情况:
MediaBuffer
能用binder
传递, 直接到最后一个else
的位置通过reply->writeByteArray()
写入数据到binder
MediaBuffer
不能通过binder
传递, 这里又分两种情况:
MediaBuffer
未使用共享内存, 此时抱怨一下, 然后从RemoteMediaSource
的父类BnMediaSource
所持有的MediaBufferGroup
中分配一个共享内存的MediaBuffer
, 然后获取解码器返回的数据, 拷贝到新分配的共享内存中MediaBuffer
使用的为共享内存, 则直接向后传递, 传递到后面, 如果是共享内存还分两种情况:
MediaBuffer
中的IMemory
是否有缓存在BnMediaSource
的mIndexCache
(类型为IndexCache
)中, 如果没有, mIndexCache.lookup()
返回的index
就是0
, 所以插入到缓存当中, 等待后续获取.MediaPlayerService
的数据可能是ABuffer
也可能是IMemory
所创建的ABuffer
, 那我们看看MediaPlayerService
读取数据完成后, 是如何通过IMediaSource
的实现BpMediaSource
处理的.BpMediaSource
根据返回的类型判断, 如果是IMemory
的缓冲, 则构造了RemoteMediaBufferWrapper
(其继承关系:RemoteMediaBufferWrapper
-> MediaBuffer
-> MediaBufferBase
), 如果是ABuffer
的类型, 那就直接构造一个ABuffer
.NuPlayer::GenericSource::readBuffer()
将通过mediaBufferToABuffer()
从MediaBufferBase
(类型可能为RemoteMediaBufferWrapper
或者MediaBuffer
)的data()
返回的指针, 然后构造(注意不是拷贝)一个新的ABuffer
, 并将ABuffer
插入GenericSource
的track->mPackets
(音频/视频).IMediaSource
中读取到的数据合适被读取呢? 它们将在NuPlayer::Decoder::fetchInputData()
是, NuPlayer::Decoder
通过GenericSource::dequeueAccessUnit()
被提取.系统相册在播放视频时会创建一个SurfaceView
, 该类在构造是通过其Java
层: updateSurface()
-> createBlastSurfaceControls()
构造了BLASTBufferQueue
类, 此时会触发Native
层构造BLASTBufferQueue
, 该过程将创建一对消费这和生产者:
IGraphicBufferProducer
=> BufferQueueProducer
=> BBQBufferQueueProducer
IGraphicBufferConsumer
=> BufferQueueConsumer
=> BufferQueueConsumer
updateSurface()
过程, 通过copySurfac()
方法构造Surface
(Java
层), 构造的方式是:Surface.copyFrom()
, 这将通过底层的BLASTBufferQueue::getSurface()
获取一个Native
的Surface
, 而BLASTBufferQueue
的生产者将被记录在这个Surfac
中.MediaPlayer.setDisplay()
-> MediaPlayer._setVideoSurface()
-> android_media_MediaPlayer_setVideoSurface()
-> MediaPlayer::setVideoSurfaceTexture()
.android_view_Surface_getSurface()
将上层的Surface
(Java)转换为底层的Surface
(Native), 然后将该Surface
(Native)指针记录在MediaPlayer.mNativeSurfaceTexture
(Java)中, 最后通过mp->setVideoSurfaceTexture()
也就是MediaPlayer::setVideoSurfaceTexture()
设置从Surface
(Native)调用getIGraphicBufferProducer()
获得的IGraphicBufferProducer
, 这个IGraphicBufferProducer
正是上文BLASTBufferQueue
中的, 该接口最终配置给底层的MediaPlayer
(Native).mPlayer->setVideoSurfaceTexture()
通过Binder调用到MediaPlayerService::Client::setVideoSurfaceTexture()
, 通过上层传递的bufferProducer
创建了新的Surface
, 又通过disconnectNativeWindow_l()
断开了bufferProducer
与应用持有的Surface
(Native)的联系, 然后将新创建的Surface
保存到Client::mConnectedWindow
, 这意味着, mediaserver
直接负责获取并填充GraphicBuffer
给原本属于应用持有的Surface
. 进一步, 将Surface
配置给NuPlayerDriver
, NuPlayerDriver
通过kWhatSetVideoSurface
将Surface
发个给异步线程.NuPlayer
保存上层的Surface
即mediaserver
使用应用传递的IGraphicBufferProducer
所创建的Surface
到mSurface
, 并调用NuPlayerDriver::notifySetSurfaceComplete()
告知NuPlayerDriver::setVideoSurfaceTexture()
可以返回.开始过程和上文的几个操作类似, 受限于篇幅, 仅给出简化的流程MediaPlayer.start()
-> MediaPlayer._start()
-> android_media_MediaPlayer_start()
-> MediaPlayer::start()
--[Binder]–> NuPlayerDriver::start()
-> NuPlayerDriver::start_l()
-> NuPlayer::start()
.
NuPlayer::start()
通过kWhatStart
通知异步线程启动, NuPlayer::onStart()
负责相应kWhatStart
消息, 其创建了NuPlayer::Rennderer
, 但并没有设置给mVideoDecoder
(类型为NuPlayer::Decoder
), 因为此时还没有创建mVideoDecoder
和mAudioDecoder
.
这个Renderer
后续通过其queueBuffer()
接受MediaCodecBuffer
, 它完成处理后, 通过kWhatRenderBuffer
通知NuPlayer::Decoder
进行MediaCodecBuffer
的释放.
MediaCodec
解码器的创建及初始化在NuPlayer
中, 解码器由NuPlayer::Decoder
进行抽象. 在NuPlayer
开始后, 如上文所述, 其首先完成了IMediaSource
的开始, 然后通过置身的postScanSources()
异步发出了kWhatScanSources
消息, 该消息被异步线程收到后, 开始执行NuPlayer::instantiateDecoder()
实例化解码器, 如果是音频解码器, 分两种情况:
DecoderPassThrough
Decoder
Decoder
Decoder
被创建后, 其init()
和configure()
方法被分别调用DecoderBase::configure()
是DecoderBase
通过异步消息kWhatConfigure
调用到子类Decoder
的onConfigure()
, Decoder
需要创建实际的解码器, 因此通过MediaCodec::CreateByType()
创建MediaCodec
, MediaCodecList::findMatchingCodecs()
负责查找支持当前解码格式解码器的名字, 其定义在MediaCodecList.cpp
, 如果找到解码器则创建MediaCodec
, 创建MediaCodec
时, 其mGetCodecBase
被初始化为一个std::function<>
对象, 后文的MediaCodec::init()
会调用此lambada
.创建完成后通过
init()调用上文的
mGetCodecBase也就是
MediaCodec::GetCodecBase()创建更底层的
CodecBase,
CodecBase`的实现有多种:CCodec
ACodec
MediaFilter
Codec 2
解码框架解码器CCodec
CCodec
的创建Android Q
以后的版本采用CCodec
的方式加载解码插件, 此处仅仅是创建了Codecbase
(这里是CCodec
), 确定了解码器的名字, 但还没有初始化CCodec
.
CCodec
事件监听的注册而MediaCodec
在初始化完CCodec
(CodecBase
)后:
CodecCallback
, 其实现了CodecBase::CodecCallback
接口, 而CCodec::setCallback()
是在父类CodecBase
实现的BufferCallback
, 其实现了CodecBase::BufferCallback
接口, 用于监听来自CCodecBufferChannel
的消息. 而mBufferChannel
的类型是CCodecBufferChannel
, 其setCallback()
是在父类BufferChannelBase
实现的, 最后MediaCodec::BufferCallback
作为CodecBase::BufferCallback
设置在了CCodecBufferChannel
的mCallback
方法CCodec
的实例化初始化过程仍在MediaCodec::init()
中继续, 该函数后续发出了kWhatInit
消息, 并传递了解码器的名字给异步线程,kWhatInit
由CodecBase::initiateAllocateComponent()
响应, 其对解码器进行实例化. 在CCodec
创建时CCodecBufferChannel
也被创建, 其继承自BufferChannelBase
, 并设置在CCodec
的mChannel
中
CCodec
再次发出异步消息kWhatAllocate
, 由CCodec::allocate()
响应. CCodec通过Codec2Client::CreateFromService()
创建了Codec2Client
, Codec2Client
持有IComponentStore
接口, 并通过其访问media.swcodec
的ComponnetStore
.
CCodec
后续通过Codec2Client::CreateComponentByName()
创建了Codec2Client::Component
, 大体的过程是: Codec2Client::CreateComponentByName()
-> Codec2Client::createComponent()
--[Binder
]–> [IComponentStore::createComponent_1_2()
=> ComponentStore::createComponent_1_2()
]. 该过程涉及解码器插件的加载和解码器组件的查找, 先了解接加载过程.
CCodec
视频解码插件加载C2SoftVpxDec
的加载过程:
libcodec2_soft_vp9dec.so
对应的ComponentModule
创建
media.swcodec
启动时, 通过RegisterCodecServices
注册ComponentStore
服务, 此时会创建C2PlatformComponentStore
, 其集成关系:C2PlatformComponentStore
-> C2ComponentStore
C2PlatformComponentStore
将创建mLibPath
为libcodec2_soft_vp9dec.so
的ComponentLoader
类型C2ComponentStore
创建实现了IComponentStore
的V1_2::utils::ComponentStore
实例, 返回给了Codec2Client
CCodec
视频解码组件Component
的查找Codec2Client
在通过createComponent()
方法创建组件时, ComponentStore
首先找到匹配的ComponentLoader
, 在Loader的初始化过程中欧给你, 将创建ComponentModule
对象ComponentLoader
对象从对应的libcodec2_soft_vp9dec.so
中查找CreateCodec2Factory
符号CreateCodec2Factory
符号将返回C2ComponentFactory
类型, 其实现为C2SoftVpxFactory
createInterface
方法, 返回一个C2ComponentInterface
接口, 其实现为SimpleC2Interface
模板类C2ComponentFactory
的createInterface
方法, 也就是C2SoftVpxFactory::createInterface
, 这将欻功能键一个C2ComponentInterface
接口, 实现为SimpleC2Interface
模板类, 对于Vpx9
该类的实现为C2SoftVpxDec::IntfImpl
, 其将被记录在C2Component::Traits
中ComponentModule
组件的createComponent
方法被调用, 该方法将调用上文CreateCodec2Factory
的对应方法, 而CreateCodec2Factory::createComponent
负责创建C2SoftVpxDec
, 继承关系: C2SoftVpxDec
-> SimpleC2Component
-> C2Component
, 而该C2Component
最后由ComponentStore
创建的Component
对象持有, 而Component
对象实现了IComponent
, 其后续将被返回给Codec2Client
.IComponent
被设置在Codec2Client::Component
后续被设置给上文CCodec
的CCodecBufferChannel
中.MediaCodec
的配置MediaCodec
通过kWhatConfigure
通知异步线程执行配置, 该消息由CCodec::initiateConfigureComponent()
负责响应, 该方法继续发出kWhatConfigure
消息给CCodec
的异步线程, 并由CCodec::configure()
响应.
doConfig
是个非常复杂的lambada
, 作为std::fucntion
传递给tryAndReportOnError()
, 该部分代码做了大量配置工作, 完成配置后, mCallback->onComponentConfigured()
回调到上文设置的MediaCodec::CodecCallback::onComponentConfigured()
MediaCodec
的启动Decoder::onConfigure()
最后负责启动MediaCodec
, MediaCodec
通过kWhatStart
通知异步线程执行配置, 该消息由CCodec::initiateStart()
负责响应.
CCodec
的启动该方法继续发出kWhatStart
消息给CCodec
的异步线程, 并由CCodec::start()
响应. 而CCodec::start()
也调用了CCodecBufferChannel::start()
, 上文说到CCodecBufferChannel
保存了Codec2Client::Component
, 此处Conponent::setOutputSurface()
被调用. mOutputBufferQueue
的类型是OutputBufferQueue
, 因此不管那个分支, 都调用了OutputBufferQueue::configure()
, 因此IGraphicBufferProducer
被设置到了OutputBufferQueue
的mIgbp
, 在后文OutputBufferQueue::outputBuffer()
时会用到. OutputBufferQueue
是视频解码器的输出队列, 当解码器有GraphicBuffer
通过C2Block2D
描述返回给CCodecBufferChannel
, 会注册到Codec2Client::Component
的OutputBufferQueue
中, 等待后续渲染时提取并送出.
postPendingRepliesAndDeferredMessages("kWhatStartCompleted")
完成后, MediaCodec::start()
返回
CCodec
解码CCodec
在启动CCodecBufferChannel
后立刻调用其requestInitialInputBuffers()
开始从数据源读取数据. 该方法从当前类的input->buffers
中请求缓冲, 其类型为LinearInputBuffers
, 继承关系: LinearInputBuffers
-> InputBuffers
-> CCodecBuffers
, requestNewBuffer()
正是由InputBuffers
提供. 在请求时, 如果缓冲区没有申请过, 则通过LinearInputBuffers::createNewBuffer()
-> LinearInputBuffers::Alloc()
进行申请, 申请的类型为Codec2Buffer
(父类MediaCodecBuffer
), 其实现是LinearBlockBuffer
, 在LinearBlockBuffer::Allocate()
创建LinearBlockBuffer
时, 首先从C2LinearBlock::map()
获取一个写入视图C2WriteView
, 该试图的data()
将返回C2LinearBlock
底层对应的ION
缓冲区的指针, 该指针在创建LinearBlockBuffer
时直接构造了ABuffer
并保存到了LinearBlockBuffer
父类Codec2Buffer
的父类MediaCodecBuffer
的mBuffer
成员中用于后续写入未解码数据时引用.
C2LinearBlock
对于编码数据, 其用线性数据块C2LinearBlock
(实现自C2Block1D
), 底层的实现是ION
, 其引用关系:
C2LinearBlock
=> C2Block1D
mImpl
: _C2Block1DImpl
=> C2Block1D::Impl
mAllocation
: C2LinearAllocation
=> C2AllocationIon
mImpl
: C2AllocationIon::Impl
C2Block1D
是从C2BlockPool
分配的, 其引用关系:C2BlockPool::mBase
: C2PooledBlockPool::Impl
mBufferPoolManager.mImpl
: ClientManager::Impl
mClients[x].mImpl
: BufferPoolClient::Impl
mLocalConnection
: Connectoin
mAccessor.mImpl
: Accessor::Impl
mAllocator
: _C2BufferPoolAllocator
=> BufferPoolAllocator
mAllocator
: C2Allocator
=> C2AllocatorIon
mImpl
: C2AllocationIon::Impl
ion_alloc()
C2AllocatorIon::newLinearAllocation()
创建了上文的C2AllocationIon
极其实现C2AllocationIon::Impl
, 创建完成后进行的分配.Codec2Buffer
申请完成后保存到mImpl
(也就是BuffersArrayImpl
), 最后作为MediaCodecBuffer
(父类)返回. 请求成功之后立马通知上层, 输入缓冲可用, 该时间是通过BufferCallback::onInputBufferAvailable()
, 上文提到BufferCallback
是MediaCodec
用来监听BufferChannelBase
(也就是CCodecBufferChannel
)消息的, 所以, BufferCallback
会通过kWhatCodecNotify
的AMessaage
通知通知MediaCodec
, 具体通知的消息为kWhatFillThisBuffer
.
kWhatFillThisBuffer
消息由MediaCodec::onInputBufferAvailable()
响应, MediaBuffer
继续通过mCallback
(类型为AMessage
)通知上层的NuPlayer::Decoder
, 具体的消息类型为MediaCodec::CB_INPUT_AVAILABLE
, 播放器在得知底层输入缓冲可用时, 试图提取一段输入数据.
NuPlayer::Decoder
通过基类NuPlayer::DecoderBase
的onRequestInputBuffers()
去拉取数据, 这个过程将通过GenericSource
的dequeueAccessUnit()
方法完成, 注意: 此处dequeueAccessUnit()
需要一个判断读取音频还是视频的参数, 继而判断通过mAudioTrack
还是mVideoTrack
来获取数据, 这两个成员上文已经介绍过. GenericSource::dequeueAccessUnit()
上文已经讲过. 当该函数无法从缓冲区读取到数据时会通过postReadBuffer()
从拉流, 该函数调用的GenericSource::readBuffer()
上文已经讲过, 此处略去.
dequeueAccessUnit
得到ABuffer
是上文GenericSource
给出的, 其所有权属于media.extractor
, 其指针指向的是该进程中的共享内存, 但MediaCodecBuffer
才是解码器需要的缓冲区描述, 上面说到, 该缓存其实是ION
缓冲区, 已经通过写入视图(C2WriteView
)映射到ABuffer
, 那么什么何时从ABuffer
拷贝到MediaCodecBuffer::mBuffer
的ABuffer
中的呢? 是在DecoderBase::onRequestInputBuffers()
-> Decoder::doRequestBuffers()
-> Decoder::onInputBufferFetched()
完成GenericSource::dequeueAccessUnit()
后执行的. 至此编码数据已经填充到LinearBlockBuffer
的C2Block1D
(也就是C2LinearBlock
)中.
C2Work
以及编码数据描述C2Buffer
的创建通过objcpy()
完成C2Work
到Work
的转换, 后者支持序列化, 便于通过Binder发送:
C2Work[]
-> WorkBundle
C2Work
-> Work
C2FrameData
-> FrameData
C2InfoBuffer
-> InfoBuffer
C2Buffer
-> Buffer
C2Block
-> Block
C2Worklet
-> Worklet
C2FrameData
-> FrameData
C2InfoBuffer
-> InfoBuffer
C2Buffer
-> Buffer
C2Block[1|2]D
-> Block
media.swcodec
对解码工作的接收以及解码数据的获取mediaserver
通过IComponent::queue()
发送C2Work
到media.swcodec
, 在服务端, objcpy()
负责WorkBundle
中的Work
到C2Work
的转换, 大概的层级关系:
WorkBundle
-> C2Work[]
Work
-> C2Work
FrameData
-> C2FrameData
InfoBuffer
-> C2InfoBuffer
Buffer
-> C2Buffer
Block
-> C2Block
Worklet
-> C2Worklet
FrameData
-> C2FrameData
InfoBuffer
-> C2InfoBuffer
Buffer
-> C2Buffer
Block
-> C2Block[1|2]D
C2Block1D
的实现是C2LinearBlock
, 通过底层的C2AllocationIon::Impl::map()
可完成对ION
缓存的映射, 获取待解码的数据.SimpleC2Component::queue_nb()
响应binder请求, 并获取C2Block1D
描述后, 发送kWhatProcess
消息, 该消息由SimpleC2Component::processQueue()
响应, 该方法直接调用子类的实现, 本文视频采用VP9
的编码, 因此子类实现为C2SoftVpxDec::process()
, 其同构work->input.buffers[0]->data().linearBlocks().front().map().get()
获取输入数据, 这个调用可分如下步骤看待:work->input.buffers[0]->data()
返回C2BufferData
类型C2ConstLinearBlock::linearBlocks()
返回C2ConstLinearBlock
类型, 该类型本质上是C2Block1D
C2ConstLinearBlock::map()
返回C2ReadView
, 此时C2ConstLinearBlock
通过实现C2Block1D::Impl
所保存的C2LinearAllocation
对ION
的缓存进行映射, 映射完成后创建AcquirableReadViewBuddy
(父类为C2ReadView
)并将数据保存到它的mImpl
(类型为: ReadViewBuddy::Impl
, 实际上就是C2ReadView::Impl
)中.uint8_t *bitstream = const_cast(rView.data() + inOffset
, 是通过C2ReadView::data()
获取数据指针, 正式从上面的C2ReadView::Impl.mData
获取的.vpx_codec_decode()
完成解码工作, 前提是输入数据长度有效.
C2GraphicBlock
的创建解码完成后解码器从C2BlockPool
中通过fetchGraphicBlock()
拉取一个C2GraphicBlock
, 此时将触发GraphicBuffer
的创建. 这里C2BlockPool
的实现是BlockingBlockPool
, 通过mImpl
引用C2BufferQueueBlockPool::Impl
, 从这个实现开始:
android::hardware::graphics::bufferqueue::V2_0::IBufferQueueProducer
(实现为BpHwBufferQueueProducer
)获取一个HardwareBuffer
h2b()
将HardwareBuffer
通过AHardwareBuffer_createFromHandle()
将HardwareBuffer
转化为AHardwareBuffer
GraphicBuffer::fromAHardwareBuffer()
通过AHardwareBuffer
创建GraphicBuffer
GraphicBuffer
是通过native_handle_t
创建的, 那么将涉及GraphicBuffer
的导入, GraphicBuffer
通过GraphicBufferMapper::importBuffer()
(后端实现是Gralloc2Mapper
)完成导入.android::hardware::graphics::bufferqueue::V2_0::IBufferQueueProducer
实现是BpHwGraphicBufferProducer
, 该方法的对端为mediaserver
进程中的BnHwGraphicBufferProducer
, 最后处理消息的类为B2HGraphicBufferProducer
, 而该方法中的mBase
类型为android::IGraphicBufferProducer
, 其实现为android::BpGraphicBufferProducer
. 该方法将跨进程从应用系统相册一侧的BnGraphicBufferProducer
=> BufferQueueProducer
=> BBQBufferQueueProducer
提取一个GraphicBuffer
, 该对象将通过b2h()
转换为一个IGraphicBufferProducer::QueueBufferOutput
, 该类继承自Flattenable
是可序列化的, 最终b2h()
转化GraphicBuffer
得到的HardwareBuffer
将通过Binder
传递给media.swcodec
的解码器.GraphicBuffer
创建后被C2Handle
所引用, C2Handle
通过WrapNativeCodec2GrallocHandle()
创建, 在为视频时, 实现为C2HandleGralloc
, 通过C2Handle
进一步分配了C2GraphicAllocation
, 此时C2BufferQueueBlockPoolData
被创建, 主要保存GraphicBuffer
的信息._C2BlockFactory::CreateGraphicBlock()
则负责创建C2GraphicBlock
, 上文的创建的C2GraphicAllocation
(子类C2AllocationGralloc
)和C2BufferQueueBlockPoolData
(类型为_C2BlockPoolData
)保存到C2GraphicBlock
的父类C2Block2D
的mImpl
(类型为C2Block2D::Impl
)中. 直到此时GraphicBuffer
中的数据指针还没有被获取. 但是, C2Block2D
已经被创建.C2Block2D
获取到GraphicBuffer
中的数据指针呢?block->map().get()
通过C2Block2D::Impl
, 也就是_C2MappingBlock2DImpl
创建一个Mapped
, 这个Mapped
通过C2GraphicAllocation
执行映射, 这个过程中mHidlHandle.getNativeHandle()
将获得native_handle_t
(其中mHidlHandle
的类型是上文创建的C2Handle
). 只要有native_handle_t
就可以通过GraphicBufferMapper::lockYCbCr()
去锁定HardwareBuffer
中的数据, 将获取PixelFormat4::YV12
格式的GraphicBuffer
中各数据分量的布局地址信息, 这些地址信息会保存到mOffsetData
, 后面会通过_C2MappingBlock2DImpl::Mapped::data()
获取. 最后C2GraphicBlock::map()
返回的是C2Acquirable
, 而C2Acquirable<>
返回的是C2GraphicView
.wView.data()
获取数据指针, 该过程通过C2GraphicView
:
mImpl
: _C2MappedBlock2DImpl
_C2MappedBlock2DImpl::mapping()
: Mapped
Mapped::data()
: 为上文保存的各数据分量的地址.copyOutputBufferToYuvPlanarFrame()
完成解码后数据到GraphicBuffer
的拷贝. createGraphicBuffer()
负责从填充过的包含GraphicBuffer
的C2GraphicBlock
包装为C2Buffer
, 然后通过fillWork
代码块将C2Buffer
打包到C2Work
中, 等待返回. (举证请参见附件 C2GraphicBlock
)解码完成后, 解码器填充数据到C2Buffer
, 该结构将被描述到C2Work
-> C2Worklet
-> C2FrameData
-> C2Buffer
(以及C2BufferInfo
)中, 按照上文的描述通过Binder
返回给mediaserver
, 该过程将通过objcpy()
完成从C2Work[]
到WorkBundle
的转换, 该过程略去.
mediaserver
通过HidlListener
(实现了接口IComponentListener
)来接收WorkBundle
(也就是Work[]
), 上文提到objcpy()
可完成Work
到C2Work
的转换, 该过程同样包含了各个阶段相应对象的创建, 这里只提及几个地方:
CreateGraphicBlock()
负责创建C2GraphicBlock
, 并配置给了dBaseBlocks[i]
(类型为C2BaseBlock
)的graphic
成员createGraphicBuffer()
负责从C2ConstGraphicBlock
到GraphicBuffer
的转化(导入), 并保存到OutputBufferQueue
的mBuffers[oldSlot]
. 而C2ConstGraphicBlock
来子上文转化后的C2FrameData::buffers(C2Buffer)::mImpl(C2Buffer::Impl)::mData(BufferDataBuddy)::mImpl(C2BufferData::Impl)::mGraphicBlocks(C2ConstGraphicBlock)
GraphicBuffer
已经完成从IComponent
到mediaserver
的传递.HidlListener::onWorkDone()
->
Codec2Client::Component::handleOnWorkDone()
, 这条路径未执行Codec2Client::Listener
=> CCodec::ClientListener::onWorkDone()
->
CCodec
->
CCodecBufferChannel::onWorkDone()
->
BufferCallback::onOutputBufferAvailable()
Buffercallback
上文提到过是MediaCodec
用来监听BufferChannelBase
(也就是CCodecBufferChannel
)的, 所以这里BufferCallback
通过kWhatDrainThisBuffer
通知MediaCodec
, MediaCodec::onOutputBufferAvailable()
负责响应该消息, 该方法有进一步通过CB_OUTPUT_AVAILABLE
消息, 通知到NuPlayer::Decoder
, NuPlayer::Decoder::handleAnOutputBuffer()
需要处理返回的视频帧, 解码器收到视频帧后推如渲染器, 也就是NuPlayer::Renderer
, 这个过程会对Renderer
发出kWhatQueueBuffer
消息, Renderer::onQueueBuffer()
负责响应该消息.音频解码输入的部分与视频解码并没有特别大的不同, 但输出的缓冲区类型也是C2LinearBlock
, 且该输出缓存的来源和上文在解码数据的分配过程是一样的.
暂时不讨论该话题.
mediaserver
中的队列上文讲过, GraphicBuffer
的跨进程经历了很多步骤, 它通过BnHwGraphicBufferProducer::dequeueBuffer()
响应media.swcodec
被转化为HardwareBuffer
通过Binder
获取, 通过h2b()
转化为GraphicBuffer
, 在数据填充完成后, 又通过Binder
传回, 在mediaserver
中通过h2b
转化回GraphicBuffer
, 并通过Codec2Buffer
作为MediaCodecBuffer
给到MediaCodec
通知上层同步, 同步完成后它最终将由MediaCodec
触发渲染, NuPlayer::Renderer
确定可以渲染时, 将通过kWhatReleaseOutputBuffer
消息告知MediaCodec
渲染, 响应该消息的是:MediaCodec::onReleaseOutputBuffer()
, 显然MediaCodec
将调用CCodecBufferChannel
执行Codec2Buffer
的渲染, Codec2Buffer
原本是保存在CCodecBufferChannel
的OutputBufferQueue
中, 在渲染时GraphicBuffer
将通过BpGraphicBufferProducer::queueBuffer()
被推出.
BLASTBufferQueue
上文说到GraphicBuffer
通过CCodecBufferChannel
的OutputBufferQueue
由IGraphicBufferProducer::queueBuffer()
被推送到系统相册里, Surface
表面内嵌缓冲队列BLASTBufferQueue
的生产者BBQBufferQueueProducer
, 然后BLASTBufferQueue
的消费者BufferQueueConsumer
将通过父类接口ConsumerBase::FrameAvailableListener()
通知对应的实现为:BLASTBufferQueue::onFrameAvailable()
进行 进一步处理. BLASTBufferQueue
在processNextBufferLocked()
中进一步处理收到的GraphicBuffer
, 其此时构造一个SurfaceComposerClient::Transaction
请求, 准备提交给SurfaceFlinger
MediaPlayer
将收到的通知类型MEDIA_NOP
MEDIA_PREPARED
: 来自Source::kWhatPrepared
MEDIA_PLAYBACK_COMPLETE
: kWhatScanSources
操作完成后MEDIA_BUFFERING_UPDATE
: 来自Source::kWhatBufferingUpdate
MEDIA_SEEK_COMPLETE
: kWhatSeek
完成后MEDIA_SET_VIDEO_SIZE
: 来自DecoderBase::kWhatVideoSizeChanged
或者Source::kWhatVideoSizeChanged
MEDIA_STARTED
: 来自Renderer::kWhatMediaRenderingStart
MEDIA_PAUSED
: 主要来自pause()
和seekTo()
两个操作MEDIA_STOPPED
: 主要来来自stop()
操作MEDIA_SKIPPED
: 未使用MEDIA_NOTIFY_TIME
: 主要来自MediaClock
回调MEDIA_TIMED_TEXT
: 来自Source::kWhatTimedTextData
MEDIA_ERROR
: ext1
类型为media_error_type
MEDIA_ERROR_UNKNOWN
: 1, 具体的类型关注ext2
:
ERROR_ALREADY_CONNECTED
ERROR_NOT_CONNECTED
MEDIA_ERROR_SERVER_DIED
: 100MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK
: 200MEDIA_INFO
: ext1
类型为media_info_type
MEDIA_INFO_UNKNOWN
= 1MEDIA_INFO_STARTED_AS_NEXT
: 准备用于下一个的播放MEDIA_INFO_RENDERING_START
: 首帧已播放, 来自Renderer::kWhatVideoRenderingStart
MEDIA_INFO_VIDEO_TRACK_LAGGING
: 待解码的数据过于复杂可能会延迟较大MEDIA_INFO_BUFFERING_START
: 播放器内部已暂停, 等待更多的数据, 来自Source::kWhatPauseOnBufferingStart
MEDIA_INFO_BUFFERING_END
: 播放器等待足够多的数据后恢复播放, 来自Source::kWhatResumeOnBufferingEnd
MEDIA_INFO_NETWORK_BANDWIDTH
: 网络带宽统计信息, 来自Source::kWhatCacheStats
MEDIA_INFO_BAD_INTERLEAVING
: 错误的数据插入?MEDIA_INFO_NOT_SEEKABLE
: 媒体不允许SEEK操作MEDIA_INFO_METADATA_UPDATE
: 有新的原数据, 来自Source::kWhatTimedMetaData
MEDIA_INFO_PLAY_AUDIO_ERROR
: 无法播放音频数据, 来自DecoderBase::kWhatError
MEDIA_INFO_PLAY_VIDEO_ERROR
: 无法播放视频数据, 来自DecoderBase::kWhatError
MEDIA_INFO_TIMED_TEXT_ERROR
MEDIA_SUBTITLE_DATA
: 来自Source::kWhatSubtitleData
MEDIA_META_DATA
: 来自Source::kWhatTimedMetaData
MEDIA_DRM_INFO
: 来自Source::kWhatDrmInfo
MEDIA_TIME_DISCONTINUITY
: 来自kWhatMediaClockNotify
, MediaClock
回调MEDIA_IMS_RX_NOTICE
: 来自RTP流的Source::kWhatIMSRxNotice
MEDIA_AUDIO_ROUTING_CHANGED
: 在音频通路输出切换时, 将通过该消息通知上层NuPlayer
接受底层的消息主要有以下集中类型kWhatMediaClockNotify
: 负责监听来自MediaClock
的消息, 消息中有如下三种信息:
"anchor-media-us"
"anchor-real-us"
"playback-rate"
kWhatSourceNotify
: 负责处理来自NuPlayer::Source
的消息, 有以下信息:
"what"
: 具体的数据源消息类型, 又分以下几种类型:
Source::kWhatInstantiateSecureDecoders
: 数据源是安全类型, 通知创建安全类型的解码器
"reply"
: 等待消息的AMesage
对象, 用于通知数据源安全解码器的创建完成kWhatPrepared
: 数据源已准备好, 可以读取数据
"err"
: 准备结束不代表成功, 该字段返回具体的状态码Source::kWhatDrmInfo
: 有关于DRM
信息
"drmInfo"
一个类型为ABuffer
的对象kWhatFlagsChanged
播放标识改变
"flags"
改变的标识Source::kWhatVideoSizeChanged
: 播放源表示播放的解码信息发生改变
"format"
为具体改变的格式信息, 一AMessage
体现, 其中主要的信息有
"width"
"height"
"sar-width"
"sar-height"
"display-width"
"display-height"
"rotation-degrees"
Source::kWhatBufferingUpdate
"percentage"
Source::kWhatPauseOnBufferingStart
Source::kWhatResumeOnBufferingEnd
Source::kWhatCacheStats
"bandwidth"
Source::kWhatSubtitleData
: 字幕数据
"buffer"
: ABuffer
Source::kWhatTimedMetaData
: 元数据
"buffer"
: ABuffer
Source::kWhatTimedTextData
: 文本数据?
"generation"
"buffer"
: ABuffer
"timeUs"
Source::kWhatQueueDecoderShutdown
"audio"
"video"
"reply"
Source::kWhatDrmNoLicense
: 没有DRM
的LicenseSource::kWhatIMSRxNotice
AMessage
kWhatRendererNotify
: 来自渲染器ALooper
的消息, 提供以下信息:
"generation"
"what"
: 有多种类型:
Renderer::kWhatEOS
"audio"
"finalResult"
Renderer::kWhatFlushComplete
"audio"
Renderer::kWhatVideoRenderingStart
Renderer::kWhatMediaRenderingStart
"audio"
Renderer::kWhatAudioTearDown
"reason"
: 原因"positionUs"
: 多久kWhatClosedCaptionNotify
kWhatAudioNotify
和kWhatVideoNotify
负责相应来自音视频NuPlayer::Decoder
的消息, 主要有以下信息:
"generation"
"reply"
"what"
, 有以下类型:
DecoderBase::kWhatInputDiscontinuity
: 输入不连续, 可能需要重新扫描数据源DecoderBase::kWhatEOS
: 码流结束
"err"
DecoderBase::kWhatFlushCompleted
: Flush操作结束DecoderBase::kWhatVideoSizeChanged
: 解码时格式改变
"format"
: AMessage
形式的格式描述DecoderBase::kWhatShutdownCompleted
: 停止播放DecoderBase::kWhatResumeCompleted
: 恢复播放DecoderBase::kWhatError
: 解码遇到错误
"err"
: 错误原因, 错误码将通过MEDIA_INFO
类型报给上层IMediaExtractor
MediaExtractorService::makeExtractor()
下断点:
p *((TinyCacheSource *)((RemoteMediaExtractor *)0x0000007d68639df0)->mSource.m_ptr->mWrapper->handle)
warning: `this' is not accessible (substituting 0). Couldn't load 'this' because its value couldn't be evaluated
(android::TinyCacheSource) $35 = {
android::DataSource = {
mWrapper = 0x0000007d4864b410
}
mSource = {
m_ptr = 0x0000007d58647640
}
mCache = "..."...
mCachedOffset = 405144
mCachedSize = 2048
mName = (mString = "TinyCacheSource(CallbackDataSource(4894->4875, RemoteDataSource(FileSource(fd(/storage/emulated/0/Movies/VID_20220317_221515.mp4), 0, 4948142))))")
}
对于地址0x0000007d58647640
, 已经知道其类型为CallbackDataSource
, 因此:
p *(CallbackDataSource *)0x0000007d58647640
warning: `this' is not accessible (substituting 0). Couldn't load 'this' because its value couldn't be evaluated
(android::CallbackDataSource) $37 = {
android::DataSource = {
mWrapper = nullptr
}
mIDataSource = (m_ptr = 0x0000007d88645ab0)
mMemory = (m_ptr = 0x0000007d68639cd0)
mIsClosed = false
mName = (mString = "CallbackDataSource(4894->4875, RemoteDataSource(FileSource(fd(/storage/emulated/0/Movies/VID_20220317_221515.mp4), 0, 4948142)))")
}
通过mName
确认到以上所有类型行的总结都是正确的
IMediaSource
为了验证该总结的正确性, 通过调试器:
p track
(const android::sp) $79 = (m_ptr = 0x0000007d98639b10)
得到的 track 的类型应为 RemoteMediaSource, 因此:
p *(android::RemoteMediaSource *)0x0000007d98639b10
(android::RemoteMediaSource) $80 = {
mExtractor = {
m_ptr = 0x0000007d68639fd0
}
mTrack = 0x0000007d3863c290
mExtractorPlugin = (m_ptr = 0x0000007d7863d9b0)
}
得到的 mTrack 类型应为 MediaTrackCUnwrapper, 因此
p *(android::MediaTrackCUnwrapper *)0x0000007d3863c290
(android::MediaTrackCUnwrapper) $81 = {
wrapper = 0x0000007d586463d0
bufferGroup = nullptr
}
得到的 wrapper 类型应为 CMediaTrack, 因此
p *(CMediaTrack *)0x0000007d586463d0
(CMediaTrack) $83 = {
data = 0x0000007de8638ed0
free = 0x0000007d143696b8 (libmp4extractor.so`android::wrap(android::MediaExtractorPluginHelper*)::'lambda'(void*)::__invoke(void*) + 4)
start = 0x0000007d143696bc (libmp4extractor.so`android::wrap(android::MediaTrackHelper*)::'lambda'(void*)::__invoke(void*) + 4)
stop = 0x0000007d143696c0 (libmp4extractor.so`android::wrap(android::MediaTrackHelper*)::'lambda0'(void*)::__invoke(void*))
getFormat = 0x0000007d143696c8 (libmp4extractor.so`android::wrap(android::MediaExtractorPluginHelper*)::'lambda'(void*, AMediaFormat*)::__invoke(void*, AMediaFormat*) + 4)
read = 0x0000007d143696cc (libmp4extractor.so`android::wrap(android::MediaTrackHelper*)::'lambda'(void*, AMediaFormat*)::__invoke(void*, AMediaFormat*) + 4)
supportsNonBlockingRead = 0x0000007d143696d0 (libmp4extractor.so`__typeid__ZTSFbPvE_global_addr)
}
得到的 data 的类型为: MPEG4Source, 因此:
p *((android::MPEG4Source *)0x0000007de8638ed0)
(android::MPEG4Source) $67 = {
android::MediaTrackHelper = {
mBufferGroup = nullptr
}
mLock = {
mMutex = {
__private = ([0] = 0, [1] = 0, [2] = 0, [3] = 0, [4] = 0, [5] = 0, [6] = 0, [7] = 0, [8] = 0, [9] = 0)
}
}
mFormat = 0x0000007d586489a0
mDataSource = 0x0000007d2863b850
mTimescale = 90000
...
}
此时关注 mFormat 我们打印其内容:
p *(AMediaFormat *)0x0000007d586489a0
(AMediaFormat) $87 = {
mFormat = {
m_ptr = 0x0000007d686398b0
}
mDebug = (mString = "")
}
此处 mFormat 的类型为 android::AMessage, 因此:
p *(android::AMessage *)0x0000007d686398b0
(android::AMessage) $89 = {
android::RefBase = {
mRefs = 0x0000007d3863c230
}
mWhat = 0
mTarget = 0
mHandler = {
m_ptr = nullptr
m_refs = nullptr
}
mLooper = {
m_ptr = nullptr
m_refs = nullptr
}
mItems = size=16 {
[0] = {
u = {
int32Value = 946061584
int64Value = 537816973584
sizeValue = 537816973584
floatValue = 0.0000543008209
doubleValue = 2.6571689039816359E-312
ptrValue = 0x0000007d3863c110
refValue = 0x0000007d3863c110
stringValue = 0x0000007d3863c110
rectValue = (mLeft = 946061584, mTop = 125, mRight = 0, mBottom = 0)
}
mName = 0x0000007d2863c270 "mime"
mNameLength = 4
mType = kTypeString
}
...
}
...
}
AMessage 中, mItems 的第一个 Item 类型中的 stringValue 类型为: AString *, 因此可以求 “mime” 的值:
p *(android::AString *)0x0000007d3863c110
(android::AString) $91 = (mData = "video/avc", mSize = 9, mAllocSize = 32)
可以清晰的看到, 有一个Track
的mime
类型为"video/avc"
, 而另一个通过同样的方法可得知为: "audio/mp4a-latm"
.
C2GraphicBlock
断点:C2BufferQueueBlockPool::Impl::fetchFromIgbp_l()
(C2BqBuffer.cpp:463:13
):
(lldb) p *block
(std::shared_ptr) $36 = std::__1::shared_ptr::element_type @ 0x0000006fa2dbbcd0 strong=1 weak=1 {
__ptr_ = 0x0000006fa2dbbcd0
}
(lldb) p *(C2GraphicBlock *)0x0000006fa2dbbcd0
(C2GraphicBlock) $38 = {
C2Block2D = {
_C2PlanarSectionAspect = {
_C2PlanarCapacityAspect = (_mWidth = 320, _mHeight = 240)
mCrop = (width = 320, height = 240, left = 0, top = 0)
}
mImpl = std::__1::shared_ptr::element_type @ 0x0000006ff2dbc5e8 strong=1 weak=2 {
__ptr_ = 0x0000006ff2dbc5e8
}
}
}
(lldb) p *(C2Block2D::Impl *)0x0000006ff2dbc5e8
(C2Block2D::Impl) $39 = {
_C2MappingBlock2DImpl = {
_C2Block2DImpl = {
_C2PlanarSectionAspect = {
_C2PlanarCapacityAspect = (_mWidth = 320, _mHeight = 240)
mCrop = (width = 320, height = 240, left = 0, top = 0)
}
mAllocation = std::__1::shared_ptr::element_type @ 0x0000006ff2db7cf0 strong=2 weak=1 {
__ptr_ = 0x0000006ff2db7cf0
}
mPoolData = std::__1::shared_ptr<_C2BlockPoolData>::element_type @ 0x0000006ff2dbabc8 strong=2 weak=2 {
__ptr_ = 0x0000006ff2dbabc8
}
}
... ...
// C2GraphicAllocation 的类型实际上是 C2AllocationGralloc
(lldb) p *(android::C2AllocationGralloc *)0x0000006ff2db7cf0
(android::C2AllocationGralloc) $40 = {
C2GraphicAllocation = {
_C2PlanarCapacityAspect = (_mWidth = 320, _mHeight = 240)
}
mWidth = 320
mHeight = 240
mFormat = 842094169
mLayerCount = 1
mGrallocUsage = 2355
mStride = 320
mHidlHandle = {
mHandle = {
= {
mPointer = 0x0000006fe2db5b40
_pad = 480547396416
}
}
...
// mHidlHandle.mHandle 的类型是 native_handle_t
(lldb) p *((android::C2AllocationGralloc *)0x0000006ff2db7cf0)->mHidlHandle.mHandle.mPointer
(const native_handle) $64 = (version = 12, numFds = 2, numInts = 22, data = int [] @ 0x0000000008a61a0c)
这里多扯一句, native_handl
(也就是natvie_handle_t
)其实是高通的私有的private_handle_t
, 该数据的幻数是'msmg'
, 其也保存了宽高, 其定义在hardware/qcom/sdm845/display/gralloc/gr_priv_handle.h
文件中, 名称为:private_handle_t
, 其数据:
(lldb) x -c64 0x0000006fe2db5b40
0x6fe2db5b40: 0c 00 00 00 02 00 00 00 16 00 00 00 3e 00 00 00 ............>...
0x6fe2db5b50: 3f 00 00 00 6d 73 6d 67 08 02 10 14 40 01 00 00 ?...msmg....@...
0x6fe2db5b60: f0 00 00 00 40 01 00 00 f0 00 00 00 59 56 31 32 [email protected]
0x6fe2db5b70: 01 00 00 00 01 00 00 00 84 05 00 00 00 00 00 00 ................
可自行对比.