Bug 842243 - Part 3: Add TrackEncoder.h/.cpp; Base class for audio/video track encoder. r=roc
authorShelly Lin <slin@mozilla.com>
Fri, 17 May 2013 19:16:36 +0800
changeset 146111 926801efe857f35c56f6b61e8c71c1fea15282ac
parent 146110 f0c287193841e654b87196c0d2f53c9613db99d2
child 146112 bf6d5b00ebe2455556461e56a8b1af7de5b1e716
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs842243
milestone24.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 842243 - Part 3: Add TrackEncoder.h/.cpp; Base class for audio/video track encoder. r=roc
content/media/encoder/Makefile.in
content/media/encoder/TrackEncoder.cpp
content/media/encoder/TrackEncoder.h
content/media/encoder/moz.build
layout/build/Makefile.in
new file mode 100644
--- /dev/null
+++ b/content/media/encoder/Makefile.in
@@ -0,0 +1,19 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+VPATH     = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME = gkconencoder_s
+LIBXUL_LIBRARY = 1
+FAIL_ON_WARNINGS := 1
+
+
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/content/media/encoder/TrackEncoder.cpp
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "TrackEncoder.h"
+#include "MediaStreamGraph.h"
+#include "AudioChannelFormat.h"
+
+#undef LOG
+#ifdef MOZ_WIDGET_GONK
+#include <android/log.h>
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediakEncoder", ## args);
+#else
+#define LOG(args, ...)
+#endif
+
+namespace mozilla {
+
+#define MAX_FRAMES_TO_DROP  48000
+
+void
+AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
+                                            TrackID aID,
+                                            TrackRate aTrackRate,
+                                            TrackTicks aTrackOffset,
+                                            uint32_t aTrackEvents,
+                                            const MediaSegment& aQueuedMedia)
+{
+  AudioSegment* audio = const_cast<AudioSegment*>
+                        (static_cast<const AudioSegment*>(&aQueuedMedia));
+
+  // Check and initialize parameters for codec encoder.
+  if (!mInitialized) {
+    AudioSegment::ChunkIterator iter(*audio);
+    while (!iter.IsEnded()) {
+      AudioChunk chunk = *iter;
+      if (chunk.mBuffer) {
+        Init(chunk.mChannelData.Length(), aTrackRate);
+        break;
+      }
+      iter.Next();
+    }
+  }
+
+  // Append and consume this raw segment.
+  AppendAudioSegment(audio);
+
+  // The stream has stopped and reached the end of track.
+  if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) {
+    LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED .");
+    NotifyEndOfStream();
+  }
+}
+
+void
+AudioTrackEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
+{
+  // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
+  LOG("[AudioTrackEncoder]: NotifyRemoved.");
+  NotifyEndOfStream();
+}
+
+nsresult
+AudioTrackEncoder::AppendAudioSegment(MediaSegment* aSegment)
+{
+  // Drop the in-coming segment if buffer(mRawSegment) is overflow.
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  AudioSegment* audio = static_cast<AudioSegment*>(aSegment);
+  AudioSegment::ChunkIterator iter(*audio);
+
+  if (mRawSegment->GetDuration() < MAX_FRAMES_TO_DROP) {
+    while(!iter.IsEnded()) {
+      AudioChunk chunk = *iter;
+      if (chunk.mBuffer) {
+        mRawSegment->AppendAndConsumeChunk(&chunk);
+      }
+      iter.Next();
+    }
+    if (mRawSegment->GetDuration() >= GetPacketDuration()) {
+      mReentrantMonitor.NotifyAll();
+    }
+  }
+#ifdef DEBUG
+  else {
+    LOG("[AudioTrackEncoder]: A segment has dropped!");
+  }
+#endif
+
+  return NS_OK;
+}
+
+static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
+static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
+
+void
+AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
+                                       int32_t aDuration,
+                                       uint32_t aOutputChannels,
+                                       AudioDataValue* aOutput)
+{
+  if (aChunk.mChannelData.Length() < aOutputChannels) {
+    // Up-mix. This might make the mChannelData have more than aChannels.
+    AudioChannelsUpMix(&aChunk.mChannelData, aOutputChannels, gZeroChannel);
+  }
+
+  if (aChunk.mChannelData.Length() > aOutputChannels) {
+    DownmixAndInterleave(aChunk.mChannelData, aChunk.mBufferFormat, aDuration,
+                         aChunk.mVolume, mChannels, aOutput);
+  } else {
+    InterleaveAndConvertBuffer(aChunk.mChannelData.Elements(),
+                               aChunk.mBufferFormat, aDuration, aChunk.mVolume,
+                               mChannels, aOutput);
+  }
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/encoder/TrackEncoder.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TrackEncoder_h_
+#define TrackEncoder_h_
+
+#include "mozilla/ReentrantMonitor.h"
+
+#include "AudioSegment.h"
+#include "StreamBuffer.h"
+
+namespace mozilla {
+
+class MediaStreamGraph;
+
+/**
+ * Base class of AudioTrackEncoder and VideoTrackEncoder. Lifetimes managed by
+ * MediaEncoder. Most methods can only be called on the MediaEncoder's thread,
+ * but some subclass methods can be called on other threads when noted.
+ *
+ * NotifyQueuedTrackChanges is called on subclasses of this class from the
+ * MediaStreamGraph thread, and AppendAudioSegment/AppendVideoSegment is then
+ * called to store media data in the TrackEncoder. Later on, GetEncodedTrack is
+ * called on MediaEncoder's thread to encode and retrieve the encoded data.
+ */
+class TrackEncoder
+{
+public:
+  TrackEncoder() {}
+  virtual ~TrackEncoder() {}
+
+  /**
+   * Notified by the same callbcak of MediaEncoder when it has received a track
+   * change from MediaStreamGraph. Called on the MediaStreamGraph thread.
+   */
+  virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                        TrackRate aTrackRate,
+                                        TrackTicks aTrackOffset,
+                                        uint32_t aTrackEvents,
+                                        const MediaSegment& aQueuedMedia) = 0;
+
+  /**
+   * Notified by the same callback of MediaEncoder when it has been removed from
+   * MediaStreamGraph. Called on the MediaStreamGraph thread.
+   */
+  virtual void NotifyRemoved(MediaStreamGraph* aGraph) = 0;
+
+  /**
+   * Creates and sets up header for a specific codec. Result data is returned
+   * in aOutput.
+   */
+  virtual nsresult GetHeader(nsTArray<uint8_t>* aOutput) = 0;
+
+  /**
+   * Encodes raw segments. Result data is returned in aOutput. aOutputDuration
+   * is the playback duration of this packet in number of samples.
+   */
+  virtual nsresult GetEncodedTrack(nsTArray<uint8_t>* aOutput,
+                                   int &aOutputDuration) = 0;
+};
+
+class AudioTrackEncoder : public TrackEncoder
+{
+public:
+  AudioTrackEncoder()
+    : TrackEncoder()
+    , mChannels(0)
+    , mSamplingRate(0)
+    , mInitialized(false)
+    , mDoneEncoding(false)
+    , mReentrantMonitor("media.AudioEncoder")
+    , mRawSegment(new AudioSegment())
+    , mEndOfStream(false)
+    , mCanceled(false)
+  {}
+
+  void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
+                                TrackRate aTrackRate,
+                                TrackTicks aTrackOffset,
+                                uint32_t aTrackEvents,
+                                const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
+
+  void NotifyRemoved(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+
+  bool IsEncodingComplete()
+  {
+    return mDoneEncoding;
+  }
+
+  /**
+   * Notifies from MediaEncoder to cancel the encoding, and wakes up
+   * mReentrantMonitor if encoder is waiting on it.
+   */
+  void NotifyCancel()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    mCanceled = true;
+    mReentrantMonitor.NotifyAll();
+  }
+
+protected:
+  /**
+   * Number of samples per channel in a pcm buffer. This is also the value of
+   * frame size required by audio encoder, and mReentrantMonitor will be
+   * notified when at least this much data has been added to mRawSegment.
+   */
+  virtual int GetPacketDuration() = 0;
+
+  /**
+   * Initializes the audio encoder. The call of this method is delayed until we
+   * have received the first valid track from MediaStreamGraph, and the
+   * mReentrantMonitor will be notified if other methods is waiting for encoder
+   * to be completely initialized. This method is called on the MediaStreamGraph
+   * thread.
+   */
+  virtual nsresult Init(int aChannels, int aSamplingRate) = 0;
+
+  /**
+   * Appends and consumes track data from aSegment, this method is called on
+   * the MediaStreamGraph thread. mReentrantMonitor will be notified when at
+   * least GetPacketDuration() data has been added to mRawSegment, wake up other
+   * method which is waiting for more data from mRawSegment.
+   */
+  nsresult AppendAudioSegment(MediaSegment* aSegment);
+
+  /**
+   * Notifies the audio encoder that we have reached the end of source stream,
+   * and wakes up mReentrantMonitor if encoder is waiting for more track data.
+   */
+  void NotifyEndOfStream()
+  {
+    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    mEndOfStream = true;
+    mReentrantMonitor.NotifyAll();
+  }
+
+  /**
+   * Interleaves the track data and stores the result into aOutput. Might need
+   * to up-mix or down-mix the channel data if the channels number of this chunk
+   * is different from mChannels. The channel data from aChunk might be modified
+   * by up-mixing.
+   */
+  void InterleaveTrackData(AudioChunk& aChunk, int32_t aDuration,
+                           uint32_t aOutputChannels, AudioDataValue* aOutput);
+
+  /**
+   * The number of channels in the first valid audio chunk, and is being used
+   * to initialize the audio encoder.
+   */
+  int mChannels;
+  int mSamplingRate;
+  bool mInitialized;
+  bool mDoneEncoding;
+
+  /**
+   * A ReentrantMonitor to protect the pushing and pulling of mRawSegment.
+   */
+  ReentrantMonitor mReentrantMonitor;
+
+  /**
+   * A segment queue of audio track data, protected by mReentrantMonitor.
+   */
+  nsAutoPtr<AudioSegment> mRawSegment;
+
+  /**
+   * True if we have received an event of TRACK_EVENT_ENDED from MediaStreamGraph,
+   * or the MediaEncoder is removed from its source stream, protected by
+   * mReentrantMonitor.
+   */
+  bool mEndOfStream;
+
+  /**
+   * True if a cancellation of encoding is sent from MediaEncoder, protected by
+   * mReentrantMonitor.
+   */
+  bool mCanceled;
+};
+
+class VideoTrackEncoder : public TrackEncoder
+{
+
+};
+
+}
+#endif
--- a/content/media/encoder/moz.build
+++ b/content/media/encoder/moz.build
@@ -3,9 +3,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'content'
 
 EXPORTS += [
     'ContainerWriter.h',
+    'TrackEncoder.h',
 ]
