Merge "Added number of encoded frames and duration to the MPEG4Writer::Track::dump"
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 059a8a5..872512a 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -155,6 +155,7 @@
const char CameraParameters::PIXEL_FORMAT_RGBA8888[] = "rgba8888";
const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg";
const char CameraParameters::PIXEL_FORMAT_BAYER_RGGB[] = "bayer-rggb";
+const char CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE[] = "android-opaque";
// Values for focus mode settings.
const char CameraParameters::FOCUS_MODE_AUTO[] = "auto";
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 36248a0..4d5aa36 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -606,6 +606,8 @@
// Raw bayer format used for images, which is 10 bit precision samples
// stored in 16 bit words. The filter pattern is RGGB.
static const char PIXEL_FORMAT_BAYER_RGGB[];
+ // Pixel format is not known to the framework
+ static const char PIXEL_FORMAT_ANDROID_OPAQUE[];
// Values for focus mode settings.
// Auto-focus mode. Applications should call
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 7e66ac9..156c592 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -84,8 +84,8 @@
* more bytes than indicated by 'size' field and update 'size' if less bytes are
* read.
* - EVENT_OVERRUN: unused.
- * - EVENT_MARKER: pointer to an uin32_t containing the marker position in frames.
- * - EVENT_NEW_POS: pointer to an uin32_t containing the new position in frames.
+ * - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames.
+ * - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames.
*/
typedef void (*callback_t)(int event, void* user, void *info);
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 1fad383..f7cebc5 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -99,6 +99,8 @@
// The player was started because it was used as the next player for another
// player, which just completed playback
MEDIA_INFO_STARTED_AS_NEXT = 2,
+ // The player just pushed the very first video frame for rendering
+ MEDIA_INFO_RENDERING_START = 3,
// 7xx
// The video is too complex for the decoder: it can't decode frames fast
// enough. Possibly only the audio plays fine at this stage.
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 9d5813b..5060525 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -355,7 +355,6 @@
uint32_t AudioRecord::getSampleRate() const
{
- AutoMutex lock(mLock);
return mCblk->sampleRate;
}
@@ -363,6 +362,7 @@
{
if (mCbf == NULL) return INVALID_OPERATION;
+ AutoMutex lock(mLock);
mMarkerPosition = marker;
mMarkerReached = false;
@@ -373,6 +373,7 @@
{
if (marker == NULL) return BAD_VALUE;
+ AutoMutex lock(mLock);
*marker = mMarkerPosition;
return NO_ERROR;
@@ -384,6 +385,8 @@
uint32_t curPosition;
getPosition(&curPosition);
+
+ AutoMutex lock(mLock);
mNewPosition = curPosition + updatePeriod;
mUpdatePeriod = updatePeriod;
@@ -394,6 +397,7 @@
{
if (updatePeriod == NULL) return BAD_VALUE;
+ AutoMutex lock(mLock);
*updatePeriod = mUpdatePeriod;
return NO_ERROR;
@@ -434,6 +438,7 @@
pid_t tid = -1;
// FIXME see similar logic at AudioTrack
+ int originalSessionId = mSessionId;
sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), input,
sampleRate, format,
channelMask,
@@ -442,6 +447,8 @@
tid,
&mSessionId,
&status);
+ ALOGE_IF(originalSessionId != 0 && mSessionId != originalSessionId,
+ "session ID changed from %d to %d", originalSessionId, mSessionId);
if (record == 0) {
ALOGE("AudioFlinger could not create record track, status: %d", status);
@@ -587,6 +594,7 @@
int AudioRecord::getSessionId() const
{
+ // no lock needed because session ID doesn't change after first set()
return mSessionId;
}
@@ -658,22 +666,31 @@
sp<IMemory> iMem = mCblkMemory;
audio_track_cblk_t* cblk = mCblk;
bool active = mActive;
+ uint32_t markerPosition = mMarkerPosition;
+ uint32_t newPosition = mNewPosition;
+ uint32_t user = cblk->user;
+ // determine whether a marker callback will be needed, while locked
+ bool needMarker = !mMarkerReached && (mMarkerPosition > 0) && (user >= mMarkerPosition);
+ if (needMarker) {
+ mMarkerReached = true;
+ }
+ // determine the number of new position callback(s) that will be needed, while locked
+ uint32_t updatePeriod = mUpdatePeriod;
+ uint32_t needNewPos = updatePeriod > 0 && user >= newPosition ?
+ ((user - newPosition) / updatePeriod) + 1 : 0;
+ mNewPosition = newPosition + updatePeriod * needNewPos;
mLock.unlock();
- // Manage marker callback
- if (!mMarkerReached && (mMarkerPosition > 0)) {
- if (cblk->user >= mMarkerPosition) {
- mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
- mMarkerReached = true;
- }
+ // perform marker callback, while unlocked
+ if (needMarker) {
+ mCbf(EVENT_MARKER, mUserData, &markerPosition);
}
- // Manage new position callback
- if (mUpdatePeriod > 0) {
- while (cblk->user >= mNewPosition) {
- mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
- mNewPosition += mUpdatePeriod;
- }
+ // perform new position callback(s), while unlocked
+ for (; needNewPos > 0; --needNewPos) {
+ uint32_t temp = newPosition;
+ mCbf(EVENT_NEW_POS, mUserData, &temp);
+ newPosition += updatePeriod;
}
do {
@@ -721,7 +738,7 @@
// otherwise we would have exited when obtainBuffer returned STOPPED earlier.
ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) {
- mCbf(EVENT_OVERRUN, mUserData, 0);
+ mCbf(EVENT_OVERRUN, mUserData, NULL);
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 86e122f..8f45491 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -500,6 +500,8 @@
CHECK(msg->findInt32("audio", &audio));
ALOGV("renderer %s flush completed.", audio ? "audio" : "video");
+ } else if (what == Renderer::kWhatVideoRenderingStart) {
+ notifyListener(MEDIA_INFO, MEDIA_INFO_RENDERING_START, 0);
}
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 1f13955..8a75f83 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -47,6 +47,7 @@
mHasVideo(false),
mSyncQueues(false),
mPaused(false),
+ mVideoRenderingStarted(false),
mLastPositionUpdateUs(-1ll),
mVideoLateByUs(0ll) {
}
@@ -387,9 +388,20 @@
mVideoQueue.erase(mVideoQueue.begin());
entry = NULL;
+ if (!mVideoRenderingStarted) {
+ mVideoRenderingStarted = true;
+ notifyVideoRenderingStart();
+ }
+
notifyPosition();
}
+void NuPlayer::Renderer::notifyVideoRenderingStart() {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatVideoRenderingStart);
+ notify->post();
+}
+
void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatEOS);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 268628b..e4368c7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -45,9 +45,10 @@
void resume();
enum {
- kWhatEOS = 'eos ',
- kWhatFlushComplete = 'fluC',
- kWhatPosition = 'posi',
+ kWhatEOS = 'eos ',
+ kWhatFlushComplete = 'fluC',
+ kWhatPosition = 'posi',
+ kWhatVideoRenderingStart = 'vdrd',
};
protected:
@@ -99,6 +100,7 @@
bool mSyncQueues;
bool mPaused;
+ bool mVideoRenderingStarted;
int64_t mLastPositionUpdateUs;
int64_t mVideoLateByUs;
@@ -120,6 +122,7 @@
void notifyFlushComplete(bool audio);
void notifyPosition();
void notifyVideoLateBy(int64_t lateByUs);
+ void notifyVideoRenderingStart();
void flushQueue(List<QueueEntry> *queue);
bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 2c68075..664d5dd 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -183,6 +183,7 @@
: mQueueStarted(false),
mUIDValid(false),
mTimeSource(NULL),
+ mVideoRenderingStarted(false),
mVideoRendererIsPreview(false),
mAudioPlayer(NULL),
mDisplayWidth(0),
@@ -468,6 +469,7 @@
}
void AwesomePlayer::reset_l() {
+ mVideoRenderingStarted = false;
mActiveAudioTrackIndex = -1;
mDisplayWidth = 0;
mDisplayHeight = 0;
@@ -710,7 +712,6 @@
kHighWaterMarkBytes);
modifyFlags(CACHE_UNDERRUN, CLEAR);
play_l();
- notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
} else if (mFlags & PREPARING) {
ALOGV("cache has filled up (> %d), prepare is done",
kHighWaterMarkBytes);
@@ -770,7 +771,6 @@
cachedDurationUs / 1E6);
play_l();
}
- notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
} else if (mFlags & PREPARING) {
ALOGV("cache has filled up (%.2f secs), prepare is done",
cachedDurationUs / 1E6);
@@ -1807,6 +1807,11 @@
if (mVideoRenderer != NULL) {
mSinceLastDropped++;
mVideoRenderer->render(mVideoBuffer);
+ if (!mVideoRenderingStarted) {
+ mVideoRenderingStarted = true;
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_RENDERING_START);
+ }
+
}
mVideoBuffer->release();
@@ -2626,6 +2631,9 @@
mFlags |= value;
break;
case CLEAR:
+ if ((value & CACHE_UNDERRUN) && (mFlags & CACHE_UNDERRUN)) {
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+ }
mFlags &= ~value;
break;
case ASSIGN:
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 68380a8..1422687 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -166,6 +166,7 @@
sp<MediaSource> mVideoTrack;
sp<MediaSource> mVideoSource;
sp<AwesomeRenderer> mVideoRenderer;
+ bool mVideoRenderingStarted;
bool mVideoRendererIsPreview;
ssize_t mActiveAudioTrackIndex;
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.cpp b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
index 1f5d037..eac23ba 100644
--- a/media/libstagefright/timedtext/TimedTextSRTSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.cpp
@@ -79,6 +79,10 @@
return OK;
}
+sp<MetaData> TimedTextSRTSource::getFormat() {
+ return mMetaData;
+}
+
status_t TimedTextSRTSource::scanFile() {
off64_t offset = 0;
int64_t startTimeUs;
@@ -155,18 +159,16 @@
while (needMoreData) {
if ((err = readNextLine(offset, &data)) != OK) {
if (err == ERROR_END_OF_STREAM) {
- needMoreData = false;
+ break;
} else {
return err;
}
}
- if (needMoreData) {
- data.trim();
- if (data.empty()) {
- // it's an empty line used to separate two subtitles
- needMoreData = false;
- }
+ data.trim();
+ if (data.empty()) {
+ // it's an empty line used to separate two subtitles
+ needMoreData = false;
}
}
info->textLen = *offset - info->offset;
@@ -221,35 +223,24 @@
if (options != NULL && options->getSeekTo(&seekTimeUs, &mode)) {
int64_t lastEndTimeUs =
mTextVector.valueAt(mTextVector.size() - 1).endTimeUs;
- int64_t firstStartTimeUs = mTextVector.keyAt(0);
- if (seekTimeUs < 0 || seekTimeUs > lastEndTimeUs) {
+ if (seekTimeUs < 0) {
return ERROR_OUT_OF_RANGE;
- } else if (seekTimeUs < firstStartTimeUs) {
- mIndex = 0;
+ } else if (seekTimeUs >= lastEndTimeUs) {
+ return ERROR_END_OF_STREAM;
} else {
// binary search
size_t low = 0;
size_t high = mTextVector.size() - 1;
size_t mid = 0;
- int64_t currTimeUs;
while (low <= high) {
mid = low + (high - low)/2;
- currTimeUs = mTextVector.keyAt(mid);
- const int64_t diffTime = currTimeUs - seekTimeUs;
-
- if (diffTime == 0) {
+ int diff = compareExtendedRangeAndTime(mid, seekTimeUs);
+ if (diff == 0) {
break;
- } else if (diffTime < 0) {
+ } else if (diff < 0) {
low = mid + 1;
} else {
- if ((high == mid + 1)
- && (seekTimeUs < mTextVector.keyAt(high))) {
- break;
- }
- if (mid < 1) {
- break;
- }
high = mid - 1;
}
}
@@ -260,6 +251,7 @@
if (mIndex >= mTextVector.size()) {
return ERROR_END_OF_STREAM;
}
+
const TextInfo &info = mTextVector.valueAt(mIndex);
*startTimeUs = mTextVector.keyAt(mIndex);
*endTimeUs = info.endTimeUs;
@@ -289,8 +281,18 @@
return OK;
}
-sp<MetaData> TimedTextSRTSource::getFormat() {
- return mMetaData;
+int TimedTextSRTSource::compareExtendedRangeAndTime(size_t index, int64_t timeUs) {
+ 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
diff --git a/media/libstagefright/timedtext/TimedTextSRTSource.h b/media/libstagefright/timedtext/TimedTextSRTSource.h
index 9eeab39..598c200 100644
--- a/media/libstagefright/timedtext/TimedTextSRTSource.h
+++ b/media/libstagefright/timedtext/TimedTextSRTSource.h
@@ -70,6 +70,25 @@
status_t extractAndAppendLocalDescriptions(
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);
};
diff --git a/media/libstagefright/timedtext/test/Android.mk b/media/libstagefright/timedtext/test/Android.mk
new file mode 100644
index 0000000..a5e7ba2
--- /dev/null
+++ b/media/libstagefright/timedtext/test/Android.mk
@@ -0,0 +1,27 @@
+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)
diff --git a/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
new file mode 100644
index 0000000..40e93c7
--- /dev/null
+++ b/media/libstagefright/timedtext/test/TimedTextSRTSource_test.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b2aa388..424d8bc 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -299,7 +299,7 @@
return NULL;
}
-status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -322,11 +322,10 @@
result.append(buffer);
}
write(fd, result.string(), result.size());
- return NO_ERROR;
}
-status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -339,10 +338,9 @@
(uint32_t)(mStandbyTimeInNsecs / 1000000));
result.append(buffer);
write(fd, result.string(), result.size());
- return NO_ERROR;
}
-status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
+void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -353,7 +351,6 @@
IPCThreadState::self()->getCallingUid());
result.append(buffer);
write(fd, result.string(), result.size());
- return NO_ERROR;
}
static bool tryLock(Mutex& mutex)
@@ -1216,7 +1213,7 @@
mLock.unlock();
}
-status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
+void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1273,10 +1270,9 @@
if (locked) {
mLock.unlock();
}
- return NO_ERROR;
}
-status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args)
+void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1291,7 +1287,6 @@
chain->dump(fd, args);
}
}
- return NO_ERROR;
}
void AudioFlinger::ThreadBase::acquireWakeLock()
@@ -1539,15 +1534,14 @@
delete [] mMixBuffer;
}
-status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
{
dumpInternals(fd, args);
dumpTracks(fd, args);
dumpEffectChains(fd, args);
- return NO_ERROR;
}
-status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1595,11 +1589,9 @@
FastTrackUnderruns underruns = getFastTrackUnderruns(0);
fdprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n",
underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
-
- return NO_ERROR;
}
-status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -1623,8 +1615,6 @@
fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask);
dumpBase(fd, args);
-
- return NO_ERROR;
}
// Thread virtuals
@@ -3498,7 +3488,7 @@
return reconfig;
}
-status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
+void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -3583,8 +3573,6 @@
AudioWatchdogDump wdCopy = mAudioWatchdogDump;
wdCopy.dump(fd);
}
-
- return NO_ERROR;
}
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
@@ -6132,9 +6120,12 @@
if (!mStandby) {
mInput->stream->common.standby(&mInput->stream->common);
}
- mActiveTrack.clear();
- mStartStopCond.broadcast();
+ {
+ Mutex::Autolock _l(mLock);
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ }
releaseWakeLock();
@@ -6310,7 +6301,7 @@
}
mStartStopCond.wait(mLock);
// if we have been restarted, recordTrack == mActiveTrack.get() here
- if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) {
+ if (exitPending() || mActiveTrack == 0 || recordTrack != mActiveTrack.get()) {
mLock.unlock();
AudioSystem::stopInput(mId);
mLock.lock();
@@ -6340,7 +6331,7 @@
return NAME_NOT_FOUND;
}
-status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -6374,8 +6365,6 @@
dumpBase(fd, args);
dumpEffectChains(fd, args);
-
- return NO_ERROR;
}
// AudioBufferProvider interface
@@ -7954,7 +7943,7 @@
int sessionId)
: mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
- // mDescriptor is set below
+ mDescriptor(*desc),
// mConfig is set by configure() and not used before then
mEffectInterface(NULL),
mStatus(NO_INIT), mState(IDLE),
@@ -7964,11 +7953,6 @@
{
ALOGV("Constructor %p", this);
int lStatus;
- if (thread == NULL) {
- return;
- }
-
- memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t));
// create effect engine from effect factory
mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface);
@@ -8072,7 +8056,7 @@
mState = DESTROYED;
}
- return size;
+ return mHandles.size();
}
// must be called with EffectModule::mLock held
@@ -8625,7 +8609,7 @@
return enabled;
}
-status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -8705,8 +8689,6 @@
if (locked) {
mLock.unlock();
}
-
- return NO_ERROR;
}
// ----------------------------------------------------------------------------
@@ -9332,7 +9314,7 @@
return hasControl;
}
-status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
+void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args)
{
const size_t SIZE = 256;
char buffer[SIZE];
@@ -9366,8 +9348,6 @@
if (locked) {
mLock.unlock();
}
-
- return NO_ERROR;
}
// must be called with ThreadBase::mLock held
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c6f08cd..c9a3c3f 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -277,10 +277,10 @@
// ro.audio.flinger_standbytime_ms or defaults to kDefaultStandbyTimeInNsecs
static nsecs_t mStandbyTimeInNsecs;
- // Internal dump utilites.
- status_t dumpPermissionDenial(int fd, const Vector<String16>& args);
- status_t dumpClients(int fd, const Vector<String16>& args);
- status_t dumpInternals(int fd, const Vector<String16>& args);
+ // Internal dump utilities.
+ void dumpPermissionDenial(int fd, const Vector<String16>& args);
+ void dumpClients(int fd, const Vector<String16>& args);
+ void dumpInternals(int fd, const Vector<String16>& args);
// --- Client ---
class Client : public RefBase {
@@ -355,8 +355,8 @@
ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t device, type_t type);
virtual ~ThreadBase();
- status_t dumpBase(int fd, const Vector<String16>& args);
- status_t dumpEffectChains(int fd, const Vector<String16>& args);
+ void dumpBase(int fd, const Vector<String16>& args);
+ void dumpEffectChains(int fd, const Vector<String16>& args);
void clearPowerManager();
@@ -966,7 +966,7 @@
audio_io_handle_t id, audio_devices_t device, type_t type);
virtual ~PlaybackThread();
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
// Thread virtuals
virtual status_t readyToRun();
@@ -1115,8 +1115,8 @@
void readOutputParameters();
- virtual status_t dumpInternals(int fd, const Vector<String16>& args);
- status_t dumpTracks(int fd, const Vector<String16>& args);
+ virtual void dumpInternals(int fd, const Vector<String16>& args);
+ void dumpTracks(int fd, const Vector<String16>& args);
SortedVector< sp<Track> > mTracks;
// mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by DuplicatingThread
@@ -1192,7 +1192,7 @@
// Thread virtuals
virtual bool checkForNewParameters_l();
- virtual status_t dumpInternals(int fd, const Vector<String16>& args);
+ virtual void dumpInternals(int fd, const Vector<String16>& args);
protected:
virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
@@ -1433,7 +1433,7 @@
AudioSystem::sync_event_t event,
int triggerSession);
void stop(RecordTrack* recordTrack);
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
AudioStreamIn* clearInput();
virtual audio_stream_t* stream() const;
@@ -1591,7 +1591,7 @@
void lock() { mLock.lock(); }
void unlock() { mLock.unlock(); }
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
protected:
friend class AudioFlinger; // for mHandles
@@ -1779,7 +1779,7 @@
void clearInputBuffer();
- status_t dump(int fd, const Vector<String16>& args);
+ void dump(int fd, const Vector<String16>& args);
protected:
friend class AudioFlinger; // for mThread, mEffects
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 8d4add4..f21a518 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -67,6 +67,7 @@
status_t Camera2Client::initialize(camera_module_t *module)
{
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
status_t res;
res = mDevice->initialize(module);
@@ -320,6 +321,7 @@
void Camera2Client::disconnect() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
if (mDevice == 0) return;
@@ -338,12 +340,17 @@
mCaptureStreamId = NO_STREAM;
}
+ if (mRecordingStreamId != NO_STREAM) {
+ mDevice->deleteStream(mRecordingStreamId);
+ mRecordingStreamId = NO_STREAM;
+ }
+
CameraService::Client::disconnect();
}
status_t Camera2Client::connect(const sp<ICameraClient>& client) {
ATRACE_CALL();
-
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
if (mClientPid != 0 && getCallingPid() != mClientPid) {
@@ -361,6 +368,7 @@
status_t Camera2Client::lock() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
ALOGV("%s: Camera %d: Lock call from pid %d; current client pid %d",
__FUNCTION__, mCameraId, getCallingPid(), mClientPid);
@@ -381,6 +389,7 @@
status_t Camera2Client::unlock() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
ALOGV("%s: Camera %d: Unlock call from pid %d; current client pid %d",
__FUNCTION__, mCameraId, getCallingPid(), mClientPid);
@@ -401,6 +410,7 @@
status_t Camera2Client::setPreviewDisplay(
const sp<Surface>& surface) {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
sp<IBinder> binder;
@@ -416,6 +426,7 @@
status_t Camera2Client::setPreviewTexture(
const sp<ISurfaceTexture>& surfaceTexture) {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
sp<IBinder> binder;
@@ -433,6 +444,8 @@
status_t res;
if (binder == mPreviewSurface) {
+ ALOGV("%s: Camera %d: New window is same as old window",
+ __FUNCTION__, mCameraId);
return NO_ERROR;
}
@@ -490,6 +503,7 @@
status_t Camera2Client::startPreview() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
return startPreviewLocked();
}
@@ -556,6 +570,7 @@
void Camera2Client::stopPreview() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
stopPreviewLocked();
}
@@ -577,6 +592,7 @@
// TODO: Handle record stop here
case PREVIEW:
mDevice->setStreamingRequest(NULL);
+ mDevice->waitUntilDrained();
case WAITING_FOR_PREVIEW_WINDOW:
mState = STOPPED;
break;
@@ -600,6 +616,7 @@
status_t Camera2Client::startRecording() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
status_t res;
switch (mState) {
@@ -669,6 +686,7 @@
void Camera2Client::stopRecording() {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
status_t res;
switch (mState) {
@@ -843,6 +861,7 @@
status_t Camera2Client::setParameters(const String8& params) {
ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
Mutex::Autolock icl(mICameraLock);
Mutex::Autolock pl(mParamsLock);
status_t res;
@@ -1492,10 +1511,12 @@
{
Mutex::Autolock icl(mICameraLock);
// TODO: Signal errors here upstream
+ bool discardData = false;
if (mState != RECORD && mState != VIDEO_SNAPSHOT) {
- ALOGE("%s: Camera %d: Recording image buffer produced unexpectedly!",
+ ALOGV("%s: Camera %d: Discarding recording image buffers received after "
+ "recording done",
__FUNCTION__, mCameraId);
- return;
+ discardData = true;
}
CpuConsumer::LockedBuffer imgBuffer;
@@ -1509,9 +1530,14 @@
if (imgBuffer.format != (int)kRecordingFormat) {
ALOGE("%s: Camera %d: Unexpected recording format: %x",
__FUNCTION__, mCameraId, imgBuffer.format);
+ discardData = true;
+ }
+
+ if (discardData) {
mRecordingConsumer->unlockBuffer(imgBuffer);
return;
}
+
size_t bufferSize = imgBuffer.width * imgBuffer.height * 3 / 2;
if (mRecordingHeap == 0 ||
@@ -2323,6 +2349,9 @@
}
if (currentWidth != (uint32_t)mParameters.previewWidth ||
currentHeight != (uint32_t)mParameters.previewHeight) {
+ ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d",
+ __FUNCTION__, mCameraId, currentWidth, currentHeight,
+ mParameters.previewWidth, mParameters.previewHeight);
res = mDevice->waitUntilDrained();
if (res != OK) {
ALOGE("%s: Camera %d: Error waiting for preview to drain: "
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 54dde80..f42e3a5 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -146,7 +146,7 @@
}
camera_metadata_t *Camera2Device::info() {
- ALOGV("%s: E", __FUNCTION__);
+ ALOGVV("%s: E", __FUNCTION__);
return mDeviceInfo;
}
@@ -268,7 +268,7 @@
status_t Camera2Device::waitUntilDrained() {
static const uint32_t kSleepTime = 50000; // 50 ms
static const uint32_t kMaxSleepTime = 10000000; // 10 s
-
+ ALOGV("%s: E", __FUNCTION__);
if (mRequestQueue.getBufferCount() ==
CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS) return INVALID_OPERATION;
@@ -642,6 +642,7 @@
sp<ANativeWindow> consumer,
uint32_t width, uint32_t height, int format, size_t size) {
status_t res;
+ ALOGV("%s: E", __FUNCTION__);
if (mState != RELEASED) return INVALID_OPERATION;
if (consumer == NULL) {