Nicolas Catania 88f3b81d06 Allow invoke in all the player states except idle and error.
Previously invoke would work only after prepare but not
when the player is in play or pause state (for instance).
This new change just check that the player has been initialized
and is not in the error state.

Bug:2488931

Change-Id: I7a69d1b6e3eec1e5dbdf7378ff2085329062595a
2010-03-10 16:17:30 -08:00

697 lines
20 KiB
C++

/* mediaplayer.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer"
#include <utils/Log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <media/mediaplayer.h>
#include <media/AudioTrack.h>
#include <surfaceflinger/Surface.h>
#include <binder/MemoryBase.h>
#include <utils/KeyedVector.h>
#include <utils/String8.h>
namespace android {
MediaPlayer::MediaPlayer()
{
LOGV("constructor");
mListener = NULL;
mCookie = NULL;
mDuration = -1;
mStreamType = AudioSystem::MUSIC;
mCurrentPosition = -1;
mSeekPosition = -1;
mCurrentState = MEDIA_PLAYER_IDLE;
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mLoop = false;
mLeftVolume = mRightVolume = 1.0;
mVideoWidth = mVideoHeight = 0;
mLockThreadId = 0;
}
MediaPlayer::~MediaPlayer()
{
LOGV("destructor");
disconnect();
IPCThreadState::self()->flushCommands();
}
void MediaPlayer::disconnect()
{
LOGV("disconnect");
sp<IMediaPlayer> p;
{
Mutex::Autolock _l(mLock);
p = mPlayer;
mPlayer.clear();
}
if (p != 0) {
p->disconnect();
}
}
// always call with lock held
void MediaPlayer::clear_l()
{
mDuration = -1;
mCurrentPosition = -1;
mSeekPosition = -1;
mVideoWidth = mVideoHeight = 0;
}
status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
{
LOGV("setListener");
Mutex::Autolock _l(mLock);
mListener = listener;
return NO_ERROR;
}
status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player)
{
status_t err = UNKNOWN_ERROR;
sp<IMediaPlayer> p;
{ // scope for the lock
Mutex::Autolock _l(mLock);
if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) ||
(mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) {
LOGE("setDataSource called in state %d", mCurrentState);
return INVALID_OPERATION;
}
clear_l();
p = mPlayer;
mPlayer = player;
if (player != 0) {
mCurrentState = MEDIA_PLAYER_INITIALIZED;
err = NO_ERROR;
} else {
LOGE("Unable to to create media player");
}
}
if (p != 0) {
p->disconnect();
}
return err;
}
status_t MediaPlayer::setDataSource(
const char *url, const KeyedVector<String8, String8> *headers)
{
LOGV("setDataSource(%s)", url);
status_t err = BAD_VALUE;
if (url != NULL) {
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(
service->create(getpid(), this, url, headers));
err = setDataSource(player);
}
}
return err;
}
status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
{
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
status_t err = UNKNOWN_ERROR;
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(getpid(), this, fd, offset, length));
err = setDataSource(player);
}
return err;
}
status_t MediaPlayer::invoke(const Parcel& request, Parcel *reply)
{
Mutex::Autolock _l(mLock);
const bool hasBeenInitialized =
(mCurrentState != MEDIA_PLAYER_STATE_ERROR) &&
((mCurrentState & MEDIA_PLAYER_IDLE) != MEDIA_PLAYER_STATE_IDLE);
if ((mPlayer != NULL) && hasBeenInitialized) {
LOGV("invoke %d", request.dataSize());
return mPlayer->invoke(request, reply);
}
LOGE("invoke failed: wrong state %X", mCurrentState);
return INVALID_OPERATION;
}
status_t MediaPlayer::suspend() {
Mutex::Autolock _l(mLock);
return mPlayer->suspend();
}
status_t MediaPlayer::resume() {
Mutex::Autolock _l(mLock);
return mPlayer->resume();
}
status_t MediaPlayer::setMetadataFilter(const Parcel& filter)
{
LOGD("setMetadataFilter");
Mutex::Autolock lock(mLock);
if (mPlayer == NULL) {
return NO_INIT;
}
return mPlayer->setMetadataFilter(filter);
}
status_t MediaPlayer::getMetadata(bool update_only, bool apply_filter, Parcel *metadata)
{
LOGD("getMetadata");
Mutex::Autolock lock(mLock);
if (mPlayer == NULL) {
return NO_INIT;
}
return mPlayer->getMetadata(update_only, apply_filter, metadata);
}
status_t MediaPlayer::setVideoSurface(const sp<Surface>& surface)
{
LOGV("setVideoSurface");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return NO_INIT;
if (surface != NULL)
return mPlayer->setVideoSurface(surface->getISurface());
else
return mPlayer->setVideoSurface(NULL);
}
// must call with lock held
status_t MediaPlayer::prepareAsync_l()
{
if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_INITIALIZED | MEDIA_PLAYER_STOPPED) ) ) {
mPlayer->setAudioStreamType(mStreamType);
mCurrentState = MEDIA_PLAYER_PREPARING;
return mPlayer->prepareAsync();
}
LOGE("prepareAsync called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// TODO: In case of error, prepareAsync provides the caller with 2 error codes,
// one defined in the Android framework and one provided by the implementation
// that generated the error. The sync version of prepare returns only 1 error
// code.
status_t MediaPlayer::prepare()
{
LOGV("prepare");
Mutex::Autolock _l(mLock);
mLockThreadId = getThreadId();
if (mPrepareSync) {
mLockThreadId = 0;
return -EALREADY;
}
mPrepareSync = true;
status_t ret = prepareAsync_l();
if (ret != NO_ERROR) {
mLockThreadId = 0;
return ret;
}
if (mPrepareSync) {
mSignal.wait(mLock); // wait for prepare done
mPrepareSync = false;
}
LOGV("prepare complete - status=%d", mPrepareStatus);
mLockThreadId = 0;
return mPrepareStatus;
}
status_t MediaPlayer::prepareAsync()
{
LOGV("prepareAsync");
Mutex::Autolock _l(mLock);
return prepareAsync_l();
}
status_t MediaPlayer::start()
{
LOGV("start");
Mutex::Autolock _l(mLock);
if (mCurrentState & MEDIA_PLAYER_STARTED)
return NO_ERROR;
if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
mPlayer->setLooping(mLoop);
mPlayer->setVolume(mLeftVolume, mRightVolume);
mCurrentState = MEDIA_PLAYER_STARTED;
status_t ret = mPlayer->start();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
} else {
if (mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
LOGV("playback completed immediately following start()");
}
}
return ret;
}
LOGE("start called in state %d", mCurrentState);
return INVALID_OPERATION;
}
status_t MediaPlayer::stop()
{
LOGV("stop");
Mutex::Autolock _l(mLock);
if (mCurrentState & MEDIA_PLAYER_STOPPED) return NO_ERROR;
if ( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED |
MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) ) {
status_t ret = mPlayer->stop();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
} else {
mCurrentState = MEDIA_PLAYER_STOPPED;
}
return ret;
}
LOGE("stop called in state %d", mCurrentState);
return INVALID_OPERATION;
}
status_t MediaPlayer::pause()
{
LOGV("pause");
Mutex::Autolock _l(mLock);
if (mCurrentState & (MEDIA_PLAYER_PAUSED|MEDIA_PLAYER_PLAYBACK_COMPLETE))
return NO_ERROR;
if ((mPlayer != 0) && (mCurrentState & MEDIA_PLAYER_STARTED)) {
status_t ret = mPlayer->pause();
if (ret != NO_ERROR) {
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
} else {
mCurrentState = MEDIA_PLAYER_PAUSED;
}
return ret;
}
LOGE("pause called in state %d", mCurrentState);
return INVALID_OPERATION;
}
bool MediaPlayer::isPlaying()
{
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
bool temp = false;
mPlayer->isPlaying(&temp);
LOGV("isPlaying: %d", temp);
if ((mCurrentState & MEDIA_PLAYER_STARTED) && ! temp) {
LOGE("internal/external state mismatch corrected");
mCurrentState = MEDIA_PLAYER_PAUSED;
}
return temp;
}
LOGV("isPlaying: no active player");
return false;
}
status_t MediaPlayer::getVideoWidth(int *w)
{
LOGV("getVideoWidth");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return INVALID_OPERATION;
*w = mVideoWidth;
return NO_ERROR;
}
status_t MediaPlayer::getVideoHeight(int *h)
{
LOGV("getVideoHeight");
Mutex::Autolock _l(mLock);
if (mPlayer == 0) return INVALID_OPERATION;
*h = mVideoHeight;
return NO_ERROR;
}
status_t MediaPlayer::getCurrentPosition(int *msec)
{
LOGV("getCurrentPosition");
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
if (mCurrentPosition >= 0) {
LOGV("Using cached seek position: %d", mCurrentPosition);
*msec = mCurrentPosition;
return NO_ERROR;
}
return mPlayer->getCurrentPosition(msec);
}
return INVALID_OPERATION;
}
status_t MediaPlayer::getDuration_l(int *msec)
{
LOGV("getDuration");
bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE));
if (mPlayer != 0 && isValidState) {
status_t ret = NO_ERROR;
if (mDuration <= 0)
ret = mPlayer->getDuration(&mDuration);
if (msec)
*msec = mDuration;
return ret;
}
LOGE("Attempt to call getDuration without a valid mediaplayer");
return INVALID_OPERATION;
}
status_t MediaPlayer::getDuration(int *msec)
{
Mutex::Autolock _l(mLock);
return getDuration_l(msec);
}
status_t MediaPlayer::seekTo_l(int msec)
{
LOGV("seekTo %d", msec);
if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) {
if ( msec < 0 ) {
LOGW("Attempt to seek to invalid position: %d", msec);
msec = 0;
} else if ((mDuration > 0) && (msec > mDuration)) {
LOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration);
msec = mDuration;
}
// cache duration
mCurrentPosition = msec;
if (mSeekPosition < 0) {
getDuration_l(NULL);
mSeekPosition = msec;
return mPlayer->seekTo(msec);
}
else {
LOGV("Seek in progress - queue up seekTo[%d]", msec);
return NO_ERROR;
}
}
LOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState);
return INVALID_OPERATION;
}
status_t MediaPlayer::seekTo(int msec)
{
mLockThreadId = getThreadId();
Mutex::Autolock _l(mLock);
status_t result = seekTo_l(msec);
mLockThreadId = 0;
return result;
}
status_t MediaPlayer::reset()
{
LOGV("reset");
Mutex::Autolock _l(mLock);
mLoop = false;
if (mCurrentState == MEDIA_PLAYER_IDLE) return NO_ERROR;
mPrepareSync = false;
if (mPlayer != 0) {
status_t ret = mPlayer->reset();
if (ret != NO_ERROR) {
LOGE("reset() failed with return code (%d)", ret);
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
} else {
mCurrentState = MEDIA_PLAYER_IDLE;
}
return ret;
}
clear_l();
return NO_ERROR;
}
status_t MediaPlayer::setAudioStreamType(int type)
{
LOGV("MediaPlayer::setAudioStreamType");
Mutex::Autolock _l(mLock);
if (mStreamType == type) return NO_ERROR;
if (mCurrentState & ( MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED |
MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE ) ) {
// Can't change the stream type after prepare
LOGE("setAudioStream called in state %d", mCurrentState);
return INVALID_OPERATION;
}
// cache
mStreamType = type;
return OK;
}
status_t MediaPlayer::setLooping(int loop)
{
LOGV("MediaPlayer::setLooping");
Mutex::Autolock _l(mLock);
mLoop = (loop != 0);
if (mPlayer != 0) {
return mPlayer->setLooping(loop);
}
return OK;
}
bool MediaPlayer::isLooping() {
LOGV("isLooping");
Mutex::Autolock _l(mLock);
if (mPlayer != 0) {
return mLoop;
}
LOGV("isLooping: no active player");
return false;
}
status_t MediaPlayer::setVolume(float leftVolume, float rightVolume)
{
LOGV("MediaPlayer::setVolume(%f, %f)", leftVolume, rightVolume);
Mutex::Autolock _l(mLock);
mLeftVolume = leftVolume;
mRightVolume = rightVolume;
if (mPlayer != 0) {
return mPlayer->setVolume(leftVolume, rightVolume);
}
return OK;
}
void MediaPlayer::notify(int msg, int ext1, int ext2)
{
LOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
bool send = true;
bool locked = false;
// TODO: In the future, we might be on the same thread if the app is
// running in the same process as the media server. In that case,
// this will deadlock.
//
// The threadId hack below works around this for the care of prepare
// and seekTo within the same process.
// FIXME: Remember, this is a hack, it's not even a hack that is applied
// consistently for all use-cases, this needs to be revisited.
if (mLockThreadId != getThreadId()) {
mLock.lock();
locked = true;
}
if (mPlayer == 0) {
LOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
if (locked) mLock.unlock(); // release the lock when done.
return;
}
switch (msg) {
case MEDIA_NOP: // interface test message
break;
case MEDIA_PREPARED:
LOGV("prepared");
mCurrentState = MEDIA_PLAYER_PREPARED;
if (mPrepareSync) {
LOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = NO_ERROR;
mSignal.signal();
}
break;
case MEDIA_PLAYBACK_COMPLETE:
LOGV("playback complete");
if (!mLoop) {
mCurrentState = MEDIA_PLAYER_PLAYBACK_COMPLETE;
}
break;
case MEDIA_ERROR:
// Always log errors.
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
LOGE("error (%d, %d)", ext1, ext2);
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
if (mPrepareSync)
{
LOGV("signal application thread");
mPrepareSync = false;
mPrepareStatus = ext1;
mSignal.signal();
send = false;
}
break;
case MEDIA_INFO:
// ext1: Media framework error code.
// ext2: Implementation dependant error code.
LOGW("info/warning (%d, %d)", ext1, ext2);
break;
case MEDIA_SEEK_COMPLETE:
LOGV("Received seek complete");
if (mSeekPosition != mCurrentPosition) {
LOGV("Executing queued seekTo(%d)", mSeekPosition);
mSeekPosition = -1;
seekTo_l(mCurrentPosition);
}
else {
LOGV("All seeks complete - return to regularly scheduled program");
mCurrentPosition = mSeekPosition = -1;
}
break;
case MEDIA_BUFFERING_UPDATE:
LOGV("buffering %d", ext1);
break;
case MEDIA_SET_VIDEO_SIZE:
LOGV("New video size %d x %d", ext1, ext2);
mVideoWidth = ext1;
mVideoHeight = ext2;
break;
default:
LOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
break;
}
sp<MediaPlayerListener> listener = mListener;
if (locked) mLock.unlock();
// this prevents re-entrant calls into client code
if ((listener != 0) && send) {
Mutex::Autolock _l(mNotifyLock);
LOGV("callback application");
listener->notify(msg, ext1, ext2);
LOGV("back from callback");
}
}
/*static*/ sp<IMemory> MediaPlayer::decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
{
LOGV("decode(%s)", url);
sp<IMemory> p;
const sp<IMediaPlayerService>& service = getMediaPlayerService();
if (service != 0) {
p = service->decode(url, pSampleRate, pNumChannels, pFormat);
} else {
LOGE("Unable to locate media service");
}
return p;
}
void MediaPlayer::died()
{
LOGV("died");
notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
}
/*static*/ sp<IMemory> MediaPlayer::decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat)
{
LOGV("decode(%d, %lld, %lld)", fd, offset, length);
sp<IMemory> p;
const sp<IMediaPlayerService>& service = getMediaPlayerService();
if (service != 0) {
p = service->decode(fd, offset, length, pSampleRate, pNumChannels, pFormat);
} else {
LOGE("Unable to locate media service");
}
return p;
}
extern "C" {
#define FLOATING_POINT 1
#include "fftwrap.h"
}
static void *ffttable = NULL;
// peeks at the audio data and fills 'data' with the requested kind
// (currently kind=0 returns mono 16 bit PCM data, and kind=1 returns
// 256 point FFT data). Return value is number of samples returned,
// which may be 0.
/*static*/ int MediaPlayer::snoop(short* data, int len, int kind) {
sp<IMemory> p;
const sp<IMediaPlayerService>& service = getMediaPlayerService();
if (service != 0) {
// Take a peek at the waveform. The returned data consists of 16 bit mono PCM data.
p = service->snoop();
if (p == NULL) {
return 0;
}
if (kind == 0) { // return waveform data
int plen = p->size();
len *= 2; // number of shorts -> number of bytes
short *src = (short*) p->pointer();
if (plen > len) {
plen = len;
}
memcpy(data, src, plen);
return plen / sizeof(short); // return number of samples
} else if (kind == 1) {
// TODO: use a more efficient FFT
// Right now this uses the speex library, which is compiled to do a float FFT
if (!ffttable) ffttable = spx_fft_init(512);
short *usrc = (short*) p->pointer();
float fsrc[512];
for (int i=0;i<512;i++)
fsrc[i] = usrc[i];
float fdst[512];
spx_fft_float(ffttable, fsrc, fdst);
if (len > 512) {
len = 512;
}
len /= 2; // only half the output data is valid
for (int i=0; i < len; i++)
data[i] = fdst[i];
return len;
}
} else {
LOGE("Unable to locate media service");
}
return 0;
}
}; // namespace android