[go: nahoru, domu]

blob: 4daefa342451e01a295b7f7cb5eb7b942e2b1608 [file] [log] [blame]
// 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,
uint32_t frames_delayed) 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*>(base::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,
uint32_t frames_delayed) {
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(
base::MakeRefCounted<WiFiDisplayAudioEncoderLPCM>(audio_codec));
}
} // namespace extensions