以上四篇对Audio系统的简要分析,由于Audio涉及的范围比较广,以后分析其他子系统时在做详细分析。我们继续Media系统的征程,Media系统仍然是一个庞大的系统,以MediaPlayer为例,贯穿了Java,JNI,C++库,硬件抽象层,OpenMax驱动,涉及到音视频,编解码等内容。

为了简化分析,又不脱离主线,我们仍然从一个Demo实例开始,逐点逐层地取剖析,由于贯穿的篇幅比较长,我们尽可能得以一个完整的知识点为一篇。本篇我们就以MediaPlayer为主。

1. 创建MediaPlayer

MediaPlayer mediaPlayer = new MediaPlayer();  //创建多媒体播放器
mediaPlayer.setDataSource(path);  //设置源数据
mediaPlayer.setDisplay(surfaceView.getHolder());  //播放视频
mediaPlayer.prepare();  //播放器准备
mediaPlayer.start(); //开始播放

frameworks\base\media\java\android\media\MediaPlayer.java
静态代码块在构造函数之前创建的

static {System.loadLibrary("media_jni");native_init(); //初始化
}

frameworks\base\media\jni\android_media_MediaPlayer.cpp

static voidandroid_media_MediaPlayer_native_init(JNIEnv *env)
{jclass clazz;clazz = env->FindClass("android/media/MediaPlayer"); //查找类fields.context = env->GetFieldID(clazz, "mNativeContext", "J"); //绑定上下文fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", //绑定Java方法"(Ljava/lang/Object;IIILjava/lang/Object;)V");fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");env->DeleteLocalRef(clazz);clazz = env->FindClass("android/net/ProxyInfo"); //查找代理信息fields.proxyConfigGetHost =env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");fields.proxyConfigGetPort =env->GetMethodID(clazz, "getPort", "()I");fields.proxyConfigGetExclusionList =env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");env->DeleteLocalRef(clazz);gBufferingParamsFields.init(env);// Modular DRMFIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");gPlaybackParamsFields.init(env);gSyncParamsFields.init(env);gVolumeShaperFields.init(env);
}

frameworks\base\media\java\android\media\MediaPlayer.java

public MediaPlayer() {......native_setup(new WeakReference<MediaPlayer>(this)); //调用JNI层......
}

frameworks\base\media\jni\android_media_MediaPlayer.cpp

static void android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
{sp<MediaPlayer> mp = new MediaPlayer(); //创建native对象// create new listener and give it to MediaPlayersp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);mp->setListener(listener); //设置Java层的回调监听// Stow our new C++ MediaPlayer in an opaque field in the Java object.setMediaPlayer(env, thiz, mp); //存个副本
}

2. 设置播放数据源

static void android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
{sp<MediaPlayer> mp = getMediaPlayer(env, thiz); //取先前保存的mp......int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); //取文件描述ALOGV("setDataSourceFD: fd %d", fd);//setDataSourceprocess_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}

frameworks\av\media\libmedia\mediaplayer.cpp

status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);status_t err = UNKNOWN_ERROR;const sp<IMediaPlayerService> service(getMediaPlayerService()); //服务代理if (service != 0) {sp<IMediaPlayer> player(service->create(this, mAudioSessionId)); //创建Playerif ((NO_ERROR != doSetRetransmitEndpoint(player)) ||(NO_ERROR != player->setDataSource(fd, offset, length))) {player.clear();}err = attachNewPlayer(player); //更新备份}return err;
}

frameworks\av\media\libmediaplayerservice\MediaPlayerService.cpp

sp<IMediaPlayer> MediaPlayerService::create(const sp<IMediaPlayerClient>& client,audio_session_t audioSessionId)
{pid_t pid = IPCThreadState::self()->getCallingPid();int32_t connId = android_atomic_inc(&mNextConnId);//创建一个Clientsp<Client> c = new Client(this, pid, connId, client, audioSessionId,IPCThreadState::self()->getCallingUid());wp<Client> w = c;{Mutex::Autolock lock(mLock);mClients.add(w);}return c;
}
 class Client : public BnMediaPlayer {......}
