Chromium Code Reviews| Index: remoting/protocol/audio_pump.cc |
| diff --git a/remoting/protocol/audio_pump.cc b/remoting/protocol/audio_pump.cc |
| index 66eff6ebff4d58946b7746163d2b9a1c32c2ef20..25daa905e12599276a0282134ef480830f460b53 100644 |
| --- a/remoting/protocol/audio_pump.cc |
| +++ b/remoting/protocol/audio_pump.cc |
| @@ -4,19 +4,93 @@ |
| #include "remoting/protocol/audio_pump.h" |
| +#include <memory> |
| #include <utility> |
| #include "base/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| +#include "base/memory/ptr_util.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| +#include "media/base/audio_bus.h" |
| +#include "media/base/audio_sample_types.h" |
| +#include "media/base/channel_layout.h" |
| +#include "media/base/channel_mixer.h" |
| #include "remoting/codec/audio_encoder.h" |
| #include "remoting/proto/audio.pb.h" |
| #include "remoting/protocol/audio_source.h" |
| #include "remoting/protocol/audio_stub.h" |
| +namespace { |
| + |
| +int CalculateFrameCount(const remoting::AudioPacket& packet) { |
| + return packet.data(0).size() / packet.channels() / packet.bytes_per_sample(); |
|
joedow
2017/05/26 16:01:50
Could an empty packet be passed in here (with the
Hzj_jie
2017/05/26 20:08:03
AudioPump uses an AudioSource interface. So assumi
|
| +} |
| + |
| +std::unique_ptr<media::AudioBus> ToAudioBus( |
|
joedow
2017/05/26 16:01:50
nit: Rename to 'PacketToAudioBus' ?
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + const remoting::AudioPacket& packet) { |
| + using namespace media; |
|
joedow
2017/05/26 16:01:50
The inline using directives are a bit repetitive.
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + const int frame_count = CalculateFrameCount(packet); |
| + if (frame_count <= 0) { |
|
joedow
2017/05/26 16:01:50
nit: 'frame_count < 1' is simpler
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + return nullptr; |
| + } |
| + if (static_cast<int>(packet.data(0).size()) >= |
| + AudioBus::CalculateMemorySize(packet.channels(), frame_count)) { |
| + // AudioBus::BuildChannelData() should receive const float* instead of |
| + // float*, meanwhile WrapMemory() function. |
|
joedow
2017/05/26 16:01:50
Can you fix this sentence fragment. The first bit
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + return media::AudioBus::WrapMemory( |
| + packet.channels(), |
| + frame_count, |
| + reinterpret_cast<int16_t*>(const_cast<char*>(packet.data(0).data()))); |
| + } |
| + |
| + std::unique_ptr<AudioBus> result = |
| + AudioBus::Create(packet.channels(), frame_count); |
| + result->FromInterleaved<SignedInt16SampleTypeTraits>( |
| + reinterpret_cast<const int16_t*>(packet.data(0).data()), frame_count); |
| + return result; |
| +} |
| + |
| +std::unique_ptr<remoting::AudioPacket> FromAudioBus( |
| + const media::AudioBus& packet) { |
| + using namespace media; |
| + using remoting::AudioPacket; |
| + std::unique_ptr<AudioPacket> result = base::MakeUnique<AudioPacket>(); |
| + result->add_data()->resize( |
| + AudioBus::CalculateMemorySize(packet.channels(), packet.frames()) / |
| + sizeof(float) * sizeof(int16_t)); |
| + packet.ToInterleaved<SignedInt16SampleTypeTraits>( |
| + packet.frames(), |
| + reinterpret_cast<int16_t*>(&(result->mutable_data(0)->at(0)))); |
| + return result; |
| +} |
| + |
| +media::ChannelLayout RetrieveLayout(const remoting::AudioPacket& packet) { |
| + // This switch should match AudioPacket::Channels enum in audio.proto. |
| + using namespace media; |
| + using remoting::AudioPacket; |
| + switch (packet.channels()) { |
| + case AudioPacket::CHANNELS_INVALID: |
| + return CHANNEL_LAYOUT_UNSUPPORTED; |
| + case AudioPacket::CHANNELS_MONO: |
| + return CHANNEL_LAYOUT_MONO; |
| + case AudioPacket::CHANNELS_STEREO: |
| + return CHANNEL_LAYOUT_STEREO; |
| + case AudioPacket::CHANNELS_5_1: |
| + return CHANNEL_LAYOUT_5_1; |
| + case AudioPacket::CHANNELS_6_1: |
| + return CHANNEL_LAYOUT_6_1; |
| + case AudioPacket::CHANNELS_7_1: |
| + return CHANNEL_LAYOUT_7_1; |
| + } |
| + NOTREACHED() << "Invalid AudioPacket::Channels"; |
| + return CHANNEL_LAYOUT_UNSUPPORTED; |
| +} |
| + |
| +} // namespace |
| + |
| namespace remoting { |
| namespace protocol { |
| @@ -36,6 +110,9 @@ class AudioPump::Core { |
| void OnPacketSent(int size); |
| private: |
| + std::unique_ptr<remoting::AudioPacket> Downmix( |
| + std::unique_ptr<remoting::AudioPacket> packet); |
|
joedow
2017/05/26 16:01:50
You don't need the remote prefix here since it is
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + |
| void EncodeAudioPacket(std::unique_ptr<AudioPacket> packet); |
| base::ThreadChecker thread_checker_; |
| @@ -53,6 +130,9 @@ class AudioPump::Core { |
| // yet. |
| int bytes_pending_; |
| + std::unique_ptr<media::ChannelMixer> mixer_ = nullptr; |
|
joedow
2017/05/26 16:01:50
No need to assign nullptr here since unique_ptr is
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + media::ChannelLayout last_input_layout_ = media::CHANNEL_LAYOUT_NONE; |
| + |
| DISALLOW_COPY_AND_ASSIGN(Core); |
| }; |
| @@ -101,6 +181,10 @@ void AudioPump::Core::EncodeAudioPacket(std::unique_ptr<AudioPacket> packet) { |
| if (!enabled_ || bytes_pending_ > max_buffered_bytes) |
| return; |
| + if (packet->channels() > 2) { |
| + packet = Downmix(std::move(packet)); |
| + } |
| + |
| std::unique_ptr<AudioPacket> encoded_packet = |
| audio_encoder_->Encode(std::move(packet)); |
| @@ -116,6 +200,45 @@ void AudioPump::Core::EncodeAudioPacket(std::unique_ptr<AudioPacket> packet) { |
| base::Passed(&encoded_packet), packet_size)); |
| } |
| +std::unique_ptr<AudioPacket> AudioPump::Core::Downmix( |
| + std::unique_ptr<AudioPacket> packet) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(packet); |
| + using namespace media; |
| + if (packet->data_size() != 1 || |
| + packet->data(0).empty() || |
| + packet->bytes_per_sample() != AudioPacket::BYTES_PER_SAMPLE_2) { |
| + return packet; |
| + } |
| + const ChannelLayout input_layout = RetrieveLayout(*packet); |
| + if (input_layout == CHANNEL_LAYOUT_UNSUPPORTED || |
| + input_layout == CHANNEL_LAYOUT_MONO || |
| + input_layout == CHANNEL_LAYOUT_STEREO) { |
| + return packet; |
| + } |
| + |
| + if (!mixer_ || last_input_layout_ != input_layout) { |
| + last_input_layout_ = input_layout; |
| + mixer_ = base::MakeUnique<ChannelMixer>( |
| + input_layout, CHANNEL_LAYOUT_STEREO); |
| + } |
| + |
| + std::unique_ptr<AudioBus> input = ToAudioBus(*packet); |
| + if (!input) { |
| + return packet; |
| + } |
| + std::unique_ptr<AudioBus> output = AudioBus::Create(2, input->frames()); |
|
joedow
2017/05/26 16:01:50
Can you replace the magic '2' with AudioPacket::CH
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + mixer_->Transform(input.get(), output.get()); |
| + |
| + std::unique_ptr<AudioPacket> result = FromAudioBus(*output); |
| + DCHECK(result); |
|
joedow
2017/05/26 16:01:50
I don't think this DCHECK is needed here as the ne
Hzj_jie
2017/05/26 20:08:03
Done.
|
| + result->set_encoding(AudioPacket::ENCODING_RAW); |
| + result->set_sampling_rate(packet->sampling_rate()); |
| + result->set_bytes_per_sample(packet->bytes_per_sample()); |
| + result->set_channels(AudioPacket::CHANNELS_STEREO); |
| + return result; |
| +} |
| + |
| AudioPump::AudioPump( |
| scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner, |
| std::unique_ptr<AudioSource> audio_source, |