+
+CPP_SOURCES += [
+    'TrackEncoder.cpp',
+]
+
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -34,16 +34,17 @@ SHARED_LIBRARY_LIBS = \
 	../xul/base/src/$(LIB_PREFIX)gkxulbase_s.$(LIB_SUFFIX) \
 	../mathml/$(LIB_PREFIX)gkmathml_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/base/src/$(LIB_PREFIX)gkconbase_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/canvas/src/$(LIB_PREFIX)gkconcvs_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/events/src/$(LIB_PREFIX)gkconevents_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/html/content/src/$(LIB_PREFIX)gkconhtmlcon_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/html/document/src/$(LIB_PREFIX)gkconhtmldoc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/media/$(LIB_PREFIX)gkconmedia_s.$(LIB_SUFFIX) \
+	$(DEPTH)/content/media/encoder/$(LIB_PREFIX)gkconencoder_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/media/webaudio/$(LIB_PREFIX)gkconwebaudio_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/media/webaudio/blink/$(LIB_PREFIX)gkconwebaudio_blink_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/media/webrtc/$(LIB_PREFIX)gkconwebrtc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/xml/content/src/$(LIB_PREFIX)gkconxmlcon_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/xml/document/src/$(LIB_PREFIX)gkconxmldoc_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/xslt/src/base/$(LIB_PREFIX)txbase_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/xslt/src/xml/$(LIB_PREFIX)txxml_s.$(LIB_SUFFIX) \
 	$(DEPTH)/content/xslt/src/xpath/$(LIB_PREFIX)txxpath_s.$(LIB_SUFFIX) \