|
30 | 30 | #include <string.h> |
31 | 31 |
|
32 | 32 | #include <media/stagefright/foundation/ADebug.h> |
| 33 | +#include <media/stagefright/foundation/AMessage.h> |
33 | 34 | #include <media/stagefright/DataSource.h> |
34 | 35 | #include <media/stagefright/MediaBuffer.h> |
35 | 36 | #include <media/stagefright/MediaBufferGroup.h> |
@@ -2301,51 +2302,121 @@ static bool isCompatibleBrand(uint32_t fourcc) { |
2301 | 2302 |
|
2302 | 2303 | // Attempt to actually parse the 'ftyp' atom and determine if a suitable |
2303 | 2304 | // compatible brand is present. |
| 2305 | +// Also try to identify where this file's metadata ends |
| 2306 | +// (end of the 'moov' atom) and report it to the caller as part of |
| 2307 | +// the metadata. |
2304 | 2308 | static bool BetterSniffMPEG4( |
2305 | | - const sp<DataSource> &source, String8 *mimeType, float *confidence) { |
2306 | | - uint8_t header[12]; |
2307 | | - if (source->readAt(0, header, 12) != 12 |
2308 | | - || memcmp("ftyp", &header[4], 4)) { |
2309 | | - return false; |
2310 | | - } |
| 2309 | + const sp<DataSource> &source, String8 *mimeType, float *confidence, |
| 2310 | + sp<AMessage> *meta) { |
| 2311 | + // We scan up to 128 bytes to identify this file as an MP4. |
| 2312 | + static const off64_t kMaxScanOffset = 128ll; |
2311 | 2313 |
|
2312 | | - size_t atomSize = U32_AT(&header[0]); |
2313 | | - if (atomSize < 16 || (atomSize % 4) != 0) { |
2314 | | - return false; |
2315 | | - } |
| 2314 | + off64_t offset = 0ll; |
| 2315 | + bool foundGoodFileType = false; |
| 2316 | + off64_t moovAtomEndOffset = -1ll; |
| 2317 | + bool done = false; |
2316 | 2318 |
|
2317 | | - bool success = false; |
2318 | | - if (isCompatibleBrand(U32_AT(&header[8]))) { |
2319 | | - success = true; |
2320 | | - } else { |
2321 | | - size_t numCompatibleBrands = (atomSize - 16) / 4; |
2322 | | - for (size_t i = 0; i < numCompatibleBrands; ++i) { |
2323 | | - uint8_t tmp[4]; |
2324 | | - if (source->readAt(16 + i * 4, tmp, 4) != 4) { |
| 2319 | + while (!done && offset < kMaxScanOffset) { |
| 2320 | + uint32_t hdr[2]; |
| 2321 | + if (source->readAt(offset, hdr, 8) < 8) { |
| 2322 | + return false; |
| 2323 | + } |
| 2324 | + |
| 2325 | + uint64_t chunkSize = ntohl(hdr[0]); |
| 2326 | + uint32_t chunkType = ntohl(hdr[1]); |
| 2327 | + off64_t chunkDataOffset = offset + 8; |
| 2328 | + |
| 2329 | + if (chunkSize == 1) { |
| 2330 | + if (source->readAt(offset + 8, &chunkSize, 8) < 8) { |
2325 | 2331 | return false; |
2326 | 2332 | } |
2327 | 2333 |
|
2328 | | - if (isCompatibleBrand(U32_AT(&tmp[0]))) { |
2329 | | - success = true; |
| 2334 | + chunkSize = ntoh64(chunkSize); |
| 2335 | + chunkDataOffset += 8; |
| 2336 | + |
| 2337 | + if (chunkSize < 16) { |
| 2338 | + // The smallest valid chunk is 16 bytes long in this case. |
| 2339 | + return false; |
| 2340 | + } |
| 2341 | + } else if (chunkSize < 8) { |
| 2342 | + // The smallest valid chunk is 8 bytes long. |
| 2343 | + return false; |
| 2344 | + } |
| 2345 | + |
| 2346 | + off64_t chunkDataSize = offset + chunkSize - chunkDataOffset; |
| 2347 | + |
| 2348 | + switch (chunkType) { |
| 2349 | + case FOURCC('f', 't', 'y', 'p'): |
| 2350 | + { |
| 2351 | + if (chunkDataSize < 8) { |
| 2352 | + return false; |
| 2353 | + } |
| 2354 | + |
| 2355 | + uint32_t numCompatibleBrands = (chunkDataSize - 8) / 4; |
| 2356 | + for (size_t i = 0; i < numCompatibleBrands + 2; ++i) { |
| 2357 | + if (i == 1) { |
| 2358 | + // Skip this index, it refers to the minorVersion, |
| 2359 | + // not a brand. |
| 2360 | + continue; |
| 2361 | + } |
| 2362 | + |
| 2363 | + uint32_t brand; |
| 2364 | + if (source->readAt( |
| 2365 | + chunkDataOffset + 4 * i, &brand, 4) < 4) { |
| 2366 | + return false; |
| 2367 | + } |
| 2368 | + |
| 2369 | + brand = ntohl(brand); |
| 2370 | + |
| 2371 | + if (isCompatibleBrand(brand)) { |
| 2372 | + foundGoodFileType = true; |
| 2373 | + break; |
| 2374 | + } |
| 2375 | + } |
| 2376 | + |
| 2377 | + if (!foundGoodFileType) { |
| 2378 | + return false; |
| 2379 | + } |
| 2380 | + |
2330 | 2381 | break; |
2331 | 2382 | } |
| 2383 | + |
| 2384 | + case FOURCC('m', 'o', 'o', 'v'): |
| 2385 | + { |
| 2386 | + moovAtomEndOffset = offset + chunkSize; |
| 2387 | + |
| 2388 | + done = true; |
| 2389 | + break; |
| 2390 | + } |
| 2391 | + |
| 2392 | + default: |
| 2393 | + break; |
2332 | 2394 | } |
| 2395 | + |
| 2396 | + offset += chunkSize; |
2333 | 2397 | } |
2334 | 2398 |
|
2335 | | - if (!success) { |
| 2399 | + if (!foundGoodFileType) { |
2336 | 2400 | return false; |
2337 | 2401 | } |
2338 | 2402 |
|
2339 | 2403 | *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4; |
2340 | 2404 | *confidence = 0.4f; |
2341 | 2405 |
|
| 2406 | + if (moovAtomEndOffset >= 0) { |
| 2407 | + *meta = new AMessage; |
| 2408 | + (*meta)->setInt64("meta-data-size", moovAtomEndOffset); |
| 2409 | + |
| 2410 | + LOGV("found metadata size: %lld", moovAtomEndOffset); |
| 2411 | + } |
| 2412 | + |
2342 | 2413 | return true; |
2343 | 2414 | } |
2344 | 2415 |
|
2345 | 2416 | bool SniffMPEG4( |
2346 | 2417 | const sp<DataSource> &source, String8 *mimeType, float *confidence, |
2347 | | - sp<AMessage> *) { |
2348 | | - if (BetterSniffMPEG4(source, mimeType, confidence)) { |
| 2418 | + sp<AMessage> *meta) { |
| 2419 | + if (BetterSniffMPEG4(source, mimeType, confidence, meta)) { |
2349 | 2420 | return true; |
2350 | 2421 | } |
2351 | 2422 |
|
|
0 commit comments