Chromium Code Reviews| 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 |