summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/timedtext/TimedTextPlayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/timedtext/TimedTextPlayer.cpp')
-rw-r--r--media/libstagefright/timedtext/TimedTextPlayer.cpp157
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);
}
}