TimedTextPlayer.cpp revision 4e1c91dd07fad96ee2387eda510c6da45e5dff0a
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 31namespace android { 32 33static const int64_t kAdjustmentProcessingTimeUs = 100000ll; 34static const int64_t kWaitTimeUsToRetryRead = 100000ll; 35 36TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener) 37 : mListener(listener), 38 mSource(NULL), 39 mSendSubtitleGeneration(0) { 40} 41 42TimedTextPlayer::~TimedTextPlayer() { 43 if (mSource != NULL) { 44 mSource->stop(); 45 mSource.clear(); 46 mSource = NULL; 47 } 48} 49 50void TimedTextPlayer::start() { 51 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 52 msg->setInt64("seekTimeUs", -1); 53 msg->post(); 54} 55 56void TimedTextPlayer::pause() { 57 (new AMessage(kWhatPause, id()))->post(); 58} 59 60void TimedTextPlayer::seekToAsync(int64_t timeUs) { 61 sp<AMessage> msg = new AMessage(kWhatSeek, id()); 62 msg->setInt64("seekTimeUs", timeUs); 63 msg->post(); 64} 65 66void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) { 67 sp<AMessage> msg = new AMessage(kWhatSetSource, id()); 68 msg->setObject("source", source); 69 msg->post(); 70} 71 72void 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 msg->findObject("subtitle", &obj); 116 if (obj != NULL) { 117 sp<ParcelEvent> parcelEvent; 118 parcelEvent = static_cast<ParcelEvent*>(obj.get()); 119 notifyListener(&(parcelEvent->parcel)); 120 } else { 121 notifyListener(); 122 } 123 doRead(); 124 break; 125 } 126 case kWhatSetSource: { 127 sp<RefBase> obj; 128 msg->findObject("source", &obj); 129 if (obj == NULL) break; 130 if (mSource != NULL) { 131 mSource->stop(); 132 } 133 mSource = static_cast<TimedTextSource*>(obj.get()); 134 status_t err = mSource->start(); 135 if (err != OK) { 136 notifyError(err); 137 break; 138 } 139 Parcel parcel; 140 err = mSource->extractGlobalDescriptions(&parcel); 141 if (err != OK) { 142 notifyError(err); 143 break; 144 } 145 notifyListener(&parcel); 146 break; 147 } 148 } 149} 150 151void TimedTextPlayer::doSeekAndRead(int64_t seekTimeUs) { 152 MediaSource::ReadOptions options; 153 options.setSeekTo(seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); 154 doRead(&options); 155} 156 157void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { 158 int64_t startTimeUs = 0; 159 int64_t endTimeUs = 0; 160 sp<ParcelEvent> parcelEvent = new ParcelEvent(); 161 status_t err = mSource->read(&startTimeUs, &endTimeUs, 162 &(parcelEvent->parcel), options); 163 if (err == WOULD_BLOCK) { 164 sp<AMessage> msg = new AMessage(kWhatRetryRead); 165 if (options != NULL) { 166 int64_t seekTimeUs; 167 MediaSource::ReadOptions::SeekMode seekMode; 168 CHECK(options->getSeekTo(&seekTimeUs, &seekMode)); 169 msg->setInt64("seekTimeUs", seekTimeUs); 170 msg->setInt32("seekMode", seekMode); 171 } 172 msg->post(kWaitTimeUsToRetryRead); 173 return; 174 } else if (err != OK) { 175 notifyError(err); 176 return; 177 } 178 179 postTextEvent(parcelEvent, startTimeUs); 180 if (endTimeUs > 0) { 181 CHECK_GE(endTimeUs, startTimeUs); 182 // send an empty timed text to clear the subtitle when it reaches to the 183 // end time. 184 postTextEvent(NULL, endTimeUs); 185 } 186} 187 188void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { 189 sp<MediaPlayerBase> listener = mListener.promote(); 190 if (listener != NULL) { 191 int64_t positionUs, delayUs; 192 int32_t positionMs = 0; 193 listener->getCurrentPosition(&positionMs); 194 positionUs = positionMs * 1000ll; 195 196 if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) { 197 delayUs = 0; 198 } else { 199 delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs; 200 } 201 postTextEventDelayUs(parcel, delayUs); 202 } 203} 204 205void TimedTextPlayer::postTextEventDelayUs(const sp<ParcelEvent>& parcel, int64_t delayUs) { 206 sp<MediaPlayerBase> listener = mListener.promote(); 207 if (listener != NULL) { 208 sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); 209 msg->setInt32("generation", mSendSubtitleGeneration); 210 if (parcel != NULL) { 211 msg->setObject("subtitle", parcel); 212 } 213 msg->post(delayUs); 214 } 215} 216 217void TimedTextPlayer::notifyError(int error) { 218 sp<MediaPlayerBase> listener = mListener.promote(); 219 if (listener != NULL) { 220 listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error); 221 } 222} 223 224void TimedTextPlayer::notifyListener(const Parcel *parcel) { 225 sp<MediaPlayerBase> listener = mListener.promote(); 226 if (listener != NULL) { 227 if (parcel != NULL && (parcel->dataSize() > 0)) { 228 listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel); 229 } else { // send an empty timed text to clear the screen 230 listener->sendEvent(MEDIA_TIMED_TEXT); 231 } 232 } 233} 234 235} // namespace android 236