TimedTextPlayer.cpp revision 965d08ba16ee82bc85f69546360c18e7da907406
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#include <media/stagefright/MediaDebug.h> 23#include <media/stagefright/MediaDefs.h> 24#include <media/stagefright/MediaErrors.h> 25#include <media/stagefright/MediaSource.h> 26#include <media/stagefright/MetaData.h> 27#include <media/stagefright/MediaBuffer.h> 28#include <media/stagefright/FileSource.h> 29#include <media/stagefright/Utils.h> 30#include "include/AwesomePlayer.h" 31#include "TimedTextPlayer.h" 32#include "TimedTextParser.h" 33 34namespace android { 35 36struct TimedTextEvent : public TimedEventQueue::Event { 37 TimedTextEvent( 38 TimedTextPlayer *player, 39 void (TimedTextPlayer::*method)()) 40 : mPlayer(player), 41 mMethod(method) { 42 } 43 44protected: 45 virtual ~TimedTextEvent() {} 46 47 virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { 48 (mPlayer->*mMethod)(); 49 } 50 51private: 52 TimedTextPlayer *mPlayer; 53 void (TimedTextPlayer::*mMethod)(); 54 55 TimedTextEvent(const TimedTextEvent &); 56 TimedTextEvent &operator=(const TimedTextEvent &); 57}; 58 59TimedTextPlayer::TimedTextPlayer( 60 AwesomePlayer *observer, 61 const wp<MediaPlayerBase> &listener, 62 TimedEventQueue *queue) 63 : mSource(NULL), 64 mOutOfBandSource(NULL), 65 mSeekTimeUs(0), 66 mStarted(false), 67 mTextEventPending(false), 68 mQueue(queue), 69 mListener(listener), 70 mObserver(observer), 71 mTextBuffer(NULL), 72 mTextParser(NULL), 73 mTextType(kNoText) { 74 mTextEvent = new TimedTextEvent(this, &TimedTextPlayer::onTextEvent); 75} 76 77TimedTextPlayer::~TimedTextPlayer() { 78 if (mStarted) { 79 reset(); 80 } 81 82 mTextTrackVector.clear(); 83 mTextOutOfBandVector.clear(); 84} 85 86status_t TimedTextPlayer::start(uint8_t index) { 87 CHECK(!mStarted); 88 89 if (index >= 90 mTextTrackVector.size() + mTextOutOfBandVector.size()) { 91 LOGE("Incorrect text track index: %d", index); 92 return BAD_VALUE; 93 } 94 95 if (index < mTextTrackVector.size()) { // start an in-band text 96 mSource = mTextTrackVector.itemAt(index); 97 98 status_t err = mSource->start(); 99 100 if (err != OK) { 101 return err; 102 } 103 mTextType = kInBandText; 104 } else { // start an out-of-band text 105 OutOfBandText text = 106 mTextOutOfBandVector.itemAt(index - mTextTrackVector.size()); 107 108 mOutOfBandSource = text.source; 109 TimedTextParser::FileType fileType = text.type; 110 111 if (mTextParser == NULL) { 112 mTextParser = new TimedTextParser(); 113 } 114 115 status_t err; 116 if ((err = mTextParser->init(mOutOfBandSource, fileType)) != OK) { 117 return err; 118 } 119 mTextType = kOutOfBandText; 120 } 121 122 int64_t positionUs; 123 mObserver->getPosition(&positionUs); 124 seekTo(positionUs); 125 126 postTextEvent(); 127 128 mStarted = true; 129 130 return OK; 131} 132 133void TimedTextPlayer::pause() { 134 CHECK(mStarted); 135 136 cancelTextEvent(); 137} 138 139void TimedTextPlayer::resume() { 140 CHECK(mStarted); 141 142 postTextEvent(); 143} 144 145void TimedTextPlayer::reset() { 146 CHECK(mStarted); 147 148 // send an empty text to clear the screen 149 notifyListener(MEDIA_TIMED_TEXT); 150 151 cancelTextEvent(); 152 153 mSeeking = false; 154 mStarted = false; 155 156 if (mTextType == kInBandText) { 157 if (mTextBuffer != NULL) { 158 mTextBuffer->release(); 159 mTextBuffer = NULL; 160 } 161 162 if (mSource != NULL) { 163 mSource->stop(); 164 mSource.clear(); 165 mSource = NULL; 166 } 167 } else { 168 if (mTextParser != NULL) { 169 mTextParser.clear(); 170 mTextParser = NULL; 171 } 172 if (mOutOfBandSource != NULL) { 173 mOutOfBandSource.clear(); 174 mOutOfBandSource = NULL; 175 } 176 } 177} 178 179status_t TimedTextPlayer::seekTo(int64_t time_us) { 180 Mutex::Autolock autoLock(mLock); 181 182 mSeeking = true; 183 mSeekTimeUs = time_us; 184 185 postTextEvent(); 186 187 return OK; 188} 189 190status_t TimedTextPlayer::setTimedTextTrackIndex(int32_t index) { 191 if (index >= 192 (int)(mTextTrackVector.size() + mTextOutOfBandVector.size())) { 193 return BAD_VALUE; 194 } 195 196 if (mStarted) { 197 reset(); 198 } 199 200 if (index >= 0) { 201 return start(index); 202 } 203 return OK; 204} 205 206void TimedTextPlayer::onTextEvent() { 207 Mutex::Autolock autoLock(mLock); 208 209 if (!mTextEventPending) { 210 return; 211 } 212 mTextEventPending = false; 213 214 MediaSource::ReadOptions options; 215 if (mSeeking) { 216 options.setSeekTo(mSeekTimeUs, 217 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 218 mSeeking = false; 219 220 if (mTextType == kInBandText) { 221 if (mTextBuffer != NULL) { 222 mTextBuffer->release(); 223 mTextBuffer = NULL; 224 } 225 } else { 226 mText.clear(); 227 } 228 229 notifyListener(MEDIA_TIMED_TEXT); //empty text to clear the screen 230 } 231 232 int64_t positionUs, timeUs; 233 mObserver->getPosition(&positionUs); 234 235 if (mTextType == kInBandText) { 236 if (mTextBuffer != NULL) { 237 uint8_t *tmp = (uint8_t *)(mTextBuffer->data()); 238 size_t len = (*tmp) << 8 | (*(tmp + 1)); 239 240 notifyListener(MEDIA_TIMED_TEXT, 241 tmp + 2, 242 len); 243 244 mTextBuffer->release(); 245 mTextBuffer = NULL; 246 247 } 248 249 if (mSource->read(&mTextBuffer, &options) != OK) { 250 return; 251 } 252 253 mTextBuffer->meta_data()->findInt64(kKeyTime, &timeUs); 254 } else { 255 if (mText.size() > 0) { 256 notifyListener(MEDIA_TIMED_TEXT, 257 mText.c_str(), 258 mText.size()); 259 mText.clear(); 260 } 261 262 int64_t endTimeUs; 263 if (mTextParser->getText( 264 &mText, &timeUs, &endTimeUs, &options) != OK) { 265 return; 266 } 267 } 268 269 //send the text now 270 if (timeUs <= positionUs + 100000ll) { 271 postTextEvent(); 272 } else { 273 postTextEvent(timeUs - positionUs - 100000ll); 274 } 275} 276 277void TimedTextPlayer::postTextEvent(int64_t delayUs) { 278 if (mTextEventPending) { 279 return; 280 } 281 282 mTextEventPending = true; 283 mQueue->postEventWithDelay(mTextEvent, delayUs < 0 ? 10000 : delayUs); 284} 285 286void TimedTextPlayer::cancelTextEvent() { 287 mQueue->cancelEvent(mTextEvent->eventID()); 288 mTextEventPending = false; 289} 290 291void TimedTextPlayer::addTextSource(sp<MediaSource> source) { 292 Mutex::Autolock autoLock(mLock); 293 mTextTrackVector.add(source); 294} 295 296status_t TimedTextPlayer::setParameter(int key, const Parcel &request) { 297 Mutex::Autolock autoLock(mLock); 298 299 if (key == KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE) { 300 String8 uri = request.readString8(); 301 KeyedVector<String8, String8> headers; 302 303 // To support local subtitle file only for now 304 if (strncasecmp("file://", uri.string(), 7)) { 305 return INVALID_OPERATION; 306 } 307 sp<DataSource> dataSource = 308 DataSource::CreateFromURI(uri, &headers); 309 status_t err = dataSource->initCheck(); 310 311 if (err != OK) { 312 return err; 313 } 314 315 OutOfBandText text; 316 text.source = dataSource; 317 if (uri.getPathExtension() == String8(".srt")) { 318 text.type = TimedTextParser::OUT_OF_BAND_FILE_SRT; 319 } else { 320 return ERROR_UNSUPPORTED; 321 } 322 323 mTextOutOfBandVector.add(text); 324 325 return OK; 326 } 327 return INVALID_OPERATION; 328} 329 330void TimedTextPlayer::notifyListener( 331 int msg, const void *data, size_t size) { 332 if (mListener != NULL) { 333 sp<MediaPlayerBase> listener = mListener.promote(); 334 335 if (listener != NULL) { 336 if (size > 0) { 337 mData.freeData(); 338 mData.write(data, size); 339 340 listener->sendEvent(msg, 0, 0, &mData); 341 } else { // send an empty timed text to clear the screen 342 listener->sendEvent(msg); 343 } 344 } 345 } 346} 347} 348