diff options
25 files changed, 457 insertions, 138 deletions
diff --git a/api/current.xml b/api/current.xml index 47a847259d5d..1fef0ec5a0f8 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 e432a474b82d..21cb3a88f1ad 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1224,7 +1224,6 @@ public class Camera { * 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 @@ public class Camera { * 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 @@ public class Camera { * 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 @@ public class Camera { * 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 @@ public class Camera { * * @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 @@ public class Camera { /** * 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 @@ public class Camera { * @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 @@ public class Camera { * @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 @@ public class Camera { * 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 09fc0f0ad571..03b721c19b53 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 2597e9efd3f4..1af42543c608 100644 --- a/include/media/stagefright/AudioSource.h +++ b/include/media/stagefright/AudioSource.h @@ -49,7 +49,17 @@ protected: 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 @@ private: 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 010ded1d5b62..875bc5b58e20 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -102,6 +102,7 @@ private: kInputBufferSizesAreBogus = 512, kSupportsMultipleFramesPerInputBuffer = 1024, kAvoidMemcopyInputRecordingFrames = 2048, + kRequiresLargerEncoderOutputBuffer = 4096, }; struct BufferInfo { diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 604f55876e55..a0e01c693dde 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -508,8 +508,8 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } 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 c8dfeded7fc5..4c729e4533bb 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -40,6 +40,7 @@ AudioSource::AudioSource( 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 @@ static int skipFrame(int64_t timestampUs, } +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 @@ status_t AudioSource::read( 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 a0cd5c35a63b..7e25e8867f0b 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -196,7 +196,6 @@ AwesomePlayer::AwesomePlayer() mExtractorFlags(0), mLastVideoBuffer(NULL), mVideoBuffer(NULL), - mRTSPTimeOffset(0), mSuspensionState(NULL) { CHECK_EQ(mClient.connect(), OK); @@ -739,7 +738,10 @@ status_t AwesomePlayer::getDuration(int64_t *durationUs) { } 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 @@ status_t AwesomePlayer::getPosition(int64_t *positionUs) { *positionUs = 0; } - if (mRTSPController != NULL) { - *positionUs += mRTSPTimeOffset; - } - return OK; } @@ -770,13 +768,10 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) { 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 4741b1d84e80..165cec9f8ded 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -353,6 +353,15 @@ uint32_t OMXCodec::getComponentQuirks(const char *componentName) { 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 @@ void OMXCodec::setVideoInputFormat( 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 70205648b54e..7016880a6442 100644 --- a/media/libstagefright/include/ARTSPController.h +++ b/media/libstagefright/include/ARTSPController.h @@ -41,6 +41,8 @@ struct ARTSPController : public MediaExtractor { 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 49b5c78946a1..55e2c3651a9e 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -180,7 +180,6 @@ private: 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 c56578bd487c..154ba31aa686 100644 --- a/media/libstagefright/rtsp/AAMRAssembler.cpp +++ b/media/libstagefright/rtsp/AAMRAssembler.cpp @@ -178,12 +178,8 @@ ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket( } } - 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 b22de2c8d341..6b1e292db7a4 100644 --- a/media/libstagefright/rtsp/AAVCAssembler.cpp +++ b/media/libstagefright/rtsp/AAVCAssembler.cpp @@ -155,7 +155,7 @@ bool AAVCAssembler::addSingleTimeAggregationPacket(const sp<ABuffer> &buffer) { sp<ABuffer> unit = new ABuffer(nalSize); memcpy(unit->data(), &data[2], nalSize); - PropagateTimes(buffer, unit); + CopyTimes(unit, buffer); addSingleNALUnit(unit); @@ -287,7 +287,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit( ++totalSize; sp<ABuffer> unit = new ABuffer(totalSize); - PropagateTimes(buffer, unit); + CopyTimes(unit, *queue->begin()); unit->data()[0] = (nri << 5) | nalType; @@ -325,10 +325,6 @@ void AAVCAssembler::submitAccessUnit() { 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 @@ void AAVCAssembler::submitAccessUnit() { 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 2818041d5979..498295c46e1b 100644 --- a/media/libstagefright/rtsp/AH263Assembler.cpp +++ b/media/libstagefright/rtsp/AH263Assembler.cpp @@ -128,10 +128,6 @@ void AH263Assembler::submitAccessUnit() { 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 @@ void AH263Assembler::submitAccessUnit() { ++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 6e46361e9f3c..b0d2c64e3f40 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -103,10 +103,6 @@ void AMPEG4AudioAssembler::submitAccessUnit() { 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 @@ void AMPEG4AudioAssembler::submitAccessUnit() { ++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 7e633d7b7e56..7dd3e3f1cd00 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -101,10 +101,6 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() { 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 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() { 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 b930184e7438..2d7738b0d8c7 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -402,16 +402,41 @@ sp<ABuffer> MakeMPEG4VideoCodecSpecificData( 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 @@ status_t APacketSource::read( 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 @@ status_t APacketSource::read( 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 @@ void APacketSource::flushQueue() { 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 197af3ed54f1..3833ab1745f2 100644 --- a/media/libstagefright/rtsp/APacketSource.h +++ b/media/libstagefright/rtsp/APacketSource.h @@ -45,6 +45,11 @@ struct APacketSource : public MediaSource { void flushQueue(); + int64_t getNormalPlayTimeUs(); + + void setNormalPlayTimeMapping( + uint32_t rtpTime, int64_t normalPlayTimeUs); + protected: virtual ~APacketSource(); @@ -58,6 +63,15 @@ private: 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 24225b882656..9ba2b37c8899 100644 --- a/media/libstagefright/rtsp/ARTPAssembler.cpp +++ b/media/libstagefright/rtsp/ARTPAssembler.cpp @@ -35,18 +35,6 @@ ARTPAssembler::ARTPAssembler() : 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 @@ void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) { } } +// 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 e598088b1cba..70ea18667366 100644 --- a/media/libstagefright/rtsp/ARTPAssembler.h +++ b/media/libstagefright/rtsp/ARTPAssembler.h @@ -40,12 +40,11 @@ struct ARTPAssembler : public RefBase { 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 9df17cbad45c..a89946bc41fa 100644 --- a/media/libstagefright/rtsp/ARTSPController.cpp +++ b/media/libstagefright/rtsp/ARTSPController.cpp @@ -138,4 +138,9 @@ void ARTSPController::onMessageReceived(const sp<AMessage> &msg) { } } +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 4c6f058797a2..0685a4732af9 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 @@ struct MyHandler : public AHandler { mSetupTracksSuccessful(false), mSeekPending(false), mFirstAccessUnit(true), - mFirstAccessUnitNTP(0) { + mFirstAccessUnitNTP(0), + mNumAccessUnitsReceived(0), + mCheckPending(false) { mNetLooper->start(false /* runOnCallingThread */, false /* canCallJava */, @@ -76,6 +108,20 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { CHECK_EQ(response->mStatusCode, 200u); + parsePlayResponse(response); + mDoneMsg->setInt32("result", OK); mDoneMsg->post(); mDoneMsg = NULL; @@ -332,16 +380,38 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { { 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 @@ struct MyHandler : public AHandler { 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 @@ struct MyHandler : public AHandler { { if (mFirstAccessUnit) { LOG(WARNING) << "Never received any data, disconnecting."; - + (new AMessage('abor', id()))->post(); } - (new AMessage('abor', id()))->post(); break; } @@ -492,6 +587,90 @@ struct MyHandler : public AHandler { } } + 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 @@ private: 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 @@ private: 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 b12115820faf..1ffcd563ff6e 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -229,14 +229,6 @@ struct android_native_buffer_t; #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 9e256818e5d7..163b2dbcd3a4 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -158,7 +158,6 @@ struct egl_surface_t 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 @@ EGLBoolean egl_surface_t::setSwapRectangle( { return EGL_FALSE; } -EGLClientBuffer egl_surface_t::getRenderBuffer() const { - return 0; -} // ---------------------------------------------------------------------------- @@ -230,7 +226,6 @@ struct egl_window_surface_v2_t : public egl_surface_t 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 @@ EGLBoolean egl_window_surface_v2_t::setSwapRectangle( 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 @@ static char const * const gExtensionsString = // "KHR_image_pixmap " "EGL_ANDROID_image_native_buffer " "EGL_ANDROID_swap_rectangle " - "EGL_ANDROID_get_render_buffer " ; // ---------------------------------------------------------------------------- @@ -910,8 +899,6 @@ static const extention_map_t gExtentionMap[] = { (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, { "eglSetSwapRectangleANDROID", (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, - { "eglGetRenderBufferANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID }, }; /* @@ -2129,18 +2116,3 @@ EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, 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 94b60a106410..5e61607da85e 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -61,7 +61,6 @@ static char const * const gExtensionString = "EGL_KHR_image_pixmap " "EGL_ANDROID_image_native_buffer " "EGL_ANDROID_swap_rectangle " - "EGL_ANDROID_get_render_buffer " ; // ---------------------------------------------------------------------------- @@ -408,8 +407,6 @@ static const extention_map_t gExtentionMap[] = { (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, { "eglSetSwapRectangleANDROID", (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, - { "eglGetRenderBufferANDROID", - (__eglMustCastToProperFunctionPointerType)&eglGetRenderBufferANDROID }, }; extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS]; @@ -1810,19 +1807,3 @@ EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, } 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); -} |