diff options
Diffstat (limited to 'media/libstagefright')
-rw-r--r-- | media/libstagefright/AwesomePlayer.cpp | 34 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/include/AwesomePlayer.h | 5 | ||||
-rw-r--r-- | media/libstagefright/timedtext/Android.mk | 5 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextDriver.cpp | 223 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextDriver.h | 81 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextInBandSource.cpp | 118 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextInBandSource.h | 55 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextParser.h | 75 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextPlayer.cpp | 467 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextPlayer.h | 100 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextSRTSource.cpp (renamed from media/libstagefright/timedtext/TimedTextParser.cpp) | 235 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextSRTSource.h | 75 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextSource.cpp | 53 | ||||
-rw-r--r-- | media/libstagefright/timedtext/TimedTextSource.h | 61 |
15 files changed, 960 insertions, 629 deletions
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 8480b6d..8073af8 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -30,7 +30,7 @@ #include "include/MPEG2TSExtractor.h" #include "include/WVMExtractor.h" -#include "timedtext/TimedTextPlayer.h" +#include "timedtext/TimedTextDriver.h" #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -192,7 +192,7 @@ AwesomePlayer::AwesomePlayer() mVideoBuffer(NULL), mDecryptHandle(NULL), mLastVideoTimeUs(-1), - mTextPlayer(NULL) { + mTextDriver(NULL) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -530,9 +530,9 @@ void AwesomePlayer::reset_l() { delete mAudioPlayer; mAudioPlayer = NULL; - if (mTextPlayer != NULL) { - delete mTextPlayer; - mTextPlayer = NULL; + if (mTextDriver != NULL) { + delete mTextDriver; + mTextDriver = NULL; } mVideoRenderer.clear(); @@ -1118,7 +1118,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) { } if (mFlags & TEXTPLAYER_STARTED) { - mTextPlayer->pause(); + mTextDriver->pause(); modifyFlags(TEXT_RUNNING, CLEAR); } @@ -1272,9 +1272,9 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { } status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) { - if (mTextPlayer != NULL) { + if (mTextDriver != NULL) { if (index >= 0) { // to turn on a text track - status_t err = mTextPlayer->setTimedTextTrackIndex(index); + status_t err = mTextDriver->setTimedTextTrackIndex(index); if (err != OK) { return err; } @@ -1290,7 +1290,7 @@ status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) { modifyFlags(TEXTPLAYER_STARTED, CLEAR); } - return mTextPlayer->setTimedTextTrackIndex(index); + return mTextDriver->setTimedTextTrackIndex(index); } } else { return INVALID_OPERATION; @@ -1319,7 +1319,7 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { seekAudioIfNecessary_l(); if (mFlags & TEXTPLAYER_STARTED) { - mTextPlayer->seekTo(mSeekTimeUs); + mTextDriver->seekToAsync(mSeekTimeUs); } if (!(mFlags & PLAYING)) { @@ -1364,11 +1364,11 @@ void AwesomePlayer::addTextSource(sp<MediaSource> source) { Mutex::Autolock autoLock(mTimedTextLock); CHECK(source != NULL); - if (mTextPlayer == NULL) { - mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue); + if (mTextDriver == NULL) { + mTextDriver = new TimedTextDriver(mListener); } - mTextPlayer->addTextSource(source); + mTextDriver->addInBandTextSource(source); } status_t AwesomePlayer::initAudioDecoder() { @@ -1695,7 +1695,7 @@ void AwesomePlayer::onVideoEvent() { } if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) { - mTextPlayer->resume(); + mTextDriver->resume(); modifyFlags(TEXT_RUNNING, SET); } @@ -2241,11 +2241,11 @@ status_t AwesomePlayer::setParameter(int key, const Parcel &request) { case KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE: { Mutex::Autolock autoLock(mTimedTextLock); - if (mTextPlayer == NULL) { - mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue); + if (mTextDriver == NULL) { + mTextDriver = new TimedTextDriver(mListener); } - return mTextPlayer->setParameter(key, request); + return mTextDriver->addOutOfBandTextSource(request); } case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS: { diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index bc88015..6c95d4e 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -20,7 +20,6 @@ #include "include/MPEG4Extractor.h" #include "include/SampleTable.h" #include "include/ESDS.h" -#include "timedtext/TimedTextPlayer.h" #include <arpa/inet.h> @@ -2430,4 +2429,3 @@ bool SniffMPEG4( } } // namespace android - diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 82c6476..a7a3d47 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -41,7 +41,7 @@ struct ISurfaceTexture; class DrmManagerClinet; class DecryptHandle; -class TimedTextPlayer; +class TimedTextDriver; struct WVMExtractor; struct AwesomeRenderer : public RefBase { @@ -232,7 +232,7 @@ private: sp<DecryptHandle> mDecryptHandle; int64_t mLastVideoTimeUs; - TimedTextPlayer *mTextPlayer; + TimedTextDriver *mTextDriver; mutable Mutex mTimedTextLock; sp<WVMExtractor> mWVMExtractor; @@ -326,4 +326,3 @@ private: } // namespace android #endif // AWESOME_PLAYER_H_ - diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk index 59d0e15..8b23dee 100644 --- a/media/libstagefright/timedtext/Android.mk +++ b/media/libstagefright/timedtext/Android.mk @@ -3,7 +3,10 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ TextDescriptions.cpp \ - TimedTextParser.cpp \ + TimedTextDriver.cpp \ + TimedTextInBandSource.cpp \ + TimedTextSource.cpp \ + TimedTextSRTSource.cpp \ TimedTextPlayer.cpp LOCAL_CFLAGS += -Wno-multichar diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp new file mode 100644 index 0000000..9ec9415 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextDriver.cpp @@ -0,0 +1,223 @@ + /* + * Copyright (C) 2012 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 "TimedTextDriver" +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> + +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/Utils.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> + +#include "TimedTextDriver.h" + +#include "TextDescriptions.h" +#include "TimedTextPlayer.h" +#include "TimedTextSource.h" + +namespace android { + +TimedTextDriver::TimedTextDriver( + const wp<MediaPlayerBase> &listener) + : mLooper(new ALooper), + mListener(listener), + mState(UNINITIALIZED) { + mLooper->setName("TimedTextDriver"); + mLooper->start(); + mPlayer = new TimedTextPlayer(listener); + mLooper->registerHandler(mPlayer); +} + +TimedTextDriver::~TimedTextDriver() { + mTextInBandVector.clear(); + mTextOutOfBandVector.clear(); + mLooper->stop(); +} + +status_t TimedTextDriver::setTimedTextTrackIndex_l(int32_t index) { + if (index >= + (int)(mTextInBandVector.size() + mTextOutOfBandVector.size())) { + return BAD_VALUE; + } + + sp<TimedTextSource> source; + if (index < mTextInBandVector.size()) { + source = mTextInBandVector.itemAt(index); + } else { + source = mTextOutOfBandVector.itemAt(index - mTextInBandVector.size()); + } + mPlayer->setDataSource(source); + return OK; +} + +status_t TimedTextDriver::start() { + Mutex::Autolock autoLock(mLock); + switch (mState) { + case UNINITIALIZED: + return INVALID_OPERATION; + case STOPPED: + mPlayer->start(); + break; + case PLAYING: + return OK; + case PAUSED: + mPlayer->resume(); + break; + default: + TRESPASS(); + } + mState = PLAYING; + return OK; +} + +status_t TimedTextDriver::stop() { + return pause(); +} + +// TODO: Test if pause() works properly. +// Scenario 1: start - pause - resume +// Scenario 2: start - seek +// Scenario 3: start - pause - seek - resume +status_t TimedTextDriver::pause() { + Mutex::Autolock autoLock(mLock); + switch (mState) { + case UNINITIALIZED: + return INVALID_OPERATION; + case STOPPED: + return OK; + case PLAYING: + mPlayer->pause(); + break; + case PAUSED: + return OK; + default: + TRESPASS(); + } + mState = PAUSED; + return OK; +} + +status_t TimedTextDriver::resume() { + return start(); +} + +status_t TimedTextDriver::seekToAsync(int64_t timeUs) { + mPlayer->seekToAsync(timeUs); + return OK; +} + +status_t TimedTextDriver::setTimedTextTrackIndex(int32_t index) { + // TODO: This is current implementation for MediaPlayer::disableTimedText(). + // Find better way for readability. + if (index < 0) { + mPlayer->pause(); + return OK; + } + + status_t ret = OK; + Mutex::Autolock autoLock(mLock); + switch (mState) { + case UNINITIALIZED: + ret = INVALID_OPERATION; + break; + case PAUSED: + ret = setTimedTextTrackIndex_l(index); + break; + case PLAYING: + mPlayer->pause(); + ret = setTimedTextTrackIndex_l(index); + if (ret != OK) { + break; + } + mPlayer->start(); + break; + case STOPPED: + // TODO: The only difference between STOPPED and PAUSED is this + // part. Revise the flow from "MediaPlayer::enableTimedText()" and + // remove one of the status, PAUSED and STOPPED, if possible. + ret = setTimedTextTrackIndex_l(index); + if (ret != OK) { + break; + } + mPlayer->start(); + break; + defaut: + TRESPASS(); + } + return ret; +} + +status_t TimedTextDriver::addInBandTextSource( + const sp<MediaSource>& mediaSource) { + sp<TimedTextSource> source = + TimedTextSource::CreateTimedTextSource(mediaSource); + if (source == NULL) { + return ERROR_UNSUPPORTED; + } + Mutex::Autolock autoLock(mLock); + mTextInBandVector.add(source); + if (mState == UNINITIALIZED) { + mState = STOPPED; + } + return OK; +} + +status_t TimedTextDriver::addOutOfBandTextSource( + const Parcel &request) { + // TODO: Define "TimedTextSource::CreateFromURI(uri)" + // and move below lines there..? + + // String values written in Parcel are UTF-16 values. + const String16 uri16 = request.readString16(); + String8 uri = String8(request.readString16()); + + uri.toLower(); + // To support local subtitle file only for now + if (strncasecmp("file://", uri.string(), 7)) { + return ERROR_UNSUPPORTED; + } + sp<DataSource> dataSource = + DataSource::CreateFromURI(uri); + if (dataSource == NULL) { + return ERROR_UNSUPPORTED; + } + + sp<TimedTextSource> source; + if (uri.getPathExtension() == String8(".srt")) { + source = TimedTextSource::CreateTimedTextSource( + dataSource, TimedTextSource::OUT_OF_BAND_FILE_SRT); + } + + if (source == NULL) { + return ERROR_UNSUPPORTED; + } + + Mutex::Autolock autoLock(mLock); + + mTextOutOfBandVector.add(source); + if (mState == UNINITIALIZED) { + mState = STOPPED; + } + return OK; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextDriver.h b/media/libstagefright/timedtext/TimedTextDriver.h new file mode 100644 index 0000000..efedb6e --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextDriver.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 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 TIMED_TEXT_DRIVER_H_ +#define TIMED_TEXT_DRIVER_H_ + +#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro +#include <utils/Errors.h> // for status_t +#include <utils/RefBase.h> +#include <utils/threads.h> + +namespace android { + +class ALooper; +class MediaPlayerBase; +class MediaSource; +class Parcel; +class TimedTextPlayer; +class TimedTextSource; + +class TimedTextDriver { +public: + TimedTextDriver(const wp<MediaPlayerBase> &listener); + + ~TimedTextDriver(); + + // TODO: pause-resume pair seems equivalent to stop-start pair. + // Check if it is replaceable with stop-start. + status_t start(); + status_t stop(); + status_t pause(); + status_t resume(); + + status_t seekToAsync(int64_t timeUs); + + status_t addInBandTextSource(const sp<MediaSource>& source); + status_t addOutOfBandTextSource(const Parcel &request); + + status_t setTimedTextTrackIndex(int32_t index); + +private: + Mutex mLock; + + enum State { + UNINITIALIZED, + STOPPED, + PLAYING, + PAUSED, + }; + + sp<ALooper> mLooper; + sp<TimedTextPlayer> mPlayer; + wp<MediaPlayerBase> mListener; + + // Variables to be guarded by mLock. + State mState; + Vector<sp<TimedTextSource> > mTextInBandVector; + Vector<sp<TimedTextSource> > mTextOutOfBandVector; + // -- End of variables to be guarded by mLock + + status_t setTimedTextTrackIndex_l(int32_t index); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver); +}; + +} // namespace android + +#endif // TIMED_TEXT_DRIVER_H_ diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.cpp b/media/libstagefright/timedtext/TimedTextInBandSource.cpp new file mode 100644 index 0000000..f2c4d54 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextInBandSource.cpp @@ -0,0 +1,118 @@ + /* + * Copyright (C) 2012 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 "TimedTextInBandSource" +#include <utils/Log.h> + +#include <binder/Parcel.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDebug.h> // CHECK_XX macro +#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> + +#include "TimedTextInBandSource.h" +#include "TextDescriptions.h" + +namespace android { + +TimedTextInBandSource::TimedTextInBandSource(const sp<MediaSource>& mediaSource) + : mSource(mediaSource) { +} + +TimedTextInBandSource::~TimedTextInBandSource() { +} + +status_t TimedTextInBandSource::read( + int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) { + MediaBuffer *textBuffer = NULL; + status_t err = mSource->read(&textBuffer, options); + if (err != OK) { + return err; + } + CHECK(textBuffer != NULL); + textBuffer->meta_data()->findInt64(kKeyTime, timeUs); + // TODO: this is legacy code. when 'timeUs' can be <= 0? + if (*timeUs > 0) { + extractAndAppendLocalDescriptions(*timeUs, textBuffer, parcel); + } + textBuffer->release(); + return OK; +} + +// Each text sample consists of a string of text, optionally with sample +// modifier description. The modifier description could specify a new +// text style for the string of text. These descriptions are present only +// if they are needed. This method is used to extract the modifier +// description and append it at the end of the text. +status_t TimedTextInBandSource::extractAndAppendLocalDescriptions( + int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) { + const void *data; + size_t size = 0; + int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS; + + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) { + data = textBuffer->data(); + size = textBuffer->size(); + + if (size > 0) { + parcel->freeData(); + flag |= TextDescriptions::IN_BAND_TEXT_3GPP; + return TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, timeUs / 1000, parcel); + } + return OK; + } + return ERROR_UNSUPPORTED; +} + +// To extract and send the global text descriptions for all the text samples +// in the text track or text file. +// TODO: send error message to application via notifyListener()...? +status_t TimedTextInBandSource::extractGlobalDescriptions(Parcel *parcel) { + const void *data; + size_t size = 0; + int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS; + + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + // support 3GPP only for now + if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) { + uint32_t type; + // get the 'tx3g' box content. This box contains the text descriptions + // used to render the text track + if (!mSource->getFormat()->findData( + kKeyTextFormatData, &type, &data, &size)) { + return ERROR_MALFORMED; + } + + if (size > 0) { + flag |= TextDescriptions::IN_BAND_TEXT_3GPP; + return TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, 0, parcel); + } + return OK; + } + return ERROR_UNSUPPORTED; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.h b/media/libstagefright/timedtext/TimedTextInBandSource.h new file mode 100644 index 0000000..26e5737 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextInBandSource.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 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 TIMED_TEXT_IN_BAND_SOURCE_H_ +#define TIMED_TEXT_IN_BAND_SOURCE_H_ + +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> + +#include "TimedTextSource.h" + +namespace android { + +class MediaBuffer; +class Parcel; + +class TimedTextInBandSource : public TimedTextSource { + public: + TimedTextInBandSource(const sp<MediaSource>& mediaSource); + virtual status_t start() { return mSource->start(); } + virtual status_t stop() { return mSource->stop(); } + virtual status_t read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options = NULL); + virtual status_t extractGlobalDescriptions(Parcel *parcel); + + protected: + virtual ~TimedTextInBandSource(); + + private: + sp<MediaSource> mSource; + + status_t extractAndAppendLocalDescriptions( + int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextInBandSource); +}; + +} // namespace android + +#endif // TIMED_TEXT_IN_BAND_SOURCE_H_ diff --git a/media/libstagefright/timedtext/TimedTextParser.h b/media/libstagefright/timedtext/TimedTextParser.h deleted file mode 100644 index 44774c2..0000000 --- a/media/libstagefright/timedtext/TimedTextParser.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ - -#ifndef TIMED_TEXT_PARSER_H_ - -#define TIMED_TEXT_PARSER_H_ - -#include <media/MediaPlayerInterface.h> -#include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/foundation/AString.h> -#include <media/stagefright/MediaSource.h> - -namespace android { - -class DataSource; - -class TimedTextParser : public RefBase { -public: - TimedTextParser(); - virtual ~TimedTextParser(); - - enum FileType { - OUT_OF_BAND_FILE_SRT = 1, - }; - - status_t getText(AString *text, int64_t *startTimeUs, int64_t *endTimeUs, - const MediaSource::ReadOptions *options = NULL); - status_t init(const sp<DataSource> &dataSource, FileType fileType); - void reset(); - -private: - Mutex mLock; - - sp<DataSource> mDataSource; - off64_t mOffset; - - struct TextInfo { - int64_t endTimeUs; - // the offset of the text in the original file - off64_t offset; - int textLen; - }; - - int mIndex; - FileType mFileType; - - // the key indicated the start time of the text - KeyedVector<int64_t, TextInfo> mTextVector; - - status_t getNextInSrtFileFormat( - off64_t *offset, int64_t *startTimeUs, TextInfo *info); - status_t readNextLine(off64_t *offset, AString *data); - - status_t scanFile(); - - DISALLOW_EVIL_CONSTRUCTORS(TimedTextParser); -}; - -} // namespace android - -#endif // TIMED_TEXT_PARSER_H_ - diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp index 3014b0b..8c2df88 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.cpp +++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2012 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. @@ -18,399 +18,164 @@ #define LOG_TAG "TimedTextPlayer" #include <utils/Log.h> -#include <binder/IPCThreadState.h> - +#include <media/stagefright/foundation/AMessage.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 <media/MediaPlayerInterface.h> -#include "include/AwesomePlayer.h" #include "TimedTextPlayer.h" -#include "TimedTextParser.h" -#include "TextDescriptions.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)(); - } +#include "TimedTextDriver.h" +#include "TimedTextSource.h" -private: - TimedTextPlayer *mPlayer; - void (TimedTextPlayer::*mMethod)(); +namespace android { - TimedTextEvent(const TimedTextEvent &); - TimedTextEvent &operator=(const TimedTextEvent &); -}; +static const int64_t kAdjustmentProcessingTimeUs = 100000ll; -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(const wp<MediaPlayerBase> &listener) + : mListener(listener), + mSource(NULL), + mSendSubtitleGeneration(0) { } TimedTextPlayer::~TimedTextPlayer() { - if (mStarted) { - reset(); + if (mSource != NULL) { + mSource->stop(); + mSource.clear(); + mSource = NULL; } - - mTextTrackVector.clear(); - mTextOutOfBandVector.clear(); } -status_t TimedTextPlayer::start(uint8_t index) { - CHECK(!mStarted); - - if (index >= - mTextTrackVector.size() + mTextOutOfBandVector.size()) { - ALOGE("Incorrect text track index: %d", index); - return BAD_VALUE; - } - - status_t err; - if (index < mTextTrackVector.size()) { // start an in-band text - mSource = mTextTrackVector.itemAt(index); - - 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(); - } - - if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) { - return err; - } - mTextType = kOutOfBandText; - } - - // send sample description format - if ((err = extractAndSendGlobalDescriptions()) != OK) { - return err; - } - - int64_t positionUs; - mObserver->getPosition(&positionUs); - seekTo(positionUs); - - postTextEvent(); - - mStarted = true; - - return OK; +void TimedTextPlayer::start() { + sp<AMessage> msg = new AMessage(kWhatSeek, id()); + msg->setInt64("seekTimeUs", -1); + msg->post(); } void TimedTextPlayer::pause() { - CHECK(mStarted); - - cancelTextEvent(); + (new AMessage(kWhatPause, id()))->post(); } 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; - } - } + start(); } -status_t TimedTextPlayer::seekTo(int64_t time_us) { - Mutex::Autolock autoLock(mLock); - - mSeeking = true; - mSeekTimeUs = time_us; - - postTextEvent(); - - return OK; +void TimedTextPlayer::seekToAsync(int64_t timeUs) { + sp<AMessage> msg = new AMessage(kWhatSeek, id()); + msg->setInt64("seekTimeUs", timeUs); + msg->post(); } -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::setDataSource(sp<TimedTextSource> source) { + sp<AMessage> msg = new AMessage(kWhatSetSource, id()); + msg->setObject("source", source); + msg->post(); } -void TimedTextPlayer::onTextEvent() { - Mutex::Autolock autoLock(mLock); - - if (!mTextEventPending) { - return; - } - mTextEventPending = false; - - if (mData.dataSize() > 0) { - notifyListener(MEDIA_TIMED_TEXT, &mData); - mData.freeData(); - } - - MediaSource::ReadOptions options; - if (mSeeking) { - options.setSeekTo(mSeekTimeUs, - MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); - mSeeking = false; - - notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen - } - - int64_t positionUs, timeUs; - mObserver->getPosition(&positionUs); - - if (mTextType == kInBandText) { - if (mSource->read(&mTextBuffer, &options) != OK) { - return; +void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatPause: { + mSendSubtitleGeneration++; + break; } - - mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs); - } else { - int64_t endTimeUs; - if (mTextParser->getText( - &mText, &timeUs, &endTimeUs, &options) != OK) { - return; - } - } - - if (timeUs > 0) { - extractAndAppendLocalDescriptions(timeUs); - } - - if (mTextType == kInBandText) { - if (mTextBuffer != NULL) { - mTextBuffer->release(); - mTextBuffer = NULL; + case kWhatSeek: { + int64_t seekTimeUs = 0; + msg->findInt64("seekTimeUs", &seekTimeUs); + if (seekTimeUs < 0) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { + int32_t positionMs = 0; + listener->getCurrentPosition(&positionMs); + seekTimeUs = positionMs * 1000ll; + } + } + doSeekAndRead(seekTimeUs); + break; + } + case kWhatSendSubtitle: { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mSendSubtitleGeneration) { + // Drop obsolete msg. + break; + } + sp<RefBase> obj; + msg->findObject("subtitle", &obj); + if (obj != NULL) { + sp<ParcelEvent> parcelEvent; + parcelEvent = static_cast<ParcelEvent*>(obj.get()); + notifyListener(MEDIA_TIMED_TEXT, &(parcelEvent->parcel)); + } else { + notifyListener(MEDIA_TIMED_TEXT); + } + doRead(); + break; + } + case kWhatSetSource: { + sp<RefBase> obj; + msg->findObject("source", &obj); + if (obj == NULL) break; + if (mSource != NULL) { + mSource->stop(); + } + mSource = static_cast<TimedTextSource*>(obj.get()); + mSource->start(); + Parcel parcel; + if (mSource->extractGlobalDescriptions(&parcel) == OK && + parcel.dataSize() > 0) { + notifyListener(MEDIA_TIMED_TEXT, &parcel); + } else { + notifyListener(MEDIA_TIMED_TEXT); + } + break; } - } else { - mText.clear(); - } - - //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::doSeekAndRead(int64_t seekTimeUs) { + MediaSource::ReadOptions options; + options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); + doRead(&options); } -void TimedTextPlayer::addTextSource(sp<MediaSource> source) { - Mutex::Autolock autoLock(mLock); - mTextTrackVector.add(source); +void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { + int64_t timeUs = 0; + sp<ParcelEvent> parcelEvent = new ParcelEvent(); + mSource->read(&timeUs, &(parcelEvent->parcel), options); + postTextEvent(parcelEvent, timeUs); } -status_t TimedTextPlayer::setParameter(int key, const Parcel &request) { - Mutex::Autolock autoLock(mLock); - - if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) { - const String16 uri16 = request.readString16(); - String8 uri = String8(uri16); - 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(); +void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { + int64_t positionUs, delayUs; + int32_t positionMs = 0; + listener->getCurrentPosition(&positionMs); + positionUs = positionMs * 1000; - if (err != OK) { - return err; - } - - OutOfBandText text; - text.source = dataSource; - if (uri.getPathExtension() == String8(".srt")) { - text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT; + if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) { + delayUs = 0; } else { - return ERROR_UNSUPPORTED; + delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs; } - - mTextOutOfBandVector.add(text); - - return OK; - } - return INVALID_OPERATION; -} - -void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) { - if (mListener != NULL) { - sp<MediaPlayerBase> listener = mListener.promote(); - - if (listener != NULL) { - if (parcel && (parcel->dataSize() > 0)) { - listener->sendEvent(msg, 0, 0, parcel); - } else { // send an empty timed text to clear the screen - listener->sendEvent(msg); - } + sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); + msg->setInt32("generation", mSendSubtitleGeneration); + if (parcel != NULL) { + msg->setObject("subtitle", parcel); } + msg->post(delayUs); } } -// Each text sample consists of a string of text, optionally with sample -// modifier description. The modifier description could specify a new -// text style for the string of text. These descriptions are present only -// if they are needed. This method is used to extract the modifier -// description and append it at the end of the text. -status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) { - const void *data; - size_t size = 0; - int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS; - - if (mTextType == kInBandText) { - const char *mime; - CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); - - if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { - flag |= TextDescriptions::IN_BAND_TEXT_3GPP; - data = mTextBuffer->data(); - size = mTextBuffer->size(); - } else { - // support 3GPP only for now - return ERROR_UNSUPPORTED; +void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener != NULL) { + if (parcel != NULL && (parcel->dataSize() > 0)) { + listener->sendEvent(msg, 0, 0, parcel); + } else { // send an empty timed text to clear the screen + listener->sendEvent(msg); } - } else { - data = mText.c_str(); - size = mText.size(); - flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT; } - - if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) { - mData.freeData(); - return TextDescriptions::getParcelOfDescriptions( - (const uint8_t *)data, size, flag, timeUs / 1000, &mData); - } - - return OK; } -// To extract and send the global text descriptions for all the text samples -// in the text track or text file. -status_t TimedTextPlayer::extractAndSendGlobalDescriptions() { - const void *data; - size_t size = 0; - int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS; - - if (mTextType == kInBandText) { - const char *mime; - CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); - - // support 3GPP only for now - if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { - uint32_t type; - // get the 'tx3g' box content. This box contains the text descriptions - // used to render the text track - if (!mSource->getFormat()->findData( - kKeyTextFormatData, &type, &data, &size)) { - return ERROR_MALFORMED; - } - - flag |= TextDescriptions::IN_BAND_TEXT_3GPP; - } - } - - if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) { - Parcel parcel; - if (TextDescriptions::getParcelOfDescriptions( - (const uint8_t *)data, size, flag, 0, &parcel) == OK) { - if (parcel.dataSize() > 0) { - notifyListener(MEDIA_TIMED_TEXT, &parcel); - } - } - } - - return OK; -} -} +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextPlayer.h b/media/libstagefright/timedtext/TimedTextPlayer.h index a744db5..837beeb 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.h +++ b/media/libstagefright/timedtext/TimedTextPlayer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2012 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. @@ -15,99 +15,61 @@ */ #ifndef TIMEDTEXT_PLAYER_H_ - #define TIMEDTEXT_PLAYER_H_ -#include <media/MediaPlayerInterface.h> +#include <binder/Parcel.h> #include <media/stagefright/foundation/ABase.h> -#include <media/stagefright/foundation/AString.h> +#include <media/stagefright/foundation/AHandler.h> +#include <media/stagefright/MediaSource.h> +#include <utils/RefBase.h> -#include "include/TimedEventQueue.h" -#include "TimedTextParser.h" +#include "TimedTextSource.h" namespace android { -class MediaSource; -class AwesomePlayer; -class MediaBuffer; +class AMessage; +class MediaPlayerBase; +class TimedTextDriver; +class TimedTextSource; -class TimedTextPlayer { +class TimedTextPlayer : public AHandler { public: - TimedTextPlayer(AwesomePlayer *observer, - const wp<MediaPlayerBase> &listener, - TimedEventQueue *queue); + TimedTextPlayer(const wp<MediaPlayerBase> &listener); virtual ~TimedTextPlayer(); - // index: the index of the text track which will - // be turned on - status_t start(uint8_t index); - + void start(); void pause(); - void resume(); + void seekToAsync(int64_t timeUs); + void setDataSource(sp<TimedTextSource> source); - status_t seekTo(int64_t time_us); - - void addTextSource(sp<MediaSource> source); - - status_t setTimedTextTrackIndex(int32_t index); - status_t setParameter(int key, const Parcel &request); +protected: + virtual void onMessageReceived(const sp<AMessage> &msg); private: - enum TextType { - kNoText = 0, - kInBandText = 1, - kOutOfBandText = 2, + enum { + kWhatPause = 'paus', + kWhatSeek = 'seek', + kWhatSendSubtitle = 'send', + kWhatSetSource = 'ssrc', }; - Mutex mLock; - - sp<MediaSource> mSource; - sp<DataSource> mOutOfBandSource; - - bool mSeeking; - int64_t mSeekTimeUs; - - bool mStarted; - - sp<TimedEventQueue::Event> mTextEvent; - bool mTextEventPending; - - TimedEventQueue *mQueue; - - wp<MediaPlayerBase> mListener; - AwesomePlayer *mObserver; - - MediaBuffer *mTextBuffer; - Parcel mData; - - // for in-band timed text - Vector<sp<MediaSource> > mTextTrackVector; - - // for out-of-band timed text - struct OutOfBandText { - TimedTextParser::FileType type; - sp<DataSource> source; + // To add Parcel into an AMessage as an object, it should be 'RefBase'. + struct ParcelEvent : public RefBase { + Parcel parcel; }; - Vector<OutOfBandText > mTextOutOfBandVector; - sp<TimedTextParser> mTextParser; - AString mText; - - TextType mTextType; - - void reset(); + wp<MediaPlayerBase> mListener; + sp<TimedTextSource> mSource; + int32_t mSendSubtitleGeneration; + void doSeekAndRead(int64_t seekTimeUs); + void doRead(MediaSource::ReadOptions* options = NULL); void onTextEvent(); - void postTextEvent(int64_t delayUs = -1); - void cancelTextEvent(); - + void postTextEvent(const sp<ParcelEvent>& parcel = NULL, int64_t timeUs = -1); void notifyListener(int msg, const Parcel *parcel = NULL); - status_t extractAndAppendLocalDescriptions(int64_t timeUs); - status_t extractAndSendGlobalDescriptions(); - DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer); }; diff --git a/media/libstagefright/timedtext/TimedTextParser.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp index caea0a4..3752d34 100644 --- a/media/libstagefright/timedtext/TimedTextParser.cpp +++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2011 The Android Open Source Project + /* + * Copyright (C) 2012 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. @@ -14,133 +14,106 @@ * limitations under the License. */ -#include "TimedTextParser.h" +//#define LOG_NDEBUG 0 +#define LOG_TAG "TimedTextSRTSource" +#include <utils/Log.h> + +#include <binder/Parcel.h> +#include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> + +#include "TimedTextSRTSource.h" +#include "TextDescriptions.h" namespace android { -TimedTextParser::TimedTextParser() - : mDataSource(NULL), - mOffset(0), - mIndex(0) { +TimedTextSRTSource::TimedTextSRTSource(const sp<DataSource>& dataSource) + : mSource(dataSource), + mIndex(0) { } -TimedTextParser::~TimedTextParser() { - reset(); +TimedTextSRTSource::~TimedTextSRTSource() { } -status_t TimedTextParser::init( - const sp<DataSource> &dataSource, FileType fileType) { - mDataSource = dataSource; - mFileType = fileType; - - status_t err; - if ((err = scanFile()) != OK) { +status_t TimedTextSRTSource::start() { + status_t err = scanFile(); + if (err != OK) { reset(); - return err; } - - return OK; + return err; } -void TimedTextParser::reset() { - mDataSource.clear(); +void TimedTextSRTSource::reset() { mTextVector.clear(); - mOffset = 0; mIndex = 0; } -// scan the text file to get start/stop time and the -// offset of each piece of text content -status_t TimedTextParser::scanFile() { - if (mFileType != OUT_OF_BAND_FILE_SRT) { - return ERROR_UNSUPPORTED; +status_t TimedTextSRTSource::stop() { + reset(); + return OK; +} + +status_t TimedTextSRTSource::read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options) { + int64_t endTimeUs; + AString text; + status_t err = getText(options, &text, timeUs, &endTimeUs); + if (err != OK) { + return err; + } + + if (*timeUs > 0) { + extractAndAppendLocalDescriptions(*timeUs, text, parcel); } + return OK; +} +status_t TimedTextSRTSource::scanFile() { off64_t offset = 0; int64_t startTimeUs; bool endOfFile = false; while (!endOfFile) { TextInfo info; - status_t err = getNextInSrtFileFormat(&offset, &startTimeUs, &info); - - if (err != OK) { - if (err == ERROR_END_OF_STREAM) { + status_t err = getNextSubtitleInfo(&offset, &startTimeUs, &info); + switch (err) { + case OK: + mTextVector.add(startTimeUs, info); + break; + case ERROR_END_OF_STREAM: endOfFile = true; - } else { + break; + default: return err; - } - } else { - mTextVector.add(startTimeUs, info); } } - if (mTextVector.isEmpty()) { return ERROR_MALFORMED; } return OK; } -// read one line started from *offset and store it into data. -status_t TimedTextParser::readNextLine(off64_t *offset, AString *data) { - char character; - - data->clear(); - - while (true) { - ssize_t err; - if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) { - if (err == 0) { - return ERROR_END_OF_STREAM; - } - return ERROR_IO; - } - - (*offset) ++; - - // a line could end with CR, LF or CR + LF - if (character == 10) { - break; - } else if (character == 13) { - if ((err = mDataSource->readAt(*offset, &character, 1)) < 1) { - if (err == 0) { // end of the stream - return OK; - } - return ERROR_IO; - } - - (*offset) ++; - - if (character != 10) { - (*offset) --; - } - break; - } - - data->append(character); - } - - return OK; -} - /* SRT format: - * Subtitle number - * Start time --> End time - * Text of subtitle (one or more lines) - * Blank lines + * Subtitle number + * Start time --> End time + * Text of subtitle (one or more lines) + * Blank lines * * .srt file example: - * 1 - * 00:00:20,000 --> 00:00:24,400 - * Altocumulus clouds occur between six thousand + * 1 + * 00:00:20,000 --> 00:00:24,400 + * Altocumulus clouds occr between six thousand * - * 2 - * 00:00:24,600 --> 00:00:27,800 - * and twenty thousand feet above ground level. + * 2 + * 00:00:24,600 --> 00:00:27,800 + * and twenty thousand feet above ground level. */ -status_t TimedTextParser::getNextInSrtFileFormat( - off64_t *offset, int64_t *startTimeUs, TextInfo *info) { +status_t TimedTextSRTSource::getNextSubtitleInfo( + off64_t *offset, int64_t *startTimeUs, TextInfo *info) { AString data; status_t err; @@ -150,10 +123,9 @@ status_t TimedTextParser::getNextInSrtFileFormat( return err; } data.trim(); - } while(data.empty()); + } while (data.empty()); // Just ignore the first non-blank line which is subtitle sequence number. - if ((err = readNextLine(offset, &data)) != OK) { return err; } @@ -161,7 +133,7 @@ status_t TimedTextParser::getNextInSrtFileFormat( // the start time format is: hours:minutes:seconds,milliseconds // 00:00:24,600 --> 00:00:27,800 if (sscanf(data.c_str(), "%02d:%02d:%02d,%03d --> %02d:%02d:%02d,%03d", - &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) { + &hour1, &min1, &sec1, &msec1, &hour2, &min2, &sec2, &msec2) != 8) { return ERROR_MALFORMED; } @@ -172,7 +144,6 @@ status_t TimedTextParser::getNextInSrtFileFormat( } info->offset = *offset; - bool needMoreData = true; while (needMoreData) { if ((err = readNextLine(offset, &data)) != OK) { @@ -191,25 +162,56 @@ status_t TimedTextParser::getNextInSrtFileFormat( } } } - info->textLen = *offset - info->offset; - return OK; } -status_t TimedTextParser::getText( - AString *text, int64_t *startTimeUs, int64_t *endTimeUs, - const MediaSource::ReadOptions *options) { - Mutex::Autolock autoLock(mLock); +status_t TimedTextSRTSource::readNextLine(off64_t *offset, AString *data) { + data->clear(); + while (true) { + ssize_t readSize; + char character; + if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) { + if (readSize == 0) { + return ERROR_END_OF_STREAM; + } + return ERROR_IO; + } - text->clear(); + (*offset)++; + // a line could end with CR, LF or CR + LF + if (character == 10) { + break; + } else if (character == 13) { + if ((readSize = mSource->readAt(*offset, &character, 1)) < 1) { + if (readSize == 0) { // end of the stream + return OK; + } + return ERROR_IO; + } + + (*offset)++; + if (character != 10) { + (*offset)--; + } + break; + } + data->append(character); + } + return OK; +} + +status_t TimedTextSRTSource::getText( + const MediaSource::ReadOptions *options, + AString *text, int64_t *startTimeUs, int64_t *endTimeUs) { + text->clear(); int64_t seekTimeUs; MediaSource::ReadOptions::SeekMode mode; - if (options && options->getSeekTo(&seekTimeUs, &mode)) { - int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; + if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { + int64_t lastEndTimeUs = + mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; int64_t firstStartTimeUs = mTextVector.keyAt(0); - if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) { return ERROR_OUT_OF_RANGE; } else if (seekTimeUs < firstStartTimeUs) { @@ -232,31 +234,42 @@ status_t TimedTextParser::getText( low = mid + 1; } else { if ((high == mid + 1) - && (seekTimeUs < mTextVector.keyAt(high))) { + && (seekTimeUs < mTextVector.keyAt(high))) { break; } high = mid - 1; } } - mIndex = mid; } } - - TextInfo info = mTextVector.valueAt(mIndex); + const TextInfo &info = mTextVector.valueAt(mIndex); *startTimeUs = mTextVector.keyAt(mIndex); *endTimeUs = info.endTimeUs; - mIndex ++; + mIndex++; char *str = new char[info.textLen]; - if (mDataSource->readAt(info.offset, str, info.textLen) < info.textLen) { + if (mSource->readAt(info.offset, str, info.textLen) < info.textLen) { delete[] str; return ERROR_IO; } - text->append(str, info.textLen); delete[] str; return OK; } +status_t TimedTextSRTSource::extractAndAppendLocalDescriptions( + int64_t timeUs, const AString &text, Parcel *parcel) { + const void *data = text.c_str(); + size_t size = text.size(); + int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS | + TextDescriptions::OUT_OF_BAND_TEXT_SRT; + + if (size > 0) { + return TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, timeUs / 1000, parcel); + } + return OK; +} + } // namespace android diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h new file mode 100644 index 0000000..a0734d9 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextSRTSource.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 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 TIMED_TEXT_SRT_SOURCE_H_ +#define TIMED_TEXT_SRT_SOURCE_H_ + +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <utils/Compat.h> // off64_t + +#include "TimedTextSource.h" + +namespace android { + +class AString; +class DataSource; +class MediaBuffer; +class Parcel; + +class TimedTextSRTSource : public TimedTextSource { + public: + TimedTextSRTSource(const sp<DataSource>& dataSource); + virtual status_t start(); + virtual status_t stop(); + virtual status_t read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options = NULL); + + protected: + virtual ~TimedTextSRTSource(); + + private: + sp<DataSource> mSource; + + struct TextInfo { + int64_t endTimeUs; + // The offset of the text in the original file. + off64_t offset; + int textLen; + }; + + int mIndex; + KeyedVector<int64_t, TextInfo> mTextVector; + + void reset(); + status_t scanFile(); + status_t getNextSubtitleInfo( + off64_t *offset, int64_t *startTimeUs, TextInfo *info); + status_t readNextLine(off64_t *offset, AString *data); + status_t getText( + const MediaSource::ReadOptions *options, + AString *text, int64_t *startTimeUs, int64_t *endTimeUs); + status_t extractAndAppendLocalDescriptions( + int64_t timeUs, const AString &text, Parcel *parcel); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource); +}; + +} // namespace android + +#endif // TIMED_TEXT_SRT_SOURCE_H_ diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp new file mode 100644 index 0000000..9efe67c --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextSource.cpp @@ -0,0 +1,53 @@ + /* + * Copyright (C) 2012 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 "TimedTextSource" +#include <utils/Log.h> + +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaSource.h> + +#include "TimedTextSource.h" + +#include "TimedTextInBandSource.h" +#include "TimedTextSRTSource.h" + +namespace android { + +// static +sp<TimedTextSource> TimedTextSource::CreateTimedTextSource( + const sp<MediaSource>& mediaSource) { + return new TimedTextInBandSource(mediaSource); +} + +// static +sp<TimedTextSource> TimedTextSource::CreateTimedTextSource( + const sp<DataSource>& dataSource, FileType filetype) { + switch(filetype) { + case OUT_OF_BAND_FILE_SRT: + return new TimedTextSRTSource(dataSource); + case OUT_OF_BAND_FILE_SMI: + // TODO: Implement for SMI. + ALOGE("Supporting SMI is not implemented yet"); + break; + default: + ALOGE("Undefined subtitle format. : %d", filetype); + } + return NULL; +} + +} // namespace android diff --git a/media/libstagefright/timedtext/TimedTextSource.h b/media/libstagefright/timedtext/TimedTextSource.h new file mode 100644 index 0000000..06bae71 --- /dev/null +++ b/media/libstagefright/timedtext/TimedTextSource.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 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 TIMED_TEXT_SOURCE_H_ +#define TIMED_TEXT_SOURCE_H_ + +#include <media/stagefright/foundation/ABase.h> // for DISALLOW_XXX macro. +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> // for MediaSource::ReadOptions +#include <utils/RefBase.h> + +namespace android { + +class DataSource; +class Parcel; + +class TimedTextSource : public RefBase { + public: + enum FileType { + OUT_OF_BAND_FILE_SRT = 1, + OUT_OF_BAND_FILE_SMI = 2, + }; + static sp<TimedTextSource> CreateTimedTextSource( + const sp<MediaSource>& source); + static sp<TimedTextSource> CreateTimedTextSource( + const sp<DataSource>& source, FileType filetype); + TimedTextSource() {} + virtual status_t start() = 0; + virtual status_t stop() = 0; + // Returns subtitle parcel and its start time. + virtual status_t read( + int64_t *timeUs, + Parcel *parcel, + const MediaSource::ReadOptions *options = NULL) = 0; + virtual status_t extractGlobalDescriptions(Parcel *parcel) { + return INVALID_OPERATION; + } + + protected: + virtual ~TimedTextSource() { } + + private: + DISALLOW_EVIL_CONSTRUCTORS(TimedTextSource); +}; + +} // namespace android + +#endif // TIMED_TEXT_SOURCE_H_ |