Bug 751219 - Support header gain in Opus files, r=kinetik
authorTimothy B. Terriberry <tterribe@vt.edu>
Thu, 31 May 2012 11:13:17 -0700
changeset 95368 359f1096bc7cb983ebd59144ba6955160937bc9e
parent 95367 010313752c64f55a1978cc32d7d92165ffdbe1cb
child 95369 f96e0f078b49e1e7a1be42c41990a10107ee5b91
push id10086
push usertterriberry@mozilla.com
push dateThu, 31 May 2012 18:13:50 +0000
treeherdermozilla-inbound@359f1096bc7c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs751219
milestone15.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 751219 - Support header gain in Opus files, r=kinetik
content/media/ogg/nsOggCodecState.cpp
content/media/ogg/nsOggCodecState.h
content/media/ogg/nsOggReader.cpp
--- a/content/media/ogg/nsOggCodecState.cpp
+++ b/content/media/ogg/nsOggCodecState.cpp
@@ -41,16 +41,22 @@ static PRInt64 LEInt64(const unsigned ch
 }
 
 // Reads a little-endian encoded unsigned 16bit integer at p.
 static PRUint16 LEUint16(const unsigned char* p)
 {
   return p[0] + (p[1] << 8);
 }
 
+// Reads a little-endian encoded signed 16bit integer at p.
+static PRInt16 LEInt16(const unsigned char* p)
+{
+  return static_cast<PRInt16>(LEUint16(p));
+}
+
 /** Decoder base class for Ogg-encapsulated streams. */
 nsOggCodecState*
 nsOggCodecState::Create(ogg_page* aPage)
 {
   NS_ASSERTION(ogg_page_bos(aPage), "Only call on BOS page!");
   nsAutoPtr<nsOggCodecState> codecState;
   if (aPage->body_len > 6 && memcmp(aPage->body+1, "theora", 6) == 0) {
     codecState = new nsTheoraState(aPage);
@@ -752,17 +758,21 @@ nsresult nsVorbisState::ReconstructVorbi
 
 #ifdef MOZ_OPUS
 nsOpusState::nsOpusState(ogg_page* aBosPage) :
   nsOggCodecState(aBosPage, true),
   mRate(0),
   mNominalRate(0),
   mChannels(0),
   mPreSkip(0),
-  mGain(0.0),
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+  mGain(1.0f),
+#else
+  mGain_Q16(65536),
+#endif
   mChannelMapping(0),
   mStreams(0),
   mDecoder(NULL),
   mSkip(0),
   mPrevPacketGranulepos(0),
   mPrevPageGranulepos(0)
 {
   MOZ_COUNT_CTOR(nsOpusState);
@@ -843,17 +853,23 @@ bool nsOpusState::DecodeHeader(ogg_packe
       if ((version & 0xf0) != 0) {
         LOG(PR_LOG_DEBUG, ("Rejecting unknown Opus file version %d", version));
         return false;
       }
 
       mChannels= aPacket->packet[9];
       mPreSkip = LEUint16(aPacket->packet + 10);
       mNominalRate = LEUint32(aPacket->packet + 12);
-      mGain = (float)LEUint16(aPacket->packet + 16) / 256.0;
+      double gain_dB = LEInt16(aPacket->packet + 16) / 256.0;
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+      mGain = static_cast<float>(pow(10,0.05*gain_dB));
+#else
+      mGain_Q16 = static_cast<PRInt32>(NS_MIN(65536*pow(10,0.05*gain_dB)+0.5,
+                                              static_cast<double>(PR_INT32_MAX)));
+#endif
       mChannelMapping = aPacket->packet[18];
 
       if (mChannelMapping == 0) {
         mStreams = 1;
       } else if (aPacket->bytes > 19) {
         mStreams = aPacket->packet[19];
       } else {
         LOG(PR_LOG_DEBUG, ("Invalid Opus file: channel mapping %d,"
@@ -861,17 +877,17 @@ bool nsOpusState::DecodeHeader(ogg_packe
         return false;
       }
 
 #ifdef DEBUG
       LOG(PR_LOG_DEBUG, ("Opus stream header:"));
       LOG(PR_LOG_DEBUG, (" channels: %d", mChannels));
       LOG(PR_LOG_DEBUG, ("  preskip: %d", mPreSkip));
       LOG(PR_LOG_DEBUG, (" original: %d Hz", mNominalRate));
-      LOG(PR_LOG_DEBUG, ("     gain: %.2f dB", mGain));
+      LOG(PR_LOG_DEBUG, ("     gain: %.2f dB", gain_dB));
       LOG(PR_LOG_DEBUG, ("Channel Mapping:"));
       LOG(PR_LOG_DEBUG, ("   family: %d", mChannelMapping));
       LOG(PR_LOG_DEBUG, ("  streams: %d", mStreams));
 #endif
     }
     break;
 
     // Parse the metadata header.
--- a/content/media/ogg/nsOggCodecState.h
+++ b/content/media/ogg/nsOggCodecState.h
@@ -10,16 +10,19 @@
 #include <theora/theoradec.h>
 #ifdef MOZ_TREMOR
 #include <tremor/ivorbiscodec.h>
 #else
 #include <vorbis/codec.h>
 #endif
 #ifdef MOZ_OPUS
 #include <opus/opus.h>
+// For MOZ_SAMPLE_TYPE_*
+#include "nsBuiltinDecoderStateMachine.h"
+#include "nsBuiltinDecoderReader.h"
 #endif
 #include <nsAutoRef.h>
 #include <nsDeque.h>
 #include <nsTArray.h>
 #include <nsClassHashtable.h>
 #include "VideoUtils.h"
 
 #include "mozilla/StandardInteger.h"
@@ -311,17 +314,21 @@ public:
   // Returns the end time that a granulepos represents.
   static PRInt64 Time(int aPreSkip, PRInt64 aGranulepos);
 
   // Various fields from the Ogg Opus header.
   int mRate;        // Sample rate the decoder uses (always 48 kHz).
   PRUint32 mNominalRate; // Original sample rate of the data (informational).
   int mChannels;    // Number of channels the stream encodes.
   PRUint16 mPreSkip; // Number of samples to strip after decoder reset.
-  float mGain;      // Gain (dB) to apply to decoder output.
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+  float mGain;      // Gain to apply to decoder output.
+#else
+  PRInt32 mGain_Q16; // Gain to apply to the decoder output.
+#endif
   int mChannelMapping; // Channel mapping family.
   int mStreams;     // Number of packed streams in each packet.
 
   OpusDecoder *mDecoder;
   int mSkip;        // Number of samples left to trim before playback.
   // Granule position (end sample) of the last decoded Opus packet. This is
   // used to calculate the amount we should trim from the last packet.
   PRInt64 mPrevPacketGranulepos;
--- a/content/media/ogg/nsOggReader.cpp
+++ b/content/media/ogg/nsOggReader.cpp
@@ -446,16 +446,36 @@ nsresult nsOggReader::DecodeOpus(ogg_pac
 
     mOpusState->mSkip -= skipFrames;
     LOG(PR_LOG_DEBUG, ("Opus decoder skipping %d frames", skipFrames));
   }
   // Save this packet's granule position in case we need to perform end
   // trimming on the next packet.
   mOpusState->mPrevPacketGranulepos = endFrame;
 
+  // Apply the header gain if one was specified.
+#ifdef MOZ_SAMPLE_TYPE_FLOAT32
+  if (mOpusState->mGain != 1.0f) {
+    float gain = mOpusState->mGain;
+    int samples = frames * channels;
+    for (int i = 0; i < samples; i++) {
+      buffer[i] *= gain;
+    }
+  }
+#else
+  if (mOpusState->mGain_Q16 != 65536) {
+    PRInt64 gain_Q16 = mOpusState->mGain_Q16;
+    int samples = frames * channels;
+    for (int i = 0; i < samples; i++) {
+      PRInt32 val = static_cast<PRInt32>((gain_Q16*buffer[i] + 32768)>>16);
+      buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
+    }
+  }
+#endif
+
   LOG(PR_LOG_DEBUG, ("Opus decoder pushing %d frames", frames));
   PRInt64 startTime = mOpusState->Time(startFrame);
   PRInt64 endTime = mOpusState->Time(endFrame);
   mAudioQueue.Push(new AudioData(mPageOffset,
                                  startTime,
                                  endTime - startTime,
                                  frames,
                                  buffer.forget(),