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