Merge "Send broadcast intent when configured location providers change." into gingerbread
diff --git a/api/current.xml b/api/current.xml
index 47a8472..1fef0ec 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -76089,6 +76089,19 @@
visibility="public"
>
</method>
+<method name="getPreviewFpsRange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="range" type="int[]">
+</parameter>
+</method>
<method name="getPreviewFrameRate"
return="int"
abstract="false"
@@ -76096,7 +76109,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -76221,6 +76234,17 @@
visibility="public"
>
</method>
+<method name="getSupportedPreviewFpsRange"
+ return="java.util.List<int[]>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getSupportedPreviewFrameRates"
return="java.util.List<java.lang.Integer>"
abstract="false"
@@ -76228,7 +76252,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
</method>
@@ -76610,7 +76634,7 @@
<parameter name="pixel_format" type="int">
</parameter>
</method>
-<method name="setPreviewFrameRate"
+<method name="setPreviewFpsRange"
return="void"
abstract="false"
native="false"
@@ -76620,6 +76644,21 @@
deprecated="not deprecated"
visibility="public"
>
+<parameter name="min" type="int">
+</parameter>
+<parameter name="max" type="int">
+</parameter>
+</method>
+<method name="setPreviewFrameRate"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="deprecated"
+ visibility="public"
+>
<parameter name="fps" type="int">
</parameter>
</method>
@@ -77033,6 +77072,28 @@
visibility="public"
>
</field>
+<field name="PREVIEW_FPS_MAX_INDEX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="PREVIEW_FPS_MIN_INDEX"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="SCENE_MODE_ACTION"
type="java.lang.String"
transient="false"
@@ -78424,7 +78485,7 @@
type="float"
transient="false"
volatile="false"
- value="0.001f"
+ value="0.0010f"
static="true"
final="true"
deprecated="not deprecated"
@@ -224842,7 +224903,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="arg0" type="T">
+<parameter name="t" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index e432a47..21cb3a8 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1224,7 +1224,6 @@
* The array index of minimum preview fps for use with {@link
* #getPreviewFpsRange(int[])} or {@link
* #getSupportedPreviewFpsRange()}.
- * @hide
*/
public static final int PREVIEW_FPS_MIN_INDEX = 0;
@@ -1232,7 +1231,6 @@
* The array index of maximum preview fps for use with {@link
* #getPreviewFpsRange(int[])} or {@link
* #getSupportedPreviewFpsRange()}.
- * @hide
*/
public static final int PREVIEW_FPS_MAX_INDEX = 1;
@@ -1471,7 +1469,9 @@
* target frame rate. The actual frame rate depends on the driver.
*
* @param fps the frame rate (frames per second)
+ * @deprecated replaced by {@link #setPreviewFpsRange(int,int)}
*/
+ @Deprecated
public void setPreviewFrameRate(int fps) {
set(KEY_PREVIEW_FRAME_RATE, fps);
}
@@ -1482,7 +1482,9 @@
* depends on the driver.
*
* @return the frame rate setting (frames per second)
+ * @deprecated replaced by {@link #getPreviewFpsRange(int[])}
*/
+ @Deprecated
public int getPreviewFrameRate() {
return getInt(KEY_PREVIEW_FRAME_RATE);
}
@@ -1492,7 +1494,9 @@
*
* @return a list of supported preview frame rates. null if preview
* frame rate setting is not supported.
+ * @deprecated replaced by {@link #getSupportedPreviewFpsRange()}
*/
+ @Deprecated
public List<Integer> getSupportedPreviewFrameRates() {
String str = get(KEY_PREVIEW_FRAME_RATE + SUPPORTED_VALUES_SUFFIX);
return splitInt(str);
@@ -1500,7 +1504,7 @@
/**
* Sets the maximum and maximum preview fps. This controls the rate of
- * preview frames received in {@link #PreviewCallback}. The minimum and
+ * preview frames received in {@link PreviewCallback}. The minimum and
* maximum preview fps must be one of the elements from {@link
* #getSupportedPreviewFpsRange}.
*
@@ -1509,7 +1513,6 @@
* @throws RuntimeException if fps range is invalid.
* @see #setPreviewCallbackWithBuffer(Camera.PreviewCallback)
* @see #getSupportedPreviewFpsRange()
- * @hide
*/
public void setPreviewFpsRange(int min, int max) {
set(KEY_PREVIEW_FPS_RANGE, "" + min + "," + max);
@@ -1523,12 +1526,11 @@
* @see #PREVIEW_FPS_MIN_INDEX
* @see #PREVIEW_FPS_MAX_INDEX
* @see #getSupportedPreviewFpsRange()
- * @hide
*/
public void getPreviewFpsRange(int[] range) {
if (range == null || range.length != 2) {
throw new IllegalArgumentException(
- "range must be an float array with two elements.");
+ "range must be an array with two elements.");
}
splitInt(get(KEY_PREVIEW_FPS_RANGE), range);
}
@@ -1549,7 +1551,6 @@
* minimum fps).
* @see #PREVIEW_FPS_MIN_INDEX
* @see #PREVIEW_FPS_MAX_INDEX
- * @hide
*/
public List<int[]> getSupportedPreviewFpsRange() {
String str = get(KEY_PREVIEW_FPS_RANGE + SUPPORTED_VALUES_SUFFIX);
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 09fc0f0..03b721c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1853,7 +1853,7 @@
<!-- Item on EditText context menu. This action is used to select all text in the edit field. -->
<string name="selectAll">Select all</string>
- <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. -->
+ <!-- Item on EditText context menu. This action is used to start selecting text in the edit field. [CHAR LIMIT=20] -->
<string name="selectText">Select word</string>
<!-- Item on EditText context menu. This action is used to cut selected the text into the clipboard. -->
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 2597e9e..1af4254 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -49,7 +49,17 @@
virtual ~AudioSource();
private:
- enum { kMaxBufferSize = 2048 };
+ enum {
+ kMaxBufferSize = 2048,
+
+ // After the initial mute, we raise the volume linearly
+ // over kAutoRampDurationUs.
+ kAutoRampDurationUs = 300000,
+
+ // This is the initial mute duration to suppress
+ // the video recording signal tone
+ kAutoRampStartUs = 700000,
+ };
AudioRecord *mRecord;
status_t mInitCheck;
@@ -67,6 +77,12 @@
void trackMaxAmplitude(int16_t *data, int nSamples);
+ // This is used to raise the volume from mute to the
+ // actual level linearly.
+ void rampVolume(
+ int32_t startFrame, int32_t rampDurationFrames,
+ uint8_t *data, size_t bytes);
+
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
};
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 010ded1..875bc5b 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -102,6 +102,7 @@
kInputBufferSizesAreBogus = 512,
kSupportsMultipleFramesPerInputBuffer = 1024,
kAvoidMemcopyInputRecordingFrames = 2048,
+ kRequiresLargerEncoderOutputBuffer = 4096,
};
struct BufferInfo {
diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp
index 604f558..a0e01c6 100644
--- a/libs/utils/ZipFileRO.cpp
+++ b/libs/utils/ZipFileRO.cpp
@@ -508,8 +508,8 @@
}
if (get4LE(lfhBuf) != kLFHSignature) {
- LOGW("didn't find signature at start of lfh, offset=%ld\n",
- localHdrOffset);
+ LOGW("didn't find signature at start of lfh, offset=%ld (got 0x%08lx, expected 0x%08x)\n",
+ localHdrOffset, get4LE(lfhBuf), kLFHSignature);
return false;
}
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index c8dfede..4c729e4 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -40,6 +40,7 @@
mGroup(NULL) {
LOGV("sampleRate: %d, channels: %d", sampleRate, channels);
+ CHECK(channels == 1 || channels == 2);
uint32_t flags = AudioRecord::RECORD_AGC_ENABLE |
AudioRecord::RECORD_NS_ENABLE |
AudioRecord::RECORD_IIR_ENABLE;
@@ -158,6 +159,38 @@
}
+void AudioSource::rampVolume(
+ int32_t startFrame, int32_t rampDurationFrames,
+ uint8_t *data, size_t bytes) {
+
+ const int32_t kShift = 14;
+ int32_t fixedMultiplier = (startFrame << kShift) / rampDurationFrames;
+ const int32_t nChannels = mRecord->channelCount();
+ int32_t stopFrame = startFrame + bytes / sizeof(int16_t);
+ int16_t *frame = (int16_t *) data;
+ if (stopFrame > rampDurationFrames) {
+ stopFrame = rampDurationFrames;
+ }
+
+ while (startFrame < stopFrame) {
+ if (nChannels == 1) { // mono
+ frame[0] = (frame[0] * fixedMultiplier) >> kShift;
+ ++frame;
+ ++startFrame;
+ } else { // stereo
+ frame[0] = (frame[0] * fixedMultiplier) >> kShift;
+ frame[1] = (frame[1] * fixedMultiplier) >> kShift;
+ frame += 2;
+ startFrame += 2;
+ }
+
+ // Update the multiplier every 4 frames
+ if ((startFrame & 3) == 0) {
+ fixedMultiplier = (startFrame << kShift) / rampDurationFrames;
+ }
+ }
+}
+
status_t AudioSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
@@ -242,6 +275,19 @@
continue;
}
+ if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs) {
+ // Mute the initial video recording signal
+ memset((uint8_t *) buffer->data(), 0, n);
+ } else if (mPrevSampleTimeUs - mStartTimeUs < kAutoRampStartUs + kAutoRampDurationUs) {
+ int32_t autoRampDurationFrames =
+ (kAutoRampDurationUs * sampleRate + 500000LL) / 1000000LL;
+
+ int32_t autoRampStartFrames =
+ (kAutoRampStartUs * sampleRate + 500000LL) / 1000000LL;
+
+ int32_t nFrames = numFramesRecorded - autoRampStartFrames;
+ rampVolume(nFrames, autoRampDurationFrames, (uint8_t *) buffer->data(), n);
+ }
if (mTrackMaxAmplitude) {
trackMaxAmplitude((int16_t *) buffer->data(), n >> 1);
}
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index a0cd5c3..7e25e88 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -196,7 +196,6 @@
mExtractorFlags(0),
mLastVideoBuffer(NULL),
mVideoBuffer(NULL),
- mRTSPTimeOffset(0),
mSuspensionState(NULL) {
CHECK_EQ(mClient.connect(), OK);
@@ -739,7 +738,10 @@
}
status_t AwesomePlayer::getPosition(int64_t *positionUs) {
- if (mSeeking) {
+ if (mRTSPController != NULL) {
+ *positionUs = mRTSPController->getNormalPlayTimeUs();
+ }
+ else if (mSeeking) {
*positionUs = mSeekTimeUs;
} else if (mVideoSource != NULL) {
Mutex::Autolock autoLock(mMiscStateLock);
@@ -750,10 +752,6 @@
*positionUs = 0;
}
- if (mRTSPController != NULL) {
- *positionUs += mRTSPTimeOffset;
- }
-
return OK;
}
@@ -770,13 +768,10 @@
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
if (mRTSPController != NULL) {
- pause_l();
mRTSPController->seek(timeUs);
- play_l();
notifyListener_l(MEDIA_SEEK_COMPLETE);
mSeekNotificationSent = true;
- mRTSPTimeOffset = timeUs;
return OK;
}
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 4741b1d..165cec9 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -353,6 +353,15 @@
quirks |= kRequiresLoadedToIdleAfterAllocation;
quirks |= kRequiresAllocateBufferOnInputPorts;
quirks |= kRequiresAllocateBufferOnOutputPorts;
+ if (!strncmp(componentName, "OMX.qcom.video.encoder.avc", 26)) {
+
+ // The AVC encoder advertises the size of output buffers
+ // based on the input video resolution and assumes
+ // the worst/least compression ratio is 0.5. It is found that
+ // sometimes, the output buffer size is larger than
+ // size advertised by the encoder.
+ quirks |= kRequiresLargerEncoderOutputBuffer;
+ }
}
if (!strncmp(componentName, "OMX.qcom.7x30.video.encoder.", 28)) {
}
@@ -906,6 +915,10 @@
video_def->nBitrate = bitRate; // Q16 format
video_def->eCompressionFormat = compressionFormat;
video_def->eColorFormat = OMX_COLOR_FormatUnused;
+ if (mQuirks & kRequiresLargerEncoderOutputBuffer) {
+ // Increases the output buffer size
+ def.nBufferSize = ((def.nBufferSize * 3) >> 1);
+ }
err = mOMX->setParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 7020564..7016880 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -41,6 +41,8 @@
virtual sp<MetaData> getTrackMetaData(
size_t index, uint32_t flags);
+ int64_t getNormalPlayTimeUs();
+
void onMessageReceived(const sp<AMessage> &msg);
protected:
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 49b5c78..55e2c36 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -180,7 +180,6 @@
sp<ALooper> mLooper;
sp<ARTSPController> mRTSPController;
- int64_t mRTSPTimeOffset;
sp<ARTPSession> mRTPSession;
sp<UDPPusher> mRTPPusher, mRTCPPusher;
diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp
index c56578b..154ba31 100644
--- a/media/libstagefright/rtsp/AAMRAssembler.cpp
+++ b/media/libstagefright/rtsp/AAMRAssembler.cpp
@@ -178,12 +178,8 @@
}
}
- uint64_t ntpTime;
- CHECK(buffer->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
sp<ABuffer> accessUnit = new ABuffer(totalSize);
- accessUnit->meta()->setInt64("ntp-time", ntpTime);
+ CopyTimes(accessUnit, buffer);
size_t dstOffset = 0;
for (size_t i = 0; i < tableOfContents.size(); ++i) {
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index b22de2c..6b1e292 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -155,7 +155,7 @@
sp<ABuffer> unit = new ABuffer(nalSize);
memcpy(unit->data(), &data[2], nalSize);
- PropagateTimes(buffer, unit);
+ CopyTimes(unit, buffer);
addSingleNALUnit(unit);
@@ -287,7 +287,7 @@
++totalSize;
sp<ABuffer> unit = new ABuffer(totalSize);
- PropagateTimes(buffer, unit);
+ CopyTimes(unit, *queue->begin());
unit->data()[0] = (nri << 5) | nalType;
@@ -325,10 +325,6 @@
LOG(VERBOSE) << "Access unit complete (" << mNALUnits.size() << " nal units)";
#endif
- uint64_t ntpTime;
- CHECK((*mNALUnits.begin())->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
size_t totalSize = 0;
for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
it != mNALUnits.end(); ++it) {
@@ -347,7 +343,7 @@
offset += nal->size();
}
- accessUnit->meta()->setInt64("ntp-time", ntpTime);
+ CopyTimes(accessUnit, *mNALUnits.begin());
#if 0
printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 2818041..498295c 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -128,10 +128,6 @@
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
#endif
- uint64_t ntpTime;
- CHECK((*mPackets.begin())->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
size_t totalSize = 0;
List<sp<ABuffer> >::iterator it = mPackets.begin();
while (it != mPackets.end()) {
@@ -155,7 +151,7 @@
++it;
}
- accessUnit->meta()->setInt64("ntp-time", ntpTime);
+ CopyTimes(accessUnit, *mPackets.begin());
#if 0
printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index 6e46361..b0d2c64 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -103,10 +103,6 @@
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
#endif
- uint64_t ntpTime;
- CHECK((*mPackets.begin())->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
size_t totalSize = 0;
List<sp<ABuffer> >::iterator it = mPackets.begin();
while (it != mPackets.end()) {
@@ -142,7 +138,7 @@
++it;
}
- accessUnit->meta()->setInt64("ntp-time", ntpTime);
+ CopyTimes(accessUnit, *mPackets.begin());
#if 0
printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 7e633d7..7dd3e3f 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -101,10 +101,6 @@
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
#endif
- uint64_t ntpTime;
- CHECK((*mPackets.begin())->meta()->findInt64(
- "ntp-time", (int64_t *)&ntpTime));
-
size_t totalSize = 0;
for (List<sp<ABuffer> >::iterator it = mPackets.begin();
it != mPackets.end(); ++it) {
@@ -120,7 +116,7 @@
offset += nal->size();
}
- accessUnit->meta()->setInt64("ntp-time", ntpTime);
+ CopyTimes(accessUnit, *mPackets.begin());
#if 0
printf(mAccessUnitDamaged ? "X" : ".");
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index b930184..2d7738b 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -402,16 +402,41 @@
return csd;
}
+static bool GetClockRate(const AString &desc, uint32_t *clockRate) {
+ ssize_t slashPos = desc.find("/");
+ if (slashPos < 0) {
+ return false;
+ }
+
+ const char *s = desc.c_str() + slashPos + 1;
+
+ char *end;
+ unsigned long x = strtoul(s, &end, 10);
+
+ if (end == s || (*end != '\0' && *end != '/')) {
+ return false;
+ }
+
+ *clockRate = x;
+
+ return true;
+}
+
APacketSource::APacketSource(
const sp<ASessionDescription> &sessionDesc, size_t index)
: mInitCheck(NO_INIT),
mFormat(new MetaData),
- mEOSResult(OK) {
+ mEOSResult(OK),
+ mRTPTimeBase(0),
+ mNormalPlayTimeBaseUs(0),
+ mLastNormalPlayTimeUs(0) {
unsigned long PT;
AString desc;
AString params;
sessionDesc->getFormatType(index, &PT, &desc, ¶ms);
+ CHECK(GetClockRate(desc, &mClockRate));
+
int64_t durationUs;
if (sessionDesc->getDurationUs(&durationUs)) {
mFormat->setInt64(kKeyDuration, durationUs);
@@ -571,6 +596,8 @@
if (!mBuffers.empty()) {
const sp<ABuffer> buffer = *mBuffers.begin();
+ updateNormalPlayTime_l(buffer);
+
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
int64_t timeUs;
@@ -588,6 +615,16 @@
return mEOSResult;
}
+void APacketSource::updateNormalPlayTime_l(const sp<ABuffer> &buffer) {
+ uint32_t rtpTime;
+ CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+ mLastNormalPlayTimeUs =
+ (((double)rtpTime - (double)mRTPTimeBase) / mClockRate)
+ * 1000000ll
+ + mNormalPlayTimeBaseUs;
+}
+
void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
@@ -613,4 +650,17 @@
mBuffers.clear();
}
+int64_t APacketSource::getNormalPlayTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+ return mLastNormalPlayTimeUs;
+}
+
+void APacketSource::setNormalPlayTimeMapping(
+ uint32_t rtpTime, int64_t normalPlayTimeUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ mRTPTimeBase = rtpTime;
+ mNormalPlayTimeBaseUs = normalPlayTimeUs;
+}
+
} // namespace android
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 197af3e..3833ab1 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -45,6 +45,11 @@
void flushQueue();
+ int64_t getNormalPlayTimeUs();
+
+ void setNormalPlayTimeMapping(
+ uint32_t rtpTime, int64_t normalPlayTimeUs);
+
protected:
virtual ~APacketSource();
@@ -58,6 +63,15 @@
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
+ uint32_t mClockRate;
+
+ uint32_t mRTPTimeBase;
+ int64_t mNormalPlayTimeBaseUs;
+
+ int64_t mLastNormalPlayTimeUs;
+
+ void updateNormalPlayTime_l(const sp<ABuffer> &buffer);
+
DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
};
diff --git a/media/libstagefright/rtsp/ARTPAssembler.cpp b/media/libstagefright/rtsp/ARTPAssembler.cpp
index 24225b8..9ba2b37 100644
--- a/media/libstagefright/rtsp/ARTPAssembler.cpp
+++ b/media/libstagefright/rtsp/ARTPAssembler.cpp
@@ -35,18 +35,6 @@
: mFirstFailureTimeUs(-1) {
}
-void ARTPAssembler::PropagateTimes(
- const sp<ABuffer> &from, const sp<ABuffer> &to) {
- uint32_t rtpTime;
- CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
-
- uint64_t ntpTime = 0;
- CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
-
- to->meta()->setInt32("rtp-time", rtpTime);
- to->meta()->setInt64("ntp-time", ntpTime);
-}
-
void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) {
AssemblyStatus status;
for (;;) {
@@ -75,4 +63,19 @@
}
}
+// static
+void ARTPAssembler::CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from) {
+ uint64_t ntpTime;
+ CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
+
+ uint32_t rtpTime;
+ CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+ to->meta()->setInt64("ntp-time", ntpTime);
+ to->meta()->setInt32("rtp-time", rtpTime);
+
+ // Copy the seq number.
+ to->setInt32Data(from->int32Data());
+}
+
} // namespace android
diff --git a/media/libstagefright/rtsp/ARTPAssembler.h b/media/libstagefright/rtsp/ARTPAssembler.h
index e598088..70ea1866 100644
--- a/media/libstagefright/rtsp/ARTPAssembler.h
+++ b/media/libstagefright/rtsp/ARTPAssembler.h
@@ -40,12 +40,11 @@
virtual void onByeReceived() = 0;
protected:
- static void PropagateTimes(
- const sp<ABuffer> &from, const sp<ABuffer> &to);
-
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0;
virtual void packetLost() = 0;
+ static void CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from);
+
private:
int64_t mFirstFailureTimeUs;
diff --git a/media/libstagefright/rtsp/ARTSPController.cpp b/media/libstagefright/rtsp/ARTSPController.cpp
index 9df17cba..a89946b 100644
--- a/media/libstagefright/rtsp/ARTSPController.cpp
+++ b/media/libstagefright/rtsp/ARTSPController.cpp
@@ -138,4 +138,9 @@
}
}
+int64_t ARTSPController::getNormalPlayTimeUs() {
+ CHECK(mHandler != NULL);
+ return mHandler->getNormalPlayTimeUs();
+}
+
} // namespace android
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 4c6f058..0685a47 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -23,6 +23,8 @@
#include "ARTSPConnection.h"
#include "ASessionDescription.h"
+#include <ctype.h>
+
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
@@ -33,6 +35,34 @@
namespace android {
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+ value->clear();
+
+ size_t keyLen = strlen(key);
+
+ for (;;) {
+ while (isspace(*s)) {
+ ++s;
+ }
+
+ const char *colonPos = strchr(s, ';');
+
+ size_t len =
+ (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+ if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+ value->setTo(&s[keyLen + 1], len - keyLen - 1);
+ return true;
+ }
+
+ if (colonPos == NULL) {
+ return false;
+ }
+
+ s = colonPos + 1;
+ }
+}
+
struct MyHandler : public AHandler {
MyHandler(const char *url, const sp<ALooper> &looper)
: mLooper(looper),
@@ -43,7 +73,9 @@
mSetupTracksSuccessful(false),
mSeekPending(false),
mFirstAccessUnit(true),
- mFirstAccessUnitNTP(0) {
+ mFirstAccessUnitNTP(0),
+ mNumAccessUnitsReceived(0),
+ mCheckPending(false) {
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
@@ -76,6 +108,20 @@
msg->post();
}
+ int64_t getNormalPlayTimeUs() {
+ int64_t maxTimeUs = 0;
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ int64_t timeUs = mTracks.editItemAt(i).mPacketSource
+ ->getNormalPlayTimeUs();
+
+ if (i == 0 || timeUs > maxTimeUs) {
+ maxTimeUs = timeUs;
+ }
+ }
+
+ return maxTimeUs;
+ }
+
virtual void onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case 'conn':
@@ -269,6 +315,8 @@
CHECK_EQ(response->mStatusCode, 200u);
+ parsePlayResponse(response);
+
mDoneMsg->setInt32("result", OK);
mDoneMsg->post();
mDoneMsg = NULL;
@@ -332,16 +380,38 @@
break;
}
+ case 'chek':
+ {
+ if (mNumAccessUnitsReceived == 0) {
+ LOG(INFO) << "stream ended? aborting.";
+ (new AMessage('abor', id()))->post();
+ break;
+ }
+
+ mNumAccessUnitsReceived = 0;
+ msg->post(500000);
+ break;
+ }
+
case 'accu':
{
+ ++mNumAccessUnitsReceived;
+
+ if (!mCheckPending) {
+ mCheckPending = true;
+ sp<AMessage> check = new AMessage('chek', id());
+ check->post(500000);
+ }
+
size_t trackIndex;
CHECK(msg->findSize("track-index", &trackIndex));
+ TrackInfo *track = &mTracks.editItemAt(trackIndex);
+
int32_t eos;
if (msg->findInt32("eos", &eos)) {
LOG(INFO) << "received BYE on track index " << trackIndex;
#if 0
- TrackInfo *track = &mTracks.editItemAt(trackIndex);
track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
#endif
return;
@@ -352,10 +422,32 @@
sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+ uint32_t seqNum = (uint32_t)accessUnit->int32Data();
+
+ if (seqNum < track->mFirstSeqNumInSegment) {
+ LOG(INFO) << "dropping stale access-unit "
+ << "(" << seqNum << " < "
+ << track->mFirstSeqNumInSegment << ")";
+ break;
+ }
+
uint64_t ntpTime;
CHECK(accessUnit->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
+ uint32_t rtpTime;
+ CHECK(accessUnit->meta()->findInt32(
+ "rtp-time", (int32_t *)&rtpTime));
+
+ if (track->mNewSegment) {
+ track->mNewSegment = false;
+
+ LOG(VERBOSE) << "first segment unit ntpTime="
+ << StringPrintf("0x%016llx", ntpTime)
+ << " rtpTime=" << rtpTime
+ << " seq=" << seqNum;
+ }
+
if (mFirstAccessUnit) {
mFirstAccessUnit = false;
mFirstAccessUnitNTP = ntpTime;
@@ -414,6 +506,11 @@
case 'see1':
{
+ // Session is paused now.
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ mTracks.editItemAt(i).mPacketSource->flushQueue();
+ }
+
int64_t timeUs;
CHECK(msg->findInt64("time", &timeUs));
@@ -440,15 +537,13 @@
{
CHECK(mSeekPending);
- LOG(INFO) << "seek completed.";
- mSeekPending = false;
-
int32_t result;
CHECK(msg->findInt32("result", &result));
- if (result != OK) {
- LOG(ERROR) << "seek FAILED";
- break;
- }
+
+ LOG(INFO) << "PLAY completed with result "
+ << result << " (" << strerror(-result) << ")";
+
+ CHECK_EQ(result, (status_t)OK);
sp<RefBase> obj;
CHECK(msg->findObject("response", &obj));
@@ -457,9 +552,10 @@
CHECK_EQ(response->mStatusCode, 200u);
- for (size_t i = 0; i < mTracks.size(); ++i) {
- mTracks.editItemAt(i).mPacketSource->flushQueue();
- }
+ parsePlayResponse(response);
+
+ LOG(INFO) << "seek completed.";
+ mSeekPending = false;
break;
}
@@ -480,9 +576,8 @@
{
if (mFirstAccessUnit) {
LOG(WARNING) << "Never received any data, disconnecting.";
-
+ (new AMessage('abor', id()))->post();
}
- (new AMessage('abor', id()))->post();
break;
}
@@ -492,6 +587,90 @@
}
}
+ static void SplitString(
+ const AString &s, const char *separator, List<AString> *items) {
+ items->clear();
+ size_t start = 0;
+ while (start < s.size()) {
+ ssize_t offset = s.find(separator, start);
+
+ if (offset < 0) {
+ items->push_back(AString(s, start, s.size() - start));
+ break;
+ }
+
+ items->push_back(AString(s, start, offset - start));
+ start = offset + strlen(separator);
+ }
+ }
+
+ void parsePlayResponse(const sp<ARTSPResponse> &response) {
+ ssize_t i = response->mHeaders.indexOfKey("range");
+ if (i < 0) {
+ // Server doesn't even tell use what range it is going to
+ // play, therefore we won't support seeking.
+ return;
+ }
+
+ AString range = response->mHeaders.valueAt(i);
+ LOG(VERBOSE) << "Range: " << range;
+
+ AString val;
+ CHECK(GetAttribute(range.c_str(), "npt", &val));
+ float npt1, npt2;
+
+ if (val == "now-") {
+ // This is a live stream and therefore not seekable.
+ return;
+ } else {
+ CHECK_EQ(sscanf(val.c_str(), "%f-%f", &npt1, &npt2), 2);
+ }
+
+ i = response->mHeaders.indexOfKey("rtp-info");
+ CHECK_GE(i, 0);
+
+ AString rtpInfo = response->mHeaders.valueAt(i);
+ List<AString> streamInfos;
+ SplitString(rtpInfo, ",", &streamInfos);
+
+ int n = 1;
+ for (List<AString>::iterator it = streamInfos.begin();
+ it != streamInfos.end(); ++it) {
+ (*it).trim();
+ LOG(VERBOSE) << "streamInfo[" << n << "] = " << *it;
+
+ CHECK(GetAttribute((*it).c_str(), "url", &val));
+
+ size_t trackIndex = 0;
+ while (trackIndex < mTracks.size()
+ && !(val == mTracks.editItemAt(trackIndex).mURL)) {
+ ++trackIndex;
+ }
+ CHECK_LT(trackIndex, mTracks.size());
+
+ CHECK(GetAttribute((*it).c_str(), "seq", &val));
+
+ char *end;
+ unsigned long seq = strtoul(val.c_str(), &end, 10);
+
+ TrackInfo *info = &mTracks.editItemAt(trackIndex);
+ info->mFirstSeqNumInSegment = seq;
+ info->mNewSegment = true;
+
+ CHECK(GetAttribute((*it).c_str(), "rtptime", &val));
+
+ uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
+
+ LOG(VERBOSE) << "track #" << n
+ << ": rtpTime=" << rtpTime << " <=> npt=" << npt1;
+
+ info->mPacketSource->setNormalPlayTimeMapping(
+ rtpTime, (int64_t)(npt1 * 1E6));
+
+ ++n;
+ }
+ }
+
sp<APacketSource> getPacketSource(size_t index) {
CHECK_GE(index, 0u);
CHECK_LT(index, mTracks.size());
@@ -516,11 +695,16 @@
bool mSeekPending;
bool mFirstAccessUnit;
uint64_t mFirstAccessUnitNTP;
+ int64_t mNumAccessUnitsReceived;
+ bool mCheckPending;
struct TrackInfo {
+ AString mURL;
int mRTPSocket;
int mRTCPSocket;
bool mUsingInterleavedTCP;
+ uint32_t mFirstSeqNumInSegment;
+ bool mNewSegment;
sp<APacketSource> mPacketSource;
};
@@ -550,8 +734,13 @@
mTracks.push(TrackInfo());
TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
+ info->mURL = trackURL;
info->mPacketSource = source;
info->mUsingInterleavedTCP = false;
+ info->mFirstSeqNumInSegment = 0;
+ info->mNewSegment = true;
+
+ LOG(VERBOSE) << "track #" << mTracks.size() << " URL=" << trackURL;
AString request = "SETUP ";
request.append(trackURL);
diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h
index b121158..1ffcd56 100644
--- a/opengl/include/EGL/eglext.h
+++ b/opengl/include/EGL/eglext.h
@@ -229,14 +229,6 @@
#define EGL_NATIVE_BUFFER_ANDROID 0x3140 /* eglCreateImageKHR target */
#endif
-#ifndef EGL_ANDROID_get_render_buffer
-#define EGL_ANDROID_get_render_buffer 1
-#ifdef EGL_EGLEXT_PROTOTYPES
-EGLAPI EGLClientBuffer EGLAPIENTRY eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw);
-#endif
-typedef EGLClientBuffer (EGLAPIENTRYP PFNEGLGETRENDERBUFFERANDROIDPROC) (EGLDisplay dpy, EGLSurface draw);
-#endif
-
#ifndef EGL_ANDROID_swap_rectangle
#define EGL_ANDROID_swap_rectangle 1
#ifdef EGL_EGLEXT_PROTOTYPES
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 9e25681..163b2dbc 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -158,7 +158,6 @@
virtual EGLint getSwapBehavior() const;
virtual EGLBoolean swapBuffers();
virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
- virtual EGLClientBuffer getRenderBuffer() const;
protected:
GGLSurface depth;
};
@@ -202,9 +201,6 @@
{
return EGL_FALSE;
}
-EGLClientBuffer egl_surface_t::getRenderBuffer() const {
- return 0;
-}
// ----------------------------------------------------------------------------
@@ -230,7 +226,6 @@
virtual EGLint getRefreshRate() const;
virtual EGLint getSwapBehavior() const;
virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h);
- virtual EGLClientBuffer getRenderBuffer() const;
private:
status_t lock(android_native_buffer_t* buf, int usage, void** vaddr);
@@ -626,11 +621,6 @@
return EGL_TRUE;
}
-EGLClientBuffer egl_window_surface_v2_t::getRenderBuffer() const
-{
- return buffer;
-}
-
EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl)
{
GGLSurface buffer;
@@ -857,7 +847,6 @@
// "KHR_image_pixmap "
"EGL_ANDROID_image_native_buffer "
"EGL_ANDROID_swap_rectangle "
- "EGL_ANDROID_get_render_buffer "
;
// ----------------------------------------------------------------------------
@@ -910,8 +899,6 @@
(__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
{ "eglSetSwapRectangleANDROID",
(__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID },
- { "eglGetRenderBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
/*
@@ -2129,18 +2116,3 @@
return EGL_TRUE;
}
-
-EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
-{
- if (egl_display_t::is_valid(dpy) == EGL_FALSE)
- return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
-
- egl_surface_t* d = static_cast<egl_surface_t*>(draw);
- if (!d->isValid())
- return setError(EGL_BAD_SURFACE, (EGLClientBuffer)0);
- if (d->dpy != dpy)
- return setError(EGL_BAD_DISPLAY, (EGLClientBuffer)0);
-
- // post the surface
- return d->getRenderBuffer();
-}
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 94b60a1..5e61607 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -61,7 +61,6 @@
"EGL_KHR_image_pixmap "
"EGL_ANDROID_image_native_buffer "
"EGL_ANDROID_swap_rectangle "
- "EGL_ANDROID_get_render_buffer "
;
// ----------------------------------------------------------------------------
@@ -408,8 +407,6 @@
(__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
{ "eglSetSwapRectangleANDROID",
(__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID },
- { "eglGetRenderBufferANDROID",
- (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID },
};
extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
@@ -1810,19 +1807,3 @@
}
return setError(EGL_BAD_DISPLAY, NULL);
}
-
-EGLClientBuffer eglGetRenderBufferANDROID(EGLDisplay dpy, EGLSurface draw)
-{
- SurfaceRef _s(draw);
- if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLClientBuffer*)0);
-
- if (!validate_display_surface(dpy, draw))
- return 0;
- egl_display_t const * const dp = get_display(dpy);
- egl_surface_t const * const s = get_surface(draw);
- if (s->cnx->egl.eglGetRenderBufferANDROID) {
- return s->cnx->egl.eglGetRenderBufferANDROID(
- dp->disp[s->impl].dpy, s->surface);
- }
- return setError(EGL_BAD_DISPLAY, (EGLClientBuffer*)0);
-}