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

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..73279d6a8a7d521ab1cfa6c06a02a2e2e63dcd9c
--- /dev/null
+++ b/media/base/container_names.cc
@@ -0,0 +1,1993 @@
+// 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 <cstdio>
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+
+namespace media {
+
+// Template to extract array size.
+template <typename T, size_t N> inline size_t size(T(&)[N]) { return N; }
Ilya Sherman 2013/04/29 23:48:06 Any reason not to use the shared arraysize macro d
jrummell 2013/04/30 21:36:50 Done.
+
+#define READ16(p) \
+ (((uint32_t)((const uint8_t*)(p))[0] << 8) | \
+ ((const uint8_t*)(p))[1])
Ilya Sherman 2013/04/29 23:48:06 Please use C++-style casts, rather than C-style ca
jrummell 2013/04/30 21:36:50 Unable to find a similar function in base. Convert
+#define READ24(p) \
+ (((uint32_t)((const uint8_t*)(p))[0] << 16) | \
+ (((const uint8_t*)(p))[1] << 8) | \
+ ((const uint8_t*)(p))[2])
+#define READ32(p) \
+ (((uint32_t)((const uint8_t*)(p))[0] << 24) | \
+ (((const uint8_t*)(p))[1] << 16) | \
+ (((const uint8_t*)(p))[2] << 8) | \
+ ((const uint8_t*)(p))[3])
+
+#define TAG(a, b, c, d) \
+ (((uint32_t)(a) << 24) | ((uint8_t)(b) << 16) | \
+ ((uint8_t)(c) << 8) | (uint8_t)(d))
+
+// 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.
+const ContainerNameMapping ContainerNames::container_name_mapping[] = {
Ilya Sherman 2013/04/29 23:48:06 Any reason not to tuck this into an anonymous name
jrummell 2013/04/30 21:36:50 This is used by the unittests to verify it is sort
Ilya Sherman 2013/05/02 06:34:09 If you're only exposing it for testing, then I'd r
jrummell 2013/05/02 21:53:09 Moved to internal, test class is a friend.
+ { 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" }
+};
+
+// Lookup a container name using the list above (container_name_mapping)
+// to determine the container enum. If not found (recent addition to
+// FFmpeg or a name change), return CONTAINER_UNKNOWN.
+enum FFmpegContainerName ContainerNames::LookupContainer(
+ const char* container_name) {
+ DCHECK_NE(container_name, (const char*)NULL);
Ilya Sherman 2013/04/29 23:48:06 nit: This is usually written as "DCHECK(container_
jrummell 2013/04/30 21:36:50 Done.
+
+ // Do a binary search through supported_mapping looking for a match.
Ilya Sherman 2013/04/29 23:48:06 Any reason not to just use binary_search() from st
jrummell 2013/04/30 21:36:50 My understanding of binary_search is that it simpl
Ilya Sherman 2013/05/02 06:34:09 lower_bound should work with raw pointers into an
jrummell 2013/05/02 21:53:09 Done.
+ int left = 0;
+ int right = size(container_name_mapping) - 1;
+ while (left <= right) {
+ int middle = (left + right) / 2;
+ int compare = strcmp(container_name, container_name_mapping[middle].name);
+ if (compare == 0)
+ return container_name_mapping[middle].id;
+ else if (compare < 0)
+ right = middle - 1;
+ else
+ left = middle + 1;
+ }
xhwang 2013/04/29 21:00:25 indent
jrummell 2013/04/30 21:36:50 Done.
+ return CONTAINER_UNKNOWN;
+}
+
+// For some formats the signature is a bunch of characters. They are defined
+// below. If the string contains \00, then it needs to be handled differently.
+#define BOM "\xef\xbb\xbf"
Ilya Sherman 2013/04/29 23:48:06 Any reason not to define this as a named constant,
jrummell 2013/04/30 21:36:50 This is used in several definitions below, where i
+#define AMR_SIGNATURE "#!AMR"
+#define AMR_TAG TAG('#','!','A','M')
+
+#define APC_SIGNATURE "CRYO_APC"
+#define APC_TAG TAG('C','R','Y','O')
+
+#define AQTITLE_FORMAT "-->> %d"
+#define AQTITLE_TAG TAG('-','-','>','>')
+
+static const char ASF_SIGNATURE[] =
+ "\x30\x26\xb2\x75\x8e\x66\xcf\x11\xa6\xd9\x00\xaa\x00\x62\xce\x6c";
+#define ASF_TAG 0x3026b275
+
+#define ASS_SIGNATURE "[Script Info]"
+#define ASS_TAG TAG('[','S','c','r')
+#define ASS_BOM_SIGNATURE BOM ASS_SIGNATURE
+#define ASS_BOM_TAG TAG('\xef','\xbb','\xbf','[')
+
+#define CONCAT_SIGNATURE "ffconcat version 1.0"
+#define CONCAT_TAG TAG('f','f','c','o')
+
+static const char DNXHD_SIGNATURE[] = "\x00\x00\x02\x80\x01";
+#define DNXHD_TAG 0x00000280
+
+#define FFMETADATA_SIGNATURE ";FFMETADATA"
+#define FFMETADATA_TAG TAG(';','F','F','M')
+
+#define HLS_SIGNATURE "#EXTM3U"
+#define HLS_TAG TAG('#','E','X','T')
+#define HLS1 "#EXT-X-STREAM-INF:"
+#define HLS2 "#EXT-X-TARGETDURATION:"
+#define HLS3 "#EXT-X-MEDIA-SEQUENCE:"
+
+static const char IDF_SIGNATURE[] =
Ilya Sherman 2013/04/29 23:48:06 nit: This should use kConstantName casing rather t
jrummell 2013/04/30 21:36:50 Done.
+ "\x04\x31\x2e\x34\x00\x00\x00\x00\x4f\x00\x15\x00";
+#define IDF_TAG 0x04312e34
+
+#define ILBC_SIGNATURE "#!iLBC"
+#define ILBC_TAG TAG('#','!','i','L')
+
+#define ISS_SIGNATURE "IMA_ADPCM_Sound"
+#define ISS_TAG TAG('I','M','A','_')
+
+#define IV8_SIGNATURE "\x01\x01\x03\xb8\x80\x60"
+#define IV8_TAG 0x010103b8
+
+#define JV_SIGNATURE " Compression by John M Phillips " \
+ "Copyright (C) 1995 The Bitmap Brothers Ltd."
+
+#define LIBNUT_SIGNATURE "nut/multimedia container"
+#define LIBNUT_TAG TAG('n','u','t','/')
+
+static const char LXF_SIGNATURE[] = "LEITCH\x00\x00";
+#define LXF_TAG TAG('L','E','I','T')
+
+#define NUV1_SIGNATURE "NuppelVideo"
+#define NUV1_TAG TAG('N','u','p','p')
+#define NUV2_SIGNATURE "MythTVVideo"
+#define NUV2_TAG TAG('M','y','t','h')
+
+#define PAF_SIGNATURE \
+ "Packed Animation File V1.0\n(c) 1992-96 Amazing Studio\x0a\x1a"
+#define PAF_TAG TAG('P','a','c','k')
+
+#define REALTEXT_SIGNATURE "<window"
+#define REALTEXT_TAG TAG('<','w','i','n')
+#define REALTEXT_BOM_SIGNATURE BOM REALTEXT_SIGNATURE
+#define REALTEXT_BOM_TAG TAG('\xef','\xbb','\xbf','<')
+
+#define RPL_SIGNATURE "ARMovie\x0A"
+#define RPL_TAG TAG('A','R','M','o')
+
+#define SAMI_SIGNATURE "<SAMI>"
+#define SAMI_TAG TAG('<','S','A','M')
+#define SAMI_BOM_SIGNATURE BOM SAMI_SIGNATURE
+#define SAMI_BOM_TAG TAG('\xef','\xbb','\xbf','<')
+
+static const char SMJPEG_SIGNATURE[] = "\x00\x0aSMJPEG";
+#define SMJPEG_TAG TAG('\x00','\x0a','S','M')
+
+#define VIVO_SIGNATURE "\r\nVersion:Vivo/"
+
+#define VOBSUB_SIGNATURE "# VobSub index file,"
+#define VOBSUB_TAG TAG('#',' ','V','o')
+
+#define VOC_SIGNATURE "Creative Voice File\x1A"
+#define VOC_TAG TAG('C','r','e','a')
+
+static const char W64_SIGNATURE[] =
+ "riff\x2e\x91\xcf\x11\xa5\xd6\x28\xdb\x04\xc1\x00\x00";
+static const char W64_SIGNATURE2[] =
+ "wave\xf3\xac\xd3\x11\x8c\xd1\x00\xc0\x4f\x8e\xdb\x8a";
+
+#define WEBVTT_SIGNATURE "WEBVTT"
+#define WEBVTT_TAG TAG('W','E','B','V')
+#define WEBVTT_BOM_SIGNATURE BOM WEBVTT_SIGNATURE
+#define WEBVTT_BOM_TAG TAG('\xef','\xbb','\xbf','W')
+
+static const char WTV_SIGNATURE[] =
+ "\xb7\xd8\x00\x20\x37\x49\xda\x11\xa6\x4e\x00\x07\xe9\x5e\xad\x8d";
+#define WTV_TAG 0xb7d80020
+
+#define YUV4_SIGNATURE "YUV4MPEG2"
+#define YUV4_TAG TAG('Y','U','V','4')
+
+const uint16_t ac3_frame_size_table[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 },
+};
+
+// Additional checks for an AC3 container.
+bool check_ac3(const uint8_t buffer[], size_t 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
+ size_t 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 += ac3_frame_size_table[frame_size_code][sample_rate_code];
+ }
+ return true;
+}
+
+// Additional checks for an EAC3 container (very similar to AC3)
+bool check_eac3(const uint8_t buffer[], size_t 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
+ size_t 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 true;
+}
+
+// Additional checks for an ACT container.
+bool check_act(const uint8_t buffer[], size_t 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.
+bool check_aea(const uint8_t buffer[], size_t 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.
+bool check_bink(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t frames = READ32(buffer + 8);
+ uint32_t width = READ32(buffer + 20);
+ uint32_t height = READ32(buffer + 24);
+ uint32_t fps = READ32(buffer + 28);
+ uint32_t 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.
+bool check_c93(const uint8_t buffer[], size_t buffer_size) {
+ int 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.
+bool check_cdxl(const uint8_t buffer[], size_t buffer_size) {
+ uint8_t type = buffer[0];
+ uint32_t current_size = READ32(buffer + 2);
+ uint32_t width = READ16(buffer + 14);
+ uint32_t height = READ16(buffer + 16);
+ uint8_t plane1 = buffer[18];
+ uint8_t plane2 = buffer[19];
+ uint32_t palette_size = READ16(buffer + 20);
+ uint32_t audio_size = READ16(buffer + 22);
+ uint32_t 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.
+bool check_dnxhd(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t height = READ16(buffer + 24);
+ uint32_t width = READ16(buffer + 26);
+ uint32_t compression = READ16(buffer + 40);
+ return (memcmp(buffer, DNXHD_SIGNATURE, sizeof(DNXHD_SIGNATURE)-1) == 0 &&
+ height > 0 &&
+ width > 0 &&
+ compression >= 1235 && compression <= 1253);
+}
+
+// Additional checks for a DSICIN container.
+bool check_dsicin(const uint8_t buffer[], size_t buffer_size) {
+ return (READ32(buffer + 12) == 22050 &&
+ buffer[16] == 16 &&
+ buffer[17] == 0);
+}
+
+// Additional checks for an IDCIN container.
+bool check_idcin(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t width = READ32(buffer);
+ uint32_t height = READ32(buffer + 4);
+ uint32_t rate = READ32(buffer + 8);
+ uint32_t bytes = READ32(buffer + 12);
+ uint32_t channels = READ32(buffer + 16);
+ return (width > 0 && width <= 1024 &&
+ height > 0 && height <= 1024 &&
+ rate >= 8000 && rate <= 48000 &&
+ bytes <= 2 &&
+ channels <= 2);
+}
+
+// Additional checks for a HLS container.
+bool check_hls(const uint8_t buffer[], size_t buffer_size) {
+ if (memcmp(buffer, HLS_SIGNATURE, strlen(HLS_SIGNATURE)) == 0) {
+ // Need to find "#EXT-X-STREAM-INF:", "#EXT-X-TARGETDURATION:",
+ // or "#EXT-X-MEDIA-SEQUENCE:" somewhere in the buffer
+ size_t offset = strlen(HLS_SIGNATURE);
+ while (offset < buffer_size - strlen(HLS2)) {
+ if (buffer[offset] == '#') {
+ if (memcmp(buffer + offset, HLS1, strlen(HLS1)) == 0 ||
+ memcmp(buffer + offset, HLS2, strlen(HLS2)) == 0 ||
+ memcmp(buffer + offset, HLS3, strlen(HLS3)) == 0)
+ return true;
+ }
+ ++offset;
+ }
+ }
+ return false;
+}
+
+// Additional checks for a MM container.
+bool check_mm(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t length = READ32(buffer + 2);
+ uint32_t fps = READ16(buffer + 8);
+ uint32_t width = READ16(buffer + 12);
+ uint32_t height = READ16(buffer + 14);
+ uint32_t 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.
+bool check_mov(const uint8_t buffer[], size_t buffer_size) {
+ size_t offset = 0;
+ while (offset + 16 < buffer_size) {
+ uint32_t 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
+ 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 true;
+}
+
+enum MPEGVersion {
+ Version25 = 0,
+ v_reserved,
+ Version2,
+ Version1
+};
+enum MPEGlayer {
+ l_reserved = 0,
+ Layer3,
+ Layer2,
+ Layer1
+};
+
+static int Sample_Rate_Table[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 Bit_Rate_Table_v1_l1[16] =
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 };
+static int Bit_Rate_Table_v1_l2[16] =
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 };
+static int Bit_Rate_Table_v1_l3[16] =
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
+static int Bit_Rate_Table_v2_l1[16] =
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 };
+static int Bit_Rate_Table_v2_l23[16] =
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 };
+
+bool valid_mpeg_audio_frame_header(uint32_t header, size_t *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 = Sample_Rate_Table[version][sampling_index];
+ int bitrate;
+ if (version == Version1) {
+ if (layer == Layer1)
+ bitrate = Bit_Rate_Table_v1_l1[bitrate_index];
+ else if (layer == Layer2)
+ bitrate = Bit_Rate_Table_v1_l2[bitrate_index];
+ else
+ bitrate = Bit_Rate_Table_v1_l3[bitrate_index];
+ }
+ else {
+ if (layer == Layer1)
+ bitrate = Bit_Rate_Table_v2_l1[bitrate_index];
+ else
+ bitrate = Bit_Rate_Table_v2_l23[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
+uint32_t get_mp3_header_size(const uint8_t buffer[]) {
+ uint32_t 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.
+bool check_mp3(const uint8_t buffer[], size_t buffer_size, bool seenHeader) {
+ size_t framesize;
+ size_t numSeen = 0;
+ size_t offset = 0;
+ if (seenHeader)
+ offset = get_mp3_header_size(buffer);
+ while (offset + 3 <= buffer_size &&
+ valid_mpeg_audio_frame_header(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.
+int64_t get_mpc8_header_size(const uint8_t * b, size_t * count) {
+ int64_t size = 0;
+ uint8_t c;
+ *count = 0;
+ do {
+ c = *b++;
+ if (++(*count) > 10)
+ return -1;
+ size = (size << 7) | (c & 0x7f);
+ } while (c & 0x80);
+ return size;
+}
+
+// Additional checks for a MPC8 container.
+bool check_mpc8(const uint8_t buffer[], size_t buffer_size) {
+ size_t offset = 4;
+ while (offset < buffer_size - 14) {
+ if (!isupper(buffer[offset]) || !isupper(buffer[offset + 1]))
+ return false;
+
+ size_t size_characters;
+ int64_t size = get_mpc8_header_size(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.
+bool check_msnwctcp(const uint8_t buffer[], size_t 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.
+ uint32_t width = READ16(buffer + 2);
+ uint32_t 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.
+bool check_mtv(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t bpp = READ16(buffer + 50);
+ if (bpp != 16)
+ return false;
+ uint32_t width = READ16(buffer + 52);
+ uint32_t height = READ16(buffer + 54);
+ uint32_t segment_size = READ16(buffer + 56);
+ return ((width != 0 && height != 0) || (segment_size != 0));
+}
+
+// Additional checks for a NC container.
+bool check_nc(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t 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.
+bool check_nsv(const uint8_t buffer[], size_t 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);
+ size_t offset = 24 + asize + vsize;
+ return (offset + 2 <= buffer_size && READ16(buffer + offset) == 0xbeef);
+}
+
+// Additional checks for an OMA container.
+bool check_oma(const uint8_t buffer[], size_t buffer_size) {
+ if (buffer[4] != 0)
+ return false;
+ uint32_t tag_len = get_mp3_header_size(buffer);
+ if (tag_len > buffer_size - 6)
+ 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.
+bool check_pjs(const uint8_t buffer[], size_t buffer_size) {
+ const char * ptr = reinterpret_cast<const char *>(buffer);
+ char c;
+ if (sscanf(ptr, "%*64d,%*64d,%c", &c) == 1) {
+ size_t offset = 0;
+ while (buffer[offset] != '"' && offset < buffer_size - 2)
+ ++offset;
+ ++offset;
+ while (buffer[offset] != '"' && offset < buffer_size) {
+ if (buffer[offset] == '\n' || buffer[offset] == '\r')
+ return false;
+ ++offset;
+ }
+ return true;
+ }
+ return false;
+}
+
+// Additional checks for a PVA container.
+bool check_pva(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t 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.
+bool check_tiertexseq(const uint8_t buffer[], size_t 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.
+bool check_tmv(const uint8_t buffer[], size_t buffer_size) {
+ return (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.
+bool check_vivo(const uint8_t buffer[], size_t buffer_size) {
+ if (buffer[0] != 0)
+ return false;
+ uint32_t length = buffer[1] & 0x7f;
+ size_t 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)
+ return false;
+ return (memcmp(buffer + offset,
+ VIVO_SIGNATURE,
+ strlen(VIVO_SIGNATURE)) == 0 &&
+ buffer[offset + 16] >= '0' && buffer[offset + 16] <= '2');
+
+}
+
+// Additional checks for a VMD container.
+bool check_vmd(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t width = READ16(buffer + 12);
+ uint32_t height = READ16(buffer + 14);
+ return (width > 0 && width <= 2048 && height > 0 && height <= 2048);
+}
+
+// Additional checks for a VPLAYER container.
+bool check_vplayer(const uint8_t buffer[], size_t buffer_size) {
+ const char* ptr = reinterpret_cast<const char*>(buffer);
+ char c;
+ return (sscanf(ptr, "%*d:%*d:%*d.%*d%c", &c) == 1 && strchr(": =", c));
+}
+
+// Additional checks for a VQF container.
+bool check_vqf(const uint8_t buffer[], size_t buffer_size) {
+ const char* ptr = reinterpret_cast<const char*>(buffer + 4);
+ return (memcmp(ptr, "97012000", 8) == 0 || memcmp(ptr, "00052200", 8) == 0);
+}
+
+// Read a Matroska TAG, updating offset to point past it.
+int get_vtag(const uint8_t buffer[], size_t * offset) {
+ // Size of the tag is determined the same way as VINT,
+ // but the bit is not removed. Maximum of 4 bytes.
+ int result = buffer[*offset];
+ if ((result & 0x80) != 0) {
+ // It is a one byte tag
+ ++*offset;
+ }
+ else if ((result & 0x40) != 0) {
+ // It is a 2 byte tag
+ result = READ16(buffer + *offset);
+ *offset += 2;
+ }
+ else if ((result & 0x20) != 0) {
+ // It is a 3 byte tag
+ result = READ24(buffer + *offset);
+ *offset += 3;
+ }
+ else {
+ // It is a 4 byte tag
+ result = READ32(buffer + *offset);
+ *offset += 4;
+ }
+ return result;
+}
+
+// Read a Matroska VINT, updating offset to point past it.
+int get_vint(const uint8_t buffer[], size_t * offset) {
+ // Length = 1 + [number_of_leading_zero_bits].
+ 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.
+ int result = buffer[(*offset)++] & (mask - 1);
+ while (--size > 0)
+ result = (result << 8) | buffer[(*offset)++];
+ return result;
+}
+
+// Additional checks for a WEBM container.
+bool check_webm(const uint8_t buffer[], size_t buffer_size) {
+ size_t offset = 4;
+ int header_size = get_vint(buffer, &offset);
+ size_t lastoffset = offset + header_size;
+ if (lastoffset > buffer_size)
+ return false;
+ while (offset < lastoffset) {
+ int tag = get_vtag(buffer, &offset);
+ int tagsize = get_vint(buffer, &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 && memcmp(buffer + offset, "webm", 4) == 0) ||
+ (tagsize >= 8 && memcmp(buffer + offset, "matroska", 8) == 0));
+ default:
+ // Unrecognized tag
+ return false;
+ }
+ }
+ return false;
+}
+
+// Additional checks for a WSAUD container.
+bool check_wsaud(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t 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.
+bool check_xa(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t channels = READ16(buffer + 10);
+ if (channels < 1 || channels > 8)
+ return false;
+ uint32_t srate = READ16(buffer + 12);
+ if (srate < 1 || srate > 192000)
+ return false;
+ uint32_t bits_per_sample = READ16(buffer + 10);
+ return (bits_per_sample >= 4 || bits_per_sample <= 32);
+}
+
+// Additional checks for a XBIN container.
+bool check_xbin(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t x = READ16(buffer + 5);
+ return (buffer[4] == 0x1a &&
+ x > 0 && x <= 160 &&
+ buffer[9] > 0 && buffer[9] <= 32);
+}
+
+// Additional checks for a XMV container.
+bool check_xmv(const uint8_t buffer[], size_t buffer_size) {
+ uint32_t version = READ16(buffer + 16);
+ return (READ32(buffer + 12) == TAG('x','o','b','X') &&
+ version > 0 && version <= 4);
+}
+
+// Additional checks for a YOP container.
+bool check_yop(const uint8_t buffer[], size_t buffer_size) {
+ return (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) < 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
+enum FFmpegContainerName LookupContainerByFirst4(const uint8 buffer[],
+ size_t buffer_size) {
+ // Minimum size that the code expects to exist without checking size.
+ // This includes the longest SIGNATURE checked.
+ if (buffer_size < 128)
+ return CONTAINER_UNKNOWN;
+
+ uint32_t first4 = READ32(buffer);
+ uint32_t second4 = READ32(buffer + 4);
+ uint32_t third4 = READ32(buffer + 8);
+ const char* ptr = reinterpret_cast<const char*>(buffer);
+ int frame;
+ uint32_t next;
+
+ switch (first4) {
+ case 0:
+ if (READ16(buffer + 4) == 0x1bc &&
+ READ32(buffer + 10) == 0 &&
+ READ16(buffer + 14) == 0xe1e2)
+ return CONTAINER_GXF;
+ if (check_mm(buffer, buffer_size))
+ return CONTAINER_MM;
+ if (second4 > 0 && second4 <= 1024 * 1024 && READ24(buffer + 8) == 1)
+ return CONTAINER_LMLM4;
+ if (check_tiertexseq(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 (second4 == 0x50 && READ32(buffer + 12) == 0x34)
+ return CONTAINER_MGSTS;
+ break;
+ case 0x16:
+ if (third4 == 0x1803ffff || third4 == 0x1003ffff)
+ return CONTAINER_TXD;
+ break;
+ case 0x1a5:
+ if (check_nc(buffer, buffer_size))
+ return CONTAINER_NC;
+ break;
+ case DNXHD_TAG: // TAG('\x00','\x00','\x02','\x80')
+ if (check_dnxhd(buffer, buffer_size))
+ return CONTAINER_DNXHD;
+ break;
+ case 0x800:
+ if (check_aea(buffer, buffer_size))
+ return CONTAINER_AEA;
+ break;
+ case 0x001800a0:
+ case 0x00180140:
+ if (check_msnwctcp(buffer, buffer_size))
+ return CONTAINER_MSNWCTCP;
+ break;
+ case SMJPEG_TAG: // TAG('\x00','\x0a','S','M')
+ if (memcmp(buffer, SMJPEG_SIGNATURE, sizeof(SMJPEG_SIGNATURE) - 1) == 0)
+ return CONTAINER_SMJPEG;
+ break;
+ case 0x1084ffff:
+ if (READ16(buffer + 4) == 0xffff)
+ return CONTAINER_ROQ;
+ break;
+ case IV8_TAG: // TAG('\x01','\x01','\x03','\xb8')
+ if (memcmp(buffer, IV8_SIGNATURE, strlen(IV8_SIGNATURE)) == 0)
+ return CONTAINER_IV8;
+ break;
+ case IDF_TAG: // TAG('\x04','\x31','\x2e','\x34')
+ if (memcmp(buffer, IDF_SIGNATURE, sizeof(IDF_SIGNATURE) - 1) == 0)
+ 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 (check_webm(buffer, buffer_size))
+ return CONTAINER_WEBM;
+ break;
+ case TAG('.','s','n','d'):
+ return CONTAINER_AU;
+ case ASF_TAG: // TAG('\x30','\x26','\xB2','\x75')
+ if (memcmp(buffer, ASF_SIGNATURE, sizeof(ASF_SIGNATURE) - 1) == 0)
+ return CONTAINER_ASF;
+ break;
+ case TAG(' ','p','a','f'):
+ if (second4 == 0 && third4 == 0 &&
+ READ32(buffer + 12) != 0 &&
+ READ32(buffer + 20) != 0)
+ return CONTAINER_EPAF;
+ break;
+ case AMR_TAG: // TAG('#','!','A','M'):
+ if (memcmp(buffer, AMR_SIGNATURE, strlen(AMR_SIGNATURE)) == 0)
+ return CONTAINER_AMR;
+ break;
+ case ILBC_TAG: // TAG('#','!','i','L')
+ if (memcmp(buffer, ILBC_SIGNATURE, strlen(ILBC_SIGNATURE)) == 0)
+ return CONTAINER_ILBC;
+ break;
+ case HLS_TAG: // TAG('#','E','X','T')
+ if (check_hls(buffer, buffer_size))
+ return CONTAINER_HLS;
+ break;
+ case VOBSUB_TAG: // TAG('#',' ','V','o')
+ if (memcmp(buffer, VOBSUB_SIGNATURE, strlen(VOBSUB_SIGNATURE)) == 0)
+ return CONTAINER_VOBSUB;
+ break;
+ case AQTITLE_TAG: // TAG('-','-','>','>')
+ if (sscanf(ptr, AQTITLE_FORMAT, &frame) == 1)
+ return CONTAINER_AQTITLE;
+ 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 RPL_TAG: // TAG('A','R','M','o')
+ if (memcmp(buffer, RPL_SIGNATURE, strlen(RPL_SIGNATURE)) == 0)
+ return CONTAINER_RPL;
+ break;
+ case TAG('B','B','C','D'):
+ return CONTAINER_DIRAC;
+ case TAG('B','F','&','I'):
+ return CONTAINER_BFI;
+ case FFMETADATA_TAG: // TAG(';','F','F','M')
+ if (memcmp(buffer,
+ FFMETADATA_SIGNATURE,
+ strlen(FFMETADATA_SIGNATURE)) == 0)
+ 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 (check_bink(buffer, buffer_size))
+ return CONTAINER_BINK;
+ break;
+ case TAG('c','a','f','f'):
+ if (READ16(buffer + 4) == 1)
+ return CONTAINER_CAF;
+ break;
+ case VOC_TAG: // TAG('C','r','e','a')
+ if (memcmp(buffer, VOC_SIGNATURE, strlen(VOC_SIGNATURE)) == 0)
+ return CONTAINER_VOC;
+ break;
+ case APC_TAG: // TAG('C','R','Y','O'):
+ if (memcmp(buffer, APC_SIGNATURE, strlen(APC_SIGNATURE)) == 0)
+ return CONTAINER_APC;
+ break;
+ case TAG('D','E','X','A'):
+ if (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 (check_oma(buffer, buffer_size))
+ return CONTAINER_OMA;
+ break;
+ case TAG('f','a','p',' '):
+ if (second4 == 0 && third4 == 1 &&
+ READ32(buffer + 12) != 0 &&
+ READ32(buffer + 20) != 0)
+ return CONTAINER_EPAF;
+ break;
+ case CONCAT_TAG: // TAG('f','f','c','o')
+ if (memcmp(buffer, CONCAT_SIGNATURE, strlen(CONCAT_SIGNATURE)) == 0)
+ 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 ISS_TAG: // TAG('I','M','A','_')
+ if (memcmp(buffer, ISS_SIGNATURE, strlen(ISS_SIGNATURE)) == 0)
+ return CONTAINER_ISS;
+ break;
+ case TAG('k','!','\x00','\x40'):
+ case TAG('k','!','\x00','\x50'):
+ return CONTAINER_BIT;
+ case LXF_TAG: // TAG('L','E','I','T')
+ if (memcmp(buffer, LXF_SIGNATURE, sizeof(LXF_SIGNATURE) - 1) == 0)
+ return CONTAINER_LXF;
+ break;
+ case TAG('L','P','F',' '):
+ if (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 (check_mpc8(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 LIBNUT_TAG: // TAG('n','u','t','/')
+ if (memcmp(buffer, LIBNUT_SIGNATURE, strlen(LIBNUT_SIGNATURE)) == 0)
+ return CONTAINER_LIBNUT;
+ break;
+ case NUV1_TAG: // TAG('N','u','p','p')
+ if (memcmp(buffer, NUV1_SIGNATURE, strlen(NUV1_SIGNATURE)) == 0)
+ return CONTAINER_NUV;
+ break;
+ case NUV2_TAG: // TAG('M','y','t','h')
+ if (memcmp(buffer, NUV2_SIGNATURE, strlen(NUV2_SIGNATURE)) == 0)
+ 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 PAF_TAG: // TAG('P','a','c','k')
+ if (memcmp(buffer, PAF_SIGNATURE, strlen(PAF_SIGNATURE)) == 0)
+ 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 (READ32(buffer + 12) == TAG('d','s','6','4'))
+ return CONTAINER_WAV;
+ break;
+ case TAG('r','i','f','f'):
+ if (memcmp(buffer, W64_SIGNATURE, sizeof(W64_SIGNATURE) - 1) == 0 &&
+ memcmp(buffer + 24, W64_SIGNATURE2, sizeof(W64_SIGNATURE2) - 1) == 0)
+ 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 (READ32(buffer + 12) == TAG('f','m','t',' '))
+ return CONTAINER_QCP;
+ break;
+ case TAG('W','A','V','E'):
+ // possibly ACT or WAV
+ return (check_act(buffer, buffer_size)) ?
+ CONTAINER_ACT : CONTAINER_WAV;
+ case TAG('X','W','M','A'):
+ return CONTAINER_XWMA;
+ }
+ break;
+ case TAG('R','S','T','M'):
+ next = READ16(buffer + 4);
+ if (next == 0xfffe || next == 0xfeff)
+ 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 (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 (check_tmv(buffer, buffer_size))
+ return CONTAINER_TMV;
+ break;
+ case TAG('T','T','A','1'):
+ return CONTAINER_TTA;
+ case TAG('\x55','\xaa','\x00','\x00'):
+ if (check_dsicin(buffer, buffer_size))
+ return CONTAINER_DSICIN;
+ break;
+ case TAG('T','W','I','N'):
+ if (check_vqf(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 (check_xa(buffer, buffer_size))
+ return CONTAINER_XA;
+ break;
+ case TAG('X','B','I','N'):
+ if (check_xbin(buffer, buffer_size))
+ return CONTAINER_XBIN;
+ break;
+ case YUV4_TAG: // TAG('Y','U','V','4')
+ if (memcmp(buffer, YUV4_SIGNATURE, strlen(YUV4_SIGNATURE)) == 0)
+ return CONTAINER_YUV4MPEGPIPE;
+ break;
+ case WEBVTT_TAG: // TAG('W','E','B','V')
+ if (memcmp(buffer, WEBVTT_SIGNATURE, strlen(WEBVTT_SIGNATURE)) == 0)
+ return CONTAINER_WEBVTT;
+ break;
+ case WEBVTT_BOM_TAG: // TAG('\xef','\xbb','\xbf','W')
+ if (memcmp(buffer,
+ WEBVTT_BOM_SIGNATURE,
+ strlen(WEBVTT_BOM_SIGNATURE)) == 0)
+ return CONTAINER_WEBVTT;
+ break;
+ case ASS_TAG: // TAG('[','S','c','r')
+ if (memcmp(buffer, ASS_SIGNATURE, strlen(ASS_SIGNATURE)) == 0)
+ return CONTAINER_ASS;
+ break;
+ case ASS_BOM_TAG: // TAG('\xef','\xbb','\xbf','[')
+ if (memcmp(buffer, ASS_BOM_SIGNATURE, strlen(ASS_BOM_SIGNATURE)) == 0)
+ return CONTAINER_ASS;
+ break;
+ case REALTEXT_TAG: // TAG('<','w','i','n')
+ if (memcmp(ptr, REALTEXT_SIGNATURE, strlen(REALTEXT_SIGNATURE)) == 0)
+ return CONTAINER_REALTEXT;
+ break;
+ case SAMI_TAG: // TAG('<','S','A','M')
+ if (memcmp(ptr, SAMI_SIGNATURE, strlen(SAMI_SIGNATURE)) == 0)
+ return CONTAINER_SAMI;
+ break;
+ case REALTEXT_BOM_TAG: // TAG('\xef','\xbb','\xbf','<')
+ // also SAMI_BOM_TAG
+ if (memcmp(ptr,
+ REALTEXT_BOM_SIGNATURE,
+ strlen(REALTEXT_BOM_SIGNATURE)) == 0)
+ return CONTAINER_REALTEXT;
+ if (memcmp(ptr, SAMI_BOM_SIGNATURE, strlen(SAMI_BOM_SIGNATURE)) == 0)
+ return CONTAINER_SAMI;
+ break;
+ case WTV_TAG: // TAG('\xb7','\xd8','\x00','\x20')
+ if (memcmp(ptr, WTV_SIGNATURE, sizeof(WTV_SIGNATURE) - 1) == 0)
+ 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 (check_mtv(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 (check_pva(buffer, buffer_size))
+ return CONTAINER_PVA;
+ break;
+ case TAG('I','D','3',0):
+ if (check_mp3(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 (check_vmd(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 (memcmp(buffer + 4, JV_SIGNATURE, strlen(JV_SIGNATURE)) == 0)
+ return CONTAINER_JV;
+ break;
+ case 0x0b770000:
+ if (check_ac3(buffer, buffer_size))
+ return CONTAINER_AC3;
+ if (check_eac3(buffer, buffer_size))
+ return CONTAINER_EAC3;
+ break;
+ case TAG('Y','O',0,0):
+ if (check_yop(buffer, buffer_size))
+ return CONTAINER_JV;
+ break;
+ }
+
+ // Now try the second set of 4 characters.
+ switch (second4) {
+ case 4:
+ if (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 (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 (check_c93(buffer, buffer_size))
+ return CONTAINER_C93;
+ if (check_cdxl(buffer, buffer_size))
+ return CONTAINER_CDXL;
+ if (check_idcin(buffer, buffer_size))
+ return CONTAINER_IDCIN;
+ if (check_mov(buffer, buffer_size))
+ return CONTAINER_MOV;
+ if (check_vivo(buffer, buffer_size))
+ return CONTAINER_VIVO;
+ if (check_wsaud(buffer, buffer_size))
+ return CONTAINER_WSAUD;
+ if (check_xmv(buffer, buffer_size))
+ return CONTAINER_XMV;
+ // Check if the file is in MP3 format without the header
+ if (check_mp3(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 (check_ac3(buffer + offset, remaining))
+ return CONTAINER_AC3;
+ if (check_eac3(buffer + offset, remaining))
+ return CONTAINER_EAC3;
+ }
+ else if (check_mp3(buffer, buffer_size, false))
+ return CONTAINER_MP3;
+ }
+ }
+
+ return CONTAINER_UNKNOWN;
+}
+
+static const char ipm_string[] = "Interplay MVE File\x1A\0\x1A";
+static const char mxf_string[] =
+ "\x06\x0e\x2b\x34\x02\x05\x01\x01\x0d\x01\x02\x01\x01\x02";
+static const char sub1_string[] = "******** 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
+enum FFmpegContainerName LookupContainerByStringScan(const uint8 buffer[],
+ size_t buffer_size) {
+ size_t offset = 0;
+ size_t remaining = buffer_size;
+ while (offset + 4 < buffer_size) {
+ if (buffer[offset] == ipm_string[0] &&
+ remaining >= sizeof(ipm_string) - 1 &&
+ memcmp(buffer + offset, ipm_string, sizeof(ipm_string) - 1) == 0)
+ return CONTAINER_IPMOVIE;
+
+ if (buffer[offset] == mxf_string[0] &&
+ remaining >= sizeof(mxf_string) - 1 &&
+ memcmp(buffer + offset, mxf_string, sizeof(mxf_string) - 1) == 0)
+ return CONTAINER_MXF;
+
+ if (buffer[offset] == sub1_string[0] &&
+ remaining >= sizeof(sub1_string) - 1 &&
+ memcmp(buffer + offset, sub1_string, sizeof(sub1_string) - 1) == 0)
+ return CONTAINER_SUBVIEWER1;
+
+ // Additional checks for nsv and msnwctcp.
+ uint32_t tag = READ32(buffer + offset);
+ switch (tag) {
+ case TAG('N','S','V','s'):
+ if (check_nsv(buffer + offset, remaining))
+ return CONTAINER_NSV;
+ break;
+ case 0x001800a0:
+ case 0x00180140:
+ if (check_msnwctcp(buffer + offset, remaining))
+ return CONTAINER_MSNWCTCP;
+ break;
+ }
+
+ // Not found, move forward to next character.
+ ++offset;
+ --remaining;
+ }
+
+ // didn't find a string match for any of the formats
+ return CONTAINER_UNKNOWN;
+}
+
+// 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
+enum FFmpegContainerName LookupContainerByStringLine(const uint8 buffer[],
+ size_t buffer_size) {
+ size_t offset = memcmp(buffer, BOM, 3) == 0 ? 3 : 0;
+ size_t lines = 0;
+ char c;
+ int n;
+
+ // PJS is a scan from the beginning only.
+ if (check_pjs(buffer, buffer_size))
+ return CONTAINER_PJS;
+
+ // Same for VPLAYER.
+ if (check_vplayer(buffer, buffer_size))
+ return CONTAINER_VPLAYER;
+
+ // Assume that the longest scanf will be 100 characters.
+ while (offset + 100 < buffer_size) {
+ const char* ptr = reinterpret_cast<const char*>(buffer + offset);
+
+ if (*ptr == '{' && lines < 3) {
+ if (sscanf(ptr, "{%*d}{}%c", &c) == 1 ||
+ sscanf(ptr, "{%*d}{%*d}%c", &c) == 1 ||
+ sscanf(ptr, "{DEFAULT}{}%c", &c) == 1)
+ return CONTAINER_MICRODVD;
+ }
+
+ if (*ptr == '[' && lines < 3) {
+ if (sscanf(ptr, "[%*64d][%*64d]%c", &c) == 1 ||
+ sscanf(ptr, "[%*64d][]%c", &c) == 1)
+ return CONTAINER_MPL2;
+ }
+
+ if (*ptr == 'F') {
+ if (memcmp(ptr, "FORMAT=TIME", 11) == 0 ||
+ sscanf(ptr, "FORMAT=%d", &n) == 1)
+ return CONTAINER_MPSUB;
+ }
+
+ if (*ptr == 'c') {
+ if (memcmp(ptr, "c=IN IP", 7) == 0)
+ return CONTAINER_SDP;
+ }
+
+ if (isdigit(*ptr) && lines < 3) {
+ if (sscanf(ptr,
+ "%*d:%*2d:%*2d%*1[,.]%*3d --> %*d:%*2d:%*2d%*1[,.]%3d",
+ &n) == 1)
+ return CONTAINER_SRT;
+ }
+
+ if (isdigit(*ptr) && lines < 1) {
+ if (sscanf(ptr, "%*u:%*u:%*u.%*u,%*u:%*u:%*u.%*u%c", &c) == 1 ||
+ memcmp(ptr, "[INFORMATION]", 13) == 0)
+ 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;
+}
+
+// Attempt to determine the container name from the buffer provided.
+enum FFmpegContainerName ContainerNames::LookupContainer(const uint8 buffer[],
+ size_t buffer_size) {
+ // TODO(jrumell): The following formats are not scanned for
+ // aac, cavsvideo, dts, dv, h261, h263, h264, jacosub, loas,
+ // m4v, 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 ContainerNames::LogContainer(const char* container_name) {
+ FFmpegContainerName container = LookupContainer(container_name);
+ // Even container IDs are the ones detected by FFmpeg.
+ UMA_HISTOGRAM_ENUMERATION(
+ "Media.Container", container * 2, CONTAINER_MAX * 2);
Ilya Sherman 2013/04/29 23:48:06 Can you use a sparse histogram for this histogram?
jrummell 2013/04/30 21:36:50 Done.
+}
+
+// Log the container by examining the first part of the stream.
+void ContainerNames::LogContainer(const uint8 buffer[], size_t buffer_size) {
+ FFmpegContainerName container = LookupContainer(buffer, buffer_size);
+ // Odd container IDs are guessed at by this code.
+ UMA_HISTOGRAM_ENUMERATION(
+ "Media.Container", container * 2 + 1, CONTAINER_MAX * 2);
Ilya Sherman 2013/04/29 23:48:06 Please define a helper function along the lines of
jrummell 2013/04/30 21:36:50 Great idea. Done.
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698