diff options
Diffstat (limited to 'media/libstagefright/timedtext/TimedTextPlayer.cpp')
-rw-r--r-- | media/libstagefright/timedtext/TimedTextPlayer.cpp | 157 |
1 files changed, 119 insertions, 38 deletions
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp index 9b001cc..9fb0afe 100644 --- a/media/libstagefright/timedtext/TimedTextPlayer.cpp +++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "TimedTextPlayer" #include <utils/Log.h> +#include <limits.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> #include <media/stagefright/timedtext/TimedTextDriver.h> @@ -30,12 +31,18 @@ namespace android { +// Event should be fired a bit earlier considering the processing time till +// application actually gets the notification message. static const int64_t kAdjustmentProcessingTimeUs = 100000ll; +static const int64_t kMaxDelayUs = 5000000ll; static const int64_t kWaitTimeUsToRetryRead = 100000ll; +static const int64_t kInvalidTimeUs = INT_MIN; TimedTextPlayer::TimedTextPlayer(const wp<MediaPlayerBase> &listener) : mListener(listener), mSource(NULL), + mPendingSeekTimeUs(kInvalidTimeUs), + mPaused(false), mSendSubtitleGeneration(0) { } @@ -48,15 +55,17 @@ TimedTextPlayer::~TimedTextPlayer() { } void TimedTextPlayer::start() { - sp<AMessage> msg = new AMessage(kWhatSeek, id()); - msg->setInt64("seekTimeUs", -1); - msg->post(); + (new AMessage(kWhatStart, id()))->post(); } void TimedTextPlayer::pause() { (new AMessage(kWhatPause, id()))->post(); } +void TimedTextPlayer::resume() { + (new AMessage(kWhatResume, id()))->post(); +} + void TimedTextPlayer::seekToAsync(int64_t timeUs) { sp<AMessage> msg = new AMessage(kWhatSeek, id()); msg->setInt64("seekTimeUs", timeUs); @@ -72,10 +81,43 @@ void TimedTextPlayer::setDataSource(sp<TimedTextSource> source) { void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatPause: { + mPaused = true; + break; + } + case kWhatResume: { + mPaused = false; + if (mPendingSeekTimeUs != kInvalidTimeUs) { + seekToAsync(mPendingSeekTimeUs); + mPendingSeekTimeUs = kInvalidTimeUs; + } else { + doRead(); + } + break; + } + case kWhatStart: { + sp<MediaPlayerBase> listener = mListener.promote(); + if (listener == NULL) { + ALOGE("Listener is NULL when kWhatStart is received."); + break; + } + mPaused = false; + mPendingSeekTimeUs = kInvalidTimeUs; + int32_t positionMs = 0; + listener->getCurrentPosition(&positionMs); + int64_t seekTimeUs = positionMs * 1000ll; + + notifyListener(); mSendSubtitleGeneration++; + doSeekAndRead(seekTimeUs); break; } case kWhatRetryRead: { + int32_t generation = -1; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mSendSubtitleGeneration) { + // Drop obsolete msg. + break; + } int64_t seekTimeUs; int seekMode; if (msg->findInt64("seekTimeUs", &seekTimeUs) && @@ -91,9 +133,11 @@ void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { break; } case kWhatSeek: { - int64_t seekTimeUs = 0; + int64_t seekTimeUs = kInvalidTimeUs; + // Clear a displayed timed text before seeking. + notifyListener(); msg->findInt64("seekTimeUs", &seekTimeUs); - if (seekTimeUs < 0) { + if (seekTimeUs == kInvalidTimeUs) { sp<MediaPlayerBase> listener = mListener.promote(); if (listener != NULL) { int32_t positionMs = 0; @@ -101,6 +145,11 @@ void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { seekTimeUs = positionMs * 1000ll; } } + if (mPaused) { + mPendingSeekTimeUs = seekTimeUs; + break; + } + mSendSubtitleGeneration++; doSeekAndRead(seekTimeUs); break; } @@ -108,8 +157,19 @@ void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { int32_t generation; CHECK(msg->findInt32("generation", &generation)); if (generation != mSendSubtitleGeneration) { - // Drop obsolete msg. - break; + // Drop obsolete msg. + break; + } + // If current time doesn't reach to the fire time, + // re-post the message with the adjusted delay time. + int64_t fireTimeUs = kInvalidTimeUs; + if (msg->findInt64("fireTimeUs", &fireTimeUs)) { + // TODO: check if fireTimeUs is not kInvalidTimeUs. + int64_t delayUs = delayUsFromCurrentTime(fireTimeUs); + if (delayUs > 0) { + msg->post(delayUs); + break; + } } sp<RefBase> obj; if (msg->findObject("subtitle", &obj)) { @@ -123,11 +183,20 @@ void TimedTextPlayer::onMessageReceived(const sp<AMessage> &msg) { break; } case kWhatSetSource: { + mSendSubtitleGeneration++; sp<RefBase> obj; msg->findObject("source", &obj); - if (obj == NULL) break; if (mSource != NULL) { mSource->stop(); + mSource.clear(); + mSource = NULL; + } + // null source means deselect track. + if (obj == NULL) { + mPendingSeekTimeUs = kInvalidTimeUs; + mPaused = false; + notifyListener(); + break; } mSource = static_cast<TimedTextSource*>(obj.get()); status_t err = mSource->start(); @@ -157,17 +226,20 @@ void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { int64_t startTimeUs = 0; int64_t endTimeUs = 0; sp<ParcelEvent> parcelEvent = new ParcelEvent(); + CHECK(mSource != NULL); status_t err = mSource->read(&startTimeUs, &endTimeUs, &(parcelEvent->parcel), options); if (err == WOULD_BLOCK) { sp<AMessage> msg = new AMessage(kWhatRetryRead, id()); if (options != NULL) { - int64_t seekTimeUs; - MediaSource::ReadOptions::SeekMode seekMode; + int64_t seekTimeUs = kInvalidTimeUs; + MediaSource::ReadOptions::SeekMode seekMode = + MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC; CHECK(options->getSeekTo(&seekTimeUs, &seekMode)); msg->setInt64("seekTimeUs", seekTimeUs); msg->setInt32("seekMode", seekMode); } + msg->setInt32("generation", mSendSubtitleGeneration); msg->post(kWaitTimeUsToRetryRead); return; } else if (err != OK) { @@ -185,49 +257,58 @@ void TimedTextPlayer::doRead(MediaSource::ReadOptions* options) { } void TimedTextPlayer::postTextEvent(const sp<ParcelEvent>& parcel, int64_t timeUs) { - sp<MediaPlayerBase> listener = mListener.promote(); - if (listener != NULL) { - int64_t positionUs, delayUs; - int32_t positionMs = 0; - listener->getCurrentPosition(&positionMs); - positionUs = positionMs * 1000ll; - - if (timeUs <= positionUs + kAdjustmentProcessingTimeUs) { - delayUs = 0; - } else { - delayUs = timeUs - positionUs - kAdjustmentProcessingTimeUs; - } - postTextEventDelayUs(parcel, delayUs); + int64_t delayUs = delayUsFromCurrentTime(timeUs); + sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); + msg->setInt32("generation", mSendSubtitleGeneration); + if (parcel != NULL) { + msg->setObject("subtitle", parcel); } + msg->setInt64("fireTimeUs", timeUs); + msg->post(delayUs); } -void TimedTextPlayer::postTextEventDelayUs(const sp<ParcelEvent>& parcel, int64_t delayUs) { +int64_t TimedTextPlayer::delayUsFromCurrentTime(int64_t fireTimeUs) { sp<MediaPlayerBase> listener = mListener.promote(); - if (listener != NULL) { - sp<AMessage> msg = new AMessage(kWhatSendSubtitle, id()); - msg->setInt32("generation", mSendSubtitleGeneration); - if (parcel != NULL) { - msg->setObject("subtitle", parcel); + if (listener == NULL) { + // TODO: it may be better to return kInvalidTimeUs + ALOGE("%s: Listener is NULL. (fireTimeUs = %lld)", + __FUNCTION__, fireTimeUs); + return 0; + } + int32_t positionMs = 0; + listener->getCurrentPosition(&positionMs); + int64_t positionUs = positionMs * 1000ll; + + if (fireTimeUs <= positionUs + kAdjustmentProcessingTimeUs) { + return 0; + } else { + int64_t delayUs = fireTimeUs - positionUs - kAdjustmentProcessingTimeUs; + if (delayUs > kMaxDelayUs) { + return kMaxDelayUs; } - msg->post(delayUs); + return delayUs; } } void TimedTextPlayer::notifyError(int error) { sp<MediaPlayerBase> listener = mListener.promote(); - if (listener != NULL) { - listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error); + if (listener == NULL) { + ALOGE("%s(error=%d): Listener is NULL.", __FUNCTION__, error); + return; } + listener->sendEvent(MEDIA_INFO, MEDIA_INFO_TIMED_TEXT_ERROR, error); } void TimedTextPlayer::notifyListener(const Parcel *parcel) { sp<MediaPlayerBase> listener = mListener.promote(); - if (listener != NULL) { - if (parcel != NULL && (parcel->dataSize() > 0)) { - listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel); - } else { // send an empty timed text to clear the screen - listener->sendEvent(MEDIA_TIMED_TEXT); - } + if (listener == NULL) { + ALOGE("%s: Listener is NULL.", __FUNCTION__); + return; + } + if (parcel != NULL && (parcel->dataSize() > 0)) { + listener->sendEvent(MEDIA_TIMED_TEXT, 0, 0, parcel); + } else { // send an empty timed text to clear the screen + listener->sendEvent(MEDIA_TIMED_TEXT); } } |