From 96b48f71a97d9a0b72b3fef2ecfcd8215857fcc6 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Mon, 10 Jul 2017 14:44:49 -0700 Subject: [PATCH] Allow edit list atom to appear earlier in the file While processing the 'elst' atom, MPEG4Extractor was expecting duration and sample rate to already be known. This implies an ordering of atoms that is not required by the spec, and indeed some muxers may emit the 'elst' atom before the atoms that define sample rate and duration. This change caches the values from the 'elst' atom until a track has been completely processed, allowing the 'elst' atom to appear first. Bug: 63390273 Test: decoded test files, ran CTS Change-Id: I30abca70ead7990eb8a7d19a2e4b31212b9bc311 --- media/libstagefright/MPEG4Extractor.cpp | 98 ++++++++++++++----- media/libstagefright/include/MPEG4Extractor.h | 3 + 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 941c759fca..65e172a019 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -463,6 +463,66 @@ sp MPEG4Extractor::getTrackMetaData( return NULL; } + int64_t duration; + int32_t samplerate; + if (track->has_elst && mHeaderTimescale != 0 && + track->meta->findInt64(kKeyDuration, &duration) && + track->meta->findInt32(kKeySampleRate, &samplerate)) { + + track->has_elst = false; + + if (track->elst_segment_duration > INT64_MAX) { + goto editlistoverflow; + } + int64_t segment_duration = track->elst_segment_duration; + int64_t media_time = track->elst_media_time; + int64_t halfscale = mHeaderTimescale / 2; + + int64_t delay; + // delay = ((media_time * samplerate) + halfscale) / mHeaderTimescale; + if (__builtin_mul_overflow(media_time, samplerate, &delay) || + __builtin_add_overflow(delay, halfscale, &delay) || + (delay /= mHeaderTimescale, false) || + delay > INT32_MAX || + delay < INT32_MIN) { + goto editlistoverflow; + } + track->meta->setInt32(kKeyEncoderDelay, delay); + + int64_t scaled_duration; + // scaled_duration = ((duration * mHeaderTimescale) + 500000) / 1000000; + if (__builtin_mul_overflow(duration, mHeaderTimescale, &scaled_duration) || + __builtin_add_overflow(scaled_duration, 500000, &scaled_duration)) { + goto editlistoverflow; + } + scaled_duration /= 1000000; + + int64_t segment_end; + int64_t padding; + if (__builtin_add_overflow(segment_duration, media_time, &segment_end) || + __builtin_sub_overflow(scaled_duration, segment_end, &padding)) { + goto editlistoverflow; + } + + if (padding < 0) { + // track duration from media header (which is what kKeyDuration is) might + // be slightly shorter than the segment duration, which would make the + // padding negative. Clamp to zero. + padding = 0; + } + + int64_t paddingsamples; + // paddingsamples = ((padding * samplerate) + halfscale) / mHeaderTimescale; + if (__builtin_mul_overflow(padding, samplerate, &paddingsamples) || + __builtin_add_overflow(paddingsamples, halfscale, &paddingsamples) || + (paddingsamples /= mHeaderTimescale, false) || + paddingsamples > INT32_MAX) { + goto editlistoverflow; + } + track->meta->setInt32(kKeyEncoderPadding, paddingsamples); + } + editlistoverflow: + if ((flags & kIncludeExtensiveMetaData) && !track->includes_expensive_metadata) { track->includes_expensive_metadata = true; @@ -989,6 +1049,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { track->skipTrack = false; track->timescale = 0; track->meta->setCString(kKeyMIMEType, "application/octet-stream"); + track->has_elst = false; } off64_t stop_offset = *offset + chunk_size; @@ -1055,6 +1116,10 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { { *offset += chunk_size; + if (!mLastTrack) { + return ERROR_MALFORMED; + } + // See 14496-12 8.6.6 uint8_t version; if (mDataSource->readAt(data_offset, &version, 1) < 1) { @@ -1069,8 +1134,6 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { if (entry_count != 1) { // we only support a single entry at the moment, for gapless playback ALOGW("ignoring edit list with %d entries", entry_count); - } else if (mHeaderTimescale == 0) { - ALOGW("ignoring edit list because timescale is 0"); } else { off64_t entriesoffset = data_offset + 8; uint64_t segment_duration; @@ -1094,31 +1157,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { return ERROR_IO; } - uint64_t halfscale = mHeaderTimescale / 2; - segment_duration = (segment_duration * 1000000 + halfscale)/ mHeaderTimescale; - media_time = (media_time * 1000000 + halfscale) / mHeaderTimescale; - - int64_t duration; - int32_t samplerate; - if (!mLastTrack) { - return ERROR_MALFORMED; - } - if (mLastTrack->meta->findInt64(kKeyDuration, &duration) && - mLastTrack->meta->findInt32(kKeySampleRate, &samplerate)) { - - int64_t delay = (media_time * samplerate + 500000) / 1000000; - mLastTrack->meta->setInt32(kKeyEncoderDelay, delay); - - int64_t paddingus = duration - (int64_t)(segment_duration + media_time); - if (paddingus < 0) { - // track duration from media header (which is what kKeyDuration is) might - // be slightly shorter than the segment duration, which would make the - // padding negative. Clamp to zero. - paddingus = 0; - } - int64_t paddingsamples = (paddingus * samplerate + 500000) / 1000000; - mLastTrack->meta->setInt32(kKeyEncoderPadding, paddingsamples); - } + // save these for later, because the elst atom might precede + // the atoms that actually gives us the duration and sample rate + // needed to calculate the padding and delay values + mLastTrack->has_elst = true; + mLastTrack->elst_media_time = media_time; + mLastTrack->elst_segment_duration = segment_duration; } break; } diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h index f84711971d..1efe6b984b 100644 --- a/media/libstagefright/include/MPEG4Extractor.h +++ b/media/libstagefright/include/MPEG4Extractor.h @@ -82,6 +82,9 @@ private: sp sampleTable; bool includes_expensive_metadata; bool skipTrack; + bool has_elst; + int64_t elst_media_time; + uint64_t elst_segment_duration; }; Vector mSidxEntries;