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 <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