Merge "Add method to get redirected Uri"
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 3db2c38..f6f9e7a 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -144,6 +144,7 @@
INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY, // data is an int64_t
INTERNAL_OPTION_MAX_TIMESTAMP_GAP, // data is int64_t
INTERNAL_OPTION_START_TIME, // data is an int64_t
+ INTERNAL_OPTION_TIME_LAPSE, // data is an int64_t[2]
};
virtual status_t setInternalOption(
node_id node,
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index e284109..36f2a67 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -207,6 +207,9 @@
int64_t mRepeatFrameDelayUs;
int64_t mMaxPtsGapUs;
+ int64_t mTimePerFrameUs;
+ int64_t mTimePerCaptureUs;
+
bool mCreateInputBuffersSuspended;
status_t setCyclicIntraMacroblockRefresh(const sp<AMessage> &msg, int32_t mode);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d377acd..845a589 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -690,10 +690,10 @@
return setParamTimeLapseEnable(timeLapseEnable);
}
} else if (key == "time-between-time-lapse-frame-capture") {
- int64_t timeBetweenTimeLapseFrameCaptureMs;
- if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureMs)) {
+ int64_t timeBetweenTimeLapseFrameCaptureUs;
+ if (safe_strtoi64(value.string(), &timeBetweenTimeLapseFrameCaptureUs)) {
return setParamTimeBetweenTimeLapseFrameCapture(
- 1000LL * timeBetweenTimeLapseFrameCaptureMs);
+ timeBetweenTimeLapseFrameCaptureUs);
}
} else {
ALOGE("setParameter: failed to find key %s", key.string());
@@ -1436,6 +1436,17 @@
format->setInt32("stride", mVideoWidth);
format->setInt32("slice-height", mVideoWidth);
format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque);
+
+ // set up time lapse/slow motion for surface source
+ if (mCaptureTimeLapse) {
+ if (mTimeBetweenTimeLapseFrameCaptureUs <= 0) {
+ ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
+ mTimeBetweenTimeLapseFrameCaptureUs);
+ return BAD_VALUE;
+ }
+ format->setInt64("time-lapse",
+ mTimeBetweenTimeLapseFrameCaptureUs);
+ }
}
format->setInt32("bitrate", mVideoBitRate);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ac78d6c..4450d62 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -374,7 +374,9 @@
mStoreMetaDataInOutputBuffers(false),
mMetaDataBuffersToSubmit(0),
mRepeatFrameDelayUs(-1ll),
- mMaxPtsGapUs(-1l),
+ mMaxPtsGapUs(-1ll),
+ mTimePerCaptureUs(-1ll),
+ mTimePerFrameUs(-1ll),
mCreateInputBuffersSuspended(false) {
mUninitializedState = new UninitializedState(this);
mLoadedState = new LoadedState(this);
@@ -1119,7 +1121,11 @@
}
if (!msg->findInt64("max-pts-gap-to-encoder", &mMaxPtsGapUs)) {
- mMaxPtsGapUs = -1l;
+ mMaxPtsGapUs = -1ll;
+ }
+
+ if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) {
+ mTimePerCaptureUs = -1ll;
}
if (!msg->findInt32(
@@ -1916,6 +1922,7 @@
return INVALID_OPERATION;
}
frameRate = (float)tmp;
+ mTimePerFrameUs = (int64_t) (1000000.0f / frameRate);
}
video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
@@ -3939,7 +3946,7 @@
}
}
- if (err == OK && mCodec->mMaxPtsGapUs > 0l) {
+ if (err == OK && mCodec->mMaxPtsGapUs > 0ll) {
err = mCodec->mOMX->setInternalOption(
mCodec->mNode,
kPortIndexInput,
@@ -3951,8 +3958,27 @@
ALOGE("[%s] Unable to configure max timestamp gap (err %d)",
mCodec->mComponentName.c_str(),
err);
- }
- }
+ }
+ }
+
+ if (err == OK && mCodec->mTimePerCaptureUs > 0ll
+ && mCodec->mTimePerFrameUs > 0ll) {
+ int64_t timeLapse[2];
+ timeLapse[0] = mCodec->mTimePerFrameUs;
+ timeLapse[1] = mCodec->mTimePerCaptureUs;
+ err = mCodec->mOMX->setInternalOption(
+ mCodec->mNode,
+ kPortIndexInput,
+ IOMX::INTERNAL_OPTION_TIME_LAPSE,
+ &timeLapse[0],
+ sizeof(timeLapse));
+
+ if (err != OK) {
+ ALOGE("[%s] Unable to configure time lapse (err %d)",
+ mCodec->mComponentName.c_str(),
+ err);
+ }
+ }
if (err == OK && mCodec->mCreateInputBuffersSuspended) {
bool suspend = true;
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 216a329..451e907 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -452,6 +452,11 @@
}
}
+ int32_t timeScale;
+ if (msg->findInt32("time-scale", &timeScale)) {
+ meta->setInt32(kKeyTimeScale, timeScale);
+ }
+
// XXX TODO add whatever other keys there are
#if 0
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 7144efd..b81b116 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -51,7 +51,11 @@
mLatestSubmittedBufferId(-1),
mLatestSubmittedBufferFrameNum(0),
mLatestSubmittedBufferUseCount(0),
- mRepeatBufferDeferred(false) {
+ mRepeatBufferDeferred(false),
+ mTimePerCaptureUs(-1ll),
+ mTimePerFrameUs(-1ll),
+ mPrevCaptureUs(-1ll),
+ mPrevFrameUs(-1ll) {
ALOGV("GraphicBufferSource w=%u h=%u c=%u",
bufferWidth, bufferHeight, bufferCount);
@@ -560,7 +564,30 @@
int64_t GraphicBufferSource::getTimestamp(const BufferQueue::BufferItem &item) {
int64_t timeUs = item.mTimestamp / 1000;
- if (mMaxTimestampGapUs > 0ll) {
+ if (mTimePerCaptureUs > 0ll) {
+ // Time lapse or slow motion mode
+ if (mPrevCaptureUs < 0ll) {
+ // first capture
+ mPrevCaptureUs = timeUs;
+ mPrevFrameUs = timeUs;
+ } else {
+ // snap to nearest capture point
+ int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
+ / mTimePerCaptureUs;
+ if (nFrames <= 0) {
+ // skip this frame as it's too close to previous capture
+ ALOGV("skipping frame, timeUs %lld", timeUs);
+ return -1;
+ }
+ mPrevCaptureUs = mPrevCaptureUs + nFrames * mTimePerCaptureUs;
+ mPrevFrameUs += mTimePerFrameUs * nFrames;
+ }
+
+ ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
+ timeUs, mPrevCaptureUs, mPrevFrameUs);
+
+ return mPrevFrameUs;
+ } else if (mMaxTimestampGapUs > 0ll) {
/* Cap timestamp gap between adjacent frames to specified max
*
* In the scenario of cast mirroring, encoding could be suspended for
@@ -711,7 +738,7 @@
// If this is the first time we're seeing this buffer, add it to our
// slot table.
if (item.mGraphicBuffer != NULL) {
- ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf);
+ ALOGV("onFrameAvailable: setting mBufferSlot %d", item.mBuf);
mBufferSlot[item.mBuf] = item.mGraphicBuffer;
}
mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber,
@@ -782,6 +809,19 @@
(skipFramesBeforeUs > 0) ? (skipFramesBeforeUs * 1000) : -1ll;
}
+status_t GraphicBufferSource::setTimeLapseUs(int64_t* data) {
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mExecuting || data[0] <= 0ll || data[1] <= 0ll) {
+ return INVALID_OPERATION;
+ }
+
+ mTimePerFrameUs = data[0];
+ mTimePerCaptureUs = data[1];
+
+ return OK;
+}
+
void GraphicBufferSource::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatRepeatLastFrame:
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 153f2a0..fba42b7 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -118,6 +118,13 @@
// of suspension on input.
status_t setMaxTimestampGapUs(int64_t maxGapUs);
+ // Sets the time lapse (or slow motion) parameters.
+ // data[0] is the time (us) between two frames for playback
+ // data[1] is the time (us) between two frames for capture
+ // When set, the sample's timestamp will be modified to playback framerate,
+ // and capture timestamp will be modified to capture rate.
+ status_t setTimeLapseUs(int64_t* data);
+
// Sets the start time us (in system time), samples before which should
// be dropped and not submitted to encoder
void setSkipFramesBeforeUs(int64_t startTimeUs);
@@ -250,6 +257,12 @@
// no codec buffer was available at the time.
bool mRepeatBufferDeferred;
+ // Time lapse / slow motion configuration
+ int64_t mTimePerCaptureUs;
+ int64_t mTimePerFrameUs;
+ int64_t mPrevCaptureUs;
+ int64_t mPrevFrameUs;
+
void onMessageReceived(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(GraphicBufferSource);
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index aa96389..0fb38fa 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -851,6 +851,7 @@
case IOMX::INTERNAL_OPTION_REPEAT_PREVIOUS_FRAME_DELAY:
case IOMX::INTERNAL_OPTION_MAX_TIMESTAMP_GAP:
case IOMX::INTERNAL_OPTION_START_TIME:
+ case IOMX::INTERNAL_OPTION_TIME_LAPSE:
{
const sp<GraphicBufferSource> &bufferSource =
getGraphicBufferSource();
@@ -884,7 +885,7 @@
int64_t maxGapUs = *(int64_t *)data;
return bufferSource->setMaxTimestampGapUs(maxGapUs);
- } else { // IOMX::INTERNAL_OPTION_START_TIME
+ } else if (type == IOMX::INTERNAL_OPTION_START_TIME) {
if (size != sizeof(int64_t)) {
return INVALID_OPERATION;
}
@@ -892,6 +893,12 @@
int64_t skipFramesBeforeUs = *(int64_t *)data;
bufferSource->setSkipFramesBeforeUs(skipFramesBeforeUs);
+ } else { // IOMX::INTERNAL_OPTION_TIME_LAPSE
+ if (size != sizeof(int64_t) * 2) {
+ return INVALID_OPERATION;
+ }
+
+ bufferSource->setTimeLapseUs((int64_t *)data);
}
return OK;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6f2dce3..7615086 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -183,6 +183,7 @@
(void) property_get("af.tee", value, "0");
teeEnabled = atoi(value);
}
+ // FIXME symbolic constants here
if (teeEnabled & 1) {
mTeeSinkInputEnabled = true;
}
@@ -1810,7 +1811,7 @@
kind = TEE_SINK_NEW;
} else if (mRecordTeeSink->getStrongCount() != 1) {
kind = TEE_SINK_NO;
- } else if (format == mRecordTeeSink->format()) {
+ } else if (Format_isEqual(format, mRecordTeeSink->format())) {
kind = TEE_SINK_OLD;
} else {
kind = TEE_SINK_NEW;
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c5b8d33..21d05d4 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -638,7 +638,7 @@
// 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
static const size_t kTeeSinkInputFramesDefault = 0x200000;
static const size_t kTeeSinkOutputFramesDefault = 0x200000;
- static const size_t kTeeSinkTrackFramesDefault = 0x1000;
+ static const size_t kTeeSinkTrackFramesDefault = 0x200000;
#endif
// This method reads from a variable without mLock, but the variable is updated under mLock. So
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 9145a0e..3e8c133 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -4694,12 +4694,12 @@
framesIn = 0;
activeTrack->mRsmpInFront = rear;
overrun = OVERRUN_TRUE;
- } else if ((size_t) filled <= mRsmpInFramesP2) {
+ } else if ((size_t) filled <= mRsmpInFrames) {
framesIn = (size_t) filled;
} else {
// client is not keeping up with server, but give it latest data
- framesIn = mRsmpInFramesP2;
- activeTrack->mRsmpInFront = rear - framesIn;
+ framesIn = mRsmpInFrames;
+ activeTrack->mRsmpInFront = front = rear - framesIn;
overrun = OVERRUN_TRUE;
}
@@ -4747,32 +4747,38 @@
double inOverOut = (double) mSampleRate / activeTrack->mSampleRate;
double outOverIn = (double) activeTrack->mSampleRate / mSampleRate;
framesInNeeded = ceil(framesOut * inOverOut) + 1;
+ ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g",
+ framesInNeeded, framesOut, inOverOut);
+ // Although we theoretically have framesIn in circular buffer, some of those are
+ // unreleased frames, and thus must be discounted for purpose of budgeting.
+ size_t unreleased = activeTrack->mRsmpInUnrel;
+ framesIn = framesIn > unreleased ? framesIn - unreleased : 0;
if (framesIn < framesInNeeded) {
- ALOGV("not enough to resample: have %u but need %u to produce %u "
- "given in/out ratio of %.4g",
+ ALOGV("not enough to resample: have %u frames in but need %u in to "
+ "produce %u out given in/out ratio of %.4g",
framesIn, framesInNeeded, framesOut, inOverOut);
size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * outOverIn) : 0;
- size_t newFramesInNeeded = ceil(newFramesOut * inOverOut) + 1;
- ALOGV("now need %u frames to produce %u given out/in ratio of %.4g",
- newFramesInNeeded, newFramesOut, outOverIn);
- if (framesIn < newFramesInNeeded) {
- ALOGE("failure: have %u but need %u", framesIn, newFramesInNeeded);
- framesOut = 0;
- } else {
- ALOGV("success 2: have %u and need %u to produce %u "
- "given in/out ratio of %.4g",
- framesIn, newFramesInNeeded, newFramesOut, inOverOut);
- LOG_ALWAYS_FATAL_IF(newFramesOut > framesOut);
- framesOut = newFramesOut;
+ LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut);
+ if (newFramesOut == 0) {
+ break;
}
+ framesInNeeded = ceil(newFramesOut * inOverOut) + 1;
+ ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g",
+ framesInNeeded, newFramesOut, outOverIn);
+ LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded);
+ ALOGV("success 2: have %u frames in and need %u in to produce %u out "
+ "given in/out ratio of %.4g",
+ framesIn, framesInNeeded, newFramesOut, inOverOut);
+ framesOut = newFramesOut;
} else {
- ALOGI("success 1: have %u and need %u to produce %u "
+ ALOGV("success 1: have %u in and need %u in to produce %u out "
"given in/out ratio of %.4g",
framesIn, framesInNeeded, framesOut, inOverOut);
}
// reallocate mRsmpOutBuffer as needed; we will grow but never shrink
if (activeTrack->mRsmpOutFrameCount < framesOut) {
+ // FIXME why does each track need it's own mRsmpOutBuffer? can't they share?
delete[] activeTrack->mRsmpOutBuffer;
// resampler always outputs stereo
activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2];
@@ -4782,6 +4788,7 @@
// resampler accumulates, but we only have one source track
memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut,
+ // FIXME how about having activeTrack implement this interface itself?
activeTrack->mResamplerBufferProvider
/*this*/ /* AudioBufferProvider* */);
// ditherAndClamp() works as long as all buffers returned by
@@ -5245,6 +5252,7 @@
sp<ThreadBase> threadBase = activeTrack->mThread.promote();
if (threadBase == 0) {
buffer->frameCount = 0;
+ buffer->raw = NULL;
return NOT_ENOUGH_DATA;
}
RecordThread *recordThread = (RecordThread *) threadBase.get();
@@ -5253,7 +5261,7 @@
ssize_t filled = rear - front;
// FIXME should not be P2 (don't want to increase latency)
// FIXME if client not keeping up, discard
- ALOG_ASSERT(0 <= filled && (size_t) filled <= recordThread->mRsmpInFramesP2);
+ LOG_ALWAYS_FATAL_IF(!(0 <= filled && (size_t) filled <= recordThread->mRsmpInFrames));
// 'filled' may be non-contiguous, so return only the first contiguous chunk
front &= recordThread->mRsmpInFramesP2 - 1;
size_t part1 = recordThread->mRsmpInFramesP2 - front;
@@ -5267,7 +5275,7 @@
}
if (part1 == 0) {
// Higher-level should keep mRsmpInBuffer full, and not call resampler if empty
- LOG_FATAL("RecordThread::getNextBuffer() starved");
+ LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved");
buffer->raw = NULL;
buffer->frameCount = 0;
activeTrack->mRsmpInUnrel = 0;