c39d2e3c01
This currently assumes 44k stereo (won't crash on other formats, but won't give the correct results either), and links statically with libspeex to get FFT data, increasing the size of libmedia by about 45kb.
751 lines
21 KiB
C++
751 lines
21 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 <binder/MemoryBase.h>
|
|
|
|
namespace android {
|
|
|
|
// client singleton for binder interface to service
|
|
Mutex MediaPlayer::sServiceLock;
|
|
sp<IMediaPlayerService> MediaPlayer::sMediaPlayerService;
|
|
sp<MediaPlayer::DeathNotifier> MediaPlayer::sDeathNotifier;
|
|
SortedVector< wp<MediaPlayer> > MediaPlayer::sObitRecipients;
|
|
|
|
// establish binder interface to service
|
|
const sp<IMediaPlayerService>& MediaPlayer::getMediaPlayerService()
|
|
{
|
|
Mutex::Autolock _l(sServiceLock);
|
|
if (sMediaPlayerService.get() == 0) {
|
|
sp<IServiceManager> sm = defaultServiceManager();
|
|
sp<IBinder> binder;
|
|
do {
|
|
binder = sm->getService(String16("media.player"));
|
|
if (binder != 0)
|
|
break;
|
|
LOGW("MediaPlayerService not published, waiting...");
|
|
usleep(500000); // 0.5 s
|
|
} while(true);
|
|
if (sDeathNotifier == NULL) {
|
|
sDeathNotifier = new DeathNotifier();
|
|
}
|
|
binder->linkToDeath(sDeathNotifier);
|
|
sMediaPlayerService = interface_cast<IMediaPlayerService>(binder);
|
|
}
|
|
LOGE_IF(sMediaPlayerService==0, "no MediaPlayerService!?");
|
|
return sMediaPlayerService;
|
|
}
|
|
|
|
void MediaPlayer::addObitRecipient(const wp<MediaPlayer>& recipient)
|
|
{
|
|
Mutex::Autolock _l(sServiceLock);
|
|
sObitRecipients.add(recipient);
|
|
}
|
|
|
|
void MediaPlayer::removeObitRecipient(const wp<MediaPlayer>& recipient)
|
|
{
|
|
Mutex::Autolock _l(sServiceLock);
|
|
sObitRecipients.remove(recipient);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void MediaPlayer::onFirstRef()
|
|
{
|
|
addObitRecipient(this);
|
|
}
|
|
|
|
MediaPlayer::~MediaPlayer()
|
|
{
|
|
LOGV("destructor");
|
|
removeObitRecipient(this);
|
|
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 | 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)
|
|
{
|
|
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));
|
|
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);
|
|
if ((mPlayer != NULL) && ( mCurrentState & MEDIA_PLAYER_INITIALIZED ))
|
|
{
|
|
LOGV("invoke %d", request.dataSize());
|
|
return mPlayer->invoke(request, reply);
|
|
}
|
|
LOGE("invoke failed: wrong state %X", mCurrentState);
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
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)
|
|
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");
|
|
}
|
|
}
|
|
|
|
void MediaPlayer::DeathNotifier::binderDied(const wp<IBinder>& who) {
|
|
LOGW("MediaPlayer server died!");
|
|
|
|
// Need to do this with the lock held
|
|
SortedVector< wp<MediaPlayer> > list;
|
|
{
|
|
Mutex::Autolock _l(MediaPlayer::sServiceLock);
|
|
MediaPlayer::sMediaPlayerService.clear();
|
|
list = sObitRecipients;
|
|
}
|
|
|
|
// Notify application when media server dies.
|
|
// Don't hold the static lock during callback in case app
|
|
// makes a call that needs the lock.
|
|
size_t count = list.size();
|
|
for (size_t iter = 0; iter < count; ++iter) {
|
|
sp<MediaPlayer> player = list[iter].promote();
|
|
if ((player != 0) && (player->mPlayer != 0)) {
|
|
player->notify(MEDIA_ERROR, MEDIA_ERROR_SERVER_DIED, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
MediaPlayer::DeathNotifier::~DeathNotifier()
|
|
{
|
|
Mutex::Autolock _l(sServiceLock);
|
|
sObitRecipients.clear();
|
|
if (sMediaPlayerService != 0) {
|
|
sMediaPlayerService->asBinder()->unlinkToDeath(this);
|
|
}
|
|
}
|
|
|
|
/*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 = sMediaPlayerService->decode(url, pSampleRate, pNumChannels, pFormat);
|
|
} else {
|
|
LOGE("Unable to locate media service");
|
|
}
|
|
return p;
|
|
|
|
}
|
|
|
|
/*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 = sMediaPlayerService->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
|