| Index: media/cast/audio_sender/audio_encoder.cc
|
| diff --git a/media/cast/audio_sender/audio_encoder.cc b/media/cast/audio_sender/audio_encoder.cc
|
| deleted file mode 100644
|
| index 8f7977b6c336982cdf5a72b8cf721a25f155f517..0000000000000000000000000000000000000000
|
| --- a/media/cast/audio_sender/audio_encoder.cc
|
| +++ /dev/null
|
| @@ -1,385 +0,0 @@
|
| -// Copyright 2013 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "media/cast/audio_sender/audio_encoder.h"
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/bind_helpers.h"
|
| -#include "base/location.h"
|
| -#include "base/stl_util.h"
|
| -#include "base/sys_byteorder.h"
|
| -#include "base/time/time.h"
|
| -#include "media/base/audio_bus.h"
|
| -#include "media/cast/cast_defines.h"
|
| -#include "media/cast/cast_environment.h"
|
| -#include "third_party/opus/src/include/opus.h"
|
| -
|
| -namespace media {
|
| -namespace cast {
|
| -
|
| -namespace {
|
| -
|
| -// The fixed number of audio frames per second and, inversely, the duration of
|
| -// one frame's worth of samples.
|
| -const int kFramesPerSecond = 100;
|
| -const int kFrameDurationMillis = 1000 / kFramesPerSecond; // No remainder!
|
| -
|
| -// Threshold used to decide whether audio being delivered to the encoder is
|
| -// coming in too slow with respect to the capture timestamps.
|
| -const int kUnderrunThresholdMillis = 3 * kFrameDurationMillis;
|
| -
|
| -} // namespace
|
| -
|
| -
|
| -// Base class that handles the common problem of feeding one or more AudioBus'
|
| -// data into a buffer and then, once the buffer is full, encoding the signal and
|
| -// emitting an EncodedFrame via the FrameEncodedCallback.
|
| -//
|
| -// Subclasses complete the implementation by handling the actual encoding
|
| -// details.
|
| -class AudioEncoder::ImplBase
|
| - : public base::RefCountedThreadSafe<AudioEncoder::ImplBase> {
|
| - public:
|
| - ImplBase(const scoped_refptr<CastEnvironment>& cast_environment,
|
| - transport::Codec codec,
|
| - int num_channels,
|
| - int sampling_rate,
|
| - const FrameEncodedCallback& callback)
|
| - : cast_environment_(cast_environment),
|
| - codec_(codec),
|
| - num_channels_(num_channels),
|
| - samples_per_frame_(sampling_rate / kFramesPerSecond),
|
| - callback_(callback),
|
| - cast_initialization_status_(STATUS_AUDIO_UNINITIALIZED),
|
| - buffer_fill_end_(0),
|
| - frame_id_(0),
|
| - frame_rtp_timestamp_(0) {
|
| - // Support for max sampling rate of 48KHz, 2 channels, 100 ms duration.
|
| - const int kMaxSamplesTimesChannelsPerFrame = 48 * 2 * 100;
|
| - if (num_channels_ <= 0 || samples_per_frame_ <= 0 ||
|
| - sampling_rate % kFramesPerSecond != 0 ||
|
| - samples_per_frame_ * num_channels_ > kMaxSamplesTimesChannelsPerFrame) {
|
| - cast_initialization_status_ = STATUS_INVALID_AUDIO_CONFIGURATION;
|
| - }
|
| - }
|
| -
|
| - CastInitializationStatus InitializationResult() const {
|
| - return cast_initialization_status_;
|
| - }
|
| -
|
| - void EncodeAudio(scoped_ptr<AudioBus> audio_bus,
|
| - const base::TimeTicks& recorded_time) {
|
| - DCHECK_EQ(cast_initialization_status_, STATUS_AUDIO_INITIALIZED);
|
| - DCHECK(!recorded_time.is_null());
|
| -
|
| - // Determine whether |recorded_time| is consistent with the amount of audio
|
| - // data having been processed in the past. Resolve the underrun problem by
|
| - // dropping data from the internal buffer and skipping ahead the next
|
| - // frame's RTP timestamp by the estimated number of frames missed. On the
|
| - // other hand, don't attempt to resolve overruns: A receiver should
|
| - // gracefully deal with an excess of audio data.
|
| - const base::TimeDelta frame_duration =
|
| - base::TimeDelta::FromMilliseconds(kFrameDurationMillis);
|
| - base::TimeDelta buffer_fill_duration =
|
| - buffer_fill_end_ * frame_duration / samples_per_frame_;
|
| - if (!frame_capture_time_.is_null()) {
|
| - const base::TimeDelta amount_ahead_by =
|
| - recorded_time - (frame_capture_time_ + buffer_fill_duration);
|
| - if (amount_ahead_by >
|
| - base::TimeDelta::FromMilliseconds(kUnderrunThresholdMillis)) {
|
| - buffer_fill_end_ = 0;
|
| - buffer_fill_duration = base::TimeDelta();
|
| - const int64 num_frames_missed = amount_ahead_by /
|
| - base::TimeDelta::FromMilliseconds(kFrameDurationMillis);
|
| - frame_rtp_timestamp_ +=
|
| - static_cast<uint32>(num_frames_missed * samples_per_frame_);
|
| - DVLOG(1) << "Skipping RTP timestamp ahead to account for "
|
| - << num_frames_missed * samples_per_frame_
|
| - << " samples' worth of underrun.";
|
| - }
|
| - }
|
| - frame_capture_time_ = recorded_time - buffer_fill_duration;
|
| -
|
| - // Encode all audio in |audio_bus| into zero or more frames.
|
| - int src_pos = 0;
|
| - while (src_pos < audio_bus->frames()) {
|
| - const int num_samples_to_xfer = std::min(
|
| - samples_per_frame_ - buffer_fill_end_, audio_bus->frames() - src_pos);
|
| - DCHECK_EQ(audio_bus->channels(), num_channels_);
|
| - TransferSamplesIntoBuffer(
|
| - audio_bus.get(), src_pos, buffer_fill_end_, num_samples_to_xfer);
|
| - src_pos += num_samples_to_xfer;
|
| - buffer_fill_end_ += num_samples_to_xfer;
|
| -
|
| - if (buffer_fill_end_ < samples_per_frame_)
|
| - break;
|
| -
|
| - scoped_ptr<transport::EncodedFrame> audio_frame(
|
| - new transport::EncodedFrame());
|
| - audio_frame->dependency = transport::EncodedFrame::KEY;
|
| - audio_frame->frame_id = frame_id_;
|
| - audio_frame->referenced_frame_id = frame_id_;
|
| - audio_frame->rtp_timestamp = frame_rtp_timestamp_;
|
| - audio_frame->reference_time = frame_capture_time_;
|
| -
|
| - if (EncodeFromFilledBuffer(&audio_frame->data)) {
|
| - cast_environment_->PostTask(
|
| - CastEnvironment::MAIN,
|
| - FROM_HERE,
|
| - base::Bind(callback_, base::Passed(&audio_frame)));
|
| - }
|
| -
|
| - // Reset the internal buffer, frame ID, and timestamps for the next frame.
|
| - buffer_fill_end_ = 0;
|
| - ++frame_id_;
|
| - frame_rtp_timestamp_ += samples_per_frame_;
|
| - frame_capture_time_ += frame_duration;
|
| - }
|
| - }
|
| -
|
| - protected:
|
| - friend class base::RefCountedThreadSafe<ImplBase>;
|
| - virtual ~ImplBase() {}
|
| -
|
| - virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
|
| - int source_offset,
|
| - int buffer_fill_offset,
|
| - int num_samples) = 0;
|
| - virtual bool EncodeFromFilledBuffer(std::string* out) = 0;
|
| -
|
| - const scoped_refptr<CastEnvironment> cast_environment_;
|
| - const transport::Codec codec_;
|
| - const int num_channels_;
|
| - const int samples_per_frame_;
|
| - const FrameEncodedCallback callback_;
|
| -
|
| - // Subclass' ctor is expected to set this to STATUS_AUDIO_INITIALIZED.
|
| - CastInitializationStatus cast_initialization_status_;
|
| -
|
| - private:
|
| - // In the case where a call to EncodeAudio() cannot completely fill the
|
| - // buffer, this points to the position at which to populate data in a later
|
| - // call.
|
| - int buffer_fill_end_;
|
| -
|
| - // A counter used to label EncodedFrames.
|
| - uint32 frame_id_;
|
| -
|
| - // The RTP timestamp for the next frame of encoded audio. This is defined as
|
| - // the number of audio samples encoded so far, plus the estimated number of
|
| - // samples that were missed due to data underruns. A receiver uses this value
|
| - // to detect gaps in the audio signal data being provided. Per the spec, RTP
|
| - // timestamp values are allowed to overflow and roll around past zero.
|
| - uint32 frame_rtp_timestamp_;
|
| -
|
| - // The local system time associated with the start of the next frame of
|
| - // encoded audio. This value is passed on to a receiver as a reference clock
|
| - // timestamp for the purposes of synchronizing audio and video. Its
|
| - // progression is expected to drift relative to the elapsed time implied by
|
| - // the RTP timestamps.
|
| - base::TimeTicks frame_capture_time_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ImplBase);
|
| -};
|
| -
|
| -class AudioEncoder::OpusImpl : public AudioEncoder::ImplBase {
|
| - public:
|
| - OpusImpl(const scoped_refptr<CastEnvironment>& cast_environment,
|
| - int num_channels,
|
| - int sampling_rate,
|
| - int bitrate,
|
| - const FrameEncodedCallback& callback)
|
| - : ImplBase(cast_environment,
|
| - transport::CODEC_AUDIO_OPUS,
|
| - 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_frame_]) {
|
| - if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
|
| - return;
|
| - if (opus_encoder_init(opus_encoder_,
|
| - sampling_rate,
|
| - num_channels,
|
| - OPUS_APPLICATION_AUDIO) != OPUS_OK) {
|
| - ImplBase::cast_initialization_status_ =
|
| - STATUS_INVALID_AUDIO_CONFIGURATION;
|
| - return;
|
| - }
|
| - ImplBase::cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
|
| -
|
| - 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
|
| - // frame size. The opus library authors may, of course, adjust this in
|
| - // later versions.
|
| - bitrate = OPUS_AUTO;
|
| - }
|
| - CHECK_EQ(opus_encoder_ctl(opus_encoder_, OPUS_SET_BITRATE(bitrate)),
|
| - OPUS_OK);
|
| - }
|
| -
|
| - private:
|
| - virtual ~OpusImpl() {}
|
| -
|
| - virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
|
| - int source_offset,
|
| - int buffer_fill_offset,
|
| - int num_samples) OVERRIDE {
|
| - // Opus requires channel-interleaved samples in a single array.
|
| - for (int ch = 0; ch < audio_bus->channels(); ++ch) {
|
| - const float* src = audio_bus->channel(ch) + source_offset;
|
| - const float* const src_end = src + num_samples;
|
| - float* dest = buffer_.get() + buffer_fill_offset * num_channels_ + ch;
|
| - for (; src < src_end; ++src, dest += num_channels_)
|
| - *dest = *src;
|
| - }
|
| - }
|
| -
|
| - virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
|
| - out->resize(kOpusMaxPayloadSize);
|
| - const opus_int32 result =
|
| - opus_encode_float(opus_encoder_,
|
| - buffer_.get(),
|
| - samples_per_frame_,
|
| - reinterpret_cast<uint8*>(string_as_array(out)),
|
| - kOpusMaxPayloadSize);
|
| - if (result > 1) {
|
| - out->resize(result);
|
| - return true;
|
| - } else if (result < 0) {
|
| - LOG(ERROR) << "Error code from opus_encode_float(): " << result;
|
| - return false;
|
| - } else {
|
| - // Do nothing: The documentation says that a return value of zero or
|
| - // one byte means the packet does not need to be transmitted.
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - const scoped_ptr<uint8[]> encoder_memory_;
|
| - OpusEncoder* const opus_encoder_;
|
| - const scoped_ptr<float[]> buffer_;
|
| -
|
| - // This is the recommended value, according to documentation in
|
| - // third_party/opus/src/include/opus.h, so that the Opus encoder does not
|
| - // degrade the audio due to memory constraints.
|
| - //
|
| - // Note: Whereas other RTP implementations do not, the cast library is
|
| - // perfectly capable of transporting larger than MTU-sized audio frames.
|
| - static const int kOpusMaxPayloadSize = 4000;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(OpusImpl);
|
| -};
|
| -
|
| -class AudioEncoder::Pcm16Impl : public AudioEncoder::ImplBase {
|
| - public:
|
| - Pcm16Impl(const scoped_refptr<CastEnvironment>& cast_environment,
|
| - int num_channels,
|
| - int sampling_rate,
|
| - const FrameEncodedCallback& callback)
|
| - : ImplBase(cast_environment,
|
| - transport::CODEC_AUDIO_PCM16,
|
| - num_channels,
|
| - sampling_rate,
|
| - callback),
|
| - buffer_(new int16[num_channels * samples_per_frame_]) {
|
| - if (ImplBase::cast_initialization_status_ != STATUS_AUDIO_UNINITIALIZED)
|
| - return;
|
| - cast_initialization_status_ = STATUS_AUDIO_INITIALIZED;
|
| - }
|
| -
|
| - private:
|
| - virtual ~Pcm16Impl() {}
|
| -
|
| - virtual void TransferSamplesIntoBuffer(const AudioBus* audio_bus,
|
| - int source_offset,
|
| - int buffer_fill_offset,
|
| - int num_samples) OVERRIDE {
|
| - audio_bus->ToInterleavedPartial(
|
| - source_offset,
|
| - num_samples,
|
| - sizeof(int16),
|
| - buffer_.get() + buffer_fill_offset * num_channels_);
|
| - }
|
| -
|
| - virtual bool EncodeFromFilledBuffer(std::string* out) OVERRIDE {
|
| - // Output 16-bit PCM integers in big-endian byte order.
|
| - out->resize(num_channels_ * samples_per_frame_ * sizeof(int16));
|
| - const int16* src = buffer_.get();
|
| - const int16* const src_end = src + num_channels_ * samples_per_frame_;
|
| - uint16* dest = reinterpret_cast<uint16*>(&out->at(0));
|
| - for (; src < src_end; ++src, ++dest)
|
| - *dest = base::HostToNet16(*src);
|
| - return true;
|
| - }
|
| -
|
| - private:
|
| - const scoped_ptr<int16[]> buffer_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(Pcm16Impl);
|
| -};
|
| -
|
| -AudioEncoder::AudioEncoder(
|
| - const scoped_refptr<CastEnvironment>& cast_environment,
|
| - int num_channels,
|
| - int sampling_rate,
|
| - int bitrate,
|
| - transport::Codec codec,
|
| - const FrameEncodedCallback& frame_encoded_callback)
|
| - : cast_environment_(cast_environment) {
|
| - // Note: It doesn't matter which thread constructs AudioEncoder, just so long
|
| - // as all calls to InsertAudio() are by the same thread.
|
| - insert_thread_checker_.DetachFromThread();
|
| - switch (codec) {
|
| - case transport::CODEC_AUDIO_OPUS:
|
| - impl_ = new OpusImpl(cast_environment,
|
| - num_channels,
|
| - sampling_rate,
|
| - bitrate,
|
| - frame_encoded_callback);
|
| - break;
|
| - case transport::CODEC_AUDIO_PCM16:
|
| - impl_ = new Pcm16Impl(cast_environment,
|
| - num_channels,
|
| - sampling_rate,
|
| - frame_encoded_callback);
|
| - break;
|
| - default:
|
| - NOTREACHED() << "Unsupported or unspecified codec for audio encoder";
|
| - break;
|
| - }
|
| -}
|
| -
|
| -AudioEncoder::~AudioEncoder() {}
|
| -
|
| -CastInitializationStatus AudioEncoder::InitializationResult() const {
|
| - DCHECK(insert_thread_checker_.CalledOnValidThread());
|
| - if (impl_) {
|
| - return impl_->InitializationResult();
|
| - }
|
| - return STATUS_UNSUPPORTED_AUDIO_CODEC;
|
| -}
|
| -
|
| -void AudioEncoder::InsertAudio(scoped_ptr<AudioBus> audio_bus,
|
| - const base::TimeTicks& recorded_time) {
|
| - DCHECK(insert_thread_checker_.CalledOnValidThread());
|
| - DCHECK(audio_bus.get());
|
| - if (!impl_) {
|
| - NOTREACHED();
|
| - return;
|
| - }
|
| - cast_environment_->PostTask(CastEnvironment::AUDIO,
|
| - FROM_HERE,
|
| - base::Bind(&AudioEncoder::ImplBase::EncodeAudio,
|
| - impl_,
|
| - base::Passed(&audio_bus),
|
| - recorded_time));
|
| -}
|
| -
|
| -} // namespace cast
|
| -} // namespace media
|
|
|