toolkit/components/mediasniffer/nsMediaSniffer.cpp

branch
TOR_BUG_9701
changeset 14
925c144e1f1f
equal deleted inserted replaced
-1:000000000000 0:a0eea0362e69
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 tw=80 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "nsMediaSniffer.h"
8 #include "nsIHttpChannel.h"
9 #include "nsString.h"
10 #include "nsMimeTypes.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/ModuleUtils.h"
13 #include "mp3sniff.h"
14 #ifdef MOZ_WEBM
15 #include "nestegg/nestegg.h"
16 #endif
17
18 #include "nsIClassInfoImpl.h"
19 #include <algorithm>
20
21 // The minimum number of bytes that are needed to attempt to sniff an mp4 file.
22 static const unsigned MP4_MIN_BYTES_COUNT = 12;
23 // The maximum number of bytes to consider when attempting to sniff a file.
24 static const uint32_t MAX_BYTES_SNIFFED = 512;
25 // The maximum number of bytes to consider when attempting to sniff for a mp3
26 // bitstream.
27 // This is 320kbps * 144 / 32kHz + 1 padding byte + 4 bytes of capture pattern.
28 static const uint32_t MAX_BYTES_SNIFFED_MP3 = 320 * 144 / 32 + 1 + 4;
29
30 NS_IMPL_ISUPPORTS(nsMediaSniffer, nsIContentSniffer)
31
32 nsMediaSniffer::nsMediaSnifferEntry nsMediaSniffer::sSnifferEntries[] = {
33 // The string OggS, followed by the null byte.
34 PATTERN_ENTRY("\xFF\xFF\xFF\xFF\xFF", "OggS", APPLICATION_OGG),
35 // The string RIFF, followed by four bytes, followed by the string WAVE
36 PATTERN_ENTRY("\xFF\xFF\xFF\xFF\x00\x00\x00\x00\xFF\xFF\xFF\xFF", "RIFF\x00\x00\x00\x00WAVE", AUDIO_WAV),
37 // mp3 with ID3 tags, the string "ID3".
38 PATTERN_ENTRY("\xFF\xFF\xFF", "ID3", AUDIO_MP3)
39 };
40
41 // This function implements mp4 sniffing algorithm, described at
42 // http://mimesniff.spec.whatwg.org/#signature-for-mp4
43 static bool MatchesMP4(const uint8_t* aData, const uint32_t aLength)
44 {
45 if (aLength <= MP4_MIN_BYTES_COUNT) {
46 return false;
47 }
48 // Conversion from big endian to host byte order.
49 uint32_t boxSize = (uint32_t)(aData[3] | aData[2] << 8 | aData[1] << 16 | aData[0] << 24);
50
51 // Boxsize should be evenly divisible by 4.
52 if (boxSize % 4 || aLength < boxSize) {
53 return false;
54 }
55 // The string "ftyp".
56 if (aData[4] != 0x66 ||
57 aData[5] != 0x74 ||
58 aData[6] != 0x79 ||
59 aData[7] != 0x70) {
60 return false;
61 }
62 for (uint32_t i = 2; i <= boxSize / 4 - 1 ; i++) {
63 if (i == 3) {
64 continue;
65 }
66 // The string "mp42" or "mp41".
67 if (aData[4*i] == 0x6D &&
68 aData[4*i+1] == 0x70 &&
69 aData[4*i+2] == 0x34) {
70 return true;
71 }
72 // The string "isom" or "iso2".
73 if (aData[4*i] == 0x69 &&
74 aData[4*i+1] == 0x73 &&
75 aData[4*i+2] == 0x6F &&
76 (aData[4*i+3] == 0x6D || aData[4*i+3] == 0x32)) {
77 return true;
78 }
79 }
80 return false;
81 }
82
83 static bool MatchesWebM(const uint8_t* aData, const uint32_t aLength)
84 {
85 #ifdef MOZ_WEBM
86 return nestegg_sniff((uint8_t*)aData, aLength) ? true : false;
87 #else
88 return false;
89 #endif
90 }
91
92 // This function implements mp3 sniffing based on parsing
93 // packet headers and looking for expected boundaries.
94 static bool MatchesMP3(const uint8_t* aData, const uint32_t aLength)
95 {
96 return mp3_sniff(aData, (long)aLength);
97 }
98
99 NS_IMETHODIMP
100 nsMediaSniffer::GetMIMETypeFromContent(nsIRequest* aRequest,
101 const uint8_t* aData,
102 const uint32_t aLength,
103 nsACString& aSniffedType)
104 {
105 // For media, we want to sniff only if the Content-Type is unknown, or if it
106 // is application/octet-stream.
107 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
108 if (channel) {
109 nsAutoCString contentType;
110 nsresult rv = channel->GetContentType(contentType);
111 NS_ENSURE_SUCCESS(rv, rv);
112 if (!contentType.IsEmpty() &&
113 !contentType.EqualsLiteral(APPLICATION_OCTET_STREAM) &&
114 !contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
115 return NS_ERROR_NOT_AVAILABLE;
116 }
117 }
118
119 const uint32_t clampedLength = std::min(aLength, MAX_BYTES_SNIFFED);
120
121 for (uint32_t i = 0; i < mozilla::ArrayLength(sSnifferEntries); ++i) {
122 const nsMediaSnifferEntry& currentEntry = sSnifferEntries[i];
123 if (clampedLength < currentEntry.mLength || currentEntry.mLength == 0) {
124 continue;
125 }
126 bool matched = true;
127 for (uint32_t j = 0; j < currentEntry.mLength; ++j) {
128 if ((currentEntry.mMask[j] & aData[j]) != currentEntry.mPattern[j]) {
129 matched = false;
130 break;
131 }
132 }
133 if (matched) {
134 aSniffedType.AssignASCII(currentEntry.mContentType);
135 return NS_OK;
136 }
137 }
138
139 if (MatchesMP4(aData, clampedLength)) {
140 aSniffedType.AssignLiteral(VIDEO_MP4);
141 return NS_OK;
142 }
143
144 if (MatchesWebM(aData, clampedLength)) {
145 aSniffedType.AssignLiteral(VIDEO_WEBM);
146 return NS_OK;
147 }
148
149 // Bug 950023: 512 bytes are often not enough to sniff for mp3.
150 if (MatchesMP3(aData, std::min(aLength, MAX_BYTES_SNIFFED_MP3))) {
151 aSniffedType.AssignLiteral(AUDIO_MP3);
152 return NS_OK;
153 }
154
155 // Could not sniff the media type, we are required to set it to
156 // application/octet-stream.
157 aSniffedType.AssignLiteral(APPLICATION_OCTET_STREAM);
158 return NS_ERROR_NOT_AVAILABLE;
159 }

mercurial