flow of events during media player creation in android

8

Click here to load reader

Upload: somenath-mukhopadhyay

Post on 09-Jul-2015

308 views

Category:

Technology


1 download

DESCRIPTION

This document is a code walk through for the media player subsystem in Android

TRANSCRIPT

Page 1: Flow of events during Media Player creation in Android

Flow of events during Media Player Creation inAndroid

Somenath Mukhopadhyay

[email protected]

The flow of events of the Android media player is complex. This document will serve as ahand-holding for code walkthrough of the Android multimedia framework for the Android lovers.

To start with, let me give you the call stack of the media player framework in Android. This hasbeen depicted as in the following diagram.

Now lets come to the fact findings. There are two sides of the Android Media Framework. What weas an user see is the Java interface which is called the Mediaplayer.java. However, this javainterface interacts with a native mediaplayer object through Java Native Interface (JNI)mechanism. This interaction is done through the functionalities defined inAndroid_media_Mediaplayer.cpp. In this file the framework engineers have kept all thenecessary JNI functions.

Now when we are about to start the MediaPlayer, the JNI function that is called is the

private native final void native_setup.

This function is actually responsible for creating a C++ mediaplayer object in the native side andstoring it as an opaque reference in the Java client side. So when we interact with the client sideJava mediaplayer object, we internally interact with this native C++ object.

Page 2: Flow of events during Media Player creation in Android

The JNI layer actually delegates its task to a Mediaplayer object. The functionalities of this C++class are defined in the file Mediaplayer.cpp.

Now the next step is that we set a data source in the Java side using the function setDataSourcefunction in which the URI of the data source is passed. This in turn calls the native function

android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)

In the native source, this function is defined as

android_media_MediaPlayer_setDataSource(JNIEnv *env, jobject thiz, jstring path)

{

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 *pathStr = env->GetStringUTFChars(path, NULL);if (pathStr == NULL) { // Out of memory jniThrowException(env, "java/lang/RuntimeException", "Out of memory");

return;

}

LOGV("setDataSource: path %s", pathStr);

status_t opStatus = mp->setDataSource(pathStr);

// Make sure that local ref is released before a potential exception

env->ReleaseStringUTFChars(path, pathStr);

process_media_player_call( env, thiz, opStatus, "java/io/IOException", "setDataSource

failed." );}

Look at the line:

status_t opStatus = mp->setDataSource(pathStr);

This function is defined as

status_t MediaPlayer::setDataSource(const char *url){

LOGV("setDataSource(%s)", url);

status_t err = BAD_VALUE;if (url != NULL) { const sp<IMediaPlayerService>& service(getMediaPlayerService()); if (service != 0) {

Page 3: Flow of events during Media Player creation in Android

sp<IMediaPlayer> player(service->create(getpid(), this, url)); err = setDataSource(player); }

}

return err;}

Look at the line :

sp<IMediaPlayer> player(service->create(getpid(), this, url));

It actually takes the help of the IMediaPlayerservice layer and calls the create function on this. Thisfunction can be found in \\base\media\libmedia\IMediaPlayerService.cpp file.

The create function of the MediaPlayerService looks like the following. It can be found at\\base\media\libmediaplayerservice\MediaPlayerService.cpp.

sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, constsp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>&client, const char* url){

int32_t connId = android_atomic_inc(&mNextConnId);sp<Client> c = new Client(this, pid, connId, client);LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);

if (NO_ERROR != c->setDataSource(url)){

c.clear();

return c;}

wp<Client> w = c;Mutex::Autolock lock(mLock);

mClients.add(w);

return c;}

Look at the line :

sp<Client> c = new Client(this, pid, connId, client)

The client constructor is as the following:

sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>&client, const char* url){

int32_t connId = android_atomic_inc(&mNextConnId);sp<Client> c = new Client(this, pid, connId, client);

Page 4: Flow of events during Media Player creation in Android

LOGV("Create new client(%d) from pid %d, url=%s, connId=%d", connId, pid, url, connId);

if (NO_ERROR != c->setDataSource(url)){

c.clear();

return c;}

wp<Client> w = c;Mutex::Autolock lock(mLock);

mClients.add(w);

return c;}

Now look at the following line (line number 6) of sp<IMediaPlayer>MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClient>& client, const char* url)

if (NO_ERROR != c->setDataSource(url))

So we are basically calling the setDataSource on the Client.

This function is like the following:

