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 <media/stagefright/foundation/ADebug.h> 22 #include <media/stagefright/foundation/AMessage.h> 23 #include <media/stagefright/timedtext/TimedTextDriver.h> 24 #include <media/stagefright/MediaErrors.h> 25 #include <media/MediaPlayerInterface.h> 26 27 #include "TimedTextPlayer.h" 28 29 #include "TimedTextSource.h" 30 31 namespace android { 32 33 static const int64_t kAdjustmentProcessingTimeUs = 100000ll; 34 static const int64_t kWaitTimeUsToRetryRead = 100000ll; 35 36 TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener) 37 : mListener(listener), 38 mSource(NULL), 39 mSendSubtitleGeneration(0) { 40 } 41 42 TimedTextPlayer::~TimedTextPlayer() { 43 if (mSource != NULL) { 44 mSource->stop(); 45 mSource.clear(); 46 mSource = NULL; 47 } 48 } 49 50 void TimedTextPlayer::start() { 51 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 52 msg->setInt64("seekTimeUs", -1); 53 msg->post(); 54 } 55 56 void TimedTextPlayer::pause() { 57 (new AMessage(kWhatPause, id()))->post(); 58 } 59 60 void TimedTextPlayer::seekToAsync(int64_t timeUs) { 61 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 62 msg->setInt64("seekTimeUs", timeUs); 63 msg->post(); 64 } 65 66 void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) { 67 sp<AMessage> msg = new AMessage(kWhatSetSource, id()); 68 msg->setObject("source", source); 69 msg->post(); 70 } 71 72 void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { 73 switch (msg->what()) { 74 case kWhatPause: { 75 mSendSubtitleGeneration++; 76 break; 77 } 78 case kWhatRetryRead: { 79 int64_t seekTimeUs; 80 int seekMode; 81 if (msg->findInt64("seekTimeUs", &seekTimeUs) && 82 msg->findInt32("seekMode", &seekMode)) { 83 MediaSource::ReadOptions options; 84 options.setSeekTo( 85 seekTimeUs, 86 static_cast<MediaSource::ReadOptions::SeekMode>(seekMode)); 87 doRead(&options); 88 } else { 89 doRead(); 90 } 91 break; 92 } 93 case kWhatSeek: { 94 int64_t seekTimeUs = 0; 95 msg->findInt64("seekTimeUs", &seekTimeUs); 96 if (seekTimeUs < 0) { 97 sp<MediaPlayerBase> listener = mListener.promote(); 98 if (listener != NULL) { 99 int32_t positionMs = 0; 100 listener->getCurrentPosition(&positionMs); 101 seekTimeUs = positionMs * 1000ll; 102 } 103 } 104 doSeekAndRead(seekTimeUs); 105 break; 106 } 107 case kWhatSendSubtitle: { 108 int32_t generation; 109 CHECK(msg->findInt32("generation", &generation)); 110 if (generation != mSendSubtitleGeneration) { 111 // Drop obsolete msg. 112 break; 113 } 114 sp<RefBase> obj; 115 if (msg->findObject("subtitle", &obj)) { 116 sp<ParcelEvent> parcelEvent; 117 parcelEvent = static_cast<ParcelEvent*>(obj.get()); 118 notifyListener(&(parcelEvent->parcel)); 119 doRead(); 120 } else { 121 notifyListener(); 122 } 123 break; 124 } 125 case kWhatSetSource: { 126 sp<RefBase> obj; 127 msg->findObject("source", &obj); 128 if (obj == NULL) break; 129 if (mSource != NULL) { 130 mSource->stop(); 131 } 132 mSource = static_cast<TimedTextSource*>(obj.get()); 133 status_t err = mSource->start(); 134 if (err != OK) { 135 notifyError(err); 136 break; 137 } 138 Parcel parcel; 139 err = mSource->extractGlobalDescriptions(&parcel); 140 if (err != OK) { 141 notifyError(err); 142 break; 143 } 144 notifyListener(&parcel); 145 break; 146 } 147 } 148 } 149 150 void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) { 151 MediaSource::ReadOptions options; 152 options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 153 doRead(&options); 154 } 155 156 void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { 157 int64_t startTimeUs = 0; 158 int64_t endTimeUs = 0; 159 sp<ParcelEvent> parcelEvent = new ParcelEvent(); 160 status_t err = mSource->read(&startTimeUs, &endTimeUs, 161 &(parcelEvent->parcel), options); 162 if (err == WOULD_BLOCK) { 163 sp<AMessage> msg = new AMessage(kWhatRetryRead, id()); 164 if (options != NULL) { 165 int64_t seekTimeUs; 166 MediaSource::ReadOptions::SeekMode seekMode; 167 CHECK(options->getSeekTo(&seekTimeUs, &seekMode)); 168 msg->setInt64("seekTimeUs", seekTimeUs); 169 msg->setInt32("seekMode", seekMode); 170 } 171 msg->post(kWaitTimeUsToRetryRead); 172 return; 173 } else if (err != OK) { 174 notifyError(err); 175 return; 176 } 177 178 postTextEvent(parcelEvent, startTimeUs); 179 if (endTimeUs > 0) { 180 CHECK_GE(endTimeUs, startTimeUs); 181 // send an empty timed text to clear the subtitle when it reaches to the 182 // end time. 183 postTextEvent(NULL, endTimeUs); 184 } 185 } 186 187 void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { 188 sp<MediaPlayerBase> listener = mListener.promote(); 189 if (listener != NULL) { 190 int64_t positionUs, delayUs; 191 int32_t positionMs = 0; 192 listener->getCurrentPosition(&positionMs); 193 positionUs = positionMs * 1000ll; 194 195 if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) { 196 delayUs = 0; 197 } else { 198 delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs; 199 } 200 postTextEventDelayUs(parcel, delayUs); 201 } 202 } 203 204 void TimedTextPlayer::postTextEventDelayUs(const sp<ParcelEvent>& parcel, int64_t delayUs) { 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->post(delayUs); 213 } 214 } 215 216 void TimedTextPlayer::notifyError(int error) { 217 sp<MediaPlayerBase> listener = mListener.promote(); 218 if (listener != NULL) { 219 listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error); 220 } 221 } 222 223 void TimedTextPlayer::notifyListener(const Parcel *parcel) { 224 sp<MediaPlayerBase> listener = mListener.promote(); 225 if (listener != NULL) { 226 if (parcel != NULL && (parcel->dataSize() > 0)) { 227 listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel); 228 } else { // send an empty timed text to clear the screen 229 listener->sendEvent(MEDIA_TIMED_TEXT); 230 } 231 } 232 } 233 234 } // namespace android 235