TimedTextPlayer.cpp revision 3478eaa23c19dbbe473be70e8f3cc0c904d2465c
1 /* 2 * Copyright (C) 2012 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 <limits.h> 22#include <media/stagefright/foundation/ADebug.h> 23#include <media/stagefright/foundation/AMessage.h> 24#include <media/stagefright/timedtext/TimedTextDriver.h> 25#include <media/stagefright/MediaErrors.h> 26#include <media/MediaPlayerInterface.h> 27 28#include "TimedTextPlayer.h" 29 30#include "TimedTextSource.h" 31 32namespace android { 33 34// Event should be fired a bit earlier considering the processing time till 35// application actually gets the notification message. 36static const int64_t kAdjustmentProcessingTimeUs = 100000ll; 37static const int64_t kMaxDelayUs = 5000000ll; 38static const int64_t kWaitTimeUsToRetryRead = 100000ll; 39static const int64_t kInvalidTimeUs = INT_MIN; 40 41TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener) 42 : mListener(listener), 43 mSource(NULL), 44 mPendingSeekTimeUs(kInvalidTimeUs), 45 mPaused(false), 46 mSendSubtitleGeneration(0) { 47} 48 49TimedTextPlayer::~TimedTextPlayer() { 50 if (mSource != NULL) { 51 mSource->stop(); 52 mSource.clear(); 53 mSource = NULL; 54 } 55} 56 57void TimedTextPlayer::start() { 58 (new AMessage(kWhatStart, id()))->post(); 59} 60 61void TimedTextPlayer::pause() { 62 (new AMessage(kWhatPause, id()))->post(); 63} 64 65void TimedTextPlayer::resume() { 66 (new AMessage(kWhatResume, id()))->post(); 67} 68 69void TimedTextPlayer::seekToAsync(int64_t timeUs) { 70 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 71 msg->setInt64("seekTimeUs", timeUs); 72 msg->post(); 73} 74 75void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) { 76 sp<AMessage> msg = new AMessage(kWhatSetSource, id()); 77 msg->setObject("source", source); 78 msg->post(); 79} 80 81void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { 82 switch (msg->what()) { 83 case kWhatPause: { 84 mPaused = true; 85 break; 86 } 87 case kWhatResume: { 88 mPaused = false; 89 if (mPendingSeekTimeUs != kInvalidTimeUs) { 90 seekToAsync(mPendingSeekTimeUs); 91 mPendingSeekTimeUs = kInvalidTimeUs; 92 } else { 93 doRead(); 94 } 95 break; 96 } 97 case kWhatStart: { 98 sp<MediaPlayerBase> listener = mListener.promote(); 99 if (listener == NULL) { 100 ALOGE("Listener is NULL when kWhatStart is received."); 101 break; 102 } 103 mPaused = false; 104 mPendingSeekTimeUs = kInvalidTimeUs; 105 int32_t positionMs = 0; 106 listener->getCurrentPosition(&positionMs); 107 int64_t seekTimeUs = positionMs * 1000ll; 108 109 notifyListener(); 110 mSendSubtitleGeneration++; 111 doSeekAndRead(seekTimeUs); 112 break; 113 } 114 case kWhatRetryRead: { 115 int32_t generation = -1; 116 CHECK(msg->findInt32("generation", &generation)); 117 if (generation != mSendSubtitleGeneration) { 118 // Drop obsolete msg. 119 break; 120 } 121 int64_t seekTimeUs; 122 int seekMode; 123 if (msg->findInt64("seekTimeUs", &seekTimeUs) && 124 msg->findInt32("seekMode", &seekMode)) { 125 MediaSource::ReadOptions options; 126 options.setSeekTo( 127 seekTimeUs, 128 static_cast<MediaSource::ReadOptions::SeekMode>(seekMode)); 129 doRead(&options); 130 } else { 131 doRead(); 132 } 133 break; 134 } 135 case kWhatSeek: { 136 int64_t seekTimeUs = kInvalidTimeUs; 137 // Clear a displayed timed text before seeking. 138 notifyListener(); 139 msg->findInt64("seekTimeUs", &seekTimeUs); 140 if (seekTimeUs == kInvalidTimeUs) { 141 sp<MediaPlayerBase> listener = mListener.promote(); 142 if (listener != NULL) { 143 int32_t positionMs = 0; 144 listener->getCurrentPosition(&positionMs); 145 seekTimeUs = positionMs * 1000ll; 146 } 147 } 148 if (mPaused) { 149 mPendingSeekTimeUs = seekTimeUs; 150 break; 151 } 152 mSendSubtitleGeneration++; 153 doSeekAndRead(seekTimeUs); 154 break; 155 } 156 case kWhatSendSubtitle: { 157 int32_t generation; 158 CHECK(msg->findInt32("generation", &generation)); 159 if (generation != mSendSubtitleGeneration) { 160 // Drop obsolete msg. 161 break; 162 } 163 // If current time doesn't reach to the fire time, 164 // re-post the message with the adjusted delay time. 165 int64_t fireTimeUs = kInvalidTimeUs; 166 if (msg->findInt64("fireTimeUs", &fireTimeUs)) { 167 // TODO: check if fireTimeUs is not kInvalidTimeUs. 168 int64_t delayUs = delayUsFromCurrentTime(fireTimeUs); 169 if (delayUs > 0) { 170 msg->post(delayUs); 171 break; 172 } 173 } 174 sp<RefBase> obj; 175 if (msg->findObject("subtitle", &obj)) { 176 sp<ParcelEvent> parcelEvent; 177 parcelEvent = static_cast<ParcelEvent*>(obj.get()); 178 notifyListener(&(parcelEvent->parcel)); 179 doRead(); 180 } else { 181 notifyListener(); 182 } 183 break; 184 } 185 case kWhatSetSource: { 186 sp<RefBase> obj; 187 msg->findObject("source", &obj); 188 if (obj == NULL) break; 189 if (mSource != NULL) { 190 mSource->stop(); 191 } 192 mSource = static_cast<TimedTextSource*>(obj.get()); 193 status_t err = mSource->start(); 194 if (err != OK) { 195 notifyError(err); 196 break; 197 } 198 Parcel parcel; 199 err = mSource->extractGlobalDescriptions(&parcel); 200 if (err != OK) { 201 notifyError(err); 202 break; 203 } 204 notifyListener(&parcel); 205 break; 206 } 207 } 208} 209 210void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) { 211 MediaSource::ReadOptions options; 212 options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 213 doRead(&options); 214} 215 216void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { 217 int64_t startTimeUs = 0; 218 int64_t endTimeUs = 0; 219 sp<ParcelEvent> parcelEvent = new ParcelEvent(); 220 status_t err = mSource->read(&startTimeUs, &endTimeUs, 221 &(parcelEvent->parcel), options); 222 if (err == WOULD_BLOCK) { 223 sp<AMessage> msg = new AMessage(kWhatRetryRead, id()); 224 if (options != NULL) { 225 int64_t seekTimeUs = kInvalidTimeUs; 226 MediaSource::ReadOptions::SeekMode seekMode = 227 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC; 228 CHECK(options->getSeekTo(&seekTimeUs, &seekMode)); 229 msg->setInt64("seekTimeUs", seekTimeUs); 230 msg->setInt32("seekMode", seekMode); 231 } 232 msg->setInt32("generation", mSendSubtitleGeneration); 233 msg->post(kWaitTimeUsToRetryRead); 234 return; 235 } else if (err != OK) { 236 notifyError(err); 237 return; 238 } 239 240 postTextEvent(parcelEvent, startTimeUs); 241 if (endTimeUs > 0) { 242 CHECK_GE(endTimeUs, startTimeUs); 243 // send an empty timed text to clear the subtitle when it reaches to the 244 // end time. 245 postTextEvent(NULL, endTimeUs); 246 } 247} 248 249void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { 250 int64_t delayUs = delayUsFromCurrentTime(timeUs); 251 sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); 252 msg->setInt32("generation", mSendSubtitleGeneration); 253 if (parcel != NULL) { 254 msg->setObject("subtitle", parcel); 255 } 256 msg->setInt64("fireTimeUs", timeUs); 257 msg->post(delayUs); 258} 259 260int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) { 261 sp<MediaPlayerBase> listener = mListener.promote(); 262 if (listener == NULL) { 263 // TODO: it may be better to return kInvalidTimeUs 264 ALOGE("%s: Listener is NULL. (fireTimeUs = %lld)", 265 __FUNCTION__, fireTimeUs); 266 return 0; 267 } 268 int32_t positionMs = 0; 269 listener->getCurrentPosition(&positionMs); 270 int64_t positionUs = positionMs * 1000ll; 271 272 if (fireTimeUs <= positionUs + kAdjustmentProcessingTimeUs) { 273 return 0; 274 } else { 275 int64_t delayUs = fireTimeUs - positionUs - kAdjustmentProcessingTimeUs; 276 if (delayUs > kMaxDelayUs) { 277 return kMaxDelayUs; 278 } 279 return delayUs; 280 } 281} 282 283void TimedTextPlayer::notifyError(int error) { 284 sp<MediaPlayerBase> listener = mListener.promote(); 285 if (listener == NULL) { 286 ALOGE("%s(error=%d): Listener is NULL.", __FUNCTION__, error); 287 return; 288 } 289 listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error); 290} 291 292void TimedTextPlayer::notifyListener(const Parcel *parcel) { 293 sp<MediaPlayerBase> listener = mListener.promote(); 294 if (listener == NULL) { 295 ALOGE("%s: Listener is NULL.", __FUNCTION__); 296 return; 297 } 298 if (parcel != NULL && (parcel->dataSize() > 0)) { 299 listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel); 300 } else { // send an empty timed text to clear the screen 301 listener->sendEvent(MEDIA_TIMED_TEXT); 302 } 303} 304 305} // namespace android 306