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