当前位置: 首页 > news >正文

Android 源码解析 之 MediaPlayer

Android 源码解析 之 MediaPlayer

我们可以使用如下工具方法获取视频或者音频时长。

internal fun getDuration(context: Context, uri: Uri): Int {val mediaPlayer: MediaPlayer? = MediaPlayer.create(context, uri)val duration: Int = mediaPlayer?.duration ?: 0Log.i(TAG, "MusicScreen -> duration: $duration")mediaPlayer?.release()return duration
}

源码查看

MediaPlayer.java

// holder为null,audioAttributes为null,audioSessionId为0
public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,AudioAttributes audioAttributes, int audioSessionId) {try {MediaPlayer mp = new MediaPlayer(context, audioSessionId); // 1final AudioAttributes aa = audioAttributes != null ? audioAttributes :new AudioAttributes.Builder().build();mp.setAudioAttributes(aa); // 2mp.setDataSource(context, uri); // 3if (holder != null) {mp.setDisplay(holder);}mp.prepare();return mp;} catch (IOException ex) {Log.d(TAG, "create failed:", ex);// fall through} catch (IllegalArgumentException ex) {Log.d(TAG, "create failed:", ex);// fall through} catch (SecurityException ex) {Log.d(TAG, "create failed:", ex);// fall through}return null;
}
// 构造函数
private MediaPlayer(Context context, int sessionId) {super(new AudioAttributes.Builder().build(),AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);Looper looper;if ((looper = Looper.myLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else if ((looper = Looper.getMainLooper()) != null) {mEventHandler = new EventHandler(this, looper);} else {mEventHandler = null;}mTimeProvider = new TimeProvider(this);mOpenSubtitleSources = new Vector<InputStream>();AttributionSource attributionSource =context == null ? AttributionSource.myAttributionSource(): context.getAttributionSource();// set the package name to empty if it was nullif (attributionSource.getPackageName() == null) {attributionSource = attributionSource.withPackageName("");}/* Native setup requires a weak reference to our object.* It's easier to create it here than in C++.*/try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {native_setup(new WeakReference<>(this), attributionSourceState.getParcel(),resolvePlaybackSessionId(context, sessionId)); // 1}baseRegisterPlayer(getAudioSessionId()); // 2
}public void setDataSource(FileDescriptor fd)throws IOException, IllegalArgumentException, IllegalStateException {// intentionally less than LONG_MAXsetDataSource(fd, 0, 0x7ffffffffffffffL);
}public void setDataSource(FileDescriptor fd, long offset, long length)throws IOException, IllegalArgumentException, IllegalStateException {try (ParcelFileDescriptor modernFd = FileUtils.convertToModernFd(fd)) {if (modernFd == null) {_setDataSource(fd, offset, length);} else {_setDataSource(modernFd.getFileDescriptor(), offset, length);}} catch (IOException e) {Log.w(TAG, "Ignoring IO error while setting data source", e);}
}private native void _setDataSource(FileDescriptor fd, long offset, long length)throws IOException, IllegalArgumentException, IllegalStateException;public void prepare() throws IOException, IllegalStateException {Parcel piidParcel = createPlayerIIdParcel();try {int retCode = _prepare(piidParcel);if (retCode != 0) {Log.w(TAG, "prepare(): could not set piid " + mPlayerIId);}} finally {piidParcel.recycle();}scanInternalSubtitleTracks();// DrmInfo, if any, has been resolved by now.synchronized (mDrmLock) {mDrmInfoResolved = true;}
}public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException {if (attributes == null) {final String msg = "Cannot set AudioAttributes to null";throw new IllegalArgumentException(msg);}baseUpdateAudioAttributes(attributes);Parcel pattributes = Parcel.obtain();attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);pattributes.recycle();
}
public native int getDuration();

可以看到 MediaPlayer的create的方法返回一个可空的MediaPlayer对象,并且处理掉了异常。
其父类是PlayerBase


protected void baseRegisterPlayer(int sessionId) {try {mPlayerIId = getService().trackPlayer(new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),sessionId));} catch (RemoteException e) {Log.e(TAG, "Error talking to audio service, player will not be tracked", e);}
}void baseUpdateAudioAttributes(@NonNull AudioAttributes attr) {if (attr == null) {throw new IllegalArgumentException("Illegal null AudioAttributes");}try {getService().playerAttributes(mPlayerIId, attr);} catch (RemoteException e) {Log.e(TAG, "Error talking to audio service, audio attributes will not be updated", e);}synchronized (mLock) {mAttributes = attr;}
}