status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
{//获取Player的Typeplayer_type playerType = MediaPlayerFactory::getPlayerType(this,fd, offset,length);//按类型创建播放器sp<MediaPlayerBase> p = setDataSource_pre(playerType);// now set data sourcesetDataSource_post(p, p->setDataSource(fd, offset, length)); //设置数据源return mStatus;
}

frameworks\av\media\libmediaplayerservice\MediaPlayerFactory.cpp

player_type MediaPlayerFactory::getPlayerType(const sp<IMediaPlayer>& client,int fd,int64_t offset,int64_t length) {GET_PLAYER_TYPE_IMPL(client, fd, offset, length); //调用下面的宏函数
}

泛型宏定义,按score值创建播放器

#define GET_PLAYER_TYPE_IMPL(a...)                      \Mutex::Autolock lock_(&sLock);                      \\player_type ret = STAGEFRIGHT_PLAYER;               \float bestScore = 0.0;                              \\for (size_t i = 0; i < sFactoryMap.size(); ++i) {   \\IFactory* v = sFactoryMap.valueAt(i);           \float thisScore;                                \CHECK(v != NULL);                               \thisScore = v->scoreFactory(a, bestScore);      \if (thisScore > bestScore) {                    \ret = sFactoryMap.keyAt(i);                 \bestScore = thisScore;                      \}                                               \}                                                   \\if (0.0 == bestScore) {                             \ret = getDefaultPlayerType();                   \}                                                   \\return ret;

默认返回的播放器类型

static player_type getDefaultPlayerType() {return NU_PLAYER; //默认返回NuPlayer
}

执行 setDataSource_pre,先去创建播放器

sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(player_type playerType)
{// create the right type of playersp<MediaPlayerBase> p = createPlayer(playerType); //按类型创建播放器sp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder = sm->getService(String16("media.extractor")); //获取提取器if (property_get_bool("persist.media.treble_omx", true)) { //获取 treble 架构下openmax 属性// Treble IOmxsp<IOmx> omx = IOmx::getService(); //获取openmax服务......} else {// Legacy IOMXbinder = sm->getService(String16("media.codec")); //获取解码器服务......}if (!p->hardwareOutput()) {Mutex::Autolock l(mLock);//按Session创建音频输出mAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),mPid, mAudioAttributes);static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);}return p;
}

取创建播放器

sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType)
{// determine if we have the right player typesp<MediaPlayerBase> p = mPlayer;......if (p == NULL) {p = MediaPlayerFactory::createPlayer(playerType, this, notify, mPid); //创建Player}if (p != NULL) {p->setUID(mUid);}return p;
}

调用工厂的createPlayer方法

sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(player_type playerType,void* cookie,notify_callback_f notifyFunc,pid_t pid) {sp<MediaPlayerBase> p;IFactory* factory;status_t init_result;Mutex::Autolock lock_(&sLock);factory = sFactoryMap.valueFor(playerType);CHECK(NULL != factory);p = factory->createPlayer(pid); //调用工厂子类吃醋昂见Player......return p;
}

NuPlayerFactory 继承自 IFactory

class NuPlayerFactory : public MediaPlayerFactory::IFactory {public:........virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {ALOGV(" create NuPlayer");return new NuPlayerDriver(pid); //返回 NuPlayerDriver}
};

frameworks\av\media\libmediaplayerservice\nuplayer\NuPlayerDriver.cpp
去看看NuPlayerDriver做了什么

NuPlayerDriver::NuPlayerDriver(pid_t pid): mState(STATE_IDLE),mIsAsyncPrepare(false),mAsyncResult(UNKNOWN_ERROR),mSetSurfaceInProgress(false),mDurationUs(-1),mPositionUs(-1),mSeekInProgress(false),mPlayingTimeUs(0),mLooper(new ALooper),mPlayer(new NuPlayer(pid)), //新建NuPlayermPlayerFlags(0),mAnalyticsItem(NULL),mAtEOS(false),mLooping(false),mAutoLoop(false) {mLooper->setName("NuPlayerDriver Looper");// set up an analytics recordmAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);mAnalyticsItem->generateSessionID();//注册启动消息循环mLooper->start(false, /* runOnCallingThread */true,  /* canCallJava */PRIORITY_AUDIO);mLooper->registerHandler(mPlayer);mPlayer->setDriver(this);
}

继续上面,setDataSource调用

