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 allowed mAppOpsCallback = 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那么就获取主线程的looper Looper 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 MediaPlayer sp<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 class jobject 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方法 postEventFromNative env->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; } // 获取静态方法postEventFromNative fields.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同步执行功能失败时,置为false send = 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 code if ((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 code jniThrowException( env, exception, message); } else { char msg[256]; // append the status code to the message sprintf(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服务在客户端的代理对象BpMediaPlayerService const 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) { //获取ServiceManager sp<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对象转换为MediaPlayerService sMediaPlayerService = 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 Begin if (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中 setAudioSink mAudioOutput = 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 type sp<MediaPlayerBase> p = getPlayer(); if ((p != NULL) && (p->playerType() != playerType)) { ALOGV("delete player"); p.clear(); } //根据播放器类型创建播放器,并设置监听和pid号 if (p == NULL) { p = MediaPlayerFactory::createPlayer(playerType, mListener, mPid); } //设置uid if (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)) { //本地路径,直接赋值给path path = 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 memory return; } 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 Begin bool 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对象setDataSource status_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服务在客户端的代理对象BpMediaPlayerService const 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) method String16 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 mStatus close(fd); return mStatus = status; } else { // Mediatek Android Patch Begin const 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
frameworks/av/media/libmediaplayerservice/MediaPlayerService.cppMediaPlayerService::MediaPlayerService() { ALOGV("MediaPlayerService created"); mNextConnId = 1; // Mediatek Android Patch Begin #ifdef BUILD_WITH_TEE MDrv_SYS_GlobalInit(); TEEC_Initialize(); #endif if (IS_VR360_SUPPORTED) Mdrv_SYS_VR360_Restore_BW_Setting(); // Mediatek Android Patch End // 注册播放器工厂 MediaPlayerFactory::registerBuiltinFactories(); }
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_MM registerFactory_l(new MstPlayerFactory(), MST_PLAYER); #ifdef SUPPORT_IMAGEPLAYER registerFactory_l(new ImagePlayerFactory(), IMG_PLAYER); #endif #ifdef SUPPORT_MSTARPLAYER registerFactory_l(new MstarPlayerFactory(), MSTAR_PLAYER); #endif #endif registerVendorFactories(); // Mediatek Android Patch End sInitComplete = 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_MM char 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_PLAYER return 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 Begin MST_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等都继承了IFactoryclass NuPlayerFactory : public MediaPlayerFactory::IFactory { public: // Mediatek Android Patch Begin #ifdef BUILD_WITH_MSTAR_MM virtual 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 clips return 1.0; if (ident == 0xc450fbff && length == 40541) // FIXME: for CTS testGapless1 test work around return 1.0; if (!memcmp(buf+3, " ftypM4A ", 9) && (length == 46180 || length == 83892)) // FIXME: for CTS testGapless2&3 test work around return 1.0; // FIXME: for AN7 CTS closed caption tests work around // testChangeSubtitleTrack // testDeselectTrackForSubtitleTracks // testGetTrackInfoForVideoWithSubtitleTracks if (length == 210936) return 1.0; // FIXME: for AN7 CTS NativeDecoderTest#testMuxerVp8 if (length == 2829176) return 1.0; // FIXME: for AN8 CTS android.media.cts.MediaPlayerDrmTest if (length == 3012095) { return 1.0; } // FIXME: for AN8 GTS MediaPlayerTest if (length == 1481047 || length == 8127587) return 1.0; return 0.0; } #endif // Mediatek Android Patch End // Mediatek Android Patch Begin virtual float scoreFactory(const sp<IMediaPlayer>& /*client*/, const char* url, const sp<IMediaHTTPService>& /* httpService */, float curScore) { // Mediatek Android Patch End static const float kOurScore = 0.8; if (kOurScore <= curScore) return 0.0; // Mediatek Android Patch Begin #ifdef BUILD_WITH_MARLIN if (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_MM if (strcasestr(url, ".ogg") != NULL) { // use NuPlayer to play OGG clips return 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 clips return kOurScore; } if (!(strncmp(url, "data:", 5))) { // using NuPlayer when url type is the "data:" return kOurScore; } #endif // FIXME: for AN8 GTS MediaPlayerTest if (!strcasecmp(url, "https://storage.googleapis.com/wvmedia/cenc/h264/llama/llama_aac_audio.mp4")) return 1.0; // Mediatek Android Patch End if (!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_NUPLAYER if (strstr(url, "/sample_aes/") || strstr(url, "/audio_only/") || strstr(url, "/unmuxed_1500k/")) { return 1.0; } #endif // Mediatek Android Patch End return kOurScore; } if (strstr(url,"m3u8")) { // Mediatek Android Patch Begin #ifdef SPECIAL_STREAM_USE_NUPLAYER if (strstr(url, "/sample_aes/") || strstr(url, "/audio_only/") || strstr(url, "/unmuxed_1500k/")) { return 1.0; } #endif // Mediatek Android Patch End return kOurScore; } if (strstr(url, "/videoplayback/")) { // use NuPlayer to play Sonyliv ad return 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 Begin return 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); } };
- mediaplayer和mediacodec最终的调用关系
-
-
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_l status_t ret = prepareAsync_l(); if (ret != NO_ERROR) { mLockThreadId = 0; return ret; } if (mPrepareSync) { //准备失败,阻塞等待准备完成 mSignal.wait(mLock); // wait for prepare done mPrepareSync = 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 End if ( (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_PREPARING mCurrentState = MEDIA_PLAYER_PREPARING; //调用服务端的prepareAsync return 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.player if (property_check_multiple_payerback_process("mstar.multiple.player") == true) { //支持倍数解码 mEnableMultiDecode = true; } //如果是mstplayer if (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 //调用播放器的prepareAsync status_t ret = p->prepareAsync(); #if CALLBACK_ANTAGONIZER ALOGD("start Antagonizer"); if (ret == NO_ERROR) mAntagonizer->start(); #endif return 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 record mAnalyticsItem = 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_PREPARING mState = 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 start mAtEOS = false; //把播放器状态置为STATE_STOPPED_AND_PREPARING mState = 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_MARLIN if (IsWasabiSourceUrl(url)) { //这是啥? source = WasabiSourceCreate(notify, url, headers, mUIDValid, mUID); } else #endif const 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 End if (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/failure mDataSourceType = 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就是NuPlayerDriver sp<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> ¬ify, 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() { //创建并启动一个Looper if (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并传入url sp<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) { //获取传过来的Uri CHECK(msg->findString("url", &mMasterURL)); // TODO currently we don't know if we are coming here from incognito mode ALOGI("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; //获取一个M3U8Parser sp<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) { //恢复播放源需要强制连接HTTP if (!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) { //添加分段请求的range headers.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大小错误时,赋予最大值65536 size = 65536; } //初始化一个缓冲队列buffer sp<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 use mHTTPDataSource->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; //调用fetchFile ssize_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 //将获取到的缓存数据包装成M3UParser sp<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)); } #endif return 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返回是安全的,再设置为true mIsSecure = false; if (!mUri.empty()) { //流媒体服务器创建dataSource const 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://"为开头,创建NuCachedSource2 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 source // 初始化extractor status_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 app FLAG_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();// 获取metadata size_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);//获取当前通道的metadata 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()); /** 常用的音频类型 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); } } //保存每一个Track 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. //返回值不影响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 extractor ALOGV("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 needed source->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 Begin MediaExtractor::CreatorFunc creator = NULL; //Sniff函数是创建解析器最重要的一个环节,以上代码可以看这里遍历了plugins迭代器,自从mediaExtractor服务从mediaserver中分离出来之后,所有的Extractor(如MPEG4Extractor、MP3Extractor等等)都在开机的时候就加载起来了,这里plugins就是开机时候将所有extractor都存进来了,所以这里的遍历plugins,同时调用每一个extractor的sniff,每一个Extractor中sniff函数都会根据datasource的头部来确定播放文件的类型,根据confidence选择最合适的extractor for (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; }
-
- setDataSource
-