Add native self-hosted instance connection to fluxer_desktop
Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build.
This commit is contained in:
+315
@@ -0,0 +1,315 @@
|
||||
#include "h264_encoder_impl.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/video/video_codec_constants.h"
|
||||
#include "api/video_codecs/scalability_mode.h"
|
||||
#include "common_video/libyuv/include/webrtc_libyuv.h"
|
||||
#include "modules/video_coding/include/video_codec_interface.h"
|
||||
#include "modules/video_coding/include/video_error_codes.h"
|
||||
#include "modules/video_coding/svc/create_scalability_structure.h"
|
||||
#include "modules/video_coding/utility/simulcast_rate_allocator.h"
|
||||
#include "modules/video_coding/utility/simulcast_utility.h"
|
||||
#include "rtc_base/checks.h"
|
||||
#include "rtc_base/logging.h"
|
||||
#include "rtc_base/time_utils.h"
|
||||
#include "system_wrappers/include/metrics.h"
|
||||
#include "third_party/libyuv/include/libyuv/convert.h"
|
||||
#include "third_party/libyuv/include/libyuv/scale.h"
|
||||
|
||||
#define VA_FOURCC_I420 0x30323449 // I420
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// Used by histograms. Values of entries should not be changed.
|
||||
enum H264EncoderImplEvent {
|
||||
kH264EncoderEventInit = 0,
|
||||
kH264EncoderEventError = 1,
|
||||
kH264EncoderEventMax = 16,
|
||||
};
|
||||
|
||||
VAAPIH264EncoderWrapper::VAAPIH264EncoderWrapper(const webrtc::Environment& env,
|
||||
const SdpVideoFormat& format)
|
||||
: env_(env),
|
||||
encoder_(new livekit_ffi::VaapiH264EncoderWrapper()),
|
||||
packetization_mode_(
|
||||
H264EncoderSettings::Parse(format).packetization_mode),
|
||||
format_(format) {
|
||||
std::string hexString = format_.parameters.at("profile-level-id");
|
||||
std::optional<webrtc::H264ProfileLevelId> profile_level_id =
|
||||
webrtc::ParseH264ProfileLevelId(hexString.c_str());
|
||||
if (profile_level_id.has_value()) {
|
||||
profile_ = profile_level_id->profile;
|
||||
level_ = profile_level_id->level;
|
||||
}
|
||||
}
|
||||
|
||||
VAProfile VAAPIH264EncoderWrapper::GetVAProfile() const {
|
||||
switch (profile_) {
|
||||
case H264Profile::kProfileConstrainedBaseline:
|
||||
case H264Profile::kProfileBaseline:
|
||||
return VAProfileH264ConstrainedBaseline;
|
||||
|
||||
case H264Profile::kProfileMain:
|
||||
return VAProfileH264Main;
|
||||
|
||||
case H264Profile::kProfileConstrainedHigh:
|
||||
case H264Profile::kProfileHigh:
|
||||
return VAProfileH264High;
|
||||
}
|
||||
return VAProfileNone;
|
||||
}
|
||||
|
||||
VAAPIH264EncoderWrapper::~VAAPIH264EncoderWrapper() {
|
||||
Release();
|
||||
}
|
||||
|
||||
void VAAPIH264EncoderWrapper::ReportInit() {
|
||||
if (has_reported_init_)
|
||||
return;
|
||||
RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event",
|
||||
kH264EncoderEventInit, kH264EncoderEventMax);
|
||||
has_reported_init_ = true;
|
||||
}
|
||||
|
||||
void VAAPIH264EncoderWrapper::ReportError() {
|
||||
if (has_reported_error_)
|
||||
return;
|
||||
RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event",
|
||||
kH264EncoderEventError, kH264EncoderEventMax);
|
||||
has_reported_error_ = true;
|
||||
}
|
||||
|
||||
int32_t VAAPIH264EncoderWrapper::InitEncode(
|
||||
const VideoCodec* inst,
|
||||
const VideoEncoder::Settings& settings) {
|
||||
if (!inst || inst->codecType != kVideoCodecH264) {
|
||||
ReportError();
|
||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||
}
|
||||
if (inst->maxFramerate == 0) {
|
||||
ReportError();
|
||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||
}
|
||||
if (inst->width < 1 || inst->height < 1) {
|
||||
ReportError();
|
||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||
}
|
||||
|
||||
int32_t release_ret = Release();
|
||||
if (release_ret != WEBRTC_VIDEO_CODEC_OK) {
|
||||
ReportError();
|
||||
return release_ret;
|
||||
}
|
||||
|
||||
codec_ = *inst;
|
||||
|
||||
// Code expects simulcastStream resolutions to be correct, make sure they are
|
||||
// filled even when there are no simulcast layers.
|
||||
if (codec_.numberOfSimulcastStreams == 0) {
|
||||
codec_.simulcastStream[0].width = codec_.width;
|
||||
codec_.simulcastStream[0].height = codec_.height;
|
||||
}
|
||||
|
||||
// Initialize encoded image. Default buffer size: size of unencoded data.
|
||||
const size_t new_capacity =
|
||||
CalcBufferSize(VideoType::kI420, codec_.width, codec_.height);
|
||||
encoded_image_.SetEncodedData(EncodedImageBuffer::Create(new_capacity));
|
||||
encoded_image_._encodedWidth = codec_.width;
|
||||
encoded_image_._encodedHeight = codec_.height;
|
||||
encoded_image_.set_size(0);
|
||||
|
||||
configuration_.sending = false;
|
||||
configuration_.frame_dropping_on = codec_.GetFrameDropEnabled();
|
||||
configuration_.key_frame_interval = codec_.H264()->keyFrameInterval;
|
||||
|
||||
configuration_.width = codec_.width;
|
||||
configuration_.height = codec_.height;
|
||||
|
||||
configuration_.max_frame_rate = codec_.maxFramerate;
|
||||
configuration_.target_bps = codec_.startBitrate * 1000;
|
||||
configuration_.max_bps = codec_.maxBitrate * 1000;
|
||||
|
||||
if (!encoder_->IsInitialized()) {
|
||||
// Initialize encoder.
|
||||
int keyFrameInterval = 60;
|
||||
if (codec_.maxFramerate > 0) {
|
||||
keyFrameInterval = codec_.maxFramerate * 5;
|
||||
}
|
||||
auto va_profile = GetVAProfile();
|
||||
if (va_profile == VAProfileNone) {
|
||||
RTC_LOG(LS_ERROR) << "Unsupported H264 profile: "
|
||||
<< static_cast<int>(profile_);
|
||||
ReportError();
|
||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||
}
|
||||
encoder_->Initialize(codec_.width, codec_.height,
|
||||
codec_.startBitrate * 1000, keyFrameInterval,
|
||||
keyFrameInterval, 1, codec_.maxFramerate,
|
||||
va_profile, VA_RC_CBR);
|
||||
}
|
||||
|
||||
SimulcastRateAllocator init_allocator(env_, codec_);
|
||||
VideoBitrateAllocation allocation =
|
||||
init_allocator.Allocate(VideoBitrateAllocationParameters(
|
||||
DataRate::KilobitsPerSec(codec_.startBitrate), codec_.maxFramerate));
|
||||
SetRates(RateControlParameters(allocation, codec_.maxFramerate));
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int32_t VAAPIH264EncoderWrapper::RegisterEncodeCompleteCallback(
|
||||
EncodedImageCallback* callback) {
|
||||
encoded_image_callback_ = callback;
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int32_t VAAPIH264EncoderWrapper::Release() {
|
||||
if (encoder_->IsInitialized()) {
|
||||
encoder_->Destroy();
|
||||
}
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
int32_t VAAPIH264EncoderWrapper::Encode(
|
||||
const VideoFrame& input_frame,
|
||||
const std::vector<VideoFrameType>* frame_types) {
|
||||
if (!encoder_) {
|
||||
ReportError();
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
if (!encoded_image_callback_) {
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "InitEncode() has been called, but a callback function "
|
||||
"has not been set with RegisterEncodeCompleteCallback()";
|
||||
ReportError();
|
||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||
}
|
||||
|
||||
webrtc::scoped_refptr<I420BufferInterface> frame_buffer =
|
||||
input_frame.video_frame_buffer()->ToI420();
|
||||
if (!frame_buffer) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to convert "
|
||||
<< VideoFrameBufferTypeToString(
|
||||
input_frame.video_frame_buffer()->type())
|
||||
<< " image to I420. Can't encode frame.";
|
||||
return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE;
|
||||
}
|
||||
RTC_CHECK(frame_buffer->type() == VideoFrameBuffer::Type::kI420 ||
|
||||
frame_buffer->type() == VideoFrameBuffer::Type::kI420A);
|
||||
|
||||
bool is_keyframe_needed = false;
|
||||
if (configuration_.key_frame_request && configuration_.sending) {
|
||||
is_keyframe_needed = true;
|
||||
}
|
||||
|
||||
bool send_key_frame =
|
||||
is_keyframe_needed ||
|
||||
(frame_types && (*frame_types)[0] == VideoFrameType::kVideoFrameKey);
|
||||
if (send_key_frame) {
|
||||
is_keyframe_needed = true;
|
||||
configuration_.key_frame_request = false;
|
||||
}
|
||||
|
||||
RTC_DCHECK_EQ(configuration_.width, frame_buffer->width());
|
||||
RTC_DCHECK_EQ(configuration_.height, frame_buffer->height());
|
||||
|
||||
if (!configuration_.sending) {
|
||||
return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
|
||||
}
|
||||
|
||||
if (frame_types != nullptr) {
|
||||
// Skip frame?
|
||||
if ((*frame_types)[0] == VideoFrameType::kEmptyFrame) {
|
||||
return WEBRTC_VIDEO_CODEC_NO_OUTPUT;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> output;
|
||||
encoder_->Encode(VA_FOURCC_I420, frame_buffer->DataY(), frame_buffer->DataU(),
|
||||
frame_buffer->DataV(), send_key_frame, output);
|
||||
|
||||
if (output.empty()) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to encode frame.";
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
|
||||
encoded_image_.SetEncodedData(
|
||||
EncodedImageBuffer::Create(output.data(), output.size()));
|
||||
|
||||
h264_bitstream_parser_.ParseBitstream(encoded_image_);
|
||||
|
||||
encoded_image_.qp_ = h264_bitstream_parser_.GetLastSliceQp().value_or(-1);
|
||||
|
||||
encoded_image_._encodedWidth = configuration_.width;
|
||||
encoded_image_._encodedHeight = configuration_.height;
|
||||
encoded_image_.SetRtpTimestamp(input_frame.rtp_timestamp());
|
||||
encoded_image_.SetColorSpace(input_frame.color_space());
|
||||
encoded_image_._frameType = send_key_frame ? VideoFrameType::kVideoFrameKey
|
||||
: VideoFrameType::kVideoFrameDelta;
|
||||
CodecSpecificInfo codec_specific;
|
||||
codec_specific.codecType = kVideoCodecH264;
|
||||
codec_specific.codecSpecific.H264.packetization_mode = packetization_mode_;
|
||||
codec_specific.codecSpecific.H264.temporal_idx = kNoTemporalIdx;
|
||||
codec_specific.codecSpecific.H264.base_layer_sync = false;
|
||||
codec_specific.codecSpecific.H264.idr_frame = send_key_frame;
|
||||
encoded_image_callback_->OnEncodedImage(encoded_image_, &codec_specific);
|
||||
|
||||
return WEBRTC_VIDEO_CODEC_OK;
|
||||
}
|
||||
|
||||
VideoEncoder::EncoderInfo VAAPIH264EncoderWrapper::GetEncoderInfo() const {
|
||||
EncoderInfo info;
|
||||
info.supports_native_handle = false;
|
||||
info.implementation_name = "VAAPI H264 Encoder";
|
||||
info.scaling_settings = VideoEncoder::ScalingSettings::kOff;
|
||||
info.is_hardware_accelerated = true;
|
||||
info.supports_simulcast = false;
|
||||
info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420};
|
||||
return info;
|
||||
}
|
||||
|
||||
void VAAPIH264EncoderWrapper::SetRates(
|
||||
const RateControlParameters& parameters) {
|
||||
if (!encoder_) {
|
||||
RTC_LOG(LS_WARNING) << "SetRates() while uninitialized.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.framerate_fps < 1.0) {
|
||||
RTC_LOG(LS_WARNING) << "Invalid frame rate: " << parameters.framerate_fps;
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters.bitrate.get_sum_bps() == 0) {
|
||||
configuration_.SetStreamState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
codec_.maxFramerate = static_cast<uint32_t>(parameters.framerate_fps);
|
||||
|
||||
configuration_.target_bps = parameters.bitrate.GetSpatialLayerSum(0);
|
||||
configuration_.max_frame_rate = parameters.framerate_fps;
|
||||
|
||||
if (configuration_.target_bps) {
|
||||
configuration_.SetStreamState(true);
|
||||
// Update max_frame_rate/target_bitrate for vaapi encoder.
|
||||
encoder_->UpdateRates(configuration_.max_frame_rate,
|
||||
configuration_.target_bps);
|
||||
} else {
|
||||
configuration_.SetStreamState(false);
|
||||
}
|
||||
}
|
||||
|
||||
void VAAPIH264EncoderWrapper::LayerConfig::SetStreamState(bool send_stream) {
|
||||
if (send_stream && !sending) {
|
||||
// Need a key frame if we have not sent this stream before.
|
||||
key_frame_request = true;
|
||||
}
|
||||
sending = send_stream;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
#ifndef VAAPI_H264_ENCODER_IMPL_H_
|
||||
#define VAAPI_H264_ENCODER_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/inlined_vector.h"
|
||||
#include "api/transport/rtp/dependency_descriptor.h"
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "api/video/video_codec_constants.h"
|
||||
#include "api/video_codecs/scalability_mode.h"
|
||||
#include "api/video_codecs/video_encoder.h"
|
||||
#include "common_video/h264/h264_bitstream_parser.h"
|
||||
#include "modules/video_coding/codecs/h264/include/h264.h"
|
||||
#include "modules/video_coding/svc/scalable_video_controller.h"
|
||||
#include "modules/video_coding/utility/quality_scaler.h"
|
||||
|
||||
#include "vaapi_h264_encoder_wrapper.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VAAPIH264EncoderWrapper : public VideoEncoder {
|
||||
public:
|
||||
struct LayerConfig {
|
||||
int simulcast_idx = 0;
|
||||
int width = -1;
|
||||
int height = -1;
|
||||
bool sending = true;
|
||||
bool key_frame_request = false;
|
||||
float max_frame_rate = 0;
|
||||
uint32_t target_bps = 0;
|
||||
uint32_t max_bps = 0;
|
||||
bool frame_dropping_on = false;
|
||||
int key_frame_interval = 0;
|
||||
int num_temporal_layers = 1;
|
||||
|
||||
void SetStreamState(bool send_stream);
|
||||
};
|
||||
|
||||
public:
|
||||
VAAPIH264EncoderWrapper(const webrtc::Environment& env,
|
||||
const SdpVideoFormat& format);
|
||||
~VAAPIH264EncoderWrapper() override;
|
||||
|
||||
int32_t InitEncode(const VideoCodec* codec_settings,
|
||||
const Settings& settings) override;
|
||||
|
||||
int32_t RegisterEncodeCompleteCallback(
|
||||
EncodedImageCallback* callback) override;
|
||||
|
||||
int32_t Release() override;
|
||||
|
||||
int32_t Encode(const VideoFrame& frame,
|
||||
const std::vector<VideoFrameType>* frame_types) override;
|
||||
|
||||
void SetRates(const RateControlParameters& rc_parameters) override;
|
||||
|
||||
EncoderInfo GetEncoderInfo() const override;
|
||||
|
||||
private:
|
||||
VAProfile GetVAProfile() const;
|
||||
|
||||
private:
|
||||
const webrtc::Environment& env_;
|
||||
EncodedImageCallback* encoded_image_callback_ = nullptr;
|
||||
std::unique_ptr<livekit_ffi::VaapiH264EncoderWrapper> encoder_;
|
||||
LayerConfig configuration_;
|
||||
EncodedImage encoded_image_;
|
||||
H264PacketizationMode packetization_mode_;
|
||||
VideoCodec codec_;
|
||||
void ReportInit();
|
||||
void ReportError();
|
||||
bool has_reported_init_ = false;
|
||||
bool has_reported_error_ = false;
|
||||
webrtc::H264BitstreamParser h264_bitstream_parser_;
|
||||
const SdpVideoFormat format_;
|
||||
H264Profile profile_ = H264Profile::kProfileConstrainedBaseline;
|
||||
H264Level level_ = H264Level::kLevel1_b;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // VAAPI_H264_ENCODER_IMPL_H_
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
#include "vaapi_display_drm.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef IN_LIBVA
|
||||
#include "va/drm/va_drm.h"
|
||||
#else
|
||||
#include <va/va_drm.h>
|
||||
#endif
|
||||
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
static bool check_h264_encoding_support(VADisplay va_display) {
|
||||
VAProfile profile_list[] = {VAProfileH264High, VAProfileH264Main,
|
||||
VAProfileH264ConstrainedBaseline};
|
||||
|
||||
VAProfile h264_profile = VAProfileH264ConstrainedBaseline;
|
||||
VAEntrypoint* entrypoints;
|
||||
int num_entrypoints, slice_entrypoint;
|
||||
bool support_encode = false;
|
||||
int selected_entrypoint = -1;
|
||||
int major_ver, minor_ver;
|
||||
VAStatus va_status;
|
||||
uint32_t i;
|
||||
|
||||
if (!va_display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
va_status = vaInitialize(va_display, &major_ver, &minor_ver);
|
||||
|
||||
if (major_ver < 0 || minor_ver < 0 || va_status != VA_STATUS_SUCCESS) {
|
||||
RTC_LOG(LS_ERROR) << "vaInitialize failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
num_entrypoints = vaMaxNumEntrypoints(va_display);
|
||||
entrypoints = new VAEntrypoint[num_entrypoints * sizeof(*entrypoints)];
|
||||
if (!entrypoints) {
|
||||
RTC_LOG(LS_ERROR) << "failed to allocate VA entrypoints";
|
||||
return false;
|
||||
}
|
||||
|
||||
/* use the highest profile */
|
||||
for (i = 0; i < sizeof(profile_list) / sizeof(profile_list[0]); i++) {
|
||||
if ((h264_profile != ~0) && h264_profile != profile_list[i])
|
||||
continue;
|
||||
|
||||
h264_profile = profile_list[i];
|
||||
vaQueryConfigEntrypoints(va_display, h264_profile, entrypoints,
|
||||
&num_entrypoints);
|
||||
for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints;
|
||||
slice_entrypoint++) {
|
||||
if ((entrypoints[slice_entrypoint] == VAEntrypointEncSlice) ||
|
||||
(entrypoints[slice_entrypoint] == VAEntrypointEncSliceLP)) {
|
||||
support_encode = true;
|
||||
selected_entrypoint = entrypoints[slice_entrypoint];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (support_encode) {
|
||||
RTC_LOG(LS_INFO) << "Using EntryPoint - " << selected_entrypoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (support_encode) {
|
||||
RTC_LOG(LS_INFO) << "Supported H264 Encoder, Using EntryPoint - "
|
||||
<< selected_entrypoint;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR)
|
||||
<< "Can't find VAEntrypointEncSlice or VAEntrypointEncSliceLP for "
|
||||
"H264 profiles";
|
||||
delete[] entrypoints;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete[] entrypoints;
|
||||
return true;
|
||||
}
|
||||
|
||||
static VADisplay va_open_display_drm(int* drm_fd) {
|
||||
VADisplay va_dpy;
|
||||
int i;
|
||||
|
||||
static const char* drm_device_paths[] = {"/dev/dri/renderD128",
|
||||
"/dev/dri/renderD129", NULL};
|
||||
for (i = 0; drm_device_paths[i]; i++) {
|
||||
*drm_fd = open(drm_device_paths[i], O_RDWR);
|
||||
if (*drm_fd < 0)
|
||||
continue;
|
||||
|
||||
va_dpy = vaGetDisplayDRM(*drm_fd);
|
||||
vaSetErrorCallback(va_dpy, NULL, NULL);
|
||||
vaSetInfoCallback(va_dpy, NULL, NULL);
|
||||
if (va_dpy && check_h264_encoding_support(va_dpy))
|
||||
return va_dpy;
|
||||
|
||||
close(*drm_fd);
|
||||
*drm_fd = -1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
bool VaapiDisplayDrm::Open() {
|
||||
va_display_ = va_open_display_drm(&drm_fd_);
|
||||
if (!va_display_) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to open VA drm display. Maybe the AMD video "
|
||||
"driver or libva-dev/libdrm-dev is not installed?";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VaapiDisplayDrm::isOpen() const {
|
||||
return va_display_ != nullptr;
|
||||
}
|
||||
|
||||
void VaapiDisplayDrm::Close() {
|
||||
if (va_display_) {
|
||||
if (drm_fd_ < 0)
|
||||
return;
|
||||
|
||||
close(drm_fd_);
|
||||
drm_fd_ = -1;
|
||||
va_display_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
#ifndef VAAPI_DISPLAY_DRM_H_
|
||||
#define VAAPI_DISPLAY_DRM_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <va/va.h>
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
// VAAPI drm display wrapper class
|
||||
class VaapiDisplayDrm {
|
||||
public:
|
||||
VaapiDisplayDrm() = default;
|
||||
VaapiDisplayDrm(const VaapiDisplayDrm&) = delete;
|
||||
~VaapiDisplayDrm() = default;
|
||||
|
||||
// Initialize the VAAPI display
|
||||
bool Open();
|
||||
|
||||
// Check if the VAAPI display is open
|
||||
bool isOpen() const;
|
||||
|
||||
// Close the VAAPI display
|
||||
void Close();
|
||||
|
||||
// Get the VAAPI display handle
|
||||
VADisplay display() const { return va_display_; }
|
||||
|
||||
private:
|
||||
VADisplay va_display_;
|
||||
int drm_fd_;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
|
||||
#endif // VAAPI_DISPLAY_DRM_H_
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright © Microsoft Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "vaapi_display_win32.h"
|
||||
|
||||
#include <directx/dxcore_interface.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static const char* g_device_name;
|
||||
|
||||
void dxcore_resolve_adapter(const char* adapter_string,
|
||||
/*out*/ bool* ptr_device_found,
|
||||
/*out*/ LUID* ptr_adapter_luid) {
|
||||
int selected_adapter_index = -1;
|
||||
IDXCoreAdapterFactory* factory = nullptr;
|
||||
IDXCoreAdapterList* adapter_list = nullptr;
|
||||
IDXCoreAdapter* adapter = nullptr;
|
||||
typedef HRESULT(WINAPI * PFN_CREATE_DXCORE_ADAPTER_FACTORY)(REFIID riid,
|
||||
void** ppFactory);
|
||||
PFN_CREATE_DXCORE_ADAPTER_FACTORY DXCoreCreateAdapterFactory;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
memset(ptr_adapter_luid, 0, sizeof(LUID));
|
||||
*ptr_device_found = false;
|
||||
|
||||
HMODULE dxcore_mod = LoadLibraryA("DXCore.DLL");
|
||||
if (!dxcore_mod) {
|
||||
fprintf(stderr, "Failed to load DXCore.DLL to enumerate adapters.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DXCoreCreateAdapterFactory =
|
||||
(PFN_CREATE_DXCORE_ADAPTER_FACTORY)GetProcAddress(
|
||||
dxcore_mod, "DXCoreCreateAdapterFactory");
|
||||
if (!DXCoreCreateAdapterFactory) {
|
||||
fprintf(stderr,
|
||||
"Failed to load DXCoreCreateAdapterFactory from DXCore.DLL.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr = DXCoreCreateAdapterFactory(IID_IDXCoreAdapterFactory, (void**)&factory);
|
||||
if (FAILED(hr)) {
|
||||
fprintf(stderr, "DXCoreCreateAdapterFactory failed: %lx\n", hr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hr =
|
||||
factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS,
|
||||
IID_IDXCoreAdapterList, (void**)&adapter_list);
|
||||
if (FAILED(hr)) {
|
||||
fprintf(stderr, "CreateAdapterList failed: %lx\n", hr);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (adapter_string &&
|
||||
(sscanf_s(adapter_string, "%d", &selected_adapter_index) != 1)) {
|
||||
fprintf(stderr, "Invalid device index received for -hwaccel_device %s\n",
|
||||
adapter_string ? adapter_string : "");
|
||||
}
|
||||
|
||||
if (!adapter_string)
|
||||
fprintf(stdout, "Available devices for --display win32:\n");
|
||||
for (int i = 0; i < adapter_list->GetAdapterCount(); i++) {
|
||||
if (SUCCEEDED(adapter_list->GetAdapter(i, IID_IDXCoreAdapter,
|
||||
(void**)&adapter))) {
|
||||
size_t desc_size = 0;
|
||||
if (FAILED(adapter->GetPropertySize(
|
||||
DXCoreAdapterProperty::DriverDescription, &desc_size))) {
|
||||
adapter->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
char* adapter_name = (char*)malloc(desc_size);
|
||||
if (!adapter_name) {
|
||||
adapter->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::DriverDescription,
|
||||
desc_size, adapter_name))) {
|
||||
free(adapter_name);
|
||||
adapter->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
LUID cur_adapter_luid = {0, 0};
|
||||
if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::InstanceLuid,
|
||||
&cur_adapter_luid))) {
|
||||
free(adapter_name);
|
||||
adapter->Release();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (selected_adapter_index == i) {
|
||||
*ptr_adapter_luid = cur_adapter_luid;
|
||||
*ptr_device_found = true;
|
||||
}
|
||||
|
||||
if (!adapter_string)
|
||||
fprintf(stdout,
|
||||
"\tDevice Index: %d Device LUID: %lu %ld - Device Name: %s\n",
|
||||
i, cur_adapter_luid.LowPart, cur_adapter_luid.HighPart,
|
||||
adapter_name);
|
||||
free(adapter_name);
|
||||
adapter->Release();
|
||||
}
|
||||
}
|
||||
|
||||
fail:
|
||||
if (adapter_list)
|
||||
adapter_list->Release();
|
||||
if (factory)
|
||||
factory->Release();
|
||||
if (dxcore_mod)
|
||||
FreeLibrary(dxcore_mod);
|
||||
}
|
||||
|
||||
static VADisplay va_open_display_win32(void) {
|
||||
LUID adapter_luid = {0, 0};
|
||||
bool device_found = false;
|
||||
if (g_device_name) {
|
||||
bool print_devices = (0 == strcmp(g_device_name, "help"));
|
||||
dxcore_resolve_adapter(print_devices ? NULL : g_device_name, &device_found,
|
||||
&adapter_luid);
|
||||
if (print_devices) {
|
||||
exit(0);
|
||||
} else if (g_device_name && !device_found) {
|
||||
fprintf(stderr,
|
||||
"Could not find device %s for --display win32. Please try "
|
||||
"--device help for a list of available devices.\n",
|
||||
g_device_name);
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Adapter automatic selection supported by sending NULL adapter to
|
||||
// vaGetDisplayWin32
|
||||
return vaGetDisplayWin32(device_found ? &adapter_luid : NULL);
|
||||
}
|
||||
|
||||
static void va_close_display_win32(VADisplay va_dpy) {}
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
VaapiDisplayWin32::VaapiDisplayWin32() : va_display_(nullptr) {
|
||||
putenv("LIBVA_DRIVER_NAME=vaon12");
|
||||
putenv("LIBVA_DRIVERS_PATH=.");
|
||||
}
|
||||
|
||||
bool VaapiDisplayWin32::Open() {
|
||||
va_display_ = va_open_display_win32();
|
||||
if (!va_display_) {
|
||||
fprintf(stderr, "Failed to open VA display\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VaapiDisplayWin32::isOpen() const {
|
||||
return va_display_ != nullptr;
|
||||
}
|
||||
|
||||
void VaapiDisplayWin32::Close() {
|
||||
if (va_display_) {
|
||||
va_close_display_win32(va_display_);
|
||||
va_display_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace livekit_ffi
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#ifndef VAAPI_DISPLAY_WIN32_H_
|
||||
#define VAAPI_DISPLAY_WIN32_H_
|
||||
|
||||
#include <va/va.h>
|
||||
#include <va/va_win32.h>
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
// VAAPI win32 display wrapper class
|
||||
class VaapiDisplayWin32 {
|
||||
public:
|
||||
VaapiDisplayWin32();
|
||||
~VaapiDisplayWin32() {}
|
||||
|
||||
// Initialize the VAAPI display
|
||||
bool Open();
|
||||
|
||||
// Check if the VAAPI display is open
|
||||
bool isOpen() const;
|
||||
|
||||
// Close the VAAPI display
|
||||
void Close();
|
||||
|
||||
// Get the VAAPI display handle
|
||||
VADisplay display() const { return va_display_; }
|
||||
|
||||
private:
|
||||
VADisplay va_display_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
|
||||
#endif // VAAPI_DISPLAY_WIN32_H_
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
#include "vaapi_encoder_factory.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "h264_encoder_impl.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
#if defined(WIN32)
|
||||
#include "vaapi_display_win32.h"
|
||||
using VaapiDisplay = livekit_ffi::VaapiDisplayWin32;
|
||||
#elif defined(__linux__)
|
||||
#include "vaapi_display_drm.h"
|
||||
using VaapiDisplay = livekit_ffi::VaapiDisplayDrm;
|
||||
#endif
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
VAAPIVideoEncoderFactory::VAAPIVideoEncoderFactory() {
|
||||
std::map<std::string, std::string> baselineParameters = {
|
||||
{"profile-level-id", "42e01f"},
|
||||
{"level-asymmetry-allowed", "1"},
|
||||
{"packetization-mode", "1"},
|
||||
};
|
||||
supported_formats_.push_back(SdpVideoFormat("H264", baselineParameters));
|
||||
/*
|
||||
std::map<std::string, std::string> highParameters = {
|
||||
{"profile-level-id", "4d0032"},
|
||||
{"level-asymmetry-allowed", "1"},
|
||||
{"packetization-mode", "1"},
|
||||
};
|
||||
|
||||
supported_formats_.push_back(SdpVideoFormat("H264", highParameters));
|
||||
*/
|
||||
}
|
||||
|
||||
VAAPIVideoEncoderFactory::~VAAPIVideoEncoderFactory() {}
|
||||
|
||||
bool VAAPIVideoEncoderFactory::IsSupported() {
|
||||
// Ensure that libva and libva-drm are actually available for loading.
|
||||
// Otherwise, we will immediately abort.
|
||||
void* libva_ptr = dlopen("libva.so.2", RTLD_LAZY);
|
||||
if (!libva_ptr) {
|
||||
RTC_LOG(LS_INFO) << "libva.so.2 is not found";
|
||||
return false;
|
||||
}
|
||||
dlclose(libva_ptr);
|
||||
|
||||
void* libvadrm_ptr = dlopen("libva-drm.so.2", RTLD_LAZY);
|
||||
if (!libvadrm_ptr) {
|
||||
RTC_LOG(LS_INFO) << "libva-drm.so.2 is not found";
|
||||
return false;
|
||||
}
|
||||
dlclose(libvadrm_ptr);
|
||||
|
||||
// Check if VAAPI is supported by the environment.
|
||||
// This could involve checking if the VAAPI display can be opened.
|
||||
VaapiDisplay vaapi_display;
|
||||
if (!vaapi_display.Open()) {
|
||||
RTC_LOG(LS_WARNING) << "Failed to open VAAPI display.";
|
||||
return false;
|
||||
}
|
||||
|
||||
vaapi_display.Close();
|
||||
// If we can open the VAAPI display, we consider it supported.
|
||||
std::cout << "VAAPI is supported." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<VideoEncoder> VAAPIVideoEncoderFactory::Create(
|
||||
const Environment& env,
|
||||
const SdpVideoFormat& format) {
|
||||
// Check if the requested format is supported.
|
||||
for (const auto& supported_format : supported_formats_) {
|
||||
if (format.IsSameCodec(supported_format)) {
|
||||
// If the format is supported, create and return the encoder.
|
||||
return std::make_unique<VAAPIH264EncoderWrapper>(env, format);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<SdpVideoFormat> VAAPIVideoEncoderFactory::GetSupportedFormats()
|
||||
const {
|
||||
return supported_formats_;
|
||||
}
|
||||
|
||||
std::vector<SdpVideoFormat> VAAPIVideoEncoderFactory::GetImplementations()
|
||||
const {
|
||||
return supported_formats_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
|
||||
|
||||
#ifndef VAAPI_VIDEO_ENCODER_FACTORY_H_
|
||||
#define VAAPI_VIDEO_ENCODER_FACTORY_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "api/environment/environment.h"
|
||||
#include "api/video_codecs/sdp_video_format.h"
|
||||
#include "api/video_codecs/video_encoder_factory.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class VAAPIVideoEncoderFactory : public VideoEncoderFactory {
|
||||
public:
|
||||
VAAPIVideoEncoderFactory();
|
||||
~VAAPIVideoEncoderFactory() override;
|
||||
|
||||
static bool IsSupported();
|
||||
|
||||
std::unique_ptr<VideoEncoder> Create(const Environment& env,
|
||||
const SdpVideoFormat& format) override;
|
||||
|
||||
// Returns a list of supported codecs in order of preference.
|
||||
std::vector<SdpVideoFormat> GetSupportedFormats() const override;
|
||||
|
||||
std::vector<SdpVideoFormat> GetImplementations() const override;
|
||||
|
||||
std::unique_ptr<EncoderSelectorInterface> GetEncoderSelector() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<SdpVideoFormat> supported_formats_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // VAAPI_VIDEO_ENCODER_FACTORY_H_
|
||||
|
||||
Vendored
+1880
File diff suppressed because it is too large
Load Diff
Vendored
+127
@@ -0,0 +1,127 @@
|
||||
#ifndef VAAPI_H264_ENCODER_WRAPPER_H_
|
||||
#define VAAPI_H264_ENCODER_WRAPPER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <va/va.h>
|
||||
#include <va/va_enc_h264.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include "vaapi_display_win32.h"
|
||||
using VaapiDisplay = livekit_ffi::VaapiDisplayWin32;
|
||||
#elif defined(__linux__)
|
||||
#include "vaapi_display_drm.h"
|
||||
using VaapiDisplay = livekit_ffi::VaapiDisplayDrm ;
|
||||
#endif
|
||||
#define SURFACE_NUM 16 /* 16 surfaces for reference */
|
||||
|
||||
typedef struct {
|
||||
// one of: VAProfileH264ConstrainedBaseline, VAProfileH264Main,
|
||||
// VAProfileH264High
|
||||
VAProfile h264_profile;
|
||||
int h264_entropy_mode;
|
||||
int frame_width;
|
||||
int frame_height;
|
||||
int frame_rate;
|
||||
uint32_t bitrate;
|
||||
int initial_qp;
|
||||
int minimal_qp;
|
||||
int intra_period;
|
||||
int intra_idr_period;
|
||||
int ip_period;
|
||||
int rc_mode;
|
||||
} VA264Config;
|
||||
|
||||
typedef struct {
|
||||
VADisplay va_dpy;
|
||||
|
||||
VAConfigAttrib attrib[VAConfigAttribTypeMax];
|
||||
VAConfigAttrib config_attrib[VAConfigAttribTypeMax];
|
||||
int config_attrib_num;
|
||||
int enc_packed_header_idx;
|
||||
VASurfaceID src_surface[SURFACE_NUM];
|
||||
VABufferID coded_buf[SURFACE_NUM];
|
||||
VASurfaceID ref_surface[SURFACE_NUM];
|
||||
VAConfigID config_id;
|
||||
VAContextID context_id;
|
||||
VAEncSequenceParameterBufferH264 seq_param;
|
||||
VAEncPictureParameterBufferH264 pic_param;
|
||||
VAEncSliceParameterBufferH264 slice_param;
|
||||
VAPictureH264 current_curr_pic;
|
||||
VAPictureH264 reference_frames[SURFACE_NUM];
|
||||
VAPictureH264 ref_pic_list0_p[SURFACE_NUM * 2];
|
||||
VAPictureH264 ref_pic_list0_b[SURFACE_NUM * 2];
|
||||
VAPictureH264 ref_pic_list1_b[SURFACE_NUM * 2];
|
||||
|
||||
// Default entrypoint for Encode
|
||||
int requested_entrypoint;
|
||||
int selected_entrypoint;
|
||||
|
||||
uint32_t num_short_term;
|
||||
int constraint_set_flag;
|
||||
int h264_packedheader; /* support pack header? */
|
||||
int h264_maxref;
|
||||
int frame_width_mbaligned;
|
||||
int frame_height_mbaligned;
|
||||
uint32_t current_frame_num;
|
||||
int current_frame_type;
|
||||
uint64_t current_frame_encoding;
|
||||
uint64_t current_frame_display;
|
||||
uint64_t current_idr_display;
|
||||
|
||||
uint8_t* encoded_buffer;
|
||||
VA264Config config;
|
||||
} VA264Context;
|
||||
|
||||
namespace livekit_ffi {
|
||||
|
||||
class VaapiH264EncoderWrapper {
|
||||
public:
|
||||
VaapiH264EncoderWrapper();
|
||||
~VaapiH264EncoderWrapper();
|
||||
|
||||
// Initialize the encoder with the given parameters.
|
||||
bool Initialize(int width,
|
||||
int height,
|
||||
int bitrate,
|
||||
int intra_period,
|
||||
int idr_period,
|
||||
int ip_period,
|
||||
int frame_rate,
|
||||
VAProfile profile,
|
||||
int rc_mode);
|
||||
|
||||
// Encode a frame and return the encoded data.
|
||||
bool Encode(int fourcc,
|
||||
const uint8_t* y,
|
||||
const uint8_t* u,
|
||||
const uint8_t* v,
|
||||
bool forceIDR,
|
||||
std::vector<uint8_t>& output);
|
||||
|
||||
void UpdateRates(int frame_rate, int bitrate) {
|
||||
if (context_) {
|
||||
context_->config.frame_rate = frame_rate;
|
||||
context_->config.bitrate = bitrate;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsInitialized() const {
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
// Release resources.
|
||||
void Destroy();
|
||||
|
||||
private:
|
||||
std::unique_ptr<VA264Context> context_;
|
||||
std::unique_ptr<VaapiDisplay> va_display_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace livekit_ffi
|
||||
|
||||
#endif // VAAPI_H264_ENCODER_WRAPPER_H_
|
||||
Reference in New Issue
Block a user