diff options
21 files changed, 270 insertions, 115 deletions
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index c0963a6..048f041 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -126,8 +126,6 @@ public: virtual status_t setLooping(int loop) = 0; virtual player_type playerType() = 0; - virtual void setNotifyCallback(void* cookie, notify_callback_f notifyFunc) { - mCookie = cookie; mNotify = notifyFunc; } // Invoke a generic method on the player by using opaque parcels // for the request and reply. // @@ -149,9 +147,21 @@ public: return INVALID_OPERATION; }; - virtual void sendEvent(int msg, int ext1=0, int ext2=0) { if (mNotify) mNotify(mCookie, msg, ext1, ext2); } + void setNotifyCallback( + void* cookie, notify_callback_f notifyFunc) { + Mutex::Autolock autoLock(mNotifyLock); + mCookie = cookie; mNotify = notifyFunc; + } -protected: + void sendEvent(int msg, int ext1=0, int ext2=0) { + Mutex::Autolock autoLock(mNotifyLock); + if (mNotify) mNotify(mCookie, msg, ext1, ext2); + } + +private: + friend class MediaPlayerService; + + Mutex mNotifyLock; void* mCookie; notify_callback_f mNotify; }; diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h index 794355b..4a39fbf 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -158,6 +158,7 @@ protected: int32_t mNumFramesReceived; int64_t mLastFrameTimestampUs; bool mStarted; + int32_t mNumFramesEncoded; CameraSource(const sp<ICamera>& camera, int32_t cameraId, Size videoSize, int32_t frameRate, @@ -189,7 +190,6 @@ private: List<int64_t> mFrameTimes; int64_t mFirstFrameTimeUs; - int32_t mNumFramesEncoded; int32_t mNumFramesDropped; int32_t mNumGlitches; int64_t mGlitchDurationThresholdUs; diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h index 3251c28..82948cb 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -47,6 +47,9 @@ struct OMXCodec : public MediaSource, // Store meta data in video buffers kStoreMetaDataInVideoBuffers = 32, + + // Only submit one input buffer at one time. + kOnlySubmitOneInputBufferAtOneTime = 64, }; static sp<MediaSource> Create( const sp<IOMX> &omx, @@ -192,6 +195,7 @@ private: Condition mBufferFilled; bool mIsMetaDataStoredInVideoBuffers; + bool mOnlySubmitOneBufferAtOneTime; OMXCodec(const sp<IOMX> &omx, IOMX::node_id node, uint32_t quirks, bool isEncoder, const char *mime, const char *componentName, diff --git a/include/media/stagefright/StagefrightMediaScanner.h b/include/media/stagefright/StagefrightMediaScanner.h index 4437eee..108acb4 100644 --- a/include/media/stagefright/StagefrightMediaScanner.h +++ b/include/media/stagefright/StagefrightMediaScanner.h @@ -22,8 +22,6 @@ namespace android { -struct MediaMetadataRetriever; - struct StagefrightMediaScanner : public MediaScanner { StagefrightMediaScanner(); virtual ~StagefrightMediaScanner(); @@ -35,8 +33,6 @@ struct StagefrightMediaScanner : public MediaScanner { virtual char *extractAlbumArt(int fd); private: - sp<MediaMetadataRetriever> mRetriever; - StagefrightMediaScanner(const StagefrightMediaScanner &); StagefrightMediaScanner &operator=(const StagefrightMediaScanner &); }; diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 65df68c..f134cba 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1239,6 +1239,14 @@ status_t StagefrightRecorder::setupVideoEncoder( encoder_flags |= OMXCodec::kHardwareCodecsOnly; encoder_flags |= OMXCodec::kStoreMetaDataInVideoBuffers; } + + // Do not wait for all the input buffers to become available. + // This give timelapse video recording faster response in + // receiving output from video encoder component. + if (mCaptureTimeLapse) { + encoder_flags |= OMXCodec::kOnlySubmitOneInputBufferAtOneTime; + } + sp<MediaSource> encoder = OMXCodec::Create( client.interface(), enc_meta, true /* createEncoder */, cameraSource, diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index 5ff934d..e7c0299 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -286,7 +286,9 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { } if (mReachedEOS) { - return 0; + memset(data, 0, size); + + return size; } size_t size_done = 0; diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 235d752..f96df18 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -91,14 +91,17 @@ status_t AudioSource::start(MetaData *params) { mStartTimeUs = startTimeUs; } status_t err = mRecord->start(); - if (err == OK) { mGroup = new MediaBufferGroup; mGroup->add_buffer(new MediaBuffer(kMaxBufferSize)); mStarted = true; + } else { + delete mRecord; + mRecord = NULL; } + return err; } diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 11ac56c..89b3dab 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -378,14 +378,11 @@ status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { } void AwesomePlayer::reset() { - LOGI("reset"); - Mutex::Autolock autoLock(mLock); reset_l(); } void AwesomePlayer::reset_l() { - LOGI("reset_l"); mDisplayWidth = 0; mDisplayHeight = 0; @@ -411,10 +408,6 @@ void AwesomePlayer::reset_l() { } } - if (mFlags & PREPARING) { - LOGI("waiting until preparation is completes."); - } - while (mFlags & PREPARING) { mPreparedCondition.wait(mLock); } @@ -438,8 +431,6 @@ void AwesomePlayer::reset_l() { } mAudioSource.clear(); - LOGI("audio source cleared"); - mTimeSource = NULL; delete mAudioPlayer; @@ -480,8 +471,6 @@ void AwesomePlayer::reset_l() { IPCThreadState::self()->flushCommands(); } - LOGI("video source cleared"); - mDurationUs = -1; mFlags = 0; mExtractorFlags = 0; @@ -498,8 +487,6 @@ void AwesomePlayer::reset_l() { mFileSource.clear(); mBitrate = -1; - - LOGI("reset_l completed"); } void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) { diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index b1c6b18..66e0657 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -147,8 +147,8 @@ CameraSource::CameraSource( mNumFramesReceived(0), mLastFrameTimestampUs(0), mStarted(false), - mFirstFrameTimeUs(0), mNumFramesEncoded(0), + mFirstFrameTimeUs(0), mNumFramesDropped(0), mNumGlitches(0), mGlitchDurationThresholdUs(200000), diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index b58b9d8..e6fe618 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -491,7 +491,10 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { } } - if (*timestampUs < + // Workaround to bypass the first 2 input frames for skipping. + // The first 2 output frames from the encoder are: decoder specific info and + // the compressed video frame data for the first input video frame. + if (mNumFramesEncoded >= 1 && *timestampUs < (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) { // Skip all frames from last encoded frame until // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed. diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index 057868c..77a61a5 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -124,6 +124,80 @@ static status_t MyConnect( return result; } +// Apparently under out linux closing a socket descriptor from one thread +// will not unblock a pending send/recv on that socket on another thread. +static ssize_t MySendReceive( + int s, void *data, size_t size, int flags, bool sendData) { + ssize_t result = 0; + + while (size > 0) { + fd_set rs, ws, es; + FD_ZERO(&rs); + FD_ZERO(&ws); + FD_ZERO(&es); + FD_SET(s, sendData ? &ws : &rs); + FD_SET(s, &es); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000ll; + + int nfds = ::select( + s + 1, + sendData ? NULL : &rs, + sendData ? &ws : NULL, + &es, + &tv); + + if (nfds < 0) { + if (errno == EINTR) { + continue; + } + + result = -errno; + break; + } else if (nfds == 0) { + // timeout + + continue; + } + + CHECK_EQ(nfds, 1); + + ssize_t nbytes = + sendData ? send(s, data, size, flags) : recv(s, data, size, flags); + + if (nbytes < 0) { + if (errno == EINTR) { + continue; + } + + result = -errno; + break; + } else if (nbytes == 0) { + result = 0; + break; + } + + data = (uint8_t *)data + nbytes; + size -= nbytes; + + result = nbytes; + break; + } + + return result; +} + +static ssize_t MySend(int s, const void *data, size_t size, int flags) { + return MySendReceive( + s, const_cast<void *>(data), size, flags, true /* sendData */); +} + +static ssize_t MyReceive(int s, void *data, size_t size, int flags) { + return MySendReceive(s, data, size, flags, false /* sendData */); +} + status_t HTTPStream::connect(const char *server, int port) { Mutex::Autolock autoLock(mLock); @@ -202,16 +276,12 @@ status_t HTTPStream::send(const char *data, size_t size) { } while (size > 0) { - ssize_t n = ::send(mSocket, data, size, 0); + ssize_t n = MySend(mSocket, data, size, 0); if (n < 0) { - if (errno == EINTR) { - continue; - } - disconnect(); - return ERROR_IO; + return n; } else if (n == 0) { disconnect(); @@ -247,12 +317,8 @@ status_t HTTPStream::receive_line(char *line, size_t size) { for (;;) { char c; - ssize_t n = recv(mSocket, &c, 1, 0); + ssize_t n = MyReceive(mSocket, &c, 1, 0); if (n < 0) { - if (errno == EINTR) { - continue; - } - disconnect(); return ERROR_IO; @@ -371,14 +437,10 @@ status_t HTTPStream::receive_header(int *http_status) { ssize_t HTTPStream::receive(void *data, size_t size) { size_t total = 0; while (total < size) { - ssize_t n = recv(mSocket, (char *)data + total, size - total, 0); + ssize_t n = MyReceive(mSocket, (char *)data + total, size - total, 0); if (n < 0) { - if (errno == EINTR) { - continue; - } - - LOGE("recv failed, errno = %d (%s)", errno, strerror(errno)); + LOGE("recv failed, errno = %d (%s)", (int)n, strerror(-n)); disconnect(); return (ssize_t)ERROR_IO; diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index a47ee3a..d1a497f 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -78,6 +78,7 @@ private: volatile bool mDone; volatile bool mPaused; volatile bool mResumed; + volatile bool mStarted; bool mIsAvc; bool mIsAudio; bool mIsMPEG4; @@ -951,6 +952,7 @@ MPEG4Writer::Track::Track( mDone(false), mPaused(false), mResumed(false), + mStarted(false), mTrackDurationUs(0), mEstimatedTrackSizeBytes(0), mSamplesHaveSameSize(true), @@ -1279,6 +1281,7 @@ status_t MPEG4Writer::Track::start(MetaData *params) { pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); mDone = false; + mStarted = true; mTrackDurationUs = 0; mReachedEOS = false; mEstimatedTrackSizeBytes = 0; @@ -1307,10 +1310,14 @@ status_t MPEG4Writer::Track::pause() { status_t MPEG4Writer::Track::stop() { LOGD("Stopping %s track", mIsAudio? "Audio": "Video"); + if (!mStarted) { + LOGE("Stop() called but track is not started"); + return ERROR_END_OF_STREAM; + } + if (mDone) { return OK; } - mDone = true; void *dummy; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 94694a3..2a19b25 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -522,6 +522,12 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) { if (flags & kStoreMetaDataInVideoBuffers) { mIsMetaDataStoredInVideoBuffers = true; } + + mOnlySubmitOneBufferAtOneTime = false; + if (flags & kOnlySubmitOneInputBufferAtOneTime) { + mOnlySubmitOneBufferAtOneTime = true; + } + if (!(flags & kIgnoreCodecSpecificData)) { uint32_t type; const void *data; @@ -2203,8 +2209,9 @@ void OMXCodec::onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { crop.right = right; crop.bottom = bottom; - CHECK_EQ(0, native_window_set_crop( - mNativeWindow.get(), &crop)); + // We'll ignore any errors here, if the surface is + // already invalid, we'll know soon enough. + native_window_set_crop(mNativeWindow.get(), &crop); } } } @@ -2609,7 +2616,17 @@ void OMXCodec::drainInputBuffers() { Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput]; for (size_t i = 0; i < buffers->size(); ++i) { - if (!drainInputBuffer(&buffers->editItemAt(i))) { + BufferInfo *info = &buffers->editItemAt(i); + + if (info->mStatus != OWNED_BY_US) { + continue; + } + + if (!drainInputBuffer(info)) { + break; + } + + if (mOnlySubmitOneBufferAtOneTime) { break; } } @@ -3316,7 +3333,7 @@ status_t OMXCodec::stop() { mSource->stop(); - CODEC_LOGV("stopped"); + CODEC_LOGI("stopped in state %d", mState); return OK; } diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 39b0021..be3df7c 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -28,9 +28,7 @@ namespace android { -StagefrightMediaScanner::StagefrightMediaScanner() - : mRetriever(new MediaMetadataRetriever) { -} +StagefrightMediaScanner::StagefrightMediaScanner() {} StagefrightMediaScanner::~StagefrightMediaScanner() {} @@ -131,37 +129,41 @@ status_t StagefrightMediaScanner::processFile( if (status != OK) { return status; } - } else if (mRetriever->setDataSource(path) == OK) { - const char *value; - if ((value = mRetriever->extractMetadata( - METADATA_KEY_MIMETYPE)) != NULL) { - client.setMimeType(value); - } + } else { + sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); - struct KeyMap { - const char *tag; - int key; - }; - static const KeyMap kKeyMap[] = { - { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, - { "discnumber", METADATA_KEY_DISC_NUMBER }, - { "album", METADATA_KEY_ALBUM }, - { "artist", METADATA_KEY_ARTIST }, - { "albumartist", METADATA_KEY_ALBUMARTIST }, - { "composer", METADATA_KEY_COMPOSER }, - { "genre", METADATA_KEY_GENRE }, - { "title", METADATA_KEY_TITLE }, - { "year", METADATA_KEY_YEAR }, - { "duration", METADATA_KEY_DURATION }, - { "writer", METADATA_KEY_WRITER }, - { "compilation", METADATA_KEY_COMPILATION }, - }; - static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); - - for (size_t i = 0; i < kNumEntries; ++i) { + if (mRetriever->setDataSource(path) == OK) { const char *value; - if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { - client.addStringTag(kKeyMap[i].tag, value); + if ((value = mRetriever->extractMetadata( + METADATA_KEY_MIMETYPE)) != NULL) { + client.setMimeType(value); + } + + struct KeyMap { + const char *tag; + int key; + }; + static const KeyMap kKeyMap[] = { + { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER }, + { "discnumber", METADATA_KEY_DISC_NUMBER }, + { "album", METADATA_KEY_ALBUM }, + { "artist", METADATA_KEY_ARTIST }, + { "albumartist", METADATA_KEY_ALBUMARTIST }, + { "composer", METADATA_KEY_COMPOSER }, + { "genre", METADATA_KEY_GENRE }, + { "title", METADATA_KEY_TITLE }, + { "year", METADATA_KEY_YEAR }, + { "duration", METADATA_KEY_DURATION }, + { "writer", METADATA_KEY_WRITER }, + { "compilation", METADATA_KEY_COMPILATION }, + }; + static const size_t kNumEntries = sizeof(kKeyMap) / sizeof(kKeyMap[0]); + + for (size_t i = 0; i < kNumEntries; ++i) { + const char *value; + if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { + client.addStringTag(kKeyMap[i].tag, value); + } } } } @@ -180,6 +182,7 @@ char *StagefrightMediaScanner::extractAlbumArt(int fd) { } lseek64(fd, 0, SEEK_SET); + sp<MediaMetadataRetriever> mRetriever(new MediaMetadataRetriever); if (mRetriever->setDataSource(fd, 0, size) == OK) { sp<IMemory> mem = mRetriever->extractAlbumArt(); diff --git a/media/libstagefright/codecs/aacenc/AACEncoder.cpp b/media/libstagefright/codecs/aacenc/AACEncoder.cpp index 9524884..a8b1292 100644 --- a/media/libstagefright/codecs/aacenc/AACEncoder.cpp +++ b/media/libstagefright/codecs/aacenc/AACEncoder.cpp @@ -151,7 +151,11 @@ status_t AACEncoder::start(MetaData *params) { mInputFrame = new int16_t[mChannels * kNumSamplesPerFrame]; CHECK(mInputFrame != NULL); - mSource->start(params); + status_t err = mSource->start(params); + if (err != OK) { + LOGE("AudioSource is not available"); + return err; + } mStarted = true; @@ -159,11 +163,6 @@ status_t AACEncoder::start(MetaData *params) { } status_t AACEncoder::stop() { - if (!mStarted) { - LOGW("Call stop() when encoder has not started"); - return OK; - } - if (mInputBuffer) { mInputBuffer->release(); mInputBuffer = NULL; @@ -172,8 +171,17 @@ status_t AACEncoder::stop() { delete mBufferGroup; mBufferGroup = NULL; - mSource->stop(); + if (mInputFrame) { + delete[] mInputFrame; + mInputFrame = NULL; + } + + if (!mStarted) { + LOGW("Call stop() when encoder has not started"); + return ERROR_END_OF_STREAM; + } + mSource->stop(); if (mEncoderHandle) { CHECK_EQ(VO_ERR_NONE, mApiHandle->Uninit(mEncoderHandle)); mEncoderHandle = NULL; @@ -182,10 +190,6 @@ status_t AACEncoder::stop() { mApiHandle = NULL; mStarted = false; - if (mInputFrame) { - delete[] mInputFrame; - mInputFrame = NULL; - } return OK; } diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp index 9b6d441..c7c1409 100644 --- a/media/libstagefright/omx/OMXNodeInstance.cpp +++ b/media/libstagefright/omx/OMXNodeInstance.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -//#define LOG_NDEBUG 0 +#define LOG_NDEBUG 0 #define LOG_TAG "OMXNodeInstance" #include <utils/Log.h> @@ -124,6 +124,8 @@ static status_t StatusFromOMXError(OMX_ERRORTYPE err) { } status_t OMXNodeInstance::freeNode(OMXMaster *master) { + static int32_t kMaxNumIterations = 10; + // Transition the node from its current state all the way down // to "Loaded". // This ensures that all active buffers are properly freed even @@ -143,9 +145,16 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { LOGV("forcing Executing->Idle"); sendCommand(OMX_CommandStateSet, OMX_StateIdle); OMX_ERRORTYPE err; + int32_t iteration = 0; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone && state != OMX_StateIdle && state != OMX_StateInvalid) { + if (++iteration > kMaxNumIterations) { + LOGE("component failed to enter Idle state, aborting."); + state = OMX_StateInvalid; + break; + } + usleep(100000); } CHECK_EQ(err, OMX_ErrorNone); @@ -165,9 +174,16 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { freeActiveBuffers(); OMX_ERRORTYPE err; + int32_t iteration = 0; while ((err = OMX_GetState(mHandle, &state)) == OMX_ErrorNone && state != OMX_StateLoaded && state != OMX_StateInvalid) { + if (++iteration > kMaxNumIterations) { + LOGE("component failed to enter Loaded state, aborting."); + state = OMX_StateInvalid; + break; + } + LOGV("waiting for Loaded state..."); usleep(100000); } @@ -185,8 +201,10 @@ status_t OMXNodeInstance::freeNode(OMXMaster *master) { break; } + LOGV("calling destroyComponentInstance"); OMX_ERRORTYPE err = master->destroyComponentInstance( static_cast<OMX_COMPONENTTYPE *>(mHandle)); + LOGV("destroyComponentInstance returned err %d", err); mHandle = NULL; diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index bbde516..8bfe285 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -359,7 +359,10 @@ sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) { } } - CHECK_EQ(offset, buffer->size()); + if (offset < buffer->size()) { + LOGI("ignoring %d bytes of trailing data", buffer->size() - offset); + } + CHECK_LE(offset, buffer->size()); return out; } diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp index 77917b3..3e710dc 100644 --- a/media/libstagefright/rtsp/ASessionDescription.cpp +++ b/media/libstagefright/rtsp/ASessionDescription.cpp @@ -254,26 +254,12 @@ bool ASessionDescription::getDurationUs(int64_t *durationUs) const { return false; } - if (value == "npt=now-" || value == "npt=0-") { - return false; - } - if (strncmp(value.c_str(), "npt=", 4)) { return false; } - const char *s = value.c_str() + 4; - char *end; - double from = strtod(s, &end); - - if (end == s || *end != '-') { - return false; - } - - s = end + 1; - double to = strtod(s, &end); - - if (end == s || *end != '\0' || to < from) { + float from, to; + if (!parseNTPRange(value.c_str() + 4, &from, &to)) { return false; } @@ -307,5 +293,39 @@ void ASessionDescription::ParseFormatDesc( } } +// static +bool ASessionDescription::parseNTPRange( + const char *s, float *npt1, float *npt2) { + if (s[0] == '-') { + return false; // no start time available. + } + + if (!strncmp("now", s, 3)) { + return false; // no absolute start time available + } + + char *end; + *npt1 = strtof(s, &end); + + if (end == s || *end != '-') { + // Failed to parse float or trailing "dash". + return false; + } + + s = end + 1; // skip the dash. + + if (!strncmp("now", s, 3)) { + return false; // no absolute end time available + } + + *npt2 = strtof(s, &end); + + if (end == s || *end != '\0') { + return false; + } + + return *npt2 > *npt1; +} + } // namespace android diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h index a3fa79e..b462983 100644 --- a/media/libstagefright/rtsp/ASessionDescription.h +++ b/media/libstagefright/rtsp/ASessionDescription.h @@ -55,6 +55,14 @@ struct ASessionDescription : public RefBase { bool findAttribute(size_t index, const char *key, AString *value) const; + // parses strings of the form + // npt := npt-time "-" npt-time? | "-" npt-time + // npt-time := "now" | [0-9]+("." [0-9]*)? + // + // Returns true iff both "npt1" and "npt2" times were available, + // i.e. we have a fixed duration, otherwise this is live streaming. + static bool parseNTPRange(const char *s, float *npt1, float *npt2); + protected: virtual ~ASessionDescription(); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 9bb8c46..306a9c1 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -938,13 +938,11 @@ struct MyHandler : public AHandler { AString val; CHECK(GetAttribute(range.c_str(), "npt", &val)); - float npt1, npt2; - if (val == "now-" || val == "0-") { + float npt1, npt2; + if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) { // 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"); diff --git a/media/mtp/MtpUtils.cpp b/media/mtp/MtpUtils.cpp index ab01ef5..6ec8876 100644 --- a/media/mtp/MtpUtils.cpp +++ b/media/mtp/MtpUtils.cpp @@ -55,7 +55,7 @@ bool parseDateTime(const char* dateTime, time_t& outSeconds) { tm.tm_min = minute; tm.tm_hour = hour; tm.tm_mday = day; - tm.tm_mon = month; + tm.tm_mon = month - 1; // mktime uses months in 0 - 11 range tm.tm_year = year - 1900; tm.tm_wday = 0; tm.tm_isdst = -1; @@ -72,7 +72,9 @@ void formatDateTime(time_t seconds, char* buffer, int bufferLength) { localtime_r(&seconds, &tm); snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d", - tm.tm_year + 1900, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + tm.tm_year + 1900, + tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); } } // namespace android |