Merge change Ib1b7bce4 into eclair-mr2
* changes: Squashed commit of the following:
This commit is contained in:
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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/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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 &);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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/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) {
|
||||
|
||||
@@ -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/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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 &);
|
||||
|
||||
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