summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/timedtext/TimedTextPlayer.cpp
diff options
context:
space:
mode:
authorGloria Wang <gwang@google.com>2011-05-11 11:24:09 -0700
committerGloria Wang <gwang@google.com>2011-05-25 17:53:29 -0700
commit965d08ba16ee82bc85f69546360c18e7da907406 (patch)
treef581723790736b2ded3529c33d16a7b39701fbbe /media/libstagefright/timedtext/TimedTextPlayer.cpp
parent162f7d15ac5c8c23d1c3de171239f3a4e6e06b2a (diff)
downloadframeworks_av-965d08ba16ee82bc85f69546360c18e7da907406.zip
frameworks_av-965d08ba16ee82bc85f69546360c18e7da907406.tar.gz
frameworks_av-965d08ba16ee82bc85f69546360c18e7da907406.tar.bz2
For out of band timed text support (timed text in a separate file).
Change-Id: I9e024a63eb9bf6f839deee3c7766a66e63126c96
Diffstat (limited to 'media/libstagefright/timedtext/TimedTextPlayer.cpp')
-rw-r--r--media/libstagefright/timedtext/TimedTextPlayer.cpp347
1 files changed, 347 insertions, 0 deletions
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
new file mode 100644
index 0000000..50bb16d
--- /dev/null
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -0,0 +1,347 @@
+ /*
+ * Copyright (C) 2011 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 "TimedTextPlayer"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/Utils.h>
+#include "include/AwesomePlayer.h"
+#include "TimedTextPlayer.h"
+#include "TimedTextParser.h"
+
+namespace android {
+
+struct TimedTextEvent : public TimedEventQueue::Event {
+ TimedTextEvent(
+ TimedTextPlayer *player,
+ void (TimedTextPlayer::*method)())
+ : mPlayer(player),
+ mMethod(method) {
+ }
+
+protected:
+ virtual ~TimedTextEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ (mPlayer->*mMethod)();
+ }
+
+private:
+ TimedTextPlayer *mPlayer;
+ void (TimedTextPlayer::*mMethod)();
+
+ TimedTextEvent(const TimedTextEvent &);
+ TimedTextEvent &operator=(const TimedTextEvent &);
+};
+
+TimedTextPlayer::TimedTextPlayer(
+ AwesomePlayer *observer,
+ const wp<MediaPlayerBase> &listener,
+ TimedEventQueue *queue)
+ : mSource(NULL),
+ mOutOfBandSource(NULL),
+ mSeekTimeUs(0),
+ mStarted(false),
+ mTextEventPending(false),
+ mQueue(queue),
+ mListener(listener),
+ mObserver(observer),
+ mTextBuffer(NULL),
+ mTextParser(NULL),
+ mTextType(kNoText) {
+ mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent);
+}
+
+TimedTextPlayer::~TimedTextPlayer() {
+ if (mStarted) {
+ reset();
+ }
+
+ mTextTrackVector.clear();
+ mTextOutOfBandVector.clear();
+}
+
+status_t TimedTextPlayer::start(uint8_t index) {
+ CHECK(!mStarted);
+
+ if (index >=
+ mTextTrackVector.size() + mTextOutOfBandVector.size()) {
+ LOGE("Incorrect text track index: %d", index);
+ return BAD_VALUE;
+ }
+
+ if (index < mTextTrackVector.size()) { // start an in-band text
+ mSource = mTextTrackVector.itemAt(index);
+
+ status_t err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+ mTextType = kInBandText;
+ } else { // start an out-of-band text
+ OutOfBandText text =
+ mTextOutOfBandVector.itemAt(index - mTextTrackVector.size());
+
+ mOutOfBandSource = text.source;
+ TimedTextParser::FileType fileType = text.type;
+
+ if (mTextParser == NULL) {
+ mTextParser = new TimedTextParser();
+ }
+
+ status_t err;
+ if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) {
+ return err;
+ }
+ mTextType = kOutOfBandText;
+ }
+
+ int64_t positionUs;
+ mObserver->getPosition(&positionUs);
+ seekTo(positionUs);
+
+ postTextEvent();
+
+ mStarted = true;
+
+ return OK;
+}
+
+void TimedTextPlayer::pause() {
+ CHECK(mStarted);
+
+ cancelTextEvent();
+}
+
+void TimedTextPlayer::resume() {
+ CHECK(mStarted);
+
+ postTextEvent();
+}
+
+void TimedTextPlayer::reset() {
+ CHECK(mStarted);
+
+ // send an empty text to clear the screen
+ notifyListener(MEDIA_TIMED_TEXT);
+
+ cancelTextEvent();
+
+ mSeeking = false;
+ mStarted = false;
+
+ if (mTextType == kInBandText) {
+ if (mTextBuffer != NULL) {
+ mTextBuffer->release();
+ mTextBuffer = NULL;
+ }
+
+ if (mSource != NULL) {
+ mSource->stop();
+ mSource.clear();
+ mSource = NULL;
+ }
+ } else {
+ if (mTextParser != NULL) {
+ mTextParser.clear();
+ mTextParser = NULL;
+ }
+ if (mOutOfBandSource != NULL) {
+ mOutOfBandSource.clear();
+ mOutOfBandSource = NULL;
+ }
+ }
+}
+
+status_t TimedTextPlayer::seekTo(int64_t time_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSeeking = true;
+ mSeekTimeUs = time_us;
+
+ postTextEvent();
+
+ return OK;
+}
+
+status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) {
+ if (index >=
+ (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) {
+ return BAD_VALUE;
+ }
+
+ if (mStarted) {
+ reset();
+ }
+
+ if (index >= 0) {
+ return start(index);
+ }
+ return OK;
+}
+
+void TimedTextPlayer::onTextEvent() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (!mTextEventPending) {
+ return;
+ }
+ mTextEventPending = false;
+
+ MediaSource::ReadOptions options;
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs,
+ MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC);
+ mSeeking = false;
+
+ if (mTextType == kInBandText) {
+ if (mTextBuffer != NULL) {
+ mTextBuffer->release();
+ mTextBuffer = NULL;
+ }
+ } else {
+ mText.clear();
+ }
+
+ notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen
+ }
+
+ int64_t positionUs, timeUs;
+ mObserver->getPosition(&positionUs);
+
+ if (mTextType == kInBandText) {
+ if (mTextBuffer != NULL) {
+ uint8_t *tmp = (uint8_t *)(mTextBuffer->data());
+ size_t len = (*tmp) << 8 | (*(tmp + 1));
+
+ notifyListener(MEDIA_TIMED_TEXT,
+ tmp + 2,
+ len);
+
+ mTextBuffer->release();
+ mTextBuffer = NULL;
+
+ }
+
+ if (mSource->read(&mTextBuffer, &options) != OK) {
+ return;
+ }
+
+ mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs);
+ } else {
+ if (mText.size() > 0) {
+ notifyListener(MEDIA_TIMED_TEXT,
+ mText.c_str(),
+ mText.size());
+ mText.clear();
+ }
+
+ int64_t endTimeUs;
+ if (mTextParser->getText(
+ &mText, &timeUs, &endTimeUs, &options) != OK) {
+ return;
+ }
+ }
+
+ //send the text now
+ if (timeUs <= positionUs + 100000ll) {
+ postTextEvent();
+ } else {
+ postTextEvent(timeUs - positionUs - 100000ll);
+ }
+}
+
+void TimedTextPlayer::postTextEvent(int64_t delayUs) {
+ if (mTextEventPending) {
+ return;
+ }
+
+ mTextEventPending = true;
+ mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void TimedTextPlayer::cancelTextEvent() {
+ mQueue->cancelEvent(mTextEvent->eventID());
+ mTextEventPending = false;
+}
+
+void TimedTextPlayer::addTextSource(sp<MediaSource> source) {
+ Mutex::Autolock autoLock(mLock);
+ mTextTrackVector.add(source);
+}
+
+status_t TimedTextPlayer::setParameter(int key, const Parcel &request) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) {
+ String8 uri = request.readString8();
+ KeyedVector<String8, String8> headers;
+
+ // To support local subtitle file only for now
+ if (strncasecmp("file://", uri.string(), 7)) {
+ return INVALID_OPERATION;
+ }
+ sp<DataSource> dataSource =
+ DataSource::CreateFromURI(uri, &headers);
+ status_t err = dataSource->initCheck();
+
+ if (err != OK) {
+ return err;
+ }
+
+ OutOfBandText text;
+ text.source = dataSource;
+ if (uri.getPathExtension() == String8(".srt")) {
+ text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT;
+ } else {
+ return ERROR_UNSUPPORTED;
+ }
+
+ mTextOutOfBandVector.add(text);
+
+ return OK;
+ }
+ return INVALID_OPERATION;
+}
+
+void TimedTextPlayer::notifyListener(
+ int msg, const void *data, size_t size) {
+ if (mListener != NULL) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+
+ if (listener != NULL) {
+ if (size > 0) {
+ mData.freeData();
+ mData.write(data, size);
+
+ listener->sendEvent(msg, 0, 0, &mData);
+ } else { // send an empty timed text to clear the screen
+ listener->sendEvent(msg);
+ }
+ }
+ }
+}
+}