Merge change Ib1b7bce4 into eclair-mr2

* changes:
  Squashed commit of the following:
This commit is contained in:
Android (Google) Code Review
2009-11-23 10:46:25 -08:00
20 changed files with 925 additions and 1086 deletions

View File

@@ -19,13 +19,13 @@
#include <binder/ProcessState.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MPEG4Writer.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <media/MediaPlayerInterface.h>
@@ -105,7 +105,7 @@ sp<MediaSource> createSource(const char *filename) {
sp<MediaSource> source;
sp<MediaExtractor> extractor =
MediaExtractor::Create(new MmapSource(filename));
MediaExtractor::Create(new FileSource(filename));
size_t num_tracks = extractor->countTracks();

View File

@@ -24,15 +24,14 @@
#include <binder/ProcessState.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/JPEGSource.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaPlayerImpl.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
@@ -52,9 +51,6 @@ static int64_t getNowUs() {
static void playSource(OMXClient *client, const sp<MediaSource> &source) {
sp<MetaData> meta = source->getFormat();
int64_t durationUs;
CHECK(meta->findInt64(kKeyDuration, &durationUs));
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
@@ -74,6 +70,9 @@ static void playSource(OMXClient *client, const sp<MediaSource> &source) {
rawSource->start();
if (gReproduceBug >= 3 && gReproduceBug <= 5) {
int64_t durationUs;
CHECK(meta->findInt64(kKeyDuration, &durationUs));
status_t err;
MediaBuffer *buffer;
MediaSource::ReadOptions options;
@@ -368,7 +367,7 @@ int main(int argc, char **argv) {
dataSource = new HTTPDataSource(filename);
dataSource = new CachingDataSource(dataSource, 64 * 1024, 10);
} else {
dataSource = new MmapSource(filename);
dataSource = new FileSource(filename);
}
bool isJPEG = false;

View File

@@ -133,9 +133,9 @@ public:
return INVALID_OPERATION;
};
protected:
virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); }
protected:
void* mCookie;
notify_callback_f mNotify;
};

View File

@@ -30,12 +30,20 @@ class AudioTrack;
class AudioPlayer : public TimeSource {
public:
enum {
REACHED_EOS,
SEEK_COMPLETE
};
AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
virtual ~AudioPlayer();
// Caller retains ownership of "source".
void setSource(const sp<MediaSource> &source);
void setListenerCallback(
void (*notify)(void *cookie, int what), void *cookie);
// Return time in us.
virtual int64_t getRealTimeUs();
@@ -76,6 +84,9 @@ private:
bool mStarted;
void (*mListenerCallback)(void *cookie, int what);
void *mListenerCookie;
sp<MediaPlayerBase::AudioSink> mAudioSink;
static void AudioCallback(int event, void *user, void *info);

View File

@@ -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_

View File

@@ -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_

View File

@@ -24,11 +24,11 @@
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/ColorConverter.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXCodec.h>
namespace android {
@@ -58,7 +58,7 @@ status_t StagefrightMetadataRetriever::setDataSource(
LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
mExtractor = MediaExtractor::Create(
new MmapSource(fd, offset, length));
new FileSource(fd, offset, length));
return OK;
}

View File

@@ -3,19 +3,24 @@
#include <utils/Log.h>
#include "StagefrightPlayer.h"
#include <media/stagefright/MediaPlayerImpl.h>
#include "AwesomePlayer.h"
namespace android {
StagefrightPlayer::StagefrightPlayer()
: mPlayer(NULL) {
: mPlayer(new AwesomePlayer) {
LOGV("StagefrightPlayer");
mPlayer->setListener(this);
}
StagefrightPlayer::~StagefrightPlayer() {
LOGV("~StagefrightPlayer");
reset();
LOGV("~StagefrightPlayer done.");
delete mPlayer;
mPlayer = NULL;
}
status_t StagefrightPlayer::initCheck() {
@@ -25,62 +30,32 @@ status_t StagefrightPlayer::initCheck() {
status_t StagefrightPlayer::setDataSource(const char *url) {
LOGV("setDataSource('%s')", url);
reset();
mPlayer = new MediaPlayerImpl(url);
status_t err = mPlayer->initCheck();
if (err != OK) {
delete mPlayer;
mPlayer = NULL;
} else {
mPlayer->setAudioSink(mAudioSink);
}
return err;
return mPlayer->setDataSource(url);
}
// Warning: The filedescriptor passed into this method will only be valid until
// the method returns, if you want to keep it, dup it!
status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
LOGV("setDataSource(%d, %lld, %lld)", 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;
return mPlayer->setDataSource(dup(fd), offset, length);
}
status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
LOGV("setVideoSurface");
if (mPlayer == NULL) {
return NO_INIT;
}
mPlayer->setISurface(surface);
return OK;
}
status_t StagefrightPlayer::prepare() {
LOGV("prepare");
if (mPlayer == NULL) {
return NO_INIT;
int32_t width, height;
if (mPlayer->getVideoDimensions(&width, &height) != OK) {
width = height = 0;
}
sendEvent(
MEDIA_SET_VIDEO_SIZE,
mPlayer->getWidth(), mPlayer->getHeight());
sendEvent(MEDIA_SET_VIDEO_SIZE, width, height);
return OK;
}
@@ -102,92 +77,76 @@ status_t StagefrightPlayer::prepareAsync() {
status_t StagefrightPlayer::start() {
LOGV("start");
if (mPlayer == NULL) {
return NO_INIT;
}
mPlayer->play();
return OK;
return mPlayer->play();
}
status_t StagefrightPlayer::stop() {
LOGV("stop");
if (mPlayer == NULL) {
return NO_INIT;
}
reset();
return OK;
return pause(); // what's the difference?
}
status_t StagefrightPlayer::pause() {
LOGV("pause");
if (mPlayer == NULL) {
return NO_INIT;
}
mPlayer->pause();
return OK;
return mPlayer->pause();
}
bool StagefrightPlayer::isPlaying() {
LOGV("isPlaying");
return mPlayer != NULL && mPlayer->isPlaying();
return mPlayer->isPlaying();
}
status_t StagefrightPlayer::seekTo(int msec) {
LOGV("seekTo");
if (mPlayer == NULL) {
return NO_INIT;
}
status_t err = mPlayer->seekTo((int64_t)msec * 1000);
sendEvent(MEDIA_SEEK_COMPLETE);
return err;
}
status_t StagefrightPlayer::getCurrentPosition(int *msec) {
LOGV("getCurrentPosition");
if (mPlayer == NULL) {
return NO_INIT;
int64_t positionUs;
status_t err = mPlayer->getPosition(&positionUs);
if (err != OK) {
return err;
}
*msec = mPlayer->getPosition() / 1000;
*msec = (positionUs + 500) / 1000;
return OK;
}
status_t StagefrightPlayer::getDuration(int *msec) {
LOGV("getDuration");
if (mPlayer == NULL) {
return NO_INIT;
int64_t durationUs;
status_t err = mPlayer->getDuration(&durationUs);
if (err != OK) {
return err;
}
*msec = mPlayer->getDuration() / 1000;
*msec = (durationUs + 500) / 1000;
return OK;
}
status_t StagefrightPlayer::reset() {
LOGV("reset");
delete mPlayer;
mPlayer = NULL;
mPlayer->reset();
return OK;
}
status_t StagefrightPlayer::setLooping(int loop) {
LOGV("setLooping");
return UNKNOWN_ERROR;
return mPlayer->setLooping(loop);
}
player_type StagefrightPlayer::playerType() {
@@ -202,9 +161,7 @@ status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
MediaPlayerInterface::setAudioSink(audioSink);
if (mPlayer != NULL) {
mPlayer->setAudioSink(audioSink);
}
mPlayer->setAudioSink(audioSink);
}
} // namespace android

View File

@@ -22,7 +22,7 @@
namespace android {
class MediaPlayerImpl;
struct AwesomePlayer;
class StagefrightPlayer : public MediaPlayerInterface {
public:
@@ -49,7 +49,7 @@ public:
virtual void setAudioSink(const sp<AudioSink> &audioSink);
private:
MediaPlayerImpl *mPlayer;
AwesomePlayer *mPlayer;
StagefrightPlayer(const StagefrightPlayer &);
StagefrightPlayer &operator=(const StagefrightPlayer &);

View File

@@ -33,7 +33,10 @@ namespace android {
class AMRSource : public MediaSource {
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 stop();
@@ -48,6 +51,8 @@ protected:
private:
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
size_t mFrameSize;
bool mIsWide;
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)
: mDataSource(source),
mInitCheck(NO_INIT) {
String8 mimeType;
float confidence;
if (SniffAMR(mDataSource, &mimeType, &confidence)) {
mInitCheck = OK;
mIsWide = (mimeType == MEDIA_MIMETYPE_AUDIO_AMR_WB);
if (!SniffAMR(mDataSource, &mimeType, &confidence)) {
return;
}
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() {
@@ -84,7 +137,7 @@ sp<MediaSource> AMRExtractor::getTrack(size_t index) {
return NULL;
}
return new AMRSource(mDataSource, mIsWide);
return new AMRSource(mDataSource, mMeta, mFrameSize, mIsWide);
}
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 makeAMRFormat(mIsWide);
}
// 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;
return mMeta;
}
////////////////////////////////////////////////////////////////////////////////
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),
mMeta(meta),
mFrameSize(frameSize),
mIsWide(isWide),
mOffset(mIsWide ? 9 : 6),
mCurrentTimeUs(0),
@@ -148,13 +192,20 @@ status_t AMRSource::stop() {
}
sp<MetaData> AMRSource::getFormat() {
return AMRExtractor::makeAMRFormat(mIsWide);
return mMeta;
}
status_t AMRSource::read(
MediaBuffer **out, const ReadOptions *options) {
*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;
ssize_t n = mDataSource->readAt(mOffset, &header, 1);
@@ -180,17 +231,8 @@ status_t AMRSource::read(
return ERROR_MALFORMED;
}
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 = mIsWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
// Round up bits to bytes and add 1 for the header byte.
frameSize = (frameSize + 7) / 8 + 1;
size_t frameSize = getFrameSize(mIsWide, FT);
CHECK_EQ(frameSize, mFrameSize);
n = mDataSource->readAt(mOffset, buffer->data(), frameSize);

View File

@@ -17,6 +17,7 @@ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
LOCAL_SRC_FILES += \
AMRExtractor.cpp \
AudioPlayer.cpp \
AwesomePlayer.cpp \
CachingDataSource.cpp \
CameraSource.cpp \
DataSource.cpp \
@@ -28,8 +29,6 @@ LOCAL_SRC_FILES += \
MPEG4Extractor.cpp \
MPEG4Writer.cpp \
MediaExtractor.cpp \
MediaPlayerImpl.cpp \
MmapSource.cpp \
SampleTable.cpp \
ShoutcastSource.cpp \
TimeSource.cpp \

View File

@@ -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) {
CHECK_EQ(mSource, NULL);
mSource = source;
@@ -195,7 +201,6 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
mInputBuffer->release();
mInputBuffer = NULL;
}
mSeeking = false;
}
}
@@ -205,7 +210,19 @@ void AudioPlayer::fillBuffer(void *data, size_t size) {
CHECK((err == OK && mInputBuffer != NULL)
|| (err != OK && mInputBuffer == NULL));
if (mSeeking) {
mSeeking = false;
if (mListenerCallback) {
(*mListenerCallback)(mListenerCookie, SEEK_COMPLETE);
}
}
if (err != OK) {
if (mListenerCallback) {
(*mListenerCallback)(mListenerCookie, REACHED_EOS);
}
memset((char *)data + size_done, 0, size_remaining);
break;
}

View 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

View File

@@ -25,10 +25,10 @@
#include <media/stagefright/CachingDataSource.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/HTTPDataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MmapSource.h>
#include <utils/String8.h>
namespace android {
@@ -70,13 +70,13 @@ sp<MediaExtractor> MediaExtractor::CreateFromURI(
const char *uri, const char *mime) {
sp<DataSource> source;
if (!strncasecmp("file://", uri, 7)) {
source = new MmapSource(uri + 7);
source = new FileSource(uri + 7);
} else if (!strncasecmp("http://", uri, 7)) {
source = new HTTPDataSource(uri);
source = new CachingDataSource(source, 64 * 1024, 10);
} else {
// Assume it's a filename.
source = new MmapSource(uri);
source = new FileSource(uri);
}
if (source == NULL || source->initCheck() != OK) {

View File

@@ -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

View File

@@ -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

View File

@@ -30,7 +30,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/Utils.h>
#include <utils/Vector.h>

View File

@@ -135,7 +135,9 @@ static bool MatchesEventID(
}
bool TimedEventQueue::cancelEvent(event_id id) {
CHECK(id != 0);
if (id == 0) {
return false;
}
cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
@@ -162,6 +164,7 @@ void TimedEventQueue::cancelEvents(
mQueueHeadChangedCondition.signal();
}
(*it).event->setEventID(0);
it = mQueue.erase(it);
if (stopAfterFirstMatch) {
@@ -228,7 +231,12 @@ void TimedEventQueue::threadEntry() {
}
}
if (mQueue.empty()) {
continue;
}
event = (*it).event;
event->setEventID(0);
mQueue.erase(it);
}

View File

@@ -32,14 +32,14 @@ public:
virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
static sp<MetaData> makeAMRFormat(bool isWide);
protected:
virtual ~AMRExtractor();
private:
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
status_t mInitCheck;
size_t mFrameSize;
bool mIsWide;
AMRExtractor(const AMRExtractor &);

View 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_