diff options
Diffstat (limited to 'media/libstagefright/MPEG4Extractor.cpp')
-rw-r--r-- | media/libstagefright/MPEG4Extractor.cpp | 258 |
1 files changed, 235 insertions, 23 deletions
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp index 910ae32..28a9ed9 100644 --- a/media/libstagefright/MPEG4Extractor.cpp +++ b/media/libstagefright/MPEG4Extractor.cpp @@ -739,6 +739,17 @@ static bool underMetaDataPath(const Vector<uint32_t> &path) { && path[3] == FOURCC('i', 'l', 's', 't'); } +static bool underQTMetaPath(const Vector<uint32_t> &path, int32_t depth) { + return path.size() >= 2 + && path[0] == FOURCC('m', 'o', 'o', 'v') + && path[1] == FOURCC('m', 'e', 't', 'a') + && (depth == 2 + || (depth == 3 + && (path[2] == FOURCC('h', 'd', 'l', 'r') + || path[2] == FOURCC('i', 'l', 's', 't') + || path[2] == FOURCC('k', 'e', 'y', 's')))); +} + // Given a time in seconds since Jan 1 1904, produce a human-readable string. static void convertTimeToDate(int64_t time_1904, String8 *s) { time_t time_1970 = time_1904 - (((66 * 365 + 17) * 24) * 3600); @@ -874,6 +885,9 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { } } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + mLastTrack->sampleTable = new SampleTable(mDataSource); } @@ -1028,6 +1042,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 +1101,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 +1219,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 +1283,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 +1333,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 +1397,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 +1425,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 +1443,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 +1462,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 +1534,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 +1552,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 +1570,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 +1645,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 +1680,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 +1697,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 +1733,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; @@ -1682,31 +1743,35 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { case FOURCC('m', 'e', 't', 'a'): { - uint8_t buffer[4]; - if (chunk_data_size < (off64_t)sizeof(buffer)) { - *offset += chunk_size; - return ERROR_MALFORMED; - } + off64_t stop_offset = *offset + chunk_size; + *offset = data_offset; + bool isParsingMetaKeys = underQTMetaPath(mPath, 2); + if (!isParsingMetaKeys) { + uint8_t buffer[4]; + if (chunk_data_size < (off64_t)sizeof(buffer)) { + *offset += chunk_size; + return ERROR_MALFORMED; + } - if (mDataSource->readAt( - data_offset, buffer, 4) < 4) { - *offset += chunk_size; - return ERROR_IO; - } + if (mDataSource->readAt( + data_offset, buffer, 4) < 4) { + *offset += chunk_size; + return ERROR_IO; + } - if (U32_AT(buffer) != 0) { - // Should be version 0, flags 0. + if (U32_AT(buffer) != 0) { + // Should be version 0, flags 0. - // If it's not, let's assume this is one of those - // apparently malformed chunks that don't have flags - // and completely different semantics than what's - // in the MPEG4 specs and skip it. - *offset += chunk_size; - return OK; + // If it's not, let's assume this is one of those + // apparently malformed chunks that don't have flags + // and completely different semantics than what's + // in the MPEG4 specs and skip it. + *offset += chunk_size; + return OK; + } + *offset += sizeof(buffer); } - off64_t stop_offset = *offset + chunk_size; - *offset = data_offset + sizeof(buffer); while (*offset < stop_offset) { status_t err = parseChunk(offset, depth + 1); if (err != OK) { @@ -1772,7 +1837,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 +1886,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); } @@ -1850,12 +1915,19 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { { *offset += chunk_size; + if (underQTMetaPath(mPath, 3)) { + break; + } + uint32_t buffer; if (mDataSource->readAt( data_offset + 8, &buffer, 4) < 4) { 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 @@ -1867,6 +1939,16 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { break; } + case FOURCC('k', 'e', 'y', 's'): + { + *offset += chunk_size; + + if (underQTMetaPath(mPath, 3)) { + parseQTMetaKey(data_offset, chunk_data_size); + } + break; + } + case FOURCC('t', 'r', 'e', 'x'): { *offset += chunk_size; @@ -1888,6 +1970,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; @@ -1994,6 +2079,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) { default: { + // check if we're parsing 'ilst' for meta keys + // if so, treat type as a number (key-id). + if (underQTMetaPath(mPath, 3)) { + parseQTMetaVal(chunk_type, data_offset, chunk_data_size); + } + *offset += chunk_size; break; } @@ -2029,6 +2120,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 +2205,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); @@ -2119,7 +2215,108 @@ status_t MPEG4Extractor::parseSegmentIndex(off64_t offset, size_t size) { return OK; } +status_t MPEG4Extractor::parseQTMetaKey(off64_t offset, size_t size) { + if (size < 8) { + return ERROR_MALFORMED; + } + uint32_t count; + if (!mDataSource->getUInt32(offset + 4, &count)) { + return ERROR_MALFORMED; + } + + if (mMetaKeyMap.size() > 0) { + ALOGW("'keys' atom seen again, discarding existing entries"); + mMetaKeyMap.clear(); + } + + off64_t keyOffset = offset + 8; + off64_t stopOffset = offset + size; + for (size_t i = 1; i <= count; i++) { + if (keyOffset + 8 > stopOffset) { + return ERROR_MALFORMED; + } + + uint32_t keySize; + if (!mDataSource->getUInt32(keyOffset, &keySize) + || keySize < 8 + || keyOffset + keySize > stopOffset) { + return ERROR_MALFORMED; + } + + uint32_t type; + if (!mDataSource->getUInt32(keyOffset + 4, &type) + || type != FOURCC('m', 'd', 't', 'a')) { + return ERROR_MALFORMED; + } + + keySize -= 8; + keyOffset += 8; + + sp<ABuffer> keyData = new ABuffer(keySize); + if (keyData->data() == NULL) { + return ERROR_MALFORMED; + } + if (mDataSource->readAt( + keyOffset, keyData->data(), keySize) < (ssize_t) keySize) { + return ERROR_MALFORMED; + } + + AString key((const char *)keyData->data(), keySize); + mMetaKeyMap.add(i, key); + + keyOffset += keySize; + } + return OK; +} + +status_t MPEG4Extractor::parseQTMetaVal( + int32_t keyId, off64_t offset, size_t size) { + ssize_t index = mMetaKeyMap.indexOfKey(keyId); + if (index < 0) { + // corresponding key is not present, ignore + return ERROR_MALFORMED; + } + + if (size <= 16) { + return ERROR_MALFORMED; + } + uint32_t dataSize; + if (!mDataSource->getUInt32(offset, &dataSize) + || dataSize > size || dataSize <= 16) { + return ERROR_MALFORMED; + } + uint32_t atomFourCC; + if (!mDataSource->getUInt32(offset + 4, &atomFourCC) + || atomFourCC != FOURCC('d', 'a', 't', 'a')) { + return ERROR_MALFORMED; + } + uint32_t dataType; + if (!mDataSource->getUInt32(offset + 8, &dataType) + || ((dataType & 0xff000000) != 0)) { + // not well-known type + return ERROR_MALFORMED; + } + + dataSize -= 16; + offset += 16; + + if (dataType == 23 && dataSize >= 4) { + // BE Float32 + uint32_t val; + if (!mDataSource->getUInt32(offset, &val)) { + return ERROR_MALFORMED; + } + if (!strcasecmp(mMetaKeyMap[index].c_str(), "com.android.capture.fps")) { + mFileMetaData->setFloat(kKeyCaptureFramerate, *(float *)&val); + } + } else { + // add more keys if needed + ALOGV("ignoring key: type %d, size %d", dataType, dataSize); + } + + return OK; +} status_t MPEG4Extractor::parseTrackHeader( off64_t data_offset, off64_t data_size) { @@ -2162,6 +2359,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 +2544,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 +2914,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 +2965,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 +3138,9 @@ status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio( return ERROR_UNSUPPORTED; } + if (mLastTrack == NULL) + return ERROR_MALFORMED; + int32_t prevSampleRate; CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate)); |