Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Enhanced Broadcasting support for Linux #11541

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

lexano-ivs
Copy link
Contributor

@lexano-ivs lexano-ivs commented Nov 20, 2024

Description

TEB (Twitch Enhanced Broadcasting) requires the client to gather information about the host, such as which GPU(s) are available, memory sizes, OS version, etc..., and send that information to a server which then responds with a configuration set for the client to use with multitrack encoding. TEB originally supported Windows, and this PR adds support for Linux-based operating systems.

TEB on Linux supports NVIDIA and AMD GPUs, and Intel GPUs as of 2024.12.17. Intel GPUs with Linux seem to work at least with a flatpak build. For NVIDIA GPUs, NVENC is used and the configurations from the server are currently identical between Windows and Linux. For AMD GPUs, AMF is used on Windows and VAAPI is used on Linux; AMF on Linux is not currently supported for TEB. AMF on Linux might be supported in the future.

Motivation and Context

From the client encoding perspective, Enhanced Broadcasting was originally envisioned to support Windows, Linux, and MacOS. This PR addresses the market need to expand support for Linux-based users.

How Has This Been Tested?

The code has been tested in the TEB beta program and also by the primary developer (@lexano-ivs) on a host setup specifically to test TEB on Linux. This host includes:

  • iGPU (Intel)
  • NVIDIA 4060
  • AMD 6650 XT and AMD 7800 XT
  • Intel Arc A770
  • Multi-boot system with Ubuntu 24.04, Linux Mint 22, and EndeavourOS (Arch-based)

The code was tested against multiple configurations, including single & dual dGPU configurations, swapping the primary/secondary GPU between NVIDIA and AMD, and across the 3 Linux operating systems. DEB-based builds, local builds, and flatpak builds were also tested.

Refer to TEB Installation Instructions for additional context.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • My code has been run through clang-format.
  • I have read the contributing document.
  • My code is not on the master branch.
  • The code has been tested.
  • All commit messages are properly formatted and commits squashed where appropriate.
  • I have included updates to all appropriate documentation.

plugins/obs-ffmpeg/obs-ffmpeg-vaapi.c Outdated Show resolved Hide resolved

static bool get_distribution_info(string &distro, string &version)
{
ifstream file("/etc/os-release");
Copy link
Collaborator

@tytan652 tytan652 Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a Flatpak context, this file should not be relied on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks tytan652. Is there something else within a flatpak that can be relied upon?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The presence of /.flatpak-info and it's content can be used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I'll look into that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added parsing of the /.flatpak-info in addition to /etc/os-release.

@WizardCM WizardCM added Linux Affects Linux New Feature New feature or plugin labels Nov 24, 2024
Copy link
Collaborator

@Lain-B Lain-B left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to my comment on Tytan's comment about VAAPI settings migration, these are the only issues I see so far.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so for the all of the UI/system-info-posix.cpp changes, there seems to be a bit of a mix between using raw pointers and references.

static void trim_ws(std::string &s)
static bool get_distribution_info(string &distro, string &version)
static bool get_cpu_freq(uint32_t *cpu_freq)
static bool get_drm_card_info(uint8_t *card_idx, struct drm_card_info *dci)
static void adjust_gpu_model(std::string *model)
bool compare_match_strength(const drm_card_info &a, const drm_card_info &b)

I'm not entirely sure why pointers were used in certain cases here like this versus references. In C this would be understandable but in C++ this just adds unnecessarily unsafe code. Might want to fix these (and any other similar cases in here I haven't spotted) to use references.

If you need the ability to pass nullptr (which I don't quite see), that would also make sense, but I would probably try to see if that's absolutely necessary, and if so, to check to see if there's a safer way of passing that (e.g. std::optional).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I've cleaned this up with the latest commits. It's more consistent now and using references instead of pointers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lain-B Looks like these have been addressed?

}
}

bool compare_match_strength(const drm_card_info &a, const drm_card_info &b)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this function is used in a different translation unit, might want to mark it as static.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also recommend checking other newly added non-exported functions elsewhere where this situation might apply, but this is just the one I noticed at first glance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless this function is used in a different translation unit, might want to mark it as static.

Or for better C++-isms, put them into an anonymous (unnamed) namespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've put almost all functions in an anonymous namespace and dropped "static" for these. It's cleaner from perspective. Thanks for the suggestion, it makes sense.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Lain-B looks like this has been addressed?

@lexano-ivs lexano-ivs force-pushed the lexano/teb-linux branch 2 times, most recently from 69c3d29 to efd4f5e Compare December 18, 2024 19:17
distro = "";
version = "";

