content/media/ogg/OpusParser.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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 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 <string.h>
michael@0 8
michael@0 9 #include "mozilla/DebugOnly.h"
michael@0 10 #include "mozilla/Endian.h"
michael@0 11 #include <stdint.h>
michael@0 12
michael@0 13 #include "OpusParser.h"
michael@0 14
michael@0 15 #include "nsDebug.h"
michael@0 16 #include "MediaDecoderReader.h"
michael@0 17 #include "VideoUtils.h"
michael@0 18 #include <algorithm>
michael@0 19
michael@0 20 #include "opus/opus.h"
michael@0 21 extern "C" {
michael@0 22 #include "opus/opus_multistream.h"
michael@0 23 }
michael@0 24
michael@0 25 namespace mozilla {
michael@0 26
michael@0 27 #ifdef PR_LOGGING
michael@0 28 extern PRLogModuleInfo* gMediaDecoderLog;
michael@0 29 #define OPUS_LOG(type, msg) PR_LOG(gMediaDecoderLog, type, msg)
michael@0 30 #else
michael@0 31 #define OPUS_LOG(type, msg)
michael@0 32 #endif
michael@0 33
michael@0 34 OpusParser::OpusParser():
michael@0 35 mRate(0),
michael@0 36 mNominalRate(0),
michael@0 37 mChannels(0),
michael@0 38 mPreSkip(0),
michael@0 39 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
michael@0 40 mGain(1.0f),
michael@0 41 #else
michael@0 42 mGain_Q16(65536),
michael@0 43 #endif
michael@0 44 mChannelMapping(0),
michael@0 45 mStreams(0),
michael@0 46 mCoupledStreams(0)
michael@0 47 { }
michael@0 48
michael@0 49 bool OpusParser::DecodeHeader(unsigned char* aData, size_t aLength)
michael@0 50 {
michael@0 51 if (aLength < 19 || memcmp(aData, "OpusHead", 8)) {
michael@0 52 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: unrecognized header"));
michael@0 53 return false;
michael@0 54 }
michael@0 55
michael@0 56 mRate = 48000; // The Opus decoder runs at 48 kHz regardless.
michael@0 57
michael@0 58 int version = aData[8];
michael@0 59 // Accept file format versions 0.x.
michael@0 60 if ((version & 0xf0) != 0) {
michael@0 61 OPUS_LOG(PR_LOG_DEBUG, ("Rejecting unknown Opus file version %d", version));
michael@0 62 return false;
michael@0 63 }
michael@0 64
michael@0 65 mChannels = aData[9];
michael@0 66 if (mChannels<1) {
michael@0 67 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: Number of channels %d", mChannels));
michael@0 68 return false;
michael@0 69 }
michael@0 70
michael@0 71 mPreSkip = LittleEndian::readUint16(aData + 10);
michael@0 72 mNominalRate = LittleEndian::readUint32(aData + 12);
michael@0 73 double gain_dB = LittleEndian::readInt16(aData + 16) / 256.0;
michael@0 74 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
michael@0 75 mGain = static_cast<float>(pow(10,0.05*gain_dB));
michael@0 76 #else
michael@0 77 mGain_Q16 = static_cast<int32_t>(std::min(65536*pow(10,0.05*gain_dB)+0.5,
michael@0 78 static_cast<double>(INT32_MAX)));
michael@0 79 #endif
michael@0 80 mChannelMapping = aData[18];
michael@0 81
michael@0 82 if (mChannelMapping == 0) {
michael@0 83 // Mapping family 0 only allows two channels
michael@0 84 if (mChannels>2) {
michael@0 85 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: too many channels (%d) for"
michael@0 86 " mapping family 0.", mChannels));
michael@0 87 return false;
michael@0 88 }
michael@0 89 mStreams = 1;
michael@0 90 mCoupledStreams = mChannels - 1;
michael@0 91 mMappingTable[0] = 0;
michael@0 92 mMappingTable[1] = 1;
michael@0 93 } else if (mChannelMapping == 1) {
michael@0 94 // Currently only up to 8 channels are defined for mapping family 1
michael@0 95 if (mChannels>8) {
michael@0 96 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: too many channels (%d) for"
michael@0 97 " mapping family 1.", mChannels));
michael@0 98 return false;
michael@0 99 }
michael@0 100 if (aLength>static_cast<unsigned>(20+mChannels)) {
michael@0 101 mStreams = aData[19];
michael@0 102 mCoupledStreams = aData[20];
michael@0 103 int i;
michael@0 104 for (i=0; i<mChannels; i++)
michael@0 105 mMappingTable[i] = aData[21+i];
michael@0 106 } else {
michael@0 107 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: channel mapping %d,"
michael@0 108 " but no channel mapping table", mChannelMapping));
michael@0 109 return false;
michael@0 110 }
michael@0 111 } else {
michael@0 112 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: unsupported channel mapping "
michael@0 113 "family %d", mChannelMapping));
michael@0 114 return false;
michael@0 115 }
michael@0 116 if (mStreams < 1) {
michael@0 117 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: no streams"));
michael@0 118 return false;
michael@0 119 }
michael@0 120 if (mCoupledStreams > mStreams) {
michael@0 121 OPUS_LOG(PR_LOG_DEBUG, ("Invalid Opus file: more coupled streams (%d) than "
michael@0 122 "total streams (%d)", mCoupledStreams, mStreams));
michael@0 123 return false;
michael@0 124 }
michael@0 125
michael@0 126 #ifdef DEBUG
michael@0 127 OPUS_LOG(PR_LOG_DEBUG, ("Opus stream header:"));
michael@0 128 OPUS_LOG(PR_LOG_DEBUG, (" channels: %d", mChannels));
michael@0 129 OPUS_LOG(PR_LOG_DEBUG, (" preskip: %d", mPreSkip));
michael@0 130 OPUS_LOG(PR_LOG_DEBUG, (" original: %d Hz", mNominalRate));
michael@0 131 OPUS_LOG(PR_LOG_DEBUG, (" gain: %.2f dB", gain_dB));
michael@0 132 OPUS_LOG(PR_LOG_DEBUG, ("Channel Mapping:"));
michael@0 133 OPUS_LOG(PR_LOG_DEBUG, (" family: %d", mChannelMapping));
michael@0 134 OPUS_LOG(PR_LOG_DEBUG, (" streams: %d", mStreams));
michael@0 135 #endif
michael@0 136 return true;
michael@0 137 }
michael@0 138
michael@0 139 bool OpusParser::DecodeTags(unsigned char* aData, size_t aLength)
michael@0 140 {
michael@0 141 if (aLength < 16 || memcmp(aData, "OpusTags", 8))
michael@0 142 return false;
michael@0 143
michael@0 144 // Copy out the raw comment lines, but only do basic validation
michael@0 145 // checks against the string packing: too little data, too many
michael@0 146 // comments, or comments that are too long. Rejecting these cases
michael@0 147 // helps reduce the propagation of broken files.
michael@0 148 // We do not ensure they are valid UTF-8 here, nor do we validate
michael@0 149 // the required ASCII_TAG=value format of the user comments.
michael@0 150 const unsigned char* buf = aData + 8;
michael@0 151 uint32_t bytes = aLength - 8;
michael@0 152 uint32_t len;
michael@0 153 // Read the vendor string.
michael@0 154 len = LittleEndian::readUint32(buf);
michael@0 155 buf += 4;
michael@0 156 bytes -= 4;
michael@0 157 if (len > bytes)
michael@0 158 return false;
michael@0 159 mVendorString = nsCString(reinterpret_cast<const char*>(buf), len);
michael@0 160 buf += len;
michael@0 161 bytes -= len;
michael@0 162 // Read the user comments.
michael@0 163 if (bytes < 4)
michael@0 164 return false;
michael@0 165 uint32_t ncomments = LittleEndian::readUint32(buf);
michael@0 166 buf += 4;
michael@0 167 bytes -= 4;
michael@0 168 // If there are so many comments even their length fields
michael@0 169 // won't fit in the packet, stop reading now.
michael@0 170 if (ncomments > (bytes>>2))
michael@0 171 return false;
michael@0 172 uint32_t i;
michael@0 173 for (i = 0; i < ncomments; i++) {
michael@0 174 if (bytes < 4)
michael@0 175 return false;
michael@0 176 len = LittleEndian::readUint32(buf);
michael@0 177 buf += 4;
michael@0 178 bytes -= 4;
michael@0 179 if (len > bytes)
michael@0 180 return false;
michael@0 181 mTags.AppendElement(nsCString(reinterpret_cast<const char*>(buf), len));
michael@0 182 buf += len;
michael@0 183 bytes -= len;
michael@0 184 }
michael@0 185
michael@0 186 #ifdef DEBUG
michael@0 187 OPUS_LOG(PR_LOG_DEBUG, ("Opus metadata header:"));
michael@0 188 OPUS_LOG(PR_LOG_DEBUG, (" vendor: %s", mVendorString.get()));
michael@0 189 for (uint32_t i = 0; i < mTags.Length(); i++) {
michael@0 190 OPUS_LOG(PR_LOG_DEBUG, (" %s", mTags[i].get()));
michael@0 191 }
michael@0 192 #endif
michael@0 193 return true;
michael@0 194 }
michael@0 195
michael@0 196 } // namespace mozilla
michael@0 197

mercurial