diff options
Diffstat (limited to 'cmds/stagefright/play.cpp')
-rw-r--r-- | cmds/stagefright/play.cpp | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/cmds/stagefright/play.cpp b/cmds/stagefright/play.cpp new file mode 100644 index 0000000..c6e778e --- /dev/null +++ b/cmds/stagefright/play.cpp @@ -0,0 +1,295 @@ +/* + * 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. + */ + +#include <binder/ProcessState.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/TimedEventQueue.h> +#include <media/stagefright/MPEG4Extractor.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MmapSource.h> +#include <media/stagefright/OMXDecoder.h> + +using namespace android; + +struct NewPlayer { + NewPlayer(); + ~NewPlayer(); + + void setSource(const char *uri); + void start(); + void pause(); + void stop(); + +private: + struct PlayerEvent : public TimedEventQueue::Event { + PlayerEvent(NewPlayer *player, + void (NewPlayer::*method)(int64_t realtime_us)) + : mPlayer(player), + mMethod(method) { + } + + virtual void fire(TimedEventQueue *queue, int64_t realtime_us) { + (mPlayer->*mMethod)(realtime_us); + } + + private: + NewPlayer *mPlayer; + void (NewPlayer::*mMethod)(int64_t realtime_us); + + PlayerEvent(const PlayerEvent &); + PlayerEvent &operator=(const PlayerEvent &); + }; + + struct PlayVideoFrameEvent : public TimedEventQueue::Event { + PlayVideoFrameEvent(NewPlayer *player, MediaBuffer *buffer) + : mPlayer(player), + mBuffer(buffer) { + } + + virtual ~PlayVideoFrameEvent() { + if (mBuffer != NULL) { + mBuffer->release(); + mBuffer = NULL; + } + } + + virtual void fire(TimedEventQueue *queue, int64_t realtime_us) { + mPlayer->onPlayVideoFrame(realtime_us, mBuffer); + mBuffer = NULL; + } + + private: + NewPlayer *mPlayer; + MediaBuffer *mBuffer; + + PlayVideoFrameEvent(const PlayVideoFrameEvent &); + PlayVideoFrameEvent &operator=(const PlayVideoFrameEvent &); + }; + + OMXClient mClient; + + MPEG4Extractor *mExtractor; + MediaSource *mAudioSource; + OMXDecoder *mAudioDecoder; + MediaSource *mVideoSource; + OMXDecoder *mVideoDecoder; + + int32_t mVideoWidth, mVideoHeight; + + TimedEventQueue mQueue; + wp<TimedEventQueue::Event> mPlayVideoFrameEvent; + + int64_t mMediaTimeUsStart; + int64_t mRealTimeUsStart; + + void setAudioSource(MediaSource *source); + void setVideoSource(MediaSource *source); + + int64_t approxRealTime(int64_t mediatime_us) const; + + void onStart(int64_t realtime_us); + void onPause(int64_t realtime_us); + void onFetchVideoFrame(int64_t realtime_us); + void onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer); + + static int64_t getMediaBufferTimeUs(MediaBuffer *buffer); + + NewPlayer(const NewPlayer &); + NewPlayer &operator=(const NewPlayer &); +}; + +NewPlayer::NewPlayer() + : mExtractor(NULL), + mAudioSource(NULL), + mAudioDecoder(NULL), + mVideoSource(NULL), + mVideoDecoder(NULL), + mVideoWidth(0), + mVideoHeight(0) { + status_t err = mClient.connect(); + assert(err == OK); +} + +NewPlayer::~NewPlayer() { + stop(); + + mClient.disconnect(); +} + +void NewPlayer::setSource(const char *uri) { + stop(); + + mExtractor = new MPEG4Extractor(new MmapSource(uri)); + + int num_tracks; + status_t err = mExtractor->countTracks(&num_tracks); + assert(err == OK); + + for (int i = 0; i < num_tracks; ++i) { + const sp<MetaData> meta = mExtractor->getTrackMetaData(i); + assert(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; + } + + MediaSource *source; + if (mExtractor->getTrack(i, &source) != OK) { + continue; + } + + if (is_audio) { + setAudioSource(source); + } else { + setVideoSource(source); + } + } +} + +void NewPlayer::setAudioSource(MediaSource *source) { + mAudioSource = source; + + sp<MetaData> meta = source->getFormat(); + + mAudioDecoder = OMXDecoder::Create(&mClient, meta); + mAudioDecoder->setSource(source); +} + +void NewPlayer::setVideoSource(MediaSource *source) { + mVideoSource = source; + + sp<MetaData> meta = source->getFormat(); + + bool success = meta->findInt32(kKeyWidth, &mVideoWidth); + assert(success); + + success = meta->findInt32(kKeyHeight, &mVideoHeight); + assert(success); + + mVideoDecoder = OMXDecoder::Create(&mClient, meta); + mVideoDecoder->setSource(source); +} + +void NewPlayer::start() { + mQueue.start(); + mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onStart)); +} + +void NewPlayer::pause() { + mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onPause)); +} + +void NewPlayer::stop() { + mQueue.stop(); + + delete mVideoDecoder; + mVideoDecoder = NULL; + delete mVideoSource; + mVideoSource = NULL; + mVideoWidth = mVideoHeight = 0; + + delete mAudioDecoder; + mAudioDecoder = NULL; + delete mAudioSource; + mAudioSource = NULL; + + delete mExtractor; + mExtractor = NULL; +} + +int64_t NewPlayer::approxRealTime(int64_t mediatime_us) const { + return mRealTimeUsStart + (mediatime_us - mMediaTimeUsStart); +} + +void NewPlayer::onStart(int64_t realtime_us) { + mRealTimeUsStart = TimedEventQueue::getRealTimeUs(); + + if (mVideoDecoder != NULL) { + mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame)); + } +} + +void NewPlayer::onFetchVideoFrame(int64_t realtime_us) { + MediaBuffer *buffer; + status_t err = mVideoDecoder->read(&buffer); + assert(err == OK); + + int64_t mediatime_us = getMediaBufferTimeUs(buffer); + + sp<TimedEventQueue::Event> event = new PlayVideoFrameEvent(this, buffer); + mPlayVideoFrameEvent = event; + + mQueue.postTimedEvent(event, approxRealTime(mediatime_us)); +} + +// static +int64_t NewPlayer::getMediaBufferTimeUs(MediaBuffer *buffer) { + int32_t units, scale; + bool success = + buffer->meta_data()->findInt32(kKeyTimeUnits, &units); + assert(success); + success = + buffer->meta_data()->findInt32(kKeyTimeScale, &scale); + assert(success); + + return (int64_t)units * 1000000 / scale; +} + +void NewPlayer::onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer) { + LOGI("playing video frame (mediatime: %.2f sec)\n", + getMediaBufferTimeUs(buffer) / 1E6); + fflush(stdout); + + buffer->release(); + buffer = NULL; + + mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame)); +} + +void NewPlayer::onPause(int64_t realtime_us) { +} + +int main(int argc, char **argv) { + android::ProcessState::self()->startThreadPool(); + + if (argc != 2) { + fprintf(stderr, "usage: %s filename\n", argv[0]); + return 1; + } + + NewPlayer player; + player.setSource(argv[1]); + player.start(); + sleep(10); + player.stop(); + + return 0; +} |