diff options
Diffstat (limited to 'media')
-rw-r--r-- | media/libmedia/AudioTrack.cpp | 133 | ||||
-rw-r--r-- | media/libmedia/AudioTrackShared.cpp | 10 | ||||
-rw-r--r-- | media/libstagefright/ESDS.cpp | 6 | ||||
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 82 | ||||
-rw-r--r-- | media/libstagefright/MediaClock.cpp | 5 | ||||
-rw-r--r-- | media/libstagefright/SampleTable.cpp | 30 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSession.cpp | 67 | ||||
-rw-r--r-- | media/libstagefright/httplive/LiveSession.h | 2 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/AnotherPacketSource.cpp | 27 |
9 files changed, 303 insertions, 59 deletions
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 9e9ec5b..89138e2 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -56,6 +56,24 @@ static int64_t getNowUs() return convertTimespecToUs(tv); } +// Must match similar computation in createTrack_l in Threads.cpp. +// TODO: Move to a common library +static size_t calculateMinFrameCount( + uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate, + uint32_t sampleRate, float speed) +{ + // Ensure that buffer depth covers at least audio hardware latency + uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate); + if (minBufCount < 2) { + minBufCount = 2; + } + ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u " + "sampleRate %u speed %f minBufCount: %u", + afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount); + return minBufCount * sourceFramesNeededWithTimestretch( + sampleRate, afFrameCount, afSampleRate, speed); +} + // static status_t AudioTrack::getMinFrameCount( size_t* frameCount, @@ -94,13 +112,10 @@ status_t AudioTrack::getMinFrameCount( return status; } - // Ensure that buffer depth covers at least audio hardware latency - uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate); - if (minBufCount < 2) { - minBufCount = 2; - } + // When called from createTrack, speed is 1.0f (normal speed). + // This is rechecked again on setting playback rate (TODO: on setting sample rate, too). + *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f); - *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate); // The formula above should always produce a non-zero value under normal circumstances: // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX. // Return error in the unlikely event that it does not, as that's part of the API contract. @@ -109,8 +124,8 @@ status_t AudioTrack::getMinFrameCount( streamType, sampleRate); return BAD_VALUE; } - ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u", - *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency); + ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u", + *frameCount, afFrameCount, afSampleRate, afLatency); return NO_ERROR; } @@ -360,6 +375,8 @@ status_t AudioTrack::set( return BAD_VALUE; } mSampleRate = sampleRate; + mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL; + mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL; // Make copy of input parameter offloadInfo so that in the future: // (a) createTrack_l doesn't need it as an input parameter @@ -689,6 +706,7 @@ status_t AudioTrack::setSampleRate(uint32_t rate) if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { return BAD_VALUE; } + // TODO: Should we also check if the buffer size is compatible? mSampleRate = rate; mProxy->setSampleRate(rate); @@ -719,6 +737,42 @@ uint32_t AudioTrack::getSampleRate() const return mSampleRate; } +status_t AudioTrack::setPlaybackRate(float speed, float pitch) +{ + if (speed < AUDIO_TIMESTRETCH_SPEED_MIN + || speed > AUDIO_TIMESTRETCH_SPEED_MAX + || pitch < AUDIO_TIMESTRETCH_PITCH_MIN + || pitch > AUDIO_TIMESTRETCH_PITCH_MAX) { + return BAD_VALUE; + } + AutoMutex lock(mLock); + if (speed == mSpeed && pitch == mPitch) { + return NO_ERROR; + } + if (mIsTimed || isOffloadedOrDirect_l()) { + return INVALID_OPERATION; + } + if (mFlags & AUDIO_OUTPUT_FLAG_FAST) { + return INVALID_OPERATION; + } + // Check if the buffer size is compatible. + if (!isSampleRateSpeedAllowed_l(mSampleRate, speed)) { + ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch); + return BAD_VALUE; + } + mSpeed = speed; + mPitch = pitch; + mProxy->setPlaybackRate(speed, pitch); + return NO_ERROR; +} + +void AudioTrack::getPlaybackRate(float *speed, float *pitch) const +{ + AutoMutex lock(mLock); + *speed = mSpeed; + *pitch = mPitch; +} + status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount) { if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) { @@ -1086,8 +1140,16 @@ status_t AudioTrack::createTrack_l() // there _is_ a frameCount parameter. We silently ignore it. frameCount = mSharedBuffer->size() / mFrameSize; } else { - // For fast and normal streaming tracks, - // the frame count calculations and checks are done by server + // For fast tracks the frame count calculations and checks are done by server + + if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) { + // for normal tracks precompute the frame count based on speed. + const size_t minFrameCount = calculateMinFrameCount( + afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed); + if (frameCount < minFrameCount) { + frameCount = minFrameCount; + } + } } IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT; @@ -1230,6 +1292,7 @@ status_t AudioTrack::createTrack_l() } mAudioTrack->attachAuxEffect(mAuxEffectId); + // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack) // FIXME don't believe this lie mLatency = afLatency + (1000*frameCount) / mSampleRate; @@ -1255,6 +1318,7 @@ status_t AudioTrack::createTrack_l() mProxy->setSendLevel(mSendLevel); mProxy->setSampleRate(mSampleRate); + mProxy->setPlaybackRate(mSpeed, mPitch); mProxy->setMinimum(mNotificationFramesAct); mDeathNotifier = new DeathNotifier(this); @@ -1617,6 +1681,7 @@ nsecs_t AudioTrack::processAudioBuffer() // Cache other fields that will be needed soon uint32_t sampleRate = mSampleRate; + float speed = mSpeed; uint32_t notificationFrames = mNotificationFramesAct; if (mRefreshRemaining) { mRefreshRemaining = false; @@ -1745,7 +1810,7 @@ nsecs_t AudioTrack::processAudioBuffer() if (minFrames != (uint32_t) ~0) { // This "fudge factor" avoids soaking CPU, and compensates for late progress by server static const nsecs_t kFudgeNs = 10000000LL; // 10 ms - ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs; + ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs; } // If not supplying data by EVENT_MORE_DATA, then we're done @@ -1786,7 +1851,8 @@ nsecs_t AudioTrack::processAudioBuffer() if (mRetryOnPartialBuffer && !isOffloaded()) { mRetryOnPartialBuffer = false; if (avail < mRemainingFrames) { - int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate; + int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000) + / ((double)sampleRate * speed); if (ns < 0 || myns < ns) { ns = myns; } @@ -1841,7 +1907,7 @@ nsecs_t AudioTrack::processAudioBuffer() // that total to a sum == notificationFrames. if (0 < misalignment && misalignment <= mRemainingFrames) { mRemainingFrames = misalignment; - return (mRemainingFrames * 1100000000LL) / sampleRate; + return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed); } #endif @@ -1936,6 +2002,41 @@ uint32_t AudioTrack::updateAndGetPosition_l() return mPosition += (uint32_t) delta; } +bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const +{ + // applicable for mixing tracks only (not offloaded or direct) + if (mStaticProxy != 0) { + return true; // static tracks do not have issues with buffer sizing. + } + status_t status; + uint32_t afLatency; + status = AudioSystem::getLatency(mOutput, &afLatency); + if (status != NO_ERROR) { + ALOGE("getLatency(%d) failed status %d", mOutput, status); + return false; + } + + size_t afFrameCount; + status = AudioSystem::getFrameCount(mOutput, &afFrameCount); + if (status != NO_ERROR) { + ALOGE("getFrameCount(output=%d) status %d", mOutput, status); + return false; + } + + uint32_t afSampleRate; + status = AudioSystem::getSamplingRate(mOutput, &afSampleRate); + if (status != NO_ERROR) { + ALOGE("getSamplingRate(output=%d) status %d", mOutput, status); + return false; + } + + const size_t minFrameCount = + calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed); + ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu", + mFrameCount, minFrameCount); + return mFrameCount >= minFrameCount; +} + status_t AudioTrack::setParameters(const String8& keyValuePairs) { AutoMutex lock(mLock); @@ -2001,7 +2102,8 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp) return WOULD_BLOCK; // stale timestamp time, occurs before start. } const int64_t deltaTimeUs = timestampTimeUs - mStartUs; - const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate; + const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000 + / ((double)mSampleRate * mSpeed); if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) { // Verify that the counter can't count faster than the sample rate @@ -2088,7 +2190,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat, mChannelCount, mFrameCount); result.append(buffer); - snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus); + snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n", + mSampleRate, mSpeed, mStatus); result.append(buffer); snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency); result.append(buffer); diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp index 6d5f1af..ba67b40 100644 --- a/media/libmedia/AudioTrackShared.cpp +++ b/media/libmedia/AudioTrackShared.cpp @@ -793,6 +793,16 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount) (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags); } +void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch) +{ // do not call from multiple threads without holding lock + AudioTrackPlaybackRate playbackRate; + if (mPlaybackRateObserver.poll(playbackRate)) { + mPlaybackRate = playbackRate; + } + *speed = mPlaybackRate.mSpeed; + *pitch = mPlaybackRate.mPitch; +} + // --------------------------------------------------------------------------- StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp index 427bf7b..8fbb57c 100644 --- a/media/libstagefright/ESDS.cpp +++ b/media/libstagefright/ESDS.cpp @@ -136,6 +136,8 @@ status_t ESDS::parseESDescriptor(size_t offset, size_t size) { --size; if (streamDependenceFlag) { + if (size < 2) + return ERROR_MALFORMED; offset += 2; size -= 2; } @@ -145,11 +147,15 @@ status_t ESDS::parseESDescriptor(size_t offset, size_t size) { return ERROR_MALFORMED; } unsigned URLlength = mData[offset]; + if (URLlength >= size) + return ERROR_MALFORMED; offset += URLlength + 1; size -= URLlength + 1; } if (OCRstreamFlag) { + if (size < 2) + return ERROR_MALFORMED; offset += 2; size -= 2; diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 910ae32..f7fa2b6 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -874,6 +874,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->sampleTable = new SampleTable(mDataSource); } @@ -1028,6 +1031,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } original_fourcc = ntohl(original_fourcc); ALOGV("read original format: %d", original_fourcc); + + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(original_fourcc)); uint32_t num_channels = 0; uint32_t sample_rate = 0; @@ -1083,6 +1090,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setInt32(kKeyCryptoMode, defaultAlgorithmId); mLastTrack->meta->setInt32(kKeyCryptoDefaultIVSize, defaultIVSize); mLastTrack->meta->setData(kKeyCryptoKey, 'tenc', defaultKeyId, 16); @@ -1198,7 +1208,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { duration = ntohl(duration32); } } - if (duration != 0) { + if (duration != 0 && mLastTrack->timescale != 0) { mLastTrack->meta->setInt64( kKeyDuration, (duration * 1000000) / mLastTrack->timescale); } @@ -1262,6 +1272,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // display the timed text. // For encrypted files, there may also be more than one entry. const char *mime; + + if (mLastTrack == NULL) + return ERROR_MALFORMED; + CHECK(mLastTrack->meta->findCString(kKeyMIMEType, &mime)); if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) && strcasecmp(mime, "application/octet-stream")) { @@ -1308,6 +1322,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { uint16_t sample_size = U16_AT(&buffer[18]); uint32_t sample_rate = U32_AT(&buffer[24]) >> 16; + if (mLastTrack == NULL) + return ERROR_MALFORMED; + if (chunk_type != FOURCC('e', 'n', 'c', 'a')) { // if the chunk type is enca, we'll get the type from the sinf/frma box later mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); @@ -1369,6 +1386,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { // printf("*** coding='%s' width=%d height=%d\n", // chunk, width, height); + if (mLastTrack == NULL) + return ERROR_MALFORMED; + if (chunk_type != FOURCC('e', 'n', 'c', 'v')) { // if the chunk type is encv, we'll get the type from the sinf/frma box later mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type)); @@ -1394,6 +1414,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 't', 'c', 'o'): case FOURCC('c', 'o', '6', '4'): { + if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) + return ERROR_MALFORMED; + status_t err = mLastTrack->sampleTable->setChunkOffsetParams( chunk_type, data_offset, chunk_data_size); @@ -1409,6 +1432,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 't', 's', 'c'): { + if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) + return ERROR_MALFORMED; + status_t err = mLastTrack->sampleTable->setSampleToChunkParams( data_offset, chunk_data_size); @@ -1425,6 +1451,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 't', 's', 'z'): case FOURCC('s', 't', 'z', '2'): { + if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) + return ERROR_MALFORMED; + status_t err = mLastTrack->sampleTable->setSampleSizeParams( chunk_type, data_offset, chunk_data_size); @@ -1494,6 +1523,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 't', 't', 's'): { + if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) + return ERROR_MALFORMED; + *offset += chunk_size; status_t err = @@ -1509,6 +1541,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('c', 't', 't', 's'): { + if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) + return ERROR_MALFORMED; + *offset += chunk_size; status_t err = @@ -1524,6 +1559,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('s', 't', 's', 's'): { + if ((mLastTrack == NULL) || (mLastTrack->sampleTable == NULL)) + return ERROR_MALFORMED; + *offset += chunk_size; status_t err = @@ -1596,6 +1634,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_MALFORMED; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setData( kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4); @@ -1628,6 +1669,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setData( kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size); @@ -1642,6 +1686,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setData( kKeyHVCC, kTypeHVCC, buffer->data(), chunk_data_size); @@ -1675,6 +1722,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setData(kKeyD263, kTypeD263, buffer, chunk_data_size); break; @@ -1772,7 +1822,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } duration = d32; } - if (duration != 0) { + if (duration != 0 && mHeaderTimescale != 0) { mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale); } @@ -1821,7 +1871,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_MALFORMED; } - if (duration != 0) { + if (duration != 0 && mHeaderTimescale != 0) { mFileMetaData->setInt64(kKeyDuration, duration * 1000000 / mHeaderTimescale); } @@ -1856,6 +1906,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + uint32_t type = ntohl(buffer); // For the 3GPP file format, the handler-type within the 'hdlr' box // shall be 'text'. We also want to support 'sbtl' handler type @@ -1888,6 +1941,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('t', 'x', '3', 'g'): { + if (mLastTrack == NULL) + return ERROR_MALFORMED; + uint32_t type; const void *data; size_t size = 0; @@ -2029,6 +2085,8 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { return ERROR_MALFORMED; } ALOGV("sidx refid/timescale: %d/%d", referenceId, timeScale); + if (timeScale == 0) + return ERROR_MALFORMED; uint64_t earliestPresentationTime; uint64_t firstOffset; @@ -2112,6 +2170,9 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { uint64_t sidxDuration = total_duration * 1000000 / timeScale; + if (mLastTrack == NULL) + return ERROR_MALFORMED; + int64_t metaDuration; if (!mLastTrack->meta->findInt64(kKeyDuration, &metaDuration) || metaDuration == 0) { mLastTrack->meta->setInt64(kKeyDuration, sidxDuration); @@ -2162,6 +2223,9 @@ status_t MPEG4Extractor::parseTrackHeader( return ERROR_UNSUPPORTED; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setInt32(kKeyTrackID, id); size_t matrixOffset = dynSize + 16; @@ -2344,6 +2408,9 @@ status_t MPEG4Extractor::parseITunesMetaData(off64_t offset, size_t size) { int32_t delay, padding; if (sscanf(mLastCommentData, " %*x %x %x %*x", &delay, &padding) == 2) { + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); mLastTrack->meta->setInt32(kKeyEncoderPadding, padding); } @@ -2711,6 +2778,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( if (objectTypeIndication == 0xe1) { // This isn't MPEG4 audio at all, it's QCELP 14k... + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP); return OK; } @@ -2759,6 +2829,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( objectType = 32 + br.getBits(6); } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + //keep AOT type mLastTrack->meta->setInt32(kKeyAACAOT, objectType); @@ -2929,6 +3002,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return ERROR_UNSUPPORTED; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + int32_t prevSampleRate; CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate)); diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp index 433f555..2641e4e 100644 --- a/media/libstagefright/MediaClock.cpp +++ b/media/libstagefright/MediaClock.cpp @@ -92,6 +92,11 @@ void MediaClock::setPlaybackRate(float rate) { mPlaybackRate = rate; } +float MediaClock::getPlaybackRate() const { + Mutex::Autolock autoLock(mLock); + return mPlaybackRate; +} + status_t MediaClock::getMediaTime( int64_t realUs, int64_t *outMediaUs, bool allowPastMaxTime) const { if (outMediaUs == NULL) { diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp index bdd6d56..aba64d5 100644 --- a/media/libstagefright/SampleTable.cpp +++ b/media/libstagefright/SampleTable.cpp @@ -230,8 +230,13 @@ status_t SampleTable::setSampleToChunkParams( return ERROR_MALFORMED; } + if (SIZE_MAX / sizeof(SampleToChunkEntry) <= mNumSampleToChunkOffsets) + return ERROR_OUT_OF_RANGE; + mSampleToChunkEntries = - new SampleToChunkEntry[mNumSampleToChunkOffsets]; + new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets]; + if (!mSampleToChunkEntries) + return ERROR_OUT_OF_RANGE; for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) { uint8_t buffer[12]; @@ -330,11 +335,13 @@ status_t SampleTable::setTimeToSampleParams( } mTimeToSampleCount = U32_AT(&header[4]); - uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t); + uint64_t allocSize = mTimeToSampleCount * 2 * (uint64_t)sizeof(uint32_t); if (allocSize > SIZE_MAX) { return ERROR_OUT_OF_RANGE; } - mTimeToSample = new uint32_t[mTimeToSampleCount * 2]; + mTimeToSample = new (std::nothrow) uint32_t[mTimeToSampleCount * 2]; + if (!mTimeToSample) + return ERROR_OUT_OF_RANGE; size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2; if (mDataSource->readAt( @@ -376,12 +383,14 @@ status_t SampleTable::setCompositionTimeToSampleParams( } mNumCompositionTimeDeltaEntries = numEntries; - uint64_t allocSize = numEntries * 2 * sizeof(uint32_t); + uint64_t allocSize = numEntries * 2 * (uint64_t)sizeof(uint32_t); if (allocSize > SIZE_MAX) { return ERROR_OUT_OF_RANGE; } - mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries]; + mCompositionTimeDeltaEntries = new (std::nothrow) uint32_t[2 * numEntries]; + if (!mCompositionTimeDeltaEntries) + return ERROR_OUT_OF_RANGE; if (mDataSource->readAt( data_offset + 8, mCompositionTimeDeltaEntries, numEntries * 8) @@ -426,12 +435,15 @@ status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size) ALOGV("Table of sync samples is empty or has only a single entry!"); } - uint64_t allocSize = mNumSyncSamples * sizeof(uint32_t); + uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t); if (allocSize > SIZE_MAX) { return ERROR_OUT_OF_RANGE; } - mSyncSamples = new uint32_t[mNumSyncSamples]; + mSyncSamples = new (std::nothrow) uint32_t[mNumSyncSamples]; + if (!mSyncSamples) + return ERROR_OUT_OF_RANGE; + size_t size = mNumSyncSamples * sizeof(uint32_t); if (mDataSource->readAt(mSyncSampleOffset + 8, mSyncSamples, size) != (ssize_t)size) { @@ -499,7 +511,9 @@ void SampleTable::buildSampleEntriesTable() { return; } - mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes]; + mSampleTimeEntries = new (std::nothrow) SampleTimeEntry[mNumSampleSizes]; + if (!mSampleTimeEntries) + return; uint32_t sampleIndex = 0; uint32_t sampleTime = 0; diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 26f8da1..74f58e9 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -1455,6 +1455,10 @@ void LiveSession::changeConfiguration( if (bandwidthIndex >= 0) { mOrigBandwidthIndex = mCurBandwidthIndex; mCurBandwidthIndex = bandwidthIndex; + if (mOrigBandwidthIndex != mCurBandwidthIndex) { + ALOGI("#### Starting Bandwidth Switch: %zd => %zd", + mOrigBandwidthIndex, mCurBandwidthIndex); + } } CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size()); const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex); @@ -1574,6 +1578,7 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { if (timeUs >= 0) { mLastSeekTimeUs = timeUs; + mLastDequeuedTimeUs = timeUs; for (size_t i = 0; i < mPacketSources.size(); i++) { mPacketSources.editValueAt(i)->clear(); @@ -1626,8 +1631,10 @@ void LiveSession::onChangeConfiguration2(const sp<AMessage> &msg) { ALOGV("stream %zu changed: oldURI %s, newURI %s", i, mStreams[i].mUri.c_str(), URIs[i].c_str()); sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i)); - source->queueDiscontinuity( - ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + if (source->getLatestDequeuedMeta() != NULL) { + source->queueDiscontinuity( + ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true); + } } // Determine which decoders to shutdown on the player side, // a decoder has to be shutdown if its streamtype was active @@ -1687,10 +1694,6 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { // and resume audio. mSwapMask = mNewStreamMask & mStreamMask & ~resumeMask; switching = (mSwapMask != 0); - if (!switching) { - ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd", - mOrigBandwidthIndex, mCurBandwidthIndex); - } } mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs; } else { @@ -1843,7 +1846,11 @@ void LiveSession::onChangeConfiguration3(const sp<AMessage> &msg) { mSwitchInProgress = true; } else { mStreamMask = mNewStreamMask; - mOrigBandwidthIndex = mCurBandwidthIndex; + if (mOrigBandwidthIndex != mCurBandwidthIndex) { + ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd", + mOrigBandwidthIndex, mCurBandwidthIndex); + mOrigBandwidthIndex = mCurBandwidthIndex; + } } ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x", @@ -1970,11 +1977,19 @@ void LiveSession::onPollBuffering() { bool underflow, ready, down, up; if (checkBuffering(underflow, ready, down, up)) { - if (mInPreparationPhase && ready) { - postPrepared(OK); + if (mInPreparationPhase) { + // Allow down switch even if we're still preparing. + // + // Some streams have a high bandwidth index as default, + // when bandwidth is low, it takes a long time to buffer + // to ready mark, then it immediately pauses after start + // as we have to do a down switch. It's better experience + // to restart from a lower index, if we detect low bw. + if (!switchBandwidthIfNeeded(false /* up */, down) && ready) { + postPrepared(OK); + } } - // don't switch before we report prepared if (!mInPreparationPhase) { if (ready) { stopBufferingIfNecessary(); @@ -1982,8 +1997,7 @@ void LiveSession::onPollBuffering() { startBufferingIfNecessary(); } switchBandwidthIfNeeded(up, down); - } - + } } schedulePollBuffering(); @@ -2075,7 +2089,8 @@ bool LiveSession::checkBuffering( if (mPacketSources[i]->isFinished(0 /* duration */)) { percent = 100; } else { - percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs); + percent = (int32_t)(100.0 * + (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs); } if (minBufferPercent < 0 || percent < minBufferPercent) { minBufferPercent = percent; @@ -2158,10 +2173,14 @@ void LiveSession::notifyBufferingUpdate(int32_t percentage) { notify->post(); } -void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { +/* + * returns true if a bandwidth switch is actually needed (and started), + * returns false otherwise + */ +bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // no need to check bandwidth if we only have 1 bandwidth settings if (mSwitchInProgress || mBandwidthItems.size() < 2) { - return; + return false; } int32_t bandwidthBps; @@ -2170,7 +2189,7 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { mLastBandwidthBps = bandwidthBps; } else { ALOGV("no bandwidth estimate."); - return; + return false; } int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth; @@ -2189,16 +2208,16 @@ void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) { // bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70% // of measured bw. In that case we don't want to do anything, since we have // both enough buffer and enough bw. - if (bandwidthIndex == mCurBandwidthIndex - || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex) - || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) { - return; + if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex) + || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) { + // if not yet prepared, just restart again with new bw index. + // this is faster and playback experience is cleaner. + changeConfiguration( + mInPreparationPhase ? 0 : -1ll, bandwidthIndex); + return true; } - - ALOGI("#### Starting Bandwidth Switch: %zd => %zd", - mCurBandwidthIndex, bandwidthIndex); - changeConfiguration(-1, bandwidthIndex, false); } + return false; } void LiveSession::postError(status_t err) { diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h index c587f40..9117bb1 100644 --- a/media/libstagefright/httplive/LiveSession.h +++ b/media/libstagefright/httplive/LiveSession.h @@ -292,7 +292,7 @@ private: bool checkSwitchProgress( sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil); - void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); + bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow); void schedulePollBuffering(); void cancelPollBuffering(); diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 0676a33..c7912c0 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -355,10 +355,15 @@ int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) { int64_t time2 = -1; int64_t durationUs = 0; - List<sp<ABuffer> >::iterator it = mBuffers.begin(); - while (it != mBuffers.end()) { + List<sp<ABuffer> >::iterator it; + for (it = mBuffers.begin(); it != mBuffers.end(); it++) { const sp<ABuffer> &buffer = *it; + int32_t discard; + if (buffer->meta()->findInt32("discard", &discard) && discard) { + continue; + } + int64_t timeUs; if (buffer->meta()->findInt64("timeUs", &timeUs)) { if (time1 < 0 || timeUs < time1) { @@ -373,8 +378,6 @@ int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) { durationUs += time2 - time1; time1 = time2 = -1; } - - ++it; } return durationUs + (time2 - time1); @@ -393,11 +396,19 @@ int64_t AnotherPacketSource::getEstimatedDurationUs() { return getBufferedDurationUs_l(&finalResult); } - List<sp<ABuffer> >::iterator it = mBuffers.begin(); - sp<ABuffer> buffer = *it; + sp<ABuffer> buffer; + int32_t discard; + int64_t startTimeUs = -1ll; + List<sp<ABuffer> >::iterator it; + for (it = mBuffers.begin(); it != mBuffers.end(); it++) { + buffer = *it; + if (buffer->meta()->findInt32("discard", &discard) && discard) { + continue; + } + buffer->meta()->findInt64("timeUs", &startTimeUs); + break; + } - int64_t startTimeUs; - buffer->meta()->findInt64("timeUs", &startTimeUs); if (startTimeUs < 0) { return 0; } |