提交 8b2614dd 编写于 作者: J jp9000

UI: Add advanced output settings

Adds an 'advanced' mode to the output settings to allow more powerful
and complex streaming and recording options:

- Optionally use a different encoder for recording than for streaming to
  allow the recording to use a different encoder or encoder settings if
  desired (though at the cost if increased CPU usage depending on the
  encoders being used)

- Use encoders other than x264

- Rescale the recording or streaming encoders in case the user wishes to
  stream and record at different resolutions

- Select the specific mixer to use for recording and for streaming,
  allowing the stream and recording to use separate mixers (to for
  example allow a user to stream the game/mic audio but only record the
  game audio)

- Use FFmpeg output for the recording button instead of only recording
  h264/aac to FLV, allowing the user to output to various different
  types of file formats or remote URLs, as well as allowing the user to
  select and use different encoders and encoder settings that are
  available in the FFmpeg library

- Optionally allow the use of multiple audio tracks in a single output
  if the file formats or stream services support it
上级 a2373b12
...@@ -19,6 +19,7 @@ Properties="Properties" ...@@ -19,6 +19,7 @@ Properties="Properties"
MoveUp="Move Up" MoveUp="Move Up"
MoveDown="Move Down" MoveDown="Move Down"
Settings="Settings" Settings="Settings"
Name="Name"
Exit="Exit" Exit="Exit"
Mixer="Mixer" Mixer="Mixer"
Browse="Browse" Browse="Browse"
...@@ -216,9 +217,13 @@ Basic.Settings.Stream.StreamType="Stream Type" ...@@ -216,9 +217,13 @@ Basic.Settings.Stream.StreamType="Stream Type"
# basic mode 'output' settings # basic mode 'output' settings
Basic.Settings.Output="Output" Basic.Settings.Output="Output"
Basic.Settings.Output.Encoder="Encoder"
Basic.Settings.Output.SelectDirectory="Select Recording Directory"
Basic.Settings.Output.SelectFile="Select Recording File"
Basic.Settings.Output.Mode="Output Mode" Basic.Settings.Output.Mode="Output Mode"
Basic.Settings.Output.Mode.Simple="Simple (Stream and/or record)" Basic.Settings.Output.Mode.Simple="Simple (Stream and/or record)"
Basic.Settings.Output.Mode.Advanced="Advanced (Custom output type)" Basic.Settings.Output.Mode.Adv="Advanced"
Basic.Settings.Output.Mode.FFmpeg="FFmpeg Output"
Basic.Settings.Output.Simple.SavePath="FLV Recording Path" Basic.Settings.Output.Simple.SavePath="FLV Recording Path"
Basic.Settings.Output.VideoBitrate="Video Bitrate" Basic.Settings.Output.VideoBitrate="Video Bitrate"
Basic.Settings.Output.AudioBitrate="Audio Bitrate" Basic.Settings.Output.AudioBitrate="Audio Bitrate"
...@@ -229,6 +234,29 @@ Basic.Settings.Output.Advanced="Enable Advanced Encoder Settings" ...@@ -229,6 +234,29 @@ Basic.Settings.Output.Advanced="Enable Advanced Encoder Settings"
Basic.Settings.Output.EncoderPreset="Encoder Preset (higher = less CPU)" Basic.Settings.Output.EncoderPreset="Encoder Preset (higher = less CPU)"
Basic.Settings.Output.CustomEncoderSettings="Custom Encoder Settings" Basic.Settings.Output.CustomEncoderSettings="Custom Encoder Settings"
Basic.Settings.Output.UseCBR="Use Constant Bitrate" Basic.Settings.Output.UseCBR="Use Constant Bitrate"
Basic.Settings.Output.UseBufferSize="Use Custom Buffer Size"
# basic mode 'output' settings - advanced section
Basic.Settings.Output.Adv.Rescale="Rescale Output"
Basic.Settings.Output.Adv.AudioTrack="Audio Track"
Basic.Settings.Output.Adv.Streaming="Streaming"
Basic.Settings.Output.Adv.Audio.Track1="Track 1"
Basic.Settings.Output.Adv.Audio.Track2="Track 2"
Basic.Settings.Output.Adv.Audio.Track3="Track 3"
Basic.Settings.Output.Adv.Audio.Track4="Track 4"
# basic mode 'output' settings - advanced section - recording subsection
Basic.Settings.Output.Adv.Recording="Recording"
Basic.Settings.Output.Adv.Recording.Type="Type"
Basic.Settings.Output.Adv.Recording.Type.Standard="Standard"
Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput="Custom Output (FFmpeg)"
Basic.Settings.Output.Adv.Recording.UseStreamEncoder="(Use stream encoder)"
Basic.Settings.Output.Adv.FFmpeg.SaveFilter="Common recording formats (*.avi *.mp4 *.flv *.ts *.mkv *.wav *.aac);;All Files (*.*)"
Basic.Settings.Output.Adv.FFmpeg.SavePathURL="File path or URL"
Basic.Settings.Output.Adv.FFmpeg.VEncoder="Video Encoder (blank=default)"
Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings="Video Encoder Settings (if any)"
Basic.Settings.Output.Adv.FFmpeg.AEncoder="Audio Encoder (blank=default)"
Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings="Audio Encoder Settings (if any)"
# basic mode 'video' settings # basic mode 'video' settings
Basic.Settings.Video="Video" Basic.Settings.Video="Video"
......
...@@ -115,9 +115,6 @@ ...@@ -115,9 +115,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="1" column="1">
<widget class="QComboBox" name="language"/>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="minimumSize"> <property name="minimumSize">
...@@ -134,6 +131,9 @@ ...@@ -134,6 +131,9 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1">
<widget class="QComboBox" name="language"/>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="streamPage"> <widget class="QWidget" name="streamPage">
...@@ -280,7 +280,7 @@ ...@@ -280,7 +280,7 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="outputMode"> <widget class="QComboBox" name="outputMode">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>true</bool>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
...@@ -298,7 +298,7 @@ ...@@ -298,7 +298,7 @@
</item> </item>
<item> <item>
<property name="text"> <property name="text">
<string>Basic.Settings.Output.Mode.Custom</string> <string>Basic.Settings.Output.Mode.Adv</string>
</property> </property>
</item> </item>
</widget> </widget>
...@@ -638,7 +638,1167 @@ ...@@ -638,7 +638,1167 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="customOutputsPage"/> <widget class="QWidget" name="advOutputsPage">
<layout class="QVBoxLayout" name="verticalLayout_8">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QTabWidget" name="advOutTabs">
<property name="currentIndex">
<number>0</number>
</property>
<property name="usesScrollButtons">
<bool>true</bool>
</property>
<widget class="QWidget" name="advOutputStreamTab">
<attribute name="title">
<string>Basic.Settings.Output.Adv.Streaming</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_12">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget_4" native="true">
<layout class="QVBoxLayout" name="verticalLayout_14">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="advOutTopContainer" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QFormLayout" name="formLayout_7">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="1">
<widget class="QCheckBox" name="advOutReconnect">
<property name="text">
<string>Basic.Settings.Output.Reconnect</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_27">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.RetryDelay</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="advOutRetryDelay">
<property name="maximum">
<number>30</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_26">
<property name="text">
<string>Basic.Settings.Output.MaxRetries</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="advOutMaxRetries">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="advOutEncLabel">
<property name="text">
<string>Basic.Settings.Output.Encoder</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="advOutEncoder"/>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="advOutUseRescale">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Basic.Settings.Output.Adv.Rescale</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="advOutRescale">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_28">
<property name="text">
<string>Basic.Settings.Output.Adv.AudioTrack</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QWidget" name="widget_8" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="advOutTrack1">
<property name="text">
<string notr="true">1</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutTrack2">
<property name="text">
<string notr="true">2</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutTrack3">
<property name="text">
<string notr="true">3</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutTrack4">
<property name="text">
<string notr="true">4</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="advOutputRecordTab">
<attribute name="title">
<string>Basic.Settings.Output.Adv.Recording</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_11">
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QWidget" name="advOutRecTypeContainer" native="true">
<layout class="QFormLayout" name="formLayout_9">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_31">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.Adv.Recording.Type</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="advOutRecType">
<item>
<property name="text">
<string>Basic.Settings.Output.Adv.Recording.Type.Standard</string>
</property>
</item>
<item>
<property name="text">
<string>Basic.Settings.Output.Adv.Recording.Type.FFmpegOutput</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="advOutRecStandard">
<layout class="QVBoxLayout" name="verticalLayout_13">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget_7" native="true">
<layout class="QVBoxLayout" name="verticalLayout_15">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="advOutRecTopContainer" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QFormLayout" name="formLayout_16">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_32">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.Simple.SavePath</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLineEdit" name="advOutRecPath">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="advOutRecPathBrowse">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="advOutRecEncLabel">
<property name="text">
<string>Basic.Settings.Output.Encoder</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="advOutRecEncoder"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="advOutRecUseRescale">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Basic.Settings.Output.Adv.Rescale</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QWidget" name="advOutRecRescaleContainer" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QComboBox" name="advOutRecRescale">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_29">
<property name="text">
<string>Basic.Settings.Output.Adv.AudioTrack</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QWidget" name="widget_9" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="advOutRecTrack1">
<property name="text">
<string notr="true">1</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutRecTrack2">
<property name="text">
<string notr="true">2</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutRecTrack3">
<property name="text">
<string notr="true">3</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutRecTrack4">
<property name="text">
<string notr="true">4</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="advOutRecFFmpeg">
<layout class="QFormLayout" name="formLayout_15">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_36">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.Adv.FFmpeg.SavePathURL</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLineEdit" name="advOutFFURL">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="advOutFFPathBrowse">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Browse</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_40">
<property name="text">
<string>Basic.Settings.Output.VideoBitrate</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="advOutFFVBitrate">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000000000</number>
</property>
<property name="value">
<number>2500</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_37">
<property name="text">
<string>Basic.Settings.Output.Adv.FFmpeg.VEncoder</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="advOutFFVEncoder"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_38">
<property name="text">
<string>Basic.Settings.Output.Adv.FFmpeg.VEncoderSettings</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="advOutFFVCfg"/>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_41">
<property name="text">
<string>Basic.Settings.Output.AudioBitrate</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="advOutFFABitrate">
<property name="minimum">
<number>32</number>
</property>
<property name="maximum">
<number>4096</number>
</property>
<property name="singleStep">
<number>16</number>
</property>
<property name="value">
<number>128</number>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_47">
<property name="text">
<string>Basic.Settings.Output.Adv.AudioTrack</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_39">
<property name="text">
<string>Basic.Settings.Output.Adv.FFmpeg.AEncoder</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="advOutFFAEncoder"/>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_46">
<property name="text">
<string>Basic.Settings.Output.Adv.FFmpeg.AEncoderSettings</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QLineEdit" name="advOutFFACfg"/>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="advOutFFUseRescale">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::RightToLeft</enum>
</property>
<property name="text">
<string>Basic.Settings.Output.Adv.Rescale</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="advOutFFRescale">
<property name="enabled">
<bool>false</bool>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QWidget" name="widget_10" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="advOutFFTrack1">
<property name="text">
<string notr="true">1</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutFFTrack2">
<property name="text">
<string notr="true">2</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutFFTrack3">
<property name="text">
<string notr="true">3</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="advOutFFTrack4">
<property name="text">
<string notr="true">4</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="advOutputAudioTracksTab">
<attribute name="title">
<string>Basic.Settings.Audio</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_9">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item alignment="Qt::AlignTop">
<widget class="QWidget" name="widget_3" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Basic.Settings.Output.Adv.Audio.Track1</string>
</property>
<layout class="QFormLayout" name="formLayout_10">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="1">
<widget class="QComboBox" name="advOutTrack1Bitrate">
<property name="currentIndex">
<number>4</number>
</property>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>96</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>160</string>
</property>
</item>
<item>
<property name="text">
<string>192</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>320</string>
</property>
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_25">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.AudioBitrate</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_55">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="advOutTrack1Name"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Basic.Settings.Output.Adv.Audio.Track2</string>
</property>
<layout class="QFormLayout" name="formLayout_11">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_49">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.AudioBitrate</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="advOutTrack2Bitrate">
<property name="currentIndex">
<number>4</number>
</property>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>96</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>160</string>
</property>
</item>
<item>
<property name="text">
<string>192</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>320</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_50">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="advOutTrack2Name"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Basic.Settings.Output.Adv.Audio.Track3</string>
</property>
<layout class="QFormLayout" name="formLayout_12">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_51">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.AudioBitrate</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="advOutTrack3Bitrate">
<property name="currentIndex">
<number>4</number>
</property>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>96</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>160</string>
</property>
</item>
<item>
<property name="text">
<string>192</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>320</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_52">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="advOutTrack3Name"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Basic.Settings.Output.Adv.Audio.Track4</string>
</property>
<layout class="QFormLayout" name="formLayout_13">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_53">
<property name="minimumSize">
<size>
<width>170</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Basic.Settings.Output.AudioBitrate</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="advOutTrack4Bitrate">
<property name="currentIndex">
<number>4</number>
</property>
<item>
<property name="text">
<string>32</string>
</property>
</item>
<item>
<property name="text">
<string>64</string>
</property>
</item>
<item>
<property name="text">
<string>96</string>
</property>
</item>
<item>
<property name="text">
<string>128</string>
</property>
</item>
<item>
<property name="text">
<string>160</string>
</property>
</item>
<item>
<property name="text">
<string>192</string>
</property>
</item>
<item>
<property name="text">
<string>256</string>
</property>
</item>
<item>
<property name="text">
<string>320</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_54">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="advOutTrack4Name"/>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
...@@ -1162,12 +2322,12 @@ ...@@ -1162,12 +2322,12 @@
<slot>setCurrentIndex(int)</slot> <slot>setCurrentIndex(int)</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">
<x>252</x> <x>159</x>
<y>29</y> <y>34</y>
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>250</x> <x>241</x>
<y>39</y> <y>34</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
...@@ -1263,7 +2423,7 @@ ...@@ -1263,7 +2423,7 @@
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>750</x> <x>750</x>
<y>321</y> <y>347</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
...@@ -1279,7 +2439,7 @@ ...@@ -1279,7 +2439,7 @@
</hint> </hint>
<hint type="destinationlabel"> <hint type="destinationlabel">
<x>367</x> <x>367</x>
<y>321</y> <y>347</y>
</hint> </hint>
</hints> </hints>
</connection> </connection>
...@@ -1347,5 +2507,133 @@ ...@@ -1347,5 +2507,133 @@
</hint> </hint>
</hints> </hints>
</connection> </connection>
<connection>
<sender>advOutRecType</sender>
<signal>currentIndexChanged(int)</signal>
<receiver>stackedWidget</receiver>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>737</x>
<y>113</y>
</hint>
<hint type="destinationlabel">
<x>528</x>
<y>426</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutFFUseRescale</sender>
<signal>toggled(bool)</signal>
<receiver>advOutFFRescale</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>238</x>
<y>186</y>
</hint>
<hint type="destinationlabel">
<x>495</x>
<y>186</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutReconnect</sender>
<signal>toggled(bool)</signal>
<receiver>label_27</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>467</x>
<y>103</y>
</hint>
<hint type="destinationlabel">
<x>266</x>
<y>123</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutReconnect</sender>
<signal>toggled(bool)</signal>
<receiver>advOutRetryDelay</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>505</x>
<y>104</y>
</hint>
<hint type="destinationlabel">
<x>473</x>
<y>134</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutReconnect</sender>
<signal>toggled(bool)</signal>
<receiver>label_26</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>536</x>
<y>105</y>
</hint>
<hint type="destinationlabel">
<x>288</x>
<y>153</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutReconnect</sender>
<signal>toggled(bool)</signal>
<receiver>advOutMaxRetries</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>544</x>
<y>103</y>
</hint>
<hint type="destinationlabel">
<x>463</x>
<y>154</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutUseRescale</sender>
<signal>toggled(bool)</signal>
<receiver>advOutRescale</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>409</x>
<y>253</y>
</hint>
<hint type="destinationlabel">
<x>466</x>
<y>263</y>
</hint>
</hints>
</connection>
<connection>
<sender>advOutRecUseRescale</sender>
<signal>toggled(bool)</signal>
<receiver>advOutRecRescale</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>399</x>
<y>247</y>
</hint>
<hint type="destinationlabel">
<x>469</x>
<y>248</y>
</hint>
</hints>
</connection>
</connections> </connections>
</ui> </ui>
...@@ -245,7 +245,446 @@ bool SimpleOutput::RecordingActive() const ...@@ -245,7 +245,446 @@ bool SimpleOutput::RecordingActive() const
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
struct AdvancedOutput : BasicOutputHandler {
OBSEncoder aacTrack[4];
OBSEncoder h264Streaming;
OBSEncoder h264Recording;
bool ffmpegRecording;
bool useStreamEncoder;
AdvancedOutput(OBSBasic *main_);
inline void UpdateStreamSettings();
inline void UpdateRecordingSettings();
virtual void Update() override;
inline void SetupStreaming();
inline void SetupRecording();
inline void SetupFFmpeg();
inline void SetupAudio();
void SetupOutputs();
virtual bool StartStreaming(obs_service_t *service) override;
virtual bool StartRecording() override;
virtual void StopStreaming() override;
virtual void StopRecording() override;
virtual bool StreamingActive() const override;
virtual bool RecordingActive() const override;
};
static OBSData GetDataFromJsonFile(const char *jsonFile)
{
char fullPath[512];
int ret = os_get_config_path(fullPath, sizeof(fullPath), jsonFile);
if (ret > 0) {
BPtr<char> jsonData = os_quick_read_utf8_file(fullPath);
if (!!jsonData) {
obs_data_t *data = obs_data_create_from_json(jsonData);
OBSData dataRet(data);
obs_data_release(data);
return dataRet;
}
}
return nullptr;
}
AdvancedOutput::AdvancedOutput(OBSBasic *main_) : BasicOutputHandler(main_)
{
const char *recType = config_get_string(main->Config(), "AdvOut",
"RecType");
const char *streamEncoder = config_get_string(main->Config(), "AdvOut",
"Encoder");
const char *recordEncoder = config_get_string(main->Config(), "AdvOut",
"RecEncoder");
ffmpegRecording = astrcmpi(recType, "FFmpeg") == 0;
useStreamEncoder = astrcmpi(recordEncoder, "none") == 0;
OBSData streamEncSettings = GetDataFromJsonFile(
"obs-studio/basic/streamEncoder.json");
OBSData recordEncSettings = GetDataFromJsonFile(
"obs-studio/basic/recordEncoder.json");
streamOutput = obs_output_create("rtmp_output", "adv_stream",
nullptr);
if (!streamOutput)
throw "Failed to create stream output (advanced output)";
if (ffmpegRecording) {
fileOutput = obs_output_create("ffmpeg_output",
"adv_ffmpeg_output", nullptr);
if (!fileOutput)
throw "Failed to create recording FFmpeg output "
"(advanced output)";
} else {
fileOutput = obs_output_create("flv_output", "adv_file_output",
nullptr);
if (!fileOutput)
throw "Failed to create recording output "
"(advanced output)";
if (!useStreamEncoder) {
h264Recording = obs_video_encoder_create(recordEncoder,
"recording_h264", recordEncSettings);
if (!h264Recording)
throw "Failed to create recording h264 "
"encoder (advanced output)";
}
}
h264Streaming = obs_video_encoder_create(streamEncoder,
"streaming_h264", streamEncSettings);
if (!h264Streaming)
throw "Failed to create streaming h264 encoder "
"(advanced output)";
for (int i = 0; i < 4; i++) {
char name[9];
sprintf(name, "adv_aac%d", i);
aacTrack[i] = obs_audio_encoder_create("libfdk_aac",
name, nullptr, i);
if (!aacTrack[i])
aacTrack[i] = obs_audio_encoder_create("ffmpeg_aac",
name, nullptr, i);
if (!aacTrack[i])
throw "Failed to create audio encoder "
"(advanced output)";
}
signal_handler_connect(obs_output_get_signal_handler(streamOutput),
"start", OBSStartStreaming, this);
signal_handler_connect(obs_output_get_signal_handler(streamOutput),
"stop", OBSStopStreaming, this);
signal_handler_connect(obs_output_get_signal_handler(fileOutput),
"start", OBSStartRecording, this);
signal_handler_connect(obs_output_get_signal_handler(fileOutput),
"stop", OBSStopRecording, this);
}
void AdvancedOutput::UpdateStreamSettings()
{
OBSData settings = GetDataFromJsonFile(
"obs-studio/basic/streamEncoder.json");
obs_encoder_update(h264Streaming, settings);
}
inline void AdvancedOutput::UpdateRecordingSettings()
{
OBSData settings = GetDataFromJsonFile(
"obs-studio/basic/recordEncoder.json");
obs_encoder_update(h264Recording, settings);
}
void AdvancedOutput::Update()
{
UpdateStreamSettings();
if (useStreamEncoder && !ffmpegRecording)
UpdateRecordingSettings();
}
inline void AdvancedOutput::SetupStreaming()
{
bool rescale = config_get_bool(main->Config(), "AdvOut",
"Rescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"RescaleRes");
bool multitrack = config_get_bool(main->Config(), "AdvOut",
"Multitrack");
int trackIndex = config_get_int(main->Config(), "AdvOut",
"TrackIndex");
int trackCount = config_get_int(main->Config(), "AdvOut",
"TrackCount");
unsigned int cx = 0;
unsigned int cy = 0;
if (rescale && sscanf(rescaleRes, "%ux%u", &cx, &cy) != 3) {
cx = 0;
cy = 0;
}
obs_encoder_set_scaled_size(h264Streaming, cx, cy);
obs_encoder_set_video(h264Streaming, obs_get_video());
obs_output_set_video_encoder(streamOutput, h264Streaming);
if (multitrack) {
int i = 0;
for (; i < trackCount; i++)
obs_output_set_audio_encoder(streamOutput, aacTrack[i],
i);
for (; i < 4; i++)
obs_output_set_audio_encoder(streamOutput, nullptr, i);
} else {
obs_output_set_audio_encoder(streamOutput,
aacTrack[trackIndex - 1], 0);
}
}
inline void AdvancedOutput::SetupRecording()
{
const char *path = config_get_string(main->Config(), "AdvOut",
"RecFilePath");
bool rescale = config_get_bool(main->Config(), "AdvOut",
"RecRescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"RecRescaleRes");
bool multitrack = config_get_bool(main->Config(), "AdvOut",
"RecMultitrack");
int trackIndex = config_get_int(main->Config(), "AdvOut",
"RecTrackIndex");
int trackCount = config_get_int(main->Config(), "AdvOut",
"RecTrackCount");
obs_data_t *settings = obs_data_create();
unsigned int cx = 0;
unsigned int cy = 0;
if (useStreamEncoder) {
obs_output_set_video_encoder(fileOutput, h264Streaming);
} else {
if (rescale && sscanf(rescaleRes, "%ux%u", &cx, &cy) != 2) {
cx = 0;
cy = 0;
}
obs_encoder_set_scaled_size(h264Recording, cx, cy);
obs_encoder_set_video(h264Recording, obs_get_video());
obs_output_set_video_encoder(fileOutput, h264Recording);
}
if (multitrack) {
int i = 0;
for (; i < trackCount; i++)
obs_output_set_audio_encoder(fileOutput, aacTrack[i],
i);
for (; i < 4; i++)
obs_output_set_audio_encoder(fileOutput, nullptr, i);
} else {
obs_output_set_audio_encoder(fileOutput,
aacTrack[trackIndex - 1], 0);
}
obs_data_set_string(settings, "path", path);
obs_output_update(fileOutput, settings);
obs_data_release(settings);
}
inline void AdvancedOutput::SetupFFmpeg()
{
const char *url = config_get_string(main->Config(), "AdvOut", "FFURL");
int vBitrate = config_get_int(main->Config(), "AdvOut",
"FFVBitrate");
bool rescale = config_get_bool(main->Config(), "AdvOut",
"FFRescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"FFRescaleRes");
const char *vEncoder = config_get_string(main->Config(), "AdvOut",
"FFVEncoder");
const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
"FFVCustom");
int aBitrate = config_get_int(main->Config(), "AdvOut",
"FFABitrate");
int aTrack = config_get_int(main->Config(), "AdvOut",
"FFAudioTrack");
const char *aEncoder = config_get_string(main->Config(), "AdvOut",
"FFAEncoder");
const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
"FFACustom");
obs_data_t *settings = obs_data_create();
obs_data_set_string(settings, "url", url);
obs_data_set_int(settings, "video_bitrate", vBitrate);
obs_data_set_string(settings, "video_encoder", vEncoder);
obs_data_set_string(settings, "video_settings", vEncCustom);
obs_data_set_int(settings, "audio_bitrate", aBitrate);
obs_data_set_string(settings, "audio_encoder", aEncoder);
obs_data_set_string(settings, "audio_settings", aEncCustom);
if (rescale && rescaleRes && *rescaleRes) {
int width;
int height;
int val = sscanf(rescaleRes, "%dx%d", &width, &height);
if (val == 2 && width && height) {
obs_data_set_int(settings, "scale_width", width);
obs_data_set_int(settings, "scale_height", height);
}
}
obs_output_set_mixer(fileOutput, aTrack - 1);
obs_output_set_media(fileOutput, obs_get_video(), obs_get_audio());
obs_output_update(fileOutput, settings);
obs_data_release(settings);
}
static inline void SetEncoderName(obs_encoder_t *encoder, const char *name,
const char *defaultName)
{
obs_encoder_set_name(encoder, (name && *name) ? name : defaultName);
}
inline void AdvancedOutput::SetupAudio()
{
int track1Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track1Bitrate");
int track2Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track2Bitrate");
int track3Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track3Bitrate");
int track4Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track4Bitrate");
const char *name1 = config_get_string(main->Config(), "AdvOut",
"Track1Name");
const char *name2 = config_get_string(main->Config(), "AdvOut",
"Track2Name");
const char *name3 = config_get_string(main->Config(), "AdvOut",
"Track3Name");
const char *name4 = config_get_string(main->Config(), "AdvOut",
"Track4Name");
obs_data_t *settings[4];
for (size_t i = 0; i < 4; i++)
settings[i] = obs_data_create();
obs_data_set_int(settings[0], "bitrate", track1Bitrate);
obs_data_set_int(settings[1], "bitrate", track2Bitrate);
obs_data_set_int(settings[2], "bitrate", track3Bitrate);
obs_data_set_int(settings[3], "bitrate", track4Bitrate);
SetEncoderName(aacTrack[0], name1, "Track1");
SetEncoderName(aacTrack[1], name2, "Track2");
SetEncoderName(aacTrack[2], name3, "Track3");
SetEncoderName(aacTrack[3], name4, "Track4");
for (size_t i = 0; i < 4; i++) {
obs_encoder_update(aacTrack[i], settings[i]);
obs_data_release(settings[i]);
}
}
void AdvancedOutput::SetupOutputs()
{
obs_encoder_set_video(h264Streaming, obs_get_video());
if (h264Recording)
obs_encoder_set_video(h264Recording, obs_get_video());
obs_encoder_set_audio(aacTrack[0], obs_get_audio());
obs_encoder_set_audio(aacTrack[1], obs_get_audio());
obs_encoder_set_audio(aacTrack[2], obs_get_audio());
obs_encoder_set_audio(aacTrack[3], obs_get_audio());
SetupStreaming();
SetupAudio();
if (ffmpegRecording)
SetupFFmpeg();
else
SetupRecording();
}
bool AdvancedOutput::StartStreaming(obs_service_t *service)
{
AdvancedOutput::Update();
if (!Active())
SetupOutputs();
obs_output_set_service(streamOutput, service);
bool reconnect = config_get_bool(main->Config(), "AdvOut", "Reconnect");
int retryDelay = config_get_int(main->Config(), "AdvOut", "RetryDelay");
int maxRetries = config_get_int(main->Config(), "AdvOut", "MaxRetries");
if (!reconnect)
maxRetries = 0;
obs_output_set_reconnect_settings(streamOutput, maxRetries,
retryDelay);
if (obs_output_start(streamOutput)) {
activeRefs++;
return true;
}
return false;
}
bool AdvancedOutput::StartRecording()
{
AdvancedOutput::Update();
if (!Active())
SetupOutputs();
if (!ffmpegRecording) {
const char *path = config_get_string(main->Config(),
"AdvOut", "RecFilePath");
os_dir_t *dir = path ? os_opendir(path) : nullptr;
if (!dir) {
QMessageBox::information(main,
QTStr("Output.BadPath.Title"),
QTStr("Output.BadPath.Text"));
return false;
}
os_closedir(dir);
string strPath;
strPath += path;
char lastChar = strPath.back();
if (lastChar != '/' && lastChar != '\\')
strPath += "/";
strPath += GenerateTimeDateFilename("flv");
obs_data_t *settings = obs_data_create();
obs_data_set_string(settings, "path", strPath.c_str());
obs_output_update(fileOutput, settings);
obs_data_release(settings);
}
if (obs_output_start(fileOutput)) {
activeRefs++;
return true;
}
return false;
}
void AdvancedOutput::StopStreaming()
{
obs_output_stop(streamOutput);
}
void AdvancedOutput::StopRecording()
{
obs_output_stop(fileOutput);
}
bool AdvancedOutput::StreamingActive() const
{
return obs_output_active(streamOutput);
}
bool AdvancedOutput::RecordingActive() const
{
return obs_output_active(fileOutput);
}
/* ------------------------------------------------------------------------ */
BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main) BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main)
{ {
return new SimpleOutput(main); return new SimpleOutput(main);
} }
BasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main)
{
return new AdvancedOutput(main);
}
...@@ -25,3 +25,4 @@ struct BasicOutputHandler { ...@@ -25,3 +25,4 @@ struct BasicOutputHandler {
}; };
BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main); BasicOutputHandler *CreateSimpleOutputHandler(OBSBasic *main);
BasicOutputHandler *CreateAdvancedOutputHandler(OBSBasic *main);
...@@ -385,7 +385,6 @@ bool OBSBasic::InitBasicConfigDefaults() ...@@ -385,7 +385,6 @@ bool OBSBasic::InitBasicConfigDefaults()
uint32_t cx = monitors[0].cx; uint32_t cx = monitors[0].cx;
uint32_t cy = monitors[0].cy; uint32_t cy = monitors[0].cy;
/* TODO: temporary */
config_set_default_string(basicConfig, "Output", "Type", "Simple"); config_set_default_string(basicConfig, "Output", "Type", "Simple");
config_set_default_string(basicConfig, "SimpleOutput", "FilePath", config_set_default_string(basicConfig, "SimpleOutput", "FilePath",
...@@ -407,6 +406,39 @@ bool OBSBasic::InitBasicConfigDefaults() ...@@ -407,6 +406,39 @@ bool OBSBasic::InitBasicConfigDefaults()
config_set_default_string(basicConfig, "SimpleOutput", "Preset", config_set_default_string(basicConfig, "SimpleOutput", "Preset",
"veryfast"); "veryfast");
config_set_default_bool (basicConfig, "AdvOut", "Reconnect", true);
config_set_default_uint (basicConfig, "AdvOut", "RetryDelay", 2);
config_set_default_uint (basicConfig, "AdvOut", "MaxRetries", 20);
config_set_default_bool (basicConfig, "AdvOut", "UseRescale", false);
config_set_default_bool (basicConfig, "AdvOut", "Multitrack", false);
config_set_default_uint (basicConfig, "AdvOut", "TrackIndex", 1);
config_set_default_uint (basicConfig, "AdvOut", "TrackCount", 1);
config_set_default_string(basicConfig, "AdvOut", "Encoder", "obs_x264");
config_set_default_string(basicConfig, "AdvOut", "RecType", "Standard");
config_set_default_string(basicConfig, "AdvOut", "RecFilePath",
GetDefaultVideoSavePath().c_str());
config_set_default_bool (basicConfig, "AdvOut", "RecUseRescale",
false);
config_set_default_bool (basicConfig, "AdvOut", "RecMultitrack",
false);
config_set_default_uint (basicConfig, "AdvOut", "RecTrackIndex", 1);
config_set_default_uint (basicConfig, "AdvOut", "RecTrackCount", 1);
config_set_default_string(basicConfig, "AdvOut", "RecEncoder",
"none");
config_set_default_uint (basicConfig, "AdvOut", "FFVBitrate", 2500);
config_set_default_bool (basicConfig, "AdvOut", "FFUseRescale",
false);
config_set_default_uint (basicConfig, "AdvOut", "FFABitrate", 160);
config_set_default_uint (basicConfig, "AdvOut", "FFAudioTrack", 1);
config_set_default_uint (basicConfig, "AdvOut", "Track1Bitrate", 160);
config_set_default_uint (basicConfig, "AdvOut", "Track2Bitrate", 160);
config_set_default_uint (basicConfig, "AdvOut", "Track3Bitrate", 160);
config_set_default_uint (basicConfig, "AdvOut", "Track4Bitrate", 160);
config_set_default_uint (basicConfig, "Video", "BaseCX", cx); config_set_default_uint (basicConfig, "Video", "BaseCX", cx);
config_set_default_uint (basicConfig, "Video", "BaseCY", cy); config_set_default_uint (basicConfig, "Video", "BaseCY", cy);
...@@ -504,9 +536,14 @@ void OBSBasic::InitPrimitives() ...@@ -504,9 +536,14 @@ void OBSBasic::InitPrimitives()
void OBSBasic::ResetOutputs() void OBSBasic::ResetOutputs()
{ {
const char *mode = config_get_string(basicConfig, "Output", "Mode");
bool advOut = astrcmpi(mode, "Advanced") == 0;
if (!outputHandler || !outputHandler->Active()) { if (!outputHandler || !outputHandler->Active()) {
outputHandler.reset(); outputHandler.reset();
outputHandler.reset(CreateSimpleOutputHandler(this)); outputHandler.reset(advOut ?
CreateAdvancedOutputHandler(this) :
CreateSimpleOutputHandler(this));
} else { } else {
outputHandler->Update(); outputHandler->Update();
} }
......
...@@ -141,9 +141,49 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) ...@@ -141,9 +141,49 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
HookWidget(ui->simpleOutAdvanced, CHECK_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutAdvanced, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->simpleOutUseCBR, CHECK_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutUseCBR, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->simpleOutPreset, COMBO_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutPreset, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->simpleOutCustom, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->simpleOutUseBufsize, CHECK_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutUseBufsize, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->simpleOutPreset, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->simpleOutVBufsize, SCROLL_CHANGED, OUTPUTS_CHANGED); HookWidget(ui->simpleOutVBufsize, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutReconnect, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRetryDelay, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutMaxRetries, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutEncoder, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecType, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecPath, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecEncoder, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutRecTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFURL, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFVBitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFUseRescale, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFRescale, CBEDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFVEncoder, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFVCfg, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFABitrate, SCROLL_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack1, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack2, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack3, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFTrack4, CHECK_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFAEncoder, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutFFACfg, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack1Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack1Name, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack2Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack2Name, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack3Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack3Name, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack4Bitrate, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->advOutTrack4Name, EDIT_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->channelSetup, COMBO_CHANGED, AUDIO_RESTART); HookWidget(ui->channelSetup, COMBO_CHANGED, AUDIO_RESTART);
HookWidget(ui->sampleRate, COMBO_CHANGED, AUDIO_RESTART); HookWidget(ui->sampleRate, COMBO_CHANGED, AUDIO_RESTART);
HookWidget(ui->desktopAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED); HookWidget(ui->desktopAudioDevice1, COMBO_CHANGED, AUDIO_CHANGED);
...@@ -168,6 +208,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) ...@@ -168,6 +208,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
LoadServiceTypes(); LoadServiceTypes();
LoadServiceInfo(); LoadServiceInfo();
LoadEncoderTypes();
LoadSettings(false); LoadSettings(false);
} }
...@@ -189,12 +230,15 @@ void OBSBasicSettings::SaveComboData(QComboBox *widget, const char *section, ...@@ -189,12 +230,15 @@ void OBSBasicSettings::SaveComboData(QComboBox *widget, const char *section,
} }
} }
void OBSBasicSettings::SaveCheckBox(QCheckBox *widget, const char *section, void OBSBasicSettings::SaveCheckBox(QAbstractButton *widget,
const char *value) const char *section, const char *value, bool invert)
{ {
if (WidgetChanged(widget)) if (WidgetChanged(widget)) {
config_set_bool(main->Config(), section, value, bool checked = widget->isChecked();
widget->isChecked()); if (invert) checked = !checked;
config_set_bool(main->Config(), section, value, checked);
}
} }
void OBSBasicSettings::SaveEdit(QLineEdit *widget, const char *section, void OBSBasicSettings::SaveEdit(QLineEdit *widget, const char *section,
...@@ -246,6 +290,31 @@ void OBSBasicSettings::LoadServiceInfo() ...@@ -246,6 +290,31 @@ void OBSBasicSettings::LoadServiceInfo()
obs_data_release(settings); obs_data_release(settings);
} }
#define TEXT_USE_STREAM_ENC \
QTStr("Basic.Settings.Output.Adv.Recording.UseStreamEncoder")
void OBSBasicSettings::LoadEncoderTypes()
{
const char *type;
size_t idx = 0;
ui->advOutRecEncoder->addItem(TEXT_USE_STREAM_ENC, "none");
while (obs_enum_encoder_types(idx++, &type)) {
const char *name = obs_encoder_get_display_name(type);
const char *codec = obs_get_encoder_codec(type);
if (strcmp(codec, "h264") != 0)
continue;
QString qName = QT_UTF8(name);
QString qType = QT_UTF8(type);
ui->advOutEncoder->addItem(qName, qType);
ui->advOutRecEncoder->addItem(qName, qType);
}
}
void OBSBasicSettings::LoadLanguageList() void OBSBasicSettings::LoadLanguageList()
{ {
const char *currentLang = App()->GetLocale(); const char *currentLang = App()->GetLocale();
...@@ -320,7 +389,18 @@ static const size_t numVals = sizeof(vals)/sizeof(double); ...@@ -320,7 +389,18 @@ static const size_t numVals = sizeof(vals)/sizeof(double);
void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy) void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy)
{ {
QString advRescale;
QString advRecRescale;
QString advFFRescale;
advRescale = ui->advOutRescale->lineEdit()->text();
advRecRescale = ui->advOutRecRescale->lineEdit()->text();
advFFRescale = ui->advOutFFRescale->lineEdit()->text();
ui->outputResolution->clear(); ui->outputResolution->clear();
ui->advOutRescale->clear();
ui->advOutRecRescale->clear();
ui->advOutFFRescale->clear();
for (size_t idx = 0; idx < numVals; idx++) { for (size_t idx = 0; idx < numVals; idx++) {
uint32_t downscaleCX = uint32_t(double(cx) / vals[idx]); uint32_t downscaleCX = uint32_t(double(cx) / vals[idx]);
...@@ -328,9 +408,25 @@ void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy) ...@@ -328,9 +408,25 @@ void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy)
string res = ResString(downscaleCX, downscaleCY); string res = ResString(downscaleCX, downscaleCY);
ui->outputResolution->addItem(res.c_str()); ui->outputResolution->addItem(res.c_str());
ui->advOutRescale->addItem(res.c_str());
ui->advOutRecRescale->addItem(res.c_str());
ui->advOutFFRescale->addItem(res.c_str());
} }
ui->outputResolution->lineEdit()->setText(ResString(cx, cy).c_str()); string res = ResString(cx, cy);
ui->outputResolution->lineEdit()->setText(res.c_str());
if (advRescale.isEmpty())
advRescale = res.c_str();
if (advRecRescale.isEmpty())
advRecRescale = res.c_str();
if (advFFRescale.isEmpty())
advFFRescale = res.c_str();
ui->advOutRescale->lineEdit()->setText(advRescale);
ui->advOutRecRescale->lineEdit()->setText(advRecRescale);
ui->advOutFFRescale->lineEdit()->setText(advFFRescale);
} }
void OBSBasicSettings::LoadDownscaleFilters() void OBSBasicSettings::LoadDownscaleFilters()
...@@ -483,11 +579,219 @@ void OBSBasicSettings::LoadSimpleOutputSettings() ...@@ -483,11 +579,219 @@ void OBSBasicSettings::LoadSimpleOutputSettings()
ui->simpleOutCustom->setText(custom); ui->simpleOutCustom->setText(custom);
} }
void OBSBasicSettings::LoadAdvOutputStreamingSettings()
{
bool reconnect = config_get_bool(main->Config(), "AdvOut",
"Reconnect");
int retryDelay = config_get_int(main->Config(), "AdvOut",
"RetryDelay");
int maxRetries = config_get_int(main->Config(), "AdvOut",
"MaxRetries");
bool rescale = config_get_bool(main->Config(), "AdvOut",
"Rescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"RescaleRes");
int trackIndex = config_get_int(main->Config(), "AdvOut",
"TrackIndex");
ui->advOutReconnect->setChecked(reconnect);
ui->advOutRetryDelay->setValue(retryDelay);
ui->advOutMaxRetries->setValue(maxRetries);
ui->advOutUseRescale->setChecked(rescale);
ui->advOutRescale->setCurrentText(rescaleRes);
switch (trackIndex) {
case 1: ui->advOutTrack1->setChecked(true); break;
case 2: ui->advOutTrack2->setChecked(true); break;
case 3: ui->advOutTrack3->setChecked(true); break;
case 4: ui->advOutTrack4->setChecked(true); break;
}
}
OBSPropertiesView *OBSBasicSettings::CreateEncoderPropertyView(
const char *encoder, const char *path, bool changed)
{
obs_data_t *settings = obs_encoder_defaults(encoder);
OBSPropertiesView *view;
char encoderJsonPath[512];
int ret = os_get_config_path(encoderJsonPath, sizeof(encoderJsonPath),
path);
if (ret > 0) {
BPtr<char> jsonData = os_quick_read_utf8_file(encoderJsonPath);
if (!!jsonData) {
obs_data_t *data = obs_data_create_from_json(jsonData);
obs_data_apply(settings, data);
obs_data_release(data);
}
}
view = new OBSPropertiesView(settings, encoder,
(PropertiesReloadCallback)obs_get_encoder_properties,
170);
view->setFrameShape(QFrame::StyledPanel);
view->setProperty("changed", QVariant(changed));
QObject::connect(view, SIGNAL(Changed()), this, SLOT(OutputsChanged()));
obs_data_release(settings);
return view;
}
void OBSBasicSettings::LoadAdvOutputStreamingEncoderProperties()
{
const char *encoder = config_get_string(main->Config(), "AdvOut",
"Encoder");
delete streamEncoderProps;
streamEncoderProps = CreateEncoderPropertyView(encoder,
"obs-studio/basic/streamEncoder.json");
ui->advOutputStreamTab->layout()->addWidget(streamEncoderProps);
SetComboByValue(ui->advOutEncoder, encoder);
}
void OBSBasicSettings::LoadAdvOutputRecordingSettings()
{
const char *type = config_get_string(main->Config(), "AdvOut",
"RecType");
const char *path = config_get_string(main->Config(), "AdvOut",
"RecFilePath");
bool rescale = config_get_bool(main->Config(), "AdvOut",
"RecRescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"RecRescaleRes");
int trackIndex = config_get_int(main->Config(), "AdvOut",
"RecTrackIndex");
int typeIndex = (astrcmpi(type, "FFmpeg") == 0) ? 1 : 0;
ui->advOutRecType->setCurrentIndex(typeIndex);
ui->advOutRecPath->setText(path);
ui->advOutRecUseRescale->setChecked(rescale);
ui->advOutRecRescale->setCurrentText(rescaleRes);
switch (trackIndex) {
case 1: ui->advOutRecTrack1->setChecked(true); break;
case 2: ui->advOutRecTrack2->setChecked(true); break;
case 3: ui->advOutRecTrack3->setChecked(true); break;
case 4: ui->advOutRecTrack4->setChecked(true); break;
}
}
void OBSBasicSettings::LoadAdvOutputRecordingEncoderProperties()
{
const char *encoder = config_get_string(main->Config(), "AdvOut",
"RecEncoder");
delete recordEncoderProps;
recordEncoderProps = nullptr;
if (astrcmpi(encoder, "none") != 0) {
recordEncoderProps = CreateEncoderPropertyView(encoder,
"obs-studio/basic/recordEncoder.json");
ui->advOutRecStandard->layout()->addWidget(recordEncoderProps);
}
SetComboByValue(ui->advOutRecEncoder, encoder);
}
void OBSBasicSettings::LoadAdvOutputFFmpegSettings()
{
const char *url = config_get_string(main->Config(), "AdvOut", "FFURL");
int videoBitrate = config_get_int(main->Config(), "AdvOut",
"FFVBitrate");
bool rescale = config_get_bool(main->Config(), "AdvOut",
"FFRescale");
const char *rescaleRes = config_get_string(main->Config(), "AdvOut",
"FFRescaleRes");
const char *vEncoder = config_get_string(main->Config(), "AdvOut",
"FFVEncoder");
const char *vEncCustom = config_get_string(main->Config(), "AdvOut",
"FFVCustom");
int audioBitrate = config_get_int(main->Config(), "AdvOut",
"FFABitrate");
int audioTrack = config_get_int(main->Config(), "AdvOut",
"FFAudioTrack");
const char *aEncoder = config_get_string(main->Config(), "AdvOut",
"FFAEncoder");
const char *aEncCustom = config_get_string(main->Config(), "AdvOut",
"FFACustom");
ui->advOutFFURL->setText(url);
ui->advOutFFVBitrate->setValue(videoBitrate);
ui->advOutFFUseRescale->setChecked(rescale);
ui->advOutFFRescale->setCurrentText(rescaleRes);
ui->advOutFFVEncoder->setText(vEncoder);
ui->advOutFFVCfg->setText(vEncCustom);
ui->advOutFFABitrate->setValue(audioBitrate);
ui->advOutFFAEncoder->setText(aEncoder);
ui->advOutFFACfg->setText(aEncCustom);
switch (audioTrack) {
case 1: ui->advOutFFTrack1->setChecked(true); break;
case 2: ui->advOutFFTrack2->setChecked(true); break;
case 3: ui->advOutFFTrack3->setChecked(true); break;
case 4: ui->advOutFFTrack4->setChecked(true); break;
}
}
void OBSBasicSettings::LoadAdvOutputAudioSettings()
{
int track1Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track1Bitrate");
int track2Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track2Bitrate");
int track3Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track3Bitrate");
int track4Bitrate = config_get_uint(main->Config(), "AdvOut",
"Track4Bitrate");
const char *name1 = config_get_string(main->Config(), "AdvOut",
"Track1Name");
const char *name2 = config_get_string(main->Config(), "AdvOut",
"Track2Name");
const char *name3 = config_get_string(main->Config(), "AdvOut",
"Track3Name");
const char *name4 = config_get_string(main->Config(), "AdvOut",
"Track4Name");
SetComboByName(ui->advOutTrack1Bitrate,
std::to_string(track1Bitrate).c_str());
SetComboByName(ui->advOutTrack2Bitrate,
std::to_string(track2Bitrate).c_str());
SetComboByName(ui->advOutTrack3Bitrate,
std::to_string(track3Bitrate).c_str());
SetComboByName(ui->advOutTrack4Bitrate,
std::to_string(track4Bitrate).c_str());
ui->advOutTrack1Name->setText(name1);
ui->advOutTrack2Name->setText(name2);
ui->advOutTrack3Name->setText(name3);
ui->advOutTrack4Name->setText(name4);
}
void OBSBasicSettings::LoadOutputSettings() void OBSBasicSettings::LoadOutputSettings()
{ {
loading = true; loading = true;
const char *mode = config_get_string(main->Config(), "Output", "Mode");
int modeIdx = astrcmpi(mode, "Advanced") == 0 ? 1 : 0;
ui->outputMode->setCurrentIndex(modeIdx);
LoadSimpleOutputSettings(); LoadSimpleOutputSettings();
LoadAdvOutputStreamingSettings();
LoadAdvOutputStreamingEncoderProperties();
LoadAdvOutputRecordingSettings();
LoadAdvOutputRecordingEncoderProperties();
LoadAdvOutputFFmpegSettings();
LoadAdvOutputAudioSettings();
if (video_output_active(obs_get_video())) {
ui->outputMode->setEnabled(false);
ui->advOutTopContainer->setEnabled(false);
ui->advOutRecTopContainer->setEnabled(false);
ui->advOutRecTypeContainer->setEnabled(false);
ui->advOutputAudioTracksTab->setEnabled(false);
}
loading = false; loading = false;
} }
...@@ -645,9 +949,60 @@ void OBSBasicSettings::SaveVideoSettings() ...@@ -645,9 +949,60 @@ void OBSBasicSettings::SaveVideoSettings()
main->ResetVideo(); main->ResetVideo();
} }
/* TODO: Temporary! */ static inline const char *OutputModeFromIdx(int idx)
{
if (idx == 1)
return "Advanced";
else
return "Simple";
}
static inline const char *RecTypeFromIdx(int idx)
{
if (idx == 1)
return "FFmpeg";
else
return "Standard";
}
static void WriteJsonData(OBSPropertiesView *view, const char *path)
{
char full_path[512];
if (!view || !WidgetChanged(view))
return;
int ret = os_get_config_path(full_path, sizeof(full_path), path);
if (ret > 0) {
obs_data_t *settings = view->GetSettings();
if (settings) {
const char *json = obs_data_get_json(settings);
if (json && *json) {
os_quick_write_utf8_file(full_path, json,
strlen(json), false);
}
}
}
}
static void SaveTrackIndex(config_t *config, const char *section,
const char *name,
QAbstractButton *check1,
QAbstractButton *check2,
QAbstractButton *check3,
QAbstractButton *check4)
{
if (check1->isChecked()) config_set_int(config, section, name, 1);
else if (check2->isChecked()) config_set_int(config, section, name, 2);
else if (check3->isChecked()) config_set_int(config, section, name, 3);
else if (check4->isChecked()) config_set_int(config, section, name, 4);
}
void OBSBasicSettings::SaveOutputSettings() void OBSBasicSettings::SaveOutputSettings()
{ {
config_set_string(main->Config(), "Output", "Mode",
OutputModeFromIdx(ui->outputMode->currentIndex()));
SaveSpinBox(ui->simpleOutputVBitrate, "SimpleOutput", "VBitrate"); SaveSpinBox(ui->simpleOutputVBitrate, "SimpleOutput", "VBitrate");
SaveCombo(ui->simpleOutputABitrate, "SimpleOutput", "ABitrate"); SaveCombo(ui->simpleOutputABitrate, "SimpleOutput", "ABitrate");
SaveEdit(ui->simpleOutputPath, "SimpleOutput", "FilePath"); SaveEdit(ui->simpleOutputPath, "SimpleOutput", "FilePath");
...@@ -662,6 +1017,55 @@ void OBSBasicSettings::SaveOutputSettings() ...@@ -662,6 +1017,55 @@ void OBSBasicSettings::SaveOutputSettings()
if (ui->simpleOutUseBufsize->isChecked()) if (ui->simpleOutUseBufsize->isChecked())
SaveSpinBox(ui->simpleOutVBufsize, "SimpleOutput", "VBufsize"); SaveSpinBox(ui->simpleOutVBufsize, "SimpleOutput", "VBufsize");
SaveCheckBox(ui->advOutReconnect, "AdvOut", "Reconnect");
SaveSpinBox(ui->advOutRetryDelay, "AdvOut", "RetryDelay");
SaveSpinBox(ui->advOutMaxRetries, "AdvOut", "MaxRetries");
SaveComboData(ui->advOutEncoder, "AdvOut", "Encoder");
SaveCheckBox(ui->advOutUseRescale, "AdvOut", "Rescale");
SaveCombo(ui->advOutRescale, "AdvOut", "RescaleRes");
SaveTrackIndex(main->Config(), "AdvOut", "TrackIndex",
ui->advOutTrack1, ui->advOutTrack2,
ui->advOutTrack3, ui->advOutTrack4);
config_set_string(main->Config(), "AdvOut", "RecType",
RecTypeFromIdx(ui->advOutRecType->currentIndex()));
SaveEdit(ui->advOutRecPath, "AdvOut", "RecFilePath");
SaveComboData(ui->advOutRecEncoder, "AdvOut", "RecEncoder");
SaveCheckBox(ui->advOutRecUseRescale, "AdvOut", "RecRescale");
SaveCombo(ui->advOutRecRescale, "AdvOut", "RecRescaleRes");
SaveTrackIndex(main->Config(), "AdvOut", "RecTrackIndex",
ui->advOutRecTrack1, ui->advOutRecTrack2,
ui->advOutRecTrack3, ui->advOutRecTrack4);
SaveEdit(ui->advOutFFURL, "AdvOut", "FFURL");
SaveSpinBox(ui->advOutFFVBitrate, "AdvOut", "FFVBitrate");
SaveCheckBox(ui->advOutFFUseRescale, "AdvOut", "FFRescale");
SaveCombo(ui->advOutFFRescale, "AdvOut", "FFRescaleRes");
SaveEdit(ui->advOutFFVEncoder, "AdvOut", "FFVEncoder");
SaveEdit(ui->advOutFFVCfg, "AdvOut", "FFVCustom");
SaveSpinBox(ui->advOutFFABitrate, "AdvOut", "FFABitrate");
SaveEdit(ui->advOutFFAEncoder, "AdvOut", "FFAEncoder");
SaveEdit(ui->advOutFFACfg, "AdvOut", "FFACustom");
SaveTrackIndex(main->Config(), "AdvOut", "FFAudioTrack",
ui->advOutFFTrack1, ui->advOutFFTrack2,
ui->advOutFFTrack3, ui->advOutFFTrack4);
SaveCombo(ui->advOutTrack1Bitrate, "AdvOut", "Track1Bitrate");
SaveCombo(ui->advOutTrack2Bitrate, "AdvOut", "Track2Bitrate");
SaveCombo(ui->advOutTrack3Bitrate, "AdvOut", "Track3Bitrate");
SaveCombo(ui->advOutTrack4Bitrate, "AdvOut", "Track4Bitrate");
SaveEdit(ui->advOutTrack1Name, "AdvOut", "Track1Name");
SaveEdit(ui->advOutTrack2Name, "AdvOut", "Track2Name");
SaveEdit(ui->advOutTrack3Name, "AdvOut", "Track3Name");
SaveEdit(ui->advOutTrack4Name, "AdvOut", "Track4Name");
WriteJsonData(streamEncoderProps,
"obs-studio/basic/streamEncoder.json");
WriteJsonData(recordEncoderProps,
"obs-studio/basic/recordEncoder.json");
main->ResetOutputs();
} }
void OBSBasicSettings::SaveAudioSettings() void OBSBasicSettings::SaveAudioSettings()
...@@ -797,7 +1201,7 @@ void OBSBasicSettings::on_streamType_currentIndexChanged(int idx) ...@@ -797,7 +1201,7 @@ void OBSBasicSettings::on_streamType_currentIndexChanged(int idx)
void OBSBasicSettings::on_simpleOutputBrowse_clicked() void OBSBasicSettings::on_simpleOutputBrowse_clicked()
{ {
QString dir = QFileDialog::getExistingDirectory(this, QString dir = QFileDialog::getExistingDirectory(this,
QTStr("OpenDirectory"), QTStr("Basic.Settings.Output.SelectDirectory"),
ui->simpleOutputPath->text(), ui->simpleOutputPath->text(),
QFileDialog::ShowDirsOnly | QFileDialog::ShowDirsOnly |
QFileDialog::DontResolveSymlinks); QFileDialog::DontResolveSymlinks);
...@@ -807,6 +1211,61 @@ void OBSBasicSettings::on_simpleOutputBrowse_clicked() ...@@ -807,6 +1211,61 @@ void OBSBasicSettings::on_simpleOutputBrowse_clicked()
ui->simpleOutputPath->setText(dir); ui->simpleOutputPath->setText(dir);
} }
void OBSBasicSettings::on_advOutRecPathBrowse_clicked()
{
QString dir = QFileDialog::getExistingDirectory(this,
QTStr("Basic.Settings.Output.SelectDirectory"),
ui->advOutRecPath->text(),
QFileDialog::ShowDirsOnly |
QFileDialog::DontResolveSymlinks);
if (dir.isEmpty())
return;
ui->advOutRecPath->setText(dir);
}
void OBSBasicSettings::on_advOutFFPathBrowse_clicked()
{
QString file = QFileDialog::getSaveFileName(this,
QTStr("Basic.Settings.Output.SelectFile"),
ui->simpleOutputPath->text(),
QTStr("Basic.Settings.Output.Adv.FFmpeg.SaveFilter"));
if (file.isEmpty())
return;
ui->advOutFFURL->setText(file);
}
void OBSBasicSettings::on_advOutEncoder_currentIndexChanged(int idx)
{
QString encoder = GetComboData(ui->advOutEncoder);
delete streamEncoderProps;
streamEncoderProps = CreateEncoderPropertyView(QT_TO_UTF8(encoder),
"obs-studio/basic/streamEncoder.json", true);
ui->advOutputStreamTab->layout()->addWidget(streamEncoderProps);
UNUSED_PARAMETER(idx);
}
void OBSBasicSettings::on_advOutRecEncoder_currentIndexChanged(int idx)
{
ui->advOutRecUseRescale->setEnabled(idx > 0);
ui->advOutRecRescaleContainer->setEnabled(idx > 0);
delete recordEncoderProps;
recordEncoderProps = nullptr;
if (idx > 0) {
QString encoder = GetComboData(ui->advOutRecEncoder);
recordEncoderProps = CreateEncoderPropertyView(
QT_TO_UTF8(encoder),
"obs-studio/basic/recordEncoder.json", true);
ui->advOutRecStandard->layout()->addWidget(recordEncoderProps);
}
}
static inline bool StreamExists(const char *name) static inline bool StreamExists(const char *name)
{ {
return obs_get_service_by_name(name) != nullptr; return obs_get_service_by_name(name) != nullptr;
......
...@@ -45,13 +45,15 @@ private: ...@@ -45,13 +45,15 @@ private:
bool loading = true; bool loading = true;
OBSPropertiesView *streamProperties = nullptr; OBSPropertiesView *streamProperties = nullptr;
OBSPropertiesView *streamEncoderProps = nullptr;
OBSPropertiesView *recordEncoderProps = nullptr;
void SaveCombo(QComboBox *widget, const char *section, void SaveCombo(QComboBox *widget, const char *section,
const char *value); const char *value);
void SaveComboData(QComboBox *widget, const char *section, void SaveComboData(QComboBox *widget, const char *section,
const char *value); const char *value);
void SaveCheckBox(QCheckBox *widget, const char *section, void SaveCheckBox(QAbstractButton *widget, const char *section,
const char *value); const char *value, bool invert = false);
void SaveEdit(QLineEdit *widget, const char *section, void SaveEdit(QLineEdit *widget, const char *section,
const char *value); const char *value);
void SaveSpinBox(QSpinBox *widget, const char *section, void SaveSpinBox(QSpinBox *widget, const char *section,
...@@ -83,6 +85,7 @@ private: ...@@ -83,6 +85,7 @@ private:
void LoadServiceTypes(); void LoadServiceTypes();
void LoadServiceInfo(); void LoadServiceInfo();
void LoadEncoderTypes();
void LoadGeneralSettings(); void LoadGeneralSettings();
void LoadOutputSettings(); void LoadOutputSettings();
...@@ -90,11 +93,20 @@ private: ...@@ -90,11 +93,20 @@ private:
void LoadVideoSettings(); void LoadVideoSettings();
void LoadSettings(bool changedOnly); void LoadSettings(bool changedOnly);
OBSPropertiesView *CreateEncoderPropertyView(const char *encoder,
const char *path, bool changed = false);
/* general */ /* general */
void LoadLanguageList(); void LoadLanguageList();
/* output */ /* output */
void LoadSimpleOutputSettings(); void LoadSimpleOutputSettings();
void LoadAdvOutputStreamingSettings();
void LoadAdvOutputStreamingEncoderProperties();
void LoadAdvOutputRecordingSettings();
void LoadAdvOutputRecordingEncoderProperties();
void LoadAdvOutputFFmpegSettings();
void LoadAdvOutputAudioSettings();
/* audio */ /* audio */
void LoadListValues(QComboBox *widget, obs_property_t *prop, void LoadListValues(QComboBox *widget, obs_property_t *prop,
...@@ -123,6 +135,10 @@ private slots: ...@@ -123,6 +135,10 @@ private slots:
void on_streamType_currentIndexChanged(int idx); void on_streamType_currentIndexChanged(int idx);
void on_simpleOutputBrowse_clicked(); void on_simpleOutputBrowse_clicked();
void on_advOutRecPathBrowse_clicked();
void on_advOutFFPathBrowse_clicked();
void on_advOutEncoder_currentIndexChanged(int idx);
void on_advOutRecEncoder_currentIndexChanged(int idx);
void on_baseResolution_editTextChanged(const QString &text); void on_baseResolution_editTextChanged(const QString &text);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册