Compare commits

...

18 commits

Author SHA1 Message Date
Sebastian Beckmann 3cbc0094d5
Merge 5a9f9af9ad into 9d67bf2662 2024-06-27 11:04:08 +02:00
Ryan Foster 9d67bf2662 Revert "plugins/win-dshow: Add CUDA decoder"
This reverts commit ce4c99be4e.

This was causing infinitely looping log errors in systems with no
CUDA-capable hardware when hardware decoding was enabled on video
capture devices with custom config enabled.
2024-06-26 18:43:09 -04:00
tt2468 8892edde05 libobs: Merge obs_encoder_stop() and ..._stop_internal()
There is no longer any need for them to be separate functions. This is
just code cleanup.
2024-06-26 16:42:05 -04:00
tt2468 06291c7201 libobs: Fix race when to-be-destroyed encoder group finishes stopping
Fixes a crash when the following steps occur:
- Encoder group created and then started with encoders, only the group
owning encoder refs
- Encoder group destroy called: group has `destroy_on_stop` set
without actual destroy
- Outputs holding encoders from stopping are stopped
- `remove_connection()` function destroys encoder group, releasing
encoders and causing the currently processed encoder to be destroyed
early
- parent scopes try to access destroyed encoder pointer and crash

This change moves some logic around to improve the release/destruct
order of `obs_encoder_stop()` to fix the race above.

Note: Cases of encoder errors will not destruct the group if it has
`destroy_on_stop` set, as the encoder is also not destroyed if it also
is set to destroy on stop. This part of the code should be revisited
at a later date and fixed up to prevent memory leaks.
2024-06-26 16:42:05 -04:00
tt2468 fb5bbc8575 libobs: Set encoder initialized call closer to shutdown
This is mainly code cleanup.
2024-06-26 16:42:05 -04:00
Ruwen Hahn 7d19add10b UI: Display dialog for multitrack video output audio channels mismatch 2024-06-26 16:10:15 -04:00
Vainock 4830d6903e UI: Fix capitalization of 'OBS' and 'RTMP' 2024-06-26 15:01:01 -04:00
Warchamp7 47fb194223 UI: Adjust Yami (Classic) styling 2024-06-26 14:58:13 -04:00
derrod edcda5a825 obs-x264: Ignore stats/qp file and multipass options 2024-06-26 13:58:08 -04:00
derrod 2e6e79b4f5 obs-outputs: Skip trak box if track has no data 2024-06-26 13:57:59 -04:00
derrod b34fbb116e obs-ffmpeg: Check if current NVENC configuration supports 4:4:4 encode 2024-06-26 13:57:43 -04:00
Penwywern 4517918c7a cmake: Fix FFmpeg version regex 2024-06-26 12:26:56 -04:00
derrod 6cc0e2b803 obs-outputs: Fix file splitting ts offset using video DTS instead of PTS 2024-06-25 23:36:33 -04:00
derrod ed2478535f obs-outputs: Do not create MP4 track chunks without samples 2024-06-25 23:36:33 -04:00
Ed Maste fd34bab615 UI: Link Qt::DBus on FreeBSD
As with Linux we need to link Qt::DBus on FreeBSD now that there's a
HighContrastEnabled implementation that makes use of it.

Fixes: 41ba8bdfdd ("UI: Add HighContrastEnabled implementation fo...")
2024-06-25 22:37:14 -04:00
gxalpha 5a9f9af9ad
UI: Unify filters clipboard of main window and filters menu
Previously, the filters window and the main window had separate
clipboards where copied filters would live. With previous changes to how
filters are copied in the main window, we can now easily unify the two.
2024-06-07 22:43:01 +02:00
gxalpha f402f7a091
UI: Actually copy-paste filters instead of remembering the source
The current behavior makes OBS remember the source that filters should
get copied from and then clones that source's filters to the source that
filters are pasted to. This means however that if the original source
has filters added or removed after the copy-operation, that will still
effect the paste-operation, so filters that were added after copying
would still get pasted.
This commit changes this to something similar to the source clipboard.
Now, we're actually copying the filters, so if the original source has
filters added to afterwards we don't copy those as well.
2024-06-07 22:12:27 +02:00
gxalpha 6ea1520318
UI: Move filter copying into its own function
Preparation for a larger change that reworks filter copying
2024-06-07 22:12:26 +02:00
17 changed files with 350 additions and 119 deletions

View file

@ -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)

View file

@ -318,7 +318,7 @@ Undo.Item.Redo="Redo %1"
Undo.Sources.Multi="Delete %1 Sources"
Undo.Filters="Filter Changes on '%1'"
Undo.Filters.Paste.Single="Paste Filter '%1' to '%2'"
Undo.Filters.Paste.Multiple="Copy Filters from '%1' to '%2'"
Undo.Filters.Paste.Multiple="Paste %1 Filters to '%2'"
Undo.Transform="Transform source(s) In '%1'"
Undo.Transform.Paste="Paste Transformation in '%1'"
Undo.Transform.Rotate="Rotation In '%1'"
@ -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]"

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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>

