blob: ca6b155e188b8162944ea8a77d88c614864d69c5 [file] [log] [blame]
/*
* 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.
*/
#pragma once
#include <cassert>
#include <chrono>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <thread>
#include <type_traits>
#include <android-base/logging.h>
#include "common/libs/concurrency/semaphore.h"
#include "common/libs/confui/confui.h"
#include "common/libs/fs/shared_fd.h"
#include "common/libs/utils/size_utils.h"
#include "host/libs/config/cuttlefish_config.h"
#include "host/libs/confui/host_mode_ctrl.h"
#include "host/libs/confui/host_utils.h"
#include "host/libs/screen_connector/screen_connector_common.h"
#include "host/libs/screen_connector/screen_connector_queue.h"
#include "host/libs/screen_connector/wayland_screen_connector.h"
namespace cuttlefish {
template <typename ProcessedFrameType>
class ScreenConnector : public ScreenConnectorInfo,
public ScreenConnectorFrameRenderer {
public:
static_assert(cuttlefish::is_movable<ProcessedFrameType>::value,
"ProcessedFrameType should be std::move-able.");
static_assert(std::is_base_of<ScreenConnectorFrameInfo, ProcessedFrameType>::value,
"ProcessedFrameType should inherit ScreenConnectorFrameInfo");
/**
* This is the type of the callback function WebRTC/VNC is supposed to provide
* ScreenConnector with.
*
* The callback function should be defined so that the two parameters are
* given by the callback function caller (e.g. ScreenConnectorSource) and used
* to fill out the ProcessedFrameType object, msg.
*
* The ProcessedFrameType object is internally created by ScreenConnector,
* filled out by the ScreenConnectorSource, and returned via OnNextFrame()
* call.
*/
using GenerateProcessedFrameCallback = std::function<void(
std::uint32_t /*display_number*/, std::uint32_t /*frame_width*/,
std::uint32_t /*frame_height*/, std::uint32_t /*frame_stride_bytes*/,
std::uint8_t* /*frame_bytes*/,
/* ScImpl enqueues this type into the Q */
ProcessedFrameType& msg)>;
static std::unique_ptr<ScreenConnector<ProcessedFrameType>> Get(
const int frames_fd, HostModeCtrl& host_mode_ctrl) {
auto config = cuttlefish::CuttlefishConfig::Get();
ScreenConnector<ProcessedFrameType>* raw_ptr = nullptr;
if (config->gpu_mode() == cuttlefish::kGpuModeDrmVirgl ||
config->gpu_mode() == cuttlefish::kGpuModeGfxStream ||
config->gpu_mode() == cuttlefish::kGpuModeGuestSwiftshader) {
raw_ptr = new ScreenConnector<ProcessedFrameType>(
std::make_unique<WaylandScreenConnector>(frames_fd), host_mode_ctrl);
} else {
LOG(FATAL) << "Invalid gpu mode: " << config->gpu_mode();
}
return std::unique_ptr<ScreenConnector<ProcessedFrameType>>(raw_ptr);
}
virtual ~ScreenConnector() = default;
/**
* set the callback function to be eventually used by Wayland/Socket-Based Connectors
*
* @param[in] To tell how ScreenConnectorSource caches the frame & meta info
*/
void SetCallback(GenerateProcessedFrameCallback&& frame_callback) {
std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
callback_from_streamer_ = std::move(frame_callback);
streamer_callback_set_cv_.notify_all();
sc_android_src_->SetFrameCallback(
[this](std::uint32_t display_number, std::uint32_t frame_w,
std::uint32_t frame_h, std::uint32_t frame_stride_bytes,
std::uint8_t* frame_bytes) {
const bool is_confui_mode = host_mode_ctrl_.IsConfirmatioUiMode();
if (is_confui_mode) {
return;
}
ProcessedFrameType processed_frame;
{
std::lock_guard<std::mutex> lock(streamer_callback_mutex_);
callback_from_streamer_(display_number, frame_w, frame_h,
frame_stride_bytes, frame_bytes,
processed_frame);
}
sc_android_queue_.PushBack(std::move(processed_frame));
});
}
bool IsCallbackSet() const override {
if (callback_from_streamer_) {
return true;
}
return false;
}
/* returns the processed frame that also includes meta-info such as success/fail
* and display number from the guest
*
* NOTE THAT THIS IS THE ONLY CONSUMER OF THE TWO QUEUES
*/
ProcessedFrameType OnNextFrame() {
on_next_frame_cnt_++;
while (true) {
ConfUiLog(VERBOSE) << "Streamer waiting Semaphore with host ctrl mode ="
<< static_cast<std::uint32_t>(
host_mode_ctrl_.GetMode())
<< " and cnd = #" << on_next_frame_cnt_;
sc_sem_.SemWait();
ConfUiLog(VERBOSE)
<< "Streamer got Semaphore'ed resources with host ctrl mode ="
<< static_cast<std::uint32_t>(host_mode_ctrl_.GetMode())
<< "and cnd = #" << on_next_frame_cnt_;
// do something
if (!sc_android_queue_.Empty()) {
auto mode = host_mode_ctrl_.GetMode();
if (mode == HostModeCtrl::ModeType::kAndroidMode) {
ConfUiLog(VERBOSE)
<< "Streamer gets Android frame with host ctrl mode ="
<< static_cast<std::uint32_t>(mode) << "and cnd = #"
<< on_next_frame_cnt_;
return sc_android_queue_.PopFront();
}
// AndroidFrameFetchingLoop could have added 1 or 2 frames
// before it becomes Conf UI mode.
ConfUiLog(VERBOSE)
<< "Streamer ignores Android frame with host ctrl mode ="
<< static_cast<std::uint32_t>(mode) << "and cnd = #"
<< on_next_frame_cnt_;
sc_android_queue_.PopFront();
continue;
}
ConfUiLog(VERBOSE) << "Streamer gets Conf UI frame with host ctrl mode = "
<< static_cast<std::uint32_t>(
host_mode_ctrl_.GetMode())
<< " and cnd = #" << on_next_frame_cnt_;
return sc_confui_queue_.PopFront();
}
}
/**
* ConfUi calls this when it has frames to render
*
* This won't be called if not by Confirmation UI. This won't affect rendering
* Android guest frames if Confirmation UI HAL is not active.
*
*/
bool RenderConfirmationUi(std::uint32_t display_number,
std::uint32_t frame_width,
std::uint32_t frame_height,
std::uint32_t frame_stride_bytes,
std::uint8_t* frame_bytes) override {
render_confui_cnt_++;
// wait callback is not set, the streamer is not ready
// return with LOG(ERROR)
if (!IsCallbackSet()) {
ConfUiLog(ERROR) << "callback function to process frames is not yet set";
return false;
}
ProcessedFrameType processed_frame;
auto this_thread_name = cuttlefish::confui::thread::GetName();
ConfUiLog(DEBUG) << this_thread_name
<< "is sending a #" + std::to_string(render_confui_cnt_)
<< "Conf UI frame";
callback_from_streamer_(display_number, frame_width, frame_height,
frame_stride_bytes, frame_bytes, processed_frame);
// now add processed_frame to the queue
sc_confui_queue_.PushBack(std::move(processed_frame));
return true;
}
// Let the screen connector know when there are clients connected
void ReportClientsConnected(bool have_clients) {
// screen connector implementation must implement ReportClientsConnected
sc_android_src_->ReportClientsConnected(have_clients);
return ;
}
protected:
template <typename T,
typename = std::enable_if_t<
std::is_base_of<ScreenConnectorSource, T>::value, void>>
ScreenConnector(std::unique_ptr<T>&& impl, HostModeCtrl& host_mode_ctrl)
: sc_android_src_{std::move(impl)},
host_mode_ctrl_{host_mode_ctrl},
on_next_frame_cnt_{0},
render_confui_cnt_{0},
sc_android_queue_{sc_sem_},
sc_confui_queue_{sc_sem_} {}
ScreenConnector() = delete;
private:
// either socket_based or wayland
std::unique_ptr<ScreenConnectorSource> sc_android_src_;
HostModeCtrl& host_mode_ctrl_;
unsigned long long int on_next_frame_cnt_;
unsigned long long int render_confui_cnt_;
Semaphore sc_sem_;
ScreenConnectorQueue<ProcessedFrameType> sc_android_queue_;
ScreenConnectorQueue<ProcessedFrameType> sc_confui_queue_;
GenerateProcessedFrameCallback callback_from_streamer_;
std::mutex streamer_callback_mutex_; // mutex to set & read callback_from_streamer_
std::condition_variable streamer_callback_set_cv_;
};
} // namespace cuttlefish