Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
尘离序散
obs-studio
提交
50ff29e5
O
obs-studio
项目概览
尘离序散
/
obs-studio
与 Fork 源项目一致
从无法访问的项目Fork
通知
30
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
O
obs-studio
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
50ff29e5
编写于
7月 07, 2020
作者:
J
Jim
提交者:
GitHub
7月 07, 2020
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #3087 from obsproject/virtualcam
Add Windows Virtual Camera
上级
79fff2e1
a72a52fa
变更
30
隐藏空白更改
内联
并排
Showing
30 changed file
with
1756 addition
and
10 deletion
+1756
-10
UI/data/locale/en-US.ini
UI/data/locale/en-US.ini
+3
-0
UI/installer/mp-installer.nsi
UI/installer/mp-installer.nsi
+14
-0
UI/obs-app.cpp
UI/obs-app.cpp
+6
-1
UI/obs-app.hpp
UI/obs-app.hpp
+1
-0
UI/win-update/updater/updater.cpp
UI/win-update/updater/updater.cpp
+34
-0
UI/window-basic-auto-config-test.cpp
UI/window-basic-auto-config-test.cpp
+12
-3
UI/window-basic-auto-config.cpp
UI/window-basic-auto-config.cpp
+20
-1
UI/window-basic-auto-config.hpp
UI/window-basic-auto-config.hpp
+2
-0
UI/window-basic-main-outputs.cpp
UI/window-basic-main-outputs.cpp
+68
-0
UI/window-basic-main-outputs.hpp
UI/window-basic-main-outputs.hpp
+9
-2
UI/window-basic-main.cpp
UI/window-basic-main.cpp
+115
-0
UI/window-basic-main.hpp
UI/window-basic-main.hpp
+15
-1
plugins/win-dshow/CMakeLists.txt
plugins/win-dshow/CMakeLists.txt
+17
-1
plugins/win-dshow/data/placeholder.png
plugins/win-dshow/data/placeholder.png
+0
-0
plugins/win-dshow/dshow-plugin.cpp
plugins/win-dshow/dshow-plugin.cpp
+35
-0
plugins/win-dshow/libdshowcapture
plugins/win-dshow/libdshowcapture
+1
-1
plugins/win-dshow/shared-memory-queue.c
plugins/win-dshow/shared-memory-queue.c
+209
-0
plugins/win-dshow/shared-memory-queue.h
plugins/win-dshow/shared-memory-queue.h
+37
-0
plugins/win-dshow/tiny-nv12-scale.c
plugins/win-dshow/tiny-nv12-scale.c
+134
-0
plugins/win-dshow/tiny-nv12-scale.h
plugins/win-dshow/tiny-nv12-scale.h
+28
-0
plugins/win-dshow/virtualcam-guid.h
plugins/win-dshow/virtualcam-guid.h
+8
-0
plugins/win-dshow/virtualcam-module/CMakeLists.txt
plugins/win-dshow/virtualcam-module/CMakeLists.txt
+78
-0
plugins/win-dshow/virtualcam-module/placeholder.cpp
plugins/win-dshow/virtualcam-module/placeholder.cpp
+146
-0
plugins/win-dshow/virtualcam-module/sleepto.c
plugins/win-dshow/virtualcam-module/sleepto.c
+50
-0
plugins/win-dshow/virtualcam-module/sleepto.h
plugins/win-dshow/virtualcam-module/sleepto.h
+14
-0
plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp
plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp
+241
-0
plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp
plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp
+51
-0
plugins/win-dshow/virtualcam-module/virtualcam-module.cpp
plugins/win-dshow/virtualcam-module/virtualcam-module.cpp
+298
-0
plugins/win-dshow/virtualcam-module/virtualcam-module.def.in
plugins/win-dshow/virtualcam-module/virtualcam-module.def.in
+8
-0
plugins/win-dshow/virtualcam.c
plugins/win-dshow/virtualcam.c
+102
-0
未找到文件。
UI/data/locale/en-US.ini
浏览文件 @
50ff29e5
...
...
@@ -150,6 +150,7 @@ Basic.AutoConfig.StartPage="Usage Information"
Basic.AutoConfig.StartPage.SubTitle
=
"Specify what you want to use the program for"
Basic.AutoConfig.StartPage.PrioritizeStreaming
=
"Optimize for streaming, recording is secondary"
Basic.AutoConfig.StartPage.PrioritizeRecording
=
"Optimize just for recording, I will not be streaming"
Basic.AutoConfig.StartPage.PrioritizeVirtualCam
=
"I will only be using the virtual camera"
Basic.AutoConfig.VideoPage
=
"Video Settings"
Basic.AutoConfig.VideoPage.SubTitle
=
"Specify the desired video settings you would like to use"
Basic.AutoConfig.VideoPage.BaseResolution.UseCurrent
=
"Use Current (%1x%2)"
...
...
@@ -520,6 +521,7 @@ Basic.Main.StartRecording="Start Recording"
Basic.Main.StartReplayBuffer
=
"Start Replay Buffer"
Basic.Main.SaveReplay
=
"Save Replay"
Basic.Main.StartStreaming
=
"Start Streaming"
Basic.Main.StartVirtualCam
=
"Start Virtual Camera"
Basic.Main.StopRecording
=
"Stop Recording"
Basic.Main.PauseRecording
=
"Pause Recording"
Basic.Main.UnpauseRecording
=
"Unpause Recording"
...
...
@@ -529,6 +531,7 @@ Basic.Main.StoppingReplayBuffer="Stopping Replay Buffer..."
Basic.Main.StopStreaming
=
"Stop Streaming"
Basic.Main.StoppingStreaming
=
"Stopping Stream..."
Basic.Main.ForceStopStreaming
=
"Stop Streaming (discard delay)"
Basic.Main.StopVirtualCam
=
"Stop Virtual Camera"
Basic.Main.Group
=
"Group %1"
Basic.Main.GroupItems
=
"Group Selected Items"
Basic.Main.Ungroup
=
"Ungroup"
...
...
UI/installer/mp-installer.nsi
浏览文件 @
50ff29e5
...
...
@@ -373,6 +373,14 @@ Section -FinishSection
ClearErrors
WriteRegDWORD HKLM "Software\Khronos\Vulkan\ImplicitLayers" "$APPDATA\obs-studio-hook\obs-vulkan32.json" 0
# ---------------------------------------
# Register virtual camera dlls
Exec '"$SYSDIR\regsvr32.exe" /s "$INSTDIR\data\obs-plugins\win-dshow\obs-virtualcam-module32.dll"'
${if} ${RunningX64}
Exec '"$SYSDIR\regsvr32.exe" /s "$INSTDIR\data\obs-plugins\win-dshow\obs-virtualcam-module64.dll"'
${endif}
# ---------------------------------------
ClearErrors
...
...
@@ -420,6 +428,12 @@ Section "un.obs-studio Program Files" UninstallSection1
SetShellVarContext current
ClearErrors
; Unregister virtual camera dlls
Exec '"$SYSDIR\regsvr32.exe" /u /s "$INSTDIR\data\obs-plugins\win-dshow\obs-virtualcam-module32.dll"'
${if} ${RunningX64}
Exec '"$SYSDIR\regsvr32.exe" /u /s "$INSTDIR\data\obs-plugins\win-dshow\obs-virtualcam-module64.dll"'
${endif}
; Remove from registry...
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
DeleteRegKey HKLM "SOFTWARE\${APPNAME}"
...
...
UI/obs-app.cpp
浏览文件 @
50ff29e5
...
...
@@ -75,6 +75,7 @@ bool opt_start_streaming = false;
bool
opt_start_recording
=
false
;
bool
opt_studio_mode
=
false
;
bool
opt_start_replaybuffer
=
false
;
bool
opt_start_virtualcam
=
false
;
bool
opt_minimize_tray
=
false
;
bool
opt_allow_opengl
=
false
;
bool
opt_always_on_top
=
false
;
...
...
@@ -2425,6 +2426,9 @@ int main(int argc, char *argv[])
}
else
if
(
arg_is
(
argv
[
i
],
"--startreplaybuffer"
,
nullptr
))
{
opt_start_replaybuffer
=
true
;
}
else
if
(
arg_is
(
argv
[
i
],
"--startvirtualcam"
,
nullptr
))
{
opt_start_virtualcam
=
true
;
}
else
if
(
arg_is
(
argv
[
i
],
"--collection"
,
nullptr
))
{
if
(
++
i
<
argc
)
opt_starting_collection
=
argv
[
i
];
...
...
@@ -2451,7 +2455,8 @@ int main(int argc, char *argv[])
<<
"--help, -h: Get list of available commands.
\n\n
"
<<
"--startstreaming: Automatically start streaming.
\n
"
<<
"--startrecording: Automatically start recording.
\n
"
<<
"--startreplaybuffer: Start replay buffer.
\n\n
"
<<
"--startreplaybuffer: Start replay buffer.
\n
"
<<
"--startvirtualcam: Start virtual camera (if available).
\n\n
"
<<
"--collection <string>: Use specific scene collection."
<<
"
\n
"
<<
"--profile <string>: Use specific profile.
\n
"
...
...
UI/obs-app.hpp
浏览文件 @
50ff29e5
...
...
@@ -225,6 +225,7 @@ extern std::string remuxFilename;
extern
bool
opt_start_streaming
;
extern
bool
opt_start_recording
;
extern
bool
opt_start_replaybuffer
;
extern
bool
opt_start_virtualcam
;
extern
bool
opt_minimize_tray
;
extern
bool
opt_studio_mode
;
extern
bool
opt_allow_opengl
;
...
...
UI/win-update/updater/updater.cpp
浏览文件 @
50ff29e5
...
...
@@ -1424,6 +1424,40 @@ static bool Update(wchar_t *cmdLine)
}
}
/* ------------------------------------- *
* Install virtual camera */
if
(
!
bIsPortable
)
{
wchar_t
regsvr
[
MAX_PATH
];
wchar_t
src
[
MAX_PATH
];
wchar_t
tmp
[
MAX_PATH
];
wchar_t
tmp2
[
MAX_PATH
];
SHGetFolderPathW
(
nullptr
,
CSIDL_SYSTEM
,
nullptr
,
SHGFP_TYPE_CURRENT
,
regsvr
);
StringCbCat
(
regsvr
,
sizeof
(
regsvr
),
L"
\\
regsvr32.exe"
);
GetCurrentDirectoryW
(
_countof
(
src
),
src
);
StringCbCat
(
src
,
sizeof
(
src
),
L"
\\
data
\\
obs-plugins
\\
win-dshow
\\
"
);
StringCbCopy
(
tmp
,
sizeof
(
tmp
),
L"
\"\"
"
);
StringCbCat
(
tmp
,
sizeof
(
tmp
),
regsvr
);
StringCbCat
(
tmp
,
sizeof
(
tmp
),
L"
\"
/s
\"
"
);
StringCbCat
(
tmp
,
sizeof
(
tmp
),
src
);
StringCbCat
(
tmp
,
sizeof
(
tmp
),
L"obs-virtualcam-module"
);
StringCbCopy
(
tmp2
,
sizeof
(
tmp2
),
tmp
);
StringCbCat
(
tmp2
,
sizeof
(
tmp2
),
L"32.dll
\"\"
"
);
_wsystem
(
tmp2
);
if
(
is_64bit_windows
())
{
StringCbCopy
(
tmp2
,
sizeof
(
tmp2
),
tmp
);
StringCbCat
(
tmp2
,
sizeof
(
tmp2
),
L"64.dll
\"\"
"
);
_wsystem
(
tmp2
);
}
}
/* ------------------------------------- *
* Update hook files and vulkan registry */
...
...
UI/window-basic-auto-config-test.cpp
浏览文件 @
50ff29e5
...
...
@@ -990,7 +990,7 @@ void AutoConfigTestPage::FinalizeResults()
return
new
QLabel
(
QTStr
(
str
),
this
);
};
if
(
wiz
->
type
!=
AutoConfig
::
Type
::
Record
ing
)
{
if
(
wiz
->
type
==
AutoConfig
::
Type
::
Stream
ing
)
{
const
char
*
serverType
=
wiz
->
customServer
?
"rtmp_custom"
:
"rtmp_common"
;
...
...
@@ -1093,7 +1093,7 @@ void AutoConfigTestPage::NextStage()
started
=
true
;
}
if
(
wiz
->
type
==
AutoConfig
::
Type
::
Record
ing
)
{
if
(
wiz
->
type
!=
AutoConfig
::
Type
::
Stream
ing
)
{
stage
=
Stage
::
StreamEncoder
;
}
else
if
(
!
wiz
->
bandwidthTest
)
{
stage
=
Stage
::
BandwidthTest
;
...
...
@@ -1163,8 +1163,17 @@ AutoConfigTestPage::~AutoConfigTestPage()
void
AutoConfigTestPage
::
initializePage
()
{
if
(
wiz
->
type
==
AutoConfig
::
Type
::
VirtualCam
)
{
wiz
->
idealResolutionCX
=
wiz
->
baseResolutionCX
;
wiz
->
idealResolutionCY
=
wiz
->
baseResolutionCY
;
wiz
->
idealFPSNum
=
30
;
wiz
->
idealFPSDen
=
1
;
stage
=
Stage
::
Finished
;
}
else
{
stage
=
Stage
::
Starting
;
}
setSubTitle
(
QTStr
(
SUBTITLE_TESTING
));
stage
=
Stage
::
Starting
;
softwareTested
=
false
;
cancel
=
false
;
DeleteLayout
(
results
);
...
...
UI/window-basic-auto-config.cpp
浏览文件 @
50ff29e5
...
...
@@ -69,6 +69,18 @@ AutoConfigStartPage::AutoConfigStartPage(QWidget *parent)
ui
->
setupUi
(
this
);
setTitle
(
QTStr
(
"Basic.AutoConfig.StartPage"
));
setSubTitle
(
QTStr
(
"Basic.AutoConfig.StartPage.SubTitle"
));
OBSBasic
*
main
=
OBSBasic
::
Get
();
if
(
main
->
VCamEnabled
())
{
QRadioButton
*
prioritizeVCam
=
new
QRadioButton
(
QTStr
(
"Basic.AutoConfig.StartPage.PrioritizeVirtualCam"
),
this
);
QBoxLayout
*
box
=
reinterpret_cast
<
QBoxLayout
*>
(
layout
());
box
->
insertWidget
(
2
,
prioritizeVCam
);
connect
(
prioritizeVCam
,
&
QPushButton
::
clicked
,
this
,
&
AutoConfigStartPage
::
PrioritizeVCam
);
}
}
AutoConfigStartPage
::~
AutoConfigStartPage
()
...
...
@@ -78,7 +90,9 @@ AutoConfigStartPage::~AutoConfigStartPage()
int
AutoConfigStartPage
::
nextId
()
const
{
return
AutoConfig
::
VideoPage
;
return
wiz
->
type
==
AutoConfig
::
Type
::
VirtualCam
?
AutoConfig
::
TestPage
:
AutoConfig
::
VideoPage
;
}
void
AutoConfigStartPage
::
on_prioritizeStreaming_clicked
()
...
...
@@ -91,6 +105,11 @@ void AutoConfigStartPage::on_prioritizeRecording_clicked()
wiz
->
type
=
AutoConfig
::
Type
::
Recording
;
}
void
AutoConfigStartPage
::
PrioritizeVCam
()
{
wiz
->
type
=
AutoConfig
::
Type
::
VirtualCam
;
}
/* ------------------------------------------------------------------------- */
#define RES_TEXT(x) "Basic.AutoConfig.VideoPage." x
...
...
UI/window-basic-auto-config.hpp
浏览文件 @
50ff29e5
...
...
@@ -33,6 +33,7 @@ class AutoConfig : public QWizard {
Invalid
,
Streaming
,
Recording
,
VirtualCam
,
};
enum
class
Service
{
...
...
@@ -139,6 +140,7 @@ public:
public
slots
:
void
on_prioritizeStreaming_clicked
();
void
on_prioritizeRecording_clicked
();
void
PrioritizeVCam
();
};
class
AutoConfigVideoPage
:
public
QWizardPage
{
...
...
UI/window-basic-main-outputs.cpp
浏览文件 @
50ff29e5
...
...
@@ -14,6 +14,7 @@ volatile bool streaming_active = false;
volatile
bool
recording_active
=
false
;
volatile
bool
recording_paused
=
false
;
volatile
bool
replaybuf_active
=
false
;
volatile
bool
virtualcam_active
=
false
;
#define RTMP_PROTOCOL "rtmp"
...
...
@@ -139,6 +140,30 @@ static void OBSReplayBufferStopping(void *data, calldata_t *params)
UNUSED_PARAMETER
(
params
);
}
static
void
OBSStartVirtualCam
(
void
*
data
,
calldata_t
*
params
)
{
BasicOutputHandler
*
output
=
static_cast
<
BasicOutputHandler
*>
(
data
);
output
->
virtualCamActive
=
true
;
os_atomic_set_bool
(
&
virtualcam_active
,
true
);
QMetaObject
::
invokeMethod
(
output
->
main
,
"OnVirtualCamStart"
);
UNUSED_PARAMETER
(
params
);
}
static
void
OBSStopVirtualCam
(
void
*
data
,
calldata_t
*
params
)
{
BasicOutputHandler
*
output
=
static_cast
<
BasicOutputHandler
*>
(
data
);
int
code
=
(
int
)
calldata_int
(
params
,
"code"
);
output
->
virtualCamActive
=
false
;
os_atomic_set_bool
(
&
virtualcam_active
,
false
);
QMetaObject
::
invokeMethod
(
output
->
main
,
"OnVirtualCamStop"
,
Q_ARG
(
int
,
code
));
UNUSED_PARAMETER
(
params
);
}
static
void
FindBestFilename
(
string
&
strPath
,
bool
noSpace
)
{
int
num
=
2
;
...
...
@@ -197,6 +222,49 @@ static bool CreateAACEncoder(OBSEncoder &res, string &id, int bitrate,
/* ------------------------------------------------------------------------ */
inline
BasicOutputHandler
::
BasicOutputHandler
(
OBSBasic
*
main_
)
:
main
(
main_
)
{
if
(
main
->
vcamEnabled
)
{
virtualCam
=
obs_output_create
(
"virtualcam_output"
,
"virtualcam_output"
,
nullptr
,
nullptr
);
obs_output_release
(
virtualCam
);
signal_handler_t
*
signal
=
obs_output_get_signal_handler
(
virtualCam
);
startVirtualCam
.
Connect
(
signal
,
"start"
,
OBSStartVirtualCam
,
this
);
stopVirtualCam
.
Connect
(
signal
,
"stop"
,
OBSStopVirtualCam
,
this
);
}
}
bool
BasicOutputHandler
::
StartVirtualCam
()
{
if
(
main
->
vcamEnabled
)
{
obs_output_set_media
(
virtualCam
,
obs_get_video
(),
obs_get_audio
());
return
obs_output_start
(
virtualCam
);
}
return
false
;
}
void
BasicOutputHandler
::
StopVirtualCam
()
{
if
(
main
->
vcamEnabled
)
{
obs_output_stop
(
virtualCam
);
}
}
bool
BasicOutputHandler
::
VirtualCamActive
()
const
{
if
(
main
->
vcamEnabled
)
{
return
obs_output_active
(
virtualCam
);
}
return
false
;
}
/* ------------------------------------------------------------------------ */
struct
SimpleOutput
:
BasicOutputHandler
{
OBSEncoder
aacStreaming
;
OBSEncoder
h264Streaming
;
...
...
UI/window-basic-main-outputs.hpp
浏览文件 @
50ff29e5
...
...
@@ -8,10 +8,12 @@ struct BasicOutputHandler {
OBSOutput
fileOutput
;
OBSOutput
streamOutput
;
OBSOutput
replayBuffer
;
OBSOutput
virtualCam
;
bool
streamingActive
=
false
;
bool
recordingActive
=
false
;
bool
delayActive
=
false
;
bool
replayBufferActive
=
false
;
bool
virtualCamActive
=
false
;
OBSBasic
*
main
;
std
::
string
outputType
;
...
...
@@ -23,31 +25,36 @@ struct BasicOutputHandler {
OBSSignal
stopReplayBuffer
;
OBSSignal
startStreaming
;
OBSSignal
stopStreaming
;
OBSSignal
startVirtualCam
;
OBSSignal
stopVirtualCam
;
OBSSignal
streamDelayStarting
;
OBSSignal
streamStopping
;
OBSSignal
recordStopping
;
OBSSignal
replayBufferStopping
;
inline
BasicOutputHandler
(
OBSBasic
*
main_
)
:
main
(
main_
)
{}
inline
BasicOutputHandler
(
OBSBasic
*
main_
)
;
virtual
~
BasicOutputHandler
(){};
virtual
bool
StartStreaming
(
obs_service_t
*
service
)
=
0
;
virtual
bool
StartRecording
()
=
0
;
virtual
bool
StartReplayBuffer
()
{
return
false
;
}
virtual
bool
StartVirtualCam
();
virtual
void
StopStreaming
(
bool
force
=
false
)
=
0
;
virtual
void
StopRecording
(
bool
force
=
false
)
=
0
;
virtual
void
StopReplayBuffer
(
bool
force
=
false
)
{
(
void
)
force
;
}
virtual
void
StopVirtualCam
();
virtual
bool
StreamingActive
()
const
=
0
;
virtual
bool
RecordingActive
()
const
=
0
;
virtual
bool
ReplayBufferActive
()
const
{
return
false
;
}
virtual
bool
VirtualCamActive
()
const
;
virtual
void
Update
()
=
0
;
inline
bool
Active
()
const
{
return
streamingActive
||
recordingActive
||
delayActive
||
replayBufferActive
;
replayBufferActive
||
virtualCamActive
;
}
};
...
...
UI/window-basic-main.cpp
浏览文件 @
50ff29e5
...
...
@@ -1080,6 +1080,12 @@ retryScene:
opt_start_replaybuffer
=
false
;
}
if
(
opt_start_virtualcam
)
{
QMetaObject
::
invokeMethod
(
this
,
"StartVirtualCam"
,
Qt
::
QueuedConnection
);
opt_start_virtualcam
=
false
;
}
copyStrings
.
clear
();
copyFiltersString
=
nullptr
;
...
...
@@ -1518,6 +1524,18 @@ void OBSBasic::ReplayBufferClicked()
StartReplayBuffer
();
};
void
OBSBasic
::
AddVCamButton
()
{
vcamButton
=
new
ReplayBufferButton
(
QTStr
(
"Basic.Main.StartVirtualCam"
),
this
);
vcamButton
->
setCheckable
(
true
);
connect
(
vcamButton
.
data
(),
&
QPushButton
::
clicked
,
this
,
&
OBSBasic
::
VCamButtonClicked
);
vcamButton
->
setProperty
(
"themeID"
,
"vcamButton"
);
ui
->
buttonsVLayout
->
insertWidget
(
2
,
vcamButton
);
}
void
OBSBasic
::
ResetOutputs
()
{
ProfileScope
(
"OBSBasic::ResetOutputs"
);
...
...
@@ -1645,6 +1663,13 @@ void OBSBasic::OBSInit()
cef
=
obs_browser_init_panel
();
#endif
obs_data_t
*
obsData
=
obs_get_private_data
();
vcamEnabled
=
obs_data_get_bool
(
obsData
,
"vcamEnabled"
);
if
(
vcamEnabled
)
{
AddVCamButton
();
}
obs_data_release
(
obsData
);
InitBasicConfigDefaults2
();
CheckForSimpleModeX264Fallback
();
...
...
@@ -2248,6 +2273,23 @@ void OBSBasic::CreateHotkeys()
LoadHotkeyPair
(
replayBufHotkeys
,
"OBSBasic.StartReplayBuffer"
,
"OBSBasic.StopReplayBuffer"
);
if
(
vcamEnabled
)
{
vcamHotkeys
=
obs_hotkey_pair_register_frontend
(
"OBSBasic.StartVirtualCam"
,
Str
(
"Basic.Main.StartVirtualCam"
),
"OBSBasic.StopVirtualCam"
,
Str
(
"Basic.Main.StopVirtualCam"
),
MAKE_CALLBACK
(
!
basic
.
outputHandler
->
VirtualCamActive
(),
basic
.
StartVirtualCam
,
"Starting virtual camera"
),
MAKE_CALLBACK
(
basic
.
outputHandler
->
VirtualCamActive
(),
basic
.
StopVirtualCam
,
"Stopping virtual camera"
),
this
,
this
);
LoadHotkeyPair
(
vcamHotkeys
,
"OBSBasic.StartVirtualCam"
,
"OBSBasic.StopVirtualCam"
);
}
togglePreviewHotkeys
=
obs_hotkey_pair_register_frontend
(
"OBSBasic.EnablePreview"
,
Str
(
"Basic.Main.PreviewConextMenu.Enable"
),
...
...
@@ -5247,6 +5289,10 @@ void OBSBasic::OpenSceneFilters()
"==== Streaming Start ==============================================="
#define STREAMING_STOP \
"==== Streaming Stop ================================================"
#define VIRTUAL_CAM_START \
"==== Virtual Camera Start =========================================="
#define VIRTUAL_CAM_STOP \
"==== Virtual Camera Stop ==========================================="
void
OBSBasic
::
StartStreaming
()
{
...
...
@@ -5967,6 +6013,61 @@ void OBSBasic::ReplayBufferStop(int code)
UpdateReplayBuffer
(
false
);
}
void
OBSBasic
::
StartVirtualCam
()
{
if
(
!
outputHandler
||
!
outputHandler
->
virtualCam
)
return
;
if
(
outputHandler
->
VirtualCamActive
())
return
;
if
(
disableOutputsRef
)
return
;
SaveProject
();
if
(
!
outputHandler
->
StartVirtualCam
())
{
vcamButton
->
setChecked
(
false
);
}
}
void
OBSBasic
::
StopVirtualCam
()
{
if
(
!
outputHandler
||
!
outputHandler
->
virtualCam
)
return
;
SaveProject
();
if
(
outputHandler
->
VirtualCamActive
())
outputHandler
->
StopVirtualCam
();
OnDeactivate
();
}
void
OBSBasic
::
OnVirtualCamStart
()
{
if
(
!
outputHandler
||
!
outputHandler
->
virtualCam
)
return
;
vcamButton
->
setText
(
QTStr
(
"Basic.Main.StopVirtualCam"
));
vcamButton
->
setChecked
(
true
);
OnActivate
();
blog
(
LOG_INFO
,
VIRTUAL_CAM_START
);
}
void
OBSBasic
::
OnVirtualCamStop
(
int
)
{
if
(
!
outputHandler
||
!
outputHandler
->
virtualCam
)
return
;
vcamButton
->
setText
(
QTStr
(
"Basic.Main.StartVirtualCam"
));
vcamButton
->
setChecked
(
false
);
blog
(
LOG_INFO
,
VIRTUAL_CAM_STOP
);
OnDeactivate
();
}
void
OBSBasic
::
on_streamButton_clicked
()
{
if
(
outputHandler
->
StreamingActive
())
{
...
...
@@ -6073,6 +6174,20 @@ void OBSBasic::on_recordButton_clicked()
}
}
void
OBSBasic
::
VCamButtonClicked
()
{
if
(
outputHandler
->
VirtualCamActive
())
{
StopVirtualCam
();
}
else
{
if
(
!
UIValidation
::
NoSourcesConfirmation
(
this
))
{
vcamButton
->
setChecked
(
false
);
return
;
}
StartVirtualCam
();
}
}
void
OBSBasic
::
on_settingsButton_clicked
()
{
on_action_Settings_triggered
();
...
...
UI/window-basic-main.hpp
浏览文件 @
50ff29e5
...
...
@@ -166,6 +166,7 @@ class OBSBasic : public OBSMainWindow {
friend
class
ReplayBufferButton
;
friend
class
ExtraBrowsersModel
;
friend
class
ExtraBrowsersDelegate
;
friend
struct
BasicOutputHandler
;
friend
struct
OBSStudioAPI
;
enum
class
MoveDir
{
Up
,
Down
,
Left
,
Right
};
...
...
@@ -256,6 +257,9 @@ private:
QScopedPointer
<
QPushButton
>
pause
;
QScopedPointer
<
QPushButton
>
replay
;
QPointer
<
QPushButton
>
vcamButton
;
bool
vcamEnabled
=
false
;
QScopedPointer
<
QSystemTrayIcon
>
trayIcon
;
QPointer
<
QAction
>
sysTrayStream
;
QPointer
<
QAction
>
sysTrayRecord
;
...
...
@@ -382,7 +386,7 @@ private:
QModelIndexList
GetAllSelectedSourceItems
();
obs_hotkey_pair_id
streamingHotkeys
,
recordingHotkeys
,
pauseHotkeys
,
replayBufHotkeys
,
togglePreviewHotkeys
;
replayBufHotkeys
,
vcamHotkeys
,
togglePreviewHotkeys
;
obs_hotkey_id
forceStreamingStopHotkey
;
void
InitDefaultTransitions
();
...
...
@@ -561,6 +565,12 @@ public slots:
void
ReplayBufferStopping
();
void
ReplayBufferStop
(
int
code
);
void
StartVirtualCam
();
void
StopVirtualCam
();
void
OnVirtualCamStart
();
void
OnVirtualCamStop
(
int
code
);
void
SaveProjectDeferred
();
void
SaveProject
();
...
...
@@ -740,6 +750,8 @@ public:
return
os_atomic_load_bool
(
&
previewProgramMode
);
}
inline
bool
VCamEnabled
()
const
{
return
vcamEnabled
;
}
bool
StreamingActive
()
const
;
bool
Active
()
const
;
...
...
@@ -747,6 +759,7 @@ public:
int
ResetVideo
();
bool
ResetAudio
();
void
AddVCamButton
();
void
ResetOutputs
();
void
ResetAudioDevice
(
const
char
*
sourceId
,
const
char
*
deviceId
,
...
...
@@ -887,6 +900,7 @@ private slots:
void
on_streamButton_clicked
();
void
on_recordButton_clicked
();
void
VCamButtonClicked
();
void
on_settingsButton_clicked
();
void
on_actionHelpPortal_triggered
();
...
...
plugins/win-dshow/CMakeLists.txt
浏览文件 @
50ff29e5
...
...
@@ -21,6 +21,14 @@ set(win-dshow_SOURCES
ffmpeg-decode.c
win-dshow.rc
)
set
(
virtualcam-output_SOURCES
tiny-nv12-scale.c
shared-memory-queue.c
virtualcam.c
)
set
(
virtualcam-output_HEADERS
tiny-nv12-scale.h
shared-memory-queue.h
)
set
(
libdshowcapture_SOURCES
libdshowcapture/source/capture-filter.cpp
libdshowcapture/source/output-filter.cpp
...
...
@@ -54,6 +62,8 @@ set(libdshowcapture_HEADERS
add_library
(
win-dshow MODULE
${
win-dshow_SOURCES
}
${
win-dshow_HEADERS
}
${
virtualcam-output_SOURCES
}
${
virtualcam-output_HEADERS
}
${
libdshowcapture_SOURCES
}
${
libdshowcapture_HEADERS
}
)
target_link_libraries
(
win-dshow
...
...
@@ -61,7 +71,13 @@ target_link_libraries(win-dshow
strmiids
ksuser
wmcodecdspuuid
w32-pthreads
${
FFMPEG_LIBRARIES
}
)
set_target_properties
(
win-dshow PROPERTIES FOLDER
"plugins"
)
set_target_properties
(
win-dshow PROPERTIES FOLDER
"plugins/win-dshow"
)
source_group
(
"libdshowcapture
\\
Source Files"
FILES
${
libdshowcapture_SOURCES
}
)
source_group
(
"libdshowcapture
\\
Header Files"
FILES
${
libdshowcapture_HEADERS
}
)
install_obs_plugin_with_data
(
win-dshow data
)
add_subdirectory
(
virtualcam-module
)
plugins/win-dshow/data/placeholder.png
0 → 100644
浏览文件 @
50ff29e5
451.3 KB
plugins/win-dshow/dshow-plugin.cpp
浏览文件 @
50ff29e5
#include <obs-module.h>
#include <strsafe.h>
#include <strmif.h>
#include "virtualcam-guid.h"
OBS_DECLARE_MODULE
()
OBS_MODULE_USE_DEFAULT_LOCALE
(
"win-dshow"
,
"en-US"
)
...
...
@@ -10,9 +13,41 @@ MODULE_EXPORT const char *obs_module_description(void)
extern
void
RegisterDShowSource
();
extern
void
RegisterDShowEncoders
();
extern
"C"
struct
obs_output_info
virtualcam_info
;
static
bool
vcam_installed
(
bool
b64
)
{
wchar_t
cls_str
[
CHARS_IN_GUID
];
wchar_t
temp
[
MAX_PATH
];
HKEY
key
=
nullptr
;
StringFromGUID2
(
CLSID_OBS_VirtualVideo
,
cls_str
,
CHARS_IN_GUID
);
StringCbPrintf
(
temp
,
sizeof
(
temp
),
L"CLSID
\\
%s"
,
cls_str
);
DWORD
flags
=
KEY_READ
;
flags
|=
b64
?
KEY_WOW64_64KEY
:
KEY_WOW64_32KEY
;
LSTATUS
status
=
RegOpenKeyExW
(
HKEY_CLASSES_ROOT
,
temp
,
0
,
flags
,
&
key
);
if
(
status
!=
ERROR_SUCCESS
)
{
return
false
;
}
RegCloseKey
(
key
);
return
true
;
}
bool
obs_module_load
(
void
)
{
RegisterDShowSource
();
RegisterDShowEncoders
();
obs_register_output
(
&
virtualcam_info
);
if
(
vcam_installed
(
false
))
{
obs_data_t
*
obs_settings
=
obs_data_create
();
obs_data_set_bool
(
obs_settings
,
"vcamEnabled"
,
true
);
obs_apply_private_data
(
obs_settings
);
obs_data_release
(
obs_settings
);
}
return
true
;
}
libdshowcapture
@
7a495a9d
比较
03fbb381
...
7a495a9d
Subproject commit
03fbb3814b2ad678f5be4b23b8ca05fb4172e12e
Subproject commit
7a495a9d3844152e3c43d14855a386d044ecd411
plugins/win-dshow/shared-memory-queue.c
0 → 100644
浏览文件 @
50ff29e5
#include <windows.h>
#include "shared-memory-queue.h"
#include "tiny-nv12-scale.h"
#define VIDEO_NAME L"OBSVirtualCamVideo"
enum
queue_type
{
SHARED_QUEUE_TYPE_VIDEO
,
};
struct
queue_header
{
volatile
uint32_t
write_idx
;
volatile
uint32_t
read_idx
;
volatile
uint32_t
state
;
uint32_t
offsets
[
3
];
uint32_t
type
;
uint32_t
cx
;
uint32_t
cy
;
uint64_t
interval
;
uint32_t
reserved
[
8
];
};
struct
video_queue
{
HANDLE
handle
;
bool
ready_to_read
;
struct
queue_header
*
header
;
uint64_t
*
ts
[
3
];
uint8_t
*
frame
[
3
];
long
last_inc
;
int
dup_counter
;
bool
is_writer
;
};
#define ALIGN_SIZE(size, align) size = (((size) + (align - 1)) & (~(align - 1)))
#define FRAME_HEADER_SIZE 32
video_queue_t
*
video_queue_create
(
uint32_t
cx
,
uint32_t
cy
,
uint64_t
interval
)
{
struct
video_queue
vq
=
{
0
};
struct
video_queue
*
pvq
;
DWORD
frame_size
=
cx
*
cy
*
3
/
2
;
uint32_t
offset_frame
[
3
];
DWORD
size
;
size
=
sizeof
(
struct
queue_header
);
ALIGN_SIZE
(
size
,
32
);
offset_frame
[
0
]
=
size
;
size
+=
frame_size
+
FRAME_HEADER_SIZE
;
ALIGN_SIZE
(
size
,
32
);
offset_frame
[
1
]
=
size
;
size
+=
frame_size
+
FRAME_HEADER_SIZE
;
ALIGN_SIZE
(
size
,
32
);
offset_frame
[
2
]
=
size
;
size
+=
frame_size
+
FRAME_HEADER_SIZE
;
ALIGN_SIZE
(
size
,
32
);
struct
queue_header
header
=
{
0
};
header
.
state
=
SHARED_QUEUE_STATE_STARTING
;
header
.
cx
=
cx
;
header
.
cy
=
cy
;
header
.
interval
=
interval
;
vq
.
is_writer
=
true
;
for
(
size_t
i
=
0
;
i
<
3
;
i
++
)
{
uint32_t
off
=
offset_frame
[
i
];
header
.
offsets
[
i
]
=
off
;
}
/* fail if already in use */
vq
.
handle
=
OpenFileMappingW
(
FILE_MAP_READ
,
false
,
VIDEO_NAME
);
if
(
vq
.
handle
)
{
CloseHandle
(
vq
.
handle
);
return
NULL
;
}
vq
.
handle
=
CreateFileMappingW
(
INVALID_HANDLE_VALUE
,
NULL
,
PAGE_READWRITE
,
0
,
size
,
VIDEO_NAME
);
if
(
!
vq
.
handle
)
{
return
NULL
;
}
vq
.
header
=
(
struct
queue_header
*
)
MapViewOfFile
(
vq
.
handle
,
FILE_MAP_ALL_ACCESS
,
0
,
0
,
0
);
memcpy
(
vq
.
header
,
&
header
,
sizeof
(
header
));
for
(
size_t
i
=
0
;
i
<
3
;
i
++
)
{
uint32_t
off
=
offset_frame
[
i
];
vq
.
ts
[
i
]
=
(
uint64_t
*
)(((
uint8_t
*
)
vq
.
header
)
+
off
);
vq
.
frame
[
i
]
=
((
uint8_t
*
)
vq
.
header
)
+
off
+
FRAME_HEADER_SIZE
;
}
pvq
=
malloc
(
sizeof
(
vq
));
memcpy
(
pvq
,
&
vq
,
sizeof
(
vq
));
return
pvq
;
}
video_queue_t
*
video_queue_open
()
{
struct
video_queue
vq
=
{
0
};
vq
.
handle
=
OpenFileMappingW
(
FILE_MAP_READ
,
false
,
VIDEO_NAME
);
if
(
!
vq
.
handle
)
{
return
NULL
;
}
vq
.
header
=
(
struct
queue_header
*
)
MapViewOfFile
(
vq
.
handle
,
FILE_MAP_READ
,
0
,
0
,
0
);
struct
video_queue
*
pvq
=
malloc
(
sizeof
(
vq
));
memcpy
(
pvq
,
&
vq
,
sizeof
(
vq
));
return
pvq
;
}
void
video_queue_close
(
video_queue_t
*
vq
)
{
if
(
!
vq
)
{
return
;
}
if
(
vq
->
is_writer
)
{
vq
->
header
->
state
=
SHARED_QUEUE_STATE_STOPPING
;
}
UnmapViewOfFile
(
vq
->
header
);
CloseHandle
(
vq
->
handle
);
free
(
vq
);
}
void
video_queue_get_info
(
video_queue_t
*
vq
,
uint32_t
*
cx
,
uint32_t
*
cy
,
uint64_t
*
interval
)
{
struct
queue_header
*
qh
=
vq
->
header
;
*
cx
=
qh
->
cx
;
*
cy
=
qh
->
cy
;
*
interval
=
qh
->
interval
;
}
#define get_idx(inc) ((unsigned long)inc % 3)
void
video_queue_write
(
video_queue_t
*
vq
,
uint8_t
**
data
,
uint32_t
*
linesize
,
uint64_t
timestamp
)
{
struct
queue_header
*
qh
=
vq
->
header
;
long
inc
=
++
qh
->
write_idx
;
unsigned
long
idx
=
get_idx
(
inc
);
size_t
size
=
linesize
[
0
]
*
qh
->
cy
;
*
vq
->
ts
[
idx
]
=
timestamp
;
memcpy
(
vq
->
frame
[
idx
],
data
[
0
],
size
);
memcpy
(
vq
->
frame
[
idx
]
+
size
,
data
[
1
],
size
/
2
);
qh
->
read_idx
=
inc
;
qh
->
state
=
SHARED_QUEUE_STATE_READY
;
}
enum
queue_state
video_queue_state
(
video_queue_t
*
vq
)
{
if
(
!
vq
)
{
return
SHARED_QUEUE_STATE_INVALID
;
}
enum
queue_state
state
=
(
enum
queue_state
)
vq
->
header
->
state
;
if
(
!
vq
->
ready_to_read
&&
state
==
SHARED_QUEUE_STATE_READY
)
{
for
(
size_t
i
=
0
;
i
<
3
;
i
++
)
{
size_t
off
=
vq
->
header
->
offsets
[
i
];
vq
->
ts
[
i
]
=
(
uint64_t
*
)(((
uint8_t
*
)
vq
->
header
)
+
off
);
vq
->
frame
[
i
]
=
((
uint8_t
*
)
vq
->
header
)
+
off
+
FRAME_HEADER_SIZE
;
}
vq
->
ready_to_read
=
true
;
}
return
state
;
}
bool
video_queue_read
(
video_queue_t
*
vq
,
nv12_scale_t
*
scale
,
void
*
dst
,
uint64_t
*
ts
)
{
struct
queue_header
*
qh
=
vq
->
header
;
long
inc
=
qh
->
read_idx
;
if
(
qh
->
state
==
SHARED_QUEUE_STATE_STOPPING
)
{
return
false
;
}
if
(
inc
==
vq
->
last_inc
)
{
if
(
++
vq
->
dup_counter
==
10
)
{
return
false
;
}
}
else
{
vq
->
dup_counter
=
0
;
vq
->
last_inc
=
inc
;
}
unsigned
long
idx
=
get_idx
(
inc
);
*
ts
=
*
vq
->
ts
[
idx
];
nv12_do_scale
(
scale
,
dst
,
vq
->
frame
[
idx
]);
return
true
;
}
plugins/win-dshow/shared-memory-queue.h
0 → 100644
浏览文件 @
50ff29e5
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern
"C"
{
#endif
struct
video_queue
;
struct
nv12_scale
;
typedef
struct
video_queue
video_queue_t
;
typedef
struct
nv12_scale
nv12_scale_t
;
enum
queue_state
{
SHARED_QUEUE_STATE_INVALID
,
SHARED_QUEUE_STATE_STARTING
,
SHARED_QUEUE_STATE_READY
,
SHARED_QUEUE_STATE_STOPPING
,
};
extern
video_queue_t
*
video_queue_create
(
uint32_t
cx
,
uint32_t
cy
,
uint64_t
interval
);
extern
video_queue_t
*
video_queue_open
();
extern
void
video_queue_close
(
video_queue_t
*
vq
);
extern
void
video_queue_get_info
(
video_queue_t
*
vq
,
uint32_t
*
cx
,
uint32_t
*
cy
,
uint64_t
*
interval
);
extern
void
video_queue_write
(
video_queue_t
*
vq
,
uint8_t
**
data
,
uint32_t
*
linesize
,
uint64_t
timestamp
);
extern
enum
queue_state
video_queue_state
(
video_queue_t
*
vq
);
extern
bool
video_queue_read
(
video_queue_t
*
vq
,
nv12_scale_t
*
scale
,
void
*
dst
,
uint64_t
*
ts
);
#ifdef __cplusplus
}
#endif
plugins/win-dshow/tiny-nv12-scale.c
0 → 100644
浏览文件 @
50ff29e5
#include <string.h>
#include "tiny-nv12-scale.h"
/* TODO: optimize this stuff later, or replace with something better. it's
* kind of garbage. although normally it shouldn't be called that often. plus
* it's nearest neighbor so not really a huge deal. at the very least it
* should be sse2 at some point. */
void
nv12_scale_init
(
nv12_scale_t
*
s
,
bool
convert_to_i420
,
int
dst_cx
,
int
dst_cy
,
int
src_cx
,
int
src_cy
)
{
s
->
convert_to_i420
=
convert_to_i420
;
s
->
src_cx
=
src_cx
;
s
->
src_cy
=
src_cy
;
s
->
dst_cx
=
dst_cx
;
s
->
dst_cy
=
dst_cy
;
}
static
void
nv12_scale_nearest
(
nv12_scale_t
*
s
,
uint8_t
*
dst_start
,
const
uint8_t
*
src
)
{
register
uint8_t
*
dst
=
dst_start
;
const
int
src_cx
=
s
->
src_cx
;
const
int
src_cy
=
s
->
src_cy
;
const
int
dst_cx
=
s
->
dst_cx
;
const
int
dst_cy
=
s
->
dst_cy
;
/* lum */
for
(
int
y
=
0
;
y
<
dst_cy
;
y
++
)
{
const
int
src_line
=
y
*
src_cy
/
dst_cy
*
s
->
src_cx
;
for
(
int
x
=
0
;
x
<
dst_cx
;
x
++
)
{
const
int
src_x
=
x
*
src_cx
/
dst_cx
;
*
(
dst
++
)
=
src
[
src_line
+
src_x
];
}
}
src
+=
src_cx
*
src_cy
;
/* uv */
const
int
dst_cx_d2
=
dst_cx
/
2
;
const
int
dst_cy_d2
=
dst_cy
/
2
;
for
(
int
y
=
0
;
y
<
dst_cy_d2
;
y
++
)
{
const
int
src_line
=
y
*
src_cy
/
dst_cy
*
src_cx
;
for
(
int
x
=
0
;
x
<
dst_cx_d2
;
x
++
)
{
const
int
src_x
=
x
*
src_cx
/
dst_cx
*
2
;
const
int
pos
=
src_line
+
src_x
;
*
(
dst
++
)
=
src
[
pos
];
*
(
dst
++
)
=
src
[
pos
+
1
];
}
}
}
static
void
nv12_scale_nearest_to_i420
(
nv12_scale_t
*
s
,
uint8_t
*
dst_start
,
const
uint8_t
*
src
)
{
register
uint8_t
*
dst
=
dst_start
;
const
int
src_cx
=
s
->
src_cx
;
const
int
src_cy
=
s
->
src_cy
;
const
int
dst_cx
=
s
->
dst_cx
;
const
int
dst_cy
=
s
->
dst_cy
;
const
int
size
=
src_cx
*
src_cy
;
/* lum */
for
(
int
y
=
0
;
y
<
dst_cy
;
y
++
)
{
const
int
src_line
=
y
*
src_cy
/
dst_cy
*
s
->
src_cx
;
for
(
int
x
=
0
;
x
<
dst_cx
;
x
++
)
{
const
int
src_x
=
x
*
src_cx
/
dst_cx
;
*
(
dst
++
)
=
src
[
src_line
+
src_x
];
}
}
src
+=
size
;
/* uv */
const
int
dst_cx_d2
=
dst_cx
/
2
;
const
int
dst_cy_d2
=
dst_cy
/
2
;
register
uint8_t
*
dst2
=
dst
+
dst_cx
*
dst_cy
/
4
;
for
(
int
y
=
0
;
y
<
dst_cy_d2
;
y
++
)
{
const
int
src_line
=
y
*
src_cy
/
dst_cy
*
src_cx
;
for
(
int
x
=
0
;
x
<
dst_cx_d2
;
x
++
)
{
const
int
src_x
=
x
*
src_cx
/
dst_cx
*
2
;
const
int
pos
=
src_line
+
src_x
;
*
(
dst
++
)
=
src
[
pos
];
*
(
dst2
++
)
=
src
[
pos
+
1
];
}
}
}
static
void
nv12_convert_to_i420
(
nv12_scale_t
*
s
,
uint8_t
*
dst_start
,
const
uint8_t
*
src_start
)
{
const
int
size
=
s
->
src_cx
*
s
->
src_cy
;
const
int
size_d4
=
size
/
4
;
memcpy
(
dst_start
,
src_start
,
size
);
register
uint8_t
*
dst1
=
dst_start
+
size
;
register
uint8_t
*
dst2
=
dst1
+
size_d4
;
register
uint8_t
*
dst_end
=
dst2
+
size_d4
;
register
const
uint8_t
*
src
=
src_start
+
size
;
while
(
dst2
<
dst_end
)
{
*
(
dst1
++
)
=
*
(
src
++
);
*
(
dst2
++
)
=
*
(
src
++
);
}
}
void
nv12_do_scale
(
nv12_scale_t
*
s
,
uint8_t
*
dst
,
const
uint8_t
*
src
)
{
if
(
s
->
src_cx
==
s
->
dst_cx
&&
s
->
src_cy
==
s
->
dst_cy
)
{
if
(
s
->
convert_to_i420
)
nv12_convert_to_i420
(
s
,
dst
,
src
);
else
memcpy
(
dst
,
src
,
s
->
src_cx
*
s
->
src_cy
*
3
/
2
);
}
else
{
if
(
s
->
convert_to_i420
)
nv12_scale_nearest_to_i420
(
s
,
dst
,
src
);
else
nv12_scale_nearest
(
s
,
dst
,
src
);
}
}
plugins/win-dshow/tiny-nv12-scale.h
0 → 100644
浏览文件 @
50ff29e5
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern
"C"
{
#endif
struct
nv12_scale
{
bool
convert_to_i420
;
int
src_cx
;
int
src_cy
;
int
dst_cx
;
int
dst_cy
;
};
typedef
struct
nv12_scale
nv12_scale_t
;
extern
void
nv12_scale_init
(
nv12_scale_t
*
s
,
bool
convert_to_i420
,
int
dst_cx
,
int
dst_cy
,
int
src_cx
,
int
src_cy
);
extern
void
nv12_do_scale
(
nv12_scale_t
*
s
,
uint8_t
*
dst
,
const
uint8_t
*
src
);
#ifdef __cplusplus
}
#endif
plugins/win-dshow/virtualcam-guid.h
0 → 100644
浏览文件 @
50ff29e5
#pragma once
#include <windows.h>
#include <initguid.h>
// {A3FCE0F5-3493-419F-958A-ABA1250EC20B}
DEFINE_GUID
(
CLSID_OBS_VirtualVideo
,
0xa3fce0f5
,
0x3493
,
0x419f
,
0x95
,
0x8a
,
0xab
,
0xa1
,
0x25
,
0xe
,
0xc2
,
0xb
);
plugins/win-dshow/virtualcam-module/CMakeLists.txt
0 → 100644
浏览文件 @
50ff29e5
cmake_minimum_required
(
VERSION 3.5
)
project
(
obs-virtualcam-module
)
if
(
CMAKE_SIZEOF_VOID_P EQUAL 8
)
set
(
_output_suffix
"64"
)
else
()
set
(
_output_suffix
"32"
)
endif
()
configure_file
(
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/virtualcam-module.def.in"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/virtualcam-module.def"
)
set
(
libdshowcapture_SOURCES
../libdshowcapture/source/log.cpp
../libdshowcapture/source/dshow-base.cpp
../libdshowcapture/source/dshow-enum.cpp
../libdshowcapture/source/dshow-formats.cpp
../libdshowcapture/source/dshow-media-type.cpp
../libdshowcapture/source/output-filter.cpp
)
set
(
libdshowcapture_HEADERS
../libdshowcapture/source/ComPtr.hpp
../libdshowcapture/source/CoTaskMemPtr.hpp
../libdshowcapture/source/log.hpp
../libdshowcapture/source/dshow-base.hpp
../libdshowcapture/source/dshow-enum.hpp
../libdshowcapture/source/dshow-formats.hpp
../libdshowcapture/source/dshow-media-type.hpp
../libdshowcapture/source/output-filter.hpp
../libdshowcapture/dshowcapture.hpp
)
set
(
obs-virtualcam-module_SOURCES
"
${
CMAKE_CURRENT_BINARY_DIR
}
/virtualcam-module.def"
sleepto.c
placeholder.cpp
virtualcam-module.cpp
virtualcam-filter.cpp
../shared-memory-queue.c
../tiny-nv12-scale.c
)
set
(
obs-virtualcam-module_HEADERS
sleepto.h
virtualcam-filter.hpp
../shared-memory-queue.h
../tiny-nv12-scale.h
)
if
(
MSVC
)
add_compile_options
(
"$<IF:$<CONFIG:Debug>,/MTd,/MT>"
)
endif
()
include_directories
(
${
CMAKE_SOURCE_DIR
}
/libobs/util
)
source_group
(
"libdshowcapture
\\
Source Files"
FILES
${
libdshowcapture_SOURCES
}
)
source_group
(
"libdshowcapture
\\
Header Files"
FILES
${
libdshowcapture_HEADERS
}
)
set
(
CMAKE_MODULE_LINKER_FLAGS
"
${
MAKE_MODULE_LINKER_FLAGS
}
/ignore:4104"
)
add_library
(
obs-virtualcam-module MODULE
${
libdshowcapture_SOURCES
}
${
libdshowcapture_HEADERS
}
${
obs-virtualcam-module_SOURCES
}
${
obs-virtualcam-module_HEADERS
}
)
target_link_libraries
(
obs-virtualcam-module
winmm
strmiids
gdiplus
)
set_target_properties
(
obs-virtualcam-module PROPERTIES FOLDER
"plugins/win-dshow"
)
set_target_properties
(
obs-virtualcam-module
PROPERTIES
OUTPUT_NAME
"obs-virtualcam-module
${
_output_suffix
}
"
)
install_obs_datatarget
(
obs-virtualcam-module
"obs-plugins/win-dshow"
)
plugins/win-dshow/virtualcam-module/placeholder.cpp
0 → 100644
浏览文件 @
50ff29e5
#include <windows.h>
#include <strsafe.h>
#include <gdiplus.h>
#include <stdint.h>
#include <vector>
using
namespace
Gdiplus
;
extern
HINSTANCE
dll_inst
;
static
std
::
vector
<
uint8_t
>
placeholder
;
/* XXX: optimize this later. or don't, it's only called once. */
static
void
convert_placeholder
(
const
uint8_t
*
rgb_in
,
int
width
,
int
height
)
{
size_t
size
=
width
*
height
*
3
;
size_t
linesize
=
width
*
3
;
std
::
vector
<
uint8_t
>
yuv_out
;
yuv_out
.
resize
(
size
);
const
uint8_t
*
in
=
rgb_in
;
const
uint8_t
*
end
=
in
+
size
;
uint8_t
*
out
=
&
yuv_out
[
0
];
while
(
in
<
end
)
{
const
int16_t
b
=
*
(
in
++
);
const
int16_t
g
=
*
(
in
++
);
const
int16_t
r
=
*
(
in
++
);
*
(
out
++
)
=
(
uint8_t
)(((
66
*
r
+
129
*
g
+
25
*
b
+
128
)
>>
8
)
+
16
);
*
(
out
++
)
=
(
uint8_t
)(((
-
38
*
r
-
74
*
g
+
112
*
b
+
128
)
>>
8
)
+
128
);
*
(
out
++
)
=
(
uint8_t
)(((
112
*
r
-
94
*
g
-
18
*
b
+
128
)
>>
8
)
+
128
);
}
placeholder
.
resize
(
width
*
height
*
3
/
2
);
in
=
&
yuv_out
[
0
];
end
=
in
+
size
;
out
=
&
placeholder
[
0
];
uint8_t
*
chroma
=
out
+
width
*
height
;
while
(
in
<
end
)
{
const
uint8_t
*
in2
=
in
+
linesize
;
const
uint8_t
*
end2
=
in2
;
uint8_t
*
out2
=
out
+
width
;
while
(
in
<
end2
)
{
int16_t
u
;
int16_t
v
;
*
(
out
++
)
=
*
(
in
++
);
u
=
*
(
in
++
);
v
=
*
(
in
++
);
*
(
out
++
)
=
*
(
in
++
);
u
+=
*
(
in
++
);
v
+=
*
(
in
++
);
*
(
out2
++
)
=
*
(
in2
++
);
u
+=
*
(
in2
++
);
v
+=
*
(
in2
++
);
*
(
out2
++
)
=
*
(
in2
++
);
u
+=
*
(
in2
++
);
v
+=
*
(
in2
++
);
*
(
chroma
++
)
=
(
uint8_t
)(
u
/
4
);
*
(
chroma
++
)
=
(
uint8_t
)(
v
/
4
);
}
in
=
in2
;
out
=
out2
;
}
}
static
bool
load_placeholder_internal
()
{
Status
s
;
wchar_t
file
[
MAX_PATH
];
if
(
!
GetModuleFileNameW
(
dll_inst
,
file
,
MAX_PATH
))
{
return
false
;
}
wchar_t
*
slash
=
wcsrchr
(
file
,
'\\'
);
if
(
!
slash
)
{
return
false
;
}
slash
[
1
]
=
0
;
StringCbCat
(
file
,
sizeof
(
file
),
L"placeholder.png"
);
Bitmap
bmp
(
file
);
if
(
bmp
.
GetLastStatus
()
!=
Status
::
Ok
)
{
return
false
;
}
BitmapData
bmd
=
{};
Rect
r
(
0
,
0
,
bmp
.
GetWidth
(),
bmp
.
GetHeight
());
s
=
bmp
.
LockBits
(
&
r
,
ImageLockModeRead
,
PixelFormat24bppRGB
,
&
bmd
);
if
(
s
!=
Status
::
Ok
)
{
return
false
;
}
convert_placeholder
((
const
uint8_t
*
)
bmd
.
Scan0
,
bmp
.
GetWidth
(),
bmp
.
GetHeight
());
bmp
.
UnlockBits
(
&
bmd
);
return
true
;
}
static
bool
load_placeholder
()
{
GdiplusStartupInput
si
;
ULONG_PTR
token
;
GdiplusStartup
(
&
token
,
&
si
,
nullptr
);
bool
success
=
load_placeholder_internal
();
GdiplusShutdown
(
token
);
return
success
;
}
const
uint8_t
*
get_placeholder
()
{
static
bool
failed
=
false
;
static
bool
initialized
=
false
;
if
(
initialized
)
{
return
placeholder
.
data
();
}
else
if
(
failed
)
{
return
nullptr
;
}
initialized
=
load_placeholder
();
failed
=
!
initialized
;
return
initialized
?
placeholder
.
data
()
:
nullptr
;
}
plugins/win-dshow/virtualcam-module/sleepto.c
0 → 100644
浏览文件 @
50ff29e5
#include <windows.h>
#include <stdbool.h>
#include "sleepto.h"
static
bool
have_clockfreq
=
false
;
static
LARGE_INTEGER
clock_freq
;
static
inline
uint64_t
get_clockfreq
(
void
)
{
if
(
!
have_clockfreq
)
{
QueryPerformanceFrequency
(
&
clock_freq
);
have_clockfreq
=
true
;
}
return
clock_freq
.
QuadPart
;
}
uint64_t
gettime_100ns
(
void
)
{
LARGE_INTEGER
current_time
;
double
time_val
;
QueryPerformanceCounter
(
&
current_time
);
time_val
=
(
double
)
current_time
.
QuadPart
;
time_val
*=
10000000
.
0
;
time_val
/=
(
double
)
get_clockfreq
();
return
(
uint64_t
)
time_val
;
}
bool
sleepto_100ns
(
uint64_t
time_target
)
{
uint64_t
t
=
gettime_100ns
();
uint32_t
milliseconds
;
if
(
t
>=
time_target
)
return
false
;
milliseconds
=
(
uint32_t
)((
time_target
-
t
)
/
10000
);
if
(
milliseconds
>
1
)
Sleep
(
milliseconds
-
1
);
for
(;;)
{
t
=
gettime_100ns
();
if
(
t
>=
time_target
)
return
true
;
Sleep
(
0
);
}
}
plugins/win-dshow/virtualcam-module/sleepto.h
0 → 100644
浏览文件 @
50ff29e5
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern
"C"
{
#endif
extern
uint64_t
gettime_100ns
(
void
);
extern
bool
sleepto_100ns
(
uint64_t
time_target
);
#ifdef __cplusplus
}
#endif
plugins/win-dshow/virtualcam-module/virtualcam-filter.cpp
0 → 100644
浏览文件 @
50ff29e5
#include "virtualcam-filter.hpp"
#include "sleepto.h"
#include <shlobj_core.h>
#include <strsafe.h>
#include <inttypes.h>
using
namespace
DShow
;
extern
const
uint8_t
*
get_placeholder
();
/* ========================================================================= */
VCamFilter
::
VCamFilter
()
:
OutputFilter
(
VideoFormat
::
NV12
,
DEFAULT_CX
,
DEFAULT_CY
,
DEFAULT_INTERVAL
)
{
thread_start
=
CreateEvent
(
nullptr
,
true
,
false
,
nullptr
);
thread_stop
=
CreateEvent
(
nullptr
,
true
,
false
,
nullptr
);
AddVideoFormat
(
VideoFormat
::
I420
,
DEFAULT_CX
,
DEFAULT_CY
,
DEFAULT_INTERVAL
);
/* ---------------------------------------- */
/* load placeholder image */
placeholder
=
get_placeholder
();
/* ---------------------------------------- */
/* detect if this filter is within obs */
wchar_t
file
[
MAX_PATH
];
if
(
!
GetModuleFileNameW
(
nullptr
,
file
,
MAX_PATH
))
{
file
[
0
]
=
0
;
}
#ifdef _WIN64
const
wchar_t
*
obs_process
=
L"obs64.exe"
;
#else
const
wchar_t
*
obs_process
=
L"obs32.exe"
;
#endif
in_obs
=
!!
wcsstr
(
file
,
obs_process
);
/* ---------------------------------------- */
/* add last/current obs res/interval */
uint32_t
new_cx
=
cx
;
uint32_t
new_cy
=
cy
;
uint64_t
new_interval
=
interval
;
vq
=
video_queue_open
();
if
(
vq
)
{
if
(
video_queue_state
(
vq
)
==
SHARED_QUEUE_STATE_READY
)
{
video_queue_get_info
(
vq
,
&
new_cx
,
&
new_cy
,
&
new_interval
);
}
/* don't keep it open until the filter actually starts */
video_queue_close
(
vq
);
vq
=
nullptr
;
}
else
{
wchar_t
res_file
[
MAX_PATH
];
SHGetFolderPathW
(
nullptr
,
CSIDL_APPDATA
,
nullptr
,
SHGFP_TYPE_CURRENT
,
res_file
);
StringCbCat
(
res_file
,
sizeof
(
res_file
),
L"
\\
obs-virtualcam.txt"
);
HANDLE
file
=
CreateFileW
(
res_file
,
GENERIC_READ
,
0
,
nullptr
,
OPEN_EXISTING
,
0
,
nullptr
);
if
(
file
)
{
char
res
[
128
];
DWORD
len
=
0
;
ReadFile
(
file
,
res
,
sizeof
(
res
),
&
len
,
nullptr
);
CloseHandle
(
file
);
res
[
len
]
=
0
;
int
vals
=
sscanf
(
res
,
"%"
PRIu32
"x%"
PRIu32
"x%"
PRIu64
,
&
new_cx
,
&
new_cy
,
&
new_interval
);
if
(
vals
!=
3
)
{
new_cx
=
cx
;
new_cy
=
cy
;
new_interval
=
interval
;
}
}
}
if
(
new_cx
!=
cx
||
new_cy
!=
cy
||
new_interval
!=
interval
)
{
AddVideoFormat
(
VideoFormat
::
NV12
,
new_cx
,
new_cy
,
new_interval
);
AddVideoFormat
(
VideoFormat
::
I420
,
new_cx
,
new_cy
,
new_interval
);
SetVideoFormat
(
VideoFormat
::
NV12
,
new_cx
,
new_cy
,
new_interval
);
cx
=
new_cx
;
cy
=
new_cy
;
interval
=
new_interval
;
}
nv12_scale_init
(
&
scaler
,
false
,
cx
,
cy
,
cx
,
cy
);
/* ---------------------------------------- */
th
=
std
::
thread
([
this
]
{
Thread
();
});
AddRef
();
}
VCamFilter
::~
VCamFilter
()
{
SetEvent
(
thread_stop
);
th
.
join
();
video_queue_close
(
vq
);
}
const
wchar_t
*
VCamFilter
::
FilterName
()
const
{
return
L"VCamFilter"
;
}
STDMETHODIMP
VCamFilter
::
Pause
()
{
HRESULT
hr
;
hr
=
OutputFilter
::
Pause
();
if
(
FAILED
(
hr
))
{
return
hr
;
}
SetEvent
(
thread_start
);
return
S_OK
;
}
inline
uint64_t
VCamFilter
::
GetTime
()
{
if
(
!!
clock
)
{
REFERENCE_TIME
rt
;
HRESULT
hr
=
clock
->
GetTime
(
&
rt
);
if
(
SUCCEEDED
(
hr
))
{
return
(
uint64_t
)
rt
;
}
}
return
gettime_100ns
();
}
void
VCamFilter
::
Thread
()
{
HANDLE
h
[
2
]
=
{
thread_start
,
thread_stop
};
DWORD
ret
=
WaitForMultipleObjects
(
2
,
h
,
false
,
INFINITE
);
if
(
ret
!=
WAIT_OBJECT_0
)
return
;
uint64_t
cur_time
=
gettime_100ns
();
uint64_t
filter_time
=
GetTime
();
cx
=
GetCX
();
cy
=
GetCY
();
interval
=
GetInterval
();
nv12_scale_init
(
&
scaler
,
false
,
GetCX
(),
GetCY
(),
cx
,
cy
);
while
(
!
stopped
())
{
Frame
(
filter_time
);
sleepto_100ns
(
cur_time
+=
interval
);
filter_time
+=
interval
;
}
}
void
VCamFilter
::
Frame
(
uint64_t
ts
)
{
uint32_t
new_cx
=
cx
;
uint32_t
new_cy
=
cy
;
uint64_t
new_interval
=
interval
;
if
(
!
vq
)
{
vq
=
video_queue_open
();
}
enum
queue_state
state
=
video_queue_state
(
vq
);
if
(
state
!=
prev_state
)
{
if
(
state
==
SHARED_QUEUE_STATE_READY
)
{
video_queue_get_info
(
vq
,
&
new_cx
,
&
new_cy
,
&
new_interval
);
}
else
if
(
state
==
SHARED_QUEUE_STATE_STOPPING
)
{
video_queue_close
(
vq
);
vq
=
nullptr
;
}
prev_state
=
state
;
}
if
(
state
!=
SHARED_QUEUE_STATE_READY
)
{
new_cx
=
DEFAULT_CX
;
new_cy
=
DEFAULT_CY
;
new_interval
=
DEFAULT_INTERVAL
;
}
if
(
new_cx
!=
cx
||
new_cy
!=
cy
||
new_interval
!=
interval
)
{
if
(
in_obs
)
{
SetVideoFormat
(
GetVideoFormat
(),
new_cx
,
new_cy
,
new_interval
);
}
nv12_scale_init
(
&
scaler
,
false
,
GetCX
(),
GetCY
(),
new_cx
,
new_cy
);
cx
=
new_cx
;
cy
=
new_cy
;
interval
=
new_interval
;
}
scaler
.
convert_to_i420
=
GetVideoFormat
()
==
VideoFormat
::
I420
;
uint8_t
*
ptr
;
if
(
LockSampleData
(
&
ptr
))
{
if
(
state
==
SHARED_QUEUE_STATE_READY
)
ShowOBSFrame
(
ptr
);
else
ShowDefaultFrame
(
ptr
);
UnlockSampleData
(
ts
,
ts
+
interval
);
}
}
void
VCamFilter
::
ShowOBSFrame
(
uint8_t
*
ptr
)
{
uint64_t
temp
;
if
(
!
video_queue_read
(
vq
,
&
scaler
,
ptr
,
&
temp
))
{
video_queue_close
(
vq
);
vq
=
nullptr
;
}
}
void
VCamFilter
::
ShowDefaultFrame
(
uint8_t
*
ptr
)
{
if
(
placeholder
)
{
nv12_do_scale
(
&
scaler
,
ptr
,
placeholder
);
}
else
{
memset
(
ptr
,
127
,
GetCX
()
*
GetCY
()
*
3
/
2
);
}
}
plugins/win-dshow/virtualcam-module/virtualcam-filter.hpp
0 → 100644
浏览文件 @
50ff29e5
#pragma once
#include <windows.h>
#include <cstdint>
#include <thread>
#include "../shared-memory-queue.h"
#include "../tiny-nv12-scale.h"
#include "../libdshowcapture/source/output-filter.hpp"
#include "../../../libobs/util/windows/WinHandle.hpp"
#define DEFAULT_CX 1920
#define DEFAULT_CY 1080
#define DEFAULT_INTERVAL 333333ULL
class
VCamFilter
:
public
DShow
::
OutputFilter
{
std
::
thread
th
;
video_queue_t
*
vq
=
nullptr
;
int
queue_mode
=
0
;
bool
in_obs
=
false
;
enum
queue_state
prev_state
=
SHARED_QUEUE_STATE_INVALID
;
const
uint8_t
*
placeholder
;
uint32_t
cx
=
DEFAULT_CX
;
uint32_t
cy
=
DEFAULT_CY
;
uint64_t
interval
=
DEFAULT_INTERVAL
;
WinHandle
thread_start
;
WinHandle
thread_stop
;
nv12_scale_t
scaler
=
{};
inline
bool
stopped
()
const
{
return
WaitForSingleObject
(
thread_stop
,
0
)
!=
WAIT_TIMEOUT
;
}
inline
uint64_t
GetTime
();
void
Thread
();
void
Frame
(
uint64_t
ts
);
void
ShowOBSFrame
(
uint8_t
*
ptr
);
void
ShowDefaultFrame
(
uint8_t
*
ptr
);
protected:
const
wchar_t
*
FilterName
()
const
override
;
public:
VCamFilter
();
~
VCamFilter
()
override
;
STDMETHODIMP
Pause
()
override
;
};
plugins/win-dshow/virtualcam-module/virtualcam-module.cpp
0 → 100644
浏览文件 @
50ff29e5
#include "virtualcam-filter.hpp"
#include "../virtualcam-guid.h"
/* ========================================================================= */
static
const
REGPINTYPES
AMSMediaTypesV
=
{
&
MEDIATYPE_Video
,
&
MEDIASUBTYPE_NV12
};
static
const
REGFILTERPINS
AMSPinVideo
=
{
L"Output"
,
false
,
true
,
false
,
false
,
&
CLSID_NULL
,
nullptr
,
1
,
&
AMSMediaTypesV
};
HINSTANCE
dll_inst
=
nullptr
;
static
volatile
long
locks
=
0
;
/* ========================================================================= */
class
VCamFactory
:
public
IClassFactory
{
volatile
long
refs
=
1
;
CLSID
cls
;
public:
inline
VCamFactory
(
CLSID
cls_
)
:
cls
(
cls_
)
{}
// IUnknown
STDMETHODIMP
QueryInterface
(
REFIID
riid
,
void
**
p_ptr
);
STDMETHODIMP_
(
ULONG
)
AddRef
();
STDMETHODIMP_
(
ULONG
)
Release
();
// IClassFactory
STDMETHODIMP
CreateInstance
(
LPUNKNOWN
parent
,
REFIID
riid
,
void
**
p_ptr
);
STDMETHODIMP
LockServer
(
BOOL
lock
);
};
STDMETHODIMP
VCamFactory
::
QueryInterface
(
REFIID
riid
,
void
**
p_ptr
)
{
if
(
!
p_ptr
)
{
return
E_POINTER
;
}
if
((
riid
==
IID_IUnknown
)
||
(
riid
==
IID_IClassFactory
))
{
AddRef
();
*
p_ptr
=
(
void
*
)
this
;
return
S_OK
;
}
else
{
*
p_ptr
=
nullptr
;
return
E_NOINTERFACE
;
}
}
STDMETHODIMP_
(
ULONG
)
VCamFactory
::
AddRef
()
{
return
InterlockedIncrement
(
&
refs
);
}
STDMETHODIMP_
(
ULONG
)
VCamFactory
::
Release
()
{
long
new_refs
=
InterlockedDecrement
(
&
refs
);
if
(
new_refs
==
0
)
{
delete
this
;
return
0
;
}
return
(
ULONG
)
new_refs
;
}
STDMETHODIMP
VCamFactory
::
CreateInstance
(
LPUNKNOWN
parent
,
REFIID
,
void
**
p_ptr
)
{
if
(
!
p_ptr
)
{
return
E_POINTER
;
}
*
p_ptr
=
nullptr
;
/* don't bother supporting the "parent" functionality */
if
(
parent
)
{
return
E_NOINTERFACE
;
}
if
(
IsEqualCLSID
(
cls
,
CLSID_OBS_VirtualVideo
))
{
*
p_ptr
=
(
void
*
)
new
VCamFilter
();
return
S_OK
;
}
return
E_NOINTERFACE
;
}
STDMETHODIMP
VCamFactory
::
LockServer
(
BOOL
lock
)
{
if
(
lock
)
{
InterlockedIncrement
(
&
locks
);
}
else
{
InterlockedDecrement
(
&
locks
);
}
return
S_OK
;
}
/* ========================================================================= */
static
inline
DWORD
string_size
(
const
wchar_t
*
str
)
{
return
(
DWORD
)(
wcslen
(
str
)
+
1
)
*
sizeof
(
wchar_t
);
}
static
bool
RegServer
(
CLSID
cls
,
const
wchar_t
*
desc
,
const
wchar_t
*
file
,
const
wchar_t
*
model
=
L"Both"
,
const
wchar_t
*
type
=
L"InprocServer32"
)
{
wchar_t
cls_str
[
CHARS_IN_GUID
];
wchar_t
temp
[
MAX_PATH
];
HKEY
key
=
nullptr
;
HKEY
subkey
=
nullptr
;
bool
success
=
false
;
StringFromGUID2
(
cls
,
cls_str
,
CHARS_IN_GUID
);
StringCbPrintf
(
temp
,
sizeof
(
temp
),
L"CLSID
\\
%s"
,
cls_str
);
if
(
RegCreateKey
(
HKEY_CLASSES_ROOT
,
temp
,
&
key
)
!=
ERROR_SUCCESS
)
{
goto
fail
;
}
RegSetValueW
(
key
,
nullptr
,
REG_SZ
,
desc
,
string_size
(
desc
));
if
(
RegCreateKey
(
key
,
type
,
&
subkey
)
!=
ERROR_SUCCESS
)
{
goto
fail
;
}
RegSetValueW
(
subkey
,
nullptr
,
REG_SZ
,
file
,
string_size
(
file
));
RegSetValueExW
(
subkey
,
L"ThreadingModel"
,
0
,
REG_SZ
,
(
const
BYTE
*
)
model
,
string_size
(
model
));
success
=
true
;
fail:
if
(
key
)
{
RegCloseKey
(
key
);
}
if
(
key
)
{
RegCloseKey
(
subkey
);
}
return
success
;
}
static
bool
UnregServer
(
CLSID
cls
)
{
wchar_t
cls_str
[
CHARS_IN_GUID
];
wchar_t
temp
[
MAX_PATH
];
StringFromGUID2
(
cls
,
cls_str
,
CHARS_IN_GUID
);
StringCbPrintf
(
temp
,
sizeof
(
temp
),
L"CLSID
\\
%s"
,
cls_str
);
return
RegDeleteTreeW
(
HKEY_CLASSES_ROOT
,
temp
)
==
ERROR_SUCCESS
;
}
static
bool
RegServers
(
bool
reg
)
{
wchar_t
file
[
MAX_PATH
];
if
(
!
GetModuleFileNameW
(
dll_inst
,
file
,
MAX_PATH
))
{
return
false
;
}
if
(
reg
)
{
return
RegServer
(
CLSID_OBS_VirtualVideo
,
L"OBS Virtual Camera"
,
file
);
}
else
{
return
UnregServer
(
CLSID_OBS_VirtualVideo
);
}
}
static
bool
RegFilters
(
bool
reg
)
{
ComPtr
<
IFilterMapper2
>
fm
;
HRESULT
hr
;
hr
=
CoCreateInstance
(
CLSID_FilterMapper2
,
nullptr
,
CLSCTX_INPROC_SERVER
,
IID_IFilterMapper2
,
(
void
**
)
&
fm
);
if
(
FAILED
(
hr
))
{
return
false
;
}
if
(
reg
)
{
ComPtr
<
IMoniker
>
moniker
;
REGFILTER2
rf2
;
rf2
.
dwVersion
=
1
;
rf2
.
dwMerit
=
MERIT_DO_NOT_USE
;
rf2
.
cPins
=
1
;
rf2
.
rgPins
=
&
AMSPinVideo
;
hr
=
fm
->
RegisterFilter
(
CLSID_OBS_VirtualVideo
,
L"OBS Video Output"
,
&
moniker
,
&
CLSID_VideoInputDeviceCategory
,
nullptr
,
&
rf2
);
if
(
FAILED
(
hr
))
{
return
false
;
}
}
else
{
hr
=
fm
->
UnregisterFilter
(
&
CLSID_VideoInputDeviceCategory
,
0
,
CLSID_OBS_VirtualVideo
);
if
(
FAILED
(
hr
))
{
return
false
;
}
}
return
true
;
}
/* ========================================================================= */
STDAPI
DllRegisterServer
()
{
if
(
!
RegServers
(
true
))
{
RegServers
(
false
);
return
E_FAIL
;
}
CoInitialize
(
0
);
if
(
!
RegFilters
(
true
))
{
RegFilters
(
false
);
RegServers
(
false
);
CoUninitialize
();
return
E_FAIL
;
}
CoUninitialize
();
return
S_OK
;
}
STDAPI
DllUnregisterServer
()
{
CoInitialize
(
0
);
RegFilters
(
false
);
RegServers
(
false
);
CoUninitialize
();
return
S_OK
;
}
STDAPI
DllInstall
(
BOOL
install
,
LPCWSTR
)
{
if
(
!
install
)
{
return
DllUnregisterServer
();
}
else
{
return
DllRegisterServer
();
}
}
STDAPI
DllCanUnloadNow
()
{
return
InterlockedOr
(
&
locks
,
0
)
==
0
?
S_OK
:
S_FALSE
;
}
STDAPI
DllGetClassObject
(
REFCLSID
cls
,
REFIID
riid
,
void
**
p_ptr
)
{
if
(
!
p_ptr
)
{
return
E_POINTER
;
}
*
p_ptr
=
nullptr
;
if
(
riid
!=
IID_IClassFactory
&&
riid
!=
IID_IUnknown
)
{
return
E_NOINTERFACE
;
}
if
(
!
IsEqualCLSID
(
cls
,
CLSID_OBS_VirtualVideo
))
{
return
E_INVALIDARG
;
}
*
p_ptr
=
(
void
*
)
new
VCamFactory
(
cls
);
return
S_OK
;
}
//#define ENABLE_LOGGING
#ifdef ENABLE_LOGGING
void
logcallback
(
DShow
::
LogType
,
const
wchar_t
*
msg
,
void
*
)
{
OutputDebugStringW
(
msg
);
OutputDebugStringW
(
L"
\n
"
);
}
#endif
BOOL
WINAPI
DllMain
(
HINSTANCE
inst
,
DWORD
reason
,
LPVOID
)
{
if
(
reason
==
DLL_PROCESS_ATTACH
)
{
DisableThreadLibraryCalls
(
inst
);
#ifdef ENABLE_LOGGING
DShow
::
SetLogCallback
(
logcallback
,
nullptr
);
#endif
dll_inst
=
inst
;
}
return
true
;
}
plugins/win-dshow/virtualcam-module/virtualcam-module.def.in
0 → 100644
浏览文件 @
50ff29e5
LIBRARY obs-virtualcam-module@_output_suffix@.dll
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllInstall PRIVATE
plugins/win-dshow/virtualcam.c
0 → 100644
浏览文件 @
50ff29e5
#include <obs-module.h>
#include <util/platform.h>
#include "shared-memory-queue.h"
struct
virtualcam_data
{
obs_output_t
*
output
;
video_queue_t
*
vq
;
};
static
const
char
*
virtualcam_name
(
void
*
unused
)
{
UNUSED_PARAMETER
(
unused
);
return
"Virtual Camera Output"
;
}
static
void
virtualcam_destroy
(
void
*
data
)
{
struct
virtualcam_data
*
vcam
=
(
struct
virtualcam_data
*
)
data
;
video_queue_close
(
vcam
->
vq
);
bfree
(
data
);
}
static
void
*
virtualcam_create
(
obs_data_t
*
settings
,
obs_output_t
*
output
)
{
struct
virtualcam_data
*
vcam
=
(
struct
virtualcam_data
*
)
bzalloc
(
sizeof
(
*
vcam
));
vcam
->
output
=
output
;
UNUSED_PARAMETER
(
settings
);
return
vcam
;
}
static
bool
virtualcam_start
(
void
*
data
)
{
struct
virtualcam_data
*
vcam
=
(
struct
virtualcam_data
*
)
data
;
uint32_t
width
=
obs_output_get_width
(
vcam
->
output
);
uint32_t
height
=
obs_output_get_height
(
vcam
->
output
);
struct
obs_video_info
ovi
;
obs_get_video_info
(
&
ovi
);
uint64_t
interval
=
ovi
.
fps_den
*
10000000ULL
/
ovi
.
fps_num
;
char
res
[
64
];
snprintf
(
res
,
sizeof
(
res
),
"%dx%dx%lld"
,
(
int
)
width
,
(
int
)
height
,
(
long
long
)
interval
);
char
*
res_file
=
os_get_config_path_ptr
(
"obs-virtualcam.txt"
);
os_quick_write_utf8_file_safe
(
res_file
,
res
,
strlen
(
res
),
false
,
"tmp"
,
NULL
);
bfree
(
res_file
);
vcam
->
vq
=
video_queue_create
(
width
,
height
,
interval
);
if
(
!
vcam
->
vq
)
{
blog
(
LOG_WARNING
,
"starting virtual-output failed"
);
return
false
;
}
struct
video_scale_info
vsi
=
{
0
};
vsi
.
format
=
VIDEO_FORMAT_NV12
;
vsi
.
width
=
width
;
vsi
.
height
=
height
;
obs_output_set_video_conversion
(
vcam
->
output
,
&
vsi
);
blog
(
LOG_INFO
,
"Virtual output started"
);
obs_output_begin_data_capture
(
vcam
->
output
,
0
);
return
true
;
}
static
void
virtualcam_stop
(
void
*
data
,
uint64_t
ts
)
{
struct
virtualcam_data
*
vcam
=
(
struct
virtualcam_data
*
)
data
;
obs_output_end_data_capture
(
vcam
->
output
);
video_queue_close
(
vcam
->
vq
);
vcam
->
vq
=
NULL
;
blog
(
LOG_INFO
,
"Virtual output stopped"
);
UNUSED_PARAMETER
(
ts
);
}
static
void
virtual_video
(
void
*
param
,
struct
video_data
*
frame
)
{
struct
virtualcam_data
*
vcam
=
(
struct
virtualcam_data
*
)
param
;
if
(
!
vcam
->
vq
)
return
;
video_queue_write
(
vcam
->
vq
,
frame
->
data
,
frame
->
linesize
,
frame
->
timestamp
);
}
struct
obs_output_info
virtualcam_info
=
{
.
id
=
"virtualcam_output"
,
.
flags
=
OBS_OUTPUT_VIDEO
,
.
get_name
=
virtualcam_name
,
.
create
=
virtualcam_create
,
.
destroy
=
virtualcam_destroy
,
.
start
=
virtualcam_start
,
.
stop
=
virtualcam_stop
,
.
raw_video
=
virtual_video
,
};
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录