View file

@ -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 &current_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)};
}

View file

@ -945,7 +945,7 @@ void OBSBasicFilters::CustomContextMenu(const QPoint &pos, bool async)
}
QAction *pasteAction = new QAction(QTStr("Paste"));
pasteAction->setEnabled(main->copyFilter);
pasteAction->setEnabled(!main->filtersClipboard.empty());
connect(pasteAction, &QAction::triggered, this,
&OBSBasicFilters::PasteFilter);
pasteAction->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_V));
@ -1165,26 +1165,13 @@ void OBSBasicFilters::CopyFilter()
else
filter = GetFilter(ui->effectFilters->currentRow(), false);
main->copyFilter = OBSGetWeakRef(filter);
main->filtersClipboard.clear();
main->filtersClipboard.push_back(OBSGetWeakRef(filter));
}
void OBSBasicFilters::PasteFilter()
{
OBSSource filter = OBSGetStrongRef(main->copyFilter);
if (!filter)
return;
OBSDataArrayAutoRelease undo_array = obs_source_backup_filters(source);
obs_source_copy_single_filter(source, filter);
OBSDataArrayAutoRelease redo_array = obs_source_backup_filters(source);
const char *filterName = obs_source_get_name(filter);
const char *sourceName = obs_source_get_name(source);
QString text =
QTStr("Undo.Filters.Paste.Single").arg(filterName, sourceName);
main->CreateFilterPasteUndoRedoAction(text, source, undo_array,
redo_array);
main->SourcePasteFilters(source);
}
void OBSBasicFilters::delete_filter(OBSSource filter)

View file

