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