if (std::filesystem::exists(systemd_file)) {
Copy link
Collaborator

@tytan652 tytan652 Dec 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong priority, Flatpak should be checked first.

Runtime can add an os-release file for compatibility, but has been proven to be an issue (e.g. 6.2 was still showing 5.15 in a lsb-release made for compat) , let's avoid it to happen again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks tytan652. I reversed the parsing order and pushed the update.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tytan652 has this been addressed?

UI/system-info-posix.cpp Outdated Show resolved Hide resolved
@mihawk90
Copy link

mihawk90 commented Jan 19, 2025

Just wanted to add my feedback for future reviewers:
I've applied this to my build a couple weeks ago and just ran a 9h stream (on an AMD RX 7700XT) with 0 issues, it's working great.
Thanks for putting the work into this.

The only thing that was odd was that the Twitch Inspector doesn't show any of the streams when in Bandwidth Testing mode, but it's showing them fine for a Live Broadcast. I don't think that's related to this PR though and more of an Inspector thing.

@dsaedtler
Copy link
Contributor

The only thing that was odd was that the Twitch Inspector doesn't show any of the streams when in Bandwidth Testing mode, but it's showing them fine for a Live Broadcast. I don't think that's related to this PR though and more of an Inspector thing.

Yeah in bandwidth testing mode the data doesn't actually get processed, so it doesn't log codecs etc.

Add graphics APIs to obtain the GPU driver version and renderer
strings, as well as GDDR memory sizes. The GDDR memory sizes
include the dmem (dedicated memory on the GPU) and smem
(shared CPU memory used by the GPU but resides in the CPU DDR).

The version and renderer strings are needed for identification
purposes, for example enhanced broadcasting used by Twitch, to
associate the GPU used by OBS with the PCIe-based identification
values such as device_id and vendor_id.
`os_get_free_size()` was simply returning 0. For Linux,
implement the free size calculation based on the `sysinfo()`
system call.
`get_rc_mode()` compares the incoming rate control mode string
to the .name member of rc_mode_t, and the table entries are
all upper-case. This caused a crash when the incoming string
is set to "cbr" instead of "CBR". Make the string comparison
case insensitive.
VAAPI encoders deviate from other encoders (e.g. AMF, NVENC) with
the "profile" setting being an integer instead of a string. With
enhanced broadcasting, "profile" is signalled as a string. Convert
the string-based profile to the appropriate integer-based profile
for VAAPI encoders as a workaround, until VAAPI supports string-based
"profile" (if ever).
Enhanced broadcasting requires system information to be gathered
on the client and submitted to the GetClientConfiguration request
in order to obtain a valid response from the server. This commit
adds support for gathering the required information on Linux-based
systems.
Comment on lines +45 to +48
GRAPHICS_IMPORT_OPTIONAL(gpu_get_smem);
GRAPHICS_IMPORT_OPTIONAL(gpu_get_dmem);
GRAPHICS_IMPORT_OPTIONAL(gpu_get_driver_version);
GRAPHICS_IMPORT_OPTIONAL(gpu_get_renderer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer these in the same order they are declared/defined.

Comment on lines +300 to +319
uint64_t gs_get_gpu_dmem(void)
{
return gs_valid("gs_get_gpu_dmem") ? thread_graphics->exports.gpu_get_dmem() : 0;
}

uint64_t gs_get_gpu_smem(void)
{
return gs_valid("gs_get_gpu_smem") ? thread_graphics->exports.gpu_get_smem() : 0;
}

const char *gs_get_driver_version(void)
{
return gs_valid("gs_get_driver_version") ? thread_graphics->exports.gpu_get_driver_version() : NULL;
}

const char *gs_get_renderer(void)
{
return gs_valid("gs_get_renderer") ? thread_graphics->exports.gpu_get_renderer() : NULL;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer these in the same order they are declared/defined in the header.

Comment on lines 5619 to 5622
// Enhanced Broadcasting works on Windows and Linux. For other
// OS variants, only enable the GUI controls if developer mode
// was invoked.
#if (!defined(__linux__) && !defined(_WIN32))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will probably conflict with the PR to enable TEB on macOS.

Comment on lines -5591 to +5594
#ifndef _WIN32
// Enhanced Broadcasting works on Windows and Linux. For other
// OS variants, only enable the GUI controls if developer mode
// was invoked.
#if (!defined(__linux__) && !defined(_WIN32))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will need updated to account for #11440 being merged.

*/
if (dmem < 0)
dmem = 0;
return (uint64_t)dmem;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer a blank line after an un-bracketed if, or just enclose the if in braces.

while (!!rc_mode->name && strcmp(rc_mode->name, name) != 0)
while (!!rc_mode->name && astrcmpi(rc_mode->name, name) != 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the rationale for this change?

Comment on lines +237 to +241
/* VAAPI-based encoders unfortunately use an integer for "profile".
* Until a string-based "profile" can be used with VAAPI, find the
* corresponding integer value and update the settings with an
* integer-based "profile".
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines can be wrapped at 120 columns.

* integer-based "profile".
*/
if (strstr(encoder_type, "vaapi")) {
// Move the "profile" string to "profile_str"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For comments using complete sentences, add final punctuation at the end.

Comment on lines +227 to +229
/* Get the amount of dedicated GDDR memory, aka VRAM, in
* units of kilobytes.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines can be wrapped at 120 columns.

dmem = 0;
if (total_mem < 0)
total_mem = 0;
return (uint64_t)total_mem - dmem;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer a blank line after an un-bracketed if, or enclose all ifs in braces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Linux Affects Linux New Feature New feature or plugin
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants