TimedTextPlayer.cpp revision 3b573f7bf1c5736d500e39013b8d32478a1429e6
1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "TimedTextPlayer" 19#include <utils/Log.h> 20 21#include <binder/IPCThreadState.h> 22 23#include <media/stagefright/MediaDebug.h> 24#include <media/stagefright/MediaDefs.h> 25#include <media/stagefright/MediaErrors.h> 26#include <media/stagefright/MediaSource.h> 27#include <media/stagefright/MetaData.h> 28#include <media/stagefright/MediaBuffer.h> 29#include <media/stagefright/FileSource.h> 30#include <media/stagefright/Utils.h> 31 32#include "include/AwesomePlayer.h" 33#include "TimedTextPlayer.h" 34#include "TimedTextParser.h" 35#include "TextDescriptions.h" 36 37namespace android { 38 39struct TimedTextEvent : public TimedEventQueue::Event { 40 TimedTextEvent( 41 TimedTextPlayer *player, 42 void (TimedTextPlayer::*method)()) 43 : mPlayer(player), 44 mMethod(method) { 45 } 46 47protected: 48 virtual ~TimedTextEvent() {} 49 50 virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { 51 (mPlayer->*mMethod)(); 52 } 53 54private: 55 TimedTextPlayer *mPlayer; 56 void (TimedTextPlayer::*mMethod)(); 57 58 TimedTextEvent(const TimedTextEvent &); 59 TimedTextEvent &operator=(const TimedTextEvent &); 60}; 61 62TimedTextPlayer::TimedTextPlayer( 63 AwesomePlayer *observer, 64 const wp<MediaPlayerBase> &listener, 65 TimedEventQueue *queue) 66 : mSource(NULL), 67 mOutOfBandSource(NULL), 68 mSeekTimeUs(0), 69 mStarted(false), 70 mTextEventPending(false), 71 mQueue(queue), 72 mListener(listener), 73 mObserver(observer), 74 mTextBuffer(NULL), 75 mTextParser(NULL), 76 mTextType(kNoText) { 77 mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent); 78} 79 80TimedTextPlayer::~TimedTextPlayer() { 81 if (mStarted) { 82 reset(); 83 } 84 85 mTextTrackVector.clear(); 86 mTextOutOfBandVector.clear(); 87} 88 89status_t TimedTextPlayer::start(uint8_t index) { 90 CHECK(!mStarted); 91 92 if (index >= 93 mTextTrackVector.size() + mTextOutOfBandVector.size()) { 94 LOGE("Incorrect text track index: %d", index); 95 return BAD_VALUE; 96 } 97 98 status_t err; 99 if (index < mTextTrackVector.size()) { // start an in-band text 100 mSource = mTextTrackVector.itemAt(index); 101 102 err = mSource->start(); 103 104 if (err != OK) { 105 return err; 106 } 107 mTextType = kInBandText; 108 } else { // start an out-of-band text 109 OutOfBandText text = 110 mTextOutOfBandVector.itemAt(index - mTextTrackVector.size()); 111 112 mOutOfBandSource = text.source; 113 TimedTextParser::FileType fileType = text.type; 114 115 if (mTextParser == NULL) { 116 mTextParser = new TimedTextParser(); 117 } 118 119 if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) { 120 return err; 121 } 122 mTextType = kOutOfBandText; 123 } 124 125 // send sample description format 126 if ((err = extractAndSendGlobalDescriptions()) != OK) { 127 return err; 128 } 129 130 int64_t positionUs; 131 mObserver->getPosition(&positionUs); 132 seekTo(positionUs); 133 134 postTextEvent(); 135 136 mStarted = true; 137 138 return OK; 139} 140 141void TimedTextPlayer::pause() { 142 CHECK(mStarted); 143 144 cancelTextEvent(); 145} 146 147void TimedTextPlayer::resume() { 148 CHECK(mStarted); 149 150 postTextEvent(); 151} 152 153void TimedTextPlayer::reset() { 154 CHECK(mStarted); 155 156 // send an empty text to clear the screen 157 notifyListener(MEDIA_TIMED_TEXT); 158 159 cancelTextEvent(); 160 161 mSeeking = false; 162 mStarted = false; 163 164 if (mTextType == kInBandText) { 165 if (mTextBuffer != NULL) { 166 mTextBuffer->release(); 167 mTextBuffer = NULL; 168 } 169 170 if (mSource != NULL) { 171 mSource->stop(); 172 mSource.clear(); 173 mSource = NULL; 174 } 175 } else { 176 if (mTextParser != NULL) { 177 mTextParser.clear(); 178 mTextParser = NULL; 179 } 180 if (mOutOfBandSource != NULL) { 181 mOutOfBandSource.clear(); 182 mOutOfBandSource = NULL; 183 } 184 } 185} 186 187status_t TimedTextPlayer::seekTo(int64_t time_us) { 188 Mutex::Autolock autoLock(mLock); 189 190 mSeeking = true; 191 mSeekTimeUs = time_us; 192 193 postTextEvent(); 194 195 return OK; 196} 197 198status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) { 199 if (index >= 200 (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) { 201 return BAD_VALUE; 202 } 203 204 if (mStarted) { 205 reset(); 206 } 207 208 if (index >= 0) { 209 return start(index); 210 } 211 return OK; 212} 213 214void TimedTextPlayer::onTextEvent() { 215 Mutex::Autolock autoLock(mLock); 216 217 if (!mTextEventPending) { 218 return; 219 } 220 mTextEventPending = false; 221 222 if (mData.dataSize() > 0) { 223 notifyListener(MEDIA_TIMED_TEXT, &mData); 224 mData.freeData(); 225 } 226 227 MediaSource::ReadOptions options; 228 if (mSeeking) { 229 options.setSeekTo(mSeekTimeUs, 230 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 231 mSeeking = false; 232 233 notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen 234 } 235 236 int64_t positionUs, timeUs; 237 mObserver->getPosition(&positionUs); 238 239 if (mTextType == kInBandText) { 240 if (mSource->read(&mTextBuffer, &options) != OK) { 241 return; 242 } 243 244 mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs); 245 } else { 246 int64_t endTimeUs; 247 if (mTextParser->getText( 248 &mText, &timeUs, &endTimeUs, &options) != OK) { 249 return; 250 } 251 } 252 253 if (timeUs > 0) { 254 extractAndAppendLocalDescriptions(timeUs); 255 } 256 257 if (mTextType == kInBandText) { 258 if (mTextBuffer != NULL) { 259 mTextBuffer->release(); 260 mTextBuffer = NULL; 261 } 262 } else { 263 mText.clear(); 264 } 265 266 //send the text now 267 if (timeUs <= positionUs + 100000ll) { 268 postTextEvent(); 269 } else { 270 postTextEvent(timeUs - positionUs - 100000ll); 271 } 272} 273 274void TimedTextPlayer::postTextEvent(int64_t delayUs) { 275 if (mTextEventPending) { 276 return; 277 } 278 279 mTextEventPending = true; 280 mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs); 281} 282 283void TimedTextPlayer::cancelTextEvent() { 284 mQueue->cancelEvent(mTextEvent->eventID()); 285 mTextEventPending = false; 286} 287 288void TimedTextPlayer::addTextSource(sp<MediaSource> source) { 289 Mutex::Autolock autoLock(mLock); 290 mTextTrackVector.add(source); 291} 292 293status_t TimedTextPlayer::setParameter(int key, const Parcel &request) { 294 Mutex::Autolock autoLock(mLock); 295 296 if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) { 297 const String16 uri16 = request.readString16(); 298 String8 uri = String8(uri16); 299 KeyedVector<String8, String8> headers; 300 301 // To support local subtitle file only for now 302 if (strncasecmp("file://", uri.string(), 7)) { 303 return INVALID_OPERATION; 304 } 305 sp<DataSource> dataSource = 306 DataSource::CreateFromURI(uri, &headers); 307 status_t err = dataSource->initCheck(); 308 309 if (err != OK) { 310 return err; 311 } 312 313 OutOfBandText text; 314 text.source = dataSource; 315 if (uri.getPathExtension() == String8(".srt")) { 316 text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT; 317 } else { 318 return ERROR_UNSUPPORTED; 319 } 320 321 mTextOutOfBandVector.add(text); 322 323 return OK; 324 } 325 return INVALID_OPERATION; 326} 327 328void TimedTextPlayer::notifyListener(int msg, const Parcel *parcel) { 329 if (mListener != NULL) { 330 sp<MediaPlayerBase> listener = mListener.promote(); 331 332 if (listener != NULL) { 333 if (parcel && (parcel->dataSize() > 0)) { 334 listener->sendEvent(msg, 0, 0, parcel); 335 } else { // send an empty timed text to clear the screen 336 listener->sendEvent(msg); 337 } 338 } 339 } 340} 341 342// Each text sample consists of a string of text, optionally with sample 343// modifier description. The modifier description could specify a new 344// text style for the string of text. These descriptions are present only 345// if they are needed. This method is used to extract the modifier 346// description and append it at the end of the text. 347status_t TimedTextPlayer::extractAndAppendLocalDescriptions(int64_t timeUs) { 348 const void *data; 349 size_t size = 0; 350 int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS; 351 352 if (mTextType == kInBandText) { 353 const char *mime; 354 CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); 355 356 if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { 357 flag |= TextDescriptions::IN_BAND_TEXT_3GPP; 358 data = mTextBuffer->data(); 359 size = mTextBuffer->size(); 360 } else { 361 // support 3GPP only for now 362 return ERROR_UNSUPPORTED; 363 } 364 } else { 365 data = mText.c_str(); 366 size = mText.size(); 367 flag |= TextDescriptions::OUT_OF_BAND_TEXT_SRT; 368 } 369 370 if ((size > 0) && (flag != TextDescriptions::LOCAL_DESCRIPTIONS)) { 371 mData.freeData(); 372 return TextDescriptions::getParcelOfDescriptions( 373 (const uint8_t *)data, size, flag, timeUs / 1000, &mData); 374 } 375 376 return OK; 377} 378 379// To extract and send the global text descriptions for all the text samples 380// in the text track or text file. 381status_t TimedTextPlayer::extractAndSendGlobalDescriptions() { 382 const void *data; 383 size_t size = 0; 384 int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS; 385 386 if (mTextType == kInBandText) { 387 const char *mime; 388 CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); 389 390 // support 3GPP only for now 391 if (!strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP)) { 392 uint32_t type; 393 // get the 'tx3g' box content. This box contains the text descriptions 394 // used to render the text track 395 if (!mSource->getFormat()->findData( 396 kKeyTextFormatData, &type, &data, &size)) { 397 return ERROR_MALFORMED; 398 } 399 400 flag |= TextDescriptions::IN_BAND_TEXT_3GPP; 401 } 402 } 403 404 if ((size > 0) && (flag != TextDescriptions::GLOBAL_DESCRIPTIONS)) { 405 Parcel parcel; 406 if (TextDescriptions::getParcelOfDescriptions( 407 (const uint8_t *)data, size, flag, 0, &parcel) == OK) { 408 if (parcel.dataSize() > 0) { 409 notifyListener(MEDIA_TIMED_TEXT, &parcel); 410 } 411 } 412 } 413 414 return OK; 415} 416} 417