toolkit/components/mediasniffer/nsMediaSniffer.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

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

mercurial