status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {ALOGV("setDataSource(%p) file(%d)", this, fd);Mutex::Autolock autoLock(mLock);if (mState != STATE_IDLE) {return INVALID_OPERATION;}mState = STATE_SET_DATASOURCE_PENDING;mPlayer->setDataSourceAsync(fd, offset, length);while (mState == STATE_SET_DATASOURCE_PENDING) {mCondition.wait(mLock);}return mAsyncResult;
}
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);sp<GenericSource> source = new GenericSource(notify, mUIDValid, mUID);status_t err = source->setDataSource(fd, offset, length);msg->setObject("source", source);msg->post();mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
}
status_t NuPlayer::GenericSource::setDataSource(int fd, int64_t offset, int64_t length) {resetDataSource(); //重设参数mFd = dup(fd);mOffset = offset;mLength = length;// delay data source creation to prepareAsync() to avoid blocking// the calling thread in setDataSource for any significant time.return OK;
}

3. 播放器准备

void NuPlayer::prepareAsync() {ALOGV("prepareAsync");(new AMessage(kWhatPrepare, this))->post();
}
  case kWhatPrepare:{ALOGV("onMessageReceived kWhatPrepare");mSource->prepareAsync();break;}

frameworks\av\media\libmediaplayerservice\nuplayer\GenericSource.cpp

由AHandler消息处理器调用onPrepareAsync()函数

void NuPlayer::GenericSource::onPrepareAsync() {// delayed data source creationif (mDataSource == NULL) {// set to false first, if the extractor// comes back as secure, set it to true then.mIsSecure = false;if (!mUri.empty()) {const char* uri = mUri.c_str();String8 contentType;if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {mHttpSource = DataSource::CreateMediaHTTP(mHTTPService);if (mHttpSource == NULL) {ALOGE("Failed to create http source!");notifyPreparedAndCleanup(UNKNOWN_ERROR);return;}}mDataSource = DataSource::CreateFromURI(mHTTPService, uri, &mUriHeaders, &contentType,static_cast<HTTPBase *>(mHttpSource.get()));} else {if (property_get_bool("media.stagefright.extractremote", true) &&!FileSource::requiresDrm(mFd, mOffset, mLength, nullptr /* mime */)) {sp<IBinder> binder =defaultServiceManager()->getService(String16("media.extractor")); //获取提取器if (binder != nullptr) {ALOGD("FileSource remote");sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder)); //提取器服务代理sp<IDataSource> source =mediaExService->makeIDataSource(mFd, mOffset, mLength); //数据源代理ALOGV("IDataSource(FileSource): %p %d %lld %lld",source.get(), mFd, (long long)mOffset, (long long)mLength);if (source.get() != nullptr) {mDataSource = DataSource::CreateFromIDataSource(source);if (mDataSource != nullptr) {// Close the local file descriptor as it is not needed anymore.close(mFd);mFd = -1;}} else {ALOGW("extractor service cannot make data source");}} else {ALOGW("extractor service not running");}}if (mDataSource == nullptr) {ALOGD("FileSource local");mDataSource = new FileSource(mFd, mOffset, mLength);}// TODO: close should always be done on mFd, see the lines following// DataSource::CreateFromIDataSource above,// and the FileSource constructor should dup the mFd argument as needed.mFd = -1;}if (mDataSource == NULL) {ALOGE("Failed to create data source!");notifyPreparedAndCleanup(UNKNOWN_ERROR);return;}}if (mDataSource->flags() & DataSource::kIsCachingDataSource) {mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());}// For cached streaming cases, we need to wait for enough// buffering before reporting prepared.mIsStreaming = (mCachedSource != NULL);// init extractor from data sourcestatus_t err = initFromDataSource(); //初始化数据源提取器if (err != OK) {ALOGE("Failed to init from data source!");notifyPreparedAndCleanup(err);return;}if (mVideoTrack.mSource != NULL) {sp<MetaData> meta = doGetFormatMeta(false /* audio */);sp<AMessage> msg = new AMessage;err = convertMetaDataToMessage(meta, &msg);if(err != OK) {notifyPreparedAndCleanup(err);return;}notifyVideoSizeChanged(msg); //通知视频大小改变}notifyFlagsChanged(// FLAG_SECURE will be known if/when prepareDrm is called by the app// FLAG_PROTECTED will be known if/when prepareDrm is called by the appFLAG_CAN_PAUSE |FLAG_CAN_SEEK_BACKWARD |FLAG_CAN_SEEK_FORWARD |FLAG_CAN_SEEK);finishPrepareAsync();ALOGV("onPrepareAsync: Done");
}

