| Index: extensions/renderer/api/display_source/wifi_display/wifi_display_audio_encoder_lpcm.cc
|
| diff --git a/extensions/renderer/api/display_source/wifi_display/wifi_display_audio_encoder_lpcm.cc b/extensions/renderer/api/display_source/wifi_display/wifi_display_audio_encoder_lpcm.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4e91de25f99c835f47484e87b2552d08263216a2
|
| --- /dev/null
|
| +++ b/extensions/renderer/api/display_source/wifi_display/wifi_display_audio_encoder_lpcm.cc
|
| @@ -0,0 +1,237 @@
|
| +// Copyright 2016 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 "extensions/renderer/api/display_source/wifi_display/wifi_display_audio_encoder.h"
|
| +
|
| +#include <memory>
|
| +#include <vector>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/stl_util.h"
|
| +#include "base/sys_byteorder.h"
|
| +#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_descriptor.h"
|
| +#include "extensions/renderer/api/display_source/wifi_display/wifi_display_elementary_stream_info.h"
|
| +#include "extensions/renderer/api/display_source/wifi_display/wifi_display_media_packetizer.h"
|
| +#include "media/base/audio_bus.h"
|
| +#include "media/base/audio_converter.h"
|
| +#include "media/base/audio_parameters.h"
|
| +
|
| +namespace extensions {
|
| +
|
| +namespace {
|
| +
|
| +using LPCMAudioStreamDescriptor =
|
| + WiFiDisplayElementaryStreamDescriptor::LPCMAudioStream;
|
| +
|
| +// This audio encoder implements Linear Pulse-Code Modulation (LPCM) audio
|
| +// encoding.
|
| +class WiFiDisplayAudioEncoderLPCM final
|
| + : public WiFiDisplayAudioEncoder,
|
| + private media::AudioConverter::InputCallback {
|
| + public:
|
| + enum {
|
| + kOutputBitsPerSample = 16,
|
| + kOutputBytesPerSample = kOutputBitsPerSample / 8,
|
| + kOutputChannels = 2
|
| + };
|
| +
|
| + explicit WiFiDisplayAudioEncoderLPCM(const wds::AudioCodec& audio_codec);
|
| +
|
| + protected:
|
| + ~WiFiDisplayAudioEncoderLPCM() override = default;
|
| +
|
| + // WiFiDisplayMediaEncoder
|
| + WiFiDisplayElementaryStreamInfo CreateElementaryStreamInfo() const override;
|
| +
|
| + // content::MediaStreamAudioSink
|
| + void OnData(const media::AudioBus& input_bus,
|
| + base::TimeTicks estimated_capture_time) override;
|
| + void OnSetFormat(const media::AudioParameters& params) override;
|
| +
|
| + // media::AudioConverter::InputCallback
|
| + double ProvideInput(media::AudioBus* audio_bus,
|
| + base::TimeDelta buffer_delay) override;
|
| +
|
| + LPCMAudioStreamDescriptor::SamplingFrequency GetOutputSamplingFrequency()
|
| + const;
|
| +
|
| + private:
|
| + const int output_sample_rate_;
|
| +
|
| + // These members are accessed on the real-time audio time only.
|
| + std::unique_ptr<media::AudioConverter> converter_;
|
| + const media::AudioBus* current_input_bus_;
|
| + int64_t input_frames_in_;
|
| + media::AudioParameters input_params_;
|
| + std::unique_ptr<media::AudioBus> fifo_bus_;
|
| + int fifo_end_frame_;
|
| + int64_t fifo_frames_out_;
|
| +};
|
| +
|
| +WiFiDisplayAudioEncoderLPCM::WiFiDisplayAudioEncoderLPCM(
|
| + const wds::AudioCodec& audio_codec)
|
| + : WiFiDisplayAudioEncoder(audio_codec),
|
| + output_sample_rate_(
|
| + GetOutputSamplingFrequency() ==
|
| + LPCMAudioStreamDescriptor::SAMPLING_FREQUENCY_48K
|
| + ? 48000
|
| + : 44100),
|
| + current_input_bus_(nullptr),
|
| + input_frames_in_(0),
|
| + fifo_end_frame_(0),
|
| + fifo_frames_out_(0) {}
|
| +
|
| +WiFiDisplayElementaryStreamInfo
|
| +WiFiDisplayAudioEncoderLPCM::CreateElementaryStreamInfo() const {
|
| + DCHECK(client_thread_checker_.CalledOnValidThread());
|
| + std::vector<WiFiDisplayElementaryStreamDescriptor> descriptors;
|
| + descriptors.push_back(LPCMAudioStreamDescriptor::Create(
|
| + GetOutputSamplingFrequency(),
|
| + LPCMAudioStreamDescriptor::BITS_PER_SAMPLE_16,
|
| + false, // emphasis_flag
|
| + LPCMAudioStreamDescriptor::NUMBER_OF_CHANNELS_STEREO));
|
| + return WiFiDisplayElementaryStreamInfo(
|
| + WiFiDisplayElementaryStreamInfo::AUDIO_LPCM, std::move(descriptors));
|
| +}
|
| +
|
| +// Called on real-time audio thread.
|
| +void WiFiDisplayAudioEncoderLPCM::OnData(
|
| + const media::AudioBus& input_bus,
|
| + base::TimeTicks estimated_capture_time) {
|
| + DCHECK(input_params_.IsValid());
|
| + DCHECK_EQ(input_bus.channels(), input_params_.channels());
|
| + DCHECK_EQ(input_bus.frames(), input_params_.frames_per_buffer());
|
| + DCHECK(!estimated_capture_time.is_null());
|
| +
|
| + const media::AudioBus* source_bus = &input_bus;
|
| +
|
| + std::unique_ptr<media::AudioBus> converted_input_bus;
|
| + if (converter_) {
|
| + // Convert the entire input signal.
|
| + // Note that while the number of sample frames provided as input is always
|
| + // the same, the chunk size (and the size of the |converted_input_bus|
|
| + // here) can be variable.
|
| + converted_input_bus =
|
| + media::AudioBus::Create(kOutputChannels, converter_->ChunkSize());
|
| + current_input_bus_ = &input_bus;
|
| + converter_->Convert(converted_input_bus.get());
|
| + DCHECK(!current_input_bus_);
|
| + source_bus = converted_input_bus.get();
|
| + }
|
| +
|
| + // Loop in order to handle frame number differences between |source_bus| and
|
| + // |fifo_bus_|.
|
| + int source_start_frame = 0;
|
| + do {
|
| + // Copy as many source frames (either raw or converted input frames) as
|
| + // possible to |fifo_bus_|.
|
| + int frame_count = std::min(source_bus->frames() - source_start_frame,
|
| + fifo_bus_->frames() - fifo_end_frame_);
|
| + DCHECK_GT(frame_count, 0);
|
| + source_bus->CopyPartialFramesTo(source_start_frame, frame_count,
|
| + fifo_end_frame_, fifo_bus_.get());
|
| + fifo_end_frame_ += frame_count;
|
| + source_start_frame += frame_count;
|
| +
|
| + if (fifo_end_frame_ == fifo_bus_->frames()) {
|
| + // There are enough frames in |fifo_bus_| for one encoded unit.
|
| +
|
| + // Determine the duration of the audio signal enqueued within
|
| + // |converter_| and |fifo_bus_|.
|
| + const base::TimeDelta signal_duration_already_buffered =
|
| + (input_frames_in_ * base::TimeDelta::FromSeconds(1) /
|
| + input_params_.sample_rate()) -
|
| + (fifo_frames_out_ * base::TimeDelta::FromSeconds(1) /
|
| + output_sample_rate_);
|
| + DVLOG(2) << "Audio reference time adjustment: -("
|
| + << signal_duration_already_buffered.InMicroseconds() << " us)";
|
| + const base::TimeTicks capture_time_of_first_converted_sample =
|
| + estimated_capture_time - signal_duration_already_buffered;
|
| +
|
| + // Encode frames in |fifo_bus_|.
|
| + std::string data;
|
| + int sample_count = fifo_bus_->channels() * fifo_bus_->frames();
|
| + data.resize(sample_count * sizeof(uint16_t));
|
| + uint16_t* encoded_samples =
|
| + reinterpret_cast<uint16_t*>(string_as_array(&data));
|
| + fifo_bus_->ToInterleaved(fifo_bus_->frames(), kOutputBytesPerSample,
|
| + encoded_samples);
|
| + for (int i = 0; i < sample_count; ++i)
|
| + encoded_samples[i] = base::HostToNet16(encoded_samples[i]);
|
| + fifo_end_frame_ = 0;
|
| + fifo_frames_out_ += fifo_bus_->frames();
|
| +
|
| + // Pass the encoded unit to the client.
|
| + encoded_callback_.Run(
|
| + std::unique_ptr<WiFiDisplayEncodedUnit>(new WiFiDisplayEncodedUnit(
|
| + std::move(data), capture_time_of_first_converted_sample, true)));
|
| + }
|
| + } while (source_start_frame < source_bus->frames());
|
| +}
|
| +
|
| +// Called on real-time audio thread.
|
| +void WiFiDisplayAudioEncoderLPCM::OnSetFormat(
|
| + const media::AudioParameters& params) {
|
| + if (input_params_.Equals(params))
|
| + return;
|
| + input_params_ = params;
|
| +
|
| + if (output_sample_rate_ != input_params_.sample_rate()) {
|
| + const media::AudioParameters output_params(
|
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| + media::CHANNEL_LAYOUT_STEREO, output_sample_rate_, kOutputBitsPerSample,
|
| + WiFiDisplayMediaPacketizer::LPCM::kChannelSamplesPerUnit);
|
| + DVLOG(2) << "Setting up audio resampling: "
|
| + << input_params_.sample_rate() << " Hz --> "
|
| + << output_sample_rate_ << " Hz";
|
| + converter_.reset(
|
| + new media::AudioConverter(input_params_, output_params, false));
|
| + converter_->AddInput(this);
|
| + } else {
|
| + // Do not use an AudioConverter if that is not needed in order to avoid
|
| + // additional copyings caused by additional intermediate audio busses
|
| + // needed for capturing enough samples for each encoded unit.
|
| + converter_.reset();
|
| + }
|
| +
|
| + fifo_bus_ = media::AudioBus::Create(
|
| + kOutputChannels,
|
| + WiFiDisplayMediaPacketizer::LPCM::kChannelSamplesPerUnit);
|
| + fifo_end_frame_ = 0;
|
| + fifo_frames_out_ = 0;
|
| + input_frames_in_ = 0;
|
| +}
|
| +
|
| +// Called on real-time audio thread by |converter_| invoked by |OnData|.
|
| +double WiFiDisplayAudioEncoderLPCM::ProvideInput(media::AudioBus* audio_bus,
|
| + base::TimeDelta buffer_delay) {
|
| + DCHECK(current_input_bus_);
|
| + current_input_bus_->CopyTo(audio_bus);
|
| + current_input_bus_ = nullptr;
|
| + return 1.0;
|
| +}
|
| +
|
| +LPCMAudioStreamDescriptor::SamplingFrequency
|
| +WiFiDisplayAudioEncoderLPCM::GetOutputSamplingFrequency() const {
|
| + switch (GetAudioCodecMode()) {
|
| + case wds::LPCM_44_1K_16B_2CH:
|
| + return LPCMAudioStreamDescriptor::SAMPLING_FREQUENCY_44_1K;
|
| + case wds::LPCM_48K_16B_2CH:
|
| + return LPCMAudioStreamDescriptor::SAMPLING_FREQUENCY_48K;
|
| + default:
|
| + NOTREACHED();
|
| + return LPCMAudioStreamDescriptor::SAMPLING_FREQUENCY_44_1K;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +void WiFiDisplayAudioEncoder::CreateLPCM(
|
| + const wds::AudioCodec& audio_codec,
|
| + const AudioEncoderCallback& encoder_callback) {
|
| + encoder_callback.Run(
|
| + make_scoped_refptr(new WiFiDisplayAudioEncoderLPCM(audio_codec)));
|
| +}
|
| +
|
| +} // namespace extensions
|
|
|