1package magic
2
3import (
4	"bytes"
5)
6
7var (
8	// Flv matches a Flash video file.
9	Flv = prefix([]byte("\x46\x4C\x56\x01"))
10	// Asf matches an Advanced Systems Format file.
11	Asf = prefix([]byte{
12		0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11,
13		0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C,
14	})
15	// Rmvb matches a RealMedia Variable Bitrate file.
16	Rmvb = prefix([]byte{0x2E, 0x52, 0x4D, 0x46})
17)
18
19// WebM matches a WebM file.
20func WebM(raw []byte, limit uint32) bool {
21	return isMatroskaFileTypeMatched(raw, "webm")
22}
23
24// Mkv matches a mkv file.
25func Mkv(raw []byte, limit uint32) bool {
26	return isMatroskaFileTypeMatched(raw, "matroska")
27}
28
29// isMatroskaFileTypeMatched is used for webm and mkv file matching.
30// It checks for .Eߣ sequence. If the sequence is found,
31// then it means it is Matroska media container, including WebM.
32// Then it verifies which of the file type it is representing by matching the
33// file specific string.
34func isMatroskaFileTypeMatched(in []byte, flType string) bool {
35	if bytes.HasPrefix(in, []byte("\x1A\x45\xDF\xA3")) {
36		return isFileTypeNamePresent(in, flType)
37	}
38	return false
39}
40
41// isFileTypeNamePresent accepts the matroska input data stream and searches
42// for the given file type in the stream. Return whether a match is found.
43// The logic of search is: find first instance of \x42\x82 and then
44// search for given string after n bytes of above instance.
45func isFileTypeNamePresent(in []byte, flType string) bool {
46	ind, maxInd, lenIn := 0, 4096, len(in)
47	if lenIn < maxInd { // restricting length to 4096
48		maxInd = lenIn
49	}
50	ind = bytes.Index(in[:maxInd], []byte("\x42\x82"))
51	if ind > 0 && lenIn > ind+2 {
52		ind += 2
53
54		// filetype name will be present exactly
55		// n bytes after the match of the two bytes "\x42\x82"
56		n := vintWidth(int(in[ind]))
57		if lenIn > ind+n {
58			return bytes.HasPrefix(in[ind+n:], []byte(flType))
59		}
60	}
61	return false
62}
63
64// vintWidth parses the variable-integer width in matroska containers
65func vintWidth(v int) int {
66	mask, max, num := 128, 8, 1
67	for num < max && v&mask == 0 {
68		mask = mask >> 1
69		num++
70	}
71	return num
72}
73
74// Mpeg matches a Moving Picture Experts Group file.
75func Mpeg(raw []byte, limit uint32) bool {
76	return len(raw) > 3 && bytes.HasPrefix(raw, []byte{0x00, 0x00, 0x01}) &&
77		raw[3] >= 0xB0 && raw[3] <= 0xBF
78}
79
80// Avi matches an Audio Video Interleaved file.
81func Avi(raw []byte, limit uint32) bool {
82	return len(raw) > 16 &&
83		bytes.Equal(raw[:4], []byte("RIFF")) &&
84		bytes.Equal(raw[8:16], []byte("AVI LIST"))
85}
86