status_t MediaPlayerService::Client::setDataSource(const char *url){

LOGV("setDataSource(%s)", url);

if (url == NULL) return UNKNOWN_ERROR;

if (strncmp(url, "content://", 10) == 0) { // get a filedescriptor for the content Uri and

// pass it to the setDataSource(fd) method

String16 url16(url);

int fd = android::openContentProviderFile(url16); if (fd < 0) {

LOGE("Couldn't open fd for %s", url);

return UNKNOWN_ERROR; }

setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus

close(fd);

return mStatus;} else { player_type playerType = getPlayerType(url); LOGV("player type = %d", playerType);

// create the right type of player

sp<MediaPlayerBase> p = createPlayer(playerType); if (p == NULL) return NO_INIT;

if (!p->hardwareOutput()) {

Page 5: Flow of events during Media Player creation in Android

mAudioOutput = new AudioOutput(); static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);

}

// now set data source

LOGV(" setDataSource");

mStatus = p->setDataSource(url); if (mStatus == NO_ERROR) mPlayer = p; return mStatus;}

}

From the above code snippet it becomes clear that either we do

if (strncmp(url, "content://", 10) == 0) { // get a filedescriptor for the content Uri and

// pass it to the setDataSource(fd) method

String16 url16(url);

int fd = android::openContentProviderFile(url16); if (fd < 0) {

LOGE("Couldn't open fd for %s", url);

return UNKNOWN_ERROR; }

setDataSource(fd, 0, 0x7fffffffffLL); // this sets mStatus

close(fd);

return mStatus;

●● or we do the following:

else { player_type playerType = getPlayerType(url); //here it extracts the Player Type fromthe URL.

LOGV("player type = %d", playerType);

// create the right type of player

sp<MediaPlayerBase> p = createPlayer(playerType);

..............

..............

In the first case the setDataSource (line 12) function looks like the following:

status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64_t length){

Page 6: Flow of events during Media Player creation in Android

LOGV("setDataSource fd=%d, offset=%lld, length=%lld", fd, offset, length);

struct stat sb;int ret = fstat(fd, &sb);if (ret != 0) { LOGE("fstat(%d) failed: %d, %s", fd, ret, strerror(errno));

return UNKNOWN_ERROR;}

LOGV("st_dev = %llu", sb.st_dev);

LOGV("st_mode = %u", sb.st_mode);

LOGV("st_uid = %lu", sb.st_uid);

LOGV("st_gid = %lu", sb.st_gid);

LOGV("st_size = %llu", sb.st_size);

if (offset >= sb.st_size) { LOGE("offset error");

::close(fd);

return UNKNOWN_ERROR;}

if (offset + length > sb.st_size) { length = sb.st_size - offset; LOGV("calculated length = %lld", length);

}

player_type playerType = getPlayerType(fd, offset, length); //here it gets the file typefrom the File descriptor

LOGV("player type = %d", playerType);

// create the right type of player

sp<MediaPlayerBase> p = createPlayer(playerType);if (p == NULL) return NO_INIT;

if (!p->hardwareOutput()) { mAudioOutput = new AudioOutput(); static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);

}

// now set data source

mStatus = p->setDataSource(fd, offset, length);if (mStatus == NO_ERROR) mPlayer = p;return mStatus;}

Look at the line :

sp<MediaPlayerBase> p = createPlayer(playerType). It becomes clear that we create theplayer here.

The sp<MediaPlayerBase> p = createPlayer (playerType) actually creates the right player.

Page 7: Flow of events during Media Player creation in Android

In the second case (the else part) we call sp<MediaPlayerBase> p = createPlayer(playerType)at line 6. We extract the file type from the URL. This helps us in creating the right player object.

The createPlayer function looks like the following:

sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerType){

// determine if we have the right player type

sp<MediaPlayerBase> p = mPlayer;if ((p != NULL) && (p->playerType() != playerType)) { LOGV("delete player");

p.clear();

}

if (p == NULL) { p = android::createPlayer(playerType, this, notify);}

return p;}

Hence it actually delegates the task to p = android::createPlayer(playerType, this, notify);

The above function is as follows;

static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, notify_callback_f notifyFunc)

{

sp<MediaPlayerBase> p;switch (playerType) {#ifndef NO_OPENCORE

case PV_PLAYER: LOGV(" create PVPlayer");

p = new PVPlayer(); break;

#endif

case SONIVOX_PLAYER: LOGV(" create MidiFile");

p = new MidiFile(); break;

case VORBIS_PLAYER: LOGV(" create VorbisPlayer");

p = new VorbisPlayer(); break;

}

if (p != NULL) { if (p->initCheck() == NO_ERROR) { p->setNotifyCallback(cookie, notifyFunc);

} else { p.clear();

}

Page 8: Flow of events during Media Player creation in Android

}

if (p == NULL) { LOGE("Failed to create player object");

}

return p;}

Thus we find that the right player is created through the parameterized factory functioncreatePlayer.

i hope this explains how the right mediaplayer is created from the Uri passed in the Java clientside interface of the Mediaplyer.