Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
尘离序散
obs-studio
提交
81e4211f
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,发现更多精彩内容 >>
未验证
提交
81e4211f
编写于
7月 30, 2020
作者:
J
Jim
提交者:
GitHub
7月 30, 2020
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #3202 from cg2121/screenshot-output
UI: Add ability to make screenshots
上级
4b17b82a
a70014d7
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
496 addition
and
171 deletion
+496
-171
UI/CMakeLists.txt
UI/CMakeLists.txt
+2
-0
UI/api-interface.cpp
UI/api-interface.cpp
+11
-0
UI/data/locale/en-US.ini
UI/data/locale/en-US.ini
+8
-0
UI/obs-app.cpp
UI/obs-app.cpp
+113
-0
UI/obs-app.hpp
UI/obs-app.hpp
+4
-0
UI/obs-frontend-api/obs-frontend-api.cpp
UI/obs-frontend-api/obs-frontend-api.cpp
+12
-0
UI/obs-frontend-api/obs-frontend-api.h
UI/obs-frontend-api/obs-frontend-api.h
+3
-0
UI/obs-frontend-api/obs-frontend-internal.hpp
UI/obs-frontend-api/obs-frontend-internal.hpp
+4
-0
UI/screenshot-obj.hpp
UI/screenshot-obj.hpp
+47
-0
UI/window-basic-main-outputs.cpp
UI/window-basic-main-outputs.cpp
+15
-171
UI/window-basic-main-screenshot.cpp
UI/window-basic-main-screenshot.cpp
+217
-0
UI/window-basic-main.cpp
UI/window-basic-main.cpp
+38
-0
UI/window-basic-main.hpp
UI/window-basic-main.hpp
+8
-0
docs/sphinx/reference-frontend-api.rst
docs/sphinx/reference-frontend-api.rst
+14
-0
未找到文件。
UI/CMakeLists.txt
浏览文件 @
81e4211f
...
...
@@ -200,6 +200,7 @@ set(obs_SOURCES
window-basic-main-outputs.cpp
window-basic-source-select.cpp
window-basic-settings-stream.cpp
window-basic-main-screenshot.cpp
window-basic-auto-config-test.cpp
window-basic-main-scene-collections.cpp
window-basic-main-transitions.cpp
...
...
@@ -288,6 +289,7 @@ set(obs_HEADERS
mute-checkbox.hpp
record-button.hpp
ui-validation.hpp
screenshot-obj.hpp
url-push-button.hpp
volume-control.hpp
adv-audio-control.hpp
...
...
UI/api-interface.cpp
浏览文件 @
81e4211f
...
...
@@ -534,6 +534,17 @@ struct OBSStudioAPI : obs_frontend_callbacks {
}
}
void
obs_frontend_take_screenshot
(
void
)
override
{
QMetaObject
::
invokeMethod
(
main
,
"Screenshot"
);
}
void
obs_frontend_take_source_screenshot
(
obs_source_t
*
source
)
override
{
QMetaObject
::
invokeMethod
(
main
,
"Screenshot"
,
Q_ARG
(
OBSSource
,
OBSSource
(
source
)));
}
void
on_load
(
obs_data_t
*
settings
)
override
{
for
(
size_t
i
=
saveCallbacks
.
size
();
i
>
0
;
i
--
)
{
...
...
UI/data/locale/en-US.ini
浏览文件 @
81e4211f
...
...
@@ -787,6 +787,14 @@ Basic.Settings.Output.Adv.FFmpeg.MuxerSettings="Muxer Settings (if any)"
Basic.Settings.Output.Adv.FFmpeg.GOPSize
=
"Keyframe interval (frames)"
Basic.Settings.Output.Adv.FFmpeg.IgnoreCodecCompat
=
"Show all codecs (even if potentially incompatible)"
# Screenshot
Screenshot
=
"Screenshot Output"
Screenshot.SourceHotkey
=
"Screenshot Selected Source"
Screenshot.StudioProgram
=
"Screenshot (Program)"
Screenshot.Preview
=
"Screenshot (Preview)"
Screenshot.Scene
=
"Screenshot (Scene)"
Screenshot.Source
=
"Screenshot (Source)"
# basic mode 'output' settings - advanced section - recording subsection - completer
FilenameFormatting.completer
=
"%CCYY-%MM-%DD %hh-%mm-%ss
\n
%YY-%MM-%DD %hh-%mm-%ss
\n
%Y-%m-%d %H-%M-%S
\n
%y-%m-%d %H-%M-%S
\n
%a %Y-%m-%d %H-%M-%S
\n
%A %Y-%m-%d %H-%M-%S
\n
%Y-%b-%d %H-%M-%S
\n
%Y-%B-%d %H-%M-%S
\n
%Y-%m-%d %I-%M-%S-%p
\n
%Y-%m-%d %H-%M-%S-%z
\n
%Y-%m-%d %H-%M-%S-%Z
\n
%FPS
\n
%CRES
\n
%ORES
\n
%VF"
...
...
UI/obs-app.cpp
浏览文件 @
81e4211f
...
...
@@ -1657,6 +1657,119 @@ string GenerateSpecifiedFilename(const char *extension, bool noSpace,
return
string
(
filename
);
}
static
void
FindBestFilename
(
string
&
strPath
,
bool
noSpace
)
{
int
num
=
2
;
if
(
!
os_file_exists
(
strPath
.
c_str
()))
return
;
const
char
*
ext
=
strrchr
(
strPath
.
c_str
(),
'.'
);
if
(
!
ext
)
return
;
int
extStart
=
int
(
ext
-
strPath
.
c_str
());
for
(;;)
{
string
testPath
=
strPath
;
string
numStr
;
numStr
=
noSpace
?
"_"
:
" ("
;
numStr
+=
to_string
(
num
++
);
if
(
!
noSpace
)
numStr
+=
")"
;
testPath
.
insert
(
extStart
,
numStr
);
if
(
!
os_file_exists
(
testPath
.
c_str
()))
{
strPath
=
testPath
;
break
;
}
}
}
static
void
ensure_directory_exists
(
string
&
path
)
{
replace
(
path
.
begin
(),
path
.
end
(),
'\\'
,
'/'
);
size_t
last
=
path
.
rfind
(
'/'
);
if
(
last
==
string
::
npos
)
return
;
string
directory
=
path
.
substr
(
0
,
last
);
os_mkdirs
(
directory
.
c_str
());
}
static
void
remove_reserved_file_characters
(
string
&
s
)
{
replace
(
s
.
begin
(),
s
.
end
(),
'\\'
,
'/'
);
replace
(
s
.
begin
(),
s
.
end
(),
'*'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'?'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'"'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'|'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
':'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'>'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'<'
,
'_'
);
}
string
GetFormatString
(
const
char
*
format
,
const
char
*
prefix
,
const
char
*
suffix
)
{
string
f
;
if
(
prefix
&&
*
prefix
)
{
f
+=
prefix
;
if
(
f
.
back
()
!=
' '
)
f
+=
" "
;
}
f
+=
format
;
if
(
suffix
&&
*
suffix
)
{
if
(
*
suffix
!=
' '
)
f
+=
" "
;
f
+=
suffix
;
}
remove_reserved_file_characters
(
f
);
return
f
;
}
string
GetOutputFilename
(
const
char
*
path
,
const
char
*
ext
,
bool
noSpace
,
bool
overwrite
,
const
char
*
format
)
{
OBSBasic
*
main
=
reinterpret_cast
<
OBSBasic
*>
(
App
()
->
GetMainWindow
());
os_dir_t
*
dir
=
path
&&
path
[
0
]
?
os_opendir
(
path
)
:
nullptr
;
if
(
!
dir
)
{
if
(
main
->
isVisible
())
OBSMessageBox
::
warning
(
main
,
QTStr
(
"Output.BadPath.Title"
),
QTStr
(
"Output.BadPath.Text"
));
else
main
->
SysTrayNotify
(
QTStr
(
"Output.BadPath.Text"
),
QSystemTrayIcon
::
Warning
);
return
""
;
}
os_closedir
(
dir
);
string
strPath
;
strPath
+=
path
;
char
lastChar
=
strPath
.
back
();
if
(
lastChar
!=
'/'
&&
lastChar
!=
'\\'
)
strPath
+=
"/"
;
strPath
+=
GenerateSpecifiedFilename
(
ext
,
noSpace
,
format
);
ensure_directory_exists
(
strPath
);
if
(
!
overwrite
)
FindBestFilename
(
strPath
,
noSpace
);
return
strPath
;
}
vector
<
pair
<
string
,
string
>>
GetLocaleNames
()
{
string
path
;
...
...
UI/obs-app.hpp
浏览文件 @
81e4211f
...
...
@@ -40,6 +40,10 @@ std::string GenerateTimeDateFilename(const char *extension,
bool
noSpace
=
false
);
std
::
string
GenerateSpecifiedFilename
(
const
char
*
extension
,
bool
noSpace
,
const
char
*
format
);
std
::
string
GetFormatString
(
const
char
*
format
,
const
char
*
prefix
,
const
char
*
suffix
);
std
::
string
GetOutputFilename
(
const
char
*
path
,
const
char
*
ext
,
bool
noSpace
,
bool
overwrite
,
const
char
*
format
);
QObject
*
CreateShortcutFilter
();
struct
BaseLexer
{
...
...
UI/obs-frontend-api/obs-frontend-api.cpp
浏览文件 @
81e4211f
...
...
@@ -463,3 +463,15 @@ void obs_frontend_set_current_preview_scene(obs_source_t *scene)
if
(
callbacks_valid
())
c
->
obs_frontend_set_current_preview_scene
(
scene
);
}
void
obs_frontend_take_screenshot
(
void
)
{
if
(
callbacks_valid
())
c
->
obs_frontend_take_screenshot
();
}
void
obs_frontend_take_source_screenshot
(
obs_source_t
*
source
)
{
if
(
callbacks_valid
())
c
->
obs_frontend_take_source_screenshot
(
source
);
}
UI/obs-frontend-api/obs-frontend-api.h
浏览文件 @
81e4211f
...
...
@@ -194,6 +194,9 @@ EXPORT bool obs_frontend_preview_enabled(void);
EXPORT
obs_source_t
*
obs_frontend_get_current_preview_scene
(
void
);
EXPORT
void
obs_frontend_set_current_preview_scene
(
obs_source_t
*
scene
);
EXPORT
void
obs_frontend_take_screenshot
(
void
);
EXPORT
void
obs_frontend_take_source_screenshot
(
obs_source_t
*
source
);
/* ------------------------------------------------------------------------- */
#ifdef __cplusplus
...
...
UI/obs-frontend-api/obs-frontend-internal.hpp
浏览文件 @
81e4211f
...
...
@@ -118,6 +118,10 @@ struct obs_frontend_callbacks {
virtual
void
on_preload
(
obs_data_t
*
settings
)
=
0
;
virtual
void
on_save
(
obs_data_t
*
settings
)
=
0
;
virtual
void
on_event
(
enum
obs_frontend_event
event
)
=
0
;
virtual
void
obs_frontend_take_screenshot
()
=
0
;
virtual
void
obs_frontend_take_source_screenshot
(
obs_source_t
*
source
)
=
0
;
};
EXPORT
void
...
...
UI/screenshot-obj.hpp
0 → 100644
浏览文件 @
81e4211f
/******************************************************************************
Copyright (C) 2020 by Hugh Bailey <obs.jim@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include <QObject>
#include <string>
#include <thread>
#include <obs.hpp>
class
ScreenshotObj
:
public
QObject
{
Q_OBJECT
public:
ScreenshotObj
(
obs_source_t
*
source
);
~
ScreenshotObj
()
override
;
void
Screenshot
();
void
Download
();
void
Copy
();
void
MuxAndFinish
();
gs_texrender_t
*
texrender
=
nullptr
;
gs_stagesurf_t
*
stagesurf
=
nullptr
;
OBSWeakSource
weakSource
;
std
::
string
path
;
QImage
image
;
uint32_t
cx
;
uint32_t
cy
;
std
::
thread
th
;
int
stage
=
0
;
public
slots
:
void
Save
();
};
UI/window-basic-main-outputs.cpp
浏览文件 @
81e4211f
...
...
@@ -164,36 +164,6 @@ static void OBSStopVirtualCam(void *data, calldata_t *params)
UNUSED_PARAMETER
(
params
);
}
static
void
FindBestFilename
(
string
&
strPath
,
bool
noSpace
)
{
int
num
=
2
;
if
(
!
os_file_exists
(
strPath
.
c_str
()))
return
;
const
char
*
ext
=
strrchr
(
strPath
.
c_str
(),
'.'
);
if
(
!
ext
)
return
;
int
extStart
=
int
(
ext
-
strPath
.
c_str
());
for
(;;)
{
string
testPath
=
strPath
;
string
numStr
;
numStr
=
noSpace
?
"_"
:
" ("
;
numStr
+=
to_string
(
num
++
);
if
(
!
noSpace
)
numStr
+=
")"
;
testPath
.
insert
(
extStart
,
numStr
);
if
(
!
os_file_exists
(
testPath
.
c_str
()))
{
strPath
=
testPath
;
break
;
}
}
}
/* ------------------------------------------------------------------------ */
static
bool
CreateAACEncoder
(
OBSEncoder
&
res
,
string
&
id
,
int
bitrate
,
...
...
@@ -903,30 +873,6 @@ bool SimpleOutput::StartStreaming(obs_service_t *service)
return
false
;
}
static
void
remove_reserved_file_characters
(
string
&
s
)
{
replace
(
s
.
begin
(),
s
.
end
(),
'\\'
,
'/'
);
replace
(
s
.
begin
(),
s
.
end
(),
'*'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'?'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'"'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'|'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
':'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'>'
,
'_'
);
replace
(
s
.
begin
(),
s
.
end
(),
'<'
,
'_'
);
}
static
void
ensure_directory_exists
(
string
&
path
)
{
replace
(
path
.
begin
(),
path
.
end
(),
'\\'
,
'/'
);
size_t
last
=
path
.
rfind
(
'/'
);
if
(
last
==
string
::
npos
)
return
;
string
directory
=
path
.
substr
(
0
,
last
);
os_mkdirs
(
directory
.
c_str
());
}
void
SimpleOutput
::
UpdateRecording
()
{
if
(
replayBufferActive
||
recordingActive
)
...
...
@@ -977,54 +923,15 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
int
rbSize
=
config_get_int
(
main
->
Config
(),
"SimpleOutput"
,
"RecRBSize"
);
os_dir_t
*
dir
=
path
&&
path
[
0
]
?
os_opendir
(
path
)
:
nullptr
;
if
(
!
dir
)
{
if
(
main
->
isVisible
())
OBSMessageBox
::
warning
(
main
,
QTStr
(
"Output.BadPath.Title"
),
QTStr
(
"Output.BadPath.Text"
));
else
main
->
SysTrayNotify
(
QTStr
(
"Output.BadPath.Text"
),
QSystemTrayIcon
::
Warning
);
return
false
;
}
os_closedir
(
dir
);
string
f
;
string
strPath
;
strPath
+=
path
;
char
lastChar
=
strPath
.
back
();
if
(
lastChar
!=
'/'
&&
lastChar
!=
'\\'
)
strPath
+=
"/"
;
strPath
+=
GenerateSpecifiedFilename
(
ffmpegOutput
?
"avi"
:
format
,
noSpace
,
filenameFormat
);
ensure_directory_exists
(
strPath
);
if
(
!
overwriteIfExists
)
FindBestFilename
(
strPath
,
noSpace
);
obs_data_t
*
settings
=
obs_data_create
();
if
(
updateReplayBuffer
)
{
string
f
;
if
(
rbPrefix
&&
*
rbPrefix
)
{
f
+=
rbPrefix
;
if
(
f
.
back
()
!=
' '
)
f
+=
" "
;
}
f
+=
filenameFormat
;
if
(
rbSuffix
&&
*
rbSuffix
)
{
if
(
*
rbSuffix
!=
' '
)
f
+=
" "
;
f
+=
rbSuffix
;
}
remove_reserved_file_characters
(
f
);
f
=
GetFormatString
(
filenameFormat
,
rbPrefix
,
rbSuffix
);
strPath
=
GetOutputFilename
(
path
,
ffmpegOutput
?
"avi"
:
format
,
noSpace
,
overwriteIfExists
,
f
.
c_str
());
obs_data_set_string
(
settings
,
"directory"
,
path
);
obs_data_set_string
(
settings
,
"format"
,
f
.
c_str
());
obs_data_set_string
(
settings
,
"extension"
,
format
);
...
...
@@ -1033,6 +940,10 @@ bool SimpleOutput::ConfigureRecording(bool updateReplayBuffer)
obs_data_set_int
(
settings
,
"max_size_mb"
,
usingRecordingPreset
?
rbSize
:
0
);
}
else
{
f
=
GetFormatString
(
filenameFormat
,
nullptr
,
nullptr
);
strPath
=
GetOutputFilename
(
path
,
ffmpegOutput
?
"avi"
:
format
,
noSpace
,
overwriteIfExists
,
f
.
c_str
());
obs_data_set_string
(
settings
,
ffmpegOutput
?
"url"
:
"path"
,
strPath
.
c_str
());
}
...
...
@@ -1779,34 +1690,9 @@ bool AdvancedOutput::StartRecording()
?
"FFFileNameWithoutSpace"
:
"RecFileNameWithoutSpace"
);
os_dir_t
*
dir
=
path
&&
path
[
0
]
?
os_opendir
(
path
)
:
nullptr
;
if
(
!
dir
)
{
if
(
main
->
isVisible
())
OBSMessageBox
::
warning
(
main
,
QTStr
(
"Output.BadPath.Title"
),
QTStr
(
"Output.BadPath.Text"
));
else
main
->
SysTrayNotify
(
QTStr
(
"Output.BadPath.Text"
),
QSystemTrayIcon
::
Warning
);
return
false
;
}
os_closedir
(
dir
);
string
strPath
;
strPath
+=
path
;
char
lastChar
=
strPath
.
back
();
if
(
lastChar
!=
'/'
&&
lastChar
!=
'\\'
)
strPath
+=
"/"
;
strPath
+=
GenerateSpecifiedFilename
(
recFormat
,
noSpace
,
filenameFormat
);
ensure_directory_exists
(
strPath
);
if
(
!
overwriteIfExists
)
FindBestFilename
(
strPath
,
noSpace
);
string
strPath
=
GetOutputFilename
(
path
,
recFormat
,
noSpace
,
overwriteIfExists
,
filenameFormat
);
obs_data_t
*
settings
=
obs_data_create
();
obs_data_set_string
(
settings
,
ffmpegRecording
?
"url"
:
"path"
,
...
...
@@ -1879,53 +1765,11 @@ bool AdvancedOutput::StartReplayBuffer()
rbTime
=
config_get_int
(
main
->
Config
(),
"AdvOut"
,
"RecRBTime"
);
rbSize
=
config_get_int
(
main
->
Config
(),
"AdvOut"
,
"RecRBSize"
);
os_dir_t
*
dir
=
path
&&
path
[
0
]
?
os_opendir
(
path
)
:
nullptr
;
if
(
!
dir
)
{
if
(
main
->
isVisible
())
OBSMessageBox
::
warning
(
main
,
QTStr
(
"Output.BadPath.Title"
),
QTStr
(
"Output.BadPath.Text"
));
else
main
->
SysTrayNotify
(
QTStr
(
"Output.BadPath.Text"
),
QSystemTrayIcon
::
Warning
);
return
false
;
}
os_closedir
(
dir
);
string
strPath
;
strPath
+=
path
;
char
lastChar
=
strPath
.
back
();
if
(
lastChar
!=
'/'
&&
lastChar
!=
'\\'
)
strPath
+=
"/"
;
strPath
+=
GenerateSpecifiedFilename
(
recFormat
,
noSpace
,
filenameFormat
);
ensure_directory_exists
(
strPath
);
if
(
!
overwriteIfExists
)
FindBestFilename
(
strPath
,
noSpace
);
string
f
=
GetFormatString
(
filenameFormat
,
rbPrefix
,
rbSuffix
);
string
strPath
=
GetOutputFilename
(
path
,
recFormat
,
noSpace
,
overwriteIfExists
,
f
.
c_str
());
obs_data_t
*
settings
=
obs_data_create
();
string
f
;
if
(
rbPrefix
&&
*
rbPrefix
)
{
f
+=
rbPrefix
;
if
(
f
.
back
()
!=
' '
)
f
+=
" "
;
}
f
+=
filenameFormat
;
if
(
rbSuffix
&&
*
rbSuffix
)
{
if
(
*
rbSuffix
!=
' '
)
f
+=
" "
;
f
+=
rbSuffix
;
}
remove_reserved_file_characters
(
f
);
obs_data_set_string
(
settings
,
"directory"
,
path
);
obs_data_set_string
(
settings
,
"format"
,
f
.
c_str
());
...
...
UI/window-basic-main-screenshot.cpp
0 → 100644
浏览文件 @
81e4211f
/******************************************************************************
Copyright (C) 2020 by Hugh Bailey <obs.jim@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "window-basic-main.hpp"
#include "screenshot-obj.hpp"
#include "qt-wrappers.hpp"
static
void
ScreenshotTick
(
void
*
param
,
float
);
/* ========================================================================= */
ScreenshotObj
::
ScreenshotObj
(
obs_source_t
*
source
)
:
weakSource
(
OBSGetWeakRef
(
source
))
{
obs_add_tick_callback
(
ScreenshotTick
,
this
);
}
ScreenshotObj
::~
ScreenshotObj
()
{
obs_enter_graphics
();
gs_stagesurface_destroy
(
stagesurf
);
gs_texrender_destroy
(
texrender
);
obs_leave_graphics
();
obs_remove_tick_callback
(
ScreenshotTick
,
this
);
if
(
th
.
joinable
())
th
.
join
();
}
void
ScreenshotObj
::
Screenshot
()
{
OBSSource
source
=
OBSGetStrongRef
(
weakSource
);
if
(
source
)
{
cx
=
obs_source_get_base_width
(
source
);
cy
=
obs_source_get_base_height
(
source
);
}
else
{
obs_video_info
ovi
;
obs_get_video_info
(
&
ovi
);
cx
=
ovi
.
base_width
;
cy
=
ovi
.
base_height
;
}
if
(
!
cx
||
!
cy
)
{
blog
(
LOG_WARNING
,
"Cannot screenshot, invalid target size"
);
obs_remove_tick_callback
(
ScreenshotTick
,
this
);
deleteLater
();
return
;
}
texrender
=
gs_texrender_create
(
GS_RGBA
,
GS_ZS_NONE
);
stagesurf
=
gs_stagesurface_create
(
cx
,
cy
,
GS_RGBA
);
gs_texrender_reset
(
texrender
);
if
(
gs_texrender_begin
(
texrender
,
cx
,
cy
))
{
vec4
zero
;
vec4_zero
(
&
zero
);
gs_clear
(
GS_CLEAR_COLOR
,
&
zero
,
0.0
f
,
0
);
gs_ortho
(
0.0
f
,
(
float
)
cx
,
0.0
f
,
(
float
)
cy
,
-
100.0
f
,
100.0
f
);
gs_blend_state_push
();
gs_blend_function
(
GS_BLEND_ONE
,
GS_BLEND_ZERO
);
if
(
source
)
{
obs_source_inc_showing
(
source
);
obs_source_video_render
(
source
);
obs_source_dec_showing
(
source
);
}
else
{
obs_render_main_texture
();
}
gs_blend_state_pop
();
gs_texrender_end
(
texrender
);
}
}
void
ScreenshotObj
::
Download
()
{
gs_stage_texture
(
stagesurf
,
gs_texrender_get_texture
(
texrender
));
}
void
ScreenshotObj
::
Copy
()
{
uint8_t
*
videoData
=
nullptr
;
uint32_t
videoLinesize
=
0
;
bool
success
=
false
;
image
=
QImage
(
cx
,
cy
,
QImage
::
Format
::
Format_RGBX8888
);
if
(
gs_stagesurface_map
(
stagesurf
,
&
videoData
,
&
videoLinesize
))
{
int
linesize
=
image
.
bytesPerLine
();
for
(
int
y
=
0
;
y
<
(
int
)
cy
;
y
++
)
memcpy
(
image
.
scanLine
(
y
),
videoData
+
(
y
*
videoLinesize
),
linesize
);
gs_stagesurface_unmap
(
stagesurf
);
success
=
true
;
}
}
void
ScreenshotObj
::
Save
()
{
OBSBasic
*
main
=
OBSBasic
::
Get
();
config_t
*
config
=
main
->
Config
();
const
char
*
mode
=
config_get_string
(
config
,
"Output"
,
"Mode"
);
const
char
*
type
=
config_get_string
(
config
,
"AdvOut"
,
"RecType"
);
const
char
*
adv_path
=
strcmp
(
type
,
"Standard"
)
?
config_get_string
(
config
,
"AdvOut"
,
"FFFilePath"
)
:
config_get_string
(
config
,
"AdvOut"
,
"RecFilePath"
);
const
char
*
rec_path
=
strcmp
(
mode
,
"Advanced"
)
?
config_get_string
(
config
,
"SimpleOutput"
,
"FilePath"
)
:
adv_path
;
const
char
*
filenameFormat
=
config_get_string
(
config
,
"Output"
,
"FilenameFormatting"
);
bool
overwriteIfExists
=
config_get_bool
(
config
,
"Output"
,
"OverwriteIfExists"
);
path
=
GetOutputFilename
(
rec_path
,
"png"
,
false
,
overwriteIfExists
,
GetFormatString
(
filenameFormat
,
"Screenshot"
,
nullptr
).
c_str
());
th
=
std
::
thread
([
this
]
{
MuxAndFinish
();
});
}
void
ScreenshotObj
::
MuxAndFinish
()
{
image
.
save
(
QT_UTF8
(
path
.
c_str
()));
blog
(
LOG_INFO
,
"Saved screenshot to '%s'"
,
path
.
c_str
());
deleteLater
();
}
/* ========================================================================= */
#define STAGE_SCREENSHOT 0
#define STAGE_DOWNLOAD 1
#define STAGE_COPY_AND_SAVE 2
#define STAGE_FINISH 3
static
void
ScreenshotTick
(
void
*
param
,
float
)
{
ScreenshotObj
*
data
=
reinterpret_cast
<
ScreenshotObj
*>
(
param
);
if
(
data
->
stage
==
STAGE_FINISH
)
{
return
;
}
obs_enter_graphics
();
switch
(
data
->
stage
)
{
case
STAGE_SCREENSHOT
:
data
->
Screenshot
();
break
;
case
STAGE_DOWNLOAD
:
data
->
Download
();
break
;
case
STAGE_COPY_AND_SAVE
:
data
->
Copy
();
QMetaObject
::
invokeMethod
(
data
,
"Save"
);
obs_remove_tick_callback
(
ScreenshotTick
,
data
);
break
;
}
obs_leave_graphics
();
data
->
stage
++
;
}
void
OBSBasic
::
Screenshot
(
OBSSource
source
)
{
if
(
!!
screenshotData
)
{
blog
(
LOG_WARNING
,
"Cannot take new screenshot, "
"screenshot currently in progress"
);
return
;
}
screenshotData
=
new
ScreenshotObj
(
source
);
}
void
OBSBasic
::
ScreenshotSelectedSource
()
{
OBSSceneItem
item
=
GetCurrentSceneItem
();
if
(
item
)
{
Screenshot
(
obs_sceneitem_get_source
(
item
));
}
else
{
blog
(
LOG_INFO
,
"Could not take a source screenshot: "
"no source selected"
);
}
}
void
OBSBasic
::
ScreenshotProgram
()
{
Screenshot
(
GetProgramSource
());
}
void
OBSBasic
::
ScreenshotScene
()
{
Screenshot
(
GetCurrentSceneSource
());
}
UI/window-basic-main.cpp
浏览文件 @
81e4211f
...
...
@@ -2347,6 +2347,31 @@ void OBSBasic::CreateHotkeys()
"OBSBasic.ResetStats"
,
Str
(
"Basic.Stats.ResetStats"
),
resetStats
,
this
);
LoadHotkey
(
statsHotkey
,
"OBSBasic.ResetStats"
);
auto
screenshot
=
[](
void
*
data
,
obs_hotkey_id
,
obs_hotkey_t
*
,
bool
pressed
)
{
if
(
pressed
)
QMetaObject
::
invokeMethod
(
static_cast
<
OBSBasic
*>
(
data
),
"Screenshot"
,
Qt
::
QueuedConnection
);
};
screenshotHotkey
=
obs_hotkey_register_frontend
(
"OBSBasic.Screenshot"
,
Str
(
"Screenshot"
),
screenshot
,
this
);
LoadHotkey
(
screenshotHotkey
,
"OBSBasic.Screenshot"
);
auto
screenshotSource
=
[](
void
*
data
,
obs_hotkey_id
,
obs_hotkey_t
*
,
bool
pressed
)
{
if
(
pressed
)
QMetaObject
::
invokeMethod
(
static_cast
<
OBSBasic
*>
(
data
),
"ScreenshotSelectedSource"
,
Qt
::
QueuedConnection
);
};
sourceScreenshotHotkey
=
obs_hotkey_register_frontend
(
"OBSBasic.SelectedSourceScreenshot"
,
Str
(
"Screenshot.SourceHotkey"
),
screenshotSource
,
this
);
LoadHotkey
(
sourceScreenshotHotkey
,
"OBSBasic.SelectedSourceScreenshot"
);
}
void
OBSBasic
::
ClearHotkeys
()
...
...
@@ -2360,6 +2385,8 @@ void OBSBasic::ClearHotkeys()
obs_hotkey_unregister
(
togglePreviewProgramHotkey
);
obs_hotkey_unregister
(
transitionHotkey
);
obs_hotkey_unregister
(
statsHotkey
);
obs_hotkey_unregister
(
screenshotHotkey
);
obs_hotkey_unregister
(
sourceScreenshotHotkey
);
}
OBSBasic
::~
OBSBasic
()
...
...
@@ -2370,6 +2397,7 @@ OBSBasic::~OBSBasic()
if
(
updateCheckThread
&&
updateCheckThread
->
isRunning
())
updateCheckThread
->
wait
();
delete
screenshotData
;
delete
multiviewProjectorMenu
;
delete
previewProjector
;
delete
studioProgramProjector
;
...
...
@@ -4330,6 +4358,8 @@ void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
QTStr
(
"SceneWindow"
),
this
,
SLOT
(
OpenSceneWindow
()));
popup
.
addAction
(
sceneWindow
);
popup
.
addAction
(
QTStr
(
"Screenshot.Scene"
),
this
,
SLOT
(
ScreenshotScene
()));
popup
.
addSeparator
();
popup
.
addAction
(
QTStr
(
"Filters"
),
this
,
SLOT
(
OpenSceneFilters
()));
...
...
@@ -4681,6 +4711,9 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
popup
.
addAction
(
previewWindow
);
popup
.
addAction
(
QTStr
(
"Screenshot.Preview"
),
this
,
SLOT
(
ScreenshotScene
()));
popup
.
addSeparator
();
}
...
...
@@ -4796,6 +4829,8 @@ void OBSBasic::CreateSourcePopupMenu(int idx, bool preview)
popup
.
addMenu
(
sourceProjector
);
popup
.
addAction
(
sourceWindow
);
popup
.
addAction
(
QTStr
(
"Screenshot.Source"
),
this
,
SLOT
(
ScreenshotSelectedSource
()));
popup
.
addSeparator
();
action
=
popup
.
addAction
(
QTStr
(
"Interact"
),
this
,
...
...
@@ -6249,6 +6284,9 @@ void OBSBasic::on_program_customContextMenuRequested(const QPoint &)
popup
.
addAction
(
studioProgramWindow
);
popup
.
addAction
(
QTStr
(
"Screenshot.StudioProgram"
),
this
,
SLOT
(
ScreenshotProgram
()));
popup
.
exec
(
QCursor
::
pos
());
}
...
...
UI/window-basic-main.hpp
浏览文件 @
81e4211f
...
...
@@ -432,6 +432,8 @@ private:
obs_hotkey_id
togglePreviewProgramHotkey
=
0
;
obs_hotkey_id
transitionHotkey
=
0
;
obs_hotkey_id
statsHotkey
=
0
;
obs_hotkey_id
screenshotHotkey
=
0
;
obs_hotkey_id
sourceScreenshotHotkey
=
0
;
int
quickTransitionIdCounter
=
1
;
bool
overridingTransition
=
false
;
...
...
@@ -525,6 +527,8 @@ private:
void
UpdateProjectorHideCursor
();
void
UpdateProjectorAlwaysOnTop
(
bool
top
);
QPointer
<
QObject
>
screenshotData
;
public
slots
:
void
DeferSaveBegin
();
void
DeferSaveEnd
();
...
...
@@ -893,6 +897,10 @@ private slots:
void
on_recordButton_clicked
();
void
VCamButtonClicked
();
void
on_settingsButton_clicked
();
void
Screenshot
(
OBSSource
source_
=
nullptr
);
void
ScreenshotSelectedSource
();
void
ScreenshotProgram
();
void
ScreenshotScene
();
void
on_actionHelpPortal_triggered
();
void
on_actionWebsite_triggered
();
...
...
docs/sphinx/reference-frontend-api.rst
浏览文件 @
81e4211f
...
...
@@ -529,3 +529,17 @@ Functions
active scene if not in studio mode.
:param scene: The scene to set as the current preview.
---------------------------------------
.. function:: void *obs_frontend_take_screenshot(void)
Takes a screenshot of the main OBS output.
---------------------------------------
.. function:: void *obs_frontend_take_source_screenshot(obs_source_t *source)
Takes a screenshot of the specified source.
:param source: The source to take screenshot of.
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录