TimedTextPlayer.cpp revision bb6bc8491fe819f96e1902e56694715cb110ce94
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 mSendSubtitleGeneration(0) { 45} 46 47TimedTextPlayer::~TimedTextPlayer() { 48 if (mSource != NULL) { 49 mSource->stop(); 50 mSource.clear(); 51 mSource = NULL; 52 } 53} 54 55void TimedTextPlayer::start() { 56 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 57 msg->setInt64("seekTimeUs", kInvalidTimeUs); 58 msg->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 mSendSubtitleGeneration++; 85 break; 86 } 87 case kWhatResume: { 88 doRead(); 89 break; 90 } 91 case kWhatRetryRead: { 92 int32_t generation = -1; 93 CHECK(msg->findInt32("generation", &generation)); 94 if (generation != mSendSubtitleGeneration) { 95 // Drop obsolete msg. 96 break; 97 } 98 int64_t seekTimeUs; 99 int seekMode; 100 if (msg->findInt64("seekTimeUs", &seekTimeUs) && 101 msg->findInt32("seekMode", &seekMode)) { 102 MediaSource::ReadOptions options; 103 options.setSeekTo( 104 seekTimeUs, 105 static_cast<MediaSource::ReadOptions::SeekMode>(seekMode)); 106 doRead(&options); 107 } else { 108 doRead(); 109 } 110 break; 111 } 112 case kWhatSeek: { 113 mSendSubtitleGeneration++; 114 int64_t seekTimeUs = kInvalidTimeUs; 115 // Clear a displayed timed text before seeking. 116 notifyListener(); 117 msg->findInt64("seekTimeUs", &seekTimeUs); 118 if (seekTimeUs == kInvalidTimeUs) { 119 sp<MediaPlayerBase> listener = mListener.promote(); 120 if (listener != NULL) { 121 int32_t positionMs = 0; 122 listener->getCurrentPosition(&positionMs); 123 seekTimeUs = positionMs * 1000ll; 124 } 125 } 126 doSeekAndRead(seekTimeUs); 127 break; 128 } 129 case kWhatSendSubtitle: { 130 int32_t generation; 131 CHECK(msg->findInt32("generation", &generation)); 132 if (generation != mSendSubtitleGeneration) { 133 // Drop obsolete msg. 134 break; 135 } 136 // If current time doesn't reach to the fire time, 137 // re-post the message with the adjusted delay time. 138 int64_t fireTimeUs = kInvalidTimeUs; 139 if (msg->findInt64("fireTimeUs", &fireTimeUs)) { 140 // TODO: check if fireTimeUs is not kInvalidTimeUs. 141 int64_t delayUs = delayUsFromCurrentTime(fireTimeUs); 142 if (delayUs > 0) { 143 msg->post(delayUs); 144 break; 145 } 146 } 147 sp<RefBase> obj; 148 if (msg->findObject("subtitle", &obj)) { 149 sp<ParcelEvent> parcelEvent; 150 parcelEvent = static_cast<ParcelEvent*>(obj.get()); 151 notifyListener(&(parcelEvent->parcel)); 152 doRead(); 153 } else { 154 notifyListener(); 155 } 156 break; 157 } 158 case kWhatSetSource: { 159 sp<RefBase> obj; 160 msg->findObject("source", &obj); 161 if (obj == NULL) break; 162 if (mSource != NULL) { 163 mSource->stop(); 164 } 165 mSource = static_cast<TimedTextSource*>(obj.get()); 166 status_t err = mSource->start(); 167 if (err != OK) { 168 notifyError(err); 169 break; 170 } 171 Parcel parcel; 172 err = mSource->extractGlobalDescriptions(&parcel); 173 if (err != OK) { 174 notifyError(err); 175 break; 176 } 177 notifyListener(&parcel); 178 break; 179 } 180 } 181} 182 183void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) { 184 MediaSource::ReadOptions options; 185 options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 186 doRead(&options); 187} 188 189void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { 190 int64_t startTimeUs = 0; 191 int64_t endTimeUs = 0; 192 sp<ParcelEvent> parcelEvent = new ParcelEvent(); 193 status_t err = mSource->read(&startTimeUs, &endTimeUs, 194 &(parcelEvent->parcel), options); 195 if (err == WOULD_BLOCK) { 196 sp<AMessage> msg = new AMessage(kWhatRetryRead, id()); 197 if (options != NULL) { 198 int64_t seekTimeUs = kInvalidTimeUs; 199 MediaSource::ReadOptions::SeekMode seekMode = 200 MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC; 201 CHECK(options->getSeekTo(&seekTimeUs, &seekMode)); 202 msg->setInt64("seekTimeUs", seekTimeUs); 203 msg->setInt32("seekMode", seekMode); 204 } 205 msg->setInt32("generation", mSendSubtitleGeneration); 206 msg->post(kWaitTimeUsToRetryRead); 207 return; 208 } else if (err != OK) { 209 notifyError(err); 210 return; 211 } 212 213 postTextEvent(parcelEvent, startTimeUs); 214 if (endTimeUs > 0) { 215 CHECK_GE(endTimeUs, startTimeUs); 216 // send an empty timed text to clear the subtitle when it reaches to the 217 // end time. 218 postTextEvent(NULL, endTimeUs); 219 } 220} 221 222void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { 223 int64_t delayUs = delayUsFromCurrentTime(timeUs); 224 sp<MediaPlayerBase> listener = mListener.promote(); 225 if (listener != NULL) { 226 sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); 227 msg->setInt32("generation", mSendSubtitleGeneration); 228 if (parcel != NULL) { 229 msg->setObject("subtitle", parcel); 230 } 231 msg->setInt64("fireTimeUs", timeUs); 232 msg->post(delayUs); 233 } 234} 235 236int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) { 237 sp<MediaPlayerBase> listener = mListener.promote(); 238 if (listener == NULL) { 239 // TODO: it may be better to return kInvalidTimeUs 240 return 0; 241 } 242 int32_t positionMs = 0; 243 listener->getCurrentPosition(&positionMs); 244 int64_t positionUs = positionMs * 1000ll; 245 246 if (fireTimeUs <= positionUs + kAdjustmentProcessingTimeUs) { 247 return 0; 248 } else { 249 int64_t delayUs = fireTimeUs - positionUs - kAdjustmentProcessingTimeUs; 250 if (delayUs > kMaxDelayUs) { 251 return kMaxDelayUs; 252 } 253 return delayUs; 254 } 255} 256 257void TimedTextPlayer::notifyError(int error) { 258 sp<MediaPlayerBase> listener = mListener.promote(); 259 if (listener != NULL) { 260 listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error); 261 } 262} 263 264void TimedTextPlayer::notifyListener(const Parcel *parcel) { 265 sp<MediaPlayerBase> listener = mListener.promote(); 266 if (listener != NULL) { 267 if (parcel != NULL && (parcel->dataSize() > 0)) { 268 listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel); 269 } else { // send an empty timed text to clear the screen 270 listener->sendEvent(MEDIA_TIMED_TEXT); 271 } 272 } 273} 274 275} // namespace android 276