blob: c6e778e1361dd3fe6dac3c1083bf47b62a8e6c2a [file] [log] [blame]
/*
* 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;
}