调用流程图如下:
image

Android中的sp、wp

system/core/libutils/binder/include/utils/StrongPointer.h

// TODO: Ideally we should find a way to increment the reference count before running the
// constructor, so that generating an sp<> to this in the constructor is no longer dangerous.
template <typename T>
template <typename... Args>
sp<T> sp<T>::make(Args&&... args) {T* t = new T(std::forward<Args>(args)...);sp<T> result;result.m_ptr = t;t->incStrong(t);return result;
}

system/core/libutils/binder/include/utils/RefBase.h

template <typename T>
class wp
{
public:typedef typename RefBase::weakref_type weakref_type;inline constexpr wp() : m_ptr(nullptr), m_refs(nullptr) { }
// ...
}

可以使用智能指针的对象必须继承自RefBase.

native_init

frameworks/base/media/jni/android_media_MediaPlayer.cpp

struct fields_t {jfieldID    context;jfieldID    surface_texture;jmethodID   post_event;jmethodID   proxyConfigGetHost;jmethodID   proxyConfigGetPort;jmethodID   proxyConfigGetExclusionList;
};
static fields_t fields;
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;}fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative","(Ljava/lang/Object;IIILjava/lang/Object;)V");if (fields.post_event == NULL) {return;}fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");if (fields.surface_texture == NULL) {return;}env->DeleteLocalRef(clazz);clazz = env->FindClass("android/net/ProxyInfo");if (clazz == NULL) {return;}fields.proxyConfigGetHost =env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");fields.proxyConfigGetPort =env->GetMethodID(clazz, "getPort", "()I");fields.proxyConfigGetExclusionList =env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");env->DeleteLocalRef(clazz);// Modular DRMFIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");if (clazz) {GET_METHOD_ID(gStateExceptionFields.init, clazz, "<init>", "(ILjava/lang/String;)V");gStateExceptionFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));env->DeleteLocalRef(clazz);} else {ALOGE("JNI android_media_MediaPlayer_native_init couldn't ""get clazz android/media/MediaDrm$MediaDrmStateException");}gPlaybackParamsFields.init(env);gSyncParamsFields.init(env);gVolumeShaperFields.init(env);
}

native_setup

frameworks/base/media/jni/android_media_MediaPlayer.cpp

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,jobject jAttributionSource,jint jAudioSessionId)
{ALOGV("native_setup");Parcel* parcel = parcelForJavaObject(env, jAttributionSource);android::content::AttributionSourceState attributionSource;attributionSource.readFromParcel(parcel);sp<MediaPlayer> mp = sp<MediaPlayer>::make(attributionSource, static_cast<audio_session_t>(jAudioSessionId));if (mp == NULL) {jniThrowException(env, "java/lang/RuntimeException", "Out of memory");return;}// 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);
}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);}env->SetLongField(thiz, fields.context, (jlong)player.get());return old;
}

setMediaPlayer将Jave层的mNativeContext指针指向了native层的播放器.并处理了一下智能指针的计数。
sp<MediaPlayer>::make意思就是调用MediaPlayer的构造函数创建了了一个MediaPlayer对象。

frameworks/av/media/libmedia/include/media/mediaplayer.h
frameworks/av/media/libmedia/mediaplayer.cpp

