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.

     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/. */
     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
    18 #include "nsIClassInfoImpl.h"
    19 #include <algorithm>
    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;
    30 NS_IMPL_ISUPPORTS(nsMediaSniffer, nsIContentSniffer)
    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 };
    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);
    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 }
    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 }
    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 }
    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   }
   119   const uint32_t clampedLength = std::min(aLength, MAX_BYTES_SNIFFED);
   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   }
   139   if (MatchesMP4(aData, clampedLength)) {
   140     aSniffedType.AssignLiteral(VIDEO_MP4);
   141     return NS_OK;
   142   }
   144   if (MatchesWebM(aData, clampedLength)) {
   145     aSniffedType.AssignLiteral(VIDEO_WEBM);
   146     return NS_OK;
   147   }
   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   }
   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