@ -3926,8 +3926,7 @@ void OBSBasic::VolControlContextMenu()
copyFiltersAction.setEnabled(obs_source_filter_count(vol->GetSource()) >
0);
pasteFiltersAction.setEnabled(
!obs_weak_source_expired(copyFiltersSource));
pasteFiltersAction.setEnabled(!filtersClipboard.empty());
QMenu popup;
vol->SetContextMenu(&popup);
@ -5096,8 +5095,7 @@ void OBSBasic::ClearSceneData()
prevFTBSource = nullptr;
clipboard.clear();
copyFiltersSource = nullptr;
copyFilter = nullptr;
filtersClipboard.clear();
auto cb = [](void *, obs_source_t *source) {
obs_source_remove(source);
@ -5615,8 +5613,7 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
&OBSBasic::SceneCopyFilters);
QAction *pasteFilters =
new QAction(QTStr("Paste.Filters"), this);
pasteFilters->setEnabled(
!obs_weak_source_expired(copyFiltersSource));
pasteFilters->setEnabled(!filtersClipboard.empty());
connect(pasteFilters, &QAction::triggered, this,
&OBSBasic::ScenePasteFilters);
@ -8634,8 +8631,8 @@ void OBSBasic::UpdateEditMenu()
ui->actionPasteTransform->setEnabled(
canTransformMultiple && hasCopiedTransform && videoCount > 0);
ui->actionCopyFilters->setEnabled(filter_count > 0);
ui->actionPasteFilters->setEnabled(
!obs_weak_source_expired(copyFiltersSource) && totalCount > 0);
ui->actionPasteFilters->setEnabled(!filtersClipboard.empty() &&
totalCount > 0);
ui->actionPasteRef->setEnabled(!!clipboard.size());
ui->actionPasteDup->setEnabled(allowPastingDuplicate);
@ -10110,21 +10107,61 @@ void OBSBasic::on_actionPasteDup_triggered()
redo_data);
}
void OBSBasic::SourcePasteFilters(OBSSource source, OBSSource dstSource)
void OBSBasic::SourceCopyFilters(OBSSource source)
{
if (source == dstSource)
return;
filtersClipboard.clear();
filtersClipboard.reserve(obs_source_filter_count(source));
obs_source_enum_filters(
source,
[](obs_source_t *, obs_source_t *filter, void *param) {
auto filters =
static_cast<std::vector<OBSWeakSource> *>(
param);
filters->push_back(OBSGetWeakRef(filter));
},
&filtersClipboard);
ui->actionPasteFilters->setEnabled(true);
}
void OBSBasic::SourcePasteFilters(OBSSource dstSource)
{
OBSDataArrayAutoRelease undo_array =
obs_source_backup_filters(dstSource);
obs_source_copy_filters(dstSource, source);
int copiedFiltersCount = 0;
OBSSource copiedFilter = nullptr;
for (auto &weakFilter : filtersClipboard) {
if (obs_weak_source_expired(weakFilter)) {
continue;
}
OBSSource filter = OBSGetStrongRef(weakFilter);
obs_source_copy_single_filter(dstSource, filter);
if (copiedFiltersCount == 0) {
copiedFilter = filter;
}
copiedFiltersCount++;
}
if (copiedFiltersCount == 0) {
return;
}
OBSDataArrayAutoRelease redo_array =
obs_source_backup_filters(dstSource);
const char *srcName = obs_source_get_name(source);
const char *dstName = obs_source_get_name(dstSource);
QString text =
QTStr("Undo.Filters.Paste.Multiple").arg(srcName, dstName);
QString text;
if (copiedFiltersCount == 1) {
const char *filterName = obs_source_get_name(copiedFilter);
text = QTStr("Undo.Filters.Paste.Single")
.arg(filterName, dstName);
} else {
text = QTStr("Undo.Filters.Paste.Multiple")
.arg(QString::number(copiedFiltersCount),
dstName);
}
CreateFilterPasteUndoRedoAction(text, dstSource, undo_array,
redo_array);
@ -10136,36 +10173,25 @@ void OBSBasic::AudioMixerCopyFilters()
VolControl *vol = action->property("volControl").value<VolControl *>();
obs_source_t *source = vol->GetSource();
copyFiltersSource = obs_source_get_weak_source(source);
ui->actionPasteFilters->setEnabled(true);
SourceCopyFilters(source);
}
void OBSBasic::AudioMixerPasteFilters()
{
QAction *action = reinterpret_cast<QAction *>(sender());
VolControl *vol = action->property("volControl").value<VolControl *>();
obs_source_t *dstSource = vol->GetSource();
OBSSourceAutoRelease source =
obs_weak_source_get_source(copyFiltersSource);
SourcePasteFilters(source.Get(), dstSource);
OBSSource dstSource = vol->GetSource();
SourcePasteFilters(dstSource);
}
void OBSBasic::SceneCopyFilters()
{
copyFiltersSource = obs_source_get_weak_source(GetCurrentSceneSource());
ui->actionPasteFilters->setEnabled(true);
SourceCopyFilters(GetCurrentSceneSource());
}
void OBSBasic::ScenePasteFilters()
{
OBSSourceAutoRelease source =
obs_weak_source_get_source(copyFiltersSource);
OBSSource dstSource = GetCurrentSceneSource();
SourcePasteFilters(source.Get(), dstSource);
SourcePasteFilters(GetCurrentSceneSource());
}
void OBSBasic::on_actionCopyFilters_triggered()
@ -10177,9 +10203,7 @@ void OBSBasic::on_actionCopyFilters_triggered()
OBSSource source = obs_sceneitem_get_source(item);
copyFiltersSource = obs_source_get_weak_source(source);
ui->actionPasteFilters->setEnabled(true);
SourceCopyFilters(source);
}
void OBSBasic::CreateFilterPasteUndoRedoAction(const QString &text,
@ -10217,13 +10241,9 @@ void OBSBasic::CreateFilterPasteUndoRedoAction(const QString &text,
void OBSBasic::on_actionPasteFilters_triggered()
{
OBSSourceAutoRelease source =
obs_weak_source_get_source(copyFiltersSource);
OBSSceneItem sceneItem = GetCurrentSceneItem();
OBSSource dstSource = obs_sceneitem_get_source(sceneItem);
SourcePasteFilters(source.Get(), dstSource);
SourcePasteFilters(dstSource);
}
static void ConfirmColor(SourceTree *sources, const QColor &color,

View file

@ -245,7 +245,6 @@ private:
ContextBarSize contextBarSize = ContextBarSize_Normal;
std::deque<SourceCopyInfo> clipboard;
OBSWeakSourceAutoRelease copyFiltersSource;
bool copyVisible = true;
obs_transform_info copiedTransformInfo;
obs_sceneitem_crop copiedCropInfo;
@ -807,7 +806,6 @@ private slots:
void on_actionPasteFilters_triggered();
void AudioMixerCopyFilters();
void AudioMixerPasteFilters();
void SourcePasteFilters(OBSSource source, OBSSource dstSource);
void ColorChange();
@ -1015,7 +1013,9 @@ public:
QIcon GetGroupIcon() const;
QIcon GetSceneIcon() const;
OBSWeakSource copyFilter;
std::vector<OBSWeakSource> filtersClipboard;
void SourceCopyFilters(OBSSource source);
void SourcePasteFilters(OBSSource dstSource);
void ShowStatusBarMessage(const QString &message);

View file

@ -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()

View file

@ -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;
}
}

View file

@ -40,6 +40,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)"

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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)

View file

@ -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)