Loading media/libstagefright/timedtext/TimedTextSRTSource.cpp +28 −26 Original line number Original line Diff line number Diff line Loading @@ -79,6 +79,10 @@ status_t TimedTextSRTSource::read( return OK; return OK; } } sp<MetaData> TimedTextSRTSource::getFormat() { return mMetaData; } status_t TimedTextSRTSource::scanFile() { status_t TimedTextSRTSource::scanFile() { off64_t offset = 0; off64_t offset = 0; int64_t startTimeUs; int64_t startTimeUs; Loading Loading @@ -155,20 +159,18 @@ status_t TimedTextSRTSource::getNextSubtitleInfo( while (needMoreData) { while (needMoreData) { if ((err = readNextLine(offset, &data)) != OK) { if ((err = readNextLine(offset, &data)) != OK) { if (err == ERROR_END_OF_STREAM) { if (err == ERROR_END_OF_STREAM) { needMoreData = false; break; } else { } else { return err; return err; } } } } if (needMoreData) { data.trim(); data.trim(); if (data.empty()) { if (data.empty()) { // it's an empty line used to separate two subtitles // it's an empty line used to separate two subtitles needMoreData = false; needMoreData = false; } } } } } info->textLen = *offset - info->offset; info->textLen = *offset - info->offset; return OK; return OK; } } Loading Loading @@ -221,35 +223,24 @@ status_t TimedTextSRTSource::getText( if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { int64_t lastEndTimeUs = int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; int64_t firstStartTimeUs = mTextVector.keyAt(0); if (seekTimeUs < 0) { if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) { return ERROR_OUT_OF_RANGE; return ERROR_OUT_OF_RANGE; } else if (seekTimeUs < firstStartTimeUs) { } else if (seekTimeUs >= lastEndTimeUs) { mIndex = 0; return ERROR_END_OF_STREAM; } else { } else { // binary search // binary search size_t low = 0; size_t low = 0; size_t high = mTextVector.size() - 1; size_t high = mTextVector.size() - 1; size_t mid = 0; size_t mid = 0; int64_t currTimeUs; while (low <= high) { while (low <= high) { mid = low + (high - low)/2; mid = low + (high - low)/2; currTimeUs = mTextVector.keyAt(mid); int diff = compareExtendedRangeAndTime(mid, seekTimeUs); const int64_t diffTime = currTimeUs - seekTimeUs; if (diff == 0) { if (diffTime == 0) { break; break; } else if (diffTime < 0) { } else if (diff < 0) { low = mid + 1; low = mid + 1; } else { } else { if ((high == mid + 1) && (seekTimeUs < mTextVector.keyAt(high))) { break; } if (mid < 1) { break; } high = mid - 1; high = mid - 1; } } } } Loading @@ -260,6 +251,7 @@ status_t TimedTextSRTSource::getText( if (mIndex >= mTextVector.size()) { if (mIndex >= mTextVector.size()) { return ERROR_END_OF_STREAM; return ERROR_END_OF_STREAM; } } const TextInfo &info = mTextVector.valueAt(mIndex); const TextInfo &info = mTextVector.valueAt(mIndex); *startTimeUs = mTextVector.keyAt(mIndex); *startTimeUs = mTextVector.keyAt(mIndex); *endTimeUs = info.endTimeUs; *endTimeUs = info.endTimeUs; Loading Loading @@ -289,8 +281,18 @@ status_t TimedTextSRTSource::extractAndAppendLocalDescriptions( return OK; return OK; } } sp<MetaData> TimedTextSRTSource::getFormat() { int TimedTextSRTSource::compareExtendedRangeAndTime(size_t index, int64_t timeUs) { return mMetaData; CHECK_LT(index, mTextVector.size()); int64_t endTimeUs = mTextVector.valueAt(index).endTimeUs; int64_t startTimeUs = (index > 0) ? mTextVector.valueAt(index - 1).endTimeUs : 0; if (timeUs >= startTimeUs && timeUs < endTimeUs) { return 0; } else if (endTimeUs <= timeUs) { return -1; } else { return 1; } } } } // namespace android } // namespace android media/libstagefright/timedtext/TimedTextSRTSource.h +19 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,25 @@ private: status_t extractAndAppendLocalDescriptions( status_t extractAndAppendLocalDescriptions( int64_t timeUs, const AString &text, Parcel *parcel); int64_t timeUs, const AString &text, Parcel *parcel); // Compares the time range of the subtitle at index to the given timeUs. // The time range of the subtitle to match with given timeUs is extended to // [endTimeUs of the previous subtitle, endTimeUs of current subtitle). // // This compare function is used to find a next subtitle when read() is // called with seek options. Note that timeUs within gap ranges, such as // [200, 300) in the below example, will be matched to the closest future // subtitle, [300, 400). // // For instance, assuming there are 3 subtitles in mTextVector, // 0: [100, 200) ----> [0, 200) // 1: [300, 400) ----> [200, 400) // 2: [500, 600) ----> [400, 600) // If the 'index' parameter contains 1, this function // returns 0, if timeUs is in [200, 400) // returns -1, if timeUs >= 400, // returns 1, if timeUs < 200. int compareExtendedRangeAndTime(size_t index, int64_t timeUs); DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource); DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource); }; }; Loading media/libstagefright/timedtext/test/Android.mk 0 → 100644 +27 −0 Original line number Original line Diff line number Diff line LOCAL_PATH:= $(call my-dir) # ================================================================ # Unit tests for libstagefright_timedtext # See also /development/testrunner/test_defs.xml # ================================================================ # ================================================================ # A test for TimedTextSRTSource # ================================================================ include $(CLEAR_VARS) LOCAL_MODULE := TimedTextSRTSource_test LOCAL_MODULE_TAGS := eng tests LOCAL_SRC_FILES := TimedTextSRTSource_test.cpp LOCAL_C_INCLUDES := \ $(TOP)/external/expat/lib \ $(TOP)/frameworks/base/media/libstagefright/timedtext LOCAL_SHARED_LIBRARIES := \ libexpat \ libstagefright include $(BUILD_NATIVE_TEST) media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp 0 → 100644 +224 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "TimedTextSRTSource_test" #include <utils/Log.h> #include <gtest/gtest.h> #include <binder/Parcel.h> #include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaErrors.h> #include <utils/misc.h> #include <TimedTextSource.h> #include <TimedTextSRTSource.h> namespace android { namespace test { static const int kSecToUsec = 1000000; static const int kSecToMsec = 1000; static const int kMsecToUsec = 1000; /* SRT format (http://en.wikipedia.org/wiki/SubRip) * Subtitle number * Start time --> End time * Text of subtitle (one or more lines) * Blank lines */ static const char *kSRTString = "1\n00:00:1,000 --> 00:00:1,500\n1\n\n" "2\n00:00:2,000 --> 00:00:2,500\n2\n\n" "3\n00:00:3,000 --> 00:00:3,500\n3\n\n" "4\n00:00:4,000 --> 00:00:4,500\n4\n\n" "5\n00:00:5,000 --> 00:00:5,500\n5\n\n" // edge case : previos end time = next start time "6\n00:00:5,500 --> 00:00:5,800\n6\n\n" "7\n00:00:5,800 --> 00:00:6,000\n7\n\n" "8\n00:00:6,000 --> 00:00:7,000\n8\n\n"; class SRTDataSourceStub : public DataSource { public: SRTDataSourceStub(const char *data, size_t size) : mData(data), mSize(size) {} virtual ~SRTDataSourceStub() {} virtual status_t initCheck() const { return OK; } virtual ssize_t readAt(off64_t offset, void *data, size_t size) { if (offset >= mSize) return 0; ssize_t avail = mSize - offset; if (avail > size) { avail = size; } memcpy(data, mData + offset, avail); return avail; } private: const char *mData; size_t mSize; }; class TimedTextSRTSourceTest : public testing::Test { protected: void SetUp() { sp<DataSource> stub= new SRTDataSourceStub( kSRTString, strlen(kSRTString)); mSource = new TimedTextSRTSource(stub); mSource->start(); } void CheckStartTimeMs(const Parcel& parcel, int32_t timeMs) { int32_t intval; parcel.setDataPosition(8); parcel.readInt32(&intval); EXPECT_EQ(timeMs, intval); } void CheckDataEquals(const Parcel& parcel, const char* content) { int32_t intval; parcel.setDataPosition(16); parcel.readInt32(&intval); parcel.setDataPosition(24); const char* data = (const char*) parcel.readInplace(intval); int32_t content_len = strlen(content); EXPECT_EQ(content_len, intval); EXPECT_TRUE(strncmp(data, content, content_len) == 0); } sp<TimedTextSource> mSource; int64_t startTimeUs; int64_t endTimeUs; Parcel parcel; AString subtitle; status_t err; }; TEST_F(TimedTextSRTSourceTest, readAll) { for (int i = 1; i <= 5; i++) { err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, i * kSecToMsec); subtitle = StringPrintf("%d\n\n", i); CheckDataEquals(parcel, subtitle.c_str()); } // read edge cases err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 5500); subtitle = StringPrintf("6\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 5800); subtitle = StringPrintf("7\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 6000); subtitle = StringPrintf("8\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(ERROR_END_OF_STREAM, err); } TEST_F(TimedTextSRTSourceTest, seekTimeIsEarlierThanFirst) { MediaSource::ReadOptions options; options.setSeekTo(500, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(1 * kSecToUsec, startTimeUs); CheckStartTimeMs(parcel, 1 * kSecToMsec); } TEST_F(TimedTextSRTSourceTest, seekTimeIsLaterThanLast) { MediaSource::ReadOptions options; options.setSeekTo(7 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(ERROR_END_OF_STREAM, err); options.setSeekTo(8 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(ERROR_END_OF_STREAM, err); } TEST_F(TimedTextSRTSourceTest, seekTimeIsMatched) { for (int i = 1; i <= 5; i++) { MediaSource::ReadOptions options; options.setSeekTo(i * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(i * kSecToUsec, startTimeUs); options.setSeekTo(i * kSecToUsec + 100, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(i * kSecToUsec, startTimeUs); } } TEST_F(TimedTextSRTSourceTest, seekTimeInBetweenTwo) { for (int i = 1; i <= 4; i++) { MediaSource::ReadOptions options; options.setSeekTo(i * kSecToUsec + 500000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs); options.setSeekTo(i * kSecToUsec + 600000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs); } } TEST_F(TimedTextSRTSourceTest, checkEdgeCase) { MediaSource::ReadOptions options; options.setSeekTo(5500 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(5500 * kMsecToUsec, startTimeUs); subtitle = StringPrintf("6\n\n"); CheckDataEquals(parcel, subtitle.c_str()); options.setSeekTo(5800 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(5800 * kMsecToUsec, startTimeUs); subtitle = StringPrintf("7\n\n"); CheckDataEquals(parcel, subtitle.c_str()); options.setSeekTo(6000 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(6000 * kMsecToUsec, startTimeUs); subtitle = StringPrintf("8\n\n"); CheckDataEquals(parcel, subtitle.c_str()); } } // namespace test } // namespace android Loading
media/libstagefright/timedtext/TimedTextSRTSource.cpp +28 −26 Original line number Original line Diff line number Diff line Loading @@ -79,6 +79,10 @@ status_t TimedTextSRTSource::read( return OK; return OK; } } sp<MetaData> TimedTextSRTSource::getFormat() { return mMetaData; } status_t TimedTextSRTSource::scanFile() { status_t TimedTextSRTSource::scanFile() { off64_t offset = 0; off64_t offset = 0; int64_t startTimeUs; int64_t startTimeUs; Loading Loading @@ -155,20 +159,18 @@ status_t TimedTextSRTSource::getNextSubtitleInfo( while (needMoreData) { while (needMoreData) { if ((err = readNextLine(offset, &data)) != OK) { if ((err = readNextLine(offset, &data)) != OK) { if (err == ERROR_END_OF_STREAM) { if (err == ERROR_END_OF_STREAM) { needMoreData = false; break; } else { } else { return err; return err; } } } } if (needMoreData) { data.trim(); data.trim(); if (data.empty()) { if (data.empty()) { // it's an empty line used to separate two subtitles // it's an empty line used to separate two subtitles needMoreData = false; needMoreData = false; } } } } } info->textLen = *offset - info->offset; info->textLen = *offset - info->offset; return OK; return OK; } } Loading Loading @@ -221,35 +223,24 @@ status_t TimedTextSRTSource::getText( if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) { int64_t lastEndTimeUs = int64_t lastEndTimeUs = mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; mTextVector.valueAt(mTextVector.size() - 1).endTimeUs; int64_t firstStartTimeUs = mTextVector.keyAt(0); if (seekTimeUs < 0) { if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) { return ERROR_OUT_OF_RANGE; return ERROR_OUT_OF_RANGE; } else if (seekTimeUs < firstStartTimeUs) { } else if (seekTimeUs >= lastEndTimeUs) { mIndex = 0; return ERROR_END_OF_STREAM; } else { } else { // binary search // binary search size_t low = 0; size_t low = 0; size_t high = mTextVector.size() - 1; size_t high = mTextVector.size() - 1; size_t mid = 0; size_t mid = 0; int64_t currTimeUs; while (low <= high) { while (low <= high) { mid = low + (high - low)/2; mid = low + (high - low)/2; currTimeUs = mTextVector.keyAt(mid); int diff = compareExtendedRangeAndTime(mid, seekTimeUs); const int64_t diffTime = currTimeUs - seekTimeUs; if (diff == 0) { if (diffTime == 0) { break; break; } else if (diffTime < 0) { } else if (diff < 0) { low = mid + 1; low = mid + 1; } else { } else { if ((high == mid + 1) && (seekTimeUs < mTextVector.keyAt(high))) { break; } if (mid < 1) { break; } high = mid - 1; high = mid - 1; } } } } Loading @@ -260,6 +251,7 @@ status_t TimedTextSRTSource::getText( if (mIndex >= mTextVector.size()) { if (mIndex >= mTextVector.size()) { return ERROR_END_OF_STREAM; return ERROR_END_OF_STREAM; } } const TextInfo &info = mTextVector.valueAt(mIndex); const TextInfo &info = mTextVector.valueAt(mIndex); *startTimeUs = mTextVector.keyAt(mIndex); *startTimeUs = mTextVector.keyAt(mIndex); *endTimeUs = info.endTimeUs; *endTimeUs = info.endTimeUs; Loading Loading @@ -289,8 +281,18 @@ status_t TimedTextSRTSource::extractAndAppendLocalDescriptions( return OK; return OK; } } sp<MetaData> TimedTextSRTSource::getFormat() { int TimedTextSRTSource::compareExtendedRangeAndTime(size_t index, int64_t timeUs) { return mMetaData; CHECK_LT(index, mTextVector.size()); int64_t endTimeUs = mTextVector.valueAt(index).endTimeUs; int64_t startTimeUs = (index > 0) ? mTextVector.valueAt(index - 1).endTimeUs : 0; if (timeUs >= startTimeUs && timeUs < endTimeUs) { return 0; } else if (endTimeUs <= timeUs) { return -1; } else { return 1; } } } } // namespace android } // namespace android
media/libstagefright/timedtext/TimedTextSRTSource.h +19 −0 Original line number Original line Diff line number Diff line Loading @@ -70,6 +70,25 @@ private: status_t extractAndAppendLocalDescriptions( status_t extractAndAppendLocalDescriptions( int64_t timeUs, const AString &text, Parcel *parcel); int64_t timeUs, const AString &text, Parcel *parcel); // Compares the time range of the subtitle at index to the given timeUs. // The time range of the subtitle to match with given timeUs is extended to // [endTimeUs of the previous subtitle, endTimeUs of current subtitle). // // This compare function is used to find a next subtitle when read() is // called with seek options. Note that timeUs within gap ranges, such as // [200, 300) in the below example, will be matched to the closest future // subtitle, [300, 400). // // For instance, assuming there are 3 subtitles in mTextVector, // 0: [100, 200) ----> [0, 200) // 1: [300, 400) ----> [200, 400) // 2: [500, 600) ----> [400, 600) // If the 'index' parameter contains 1, this function // returns 0, if timeUs is in [200, 400) // returns -1, if timeUs >= 400, // returns 1, if timeUs < 200. int compareExtendedRangeAndTime(size_t index, int64_t timeUs); DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource); DISALLOW_EVIL_CONSTRUCTORS(TimedTextSRTSource); }; }; Loading
media/libstagefright/timedtext/test/Android.mk 0 → 100644 +27 −0 Original line number Original line Diff line number Diff line LOCAL_PATH:= $(call my-dir) # ================================================================ # Unit tests for libstagefright_timedtext # See also /development/testrunner/test_defs.xml # ================================================================ # ================================================================ # A test for TimedTextSRTSource # ================================================================ include $(CLEAR_VARS) LOCAL_MODULE := TimedTextSRTSource_test LOCAL_MODULE_TAGS := eng tests LOCAL_SRC_FILES := TimedTextSRTSource_test.cpp LOCAL_C_INCLUDES := \ $(TOP)/external/expat/lib \ $(TOP)/frameworks/base/media/libstagefright/timedtext LOCAL_SHARED_LIBRARIES := \ libexpat \ libstagefright include $(BUILD_NATIVE_TEST)
media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp 0 → 100644 +224 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "TimedTextSRTSource_test" #include <utils/Log.h> #include <gtest/gtest.h> #include <binder/Parcel.h> #include <media/stagefright/foundation/AString.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/MediaErrors.h> #include <utils/misc.h> #include <TimedTextSource.h> #include <TimedTextSRTSource.h> namespace android { namespace test { static const int kSecToUsec = 1000000; static const int kSecToMsec = 1000; static const int kMsecToUsec = 1000; /* SRT format (http://en.wikipedia.org/wiki/SubRip) * Subtitle number * Start time --> End time * Text of subtitle (one or more lines) * Blank lines */ static const char *kSRTString = "1\n00:00:1,000 --> 00:00:1,500\n1\n\n" "2\n00:00:2,000 --> 00:00:2,500\n2\n\n" "3\n00:00:3,000 --> 00:00:3,500\n3\n\n" "4\n00:00:4,000 --> 00:00:4,500\n4\n\n" "5\n00:00:5,000 --> 00:00:5,500\n5\n\n" // edge case : previos end time = next start time "6\n00:00:5,500 --> 00:00:5,800\n6\n\n" "7\n00:00:5,800 --> 00:00:6,000\n7\n\n" "8\n00:00:6,000 --> 00:00:7,000\n8\n\n"; class SRTDataSourceStub : public DataSource { public: SRTDataSourceStub(const char *data, size_t size) : mData(data), mSize(size) {} virtual ~SRTDataSourceStub() {} virtual status_t initCheck() const { return OK; } virtual ssize_t readAt(off64_t offset, void *data, size_t size) { if (offset >= mSize) return 0; ssize_t avail = mSize - offset; if (avail > size) { avail = size; } memcpy(data, mData + offset, avail); return avail; } private: const char *mData; size_t mSize; }; class TimedTextSRTSourceTest : public testing::Test { protected: void SetUp() { sp<DataSource> stub= new SRTDataSourceStub( kSRTString, strlen(kSRTString)); mSource = new TimedTextSRTSource(stub); mSource->start(); } void CheckStartTimeMs(const Parcel& parcel, int32_t timeMs) { int32_t intval; parcel.setDataPosition(8); parcel.readInt32(&intval); EXPECT_EQ(timeMs, intval); } void CheckDataEquals(const Parcel& parcel, const char* content) { int32_t intval; parcel.setDataPosition(16); parcel.readInt32(&intval); parcel.setDataPosition(24); const char* data = (const char*) parcel.readInplace(intval); int32_t content_len = strlen(content); EXPECT_EQ(content_len, intval); EXPECT_TRUE(strncmp(data, content, content_len) == 0); } sp<TimedTextSource> mSource; int64_t startTimeUs; int64_t endTimeUs; Parcel parcel; AString subtitle; status_t err; }; TEST_F(TimedTextSRTSourceTest, readAll) { for (int i = 1; i <= 5; i++) { err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, i * kSecToMsec); subtitle = StringPrintf("%d\n\n", i); CheckDataEquals(parcel, subtitle.c_str()); } // read edge cases err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 5500); subtitle = StringPrintf("6\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 5800); subtitle = StringPrintf("7\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(OK, err); CheckStartTimeMs(parcel, 6000); subtitle = StringPrintf("8\n\n"); CheckDataEquals(parcel, subtitle.c_str()); err = mSource->read(&startTimeUs, &endTimeUs, &parcel); EXPECT_EQ(ERROR_END_OF_STREAM, err); } TEST_F(TimedTextSRTSourceTest, seekTimeIsEarlierThanFirst) { MediaSource::ReadOptions options; options.setSeekTo(500, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(1 * kSecToUsec, startTimeUs); CheckStartTimeMs(parcel, 1 * kSecToMsec); } TEST_F(TimedTextSRTSourceTest, seekTimeIsLaterThanLast) { MediaSource::ReadOptions options; options.setSeekTo(7 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(ERROR_END_OF_STREAM, err); options.setSeekTo(8 * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(ERROR_END_OF_STREAM, err); } TEST_F(TimedTextSRTSourceTest, seekTimeIsMatched) { for (int i = 1; i <= 5; i++) { MediaSource::ReadOptions options; options.setSeekTo(i * kSecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(i * kSecToUsec, startTimeUs); options.setSeekTo(i * kSecToUsec + 100, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(i * kSecToUsec, startTimeUs); } } TEST_F(TimedTextSRTSourceTest, seekTimeInBetweenTwo) { for (int i = 1; i <= 4; i++) { MediaSource::ReadOptions options; options.setSeekTo(i * kSecToUsec + 500000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs); options.setSeekTo(i * kSecToUsec + 600000, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ((i + 1) * kSecToUsec, startTimeUs); } } TEST_F(TimedTextSRTSourceTest, checkEdgeCase) { MediaSource::ReadOptions options; options.setSeekTo(5500 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(5500 * kMsecToUsec, startTimeUs); subtitle = StringPrintf("6\n\n"); CheckDataEquals(parcel, subtitle.c_str()); options.setSeekTo(5800 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(5800 * kMsecToUsec, startTimeUs); subtitle = StringPrintf("7\n\n"); CheckDataEquals(parcel, subtitle.c_str()); options.setSeekTo(6000 * kMsecToUsec, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); err = mSource->read(&startTimeUs, &endTimeUs, &parcel, &options); EXPECT_EQ(OK, err); EXPECT_EQ(6000 * kMsecToUsec, startTimeUs); subtitle = StringPrintf("8\n\n"); CheckDataEquals(parcel, subtitle.c_str()); } } // namespace test } // namespace android