承接上一章节分析:【五】Android MediaPlayer整体架构源码分析 -【prepareAsync/prepare数据准备处理流程】【Part 2】
本系列文章分析的安卓源码版本:【Android 10.0 版本】
【此章节小节编号就接着上一章节排列】
3.4.1.3、MediaExtractorCUnwrapper封装代理类声明及构造函数定义:
将CMediaExtractor对象封装进代理对象。
MediaExtractorCUnwrapper封装代理类声明
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaExtractor.h]
class MediaExtractorCUnwrapper : public MediaExtractor {}
// [frameworks/av/media/libstagefright/include/media/stagefright/MediaExtractor.h]
class MediaExtractor
// : public ExtractorAllocTracker
{}
MediaExtractorCUnwrapper封装代理类构造函数定义
// [frameworks/av/media/libstagefright/MediaExtractor.cpp]
MediaExtractorCUnwrapper::MediaExtractorCUnwrapper(CMediaExtractor *plugin) {
this->plugin = plugin;
}
3.4.1.4、CreateIMediaExtractorFromMediaExtractor(ex, source, plugin)实现分析:
由于MediaExtractorCUnwrapper对象未实现Binder机制功能,因此再次封装成该对象可跨进程交互的IMediaExtractor相关的子类Binder类型对象。
// [frameworks/av/media/libstagefright/InterfaceUtils.cpp]
sp<IMediaExtractor> CreateIMediaExtractorFromMediaExtractor(
MediaExtractor *extractor,
const sp<DataSource> &source,
const sp<RefBase> &plugin) {
if (extractor == nullptr) {
return nullptr;
}
// 封装转换成 IMediaExtractor 类型对象,可用于跨进程通信
return RemoteMediaExtractor::wrap(extractor, source, plugin);
}
RemoteMediaExtractor::wrap(extractor, source, plugin)实现分析:
其实从此处可以看出,就是需要封装成可通过Binder机制跨进程来调用的操作类即IMediaExtractor子类RemoteMediaExtractor,其实现了Binder接口,成为了Bn实现端BnMediaExtractor的子类,因此再返回给其他进程调用端跨进程使用。
// [frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp]
// static
sp<IMediaExtractor> RemoteMediaExtractor::wrap(
MediaExtractor *extractor,
const sp<DataSource> &source,
const sp<RefBase> &plugin) {
if (extractor == nullptr) {
return nullptr;
}
// 创建封装代理类对象
return new RemoteMediaExtractor(extractor, source, plugin);
}
RemoteMediaExtractor封装代理类声明:
// [frameworks/av/media/libmedia/include/media/IMediaExtractor.h]
class IMediaExtractor : public IInterface {}
// [frameworks/av/media/libmedia/include/media/IMediaExtractor.h]
class BnMediaExtractor: public BnInterface<IMediaExtractor> {}
// [frameworks/av/media/libstagefright/include/media/stagefright/RemoteMediaExtractor.h]
// IMediaExtractor wrapper to the MediaExtractor.
class RemoteMediaExtractor : public BnMediaExtractor {}
RemoteMediaExtractor封装代理类构造函数实现:
// [frameworks/av/media/libstagefright/RemoteMediaExtractor.cpp]
RemoteMediaExtractor::RemoteMediaExtractor(
MediaExtractor *extractor,
const sp<DataSource> &source,
const sp<RefBase> &plugin)
// 缓存参数
:mExtractor(extractor),
mSource(source),
mExtractorPlugin(plugin) {
mAnalyticsItem = nullptr;
// debug媒体数据开关宏定义默认为1即开启,主要就是处理媒体分析统计数据项(属性)信息
// 其实该处理通过英文注释可知,其获取的值是给java层的
// frameworks/base/media/java/android/media/MediaExtractor.java类来获取这些信息。
if (MEDIA_LOG) {
mAnalyticsItem = MediaAnalyticsItem::create(kKeyExtractor);
// 获取调用端进程用户id
// we're in the extractor service, we want to attribute to the app
// that invoked us.
int uid = IPCThreadState::self()->getCallingUid();
mAnalyticsItem->setUid(uid);
// track the container format (mpeg, aac, wvm, etc)
size_t ntracks = extractor->countTracks();
mAnalyticsItem->setCString(kExtractorFormat, extractor->name());
// tracks (size_t)
mAnalyticsItem->setInt32(kExtractorTracks, ntracks);
// 从媒体提取器中获取容器特殊媒体元数据信息对象
// metadata
MetaDataBase pMetaData;
if (extractor->getMetaData(pMetaData) == OK) {
String8 xx = pMetaData.toString();
// 'titl' -- but this verges into PII
// 'mime'
// 获取MIME字段对应的值即文件类型,并记录在统计分析数据项中
const char *mime = nullptr;
if (pMetaData.findCString(kKeyMIMEType, &mime)) {
mAnalyticsItem->setCString(kExtractorMime, mime);
}
// 还可以添加我们感兴趣的媒体元数据信息项
// what else is interesting and not already available?
}
}
}
3.4.2、AnotherPacketSource类声明和构造函数实现
AnotherPacketSource类声明:
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.h]
struct AnotherPacketSource : public MediaSource {}
// [frameworks/av/media/libstagefrigh/include/media/stagefright/MediaSource.h]
struct MediaSource : public virtual RefBase {}
AnotherPacketSource类构造函数实现:
初始化值操作
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.cpp]
AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta)
: mIsAudio(false),
mIsVideo(false),
mEnabled(true),
mFormat(NULL),
// 上一次(最近一次)媒体数据加入队列的媒体时间戳
// 即缓存读取到的媒体数据的媒体时间戳【非系统时间戳】
mLastQueuedTimeUs(0),
// 估算缓冲数据时长
mEstimatedBufferDurationUs(-1),
// 标记EOS结果状态,OK表示非EOS
mEOSResult(OK),
// 最近媒体数据加入队列(读取的解复用数据)的媒体元数据信息的消息对象(AMessage)
mLatestEnqueuedMeta(NULL),
// 最近媒体数据出队列(解码器获取已解复用数据)的媒体元数据信息的消息对象(AMessage)
mLatestDequeuedMeta(NULL) {
// 设置文件格式元数据
// 见下面的分析
setFormat(meta);
// DiscontinuitySegment:非连续片段数据是非连续标记之间的连续存取单元。应该总是至少有一个非连续数据片段。
// DiscontinuitySegment 类声明,见下面的分析。
// 此处存入至少一个非连续数据片段对象到列表中。
mDiscontinuitySegments.push_back(DiscontinuitySegment());
}
setFormat(meta)实现分析:
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.cpp]
void AnotherPacketSource::setFormat(const sp<MetaData> &meta) {
// 只允许设置一次track信息的媒体元数据信息,除非调用reset
if (mFormat != NULL) {
// Only allowed to be set once. Requires explicit clear to reset.
return;
}
mIsAudio = false;
mIsVideo = false;
if (meta == NULL) {
return;
}
// 获取文件格式mime类型值
mFormat = meta;
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
// 处理标记当前文件类型
if (!strncasecmp("audio/", mime, 6)) {
// audio文件格式类型时
mIsAudio = true;
} else if (!strncasecmp("video/", mime, 6)) {
// video文件类型时
mIsVideo = true;
} else {
// text文件类型或application文件类型时,其实应该就是网络请求方式时
CHECK(!strncasecmp("text/", mime, 5) || !strncasecmp("application/", mime, 12));
}
}
DiscontinuitySegment 类声明:
AnotherPacketSource的内部类
// [frameworks/av/media/libstagefrigh/mpeg2ts/AnotherPacketSource.h]
struct AnotherPacketSource : public MediaSource {
private:
struct DiscontinuitySegment {
int64_t mMaxDequeTimeUs, mMaxEnqueTimeUs;
DiscontinuitySegment()
: mMaxDequeTimeUs(-1),
mMaxEnqueTimeUs(-1) {
};
void clear() {
mMaxDequeTimeUs = mMaxEnqueTimeUs = -1;
}
};
}
3.5、getFormatMeta_l(false /* audio */)实现分析:
获取video track信息的元数据。
其实也就是通过video track对象(source)的getFormat()方法来获取video track信息的元数据信息,注意此元数据信息是只包含视频的元数据信息的。要能区分这个概念。
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
sp<MetaData> NuPlayer::GenericSource::getFormatMeta_l(bool audio) {
sp<IMediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
if (source == NULL) {
return NULL;
}
return source->getFormat();
}
3.6、notifyVideoSizeChanged(msg)实现分析:
通知视频大小变化事件,并参入携带当前track信息中所有元数据信息的消息对象。
注意:该方法也是在NuPlayer.cpp中实现的。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::Source::notifyVideoSizeChanged(const sp<AMessage> &format) {
// dupNotify() 方法实现,见前面已有的分析
// 也就是发送一个【kWhatSourceNotify】事件消息给NuPlayer的消息循环系统接收
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatVideoSizeChanged);
notify->setMessage("format", format);
// 携带参数后立即发送
notify->post();
}
发送【kWhatSourceNotify】事件消息给NuPlayer的消息循环系统接收,因此处理如下:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSourceNotify:
{
onSourceNotify(msg);
break;
}
}
}
onSourceNotify(msg)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
// 取出事件参数
int32_t what;
CHECK(msg->findInt32("what", &what));
switch (what) {
case Source::kWhatVideoSizeChanged:
{
sp<AMessage> format;
CHECK(msg->findMessage("format", &format));
updateVideoSize(format);
break;
}
}
}
updateVideoSize(format)实现分析:
只传入了一个参数,因此第二个参数会默认为null。inputFormat参数如上分析就是携带当前track信息中所有元数据的消息对象即作为视频输入格式元数据,还可以传入指定视频大小信息的视频输出格式元数据信息。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::updateVideoSize(
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat) {
// 有效性检查
if (inputFormat == NULL) {
ALOGW("Unknown video size, reporting 0x0!");
// 此错误需要通知上层APP知晓处理。
// 也就是调用了NuPlayerDriver的notifyListener()方法一层一层往上层APP上报处理的。
// 此处不再分析,可参考前面相关章节。
notifyListener(MEDIA_SET_VIDEO_SIZE, 0, 0);
return;
}
int32_t err = OK;
inputFormat->findInt32("err", &err);
if (err == -EWOULDBLOCK) {
ALOGW("Video meta is not available yet!");
return;
}
if (err != OK) {
ALOGW("Something is wrong with video meta!");
return;
}
int32_t displayWidth, displayHeight;
if (outputFormat != NULL) {
// 若第二个参数不为空
// 从输出格式元数据中获取指定视频大小信息
int32_t width, height;
CHECK(outputFormat->findInt32("width", &width));
CHECK(outputFormat->findInt32("height", &height));
// 然后获取视频剪切大小的(对应界面坐标上的)矩形坐标【左上右下】,即两个对角的坐标
int32_t cropLeft, cropTop, cropRight, cropBottom;
CHECK(outputFormat->findRect(
"crop",
&cropLeft, &cropTop, &cropRight, &cropBottom));
// 计算显示的宽高,此处都加了个1
displayWidth = cropRight - cropLeft + 1;
displayHeight = cropBottom - cropTop + 1;
ALOGV("Video output format changed to %d x %d "
"(crop: %d x %d @ (%d, %d))",
width, height,
displayWidth,
displayHeight,
cropLeft, cropTop);
} else {
// 若上层APP未指定视频大小时
// 默认直接获取原视频大小展示
CHECK(inputFormat->findInt32("width", &displayWidth));
CHECK(inputFormat->findInt32("height", &displayHeight));
ALOGV("Video input format %d x %d", displayWidth, displayHeight);
}
// SAR:Sample Aspect Ratio 采样宽高比。
// 类似还有:DAR(Sample Aspect Ratio)显示宽高比,也就是常说的16:9、4:3等,
// PAR - pixel aspect ratio大多数情况为1:1,就是一个正方形像素,否则为长方形像素。
// 三者的关系PAR x SAR = DAR或者PAR = DAR/SAR。
// 注:考虑采样宽高比处理
// Take into account sample aspect ratio if necessary:
int32_t sarWidth, sarHeight;
if (inputFormat->findInt32("sar-width", &sarWidth)
&& inputFormat->findInt32("sar-height", &sarHeight)
&& sarWidth > 0 && sarHeight > 0) {
// 当前video track信息媒体元数据中有配置SAR信息时
ALOGV("Sample aspect ratio %d : %d", sarWidth, sarHeight);
// 只处理显示宽的采样缩放:即宽 * 采样宽高比值
displayWidth = (displayWidth * sarWidth) / sarHeight;
ALOGV("display dimensions %d x %d", displayWidth, displayHeight);
} else {
int32_t width, height;
// 获取原始video track元数据中的(推荐)显示宽高值,存在时处理缩放
if (inputFormat->findInt32("display-width", &width)
&& inputFormat->findInt32("display-height", &height)
&& width > 0 && height > 0
&& displayWidth > 0 && displayHeight > 0) {
if (displayHeight * (int64_t)width / height > (int64_t)displayWidth) {
// 若高度过高,则按显示宽高比重新计算显示高度
displayHeight = (int32_t)(displayWidth * (int64_t)height / width);
} else {
// 否则宽度过宽,则按显示宽高比重新计算显示宽度
displayWidth = (int32_t)(displayHeight * (int64_t)width / height);
}
ALOGV("Video display width and height are overridden to %d x %d",
displayWidth, displayHeight);
}
}
// 获取原始视频元数据的原始视频旋转角度,若获取失败则默认为0
int32_t rotationDegrees;
if (!inputFormat->findInt32("rotation-degrees", &rotationDegrees)) {
rotationDegrees = 0;
}
// 若是90度或270度【安卓设备录像时候的原始视频角度】,则必须将视频显示宽高值对换
if (rotationDegrees == 90 || rotationDegrees == 270) {
int32_t tmp = displayWidth;
displayWidth = displayHeight;
displayHeight = tmp;
}
// 最后,向上层APP通知视频显示宽高大小值
// 会调用了NuPlayerDriver的notifyListener()方法一层一层往上层APP上报处理的。
// 此处不再分析,可参考前面相关章节。
notifyListener(
MEDIA_SET_VIDEO_SIZE,
displayWidth,
displayHeight);
}
3.7、notifyFlagsChanged()实现分析:
通知音视频媒体相关标志位变化事件【即数据源解复用模块是否可以暂停、seek、前进后退等操作】。
传入的参数是一个由bit标志位通过或运算符计算得到的int值,每一个bit位都代表不同的参数。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) {
// 同上分析,发送一个【kWhatSourceNotify】事件消息给NuPlayer的消息循环系统接收,
// 并最后在onSourceNotify(msg)方法中处理该事件【kWhatFlagsChanged】
sp<AMessage> notify = dupNotify();
notify->setInt32("what", kWhatFlagsChanged);
notify->setInt32("flags", flags);
notify->post();
}
onSourceNotify(msg)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp]
void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
// 取出事件参数
int32_t what;
CHECK(msg->findInt32("what", &what));
switch (what) {
case Source::kWhatFlagsChanged:
{
uint32_t flags;
CHECK(msg->findInt32("flags", (int32_t *)&flags));
sp<NuPlayerDriver> driver = mDriver.promote();
if (driver != NULL) {
// 此处就是获取打印媒体可操作标志位的开启情况
ALOGV("onSourceNotify() kWhatFlagsChanged FLAG_CAN_PAUSE: %d "
"FLAG_CAN_SEEK_BACKWARD: %d \n\t\t\t\t FLAG_CAN_SEEK_FORWARD: %d "
"FLAG_CAN_SEEK: %d FLAG_DYNAMIC_DURATION: %d \n"
"\t\t\t\t FLAG_SECURE: %d FLAG_PROTECTED: %d",
(flags & Source::FLAG_CAN_PAUSE) != 0,
(flags & Source::FLAG_CAN_SEEK_BACKWARD) != 0,
(flags & Source::FLAG_CAN_SEEK_FORWARD) != 0,
(flags & Source::FLAG_CAN_SEEK) != 0,
(flags & Source::FLAG_DYNAMIC_DURATION) != 0,
(flags & Source::FLAG_SECURE) != 0,
(flags & Source::FLAG_PROTECTED) != 0);
// 与运算符的计算原理此处不展开分析,比较简单,就是一种bit位操作实现的
if ((flags & NuPlayer::Source::FLAG_CAN_SEEK) == 0) {
// 若数据源不支持seek操作,则调用NuPlayerDriver该方法通知上层APP
driver->notifyListener(
MEDIA_INFO, MEDIA_INFO_NOT_SEEKABLE, 0);
}
// 见下面的分析
driver->notifyFlagsChanged(flags);
}
if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION)
&& (!(flags & Source::FLAG_DYNAMIC_DURATION))) {
// 此处处理为:不支持动态获取媒体时长时,执行取消获取媒体时长操作。
// 该部分不必要分析,实现原理很简单,就是通过AHandler的延迟执行功能,
// 实现了一个每隔1秒获取一次数据源中的媒体时长值,
// 然后将该值缓存在NuPlayerDriver的mDuration变量中,
// 其实获取媒体时长值在前面分析的prepare完成事件通知时会再次主动获取一次的。
cancelPollDuration();
} else if (!(mSourceFlags & Source::FLAG_DYNAMIC_DURATION)
&& (flags & Source::FLAG_DYNAMIC_DURATION)
&& (mAudioDecoder != NULL || mVideoDecoder != NULL)) {
// 此处就对应if中的分析,即实现了一个每隔1秒周期性去获取数据源媒体时长的任务
schedulePollDuration();
}
// 缓存当前数据源的操作标志位
mSourceFlags = flags;
break;
}
}
}
driver->notifyFlagsChanged(flags)实现分析:
其实也是缓存该媒体数据源播放标志位支持的操作。
// [frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp]
void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
Mutex::Autolock autoLock(mLock);
mPlayerFlags = flags;
}
3.8、finishPrepareAsync()实现分析:
结束(完成)异步prepare处理流程。
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::finishPrepareAsync() {
ALOGV("finishPrepareAsync");
// 此处其实就是启动音视频track
// 见下面的分析
status_t err = startSources();
if (err != OK) {
ALOGE("Failed to init start data source!");
// 向上层应用通知该错误
// 见3.3小节分析
notifyPreparedAndCleanup(err);
return;
}
if (mIsStreaming) {
// 若是(网络数据)流媒体数据源,那么需要执行缓冲数据处理流程,
// 其会执行每隔1秒周期性拉取网络媒体数据。
// 此处暂不分析网络数据处理部分
mCachedSource->resumeFetchingIfNecessary();
// 这种情况就标记正在prepare状态中,因为需要拉取足够的缓冲数据
mPreparing = true;
// 每隔1秒周期性拉取网络媒体数据
schedulePollBuffering();
} else {
// 非流媒体数据源时,如本地文件播放时,直接通知prepare完成事件,
// 该方法不带参数调用时,默认传入prepare成功状态参数。
// 见前面3.3小节中的分析
// 备注:该方法实际是在NuPlayer.cpp中定义实现的
notifyPrepared();
// 注意:
// 其实,在非流媒体数据源时,此时通过notifyPrepared通知上层APP数据准备完成事件后,
// 下面的开始获取数据流程处理将和上层APP的接下来的请求同步执行的,
// 比如上层接收到完成事件后会执行MediaPlayer.start()请求开始播放,
// 同时下面的流程也在执行必要开始读取足够的音视频数据的一次缓冲处理。
// 当然上面的if中的流媒体数据时就必须等待下面执行音视频数据缓冲足够数据支撑播放后才会通知上层APP该prepare完成事件。
}
// 开始通知音频track读取音频数据事件处理流程
// 备注:因此根据此处的实现可知,音频数据先track,视频后track。
if (mAudioTrack.mSource != NULL) {
// 见下面的分析
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
// 开始通知视频track读取视频数据事件处理流程
if (mVideoTrack.mSource != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
}
startSources()实现分析:
其实看下面的实现很简单,就是执行前面分析的音视频track信息对象的start()方法,而根据前面相关章节的分析可知,此处调用track的start()方法其实际会通过一层层的代理对象往下层具体媒体提取器解复用模块的音视频track具体实现对象的对应start()方法将被执行。
其实根据下面的英文注释可以知道,该方法其实只是让下层解复用模块具体track实现类启动工作例如分配buffer缓冲区等,但并不是真正的开始读取数据源数据,只有当调用了GenericSource的start()方法后才会开始读取数据。
关于解复用模块化加载实现原理请查看:Android底层音视频播放媒体提取器【MediaExtractor】的解复用模块demuxers模块化加载和注册流程实现源码分析
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
status_t NuPlayer::GenericSource::startSources() {
// Start the selected A/V tracks now before we start buffering.
// Widevine sources might re-initialize crypto when starting, if we delay
// this to start(), all data buffered during prepare would be wasted.
// (We don't actually start reading until start().)
//
// TODO: this logic may no longer be relevant after the removal of widevine
// support
if (mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK) {
ALOGE("failed to start audio track!");
return UNKNOWN_ERROR;
}
if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK) {
ALOGE("failed to start video track!");
return UNKNOWN_ERROR;
}
return OK;
}
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
// mPendingReadBufferTypes该值表示最近请求的将要执行的音频或视频track读取数据类型,默认值为0,
// 其实它的作用就是为了避免同一种track多次(重复)请求track读取数据,其实只需要一次就足够了,
// 若此前相同track类型已请求过将会跳过本次请求,而该值将在每个track真正开始执行读取足够数据后
// 删除该track请求记录以供下次可再次请求该track读取数据。
// 简单的说就是该标志位作用过程是:
// post发起该track的【kWhatReadBuffer】事件请求消息添加到ALooper待执行消息队列中,
// 到消息出消息队列被执行时,这段时间里检查重复请求问题。
// 关于此次的三种运算符计算过程,此次就不再详细分析了,实现结果就是上面描述的,可自行分析下。
// 该类型media_track_type定义见下面的分析
if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
// 此前相同track类型未请求读取数据时进入
// 缓存当前已请求的(可能多种)track读取数据事件
mPendingReadBufferTypes |= (1 << trackType);
// 立即发送【kWhatReadBuffer】事件消息给GenericSource自身的消息循环AHandler处理者即自身来接收处理
// 见下面的分析
sp<AMessage> msg = new AMessage(kWhatReadBuffer, this);
msg->setInt32("trackType", trackType);
msg->post();
}
}
media_track_type参数是个枚举类型,表示媒体track类型:
该枚举类型必须和java层MediaPlayer中的保持一致。
// [frameworks/av/media/libmedia/include/media/mediaplayer_common.h]
// Keep MEDIA_TRACK_TYPE_* in sync with MediaPlayer.java.
enum media_track_type {
MEDIA_TRACK_TYPE_UNKNOWN = 0,
MEDIA_TRACK_TYPE_VIDEO = 1,
MEDIA_TRACK_TYPE_AUDIO = 2,
MEDIA_TRACK_TYPE_TIMEDTEXT = 3,
MEDIA_TRACK_TYPE_SUBTITLE = 4,
MEDIA_TRACK_TYPE_METADATA = 5,
};
GenericSource接收处理【kWhatReadBuffer】事件消息:
Android native层媒体通信架构AHandler/ALooper机制实现源码分析
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
Mutex::Autolock _l(mLock);
switch (msg->what()) {
case kWhatReadBuffer:
{
onReadBuffer(msg);
break;
}
}
}
onReadBuffer(msg)实现分析:
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp]
void NuPlayer::GenericSource::onReadBuffer(const sp<AMessage>& msg) {
// 获取当前读取数据请求的track类型
int32_t tmpType;
CHECK(msg->findInt32("trackType", &tmpType));
// 可以强转为对应值的枚举
media_track_type trackType = (media_track_type)tmpType;
// 根据上面的分析,可知此处的运算符计算作用:
// 就是在当前消息事件被接收处理时去掉记录的当前track请求类型。
// 注意:~运算符的作用是将int值的每一位bit取反,即0变成1,1变成0。
mPendingReadBufferTypes &= ~(1 << trackType);
readBuffer(trackType);
}
readBuffer(trackType)实现分析:
该方法只传入了一个参数,实际上有多个参数的,其他参数值为默认值,如下方法声明:
// [frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.h]
struct NuPlayer::GenericSource : public NuPlayer::Source,
public MediaBufferObserver // Modular DRM
{
private:
// When |mode| is MediaPlayerSeekMode::SEEK_CLOSEST, the buffer read shall
// include an item indicating skipping rendering all buffers with timestamp
// earlier than |seekTimeUs|.
// For other modes, the buffer read will not include the item as above in order
// to facilitate fast seek operation.
void readBuffer(
media_track_type trackType,
int64_t seekTimeUs = -1ll,
MediaPlayerSeekMode mode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC,
int64_t *actualTimeUs = NULL, bool formatChange = false);
}
方法定义实现:
由于篇幅长度过长,因此放入下一章节分析,请查看:
【五】Android MediaPlayer整体架构源码分析 -【prepareAsync/prepare数据准备处理流程】【Part 4】