diff options
author | Gloria Wang <gwang@google.com> | 2011-05-04 09:00:15 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-05-04 09:00:15 -0700 |
commit | 89dc0dffdcbf0999a854a9b1bbea8f56395cc209 (patch) | |
tree | 2c5fd3e04f8a4dc89a57967e0379e017cc45b81a /media/libstagefright | |
parent | 8ce2364512f7c32c824f5ec5719688830ba72427 (diff) | |
parent | 7a1e3e81264189e23a1db2b174e1b5a5d4c7d1c3 (diff) | |
download | frameworks_av-89dc0dffdcbf0999a854a9b1bbea8f56395cc209.zip frameworks_av-89dc0dffdcbf0999a854a9b1bbea8f56395cc209.tar.gz frameworks_av-89dc0dffdcbf0999a854a9b1bbea8f56395cc209.tar.bz2 |
Merge "Initial CL for the timed text support: - Add support for MP4 timed text - Add API for app to turn on/off a text track - Add timed text metadata(language) in the MediaMetadataRetriever"
Diffstat (limited to 'media/libstagefright')
-rw-r--r-- | media/libstagefright/Android.mk | 1 | ||||
-rw-r--r-- | media/libstagefright/AwesomePlayer.cpp | 70 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 38 | ||||
-rw-r--r-- | media/libstagefright/MediaDefs.cpp | 2 | ||||
-rw-r--r-- | media/libstagefright/StagefrightMetadataRetriever.cpp | 14 | ||||
-rw-r--r-- | media/libstagefright/TimedTextPlayer.cpp | 252 | ||||
-rw-r--r-- | media/libstagefright/include/AwesomePlayer.h | 42 | ||||
-rw-r--r-- | media/libstagefright/include/TimedTextPlayer.h | 91 |
8 files changed, 487 insertions, 23 deletions
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 2f3e141..6c7176c 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -48,6 +48,7 @@ LOCAL_SRC_FILES:= \ ThrottledSource.cpp \ TimeSource.cpp \ TimedEventQueue.cpp \ + TimedTextPlayer.cpp \ Utils.cpp \ VBRISeeker.cpp \ WAVExtractor.cpp \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 974efa7..cccd0b7 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -28,6 +28,7 @@ #include "include/NuCachedSource2.h" #include "include/ThrottledSource.h" #include "include/MPEG2TSExtractor.h" +#include "include/TimedTextPlayer.h" #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> @@ -185,7 +186,8 @@ AwesomePlayer::AwesomePlayer() mExtractorFlags(0), mVideoBuffer(NULL), mDecryptHandle(NULL), - mLastVideoTimeUs(-1) { + mLastVideoTimeUs(-1), + mTextPlayer(NULL) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -381,10 +383,8 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { mFlags |= AUTO_LOOPING; } } - } - - if (haveAudio && haveVideo) { - break; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { + addTextSource(extractor->getTrack(i)); } } @@ -469,6 +469,11 @@ void AwesomePlayer::reset_l() { delete mAudioPlayer; mAudioPlayer = NULL; + if (mTextPlayer != NULL) { + delete mTextPlayer; + mTextPlayer = NULL; + } + mVideoRenderer.clear(); if (mRTSPController != NULL) { @@ -971,6 +976,11 @@ status_t AwesomePlayer::pause_l(bool at_eos) { mFlags &= ~AUDIO_RUNNING; } + if (mFlags & TEXTPLAYER_STARTED) { + mTextPlayer->pause(); + mFlags &= ~TEXT_RUNNING; + } + mFlags &= ~PLAYING; if (mDecryptHandle != NULL) { @@ -1119,6 +1129,32 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { return OK; } +status_t AwesomePlayer::setTimedTextTrackIndex(int32_t index) { + if (mTextPlayer != NULL) { + if (index >= 0) { // to turn on a text track + status_t err = mTextPlayer->setTimedTextTrackIndex(index); + if (err != OK) { + return err; + } + + mFlags |= TEXT_RUNNING; + mFlags |= TEXTPLAYER_STARTED; + return OK; + } else { // to turn off the text track display + if (mFlags & TEXT_RUNNING) { + mFlags &= ~TEXT_RUNNING; + } + if (mFlags & TEXTPLAYER_STARTED) { + mFlags &= ~TEXTPLAYER_STARTED; + } + + return mTextPlayer->setTimedTextTrackIndex(index); + } + } else { + return INVALID_OPERATION; + } +} + // static void AwesomePlayer::OnRTSPSeekDoneWrapper(void *cookie) { static_cast<AwesomePlayer *>(cookie)->onRTSPSeekDone(); @@ -1155,6 +1191,10 @@ status_t AwesomePlayer::seekTo_l(int64_t timeUs) { seekAudioIfNecessary_l(); + if (mFlags & TEXTPLAYER_STARTED) { + mTextPlayer->seekTo(mSeekTimeUs); + } + if (!(mFlags & PLAYING)) { LOGV("seeking while paused, sending SEEK_COMPLETE notification" " immediately."); @@ -1193,6 +1233,16 @@ void AwesomePlayer::setAudioSource(sp<MediaSource> source) { mAudioTrack = source; } +void AwesomePlayer::addTextSource(sp<MediaSource> source) { + CHECK(source != NULL); + + if (mTextPlayer == NULL) { + mTextPlayer = new TimedTextPlayer(this, mListener, &mQueue); + } + + mTextPlayer->addTextSource(source); +} + status_t AwesomePlayer::initAudioDecoder() { sp<MetaData> meta = mAudioTrack->getFormat(); @@ -1472,6 +1522,11 @@ void AwesomePlayer::onVideoEvent() { } } + if ((mFlags & TEXTPLAYER_STARTED) && !(mFlags & (TEXT_RUNNING | SEEK_PREVIEW))) { + mTextPlayer->resume(); + mFlags |= TEXT_RUNNING; + } + TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource; if (mFlags & FIRST_FRAME) { @@ -1906,7 +1961,10 @@ void AwesomePlayer::postAudioSeekComplete() { } status_t AwesomePlayer::setParameter(int key, const Parcel &request) { - return OK; + if (key == KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX) { + return setTimedTextTrackIndex(request.readInt32()); + } + return ERROR_UNSUPPORTED; } status_t AwesomePlayer::getParameter(int key, Parcel *reply) { diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 8787214..c79d02e 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -19,6 +19,8 @@ #include "include/MPEG4Extractor.h" #include "include/SampleTable.h" +#include "include/ESDS.h" +#include "include/TimedTextPlayer.h" #include <arpa/inet.h> @@ -29,7 +31,6 @@ #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/DataSource.h> -#include "include/ESDS.h" #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDefs.h> @@ -832,6 +833,33 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { mLastTrack->meta->setInt64( kKeyDuration, (duration * 1000000) / mLastTrack->timescale); + uint8_t lang[2]; + off64_t lang_offset; + if (version == 1) { + lang_offset = timescale_offset + 4 + 8; + } else if (version == 0) { + lang_offset = timescale_offset + 4 + 4; + } else { + return ERROR_IO; + } + + if (mDataSource->readAt(lang_offset, &lang, sizeof(lang)) + < (ssize_t)sizeof(lang)) { + return ERROR_IO; + } + + // To get the ISO-639-2/T three character language code + // 1 bit pad followed by 3 5-bits characters. Each character + // is packed as the difference between its ASCII value and 0x60. + char lang_code[4]; + lang_code[0] = ((lang[0] >> 2) & 0x1f) + 0x60; + lang_code[1] = ((lang[0] & 0x3) << 3 | (lang[1] >> 5)) + 0x60; + lang_code[2] = (lang[1] & 0x1f) + 0x60; + lang_code[3] = '\0'; + + mLastTrack->meta->setCString( + kKeyMediaLanguage, lang_code); + *offset += chunk_size; break; } @@ -1295,6 +1323,14 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return parseDrmSINF(offset, data_offset); } + case FOURCC('t', 'x', '3', 'g'): + { + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_TEXT_3GPP); + + *offset += chunk_size; + break; + } + default: { *offset += chunk_size; diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 8ca6ee8..8cd08bc 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -47,4 +47,6 @@ const char *MEDIA_MIMETYPE_CONTAINER_AVI = "video/avi"; const char *MEDIA_MIMETYPE_CONTAINER_WVM = "video/wvm"; +const char *MEDIA_MIMETYPE_TEXT_3GPP = "text/3gpp-tt"; + } // namespace android diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp index 7621f2c..4c3dc47 100644 --- a/media/libstagefright/StagefrightMetadataRetriever.cpp +++ b/media/libstagefright/StagefrightMetadataRetriever.cpp @@ -27,6 +27,7 @@ #include <media/stagefright/MediaExtractor.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> +#include <media/stagefright/MediaDefs.h> namespace android { @@ -429,6 +430,7 @@ void StagefrightMetadataRetriever::parseMetaData() { // The overall duration is the duration of the longest track. int64_t maxDurationUs = 0; + String8 timedTextLang; for (size_t i = 0; i < numTracks; ++i) { sp<MetaData> trackMeta = mExtractor->getTrackMetaData(i); @@ -452,10 +454,22 @@ void StagefrightMetadataRetriever::parseMetaData() { CHECK(trackMeta->findInt32(kKeyWidth, &videoWidth)); CHECK(trackMeta->findInt32(kKeyHeight, &videoHeight)); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { + const char *lang; + trackMeta->findCString(kKeyMediaLanguage, &lang); + timedTextLang.append(String8(lang)); + timedTextLang.append(String8(":")); } } } + // To save the language codes for all timed text tracks + // If multiple text tracks present, the format will look + // like "eng:chi" + if (!timedTextLang.isEmpty()) { + mMetaData.add(METADATA_KEY_TIMED_TEXT_LANGUAGES, timedTextLang); + } + // The duration value is a string representing the duration in ms. sprintf(tmp, "%lld", (maxDurationUs + 500) / 1000); mMetaData.add(METADATA_KEY_DURATION, String8(tmp)); diff --git a/media/libstagefright/TimedTextPlayer.cpp b/media/libstagefright/TimedTextPlayer.cpp new file mode 100644 index 0000000..1ac22cb --- /dev/null +++ b/media/libstagefright/TimedTextPlayer.cpp @@ -0,0 +1,252 @@ + /* + * 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/Utils.h> +#include "include/AwesomePlayer.h" +#include "include/TimedTextPlayer.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), + mSeekTimeUs(0), + mStarted(false), + mTextEventPending(false), + mQueue(queue), + mListener(listener), + mObserver(observer), + mTextBuffer(NULL) { + mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent); +} + +TimedTextPlayer::~TimedTextPlayer() { + if (mStarted) { + reset(); + } + + mTextTrackVector.clear(); +} + +status_t TimedTextPlayer::start(uint8_t index) { + CHECK(!mStarted); + + if (index >= mTextTrackVector.size()) { + LOGE("Incorrect text track index"); + return BAD_VALUE; + } + + mSource = mTextTrackVector.itemAt(index); + + status_t err = mSource->start(); + + if (err != OK) { + return err; + } + + 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 (mTextBuffer != NULL) { + mTextBuffer->release(); + mTextBuffer = NULL; + } + + if (mSource != NULL) { + mSource->stop(); + mSource.clear(); + mSource = NULL; + } +} + +status_t TimedTextPlayer::seekTo(int64_t time_us) { + Mutex::Autolock autoLock(mLock); + + mSeeking = true; + mSeekTimeUs = time_us; + + return OK; +} + +status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) { + if (index >= (int)(mTextTrackVector.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 (mTextBuffer != NULL) { + mTextBuffer->release(); + mTextBuffer = NULL; + } + + notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen + } + + 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; + } + + int64_t positionUs, timeUs; + mObserver->getPosition(&positionUs); + mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs); + + //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) { + mTextTrackVector.add(source); +} + +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); + } + } + } +} +} diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index 2c17d92..fd3ddf7 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -44,6 +44,8 @@ struct ARTSPController; class DrmManagerClinet; class DecryptHandle; +class TimedTextPlayer; + struct AwesomeRenderer : public RefBase { AwesomeRenderer() {} @@ -99,36 +101,41 @@ struct AwesomePlayer { void postAudioEOS(int64_t delayUs = 0ll); void postAudioSeekComplete(); + status_t setTimedTextTrackIndex(int32_t index); + private: friend struct AwesomeEvent; friend struct PreviewPlayer; enum { - PLAYING = 1, - LOOPING = 2, - FIRST_FRAME = 4, - PREPARING = 8, - PREPARED = 16, - AT_EOS = 32, - PREPARE_CANCELLED = 64, - CACHE_UNDERRUN = 128, - AUDIO_AT_EOS = 256, - VIDEO_AT_EOS = 512, - AUTO_LOOPING = 1024, + PLAYING = 0x01, + LOOPING = 0x02, + FIRST_FRAME = 0x04, + PREPARING = 0x08, + PREPARED = 0x10, + AT_EOS = 0x20, + PREPARE_CANCELLED = 0x40, + CACHE_UNDERRUN = 0x80, + AUDIO_AT_EOS = 0x0100, + VIDEO_AT_EOS = 0x0200, + AUTO_LOOPING = 0x0400, // We are basically done preparing but are currently buffering // sufficient data to begin playback and finish the preparation phase // for good. - PREPARING_CONNECTED = 2048, + PREPARING_CONNECTED = 0x0800, // We're triggering a single video event to display the first frame // after the seekpoint. - SEEK_PREVIEW = 4096, + SEEK_PREVIEW = 0x1000, - AUDIO_RUNNING = 8192, - AUDIOPLAYER_STARTED = 16384, + AUDIO_RUNNING = 0x2000, + AUDIOPLAYER_STARTED = 0x4000, - INCOGNITO = 32768, + INCOGNITO = 0x8000, + + TEXT_RUNNING = 0x10000, + TEXTPLAYER_STARTED = 0x20000, }; mutable Mutex mLock; @@ -222,6 +229,7 @@ private: sp<DecryptHandle> mDecryptHandle; int64_t mLastVideoTimeUs; + TimedTextPlayer *mTextPlayer; status_t setDataSource_l( const char *uri, @@ -244,6 +252,8 @@ private: void setVideoSource(sp<MediaSource> source); status_t initVideoDecoder(uint32_t flags = 0); + void addTextSource(sp<MediaSource> source); + void onStreamDone(); void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0); diff --git a/media/libstagefright/include/TimedTextPlayer.h b/media/libstagefright/include/TimedTextPlayer.h new file mode 100644 index 0000000..ac41b4f --- /dev/null +++ b/media/libstagefright/include/TimedTextPlayer.h @@ -0,0 +1,91 @@ +/* + * 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 TIMEDTEXT_PLAYER_H_ + +#define TIMEDTEXT_PLAYER_H_ + +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/foundation/ABase.h> + +#include "include/TimedEventQueue.h" + +namespace android { + +class MediaSource; +class AwesomePlayer; +class MediaBuffer; + +class TimedTextPlayer { +public: + TimedTextPlayer(AwesomePlayer *observer, + const wp<MediaPlayerBase> &listener, + TimedEventQueue *queue); + + virtual ~TimedTextPlayer(); + + // index: the index of the text track which will + // be turned on + status_t start(uint8_t index); + + void pause(); + + void resume(); + + status_t seekTo(int64_t time_us); + + void addTextSource(sp<MediaSource> source); + + status_t setTimedTextTrackIndex(int32_t index); + +private: + Mutex mLock; + + sp<MediaSource> mSource; + + bool mSeeking; + int64_t mSeekTimeUs; + + bool mStarted; + + sp<TimedEventQueue::Event> mTextEvent; + bool mTextEventPending; + + TimedEventQueue *mQueue; + + wp<MediaPlayerBase> mListener; + AwesomePlayer *mObserver; + + MediaBuffer *mTextBuffer; + Parcel mData; + + Vector<sp<MediaSource> > mTextTrackVector; + + void reset(); + + void onTextEvent(); + void postTextEvent(int64_t delayUs = -1); + void cancelTextEvent(); + + void notifyListener( + int msg, const void *data = NULL, size_t size = 0); + + DISALLOW_EVIL_CONSTRUCTORS(TimedTextPlayer); +}; + +} // namespace android + +#endif // TIMEDTEXT_PLAYER_H_ |