从数据源初始化提取器

status_t NuPlayer::GenericSource::initFromDataSource() {sp<IMediaExtractor> extractor;CHECK(mDataSource != NULL);extractor = MediaExtractor::Create(mDataSource, NULL); //从StageFright创建提取器if (extractor == NULL) {ALOGE("initFromDataSource, cannot create extractor!");return UNKNOWN_ERROR;}mFileMeta = extractor->getMetaData(); //提取元数据if (mFileMeta != NULL) {int64_t duration;if (mFileMeta->findInt64(kKeyDuration, &duration)) {mDurationUs = duration;}}int32_t totalBitrate = 0;size_t numtracks = extractor->countTracks(); //获取总Trackif (numtracks == 0) {ALOGE("initFromDataSource, source has no track!");return UNKNOWN_ERROR;}mMimes.clear();for (size_t i = 0; i < numtracks; ++i) {sp<IMediaSource> track = extractor->getTrack(i);if (track == NULL) {continue;}sp<MetaData> meta = extractor->getTrackMetaData(i); //获取每一Track的元数据if (meta == NULL) {ALOGE("no metadata for track %zu", i);return UNKNOWN_ERROR;}const char *mime;CHECK(meta->findCString(kKeyMIMEType, &mime));ALOGV("initFromDataSource track[%zu]: %s", i, mime);// Do the string compare immediately with "mime",// we can't assume "mime" would stay valid after another// extractor operation, some extractors might modify meta// during getTrack() and make it invalid.if (!strncasecmp(mime, "audio/", 6)) { //音频if (mAudioTrack.mSource == NULL) {mAudioTrack.mIndex = i;mAudioTrack.mSource = track;mAudioTrack.mPackets =new AnotherPacketSource(mAudioTrack.mSource->getFormat());if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {mAudioIsVorbis = true;} else {mAudioIsVorbis = false;}mMimes.add(String8(mime));}} else if (!strncasecmp(mime, "video/", 6)) {//视频if (mVideoTrack.mSource == NULL) {mVideoTrack.mIndex = i;mVideoTrack.mSource = track;mVideoTrack.mPackets =new AnotherPacketSource(mVideoTrack.mSource->getFormat());// video always at the beginningmMimes.insertAt(String8(mime), 0);}}mSources.push(track);int64_t durationUs;if (meta->findInt64(kKeyDuration, &durationUs)) {//时长if (durationUs > mDurationUs) {mDurationUs = durationUs;}}int32_t bitrate;if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {//比特率totalBitrate += bitrate;} else {totalBitrate = -1;}}ALOGV("initFromDataSource mSources.size(): %zu  mIsSecure: %d  mime[0]: %s", mSources.size(),mIsSecure, (mMimes.isEmpty() ? "NONE" : mMimes[0].string()));if (mSources.size() == 0) {ALOGE("b/23705695");return UNKNOWN_ERROR;}// Modular DRM: The return value doesn't affect source initialization.(void)checkDrmInfo();mBitrate = totalBitrate;return OK;
}

篇幅过长,暂且到此为止,下一篇接着从StageFright的数据提取,解码来分析,可以猜测从多种数据源提取多媒体元数据后经由openmax解码后输出;预知后事如何请听下回分解。

