Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
201bcda
libobs,docs: Add protocol in Outputs API
tytan652 Aug 21, 2021
855956f
obs-outputs,obs-ffmpeg: Add protocol to service outputs
tytan652 Aug 21, 2021
513c6bc
rtmp-services: Add protocols to services JSON
tytan652 Apr 25, 2022
813b3b2
libobs,docs: Add protocol to Services API
tytan652 Sep 5, 2022
56411ed
libobs,docs: Add protocol enumeration functions
tytan652 Sep 5, 2022
fa58a38
rtmp-services: Add protocol getter to services
tytan652 Sep 5, 2022
48dc6dd
libobs,docs: Add supported codecs functions with output id
tytan652 Sep 28, 2022
5a40934
UI: Refactor Qt slots in stream settings page
tytan652 Sep 21, 2022
e00e271
UI: Use protocol to list compatible codecs
tytan652 Sep 28, 2022
9d8d98b
rtmp-services: Remove fallback to H264 if no codec found
tytan652 Oct 7, 2022
44ca002
UI: Use protocol to enable network options
tytan652 Sep 28, 2022
8d6cacc
UI: Remove hardcoded stream codec list
tytan652 Sep 28, 2022
e317c88
obs-ffmpeg: Remove AV1 from SRT/RIST supported codecs
tytan652 Jan 19, 2023
1e0f4a6
libobs,docs: Add preferred output type to Service API
tytan652 Oct 7, 2022
213eb61
rtmp-services: Remove output getter from rtmp_common
tytan652 Oct 7, 2022
ea28587
UI: Select streaming output based on the protocol
tytan652 Oct 7, 2022
aeab6b8
libobs,docs,rtmps-services: Add supported audio codecs
tytan652 Oct 7, 2022
1514f15
plugins: Fix codec name on AAC encoders
tytan652 Dec 15, 2022
cd83b94
plugins: Rename audio encoders
tytan652 Dec 15, 2022
b2d686e
obs-ffmpeg: Allow opus for SRT and RIST
tytan652 Dec 15, 2022
b15684a
UI: Add Opus bitrate map and per encoder bitrate list
tytan652 Dec 17, 2022
5fe417b
UI: Add audio codec selections
tytan652 Dec 17, 2022
ea4ac2d
libobs,docs: Add connect infos to the Services API
tytan652 Jan 19, 2023
258e339
rtmp-services: Add connect infos to services
tytan652 Jan 19, 2023
5e4ed49
obs-outputs,obs-ffmpeg: Use connect infos in outputs
tytan652 Jan 19, 2023
22ebed8
libobs,docs: Add connect infos check to the Services API
tytan652 Jan 19, 2023
d917cea
rtmp-services: Add connect infos checks
tytan652 Jan 19, 2023
215426b
UI: Use connect infos check in before stream check
tytan652 Jan 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 206 additions & 26 deletions UI/audio-encoders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@ static const char *EncoderName(const std::string &id)
return NullToEmpty(obs_encoder_get_display_name(id.c_str()));
}

static map<int, std::string> bitrateMap;

static void HandleIntProperty(obs_property_t *prop, const char *id)
static void HandleIntProperty(obs_property_t *prop, std::vector<int> &bitrates)
{
const int max_ = obs_property_int_max(prop);
const int step = obs_property_int_step(prop);

for (int i = obs_property_int_min(prop); i <= max_; i += step)
bitrateMap[i] = id;
bitrates.push_back(i);
}

static void HandleListProperty(obs_property_t *prop, const char *id)
static void HandleListProperty(obs_property_t *prop, const char *id,
std::vector<int> &bitrates)
{
obs_combo_format format = obs_property_list_format(prop);
if (format != OBS_COMBO_FORMAT_INT) {
Expand All @@ -53,7 +52,7 @@ static void HandleListProperty(obs_property_t *prop, const char *id)

int bitrate =
static_cast<int>(obs_property_list_item_int(prop, i));
bitrateMap[bitrate] = id;
bitrates.push_back(bitrate);
}
}

Expand Down Expand Up @@ -86,7 +85,7 @@ static void HandleSampleRate(obs_property_t *prop, const char *id)
obs_property_modified(prop, data.get());
}

