am bbd6cb46: Merge change Ib1b7bce4 into eclair-mr2
Merge commit 'bbd6cb463b405fc90912dee470fe6c7b8c6b1f54' into eclair-mr2-plus-aosp * commit 'bbd6cb463b405fc90912dee470fe6c7b8c6b1f54': Squashed commit of the following:
This commit is contained in:
@@ -19,13 +19,13 @@
|
|||||||
#include <binder/ProcessState.h>
|
#include <binder/ProcessState.h>
|
||||||
#include <media/stagefright/AudioPlayer.h>
|
#include <media/stagefright/AudioPlayer.h>
|
||||||
#include <media/stagefright/CameraSource.h>
|
#include <media/stagefright/CameraSource.h>
|
||||||
|
#include <media/stagefright/FileSource.h>
|
||||||
#include <media/stagefright/MediaBufferGroup.h>
|
#include <media/stagefright/MediaBufferGroup.h>
|
||||||
#include <media/stagefright/MediaDebug.h>
|
#include <media/stagefright/MediaDebug.h>
|
||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
#include <media/stagefright/MetaData.h>
|
#include <media/stagefright/MetaData.h>
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
#include <media/stagefright/MPEG4Writer.h>
|
#include <media/stagefright/MPEG4Writer.h>
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
#include <media/stagefright/OMXClient.h>
|
#include <media/stagefright/OMXClient.h>
|
||||||
#include <media/stagefright/OMXCodec.h>
|
#include <media/stagefright/OMXCodec.h>
|
||||||
#include <media/MediaPlayerInterface.h>
|
#include <media/MediaPlayerInterface.h>
|
||||||
@@ -105,7 +105,7 @@ sp<MediaSource> createSource(const char *filename) {
|
|||||||
sp<MediaSource> source;
|
sp<MediaSource> source;
|
||||||
|
|
||||||
sp<MediaExtractor> extractor =
|
sp<MediaExtractor> extractor =
|
||||||
MediaExtractor::Create(new MmapSource(filename));
|
MediaExtractor::Create(new FileSource(filename));
|
||||||
|
|
||||||
size_t num_tracks = extractor->countTracks();
|
size_t num_tracks = extractor->countTracks();
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,14 @@
|
|||||||
#include <binder/ProcessState.h>
|
#include <binder/ProcessState.h>
|
||||||
#include <media/IMediaPlayerService.h>
|
#include <media/IMediaPlayerService.h>
|
||||||
#include <media/stagefright/CachingDataSource.h>
|
#include <media/stagefright/CachingDataSource.h>
|
||||||
|
#include <media/stagefright/FileSource.h>
|
||||||
#include <media/stagefright/HTTPDataSource.h>
|
#include <media/stagefright/HTTPDataSource.h>
|
||||||
#include <media/stagefright/JPEGSource.h>
|
#include <media/stagefright/JPEGSource.h>
|
||||||
#include <media/stagefright/MediaDebug.h>
|
#include <media/stagefright/MediaDebug.h>
|
||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
#include <media/stagefright/MediaPlayerImpl.h>
|
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
#include <media/stagefright/MediaSource.h>
|
#include <media/stagefright/MediaSource.h>
|
||||||
#include <media/stagefright/MetaData.h>
|
#include <media/stagefright/MetaData.h>
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
#include <media/stagefright/OMXClient.h>
|
#include <media/stagefright/OMXClient.h>
|
||||||
#include <media/stagefright/OMXCodec.h>
|
#include <media/stagefright/OMXCodec.h>
|
||||||
|
|
||||||
@@ -52,9 +51,6 @@ static int64_t getNowUs() {
|
|||||||
static void playSource(OMXClient *client, const sp<MediaSource> &source) {
|
static void playSource(OMXClient *client, const sp<MediaSource> &source) {
|
||||||
sp<MetaData> meta = source->getFormat();
|
sp<MetaData> meta = source->getFormat();
|
||||||
|
|
||||||
int64_t durationUs;
|
|
||||||
CHECK(meta->findInt64(kKeyDuration, &durationUs));
|
|
||||||
|
|
||||||
const char *mime;
|
const char *mime;
|
||||||
CHECK(meta->findCString(kKeyMIMEType, &mime));
|
CHECK(meta->findCString(kKeyMIMEType, &mime));
|
||||||
|
|
||||||
@@ -74,6 +70,9 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) {
|
|||||||
rawSource->start();
|
rawSource->start();
|
||||||
|
|
||||||
if (gReproduceBug >= 3 && gReproduceBug <= 5) {
|
if (gReproduceBug >= 3 && gReproduceBug <= 5) {
|
||||||
|
int64_t durationUs;
|
||||||
|
CHECK(meta->findInt64(kKeyDuration, &durationUs));
|
||||||
|
|
||||||
status_t err;
|
status_t err;
|
||||||
MediaBuffer *buffer;
|
MediaBuffer *buffer;
|
||||||
MediaSource::ReadOptions options;
|
MediaSource::ReadOptions options;
|
||||||
@@ -368,7 +367,7 @@ int main(int argc, char **argv) {
|
|||||||
dataSource = new HTTPDataSource(filename);
|
dataSource = new HTTPDataSource(filename);
|
||||||
dataSource = new CachingDataSource(dataSource, 64 * 1024, 10);
|
dataSource = new CachingDataSource(dataSource, 64 * 1024, 10);
|
||||||
} else {
|
} else {
|
||||||
dataSource = new MmapSource(filename);
|
dataSource = new FileSource(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isJPEG = false;
|
bool isJPEG = false;
|
||||||
|
|||||||
@@ -133,9 +133,9 @@ public:
|
|||||||
return INVALID_OPERATION;
|
return INVALID_OPERATION;
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
|
virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
|
||||||
|
|
||||||
|
protected:
|
||||||
void* mCookie;
|
void* mCookie;
|
||||||
notify_callback_f mNotify;
|
notify_callback_f mNotify;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,12 +30,20 @@ class AudioTrack;
|
|||||||
|
|
||||||
class AudioPlayer : public TimeSource {
|
class AudioPlayer : public TimeSource {
|
||||||
public:
|
public:
|
||||||
|
enum {
|
||||||
|
REACHED_EOS,
|
||||||
|
SEEK_COMPLETE
|
||||||
|
};
|
||||||
|
|
||||||
AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
|
AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
|
||||||
virtual ~AudioPlayer();
|
virtual ~AudioPlayer();
|
||||||
|
|
||||||
// Caller retains ownership of "source".
|
// Caller retains ownership of "source".
|
||||||
void setSource(const sp<MediaSource> &source);
|
void setSource(const sp<MediaSource> &source);
|
||||||
|
|
||||||
|
void setListenerCallback(
|
||||||
|
void (*notify)(void *cookie, int what), void *cookie);
|
||||||
|
|
||||||
// Return time in us.
|
// Return time in us.
|
||||||
virtual int64_t getRealTimeUs();
|
virtual int64_t getRealTimeUs();
|
||||||
|
|
||||||
@@ -76,6 +84,9 @@ private:
|
|||||||
|
|
||||||
bool mStarted;
|
bool mStarted;
|
||||||
|
|
||||||
|
void (*mListenerCallback)(void *cookie, int what);
|
||||||
|
void *mListenerCookie;
|
||||||
|
|
||||||
sp<MediaPlayerBase::AudioSink> mAudioSink;
|
sp<MediaPlayerBase::AudioSink> mAudioSink;
|
||||||
|
|
||||||
static void AudioCallback(int event, void *user, void *info);
|
static void AudioCallback(int event, void *user, void *info);
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MEDIA_PLAYER_IMPL_H_
|
|
||||||
|
|
||||||
#define MEDIA_PLAYER_IMPL_H_
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <media/MediaPlayerInterface.h>
|
|
||||||
#include <media/stagefright/OMXClient.h>
|
|
||||||
#include <utils/RefBase.h>
|
|
||||||
#include <utils/threads.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
class AudioPlayer;
|
|
||||||
class IOMXRenderer;
|
|
||||||
class ISurface;
|
|
||||||
class MediaExtractor;
|
|
||||||
class MediaBuffer;
|
|
||||||
class MediaSource;
|
|
||||||
class MemoryHeapPmem;
|
|
||||||
class MetaData;
|
|
||||||
class Surface;
|
|
||||||
class TimeSource;
|
|
||||||
|
|
||||||
class MediaPlayerImpl {
|
|
||||||
public:
|
|
||||||
MediaPlayerImpl(const char *uri);
|
|
||||||
|
|
||||||
status_t initCheck() const;
|
|
||||||
|
|
||||||
// Assumes ownership of "fd".
|
|
||||||
MediaPlayerImpl(int fd, int64_t offset, int64_t length);
|
|
||||||
|
|
||||||
~MediaPlayerImpl();
|
|
||||||
|
|
||||||
void play();
|
|
||||||
void pause();
|
|
||||||
bool isPlaying() const;
|
|
||||||
|
|
||||||
void setSurface(const sp<Surface> &surface);
|
|
||||||
void setISurface(const sp<ISurface> &isurface);
|
|
||||||
|
|
||||||
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
|
|
||||||
|
|
||||||
int32_t getWidth() const { return mVideoWidth; }
|
|
||||||
int32_t getHeight() const { return mVideoHeight; }
|
|
||||||
|
|
||||||
int64_t getDuration();
|
|
||||||
int64_t getPosition();
|
|
||||||
status_t seekTo(int64_t time);
|
|
||||||
|
|
||||||
private:
|
|
||||||
status_t mInitCheck;
|
|
||||||
|
|
||||||
OMXClient mClient;
|
|
||||||
|
|
||||||
sp<MediaExtractor> mExtractor;
|
|
||||||
|
|
||||||
TimeSource *mTimeSource;
|
|
||||||
|
|
||||||
sp<MediaSource> mAudioSource;
|
|
||||||
sp<MediaSource> mAudioDecoder;
|
|
||||||
AudioPlayer *mAudioPlayer;
|
|
||||||
|
|
||||||
sp<MediaSource> mVideoSource;
|
|
||||||
sp<MediaSource> mVideoDecoder;
|
|
||||||
int32_t mVideoWidth, mVideoHeight;
|
|
||||||
int64_t mVideoPosition;
|
|
||||||
|
|
||||||
int64_t mDuration;
|
|
||||||
|
|
||||||
bool mPlaying;
|
|
||||||
bool mPaused;
|
|
||||||
|
|
||||||
int64_t mTimeSourceDeltaUs;
|
|
||||||
|
|
||||||
sp<Surface> mSurface;
|
|
||||||
sp<ISurface> mISurface;
|
|
||||||
sp<IOMXRenderer> mVideoRenderer;
|
|
||||||
|
|
||||||
sp<MediaPlayerBase::AudioSink> mAudioSink;
|
|
||||||
|
|
||||||
Mutex mLock;
|
|
||||||
pthread_t mVideoThread;
|
|
||||||
|
|
||||||
bool mSeeking;
|
|
||||||
int64_t mSeekTimeUs;
|
|
||||||
|
|
||||||
void init();
|
|
||||||
|
|
||||||
static void *VideoWrapper(void *me);
|
|
||||||
void videoEntry();
|
|
||||||
|
|
||||||
void setAudioSource(const sp<MediaSource> &source);
|
|
||||||
void setVideoSource(const sp<MediaSource> &source);
|
|
||||||
|
|
||||||
MediaSource *makeShoutcastSource(const char *path);
|
|
||||||
|
|
||||||
void displayOrDiscardFrame(MediaBuffer *buffer, int64_t pts_us);
|
|
||||||
void populateISurface();
|
|
||||||
void depopulateISurface();
|
|
||||||
void sendFrameToISurface(MediaBuffer *buffer);
|
|
||||||
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
MediaPlayerImpl(const MediaPlayerImpl &);
|
|
||||||
MediaPlayerImpl &operator=(const MediaPlayerImpl &);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
#endif // MEDIA_PLAYER_IMPL_H_
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MMAP_SOURCE_H_
|
|
||||||
|
|
||||||
#define MMAP_SOURCE_H_
|
|
||||||
|
|
||||||
#include <media/stagefright/DataSource.h>
|
|
||||||
#include <media/stagefright/MediaErrors.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
class MmapSource : public DataSource {
|
|
||||||
public:
|
|
||||||
MmapSource(const char *filename);
|
|
||||||
|
|
||||||
// Assumes ownership of "fd".
|
|
||||||
MmapSource(int fd, int64_t offset, int64_t length);
|
|
||||||
|
|
||||||
virtual status_t initCheck() const;
|
|
||||||
|
|
||||||
virtual ssize_t readAt(off_t offset, void *data, size_t size);
|
|
||||||
virtual status_t getSize(off_t *size);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual ~MmapSource();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int mFd;
|
|
||||||
void *mBase;
|
|
||||||
size_t mSize;
|
|
||||||
|
|
||||||
MmapSource(const MmapSource &);
|
|
||||||
MmapSource &operator=(const MmapSource &);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
#endif // MMAP_SOURCE_H_
|
|
||||||
|
|
||||||
@@ -24,11 +24,11 @@
|
|||||||
#include <media/stagefright/CachingDataSource.h>
|
#include <media/stagefright/CachingDataSource.h>
|
||||||
#include <media/stagefright/ColorConverter.h>
|
#include <media/stagefright/ColorConverter.h>
|
||||||
#include <media/stagefright/DataSource.h>
|
#include <media/stagefright/DataSource.h>
|
||||||
|
#include <media/stagefright/FileSource.h>
|
||||||
#include <media/stagefright/HTTPDataSource.h>
|
#include <media/stagefright/HTTPDataSource.h>
|
||||||
#include <media/stagefright/MediaDebug.h>
|
#include <media/stagefright/MediaDebug.h>
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
#include <media/stagefright/MetaData.h>
|
#include <media/stagefright/MetaData.h>
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
#include <media/stagefright/OMXCodec.h>
|
#include <media/stagefright/OMXCodec.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@@ -58,7 +58,7 @@ status_t StagefrightMetadataRetriever::setDataSource(
|
|||||||
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
|
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
|
||||||
|
|
||||||
mExtractor = MediaExtractor::Create(
|
mExtractor = MediaExtractor::Create(
|
||||||
new MmapSource(fd, offset, length));
|
new FileSource(fd, offset, length));
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,24 @@
|
|||||||
#include <utils/Log.h>
|
#include <utils/Log.h>
|
||||||
|
|
||||||
#include "StagefrightPlayer.h"
|
#include "StagefrightPlayer.h"
|
||||||
#include <media/stagefright/MediaPlayerImpl.h>
|
|
||||||
|
#include "AwesomePlayer.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
StagefrightPlayer::StagefrightPlayer()
|
StagefrightPlayer::StagefrightPlayer()
|
||||||
: mPlayer(NULL) {
|
: mPlayer(new AwesomePlayer) {
|
||||||
LOGV("StagefrightPlayer");
|
LOGV("StagefrightPlayer");
|
||||||
|
|
||||||
|
mPlayer->setListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
StagefrightPlayer::~StagefrightPlayer() {
|
StagefrightPlayer::~StagefrightPlayer() {
|
||||||
LOGV("~StagefrightPlayer");
|
LOGV("~StagefrightPlayer");
|
||||||
reset();
|
reset();
|
||||||
LOGV("~StagefrightPlayer done.");
|
|
||||||
|
delete mPlayer;
|
||||||
|
mPlayer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::initCheck() {
|
status_t StagefrightPlayer::initCheck() {
|
||||||
@@ -25,62 +30,32 @@ status_t StagefrightPlayer::initCheck() {
|
|||||||
|
|
||||||
status_t StagefrightPlayer::setDataSource(const char *url) {
|
status_t StagefrightPlayer::setDataSource(const char *url) {
|
||||||
LOGV("setDataSource('%s')", url);
|
LOGV("setDataSource('%s')", url);
|
||||||
|
return mPlayer->setDataSource(url);
|
||||||
reset();
|
|
||||||
mPlayer = new MediaPlayerImpl(url);
|
|
||||||
|
|
||||||
status_t err = mPlayer->initCheck();
|
|
||||||
if (err != OK) {
|
|
||||||
delete mPlayer;
|
|
||||||
mPlayer = NULL;
|
|
||||||
} else {
|
|
||||||
mPlayer->setAudioSink(mAudioSink);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning: The filedescriptor passed into this method will only be valid until
|
// Warning: The filedescriptor passed into this method will only be valid until
|
||||||
// the method returns, if you want to keep it, dup it!
|
// the method returns, if you want to keep it, dup it!
|
||||||
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
|
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
|
||||||
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
|
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
|
||||||
|
return mPlayer->setDataSource(dup(fd), offset, length);
|
||||||
reset();
|
|
||||||
mPlayer = new MediaPlayerImpl(dup(fd), offset, length);
|
|
||||||
|
|
||||||
status_t err = mPlayer->initCheck();
|
|
||||||
if (err != OK) {
|
|
||||||
delete mPlayer;
|
|
||||||
mPlayer = NULL;
|
|
||||||
} else {
|
|
||||||
mPlayer->setAudioSink(mAudioSink);
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
|
status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
|
||||||
LOGV("setVideoSurface");
|
LOGV("setVideoSurface");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
|
||||||
return NO_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlayer->setISurface(surface);
|
mPlayer->setISurface(surface);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::prepare() {
|
status_t StagefrightPlayer::prepare() {
|
||||||
LOGV("prepare");
|
LOGV("prepare");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
int32_t width, height;
|
||||||
return NO_INIT;
|
if (mPlayer->getVideoDimensions(&width, &height) != OK) {
|
||||||
|
width = height = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendEvent(
|
sendEvent(MEDIA_SET_VIDEO_SIZE, width, height);
|
||||||
MEDIA_SET_VIDEO_SIZE,
|
|
||||||
mPlayer->getWidth(), mPlayer->getHeight());
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -102,92 +77,76 @@ status_t StagefrightPlayer::prepareAsync() {
|
|||||||
status_t StagefrightPlayer::start() {
|
status_t StagefrightPlayer::start() {
|
||||||
LOGV("start");
|
LOGV("start");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
return mPlayer->play();
|
||||||
return NO_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlayer->play();
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::stop() {
|
status_t StagefrightPlayer::stop() {
|
||||||
LOGV("stop");
|
LOGV("stop");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
return pause(); // what's the difference?
|
||||||
return NO_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset();
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::pause() {
|
status_t StagefrightPlayer::pause() {
|
||||||
LOGV("pause");
|
LOGV("pause");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
return mPlayer->pause();
|
||||||
return NO_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlayer->pause();
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StagefrightPlayer::isPlaying() {
|
bool StagefrightPlayer::isPlaying() {
|
||||||
LOGV("isPlaying");
|
LOGV("isPlaying");
|
||||||
return mPlayer != NULL && mPlayer->isPlaying();
|
return mPlayer->isPlaying();
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::seekTo(int msec) {
|
status_t StagefrightPlayer::seekTo(int msec) {
|
||||||
LOGV("seekTo");
|
LOGV("seekTo");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
|
||||||
return NO_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t err = mPlayer->seekTo((int64_t)msec * 1000);
|
status_t err = mPlayer->seekTo((int64_t)msec * 1000);
|
||||||
|
|
||||||
sendEvent(MEDIA_SEEK_COMPLETE);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::getCurrentPosition(int *msec) {
|
status_t StagefrightPlayer::getCurrentPosition(int *msec) {
|
||||||
LOGV("getCurrentPosition");
|
LOGV("getCurrentPosition");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
int64_t positionUs;
|
||||||
return NO_INIT;
|
status_t err = mPlayer->getPosition(&positionUs);
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
*msec = mPlayer->getPosition() / 1000;
|
*msec = (positionUs + 500) / 1000;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::getDuration(int *msec) {
|
status_t StagefrightPlayer::getDuration(int *msec) {
|
||||||
LOGV("getDuration");
|
LOGV("getDuration");
|
||||||
|
|
||||||
if (mPlayer == NULL) {
|
int64_t durationUs;
|
||||||
return NO_INIT;
|
status_t err = mPlayer->getDuration(&durationUs);
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
*msec = mPlayer->getDuration() / 1000;
|
*msec = (durationUs + 500) / 1000;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::reset() {
|
status_t StagefrightPlayer::reset() {
|
||||||
LOGV("reset");
|
LOGV("reset");
|
||||||
|
|
||||||
delete mPlayer;
|
mPlayer->reset();
|
||||||
mPlayer = NULL;
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t StagefrightPlayer::setLooping(int loop) {
|
status_t StagefrightPlayer::setLooping(int loop) {
|
||||||
LOGV("setLooping");
|
LOGV("setLooping");
|
||||||
return UNKNOWN_ERROR;
|
|
||||||
|
return mPlayer->setLooping(loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
player_type StagefrightPlayer::playerType() {
|
player_type StagefrightPlayer::playerType() {
|
||||||
@@ -202,9 +161,7 @@ status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
|
|||||||
void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
|
void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
|
||||||
MediaPlayerInterface::setAudioSink(audioSink);
|
MediaPlayerInterface::setAudioSink(audioSink);
|
||||||
|
|
||||||
if (mPlayer != NULL) {
|
mPlayer->setAudioSink(audioSink);
|
||||||
mPlayer->setAudioSink(audioSink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace android
|
} // namespace android
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
class MediaPlayerImpl;
|
struct AwesomePlayer;
|
||||||
|
|
||||||
class StagefrightPlayer : public MediaPlayerInterface {
|
class StagefrightPlayer : public MediaPlayerInterface {
|
||||||
public:
|
public:
|
||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
virtual void setAudioSink(const sp<AudioSink> &audioSink);
|
virtual void setAudioSink(const sp<AudioSink> &audioSink);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MediaPlayerImpl *mPlayer;
|
AwesomePlayer *mPlayer;
|
||||||
|
|
||||||
StagefrightPlayer(const StagefrightPlayer &);
|
StagefrightPlayer(const StagefrightPlayer &);
|
||||||
StagefrightPlayer &operator=(const StagefrightPlayer &);
|
StagefrightPlayer &operator=(const StagefrightPlayer &);
|
||||||
|
|||||||
@@ -33,7 +33,10 @@ namespace android {
|
|||||||
|
|
||||||
class AMRSource : public MediaSource {
|
class AMRSource : public MediaSource {
|
||||||
public:
|
public:
|
||||||
AMRSource(const sp<DataSource> &source, bool isWide);
|
AMRSource(const sp<DataSource> &source,
|
||||||
|
const sp<MetaData> &meta,
|
||||||
|
size_t frameSize,
|
||||||
|
bool isWide);
|
||||||
|
|
||||||
virtual status_t start(MetaData *params = NULL);
|
virtual status_t start(MetaData *params = NULL);
|
||||||
virtual status_t stop();
|
virtual status_t stop();
|
||||||
@@ -48,6 +51,8 @@ protected:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
sp<DataSource> mDataSource;
|
sp<DataSource> mDataSource;
|
||||||
|
sp<MetaData> mMeta;
|
||||||
|
size_t mFrameSize;
|
||||||
bool mIsWide;
|
bool mIsWide;
|
||||||
|
|
||||||
off_t mOffset;
|
off_t mOffset;
|
||||||
@@ -61,15 +66,63 @@ private:
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static size_t getFrameSize(bool isWide, unsigned FT) {
|
||||||
|
static const size_t kFrameSizeNB[8] = {
|
||||||
|
95, 103, 118, 134, 148, 159, 204, 244
|
||||||
|
};
|
||||||
|
static const size_t kFrameSizeWB[9] = {
|
||||||
|
132, 177, 253, 285, 317, 365, 397, 461, 477
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
|
||||||
|
|
||||||
|
// Round up bits to bytes and add 1 for the header byte.
|
||||||
|
frameSize = (frameSize + 7) / 8 + 1;
|
||||||
|
|
||||||
|
return frameSize;
|
||||||
|
}
|
||||||
|
|
||||||
AMRExtractor::AMRExtractor(const sp<DataSource> &source)
|
AMRExtractor::AMRExtractor(const sp<DataSource> &source)
|
||||||
: mDataSource(source),
|
: mDataSource(source),
|
||||||
mInitCheck(NO_INIT) {
|
mInitCheck(NO_INIT) {
|
||||||
String8 mimeType;
|
String8 mimeType;
|
||||||
float confidence;
|
float confidence;
|
||||||
if (SniffAMR(mDataSource, &mimeType, &confidence)) {
|
if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
|
||||||
mInitCheck = OK;
|
return;
|
||||||
mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
|
||||||
|
|
||||||
|
mMeta = new MetaData;
|
||||||
|
mMeta->setCString(
|
||||||
|
kKeyMIMEType, mIsWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
|
||||||
|
: MEDIA_MIMETYPE_AUDIO_AMR_NB);
|
||||||
|
|
||||||
|
mMeta->setInt32(kKeyChannelCount, 1);
|
||||||
|
mMeta->setInt32(kKeySampleRate, mIsWide ? 16000 : 8000);
|
||||||
|
|
||||||
|
size_t offset = mIsWide ? 9 : 6;
|
||||||
|
uint8_t header;
|
||||||
|
if (mDataSource->readAt(offset, &header, 1) != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned FT = (header >> 3) & 0x0f;
|
||||||
|
|
||||||
|
if (FT > 8 || (!mIsWide && FT > 7)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFrameSize = getFrameSize(mIsWide, FT);
|
||||||
|
|
||||||
|
off_t streamSize;
|
||||||
|
if (mDataSource->getSize(&streamSize) == OK) {
|
||||||
|
off_t numFrames = streamSize / mFrameSize;
|
||||||
|
|
||||||
|
mMeta->setInt64(kKeyDuration, 20000ll * numFrames);
|
||||||
|
}
|
||||||
|
|
||||||
|
mInitCheck = OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMRExtractor::~AMRExtractor() {
|
AMRExtractor::~AMRExtractor() {
|
||||||
@@ -84,7 +137,7 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new AMRSource(mDataSource, mIsWide);
|
return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide);
|
||||||
}
|
}
|
||||||
|
|
||||||
sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
|
sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
|
||||||
@@ -92,26 +145,17 @@ sp<MetaData> AMRExtractor::getTrackMetaData(size_t index, uint32_t flags) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeAMRFormat(mIsWide);
|
return mMeta;
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
sp<MetaData> AMRExtractor::makeAMRFormat(bool isWide) {
|
|
||||||
sp<MetaData> meta = new MetaData;
|
|
||||||
meta->setCString(
|
|
||||||
kKeyMIMEType, isWide ? MEDIA_MIMETYPE_AUDIO_AMR_WB
|
|
||||||
: MEDIA_MIMETYPE_AUDIO_AMR_NB);
|
|
||||||
|
|
||||||
meta->setInt32(kKeyChannelCount, 1);
|
|
||||||
meta->setInt32(kKeySampleRate, isWide ? 16000 : 8000);
|
|
||||||
|
|
||||||
return meta;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
AMRSource::AMRSource(const sp<DataSource> &source, bool isWide)
|
AMRSource::AMRSource(
|
||||||
|
const sp<DataSource> &source, const sp<MetaData> &meta,
|
||||||
|
size_t frameSize, bool isWide)
|
||||||
: mDataSource(source),
|
: mDataSource(source),
|
||||||
|
mMeta(meta),
|
||||||
|
mFrameSize(frameSize),
|
||||||
mIsWide(isWide),
|
mIsWide(isWide),
|
||||||
mOffset(mIsWide ? 9 : 6),
|
mOffset(mIsWide ? 9 : 6),
|
||||||
mCurrentTimeUs(0),
|
mCurrentTimeUs(0),
|
||||||
@@ -148,13 +192,20 @@ status_t AMRSource::stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sp<MetaData> AMRSource::getFormat() {
|
sp<MetaData> AMRSource::getFormat() {
|
||||||
return AMRExtractor::makeAMRFormat(mIsWide);
|
return mMeta;
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AMRSource::read(
|
status_t AMRSource::read(
|
||||||
MediaBuffer **out, const ReadOptions *options) {
|
MediaBuffer **out, const ReadOptions *options) {
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
|
|
||||||
|
int64_t seekTimeUs;
|
||||||
|
if (options && options->getSeekTo(&seekTimeUs)) {
|
||||||
|
int64_t seekFrame = seekTimeUs / 20000ll; // 20ms per frame.
|
||||||
|
mCurrentTimeUs = seekFrame * 20000ll;
|
||||||
|
mOffset = seekFrame * mFrameSize + (mIsWide ? 9 : 6);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t header;
|
uint8_t header;
|
||||||
ssize_t n = mDataSource->readAt(mOffset, &header, 1);
|
ssize_t n = mDataSource->readAt(mOffset, &header, 1);
|
||||||
|
|
||||||
@@ -180,17 +231,8 @@ status_t AMRSource::read(
|
|||||||
return ERROR_MALFORMED;
|
return ERROR_MALFORMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const size_t kFrameSizeNB[8] = {
|
size_t frameSize = getFrameSize(mIsWide, FT);
|
||||||
95, 103, 118, 134, 148, 159, 204, 244
|
CHECK_EQ(frameSize, mFrameSize);
|
||||||
};
|
|
||||||
static const size_t kFrameSizeWB[9] = {
|
|
||||||
132, 177, 253, 285, 317, 365, 397, 461, 477
|
|
||||||
};
|
|
||||||
|
|
||||||
size_t frameSize = mIsWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
|
|
||||||
|
|
||||||
// Round up bits to bytes and add 1 for the header byte.
|
|
||||||
frameSize = (frameSize + 7) / 8 + 1;
|
|
||||||
|
|
||||||
n = mDataSource->readAt(mOffset, buffer->data(), frameSize);
|
n = mDataSource->readAt(mOffset, buffer->data(), frameSize);
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
|
|||||||
LOCAL_SRC_FILES += \
|
LOCAL_SRC_FILES += \
|
||||||
AMRExtractor.cpp \
|
AMRExtractor.cpp \
|
||||||
AudioPlayer.cpp \
|
AudioPlayer.cpp \
|
||||||
|
AwesomePlayer.cpp \
|
||||||
CachingDataSource.cpp \
|
CachingDataSource.cpp \
|
||||||
CameraSource.cpp \
|
CameraSource.cpp \
|
||||||
DataSource.cpp \
|
DataSource.cpp \
|
||||||
@@ -28,8 +29,6 @@ LOCAL_SRC_FILES += \
|
|||||||
MPEG4Extractor.cpp \
|
MPEG4Extractor.cpp \
|
||||||
MPEG4Writer.cpp \
|
MPEG4Writer.cpp \
|
||||||
MediaExtractor.cpp \
|
MediaExtractor.cpp \
|
||||||
MediaPlayerImpl.cpp \
|
|
||||||
MmapSource.cpp \
|
|
||||||
SampleTable.cpp \
|
SampleTable.cpp \
|
||||||
ShoutcastSource.cpp \
|
ShoutcastSource.cpp \
|
||||||
TimeSource.cpp \
|
TimeSource.cpp \
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ AudioPlayer::~AudioPlayer() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AudioPlayer::setListenerCallback(
|
||||||
|
void (*notify)(void *cookie, int what), void *cookie) {
|
||||||
|
mListenerCallback = notify;
|
||||||
|
mListenerCookie = cookie;
|
||||||
|
}
|
||||||
|
|
||||||
void AudioPlayer::setSource(const sp<MediaSource> &source) {
|
void AudioPlayer::setSource(const sp<MediaSource> &source) {
|
||||||
CHECK_EQ(mSource, NULL);
|
CHECK_EQ(mSource, NULL);
|
||||||
mSource = source;
|
mSource = source;
|
||||||
@@ -195,7 +201,6 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
|
|||||||
mInputBuffer->release();
|
mInputBuffer->release();
|
||||||
mInputBuffer = NULL;
|
mInputBuffer = NULL;
|
||||||
}
|
}
|
||||||
mSeeking = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +210,19 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
|
|||||||
CHECK((err == OK && mInputBuffer != NULL)
|
CHECK((err == OK && mInputBuffer != NULL)
|
||||||
|| (err != OK && mInputBuffer == NULL));
|
|| (err != OK && mInputBuffer == NULL));
|
||||||
|
|
||||||
|
if (mSeeking) {
|
||||||
|
mSeeking = false;
|
||||||
|
|
||||||
|
if (mListenerCallback) {
|
||||||
|
(*mListenerCallback)(mListenerCookie, SEEK_COMPLETE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (err != OK) {
|
if (err != OK) {
|
||||||
|
if (mListenerCallback) {
|
||||||
|
(*mListenerCallback)(mListenerCookie, REACHED_EOS);
|
||||||
|
}
|
||||||
|
|
||||||
memset((char *)data + size_done, 0, size_remaining);
|
memset((char *)data + size_done, 0, size_remaining);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
626
media/libstagefright/AwesomePlayer.cpp
Normal file
626
media/libstagefright/AwesomePlayer.cpp
Normal file
@@ -0,0 +1,626 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 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 "AwesomePlayer"
|
||||||
|
#include <utils/Log.h>
|
||||||
|
|
||||||
|
#include "include/AwesomePlayer.h"
|
||||||
|
|
||||||
|
#include <media/stagefright/AudioPlayer.h>
|
||||||
|
#include <media/stagefright/DataSource.h>
|
||||||
|
#include <media/stagefright/FileSource.h>
|
||||||
|
#include <media/stagefright/MediaBuffer.h>
|
||||||
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
|
#include <media/stagefright/MediaDebug.h>
|
||||||
|
#include <media/stagefright/MediaSource.h>
|
||||||
|
#include <media/stagefright/MetaData.h>
|
||||||
|
#include <media/stagefright/OMXCodec.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
struct AwesomeEvent : public TimedEventQueue::Event {
|
||||||
|
AwesomeEvent(AwesomePlayer *player, int32_t code)
|
||||||
|
: mPlayer(player),
|
||||||
|
mCode(code) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~AwesomeEvent() {}
|
||||||
|
|
||||||
|
virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
|
||||||
|
mPlayer->onEvent(mCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AwesomePlayer *mPlayer;
|
||||||
|
int32_t mCode;
|
||||||
|
|
||||||
|
AwesomeEvent(const AwesomeEvent &);
|
||||||
|
AwesomeEvent &operator=(const AwesomeEvent &);
|
||||||
|
};
|
||||||
|
|
||||||
|
AwesomePlayer::AwesomePlayer()
|
||||||
|
: mTimeSource(NULL),
|
||||||
|
mAudioPlayer(NULL),
|
||||||
|
mLastVideoBuffer(NULL),
|
||||||
|
mVideoBuffer(NULL) {
|
||||||
|
CHECK_EQ(mClient.connect(), OK);
|
||||||
|
|
||||||
|
DataSource::RegisterDefaultSniffers();
|
||||||
|
|
||||||
|
mVideoEvent = new AwesomeEvent(this, 0);
|
||||||
|
mVideoEventPending = false;
|
||||||
|
mStreamDoneEvent = new AwesomeEvent(this, 1);
|
||||||
|
mStreamDoneEventPending = false;
|
||||||
|
|
||||||
|
mQueue.start();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
AwesomePlayer::~AwesomePlayer() {
|
||||||
|
mQueue.stop();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
|
||||||
|
mClient.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::cancelPlayerEvents() {
|
||||||
|
mQueue.cancelEvent(mVideoEvent->eventID());
|
||||||
|
mVideoEventPending = false;
|
||||||
|
mQueue.cancelEvent(mStreamDoneEvent->eventID());
|
||||||
|
mStreamDoneEventPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::setListener(const sp<MediaPlayerBase> &listener) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::setDataSource(const char *uri) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
reset_l();
|
||||||
|
|
||||||
|
sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
|
||||||
|
|
||||||
|
if (extractor == NULL) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setDataSource_l(extractor);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::setDataSource(
|
||||||
|
int fd, int64_t offset, int64_t length) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
reset_l();
|
||||||
|
|
||||||
|
sp<DataSource> source = new FileSource(fd, offset, length);
|
||||||
|
|
||||||
|
status_t err = source->initCheck();
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<MediaExtractor> extractor = MediaExtractor::Create(source);
|
||||||
|
|
||||||
|
if (extractor == NULL) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return setDataSource_l(extractor);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
|
||||||
|
reset_l();
|
||||||
|
|
||||||
|
bool haveAudio = false;
|
||||||
|
bool haveVideo = false;
|
||||||
|
for (size_t i = 0; i < extractor->countTracks(); ++i) {
|
||||||
|
sp<MetaData> meta = extractor->getTrackMetaData(i);
|
||||||
|
|
||||||
|
const char *mime;
|
||||||
|
CHECK(meta->findCString(kKeyMIMEType, &mime));
|
||||||
|
|
||||||
|
if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
|
||||||
|
if (setVideoSource(extractor->getTrack(i)) == OK) {
|
||||||
|
haveVideo = true;
|
||||||
|
}
|
||||||
|
} else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
|
||||||
|
if (setAudioSource(extractor->getTrack(i)) == OK) {
|
||||||
|
haveAudio = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveAudio && haveVideo) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !haveAudio && !haveVideo ? UNKNOWN_ERROR : OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::reset() {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
reset_l();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::reset_l() {
|
||||||
|
cancelPlayerEvents();
|
||||||
|
|
||||||
|
if (mLastVideoBuffer) {
|
||||||
|
mLastVideoBuffer->release();
|
||||||
|
mLastVideoBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVideoBuffer) {
|
||||||
|
mVideoBuffer->release();
|
||||||
|
mVideoBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVideoSource != NULL) {
|
||||||
|
mVideoSource->stop();
|
||||||
|
mVideoSource.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioSource.clear();
|
||||||
|
|
||||||
|
if (mTimeSource != mAudioPlayer) {
|
||||||
|
delete mTimeSource;
|
||||||
|
}
|
||||||
|
mTimeSource = NULL;
|
||||||
|
|
||||||
|
delete mAudioPlayer;
|
||||||
|
mAudioPlayer = NULL;
|
||||||
|
|
||||||
|
mVideoRenderer.clear();
|
||||||
|
|
||||||
|
mDurationUs = -1;
|
||||||
|
mFlags = 0;
|
||||||
|
mVideoWidth = mVideoHeight = -1;
|
||||||
|
mTimeSourceDeltaUs = 0;
|
||||||
|
mVideoTimeUs = 0;
|
||||||
|
|
||||||
|
mSeeking = false;
|
||||||
|
mSeekTimeUs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void AwesomePlayer::AudioNotify(void *_me, int what) {
|
||||||
|
AwesomePlayer *me = (AwesomePlayer *)_me;
|
||||||
|
|
||||||
|
Mutex::Autolock autoLock(me->mLock);
|
||||||
|
|
||||||
|
switch (what) {
|
||||||
|
case AudioPlayer::REACHED_EOS:
|
||||||
|
me->postStreamDoneEvent_l();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case AudioPlayer::SEEK_COMPLETE:
|
||||||
|
{
|
||||||
|
if (me->mListener != NULL) {
|
||||||
|
me->mListener->sendEvent(MEDIA_SEEK_COMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
CHECK(!"should not be here.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::onStreamDone() {
|
||||||
|
// Posted whenever any stream finishes playing.
|
||||||
|
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
mStreamDoneEventPending = false;
|
||||||
|
|
||||||
|
if (mFlags & LOOPING) {
|
||||||
|
seekTo_l(0);
|
||||||
|
|
||||||
|
if (mVideoRenderer != NULL) {
|
||||||
|
postVideoEvent_l();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mListener != NULL) {
|
||||||
|
mListener->sendEvent(MEDIA_PLAYBACK_COMPLETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pause_l();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::play() {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
if (mFlags & PLAYING) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
mFlags |= PLAYING;
|
||||||
|
mFlags |= FIRST_FRAME;
|
||||||
|
|
||||||
|
if (mAudioSource != NULL) {
|
||||||
|
if (mAudioPlayer == NULL) {
|
||||||
|
if (mAudioSink != NULL) {
|
||||||
|
mAudioPlayer = new AudioPlayer(mAudioSink);
|
||||||
|
|
||||||
|
mAudioPlayer->setListenerCallback(
|
||||||
|
&AwesomePlayer::AudioNotify, this);
|
||||||
|
|
||||||
|
mAudioPlayer->setSource(mAudioSource);
|
||||||
|
mAudioPlayer->start();
|
||||||
|
|
||||||
|
delete mTimeSource;
|
||||||
|
mTimeSource = mAudioPlayer;
|
||||||
|
|
||||||
|
// If there was a seek request while we were paused
|
||||||
|
// and we're just starting up again, honor the request now.
|
||||||
|
seekAudioIfNecessary_l();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mAudioPlayer->resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTimeSource == NULL && mAudioPlayer == NULL) {
|
||||||
|
mTimeSource = new SystemTimeSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVideoSource != NULL) {
|
||||||
|
if (mVideoRenderer == NULL) {
|
||||||
|
initRenderer_l();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVideoRenderer != NULL) {
|
||||||
|
// Kick off video playback
|
||||||
|
postVideoEvent_l();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::initRenderer_l() {
|
||||||
|
if (mISurface != NULL) {
|
||||||
|
sp<MetaData> meta = mVideoSource->getFormat();
|
||||||
|
|
||||||
|
int32_t format;
|
||||||
|
const char *component;
|
||||||
|
int32_t decodedWidth, decodedHeight;
|
||||||
|
CHECK(meta->findInt32(kKeyColorFormat, &format));
|
||||||
|
CHECK(meta->findCString(kKeyDecoderComponent, &component));
|
||||||
|
CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
|
||||||
|
CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
|
||||||
|
|
||||||
|
mVideoRenderer =
|
||||||
|
mClient.interface()->createRenderer(
|
||||||
|
mISurface, component,
|
||||||
|
(OMX_COLOR_FORMATTYPE)format,
|
||||||
|
decodedWidth, decodedHeight,
|
||||||
|
mVideoWidth, mVideoHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::pause() {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
return pause_l();
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::pause_l() {
|
||||||
|
if (!(mFlags & PLAYING)) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelPlayerEvents();
|
||||||
|
|
||||||
|
if (mAudioPlayer != NULL) {
|
||||||
|
mAudioPlayer->pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
mFlags &= ~PLAYING;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AwesomePlayer::isPlaying() const {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
return mFlags & PLAYING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::setISurface(const sp<ISurface> &isurface) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
mISurface = isurface;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::setAudioSink(
|
||||||
|
const sp<MediaPlayerBase::AudioSink> &audioSink) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
mAudioSink = audioSink;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::setLooping(bool shouldLoop) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
mFlags = mFlags & ~LOOPING;
|
||||||
|
|
||||||
|
if (shouldLoop) {
|
||||||
|
mFlags |= LOOPING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::getDuration(int64_t *durationUs) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
if (mDurationUs < 0) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*durationUs = mDurationUs;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::getPosition(int64_t *positionUs) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
if (mVideoRenderer != NULL) {
|
||||||
|
*positionUs = mVideoTimeUs;
|
||||||
|
} else if (mAudioPlayer != NULL) {
|
||||||
|
*positionUs = mAudioPlayer->getMediaTimeUs();
|
||||||
|
} else {
|
||||||
|
*positionUs = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::seekTo(int64_t timeUs) {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
return seekTo_l(timeUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
|
||||||
|
mSeeking = true;
|
||||||
|
mSeekTimeUs = timeUs;
|
||||||
|
|
||||||
|
seekAudioIfNecessary_l();
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::seekAudioIfNecessary_l() {
|
||||||
|
if (mSeeking && mVideoRenderer == NULL && mAudioPlayer != NULL) {
|
||||||
|
mAudioPlayer->seekTo(mSeekTimeUs);
|
||||||
|
|
||||||
|
mSeeking = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::getVideoDimensions(
|
||||||
|
int32_t *width, int32_t *height) const {
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
|
||||||
|
if (mVideoWidth < 0 || mVideoHeight < 0) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*width = mVideoWidth;
|
||||||
|
*height = mVideoHeight;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) {
|
||||||
|
if (source == NULL) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAudioSource = OMXCodec::Create(
|
||||||
|
mClient.interface(), source->getFormat(),
|
||||||
|
false, // createEncoder
|
||||||
|
source);
|
||||||
|
|
||||||
|
if (mAudioSource != NULL) {
|
||||||
|
int64_t durationUs;
|
||||||
|
if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
|
||||||
|
if (mDurationUs < 0 || durationUs > mDurationUs) {
|
||||||
|
mDurationUs = durationUs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t AwesomePlayer::setVideoSource(const sp<MediaSource> &source) {
|
||||||
|
if (source == NULL) {
|
||||||
|
return UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVideoSource = OMXCodec::Create(
|
||||||
|
mClient.interface(), source->getFormat(),
|
||||||
|
false, // createEncoder
|
||||||
|
source);
|
||||||
|
|
||||||
|
if (mVideoSource != NULL) {
|
||||||
|
int64_t durationUs;
|
||||||
|
if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
|
||||||
|
if (mDurationUs < 0 || durationUs > mDurationUs) {
|
||||||
|
mDurationUs = durationUs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(source->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
|
||||||
|
CHECK(source->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
|
||||||
|
|
||||||
|
mVideoSource->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::onEvent(int32_t code) {
|
||||||
|
if (code == 1) {
|
||||||
|
onStreamDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex::Autolock autoLock(mLock);
|
||||||
|
mVideoEventPending = false;
|
||||||
|
|
||||||
|
if (mSeeking) {
|
||||||
|
if (mLastVideoBuffer) {
|
||||||
|
mLastVideoBuffer->release();
|
||||||
|
mLastVideoBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mVideoBuffer) {
|
||||||
|
mVideoBuffer->release();
|
||||||
|
mVideoBuffer = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mVideoBuffer) {
|
||||||
|
MediaSource::ReadOptions options;
|
||||||
|
if (mSeeking) {
|
||||||
|
LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
|
||||||
|
|
||||||
|
options.setSeekTo(mSeekTimeUs);
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
status_t err = mVideoSource->read(&mVideoBuffer, &options);
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
CHECK_EQ(mVideoBuffer, NULL);
|
||||||
|
|
||||||
|
if (err == INFO_FORMAT_CHANGED) {
|
||||||
|
LOGV("VideoSource signalled format change.");
|
||||||
|
|
||||||
|
initRenderer_l();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
postStreamDoneEvent_l();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t timeUs;
|
||||||
|
CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
|
||||||
|
|
||||||
|
mVideoTimeUs = timeUs;
|
||||||
|
|
||||||
|
if (mSeeking) {
|
||||||
|
if (mAudioPlayer != NULL) {
|
||||||
|
LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
|
||||||
|
|
||||||
|
mAudioPlayer->seekTo(timeUs);
|
||||||
|
} else {
|
||||||
|
// If we're playing video only, report seek complete now,
|
||||||
|
// otherwise audio player will notify us later.
|
||||||
|
if (mListener != NULL) {
|
||||||
|
mListener->sendEvent(MEDIA_SEEK_COMPLETE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mFlags |= FIRST_FRAME;
|
||||||
|
mSeeking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFlags & FIRST_FRAME) {
|
||||||
|
mFlags &= ~FIRST_FRAME;
|
||||||
|
|
||||||
|
mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - timeUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t realTimeUs, mediaTimeUs;
|
||||||
|
if (mAudioPlayer != NULL
|
||||||
|
&& mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
|
||||||
|
mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t nowUs = mTimeSource->getRealTimeUs() - mTimeSourceDeltaUs;
|
||||||
|
|
||||||
|
int64_t latenessUs = nowUs - timeUs;
|
||||||
|
|
||||||
|
if (latenessUs > 20000) {
|
||||||
|
// We're more than 20ms late.
|
||||||
|
LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
|
||||||
|
|
||||||
|
mVideoBuffer->release();
|
||||||
|
mVideoBuffer = NULL;
|
||||||
|
|
||||||
|
postVideoEvent_l();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latenessUs < -10000) {
|
||||||
|
// We're more than 10ms early.
|
||||||
|
|
||||||
|
postVideoEvent_l(10000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *id;
|
||||||
|
if (mVideoBuffer->meta_data()->findPointer(kKeyBufferID, &id)) {
|
||||||
|
mVideoRenderer->render((IOMX::buffer_id)id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mLastVideoBuffer) {
|
||||||
|
mLastVideoBuffer->release();
|
||||||
|
mLastVideoBuffer = NULL;
|
||||||
|
}
|
||||||
|
mLastVideoBuffer = mVideoBuffer;
|
||||||
|
mVideoBuffer = NULL;
|
||||||
|
|
||||||
|
postVideoEvent_l();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {
|
||||||
|
if (mVideoEventPending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mVideoEventPending = true;
|
||||||
|
mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::postStreamDoneEvent_l() {
|
||||||
|
if (mStreamDoneEventPending) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mStreamDoneEventPending = true;
|
||||||
|
mQueue.postEvent(mStreamDoneEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
|
|
||||||
#include <media/stagefright/CachingDataSource.h>
|
#include <media/stagefright/CachingDataSource.h>
|
||||||
#include <media/stagefright/DataSource.h>
|
#include <media/stagefright/DataSource.h>
|
||||||
|
#include <media/stagefright/FileSource.h>
|
||||||
#include <media/stagefright/HTTPDataSource.h>
|
#include <media/stagefright/HTTPDataSource.h>
|
||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
#include <utils/String8.h>
|
#include <utils/String8.h>
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@@ -70,13 +70,13 @@ sp<MediaExtractor> MediaExtractor::CreateFromURI(
|
|||||||
const char *uri, const char *mime) {
|
const char *uri, const char *mime) {
|
||||||
sp<DataSource> source;
|
sp<DataSource> source;
|
||||||
if (!strncasecmp("file://", uri, 7)) {
|
if (!strncasecmp("file://", uri, 7)) {
|
||||||
source = new MmapSource(uri + 7);
|
source = new FileSource(uri + 7);
|
||||||
} else if (!strncasecmp("http://", uri, 7)) {
|
} else if (!strncasecmp("http://", uri, 7)) {
|
||||||
source = new HTTPDataSource(uri);
|
source = new HTTPDataSource(uri);
|
||||||
source = new CachingDataSource(source, 64 * 1024, 10);
|
source = new CachingDataSource(source, 64 * 1024, 10);
|
||||||
} else {
|
} else {
|
||||||
// Assume it's a filename.
|
// Assume it's a filename.
|
||||||
source = new MmapSource(uri);
|
source = new FileSource(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source == NULL || source->initCheck() != OK) {
|
if (source == NULL || source->initCheck() != OK) {
|
||||||
|
|||||||
@@ -1,658 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009 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 "MediaPlayerImpl"
|
|
||||||
#include "utils/Log.h"
|
|
||||||
|
|
||||||
#include "include/stagefright_string.h"
|
|
||||||
#include "include/HTTPStream.h"
|
|
||||||
|
|
||||||
#include <OMX_Component.h>
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <media/stagefright/AudioPlayer.h>
|
|
||||||
// #include <media/stagefright/CameraSource.h>
|
|
||||||
#include <media/stagefright/MediaDebug.h>
|
|
||||||
#include <media/stagefright/MediaDefs.h>
|
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
|
||||||
#include <media/stagefright/MediaPlayerImpl.h>
|
|
||||||
#include <media/stagefright/MetaData.h>
|
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
#include <media/stagefright/OMXCodec.h>
|
|
||||||
#include <media/stagefright/ShoutcastSource.h>
|
|
||||||
#include <media/stagefright/TimeSource.h>
|
|
||||||
#include <ui/PixelFormat.h>
|
|
||||||
#include <ui/Surface.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
MediaPlayerImpl::MediaPlayerImpl(const char *uri)
|
|
||||||
: mInitCheck(NO_INIT),
|
|
||||||
mTimeSource(NULL),
|
|
||||||
mAudioPlayer(NULL),
|
|
||||||
mVideoWidth(0),
|
|
||||||
mVideoHeight(0),
|
|
||||||
mVideoPosition(0),
|
|
||||||
mDuration(0),
|
|
||||||
mPlaying(false),
|
|
||||||
mPaused(false),
|
|
||||||
mSeeking(false) {
|
|
||||||
LOGV("MediaPlayerImpl(%s)", uri);
|
|
||||||
DataSource::RegisterDefaultSniffers();
|
|
||||||
|
|
||||||
status_t err = mClient.connect();
|
|
||||||
if (err != OK) {
|
|
||||||
LOGE("Failed to connect to OMXClient.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strncasecmp("shoutcast://", uri, 12)) {
|
|
||||||
setAudioSource(makeShoutcastSource(uri));
|
|
||||||
#if 0
|
|
||||||
} else if (!strncasecmp("camera:", uri, 7)) {
|
|
||||||
mVideoWidth = 480;
|
|
||||||
mVideoHeight = 320;
|
|
||||||
mVideoDecoder = CameraSource::Create();
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
mExtractor = MediaExtractor::CreateFromURI(uri);
|
|
||||||
|
|
||||||
if (mExtractor == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
mInitCheck = OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
|
|
||||||
: mInitCheck(NO_INIT),
|
|
||||||
mTimeSource(NULL),
|
|
||||||
mAudioPlayer(NULL),
|
|
||||||
mVideoWidth(0),
|
|
||||||
mVideoHeight(0),
|
|
||||||
mVideoPosition(0),
|
|
||||||
mDuration(0),
|
|
||||||
mPlaying(false),
|
|
||||||
mPaused(false),
|
|
||||||
mSeeking(false) {
|
|
||||||
LOGV("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
|
|
||||||
DataSource::RegisterDefaultSniffers();
|
|
||||||
|
|
||||||
status_t err = mClient.connect();
|
|
||||||
if (err != OK) {
|
|
||||||
LOGE("Failed to connect to OMXClient.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mExtractor = MediaExtractor::Create(
|
|
||||||
new MmapSource(fd, offset, length));
|
|
||||||
|
|
||||||
if (mExtractor == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
mInitCheck = OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MediaPlayerImpl::initCheck() const {
|
|
||||||
return mInitCheck;
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaPlayerImpl::~MediaPlayerImpl() {
|
|
||||||
stop();
|
|
||||||
setSurface(NULL);
|
|
||||||
|
|
||||||
if (mInitCheck == OK) {
|
|
||||||
mClient.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGV("~MediaPlayerImpl done.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::play() {
|
|
||||||
LOGV("play");
|
|
||||||
|
|
||||||
if (mPlaying) {
|
|
||||||
if (mPaused) {
|
|
||||||
if (mAudioSource != NULL) {
|
|
||||||
mAudioPlayer->resume();
|
|
||||||
}
|
|
||||||
mPaused = false;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlaying = true;
|
|
||||||
|
|
||||||
if (mAudioSource != NULL) {
|
|
||||||
mAudioPlayer = new AudioPlayer(mAudioSink);
|
|
||||||
mAudioPlayer->setSource(mAudioDecoder);
|
|
||||||
|
|
||||||
if (mVideoDecoder == NULL) {
|
|
||||||
// If there is no video, start playing right away,
|
|
||||||
// otherwise we'll start the audio player after we decode
|
|
||||||
// the first video frame, this way we won't be behind right
|
|
||||||
// away.
|
|
||||||
mAudioPlayer->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
mTimeSource = mAudioPlayer;
|
|
||||||
} else {
|
|
||||||
mTimeSource = new SystemTimeSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mVideoDecoder != NULL) {
|
|
||||||
pthread_attr_t attr;
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
|
||||||
|
|
||||||
pthread_create(&mVideoThread, &attr, VideoWrapper, this);
|
|
||||||
|
|
||||||
pthread_attr_destroy(&attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::pause() {
|
|
||||||
if (!mPlaying || mPaused) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAudioSource != NULL) {
|
|
||||||
mAudioPlayer->pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
mPaused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::stop() {
|
|
||||||
if (!mPlaying) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPlaying = false;
|
|
||||||
|
|
||||||
if (mVideoDecoder != NULL) {
|
|
||||||
void *dummy;
|
|
||||||
pthread_join(mVideoThread, &dummy);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mAudioSource != NULL) {
|
|
||||||
mAudioPlayer->stop();
|
|
||||||
|
|
||||||
delete mAudioPlayer;
|
|
||||||
mAudioPlayer = NULL;
|
|
||||||
} else {
|
|
||||||
delete mTimeSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
mTimeSource = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void *MediaPlayerImpl::VideoWrapper(void *me) {
|
|
||||||
((MediaPlayerImpl *)me)->videoEntry();
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::videoEntry() {
|
|
||||||
bool firstFrame = true;
|
|
||||||
bool eof = false;
|
|
||||||
|
|
||||||
status_t err = mVideoDecoder->start();
|
|
||||||
CHECK_EQ(err, OK);
|
|
||||||
|
|
||||||
while (mPlaying) {
|
|
||||||
MediaBuffer *buffer;
|
|
||||||
|
|
||||||
MediaSource::ReadOptions options;
|
|
||||||
bool seeking = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
if (mSeeking) {
|
|
||||||
LOGV("seek-options to %lld", mSeekTimeUs);
|
|
||||||
options.setSeekTo(mSeekTimeUs);
|
|
||||||
|
|
||||||
mSeeking = false;
|
|
||||||
seeking = true;
|
|
||||||
eof = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eof || mPaused) {
|
|
||||||
usleep(100000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t err = mVideoDecoder->read(&buffer, &options);
|
|
||||||
CHECK((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
|
|
||||||
|
|
||||||
if (err == INFO_FORMAT_CHANGED) {
|
|
||||||
LOGV("format changed.");
|
|
||||||
depopulateISurface();
|
|
||||||
populateISurface();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err == ERROR_END_OF_STREAM || err != OK) {
|
|
||||||
eof = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer->range_length() == 0) {
|
|
||||||
// The final buffer is empty.
|
|
||||||
buffer->release();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t pts_us;
|
|
||||||
CHECK(buffer->meta_data()->findInt64(kKeyTime, &pts_us));
|
|
||||||
|
|
||||||
{
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
mVideoPosition = pts_us;
|
|
||||||
|
|
||||||
LOGV("now_video = %.2f secs (%lld ms)",
|
|
||||||
pts_us / 1E6, (pts_us + 500) / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seeking && mAudioPlayer != NULL) {
|
|
||||||
// Now that we know where exactly video seeked (taking sync-samples
|
|
||||||
// into account), we will seek the audio track to the same time.
|
|
||||||
mAudioPlayer->seekTo(pts_us);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstFrame || seeking) {
|
|
||||||
if (firstFrame && mAudioPlayer != NULL) {
|
|
||||||
// We've deferred starting the audio player until now.
|
|
||||||
mAudioPlayer->start();
|
|
||||||
}
|
|
||||||
mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - pts_us;
|
|
||||||
firstFrame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
displayOrDiscardFrame(buffer, pts_us);
|
|
||||||
}
|
|
||||||
|
|
||||||
mVideoDecoder->stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::displayOrDiscardFrame(
|
|
||||||
MediaBuffer *buffer, int64_t pts_us) {
|
|
||||||
for (;;) {
|
|
||||||
if (!mPlaying || mPaused) {
|
|
||||||
buffer->release();
|
|
||||||
buffer = NULL;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t realtime_us, mediatime_us;
|
|
||||||
if (mAudioPlayer != NULL
|
|
||||||
&& mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
|
|
||||||
mTimeSourceDeltaUs = realtime_us - mediatime_us;
|
|
||||||
LOGV("mTimeSourceDeltaUs = %.2f secs", mTimeSourceDeltaUs / 1E6);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t now_us = mTimeSource->getRealTimeUs();
|
|
||||||
now_us -= mTimeSourceDeltaUs;
|
|
||||||
|
|
||||||
int64_t delay_us = pts_us - now_us;
|
|
||||||
|
|
||||||
if (delay_us < -15000) {
|
|
||||||
// We're late.
|
|
||||||
|
|
||||||
LOGV("we're late by %lld ms, dropping a frame\n",
|
|
||||||
-delay_us / 1000);
|
|
||||||
|
|
||||||
buffer->release();
|
|
||||||
buffer = NULL;
|
|
||||||
return;
|
|
||||||
} else if (delay_us > 100000) {
|
|
||||||
LOGV("we're much too early (by %lld ms)\n",
|
|
||||||
delay_us / 1000);
|
|
||||||
usleep(100000);
|
|
||||||
continue;
|
|
||||||
} else if (delay_us > 0) {
|
|
||||||
usleep(delay_us);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
if (mVideoRenderer.get() != NULL) {
|
|
||||||
sendFrameToISurface(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer->release();
|
|
||||||
buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::init() {
|
|
||||||
if (mExtractor != NULL) {
|
|
||||||
size_t num_tracks = mExtractor->countTracks();
|
|
||||||
|
|
||||||
mDuration = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_tracks; ++i) {
|
|
||||||
const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
|
|
||||||
CHECK(meta != NULL);
|
|
||||||
|
|
||||||
const char *mime;
|
|
||||||
if (!meta->findCString(kKeyMIMEType, &mime)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_audio = false;
|
|
||||||
bool is_acceptable = false;
|
|
||||||
if (!strncasecmp(mime, "audio/", 6)) {
|
|
||||||
is_audio = true;
|
|
||||||
is_acceptable = (mAudioSource == NULL);
|
|
||||||
} else if (!strncasecmp(mime, "video/", 6)) {
|
|
||||||
is_acceptable = (mVideoSource == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_acceptable) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp<MediaSource> source = mExtractor->getTrack(i);
|
|
||||||
|
|
||||||
int64_t durationUs;
|
|
||||||
if (meta->findInt64(kKeyDuration, &durationUs)) {
|
|
||||||
if (durationUs > mDuration) {
|
|
||||||
mDuration = durationUs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_audio) {
|
|
||||||
setAudioSource(source);
|
|
||||||
} else {
|
|
||||||
setVideoSource(source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::setAudioSource(const sp<MediaSource> &source) {
|
|
||||||
LOGV("setAudioSource");
|
|
||||||
mAudioSource = source;
|
|
||||||
|
|
||||||
sp<MetaData> meta = source->getFormat();
|
|
||||||
|
|
||||||
const char *mime;
|
|
||||||
CHECK(meta->findCString(kKeyMIMEType, &mime));
|
|
||||||
|
|
||||||
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
|
|
||||||
mAudioDecoder = source;
|
|
||||||
} else {
|
|
||||||
mAudioDecoder = OMXCodec::Create(
|
|
||||||
mClient.interface(), meta, false /* createEncoder */, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::setVideoSource(const sp<MediaSource> &source) {
|
|
||||||
LOGV("setVideoSource");
|
|
||||||
mVideoSource = source;
|
|
||||||
|
|
||||||
sp<MetaData> meta = source->getFormat();
|
|
||||||
|
|
||||||
bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
|
|
||||||
CHECK(success);
|
|
||||||
|
|
||||||
success = meta->findInt32(kKeyHeight, &mVideoHeight);
|
|
||||||
CHECK(success);
|
|
||||||
|
|
||||||
mVideoDecoder = OMXCodec::Create(
|
|
||||||
mClient.interface(), meta, false /* createEncoder */, source);
|
|
||||||
|
|
||||||
if (mISurface.get() != NULL || mSurface.get() != NULL) {
|
|
||||||
depopulateISurface();
|
|
||||||
populateISurface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::setSurface(const sp<Surface> &surface) {
|
|
||||||
LOGV("setSurface %p", surface.get());
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
|
|
||||||
depopulateISurface();
|
|
||||||
|
|
||||||
mSurface = surface;
|
|
||||||
mISurface = NULL;
|
|
||||||
|
|
||||||
if (mSurface.get() != NULL) {
|
|
||||||
populateISurface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) {
|
|
||||||
LOGV("setISurface %p", isurface.get());
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
|
|
||||||
depopulateISurface();
|
|
||||||
|
|
||||||
mSurface = NULL;
|
|
||||||
mISurface = isurface;
|
|
||||||
|
|
||||||
if (mISurface.get() != NULL) {
|
|
||||||
populateISurface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) {
|
|
||||||
if (strncasecmp(uri, "shoutcast://", 12)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
string host;
|
|
||||||
string path;
|
|
||||||
int port;
|
|
||||||
|
|
||||||
char *slash = strchr(uri + 12, '/');
|
|
||||||
if (slash == NULL) {
|
|
||||||
host = uri + 12;
|
|
||||||
path = "/";
|
|
||||||
} else {
|
|
||||||
host = string(uri + 12, slash - (uri + 12));
|
|
||||||
path = slash;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *colon = strchr(host.c_str(), ':');
|
|
||||||
if (colon == NULL) {
|
|
||||||
port = 80;
|
|
||||||
} else {
|
|
||||||
char *end;
|
|
||||||
long tmp = strtol(colon + 1, &end, 10);
|
|
||||||
CHECK(end > colon + 1);
|
|
||||||
CHECK(tmp > 0 && tmp < 65536);
|
|
||||||
port = tmp;
|
|
||||||
|
|
||||||
host = string(host, 0, colon - host.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGV("Connecting to host '%s', port %d, path '%s'",
|
|
||||||
host.c_str(), port, path.c_str());
|
|
||||||
|
|
||||||
HTTPStream *http = new HTTPStream;
|
|
||||||
int http_status;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
status_t err = http->connect(host.c_str(), port);
|
|
||||||
CHECK_EQ(err, OK);
|
|
||||||
|
|
||||||
err = http->send("GET ");
|
|
||||||
err = http->send(path.c_str());
|
|
||||||
err = http->send(" HTTP/1.1\r\n");
|
|
||||||
err = http->send("Host: ");
|
|
||||||
err = http->send(host.c_str());
|
|
||||||
err = http->send("\r\n");
|
|
||||||
err = http->send("Icy-MetaData: 1\r\n\r\n");
|
|
||||||
|
|
||||||
CHECK_EQ(OK, http->receive_header(&http_status));
|
|
||||||
|
|
||||||
if (http_status == 301 || http_status == 302) {
|
|
||||||
string location;
|
|
||||||
CHECK(http->find_header_value("Location", &location));
|
|
||||||
|
|
||||||
CHECK(string(location, 0, 7) == "http://");
|
|
||||||
location.erase(0, 7);
|
|
||||||
string::size_type slashPos = location.find('/');
|
|
||||||
if (slashPos == string::npos) {
|
|
||||||
slashPos = location.size();
|
|
||||||
location += '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
http->disconnect();
|
|
||||||
|
|
||||||
LOGV("Redirecting to %s\n", location.c_str());
|
|
||||||
|
|
||||||
host = string(location, 0, slashPos);
|
|
||||||
|
|
||||||
string::size_type colonPos = host.find(':');
|
|
||||||
if (colonPos != string::npos) {
|
|
||||||
const char *start = host.c_str() + colonPos + 1;
|
|
||||||
char *end;
|
|
||||||
long tmp = strtol(start, &end, 10);
|
|
||||||
CHECK(end > start && (*end == '\0'));
|
|
||||||
|
|
||||||
port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
|
|
||||||
} else {
|
|
||||||
port = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = string(location, slashPos);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (http_status != 200) {
|
|
||||||
LOGE("Connection failed: http_status = %d", http_status);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaSource *source = new ShoutcastSource(http);
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MediaPlayerImpl::isPlaying() const {
|
|
||||||
return mPlaying && !mPaused;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t MediaPlayerImpl::getDuration() {
|
|
||||||
return mDuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t MediaPlayerImpl::getPosition() {
|
|
||||||
int64_t position = 0;
|
|
||||||
if (mVideoSource != NULL) {
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
position = mVideoPosition;
|
|
||||||
} else if (mAudioPlayer != NULL) {
|
|
||||||
position = mAudioPlayer->getMediaTimeUs();
|
|
||||||
}
|
|
||||||
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MediaPlayerImpl::seekTo(int64_t time) {
|
|
||||||
LOGV("seekTo %lld", time);
|
|
||||||
|
|
||||||
if (mPaused) {
|
|
||||||
return UNKNOWN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mVideoSource == NULL && mAudioPlayer != NULL) {
|
|
||||||
mAudioPlayer->seekTo(time);
|
|
||||||
} else {
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
mSeekTimeUs = time;
|
|
||||||
mSeeking = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::populateISurface() {
|
|
||||||
if (mVideoSource == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sp<MetaData> meta = mVideoDecoder->getFormat();
|
|
||||||
|
|
||||||
int32_t format;
|
|
||||||
const char *component;
|
|
||||||
int32_t decodedWidth, decodedHeight;
|
|
||||||
bool success = meta->findInt32(kKeyColorFormat, &format);
|
|
||||||
success = success && meta->findCString(kKeyDecoderComponent, &component);
|
|
||||||
success = success && meta->findInt32(kKeyWidth, &decodedWidth);
|
|
||||||
success = success && meta->findInt32(kKeyHeight, &decodedHeight);
|
|
||||||
CHECK(success);
|
|
||||||
|
|
||||||
LOGV("mVideoWidth=%d, mVideoHeight=%d, decodedWidth=%d, decodedHeight=%d",
|
|
||||||
mVideoWidth, mVideoHeight, decodedWidth, decodedHeight);
|
|
||||||
|
|
||||||
if (mSurface.get() != NULL) {
|
|
||||||
mVideoRenderer =
|
|
||||||
mClient.interface()->createRenderer(
|
|
||||||
mSurface, component,
|
|
||||||
(OMX_COLOR_FORMATTYPE)format,
|
|
||||||
decodedWidth, decodedHeight,
|
|
||||||
mVideoWidth, mVideoHeight);
|
|
||||||
} else {
|
|
||||||
mVideoRenderer =
|
|
||||||
mClient.interface()->createRenderer(
|
|
||||||
mISurface, component,
|
|
||||||
(OMX_COLOR_FORMATTYPE)format,
|
|
||||||
decodedWidth, decodedHeight,
|
|
||||||
mVideoWidth, mVideoHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::depopulateISurface() {
|
|
||||||
mVideoRenderer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) {
|
|
||||||
void *id;
|
|
||||||
if (buffer->meta_data()->findPointer(kKeyBufferID, &id)) {
|
|
||||||
mVideoRenderer->render((IOMX::buffer_id)id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaPlayerImpl::setAudioSink(
|
|
||||||
const sp<MediaPlayerBase::AudioSink> &audioSink) {
|
|
||||||
LOGV("setAudioSink %p", audioSink.get());
|
|
||||||
mAudioSink = audioSink;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009 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 "MmapSource"
|
|
||||||
#include <utils/Log.h>
|
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <media/stagefright/MediaDebug.h>
|
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
MmapSource::MmapSource(const char *filename)
|
|
||||||
: mFd(open(filename, O_RDONLY)),
|
|
||||||
mBase(NULL),
|
|
||||||
mSize(0) {
|
|
||||||
LOGV("MmapSource '%s'", filename);
|
|
||||||
|
|
||||||
if (mFd < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
off_t size = lseek(mFd, 0, SEEK_END);
|
|
||||||
mSize = (size_t)size;
|
|
||||||
|
|
||||||
mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, 0);
|
|
||||||
|
|
||||||
if (mBase == (void *)-1) {
|
|
||||||
mBase = NULL;
|
|
||||||
|
|
||||||
close(mFd);
|
|
||||||
mFd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MmapSource::MmapSource(int fd, int64_t offset, int64_t length)
|
|
||||||
: mFd(fd),
|
|
||||||
mBase(NULL),
|
|
||||||
mSize(length) {
|
|
||||||
LOGV("MmapSource fd:%d offset:%lld length:%lld", fd, offset, length);
|
|
||||||
CHECK(fd >= 0);
|
|
||||||
|
|
||||||
mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, offset);
|
|
||||||
|
|
||||||
if (mBase == (void *)-1) {
|
|
||||||
mBase = NULL;
|
|
||||||
|
|
||||||
close(mFd);
|
|
||||||
mFd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MmapSource::~MmapSource() {
|
|
||||||
if (mFd != -1) {
|
|
||||||
munmap(mBase, mSize);
|
|
||||||
mBase = NULL;
|
|
||||||
mSize = 0;
|
|
||||||
|
|
||||||
close(mFd);
|
|
||||||
mFd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MmapSource::initCheck() const {
|
|
||||||
return mFd == -1 ? NO_INIT : OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t MmapSource::readAt(off_t offset, void *data, size_t size) {
|
|
||||||
LOGV("readAt offset:%ld data:%p size:%d", offset, data, size);
|
|
||||||
CHECK(offset >= 0);
|
|
||||||
|
|
||||||
size_t avail = 0;
|
|
||||||
if (offset >= 0 && offset < (off_t)mSize) {
|
|
||||||
avail = mSize - offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size > avail) {
|
|
||||||
size = avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(data, (const uint8_t *)mBase + offset, size);
|
|
||||||
|
|
||||||
return (ssize_t)size;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t MmapSource::getSize(off_t *size) {
|
|
||||||
*size = mSize;
|
|
||||||
|
|
||||||
return OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace android
|
|
||||||
|
|
||||||
@@ -30,7 +30,6 @@
|
|||||||
#include <media/stagefright/MediaDefs.h>
|
#include <media/stagefright/MediaDefs.h>
|
||||||
#include <media/stagefright/MediaExtractor.h>
|
#include <media/stagefright/MediaExtractor.h>
|
||||||
#include <media/stagefright/MetaData.h>
|
#include <media/stagefright/MetaData.h>
|
||||||
#include <media/stagefright/MmapSource.h>
|
|
||||||
#include <media/stagefright/OMXCodec.h>
|
#include <media/stagefright/OMXCodec.h>
|
||||||
#include <media/stagefright/Utils.h>
|
#include <media/stagefright/Utils.h>
|
||||||
#include <utils/Vector.h>
|
#include <utils/Vector.h>
|
||||||
|
|||||||
@@ -135,7 +135,9 @@ static bool MatchesEventID(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool TimedEventQueue::cancelEvent(event_id id) {
|
bool TimedEventQueue::cancelEvent(event_id id) {
|
||||||
CHECK(id != 0);
|
if (id == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
|
cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
|
||||||
|
|
||||||
@@ -162,6 +164,7 @@ void TimedEventQueue::cancelEvents(
|
|||||||
mQueueHeadChangedCondition.signal();
|
mQueueHeadChangedCondition.signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*it).event->setEventID(0);
|
||||||
it = mQueue.erase(it);
|
it = mQueue.erase(it);
|
||||||
|
|
||||||
if (stopAfterFirstMatch) {
|
if (stopAfterFirstMatch) {
|
||||||
@@ -228,7 +231,12 @@ void TimedEventQueue::threadEntry() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mQueue.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
event = (*it).event;
|
event = (*it).event;
|
||||||
|
event->setEventID(0);
|
||||||
mQueue.erase(it);
|
mQueue.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,14 +32,14 @@ public:
|
|||||||
virtual sp<MediaSource> getTrack(size_t index);
|
virtual sp<MediaSource> getTrack(size_t index);
|
||||||
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
|
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
|
||||||
|
|
||||||
static sp<MetaData> makeAMRFormat(bool isWide);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~AMRExtractor();
|
virtual ~AMRExtractor();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sp<DataSource> mDataSource;
|
sp<DataSource> mDataSource;
|
||||||
|
sp<MetaData> mMeta;
|
||||||
status_t mInitCheck;
|
status_t mInitCheck;
|
||||||
|
size_t mFrameSize;
|
||||||
bool mIsWide;
|
bool mIsWide;
|
||||||
|
|
||||||
AMRExtractor(const AMRExtractor &);
|
AMRExtractor(const AMRExtractor &);
|
||||||
|
|||||||
133
media/libstagefright/include/AwesomePlayer.h
Normal file
133
media/libstagefright/include/AwesomePlayer.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AWESOME_PLAYER_H_
|
||||||
|
|
||||||
|
#define AWESOME_PLAYER_H_
|
||||||
|
|
||||||
|
#include "TimedEventQueue.h"
|
||||||
|
|
||||||
|
#include <media/MediaPlayerInterface.h>
|
||||||
|
#include <media/stagefright/OMXClient.h>
|
||||||
|
#include <utils/threads.h>
|
||||||
|
|
||||||
|
namespace android {
|
||||||
|
|
||||||
|
struct MediaBuffer;
|
||||||
|
struct MediaExtractor;
|
||||||
|
struct MediaSource;
|
||||||
|
struct AudioPlayer;
|
||||||
|
struct TimeSource;
|
||||||
|
|
||||||
|
struct AwesomePlayer {
|
||||||
|
AwesomePlayer();
|
||||||
|
~AwesomePlayer();
|
||||||
|
|
||||||
|
void setListener(const sp<MediaPlayerBase> &listener);
|
||||||
|
|
||||||
|
status_t setDataSource(const char *uri);
|
||||||
|
status_t setDataSource(int fd, int64_t offset, int64_t length);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
status_t play();
|
||||||
|
status_t pause();
|
||||||
|
|
||||||
|
bool isPlaying() const;
|
||||||
|
|
||||||
|
void setISurface(const sp<ISurface> &isurface);
|
||||||
|
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
|
||||||
|
status_t setLooping(bool shouldLoop);
|
||||||
|
|
||||||
|
status_t getDuration(int64_t *durationUs);
|
||||||
|
status_t getPosition(int64_t *positionUs);
|
||||||
|
|
||||||
|
status_t seekTo(int64_t timeUs);
|
||||||
|
|
||||||
|
status_t getVideoDimensions(int32_t *width, int32_t *height) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend struct AwesomeEvent;
|
||||||
|
|
||||||
|
enum Flags {
|
||||||
|
PLAYING = 1,
|
||||||
|
LOOPING = 2,
|
||||||
|
FIRST_FRAME = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
mutable Mutex mLock;
|
||||||
|
|
||||||
|
OMXClient mClient;
|
||||||
|
TimedEventQueue mQueue;
|
||||||
|
sp<MediaPlayerBase> mListener;
|
||||||
|
|
||||||
|
sp<ISurface> mISurface;
|
||||||
|
sp<MediaPlayerBase::AudioSink> mAudioSink;
|
||||||
|
|
||||||
|
TimeSource *mTimeSource;
|
||||||
|
|
||||||
|
sp<MediaSource> mVideoSource;
|
||||||
|
sp<IOMXRenderer> mVideoRenderer;
|
||||||
|
|
||||||
|
sp<MediaSource> mAudioSource;
|
||||||
|
AudioPlayer *mAudioPlayer;
|
||||||
|
int64_t mDurationUs;
|
||||||
|
|
||||||
|
uint32_t mFlags;
|
||||||
|
|
||||||
|
int32_t mVideoWidth, mVideoHeight;
|
||||||
|
int64_t mTimeSourceDeltaUs;
|
||||||
|
int64_t mVideoTimeUs;
|
||||||
|
|
||||||
|
bool mSeeking;
|
||||||
|
int64_t mSeekTimeUs;
|
||||||
|
|
||||||
|
sp<TimedEventQueue::Event> mVideoEvent;
|
||||||
|
bool mVideoEventPending;
|
||||||
|
sp<TimedEventQueue::Event> mStreamDoneEvent;
|
||||||
|
bool mStreamDoneEventPending;
|
||||||
|
|
||||||
|
void postVideoEvent_l(int64_t delayUs = -1);
|
||||||
|
void postStreamDoneEvent_l();
|
||||||
|
|
||||||
|
MediaBuffer *mLastVideoBuffer;
|
||||||
|
MediaBuffer *mVideoBuffer;
|
||||||
|
|
||||||
|
status_t setDataSource_l(const sp<MediaExtractor> &extractor);
|
||||||
|
void reset_l();
|
||||||
|
status_t seekTo_l(int64_t timeUs);
|
||||||
|
status_t pause_l();
|
||||||
|
void initRenderer_l();
|
||||||
|
void seekAudioIfNecessary_l();
|
||||||
|
|
||||||
|
void cancelPlayerEvents();
|
||||||
|
|
||||||
|
status_t setAudioSource(const sp<MediaSource> &source);
|
||||||
|
status_t setVideoSource(const sp<MediaSource> &source);
|
||||||
|
|
||||||
|
void onEvent(int32_t code);
|
||||||
|
|
||||||
|
static void AudioNotify(void *me, int what);
|
||||||
|
void onStreamDone();
|
||||||
|
|
||||||
|
AwesomePlayer(const AwesomePlayer &);
|
||||||
|
AwesomePlayer &operator=(const AwesomePlayer &);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace android
|
||||||
|
|
||||||
|
#endif // AWESOME_PLAYER_H_
|
||||||
|
|
||||||
Reference in New Issue
Block a user