Android8.0 Media系统(一)相关推荐

  1. Android8.0 Audio系统之AudioFlinger

    继上一篇AudioTrack的分析,本篇我们来看AudioFlinger,AF主要承担音频混合输出,是Audio系统的核心,从AudioTrack来的数据最终都会在这里处理,并被写入到Audio的HA ...

  2. Android8.0 Audio系统之AudioPolicy

    上一篇我们跟踪分析了AudioFlinger,它是Audio系统的核心,但是AudioFlinger却不能脱离AudioPolicy工作.AudioPolicy模块承载着音频切换,音轨路由的重要工作, ...

  3. Android8.0 蓝牙系统

    Android 提供支持经典蓝牙和蓝牙低功耗的默认蓝牙堆栈.借助蓝牙,Android 设备可以创建个人区域网络,以便通过附近的蓝牙设备发送和接收数据,在 Android 4.3 及更高版本中,Andr ...

  4. Android8.0 USB系统框架

    USB(通用串行总线)主机模式向外设进行供电,使 Android 设备能够驱动 USB 总线,并且可以使用各种 USB 外设(包括音频接口,存储,MIDI),USB 和蓝牙低功耗连接都可以用于传输 M ...

  5. Android8.0 USB系统框架

    USB(通用串行总线)主机模式向外设进行供电,使 Android 设备能够驱动 USB 总线,并且可以使用各种 USB 外设(包括音频接口,存储,MIDI),USB 和蓝牙低功耗连接都可以用于传输 M ...

  6. Android8.0以上系统ROOT时,Magisk框架替代SpuerSU

    9月末,我负责的一个手机评测业务,有ROOT手机的刚需. 手机是刚上市华为Nova3,Android版本8.1. 按照"祖传"刷机手法,使用SuperSU来ROOT手机一直没有成功 ...

  7. android8.0调用系统浏览器,ie浏览器在线使用,ie浏览器8.0手机安卓版-

    Ie浏览器是微软开发的浏览器,这种浏览器是目前最常见的兼容性最强的浏览器. Ie浏览器也是世界上最早期出现的网络浏览器,主要是用于查资料和上网使用的. 过去的年代里没有太多的播放器和图片查看软件,人们 ...

  8. 华为android贡献度,Android8.0系统占比大幅提升,华为、荣耀、小米成为主要贡献...

    原标题:Android8.0系统占比大幅提升,华为.荣耀.小米成为主要贡献 安卓推出Android8.0系统至今已半年有余,如今Android8.1稳定版.Android9.0开发者预览版也发布了,但 ...

  9. Android8.0及以上系统 WiFi热点 版本适配

    代码地址如下: http://www.demodashi.com/demo/13907.html ###一.准备工作 开发环境:  jdk1.8  AS(3.0.1) 运行环境:  华为V10(And ...

最新文章

  1. c语言把一个数组赋值给另一个数组_如何把一个固定数组的值传递给另外一个数组...
  2. 安全设置Windows组策略 有效阻止黑客
  3. XamarinSQLite教程添加索引
  4. 大学python用什么教材-Python大学教程(普通高等教育十三五规划教材)
  5. replace和replaceAll
  6. 生活质量衡量系统_数据质量与数据质量八个维度指标
  7. 黑客攻防技术宝典Web实战篇第2版—第11章 攻击应用程序逻辑
  8. kafka集群部署成功后,创建生产者往指定主题里面发送消息时出错
  9. Intent的一些简单用法
  10. mysql 插入数据 自增长_mysql插入数据后返回自增ID的方法
  11. 通达信标记符号_通达信49个图标,高手指教,通达信软件里的股票标记符号,能增加吗...
  12. 解决无法卸载vmware bridge protocol功能,错误0×8007007E
  13. android11 前摄相头隐藏闪光灯图标
  14. Python入门篇(下)
  15. 关于语音会议自动记录的大概设计思路
  16. 如何实时计算日累计逐单资金流
  17. presto sql 求占比--开窗函数解法
  18. 安卓手机玩游戏卡顿怎么解决_解决安卓手机卡顿反应慢的9个技巧
  19. # 2017-2018-1 20155232《信息安全技术》实验二——Windows口令破解
  20. nuxt的asyncData在方法内没有办法通过 this调用

热门文章

  1. HAUT 1261地狱飞龙 自适应辛普森 数值积分
  2. python中列表嵌套字典/列表排序,字典排序,列表排序。
  3. Unify学习笔记1
  4. 关于js中无法绑定点击事件
  5. 学习如何使用 Python 将你的照片变成卡通版本
  6. 微信电商小程序多商户入驻拼团秒杀源码
  7. 【进阶版】机器学习之特征降维、超参数调优及检验方法(04)
  8. C语言实战题目:从键盘输入数字,计算其中正数的个数,并且计算所有正数的和
  9. Java实现发送手机验证码
  10. 免费在线markdown转pdf