Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / media / cast / audio_sender / audio_encoder.cc
index 32db9c2..2363619 100644 (file)
 namespace media {
 namespace cast {
 
-void LogAudioEncodedEvent(CastEnvironment* const cast_environment,
-                          const base::TimeTicks& recorded_time) {
-  // TODO(mikhal): Resolve timestamp calculation for audio.
-  base::TimeTicks now = cast_environment->Clock()->NowTicks();
-
-  cast_environment->Logging()->InsertFrameEvent(now, kAudioFrameEncoded,
-      GetVideoRtpTimestamp(recorded_time), kFrameIdUnknown);
-}
-
 // Base class that handles the common problem of feeding one or more AudioBus'
 // data into a 10 ms buffer and then, once the buffer is full, encoding the
 // signal and emitting an EncodedAudioFrame via the FrameEncodedCallback.
 //
 // Subclasses complete the implementation by handling the actual encoding
 // details.
-class AudioEncoder::ImplBase {
+class AudioEncoder::ImplBase : public base::SupportsWeakPtr<ImplBase> {
  public:
   ImplBase(CastEnvironment* cast_environment,
-           transport::AudioCodec codec, int num_channels, int sampling_rate,
+           transport::AudioCodec codec,
+           int num_channels,
+           int sampling_rate,
            const FrameEncodedCallback& callback)
       : cast_environment_(cast_environment),
-        codec_(codec), num_channels_(num_channels),
+        codec_(codec),
+        num_channels_(num_channels),
         samples_per_10ms_(sampling_rate / 100),
         callback_(callback),
         buffer_fill_end_(0),
-        frame_id_(0) {
-    CHECK_GT(num_channels_, 0);
-    CHECK_GT(samples_per_10ms_, 0);
-    CHECK_EQ(sampling_rate % 100, 0);
-    CHECK_LE(samples_per_10ms_ * num_channels_,
-             transport::EncodedAudioFrame::kMaxNumberOfSamples);
+        frame_id_(0),
+        rtp_timestamp_(0),
+        weak_factory_(this) {
+    DCHECK_GT(num_channels_, 0);
+    DCHECK_GT(samples_per_10ms_, 0);
+    DCHECK_EQ(sampling_rate % 100, 0);
+    DCHECK_LE(samples_per_10ms_ * num_channels_,
+              transport::EncodedAudioFrame::kMaxNumberOfSamples);
+
+    if (num_channels_ <= 0 || samples_per_10ms_ <= 0 ||
+        sampling_rate % 100 != 0 ||
+        samples_per_10ms_ * num_channels_ >
+            transport::EncodedAudioFrame::kMaxNumberOfSamples) {
+      initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
+    } else {
+      initialization_status_ = STATUS_INITIALIZED;
+    }
   }
 
   virtual ~ImplBase() {}
 
+  CastInitializationStatus InitializationResult() const {
+    return initialization_status_;
+  }
+
+  void LogAudioFrameEvent(uint32 rtp_timestamp,
+                          uint32 frame_id,
+                          CastLoggingEvent type) {
+    cast_environment_->Logging()->InsertFrameEvent(
+        cast_environment_->Clock()->NowTicks(), type, rtp_timestamp, frame_id);
+  }
+
   void EncodeAudio(const AudioBus* audio_bus,
                    const base::TimeTicks& recorded_time,
                    const base::Closure& done_callback) {
     int src_pos = 0;
+    int packet_count = 0;
     while (audio_bus && src_pos < audio_bus->frames()) {
-      const int num_samples_to_xfer =
-          std::min(samples_per_10ms_ - buffer_fill_end_,
-                   audio_bus->frames() - src_pos);
+      const int num_samples_to_xfer = std::min(
+          samples_per_10ms_ - buffer_fill_end_, audio_bus->frames() - src_pos);
       DCHECK_EQ(audio_bus->channels(), num_channels_);
       TransferSamplesIntoBuffer(
           audio_bus, src_pos, buffer_fill_end_, num_samples_to_xfer);
@@ -70,8 +86,8 @@ class AudioEncoder::ImplBase {
       buffer_fill_end_ += num_samples_to_xfer;
 
       if (src_pos == audio_bus->frames()) {
-        cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
-                                    done_callback);
+        cast_environment_->PostTask(
+            CastEnvironment::MAIN, FROM_HERE, done_callback);
         // Note: |audio_bus| is invalid once done_callback is invoked.
         audio_bus = NULL;
       }
@@ -81,8 +97,27 @@ class AudioEncoder::ImplBase {
             new transport::EncodedAudioFrame());
         audio_frame->codec = codec_;
         audio_frame->frame_id = frame_id_++;
-        audio_frame->samples = samples_per_10ms_;
+        rtp_timestamp_ += samples_per_10ms_;
+        audio_frame->rtp_timestamp = rtp_timestamp_;
+
+        // Update logging.
+        cast_environment_->PostTask(CastEnvironment::MAIN,
+                                    FROM_HERE,
+                                    base::Bind(&ImplBase::LogAudioFrameEvent,
+                                               weak_factory_.GetWeakPtr(),
+                                               audio_frame->rtp_timestamp,
+                                               audio_frame->frame_id,
+                                               kAudioFrameReceived));
+
         if (EncodeFromFilledBuffer(&audio_frame->data)) {
+          // Update logging.
+          cast_environment_->PostTask(CastEnvironment::MAIN,
+                                      FROM_HERE,
+                                      base::Bind(&ImplBase::LogAudioFrameEvent,
+                                                 weak_factory_.GetWeakPtr(),
+                                                 audio_frame->rtp_timestamp,
+                                                 audio_frame->frame_id,
+                                                 kAudioFrameEncoded));
           // Compute an offset to determine the recorded time for the first
           // audio sample in the buffer.
           const base::TimeDelta buffer_time_offset =
@@ -90,10 +125,19 @@ class AudioEncoder::ImplBase {
               base::TimeDelta::FromMilliseconds(10) / samples_per_10ms_;
           // TODO(miu): Consider batching EncodedAudioFrames so we only post a
           // at most one task for each call to this method.
-          cast_environment_->PostTask(
-              CastEnvironment::MAIN, FROM_HERE,
-              base::Bind(callback_, base::Passed(&audio_frame),
-                         recorded_time - buffer_time_offset));
+          // Postpone every packet by 10mS with respect to the previous. Playout
+          // is postponed already by 10mS, and this will better correlate with
+          // the pacer's expectations.
+          //TODO(mikhal): Turn this into a list of packets.
+          // Update the end2end allowed error once this is fixed.
+          cast_environment_->PostDelayedTask(
+              CastEnvironment::MAIN,
+              FROM_HERE,
+              base::Bind(callback_,
+                         base::Passed(&audio_frame),
+                         recorded_time - buffer_time_offset),
+              base::TimeDelta::FromMilliseconds(packet_count * 10));
+          ++packet_count;
         }
         buffer_fill_end_ = 0;
       }
@@ -112,6 +156,7 @@ class AudioEncoder::ImplBase {
   const int num_channels_;
   const int samples_per_10ms_;
   const FrameEncodedCallback callback_;
+  CastInitializationStatus initialization_status_;
 
  private:
   // In the case where a call to EncodeAudio() cannot completely fill the
@@ -122,6 +167,12 @@ class AudioEncoder::ImplBase {
   // A counter used to label EncodedAudioFrames.
   uint32 frame_id_;
 
+  // For audio, rtp_timestamp is computed as the sum of the audio samples seen
+  // so far.
+  uint32 rtp_timestamp_;
+
+  base::WeakPtrFactory<ImplBase> weak_factory_;
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ImplBase);
 };
@@ -129,16 +180,26 @@ class AudioEncoder::ImplBase {
 class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
  public:
   OpusImpl(CastEnvironment* cast_environment,
-           int num_channels, int sampling_rate, int bitrate,
+           int num_channels,
+           int sampling_rate,
+           int bitrate,
            const FrameEncodedCallback& callback)
-      : ImplBase(cast_environment, transport::kOpus, num_channels,
-                 sampling_rate, callback),
+      : ImplBase(cast_environment,
+                 transport::kOpus,
+                 num_channels,
+                 sampling_rate,
+                 callback),
         encoder_memory_(new uint8[opus_encoder_get_size(num_channels)]),
         opus_encoder_(reinterpret_cast<OpusEncoder*>(encoder_memory_.get())),
         buffer_(new float[num_channels * samples_per_10ms_]) {
-    CHECK_EQ(opus_encoder_init(opus_encoder_, sampling_rate, num_channels,
-                               OPUS_APPLICATION_AUDIO),
-             OPUS_OK);
+    if (ImplBase::initialization_status_ != STATUS_INITIALIZED) {
+      return;
+    }
+
+    CHECK_EQ(
+        opus_encoder_init(
+            opus_encoder_, sampling_rate, num_channels, OPUS_APPLICATION_AUDIO),
+        OPUS_OK);
     if (bitrate <= 0) {
       // Note: As of 2013-10-31, the encoder in "auto bitrate" mode would use a
       // variable bitrate up to 102kbps for 2-channel, 48 kHz audio and a 10 ms
@@ -169,9 +230,12 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
 
   virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
     out->resize(kOpusMaxPayloadSize);
-    const opus_int32 result = opus_encode_float(
-        opus_encoder_, buffer_.get(), samples_per_10ms_,
-        reinterpret_cast<uint8*>(&out->at(0)), kOpusMaxPayloadSize);
+    const opus_int32 result =
+        opus_encode_float(opus_encoder_,
+                          buffer_.get(),
+                          samples_per_10ms_,
+                          reinterpret_cast<uint8*>(&out->at(0)),
+                          kOpusMaxPayloadSize);
     if (result > 1) {
       out->resize(result);
       return true;
@@ -203,10 +267,14 @@ class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
 class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
  public:
   Pcm16Impl(CastEnvironment* cast_environment,
-            int num_channels, int sampling_rate,
+            int num_channels,
+            int sampling_rate,
             const FrameEncodedCallback& callback)
-      : ImplBase(cast_environment, transport::kPcm16, num_channels,
-                 sampling_rate, callback),
+      : ImplBase(cast_environment,
+                 transport::kPcm16,
+                 num_channels,
+                 sampling_rate,
+                 callback),
         buffer_(new int16[num_channels * samples_per_10ms_]) {}
 
   virtual ~Pcm16Impl() {}
@@ -217,7 +285,9 @@ class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
                                          int buffer_fill_offset,
                                          int num_samples) OVERRIDE {
     audio_bus->ToInterleavedPartial(
-        source_offset, num_samples, sizeof(int16),
+        source_offset,
+        num_samples,
+        sizeof(int16),
         buffer_.get() + buffer_fill_offset * num_channels_);
   }
 
@@ -249,14 +319,17 @@ AudioEncoder::AudioEncoder(
 
   switch (audio_config.codec) {
     case transport::kOpus:
-      impl_.reset(new OpusImpl(
-          cast_environment, audio_config.channels, audio_config.frequency,
-          audio_config.bitrate, frame_encoded_callback));
+      impl_.reset(new OpusImpl(cast_environment,
+                               audio_config.channels,
+                               audio_config.frequency,
+                               audio_config.bitrate,
+                               frame_encoded_callback));
       break;
     case transport::kPcm16:
-      impl_.reset(new Pcm16Impl(
-          cast_environment, audio_config.channels, audio_config.frequency,
-          frame_encoded_callback));
+      impl_.reset(new Pcm16Impl(cast_environment,
+                                audio_config.channels,
+                                audio_config.frequency,
+                                frame_encoded_callback));
       break;
     default:
       NOTREACHED() << "Unsupported or unspecified codec for audio encoder";
@@ -266,30 +339,37 @@ AudioEncoder::AudioEncoder(
 
 AudioEncoder::~AudioEncoder() {}
 
-void AudioEncoder::InsertAudio(
-    const AudioBus* audio_bus,
-    const base::TimeTicks& recorded_time,
-    const base::Closure& done_callback) {
+CastInitializationStatus AudioEncoder::InitializationResult() const {
+  if (impl_) {
+    return impl_->InitializationResult();
+  }
+  return STATUS_UNSUPPORTED_AUDIO_CODEC;
+}
+
+void AudioEncoder::InsertAudio(const AudioBus* audio_bus,
+                               const base::TimeTicks& recorded_time,
+                               const base::Closure& done_callback) {
   DCHECK(insert_thread_checker_.CalledOnValidThread());
   if (!impl_) {
     NOTREACHED();
-    cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
-                                done_callback);
+    cast_environment_->PostTask(
+        CastEnvironment::MAIN, FROM_HERE, done_callback);
     return;
   }
-  cast_environment_->PostTask(CastEnvironment::AUDIO_ENCODER, FROM_HERE,
-      base::Bind(&AudioEncoder::EncodeAudio, this, audio_bus, recorded_time,
-                 done_callback));
+  cast_environment_->PostTask(CastEnvironment::AUDIO_ENCODER,
+                              FROM_HERE,
+                              base::Bind(&AudioEncoder::EncodeAudio,
+                                         this,
+                                         audio_bus,
+                                         recorded_time,
+                                         done_callback));
 }
 
-void AudioEncoder::EncodeAudio(
-    const AudioBus* audio_bus,
-    const base::TimeTicks& recorded_time,
-    const base::Closure& done_callback) {
+void AudioEncoder::EncodeAudio(const AudioBus* audio_bus,
+                               const base::TimeTicks& recorded_time,
+                               const base::Closure& done_callback) {
   DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::AUDIO_ENCODER));
   impl_->EncodeAudio(audio_bus, recorded_time, done_callback);
-  cast_environment_->PostTask(CastEnvironment::MAIN, FROM_HERE,
-      base::Bind(LogAudioEncodedEvent, cast_environment_, recorded_time));
 }
 
 }  // namespace cast