Bug 1130450: Properly handle MP4 with Apple QT extension. r=k17e a=lmandel
authorJean-Yves Avenard <jyavenard@mozilla.com>
Thu, 19 Feb 2015 15:37:11 +1300
changeset 240841 269ea0da72c30ecededd5776b819710865a3e783
parent 240840 b13975d7d5d2fda458a088151f5c966818f41624
child 240842 82ab77166a5f014a4ffb3ab139cafa9a56c6f20d
push id7672
push userrgiles@mozilla.com
push dateMon, 23 Feb 2015 06:50:33 +0000
treeherdermozilla-aurora@d8e630336213 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersk17e, lmandel
bugs1130450
milestone37.0a2
Bug 1130450: Properly handle MP4 with Apple QT extension. r=k17e a=lmandel
media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -1238,46 +1238,86 @@ status_t MPEG4Extractor::parseChunk(off6
         }
 
         case FOURCC('m', 'p', '4', 'a'):
         case FOURCC('.', 'm', 'p', '3'):
         case FOURCC('e', 'n', 'c', 'a'):
         case FOURCC('s', 'a', 'm', 'r'):
         case FOURCC('s', 'a', 'w', 'b'):
         {
+            // QT's MP4 may have an empty MP4A atom within a MP4A atom.
+            // Ignore it.
+            if (chunk_data_size == 4) {
+                *offset += chunk_size;
+                break;
+            }
             uint8_t buffer[8 + 20];
             if (chunk_data_size < (ssize_t)sizeof(buffer)) {
                 // Basic AudioSampleEntry size.
                 return ERROR_MALFORMED;
             }
 
             if (mDataSource->readAt(
                         data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
                 return ERROR_IO;
             }
 
             uint16_t data_ref_index = U16_AT(&buffer[6]);
+            uint16_t qt_version = U16_AT(&buffer[8]);
             uint32_t num_channels = U16_AT(&buffer[16]);
 
             uint16_t sample_size = U16_AT(&buffer[18]);
             uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
 
             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));
                 AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
             }
             ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
                    chunk, num_channels, sample_size, sample_rate);
             mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
             mLastTrack->meta->setInt32(kKeySampleSize, sample_size);
             mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
 
+            uint64_t skip = 0;
+            if (qt_version == 1) {
+                // Skip QTv1 extension
+                // uint32_t SamplesPerPacket
+                // uint32_t BytesPerPacket
+                // uint32_t BytesPerFrame
+                // uint32_t BytesPerSample
+                skip = 16;
+            } else if (qt_version == 2) {
+                // Skip QTv2 extension
+                // uint32_t Qt V2 StructSize
+                // double SampleRate
+                // uint32_t ChannelCount
+                // uint32_t Reserved
+                // uint32_t BitsPerChannel
+                // uint32_t LPCMFormatSpecificFlags
+                // uint32_t BytesPerAudioPacket
+                // uint32_t LPCMFramesPerAudioPacket
+                // if (Qt V2 StructSize > 72) {
+                //     StructSize-72: Qt V2 extension
+                // }
+                uint32_t structSize32;
+                if (mDataSource->readAt(
+                            data_offset + 28, &structSize32, sizeof(structSize32))
+                        < (ssize_t)sizeof(structSize32)) {
+                    return ERROR_IO;
+                }
+                uint32_t structSize = ntohl(structSize32);
+                skip += 36;
+                if (structSize > 72) {
+                    skip += structSize - 72;
+                }
+            }
             off64_t stop_offset = *offset + chunk_size;
-            *offset = data_offset + sizeof(buffer);
+            *offset = data_offset + sizeof(buffer) + skip;
             while (*offset < stop_offset) {
                 status_t err = parseChunk(offset, depth + 1);
                 if (err != OK) {
                     return err;
                 }
             }
 
             if (*offset != stop_offset) {
@@ -1331,16 +1371,21 @@ status_t MPEG4Extractor::parseChunk(off6
 
             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) {
                     return err;
                 }
+                // Some Apple QuickTime muxed videos appear to have some padding.
+                // Ignore it and assume we've reached the end.
+                if (stop_offset - *offset < 8) {
+                    *offset = stop_offset;
+                }
             }
 
             if (*offset != stop_offset) {
                 return ERROR_MALFORMED;
             }
             break;
         }
 
@@ -1851,16 +1896,33 @@ status_t MPEG4Extractor::parseChunk(off6
 
         case FOURCC('s', 'i', 'd', 'x'):
         {
             parseSegmentIndex(data_offset, chunk_data_size);
             *offset += chunk_size;
             return UNKNOWN_ERROR; // stop parsing after sidx
         }
 
+        case FOURCC('w', 'a', 'v', 'e'):
+        {
+            off64_t stop_offset = *offset + chunk_size;
+            *offset = data_offset;
+            while (*offset < stop_offset) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+            break;
+        }
+
         default:
         {
             *offset += chunk_size;
             break;
         }
     }
 
     return OK;