003篇中讲到了最简单的mediaplayer播放,首先调用create函数创建一个实例,然后调用start进行播放。
现在来说明每个函数调用的时候底层都会做些什么。
/**
* Convenience method to create a MediaPlayer for a given resource id.
* On success, {@link #prepare()} will already have been called and must not be called again.
* <p>When done with the MediaPlayer, you should call {@link #release()},
* to free the resources. If not released, too many MediaPlayer instances will
* result in an exception.</p>
* <p>Note that since {@link #prepare()} is called automatically in this method,
* you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
* session ID (see {@link #setAudioSessionId(int)}) or audio attributes
* (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
*
* @param context the Context to use
* @param resid the raw resource id (<var>R.raw.<something></var>) for
* the resource to use as the datasource
* @return a MediaPlayer object, or null if creation failed
*/
public static MediaPlayer create(Context context, int resid) {
int s = AudioSystem.newAudioSessionId();
return create(context, resid, null, s > 0 ? s : 0);
}
/**
* Same factory method as {@link #create(Context, int)} but that lets you specify the audio
* attributes and session ID to be used by the new MediaPlayer instance.
* @param context the Context to use
* @param resid the raw resource id (<var>R.raw.<something></var>) for
* the resource to use as the datasource
* @param audioAttributes the {@link AudioAttributes} to be used by the media player.
* @param audioSessionId the audio session ID to be used by the media player,
* see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
* @return a MediaPlayer object, or null if creation failed
*/
public static MediaPlayer create(Context context, int resid,
AudioAttributes audioAttributes, int audioSessionId) {
try {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
if (afd == null) return null;
MediaPlayer mp = new MediaPlayer();
final AudioAttributes aa = audioAttributes != null ? audioAttributes :
new AudioAttributes.Builder().build();
mp.setAudioAttributes(aa);
mp.setAudioSessionId(audioSessionId);
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
afd.close();
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;
资源创建过程中,首先调用的是AudioSystem.newAudioSessionId创建一个AudioSession id.
设置了一个AudioAttributes,设置了AudioSessionId,设置了setDataSource,检查setDataSource函数。
/**
* Sets the data source (FileDescriptor) to use. The FileDescriptor must be
* seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
* to close the file descriptor. It is safe to do so as soon as this call returns.
*
* @param fd the FileDescriptor for the file you want to play
* @param offset the offset into the file where the data to be played starts, in bytes
* @param length the length in bytes of the data to be played
* @throws IllegalStateException if it is called in an invalid state
* @throws IllegalArgumentException if fd is not a valid FileDescriptor
* @throws IOException if fd can not be read
*/
public void setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException {
_setDataSource(fd, offset, length);
}
jni调用
private native void _setDataSource(FileDescriptor fd, long offset, long length)
throws IOException, IllegalArgumentException, IllegalStateException;
通过android_media_MediaPlayer.cpp (\frameworks\base\media\jni)
{"_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)
{
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;
}
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
ALOGV("setDataSourceFD: fd %d", fd);
process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
}
会调用Mediaplayer.cpp (\frameworks\av\media\libmedia)
status_t MediaPlayer::setDataSource(const sp<IDataSource> &source)
{
ALOGV("setDataSource(IDataSource)");
status_t err = UNKNOWN_ERROR;
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(this, mAudioSessionId));
if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
(NO_ERROR != player->setDataSource(source))) {
player.clear();
}
err = attachNewPlayer(player);
}
return err;
}
创建IMediaPlayerService服务,通过该服务创建IMediaPlayer,调用setDataSource实现远程调用。
status_t setDataSource(const sp<IDataSource> &source) {
Parcel data, reply;
data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
data.writeStrongBinder(IInterface::asBinder(source));
remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
return reply.readInt32();
}
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;
}
从代码中,我们可以看出,在Server端,我们新建的对象是Client,是MediaPlayerService的内部类。
然后调用setDataSource函数,还是在server端的client执行。
status_t MediaPlayerService::Client::setDataSource(
const sp<IDataSource> &source) {
sp<DataSource> dataSource = DataSource::CreateFromIDataSource(source);
player_type playerType = MediaPlayerFactory::getPlayerType(this, dataSource);
sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
// now set data source
setDataSource_post(p, p->setDataSource(dataSource));
return mStatus;
}