static void HandleEncoderProperties(const char *id)
static void HandleEncoderProperties(const char *id, std::vector<int> &bitrates)
{
auto DestroyProperties = [](obs_properties_t *props) {
obs_properties_destroy(props);
Expand All @@ -112,10 +111,10 @@ static void HandleEncoderProperties(const char *id)
obs_property_type type = obs_property_get_type(bitrate);
switch (type) {
case OBS_PROPERTY_INT:
return HandleIntProperty(bitrate, id);
return HandleIntProperty(bitrate, bitrates);

case OBS_PROPERTY_LIST:
return HandleListProperty(bitrate, id);
return HandleListProperty(bitrate, id, bitrates);

default:
break;
Expand All @@ -132,10 +131,75 @@ static const char *GetCodec(const char *id)
return NullToEmpty(obs_get_encoder_codec(id));
}

static void PopulateBitrateMap()
static std::vector<int> fallbackBitrates;
static map<std::string, std::vector<int>> encoderBitrates;

static void PopulateBitrateLists()
{
static once_flag once;

call_once(once, []() {
struct obs_audio_info aoi;
obs_get_audio_info(&aoi);
uint32_t output_channels = get_audio_channels(aoi.speakers);

/* NOTE: ffmpeg_aac and ffmpeg_opus have the same properties
* their bitrates will also be used as a fallback */
HandleEncoderProperties("ffmpeg_aac", fallbackBitrates);

if (fallbackBitrates.empty())
blog(LOG_ERROR, "Could not enumerate fallback encoder "
"bitrates");

ostringstream ss;
for (auto &bitrate : fallbackBitrates)
ss << "\n " << setw(3) << bitrate << " kbit/s:";

blog(LOG_DEBUG, "Fallback encoder bitrates:%s",
ss.str().c_str());

const char *id = nullptr;
for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
if (obs_get_encoder_type(id) != OBS_ENCODER_AUDIO)
continue;

if (strcmp(id, "ffmpeg_aac") == 0 ||
strcmp(id, "ffmpeg_opus") == 0)
continue;

std::string encoder = id;

HandleEncoderProperties(id, encoderBitrates[encoder]);

if (encoderBitrates[encoder].empty())
blog(LOG_ERROR,
"Could not enumerate %s encoder "
"bitrates",
id);

ostringstream ss;
for (auto &bitrate : encoderBitrates[encoder])
ss << "\n " << setw(3) << bitrate
<< " kbit/s";

blog(LOG_DEBUG, "%s (%s) encoder bitrates:%s",
EncoderName(id), id, ss.str().c_str());
}

if (encoderBitrates.empty() && fallbackBitrates.empty())
blog(LOG_ERROR, "Could not enumerate any audio encoder "
"bitrates");
});
}

static map<int, std::string> simpleAACBitrateMap;

static void PopulateSimpleAACBitrateMap()
{
PopulateBitrateLists();

static once_flag once;

call_once(once, []() {
const string encoders[] = {
"ffmpeg_aac",
Expand All @@ -148,7 +212,8 @@ static void PopulateBitrateMap()
struct obs_audio_info aoi;
obs_get_audio_info(&aoi);

HandleEncoderProperties(fallbackEncoder.c_str());
for (auto &bitrate : fallbackBitrates)
simpleAACBitrateMap[bitrate] = fallbackEncoder;

const char *id = nullptr;
for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
Expand All @@ -160,48 +225,115 @@ static void PopulateBitrateMap()
end(encoders))
continue;

if (strcmp(GetCodec(id), "AAC") != 0)
if (strcmp(GetCodec(id), "aac") != 0)
continue;

std::string encoder = id;
if (encoderBitrates[encoder].empty())
continue;

HandleEncoderProperties(id);
for (auto &bitrate : encoderBitrates[encoder])
simpleAACBitrateMap[bitrate] = encoder;
}

for (auto &encoder : encoders) {
if (encoder == fallbackEncoder)
continue;

if (strcmp(GetCodec(encoder.c_str()), "AAC") != 0)
if (strcmp(GetCodec(encoder.c_str()), "aac") != 0)
continue;

HandleEncoderProperties(encoder.c_str());
for (auto &bitrate : encoderBitrates[encoder])
simpleAACBitrateMap[bitrate] = encoder;
}

if (bitrateMap.empty()) {
if (simpleAACBitrateMap.empty()) {
blog(LOG_ERROR, "Could not enumerate any AAC encoder "
"bitrates");
return;
}

ostringstream ss;
for (auto &entry : bitrateMap)
for (auto &entry : simpleAACBitrateMap)
ss << "\n " << setw(3) << entry.first
<< " kbit/s: '" << EncoderName(entry.second) << "' ("
<< entry.second << ')';

blog(LOG_DEBUG, "AAC simple encoder bitrate mapping:%s",
ss.str().c_str());
});
}

static map<int, std::string> simpleOpusBitrateMap;

static void PopulateSimpleOpusBitrateMap()
{
PopulateBitrateLists();

static once_flag once;

call_once(once, []() {
struct obs_audio_info aoi;
obs_get_audio_info(&aoi);
uint32_t output_channels = get_audio_channels(aoi.speakers);

for (auto &bitrate : fallbackBitrates)
simpleOpusBitrateMap[bitrate] = "ffmpeg_opus";

const char *id = nullptr;
for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
if (strcmp(GetCodec(id), "opus") != 0)
continue;

std::string encoder = id;
if (encoderBitrates[encoder].empty())
continue;

for (auto &bitrate : encoderBitrates[encoder])
simpleOpusBitrateMap[bitrate] = encoder;
}

if (simpleOpusBitrateMap.empty()) {
blog(LOG_ERROR, "Could not enumerate any Opus encoder "
"bitrates");
return;
}

ostringstream ss;
for (auto &entry : simpleOpusBitrateMap)
ss << "\n " << setw(3) << entry.first
<< " kbit/s: '" << EncoderName(entry.second) << "' ("
<< entry.second << ')';

blog(LOG_DEBUG, "AAC encoder bitrate mapping:%s",
blog(LOG_DEBUG, "Opus simple encoder bitrate mapping:%s",
ss.str().c_str());
});
}

const map<int, std::string> &GetAACEncoderBitrateMap()
const map<int, std::string> &GetSimpleAACEncoderBitrateMap()
{
PopulateBitrateMap();
return bitrateMap;
PopulateSimpleAACBitrateMap();
return simpleAACBitrateMap;
}

const map<int, std::string> &GetSimpleOpusEncoderBitrateMap()
{
PopulateSimpleOpusBitrateMap();
return simpleOpusBitrateMap;
}

const char *GetSimpleAACEncoderForBitrate(int bitrate)
{
auto &map_ = GetSimpleAACEncoderBitrateMap();
auto res = map_.find(bitrate);
if (res == end(map_))
return NULL;
return res->second.c_str();
}

const char *GetAACEncoderForBitrate(int bitrate)
const char *GetSimpleOpusEncoderForBitrate(int bitrate)
{
auto &map_ = GetAACEncoderBitrateMap();
auto &map_ = GetSimpleOpusEncoderBitrateMap();
auto res = map_.find(bitrate);
if (res == end(map_))
return NULL;
Expand All @@ -210,13 +342,13 @@ const char *GetAACEncoderForBitrate(int bitrate)

#define INVALID_BITRATE 10000

int FindClosestAvailableAACBitrate(int bitrate)
static int FindClosestAvailableSimpleBitrate(int bitrate,
const map<int, std::string> &map)
{
auto &map_ = GetAACEncoderBitrateMap();
int prev = 0;
int next = INVALID_BITRATE;

for (auto val : map_) {
for (auto val : map) {
if (next > val.first) {
if (val.first == bitrate)
return bitrate;
Expand All @@ -234,3 +366,51 @@ int FindClosestAvailableAACBitrate(int bitrate)
return prev;
return 192;
}

int FindClosestAvailableSimpleAACBitrate(int bitrate)
{
return FindClosestAvailableSimpleBitrate(
bitrate, GetSimpleAACEncoderBitrateMap());
}

int FindClosestAvailableSimpleOpusBitrate(int bitrate)
{
return FindClosestAvailableSimpleBitrate(
bitrate, GetSimpleOpusEncoderBitrateMap());
}

const std::vector<int> &GetAudioEncoderBitrates(const char *id)
{
std::string encoder = id;
PopulateBitrateLists();
if (encoderBitrates[encoder].empty())
return fallbackBitrates;
return encoderBitrates[encoder];
}

int FindClosestAvailableAudioBitrate(const char *id, int bitrate)
{
int prev = 0;
int next = INVALID_BITRATE;
std::string encoder = id;

for (auto val : encoderBitrates[encoder].empty()
? fallbackBitrates
: encoderBitrates[encoder]) {
if (next > val) {
if (val == bitrate)
return bitrate;

if (val < next && val > bitrate)
next = val;
if (val > prev && val < bitrate)
prev = val;
}
}

if (next != INVALID_BITRATE)
return next;
if (prev != 0)
return prev;
return 192;
}
14 changes: 11 additions & 3 deletions UI/audio-encoders.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
#include <obs.hpp>

#include <map>
#include <vector>

const std::map<int, std::string> &GetAACEncoderBitrateMap();
const char *GetAACEncoderForBitrate(int bitrate);
int FindClosestAvailableAACBitrate(int bitrate);
const std::map<int, std::string> &GetSimpleAACEncoderBitrateMap();
const char *GetSimpleAACEncoderForBitrate(int bitrate);
int FindClosestAvailableSimpleAACBitrate(int bitrate);

const std::map<int, std::string> &GetSimpleOpusEncoderBitrateMap();
const char *GetSimpleOpusEncoderForBitrate(int bitrate);
int FindClosestAvailableSimpleOpusBitrate(int bitrate);

const std::vector<int> &GetAudioEncoderBitrates(const char *id);
int FindClosestAvailableAudioBitrate(const char *id, int bitrate);
5 changes: 4 additions & 1 deletion UI/data/locale/en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ Basic.AutoConfig.TestPage="Final Results"
Basic.AutoConfig.TestPage.SubTitle.Testing="The program is now executing a set of tests to estimate the ideal settings"
Basic.AutoConfig.TestPage.SubTitle.Complete="Testing complete"
Basic.AutoConfig.TestPage.TestingBandwidth="Performing bandwidth test, this may take a few minutes..."
Basic.AutoConfig.TestPage.TestingBandwidth.NoOutput="No output for the protocol of this service was found"
Basic.AutoConfig.TestPage.TestingBandwidth.Connecting="Connecting to: %1..."
Basic.AutoConfig.TestPage.TestingBandwidth.ConnectFailed="Failed to connect to any servers, please check your internet connection and try again."
Basic.AutoConfig.TestPage.TestingBandwidth.Server="Testing bandwidth for: %1"
Expand Down Expand Up @@ -941,7 +942,8 @@ Basic.Settings.Output.Format.HLS="HLS (.m3u8 + .ts)"
Basic.Settings.Output.Format.fMP4="Fragmented MP4 (.mp4)"
Basic.Settings.Output.Format.fMOV="Fragmented MOV (.mov)"
Basic.Settings.Output.Format.TT="Fragmented MP4/MOV writes the recording in chunks and does not require the same finalization as traditional MP4/MOV files.\nThis ensures the file remains playable even if writing to disk is interrupted, for example, as a result of a BSOD or power loss.\n\nThis may not be compatible with all players and editors. Use File → Remux Recordings to convert the file into a more compatible format if necessary."
Basic.Settings.Output.Encoder="Encoder"
Basic.Settings.Output.Encoder.Video="Video Encoder"
Basic.Settings.Output.Encoder.Audio="Audio Encoder"
Basic.Settings.Output.SelectDirectory="Select Recording Directory"
Basic.Settings.Output.SelectFile="Select Recording File"
Basic.Settings.Output.DynamicBitrate="Dynamically change bitrate to manage congestion"
Expand Down Expand Up @@ -992,6 +994,7 @@ Basic.Settings.Output.Warn.EnforceResolutionFPS.Resolution="Resolution: %1"
Basic.Settings.Output.Warn.EnforceResolutionFPS.FPS="FPS: %1"
Basic.Settings.Output.Warn.ServiceCodecCompatibility.Title="Incompatible Encoder"
Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg="The streaming service \"%1\" does not support the encoder \"%2\". The encoder will be changed to \"%3\".\n\nDo you want to continue?"
Basic.Settings.Output.Warn.ServiceCodecCompatibility.Msg2="The streaming service \"%1\" does not support encoders \"%2\" and \"%3\". These encoders will be changed to \"%4\" and \"%5\".\n\nDo you want to continue?"
Basic.Settings.Output.VideoBitrate="Video Bitrate"
Basic.Settings.Output.AudioBitrate="Audio Bitrate"
Basic.Settings.Output.Reconnect="Automatically Reconnect"
Expand Down
Loading