MediaPlayer::MediaPlayer(const AttributionSourceState& attributionSource,const audio_session_t sessionId) : mAttributionSource(attributionSource)
{ALOGV("constructor");mListener = NULL;mCookie = NULL;mStreamType = AUDIO_STREAM_MUSIC;mAudioAttributesParcel = NULL;mCurrentPosition = -1;mCurrentSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;mSeekPosition = -1;mSeekMode = MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC;mCurrentState = MEDIA_PLAYER_IDLE;mPrepareSync = false;mPrepareStatus = NO_ERROR;mLoop = false;mLeftVolume = mRightVolume = 1.0;mVideoWidth = mVideoHeight = 0;mLockThreadId = 0;if (sessionId == AUDIO_SESSION_ALLOCATE) {mAudioSessionId = static_cast<audio_session_t>(AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION));} else {mAudioSessionId = sessionId;}AudioSystem::acquireAudioSessionId(mAudioSessionId, (pid_t)-1, (uid_t)-1); // always in client.mSendLevel = 0;mRetransmitEndpointValid = false;
}

getAudioSessionId

frameworks/base/media/jni/android_media_MediaPlayer.cpp

static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env,  jobject thiz) {ALOGV("get_session_id()");sp<MediaPlayer> mp = getMediaPlayer(env, thiz);if (mp == NULL ) {jniThrowException(env, "java/lang/IllegalStateException", NULL);return 0;}return (jint) mp->getAudioSessionId();
}

frameworks/av/media/libmedia/mediaplayer.cpp

audio_session_t MediaPlayer::getAudioSessionId()
{Mutex::Autolock _l(mLock);return mAudioSessionId;
}

system/media/audio/include/system/audio.h

typedef enum : int32_t {AUDIO_SESSION_DEVICE = HAL_AUDIO_SESSION_DEVICE,AUDIO_SESSION_OUTPUT_STAGE = HAL_AUDIO_SESSION_OUTPUT_STAGE,AUDIO_SESSION_OUTPUT_MIX = HAL_AUDIO_SESSION_OUTPUT_MIX,
#ifndef AUDIO_NO_SYSTEM_DECLARATIONSAUDIO_SESSION_ALLOCATE = 0,AUDIO_SESSION_NONE = 0,
#endif
} audio_session_t;
typedef int audio_unique_id_t;/* A unique ID with use AUDIO_UNIQUE_ID_USE_EFFECT */
typedef int audio_effect_handle_t;/* Possible uses for an audio_unique_id_t */
typedef enum {AUDIO_UNIQUE_ID_USE_UNSPECIFIED = 0,AUDIO_UNIQUE_ID_USE_SESSION = 1, // audio_session_t// for allocated sessions, not special AUDIO_SESSION_*AUDIO_UNIQUE_ID_USE_MODULE = 2,  // audio_module_handle_tAUDIO_UNIQUE_ID_USE_EFFECT = 3,  // audio_effect_handle_tAUDIO_UNIQUE_ID_USE_PATCH = 4,   // audio_patch_handle_tAUDIO_UNIQUE_ID_USE_OUTPUT = 5,  // audio_io_handle_tAUDIO_UNIQUE_ID_USE_INPUT = 6,   // audio_io_handle_tAUDIO_UNIQUE_ID_USE_CLIENT = 7,  // client-side players and recorders// FIXME should move to a separate namespace;// these IDs are allocated by AudioFlinger on client request,// but are never used by AudioFlingerAUDIO_UNIQUE_ID_USE_MAX = 8,     // must be a power-of-twoAUDIO_UNIQUE_ID_USE_MASK = AUDIO_UNIQUE_ID_USE_MAX - 1
} audio_unique_id_use_t;
/* Reserved audio_unique_id_t values.  FIXME: not a complete list. */
#define AUDIO_UNIQUE_ID_ALLOCATE AUDIO_SESSION_ALLOCATE

mAudioSessionIdMediaPlayer对象初始化时赋值的,构造参数传递过来的是0,即AUDIO_SESSION_ALLOCATE,所以调用如下方法创建一个

AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION));

frameworks/av/media/libaudioclient/AudioSystem.cpp

audio_unique_id_t AudioSystem::newAudioUniqueId(audio_unique_id_use_t use) {// Must not use AF as IDs will re-roll on audioserver restart, b/130369529.const sp<IAudioFlinger> af = get_audio_flinger();if (af == nullptr) return AUDIO_UNIQUE_ID_ALLOCATE;return af->newAudioUniqueId(use);
}
sp<IAudioFlinger> AudioSystem::get_audio_flinger() {return AudioFlingerServiceTraits::getService();
}
class AudioFlingerServiceTraits {
public:// ------- required by ServiceSingletonstatic constexpr const char* getServiceName() { return "media.audio_flinger"; }static void onNewService(const sp<media::IAudioFlingerService>& afs) {onNewServiceWithAdapter(createServiceAdapter(afs));}static void onServiceDied(const sp<media::IAudioFlingerService>& service) {ALOGW("%s: %s service died %p", __func__, getServiceName(), service.get());{std::lock_guard l(mMutex);if (!mValid) {ALOGW("%s: %s service already invalidated, ignoring", __func__, getServiceName());return;}if (!mService || mService->getDelegate() != service) {ALOGW("%s: %s unmatched service death pointers, ignoring",__func__, getServiceName());return;}mValid = false;if (mClient) {mClient->clearIoCache();} else {ALOGW("%s: null client", __func__);}}AudioSystem::reportError(DEAD_OBJECT);}static constexpr mediautils::ServiceOptions options() {return mediautils::ServiceOptions::kNone;}// ------- required by AudioSystemstatic sp<IAudioFlinger> getService(std::chrono::milliseconds waitMs = std::chrono::milliseconds{-1}) {static bool init = false;audio_utils::unique_lock ul(mMutex);if (!init) {if (!mDisableThreadPoolStart) {ProcessState::self()->startThreadPool();}if (multiuser_get_app_id(getuid()) == AID_AUDIOSERVER) {mediautils::skipService<media::IAudioFlingerService>(mediautils::SkipMode::kWait);mWaitMs = std::chrono::milliseconds(INT32_MAX);} else {mediautils::initService<media::IAudioFlingerService, AudioFlingerServiceTraits>();mWaitMs = std::chrono::milliseconds(property_get_int32(kServiceWaitProperty, kServiceClientWaitMs));}init = true;}if (mValid) return mService;if (waitMs.count() < 0) waitMs = mWaitMs;ul.unlock();// mediautils::getService() installs a persistent new service notification.auto service = mediautils::getService<media::IAudioFlingerService>(waitMs);ALOGD("%s: checking for service %s: %p", __func__, getServiceName(), service.get());ul.lock();// return the IAudioFlinger interface which is adapted// from the media::IAudioFlingerService.return mService;}
// ....
}

// TODO

音频服务的trackPlayer方法

playerAttributes

_setDataSource

_prepare

getDuration

TODO 1. Handler Native 2、invalidate doFrame 3、Binder Native

http://www.hskmm.com/?act=detail&tid=12486

相关文章:

  • 5. 二叉树
  • 第二周预习作业
  • Revit二次开发环境配置
  • CF1016G Appropriate Team
  • CF494C Helping People
  • 深入解析:Extract Chart Data Directly to Excel
  • AOSP Android12 Source 下载同步
  • 02020404 EF Core基础04-自增主键、Guid主键、混合自增、Hi/Lo算法、Migration深入、数据库其它迁移命令
  • 02020403 EF Core基础03-Fluent API、Data Annotation、两种配置的选择
  • Java中异步任务的执行方式有几种?
  • 广二联考题解补全计划:
  • Chapter 8 Contour / Shape Detection
  • 【左程云算法笔记016】双端队列-双链表和固定数组实现 - 教程
  • java相关问题:面向对象入门2与类的识别
  • EXCEL自动调整列宽的快捷键
  • 【C++实战⑬】解锁C++文件操作:从基础到实战的进阶之路 - 实践
  • 破解塔吊顶升高危难题!让事故率降 50%、审批快 70%
  • logicFlow________文档2
  • CF2086D Even String
  • logicflow___文档3
  • 2025年运营商API安全建设最佳实践:某头部省级电信案例解析与方案推荐
  • 软件工程第二次作业-第一次个人编程作业
  • 面向对象入门2与类的识别
  • 202508_天山固网_to
  • jmeter分布式压测
  • 怎么屏蔽 ahref.com 上你不想看到的网站链接(垃圾外链)
  • 浅谈字典树
  • go-mapus为局域网地图协作而生
  • 《手搓动态顺序表:从数组到自动扩容的华丽转身》 - 详解
  • 板子大全