New upstream version 18.0.1+dfsg1
This commit is contained in:
parent
6efda2859e
commit
f2cf6cce50
1337 changed files with 41178 additions and 84670 deletions
|
|
@ -5,20 +5,53 @@ if(APPLE)
|
|||
include_directories(${COCOA})
|
||||
endif()
|
||||
|
||||
if(WIN32 OR APPLE)
|
||||
set(frontend-tools_HEADERS
|
||||
auto-scene-switcher.hpp
|
||||
)
|
||||
set(frontend-tools_SOURCES
|
||||
auto-scene-switcher.cpp
|
||||
)
|
||||
set(frontend-tools_UI
|
||||
forms/auto-scene-switcher.ui
|
||||
)
|
||||
endif()
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/frontend-tools-config.h.in"
|
||||
"${CMAKE_BINARY_DIR}/config/frontend-tools-config.h")
|
||||
|
||||
set(frontend-tools_HEADERS
|
||||
auto-scene-switcher.hpp
|
||||
${frontend-tools_HEADERS}
|
||||
"${CMAKE_BINARY_DIR}/config/frontend-tools-config.h"
|
||||
output-timer.hpp
|
||||
tool-helpers.hpp
|
||||
)
|
||||
set(frontend-tools_SOURCES
|
||||
${frontend-tools_SOURCES}
|
||||
frontend-tools.c
|
||||
auto-scene-switcher.cpp
|
||||
output-timer.cpp
|
||||
)
|
||||
set(frontend-tools_UI
|
||||
forms/auto-scene-switcher.ui
|
||||
${frontend-tools_UI}
|
||||
forms/output-timer.ui
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set(frontend-tools_PLATFORM_SOURCES
|
||||
auto-scene-switcher-win.cpp)
|
||||
|
||||
if(BUILD_CAPTIONS)
|
||||
set(frontend-tools_PLATFORM_SOURCES
|
||||
${frontend-tools_PLATFORM_SOURCES}
|
||||
captions.cpp
|
||||
captions-stream.cpp)
|
||||
set(frontend-tools_PLATFORM_HEADERS
|
||||
captions.hpp
|
||||
captions-stream.hpp)
|
||||
set(frontend-tools_PLATFORM_UI
|
||||
forms/captions.ui)
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
set(frontend-tools_PLATFORM_SOURCES
|
||||
auto-scene-switcher-osx.mm)
|
||||
|
|
@ -29,13 +62,16 @@ elseif(APPLE)
|
|||
${COCOA})
|
||||
endif()
|
||||
|
||||
qt5_wrap_ui(frontend-tools_UI_HEADERS ${frontend-tools_UI})
|
||||
qt5_wrap_ui(frontend-tools_UI_HEADERS
|
||||
${frontend-tools_UI}
|
||||
${frontend-tools_PLATFORM_UI})
|
||||
|
||||
add_library(frontend-tools MODULE
|
||||
${frontend-tools_HEADERS}
|
||||
${frontend-tools_SOURCES}
|
||||
${frontend-tools_PLATFORM_SOURCES}
|
||||
${frontend-tools_UI_HEADERS}
|
||||
${frontend-tools_PLATFORM_SOURCES}
|
||||
${frontend-tools_PLATFORM_HEADERS}
|
||||
)
|
||||
target_link_libraries(frontend-tools
|
||||
${frontend-tools_PLATFORM_LIBS}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@
|
|||
#include <obs.hpp>
|
||||
#include <util/util.hpp>
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QAction>
|
||||
#include "auto-scene-switcher.hpp"
|
||||
#include "tool-helpers.hpp"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
|
@ -81,37 +83,6 @@ static inline QString MakeSwitchName(const QString &scene,
|
|||
return QStringLiteral("[") + scene + QStringLiteral("]: ") + window;
|
||||
}
|
||||
|
||||
static inline string GetWeakSourceName(obs_weak_source_t *weak_source)
|
||||
{
|
||||
string name;
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(weak_source);
|
||||
if (source) {
|
||||
name = obs_source_get_name(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakSourceByName(const char *name)
|
||||
{
|
||||
OBSWeakSource weak;
|
||||
obs_source_t *source = obs_get_source_by_name(name);
|
||||
if (source) {
|
||||
weak = obs_source_get_weak_source(source);
|
||||
obs_weak_source_release(weak);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
return weak;
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakSourceByQString(const QString &name)
|
||||
{
|
||||
return GetWeakSourceByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
SceneSwitcher::SceneSwitcher(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui(new Ui_SceneSwitcher)
|
||||
|
|
@ -231,13 +202,19 @@ void SceneSwitcher::on_add_clicked()
|
|||
int idx = FindByData(windowName);
|
||||
|
||||
if (idx == -1) {
|
||||
QListWidgetItem *item = new QListWidgetItem(text,
|
||||
ui->switches);
|
||||
item->setData(Qt::UserRole, v);
|
||||
|
||||
lock_guard<mutex> lock(switcher->m);
|
||||
switcher->switches.emplace_back(source,
|
||||
windowName.toUtf8().constData());
|
||||
try {
|
||||
lock_guard<mutex> lock(switcher->m);
|
||||
switcher->switches.emplace_back(source,
|
||||
windowName.toUtf8().constData());
|
||||
|
||||
QListWidgetItem *item = new QListWidgetItem(text,
|
||||
ui->switches);
|
||||
item->setData(Qt::UserRole, v);
|
||||
} catch (const regex_error &) {
|
||||
QMessageBox::warning(this,
|
||||
obs_module_text("InvalidRegex.Title"),
|
||||
obs_module_text("InvalidRegex.Text"));
|
||||
}
|
||||
} else {
|
||||
QListWidgetItem *item = ui->switches->item(idx);
|
||||
item->setText(text);
|
||||
|
|
|
|||
418
UI/frontend-plugins/frontend-tools/captions-stream.cpp
Normal file
418
UI/frontend-plugins/frontend-tools/captions-stream.cpp
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
#include "captions-stream.hpp"
|
||||
#include <mmreg.h>
|
||||
#include <util/windows/CoTaskMemPtr.hpp>
|
||||
#include <util/threading.h>
|
||||
#include <util/base.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if 0
|
||||
#define debugfunc(format, ...) blog(LOG_DEBUG, "[Captions] %s(" format ")", \
|
||||
__FUNCTION__, ##__VA_ARGS__)
|
||||
#else
|
||||
#define debugfunc(format, ...)
|
||||
#endif
|
||||
|
||||
CaptionStream::CaptionStream(DWORD samplerate_) :
|
||||
samplerate(samplerate_),
|
||||
event(CreateEvent(nullptr, false, false, nullptr))
|
||||
{
|
||||
buf_info.ulMsMinNotification = 50;
|
||||
buf_info.ulMsBufferSize = 500;
|
||||
buf_info.ulMsEventBias = 0;
|
||||
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.nChannels = 1;
|
||||
format.nSamplesPerSec = 16000;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * sizeof(uint16_t);
|
||||
format.nBlockAlign = 2;
|
||||
format.wBitsPerSample = 16;
|
||||
format.cbSize = sizeof(format);
|
||||
|
||||
resampler.Reset(&format);
|
||||
}
|
||||
|
||||
void CaptionStream::Stop()
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> lock(m);
|
||||
circlebuf_free(buf);
|
||||
}
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void CaptionStream::PushAudio(const struct audio_data *data, bool muted)
|
||||
{
|
||||
uint8_t *output[MAX_AV_PLANES] = {};
|
||||
uint32_t frames = data->frames;
|
||||
uint64_t ts_offset;
|
||||
bool ready = false;
|
||||
|
||||
audio_resampler_resample(resampler, output, &frames, &ts_offset,
|
||||
data->data, data->frames);
|
||||
|
||||
if (output[0]) {
|
||||
if (muted)
|
||||
memset(output[0], 0, frames * sizeof(int16_t));
|
||||
|
||||
lock_guard<mutex> lock(m);
|
||||
circlebuf_push_back(buf, output[0], frames * sizeof(int16_t));
|
||||
write_pos += frames * sizeof(int16_t);
|
||||
|
||||
if (wait_size && buf->size >= wait_size)
|
||||
ready = true;
|
||||
}
|
||||
|
||||
if (ready)
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
// IUnknown methods
|
||||
|
||||
STDMETHODIMP CaptionStream::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
if (riid == IID_IUnknown) {
|
||||
AddRef();
|
||||
*ppv = this;
|
||||
|
||||
} else if (riid == IID_IStream) {
|
||||
AddRef();
|
||||
*ppv = (IStream*)this;
|
||||
|
||||
} else if (riid == IID_ISpStreamFormat) {
|
||||
AddRef();
|
||||
*ppv = (ISpStreamFormat*)this;
|
||||
|
||||
} else if (riid == IID_ISpAudio) {
|
||||
AddRef();
|
||||
*ppv = (ISpAudio*)this;
|
||||
|
||||
} else {
|
||||
*ppv = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
return NOERROR;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptionStream::AddRef()
|
||||
{
|
||||
return (ULONG)os_atomic_inc_long(&refs);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CaptionStream::Release()
|
||||
{
|
||||
ULONG new_refs = (ULONG)os_atomic_dec_long(&refs);
|
||||
if (!new_refs)
|
||||
delete this;
|
||||
|
||||
return new_refs;
|
||||
}
|
||||
|
||||
// ISequentialStream methods
|
||||
|
||||
STDMETHODIMP CaptionStream::Read(void *data, ULONG bytes, ULONG *read_bytes)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
size_t cur_size;
|
||||
|
||||
debugfunc("data, %lu, read_bytes", bytes);
|
||||
if (!data)
|
||||
return STG_E_INVALIDPOINTER;
|
||||
|
||||
{
|
||||
lock_guard<mutex> lock1(m);
|
||||
wait_size = bytes;
|
||||
cur_size = buf->size;
|
||||
}
|
||||
|
||||
unique_lock<mutex> lock(m);
|
||||
|
||||
if (bytes > cur_size)
|
||||
cv.wait(lock);
|
||||
|
||||
if (bytes > (ULONG)buf->size) {
|
||||
bytes = (ULONG)buf->size;
|
||||
hr = S_FALSE;
|
||||
}
|
||||
if (bytes)
|
||||
circlebuf_pop_front(buf, data, bytes);
|
||||
if (read_bytes)
|
||||
*read_bytes = bytes;
|
||||
|
||||
wait_size = 0;
|
||||
pos.QuadPart += bytes;
|
||||
return hr;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::Write(const void *, ULONG bytes,
|
||||
ULONG*)
|
||||
{
|
||||
debugfunc("data, %lu, written_bytes", bytes);
|
||||
UNUSED_PARAMETER(bytes);
|
||||
|
||||
return STG_E_INVALIDFUNCTION;
|
||||
}
|
||||
|
||||
// IStream methods
|
||||
|
||||
STDMETHODIMP CaptionStream::Seek(LARGE_INTEGER move, DWORD origin,
|
||||
ULARGE_INTEGER *new_pos)
|
||||
{
|
||||
debugfunc("%lld, %lx, new_pos", move, origin);
|
||||
UNUSED_PARAMETER(move);
|
||||
UNUSED_PARAMETER(origin);
|
||||
|
||||
if (!new_pos)
|
||||
return E_POINTER;
|
||||
|
||||
if (origin != SEEK_CUR || move.QuadPart != 0)
|
||||
return E_NOTIMPL;
|
||||
|
||||
*new_pos = pos;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::SetSize(ULARGE_INTEGER new_size)
|
||||
{
|
||||
debugfunc("%llu", new_size);
|
||||
UNUSED_PARAMETER(new_size);
|
||||
return STG_E_INVALIDFUNCTION;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::CopyTo(IStream *stream, ULARGE_INTEGER bytes,
|
||||
ULARGE_INTEGER *read_bytes,
|
||||
ULARGE_INTEGER *written_bytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
debugfunc("stream, %llu, read_bytes, written_bytes", bytes);
|
||||
|
||||
if (!stream)
|
||||
return STG_E_INVALIDPOINTER;
|
||||
|
||||
ULONG written = 0;
|
||||
if (bytes.QuadPart > (ULONGLONG)buf->size)
|
||||
bytes.QuadPart = (ULONGLONG)buf->size;
|
||||
|
||||
lock_guard<mutex> lock(m);
|
||||
temp_buf.resize((size_t)bytes.QuadPart);
|
||||
circlebuf_peek_front(buf, &temp_buf[0], (size_t)bytes.QuadPart);
|
||||
|
||||
hr = stream->Write(temp_buf.data(), (ULONG)bytes.QuadPart, &written);
|
||||
|
||||
if (read_bytes)
|
||||
*read_bytes = bytes;
|
||||
if (written_bytes)
|
||||
written_bytes->QuadPart = written;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::Commit(DWORD commit_flags)
|
||||
{
|
||||
debugfunc("%lx", commit_flags);
|
||||
UNUSED_PARAMETER(commit_flags);
|
||||
/* TODO? */
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::Revert(void)
|
||||
{
|
||||
debugfunc("");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::LockRegion(ULARGE_INTEGER offset,
|
||||
ULARGE_INTEGER size, DWORD type)
|
||||
{
|
||||
debugfunc("%llu, %llu, %ld", offset, size, type);
|
||||
UNUSED_PARAMETER(offset);
|
||||
UNUSED_PARAMETER(size);
|
||||
UNUSED_PARAMETER(type);
|
||||
/* TODO? */
|
||||
return STG_E_INVALIDFUNCTION;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::UnlockRegion(ULARGE_INTEGER offset,
|
||||
ULARGE_INTEGER size, DWORD type)
|
||||
{
|
||||
debugfunc("%llu, %llu, %ld", offset, size, type);
|
||||
UNUSED_PARAMETER(offset);
|
||||
UNUSED_PARAMETER(size);
|
||||
UNUSED_PARAMETER(type);
|
||||
/* TODO? */
|
||||
return STG_E_INVALIDFUNCTION;
|
||||
}
|
||||
|
||||
static const wchar_t *stat_name = L"Caption stream";
|
||||
|
||||
STDMETHODIMP CaptionStream::Stat(STATSTG *stg, DWORD flag)
|
||||
{
|
||||
debugfunc("stg, %lu", flag);
|
||||
|
||||
if (!stg)
|
||||
return E_POINTER;
|
||||
|
||||
lock_guard<mutex> lock(m);
|
||||
*stg = {};
|
||||
stg->type = STGTY_STREAM;
|
||||
stg->cbSize.QuadPart = (ULONGLONG)buf->size;
|
||||
|
||||
if (flag == STATFLAG_DEFAULT) {
|
||||
stg->pwcsName = (wchar_t*)CoTaskMemAlloc(sizeof(stat_name));
|
||||
memcpy(stg->pwcsName, stat_name, sizeof(stat_name));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::Clone(IStream **stream)
|
||||
{
|
||||
debugfunc("stream");
|
||||
*stream = nullptr;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// ISpStreamFormat methods
|
||||
|
||||
STDMETHODIMP CaptionStream::GetFormat(GUID *guid,
|
||||
WAVEFORMATEX **co_mem_wfex_out)
|
||||
{
|
||||
debugfunc("guid, co_mem_wfex_out");
|
||||
|
||||
if (!guid || !co_mem_wfex_out)
|
||||
return E_POINTER;
|
||||
|
||||
if (format.wFormatTag == 0) {
|
||||
*co_mem_wfex_out = nullptr;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void *wfex = CoTaskMemAlloc(sizeof(format));
|
||||
memcpy(wfex, &format, sizeof(format));
|
||||
|
||||
*co_mem_wfex_out = (WAVEFORMATEX*)wfex;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// ISpAudio methods
|
||||
|
||||
STDMETHODIMP CaptionStream::SetState(SPAUDIOSTATE state_, ULONGLONG)
|
||||
{
|
||||
debugfunc("%lu, reserved", state_);
|
||||
state = state_;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::SetFormat(REFGUID guid_ref,
|
||||
const WAVEFORMATEX *wfex)
|
||||
{
|
||||
debugfunc("guid, wfex");
|
||||
if (!wfex)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (guid_ref == SPDFID_WaveFormatEx) {
|
||||
lock_guard<mutex> lock(m);
|
||||
memcpy(&format, wfex, sizeof(format));
|
||||
resampler.Reset(wfex);
|
||||
|
||||
/* 50 msec */
|
||||
DWORD size = format.nSamplesPerSec / 20;
|
||||
DWORD byte_size = size * format.nBlockAlign;
|
||||
circlebuf_reserve(buf, (size_t)byte_size);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::GetStatus(SPAUDIOSTATUS *status)
|
||||
{
|
||||
debugfunc("status");
|
||||
|
||||
if (!status)
|
||||
return E_POINTER;
|
||||
|
||||
/* TODO? */
|
||||
lock_guard<mutex> lock(m);
|
||||
*status = {};
|
||||
status->cbNonBlockingIO = (ULONG)buf->size;
|
||||
status->State = state;
|
||||
status->CurSeekPos = pos.QuadPart;
|
||||
status->CurDevicePos = write_pos;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info_)
|
||||
{
|
||||
debugfunc("buf_info");
|
||||
|
||||
/* TODO */
|
||||
buf_info = *buf_info_;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::GetBufferInfo(SPAUDIOBUFFERINFO *buf_info_)
|
||||
{
|
||||
debugfunc("buf_info");
|
||||
if (!buf_info_)
|
||||
return E_POINTER;
|
||||
|
||||
*buf_info_ = buf_info;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::GetDefaultFormat(GUID *format,
|
||||
WAVEFORMATEX **co_mem_wfex_out)
|
||||
{
|
||||
debugfunc("format, co_mem_wfex_out");
|
||||
|
||||
if (!format || !co_mem_wfex_out)
|
||||
return E_POINTER;
|
||||
|
||||
void *wfex = CoTaskMemAlloc(sizeof(format));
|
||||
memcpy(wfex, &format, sizeof(format));
|
||||
|
||||
*format = SPDFID_WaveFormatEx;
|
||||
*co_mem_wfex_out = (WAVEFORMATEX*)wfex;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP_(HANDLE) CaptionStream::EventHandle(void)
|
||||
{
|
||||
debugfunc("");
|
||||
return event;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::GetVolumeLevel(ULONG *level)
|
||||
{
|
||||
debugfunc("level");
|
||||
if (!level)
|
||||
return E_POINTER;
|
||||
|
||||
*level = vol;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::SetVolumeLevel(ULONG level)
|
||||
{
|
||||
debugfunc("%lu", level);
|
||||
vol = level;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::GetBufferNotifySize(ULONG *size)
|
||||
{
|
||||
debugfunc("size");
|
||||
if (!size)
|
||||
return E_POINTER;
|
||||
*size = notify_size;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
STDMETHODIMP CaptionStream::SetBufferNotifySize(ULONG size)
|
||||
{
|
||||
debugfunc("%lu", size);
|
||||
notify_size = size;
|
||||
return S_OK;
|
||||
}
|
||||
119
UI/frontend-plugins/frontend-tools/captions-stream.hpp
Normal file
119
UI/frontend-plugins/frontend-tools/captions-stream.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include <windows.h>
|
||||
#include <sapi.h>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <obs.h>
|
||||
#include <media-io/audio-resampler.h>
|
||||
#include <util/circlebuf.h>
|
||||
#include <util/windows/WinHandle.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
class CircleBuf {
|
||||
circlebuf buf = {};
|
||||
public:
|
||||
inline ~CircleBuf() {circlebuf_free(&buf);}
|
||||
inline operator circlebuf*() {return &buf;}
|
||||
inline circlebuf *operator->() {return &buf;}
|
||||
};
|
||||
|
||||
class Resampler {
|
||||
audio_resampler_t *resampler = nullptr;
|
||||
|
||||
public:
|
||||
inline void Reset(const WAVEFORMATEX *wfex)
|
||||
{
|
||||
const struct audio_output_info *aoi =
|
||||
audio_output_get_info(obs_get_audio());
|
||||
|
||||
struct resample_info src;
|
||||
src.samples_per_sec = aoi->samples_per_sec;
|
||||
src.format = aoi->format;
|
||||
src.speakers = aoi->speakers;
|
||||
|
||||
struct resample_info dst;
|
||||
dst.samples_per_sec = uint32_t(wfex->nSamplesPerSec);
|
||||
dst.format = AUDIO_FORMAT_16BIT;
|
||||
dst.speakers = (enum speaker_layout)wfex->nChannels;
|
||||
|
||||
if (resampler)
|
||||
audio_resampler_destroy(resampler);
|
||||
resampler = audio_resampler_create(&dst, &src);
|
||||
}
|
||||
|
||||
inline ~Resampler() {audio_resampler_destroy(resampler);}
|
||||
inline operator audio_resampler_t*() {return resampler;}
|
||||
};
|
||||
|
||||
class CaptionStream : public ISpAudio {
|
||||
volatile long refs = 1;
|
||||
SPAUDIOBUFFERINFO buf_info = {};
|
||||
ULONG notify_size = 0;
|
||||
SPAUDIOSTATE state;
|
||||
WinHandle event;
|
||||
ULONG vol = 0;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex m;
|
||||
std::vector<int16_t> temp_buf;
|
||||
WAVEFORMATEX format = {};
|
||||
Resampler resampler;
|
||||
|
||||
CircleBuf buf;
|
||||
ULONG wait_size = 0;
|
||||
DWORD samplerate = 0;
|
||||
ULARGE_INTEGER pos = {};
|
||||
ULONGLONG write_pos = 0;
|
||||
|
||||
public:
|
||||
CaptionStream(DWORD samplerate);
|
||||
|
||||
void Stop();
|
||||
void PushAudio(const struct audio_data *audio_data, bool muted);
|
||||
|
||||
// IUnknown methods
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) override;
|
||||
STDMETHODIMP_(ULONG) AddRef() override;
|
||||
STDMETHODIMP_(ULONG) Release() override;
|
||||
|
||||
// ISequentialStream methods
|
||||
STDMETHODIMP Read(void *data, ULONG bytes, ULONG *read_bytes) override;
|
||||
STDMETHODIMP Write(const void *data, ULONG bytes, ULONG *written_bytes)
|
||||
override;
|
||||
|
||||
// IStream methods
|
||||
STDMETHODIMP Seek(LARGE_INTEGER move, DWORD origin,
|
||||
ULARGE_INTEGER *new_pos) override;
|
||||
STDMETHODIMP SetSize(ULARGE_INTEGER new_size) override;
|
||||
STDMETHODIMP CopyTo(IStream *stream, ULARGE_INTEGER bytes,
|
||||
ULARGE_INTEGER *read_bytes,
|
||||
ULARGE_INTEGER *written_bytes) override;
|
||||
STDMETHODIMP Commit(DWORD commit_flags) override;
|
||||
STDMETHODIMP Revert(void) override;
|
||||
STDMETHODIMP LockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size,
|
||||
DWORD type) override;
|
||||
STDMETHODIMP UnlockRegion(ULARGE_INTEGER offset, ULARGE_INTEGER size,
|
||||
DWORD type) override;
|
||||
STDMETHODIMP Stat(STATSTG *stg, DWORD flags) override;
|
||||
STDMETHODIMP Clone(IStream **stream) override;
|
||||
|
||||
// ISpStreamFormat methods
|
||||
STDMETHODIMP GetFormat(GUID *guid, WAVEFORMATEX **co_mem_wfex_out)
|
||||
override;
|
||||
|
||||
// ISpAudio methods
|
||||
STDMETHODIMP SetState(SPAUDIOSTATE state, ULONGLONG reserved) override;
|
||||
STDMETHODIMP SetFormat(REFGUID guid_ref, const WAVEFORMATEX *wfex)
|
||||
override;
|
||||
STDMETHODIMP GetStatus(SPAUDIOSTATUS *status) override;
|
||||
STDMETHODIMP SetBufferInfo(const SPAUDIOBUFFERINFO *buf_info) override;
|
||||
STDMETHODIMP GetBufferInfo(SPAUDIOBUFFERINFO *buf_info) override;
|
||||
STDMETHODIMP GetDefaultFormat(GUID *format,
|
||||
WAVEFORMATEX **co_mem_wfex_out) override;
|
||||
STDMETHODIMP_(HANDLE) EventHandle(void) override;
|
||||
STDMETHODIMP GetVolumeLevel(ULONG *level) override;
|
||||
STDMETHODIMP SetVolumeLevel(ULONG level) override;
|
||||
STDMETHODIMP GetBufferNotifySize(ULONG *size) override;
|
||||
STDMETHODIMP SetBufferNotifySize(ULONG size) override;
|
||||
};
|
||||
533
UI/frontend-plugins/frontend-tools/captions.cpp
Normal file
533
UI/frontend-plugins/frontend-tools/captions.cpp
Normal file
|
|
@ -0,0 +1,533 @@
|
|||
#include <obs-frontend-api.h>
|
||||
#include "captions-stream.hpp"
|
||||
#include "captions.hpp"
|
||||
#include "tool-helpers.hpp"
|
||||
#include <sphelper.h>
|
||||
#include <util/dstr.hpp>
|
||||
#include <util/platform.h>
|
||||
#include <util/windows/HRError.hpp>
|
||||
#include <util/windows/ComPtr.hpp>
|
||||
#include <util/windows/CoTaskMemPtr.hpp>
|
||||
#include <util/threading.h>
|
||||
#include <obs-module.h>
|
||||
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#define do_log(type, format, ...) blog(type, "[Captions] " format, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define error(format, ...) do_log(LOG_ERROR, format, ##__VA_ARGS__)
|
||||
#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__)
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct obs_captions {
|
||||
thread th;
|
||||
recursive_mutex m;
|
||||
WinHandle stop_event;
|
||||
|
||||
string source_name;
|
||||
OBSWeakSource source;
|
||||
LANGID lang_id;
|
||||
|
||||
void main_thread();
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
inline obs_captions() :
|
||||
stop_event(CreateEvent(nullptr, false, false, nullptr)),
|
||||
lang_id(GetUserDefaultUILanguage())
|
||||
{
|
||||
}
|
||||
|
||||
inline ~obs_captions() {stop();}
|
||||
};
|
||||
|
||||
static obs_captions *captions = nullptr;
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
struct locale_info {
|
||||
DStr name;
|
||||
LANGID id;
|
||||
|
||||
inline locale_info() {}
|
||||
inline locale_info(const locale_info &) = delete;
|
||||
inline locale_info(locale_info &&li)
|
||||
: name(std::move(li.name)),
|
||||
id(li.id)
|
||||
{}
|
||||
};
|
||||
|
||||
static void get_valid_locale_names(vector<locale_info> &names);
|
||||
static bool valid_lang(LANGID id);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
CaptionsDialog::CaptionsDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui_CaptionsDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
lock_guard<recursive_mutex> lock(captions->m);
|
||||
|
||||
auto cb = [this] (obs_source_t *source)
|
||||
{
|
||||
uint32_t caps = obs_source_get_output_flags(source);
|
||||
QString name = obs_source_get_name(source);
|
||||
|
||||
if (caps & OBS_SOURCE_AUDIO)
|
||||
ui->source->addItem(name);
|
||||
|
||||
OBSWeakSource weak = OBSGetWeakRef(source);
|
||||
if (weak == captions->source)
|
||||
ui->source->setCurrentText(name);
|
||||
return true;
|
||||
};
|
||||
|
||||
using cb_t = decltype(cb);
|
||||
|
||||
ui->source->blockSignals(true);
|
||||
ui->source->addItem(QStringLiteral(""));
|
||||
ui->source->setCurrentIndex(0);
|
||||
obs_enum_sources([] (void *data, obs_source_t *source) {
|
||||
return (*static_cast<cb_t*>(data))(source);}, &cb);
|
||||
ui->source->blockSignals(false);
|
||||
|
||||
ui->enable->blockSignals(true);
|
||||
ui->enable->setChecked(captions->th.joinable());
|
||||
ui->enable->blockSignals(false);
|
||||
|
||||
vector<locale_info> locales;
|
||||
get_valid_locale_names(locales);
|
||||
|
||||
bool set_language = false;
|
||||
|
||||
ui->language->blockSignals(true);
|
||||
for (int idx = 0; idx < (int)locales.size(); idx++) {
|
||||
locale_info &locale = locales[idx];
|
||||
|
||||
ui->language->addItem(locale.name->array, (int)locale.id);
|
||||
|
||||
if (locale.id == captions->lang_id) {
|
||||
ui->language->setCurrentIndex(idx);
|
||||
set_language = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!set_language && locales.size())
|
||||
ui->language->setCurrentIndex(0);
|
||||
|
||||
ui->language->blockSignals(false);
|
||||
|
||||
if (!locales.size()) {
|
||||
ui->source->setEnabled(false);
|
||||
ui->enable->setEnabled(false);
|
||||
ui->language->setEnabled(false);
|
||||
|
||||
} else if (!set_language) {
|
||||
bool started = captions->th.joinable();
|
||||
if (started)
|
||||
captions->stop();
|
||||
|
||||
captions->m.lock();
|
||||
captions->lang_id = locales[0].id;
|
||||
captions->m.unlock();
|
||||
|
||||
if (started)
|
||||
captions->start();
|
||||
}
|
||||
}
|
||||
|
||||
void CaptionsDialog::on_source_currentIndexChanged(int)
|
||||
{
|
||||
bool started = captions->th.joinable();
|
||||
if (started)
|
||||
captions->stop();
|
||||
|
||||
captions->m.lock();
|
||||
captions->source_name = ui->source->currentText().toUtf8().constData();
|
||||
captions->source = GetWeakSourceByName(captions->source_name.c_str());
|
||||
captions->m.unlock();
|
||||
|
||||
if (started)
|
||||
captions->start();
|
||||
}
|
||||
|
||||
void CaptionsDialog::on_enable_clicked(bool checked)
|
||||
{
|
||||
if (checked)
|
||||
captions->start();
|
||||
else
|
||||
captions->stop();
|
||||
}
|
||||
|
||||
void CaptionsDialog::on_language_currentIndexChanged(int)
|
||||
{
|
||||
bool started = captions->th.joinable();
|
||||
if (started)
|
||||
captions->stop();
|
||||
|
||||
captions->m.lock();
|
||||
captions->lang_id = (LANGID)ui->language->currentData().toInt();
|
||||
captions->m.unlock();
|
||||
|
||||
if (started)
|
||||
captions->start();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
void obs_captions::main_thread()
|
||||
try {
|
||||
ComPtr<CaptionStream> audio;
|
||||
ComPtr<ISpObjectToken> token;
|
||||
ComPtr<ISpRecoGrammar> grammar;
|
||||
ComPtr<ISpRecognizer> recognizer;
|
||||
ComPtr<ISpRecoContext> context;
|
||||
HRESULT hr;
|
||||
|
||||
auto cb = [&] (const struct audio_data *audio_data,
|
||||
bool muted)
|
||||
{
|
||||
audio->PushAudio(audio_data, muted);
|
||||
};
|
||||
|
||||
using cb_t = decltype(cb);
|
||||
|
||||
auto pre_cb = [] (void *param, obs_source_t*,
|
||||
const struct audio_data *audio_data, bool muted)
|
||||
{
|
||||
return (*static_cast<cb_t*>(param))(audio_data, muted);
|
||||
};
|
||||
|
||||
os_set_thread_name(__FUNCTION__);
|
||||
|
||||
CoInitialize(nullptr);
|
||||
|
||||
wchar_t lang_str[32];
|
||||
_snwprintf(lang_str, 31, L"language=%x", (int)captions->lang_id);
|
||||
|
||||
hr = SpFindBestToken(SPCAT_RECOGNIZERS, lang_str, nullptr, &token);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SpFindBestToken failed", hr);
|
||||
|
||||
hr = CoCreateInstance(CLSID_SpInprocRecognizer, nullptr, CLSCTX_ALL,
|
||||
__uuidof(ISpRecognizer), (void**)&recognizer);
|
||||
if (FAILED(hr))
|
||||
throw HRError("CoCreateInstance for recognizer failed", hr);
|
||||
|
||||
hr = recognizer->SetRecognizer(token);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetRecognizer failed", hr);
|
||||
|
||||
hr = recognizer->SetRecoState(SPRST_INACTIVE);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetRecoState(SPRST_INACTIVE) failed", hr);
|
||||
|
||||
hr = recognizer->CreateRecoContext(&context);
|
||||
if (FAILED(hr))
|
||||
throw HRError("CreateRecoContext failed", hr);
|
||||
|
||||
ULONGLONG interest = SPFEI(SPEI_RECOGNITION) |
|
||||
SPFEI(SPEI_END_SR_STREAM);
|
||||
hr = context->SetInterest(interest, interest);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetInterest failed", hr);
|
||||
|
||||
HANDLE notify;
|
||||
|
||||
hr = context->SetNotifyWin32Event();
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetNotifyWin32Event", hr);
|
||||
|
||||
notify = context->GetNotifyEventHandle();
|
||||
if (notify == INVALID_HANDLE_VALUE)
|
||||
throw HRError("GetNotifyEventHandle failed", E_NOINTERFACE);
|
||||
|
||||
size_t sample_rate = audio_output_get_sample_rate(obs_get_audio());
|
||||
audio = new CaptionStream((DWORD)sample_rate);
|
||||
audio->Release();
|
||||
|
||||
hr = recognizer->SetInput(audio, false);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetInput failed", hr);
|
||||
|
||||
hr = context->CreateGrammar(1, &grammar);
|
||||
if (FAILED(hr))
|
||||
throw HRError("CreateGrammar failed", hr);
|
||||
|
||||
hr = grammar->LoadDictation(nullptr, SPLO_STATIC);
|
||||
if (FAILED(hr))
|
||||
throw HRError("LoadDictation failed", hr);
|
||||
|
||||
hr = grammar->SetDictationState(SPRS_ACTIVE);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetDictationState failed", hr);
|
||||
|
||||
hr = recognizer->SetRecoState(SPRST_ACTIVE);
|
||||
if (FAILED(hr))
|
||||
throw HRError("SetRecoState(SPRST_ACTIVE) failed", hr);
|
||||
|
||||
HANDLE events[] = {notify, stop_event};
|
||||
|
||||
{
|
||||
captions->source = GetWeakSourceByName(
|
||||
captions->source_name.c_str());
|
||||
OBSSource strong = OBSGetStrongRef(source);
|
||||
if (strong)
|
||||
obs_source_add_audio_capture_callback(strong,
|
||||
pre_cb, &cb);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE);
|
||||
if (ret != WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
CSpEvent event;
|
||||
bool exit = false;
|
||||
|
||||
while (event.GetFrom(context) == S_OK) {
|
||||
if (event.eEventId == SPEI_RECOGNITION) {
|
||||
ISpRecoResult *result = event.RecoResult();
|
||||
|
||||
CoTaskMemPtr<wchar_t> text;
|
||||
hr = result->GetText((ULONG)-1, (ULONG)-1,
|
||||
true, &text, nullptr);
|
||||
if (FAILED(hr))
|
||||
continue;
|
||||
|
||||
char text_utf8[512];
|
||||
os_wcs_to_utf8(text, 0, text_utf8, 512);
|
||||
|
||||
obs_output_t *output =
|
||||
obs_frontend_get_streaming_output();
|
||||
if (output)
|
||||
obs_output_output_caption_text1(output,
|
||||
text_utf8);
|
||||
|
||||
debug("\"%s\"", text_utf8);
|
||||
|
||||
obs_output_release(output);
|
||||
|
||||
} else if (event.eEventId == SPEI_END_SR_STREAM) {
|
||||
exit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exit)
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
OBSSource strong = OBSGetStrongRef(source);
|
||||
if (strong)
|
||||
obs_source_remove_audio_capture_callback(strong,
|
||||
pre_cb, &cb);
|
||||
}
|
||||
|
||||
audio->Stop();
|
||||
|
||||
CoUninitialize();
|
||||
|
||||
} catch (HRError err) {
|
||||
error("%s failed: %s (%lX)", __FUNCTION__, err.str, err.hr);
|
||||
CoUninitialize();
|
||||
captions->th.detach();
|
||||
}
|
||||
|
||||
void obs_captions::start()
|
||||
{
|
||||
if (!captions->th.joinable()) {
|
||||
ResetEvent(captions->stop_event);
|
||||
|
||||
if (valid_lang(captions->lang_id))
|
||||
captions->th = thread([] () {captions->main_thread();});
|
||||
}
|
||||
}
|
||||
|
||||
void obs_captions::stop()
|
||||
{
|
||||
if (!captions->th.joinable())
|
||||
return;
|
||||
|
||||
SetEvent(captions->stop_event);
|
||||
captions->th.join();
|
||||
}
|
||||
|
||||
static bool get_locale_name(LANGID id, char *out)
|
||||
{
|
||||
wchar_t name[256];
|
||||
|
||||
int size = GetLocaleInfoW(id, LOCALE_SENGLISHLANGUAGENAME, name, 256);
|
||||
if (size <= 0)
|
||||
return false;
|
||||
|
||||
os_wcs_to_utf8(name, 0, out, 256);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool valid_lang(LANGID id)
|
||||
{
|
||||
ComPtr<ISpObjectToken> token;
|
||||
wchar_t lang_str[32];
|
||||
HRESULT hr;
|
||||
|
||||
_snwprintf(lang_str, 31, L"language=%x", (int)id);
|
||||
|
||||
hr = SpFindBestToken(SPCAT_RECOGNIZERS, lang_str, nullptr, &token);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
static void get_valid_locale_names(vector<locale_info> &locales)
|
||||
{
|
||||
locale_info cur;
|
||||
char locale_name[256];
|
||||
|
||||
static const LANGID default_locales[] = {
|
||||
0x0409,
|
||||
0x0401,
|
||||
0x0402,
|
||||
0x0403,
|
||||
0x0404,
|
||||
0x0405,
|
||||
0x0406,
|
||||
0x0407,
|
||||
0x0408,
|
||||
0x040a,
|
||||
0x040b,
|
||||
0x040c,
|
||||
0x040d,
|
||||
0x040e,
|
||||
0x040f,
|
||||
0x0410,
|
||||
0x0411,
|
||||
0x0412,
|
||||
0x0413,
|
||||
0x0414,
|
||||
0x0415,
|
||||
0x0416,
|
||||
0x0417,
|
||||
0x0418,
|
||||
0x0419,
|
||||
0x041a,
|
||||
0
|
||||
};
|
||||
|
||||
/* ---------------------------------- */
|
||||
|
||||
LANGID def_id = GetUserDefaultUILanguage();
|
||||
LANGID id = def_id;
|
||||
if (valid_lang(id) && get_locale_name(id, locale_name)) {
|
||||
dstr_copy(cur.name, obs_module_text(
|
||||
"Captions.CurrentSystemLanguage"));
|
||||
dstr_replace(cur.name, "%1", locale_name);
|
||||
cur.id = id;
|
||||
|
||||
locales.push_back(std::move(cur));
|
||||
}
|
||||
|
||||
/* ---------------------------------- */
|
||||
|
||||
const LANGID *locale = default_locales;
|
||||
|
||||
while (*locale) {
|
||||
id = *locale;
|
||||
|
||||
if (id != def_id &&
|
||||
valid_lang(id) &&
|
||||
get_locale_name(id, locale_name)) {
|
||||
|
||||
dstr_copy(cur.name, locale_name);
|
||||
cur.id = id;
|
||||
|
||||
locales.push_back(std::move(cur));
|
||||
}
|
||||
|
||||
locale++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
extern "C" void FreeCaptions()
|
||||
{
|
||||
delete captions;
|
||||
captions = nullptr;
|
||||
}
|
||||
|
||||
static void obs_event(enum obs_frontend_event event, void *)
|
||||
{
|
||||
if (event == OBS_FRONTEND_EVENT_EXIT)
|
||||
FreeCaptions();
|
||||
}
|
||||
|
||||
static void save_caption_data(obs_data_t *save_data, bool saving, void*)
|
||||
{
|
||||
if (saving) {
|
||||
lock_guard<recursive_mutex> lock(captions->m);
|
||||
obs_data_t *obj = obs_data_create();
|
||||
|
||||
obs_data_set_string(obj, "source",
|
||||
captions->source_name.c_str());
|
||||
obs_data_set_bool(obj, "enabled", captions->th.joinable());
|
||||
obs_data_set_int(obj, "lang_id", captions->lang_id);
|
||||
|
||||
obs_data_set_obj(save_data, "captions", obj);
|
||||
obs_data_release(obj);
|
||||
} else {
|
||||
captions->stop();
|
||||
|
||||
captions->m.lock();
|
||||
|
||||
obs_data_t *obj = obs_data_get_obj(save_data, "captions");
|
||||
if (!obj)
|
||||
obj = obs_data_create();
|
||||
|
||||
obs_data_set_default_int(obj, "lang_id",
|
||||
GetUserDefaultUILanguage());
|
||||
|
||||
bool enabled = obs_data_get_bool(obj, "enabled");
|
||||
captions->source_name = obs_data_get_string(obj, "source");
|
||||
captions->lang_id = (int)obs_data_get_int(obj, "lang_id");
|
||||
captions->source = GetWeakSourceByName(
|
||||
captions->source_name.c_str());
|
||||
obs_data_release(obj);
|
||||
|
||||
captions->m.unlock();
|
||||
|
||||
if (enabled)
|
||||
captions->start();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void InitCaptions()
|
||||
{
|
||||
QAction *action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
||||
obs_module_text("Captions"));
|
||||
|
||||
captions = new obs_captions;
|
||||
|
||||
auto cb = [] ()
|
||||
{
|
||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||
|
||||
QWidget *window =
|
||||
(QWidget*)obs_frontend_get_main_window();
|
||||
|
||||
CaptionsDialog dialog(window);
|
||||
dialog.exec();
|
||||
|
||||
obs_frontend_pop_ui_translation();
|
||||
};
|
||||
|
||||
obs_frontend_add_save_callback(save_caption_data, nullptr);
|
||||
obs_frontend_add_event_callback(obs_event, nullptr);
|
||||
|
||||
action->connect(action, &QAction::triggered, cb);
|
||||
}
|
||||
20
UI/frontend-plugins/frontend-tools/captions.hpp
Normal file
20
UI/frontend-plugins/frontend-tools/captions.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
#include "ui_captions.h"
|
||||
|
||||
class CaptionsDialog : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
std::unique_ptr<Ui_CaptionsDialog> ui;
|
||||
|
||||
public:
|
||||
CaptionsDialog(QWidget *parent);
|
||||
|
||||
public slots:
|
||||
void on_source_currentIndexChanged(int idx);
|
||||
void on_enable_clicked(bool checked);
|
||||
void on_language_currentIndexChanged(int idx);
|
||||
};
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="No canviar"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Canvia a:"
|
||||
SceneSwitcher.CheckInterval="Comprova el títol de la finestra activa cada:"
|
||||
SceneSwitcher.ActiveOrNotActive="El canviador de escena està:"
|
||||
InvalidRegex.Title="Expressió regular no vàlida"
|
||||
InvalidRegex.Text="La expressió regular que heu introduït no es vàlida."
|
||||
Active="Actiu"
|
||||
Inactive="Inactiu"
|
||||
Start="Inicia"
|
||||
Stop="Atura"
|
||||
|
||||
Captions="Subtítols (Experimental)"
|
||||
Captions.AudioSource="Font d'àudio"
|
||||
Captions.CurrentSystemLanguage="Idioma actual del sistema (%1)"
|
||||
|
||||
OutputTimer="Temporitzador de sortida"
|
||||
OutputTimer.Stream="Atura la transmissió després de:"
|
||||
OutputTimer.Record="Atura la gravació després de:"
|
||||
OutputTimer.Stream.StoppingIn="La transmissió s'aturarà en:"
|
||||
OutputTimer.Record.StoppingIn="La gravació s'aturarà en:"
|
||||
OutputTimer.Stream.EnableEverytime="Activa el temporitzador en cada transmissió"
|
||||
OutputTimer.Record.EnableEverytime="Activa el temporitzador en cada enregistrament"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Nepřepínat"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Přepnout na:"
|
||||
SceneSwitcher.CheckInterval="Kontrolovat titulek aktivního okna každých:"
|
||||
SceneSwitcher.ActiveOrNotActive="Přepínač scén je:"
|
||||
InvalidRegex.Title="Chybný regulární výraz"
|
||||
InvalidRegex.Text="Zadaný regulární výraz je chybný."
|
||||
Active="Aktivní"
|
||||
Inactive="Neaktivní"
|
||||
Start="Spustit"
|
||||
Stop="Zastavit"
|
||||
|
||||
Captions="Titulky (experiment.)"
|
||||
Captions.AudioSource="Zdroj zvuku"
|
||||
Captions.CurrentSystemLanguage="Aktuální systémový jazyk (%1)"
|
||||
|
||||
OutputTimer="Časovač"
|
||||
OutputTimer.Stream="Přestat vysílat po:"
|
||||
OutputTimer.Record="Přestat nahrávat po:"
|
||||
OutputTimer.Stream.StoppingIn="Vysílání se zastaví za:"
|
||||
OutputTimer.Record.StoppingIn="Nahrávání se zastaví za:"
|
||||
OutputTimer.Stream.EnableEverytime="Pokaždé povolit časovač vysílání"
|
||||
OutputTimer.Record.EnableEverytime="Pokaždé povolit časovač nahrávání"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,25 @@
|
|||
SceneSwitcher="Automatisk sceneskifter"
|
||||
SceneSwitcher.OnNoMatch="Når intet vindue svarer til:"
|
||||
SceneSwitcher="Automatisk sceneomskifter"
|
||||
SceneSwitcher.OnNoMatch="Når intet vindue matcher:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Skift ikke"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Skift til:"
|
||||
SceneSwitcher.CheckInterval="Kontroller aktivt vinduestitel hvert:"
|
||||
SceneSwitcher.ActiveOrNotActive="Sceneskifter er:"
|
||||
SceneSwitcher.ActiveOrNotActive="Sceneomskifter er:"
|
||||
InvalidRegex.Title="Ugyldigt regulært udtryk"
|
||||
InvalidRegex.Text="Det af dig angivne regulære udtryk er ugyldigt."
|
||||
Active="Aktiv"
|
||||
Inactive="Inaktiv"
|
||||
Start="Start"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Undertekster (eksperimentel)"
|
||||
Captions.AudioSource="Lydkilde"
|
||||
Captions.CurrentSystemLanguage="Aktuelt systemsprog (%1)"
|
||||
|
||||
OutputTimer="Output-timer"
|
||||
OutputTimer.Stream="Stands streaming efter:"
|
||||
OutputTimer.Record="Stands optagelse efter:"
|
||||
OutputTimer.Stream.StoppingIn="Streaming standser om:"
|
||||
OutputTimer.Record.StoppingIn="Streaming standser om:"
|
||||
OutputTimer.Stream.EnableEverytime="Aktivér streaming-timer hver gang"
|
||||
OutputTimer.Record.EnableEverytime="Aktivér optage-timer hver gang"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Nicht wechseln"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Wechseln zu:"
|
||||
SceneSwitcher.CheckInterval="Titel des aktiven Fensters überprüfen alle:"
|
||||
SceneSwitcher.ActiveOrNotActive="Szenenwechsler ist:"
|
||||
InvalidRegex.Title="Ungültiger regulärer Ausdruck"
|
||||
InvalidRegex.Text="Der reguläre Ausdruck, den Sie eingegeben haben, ist ungültig."
|
||||
Active="Aktiv"
|
||||
Inactive="Inaktiv"
|
||||
Start="Start"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Untertitel (experimentell)"
|
||||
Captions.AudioSource="Audioquelle"
|
||||
Captions.CurrentSystemLanguage="Aktuelle Systemsprache (%1)"
|
||||
|
||||
OutputTimer="Ausgabetimer"
|
||||
OutputTimer.Stream="Stoppe Stream nach:"
|
||||
OutputTimer.Record="Stoppe Aufnahme nach:"
|
||||
OutputTimer.Stream.StoppingIn="Stream stoppt in:"
|
||||
OutputTimer.Record.StoppingIn="Aufnahme stoppt in:"
|
||||
OutputTimer.Stream.EnableEverytime="Streaming-Timer jedes Mal aktivieren"
|
||||
OutputTimer.Record.EnableEverytime="Aufnahme-Timer jedes Mal aktivieren"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,21 @@ SceneSwitcher.OnNoMatch.DontSwitch="Don't switch"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Switch to:"
|
||||
SceneSwitcher.CheckInterval="Check active window title every:"
|
||||
SceneSwitcher.ActiveOrNotActive="Scene Switcher is:"
|
||||
InvalidRegex.Title="Invalid Regular Expression"
|
||||
InvalidRegex.Text="The regular expression that you entered is invalid."
|
||||
Active="Active"
|
||||
Inactive="Inactive"
|
||||
Start="Start"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Captions (Experimental)"
|
||||
Captions.AudioSource="Audio source"
|
||||
Captions.CurrentSystemLanguage="Current System Language (%1)"
|
||||
|
||||
OutputTimer="Output Timer"
|
||||
OutputTimer.Stream="Stop streaming after:"
|
||||
OutputTimer.Record="Stop recording after:"
|
||||
OutputTimer.Stream.StoppingIn="Streaming stopping in:"
|
||||
OutputTimer.Record.StoppingIn="Recording stopping in:"
|
||||
OutputTimer.Stream.EnableEverytime="Enable streaming timer every time"
|
||||
OutputTimer.Record.EnableEverytime="Enable recording timer every time"
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="No cambiar"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Cambiar a:"
|
||||
SceneSwitcher.CheckInterval="Comprobar el título de la ventana activa cada:"
|
||||
SceneSwitcher.ActiveOrNotActive="El cambiador de escena está:"
|
||||
InvalidRegex.Title="Expresión regular no válida"
|
||||
InvalidRegex.Text="La expresión regular que ha introducido no es válida."
|
||||
Active="Activo"
|
||||
Inactive="Inactivo"
|
||||
Start="Iniciar"
|
||||
Stop="Detener"
|
||||
|
||||
Captions="Subtítulos (Experimental)"
|
||||
Captions.AudioSource="Fuente de audio"
|
||||
Captions.CurrentSystemLanguage="Idioma actual del sistema (%1)"
|
||||
|
||||
OutputTimer="Temporizador de salida"
|
||||
OutputTimer.Stream="Detener la transmisión después de:"
|
||||
OutputTimer.Record="Detener la grabación después de:"
|
||||
OutputTimer.Stream.StoppingIn="Finalizando transmisión en:"
|
||||
OutputTimer.Record.StoppingIn="Finalizando grabación en:"
|
||||
OutputTimer.Stream.EnableEverytime="Activar temporizador en cada transmisión"
|
||||
OutputTimer.Record.EnableEverytime="Activar temporizador en cada grabación"
|
||||
|
||||
|
|
|
|||
21
UI/frontend-plugins/frontend-tools/data/locale/et-EE.ini
Normal file
21
UI/frontend-plugins/frontend-tools/data/locale/et-EE.ini
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
SceneSwitcher="Automaatne stseeni vahetaja"
|
||||
SceneSwitcher.OnNoMatch="Kui üksi aken ei sobi:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Ära vaheta"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Lülitu ümber:"
|
||||
SceneSwitcher.CheckInterval="Kontrollige aktiivse akna pealkiri iga:"
|
||||
SceneSwitcher.ActiveOrNotActive="Stseen vahetaja on:"
|
||||
Active="Aktiivne"
|
||||
Inactive="Inaktiivne"
|
||||
Start="Alusta"
|
||||
Stop="Lõpeta"
|
||||
|
||||
Captions="Subtiitrid (eksperimentaalne)"
|
||||
Captions.AudioSource="Heli allikas"
|
||||
Captions.CurrentSystemLanguage="Praegune süsteemi keel (%1)"
|
||||
|
||||
OutputTimer="Väljundi taimer"
|
||||
OutputTimer.Stream="Lõpeta voogedastus pärast:"
|
||||
OutputTimer.Record="Lõpeta voogedastus pärast:"
|
||||
OutputTimer.Stream.StoppingIn="Voogedastus lõppeb:"
|
||||
OutputTimer.Record.StoppingIn="Salvestamine lõppeb:"
|
||||
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Ez aldatu"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Aldatu hona:"
|
||||
SceneSwitcher.CheckInterval="Leiho aktiboaren titulua egiaztatzeko maiztasuna:"
|
||||
SceneSwitcher.ActiveOrNotActive="Eszena aldatzailea dago:"
|
||||
InvalidRegex.Title="Adierazpen erregular baliogabea"
|
||||
InvalidRegex.Text="Sartu duzun adierazpen erregularra baliogabea da."
|
||||
Active="Aktiboa"
|
||||
Inactive="Inaktiboa"
|
||||
Start="Hasi"
|
||||
Stop="Gelditu"
|
||||
|
||||
Captions="Epigrafeak (esperimentala)"
|
||||
Captions.AudioSource="Audio-iturburua"
|
||||
Captions.CurrentSystemLanguage="Sistemaren hizkuntza (%1)"
|
||||
|
||||
OutputTimer="Irteera tenporizadorea"
|
||||
OutputTimer.Stream="Gelditu transmisioa hau pasata:"
|
||||
OutputTimer.Record="Gelditu grabazioa hau pasata:"
|
||||
OutputTimer.Stream.StoppingIn="Transmisioa geldituko da: hau barru:"
|
||||
OutputTimer.Record.StoppingIn="Grabazioa geldituko da hau barru:"
|
||||
OutputTimer.Stream.EnableEverytime="Gaitu transmisio tenporizadorea aldiro"
|
||||
OutputTimer.Record.EnableEverytime="Gaitu grabazio tenporizadorea aldiro"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Älä vaihda"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Vaihda:"
|
||||
SceneSwitcher.CheckInterval="Tarkista aktiivisen ikkunan otsikko:"
|
||||
SceneSwitcher.ActiveOrNotActive="Skenen vaihtaja:"
|
||||
InvalidRegex.Title="Invalid Regular Expression"
|
||||
InvalidRegex.Text="The regular expression that you entered is invalid."
|
||||
Active="Aktiivinen"
|
||||
Inactive="Ei käytössä"
|
||||
Start="Käynnistä"
|
||||
Stop="Pysäytä"
|
||||
|
||||
Captions="Kuvatekstit (Experimental)"
|
||||
Captions.AudioSource="Äänilähde"
|
||||
Captions.CurrentSystemLanguage="Järjestelmän kieli (%1)"
|
||||
|
||||
OutputTimer="Ulostulo-ajastin"
|
||||
OutputTimer.Stream="Pysäyttää lähetyksen:"
|
||||
OutputTimer.Record="Pysäyttää tallennuksen:"
|
||||
OutputTimer.Stream.StoppingIn="Lähetys pysäytetään:"
|
||||
OutputTimer.Record.StoppingIn="Tallennus pysäytetään:"
|
||||
OutputTimer.Stream.EnableEverytime="Ota lähetysajastin käyttöön aina"
|
||||
OutputTimer.Record.EnableEverytime="Ota tallennusajastin käyttöön aina"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Ne rien faire"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Basculer vers :"
|
||||
SceneSwitcher.CheckInterval="Détecter le titre de la fenêtre active toutes les :"
|
||||
SceneSwitcher.ActiveOrNotActive="Etat du sélecteur automatique :"
|
||||
InvalidRegex.Title="Expression invalide"
|
||||
InvalidRegex.Text="L'expression régulière saisie est invalide."
|
||||
Active="Actif"
|
||||
Inactive="Inactif"
|
||||
Start="Démarrer"
|
||||
Stop="Arrêter"
|
||||
|
||||
Captions="Sous-titres (expérimental)"
|
||||
Captions.AudioSource="Source audio"
|
||||
Captions.CurrentSystemLanguage="Langue du système (%1)"
|
||||
|
||||
OutputTimer="Minuterie des sorties"
|
||||
OutputTimer.Stream="Arrêter le streaming dans :"
|
||||
OutputTimer.Record="Arrêter l'enregistrement dans :"
|
||||
OutputTimer.Stream.StoppingIn="Arrêt du streaming dans :"
|
||||
OutputTimer.Record.StoppingIn="Arrêt de l'enregistrement dans :"
|
||||
OutputTimer.Stream.EnableEverytime="Activer le minuteur automatiquement à chaque stream"
|
||||
OutputTimer.Record.EnableEverytime="Activer le minuteur automatiquement à chaque enregistrement"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,19 @@ SceneSwitcher.OnNoMatch.DontSwitch="Ne menjaj"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Promeni na:"
|
||||
SceneSwitcher.CheckInterval="Proveravaj naziv aktivnog prozora svakih:"
|
||||
SceneSwitcher.ActiveOrNotActive="Menjač scena je:"
|
||||
InvalidRegex.Title="Neispravan regularni izraz"
|
||||
InvalidRegex.Text="Regularni izraz koji ste uneli nije ispravan."
|
||||
Active="Aktivan"
|
||||
Inactive="Neaktivan"
|
||||
Start="Pokreni"
|
||||
Stop="Zaustavi"
|
||||
|
||||
|
||||
OutputTimer="Tempomat snimanja i emitovanja"
|
||||
OutputTimer.Stream="Zaustavi emitovanje nakon:"
|
||||
OutputTimer.Record="Zaustavi snimanje nakon:"
|
||||
OutputTimer.Stream.StoppingIn="Prekidanje emitovanja za:"
|
||||
OutputTimer.Record.StoppingIn="Prekidanje snimanja za:"
|
||||
OutputTimer.Stream.EnableEverytime="Omogući štopovanje emitovanja svaki put"
|
||||
OutputTimer.Record.EnableEverytime="Omogući štopovanje snimanja svaki put"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Ne váltson"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Váltson:"
|
||||
SceneSwitcher.CheckInterval="Aktív ablak ellenőrzése ennyi időközönként:"
|
||||
SceneSwitcher.ActiveOrNotActive="Jelenet váltó:"
|
||||
InvalidRegex.Title="Érvénytelen kifejezés"
|
||||
InvalidRegex.Text="A megadott kifejezés érvénytelen."
|
||||
Active="Aktív"
|
||||
Inactive="Inaktiv"
|
||||
Start="Start"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Feliratok (Kísérleti)"
|
||||
Captions.AudioSource="Audio forrás"
|
||||
Captions.CurrentSystemLanguage="Rendszer aktuális nyelve (%1)"
|
||||
|
||||
OutputTimer="Kimeneti időzítő"
|
||||
OutputTimer.Stream="Stream leállítása:"
|
||||
OutputTimer.Record="Felvétel leállítása:"
|
||||
OutputTimer.Stream.StoppingIn="A stream leáll:"
|
||||
OutputTimer.Record.StoppingIn="Felvétel leáll:"
|
||||
OutputTimer.Stream.EnableEverytime="Stream időzítő indítása minden alkalommal"
|
||||
OutputTimer.Record.EnableEverytime="Felvétel időzítő indítása minden alkalommal"
|
||||
|
||||
|
|
|
|||
25
UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini
Normal file
25
UI/frontend-plugins/frontend-tools/data/locale/it-IT.ini
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
SceneSwitcher="Cambia scena automatico"
|
||||
SceneSwitcher.OnNoMatch="Quando nessuna scena coincide a:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Non passare"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Passa a:"
|
||||
SceneSwitcher.CheckInterval="Controlla il titolo della finestra attiva ogni:"
|
||||
SceneSwitcher.ActiveOrNotActive="Lo scene switcher è:"
|
||||
InvalidRegex.Title="Espressione regolare non valida"
|
||||
InvalidRegex.Text="L'espressione regolare che hai inserito non è valido."
|
||||
Active="Attivo"
|
||||
Inactive="Inattivo"
|
||||
Start="Inizio"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Sottotitoli (Sperimentale)"
|
||||
Captions.AudioSource="Fonte audio"
|
||||
Captions.CurrentSystemLanguage="Lingua del sistema in uso (%1)"
|
||||
|
||||
OutputTimer="Timer Output"
|
||||
OutputTimer.Stream="Termina diretta dopo:"
|
||||
OutputTimer.Record="Termina registrazione dopo:"
|
||||
OutputTimer.Stream.StoppingIn="La diretta terminerà in:"
|
||||
OutputTimer.Record.StoppingIn="La registrazione terminerà in:"
|
||||
OutputTimer.Stream.EnableEverytime="Abilita il timer per lo streaming ogni volta"
|
||||
OutputTimer.Record.EnableEverytime="Abilita il timer per la registrazione ogni volta"
|
||||
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="切り替えない"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="切り替える:"
|
||||
SceneSwitcher.CheckInterval="アクティブウィンドウタイトルを確認する間隔:"
|
||||
SceneSwitcher.ActiveOrNotActive="シーンスイッチャーは:"
|
||||
InvalidRegex.Title="無効な正規表現"
|
||||
InvalidRegex.Text="入力した正規表現は有効ではありません。"
|
||||
Active="アクティブ"
|
||||
Inactive="非アクティブ"
|
||||
Start="開始"
|
||||
Stop="停止"
|
||||
|
||||
Captions="見出し (実験的)"
|
||||
Captions.AudioSource="音声ソース"
|
||||
Captions.CurrentSystemLanguage="現在のシステム言語 (%1)"
|
||||
|
||||
OutputTimer="出力タイマー"
|
||||
OutputTimer.Stream="配信停止の時間設定:"
|
||||
OutputTimer.Record="録画停止の時間設定:"
|
||||
OutputTimer.Stream.StoppingIn="配信停止まで:"
|
||||
OutputTimer.Record.StoppingIn="録画停止まで:"
|
||||
OutputTimer.Stream.EnableEverytime="毎回配信タイマーを有効にする"
|
||||
OutputTimer.Record.EnableEverytime="毎回録画タイマーを有効にする"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="전환하지 않음"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="여기로 전환:"
|
||||
SceneSwitcher.CheckInterval="활성화된 윈도우 제목을 확인:"
|
||||
SceneSwitcher.ActiveOrNotActive="장면 전환기:"
|
||||
InvalidRegex.Title="유효하지 않은 정규 표현"
|
||||
InvalidRegex.Text="입력한 정규 표현이 유효하지 않습니다."
|
||||
Active="활성화"
|
||||
Inactive="비활성화"
|
||||
Start="시작"
|
||||
Stop="중단"
|
||||
|
||||
Captions="자막 (실험적 기능)"
|
||||
Captions.AudioSource="오디오 소스"
|
||||
Captions.CurrentSystemLanguage="현재 시스템 언어 (%1)"
|
||||
|
||||
OutputTimer="출력 시간 설정"
|
||||
OutputTimer.Stream="이 시간 이후 방송 중단:"
|
||||
OutputTimer.Record="이 시간 이후 녹화 중단:"
|
||||
OutputTimer.Stream.StoppingIn="방송 중지까지 남은 시간:"
|
||||
OutputTimer.Record.StoppingIn="녹화 중지까지 남은 시간:"
|
||||
OutputTimer.Stream.EnableEverytime="매번 방송 시간 기록기 활성화"
|
||||
OutputTimer.Record.EnableEverytime="매번 녹화 시간 기록기 활성화"
|
||||
|
||||
|
|
|
|||
11
UI/frontend-plugins/frontend-tools/data/locale/ms-MY.ini
Normal file
11
UI/frontend-plugins/frontend-tools/data/locale/ms-MY.ini
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Active="Aktif"
|
||||
Inactive="Tidak Aktif"
|
||||
Start="Mula"
|
||||
Stop="Berhenti"
|
||||
|
||||
|
||||
OutputTimer.Stream="Berhenti 'streaming' selepas:"
|
||||
OutputTimer.Record="Berhenti merakam selepas:"
|
||||
OutputTimer.Stream.StoppingIn="'Streaming' dihentikan dalam:"
|
||||
OutputTimer.Record.StoppingIn="Rakaman dihentikan dalam:"
|
||||
|
||||
25
UI/frontend-plugins/frontend-tools/data/locale/nb-NO.ini
Normal file
25
UI/frontend-plugins/frontend-tools/data/locale/nb-NO.ini
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
SceneSwitcher="Automatisk Scene Skifter"
|
||||
SceneSwitcher.OnNoMatch="Når ingen vindu passer overens:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Ikke bytt"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Bytt til:"
|
||||
SceneSwitcher.CheckInterval="Sjekk aktivt vindu hver:"
|
||||
SceneSwitcher.ActiveOrNotActive="Sceneskifter er:"
|
||||
InvalidRegex.Title="Ugyldig regulært utrykk"
|
||||
InvalidRegex.Text="Det regulære utrykket du har angitt er ugyldig."
|
||||
Active="Aktiv"
|
||||
Inactive="Inaktiv"
|
||||
Start="Start"
|
||||
Stop="Stopp"
|
||||
|
||||
Captions="Bildetekster (eksperimentell)"
|
||||
Captions.AudioSource="Lyd kilde"
|
||||
Captions.CurrentSystemLanguage="Någjeldende System Språk"
|
||||
|
||||
OutputTimer="Stoppeklokke"
|
||||
OutputTimer.Stream="Stopp streaming etter:"
|
||||
OutputTimer.Record="Stopp opptak etter:"
|
||||
OutputTimer.Stream.StoppingIn="Streaming stopper om:"
|
||||
OutputTimer.Record.StoppingIn="Opptak stopper om:"
|
||||
OutputTimer.Stream.EnableEverytime="Aktiver streaming timer hver gang"
|
||||
OutputTimer.Record.EnableEverytime="Aktiver opptaks timer hver gang"
|
||||
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Wissel niet"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Wissel naar:"
|
||||
SceneSwitcher.CheckInterval="Controleer actieve venstertitel elke:"
|
||||
SceneSwitcher.ActiveOrNotActive="Scènewisselaar is:"
|
||||
InvalidRegex.Title="Ongeldige Reguliere Expressie"
|
||||
InvalidRegex.Text="De ingevoerde reguliere expressie is ongeldig."
|
||||
Active="Actief"
|
||||
Inactive="Inactief"
|
||||
Start="Start"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Ondertiteling (Experimenteel)"
|
||||
Captions.AudioSource="Audiobron"
|
||||
Captions.CurrentSystemLanguage="Huidige Systeemtaal (%1)"
|
||||
|
||||
OutputTimer="Uitvoertimer"
|
||||
OutputTimer.Stream="Stop met streamen na:"
|
||||
OutputTimer.Record="Stop met opnemen na:"
|
||||
OutputTimer.Stream.StoppingIn="Stream stopt over:"
|
||||
OutputTimer.Record.StoppingIn="Opname stopt over:"
|
||||
OutputTimer.Stream.EnableEverytime="Schakel streaming timer elke keer in"
|
||||
OutputTimer.Record.EnableEverytime="Schakel opnametimer elke keer in"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Nie przełączaj"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Przełącz na:"
|
||||
SceneSwitcher.CheckInterval="Sprawdź tytuł aktywnego okna co:"
|
||||
SceneSwitcher.ActiveOrNotActive="Przełączanie scen jest:"
|
||||
InvalidRegex.Title="Nieprawidłowe wyrażenie regularne"
|
||||
InvalidRegex.Text="Podane wyrażenie regularne jest nieprawidłowe."
|
||||
Active="Aktywne"
|
||||
Inactive="Nieaktywne"
|
||||
Start="Start"
|
||||
Stop="Stop"
|
||||
|
||||
Captions="Podpisy (eksperymentalne)"
|
||||
Captions.AudioSource="Źródła dźwięku"
|
||||
Captions.CurrentSystemLanguage="Obecny język systemu (%1)"
|
||||
|
||||
OutputTimer="Wyłącznik czasowy"
|
||||
OutputTimer.Stream="Zatrzymaj stream po:"
|
||||
OutputTimer.Record="Zatrzymaj nagrywanie po:"
|
||||
OutputTimer.Stream.StoppingIn="Zatrzymanie streamu za:"
|
||||
OutputTimer.Record.StoppingIn="Zatrzymanie nagrywania za:"
|
||||
OutputTimer.Stream.EnableEverytime="Włącz timer streamu za każdym razem"
|
||||
OutputTimer.Record.EnableEverytime="Włącz timer nagrywania za każdym razem"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Não alternar"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Alternar para:"
|
||||
SceneSwitcher.CheckInterval="Checar o título da janela ativa a cada:"
|
||||
SceneSwitcher.ActiveOrNotActive="O alternador de cenas está:"
|
||||
InvalidRegex.Title="Expressão Regular inválida"
|
||||
InvalidRegex.Text="A expressão regular que você inseriu é inválida."
|
||||
Active="Ligado"
|
||||
Inactive="Desligado"
|
||||
Start="Iniciar"
|
||||
Stop="Parar"
|
||||
|
||||
Captions="Legendas (Experimental)"
|
||||
Captions.AudioSource="Fonte de Áudio"
|
||||
Captions.CurrentSystemLanguage="Idioma Atual do Sistema (%1)"
|
||||
|
||||
OutputTimer="Temporizador de saída"
|
||||
OutputTimer.Stream="Parar a transmissão após:"
|
||||
OutputTimer.Record="Parar a gravação após:"
|
||||
OutputTimer.Stream.StoppingIn="A transmissão irá parar em:"
|
||||
OutputTimer.Record.StoppingIn="A gravação irá parar em:"
|
||||
OutputTimer.Stream.EnableEverytime="Ativar o timer streaming o tempo todo"
|
||||
OutputTimer.Record.EnableEverytime="Ativar o timer de gravação o tempo todo"
|
||||
|
||||
|
|
|
|||
20
UI/frontend-plugins/frontend-tools/data/locale/pt-PT.ini
Normal file
20
UI/frontend-plugins/frontend-tools/data/locale/pt-PT.ini
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
SceneSwitcher="Troca automática de cenas"
|
||||
SceneSwitcher.OnNoMatch="Quando nenhuma janela corresponder:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Não trocar"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Trocar para:"
|
||||
SceneSwitcher.CheckInterval="Verificar o nome da janela ativa a cada:"
|
||||
SceneSwitcher.ActiveOrNotActive="A troca de cenas está:"
|
||||
InvalidRegex.Title="Expressão regular inválida"
|
||||
InvalidRegex.Text="A expressão regular que introduziu é inválida."
|
||||
Active="Ativa"
|
||||
Inactive="Inativa"
|
||||
Start="Iniciar"
|
||||
Stop="Parar"
|
||||
|
||||
|
||||
OutputTimer="Temporizador de saída"
|
||||
OutputTimer.Stream="Para a transmissão após:"
|
||||
OutputTimer.Record="Parar a gravação após:"
|
||||
OutputTimer.Stream.StoppingIn="A transmissão irá parar em:"
|
||||
OutputTimer.Record.StoppingIn="A gravação irá parar em:"
|
||||
|
||||
13
UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini
Normal file
13
UI/frontend-plugins/frontend-tools/data/locale/ro-RO.ini
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
SceneSwitcher="Schimbator automat de scenă"
|
||||
SceneSwitcher.OnNoMatch="Cand nici o fereastra nu se potriveste:"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Schimbă la:"
|
||||
Active="Activ"
|
||||
Inactive="Inactiv"
|
||||
Start="Pornire"
|
||||
Stop="Oprire"
|
||||
|
||||
Captions.AudioSource="Sursa audio"
|
||||
Captions.CurrentSystemLanguage="Limba curentă a sistemului (%1)"
|
||||
|
||||
OutputTimer.Record="Opriți inregistrarea dupa:"
|
||||
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Не переключать"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Переключать на:"
|
||||
SceneSwitcher.CheckInterval="Проверять имя активного окна каждые:"
|
||||
SceneSwitcher.ActiveOrNotActive="Переключатель сцен:"
|
||||
InvalidRegex.Title="Недопустимое регулярное выражение"
|
||||
InvalidRegex.Text="Регулярное выражение, которое вы ввели, содержит ошибки."
|
||||
Active="Активен"
|
||||
Inactive="Неактивен"
|
||||
Start="Запустить"
|
||||
Stop="Остановить"
|
||||
|
||||
Captions="Субтитры (экспериментально)"
|
||||
Captions.AudioSource="Источник звука"
|
||||
Captions.CurrentSystemLanguage="Текущий язык системы (%1)"
|
||||
|
||||
OutputTimer="Таймер записи и стрима"
|
||||
OutputTimer.Stream="Завершить стрим через:"
|
||||
OutputTimer.Record="Завершить запись через:"
|
||||
OutputTimer.Stream.StoppingIn="Стрим будет завершён через:"
|
||||
OutputTimer.Record.StoppingIn="Запись будет завершена через:"
|
||||
OutputTimer.Stream.EnableEverytime="Включать таймер стрима каждый раз"
|
||||
OutputTimer.Record.EnableEverytime="Включать таймер записи каждый раз"
|
||||
|
||||
|
|
|
|||
10
UI/frontend-plugins/frontend-tools/data/locale/sk-SK.ini
Normal file
10
UI/frontend-plugins/frontend-tools/data/locale/sk-SK.ini
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
SceneSwitcher.CheckInterval="Kontrolovať aktívne okno každých:"
|
||||
Active="Aktívny"
|
||||
Inactive="Neaktivní"
|
||||
Start="Spustiť"
|
||||
Stop="Zastaviť"
|
||||
|
||||
|
||||
OutputTimer.Stream="Zastaviť stream po:"
|
||||
OutputTimer.Record="Zastaviť nahrávanie po:"
|
||||
|
||||
|
|
@ -4,8 +4,19 @@ SceneSwitcher.OnNoMatch.DontSwitch="Ne menjaj"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Promeni na:"
|
||||
SceneSwitcher.CheckInterval="Proveravaj naziv aktivnog prozora svakih:"
|
||||
SceneSwitcher.ActiveOrNotActive="Menjač scena je:"
|
||||
InvalidRegex.Title="Neispravan regularni izraz"
|
||||
InvalidRegex.Text="Regularni izraz koji ste uneli nije ispravan."
|
||||
Active="Aktivan"
|
||||
Inactive="Neaktivan"
|
||||
Start="Pokreni"
|
||||
Stop="Zaustavi"
|
||||
|
||||
|
||||
OutputTimer="Tempomat snimanja i emitovanja"
|
||||
OutputTimer.Stream="Zaustavi emitovanje nakon:"
|
||||
OutputTimer.Record="Zaustavi snimanje nakon:"
|
||||
OutputTimer.Stream.StoppingIn="Prekidanje emitovanja za:"
|
||||
OutputTimer.Record.StoppingIn="Prekidanje snimanja za:"
|
||||
OutputTimer.Stream.EnableEverytime="Omogući štopovanje emitovanja svaki put"
|
||||
OutputTimer.Record.EnableEverytime="Omogući štopovanje snimanja svaki put"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,19 @@ SceneSwitcher.OnNoMatch.DontSwitch="Не мењај"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Промени на:"
|
||||
SceneSwitcher.CheckInterval="Проверавај назив активног прозора сваких:"
|
||||
SceneSwitcher.ActiveOrNotActive="Мењач сцена је:"
|
||||
InvalidRegex.Title="Неисправан регуларни израз"
|
||||
InvalidRegex.Text="Регуларни израз који сте унели није исправан."
|
||||
Active="Активан"
|
||||
Inactive="Неактиван"
|
||||
Start="Покрени"
|
||||
Stop="Заустави"
|
||||
|
||||
|
||||
OutputTimer="Темпомат снимања и емитовања"
|
||||
OutputTimer.Stream="Заустави емитовање након:"
|
||||
OutputTimer.Record="Заустави снимање након:"
|
||||
OutputTimer.Stream.StoppingIn="Прекидање емитовања за:"
|
||||
OutputTimer.Record.StoppingIn="Прекидање снимања за:"
|
||||
OutputTimer.Stream.EnableEverytime="Омогући штоповање емитовање сваки пут"
|
||||
OutputTimer.Record.EnableEverytime="Омогући штоповање снимања сваки пут"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Byt inte"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Byt till:"
|
||||
SceneSwitcher.CheckInterval="Kontrollera aktiv fönstertitel varje:"
|
||||
SceneSwitcher.ActiveOrNotActive="Scenbytaren är:"
|
||||
InvalidRegex.Title="Ogiltigt reguljärt uttryck"
|
||||
InvalidRegex.Text="Det reguljära uttrycket du angav är ogiltigt."
|
||||
Active="Aktiv"
|
||||
Inactive="Inaktiv"
|
||||
Start="Starta"
|
||||
Stop="Stoppa"
|
||||
|
||||
Captions="Undertexter (experimentell)"
|
||||
Captions.AudioSource="Ljudkälla"
|
||||
Captions.CurrentSystemLanguage="Aktuellt systemspråk (%1)"
|
||||
|
||||
OutputTimer="Utdatatimer"
|
||||
OutputTimer.Stream="Sluta streama efter:"
|
||||
OutputTimer.Record="Stoppa inspelningen efter:"
|
||||
OutputTimer.Stream.StoppingIn="Streamen stoppas om:"
|
||||
OutputTimer.Record.StoppingIn="Inspelningen stoppas om:"
|
||||
OutputTimer.Stream.EnableEverytime="Aktivera strömtimer varje gång"
|
||||
OutputTimer.Record.EnableEverytime="Aktivera inspelningstimer varje gång"
|
||||
|
||||
|
|
|
|||
25
UI/frontend-plugins/frontend-tools/data/locale/tr-TR.ini
Normal file
25
UI/frontend-plugins/frontend-tools/data/locale/tr-TR.ini
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
SceneSwitcher="Otomatik Sahne Değiştirici"
|
||||
SceneSwitcher.OnNoMatch="Hiçbir pencere ile eşleşmez ise:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Geçiş yapma"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Şuna geç:"
|
||||
SceneSwitcher.CheckInterval="Etkin pencere başlığını kontrol et:"
|
||||
SceneSwitcher.ActiveOrNotActive="Sahne Değiştirici:"
|
||||
InvalidRegex.Title="Geçersiz Kurallı İfade"
|
||||
InvalidRegex.Text="Girdiğiniz kurallı ifade geçersiz."
|
||||
Active="Etkin"
|
||||
Inactive="Devre Dışı"
|
||||
Start="Başlat"
|
||||
Stop="Durdur"
|
||||
|
||||
Captions="Altyazı (Deneysel)"
|
||||
Captions.AudioSource="Ses kaynağı"
|
||||
Captions.CurrentSystemLanguage="Geçerli Sistem Dili (%1)"
|
||||
|
||||
OutputTimer="Çıkış Zamanlayıcısı"
|
||||
OutputTimer.Stream="Şuradan sonra yayını durdur:"
|
||||
OutputTimer.Record="Şuradan sonra kaydı durdur:"
|
||||
OutputTimer.Stream.StoppingIn="Yayın durduruluyor:"
|
||||
OutputTimer.Record.StoppingIn="Kayıt durduruluyor:"
|
||||
OutputTimer.Stream.EnableEverytime="Her zaman yayın zamanlayıcıyı etkinleştir"
|
||||
OutputTimer.Record.EnableEverytime="Her zaman kayıt zamanlayıcıyı etkinleştir"
|
||||
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="Нічого не робити"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="Перейти до:"
|
||||
SceneSwitcher.CheckInterval="Перевіряти заголовок активного вікна кожні:"
|
||||
SceneSwitcher.ActiveOrNotActive="Автоматичний перехід між Сценами:"
|
||||
InvalidRegex.Title="Неприпустимий регулярний вираз"
|
||||
InvalidRegex.Text="Ви ввели неприпустимий регулярний вираз (шаблон)."
|
||||
Active="Активний"
|
||||
Inactive="Неактивний"
|
||||
Start="Запустити"
|
||||
Stop="Зупинити"
|
||||
|
||||
Captions="Субтитри (експериментально)"
|
||||
Captions.AudioSource="Джерело Аудіо"
|
||||
Captions.CurrentSystemLanguage="Поточна мова Системи (%1)"
|
||||
|
||||
OutputTimer="Таймер для Виводу"
|
||||
OutputTimer.Stream="Закінчити трансляцію за:"
|
||||
OutputTimer.Record="Зупинити запис за:"
|
||||
OutputTimer.Stream.StoppingIn="Трансляція припиниться за:"
|
||||
OutputTimer.Record.StoppingIn="Запис зупиниться за:"
|
||||
OutputTimer.Stream.EnableEverytime="Щоразу запускається трансляція - вмикати Таймер для Виводу"
|
||||
OutputTimer.Record.EnableEverytime="Щоразу починається запис - вмикати Таймер для Виводу"
|
||||
|
||||
|
|
|
|||
17
UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini
Normal file
17
UI/frontend-plugins/frontend-tools/data/locale/vi-VN.ini
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
SceneSwitcher="Tự động chuyển cảnh"
|
||||
SceneSwitcher.OnNoMatch="Khi cửa sổ không khớp:"
|
||||
SceneSwitcher.OnNoMatch.DontSwitch="Không chuyển"
|
||||
SceneSwitcher.OnNoMatch.SwitchTo="Chuyển sang:"
|
||||
SceneSwitcher.CheckInterval="Kiểm tra tiêu đề cửa sổ mỗi:"
|
||||
SceneSwitcher.ActiveOrNotActive="Chuyển cảnh đang:"
|
||||
Active="Đang hoạt động"
|
||||
Inactive="Không hoạt động"
|
||||
Start="Bắt đầu"
|
||||
Stop="Dừng"
|
||||
|
||||
|
||||
OutputTimer.Stream="Dừng stream sau:"
|
||||
OutputTimer.Record="Dừng ghi video sau:"
|
||||
OutputTimer.Stream.StoppingIn="Stream sẽ dừng trong:"
|
||||
OutputTimer.Record.StoppingIn="Quay video sẽ dừng trong:"
|
||||
|
||||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="不切换"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="切换到:"
|
||||
SceneSwitcher.CheckInterval="检查活动窗口的标题,每:"
|
||||
SceneSwitcher.ActiveOrNotActive="场景切换器是:"
|
||||
InvalidRegex.Title="无效的正则表达式"
|
||||
InvalidRegex.Text="您输入的正则表达式是无效的。"
|
||||
Active="已激活"
|
||||
Inactive="未激活"
|
||||
Start="开始"
|
||||
Stop="停止"
|
||||
|
||||
Captions="标题(实验)"
|
||||
Captions.AudioSource="音频源"
|
||||
Captions.CurrentSystemLanguage="当前系统语言 (%1)"
|
||||
|
||||
OutputTimer="输出计时器"
|
||||
OutputTimer.Stream="停止流处理后:"
|
||||
OutputTimer.Record="停止录制后:"
|
||||
OutputTimer.Stream.StoppingIn="串流停止在:"
|
||||
OutputTimer.Record.StoppingIn="录制停止在:"
|
||||
OutputTimer.Stream.EnableEverytime="每次启用流计时器"
|
||||
OutputTimer.Record.EnableEverytime="每次启用录制计时器"
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,22 @@ SceneSwitcher.OnNoMatch.DontSwitch="不切換"
|
|||
SceneSwitcher.OnNoMatch.SwitchTo="切換到︰"
|
||||
SceneSwitcher.CheckInterval="檢查使用中視窗標題的頻率︰"
|
||||
SceneSwitcher.ActiveOrNotActive="場景切換器︰"
|
||||
InvalidRegex.Title="無效的正規表達式"
|
||||
InvalidRegex.Text="您輸入的正則運算式不正確"
|
||||
Active="啟動中"
|
||||
Inactive="未啟動"
|
||||
Start="開始"
|
||||
Stop="停止"
|
||||
|
||||
Captions="標題 (實驗)"
|
||||
Captions.AudioSource="音訊源"
|
||||
Captions.CurrentSystemLanguage="目前系統語言 (%1)"
|
||||
|
||||
OutputTimer="輸出計時器"
|
||||
OutputTimer.Stream="在下面時間後停止串流:"
|
||||
OutputTimer.Record="在下面時間後停止錄影:"
|
||||
OutputTimer.Stream.StoppingIn="串流將在下面時間內停止"
|
||||
OutputTimer.Record.StoppingIn="錄影將在下面時間內停止"
|
||||
OutputTimer.Stream.EnableEverytime="每次都啟動串流計時器"
|
||||
OutputTimer.Record.EnableEverytime="每次都啟動錄影計時器"
|
||||
|
||||
|
|
|
|||
132
UI/frontend-plugins/frontend-tools/forms/captions.ui
Normal file
132
UI/frontend-plugins/frontend-tools/forms/captions.ui
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>CaptionsDialog</class>
|
||||
<widget class="QDialog" name="CaptionsDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>519</width>
|
||||
<height>140</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Captions</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Captions.AudioSource</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>source</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="source">
|
||||
<property name="insertPolicy">
|
||||
<enum>QComboBox::InsertAlphabetically</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QCheckBox" name="enable">
|
||||
<property name="text">
|
||||
<string>Enable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Basic.Settings.General.Language</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>language</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="language"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="accept">
|
||||
<property name="text">
|
||||
<string>OK</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>accept</sender>
|
||||
<signal>clicked()</signal>
|
||||
<receiver>CaptionsDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>268</x>
|
||||
<y>331</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>229</x>
|
||||
<y>-11</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
208
UI/frontend-plugins/frontend-tools/forms/output-timer.ui
Normal file
208
UI/frontend-plugins/frontend-tools/forms/output-timer.ui
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>OutputTimer</class>
|
||||
<widget class="QDialog" name="OutputTimer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>200</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>OutputTimer</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="timerLayout">
|
||||
<item row="0" column="1">
|
||||
<widget class="QLabel" name="streamingLabel">
|
||||
<property name="text">
|
||||
<string>OutputTimer.Stream</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QSpinBox" name="streamingTimerHours">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QLabel" name="hoursLabel">
|
||||
<property name="text">
|
||||
<string>Hours</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QSpinBox" name="streamingTimerMinutes">
|
||||
<property name="maximum">
|
||||
<number>59</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QLabel" name="minutesLabel">
|
||||
<property name="text">
|
||||
<string>Minutes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="6">
|
||||
<widget class="QSpinBox" name="streamingTimerSeconds">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>59</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="7">
|
||||
<widget class="QLabel" name="secondsLabel">
|
||||
<property name="text">
|
||||
<string>Seconds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="8">
|
||||
<widget class="QPushButton" name="outputTimerStream">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="streamStoppingIn">
|
||||
<property name="text">
|
||||
<string>OutputTimer.Stream.StoppingIn</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="streamTime">
|
||||
<property name="text">
|
||||
<string>00:00:00</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QLabel" name="recordingLabel">
|
||||
<property name="text">
|
||||
<string>OutputTimer.Record</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QSpinBox" name="recordingTimerHours">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>24</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QLabel" name="hoursLabel_2">
|
||||
<property name="text">
|
||||
<string>Hours</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="4">
|
||||
<widget class="QSpinBox" name="recordingTimerMinutes">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>59</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="5">
|
||||
<widget class="QLabel" name="minutesLabel_2">
|
||||
<property name="text">
|
||||
<string>Minutes</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="6">
|
||||
<widget class="QSpinBox" name="recordingTimerSeconds">
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>59</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>30</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="7">
|
||||
<widget class="QLabel" name="secondsLabel_2">
|
||||
<property name="text">
|
||||
<string>Seconds</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="8">
|
||||
<widget class="QPushButton" name="outputTimerRecord">
|
||||
<property name="text">
|
||||
<string>Start</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QLabel" name="recordStoppingIn">
|
||||
<property name="text">
|
||||
<string>OutputTimer.Record.StoppingIn</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QLabel" name="recordTime">
|
||||
<property name="text">
|
||||
<string>00:00:00</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="autoStartStreamTimer">
|
||||
<property name="text">
|
||||
<string>OutputTimer.Stream.EnableEverytime</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="autoStartRecordTimer">
|
||||
<property name="text">
|
||||
<string>OutputTimer.Record.EnableEverytime</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources />
|
||||
<connections />
|
||||
</ui>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
#define BUILD_CAPTIONS @BUILD_CAPTIONS@
|
||||
|
|
@ -1,18 +1,41 @@
|
|||
#include <obs-module.h>
|
||||
#include "frontend-tools-config.h"
|
||||
|
||||
OBS_DECLARE_MODULE()
|
||||
OBS_MODULE_USE_DEFAULT_LOCALE("frontend-tools", "en-US")
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
void InitSceneSwitcher();
|
||||
void FreeSceneSwitcher();
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && BUILD_CAPTIONS
|
||||
void InitCaptions();
|
||||
void FreeCaptions();
|
||||
#endif
|
||||
|
||||
void InitOutputTimer();
|
||||
void FreeOutputTimer();
|
||||
|
||||
bool obs_module_load(void)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
InitSceneSwitcher();
|
||||
#endif
|
||||
#if defined(_WIN32) && BUILD_CAPTIONS
|
||||
InitCaptions();
|
||||
#endif
|
||||
InitOutputTimer();
|
||||
return true;
|
||||
}
|
||||
|
||||
void obs_module_unload(void)
|
||||
{
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
FreeSceneSwitcher();
|
||||
#endif
|
||||
#if defined(_WIN32) && BUILD_CAPTIONS
|
||||
FreeCaptions();
|
||||
#endif
|
||||
FreeOutputTimer();
|
||||
}
|
||||
|
|
|
|||
312
UI/frontend-plugins/frontend-tools/output-timer.cpp
Normal file
312
UI/frontend-plugins/frontend-tools/output-timer.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
#include <obs-frontend-api.h>
|
||||
#include <obs-module.h>
|
||||
#include <obs.hpp>
|
||||
#include <util/util.hpp>
|
||||
#include <QAction>
|
||||
#include <QMainWindow>
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include "output-timer.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
OutputTimer *ot;
|
||||
|
||||
OutputTimer::OutputTimer(QWidget *parent)
|
||||
: QDialog(parent),
|
||||
ui(new Ui_OutputTimer)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
QObject::connect(ui->outputTimerStream, SIGNAL(clicked()), this,
|
||||
SLOT(StreamingTimerButton()));
|
||||
QObject::connect(ui->outputTimerRecord, SIGNAL(clicked()), this,
|
||||
SLOT(RecordingTimerButton()));
|
||||
|
||||
streamingTimer = new QTimer(this);
|
||||
streamingTimerDisplay = new QTimer(this);
|
||||
|
||||
recordingTimer = new QTimer(this);
|
||||
recordingTimerDisplay = new QTimer(this);
|
||||
}
|
||||
|
||||
void OutputTimer::closeEvent(QCloseEvent*)
|
||||
{
|
||||
obs_frontend_save();
|
||||
}
|
||||
|
||||
void OutputTimer::StreamingTimerButton()
|
||||
{
|
||||
if (!obs_frontend_streaming_active()) {
|
||||
obs_frontend_streaming_start();
|
||||
} else if (streamingAlreadyActive) {
|
||||
StreamTimerStart();
|
||||
streamingAlreadyActive = false;
|
||||
} else if (obs_frontend_streaming_active()) {
|
||||
obs_frontend_streaming_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void OutputTimer::RecordingTimerButton()
|
||||
{
|
||||
if (!obs_frontend_recording_active()) {
|
||||
obs_frontend_recording_start();
|
||||
} else if (recordingAlreadyActive) {
|
||||
RecordTimerStart();
|
||||
recordingAlreadyActive = false;
|
||||
} else if (obs_frontend_recording_active()) {
|
||||
obs_frontend_recording_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void OutputTimer::StreamTimerStart()
|
||||
{
|
||||
if (!isVisible() && ui->autoStartStreamTimer->isChecked() == false) {
|
||||
streamingAlreadyActive = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int hours = ui->streamingTimerHours->value();
|
||||
int minutes = ui->streamingTimerMinutes->value();
|
||||
int seconds = ui->streamingTimerSeconds->value();
|
||||
|
||||
int total = (((hours * 3600) +
|
||||
(minutes * 60)) +
|
||||
seconds) * 1000;
|
||||
|
||||
if (total == 0)
|
||||
total = 1000;
|
||||
|
||||
streamingTimer->setInterval(total);
|
||||
streamingTimer->setSingleShot(true);
|
||||
|
||||
QObject::connect(streamingTimer, SIGNAL(timeout()),
|
||||
SLOT(EventStopStreaming()));
|
||||
|
||||
QObject::connect(streamingTimerDisplay, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateStreamTimerDisplay()));
|
||||
|
||||
streamingTimer->start();
|
||||
streamingTimerDisplay->start(1000);
|
||||
ui->outputTimerStream->setText(obs_module_text("Stop"));
|
||||
|
||||
UpdateStreamTimerDisplay();
|
||||
}
|
||||
|
||||
void OutputTimer::RecordTimerStart()
|
||||
{
|
||||
if (!isVisible() && ui->autoStartRecordTimer->isChecked() == false) {
|
||||
recordingAlreadyActive = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int hours = ui->recordingTimerHours->value();
|
||||
int minutes = ui->recordingTimerMinutes->value();
|
||||
int seconds = ui->recordingTimerSeconds->value();
|
||||
|
||||
int total = (((hours * 3600) +
|
||||
(minutes * 60)) +
|
||||
seconds) * 1000;
|
||||
|
||||
if (total == 0)
|
||||
total = 1000;
|
||||
|
||||
recordingTimer->setInterval(total);
|
||||
recordingTimer->setSingleShot(true);
|
||||
|
||||
QObject::connect(recordingTimer, SIGNAL(timeout()),
|
||||
SLOT(EventStopRecording()));
|
||||
|
||||
QObject::connect(recordingTimerDisplay, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateRecordTimerDisplay()));
|
||||
|
||||
recordingTimer->start();
|
||||
recordingTimerDisplay->start(1000);
|
||||
ui->outputTimerRecord->setText(obs_module_text("Stop"));
|
||||
|
||||
UpdateRecordTimerDisplay();
|
||||
}
|
||||
|
||||
void OutputTimer::StreamTimerStop()
|
||||
{
|
||||
streamingAlreadyActive = false;
|
||||
|
||||
if (!isVisible() && streamingTimer->isActive() == false)
|
||||
return;
|
||||
|
||||
if (streamingTimer->isActive())
|
||||
streamingTimer->stop();
|
||||
|
||||
ui->outputTimerStream->setText(obs_module_text("Start"));
|
||||
|
||||
if (streamingTimerDisplay->isActive())
|
||||
streamingTimerDisplay->stop();
|
||||
|
||||
ui->streamTime->setText("00:00:00");
|
||||
}
|
||||
|
||||
void OutputTimer::RecordTimerStop()
|
||||
{
|
||||
recordingAlreadyActive = false;
|
||||
|
||||
if (!isVisible() && recordingTimer->isActive() == false)
|
||||
return;
|
||||
|
||||
if (recordingTimer->isActive())
|
||||
recordingTimer->stop();
|
||||
|
||||
ui->outputTimerRecord->setText(obs_module_text("Start"));
|
||||
|
||||
if (recordingTimerDisplay->isActive())
|
||||
recordingTimerDisplay->stop();
|
||||
|
||||
ui->recordTime->setText("00:00:00");
|
||||
}
|
||||
|
||||
void OutputTimer::UpdateStreamTimerDisplay()
|
||||
{
|
||||
int remainingTime = streamingTimer->remainingTime() / 1000;
|
||||
|
||||
int seconds = remainingTime % 60;
|
||||
int minutes = (remainingTime % 3600) / 60;
|
||||
int hours = remainingTime / 3600;
|
||||
|
||||
QString text;
|
||||
text.sprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
ui->streamTime->setText(text);
|
||||
}
|
||||
|
||||
void OutputTimer::UpdateRecordTimerDisplay()
|
||||
{
|
||||
int remainingTime = recordingTimer->remainingTime() / 1000;
|
||||
|
||||
int seconds = remainingTime % 60;
|
||||
int minutes = (remainingTime % 3600) / 60;
|
||||
int hours = remainingTime / 3600;
|
||||
|
||||
QString text;
|
||||
text.sprintf("%02d:%02d:%02d", hours, minutes, seconds);
|
||||
ui->recordTime->setText(text);
|
||||
}
|
||||
|
||||
void OutputTimer::ShowHideDialog()
|
||||
{
|
||||
if (!isVisible()) {
|
||||
setVisible(true);
|
||||
QTimer::singleShot(250, this, SLOT(show()));
|
||||
} else {
|
||||
setVisible(false);
|
||||
QTimer::singleShot(250, this, SLOT(hide()));
|
||||
}
|
||||
}
|
||||
|
||||
void OutputTimer::EventStopStreaming()
|
||||
{
|
||||
obs_frontend_streaming_stop();
|
||||
}
|
||||
|
||||
void OutputTimer::EventStopRecording()
|
||||
{
|
||||
obs_frontend_recording_stop();
|
||||
}
|
||||
|
||||
static void SaveOutputTimer(obs_data_t *save_data, bool saving, void *)
|
||||
{
|
||||
if (saving) {
|
||||
obs_data_t *obj = obs_data_create();
|
||||
|
||||
obs_data_set_int(obj, "streamTimerHours",
|
||||
ot->ui->streamingTimerHours->value());
|
||||
obs_data_set_int(obj, "streamTimerMinutes",
|
||||
ot->ui->streamingTimerMinutes->value());
|
||||
obs_data_set_int(obj, "streamTimerSeconds",
|
||||
ot->ui->streamingTimerSeconds->value());
|
||||
|
||||
obs_data_set_int(obj, "recordTimerHours",
|
||||
ot->ui->recordingTimerHours->value());
|
||||
obs_data_set_int(obj, "recordTimerMinutes",
|
||||
ot->ui->recordingTimerMinutes->value());
|
||||
obs_data_set_int(obj, "recordTimerSeconds",
|
||||
ot->ui->recordingTimerSeconds->value());
|
||||
|
||||
obs_data_set_bool(obj, "autoStartStreamTimer",
|
||||
ot->ui->autoStartStreamTimer->isChecked());
|
||||
obs_data_set_bool(obj, "autoStartRecordTimer",
|
||||
ot->ui->autoStartRecordTimer->isChecked());
|
||||
|
||||
obs_data_set_obj(save_data, "output-timer", obj);
|
||||
|
||||
obs_data_release(obj);
|
||||
} else {
|
||||
obs_data_t *obj = obs_data_get_obj(save_data,
|
||||
"output-timer");
|
||||
|
||||
if (!obj)
|
||||
obj = obs_data_create();
|
||||
|
||||
ot->ui->streamingTimerHours->setValue(
|
||||
obs_data_get_int(obj, "streamTimerHours"));
|
||||
ot->ui->streamingTimerMinutes->setValue(
|
||||
obs_data_get_int(obj, "streamTimerMinutes"));
|
||||
ot->ui->streamingTimerSeconds->setValue(
|
||||
obs_data_get_int(obj, "streamTimerSeconds"));
|
||||
|
||||
ot->ui->recordingTimerHours->setValue(
|
||||
obs_data_get_int(obj, "recordTimerHours"));
|
||||
ot->ui->recordingTimerMinutes->setValue(
|
||||
obs_data_get_int(obj, "recordTimerMinutes"));
|
||||
ot->ui->recordingTimerSeconds->setValue(
|
||||
obs_data_get_int(obj, "recordTimerSeconds"));
|
||||
|
||||
ot->ui->autoStartStreamTimer->setChecked(
|
||||
obs_data_get_bool(obj, "autoStartStreamTimer"));
|
||||
ot->ui->autoStartRecordTimer->setChecked(
|
||||
obs_data_get_bool(obj, "autoStartRecordTimer"));
|
||||
|
||||
obs_data_release(obj);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void FreeOutputTimer()
|
||||
{
|
||||
}
|
||||
|
||||
static void OBSEvent(enum obs_frontend_event event, void *)
|
||||
{
|
||||
if (event == OBS_FRONTEND_EVENT_EXIT) {
|
||||
obs_frontend_save();
|
||||
FreeOutputTimer();
|
||||
} else if (event == OBS_FRONTEND_EVENT_STREAMING_STARTED) {
|
||||
ot->StreamTimerStart();
|
||||
} else if (event == OBS_FRONTEND_EVENT_STREAMING_STOPPING) {
|
||||
ot->StreamTimerStop();
|
||||
} else if (event == OBS_FRONTEND_EVENT_RECORDING_STARTED) {
|
||||
ot->RecordTimerStart();
|
||||
} else if (event == OBS_FRONTEND_EVENT_RECORDING_STOPPING) {
|
||||
ot->RecordTimerStop();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void InitOutputTimer()
|
||||
{
|
||||
QAction *action = (QAction*)obs_frontend_add_tools_menu_qaction(
|
||||
obs_module_text("OutputTimer"));
|
||||
|
||||
obs_frontend_push_ui_translation(obs_module_get_string);
|
||||
|
||||
QMainWindow *window = (QMainWindow*)obs_frontend_get_main_window();
|
||||
|
||||
ot = new OutputTimer(window);
|
||||
|
||||
auto cb = [] ()
|
||||
{
|
||||
ot->ShowHideDialog();
|
||||
};
|
||||
|
||||
obs_frontend_pop_ui_translation();
|
||||
|
||||
obs_frontend_add_save_callback(SaveOutputTimer, nullptr);
|
||||
obs_frontend_add_event_callback(OBSEvent, nullptr);
|
||||
|
||||
action->connect(action, &QAction::triggered, cb);
|
||||
}
|
||||
40
UI/frontend-plugins/frontend-tools/output-timer.hpp
Normal file
40
UI/frontend-plugins/frontend-tools/output-timer.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <memory>
|
||||
|
||||
#include "ui_output-timer.h"
|
||||
|
||||
class QCloseEvent;
|
||||
|
||||
class OutputTimer : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
std::unique_ptr<Ui_OutputTimer> ui;
|
||||
OutputTimer(QWidget *parent);
|
||||
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
|
||||
public slots:
|
||||
void StreamingTimerButton();
|
||||
void RecordingTimerButton();
|
||||
void StreamTimerStart();
|
||||
void RecordTimerStart();
|
||||
void StreamTimerStop();
|
||||
void RecordTimerStop();
|
||||
void UpdateStreamTimerDisplay();
|
||||
void UpdateRecordTimerDisplay();
|
||||
void ShowHideDialog();
|
||||
void EventStopStreaming();
|
||||
void EventStopRecording();
|
||||
|
||||
private:
|
||||
bool streamingAlreadyActive = false;
|
||||
bool recordingAlreadyActive = false;
|
||||
|
||||
QTimer *streamingTimer;
|
||||
QTimer *recordingTimer;
|
||||
QTimer *streamingTimerDisplay;
|
||||
QTimer *recordingTimerDisplay;
|
||||
};
|
||||
36
UI/frontend-plugins/frontend-tools/tool-helpers.hpp
Normal file
36
UI/frontend-plugins/frontend-tools/tool-helpers.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <obs.hpp>
|
||||
#include <string>
|
||||
#include <QString>
|
||||
|
||||
static inline OBSWeakSource GetWeakSourceByName(const char *name)
|
||||
{
|
||||
OBSWeakSource weak;
|
||||
obs_source_t *source = obs_get_source_by_name(name);
|
||||
if (source) {
|
||||
weak = obs_source_get_weak_source(source);
|
||||
obs_weak_source_release(weak);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
return weak;
|
||||
}
|
||||
|
||||
static inline OBSWeakSource GetWeakSourceByQString(const QString &name)
|
||||
{
|
||||
return GetWeakSourceByName(name.toUtf8().constData());
|
||||
}
|
||||
|
||||
static inline std::string GetWeakSourceName(obs_weak_source_t *weak_source)
|
||||
{
|
||||
std::string name;
|
||||
|
||||
obs_source_t *source = obs_weak_source_get_source(weak_source);
|
||||
if (source) {
|
||||
name = obs_source_get_name(source);
|
||||
obs_source_release(source);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue