Index: media/base/container_names.cc |
diff --git a/media/base/container_names.cc b/media/base/container_names.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..96ccce2862da515c296f4cf05fc25b0a4cf33c01 |
--- /dev/null |
+++ b/media/base/container_names.cc |
@@ -0,0 +1,1915 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "media/base/container_names.h" |
+ |
+#include <cctype> |
+#include <limits> |
+ |
+#include "base/basictypes.h" |
+#include "base/logging.h" |
+#include "media/base/bit_reader.h" |
+ |
+namespace media { |
+ |
+namespace container_names { |
+ |
+#define TAG(a, b, c, d) \ |
+ ((static_cast<uint8>(a) << 24) | (static_cast<uint8>(b) << 16) | \ |
acolwell GONE FROM CHROMIUM
2013/05/18 01:22:09
nit: Is it always safe to shift a uint8 more than
jrummell
2013/05/22 18:27:39
The problem with casting to uint32 is that each va
|
+ (static_cast<uint8>(c) << 8) | (static_cast<uint8>(d))) |
+ |
+// Helper function to read 2 bytes (16 bits, big endian) from a buffer. |
+static uint32 Read16(const uint8* p) { |
acolwell GONE FROM CHROMIUM
2013/05/18 01:22:09
I think these ReadXXX methods should just be repla
jrummell
2013/05/22 18:27:39
The problem with that is that most formats have a
|
+ return p[0] << 8 | p[1]; |
+} |
+ |
+// Helper function to read 3 bytes (24 bits, big endian) from a buffer. |
+static uint32 Read24(const uint8* p) { |
+ return p[0] << 16 | p[1] << 8 | p[2]; |
+} |
+ |
+// Helper function to read 4 bytes (32 bits, big endian) from a buffer. |
+static uint32 Read32(const uint8* p) { |
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; |
+} |
+ |
+// Helper function to read 4 bytes (32 bits, little endian) from a buffer. |
+static uint32 Read32LE(const uint8* p) { |
+ return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; |
acolwell GONE FROM CHROMIUM
2013/05/18 01:22:09
It would be nice to see this method rewritten in t
|
+} |
+ |
+// Helper function to do buffer comparisons with a string without going off the |
+// end of the buffer. |
+static bool StartsWith(const uint8* buffer, |
+ size_t buffer_size, |
+ const char* search) { |
+ size_t search_size = strlen(search); |
+ return (search_size <= buffer_size && |
+ memcmp(buffer, search, search_size) == 0); |
+} |
+ |
+// Helper function to do buffer comparisons with another buffer (to allow for |
+// embedded \0 in the comparison) without going off the end of the buffer. |
+static bool StartsWith(const uint8* buffer, |
+ size_t buffer_size, |
+ const uint8* search, |
+ size_t search_size) { |
+ return (search_size <= buffer_size && |
+ memcmp(buffer, search, search_size) == 0); |
xhwang
2013/05/20 23:24:24
nit: how about s/search/prefix ?
jrummell
2013/05/22 18:27:39
Done.
|
+} |
+ |
+// Helper function to read bits from a bit stream. |
+static int ReadBits(BitReader* reader, int num_bits) { |
xhwang
2013/05/20 23:24:24
I wonder whether it's always more convenient to wr
jrummell
2013/05/22 18:27:39
This is used in the MP4 code. They want to fail if
|
+ DCHECK_GE(reader->bits_available(), num_bits); |
+ int value; |
+ reader->ReadBits(num_bits, &value); |
+ return value; |
+} |
+ |
+// For some formats the signature is a bunch of characters. They are defined |
+// below. Note that the first 4 characters of the string may be used as a TAG |
+// in LookupContainerByFirst4. For signatures that contain embedded \0, use |
+// uint8[]. |
+#define BYTE_ORDER_MARK "\xef\xbb\xbf" |
xhwang
2013/05/20 23:24:24
drop extra white spaces?
jrummell
2013/05/22 18:27:39
Done.
|
+ |
+static const char kAmrSignature[] = "#!AMR"; |
+static const uint8 kAsfSignature[] = { 0x30, 0x26, 0xb2, 0x75, 0x8e, 0x66, 0xcf, |
+ 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, |
+ 0xce, 0x6c }; |
+static const char kAssSignature[] = "[Script Info]"; |
+static const char kAssBomSignature[] = BYTE_ORDER_MARK "[Script Info]"; |
+static const char kHlsSignature[] = "#EXTM3U"; |
xhwang
2013/05/20 23:24:24
Define consts right before where where it's used.
jrummell
2013/05/22 18:27:39
Done.
|
+static const uint8 kWtvSignature[] = { 0xb7, 0xd8, 0x00, 0x20, 0x37, 0x49, 0xda, |
+ 0x11, 0xa6, 0x4e, 0x00, 0x07, 0xe9, 0x5e, |
+ 0xad, 0x8d }; |
+ |
+const int kAc3FrameSizeTable[38][3] = { |
+ { 128, 138, 192 }, { 128, 140, 192 }, { 160, 174, 240 }, { 160, 176, 240 }, |
+ { 192, 208, 288 }, { 192, 210, 288 }, { 224, 242, 336 }, { 224, 244, 336 }, |
+ { 256, 278, 384 }, { 256, 280, 384 }, { 320, 348, 480 }, { 320, 350, 480 }, |
+ { 384, 416, 576 }, { 384, 418, 576 }, { 448, 486, 672 }, { 448, 488, 672 }, |
+ { 512, 556, 768 }, { 512, 558, 768 }, { 640, 696, 960 }, { 640, 698, 960 }, |
+ { 768, 834, 1152 }, { 768, 836, 1152 }, { 896, 974, 1344 }, |
+ { 896, 976, 1344 }, { 1024, 1114, 1536 }, { 1024, 1116, 1536 }, |
+ { 1280, 1392, 1920 }, { 1280, 1394, 1920 }, { 1536, 1670, 2304 }, |
+ { 1536, 1672, 2304 }, { 1792, 1950, 2688 }, { 1792, 1952, 2688 }, |
+ { 2048, 2228, 3072 }, { 2048, 2230, 3072 }, { 2304, 2506, 3456 }, |
+ { 2304, 2508, 3456 }, { 2560, 2768, 3840 }, { 2560, 2770, 3840 }, |
xhwang
2013/05/20 23:24:24
nit: drop the ',' at EOL?
jrummell
2013/05/22 18:27:39
Done.
|
+}; |
+ |
+// Checks for an ADTS AAC container. |
+static bool CheckAac(const uint8* buffer, int buffer_size) { |
+ // Audio Data Transport Stream (ADTS) header is 7 or 9 bytes |
+ // (from http://wiki.multimedia.cx/index.php?title=ADTS) |
+ if (buffer_size < 7) |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset + 6 < buffer_size) { |
xhwang
2013/05/20 23:24:24
It's a bit confusing that we use 6 here but the he
jrummell
2013/05/22 18:27:39
Done.
|
+ BitReader reader(buffer + offset, 6); |
+ |
+ // Syncword must be 0xfff. |
+ if (ReadBits(&reader, 12) != 0xfff) |
+ return false; |
+ |
+ // Skip MPEG version. |
+ reader.SkipBits(1); |
+ |
+ // Layer is always 0. |
+ if (ReadBits(&reader, 2) != 0) |
+ return false; |
+ |
+ // Skip protection + profile. |
+ reader.SkipBits(1 + 2); |
+ |
+ // Check sampling frequency index. |
+ if (ReadBits(&reader, 4) == 15) // Forbidden. |
+ return false; |
+ |
+ // Skip private stream, channel configuration, originality, home, |
+ // copyrighted stream, and copyright start. |
+ reader.SkipBits(1 + 3 + 1 + 1 + 1 + 1); |
+ |
+ // Get frame length (includes header). |
+ int size = ReadBits(&reader, 13); |
+ if (size == 0) |
+ return false; |
+ |
+ offset += size; |
+ } |
+ return true; |
+} |
+ |
+#define AC3_SYNCWORD 0x0b77 |
xhwang
2013/05/20 23:24:24
const int instead of macro?
jrummell
2013/05/22 18:27:39
Done.
|
+ |
+// Checks for an AC3 container. |
+static bool CheckAc3(const uint8* buffer, int buffer_size) { |
+ // Reference: ATSC Standard: Digital Audio Compression (AC-3, E-AC-3) |
+ // Doc. A/52:2012 |
+ // (http://www.atsc.org/cms/standards/A52-2012(12-17).pdf) |
+ |
+ // AC3 container looks like syncinfo | bsi | audblk * 6 | aux | check. |
+ if (buffer_size < 6) |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset + 6 < buffer_size) { |
+ BitReader reader(buffer + offset, 6); |
+ |
+ // Check syncinfo. |
+ if (ReadBits(&reader, 16) != AC3_SYNCWORD) |
+ return false; |
+ |
+ // Skip crc1. |
+ reader.SkipBits(16); |
+ |
+ // Verify fscod. |
+ int sample_rate_code = ReadBits(&reader, 2); |
+ if (sample_rate_code == 3) // Reserved. |
xhwang
2013/05/20 23:24:24
merge the above two lines?
jrummell
2013/05/22 18:27:39
sample_rate_code is used below to determine the fr
|
+ return false; |
+ |
+ // Verify frmsizecod. |
+ int frame_size_code = ReadBits(&reader, 6); |
+ if (frame_size_code >= 38) // Undefined. |
xhwang
2013/05/20 23:24:24
ditto?
jrummell
2013/05/22 18:27:39
frame_size_code is used below to determine the fra
|
+ return false; |
+ |
+ // Verify bsid. |
+ if (ReadBits(&reader, 5) >= 10) // Normally 8 or 6, 16 used by EAC3. |
+ return false; |
+ |
+ offset += kAc3FrameSizeTable[frame_size_code][sample_rate_code]; |
+ } |
+ return true; |
+} |
+ |
+// Checks for an EAC3 container (very similar to AC3) |
+static bool CheckEac3(const uint8* buffer, int buffer_size) { |
+ // Reference: ATSC Standard: Digital Audio Compression (AC-3, E-AC-3) |
+ // Doc. A/52:2012 |
+ // (http://www.atsc.org/cms/standards/A52-2012(12-17).pdf) |
+ |
+ // EAC3 container looks like syncinfo | bsi | audfrm | audblk* | aux | check. |
+ if (buffer_size < 6) |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset + 6 < buffer_size) { |
+ BitReader reader(buffer + offset, 6); |
+ |
+ // Check syncinfo. |
+ if (ReadBits(&reader, 16) != AC3_SYNCWORD) |
+ return false; |
+ |
+ // Verify strmtyp. |
+ if (ReadBits(&reader, 2) == 3) |
+ return false; |
+ |
+ // Skip substreamid. |
+ reader.SkipBits(3); |
+ |
+ // Get frmsize. Include syncinfo size and convert to bytes. |
+ int frame_size = (ReadBits(&reader, 11) + 1) * 2; |
+ if (frame_size < 7) |
+ return false; |
+ |
+ // Skip fscod, fscod2, acmod, and lfeon. |
+ reader.SkipBits(2 + 2 + 3 + 1); |
+ |
+ // Verify bsid. |
+ int bit_stream_id = ReadBits(&reader, 5); |
+ if (bit_stream_id < 11 || bit_stream_id > 16) |
+ return false; |
+ |
+ offset += frame_size; |
+ } |
+ return true; |
+} |
+ |
+// Additional checks for a BINK container. |
+static bool CheckBink(const uint8* buffer, int buffer_size) { |
+ // Reference: http://wiki.multimedia.cx/index.php?title=Bink_Container |
+ if (buffer_size < 44) |
+ return false; |
+ |
+ int frames = Read32LE(buffer + 8); |
+ int width = Read32LE(buffer + 20); |
+ int height = Read32LE(buffer + 24); |
+ int fps = Read32LE(buffer + 28); |
+ int den = Read32LE(buffer + 32); |
+ int audio_tracks = Read32LE(buffer + 40); |
+ return (frames > 0 && fps > 0 && den > 0 && audio_tracks <= 256 && |
+ (width > 0 && width <= 32767) && |
+ (height > 0 && height <= 32767)); |
+} |
+ |
+// Additional checks for a CAF container. |
+static bool CheckCaf(const uint8* buffer, int buffer_size) { |
+ // Reference: Apple Core Audio Format Specification 1.0 |
+ // (https://developer.apple.com/library/mac/#documentation/ |
+ // MusicAudio/Reference/CAFSpec/CAF_spec/CAF_spec.html) |
+ if (buffer_size < 52) |
+ return false; |
+ |
+ // mFileVersion should be 1. |
+ if (Read16(buffer + 4) != 1) |
+ return false; |
+ // First chunk should be Audio Description chunk, size 32. |
+ if (Read32(buffer + 8) != TAG('d','e','s','c')) |
+ return false; |
+ if (Read32(buffer + 12) != 0 || Read32(buffer + 16) != 32) |
+ return false; |
+ // CAFAudioFormat.mSampleRate not 0 |
+ if (Read32(buffer + 20) == 0 && Read32(buffer + 24) == 0) |
+ return false; |
+ // CAFAudioFormat.mFormatID not 0 |
+ if (Read32(buffer + 28) == 0) |
+ return false; |
+ // CAFAudioFormat.mChannelsPerFrame not 0 |
+ if (Read32(buffer + 44) == 0) |
+ return false; |
+ return true; |
+} |
+ |
+static bool kSamplingFrequencyValid[16] = { false, true, true, true, false, |
+ false, true, true, true, false, |
+ false, true, true, true, false, |
+ false }; |
+static bool kExtAudioIdValid[8] = { true, false, true, false, false, false, |
+ true, false }; |
+ |
+// Additional checks for a DTS container. |
+static bool CheckDts(const uint8* buffer, int buffer_size) { |
+ // Reference: ETSI TS 102 114 V1.3.1 (2011-08) |
+ // (http://www.etsi.org/deliver/etsi_ts/102100_102199/102114/ |
+ // 01.03.01_60/ts_102114v010301p.pdf) |
xhwang
2013/05/20 23:24:24
here and elsewhere, URL is not subject to 80-char
jrummell
2013/05/22 18:27:39
Guide says "that line may be longer than 80 charac
|
+ if (buffer_size < 12) |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset + 11 < buffer_size) { |
+ BitReader reader(buffer + offset, 11); |
xhwang
2013/05/20 23:24:24
Again, requiring 12 bytes but only parsing 11 byte
jrummell
2013/05/22 18:27:39
Done.
|
+ |
+ // Verify sync word. |
+ if (ReadBits(&reader, 32) != 0x7ffe8001) |
+ return false; |
+ |
+ // Skip frame type and deficit sample count. |
+ reader.SkipBits(1 + 5); |
+ |
+ // Verify CRC present flag. |
+ if (ReadBits(&reader, 1) != 0) // CPF must be 0. |
+ return false; |
+ |
+ // Verify number of PCM sample blocks. |
+ if (ReadBits(&reader, 7) < 5) |
+ return false; |
+ |
+ // Verify primary frame byte size. |
+ int frame_size = ReadBits(&reader, 14); |
+ if (frame_size < 95) |
+ return false; |
+ |
+ // Skip audio channel arrangement. |
+ reader.SkipBits(6); |
+ |
+ // Verify core audio sampling frequency is an allowed value. |
+ int sfreq = ReadBits(&reader, 4); |
+ if (!kSamplingFrequencyValid[sfreq]) |
+ return false; |
+ |
+ // Verify transmission bit rate is valid. |
+ if (ReadBits(&reader, 5) > 25) |
+ return false; |
+ |
+ // Verify reserved field is 0. |
+ if (ReadBits(&reader, 1) != 0) |
+ return false; |
+ |
+ // Skip dynamic range flag, time stamp flag, auxiliary data flag, and HDCD. |
+ reader.SkipBits(1 + 1 + 1 + 1); |
+ |
+ // Verify extension audio descriptor flag is an allowed value. |
+ int ext_audio_id = ReadBits(&reader, 3); |
+ if (!kExtAudioIdValid[ext_audio_id]) |
+ return false; |
+ |
+ // Skip extended coding flag and audio sync word insertion flag. |
+ reader.SkipBits(1 + 1); |
+ |
+ // Verify low frequency effects flag is an allowed value. |
+ if (ReadBits(&reader, 2) == 3) |
+ return false; |
+ |
+ offset += frame_size + 1; |
+ } |
+ return true; |
+} |
+ |
+// Checks for a DV container. |
+static bool CheckDV(const uint8* buffer, int buffer_size) { |
+ // Reference: SMPTE 314M (Annex A has differences with IEC 61834). |
+ // (http://standards.smpte.org/content/978-1-61482-454-1/ |
+ // st-314-2005/SEC1.body.pdf) |
+ if (buffer_size < 12) |
+ return false; |
+ |
+ int offset = 0; |
+ int current_sequence_number = -1; |
+ int last_block_number[6]; |
+ while (offset + 11 < buffer_size) { |
+ BitReader reader(buffer + offset, 11); |
+ |
+ // Decode ID data. Sections 5, 6, and 7 are reserved. |
+ int section = ReadBits(&reader, 3); |
+ if (section >= 5) |
+ return false; |
+ |
+ // Next bit must be 1. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ // Skip arbitrary bits. |
+ reader.SkipBits(4); |
+ |
+ int sequence_number = ReadBits(&reader, 4); |
+ |
+ // Skip FSC. |
+ reader.SkipBits(1); |
+ |
+ // Next 3 bits must be 1. |
+ if (ReadBits(&reader, 3) != 7) |
+ return false; |
+ |
+ int block_number = ReadBits(&reader, 8); |
+ |
+ if (section == 0) { // Header. |
+ // Validate the reserved bits in the next 8 bytes. |
+ reader.SkipBits(1); |
+ if (ReadBits(&reader, 1) != 0) |
+ return false; |
+ if (ReadBits(&reader, 11) != 0x7ff) |
+ return false; |
+ reader.SkipBits(4); |
+ if (ReadBits(&reader, 4) != 0xf) |
+ return false; |
+ reader.SkipBits(4); |
+ if (ReadBits(&reader, 4) != 0xf) |
+ return false; |
+ reader.SkipBits(4); |
+ if (ReadBits(&reader, 4) != 0xf) |
+ return false; |
+ reader.SkipBits(3); |
+ if (ReadBits(&reader, 24) != 0xffffff) |
+ return false; |
+ current_sequence_number = sequence_number; |
+ for (size_t i = 0; i < arraysize(last_block_number); ++i) |
+ last_block_number[i] = -1; |
+ } else { |
+ // Sequence number must match (this will also fail if no header seen). |
+ if (sequence_number != current_sequence_number) |
+ return false; |
+ // Block number should be increasing. |
+ if (block_number <= last_block_number[section]) |
+ return false; |
+ last_block_number[section] = block_number; |
+ } |
+ |
+ // Move to next block. |
+ offset += 80; |
+ } |
+ return true; |
+} |
+ |
+ |
+// Checks for a GSM container. |
+static bool CheckGsm(const uint8* buffer, int buffer_size) { |
+ // Reference: ETSI EN 300 961 V8.1.1 |
+ // (http://www.etsi.org/deliver/etsi_en/300900_300999/300961/ |
+ // 08.01.01_60/en_300961v080101p.pdf) |
+ // also http://tools.ietf.org/html/rfc3551#page-24 |
+ // GSM files have a 33 byte block, only first 4 bits are fixed. |
+ if (buffer_size < 1024) // Need enough data to do a decent check. |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset < buffer_size) { |
+ // First 4 bits of each block are xD. |
+ if ((buffer[offset] & 0xf0) != 0xd0) |
+ return false; |
+ offset = offset + 33; |
xhwang
2013/05/20 23:24:24
nit: offset += 33;
jrummell
2013/05/22 18:27:39
Done.
|
+ } |
+ return true; |
+} |
+ |
+// Check that value is a picture_start_code for H.261. |
+static inline bool IsH261PictureStartCode(uint32 value) { |
+ return (value & 0xfffff000) == 0x10000; |
+} |
+ |
+// Checks for a H.261 container. |
xhwang
2013/05/20 23:24:24
nit: s/a/an
jrummell
2013/05/22 18:27:39
Done.
|
+static bool CheckH261(const uint8* buffer, int buffer_size) { |
+ // Reference: ITU-T Recommendation H.261 (03/1993) |
+ // (http://www.itu.int/rec/T-REC-H.261-199303-I/en) |
+ if (buffer_size < 16) |
+ return false; |
+ |
+ // Advance to picture_start_code, if there is one. |
+ int offset = 0; |
+ uint32 next = 0; |
+ while (offset + 4 < buffer_size) { |
+ next = Read32(buffer + offset); |
+ if (IsH261PictureStartCode(next)) |
+ break; |
+ ++offset; |
+ } |
xhwang
2013/05/20 23:24:24
Can we move this block into the bit while loop on
jrummell
2013/05/22 18:27:39
Done.
|
+ // The picture_start_code must exist. |
+ if (!IsH261PictureStartCode(next)) |
+ return false; |
+ |
+ while (offset + 4 < buffer_size) { |
+ BitReader reader(buffer + offset, buffer_size - offset); |
+ |
+ // Skip the picture_start_code, temporal reference, and PTYPE. |
+ reader.SkipBits(20 + 5 + 6); |
+ |
+ // Skip any extra insertion information. Since this is open-ended, if we run |
+ // out of bits assume that the buffer is correctly formatted. |
+ int extra = ReadBits(&reader, 1); |
+ while (extra == 1) { |
+ if (!reader.SkipBits(8)) |
+ return true; |
+ if (!reader.ReadBits(1, &extra)) |
+ return true; |
+ } |
+ |
+ // Next should be a Group of Blocks start code. Again, if we run out of |
+ // bits, then assume that the buffer up to here is correct, and the buffer |
+ // just happened to end in the middle of a header. |
+ if (!reader.ReadBits(16, &next)) |
+ return true; |
+ if (next != 1) |
+ return false; |
+ |
+ // Move to the next picture_start_code. |
+ offset += 4; |
+ while (offset + 4 < buffer_size) { |
+ next = Read32(buffer + offset); |
+ if (IsH261PictureStartCode(next)) |
+ break; |
+ ++offset; |
+ } |
+ } |
+ return true; |
+} |
+ |
+// Check that value is a picture_start_code for H.263. |
+static inline bool IsH263PictureStartCode(uint32 value) { |
+ return (value & 0xfffffc00) == 0x8000; |
+} |
+ |
+// Checks for a H.263 container. |
+static bool CheckH263(const uint8* buffer, int buffer_size) { |
+ // Reference: ITU-T Recommendation H.263 (01/2005) |
+ // (http://www.itu.int/rec/T-REC-H.263-200501-I/en) |
+ // header is PSC(22b) + TR(8b) + PTYPE(8+b). |
+ if (buffer_size < 16) |
+ return false; |
+ |
+ // Advance to picture_start_code, if there is one. |
+ int offset = 0; |
+ uint32 next = 0; |
+ while (offset + 6 < buffer_size) { |
+ next = Read32(buffer + offset); |
+ if (IsH263PictureStartCode(next)) |
+ break; |
+ ++offset; |
+ } |
xhwang
2013/05/20 23:24:24
ditto about duplicate start code searching code.
jrummell
2013/05/22 18:27:39
Done.
|
+ // The picture_start_code must exist. |
+ if (!IsH263PictureStartCode(next)) |
+ return false; |
+ |
+ while (offset + 9 < buffer_size) { |
+ BitReader reader(buffer + offset, 9); |
+ |
+ // Skip the picture start code and temporal reference. |
+ reader.SkipBits(22 + 8); |
+ |
+ // Verify that the first 2 bits of PTYPE are 10b. |
+ if (ReadBits(&reader, 2) != 2) |
+ return false; |
+ |
+ // Skip the split screen indicator, document camera indicator, and full |
+ // picture freeze release. |
+ reader.SkipBits(1 + 1 + 1); |
+ |
+ // Verify Source Format. |
+ int format = ReadBits(&reader, 3); |
+ if (format == 0 || format == 6) // Forbidden or reserved. |
+ return false; |
+ |
+ if (format == 7) { |
+ // Verify full extended PTYPE. |
+ int ufep = ReadBits(&reader, 3); |
+ if (ufep == 1) { |
+ // Verify the optional part of PLUSPTYPE. |
+ format = ReadBits(&reader, 3); |
+ if (format == 0 || format == 7) // Reserved. |
+ return false; |
+ reader.SkipBits(11); |
+ // Next 4 bits should be b1000. |
+ if (ReadBits(&reader, 4) != 8) // Not allowed. |
+ return false; |
+ } else if (ufep != 0) // Only 0 and 1 allowed. |
+ return false; |
+ |
+ // Verify picture type code is not a reserved value. |
+ int picture_type_code = ReadBits(&reader, 3); |
+ if (picture_type_code == 6 || picture_type_code == 7) // Reserved. |
+ return false; |
+ |
+ // Skip picture resampling mode, reduced resolution mode, |
+ // and rounding type. |
+ reader.SkipBits(1 + 1 + 1); |
+ |
+ // Next 3 bits should be b001. |
+ if (ReadBits(&reader, 3) != 1) // Not allowed. |
+ return false; |
+ } |
+ |
+ // Move to the next picture_start_code. |
+ offset += 4; |
+ while (offset + 4 < buffer_size) { |
+ next = Read32(buffer + offset); |
+ if (IsH263PictureStartCode(next)) |
+ break; |
+ ++offset; |
+ } |
+ } |
+ return true; |
+} |
+ |
+// Checks for a H.264 container. |
+static bool CheckH264(const uint8* buffer, int buffer_size) { |
+ // Reference: ITU-T Recommendation H.264 (01/2012) |
+ // (http://www.itu.int/rec/T-REC-H.264) |
+ // Section B.1: Byte stream NAL unit syntax and semantics. |
+ if (buffer_size < 4) |
+ return false; |
+ |
+ int offset = 0; |
+ int parameter_count = 0; |
+ |
+ // Advance to start_code_prefix (x000001), if there is one. |
+ while (offset + 4 < buffer_size) { |
+ if (Read24(buffer + offset) == 1) |
+ break; |
+ ++offset; |
+ } |
xhwang
2013/05/20 23:24:24
ditto. Also can we make all startcode searching co
jrummell
2013/05/22 18:27:39
Done.
|
+ |
+ // The start_code_prefix must exist. |
+ if (Read24(buffer + offset) != 1) |
+ return false; |
+ |
+ while (offset + 4 < buffer_size) { |
+ BitReader reader(buffer + offset, 4); |
+ |
+ // Verify start_code_prefix. |
+ if (ReadBits(&reader, 24) != 1) |
+ return false; |
+ |
+ // Verify forbidden_zero_bit. |
+ if (ReadBits(&reader, 1) != 0) |
+ return false; |
+ |
+ // Extract nal_ref_idc and nal_unit_type. |
+ int nal_ref_idc = ReadBits(&reader, 2); |
+ int nal_unit_type = ReadBits(&reader, 5); |
+ |
+ switch (nal_unit_type) { |
+ case 5: // Coded slice of an IDR picture. |
+ if (nal_ref_idc == 0) |
+ return false; |
+ break; |
+ case 6: // Supplemental enhancement information (SEI). |
+ case 9: // Access unit delimiter. |
+ case 10: // End of sequence. |
+ case 11: // End of stream. |
+ case 12: // Filler data. |
+ if (nal_ref_idc != 0) |
+ return false; |
+ break; |
+ case 7: // Sequence parameter set. |
+ case 8: // Picture parameter set. |
+ ++parameter_count; |
+ break; |
+ } |
+ |
+ // Skip the current start_code_prefix and move to the next. |
+ offset += 3; |
+ while (offset + 4 < buffer_size && Read24(buffer + offset) != 1) |
+ ++offset; |
+ } |
+ |
+ return (parameter_count > 0); |
+} |
+ |
+static const char kHls1[] = "#EXT-X-STREAM-INF:"; |
+static const char kHls2[] = "#EXT-X-TARGETDURATION:"; |
+static const char kHls3[] = "#EXT-X-MEDIA-SEQUENCE:"; |
+ |
+// Additional checks for a HLS container. |
+static bool CheckHls(const uint8* buffer, int buffer_size) { |
+ // HLS is simply a play list used for Apple HTTP Live Streaming. |
+ // Reference: Apple HTTP Live Streaming Overview |
+ // (http://developer.apple.com/library/ios/#documentation/NetworkingInternet/ |
+ // Conceptual/StreamingMediaGuide/Introduction/Introduction.html) |
+ |
+ if (StartsWith(buffer, buffer_size, kHlsSignature)) { |
+ // Need to find "#EXT-X-STREAM-INF:", "#EXT-X-TARGETDURATION:", or |
+ // "#EXT-X-MEDIA-SEQUENCE:" somewhere in the buffer. Other playlists (like |
+ // WinAmp) only have additional lines with #EXTINF |
+ // (http://en.wikipedia.org/wiki/M3U). |
+ int offset = strlen(kHlsSignature); |
+ while (offset < buffer_size) { |
+ if (buffer[offset] == '#') { |
+ if (StartsWith(buffer + offset, buffer_size - offset, kHls1) || |
+ StartsWith(buffer + offset, buffer_size - offset, kHls2) || |
+ StartsWith(buffer + offset, buffer_size - offset, kHls3)) { |
+ return true; |
+ } |
+ } |
+ ++offset; |
+ } |
+ } |
+ return false; |
+} |
+ |
+// Checks for a MJPEG stream. |
+static bool CheckMJpeg(const uint8* buffer, int buffer_size) { |
+ // Reference: ISO/IEC 10918-1 : 1993(E), Annex B |
+ // (http://www.w3.org/Graphics/JPEG/itu-t81.pdf) |
+ if (buffer_size < 20) |
+ return false; |
+ |
+ int offset = 0; |
+ int last_restart = -1; |
+ while (offset + 5 < buffer_size) { |
+ // Marker codes are always a two byte code with the first byte xFF. |
+ if (buffer[offset] != 0xff) |
+ return false; |
+ uint8 code = buffer[offset + 1]; |
+ |
+ // Skip sequences of xFF. |
+ if (code == 0xff) { |
+ ++offset; |
+ continue; |
+ } |
+ |
+ // Fail if marker code is a reserved value. |
+ if (code < 0xc0 && code != 1) |
+ return false; |
+ |
+ // Success if the next marker code is EOI (end of image) |
+ if (code == 0xd9) |
+ return true; |
+ |
+ // Check remaining codes. |
+ if (code == 0xd8 || code == 1) { |
+ // SOI (start of image) / TEM (private use). No other data with header. |
+ offset += 2; |
+ } else if (code >= 0xd0 && code <= 0xd7) { |
+ // RST (restart) codes must be in sequence. No other data with header. |
+ int restart = code & 0x07; |
+ if (last_restart >= 0) { |
+ if (restart != (last_restart + 1) % 8) |
+ return false; |
+ } |
+ last_restart = restart; |
+ offset += 2; |
+ } else { |
+ // All remaining marker codes are followed by a length of the header. |
+ int length = Read16(buffer + offset + 2) + 2; |
+ |
+ // Special handling of SOS (start of scan) marker since the entrophy |
+ // coded data follows the SOS. Any xFF byte in the data block must be |
+ // followed by x00 in the data. |
+ if (code == 0xda) { |
+ int number_components = buffer[offset + 4]; |
+ if (length != 8 + 2 * number_components) |
+ return false; |
+ |
+ // Advance to the next marker. |
+ offset += length; |
+ while (offset + 2 < buffer_size) { |
+ if (buffer[offset] == 0xff && buffer[offset + 1] != 0) |
+ break; |
+ ++offset; |
+ } |
+ } else { |
+ // Skip over the marker data for the other marker codes. |
+ offset += length; |
+ } |
+ } |
+ } |
+ return true; |
+} |
+ |
+// Checks for a MPEG2 Program Stream. |
+static bool CheckMpeg2ProgramStream(const uint8* buffer, int buffer_size) { |
+ // Reference: ISO/IEC 13818-1 : 2000 (E) / ITU-T Rec. H.222.0 (2000 E). |
+ if (buffer_size < 14) |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset + 14 < buffer_size) { |
+ BitReader reader(buffer + offset, 14); |
+ |
+ // Must start with pack_start_code. |
+ if (ReadBits(&reader, 32) != 0x000001ba) |
+ return false; |
+ |
+ // Determine MPEG version (MPEG1 has b0010, while MPEG2 has b01). |
+ int mpeg_version = ReadBits(&reader, 2); |
+ if (mpeg_version == 0) { |
+ // MPEG1, 10 byte header |
+ // Validate rest of version code |
+ if (ReadBits(&reader, 2) != 2) |
+ return false; |
+ } else if (mpeg_version != 1) { |
+ // Not recognized. |
+ return false; |
+ } |
+ |
+ // Skip system_clock_reference_base [32..30]. |
+ reader.SkipBits(3); |
+ |
+ // Verify marker bit. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ // Skip system_clock_reference_base [29..15]. |
+ reader.SkipBits(15); |
+ |
+ // Verify next marker bit. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ // Skip system_clock_reference_base [14..0]. |
+ reader.SkipBits(15); |
+ |
+ // Verify next marker bit. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ if (mpeg_version == 0) { |
+ // Verify second marker bit. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ // Skip mux_rate. |
+ reader.SkipBits(22); |
+ |
+ // Verify next marker bit. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ // Update offset to be after this header. |
+ offset += 12; |
+ } else { |
+ // Must be MPEG2. |
+ // Skip program_mux_rate. |
+ reader.SkipBits(22); |
+ |
+ // Verify pair of marker bits. |
+ if (ReadBits(&reader, 2) != 3) |
+ return false; |
+ |
+ // Skip reserved. |
+ reader.SkipBits(5); |
+ |
+ // Update offset to be after this header. |
+ int pack_stuffing_length = ReadBits(&reader, 3); |
+ offset += 14 + pack_stuffing_length; |
+ } |
+ |
+ if (offset + 6 < buffer_size && Read32(buffer + offset) == 0x000001bb) { |
+ // If there is a System header, skip it. |
+ offset = offset + 6 + Read16(buffer + offset + 4); |
+ } |
+ // Check for PES_packets. |
+ while (offset + 6 < buffer_size && Read24(buffer + offset) == 1) { |
+ // Next 8 bits determine stream type. |
+ int stream_id = buffer[offset + 3]; |
+ |
+ // Some stream types are reserved and shouldn't occur. |
+ if (mpeg_version == 0) { |
+ if (stream_id == 0xbc || stream_id >= 0xf0) |
+ return false; |
+ } else { |
+ if (stream_id == 0xfc || stream_id == 0xfd || stream_id == 0xfe) |
+ return false; |
+ } |
+ |
+ // Some stream types are used for pack headers. |
+ if (stream_id == 0xba || stream_id == 0xbb || stream_id == 0xb9) |
+ break; |
+ |
+ int pes_length = Read16(buffer + offset + 4); |
+ if (pes_length == 0) |
+ return false; |
+ offset = offset + 6 + pes_length; |
+ } |
+ } |
+ // Success if we are off the end of the buffer or the stream ends |
+ // with the program_end_code. |
+ return (offset + 4 > buffer_size || Read32(buffer + offset) == 0x000001b9); |
+} |
+ |
+// Checks for a MPEG2 Transport Stream. |
+static bool CheckMpeg2TransportStream(const uint8* buffer, int buffer_size) { |
+ // Spec: ISO/IEC 13818-1 : 2000 (E) / ITU-T Rec. H.222.0 (2000 E). |
+ // Normal packet size is 188 bytes. However, some systems add various error |
+ // correction data at the end, resulting in packet of length 192/204/208 |
+ // (https://en.wikipedia.org/wiki/MPEG_transport_stream). Determine the |
+ // length with the first packet. |
+ if (buffer_size < 250) // Want more than 1 packet to check. |
+ return false; |
+ |
+ int offset = 0; |
+ int packet_length = -1; |
+ while (buffer[offset] != 0x47 && offset < 20) { |
+ // Skip over any header in the first 20 bytes. |
+ ++offset; |
+ } |
+ |
+ while (offset + 6 < buffer_size) { |
+ BitReader reader(buffer + offset, 6); |
+ |
+ // Must start with sync byte. |
+ if (ReadBits(&reader, 8) != 0x47) |
+ return false; |
+ |
+ // Skip transport_error_indicator, payload_unit_start_indicator, and |
+ // transport_priority.A |
+ reader.SkipBits(1 + 1 + 1); |
+ |
+ // Verify the pid is not a reserved value. |
+ int pid = ReadBits(&reader, 13); |
+ if (pid >= 3 && pid <= 15) |
+ return false; |
+ |
+ // Skip transport_scrambling_control. |
+ reader.SkipBits(2); |
+ |
+ // Adaptation_field_control can not be 0. |
+ int adaptation_field_control = ReadBits(&reader, 2); |
+ if (adaptation_field_control == 0) |
+ return false; |
+ |
+ // If there is an adaptation_field, verify it. |
+ if (adaptation_field_control >= 2) { |
+ // Skip continuity_counter. |
+ reader.SkipBits(4); |
+ |
+ // Get adaptation_field_length and verify it. |
+ int adaptation_field_length = ReadBits(&reader, 8); |
+ if (adaptation_field_control == 2) { |
+ if (adaptation_field_length != 183) |
+ return false; |
+ } else if (adaptation_field_length > 182) |
+ return false; |
+ } |
+ |
+ // Attempt to determine the packet length on the first packet. |
+ if (packet_length < 0) { |
+ if (buffer[offset + 188] == 0x47) |
+ packet_length = 188; |
+ else if (buffer[offset + 192] == 0x47) |
+ packet_length = 192; |
+ else if (buffer[offset + 204] == 0x47) |
+ packet_length = 204; |
+ else |
+ packet_length = 208; |
+ } |
+ offset += packet_length; |
+ } |
+ return true; |
+} |
+ |
+#define VISUAL_OBJECT_SEQUENCE_START_CODE 0xb0 |
+#define VISUAL_OBJECT_SEQUENCE_END_CODE 0xb1 |
+#define VISUAL_OBJECT_START_CODE 0xb5 |
+#define VOP_START_CODE 0xb6 |
xhwang
2013/05/20 23:24:24
static const int?
jrummell
2013/05/22 18:27:39
Converted to an enum.
|
+ |
+// Checks for a raw MPEG4 bitstream container. |
+static bool CheckMpeg4BitStream(const uint8* buffer, int buffer_size) { |
+ // Defined in ISO/IEC 14496-2:2001. |
+ // However, no length ... simply scan for start code values. |
+ // Expect to see SEQ | VO1 | VOL* | VO2 ... |
xhwang
2013/05/20 23:24:24
Are those "..." something to finish?
jrummell
2013/05/22 18:27:39
Removed.
|
+ // Note tags are very similar to H.264. |
+ |
+ if (buffer_size < 4) |
+ return false; |
+ |
+ int offset = 0; |
+ int sequence_start_count = 0; |
+ int sequence_end_count = 0; |
+ int visual_object_count = 0; |
+ int vop_count = 0; |
+ while (offset + 4 < buffer_size) { |
+ int start_code = Read24(buffer + offset); |
+ if (start_code == 1) { |
+ // Fail if it is a reserved value. |
+ offset += 3; |
+ if (buffer[offset] >= 0x30 && buffer[offset] <= 0xaf) |
+ return false; |
+ if (buffer[offset] >= 0xb7 && buffer[offset] <= 0xb9) |
+ return false; |
+ |
+ switch (buffer[offset]) { |
+ case VISUAL_OBJECT_SEQUENCE_START_CODE: |
+ ++sequence_start_count; |
+ break; |
+ case VISUAL_OBJECT_SEQUENCE_END_CODE: |
+ if (++sequence_end_count > sequence_start_count) |
+ return false; |
+ break; |
+ case VISUAL_OBJECT_START_CODE: |
+ ++visual_object_count; |
+ break; |
+ case VOP_START_CODE: |
+ if (++vop_count > visual_object_count) |
+ return false; |
+ break; |
+ } |
+ } |
+ // Start codes can start on any byte boundary. |
+ ++offset; |
+ } |
+ // Not a complete sequence in memory, so return true if we've seen a |
+ // visual_object_sequence_start_code and a visual_object_start_code. |
+ return (sequence_start_count > 0 && visual_object_count > 0); |
+} |
+ |
+// Additional checks for a MOV/QuickTime/MPEG4 container. |
+static bool CheckMov(const uint8* buffer, int buffer_size) { |
+ // Reference: ISO/IEC 14496-12:2005(E). |
+ // (http://standards.iso.org/ittf/PubliclyAvailableStandards/ |
+ // c061988_ISO_IEC_14496-12_2012.zip) |
+ if (buffer_size < 8) |
+ return false; |
+ |
+ int offset = 0; |
+ while (offset + 8 < buffer_size) { |
+ int atomsize = Read32(buffer + offset); |
+ uint32 atomtype = Read32(buffer + offset + 4); |
+ // Valid atoms from http://www.mp4ra.org/atoms.html. |
+ switch (atomtype) { |
+ case TAG('a','i','n','f'): |
+ case TAG('a','v','c','n'): |
+ case TAG('b','l','o','c'): |
+ case TAG('b','p','c','c'): |
+ case TAG('b','u','f','f'): |
+ case TAG('b','x','m','l'): |
+ case TAG('c','c','i','d'): |
+ case TAG('c','d','e','f'): |
+ case TAG('c','m','a','p'): |
+ case TAG('c','o','6','4'): |
+ case TAG('c','o','l','r'): |
+ case TAG('c','r','h','d'): |
+ case TAG('c','s','l','g'): |
+ case TAG('c','t','t','s'): |
+ case TAG('c','v','r','u'): |
+ case TAG('d','i','n','f'): |
+ case TAG('d','r','e','f'): |
+ case TAG('d','s','g','d'): |
+ case TAG('d','s','t','g'): |
+ case TAG('e','d','t','s'): |
+ case TAG('e','l','s','t'): |
+ case TAG('f','e','c','i'): |
+ case TAG('f','e','c','r'): |
+ case TAG('f','i','i','n'): |
+ case TAG('f','i','r','e'): |
+ case TAG('f','p','a','r'): |
+ case TAG('f','r','e','e'): |
+ case TAG('f','r','m','a'): |
+ case TAG('f','t','y','p'): |
+ case TAG('g','i','t','n'): |
+ case TAG('g','r','p','i'): |
+ case TAG('h','d','l','r'): |
+ case TAG('h','m','h','d'): |
+ case TAG('h','p','i','x'): |
+ case TAG('i','c','n','u'): |
+ case TAG('I','D','3','2'): |
+ case TAG('i','d','a','t'): |
+ case TAG('i','h','d','r'): |
+ case TAG('i','i','n','f'): |
+ case TAG('i','l','o','c'): |
+ case TAG('i','m','i','f'): |
+ case TAG('i','n','f','u'): |
+ case TAG('i','o','d','s'): |
+ case TAG('i','p','h','d'): |
+ case TAG('i','p','m','c'): |
+ case TAG('i','p','r','o'): |
+ case TAG('i','r','e','f'): |
+ case TAG('j','P',' ',' '): |
+ case TAG('j','p','2','c'): |
+ case TAG('j','p','2','h'): |
+ case TAG('j','p','2','i'): |
+ case TAG('l','r','c','u'): |
+ case TAG('m','7','h','d'): |
+ case TAG('m','d','a','t'): |
+ case TAG('m','d','h','d'): |
+ case TAG('m','d','i','a'): |
+ case TAG('m','d','r','i'): |
+ case TAG('m','e','c','o'): |
+ case TAG('m','e','h','d'): |
+ case TAG('m','e','r','e'): |
+ case TAG('m','e','t','a'): |
+ case TAG('m','f','h','d'): |
+ case TAG('m','f','r','a'): |
+ case TAG('m','f','r','o'): |
+ case TAG('m','i','n','f'): |
+ case TAG('m','j','h','d'): |
+ case TAG('m','o','o','f'): |
+ case TAG('m','o','o','v'): |
+ case TAG('m','v','c','g'): |
+ case TAG('m','v','c','i'): |
+ case TAG('m','v','e','x'): |
+ case TAG('m','v','h','d'): |
+ case TAG('m','v','r','a'): |
+ case TAG('n','m','h','d'): |
+ case TAG('o','c','h','d'): |
+ case TAG('o','d','a','f'): |
+ case TAG('o','d','d','a'): |
+ case TAG('o','d','h','d'): |
+ case TAG('o','d','h','e'): |
+ case TAG('o','d','r','b'): |
+ case TAG('o','d','r','m'): |
+ case TAG('o','d','t','t'): |
+ case TAG('o','h','d','r'): |
+ case TAG('p','a','d','b'): |
+ case TAG('p','a','e','n'): |
+ case TAG('p','c','l','r'): |
+ case TAG('p','d','i','n'): |
+ case TAG('p','i','t','m'): |
+ case TAG('r','e','s',' '): |
+ case TAG('r','e','s','c'): |
+ case TAG('r','e','s','d'): |
+ case TAG('s','b','g','p'): |
+ case TAG('s','c','h','i'): |
+ case TAG('s','c','h','m'): |
+ case TAG('s','d','e','p'): |
+ case TAG('s','d','h','d'): |
+ case TAG('s','d','t','p'): |
+ case TAG('s','d','v','p'): |
+ case TAG('s','e','g','r'): |
+ case TAG('s','e','n','c'): |
+ case TAG('s','g','p','d'): |
+ case TAG('s','i','d','x'): |
+ case TAG('s','i','n','f'): |
+ case TAG('s','k','i','p'): |
+ case TAG('s','m','h','d'): |
+ case TAG('s','r','m','b'): |
+ case TAG('s','r','m','c'): |
+ case TAG('s','r','p','p'): |
+ case TAG('s','t','b','l'): |
+ case TAG('s','t','c','o'): |
+ case TAG('s','t','d','p'): |
+ case TAG('s','t','h','d'): |
+ case TAG('s','t','s','c'): |
+ case TAG('s','t','s','d'): |
+ case TAG('s','t','s','h'): |
+ case TAG('s','t','s','s'): |
+ case TAG('s','t','s','z'): |
+ case TAG('s','t','t','s'): |
+ case TAG('s','t','y','p'): |
+ case TAG('s','t','z','2'): |
+ case TAG('s','u','b','s'): |
+ case TAG('s','w','t','c'): |
+ case TAG('t','f','a','d'): |
+ case TAG('t','f','h','d'): |
+ case TAG('t','f','m','a'): |
+ case TAG('t','f','r','a'): |
+ case TAG('t','i','b','r'): |
+ case TAG('t','i','r','i'): |
+ case TAG('t','k','h','d'): |
+ case TAG('t','r','a','f'): |
+ case TAG('t','r','a','k'): |
+ case TAG('t','r','e','f'): |
+ case TAG('t','r','e','x'): |
+ case TAG('t','r','g','r'): |
+ case TAG('t','r','i','k'): |
+ case TAG('t','r','u','n'): |
+ case TAG('u','d','t','a'): |
+ case TAG('u','i','n','f'): |
+ case TAG('U','I','T','S'): |
+ case TAG('u','l','s','t'): |
+ case TAG('u','r','l',' '): |
+ case TAG('u','u','i','d'): |
+ case TAG('v','m','h','d'): |
+ case TAG('v','w','d','i'): |
+ case TAG('x','m','l',' '): |
xhwang
2013/05/20 23:24:24
Up to this point all tags are ordered. But the tag
jrummell
2013/05/22 18:27:39
This is the order on www.mp4ra.org. However, list
|
+ case TAG('C','o','d','e'): |
+ case TAG('a','l','b','m'): |
+ case TAG('a','n','g','l'): |
+ case TAG('a','u','t','h'): |
+ case TAG('c','l','f','n'): |
+ case TAG('c','l','i','d'): |
+ case TAG('c','l','s','f'): |
+ case TAG('c','m','i','d'): |
+ case TAG('c','m','n','m'): |
+ case TAG('c','o','l','l'): |
+ case TAG('c','p','r','t'): |
+ case TAG('d','a','t','e'): |
+ case TAG('d','s','c','p'): |
+ case TAG('g','n','r','e'): |
+ case TAG('h','n','t','i'): |
+ case TAG('k','y','w','d'): |
+ case TAG('l','o','c','i'): |
+ case TAG('m','a','n','u'): |
+ case TAG('m','o','d','l'): |
+ case TAG('p','e','r','f'): |
+ case TAG('r','e','e','l'): |
+ case TAG('r','t','n','g'): |
+ case TAG('s','c','e','n'): |
+ case TAG('s','h','o','t'): |
+ case TAG('s','l','n','o'): |
+ case TAG('s','t','r','k'): |
+ case TAG('t','h','m','b'): |
+ case TAG('t','s','e','l'): |
+ case TAG('t','i','t','l'): |
+ case TAG('u','r','a','t'): |
+ case TAG('y','r','r','c'): |
+ case TAG('c','l','i','p'): |
+ case TAG('c','r','g','n'): |
+ case TAG('c','t','a','b'): |
+ case TAG('d','c','f','D'): |
+ case TAG('e','l','n','g'): |
+ case TAG('i','m','a','p'): |
+ case TAG('k','m','a','t'): |
+ case TAG('l','o','a','d'): |
+ case TAG('m','a','t','t'): |
+ case TAG('p','n','o','t'): |
+ case TAG('w','i','d','e'): |
+ break; |
+ default: |
+ return false; |
+ } |
+ if (atomsize <= 0) |
xhwang
2013/05/20 23:24:24
Since we read |atomsize| first, can be handle it f
jrummell
2013/05/22 18:27:39
Not really. If the size is extended, it follows th
|
+ break; // Indicates the last atom or length too big. |
+ if (atomsize == 1) { |
+ // Indicates that the length is the next 64bits. |
+ if (offset + 16 > buffer_size) |
+ break; |
+ if (Read32(buffer + offset + 8) != 0) |
+ break; // Offset is way past buffer size. |
+ atomsize = Read32(buffer + offset + 12); |
+ } |
+ offset += atomsize; |
+ } |
+ return true; |
+} |
+ |
+enum MPEGVersion { |
+ Version25 = 0, |
+ v_reserved, |
+ Version2, |
+ Version1 |
+}; |
+enum MPEGlayer { |
+ l_reserved = 0, |
+ Layer3, |
+ Layer2, |
+ Layer1 |
+}; |
xhwang
2013/05/20 23:24:24
style nit: We use UPPER_CASE_0 for enums in chromi
jrummell
2013/05/22 18:27:39
Done.
|
+ |
+static int kSampleRateTable[4][4] = { { 11025, 12000, 8000, 0 }, // v2.5 |
+ { 0, 0, 0, 0 }, // not used |
+ { 22050, 24000, 16000, 0 }, // v2 |
+ { 44100, 48000, 32000, 0 } // v1 |
+}; |
+ |
+static int kBitRateTableV1L1[16] = { 0, 32, 64, 96, 128, 160, 192, 224, 256, |
+ 288, 320, 352, 384, 416, 448, 0 }; |
+static int kBitRateTableV1L2[16] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, |
+ 192, 224, 256, 320, 384, 0 }; |
+static int kBitRateTableV1L3[16] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, |
+ 160, 192, 224, 256, 320, 0 }; |
+static int kBitRateTableV2L1[16] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, |
+ 160, 176, 192, 224, 256, 0 }; |
+static int kBitRateTableV2L23[16] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, |
+ 112, 128, 144, 160, 0 }; |
+ |
+static bool ValidMpegAudioFrameHeader(const uint8* header, int* framesize) { |
xhwang
2013/05/20 23:24:24
It's a bit scary to have a function reading memory
jrummell
2013/05/22 18:27:39
Done.
|
+ // Reference: http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm. |
+ *framesize = 0; |
+ BitReader reader(header, 4); // Header can only be 4 bytes long. |
+ |
+ // Verify frame sync (11 bits) are all set. |
+ if (ReadBits(&reader, 11) != 0x7ff) |
+ return false; |
+ |
+ // Verify MPEG audio version id. |
+ int version = ReadBits(&reader, 2); |
+ if (version == 1) // Reserved. |
+ return false; |
+ |
+ // Verify layer. |
+ int layer = ReadBits(&reader, 2); |
+ if (layer == 0) |
+ return false; |
+ |
+ // Skip protection bit. |
+ reader.SkipBits(1); |
+ |
+ // Verify bitrate index. |
+ int bitrate_index = ReadBits(&reader, 4); |
+ if (bitrate_index == 0xf) |
+ return false; |
+ |
+ // Verify sampling rate frequency index. |
+ int sampling_index = ReadBits(&reader, 2); |
+ if (sampling_index == 3) |
+ return false; |
+ |
+ // Get padding bit. |
+ int padding = ReadBits(&reader, 1); |
+ |
+ // Frame size: |
+ // For Layer I files = (12 * BitRate / SampleRate + Padding) * 4 |
+ // For others = 144 * BitRate / SampleRate + Padding |
+ // Unfortunately, BitRate and SampleRate are coded. |
+ int sampling_rate = kSampleRateTable[version][sampling_index]; |
+ int bitrate; |
+ if (version == Version1) { |
+ if (layer == Layer1) |
+ bitrate = kBitRateTableV1L1[bitrate_index]; |
+ else if (layer == Layer2) |
+ bitrate = kBitRateTableV1L2[bitrate_index]; |
+ else |
+ bitrate = kBitRateTableV1L3[bitrate_index]; |
+ } else { |
+ if (layer == Layer1) |
+ bitrate = kBitRateTableV2L1[bitrate_index]; |
+ else |
+ bitrate = kBitRateTableV2L23[bitrate_index]; |
+ } |
+ if (layer == Layer1) |
+ *framesize = ((12000 * bitrate) / sampling_rate + padding) * 4; |
+ else |
+ *framesize = (144000 * bitrate) / sampling_rate + padding; |
+ return (bitrate > 0 && sampling_rate > 0); |
+} |
+ |
+// Extract a size encoded the MP3 way. |
+static int GetMp3HeaderSize(const uint8* buffer) { |
+ int size = ((buffer[6] & 0x7f) << 21) + ((buffer[7] & 0x7f) << 14) + |
+ ((buffer[8] & 0x7f) << 7) + (buffer[9] & 0x7f) + 10; |
+ if (buffer[5] & 0x10) // Footer added? |
+ size += 10; |
+ return size; |
+} |
+ |
+// Additional checks for a MP3 container. |
+static bool CheckMp3(const uint8* buffer, int buffer_size, bool seenHeader) { |
+ if (buffer_size < 10) |
+ return false; |
+ int framesize; |
+ int numSeen = 0; |
+ int offset = 0; |
+ if (seenHeader) |
+ offset = GetMp3HeaderSize(buffer); |
+ while (offset + 3 < buffer_size && |
+ ValidMpegAudioFrameHeader(buffer + offset, &framesize)) { |
+ ++numSeen; |
+ offset += framesize; |
+ } |
+ return (numSeen > 10 || offset >= buffer_size); |
+} |
+ |
+// Read a Matroska TAG, updating offset to point past it. |
xhwang
2013/05/20 23:24:24
I am not sure what a TAG or VTAG type is. Use spec
jrummell
2013/05/22 18:27:39
Done.
|
+static int GetVtag(const uint8* buffer, int buffer_size, int* offset) { |
+ // Size of the tag is determined the same way as VINT, |
+ // but the bit is not removed. Maximum of 4 bytes. |
+ if (*offset >= buffer_size) |
+ return -1; |
+ int remaining = buffer_size - *offset; |
+ int result = buffer[*offset]; |
+ if ((result & 0x80) != 0) { |
+ // It is a one byte tag. |
+ ++*offset; |
+ } |
+ else if ((result & 0x40) != 0 && remaining >= 2) { |
+ // It is a 2 byte tag. |
+ result = Read16(buffer + *offset); |
+ *offset += 2; |
+ } |
+ else if ((result & 0x20) != 0 && remaining >= 3) { |
+ // It is a 3 byte tag. |
+ result = Read24(buffer + *offset); |
+ *offset += 3; |
+ } |
+ else if (remaining >= 4) { |
+ // It is a 4 byte tag. |
+ result = Read32(buffer + *offset); |
+ *offset += 4; |
+ } |
+ return result; |
+} |
+ |
+// Read a Matroska VINT, updating offset to point past it. |
+static int GetVint(const uint8* buffer, int buffer_size, int* offset) { |
+ // Length = 1 + [number_of_leading_zero_bits]. |
+ if (*offset >= buffer_size) { |
+ // Return something big so it goes off the end of the buffer. |
+ return buffer_size; |
+ } |
+ int remaining = buffer_size - *offset; |
+ int size = 1; |
+ int mask = 0x80; |
+ uint8 b = buffer[*offset]; |
+ while (mask > 0 && (b & mask) == 0) { |
+ ++size; |
+ mask = mask >> 1; |
+ } |
+ |
+ // Now that we know the size, use the remaining bits plus |
+ // following bytes to get the value. |
+ if (size > remaining) |
+ return buffer_size; |
+ int result = buffer[(*offset)++] & (mask - 1); |
+ while (--size > 0) |
+ result = (result << 8) | buffer[(*offset)++]; |
+ return result; |
+} |
+ |
+// Additional checks for a WEBM container. |
+static bool CheckWebm(const uint8* buffer, int buffer_size) { |
+ // Reference: http://www.matroska.org/technical/specs/index.html |
+ if (buffer_size < 12) |
+ return false; |
+ |
+ int offset = 4; |
+ int header_size = GetVint(buffer, buffer_size, &offset); |
+ int lastoffset = offset + header_size; |
+ if (lastoffset > buffer_size) |
+ return false; |
+ while (offset < lastoffset) { |
+ int tag = GetVtag(buffer, buffer_size, &offset); |
+ int tagsize = GetVint(buffer, buffer_size, &offset); |
+ switch (tag) { |
+ case 0x4286: // EBMLVersion |
+ case 0x42f7: // EBMLReadVersion |
+ case 0x42f2: // EBMLMaxIdLength |
+ case 0x42f3: // EBMLMaxSizeLength |
+ case 0x4287: // DocTypeVersion |
+ case 0x4285: // DocTypeReadVersion |
+ case 0xec: // void |
+ case 0xbf: // CRC32 |
+ offset += tagsize; |
+ break; |
+ case 0x4282: // EBMLDocType |
+ return ( |
+ (tagsize >= 4 && |
+ offset < buffer_size && |
+ StartsWith(buffer + offset, buffer_size - offset, "webm")) || |
+ (tagsize >= 8 && |
+ offset < buffer_size && |
+ StartsWith(buffer + offset, buffer_size - offset, "matroska"))); |
+ default: |
+ // Unrecognized tag |
+ return false; |
+ } |
+ } |
+ return false; |
+} |
+ |
+#define VC1_SEQUENCE_START_CODE 0x0f |
+#define VC1_ENTRY_POINT_START_CODE 0x0e |
+#define VC1_FRAME_START_CODE 0x0d |
xhwang
2013/05/20 23:24:24
static const int?
jrummell
2013/05/22 18:27:39
Converted to an enum.
|
+ |
+// Checks for a VC1 bitstream container. |
+static bool CheckVC1(const uint8* buffer, int buffer_size) { |
+ // Reference: SMPTE 421M |
+ // (http://standards.smpte.org/content/978-1-61482-555-5/ |
+ // st-421-2006/SEC1.body.pdf) |
+ // However, no length ... simply scan for start code values. |
+ // Expect to see SEQ | [ [ ENTRY ] PIC* ]* |
+ // Note tags are very similar to H.264. |
+ |
+ if (buffer_size < 24) |
+ return false; |
+ |
+ // First check for Bitstream Metadata Serialization (Annex L) |
+ if (buffer[0] == 0xc5 && |
+ Read32(buffer + 4) == 0x04 && |
+ Read32(buffer + 20) == 0x0c) { |
+ // Verify settings in STRUCT_C and STRUCT_A |
+ BitReader reader(buffer + 8, 12); |
+ |
+ int profile = ReadBits(&reader, 4); |
+ if (profile == 0 || profile == 4) { // simple or main |
+ // Skip FRMRTQ_POSTPROC, BITRTQ_POSTPROC, and LOOPFILTER. |
+ reader.SkipBits(3 + 5 + 1); |
+ |
+ // Next bit must be 0. |
+ if (ReadBits(&reader, 1) != 0) |
+ return false; |
+ |
+ // Skip MULTIRES. |
+ reader.SkipBits(1); |
+ |
+ // Next bit must be 1. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ // Skip FASTUVMC, EXTENDED_MV, DQUANT, and VSTRANSFORM. |
+ reader.SkipBits(1 + 1 + 2 + 1); |
+ |
+ // Next bit must be 0. |
+ if (ReadBits(&reader, 1) != 0) |
+ return false; |
+ |
+ // Skip OVERLAP, SYNCMARKER, RANGERED, MAXBFRAMES, QUANTIZER, and |
+ // FINTERPFLAG. |
+ reader.SkipBits(1 + 1 + 1 + 3 + 2 + 1); |
+ |
+ // Next bit must be 1. |
+ if (ReadBits(&reader, 1) != 1) |
+ return false; |
+ |
+ } else if (profile == 12) { // advanced |
+ if (ReadBits(&reader, 28) != 0) |
+ return false; |
+ } else // Other profile values not allowed. |
+ return false; |
+ |
+ // Now check HORIZ_SIZE and VERT_SIZE, which must be 8192 or less. |
+ if (ReadBits(&reader, 32) > 8192) |
+ return false; |
+ if (ReadBits(&reader, 32) > 8192) |
+ return false; |
+ } |
+ |
+ // Buffer isn't Bitstream Metadata, so scan for start codes. |
+ int offset = 0; |
+ int sequence_start_code = 0; |
+ int frame_start_code = 0; |
+ while (offset + 5 < buffer_size) { |
+ int start_code = Read24(buffer + offset); |
+ if (start_code == 1) { |
+ offset += 3; |
+ |
+ // Keep track of the number of certain types received. |
+ switch (buffer[offset]) { |
+ case VC1_SEQUENCE_START_CODE: { |
+ ++sequence_start_code; |
+ |
+ // Verify some of the information in the header. |
+ BitReader reader(buffer + offset, 2); |
+ int profile = ReadBits(&reader, 2); |
+ switch (profile) { |
+ case 0: // simple |
+ case 1: // main |
+ if (ReadBits(&reader, 2) != 0) |
+ return false; |
+ break; |
+ case 2: // complex |
+ return false; |
+ case 3: // advanced |
+ // Verify level = 0..4 |
+ if (ReadBits(&reader, 3) > 4) |
+ return false; |
+ |
+ // Verify colordiff_format = 1 |
+ if (ReadBits(&reader, 2) != 1) |
+ return false; |
+ break; |
+ } |
+ } break; |
xhwang
2013/05/20 23:24:24
nit: Put break on a separate line.
nit: Here and b
jrummell
2013/05/22 18:27:39
Done.
|
+ case VC1_ENTRY_POINT_START_CODE: |
+ // No fields in entry data to check. However, it must occur after |
+ // sequence header. |
+ if (sequence_start_code == 0) |
+ return false; |
+ break; |
+ case VC1_FRAME_START_CODE: |
+ ++frame_start_code; |
+ break; |
+ } |
+ } |
+ // Start codes can start on any byte boundary. |
+ ++offset; |
+ } |
+ // Not a complete sequence in memory, so return true if we've seen a |
+ // sequence start (since entry points only occur in advanced profiles). |
+ return (sequence_start_code > 0 && frame_start_code > 0); |
+} |
+ |
+// Attempt to determine the container type from the buffer provided. This is |
+// a simple pass, that uses the first 4 bytes of the buffer as an index to get |
+// a rough idea of the container format. |
+static FFmpegContainerName LookupContainerByFirst4(const uint8* buffer, |
+ int buffer_size) { |
xhwang
2013/05/20 23:24:24
s/First4/First4Bytes
It looks like we need at lea
jrummell
2013/05/22 18:27:39
Uses the first 4 bytes as an index. Since second4
|
+ // Minimum size that the code expects to exist without checking size. |
+ if (buffer_size < 12) |
+ return CONTAINER_UNKNOWN; |
+ |
+ uint32 first4 = Read32(buffer); |
+ uint32 second4 = Read32(buffer + 4); |
+ uint32 third4 = Read32(buffer + 8); |
+ |
+ switch (first4) { |
+ case 0x1a45dfa3: |
+ if (CheckWebm(buffer, buffer_size)) |
+ return CONTAINER_WEBM; |
+ break; |
+ case 0x3026b275: |
+ if (StartsWith(buffer, |
+ buffer_size, |
+ kAsfSignature, |
+ sizeof(kAsfSignature))) { |
+ return CONTAINER_ASF; |
+ } |
+ break; |
+ case TAG('#','!','A','M'): |
+ if (StartsWith(buffer, buffer_size, kAmrSignature)) |
+ return CONTAINER_AMR; |
+ break; |
+ case TAG('#','E','X','T'): |
+ if (CheckHls(buffer, buffer_size)) |
+ return CONTAINER_HLS; |
+ break; |
+ case TAG('.','R','M','F'): |
+ if (buffer[4] == 0 && buffer[5] == 0) |
+ return CONTAINER_RM; |
+ break; |
+ case TAG('.','r','a','\xfd'): |
+ return CONTAINER_RM; |
+ case TAG('B','I','K','b'): |
+ case TAG('B','I','K','d'): |
+ case TAG('B','I','K','f'): |
+ case TAG('B','I','K','g'): |
+ case TAG('B','I','K','h'): |
+ case TAG('B','I','K','i'): |
+ if (CheckBink(buffer, buffer_size)) |
+ return CONTAINER_BINK; |
+ break; |
+ case TAG('c','a','f','f'): |
+ if (CheckCaf(buffer, buffer_size)) |
+ return CONTAINER_CAF; |
+ break; |
+ case TAG('D','E','X','A'): |
+ if (buffer_size > 15 && |
+ Read16(buffer + 11) <= 2048 && |
+ Read16(buffer + 13) <= 2048) { |
+ return CONTAINER_DXA; |
+ } |
+ break; |
+ case TAG('D','T','S','H'): |
+ if (second4 == TAG('D','H','D','R')) |
+ return CONTAINER_DTSHD; |
+ break; |
+ case TAG('\x64','\xa3','\x01','\x00'): |
xhwang
2013/05/20 23:24:24
s/'\x64'/0x64?
jrummell
2013/05/22 18:27:39
Simplified even farther.
|
+ case TAG('\x64','\xa3','\x02','\x00'): |
+ case TAG('\x64','\xa3','\x03','\x00'): |
+ case TAG('\x64','\xa3','\x04','\x00'): |
+ case TAG('\x00','\x01','\xa3','\x64'): |
+ case TAG('\x00','\x02','\xa3','\x64'): |
+ case TAG('\x00','\x03','\xa3','\x64'): |
+ if (second4 != 0 && third4 != 0) |
+ return CONTAINER_IRCAM; |
+ break; |
+ case TAG('f','L','a','C'): |
+ return CONTAINER_FLAC; |
+ case TAG('F','L','V','\x00'): |
+ case TAG('F','L','V','\x01'): |
+ case TAG('F','L','V','\x02'): |
+ case TAG('F','L','V','\x03'): |
+ case TAG('F','L','V','\x04'): |
+ if (buffer[5] == 0 && Read32(buffer + 5) > 8) |
+ return CONTAINER_FLV; |
+ break; |
+ case TAG('F','O','R','M'): |
+ switch (third4) { |
+ case TAG('A','I','F','F'): |
+ case TAG('A','I','F','C'): |
+ return CONTAINER_AIFF; |
+ } |
+ break; |
+ case TAG('M','A','C',' '): |
+ return CONTAINER_APE; |
+ case TAG('O','N','2',' '): |
+ if (third4 == TAG('O','N','2','f')) |
+ return CONTAINER_AVI; |
+ case TAG('O','g','g','S'): |
+ if (buffer[5] <= 7) |
+ return CONTAINER_OGG; |
+ break; |
+ case TAG('R','F','6','4'): |
+ if (buffer_size > 16 && Read32(buffer + 12) == TAG('d','s','6','4')) |
+ return CONTAINER_WAV; |
+ break; |
+ case TAG('R','I','F','F'): |
+ switch (third4) { |
+ case TAG('A','V','I',' '): |
+ case TAG('A','V','I','X'): |
+ case TAG('A','V','I','\x19'): |
+ case TAG('A','M','V',' '): |
+ return CONTAINER_AVI; |
+ case TAG('W','A','V','E'): |
+ return CONTAINER_WAV; |
+ } |
+ break; |
+ case TAG('[','S','c','r'): |
+ if (StartsWith(buffer, buffer_size, kAssSignature)) |
+ return CONTAINER_ASS; |
+ break; |
+ case TAG('\xef','\xbb','\xbf','['): |
+ if (StartsWith(buffer, buffer_size, kAssBomSignature)) |
+ return CONTAINER_ASS; |
+ break; |
+ case 0x7ffe8001: |
+ case 0xfe7f0180: |
+ case 0x1fffe800: |
+ case 0xff1f00e8: |
+ if (CheckDts(buffer, buffer_size)) |
+ return CONTAINER_DTS; |
+ case 0xb7d80020: |
+ if (StartsWith(buffer, |
+ buffer_size, |
+ kWtvSignature, |
+ sizeof(kWtvSignature))) { |
+ return CONTAINER_WTV; |
+ } |
+ break; |
+ } |
+ |
+ // Now try a few different ones that look at something other |
+ // than the first 4 bytes. |
+ uint32 first3 = first4 & 0xffffff00; |
+ switch (first3) { |
+ case TAG('C','W','S',0): |
+ case TAG('F','W','S',0): |
+ return CONTAINER_SWF; |
+ case TAG('I','D','3',0): |
+ if (CheckMp3(buffer, buffer_size, true)) |
+ return CONTAINER_MP3; |
+ break; |
+ } |
+ |
+ // Maybe the first 2 characters are something we can use. |
+ uint32 first2 = Read16(buffer); |
+ switch (first2) { |
+ case AC3_SYNCWORD: |
+ if (CheckAc3(buffer, buffer_size)) |
+ return CONTAINER_AC3; |
+ if (CheckEac3(buffer, buffer_size)) |
+ return CONTAINER_EAC3; |
+ break; |
+ case 0xfff0: |
+ case 0xfff1: |
+ case 0xfff8: |
+ case 0xfff9: |
+ if (CheckAac(buffer, buffer_size)) |
+ return CONTAINER_AAC; |
+ break; |
+ } |
+ |
+ // Check if the file is in MP3 format without the header. |
+ if (CheckMp3(buffer, buffer_size, false)) |
+ return CONTAINER_MP3; |
+ |
+ // Skip over starting 0's, and see if it is MP3. |
+ if (buffer[0] == 0) { |
+ int offset = 1; |
+ int remaining = buffer_size - 1; |
+ while (remaining > 0 && buffer[offset] == 0) { |
+ ++offset; |
+ --remaining; |
+ } |
+ if (remaining > 4) { |
+ if (CheckMp3(buffer + offset, remaining, false)) { |
+ return CONTAINER_MP3; |
+ } |
+ } |
+ } |
+ |
+ return CONTAINER_UNKNOWN; |
+} |
+ |
+// Helper function to do limited scanf functionality without going off |
+// the end of the buffer. |
+static bool StringFormatCompare(const uint8* buffer, |
+ int buffer_size, |
+ const char* format) { |
+ // This function only supports the following items in the format string: |
+ // %<maxlength>d -- integer (can start with +/-, maxlength is optional) |
+ // %c -- any single character |
+ // %[<characters>] -- character must be one of the set |
+ // -- everything else is a literal |
+ // This code assumes that format is correct. |
+ int offset = 0; |
+ while (offset < buffer_size) { |
+ // Determine next format item. |
+ if (*format == '\0') { |
+ // End of format string, success. |
+ return true; |
+ } else if (*format != '%') { |
+ // Not a specifier, so it must match exactly. |
+ if (buffer[offset] != *format) |
+ return false; |
+ ++offset; |
+ } else { |
+ ++format; |
+ int maxLength = 0; |
+ int numSeen = 0; |
+ while (isdigit(*format)) { |
+ maxLength = maxLength * 10 + (*format - '0'); |
+ ++format; |
+ } |
+ if (maxLength < 1) // If not specified set it to 100 to simplify below. |
+ maxLength = 100; |
+ switch (*format) { |
+ case 'c': |
+ ++offset; // Don't care what the character is. |
+ break; |
+ case 'd': |
+ while (offset < buffer_size && isspace(buffer[offset])) |
+ ++offset; |
+ if (offset >= buffer_size) |
+ return false; |
+ if (buffer[offset] == '+' || buffer[offset] == '-') |
+ ++offset; |
+ // Need to process up to maxLength digits. |
+ while (offset < buffer_size && |
+ --maxLength >= 0 && |
+ isdigit(buffer[offset])) { |
+ ++numSeen; |
+ ++offset; |
+ } |
+ if (numSeen == 0) // No digits, so it is not a match for %d. |
+ return false; |
+ break; |
+ case '[': |
+ ++format; // Skip [. |
+ while (*format != ']') { |
+ if (buffer[offset] == *format) { |
+ ++numSeen; |
+ } |
+ ++format; |
+ } |
+ if (numSeen == 0) // No character match. |
+ return false; |
+ ++offset; // Skip the character matched. |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+ } |
+ // Move to the next format specification. |
+ ++format; |
+ } |
+ // Out of buffer, so it doesn't match. |
+ return false; |
+} |
+ |
+// Attempt to determine the container type by scanning for a set of strings, |
+// line by line (delimityed by \n or \r). |
+static FFmpegContainerName LookupContainerByStringLine(const uint8* buffer, |
xhwang
2013/05/20 23:24:24
Can we rename this to something like CheckSrt to b
jrummell
2013/05/22 18:27:39
Done.
|
+ int buffer_size) { |
+ int offset = StartsWith(buffer, buffer_size, BYTE_ORDER_MARK) ? 3 : 0; |
+ int lines = 0; |
+ |
+ while (offset < buffer_size && lines < 3) { |
+ const uint8* ptr = buffer + offset; |
+ int remaining = buffer_size - offset;; |
+ |
+ if (isdigit(*ptr)) { |
+ if (StringFormatCompare( |
+ ptr, remaining, "%d:%2d:%2d%[,.]%3d --> %d:%2d:%2d%[,.]%3d")) { |
xhwang
2013/05/20 23:24:24
The function StringFormatCompare() looks cool! But
jrummell
2013/05/22 18:27:39
Done.
|
+ return CONTAINER_SRT; |
+ } |
+ } |
+ |
+ // Find the end of the line. |
+ while (offset < buffer_size && |
+ buffer[offset] != '\n' && buffer[offset] != '\r') { |
+ ++offset; |
+ } |
+ |
+ // Skip the \n\r. |
+ while (offset < buffer_size && |
+ (buffer[offset] == '\n' || buffer[offset] == '\r')) { |
+ ++offset; |
+ } |
+ ++lines; |
+ } |
+ |
+ // Didn't find a string match for any of the formats. |
+ return CONTAINER_UNKNOWN; |
+} |
+ |
+// Attempt to determine the container name from the buffer provided. |
+FFmpegContainerName DetermineContainer(const uint8* buffer, int buffer_size) { |
+ DCHECK(buffer); |
+ |
+ // Since MOV/QuickTime/MPEG4 streams are common, check for them first. |
+ if (CheckMov(buffer, buffer_size)) |
+ return CONTAINER_MOV; |
+ |
+ // Next attempt the simple checks, that typically look at just the |
+ // first few bytes of the file. |
+ FFmpegContainerName result = LookupContainerByFirst4(buffer, buffer_size); |
+ if (result != CONTAINER_UNKNOWN) |
+ return result; |
+ |
+ // Additional checks that may scan a portion of the buffer. |
+ if (CheckMpeg2ProgramStream(buffer, buffer_size)) |
+ return CONTAINER_MPEG2PS; |
+ if (CheckMpeg2TransportStream(buffer, buffer_size)) |
+ return CONTAINER_MPEG2TS; |
+ if (CheckDV(buffer, buffer_size)) |
+ return CONTAINER_DV; |
+ if (CheckH261(buffer, buffer_size)) |
+ return CONTAINER_H261; |
+ if (CheckH263(buffer, buffer_size)) |
+ return CONTAINER_H263; |
+ if (CheckH264(buffer, buffer_size)) |
+ return CONTAINER_H264; |
+ if (CheckMpeg4BitStream(buffer, buffer_size)) |
+ return CONTAINER_MPEG4BS; |
+ if (CheckMJpeg(buffer, buffer_size)) |
+ return CONTAINER_MJPEG; |
+ if (CheckVC1(buffer, buffer_size)) |
+ return CONTAINER_VC1; |
+ |
+ // AC3/EAC3 might not start at the beginning of the stream, |
+ // so scan for a start code. |
+ int offset = 1; // No need to start at byte 0 due to First4 check. |
+ while (offset + 2 < buffer_size && Read16(buffer + offset) != AC3_SYNCWORD) |
+ ++offset; |
+ if (Read16(buffer + offset) == AC3_SYNCWORD) { |
+ if (CheckAc3(buffer + offset, buffer_size - offset)) |
+ return CONTAINER_AC3; |
+ if (CheckEac3(buffer + offset, buffer_size - offset)) |
+ return CONTAINER_EAC3; |
+ } |
+ |
+ // Scan for strings in the buffer. |
+ result = LookupContainerByStringLine(buffer, buffer_size); |
+ if (result != CONTAINER_UNKNOWN) |
+ return result; |
+ |
+ // GSM files have very limited fixed data, so check for them last |
+ if (CheckGsm(buffer, buffer_size)) |
+ return CONTAINER_GSM; |
+ |
+ return CONTAINER_UNKNOWN; |
+} |
+ |
+} // namespace container_names |
+ |
+} // namespace media |