MediaPlayer视频播放流程

MediaPlayer视频播放流程(基于Android8.0)

  • 1. MediaPlayer源码分析

    • 1.0

      public class MediaPlayer extends PlayerBase implements SubtitleController.Listener, VolumeAutomation, AudioRouting
      

      MediaPayer继承自PlayerBase类,分别实现了SubtitleController VolumeAutomation AudioRouting接口

      • frameworks/base/media/java/android/media/PlayerBase.java

        PlayeBase是一个抽象类,封装了常规的播放器操作,如start pause stop release

        baseRegisterPlayer(),获取AppOpsService对象,并通过startWatchingMode对播放音乐的权限OP_PLAY_AUDIO进行监听

        protected void baseRegisterPlayer() {int newPiid = AudioPlaybackConfiguration.PLAYER_PIID_INVALID;//获取AppOpsService对象IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);mAppOps = IAppOpsService.Stub.asInterface(b);// initialize mHasAppOpsPlayAudio//检查权限updateAppOpsPlayAudio();// register a callback to monitor whether the OP_PLAY_AUDIO is still allowedmAppOpsCallback = new IAppOpsCallbackWrapper(this);try {//注册了权限的监听mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO,ActivityThread.currentPackageName(), mAppOpsCallback);} catch (RemoteException e) {Log.e(TAG, "Error registering appOps callback", e);mHasAppOpsPlayAudio = false;}try {//调用了AudioService的 trackPlayer,主要将mediaplayer统一管理起来,已方便提供 duck,mediaplayer的状态监听啊,以及外部对mediaplayer控制等功能newPiid = getService().trackPlayer(new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this)));} catch (RemoteException e) {Log.e(TAG, "Error talking to audio service, player will not be tracked", e);}mPlayerIId = newPiid;
        }
        

        在MediaPlayer()构造函数中调用

        public MediaPlayer() {//通过super在playerBase创建一个默认的AudioAttributes,以及player类             //PLAYER_TYPE_JAM_MEDIAPLAYER 表示MediaPlayer super(new AudioAttributes.Builder().build(),AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);//获取当前线程的looper,如果当前线程没有lopper那么就获取主线程的looperLooper looper;if ((looper = Looper.myLooper()) != null) {//创建一个handler主要用于控制mediaplayer的播放 mEventHandler = new EventHandler(this, looper);} else if ((looper = Looper.getMainLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else {mEventHandler = null;}//控制播放时间和播放进度的相关mTimeProvider = new TimeProvider(this);//创建了一个InputStream向量集合 mOpenSubtitleSources = new Vector<InputStream>();//通过jni向下调用,初始化native_setup(new WeakReference<MediaPlayer>(this));baseRegisterPlayer();}
        
      • frameworks/base/media/java/android/media/SubtitleController.java

        frameworks/base/media/java/android/media/SubtitleTrack.java
        frameworks/base/media/java/android/media/SubtitleData.java

        用于为用户显示字幕,允许指定要显示的轨迹、定位点,还允许添加外部带外字幕曲目

      • frameworks/base/media/java/android/media/VolumeAutomation.java

        frameworks/base/media/java/android/media/VolumeShaper.java
        frameworks/base/media/jni/android_media_VolumeShaper.h等

        主要用于音频应用的淡入,淡出,淡入淡出,淡入淡出以及其他短暂的自动音量转换,音量控制通过VolumeShaper.Configuration实现,必须要在8.0及以后才能使用。

    • 2.0

      加载so库

      static {System.loadLibrary("media_jni");native_init();
      }
      
    • 3.0

      初始化

      public MediaPlayer() {......//通过jni向下调用,初始化native_setup(new WeakReference<MediaPlayer>(this));......
      }
      
      • frameworks/base/media/jni/android_media_MediaPlayer.cpp
        static void
        android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
        {ALOGV("native_setup");// 创建一个MediaPlayer对象(frameworks/av/media/libmedia/mediaplayer.cpp)sp<MediaPlayer> mp = new MediaPlayer();if (mp == NULL) {jniThrowException(env, "java/lang/RuntimeException", "Out of memory");return;}// 为MediaPlayer设置listener,目的就是通过callback的方式将player的事件上传至   java层,以便用户做出对应的处理// create new listener and give it to MediaPlayersp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);mp->setListener(listener);// Stow our new C++ MediaPlayer in an opaque field in the Java object.setMediaPlayer(env, thiz, mp);
        }
        
        //JNIMediaPlayerListener类
        class JNIMediaPlayerListener: public MediaPlayerListener
        {
        public:JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);~JNIMediaPlayerListener();virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
        private:JNIMediaPlayerListener();jclass      mClass;     // Reference to MediaPlayer classjobject     mObject;    // Weak ref to MediaPlayer Java object to call on
        };......//callback事件的传递
        void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
        {JNIEnv *env = AndroidRuntime::getJNIEnv();if (obj && obj->dataSize() > 0) {// 创建java层Parcel对象对应的JNI类型对象(JNI本地引用对象)jobject jParcel = createJavaParcelObject(env);if (jParcel != NULL) {// 然后获取该Java层Parcel对象对应的native层Parcel对象指针(值)// 然后缓存当前obj额外参数值及其大小Parcel* nativeParcel = parcelForJavaObject(env, jParcel);nativeParcel->setData(obj->data(), obj->dataSize());// 最后调用java层事件回调静态方法【fields.post_event】通知java层// fields.post_event对应于MediaPlayer的java方法 postEventFromNativeenv->CallStaticVoidMethod(mClass, fields.post_event, mObject,msg, ext1, ext2, jParcel);// 释放创建的java层Parcel对象的JNI本地引用对象env->DeleteLocalRef(jParcel);}} else {// 若无额外参数,则直接调用通知java层env->CallStaticVoidMethod(mClass, fields.post_event, mObject,msg, ext1, ext2, NULL);}// 检查【post_event】方法调用是否有异常,若异常则需要清空该异常等处理// 备注:当一个JNI函数返回一个明确的错误码时,仍可用ExceptionCheck来检查是否有异常发生。// 但是,用返回的错误码来判断比较高效。一旦JNI函数的返回值是一个错误码,// 那么接下来调用ExceptionCheck肯定会返回JNI_TRUE。if (env->ExceptionCheck()) {ALOGW("An exception occurred while notifying an event.");LOGW_EX(env);env->ExceptionClear();}
        }......static void
        android_media_MediaPlayer_native_init(JNIEnv *env)
        {jclass clazz;clazz = env->FindClass("android/media/MediaPlayer");if (clazz == NULL) {return;}fields.context = env->GetFieldID(clazz, "mNativeContext", "J");if (fields.context == NULL) {return;}// 获取静态方法postEventFromNativefields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");if (fields.post_event == NULL) {return;}......
        }.....static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
        {Mutex::Autolock l(sLock);sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);if (player.get()) {player->incStrong((void*)setMediaPlayer);}if (old != 0) {old->decStrong((void*)setMediaPlayer);}// 将mediaplayer对象指针给与了java层的mediaplayer的mNativeContext字段env->SetLongField(thiz, fields.context, (jlong)player.get());return old;
        }
        
      • frameworks/av/media/libmedia/include/media/mediaplayer.h
        // ref-counted object for callbacks
        class MediaPlayerListener: virtual public RefBase
        {
        public:virtual void notify(int msg, int ext1, int ext2, const Parcel *obj) = 0;
        };
        
      • framework/av/media/libmedia/mediaplayer.cpp
        status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
        {ALOGV("setListener");Mutex::Autolock _l(mLock);mListener = listener;return NO_ERROR;
        }......void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
        {ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);bool send = true;bool locked = false;......case MEDIA_ERROR:// Always log errors.// ext1: Media framework error code.// ext2: Implementation dependant error code.ALOGE("error (%d, %d)", ext1, ext2);mCurrentState = MEDIA_PLAYER_STATE_ERROR;// 错误发生时,【mPrepareSync】该标识为true时,// 表示当前执行的是同步prepare准备流程,非异步执行prepareAsync流程if (mPrepareSync){ALOGV("signal application thread");mPrepareSync = false;mPrepareStatus = ext1;mSignal.signal();// prepare同步执行功能失败时,置为falsesend = false;}......ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);sp<MediaPlayerListener> listener = mListener;if (locked) mLock.unlock();// this prevents re-entrant calls into client codeif ((listener != 0) && send) {// 此处根据上面的分析可知,listener不为空并且send为true(即当prepare同步执行过程中无错误发生)时,// 进行加锁调用通知java层该事件Mutex::Autolock _l(mNotifyLock);ALOGV("callback application");listener->notify(msg, ext1, ext2, obj);ALOGV("back from callback");}
        }
    • 4.0

      setDataSource(文件类型)

      public void setDataSource(FileDescriptor fd, long offset, long length)throws IOException, IllegalArgumentException, IllegalStateException {_setDataSource(fd, offset, length);
      }private native void _setDataSource(FileDescriptor fd, long offset, long length)throws IOException, IllegalArgumentException, IllegalStateException;
      
      • frameworks/base/media/jni/android_media_MediaPlayer.cpp
        {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},......static void
        android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
        {// 获取mediaplayer指针对象sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}if (fileDescriptor == NULL) {jniThrowException(env, "java/lang/IllegalArgumentException", NULL);return;}// 在jni中获取文件描述符int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);ALOGV("setDataSourceFD: fd %d", fd);// 处理mediaplayer指针对象对于setDataSource的结果process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
        }......// 获取java层MediaPlayer对象的mNativeContext字段,转为native层mediaplayer对象
        static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
        {Mutex::Autolock l(sLock);MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);return sp<MediaPlayer>(p);
        }......// 对setDataSource的结果进行处理
        static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
        {   // 不抛出异常,直接向上层反馈错误事件if (exception == NULL) {  // Don't throw exception. Instead, send an event.if (opStatus != (status_t) OK) {sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);}} else {  // Throw exception!if ( opStatus == (status_t) INVALID_OPERATION ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);} else if ( opStatus == (status_t) BAD_VALUE ) {jniThrowException(env, "java/lang/IllegalArgumentException", NULL);} else if ( opStatus == (status_t) PERMISSION_DENIED ) {jniThrowException(env, "java/lang/SecurityException", NULL);} else if ( opStatus != (status_t) OK ) {if (strlen(message) > 230) {// if the message is too long, don't bother displaying the status codejniThrowException( env, exception, message);} else {char msg[256];// append the status code to the messagesprintf(msg, "%s: status=0x%X", message, opStatus);jniThrowException( env, exception, msg);}}}
        }
        
      • frameworks/av/media/libmedia/mediaplayer.cpp
        status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
        {// //用于记录framework APIs返回成功与否ALOGV("setDataSource(%d, %" PRId64 ", %" PRId64 ")", fd, offset, length);status_t err = UNKNOWN_ERROR;//getMediaPlayerService()函数定义域framework/av/media/libmedia/ IMediaDeathNotifier.cpp中//getMediaPlayerService()返回的是MediaPlayerService服务在客户端的代理对象BpMediaPlayerServiceconst sp<IMediaPlayerService> service(getMediaPlayerService());if (service != 0) {//调用IMediaPlayerService中的create()函数查询服务//并返回一个调用IMediaPlayer对象sp<IMediaPlayer> player(service->create(this, mAudioSessionId));//调用服务端的setDataSource(int fd, int64_t offset, int64_t length)方法if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||(NO_ERROR != player->setDataSource(fd, offset, length))) {player.clear();}//重置一些变量,更新MediaPlayer对象err = attachNewPlayer(player);}return err;
        }
        
      • frameworks/av/media/libmedia/IMediaDeathNotifier.cpp
        /*static*/const sp<IMediaPlayerService>
        IMediaDeathNotifier::getMediaPlayerService()
        {ALOGV("getMediaPlayerService");Mutex::Autolock _l(sServiceLock);if (sMediaPlayerService == 0) {//获取ServiceManagersp<IServiceManager> sm = defaultServiceManager();sp<IBinder> binder;do {//获取MediaPlayer服务的binder的对象binder = sm->getService(String16("media.player"));if (binder != 0) {break;}ALOGW("Media player service not published, waiting...");usleep(500000); // 0.5 s} while (true);if (sDeathNotifier == NULL) {sDeathNotifier = new DeathNotifier();}binder->linkToDeath(sDeathNotifier);//将binder对象转换为MediaPlayerServicesMediaPlayerService = interface_cast<IMediaPlayerService>(binder);}ALOGE_IF(sMediaPlayerService == 0, "no media player service!?");return sMediaPlayerService;
        }
        
      • frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
        //存储各种参数到client对象中,方便client通过IPC调用
        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);sp<Client> c = new Client(this, pid, connId, client, audioSessionId,IPCThreadState::self()->getCallingUid());ALOGV("Create new client(%d) from pid %d, uid %d, ", connId, pid,IPCThreadState::self()->getCallingUid());wp<Client> w = c;{Mutex::Autolock lock(mLock);mClients.add(w);}return c;
        }
        
        status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length)
        {ALOGV("setDataSource fd=%d (%s), offset=%lld, length=%lld",fd, nameForFd(fd).c_str(), (long long) offset, (long long) length);//stat结构体主要用来描述系统文件中文件的属性结构,如, 修改时间,文件用户标识等struct stat sb;//fstat获取文件相关信息保持在stat结构体中,为0表示返回成功int ret = fstat(fd, &sb);if (ret != 0) {ALOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));return UNKNOWN_ERROR;}ALOGV("st_dev  = %llu", static_cast<unsigned long long>(sb.st_dev));ALOGV("st_mode = %u", sb.st_mode);ALOGV("st_uid  = %lu", static_cast<unsigned long>(sb.st_uid));ALOGV("st_gid  = %lu", static_cast<unsigned long>(sb.st_gid));ALOGV("st_size = %llu", static_cast<unsigned long long>(sb.st_size));//做一些检查,判断输入参数的合法性if (offset >= sb.st_size) {ALOGE("offset error");return UNKNOWN_ERROR;}if (offset + length > sb.st_size) {length = sb.st_size - offset;ALOGV("calculated length = %lld", (long long)length);}//调用getPlayerType函数,通过评分机制,获取最优播放器类型player_type playerType = MediaPlayerFactory::getPlayerType(this,fd,offset,length);//创建播放器sp<MediaPlayerBase> p = setDataSource_pre(playerType);if (p == NULL) {return NO_INIT;}// now set data source//在创建的播放器中,设置播放器的数据源return mStatus = setDataSource_post(p, p->setDataSource(fd, offset, length));
        }......sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(player_type playerType)
        {ALOGV("player type = %d", playerType);// Mediatek Android Patch Beginif (playerType == MST_PLAYER) {mIsMstplayer = true;}// Mediatek Android Patch End// create the right type of player//根据播放器类型创建播放器sp<MediaPlayerBase> p = createPlayer(playerType);if (p == NULL) {return p;}sp<IServiceManager> sm = defaultServiceManager();//获取MediaExtractor服务的binder对象sp<IBinder> binder = sm->getService(String16("media.extractor"));if (binder == NULL) {ALOGE("extractor service not available");return NULL;}//绑定提取器extractor死亡通知sp<ServiceDeathNotifier> extractorDeathListener =new ServiceDeathNotifier(binder, p, MEDIAEXTRACTOR_PROCESS_DEATH);binder->linkToDeath(extractorDeathListener);//获取解码器IOmx服务sp<IOmx> omx = IOmx::getService();if (omx == nullptr) {ALOGE("IOmx service is not available");return NULL;}//绑定解码器死亡通知sp<ServiceDeathNotifier> codecDeathListener =new ServiceDeathNotifier(omx, p, MEDIACODEC_PROCESS_DEATH);omx->linkToDeath(codecDeathListener, 0);Mutex::Autolock lock(mLock);clearDeathNotifiers_l();mExtractorDeathListener = extractorDeathListener;mCodecDeathListener = codecDeathListener;mAudioDeviceUpdatedListener = new AudioDeviceUpdatedNotifier(p);if (!p->hardwareOutput()) {//初始化AudioOutput,并且将其设置给player//AudioOutput继承自AudioSink,其头文件中的注释:音频输出的抽象层//NU_PLAYER播放器的实现在Nuplayer.cpp中 setAudioSinkmAudioOutput = new AudioOutput(mAudioSessionId, IPCThreadState::self()->getCallingUid(),mPid, mAudioAttributes, mAudioDeviceUpdatedListener);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 = getPlayer();if ((p != NULL) && (p->playerType() != playerType)) {ALOGV("delete player");p.clear();}//根据播放器类型创建播放器,并设置监听和pid号if (p == NULL) {p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid);}//设置uidif (p != NULL) {p->setUID(mUid);}return p;
        }
        
      • frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp
        sp<MediaPlayerBase> MediaPlayerFactory::createPlayer(player_type playerType,const sp<MediaPlayerBase::Listener> &listener,pid_t pid) {sp<MediaPlayerBase> p;IFactory* factory;status_t init_result;Mutex::Autolock lock_(&sLock);if (sFactoryMap.indexOfKey(playerType) < 0) {ALOGE("Failed to create player object of type %d, no registered"" factory", playerType);return p;}factory = sFactoryMap.valueFor(playerType);CHECK(NULL != factory);//各个工厂类去创建对应的Player//如://playervirtual sp<MediaPlayerBase> createPlayer(pid_t pid) {//ALOGV(" create NuPlayer");//return new NuPlayerDriver(pid);//}p = factory->createPlayer(pid);if (p == NULL) {ALOGE("Failed to create player object of type %d, create failed",playerType);return p;}init_result = p->initCheck();if (init_result == NO_ERROR) {p->setNotifyCallback(listener);} else {ALOGE("Failed to create player object of type %d, initCheck failed"" (res = %d)", playerType, init_result);p.clear();}return p;
        }
        

      setDataSource(网络视频类型)

      public void setDataSource(String path)throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {setDataSource(path, null, null);
      }/**
      * Sets the data source (file-path or http/rtsp URL) to use.
      */
      public void setDataSource(String path, Map<String, String> headers)throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {setDataSource(path, headers, null);
      }private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
      {String[] keys = null;String[] values = null;if (headers != null) {keys = new String[headers.size()];values = new String[headers.size()];int i = 0;for (Map.Entry<String, String> entry: headers.entrySet()) {keys[i] = entry.getKey();values[i] = entry.getValue();++i;}}setDataSource(path, keys, values, cookies);
      }private void setDataSource(String path, String[] keys, String[] values,List<HttpCookie> cookies)throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {//使用Uri解析文件路径final Uri uri = Uri.parse(path);//如果本地路径,函数返回值为file,如果是网络地址,返回值为"http"final String scheme = uri.getScheme();if ("file".equals(scheme)) {//本地路径,直接赋值给pathpath = uri.getPath();} else if (scheme != null) {// handle non-file sources//播放网络视频nativeSetDataSource(MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),path,keys,values);return;}//播放本地视频final File file = new File(path);if (file.exists()) {FileInputStream is = new FileInputStream(file);FileDescriptor fd = is.getFD();setDataSource(fd);is.close();} else {throw new IOException("setDataSource failed.");}
      }private native void nativeSetDataSource(IBinder httpServiceBinder, String path, String[] keys, String[] values)throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
      
      • frameworks/base/media/jni/android_media_MediaPlayer.cpp
        {"nativeSetDataSource","(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;""[Ljava/lang/String;)V",(void *)android_media_MediaPlayer_setDataSourceAndHeaders
        },......static void
        android_media_MediaPlayer_setDataSourceAndHeaders(JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,jobjectArray keys, jobjectArray values) {// 同播放本地视频一样,获取java层的mNativeContext,即mediaplayer指针对象sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp == NULL ) {//对象为空,抛出异常jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}if (path == NULL) {//地址为空,抛出异常jniThrowException(env, "java/lang/IllegalArgumentException", NULL);return;}const char *tmp = env->GetStringUTFChars(path, NULL);if (tmp == NULL) {  // Out of memoryreturn;}ALOGV("setDataSource: path %s", tmp);String8 pathStr(tmp);//将地址添加到pathStr中env->ReleaseStringUTFChars(path, tmp);tmp = NULL;// We build a KeyedVector out of the key and val arrays// 转换播放请求头KeyedVector<String8, String8> headersVector;if (!ConvertKeyValueArraysToKeyedVector(env, keys, values, &headersVector)) {return;}sp<IMediaHTTPService> httpService;if (httpServiceBinderObj != NULL) {sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);//获取MediaHttpService对象httpService = interface_cast<IMediaHTTPService>(binder);}// Mediatek Android Patch Beginbool bNWayPlayback = false;int keyValue;for (size_t i = 0; i < headersVector.size(); ++i) {//如果请求头中不包含set_multiple_player字段,需要回放???if (!strncmp(headersVector.keyAt(i), "set_multiple_player", strlen("set_multiple_player"))) {bNWayPlayback = true;keyValue = atoi(headersVector.valueAt(i));headersVector.removeItemsAt(i, 1);break;}}// Mediatek Android Patch End// 调用mp对象setDataSourcestatus_t opStatus =mp->setDataSource(httpService,pathStr,headersVector.size() > 0? &headersVector : NULL);// 处理setDataSource结果process_media_player_call(env, thiz, opStatus, "java/io/IOException","setDataSource failed." );// Mediatek Android Patch Begin// 回放这段视频if (bNWayPlayback) {Parcel data;data.writeInt32(keyValue);opStatus = mp->setParameter(KEY_PARAMETER_SET_MULTIPLE_PLAYER, data);process_media_player_call(env, thiz, opStatus, "java/io/IOException", "setParameter failed." );}// Mediatek Android Patch End
        }
        
      • frameworks/av/media/libmedia/mediaplayer.cpp
        status_t MediaPlayer::setDataSource(const sp<IMediaHTTPService> &httpService,const char *url, const KeyedVector<String8, String8> *headers)
        {ALOGV("setDataSource(%s)", url);status_t err = BAD_VALUE;if (url != NULL) {//getMediaPlayerService()函数定义域framework/av/media/libmedia/IMediaDeathNotifier.cpp中//getMediaPlayerService()返回的是MediaPlayerService服务在客户端的代理对象BpMediaPlayerServiceconst sp<IMediaPlayerService> service(getMediaPlayerService());if (service != 0) {//调用IMediaPlayerService中的create()函数查询服务//并返回一个调用IMediaPlayer对象sp<IMediaPlayer> player(service->create(this, mAudioSessionId));if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||(NO_ERROR != player->setDataSource(httpService, url, headers))) {player.clear();}//重置一些变量,更新MediaPlayer对象err = attachNewPlayer(player);}}return err;
        }
        
      • frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
        status_t MediaPlayerService::Client::setDataSource(const sp<IMediaHTTPService> &httpService,const char *url,const KeyedVector<String8, String8> *headers)
        {ALOGV("setDataSource(%s)", url);if (url == NULL)return UNKNOWN_ERROR;//如果播放的视频是网络视频,需要检查网络权限if ((strncmp(url, "http://", 7) == 0) ||(strncmp(url, "https://", 8) == 0) ||(strncmp(url, "rtsp://", 7) == 0)) {if (!checkPermission("android.permission.INTERNET")) {return PERMISSION_DENIED;}}//如果播放的是本地视频if (strncmp(url, "content://", 10) == 0) {// get a filedescriptor for the content Uri and// pass it to the setDataSource(fd) methodString16 url16(url);//通过provider获取到fd对象,然后走本地播放的流程int fd = android::openContentProviderFile(url16);if (fd < 0){ALOGE("Couldn't open fd for %s", url);return UNKNOWN_ERROR;}status_t status = setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatusclose(fd);return mStatus = status;} else {// Mediatek Android Patch Beginconst char *DLNA_FEATURE = "DLNA.FEATURE";const char *IMAGE_FEATURE = "IMAGE.FEATURE";const char *CHINADRM_FEATURE = "CHINADRM.FEATURE";player_type playerType;size_t idx;//如果播放请求头中指定了对应播放器,则使用对应的播放器类型if (headers != NULL) {for (idx = 0; idx < headers->size(); idx++) {if (!strncmp(headers->keyAt(idx), DLNA_FEATURE, strlen(DLNA_FEATURE)) && atoi(headers->valueAt(idx)) > 0) {playerType = MST_PLAYER;break;}if (!strncmp(headers->keyAt(idx), IMAGE_FEATURE, strlen(IMAGE_FEATURE)) && atoi(headers->valueAt(idx)) > 0) {playerType = IMG_PLAYER;break;}if (!strncmp(headers->keyAt(idx), CHINADRM_FEATURE, strlen(CHINADRM_FEATURE)) && atoi(headers->valueAt(idx)) > 0) {ALOGW("CHINADRM.FEATURE is enable for %s, switch to NuPlayer", url);playerType = NU_PLAYER;break;}}}if (headers == NULL || idx == headers->size()) {//如果没有指定播放器,调用getPlayerType函数,通过评分机制,获取最优播放器类型playerType = MediaPlayerFactory::getPlayerType(this, url, httpService);}// Mediatek Android Patch End//创建播放器sp<MediaPlayerBase> p = setDataSource_pre(playerType);if (p == NULL) {return NO_INIT;}//在创建的播放器中,设置播放器的数据源return mStatus =setDataSource_post(p, p->setDataSource(httpService, url, headers));}
        
      • 播放器评分机制

        • mediaplayer和mediacodec最终的调用关系
          在这里插入图片描述

        不管是上层的MediaPlayer还是framework层的MediaPlayer都不是不能说是真正的播放器。真正播放器是底层的NuPlayer和TestPlayer。当应用层的MediaPlayer通过JNI调用了framework层的MediaPlayer,最终调用到MediaPlayerService。MediaPlayerService根据评分机制,比较NuPlayer和TestPlayer的得分,实例化得分最高的。这便是最终使用的底层播放器。

        • 考虑到扩展性,google工程师们使用了工厂模式,来管理播放器的创建和评分机制。这样,出了Android自己的内置播放器,各大Rom厂商也可以实现自己的播放器。我们来看看播放器相关工厂类的类图。
          在这里插入图片描述

        播放器的创建,会使用MediaPlayerFactory的createPlayer函数。MediaPlayerFactory的createPlayer函数中,会根据片源情况,调用注册在tFactoryMap中的各个IFactory实现,获取对应的分值,获得最大分值的工厂类,调用该工厂类的createPlayer函数,最终创建出指定的播放器。

        • 注册播放器工厂的目的,是初始化tFactoryMap,以便通过打分机制比较出最优的播放器,最终创建出对应的播放器。
          初次调用注册播放器工厂的函数是在MediaPlayerService的构造器中,MediaPlayerService作为基础服务,在Android世界启动的时候,便会调用。
          frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
          MediaPlayerService::MediaPlayerService()
          {ALOGV("MediaPlayerService created");mNextConnId = 1;// Mediatek Android Patch Begin#ifdef BUILD_WITH_TEEMDrv_SYS_GlobalInit();TEEC_Initialize();#endifif (IS_VR360_SUPPORTED)Mdrv_SYS_VR360_Restore_BW_Setting();// Mediatek Android Patch End// 注册播放器工厂MediaPlayerFactory::registerBuiltinFactories();
          }
          
          frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
          void MediaPlayerFactory::registerBuiltinFactories() {Mutex::Autolock lock_(&sLock);if (sInitComplete)return;IFactory* factory = new NuPlayerFactory();if (registerFactory_l(factory, NU_PLAYER) != OK)delete factory;factory = new TestPlayerFactory();if (registerFactory_l(factory, TEST_PLAYER) != OK)delete factory;// Mediatek Android Patch Begin
          #ifdef BUILD_WITH_MSTAR_MMregisterFactory_l(new MstPlayerFactory(), MST_PLAYER);
          #ifdef SUPPORT_IMAGEPLAYERregisterFactory_l(new ImagePlayerFactory(), IMG_PLAYER);
          #endif
          #ifdef SUPPORT_MSTARPLAYERregisterFactory_l(new MstarPlayerFactory(), MSTAR_PLAYER);
          #endif
          #endifregisterVendorFactories();// Mediatek Android Patch EndsInitComplete = true;
          }......//将播放器类型作为key,具体的播放器工厂作为值存储在tFactoryMap中
          status_t MediaPlayerFactory::registerFactory_l(IFactory* factory,player_type type) {if (NULL == factory) {ALOGE("Failed to register MediaPlayerFactory of type %d, factory is"" NULL.", type);return BAD_VALUE;}if (sFactoryMap.indexOfKey(type) >= 0) {ALOGE("Failed to register MediaPlayerFactory of type %d, type is"" already registered.", type);return ALREADY_EXISTS;}if (sFactoryMap.add(type, factory) < 0) {ALOGE("Failed to register MediaPlayerFactory of type %d, failed to add"" to map.", type);return UNKNOWN_ERROR;}return OK;
          }
          
        • 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);
          }......#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);// 这里的a是一个参数数组,包含所有getPlayerType的入参                               thisScore = v->scoreFactory(a, bestScore);// 如果当前分数比最高得分大,则替换掉最高得分       if (thisScore > bestScore) { // 同时将播放器类型交给ret以便返回                    ret = sFactoryMap.keyAt(i);                 \bestScore = thisScore;                      \}                                               \}                                                   \//分数都为0的时候,选择默认播器                                                    if (0.0 == bestScore) {                             \ret = getDefaultPlayerType();                   \}                                                   \\return ret;......static player_type getDefaultPlayerType() {// Mediatek Android Patch Begin
          //如果是mstar平台,根据属性值选择播放器
          #ifdef BUILD_WITH_MSTAR_MMchar value[PROPERTY_VALUE_MAX];if (property_get("ms.sf", value, NULL)&& (!strcmp("1", value) || !strcasecmp("true", value))) {return STAGEFRIGHT_PLAYER;}if (property_get("ms.nu", value, NULL)&& (!strcmp("1", value) || !strcasecmp("true", value))) {return NU_PLAYER;}return MST_PLAYER;
          #endif// Mediatek Android Patch End//如果不是mstar平台,默认NU_PLAYERreturn NU_PLAYER;
          }
          
        • frameworks/av/include/media/MediaPlayerInterface.h
          enum player_type {STAGEFRIGHT_PLAYER = 3,//Android7.0之后已被删除NU_PLAYER = 4,// Test players are available only in the 'test' and 'eng' builds.// The shared library with the test player is passed passed as an// argument to the 'test:' url in the setDataSource call.//测试播放器只能在’test’和’eng’版本中使用。 当setDataSource参数中带有’test:'开头的url将使用带有测试播放器的共享库TEST_PLAYER = 5,//mstar平台定制播放器// Mediatek Android Patch BeginMST_PLAYER = 6,HDMI_PLAYER = 7,MSTAR_PLAYER = 8,IMG_PLAYER = 9,// Mediatek Android Patch End
          };
          
        • frameworks/av/media/libmediaplayerservice/MediaPlayerFactory.cpp
          scoreFactory函数是抽象类IFactory中的非抽象函数,NuPlayerFactory和TestPlayerFactory等都继承了IFactory
          class NuPlayerFactory : public MediaPlayerFactory::IFactory {
          public:// Mediatek Android Patch Begin
          #ifdef BUILD_WITH_MSTAR_MMvirtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,int fd,int64_t offset,int64_t length,float /*curScore*/) {char buf[20];lseek(fd, offset, SEEK_SET);read(fd, buf, sizeof(buf));lseek(fd, offset, SEEK_SET);uint32_t ident = *((uint32_t*)buf);// Ogg vorbis?if (ident == 0x5367674f) // 'OggS': use NuPlayer to play OGG clipsreturn 1.0;if (ident == 0xc450fbff && length == 40541) // FIXME: for CTS testGapless1 test work aroundreturn 1.0;if (!memcmp(buf+3, " ftypM4A ", 9) && (length == 46180 || length == 83892)) // FIXME: for CTS testGapless2&3 test work aroundreturn 1.0;// FIXME: for AN7 CTS closed caption tests work around// testChangeSubtitleTrack// testDeselectTrackForSubtitleTracks// testGetTrackInfoForVideoWithSubtitleTracksif (length == 210936)return 1.0;// FIXME: for AN7 CTS NativeDecoderTest#testMuxerVp8if (length == 2829176)return 1.0;// FIXME: for AN8 CTS android.media.cts.MediaPlayerDrmTestif (length == 3012095) {return 1.0;}// FIXME: for AN8 GTS MediaPlayerTestif (length == 1481047 || length == 8127587)return 1.0;return 0.0;}
          #endif// Mediatek Android Patch End// Mediatek Android Patch Beginvirtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,const char* url,const sp<IMediaHTTPService>& /* httpService */,float curScore) {// Mediatek Android Patch Endstatic const float kOurScore = 0.8;if (kOurScore <= curScore)return 0.0;// Mediatek Android Patch Begin
          #ifdef BUILD_WITH_MARLINif (IsWasabiSourceUrl(url)) {char value[PROPERTY_VALUE_MAX];if (property_get("ms.wasabi.nu", value, NULL)&& (!strcmp("1", value))) {return 5.0;}}
          #endif
          #ifdef BUILD_WITH_MSTAR_MMif (strcasestr(url, ".ogg") != NULL) { // use NuPlayer to play OGG clipsreturn 2.1; // use make the value larger than mooplayer.. MStar strategy to force native playerto  play OGG files.... kOurScore;}if (strcasestr(url, ".flac") != NULL) { // use NuPlayer to play FLAC clipsreturn kOurScore;}if (!(strncmp(url, "data:", 5))) { // using NuPlayer when url type is the "data:"return kOurScore;}
          #endif// FIXME: for AN8 GTS MediaPlayerTestif (!strcasecmp(url, "https://storage.googleapis.com/wvmedia/cenc/h264/llama/llama_aac_audio.mp4"))return 1.0;// Mediatek Android Patch Endif (!strncasecmp("http://", url, 7)|| !strncasecmp("https://", url, 8)|| !strncasecmp("file://", url, 7)) {size_t len = strlen(url);if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
          // Mediatek Android Patch Begin
          #ifdef SPECIAL_STREAM_USE_NUPLAYERif (strstr(url, "/sample_aes/") || strstr(url, "/audio_only/") || strstr(url, "/unmuxed_1500k/")) {return 1.0;}
          #endif
          // Mediatek Android Patch Endreturn kOurScore;}if (strstr(url,"m3u8")) {
          // Mediatek Android Patch Begin
          #ifdef SPECIAL_STREAM_USE_NUPLAYERif (strstr(url, "/sample_aes/") || strstr(url, "/audio_only/") || strstr(url, "/unmuxed_1500k/")) {return 1.0;}
          #endif
          // Mediatek Android Patch Endreturn kOurScore;}if (strstr(url, "/videoplayback/")) { // use NuPlayer to play Sonyliv adreturn 1.0;}if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {return kOurScore;}}if (!strncasecmp("rtsp://", url, 7)) {return kOurScore;}return 0.0;}virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,const sp<IStreamSource>& /*source*/,float /*curScore*/) {// Mediatek Android Patch Beginreturn 0.8; // use Mstplayer to play IStreamSource// Mediatek Android Patch End}virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/,const sp<DataSource>& /*source*/,float /*curScore*/) {// Only NuPlayer supports setting a DataSource source directly.return 1.0;}virtual sp<MediaPlayerBase> createPlayer(pid_t pid) {ALOGV(" create NuPlayer");return new NuPlayerDriver(pid);}
          };
          
          在这里插入图片描述
    • 5.0

      prepare() && prepareAsync()

      prepare()
      同步地为播放器的回放做准备。设置数据源和显示表面之后,需要调用prepare()或prepareAsync()。对于文件,可以调用prepare(),它会阻塞,直到MediaPlayer准备好播放为止。

      prepareAsync()
      异步地为播放器的回放做准备。设置数据源和显示表面之后,需要调用prepare()或prepareAsync()。对于流,应该调用prepareAsync(),它会立即返回,而不是阻塞,直到缓冲了足够的数据。

      public void prepare() throws IOException, IllegalStateException {_prepare();scanInternalSubtitleTracks();// DrmInfo, if any, has been resolved by now.synchronized (mDrmLock) {mDrmInfoResolved = true;}
      }private native void _prepare() throws IOException, IllegalStateException;public native void prepareAsync() throws IllegalStateException;
      
      • frameworks/base/media/jni/android_media_MediaPlayer.cpp
        {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
        {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},......static void
        android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
        {//获取java层的mNativeContext,即mediaplayer指针对象sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}// Handle the case where the display surface was set before the mp was// initialized. We try again to make it stick.//在mediaplayer启动之前,获取IGraphicBufferProducer对象,重新设置了display surface,setDisplay接口可以在setDataSource之前被调用//MediaPlayer等生产者生产图形数据到Surface,Surface通过IGraphicBufferProducer把GraphicBuffer跨进程传输给消费者SurfaceFlinger,SurfaceFlinger根据WMS提供的窗口信息合成所有的Layer(对应于Surface),具体的合成策略由hwcomposerHAL模块决定并实施,最后也是由该模块送显到Display,而Gralloc模块则负责分配图形缓冲区sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);mp->setVideoSurfaceTexture(st);//处理prepare结果process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
        }static void
        android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
        {//获取java层的mNativeContext,即mediaplayer指针对象sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return;}// Handle the case where the display surface was set before the mp was// initialized. We try again to make it stick.sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);mp->setVideoSurfaceTexture(st);//处理prepareAsync结果process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
        }......static sp<IGraphicBufferProducer>
        getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {IGraphicBufferProducer * const p = (IGraphicBufferProducer*)env->GetLongField(thiz, fields.surface_texture);return sp<IGraphicBufferProducer>(p);
        }
        

        在这里插入图片描述

      • frameworks/av/media/libmedia/mediaplayer.cpp
        status_t MediaPlayer::setVideoSurfaceTexture(const sp<IGraphicBufferProducer>& bufferProducer)
        {ALOGV("setVideoSurfaceTexture");Mutex::Autolock _l(mLock);if (mPlayer == 0) return NO_INIT;return mPlayer->setVideoSurfaceTexture(bufferProducer);
        }......status_t MediaPlayer::prepare()
        {ALOGV("prepare");Mutex::Autolock _l(mLock);mLockThreadId = getThreadId();//状态不对,还没有准备好if (mPrepareSync) {mLockThreadId = 0;return -EALREADY;}mPrepareSync = true;//prepareAsync_lstatus_t ret = prepareAsync_l();if (ret != NO_ERROR) {mLockThreadId = 0;return ret;}if (mPrepareSync) {//准备失败,阻塞等待准备完成mSignal.wait(mLock);  // wait for prepare donemPrepareSync = false;}ALOGV("prepare complete - status=%d", mPrepareStatus);mLockThreadId = 0;return mPrepareStatus;
        }status_t MediaPlayer::prepareAsync()
        {ALOGV("prepareAsync");Mutex::Autolock _l(mLock);return prepareAsync_l();
        }......// must call with lock held
        status_t MediaPlayer::prepareAsync_l()
        {// Mediatek Android Patch Begin//如果是CTS测试if (isFromSpecificProcess(PROCESS_CTS)){Parcel data;data.writeInt32(DISABLE_PLAYER_RESOURCE_MANAGE);mPlayer->setParameter(KEY_PARAMETER_SET_USE_RM_WRAPPER_PLAYER, data);}// Mediatek Android Patch Endif ( (mPlayer != 0) && ( mCurrentState & (MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {if (mAudioAttributesParcel != NULL) {//设置audio参数mPlayer->setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, *mAudioAttributesParcel);} else {//设置audio流类型mPlayer->setAudioStreamType(mStreamType);}//将播放器的状态置为MEDIA_PLAYER_PREPARINGmCurrentState = MEDIA_PLAYER_PREPARING;//调用服务端的prepareAsyncreturn mPlayer->prepareAsync();}ALOGE("prepareAsync called in state %d, mPlayer(%p)", mCurrentState, mPlayer.get());return INVALID_OPERATION;
        }
        
      • frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp
        status_t MediaPlayerService::Client::prepareAsync()
        {ALOGV("[%d] prepareAsync", mConnId);//获取创建好的播放器sp<MediaPlayerBase> p = getPlayer();if (p == 0) return UNKNOWN_ERROR;// Mediatek Android Patch Begin//如果播放器是mstar.multiple.playerif (property_check_multiple_payerback_process("mstar.multiple.player") == true) {//支持倍数解码mEnableMultiDecode = true;}//如果是mstplayerif (mIsMstplayer) {bool IsMainPlayer = true;int static LastPid;if (LastPid == mPid && mMainClientId && mGetkeyvalue != KEY_PARAMETER_SET_DUAL_DECODE_PIP &&mGetkeyvalue != KEY_PARAMETER_SET_SEAMLESS_MODE) {Parcel data;int key, keyValue;IsMainPlayer = false;if (mEnableMultiDecode) {keyValue = 1;data.writeInt32(keyValue);key = KEY_PARAMETER_SET_MULTIPLE_PLAYER;data.setDataPosition(0);p->setParameter(key, data);}}LastPid = mPid;if (IsMainPlayer && mGetkeyvalue != KEY_PARAMETER_SET_DUAL_DECODE_PIP)mMainClientId = mConnId;}// Mediatek Android Patch End//调用播放器的prepareAsyncstatus_t ret = p->prepareAsync();
        #if CALLBACK_ANTAGONIZERALOGD("start Antagonizer");if (ret == NO_ERROR) mAntagonizer->start();
        #endifreturn ret;
        }
        
    • NuPlayer简单分析

      nuPlayer涉及的相关源代码路径
      在这里插入图片描述

      nuPlayer相关类结构图
      在这里插入图片描述

      nuPlayer流程图
      在这里插入图片描述

      • setDataSource
        • 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),mRebufferingTimeUs(0),mRebufferingEvents(0),mRebufferingAtExit(false),mLooper(new ALooper),mMediaClock(new MediaClock),//创建NuPlayer对象mPlayer(new NuPlayer(pid, mMediaClock)),mPlayerFlags(0),mAnalyticsItem(NULL),mClientUid(-1),mAtEOS(false),mLooping(false),mAutoLoop(false) {ALOGD("NuPlayerDriver(%p) created, clientPid(%d)", this, pid);mLooper->setName("NuPlayerDriver Looper");mMediaClock->init();// set up an analytics recordmAnalyticsItem = new MediaAnalyticsItem(kKeyPlayer);/*NuPlayer实现的是AHanlder-ALooper-AMessage机制*/mLooper->start(false, /* runOnCallingThread */true,  /* canCallJava */PRIORITY_AUDIO);mLooper->registerHandler(mPlayer);mPlayer->init(this);
          }......//播放网络视频
          status_t NuPlayerDriver::setDataSource(const sp<IMediaHTTPService> &httpService,const char *url,const KeyedVector<String8, String8> *headers) {ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());Mutex::Autolock autoLock(mLock);if (mState != STATE_IDLE) {return INVALID_OPERATION;}mState = STATE_SET_DATASOURCE_PENDING;//调用NuPlayer对象的setDataSourceAsync方法mPlayer->setDataSourceAsync(httpService, url, headers);//循环等待mPlayer->setDataSourceAsync完成while (mState == STATE_SET_DATASOURCE_PENDING) {mCondition.wait(mLock);}return mAsyncResult;
          }//播放本地视频
          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;//调用NuPlayer对象的setDataSourceAsync方法mPlayer->setDataSourceAsync(fd, offset, length);//循环等待mPlayer->setDataSourceAsync完成while (mState == STATE_SET_DATASOURCE_PENDING) {mCondition.wait(mLock);}return mAsyncResult;
          }......//setDataSource完成,但播放器的状态还是STATE_UNPREPARED
          void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {Mutex::Autolock autoLock(mLock);CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);mAsyncResult = err;mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;//广播通知NuPlayerDriver::setDataSource设置完成mCondition.broadcast();
          }......status_t NuPlayerDriver::prepareAsync() {ALOGV("prepareAsync(%p)", this);Mutex::Autolock autoLock(mLock);switch (mState) {case STATE_UNPREPARED://把播放器状态置为STATE_PREPARINGmState = STATE_PREPARING;mIsAsyncPrepare = true;//最终进入到nuplayer的prepareAsync中//用于初始化((比如构造函数、setDriver/setDataSourceAsync/prepareAsync/setVideoSurfaceTextureAsync))mPlayer->prepareAsync();return OK;case STATE_STOPPED://播放器状态暂停啦// this is really just paused. handle as seek to startmAtEOS = false;//把播放器状态置为STATE_STOPPED_AND_PREPARINGmState = STATE_STOPPED_AND_PREPARING;mIsAsyncPrepare = true;//用于播放控制(比如start/pause/seekToAsync)mPlayer->seekToAsync(0, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */,true /* needNotify */);return OK;default:return INVALID_OPERATION;};
          }
          
        • frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
          //播放网络视频
          void NuPlayer::setDataSourceAsync(const sp<IMediaHTTPService> &httpService,const char *url,const KeyedVector<String8, String8> *headers) {//发送消息的形式sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);size_t len = strlen(url);sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);sp<Source> source;
          // Mediatek Android Patch Begin
          #ifdef BUILD_WITH_MARLINif (IsWasabiSourceUrl(url)) {//这是啥?source = WasabiSourceCreate(notify, url, headers, mUIDValid, mUID);} else
          #endifconst char *CHINADRM_FEATURE = "CHINADRM.FEATURE";size_t idx;if (headers != NULL) {for (idx = 0; idx < headers->size(); idx++) {//判断是不是广电内容数字版权加密保护技术if (!strncmp(headers->keyAt(idx), CHINADRM_FEATURE, strlen(CHINADRM_FEATURE)) && atoi(headers->valueAt(idx)) > 0) {ALOGW("CHINADRM.FEATURE is enable for %s", url);mIsChinaDrm = true;break;}}}// Mediatek Android Patch Endif (IsHTTPLiveURL(url)) {//HLS协议source = new HTTPLiveSource(notify, httpService, url, headers);ALOGV("setDataSourceAsync HTTPLiveSource %s", url);mDataSourceType = DATA_SOURCE_TYPE_HTTP_LIVE;} else if (!strncasecmp(url, "rtsp://", 7)) {//rtsp协议source = new RTSPSource(notify, httpService, url, headers, mUIDValid, mUID);ALOGV("setDataSourceAsync RTSPSource %s", url);mDataSourceType = DATA_SOURCE_TYPE_RTSP;} else if ((!strncasecmp(url, "http://", 7)|| !strncasecmp(url, "https://", 8))&& ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))|| strstr(url, ".sdp?"))) {//SDP会话协议source = new RTSPSource(notify, httpService, url, headers, mUIDValid, mUID, true);ALOGV("setDataSourceAsync RTSPSource http/https/.sdp %s", url);mDataSourceType = DATA_SOURCE_TYPE_RTSP;} else {//本地播放ALOGV("setDataSourceAsync GenericSource %s", url);sp<GenericSource> genericSource =new GenericSource(notify, mUIDValid, mUID, mMediaClock);status_t err = genericSource->setDataSource(httpService, url, headers);if (err == OK) {source = genericSource;} else {ALOGE("Failed to set data source!");}// regardless of success/failuremDataSourceType = DATA_SOURCE_TYPE_GENERIC_URL;}msg->setObject("source", source);msg->post();
          }//播放本地视频
          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);//重置Source数据sp<GenericSource> source =new GenericSource(notify, mUIDValid, mUID, mMediaClock);ALOGV("setDataSourceAsync fd %d/%lld/%lld source: %p",fd, (long long)offset, (long long)length, source.get());//调用GenericSource中的setDataSource方法status_t err = source->setDataSource(fd, offset, length);if (err != OK) {ALOGE("Failed to set data source!");source = NULL;}msg->setObject("source", source);msg->post();mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
          }......//nuPlayer的重要部分,大部分的操作都在此处完成
          void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
          switch (msg->what()) {case kWhatSetDataSource:{ALOGV("kWhatSetDataSource");CHECK(mSource == NULL);status_t err = OK;sp<RefBase> obj;CHECK(msg->findObject("source", &obj));if (obj != NULL) {Mutex::Autolock autoLock(mSourceLock);mSource = static_cast<Source *>(obj.get());} else {err = UNKNOWN_ERROR;}CHECK(mDriver != NULL);//NuPlayer以mPlayer这个成员变量存在于NuPlayerDriver结构体中,同样的,NuPlayerDriver以 wp<NuPlayerDriver> mDriver; 的形式存在于NuPlayer中,它也是在NuPlayer的构造函数中赋值的。通过mDriver.promote()的方法就可以使弱指针升级成强指针sp<NuPlayerDriver> driver,这时候这个driver就是NuPlayerDriversp<NuPlayerDriver> driver = mDriver.promote();if (driver != NULL) {//setDataSource完成driver->notifySetDataSourceCompleted(err);}break;}
          ......void NuPlayer::prepareAsync() {ALOGV("prepareAsync");(new AMessage(kWhatPrepare, this))->post();
          }......case kWhatPrepare:
          {ALOGV("onMessageReceived kWhatPrepare");mSource->prepareAsync();break;
          }
          
        • frameworks/av/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp

          HLS协议

          HLS 概述

          HTTP Live Streaming(HLS)是苹果公司实现的基于HTTP的流媒体直播和点播协议,主要应用在iOS系统。相对于普通的流媒体,例如RTMP协议、RTSP协议、MMS协议等,HLS最大的优点是可以根据网络状况自动切换到不同码率的视频,如果网络状况较好,则会切换到高码率的视频,若发现网络状况不佳,则会逐渐过渡到低码率的视频。

          HLS框架结构图

          在这里插入图片描述

          我们首先将要直播的视频送到编码器中,编码器分别对视频和音频进行编码,然后输出到一个MPEG-2格式的传输流中,再由分段器将MPEG-2传输流进行分段,产生一系列等间隔的媒体片段,这些媒体片段一般很小并且保存成后缀为.ts的文件,同时生成一个指向这些媒体文件的索引文件,也就是我们很经常听到的.M3U8文件。完成分段之后将这些索引文件以及媒体文件上传到Web服务器上。客户端读取索引文件,然后按顺序请求下载索引文件中列出的媒体文件。下载后是一个ts文件。需要进行解压获得对应的媒体数据并解码后进行播放。由于在直播过程中服务器端会不断地将最新的直播数据生成新的小文件,并上传所以只要客户端不断地按顺序下载并播放从服务器获取到的文件,从整个过程上看就相当于实现了直播。而且由于分段文件的很短,客户端可以根据实际的带宽情况切换到不同码率的直播源,从而实现多码率的适配的目的。

          HLS播放流程

          1.获取不同带宽下对应的网络资源URI及音视频编解码,视频分辨率等信息的文件

          #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=899152,RESOLUTION=480x270,CODECS="avc1.4d4015,mp4a.40.5"
          http://hls.ftdp.com/video1_widld/m3u8/01.m3u8
          

          2.根据上述获取的信息初始化对应的编解码器

          3.获取第一个网络资源对应的分段索引列表(index文件)

          #EXTM3U
          #EXT-X-VERSION:3
          #EXT-X-TARGETDURATION:10
          #EXT-X-MEDIA-SEQUENCE:6532
          #EXT-X-KEY:METHOD=AES-128,URI="18319965201.key"
          #EXTINF:10,
          20125484T125708-01-6533.ts
          #EXT-X-KEY:METHOD=AES-128,URI="14319965205.key"
          #EXTINF:10,
          20125484T125708-01-6534.ts
          ....
          #EXTINF:8,
          20140804T125708-01-6593.ts
          

          4.获取某一个分片的Key

          5.请求下载某一个分片

          6.根据当前的带宽决定是否切换视频资源

          7.将下载的分片资源解密后送到解码器进行解码

          在这里插入图片描述

          //初始化(setDataSource完成)
          NuPlayer::HTTPLiveSource::HTTPLiveSource(const sp<AMessage> &notify,const sp<IMediaHTTPService> &httpService,const char *url,const KeyedVector<String8, String8> *headers): Source(notify),mHTTPService(httpService),mURL(url),mFlags(0),mFinalResult(OK),mOffset(0),mFetchSubtitleDataGeneration(0),mFetchMetaDataGeneration(0),mHasMetadata(false),mMetadataSelected(false) {mBufferingSettings.mInitialMarkMs = kPrepareMarkMs;mBufferingSettings.mResumePlaybackMarkMs = kReadyMarkMs;if (headers) {mExtraHeaders = *headers;ssize_t index =mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log"));if (index >= 0) {mFlags |= kFlagIncognito;mExtraHeaders.removeItemsAt(index);}}
          }......void NuPlayer::HTTPLiveSource::prepareAsync() {//创建并启动一个Looperif (mLiveLooper == NULL) {mLiveLooper = new ALooper;mLiveLooper->setName("http live");mLiveLooper->start();mLiveLooper->registerHandler(this);}sp<AMessage> notify = new AMessage(kWhatSessionNotify, this);//构建LiveSession对象mLiveSession = new LiveSession(notify,(mFlags & kFlagIncognito) ? LiveSession::kFlagIncognito : 0,mHTTPService);mLiveLooper->registerHandler(mLiveSession);mLiveSession->setBufferingSettings(mBufferingSettings);//创建一个异步连接会话mLiveSession->connectAsync(mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
          }
          
        • frameworks/av/media/libstagefright/httplive/LiveSession.cpp
          void LiveSession::connectAsync(const char *url, const KeyedVector<String8, String8> *headers) {//创建一个kWhatConnect并传入urlsp<AMessage> msg = new AMessage(kWhatConnect, this);msg->setString("url", url);if (headers != NULL) {msg->setPointer("headers",new KeyedVector<String8, String8>(*headers));}msg->post();
          }......case kWhatConnect:
          {onConnect(msg);break;
          }......void LiveSession::onConnect(const sp<AMessage> &msg) {//获取传过来的UriCHECK(msg->findString("url", &mMasterURL));// TODO currently we don't know if we are coming here from incognito modeALOGI("onConnect %s", uriDebugString(mMasterURL).c_str());KeyedVector<String8, String8> *headers = NULL;if (!msg->findPointer("headers", (void **)&headers)) {mExtraHeaders.clear();} else {mExtraHeaders = *headers;delete headers;headers = NULL;}// create looper for fetchers//创建一个轮询器if (mFetcherLooper == NULL) {mFetcherLooper = new ALooper();mFetcherLooper->setName("Fetcher");mFetcherLooper->start(false, /* runOnCallingThread */true  /* canCallJava */);}// create fetcher to fetch the master playlist//获取不同带宽下对应的网络资源URI及音视频编解码信息addFetcher(mMasterURL.c_str())->fetchPlaylistAsync();
          }......sp<PlaylistFetcher> LiveSession::addFetcher(const char *uri) {ssize_t index = mFetcherInfos.indexOfKey(uri);if (index >= 0) {return NULL;}sp<AMessage> notify = new AMessage(kWhatFetcherNotify, this);notify->setString("uri", uri);notify->setInt32("switchGeneration", mSwitchGeneration);FetcherInfo info;//创建一个PlaylistFetcher并返回info.mFetcher = new PlaylistFetcher(notify, this, uri, mCurBandwidthIndex, mSubtitleGeneration);info.mDurationUs = -1ll;info.mToBeRemoved = false;info.mToBeResumed = false;mFetcherLooper->registerHandler(info.mFetcher);mFetcherInfos.add(uri, info);return info.mFetcher;
          }
          
        • frameworks/av/media/libstagefright/httplive/PlaylistFetcher.cpp
          void PlaylistFetcher::fetchPlaylistAsync() {(new AMessage(kWhatFetchPlaylist, this))->post();
          }......case kWhatFetchPlaylist:
          {bool unchanged;//获取一个M3U8Parsersp<M3UParser> playlist = mHTTPDownloader->fetchPlaylist(mURI.c_str(), NULL /* curPlaylistHash */, &unchanged);sp<AMessage> notify = mNotify->dup();notify->setInt32("what", kWhatPlaylistFetched);//将playlist返回notify->setObject("playlist", playlist);notify->post();break;
          }
          
        • frameworks/av/media/libstagefright/httplive/HTTPDownloader.cpp
          ssize_t HTTPDownloader::fetchBlock(const char *url, sp<ABuffer> *out,int64_t range_offset, int64_t range_length,uint32_t block_size, /* download block size */String8 *actualUrl,bool reconnect /* force connect HTTP when resuing source */) {//没有连接if (isDisconnecting()) {return ERROR_NOT_CONNECTED;}off64_t size;if (reconnect) {//恢复播放源需要强制连接HTTPif (!strncasecmp(url, "file://", 7)) {//本地文件类的播放源mDataSource = new FileSource(url + 7);} else if (strncasecmp(url, "http://", 7)&& strncasecmp(url, "https://", 8)) {//不是http或https协议,不支持return ERROR_UNSUPPORTED;} else {KeyedVector<String8, String8> headers = mExtraHeaders;if (range_offset > 0 || range_length >= 0) {//添加分段请求的rangeheaders.add(String8("Range"),String8(AStringPrintf("bytes=%lld-%s",range_offset,range_length < 0? "" : AStringPrintf("%lld",range_offset + range_length - 1).c_str()).c_str()));}status_t err = mHTTPDataSource->connect(url, &headers);if (isDisconnecting()) {return ERROR_NOT_CONNECTED;}if (err != OK) {return err;}mDataSource = mHTTPDataSource;}}status_t getSizeErr = mDataSource->getSize(&size);if (isDisconnecting()) {return ERROR_NOT_CONNECTED;}if (getSizeErr != OK) {//获取size大小错误时,赋予最大值65536size = 65536;}//初始化一个缓冲队列buffersp<ABuffer> buffer = *out != NULL ? *out : new ABuffer(size);if (*out == NULL) {//下载的数据为空时,设置buffer从头开始buffer->setRange(0, 0);}ssize_t bytesRead = 0;// adjust range_length if only reading partial block// 如果仅读取部分块,则调整范围长度if (block_size > 0 && (range_length == -1 || (int64_t)(buffer->size() + block_size) < range_length)) {range_length = buffer->size() + block_size;}//循环下载for (;;) {// Only resize when we don't know the size.//只有当我们不知道大小时才调整大小size_t bufferRemaining = buffer->capacity() - buffer->size();if (bufferRemaining == 0 && getSizeErr != OK) {//当buffer剩余的容量为0或者获取buffer大小错误时size_t bufferIncrement = buffer->size() / 2;if (bufferIncrement < 32768) {bufferIncrement = 32768;}//增加的下载缓冲区bufferRemaining = bufferIncrement;ALOGV("increasing download buffer to %zu bytes",buffer->size() + bufferRemaining);sp<ABuffer> copy = new ABuffer(buffer->size() + bufferRemaining);if (copy->data() == NULL) {android_errorWriteLog(0x534e4554, "68399439");ALOGE("not enough memory to download: requesting %zu + %zu",buffer->size(), bufferRemaining);return NO_MEMORY;}memcpy(copy->data(), buffer->data(), buffer->size());copy->setRange(0, buffer->size());buffer = copy;}size_t maxBytesToRead = bufferRemaining;if (range_length >= 0) {int64_t bytesLeftInRange = range_length - buffer->size();if (bytesLeftInRange < 0) {ALOGE("range_length %" PRId64 " wrapped around", range_length);return ERROR_OUT_OF_RANGE;} else if (bytesLeftInRange < (int64_t)maxBytesToRead) {maxBytesToRead = bytesLeftInRange;if (bytesLeftInRange == 0) {break;}}}// The DataSource is responsible for informing us of error (n < 0) or eof (n == 0)// to help us break out of the loop.ssize_t n = mDataSource->readAt(buffer->size(), buffer->data() + buffer->size(),maxBytesToRead);if (isDisconnecting()) {return ERROR_NOT_CONNECTED;}if (n < 0) {return n;}if (n == 0) {break;}buffer->setRange(0, buffer->size() + (size_t)n);bytesRead += n;}*out = buffer;if (actualUrl != NULL) {*actualUrl = mDataSource->getUri();if (actualUrl->isEmpty()) {*actualUrl = url;}}return bytesRead;
          }ssize_t HTTPDownloader::fetchFile(const char *url, sp<ABuffer> *out, String8 *actualUrl) {ssize_t err = fetchBlock(url, out, 0, -1, 0, actualUrl, true /* reconnect */);// close off the connection after usemHTTPDataSource->disconnect();return err;
          }//从服务器端获取到m3u8 playlist内容并存放到buffer缓存区
          sp<M3UParser> HTTPDownloader::fetchPlaylist(const char *url, uint8_t *curPlaylistHash, bool *unchanged) {ALOGV("fetchPlaylist '%s'", url);*unchanged = false;sp<ABuffer> buffer;String8 actualUrl;//调用fetchFilessize_t err = fetchFile(url, &buffer, &actualUrl);// close off the connection after use//使用后关闭连接mHTTPDataSource->disconnect();if (err <= 0) {return NULL;}// MD5 functionality is not available on the simulator, treat all// playlists as changed.// MD5功能在模拟器上不可用,请将所有播放列表视为已更改#if defined(__ANDROID__)uint8_t hash[16];MD5_CTX m;MD5_Init(&m);MD5_Update(&m, buffer->data(), buffer->size());MD5_Final(hash, &m);//通过hash值判断播放的文件是否有改变if (curPlaylistHash != NULL && !memcmp(hash, curPlaylistHash, 16)) {// playlist unchanged*unchanged = true;return NULL;}
          #endif//将获取到的缓存数据包装成M3UParsersp<M3UParser> playlist =new M3UParser(actualUrl.string(), buffer->data(), buffer->size());if (playlist->initCheck() != OK) {ALOGE("failed to parse .m3u8 playlist");return NULL;}#if defined(__ANDROID__)if (curPlaylistHash != NULL) {memcpy(curPlaylistHash, hash, sizeof(hash));}
          #endifreturn playlist;
          }
          
        • frameworks/av/media/libstagefright/httplive/M3UParser.cpp
          M3UParser::M3UParser(const char *baseURI, const void *data, size_t size): mInitCheck(NO_INIT),mBaseURI(baseURI),mIsExtM3U(false),mIsVariantPlaylist(false),mIsComplete(false),mIsEvent(false),mFirstSeqNumber(-1),mLastSeqNumber(-1),mTargetDurationUs(-1ll),mDiscontinuitySeq(0),mDiscontinuityCount(0),mSelectedIndex(-1) {//解析下载的缓存数据mInitCheck = parse(data, size);
          }......//解析下载的缓存数据
          status_t M3UParser::parse(const void *_data, size_t size) {int32_t lineNo = 0;sp<AMessage> itemMeta;const char *data = (const char *)_data;size_t offset = 0;uint64_t segmentRangeOffset = 0;while (offset < size) {size_t offsetLF = offset;//寻找第一个换行符while (offsetLF < size && data[offsetLF] != '\n') {++offsetLF;}AString line;if (offsetLF > offset && data[offsetLF - 1] == '\r') {line.setTo(&data[offset], offsetLF - offset - 1);} else {line.setTo(&data[offset], offsetLF - offset);}// ALOGI("#%s#", line.c_str());if (line.empty()) {offset = offsetLF + 1;continue;}if (lineNo == 0 && line == "#EXTM3U") {mIsExtM3U = true;}if (mIsExtM3U) {status_t err = OK;if (line.startsWith("#EXT-X-TARGETDURATION")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}err = parseMetaData(line, &mMeta, "target-duration");} else if (line.startsWith("#EXT-X-MEDIA-SEQUENCE")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}err = parseMetaData(line, &mMeta, "media-sequence");} else if (line.startsWith("#EXT-X-KEY")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}err = parseCipherInfo(line, &itemMeta, mBaseURI);} else if (line.startsWith("#EXT-X-ENDLIST")) {mIsComplete = true;} else if (line.startsWith("#EXT-X-PLAYLIST-TYPE:EVENT")) {mIsEvent = true;} else if (line.startsWith("#EXTINF")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}err = parseMetaDataDuration(line, &itemMeta, "durationUs");} else if (line.startsWith("#EXT-X-DISCONTINUITY-SEQUENCE")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}size_t seq;err = parseDiscontinuitySequence(line, &seq);if (err == OK) {mDiscontinuitySeq = seq;ALOGI("mDiscontinuitySeq %zu", mDiscontinuitySeq);} else {ALOGI("Failed to parseDiscontinuitySequence %d", err);}} else if (line.startsWith("#EXT-X-DISCONTINUITY")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}if (itemMeta == NULL) {itemMeta = new AMessage;}itemMeta->setInt32("discontinuity", true);++mDiscontinuityCount;} else if (line.startsWith("#EXT-X-STREAM-INF")) {if (mMeta != NULL) {return ERROR_MALFORMED;}mIsVariantPlaylist = true;err = parseStreamInf(line, &itemMeta);} else if (line.startsWith("#EXT-X-BYTERANGE")) {if (mIsVariantPlaylist) {return ERROR_MALFORMED;}uint64_t length, offset;err = parseByteRange(line, segmentRangeOffset, &length, &offset);if (err == OK) {if (itemMeta == NULL) {itemMeta = new AMessage;}itemMeta->setInt64("range-offset", offset);itemMeta->setInt64("range-length", length);segmentRangeOffset = offset + length;}} else if (line.startsWith("#EXT-X-MEDIA")) {err = parseMedia(line);}if (err != OK) {return err;}}if (!line.startsWith("#")) {if (itemMeta == NULL) {ALOGV("itemMeta == NULL");return ERROR_MALFORMED;}if (!mIsVariantPlaylist) {int64_t durationUs;if (!itemMeta->findInt64("durationUs", &durationUs)) {return ERROR_MALFORMED;}itemMeta->setInt32("discontinuity-sequence",mDiscontinuitySeq + mDiscontinuityCount);}mItems.push();Item *item = &mItems.editItemAt(mItems.size() - 1);CHECK(MakeURL(mBaseURI.c_str(), line.c_str(), &item->mURI));item->mMeta = itemMeta;itemMeta.clear();}offset = offsetLF + 1;++lineNo;}// error checking of all fields that's required to appear once// (currently only checking "target-duration"), and// initialization of playlist properties (eg. mTargetDurationUs)if (!mIsVariantPlaylist) {int32_t targetDurationSecs;if (mMeta == NULL || !mMeta->findInt32("target-duration", &targetDurationSecs)) {ALOGE("Media playlist missing #EXT-X-TARGETDURATION");return ERROR_MALFORMED;}mTargetDurationUs = targetDurationSecs * 1000000ll;mFirstSeqNumber = 0;if (mMeta != NULL) {mMeta->findInt32("media-sequence", &mFirstSeqNumber);}mLastSeqNumber = mFirstSeqNumber + mItems.size() - 1;}for (size_t i = 0; i < mItems.size(); ++i) {sp<AMessage> meta = mItems.itemAt(i).mMeta;const char *keys[] = {"audio", "video", "subtitles"};for (size_t j = 0; j < sizeof(keys) / sizeof(const char *); ++j) {AString groupID;if (meta->findString(keys[j], &groupID)) {ssize_t groupIndex = mMediaGroups.indexOfKey(groupID);if (groupIndex < 0) {ALOGE("Undefined media group '%s' referenced in stream info.",groupID.c_str());return ERROR_MALFORMED;}}}}return OK;
          }
          
        • frameworks/av/media/libmediaplayerservice/nuplayer/GenericSource.cpp
          status_t NuPlayer::GenericSource::setDataSource(int fd, int64_t offset, int64_t length) {Mutex::Autolock _l(mLock);ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);//重置相关变量resetDataSource();//dup函数是返回一个指向同一个文件的文件描述符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;
          }......void NuPlayer::GenericSource::prepareAsync() {Mutex::Autolock _l(mLock);ALOGV("prepareAsync: (looper: %d)", (mLooper != NULL));if (mLooper == NULL) {mLooper = new ALooper;mLooper->setName("generic");mLooper->start();mLooper->registerHandler(this);}sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);msg->post();
          }......case kWhatPrepareAsync:
          {onPrepareAsync();break;
          }......void NuPlayer::GenericSource::onPrepareAsync() {ALOGV("onPrepareAsync: mDataSource: %d", (mDataSource != NULL));// delayed data source creation//延迟数据源创建if (mDataSource == NULL) {// set to false first, if the extractor// comes back as secure, set it to true then.//等待提取器extractor返回是安全的,再设置为truemIsSecure = false;if (!mUri.empty()) {//流媒体服务器创建dataSourceconst char* uri = mUri.c_str();String8 contentType;if (!strncasecmp("http://", uri, 7) || !strncasecmp("https://", uri, 8)) {mHttpSource = DataSourceFactory::CreateMediaHTTP(mHTTPService);if (mHttpSource == NULL) {ALOGE("Failed to create http source!");notifyPreparedAndCleanup(UNKNOWN_ERROR);return;}}mLock.unlock();// This might take long time if connection has some issue.//如果连接出现问题,这可能需要很长时间sp<DataSource> dataSource = DataSourceFactory::CreateFromURI(mHTTPService, uri, &mUriHeaders, &contentType,static_cast<HTTPBase *>(mHttpSource.get()));mLock.lock();if (!mDisconnected) {mDataSource = dataSource;}} else {//"media.stagefright.extractremote"是否为true,若是,就会用binder获取"media.extractor"服务,然后创建extractor,否则用本地的extractor。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 = CreateDataSourceFromIDataSource(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// CreateDataSourceFromIDataSource 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) {//当uri以"http://"为开头,创建NuCachedSource2mCachedSource = 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 source// 初始化extractorstatus_t err = initFromDataSource();if (err != OK) {ALOGE("Failed to init from data source!");//上报prepare结果notifyPreparedAndCleanup(err);return;}if (mVideoTrack.mSource != NULL) {sp<MetaData> meta = getFormatMeta_l(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);sp<DataSource> dataSource = mDataSource;mLock.unlock();// This might take long time if data source is not reliable.//在此函数中会判断"media.stagefright.extractremote"是否为true,若是,就会用binder获取"media.extractor"服务, 然后创建extractor,否则用本地的extractor,可能会耗时一段时间。extractor = MediaExtractorFactory::Create(dataSource, NULL);if (extractor == NULL) {ALOGE("initFromDataSource, cannot create extractor!");return UNKNOWN_ERROR;}/**metadata包含如下数据持续时间:(数字)FLV的长度(以秒为单位)。FLVMDI计算此值。lasttimestamp:(数字)FLV文件中最后一个标记的时间戳。lastkeyframetimestamp:(数字)最后一个视频标记的时间戳,它是关键帧。可能需要此信息,因为在此时间之后搜索帧通常不起作用。宽度:(数字)视频的宽度(以像素为单位)(Flash exporter 1.1将其设置为0)。高度:(数字)视频的高度(以像素为单位)(Flash exporter 1.1将其设置为0)。videodatarate:(数字)FLVMDI不计算此值,如果存在,则将其导入(默认值为0)。audiodatarate:(数字)FLVMDI不计算此值,如果存在,则将其导入(默认值为0)。帧率:(数字)FLVMDI计算此值,但如果不是0,则使用导入的值。creationdate:(字符串)FLVMDI无法计算此值并导入它(如果存在)(默认为“未知”)。filesize:(Number)文件大小(以字节为单位)(包括注入的数据)。videosize:(数字)文件中视频标记的总大小(字节)。audiosize:(数字)文件中音频标记的总大小(字节)。datasize:(Number)文件中数据标记的总大小(以字节为单位)。metadatacreator:(字符串)将设置为“Manitu组FLV元数据注入器2”。metadatadate:(日期)添加了日期和时间元数据(请注意,这不是类似于“creationdate”的字符串类型。xtradata:(字符串)如果指定,则附加字符串数据。VideoCodeId:(编号)FLV中使用的视频编解码器ID编号(Sorenson H.263=2,屏幕视频=3,On2 VP6=4和5,屏幕视频V2=6)。audiocodecid:(编号)FLV中使用的音频编解码器ID编号(未压缩=0,ADPCM=1,MP3=2,Nellymore=5和6)。音频延迟:(数字)以秒为单位的音频延迟。Flash 8编码器延迟视频预编器与音频同步(音频和视频不会在时间0同时启动,视频启动稍晚)。该值对于Flash 8Video编码器注入的提示点也很重要,因为提示点的逻辑时间和它们插入LF的物理时间不一致(在编码之前注入提示点,当视频被“音频延迟”秒移位时,提示点也被移位,并且它们在FLV中的物理时间发生变化)。canSeekToEnd:(布尔)如果最后一个视频标记是关键帧,因此可以进行“搜索”,则为True。关键帧:(对象)*/sp<MetaData> fileMeta = extractor->getMetaData();// 获取metadatasize_t numtracks = extractor->countTracks();// 获取track数if (numtracks == 0) {ALOGE("initFromDataSource, source has no track!");return UNKNOWN_ERROR;}mLock.lock();mFileMeta = fileMeta;if (mFileMeta != NULL) {int64_t duration;if (mFileMeta->findInt64(kKeyDuration, &duration)) {//赋值音视频时长mDurationUs = duration;}}//总码率int32_t totalBitrate = 0;//流媒体类型数组mMimes.clear();//遍历track通道,获取每个track的信息for (size_t i = 0; i < numtracks; ++i) {sp<IMediaSource> track = extractor->getTrack(i);if (track == NULL) {continue;}sp<MetaData> meta = extractor->getTrackMetaData(i);//获取当前通道的metadataif (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());/**  常用的音频类型static const MimeToRole kMimeToRole[] = {{ MEDIA_MIMETYPE_AUDIO_MPEG,"audio_decoder.mp3", "audio_encoder.mp3" },{ MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I,"audio_decoder.mp1", "audio_encoder.mp1" },{ MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II,"audio_decoder.mp2", "audio_encoder.mp2" },{ MEDIA_MIMETYPE_AUDIO_AMR_NB,"audio_decoder.amrnb", "audio_encoder.amrnb" },{ MEDIA_MIMETYPE_AUDIO_AMR_WB,"audio_decoder.amrwb", "audio_encoder.amrwb" },{ MEDIA_MIMETYPE_AUDIO_AAC,"audio_decoder.aac", "audio_encoder.aac" },{ MEDIA_MIMETYPE_AUDIO_VORBIS,"audio_decoder.vorbis", "audio_encoder.vorbis" },{ MEDIA_MIMETYPE_AUDIO_OPUS,"audio_decoder.opus", "audio_encoder.opus" },{ MEDIA_MIMETYPE_AUDIO_G711_MLAW,"audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },{ MEDIA_MIMETYPE_AUDIO_G711_ALAW,"audio_decoder.g711alaw", "audio_encoder.g711alaw" },{ MEDIA_MIMETYPE_VIDEO_AVC,"video_decoder.avc", "video_encoder.avc" },{ MEDIA_MIMETYPE_VIDEO_HEVC,"video_decoder.hevc", "video_encoder.hevc" },{ MEDIA_MIMETYPE_VIDEO_MPEG4,"video_decoder.mpeg4", "video_encoder.mpeg4" },{ MEDIA_MIMETYPE_VIDEO_H263,"video_decoder.h263", "video_encoder.h263" },{ MEDIA_MIMETYPE_VIDEO_VP8,"video_decoder.vp8", "video_encoder.vp8" },{ MEDIA_MIMETYPE_VIDEO_VP9,"video_decoder.vp9", "video_encoder.vp9" },{ MEDIA_MIMETYPE_AUDIO_RAW,"audio_decoder.raw", "audio_encoder.raw" },{ MEDIA_MIMETYPE_VIDEO_DOLBY_VISION,"video_decoder.dolby-vision", "video_encoder.dolby-vision" },{ MEDIA_MIMETYPE_AUDIO_FLAC,"audio_decoder.flac", "audio_encoder.flac" },{ MEDIA_MIMETYPE_AUDIO_MSGSM,"audio_decoder.gsm", "audio_encoder.gsm" },{ MEDIA_MIMETYPE_VIDEO_MPEG2,"video_decoder.mpeg2", "video_encoder.mpeg2" },{ MEDIA_MIMETYPE_AUDIO_AC3,"audio_decoder.ac3", "audio_encoder.ac3" },{ MEDIA_MIMETYPE_AUDIO_EAC3,"audio_decoder.eac3", "audio_encoder.eac3" },{ MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,"image_decoder.heic", "image_encoder.heic" },// Medaitek Android Patch Begin{ MEDIA_MIMETYPE_AUDIO_HW_AAC,"audio_decoder.hwaac", "audio_encoder.hwaac" },{ MEDIA_MIMETYPE_AUDIO_DTS,"audio_decoder.dts", "audio_encoder.dts" },{ MEDIA_MIMETYPE_AUDIO_DTS_HD,"audio_decoder.dtshd", "audio_encoder.dtshd" },{ MEDIA_MIMETYPE_AUDIO_DTS_EXPRESS,"audio_decoder.dtse", "audio_encoder.dtse" },{ MEDIA_MIMETYPE_AUDIO_APE,"audio_decoder.ape", "audio_encoder.ape" },{ MEDIA_MIMETYPE_AUDIO_WMA,"audio_decoder.wma", "audio_encoder.wma" },{ MEDIA_MIMETYPE_AUDIO_ADPCM_MS,"audio_decoder.adpcmms", "audio_encoder.adpcmms" },{ MEDIA_MIMETYPE_AUDIO_ADPCM_IMA,"audio_decoder.adpcmima", "audio_encoder.adpcmima" },{ MEDIA_MIMETYPE_AUDIO_WMAPRO,"audio_decoder.wmapro", "audio_encoder.wmapro" },{ MEDIA_MIMETYPE_VIDEO_MJPEG,"video_decoder.mjpeg", "video_encoder.mjpeg" },{ MEDIA_MIMETYPE_VIDEO_WMV,"video_decoder.wmv", "video_encoder.wmv" },{ MEDIA_MIMETYPE_VIDEO_VP6,"video_decoder.vp6", "video_encoder.vp6" },{ MEDIA_MIMETYPE_VIDEO_AVS,"video_decoder.avs", "video_encoder.avs" },{ MEDIA_MIMETYPE_VIDEO_REAL_VIDEO,"video_decoder.rv", "video_encoder.rv" },{ MEDIA_MIMETYPE_AUDIO_REAL_AUDIO,"audio_decoder.ra", "audio_encoder.ra" },{ MEDIA_MIMETYPE_VIDEO_AVS2,"video_decoder.avs2", "video_encoder.avs2" },{ MEDIA_MIMETYPE_VIDEO_VC1,"video_decoder.vc1", "video_encoder.vc1" },{ MEDIA_MIMETYPE_VIDEO_FLV,"video_decoder.flv", "video_encoder.flv" },{ MEDIA_MIMETYPE_VIDEO_DIVX3,"video_decoder.divx311", "video_encoder.divx311" },{ MEDIA_MIMETYPE_VIDEO_DIVX4,"video_decoder.divx412", "video_encoder.divx412" },{ MEDIA_MIMETYPE_VIDEO_RV,"video_decoder.rv", "video_encoder.rv" },{ MEDIA_MIMETYPE_VIDEO_WVC1,"video_decoder.vc1", "video_encoder.vc1" },{ MEDIA_MIMETYPE_VIDEO_WMV3,"video_decoder.wmv3", "video_encoder.wmv3" },{ MEDIA_MIMETYPE_VIDEO_MJPG,"video_decoder.mjpg", "video_encoder.mjpg" },{ MEDIA_MIMETYPE_AUDIO_AC3P,"audio_decoder.ac3p", "audio_encoder.ac3p" },{ MEDIA_MIMETYPE_AUDIO_DTS_HD,"audio_decoder.dts", "audio_encoder.dts" },{ MEDIA_MIMETYPE_AUDIO_DTS_LBR,"audio_decoder.dts", "audio_encoder.dts" },{ MEDIA_MIMETYPE_AUDIO_COOK,"audio_decoder.ra", "audio_encoder.ra" },{ MEDIA_MIMETYPE_AUDIO_WMA,"audio_decoder.wma", "audio_encoder.wma" },{ MEDIA_MIMETYPE_AUDIO_AC4,"audio_decoder.ac4", "audio_encoder.ac4" },{ MEDIA_MIMETYPE_AUDIO_MPEG_H,"audio_decoder.mpegh", "audio_encoder.mpegh" },{ MEDIA_MIMETYPE_VIDEO_MVC,"video_decoder.mvc", "video_encoder.mvc" },{ MEDIA_MIMETYPE_VIDEO_HEVCDV,"video_decoder.hevcdv", "video_encoder.hevcdv" },{ MEDIA_MIMETYPE_AUDIO_DRA,"audio_decoder.dra", "audio_encoder.dra" },{ MEDIA_MIMETYPE_AUDIO_DSP_AAC,"audio_decoder.dspaac", "audio_encoder.dspaac" },{ MEDIA_MIMETYPE_AUDIO_PASSTHROUGH,"audio_decoder.passthrough", "audio_encoder.passthrough"},// Mediatek Android Patch End{ MEDIA_MIMETYPE_VIDEO_DIVX5,"video_decoder.divx5", "video_encoder.divx5" },{ MEDIA_MIMETYPE_VIDEO_DIVX_AVC,"video_decoder.divx-avc", "video_encoder.divx-avc" },{ MEDIA_MIMETYPE_VIDEO_DIVX_HEVC,"video_decoder.divx-hevc", "video_encoder.divx-hevc" },{ MEDIA_MIMETYPE_VIDEO_AV1,"video_decoder.av1", "video_encoder.av1" },};*///如果是vorbis(一个很老的音频)音频数据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 beginning//视频总是在开头mMimes.insertAt(String8(mime), 0);}}//保存每一个TrackmSources.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.//返回值不影响source初始化(void)checkDrmInfo();mBitrate = totalBitrate;return OK;
          }
          
        • frameworks/av/media/libstagefright/MediaExtractorFactory.cpp

          sp<IMediaExtractor> MediaExtractorFactory::Create(const sp<DataSource> &source, const char *mime) {ALOGV("MediaExtractorFactory::Create %s", mime);if (!property_get_bool("media.stagefright.extractremote", true)) {// local extractor//存在本地提取服务ALOGW("creating media extractor in calling process");return CreateFromService(source, mime);} else {// remote extractorALOGV("get service manager");//获取多媒体提取器服务sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));if (binder != 0) {sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));//根据source返回具体的数据提取器sp<IMediaExtractor> ex = mediaExService->makeExtractor(CreateIDataSourceFromDataSource(source), mime);return ex;} else {ALOGE("extractor service not running");return NULL;}}return NULL;
          }sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(const sp<DataSource> &source, const char *mime) {ALOGV("MediaExtractorFactory::CreateFromService %s", mime);UpdateExtractors(nullptr);// initialize source decryption if neededsource->DrmInitialization(nullptr /* mime */);void *meta = nullptr;MediaExtractor::CreatorFunc creator = NULL;MediaExtractor::FreeMetaFunc freeMeta = nullptr;float confidence;sp<ExtractorPlugin> plugin;creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);if (!creator) {ALOGV("FAILED to autodetect media content.");return NULL;}MediaExtractor *ret = creator(source.get(), meta);if (meta != nullptr && freeMeta != nullptr) {//释放元数据freeMeta(meta);}ALOGV("Created an extractor '%s' with confidence %.2f",ret != nullptr ? ret->name() : "<null>", confidence);return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);
          }......MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(DataSourceBase *source, float *confidence, void **meta,MediaExtractor::FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin) {*confidence = 0.0f;*meta = nullptr;std::shared_ptr<List<sp<ExtractorPlugin>>> plugins;{Mutex::Autolock autoLock(gPluginMutex);if (!gPluginsRegistered) {return NULL;}plugins = gPlugins;}// Mediatek Android Patch BeginMediaExtractor::CreatorFunc creator = NULL;//Sniff函数是创建解析器最重要的一个环节,以上代码可以看这里遍历了plugins迭代器,自从mediaExtractor服务从mediaserver中分离出来之后,所有的Extractor(如MPEG4Extractor、MP3Extractor等等)都在开机的时候就加载起来了,这里plugins就是开机时候将所有extractor都存进来了,所以这里的遍历plugins,同时调用每一个extractor的sniff,每一个Extractor中sniff函数都会根据datasource的头部来确定播放文件的类型,根据confidence选择最合适的extractorfor (auto it = plugins->begin(); it != plugins->end(); ++it) {if ((creator = (*it)->def.sniff(source, confidence, meta, freeMeta))) {plugin = *it;return creator;}}return NULL;// Mediatek Android Patch End
          }
        • frameworks/av/services/mediaextractor/MediaExtractorService.cpp

          sp<IMediaExtractor> MediaExtractorService::makeExtractor(const sp<IDataSource> &remoteSource, const char *mime) {ALOGV("@@@ MediaExtractorService::makeExtractor for %s", mime);//获取本地服务sp<DataSource> localSource = CreateDataSourceFromIDataSource(remoteSource);//创建服务sp<IMediaExtractor> extractor = MediaExtractorFactory::CreateFromService(localSource, mime);ALOGV("extractor service created %p (%s)",extractor.get(),extractor == nullptr ? "" : extractor->name());if (extractor != nullptr) {registerMediaExtractor(extractor, localSource, mime);return extractor;}return nullptr;
          }
          

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1559437.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

带外管理卡虚拟控制台连接总结--持续更新

为避免维护服务器、小型机为连接虚拟控制台&#xff0c;采用的办法不对而导致浪费时间&#xff0c;特做以下总结&#xff1a; ##1、DELL PowerEdge R510 idrac 6(intergrated dell remote access contoller 6) server 2003 R2 SP2 用chomre 版本 47.0.2526.80 m打开https://…

C++:thread | condition_variable|mutex

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 文章目录 引言thread创建线程传递参数给线程函数 mutexmutex常见用法 condition_variable&#xff1a;条件变量生产消费模型 引言 相信大家在Linux系统编程中都接触过线程创建和退出的相关系统调用&#xff0…

【数据结构与算法】简单聊聊图数据的存储

文章目录 1. 邻接矩阵&#xff08;Adjacency Matrix&#xff09;2. 邻接表&#xff08;Adjacency List&#xff09;3. 邻接多重表4. 十字链表5. 图数据库&#xff08;Graph Database&#xff09; 存储图数据的方法主要有几种&#xff0c;每种方法都有其特定的应用场景和优缺点。…

毕业设计项目-古典舞在线交流平台的设计与实现(源码/论文)

项目简介 基于springboot实现的&#xff0c;主要功能如下&#xff1a; 技术栈 后端框框&#xff1a;springboot/mybatis 前端框架&#xff1a;html/JavaScript/Css/vue/elementui 运行环境&#xff1a;JDK1.8/MySQL5.7/idea&#xff08;可选&#xff09;/Maven3&#xff08…

什么是物联网nb水表?

物联网NB水表是一种利用NB-IoT(窄带物联网)技术实现远程数据传输的智能水表。这种水表不仅能够精确计量用户的用水量&#xff0c;还能通过无线通信技术实现数据的远程传输和管理。下面我们来详细介绍物联网NB水表的主要特点和功能。 一、基本概念 -定义&#xff1a;物联网NB水…

如何优化spotbugsXml.xml文件来方便debug的落地方案来了

不优化的spotbugsXml.xml 使用maven 构建来运行spotbugs的小伙伴都知道&#xff0c;执行完下面的命令后 mvn clean install -U spotbugs:spotbugs 会在默认的在target目录下生成一个spotbugsXml.xml 文件&#xff0c;而打开这个文件&#xff0c;想要debug里面的具体问题&am…

嵌入式面试——FreeRTOS篇(六) 任务通知

本篇为&#xff1a;FreeRTOS 任务通知篇 任务通知简介 1、任务通知介绍 答&#xff1a; 任务通知&#xff1a;用来通知任务的&#xff0c;任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。 使用队列、信号量、事件标志组时都需要另外创建一个结构体&#xff0c…

Ubuntu终端配置

选择shell shell有很多&#xff0c;默认的是bash&#xff0c;一般就够用里&#xff0c;想要花里胡哨点就用zsh&#xff0c;还有最近比较火的fish 如果在刚开始安装完Ubuntu没有改shell&#xff0c;后面就不要改了。 安装的软件会设置环境变量&#xff0c;这些环境变量都是写入…

QDateTime 使用详解

QDateTime 是 Qt 框架中用于处理日期和时间的类。本篇文章详细介绍、通过示例 快速了解QDateTime的各种操作&#xff0c;包括: 当前时间、获取日期和时间、获取日期、获取时间、获取时间戳、格式化输出、年、月、日、QTime时间、获取微妙、操作日期和时间、添加时间、减去时间、…

无人机避障——4D毫米波雷达点云滤波去噪(四)

噪声的来源&#xff1a; 对于4D毫米波雷达的前后两帧点云数据进行去噪&#xff0c;可以采用多种方法。首先&#xff0c;需要了解点云数据的噪声来源&#xff0c;可能是由于硬件限制、环境干扰或目标本身的反射特性等因素造成的。噪声点通常包括漂移点、孤立点、冗余点和混杂点…

毕业设计项目——基于RISC-V的标签化跨层调度应用任务管理(论文/代码)

完整的论文代码见文章末尾 以下为核心内容 摘要 在现代操作系统中&#xff0c;高效的系统调度策略对于优化系统性能、提高资源利用率和保证系统稳定性至关重要。本文提出了一个基于Linux进程文件系统&#xff08;procfs&#xff09;的系统监控工具&#xff0c;旨在通过实时收…

Spring Cloud全解析:链路追踪之springCloudSleuth简介

文章目录 springCloudSleuth简介链路追踪&#xff1f;SpringCloudSleuth术语链路示意图zipkin依赖配置 springCloudSleuth简介 链路追踪&#xff1f; 什么是链路追踪&#xff1f;就是将一次分布式请求还原成调用链路&#xff0c;将一次分布式请求的调用情况集中展示&#xff…

算法:1、动态规划算法DP(Dynamic Programming)

算法介绍 动态规划&#xff08;Dynamic Programming&#xff0c;DP&#xff09;‌&#xff0c;通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。它的关键思想是对于最终结果依赖前序步骤的问题&#xff0c;将结果定义为状态值dp&#xff0c;然后推导出后续步骤由…

深度学习常见问题

1.YOLOV5和YOLOV8的区别 YOLOv5 和 YOLOv8 是两个版本的 YOLO&#xff08;You Only Look Once&#xff09;目标检测算法&#xff0c;它们在网络架构、性能优化、功能扩展等方面有显著的区别。YOLOv5 是 YOLO 系列的重要改进版本&#xff0c;而 YOLOv8 是最新的一次重大升级&am…

SQL性能优化指南:如何优化MySQL多表join场景

目录 多表join问题SQL 这里解释下 Using join buffer (Block Nested Loop)&#xff1a; 对性能产生的影响&#xff1a; 三种join算法介绍 join操作主要使用以下几种算法&#xff1a; &#xff08;1&#xff09;Nested Loop Join &#xff08;2&#xff09;Block Nested …

搭建企业域名服务器案例

任务要求&#xff1a; 某企业要建立一台应用于以下情况的主域名服务器 拥有一个C类网段地址&#xff0c;为202.101.55.0。企业域名注册为company.com。域名服务器的IP地址定位为202.101.55.55&#xff0c;主机名为dns.company.com。企业网通过路由器与Internet连接。要解析的…

第九届清洁能源与发电技术国际学术会议(CEPGT 2024)

第九届清洁能源与发电技术国际学术会议&#xff08;CEPGT 2024&#xff09; 2024 9th International Conference on Clean Energy and Power Generation Technology (CEPGT 2024) 【早投稿早录用&#xff0c;享受早鸟优惠】 第九届清洁能源与发电技术国际学术会议&#xff0…

记录一个Ajax发送JSON数据的坑,后端RequestBody接收参数小细节?JSON对象和JSON字符串的区别?

上半部分主要介绍我实际出现的问题&#xff0c;最终下面会有总结。 起因&#xff1a;我想发送post请求的data&#xff0c;但是在浏览器中竟然被搞成了地址栏编码 如图前端发送的ajax请求数据 如图发送的请求体&#xff1a; 很明显是keyvalue这种形式&#xff0c;根本就不是…

开源的键鼠共享工具「GitHub 热点速览」

十一长假回来&#xff0c;我的手放在落灰的键盘上都有些陌生了&#xff0c;红轴竟敲出了青轴般的响声&#xff0c;仿佛在诉说对假期结束的不甘。 假期回归的首更&#xff0c;让我们看看又有什么好玩的开源项目冲上了开源热榜。一套键盘和鼠标控制多台电脑的工具 deskflow&#…

supOS加速数实融合发展

作为工业操作系统领军企业&#xff0c;蓝卓受邀参加2024金砖国家新工业革命伙伴关系论坛&#xff0c;深度参与多个环节。在9月11日召开的金砖国家新工业革命伙伴关系论坛产融合作专题研讨上&#xff0c;蓝卓总经理谭彰分享了supOS在产融协同的最新实践&#xff0c;以及supOS进入…