mirror of
https://github.com/obsproject/obs-studio.git
synced 2024-07-01 00:53:31 +00:00
Compare commits
18 commits
2116adccb3
...
0aca296e07
Author | SHA1 | Date | |
---|---|---|---|
|
0aca296e07 | ||
|
9d67bf2662 | ||
|
8892edde05 | ||
|
06291c7201 | ||
|
fb5bbc8575 | ||
|
7d19add10b | ||
|
4830d6903e | ||
|
47fb194223 | ||
|
edcda5a825 | ||
|
2e6e79b4f5 | ||
|
b34fbb116e | ||
|
4517918c7a | ||
|
6cc0e2b803 | ||
|
ed2478535f | ||
|
fd34bab615 | ||
|
39c47517ca | ||
|
ddf72e3261 | ||
|
0f18d3914d |
|
@ -1,6 +1,6 @@
|
|||
target_sources(obs-studio PRIVATE platform-x11.cpp)
|
||||
target_compile_definitions(obs-studio PRIVATE OBS_INSTALL_PREFIX="${OBS_INSTALL_PREFIX}")
|
||||
target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate procstat)
|
||||
target_link_libraries(obs-studio PRIVATE Qt::GuiPrivate Qt::DBus procstat)
|
||||
|
||||
target_sources(obs-studio PRIVATE system-info-posix.cpp)
|
||||
|
||||
|
|
|
@ -1566,10 +1566,10 @@ FailedToStartStream.MissingConfigURL="No config URL available for the current se
|
|||
FailedToStartStream.NoCustomRTMPURLInSettings="Custom RTMP URL not specified"
|
||||
FailedToStartStream.InvalidCustomConfig="Invalid custom config"
|
||||
FailedToStartStream.FailedToCreateMultitrackVideoService="Failed to create multitrack video service"
|
||||
FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video rtmp output"
|
||||
FailedToStartStream.FailedToCreateMultitrackVideoOutput="Failed to create multitrack video RTMP output"
|
||||
FailedToStartStream.EncoderNotAvailable="NVENC not available.\n\nFailed to find encoder type '%1'"
|
||||
FailedToStartStream.FailedToCreateVideoEncoder="Failed to create video encoder '%1' (type: '%2')"
|
||||
FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get obs video info while creating encoder '%1' (type: '%2')"
|
||||
FailedToStartStream.FailedToGetOBSVideoInfo="Failed to get OBS video info while creating encoder '%1' (type: '%2')"
|
||||
FailedToStartStream.FailedToCreateAudioEncoder="Failed to create audio encoder"
|
||||
FailedToStartStream.NoRTMPURLInConfig="Config does not contain stream target RTMP(S) URL"
|
||||
FailedToStartStream.FallbackToDefault="Starting the stream using %1 failed; do you want to retry using single encode settings?"
|
||||
|
@ -1585,3 +1585,6 @@ MultitrackVideo.IncompatibleSettings.Title="Incompatible Settings"
|
|||
MultitrackVideo.IncompatibleSettings.Text="%1 is not currently compatible with:\n\n%2\nTo continue streaming with %1, disable incompatible settings:\n\n%3\nand Start Streaming again."
|
||||
MultitrackVideo.IncompatibleSettings.DisableAndStartStreaming="Disable for this stream and Start Streaming"
|
||||
MultitrackVideo.IncompatibleSettings.UpdateAndStartStreaming="Update Settings and Start Streaming"
|
||||
MultitrackVideo.IncompatibleSettings.AudioChannels="%1 is not currently compatible with [Audio → General → Channels] set to '%2', %3"
|
||||
MultitrackVideo.IncompatibleSettings.AudioChannelsSingle="[Audio → General → Channels] needs to be set to '%1'"
|
||||
MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple="%1 requires multiple different settings for [Audio → General → Channels]"
|
||||
|
|
|
@ -477,7 +477,7 @@ OBSDock > QWidget {
|
|||
}
|
||||
|
||||
#transitionsFrame {
|
||||
padding: 4px 8px;
|
||||
padding: var(--padding_large);
|
||||
}
|
||||
|
||||
OBSDock QLabel {
|
||||
|
@ -827,7 +827,7 @@ QComboBox::drop-down,
|
|||
QDateTimeEdit::drop-down {
|
||||
border:none;
|
||||
border-left: 1px solid var(--grey6);
|
||||
width: 32px;
|
||||
width: var(--input_height);
|
||||
}
|
||||
|
||||
QComboBox::down-arrow,
|
||||
|
@ -923,7 +923,7 @@ QDoubleSpinBox::up-button {
|
|||
subcontrol-origin: padding;
|
||||
subcontrol-position: top right; /* position at the top right corner */
|
||||
|
||||
width: 32px;
|
||||
width: var(--input_height);
|
||||
height: var(--spinbox_button_height);
|
||||
border-left: 1px solid var(--grey6);
|
||||
border-bottom: 1px solid transparent;
|
||||
|
@ -936,7 +936,7 @@ QDoubleSpinBox::down-button {
|
|||
subcontrol-origin: padding;
|
||||
subcontrol-position: bottom right; /* position at the top right corner */
|
||||
|
||||
width: 32px;
|
||||
width: var(--input_height);
|
||||
height: var(--spinbox_button_height);
|
||||
border-left: 1px solid var(--grey6);
|
||||
border-top: 1px solid var(--grey6);
|
||||
|
@ -1078,6 +1078,8 @@ QPushButton:hover {
|
|||
|
||||
QToolButton:hover,
|
||||
QToolButton:focus,
|
||||
QPushButton[toolButton="true"]:hover,
|
||||
QPushButton[toolButton="true"]:focus,
|
||||
MuteCheckBox::indicator:hover,
|
||||
MuteCheckBox::indicator:focus {
|
||||
border-color: var(--button_border);
|
||||
|
@ -1104,7 +1106,9 @@ QPushButton:pressed:hover {
|
|||
}
|
||||
|
||||
QToolButton:pressed,
|
||||
QToolButton:pressed:hover {
|
||||
QToolButton:pressed:hover,
|
||||
QPushButton[toolButton="true"]:pressed,
|
||||
QPushButton[toolButton="true"]:pressed:hover {
|
||||
background-color: var(--button_bg_down);
|
||||
border-color: var(--button_border);
|
||||
}
|
||||
|
@ -1275,7 +1279,6 @@ VolControl #volLabel {
|
|||
#vMixerScrollArea VolControl #volLabel {
|
||||
padding: var(--padding_base) 0px var(--padding_base);
|
||||
min-width: var(--volume_slider_label);
|
||||
max-width: var(--volume_slider_label);
|
||||
margin-left: var(--padding_xlarge);
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
--grey2: rgb(134,135,134);
|
||||
--grey3: rgb(122,121,122);
|
||||
--grey4: rgb(76,76,76);
|
||||
--grey5: rgb(88,87,88);
|
||||
--grey5: rgb(70,69,70);
|
||||
--grey6: rgb(31,30,31);
|
||||
--grey7: rgb(58,57,58);
|
||||
--grey8: rgb(46,45,46);
|
||||
|
@ -31,8 +31,12 @@
|
|||
/* OS Fixes */
|
||||
--os_mac_font_base_value: 11;
|
||||
|
||||
--font_small: calc(0.8pt * var(--font_base_value));
|
||||
|
||||
--icon_base: calc(6px + var(--font_base_value));
|
||||
|
||||
--padding_xlarge: calc(2px + calc(0.5px * var(--padding_base_value)));
|
||||
|
||||
--padding_wide: calc(18px + calc(0.25 * var(--padding_base_value)));
|
||||
--padding_menu: calc(8px + calc(1 * var(--padding_base_value)));
|
||||
|
||||
|
@ -45,14 +49,14 @@
|
|||
--border_radius_large: 2px;
|
||||
|
||||
--input_bg: var(--grey4);
|
||||
--input_bg_hover: var(--grey5);
|
||||
--input_bg_hover: var(--grey1);
|
||||
--input_bg_focus: var(--grey6);
|
||||
|
||||
--list_item_bg_selected: var(--primary);
|
||||
--list_item_bg_hover: var(--primary_light);
|
||||
|
||||
--input_border: var(--grey4);
|
||||
--input_border_hover: var(--grey5);
|
||||
--input_border_hover: var(--grey1);
|
||||
--input_border_focus: var(--grey6);
|
||||
|
||||
--spacing_input: var(--spacing_base);
|
||||
|
@ -91,11 +95,17 @@ QStatusBar {
|
|||
background-color: var(--bg_window);
|
||||
}
|
||||
|
||||
OBSDock > QWidget {
|
||||
border-top: 1px solid var(--border_color);
|
||||
padding-top: var(--spacing_large);
|
||||
}
|
||||
|
||||
QDockWidget {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
QDockWidget::title {
|
||||
background-color: var(--grey5);
|
||||
padding: var(--dock_title_padding);
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -104,6 +114,10 @@ QDockWidget > QWidget {
|
|||
background: var(--bg_window);
|
||||
}
|
||||
|
||||
#transitionsFrame {
|
||||
padding: var(--padding_xlarge);
|
||||
}
|
||||
|
||||
SceneTree::item,
|
||||
SourceTreeItem {
|
||||
border-width: 0px;
|
||||
|
@ -193,12 +207,20 @@ OBSBasicSettings QListWidget::item {
|
|||
padding: 4px;
|
||||
}
|
||||
|
||||
QPushButton:checked {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
QToolButton,
|
||||
QPushButton[toolButton="true"] {
|
||||
background-color: var(--bg_window);
|
||||
border-color: var(--bg_window);
|
||||
}
|
||||
|
||||
#stackedMixerArea QScrollArea {
|
||||
background: var(--bg_base);
|
||||
}
|
||||
|
||||
#stackedMixerArea QPushButton {
|
||||
min-width: var(--icon_base);
|
||||
padding: var(--padding_large) var(--padding_large);
|
||||
|
@ -215,6 +237,33 @@ QPushButton[toolButton="true"] {
|
|||
border: none;
|
||||
}
|
||||
|
||||
#hMixerScrollArea VolControl {
|
||||
padding: 0px 2px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
#hMixerScrollArea QLabel {
|
||||
margin: var(--padding_xlarge) 0px;
|
||||
}
|
||||
|
||||
#hMixerScrollArea #volMeterFrame {
|
||||
margin-top: var(--spacing_large);
|
||||
}
|
||||
|
||||
#vMixerScrollArea VolControl {
|
||||
padding: 0px var(--padding_xlarge) var(--spacing_large);
|
||||
border-right: 1px solid var(--bg_window);
|
||||
}
|
||||
|
||||
#vMixerScrollArea QLabel {
|
||||
font-size: var(--font_small);
|
||||
margin: var(--padding_xlarge) 0px;
|
||||
}
|
||||
|
||||
#vMixerScrollArea #volLabel {
|
||||
font-size: var(--font_base);
|
||||
}
|
||||
|
||||
MuteCheckBox::indicator,
|
||||
MuteCheckBox::indicator:unchecked {
|
||||
background-color: var(--bg_base);
|
||||
|
@ -255,3 +304,7 @@ VolumeMeter {
|
|||
qproperty-minorTickColor: rgb(122,121,122); /* light */
|
||||
qproperty-meterThickness: 3;
|
||||
}
|
||||
|
||||
OBSBasicStats {
|
||||
background: var(--bg_window);
|
||||
}
|
||||
|
|
|
@ -128,6 +128,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>addIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -157,6 +160,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>removeIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -186,6 +192,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>upArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -215,6 +224,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>downArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -350,6 +362,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>addIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -379,6 +394,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>removeIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -408,6 +426,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>upArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -437,6 +458,9 @@
|
|||
<property name="themeID" stdset="0">
|
||||
<string>downArrowIconSmall</string>
|
||||
</property>
|
||||
<property name="toolButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <obs-app.hpp>
|
||||
#include <obs.hpp>
|
||||
#include <remote-text.hpp>
|
||||
#include <window-basic-main.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
|
@ -331,7 +332,8 @@ struct OBSOutputs {
|
|||
};
|
||||
|
||||
static OBSOutputs
|
||||
SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
||||
SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name,
|
||||
obs_data_t *dump_stream_to_file_config,
|
||||
const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
|
@ -465,7 +467,8 @@ void MultitrackVideoOutput::PrepareStreaming(
|
|||
|
||||
std::vector<OBSEncoderAutoRelease> audio_encoders;
|
||||
std::shared_ptr<obs_encoder_group_t> video_encoder_group;
|
||||
auto outputs = SetupOBSOutput(dump_stream_to_file_config, output_config,
|
||||
auto outputs = SetupOBSOutput(parent, multitrack_video_name,
|
||||
dump_stream_to_file_config, output_config,
|
||||
audio_encoders, video_encoder_group,
|
||||
audio_encoder_id, main_audio_mixer,
|
||||
vod_track_mixer);
|
||||
|
@ -730,8 +733,46 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config,
|
|||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
obs_output_t *output, obs_output_t *recording_output,
|
||||
const char *audio_encoder_id, size_t main_audio_mixer,
|
||||
std::optional<size_t> vod_track_mixer)
|
||||
std::optional<size_t> vod_track_mixer,
|
||||
std::vector<speaker_layout> &speaker_layouts,
|
||||
speaker_layout ¤t_layout)
|
||||
{
|
||||
speaker_layout speakers = SPEAKERS_UNKNOWN;
|
||||
obs_audio_info oai = {};
|
||||
if (obs_get_audio_info(&oai))
|
||||
speakers = oai.speakers;
|
||||
|
||||
current_layout = speakers;
|
||||
|
||||
auto sanitize_audio_channels = [&](obs_encoder_t *encoder,
|
||||
uint32_t channels) {
|
||||
speaker_layout target_speakers = SPEAKERS_UNKNOWN;
|
||||
for (size_t i = 0; i <= (size_t)SPEAKERS_7POINT1; i++) {
|
||||
if (get_audio_channels((speaker_layout)i) != channels)
|
||||
continue;
|
||||
|
||||
target_speakers = (speaker_layout)i;
|
||||
break;
|
||||
}
|
||||
if (target_speakers == SPEAKERS_UNKNOWN) {
|
||||
blog(LOG_WARNING,
|
||||
"MultitrackVideoOutput: Could not find "
|
||||
"speaker layout for %" PRIu32 "channels "
|
||||
"while configuring encoder '%s'",
|
||||
channels, obs_encoder_get_name(encoder));
|
||||
return;
|
||||
}
|
||||
if (speakers != SPEAKERS_UNKNOWN &&
|
||||
(channels > get_audio_channels(speakers) ||
|
||||
speakers == target_speakers))
|
||||
return;
|
||||
|
||||
auto it = std::find(std::begin(speaker_layouts),
|
||||
std::end(speaker_layouts), target_speakers);
|
||||
if (it == std::end(speaker_layouts))
|
||||
speaker_layouts.push_back(target_speakers);
|
||||
};
|
||||
|
||||
using encoder_configs_type =
|
||||
decltype(go_live_config.audio_configurations.live);
|
||||
DStr encoder_name_buffer;
|
||||
|
@ -757,6 +798,10 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config,
|
|||
create_audio_encoder(encoder_name_buffer->array,
|
||||
audio_encoder_id, settings,
|
||||
mixer_idx);
|
||||
|
||||
sanitize_audio_channels(audio_encoder,
|
||||
configs[i].channels);
|
||||
|
||||
obs_output_set_audio_encoder(output, audio_encoder,
|
||||
output_encoder_index);
|
||||
if (recording_output)
|
||||
|
@ -784,8 +829,80 @@ create_audio_encoders(const GoLiveApi::Config &go_live_config,
|
|||
return;
|
||||
}
|
||||
|
||||
static const char *speaker_layout_to_string(speaker_layout layout)
|
||||
{
|
||||
switch (layout) {
|
||||
case SPEAKERS_MONO:
|
||||
return "Mono";
|
||||
case SPEAKERS_2POINT1:
|
||||
return "2.1";
|
||||
case SPEAKERS_4POINT0:
|
||||
return "4.0";
|
||||
case SPEAKERS_4POINT1:
|
||||
return "4.1";
|
||||
case SPEAKERS_5POINT1:
|
||||
return "5.1";
|
||||
case SPEAKERS_7POINT1:
|
||||
return "7.1";
|
||||
case SPEAKERS_UNKNOWN:
|
||||
case SPEAKERS_STEREO:
|
||||
return "Stereo";
|
||||
}
|
||||
|
||||
return "Stereo";
|
||||
}
|
||||
|
||||
static void handle_speaker_layout_issues(
|
||||
QWidget *parent, const QString &multitrack_video_name,
|
||||
const std::vector<speaker_layout> &requested_layouts,
|
||||
speaker_layout layout)
|
||||
{
|
||||
if (requested_layouts.empty())
|
||||
return;
|
||||
|
||||
QString message;
|
||||
if (requested_layouts.size() == 1) {
|
||||
message =
|
||||
QTStr("MultitrackVideo.IncompatibleSettings.AudioChannelsSingle")
|
||||
.arg(QTStr(speaker_layout_to_string(
|
||||
requested_layouts.front())));
|
||||
} else {
|
||||
message =
|
||||
QTStr("MultitrackVideo.IncompatibleSettings.AudioChannelsMultiple")
|
||||
.arg(multitrack_video_name);
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
parent,
|
||||
[&] {
|
||||
QMessageBox mb(parent);
|
||||
mb.setIcon(QMessageBox::Critical);
|
||||
mb.setWindowTitle(QTStr(
|
||||
"MultitrackVideo.IncompatibleSettings.Title"));
|
||||
mb.setText(
|
||||
QTStr("MultitrackVideo.IncompatibleSettings.AudioChannels")
|
||||
.arg(multitrack_video_name)
|
||||
.arg(QTStr(speaker_layout_to_string(
|
||||
layout)))
|
||||
.arg(message));
|
||||
|
||||
mb.setStandardButtons(
|
||||
QMessageBox::StandardButton::Cancel);
|
||||
|
||||
mb.exec();
|
||||
},
|
||||
BlockingConnectionTypeFor(parent));
|
||||
|
||||
blog(LOG_INFO,
|
||||
"MultitrackVideoOutput: Attempted to start stream with incompatible "
|
||||
"audio channel setting. Action taken: cancel");
|
||||
|
||||
throw MultitrackVideoError::cancel();
|
||||
}
|
||||
|
||||
static OBSOutputs
|
||||
SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
||||
SetupOBSOutput(QWidget *parent, const QString &multitrack_video_name,
|
||||
obs_data_t *dump_stream_to_file_config,
|
||||
const GoLiveApi::Config &go_live_config,
|
||||
std::vector<OBSEncoderAutoRelease> &audio_encoders,
|
||||
std::shared_ptr<obs_encoder_group_t> &video_encoder_group,
|
||||
|
@ -803,9 +920,15 @@ SetupOBSOutput(obs_data_t *dump_stream_to_file_config,
|
|||
recording_output))
|
||||
return {nullptr, nullptr};
|
||||
|
||||
std::vector<speaker_layout> requested_speaker_layouts;
|
||||
speaker_layout current_layout = SPEAKERS_UNKNOWN;
|
||||
create_audio_encoders(go_live_config, audio_encoders, output,
|
||||
recording_output, audio_encoder_id,
|
||||
main_audio_mixer, vod_track_mixer);
|
||||
main_audio_mixer, vod_track_mixer,
|
||||
requested_speaker_layouts, current_layout);
|
||||
|
||||
handle_speaker_layout_issues(parent, multitrack_video_name,
|
||||
requested_speaker_layouts, current_layout);
|
||||
|
||||
return {std::move(output), std::move(recording_output)};
|
||||
}
|
||||
|
|
|
@ -277,7 +277,7 @@ endif()
|
|||
|
||||
if(EXISTS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h")
|
||||
file(STRINGS "${FFmpeg_avutil_INCLUDE_DIR}/libavutil/ffversion.h" _version_string
|
||||
REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\.-]+\"[ \t]*$")
|
||||
REGEX "^.*FFMPEG_VERSION[ \t]+\"n?[0-9a-z\\~.-]+\"[ \t]*$")
|
||||
string(REGEX REPLACE ".*FFMPEG_VERSION[ \t]+\"n?([0-9]+\\.[0-9]).*\".*" "\\1" FFmpeg_VERSION "${_version_string}")
|
||||
endif()
|
||||
|
||||
|
|
|
@ -353,15 +353,9 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
|||
|
||||
if (encoder->encoder_group) {
|
||||
pthread_mutex_lock(&encoder->encoder_group->mutex);
|
||||
if (--encoder->encoder_group->num_encoders_started == 0) {
|
||||
if (--encoder->encoder_group->num_encoders_started == 0)
|
||||
encoder->encoder_group->start_timestamp = 0;
|
||||
if (encoder->encoder_group->destroy_on_stop)
|
||||
obs_encoder_group_actually_destroy(
|
||||
encoder->encoder_group);
|
||||
}
|
||||
|
||||
if (encoder->encoder_group)
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
pthread_mutex_unlock(&encoder->encoder_group->mutex);
|
||||
}
|
||||
|
||||
/* obs_encoder_shutdown locks init_mutex, so don't call it on encode
|
||||
|
@ -371,6 +365,8 @@ static void remove_connection(struct obs_encoder *encoder, bool shutdown)
|
|||
* up again */
|
||||
if (shutdown)
|
||||
obs_encoder_shutdown(encoder);
|
||||
encoder->initialized = false;
|
||||
|
||||
set_encoder_active(encoder, false);
|
||||
}
|
||||
|
||||
|
@ -790,14 +786,20 @@ void obs_encoder_start(obs_encoder_t *encoder,
|
|||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
}
|
||||
|
||||
static inline bool obs_encoder_stop_internal(
|
||||
obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param, struct encoder_packet *packet),
|
||||
void *param)
|
||||
void obs_encoder_stop(obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param,
|
||||
struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
bool last = false;
|
||||
size_t idx;
|
||||
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_stop"))
|
||||
return;
|
||||
if (!obs_ptr_valid(new_packet, "obs_encoder_stop"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&encoder->init_mutex);
|
||||
pthread_mutex_lock(&encoder->callbacks_mutex);
|
||||
|
||||
idx = get_callback_idx(encoder, new_packet, param);
|
||||
|
@ -810,34 +812,32 @@ static inline bool obs_encoder_stop_internal(
|
|||
|
||||
if (last) {
|
||||
remove_connection(encoder, true);
|
||||
encoder->initialized = false;
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
|
||||
if (encoder->destroy_on_stop) {
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
struct obs_encoder_group *group = encoder->encoder_group;
|
||||
|
||||
if (encoder->destroy_on_stop)
|
||||
obs_encoder_actually_destroy(encoder);
|
||||
return true;
|
||||
|
||||
/* Destroying the group all the way back here prevents a race
|
||||
* where destruction of the group can prematurely destroy the
|
||||
* encoder within internal functions. This is the point where it
|
||||
* is safe to destroy the group, even if the encoder is then
|
||||
* also destroyed. */
|
||||
if (group) {
|
||||
pthread_mutex_lock(&group->mutex);
|
||||
if (group->destroy_on_stop &&
|
||||
group->num_encoders_started == 0)
|
||||
obs_encoder_group_actually_destroy(group);
|
||||
else
|
||||
pthread_mutex_unlock(&group->mutex);
|
||||
}
|
||||
|
||||
/* init_mutex already unlocked */
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void obs_encoder_stop(obs_encoder_t *encoder,
|
||||
void (*new_packet)(void *param,
|
||||
struct encoder_packet *packet),
|
||||
void *param)
|
||||
{
|
||||
bool destroyed;
|
||||
|
||||
if (!obs_encoder_valid(encoder, "obs_encoder_stop"))
|
||||
return;
|
||||
if (!obs_ptr_valid(new_packet, "obs_encoder_stop"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&encoder->init_mutex);
|
||||
destroyed = obs_encoder_stop_internal(encoder, new_packet, param);
|
||||
if (!destroyed)
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
pthread_mutex_unlock(&encoder->init_mutex);
|
||||
}
|
||||
|
||||
const char *obs_encoder_get_codec(const obs_encoder_t *encoder)
|
||||
|
@ -1334,7 +1334,6 @@ void full_stop(struct obs_encoder *encoder)
|
|||
pthread_mutex_unlock(&encoder->callbacks_mutex);
|
||||
|
||||
remove_connection(encoder, false);
|
||||
encoder->initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ FilePath="File Path"
|
|||
|
||||
AMFOpts="AMF/FFmpeg Options"
|
||||
AMFOpts.ToolTip="Use to specify custom AMF or FFmpeg options. For example, \"level=5.2 profile=main\". Check the AMF encoder docs for more details."
|
||||
AMF.PreAnalysis="Pre-Analysis"
|
||||
AMF.PreAnalysis.ToolTip="Enables improved encoding quality at the cost of performance by calculating additional metrics such as activity and spatial complexity."
|
||||
|
||||
GPU="GPU"
|
||||
BFrames="Max B-frames"
|
||||
|
@ -40,6 +42,7 @@ NVENC.8bitUnsupportedHdr="OBS does not support 8-bit output of Rec. 2100."
|
|||
NVENC.I010Unsupported="NVENC does not support I010. Use P010 instead."
|
||||
NVENC.10bitUnsupported="Cannot perform 10-bit encode on this encoder."
|
||||
NVENC.16bitUnsupported="Cannot perform 16-bit encode on this encoder."
|
||||
NVENC.444Unsupported="Cannot perform 4:4:4 encode on this encoder."
|
||||
NVENC.NoAV1FallbackPossible="AV1 encoding is not available with the current settings. Try disabling any re-scaling or GPU options that may be set. Check the log for more details."
|
||||
NVENC.Preset2.p1="P1: Fastest (Lowest Quality)"
|
||||
NVENC.Preset2.p2="P2: Faster (Lower Quality)"
|
||||
|
|
|
@ -1371,12 +1371,19 @@ static bool init_encoder(struct nvenc_data *enc, enum codec_type codec,
|
|||
int bf = (int)obs_data_get_int(settings, "bf");
|
||||
const bool support_10bit =
|
||||
nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_10BIT_ENCODE);
|
||||
const bool support_444 =
|
||||
nv_get_cap(enc, NV_ENC_CAPS_SUPPORT_YUV444_ENCODE);
|
||||
const int bf_max = nv_get_cap(enc, NV_ENC_CAPS_NUM_MAX_BFRAMES);
|
||||
|
||||
video_t *video = obs_encoder_video(enc->encoder);
|
||||
const struct video_output_info *voi = video_output_get_info(video);
|
||||
enc->in_format = get_preferred_format(voi->format);
|
||||
|
||||
if (enc->in_format == VIDEO_FORMAT_I444 && !support_444) {
|
||||
NV_FAIL(obs_module_text("NVENC.444Unsupported"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_10_bit(enc) && !support_10bit) {
|
||||
NV_FAIL(obs_module_text("NVENC.10bitUnsupported"));
|
||||
return false;
|
||||
|
|
|
@ -504,6 +504,39 @@ static inline int64_t convert_to_obs_ts(amf_base *enc, int64_t ts)
|
|||
return ts * (int64_t)enc->fps_den / amf_timebase;
|
||||
}
|
||||
|
||||
static inline const char *
|
||||
convert_amf_memory_type_to_string(AMF_MEMORY_TYPE type)
|
||||
{
|
||||
switch (type) {
|
||||
case AMF_MEMORY_UNKNOWN:
|
||||
return "Unknown";
|
||||
case AMF_MEMORY_HOST:
|
||||
return "Host";
|
||||
case AMF_MEMORY_DX9:
|
||||
return "DX9";
|
||||
case AMF_MEMORY_DX11:
|
||||
return "DX11";
|
||||
case AMF_MEMORY_OPENCL:
|
||||
return "OpenCL";
|
||||
case AMF_MEMORY_OPENGL:
|
||||
return "OpenGL";
|
||||
case AMF_MEMORY_XV:
|
||||
return "XV";
|
||||
case AMF_MEMORY_GRALLOC:
|
||||
return "Gralloc";
|
||||
case AMF_MEMORY_COMPUTE_FOR_DX9:
|
||||
return "Compute For DX9";
|
||||
case AMF_MEMORY_COMPUTE_FOR_DX11:
|
||||
return "Compute For DX11";
|
||||
case AMF_MEMORY_VULKAN:
|
||||
return "Vulkan";
|
||||
case AMF_MEMORY_DX12:
|
||||
return "DX12";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_to_encoder_packet(amf_base *enc, AMFDataPtr &data,
|
||||
encoder_packet *packet)
|
||||
{
|
||||
|
@ -1160,11 +1193,14 @@ static void check_texture_encode_capability(obs_encoder_t *encoder,
|
|||
|
||||
#include "texture-amf-opts.hpp"
|
||||
|
||||
static void amf_defaults(obs_data_t *settings)
|
||||
/* These are initial recommended settings that may be lowered later
|
||||
* once we know more info such as the resolution and frame rate.
|
||||
*/
|
||||
static void amf_avc_defaults(obs_data_t *settings)
|
||||
{
|
||||
obs_data_set_default_string(settings, "rate_control", "CBR");
|
||||
obs_data_set_default_int(settings, "bitrate", 2500);
|
||||
obs_data_set_default_int(settings, "cqp", 20);
|
||||
obs_data_set_default_string(settings, "rate_control", "CBR");
|
||||
obs_data_set_default_string(settings, "preset", "quality");
|
||||
obs_data_set_default_string(settings, "profile", "high");
|
||||
obs_data_set_default_int(settings, "bf", 3);
|
||||
|
@ -1229,7 +1265,7 @@ static obs_properties_t *amf_properties_internal(amf_codec_type codec)
|
|||
add_preset("speed");
|
||||
#undef add_preset
|
||||
|
||||
if (amf_codec_type::AVC == codec || amf_codec_type::AV1 == codec) {
|
||||
if (amf_codec_type::AVC == codec) {
|
||||
p = obs_properties_add_list(props, "profile",
|
||||
obs_module_text("Profile"),
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
|
@ -1244,6 +1280,11 @@ static obs_properties_t *amf_properties_internal(amf_codec_type codec)
|
|||
#undef add_profile
|
||||
}
|
||||
|
||||
p = obs_properties_add_bool(props, "pre_analysis",
|
||||
obs_module_text("AMF.PreAnalysis"));
|
||||
obs_property_set_long_description(
|
||||
p, obs_module_text("AMF.PreAnalysis.ToolTip"));
|
||||
|
||||
if (amf_codec_type::AVC == codec) {
|
||||
obs_properties_add_int(props, "bf", obs_module_text("BFrames"),
|
||||
0, 5, 1);
|
||||
|
@ -1339,8 +1380,20 @@ static void amf_avc_update_data(amf_base *enc, int rc, int64_t bitrate,
|
|||
set_avc_property(enc, PEAK_BITRATE, bitrate);
|
||||
set_avc_property(enc, VBV_BUFFER_SIZE, bitrate);
|
||||
|
||||
if (rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR) {
|
||||
if (rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR ||
|
||||
rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) {
|
||||
set_avc_property(enc, FILLER_DATA_ENABLE, true);
|
||||
set_avc_property(enc, ENFORCE_HRD, true);
|
||||
} else if (
|
||||
rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR ||
|
||||
rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR) {
|
||||
set_avc_property(enc, PEAK_BITRATE, bitrate * 1.5);
|
||||
set_avc_property(enc, VBV_BUFFER_SIZE, bitrate * 1.5);
|
||||
} else if (rc ==
|
||||
AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR) {
|
||||
int64_t framerate = enc->fps_num / enc->fps_den;
|
||||
set_avc_property(enc, VBV_BUFFER_SIZE,
|
||||
(bitrate / framerate) * 1.1);
|
||||
}
|
||||
} else {
|
||||
set_avc_property(enc, QP_I, qp);
|
||||
|
@ -1384,10 +1437,77 @@ try {
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline void check_recommended_avc_defaults(amf_base *enc,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
int64_t framerate = enc->fps_num / enc->fps_den;
|
||||
if ((enc->cx * enc->cy > 1920 * 1088) || (framerate > 60)) {
|
||||
// Recommended base defaults
|
||||
obs_data_set_default_int(settings, "bitrate", 2500);
|
||||
obs_data_set_default_int(settings, "cqp", 20);
|
||||
obs_data_set_default_string(settings, "rate_control", "CBR");
|
||||
obs_data_set_default_string(settings, "preset", "quality");
|
||||
obs_data_set_default_string(settings, "profile", "high");
|
||||
obs_data_set_default_int(settings, "bf", 0);
|
||||
info("Original default settings were lowered according to resolution"
|
||||
" and framerate.");
|
||||
} else {
|
||||
/* Recommended high perceptual quality defaults
|
||||
* for up to 1080p60fps
|
||||
*/
|
||||
|
||||
bool adaptive_minigop_enabled = true;
|
||||
set_avc_property(enc, ADAPTIVE_MINIGOP,
|
||||
adaptive_minigop_enabled);
|
||||
|
||||
bool pa_enabled = obs_data_get_bool(settings, "pre_analysis");
|
||||
bool pa_set_by_user =
|
||||
obs_data_has_user_value(settings, "pre_analysis");
|
||||
|
||||
if (!pa_set_by_user || (pa_set_by_user && pa_enabled)) {
|
||||
pa_enabled = true;
|
||||
uint64_t pa_lab_depth = 20;
|
||||
AMF_MEMORY_TYPE pa_engine_type = AMF_MEMORY_OPENCL;
|
||||
AMF_PA_TAQ_MODE_ENUM pa_taq_mode = AMF_PA_TAQ_MODE_2;
|
||||
obs_data_set_bool(settings, "pre_analysis", pa_enabled);
|
||||
|
||||
set_avc_property(enc, PRE_ANALYSIS_ENABLE, pa_enabled);
|
||||
set_amf_property(enc, AMF_PA_LOOKAHEAD_BUFFER_DEPTH,
|
||||
pa_lab_depth);
|
||||
set_amf_property(enc, AMF_PA_TAQ_MODE, pa_taq_mode);
|
||||
set_amf_property(enc, AMF_PA_ENGINE_TYPE,
|
||||
AMF_MEMORY_OPENCL);
|
||||
|
||||
info("High perceptual quality defaults:\n"
|
||||
"\tAdaptiveMiniGOP: %s\n"
|
||||
"\tEnablePreAnalysis: %s\n"
|
||||
"\tPALookAheadBufferDepth: %d\n"
|
||||
"\tPATemporalAQMode: %d\n"
|
||||
"\tPAEngineType: %s\n",
|
||||
adaptive_minigop_enabled ? "true" : "false",
|
||||
pa_enabled ? "true" : "false", pa_lab_depth,
|
||||
pa_taq_mode,
|
||||
convert_amf_memory_type_to_string(pa_engine_type));
|
||||
} else {
|
||||
info("High perceptual quality defaults:\n"
|
||||
"\tAdaptiveMiniGOP: %s\n",
|
||||
adaptive_minigop_enabled ? "true" : "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool amf_avc_init(void *data, obs_data_t *settings)
|
||||
{
|
||||
amf_base *enc = (amf_base *)data;
|
||||
|
||||
/* We originally set the recommended defaults for high perceptual quality in
|
||||
* the UI settings. Once the resolution and framerate are available here
|
||||
* during init, we check if the defaults need to be lowered to the base
|
||||
* defaults or the remaining recommended high perceptual quality settings can
|
||||
* be set.
|
||||
*/
|
||||
check_recommended_avc_defaults(enc, settings);
|
||||
|
||||
int64_t bitrate = obs_data_get_int(settings, "bitrate");
|
||||
int64_t qp = obs_data_get_int(settings, "cqp");
|
||||
const char *preset = obs_data_get_string(settings, "preset");
|
||||
|
@ -1417,12 +1537,13 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
|
|||
int rc = get_avc_rate_control(rc_str);
|
||||
|
||||
set_avc_property(enc, RATE_CONTROL_METHOD, rc);
|
||||
if (rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP)
|
||||
if (rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP &&
|
||||
rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR &&
|
||||
rc != AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR)
|
||||
set_avc_property(enc, ENABLE_VBAQ, true);
|
||||
|
||||
amf_avc_update_data(enc, rc, bitrate * 1000, qp);
|
||||
|
||||
set_avc_property(enc, ENFORCE_HRD, true);
|
||||
set_avc_property(enc, HIGH_MOTION_QUALITY_BOOST_ENABLE, false);
|
||||
|
||||
int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
|
||||
|
@ -1452,16 +1573,16 @@ static bool amf_avc_init(void *data, obs_data_t *settings)
|
|||
ffmpeg_opts = "(none)";
|
||||
|
||||
info("settings:\n"
|
||||
"\trate_control: %s\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tcqp: %d\n"
|
||||
"\tkeyint: %d\n"
|
||||
"\tpreset: %s\n"
|
||||
"\tprofile: %s\n"
|
||||
"\tb-frames: %d\n"
|
||||
"\twidth: %d\n"
|
||||
"\theight: %d\n"
|
||||
"\tparams: %s",
|
||||
"\trate_control: %s\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tcqp: %d\n"
|
||||
"\tkeyint: %d\n"
|
||||
"\tpreset: %s\n"
|
||||
"\tprofile: %s\n"
|
||||
"\tb-frames: %d\n"
|
||||
"\twidth: %d\n"
|
||||
"\theight: %d\n"
|
||||
"\toverriding params: %s",
|
||||
rc_str, bitrate, qp, gop_size, preset, profile, bf, enc->cx,
|
||||
enc->cy, ffmpeg_opts);
|
||||
|
||||
|
@ -1614,7 +1735,7 @@ static void register_avc()
|
|||
amf_encoder_info.destroy = amf_destroy;
|
||||
amf_encoder_info.update = amf_avc_update;
|
||||
amf_encoder_info.encode_texture = amf_encode_tex;
|
||||
amf_encoder_info.get_defaults = amf_defaults;
|
||||
amf_encoder_info.get_defaults = amf_avc_defaults;
|
||||
amf_encoder_info.get_properties = amf_avc_properties;
|
||||
amf_encoder_info.get_extra_data = amf_extra_data;
|
||||
amf_encoder_info.caps = OBS_ENCODER_CAP_PASS_TEXTURE |
|
||||
|
@ -1675,6 +1796,18 @@ static inline int get_hevc_rate_control(const char *rc_str)
|
|||
return AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR;
|
||||
}
|
||||
|
||||
static inline int get_hevc_profile(obs_data_t *settings)
|
||||
{
|
||||
const char *profile = obs_data_get_string(settings, "profile");
|
||||
|
||||
if (astrcmpi(profile, "main") == 0)
|
||||
return AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN;
|
||||
else if (astrcmpi(profile, "main10") == 0)
|
||||
return AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10;
|
||||
|
||||
return AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN;
|
||||
}
|
||||
|
||||
static void amf_hevc_update_data(amf_base *enc, int rc, int64_t bitrate,
|
||||
int64_t qp)
|
||||
{
|
||||
|
@ -1684,8 +1817,20 @@ static void amf_hevc_update_data(amf_base *enc, int rc, int64_t bitrate,
|
|||
set_hevc_property(enc, PEAK_BITRATE, bitrate);
|
||||
set_hevc_property(enc, VBV_BUFFER_SIZE, bitrate);
|
||||
|
||||
if (rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR) {
|
||||
if (rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR ||
|
||||
rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) {
|
||||
set_hevc_property(enc, FILLER_DATA_ENABLE, true);
|
||||
set_hevc_property(enc, ENFORCE_HRD, true);
|
||||
} else if (
|
||||
rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR ||
|
||||
rc == AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR) {
|
||||
set_hevc_property(enc, PEAK_BITRATE, bitrate * 1.5);
|
||||
set_hevc_property(enc, VBV_BUFFER_SIZE, bitrate * 1.5);
|
||||
} else if (rc ==
|
||||
AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR) {
|
||||
int64_t framerate = enc->fps_num / enc->fps_den;
|
||||
set_hevc_property(enc, VBV_BUFFER_SIZE,
|
||||
(bitrate / framerate) * 1.1);
|
||||
}
|
||||
} else {
|
||||
set_hevc_property(enc, QP_I, qp);
|
||||
|
@ -1728,10 +1873,65 @@ try {
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline void check_recommended_hevc_defaults(amf_base *enc,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
const bool is10bit = enc->amf_format == AMF_SURFACE_P010;
|
||||
const int64_t framerate = enc->fps_num / enc->fps_den;
|
||||
if ((enc->cx * enc->cy > 1920 * 1088) || is10bit || (framerate > 60)) {
|
||||
// Recommended base defaults
|
||||
obs_data_set_default_int(settings, "bitrate", 2500);
|
||||
obs_data_set_default_int(settings, "cqp", 20);
|
||||
obs_data_set_default_string(settings, "preset", "quality");
|
||||
info("Original default settings were lowered according to resolution"
|
||||
" and framerate.");
|
||||
} else {
|
||||
/* Recommended high perceptual quality defaults
|
||||
* for up to 1080p60fps
|
||||
*/
|
||||
|
||||
bool pa_enabled = obs_data_get_bool(settings, "pre_analysis");
|
||||
bool pa_set_by_user =
|
||||
obs_data_has_user_value(settings, "pre_analysis");
|
||||
|
||||
if (!pa_set_by_user || (pa_set_by_user && pa_enabled)) {
|
||||
pa_enabled = true;
|
||||
uint64_t pa_lab_depth = 20;
|
||||
AMF_MEMORY_TYPE pa_engine_type = AMF_MEMORY_OPENCL;
|
||||
AMF_PA_TAQ_MODE_ENUM pa_taq_mode = AMF_PA_TAQ_MODE_2;
|
||||
obs_data_set_bool(settings, "pre_analysis", pa_enabled);
|
||||
|
||||
set_hevc_property(enc, PRE_ANALYSIS_ENABLE, pa_enabled);
|
||||
set_amf_property(enc, AMF_PA_LOOKAHEAD_BUFFER_DEPTH,
|
||||
pa_lab_depth);
|
||||
set_amf_property(enc, AMF_PA_TAQ_MODE, pa_taq_mode);
|
||||
set_amf_property(enc, AMF_PA_ENGINE_TYPE,
|
||||
pa_engine_type);
|
||||
|
||||
info("High perceptual quality defaults:\n"
|
||||
"\tHevcEnablePreAnalysis: %s\n"
|
||||
"\tPALookAheadBufferDepth: %d\n"
|
||||
"\tPATemporalAQMode: %d\n"
|
||||
"\tPAEngineType: %s\n",
|
||||
pa_enabled ? "true" : "false", pa_lab_depth,
|
||||
pa_taq_mode,
|
||||
convert_amf_memory_type_to_string(pa_engine_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool amf_hevc_init(void *data, obs_data_t *settings)
|
||||
{
|
||||
amf_base *enc = (amf_base *)data;
|
||||
|
||||
/* We originally set the recommended defaults for high perceptual quality in
|
||||
* the UI settings. Once the resolution and framerate are available here
|
||||
* during init, we check if the defaults need to be lowered to the base
|
||||
* defaults or the remaining recommended high perceptual quality settings can
|
||||
* be set.
|
||||
*/
|
||||
check_recommended_hevc_defaults(enc, settings);
|
||||
|
||||
int64_t bitrate = obs_data_get_int(settings, "bitrate");
|
||||
int64_t qp = obs_data_get_int(settings, "cqp");
|
||||
const char *preset = obs_data_get_string(settings, "preset");
|
||||
|
@ -1740,12 +1940,13 @@ static bool amf_hevc_init(void *data, obs_data_t *settings)
|
|||
int rc = get_hevc_rate_control(rc_str);
|
||||
|
||||
set_hevc_property(enc, RATE_CONTROL_METHOD, rc);
|
||||
if (rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP)
|
||||
if (rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP &&
|
||||
rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR &&
|
||||
rc != AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR)
|
||||
set_hevc_property(enc, ENABLE_VBAQ, true);
|
||||
|
||||
amf_hevc_update_data(enc, rc, bitrate * 1000, qp);
|
||||
|
||||
set_hevc_property(enc, ENFORCE_HRD, true);
|
||||
set_hevc_property(enc, HIGH_MOTION_QUALITY_BOOST_ENABLE, false);
|
||||
|
||||
int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
|
||||
|
@ -1769,15 +1970,15 @@ static bool amf_hevc_init(void *data, obs_data_t *settings)
|
|||
ffmpeg_opts = "(none)";
|
||||
|
||||
info("settings:\n"
|
||||
"\trate_control: %s\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tcqp: %d\n"
|
||||
"\tkeyint: %d\n"
|
||||
"\tpreset: %s\n"
|
||||
"\tprofile: %s\n"
|
||||
"\twidth: %d\n"
|
||||
"\theight: %d\n"
|
||||
"\tparams: %s",
|
||||
"\trate_control: %s\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tcqp: %d\n"
|
||||
"\tkeyint: %d\n"
|
||||
"\tpreset: %s\n"
|
||||
"\tprofile: %s\n"
|
||||
"\twidth: %d\n"
|
||||
"\theight: %d\n"
|
||||
"\toverriding params: %s",
|
||||
rc_str, bitrate, qp, gop_size, preset, profile, enc->cx, enc->cy,
|
||||
ffmpeg_opts);
|
||||
|
||||
|
@ -1835,6 +2036,7 @@ static void amf_hevc_create_internal(amf_base *enc, obs_data_t *settings)
|
|||
const bool hlg = is_hlg(enc);
|
||||
const bool is_hdr = pq || hlg;
|
||||
const char *preset = obs_data_get_string(settings, "preset");
|
||||
obs_data_set_string(settings, "profile", is10bit ? "main10" : "main");
|
||||
|
||||
set_hevc_property(enc, FRAMESIZE, AMFConstructSize(enc->cx, enc->cy));
|
||||
set_hevc_property(enc, USAGE, AMF_VIDEO_ENCODER_USAGE_TRANSCODING);
|
||||
|
@ -1842,9 +2044,7 @@ static void amf_hevc_create_internal(amf_base *enc, obs_data_t *settings)
|
|||
set_hevc_property(enc, COLOR_BIT_DEPTH,
|
||||
is10bit ? AMF_COLOR_BIT_DEPTH_10
|
||||
: AMF_COLOR_BIT_DEPTH_8);
|
||||
set_hevc_property(enc, PROFILE,
|
||||
is10bit ? AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN_10
|
||||
: AMF_VIDEO_ENCODER_HEVC_PROFILE_MAIN);
|
||||
set_hevc_property(enc, PROFILE, get_hevc_profile(settings));
|
||||
set_hevc_property(enc, LOWLATENCY_MODE, false);
|
||||
set_hevc_property(enc, OUTPUT_COLOR_PROFILE, enc->amf_color_profile);
|
||||
set_hevc_property(enc, OUTPUT_TRANSFER_CHARACTERISTIC,
|
||||
|
@ -1960,6 +2160,17 @@ try {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/* These are initial recommended settings that may be lowered later
|
||||
* once we know more info such as the resolution and frame rate.
|
||||
*/
|
||||
static void amf_hevc_defaults(obs_data_t *settings)
|
||||
{
|
||||
obs_data_set_default_string(settings, "rate_control", "CBR");
|
||||
obs_data_set_default_int(settings, "bitrate", 2500);
|
||||
obs_data_set_default_int(settings, "cqp", 20);
|
||||
obs_data_set_default_string(settings, "preset", "quality");
|
||||
}
|
||||
|
||||
static void register_hevc()
|
||||
{
|
||||
struct obs_encoder_info amf_encoder_info = {};
|
||||
|
@ -1971,7 +2182,7 @@ static void register_hevc()
|
|||
amf_encoder_info.destroy = amf_destroy;
|
||||
amf_encoder_info.update = amf_hevc_update;
|
||||
amf_encoder_info.encode_texture = amf_encode_tex;
|
||||
amf_encoder_info.get_defaults = amf_defaults;
|
||||
amf_encoder_info.get_defaults = amf_hevc_defaults;
|
||||
amf_encoder_info.get_properties = amf_hevc_properties;
|
||||
amf_encoder_info.get_extra_data = amf_extra_data;
|
||||
amf_encoder_info.caps = OBS_ENCODER_CAP_PASS_TEXTURE |
|
||||
|
@ -2055,12 +2266,20 @@ static void amf_av1_update_data(amf_base *enc, int rc, int64_t bitrate,
|
|||
set_av1_property(enc, PEAK_BITRATE, bitrate);
|
||||
set_av1_property(enc, VBV_BUFFER_SIZE, bitrate);
|
||||
|
||||
if (rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR) {
|
||||
if (rc == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR ||
|
||||
rc == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_CBR) {
|
||||
set_av1_property(enc, FILLER_DATA, true);
|
||||
set_av1_property(enc, ENFORCE_HRD, true);
|
||||
} else if (
|
||||
rc == AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR ||
|
||||
rc == AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_HIGH_QUALITY_VBR) {
|
||||
set_av1_property(enc, PEAK_BITRATE, bitrate * 1.5);
|
||||
set_av1_property(enc, VBV_BUFFER_SIZE, bitrate * 1.5);
|
||||
} else if (rc ==
|
||||
AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR) {
|
||||
int64_t framerate = enc->fps_num / enc->fps_den;
|
||||
set_av1_property(enc, VBV_BUFFER_SIZE,
|
||||
(bitrate / framerate) * 1.1);
|
||||
}
|
||||
} else {
|
||||
int64_t qp = cq_value * 4;
|
||||
|
@ -2104,10 +2323,63 @@ try {
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline void check_recommended_av1_defaults(amf_base *enc,
|
||||
obs_data_t *settings)
|
||||
{
|
||||
const bool is10bit = enc->amf_format == AMF_SURFACE_P010;
|
||||
const int64_t framerate = enc->fps_num / enc->fps_den;
|
||||
if ((enc->cx * enc->cy > 1920 * 1088) || is10bit || (framerate > 60)) {
|
||||
// Recommended base defaults
|
||||
obs_data_set_default_int(settings, "bitrate", 2500);
|
||||
obs_data_set_default_int(settings, "cqp", 20);
|
||||
obs_data_set_default_string(settings, "preset", "balanced");
|
||||
obs_data_set_default_string(settings, "profile", "main");
|
||||
info("Original default settings were lowered according to resolution"
|
||||
" and framerate.");
|
||||
} else {
|
||||
/* Recommended high perceptual quality defaults
|
||||
* for up to 1080p60fps
|
||||
*/
|
||||
|
||||
bool pa_enabled = obs_data_get_bool(settings, "pre_analysis");
|
||||
bool pa_set_by_user =
|
||||
obs_data_has_user_value(settings, "pre_analysis");
|
||||
|
||||
if (!pa_set_by_user || (pa_set_by_user && pa_enabled)) {
|
||||
pa_enabled = true;
|
||||
uint64_t pa_lab_depth = 20;
|
||||
AMF_MEMORY_TYPE pa_engine_type = AMF_MEMORY_OPENCL;
|
||||
AMF_PA_TAQ_MODE_ENUM pa_taq_mode = AMF_PA_TAQ_MODE_2;
|
||||
set_av1_property(enc, PRE_ANALYSIS_ENABLE, pa_enabled);
|
||||
set_amf_property(enc, AMF_PA_LOOKAHEAD_BUFFER_DEPTH,
|
||||
pa_lab_depth);
|
||||
set_amf_property(enc, AMF_PA_TAQ_MODE, pa_taq_mode);
|
||||
set_amf_property(enc, AMF_PA_ENGINE_TYPE,
|
||||
AMF_MEMORY_OPENCL);
|
||||
info("High perceptual quality defaults:\n"
|
||||
"\tAv1EnablePreAnalysis: %s\n"
|
||||
"\tPALookAheadBufferDepth: %d\n"
|
||||
"\tPATemporalAQMode: %d\n"
|
||||
"\tPAEngineType: %s\n",
|
||||
pa_enabled ? "true" : "false", pa_lab_depth,
|
||||
pa_taq_mode,
|
||||
convert_amf_memory_type_to_string(pa_engine_type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool amf_av1_init(void *data, obs_data_t *settings)
|
||||
{
|
||||
amf_base *enc = (amf_base *)data;
|
||||
|
||||
/* We originally set the recommended defaults for high perceptual quality in
|
||||
* the UI settings. Once the resolution and framerate are available here
|
||||
* during init, we check if the defaults need to be lowered to the base
|
||||
* defaults or the remaining recommended high perceptual quality settings can
|
||||
* be set.
|
||||
*/
|
||||
check_recommended_av1_defaults(enc, settings);
|
||||
|
||||
int64_t bitrate = obs_data_get_int(settings, "bitrate");
|
||||
int64_t qp = obs_data_get_int(settings, "cqp");
|
||||
const char *preset = obs_data_get_string(settings, "preset");
|
||||
|
@ -2119,8 +2391,6 @@ static bool amf_av1_init(void *data, obs_data_t *settings)
|
|||
|
||||
amf_av1_update_data(enc, rc, bitrate * 1000, qp);
|
||||
|
||||
set_av1_property(enc, ENFORCE_HRD, true);
|
||||
|
||||
int keyint_sec = (int)obs_data_get_int(settings, "keyint_sec");
|
||||
int gop_size = (keyint_sec) ? keyint_sec * enc->fps_num / enc->fps_den
|
||||
: 250;
|
||||
|
@ -2141,17 +2411,19 @@ static bool amf_av1_init(void *data, obs_data_t *settings)
|
|||
ffmpeg_opts = "(none)";
|
||||
|
||||
info("settings:\n"
|
||||
"\trate_control: %s\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tcqp: %d\n"
|
||||
"\tkeyint: %d\n"
|
||||
"\tpreset: %s\n"
|
||||
"\tprofile: %s\n"
|
||||
"\twidth: %d\n"
|
||||
"\theight: %d\n"
|
||||
"\tparams: %s",
|
||||
"\trate_control: %s\n"
|
||||
"\tbitrate: %d\n"
|
||||
"\tcqp: %d\n"
|
||||
"\tkeyint: %d\n"
|
||||
"\tpreset: %s\n"
|
||||
"\tprofile: %s\n"
|
||||
"\twidth: %d\n"
|
||||
"\theight: %d\n"
|
||||
"\tscreen content tools: %s\n"
|
||||
"\tpalette mode: %s\n"
|
||||
"\toverriding params: %s",
|
||||
rc_str, bitrate, qp, gop_size, preset, profile, enc->cx, enc->cy,
|
||||
ffmpeg_opts);
|
||||
"true", "true", ffmpeg_opts);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2177,6 +2449,7 @@ static void amf_av1_create_internal(amf_base *enc, obs_data_t *settings)
|
|||
|
||||
const bool is10bit = enc->amf_format == AMF_SURFACE_P010;
|
||||
const char *preset = obs_data_get_string(settings, "preset");
|
||||
obs_data_set_string(settings, "profile", "main");
|
||||
|
||||
set_av1_property(enc, FRAMESIZE, AMFConstructSize(enc->cx, enc->cy));
|
||||
set_av1_property(enc, USAGE, AMF_VIDEO_ENCODER_USAGE_TRANSCODING);
|
||||
|
@ -2194,7 +2467,8 @@ static void amf_av1_create_internal(amf_base *enc, obs_data_t *settings)
|
|||
set_av1_property(enc, OUTPUT_TRANSFER_CHARACTERISTIC,
|
||||
enc->amf_characteristic);
|
||||
set_av1_property(enc, OUTPUT_COLOR_PRIMARIES, enc->amf_primaries);
|
||||
set_av1_property(enc, FRAMERATE, enc->amf_frame_rate);
|
||||
set_av1_property(enc, SCREEN_CONTENT_TOOLS, true);
|
||||
set_av1_property(enc, PALETTE_MODE, true);
|
||||
|
||||
amf_av1_init(enc, settings);
|
||||
|
||||
|
@ -2280,13 +2554,15 @@ try {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/* These are initial recommended settings that may be lowered later
|
||||
* once we know more info such as the resolution and frame rate.
|
||||
*/
|
||||
static void amf_av1_defaults(obs_data_t *settings)
|
||||
{
|
||||
obs_data_set_default_int(settings, "bitrate", 2500);
|
||||
obs_data_set_default_int(settings, "cqp", 20);
|
||||
obs_data_set_default_string(settings, "rate_control", "CBR");
|
||||
obs_data_set_default_string(settings, "preset", "quality");
|
||||
obs_data_set_default_string(settings, "profile", "high");
|
||||
obs_data_set_default_string(settings, "preset", "highQuality");
|
||||
}
|
||||
|
||||
static void register_av1()
|
||||
|
|
|
@ -1718,6 +1718,10 @@ static size_t mp4_write_trak(struct mp4_mux *mux, struct mp4_track *track,
|
|||
struct serializer *s = mux->serializer;
|
||||
int64_t start = serializer_get_pos(s);
|
||||
|
||||
/* If track has no data, omit it from full moov. */
|
||||
if (!fragmented && !track->chunks.num)
|
||||
return 0;
|
||||
|
||||
write_box(s, 0, "trak");
|
||||
|
||||
// tkhd
|
||||
|
@ -2409,7 +2413,7 @@ static void write_packets(struct mp4_mux *mux, struct mp4_track *track)
|
|||
struct serializer *s = mux->serializer;
|
||||
|
||||
size_t count = track->packets.size / sizeof(struct encoder_packet);
|
||||
if (!count)
|
||||
if (!count || !track->fragment_samples.num)
|
||||
return;
|
||||
|
||||
struct chunk *chk = da_push_back_new(track->chunks);
|
||||
|
|
|
@ -112,20 +112,23 @@ static inline void ts_offset_update(struct mp4_output *out,
|
|||
struct encoder_packet *packet)
|
||||
{
|
||||
int64_t *offset;
|
||||
int64_t ts;
|
||||
bool *found;
|
||||
|
||||
if (packet->type == OBS_ENCODER_VIDEO) {
|
||||
offset = &out->video_pts_offsets[packet->track_idx];
|
||||
found = &out->found_video[packet->track_idx];
|
||||
ts = packet->pts;
|
||||
} else {
|
||||
offset = &out->audio_dts_offsets[packet->track_idx];
|
||||
found = &out->found_audio[packet->track_idx];
|
||||
ts = packet->dts;
|
||||
}
|
||||
|
||||
if (*found)
|
||||
return;
|
||||
|
||||
*offset = packet->dts;
|
||||
*offset = ts;
|
||||
*found = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -309,7 +309,9 @@ static inline void set_param(struct obs_x264 *obsx264, struct obs_option option)
|
|||
if (strcmp(name, "preset") != 0 && strcmp(name, "profile") != 0 &&
|
||||
strcmp(name, "tune") != 0 && strcmp(name, "fps") != 0 &&
|
||||
strcmp(name, "force-cfr") != 0 && strcmp(name, "width") != 0 &&
|
||||
strcmp(name, "height") != 0 && strcmp(name, "opencl") != 0) {
|
||||
strcmp(name, "height") != 0 && strcmp(name, "opencl") != 0 &&
|
||||
strcmp(name, "stats") != 0 && strcmp(name, "qpfile") != 0 &&
|
||||
strcmp(name, "pass") != 0) {
|
||||
if (strcmp(option.name, OPENCL_ALIAS) == 0)
|
||||
name = "opencl";
|
||||
if (x264_param_parse(&obsx264->params, name, val) != 0)
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
#endif
|
||||
|
||||
enum AVHWDeviceType hw_priority[] = {
|
||||
AV_HWDEVICE_TYPE_CUDA, AV_HWDEVICE_TYPE_D3D11VA, AV_HWDEVICE_TYPE_DXVA2,
|
||||
AV_HWDEVICE_TYPE_QSV, AV_HWDEVICE_TYPE_NONE,
|
||||
AV_HWDEVICE_TYPE_D3D11VA,
|
||||
AV_HWDEVICE_TYPE_DXVA2,
|
||||
AV_HWDEVICE_TYPE_QSV,
|
||||
AV_HWDEVICE_TYPE_NONE,
|
||||
};
|
||||
|
||||
static bool has_hw_type(const AVCodec *c, enum AVHWDeviceType type)
|
||||
|
|
Loading…
Reference in a new issue