Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(813)

Unified Diff: media/base/container_names.cc

Issue 14495010: Add UMA stats for audio/video containers. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..ef8477facc2a9a75208a0ba9b77919879759fbb6
--- /dev/null
+++ b/media/base/container_names.cc
@@ -0,0 +1,2295 @@
+// 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>
xhwang 2013/05/06 23:51:27 nit: extra line after this
jrummell 2013/05/16 23:48:01 Done.
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+
+namespace container_names {
+
+#define TAG(a, b, c, d) \
+ ((static_cast<uint8_t>(a) << 24) | (static_cast<uint8_t>(b) << 16) | \
xhwang 2013/05/06 23:51:27 sorry for not mentioning this earlier. we use uint
scherkus (not reviewing) 2013/05/07 00:50:20 nit: this should be a 4-space indent
jrummell 2013/05/16 23:48:01 Done.
jrummell 2013/05/16 23:48:01 Done. I just let clang-format do it's thing.
+ (static_cast<uint8_t>(c) << 8) | (static_cast<uint8_t>(d)))
+
+// Helper function to read 2 bytes (16 bits, big endian) from a buffer.
+static uint32_t Read16(const uint8_t* p) {
+ return p[0] << 8 | p[1];
+}
+
+// Helper function to read 3 bytes (24 bits, big endian) from a buffer.
+static uint32_t Read24(const uint8_t* 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_t Read32(const uint8_t* p) {
+ return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+}
+
+// Helper function to do buffer comparisons without going off the end
+// of the buffer.
+static bool StartsWith(const uint8_t* buffer,
+ size_t buffer_size,
+ const char* search,
+ size_t search_size) {
+ return (search_size <= buffer_size &&
+ memcmp(buffer, search, search_size) == 0);
xhwang 2013/05/06 23:51:27 It looks like we always call StartWith with an C s
jrummell 2013/05/16 23:48:01 Done. However, some of the "strings" contain embed
+}
+
+// Comparison function used by lower_bound which returns true if the
+// first argument is less than the second in lexicographical order.
+static bool ContainerNameMappingComparer(const ContainerNameMapping& a,
+ const char* b) {
+ return strcasecmp(a.name, b) < 0;
+}
+
+// Output a container value to the histogram.
+static void LogContainerToHistogram(FFmpegContainerName container,
+ bool is_guess) {
+ int metric = 2 * container;
+ if (is_guess)
scherkus (not reviewing) 2013/05/07 00:50:20 I don't think we're gaining much by logging both w
jrummell 2013/05/16 23:48:01 Done.
+ ++metric;
+
+ UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedContainer", metric);
+}
+
+// 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.
+#define BYTE_ORDER_MARK "\xef\xbb\xbf"
+
+static const char kAmrSignature[] = "#!AMR";
+static const char kApcSignature[] = "CRYO_APC";
+static const char kAsfSignature[] =
+ "\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c";
+static const char kAssSignature[] = "[Script Info]";
+static const char kAssBomSignature[] = BYTE_ORDER_MARK "[Script Info]";
+static const char kConcatSignature[] = "ffconcat version 1.0";
+static const char kDnxhdSignature[] = "\x00\x00\x02\x80\x01";
+static const char kFfSignature[] = ";FFMETADATA";
+static const char kHlsSignature[] = "#EXTM3U";
+static const char kIdfSignature[] =
+ "\x04\x31\x2e\x34\x00\x00\x00\x00\x4f\x00\x15\x00";
+static const char kIlbcSignature[] = "#!iLBC";
+static const char kIssSignature[] = "IMA_ADPCM_Sound";
+static const char kIv8Signature[] = "\x01\x01\x03\xb8\x80\x60";
+static const char kJvSignature[] = " Compression by John M Phillips Copyright "
+ "(C) 1995 The Bitmap Brothers Ltd.";
+static const char kLibnutSignature[] = "nut/multimedia container";
+static const char kLxfSignature[] = "LEITCH\x00\x00";
+static const char kNuv1Signature[] = "NuppelVideo";
+static const char kNuv2Signature[] = "MythTVVideo";
+static const char kPafSignature[] =
+ "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a";
+static const char kRealSignature[] = "<window";
+static const char kRealBomSignature[] = BYTE_ORDER_MARK "<window";
+static const char kRplSignature[] = "ARMovie\x0A";
+static const char kSamiSignature[] = "<SAMI>";
+static const char kSamiBomSignature[] = BYTE_ORDER_MARK "<SAMI>";
+static const char kSmjpegSignature[] = "\x00\x0aSMJPEG";
+static const char kVivoSignature[] = "\r\nVersion:Vivo/";
+static const char kVobsubSignature[] = "# VobSub index file,";
+static const char kVocSignature[] = "Creative Voice File\x1A";
+static const char kW64Signature[] =
+ "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00";
+static const char kW64Signature2[] =
+ "wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a";
+static const char kWebvttSignature[] = "WEBVTT";
+static const char kWebvttBomSignature[] = BYTE_ORDER_MARK "WEBVTT";
+static const char kWtvSignature[] =
+ "\xb7\xd8\x00\x20\x37\x49\xda\x11\xa6\x4e\x00\x07\xe9\x5e\xad\x8d";
+static const char kYuv4Signature[] = "YUV4MPEG2";
+
+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 },
+};
+
+// Checks for an ADTS AAC container.
+static bool CheckAac(const uint8_t* buffer, int buffer_size) {
+ // ADTS header is 7 or 9 bytes
+ // (from http://wiki.multimedia.cx/index.php?title=ADTS)
+ int offset = 0;
+ while (offset + 5 < buffer_size) {
+ int syncword = (Read16(buffer + offset) >> 4) & 0xfff;
+ int layer = (buffer[offset + 1] >> 1) & 0x3;
+ int frequency_index = (buffer[offset + 2] >> 2) & 0xf;
+ int size = (Read24(buffer + offset + 3) >> 5) & 0x1fff;
xhwang 2013/05/06 23:51:27 shall we make sure size>0 here? Otherwise we'll ha
jrummell 2013/05/16 23:48:01 Done.
+ if (syncword != 0xfff || layer != 0 || frequency_index == 15)
+ return false;
+ offset += size;
+ }
+ return (offset > 0);
xhwang 2013/05/06 23:51:27 In a lot of functions we are checking the "buffer_
jrummell 2013/05/16 23:48:01 Done.
+}
+
+// Checks for an AC3 container.
+static bool CheckAc3(const uint8_t* buffer, int buffer_size) {
+ // AC3 container looks like syncinfo | bsi | audblk * 6 | aux | check.
+ // from spec @ http://www.atsc.org/cms/standards/A52-2012(12-17).pdf
+ int offset = 0;
+
+ while (offset + 6 < buffer_size) {
+ // Verify syncinfo (5 bytes)
+ if (Read16(buffer + offset) != 0x0b77)
+ return false;
+ int sample_rate_code = (buffer[offset + 4] >> 6) & 0x03;
+ if (sample_rate_code == 3) // reserved
+ return false;
+ int frame_size_code = buffer[offset + 4] & 0x3f;
+ if (frame_size_code >= 38)
+ return false;
+
+ // Verify bsi (no fixed alignment)
+ int bit_stream_id = (buffer[offset + 5] >> 3) & 0x1f;
+ if (bit_stream_id >= 10) // normally 8 or 6
+ return false;
+
+ offset += kAc3FrameSizeTable[frame_size_code][sample_rate_code];
+ }
+ return (offset > 0);
+}
+
+// Checks for an EAC3 container (very similar to AC3)
+static bool CheckEac3(const uint8_t* buffer, int buffer_size) {
+ // EAC3 container looks like syncinfo | bsi | audfrm | audblk* | aux | check.
+ // from spec @ http://www.atsc.org/cms/standards/A52-2012(12-17).pdf
+ int offset = 0;
+
+ while (offset + 6 < buffer_size) {
+ // Verify syncinfo (5 bytes)
+ if (Read16(buffer + offset) != 0x0b77)
+ return false;
+
+ // Verify bsi (no fixed alignment)
+ int stream_type = (buffer[offset + 2] > 6) & 0x3;
+ if (stream_type == 3)
+ return false;
+ int frame_size = ((Read16(buffer + offset + 2) & 0x7ff) + 1) * 2;
+ if (frame_size < 7)
+ return false;
+ int bit_stream_id = (buffer[offset + 5] >> 3) & 0x1f;
+ if (bit_stream_id != 16)
+ return false;
+
+ offset += frame_size;
+ }
+ return (offset > 0);
+}
+
+// Additional checks for an ACT container.
+static bool CheckAct(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 512 || Read32(buffer + 16) != 16)
+ return false;
+ // Most of the first 512 bytes should be 0.
+ for (int i = 44; i < 256; ++i)
+ if (buffer[i] != 0)
+ return false;
+ if (buffer[256] != 0x84)
+ return false;
+ for (int i = 264; i < 512; ++i)
+ if (buffer[i] != 0)
+ return false;
+ return true;
+}
+
+// Additional checks for an AEA container.
+static bool CheckAea(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 2260)
+ return false;
+ int channels = buffer[264];
+ return ((channels == 1 || channels == 2) &&
+ buffer[2048] == buffer[2259] &&
+ buffer[2049] == buffer[2258]);
+}
+
+// Additional checks for a BINK container.
+static bool CheckBink(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 36)
+ return false;
+ int frames = Read32(buffer + 8);
+ int width = Read32(buffer + 20);
+ int height = Read32(buffer + 24);
+ int fps = Read32(buffer + 28);
+ int den = Read32(buffer + 32);
+ return (frames > 0 && fps > 0 && den > 0 &&
+ (width > 0 && width <= 7680) &&
+ (height > 0 && height <= 4800));
+}
+
+// Additional checks for a C93 container.
+static bool CheckC93(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 20)
+ return false;
+ uint16_t index = 1;
+ for (int i = 0; i < 16; i += 4) {
+ if (Read16(buffer + i) != index || !buffer[i + 2] || !buffer[i + 3])
+ return false;
+ index += buffer[i + 2];
+ }
+ return true;
+}
+
+// Additional checks for a CDXL container.
+static bool CheckCdxl(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 32)
+ return false;
+ int type = buffer[0];
+ int current_size = Read32(buffer + 2);
+ int width = Read16(buffer + 14);
+ int height = Read16(buffer + 16);
+ int plane1 = buffer[18];
+ int plane2 = buffer[19];
+ int palette_size = Read16(buffer + 20);
+ int audio_size = Read16(buffer + 22);
+ int image_size = width * height * plane2 / 8;
+ return (type == 1 &&
+ palette_size <= 512 &&
+ plane1 != 0 &&
+ plane2 != 0 &&
+ width != 0 &&
+ height != 0 &&
+ current_size >= audio_size + palette_size + image_size + 32 &&
+ Read32(buffer + 24) == 0 &&
+ Read32(buffer + 28) == 0 &&
+ Read16(buffer + 10) == 0);
+}
+
+// Additional checks for a DNXHD container.
+static bool CheckDnxhd(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 42)
+ return false;
+ int height = Read16(buffer + 24);
+ int width = Read16(buffer + 26);
+ int compression = Read16(buffer + 40);
+ return (StartsWith(buffer,
+ buffer_size,
+ kDnxhdSignature,
+ sizeof(kDnxhdSignature) - 1) &&
+ height > 0 && width > 0 && compression >= 1235 &&
+ compression <= 1253);
+}
+
+// Additional checks for a DSICIN container.
+static bool CheckDsicin(const uint8_t* buffer, int buffer_size) {
+ return (buffer_size > 17 &&
+ Read32(buffer + 12) == 22050 &&
+ buffer[16] == 16 &&
+ buffer[17] == 0);
+}
+
+// Additional checks for an IDCIN container.
+static bool CheckIdcin(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 20)
+ return false;
+ int width = Read32(buffer);
+ int height = Read32(buffer + 4);
+ int rate = Read32(buffer + 8);
+ int bytes = Read32(buffer + 12);
+ int channels = Read32(buffer + 16);
+ return (width > 0 && width <= 1024 &&
+ height > 0 && height <= 1024 &&
+ rate >= 8000 && rate <= 48000 &&
+ bytes >= 0 && bytes <= 2 &&
+ channels >= 0 && channels <= 2);
+}
+
+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_t* buffer, int buffer_size) {
+ if (StartsWith(buffer,
+ buffer_size,
+ kHlsSignature,
+ sizeof(kHlsSignature) - 1)) {
+ // Need to find "#EXT-X-STREAM-INF:", "#EXT-X-TARGETDURATION:",
+ // or "#EXT-X-MEDIA-SEQUENCE:" somewhere in the buffer
+ int offset = sizeof(kHlsSignature) - 1;
+ while (offset < buffer_size) {
+ if (buffer[offset] == '#') {
+ if (StartsWith(buffer + offset,
+ buffer_size - offset,
+ kHls1,
+ sizeof(kHls1) - 1) ||
+ StartsWith(buffer + offset,
+ buffer_size - offset,
+ kHls2,
+ sizeof(kHls2) - 1) ||
+ StartsWith(buffer + offset,
+ buffer_size - offset,
+ kHls3,
+ sizeof(kHls3) - 1))
+ return true;
+ }
+ ++offset;
+ }
+ }
+ return false;
+}
+
+// Checks for a LOAS container.
+static bool CheckLoas(const uint8_t* buffer, int buffer_size) {
+ // LOAS header is 3 bytes.
+ // (from ISO/IEC 14496-3:2005, page 51)
+ int offset = 0;
+ while (offset + 3 < buffer_size) {
+ int header = Read24(buffer);
+ int syncword = (header >> 13) & 0x7ff;
+ int audio_length = (header & 0x1fff);
+ if (syncword != 0x2b7 || audio_length < 4)
+ return false;
+ offset += audio_length + 3 /* header */;
+ }
+ return (offset > 0);
+}
+
+#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
+
+// Checks for a M4V (raw MPEG4) container.
+static bool CheckM4v(const uint8_t* 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 ...
+ 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.
+ 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;
+ }
+ offset += 4;
+ }
+ else {
+ // 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 MM container.
+static bool CheckMm(const uint8_t* buffer, int buffer_size) {
+ int length = Read32(buffer + 2);
+ if (length < 0 || buffer_size < length + 2)
+ return false;
+ int fps = Read16(buffer + 8);
+ int width = Read16(buffer + 12);
+ int height = Read16(buffer + 14);
+ int type = Read16(buffer + length);
+ return ((length == 22 || length == 24) &&
+ fps > 0 && fps <= 60 &&
+ width > 0 && width <= 2048 &&
+ height > 0 && height <= 2048 &&
+ type > 0 && type < 50);
+}
+
+// Additional checks for a MOV container.
+static bool CheckMov(const uint8_t* buffer, int buffer_size) {
+ int offset = 0;
+ while (offset + 16 < buffer_size) {
+ int atomsize = Read32(buffer + offset);
+ uint32_t 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',' '):
+ 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)
+ break; // indicates the last atom or length too big
+ if (atomsize == 1) {
+ // Indicates that the length is the next 64bits.
+ if (Read32(buffer + offset + 8) != 0)
+ break; // offset is way past buffer size
+ atomsize = Read32(buffer + offset + 12);
+ }
+ offset += atomsize;
+ }
+ return (offset > 0);
+}
+
+enum MPEGVersion {
+ Version25 = 0,
+ v_reserved,
+ Version2,
+ Version1
+};
+enum MPEGlayer {
+ l_reserved = 0,
+ Layer3,
+ Layer2,
+ Layer1
+};
+
+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(uint32_t header, int* framesize) {
+ *framesize = 0;
+ // first 11 bits must be all set
+ if ((header & 0xffe00000) != 0xffe00000)
+ return false;
+ // version (bits 20-19) can not be 01
+ int version = (header >> 19) & 0x3;
+ if (version == 1)
+ return false;
+ // layer (bits 18-17) can not be 00
+ int layer = (header >> 17) & 0x3;
+ if (layer == 0)
+ return false;
+ // bitrate (bits 15-12) can not be 1111
+ int bitrate_index = (header >> 12) & 0xf;
+ if (bitrate_index == 0xf)
+ return false;
+ // sampling (bits 11-10) can not be 11
+ int sampling_index = (header >> 10) & 0x3;
+ if (sampling_index == 3)
+ return false;
+
+ // Frame size:
+ // For Layer I files = (12 * BitRate / SampleRate + Padding) * 4
+ // For others = 144 * BitRate / SampleRate + Padding
+ // Unfortunately, BitRate and SampleRate are coded
+ int padding = (header >> 9) & 0x1;
+ 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_t* 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_t* 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(Read32(buffer + offset), &framesize)) {
+ ++numSeen;
+ offset += framesize;
+ }
+ return (numSeen > 10 || offset >= buffer_size);
+}
+
+// Extract an encoded MPC size, returning the value and
+// the number of characters used by the size.
+static int64_t GetMpc8HeaderSize(const uint8_t* buffer, int* headerSize) {
+ int64_t size = 0;
+ uint8_t c;
+ *headerSize = 0;
+ do {
+ c = *buffer++;
+ if (++(*headerSize) > 10)
+ return -1;
+ size = (size << 7) | (c & 0x7f);
+ } while (c & 0x80);
+ return size;
+}
+
+// Additional checks for a MPC8 container.
+static bool CheckMpc8(const uint8_t* buffer, int buffer_size) {
+ int offset = 4;
+ while (offset + 14 < buffer_size) {
+ if (!isupper(buffer[offset]) || !isupper(buffer[offset + 1]))
+ return false;
+
+ int size_characters;
+ int64_t size = GetMpc8HeaderSize(buffer + offset, &size_characters);
+ if (size < 2)
+ return false;
+ if (buffer[offset] == 'S' && buffer[offset + 1] == 'H') {
+ return (size >= 11 && size <= 28 &&
+ Read32(buffer + offset + size_characters + 2) != 0);
+ }
+ offset = offset + size + size_characters;
+ }
+ return false;
+}
+
+// Additional checks for a MSNWCTCP container.
+static bool CheckMsnwctcp(const uint8_t* buffer, int buffer_size) {
+ // The FFmpeg probe scans through the complete buffer looking for
+ // a matching header. This code only looks for it at the beginning
+ // of the buffer.
+ if (buffer_size < 16)
+ return false;
+ int width = Read16(buffer + 2);
+ int height = Read16(buffer + 4);
+ if (!(width == 320 && height == 240) && !(width == 160 && height == 120))
+ return false;
+ return (Read32(buffer + 12) != TAG('M','L','2','0'));
+}
+
+// Additional checks for a MTV container.
+static bool CheckMtv(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 58)
+ return false;
+ int bpp = Read16(buffer + 50);
+ if (bpp != 16)
+ return false;
+ int width = Read16(buffer + 52);
+ int height = Read16(buffer + 54);
+ int segment_size = Read16(buffer + 56);
+ return ((width != 0 && height != 0) || (segment_size != 0));
+}
+
+// Additional checks for a NC container.
+static bool CheckNc(const uint8_t *buffer, int buffer_size) {
+ if (buffer_size < 7)
+ return false;
+ int size = Read16(buffer + 5);
+ return (size + 20 < buffer_size && Read32(buffer + size + 16) == 0x1a5);
+}
+
+// Additional checks for a NSV container, only if the header isn't
+// at the start of the file.
+static bool CheckNsv(const uint8_t* buffer, int buffer_size) {
+ // Get the chunk size and check if at the end we are getting 0xBEEF
+ if (buffer_size < 24)
+ return false;
+ size_t vsize = Read24(buffer + 19) >> 4;
+ size_t asize = Read16(buffer + 22);
+ int offset = 24 + asize + vsize;
+ return (offset + 2 <= buffer_size && Read16(buffer + offset) == 0xbeef);
+}
+
+// Additional checks for an OMA container.
+static bool CheckOma(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 10)
+ return false;
+ if (buffer[4] != 0)
+ return false;
+ int tag_len = GetMp3HeaderSize(buffer);
+ if (tag_len + 6 > buffer_size)
+ return false;
+ return (Read24(buffer + tag_len) == TAG(0,'E','A','3') &&
+ buffer[tag_len + 4] == 0 && buffer[tag_len + 5] == 96);
+}
+
+// Additional checks for a PJS container.
+static bool CheckPjs(const uint8_t* buffer, int buffer_size) {
+ // Movie subtitles file created in the Phoenix Japanimation Society (JPS)
+ // format. It uses a simple text format like:
+ // 1234, 5678, "My subtitle."
+ // Already checked for the 2 numbers, now verify a quoted string exists
+ int offset = 0;
+ // search for starting "
+ while (offset < buffer_size && buffer[offset] != '"') {
+ if (buffer[offset] == '\n' || buffer[offset] == '\r')
+ return false;
+ ++offset;
+ }
+ ++offset;
+ if (offset > buffer_size) // no starting "
+ return false;
+ while (offset < buffer_size && buffer[offset] != '"') {
+ if (buffer[offset] == '\n' || buffer[offset] == '\r')
+ return false;
+ ++offset;
+ }
+ return (offset < buffer_size);
+}
+
+// Additional checks for a PVA container.
+static bool CheckPva(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 8)
+ return false;
+ int length = Read16(buffer + 6);
+ if (buffer[4] != 0x55 || (buffer[5] & 0xe0) != 0 || length > 6136)
+ return false;
+ if (buffer_size < length + 8)
+ return false;
+ return (buffer[length] == 'A' &&
+ buffer[length + 1] == 'V' &&
+ (buffer[length + 2] == 1 || buffer[length + 2] == 2) &&
+ buffer[length + 4] == 0x55 &&
+ (buffer[5] & 0xe0) == 0 &&
+ Read16(buffer + length + 6) <= 6136);
+}
+
+// Additional checks for a TIERTEXSEQ container.
+static bool CheckTiertexseq(const uint8_t* buffer, int buffer_size) {
+ // No real header, but first 256 bytes are always 0
+ if (buffer_size < 257)
+ return false;
+ for (int i = 0; i < 256; ++i)
+ if (buffer[i] != 0)
+ return false;
+ return (buffer[256] != 0 && buffer[257] != 0);
+}
+
+// Additional checks for a TMV container.
+static bool CheckTmv(const uint8_t* buffer, int buffer_size) {
+ return (buffer_size > 10 &&
+ Read16(buffer + 4) >= 5000 && // sample rate
+ Read16(buffer + 6) >= 41 && // audio size
+ buffer[8] == 0 && // compression method
+ buffer[9] != 0 && // width
+ buffer[10] != 0); // height
+}
+
+// Additional checks for a VIVO container.
+static bool CheckVivo(const uint8_t *buffer, int buffer_size) {
+ if (buffer_size < 3)
+ return false;
+ if (buffer[0] != 0)
+ return false;
+ int length = buffer[1] & 0x7f;
+ int offset = 2;
+ if ((buffer[1] & 0x80) != 0) {
+ if (buffer[2] & 0x80)
+ return false;
+ length = (length << 7) + (buffer[2] & 0x7f);
+ offset = 3;
+ }
+ if (length < 21 || length > 1024 || offset + 16 > buffer_size)
+ return false;
+ return (StartsWith(buffer + offset,
+ buffer_size - offset,
+ kVivoSignature,
+ sizeof(kVivoSignature) - 1) &&
+ buffer[offset + 16] >= '0' && buffer[offset + 16] <= '2');
+}
+
+// Additional checks for a VMD container.
+static bool CheckVmd(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 16)
+ return false;
+ int width = Read16(buffer + 12);
+ int height = Read16(buffer + 14);
+ return (width > 0 && width <= 2048 && height > 0 && height <= 2048);
+}
+
+// Additional checks for a VQF container.
+static bool CheckVqf(const uint8_t* buffer, int buffer_size) {
+ return (buffer_size > 4 &&
+ (StartsWith(buffer + 4, buffer_size - 4, "97012000", 8) ||
+ StartsWith(buffer + 4, buffer_size - 4, "00052200", 8)));
+}
+
+// Read a Matroska TAG, updating offset to point past it.
+static int GetVtag(const uint8_t* 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_t* 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_t 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_t* buffer, int buffer_size) {
+ 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", 4)) ||
+ (tagsize >= 8 &&
+ offset < buffer_size &&
+ StartsWith(
+ buffer + offset, buffer_size - offset, "matroska", 8)));
+ default:
+ // Unrecognized tag
+ return false;
+ }
+ }
+ return false;
+}
+
+// Additional checks for a WSAUD container.
+static bool CheckWsaud(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 20)
+ return false;
+ int sample_rate = Read16(buffer);
+ if (sample_rate < 8000 || sample_rate > 48000)
+ return false;
+ if ((buffer[10] & 0xfc) != 0 || (buffer[11] != 1 && buffer[11] != 99))
+ return false;
+ return (Read32(buffer + 16) == 0x0000deaf);
+}
+
+// Additional checks for a XA container.
+static bool CheckXa(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 14)
+ return false;
+ int channels = Read16(buffer + 10);
+ if (channels < 1 || channels > 8)
+ return false;
+ int srate = Read16(buffer + 12);
+ if (srate < 1 || srate > 192000)
+ return false;
+ int bits_per_sample = Read16(buffer + 10);
+ return (bits_per_sample >= 4 || bits_per_sample <= 32);
+}
+
+// Additional checks for a XBIN container.
+static bool CheckXbin(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 10)
+ return false;
+ int x = Read16(buffer + 5);
+ return (buffer[4] == 0x1a &&
+ x > 0 && x <= 160 &&
+ buffer[9] > 0 && buffer[9] <= 32);
+}
+
+// Additional checks for a XMV container.
+static bool CheckXmv(const uint8_t* buffer, int buffer_size) {
+ if (buffer_size < 18)
+ return false;
+ int version = Read16(buffer + 16);
+ return (Read32(buffer + 12) == TAG('x','o','b','X') &&
+ version > 0 && version <= 4);
+}
+
+// Additional checks for a YOP container.
+static bool CheckYop(const uint8_t* buffer, int buffer_size) {
+ return (buffer_size > 20 &&
+ buffer[2] < 10 &&
+ buffer[3] < 10 &&
+ buffer[6] != 0 &&
+ buffer[7] != 0 &&
+ (buffer[8] & 1) == 0 &&
+ (buffer[10] & 1) == 0 &&
+ Read16(buffer + 18) >= 920 &&
+ Read16(buffer + 18) <
+ static_cast<uint32_t>(buffer[12] * 3 + 4 + buffer[7] * 2048));
+}
+
+// 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. It covers the following containers
+// (those with * are not fully covered):
+// 4xm, act, aea, aiff, amr, anm, apc, ape, aqtitle, asf, ass, ast, au, avi,
+// avr, avs, bethsoftvid, bfi, bink, bit, brstm, c93, caff, cdxl, concat,
+// dfa, dnxhd, dsicin, dtshd, dxa, ea, epaf, ffm, ffmetadata, file_cpk, flac,
+// flic, flv, frm, gif, gxf, hls, ico, idf, iff, ilbc, ircam, iss, iv8, ivf,
+// jv, libnut, lmlm4, lvf, lxf, mgsts, mm, mmf, mov, mp3*, mpc, mpc8,
+// msnwctcp*, mtv, mv, nc, nistsphere, nsv*, nut, nuv, ogg, oma, paf, pmp,
+// pva, pvf, qcp, r3d, realtext, rl2, rm, roq, rpl, sami, siff, smjpeg, smk,
+// smush, sol, sox, swf, tak, thp, tiertexseq, tmv, tta, txd, vc1test, vivo,
+// vmd, vobsub, voc, vqf, w64, wav, wc3movie, webm, webvtt, wsaud, wsvqa,
+// wtv, wv, xa, xbin, xmv, xwma, yop, yuv4mpegpipe
+static FFmpegContainerName LookupContainerByFirst4(const uint8_t* buffer,
+ int buffer_size) {
+ // Minimum size that the code expects to exist without checking size.
+ if (buffer_size < 12)
+ return CONTAINER_UNKNOWN;
+
+ uint32_t first4 = Read32(buffer);
+ uint32_t second4 = Read32(buffer + 4);
+ uint32_t third4 = Read32(buffer + 8);
+
+ switch (first4) {
+ case 0:
+ if (buffer_size > 16 &&
+ Read16(buffer + 4) == 0x1bc &&
+ Read32(buffer + 10) == 0 &&
+ Read16(buffer + 14) == 0xe1e2)
+ return CONTAINER_GXF;
+ if (CheckMm(buffer, buffer_size))
+ return CONTAINER_MM;
+ if (second4 > 0 && second4 <= 1024 * 1024 && Read24(buffer + 8) == 1)
+ return CONTAINER_LMLM4;
+ if (CheckTiertexseq(buffer, buffer_size))
+ return CONTAINER_TIERTEXSEQ;
+ break;
+ case 1:
+ if (second4 > 0 && second4 <= 1024 * 1024 && Read24(buffer + 8) == 1)
+ return CONTAINER_LMLM4;
+ if (Read16(buffer + 4) != 0)
+ return CONTAINER_ICO;
+ break;
+ case 2:
+ if (second4 > 0 && second4 <= 1024 * 1024 && Read24(buffer + 8) == 1)
+ return CONTAINER_LMLM4;
+ break;
+ case 4:
+ if (second4 > 0 && second4 <= 1024 * 1024 &&
+ (Read16(buffer + 8) & 0xfffe) == 0xfffc)
+ return CONTAINER_LMLM4;
+ break;
+ case 0xe:
+ if (buffer_size > 16 && second4 == 0x50 && Read32(buffer + 12) == 0x34)
+ return CONTAINER_MGSTS;
+ break;
+ case 0x16:
+ if (third4 == 0x1803ffff || third4 == 0x1003ffff)
+ return CONTAINER_TXD;
+ break;
+ case 0x1a5:
+ if (CheckNc(buffer, buffer_size))
+ return CONTAINER_NC;
+ break;
+ case TAG('\x00','\x00','\x02','\x80'):
+ if (CheckDnxhd(buffer, buffer_size))
+ return CONTAINER_DNXHD;
+ break;
+ case 0x800:
+ if (CheckAea(buffer, buffer_size))
+ return CONTAINER_AEA;
+ break;
+ case 0x001800a0:
+ case 0x00180140:
+ if (CheckMsnwctcp(buffer, buffer_size))
+ return CONTAINER_MSNWCTCP;
+ break;
+ case TAG('\x00','\x0a','S','M'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kSmjpegSignature,
+ sizeof(kSmjpegSignature) - 1))
xhwang 2013/05/06 23:51:27 "conditional or loop statements with complex condi
jrummell 2013/05/16 23:48:01 Done.
+ return CONTAINER_SMJPEG;
+ break;
+ case 0x1084ffff:
+ if (Read16(buffer + 4) == 0xffff)
+ return CONTAINER_ROQ;
+ break;
+ case TAG('\x01','\x01','\x03','\xb8'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kIv8Signature,
+ sizeof(kIv8Signature) - 1))
+ return CONTAINER_IV8;
+ break;
+ case TAG('\x04','\x31','\x2e','\x34'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kIdfSignature,
+ sizeof(kIdfSignature) - 1))
+ return CONTAINER_IDF;
+ break;
+ case TAG('\x0b','\x8d','S','O'):
+ case TAG('\x0c','\x0d','S','O'):
+ case TAG('\x0c','\x8d','S','O'):
+ if (buffer[4] == 'L' && buffer[5] == 0)
+ return CONTAINER_SOL;
+ break;
+ case 0x1a45dfa3:
+ if (CheckWebm(buffer, buffer_size))
+ return CONTAINER_WEBM;
+ break;
+ case TAG('.','s','n','d'):
+ return CONTAINER_AU;
+ case TAG('\x30','\x26','\xB2','\x75'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kAsfSignature,
+ sizeof(kAsfSignature) - 1))
+ return CONTAINER_ASF;
+ break;
+ case TAG(' ','p','a','f'):
+ if (buffer_size > 24 &&
+ second4 == 0 && third4 == 0 &&
+ Read32(buffer + 12) != 0 &&
+ Read32(buffer + 20) != 0)
+ return CONTAINER_EPAF;
+ break;
+ case TAG('#','!','A','M'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kAmrSignature,
+ sizeof(kAmrSignature) - 1))
+ return CONTAINER_AMR;
+ break;
+ case TAG('#','!','i','L'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kIlbcSignature,
+ sizeof(kIlbcSignature) - 1))
+ return CONTAINER_ILBC;
+ break;
+ case TAG('#','E','X','T'):
+ if (CheckHls(buffer, buffer_size))
+ return CONTAINER_HLS;
+ break;
+ case TAG('#',' ','V','o'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kVobsubSignature,
+ sizeof(kVobsubSignature) - 1))
+ return CONTAINER_VOBSUB;
+ 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('.','S','o','X'):
+ case TAG('X','o','S','.'):
+ return CONTAINER_SOX;
+ case TAG('1','S','N','h'):
+ case TAG('S','C','H','l'):
+ case TAG('S','E','A','D'):
+ case TAG('S','H','E','N'):
+ case TAG('k','V','G','T'):
+ case TAG('M','A','D','k'):
+ case TAG('M','P','C','h'):
+ case TAG('M','V','h','d'):
+ if ((second4 > 0x0fffff) && ((second4 & 0x0f0ff) != 0))
+ return CONTAINER_EA;
+ break;
+ case TAG('2','B','I','T'):
+ return CONTAINER_AVR;
+ case TAG('A','N','I','M'):
+ if (third4 == TAG('A','H','D','R'))
+ return CONTAINER_SMUSH;
+ break;
+ case TAG('A','R','M','o'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kRplSignature,
+ sizeof(kRplSignature) - 1))
+ return CONTAINER_RPL;
+ break;
+ case TAG('B','B','C','D'):
+ return CONTAINER_DIRAC;
+ case TAG('B','F','&','I'):
+ return CONTAINER_BFI;
+ case TAG(';','F','F','M'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kFfSignature,
+ sizeof(kFfSignature) - 1))
+ return CONTAINER_FFMETADATA;
+ break;
+ case TAG('B','I','K','b'):
+ 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 (Read16(buffer + 4) == 1)
+ return CONTAINER_CAF;
+ break;
+ case TAG('C','r','e','a'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kVocSignature,
+ sizeof(kVocSignature) - 1))
+ return CONTAINER_VOC;
+ break;
+ case TAG('C','R','Y','O'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kApcSignature,
+ sizeof(kApcSignature) - 1))
+ return CONTAINER_APC;
+ 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','K','I','F'):
+ if (second4 == 32)
+ return CONTAINER_IVF;
+ break;
+ case TAG('D','T','S','H'):
+ if (second4 == TAG('D','H','D','R'))
+ return CONTAINER_DTSHD;
+ break;
+ case TAG('D','F','I','A'):
+ return CONTAINER_DFA;
+ case TAG('\x64','\xa3','\x01','\x00'):
+ 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('e','a','3','\x03'):
+ if (CheckOma(buffer, buffer_size))
+ return CONTAINER_OMA;
+ break;
+ case TAG('f','a','p',' '):
+ if (buffer_size > 24 &&
+ second4 == 0 && third4 == 1 &&
+ Read32(buffer + 12) != 0 &&
+ Read32(buffer + 20) != 0)
+ return CONTAINER_EPAF;
+ break;
+ case TAG('f','f','c','o'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kConcatSignature,
+ sizeof(kConcatSignature) - 1))
+ return CONTAINER_CONCAT;
+ break;
+ case TAG('F','F','M','1'):
+ case TAG('F','F','M','2'):
+ return CONTAINER_FFM;
+ case TAG('F','I','L','M'):
+ return CONTAINER_FILM_CPK;
+ 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;
+ case TAG('8','S','V','X'):
+ case TAG('1','6','S','V'):
+ case TAG('M','A','U','D'):
+ case TAG('P','B','M',' '):
+ case TAG('A','C','B','M'):
+ case TAG('D','E','E','P'):
+ case TAG('I','L','B','M'):
+ case TAG('R','G','B','8'):
+ case TAG('R','G','B','N'):
+ return CONTAINER_IFF;
+ case TAG('M','O','V','E'):
+ return CONTAINER_WC3MOVIE;
+ case TAG('R','L','V','2'):
+ case TAG('R','L','V','3'):
+ return CONTAINER_RL2;
+ case TAG('W','V','Q','A'):
+ return CONTAINER_WSVQA;
+ }
+ break;
+ case TAG('G','I','F','8'):
+ if ((buffer[4] == '7' || buffer[4] == '9') &&
+ buffer[5] == 'a' &&
+ Read16(buffer + 6) != 0 &&
+ Read16(buffer + 8) != 0)
+ return CONTAINER_GIF;
+ break;
+ case TAG('I','M','A','_'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kIssSignature,
+ sizeof(kIssSignature) - 1))
+ return CONTAINER_ISS;
+ break;
+ case TAG('k','!','\x00','\x40'):
+ case TAG('k','!','\x00','\x50'):
+ return CONTAINER_BIT;
+ case TAG('L','E','I','T'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kLxfSignature,
+ sizeof(kLxfSignature) - 1))
+ return CONTAINER_LXF;
+ break;
+ case TAG('L','P','F',' '):
+ if (buffer_size > 24 &&
+ Read32(buffer + 16) == TAG('A','N','I','M') &&
+ Read16(buffer + 20) != 0 &&
+ Read16(buffer + 22) != 0)
+ return CONTAINER_ANM;
+ break;
+ case TAG('L','V','F','F'):
+ return CONTAINER_LVF;
+ case TAG('M','A','C',' '):
+ return CONTAINER_APE;
+ case TAG('M','M','M','D'):
+ if (third4 == TAG('C','N','T','I'))
+ return CONTAINER_MMF;
+ break;
+ case TAG('M','O','V','I'):
+ if (Read16(buffer + 4) < 3)
+ return CONTAINER_MV;
+ break;
+ case TAG('M','P','+','\x07'):
+ case TAG('M','P','+','\x17'):
+ return CONTAINER_MPC;
+ case TAG('M','P','C','K'):
+ if (CheckMpc8(buffer, buffer_size))
+ return CONTAINER_MPC8;
+ break;
+ case TAG('N','I','S','T'):
+ if (second4 == TAG('_','1','A','\x0a'))
+ return CONTAINER_NISTSPHERE;
+ break;
+ case TAG('N','M','\x7a','\x56'):
+ if (second4 == TAG('\x1F','\x5F','\x04','\xAD'))
+ return CONTAINER_NUT;
+ break;
+ case TAG('N','S','V','f'):
+ case TAG('N','S','V','s'):
+ return CONTAINER_NSV;
+ case TAG('n','u','t','/'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kLibnutSignature,
+ sizeof(kLibnutSignature) - 1))
+ return CONTAINER_LIBNUT;
+ break;
+ case TAG('N','u','p','p'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kNuv1Signature,
+ sizeof(kNuv1Signature) - 1))
+ return CONTAINER_NUV;
+ break;
+ case TAG('M','y','t','h'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kNuv2Signature,
+ sizeof(kNuv2Signature) - 1))
+ return CONTAINER_NUV;
+ break;
+ 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('P','a','c','k'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kPafSignature,
+ sizeof(kPafSignature) - 1))
+ return CONTAINER_PAF;
+ break;
+ case TAG('p','m','p','m'):
+ if (Read32(buffer + 4) == 1)
+ return CONTAINER_PMP;
+ break;
+ case TAG('P','V','F','1'):
+ if (buffer[4] == '\n')
+ return CONTAINER_PVF;
+ 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'):
+ if (buffer_size > 24 &&
+ StartsWith(buffer,
+ buffer_size,
+ kW64Signature,
+ sizeof(kW64Signature) - 1) &&
+ StartsWith(buffer + 24,
+ buffer_size - 24,
+ kW64Signature2,
+ sizeof(kW64Signature2) - 1))
+ return CONTAINER_W64;
+ break;
+ case TAG('R','I','F','F'):
+ switch (third4) {
+ case TAG('4','X','M','V'):
+ return CONTAINER_4XM;
+ 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('Q','L','C','M'):
+ if (buffer_size > 16 && Read32(buffer + 12) == TAG('f','m','t',' '))
+ return CONTAINER_QCP;
+ break;
+ case TAG('W','A','V','E'):
+ // possibly ACT or WAV
+ return (CheckAct(buffer, buffer_size)) ?
+ CONTAINER_ACT : CONTAINER_WAV;
+ case TAG('X','W','M','A'):
+ return CONTAINER_XWMA;
+ }
+ break;
+ case TAG('R','S','T','M'):
+ if ((second4 & 0xffff0000) == 0xfffe0000 ||
+ (second4 & 0xffff0000) == 0xfeff0000)
+ return CONTAINER_BRSTM;
+ break;
+ case TAG('S','A','N','M'):
+ if (third4 == TAG('S','H','D','R'))
+ return CONTAINER_SMUSH;
+ break;
+ case TAG('S','I','F','F'):
+ if (third4 == TAG('V','B','V','1') || third4 == TAG('S','O','U','N'))
+ return CONTAINER_SIFF;
+ break;
+ case TAG('S','M','K','2'):
+ case TAG('S','M','K','4'):
+ return CONTAINER_SMK;
+ case TAG('S','T','R','M'):
+ if (buffer_size > 18 && Read16(buffer + 10) && Read16(buffer + 12) &&
+ Read16(buffer + 16))
+ return CONTAINER_AST;
+ break;
+ case TAG('t','B','a','K'):
+ return CONTAINER_TAK;
+ case TAG('T','H','P','\x00'):
+ return CONTAINER_THP;
+ case TAG('T','M','A','V'):
+ if (CheckTmv(buffer, buffer_size))
+ return CONTAINER_TMV;
+ break;
+ case TAG('T','T','A','1'):
+ return CONTAINER_TTA;
+ case TAG('\x55','\xaa','\x00','\x00'):
+ if (CheckDsicin(buffer, buffer_size))
+ return CONTAINER_DSICIN;
+ break;
+ case TAG('T','W','I','N'):
+ if (CheckVqf(buffer, buffer_size))
+ return CONTAINER_VQF;
+ break;
+ case TAG('V','I','D','\x00'):
+ return CONTAINER_BETHSOFTVID;
+ case TAG('w','W','\x10','\x00'):
+ return CONTAINER_AVS;
+ case TAG('w','v','p','k'):
+ return CONTAINER_WV;
+ case TAG('X','A','\x00','\x00'):
+ case TAG('X','A','I','\x00'):
+ case TAG('X','A','J','\x00'):
+ if (CheckXa(buffer, buffer_size))
+ return CONTAINER_XA;
+ break;
+ case TAG('X','B','I','N'):
+ if (CheckXbin(buffer, buffer_size))
+ return CONTAINER_XBIN;
+ break;
+ case TAG('Y','U','V','4'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kYuv4Signature,
+ sizeof(kYuv4Signature) - 1))
+ return CONTAINER_YUV4MPEGPIPE;
+ break;
+ case TAG('W','E','B','V'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kWebvttSignature,
+ sizeof(kWebvttSignature) - 1))
+ return CONTAINER_WEBVTT;
+ break;
+ case TAG('\xef','\xbb','\xbf','W'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kWebvttBomSignature,
+ sizeof(kWebvttBomSignature) - 1))
+ return CONTAINER_WEBVTT;
+ break;
+ case TAG('[','S','c','r'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kAssSignature,
+ sizeof(kAssSignature) - 1))
+ return CONTAINER_ASS;
+ break;
+ case TAG('\xef','\xbb','\xbf','['):
+ if (StartsWith(buffer,
+ buffer_size,
+ kAssBomSignature,
+ sizeof(kAssBomSignature) - 1))
+ return CONTAINER_ASS;
+ break;
+ case TAG('<','w','i','n'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kRealSignature,
+ sizeof(kRealSignature) - 1))
+ return CONTAINER_REALTEXT;
+ break;
+ case TAG('<','S','A','M'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kSamiSignature,
+ sizeof(kSamiSignature) - 1))
+ return CONTAINER_SAMI;
+ break;
+ case TAG('\xef','\xbb','\xbf','<'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kRealBomSignature,
+ sizeof(kRealBomSignature) - 1))
+ return CONTAINER_REALTEXT;
+ if (StartsWith(buffer,
+ buffer_size,
+ kSamiBomSignature,
+ sizeof(kSamiBomSignature) - 1))
+ return CONTAINER_SAMI;
+ break;
+ case TAG('\xb7','\xd8','\x00','\x20'):
+ if (StartsWith(buffer,
+ buffer_size,
+ kWtvSignature,
+ sizeof(kWtvSignature) - 1))
+ return CONTAINER_WTV;
+ break;
+ }
+
+ // Now try a few different ones that look at something other
+ // than the first 4 bytes
+ uint32_t first3 = first4 & 0xffffff00;
+ switch (first3) {
+ case TAG('A','M','V',0):
+ if (CheckMtv(buffer, buffer_size))
+ return CONTAINER_MTV;
+ break;
+ case TAG('C','W','S',0):
+ case TAG('F','W','S',0):
+ return CONTAINER_SWF;
+ case TAG('F','R','M',0):
+ if (Read16(buffer + 4) != 0 && Read16(buffer + 6) != 0)
+ return CONTAINER_FRM;
+ break;
+ case TAG('A','V','\x01',0):
+ case TAG('A','V','\x02',0):
+ if (CheckPva(buffer, buffer_size))
+ return CONTAINER_PVA;
+ break;
+ 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_t first2 = first4 & 0xffff0000;
+ switch (first2) {
+ case 0x032e0000:
+ if (CheckVmd(buffer, buffer_size))
+ return CONTAINER_VMD;
+ break;
+ case 0x04000000:
+ case 0x04040000:
+ case 0x040c0000:
+ case 0x04140000:
+ return CONTAINER_EA_CDATA;
+ case TAG('J','V',0,0):
+ if (StartsWith(buffer + 4,
+ buffer_size - 4,
+ kJvSignature,
+ sizeof(kJvSignature) - 1))
+ return CONTAINER_JV;
+ break;
+ case 0x0b770000:
+ if (CheckAc3(buffer, buffer_size))
+ return CONTAINER_AC3;
+ if (CheckEac3(buffer, buffer_size))
+ return CONTAINER_EAC3;
+ break;
+ case TAG('Y','O',0,0):
+ if (CheckYop(buffer, buffer_size))
+ return CONTAINER_JV;
+ break;
+ case 0xfff00000:
+ case 0xfff10000:
+ case 0xfff80000:
+ case 0xfff90000:
+ if (CheckAac(buffer, buffer_size))
+ return CONTAINER_AAC;
+ break;
+ }
+
+ // Now try the second set of 4 characters.
+ switch (second4) {
+ case 4:
+ if (buffer_size > 24 && buffer[3] == 0xc5 && Read32(buffer + 20) == 0xc)
+ return CONTAINER_VC1TEST;
+ break;
+ case TAG('R','E','D','1'):
+ return CONTAINER_R3D;
+ }
+
+ switch (Read16(buffer + 4)) {
+ case 0xaf11:
+ case 0xaf12:
+ case 0xaf13:
+ if (buffer_size > 20 &&
+ third4 <= 4096 &&
+ Read16(buffer + 10) <= 4096 &&
+ (Read16(buffer + 16) == 0xf1fa || Read32(buffer + 16) <= 2000))
+ return CONTAINER_FLIC;
+ break;
+ }
+
+ // Lastly, there are some that are other simple checks but don't fit
+ // the above case statements.
+ if (CheckC93(buffer, buffer_size))
+ return CONTAINER_C93;
+ if (CheckCdxl(buffer, buffer_size))
+ return CONTAINER_CDXL;
+ if (CheckIdcin(buffer, buffer_size))
+ return CONTAINER_IDCIN;
+ if (CheckLoas(buffer, buffer_size))
+ return CONTAINER_LOAS;
+ if (CheckM4v(buffer, buffer_size))
+ return CONTAINER_M4V;
+ if (CheckMov(buffer, buffer_size))
+ return CONTAINER_MOV;
+ if (CheckVivo(buffer, buffer_size))
+ return CONTAINER_VIVO;
+ if (CheckWsaud(buffer, buffer_size))
+ return CONTAINER_WSAUD;
+ if (CheckXmv(buffer, buffer_size))
+ return CONTAINER_XMV;
+ // 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/AC3/EAC3
+ if (buffer[0] == 0) {
+ size_t offset = 1;
+ size_t remaining = buffer_size - 1;
+ while (remaining > 0 && buffer[offset] == 0) {
+ ++offset;
+ --remaining;
+ }
+ if (remaining > 32) {
+ // not worth trying if only a small number of bytes left
+ if (Read16(buffer + offset) == 0x0b77) {
+ if (CheckAc3(buffer + offset, remaining))
+ return CONTAINER_AC3;
+ if (CheckEac3(buffer + offset, remaining))
+ return CONTAINER_EAC3;
+ }
+ else if (CheckMp3(buffer + offset, remaining, false))
+ return CONTAINER_MP3;
+ }
+ }
+
+ return CONTAINER_UNKNOWN;
+}
+
+static const char kIpmString[] = "Interplay MVE File\x1A\0\x1A";
+static const char kMxfString[] =
+ "\x06\x0e\x2b\x34\x02\x05\x01\x01\x0d\x01\x02\x01\x01\x02";
+static const char kSub1String[] = "******** START SCRIPT ********";
+
+// Attempt to determine the container type by scanning for a set of strings,
+// character by character. It covers the following containers:
+// ipmovie, mxf, subviewer1
scherkus (not reviewing) 2013/05/07 00:50:20 I can say with a good deal of certainty that we do
jrummell 2013/05/16 23:48:01 Partially done. Still need LookupContainerByString
+static FFmpegContainerName LookupContainerByStringScan(const uint8_t* buffer,
+ int buffer_size) {
+ int offset = 0;
+ for (int remaining = buffer_size; remaining > 4; --remaining) {
+ uint32_t tag = Read32(buffer + offset);
+ switch (tag) {
+ case TAG('I','n','t','e'):
+ if (StartsWith(buffer + offset,
+ remaining,
+ kIpmString,
+ sizeof(kIpmString) - 1))
+ return CONTAINER_IPMOVIE;
+ break;
+ case 0x060e2b34:
+ if (StartsWith(buffer + offset,
+ remaining,
+ kMxfString,
+ sizeof(kMxfString) - 1))
+ return CONTAINER_MXF;
+ break;
+ case TAG('*','*','*','*'):
+ if (StartsWith(buffer + offset,
+ remaining,
+ kSub1String,
+ sizeof(kSub1String) - 1))
+ return CONTAINER_SUBVIEWER1;
+ break;
+ case TAG('N','S','V','s'):
+ if (CheckNsv(buffer + offset, remaining))
+ return CONTAINER_NSV;
+ break;
+ case 0x001800a0:
+ case 0x00180140:
+ if (CheckMsnwctcp(buffer + offset, remaining))
+ return CONTAINER_MSNWCTCP;
+ break;
+ }
+ // Not found, move forward to next character.
+ ++offset;
+ }
+
+ // didn't find a string match for any of the formats
+ return CONTAINER_UNKNOWN;
+}
+
+// Helper function to do limited scanf functionality without going off
+// the end of the buffer.
+static bool SaferScanf(const uint8_t* 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)
+ // %<maxlength>u -- unsigned integer (only digits, 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':
+ case 'u':
+ 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/%u
+ 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. It covers the following containers:
+// microdvd, mpl2, mpsub, pjs, sdp, srt, subviewer, vplayer
+static FFmpegContainerName LookupContainerByStringLine(const uint8_t* buffer,
+ int buffer_size) {
+ int offset = StartsWith(buffer, buffer_size, BYTE_ORDER_MARK, 3) ? 3 : 0;
+ int lines = 0;
+
+ // PJS is a scan from the beginning only.
+ if (SaferScanf(buffer, buffer_size, "%d,%d,%c"))
+ if (CheckPjs(buffer, buffer_size))
+ return CONTAINER_PJS;
+
+ // Same for VPLAYER.
+ if (SaferScanf(buffer, buffer_size, "%d:%d:%d.%d%[: =]"))
+ return CONTAINER_VPLAYER;
+
+ // Same for AQTITLE
+ if (SaferScanf(buffer, buffer_size, "-->> %d"))
+ return CONTAINER_AQTITLE;
+
+ while (offset < buffer_size) {
+ const uint8_t* ptr = buffer + offset;
+ int remaining = buffer_size - offset;;
+
+ if (*ptr == '{' && lines < 3) {
+ if (SaferScanf(ptr, remaining, "{%d}{}%c") ||
+ SaferScanf(ptr, remaining, "{%d}{%d}%c") ||
+ SaferScanf(ptr, remaining, "{DEFAULT}{}%c"))
+ return CONTAINER_MICRODVD;
+ }
+
+ if (*ptr == '[' && lines < 3) {
+ if (SaferScanf(ptr, remaining, "[%64d][%64d]%c") ||
+ SaferScanf(ptr, remaining, "[%64d][]%c"))
+ return CONTAINER_MPL2;
+ }
+
+ if (*ptr == 'F') {
+ if (SaferScanf(ptr, remaining, "FORMAT=%d") ||
+ SaferScanf(ptr, remaining, "FORMAT=TIME"))
+ return CONTAINER_MPSUB;
+ }
+
+ if (*ptr == 'c') {
+ if (StartsWith(ptr, remaining, "c=IN IP", 7))
+ return CONTAINER_SDP;
+ }
+
+ if (isdigit(*ptr) && lines < 3) {
+ if (SaferScanf(
+ ptr, remaining, "%d:%2d:%2d%[,.]%3d --> %d:%2d:%2d%[,.]%3d"))
+ return CONTAINER_SRT;
+ }
+
+ if (isdigit(*ptr) && lines < 1) {
+ if (SaferScanf(ptr, remaining, "%u:%u:%u.%u,%u:%u:%u.%u%c") ||
+ StartsWith(ptr, remaining, "[INFORMATION]", 13))
+ return CONTAINER_SUBVIEWER;
+ }
+
+ // Find the end of the line.
+ while (buffer[offset] != '\n' && buffer[offset] != '\r' &&
+ offset < buffer_size)
+ ++offset;
+
+ // Skip the \n\r.
+ while ((buffer[offset] == '\n' || buffer[offset] == '\r') &&
+ offset < buffer_size)
+ ++offset;
+ ++lines;
+ }
+
+ // Didn't find a string match for any of the formats.
+ return CONTAINER_UNKNOWN;
+}
+
+// The strings in the list below are the ones returned by FFmpeg.
+// This would be the string from AVInputFormat.name. The list is
+// sorted by string alphabetically, so that we can use a binary
+// search when looking for a container by name.
+static const ContainerNameMapping kContainerNameMapping[] = {
+ { CONTAINER_4XM, "4xm" },
+ { CONTAINER_AAC, "aac" },
+ { CONTAINER_AC3, "ac3" },
+ { CONTAINER_ACT, "act" },
+ { CONTAINER_ADF, "adf" },
+ { CONTAINER_ADX, "adx" },
+ { CONTAINER_AEA, "aea" },
+ { CONTAINER_AFC, "afc" },
+ { CONTAINER_AIFF, "aiff" },
+ { CONTAINER_ALAW, "alaw" },
+ { CONTAINER_ALSA, "alsa" },
+ { CONTAINER_AMR, "amr" },
+ { CONTAINER_ANM, "anm" },
+ { CONTAINER_APC, "apc" },
+ { CONTAINER_APE, "ape" },
+ { CONTAINER_AQTITLE, "aqtitle" },
+ { CONTAINER_ASF, "asf" },
+ { CONTAINER_ASS, "ass" },
+ { CONTAINER_AST, "ast" },
+ { CONTAINER_AU, "au" },
+ { CONTAINER_AVI, "avi" },
+ { CONTAINER_AVISYNTH, "avisynth" },
+ { CONTAINER_AVR, "avr" },
+ { CONTAINER_AVS, "avs" },
+ { CONTAINER_BETHSOFTVID, "bethsoftvid" },
+ { CONTAINER_BFI, "bfi" },
+ { CONTAINER_BIN, "bin" },
+ { CONTAINER_BINK, "bink" },
+ { CONTAINER_BIT, "bit" },
+ { CONTAINER_BKTR, "bktr" },
+ { CONTAINER_BMV, "bmv" },
+ { CONTAINER_BRSTM, "brstm" },
+ { CONTAINER_C93, "c93" },
+ { CONTAINER_CAF, "caf" },
+ { CONTAINER_CAVSVIDEO, "cavsvideo" },
+ { CONTAINER_CDG, "cdg" },
+ { CONTAINER_CDXL, "cdxl" },
+ { CONTAINER_CONCAT, "concat" },
+ { CONTAINER_DAUD, "daud" },
+ { CONTAINER_DFA, "dfa" },
+ { CONTAINER_DIRAC, "dirac" },
+ { CONTAINER_DNXHD, "dnxhd" },
+ { CONTAINER_DSHOW, "dshow" },
+ { CONTAINER_DSICIN, "dsicin" },
+ { CONTAINER_DTS, "dts" },
+ { CONTAINER_DTSHD, "dtshd" },
+ { CONTAINER_DV, "dv" },
+ { CONTAINER_DV1394, "dv1394" },
+ { CONTAINER_DXA, "dxa" },
+ { CONTAINER_EA, "ea" },
+ { CONTAINER_EA_CDATA, "ea_cdata" },
+ { CONTAINER_EAC3, "eac3" },
+ { CONTAINER_EPAF, "epaf" },
+ { CONTAINER_F32BE, "f32be" },
+ { CONTAINER_F32LE, "f32le" },
+ { CONTAINER_F64BE, "f64be" },
+ { CONTAINER_F64LE, "f64le" },
+ { CONTAINER_FBDEV, "fbdev" },
+ { CONTAINER_FFM, "ffm" },
+ { CONTAINER_FFMETADATA, "ffmetadata" },
+ { CONTAINER_FILM_CPK, "film_cpk" },
+ { CONTAINER_FILMSTRIP, "filmstrip" },
+ { CONTAINER_FLAC, "flac" },
+ { CONTAINER_FLIC, "flic" },
+ { CONTAINER_FLV, "flv" },
+ { CONTAINER_FRM, "frm" },
+ { CONTAINER_G722, "g722" },
+ { CONTAINER_G723_1, "g723_1" },
+ { CONTAINER_G729, "g729" },
+ { CONTAINER_GIF, "gif" },
+ { CONTAINER_GSM, "gsm" },
+ { CONTAINER_GXF, "gxf" },
+ { CONTAINER_H261, "h261" },
+ { CONTAINER_H263, "h263" },
+ { CONTAINER_H264, "h264" },
+ { CONTAINER_HLS, "hls,applehttp" },
+ { CONTAINER_ICO, "ico" },
+ { CONTAINER_IDCIN, "idcin" },
+ { CONTAINER_IDF, "idf" },
+ { CONTAINER_IEC61883, "iec61883" },
+ { CONTAINER_IFF, "iff" },
+ { CONTAINER_ILBC, "ilbc" },
+ { CONTAINER_IMAGE2, "image2" },
+ { CONTAINER_IMAGE2PIPE, "image2pipe" },
+ { CONTAINER_INGENIENT, "ingenient" },
+ { CONTAINER_IPMOVIE, "ipmovie" },
+ { CONTAINER_IRCAM, "ircam" },
+ { CONTAINER_ISS, "iss" },
+ { CONTAINER_IV8, "iv8" },
+ { CONTAINER_IVF, "ivf" },
+ { CONTAINER_JACK, "jack" },
+ { CONTAINER_JACOSUB, "jacosub" },
+ { CONTAINER_JV, "jv" },
+ { CONTAINER_LATM, "latm" },
+ { CONTAINER_LAVFI, "lavfi" },
+ { CONTAINER_LIBCDIO, "libcdio" },
+ { CONTAINER_LIBDC1394, "libdc1394" },
+ { CONTAINER_LIBMODPLUG, "libmodplug" },
+ { CONTAINER_LIBNUT, "libnut" },
+ { CONTAINER_LMLM4, "lmlm4" },
+ { CONTAINER_LOAS, "loas" },
+ { CONTAINER_LVF, "lvf" },
+ { CONTAINER_LXF, "lxf" },
+ { CONTAINER_M4V, "m4v" },
+ { CONTAINER_WEBM, "matroska,webm" },
+ { CONTAINER_MGSTS, "mgsts" },
+ { CONTAINER_MICRODVD, "microdvd" },
+ { CONTAINER_MJPEG, "mjpeg" },
+ { CONTAINER_MLP, "mlp" },
+ { CONTAINER_MM, "mm" },
+ { CONTAINER_MMF, "mmf" },
+ { CONTAINER_MOV, "mov,mp4,m4a,3gp,3g2,mj2" },
+ { CONTAINER_MP3, "mp3" },
+ { CONTAINER_MPC, "mpc" },
+ { CONTAINER_MPC8, "mpc8" },
+ { CONTAINER_MPEG, "mpeg" },
+ { CONTAINER_MPEGTS, "mpegts" },
+ { CONTAINER_MPEGTSRAW, "mpegtsraw" },
+ { CONTAINER_MPEGVIDEO, "mpegvideo" },
+ { CONTAINER_MPL2, "mpl2" },
+ { CONTAINER_MPSUB, "mpsub" },
+ { CONTAINER_MSNWCTCP, "msnwctcp" },
+ { CONTAINER_MTV, "mtv" },
+ { CONTAINER_MULAW, "mulaw" },
+ { CONTAINER_MV, "mv" },
+ { CONTAINER_MVI, "mvi" },
+ { CONTAINER_MXF, "mxf" },
+ { CONTAINER_MXG, "mxg" },
+ { CONTAINER_NC, "nc" },
+ { CONTAINER_NISTSPHERE, "nistsphere" },
+ { CONTAINER_NSV, "nsv" },
+ { CONTAINER_NUT, "nut" },
+ { CONTAINER_NUV, "nuv" },
+ { CONTAINER_OGG, "ogg" },
+ { CONTAINER_OMA, "oma" },
+ { CONTAINER_OPENAL, "openal" },
+ { CONTAINER_OSS, "oss" },
+ { CONTAINER_PAF, "paf" },
+ { CONTAINER_PJS, "pjs" },
+ { CONTAINER_PMP, "pmp" },
+ { CONTAINER_PSXSTR, "psxstr" },
+ { CONTAINER_PULSE, "pulse" },
+ { CONTAINER_PVA, "pva" },
+ { CONTAINER_PVF, "pvf" },
+ { CONTAINER_QCP, "qcp" },
+ { CONTAINER_R3D, "r3d" },
+ { CONTAINER_RAWVIDEO, "rawvideo" },
+ { CONTAINER_RDT, "rdt" },
+ { CONTAINER_REALTEXT, "realtext" },
+ { CONTAINER_RL2, "rl2" },
+ { CONTAINER_RM, "rm" },
+ { CONTAINER_ROQ, "roq" },
+ { CONTAINER_RPL, "rpl" },
+ { CONTAINER_RSO, "rso" },
+ { CONTAINER_RTP, "rtp" },
+ { CONTAINER_RTSP, "rtsp" },
+ { CONTAINER_S16BE, "s16be" },
+ { CONTAINER_S16LE, "s16le" },
+ { CONTAINER_S24BE, "s24be" },
+ { CONTAINER_S24LE, "s24le" },
+ { CONTAINER_S32BE, "s32be" },
+ { CONTAINER_S32LE, "s32le" },
+ { CONTAINER_S8, "s8" },
+ { CONTAINER_SAMI, "sami" },
+ { CONTAINER_SAP, "sap" },
+ { CONTAINER_SBG, "sbg" },
+ { CONTAINER_SDP, "sdp" },
+ { CONTAINER_SHN, "shn" },
+ { CONTAINER_SIFF, "siff" },
+ { CONTAINER_SMJPEG, "smjpeg" },
+ { CONTAINER_SMK, "smk" },
+ { CONTAINER_SMUSH, "smush" },
+ { CONTAINER_SNDIO, "sndio" },
+ { CONTAINER_SOL, "sol" },
+ { CONTAINER_SOX, "sox" },
+ { CONTAINER_SPDIF, "spdif" },
+ { CONTAINER_SRT, "srt" },
+ { CONTAINER_SUBVIEWER, "subviewer" },
+ { CONTAINER_SUBVIEWER1, "subviewer1" },
+ { CONTAINER_SWF, "swf" },
+ { CONTAINER_TAK, "tak" },
+ { CONTAINER_TEDCAPTIONS, "tedcaptions" },
+ { CONTAINER_THP, "thp" },
+ { CONTAINER_TIERTEXSEQ, "tiertexseq" },
+ { CONTAINER_TMV, "tmv" },
+ { CONTAINER_TRUEHD, "truehd" },
+ { CONTAINER_TTA, "tta" },
+ { CONTAINER_TTY, "tty" },
+ { CONTAINER_TXD, "txd" },
+ { CONTAINER_VC1, "vc1" },
+ { CONTAINER_VC1TEST, "vc1test" },
+ { CONTAINER_VFWCAP, "vfwcap" },
+ { CONTAINER_V4L, "video4linux,v4l" },
+ { CONTAINER_V4L2, "video4linux2,v4l2" },
+ { CONTAINER_VIVO, "vivo" },
+ { CONTAINER_VMD, "vmd" },
+ { CONTAINER_VOBSUB, "vobsub" },
+ { CONTAINER_VOC, "voc" },
+ { CONTAINER_VPLAYER, "vplayer" },
+ { CONTAINER_VQF, "vqf" },
+ { CONTAINER_W64, "w64" },
+ { CONTAINER_WAV, "wav" },
+ { CONTAINER_WC3MOVIE, "wc3movie" },
+ { CONTAINER_WEBVTT, "webvtt" },
+ { CONTAINER_WSAUD, "wsaud" },
+ { CONTAINER_WSVQA, "wsvqa" },
+ { CONTAINER_WTV, "wtv" },
+ { CONTAINER_WV, "wv" },
+ { CONTAINER_X11GRAB, "x11grab" },
+ { CONTAINER_XA, "xa" },
+ { CONTAINER_XBIN, "xbin" },
+ { CONTAINER_XMV, "xmv" },
+ { CONTAINER_XWMA, "xwma" },
+ { CONTAINER_YOP, "yop" },
+ { CONTAINER_YUV4MPEGPIPE, "yuv4mpegpipe" }
+};
+
+// Mapping from the strings returned by FFmpeg to container ID.
+const ContainerNameMapping ContainerNameMappingItem(int index) {
+ return kContainerNameMapping[index];
+}
+
+// Returns the number of elements in kContainerNameMapping.
+const int ContainerNameMappingSize() {
+ return arraysize(kContainerNameMapping);
+}
+
+// Lookup a container name using the list above (kContainerNameMapping)
+// to determine the container enum. If not found (recent addition to
+// FFmpeg or a name change), return CONTAINER_UNKNOWN.
+FFmpegContainerName LookupContainer(
+ const char* container_name) {
+ DCHECK(container_name);
+ const ContainerNameMapping* found =
+ std::lower_bound(kContainerNameMapping,
+ kContainerNameMapping + ContainerNameMappingSize() - 1,
+ container_name,
+ ContainerNameMappingComparer);
+ return (strcasecmp(found->name, container_name) == 0) ? found->id
+ : CONTAINER_UNKNOWN;
+}
+
+// Attempt to determine the container name from the buffer provided.
+FFmpegContainerName DetermineContainer(const uint8_t* buffer,
+ size_t buffer_size) {
xhwang 2013/05/06 23:51:27 indent
jrummell 2013/05/16 23:48:01 Done.
+ DCHECK(buffer);
+ DCHECK_LE(buffer_size, static_cast<size_t>(std::numeric_limits<int>::max()));
+ // TODO(jrummell): The following formats are not scanned for
+ // cavsvideo, dts, dv, h261, h263, h264, jacosub,
+ // mpeg, mpegts, mpegvideo, psxstr, sbg, spdif, tedcaptions
+
+ // First 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;
+
+ // No success with simple test, so attempt to determine the container by
+ // looking for strings in the buffer.
+ result = LookupContainerByStringScan(buffer, buffer_size);
+ if (result != CONTAINER_UNKNOWN)
+ return result;
+ return LookupContainerByStringLine(buffer, buffer_size);
+}
+
+// Log the container based on the name returned by FFmpeg.
+void LogContainer(const char* container_name) {
+ FFmpegContainerName container = LookupContainer(container_name);
+ LogContainerToHistogram(container, false);
+}
+
+// Log the container by examining the first part of the stream.
+void LogContainer(const uint8_t* buffer, size_t buffer_size) {
+ FFmpegContainerName container = DetermineContainer(buffer, buffer_size);
+ LogContainerToHistogram(container, true);
+}
+
+} // namespace container_names

Powered by Google App Engine
This is Rietveld 408576698