Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(489)

Unified Diff: remoting/protocol/audio_pump.cc

Issue 2903153004: [Chromoting] Implement down mixing in AudioPump (Closed)
Patch Set: Add more comments to explain the downmix and layout selection logic Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/protocol/audio_pump.h ('k') | remoting/protocol/audio_pump_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/protocol/audio_pump.cc
diff --git a/remoting/protocol/audio_pump.cc b/remoting/protocol/audio_pump.cc
index 66eff6ebff4d58946b7746163d2b9a1c32c2ef20..609ec51e2e7d60ee12fe91998ef0193619369ea4 100644
--- a/remoting/protocol/audio_pump.cc
+++ b/remoting/protocol/audio_pump.cc
@@ -4,19 +4,96 @@
#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();
+}
+
+std::unique_ptr<media::AudioBus> AudioPacketToAudioBus(
+ const remoting::AudioPacket& packet) {
+ const int frame_count = CalculateFrameCount(packet);
+ if (frame_count < 1) {
Sergey Ulanov 2017/06/02 18:34:36 packet.data(0).is_empty()
Sergey Ulanov 2017/06/02 18:34:36 Why would we get an empty packet here? Can it be r
Hzj_jie 2017/06/02 22:11:25 (frame_count == 0) != (packet.data(0).empty()) Con
Hzj_jie 2017/06/02 22:11:25 It's not an empty packet, but a defeated packet.
Sergey Ulanov 2017/06/05 18:03:19 Then this should be a DCHECK. We own AudioCapturer
Hzj_jie 2017/06/06 03:21:42 I still doubt this decision, especially the DCHECK
Sergey Ulanov 2017/06/07 06:24:59 Do you have an examples when that would make a dif
Hzj_jie 2017/06/07 20:27:18 IMO, changing the following or similar pattern: ==
Sergey Ulanov 2017/06/07 21:16:29 See https://chromium.googlesource.com/chromium/src
+ return nullptr;
+ }
+ if (static_cast<int>(packet.data(0).size()) >=
+ media::AudioBus::CalculateMemorySize(packet.channels(), frame_count)) {
+ // TODO(zijiehe): AudioBus::BuildChannelData() and WrapMemory() should
+ // receive "const float*" instead of "float*". We can remove the const_cast
+ // afterwards.
+ return media::AudioBus::WrapMemory(
Sergey Ulanov 2017/06/02 18:34:36 Does this work correctly remoting::AudioPacket alw
Hzj_jie 2017/06/02 22:11:25 I just realize this condition won't meet anyway. I
+ packet.channels(),
+ frame_count,
+ reinterpret_cast<int16_t*>(const_cast<char*>(packet.data(0).data())));
Sergey Ulanov 2017/06/02 18:34:37 You can use string_as_array() from base/stl_util.h
Hzj_jie 2017/06/02 22:11:25 Done.
+ }
+
+ std::unique_ptr<media::AudioBus> result =
+ media::AudioBus::Create(packet.channels(), frame_count);
+ result->FromInterleaved<media::SignedInt16SampleTypeTraits>(
+ reinterpret_cast<const int16_t*>(packet.data(0).data()), frame_count);
+ return result;
+}
+
+std::unique_ptr<remoting::AudioPacket> AudioBusToAudioPacket(
+ const media::AudioBus& packet) {
+ std::unique_ptr<remoting::AudioPacket> result =
+ base::MakeUnique<remoting::AudioPacket>();
+ result->add_data()->resize(
+ media::AudioBus::CalculateMemorySize(packet.channels(), packet.frames()) /
+ sizeof(float) * sizeof(int16_t));
Sergey Ulanov 2017/06/02 18:34:36 I don't think we want to use AudioBus::CalculateMe
Hzj_jie 2017/06/02 22:11:25 Done.
+ packet.ToInterleaved<media::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.
+ switch (packet.channels()) {
+ case remoting::AudioPacket::CHANNELS_INVALID:
+ return media::CHANNEL_LAYOUT_UNSUPPORTED;
+ case remoting::AudioPacket::CHANNELS_MONO:
+ return media::CHANNEL_LAYOUT_MONO;
+ case remoting::AudioPacket::CHANNELS_STEREO:
+ return media::CHANNEL_LAYOUT_STEREO;
+ case remoting::AudioPacket::CHANNELS_SURROUND:
+ return media::CHANNEL_LAYOUT_SURROUND;
+ case remoting::AudioPacket::CHANNELS_QUAD:
+ return media::CHANNEL_LAYOUT_QUAD;
Sergey Ulanov 2017/06/02 18:34:36 I don't think that downmixing all 4-channel layout
Hzj_jie 2017/06/02 22:11:25 Done.
+ case remoting::AudioPacket::CHANNELS_4_1:
+ return media::CHANNEL_LAYOUT_4_1;
+ case remoting::AudioPacket::CHANNELS_5_1:
+ return media::CHANNEL_LAYOUT_5_1;
+ case remoting::AudioPacket::CHANNELS_6_1:
+ return media::CHANNEL_LAYOUT_6_1;
+ case remoting::AudioPacket::CHANNELS_7_1:
+ return media::CHANNEL_LAYOUT_7_1;
+ }
+ NOTREACHED() << "Invalid AudioPacket::Channels";
+ return media::CHANNEL_LAYOUT_UNSUPPORTED;
+}
+
+} // namespace
+
namespace remoting {
namespace protocol {
@@ -36,6 +113,8 @@ class AudioPump::Core {
void OnPacketSent(int size);
private:
+ std::unique_ptr<AudioPacket> Downmix(std::unique_ptr<AudioPacket> packet);
+
void EncodeAudioPacket(std::unique_ptr<AudioPacket> packet);
base::ThreadChecker thread_checker_;
@@ -53,6 +132,9 @@ class AudioPump::Core {
// yet.
int bytes_pending_;
+ std::unique_ptr<media::ChannelMixer> mixer_;
+ media::ChannelLayout last_input_layout_ = media::CHANNEL_LAYOUT_NONE;
+
DISALLOW_COPY_AND_ASSIGN(Core);
};
@@ -98,8 +180,13 @@ void AudioPump::Core::EncodeAudioPacket(std::unique_ptr<AudioPacket> packet) {
int max_buffered_bytes =
audio_encoder_->GetBitrate() * kMaxBufferedIntervalMs / 1000 / 8;
- if (!enabled_ || bytes_pending_ > max_buffered_bytes)
+ if (!enabled_ || bytes_pending_ > max_buffered_bytes) {
return;
+ }
+
+ if (packet->channels() > AudioPacket::CHANNELS_STEREO) {
+ packet = Downmix(std::move(packet));
+ }
std::unique_ptr<AudioPacket> encoded_packet =
audio_encoder_->Encode(std::move(packet));
@@ -116,6 +203,44 @@ 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);
+ if (packet->data_size() != 1 ||
+ packet->data(0).empty() ||
+ packet->bytes_per_sample() != AudioPacket::BYTES_PER_SAMPLE_2) {
Sergey Ulanov 2017/06/02 18:34:36 I think all these checks can be DCHECKs
Hzj_jie 2017/06/02 22:11:25 I have removed packet->data(0).empty() check, it w
+ return packet;
+ }
+ const media::ChannelLayout input_layout = RetrieveLayout(*packet);
+ if (input_layout == media::CHANNEL_LAYOUT_UNSUPPORTED ||
Sergey Ulanov 2017/06/02 18:34:37 I think in case the layout is unsupported it doesn
Hzj_jie 2017/06/02 22:11:25 Done. But IMO, unsupported by media::ChannelMixer
+ input_layout == media::CHANNEL_LAYOUT_MONO ||
+ input_layout == media::CHANNEL_LAYOUT_STEREO) {
+ return packet;
+ }
+
+ if (!mixer_ || last_input_layout_ != input_layout) {
Sergey Ulanov 2017/06/02 18:34:36 It's better to move this code above mono/stereo ch
Hzj_jie 2017/06/02 22:11:25 If the input_layout is mono or stereo, we won't ne
+ last_input_layout_ = input_layout;
+ mixer_ = base::MakeUnique<media::ChannelMixer>(
+ input_layout, media::CHANNEL_LAYOUT_STEREO);
+ }
+
+ std::unique_ptr<media::AudioBus> input = AudioPacketToAudioBus(*packet);
+ if (!input) {
+ return packet;
Sergey Ulanov 2017/06/02 18:34:36 Why would AudioPacketToAudioBus() fail? If it does
Hzj_jie 2017/06/02 22:11:25 A corrupted data without a frame.
+ }
+ std::unique_ptr<media::AudioBus> output =
+ media::AudioBus::Create(AudioPacket::CHANNELS_STEREO, input->frames());
+ mixer_->Transform(input.get(), output.get());
+
+ std::unique_ptr<AudioPacket> result = AudioBusToAudioPacket(*output);
+ 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);
Sergey Ulanov 2017/06/02 18:34:37 I think it would be best to set all these properti
Hzj_jie 2017/06/02 22:11:24 There is no sampling rate in AudioBus, so I leave
+ return result;
+}
+
AudioPump::AudioPump(
scoped_refptr<base::SingleThreadTaskRunner> audio_task_runner,
std::unique_ptr<AudioSource> audio_source,
« no previous file with comments | « remoting/protocol/audio_pump.h ('k') | remoting/protocol/audio_pump_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698