Revert deleting vsoc
Bug: 144973127
Test: git diff 698c8df9e00602c8cd82bbcc872693509a169fe9
Change-Id: I19d15f093a69e18f6fafa00e4808620297d37df8
diff --git a/host/frontend/Android.bp b/host/frontend/Android.bp
index 8d78a9f..5a053e7 100644
--- a/host/frontend/Android.bp
+++ b/host/frontend/Android.bp
@@ -14,6 +14,7 @@
// limitations under the License.
subdirs = [
+ "stream_audio",
"vnc_server",
"adb_connector",
]
diff --git a/host/frontend/stream_audio/Android.bp b/host/frontend/stream_audio/Android.bp
new file mode 100644
index 0000000..8f07536
--- /dev/null
+++ b/host/frontend/stream_audio/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary_host {
+ name: "stream_audio",
+ srcs: [
+ "main.cpp",
+ ],
+ shared_libs: [
+ "cuttlefish_tcp_socket",
+ "libbase",
+ "libcuttlefish_utils",
+ "libcuttlefish_fs",
+ "libopus",
+ "vsoc_lib",
+ ],
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libjsoncpp",
+ "libgflags",
+ "libopuscpp",
+ ],
+ cpp_std: "c++17",
+ enabled: false,
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/stream_audio/main.cpp b/host/frontend/stream_audio/main.cpp
new file mode 100644
index 0000000..f63e315
--- /dev/null
+++ b/host/frontend/stream_audio/main.cpp
@@ -0,0 +1,270 @@
+/*
+ *
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// For each client that connects initially a header is sent with the following,
+// in this order, all as uint16_t in network-byte-order:
+// number of channels, frame rate
+//
+// Following, audio packets are sent as a uint32_t length (network byte order)
+// indicating the number of bytes
+// followed by the (opus) frame_size as a uint32_t
+// followed by <length> bytes.
+
+#include "common/libs/tcp_socket/tcp_socket.h"
+#include "common/vsoc/lib/audio_data_region_view.h"
+#include "common/vsoc/lib/circqueue_impl.h"
+#include "common/vsoc/lib/vsoc_audio_message.h"
+#include "host/frontend/stream_audio/opuscpp/opus_wrapper.h"
+#include "host/libs/config/cuttlefish_config.h"
+
+#include <android-base/logging.h>
+#include <gflags/gflags.h>
+
+#include <arpa/inet.h>
+
+#include <cstdint>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+using vsoc::audio_data::AudioDataRegionView;
+
+DEFINE_int32(port, 0, "port on which to serve audio.");
+
+namespace {
+
+// Read audio frames from the AudioDataRegionView
+class AudioStreamer {
+ public:
+ cvd::Message MakeAudioDescriptionHeader() const {
+ std::unique_lock guard(buffer_lock_);
+ while (!audio_buffer_) {
+ buffer_cv_.wait(guard);
+ }
+
+ const size_t num_channels = header_.frame_size / sizeof(opus_int16);
+ return cvd::CreateMessage(static_cast<std::uint16_t>(num_channels),
+ static_cast<std::uint16_t>(header_.frame_rate));
+ }
+
+ std::uint32_t frame_rate() const {
+ std::unique_lock guard(buffer_lock_);
+ while (!audio_buffer_) {
+ buffer_cv_.wait(guard);
+ }
+ return header_.frame_rate;
+ }
+
+ std::uint32_t num_channels() const {
+ std::unique_lock guard(buffer_lock_);
+ while (!audio_buffer_) {
+ buffer_cv_.wait(guard);
+ }
+ return header_.frame_size / sizeof(opus_int16);
+ }
+
+ // Returns the frame id and audio frame
+ std::tuple<std::int64_t, std::shared_ptr<const cvd::Message>> audio_buffer(
+ std::int64_t previous_frame_num) const {
+ std::unique_lock guard(buffer_lock_);
+ while (header_.frame_num <= previous_frame_num) {
+ buffer_cv_.wait(guard);
+ }
+
+ return {header_.frame_num, audio_buffer_};
+ }
+
+ void Update() {
+ auto audio_data_rv =
+ AudioDataRegionView::GetInstance(vsoc::GetDomain().c_str());
+ auto worker = audio_data_rv->StartWorker();
+ std::vector<char> new_buffer;
+
+ while (true) {
+ new_buffer.resize(new_buffer.capacity());
+
+ auto [new_header, payload_size, audio_data] =
+ NextAudioMessage(audio_data_rv, &new_buffer);
+
+ LOG(DEBUG) << "stream " << new_header.stream_number << ", frame "
+ << new_header.frame_num << ", rate " << new_header.frame_rate
+ << ", channel_mask " << new_header.channel_mask << ", format "
+ << new_header.format << ", payload_size " << payload_size
+ << '\n';
+
+ {
+ std::lock_guard guard(buffer_lock_);
+ CheckAudioConfigurationIsSame(new_header);
+ header_ = new_header;
+ audio_buffer_ = std::make_shared<const cvd::Message>(
+ audio_data, audio_data + payload_size);
+ }
+ buffer_cv_.notify_all();
+ }
+ }
+
+ private:
+ struct AudioMessage {
+ gce_audio_message header;
+ std::size_t payload_size;
+ const std::uint8_t* payload_data;
+ };
+
+ void ReadAudioMessage(AudioDataRegionView* audio_data_rv,
+ std::vector<char>* buffer) const {
+ while (true) {
+ auto read_size = audio_data_rv->data()->audio_queue.Read(
+ audio_data_rv, buffer->data(), buffer->size());
+ if (read_size == -ENOSPC) {
+ DoubleSize(buffer);
+ } else if (read_size < 0) {
+ LOG(ERROR) << "CircularPacketQueue::Read returned " << read_size;
+ } else {
+ buffer->resize(read_size);
+ return;
+ }
+ }
+ }
+
+ void DoubleSize(std::vector<char>* buffer) const {
+ if (buffer->empty()) {
+ buffer->resize(1);
+ } else {
+ buffer->resize(buffer->size() * 2);
+ }
+ }
+
+ gce_audio_message GetHeaderFromBuffer(const std::vector<char>& buffer) const {
+ gce_audio_message new_header{};
+ CHECK_GE(buffer.size(), sizeof new_header);
+
+ std::memcpy(&new_header, buffer.data(), sizeof new_header);
+ CHECK_GT(new_header.stream_number, 0u);
+ return new_header;
+ }
+
+ std::tuple<std::size_t, const std::uint8_t*> GetPayloadFromBuffer(
+ const std::vector<char>& buffer) const {
+ const auto payload_size = buffer.size() - sizeof(gce_audio_message);
+ const auto* audio_data =
+ reinterpret_cast<const std::uint8_t*>(buffer.data()) +
+ sizeof(gce_audio_message);
+ return {payload_size, audio_data};
+ }
+
+ AudioMessage NextAudioMessage(AudioDataRegionView* audio_data_rv,
+ std::vector<char>* buffer) const {
+ while (true) {
+ ReadAudioMessage(audio_data_rv, buffer);
+ auto header = GetHeaderFromBuffer(*buffer);
+ if (header.message_type == gce_audio_message::DATA_SAMPLES) {
+ auto [payload_size, payload_data] = GetPayloadFromBuffer(*buffer);
+ return {header, payload_size, payload_data};
+ }
+ }
+ }
+
+ void CheckAudioConfigurationIsSame(
+ const gce_audio_message& new_header) const {
+ if (audio_buffer_) {
+ CHECK_EQ(header_.frame_size, new_header.frame_size)
+ << "audio frame_size changed";
+ CHECK_EQ(header_.frame_rate, new_header.frame_rate)
+ << "audio frame_rate changed";
+ CHECK_EQ(header_.stream_number, new_header.stream_number)
+ << "audio stream_number changed";
+ }
+ }
+
+ std::shared_ptr<const cvd::Message> audio_buffer_{};
+ gce_audio_message header_{};
+ mutable std::mutex buffer_lock_;
+ mutable std::condition_variable buffer_cv_;
+};
+
+void HandleClient(AudioStreamer* audio_streamer,
+ cvd::ClientSocket client_socket) {
+ auto num_channels = audio_streamer->num_channels();
+ opus::Encoder enc(audio_streamer->frame_rate(),
+ audio_streamer->num_channels(), OPUS_APPLICATION_AUDIO);
+ CHECK(enc.valid()) << "Could not construct Encoder. Maybe bad frame_rate ("
+ << audio_streamer->frame_rate() <<") or num_channels ("
+ << audio_streamer->num_channels() << ")?";
+
+ auto header = audio_streamer->MakeAudioDescriptionHeader();
+ client_socket.SendNoSignal(header);
+ std::int64_t previous_frame_num = 0;
+
+ while (!client_socket.closed()) {
+ CHECK(enc.valid()) << "encoder in invalid state";
+ auto [frame_num, audio_data] =
+ audio_streamer->audio_buffer(previous_frame_num);
+ previous_frame_num = frame_num;
+
+ std::vector<opus_int16> pcm(audio_data->size() / sizeof(opus_int16));
+ std::memcpy(pcm.data(), audio_data->data(), audio_data->size());
+ // in opus terms "frame_size" is the number of unencoded samples per frame
+ const std::uint32_t frame_size = pcm.size() / num_channels;
+ auto encoded = enc.Encode(pcm, frame_size);
+ for (auto&& p : encoded) {
+ auto length_message =
+ cvd::CreateMessage(static_cast<std::uint32_t>(p.size()));
+ client_socket.SendNoSignal(length_message);
+ client_socket.SendNoSignal(cvd::CreateMessage(frame_size));
+ client_socket.SendNoSignal(p);
+ }
+ }
+}
+
+[[noreturn]] void AudioStreamerUpdateLoop(AudioStreamer* audio_streamer) {
+ while (true) {
+ audio_streamer->Update();
+ }
+}
+
+[[noreturn]] void MainLoop() {
+ AudioStreamer audio_streamer;
+ std::thread audio_streamer_update_thread;
+ auto server = cvd::ServerSocket(FLAGS_port);
+ while (true) {
+ LOG(INFO) << "waiting for client connection";
+ auto client = server.Accept();
+ LOG(INFO) << "client socket accepted";
+ if (!audio_streamer_update_thread.joinable()) {
+ audio_streamer_update_thread =
+ std::thread{AudioStreamerUpdateLoop, &audio_streamer};
+ }
+ std::thread(HandleClient, &audio_streamer, std::move(client)).detach();
+ }
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ gflags::SetUsageMessage(" ");
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ if (FLAGS_port <= 0) {
+ std::cerr << "--port must be specified.\n";
+ return 1;
+ }
+ MainLoop();
+}
diff --git a/host/frontend/stream_audio/opuscpp/Android.bp b/host/frontend/stream_audio/opuscpp/Android.bp
new file mode 100644
index 0000000..9ad7a52
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_host_static {
+ name: "libopuscpp",
+ srcs: [
+ "opus_wrapper.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ "libopus",
+ ],
+ cpp_std: "c++17",
+ enabled: false,
+ defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/frontend/stream_audio/opuscpp/opus_wrapper.cc b/host/frontend/stream_audio/opuscpp/opus_wrapper.cc
new file mode 100644
index 0000000..538bee8
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/opus_wrapper.cc
@@ -0,0 +1,177 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://github.com/google/opuscpp
+
+#include <iterator>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include "host/frontend/stream_audio/opuscpp/opus_wrapper.h"
+
+std::string opus::ErrorToString(int error) {
+ switch (error) {
+ case OPUS_OK:
+ return "OK";
+ case OPUS_BAD_ARG:
+ return "One or more invalid/out of range arguments.";
+ case OPUS_BUFFER_TOO_SMALL:
+ return "The mode struct passed is invalid.";
+ case OPUS_INTERNAL_ERROR:
+ return "An internal error was detected.";
+ case OPUS_INVALID_PACKET:
+ return "The compressed data passed is corrupted.";
+ case OPUS_UNIMPLEMENTED:
+ return "Invalid/unsupported request number.";
+ case OPUS_INVALID_STATE:
+ return "An encoder or decoder structure is invalid or already freed.";
+ default:
+ return "Unknown error code: " + std::to_string(error);
+ }
+}
+
+void opus::internal::OpusDestroyer::operator()(OpusEncoder* encoder) const
+ noexcept {
+ opus_encoder_destroy(encoder);
+}
+
+void opus::internal::OpusDestroyer::operator()(OpusDecoder* decoder) const
+ noexcept {
+ opus_decoder_destroy(decoder);
+}
+
+opus::Encoder::Encoder(opus_int32 sample_rate, int num_channels,
+ int application, int expected_loss_percent)
+ : num_channels_{num_channels} {
+ int error{};
+ encoder_.reset(
+ opus_encoder_create(sample_rate, num_channels, application, &error));
+ valid_ = error == OPUS_OK;
+ if (!valid()) {
+ LOG(INFO) << "Could not construct encoder. Error: " << ErrorToString(error);
+ return;
+ }
+ if (expected_loss_percent > 0) {
+ LOG(INFO) << "Enabling FEC in the encoder.";
+ Ctl(OPUS_SET_INBAND_FEC(1));
+ Ctl(OPUS_SET_PACKET_LOSS_PERC(expected_loss_percent));
+ }
+}
+
+bool opus::Encoder::ResetState() {
+ valid_ = Ctl(OPUS_RESET_STATE) == OPUS_OK;
+ return valid_;
+}
+
+bool opus::Encoder::SetBitrate(int bitrate) {
+ valid_ = Ctl(OPUS_SET_BITRATE(bitrate)) == OPUS_OK;
+ return valid_;
+}
+
+bool opus::Encoder::SetVariableBitrate(int vbr) {
+ valid_ = Ctl(OPUS_SET_VBR(vbr)) == OPUS_OK;
+ return valid_;
+}
+
+bool opus::Encoder::SetComplexity(int complexity) {
+ valid_ = Ctl(OPUS_SET_COMPLEXITY(complexity)) == OPUS_OK;
+ return valid_;
+}
+
+int opus::Encoder::GetLookahead() {
+ opus_int32 skip{};
+ valid_ = Ctl(OPUS_GET_LOOKAHEAD(&skip)) == OPUS_OK;
+ return skip;
+}
+
+std::vector<std::vector<unsigned char>> opus::Encoder::Encode(
+ const std::vector<opus_int16>& pcm, int frame_size) {
+ constexpr auto sample_size = sizeof(pcm[0]);
+ const auto frame_length = frame_size * num_channels_ * sample_size;
+ auto data_length = pcm.size() * sample_size;
+ if (data_length % frame_length != 0u) {
+ LOG(WARNING) << "PCM samples contain an incomplete frame. Ignoring the "
+ "incomplete frame.";
+ data_length -= (data_length % frame_length);
+ }
+
+ std::vector<std::vector<unsigned char>> encoded;
+ for (std::size_t i{}; i < data_length; i += frame_length) {
+ encoded.push_back(EncodeFrame(pcm.begin() + (i / sample_size), frame_size));
+ }
+ return encoded;
+}
+
+std::vector<unsigned char> opus::Encoder::EncodeFrame(
+ const std::vector<opus_int16>::const_iterator& frame_start,
+ int frame_size) {
+ const auto frame_length = (frame_size * num_channels_ * sizeof(*frame_start));
+ std::vector<unsigned char> encoded(frame_length);
+ auto num_bytes = opus_encode(encoder_.get(), &*frame_start, frame_size,
+ encoded.data(), encoded.size());
+ if (num_bytes < 0) {
+ LOG(ERROR) << "Encode error: " << opus::ErrorToString(num_bytes);
+ return {};
+ }
+ encoded.resize(num_bytes);
+ return encoded;
+}
+
+opus::Decoder::Decoder(opus_uint32 sample_rate, int num_channels)
+ : num_channels_(num_channels) {
+ int error{};
+ decoder_.reset(opus_decoder_create(sample_rate, num_channels, &error));
+ valid_ = error == OPUS_OK;
+}
+
+std::vector<opus_int16> opus::Decoder::Decode(
+ const std::vector<std::vector<unsigned char>>& packets, int frame_size,
+ bool decode_fec) {
+ std::vector<opus_int16> decoded;
+ for (const auto& enc : packets) {
+ auto just_decoded = Decode(enc, frame_size, decode_fec);
+ decoded.insert(std::end(decoded), std::begin(just_decoded),
+ std::end(just_decoded));
+ }
+ return decoded;
+}
+
+std::vector<opus_int16> opus::Decoder::Decode(
+ const std::vector<unsigned char>& packet, int frame_size, bool decode_fec) {
+ const auto frame_length = (frame_size * num_channels_ * sizeof(opus_int16));
+ std::vector<opus_int16> decoded(frame_length);
+ auto num_samples = opus_decode(decoder_.get(), packet.data(), packet.size(),
+ decoded.data(), frame_size, decode_fec);
+ if (num_samples < 0) {
+ LOG(ERROR) << "Decode error: " << opus::ErrorToString(num_samples);
+ return {};
+ }
+ decoded.resize(num_samples * num_channels_);
+ return decoded;
+}
+
+std::vector<opus_int16> opus::Decoder::DecodeDummy(int frame_size) {
+ const auto frame_length = (frame_size * num_channels_ * sizeof(opus_int16));
+ std::vector<opus_int16> decoded(frame_length);
+ auto num_samples =
+ opus_decode(decoder_.get(), nullptr, 0, decoded.data(), frame_size, true);
+ if (num_samples < 0) {
+ LOG(ERROR) << "Decode error: " << opus::ErrorToString(num_samples);
+ return {};
+ }
+ decoded.resize(num_samples * num_channels_);
+ return decoded;
+}
diff --git a/host/frontend/stream_audio/opuscpp/opus_wrapper.h b/host/frontend/stream_audio/opuscpp/opus_wrapper.h
new file mode 100644
index 0000000..07e932e
--- /dev/null
+++ b/host/frontend/stream_audio/opuscpp/opus_wrapper.h
@@ -0,0 +1,133 @@
+// Copyright 2018 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// https://github.com/google/opuscpp
+
+#ifndef OPUSCPP_OPUS_WRAPPER_H_
+#define OPUSCPP_OPUS_WRAPPER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "opus.h"
+
+namespace opus {
+
+std::string ErrorToString(int error);
+
+namespace internal {
+// Deleter for OpusEncoders and OpusDecoders
+struct OpusDestroyer {
+ void operator()(OpusEncoder* encoder) const noexcept;
+ void operator()(OpusDecoder* decoder) const noexcept;
+};
+template <typename T>
+using opus_uptr = std::unique_ptr<T, OpusDestroyer>;
+} // namespace internal
+
+class Encoder {
+ public:
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html#gaa89264fd93c9da70362a0c9b96b9ca88
+ // Fs corresponds to sample_rate
+ //
+ // If expected_loss_percent is positive, FEC will be enabled
+ Encoder(opus_int32 sample_rate, int num_channels, int application,
+ int expected_loss_percent = 0);
+
+ // Resets internal state of encoder. This should be called between encoding
+ // different streams so that back-to-back decoding and one-at-a-time decoding
+ // give the same result. Returns true on success.
+ bool ResetState();
+
+ // Sets the desired bitrate. Rates from 500 to 512000 are meaningful as well
+ // as the special values OPUS_AUTO and OPUS_BITRATE_MAX. If this method
+ // is not called, the default value of OPUS_AUTO is used.
+ // Returns true on success.
+ bool SetBitrate(int bitrate);
+
+ // Enables or disables variable bitrate in the encoder. By default, variable
+ // bitrate is enabled. Returns true on success.
+ bool SetVariableBitrate(int vbr);
+
+ // Sets the computational complexity of the encoder, in the range of 0 to 10,
+ // inclusive, with 10 being the highest complexity. Returns true on success.
+ bool SetComplexity(int complexity);
+
+ // Gets the total samples of delay added by the entire codec. This value
+ // is the minimum amount of 'preskip' that has to be specified in an
+ // ogg-stream that encapsulates the encoded audio.
+ int GetLookahead();
+
+ // Takes audio data and encodes it. Returns a sequence of encoded packets.
+ // pcm.size() must be divisible by frame_size * (number of channels);
+ // pcm must not contain any incomplete packets.
+ // see documentation for pcm and frame_size at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__encoder.html#gad2d6bf6a9ffb6674879d7605ed073e25
+ std::vector<std::vector<unsigned char>> Encode(
+ const std::vector<opus_int16>& pcm, int frame_size);
+
+ int valid() const { return valid_; }
+
+ private:
+ std::vector<unsigned char> EncodeFrame(
+ const std::vector<opus_int16>::const_iterator& frame_start,
+ int frame_size);
+
+ template <typename... Ts>
+ int Ctl(int request, Ts... args) const {
+ return opus_encoder_ctl(encoder_.get(), request, args...);
+ }
+
+ int num_channels_{};
+ bool valid_{};
+ internal::opus_uptr<OpusEncoder> encoder_;
+};
+
+class Decoder {
+ public:
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga753f6fe0b699c81cfd47d70c8e15a0bd
+ // Fs corresponds to sample_rate
+ Decoder(opus_uint32 sample_rate, int num_channels);
+
+ // Takes a sequence of encoded packets and decodes them. Returns the decoded
+ // audio.
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga7d1111f64c36027ddcb81799df9b3fc9
+ std::vector<opus_int16> Decode(
+ const std::vector<std::vector<unsigned char>>& packets, int frame_size,
+ bool decode_fec);
+
+ int valid() const { return valid_; }
+
+ // Takes an encoded packet and decodes it. Returns the decoded audio
+ // see documentation at:
+ // https://mf4.xiph.org/jenkins/view/opus/job/opus/ws/doc/html/group__opus__decoder.html#ga7d1111f64c36027ddcb81799df9b3fc9
+ std::vector<opus_int16> Decode(const std::vector<unsigned char>& packet,
+ int frame_size, bool decode_fec);
+
+ // Generates a dummy frame by passing nullptr to the underlying opus decode.
+ std::vector<opus_int16> DecodeDummy(int frame_size);
+
+ private:
+ int num_channels_{};
+ bool valid_{};
+ internal::opus_uptr<OpusDecoder> decoder_;
+};
+
+} // namespace opus
+
+#endif
diff --git a/host/frontend/vnc_server/Android.bp b/host/frontend/vnc_server/Android.bp
index 210f21d..fae5a17 100644
--- a/host/frontend/vnc_server/Android.bp
+++ b/host/frontend/vnc_server/Android.bp
@@ -30,6 +30,7 @@
"cuttlefish_glog",
],
shared_libs: [
+ "vsoc_lib",
"libcuttlefish_fs",
"libcuttlefish_utils",
"cuttlefish_tcp_socket",
diff --git a/host/frontend/vnc_server/screen_connector.cpp b/host/frontend/vnc_server/screen_connector.cpp
index 35cf22f..53cb389 100644
--- a/host/frontend/vnc_server/screen_connector.cpp
+++ b/host/frontend/vnc_server/screen_connector.cpp
@@ -18,11 +18,11 @@
#include <atomic>
#include <condition_variable>
-#include <thread>
-#include <glog/logging.h>
#include <gflags/gflags.h>
+#include <common/vsoc/lib/screen_region_view.h>
+#include <host/libs/config/cuttlefish_config.h>
#include "host/frontend/vnc_server/vnc_utils.h"
DEFINE_int32(frame_server_fd, -1, "");
@@ -31,6 +31,23 @@
namespace vnc {
namespace {
+class VSoCScreenConnector : public ScreenConnector {
+ public:
+ int WaitForNewFrameSince(std::uint32_t* seq_num) override {
+ if (!screen_view_) return -1;
+ return screen_view_->WaitForNewFrameSince(seq_num);
+ }
+
+ void* GetBuffer(int buffer_idx) override {
+ if (!screen_view_) return nullptr;
+ return screen_view_->GetBuffer(buffer_idx);
+ }
+
+ private:
+ vsoc::screen::ScreenRegionView* screen_view_ =
+ vsoc::screen::ScreenRegionView::GetInstance(vsoc::GetDomain().c_str());
+};
+
// TODO(b/128852363): Substitute with one based on memory shared with the
// wayland mock
class SocketBasedScreenConnector : public ScreenConnector {
@@ -123,7 +140,12 @@
} // namespace
ScreenConnector* ScreenConnector::Get() {
- return new SocketBasedScreenConnector();
+ auto config = vsoc::CuttlefishConfig::Get();
+ if (config->enable_ivserver()) {
+ return new VSoCScreenConnector();
+ } else {
+ return new SocketBasedScreenConnector();
+ }
}
} // namespace vnc
diff --git a/host/frontend/vnc_server/simulated_hw_composer.cpp b/host/frontend/vnc_server/simulated_hw_composer.cpp
index 96702e8..7a94454 100644
--- a/host/frontend/vnc_server/simulated_hw_composer.cpp
+++ b/host/frontend/vnc_server/simulated_hw_composer.cpp
@@ -20,6 +20,7 @@
#include "host/libs/config/cuttlefish_config.h"
using cvd::vnc::SimulatedHWComposer;
+using vsoc::screen::ScreenRegionView;
SimulatedHWComposer::SimulatedHWComposer(BlackBoard* bb)
:
diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp
index fb91409..31c0328 100644
--- a/host/frontend/vnc_server/virtual_inputs.cpp
+++ b/host/frontend/vnc_server/virtual_inputs.cpp
@@ -16,19 +16,18 @@
#include "host/frontend/vnc_server/virtual_inputs.h"
#include <gflags/gflags.h>
-#include <glog/logging.h>
#include <linux/input.h>
#include <linux/uinput.h>
#include <cstdint>
#include <mutex>
-#include <thread>
#include "keysyms.h"
#include <common/libs/fs/shared_select.h>
#include <host/libs/config/cuttlefish_config.h>
using cvd::vnc::VirtualInputs;
+using vsoc::input_events::InputEventsRegionView;
DEFINE_int32(touch_fd, -1,
"A fd for a socket where to accept touch connections");
@@ -246,6 +245,37 @@
} // namespace
+class VSoCVirtualInputs : public VirtualInputs {
+ public:
+ VSoCVirtualInputs()
+ : input_events_region_view_{
+ vsoc::input_events::InputEventsRegionView::GetInstance(
+ vsoc::GetDomain().c_str())} {
+ if (!input_events_region_view_) {
+ LOG(FATAL) << "Failed to open Input events region view";
+ }
+ }
+
+ void GenerateKeyPressEvent(int code, bool down) override {
+ if (keymapping_.count(code)) {
+ input_events_region_view_->HandleKeyboardEvent(down, keymapping_[code]);
+ } else {
+ LOG(ERROR) << "Unknown keycode" << code;
+ }
+ }
+
+ void PressPowerButton(bool down) override {
+ input_events_region_view_->HandlePowerButtonEvent(down);
+ }
+
+ void HandlePointerEvent(bool touch_down, int x, int y) override {
+ input_events_region_view_->HandleSingleTouchEvent(touch_down, x, y);
+ }
+
+ private:
+ vsoc::input_events::InputEventsRegionView* input_events_region_view_{};
+};
+
class SocketVirtualInputs : public VirtualInputs {
public:
SocketVirtualInputs()
diff --git a/host/frontend/vnc_server/virtual_inputs.h b/host/frontend/vnc_server/virtual_inputs.h
index 7aca3eb..f92693b 100644
--- a/host/frontend/vnc_server/virtual_inputs.h
+++ b/host/frontend/vnc_server/virtual_inputs.h
@@ -21,6 +21,8 @@
#include <map>
#include <mutex>
+#include "common/vsoc/lib/input_events_region_view.h"
+
namespace cvd {
namespace vnc {
diff --git a/host/frontend/vnc_server/vnc_client_connection.cpp b/host/frontend/vnc_server/vnc_client_connection.cpp
index b81bb88..a8faf47 100644
--- a/host/frontend/vnc_server/vnc_client_connection.cpp
+++ b/host/frontend/vnc_server/vnc_client_connection.cpp
@@ -42,17 +42,7 @@
using cvd::vnc::Stripe;
using cvd::vnc::StripePtrVec;
using cvd::vnc::VncClientConnection;
-
-struct ScreenRegionView {
- using Pixel = uint32_t;
- static constexpr int kSwiftShaderPadding = 4;
- static constexpr int kRedShift = 0;
- static constexpr int kGreenShift = 8;
- static constexpr int kBlueShift = 16;
- static constexpr int kRedBits = 8;
- static constexpr int kGreenBits = 8;
- static constexpr int kBlueBits = 8;
-};
+using vsoc::screen::ScreenRegionView;
DEFINE_bool(debug_client, false, "Turn on detailed logging for the client");
diff --git a/host/frontend/vnc_server/vnc_utils.h b/host/frontend/vnc_server/vnc_utils.h
index db88131..0953c4e 100644
--- a/host/frontend/vnc_server/vnc_utils.h
+++ b/host/frontend/vnc_server/vnc_utils.h
@@ -23,6 +23,7 @@
#include "common/libs/utils/size_utils.h"
#include "common/libs/tcp_socket/tcp_socket.h"
+#include "common/vsoc/lib/screen_region_view.h"
#include "host/libs/config/cuttlefish_config.h"
namespace cvd {
@@ -64,7 +65,7 @@
};
inline constexpr int BytesPerPixel() {
- return sizeof(uint32_t);
+ return sizeof(vsoc::screen::ScreenRegionView::Pixel);
}
// The width of the screen regardless of orientation. Does not change.