From 3ad35625cfef20857f08a4fb774da636782aa13d Mon Sep 17 00:00:00 2001 From: Christoph Hohmann Date: Thu, 11 Aug 2016 12:24:10 +0200 Subject: [PATCH 001/107] UI: Improve saving and restoring geometry of main window --- obs/window-basic-main.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 5b8b654ab829e1..9107f5236aaf89 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -127,25 +127,22 @@ OBSBasic::OBSBasic(QWidget *parent) ui->sources->setItemDelegate(new VisibilityItemDelegate(ui->sources)); - int width = config_get_int(App()->GlobalConfig(), "BasicWindow", "cx"); - - // Check if no values are saved (new installation). - if (width != 0) { - int height = config_get_int(App()->GlobalConfig(), - "BasicWindow", "cy"); - int posx = config_get_int(App()->GlobalConfig(), "BasicWindow", - "posx"); - int posy = config_get_int(App()->GlobalConfig(), "BasicWindow", - "posy"); - + const char *geometry = config_get_string(App()->GlobalConfig(), + "BasicWindow", "geometry"); + if (geometry != NULL) { + QByteArray byteArray = QByteArray::fromBase64( + QByteArray(geometry)); + restoreGeometry(byteArray); + + QRect windowGeometry = normalGeometry(); + int posx = windowGeometry.x(); + int posy = windowGeometry.y(); if (!WindowPositionValid(posx, posy)) { QRect rect = App()->desktop()->availableGeometry(); setGeometry(QStyle::alignedRect( Qt::LeftToRight, Qt::AlignCenter, size(), rect)); - } else { - setGeometry(posx, posy, width, height); } } @@ -1450,14 +1447,8 @@ OBSBasic::~OBSBasic() QList splitterSizes = ui->mainSplitter->sizes(); bool alwaysOnTop = IsAlwaysOnTop(this); - config_set_int(App()->GlobalConfig(), "BasicWindow", "cx", - lastGeom.width()); - config_set_int(App()->GlobalConfig(), "BasicWindow", "cy", - lastGeom.height()); - config_set_int(App()->GlobalConfig(), "BasicWindow", "posx", - lastGeom.x()); - config_set_int(App()->GlobalConfig(), "BasicWindow", "posy", - lastGeom.y()); + config_set_string(App()->GlobalConfig(), "BasicWindow", "geometry", + saveGeometry().toBase64().constData()); config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterTop", splitterSizes[0]); config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterBottom", From 92b216b46822d5b4b7ac5884262950b77ecd2b2b Mon Sep 17 00:00:00 2001 From: Christoph Hohmann Date: Mon, 8 Aug 2016 11:52:57 +0200 Subject: [PATCH 002/107] obs-filters: Fix step size for noise suppression filter --- plugins/obs-filters/noise-suppress-filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-filters/noise-suppress-filter.c b/plugins/obs-filters/noise-suppress-filter.c index 99cdb3aba97d27..9c9bb8c1246ee1 100644 --- a/plugins/obs-filters/noise-suppress-filter.c +++ b/plugins/obs-filters/noise-suppress-filter.c @@ -277,7 +277,7 @@ static obs_properties_t *noise_suppress_properties(void *data) obs_properties_t *ppts = obs_properties_create(); obs_properties_add_int_slider(ppts, S_SUPPRESS_LEVEL, - TEXT_SUPPRESS_LEVEL, SUP_MIN, SUP_MAX, 0); + TEXT_SUPPRESS_LEVEL, SUP_MIN, SUP_MAX, 1); UNUSED_PARAMETER(data); return ppts; From 6f9065ba3bc3b5667d3075f180760c0ada65c479 Mon Sep 17 00:00:00 2001 From: Jamy Timmermans Date: Fri, 12 Aug 2016 02:56:42 +0200 Subject: [PATCH 003/107] rtmp-services: Add new Beam.pro ingests --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 28 ++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index d323130588268c..47dafbce71412c 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 29, + "version": 30, "files": [ { "name": "services.json", - "version": 29 + "version": 30 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 1cc56da00a1c23..f901fbde08bee7 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -258,17 +258,41 @@ "name": "EU: Amsterdam", "url": "rtmp://ingest-ams.beam.pro:1935/beam" }, + { + "name": "EU: Milan", + "url": "rtmp://ingest-mil.beam.pro:1935/beam" + }, + { + "name": "EU: Paris", + "url": "rtmp://ingest-par.beam.pro:1935/beam" + }, { "name": "EU: Frankfurt", "url": "rtmp://ingest-fra.beam.pro:1935/beam" }, + { + "name": "Brazil: Sao Paulo", + "url": "rtmp://ingest-sao.beam.pro:1935/beam" + }, { "name": "Australia: Melbourne", "url": "rtmp://ingest-mel.beam.pro:1935/beam" }, { - "name": "Brazil: Sao Paulo", - "url": "rtmp://ingest-sao.beam.pro:1935/beam" + "name": "Australia: Sydney", + "url": "rtmp://ingest-syd.beam.pro:1935/beam" + }, + { + "name": "Mexico: Mexico City", + "url": "rtmp://ingest-mex.beam.pro:1935/beam" + }, + { + "name": "Asia: Hong Kong", + "url": "rtmp://ingest-hkg.beam.pro:1935/beam" + }, + { + "name": "Asia: Tokyo", + "url": "rtmp://ingest-tok.beam.pro:1935/beam" } ], "recommended": { From 27ac2cf9407c1e2d82f22748100b28f45f640d96 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 11 Aug 2016 15:48:37 -0700 Subject: [PATCH 004/107] obs-outputs: Remove custom version from FMLE string Causes issues with certain servers that don't parse it properly and only expect a specific string. Use the RTMP onMetaData to get the OBS version instead when possible. --- plugins/obs-outputs/rtmp-stream.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 822f10efe681de..3488dfe6ad8e6a 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -586,18 +586,7 @@ static int try_connect(struct rtmp_stream *stream) RTMP_EnableWrite(&stream->rtmp); - dstr_copy(&stream->encoder_name, "FMLE/3.0 (compatible; obs-studio/"); - -#ifdef HAVE_OBSCONFIG_H - dstr_cat(&stream->encoder_name, OBS_VERSION); -#else - dstr_catf(&stream->encoder_name, "%d.%d.%d", - LIBOBS_API_MAJOR_VER, - LIBOBS_API_MINOR_VER, - LIBOBS_API_PATCH_VER); -#endif - - dstr_cat(&stream->encoder_name, "; FMSc/1.0)"); + dstr_copy(&stream->encoder_name, "FMLE/3.0 (compatible; FMSc/1.0)"); set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username); set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password); From 6d33f7e0915e81c46bf00557ce69c5ec8ce23b49 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 8 Aug 2016 21:54:06 -0700 Subject: [PATCH 005/107] win-capture: Add trick to ensure game cap. shared tex support This new trick forgoes the use of patches and allows the ability to use shared GPU resources despite being a Direct3D 9.0c context. --- .../get-graphics-offsets/d3d9-offsets.cpp | 90 +++++++++++++++++++ .../get-graphics-offsets.c | 2 + plugins/win-capture/graphics-hook-info.h | 2 + .../graphics-hook/d3d9-capture.cpp | 35 ++++++-- plugins/win-capture/load-graphics-offsets.c | 4 + 5 files changed, 128 insertions(+), 5 deletions(-) diff --git a/plugins/win-capture/get-graphics-offsets/d3d9-offsets.cpp b/plugins/win-capture/get-graphics-offsets/d3d9-offsets.cpp index b6d7ddc3f86a6f..499392f8b48681 100644 --- a/plugins/win-capture/get-graphics-offsets/d3d9-offsets.cpp +++ b/plugins/win-capture/get-graphics-offsets/d3d9-offsets.cpp @@ -80,17 +80,107 @@ static inline void d3d9_free(d3d9_info &info) DestroyWindow(info.hwnd); } +#ifdef _WIN64 + +#define CMP_SIZE 21 + +static const uint8_t mask[CMP_SIZE] = +{0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, + 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00}; + +static const uint8_t mask_cmp[CMP_SIZE] = +{0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x75, 0x00, + 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00}; +#else + +#define CMP_SIZE 19 + +static const uint8_t mask[CMP_SIZE] = +{0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00}; + +static const uint8_t mask_cmp[CMP_SIZE] = +{0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x75, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00}; +#endif + +#define MAX_FUNC_SCAN_BYTES 200 + +static inline bool pattern_matches(uint8_t *byte) +{ + for (size_t i = 0; i < CMP_SIZE; i++) { + if ((byte[i] & mask[i]) != mask_cmp[i]) + return false; + } + + return true; +} + void get_d3d9_offsets(struct d3d9_offsets *offsets) { d3d9_info info = {}; bool success = d3d9_init(info); if (success) { + uint8_t **vt = *(uint8_t***)info.device; + uint8_t *crr = vt[125]; + offsets->present = vtable_offset(info.module, info.device, 17); offsets->present_ex = vtable_offset(info.module, info.device, 121); offsets->present_swap = vtable_offset(info.module, info.swap, 3); + + for (size_t i = 0; i < MAX_FUNC_SCAN_BYTES; i++) { + if (pattern_matches(&crr[i])) { +#define get_offset(x) *(uint32_t*)&crr[i + x] +#ifdef _WIN64 + uint32_t off1 = get_offset(3); + uint32_t off2 = get_offset(9); +#else + uint32_t off1 = get_offset(2); + uint32_t off2 = get_offset(8); +#endif + + /* check to make sure offsets are within + * expected values */ + if (off1 > 0xFFFF || off2 > 0xFFFF) + break; + + /* check to make sure offsets actually point + * toward expected data */ +#ifdef _MSC_VER + __try { + uint8_t *ptr = (uint8_t*)(info.device); + + uint8_t *d3d9_ptr = + *(uint8_t**)(ptr + off1); + if (d3d9_ptr != (uint8_t*)info.d3d9ex) + break; + + BOOL &is_d3d9ex = + *(BOOL*)(d3d9_ptr + off2); + if (is_d3d9ex != TRUE) + break; + + } __except(EXCEPTION_EXECUTE_HANDLER) { + break; + } +#endif + + offsets->d3d9_clsoff = off1; + offsets->is_d3d9ex_clsoff = off2; + break; + } + } } d3d9_free(info); diff --git a/plugins/win-capture/get-graphics-offsets/get-graphics-offsets.c b/plugins/win-capture/get-graphics-offsets/get-graphics-offsets.c index fc12f6116f245f..3345c680e46eee 100644 --- a/plugins/win-capture/get-graphics-offsets/get-graphics-offsets.c +++ b/plugins/win-capture/get-graphics-offsets/get-graphics-offsets.c @@ -31,6 +31,8 @@ int main(int argc, char *argv[]) printf("present=0x%"PRIx32"\n", d3d9.present); printf("present_ex=0x%"PRIx32"\n", d3d9.present_ex); printf("present_swap=0x%"PRIx32"\n", d3d9.present_swap); + printf("d3d9_clsoff=0x%"PRIx32"\n", d3d9.d3d9_clsoff); + printf("is_d3d9ex_clsoff=0x%"PRIx32"\n", d3d9.is_d3d9ex_clsoff); printf("[dxgi]\n"); printf("present=0x%"PRIx32"\n", dxgi.present); printf("resize=0x%"PRIx32"\n", dxgi.resize); diff --git a/plugins/win-capture/graphics-hook-info.h b/plugins/win-capture/graphics-hook-info.h index ff21372fc23a02..128fc97e50ebf0 100644 --- a/plugins/win-capture/graphics-hook-info.h +++ b/plugins/win-capture/graphics-hook-info.h @@ -33,6 +33,8 @@ struct d3d9_offsets { uint32_t present; uint32_t present_ex; uint32_t present_swap; + uint32_t d3d9_clsoff; + uint32_t is_d3d9ex_clsoff; }; struct dxgi_offsets { diff --git a/plugins/win-capture/graphics-hook/d3d9-capture.cpp b/plugins/win-capture/graphics-hook/d3d9-capture.cpp index 65ca74f1b1c4e2..221aeb296124db 100644 --- a/plugins/win-capture/graphics-hook/d3d9-capture.cpp +++ b/plugins/win-capture/graphics-hook/d3d9-capture.cpp @@ -223,14 +223,30 @@ static inline bool d3d9_shtex_init_shtex() static inline bool d3d9_shtex_init_copytex() { - uint8_t *patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch); + struct d3d9_offsets offsets = global_hook_info->offsets.d3d9; + uint8_t *patch_addr = nullptr; + BOOL *p_is_d3d9 = nullptr; uint8_t saved_data[MAX_PATCH_SIZE]; size_t patch_size = 0; + BOOL was_d3d9ex = false; IDirect3DTexture9 *tex; DWORD protect_val; HRESULT hr; - if (patch_addr) { + if (offsets.d3d9_clsoff && offsets.is_d3d9ex_clsoff) { + uint8_t *device_ptr = (uint8_t*)(data.device); + uint8_t *d3d9_ptr = + *(uint8_t**)(device_ptr + offsets.d3d9_clsoff); + p_is_d3d9 = (BOOL*)(d3d9_ptr + offsets.is_d3d9ex_clsoff); + } else { + patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch); + } + + if (p_is_d3d9) { + was_d3d9ex = *p_is_d3d9; + *p_is_d3d9 = true; + + } else if (patch_addr) { patch_size = patch[data.patch].size; VirtualProtect(patch_addr, patch_size, PAGE_EXECUTE_READWRITE, &protect_val); @@ -242,7 +258,10 @@ static inline bool d3d9_shtex_init_copytex() D3DUSAGE_RENDERTARGET, data.d3d9_format, D3DPOOL_DEFAULT, &tex, &data.handle); - if (patch_addr && patch_size) { + if (p_is_d3d9) { + *p_is_d3d9 = was_d3d9ex; + + } else if (patch_addr && patch_size) { memcpy(patch_addr, saved_data, patch_size); VirtualProtect(patch_addr, patch_size, protect_val, &protect_val); @@ -449,6 +468,9 @@ static bool d3d9_init_format_swapchain(uint32_t &cx, uint32_t &cy, HWND &window) static void d3d9_init(IDirect3DDevice9 *device) { IDirect3DDevice9Ex *d3d9ex = nullptr; + bool has_d3d9ex_bool_offset = + global_hook_info->offsets.d3d9.d3d9_clsoff && + global_hook_info->offsets.d3d9.is_d3d9ex_clsoff; bool success; uint32_t cx = 0; uint32_t cy = 0; @@ -463,8 +485,10 @@ static void d3d9_init(IDirect3DDevice9 *device) if (SUCCEEDED(hr)) { d3d9ex->Release(); data.patch = -1; - } else { + } else if (!has_d3d9ex_bool_offset) { data.patch = get_d3d9_patch(data.d3d9); + } else { + data.patch = -1; } if (!d3d9_init_format_backbuffer(cx, cy, window)) { @@ -473,7 +497,8 @@ static void d3d9_init(IDirect3DDevice9 *device) } } - if (global_hook_info->force_shmem || (!d3d9ex && data.patch == -1)) { + if (global_hook_info->force_shmem || + (!d3d9ex && data.patch == -1 && !has_d3d9ex_bool_offset)) { success = d3d9_shmem_init(cx, cy, window); } else { success = d3d9_shtex_init(cx, cy, window); diff --git a/plugins/win-capture/load-graphics-offsets.c b/plugins/win-capture/load-graphics-offsets.c index 0602a1cb91b9cc..4465d622cf39fd 100644 --- a/plugins/win-capture/load-graphics-offsets.c +++ b/plugins/win-capture/load-graphics-offsets.c @@ -30,6 +30,10 @@ static inline bool load_offsets_from_string(struct graphics_offsets *offsets, (uint32_t)config_get_uint(config, "d3d9", "present_ex"); offsets->d3d9.present_swap = (uint32_t)config_get_uint(config, "d3d9", "present_swap"); + offsets->d3d9.d3d9_clsoff = + (uint32_t)config_get_uint(config, "d3d9", "d3d9_clsoff"); + offsets->d3d9.is_d3d9ex_clsoff = + (uint32_t)config_get_uint(config, "d3d9", "is_d3d9ex_clsoff"); offsets->dxgi.present = (uint32_t)config_get_uint(config, "dxgi", "present"); From 630207d59047338512d438e85b5d8c1c9468d998 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 13 Aug 2016 01:07:20 -0700 Subject: [PATCH 006/107] obs-outputs: Improve frame drop testing Measures packet data rate and sleeps to prevent data from going above the set data rate. Uncomment the TEST_FRAMEDROPS macro, then set DROPTEST_MAX_KBPS to the desired kb/s (for example 3000 for 3000 kilobits per second), and then it will limit the data rate to that specified amount, forcing the thread to sleep to ensure it can only output the desired data rate. --- plugins/obs-outputs/rtmp-stream.c | 57 ++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 3488dfe6ad8e6a..08d04d56cbe7db 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -47,6 +47,17 @@ //#define TEST_FRAMEDROPS +#ifdef TEST_FRAMEDROPS + +#define DROPTEST_MAX_KBPS 3000 +#define DROPTEST_MAX_BYTES (DROPTEST_MAX_KBPS * 1000 / 8) + +struct droptest_info { + uint64_t ts; + size_t size; +}; +#endif + struct rtmp_stream { obs_output_t *output; @@ -82,6 +93,11 @@ struct rtmp_stream { uint64_t total_bytes_sent; int dropped_frames; +#ifdef TEST_FRAMEDROPS + struct circlebuf droptest_info; + size_t droptest_size; +#endif + RTMP rtmp; }; @@ -172,6 +188,9 @@ static void rtmp_stream_destroy(void *data) os_sem_destroy(stream->send_sem); pthread_mutex_destroy(&stream->packets_mutex); circlebuf_free(&stream->packets); +#ifdef TEST_FRAMEDROPS + circlebuf_free(&stream->droptest_info); +#endif bfree(stream); } } @@ -285,6 +304,40 @@ static bool discard_recv_data(struct rtmp_stream *stream, size_t size) return true; } +#ifdef TEST_FRAMEDROPS +static void droptest_cap_data_rate(struct rtmp_stream *stream, size_t size) +{ + uint64_t ts = os_gettime_ns(); + struct droptest_info info; + + info.ts = ts; + info.size = size; + + circlebuf_push_back(&stream->droptest_info, &info, sizeof(info)); + stream->droptest_size += size; + + if (stream->droptest_info.size) { + circlebuf_peek_front(&stream->droptest_info, + &info, sizeof(info)); + + if (stream->droptest_size > DROPTEST_MAX_BYTES) { + uint64_t elapsed = ts - info.ts; + + if (elapsed < 1000000000ULL) { + elapsed = 1000000000ULL - elapsed; + os_sleepto_ns(ts + elapsed); + } + + while (stream->droptest_size > DROPTEST_MAX_BYTES) { + circlebuf_pop_front(&stream->droptest_info, + &info, sizeof(info)); + stream->droptest_size -= info.size; + } + } + } +} +#endif + static int send_packet(struct rtmp_stream *stream, struct encoder_packet *packet, bool is_header, size_t idx) { @@ -306,9 +359,11 @@ static int send_packet(struct rtmp_stream *stream, } flv_packet_mux(packet, &data, &size, is_header); + #ifdef TEST_FRAMEDROPS - os_sleep_ms(rand() % 40); + droptest_cap_data_rate(stream, size); #endif + ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size, (int)idx); bfree(data); From 7d5df34a6b66f2ad8f9624f2e95d5b384ac67a45 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 13 Aug 2016 01:13:00 -0700 Subject: [PATCH 007/107] libobs: Do not set p-frames to highest priority P-frames were initially set as highest priority to prevent them from being dropped (not sure what the rationale was behind this), but this caused a problem where if there's too much congestion for whatever reason data will continue to stay buffered, so to prevent this p-frames should be droppable. --- libobs/obs-avc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libobs/obs-avc.c b/libobs/obs-avc.c index 757865a967ac93..647427ac8c1aa3 100644 --- a/libobs/obs-avc.c +++ b/libobs/obs-avc.c @@ -94,12 +94,7 @@ const uint8_t *obs_avc_find_startcode(const uint8_t *p, const uint8_t *end) static inline int get_drop_priority(int priority) { - switch (priority) { - case OBS_NAL_PRIORITY_DISPOSABLE: return OBS_NAL_PRIORITY_DISPOSABLE; - case OBS_NAL_PRIORITY_LOW: return OBS_NAL_PRIORITY_LOW; - } - - return OBS_NAL_PRIORITY_HIGHEST; + return priority; } static void serialize_avc_data(struct serializer *s, const uint8_t *data, From 34590b4b6a678724d5954aed298d5f1a97974425 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 13 Aug 2016 01:16:32 -0700 Subject: [PATCH 008/107] obs-outputs: Allow p-frames to be dropped Previously, for an unknown reason p-frames were marked as highest priority along with i-frames (keyframes), which means they could not be dropped. This would cause a problem where if for whatever reason there's too much congestion, data would continually buffer. This fixes the issue by dropping p-frames at a separate (higher) threshold than b-frames. --- plugins/obs-outputs/rtmp-stream.c | 77 ++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 08d04d56cbe7db..99e59681ef053b 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -42,6 +42,7 @@ #define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) #define OPT_DROP_THRESHOLD "drop_threshold_ms" +#define OPT_PFRAME_DROP_THRESHOLD "pframe_drop_threshold_ms" #define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec" #define OPT_BIND_IP "bind_ip" @@ -86,6 +87,8 @@ struct rtmp_stream { /* frame drop variables */ int64_t drop_threshold_usec; int64_t min_drop_dts_usec; + int64_t pframe_drop_threshold_usec; + int64_t pframe_min_drop_dts_usec; int min_priority; int64_t last_dts_usec; @@ -696,6 +699,8 @@ static bool init_connect(struct rtmp_stream *stream) obs_service_t *service; obs_data_t *settings; const char *bind_ip; + int64_t drop_p; + int64_t drop_b; if (stopping(stream)) pthread_join(stream->send_thread, NULL); @@ -719,11 +724,17 @@ static bool init_connect(struct rtmp_stream *stream) dstr_copy(&stream->password, obs_service_get_password(service)); dstr_depad(&stream->path); dstr_depad(&stream->key); - stream->drop_threshold_usec = - (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000; + drop_b = (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD); + drop_p = (int64_t)obs_data_get_int(settings, OPT_PFRAME_DROP_THRESHOLD); stream->max_shutdown_time_sec = (int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC); + if (drop_p < (drop_b + 200)) + drop_p = drop_b + 200; + + stream->drop_threshold_usec = 1000 * drop_b; + stream->pframe_drop_threshold_usec = 1000 * drop_p; + bind_ip = obs_data_get_string(settings, OPT_BIND_IP); dstr_copy(&stream->bind_ip, bind_ip); @@ -785,14 +796,18 @@ static inline size_t num_buffered_packets(struct rtmp_stream *stream) return stream->packets.size / sizeof(struct encoder_packet); } -static void drop_frames(struct rtmp_stream *stream) +static void drop_frames(struct rtmp_stream *stream, const char *name, + int highest_priority, int64_t *p_min_dts_usec) { struct circlebuf new_buf = {0}; - int drop_priority = 0; uint64_t last_drop_dts_usec = 0; int num_frames_dropped = 0; - debug("Previous packet count: %d", (int)num_buffered_packets(stream)); +#ifdef _DEBUG + int start_packets = (int)num_buffered_packets(stream); +#else + UNUSED_PARAMETER(name); +#endif circlebuf_reserve(&new_buf, sizeof(struct encoder_packet) * 8); @@ -804,56 +819,71 @@ static void drop_frames(struct rtmp_stream *stream) /* do not drop audio data or video keyframes */ if (packet.type == OBS_ENCODER_AUDIO || - packet.drop_priority == OBS_NAL_PRIORITY_HIGHEST) { + packet.drop_priority >= highest_priority) { circlebuf_push_back(&new_buf, &packet, sizeof(packet)); } else { - if (drop_priority < packet.drop_priority) - drop_priority = packet.drop_priority; - num_frames_dropped++; obs_free_encoder_packet(&packet); } } circlebuf_free(&stream->packets); - stream->packets = new_buf; - stream->min_priority = drop_priority; - stream->min_drop_dts_usec = last_drop_dts_usec; + stream->packets = new_buf; + + if (stream->min_priority < highest_priority) + stream->min_priority = highest_priority; + + *p_min_dts_usec = last_drop_dts_usec; stream->dropped_frames += num_frames_dropped; - debug("New packet count: %d", (int)num_buffered_packets(stream)); +#ifdef _DEBUG + debug("Dropped %s, prev packet count: %d, new packet count: %d", + name, + start_packets, + (int)num_buffered_packets(stream)); +#endif } -static void check_to_drop_frames(struct rtmp_stream *stream) +static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes) { struct encoder_packet first; int64_t buffer_duration_usec; - - if (num_buffered_packets(stream) < 5) + size_t num_packets = num_buffered_packets(stream); + const char *name = pframes ? "p-frames" : "b-frames"; + int priority = pframes ? + OBS_NAL_PRIORITY_HIGHEST : OBS_NAL_PRIORITY_HIGH; + int64_t *p_min_dts_usec = pframes ? + &stream->pframe_min_drop_dts_usec : + &stream->min_drop_dts_usec; + int64_t drop_threshold = pframes ? + stream->pframe_drop_threshold_usec : + stream->drop_threshold_usec; + + if (num_packets < 5) return; circlebuf_peek_front(&stream->packets, &first, sizeof(first)); /* do not drop frames if frames were just dropped within this time */ - if (first.dts_usec < stream->min_drop_dts_usec) + if (first.dts_usec < *p_min_dts_usec) return; /* if the amount of time stored in the buffered packets waiting to be * sent is higher than threshold, drop frames */ buffer_duration_usec = stream->last_dts_usec - first.dts_usec; - if (buffer_duration_usec > stream->drop_threshold_usec) { - drop_frames(stream); - debug("dropping %" PRId64 " worth of frames", - buffer_duration_usec); + if (buffer_duration_usec > drop_threshold) { + debug("buffer_duration_usec: %lld", buffer_duration_usec); + drop_frames(stream, name, priority, p_min_dts_usec); } } static bool add_video_packet(struct rtmp_stream *stream, struct encoder_packet *packet) { - check_to_drop_frames(stream); + check_to_drop_frames(stream, false); + check_to_drop_frames(stream, true); /* if currently dropping frames, drop packets until it reaches the * desired priority */ @@ -899,7 +929,8 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet) static void rtmp_stream_defaults(obs_data_t *defaults) { - obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 600); + obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 500); + obs_data_set_default_int(defaults, OPT_PFRAME_DROP_THRESHOLD, 800); obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 5); obs_data_set_default_string(defaults, OPT_BIND_IP, "default"); } From eca0ca8424161effcc104e8e568a0bed067f2e5e Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Mon, 15 Aug 2016 17:55:07 +0200 Subject: [PATCH 009/107] obs-outputs: Prefer IPv4 addresses for RTMP connections --- plugins/obs-outputs/librtmp/rtmp.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/plugins/obs-outputs/librtmp/rtmp.c b/plugins/obs-outputs/librtmp/rtmp.c index f498da91a371f4..404c6b9d22b604 100644 --- a/plugins/obs-outputs/librtmp/rtmp.c +++ b/plugins/obs-outputs/librtmp/rtmp.c @@ -695,10 +695,10 @@ add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, goto finish; } - // they should come back in OS preferred order + // prefer ipv4 results, since lots of ISPs have broken ipv6 connectivity for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { - if (ptr->ai_family == AF_INET || ptr->ai_family == AF_INET6) + if (ptr->ai_family == AF_INET) { memcpy(service, ptr->ai_addr, ptr->ai_addrlen); *addrlen = (socklen_t)ptr->ai_addrlen; @@ -706,6 +706,19 @@ add_addr_info(struct sockaddr_storage *service, socklen_t *addrlen, AVal *host, } } + if (!*addrlen) + { + for (ptr = result; ptr != NULL; ptr = ptr->ai_next) + { + if (ptr->ai_family == AF_INET6) + { + memcpy(service, ptr->ai_addr, ptr->ai_addrlen); + *addrlen = (socklen_t)ptr->ai_addrlen; + break; + } + } + } + freeaddrinfo(result); if (service->ss_family == AF_UNSPEC || *addrlen == 0) From d305343e8be69b09d65c9576d0b87c2632d498a0 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 18 Aug 2016 16:59:41 -0700 Subject: [PATCH 010/107] libobs: Do not enum private sources with obs_enum_sources --- libobs/obs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libobs/obs.c b/libobs/obs.c index 060134361014e5..eabbdb4147874e 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1219,6 +1219,7 @@ void obs_enum_sources(bool (*enum_proc)(void*, obs_source_t*), void *param) (obs_source_t*)source->context.next; if ((source->info.type == OBS_SOURCE_TYPE_INPUT) != 0 && + !source->context.private && !enum_proc(param, source)) break; From 0c0f6031e2c312cb906b454b7803e929103e2b5b Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Wed, 17 Aug 2016 13:09:36 -0500 Subject: [PATCH 011/107] libobs/util: Use FormatMessage on error when LoadLibrary fails Outputting a human-readable error message on library load failure makes it a little bit easier for plugin developers to determine why a plugin library may have failed to load (such as missing dependency), rather than having to look up the error code each time. Closes jp9000/obs-studio#596 --- libobs/util/platform-windows.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 2e33cfaf1ecaaf..b4201b9030f411 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -84,9 +84,24 @@ void *os_dlopen(const char *path) if (wpath_slash) SetDllDirectoryW(NULL); - if (!h_library) - blog(LOG_INFO, "LoadLibrary failed for '%s', error: %ld", - path, GetLastError()); + if (!h_library) { + DWORD error = GetLastError(); + char *message = NULL; + + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, error, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&message, 0, NULL); + + blog(LOG_INFO, "LoadLibrary failed for '%s': %s (%lu)", + path, message, error); + + if (message) + LocalFree(message); + } + return h_library; } From e6f950fde924f41c2f9ee746427f8cf3b259c4bd Mon Sep 17 00:00:00 2001 From: Andreas Reischuck Date: Wed, 10 Aug 2016 00:52:22 +0200 Subject: [PATCH 012/107] vlc-video: Fix URLs not working on windows libvlc_media_new_path implies a file. To get media based upon URLs, use libvlc_media_new_location. Additionally, if using a URL, it's best to give it some playback caching/buffering, at least 100 milliseconds. Closes jp9000/obs-studio#590 --- plugins/vlc-video/vlc-video-plugin.c | 4 ++++ plugins/vlc-video/vlc-video-plugin.h | 5 +++++ plugins/vlc-video/vlc-video-source.c | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/plugins/vlc-video/vlc-video-plugin.c b/plugins/vlc-video/vlc-video-plugin.c index 0cbe7aa54e2225..49c4e484387455 100644 --- a/plugins/vlc-video/vlc-video-plugin.c +++ b/plugins/vlc-video/vlc-video-plugin.c @@ -16,6 +16,8 @@ LIBVLC_EVENT_ATTACH libvlc_event_attach_; /* libvlc media */ LIBVLC_MEDIA_NEW_PATH libvlc_media_new_path_; +LIBVLC_MEDIA_NEW_LOCATION libvlc_media_new_location_; +LIBVLC_MEDIA_ADD_OPTION libvlc_media_add_option_; LIBVLC_MEDIA_RELEASE libvlc_media_release_; LIBVLC_MEDIA_RELEASE libvlc_media_retain_; @@ -76,6 +78,8 @@ static bool load_vlc_funcs(void) /* libvlc media */ LOAD_VLC_FUNC(libvlc_media_new_path); + LOAD_VLC_FUNC(libvlc_media_new_location); + LOAD_VLC_FUNC(libvlc_media_add_option); LOAD_VLC_FUNC(libvlc_media_release); LOAD_VLC_FUNC(libvlc_media_retain); diff --git a/plugins/vlc-video/vlc-video-plugin.h b/plugins/vlc-video/vlc-video-plugin.h index 9eefb39ec4aa35..53cb2551e03344 100644 --- a/plugins/vlc-video/vlc-video-plugin.h +++ b/plugins/vlc-video/vlc-video-plugin.h @@ -29,6 +29,9 @@ typedef int (*LIBVLC_EVENT_ATTACH)(libvlc_event_manager_t *p_event_manager, /* libvlc media */ typedef libvlc_media_t *(*LIBVLC_MEDIA_NEW_PATH)( libvlc_instance_t *p_instance, const char *path); +typedef libvlc_media_t *(*LIBVLC_MEDIA_NEW_LOCATION)( + libvlc_instance_t *p_instance, const char *location); +typedef void (*LIBVLC_MEDIA_ADD_OPTION)(libvlc_media_t *p_md, const char *options); typedef void (*LIBVLC_MEDIA_RETAIN)(libvlc_media_t *p_md); typedef void (*LIBVLC_MEDIA_RELEASE)(libvlc_media_t *p_md); @@ -119,6 +122,8 @@ extern LIBVLC_EVENT_ATTACH libvlc_event_attach_; /* libvlc media */ extern LIBVLC_MEDIA_NEW_PATH libvlc_media_new_path_; +extern LIBVLC_MEDIA_NEW_LOCATION libvlc_media_new_location_; +extern LIBVLC_MEDIA_ADD_OPTION libvlc_media_add_option_; extern LIBVLC_MEDIA_RELEASE libvlc_media_release_; extern LIBVLC_MEDIA_RETAIN libvlc_media_retain_; diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c index 824d57cc57dd21..5c4079bbe666e1 100644 --- a/plugins/vlc-video/vlc-video-source.c +++ b/plugins/vlc-video/vlc-video-source.c @@ -76,7 +76,9 @@ static libvlc_media_t *get_media(struct darray *array, const char *path) static inline libvlc_media_t *create_media_from_file(const char *file) { - return libvlc_media_new_path_(libvlc, file); + return (file && strstr(file, "://") != NULL) + ? libvlc_media_new_location_(libvlc, file) + : libvlc_media_new_path_(libvlc, file); } static void free_files(struct darray *array) @@ -299,11 +301,18 @@ static unsigned vlcs_video_format(void **p_data, char *chroma, unsigned *width, enum video_format new_format; enum video_range_type range; bool new_range; + unsigned new_width = 0; + unsigned new_height = 0; size_t i = 0; new_format = convert_vlc_video_format(chroma, &new_range); - libvlc_video_get_size_(c->media_player, 0, width, height); + libvlc_video_get_size_(c->media_player, 0, &new_width, &new_height); + + if (new_width && new_height) { + *width = new_width; + *height = new_height; + } /* don't allocate a new frame if format/width/height hasn't changed */ if (c->frame.format != new_format || @@ -382,12 +391,14 @@ static void add_file(struct vlc_source *c, struct darray *array, struct media_file_data data; struct dstr new_path = {0}; libvlc_media_t *new_media; + bool is_url = path && strstr(path, "://") != NULL; new_files.da = *array; dstr_copy(&new_path, path); #ifdef _WIN32 - dstr_replace(&new_path, "/", "\\"); + if (!is_url) + dstr_replace(&new_path, "/", "\\"); #endif path = new_path.array; @@ -399,6 +410,10 @@ static void add_file(struct vlc_source *c, struct darray *array, new_media = create_media_from_file(path); if (new_media) { + if (is_url) + libvlc_media_add_option_(new_media, + ":network-caching=100"); + data.path = new_path.array; data.media = new_media; da_push_back(new_files, &data); From ba1112470db1363d271d9c4aefb89e0c66a22ca2 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 20 Aug 2016 12:15:34 -0700 Subject: [PATCH 013/107] vlc-video: Comment libvlc_video_get_size usage with video files --- plugins/vlc-video/vlc-video-source.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c index 5c4079bbe666e1..bac5de380fcd8a 100644 --- a/plugins/vlc-video/vlc-video-source.c +++ b/plugins/vlc-video/vlc-video-source.c @@ -307,6 +307,13 @@ static unsigned vlcs_video_format(void **p_data, char *chroma, unsigned *width, new_format = convert_vlc_video_format(chroma, &new_range); + /* This is used because VLC will by default try to use a different + * scaling than what the file uses (probably for optimization reasons). + * For example, if the file is 1920x1080, it will try to render it by + * 1920x1088, which isn't what we want. Calling libvlc_video_get_size + * gets the actual video file's size, and thus fixes the problem. + * However this doesn't work with URLs, so if it returns a 0 value, it + * shouldn't be used. */ libvlc_video_get_size_(c->media_player, 0, &new_width, &new_height); if (new_width && new_height) { From 1ae46a2940bc26f8a0805229523905633505dbeb Mon Sep 17 00:00:00 2001 From: Cephas Reis Date: Sat, 6 Aug 2016 14:24:47 -0500 Subject: [PATCH 014/107] linux-jack: Allow jack support on OSX Allows usage of jack on OSX (as well as windows later). Closes jp9000/obs-studio#587 --- plugins/CMakeLists.txt | 1 + plugins/linux-jack/jack-wrapper.c | 2 +- plugins/linux-jack/jack-wrapper.h | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index d178acceab3e43..3087d75d9bf85a 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -19,6 +19,7 @@ elseif(APPLE) add_subdirectory(mac-syphon) add_subdirectory(decklink/mac) add_subdirectory(vlc-video) + add_subdirectory(linux-jack) elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") add_subdirectory(linux-capture) add_subdirectory(linux-pulseaudio) diff --git a/plugins/linux-jack/jack-wrapper.c b/plugins/linux-jack/jack-wrapper.c index f55d1ea20a31c6..25b125cef1782c 100644 --- a/plugins/linux-jack/jack-wrapper.c +++ b/plugins/linux-jack/jack-wrapper.c @@ -17,7 +17,7 @@ along with this program. If not, see . #include "jack-wrapper.h" -#include +#include #include #include diff --git a/plugins/linux-jack/jack-wrapper.h b/plugins/linux-jack/jack-wrapper.h index 85202fff8105a3..460fe91890eb3e 100644 --- a/plugins/linux-jack/jack-wrapper.h +++ b/plugins/linux-jack/jack-wrapper.h @@ -19,7 +19,7 @@ along with this program. If not, see . #include #include -#include +#include struct jack_data { obs_source_t *source; From 276e8530d9adffa67898b582d52d9f87a7bffa24 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 21 Aug 2016 12:05:25 -0700 Subject: [PATCH 015/107] UI: Refactor dup. code from stream/recording start/stop The code to enable/disable the profile menu along with incrementing/decrementing the sleep inhibitor was duplicated more times than necessary. --- obs/window-basic-main.cpp | 66 +++++++++++++++------------------------ obs/window-basic-main.hpp | 3 ++ 2 files changed, 29 insertions(+), 40 deletions(-) diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 9107f5236aaf89..b1224634297ec7 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -3566,18 +3566,32 @@ static inline void ClearProcessPriority() #define ClearProcessPriority() do {} while(false) #endif -void OBSBasic::StopStreaming() +inline void OBSBasic::OnActivate() { - SaveProject(); - - if (outputHandler->StreamingActive()) - outputHandler->StopStreaming(); + if (ui->profileMenu->isEnabled()) { + ui->profileMenu->setEnabled(false); + App()->IncrementSleepInhibition(); + UpdateProcessPriority(); + } +} +inline void OBSBasic::OnDeactivate() +{ if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); ClearProcessPriority(); } +} + +void OBSBasic::StopStreaming() +{ + SaveProject(); + + if (outputHandler->StreamingActive()) + outputHandler->StopStreaming(); + + OnDeactivate(); bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); @@ -3594,11 +3608,7 @@ void OBSBasic::ForceStopStreaming() if (outputHandler->StreamingActive()) outputHandler->ForceStopStreaming(); - if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(true); - App()->DecrementSleepInhibition(); - ClearProcessPriority(); - } + OnDeactivate(); bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), "BasicWindow", "RecordWhenStreaming"); @@ -3625,11 +3635,7 @@ void OBSBasic::StreamDelayStarting(int sec) ui->statusbar->StreamDelayStarting(sec); - if (ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(false); - App()->IncrementSleepInhibition(); - UpdateProcessPriority(); - } + OnActivate(); } void OBSBasic::StreamDelayStopping(int sec) @@ -3656,11 +3662,7 @@ void OBSBasic::StreamingStart() ui->streamButton->setEnabled(true); ui->statusbar->StreamStarted(outputHandler->streamOutput); - if (ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(false); - App()->IncrementSleepInhibition(); - UpdateProcessPriority(); - } + OnActivate(); blog(LOG_INFO, STREAMING_START); } @@ -3703,11 +3705,7 @@ void OBSBasic::StreamingStop(int code) ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); - if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(true); - App()->DecrementSleepInhibition(); - ClearProcessPriority(); - } + OnDeactivate(); blog(LOG_INFO, STREAMING_STOP); @@ -3744,11 +3742,7 @@ void OBSBasic::StopRecording() if (outputHandler->RecordingActive()) outputHandler->StopRecording(); - if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(true); - App()->DecrementSleepInhibition(); - ClearProcessPriority(); - } + OnDeactivate(); } void OBSBasic::RecordingStart() @@ -3756,11 +3750,7 @@ void OBSBasic::RecordingStart() ui->statusbar->RecordingStarted(outputHandler->fileOutput); ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); - if (ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(false); - App()->IncrementSleepInhibition(); - UpdateProcessPriority(); - } + OnActivate(); blog(LOG_INFO, RECORDING_START); } @@ -3787,11 +3777,7 @@ void OBSBasic::RecordingStop(int code) QTStr("Output.RecordError.Msg")); } - if (!outputHandler->Active() && !ui->profileMenu->isEnabled()) { - ui->profileMenu->setEnabled(true); - App()->DecrementSleepInhibition(); - ClearProcessPriority(); - } + OnDeactivate(); } void OBSBasic::on_streamButton_clicked() diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index c8d6febea414e8..4fedeee6c40e7a 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -282,6 +282,9 @@ class OBSBasic : public OBSMainWindow { return os_atomic_load_bool(&previewProgramMode); } + inline void OnActivate(); + inline void OnDeactivate(); + public slots: void StartStreaming(); void StopStreaming(); From b71f5cc4fcb324c84b63dd099d5f9b73cf7c8f38 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sat, 13 Aug 2016 09:36:17 -0500 Subject: [PATCH 016/107] UI: Add system tray capability This adds a system tray when enabled. There is also a option to minimize to the system tray when the app is started. Closes jp9000/obs-studio#595 --- obs/data/locale/en-US.ini | 9 ++ obs/forms/OBSBasicSettings.ui | 37 ++++++- obs/forms/images/tray_active.png | Bin 0 -> 38441 bytes obs/forms/obs.qrc | 1 + obs/obs-app.cpp | 4 + obs/window-basic-main-outputs.cpp | 20 ++-- obs/window-basic-main.cpp | 172 ++++++++++++++++++++++++++++-- obs/window-basic-main.hpp | 22 ++++ obs/window-basic-settings.cpp | 20 ++++ obs/window-basic-status-bar.cpp | 34 ++++-- obs/window-basic-status-bar.hpp | 1 + 11 files changed, 298 insertions(+), 22 deletions(-) create mode 100644 obs/forms/images/tray_active.png diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index 5be0dc1558a477..f3467f4d2b1f73 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -383,6 +383,8 @@ Basic.Settings.General.SourceSnapping="Snap Sources to other sources" Basic.Settings.General.SnapDistance="Snap Sensitivity" Basic.Settings.General.RecordWhenStreaming="Automatically record when streaming" Basic.Settings.General.KeepRecordingWhenStreamStops="Keep recording when stream stops" +Basic.Settings.General.SysTrayEnabled="Enable system tray icon" +Basic.Settings.General.SysTrayWhenStarted="Minimize to system tray when started" # basic mode 'stream' settings Basic.Settings.Stream="Stream" @@ -550,6 +552,13 @@ Basic.Hotkeys.StartRecording="Start Recording" Basic.Hotkeys.StopRecording="Stop Recording" Basic.Hotkeys.SelectScene="Switch to scene" +# system tray +Basic.SystemTray.Show="Show" +Basic.SystemTray.Hide="Hide" + +# system tray messages +Basic.SystemTray.Message.Reconnecting="Disconnected. Reconnecting..." + # hotkeys that may lack translation on certain operating systems Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui index be10b2f2030f91..64ce0fd1c5242a 100644 --- a/obs/forms/OBSBasicSettings.ui +++ b/obs/forms/OBSBasicSettings.ui @@ -192,14 +192,31 @@ - + + + + Basic.Settings.General.SysTrayEnabled + + + + + + + false + + + Basic.Settings.General.SysTrayWhenStarted + + + + Qt::Horizontal - + true @@ -3753,5 +3770,21 @@ + + systemTrayEnabled + toggled(bool) + systemTrayWhenStarted + setEnabled(bool) + + + 404 + 245 + + + 404 + 271 + + + diff --git a/obs/forms/images/tray_active.png b/obs/forms/images/tray_active.png new file mode 100644 index 0000000000000000000000000000000000000000..d8da1c5f92937dd4dd35c032148a75978638a329 GIT binary patch literal 38441 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 Q07VI%J8fZHatcrSKl@xD!~g&Q literal 0 HcmV?d00001 diff --git a/obs/forms/obs.qrc b/obs/forms/obs.qrc index 60ab59a4bd02e0..361fef4ae75af7 100644 --- a/obs/forms/obs.qrc +++ b/obs/forms/obs.qrc @@ -13,6 +13,7 @@ images/properties.png images/up.png images/obs.png + images/tray_active.png images/settings/advanced.png diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index 232f8c85f48f78..0584e18eb92593 100644 --- a/obs/obs-app.cpp +++ b/obs/obs-app.cpp @@ -360,6 +360,10 @@ bool OBSApp::InitGlobalConfigDefaults() "RecordWhenStreaming", false); config_set_default_bool(globalConfig, "BasicWindow", "KeepRecordingWhenStreamStops", false); + config_set_default_bool(globalConfig, "BasicWindow", + "SysTrayEnabled", true); + config_set_default_bool(globalConfig, "BasicWindow", + "SysTrayWhenStarted", false); config_set_default_bool(globalConfig, "BasicWindow", "ShowTransitions", true); config_set_default_bool(globalConfig, "BasicWindow", diff --git a/obs/window-basic-main-outputs.cpp b/obs/window-basic-main-outputs.cpp index e94503b3bcc9df..2415d75d684fd4 100644 --- a/obs/window-basic-main-outputs.cpp +++ b/obs/window-basic-main-outputs.cpp @@ -604,9 +604,13 @@ bool SimpleOutput::StartRecording() os_dir_t *dir = path ? os_opendir(path) : nullptr; if (!dir) { - QMessageBox::information(main, - QTStr("Output.BadPath.Title"), - QTStr("Output.BadPath.Text")); + if (main->isVisible()) + QMessageBox::information(main, + QTStr("Output.BadPath.Title"), + QTStr("Output.BadPath.Text")); + else + main->SysTrayNotify(QTStr("Output.BadPath.Text"), + QSystemTrayIcon::Warning); return false; } @@ -1147,9 +1151,13 @@ bool AdvancedOutput::StartRecording() os_dir_t *dir = path ? os_opendir(path) : nullptr; if (!dir) { - QMessageBox::information(main, - QTStr("Output.BadPath.Title"), - QTStr("Output.BadPath.Text")); + if (main->isVisible()) + QMessageBox::information(main, + QTStr("Output.BadPath.Title"), + QTStr("Output.BadPath.Text")); + else + main->SysTrayNotify(QTStr("Output.BadPath.Text"), + QSystemTrayIcon::Warning); return false; } diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index b1224634297ec7..d2aecbac3d3ef0 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -1181,6 +1181,8 @@ void OBSBasic::OBSInit() } ui->mainSplitter->setSizes(defSizes); + + SystemTray(true); } void OBSBasic::InitHotkeys() @@ -1447,8 +1449,11 @@ OBSBasic::~OBSBasic() QList splitterSizes = ui->mainSplitter->sizes(); bool alwaysOnTop = IsAlwaysOnTop(this); - config_set_string(App()->GlobalConfig(), "BasicWindow", "geometry", - saveGeometry().toBase64().constData()); + if (isVisible()) + config_set_string(App()->GlobalConfig(), + "BasicWindow", "geometry", + saveGeometry().toBase64().constData()); + config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterTop", splitterSizes[0]); config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterBottom", @@ -2610,6 +2615,8 @@ void OBSBasic::closeEvent(QCloseEvent *event) blog(LOG_INFO, SHUTDOWN_SEPARATOR); if (outputHandler && outputHandler->Active()) { + SetShowing(true); + QMessageBox::StandardButton button = QMessageBox::question( this, QTStr("ConfirmExit.Title"), QTStr("ConfirmExit.Text")); @@ -2668,6 +2675,7 @@ void OBSBasic::on_action_Settings_triggered() { OBSBasicSettings settings(this); settings.exec(); + SystemTray(false); } void OBSBasic::on_actionAdvAudioProperties_triggered() @@ -3533,10 +3541,14 @@ void OBSBasic::StartStreaming() ui->streamButton->setEnabled(false); ui->streamButton->setText(QTStr("Basic.Main.Connecting")); + sysTrayStream->setEnabled(false); + sysTrayStream->setText(ui->streamButton->text()); if (!outputHandler->StartStreaming(service)) { ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); } bool recordWhenStreaming = config_get_bool(GetGlobalConfig(), @@ -3572,6 +3584,8 @@ inline void OBSBasic::OnActivate() ui->profileMenu->setEnabled(false); App()->IncrementSleepInhibition(); UpdateProcessPriority(); + + trayIcon->setIcon(QIcon(":/res/images/tray_active.png")); } } @@ -3581,6 +3595,8 @@ inline void OBSBasic::OnDeactivate() ui->profileMenu->setEnabled(true); App()->DecrementSleepInhibition(); ClearProcessPriority(); + + trayIcon->setIcon(QIcon(":/res/images/obs.png")); } } @@ -3622,6 +3638,8 @@ void OBSBasic::StreamDelayStarting(int sec) { ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); if (!startStreamMenu.isNull()) startStreamMenu->deleteLater(); @@ -3642,6 +3660,8 @@ void OBSBasic::StreamDelayStopping(int sec) { ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); if (!startStreamMenu.isNull()) startStreamMenu->deleteLater(); @@ -3661,6 +3681,8 @@ void OBSBasic::StreamingStart() ui->streamButton->setText(QTStr("Basic.Main.StopStreaming")); ui->streamButton->setEnabled(true); ui->statusbar->StreamStarted(outputHandler->streamOutput); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); OnActivate(); @@ -3670,6 +3692,7 @@ void OBSBasic::StreamingStart() void OBSBasic::StreamStopping() { ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); + sysTrayStream->setText(ui->streamButton->text()); } void OBSBasic::StreamingStop(int code) @@ -3704,15 +3727,20 @@ void OBSBasic::StreamingStop(int code) ui->streamButton->setText(QTStr("Basic.Main.StartStreaming")); ui->streamButton->setEnabled(true); + sysTrayStream->setText(ui->streamButton->text()); + sysTrayStream->setEnabled(true); OnDeactivate(); blog(LOG_INFO, STREAMING_STOP); - if (code != OBS_OUTPUT_SUCCESS) + if (code != OBS_OUTPUT_SUCCESS && isVisible()) { QMessageBox::information(this, QTStr("Output.ConnectFail.Title"), QT_UTF8(errorMessage)); + } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { + SysTrayNotify(QT_UTF8(errorMessage), QSystemTrayIcon::Warning); + } if (!startStreamMenu.isNull()) { ui->streamButton->setMenu(nullptr); @@ -3733,6 +3761,7 @@ void OBSBasic::StartRecording() void OBSBasic::RecordStopping() { ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); + sysTrayRecord->setText(ui->recordButton->text()); } void OBSBasic::StopRecording() @@ -3749,6 +3778,7 @@ void OBSBasic::RecordingStart() { ui->statusbar->RecordingStarted(outputHandler->fileOutput); ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); + sysTrayRecord->setText(ui->recordButton->text()); OnActivate(); @@ -3759,22 +3789,35 @@ void OBSBasic::RecordingStop(int code) { ui->statusbar->RecordingStopped(); ui->recordButton->setText(QTStr("Basic.Main.StartRecording")); + sysTrayRecord->setText(ui->recordButton->text()); blog(LOG_INFO, RECORDING_STOP); - if (code == OBS_OUTPUT_UNSUPPORTED) { + if (code == OBS_OUTPUT_UNSUPPORTED && isVisible()) { QMessageBox::information(this, QTStr("Output.RecordFail.Title"), QTStr("Output.RecordFail.Unsupported")); - } else if (code == OBS_OUTPUT_NO_SPACE) { + } else if (code == OBS_OUTPUT_NO_SPACE && isVisible()) { QMessageBox::information(this, QTStr("Output.RecordNoSpace.Title"), QTStr("Output.RecordNoSpace.Msg")); - } else if (code != OBS_OUTPUT_SUCCESS) { + } else if (code != OBS_OUTPUT_SUCCESS && isVisible()) { QMessageBox::information(this, QTStr("Output.RecordError.Title"), QTStr("Output.RecordError.Msg")); + + } else if (code == OBS_OUTPUT_UNSUPPORTED && !isVisible()) { + SysTrayNotify(QTStr("Output.RecordFail.Unsupported"), + QSystemTrayIcon::Warning); + + } else if (code == OBS_OUTPUT_NO_SPACE && !isVisible()) { + SysTrayNotify(QTStr("Output.RecordNoSpace.Msg"), + QSystemTrayIcon::Warning); + + } else if (code != OBS_OUTPUT_SUCCESS && !isVisible()) { + SysTrayNotify(QTStr("Output.RecordError.Msg"), + QSystemTrayIcon::Warning); } OnDeactivate(); @@ -3786,7 +3829,7 @@ void OBSBasic::on_streamButton_clicked() bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStoppingStream"); - if (confirm) { + if (confirm && isVisible()) { QMessageBox::StandardButton button = QMessageBox::question(this, QTStr("ConfirmStop.Title"), @@ -3801,7 +3844,7 @@ void OBSBasic::on_streamButton_clicked() bool confirm = config_get_bool(GetGlobalConfig(), "BasicWindow", "WarnBeforeStartingStream"); - if (confirm) { + if (confirm && isVisible()) { QMessageBox::StandardButton button = QMessageBox::question(this, QTStr("ConfirmStart.Title"), @@ -3827,6 +3870,7 @@ void OBSBasic::on_settingsButton_clicked() { OBSBasicSettings settings(this); settings.exec(); + SystemTray(false); } void OBSBasic::on_actionWebsite_triggered() @@ -4389,3 +4433,115 @@ void OBSBasic::on_actionLockPreview_triggered() ui->preview->ToggleLocked(); ui->actionLockPreview->setChecked(ui->preview->Locked()); } + +void OBSBasic::SetShowing(bool showing) +{ + if (!showing && isVisible()) { + config_set_string(App()->GlobalConfig(), + "BasicWindow", "geometry", + saveGeometry().toBase64().constData()); + + showHide->setText(QTStr("Basic.SystemTray.Show")); + QTimer::singleShot(250, this, SLOT(hide())); + + if (previewEnabled) + EnablePreviewDisplay(false); + + setVisible(false); + + } else if (showing && !isVisible()) { + showHide->setText(QTStr("Basic.SystemTray.Hide")); + QTimer::singleShot(250, this, SLOT(show())); + + if (previewEnabled) + EnablePreviewDisplay(true); + + setVisible(true); + } +} + +void OBSBasic::SystemTrayInit() { + trayIcon = new QSystemTrayIcon(QIcon(":/res/images/obs.png"), + this); + trayIcon->setToolTip("OBS Studio"); + + showHide = new QAction(QTStr("Basic.SystemTray.Show"), + trayIcon); + sysTrayStream = new QAction(QTStr("Basic.Main.StartStreaming"), + trayIcon); + sysTrayRecord = new QAction(QTStr("Basic.Main.StartRecording"), + trayIcon); + exit = new QAction(QTStr("Exit"), + trayIcon); + + connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), + this, + SLOT(IconActivated(QSystemTrayIcon::ActivationReason))); + connect(showHide, SIGNAL(triggered()), + this, SLOT(ToggleShowHide())); + connect(sysTrayStream, SIGNAL(triggered()), + this, SLOT(on_streamButton_clicked())); + connect(sysTrayRecord, SIGNAL(triggered()), + this, SLOT(on_recordButton_clicked())); + connect(exit, SIGNAL(triggered()), + this, SLOT(close())); + + trayMenu = new QMenu; + trayMenu->addAction(showHide); + trayMenu->addAction(sysTrayStream); + trayMenu->addAction(sysTrayRecord); + trayMenu->addAction(exit); + trayIcon->setContextMenu(trayMenu); +} + +void OBSBasic::IconActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::Trigger) + ToggleShowHide(); +} + +void OBSBasic::SysTrayNotify(const QString &text, + QSystemTrayIcon::MessageIcon n) +{ + if (QSystemTrayIcon::supportsMessages()) { + QSystemTrayIcon::MessageIcon icon = + QSystemTrayIcon::MessageIcon(n); + trayIcon->showMessage("OBS Studio", text, icon, 10000); + } +} + +void OBSBasic::SystemTray(bool firstStarted) +{ + if (!QSystemTrayIcon::isSystemTrayAvailable()) + return; + + bool sysTrayWhenStarted = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayWhenStarted"); + bool sysTrayEnabled = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayEnabled"); + + if (firstStarted) + SystemTrayInit(); + + if (!sysTrayWhenStarted && !sysTrayEnabled) { + trayIcon->hide(); + } else if (sysTrayWhenStarted && sysTrayEnabled) { + trayIcon->show(); + if (firstStarted) { + QTimer::singleShot(50, this, SLOT(hide())); + EnablePreviewDisplay(false); + setVisible(false); + } + } else if (sysTrayEnabled) { + trayIcon->show(); + } else if (!sysTrayEnabled) { + trayIcon->hide(); + } else if (!sysTrayWhenStarted && sysTrayEnabled) { + trayIcon->hide(); + } + + if (isVisible()) + showHide->setText(QTStr("Basic.SystemTray.Hide")); + else + showHide->setText(QTStr("Basic.SystemTray.Show")); +} diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp index 4fedeee6c40e7a..c67a762cf64ebc 100644 --- a/obs/window-basic-main.hpp +++ b/obs/window-basic-main.hpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -135,6 +136,14 @@ class OBSBasic : public OBSMainWindow { QPointer startStreamMenu; + QSystemTrayIcon *trayIcon; + QMenu *trayMenu; + QAction *sysTrayStream; + QAction *sysTrayRecord; + QAction *showHide; + QAction *showPreview; + QAction *exit; + void DrawBackdrop(float cx, float cy); void SetupEncoders(); @@ -340,6 +349,14 @@ private slots: void SetScaleFilter(); + void IconActivated(QSystemTrayIcon::ActivationReason reason); + void SetShowing(bool showing); + + inline void ToggleShowHide() + { + SetShowing(!isVisible()); + } + private: /* OBS Callbacks */ static void SceneReordered(void *data, calldata_t *params); @@ -366,6 +383,8 @@ private slots: public: OBSScene GetCurrentScene(); + void SysTrayNotify(const QString &text, QSystemTrayIcon::MessageIcon n); + inline OBSSource GetCurrentSceneSource() { OBSScene curScene = GetCurrentScene(); @@ -414,6 +433,9 @@ private slots: void UpdateTitleBar(); void UpdateSceneSelection(OBSSource source); + void SystemTrayInit(); + void SystemTray(bool firstStarted); + protected: virtual void closeEvent(QCloseEvent *event) override; virtual void changeEvent(QEvent *event) override; diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index fef022bf749297..3bd064881cba49 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -274,6 +274,8 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->hideProjectorCursor, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->recordWhenStreaming, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->keepRecordStreamStops,CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->systemTrayEnabled, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->systemTrayWhenStarted,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->snappingEnabled, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->screenSnapping, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->centerSnapping, CHECK_CHANGED, GENERAL_CHANGED); @@ -845,6 +847,14 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "KeepRecordingWhenStreamStops"); ui->keepRecordStreamStops->setChecked(keepRecordStreamStops); + bool systemTrayEnabled = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayEnabled"); + ui->systemTrayEnabled->setChecked(systemTrayEnabled); + + bool systemTrayWhenStarted = config_get_bool(GetGlobalConfig(), + "BasicWindow", "SysTrayWhenStarted"); + ui->systemTrayWhenStarted->setChecked(systemTrayWhenStarted); + bool snappingEnabled = config_get_bool(GetGlobalConfig(), "BasicWindow", "SnappingEnabled"); ui->snappingEnabled->setChecked(snappingEnabled); @@ -2235,6 +2245,16 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_bool(GetGlobalConfig(), "BasicWindow", "KeepRecordingWhenStreamStops", ui->keepRecordStreamStops->isChecked()); + + if (WidgetChanged(ui->systemTrayEnabled)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "SysTrayEnabled", + ui->systemTrayEnabled->isChecked()); + + if (WidgetChanged(ui->systemTrayWhenStarted)) + config_set_bool(GetGlobalConfig(), "BasicWindow", + "SysTrayWhenStarted", + ui->systemTrayWhenStarted->isChecked()); } void OBSBasicSettings::SaveStream1Settings() diff --git a/obs/window-basic-status-bar.cpp b/obs/window-basic-status-bar.cpp index 64015efca2f2e7..4c37b0e3f0cd70 100644 --- a/obs/window-basic-status-bar.cpp +++ b/obs/window-basic-status-bar.cpp @@ -72,6 +72,7 @@ void OBSBasicStatusBar::Deactivate() delaySecStopping = 0; reconnectTimeout = 0; active = false; + overloadedNotify = true; } } @@ -165,9 +166,10 @@ void OBSBasicStatusBar::UpdateSessionTime() sessionTime->setMinimumWidth(sessionTime->width()); if (reconnectTimeout > 0) { - QString msg = QTStr("Basic.StatusBar.Reconnecting"); - showMessage(msg.arg(QString::number(retries), - QString::number(reconnectTimeout))); + QString msg = QTStr("Basic.StatusBar.Reconnecting") + .arg(QString::number(retries), + QString::number(reconnectTimeout)); + showMessage(msg); reconnectTimeout--; } else if (retries > 0) { @@ -224,12 +226,20 @@ void OBSBasicStatusBar::OBSOutputReconnectSuccess(void *data, calldata_t *params void OBSBasicStatusBar::Reconnect(int seconds) { - retries++; + OBSBasic *main = qobject_cast(parent()); + + if (!retries) + main->SysTrayNotify( + QTStr("Basic.SystemTray.Message.Reconnecting"), + QSystemTrayIcon::Warning); + reconnectTimeout = seconds; if (streamOutput) { delaySecTotal = obs_output_get_active_delay(streamOutput); UpdateDelayMsg(); + + retries++; } } @@ -246,7 +256,11 @@ void OBSBasicStatusBar::ReconnectClear() void OBSBasicStatusBar::ReconnectSuccess() { - showMessage(QTStr("Basic.StatusBar.ReconnectSuccessful"), 4000); + OBSBasic *main = qobject_cast(parent()); + + QString msg = QTStr("Basic.StatusBar.ReconnectSuccessful"); + showMessage(msg, 4000); + main->SysTrayNotify(msg, QSystemTrayIcon::Information); ReconnectClear(); if (streamOutput) { @@ -257,6 +271,8 @@ void OBSBasicStatusBar::ReconnectSuccess() void OBSBasicStatusBar::UpdateStatusBar() { + OBSBasic *main = qobject_cast(parent()); + UpdateBandwidth(); UpdateSessionTime(); UpdateDroppedFrames(); @@ -270,8 +286,14 @@ void OBSBasicStatusBar::UpdateStatusBar() int diff = skipped - lastSkippedFrameCount; double percentage = double(skipped) / double(total) * 100.0; - if (diff > 10 && percentage >= 0.1f) + if (diff > 10 && percentage >= 0.1f) { showMessage(QTStr("HighResourceUsage"), 4000); + if (!main->isVisible() && overloadedNotify) { + main->SysTrayNotify(QTStr("HighResourceUsage"), + QSystemTrayIcon::Warning); + overloadedNotify = false; + } + } lastSkippedFrameCount = skipped; } diff --git a/obs/window-basic-status-bar.hpp b/obs/window-basic-status-bar.hpp index 3f9bc8fa0f9c85..e0c7a0d0aff705 100644 --- a/obs/window-basic-status-bar.hpp +++ b/obs/window-basic-status-bar.hpp @@ -21,6 +21,7 @@ class OBSBasicStatusBar : public QStatusBar { obs_output_t *streamOutput = nullptr; obs_output_t *recordOutput = nullptr; bool active = false; + bool overloadedNotify = true; int retries = 0; int totalSeconds = 0; From 8e149a5a1ce356d7cd4737cce56ff27cd021f0ba Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 21 Aug 2016 11:26:15 -0700 Subject: [PATCH 017/107] UI: Remove trailing whitespace --- obs/obs-app.cpp | 2 +- obs/window-basic-main.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index 0584e18eb92593..ca0deda29c3132 100644 --- a/obs/obs-app.cpp +++ b/obs/obs-app.cpp @@ -1223,7 +1223,7 @@ static auto SnapshotRelease = [](profiler_snapshot_t *snap) profile_snapshot_free(snap); }; -using ProfilerSnapshot = +using ProfilerSnapshot = std::unique_ptr; ProfilerSnapshot GetSnapshot() diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index d2aecbac3d3ef0..7fdb81817944de 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -184,7 +184,7 @@ OBSBasic::OBSBasic(QWidget *parent) installEventFilter(CreateShortcutFilter()); stringstream name; - name << "OBS " << App()->GetVersionString(); + name << "OBS " << App()->GetVersionString(); blog(LOG_INFO, "%s", name.str().c_str()); blog(LOG_INFO, "---------------------------------"); From 18276c0c843810472758196ec34433b445bd9d9a Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 21 Aug 2016 11:29:56 -0700 Subject: [PATCH 018/107] UI: Only use shutdown separator upon successful close Fixes an issue where the shutdown separator in the log file would always appear even when the user decides not to shut down the program after clicking close when still streaming/recording. --- obs/window-basic-main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index 7fdb81817944de..11a801478b7e61 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -2612,8 +2612,6 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { - blog(LOG_INFO, SHUTDOWN_SEPARATOR); - if (outputHandler && outputHandler->Active()) { SetShowing(true); @@ -2631,6 +2629,8 @@ void OBSBasic::closeEvent(QCloseEvent *event) if (!event->isAccepted()) return; + blog(LOG_INFO, SHUTDOWN_SEPARATOR); + if (updateCheckThread) updateCheckThread->wait(); if (logUploadThread) From 89d6d5ffce0f74ff66e94cda81589f9e83f7fec5 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sun, 21 Aug 2016 16:11:32 -0700 Subject: [PATCH 019/107] UI: Add option to make projectors always on top Closes jp9000/obs-studio#563 --- obs/data/locale/en-US.ini | 1 + obs/forms/OBSBasicSettings.ui | 29 ++++++++++++++++++----------- obs/platform-osx.mm | 4 ++-- obs/platform-windows.cpp | 4 ++-- obs/platform-x11.cpp | 4 ++-- obs/platform.hpp | 6 +++--- obs/window-basic-settings.cpp | 8 ++++++++ obs/window-projector.cpp | 5 +++++ 8 files changed, 41 insertions(+), 20 deletions(-) diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini index f3467f4d2b1f73..0c47e9deb0cc5a 100644 --- a/obs/data/locale/en-US.ini +++ b/obs/data/locale/en-US.ini @@ -376,6 +376,7 @@ Basic.Settings.General.Language="Language" Basic.Settings.General.WarnBeforeStartingStream="Show confirmation dialog when starting streams" Basic.Settings.General.WarnBeforeStoppingStream="Show confirmation dialog when stopping streams" Basic.Settings.General.HideProjectorCursor="Hide cursor over projectors" +Basic.Settings.General.ProjectorAlwaysOnTop="Make projectors always on top" Basic.Settings.General.Snapping="Source Alignment Snapping" Basic.Settings.General.ScreenSnapping="Snap Sources to edge of screen" Basic.Settings.General.CenterSnapping="Snap Sources to horizontal and vertical center" diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui index 64ce0fd1c5242a..ff50261bfe080c 100644 --- a/obs/forms/OBSBasicSettings.ui +++ b/obs/forms/OBSBasicSettings.ui @@ -185,21 +185,21 @@ - + Basic.Settings.General.RecordWhenStreaming - + Basic.Settings.General.SysTrayEnabled - + false @@ -209,14 +209,14 @@ - + Qt::Horizontal - + true @@ -316,7 +316,7 @@ - + false @@ -326,6 +326,13 @@ + + + + Basic.Settings.General.ProjectorAlwaysOnTop + + + @@ -2538,8 +2545,8 @@ 0 0 - 98 - 28 + 80 + 16 @@ -2903,9 +2910,9 @@ 0 - -206 - 803 - 820 + 0 + 559 + 681 diff --git a/obs/platform-osx.mm b/obs/platform-osx.mm index ec02bbec4f07e2..d60f8ab921a8e3 100644 --- a/obs/platform-osx.mm +++ b/obs/platform-osx.mm @@ -132,12 +132,12 @@ string GetDefaultVideoSavePath() return result; } -bool IsAlwaysOnTop(QMainWindow *window) +bool IsAlwaysOnTop(QWidget *window) { return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0; } -void SetAlwaysOnTop(QMainWindow *window, bool enable) +void SetAlwaysOnTop(QWidget *window, bool enable) { Qt::WindowFlags flags = window->windowFlags(); diff --git a/obs/platform-windows.cpp b/obs/platform-windows.cpp index fb4c9975972499..4afde448f5797f 100644 --- a/obs/platform-windows.cpp +++ b/obs/platform-windows.cpp @@ -203,13 +203,13 @@ void SetAeroEnabled(bool enable) func(enable ? DWM_EC_ENABLECOMPOSITION : DWM_EC_DISABLECOMPOSITION); } -bool IsAlwaysOnTop(QMainWindow *window) +bool IsAlwaysOnTop(QWidget *window) { DWORD exStyle = GetWindowLong((HWND)window->winId(), GWL_EXSTYLE); return (exStyle & WS_EX_TOPMOST) != 0; } -void SetAlwaysOnTop(QMainWindow *window, bool enable) +void SetAlwaysOnTop(QWidget *window, bool enable) { HWND hwnd = (HWND)window->winId(); SetWindowPos(hwnd, enable ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, diff --git a/obs/platform-x11.cpp b/obs/platform-x11.cpp index 582d626d3fe51a..1cca5da517d92b 100644 --- a/obs/platform-x11.cpp +++ b/obs/platform-x11.cpp @@ -148,12 +148,12 @@ vector GetPreferredLocales() return {}; } -bool IsAlwaysOnTop(QMainWindow *window) +bool IsAlwaysOnTop(QWidget *window) { return (window->windowFlags() & Qt::WindowStaysOnTopHint) != 0; } -void SetAlwaysOnTop(QMainWindow *window, bool enable) +void SetAlwaysOnTop(QWidget *window, bool enable) { Qt::WindowFlags flags = window->windowFlags(); diff --git a/obs/platform.hpp b/obs/platform.hpp index a25e4a8d8b097a..d5e02d161b851d 100644 --- a/obs/platform.hpp +++ b/obs/platform.hpp @@ -22,7 +22,7 @@ #include #include -class QMainWindow; +class QWidget; struct MonitorInfo { int32_t x, y; @@ -45,8 +45,8 @@ std::string GetDefaultVideoSavePath(); std::vector GetPreferredLocales(); -bool IsAlwaysOnTop(QMainWindow *window); -void SetAlwaysOnTop(QMainWindow *window, bool enable); +bool IsAlwaysOnTop(QWidget *window); +void SetAlwaysOnTop(QWidget *window, bool enable); #ifdef _WIN32 uint32_t GetWindowsVersion(); diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index 3bd064881cba49..73d3ab58fb60d6 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -272,6 +272,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent) HookWidget(ui->warnBeforeStreamStart,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->warnBeforeStreamStop, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->hideProjectorCursor, CHECK_CHANGED, GENERAL_CHANGED); + HookWidget(ui->projectorAlwaysOnTop, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->recordWhenStreaming, CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->keepRecordStreamStops,CHECK_CHANGED, GENERAL_CHANGED); HookWidget(ui->systemTrayEnabled, CHECK_CHANGED, GENERAL_CHANGED); @@ -887,6 +888,10 @@ void OBSBasicSettings::LoadGeneralSettings() "BasicWindow", "HideProjectorCursor"); ui->hideProjectorCursor->setChecked(hideProjectorCursor); + bool projectorAlwaysOnTop = config_get_bool(GetGlobalConfig(), + "BasicWindow", "ProjectorAlwaysOnTop"); + ui->projectorAlwaysOnTop->setChecked(projectorAlwaysOnTop); + loading = false; } @@ -2236,6 +2241,9 @@ void OBSBasicSettings::SaveGeneralSettings() config_set_bool(GetGlobalConfig(), "BasicWindow", "HideProjectorCursor", ui->hideProjectorCursor->isChecked()); + config_set_bool(GetGlobalConfig(), "BasicWindow", + "ProjectorAlwaysOnTop", + ui->projectorAlwaysOnTop->isChecked()); if (WidgetChanged(ui->recordWhenStreaming)) config_set_bool(GetGlobalConfig(), "BasicWindow", diff --git a/obs/window-projector.cpp b/obs/window-projector.cpp index 172e7be0fd9629..5e3ecf972c4682 100644 --- a/obs/window-projector.cpp +++ b/obs/window-projector.cpp @@ -53,6 +53,11 @@ void OBSProjector::Init(int monitor) setGeometry(mi.x, mi.y, mi.cx, mi.cy); + bool alwaysOnTop = config_get_bool(GetGlobalConfig(), + "BasicWindow", "ProjectorAlwaysOnTop"); + if (alwaysOnTop) + SetAlwaysOnTop(this, true); + show(); if (source) From 65e738e25361695ca9e220ea4d408f3d35f0d10f Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Sun, 21 Aug 2016 21:00:07 -0400 Subject: [PATCH 020/107] libobs: Set core signal handlers to NULL after destroying Fixes https://obsproject.com/mantis/view.php?id=595 --- libobs/obs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libobs/obs.c b/libobs/obs.c index eabbdb4147874e..706a8e9a242f20 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -820,6 +820,8 @@ void obs_shutdown(void) obs_free_graphics(); proc_handler_destroy(obs->procs); signal_handler_destroy(obs->signals); + obs->procs = NULL; + obs->signals = NULL; module = obs->first_module; while (module) { From 7b2fb21dedbd6ccd3b835cb6647147e3984f586b Mon Sep 17 00:00:00 2001 From: Radzaquiel Date: Mon, 22 Aug 2016 14:27:46 +0200 Subject: [PATCH 021/107] rtmp-services: update Hitbox.tv ingest servers Added: "US-East: New York - 2" Modified: "US East: New York" to "US-East: New York - 1" Closes jp9000/obs-studio#601 --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 47dafbce71412c..8136a0e38d7e09 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 30, + "version": 31, "files": [ { "name": "services.json", - "version": 30 + "version": 31 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index f901fbde08bee7..613e5cc59f38fc 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -183,9 +183,13 @@ "url": "rtmp://live.vgn.hitbox.tv/push" }, { - "name": "US-East: New York", + "name": "US-East: New York - 1", "url": "rtmp://live.jfk.hitbox.tv/push" }, + { + "name": "US-East: New York - 2", + "url": "rtmp://live.nyc.hitbox.tv/push" + }, { "name": "US-Central: Denver", "url": "rtmp://live.den.hitbox.tv/push" From f8fd87bbf613cec2eea8863819e744ecbb429490 Mon Sep 17 00:00:00 2001 From: Gol-D-Ace Date: Mon, 22 Aug 2016 16:25:40 +0200 Subject: [PATCH 022/107] rtmp-services: Update Restream.io ingest servers --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 8136a0e38d7e09..4301f4c794bb3f 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 31, + "version": 32, "files": [ { "name": "services.json", - "version": 31 + "version": 32 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 613e5cc59f38fc..a4481f065531b4 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -579,6 +579,10 @@ }, { "name": "EU-Central (Frankfurt, Germany)", + "url": "rtmp://eu-central.restream.io/live" + }, + { + "name": "EU-Central 2 (Frankfurt, Germany)", "url": "rtmp://eu.restream.io/live" }, { @@ -593,9 +597,17 @@ "name": "Australia (Sydney)", "url": "rtmp://au.restream.io/live" }, + { + "name": "Australia Secondary (Sydney)", + "url": "rtmp://au-secondary.restream.io/live" + }, { "name": "South America (Sao Paulo, Brazil)", "url": "rtmp://sa.restream.io/live" + }, + { + "name": "Asia (Singapore)", + "url": "rtmp://singapore.restream.io/live" } ], "recommended": { From 95ce556051be22675d9d441943d0149ea3925f54 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 22 Aug 2016 12:04:23 -0700 Subject: [PATCH 023/107] libobs: Add obs_get_active_fps function Allows getting the current active framerate that the core is rendering with. This takes in to account any rendering lag or stalls that may be occurring. --- libobs/obs-internal.h | 1 + libobs/obs-video.c | 12 ++++++++++++ libobs/obs.c | 5 +++++ libobs/obs.h | 2 ++ 4 files changed, 20 insertions(+) diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index 429aa7b67d473f..07d720b57f1462 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -243,6 +243,7 @@ struct obs_core_video { int cur_texture; uint64_t video_time; + double video_fps; video_t *video; pthread_t video_thread; uint32_t total_frames; diff --git a/libobs/obs-video.c b/libobs/obs-video.c index 6b046a9fcccbab..cdf83f250b51c8 100644 --- a/libobs/obs-video.c +++ b/libobs/obs-video.c @@ -573,6 +573,8 @@ void *obs_video_thread(void *param) { uint64_t last_time = 0; uint64_t interval = video_output_get_frame_time(obs->video.video); + uint64_t fps_total_ns = 0; + uint32_t fps_total_frames = 0; obs->video.video_time = os_gettime_ns(); @@ -603,6 +605,16 @@ void *obs_video_thread(void *param) profile_reenable_thread(); video_sleep(&obs->video, &obs->video.video_time, interval); + + fps_total_ns += (obs->video.video_time - last_time); + fps_total_frames++; + + if (fps_total_ns >= 1000000000ULL) { + obs->video.video_fps = (double)fps_total_frames / + ((double)fps_total_ns / 1000000000.0); + fps_total_ns = 0; + fps_total_frames = 0; + } } UNUSED_PARAMETER(param); diff --git a/libobs/obs.c b/libobs/obs.c index 706a8e9a242f20..dbda425f094103 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -1819,6 +1819,11 @@ uint64_t obs_get_video_frame_time(void) return obs ? obs->video.video_time : 0; } +double obs_get_active_fps(void) +{ + return obs ? obs->video.video_fps : 0.0; +} + enum obs_obj_type obs_obj_get_type(void *obj) { struct obs_context_data *context = obj; diff --git a/libobs/obs.h b/libobs/obs.h index d9ba78b0d796f5..306468006f8885 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -610,6 +610,8 @@ EXPORT void obs_view_render(obs_view_t *view); EXPORT uint64_t obs_get_video_frame_time(void); +EXPORT double obs_get_active_fps(void); + /* ------------------------------------------------------------------------- */ /* Display context */ From 88d7f8129bd3ead0a6b732106119b0bf8f9a8e50 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Sat, 6 Aug 2016 11:38:42 -0500 Subject: [PATCH 024/107] UI: Add fps indicator This adds a fps indicator to the status bar, to the right of CPU usage. Closes jp9000/obs-studio#585 --- obs/window-basic-status-bar.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/obs/window-basic-status-bar.cpp b/obs/window-basic-status-bar.cpp index 4c37b0e3f0cd70..1dd42b9996fbe8 100644 --- a/obs/window-basic-status-bar.cpp +++ b/obs/window-basic-status-bar.cpp @@ -146,7 +146,9 @@ void OBSBasicStatusBar::UpdateCPUUsage() QString text; text += QString("CPU: ") + - QString::number(main->GetCPUUsage(), 'f', 1) + QString("%"); + QString::number(main->GetCPUUsage(), 'f', 1) + QString("%, ") + + QString::number(obs_get_active_fps(), 'f', 2) + QString(" fps"); + cpuUsage->setText(text); cpuUsage->setMinimumWidth(cpuUsage->width()); } From aa899c22788b51054ca61b637085069be7fa1277 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Sat, 27 Aug 2016 05:04:21 -0400 Subject: [PATCH 025/107] libobs: Log Windows bitness (32/64-bit) --- libobs/obs-windows.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/libobs/obs-windows.c b/libobs/obs-windows.c index 8bc4683ad8e474..a56a7bd5a97d77 100644 --- a/libobs/obs-windows.c +++ b/libobs/obs-windows.c @@ -172,13 +172,27 @@ static void log_available_memory(void) note); } +static bool is_64_bit_windows(void) +{ +#if defined(_WIN64) + return true; +#elif defined(_WIN32) + BOOL b64 = false; + return IsWow64Process(GetCurrentProcess(), &b64) && b64; +#endif +} + static void log_windows_version(void) { struct win_version_info ver; get_win_ver(&ver); - blog(LOG_INFO, "Windows Version: %d.%d Build %d (revision: %d)", - ver.major, ver.minor, ver.build, ver.revis); + bool b64 = is_64_bit_windows(); + const char *windows_bitness = b64 ? "64" : "32"; + + blog(LOG_INFO, "Windows Version: %d.%d Build %d (revision: %d; %s-bit)", + ver.major, ver.minor, ver.build, ver.revis, + windows_bitness); } static void log_admin_status(void) From 01b274f1daab88e7ce1ed201c0d758a172eb9e0b Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 26 Aug 2016 13:50:55 -0700 Subject: [PATCH 026/107] UI: Rename 'obs' dir to 'UI' This is to prevent confusion both when prefixing commits and when reading the directory structure for the first time. --- CMakeLists.txt | 2 +- {obs => UI}/CMakeLists.txt | 0 {obs => UI}/adv-audio-control.cpp | 0 {obs => UI}/adv-audio-control.hpp | 0 {obs => UI}/audio-encoders.cpp | 0 {obs => UI}/audio-encoders.hpp | 0 {obs => UI}/crash-report.cpp | 0 {obs => UI}/crash-report.hpp | 0 {obs => UI}/data/license/gplv2.txt | 0 {obs => UI}/data/locale.ini | 0 {obs => UI}/data/locale/ar-SA.ini | 0 {obs => UI}/data/locale/bg-BG.ini | 0 {obs => UI}/data/locale/ca-ES.ini | 0 {obs => UI}/data/locale/cs-CZ.ini | 0 {obs => UI}/data/locale/da-DK.ini | 0 {obs => UI}/data/locale/de-DE.ini | 0 {obs => UI}/data/locale/el-GR.ini | 0 {obs => UI}/data/locale/en-US.ini | 0 {obs => UI}/data/locale/es-ES.ini | 0 {obs => UI}/data/locale/et-EE.ini | 0 {obs => UI}/data/locale/eu-ES.ini | 0 {obs => UI}/data/locale/fi-FI.ini | 0 {obs => UI}/data/locale/fr-FR.ini | 0 {obs => UI}/data/locale/gl-ES.ini | 0 {obs => UI}/data/locale/he-IL.ini | 0 {obs => UI}/data/locale/hr-HR.ini | 0 {obs => UI}/data/locale/hu-HU.ini | 0 {obs => UI}/data/locale/it-IT.ini | 0 {obs => UI}/data/locale/ja-JP.ini | 0 {obs => UI}/data/locale/ko-KR.ini | 0 {obs => UI}/data/locale/lt-LT.ini | 0 {obs => UI}/data/locale/ms-MY.ini | 0 {obs => UI}/data/locale/nb-NO.ini | 0 {obs => UI}/data/locale/nl-NL.ini | 0 {obs => UI}/data/locale/pl-PL.ini | 0 {obs => UI}/data/locale/pt-BR.ini | 0 {obs => UI}/data/locale/pt-PT.ini | 0 {obs => UI}/data/locale/ro-RO.ini | 0 {obs => UI}/data/locale/ru-RU.ini | 0 {obs => UI}/data/locale/sk-SK.ini | 0 {obs => UI}/data/locale/sl-SI.ini | 0 {obs => UI}/data/locale/sr-CS.ini | 0 {obs => UI}/data/locale/sr-SP.ini | 0 {obs => UI}/data/locale/sv-SE.ini | 0 {obs => UI}/data/locale/ta-IN.ini | 0 {obs => UI}/data/locale/th-TH.ini | 0 {obs => UI}/data/locale/tr-TR.ini | 0 {obs => UI}/data/locale/uk-UA.ini | 0 {obs => UI}/data/locale/vi-VN.ini | 0 {obs => UI}/data/locale/zh-CN.ini | 0 {obs => UI}/data/locale/zh-TW.ini | 0 {obs => UI}/data/themes/Dark.qss | 0 {obs => UI}/data/themes/Dark/cogwheel.png | Bin {obs => UI}/data/themes/Dark/down_arrow.png | Bin {obs => UI}/data/themes/Dark/minus.png | Bin {obs => UI}/data/themes/Dark/mute.png | Bin {obs => UI}/data/themes/Dark/plus.png | Bin {obs => UI}/data/themes/Dark/unmute.png | Bin {obs => UI}/data/themes/Dark/up_arrow.png | Bin {obs => UI}/data/themes/Dark/updown.png | Bin {obs => UI}/data/themes/Default.qss | 0 {obs => UI}/display-helpers.hpp | 0 {obs => UI}/dist/obs.desktop | 0 {obs => UI}/double-slider.cpp | 0 {obs => UI}/double-slider.hpp | 0 {obs => UI}/focus-list.cpp | 0 {obs => UI}/focus-list.hpp | 0 {obs => UI}/forms/NameDialog.ui | 0 {obs => UI}/forms/OBSBasic.ui | 0 {obs => UI}/forms/OBSBasicFilters.ui | 0 {obs => UI}/forms/OBSBasicInteraction.ui | 0 {obs => UI}/forms/OBSBasicSettings.ui | 0 {obs => UI}/forms/OBSBasicSourceSelect.ui | 0 {obs => UI}/forms/OBSBasicTransform.ui | 0 {obs => UI}/forms/OBSLicenseAgreement.ui | 0 {obs => UI}/forms/OBSLogReply.ui | 0 {obs => UI}/forms/OBSRemux.ui | 0 {obs => UI}/forms/images/add.png | Bin {obs => UI}/forms/images/configuration21_16.png | Bin {obs => UI}/forms/images/down.png | Bin {obs => UI}/forms/images/editscene.png | Bin {obs => UI}/forms/images/invisible_mask.png | Bin {obs => UI}/forms/images/list_remove.png | Bin {obs => UI}/forms/images/live.png | Bin {obs => UI}/forms/images/mute.png | Bin {obs => UI}/forms/images/obs.png | Bin {obs => UI}/forms/images/properties.png | Bin {obs => UI}/forms/images/settings/advanced.png | Bin .../forms/images/settings/applications-system-2.png | Bin .../forms/images/settings/decibel_audio_player.png | Bin .../forms/images/settings/network-bluetooth.png | Bin {obs => UI}/forms/images/settings/network.png | Bin .../preferences-desktop-keyboard-shortcuts.png | Bin .../settings/preferences-system-network-3.png | Bin .../forms/images/settings/system-settings-3.png | Bin .../forms/images/settings/video-display-3.png | Bin {obs => UI}/forms/images/sound.ico | Bin {obs => UI}/forms/images/sound_muted.ico | Bin {obs => UI}/forms/images/tray_active.png | Bin {obs => UI}/forms/images/unmute.png | Bin {obs => UI}/forms/images/up.png | Bin {obs => UI}/forms/images/visible_mask.png | Bin {obs => UI}/forms/obs.qrc | 0 {obs => UI}/hotkey-edit.cpp | 0 {obs => UI}/hotkey-edit.hpp | 0 {obs => UI}/installer/mp-installer.nsi | 0 {obs => UI}/item-widget-helpers.cpp | 0 {obs => UI}/item-widget-helpers.hpp | 0 {obs => UI}/menu-button.cpp | 0 {obs => UI}/menu-button.hpp | 0 {obs => UI}/mute-checkbox.hpp | 0 {obs => UI}/obs-app.cpp | 0 {obs => UI}/obs-app.hpp | 0 {obs => UI}/obs.rc | 0 {obs => UI}/platform-osx.mm | 0 {obs => UI}/platform-windows.cpp | 0 {obs => UI}/platform-x11.cpp | 0 {obs => UI}/platform.hpp | 0 {obs => UI}/properties-view.cpp | 0 {obs => UI}/properties-view.hpp | 0 {obs => UI}/properties-view.moc.hpp | 0 {obs => UI}/qt-display.cpp | 0 {obs => UI}/qt-display.hpp | 0 {obs => UI}/qt-wrappers.cpp | 0 {obs => UI}/qt-wrappers.hpp | 0 {obs => UI}/remote-text.cpp | 0 {obs => UI}/remote-text.hpp | 0 {obs => UI}/slider-absoluteset-style.cpp | 0 {obs => UI}/slider-absoluteset-style.hpp | 0 {obs => UI}/source-label.cpp | 0 {obs => UI}/source-label.hpp | 0 {obs => UI}/source-list-widget.cpp | 0 {obs => UI}/source-list-widget.hpp | 0 {obs => UI}/sparkle-updater.mm | 0 {obs => UI}/vertical-scroll-area.cpp | 0 {obs => UI}/vertical-scroll-area.hpp | 0 {obs => UI}/visibility-checkbox.cpp | 0 {obs => UI}/visibility-checkbox.hpp | 0 {obs => UI}/visibility-item-widget.cpp | 0 {obs => UI}/visibility-item-widget.hpp | 0 {obs => UI}/volume-control.cpp | 0 {obs => UI}/volume-control.hpp | 0 {obs => UI}/window-basic-adv-audio.cpp | 0 {obs => UI}/window-basic-adv-audio.hpp | 0 {obs => UI}/window-basic-filters.cpp | 0 {obs => UI}/window-basic-filters.hpp | 0 {obs => UI}/window-basic-interaction.cpp | 0 {obs => UI}/window-basic-interaction.hpp | 0 {obs => UI}/window-basic-main-outputs.cpp | 0 {obs => UI}/window-basic-main-outputs.hpp | 0 {obs => UI}/window-basic-main-profiles.cpp | 0 {obs => UI}/window-basic-main-scene-collections.cpp | 0 {obs => UI}/window-basic-main-transitions.cpp | 0 {obs => UI}/window-basic-main.cpp | 0 {obs => UI}/window-basic-main.hpp | 0 {obs => UI}/window-basic-preview.cpp | 0 {obs => UI}/window-basic-preview.hpp | 0 {obs => UI}/window-basic-properties.cpp | 0 {obs => UI}/window-basic-properties.hpp | 0 {obs => UI}/window-basic-settings.cpp | 0 {obs => UI}/window-basic-settings.hpp | 0 {obs => UI}/window-basic-source-select.cpp | 0 {obs => UI}/window-basic-source-select.hpp | 0 {obs => UI}/window-basic-status-bar.cpp | 0 {obs => UI}/window-basic-status-bar.hpp | 0 {obs => UI}/window-basic-transform.cpp | 0 {obs => UI}/window-basic-transform.hpp | 0 {obs => UI}/window-license-agreement.cpp | 0 {obs => UI}/window-license-agreement.hpp | 0 {obs => UI}/window-log-reply.cpp | 0 {obs => UI}/window-log-reply.hpp | 0 {obs => UI}/window-main.hpp | 0 {obs => UI}/window-namedialog.cpp | 0 {obs => UI}/window-namedialog.hpp | 0 {obs => UI}/window-projector.cpp | 0 {obs => UI}/window-projector.hpp | 0 {obs => UI}/window-remux.cpp | 0 {obs => UI}/window-remux.hpp | 0 178 files changed, 1 insertion(+), 1 deletion(-) rename {obs => UI}/CMakeLists.txt (100%) rename {obs => UI}/adv-audio-control.cpp (100%) rename {obs => UI}/adv-audio-control.hpp (100%) rename {obs => UI}/audio-encoders.cpp (100%) rename {obs => UI}/audio-encoders.hpp (100%) rename {obs => UI}/crash-report.cpp (100%) rename {obs => UI}/crash-report.hpp (100%) rename {obs => UI}/data/license/gplv2.txt (100%) rename {obs => UI}/data/locale.ini (100%) rename {obs => UI}/data/locale/ar-SA.ini (100%) rename {obs => UI}/data/locale/bg-BG.ini (100%) rename {obs => UI}/data/locale/ca-ES.ini (100%) rename {obs => UI}/data/locale/cs-CZ.ini (100%) rename {obs => UI}/data/locale/da-DK.ini (100%) rename {obs => UI}/data/locale/de-DE.ini (100%) rename {obs => UI}/data/locale/el-GR.ini (100%) rename {obs => UI}/data/locale/en-US.ini (100%) rename {obs => UI}/data/locale/es-ES.ini (100%) rename {obs => UI}/data/locale/et-EE.ini (100%) rename {obs => UI}/data/locale/eu-ES.ini (100%) rename {obs => UI}/data/locale/fi-FI.ini (100%) rename {obs => UI}/data/locale/fr-FR.ini (100%) rename {obs => UI}/data/locale/gl-ES.ini (100%) rename {obs => UI}/data/locale/he-IL.ini (100%) rename {obs => UI}/data/locale/hr-HR.ini (100%) rename {obs => UI}/data/locale/hu-HU.ini (100%) rename {obs => UI}/data/locale/it-IT.ini (100%) rename {obs => UI}/data/locale/ja-JP.ini (100%) rename {obs => UI}/data/locale/ko-KR.ini (100%) rename {obs => UI}/data/locale/lt-LT.ini (100%) rename {obs => UI}/data/locale/ms-MY.ini (100%) rename {obs => UI}/data/locale/nb-NO.ini (100%) rename {obs => UI}/data/locale/nl-NL.ini (100%) rename {obs => UI}/data/locale/pl-PL.ini (100%) rename {obs => UI}/data/locale/pt-BR.ini (100%) rename {obs => UI}/data/locale/pt-PT.ini (100%) rename {obs => UI}/data/locale/ro-RO.ini (100%) rename {obs => UI}/data/locale/ru-RU.ini (100%) rename {obs => UI}/data/locale/sk-SK.ini (100%) rename {obs => UI}/data/locale/sl-SI.ini (100%) rename {obs => UI}/data/locale/sr-CS.ini (100%) rename {obs => UI}/data/locale/sr-SP.ini (100%) rename {obs => UI}/data/locale/sv-SE.ini (100%) rename {obs => UI}/data/locale/ta-IN.ini (100%) rename {obs => UI}/data/locale/th-TH.ini (100%) rename {obs => UI}/data/locale/tr-TR.ini (100%) rename {obs => UI}/data/locale/uk-UA.ini (100%) rename {obs => UI}/data/locale/vi-VN.ini (100%) rename {obs => UI}/data/locale/zh-CN.ini (100%) rename {obs => UI}/data/locale/zh-TW.ini (100%) rename {obs => UI}/data/themes/Dark.qss (100%) rename {obs => UI}/data/themes/Dark/cogwheel.png (100%) rename {obs => UI}/data/themes/Dark/down_arrow.png (100%) rename {obs => UI}/data/themes/Dark/minus.png (100%) rename {obs => UI}/data/themes/Dark/mute.png (100%) rename {obs => UI}/data/themes/Dark/plus.png (100%) rename {obs => UI}/data/themes/Dark/unmute.png (100%) rename {obs => UI}/data/themes/Dark/up_arrow.png (100%) rename {obs => UI}/data/themes/Dark/updown.png (100%) rename {obs => UI}/data/themes/Default.qss (100%) rename {obs => UI}/display-helpers.hpp (100%) rename {obs => UI}/dist/obs.desktop (100%) rename {obs => UI}/double-slider.cpp (100%) rename {obs => UI}/double-slider.hpp (100%) rename {obs => UI}/focus-list.cpp (100%) rename {obs => UI}/focus-list.hpp (100%) rename {obs => UI}/forms/NameDialog.ui (100%) rename {obs => UI}/forms/OBSBasic.ui (100%) rename {obs => UI}/forms/OBSBasicFilters.ui (100%) rename {obs => UI}/forms/OBSBasicInteraction.ui (100%) rename {obs => UI}/forms/OBSBasicSettings.ui (100%) rename {obs => UI}/forms/OBSBasicSourceSelect.ui (100%) rename {obs => UI}/forms/OBSBasicTransform.ui (100%) rename {obs => UI}/forms/OBSLicenseAgreement.ui (100%) rename {obs => UI}/forms/OBSLogReply.ui (100%) rename {obs => UI}/forms/OBSRemux.ui (100%) rename {obs => UI}/forms/images/add.png (100%) rename {obs => UI}/forms/images/configuration21_16.png (100%) rename {obs => UI}/forms/images/down.png (100%) rename {obs => UI}/forms/images/editscene.png (100%) rename {obs => UI}/forms/images/invisible_mask.png (100%) rename {obs => UI}/forms/images/list_remove.png (100%) rename {obs => UI}/forms/images/live.png (100%) rename {obs => UI}/forms/images/mute.png (100%) rename {obs => UI}/forms/images/obs.png (100%) rename {obs => UI}/forms/images/properties.png (100%) rename {obs => UI}/forms/images/settings/advanced.png (100%) rename {obs => UI}/forms/images/settings/applications-system-2.png (100%) rename {obs => UI}/forms/images/settings/decibel_audio_player.png (100%) rename {obs => UI}/forms/images/settings/network-bluetooth.png (100%) rename {obs => UI}/forms/images/settings/network.png (100%) rename {obs => UI}/forms/images/settings/preferences-desktop-keyboard-shortcuts.png (100%) rename {obs => UI}/forms/images/settings/preferences-system-network-3.png (100%) rename {obs => UI}/forms/images/settings/system-settings-3.png (100%) rename {obs => UI}/forms/images/settings/video-display-3.png (100%) rename {obs => UI}/forms/images/sound.ico (100%) rename {obs => UI}/forms/images/sound_muted.ico (100%) rename {obs => UI}/forms/images/tray_active.png (100%) rename {obs => UI}/forms/images/unmute.png (100%) rename {obs => UI}/forms/images/up.png (100%) rename {obs => UI}/forms/images/visible_mask.png (100%) rename {obs => UI}/forms/obs.qrc (100%) rename {obs => UI}/hotkey-edit.cpp (100%) rename {obs => UI}/hotkey-edit.hpp (100%) rename {obs => UI}/installer/mp-installer.nsi (100%) rename {obs => UI}/item-widget-helpers.cpp (100%) rename {obs => UI}/item-widget-helpers.hpp (100%) rename {obs => UI}/menu-button.cpp (100%) rename {obs => UI}/menu-button.hpp (100%) rename {obs => UI}/mute-checkbox.hpp (100%) rename {obs => UI}/obs-app.cpp (100%) rename {obs => UI}/obs-app.hpp (100%) rename {obs => UI}/obs.rc (100%) rename {obs => UI}/platform-osx.mm (100%) rename {obs => UI}/platform-windows.cpp (100%) rename {obs => UI}/platform-x11.cpp (100%) rename {obs => UI}/platform.hpp (100%) rename {obs => UI}/properties-view.cpp (100%) rename {obs => UI}/properties-view.hpp (100%) rename {obs => UI}/properties-view.moc.hpp (100%) rename {obs => UI}/qt-display.cpp (100%) rename {obs => UI}/qt-display.hpp (100%) rename {obs => UI}/qt-wrappers.cpp (100%) rename {obs => UI}/qt-wrappers.hpp (100%) rename {obs => UI}/remote-text.cpp (100%) rename {obs => UI}/remote-text.hpp (100%) rename {obs => UI}/slider-absoluteset-style.cpp (100%) rename {obs => UI}/slider-absoluteset-style.hpp (100%) rename {obs => UI}/source-label.cpp (100%) rename {obs => UI}/source-label.hpp (100%) rename {obs => UI}/source-list-widget.cpp (100%) rename {obs => UI}/source-list-widget.hpp (100%) rename {obs => UI}/sparkle-updater.mm (100%) rename {obs => UI}/vertical-scroll-area.cpp (100%) rename {obs => UI}/vertical-scroll-area.hpp (100%) rename {obs => UI}/visibility-checkbox.cpp (100%) rename {obs => UI}/visibility-checkbox.hpp (100%) rename {obs => UI}/visibility-item-widget.cpp (100%) rename {obs => UI}/visibility-item-widget.hpp (100%) rename {obs => UI}/volume-control.cpp (100%) rename {obs => UI}/volume-control.hpp (100%) rename {obs => UI}/window-basic-adv-audio.cpp (100%) rename {obs => UI}/window-basic-adv-audio.hpp (100%) rename {obs => UI}/window-basic-filters.cpp (100%) rename {obs => UI}/window-basic-filters.hpp (100%) rename {obs => UI}/window-basic-interaction.cpp (100%) rename {obs => UI}/window-basic-interaction.hpp (100%) rename {obs => UI}/window-basic-main-outputs.cpp (100%) rename {obs => UI}/window-basic-main-outputs.hpp (100%) rename {obs => UI}/window-basic-main-profiles.cpp (100%) rename {obs => UI}/window-basic-main-scene-collections.cpp (100%) rename {obs => UI}/window-basic-main-transitions.cpp (100%) rename {obs => UI}/window-basic-main.cpp (100%) rename {obs => UI}/window-basic-main.hpp (100%) rename {obs => UI}/window-basic-preview.cpp (100%) rename {obs => UI}/window-basic-preview.hpp (100%) rename {obs => UI}/window-basic-properties.cpp (100%) rename {obs => UI}/window-basic-properties.hpp (100%) rename {obs => UI}/window-basic-settings.cpp (100%) rename {obs => UI}/window-basic-settings.hpp (100%) rename {obs => UI}/window-basic-source-select.cpp (100%) rename {obs => UI}/window-basic-source-select.hpp (100%) rename {obs => UI}/window-basic-status-bar.cpp (100%) rename {obs => UI}/window-basic-status-bar.hpp (100%) rename {obs => UI}/window-basic-transform.cpp (100%) rename {obs => UI}/window-basic-transform.hpp (100%) rename {obs => UI}/window-license-agreement.cpp (100%) rename {obs => UI}/window-license-agreement.hpp (100%) rename {obs => UI}/window-log-reply.cpp (100%) rename {obs => UI}/window-log-reply.hpp (100%) rename {obs => UI}/window-main.hpp (100%) rename {obs => UI}/window-namedialog.cpp (100%) rename {obs => UI}/window-namedialog.hpp (100%) rename {obs => UI}/window-projector.cpp (100%) rename {obs => UI}/window-projector.hpp (100%) rename {obs => UI}/window-remux.cpp (100%) rename {obs => UI}/window-remux.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d4f8db79c367b..fafd69818f6105 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ if(NOT INSTALLER_RUN) add_subdirectory(libobs-opengl) add_subdirectory(libobs) - add_subdirectory(obs) + add_subdirectory(UI) add_subdirectory(plugins) if (BUILD_TESTS) add_subdirectory(test) diff --git a/obs/CMakeLists.txt b/UI/CMakeLists.txt similarity index 100% rename from obs/CMakeLists.txt rename to UI/CMakeLists.txt diff --git a/obs/adv-audio-control.cpp b/UI/adv-audio-control.cpp similarity index 100% rename from obs/adv-audio-control.cpp rename to UI/adv-audio-control.cpp diff --git a/obs/adv-audio-control.hpp b/UI/adv-audio-control.hpp similarity index 100% rename from obs/adv-audio-control.hpp rename to UI/adv-audio-control.hpp diff --git a/obs/audio-encoders.cpp b/UI/audio-encoders.cpp similarity index 100% rename from obs/audio-encoders.cpp rename to UI/audio-encoders.cpp diff --git a/obs/audio-encoders.hpp b/UI/audio-encoders.hpp similarity index 100% rename from obs/audio-encoders.hpp rename to UI/audio-encoders.hpp diff --git a/obs/crash-report.cpp b/UI/crash-report.cpp similarity index 100% rename from obs/crash-report.cpp rename to UI/crash-report.cpp diff --git a/obs/crash-report.hpp b/UI/crash-report.hpp similarity index 100% rename from obs/crash-report.hpp rename to UI/crash-report.hpp diff --git a/obs/data/license/gplv2.txt b/UI/data/license/gplv2.txt similarity index 100% rename from obs/data/license/gplv2.txt rename to UI/data/license/gplv2.txt diff --git a/obs/data/locale.ini b/UI/data/locale.ini similarity index 100% rename from obs/data/locale.ini rename to UI/data/locale.ini diff --git a/obs/data/locale/ar-SA.ini b/UI/data/locale/ar-SA.ini similarity index 100% rename from obs/data/locale/ar-SA.ini rename to UI/data/locale/ar-SA.ini diff --git a/obs/data/locale/bg-BG.ini b/UI/data/locale/bg-BG.ini similarity index 100% rename from obs/data/locale/bg-BG.ini rename to UI/data/locale/bg-BG.ini diff --git a/obs/data/locale/ca-ES.ini b/UI/data/locale/ca-ES.ini similarity index 100% rename from obs/data/locale/ca-ES.ini rename to UI/data/locale/ca-ES.ini diff --git a/obs/data/locale/cs-CZ.ini b/UI/data/locale/cs-CZ.ini similarity index 100% rename from obs/data/locale/cs-CZ.ini rename to UI/data/locale/cs-CZ.ini diff --git a/obs/data/locale/da-DK.ini b/UI/data/locale/da-DK.ini similarity index 100% rename from obs/data/locale/da-DK.ini rename to UI/data/locale/da-DK.ini diff --git a/obs/data/locale/de-DE.ini b/UI/data/locale/de-DE.ini similarity index 100% rename from obs/data/locale/de-DE.ini rename to UI/data/locale/de-DE.ini diff --git a/obs/data/locale/el-GR.ini b/UI/data/locale/el-GR.ini similarity index 100% rename from obs/data/locale/el-GR.ini rename to UI/data/locale/el-GR.ini diff --git a/obs/data/locale/en-US.ini b/UI/data/locale/en-US.ini similarity index 100% rename from obs/data/locale/en-US.ini rename to UI/data/locale/en-US.ini diff --git a/obs/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini similarity index 100% rename from obs/data/locale/es-ES.ini rename to UI/data/locale/es-ES.ini diff --git a/obs/data/locale/et-EE.ini b/UI/data/locale/et-EE.ini similarity index 100% rename from obs/data/locale/et-EE.ini rename to UI/data/locale/et-EE.ini diff --git a/obs/data/locale/eu-ES.ini b/UI/data/locale/eu-ES.ini similarity index 100% rename from obs/data/locale/eu-ES.ini rename to UI/data/locale/eu-ES.ini diff --git a/obs/data/locale/fi-FI.ini b/UI/data/locale/fi-FI.ini similarity index 100% rename from obs/data/locale/fi-FI.ini rename to UI/data/locale/fi-FI.ini diff --git a/obs/data/locale/fr-FR.ini b/UI/data/locale/fr-FR.ini similarity index 100% rename from obs/data/locale/fr-FR.ini rename to UI/data/locale/fr-FR.ini diff --git a/obs/data/locale/gl-ES.ini b/UI/data/locale/gl-ES.ini similarity index 100% rename from obs/data/locale/gl-ES.ini rename to UI/data/locale/gl-ES.ini diff --git a/obs/data/locale/he-IL.ini b/UI/data/locale/he-IL.ini similarity index 100% rename from obs/data/locale/he-IL.ini rename to UI/data/locale/he-IL.ini diff --git a/obs/data/locale/hr-HR.ini b/UI/data/locale/hr-HR.ini similarity index 100% rename from obs/data/locale/hr-HR.ini rename to UI/data/locale/hr-HR.ini diff --git a/obs/data/locale/hu-HU.ini b/UI/data/locale/hu-HU.ini similarity index 100% rename from obs/data/locale/hu-HU.ini rename to UI/data/locale/hu-HU.ini diff --git a/obs/data/locale/it-IT.ini b/UI/data/locale/it-IT.ini similarity index 100% rename from obs/data/locale/it-IT.ini rename to UI/data/locale/it-IT.ini diff --git a/obs/data/locale/ja-JP.ini b/UI/data/locale/ja-JP.ini similarity index 100% rename from obs/data/locale/ja-JP.ini rename to UI/data/locale/ja-JP.ini diff --git a/obs/data/locale/ko-KR.ini b/UI/data/locale/ko-KR.ini similarity index 100% rename from obs/data/locale/ko-KR.ini rename to UI/data/locale/ko-KR.ini diff --git a/obs/data/locale/lt-LT.ini b/UI/data/locale/lt-LT.ini similarity index 100% rename from obs/data/locale/lt-LT.ini rename to UI/data/locale/lt-LT.ini diff --git a/obs/data/locale/ms-MY.ini b/UI/data/locale/ms-MY.ini similarity index 100% rename from obs/data/locale/ms-MY.ini rename to UI/data/locale/ms-MY.ini diff --git a/obs/data/locale/nb-NO.ini b/UI/data/locale/nb-NO.ini similarity index 100% rename from obs/data/locale/nb-NO.ini rename to UI/data/locale/nb-NO.ini diff --git a/obs/data/locale/nl-NL.ini b/UI/data/locale/nl-NL.ini similarity index 100% rename from obs/data/locale/nl-NL.ini rename to UI/data/locale/nl-NL.ini diff --git a/obs/data/locale/pl-PL.ini b/UI/data/locale/pl-PL.ini similarity index 100% rename from obs/data/locale/pl-PL.ini rename to UI/data/locale/pl-PL.ini diff --git a/obs/data/locale/pt-BR.ini b/UI/data/locale/pt-BR.ini similarity index 100% rename from obs/data/locale/pt-BR.ini rename to UI/data/locale/pt-BR.ini diff --git a/obs/data/locale/pt-PT.ini b/UI/data/locale/pt-PT.ini similarity index 100% rename from obs/data/locale/pt-PT.ini rename to UI/data/locale/pt-PT.ini diff --git a/obs/data/locale/ro-RO.ini b/UI/data/locale/ro-RO.ini similarity index 100% rename from obs/data/locale/ro-RO.ini rename to UI/data/locale/ro-RO.ini diff --git a/obs/data/locale/ru-RU.ini b/UI/data/locale/ru-RU.ini similarity index 100% rename from obs/data/locale/ru-RU.ini rename to UI/data/locale/ru-RU.ini diff --git a/obs/data/locale/sk-SK.ini b/UI/data/locale/sk-SK.ini similarity index 100% rename from obs/data/locale/sk-SK.ini rename to UI/data/locale/sk-SK.ini diff --git a/obs/data/locale/sl-SI.ini b/UI/data/locale/sl-SI.ini similarity index 100% rename from obs/data/locale/sl-SI.ini rename to UI/data/locale/sl-SI.ini diff --git a/obs/data/locale/sr-CS.ini b/UI/data/locale/sr-CS.ini similarity index 100% rename from obs/data/locale/sr-CS.ini rename to UI/data/locale/sr-CS.ini diff --git a/obs/data/locale/sr-SP.ini b/UI/data/locale/sr-SP.ini similarity index 100% rename from obs/data/locale/sr-SP.ini rename to UI/data/locale/sr-SP.ini diff --git a/obs/data/locale/sv-SE.ini b/UI/data/locale/sv-SE.ini similarity index 100% rename from obs/data/locale/sv-SE.ini rename to UI/data/locale/sv-SE.ini diff --git a/obs/data/locale/ta-IN.ini b/UI/data/locale/ta-IN.ini similarity index 100% rename from obs/data/locale/ta-IN.ini rename to UI/data/locale/ta-IN.ini diff --git a/obs/data/locale/th-TH.ini b/UI/data/locale/th-TH.ini similarity index 100% rename from obs/data/locale/th-TH.ini rename to UI/data/locale/th-TH.ini diff --git a/obs/data/locale/tr-TR.ini b/UI/data/locale/tr-TR.ini similarity index 100% rename from obs/data/locale/tr-TR.ini rename to UI/data/locale/tr-TR.ini diff --git a/obs/data/locale/uk-UA.ini b/UI/data/locale/uk-UA.ini similarity index 100% rename from obs/data/locale/uk-UA.ini rename to UI/data/locale/uk-UA.ini diff --git a/obs/data/locale/vi-VN.ini b/UI/data/locale/vi-VN.ini similarity index 100% rename from obs/data/locale/vi-VN.ini rename to UI/data/locale/vi-VN.ini diff --git a/obs/data/locale/zh-CN.ini b/UI/data/locale/zh-CN.ini similarity index 100% rename from obs/data/locale/zh-CN.ini rename to UI/data/locale/zh-CN.ini diff --git a/obs/data/locale/zh-TW.ini b/UI/data/locale/zh-TW.ini similarity index 100% rename from obs/data/locale/zh-TW.ini rename to UI/data/locale/zh-TW.ini diff --git a/obs/data/themes/Dark.qss b/UI/data/themes/Dark.qss similarity index 100% rename from obs/data/themes/Dark.qss rename to UI/data/themes/Dark.qss diff --git a/obs/data/themes/Dark/cogwheel.png b/UI/data/themes/Dark/cogwheel.png similarity index 100% rename from obs/data/themes/Dark/cogwheel.png rename to UI/data/themes/Dark/cogwheel.png diff --git a/obs/data/themes/Dark/down_arrow.png b/UI/data/themes/Dark/down_arrow.png similarity index 100% rename from obs/data/themes/Dark/down_arrow.png rename to UI/data/themes/Dark/down_arrow.png diff --git a/obs/data/themes/Dark/minus.png b/UI/data/themes/Dark/minus.png similarity index 100% rename from obs/data/themes/Dark/minus.png rename to UI/data/themes/Dark/minus.png diff --git a/obs/data/themes/Dark/mute.png b/UI/data/themes/Dark/mute.png similarity index 100% rename from obs/data/themes/Dark/mute.png rename to UI/data/themes/Dark/mute.png diff --git a/obs/data/themes/Dark/plus.png b/UI/data/themes/Dark/plus.png similarity index 100% rename from obs/data/themes/Dark/plus.png rename to UI/data/themes/Dark/plus.png diff --git a/obs/data/themes/Dark/unmute.png b/UI/data/themes/Dark/unmute.png similarity index 100% rename from obs/data/themes/Dark/unmute.png rename to UI/data/themes/Dark/unmute.png diff --git a/obs/data/themes/Dark/up_arrow.png b/UI/data/themes/Dark/up_arrow.png similarity index 100% rename from obs/data/themes/Dark/up_arrow.png rename to UI/data/themes/Dark/up_arrow.png diff --git a/obs/data/themes/Dark/updown.png b/UI/data/themes/Dark/updown.png similarity index 100% rename from obs/data/themes/Dark/updown.png rename to UI/data/themes/Dark/updown.png diff --git a/obs/data/themes/Default.qss b/UI/data/themes/Default.qss similarity index 100% rename from obs/data/themes/Default.qss rename to UI/data/themes/Default.qss diff --git a/obs/display-helpers.hpp b/UI/display-helpers.hpp similarity index 100% rename from obs/display-helpers.hpp rename to UI/display-helpers.hpp diff --git a/obs/dist/obs.desktop b/UI/dist/obs.desktop similarity index 100% rename from obs/dist/obs.desktop rename to UI/dist/obs.desktop diff --git a/obs/double-slider.cpp b/UI/double-slider.cpp similarity index 100% rename from obs/double-slider.cpp rename to UI/double-slider.cpp diff --git a/obs/double-slider.hpp b/UI/double-slider.hpp similarity index 100% rename from obs/double-slider.hpp rename to UI/double-slider.hpp diff --git a/obs/focus-list.cpp b/UI/focus-list.cpp similarity index 100% rename from obs/focus-list.cpp rename to UI/focus-list.cpp diff --git a/obs/focus-list.hpp b/UI/focus-list.hpp similarity index 100% rename from obs/focus-list.hpp rename to UI/focus-list.hpp diff --git a/obs/forms/NameDialog.ui b/UI/forms/NameDialog.ui similarity index 100% rename from obs/forms/NameDialog.ui rename to UI/forms/NameDialog.ui diff --git a/obs/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui similarity index 100% rename from obs/forms/OBSBasic.ui rename to UI/forms/OBSBasic.ui diff --git a/obs/forms/OBSBasicFilters.ui b/UI/forms/OBSBasicFilters.ui similarity index 100% rename from obs/forms/OBSBasicFilters.ui rename to UI/forms/OBSBasicFilters.ui diff --git a/obs/forms/OBSBasicInteraction.ui b/UI/forms/OBSBasicInteraction.ui similarity index 100% rename from obs/forms/OBSBasicInteraction.ui rename to UI/forms/OBSBasicInteraction.ui diff --git a/obs/forms/OBSBasicSettings.ui b/UI/forms/OBSBasicSettings.ui similarity index 100% rename from obs/forms/OBSBasicSettings.ui rename to UI/forms/OBSBasicSettings.ui diff --git a/obs/forms/OBSBasicSourceSelect.ui b/UI/forms/OBSBasicSourceSelect.ui similarity index 100% rename from obs/forms/OBSBasicSourceSelect.ui rename to UI/forms/OBSBasicSourceSelect.ui diff --git a/obs/forms/OBSBasicTransform.ui b/UI/forms/OBSBasicTransform.ui similarity index 100% rename from obs/forms/OBSBasicTransform.ui rename to UI/forms/OBSBasicTransform.ui diff --git a/obs/forms/OBSLicenseAgreement.ui b/UI/forms/OBSLicenseAgreement.ui similarity index 100% rename from obs/forms/OBSLicenseAgreement.ui rename to UI/forms/OBSLicenseAgreement.ui diff --git a/obs/forms/OBSLogReply.ui b/UI/forms/OBSLogReply.ui similarity index 100% rename from obs/forms/OBSLogReply.ui rename to UI/forms/OBSLogReply.ui diff --git a/obs/forms/OBSRemux.ui b/UI/forms/OBSRemux.ui similarity index 100% rename from obs/forms/OBSRemux.ui rename to UI/forms/OBSRemux.ui diff --git a/obs/forms/images/add.png b/UI/forms/images/add.png similarity index 100% rename from obs/forms/images/add.png rename to UI/forms/images/add.png diff --git a/obs/forms/images/configuration21_16.png b/UI/forms/images/configuration21_16.png similarity index 100% rename from obs/forms/images/configuration21_16.png rename to UI/forms/images/configuration21_16.png diff --git a/obs/forms/images/down.png b/UI/forms/images/down.png similarity index 100% rename from obs/forms/images/down.png rename to UI/forms/images/down.png diff --git a/obs/forms/images/editscene.png b/UI/forms/images/editscene.png similarity index 100% rename from obs/forms/images/editscene.png rename to UI/forms/images/editscene.png diff --git a/obs/forms/images/invisible_mask.png b/UI/forms/images/invisible_mask.png similarity index 100% rename from obs/forms/images/invisible_mask.png rename to UI/forms/images/invisible_mask.png diff --git a/obs/forms/images/list_remove.png b/UI/forms/images/list_remove.png similarity index 100% rename from obs/forms/images/list_remove.png rename to UI/forms/images/list_remove.png diff --git a/obs/forms/images/live.png b/UI/forms/images/live.png similarity index 100% rename from obs/forms/images/live.png rename to UI/forms/images/live.png diff --git a/obs/forms/images/mute.png b/UI/forms/images/mute.png similarity index 100% rename from obs/forms/images/mute.png rename to UI/forms/images/mute.png diff --git a/obs/forms/images/obs.png b/UI/forms/images/obs.png similarity index 100% rename from obs/forms/images/obs.png rename to UI/forms/images/obs.png diff --git a/obs/forms/images/properties.png b/UI/forms/images/properties.png similarity index 100% rename from obs/forms/images/properties.png rename to UI/forms/images/properties.png diff --git a/obs/forms/images/settings/advanced.png b/UI/forms/images/settings/advanced.png similarity index 100% rename from obs/forms/images/settings/advanced.png rename to UI/forms/images/settings/advanced.png diff --git a/obs/forms/images/settings/applications-system-2.png b/UI/forms/images/settings/applications-system-2.png similarity index 100% rename from obs/forms/images/settings/applications-system-2.png rename to UI/forms/images/settings/applications-system-2.png diff --git a/obs/forms/images/settings/decibel_audio_player.png b/UI/forms/images/settings/decibel_audio_player.png similarity index 100% rename from obs/forms/images/settings/decibel_audio_player.png rename to UI/forms/images/settings/decibel_audio_player.png diff --git a/obs/forms/images/settings/network-bluetooth.png b/UI/forms/images/settings/network-bluetooth.png similarity index 100% rename from obs/forms/images/settings/network-bluetooth.png rename to UI/forms/images/settings/network-bluetooth.png diff --git a/obs/forms/images/settings/network.png b/UI/forms/images/settings/network.png similarity index 100% rename from obs/forms/images/settings/network.png rename to UI/forms/images/settings/network.png diff --git a/obs/forms/images/settings/preferences-desktop-keyboard-shortcuts.png b/UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png similarity index 100% rename from obs/forms/images/settings/preferences-desktop-keyboard-shortcuts.png rename to UI/forms/images/settings/preferences-desktop-keyboard-shortcuts.png diff --git a/obs/forms/images/settings/preferences-system-network-3.png b/UI/forms/images/settings/preferences-system-network-3.png similarity index 100% rename from obs/forms/images/settings/preferences-system-network-3.png rename to UI/forms/images/settings/preferences-system-network-3.png diff --git a/obs/forms/images/settings/system-settings-3.png b/UI/forms/images/settings/system-settings-3.png similarity index 100% rename from obs/forms/images/settings/system-settings-3.png rename to UI/forms/images/settings/system-settings-3.png diff --git a/obs/forms/images/settings/video-display-3.png b/UI/forms/images/settings/video-display-3.png similarity index 100% rename from obs/forms/images/settings/video-display-3.png rename to UI/forms/images/settings/video-display-3.png diff --git a/obs/forms/images/sound.ico b/UI/forms/images/sound.ico similarity index 100% rename from obs/forms/images/sound.ico rename to UI/forms/images/sound.ico diff --git a/obs/forms/images/sound_muted.ico b/UI/forms/images/sound_muted.ico similarity index 100% rename from obs/forms/images/sound_muted.ico rename to UI/forms/images/sound_muted.ico diff --git a/obs/forms/images/tray_active.png b/UI/forms/images/tray_active.png similarity index 100% rename from obs/forms/images/tray_active.png rename to UI/forms/images/tray_active.png diff --git a/obs/forms/images/unmute.png b/UI/forms/images/unmute.png similarity index 100% rename from obs/forms/images/unmute.png rename to UI/forms/images/unmute.png diff --git a/obs/forms/images/up.png b/UI/forms/images/up.png similarity index 100% rename from obs/forms/images/up.png rename to UI/forms/images/up.png diff --git a/obs/forms/images/visible_mask.png b/UI/forms/images/visible_mask.png similarity index 100% rename from obs/forms/images/visible_mask.png rename to UI/forms/images/visible_mask.png diff --git a/obs/forms/obs.qrc b/UI/forms/obs.qrc similarity index 100% rename from obs/forms/obs.qrc rename to UI/forms/obs.qrc diff --git a/obs/hotkey-edit.cpp b/UI/hotkey-edit.cpp similarity index 100% rename from obs/hotkey-edit.cpp rename to UI/hotkey-edit.cpp diff --git a/obs/hotkey-edit.hpp b/UI/hotkey-edit.hpp similarity index 100% rename from obs/hotkey-edit.hpp rename to UI/hotkey-edit.hpp diff --git a/obs/installer/mp-installer.nsi b/UI/installer/mp-installer.nsi similarity index 100% rename from obs/installer/mp-installer.nsi rename to UI/installer/mp-installer.nsi diff --git a/obs/item-widget-helpers.cpp b/UI/item-widget-helpers.cpp similarity index 100% rename from obs/item-widget-helpers.cpp rename to UI/item-widget-helpers.cpp diff --git a/obs/item-widget-helpers.hpp b/UI/item-widget-helpers.hpp similarity index 100% rename from obs/item-widget-helpers.hpp rename to UI/item-widget-helpers.hpp diff --git a/obs/menu-button.cpp b/UI/menu-button.cpp similarity index 100% rename from obs/menu-button.cpp rename to UI/menu-button.cpp diff --git a/obs/menu-button.hpp b/UI/menu-button.hpp similarity index 100% rename from obs/menu-button.hpp rename to UI/menu-button.hpp diff --git a/obs/mute-checkbox.hpp b/UI/mute-checkbox.hpp similarity index 100% rename from obs/mute-checkbox.hpp rename to UI/mute-checkbox.hpp diff --git a/obs/obs-app.cpp b/UI/obs-app.cpp similarity index 100% rename from obs/obs-app.cpp rename to UI/obs-app.cpp diff --git a/obs/obs-app.hpp b/UI/obs-app.hpp similarity index 100% rename from obs/obs-app.hpp rename to UI/obs-app.hpp diff --git a/obs/obs.rc b/UI/obs.rc similarity index 100% rename from obs/obs.rc rename to UI/obs.rc diff --git a/obs/platform-osx.mm b/UI/platform-osx.mm similarity index 100% rename from obs/platform-osx.mm rename to UI/platform-osx.mm diff --git a/obs/platform-windows.cpp b/UI/platform-windows.cpp similarity index 100% rename from obs/platform-windows.cpp rename to UI/platform-windows.cpp diff --git a/obs/platform-x11.cpp b/UI/platform-x11.cpp similarity index 100% rename from obs/platform-x11.cpp rename to UI/platform-x11.cpp diff --git a/obs/platform.hpp b/UI/platform.hpp similarity index 100% rename from obs/platform.hpp rename to UI/platform.hpp diff --git a/obs/properties-view.cpp b/UI/properties-view.cpp similarity index 100% rename from obs/properties-view.cpp rename to UI/properties-view.cpp diff --git a/obs/properties-view.hpp b/UI/properties-view.hpp similarity index 100% rename from obs/properties-view.hpp rename to UI/properties-view.hpp diff --git a/obs/properties-view.moc.hpp b/UI/properties-view.moc.hpp similarity index 100% rename from obs/properties-view.moc.hpp rename to UI/properties-view.moc.hpp diff --git a/obs/qt-display.cpp b/UI/qt-display.cpp similarity index 100% rename from obs/qt-display.cpp rename to UI/qt-display.cpp diff --git a/obs/qt-display.hpp b/UI/qt-display.hpp similarity index 100% rename from obs/qt-display.hpp rename to UI/qt-display.hpp diff --git a/obs/qt-wrappers.cpp b/UI/qt-wrappers.cpp similarity index 100% rename from obs/qt-wrappers.cpp rename to UI/qt-wrappers.cpp diff --git a/obs/qt-wrappers.hpp b/UI/qt-wrappers.hpp similarity index 100% rename from obs/qt-wrappers.hpp rename to UI/qt-wrappers.hpp diff --git a/obs/remote-text.cpp b/UI/remote-text.cpp similarity index 100% rename from obs/remote-text.cpp rename to UI/remote-text.cpp diff --git a/obs/remote-text.hpp b/UI/remote-text.hpp similarity index 100% rename from obs/remote-text.hpp rename to UI/remote-text.hpp diff --git a/obs/slider-absoluteset-style.cpp b/UI/slider-absoluteset-style.cpp similarity index 100% rename from obs/slider-absoluteset-style.cpp rename to UI/slider-absoluteset-style.cpp diff --git a/obs/slider-absoluteset-style.hpp b/UI/slider-absoluteset-style.hpp similarity index 100% rename from obs/slider-absoluteset-style.hpp rename to UI/slider-absoluteset-style.hpp diff --git a/obs/source-label.cpp b/UI/source-label.cpp similarity index 100% rename from obs/source-label.cpp rename to UI/source-label.cpp diff --git a/obs/source-label.hpp b/UI/source-label.hpp similarity index 100% rename from obs/source-label.hpp rename to UI/source-label.hpp diff --git a/obs/source-list-widget.cpp b/UI/source-list-widget.cpp similarity index 100% rename from obs/source-list-widget.cpp rename to UI/source-list-widget.cpp diff --git a/obs/source-list-widget.hpp b/UI/source-list-widget.hpp similarity index 100% rename from obs/source-list-widget.hpp rename to UI/source-list-widget.hpp diff --git a/obs/sparkle-updater.mm b/UI/sparkle-updater.mm similarity index 100% rename from obs/sparkle-updater.mm rename to UI/sparkle-updater.mm diff --git a/obs/vertical-scroll-area.cpp b/UI/vertical-scroll-area.cpp similarity index 100% rename from obs/vertical-scroll-area.cpp rename to UI/vertical-scroll-area.cpp diff --git a/obs/vertical-scroll-area.hpp b/UI/vertical-scroll-area.hpp similarity index 100% rename from obs/vertical-scroll-area.hpp rename to UI/vertical-scroll-area.hpp diff --git a/obs/visibility-checkbox.cpp b/UI/visibility-checkbox.cpp similarity index 100% rename from obs/visibility-checkbox.cpp rename to UI/visibility-checkbox.cpp diff --git a/obs/visibility-checkbox.hpp b/UI/visibility-checkbox.hpp similarity index 100% rename from obs/visibility-checkbox.hpp rename to UI/visibility-checkbox.hpp diff --git a/obs/visibility-item-widget.cpp b/UI/visibility-item-widget.cpp similarity index 100% rename from obs/visibility-item-widget.cpp rename to UI/visibility-item-widget.cpp diff --git a/obs/visibility-item-widget.hpp b/UI/visibility-item-widget.hpp similarity index 100% rename from obs/visibility-item-widget.hpp rename to UI/visibility-item-widget.hpp diff --git a/obs/volume-control.cpp b/UI/volume-control.cpp similarity index 100% rename from obs/volume-control.cpp rename to UI/volume-control.cpp diff --git a/obs/volume-control.hpp b/UI/volume-control.hpp similarity index 100% rename from obs/volume-control.hpp rename to UI/volume-control.hpp diff --git a/obs/window-basic-adv-audio.cpp b/UI/window-basic-adv-audio.cpp similarity index 100% rename from obs/window-basic-adv-audio.cpp rename to UI/window-basic-adv-audio.cpp diff --git a/obs/window-basic-adv-audio.hpp b/UI/window-basic-adv-audio.hpp similarity index 100% rename from obs/window-basic-adv-audio.hpp rename to UI/window-basic-adv-audio.hpp diff --git a/obs/window-basic-filters.cpp b/UI/window-basic-filters.cpp similarity index 100% rename from obs/window-basic-filters.cpp rename to UI/window-basic-filters.cpp diff --git a/obs/window-basic-filters.hpp b/UI/window-basic-filters.hpp similarity index 100% rename from obs/window-basic-filters.hpp rename to UI/window-basic-filters.hpp diff --git a/obs/window-basic-interaction.cpp b/UI/window-basic-interaction.cpp similarity index 100% rename from obs/window-basic-interaction.cpp rename to UI/window-basic-interaction.cpp diff --git a/obs/window-basic-interaction.hpp b/UI/window-basic-interaction.hpp similarity index 100% rename from obs/window-basic-interaction.hpp rename to UI/window-basic-interaction.hpp diff --git a/obs/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp similarity index 100% rename from obs/window-basic-main-outputs.cpp rename to UI/window-basic-main-outputs.cpp diff --git a/obs/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp similarity index 100% rename from obs/window-basic-main-outputs.hpp rename to UI/window-basic-main-outputs.hpp diff --git a/obs/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp similarity index 100% rename from obs/window-basic-main-profiles.cpp rename to UI/window-basic-main-profiles.cpp diff --git a/obs/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp similarity index 100% rename from obs/window-basic-main-scene-collections.cpp rename to UI/window-basic-main-scene-collections.cpp diff --git a/obs/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp similarity index 100% rename from obs/window-basic-main-transitions.cpp rename to UI/window-basic-main-transitions.cpp diff --git a/obs/window-basic-main.cpp b/UI/window-basic-main.cpp similarity index 100% rename from obs/window-basic-main.cpp rename to UI/window-basic-main.cpp diff --git a/obs/window-basic-main.hpp b/UI/window-basic-main.hpp similarity index 100% rename from obs/window-basic-main.hpp rename to UI/window-basic-main.hpp diff --git a/obs/window-basic-preview.cpp b/UI/window-basic-preview.cpp similarity index 100% rename from obs/window-basic-preview.cpp rename to UI/window-basic-preview.cpp diff --git a/obs/window-basic-preview.hpp b/UI/window-basic-preview.hpp similarity index 100% rename from obs/window-basic-preview.hpp rename to UI/window-basic-preview.hpp diff --git a/obs/window-basic-properties.cpp b/UI/window-basic-properties.cpp similarity index 100% rename from obs/window-basic-properties.cpp rename to UI/window-basic-properties.cpp diff --git a/obs/window-basic-properties.hpp b/UI/window-basic-properties.hpp similarity index 100% rename from obs/window-basic-properties.hpp rename to UI/window-basic-properties.hpp diff --git a/obs/window-basic-settings.cpp b/UI/window-basic-settings.cpp similarity index 100% rename from obs/window-basic-settings.cpp rename to UI/window-basic-settings.cpp diff --git a/obs/window-basic-settings.hpp b/UI/window-basic-settings.hpp similarity index 100% rename from obs/window-basic-settings.hpp rename to UI/window-basic-settings.hpp diff --git a/obs/window-basic-source-select.cpp b/UI/window-basic-source-select.cpp similarity index 100% rename from obs/window-basic-source-select.cpp rename to UI/window-basic-source-select.cpp diff --git a/obs/window-basic-source-select.hpp b/UI/window-basic-source-select.hpp similarity index 100% rename from obs/window-basic-source-select.hpp rename to UI/window-basic-source-select.hpp diff --git a/obs/window-basic-status-bar.cpp b/UI/window-basic-status-bar.cpp similarity index 100% rename from obs/window-basic-status-bar.cpp rename to UI/window-basic-status-bar.cpp diff --git a/obs/window-basic-status-bar.hpp b/UI/window-basic-status-bar.hpp similarity index 100% rename from obs/window-basic-status-bar.hpp rename to UI/window-basic-status-bar.hpp diff --git a/obs/window-basic-transform.cpp b/UI/window-basic-transform.cpp similarity index 100% rename from obs/window-basic-transform.cpp rename to UI/window-basic-transform.cpp diff --git a/obs/window-basic-transform.hpp b/UI/window-basic-transform.hpp similarity index 100% rename from obs/window-basic-transform.hpp rename to UI/window-basic-transform.hpp diff --git a/obs/window-license-agreement.cpp b/UI/window-license-agreement.cpp similarity index 100% rename from obs/window-license-agreement.cpp rename to UI/window-license-agreement.cpp diff --git a/obs/window-license-agreement.hpp b/UI/window-license-agreement.hpp similarity index 100% rename from obs/window-license-agreement.hpp rename to UI/window-license-agreement.hpp diff --git a/obs/window-log-reply.cpp b/UI/window-log-reply.cpp similarity index 100% rename from obs/window-log-reply.cpp rename to UI/window-log-reply.cpp diff --git a/obs/window-log-reply.hpp b/UI/window-log-reply.hpp similarity index 100% rename from obs/window-log-reply.hpp rename to UI/window-log-reply.hpp diff --git a/obs/window-main.hpp b/UI/window-main.hpp similarity index 100% rename from obs/window-main.hpp rename to UI/window-main.hpp diff --git a/obs/window-namedialog.cpp b/UI/window-namedialog.cpp similarity index 100% rename from obs/window-namedialog.cpp rename to UI/window-namedialog.cpp diff --git a/obs/window-namedialog.hpp b/UI/window-namedialog.hpp similarity index 100% rename from obs/window-namedialog.hpp rename to UI/window-namedialog.hpp diff --git a/obs/window-projector.cpp b/UI/window-projector.cpp similarity index 100% rename from obs/window-projector.cpp rename to UI/window-projector.cpp diff --git a/obs/window-projector.hpp b/UI/window-projector.hpp similarity index 100% rename from obs/window-projector.hpp rename to UI/window-projector.hpp diff --git a/obs/window-remux.cpp b/UI/window-remux.cpp similarity index 100% rename from obs/window-remux.cpp rename to UI/window-remux.cpp diff --git a/obs/window-remux.hpp b/UI/window-remux.hpp similarity index 100% rename from obs/window-remux.hpp rename to UI/window-remux.hpp From 562b3c2990d53e94559cb78a14915083c6d192b2 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 30 Aug 2016 18:23:49 -0700 Subject: [PATCH 027/107] rtmp-services: Add meridex service --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 4301f4c794bb3f..4365199ed0185a 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 32, + "version": 33, "files": [ { "name": "services.json", - "version": 32 + "version": 33 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index a4481f065531b4..e8bb48a01cd5be 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -697,6 +697,18 @@ "recommended": { "max video bitrate": 1500 } + }, + { + "name": "Meridix Live Sports Platform", + "servers": [ + { + "name": "Primary", + "url": "rtmp://publish.meridix.com/live" + } + ], + "recommended": { + "max video bitrate": 3500 + } } ] } From 4986cbae30fb72061c7cf6696389d6192736d27a Mon Sep 17 00:00:00 2001 From: CoDEmanX Date: Fri, 2 Sep 2016 17:13:45 +0200 Subject: [PATCH 028/107] UI: Don't nudge source if preview is locked --- UI/window-basic-main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 11a801478b7e61..3ba05879afc3db 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -4280,6 +4280,9 @@ void OBSBasic::TogglePreview() void OBSBasic::Nudge(int dist, MoveDir dir) { + if (ui->preview->Locked()) + return; + struct MoveInfo { float dist; MoveDir dir; From 4704640c03dd980e5c8e440061266dcc5420e340 Mon Sep 17 00:00:00 2001 From: CoDEmanX Date: Fri, 2 Sep 2016 18:15:09 +0200 Subject: [PATCH 029/107] Add link to CONTRIBUTE for translations --- CONTRIBUTING | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING b/CONTRIBUTING index 36dfff2df6e3ad..59b209e199f72d 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -7,6 +7,9 @@ Contributing Information: https://obsproject.com/forum/list/general-development.21/ - Development IRC channel is primarily #obs-dev on QuakeNet + + - To contribute translations, see: + https://obsproject.com/forum/threads/how-to-contribute-translations-for-obs.16327/ Contributing Guidelines: From a8b59b70eaa39e232055d958e233746d6d980ee9 Mon Sep 17 00:00:00 2001 From: rpslack Date: Tue, 6 Sep 2016 14:44:40 +0900 Subject: [PATCH 030/107] rtmp-services: Add AfreecaTV streaming service --- plugins/rtmp-services/data/package.json | 4 +- plugins/rtmp-services/data/services.json | 80 ++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 4365199ed0185a..c5204b2a4fae71 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 33, + "version": 34, "files": [ { "name": "services.json", - "version": 33 + "version": 34 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index e8bb48a01cd5be..2c2ea27e57fab1 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -709,6 +709,86 @@ "recommended": { "max video bitrate": 3500 } + }, + { + "name": "Afreeca.TV", + "servers": [ + { + "name": "North America : US East", + "url": "rtmp://rtmpmanager-aws-en-east.afreeca.tv/live" + }, + { + "name": "North America : US West", + "url": "rtmp://rtmpmanager-aws-en-west.afreeca.tv/live" + }, + { + "name": "Asia : Singapore", + "url": "rtmp://rtmpmanager-aws-sg.afreeca.tv/live" + }, + { + "name": "Asia : South Korea", + "url": "rtmp://rtmpmanager-en-ko.afreeca.tv/live" + } + ], + "recommended": { + "keyint": 1, + "profile": "main", + "max video bitrate": 5000, + "max audio bitrate": 192 + } + }, + { + "name": "アフリカTV", + "servers": [ + { + "name": "Japan", + "url": "rtmp://rtmpmanager-aws-jp.afreeca.tv/live/" + }, + { + "name": "South Korea", + "url": "rtmp://rtmpmanager-jp.afreeca.tv/live/" + } + ], + "recommended": { + "keyint": 1, + "profile": "main", + "max video bitrate": 5000, + "max audio bitrate": 192 + } + }, + { + "name": "艾菲卡TV", + "servers": [ + { + "name": "Taiwan", + "url": "rtmp://rtmpmanager-gcp-tw.afreeca.tv/live/" + }, + { + "name": "South Korea", + "url": "rtmp://rtmpmanager-tw-ko.afreeca.tv/live/" + } + ], + "recommended": { + "keyint": 1, + "profile": "main", + "max video bitrate": 5000, + "max audio bitrate": 192 + } + }, + { + "name": "아프리카TV", + "servers": [ + { + "name": "Korea", + "url": "rtmp://rtmpmanager-freecat.afreeca.tv/app/" + } + ], + "recommended": { + "keyint": 1, + "profile": "main", + "max video bitrate": 5000, + "max audio bitrate": 192 + } } ] } From 7224e8d36e6c635c9769af1a5f1d9f7c903ac9e0 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 28 Aug 2016 14:22:55 -0700 Subject: [PATCH 031/107] libobs/util: Add index operator to BPtr --- libobs/util/util.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libobs/util/util.hpp b/libobs/util/util.hpp index d7b3a6df0dbbce..4a8224d8245742 100644 --- a/libobs/util/util.hpp +++ b/libobs/util/util.hpp @@ -46,6 +46,8 @@ template class BPtr { inline bool operator!() {return ptr == NULL;} inline bool operator==(T p) {return ptr == p;} inline bool operator!=(T p) {return ptr != p;} + + inline T operator[](size_t idx) const {return ptr[idx];} }; class ConfigFile { From 9ed95b2497a8951b28f040e3e4e90575716e3eee Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 6 Sep 2016 04:52:30 -0700 Subject: [PATCH 032/107] UI: Change certain scene functions to slot functions This allows using the functions via Qt message queue. The reason for this is so a frontend API can queue these functions to call safely from another thread. --- UI/window-basic-main-transitions.cpp | 8 ++++---- UI/window-basic-main.hpp | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 1467866a604526..65460f1d54e87a 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -220,7 +220,7 @@ obs_source_t *OBSBasic::FindTransition(const char *name) return nullptr; } -void OBSBasic::TransitionToScene(obs_scene_t *scene, bool force) +void OBSBasic::TransitionToScene(OBSScene scene, bool force) { obs_source_t *source = obs_scene_get_source(scene); TransitionToScene(source, force); @@ -237,7 +237,7 @@ void OBSBasic::TransitionStopped() swapScene = nullptr; } -void OBSBasic::TransitionToScene(obs_source_t *source, bool force) +void OBSBasic::TransitionToScene(OBSSource source, bool force) { obs_scene_t *scene = obs_scene_from_source(source); bool usingPreviewProgram = IsPreviewProgramMode(); @@ -293,7 +293,7 @@ static inline void SetComboTransition(QComboBox *combo, obs_source_t *tr) } } -void OBSBasic::SetTransition(obs_source_t *transition) +void OBSBasic::SetTransition(OBSSource transition) { obs_source_t *oldTransition = obs_get_output_source(0); @@ -530,7 +530,7 @@ static T GetOBSRef(QListWidgetItem *item) return item->data(static_cast(QtDataRole::OBSRef)).value(); } -void OBSBasic::SetCurrentScene(obs_source_t *scene, bool force) +void OBSBasic::SetCurrentScene(OBSSource scene, bool force) { if (!IsPreviewProgramMode()) { TransitionToScene(scene, force); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index c67a762cf64ebc..a3376df2e72d0b 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -232,10 +232,7 @@ class OBSBasic : public OBSMainWindow { void InitDefaultTransitions(); void InitTransition(obs_source_t *transition); - void TransitionToScene(obs_scene_t *scene, bool force = false); - void TransitionToScene(obs_source_t *scene, bool force = false); obs_source_t *FindTransition(const char *name); - void SetTransition(obs_source_t *transition); OBSSource GetCurrentTransition(); obs_data_array_t *SaveTransitions(); void LoadTransitions(obs_data_array_t *transitions); @@ -265,7 +262,6 @@ class OBSBasic : public OBSMainWindow { void SetPreviewProgramMode(bool enabled); void ResizeProgram(uint32_t cx, uint32_t cy); void SetCurrentScene(obs_scene_t *scene, bool force = false); - void SetCurrentScene(obs_source_t *scene, bool force = false); static void RenderProgram(void *data, uint32_t cx, uint32_t cy); std::vector quickTransitions; @@ -316,6 +312,11 @@ public slots: void SaveProjectDeferred(); void SaveProject(); + void SetTransition(OBSSource transition); + void TransitionToScene(OBSScene scene, bool force = false); + void TransitionToScene(OBSSource scene, bool force = false); + void SetCurrentScene(OBSSource scene, bool force = false); + private slots: void AddSceneItem(OBSSceneItem item); void RemoveSceneItem(OBSSceneItem item); From bfc21317bf2ffc19a028fed6ec6e33c657e44bf4 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 6 Sep 2016 04:49:26 -0700 Subject: [PATCH 033/107] UI: Make scene collection/profile enum funcs external Allows using the functions outside of the file they're in. --- UI/window-basic-main-profiles.cpp | 2 +- UI/window-basic-main-scene-collections.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index d34b2b97d9a32b..fb04f1979a1071 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -24,7 +24,7 @@ #include "window-namedialog.hpp" #include "qt-wrappers.hpp" -template static void EnumProfiles(Func &&cb) +void EnumProfiles(std::function &&cb) { char path[512]; os_glob_t *glob; diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index b3ec51553f8049..d1ce8596a0a49b 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -24,7 +24,9 @@ #include "window-namedialog.hpp" #include "qt-wrappers.hpp" -template static void EnumSceneCollections(Func &&cb) +using namespace std; + +void EnumSceneCollections(std::function &&cb) { char path[512]; os_glob_t *glob; From 8836592d92284c454f2e2d00b512837d9c53cbe9 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 28 Aug 2016 14:24:14 -0700 Subject: [PATCH 034/107] UI: Add front-end API library Allows manipulating and modifying the front-end via plugins. --- UI/CMakeLists.txt | 11 +- UI/api-interface.cpp | 362 ++++++++++++++++++ UI/data/locale/en-US.ini | 3 + UI/forms/OBSBasic.ui | 11 +- UI/obs-app.cpp | 14 +- UI/obs-app.hpp | 16 + UI/obs-frontend-api/CMakeLists.txt | 20 + UI/obs-frontend-api/obs-frontend-api.cpp | 295 ++++++++++++++ UI/obs-frontend-api/obs-frontend-api.h | 134 +++++++ UI/obs-frontend-api/obs-frontend-internal.hpp | 73 ++++ UI/window-basic-main-profiles.cpp | 18 + UI/window-basic-main-scene-collections.cpp | 18 + UI/window-basic-main-transitions.cpp | 15 + UI/window-basic-main.cpp | 60 ++- UI/window-basic-main.hpp | 5 + 15 files changed, 1049 insertions(+), 6 deletions(-) create mode 100644 UI/api-interface.cpp create mode 100644 UI/obs-frontend-api/CMakeLists.txt create mode 100644 UI/obs-frontend-api/obs-frontend-api.cpp create mode 100644 UI/obs-frontend-api/obs-frontend-api.h create mode 100644 UI/obs-frontend-api/obs-frontend-internal.hpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 4185188063731c..e66a26d35a6923 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -1,5 +1,3 @@ -project(obs) - option(ENABLE_UI "Enables the OBS user interfaces" ON) if(DISABLE_UI) message(STATUS "UI disabled") @@ -10,6 +8,12 @@ else() set(FIND_MODE QUIET) endif() +add_subdirectory(obs-frontend-api) + +# ---------------------------------------------------------------------------- + +project(obs) + if(DEFINED QTDIR${_lib_suffix}) list(APPEND CMAKE_PREFIX_PATH "${QTDIR${_lib_suffix}}") elseif(DEFINED QTDIR) @@ -40,6 +44,7 @@ if(NOT Qt5Widgets_FOUND) endif() endif() +include_directories(SYSTEM "obs-frontend-api") include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs") find_package(Libcurl REQUIRED) @@ -97,6 +102,7 @@ endif() set(obs_SOURCES ${obs_PLATFORM_SOURCES} obs-app.cpp + api-interface.cpp window-basic-main.cpp window-basic-filters.cpp window-basic-settings.cpp @@ -219,6 +225,7 @@ target_link_libraries(obs libobs libff Qt5::Widgets + obs-frontend-api ${LIBCURL_LIBRARIES} ${obs_PLATFORM_LIBRARIES}) diff --git a/UI/api-interface.cpp b/UI/api-interface.cpp new file mode 100644 index 00000000000000..390c1da0510718 --- /dev/null +++ b/UI/api-interface.cpp @@ -0,0 +1,362 @@ +#include +#include "obs-app.hpp" +#include "qt-wrappers.hpp" +#include "window-basic-main.hpp" +#include "window-basic-main-outputs.hpp" + +#include + +using namespace std; + +Q_DECLARE_METATYPE(OBSScene); +Q_DECLARE_METATYPE(OBSSource); + +template +static T GetOBSRef(QListWidgetItem *item) +{ + return item->data(static_cast(QtDataRole::OBSRef)).value(); +} + +void EnumProfiles(function &&cb); +void EnumSceneCollections(function &&cb); + +/* ------------------------------------------------------------------------- */ + +template struct OBSStudioCallback { + T callback; + void *private_data; + + inline OBSStudioCallback(T cb, void *p) : + callback(cb), private_data(p) + {} +}; + +template inline size_t GetCallbackIdx( + vector> &callbacks, + T callback, void *private_data) +{ + for (size_t i = 0; i < callbacks.size(); i++) { + OBSStudioCallback curCB = callbacks[i]; + if (curCB.callback == callback && + curCB.private_data == private_data) + return i; + } + + return (size_t)-1; +} + +struct OBSStudioAPI : obs_frontend_callbacks { + OBSBasic *main; + vector> callbacks; + vector> saveCallbacks; + + inline OBSStudioAPI(OBSBasic *main_) : main(main_) {} + + void *obs_frontend_get_main_window(void) override + { + return (void*)main; + } + + void *obs_frontend_get_main_window_handle(void) override + { + return (void*)main->winId(); + } + + void obs_frontend_get_scenes( + struct obs_frontend_source_list *sources) override + { + for (int i = 0; i < main->ui->scenes->count(); i++) { + QListWidgetItem *item = main->ui->scenes->item(i); + OBSScene scene = GetOBSRef(item); + obs_source_t *source = obs_scene_get_source(scene); + + obs_source_addref(source); + da_push_back(sources->sources, &source); + } + } + + obs_source_t *obs_frontend_get_current_scene(void) override + { + OBSSource source; + + if (main->IsPreviewProgramMode()) { + source = obs_weak_source_get_source(main->programScene); + } else { + source = main->GetCurrentSceneSource(); + obs_source_addref(source); + } + return source; + } + + void obs_frontend_set_current_scene(obs_source_t *scene) override + { + if (main->IsPreviewProgramMode()) { + QMetaObject::invokeMethod(main, "TransitionToScene", + Q_ARG(OBSSource, OBSSource(scene))); + } else { + QMetaObject::invokeMethod(main, "SetCurrentScene", + Q_ARG(OBSSource, OBSSource(scene)), + Q_ARG(bool, false)); + } + } + + void obs_frontend_get_transitions( + struct obs_frontend_source_list *sources) override + { + for (int i = 0; i < main->ui->transitions->count(); i++) { + OBSSource tr = main->ui->transitions->itemData(i) + .value(); + + obs_source_addref(tr); + da_push_back(sources->sources, &tr); + } + } + + obs_source_t *obs_frontend_get_current_transition(void) override + { + OBSSource tr = main->GetCurrentTransition(); + + obs_source_addref(tr); + return tr; + } + + void obs_frontend_set_current_transition( + obs_source_t *transition) override + { + QMetaObject::invokeMethod(main, "SetTransition", + Q_ARG(OBSSource, OBSSource(transition))); + } + + void obs_frontend_get_scene_collections( + std::vector &strings) override + { + auto addCollection = [&](const char *name, const char *) + { + strings.emplace_back(name); + return true; + }; + + EnumSceneCollections(addCollection); + } + + char *obs_frontend_get_current_scene_collection(void) override + { + const char *cur_name = config_get_string(App()->GlobalConfig(), + "Basic", "SceneCollection"); + return bstrdup(cur_name); + } + + void obs_frontend_set_current_scene_collection( + const char *collection) override + { + QList menuActions = + main->ui->sceneCollectionMenu->actions(); + QString qstrCollection = QT_UTF8(collection); + + for (int i = 0; i < menuActions.count(); i++) { + QAction *action = menuActions[i]; + QVariant v = action->property("file_name"); + + if (v.typeName() != nullptr) { + if (action->text() == qstrCollection) { + action->trigger(); + break; + } + } + } + } + + void obs_frontend_get_profiles( + std::vector &strings) override + { + auto addProfile = [&](const char *name, const char *) + { + strings.emplace_back(name); + return true; + }; + + EnumProfiles(addProfile); + } + + char *obs_frontend_get_current_profile(void) override + { + const char *name = config_get_string(App()->GlobalConfig(), + "Basic", "Profile"); + return bstrdup(name); + } + + void obs_frontend_set_current_profile(const char *profile) override + { + QList menuActions = + main->ui->profileMenu->actions(); + QString qstrProfile = QT_UTF8(profile); + + for (int i = 0; i < menuActions.count(); i++) { + QAction *action = menuActions[i]; + QVariant v = action->property("file_name"); + + if (v.typeName() != nullptr) { + if (action->text() == qstrProfile) { + action->trigger(); + break; + } + } + } + } + + void obs_frontend_streaming_start(void) override + { + QMetaObject::invokeMethod(main, "StartStreaming"); + } + + void obs_frontend_streaming_stop(void) override + { + QMetaObject::invokeMethod(main, "StopStreaming"); + } + + bool obs_frontend_streaming_active(void) override + { + return main->outputHandler->StreamingActive(); + } + + void obs_frontend_recording_start(void) override + { + QMetaObject::invokeMethod(main, "StartRecording"); + } + + void obs_frontend_recording_stop(void) override + { + QMetaObject::invokeMethod(main, "StopRecording"); + } + + bool obs_frontend_recording_active(void) override + { + return main->outputHandler->StreamingActive(); + } + + void *obs_frontend_add_tools_menu_qaction(const char *name) override + { + main->ui->menuTools->setEnabled(true); + return (void*)main->ui->menuTools->addAction(QT_UTF8(name)); + } + + void obs_frontend_add_tools_menu_item(const char *name, + obs_frontend_cb callback, void *private_data) override + { + main->ui->menuTools->setEnabled(true); + + auto func = [private_data, callback] () + { + callback(private_data); + }; + + QAction *action = main->ui->menuTools->addAction(QT_UTF8(name)); + QObject::connect(action, &QAction::triggered, func); + } + + void obs_frontend_add_event_callback(obs_frontend_event_cb callback, + void *private_data) override + { + size_t idx = GetCallbackIdx(callbacks, callback, private_data); + if (idx == (size_t)-1) + callbacks.emplace_back(callback, private_data); + } + + void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, + void *private_data) override + { + size_t idx = GetCallbackIdx(callbacks, callback, private_data); + if (idx == (size_t)-1) + return; + + callbacks.erase(callbacks.begin() + idx); + } + + obs_output_t *obs_frontend_get_streaming_output(void) override + { + OBSOutput output = main->outputHandler->streamOutput; + obs_output_addref(output); + return output; + } + + obs_output_t *obs_frontend_get_recording_output(void) override + { + OBSOutput out = main->outputHandler->fileOutput; + obs_output_addref(out); + return out; + } + + config_t *obs_frontend_get_profile_config(void) override + { + return main->basicConfig; + } + + config_t *obs_frontend_get_global_config(void) override + { + return App()->GlobalConfig(); + } + + void obs_frontend_save(void) override + { + main->SaveProject(); + } + + void obs_frontend_add_save_callback(obs_frontend_save_cb callback, + void *private_data) override + { + size_t idx = GetCallbackIdx(saveCallbacks, callback, + private_data); + if (idx == (size_t)-1) + saveCallbacks.emplace_back(callback, private_data); + } + + void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, + void *private_data) override + { + size_t idx = GetCallbackIdx(saveCallbacks, callback, + private_data); + if (idx == (size_t)-1) + return; + + saveCallbacks.erase(saveCallbacks.begin() + idx); + } + + void obs_frontend_push_ui_translation( + obs_frontend_translate_ui_cb translate) override + { + App()->PushUITranslation(translate); + } + + void obs_frontend_pop_ui_translation(void) override + { + App()->PopUITranslation(); + } + + void on_load(obs_data_t *settings) override + { + for (auto cb : saveCallbacks) + cb.callback(settings, false, cb.private_data); + } + + void on_save(obs_data_t *settings) override + { + for (auto cb : saveCallbacks) + cb.callback(settings, true, cb.private_data); + } + + void on_event(enum obs_frontend_event event) override + { + if (main->disableSaving) + return; + + for (auto cb : callbacks) + cb.callback(event, cb.private_data); + } +}; + +obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main) +{ + obs_frontend_callbacks *api = new OBSStudioAPI(main); + obs_frontend_set_callbacks_internal(api); + return api; +} diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 0c47e9deb0cc5a..35c06fc17b85c2 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -354,6 +354,9 @@ Basic.MainMenu.View.StatusBar="&Status Bar" Basic.MainMenu.SceneCollection="&Scene Collection" Basic.MainMenu.Profile="&Profile" +# basic mode help menu +Basic.MainMenu.Tools="&Tools" + # basic mode help menu Basic.MainMenu.Help="&Help" Basic.MainMenu.Help.Website="Visit &Website" diff --git a/UI/forms/OBSBasic.ui b/UI/forms/OBSBasic.ui index 76d8e5517c7b44..5b922bcf8e17d9 100644 --- a/UI/forms/OBSBasic.ui +++ b/UI/forms/OBSBasic.ui @@ -229,7 +229,7 @@ 0 0 - 201 + 215 16 @@ -913,11 +913,20 @@ + + + false + + + Basic.MainMenu.Tools + + + diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index ca0deda29c3132..eba032ae9d0977 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -937,12 +937,22 @@ const char *OBSApp::GetCurrentLog() const return currentLogFile.c_str(); } +bool OBSApp::TranslateString(const char *lookupVal, const char **out) const +{ + for (obs_frontend_translate_ui_cb cb : translatorHooks) { + if (cb(lookupVal, out)) + return true; + } + + return text_lookup_getstr(App()->GetTextLookup(), lookupVal, out); +} + QString OBSTranslator::translate(const char *context, const char *sourceText, const char *disambiguation, int n) const { const char *out = nullptr; - if (!text_lookup_getstr(App()->GetTextLookup(), sourceText, &out)) - return QString(); + if (!App()->TranslateString(sourceText, &out)) + return QString(sourceText); UNUSED_PARAMETER(context); UNUSED_PARAMETER(disambiguation); diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index a341a821eb8955..4964f5f9a81fc8 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -25,9 +25,11 @@ #include #include #include +#include #include #include #include +#include #include "window-main.hpp" @@ -71,6 +73,8 @@ class OBSApp : public QApplication { os_inhibit_t *sleepInhibitor = nullptr; int sleepInhibitRefs = 0; + std::deque translatorHooks; + bool InitGlobalConfig(); bool InitGlobalConfigDefaults(); bool InitLocale(); @@ -102,6 +106,8 @@ class OBSApp : public QApplication { return textLookup.GetString(lookupVal); } + bool TranslateString(const char *lookupVal, const char **out) const; + profiler_name_store_t *GetProfilerNameStore() const { return profilerNameStore; @@ -131,6 +137,16 @@ class OBSApp : public QApplication { if (--sleepInhibitRefs == 0) os_inhibit_sleep_set_active(sleepInhibitor, false); } + + inline void PushUITranslation(obs_frontend_translate_ui_cb cb) + { + translatorHooks.emplace_front(cb); + } + + inline void PopUITranslation() + { + translatorHooks.pop_front(); + } }; int GetConfigPath(char *path, size_t size, const char *name); diff --git a/UI/obs-frontend-api/CMakeLists.txt b/UI/obs-frontend-api/CMakeLists.txt new file mode 100644 index 00000000000000..38eefb69eb08a4 --- /dev/null +++ b/UI/obs-frontend-api/CMakeLists.txt @@ -0,0 +1,20 @@ +project(obs-frontend-api) + +include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/libobs") + +add_definitions(-DLIBOBS_EXPORTS) + +set(obs-frontend-api_SOURCES + obs-frontend-api.cpp) + +set(obs-frontend-api_HEADERS + obs-frontend-internal.hpp + obs-frontend-api.h) + +add_library(obs-frontend-api SHARED + ${obs-frontend-api_SOURCES} + ${obs-frontend-api_HEADERS}) +target_link_libraries(obs-frontend-api + libobs) + +install_obs_core(obs-frontend-api) diff --git a/UI/obs-frontend-api/obs-frontend-api.cpp b/UI/obs-frontend-api/obs-frontend-api.cpp new file mode 100644 index 00000000000000..5d01b64d8e50d4 --- /dev/null +++ b/UI/obs-frontend-api/obs-frontend-api.cpp @@ -0,0 +1,295 @@ +#include "obs-frontend-internal.hpp" +#include + +using namespace std; + +static unique_ptr c; + +void obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks) +{ + c.reset(callbacks); +} + +static inline bool callbacks_valid_(const char *func_name) +{ + if (!c) { + blog(LOG_WARNING, "Tried to call %s with no callbacks!", + func_name); + return false; + } + + return true; +} + +#define callbacks_valid() callbacks_valid_(__FUNCTION__) + +static char **convert_string_list(vector &strings) +{ + size_t size = 0; + size_t string_data_offset = (strings.size() + 1) * sizeof(char*); + uint8_t *out; + char **ptr_list; + char *string_data; + + size += string_data_offset; + + for (auto &str : strings) + size += str.size() + 1; + + if (!size) + return 0; + + out = (uint8_t*)bmalloc(size); + ptr_list = (char**)out; + string_data = (char*)(out + string_data_offset); + + for (auto &str : strings) { + *ptr_list = string_data; + memcpy(string_data, str.c_str(), str.size() + 1); + + ptr_list++; + string_data += str.size() + 1; + } + + *ptr_list = nullptr; + return (char**)out; +} + +/* ------------------------------------------------------------------------- */ + +void *obs_frontend_get_main_window(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_main_window() + : nullptr; +} + +void *obs_frontend_get_main_window_handle(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_main_window_handle() + : nullptr; +} + +char **obs_frontend_get_scene_names(void) +{ + if (!callbacks_valid()) + return NULL; + + struct obs_frontend_source_list sources = {}; + vector names; + c->obs_frontend_get_scenes(&sources); + + for (size_t i = 0; i < sources.sources.num; i++) { + obs_source_t *source = sources.sources.array[i]; + const char *name = obs_source_get_name(source); + names.emplace_back(name); + } + + obs_frontend_source_list_free(&sources); + return convert_string_list(names); +} + +void obs_frontend_get_scenes(struct obs_frontend_source_list *sources) +{ + if (callbacks_valid()) c->obs_frontend_get_scenes(sources); +} + +obs_source_t *obs_frontend_get_current_scene(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_current_scene() + : nullptr; +} + +void obs_frontend_set_current_scene(obs_source_t *scene) +{ + if (callbacks_valid()) c->obs_frontend_set_current_scene(scene); +} + +void obs_frontend_get_transitions(struct obs_frontend_source_list *sources) +{ + if (callbacks_valid()) c->obs_frontend_get_transitions(sources); +} + +obs_source_t *obs_frontend_get_current_transition(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_current_transition() + : nullptr; +} + +void obs_frontend_set_current_transition(obs_source_t *transition) +{ + if (callbacks_valid()) + c->obs_frontend_set_current_transition(transition); +} + +char **obs_frontend_get_scene_collections(void) +{ + if (!callbacks_valid()) + return nullptr; + + vector strings; + c->obs_frontend_get_scene_collections(strings); + return convert_string_list(strings); +} + +char *obs_frontend_get_current_scene_collection(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_current_scene_collection() + : nullptr; +} + +void obs_frontend_set_current_scene_collection(const char *collection) +{ + if (callbacks_valid()) + c->obs_frontend_set_current_scene_collection(collection); +} + +char **obs_frontend_get_profiles(void) +{ + if (!callbacks_valid()) + return nullptr; + + vector strings; + c->obs_frontend_get_profiles(strings); + return convert_string_list(strings); +} + +char *obs_frontend_get_current_profile(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_current_profile() + : nullptr; +} + +void obs_frontend_set_current_profile(const char *profile) +{ + if (callbacks_valid()) + c->obs_frontend_set_current_profile(profile); +} + +void obs_frontend_streaming_start(void) +{ + if (callbacks_valid()) c->obs_frontend_streaming_start(); +} + +void obs_frontend_streaming_stop(void) +{ + if (callbacks_valid()) c->obs_frontend_streaming_stop(); +} + +bool obs_frontend_streaming_active(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_streaming_active() + : false; +} + +void obs_frontend_recording_start(void) +{ + if (callbacks_valid()) c->obs_frontend_recording_start(); +} + +void obs_frontend_recording_stop(void) +{ + if (callbacks_valid()) c->obs_frontend_recording_stop(); +} + +bool obs_frontend_recording_active(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_recording_active() + : false; +} + +void *obs_frontend_add_tools_menu_qaction(const char *name) +{ + return !!callbacks_valid() + ? c->obs_frontend_add_tools_menu_qaction(name) + : nullptr; +} + +void obs_frontend_add_tools_menu_item(const char *name, + obs_frontend_cb callback, void *private_data) +{ + if (callbacks_valid()) + c->obs_frontend_add_tools_menu_item(name, callback, + private_data); +} + +void obs_frontend_add_event_callback(obs_frontend_event_cb callback, + void *private_data) +{ + if (callbacks_valid()) + c->obs_frontend_add_event_callback(callback, private_data); +} + +void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, + void *private_data) +{ + if (callbacks_valid()) + c->obs_frontend_remove_event_callback(callback, private_data); +} + +obs_output_t *obs_frontend_get_streaming_output(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_streaming_output() + : nullptr; +} + +obs_output_t *obs_frontend_get_recording_output(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_recording_output() + : nullptr; +} + +config_t *obs_frontend_get_profile_config(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_profile_config() + : nullptr; +} + +config_t *obs_frontend_get_global_config(void) +{ + return !!callbacks_valid() + ? c->obs_frontend_get_global_config() + : nullptr; +} + +void obs_frontend_save(void) +{ + if (callbacks_valid()) + c->obs_frontend_save(); +} + +void obs_frontend_add_save_callback(obs_frontend_save_cb callback, + void *private_data) +{ + if (callbacks_valid()) + c->obs_frontend_add_save_callback(callback, private_data); +} + +void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, + void *private_data) +{ + if (callbacks_valid()) + c->obs_frontend_remove_save_callback(callback, private_data); +} + +void obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate) +{ + if (callbacks_valid()) + c->obs_frontend_push_ui_translation(translate); +} + +void obs_frontend_pop_ui_translation(void) +{ + if (callbacks_valid()) + c->obs_frontend_pop_ui_translation(); +} diff --git a/UI/obs-frontend-api/obs-frontend-api.h b/UI/obs-frontend-api/obs-frontend-api.h new file mode 100644 index 00000000000000..8a6272a2ac1abb --- /dev/null +++ b/UI/obs-frontend-api/obs-frontend-api.h @@ -0,0 +1,134 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct config_data; +typedef struct config_data config_t; + +struct obs_data; +typedef struct obs_data obs_data_t; + +/* ------------------------------------------------------------------------- */ + +struct obs_frontend_source_list { + DARRAY(obs_source_t*) sources; +}; + +static inline void obs_frontend_source_list_free( + struct obs_frontend_source_list *source_list) +{ + size_t num = source_list->sources.num; + for (size_t i = 0; i < num; i++) + obs_source_release(source_list->sources.array[i]); + da_free(source_list->sources); +} + +/* ------------------------------------------------------------------------- */ + +/* NOTE: Functions that return char** string lists are a single allocation of + * memory with pointers to itself. Free with a single call to bfree on the + * base char** pointer. */ + +/* NOTE: User interface should not use typical Qt locale translation methods, + * as the OBS UI bypasses it to use a custom translation implementation. Use + * standard module translation methods, obs_module_text. For text in a Qt + * window, use obs_frontend_push_ui_translation when the text is about to be + * translated, and obs_frontend_pop_ui_translation when translation is + * complete. */ + +EXPORT void *obs_frontend_get_main_window(void); +EXPORT void *obs_frontend_get_main_window_handle(void); + +EXPORT char **obs_frontend_get_scene_names(void); +EXPORT void obs_frontend_get_scenes(struct obs_frontend_source_list *sources); +EXPORT obs_source_t *obs_frontend_get_current_scene(void); +EXPORT void obs_frontend_set_current_scene(obs_source_t *scene); + +EXPORT void obs_frontend_get_transitions( + struct obs_frontend_source_list *sources); +EXPORT obs_source_t *obs_frontend_get_current_transition(void); +EXPORT void obs_frontend_set_current_transition(obs_source_t *transition); + +EXPORT char **obs_frontend_get_scene_collections(void); +EXPORT char *obs_frontend_get_current_scene_collection(void); +EXPORT void obs_frontend_set_current_scene_collection(const char *collection); + +EXPORT char **obs_frontend_get_profiles(void); +EXPORT char *obs_frontend_get_current_profile(void); +EXPORT void obs_frontend_set_current_profile(const char *profile); + +EXPORT void obs_frontend_streaming_start(void); +EXPORT void obs_frontend_streaming_stop(void); +EXPORT bool obs_frontend_streaming_active(void); + +EXPORT void obs_frontend_recording_start(void); +EXPORT void obs_frontend_recording_stop(void); +EXPORT bool obs_frontend_recording_active(void); + +typedef void (*obs_frontend_cb)(void *private_data); + +EXPORT void *obs_frontend_add_tools_menu_qaction(const char *name); +EXPORT void obs_frontend_add_tools_menu_item(const char *name, + obs_frontend_cb callback, void *private_data); + +enum obs_frontend_event { + OBS_FRONTEND_EVENT_STREAMING_STARTING, + OBS_FRONTEND_EVENT_STREAMING_STARTED, + OBS_FRONTEND_EVENT_STREAMING_STOPPING, + OBS_FRONTEND_EVENT_STREAMING_STOPPED, + OBS_FRONTEND_EVENT_RECORDING_STARTING, + OBS_FRONTEND_EVENT_RECORDING_STARTED, + OBS_FRONTEND_EVENT_RECORDING_STOPPING, + OBS_FRONTEND_EVENT_RECORDING_STOPPED, + OBS_FRONTEND_EVENT_SCENE_CHANGED, + OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED, + OBS_FRONTEND_EVENT_TRANSITION_CHANGED, + OBS_FRONTEND_EVENT_TRANSITION_STOPPED, + OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED, + OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED, + OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED, + OBS_FRONTEND_EVENT_PROFILE_CHANGED, + OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED, + OBS_FRONTEND_EVENT_EXIT +}; + +typedef void (*obs_frontend_event_cb)(enum obs_frontend_event event, + void *private_data); + +EXPORT void obs_frontend_add_event_callback(obs_frontend_event_cb callback, + void *private_data); +EXPORT void obs_frontend_remove_event_callback(obs_frontend_event_cb callback, + void *private_data); + +typedef void (*obs_frontend_save_cb)(obs_data_t *save_data, bool saving, + void *private_data); + +EXPORT void obs_frontend_save(void); +EXPORT void obs_frontend_add_save_callback(obs_frontend_save_cb callback, + void *private_data); +EXPORT void obs_frontend_remove_save_callback(obs_frontend_save_cb callback, + void *private_data); + +EXPORT obs_output_t *obs_frontend_get_streaming_output(void); +EXPORT obs_output_t *obs_frontend_get_recording_output(void); + +EXPORT config_t *obs_frontend_get_profile_config(void); +EXPORT config_t *obs_frontend_get_global_config(void); + +typedef bool (*obs_frontend_translate_ui_cb)(const char *text, + const char **out); + +EXPORT void obs_frontend_push_ui_translation( + obs_frontend_translate_ui_cb translate); +EXPORT void obs_frontend_pop_ui_translation(void); + +/* ------------------------------------------------------------------------- */ + +#ifdef __cplusplus +} +#endif diff --git a/UI/obs-frontend-api/obs-frontend-internal.hpp b/UI/obs-frontend-api/obs-frontend-internal.hpp new file mode 100644 index 00000000000000..cb8da964764445 --- /dev/null +++ b/UI/obs-frontend-api/obs-frontend-internal.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "obs-frontend-api.h" + +#include +#include + +struct obs_frontend_callbacks { + virtual void *obs_frontend_get_main_window(void)=0; + virtual void *obs_frontend_get_main_window_handle(void)=0; + + virtual void obs_frontend_get_scenes( + struct obs_frontend_source_list *sources)=0; + virtual obs_source_t *obs_frontend_get_current_scene(void)=0; + virtual void obs_frontend_set_current_scene(obs_source_t *scene)=0; + + virtual void obs_frontend_get_transitions( + struct obs_frontend_source_list *sources)=0; + virtual obs_source_t *obs_frontend_get_current_transition(void)=0; + virtual void obs_frontend_set_current_transition( + obs_source_t *transition)=0; + + virtual void obs_frontend_get_scene_collections( + std::vector &strings)=0; + virtual char *obs_frontend_get_current_scene_collection(void)=0; + virtual void obs_frontend_set_current_scene_collection( + const char *collection)=0; + + virtual void obs_frontend_get_profiles( + std::vector &strings)=0; + virtual char *obs_frontend_get_current_profile(void)=0; + virtual void obs_frontend_set_current_profile(const char *profile)=0; + + virtual void obs_frontend_streaming_start(void)=0; + virtual void obs_frontend_streaming_stop(void)=0; + virtual bool obs_frontend_streaming_active(void)=0; + + virtual void obs_frontend_recording_start(void)=0; + virtual void obs_frontend_recording_stop(void)=0; + virtual bool obs_frontend_recording_active(void)=0; + + virtual void *obs_frontend_add_tools_menu_qaction(const char *name)=0; + virtual void obs_frontend_add_tools_menu_item(const char *name, + obs_frontend_cb callback, void *private_data)=0; + + virtual void obs_frontend_add_event_callback( + obs_frontend_event_cb callback, void *private_data)=0; + virtual void obs_frontend_remove_event_callback( + obs_frontend_event_cb callback, void *private_data)=0; + + virtual obs_output_t *obs_frontend_get_streaming_output(void)=0; + virtual obs_output_t *obs_frontend_get_recording_output(void)=0; + + virtual config_t *obs_frontend_get_profile_config(void)=0; + virtual config_t *obs_frontend_get_global_config(void)=0; + + virtual void obs_frontend_save(void)=0; + virtual void obs_frontend_add_save_callback( + obs_frontend_save_cb callback, void *private_data)=0; + virtual void obs_frontend_remove_save_callback( + obs_frontend_save_cb callback, void *private_data)=0; + + virtual void obs_frontend_push_ui_translation( + obs_frontend_translate_ui_cb translate)=0; + virtual void obs_frontend_pop_ui_translation(void)=0; + + virtual void on_load(obs_data_t *settings)=0; + virtual void on_save(obs_data_t *settings)=0; + virtual void on_event(enum obs_frontend_event event)=0; +}; + +EXPORT void obs_frontend_set_callbacks_internal( + obs_frontend_callbacks *callbacks); diff --git a/UI/window-basic-main-profiles.cpp b/UI/window-basic-main-profiles.cpp index fb04f1979a1071..9637fca34b6391 100644 --- a/UI/window-basic-main-profiles.cpp +++ b/UI/window-basic-main-profiles.cpp @@ -239,6 +239,11 @@ bool OBSBasic::AddProfile(bool create_new, const char *title, const char *text, config_save_safe(App()->GlobalConfig(), "tmp", nullptr); UpdateTitleBar(); + + if (api) { + api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); + api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); + } return true; } @@ -363,6 +368,11 @@ void OBSBasic::on_actionRenameProfile_triggered() DeleteProfile(curName.c_str(), curDir.c_str()); RefreshProfiles(); } + + if (api) { + api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); + api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); + } } void OBSBasic::on_actionRemoveProfile_triggered() @@ -431,6 +441,11 @@ void OBSBasic::on_actionRemoveProfile_triggered() blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); + + if (api) { + api->on_event(OBS_FRONTEND_EVENT_PROFILE_LIST_CHANGED); + api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); + } } void OBSBasic::ChangeProfile() @@ -481,4 +496,7 @@ void OBSBasic::ChangeProfile() blog(LOG_INFO, "Switched to profile '%s' (%s)", newName, newDir); blog(LOG_INFO, "------------------------------------------------"); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_PROFILE_CHANGED); } diff --git a/UI/window-basic-main-scene-collections.cpp b/UI/window-basic-main-scene-collections.cpp index d1ce8596a0a49b..c35b0b1016fe45 100644 --- a/UI/window-basic-main-scene-collections.cpp +++ b/UI/window-basic-main-scene-collections.cpp @@ -178,6 +178,11 @@ void OBSBasic::AddSceneCollection(bool create_new) blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); + + if (api) { + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + } } void OBSBasic::RefreshSceneCollections() @@ -267,6 +272,11 @@ void OBSBasic::on_actionRenameSceneCollection_triggered() UpdateTitleBar(); RefreshSceneCollections(); + + if (api) { + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + } } void OBSBasic::on_actionRemoveSceneCollection_triggered() @@ -330,6 +340,11 @@ void OBSBasic::on_actionRemoveSceneCollection_triggered() blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); + + if (api) { + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_LIST_CHANGED); + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); + } } void OBSBasic::ChangeSceneCollection() @@ -366,4 +381,7 @@ void OBSBasic::ChangeSceneCollection() blog(LOG_INFO, "------------------------------------------------"); UpdateTitleBar(); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_COLLECTION_CHANGED); } diff --git a/UI/window-basic-main-transitions.cpp b/UI/window-basic-main-transitions.cpp index 65460f1d54e87a..3a69b24ce86c07 100644 --- a/UI/window-basic-main-transitions.cpp +++ b/UI/window-basic-main-transitions.cpp @@ -234,6 +234,9 @@ void OBSBasic::TransitionStopped() SetCurrentScene(scene); } + if (api) + api->on_event(OBS_FRONTEND_EVENT_TRANSITION_STOPPED); + swapScene = nullptr; } @@ -281,6 +284,9 @@ void OBSBasic::TransitionToScene(OBSSource source, bool force) obs_scene_release(scene); obs_source_release(transition); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_CHANGED); } static inline void SetComboTransition(QComboBox *combo, obs_source_t *tr) @@ -317,6 +323,9 @@ void OBSBasic::SetTransition(OBSSource transition) bool configurable = obs_source_configurable(transition); ui->transitionRemove->setEnabled(configurable); ui->transitionProps->setEnabled(configurable); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_TRANSITION_CHANGED); } OBSSource OBSBasic::GetCurrentTransition() @@ -378,6 +387,9 @@ void OBSBasic::AddTransition() ui->transitions->setCurrentIndex(ui->transitions->count() - 1); CreatePropertiesWindow(source); obs_source_release(source); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); } } @@ -428,6 +440,9 @@ void OBSBasic::on_transitionRemove_clicked() } ui->transitions->removeItem(idx); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_TRANSITION_LIST_CHANGED); } void OBSBasic::RenameTransition() diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 11a801478b7e61..eae28ffc9cab3c 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -373,6 +373,13 @@ void OBSBasic::Save(const char *file) obs_data_set_bool(saveData, "preview_locked", ui->preview->Locked()); + if (api) { + obs_data_t *moduleObj = obs_data_create(); + api->on_save(moduleObj); + obs_data_set_obj(saveData, "modules", moduleObj); + obs_data_release(moduleObj); + } + if (!obs_data_save_json_safe(saveData, file, "tmp", "bak")) blog(LOG_ERROR, "Could not save scene data to %s", file); @@ -655,6 +662,12 @@ void OBSBasic::Load(const char *file) ui->preview->SetLocked(previewLocked); ui->actionLockPreview->setChecked(previewLocked); + if (api) { + obs_data_t *modulesObj = obs_data_get_obj(data, "modules"); + api->on_load(modulesObj); + obs_data_release(modulesObj); + } + obs_data_release(data); if (!opt_starting_scene.empty()) @@ -1019,6 +1032,8 @@ void OBSBasic::ResetOutputs() #define SHUTDOWN_SEPARATOR \ "==== Shutting down ==================================================" +extern obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main); + void OBSBasic::OBSInit() { ProfileScope("OBSBasic::OBSInit"); @@ -1065,6 +1080,8 @@ void OBSBasic::OBSInit() InitOBSCallbacks(); InitHotkeys(); + api = InitializeAPIInterface(this); + AddExtraModulePaths(); blog(LOG_INFO, "---------------------------------"); obs_load_all_modules(); @@ -2067,6 +2084,9 @@ void OBSBasic::DuplicateSelectedScene() AddScene(source); SetCurrentScene(source, true); obs_scene_release(scene); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); break; } } @@ -2076,8 +2096,12 @@ void OBSBasic::RemoveSelectedScene() OBSScene scene = GetCurrentScene(); if (scene) { obs_source_t *source = obs_scene_get_source(scene); - if (QueryRemoveSource(source)) + if (QueryRemoveSource(source)) { obs_source_remove(source); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + } } } @@ -2639,6 +2663,10 @@ void OBSBasic::closeEvent(QCloseEvent *event) signalHandlers.clear(); SaveProjectNow(); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_EXIT); + disableSaving++; /* Clear all scene data (dialogs, widgets, widget sub-items, scenes, @@ -3487,6 +3515,9 @@ void OBSBasic::SceneNameEdited(QWidget *editor, obs_source_t *source = obs_scene_get_source(scene); RenameListItem(this, ui->scenes, source, text); + if (api) + api->on_event(OBS_FRONTEND_EVENT_SCENE_LIST_CHANGED); + UNUSED_PARAMETER(endHint); } @@ -3537,6 +3568,12 @@ void OBSBasic::OpenSceneFilters() void OBSBasic::StartStreaming() { + if (outputHandler->StreamingActive()) + return; + + if (api) + api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTING); + SaveProject(); ui->streamButton->setEnabled(false); @@ -3684,6 +3721,9 @@ void OBSBasic::StreamingStart() sysTrayStream->setText(ui->streamButton->text()); sysTrayStream->setEnabled(true); + if (api) + api->on_event(OBS_FRONTEND_EVENT_STREAMING_STARTED); + OnActivate(); blog(LOG_INFO, STREAMING_START); @@ -3693,6 +3733,9 @@ void OBSBasic::StreamStopping() { ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); sysTrayStream->setText(ui->streamButton->text()); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); } void OBSBasic::StreamingStop(int code) @@ -3730,6 +3773,9 @@ void OBSBasic::StreamingStop(int code) sysTrayStream->setText(ui->streamButton->text()); sysTrayStream->setEnabled(true); + if (api) + api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPED); + OnDeactivate(); blog(LOG_INFO, STREAMING_STOP); @@ -3754,6 +3800,9 @@ void OBSBasic::StartRecording() if (outputHandler->RecordingActive()) return; + if (api) + api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTING); + SaveProject(); outputHandler->StartRecording(); } @@ -3762,6 +3811,9 @@ void OBSBasic::RecordStopping() { ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); sysTrayRecord->setText(ui->recordButton->text()); + + if (api) + api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING); } void OBSBasic::StopRecording() @@ -3780,6 +3832,9 @@ void OBSBasic::RecordingStart() ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); sysTrayRecord->setText(ui->recordButton->text()); + if (api) + api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); + OnActivate(); blog(LOG_INFO, RECORDING_START); @@ -3820,6 +3875,9 @@ void OBSBasic::RecordingStop(int code) QSystemTrayIcon::Warning); } + if (api) + api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPED); + OnDeactivate(); } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index a3376df2e72d0b..426f0d1b599c7c 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -30,6 +30,8 @@ #include "window-basic-adv-audio.hpp" #include "window-basic-filters.hpp" +#include + #include #include #include @@ -83,6 +85,7 @@ class OBSBasic : public OBSMainWindow { friend class OBSBasicPreview; friend class OBSBasicStatusBar; friend class OBSBasicSourceSelect; + friend struct OBSStudioAPI; enum class MoveDir { Up, @@ -92,6 +95,8 @@ class OBSBasic : public OBSMainWindow { }; private: + obs_frontend_callbacks *api = nullptr; + std::vector volumes; std::vector signalHandlers; From 988bbc60808412aacfb4eef342e0bedfa2ea3c1e Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 1 Sep 2016 00:39:37 -0700 Subject: [PATCH 035/107] libobs: Add obs_module_get_string helper function Allows getting a translated string for the current module, returning false if no string available. --- libobs/obs-module.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libobs/obs-module.h b/libobs/obs-module.h index 5d446db68235c0..aa8c1d7bb0981e 100644 --- a/libobs/obs-module.h +++ b/libobs/obs-module.h @@ -112,6 +112,10 @@ MODULE_EXPORT void obs_module_free_locale(void); text_lookup_getstr(obs_module_lookup, val, &out); \ return out; \ } \ + bool obs_module_get_string(const char *val, const char **out) \ + { \ + return text_lookup_getstr(obs_module_lookup, val, out); \ + } \ void obs_module_set_locale(const char *locale) \ { \ if (obs_module_lookup) text_lookup_destroy(obs_module_lookup); \ @@ -127,6 +131,11 @@ MODULE_EXPORT void obs_module_free_locale(void); /** Helper function for looking up locale if default locale handler was used */ MODULE_EXTERN const char *obs_module_text(const char *lookup_string); +/** Helper function for looking up locale if default locale handler was used, + * returns true if text found, otherwise false */ +MODULE_EXTERN bool obs_module_get_string(const char *lookup_string, + const char **translated_string); + /** Helper function that returns the current module */ MODULE_EXTERN obs_module_t *obs_current_module(void); From 39592ff5ebb9aa641ae460b7c044e3fc1cc0196f Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sat, 3 Sep 2016 09:15:32 -0700 Subject: [PATCH 036/107] frontend-tools: Add scene switcher plugin --- UI/CMakeLists.txt | 2 + UI/frontend-plugins/CMakeLists.txt | 3 + .../frontend-tools/CMakeLists.txt | 46 ++ .../frontend-tools/auto-scene-switcher-osx.mm | 43 ++ .../auto-scene-switcher-win.cpp | 69 ++ .../frontend-tools/auto-scene-switcher.cpp | 588 ++++++++++++++++++ .../frontend-tools/auto-scene-switcher.hpp | 47 ++ .../frontend-tools/data/locale/en-US.ini | 11 + .../forms/auto-scene-switcher.ui | 310 +++++++++ .../frontend-tools/frontend-tools.c | 18 + 10 files changed, 1137 insertions(+) create mode 100644 UI/frontend-plugins/CMakeLists.txt create mode 100644 UI/frontend-plugins/frontend-tools/CMakeLists.txt create mode 100644 UI/frontend-plugins/frontend-tools/auto-scene-switcher-osx.mm create mode 100644 UI/frontend-plugins/frontend-tools/auto-scene-switcher-win.cpp create mode 100644 UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp create mode 100644 UI/frontend-plugins/frontend-tools/auto-scene-switcher.hpp create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/en-US.ini create mode 100644 UI/frontend-plugins/frontend-tools/forms/auto-scene-switcher.ui create mode 100644 UI/frontend-plugins/frontend-tools/frontend-tools.c diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index e66a26d35a6923..1a415345f96d24 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -239,3 +239,5 @@ if (UNIX AND UNIX_STRUCTURE AND NOT APPLE) install(FILES forms/images/obs.png DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/icons/hicolor/256x256/apps) endif() + +add_subdirectory(frontend-plugins) diff --git a/UI/frontend-plugins/CMakeLists.txt b/UI/frontend-plugins/CMakeLists.txt new file mode 100644 index 00000000000000..0a90a60a0a0d32 --- /dev/null +++ b/UI/frontend-plugins/CMakeLists.txt @@ -0,0 +1,3 @@ +if(WIN32 OR APPLE) + add_subdirectory(frontend-tools) +endif() diff --git a/UI/frontend-plugins/frontend-tools/CMakeLists.txt b/UI/frontend-plugins/frontend-tools/CMakeLists.txt new file mode 100644 index 00000000000000..b15dc5256e9580 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/CMakeLists.txt @@ -0,0 +1,46 @@ +project(frontend-tools) + +if(APPLE) + find_library(COCOA Cocoa) + include_directories(${COCOA}) +endif() + +set(frontend-tools_HEADERS + auto-scene-switcher.hpp + ) +set(frontend-tools_SOURCES + frontend-tools.c + auto-scene-switcher.cpp + ) +set(frontend-tools_UI + forms/auto-scene-switcher.ui + ) + +if(WIN32) + set(frontend-tools_PLATFORM_SOURCES + auto-scene-switcher-win.cpp) +elseif(APPLE) + set(frontend-tools_PLATFORM_SOURCES + auto-scene-switcher-osx.mm) + set_source_files_properties(auto-scene-switcher-osx.mm + PROPERTIES COMPILE_FLAGS "-fobjc-arc") + + set(frontend-tools_PLATFORM_LIBS + ${COCOA}) +endif() + +qt5_wrap_ui(frontend-tools_UI_HEADERS ${frontend-tools_UI}) + +add_library(frontend-tools MODULE + ${frontend-tools_HEADERS} + ${frontend-tools_SOURCES} + ${frontend-tools_PLATFORM_SOURCES} + ${frontend-tools_UI_HEADERS} + ) +target_link_libraries(frontend-tools + ${frontend-tools_PLATFORM_LIBS} + obs-frontend-api + Qt5::Widgets + libobs) + +install_obs_plugin_with_data(frontend-tools data) diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-osx.mm b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-osx.mm new file mode 100644 index 00000000000000..92dc8b757b2bd5 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-osx.mm @@ -0,0 +1,43 @@ +#import +#include +#include "auto-scene-switcher.hpp" + +using namespace std; + +void GetWindowList(vector &windows) +{ + windows.resize(0); + + @autoreleasepool { + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + NSArray *array = [ws runningApplications]; + for (NSRunningApplication *app in array) { + NSString *name = app.localizedName; + if (!name) + continue; + + const char *str = name.UTF8String; + if (str && *str) + windows.emplace_back(str); + } + } +} + +void GetCurrentWindowTitle(string &title) +{ + title.resize(0); + + @autoreleasepool { + NSWorkspace *ws = [NSWorkspace sharedWorkspace]; + NSRunningApplication *app = [ws frontmostApplication]; + if (app) { + NSString *name = app.localizedName; + if (!name) + return; + + const char *str = name.UTF8String; + if (str && *str) + title = str; + } + } +} diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher-win.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-win.cpp new file mode 100644 index 00000000000000..604504034aafe3 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher-win.cpp @@ -0,0 +1,69 @@ +#include +#include +#include "auto-scene-switcher.hpp" + +using namespace std; + +static bool GetWindowTitle(HWND window, string &title) +{ + size_t len = (size_t)GetWindowTextLengthW(window); + wstring wtitle; + + wtitle.resize(len); + if (!GetWindowTextW(window, &wtitle[0], (int)len + 1)) + return false; + + len = os_wcs_to_utf8(wtitle.c_str(), 0, nullptr, 0); + title.resize(len); + os_wcs_to_utf8(wtitle.c_str(), 0, &title[0], len + 1); + return true; +} + +static bool WindowValid(HWND window) +{ + LONG_PTR styles, ex_styles; + RECT rect; + DWORD id; + + if (!IsWindowVisible(window)) + return false; + GetWindowThreadProcessId(window, &id); + if (id == GetCurrentProcessId()) + return false; + + GetClientRect(window, &rect); + styles = GetWindowLongPtr(window, GWL_STYLE); + ex_styles = GetWindowLongPtr(window, GWL_EXSTYLE); + + if (ex_styles & WS_EX_TOOLWINDOW) + return false; + if (styles & WS_CHILD) + return false; + + return true; +} + +void GetWindowList(vector &windows) +{ + HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); + + while (window) { + string title; + if (WindowValid(window) && GetWindowTitle(window, title)) + windows.emplace_back(title); + window = GetNextWindow(window, GW_HWNDNEXT); + } +} + +void GetCurrentWindowTitle(string &title) +{ + HWND window = GetForegroundWindow(); + DWORD id; + + GetWindowThreadProcessId(window, &id); + if (id == GetCurrentProcessId()) { + title = ""; + return; + } + GetWindowTitle(window, title); +} diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp new file mode 100644 index 00000000000000..9c737bf4a7b014 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp @@ -0,0 +1,588 @@ +#include +#include +#include +#include +#include +#include +#include "auto-scene-switcher.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define DEFAULT_INTERVAL 300 + +struct SceneSwitch { + OBSWeakSource scene; + string window; + regex re; + + inline SceneSwitch(OBSWeakSource scene_, const char *window_) + : scene(scene_), window(window_), re(window_) + { + } +}; + +static inline bool WeakSourceValid(obs_weak_source_t *ws) +{ + obs_source_t *source = obs_weak_source_get_source(ws); + if (source) + obs_source_release(source); + return !!source; +} + +struct SwitcherData { + thread th; + condition_variable cv; + mutex m; + bool stop = false; + + vector switches; + OBSWeakSource nonMatchingScene; + int interval = DEFAULT_INTERVAL; + bool switchIfNotMatching = false; + bool startAtLaunch = false; + + void Thread(); + void Start(); + void Stop(); + + void Prune() + { + for (size_t i = 0; i < switches.size(); i++) { + SceneSwitch &s = switches[i]; + if (!WeakSourceValid(s.scene)) + switches.erase(switches.begin() + i--); + } + + if (nonMatchingScene && !WeakSourceValid(nonMatchingScene)) { + switchIfNotMatching = false; + nonMatchingScene = nullptr; + } + } + + inline ~SwitcherData() + { + Stop(); + } +}; + +static SwitcherData *switcher = nullptr; + +static inline QString MakeSwitchName(const QString &scene, + const QString &window) +{ + 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) +{ + ui->setupUi(this); + + lock_guard lock(switcher->m); + + switcher->Prune(); + + BPtr scenes = obs_frontend_get_scene_names(); + char **temp = scenes; + while (*temp) { + const char *name = *temp; + ui->scenes->addItem(name); + ui->noMatchSwitchScene->addItem(name); + temp++; + } + + if (switcher->switchIfNotMatching) + ui->noMatchSwitch->setChecked(true); + else + ui->noMatchDontSwitch->setChecked(true); + + ui->noMatchSwitchScene->setCurrentText( + GetWeakSourceName(switcher->nonMatchingScene).c_str()); + ui->checkInterval->setValue(switcher->interval); + + vector windows; + GetWindowList(windows); + + for (string &window : windows) + ui->windows->addItem(window.c_str()); + + for (auto &s : switcher->switches) { + string sceneName = GetWeakSourceName(s.scene); + QString text = MakeSwitchName(sceneName.c_str(), + s.window.c_str()); + + QListWidgetItem *item = new QListWidgetItem(text, + ui->switches); + item->setData(Qt::UserRole, s.window.c_str()); + } + + if (switcher->th.joinable()) + SetStarted(); + else + SetStopped(); + + loading = false; +} + +void SceneSwitcher::closeEvent(QCloseEvent*) +{ + obs_frontend_save(); +} + +int SceneSwitcher::FindByData(const QString &window) +{ + int count = ui->switches->count(); + int idx = -1; + + for (int i = 0; i < count; i++) { + QListWidgetItem *item = ui->switches->item(i); + QString itemWindow = + item->data(Qt::UserRole).toString(); + + if (itemWindow == window) { + idx = i; + break; + } + } + + return idx; +} + +void SceneSwitcher::on_switches_currentRowChanged(int idx) +{ + if (loading) + return; + if (idx == -1) + return; + + QListWidgetItem *item = ui->switches->item(idx); + + QString window = item->data(Qt::UserRole).toString(); + + lock_guard lock(switcher->m); + for (auto &s : switcher->switches) { + if (window.compare(s.window.c_str()) == 0) { + string name = GetWeakSourceName(s.scene); + ui->scenes->setCurrentText(name.c_str()); + ui->windows->setCurrentText(window); + break; + } + } +} + +void SceneSwitcher::on_close_clicked() +{ + done(0); +} + +void SceneSwitcher::on_add_clicked() +{ + QString sceneName = ui->scenes->currentText(); + QString windowName = ui->windows->currentText(); + + if (windowName.isEmpty()) + return; + + OBSWeakSource source = GetWeakSourceByQString(sceneName); + QVariant v = QVariant::fromValue(windowName); + + QString text = MakeSwitchName(sceneName, windowName); + + int idx = FindByData(windowName); + + if (idx == -1) { + QListWidgetItem *item = new QListWidgetItem(text, + ui->switches); + item->setData(Qt::UserRole, v); + + lock_guard lock(switcher->m); + switcher->switches.emplace_back(source, + windowName.toUtf8().constData()); + } else { + QListWidgetItem *item = ui->switches->item(idx); + item->setText(text); + + string window = windowName.toUtf8().constData(); + + { + lock_guard lock(switcher->m); + for (auto &s : switcher->switches) { + if (s.window == window) { + s.scene = source; + break; + } + } + } + + ui->switches->sortItems(); + } +} + +void SceneSwitcher::on_remove_clicked() +{ + QListWidgetItem *item = ui->switches->currentItem(); + if (!item) + return; + + string window = + item->data(Qt::UserRole).toString().toUtf8().constData(); + + { + lock_guard lock(switcher->m); + auto &switches = switcher->switches; + + for (auto it = switches.begin(); it != switches.end(); ++it) { + auto &s = *it; + + if (s.window == window) { + switches.erase(it); + break; + } + } + } + + delete item; +} + +void SceneSwitcher::on_startAtLaunch_toggled(bool value) +{ + if (loading) + return; + + lock_guard lock(switcher->m); + switcher->startAtLaunch = value; +} + +void SceneSwitcher::UpdateNonMatchingScene(const QString &name) +{ + obs_source_t *scene = obs_get_source_by_name( + name.toUtf8().constData()); + obs_weak_source_t *ws = obs_source_get_weak_source(scene); + + switcher->nonMatchingScene = ws; + + obs_weak_source_release(ws); + obs_source_release(scene); +} + +void SceneSwitcher::on_noMatchDontSwitch_clicked() +{ + if (loading) + return; + + lock_guard lock(switcher->m); + switcher->switchIfNotMatching = false; +} + +void SceneSwitcher::on_noMatchSwitch_clicked() +{ + if (loading) + return; + + lock_guard lock(switcher->m); + switcher->switchIfNotMatching = true; + UpdateNonMatchingScene(ui->noMatchSwitchScene->currentText()); +} + +void SceneSwitcher::on_noMatchSwitchScene_currentTextChanged( + const QString &text) +{ + if (loading) + return; + + lock_guard lock(switcher->m); + UpdateNonMatchingScene(text); +} + +void SceneSwitcher::on_checkInterval_valueChanged(int value) +{ + if (loading) + return; + + lock_guard lock(switcher->m); + switcher->interval = value; +} + +void SceneSwitcher::SetStarted() +{ + ui->toggleStartButton->setText(obs_module_text("Stop")); + ui->pluginRunningText->setText(obs_module_text("Active")); +} + +void SceneSwitcher::SetStopped() +{ + ui->toggleStartButton->setText(obs_module_text("Start")); + ui->pluginRunningText->setText(obs_module_text("Inactive")); +} + +void SceneSwitcher::on_toggleStartButton_clicked() +{ + if (switcher->th.joinable()) { + switcher->Stop(); + SetStopped(); + } else { + switcher->Start(); + SetStarted(); + } +} + +static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) +{ + if (saving) { + lock_guard lock(switcher->m); + obs_data_t *obj = obs_data_create(); + obs_data_array_t *array = obs_data_array_create(); + + switcher->Prune(); + + for (SceneSwitch &s : switcher->switches) { + obs_data_t *array_obj = obs_data_create(); + + obs_source_t *source = obs_weak_source_get_source( + s.scene); + if (source) { + const char *n = obs_source_get_name(source); + obs_data_set_string(array_obj, "scene", n); + obs_data_set_string(array_obj, "window_title", + s.window.c_str()); + obs_data_array_push_back(array, array_obj); + obs_source_release(source); + } + + obs_data_release(array_obj); + } + + string nonMatchingSceneName = + GetWeakSourceName(switcher->nonMatchingScene); + + obs_data_set_int(obj, "interval", switcher->interval); + obs_data_set_string(obj, "non_matching_scene", + nonMatchingSceneName.c_str()); + obs_data_set_bool(obj, "switch_if_not_matching", + switcher->switchIfNotMatching); + obs_data_set_bool(obj, "active", switcher->th.joinable()); + obs_data_set_array(obj, "switches", array); + + obs_data_set_obj(save_data, "auto-scene-switcher", obj); + + obs_data_array_release(array); + obs_data_release(obj); + } else { + switcher->m.lock(); + + obs_data_t *obj = obs_data_get_obj(save_data, + "auto-scene-switcher"); + obs_data_array_t *array = obs_data_get_array(obj, "switches"); + size_t count = obs_data_array_count(array); + + if (!obj) + obj = obs_data_create(); + + obs_data_set_default_int(obj, "interval", DEFAULT_INTERVAL); + + switcher->interval = obs_data_get_int(obj, "interval"); + switcher->switchIfNotMatching = + obs_data_get_bool(obj, "switch_if_not_matching"); + string nonMatchingScene = + obs_data_get_string(obj, "non_matching_scene"); + bool active = obs_data_get_bool(obj, "active"); + + switcher->nonMatchingScene = + GetWeakSourceByName(nonMatchingScene.c_str()); + + switcher->switches.clear(); + + for (size_t i = 0; i < count; i++) { + obs_data_t *array_obj = obs_data_array_item(array, i); + + const char *scene = + obs_data_get_string(array_obj, "scene"); + const char *window = + obs_data_get_string(array_obj, "window_title"); + + switcher->switches.emplace_back( + GetWeakSourceByName(scene), + window); + + obs_data_release(array_obj); + } + + obs_data_array_release(array); + obs_data_release(obj); + + switcher->m.unlock(); + + if (active) + switcher->Start(); + else + switcher->Stop(); + } +} + +void SwitcherData::Thread() +{ + chrono::duration duration = + chrono::milliseconds(interval); + string lastTitle; + string title; + + for (;;) { + unique_lock lock(m); + OBSWeakSource scene; + bool match = false; + + cv.wait_for(lock, duration); + if (switcher->stop) { + switcher->stop = false; + break; + } + + duration = chrono::milliseconds(interval); + + GetCurrentWindowTitle(title); + + if (lastTitle != title) { + switcher->Prune(); + + for (SceneSwitch &s : switches) { + if (s.window == title) { + match = true; + scene = s.scene; + break; + } + } + + /* try regex */ + if (!match) { + for (SceneSwitch &s : switches) { + try { + bool matches = regex_match( + title, s.re); + if (matches) { + match = true; + scene = s.scene; + break; + } + } catch (const regex_error &) {} + } + } + + if (!match && switchIfNotMatching && + nonMatchingScene) { + match = true; + scene = nonMatchingScene; + } + + if (match) { + obs_source_t *source = + obs_weak_source_get_source(scene); + obs_source_t *currentSource = + obs_frontend_get_current_scene(); + + if (source && source != currentSource) + obs_frontend_set_current_scene(source); + + obs_source_release(currentSource); + obs_source_release(source); + } + } + + lastTitle = title; + } +} + +void SwitcherData::Start() +{ + if (!switcher->th.joinable()) + switcher->th = thread([] () {switcher->Thread();}); +} + +void SwitcherData::Stop() +{ + if (th.joinable()) { + { + lock_guard lock(m); + stop = true; + } + cv.notify_one(); + th.join(); + } +} + +extern "C" void FreeSceneSwitcher() +{ + delete switcher; + switcher = nullptr; +} + +static void OBSEvent(enum obs_frontend_event event, void *) +{ + if (event == OBS_FRONTEND_EVENT_EXIT) + FreeSceneSwitcher(); +} + +extern "C" void InitSceneSwitcher() +{ + QAction *action = (QAction*)obs_frontend_add_tools_menu_qaction( + obs_module_text("SceneSwitcher")); + + switcher = new SwitcherData; + + auto cb = [] () + { + obs_frontend_push_ui_translation(obs_module_get_string); + + QMainWindow *window = + (QMainWindow*)obs_frontend_get_main_window(); + + SceneSwitcher ss(window); + ss.exec(); + + obs_frontend_pop_ui_translation(); + }; + + obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr); + obs_frontend_add_event_callback(OBSEvent, nullptr); + + action->connect(action, &QAction::triggered, cb); +} diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.hpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.hpp new file mode 100644 index 00000000000000..4a155ac9ca77e0 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +#include "ui_auto-scene-switcher.h" + +struct obs_weak_source; +typedef struct obs_weak_source obs_weak_source_t; + +class QCloseEvent; + +class SceneSwitcher : public QDialog { + Q_OBJECT + +public: + std::unique_ptr ui; + bool loading = true; + + SceneSwitcher(QWidget *parent); + + void closeEvent(QCloseEvent *event) override; + + void SetStarted(); + void SetStopped(); + + int FindByData(const QString &window); + + void UpdateNonMatchingScene(const QString &name); + +public slots: + void on_switches_currentRowChanged(int idx); + void on_close_clicked(); + void on_add_clicked(); + void on_remove_clicked(); + void on_noMatchDontSwitch_clicked(); + void on_noMatchSwitch_clicked(); + void on_startAtLaunch_toggled(bool value); + void on_noMatchSwitchScene_currentTextChanged(const QString &text); + void on_checkInterval_valueChanged(int value); + void on_toggleStartButton_clicked(); +}; + +void GetWindowList(std::vector &windows); +void GetCurrentWindowTitle(std::string &title); diff --git a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini new file mode 100644 index 00000000000000..0bf3532cb010a0 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatic Scene Switcher" +SceneSwitcher.OnNoMatch="When no window matches:" +SceneSwitcher.OnNoMatch.DontSwitch="Don't switch" +SceneSwitcher.OnNoMatch.SwitchTo="Switch to:" +SceneSwitcher.CheckInterval="Check active window title every:" +SceneSwitcher.StartAtLaunch="Automatically start for this scene collection" +SceneSwitcher.ActiveOrNotActive="Scene Switcher is:" +Active="Active" +Inactive="Inactive" +Start="Start" +Stop="Stop" diff --git a/UI/frontend-plugins/frontend-tools/forms/auto-scene-switcher.ui b/UI/frontend-plugins/frontend-tools/forms/auto-scene-switcher.ui new file mode 100644 index 00000000000000..ea8b51edfdb4c9 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/forms/auto-scene-switcher.ui @@ -0,0 +1,310 @@ + + + SceneSwitcher + + + + 0 + 0 + 743 + 563 + + + + SceneSwitcher + + + + + + + + true + + + 20 + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + + + + + + + 0 + 0 + + + + true + + + + + + + 4 + + + + + + 22 + 22 + + + + true + + + addIconSmall + + + + + + + + 22 + 22 + + + + true + + + removeIconSmall + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + 0 + 0 + + + + SceneSwitcher.OnNoMatch + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + SceneSwitcher.OnNoMatch.DontSwitch + + + true + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + SceneSwitcher.OnNoMatch.SwitchTo + + + + + + + false + + + + 100 + 0 + + + + + + + + + + + + SceneSwitcher.CheckInterval + + + checkInterval + + + + + + + + 100 + 0 + + + + ms + + + 50 + + + 20000 + + + 300 + + + + + + + SceneSwitcher.ActiveOrNotActive + + + + + + + Qt::Horizontal + + + + 200 + 20 + + + + + + + + Start + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 40 + + + + + + + + Not Active + + + + + + + + + Close + + + + + + + + + noMatchSwitch + toggled(bool) + noMatchSwitchScene + setEnabled(bool) + + + 286 + 347 + + + 483 + 352 + + + + + diff --git a/UI/frontend-plugins/frontend-tools/frontend-tools.c b/UI/frontend-plugins/frontend-tools/frontend-tools.c new file mode 100644 index 00000000000000..3826f041f3d4ff --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/frontend-tools.c @@ -0,0 +1,18 @@ +#include + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("frontend-tools", "en-US") + +void InitSceneSwitcher(); +void FreeSceneSwitcher(); + +bool obs_module_load(void) +{ + InitSceneSwitcher(); + return true; +} + +void obs_module_unload(void) +{ + FreeSceneSwitcher(); +} From be89ef871201446054356aa92e265de3dafbb26d Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 7 Sep 2016 06:01:42 -0700 Subject: [PATCH 037/107] frontend-tools: Remove unused locale --- UI/frontend-plugins/frontend-tools/data/locale/en-US.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini index 0bf3532cb010a0..63a4673f090713 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini @@ -3,7 +3,6 @@ SceneSwitcher.OnNoMatch="When no window matches:" SceneSwitcher.OnNoMatch.DontSwitch="Don't switch" SceneSwitcher.OnNoMatch.SwitchTo="Switch to:" SceneSwitcher.CheckInterval="Check active window title every:" -SceneSwitcher.StartAtLaunch="Automatically start for this scene collection" SceneSwitcher.ActiveOrNotActive="Scene Switcher is:" Active="Active" Inactive="Inactive" From 007d39e296f0fa2dce0b8471462bdf41f1cd7cba Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Wed, 7 Sep 2016 23:42:27 +0200 Subject: [PATCH 038/107] UI/installer: Miscellaneous installer improvements - Checks for previous instances by using the FindProcDLL NSIS plugin - No longer allows ignoring if there is a sharing violation. - Added version information section to allow friendly description in the UAC elevation prompt. - Kill lingering instances of cef-bootstrap before installing. --- UI/installer/mp-installer.nsi | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/UI/installer/mp-installer.nsi b/UI/installer/mp-installer.nsi index 2a9146ff382d75..28c2229fc68b21 100644 --- a/UI/installer/mp-installer.nsi +++ b/UI/installer/mp-installer.nsi @@ -156,12 +156,18 @@ Function PreReqCheck ClearErrors ; Check previous instance - ; System::Call 'kernel32::OpenMutexW(i 0x100000, b 0, w "OBSMutex") i .R0' - ; IntCmp $R0 0 notRunning - ; System::Call 'kernel32::CloseHandle(i $R0)' - ; MessageBox MB_OK|MB_ICONEXCLAMATION "${APPNAME} is already running. Please close it first before installing a new version." /SD IDOK - ; Quit -notRunning: + FindProcDLL::FindProc "obs32.exe" + IntCmp $R0 1 0 notRunning1 + MessageBox MB_OK|MB_ICONEXCLAMATION "${APPNAME} is already running. Please close it first before installing a new version." /SD IDOK + Quit + notRunning1: + ${if} ${RunningX64} + FindProcDLL::FindProc "obs64.exe" + IntCmp $R0 1 0 notRunning2 + MessageBox MB_OK|MB_ICONEXCLAMATION "${APPNAME} is already running. Please close it first before installing a new version." /SD IDOK + Quit + ${endif} + notRunning2: FunctionEnd @@ -175,6 +181,10 @@ Section "OBS Studio" Section1 ; Set Section properties SetOverwrite on + AllowSkipFiles off + + FindProcDLL::KillProc "obs-plugins\32bit\cef-bootstrap.exe" + FindProcDLL::KillProc "obs-plugins\64bit\cef-bootstrap.exe" SetShellVarContext all @@ -283,4 +293,13 @@ SectionEnd !insertmacro MUI_DESCRIPTION_TEXT ${Section2} "Removes all settings, plugins, scenes and sources, profiles, log files and other application data." !insertmacro MUI_UNFUNCTION_DESCRIPTION_END +; Version information +VIProductVersion "0.${APPVERSION}" +VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "OBS Studio" +VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "obsproject.com" +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "(c) 2012-2016" +; FileDescription is what shows in the UAC elevation prompt when signed +VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "OBS Studio" +VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "1.0" + ; eof From bc3944301f4453a82e44c264de01886659082f0c Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 9 Sep 2016 04:45:27 -0700 Subject: [PATCH 039/107] libobs: Allow forced stop even when already stopping Allow outputs to force a stop even when already in the process of stopping. If for example a stream is heavily congested and taking a very long time to stop, this allows frontends to give the users the option to forcibly stop the stream to make it stop as quickly as possible. --- libobs/obs-output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libobs/obs-output.c b/libobs/obs-output.c index 21f6745507ed76..db84cd6c2c5e21 100644 --- a/libobs/obs-output.c +++ b/libobs/obs-output.c @@ -322,7 +322,7 @@ void obs_output_actual_stop(obs_output_t *output, bool force, uint64_t ts) bool call_stop = true; bool was_reconnecting = false; - if (stopping(output)) + if (stopping(output) && !force) return; os_event_reset(output->stopping_event); @@ -391,8 +391,8 @@ void obs_output_force_stop(obs_output_t *output) if (!stopping(output)) { output->stop_code = 0; do_output_signal(output, "stopping"); - obs_output_actual_stop(output, true, 0); } + obs_output_actual_stop(output, true, 0); } bool obs_output_active(const obs_output_t *output) From 9a58772a013209266e719eea6917608f4ab7463b Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 9 Sep 2016 04:48:12 -0700 Subject: [PATCH 040/107] obs-ffmpeg: Allow ffmpeg_mux forced stop even when stopping --- plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index 65212bcb65fef1..1c45d672885465 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -274,7 +274,7 @@ static void ffmpeg_mux_stop(void *data, uint64_t ts) { struct ffmpeg_muxer *stream = data; - if (capturing(stream)) { + if (capturing(stream) || ts == 0) { stream->stop_ts = (int64_t)ts / 1000LL; os_atomic_set_bool(&stream->stopping, true); os_atomic_set_bool(&stream->capturing, false); From 50d7cc8ae61fb8a46b4ad7efaa67c297fddced66 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 9 Sep 2016 04:48:55 -0700 Subject: [PATCH 041/107] obs-outputs: Allow forced stop even when stopping --- plugins/obs-outputs/rtmp-stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 99e59681ef053b..1d10f6129414ec 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -225,7 +225,7 @@ static void rtmp_stream_stop(void *data, uint64_t ts) { struct rtmp_stream *stream = data; - if (stopping(stream)) + if (stopping(stream) && ts != 0) return; if (connecting(stream)) From 5c5d0ba9e5055ea4fc961b06d03263ed10920a96 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 9 Sep 2016 07:37:54 -0700 Subject: [PATCH 042/107] UI: Add ability to forcibly stop streams/recordings If the user hits the "stop stream" button it'll transition in to "stopping stream..." and will continue to output until the stream reaches the timing in which "stop" was pressed. However, if there is significant congestion, stopping the stream can take far longer than the user may like. So there needs to be an option to forcibly stop the stream in that case; pushing the "stop" button a second time should allow the user to tell the stream/recording to stop right away instead of waiting for the precise stop timing. --- UI/window-basic-main-outputs.cpp | 48 ++++++++++++++++---------------- UI/window-basic-main-outputs.hpp | 5 ++-- UI/window-basic-main.cpp | 10 +++++-- UI/window-basic-main.hpp | 2 ++ 4 files changed, 35 insertions(+), 30 deletions(-) diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 2415d75d684fd4..9a864df0a8e9ed 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -179,9 +179,8 @@ struct SimpleOutput : BasicOutputHandler { virtual bool StartStreaming(obs_service_t *service) override; virtual bool StartRecording() override; - virtual void StopStreaming() override; - virtual void ForceStopStreaming() override; - virtual void StopRecording() override; + virtual void StopStreaming(bool force) override; + virtual void StopRecording(bool force) override; virtual bool StreamingActive() const override; virtual bool RecordingActive() const override; }; @@ -650,19 +649,20 @@ bool SimpleOutput::StartRecording() return false; } -void SimpleOutput::StopStreaming() +void SimpleOutput::StopStreaming(bool force) { - obs_output_stop(streamOutput); -} - -void SimpleOutput::ForceStopStreaming() -{ - obs_output_force_stop(streamOutput); + if (force) + obs_output_force_stop(streamOutput); + else + obs_output_stop(streamOutput); } -void SimpleOutput::StopRecording() +void SimpleOutput::StopRecording(bool force) { - obs_output_stop(fileOutput); + if (force) + obs_output_force_stop(fileOutput); + else + obs_output_stop(fileOutput); } bool SimpleOutput::StreamingActive() const @@ -703,9 +703,8 @@ struct AdvancedOutput : BasicOutputHandler { virtual bool StartStreaming(obs_service_t *service) override; virtual bool StartRecording() override; - virtual void StopStreaming() override; - virtual void ForceStopStreaming() override; - virtual void StopRecording() override; + virtual void StopStreaming(bool force) override; + virtual void StopRecording(bool force) override; virtual bool StreamingActive() const override; virtual bool RecordingActive() const override; }; @@ -1193,19 +1192,20 @@ bool AdvancedOutput::StartRecording() return false; } -void AdvancedOutput::StopStreaming() +void AdvancedOutput::StopStreaming(bool force) { - obs_output_stop(streamOutput); -} - -void AdvancedOutput::ForceStopStreaming() -{ - obs_output_force_stop(streamOutput); + if (force) + obs_output_force_stop(streamOutput); + else + obs_output_stop(streamOutput); } -void AdvancedOutput::StopRecording() +void AdvancedOutput::StopRecording(bool force) { - obs_output_stop(fileOutput); + if (force) + obs_output_force_stop(fileOutput); + else + obs_output_stop(fileOutput); } bool AdvancedOutput::StreamingActive() const diff --git a/UI/window-basic-main-outputs.hpp b/UI/window-basic-main-outputs.hpp index 186d3b4da0bece..74a73a60e4a9aa 100644 --- a/UI/window-basic-main-outputs.hpp +++ b/UI/window-basic-main-outputs.hpp @@ -24,9 +24,8 @@ struct BasicOutputHandler { virtual bool StartStreaming(obs_service_t *service) = 0; virtual bool StartRecording() = 0; - virtual void StopStreaming() = 0; - virtual void ForceStopStreaming() = 0; - virtual void StopRecording() = 0; + virtual void StopStreaming(bool force = false) = 0; + virtual void StopRecording(bool force = false) = 0; virtual bool StreamingActive() const = 0; virtual bool RecordingActive() const = 0; diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index eae28ffc9cab3c..e4d46492ecf0fe 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3642,7 +3642,7 @@ void OBSBasic::StopStreaming() SaveProject(); if (outputHandler->StreamingActive()) - outputHandler->StopStreaming(); + outputHandler->StopStreaming(streamingStopping); OnDeactivate(); @@ -3659,7 +3659,7 @@ void OBSBasic::ForceStopStreaming() SaveProject(); if (outputHandler->StreamingActive()) - outputHandler->ForceStopStreaming(); + outputHandler->StopStreaming(true); OnDeactivate(); @@ -3734,6 +3734,7 @@ void OBSBasic::StreamStopping() ui->streamButton->setText(QTStr("Basic.Main.StoppingStreaming")); sysTrayStream->setText(ui->streamButton->text()); + streamingStopping = true; if (api) api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPING); } @@ -3773,6 +3774,7 @@ void OBSBasic::StreamingStop(int code) sysTrayStream->setText(ui->streamButton->text()); sysTrayStream->setEnabled(true); + streamingStopping = false; if (api) api->on_event(OBS_FRONTEND_EVENT_STREAMING_STOPPED); @@ -3812,6 +3814,7 @@ void OBSBasic::RecordStopping() ui->recordButton->setText(QTStr("Basic.Main.StoppingRecording")); sysTrayRecord->setText(ui->recordButton->text()); + recordingStopping = true; if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_STOPPING); } @@ -3821,7 +3824,7 @@ void OBSBasic::StopRecording() SaveProject(); if (outputHandler->RecordingActive()) - outputHandler->StopRecording(); + outputHandler->StopRecording(recordingStopping); OnDeactivate(); } @@ -3832,6 +3835,7 @@ void OBSBasic::RecordingStart() ui->recordButton->setText(QTStr("Basic.Main.StopRecording")); sysTrayRecord->setText(ui->recordButton->text()); + recordingStopping = false; if (api) api->on_event(OBS_FRONTEND_EVENT_RECORDING_STARTED); diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 426f0d1b599c7c..f7a0b8e1967093 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -120,6 +120,8 @@ class OBSBasic : public OBSMainWindow { OBSService service; std::unique_ptr outputHandler; + bool streamingStopping = false; + bool recordingStopping = false; gs_vertbuffer_t *box = nullptr; gs_vertbuffer_t *boxLeft = nullptr; From 2b4bf21416a9c00403c02cbd90873560eab0283a Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 11 Sep 2016 03:17:18 -0700 Subject: [PATCH 043/107] UI: Add tooltips (long description) to property controls --- UI/properties-view.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index 14cd3ff4e0cf29..f9b08175039590 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -210,9 +210,13 @@ void OBSPropertiesView::resizeEvent(QResizeEvent *event) QWidget *OBSPropertiesView::NewWidget(obs_property_t *prop, QWidget *widget, const char *signal) { + const char *long_desc = obs_property_long_description(prop); + WidgetInfo *info = new WidgetInfo(this, prop, widget); connect(widget, signal, info, SLOT(ControlChanged())); children.emplace_back(info); + + widget->setToolTip(QT_UTF8(long_desc)); return widget; } @@ -263,6 +267,8 @@ QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, label = new QLabel(QT_UTF8(obs_property_description(prop))); layout->addRow(label, subLayout); + edit->setToolTip(QT_UTF8(obs_property_long_description(prop))); + connect(edit, SIGNAL(textEdited(const QString &)), info, SLOT(ControlChanged())); return nullptr; @@ -271,6 +277,7 @@ QWidget *OBSPropertiesView::AddText(obs_property_t *prop, QFormLayout *layout, QLineEdit *edit = new QLineEdit(); edit->setText(QT_UTF8(val)); + edit->setToolTip(QT_UTF8(obs_property_long_description(prop))); return NewWidget(prop, edit, SIGNAL(textEdited(const QString &))); } @@ -286,6 +293,7 @@ void OBSPropertiesView::AddPath(obs_property_t *prop, QFormLayout *layout, edit->setText(QT_UTF8(val)); edit->setReadOnly(true); + edit->setToolTip(QT_UTF8(obs_property_long_description(prop))); subLayout->addWidget(edit); subLayout->addWidget(button); @@ -316,6 +324,7 @@ void OBSPropertiesView::AddInt(obs_property_t *prop, QFormLayout *layout, spin->setMaximum(maxVal); spin->setSingleStep(stepVal); spin->setValue(val); + spin->setToolTip(QT_UTF8(obs_property_long_description(prop))); WidgetInfo *info = new WidgetInfo(this, prop, spin); children.emplace_back(info); @@ -361,6 +370,7 @@ void OBSPropertiesView::AddFloat(obs_property_t *prop, QFormLayout *layout, spin->setMaximum(maxVal); spin->setSingleStep(stepVal); spin->setValue(val); + spin->setToolTip(QT_UTF8(obs_property_long_description(prop))); WidgetInfo *info = new WidgetInfo(this, prop, spin); children.emplace_back(info); @@ -471,6 +481,7 @@ QWidget *OBSPropertiesView::AddList(obs_property_t *prop, bool &warning) combo->setEditable(true); combo->setMaxVisibleItems(40); + combo->setToolTip(QT_UTF8(obs_property_long_description(prop))); string value = from_obs_data(settings, name, format); @@ -545,6 +556,7 @@ void OBSPropertiesView::AddEditableList(obs_property_t *prop, list->setSortingEnabled(false); list->setSelectionMode(QAbstractItemView::ExtendedSelection); + list->setToolTip(QT_UTF8(obs_property_long_description(prop))); for (size_t i = 0; i < count; i++) { obs_data_t *item = obs_data_array_item(array, i); @@ -598,12 +610,14 @@ void OBSPropertiesView::AddColor(obs_property_t *prop, QFormLayout *layout, QColor color = color_from_int(val); button->setText(QTStr("Basic.PropertiesWindow.SelectColor")); + button->setToolTip(QT_UTF8(obs_property_long_description(prop))); colorLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel); colorLabel->setText(color.name(QColor::HexArgb)); colorLabel->setPalette(QPalette(color)); colorLabel->setAutoFillBackground(true); colorLabel->setAlignment(Qt::AlignCenter); + colorLabel->setToolTip(QT_UTF8(obs_property_long_description(prop))); QHBoxLayout *subLayout = new QHBoxLayout; subLayout->setContentsMargins(0, 0, 0, 0); @@ -655,11 +669,13 @@ void OBSPropertiesView::AddFont(obs_property_t *prop, QFormLayout *layout, MakeQFont(font_obj, font); button->setText(QTStr("Basic.PropertiesWindow.SelectFont")); + button->setToolTip(QT_UTF8(obs_property_long_description(prop))); fontLabel->setFrameStyle(QFrame::Sunken | QFrame::Panel); fontLabel->setFont(font); fontLabel->setText(QString("%1 %2").arg(face, style)); fontLabel->setAlignment(Qt::AlignCenter); + fontLabel->setToolTip(QT_UTF8(obs_property_long_description(prop))); QHBoxLayout *subLayout = new QHBoxLayout; subLayout->setContentsMargins(0, 0, 0, 0); @@ -988,6 +1004,8 @@ static OBSFrameRatePropertyWidget *CreateFrameRateWidget(obs_property_t *prop, combo->addItem(QTStr("Basic.PropertiesView.FPS.Rational"), QVariant::fromValue(frame_rate_tag::rational())); + combo->setToolTip(QT_UTF8(obs_property_long_description(prop))); + auto num = obs_property_frame_rate_options_count(prop); if (num) combo->insertSeparator(combo->count()); @@ -1188,6 +1206,8 @@ void OBSPropertiesView::AddFrameRate(obs_property_t *prop, bool &warning, fps_ranges); auto info = new WidgetInfo(this, prop, widget); + widget->setToolTip(QT_UTF8(obs_property_long_description(prop))); + widget->name = name; widget->settings = settings; @@ -1209,6 +1229,9 @@ void OBSPropertiesView::AddFrameRate(obs_property_t *prop, bool &warning, auto stack = widget->modeDisplay; auto combo = widget->modeSelect; + stack->setToolTip(QT_UTF8(obs_property_long_description(prop))); + combo->setToolTip(QT_UTF8(obs_property_long_description(prop))); + auto comboIndexChanged = static_cast( &QComboBox::currentIndexChanged); connect(combo, comboIndexChanged, stack, From 001f3c751b2a93a6ee547b52fc800cc3f8674df0 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 11 Sep 2016 03:15:09 -0700 Subject: [PATCH 044/107] win-dshow: Add tooltip to "Use Buffering" option --- plugins/win-dshow/data/locale/en-US.ini | 1 + plugins/win-dshow/win-dshow.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/plugins/win-dshow/data/locale/en-US.ini b/plugins/win-dshow/data/locale/en-US.ini index dca867eb195a47..81568b9aee8c4a 100644 --- a/plugins/win-dshow/data/locale/en-US.ini +++ b/plugins/win-dshow/data/locale/en-US.ini @@ -25,6 +25,7 @@ AudioOutputMode.WaveOut="Output desktop audio (WaveOut)" UseCustomAudioDevice="Use custom audio device" AudioDevice="Audio Device" Buffering="Buffering" +Buffering.ToolTip="When enabled, buffers video/audio data to ensure the smoothest and most\naccurate playback possible, but at the cost of increased latency. When using\nbuffering with a video capture card, it's recommended to set the card and the\nprogram to the same framerate for best results.\n\nWhen disabled, ensures lowest latency playback, but at the cost of frame\nplayback accuracy. This is ideal for face cameras, or when you want to use the\nprogram's preview window to play a console.\n\nAuto-detect (default) sets it to enabled if the device has delay, and disabled\nif it has no delay." Buffering.AutoDetect="Auto-Detect" Buffering.Enable="Enable" Buffering.Disable="Disable" diff --git a/plugins/win-dshow/win-dshow.cpp b/plugins/win-dshow/win-dshow.cpp index ded5d7662e1269..344745de07be18 100644 --- a/plugins/win-dshow/win-dshow.cpp +++ b/plugins/win-dshow/win-dshow.cpp @@ -1794,6 +1794,9 @@ static obs_properties_t *GetDShowProperties(void *obj) obs_property_list_add_int(p, TEXT_BUFFERING_OFF, (int64_t)BufferingType::Off); + obs_property_set_long_description(p, + obs_module_text("Buffering.ToolTip")); + obs_properties_add_bool(ppts, FLIP_IMAGE, TEXT_FLIP_IMAGE); /* ------------------------------------- */ From c5c1e34d09df03e0299f8871438060bcb0d8e349 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 13 Sep 2016 09:17:07 -0700 Subject: [PATCH 045/107] obs-ffmpeg: Free remaining NVENC frames on exit This clears out remaining NVENC frames to ensure that the encoder has finished processing before shutting down. --- plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index 5ad1daf497bb06..f6faf7bf2167ac 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -231,6 +231,16 @@ static bool nvenc_update(void *data, obs_data_t *settings) static void nvenc_destroy(void *data) { struct nvenc_encoder *enc = data; + AVPacket pkt = {0}; + int r_pkt = 1; + + while (r_pkt) { + if (avcodec_encode_video2(enc->context, &pkt, NULL, &r_pkt) < 0) + break; + + if (r_pkt) + av_free_packet(&pkt); + } avcodec_close(enc->context); av_frame_free(&enc->vframe); From c8a81bba7255362efff16d6be53d937ff1bf8066 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Wed, 14 Sep 2016 05:43:48 -0400 Subject: [PATCH 046/107] image-source: Reload file when timestamp has changed This commit fixes the issue outlined in the following thread: https://obsproject.com/forum/threads/50045/ When the image source file was replaced by an outside process, it would only reload when the file's new timestamp was newer than the file's previous timestamp. This fixes that behavior. Now an image source will reload any time the file's new timestamp is different than its previous timestamp. --- plugins/image-source/image-source.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/image-source/image-source.c b/plugins/image-source/image-source.c index 91ced6c4ded7f2..5986edddcb79cd 100644 --- a/plugins/image-source/image-source.c +++ b/plugins/image-source/image-source.c @@ -207,7 +207,7 @@ static void image_source_tick(void *data, float seconds) time_t t = get_modified_timestamp(context->file); context->update_time_elapsed = 0.0f; - if (context->file_timestamp < t) { + if (context->file_timestamp != t) { image_source_load(context); } } From 855381fc9e8d19897e4ba6a66c0fe82a12a1939d Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 14 Sep 2016 16:27:45 -0700 Subject: [PATCH 047/107] libobs: Add source capability flag for deprecation --- libobs/obs-source.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libobs/obs-source.h b/libobs/obs-source.h index f4b0f809e4ea84..42efaccf2f8492 100644 --- a/libobs/obs-source.h +++ b/libobs/obs-source.h @@ -115,6 +115,11 @@ enum obs_source_type { */ #define OBS_SOURCE_DO_NOT_DUPLICATE (1<<7) +/** + * Source is deprecated and should not be used + */ +#define OBS_SOURCE_DEPRECATED (1<<8) + /** @} */ typedef void (*obs_source_enum_proc_t)(obs_source_t *parent, From 4584ef251138024c39bb25caeb7c70993435cddb Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 13 Sep 2016 14:42:13 -0700 Subject: [PATCH 048/107] UI: Limit font size for font property label If the font size is too big, it can take up the entire properties view window. --- UI/properties-view.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/UI/properties-view.cpp b/UI/properties-view.cpp index f9b08175039590..f8e8faf13b052e 100644 --- a/UI/properties-view.cpp +++ b/UI/properties-view.cpp @@ -633,7 +633,7 @@ void OBSPropertiesView::AddColor(obs_property_t *prop, QFormLayout *layout, layout->addRow(label, subLayout); } -static void MakeQFont(obs_data_t *font_obj, QFont &font) +static void MakeQFont(obs_data_t *font_obj, QFont &font, bool limit = false) { const char *face = obs_data_get_string(font_obj, "face"); const char *style = obs_data_get_string(font_obj, "style"); @@ -645,8 +645,14 @@ static void MakeQFont(obs_data_t *font_obj, QFont &font) font.setStyleName(style); } - if (size) + if (size) { + if (limit) { + int max_size = font.pointSize(); + if (max_size < 28) max_size = 28; + if (size > max_size) size = max_size; + } font.setPointSize(size); + } if (flags & OBS_FONT_BOLD) font.setBold(true); if (flags & OBS_FONT_ITALIC) font.setItalic(true); @@ -666,7 +672,7 @@ void OBSPropertiesView::AddFont(obs_property_t *prop, QFormLayout *layout, QFont font; font = fontLabel->font(); - MakeQFont(font_obj, font); + MakeQFont(font_obj, font, true); button->setText(QTStr("Basic.PropertiesWindow.SelectFont")); button->setToolTip(QT_UTF8(obs_property_long_description(prop))); @@ -1632,7 +1638,9 @@ bool WidgetInfo::FontChanged(const char *setting) obs_data_set_int(font_obj, "flags", flags); QLabel *label = static_cast(widget); - label->setFont(font); + QFont labelFont; + MakeQFont(font_obj, labelFont, true); + label->setFont(labelFont); label->setText(QString("%1 %2").arg(font.family(), font.styleName())); obs_data_set_obj(view->settings, setting, font_obj); From dc2915dc3ba37da04b6ad30732985d871ed831d8 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 14 Sep 2016 16:27:04 -0700 Subject: [PATCH 049/107] UI: Sort 'Add' source popup menu --- UI/window-basic-main.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index e4d46492ecf0fe..83bd9e1be4fdf9 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3185,22 +3185,39 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu() QMenu *popup = new QMenu(QTStr("Add"), this); - auto addSource = [this, popup] (const char *type, const char *name) { - QAction *popupItem = new QAction(QT_UTF8(name), this); + auto getActionAfter = [] (QMenu *menu, const QString &name) + { + QList actions = menu->actions(); + + for (QAction *menuAction : actions) { + if (menuAction->text().compare(name) >= 0) + return menuAction; + } + + return (QAction*)nullptr; + }; + + auto addSource = [this, getActionAfter] (QMenu *popup, + const char *type, const char *name) + { + QString qname = QT_UTF8(name); + QAction *popupItem = new QAction(qname, this); popupItem->setData(QT_UTF8(type)); connect(popupItem, SIGNAL(triggered(bool)), this, SLOT(AddSourceFromAction())); - popup->addAction(popupItem); + + QAction *after = getActionAfter(popup, qname); + popup->insertAction(after, popupItem); }; while (obs_enum_input_types(idx++, &type)) { const char *name = obs_source_get_display_name(type); - addSource(type, name); + addSource(popup, type, name); foundValues = true; } - addSource("scene", Str("Basic.Scene")); + addSource(popup, "scene", Str("Basic.Scene")); if (!foundValues) { delete popup; From b8acca7cca6ccdfb464372a189a7cc2533797916 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 14 Sep 2016 16:32:56 -0700 Subject: [PATCH 050/107] UI: Hide deprecated sources from add source popup menu --- UI/window-basic-main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 83bd9e1be4fdf9..f04a756a1373ba 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -3212,9 +3212,12 @@ QMenu *OBSBasic::CreateAddSourcePopupMenu() while (obs_enum_input_types(idx++, &type)) { const char *name = obs_source_get_display_name(type); + uint32_t caps = obs_get_source_output_flags(type); - addSource(popup, type, name); - foundValues = true; + if ((caps & OBS_SOURCE_DEPRECATED) == 0) { + addSource(popup, type, name); + foundValues = true; + } } addSource(popup, "scene", Str("Basic.Scene")); From 34eb4cd4097aa74fd1b5470d731ede6ac3259cd5 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 11 Sep 2016 10:25:26 -0700 Subject: [PATCH 051/107] obs-text: Add GDI+ text source --- plugins/CMakeLists.txt | 1 + plugins/obs-text/CMakeLists.txt | 21 + plugins/obs-text/data/locale/en-US.ini | 27 + plugins/obs-text/gdiplus/obs-text.cpp | 959 +++++++++++++++++++++++++ 4 files changed, 1008 insertions(+) create mode 100644 plugins/obs-text/CMakeLists.txt create mode 100644 plugins/obs-text/data/locale/en-US.ini create mode 100644 plugins/obs-text/gdiplus/obs-text.cpp diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3087d75d9bf85a..e6966589a5b90b 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -42,5 +42,6 @@ add_subdirectory(obs-ffmpeg) add_subdirectory(obs-outputs) add_subdirectory(obs-filters) add_subdirectory(obs-transitions) +add_subdirectory(obs-text) add_subdirectory(rtmp-services) add_subdirectory(text-freetype2) diff --git a/plugins/obs-text/CMakeLists.txt b/plugins/obs-text/CMakeLists.txt new file mode 100644 index 00000000000000..973f2a7ee20e90 --- /dev/null +++ b/plugins/obs-text/CMakeLists.txt @@ -0,0 +1,21 @@ +if (NOT WIN32) + return() +endif() + +project(obs-text) + +if(WIN32) + set(obs-text_PLATFORM_SOURCES + gdiplus/obs-text.cpp) + set(obs-text_PLATFORM_DEPS + gdiplus) +endif() + +add_library(obs-text MODULE + ${obs-text_PLATFORM_SOURCES} + ${obs-text_PLATFORM_HEADERS}) +target_link_libraries(obs-text + libobs + ${obs-text_PLATFORM_DEPS}) + +install_obs_plugin_with_data(obs-text data) diff --git a/plugins/obs-text/data/locale/en-US.ini b/plugins/obs-text/data/locale/en-US.ini new file mode 100644 index 00000000000000..89c62ceaec3693 --- /dev/null +++ b/plugins/obs-text/data/locale/en-US.ini @@ -0,0 +1,27 @@ +TextGDIPlus="Text (GDI+)" +Font="Font" +Text="Text" +ReadFromFile="Read from file" +TextFile="Text File (UTF-8)" +Filter.TextFiles="Text Files" +Filter.AllFiles="All Files" +Color="Color" +Opacity="Opacity" +Alignment="Alignment" +Alignment.Left="Left" +Alignment.Center="Center" +Alignment.Right="Right" +Vertical="Vertical" +VerticalAlignment="Vertical Alignment" +VerticalAlignment.Top="Top" +VerticalAlignment.Bottom="Bottom" +Outline="Outline" +Outline.Size="Outline Size" +Outline.Color="Outline Color" +Outline.Opacity="Outline Opacity" +ChatlogMode="Chatlog Mode" +ChatlogMode.Lines="Chatlog Line Limit" +UseCustomExtents="Use Custom Text Extents" +UseCustomExtents.Wrap="Wrap" +Width="Width" +Height="Height" diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp new file mode 100644 index 00000000000000..917c0f31c20cbb --- /dev/null +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -0,0 +1,959 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace Gdiplus; + +#define warning(format, ...) blog(LOG_WARNING, "[%s] " format, \ + obs_source_get_name(source), ##__VA_ARGS__) + +#define warn_stat(call) \ + do { \ + if (stat != Ok) \ + warning("%s: %s failed (%d)", __FUNCTION__, call, \ + (int)stat); \ + } while (false) + +#ifndef clamp +#define clamp(val, min_val, max_val) \ + if (val < min_val) val = min_val; \ + else if (val > max_val) val = max_val; +#endif + +#define MIN_SIZE_CX 2 +#define MIN_SIZE_CY 2 +#define MAX_SIZE_CX 16384 +#define MAX_SIZE_CY 16384 + +#define MAX_AREA (4096LL * 4096LL) + +/* ------------------------------------------------------------------------- */ + +#define S_FONT "font" +#define S_USE_FILE "read_from_file" +#define S_FILE "file" +#define S_TEXT "text" +#define S_COLOR "color" +#define S_ALIGN "align" +#define S_VALIGN "valign" +#define S_OPACITY "opacity" +#define S_VERTICAL "vertical" +#define S_OUTLINE "outline" +#define S_OUTLINE_SIZE "outline_size" +#define S_OUTLINE_COLOR "outline_color" +#define S_OUTLINE_OPACITY "outline_opacity" +#define S_CHATLOG_MODE "chatlog" +#define S_CHATLOG_LINES "chatlog_lines" +#define S_EXTENTS "extents" +#define S_EXTENTS_WRAP "extents_wrap" +#define S_EXTENTS_CX "extents_cx" +#define S_EXTENTS_CY "extents_cy" + +#define S_ALIGN_LEFT "left" +#define S_ALIGN_CENTER "center" +#define S_ALIGN_RIGHT "right" + +#define S_VALIGN_TOP "top" +#define S_VALIGN_CENTER S_ALIGN_CENTER +#define S_VALIGN_BOTTOM "bottom" + +#define T_(v) obs_module_text(v) +#define T_FONT T_("Font") +#define T_USE_FILE T_("ReadFromFile") +#define T_FILE T_("TextFile") +#define T_TEXT T_("Text") +#define T_COLOR T_("Color") +#define T_ALIGN T_("Alignment") +#define T_VALIGN T_("VerticalAlignment") +#define T_OPACITY T_("Opacity") +#define T_VERTICAL T_("Veritcal") +#define T_OUTLINE T_("Outline") +#define T_OUTLINE_SIZE T_("Outline.Size") +#define T_OUTLINE_COLOR T_("Outline.Color") +#define T_OUTLINE_OPACITY T_("Outline.Opacity") +#define T_CHATLOG_MODE T_("ChatlogMode") +#define T_CHATLOG_LINES T_("ChatlogMode.Lines") +#define T_EXTENTS T_("UseCustomExtents") +#define T_EXTENTS_WRAP T_("UseCustomExtents.Wrap") +#define T_EXTENTS_CX T_("Width") +#define T_EXTENTS_CY T_("Height") + +#define T_FILTER_TEXT_FILES T_("Filter.TextFiles") +#define T_FILTER_ALL_FILES T_("Filter.AllFiles") + +#define T_ALIGN_LEFT T_("Alignment.Left") +#define T_ALIGN_CENTER T_("Alignment.Center") +#define T_ALIGN_RIGHT T_("Alignment.Right") + +#define T_VALIGN_TOP T_("VerticalAlignment.Top") +#define T_VALIGN_CENTER T_ALIGN_CENTER +#define T_VALIGN_BOTTOM T_("VerticalAlignment.Bottom") + +/* ------------------------------------------------------------------------- */ + +static inline DWORD get_alpha_val(uint32_t opacity) +{ + return ((opacity * 255 / 100) & 0xFF) << 24; +} + +static inline DWORD calc_color(uint32_t color, uint32_t opacity) +{ + return color & 0xFFFFFF | get_alpha_val(opacity); +} + +static inline wstring to_wide(const char *utf8) +{ + wstring text; + + size_t len = os_utf8_to_wcs(utf8, 0, nullptr, 0); + text.resize(len); + if (len) + os_utf8_to_wcs(utf8, 0, &text[0], len + 1); + + return text; +} + +static inline uint32_t rgb_to_bgr(uint32_t rgb) +{ + return ((rgb & 0xFF) << 16) | (rgb & 0xFF00) | ((rgb & 0xFF0000) >> 16); +} + +/* ------------------------------------------------------------------------- */ + +template class GDIObj { + T obj = nullptr; + + inline GDIObj &Replace(T obj_) + { + if (obj) deleter(obj); + obj = obj_; + return *this; + } + +public: + inline GDIObj() {} + inline GDIObj(T obj_) : obj(obj_) {} + inline ~GDIObj() {deleter(obj);} + + inline T operator=(T obj_) {Replace(obj_); return obj;} + + inline operator T() const {return obj;} + + inline bool operator==(T obj_) const {return obj == obj_;} + inline bool operator!=(T obj_) const {return obj != obj_;} +}; + +using HDCObj = GDIObj; +using HFONTObj = GDIObj; +using HBITMAPObj = GDIObj; + +/* ------------------------------------------------------------------------- */ + +enum class Align { + Left, + Center, + Right +}; + +enum class VAlign { + Top, + Center, + Bottom +}; + +struct TextSource { + obs_source_t *source = nullptr; + + gs_texture_t *tex = nullptr; + uint32_t cx = 0; + uint32_t cy = 0; + + HDCObj hdc; + Graphics graphics; + + HFONTObj hfont; + unique_ptr font; + + bool read_from_file = false; + string file; + time_t file_timestamp = 0; + float update_time_elapsed = 0.0f; + + wstring text; + wstring face; + int face_size = 0; + uint32_t color = 0xFFFFFF; + uint32_t opacity = 100; + uint32_t bk_color = 0; + uint32_t bk_opacity = 0; + Align align = Align::Left; + VAlign valign = VAlign::Top; + bool bold = false; + bool italic = false; + bool underline = false; + bool strikeout = false; + bool vertical = false; + + bool use_outline = false; + float outline_size = 0.0f; + uint32_t outline_color = 0; + uint32_t outline_opacity = 100; + + bool use_extents = false; + bool wrap = false; + uint32_t extents_cx = 0; + uint32_t extents_cy = 0; + + bool chatlog_mode = false; + int chatlog_lines = 6; + + /* --------------------------- */ + + inline TextSource(obs_source_t *source_, obs_data_t *settings) : + source (source_), + hdc (CreateCompatibleDC(nullptr)), + graphics (hdc) + { + obs_source_update(source, settings); + } + + inline ~TextSource() + { + if (tex) { + obs_enter_graphics(); + gs_texture_destroy(tex); + obs_leave_graphics(); + } + } + + void UpdateFont(); + void GetStringFormat(StringFormat &format); + void RemoveNewlinePadding(const StringFormat &format, RectF &box); + void CalculateTextSizes(const StringFormat &format, + RectF &bounding_box, SIZE &text_size); + void RenderOutlineText(Graphics &graphics, + const GraphicsPath &path, + const Brush &brush); + void RenderText(); + void LoadFileText(); + + const char *GetMainString(const char *str); + + inline void Update(obs_data_t *settings); + inline void Tick(float seconds); + inline void Render(gs_effect_t *effect); +}; + +static time_t get_modified_timestamp(const char *filename) +{ + struct stat stats; + if (os_stat(filename, &stats) != 0) + return -1; + return stats.st_mtime; +} + +void TextSource::UpdateFont() +{ + hfont = nullptr; + font.reset(nullptr); + + LOGFONT lf = {}; + lf.lfHeight = face_size; + lf.lfWeight = bold ? FW_BOLD : FW_DONTCARE; + lf.lfItalic = italic; + lf.lfUnderline = underline; + lf.lfStrikeOut = strikeout; + lf.lfQuality = ANTIALIASED_QUALITY; + + if (!face.empty()) { + wcscpy(lf.lfFaceName, face.c_str()); + hfont = CreateFontIndirect(&lf); + } + + if (!hfont) { + wcscpy(lf.lfFaceName, L"Arial"); + hfont = CreateFontIndirect(&lf); + } + + if (hfont) + font.reset(new Font(hdc, hfont)); +} + +void TextSource::GetStringFormat(StringFormat &format) +{ + UINT flags = StringFormatFlagsNoFitBlackBox | + StringFormatFlagsMeasureTrailingSpaces; + + if (vertical) + flags |= StringFormatFlagsDirectionVertical | + StringFormatFlagsDirectionRightToLeft; + + format.SetFormatFlags(flags); + format.SetTrimming(StringTrimmingWord); + + switch (align) { + case Align::Left: + if (vertical) + format.SetLineAlignment(StringAlignmentFar); + else + format.SetAlignment(StringAlignmentNear); + break; + case Align::Center: + if (vertical) + format.SetLineAlignment(StringAlignmentCenter); + else + format.SetAlignment(StringAlignmentCenter); + break; + case Align::Right: + if (vertical) + format.SetLineAlignment(StringAlignmentNear); + else + format.SetAlignment(StringAlignmentFar); + } + + switch (valign) { + case VAlign::Top: + if (vertical) + format.SetAlignment(StringAlignmentNear); + else + format.SetLineAlignment(StringAlignmentNear); + break; + case VAlign::Center: + if (vertical) + format.SetAlignment(StringAlignmentCenter); + else + format.SetLineAlignment(StringAlignmentCenter); + break; + case VAlign::Bottom: + if (vertical) + format.SetAlignment(StringAlignmentFar); + else + format.SetLineAlignment(StringAlignmentFar); + } +} + +/* GDI+ treats '\n' as an extra character with an actual render size when + * calculating the texture size, so we have to calculate the size of '\n' to + * remove the padding. Because we always add a newline to the string, we + * also remove the extra unused newline. */ +void TextSource::RemoveNewlinePadding(const StringFormat &format, RectF &box) +{ + RectF before; + RectF after; + Status stat; + + stat = graphics.MeasureString(L"W", 2, font.get(), PointF(0.0f, 0.0f), + &format, &before); + warn_stat("MeasureString (without newline)"); + + stat = graphics.MeasureString(L"W\n", 3, font.get(), PointF(0.0f, 0.0f), + &format, &after); + warn_stat("MeasureString (with newline)"); + + float offset_cx = after.Width - before.Width; + float offset_cy = after.Height - before.Height; + + if (!vertical) { + if (offset_cx >= 1.0f) + offset_cx -= 1.0f; + + if (valign == VAlign::Center) + box.Y -= offset_cy * 0.5f; + else if (valign == VAlign::Bottom) + box.Y -= offset_cy; + } else { + if (offset_cy >= 1.0f) + offset_cy -= 1.0f; + + if (align == Align::Center) + box.X -= offset_cx * 0.5f; + else if (align == Align::Right) + box.X -= offset_cx; + } + + box.Width -= offset_cx; + box.Height -= offset_cy; +} + +void TextSource::CalculateTextSizes(const StringFormat &format, + RectF &bounding_box, SIZE &text_size) +{ + RectF layout_box; + RectF temp_box; + Status stat; + + if (!text.empty()) { + if (use_extents && wrap) { + layout_box.X = layout_box.Y = 0; + layout_box.Width = float(extents_cx); + layout_box.Height = float(extents_cy); + + if (use_outline) { + layout_box.Width -= outline_size; + layout_box.Height -= outline_size; + } + + stat = graphics.MeasureString(text.c_str(), + (int)text.size() + 1, font.get(), + layout_box, &format, + &bounding_box); + warn_stat("MeasureString (wrapped)"); + + temp_box = bounding_box; + } else { + stat = graphics.MeasureString(text.c_str(), + (int)text.size() + 1, font.get(), + PointF(0.0f, 0.0f), &format, + &bounding_box); + warn_stat("MeasureString (non-wrapped)"); + + temp_box = bounding_box; + + bounding_box.X = 0.0f; + bounding_box.Y = 0.0f; + + RemoveNewlinePadding(format, bounding_box); + + if (use_outline) { + bounding_box.Width += outline_size; + bounding_box.Height += outline_size; + } + } + } + + if (vertical) { + if (bounding_box.Width < face_size) { + text_size.cx = face_size; + bounding_box.Width = float(face_size); + } else { + text_size.cx = LONG(bounding_box.Width + EPSILON); + } + + text_size.cy = LONG(bounding_box.Height + EPSILON); + } else { + if (bounding_box.Height < face_size) { + text_size.cy = face_size; + bounding_box.Height = float(face_size); + } else { + text_size.cy = LONG(bounding_box.Height + EPSILON); + } + + text_size.cx = LONG(bounding_box.Width + EPSILON); + } + + if (use_extents) { + text_size.cx = extents_cx; + text_size.cy = extents_cy; + } + + text_size.cx += text_size.cx % 2; + text_size.cy += text_size.cy % 2; + + int64_t total_size = int64_t(text_size.cx) * int64_t(text_size.cy); + + /* GPUs typically have texture size limitations */ + clamp(text_size.cx, MIN_SIZE_CX, MAX_SIZE_CX); + clamp(text_size.cy, MIN_SIZE_CY, MAX_SIZE_CY); + + /* avoid taking up too much VRAM */ + if (total_size > MAX_AREA) { + if (text_size.cx > text_size.cy) + text_size.cx = (LONG)MAX_AREA / text_size.cy; + else + text_size.cy = (LONG)MAX_AREA / text_size.cx; + } + + /* the internal text-rendering bounding box for is reset to + * its internal value in case the texture gets cut off */ + bounding_box.Width = temp_box.Width; + bounding_box.Height = temp_box.Height; +} + +void TextSource::RenderOutlineText(Graphics &graphics, + const GraphicsPath &path, + const Brush &brush) +{ + DWORD outline_rgba = calc_color(outline_color, outline_opacity); + Status stat; + + Pen pen(Color(outline_rgba), outline_size); + stat = pen.SetLineJoin(LineJoinRound); + warn_stat("pen.SetLineJoin"); + + stat = graphics.DrawPath(&pen, &path); + warn_stat("graphics.DrawPath"); + + stat = graphics.FillPath(&brush, &path); + warn_stat("graphics.FillPath"); +} + +void TextSource::RenderText() +{ + StringFormat format(StringFormat::GenericTypographic()); + Status stat; + + RectF box; + SIZE size; + + GetStringFormat(format); + CalculateTextSizes(format, box, size); + + unique_ptr bits(new uint8_t[size.cx * size.cy * 4]); + Bitmap bitmap(size.cx, size.cy, 4 * size.cx, PixelFormat32bppARGB, + bits.get()); + + Graphics graphics_bitmap(&bitmap); + SolidBrush brush(Color(get_alpha_val(opacity) | (color & 0xFFFFFF))); + DWORD full_bk_color = bk_color & 0xFFFFFF; + + if (!text.empty() || use_extents) + full_bk_color |= get_alpha_val(bk_opacity); + + if ((size.cx > box.Width || size.cy > box.Height) && !use_extents) { + stat = graphics_bitmap.Clear(Color(0)); + warn_stat("graphics_bitmap.Clear"); + + SolidBrush bk_brush = Color(full_bk_color); + stat = graphics_bitmap.FillRectangle(&bk_brush, box); + warn_stat("graphics_bitmap.FillRectangle"); + } else { + stat = graphics_bitmap.Clear(Color(full_bk_color)); + warn_stat("graphics_bitmap.Clear"); + } + + graphics_bitmap.SetTextRenderingHint(TextRenderingHintAntiAlias); + graphics_bitmap.SetCompositingMode(CompositingModeSourceOver); + graphics_bitmap.SetSmoothingMode(SmoothingModeAntiAlias); + + if (!text.empty()) { + if (use_outline) { + box.Offset(outline_size / 2, outline_size / 2); + + FontFamily family; + GraphicsPath path; + + font->GetFamily(&family); + stat = path.AddString(text.c_str(), (int)text.size(), + &family, font->GetStyle(), + font->GetSize(), box, &format); + warn_stat("path.AddString"); + + RenderOutlineText(graphics_bitmap, path, brush); + } else { + stat = graphics_bitmap.DrawString(text.c_str(), + (int)text.size(), font.get(), + box, &format, &brush); + warn_stat("graphics_bitmap.DrawString"); + } + } + + if (!tex || (LONG)cx != size.cx || (LONG)cy != size.cy) { + obs_enter_graphics(); + if (tex) + gs_texture_destroy(tex); + + const uint8_t *data = (uint8_t*)bits.get(); + tex = gs_texture_create(size.cx, size.cy, GS_BGRA, 1, &data, + GS_DYNAMIC); + + obs_leave_graphics(); + + cx = (uint32_t)size.cx; + cy = (uint32_t)size.cy; + + } else if (tex) { + obs_enter_graphics(); + gs_texture_set_image(tex, bits.get(), size.cx * 4, false); + obs_leave_graphics(); + } +} + +const char *TextSource::GetMainString(const char *str) +{ + if (!str) + return ""; + if (!chatlog_mode || !chatlog_lines) + return str; + + int lines = chatlog_lines; + size_t len = strlen(str); + if (!len) + return str; + + const char *temp = str + len; + + while(temp != str) { + temp--; + + if (temp[0] == '\n' && temp[1] != 0) { + if (!--lines) + break; + } + } + + return *temp == '\n' ? temp + 1 : temp; +} + +void TextSource::LoadFileText() +{ + BPtr file_text = os_quick_read_utf8_file(file.c_str()); + text = to_wide(GetMainString(file_text)); + + if (!text.empty() && text.back() != '\n') + text.push_back('\n'); +} + +#define obs_data_get_uint32 (uint32_t)obs_data_get_int + +inline void TextSource::Update(obs_data_t *s) +{ + const char *new_text = obs_data_get_string(s, S_TEXT); + obs_data_t *font_obj = obs_data_get_obj(s, S_FONT); + const char *align_str = obs_data_get_string(s, S_ALIGN); + const char *valign_str = obs_data_get_string(s, S_VALIGN); + uint32_t new_color = obs_data_get_uint32(s, S_COLOR); + uint32_t new_opacity = obs_data_get_uint32(s, S_OPACITY); + bool new_vertical = obs_data_get_bool(s, S_VERTICAL); + bool new_outline = obs_data_get_bool(s, S_OUTLINE); + uint32_t new_o_color = obs_data_get_uint32(s, S_OUTLINE_COLOR); + uint32_t new_o_opacity = obs_data_get_uint32(s, S_OUTLINE_OPACITY); + uint32_t new_o_size = obs_data_get_uint32(s, S_OUTLINE_SIZE); + bool new_use_file = obs_data_get_bool(s, S_USE_FILE); + const char *new_file = obs_data_get_string(s, S_FILE); + bool new_chat_mode = obs_data_get_bool(s, S_CHATLOG_MODE); + int new_chat_lines = (int)obs_data_get_int(s, S_CHATLOG_LINES); + bool new_extents = obs_data_get_bool(s, S_EXTENTS); + bool new_extents_wrap = obs_data_get_bool(s, S_EXTENTS_WRAP); + uint32_t n_extents_cx = obs_data_get_uint32(s, S_EXTENTS_CX); + uint32_t n_extents_cy = obs_data_get_uint32(s, S_EXTENTS_CY); + + const char *font_face = obs_data_get_string(font_obj, "face"); + int font_size = (int)obs_data_get_int(font_obj, "size"); + int64_t font_flags = obs_data_get_int(font_obj, "flags"); + bool new_bold = (font_flags & OBS_FONT_BOLD) != 0; + bool new_italic = (font_flags & OBS_FONT_ITALIC) != 0; + bool new_underline = (font_flags & OBS_FONT_UNDERLINE) != 0; + bool new_strikeout = (font_flags & OBS_FONT_STRIKEOUT) != 0; + + /* ----------------------------- */ + + wstring new_face = to_wide(font_face); + + if (new_face != face || + face_size != font_size || + new_bold != bold || + new_italic != italic || + new_underline != underline || + new_strikeout != strikeout) { + + face = new_face; + face_size = font_size; + bold = new_bold; + italic = new_italic; + underline = new_underline; + strikeout = new_strikeout; + + UpdateFont(); + } + + /* ----------------------------- */ + + new_color = rgb_to_bgr(new_color); + new_o_color = rgb_to_bgr(new_o_color); + + color = new_color; + opacity = new_opacity; + vertical = new_vertical; + + use_extents = new_extents; + wrap = new_extents_wrap; + extents_cx = n_extents_cx; + extents_cy = n_extents_cy; + + read_from_file = new_use_file; + + chatlog_mode = new_chat_mode; + chatlog_lines = new_chat_lines; + + if (read_from_file) { + file = new_file; + file_timestamp = get_modified_timestamp(new_file); + LoadFileText(); + + } else { + text = to_wide(GetMainString(new_text)); + + /* all text should end with newlines due to the fact that GDI+ + * treats strings without newlines differently in terms of + * render size */ + if (!text.empty()) + text.push_back('\n'); + } + + use_outline = new_outline; + outline_color = new_o_color; + outline_opacity = new_o_opacity; + outline_size = roundf(float(new_o_size)); + + if (strcmp(align_str, S_ALIGN_CENTER) == 0) + align = Align::Center; + else if (strcmp(align_str, S_ALIGN_RIGHT) == 0) + align = Align::Right; + else + align = Align::Left; + + if (strcmp(valign_str, S_VALIGN_CENTER) == 0) + valign = VAlign::Center; + else if (strcmp(valign_str, S_VALIGN_BOTTOM) == 0) + valign = VAlign::Bottom; + else + valign = VAlign::Top; + + RenderText(); + update_time_elapsed = 0.0f; + + /* ----------------------------- */ + + obs_data_release(font_obj); +} + +inline void TextSource::Tick(float seconds) +{ + if (!read_from_file) + return; + + update_time_elapsed += seconds; + + if (update_time_elapsed >= 2.0f) { + time_t t = get_modified_timestamp(file.c_str()); + update_time_elapsed = 0.0f; + + if (file_timestamp < t) { + LoadFileText(); + RenderText(); + file_timestamp = t; + } + } +} + +inline void TextSource::Render(gs_effect_t *effect) +{ + if (!tex) + return; + + gs_reset_blend_state(); + gs_effect_set_texture(gs_effect_get_param_by_name(effect, "image"), tex); + gs_draw_sprite(tex, 0, cx, cy); +} + +/* ------------------------------------------------------------------------- */ + +static ULONG_PTR gdip_token = 0; + +OBS_DECLARE_MODULE() +OBS_MODULE_USE_DEFAULT_LOCALE("obs-text", "en-US") + +#define set_vis(var, val, show) \ + do { \ + p = obs_properties_get(props, val); \ + obs_property_set_visible(p, var == show); \ + } while (false) + +static bool use_file_changed(obs_properties_t *props, obs_property_t *p, + obs_data_t *s) +{ + bool use_file = obs_data_get_bool(s, S_USE_FILE); + + set_vis(use_file, S_TEXT, false); + set_vis(use_file, S_FILE, true); + return true; +} + +static bool outline_changed(obs_properties_t *props, obs_property_t *p, + obs_data_t *s) +{ + bool outline = obs_data_get_bool(s, S_OUTLINE); + + set_vis(outline, S_OUTLINE_SIZE, true); + set_vis(outline, S_OUTLINE_COLOR, true); + set_vis(outline, S_OUTLINE_OPACITY, true); + return true; +} + +static bool chatlog_mode_changed(obs_properties_t *props, obs_property_t *p, + obs_data_t *s) +{ + bool chatlog_mode = obs_data_get_bool(s, S_CHATLOG_MODE); + + set_vis(chatlog_mode, S_CHATLOG_LINES, true); + return true; +} + +static bool extents_modified(obs_properties_t *props, obs_property_t *p, + obs_data_t *s) +{ + bool use_extents = obs_data_get_bool(s, S_EXTENTS); + + set_vis(use_extents, S_EXTENTS_WRAP, true); + set_vis(use_extents, S_EXTENTS_CX, true); + set_vis(use_extents, S_EXTENTS_CY, true); + return true; +} + +#undef set_vis + +static obs_properties_t *get_properties(void *data) +{ + TextSource *s = reinterpret_cast(data); + string path; + + obs_properties_t *props = obs_properties_create(); + obs_property_t *p; + + obs_properties_add_font(props, S_FONT, T_FONT); + + p = obs_properties_add_bool(props, S_USE_FILE, T_USE_FILE); + obs_property_set_modified_callback(p, use_file_changed); + + string filter; + filter += T_FILTER_TEXT_FILES; + filter += " (*.txt);;"; + filter += T_FILTER_ALL_FILES; + filter += " (*.*)"; + + if (s && !s->file.empty()) { + const char *slash; + + path = s->file; + replace(path.begin(), path.end(), '\\', '/'); + slash = strrchr(path.c_str(), '/'); + if (slash) + path.resize(slash - path.c_str() + 1); + } + + obs_properties_add_text(props, S_TEXT, T_TEXT, OBS_TEXT_MULTILINE); + obs_properties_add_path(props, S_FILE, T_FILE, OBS_PATH_FILE, + filter.c_str(), path.c_str()); + + obs_properties_add_bool(props, S_VERTICAL, T_VERTICAL); + obs_properties_add_color(props, S_COLOR, T_COLOR); + + obs_properties_add_int_slider(props, S_OPACITY, T_OPACITY, 0, 100, 1); + + p = obs_properties_add_list(props, S_ALIGN, T_ALIGN, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, T_ALIGN_LEFT, S_ALIGN_LEFT); + obs_property_list_add_string(p, T_ALIGN_CENTER, S_ALIGN_CENTER); + obs_property_list_add_string(p, T_ALIGN_RIGHT, S_ALIGN_RIGHT); + + p = obs_properties_add_list(props, S_VALIGN, T_VALIGN, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_list_add_string(p, T_VALIGN_TOP, S_VALIGN_TOP); + obs_property_list_add_string(p, T_VALIGN_CENTER, S_VALIGN_CENTER); + obs_property_list_add_string(p, T_VALIGN_BOTTOM, S_VALIGN_BOTTOM); + + p = obs_properties_add_bool(props, S_OUTLINE, T_OUTLINE); + obs_property_set_modified_callback(p, outline_changed); + + obs_properties_add_int(props, S_OUTLINE_SIZE, T_OUTLINE_SIZE, 1, 20, 1); + obs_properties_add_color(props, S_OUTLINE_COLOR, T_OUTLINE_COLOR); + obs_properties_add_int_slider(props, S_OUTLINE_OPACITY, + T_OUTLINE_OPACITY, 0, 100, 1); + + p = obs_properties_add_bool(props, S_CHATLOG_MODE, T_CHATLOG_MODE); + obs_property_set_modified_callback(p, chatlog_mode_changed); + + obs_properties_add_int(props, S_CHATLOG_LINES, T_CHATLOG_LINES, + 1, 1000, 1); + + p = obs_properties_add_bool(props, S_EXTENTS, T_EXTENTS); + obs_property_set_modified_callback(p, extents_modified); + + obs_properties_add_int(props, S_EXTENTS_CX, T_EXTENTS_CX, 32, 8000, 1); + obs_properties_add_int(props, S_EXTENTS_CY, T_EXTENTS_CY, 32, 8000, 1); + obs_properties_add_bool(props, S_EXTENTS_WRAP, T_EXTENTS_WRAP); + + return props; +} + +bool obs_module_load(void) +{ + obs_source_info si = {}; + si.id = "text_gdiplus"; + si.type = OBS_SOURCE_TYPE_INPUT; + si.output_flags = OBS_SOURCE_VIDEO; + si.get_properties = get_properties; + + si.get_name = [] (void*) + { + return obs_module_text("TextGDIPlus"); + }; + si.create = [] (obs_data_t *settings, obs_source_t *source) + { + return (void*)new TextSource(source, settings); + }; + si.destroy = [] (void *data) + { + delete reinterpret_cast(data); + }; + si.get_width = [] (void *data) + { + return reinterpret_cast(data)->cx; + }; + si.get_height = [] (void *data) + { + return reinterpret_cast(data)->cy; + }; + si.get_defaults = [] (obs_data_t *settings) + { + obs_data_t *font_obj = obs_data_create(); + obs_data_set_default_string(font_obj, "face", "Arial"); + obs_data_set_default_int(font_obj, "size", 22); + + obs_data_set_default_obj(settings, S_FONT, font_obj); + obs_data_set_default_string(settings, S_ALIGN, S_ALIGN_LEFT); + obs_data_set_default_string(settings, S_VALIGN, S_VALIGN_TOP); + obs_data_set_default_int(settings, S_COLOR, 0xFFFFFF); + obs_data_set_default_int(settings, S_OPACITY, 100); + obs_data_set_default_int(settings, S_OUTLINE_SIZE, 2); + obs_data_set_default_int(settings, S_OUTLINE_COLOR, 0xFFFFFF); + obs_data_set_default_int(settings, S_OUTLINE_OPACITY, 100); + obs_data_set_default_int(settings, S_CHATLOG_LINES, 6); + obs_data_set_default_bool(settings, S_EXTENTS_WRAP, true); + obs_data_set_default_int(settings, S_EXTENTS_CX, 100); + obs_data_set_default_int(settings, S_EXTENTS_CY, 100); + + obs_data_release(font_obj); + }; + si.update = [] (void *data, obs_data_t *settings) + { + reinterpret_cast(data)->Update(settings); + }; + si.video_tick = [] (void *data, float seconds) + { + reinterpret_cast(data)->Tick(seconds); + }; + si.video_render = [] (void *data, gs_effect_t *effect) + { + reinterpret_cast(data)->Render(effect); + }; + + obs_register_source(&si); + + const GdiplusStartupInput gdip_input; + GdiplusStartup(&gdip_token, &gdip_input, nullptr); + return true; +} + +void obs_module_unload(void) +{ + GdiplusShutdown(gdip_token); +} From 5af1a2b5bbf9efddf1b27636bd74127860257658 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 14 Sep 2016 16:28:55 -0700 Subject: [PATCH 052/107] text-freetype2: Deprecate on windows --- plugins/text-freetype2/text-freetype2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/text-freetype2/text-freetype2.c b/plugins/text-freetype2/text-freetype2.c index b885620449ee78..02579593d8f510 100644 --- a/plugins/text-freetype2/text-freetype2.c +++ b/plugins/text-freetype2/text-freetype2.c @@ -35,6 +35,9 @@ static struct obs_source_info freetype2_source_info = { .id = "text_ft2_source", .type = OBS_SOURCE_TYPE_INPUT, .output_flags = OBS_SOURCE_VIDEO | +#ifdef _WIN32 + OBS_SOURCE_DEPRECATED | +#endif OBS_SOURCE_CUSTOM_DRAW, .get_name = ft2_source_get_name, .create = ft2_source_create, From 6b2862fe1e6185aaf76a048181a1d719a7ed31c3 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 14 Sep 2016 16:57:26 -0700 Subject: [PATCH 053/107] Revert "libobs/util: Add index operator to BPtr" This reverts commit 7224e8d36e6c635c9769af1a5f1d9f7c903ac9e0. This change was unnecessary due to the fact that indexing is already performed automatically via the cast operator. --- libobs/util/util.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libobs/util/util.hpp b/libobs/util/util.hpp index 4a8224d8245742..d7b3a6df0dbbce 100644 --- a/libobs/util/util.hpp +++ b/libobs/util/util.hpp @@ -46,8 +46,6 @@ template class BPtr { inline bool operator!() {return ptr == NULL;} inline bool operator==(T p) {return ptr == p;} inline bool operator!=(T p) {return ptr != p;} - - inline T operator[](size_t idx) const {return ptr[idx];} }; class ConfigFile { From 1bbe152a554585ad44854efe23fb9df223ca3f31 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 15 Sep 2016 04:52:22 -0700 Subject: [PATCH 054/107] obs-text: Fix call type for GDIObj helper class The call type for the deleter function used with the GDIObj helper class is __stdcall. Without it, it'll fail to build in 32bit properly. --- plugins/obs-text/gdiplus/obs-text.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index 917c0f31c20cbb..c9d3bb506d67d5 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -128,7 +128,7 @@ static inline uint32_t rgb_to_bgr(uint32_t rgb) /* ------------------------------------------------------------------------- */ -template class GDIObj { +template class GDIObj { T obj = nullptr; inline GDIObj &Replace(T obj_) From 31f106c1b533c8b2e1a15ad914fb9d794277bcb6 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 15 Sep 2016 05:20:59 -0700 Subject: [PATCH 055/107] obs-text: Fix typo --- plugins/obs-text/gdiplus/obs-text.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index c9d3bb506d67d5..ad56862ab4472c 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -74,7 +74,7 @@ using namespace Gdiplus; #define T_ALIGN T_("Alignment") #define T_VALIGN T_("VerticalAlignment") #define T_OPACITY T_("Opacity") -#define T_VERTICAL T_("Veritcal") +#define T_VERTICAL T_("Vertical") #define T_OUTLINE T_("Outline") #define T_OUTLINE_SIZE T_("Outline.Size") #define T_OUTLINE_COLOR T_("Outline.Color") From b2a85a2820498a137a56bfa298a5649db43d2110 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 16 Sep 2016 19:47:24 -0700 Subject: [PATCH 056/107] obs-text: Reload whenever file timestamp has changed Rather than reload when the timestamp is greater than the previous timestamp, reload whenever the timestamp has changed. --- plugins/obs-text/gdiplus/obs-text.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index ad56862ab4472c..e74f73709f3590 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -736,7 +736,7 @@ inline void TextSource::Tick(float seconds) time_t t = get_modified_timestamp(file.c_str()); update_time_elapsed = 0.0f; - if (file_timestamp < t) { + if (file_timestamp != t) { LoadFileText(); RenderText(); file_timestamp = t; From 3b3933b42f0f3f5c2c88adb5db09d1d141fa1a41 Mon Sep 17 00:00:00 2001 From: derrod Date: Tue, 13 Sep 2016 14:59:42 +0200 Subject: [PATCH 057/107] obs-ffmpeg: Fix (put back in) "profile" NVENC setting The profile setting may have been unintentionally removed in 0157d02564. Closes jp9000/obs-studio#616 --- plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index f6faf7bf2167ac..a1d673f96690ef 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -159,7 +159,7 @@ static bool nvenc_update(void *data, obs_data_t *settings) nvenc_video_info(enc, &info); av_opt_set_int(enc->context->priv_data, "cbr", false, 0); - + av_opt_set(enc->context->priv_data, "profile", profile, 0); av_opt_set(enc->context->priv_data, "preset", preset, 0); if (astrcmpi(rc, "cqp") == 0) { From 883f9f204bba4a90ffb5f72863b343f0ee678213 Mon Sep 17 00:00:00 2001 From: cg2121 Date: Mon, 5 Sep 2016 22:42:17 -0500 Subject: [PATCH 058/107] UI: Add reset/close buttons to transform dialog Adds a reset and close button to the transform dialog. Closes jp9000/obs-studio#612 --- UI/data/locale/en-US.ini | 1 + UI/forms/OBSBasicTransform.ui | 7 +++++++ UI/window-basic-main.hpp | 4 +++- UI/window-basic-transform.cpp | 11 +++++++++++ UI/window-basic-transform.hpp | 1 + 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 35c06fc17b85c2..6c698a1b7d6039 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -53,6 +53,7 @@ Left="Left" Right="Right" Top="Top" Bottom="Bottom" +Reset="Reset" # quick transitions QuickTransitions.SwapScenes="Swap Preview/Output Scenes After Transitioning" diff --git a/UI/forms/OBSBasicTransform.ui b/UI/forms/OBSBasicTransform.ui index 7f49ffaf55920f..c801b3da795617 100644 --- a/UI/forms/OBSBasicTransform.ui +++ b/UI/forms/OBSBasicTransform.ui @@ -638,6 +638,13 @@ + + + + QDialogButtonBox::Reset|QDialogButtonBox::Close + + + diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index f7a0b8e1967093..792742d62f77f3 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -462,7 +462,6 @@ private slots: void on_actionCheckForUpdates_triggered(); void on_actionEditTransform_triggered(); - void on_actionResetTransform_triggered(); void on_actionRotate90CW_triggered(); void on_actionRotate90CCW_triggered(); void on_actionRotate180_triggered(); @@ -564,6 +563,9 @@ private slots: void OpenSourceProjector(); void OpenSceneProjector(); +public slots: + void on_actionResetTransform_triggered(); + public: explicit OBSBasic(QWidget *parent = 0); virtual ~OBSBasic(); diff --git a/UI/window-basic-transform.cpp b/UI/window-basic-transform.cpp index 57ad6304f9210a..f1143e7d17f1f0 100644 --- a/UI/window-basic-transform.cpp +++ b/UI/window-basic-transform.cpp @@ -1,3 +1,4 @@ +#include #include "window-basic-transform.hpp" #include "window-basic-main.hpp" @@ -55,6 +56,11 @@ OBSBasicTransform::OBSBasicTransform(OBSBasic *parent) HookWidget(ui->cropTop, ISCROLL_CHANGED, SLOT(OnCropChanged())); HookWidget(ui->cropBottom, ISCROLL_CHANGED, SLOT(OnCropChanged())); + connect(ui->buttonBox->button(QDialogButtonBox::Reset), + SIGNAL(clicked()), this, SLOT(on_resetButton_clicked())); + connect(ui->buttonBox, + SIGNAL(rejected()), this, SLOT(close())); + installEventFilter(CreateShortcutFilter()); OBSScene curScene = main->GetCurrentScene(); @@ -288,3 +294,8 @@ void OBSBasicTransform::OnCropChanged() obs_sceneitem_set_crop(item, &crop); ignoreTransformSignal = false; } + +void OBSBasicTransform::on_resetButton_clicked() +{ + main->on_actionResetTransform_triggered(); +} diff --git a/UI/window-basic-transform.hpp b/UI/window-basic-transform.hpp index 9764f28f602f4a..c86a24440e00db 100644 --- a/UI/window-basic-transform.hpp +++ b/UI/window-basic-transform.hpp @@ -42,6 +42,7 @@ private slots: void OnBoundsType(int index); void OnControlChanged(); void OnCropChanged(); + void on_resetButton_clicked(); public: OBSBasicTransform(OBSBasic *parent); From 0bc0db1207b1800ed6de90c5cec8c05773fb8b73 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 19 Sep 2016 06:38:05 -0700 Subject: [PATCH 059/107] libobs/graphics: Add gs_draw_sprite_subregion function Allows rendering only a subregion of a specific sprite. --- libobs/graphics/graphics.c | 57 ++++++++++++++++++++++++++++++++++++++ libobs/graphics/graphics.h | 3 ++ 2 files changed, 60 insertions(+) diff --git a/libobs/graphics/graphics.c b/libobs/graphics/graphics.c index a1f466ddbcbd27..aa886fb183f85c 100644 --- a/libobs/graphics/graphics.c +++ b/libobs/graphics/graphics.c @@ -962,6 +962,32 @@ static inline void build_sprite_norm(struct gs_vb_data *data, float fcx, build_sprite(data, fcx, fcy, start_u, end_u, start_v, end_v); } +static inline void build_subsprite_norm(struct gs_vb_data *data, + float fsub_x, float fsub_y, float fsub_cx, float fsub_cy, + float fcx, float fcy, uint32_t flip) +{ + float start_u, end_u; + float start_v, end_v; + + if ((flip & GS_FLIP_U) == 0) { + start_u = fsub_x / fcx; + end_u = (fsub_x + fsub_cx) / fcx; + } else { + start_u = (fsub_x + fsub_cx) / fcx; + end_u = fsub_x / fcx; + } + + if ((flip & GS_FLIP_V) == 0) { + start_v = fsub_y / fcy; + end_v = (fsub_y + fsub_cy) / fcy; + } else { + start_v = (fsub_y + fsub_cy) / fcy; + end_v = fsub_y / fcy; + } + + build_sprite(data, fsub_cx, fsub_cy, start_u, end_u, start_v, end_v); +} + static inline void build_sprite_rect(struct gs_vb_data *data, gs_texture_t *tex, float fcx, float fcy, uint32_t flip) { @@ -1011,6 +1037,37 @@ void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, gs_draw(GS_TRISTRIP, 0, 0); } +void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, + uint32_t sub_x, uint32_t sub_y, + uint32_t sub_cx, uint32_t sub_cy) +{ + graphics_t *graphics = thread_graphics; + float fcx, fcy; + struct gs_vb_data *data; + + if (tex) { + if (gs_get_texture_type(tex) != GS_TEXTURE_2D) { + blog(LOG_ERROR, "A sprite must be a 2D texture"); + return; + } + } + + fcx = (float)gs_texture_get_width(tex); + fcy = (float)gs_texture_get_height(tex); + + data = gs_vertexbuffer_get_data(graphics->sprite_buffer); + build_subsprite_norm(data, + (float)sub_x, (float)sub_y, + (float)sub_cx, (float)sub_cy, + fcx, fcy, flip); + + gs_vertexbuffer_flush(graphics->sprite_buffer); + gs_load_vertexbuffer(graphics->sprite_buffer); + gs_load_indexbuffer(NULL); + + gs_draw(GS_TRISTRIP, 0, 0); +} + void gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot, float left, float right, float top, float bottom, float znear) { diff --git a/libobs/graphics/graphics.h b/libobs/graphics/graphics.h index 6c7fc6606577c0..bd79fa800899e1 100644 --- a/libobs/graphics/graphics.h +++ b/libobs/graphics/graphics.h @@ -525,6 +525,9 @@ EXPORT uint8_t *gs_create_texture_file_data(const char *file, EXPORT void gs_draw_sprite(gs_texture_t *tex, uint32_t flip, uint32_t width, uint32_t height); +EXPORT void gs_draw_sprite_subregion(gs_texture_t *tex, uint32_t flip, + uint32_t x, uint32_t y, uint32_t cx, uint32_t cy); + EXPORT void gs_draw_cube_backdrop(gs_texture_t *cubetex, const struct quat *rot, float left, float right, float top, float bottom, float znear); From c99f65a0dff41615fbd8d11bc8c3ce28d2c6ba15 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 19 Sep 2016 06:39:02 -0700 Subject: [PATCH 060/107] libobs: Add property functions to mod. int/double limits --- libobs/obs-properties.c | 24 ++++++++++++++++++++++++ libobs/obs-properties.h | 5 +++++ 2 files changed, 29 insertions(+) diff --git a/libobs/obs-properties.c b/libobs/obs-properties.c index c2d5a99ec8b58f..1533bee5f24efb 100644 --- a/libobs/obs-properties.c +++ b/libobs/obs-properties.c @@ -726,6 +726,30 @@ enum obs_combo_format obs_property_list_format(obs_property_t *p) return data ? data->format : OBS_COMBO_FORMAT_INVALID; } +void obs_property_int_set_limits(obs_property_t *p, + int min, int max, int step) +{ + struct int_data *data = get_type_data(p, OBS_PROPERTY_INT); + if (!data) + return; + + data->min = min; + data->max = max; + data->step = step; +} + +void obs_property_float_set_limits(obs_property_t *p, + double min, double max, double step) +{ + struct float_data *data = get_type_data(p, OBS_PROPERTY_INT); + if (!data) + return; + + data->min = min; + data->max = max; + data->step = step; +} + void obs_property_list_clear(obs_property_t *p) { struct list_data *data = get_list_data(p); diff --git a/libobs/obs-properties.h b/libobs/obs-properties.h index c3b744606420cc..e280ab4a6ad64a 100644 --- a/libobs/obs-properties.h +++ b/libobs/obs-properties.h @@ -262,6 +262,11 @@ EXPORT const char * obs_property_path_default_path(obs_property_t *p); EXPORT enum obs_combo_type obs_property_list_type(obs_property_t *p); EXPORT enum obs_combo_format obs_property_list_format(obs_property_t *p); +EXPORT void obs_property_int_set_limits(obs_property_t *p, + int min, int max, int step); +EXPORT void obs_property_float_set_limits(obs_property_t *p, + double min, double max, double step); + EXPORT void obs_property_list_clear(obs_property_t *p); EXPORT size_t obs_property_list_add_string(obs_property_t *p, From 4b0f22d05a41680c1406efcc4078eac23cf79ca4 Mon Sep 17 00:00:00 2001 From: Andrei Nistor Date: Thu, 1 Sep 2016 14:48:01 +0300 Subject: [PATCH 061/107] rtmp-services: Add ability to specify tune param Some services need to set up tune=zerolatency for low-latency streaming --- plugins/rtmp-services/rtmp-common.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index a15790cb0f92bd..29b731c9d955a7 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -346,6 +346,12 @@ static void apply_video_encoder_settings(obs_data_t *settings, } } + item = json_object_get(recommended, "tune"); + if (item && json_is_string(item)) { + const char *tune = json_string_value(item); + obs_data_set_string(settings, "tune", tune); + } + item = json_object_get(recommended, "x264opts"); if (item && json_is_string(item)) { const char *x264_settings = json_string_value(item); From 95f364dd29c97ac1dfc67826549da32f78028dc7 Mon Sep 17 00:00:00 2001 From: Andrei Nistor Date: Thu, 1 Sep 2016 14:48:32 +0300 Subject: [PATCH 062/107] rtmp-services: Add CAM4 streaming service Closes jp9000/obs-studio#606 --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index c5204b2a4fae71..6ea390752b60c9 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 34, + "version": 35, "files": [ { "name": "services.json", - "version": 34 + "version": 35 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 2c2ea27e57fab1..3a6a5479fb0c4f 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -789,6 +789,22 @@ "max video bitrate": 5000, "max audio bitrate": 192 } + }, + { + "name": "CAM4", + "servers": [ + { + "name": "CAM4", + "url": "rtmp://origin.cam4.com/cam4-origin-live" + } + ], + "recommended": { + "keyint": 1, + "profile": "baseline", + "tune": "zerolatency", + "max video bitrate": 3000, + "max audio bitrate": 128 + } } ] } From 47aa56b3e9362c6867bd8e988b9dd752f9a7166f Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Mon, 19 Sep 2016 20:35:59 -0400 Subject: [PATCH 063/107] libobs/util: Fix Windows 10 revision detection Sometimes the revision number isn't put in to the kernel32.dll version information. It's best to use the registry in this case. --- libobs/util/platform-windows.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index b4201b9030f411..4f3cb543fe3e55 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -754,6 +754,8 @@ bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info) return true; } +#define WINVER_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion" + void get_win_ver(struct win_version_info *info) { static struct win_version_info ver = {0}; @@ -765,6 +767,26 @@ void get_win_ver(struct win_version_info *info) if (!got_version) { get_dll_ver(L"kernel32", &ver); got_version = true; + + if (ver.major == 10 && ver.revis == 0) { + HKEY key; + DWORD size, win10_revision; + LSTATUS status; + + status = RegOpenKeyW(HKEY_LOCAL_MACHINE, + WINVER_REG_KEY, &key); + if (status != ERROR_SUCCESS) + return; + + size = sizeof(win10_revision); + + status = RegQueryValueExW(key, L"UBR", NULL, NULL, + (LPBYTE)&win10_revision, &size); + if (status == ERROR_SUCCESS) + ver.revis = (int)win10_revision; + + RegCloseKey(key); + } } *info = ver; From 2cf5c5f7f7af5ea3b8484a133b116540cb3b1166 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 19 Sep 2016 17:43:31 -0700 Subject: [PATCH 064/107] libobs/util: Fix get_dll_ver always getting kernel32 info --- libobs/util/platform-windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/util/platform-windows.c b/libobs/util/platform-windows.c index 4f3cb543fe3e55..faadf622c5d3b5 100644 --- a/libobs/util/platform-windows.c +++ b/libobs/util/platform-windows.c @@ -732,7 +732,7 @@ bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info) } data = bmalloc(size); - if (!get_file_version_info(L"kernel32", 0, size, data)) { + if (!get_file_version_info(lib, 0, size, data)) { blog(LOG_ERROR, "Failed to get windows version info"); bfree(data); return false; From ba47bcafc8ebe161dc2525ecd3d247de94205ccb Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 19 Sep 2016 17:57:59 -0700 Subject: [PATCH 065/107] Revert "rtmp-services: Add ability to specify tune param" This reverts commit 4b0f22d05a41680c1406efcc4078eac23cf79ca4. Services should not force tunes. --- plugins/rtmp-services/rtmp-common.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 29b731c9d955a7..a15790cb0f92bd 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -346,12 +346,6 @@ static void apply_video_encoder_settings(obs_data_t *settings, } } - item = json_object_get(recommended, "tune"); - if (item && json_is_string(item)) { - const char *tune = json_string_value(item); - obs_data_set_string(settings, "tune", tune); - } - item = json_object_get(recommended, "x264opts"); if (item && json_is_string(item)) { const char *x264_settings = json_string_value(item); From 6736aeebde98e7cacdca2ea8f29a318556e92c94 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 19 Sep 2016 17:58:15 -0700 Subject: [PATCH 066/107] rtmp-services: Remove forced zerolatency on service --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 6ea390752b60c9..1bc5aa9a59d6ae 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 35, + "version": 36, "files": [ { "name": "services.json", - "version": 35 + "version": 36 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 3a6a5479fb0c4f..22af2e6676dbfe 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -801,7 +801,6 @@ "recommended": { "keyint": 1, "profile": "baseline", - "tune": "zerolatency", "max video bitrate": 3000, "max audio bitrate": 128 } From 84613cecff7cd86818d5eb341f2918cb2d9be24f Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 20 Sep 2016 05:07:30 -0700 Subject: [PATCH 067/107] win-dshow: Disable plugin and warn if submodule not found --- plugins/win-dshow/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/win-dshow/CMakeLists.txt b/plugins/win-dshow/CMakeLists.txt index f34e978b283d11..4a9f6f87ea2718 100644 --- a/plugins/win-dshow/CMakeLists.txt +++ b/plugins/win-dshow/CMakeLists.txt @@ -1,3 +1,8 @@ +if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libdshowcapture/dshowcapture.hpp") + message(STATUS "libdshowcapture submodule not found! Please fetch submodules. win-dshow plugin disabled.") + return() +endif() + project(win-dshow) find_package(FFmpeg REQUIRED COMPONENTS avcodec avutil) From 7de289f6cc499b2591bc3390cb5f989aa8b455c6 Mon Sep 17 00:00:00 2001 From: Christoph Hohmann Date: Tue, 20 Sep 2016 16:18:38 +0200 Subject: [PATCH 068/107] UI: Fix crash in filters dialog caused by access to deleted widget --- UI/window-basic-filters.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/UI/window-basic-filters.cpp b/UI/window-basic-filters.cpp index f055d6168ba3bd..f3bb847f650419 100644 --- a/UI/window-basic-filters.cpp +++ b/UI/window-basic-filters.cpp @@ -146,8 +146,10 @@ inline OBSSource OBSBasicFilters::GetFilter(int row, bool async) void OBSBasicFilters::UpdatePropertiesView(int row, bool async) { - delete view; - view = nullptr; + if (view) { + view->deleteLater(); + view = nullptr; + } OBSSource filter = GetFilter(row, async); if (!filter) From ed616ac5939fb6d01265bcbaa45f1c46f7e9f67a Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Tue, 20 Sep 2016 08:54:25 -0700 Subject: [PATCH 069/107] enc-amf: Add AMD h.264 encoder Closes jp9000/obs-studio#611 --- .gitmodules | 3 +++ plugins/CMakeLists.txt | 8 ++++++++ plugins/enc-amf | 1 + 3 files changed, 12 insertions(+) create mode 160000 plugins/enc-amf diff --git a/.gitmodules b/.gitmodules index b2afcdb8fc24dd..93dea8a0f77066 100644 --- a/.gitmodules +++ b/.gitmodules @@ -5,3 +5,6 @@ [submodule "plugins/mac-syphon/syphon-framework"] path = plugins/mac-syphon/syphon-framework url = https://github.com/palana/Syphon-Framework.git +[submodule "plugins/enc-amf"] + path = plugins/enc-amf + url = https://github.com/Xaymar/OBS-AMD-Advanced-Media-Framework.git diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index e6966589a5b90b..193cdb985695bc 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -11,6 +11,14 @@ if(WIN32) add_subdirectory(win-mf) add_subdirectory(obs-qsv11) add_subdirectory(vlc-video) + option(BUILD_AMF_ENCODER "Build AMD Advanced Media Framework encoder module" OFF) + if (BUILD_AMF_ENCODER) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/enc-amf/CMakeLists.txt") + add_subdirectory(enc-amf) + else() + message(STATUS "enc-amf submodule not found! Please fetch submodules. enc-amf plugin disabled.") + endif() + endif() elseif(APPLE) add_subdirectory(coreaudio-encoder) add_subdirectory(mac-avcapture) diff --git a/plugins/enc-amf b/plugins/enc-amf new file mode 160000 index 00000000000000..33f2581a061cfc --- /dev/null +++ b/plugins/enc-amf @@ -0,0 +1 @@ +Subproject commit 33f2581a061cfc674f54e2a2ef5f8ab8a87890c8 From 69c4dbe4ed8d0323ffac7296b1ef8a1b840e0ad2 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 20 Sep 2016 09:28:49 -0700 Subject: [PATCH 070/107] enc-amf: Remove superfluous locale text --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index 33f2581a061cfc..ca7b02c8a5ef0b 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit 33f2581a061cfc674f54e2a2ef5f8ab8a87890c8 +Subproject commit ca7b02c8a5ef0b2555152c0e9894edf62a28e253 From a4cde72698fa590bc89c27045bbd051bebde258e Mon Sep 17 00:00:00 2001 From: hwdro Date: Tue, 20 Sep 2016 23:48:45 +0300 Subject: [PATCH 071/107] obs-text: Add bk color/opacity properties --- plugins/obs-text/data/locale/en-US.ini | 2 ++ plugins/obs-text/gdiplus/obs-text.cpp | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/plugins/obs-text/data/locale/en-US.ini b/plugins/obs-text/data/locale/en-US.ini index 89c62ceaec3693..a1ada083a19964 100644 --- a/plugins/obs-text/data/locale/en-US.ini +++ b/plugins/obs-text/data/locale/en-US.ini @@ -7,6 +7,8 @@ Filter.TextFiles="Text Files" Filter.AllFiles="All Files" Color="Color" Opacity="Opacity" +BkColor="Background Color" +BkOpacity="Background Opacity" Alignment="Alignment" Alignment.Left="Left" Alignment.Center="Center" diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index e74f73709f3590..3edb1ea142ab03 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -45,6 +45,8 @@ using namespace Gdiplus; #define S_ALIGN "align" #define S_VALIGN "valign" #define S_OPACITY "opacity" +#define S_BKCOLOR "bk_color" +#define S_BKOPACITY "bk_opacity" #define S_VERTICAL "vertical" #define S_OUTLINE "outline" #define S_OUTLINE_SIZE "outline_size" @@ -74,6 +76,8 @@ using namespace Gdiplus; #define T_ALIGN T_("Alignment") #define T_VALIGN T_("VerticalAlignment") #define T_OPACITY T_("Opacity") +#define T_BKCOLOR T_("BkColor") +#define T_BKOPACITY T_("BkOpacity") #define T_VERTICAL T_("Vertical") #define T_OUTLINE T_("Outline") #define T_OUTLINE_SIZE T_("Outline.Size") @@ -643,6 +647,9 @@ inline void TextSource::Update(obs_data_t *s) bool new_underline = (font_flags & OBS_FONT_UNDERLINE) != 0; bool new_strikeout = (font_flags & OBS_FONT_STRIKEOUT) != 0; + uint32_t new_bk_color = obs_data_get_uint32(s, S_BKCOLOR); + uint32_t new_bk_opacity = obs_data_get_uint32(s, S_BKOPACITY); + /* ----------------------------- */ wstring new_face = to_wide(font_face); @@ -668,11 +675,14 @@ inline void TextSource::Update(obs_data_t *s) new_color = rgb_to_bgr(new_color); new_o_color = rgb_to_bgr(new_o_color); + new_bk_color = rgb_to_bgr(new_bk_color); color = new_color; opacity = new_opacity; vertical = new_vertical; + bk_color = new_bk_color; + bk_opacity = new_bk_opacity; use_extents = new_extents; wrap = new_extents_wrap; extents_cx = n_extents_cx; @@ -847,6 +857,10 @@ static obs_properties_t *get_properties(void *data) obs_properties_add_color(props, S_COLOR, T_COLOR); obs_properties_add_int_slider(props, S_OPACITY, T_OPACITY, 0, 100, 1); + + obs_properties_add_color(props, S_BKCOLOR, T_BKCOLOR); + obs_properties_add_int_slider(props, S_BKOPACITY, T_BKOPACITY, + 0, 100, 1); p = obs_properties_add_list(props, S_ALIGN, T_ALIGN, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); @@ -923,6 +937,8 @@ bool obs_module_load(void) obs_data_set_default_string(settings, S_VALIGN, S_VALIGN_TOP); obs_data_set_default_int(settings, S_COLOR, 0xFFFFFF); obs_data_set_default_int(settings, S_OPACITY, 100); + obs_data_set_default_int(settings, S_BKCOLOR, 0x000000); + obs_data_set_default_int(settings, S_BKOPACITY, 0); obs_data_set_default_int(settings, S_OUTLINE_SIZE, 2); obs_data_set_default_int(settings, S_OUTLINE_COLOR, 0xFFFFFF); obs_data_set_default_int(settings, S_OUTLINE_OPACITY, 100); From d4c4d7b674f1005f550fbb16d54fd01b9ae14bf5 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Wed, 21 Sep 2016 08:16:19 -0700 Subject: [PATCH 072/107] enc-amf: Fix locale typo and add VBR_LAT --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index ca7b02c8a5ef0b..ea238f29f07bbb 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit ca7b02c8a5ef0b2555152c0e9894edf62a28e253 +Subproject commit ea238f29f07bbb3f486087de702e5a75b3e8ab7f From 406a8c89ca21056b2281e855f9f9d46f3062f908 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 21 Sep 2016 21:20:06 -0700 Subject: [PATCH 073/107] UI: Disable hiding if settings open Fixes a bug where you could minimize to tray if the settings window was open. --- UI/window-basic-main.cpp | 6 +++--- UI/window-basic-main.hpp | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 5c80d52dadb020..a0410b8e5555c8 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -2701,9 +2701,11 @@ void OBSBasic::on_actionRemux_triggered() void OBSBasic::on_action_Settings_triggered() { + disableHiding = true; OBSBasicSettings settings(this); settings.exec(); SystemTray(false); + disableHiding = false; } void OBSBasic::on_actionAdvAudioProperties_triggered() @@ -3950,9 +3952,7 @@ void OBSBasic::on_recordButton_clicked() void OBSBasic::on_settingsButton_clicked() { - OBSBasicSettings settings(this); - settings.exec(); - SystemTray(false); + on_action_Settings_triggered(); } void OBSBasic::on_actionWebsite_triggered() diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 792742d62f77f3..04453fecf2c1d3 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -150,6 +150,7 @@ class OBSBasic : public OBSMainWindow { QAction *showHide; QAction *showPreview; QAction *exit; + bool disableHiding = false; void DrawBackdrop(float cx, float cy); @@ -362,7 +363,10 @@ private slots: inline void ToggleShowHide() { - SetShowing(!isVisible()); + bool showing = isVisible(); + if (disableHiding && showing) + return; + SetShowing(!showing); } private: From b9b6f70cd50714f5f6a495ae296a0669eafff08e Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 21 Sep 2016 21:20:49 -0700 Subject: [PATCH 074/107] UI: Make sure all dialogs are closed when hiding window Fixes a crash that could happen where you close the window while the main window is hiding, and then the app mistakenly thinks that "all windows are closed" and initiates shutdown while the main window is still active but hidden. --- UI/window-basic-main.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 04453fecf2c1d3..da1faaa016644a 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -366,6 +366,8 @@ private slots: bool showing = isVisible(); if (disableHiding && showing) return; + if (showing) + CloseDialogs(); SetShowing(!showing); } From 108a4aa40ecedbf99157bf79a8f3814a3cfc8415 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 21 Sep 2016 21:44:03 -0700 Subject: [PATCH 075/107] obs-outputs: Add max shutdown timeout (30 seconds) The maximum shutdown timeout value was added as a setting, but never actually fully implemented. This implements it properly, and sets its default timeout value to 30 seconds. --- plugins/obs-outputs/rtmp-stream.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c index 1d10f6129414ec..179bee53767def 100644 --- a/plugins/obs-outputs/rtmp-stream.c +++ b/plugins/obs-outputs/rtmp-stream.c @@ -78,6 +78,7 @@ struct rtmp_stream { os_sem_t *send_sem; os_event_t *stop_event; uint64_t stop_ts; + uint64_t shutdown_timeout_ts; struct dstr path, key; struct dstr username, password; @@ -234,6 +235,10 @@ static void rtmp_stream_stop(void *data, uint64_t ts) stream->stop_ts = ts / 1000ULL; os_event_signal(stream->stop_event); + if (ts) + stream->shutdown_timeout_ts = ts + + (uint64_t)stream->max_shutdown_time_sec * 1000000000ULL; + if (active(stream)) { if (stream->stop_ts == 0) os_sem_post(stream->send_sem); @@ -378,6 +383,19 @@ static int send_packet(struct rtmp_stream *stream, static inline bool send_headers(struct rtmp_stream *stream); +static inline bool can_shutdown_stream(struct rtmp_stream *stream, + struct encoder_packet *packet) +{ + uint64_t cur_time = os_gettime_ns(); + bool timeout = cur_time >= stream->shutdown_timeout_ts; + + if (timeout) + info("Stream shutdown timeout reached (%d second(s))", + stream->max_shutdown_time_sec); + + return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts; +} + static void *send_thread(void *data) { struct rtmp_stream *stream = data; @@ -395,7 +413,7 @@ static void *send_thread(void *data) continue; if (stopping(stream)) { - if (packet.sys_dts_usec >= (int64_t)stream->stop_ts) { + if (can_shutdown_stream(stream, &packet)) { obs_free_encoder_packet(&packet); break; } @@ -931,7 +949,7 @@ static void rtmp_stream_defaults(obs_data_t *defaults) { obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 500); obs_data_set_default_int(defaults, OPT_PFRAME_DROP_THRESHOLD, 800); - obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 5); + obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 30); obs_data_set_default_string(defaults, OPT_BIND_IP, "default"); } From 7d6bf872e25af5cc710aecc838f49b5405167184 Mon Sep 17 00:00:00 2001 From: Michael Hoang Date: Sun, 21 Aug 2016 20:44:53 +1000 Subject: [PATCH 076/107] linux-alsa: Add ability to specify a custom PCM Closes jp9000/obs-studio#599 --- plugins/linux-alsa/alsa-input.c | 39 ++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/plugins/linux-alsa/alsa-input.c b/plugins/linux-alsa/alsa-input.c index 94ea5efebfbdc9..e9fb2003e54af7 100644 --- a/plugins/linux-alsa/alsa-input.c +++ b/plugins/linux-alsa/alsa-input.c @@ -64,6 +64,8 @@ struct alsa_data { }; static const char * alsa_get_name(void *); +static bool alsa_devices_changed(obs_properties_t *props, + obs_property_t *p, obs_data_t *settings); static obs_properties_t * alsa_get_properties(void *); static void * alsa_create(obs_data_t *, obs_source_t *); static void alsa_destroy(void *); @@ -119,7 +121,12 @@ void * alsa_create(obs_data_t *settings, obs_source_t *source) data->listen_thread = 0; data->reopen_thread = 0; - data->device = bstrdup(obs_data_get_string(settings, "device_id")); + const char *device = obs_data_get_string(settings, "device_id"); + + if (strcmp(device, "__custom__") == 0) + device = obs_data_get_string(settings, "custom_pcm"); + + data->device = bstrdup(device); data->rate = obs_data_get_int(settings, "rate"); if (os_event_init(&data->abort_event, OS_EVENT_TYPE_MANUAL) != 0) { @@ -179,6 +186,10 @@ void alsa_update(void *vptr, obs_data_t *settings) bool reset = false; device = obs_data_get_string(settings, "device_id"); + + if (strcmp(device, "__custom__") == 0) + device = obs_data_get_string(settings, "custom_pcm"); + if (strcmp(data->device, device) != 0) { bfree(data->device); data->device = bstrdup(device); @@ -215,9 +226,28 @@ const char * alsa_get_name(void *unused) void alsa_get_defaults(obs_data_t *settings) { obs_data_set_default_string(settings, "device_id", "default"); + obs_data_set_default_string(settings, "custom_pcm", "default"); obs_data_set_default_int(settings, "rate", 44100); } +static bool alsa_devices_changed(obs_properties_t *props, + obs_property_t *p, obs_data_t *settings) +{ + UNUSED_PARAMETER(p); + bool visible = false; + const char *device_id = obs_data_get_string(settings, "device_id"); + + if (strcmp(device_id, "__custom__") == 0) + visible = true; + + obs_property_t *custom_pcm = obs_properties_get(props, "custom_pcm"); + + obs_property_set_visible(custom_pcm, visible); + obs_property_modified(custom_pcm, settings); + + return true; +} + obs_properties_t * alsa_get_properties(void *unused) { void **hints; @@ -240,10 +270,15 @@ obs_properties_t * alsa_get_properties(void *unused) obs_property_list_add_string(devices, "Default", "default"); + obs_properties_add_text(props, "custom_pcm", + obs_module_text("PCM"), OBS_TEXT_DEFAULT); + rate = obs_properties_add_list(props, "rate", obs_module_text("Rate"), OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_modified_callback(devices, alsa_devices_changed); + obs_property_list_add_int(rate, "32000 Hz", 32000); obs_property_list_add_int(rate, "44100 Hz", 44100); obs_property_list_add_int(rate, "48000 Hz", 48000); @@ -277,6 +312,8 @@ obs_properties_t * alsa_get_properties(void *unused) obs_property_list_add_string(devices, descr, name); + obs_property_list_add_string(devices, "Custom", "__custom__"); + next: if (name != NULL) free(name), name = NULL; From 43dd63aeafcc6f59249774826149347659762ec6 Mon Sep 17 00:00:00 2001 From: Christoph Hohmann Date: Sun, 28 Aug 2016 02:56:59 +0200 Subject: [PATCH 077/107] pulse-input: Fix design flaw where source is not created When a pulseaudio source is created from existing settings and the device is not found, then the private data will return NULL, making it impossible to change the source to use a different device. NULL should never be returned when possible, otherwise settings cannot be changed and properties cannot be displayed. Closes jp9000/obs-studio#604 --- plugins/linux-pulseaudio/pulse-input.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/linux-pulseaudio/pulse-input.c b/plugins/linux-pulseaudio/pulse-input.c index 563773e55f21a8..463e0c25197a59 100644 --- a/plugins/linux-pulseaudio/pulse-input.c +++ b/plugins/linux-pulseaudio/pulse-input.c @@ -183,7 +183,13 @@ static void pulse_source_info(pa_context *c, const pa_source_info *i, int eol, { UNUSED_PARAMETER(c); PULSE_DATA(userdata); - if (eol != 0) + // An error occured + if (eol < 0) { + data->format = PA_SAMPLE_INVALID; + goto skip; + } + // Terminating call for multi instance callbacks + if (eol > 0) goto skip; blog(LOG_INFO, "Audio format: %s, %"PRIu32" Hz" @@ -242,6 +248,10 @@ static int_fast32_t pulse_start_recording(struct pulse_data *data) blog(LOG_ERROR, "Unable to get source info !"); return -1; } + if (data->format == PA_SAMPLE_INVALID) { + blog(LOG_ERROR, "An error occurred while getting the source info!"); + return -1; + } pa_sample_spec spec; spec.format = data->format; @@ -507,11 +517,7 @@ static void *pulse_create(obs_data_t *settings, obs_source_t *source) pulse_init(); pulse_update(data, settings); - if (data->stream) - return data; - - pulse_destroy(data); - return NULL; + return data; } struct obs_source_info pulse_input_capture = { From ff0557465482d0810eb460655805741d5383df35 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 22 Sep 2016 20:57:43 -0700 Subject: [PATCH 078/107] obs-text: Change default font size to 36 --- plugins/obs-text/gdiplus/obs-text.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index 3edb1ea142ab03..c8681c8d848f2c 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -930,7 +930,7 @@ bool obs_module_load(void) { obs_data_t *font_obj = obs_data_create(); obs_data_set_default_string(font_obj, "face", "Arial"); - obs_data_set_default_int(font_obj, "size", 22); + obs_data_set_default_int(font_obj, "size", 36); obs_data_set_default_obj(settings, S_FONT, font_obj); obs_data_set_default_string(settings, S_ALIGN, S_ALIGN_LEFT); From 300dc8838d686df13abda8c54ac39abe14637d96 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Fri, 23 Sep 2016 00:18:06 -0400 Subject: [PATCH 079/107] libobs: Add downscale filter to log --- libobs/obs.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/libobs/obs.c b/libobs/obs.c index dbda425f094103..685d892075027d 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -925,14 +925,35 @@ int obs_reset_video(struct obs_video_info *ovi) } } + const char *scale_type_name = ""; + switch (ovi->scale_type) { + case OBS_SCALE_DISABLE: + scale_type_name = "Disabled"; + break; + case OBS_SCALE_POINT: + scale_type_name = "Point"; + break; + case OBS_SCALE_BICUBIC: + scale_type_name = "Bicubic"; + break; + case OBS_SCALE_BILINEAR: + scale_type_name = "Bilinear"; + break; + case OBS_SCALE_LANCZOS: + scale_type_name = "Lanczos"; + break; + } + blog(LOG_INFO, "---------------------------------"); blog(LOG_INFO, "video settings reset:\n" "\tbase resolution: %dx%d\n" "\toutput resolution: %dx%d\n" + "\tdownscale filter: %s\n" "\tfps: %d/%d\n" "\tformat: %s", ovi->base_width, ovi->base_height, ovi->output_width, ovi->output_height, + scale_type_name, ovi->fps_num, ovi->fps_den, get_video_format_name(ovi->output_format)); From 11ed262a36661fe81eb7d85e8b6882f9d3a5d443 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 23 Sep 2016 11:58:17 -0700 Subject: [PATCH 080/107] libobs: Fix XGetXCBConnection link error on linux A linker error for XGetXCBConnection started occurring after the frontend API was added, the fix is simply to make sure libobs is properly linking to X11_XCB. --- libobs/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 3cf6bd8db066c8..87cdd279813573 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -13,6 +13,7 @@ endif() if(UNIX) find_package(DBus QUIET) + find_package(X11_XCB REQUIRED) else() set(HAVE_DBUS "0") endif() @@ -125,6 +126,14 @@ elseif(UNIX) ${DBUS_LIBRARIES}) endif() + include_directories( + ${X11_XCB_INCLUDE_DIRS}) + add_definitions( + ${X11_XCB_DEFINITIONS}) + set(libobs_PLATFORM_DEPS + ${libobs_PLATFORM_DEPS} + ${X11_XCB_LIBRARIES}) + if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") # use the sysinfo compatibility library on bsd find_package(Libsysinfo REQUIRED) From d96092a0c9a1fd797e0edf19c48e5f7fb38efc60 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 23 Sep 2016 12:15:02 -0700 Subject: [PATCH 081/107] libobs: Make sure X11_XCB is not linked with apple systems --- libobs/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 87cdd279813573..fa19fd99a0258d 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -13,7 +13,9 @@ endif() if(UNIX) find_package(DBus QUIET) - find_package(X11_XCB REQUIRED) + if (NOT APPLE) + find_package(X11_XCB REQUIRED) + endif() else() set(HAVE_DBUS "0") endif() From e3a1ba0c7739eba41426110b3570baeecffde315 Mon Sep 17 00:00:00 2001 From: Robin Hielscher Date: Mon, 26 Sep 2016 10:31:08 +0200 Subject: [PATCH 082/107] rtmp-services: Update Facebook Live recommendations (#629) * rtmp-services: Update Facebook Live recommendations * Update package.json --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 1bc5aa9a59d6ae..190f3ac89afb96 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 36, + "version": 37, "files": [ { "name": "services.json", - "version": 36 + "version": 37 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 22af2e6676dbfe..46d2f77a3f5853 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -545,8 +545,8 @@ "recommended": { "keyint": 2, "profile": "main", - "max video bitrate": 2500, - "max audio bitrate": 160 + "max video bitrate": 4000, + "max audio bitrate": 128 } }, { From 7ea59ff3b4cc3b34a0159f457b5e01549eee61e7 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Mon, 26 Sep 2016 19:24:15 +0200 Subject: [PATCH 083/107] rtmp-services: Remove Twitch H264 profile requirement Twitch no longer recommend Main profile on their "Broadcast requirements" page. The number of devices that can't decode High profile is very small and limited to old, legacy devices such as the 1st generation iPad (six years old). Additionally, these devices have limited resolution / FPS support, so streams are often encoded at levels beyond what they could decode even disregarding the H264 profile requirement. It's not worth degrading the quality of streams for modern devices just to support these legacy decoders. Twitch still provides low-complexity transcodes for devices which cannot handle Source quality streams. --- plugins/rtmp-services/data/package.json | 4 ++-- plugins/rtmp-services/data/services.json | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index 190f3ac89afb96..99ef04c2c3e487 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,10 +1,10 @@ { "url": "https://obsproject.com/obs2_update/rtmp-services", - "version": 37, + "version": 38, "files": [ { "name": "services.json", - "version": 37 + "version": 38 } ] } diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 46d2f77a3f5853..1133d566f76989 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -112,7 +112,6 @@ ], "recommended": { "keyint": 2, - "profile": "main", "max video bitrate": 3500, "max audio bitrate": 160, "x264opts": "scenecut=0" From 1e0d5e7b3e68ca3ca67df8bc3cae4d5d141a1036 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 26 Sep 2016 14:44:35 -0700 Subject: [PATCH 084/107] obs-ffmpeg: Add b-frame support to NVENC (default: 2) It was unintentionally using intra-only before, impacting quality. --- plugins/obs-ffmpeg/data/locale/en-US.ini | 2 ++ plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/plugins/obs-ffmpeg/data/locale/en-US.ini b/plugins/obs-ffmpeg/data/locale/en-US.ini index 959d7512522caa..7dff1cef16e1be 100644 --- a/plugins/obs-ffmpeg/data/locale/en-US.ini +++ b/plugins/obs-ffmpeg/data/locale/en-US.ini @@ -6,6 +6,8 @@ RateControl="Rate Control" KeyframeIntervalSec="Keyframe Interval (seconds, 0=auto)" Lossless="Lossless" +BFrames="B-frames" + NVENC.Use2Pass="Use Two-Pass Encoding" NVENC.Preset.default="Default" NVENC.Preset.hq="High Quality" diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index a1d673f96690ef..4b693c02caacf7 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -138,6 +138,7 @@ static bool nvenc_update(void *data, obs_data_t *settings) bool twopass = obs_data_get_bool(settings, "2pass"); int gpu = (int)obs_data_get_int(settings, "gpu"); bool cbr_override = obs_data_get_bool(settings, "cbr"); + int bf = (int)obs_data_get_int(settings, "bf"); video_t *video = obs_encoder_video(enc->encoder); const struct video_output_info *voi = video_output_get_info(video); @@ -198,6 +199,7 @@ static bool nvenc_update(void *data, obs_data_t *settings) AVCOL_SPC_BT709 : AVCOL_SPC_BT470BG; enc->context->color_range = info.range == VIDEO_RANGE_FULL ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + enc->context->max_b_frames = bf; if (keyint_sec) enc->context->gop_size = keyint_sec * voi->fps_num / @@ -373,6 +375,7 @@ static void nvenc_defaults(obs_data_t *settings) obs_data_set_default_string(settings, "level", "auto"); obs_data_set_default_bool(settings, "2pass", true); obs_data_set_default_int(settings, "gpu", 0); + obs_data_set_default_int(settings, "bf", 2); } static bool rate_control_modified(obs_properties_t *ppts, obs_property_t *p, @@ -482,6 +485,9 @@ static obs_properties_t *nvenc_properties(void *unused) obs_module_text("NVENC.Use2Pass")); obs_properties_add_int(props, "gpu", obs_module_text("GPU"), 0, 8, 1); + obs_properties_add_int(props, "bf", obs_module_text("BFrames"), + 0, 4, 1); + return props; } From 7ee3dfe9e4c4b1103d969dadcfc9d79edb4bf7b2 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 26 Sep 2016 14:46:33 -0700 Subject: [PATCH 085/107] enc-amf: Update to 1.3.0rc6 --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index ea238f29f07bbb..15e5b389be9c11 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit ea238f29f07bbb3f486087de702e5a75b3e8ab7f +Subproject commit 15e5b389be9c1161ebea6b7e245c104a0681462e From 4d23e848370a26bad8bb544aff7693854c557cae Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Tue, 27 Sep 2016 00:23:39 +0200 Subject: [PATCH 086/107] librtmp: Remove reconnect on close behavior For some reason librtmp treats a close message as a request to reconnect. It does this syncronously, and uses the same event processing so that another close request will continue the cycle until the stack is exhausted and the application crashes. Fixes https://obsproject.com/mantis/view.php?id=634 --- plugins/obs-outputs/librtmp/rtmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/obs-outputs/librtmp/rtmp.c b/plugins/obs-outputs/librtmp/rtmp.c index 404c6b9d22b604..6b287a27186295 100644 --- a/plugins/obs-outputs/librtmp/rtmp.c +++ b/plugins/obs-outputs/librtmp/rtmp.c @@ -3104,7 +3104,10 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) { RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); RTMP_Close(r); -#if defined(CRYPTO) || defined(USE_ONLY_MD5) + + // disabled this for now, if the server sends an rtmp close message librtmp + // will enter an infinite loop here until stack is exhausted. +#if 0 && (defined(CRYPTO) || defined(USE_ONLY_MD5)) if ((r->Link.protocol & RTMP_FEATURE_WRITE) && !(r->Link.pFlags & RTMP_PUB_CLEAN) && ( !(r->Link.pFlags & RTMP_PUB_NAME) || From 857cf00a39b7be4dbbe2b6a0cfe6378116c7b042 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Mon, 26 Sep 2016 16:29:33 -0700 Subject: [PATCH 087/107] UI: Implement simple output mode presets for AMD Closes jp9000/obs-studio#630 --- UI/data/locale/en-US.ini | 1 + UI/window-basic-main-outputs.cpp | 64 ++++++++++++++++++++++++++++++-- UI/window-basic-main.hpp | 1 + UI/window-basic-settings.cpp | 14 +++++++ 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/UI/data/locale/en-US.ini b/UI/data/locale/en-US.ini index 6c698a1b7d6039..3d6f196bd6233e 100644 --- a/UI/data/locale/en-US.ini +++ b/UI/data/locale/en-US.ini @@ -421,6 +421,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless quality warning!" Basic.Settings.Output.Simple.Warn.MultipleQSV="Warning: You cannot use multiple separate QSV encoders when streaming and recording at the same time. If you want to stream and record at the same time, please change either the recording encoder or the stream encoder." Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardware (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 low CPU usage preset, increases file size)" Basic.Settings.Output.VideoBitrate="Video Bitrate" diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index 9a864df0a8e9ed..b703a4fb4a9ad2 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -161,9 +161,11 @@ struct SimpleOutput : BasicOutputHandler { int CalcCRF(int crf); + void UpdateStreamingSettings_amd(obs_data_t *settings, int bitrate); void UpdateRecordingSettings_x264_crf(int crf); void UpdateRecordingSettings_qsv11(int crf); void UpdateRecordingSettings_nvenc(int cqp); + void UpdateRecordingSettings_amd_cqp(int cqp); void UpdateRecordingSettings(); void UpdateRecordingAudioSettings(); virtual void Update() override; @@ -254,6 +256,8 @@ void SimpleOutput::LoadRecordingPreset() lowCPUx264 = true; } else if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) { LoadRecordingPreset_h264("obs_qsv11"); + } else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) { + LoadRecordingPreset_h264("amd_amf_h264"); } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) { LoadRecordingPreset_h264("ffmpeg_nvenc"); } @@ -278,6 +282,8 @@ SimpleOutput::SimpleOutput(OBSBasic *main_) : BasicOutputHandler(main_) "StreamEncoder"); if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) LoadStreamingPreset_h264("obs_qsv11"); + else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) + LoadStreamingPreset_h264("amd_amf_h264"); else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) LoadStreamingPreset_h264("ffmpeg_nvenc"); else @@ -343,12 +349,19 @@ void SimpleOutput::Update() const char *presetType; const char *preset; - if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) + if (strcmp(encoder, SIMPLE_ENCODER_QSV) == 0) { presetType = "QSVPreset"; - else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) + + } else if (strcmp(encoder, SIMPLE_ENCODER_AMD) == 0) { + presetType = "AMDPreset"; + UpdateStreamingSettings_amd(h264Settings, videoBitrate); + + } else if (strcmp(encoder, SIMPLE_ENCODER_NVENC) == 0) { presetType = "NVENCPreset"; - else + + } else { presetType = "Preset"; + } preset = config_get_string(main->Config(), "SimpleOutput", presetType); @@ -485,6 +498,48 @@ void SimpleOutput::UpdateRecordingSettings_nvenc(int cqp) obs_data_release(settings); } +void SimpleOutput::UpdateStreamingSettings_amd(obs_data_t *settings, + int bitrate) +{ + int bits = bitrate * 1000; + obs_data_set_int(settings, "AMF.H264.Usage", 0); + obs_data_set_int(settings, "AMF.H264.QualityPreset", 2); + obs_data_set_int(settings, "AMF.H264.ProfileLevel", 51); + obs_data_set_int(settings, "AMF.H264.FillerData", 1); + obs_data_set_int(settings, "AMF.H264.FrameSkipping", -1); + obs_data_set_int(settings, "AMF.H264.BPicture.Pattern", 2); + obs_data_set_int(settings, "AMF.H264.BPicture.Reference", 1); + obs_data_set_int(settings, "AMF.H264.Bitrate.Target", bits); + obs_data_set_int(settings, "AMF.H264.Bitrate.Peak", bits); + obs_data_set_int(settings, "AMF.H264Advanced.VBVBuffer.Size", bits); + obs_data_set_string(settings, "profile", "high"); +} + +void SimpleOutput::UpdateRecordingSettings_amd_cqp(int cqp) +{ + obs_data_t *settings = obs_data_create(); + + obs_data_set_int(settings, "AMF.H264.Usage", 0); + obs_data_set_int(settings, "AMF.H264.QualityPreset", 2); + obs_data_set_int(settings, "AMF.H264.ProfileLevel", 51); + obs_data_set_int(settings, "AMF.H264.FillerData", 0); + obs_data_set_int(settings, "AMF.H264.FrameSkipping", 0); + obs_data_set_int(settings, "AMF.H264.QP.Minimum", 0); + obs_data_set_int(settings, "AMF.H264.QP.Maximum", 51); + obs_data_set_int(settings, "AMF.H264.QP.IFrame", cqp); + obs_data_set_int(settings, "AMF.H264.QP.PFrame", cqp); + obs_data_set_int(settings, "AMF.H264.QP.BFrame", cqp); + obs_data_set_int(settings, "AMF.H264.BPicture.Pattern", 3); + obs_data_set_int(settings, "AMF.H264.BPicture.Reference", 1); + obs_data_set_int(settings, "keyint_sec", 1); + obs_data_set_string(settings, "rate_control", "CQP"); + obs_data_set_string(settings, "profile", "high"); + + obs_encoder_update(h264Recording, settings); + + obs_data_release(settings); +} + void SimpleOutput::UpdateRecordingSettings() { bool ultra_hq = (videoQuality == "HQ"); @@ -496,6 +551,9 @@ void SimpleOutput::UpdateRecordingSettings() } else if (videoEncoder == SIMPLE_ENCODER_QSV) { UpdateRecordingSettings_qsv11(crf); + } else if (videoEncoder == SIMPLE_ENCODER_AMD) { + UpdateRecordingSettings_amd_cqp(crf); + } else if (videoEncoder == SIMPLE_ENCODER_NVENC) { UpdateRecordingSettings_nvenc(crf); } diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index da1faaa016644a..93c41ea91f69f3 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -54,6 +54,7 @@ class QNetworkReply; #define SIMPLE_ENCODER_X264_LOWCPU "x264_lowcpu" #define SIMPLE_ENCODER_QSV "qsv" #define SIMPLE_ENCODER_NVENC "nvenc" +#define SIMPLE_ENCODER_AMD "amd" #define PREVIEW_EDGE_SIZE 10 diff --git a/UI/window-basic-settings.cpp b/UI/window-basic-settings.cpp index 73d3ab58fb60d6..aab5c16fe5afdc 100644 --- a/UI/window-basic-settings.cpp +++ b/UI/window-basic-settings.cpp @@ -2467,6 +2467,8 @@ void OBSBasicSettings::SaveOutputSettings() presetType = "QSVPreset"; else if (encoder == SIMPLE_ENCODER_NVENC) presetType = "NVENCPreset"; + else if (encoder == SIMPLE_ENCODER_AMD) + presetType = "AMDPreset"; else presetType = "Preset"; @@ -3246,6 +3248,10 @@ void OBSBasicSettings::FillSimpleRecordingValues() ui->simpleOutRecEncoder->addItem( ENCODER_STR("Hardware.NVENC"), QString(SIMPLE_ENCODER_NVENC)); + if (EncoderAvailable("amd_amf_h264")) + ui->simpleOutRecEncoder->addItem( + ENCODER_STR("Hardware.AMD"), + QString(SIMPLE_ENCODER_AMD)); #undef ADD_QUALITY } @@ -3262,6 +3268,10 @@ void OBSBasicSettings::FillSimpleStreamingValues() ui->simpleOutStrEncoder->addItem( ENCODER_STR("Hardware.NVENC"), QString(SIMPLE_ENCODER_NVENC)); + if (EncoderAvailable("amd_amf_h264")) + ui->simpleOutStrEncoder->addItem( + ENCODER_STR("Hardware.AMD"), + QString(SIMPLE_ENCODER_AMD)); #undef ENCODER_STR } @@ -3322,6 +3332,10 @@ void OBSBasicSettings::SimpleStreamingEncoderChanged() defaultPreset = "default"; preset = curNVENCPreset; + } else if (encoder == SIMPLE_ENCODER_AMD) { + /* none */ + defaultPreset = ""; + } else { ui->simpleOutPreset->addItem("ultrafast", "ultrafast"); ui->simpleOutPreset->addItem("superfast", "superfast"); From 66d0e25a864a1c13e7c9d58f352b0c19e1e53c35 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 26 Sep 2016 18:53:23 -0700 Subject: [PATCH 088/107] enc-amf: Update submodule to 1.3.0 --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index 15e5b389be9c11..1c62b1a9f4e8e5 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit 15e5b389be9c1161ebea6b7e245c104a0681462e +Subproject commit 1c62b1a9f4e8e5d0c917a323966747c567d58ca1 From eb1fecdd349cc17286f3358d71d2eadeb9e841ff Mon Sep 17 00:00:00 2001 From: jp9000 Date: Mon, 26 Sep 2016 12:40:23 -0700 Subject: [PATCH 089/107] UI: Add media/image file drop support --- UI/CMakeLists.txt | 1 + UI/platform-windows.cpp | 8 ++ UI/platform.hpp | 1 + UI/window-basic-main-dropfiles.cpp | 142 +++++++++++++++++++++++++++++ UI/window-basic-main.cpp | 3 + UI/window-basic-main.hpp | 6 ++ 6 files changed, 161 insertions(+) create mode 100644 UI/window-basic-main-dropfiles.cpp diff --git a/UI/CMakeLists.txt b/UI/CMakeLists.txt index 1a415345f96d24..69cd6937607564 100644 --- a/UI/CMakeLists.txt +++ b/UI/CMakeLists.txt @@ -112,6 +112,7 @@ set(obs_SOURCES window-basic-source-select.cpp window-basic-main-scene-collections.cpp window-basic-main-transitions.cpp + window-basic-main-dropfiles.cpp window-basic-main-profiles.cpp window-license-agreement.cpp window-basic-status-bar.cpp diff --git a/UI/platform-windows.cpp b/UI/platform-windows.cpp index 4afde448f5797f..8edde6ec32a3d8 100644 --- a/UI/platform-windows.cpp +++ b/UI/platform-windows.cpp @@ -230,3 +230,11 @@ void SetProcessPriority(const char *priority) else if (strcmp(priority, "Idle") == 0) SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS); } + +void SetWin32DropStyle(QWidget *window) +{ + HWND hwnd = (HWND)window->winId(); + LONG_PTR ex_style = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + ex_style |= WS_EX_ACCEPTFILES; + SetWindowLongPtr(hwnd, GWL_EXSTYLE, ex_style); +} diff --git a/UI/platform.hpp b/UI/platform.hpp index d5e02d161b851d..ad579d62540d31 100644 --- a/UI/platform.hpp +++ b/UI/platform.hpp @@ -52,6 +52,7 @@ void SetAlwaysOnTop(QWidget *window, bool enable); uint32_t GetWindowsVersion(); void SetAeroEnabled(bool enable); void SetProcessPriority(const char *priority); +void SetWin32DropStyle(QWidget *window); #endif #ifdef __APPLE__ diff --git a/UI/window-basic-main-dropfiles.cpp b/UI/window-basic-main-dropfiles.cpp new file mode 100644 index 00000000000000..64ae7f684fb116 --- /dev/null +++ b/UI/window-basic-main-dropfiles.cpp @@ -0,0 +1,142 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "window-basic-main.hpp" +#include "qt-wrappers.hpp" + +using namespace std; + +static const char *imageExtensions[] = { + "bmp", "tga", "png", "jpg", "jpeg", "gif", nullptr +}; + +static const char *mediaExtensions[] = { + "3ga", "669", "a52", "aac", "ac3", "adt", "adts", "aif", "aifc", + "aiff", "amb", "amr", "aob", "ape", "au", "awb", "caf", "dts", + "flac", "it", "kar", "m4a", "m4b", "m4p", "m5p", "mid", "mka", + "mlp", "mod", "mpa", "mp1", "mp2", "mp3", "mpc", "mpga", "mus", + "oga", "ogg", "oma", "opus", "qcp", "ra", "rmi", "s3m", "sid", + "spx", "tak", "thd", "tta", "voc", "vqf", "w64", "wav", "wma", + "wv", "xa", "xm" "3g2", "3gp", "3gp2", "3gpp", "amv", "asf", "avi", + "bik", "crf", "divx", "drc", "dv", "evo", "f4v", "flv", "gvi", + "gxf", "iso", "m1v", "m2v", "m2t", "m2ts", "m4v", "mkv", "mov", + "mp2", "mp2v", "mp4", "mp4v", "mpe", "mpeg", "mpeg1", "mpeg2", + "mpeg4", "mpg", "mpv2", "mts", "mtv", "mxf", "mxg", "nsv", "nuv", + "ogg", "ogm", "ogv", "ogx", "ps", "rec", "rm", "rmvb", "rpl", "thp", + "tod", "ts", "tts", "txd", "vob", "vro", "webm", "wm", "wmv", "wtv", + nullptr +}; + +static string GenerateSourceName(const char *base) +{ + string name; + int inc = 0; + + for (;; inc++) { + name = base; + + if (inc) { + name += " ("; + name += to_string(inc+1); + name += ")"; + } + + obs_source_t *source = obs_get_source_by_name(name.c_str()); + if (!source) + return name; + } +} + +void OBSBasic::AddDropSource(const char *file, bool image) +{ + OBSBasic *main = reinterpret_cast(App()->GetMainWindow()); + obs_data_t *settings = obs_data_create(); + obs_source_t *source = nullptr; + const char *type = nullptr; + + if (image) { + obs_data_set_string(settings, "file", file); + type = "image_source"; + } else { + obs_data_set_string(settings, "local_file", file); + type = "ffmpeg_source"; + } + + const char *name = obs_source_get_display_name(type); + source = obs_source_create(type, GenerateSourceName(name).c_str(), + settings, nullptr); + if (source) { + OBSScene scene = main->GetCurrentScene(); + obs_scene_add(scene, source); + obs_source_release(source); + } + + obs_data_release(settings); +} + +void OBSBasic::dragEnterEvent(QDragEnterEvent *event) +{ + event->acceptProposedAction(); +} + +void OBSBasic::dragLeaveEvent(QDragLeaveEvent *event) +{ + event->accept(); +} + +void OBSBasic::dragMoveEvent(QDragMoveEvent *event) +{ + event->acceptProposedAction(); +} + +void OBSBasic::dropEvent(QDropEvent *event) +{ + const QMimeData* mimeData = event->mimeData(); + + if (mimeData->hasUrls()) { + QList urls = mimeData->urls(); + + for (int i = 0; i < urls.size() && i < 5; i++) { + QString file = urls.at(i).toLocalFile(); + QFileInfo fileInfo(file); + + if (!fileInfo.exists()) + continue; + + QString suffixQStr = fileInfo.suffix(); + QByteArray suffixArray = suffixQStr.toUtf8(); + const char *suffix = suffixArray.constData(); + bool found = false; + + const char **cmp = imageExtensions; + while (*cmp) { + if (strcmp(*cmp, suffix) == 0) { + AddDropSource(QT_TO_UTF8(file), true); + found = true; + break; + } + + cmp++; + } + + if (found) + continue; + + cmp = mediaExtensions; + while (*cmp) { + if (strcmp(*cmp, suffix) == 0) { + AddDropSource(QT_TO_UTF8(file), false); + break; + } + + cmp++; + } + } + } +} + diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index a0410b8e5555c8..e795d78359c5d3 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -120,6 +120,8 @@ OBSBasic::OBSBasic(QWidget *parent) : OBSMainWindow (parent), ui (new Ui::OBSBasic) { + setAcceptDrops(true); + ui->setupUi(this); ui->previewDisabledLabel->setVisible(false); @@ -1166,6 +1168,7 @@ void OBSBasic::OBSInit() connect(ui->preview, &OBSQTDisplay::DisplayCreated, addDisplay); #ifdef _WIN32 + SetWin32DropStyle(this); show(); #endif diff --git a/UI/window-basic-main.hpp b/UI/window-basic-main.hpp index 93c41ea91f69f3..1aaa735783f5db 100644 --- a/UI/window-basic-main.hpp +++ b/UI/window-basic-main.hpp @@ -299,6 +299,12 @@ class OBSBasic : public OBSMainWindow { inline void OnActivate(); inline void OnDeactivate(); + void AddDropSource(const char *file, bool image); + void dragEnterEvent(QDragEnterEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dropEvent(QDropEvent *event) override; + public slots: void StartStreaming(); void StopStreaming(); From b4f9229cf7103960dd3db0c2f67eb9e56c41dbd3 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Tue, 27 Sep 2016 19:46:05 +0200 Subject: [PATCH 090/107] enc-amf: Update submodule to 1.3.0.1 --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index 1c62b1a9f4e8e5..4cdf4693ce1c13 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit 1c62b1a9f4e8e5d0c917a323966747c567d58ca1 +Subproject commit 4cdf4693ce1c1330da4f3bd531566a65ab7b32ac From 677ae3798dbd070b0b822b876115b597c236b206 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 27 Sep 2016 13:33:57 -0700 Subject: [PATCH 091/107] obs-browser: Add browser plugin as a submodule --- .gitmodules | 3 +++ plugins/CMakeLists.txt | 11 +++++++++++ plugins/obs-browser | 1 + 3 files changed, 15 insertions(+) create mode 160000 plugins/obs-browser diff --git a/.gitmodules b/.gitmodules index 93dea8a0f77066..cb9da4185cf595 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,6 @@ [submodule "plugins/enc-amf"] path = plugins/enc-amf url = https://github.com/Xaymar/OBS-AMD-Advanced-Media-Framework.git +[submodule "plugins/obs-browser"] + path = plugins/obs-browser + url = https://github.com/kc5nra/obs-browser.git diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 193cdb985695bc..e728c343ba3ed7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -43,6 +43,17 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") add_subdirectory(linux-jack) endif() +if(WIN32 OR APPLE) + option(BUILD_BROWSER "Build browser plugin" OFF) + if (BUILD_BROWSER) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/obs-browser/CMakeLists.txt") + add_subdirectory(obs-browser) + else() + message(STATUS "obs-browser submodule not found! Please fetch submodules. obs-browser plugin disabled.") + endif() + endif() +endif() + add_subdirectory(image-source) add_subdirectory(obs-x264) add_subdirectory(obs-libfdk) diff --git a/plugins/obs-browser b/plugins/obs-browser new file mode 160000 index 00000000000000..2eaf69f7a3d2ac --- /dev/null +++ b/plugins/obs-browser @@ -0,0 +1 @@ +Subproject commit 2eaf69f7a3d2ac98a45d5cc96124ab7c666bfa2f From 5bc7003d1266c118e1b510c4b55ce5fc518bc17e Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Tue, 27 Sep 2016 23:22:27 +0200 Subject: [PATCH 092/107] enc-amf: Update submodule to 1.3.0.3 --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index 4cdf4693ce1c13..84d7a3e6bd0b8f 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit 4cdf4693ce1c1330da4f3bd531566a65ab7b32ac +Subproject commit 84d7a3e6bd0b8f15afa73995a6f376f1cb2482bf From 8991fdc53e3988d673df86ce2e89b2652e41d94e Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 27 Sep 2016 16:05:51 -0700 Subject: [PATCH 093/107] libobs: Update version to 0.16.0 --- libobs/obs-config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 6450cfad1cf4f9..28fa7e0cee91fd 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -34,14 +34,14 @@ * * Reset to zero each major version */ -#define LIBOBS_API_MINOR_VER 15 +#define LIBOBS_API_MINOR_VER 16 /* * Increment if backward-compatible bug fix * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 4 +#define LIBOBS_API_PATCH_VER 0 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | \ From a8f335117382b0cbc5205ffdb904239ffc0b8803 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 27 Sep 2016 16:13:40 -0700 Subject: [PATCH 094/107] enc-amf: Remove unused locale files --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index 84d7a3e6bd0b8f..ae256c24648133 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit 84d7a3e6bd0b8f15afa73995a6f376f1cb2482bf +Subproject commit ae256c246481335ed727a81802c195c2bffe289b From aadc7263c01f16151f54e31f624a334e7bdf1847 Mon Sep 17 00:00:00 2001 From: Gol-D-Ace Date: Wed, 28 Sep 2016 01:17:24 +0200 Subject: [PATCH 095/107] Update translations from Crowdin --- UI/data/locale/ar-SA.ini | 17 +++ UI/data/locale/bg-BG.ini | 3 + UI/data/locale/ca-ES.ini | 11 ++ UI/data/locale/cs-CZ.ini | 11 ++ UI/data/locale/da-DK.ini | 4 + UI/data/locale/de-DE.ini | 12 ++ UI/data/locale/el-GR.ini | 3 + UI/data/locale/es-ES.ini | 12 ++ UI/data/locale/et-EE.ini | 3 + UI/data/locale/eu-ES.ini | 11 ++ UI/data/locale/fi-FI.ini | 11 ++ UI/data/locale/fr-FR.ini | 15 ++ UI/data/locale/gl-ES.ini | 3 + UI/data/locale/he-IL.ini | 7 + UI/data/locale/hr-HR.ini | 10 ++ UI/data/locale/hu-HU.ini | 12 ++ UI/data/locale/it-IT.ini | 3 + UI/data/locale/ja-JP.ini | 20 ++- UI/data/locale/ko-KR.ini | 11 ++ UI/data/locale/lt-LT.ini | 3 + UI/data/locale/ms-MY.ini | 3 + UI/data/locale/nb-NO.ini | 3 + UI/data/locale/nl-NL.ini | 12 ++ UI/data/locale/pl-PL.ini | 16 ++- UI/data/locale/pt-BR.ini | 21 +++ UI/data/locale/pt-PT.ini | 3 + UI/data/locale/ro-RO.ini | 3 + UI/data/locale/ru-RU.ini | 14 +- UI/data/locale/sk-SK.ini | 3 + UI/data/locale/sl-SI.ini | 3 + UI/data/locale/sr-CS.ini | 10 ++ UI/data/locale/sr-SP.ini | 10 ++ UI/data/locale/sv-SE.ini | 33 +++++ UI/data/locale/ta-IN.ini | 3 + UI/data/locale/th-TH.ini | 3 + UI/data/locale/tr-TR.ini | 3 + UI/data/locale/uk-UA.ini | 13 +- UI/data/locale/vi-VN.ini | 5 +- UI/data/locale/zh-CN.ini | 132 ++++++++++-------- UI/data/locale/zh-TW.ini | 17 ++- .../frontend-tools/data/locale/ca-ES.ini | 11 ++ .../frontend-tools/data/locale/cs-CZ.ini | 11 ++ .../frontend-tools/data/locale/da-DK.ini | 11 ++ .../frontend-tools/data/locale/de-DE.ini | 11 ++ .../frontend-tools/data/locale/es-ES.ini | 11 ++ .../frontend-tools/data/locale/eu-ES.ini | 11 ++ .../frontend-tools/data/locale/fi-FI.ini | 11 ++ .../frontend-tools/data/locale/fr-FR.ini | 11 ++ .../frontend-tools/data/locale/hr-HR.ini | 11 ++ .../frontend-tools/data/locale/hu-HU.ini | 11 ++ .../frontend-tools/data/locale/ja-JP.ini | 11 ++ .../frontend-tools/data/locale/ko-KR.ini | 11 ++ .../frontend-tools/data/locale/nl-NL.ini | 11 ++ .../frontend-tools/data/locale/pl-PL.ini | 11 ++ .../frontend-tools/data/locale/pt-BR.ini | 11 ++ .../frontend-tools/data/locale/ru-RU.ini | 11 ++ .../frontend-tools/data/locale/sr-CS.ini | 11 ++ .../frontend-tools/data/locale/sr-SP.ini | 11 ++ .../frontend-tools/data/locale/sv-SE.ini | 11 ++ .../frontend-tools/data/locale/uk-UA.ini | 11 ++ .../frontend-tools/data/locale/zh-CN.ini | 11 ++ .../frontend-tools/data/locale/zh-TW.ini | 11 ++ .../coreaudio-encoder/data/locale/sv-SE.ini | 1 + plugins/decklink/data/locale/ko-KR.ini | 2 +- plugins/image-source/data/locale/da-DK.ini | 10 ++ plugins/linux-capture/data/locale/ja-JP.ini | 6 +- plugins/mac-capture/data/locale/ja-JP.ini | 4 +- plugins/mac-capture/data/locale/ko-KR.ini | 2 +- plugins/obs-filters/data/locale/fr-FR.ini | 2 + plugins/obs-filters/data/locale/ko-KR.ini | 4 +- plugins/obs-filters/data/locale/pt-BR.ini | 8 ++ plugins/obs-filters/data/locale/ru-RU.ini | 2 +- plugins/obs-filters/data/locale/sv-SE.ini | 3 + plugins/obs-filters/data/locale/zh-CN.ini | 2 + plugins/obs-outputs/data/locale/fr-FR.ini | 1 + plugins/obs-outputs/data/locale/zh-CN.ini | 1 + plugins/obs-qsv11/data/locale/pt-BR.ini | 2 +- plugins/obs-text/data/locale/ca-ES.ini | 30 ++++ plugins/obs-text/data/locale/cs-CZ.ini | 30 ++++ plugins/obs-text/data/locale/de-DE.ini | 30 ++++ plugins/obs-text/data/locale/es-ES.ini | 30 ++++ plugins/obs-text/data/locale/eu-ES.ini | 30 ++++ plugins/obs-text/data/locale/fi-FI.ini | 30 ++++ plugins/obs-text/data/locale/fr-FR.ini | 30 ++++ plugins/obs-text/data/locale/hu-HU.ini | 30 ++++ plugins/obs-text/data/locale/ja-JP.ini | 30 ++++ plugins/obs-text/data/locale/ko-KR.ini | 30 ++++ plugins/obs-text/data/locale/nl-NL.ini | 28 ++++ plugins/obs-text/data/locale/pl-PL.ini | 30 ++++ plugins/obs-text/data/locale/ru-RU.ini | 30 ++++ plugins/obs-text/data/locale/sv-SE.ini | 30 ++++ plugins/obs-text/data/locale/uk-UA.ini | 30 ++++ plugins/obs-text/data/locale/zh-CN.ini | 30 ++++ plugins/obs-text/data/locale/zh-TW.ini | 28 ++++ plugins/obs-transitions/data/locale/fr-FR.ini | 28 ++++ plugins/obs-transitions/data/locale/sv-SE.ini | 20 +++ plugins/obs-transitions/data/locale/zh-CN.ini | 38 +++++ plugins/rtmp-services/data/locale/ja-JP.ini | 2 +- plugins/win-capture/data/locale/ca-ES.ini | 1 + plugins/win-capture/data/locale/cs-CZ.ini | 1 + plugins/win-capture/data/locale/de-DE.ini | 1 + plugins/win-capture/data/locale/es-ES.ini | 1 + plugins/win-capture/data/locale/eu-ES.ini | 5 + plugins/win-capture/data/locale/fi-FI.ini | 1 + plugins/win-capture/data/locale/fr-FR.ini | 5 + plugins/win-capture/data/locale/hr-HR.ini | 1 + plugins/win-capture/data/locale/hu-HU.ini | 1 + plugins/win-capture/data/locale/ja-JP.ini | 1 + plugins/win-capture/data/locale/ko-KR.ini | 1 + plugins/win-capture/data/locale/nl-NL.ini | 1 + plugins/win-capture/data/locale/pl-PL.ini | 1 + plugins/win-capture/data/locale/pt-BR.ini | 5 + plugins/win-capture/data/locale/ru-RU.ini | 1 + plugins/win-capture/data/locale/sr-CS.ini | 1 + plugins/win-capture/data/locale/sr-SP.ini | 1 + plugins/win-capture/data/locale/sv-SE.ini | 5 + plugins/win-capture/data/locale/uk-UA.ini | 1 + plugins/win-capture/data/locale/zh-CN.ini | 5 + plugins/win-capture/data/locale/zh-TW.ini | 1 + plugins/win-dshow/data/locale/ca-ES.ini | 1 + plugins/win-dshow/data/locale/cs-CZ.ini | 1 + plugins/win-dshow/data/locale/de-DE.ini | 1 + plugins/win-dshow/data/locale/es-ES.ini | 1 + plugins/win-dshow/data/locale/eu-ES.ini | 1 + plugins/win-dshow/data/locale/fi-FI.ini | 1 + plugins/win-dshow/data/locale/fr-FR.ini | 1 + plugins/win-dshow/data/locale/hr-HR.ini | 1 + plugins/win-dshow/data/locale/hu-HU.ini | 1 + plugins/win-dshow/data/locale/ja-JP.ini | 1 + plugins/win-dshow/data/locale/ko-KR.ini | 1 + plugins/win-dshow/data/locale/nl-NL.ini | 1 + plugins/win-dshow/data/locale/pl-PL.ini | 1 + plugins/win-dshow/data/locale/pt-BR.ini | 2 +- plugins/win-dshow/data/locale/ru-RU.ini | 1 + plugins/win-dshow/data/locale/sr-CS.ini | 1 + plugins/win-dshow/data/locale/sr-SP.ini | 1 + plugins/win-dshow/data/locale/sv-SE.ini | 2 + plugins/win-dshow/data/locale/uk-UA.ini | 1 + plugins/win-dshow/data/locale/zh-CN.ini | 1 + plugins/win-dshow/data/locale/zh-TW.ini | 1 + plugins/win-mf/data/locale/pt-BR.ini | 2 +- plugins/win-mf/data/locale/sv-SE.ini | 5 + plugins/win-mf/data/locale/uk-UA.ini | 4 +- 143 files changed, 1366 insertions(+), 87 deletions(-) create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini create mode 100644 UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini create mode 100644 plugins/obs-text/data/locale/ca-ES.ini create mode 100644 plugins/obs-text/data/locale/cs-CZ.ini create mode 100644 plugins/obs-text/data/locale/de-DE.ini create mode 100644 plugins/obs-text/data/locale/es-ES.ini create mode 100644 plugins/obs-text/data/locale/eu-ES.ini create mode 100644 plugins/obs-text/data/locale/fi-FI.ini create mode 100644 plugins/obs-text/data/locale/fr-FR.ini create mode 100644 plugins/obs-text/data/locale/hu-HU.ini create mode 100644 plugins/obs-text/data/locale/ja-JP.ini create mode 100644 plugins/obs-text/data/locale/ko-KR.ini create mode 100644 plugins/obs-text/data/locale/nl-NL.ini create mode 100644 plugins/obs-text/data/locale/pl-PL.ini create mode 100644 plugins/obs-text/data/locale/ru-RU.ini create mode 100644 plugins/obs-text/data/locale/sv-SE.ini create mode 100644 plugins/obs-text/data/locale/uk-UA.ini create mode 100644 plugins/obs-text/data/locale/zh-CN.ini create mode 100644 plugins/obs-text/data/locale/zh-TW.ini diff --git a/UI/data/locale/ar-SA.ini b/UI/data/locale/ar-SA.ini index bebdd20ae2155d..af10ecae560218 100644 --- a/UI/data/locale/ar-SA.ini +++ b/UI/data/locale/ar-SA.ini @@ -52,14 +52,19 @@ Bottom="أسفل" QuickTransitions.SwapScenes="التبديل بين مشهدي المعاينة و الاخراج بعد عملية الانتقال" QuickTransitions.SwapScenesTT="يقوم بتبديل مشهد المعاينة مع مشهد الاخراج بعد عملية الانتقال بين المشاهد (اذا كان مشهد الاخراج الاصلي لازال موجوداً) \n هذا لن يقوم بالتراجع عن اي تغييرات قمت بها على مشهد الاخراج الأصلي." QuickTransitions.DuplicateScene="استنساخ المشهد" +QuickTransitions.DuplicateSceneTT="عند تحرير المشهد نفسه، يسمح بتحرير تحويل/رؤية مصادر دون تعديل خصائص تحرير الإخراج. \nTo من مصادر دون تعديل الإخراج، تمكين '\"تكرار المصادر\"'. \nChanging سيتم إعادة تعيين هذه القيمة الساحة الإخراج الحالي (إذا كان لا يزال موجوداً)." QuickTransitions.EditProperties="استنساخ المصدر" +QuickTransitions.EditPropertiesTT="عند تحرير المشهد نفسه، يسمح بتحرير خصائص المصادر دون تعديل الإخراج. \nThis يمكن استخدام فقط إذا تم تمكين '\"تكرار المشهد\"'. لا تدعم هذه المصادر \nCertain (مثل مصادر التقاط أو وسائط الإعلام) ولا يمكن تحريرها بشكل منفصل. \nChanging سيتم إعادة تعيين هذه القيمة الساحة الإخراج الحالي (إذا كان لا يزال exists).\n\nWarning: لأنه سوف يتم تكرار المصادر ، وهذا قد يتطلب نظام إضافية أو موارد الفيديو." QuickTransitions.HotkeyName="الانتقال السريع: %1" +Basic.AddTransition="إضافة المراحل الانتقالية للتكوين" +Basic.RemoveTransition="سلاو" Basic.TransitionProperties="خصائص تأثير الإنتقال" Basic.SceneTransitions="تأثير انتقال المشهد" Basic.TransitionDuration="مدة الانتقال" Basic.TogglePreviewProgramMode="طور الاستوديو" +TransitionNameDlg.Text="Text" TransitionNameDlg.Title="اسم تأثير الإنتقال" TitleBar.Profile="الملف الشخصي" @@ -91,6 +96,7 @@ Output.ConnectFail.Error="حدث خطأ غير متوقع عند محاولة ا Output.ConnectFail.Disconnected="تم قطع الاتصال من السيرفر." Output.RecordFail.Title="فشل في بدء التسجيل" +Output.RecordFail.Unsupported="تنسيق الإخراج أما غير معتمد أو لا يدعم أكثر من مسار للصوت. الرجاء التحقق من الإعدادات الخاصة بك وحاول مرة أخرى." Output.RecordNoSpace.Msg="لا توجد مساحة كافية على القرص لمتابعة التسجيل." Output.RecordError.Title="خطأ في التسجيل" Output.RecordError.Msg="حدث خطأ غير محدد أثناء التسجيل." @@ -110,11 +116,17 @@ LicenseAgreement.Exit="خروج" Remux.SourceFile="تسجيل OBS" Remux.TargetFile="الملف الهدف" +Remux.Remux="تحويل الصيغة" Remux.OBSRecording="تسجيل OBS" +Remux.FinishedTitle="انتهت عملية تحويل الصيغة" +Remux.Finished="تسجيل عملية تحويل الصيغة" +Remux.FinishedError="عملية التحويل قد تكون غير مكتملة" Remux.SelectRecording="إختر تسجيل OBS …" Remux.SelectTarget="إختر الملف الهدف …" Remux.FileExistsTitle="الملف الهدف موجود" Remux.FileExists="الملف الهدف موجود، هل تريد استبداله؟" +Remux.ExitUnfinishedTitle="تقدم عملية التحويل" +Remux.ExitUnfinished="إيقاف عميلة التحويل الآن قد تجعل الملف غير قابل للاستخدام./n هل أنت متأكد أنك تريد إيقاف عملية التحويل؟" UpdateAvailable="تحديث جديد متوفر" UpdateAvailable.Text="الإصدار %1.%2.%3 متوفر الآن. انقر هنا للتتحميل" @@ -131,6 +143,8 @@ Basic.DisplayCapture="التقاط الشاشة" Basic.Main.PreviewConextMenu.Enable="تمكين المعاينة" +ScaleFiltering="مقياس التصفية" +ScaleFiltering.Point="نقطة" Deinterlacing.Discard="تجاهل" @@ -243,6 +257,7 @@ Basic.MainMenu.View.Toolbars="شريط الأدوات" Basic.MainMenu.SceneCollection="&مجموعة المشاهد" Basic.MainMenu.Profile="&الملف الشخصي" + Basic.MainMenu.Help="&مساعدة" Basic.MainMenu.Help.Website="زيارة &الموقع الإلكتروني" Basic.MainMenu.Help.Logs="&ملفات السجل" @@ -345,3 +360,5 @@ Basic.Settings.Advanced.Video.ColorRange.Full="كامل" + + diff --git a/UI/data/locale/bg-BG.ini b/UI/data/locale/bg-BG.ini index fa2a20b6aa632e..260ebfe438baf1 100644 --- a/UI/data/locale/bg-BG.ini +++ b/UI/data/locale/bg-BG.ini @@ -224,6 +224,7 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Премести най-о&тдолу" + Basic.MainMenu.Help="&Помощ" Basic.MainMenu.Help.Logs="\"Log\" &файлове" Basic.MainMenu.Help.Logs.UploadCurrentLog="Качи &текущия \"Log\" файл" @@ -289,6 +290,8 @@ Basic.Hotkeys.StartRecording="Започни запис" Basic.Hotkeys.StopRecording="Спри запис" Basic.Hotkeys.SelectScene="Премини към сцена" + + Hotkeys.Insert="Вмъкни" Hotkeys.Delete="Изтрий" Hotkeys.Home="Начало" diff --git a/UI/data/locale/ca-ES.ini b/UI/data/locale/ca-ES.ini index 62e9b7519f858a..a688099a9d0691 100644 --- a/UI/data/locale/ca-ES.ini +++ b/UI/data/locale/ca-ES.ini @@ -48,6 +48,7 @@ Left="Esquerra" Right="Dreta" Top="Part superior" Bottom="Part inferior" +Reset="Restableix" QuickTransitions.SwapScenes="Canvia la vista prèvia i sortida d'escenes després de la transició" QuickTransitions.SwapScenesTT="Canvia la vista prèvia i sortida d'escenes després de la transició (si encara existeix l'escena original de la sortida). \nAixò no desfarà qualsevol canvi que pugui haver fet a l'escena original de la sortida." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="Barra d'estat" Basic.MainMenu.SceneCollection="&Col·lecció d'escenes" Basic.MainMenu.Profile="&Perfil" +Basic.MainMenu.Tools="&Eines" + Basic.MainMenu.Help="&Ajuda" Basic.MainMenu.Help.Website="Visa el lloc &web" Basic.MainMenu.Help.Logs="Fitxers de ®istre" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Llengua" Basic.Settings.General.WarnBeforeStartingStream="Mostra diàleg de confirmació quan s'iniciï una transmissió" Basic.Settings.General.WarnBeforeStoppingStream="Mostra diàleg de confirmació quan s'aturi una transmissió" Basic.Settings.General.HideProjectorCursor="Amaga el cursor sobre projectors" +Basic.Settings.General.ProjectorAlwaysOnTop="Projectors sempre en la part superior" Basic.Settings.General.Snapping="Ajustament d'alineació de la font" Basic.Settings.General.ScreenSnapping="Ajustar les fonts a la vora de la pantalla" Basic.Settings.General.CenterSnapping="Ajustar les fonts al centre horitzontal i vertical" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Ajustar les fonts a altres fonts" Basic.Settings.General.SnapDistance="Ajusta la sensibilitat" Basic.Settings.General.RecordWhenStreaming="Enregistra automàticament quan es transmet" Basic.Settings.General.KeepRecordingWhenStreamStops="Mantenir l'enregistrament quan s'atura la transmissió" +Basic.Settings.General.SysTrayEnabled="Activar icona a la safata del sistema" +Basic.Settings.General.SysTrayWhenStarted="Minimitzar a la safata del sistema en iniciar" Basic.Settings.Stream="Directe" Basic.Settings.Stream.StreamType="Tipus de directe" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="Inicia l'enregistrament" Basic.Hotkeys.StopRecording="Atura l'enregistrament" Basic.Hotkeys.SelectScene="Canviar a escena" +Basic.SystemTray.Show="Mostra" +Basic.SystemTray.Hide="Oculta" + +Basic.SystemTray.Message.Reconnecting="Desconnectat. Tornant a connectar..." + Hotkeys.Insert="Insereix" Hotkeys.Delete="Suprimeix" Hotkeys.Home="Inici" diff --git a/UI/data/locale/cs-CZ.ini b/UI/data/locale/cs-CZ.ini index aad6296e6ba083..35042599ab845e 100644 --- a/UI/data/locale/cs-CZ.ini +++ b/UI/data/locale/cs-CZ.ini @@ -48,6 +48,7 @@ Left="Vlevo" Right="Vpravo" Top="Nahoře" Bottom="Dole" +Reset="Resetovat" QuickTransitions.SwapScenes="Prohodit scény náhledu a výstupu po přechodu" QuickTransitions.SwapScenesTT="Prohodí scény náhledu a výstupu po přechodu (pokud originální výstupní scéna stále existuje).\nTato funkce nevrátí provedené změny, které byly provedeny v originální scéně výstupu." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Stavový řádek" Basic.MainMenu.SceneCollection="Kolekce &scén" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="Nás&troje" + Basic.MainMenu.Help="Pomoc (&H)" Basic.MainMenu.Help.Website="Navštívit &web" Basic.MainMenu.Help.Logs="Soubory záznamu (&L)" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Jazyk" Basic.Settings.General.WarnBeforeStartingStream="Vyžadovat potvrzení pro spuštění vysílání" Basic.Settings.General.WarnBeforeStoppingStream="Vyžadovat potvrzení pro ukončení vysílání" Basic.Settings.General.HideProjectorCursor="Skrýt kurzor přes projektor" +Basic.Settings.General.ProjectorAlwaysOnTop="Zobrazovat projektor vždy navrchu" Basic.Settings.General.Snapping="Přichycování zdrojů" Basic.Settings.General.ScreenSnapping="Přichytávat zdroje k okraji obrazovky" Basic.Settings.General.CenterSnapping="Přichytávat zdroje k vertikálnímu a horizontálnímu středu" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Přichytávat zdroje k ostatním zdrojům Basic.Settings.General.SnapDistance="Citlivost přichycení" Basic.Settings.General.RecordWhenStreaming="Automaticky nahrávat při vysílání" Basic.Settings.General.KeepRecordingWhenStreamStops="Pokračovat v nahrávání i po zastavení vysílání" +Basic.Settings.General.SysTrayEnabled="Zobrazit ikonu v oznamovací oblasti" +Basic.Settings.General.SysTrayWhenStarted="Minimalizovat do systémové lišty při spuštění" Basic.Settings.Stream="Vysílání" Basic.Settings.Stream.StreamType="Typ vysílání" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="Začít nahrávat" Basic.Hotkeys.StopRecording="Zastavit nahrávání" Basic.Hotkeys.SelectScene="Přepnout na scénu" +Basic.SystemTray.Show="Zobrazit" +Basic.SystemTray.Hide="Skrýt" + +Basic.SystemTray.Message.Reconnecting="Odpojen. Obnovuji spojení..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/da-DK.ini b/UI/data/locale/da-DK.ini index 86f009df7aa1e9..251a7380804faa 100644 --- a/UI/data/locale/da-DK.ini +++ b/UI/data/locale/da-DK.ini @@ -48,6 +48,7 @@ Left="Venstre" Right="Højre" Top="Top" Bottom="Bund" +Reset="Nulstil" QuickTransitions.SwapScenes="Byt om på forhåndsvisning/output scener efter overgang" QuickTransitions.DuplicateScene="Dupliker scene" @@ -282,6 +283,7 @@ Basic.MainMenu.Edit.AdvAudio="&Avancerede lydegenskaber" Basic.MainMenu.SceneCollection="&Scenesamling" Basic.MainMenu.Profile="&Profil" + Basic.MainMenu.Help="&Hjælp" Basic.MainMenu.Help.Website="Besøg &websted" Basic.MainMenu.Help.Logs="&Logfiler" @@ -430,6 +432,8 @@ Basic.Hotkeys.StartRecording="Start optagelse" Basic.Hotkeys.StopRecording="Stop optagelse" Basic.Hotkeys.SelectScene="Skift til scene" + + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/de-DE.ini b/UI/data/locale/de-DE.ini index 81c9c18d7e4290..b42c6c8fb35305 100644 --- a/UI/data/locale/de-DE.ini +++ b/UI/data/locale/de-DE.ini @@ -48,6 +48,7 @@ Left="Links" Right="Rechts" Top="Oben" Bottom="Unten" +Reset="Zurücksetzen" QuickTransitions.SwapScenes="Tausche Vorschau/Ausgabe-Szenen nach dem Übergang" QuickTransitions.SwapScenesTT="Vertauscht die Vorschau- und Ausgabe-Szenen nach dem Übergang (falls die ursprüngliche Ausgabe-Szene noch vorhanden ist).\nEventuelle Änderungen an der original Ausgabe-Szene werden hierbei nicht rückgängig gemacht." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Statusleiste" Basic.MainMenu.SceneCollection="&Szenen-Sammlung" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="Werkzeuge (&T)" + Basic.MainMenu.Help="&Hilfe" Basic.MainMenu.Help.Website="&Webseite besuchen" Basic.MainMenu.Help.Logs="&Logdateien" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Sprache" Basic.Settings.General.WarnBeforeStartingStream="Bestätigungsdialog beim Streamstart anzeigen" Basic.Settings.General.WarnBeforeStoppingStream="Bestätigungsdialog beim Streamstop anzeigen" Basic.Settings.General.HideProjectorCursor="Mauszeiger über Projektoren verstecken" +Basic.Settings.General.ProjectorAlwaysOnTop="Projektoren immer im Vordergrund anzeigen" Basic.Settings.General.Snapping="Quellenausrichtung" Basic.Settings.General.ScreenSnapping="Quellen am Bildschirmrand ausrichten" Basic.Settings.General.CenterSnapping="Quellen zur horizontalen und vertikalen Mitte ausrichten" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Quellen an anderen Quellen ausrichten" Basic.Settings.General.SnapDistance="Ausrichtungsempfindlichkeit" Basic.Settings.General.RecordWhenStreaming="Stream automatisch aufnehmen" Basic.Settings.General.KeepRecordingWhenStreamStops="Weiter aufnehmen, wenn der Livestream stoppt" +Basic.Settings.General.SysTrayEnabled="Symbol in der Taskleiste aktivieren" +Basic.Settings.General.SysTrayWhenStarted="Beim Start zur Taskleiste minimieren" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Stream Typ" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Verlustfreie Qualität-Warnung Basic.Settings.Output.Simple.Warn.MultipleQSV="Achtung: Sie können nicht mehrere separate QSV-Encoder beim streamen und aufnehmen gleichzeitig verwenden. Wenn Sie zur gleichen Zeit streamen und aufnehmen möchten, dann ändern Sie bitte entweder den Aufnahme-Encoder oder den Stream-Encoder." Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardware (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 niedrige CPU-Auslastung Voreinstellung, erhöht die Dateigröße)" Basic.Settings.Output.VideoBitrate="Videobitrate" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="Aufnahme starten" Basic.Hotkeys.StopRecording="Aufnahme stoppen" Basic.Hotkeys.SelectScene="Zu Szene wechseln" +Basic.SystemTray.Show="Anzeigen" +Basic.SystemTray.Hide="Ausblenden" + +Basic.SystemTray.Message.Reconnecting="Verbindung verloren. Verbinde erneut..." + Hotkeys.Insert="Einfügen" Hotkeys.Delete="Entfernen" Hotkeys.Home="Pos1" diff --git a/UI/data/locale/el-GR.ini b/UI/data/locale/el-GR.ini index 9412b86caf44a3..4ecdca7f21dabf 100644 --- a/UI/data/locale/el-GR.ini +++ b/UI/data/locale/el-GR.ini @@ -236,6 +236,7 @@ Basic.MainMenu.Edit.AdvAudio="Ιδιότητες(&A) Ήχου για Προχω Basic.MainMenu.SceneCollection="&Συλλογή Σκηνών" Basic.MainMenu.Profile="&Προφίλ" + Basic.MainMenu.Help="Βοήθεια(&H)" Basic.MainMenu.Help.Logs="Αρχεία(&) Καταγραφής" Basic.MainMenu.Help.Logs.ShowLogs="Εμφάνιση(&S) Αρχείων Καταγραφής" @@ -359,6 +360,8 @@ Basic.Hotkeys.StopRecording="Διακοπή Καταγραφής" Basic.Hotkeys.SelectScene="Μετάβαση σε σκηνή" + + Mute="Σίγαση" Unmute="Κατάργηση σίγασης" diff --git a/UI/data/locale/es-ES.ini b/UI/data/locale/es-ES.ini index cd7cb0432a0b56..e988ac915c6dd8 100644 --- a/UI/data/locale/es-ES.ini +++ b/UI/data/locale/es-ES.ini @@ -48,6 +48,7 @@ Left="Izquierda" Right="Derecha" Top="Arriba" Bottom="Abajo" +Reset="Reiniciar" QuickTransitions.SwapScenes="Cambiar vista previa y salida escenas después de la transición" QuickTransitions.SwapScenesTT="Cambia la vista previa y salida escenas después de la transición (si todavía existe la escena original de la salida). \nEsto no deshará cualquier cambio que pueda haber hecho a la escena original de la salida." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Barra de Estado" Basic.MainMenu.SceneCollection="&Colección de Escenas" Basic.MainMenu.Profile="&Perfil" +Basic.MainMenu.Tools="&Herramientas" + Basic.MainMenu.Help="&Ayuda" Basic.MainMenu.Help.Website="Visitar Sitio &Web" Basic.MainMenu.Help.Logs="&Archivos de registro" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Idioma" Basic.Settings.General.WarnBeforeStartingStream="Mostrar diálogo de confirmación cuando se inicia una transmisión" Basic.Settings.General.WarnBeforeStoppingStream="Mostrar diálogo de confirmación cuando se para una transmisión" Basic.Settings.General.HideProjectorCursor="Ocultar el cursor sobre proyectores" +Basic.Settings.General.ProjectorAlwaysOnTop="Proyectores siempre en la parte superior" Basic.Settings.General.Snapping="Ajuste de alineación de la fuente" Basic.Settings.General.ScreenSnapping="Ajustar las fuentes al borde de la pantalla" Basic.Settings.General.CenterSnapping="Ajustar las fuentes al centro horizontal y vertical" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Ajustar las fuentes a otras fuentes" Basic.Settings.General.SnapDistance="Ajustar la sensibilidad" Basic.Settings.General.RecordWhenStreaming="Grabar automáticamente cuando se transmite" Basic.Settings.General.KeepRecordingWhenStreamStops="Mantener la grabación cuando se detiene la trasmision" +Basic.Settings.General.SysTrayEnabled="Activar icono en la bandeja del sistema" +Basic.Settings.General.SysTrayWhenStarted="Minimizar a la bandeja del sistema al iniciar" Basic.Settings.Stream="Emision" Basic.Settings.Stream.StreamType="Tipo de Emision" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="¡Atención de calidad sin pé Basic.Settings.Output.Simple.Warn.MultipleQSV="Advertencia: No se pueden usar varios codificadores QSV separados al transmitir y grabar al mismo tiempo. Si desea transmitir y grabar al mismo tiempo, por favor cambíelos, ya sea el codificador de grabación o el codificador de trasmisión." Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardware (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 bajo uso de CPU, aumenta el tamaño de archivo)" Basic.Settings.Output.VideoBitrate="Bitrate de vídeo" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="Iniciar grabación" Basic.Hotkeys.StopRecording="Detener grabación" Basic.Hotkeys.SelectScene="Cambiar a la escena" +Basic.SystemTray.Show="Mostrar" +Basic.SystemTray.Hide="Ocultar" + +Basic.SystemTray.Message.Reconnecting="Desconectado. Volviendo a conectar..." + Hotkeys.Insert="Insertar" Hotkeys.Delete="Eliminar" Hotkeys.Home="Casa" diff --git a/UI/data/locale/et-EE.ini b/UI/data/locale/et-EE.ini index 87c0edd8047686..2c31217ec9b62d 100644 --- a/UI/data/locale/et-EE.ini +++ b/UI/data/locale/et-EE.ini @@ -190,6 +190,7 @@ Basic.MainMenu.Edit.Undo="Võta tagasi (&U)" + Basic.MainMenu.Help.Logs="&Logifailid" Basic.MainMenu.Help.CheckForUpdates="Otsi värskendusi" @@ -247,6 +248,8 @@ Basic.AdvAudio.AudioTracks="Rajad" + + Hotkeys.Insert="Sisesta" Hotkeys.Delete="Kustuta" diff --git a/UI/data/locale/eu-ES.ini b/UI/data/locale/eu-ES.ini index 3eb81950cac37f..d2ac30de9775f8 100644 --- a/UI/data/locale/eu-ES.ini +++ b/UI/data/locale/eu-ES.ini @@ -48,6 +48,7 @@ Left="Ezkerrean" Right="Eskuinean" Top="Goian" Bottom="Behean" +Reset="Berrezarri" QuickTransitions.SwapScenes="Trukatu Aurrebista/Irteera-eszenak trantsizioen ondoren" QuickTransitions.SwapScenesTT="Trukatu aurrebistak eta irteera-eszenak trantsizioen ondoren (baldin eta irteerakoaren jatorrizkoa eszena badago).\n Honek ez du desegingo irteerakoaren jatorrizko eszenari egindako aldaketak." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="Egoera-barra" Basic.MainMenu.SceneCollection="&Eszena-bilduma" Basic.MainMenu.Profile="&Profila" +Basic.MainMenu.Tools="&Tresnak" + Basic.MainMenu.Help="&Laguntza" Basic.MainMenu.Help.Website="Ikusi &webgunea" Basic.MainMenu.Help.Logs="&Egunkari-fitxategiak" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Hizkuntza" Basic.Settings.General.WarnBeforeStartingStream="Erakutsi baieztapen elkarrizketa transmisioak hasterakoan" Basic.Settings.General.WarnBeforeStoppingStream="Erakutsi baieztapen elkarrizketa transmisioak gelditzerakoan" Basic.Settings.General.HideProjectorCursor="Ezkutatu kurtsorea proiekzioetan" +Basic.Settings.General.ProjectorAlwaysOnTop="Proiektoreak beti gainean" Basic.Settings.General.Snapping="Iturburuaren lerrokatzearen doitzea" Basic.Settings.General.ScreenSnapping="Doitu iturburuak pantailaren ertzera" Basic.Settings.General.CenterSnapping="Doitu iturburuak bertikalki eta horizontalki erdira" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Doitu iturburuak beste iturburuetara" Basic.Settings.General.SnapDistance="Doitu sentikortasuna" Basic.Settings.General.RecordWhenStreaming="Grabatu automatikoki transmisioa egitean" Basic.Settings.General.KeepRecordingWhenStreamStops="Mantendu grabazioa transmisioa gelditzean" +Basic.Settings.General.SysTrayEnabled="Gaitu sistemaren erretiluko ikonoa" +Basic.Settings.General.SysTrayWhenStarted="Minimizatu sistemaren erretilura hastean" Basic.Settings.Stream="Transmisioa" Basic.Settings.Stream.StreamType="Transmisio-mota" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="Hasi Grabazioa" Basic.Hotkeys.StopRecording="Gelditu grabazioa" Basic.Hotkeys.SelectScene="Aldatu eszenara" +Basic.SystemTray.Show="Erakutsi" +Basic.SystemTray.Hide="Ezkutatu" + +Basic.SystemTray.Message.Reconnecting="Deskonektatuta. Berriro konektatzen..." + Hotkeys.Insert="Txertatu" Hotkeys.Delete="Ezabatu" Hotkeys.Home="Hasiera" diff --git a/UI/data/locale/fi-FI.ini b/UI/data/locale/fi-FI.ini index fd07e1a4f809a3..b8c5ff70ecc197 100644 --- a/UI/data/locale/fi-FI.ini +++ b/UI/data/locale/fi-FI.ini @@ -48,6 +48,7 @@ Left="Vasen" Right="Oikea" Top="Ylhäältä" Bottom="Alhaalta" +Reset="Palauta" QuickTransitions.SwapScenes="Vaihda esikatselu- ja ulostulo-skenet siirtymän jälkeen" QuickTransitions.SwapScenesTT="Vaihda esikatselu- ja ulostulo-skenet siirtymän jälkeen (jos ulostulon alkuperäinen skene on yhä olemassa).\nTämä ei peruuta muutoksia joita on tehty alkuperäiseen skeneen." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Tilapalkki" Basic.MainMenu.SceneCollection="&Skene-kokoelma" Basic.MainMenu.Profile="&Profiili" +Basic.MainMenu.Tools="T&yökalut" + Basic.MainMenu.Help="&Apua" Basic.MainMenu.Help.Website="Käy &verkkosivulla" Basic.MainMenu.Help.Logs="&Lokitiedostot" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Kieli" Basic.Settings.General.WarnBeforeStartingStream="Näytä varmistus-ikkuna kun lähetys aloitetaan" Basic.Settings.General.WarnBeforeStoppingStream="Näytä varmistus-ikkuna kun lähetys pysäytetään" Basic.Settings.General.HideProjectorCursor="Piilota osoitin peilattaessa" +Basic.Settings.General.ProjectorAlwaysOnTop="Pidä peilatut esikatselut aina päällimmäisenä" Basic.Settings.General.Snapping="Lähteiden kiinnitys" Basic.Settings.General.ScreenSnapping="Kiinnitä lähteitä ruudun reunaan" Basic.Settings.General.CenterSnapping="Kiinnitä lähteitä vaaka- sekä pystysuunnan keskilinjaan" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Kiinnitä lähteitä muihin lähteisiin" Basic.Settings.General.SnapDistance="Kiinnityksen herkkyys" Basic.Settings.General.RecordWhenStreaming="Tallenna automaattisesti kun lähetetään" Basic.Settings.General.KeepRecordingWhenStreamStops="Jatka tallennusta lähetyksen loputtua" +Basic.Settings.General.SysTrayEnabled="Ota järjestelmäkuvake käyttöön" +Basic.Settings.General.SysTrayWhenStarted="Pienennä ilmaisinalueelle käynnistyessä" Basic.Settings.Stream="Lähetys" Basic.Settings.Stream.StreamType="Lähetystyyppi" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="Aloita tallennus" Basic.Hotkeys.StopRecording="Pysäytä tallennus" Basic.Hotkeys.SelectScene="Vaihda skeneen" +Basic.SystemTray.Show="Näytä" +Basic.SystemTray.Hide="Piilota" + +Basic.SystemTray.Message.Reconnecting="Yhteys katkaistu. Yhdistetään uudelleen..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/fr-FR.ini b/UI/data/locale/fr-FR.ini index ad7613554c53ce..781e993bc027a7 100644 --- a/UI/data/locale/fr-FR.ini +++ b/UI/data/locale/fr-FR.ini @@ -48,6 +48,7 @@ Left="À gauche" Right="À droite" Top="En haut" Bottom="En bas" +Reset="Réinitialiser" QuickTransitions.SwapScenes="Permuter les scènes d'aperçu et de sortie après la transition" QuickTransitions.SwapScenesTT="Permute les scènes d'aperçu et de sortie après la transition (si la scène d'origine de la sortie existe toujours). \nCela n'annulera pas les modifications qui auront pu être faites sur la scène d'origine de la sortie." @@ -280,6 +281,7 @@ Basic.MainMenu.Edit.Undo="&Annuler" Basic.MainMenu.Edit.Redo="&Rétablir" Basic.MainMenu.Edit.UndoAction="&Annuler $1" Basic.MainMenu.Edit.RedoAction="&Rétablir $1" +Basic.MainMenu.Edit.LockPreview="Verrouiller la prévisualisation" Basic.MainMenu.Edit.Transform="&Transformer" Basic.MainMenu.Edit.Transform.EditTransform="Éditer la transformation..." Basic.MainMenu.Edit.Transform.ResetTransform="Réinitialiser la transformation" @@ -307,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Barre d'état" Basic.MainMenu.SceneCollection="Collection de &scènes" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="Outils" + Basic.MainMenu.Help="&Aide" Basic.MainMenu.Help.Website="Consulter le site &Web" Basic.MainMenu.Help.Logs="&Fichiers journaux" @@ -326,6 +330,7 @@ Basic.Settings.General.Language="Langue" Basic.Settings.General.WarnBeforeStartingStream="Afficher une boîte de dialogue de confirmation au démarrage d'un stream" Basic.Settings.General.WarnBeforeStoppingStream="Afficher une boîte de dialogue de confirmation à l'arrêt d'un stream" Basic.Settings.General.HideProjectorCursor="Cacher le curseur sur les projecteurs" +Basic.Settings.General.ProjectorAlwaysOnTop="Projecteurs toujours au premier plan" Basic.Settings.General.Snapping="Déclenchement d'alignement des sources" Basic.Settings.General.ScreenSnapping="Déclencher avec les bords de l'écran" Basic.Settings.General.CenterSnapping="Déclencher avec le centre de l'écran" @@ -333,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Déclencher avec d'autres sources" Basic.Settings.General.SnapDistance="Sensibilité du déclenchement" Basic.Settings.General.RecordWhenStreaming="Enregistrer automatiquement lors d'un stream" Basic.Settings.General.KeepRecordingWhenStreamStops="Continuer à enregistrer lorsque le stream s’arrête" +Basic.Settings.General.SysTrayEnabled="Afficher une icône dans la zone de notification" +Basic.Settings.General.SysTrayWhenStarted="Réduire dans la zone de notification dès le démarrage" Basic.Settings.Stream="Flux" Basic.Settings.Stream.StreamType="Type de diffusion" @@ -362,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Avertissement de qualité sans Basic.Settings.Output.Simple.Warn.MultipleQSV="Attention : Vous ne pouvez pas utiliser plusieurs encodeurs QSV distincts lorsque vous streamez et enregistrez en même temps. Si vous voulez streamer et enregistrer en même temps, veuillez changer soit l'encodeur d'enregistrement, soit l'encodeur de streaming." Basic.Settings.Output.Simple.Encoder.Software="Logiciel (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Matériel (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Matériel (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Matériel (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Logiciel (préréglage x264 \"faible utilisation du CPU\", augmente la taille du fichier)" Basic.Settings.Output.VideoBitrate="Débit vidéo" @@ -467,6 +475,8 @@ Basic.Settings.Advanced.StreamDelay="Retard du stream" Basic.Settings.Advanced.StreamDelay.Duration="Durée (en secondes)" Basic.Settings.Advanced.StreamDelay.Preserve="Préserver le point de coupure (augmente le retard) lors d'une reconnexion" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Utilisation estimée de la mémoire : %1 Mo" +Basic.Settings.Advanced.Network="Carte réseau (adresse IP source du flux)" +Basic.Settings.Advanced.Network.BindToIP="Lier à :" Basic.AdvAudio="Propriétés audio avancées" Basic.AdvAudio.Name="Nom" @@ -485,6 +495,11 @@ Basic.Hotkeys.StartRecording="Démarrer l'enregistrement" Basic.Hotkeys.StopRecording="Arrêter l'enregistrement" Basic.Hotkeys.SelectScene="Passer à la scène" +Basic.SystemTray.Show="Restaurer" +Basic.SystemTray.Hide="Réduire" + +Basic.SystemTray.Message.Reconnecting="Déconnecté. Reconnexion en cours..." + Hotkeys.Insert="Insérer" Hotkeys.Delete="Supprimer" Hotkeys.Home="Début" diff --git a/UI/data/locale/gl-ES.ini b/UI/data/locale/gl-ES.ini index 604d47e667be52..eb8b1ee200f489 100644 --- a/UI/data/locale/gl-ES.ini +++ b/UI/data/locale/gl-ES.ini @@ -247,6 +247,7 @@ Basic.MainMenu.Edit.AdvAudio="Propiedades de audio &avanzadas" Basic.MainMenu.SceneCollection="&Colección de escenas" Basic.MainMenu.Profile="&Perfil" + Basic.MainMenu.Help="&Axuda" Basic.MainMenu.Help.Website="Visitar sitio &web" Basic.MainMenu.Help.Logs="&Ficheiros de rexistro" @@ -366,6 +367,8 @@ Basic.Hotkeys.StopStreaming="Deter retransmisión" Basic.Hotkeys.StartRecording="Iniciar gravación" Basic.Hotkeys.StopRecording="Deter gravación" + + Hotkeys.Insert="Inserir" Hotkeys.Delete="Eliminar" Hotkeys.Home="Inicio" diff --git a/UI/data/locale/he-IL.ini b/UI/data/locale/he-IL.ini index e6f30965d42202..cbeab9b032f940 100644 --- a/UI/data/locale/he-IL.ini +++ b/UI/data/locale/he-IL.ini @@ -308,6 +308,8 @@ Basic.MainMenu.View.StatusBar="&שורת מצב" Basic.MainMenu.SceneCollection="אוסף סצינות(&S)" Basic.MainMenu.Profile="פרופיל(&P)" +Basic.MainMenu.Tools="& כלים" + Basic.MainMenu.Help="עזרה(&H)" Basic.MainMenu.Help.Website="בקר אתר(&W)" Basic.MainMenu.Help.Logs="קבצי יומן רישום(&L)" @@ -468,6 +470,7 @@ Basic.Settings.Advanced.StreamDelay="השהיית זרם נתונים" Basic.Settings.Advanced.StreamDelay.Duration="משך זמן (בשניות)" Basic.Settings.Advanced.StreamDelay.Preserve="שמר נקודת חיתוך (השהייה מוגדלת) בעת חיבור מחדש" Basic.Settings.Advanced.StreamDelay.MemoryUsage="שימוש זיכרון משוער: %1 MB" +Basic.Settings.Advanced.Network="רשת" Basic.AdvAudio="מאפייני קול מתקדמים" Basic.AdvAudio.Name="שם" @@ -486,6 +489,10 @@ Basic.Hotkeys.StartRecording="התחל הקלטה" Basic.Hotkeys.StopRecording="עצור הקלטה" Basic.Hotkeys.SelectScene="עבור לסצנה" +Basic.SystemTray.Show="הצג" +Basic.SystemTray.Hide="הסתר" + + Hotkeys.Insert="הוסף" Hotkeys.Delete="מחק" Hotkeys.Home="בית" diff --git a/UI/data/locale/hr-HR.ini b/UI/data/locale/hr-HR.ini index 1e339ab9b4ebb9..497e1a96433d80 100644 --- a/UI/data/locale/hr-HR.ini +++ b/UI/data/locale/hr-HR.ini @@ -308,6 +308,8 @@ Basic.MainMenu.View.StatusBar="&Statusna linija" Basic.MainMenu.SceneCollection="Kolekcija &scena" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="Ala&ti" + Basic.MainMenu.Help="Pomoć (&H)" Basic.MainMenu.Help.Website="Poseti stranicu (&W)" Basic.MainMenu.Help.Logs="&Log datoteke" @@ -327,6 +329,7 @@ Basic.Settings.General.Language="Jezik" Basic.Settings.General.WarnBeforeStartingStream="Prikaži prozor za potvrdu kada se započinju strimovi" Basic.Settings.General.WarnBeforeStoppingStream="Prikaži prozor za potvrdu kada se zaustavljaju strimovi" Basic.Settings.General.HideProjectorCursor="Sakrij pokazivač na projektorima" +Basic.Settings.General.ProjectorAlwaysOnTop="Uvek postavi projektor na vrh prozora" Basic.Settings.General.Snapping="Poravnavanje privlačenjem izvora" Basic.Settings.General.ScreenSnapping="Privuci izvore ivici ekrana" Basic.Settings.General.CenterSnapping="Privuci izvore horizontalnoj i vertikalnoj sredini" @@ -334,6 +337,8 @@ Basic.Settings.General.SourceSnapping="Privlačenje izvora ka drugim izvorima" Basic.Settings.General.SnapDistance="Osetljivost privlačenja" Basic.Settings.General.RecordWhenStreaming="Automatsko snimanje pri emitovanju" Basic.Settings.General.KeepRecordingWhenStreamStops="Nastavi snimati kada se emitovanje zaustavi" +Basic.Settings.General.SysTrayEnabled="Omogući ikonicu u sistemskom panelu" +Basic.Settings.General.SysTrayWhenStarted="Pri pokretanju minimiziraj na ikonicu u sistemskom panelu" Basic.Settings.Stream="Strim" Basic.Settings.Stream.StreamType="Vrsta strima" @@ -488,6 +493,11 @@ Basic.Hotkeys.StartRecording="Počni snimanje" Basic.Hotkeys.StopRecording="Zaustavi snimanje" Basic.Hotkeys.SelectScene="Prebaci na scenu" +Basic.SystemTray.Show="Prikaži" +Basic.SystemTray.Hide="Sakrij" + +Basic.SystemTray.Message.Reconnecting="Veza prekinuta. Ponovno uspostavljanje..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/hu-HU.ini b/UI/data/locale/hu-HU.ini index 046a2e531365ea..f83119b3c1427d 100644 --- a/UI/data/locale/hu-HU.ini +++ b/UI/data/locale/hu-HU.ini @@ -48,6 +48,7 @@ Left="Bal" Right="Jobb" Top="Felső" Bottom="Alsó" +Reset="Újraindít" QuickTransitions.SwapScenes="Előnézeti/Kimeneti Jelenetek cseréje átmenet után" QuickTransitions.SwapScenesTT="Az előnézet és a kimeneti jelenet cseréje átmenet után (ha a kimenet eredeti jelenete még létezik).\nEz nincs kihatással a kimenet eredeti jelenetére." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Állapotsor" Basic.MainMenu.SceneCollection="&Jelenet gyűjtemény" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="&Eszközők" + Basic.MainMenu.Help="&Segítség" Basic.MainMenu.Help.Website="Weboldal meglátogatása" Basic.MainMenu.Help.Logs="&Naplófájlok" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Nyelv" Basic.Settings.General.WarnBeforeStartingStream="Megerősítő párbeszédpanel megjelenítése stream indításakor" Basic.Settings.General.WarnBeforeStoppingStream="Megerősítő párbeszédpanel megjelenítése stream leállításakor" Basic.Settings.General.HideProjectorCursor="Projektor nézetben a kurzor elrejtése" +Basic.Settings.General.ProjectorAlwaysOnTop="Projektorok mindig legfelül" Basic.Settings.General.Snapping="Forrás pozicionálásának igazítása" Basic.Settings.General.ScreenSnapping="Források igazítása a képernyő széléhez" Basic.Settings.General.CenterSnapping="Források vízszintes és függőleges középponthoz igazítása" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Források igazítása más forrásokhoz" Basic.Settings.General.SnapDistance="Igazítás érzékenysége" Basic.Settings.General.RecordWhenStreaming="Automatikus felvétel stream esetén" Basic.Settings.General.KeepRecordingWhenStreamStops="Felvétel folytatása a stream leállása esetén" +Basic.Settings.General.SysTrayEnabled="Tálca ikon elhelyezése" +Basic.Settings.General.SysTrayWhenStarted="Indításkor ikonként a tálcán" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Stream típusa" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Veszteségmentes minőség fig Basic.Settings.Output.Simple.Warn.MultipleQSV="Figyelem: Nem használható több különálló QSV kódoló streamelésre és felvételre egyidejűleg. Ha ön egyidejűleg kíván streamet és felvételt készíteni, akkor váltsa le a felvevő vagy a stream kódolóját." Basic.Settings.Output.Simple.Encoder.Software="Szoftver (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardver (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardver (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardver (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Szoftveres (x264 alacsony CPU használati készlet, növekvő fájlméret)" Basic.Settings.Output.VideoBitrate="Videó bitráta" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="Felvétel indítása" Basic.Hotkeys.StopRecording="Felvétel leállítása" Basic.Hotkeys.SelectScene="Jelenethez kapcsolás" +Basic.SystemTray.Show="Mutat" +Basic.SystemTray.Hide="Elrejt" + +Basic.SystemTray.Message.Reconnecting="Szétkapcsolva. Újrakapcsolódás..." + Hotkeys.Insert="Beszúrás" Hotkeys.Delete="Törlés" Hotkeys.Home="Teteje" diff --git a/UI/data/locale/it-IT.ini b/UI/data/locale/it-IT.ini index 44f2cff848c1f6..db93731da0b015 100644 --- a/UI/data/locale/it-IT.ini +++ b/UI/data/locale/it-IT.ini @@ -308,6 +308,7 @@ Basic.MainMenu.View.StatusBar="&Barra di stato" Basic.MainMenu.SceneCollection="&Collezione scene" Basic.MainMenu.Profile="&Profilo" + Basic.MainMenu.Help="Aiuto (&H)" Basic.MainMenu.Help.Website="Visita il sito" Basic.MainMenu.Help.Logs="File di &log" @@ -488,6 +489,8 @@ Basic.Hotkeys.StartRecording="Inizia registrazione" Basic.Hotkeys.StopRecording="Ferma registrazione" Basic.Hotkeys.SelectScene="Passa alla scena" + + Hotkeys.Insert="Ins" Hotkeys.Delete="Canc" Hotkeys.Home="Home" diff --git a/UI/data/locale/ja-JP.ini b/UI/data/locale/ja-JP.ini index 644dfbca0e0685..9fcd29c2429c43 100644 --- a/UI/data/locale/ja-JP.ini +++ b/UI/data/locale/ja-JP.ini @@ -48,6 +48,7 @@ Left="左" Right="右" Top="上" Bottom="下" +Reset="リセット" QuickTransitions.SwapScenes="トランジション後にプレビュー/出力シーンを入れ替え" QuickTransitions.SwapScenesTT="(出力のオリジナルシーンがまだ存在する場合)、トランジション後のプレビューと出力シーンを入れ替えます。\nこれは出力のオリジナルシーンに加えられた可能性があるすべての変更を元に戻しません。" @@ -218,9 +219,9 @@ Basic.Filters="フィルタ" Basic.Filters.AsyncFilters="音声/映像フィルタ" Basic.Filters.AudioFilters="音声フィルタ" Basic.Filters.EffectFilters="エフェクトフィルタ" -Basic.Filters.Title="'%1' のためのフィルター" -Basic.Filters.AddFilter.Title="フィルター名" -Basic.Filters.AddFilter.Text="フィルターの名前を指定してください" +Basic.Filters.Title="'%1' のためのフィルタ" +Basic.Filters.AddFilter.Title="フィルタ名" +Basic.Filters.AddFilter.Text="フィルタの名前を指定してください" Basic.TransformWindow="シーン アイテム 変換" Basic.TransformWindow.Position="位置" @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="ステータスバー(&S)" Basic.MainMenu.SceneCollection="シーンコレクション(&S)" Basic.MainMenu.Profile="プロファイル(&P)" +Basic.MainMenu.Tools="ツール(&T)" + Basic.MainMenu.Help="ヘルプ(&H)" Basic.MainMenu.Help.Website="ウェブサイト(&W)" Basic.MainMenu.Help.Logs="ログファイル(&L)" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="言語" Basic.Settings.General.WarnBeforeStartingStream="配信を開始するときに確認ダイアログを表示する" Basic.Settings.General.WarnBeforeStoppingStream="配信を停止するときに確認ダイアログを表示する" Basic.Settings.General.HideProjectorCursor="プロジェクター上のカーソルを非表示にする" +Basic.Settings.General.ProjectorAlwaysOnTop="プロジェクタを常に手前に表示させる" Basic.Settings.General.Snapping="ソース配置のスナップ" Basic.Settings.General.ScreenSnapping="画面の端にソースをスナップする" Basic.Settings.General.CenterSnapping="水平方向および垂直方向の中心にソースをスナップする" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="他のソースにソースをスナッ Basic.Settings.General.SnapDistance="スナップ感度" Basic.Settings.General.RecordWhenStreaming="配信時に自動的に録画" Basic.Settings.General.KeepRecordingWhenStreamStops="配信が停止しても録画を継続" +Basic.Settings.General.SysTrayEnabled="システムトレイアイコンを有効にする" +Basic.Settings.General.SysTrayWhenStarted="起動時にシステムトレイへ最小化" Basic.Settings.Stream="配信" Basic.Settings.Stream.StreamType="配信種別" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="無損失品質警告!" Basic.Settings.Output.Simple.Warn.MultipleQSV="警告: 配信と同時に録画する場合複数の独立した QSV エンコーダは使用できません。 配信と同時に録画したい場合、配信エンコーダか録画エンコーダのどちらかを変更してください。" Basic.Settings.Output.Simple.Encoder.Software="ソフトウェア (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="ハードウェア (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="ハードウェア (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="ハードウェア (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="ソフトウェア (x264 CPU使用率の低いプリセット、ファイルサイズ増加)" Basic.Settings.Output.VideoBitrate="映像ビットレート" @@ -420,7 +427,7 @@ Basic.Settings.Video="映像" Basic.Settings.Video.Adapter="ビデオアダプター:" Basic.Settings.Video.BaseResolution="基本 (キャンバス) 解像度:" Basic.Settings.Video.ScaledResolution="出力 (スケーリング) 解像度:" -Basic.Settings.Video.DownscaleFilter="縮小フィルター:" +Basic.Settings.Video.DownscaleFilter="縮小フィルタ:" Basic.Settings.Video.DisableAeroWindows="エアロ無効 (Windows のみ)" Basic.Settings.Video.FPS="FPS:" Basic.Settings.Video.FPSCommon="FPS 共通値" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="録画開始" Basic.Hotkeys.StopRecording="録画終了" Basic.Hotkeys.SelectScene="シーン切り替え" +Basic.SystemTray.Show="表示" +Basic.SystemTray.Hide="非表示" + +Basic.SystemTray.Message.Reconnecting="切断。 再接続..." + Hotkeys.Insert="Insertキー" Hotkeys.Delete="Deleteキー" Hotkeys.Home="Homeキー" diff --git a/UI/data/locale/ko-KR.ini b/UI/data/locale/ko-KR.ini index 4f545f89bf24d1..50353f0b588034 100644 --- a/UI/data/locale/ko-KR.ini +++ b/UI/data/locale/ko-KR.ini @@ -48,6 +48,7 @@ Left="왼쪽" Right="오른쪽" Top="위" Bottom="아래" +Reset="초기화" QuickTransitions.SwapScenes="전환 후 미리 보기/출력 장면을 교체" QuickTransitions.SwapScenesTT="(만약 출력 쪽 원본 장면이 있을 때) 전환 작업 이후 미리 보기와 출력 장면을 교체합니다. \n출력 쪽 원본 장면에서 변경한 내용은 사라지지 않습니다." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="상태 표시줄(&S)" Basic.MainMenu.SceneCollection="장면 모음(&S)" Basic.MainMenu.Profile="프로파일(&P)" +Basic.MainMenu.Tools="도구(&T)" + Basic.MainMenu.Help="도움말(&H)" Basic.MainMenu.Help.Website="웹사이트 방문(&W)" Basic.MainMenu.Help.Logs="기록 파일(&L)" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="언어" Basic.Settings.General.WarnBeforeStartingStream="방송을 시작할 때 확인 대화 상자 표시" Basic.Settings.General.WarnBeforeStoppingStream="방송을 중단할 때 확인 대화 상자 표시" Basic.Settings.General.HideProjectorCursor="프로젝터 위 커서 숨기기" +Basic.Settings.General.ProjectorAlwaysOnTop="프로젝터를 항상 위로" Basic.Settings.General.Snapping="소스를 자석처럼 달라붙여서 정렬" Basic.Settings.General.ScreenSnapping="소스를 화면 변두리에 붙임" Basic.Settings.General.CenterSnapping="소스를 수평과 수직 중앙에 붙임" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="소스를 다른 소스에 붙임" Basic.Settings.General.SnapDistance="자석 감도" Basic.Settings.General.RecordWhenStreaming="방송 시 자동으로 녹화" Basic.Settings.General.KeepRecordingWhenStreamStops="방송을 중단하더라도 녹화는 유지" +Basic.Settings.General.SysTrayEnabled="시스템 트레이 아이콘 활성화" +Basic.Settings.General.SysTrayWhenStarted="시작할 때 시스템 트레이로 최소화" Basic.Settings.Stream="방송" Basic.Settings.Stream.StreamType="방송 형식" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="녹화 시작" Basic.Hotkeys.StopRecording="녹화 중단" Basic.Hotkeys.SelectScene="장면 전환" +Basic.SystemTray.Show="보이기" +Basic.SystemTray.Hide="숨기기" + +Basic.SystemTray.Message.Reconnecting="접속이 끊김. 재접속 시도 중..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/lt-LT.ini b/UI/data/locale/lt-LT.ini index 00c8f27f9b7332..d3f837b519317c 100644 --- a/UI/data/locale/lt-LT.ini +++ b/UI/data/locale/lt-LT.ini @@ -271,6 +271,9 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Perkelti į apačią" + + + diff --git a/UI/data/locale/ms-MY.ini b/UI/data/locale/ms-MY.ini index 924db64de139c3..3970dbb0fe6ae8 100644 --- a/UI/data/locale/ms-MY.ini +++ b/UI/data/locale/ms-MY.ini @@ -255,6 +255,7 @@ Basic.MainMenu.Edit.Order.MoveDown="Gerakkan ke &bawah" Basic.MainMenu.Profile="&Profil" + Basic.MainMenu.Help.Website="Lawat laman &Web" Basic.MainMenu.Help.Logs="Fail &Log" Basic.MainMenu.Help.Logs.ShowLogs="&Tunjukkan Fail-Fail Log" @@ -297,6 +298,8 @@ Basic.Settings.Advanced.FormatWarning="Amaran:Format warna selain daripada 'NV12 + + Hotkeys.NumpadDecimal="Perpuluhan Numpad" diff --git a/UI/data/locale/nb-NO.ini b/UI/data/locale/nb-NO.ini index 9683ab68ba1c95..1fafc39668f1da 100644 --- a/UI/data/locale/nb-NO.ini +++ b/UI/data/locale/nb-NO.ini @@ -296,6 +296,7 @@ Basic.MainMenu.Edit.AdvAudio="&Avanserte lydinstillinger" Basic.MainMenu.SceneCollection="&Scenesamling" Basic.MainMenu.Profile="&Profil" + Basic.MainMenu.Help="&Hjelp" Basic.MainMenu.Help.Website="Besøk &nettsted" Basic.MainMenu.Help.Logs="&Loggfiler" @@ -469,6 +470,8 @@ Basic.Hotkeys.StartRecording="Start opptak" Basic.Hotkeys.StopRecording="Stopp opptak" Basic.Hotkeys.SelectScene="Bytt til scene" + + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/nl-NL.ini b/UI/data/locale/nl-NL.ini index e39ba29d05f09d..93c31a08bb4e29 100644 --- a/UI/data/locale/nl-NL.ini +++ b/UI/data/locale/nl-NL.ini @@ -48,6 +48,7 @@ Left="Links" Right="Rechts" Top="Boven" Bottom="Onder" +Reset="Herstellen" QuickTransitions.SwapScenes="Preview-/uitvoerscenes verwisselen na overgang" QuickTransitions.SwapScenesTT="Verwisselt de preview- en uitvoercenes na een overgang (als de originele uitvoerscène nog bestaat.)\nDit zal geen veranderingen ongedaan maken die mogelijk zijn gemaakt aan de originele uitvoerscène." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Statusbalk" Basic.MainMenu.SceneCollection="&Scèneverzameling" Basic.MainMenu.Profile="&Profiel" +Basic.MainMenu.Tools="&Tools" + Basic.MainMenu.Help="&Help" Basic.MainMenu.Help.Website="&Website Bezoeken" Basic.MainMenu.Help.Logs="&Logbestanden" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Taal" Basic.Settings.General.WarnBeforeStartingStream="Laat bevestigingsvenster zien bij het starten van streams" Basic.Settings.General.WarnBeforeStoppingStream="Laat bevestiginsvenster zien bij het stoppen van streams" Basic.Settings.General.HideProjectorCursor="Verberg cursor boven projectors" +Basic.Settings.General.ProjectorAlwaysOnTop="Houd projectoren altijd bovenaan" Basic.Settings.General.Snapping="Bronuitlijning" Basic.Settings.General.ScreenSnapping="Bronnen uitlijnen op de rand van het scherm" Basic.Settings.General.CenterSnapping="Bronnen uitlijnen op het horizontale en verticale midden" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Bronnen uitlijnen op andere bronnen" Basic.Settings.General.SnapDistance="Gevoeligheid" Basic.Settings.General.RecordWhenStreaming="Stream automatisch opnemen" Basic.Settings.General.KeepRecordingWhenStreamStops="Opname voortzetten als de stream stopt" +Basic.Settings.General.SysTrayEnabled="Systeemvakicoon weergeven" +Basic.Settings.General.SysTrayWhenStarted="Naar systeemvak minimaliseren bij opstarten" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Stream Type" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Lossless kwaliteit waarschuwin Basic.Settings.Output.Simple.Warn.MultipleQSV="Waarschuwing: Je kunt niet meerdere QSV encoders gebruiken wanneer je tegelijkertijd aan het streamen en opnemen bent. Als je tegelijkertijd wil streamen en opnemen, verander dan de opname- of streamencoder." Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Hardware (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Software (x264 laag cpu gebruik, verhoogt bestandsgrootte)" Basic.Settings.Output.VideoBitrate="Video Bitrate" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="Opname Starten" Basic.Hotkeys.StopRecording="Opname Stoppen" Basic.Hotkeys.SelectScene="Wissel naar scène" +Basic.SystemTray.Show="Weergeven" +Basic.SystemTray.Hide="Verbergen" + +Basic.SystemTray.Message.Reconnecting="Verbinding verbroken. Opnieuw verbinden..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/pl-PL.ini b/UI/data/locale/pl-PL.ini index 007ff2c9a13921..14018ad8a741eb 100644 --- a/UI/data/locale/pl-PL.ini +++ b/UI/data/locale/pl-PL.ini @@ -48,6 +48,7 @@ Left="Od lewej" Right="Od prawej" Top="Od góry" Bottom="Od dołu" +Reset="Reset" QuickTransitions.SwapScenes="Zamień podgląd/wyjście scen po przejściu" QuickTransitions.SwapScenesTT="Zamienia podgląd i wyjście scen po przejściu (jeżeli wyjście oryginalnej sceny istnieje).\nNie przywraca to zmian jakie zostały dokonane w oryginalnej scenie." @@ -306,9 +307,11 @@ Basic.MainMenu.View.SceneTransitions="Efekty &przejścia scen" Basic.MainMenu.View.StatusBar="Pasek &stanu" Basic.MainMenu.SceneCollection="Zbiór &scen" -Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Profile="P&rofil" -Basic.MainMenu.Help="&Pomoc" +Basic.MainMenu.Tools="&Narzędzia" + +Basic.MainMenu.Help="P&omoc" Basic.MainMenu.Help.Website="Od&wiedź naszą stronę" Basic.MainMenu.Help.Logs="P&liki dziennika" Basic.MainMenu.Help.Logs.ShowLogs="Pokaż pliki dziennika (&s)" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Język" Basic.Settings.General.WarnBeforeStartingStream="Pokaż komunikat potwierdzenia uruchomienia streamowania" Basic.Settings.General.WarnBeforeStoppingStream="Pokaż komunikat potwierdzenia zatrzymania streamowania" Basic.Settings.General.HideProjectorCursor="Ukryj kursor podglądu na pełnym ekranie" +Basic.Settings.General.ProjectorAlwaysOnTop="Podgląd na pełnym ekranie zawsze na wierzchu" Basic.Settings.General.Snapping="Przyciąganie elementów źródłowych" Basic.Settings.General.ScreenSnapping="Przyciągaj do krawędzi ekranu" Basic.Settings.General.CenterSnapping="Przyciągaj do poziomego i pionowego środka" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Przyciągaj źródła do innych źródeł Basic.Settings.General.SnapDistance="Czułość przyciągania" Basic.Settings.General.RecordWhenStreaming="Automatyczne nagrywanie streamu" Basic.Settings.General.KeepRecordingWhenStreamStops="Zachowaj nagranie po zatrzymaniu streamu" +Basic.Settings.General.SysTrayEnabled="Wyświetlaj ikonę w zasobniku systemowym" +Basic.Settings.General.SysTrayWhenStarted="Minimalizuj do zasobnika systemowego podczas uruchamiania" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Typ streamu" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Ostrzeżenie o bezstratnej jak Basic.Settings.Output.Simple.Warn.MultipleQSV="Ostrzeżenie: Korzystanie z wielu różnych enkoderów QSV do streamowania i nagrywania jest niedozwolone. Jeżeli chcesz streamować i nagrywać w tym samym czasie, zmień ustawienia enkodera nagrywania bądź streamowania." Basic.Settings.Output.Simple.Encoder.Software="Programowy (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Sprzętowy (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Sprzętowy (AMD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Sprzętowy (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Programowy (x264 ustawienia małego zużycia procesora, zwiększa wielkość pliku)" Basic.Settings.Output.VideoBitrate="Bitrate obrazu" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="Rozpocznij nagrywanie" Basic.Hotkeys.StopRecording="Zatrzymaj nagrywanie" Basic.Hotkeys.SelectScene="Przełącz na scenę" +Basic.SystemTray.Show="Pokaż" +Basic.SystemTray.Hide="Ukryj" + +Basic.SystemTray.Message.Reconnecting="Rozłączony. Łączę ponownie..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/pt-BR.ini b/UI/data/locale/pt-BR.ini index 139997a223b6fa..15cf8d3a822a88 100644 --- a/UI/data/locale/pt-BR.ini +++ b/UI/data/locale/pt-BR.ini @@ -145,7 +145,10 @@ Basic.DisplayCapture="Captura de tela" Basic.Main.PreviewConextMenu.Enable="Ativar pré-vizualização" +ScaleFiltering="Filtragem de escala" +ScaleFiltering.Point="Ponto" ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicúbico" Deinterlacing="Desentrelaçamento" Deinterlacing.Discard="Descartar" @@ -276,6 +279,7 @@ Basic.MainMenu.Edit.Undo="&Desfazer" Basic.MainMenu.Edit.Redo="&Refazer" Basic.MainMenu.Edit.UndoAction="&Desfazer $1" Basic.MainMenu.Edit.RedoAction="&Refazer $1" +Basic.MainMenu.Edit.LockPreview="&Bloquear pré-visualização" Basic.MainMenu.Edit.Transform="&Transformar" Basic.MainMenu.Edit.Transform.EditTransform="&Editar Transformação..." Basic.MainMenu.Edit.Transform.ResetTransform="&Limpar Transformação" @@ -295,12 +299,16 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Mover para a &Base" Basic.MainMenu.Edit.AdvAudio="&Propriedades de áudio avançadas" Basic.MainMenu.View="Mostrar" +Basic.MainMenu.View.Toolbars="&Barras de Ferramentas" +Basic.MainMenu.View.Toolbars.Listboxes="Caixa de &Listagem" Basic.MainMenu.View.SceneTransitions="Transições de Cena" Basic.MainMenu.View.StatusBar="Barra de Status" Basic.MainMenu.SceneCollection="&Coleção de cena" Basic.MainMenu.Profile="&Perfil" +Basic.MainMenu.Tools="Ferramentas (&T)" + Basic.MainMenu.Help="&Ajuda" Basic.MainMenu.Help.Website="Visitar &website" Basic.MainMenu.Help.Logs="&Arquivos de Log" @@ -319,12 +327,17 @@ Basic.Settings.General.Theme="Tema" Basic.Settings.General.Language="Idioma" Basic.Settings.General.WarnBeforeStartingStream="Mostrar diálogo de confirmação quando iniciar transmissões" Basic.Settings.General.WarnBeforeStoppingStream="Mostrar diálogo de confirmação quando terminar transmissões" +Basic.Settings.General.HideProjectorCursor="Ocultar o cursor sobre projetores" +Basic.Settings.General.ProjectorAlwaysOnTop="Colocar projetores sempre no topo" Basic.Settings.General.Snapping="Alinhamentos com encaixe na Cena" Basic.Settings.General.ScreenSnapping="Encaixar Fontes ás bordas da tela" Basic.Settings.General.CenterSnapping="Encaixar Fontes aos centros vertical e horizontal" Basic.Settings.General.SourceSnapping="Encaixar fontes com outras fontes" Basic.Settings.General.SnapDistance="Sensibilidade de Encaixamento" +Basic.Settings.General.RecordWhenStreaming="Gravar automaticamente quando estiver transmitindo" Basic.Settings.General.KeepRecordingWhenStreamStops="Continuar gravando quando a transmissão parar" +Basic.Settings.General.SysTrayEnabled="Habilitar o ícone de bandeja do sistema" +Basic.Settings.General.SysTrayWhenStarted="Minimizar para a bandeja do sistema quando iniciar" Basic.Settings.Stream="Stream" Basic.Settings.Stream.StreamType="Tipo de Stream" @@ -351,6 +364,7 @@ Basic.Settings.Output.Simple.Warn.Encoder="Aviso: Gravar com um codificador de s Basic.Settings.Output.Simple.Warn.Lossless="Aviso: Qualidade Lossless gera arquivos muito grandes! A qualidade Lossless pode usar mais de 7 gigabytes de espaço em disco por minuto em altas resoluções e framerates. Lossless não é recomendada para gravações longas, a menos que se tenha uma grande quantidade de espaço em disco disponível." Basic.Settings.Output.Simple.Warn.Lossless.Msg="Tem certeza que deseja usar qualidade lossless?" Basic.Settings.Output.Simple.Warn.Lossless.Title="Aviso de qualidade lossless!" +Basic.Settings.Output.Simple.Warn.MultipleQSV="Aviso: Você não pode usar vários codificadores QSV separados quando estiver transmitindo e gravando ao mesmo tempo. Se você deseja transmitir e gravar ao mesmo tempo, por favor altere o codificador de gravação ou o de transmissão." Basic.Settings.Output.Simple.Encoder.Software="Software (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Hardware (QSV)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Hardware (NVENC)" @@ -458,6 +472,8 @@ Basic.Settings.Advanced.StreamDelay="Atraso da transmissão" Basic.Settings.Advanced.StreamDelay.Duration="Duração (segundos)" Basic.Settings.Advanced.StreamDelay.Preserve="Preservar o ponto de corte (aumento de atraso) quando reconectar" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Uso de memória estimado: %1 MB" +Basic.Settings.Advanced.Network="Rede" +Basic.Settings.Advanced.Network.BindToIP="Transmitir pelo IP" Basic.AdvAudio="Propriedades de áudio avançadas" Basic.AdvAudio.Name="Nome" @@ -476,6 +492,11 @@ Basic.Hotkeys.StartRecording="Iniciar gravação" Basic.Hotkeys.StopRecording="Parar gravação" Basic.Hotkeys.SelectScene="Mudar de cena" +Basic.SystemTray.Show="Exibir" +Basic.SystemTray.Hide="Ocultar" + +Basic.SystemTray.Message.Reconnecting="Desconectado. Reconectando-se..." + Hotkeys.Insert="Inserir" Hotkeys.Delete="Deletar" Hotkeys.Home="Início" diff --git a/UI/data/locale/pt-PT.ini b/UI/data/locale/pt-PT.ini index e22eb7aaa2f5e4..6edac0afe567ec 100644 --- a/UI/data/locale/pt-PT.ini +++ b/UI/data/locale/pt-PT.ini @@ -290,6 +290,7 @@ Basic.MainMenu.Edit.AdvAudio="Propriedades &avançadas de áudio" Basic.MainMenu.SceneCollection="Coleção de cena" Basic.MainMenu.Profile="&Perfil" + Basic.MainMenu.Help="&Ajuda" Basic.MainMenu.Help.Website="Visitar &website" Basic.MainMenu.Help.Logs="Ficeiros de &Log" @@ -451,6 +452,8 @@ Basic.Hotkeys.StartRecording="Iniciar gravação" Basic.Hotkeys.StopRecording="Parar gravação" Basic.Hotkeys.SelectScene="Mudar para cena" + + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/ro-RO.ini b/UI/data/locale/ro-RO.ini index 9abadd4df6cf78..e58bc1412eff55 100644 --- a/UI/data/locale/ro-RO.ini +++ b/UI/data/locale/ro-RO.ini @@ -291,6 +291,7 @@ Basic.MainMenu.Edit.AdvAudio="Proprietăți audio &avansate" Basic.MainMenu.SceneCollection="Colecție de &scene" Basic.MainMenu.Profile="&Profil" + Basic.MainMenu.Help="&Ajutor" Basic.MainMenu.Help.Website="Vizitează site-ul &web" Basic.MainMenu.Help.Logs="Fișiere jurna&l" @@ -460,6 +461,8 @@ Basic.Hotkeys.StartRecording="Pornește înregistrarea" Basic.Hotkeys.StopRecording="Oprește înregistrarea" Basic.Hotkeys.SelectScene="Comută la scenă" + + Hotkeys.Insert="Inserează" Hotkeys.Delete="Șterge" Hotkeys.Home="Home" diff --git a/UI/data/locale/ru-RU.ini b/UI/data/locale/ru-RU.ini index 3aee290e53a521..b170c8b80e20ff 100644 --- a/UI/data/locale/ru-RU.ini +++ b/UI/data/locale/ru-RU.ini @@ -48,6 +48,7 @@ Left="Слева" Right="Справа" Top="Сверху" Bottom="Снизу" +Reset="Сбросить" QuickTransitions.SwapScenes="Замена Просмотра/Вывода Сцены После Перехода" QuickTransitions.SwapScenesTT="Замена просмотра и вывода сцены после перехода (если выходная оригинальная сцена до сих пор существует).\nЭто будет не отмена каких-либо изменений, что, возможно, было сделано в выходной оригинальной сцены." @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="&Строка состояния" Basic.MainMenu.SceneCollection="Коллекция сцен" Basic.MainMenu.Profile="Профиль" +Basic.MainMenu.Tools="&Инструменты" + Basic.MainMenu.Help="&Справка" Basic.MainMenu.Help.Website="Посетить &веб-сайт" Basic.MainMenu.Help.Logs="&Log файлы" @@ -327,13 +330,16 @@ Basic.Settings.General.Language="Язык" Basic.Settings.General.WarnBeforeStartingStream="Показывать окно подтверждения при запуске трансляции" Basic.Settings.General.WarnBeforeStoppingStream="Показывать окно подтверждения при остановке трансляции" Basic.Settings.General.HideProjectorCursor="Скрыть курсор за проекторы" +Basic.Settings.General.ProjectorAlwaysOnTop="Показывать проекторы поверх всего остального" Basic.Settings.General.Snapping="Привязка расположения источника" Basic.Settings.General.ScreenSnapping="Привязка к краю экрана" Basic.Settings.General.CenterSnapping="Привязка к центру по горизонтали и вертикали" Basic.Settings.General.SourceSnapping="Привязка к другим источникам" Basic.Settings.General.SnapDistance="Чувствительность привязки" Basic.Settings.General.RecordWhenStreaming="Автоматическая запись при стриме" -Basic.Settings.General.KeepRecordingWhenStreamStops="Сохранить запись, когда стрим остановится" +Basic.Settings.General.KeepRecordingWhenStreamStops="Продолжить запись, когда стрим остановится" +Basic.Settings.General.SysTrayEnabled="Показывать иконку в системном трее" +Basic.Settings.General.SysTrayWhenStarted="Скрывать окно в системный трей при запуске" Basic.Settings.Stream="Вещание" Basic.Settings.Stream.StreamType="Тип вещания" @@ -363,6 +369,7 @@ Basic.Settings.Output.Simple.Warn.Lossless.Title="Предупреждение Basic.Settings.Output.Simple.Warn.MultipleQSV="Предупреждение: вы не можете использовать несколько различных QSV-кодировщиков при одновременной трансляции и записи. Если вы хотите одновременно и транслировать, и записывать, поменяйте либо кодировщик вещания, либо кодировщик записи." Basic.Settings.Output.Simple.Encoder.Software="Программный (x264)" Basic.Settings.Output.Simple.Encoder.Hardware.QSV="Аппаратный (QSV)" +Basic.Settings.Output.Simple.Encoder.Hardware.AMD="Аппаратный (AМD)" Basic.Settings.Output.Simple.Encoder.Hardware.NVENC="Аппаратный (NVENC)" Basic.Settings.Output.Simple.Encoder.SoftwareLowCPU="Программный (x264 с низкой нагрузкой на ЦП, увеличивает размер файла)" Basic.Settings.Output.VideoBitrate="Видео битрейт" @@ -488,6 +495,11 @@ Basic.Hotkeys.StartRecording="Начать запись" Basic.Hotkeys.StopRecording="Остановить запись" Basic.Hotkeys.SelectScene="Перейти на сцену" +Basic.SystemTray.Show="Показать" +Basic.SystemTray.Hide="Скрыть" + +Basic.SystemTray.Message.Reconnecting="Соединение потеряно. Подключаемся заново..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/sk-SK.ini b/UI/data/locale/sk-SK.ini index 45db79573fe5bb..bec05a6df6f9f3 100644 --- a/UI/data/locale/sk-SK.ini +++ b/UI/data/locale/sk-SK.ini @@ -177,6 +177,7 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Premiestniť naspodok (&B)" + Basic.MainMenu.Help="Pomoc (&H)" Basic.MainMenu.Help.Logs="&Log súbory" Basic.MainMenu.Help.Logs.ShowLogs="Zobraziť log &súbory" @@ -229,3 +230,5 @@ Basic.Settings.Audio.Channels="Kanály" + + diff --git a/UI/data/locale/sl-SI.ini b/UI/data/locale/sl-SI.ini index adb56a35462282..780c52f285a619 100644 --- a/UI/data/locale/sl-SI.ini +++ b/UI/data/locale/sl-SI.ini @@ -174,6 +174,7 @@ Basic.MainMenu.Edit.Order.MoveToBottom="Premakni na &Dno" + Basic.MainMenu.Help="&Pomoč" Basic.MainMenu.Help.Logs="&Dnevniki" Basic.MainMenu.Help.Logs.ShowLogs="&Pokaži Zapisnik" @@ -238,3 +239,5 @@ Basic.Settings.Advanced.Video.ColorRange.Full="Celotno" + + diff --git a/UI/data/locale/sr-CS.ini b/UI/data/locale/sr-CS.ini index 35b99aa27d2beb..2097d044057bcf 100644 --- a/UI/data/locale/sr-CS.ini +++ b/UI/data/locale/sr-CS.ini @@ -308,6 +308,8 @@ Basic.MainMenu.View.StatusBar="&Statusna linija" Basic.MainMenu.SceneCollection="Kolekcija &scena" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="Ala&ti" + Basic.MainMenu.Help="Pomoć (&H)" Basic.MainMenu.Help.Website="Poseti stranicu (&W)" Basic.MainMenu.Help.Logs="&Log datoteke" @@ -327,6 +329,7 @@ Basic.Settings.General.Language="Jezik" Basic.Settings.General.WarnBeforeStartingStream="Prikaži prozor za potvrdu kada se započinju strimovi" Basic.Settings.General.WarnBeforeStoppingStream="Prikaži prozor za potvrdu kada se zaustavljaju strimovi" Basic.Settings.General.HideProjectorCursor="Sakrij pokazivač na projektorima" +Basic.Settings.General.ProjectorAlwaysOnTop="Uvek postavi projektor na vrh prozora" Basic.Settings.General.Snapping="Poravnavanje privlačenjem izvora" Basic.Settings.General.ScreenSnapping="Privuci izvore ivici ekrana" Basic.Settings.General.CenterSnapping="Privuci izvore horizontalnoj i vertikalnoj sredini" @@ -334,6 +337,8 @@ Basic.Settings.General.SourceSnapping="Privlačenje izvora ka drugim izvorima" Basic.Settings.General.SnapDistance="Osetljivost privlačenja" Basic.Settings.General.RecordWhenStreaming="Automatsko snimanje pri emitovanju" Basic.Settings.General.KeepRecordingWhenStreamStops="Nastavi snimati kada se emitovanje zaustavi" +Basic.Settings.General.SysTrayEnabled="Omogući ikonicu u sistemskom panelu" +Basic.Settings.General.SysTrayWhenStarted="Pri pokretanju minimiziraj na ikonicu u sistemskom panelu" Basic.Settings.Stream="Strim" Basic.Settings.Stream.StreamType="Vrsta strima" @@ -488,6 +493,11 @@ Basic.Hotkeys.StartRecording="Počni snimanje" Basic.Hotkeys.StopRecording="Zaustavi snimanje" Basic.Hotkeys.SelectScene="Prebaci na scenu" +Basic.SystemTray.Show="Prikaži" +Basic.SystemTray.Hide="Sakrij" + +Basic.SystemTray.Message.Reconnecting="Veza prekinuta. Ponovno uspostavljanje..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/sr-SP.ini b/UI/data/locale/sr-SP.ini index 95d6e8ca2e3c6e..a9c5a4483ad7ee 100644 --- a/UI/data/locale/sr-SP.ini +++ b/UI/data/locale/sr-SP.ini @@ -308,6 +308,8 @@ Basic.MainMenu.View.StatusBar="Статусна линија (&S)" Basic.MainMenu.SceneCollection="Колекција сцена (&S)" Basic.MainMenu.Profile="Профил (&P)" +Basic.MainMenu.Tools="Алати (&T)" + Basic.MainMenu.Help="Помоћ (&H)" Basic.MainMenu.Help.Website="Посети страницу (&W)" Basic.MainMenu.Help.Logs="Лог датотеке (&L)" @@ -327,6 +329,7 @@ Basic.Settings.General.Language="Језик" Basic.Settings.General.WarnBeforeStartingStream="Прикажи прозор за потврду када се започињу стримови" Basic.Settings.General.WarnBeforeStoppingStream="Прикажи прозор за потврду када се заустављају стримови" Basic.Settings.General.HideProjectorCursor="Сакриј показивач на пројекторима" +Basic.Settings.General.ProjectorAlwaysOnTop="Увек постави пројектор на врх прозора" Basic.Settings.General.Snapping="Поравнавање привлачењем извора" Basic.Settings.General.ScreenSnapping="Привуци изворе ивици екрана" Basic.Settings.General.CenterSnapping="Привуци изворе хоризонталној и вертикалној средини" @@ -334,6 +337,8 @@ Basic.Settings.General.SourceSnapping="Привлачење извора ка д Basic.Settings.General.SnapDistance="Осетљивост привлачења" Basic.Settings.General.RecordWhenStreaming="Аутоматско снимање при емитовању" Basic.Settings.General.KeepRecordingWhenStreamStops="Настави снимати када се емитовање заустави" +Basic.Settings.General.SysTrayEnabled="Омогући иконицу у системском панелу" +Basic.Settings.General.SysTrayWhenStarted="При покретању минимизирај на иконицу у системском панелу" Basic.Settings.Stream="Стрим" Basic.Settings.Stream.StreamType="Врста стрима" @@ -488,6 +493,11 @@ Basic.Hotkeys.StartRecording="Почни снимање" Basic.Hotkeys.StopRecording="Заустави снимање" Basic.Hotkeys.SelectScene="Пребаци на сцену" +Basic.SystemTray.Show="Прикажи" +Basic.SystemTray.Hide="Сакриј" + +Basic.SystemTray.Message.Reconnecting="Веза прекинута. Поновно успостављање..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/sv-SE.ini b/UI/data/locale/sv-SE.ini index 8491cff3369659..81c34c177b2b67 100644 --- a/UI/data/locale/sv-SE.ini +++ b/UI/data/locale/sv-SE.ini @@ -48,6 +48,7 @@ Left="Vänster" Right="Höger" Top="Överkant" Bottom="Nederkant" +Reset="Återställ" QuickTransitions.SwapScenes="Byt plats på Förhandsvisnings-/utdatascenerna efter skifte" QuickTransitions.SwapScenesTT="Byter plats på förhandsvisnings- och utdatascenerna efter övergång (om utdatans originalscen fortfarande finns). \nDet här kommer inte att ångra några förändringar i utdatans originalscen." @@ -149,8 +150,11 @@ ScaleFiltering.Bilinear="Bilinjär" ScaleFiltering.Bicubic="Bikubisk" ScaleFiltering.Lanczos="Lanczos" +Deinterlacing="Avflätning" Deinterlacing.Discard="Avfärda" Deinterlacing.Retro="Retro" +Deinterlacing.Blend="Blanda" +Deinterlacing.Blend2x="Blanda 2x" Deinterlacing.Linear="Linjär" Deinterlacing.Linear2x="Linjär 2x" Deinterlacing.Yadif="Yadif" @@ -192,6 +196,8 @@ Basic.PropertiesWindow.AddDir="Lägg till mapp" Basic.PropertiesWindow.AddURL="Lägg till Sökväg/URL" Basic.PropertiesWindow.AddEditableListDir="Lägg till mapp i '%1'" Basic.PropertiesWindow.AddEditableListFiles="Lägg till filer i '%1'" +Basic.PropertiesWindow.AddEditableListEntry="Lägg till post i '%1'" +Basic.PropertiesWindow.EditEditableListEntry="Redigera post från '%1'" Basic.PropertiesView.FPS.Simple="Enkla bildfrekvensvärden" Basic.PropertiesView.FPS.Rational="Rationella bildfrekvensvärden" @@ -273,6 +279,7 @@ Basic.MainMenu.Edit.Undo="&Ångra" Basic.MainMenu.Edit.Redo="&Gör om" Basic.MainMenu.Edit.UndoAction="&Ångra $1" Basic.MainMenu.Edit.RedoAction="&Gör om $1" +Basic.MainMenu.Edit.LockPreview="&Lås förhandsvisning" Basic.MainMenu.Edit.Transform="&Omvandla" Basic.MainMenu.Edit.Transform.EditTransform="&Redigera omvandling..." Basic.MainMenu.Edit.Transform.ResetTransform="&Återställ omvandling" @@ -300,6 +307,8 @@ Basic.MainMenu.View.StatusBar="&Statusfält" Basic.MainMenu.SceneCollection="&Scensamling" Basic.MainMenu.Profile="&Profil" +Basic.MainMenu.Tools="&Verktyg" + Basic.MainMenu.Help="&Hjälp" Basic.MainMenu.Help.Website="Besök &webbplats" Basic.MainMenu.Help.Logs="&Loggfiler" @@ -319,12 +328,16 @@ Basic.Settings.General.Language="Språk" Basic.Settings.General.WarnBeforeStartingStream="Visa bekräftelsedialog när ström startas" Basic.Settings.General.WarnBeforeStoppingStream="Visa bekräftelsedialog när ström stoppas" Basic.Settings.General.HideProjectorCursor="Dölj pekaren över projektorer" +Basic.Settings.General.ProjectorAlwaysOnTop="Lägg alltid projektorer överst" +Basic.Settings.General.Snapping="Fäst justerbara källor" Basic.Settings.General.ScreenSnapping="Fäst källor till skärmens kant" Basic.Settings.General.CenterSnapping="Fäst källor till den horisontala och vertikala mittenlinjen" Basic.Settings.General.SourceSnapping="Fäst källor till andra källor" Basic.Settings.General.SnapDistance="Fästkänslighet" Basic.Settings.General.RecordWhenStreaming="Spela automatiskt in vid strömning" Basic.Settings.General.KeepRecordingWhenStreamStops="Fortsätt spela in när strömmen stoppas" +Basic.Settings.General.SysTrayEnabled="Aktivera ikon i meddelandefältet" +Basic.Settings.General.SysTrayWhenStarted="Minimera till meddelandefältet vid start" Basic.Settings.Stream="Ström" Basic.Settings.Stream.StreamType="Strömtyp" @@ -458,6 +471,8 @@ Basic.Settings.Advanced.Video.ColorRange.Full="Full" Basic.Settings.Advanced.StreamDelay="Strömfördröjning" Basic.Settings.Advanced.StreamDelay.Duration="Varaktighet (sekunder)" Basic.Settings.Advanced.StreamDelay.MemoryUsage="Uppskattad minnesanvändning: %1 MB" +Basic.Settings.Advanced.Network="Nätverk" +Basic.Settings.Advanced.Network.BindToIP="Bind till IP" Basic.AdvAudio="Avancerade ljudinställningar" Basic.AdvAudio.Name="Namn" @@ -475,6 +490,11 @@ Basic.Hotkeys.StartRecording="Starta inspelning" Basic.Hotkeys.StopRecording="Stoppa inspelning" Basic.Hotkeys.SelectScene="Byt till scen" +Basic.SystemTray.Show="Visa" +Basic.SystemTray.Hide="Dölj" + +Basic.SystemTray.Message.Reconnecting="Frånkopplad. Återansluter..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" @@ -496,6 +516,19 @@ Hotkeys.Windows="Windows" Hotkeys.Super="Super" Hotkeys.Menu="Meny" Hotkeys.Space="Mellanslag" +Hotkeys.NumpadNum="%1 (Numpad)" +Hotkeys.NumpadMultiply="* (Numpad)" +Hotkeys.NumpadDivide="/ (Numpad)" +Hotkeys.NumpadAdd="+ (Numpad)" +Hotkeys.NumpadSubtract="- (Numpad)" +Hotkeys.NumpadDecimal=", (Numpad)" +Hotkeys.AppleKeypadNum="%1 (Keypad)" +Hotkeys.AppleKeypadMultiply="* (Keypad)" +Hotkeys.AppleKeypadDivide="/ (Keypad)" +Hotkeys.AppleKeypadAdd="+ (Keypad)" +Hotkeys.AppleKeypadSubtract="- (Keypad)" +Hotkeys.AppleKeypadDecimal=". (Keypad)" +Hotkeys.AppleKeypadEqual="= (Keypad)" Hotkeys.MouseButton="Musknapp %1" Mute="Stäng av ljud" diff --git a/UI/data/locale/ta-IN.ini b/UI/data/locale/ta-IN.ini index 5514eec2667ec9..c79b30bb3db1a9 100644 --- a/UI/data/locale/ta-IN.ini +++ b/UI/data/locale/ta-IN.ini @@ -85,6 +85,9 @@ New="புதிய" + + + diff --git a/UI/data/locale/th-TH.ini b/UI/data/locale/th-TH.ini index 8e0c37737ff9eb..0509a7ce420709 100644 --- a/UI/data/locale/th-TH.ini +++ b/UI/data/locale/th-TH.ini @@ -89,6 +89,7 @@ Basic.MainMenu.File.Exit="อ&อก" + Basic.MainMenu.Help.CheckForUpdates="ตรวจสอบการอัพเดต" Basic.Settings.ConfirmTitle="ยืนยันการเปลี่ยนแปลง" @@ -118,3 +119,5 @@ Basic.Settings.Audio="เสียง" + + diff --git a/UI/data/locale/tr-TR.ini b/UI/data/locale/tr-TR.ini index f02cd31d245915..e7c88e422c13b0 100644 --- a/UI/data/locale/tr-TR.ini +++ b/UI/data/locale/tr-TR.ini @@ -257,6 +257,7 @@ Basic.MainMenu.Edit.AdvAudio="&Gelişmiş Ses Özellikleri" Basic.MainMenu.SceneCollection="&Sahne Koleksiyonu" Basic.MainMenu.Profile="&Profil" + Basic.MainMenu.Help="&Yardım" Basic.MainMenu.Help.Website="&Siteyi Ziyaret Et" Basic.MainMenu.Help.Logs="&Günlük Dosyaları" @@ -406,6 +407,8 @@ Basic.Hotkeys.StartRecording="Kaydı Başlat" Basic.Hotkeys.StopRecording="Kaydı Durdur" Basic.Hotkeys.SelectScene="Sahneye geçiş yap" + + Hotkeys.Insert="Ekle" Hotkeys.Delete="Sil" Hotkeys.Home="Ana Sayfa" diff --git a/UI/data/locale/uk-UA.ini b/UI/data/locale/uk-UA.ini index 94486d9ca697e0..de7583584ac960 100644 --- a/UI/data/locale/uk-UA.ini +++ b/UI/data/locale/uk-UA.ini @@ -48,9 +48,10 @@ Left="Зліва" Right="Зправа" Top="Зверху" Bottom="Знизу" +Reset="Скинути" QuickTransitions.SwapScenes="Поміняти місцями сцени Перегляд/Вивід після Відео-переходу" -QuickTransitions.SwapScenesTT="Міняє місцями сцени Перегляд та Вивід після закінчення Відео-переходу (якщо сцена Вивід ще існує).\nЗміні внесені до обох сцен залишаються." +QuickTransitions.SwapScenesTT="Міняє місцями сцени Перегляд та Вивід після закінчення Відео-переходу (якщо сцена Вивід ще існує).\nЗміни внесені до обох сцен залишаються." QuickTransitions.DuplicateScene="Використовувати копію сцени" QuickTransitions.DuplicateSceneTT="Під час редагування поточної сцени дозволяє зберегти Вивід без змін.\nДля можливості редагувати властивості Джерел, увімкніть також 'Використовувати копію Джерел'.\nЗміна цієї опції призведе до оновлення поточної сцени, яка йде на Вивід (якщо вона ще існує)." QuickTransitions.EditProperties="Використовувати копію Джерел" @@ -308,6 +309,8 @@ Basic.MainMenu.View.StatusBar="Панель с&тану" Basic.MainMenu.SceneCollection="&Набір Сцен" Basic.MainMenu.Profile="&Профіль" +Basic.MainMenu.Tools="Додаткові &засоби" + Basic.MainMenu.Help="&Довідка" Basic.MainMenu.Help.Website="Відвідати &сайт" Basic.MainMenu.Help.Logs="&Файли журналів" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="Мова" Basic.Settings.General.WarnBeforeStartingStream="Показувати підтвердження для початку трансляції" Basic.Settings.General.WarnBeforeStoppingStream="Показувати підтвердження для закінчення трансляції" Basic.Settings.General.HideProjectorCursor="Приховати курсор у режимі Проектор" +Basic.Settings.General.ProjectorAlwaysOnTop="Режим Проектор відображати поверх всіх вікон" Basic.Settings.General.Snapping="Прив'язка та вирівнювання" Basic.Settings.General.ScreenSnapping="Примагнітити Джерела до краю екрана" Basic.Settings.General.CenterSnapping="Примагнітити Джерела до центру по вертикалі та горизонталі" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="Примагнітити Джерело Basic.Settings.General.SnapDistance="Чутливість примагничування" Basic.Settings.General.RecordWhenStreaming="Автоматично почати запис з початком трансляції" Basic.Settings.General.KeepRecordingWhenStreamStops="Не припиняти запис, якщо трансляцію закінчено" +Basic.Settings.General.SysTrayEnabled="Відображати іконку системному треї, та згортати в трей" +Basic.Settings.General.SysTrayWhenStarted="Згорнути програму до трею при запуску" Basic.Settings.Stream="Трансляція" Basic.Settings.Stream.StreamType="Тип Трансляції" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="Почати запис" Basic.Hotkeys.StopRecording="Зупинити запис" Basic.Hotkeys.SelectScene="Перейти до сцени" +Basic.SystemTray.Show="Показати" +Basic.SystemTray.Hide="Приховати" + +Basic.SystemTray.Message.Reconnecting="З'єднання розірвано. Повторне підключення..." + Hotkeys.Insert="Insert" Hotkeys.Delete="Delete" Hotkeys.Home="Home" diff --git a/UI/data/locale/vi-VN.ini b/UI/data/locale/vi-VN.ini index 59b22de39d04b2..22db6b23e588bf 100644 --- a/UI/data/locale/vi-VN.ini +++ b/UI/data/locale/vi-VN.ini @@ -23,7 +23,7 @@ Settings="Tùy chỉnh" Display="Hiển thị" Name="Tên" Exit="Thoát" -Mixer="Máy trộn" +Mixer="Bộ trộn" Browse="trình duyệt" Mono="Âm thanh đơn" Stereo="Âm thanh nổi" @@ -260,6 +260,7 @@ Basic.MainMenu.Edit.AdvAudio="Thuộc tính âm thanh nâng cao" Basic.MainMenu.SceneCollection="& Cảnh bộ sưu tập" Basic.MainMenu.Profile="& Hồ sơ" + Basic.MainMenu.Help="&Trợ giúp" Basic.MainMenu.Help.Website="Ghé thăm Website" Basic.MainMenu.Help.Logs="& Tập tin đăng nhập" @@ -412,6 +413,8 @@ Basic.Hotkeys.StartRecording="Bắt đầu ghi âm" Basic.Hotkeys.StopRecording="Dừng ghi âm" Basic.Hotkeys.SelectScene="Chuyển cảnh" + + Hotkeys.Insert="Chèn" Hotkeys.Delete="Xoá" Hotkeys.Home="Home" diff --git a/UI/data/locale/zh-CN.ini b/UI/data/locale/zh-CN.ini index e0ec6b48a62a66..25906032a81421 100644 --- a/UI/data/locale/zh-CN.ini +++ b/UI/data/locale/zh-CN.ini @@ -12,8 +12,8 @@ Disable="禁用" Yes="是" No="否" Add="添加" -Remove="移除" -Rename="重命名" +Remove="移除(&E)" +Rename="重命名(&R)" Interact="交互" Filters="滤镜" Properties="属性" @@ -36,8 +36,8 @@ Revert="还原" Show="显示" Hide="隐藏" Untitled="未命名" -New="新建" -Duplicate="复制" +New="新建(&N)" +Duplicate="复制(&D)" Enable="启用" DisableOSXVSync="禁用 OSX V-Sync" ResetOSXVSyncOnExit="退出时重置 OSX V-Sync" @@ -48,6 +48,7 @@ Left="左" Right="右" Top="上" Bottom="下" +Reset="重置" QuickTransitions.SwapScenes="在过渡动画后交换预览/输出场景" QuickTransitions.SwapScenesTT="在过渡后,交换预览和输出场景(如果输出的原始场景仍然存在). \n 这个不会撤消任何可能对输出的原始场景的更改." @@ -173,10 +174,10 @@ Basic.Main.AddSceneCollection.Text="请输入场景集合的名称" Basic.Main.RenameSceneCollection.Title="重命名场景集合" -AddProfile.Title="添加档案" -AddProfile.Text="请输入档案文件的名称" +AddProfile.Title="添加配置文件" +AddProfile.Text="请输入配置文件的名称" -RenameProfile.Title="重命名档案" +RenameProfile.Title="重命名配置文件" Basic.Main.PreviewDisabled="预览当前已禁用" @@ -264,57 +265,60 @@ Basic.Main.StopStreaming="停止串流" Basic.Main.StoppingStreaming="停止推流..." Basic.Main.ForceStopStreaming="停止流 (放弃延迟)" -Basic.MainMenu.File="& 文件" -Basic.MainMenu.File.Export="& 导出" -Basic.MainMenu.File.Import="& 导入" -Basic.MainMenu.File.ShowRecordings="显示 & 录像" -Basic.MainMenu.File.Remux="转&封装 录像" -Basic.MainMenu.File.Settings="& 设置" -Basic.MainMenu.File.ShowSettingsFolder="显示设置文件夹" -Basic.MainMenu.File.ShowProfileFolder="显示档案文件夹" -Basic.MainMenu.AlwaysOnTop="总是在最前面(&A)" -Basic.MainMenu.File.Exit="退出(&X)" - -Basic.MainMenu.Edit="& 编辑" -Basic.MainMenu.Edit.Undo="& 撤消" -Basic.MainMenu.Edit.Redo="& 重做" -Basic.MainMenu.Edit.UndoAction="& 撤消 $1" -Basic.MainMenu.Edit.RedoAction="& 重做 $1" -Basic.MainMenu.Edit.Transform="& 变换" -Basic.MainMenu.Edit.Transform.EditTransform="& 编辑变换" -Basic.MainMenu.Edit.Transform.ResetTransform="& 重置变换" -Basic.MainMenu.Edit.Transform.Rotate90CW="旋转 90 度 CW" -Basic.MainMenu.Edit.Transform.Rotate90CCW="旋转 90 度 CCW" -Basic.MainMenu.Edit.Transform.Rotate180="旋转 180 度" -Basic.MainMenu.Edit.Transform.FlipHorizontal="水平翻转" -Basic.MainMenu.Edit.Transform.FlipVertical="垂直翻转" -Basic.MainMenu.Edit.Transform.FitToScreen="& 适配屏幕" -Basic.MainMenu.Edit.Transform.StretchToScreen="& 拉伸到屏幕" -Basic.MainMenu.Edit.Transform.CenterToScreen="& 屏幕居中" -Basic.MainMenu.Edit.Order="& 顺序" -Basic.MainMenu.Edit.Order.MoveUp="上移" -Basic.MainMenu.Edit.Order.MoveDown="下移" -Basic.MainMenu.Edit.Order.MoveToTop="移至顶部" -Basic.MainMenu.Edit.Order.MoveToBottom="移至底部" -Basic.MainMenu.Edit.AdvAudio="&高级音频属性" - -Basic.MainMenu.View="查看(&V)" -Basic.MainMenu.View.Toolbars="工具栏(&T)" -Basic.MainMenu.View.Toolbars.Listboxes="列表框(&L)" -Basic.MainMenu.View.SceneTransitions="场景过渡(&C)" -Basic.MainMenu.View.StatusBar="状态栏(&S)" - -Basic.MainMenu.SceneCollection="&场景集合" -Basic.MainMenu.Profile="&档案" - -Basic.MainMenu.Help="& 帮助" -Basic.MainMenu.Help.Website="访问 &网站" -Basic.MainMenu.Help.Logs="& 日志文件" -Basic.MainMenu.Help.Logs.ShowLogs="& 显示日志文件" -Basic.MainMenu.Help.Logs.UploadCurrentLog="& 上传当前日志文件" -Basic.MainMenu.Help.Logs.UploadLastLog="& 上传最后一个日志文件" -Basic.MainMenu.Help.Logs.ViewCurrentLog="& 查看当前日志" -Basic.MainMenu.Help.CheckForUpdates="检查升级" +Basic.MainMenu.File="文件(&F)" +Basic.MainMenu.File.Export="导出(&E)" +Basic.MainMenu.File.Import="导入(&I)" +Basic.MainMenu.File.ShowRecordings="显示录像(&R)" +Basic.MainMenu.File.Remux="转封装录像 (&M)" +Basic.MainMenu.File.Settings="设置(&S)" +Basic.MainMenu.File.ShowSettingsFolder="打开设置所在路径(&F)" +Basic.MainMenu.File.ShowProfileFolder="打开配置文件所在路径(&P)" +Basic.MainMenu.AlwaysOnTop="窗口置顶 (&A)" +Basic.MainMenu.File.Exit="退出 (&X)" + +Basic.MainMenu.Edit="编辑 (&E)" +Basic.MainMenu.Edit.Undo="撤销 (&U)" +Basic.MainMenu.Edit.Redo="重做 (&R)" +Basic.MainMenu.Edit.UndoAction="撤消 $1 (&U)" +Basic.MainMenu.Edit.RedoAction="重做 $1 (&R)" +Basic.MainMenu.Edit.LockPreview="锁定预览 (&L)" +Basic.MainMenu.Edit.Transform="变换 (&T)" +Basic.MainMenu.Edit.Transform.EditTransform="编辑变换 (&E)" +Basic.MainMenu.Edit.Transform.ResetTransform="重置变换 (&R)" +Basic.MainMenu.Edit.Transform.Rotate90CW="顺时针旋转 90 度(&9)" +Basic.MainMenu.Edit.Transform.Rotate90CCW="逆时针旋转 90 度(&D)" +Basic.MainMenu.Edit.Transform.Rotate180="旋转 180 度(&1)" +Basic.MainMenu.Edit.Transform.FlipHorizontal="水平翻转 (&H)" +Basic.MainMenu.Edit.Transform.FlipVertical="垂直翻转 (&V)" +Basic.MainMenu.Edit.Transform.FitToScreen="比例适配屏幕 (&F)" +Basic.MainMenu.Edit.Transform.StretchToScreen="拉伸到全屏 (&S)" +Basic.MainMenu.Edit.Transform.CenterToScreen="屏幕居中 (&C)" +Basic.MainMenu.Edit.Order="排序 (&O)" +Basic.MainMenu.Edit.Order.MoveUp="上移 (&U)" +Basic.MainMenu.Edit.Order.MoveDown="下移 (&D)" +Basic.MainMenu.Edit.Order.MoveToTop="移至顶部 (&T)" +Basic.MainMenu.Edit.Order.MoveToBottom="移至底部 (&B)" +Basic.MainMenu.Edit.AdvAudio="高级音频属性(&A)" + +Basic.MainMenu.View="查看 (&V)" +Basic.MainMenu.View.Toolbars="工具栏 (&T)" +Basic.MainMenu.View.Toolbars.Listboxes="列表框 (&L)" +Basic.MainMenu.View.SceneTransitions="场景过渡 (&C)" +Basic.MainMenu.View.StatusBar="状态栏 (&S)" + +Basic.MainMenu.SceneCollection="场景集合 (&S)" +Basic.MainMenu.Profile="配置文件 (&P)" + +Basic.MainMenu.Tools="工具 (&T)" + +Basic.MainMenu.Help="帮助 (&H)" +Basic.MainMenu.Help.Website="访问OBS主页 (&W)" +Basic.MainMenu.Help.Logs="日志文件 (&L)" +Basic.MainMenu.Help.Logs.ShowLogs="显示日志文件 (&S)" +Basic.MainMenu.Help.Logs.UploadCurrentLog="上传当前日志文件 (&C)" +Basic.MainMenu.Help.Logs.UploadLastLog="上传最后一个日志文件 (&L)" +Basic.MainMenu.Help.Logs.ViewCurrentLog="查看当前日志 (&V)" +Basic.MainMenu.Help.CheckForUpdates="检查升级(&C)" Basic.Settings.ProgramRestart="要使这些设置生效,必须重新启动该程序。" Basic.Settings.ConfirmTitle="确认更改" @@ -326,6 +330,7 @@ Basic.Settings.General.Language="语言" Basic.Settings.General.WarnBeforeStartingStream="启动流时显示确认对话框" Basic.Settings.General.WarnBeforeStoppingStream="停止流时显示确认对话框" Basic.Settings.General.HideProjectorCursor="隐藏投影仪上的光标" +Basic.Settings.General.ProjectorAlwaysOnTop="使投影器总是置顶" Basic.Settings.General.Snapping="源对齐方式" Basic.Settings.General.ScreenSnapping="对齐源到屏幕边缘" Basic.Settings.General.CenterSnapping="水平和垂直居中对齐源" @@ -333,6 +338,8 @@ Basic.Settings.General.SourceSnapping="对齐源跟其他的源" Basic.Settings.General.SnapDistance="对齐的敏感性" Basic.Settings.General.RecordWhenStreaming="当推流时自动录像" Basic.Settings.General.KeepRecordingWhenStreamStops="当推流停止时保持录像" +Basic.Settings.General.SysTrayEnabled="启用系统托盘图标" +Basic.Settings.General.SysTrayWhenStarted="开始时最小化到系统托盘" Basic.Settings.Stream="串流" Basic.Settings.Stream.StreamType="串流类型" @@ -467,10 +474,12 @@ Basic.Settings.Advanced.StreamDelay="流延迟" Basic.Settings.Advanced.StreamDelay.Duration="持续时间 (秒)" Basic.Settings.Advanced.StreamDelay.Preserve="重新连接时保持截止点 (增加延迟)" Basic.Settings.Advanced.StreamDelay.MemoryUsage="估计的内存使用率: %1 MB" +Basic.Settings.Advanced.Network="网络" +Basic.Settings.Advanced.Network.BindToIP="绑定 IP" Basic.AdvAudio="高级音频属性" Basic.AdvAudio.Name="名称" -Basic.AdvAudio.Volume="卷 (%)" +Basic.AdvAudio.Volume="音量 (%)" Basic.AdvAudio.Mono="下降混合为单声道" Basic.AdvAudio.Panning="平移" Basic.AdvAudio.SyncOffset="同步偏移 (毫秒)" @@ -485,6 +494,11 @@ Basic.Hotkeys.StartRecording="开始录像" Basic.Hotkeys.StopRecording="停止录像" Basic.Hotkeys.SelectScene="切换到场景" +Basic.SystemTray.Show="显示" +Basic.SystemTray.Hide="隐藏" + +Basic.SystemTray.Message.Reconnecting="已断开连接。 重新连接..." + Hotkeys.Insert="插入" Hotkeys.Delete="删除" Hotkeys.Home="首页" diff --git a/UI/data/locale/zh-TW.ini b/UI/data/locale/zh-TW.ini index 27e34a022858c0..12e1fd6d6a06e9 100644 --- a/UI/data/locale/zh-TW.ini +++ b/UI/data/locale/zh-TW.ini @@ -1,6 +1,6 @@ -Language="正體中文(臺灣)" -Region="Taiwan, R.O.C." +Language="繁體中文" +Region="臺灣/香港/澳門" OK="確定" Apply="套用" @@ -48,6 +48,7 @@ Left="左" Right="右" Top="上" Bottom="下" +Reset="重置" QuickTransitions.SwapScenes="轉場後交換預覽/輸出場景" QuickTransitions.SwapScenesTT="(如果輸出的原始場景仍然存在) 轉場後交換預覽和輸出場景。\n這並不會復原任何對輸出原始場景所作的改動。" @@ -308,13 +309,15 @@ Basic.MainMenu.View.StatusBar="狀態列(&S)" Basic.MainMenu.SceneCollection="場景群組 (&S)" Basic.MainMenu.Profile="設定檔 (&P)" +Basic.MainMenu.Tools="工具(&T)" + Basic.MainMenu.Help="幫助 (&H)" Basic.MainMenu.Help.Website="前往 OBS 網站 (&W)" Basic.MainMenu.Help.Logs="Log 檔案 (&L)" Basic.MainMenu.Help.Logs.ShowLogs="顯示 Log (&S)" Basic.MainMenu.Help.Logs.UploadCurrentLog="上傳目前 Log 檔 (&C)" Basic.MainMenu.Help.Logs.UploadLastLog="上傳上次的 Log 檔 (&L)" -Basic.MainMenu.Help.Logs.ViewCurrentLog="顯示當前紀錄檔" +Basic.MainMenu.Help.Logs.ViewCurrentLog="顯示當前紀錄檔 (&V)" Basic.MainMenu.Help.CheckForUpdates="檢查更新" Basic.Settings.ProgramRestart="為了套用新設定,請關閉後重啟。" @@ -327,6 +330,7 @@ Basic.Settings.General.Language="語言" Basic.Settings.General.WarnBeforeStartingStream="啟動串流時顯示確認對話框" Basic.Settings.General.WarnBeforeStoppingStream="停止串流時顯示確認對話框" Basic.Settings.General.HideProjectorCursor="當游標在投影上時隱藏游標" +Basic.Settings.General.ProjectorAlwaysOnTop="讓投影總是在最上層" Basic.Settings.General.Snapping="貼齊對準來源" Basic.Settings.General.ScreenSnapping="來源與螢幕邊緣貼齊" Basic.Settings.General.CenterSnapping="來源與水平中央以及垂直中央貼齊" @@ -334,6 +338,8 @@ Basic.Settings.General.SourceSnapping="來源與其他來源貼齊" Basic.Settings.General.SnapDistance="貼齊敏感度" Basic.Settings.General.RecordWhenStreaming="串流時自動錄製" Basic.Settings.General.KeepRecordingWhenStreamStops="串流停止時繼續錄製" +Basic.Settings.General.SysTrayEnabled="啟用系統列圖示" +Basic.Settings.General.SysTrayWhenStarted="開始時最小化至系統列" Basic.Settings.Stream="串流" Basic.Settings.Stream.StreamType="串流類型" @@ -488,6 +494,11 @@ Basic.Hotkeys.StartRecording="開始錄影" Basic.Hotkeys.StopRecording="停止錄影" Basic.Hotkeys.SelectScene="切換到場景" +Basic.SystemTray.Show="顯示" +Basic.SystemTray.Hide="隱藏" + +Basic.SystemTray.Message.Reconnecting="連線已中斷。 正在重新連接..." + Hotkeys.Insert="插入鍵" Hotkeys.Delete="刪除鍵" Hotkeys.Home="Home 鍵" diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini b/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini new file mode 100644 index 00000000000000..2b178557e8d105 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/ca-ES.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Canviador d'escena automàtica" +SceneSwitcher.OnNoMatch="Quan no coincideixi amb cap finestra:" +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à:" +Active="Actiu" +Inactive="Inactiu" +Start="Inicia" +Stop="Atura" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini b/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini new file mode 100644 index 00000000000000..c8a45651ef55bb --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/cs-CZ.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatický přepínač scén" +SceneSwitcher.OnNoMatch="Výchozí chování u nesouhlasící:" +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:" +Active="Aktivní" +Inactive="Neaktivní" +Start="Spustit" +Stop="Zastavit" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini b/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini new file mode 100644 index 00000000000000..f72389cd61c883 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/da-DK.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatisk sceneskifter" +SceneSwitcher.OnNoMatch="Når intet vindue svarer til:" +SceneSwitcher.OnNoMatch.DontSwitch="Skift ikke" +SceneSwitcher.OnNoMatch.SwitchTo="Skift til:" +SceneSwitcher.CheckInterval="Kontroller aktivt vinduestitel hvert:" +SceneSwitcher.ActiveOrNotActive="Sceneskifter er:" +Active="Aktiv" +Inactive="Inaktiv" +Start="Start" +Stop="Stop" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini b/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini new file mode 100644 index 00000000000000..4caf47eabb0410 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/de-DE.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatischer Szenenwechsler" +SceneSwitcher.OnNoMatch="Wenn kein Fenster passt:" +SceneSwitcher.OnNoMatch.DontSwitch="Nicht wechseln" +SceneSwitcher.OnNoMatch.SwitchTo="Wechseln zu:" +SceneSwitcher.CheckInterval="Titel des aktiven Fensters überprüfen alle:" +SceneSwitcher.ActiveOrNotActive="Szenenwechsler ist:" +Active="Aktiv" +Inactive="Inaktiv" +Start="Start" +Stop="Stop" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini b/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini new file mode 100644 index 00000000000000..a08a8f9c7bb143 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/es-ES.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Cambiador de escena automática" +SceneSwitcher.OnNoMatch="Cuando no coincida con ninguna ventana:" +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á:" +Active="Activo" +Inactive="Inactivo" +Start="Iniciar" +Stop="Detener" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini b/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini new file mode 100644 index 00000000000000..99076b329835e0 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/eu-ES.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Eszena-aldatzaile automatikoa" +SceneSwitcher.OnNoMatch="Leihoa bat etortzen ez denean:" +SceneSwitcher.OnNoMatch.DontSwitch="Ez aldatu" +SceneSwitcher.OnNoMatch.SwitchTo="Aldatu hona:" +SceneSwitcher.CheckInterval="Leiho aktiboaren titulua egiaztatzeko maiztasuna:" +SceneSwitcher.ActiveOrNotActive="Eszena aldatzailea dago:" +Active="Aktiboa" +Inactive="Inaktiboa" +Start="Hasi" +Stop="Gelditu" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini b/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini new file mode 100644 index 00000000000000..0f208689db6c93 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/fi-FI.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automaattinen skenen vaihtaja" +SceneSwitcher.OnNoMatch="Kun ikkunaa ei löydy:" +SceneSwitcher.OnNoMatch.DontSwitch="Älä vaihda" +SceneSwitcher.OnNoMatch.SwitchTo="Vaihda:" +SceneSwitcher.CheckInterval="Tarkista aktiivisen ikkunan otsikko:" +SceneSwitcher.ActiveOrNotActive="Skenen vaihtaja:" +Active="Aktiivinen" +Inactive="Ei käytössä" +Start="Käynnistä" +Stop="Pysäytä" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini b/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini new file mode 100644 index 00000000000000..65d0b4c3c65765 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/fr-FR.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Sélecteur automatique de scène" +SceneSwitcher.OnNoMatch="Si aucune fenêtre ne correspond :" +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 :" +Active="Actif" +Inactive="Inactif" +Start="Démarrer" +Stop="Arrêter" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini b/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini new file mode 100644 index 00000000000000..fb81a5492b8769 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/hr-HR.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatski menjač scena" +SceneSwitcher.OnNoMatch="Kada se nijedan naslov prozora ne poklapa:" +SceneSwitcher.OnNoMatch.DontSwitch="Ne menjaj" +SceneSwitcher.OnNoMatch.SwitchTo="Promeni na:" +SceneSwitcher.CheckInterval="Proveravaj naziv aktivnog prozora svakih:" +SceneSwitcher.ActiveOrNotActive="Menjač scena je:" +Active="Aktivan" +Inactive="Neaktivan" +Start="Pokreni" +Stop="Zaustavi" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini b/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini new file mode 100644 index 00000000000000..bfd99aa64220e6 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/hu-HU.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatikus jelenetváltó" +SceneSwitcher.OnNoMatch="Ha nem található megfelelő ablak:" +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ó:" +Active="Aktív" +Inactive="Inaktiv" +Start="Start" +Stop="Stop" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini b/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini new file mode 100644 index 00000000000000..714c79cdcfa2a8 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/ja-JP.ini @@ -0,0 +1,11 @@ +SceneSwitcher="自動シーンスイッチャー" +SceneSwitcher.OnNoMatch="どのウィンドウにも一致しない場合:" +SceneSwitcher.OnNoMatch.DontSwitch="切り替えない" +SceneSwitcher.OnNoMatch.SwitchTo="切り替える:" +SceneSwitcher.CheckInterval="アクティブウィンドウタイトルを確認する間隔:" +SceneSwitcher.ActiveOrNotActive="シーンスイッチャーは:" +Active="アクティブ" +Inactive="非アクティブ" +Start="開始" +Stop="停止" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini b/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini new file mode 100644 index 00000000000000..cbfee50a85b04f --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/ko-KR.ini @@ -0,0 +1,11 @@ +SceneSwitcher="자동 장면 전환기" +SceneSwitcher.OnNoMatch="일치하는 윈도우가 없는 경우:" +SceneSwitcher.OnNoMatch.DontSwitch="전환하지 않음" +SceneSwitcher.OnNoMatch.SwitchTo="여기로 전환:" +SceneSwitcher.CheckInterval="활성화된 윈도우 제목을 확인:" +SceneSwitcher.ActiveOrNotActive="장면 전환기:" +Active="활성화" +Inactive="비활성화" +Start="시작" +Stop="중단" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini b/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini new file mode 100644 index 00000000000000..b785c86159bf87 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/nl-NL.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatische Scènewisselaar" +SceneSwitcher.OnNoMatch="Als geen venster overeenkomt:" +SceneSwitcher.OnNoMatch.DontSwitch="Wissel niet" +SceneSwitcher.OnNoMatch.SwitchTo="Wissel naar:" +SceneSwitcher.CheckInterval="Controleer actieve venstertitel elke:" +SceneSwitcher.ActiveOrNotActive="Scènewisselaar is:" +Active="Actief" +Inactive="Inactief" +Start="Start" +Stop="Stop" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini b/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini new file mode 100644 index 00000000000000..dd2d18dc193a93 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/pl-PL.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatyczne Przełączanie Scen" +SceneSwitcher.OnNoMatch="Gdy nie pasuje żadne okno:" +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:" +Active="Aktywne" +Inactive="Nieaktywne" +Start="Start" +Stop="Stop" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini b/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini new file mode 100644 index 00000000000000..5a6a4eb4cc3515 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/pt-BR.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Alternador automático de cena" +SceneSwitcher.OnNoMatch="Quando nenhuma janela corresponde:" +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á:" +Active="Ligado" +Inactive="Desligado" +Start="Iniciar" +Stop="Parar" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini b/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini new file mode 100644 index 00000000000000..831e0d00d73ae1 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/ru-RU.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Автоматический переключатель сцен" +SceneSwitcher.OnNoMatch="Когда ни одно окно не подходит:" +SceneSwitcher.OnNoMatch.DontSwitch="Не переключать" +SceneSwitcher.OnNoMatch.SwitchTo="Переключать на:" +SceneSwitcher.CheckInterval="Проверять имя активного окна каждые:" +SceneSwitcher.ActiveOrNotActive="Переключатель сцен:" +Active="Активен" +Inactive="Неактивен" +Start="Запустить" +Stop="Остановить" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini b/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini new file mode 100644 index 00000000000000..fb81a5492b8769 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/sr-CS.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatski menjač scena" +SceneSwitcher.OnNoMatch="Kada se nijedan naslov prozora ne poklapa:" +SceneSwitcher.OnNoMatch.DontSwitch="Ne menjaj" +SceneSwitcher.OnNoMatch.SwitchTo="Promeni na:" +SceneSwitcher.CheckInterval="Proveravaj naziv aktivnog prozora svakih:" +SceneSwitcher.ActiveOrNotActive="Menjač scena je:" +Active="Aktivan" +Inactive="Neaktivan" +Start="Pokreni" +Stop="Zaustavi" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini b/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini new file mode 100644 index 00000000000000..983378fdd56a57 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/sr-SP.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Аутоматски мењач сцена" +SceneSwitcher.OnNoMatch="Када се ниједан наслов прозора не поклапа:" +SceneSwitcher.OnNoMatch.DontSwitch="Не мењај" +SceneSwitcher.OnNoMatch.SwitchTo="Промени на:" +SceneSwitcher.CheckInterval="Проверавај назив активног прозора сваких:" +SceneSwitcher.ActiveOrNotActive="Мењач сцена је:" +Active="Активан" +Inactive="Неактиван" +Start="Покрени" +Stop="Заустави" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini b/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini new file mode 100644 index 00000000000000..a2f6330c46b66b --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/sv-SE.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Automatisk scenbytare" +SceneSwitcher.OnNoMatch="När inget fönster matchar:" +SceneSwitcher.OnNoMatch.DontSwitch="Byt inte" +SceneSwitcher.OnNoMatch.SwitchTo="Byt till:" +SceneSwitcher.CheckInterval="Kontrollera aktiv fönstertitel varje:" +SceneSwitcher.ActiveOrNotActive="Scenbytaren är:" +Active="Aktiv" +Inactive="Inaktiv" +Start="Starta" +Stop="Stoppa" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini b/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini new file mode 100644 index 00000000000000..12a1a0b0398386 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/uk-UA.ini @@ -0,0 +1,11 @@ +SceneSwitcher="Автоматичний перехід між Сценами" +SceneSwitcher.OnNoMatch="Якщо нема відповідних вікон:" +SceneSwitcher.OnNoMatch.DontSwitch="Нічого не робити" +SceneSwitcher.OnNoMatch.SwitchTo="Перейти до:" +SceneSwitcher.CheckInterval="Перевіряти заголовок активного вікна кожні:" +SceneSwitcher.ActiveOrNotActive="Автоматичний перехід між Сценами:" +Active="Активний" +Inactive="Неактивний" +Start="Запустити" +Stop="Зупинити" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini b/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini new file mode 100644 index 00000000000000..1ae756f64a7786 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/zh-CN.ini @@ -0,0 +1,11 @@ +SceneSwitcher="自动场景切换器" +SceneSwitcher.OnNoMatch="当没有窗口匹配的时候:" +SceneSwitcher.OnNoMatch.DontSwitch="不切换" +SceneSwitcher.OnNoMatch.SwitchTo="切换到:" +SceneSwitcher.CheckInterval="检查活动窗口的标题,每:" +SceneSwitcher.ActiveOrNotActive="场景切换器是:" +Active="已激活" +Inactive="未激活" +Start="开始" +Stop="停止" + diff --git a/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini b/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini new file mode 100644 index 00000000000000..ac7668aee2c4d5 --- /dev/null +++ b/UI/frontend-plugins/frontend-tools/data/locale/zh-TW.ini @@ -0,0 +1,11 @@ +SceneSwitcher="自動場景切換器" +SceneSwitcher.OnNoMatch="當沒有視窗相符時︰" +SceneSwitcher.OnNoMatch.DontSwitch="不切換" +SceneSwitcher.OnNoMatch.SwitchTo="切換到︰" +SceneSwitcher.CheckInterval="檢查使用中視窗標題的頻率︰" +SceneSwitcher.ActiveOrNotActive="場景切換器︰" +Active="啟動中" +Inactive="未啟動" +Start="開始" +Stop="停止" + diff --git a/plugins/coreaudio-encoder/data/locale/sv-SE.ini b/plugins/coreaudio-encoder/data/locale/sv-SE.ini index 8bf5f03cda0296..225da2fc61cd66 100644 --- a/plugins/coreaudio-encoder/data/locale/sv-SE.ini +++ b/plugins/coreaudio-encoder/data/locale/sv-SE.ini @@ -2,4 +2,5 @@ CoreAudioAAC="CoreAudio AAC-kodare" Bitrate="Bithastighet" AllowHEAAC="Tillåt HE-AAC" OutputSamplerate="Samplingsfrekvens för utmatning" +UseInputSampleRate="Använd inmatningens (OBS) samplingshastighet (kan lista bithastigheter som inte stöds)" diff --git a/plugins/decklink/data/locale/ko-KR.ini b/plugins/decklink/data/locale/ko-KR.ini index 43fd331989d60e..7247470301bbd7 100644 --- a/plugins/decklink/data/locale/ko-KR.ini +++ b/plugins/decklink/data/locale/ko-KR.ini @@ -1,6 +1,6 @@ BlackmagicDevice="Blackmagic 장치" Device="장치" -Mode="모드" +Mode="방식" Buffering="버퍼링 사용" PixelFormat="픽셀 형식" diff --git a/plugins/image-source/data/locale/da-DK.ini b/plugins/image-source/data/locale/da-DK.ini index 75de5c0b9f1d8e..6c988c354e07cc 100644 --- a/plugins/image-source/data/locale/da-DK.ini +++ b/plugins/image-source/data/locale/da-DK.ini @@ -2,4 +2,14 @@ ImageInput="Billede" File="Billedfil" UnloadWhenNotShowing="Fjern billede fra hukommelsen når det ikke vises" +SlideShow="Billede diasshow" +SlideShow.TransitionSpeed="Overgangshastighed (millisekunder)" +SlideShow.SlideTime="Tid mellem dias (millisekunder)" +SlideShow.Files="Billedfiler" +SlideShow.Randomize="Tilfældig afspilning" +SlideShow.Transition="Overgang" +SlideShow.Transition.Cut="Klip" +SlideShow.Transition.Fade="Overgang" +SlideShow.Transition.Swipe="Stryg" +SlideShow.Transition.Slide="Glide" diff --git a/plugins/linux-capture/data/locale/ja-JP.ini b/plugins/linux-capture/data/locale/ja-JP.ini index 332892cf02f46e..c004b9cf722947 100644 --- a/plugins/linux-capture/data/locale/ja-JP.ini +++ b/plugins/linux-capture/data/locale/ja-JP.ini @@ -2,7 +2,7 @@ X11SharedMemoryScreenInput="画面キャプチャ (XSHM)" Screen="画面" CaptureCursor="カーソルをキャプチャ" AdvancedSettings="高度な設定" -XServer="X サーバ" +XServer="X サーバー" XCCapture="ウィンドウキャプチャ (Xcomposite)" Window="ウィンドウ" CropTop="上部クロップ (ピクセル)" @@ -10,7 +10,7 @@ CropLeft="左側クロップ (ピクセル)" CropRight="右側クロップ (ピクセル)" CropBottom="下部クロップ (ピクセル)" SwapRedBlue="赤と青を入れ替え" -LockX="キャプチャ時にXサーバをロック" -IncludeXBorder="Xウインドウの境界を含める" +LockX="キャプチャ時にXサーバーをロック" +IncludeXBorder="Xウィンドウの境界を含める" ExcludeAlpha="アルファなしテクスチャ形式を使用 (Mesaの回避策)" diff --git a/plugins/mac-capture/data/locale/ja-JP.ini b/plugins/mac-capture/data/locale/ja-JP.ini index a3299f6a22586f..78ff12347840d7 100644 --- a/plugins/mac-capture/data/locale/ja-JP.ini +++ b/plugins/mac-capture/data/locale/ja-JP.ini @@ -6,14 +6,14 @@ DisplayCapture="画面キャプチャ" DisplayCapture.Display="ディスプレイ" DisplayCapture.ShowCursor="カーソルを表示" WindowCapture="ウィンドウキャプチャ" -WindowCapture.ShowShadow="ウインドウの影を表示" +WindowCapture.ShowShadow="ウィンドウの影を表示" WindowUtils.Window="ウィンドウ" WindowUtils.ShowEmptyNames="空の名前でウィンドウを表示" CropMode="クロップ" CropMode.None="未設定" CropMode.Manual="手動" CropMode.ToWindow="ウィンドウにあわせる" -CropMode.ToWindowAndManual="ウインドウに合わせて手動" +CropMode.ToWindowAndManual="ウィンドウに合わせて手動" Crop.origin.x="左をクロップ" Crop.origin.y="上をクロップ" Crop.size.width="右をクロップ" diff --git a/plugins/mac-capture/data/locale/ko-KR.ini b/plugins/mac-capture/data/locale/ko-KR.ini index c49007eb445a72..9c7727d1ef8840 100644 --- a/plugins/mac-capture/data/locale/ko-KR.ini +++ b/plugins/mac-capture/data/locale/ko-KR.ini @@ -16,6 +16,6 @@ CropMode.ToWindow="윈도우에 맞추기" CropMode.ToWindowAndManual="윈도우에 맞추고 수동" Crop.origin.x="왼쪽 자르기" Crop.origin.y="위쪽 자르기" -Crop.size.width="우측 자르기" +Crop.size.width="오른쪽 자르기" Crop.size.height="아래 자르기" diff --git a/plugins/obs-filters/data/locale/fr-FR.ini b/plugins/obs-filters/data/locale/fr-FR.ini index 8aebc6600fdee2..bdfedaf706168f 100644 --- a/plugins/obs-filters/data/locale/fr-FR.ini +++ b/plugins/obs-filters/data/locale/fr-FR.ini @@ -8,6 +8,7 @@ ColorKeyFilter="Couleur d'incrustation" SharpnessFilter="Accentuer" ScaleFilter="Mise à l’échelle / Ratio d'affichage" NoiseGate="Noise Gate" +NoiseSuppress="Suppression du bruit" Gain="Gain" DelayMs="Retard (en millisecondes)" Type="Type " @@ -59,4 +60,5 @@ ScaleFiltering.Point="Point" ScaleFiltering.Bilinear="Bilinéaire" ScaleFiltering.Bicubic="Bicubique" ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Seuil de suppression (en dB)" diff --git a/plugins/obs-filters/data/locale/ko-KR.ini b/plugins/obs-filters/data/locale/ko-KR.ini index 9e93144c3995ad..1470851aa91851 100644 --- a/plugins/obs-filters/data/locale/ko-KR.ini +++ b/plugins/obs-filters/data/locale/ko-KR.ini @@ -30,8 +30,8 @@ KeyColor="키 색상" Similarity="유사성 (1-1000)" Smoothness="매끄러움 (1-1000)" ColorSpillReduction="키 색상 유출 감소 (1-1000)" -Crop.Left="좌측" -Crop.Right="우측" +Crop.Left="왼쪽" +Crop.Right="오른쪽" Crop.Top="상단" Crop.Bottom="하단" Crop.Width="너비" diff --git a/plugins/obs-filters/data/locale/pt-BR.ini b/plugins/obs-filters/data/locale/pt-BR.ini index 137175a08838dc..e3901ed4fca19a 100644 --- a/plugins/obs-filters/data/locale/pt-BR.ini +++ b/plugins/obs-filters/data/locale/pt-BR.ini @@ -52,4 +52,12 @@ NoiseGate.HoldTime="Tempo de Bloqueio (milissegundos)" NoiseGate.ReleaseTime="Tempo de libertação (milissegundos)" Gain.GainDB="Ganho (dB)" StretchImage="Esticar a Imagem (descartar aspecto da imagem)" +Resolution="Resolução" +None="Nenhum" +ScaleFiltering="Filtragem de escala" +ScaleFiltering.Point="Ponto" +ScaleFiltering.Bilinear="Bilinear" +ScaleFiltering.Bicubic="Bicúbico" +ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Nível de redução (dB)" diff --git a/plugins/obs-filters/data/locale/ru-RU.ini b/plugins/obs-filters/data/locale/ru-RU.ini index 3ba18ce810d291..e9447a4601afc3 100644 --- a/plugins/obs-filters/data/locale/ru-RU.ini +++ b/plugins/obs-filters/data/locale/ru-RU.ini @@ -59,6 +59,6 @@ ScaleFiltering="Масштаб Фильтрации" ScaleFiltering.Point="Точечная" ScaleFiltering.Bilinear="Билинейная" ScaleFiltering.Bicubic="Бикубическая" -ScaleFiltering.Lanczos="Ланцошная" +ScaleFiltering.Lanczos="Метод Ланцоша" NoiseSuppress.SuppressLevel="Уровень подавления (дБ)" diff --git a/plugins/obs-filters/data/locale/sv-SE.ini b/plugins/obs-filters/data/locale/sv-SE.ini index b8b94be0960a72..00ad818d79892b 100644 --- a/plugins/obs-filters/data/locale/sv-SE.ini +++ b/plugins/obs-filters/data/locale/sv-SE.ini @@ -1,12 +1,14 @@ ColorFilter="Färgkorrigering" MaskFilter="Bild Mask/Blandning" AsyncDelayFilter="Videofördröjning (Async)" +CropFilter="Beskär/Fyll ut" ScrollFilter="Scrollning" ChromaKeyFilter="Kromafilter" ColorKeyFilter="Färgfilter" SharpnessFilter="Skärpa" ScaleFilter="Skalning/Bildförhållande" NoiseGate="Brusblockering" +NoiseSuppress="Brusreducering" Gain="Förstärkning" DelayMs="Fördröjning (millisekunder)" Type="Typ" @@ -58,4 +60,5 @@ ScaleFiltering.Point="Punkt" ScaleFiltering.Bilinear="Bilinjär" ScaleFiltering.Bicubic="Bikubisk" ScaleFiltering.Lanczos="Lanczos" +NoiseSuppress.SuppressLevel="Brusreduceringsnivå (dB)" diff --git a/plugins/obs-filters/data/locale/zh-CN.ini b/plugins/obs-filters/data/locale/zh-CN.ini index 48033b49165868..b818e37f5a2414 100644 --- a/plugins/obs-filters/data/locale/zh-CN.ini +++ b/plugins/obs-filters/data/locale/zh-CN.ini @@ -8,6 +8,7 @@ ColorKeyFilter="色值" SharpnessFilter="锐化" ScaleFilter="缩放比例" NoiseGate="噪音阈值" +NoiseSuppress="噪声抑制" Gain="增益" DelayMs="延迟(毫秒)" Type="类型" @@ -59,4 +60,5 @@ ScaleFiltering.Point="点" ScaleFiltering.Bilinear="双线性算法" ScaleFiltering.Bicubic="双立方算法" ScaleFiltering.Lanczos="兰索斯算法" +NoiseSuppress.SuppressLevel="抑制程度 (dB)" diff --git a/plugins/obs-outputs/data/locale/fr-FR.ini b/plugins/obs-outputs/data/locale/fr-FR.ini index 6bd6f2b5e90f30..ad4b9b83f2dcca 100644 --- a/plugins/obs-outputs/data/locale/fr-FR.ini +++ b/plugins/obs-outputs/data/locale/fr-FR.ini @@ -2,4 +2,5 @@ RTMPStream="Flux RTMP" RTMPStream.DropThreshold="Seuil de baisse (en millisecondes)" FLVOutput="Fichier FLV sortant" FLVOutput.FilePath="Chemin du fichier" +Default="Interface par défaut" diff --git a/plugins/obs-outputs/data/locale/zh-CN.ini b/plugins/obs-outputs/data/locale/zh-CN.ini index c006fc60579f55..2f0a123b4f4261 100644 --- a/plugins/obs-outputs/data/locale/zh-CN.ini +++ b/plugins/obs-outputs/data/locale/zh-CN.ini @@ -2,4 +2,5 @@ RTMPStream="RTMP 流" RTMPStream.DropThreshold="Drop阈值(毫秒)" FLVOutput="FLV 文件输出" FLVOutput.FilePath="文件路径" +Default="默认" diff --git a/plugins/obs-qsv11/data/locale/pt-BR.ini b/plugins/obs-qsv11/data/locale/pt-BR.ini index 80c0aae026b7c4..1d3dd63e6bed88 100644 --- a/plugins/obs-qsv11/data/locale/pt-BR.ini +++ b/plugins/obs-qsv11/data/locale/pt-BR.ini @@ -4,7 +4,7 @@ MaxBitrate="Taxa de Bits Máxima" RateControl="Controle da Taxa de Bits" KeyframeIntervalSec="Intervalo de Keyframe (segundos, 0=auto)" Profile="Perfil" -AsyncDepth="Profundidade do Async" +AsyncDepth="Profundidade assíncrona" Accuracy="Precisão" Convergence="Convergência" ICQQuality="Qualidade ICQ" diff --git a/plugins/obs-text/data/locale/ca-ES.ini b/plugins/obs-text/data/locale/ca-ES.ini new file mode 100644 index 00000000000000..35586b5574c565 --- /dev/null +++ b/plugins/obs-text/data/locale/ca-ES.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Text (GDI+)" +Font="Font" +Text="Text" +ReadFromFile="Llegeix del fitxer" +TextFile="Fitxer de text (UTF-8)" +Filter.TextFiles="Arxius de text" +Filter.AllFiles="Tots els fitxers" +Color="Color" +Opacity="Opacitat" +BkColor="Color de fons" +BkOpacity="Opacitat del fons" +Alignment="Alineació" +Alignment.Left="Esquerra" +Alignment.Center="Centre" +Alignment.Right="Dreta" +Vertical="Vertical" +VerticalAlignment="Alineació vertical" +VerticalAlignment.Top="Superior" +VerticalAlignment.Bottom="Inferior" +Outline="Contorn" +Outline.Size="Mida del contorn" +Outline.Color="Color del contorn" +Outline.Opacity="Opacitat del contorn" +ChatlogMode="Mode xat" +ChatlogMode.Lines="Límit de la línia de xat" +UseCustomExtents="Utilitza extensions de text personalitzat" +UseCustomExtents.Wrap="Ajusta" +Width="Amplada" +Height="Alçada" + diff --git a/plugins/obs-text/data/locale/cs-CZ.ini b/plugins/obs-text/data/locale/cs-CZ.ini new file mode 100644 index 00000000000000..dd4bf7db3e26ed --- /dev/null +++ b/plugins/obs-text/data/locale/cs-CZ.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Text (GDI+)" +Font="Písmo" +Text="Text" +ReadFromFile="Ze souboru" +TextFile="Textový soubor (UTF-8)" +Filter.TextFiles="Textové soubory" +Filter.AllFiles="Všechny soubory" +Color="Barva" +Opacity="Krytí" +BkColor="Barva pozadí" +BkOpacity="Průhlednost pozadí" +Alignment="Zarovnání" +Alignment.Left="Vlevo" +Alignment.Center="Uprostřed" +Alignment.Right="Vpravo" +Vertical="Vertikálně" +VerticalAlignment="Vertikální zarovnání" +VerticalAlignment.Top="Nahoře" +VerticalAlignment.Bottom="Dole" +Outline="Obtáhnout" +Outline.Size="Síla obtažení" +Outline.Color="Barva obtažení" +Outline.Opacity="Krytí obtažení" +ChatlogMode="Režim chatu" +ChatlogMode.Lines="Limit řádků režimu chatu" +UseCustomExtents="Použít vlastní rozsah textu" +UseCustomExtents.Wrap="Zalomit" +Width="Šířka" +Height="Výška" + diff --git a/plugins/obs-text/data/locale/de-DE.ini b/plugins/obs-text/data/locale/de-DE.ini new file mode 100644 index 00000000000000..36f8d77efeeed7 --- /dev/null +++ b/plugins/obs-text/data/locale/de-DE.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Text (GDI+)" +Font="Schriftart" +Text="Text" +ReadFromFile="Aus Datei lesen" +TextFile="Textdatei (UTF-8)" +Filter.TextFiles="Textdateien" +Filter.AllFiles="Alle Dateien" +Color="Farbe" +Opacity="Deckkraft" +BkColor="Hintergrundfarbe" +BkOpacity="Hintergrunddeckkraft" +Alignment="Ausrichtung" +Alignment.Left="Linksbündig" +Alignment.Center="Zentriert" +Alignment.Right="Rechtsbündig" +Vertical="Vertikal" +VerticalAlignment="Vertikal ausrichten" +VerticalAlignment.Top="Oben" +VerticalAlignment.Bottom="Unten" +Outline="Kontur" +Outline.Size="Konturgröße" +Outline.Color="Konturfarbe" +Outline.Opacity="Deckkraft der Kontur" +ChatlogMode="Chatlog-Modus" +ChatlogMode.Lines="Chatlog Zeilenlimit" +UseCustomExtents="Nutze benutzerdefinierten Textbereich" +UseCustomExtents.Wrap="Umbruch" +Width="Breite" +Height="Höhe" + diff --git a/plugins/obs-text/data/locale/es-ES.ini b/plugins/obs-text/data/locale/es-ES.ini new file mode 100644 index 00000000000000..2332182a2e3acd --- /dev/null +++ b/plugins/obs-text/data/locale/es-ES.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Texto (GDI+)" +Font="Fuente" +Text="Texto" +ReadFromFile="Leer desde archivo" +TextFile="Archivo de texto (UTF-8)" +Filter.TextFiles="Archivos de texto" +Filter.AllFiles="Todos los archivos" +Color="Color" +Opacity="Opacidad" +BkColor="Color del fondo" +BkOpacity="Opacidad del fondo" +Alignment="Alineamiento" +Alignment.Left="Izquierda" +Alignment.Center="Centrado" +Alignment.Right="Derecha" +Vertical="Vertical" +VerticalAlignment="Alineación vertical" +VerticalAlignment.Top="Arriba" +VerticalAlignment.Bottom="Abajo" +Outline="Contorno" +Outline.Size="Tamaño del borde" +Outline.Color="Color del borde" +Outline.Opacity="Opacidad del borde" +ChatlogMode="Modo chat" +ChatlogMode.Lines="Límite de la línea de chat" +UseCustomExtents="Usar extensiones de texto personalizado" +UseCustomExtents.Wrap="Ajustar" +Width="Ancho" +Height="Alto" + diff --git a/plugins/obs-text/data/locale/eu-ES.ini b/plugins/obs-text/data/locale/eu-ES.ini new file mode 100644 index 00000000000000..3a75a66fa65978 --- /dev/null +++ b/plugins/obs-text/data/locale/eu-ES.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Testua (GDI+)" +Font="Letra-tipoa" +Text="Testua" +ReadFromFile="Irakurri fitxategitik" +TextFile="Testu fitxategia (UTF-8)" +Filter.TextFiles="Testu fitxategiak" +Filter.AllFiles="Fitxategi guztiak" +Color="Kolorea" +Opacity="Opakutasuna" +BkColor="Atzeko planoaren kolorea" +BkOpacity="Atzeko planoaren opakutasuna" +Alignment="Lerrokatzea" +Alignment.Left="Ezkerrean" +Alignment.Center="Erdian" +Alignment.Right="Eskuinean" +Vertical="Bertikala" +VerticalAlignment="Lerrokatze bertikala" +VerticalAlignment.Top="Goian" +VerticalAlignment.Bottom="Behean" +Outline="Ertza" +Outline.Size="Ertzaren zabalera" +Outline.Color="Ertzaren kolorea" +Outline.Opacity="Ertzaren opakutasuna" +ChatlogMode="Berriketa modua" +ChatlogMode.Lines="Berriketa lerroaren limitea" +UseCustomExtents="Erabili testu hedapen pertsonalak" +UseCustomExtents.Wrap="Egokitu" +Width="Zabalera" +Height="Altuera" + diff --git a/plugins/obs-text/data/locale/fi-FI.ini b/plugins/obs-text/data/locale/fi-FI.ini new file mode 100644 index 00000000000000..912a4e14d4d26a --- /dev/null +++ b/plugins/obs-text/data/locale/fi-FI.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Teksti (GDI+)" +Font="Fontti" +Text="Teksti" +ReadFromFile="Lue tiedostosta" +TextFile="Tekstitiedosto (UTF-8)" +Filter.TextFiles="Tekstitiedostot" +Filter.AllFiles="Kaikki tiedostot" +Color="Väri" +Opacity="Läpinäkyvyys" +BkColor="Taustaväri" +BkOpacity="Taustan läpinäkyvyys" +Alignment="Sijoittelu" +Alignment.Left="Vasen" +Alignment.Center="Keskitetty" +Alignment.Right="Oikea" +Vertical="Pystysuunnassa" +VerticalAlignment="Pystysijoittelu" +VerticalAlignment.Top="Ylös" +VerticalAlignment.Bottom="Alas" +Outline="Reunaviiva" +Outline.Size="Reunaviivan koko" +Outline.Color="Reunaviivan väri" +Outline.Opacity="Reunaviivan läpinäkyvyys" +ChatlogMode="Chatlog-tila" +ChatlogMode.Lines="Chatlog riviraja" +UseCustomExtents="Käytä valinnaisia fonttilaajennuksia" +UseCustomExtents.Wrap="Sido" +Width="Leveys" +Height="Korkeus" + diff --git a/plugins/obs-text/data/locale/fr-FR.ini b/plugins/obs-text/data/locale/fr-FR.ini new file mode 100644 index 00000000000000..9db7ed5d5cb4ac --- /dev/null +++ b/plugins/obs-text/data/locale/fr-FR.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Texte (GDI+)" +Font="Police" +Text="Texte" +ReadFromFile="Lire depuis un fichier" +TextFile="Fichier texte (UTF-8)" +Filter.TextFiles="Fichiers texte" +Filter.AllFiles="Tous les fichiers" +Color="Couleur" +Opacity="Opacité" +BkColor="Couleur de l'arrière-plan" +BkOpacity="Transparence de l'arrière-plan" +Alignment="Alignement" +Alignment.Left="À gauche" +Alignment.Center="Centré" +Alignment.Right="À droite" +Vertical="Vertical" +VerticalAlignment="Alignement vertical" +VerticalAlignment.Top="En haut" +VerticalAlignment.Bottom="En bas" +Outline="Contour" +Outline.Size="Épaisseur du contour" +Outline.Color="Couleur du contour" +Outline.Opacity="Opacité du contour" +ChatlogMode="Mode discussion" +ChatlogMode.Lines="Lignes de discussion" +UseCustomExtents="Utiliser une taille personnalisée" +UseCustomExtents.Wrap="Retour à la ligne automatique" +Width="Largeur" +Height="Hauteur" + diff --git a/plugins/obs-text/data/locale/hu-HU.ini b/plugins/obs-text/data/locale/hu-HU.ini new file mode 100644 index 00000000000000..e46508a303ccc7 --- /dev/null +++ b/plugins/obs-text/data/locale/hu-HU.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Szöveg (GDI+)" +Font="Betűtípus" +Text="Szöveg" +ReadFromFile="Fájlból olvasás" +TextFile="Szövegfájl (UTF-8)" +Filter.TextFiles="Szöveges fájlok" +Filter.AllFiles="Minden fájl" +Color="Szín" +Opacity="Áttetszőség" +BkColor="Háttérszín" +BkOpacity="Háttér áttetszőség" +Alignment="Pozíció" +Alignment.Left="Balra" +Alignment.Center="Középre" +Alignment.Right="Jobbra" +Vertical="Függőleges" +VerticalAlignment="Függőleges igazítás" +VerticalAlignment.Top="Fent" +VerticalAlignment.Bottom="Lent" +Outline="Körvonal" +Outline.Size="Körvonal mérete" +Outline.Color="Körvonal színe" +Outline.Opacity="Körvonal áttetszőség" +ChatlogMode="Csevegőnapló mód" +ChatlogMode.Lines="Csevegőnapló sorlimit" +UseCustomExtents="Egyedi szövegdoboz használata" +UseCustomExtents.Wrap="Sortörés" +Width="Szélesség" +Height="Magasság" + diff --git a/plugins/obs-text/data/locale/ja-JP.ini b/plugins/obs-text/data/locale/ja-JP.ini new file mode 100644 index 00000000000000..7797aa078f720f --- /dev/null +++ b/plugins/obs-text/data/locale/ja-JP.ini @@ -0,0 +1,30 @@ +TextGDIPlus="テキスト (GDI+)" +Font="フォント" +Text="テキスト" +ReadFromFile="ファイルからの読み取り" +TextFile="テキストファイル (UTF-8)" +Filter.TextFiles="テキストファイル" +Filter.AllFiles="すべてのファイル" +Color="色" +Opacity="不透明度" +BkColor="背景色" +BkOpacity="背景の不透明度" +Alignment="位置揃え" +Alignment.Left="左" +Alignment.Center="中央" +Alignment.Right="右" +Vertical="垂直方向" +VerticalAlignment="垂直位置揃え" +VerticalAlignment.Top="上" +VerticalAlignment.Bottom="下" +Outline="輪郭" +Outline.Size="輪郭のサイズ" +Outline.Color="輪郭の色" +Outline.Opacity="輪郭の不透明度" +ChatlogMode="チャットログモード" +ChatlogMode.Lines="チャットログ行の制限" +UseCustomExtents="テキスト領域の範囲を指定する" +UseCustomExtents.Wrap="折り返す" +Width="幅" +Height="高さ" + diff --git a/plugins/obs-text/data/locale/ko-KR.ini b/plugins/obs-text/data/locale/ko-KR.ini new file mode 100644 index 00000000000000..2547c759c2be78 --- /dev/null +++ b/plugins/obs-text/data/locale/ko-KR.ini @@ -0,0 +1,30 @@ +TextGDIPlus="텍스트 (GDI+)" +Font="글꼴" +Text="텍스트" +ReadFromFile="파일에서 불러들이기" +TextFile="텍스트 파일 (UTF-8)" +Filter.TextFiles="텍스트 파일" +Filter.AllFiles="모든 파일" +Color="색" +Opacity="투명도" +BkColor="배경 색상" +BkOpacity="배경 불투명도" +Alignment="정렬" +Alignment.Left="왼쪽" +Alignment.Center="가운데" +Alignment.Right="오른쪽" +Vertical="수직" +VerticalAlignment="수직 정렬" +VerticalAlignment.Top="위" +VerticalAlignment.Bottom="아래" +Outline="외곽선" +Outline.Size="외곽선 크기" +Outline.Color="외곽선 색" +Outline.Opacity="외곽선 투명도" +ChatlogMode="채팅기록 모드" +ChatlogMode.Lines="채팅기록 줄 제한" +UseCustomExtents="사용자 정의 텍스트 설정" +UseCustomExtents.Wrap="자동 줄 바꿈" +Width="너비" +Height="높이" + diff --git a/plugins/obs-text/data/locale/nl-NL.ini b/plugins/obs-text/data/locale/nl-NL.ini new file mode 100644 index 00000000000000..dc939047c50a1f --- /dev/null +++ b/plugins/obs-text/data/locale/nl-NL.ini @@ -0,0 +1,28 @@ +TextGDIPlus="Tekst (GDI+)" +Font="Lettertype" +Text="Tekst" +ReadFromFile="Lees uit bestand" +TextFile="Tekstbestand (UTF-8)" +Filter.TextFiles="Tekstbestanden" +Filter.AllFiles="Alle bestanden" +Color="Kleur" +Opacity="Dekking" +Alignment="Uitlijning" +Alignment.Left="Links" +Alignment.Center="Midden" +Alignment.Right="Rechts" +Vertical="Verticaal" +VerticalAlignment="Verticale uitlijning" +VerticalAlignment.Top="Boven" +VerticalAlignment.Bottom="Onder" +Outline="Contour" +Outline.Size="Contourgrootte" +Outline.Color="Contourkleur" +Outline.Opacity="Contourdekking" +ChatlogMode="Chatlogmodus" +ChatlogMode.Lines="Chatlog regel-limiet" +UseCustomExtents="Aangepaste tekst-extents gebruiken" +UseCustomExtents.Wrap="Terugloop" +Width="Breedte" +Height="Hoogte" + diff --git a/plugins/obs-text/data/locale/pl-PL.ini b/plugins/obs-text/data/locale/pl-PL.ini new file mode 100644 index 00000000000000..6298b53682aed9 --- /dev/null +++ b/plugins/obs-text/data/locale/pl-PL.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Tekst (GDI +)" +Font="Czcionka" +Text="Tekst" +ReadFromFile="Czytaj z pliku" +TextFile="Plik tekstowy (UTF-8)" +Filter.TextFiles="Pliki tekstowe" +Filter.AllFiles="Wszystkie pliki" +Color="Kolor" +Opacity="Przezroczystość" +BkColor="Kolor tła" +BkOpacity="Przezroczystość tła" +Alignment="Wyrównanie" +Alignment.Left="Do lewej" +Alignment.Center="Wyśrodkuj" +Alignment.Right="Do prawej" +Vertical="Pionowo" +VerticalAlignment="Wyrównanie w pionie" +VerticalAlignment.Top="Do góry" +VerticalAlignment.Bottom="Do dołu" +Outline="Kontur" +Outline.Size="Rozmiar konturu" +Outline.Color="Kolor konturu" +Outline.Opacity="Przezroczystość konturu" +ChatlogMode="Tryb podglądu czatu" +ChatlogMode.Lines="Limit linii czatu" +UseCustomExtents="Użyj niestandardowego zakresu tekstu" +UseCustomExtents.Wrap="Zawiń" +Width="Szerokość" +Height="Wysokość" + diff --git a/plugins/obs-text/data/locale/ru-RU.ini b/plugins/obs-text/data/locale/ru-RU.ini new file mode 100644 index 00000000000000..04d81ce0f3471e --- /dev/null +++ b/plugins/obs-text/data/locale/ru-RU.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Текст (GDI+)" +Font="Шрифт" +Text="Текст" +ReadFromFile="Чтение из файла" +TextFile="Текстовый файл (UTF-8)" +Filter.TextFiles="Текстовые файлы" +Filter.AllFiles="Все файлы" +Color="Цвет" +Opacity="Непрозрачность" +BkColor="Цвет фона" +BkOpacity="Непрозрачность фона" +Alignment="Выравнивание" +Alignment.Left="По левому краю" +Alignment.Center="По центру" +Alignment.Right="По правому краю" +Vertical="Вертикальный" +VerticalAlignment="Вертикальное выравнивание" +VerticalAlignment.Top="Сверху" +VerticalAlignment.Bottom="Снизу" +Outline="Обводка" +Outline.Size="Размер обводки" +Outline.Color="Цвет обводки" +Outline.Opacity="Непрозрачность контура" +ChatlogMode="Режим чат-лога" +ChatlogMode.Lines="Лимит кол-ва строк чата" +UseCustomExtents="Свои размеры текстового поля" +UseCustomExtents.Wrap="Перенос строк" +Width="Ширина" +Height="Высота" + diff --git a/plugins/obs-text/data/locale/sv-SE.ini b/plugins/obs-text/data/locale/sv-SE.ini new file mode 100644 index 00000000000000..70fa8b9d39df3e --- /dev/null +++ b/plugins/obs-text/data/locale/sv-SE.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Text (GDI+)" +Font="Typsnitt" +Text="Text" +ReadFromFile="Läs från fil" +TextFile="Textfil (UTF-8)" +Filter.TextFiles="Textfiler" +Filter.AllFiles="Alla filer" +Color="Färg" +Opacity="Opacitet" +BkColor="Bakgrundsfärg" +BkOpacity="Bakgrundsopacitet" +Alignment="Justering" +Alignment.Left="Vänster" +Alignment.Center="Centrerad" +Alignment.Right="Höger" +Vertical="Vertikal" +VerticalAlignment="Vertikal justering" +VerticalAlignment.Top="Överkant" +VerticalAlignment.Bottom="Nederkant" +Outline="Kontur" +Outline.Size="Konturstorlek" +Outline.Color="Konturfärg" +Outline.Opacity="Konturopacitet" +ChatlogMode="Chattloggsläge" +ChatlogMode.Lines="Radgräns för chattlogg" +UseCustomExtents="Använd anpassade textmått" +UseCustomExtents.Wrap="Radbryt" +Width="Bredd" +Height="Höjd" + diff --git a/plugins/obs-text/data/locale/uk-UA.ini b/plugins/obs-text/data/locale/uk-UA.ini new file mode 100644 index 00000000000000..1ea325ccb37bcd --- /dev/null +++ b/plugins/obs-text/data/locale/uk-UA.ini @@ -0,0 +1,30 @@ +TextGDIPlus="Текст (GDI+)" +Font="Шрифт" +Text="Текст" +ReadFromFile="Текст з файлу" +TextFile="Текстовий файл (UTF-8)" +Filter.TextFiles="Текстові файли" +Filter.AllFiles="Всі файли" +Color="Колір" +Opacity="Непрозорість" +BkColor="Колір фону" +BkOpacity="Непрозорість фону" +Alignment="Вирівнювання" +Alignment.Left="Ліворуч" +Alignment.Center="Центр" +Alignment.Right="Праворуч" +Vertical="Вертикально" +VerticalAlignment="Вертикальне вирівнювання" +VerticalAlignment.Top="Вверху" +VerticalAlignment.Bottom="Внизу" +Outline="Контур" +Outline.Size="Розмір контуру" +Outline.Color="Колір контуру" +Outline.Opacity="Непрозорість контуру" +ChatlogMode="Режим чат-журналу" +ChatlogMode.Lines="Кількість рядків чат-журналу" +UseCustomExtents="Особливі властивості текстового блоку" +UseCustomExtents.Wrap="Перенос слів" +Width="Ширина" +Height="Висота" + diff --git a/plugins/obs-text/data/locale/zh-CN.ini b/plugins/obs-text/data/locale/zh-CN.ini new file mode 100644 index 00000000000000..d86290e69e060b --- /dev/null +++ b/plugins/obs-text/data/locale/zh-CN.ini @@ -0,0 +1,30 @@ +TextGDIPlus="文本 (GDI+)" +Font="字体" +Text="文本" +ReadFromFile="从文件读取" +TextFile="文本文件 (UTF-8)" +Filter.TextFiles="文本文件" +Filter.AllFiles="所有文件" +Color="色彩" +Opacity="不透明度" +BkColor="背景颜色" +BkOpacity="背景不透明度" +Alignment="对齐" +Alignment.Left="靠左对齐" +Alignment.Center="居中" +Alignment.Right="靠右对齐" +Vertical="垂直移动" +VerticalAlignment="垂直对齐" +VerticalAlignment.Top="顶部" +VerticalAlignment.Bottom="底部" +Outline="轮廓" +Outline.Size="轮廓大小" +Outline.Color="轮廓颜色" +Outline.Opacity="轮廓不透明度" +ChatlogMode="聊天模式" +ChatlogMode.Lines="聊天行限制" +UseCustomExtents="使用自定义文本区" +UseCustomExtents.Wrap="自动换行" +Width="宽度" +Height="高度" + diff --git a/plugins/obs-text/data/locale/zh-TW.ini b/plugins/obs-text/data/locale/zh-TW.ini new file mode 100644 index 00000000000000..b883e91efef2bd --- /dev/null +++ b/plugins/obs-text/data/locale/zh-TW.ini @@ -0,0 +1,28 @@ +TextGDIPlus="文字 (GDI+)" +Font="字型" +Text="文字" +ReadFromFile="從檔案讀取" +TextFile="文字檔 (UTF-8)" +Filter.TextFiles="文字檔案" +Filter.AllFiles="所有檔案" +Color="顏色" +Opacity="不透明度" +Alignment="對齊方式" +Alignment.Left="靠左對齊" +Alignment.Center="置中" +Alignment.Right="靠右對齊" +Vertical="垂直" +VerticalAlignment="垂直對齊" +VerticalAlignment.Top="向上對齊" +VerticalAlignment.Bottom="向下對齊" +Outline="外框" +Outline.Size="外框大小" +Outline.Color="外框颜色" +Outline.Opacity="外框不透明度" +ChatlogMode="聊天紀錄模式" +ChatlogMode.Lines="聊天紀錄行數" +UseCustomExtents="使用自動文字區塊大小" +UseCustomExtents.Wrap="自動換行" +Width="寬度" +Height="高度" + diff --git a/plugins/obs-transitions/data/locale/fr-FR.ini b/plugins/obs-transitions/data/locale/fr-FR.ini index 6a2a101d68b762..8ad0f6c06f97ee 100644 --- a/plugins/obs-transitions/data/locale/fr-FR.ini +++ b/plugins/obs-transitions/data/locale/fr-FR.ini @@ -11,14 +11,42 @@ Direction.Down="Bas" SwipeIn="Recouvrement" Color="Couleur" SwitchPoint="Point de couleur maximal (pourcentage)" +LumaWipeTransition="Luma" +LumaWipe.Image="Image" LumaWipe.Invert="Inverser" +LumaWipe.Softness="Douceur" +LumaWipe.Type.BarndoorBottomLeft="Portes coulissantes en diagonale (en bas à gauche)" +LumaWipe.Type.BarndoorHorizontal="Portes coulissantes horizontales" +LumaWipe.Type.BarndoorTopLeft="Portes coulissantes en diagonale (en haut à gauche)" +LumaWipe.Type.BarndoorVertical="Portes coulissantes verticales" +LumaWipe.Type.BlindsHorizontal="Persiennes horizontales" +LumaWipe.Type.BoxBottomLeft="Boîte (vers le bas à gauche)" +LumaWipe.Type.BoxBottomRight="Boîte (vers le bas à droite)" +LumaWipe.Type.BoxTopLeft="Boîte (vers le haut à gauche)" +LumaWipe.Type.BoxTopRight="Boîte (vers le haut à droîte)" +LumaWipe.Type.Burst="Explosion" +LumaWipe.Type.CheckerboardSmall="Échiquier (petit)" LumaWipe.Type.Circles="Cercles" LumaWipe.Type.Clock="Horloge" LumaWipe.Type.Cloud="Nuage" LumaWipe.Type.Curtain="Rideau" LumaWipe.Type.Fan="Ventilateur" LumaWipe.Type.Fractal="Fractale" +LumaWipe.Type.Iris="Diaphragme" +LumaWipe.Type.LinearHorizontal="Balayage horizontal" +LumaWipe.Type.LinearTopLeft="Balayage en diagonale (de gauche à droite)" +LumaWipe.Type.LinearTopRight="Balayage en diagonale (de droite à gauche)" +LumaWipe.Type.LinearVertical="Balayage vertical" +LumaWipe.Type.ParallelZigzagHorizontal="Zigzags en parallèle (horizontal)" +LumaWipe.Type.ParallelZigzagVertical="Zigzags en parallèle (vertical)" +LumaWipe.Type.Sinus9="Plasma" LumaWipe.Type.Spiral="Spirale" LumaWipe.Type.Square="Carré" +LumaWipe.Type.Squares="Carrés" +LumaWipe.Type.Stripes="Rayures" +LumaWipe.Type.StripsHorizontal="Bandes horizontales" +LumaWipe.Type.StripsVertical="Bandes verticales" LumaWipe.Type.Watercolor="Aquarelle" +LumaWipe.Type.ZigzagHorizontal="Zigzags à l'horizontale" +LumaWipe.Type.ZigzagVertical="Zigzags à la verticale" diff --git a/plugins/obs-transitions/data/locale/sv-SE.ini b/plugins/obs-transitions/data/locale/sv-SE.ini index 5eaf6a9f1d3fe6..89e14e35a9e34c 100644 --- a/plugins/obs-transitions/data/locale/sv-SE.ini +++ b/plugins/obs-transitions/data/locale/sv-SE.ini @@ -10,22 +10,42 @@ Direction.Up="Upp" Direction.Down="Ned" SwipeIn="Svep in" Color="Färg" +LumaWipeTransition="Luma Wipe" LumaWipe.Image="Bild" LumaWipe.Invert="Invertera" LumaWipe.Softness="Mjukhet" +LumaWipe.Type.BarndoorBottomLeft="Ladugårdsdörr, vänster nedkant" +LumaWipe.Type.BarndoorHorizontal="Ladugårdsdörr, horisontalt" +LumaWipe.Type.BarndoorTopLeft="Ladugårdsdörr, vänster överkant" +LumaWipe.Type.BarndoorVertical="Ladugårdsdörr, vertikalt" +LumaWipe.Type.BlindsHorizontal="Persienner, horisontalt" LumaWipe.Type.BoxBottomLeft="Box vänster nederkant" LumaWipe.Type.BoxBottomRight="Box höger nederkant" LumaWipe.Type.BoxTopLeft="Box vänster överkant" LumaWipe.Type.BoxTopRight="Box höger överkant" +LumaWipe.Type.Burst="Explosion" LumaWipe.Type.CheckerboardSmall="Schackbräde litet" LumaWipe.Type.Circles="Cirklar" LumaWipe.Type.Clock="Klocka" LumaWipe.Type.Cloud="Moln" LumaWipe.Type.Curtain="Gardin" +LumaWipe.Type.Fan="Fläkt" +LumaWipe.Type.Fractal="Fraktal" LumaWipe.Type.Iris="Iris" +LumaWipe.Type.LinearHorizontal="Linjer, horisontalt" +LumaWipe.Type.LinearTopLeft="Linjer, vänster överkant" +LumaWipe.Type.LinearTopRight="Linjer, höger överkant" +LumaWipe.Type.LinearVertical="Linjer, vertikalt" +LumaWipe.Type.ParallelZigzagHorizontal="Parallell sicksack, horisontalt" +LumaWipe.Type.ParallelZigzagVertical="Parallell sicksack, vertikalt" +LumaWipe.Type.Sinus9="Sinus 9" LumaWipe.Type.Spiral="Spiral" LumaWipe.Type.Square="Kvadrat" LumaWipe.Type.Squares="Kvadrater" LumaWipe.Type.Stripes="Ränder" +LumaWipe.Type.StripsHorizontal="Streck, horisontalt" +LumaWipe.Type.StripsVertical="Streck, vertikalt" LumaWipe.Type.Watercolor="Vattenfärg" +LumaWipe.Type.ZigzagHorizontal="Sicksack, horisontalt" +LumaWipe.Type.ZigzagVertical="Sicksack, vertikalt" diff --git a/plugins/obs-transitions/data/locale/zh-CN.ini b/plugins/obs-transitions/data/locale/zh-CN.ini index 8a4c1334557c56..5edfdff2de294a 100644 --- a/plugins/obs-transitions/data/locale/zh-CN.ini +++ b/plugins/obs-transitions/data/locale/zh-CN.ini @@ -11,4 +11,42 @@ Direction.Down="下" SwipeIn="向上滑动" Color="色彩" SwitchPoint="峰值颜色点(百分比)" +LumaWipeTransition="亮度擦除" +LumaWipe.Image="图像" +LumaWipe.Invert="反转" +LumaWipe.Softness="柔和" +LumaWipe.Type.BarndoorBottomLeft="左下角门" +LumaWipe.Type.BarndoorHorizontal="水平门" +LumaWipe.Type.BarndoorTopLeft="右上角门" +LumaWipe.Type.BarndoorVertical="垂直门" +LumaWipe.Type.BlindsHorizontal="水平百叶窗" +LumaWipe.Type.BoxBottomLeft="左下角盒子" +LumaWipe.Type.BoxBottomRight="右下角盒子" +LumaWipe.Type.BoxTopLeft="左上角盒子" +LumaWipe.Type.BoxTopRight="右上角盒子" +LumaWipe.Type.Burst="爆裂" +LumaWipe.Type.CheckerboardSmall="小棋盘" +LumaWipe.Type.Circles="圆" +LumaWipe.Type.Clock="时钟" +LumaWipe.Type.Cloud="云" +LumaWipe.Type.Curtain="窗帘" +LumaWipe.Type.Fan="风扇" +LumaWipe.Type.Fractal="不规则碎片" +LumaWipe.Type.Iris="中心" +LumaWipe.Type.LinearHorizontal="线性水平" +LumaWipe.Type.LinearTopLeft="线性左上" +LumaWipe.Type.LinearTopRight="线性右上" +LumaWipe.Type.LinearVertical="线性垂直" +LumaWipe.Type.ParallelZigzagHorizontal="平行水波水平" +LumaWipe.Type.ParallelZigzagVertical="平行水波垂直" +LumaWipe.Type.Sinus9="Sinus 9" +LumaWipe.Type.Spiral="螺旋" +LumaWipe.Type.Square="正方型" +LumaWipe.Type.Squares="方块" +LumaWipe.Type.Stripes="条纹" +LumaWipe.Type.StripsHorizontal="条纹水平" +LumaWipe.Type.StripsVertical="条纹垂直" +LumaWipe.Type.Watercolor="水彩" +LumaWipe.Type.ZigzagHorizontal="Z字形水平" +LumaWipe.Type.ZigzagVertical="Z字形垂直" diff --git a/plugins/rtmp-services/data/locale/ja-JP.ini b/plugins/rtmp-services/data/locale/ja-JP.ini index 0273b6b4a455a2..082962b72932c1 100644 --- a/plugins/rtmp-services/data/locale/ja-JP.ini +++ b/plugins/rtmp-services/data/locale/ja-JP.ini @@ -1,5 +1,5 @@ StreamingServices="ストリーミングサービス" -CustomStreamingServer="カスタムストリーミングサーバ" +CustomStreamingServer="カスタムストリーミングサーバー" Service="サービス" Server="サーバー" StreamKey="ストリームキー" diff --git a/plugins/win-capture/data/locale/ca-ES.ini b/plugins/win-capture/data/locale/ca-ES.ini index 4c93bc7d98361c..f8ae6d0e891d81 100644 --- a/plugins/win-capture/data/locale/ca-ES.ini +++ b/plugins/win-capture/data/locale/ca-ES.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Capturar capes d'aplicacions externes (tals com Ste GameCapture.AntiCheatHook="Utilitzi la compatibilitat anti-trampa" GameCapture.HotkeyStart="Captura finestra en primer pla" GameCapture.HotkeyStop="Desactiva captura" +Mode="Mode" diff --git a/plugins/win-capture/data/locale/cs-CZ.ini b/plugins/win-capture/data/locale/cs-CZ.ini index 84ebdc36bf4002..495a8175e00b61 100644 --- a/plugins/win-capture/data/locale/cs-CZ.ini +++ b/plugins/win-capture/data/locale/cs-CZ.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Zaznamenávat overlaye (např. Steam Overlay)" GameCapture.AntiCheatHook="Použít ochranu proti detekci jako hack" GameCapture.HotkeyStart="Snímat okno v popředí" GameCapture.HotkeyStop="Zastavit snímání" +Mode="Režim" diff --git a/plugins/win-capture/data/locale/de-DE.ini b/plugins/win-capture/data/locale/de-DE.ini index 34ec86dd7d7c8f..b36f683c3d4297 100644 --- a/plugins/win-capture/data/locale/de-DE.ini +++ b/plugins/win-capture/data/locale/de-DE.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Aufnahme von Drittanbieter Overlays (z.B. Steam)" GameCapture.AntiCheatHook="Verwende Anti-Cheat-Kompatibilität Hook" GameCapture.HotkeyStart="Fenster im Vordergrund erfassen" GameCapture.HotkeyStop="Erfassen deaktivieren" +Mode="Modus" diff --git a/plugins/win-capture/data/locale/es-ES.ini b/plugins/win-capture/data/locale/es-ES.ini index 032d8ce24edae9..33605484448ae8 100644 --- a/plugins/win-capture/data/locale/es-ES.ini +++ b/plugins/win-capture/data/locale/es-ES.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Capturar capas de aplicaciones externas (tales como GameCapture.AntiCheatHook="Utilice la compatibilidad anti-trampa" GameCapture.HotkeyStart="Captura ventana en primer plano" GameCapture.HotkeyStop="Desactivar captura" +Mode="Modo" diff --git a/plugins/win-capture/data/locale/eu-ES.ini b/plugins/win-capture/data/locale/eu-ES.ini index 734eb94d388ed7..999b0da720de61 100644 --- a/plugins/win-capture/data/locale/eu-ES.ini +++ b/plugins/win-capture/data/locale/eu-ES.ini @@ -12,9 +12,14 @@ Monitor="Pantaila" PrimaryMonitor="Monitore nagusia" GameCapture="Bideojoko-kaptura" GameCapture.AnyFullscreen="Kapturatu pantaila osoko edozein aplikazio" +GameCapture.CaptureWindow="Kapturatu leiho zehatza" +GameCapture.UseHotkey="Kapturatu aurreko planoko leihoa laster teklen bidez" GameCapture.ForceScaling="Behartu eskalatzea" GameCapture.ScaleRes="Bereizmen eskalatua" GameCapture.LimitFramerate="Mugatu kapturaren fotograma tasa" GameCapture.CaptureOverlays="Kapturatu hirugarrengoen gainjarpenak (steam bezalakoak)" GameCapture.AntiCheatHook="Erabili iruzurren aurkako bateragarritasun kakoa" +GameCapture.HotkeyStart="Kapturatu aurreko planoko leihoa" +GameCapture.HotkeyStop="Desaktibatu kaptura" +Mode="Modua" diff --git a/plugins/win-capture/data/locale/fi-FI.ini b/plugins/win-capture/data/locale/fi-FI.ini index 28dcd88a3d5da9..198a2b860014b7 100644 --- a/plugins/win-capture/data/locale/fi-FI.ini +++ b/plugins/win-capture/data/locale/fi-FI.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Kaappaa kolmannen osapuolen 'overlay' (kuten Steam) GameCapture.AntiCheatHook="Yhteensopivuus huijaukseneston kanssa" GameCapture.HotkeyStart="Kaappaa aktiivinen ikkuna" GameCapture.HotkeyStop="Poista kaappaus käytöstä" +Mode="Tila" diff --git a/plugins/win-capture/data/locale/fr-FR.ini b/plugins/win-capture/data/locale/fr-FR.ini index 73f02a6189faa1..1ff71ce40d0d85 100644 --- a/plugins/win-capture/data/locale/fr-FR.ini +++ b/plugins/win-capture/data/locale/fr-FR.ini @@ -12,9 +12,14 @@ Monitor="Écran" PrimaryMonitor="Écran principal" GameCapture="Capture de jeu" GameCapture.AnyFullscreen="Capturer n'importe quelle application en plein écran" +GameCapture.CaptureWindow="Capturer une fenêtre spécifique" +GameCapture.UseHotkey="Capturer la fenêtre au premier plan par raccourci clavier" GameCapture.ForceScaling="Forcer la mise à l'échelle" GameCapture.ScaleRes="Résolution mise à l'échelle" GameCapture.LimitFramerate="Limiter la fréquence d'images de la capture" GameCapture.CaptureOverlays="Capturer les surcouches tierces (comme Steam)" GameCapture.AntiCheatHook="Utiliser la méthode de capture compatible anti-triche" +GameCapture.HotkeyStart="Capturer la fenêtre au premier plan" +GameCapture.HotkeyStop="Arrêter la capture" +Mode="Mode" diff --git a/plugins/win-capture/data/locale/hr-HR.ini b/plugins/win-capture/data/locale/hr-HR.ini index 48767c9e33b840..9d21b762a65238 100644 --- a/plugins/win-capture/data/locale/hr-HR.ini +++ b/plugins/win-capture/data/locale/hr-HR.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Snimaj overlay treće strane (kao što je steam)" GameCapture.AntiCheatHook="Koristi priključak za kompatibilnost sa zaštitom od varanja" GameCapture.HotkeyStart="Snimanje prozora u prvom planu" GameCapture.HotkeyStop="Isključivanje snimanja" +Mode="Režim" diff --git a/plugins/win-capture/data/locale/hu-HU.ini b/plugins/win-capture/data/locale/hu-HU.ini index 57d9896c3f7a9e..feb8cb43b3f804 100644 --- a/plugins/win-capture/data/locale/hu-HU.ini +++ b/plugins/win-capture/data/locale/hu-HU.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Külső overlay felvétele (mint például steam)" GameCapture.AntiCheatHook="Anti-csalás kompatibilitási metódus használata" GameCapture.HotkeyStart="Előtérben lévő ablak felvétele" GameCapture.HotkeyStop="Felvétel lekapcsolása" +Mode="Mód" diff --git a/plugins/win-capture/data/locale/ja-JP.ini b/plugins/win-capture/data/locale/ja-JP.ini index 2874835c606f8f..7b06c03c5694b7 100644 --- a/plugins/win-capture/data/locale/ja-JP.ini +++ b/plugins/win-capture/data/locale/ja-JP.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="(steamなどの) サードパーティ製のオー GameCapture.AntiCheatHook="アンチチート互換性フックを使用する" GameCapture.HotkeyStart="前面のウィンドウをキャプチャ" GameCapture.HotkeyStop="キャプチャを無効化" +Mode="モード" diff --git a/plugins/win-capture/data/locale/ko-KR.ini b/plugins/win-capture/data/locale/ko-KR.ini index a781827eccc507..90c55fe41c2a93 100644 --- a/plugins/win-capture/data/locale/ko-KR.ini +++ b/plugins/win-capture/data/locale/ko-KR.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="서드-파티 오버레이 (스팀과 같은) 캡 GameCapture.AntiCheatHook="치트 방지 기술로 인한 인식이 안될 때 호환성 모드 사용" GameCapture.HotkeyStart="전경에 있는 창을 캡쳐" GameCapture.HotkeyStop="캡쳐 끄기" +Mode="방식" diff --git a/plugins/win-capture/data/locale/nl-NL.ini b/plugins/win-capture/data/locale/nl-NL.ini index 9606adb21b1e36..2e6f3eab405e3e 100644 --- a/plugins/win-capture/data/locale/nl-NL.ini +++ b/plugins/win-capture/data/locale/nl-NL.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Capture overlays van derde partijen (zoals Steam)" GameCapture.AntiCheatHook="Hook met anti-cheat compatibiliteit" GameCapture.HotkeyStart="Venster op voorgrond opnemen" GameCapture.HotkeyStop="Capture deactiveren" +Mode="Modus" diff --git a/plugins/win-capture/data/locale/pl-PL.ini b/plugins/win-capture/data/locale/pl-PL.ini index b3f5283bcac1ca..454a18e735cd3b 100644 --- a/plugins/win-capture/data/locale/pl-PL.ini +++ b/plugins/win-capture/data/locale/pl-PL.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Przechwytywanie nakładek firm trzecich (np. Steam GameCapture.AntiCheatHook="Zgodność z mechanizmami anti-cheat" GameCapture.HotkeyStart="Przechwytywanie okna na pierwszym planie" GameCapture.HotkeyStop="Wyłącz przechwytywanie" +Mode="Tryb" diff --git a/plugins/win-capture/data/locale/pt-BR.ini b/plugins/win-capture/data/locale/pt-BR.ini index 1ae379f0cc26cb..2e4c0165f27a3a 100644 --- a/plugins/win-capture/data/locale/pt-BR.ini +++ b/plugins/win-capture/data/locale/pt-BR.ini @@ -12,9 +12,14 @@ Monitor="Monitor" PrimaryMonitor="Monitor principal" GameCapture="Captura de jogo" GameCapture.AnyFullscreen="Capturar qualquer aplicação em tela cheia" +GameCapture.CaptureWindow="Capturar janela específica" +GameCapture.UseHotkey="Capturar janela de primeiro plano com a tecla de atalho" GameCapture.ForceScaling="Forçar escalamento" GameCapture.ScaleRes="Resolução de Saída" GameCapture.LimitFramerate="Limitar framerate de captura" GameCapture.CaptureOverlays="Capturar overlays de terceiros (tais como as do Steam)" GameCapture.AntiCheatHook="Utilizar hook de compatibilidade de anti-cheat" +GameCapture.HotkeyStart="Capturar janela de primeiro plano" +GameCapture.HotkeyStop="Desativar a captura" +Mode="Modo" diff --git a/plugins/win-capture/data/locale/ru-RU.ini b/plugins/win-capture/data/locale/ru-RU.ini index ab2f95981d7b52..6544a7e7d1f29d 100644 --- a/plugins/win-capture/data/locale/ru-RU.ini +++ b/plugins/win-capture/data/locale/ru-RU.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Захват сторонних оверлеев ( GameCapture.AntiCheatHook="Использовать перехватчик, совместимый с защитой от читов" GameCapture.HotkeyStart="Захват окна на переднем плане" GameCapture.HotkeyStop="Остановить захват" +Mode="Режим" diff --git a/plugins/win-capture/data/locale/sr-CS.ini b/plugins/win-capture/data/locale/sr-CS.ini index 48767c9e33b840..9d21b762a65238 100644 --- a/plugins/win-capture/data/locale/sr-CS.ini +++ b/plugins/win-capture/data/locale/sr-CS.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Snimaj overlay treće strane (kao što je steam)" GameCapture.AntiCheatHook="Koristi priključak za kompatibilnost sa zaštitom od varanja" GameCapture.HotkeyStart="Snimanje prozora u prvom planu" GameCapture.HotkeyStop="Isključivanje snimanja" +Mode="Režim" diff --git a/plugins/win-capture/data/locale/sr-SP.ini b/plugins/win-capture/data/locale/sr-SP.ini index 72b14230787f11..ec13f7a52b2a4a 100644 --- a/plugins/win-capture/data/locale/sr-SP.ini +++ b/plugins/win-capture/data/locale/sr-SP.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Снимај overlay треће стране (ка GameCapture.AntiCheatHook="Користи прикључак за компатибилност са заштитом од варања" GameCapture.HotkeyStart="Снимање прозора у првом плану" GameCapture.HotkeyStop="Искључивање снимања" +Mode="Режим" diff --git a/plugins/win-capture/data/locale/sv-SE.ini b/plugins/win-capture/data/locale/sv-SE.ini index b362074cf7566b..1456b4c6ffe1d0 100644 --- a/plugins/win-capture/data/locale/sv-SE.ini +++ b/plugins/win-capture/data/locale/sv-SE.ini @@ -12,9 +12,14 @@ Monitor="Bildskärm" PrimaryMonitor="Primära bildskärmen" GameCapture="Spelintagning" GameCapture.AnyFullscreen="Spela in alla program i fullskärm" +GameCapture.CaptureWindow="Spela in specifikt fönster" +GameCapture.UseHotkey="Spela in förgrundsfönster med kortkommando" GameCapture.ForceScaling="Tvinga skalning" GameCapture.ScaleRes="Skala upplösning" GameCapture.LimitFramerate="Begränsa inspelad bildhastighet" GameCapture.CaptureOverlays="Spela in tredje part överlägg (som Steam)" GameCapture.AntiCheatHook="Använd anit-fusk kompabilitetshake" +GameCapture.HotkeyStart="Fånga förgrundsfönster" +GameCapture.HotkeyStop="Inaktivera källa" +Mode="Läge" diff --git a/plugins/win-capture/data/locale/uk-UA.ini b/plugins/win-capture/data/locale/uk-UA.ini index 0a600d62529e03..971d948c93027a 100644 --- a/plugins/win-capture/data/locale/uk-UA.ini +++ b/plugins/win-capture/data/locale/uk-UA.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="Захоплювати сторонні оверл GameCapture.AntiCheatHook="Використовувати сумісність з Anti-cheat технологіями" GameCapture.HotkeyStart="Захоплювати перше наявне вікно" GameCapture.HotkeyStop="Деактивувати захоплення" +Mode="Тип захвату" diff --git a/plugins/win-capture/data/locale/zh-CN.ini b/plugins/win-capture/data/locale/zh-CN.ini index 6946037df72e09..4bc1e4eb2bdd8d 100644 --- a/plugins/win-capture/data/locale/zh-CN.ini +++ b/plugins/win-capture/data/locale/zh-CN.ini @@ -12,9 +12,14 @@ Monitor="显示器" PrimaryMonitor="主监视器" GameCapture="游戏捕获" GameCapture.AnyFullscreen="捕获任何全屏应用程序" +GameCapture.CaptureWindow="捕获特定窗口" +GameCapture.UseHotkey="用热键捕获前景窗口" GameCapture.ForceScaling="强制缩放" GameCapture.ScaleRes="缩放分辨率" GameCapture.LimitFramerate="限制捕获帧速率" GameCapture.CaptureOverlays="捕获第三方 (如: steam) 覆盖" GameCapture.AntiCheatHook="使用反作弊兼容性钩子" +GameCapture.HotkeyStart="捕获前景窗口" +GameCapture.HotkeyStop="停止捕获" +Mode="模式" diff --git a/plugins/win-capture/data/locale/zh-TW.ini b/plugins/win-capture/data/locale/zh-TW.ini index 42be934b290c5f..a7f2234c0cacc2 100644 --- a/plugins/win-capture/data/locale/zh-TW.ini +++ b/plugins/win-capture/data/locale/zh-TW.ini @@ -21,4 +21,5 @@ GameCapture.CaptureOverlays="擷取第三方程式覆蓋層 (如steam)" GameCapture.AntiCheatHook="使用相容於反作弊的掛鉤" GameCapture.HotkeyStart="擷取前景視窗" GameCapture.HotkeyStop="停止擷取" +Mode="模式" diff --git a/plugins/win-dshow/data/locale/ca-ES.ini b/plugins/win-dshow/data/locale/ca-ES.ini index 725fe07dc91d72..0c5e38b43aa2cd 100644 --- a/plugins/win-dshow/data/locale/ca-ES.ini +++ b/plugins/win-dshow/data/locale/ca-ES.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Àudio de la sortida de l'escriptori (WaveOut)" UseCustomAudioDevice="Usa un dispositiu d'àudio personalitzat" AudioDevice="Dispositiu d'àudio" Buffering="Memòria intermèdia" +Buffering.ToolTip="Quan s'activa, les dades de vídeo/àudio s'emmagatzemen per assegurar una\nreproducció ho més suau i fluida possible, però a costa d'un major temps de resposta.\nQuan s'usa buffering amb una targeta capturadora de vídeo, es recomana establir\nla targeta i el programa a la mateixa velocitat de fotogrames per a un millor resultat.\n\nQuan es desactiva, es garanteix la reproducció amb un temps de resposta més baix, però a costa de perdre precisió en la\n\reproducció dels fotogrames. Això és ideal per a l'ús de càmera web, o quan voleu utilitzar la finestra de vista prèvia del\nprograma per jugar a la consola.\n\nL'autodetecció (per defecte) s'activa si el dispositiu té retard i es desactiva\nsi no té cap retard." Buffering.AutoDetect="Detecció automàtica" Buffering.Enable="Activa" Buffering.Disable="Desactiva" diff --git a/plugins/win-dshow/data/locale/cs-CZ.ini b/plugins/win-dshow/data/locale/cs-CZ.ini index 8c47ff7e344ec7..2afb79b2bad75b 100644 --- a/plugins/win-dshow/data/locale/cs-CZ.ini +++ b/plugins/win-dshow/data/locale/cs-CZ.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Poslouchat na ploše (WaveOut)" UseCustomAudioDevice="Použít vlastní zvukové zařízení" AudioDevice="Zvukové zařízení" Buffering="Načítání" +Buffering.ToolTip="Při povoleném, načte obrazová/zvuková data do vyrovnávací paměti pro zajištění\nnejplynulejšího a nejpřesnějšího možného přehrání, ale za cenu zvýšení odezvy.\nPři použití se střihovou kartou je doporučeno nastavit kartu \na program na stejnou snímkovací frekvenci.\n\nPři zakázaném zaručuje nízkou odezvu, ale to za cenu ztrátě přesnosti.\nToto je ideální pro webkamery, nebo pokud chcete použít okno\nnáhledu jako konzoli.\n\nAuto-detekce (výchozí) jej nastaví na povoleno, pouze pokud má zařízení\nzpoždění, jinak jej zakáže." Buffering.AutoDetect="Vybrat automaticky" Buffering.Enable="Povolit" Buffering.Disable="Zakázat" diff --git a/plugins/win-dshow/data/locale/de-DE.ini b/plugins/win-dshow/data/locale/de-DE.ini index 0a896a015df968..4aff5bc9a639ab 100644 --- a/plugins/win-dshow/data/locale/de-DE.ini +++ b/plugins/win-dshow/data/locale/de-DE.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Audioausgabe auf dem Desktop (WaveOut)" UseCustomAudioDevice="Benutzerdefiniertes Audiogerät verwenden" AudioDevice="Audiogerät" Buffering="Pufferung" +Buffering.ToolTip="Wenn aktiviert puffert Video/Ton Daten, um die möglichst flüssigste und\ngenaueste Wiedergabe zu gewährleisten, aber auf Kosten von erhöhter Verzögerung. Wenn mit einer Capture Card Pufferung benutzt wird,\nwird es empfohlen die Karte und das Programm (OBS Studio), auf die gleiche FPS Anzahl zu setzen, um die besten Ergebnisse zu erzielen.\n\nWenn deaktiviert, wird die niedrigste Verzögerung für die Wiedergabe gewährleistet, aber auf Kosten der Bilder-Wiedergabegenauigkeit.\nDies ist ideal für Webcams, oder wenn sie das Vorschaufenster des Programms benutzen wollen,\num zum Beispiel eine Spielkonsole zu spielen\n\nAutomatisch erkennen aktiviert es, wenn das Gerät Verzögerung hat und deaktiviert es,\nwenn es keine Verzögerung hat." Buffering.AutoDetect="Automatisch erkennen" Buffering.Enable="Aktivieren" Buffering.Disable="Deaktivieren" diff --git a/plugins/win-dshow/data/locale/es-ES.ini b/plugins/win-dshow/data/locale/es-ES.ini index f65dd1c39011d6..120b5031973559 100644 --- a/plugins/win-dshow/data/locale/es-ES.ini +++ b/plugins/win-dshow/data/locale/es-ES.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Salida de audio de escritorio (WaveOut)" UseCustomAudioDevice="Utilizar dispositivo de audio personalizado" AudioDevice="Dispositivo de audio" Buffering="Almacenando en buffer" +Buffering.ToolTip="Cuando se activa, almacena datos de vídeo/audio para asegurar una\nreproducción lo mas suave y fluida posible, pero a costa de un mayor tiempo de respuesta.\nCuando se usa buffering con una tarjeta capturadora de vídeo, se recomienda establecer\nla tarjeta y el programa a la misma velocidad de fotogramas para un mejor resultado.\n\nCuando se desactiva, se garantiza la reproducción con un tiempo de respuesta más bajo, pero a costa de perder precisión en la \n\reproduccion de los fotogramas. Esto es ideal para el uso de webcam, o cuando desea utilizar la ventana de vista previa de el\nprograma para jugar a una console.\n\nAutodetectar (por defecto) se activa si el dispositivo tiene retardo y se desactiva\nsi no tiene ningun retardo." Buffering.AutoDetect="Autodetectar" Buffering.Enable="Habilitado" Buffering.Disable="Deshabilitar" diff --git a/plugins/win-dshow/data/locale/eu-ES.ini b/plugins/win-dshow/data/locale/eu-ES.ini index bf31e7172c42bc..1e08c6fc6a2fc6 100644 --- a/plugins/win-dshow/data/locale/eu-ES.ini +++ b/plugins/win-dshow/data/locale/eu-ES.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Irteera mahaigaineko audioa (WaveOut)" UseCustomAudioDevice="Erabili audio gailu pertsonalizatua" AudioDevice="Audio gailua" Buffering="Bufferrereratzen" +Buffering.ToolTip="Gaitutakoan, bideo/audio datuak oroimeneratzen ditu irakurketa lehunagoa\neta zehatzagoa egiteko, baina atzerapena handitzearen ordainarekin.\nOroimeneratzea bideo harpen txartel batekin erabiltzerakoan, gomendagarria da\ntxartela eta programa frameneurri bera erabiltzeko ezartzea.\n\nEzgaitutakoan, irakurketa atzerapen txikiagoa zihurtatzen du, baina\nframe irakurketa zehaztasunaren ordainarekin. Hau egokia da aurpegi kamerentzat,\nedo programaren aurreikuspen leihoa erabiltzea nahi duzunean kontsola batean irakurtzeko.\n\nBerez-atzemanek (berezkoa) gaitu egiten du gailuak atzerapena badu, eta ezgaitu\natzerapenik ez badu." Buffering.AutoDetect="Auto-detektatu" Buffering.Enable="Gaitu" Buffering.Disable="Ezgaitu" diff --git a/plugins/win-dshow/data/locale/fi-FI.ini b/plugins/win-dshow/data/locale/fi-FI.ini index 037da982767d05..15cffcde6afd61 100644 --- a/plugins/win-dshow/data/locale/fi-FI.ini +++ b/plugins/win-dshow/data/locale/fi-FI.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Toista äänilaite (WaveOut)" UseCustomAudioDevice="Käytä mukautettua äänilaitetta" AudioDevice="Äänilaite" Buffering="Puskurointi" +Buffering.ToolTip="Käytössä ollessaan puskuroi videokuvaa ja ääntä viiveen kustannuksella,\nettä lopputulos olisi mahdollisimman sulava ja tarkka. Kun puskurointia käytetään\nvideokaappauskortin kanssa, on suositeltavaa laittaa ruudunpäivitysnopeus\nsamaksi sekä ohjelmasta, että kortista.\n\nPoissa käytöstä ollessaan mahdollistaa pienemmän viiveen ruudunpäivityksen\nkustannuksella. Webkameroille ja videokaappauslaitteille on\nparasta pitää puskurointi pois käytöstä.\n\nAutomaattinen tunnistus (oletus) ottaa asetuksen käyttöön jos laitteessa on viivettä\nja pois käytöstä jos ei." Buffering.AutoDetect="Automaattinen tunnistus" Buffering.Enable="Ota käyttöön" Buffering.Disable="Poista käytöstä" diff --git a/plugins/win-dshow/data/locale/fr-FR.ini b/plugins/win-dshow/data/locale/fr-FR.ini index c37d41bea95949..2d141a8c3e1fe2 100644 --- a/plugins/win-dshow/data/locale/fr-FR.ini +++ b/plugins/win-dshow/data/locale/fr-FR.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Sortie audio du bureau (WaveOut)" UseCustomAudioDevice="Utiliser un périphérique audio personnalisé" AudioDevice="Périphérique audio" Buffering="Mise en mémoire tampon" +Buffering.ToolTip="Activer la mise en mémoire tampon permet de conserver un flux vidéo/audio fluide et sans accrocs, au prix\nd'un peu de latence. Si vous utilisez la mise en mémoire tampon avec une carte de capture vidéo, il est\nrecommandé de configurer la carte et OBS sur le même débit d'images pour un résultat optimal.\n\nDésactiver la mise en mémoire tampon permet de réduire la latence, au prix d'éventuelles pertes d'images\n\nL'auto-détection active la mise en mémoire tampon si le périphérique a de la latence, et la désactive le cas échéant." Buffering.AutoDetect="Détecter automatiquement" Buffering.Enable="Activer" Buffering.Disable="Désactiver" diff --git a/plugins/win-dshow/data/locale/hr-HR.ini b/plugins/win-dshow/data/locale/hr-HR.ini index 33f96d7641d63c..804ba8846311a1 100644 --- a/plugins/win-dshow/data/locale/hr-HR.ini +++ b/plugins/win-dshow/data/locale/hr-HR.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Izlaz na zvuk radne površine (WaveOut)" UseCustomAudioDevice="Koristi specifičan uređaj za zvuk" AudioDevice="Uređaj za zvuk" Buffering="Baferovanje" +Buffering.ToolTip="Kada je omogućeno, baferovanje video/zvučnih podatke osigurava tečnu i najprecizniju\nmoguću reprodukciju, ali nosi i posledicu produženog kašnjenja. Kada je baferovanje u\nupotrebi sa karticom za hvatanje videa, preporučljivo je postaviti karticu i\nprogram na isti frejmrejt da dobijete najbolje rezultate.\n\nKada je onemogućeno, obezbeđuje najmanje kašnjenje reprodukcije, ali uz posledicu nepreciznosti\nreprodukovanog frejma. Ovo je idealno za kamere koje snimaju lica, ili kada želite\nda koristite programski prozor za pregled da biste igrali na konzoli.\n\nAutomatsko-otkrivanje (podrazumevano) automatski omogućava ovo ako uređaj ima kašnjenje i onemogućava\nako nema kašnjenja." Buffering.AutoDetect="Automatsko-otkrivanje" Buffering.Enable="Omogući" Buffering.Disable="Onemogući" diff --git a/plugins/win-dshow/data/locale/hu-HU.ini b/plugins/win-dshow/data/locale/hu-HU.ini index d11cc8e8590fbe..82e16dc7477ae4 100644 --- a/plugins/win-dshow/data/locale/hu-HU.ini +++ b/plugins/win-dshow/data/locale/hu-HU.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Asztali kimeneti hangeszköz (WaveOut)" UseCustomAudioDevice="Egyedi hangeszköz használata" AudioDevice="Audio eszköz" Buffering="Pufferelés" +Buffering.ToolTip="Engedélyezés esetén, az audio/video adatot puffereli, hogy biztosítsa a folyamatos\nés pontos lejátszásért, viszont ez megnövekedett késéssel járhat. Ha \nfelvevőkártyával használja, akkor ajánlott a kártya és a program \nazonos képkockafrissítésen való futtatása a legjobb eredményért.\n\nKikapcsolt állapotban, a késés a lehető legkevesebb, viszont ez a \nképlejátszás pontosságának a rovására mehet. Ez arckamerák esetében ideális, \n vagy ha a program előnézeti ablakát használja konzollal való játékhoz.\n\nAutomata érzékelés (alapértelmezett) engedélyezi\b, ha az eszközön van késés, és kikapcsolja, ha nincs." Buffering.AutoDetect="Automatikus felismerés" Buffering.Enable="Engedélyezés" Buffering.Disable="Letiltás" diff --git a/plugins/win-dshow/data/locale/ja-JP.ini b/plugins/win-dshow/data/locale/ja-JP.ini index fc84ad90cc437f..70791e64696b73 100644 --- a/plugins/win-dshow/data/locale/ja-JP.ini +++ b/plugins/win-dshow/data/locale/ja-JP.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="デスクトップ音声出力 (WaveOut)" UseCustomAudioDevice="カスタム音声デバイスを使用する" AudioDevice="音声デバイス" Buffering="バッファリング" +Buffering.ToolTip="有効の場合、最も正確で滑らかな再生を可能にするために映像/音声のデータをバッファしますが、遅延は増加します。\nビデオキャプチャカードでバッファリングを使用する場合は、最良の結果を得るためにカードとプログラムに同じフレームレートを設定することを推奨します。\n\n無効の場合、再生の遅延は最小になりますが、フレーム再生の精度が落ちます。\nこれは顔カメラや、コンソールで再生してプログラムのプレビューウィンドウを使用したい場合に最適です。\n\n自動検出 (既定) ではデバイスに遅延がある場合は有効にし、遅延がない場合は無効にします。" Buffering.AutoDetect="自動検出" Buffering.Enable="有効" Buffering.Disable="無効" diff --git a/plugins/win-dshow/data/locale/ko-KR.ini b/plugins/win-dshow/data/locale/ko-KR.ini index 06073edf81b204..d884c79e335b43 100644 --- a/plugins/win-dshow/data/locale/ko-KR.ini +++ b/plugins/win-dshow/data/locale/ko-KR.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="출력 데스크탑 오디오 (WaveOut)" UseCustomAudioDevice="사용자 지정 오디오 장치 사용" AudioDevice="오디오 장치" Buffering="버퍼링" +Buffering.ToolTip="해당 설정이 활성화가 되면 영상/소리 데이터를 버퍼링하여 가능하면 최대한 부드럽고 \n정확하게 전달될 수 있도록 합니다. 대신 지연 시간이 증가합니다. 비디오 캡쳐 카드를 통해\n버퍼링을 한다면, 그 카드와 \n프로그램이 동일한 프레임률을 사용할 것을 추천합니다.\n\n반대로 비활성화하면 지연 시간이 최대한 낮아지지만 프레임\n과 재생 정확도가 떨어집니다. 이 설정은 전면 카메라 환경이나\n프로그램의 미리보기 창을 통해 게임 콘솔을 이용할 때 이상적입니다.\n\n자동 감지 (기본) 설정은 장치에 지연 현상이 있을 경우 활성화하고 지연이 감지 되지 않으면\n비활성화합니다." Buffering.AutoDetect="자동 감지" Buffering.Enable="활성화" Buffering.Disable="비활성화" diff --git a/plugins/win-dshow/data/locale/nl-NL.ini b/plugins/win-dshow/data/locale/nl-NL.ini index ea6d57c8a2cc81..275658d500e20e 100644 --- a/plugins/win-dshow/data/locale/nl-NL.ini +++ b/plugins/win-dshow/data/locale/nl-NL.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Afspelen naar desktop-audio (WaveOut)" UseCustomAudioDevice="Gebruik aangepast audioapparaat" AudioDevice="Audioapparaat" Buffering="Bufferen" +Buffering.ToolTip="Indien ingeschakeld wordt video/audio data gebufferd om soepele en accurate\nweergave te garanderen, ten koste van een hogere vertraging. Als buffering\ngebruikt wordt, wordt het aangeraden om de kaart en het programma op\ndezelfde frame-rate in te stellen voor de beste resultaten.\n\nIndien uitgeschakeld, wordt de vertraging geminimaliseerd ten koste van\naccurate frame weergave. Dit is ideaal voor gezichtscameras, of wanneer je de\npreview van het programma wil gebruiken om een console te spelen.\n\nAutomatisch detecteren (standaard) stelt het in op ingeschakeld als het\napparaat vertraging heeft, en uitgeschakeld als het geen vertraging heeft." Buffering.AutoDetect="Automatisch detecteren" Buffering.Enable="Inschakelen" Buffering.Disable="Uitschakelen" diff --git a/plugins/win-dshow/data/locale/pl-PL.ini b/plugins/win-dshow/data/locale/pl-PL.ini index b598b5e96d9cc1..a23e4c54f47e7e 100644 --- a/plugins/win-dshow/data/locale/pl-PL.ini +++ b/plugins/win-dshow/data/locale/pl-PL.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Przekaż dźwięk dalej (WaveOut)" UseCustomAudioDevice="Użyj własnego wyjścia audio" AudioDevice="Urządzenie audio" Buffering="Buforowanie" +Buffering.ToolTip="Przy włączonej opcji obraz i dźwięk jest buforowany. Zapewnić to ma jak\nnajbardziej płynne odtwarzanie materiału kosztem zwiększonego opóźnienia.\nKorzystając z tej opcji w przypadku kart przechwytujących zaleca się\nustawienie identycznej wartości klatek na sekundę.\n\nPrzy opcji wyłączonej materiał odtwarzany jest z jak najmniejszym\nopóźnieniem nawet kosztem dokładności odtwarzania poszczególnych klatek.\nOpcja ta nadaje się idealnie do takich źródeł jak kamera filmująca twarz\nbądź gdy chcemy użyć podglądu w programie w celu grania na konsoli.\n\nOpcja automatycznie (domyślnie) sprawia, że opcja jest włączona dla urządzeń\nz opóźnieniem a wyłączona dla tych bez opóźnienia." Buffering.AutoDetect="automatycznie" Buffering.Enable="włączone" Buffering.Disable="wyłączone" diff --git a/plugins/win-dshow/data/locale/pt-BR.ini b/plugins/win-dshow/data/locale/pt-BR.ini index 95e79befa74d2b..257c887f807ede 100644 --- a/plugins/win-dshow/data/locale/pt-BR.ini +++ b/plugins/win-dshow/data/locale/pt-BR.ini @@ -20,7 +20,7 @@ VideoFormat.Unknown="Desconhecido (%1)" AudioOutputMode="Modo da saída de Audio" AudioOutputMode.Capture="Capturar apenas o áudio" AudioOutputMode.DirectSound="Saída de áudio de ambiente de trabalho (DirectSound)" -AudioOutputMode.WaveOut="Saída de áudio de ambiente de trabalho (WaveOut)" +AudioOutputMode.WaveOut="Saída de áudio do ambiente de trabalho (WaveOut)" UseCustomAudioDevice="Utilizar dispositivo de áudio personalizado" AudioDevice="Dispositivo de Áudio" Buffering="Buffering" diff --git a/plugins/win-dshow/data/locale/ru-RU.ini b/plugins/win-dshow/data/locale/ru-RU.ini index a3f45aab6b4d2a..889f31c13b8180 100644 --- a/plugins/win-dshow/data/locale/ru-RU.ini +++ b/plugins/win-dshow/data/locale/ru-RU.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Вывод системного аудио (WaveOut)" UseCustomAudioDevice="Использовать пользовательское аудиоустройство" AudioDevice="Аудиоустройство" Buffering="Буферизация" +Buffering.ToolTip="Если функция включена, аудио/видео поток буферизируется для плавного и\nточного воспроизведения, но при этом увеличивается задержка. Если вы\nиспользуете карту захвата, рекомендуем программно установить одинаковую\nчастоту кадров для лучшего результата.\n\nЕсли функция выключена, поток воспроизводится с наименьшей задержкой,\nно при этом страдает точность воспроизведения кадров. Этот вариант\nидеален для веб-камер, или если вы хотите использовать окно предпросмотра\nпрограммы для игры на консоли.\n\nАвто-обнаружение (по умолчанию), устанавливает опцию на \"Включено\", если\nустройство имеет задержку, и на \"Отключено\", если не имеет задержки." Buffering.AutoDetect="Авто-обнаружение" Buffering.Enable="Включить" Buffering.Disable="Отключить" diff --git a/plugins/win-dshow/data/locale/sr-CS.ini b/plugins/win-dshow/data/locale/sr-CS.ini index 2a9392a2694dc3..13bfebac6f9980 100644 --- a/plugins/win-dshow/data/locale/sr-CS.ini +++ b/plugins/win-dshow/data/locale/sr-CS.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Izlaz na zvuk radne površine (WaveOut)" UseCustomAudioDevice="Koristi specifičan uređaj za zvuk" AudioDevice="Uređaj za zvuk" Buffering="Baferovanje" +Buffering.ToolTip="Kada je omogućeno, baferovanje video/zvučnih podatke osigurava tečnu i najprecizniju\nmoguću reprodukciju, ali nosi i posledicu produženog kašnjenja. Kada je baferovanje u\nupotrebi sa karticom za hvatanje videa, preporučljivo je postaviti karticu i\nprogram na isti frejmrejt da dobijete najbolje rezultate.\n\nKada je onemogućeno, obezbeđuje najmanje kašnjenje reprodukcije, ali uz posledicu nepreciznosti\nreprodukovanog frejma. Ovo je idealno za kamere koje snimaju lica, ili kada želite\nda koristite programski prozor za pregled da biste igrali na konzoli.\n\nAutomatsko-otkrivanje (podrazumevano) automatski omogućava ovo ako uređaj ima kašnjenje i onemogućava\nako nema kašnjenja." Buffering.AutoDetect="Automatsko-otkrivanje" Buffering.Enable="Omogući" Buffering.Disable="Onemogući" diff --git a/plugins/win-dshow/data/locale/sr-SP.ini b/plugins/win-dshow/data/locale/sr-SP.ini index 68fe47c6a753c6..134db99347512c 100644 --- a/plugins/win-dshow/data/locale/sr-SP.ini +++ b/plugins/win-dshow/data/locale/sr-SP.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Излаз на звук радне површине (W UseCustomAudioDevice="Користи специфичан уређај за звук" AudioDevice="Уређај за звук" Buffering="Баферовање" +Buffering.ToolTip="Када је омогућено, баферовање видео/звучних података осигурава течну и најпрецизнију\nмогућу репродукцију, али носи и последицу продуженог кашњења. Када је баферовање у\nупотреби са картицом за хватање видеа, препоручљиво је поставити картицу и\nпрограм на исти фрејмрејт да добијете најбоље резултате.\n\nКада је онемогућено, обезбеђује најмање кашњење репродукције, али уз последицу непрецизности\nрепродукованог фрејма. Ово је идеално за камере које снимају лица, или када желите\nда користите програмски прозор за преглед да бисте играли на конзоли.\n\nАутоматско-откривање (подразумевано) аутоматски омогућава ово ако уређај има кашњење и онемогућава\nако нема кашњења." Buffering.AutoDetect="Аутоматско-откривање" Buffering.Enable="Омогући" Buffering.Disable="Онемогући" diff --git a/plugins/win-dshow/data/locale/sv-SE.ini b/plugins/win-dshow/data/locale/sv-SE.ini index b9ee5979cedfa3..3f27a26ebdc7d8 100644 --- a/plugins/win-dshow/data/locale/sv-SE.ini +++ b/plugins/win-dshow/data/locale/sv-SE.ini @@ -19,6 +19,8 @@ VideoFormat.Any="Valfri" VideoFormat.Unknown="Okänd (%1)" AudioOutputMode="Ljudutgångsläge" AudioOutputMode.Capture="Fånga endast ljud" +AudioOutputMode.DirectSound="Skrivbordsljud (DirectSound)" +AudioOutputMode.WaveOut="Skrivbordsljud (WaveOut)" UseCustomAudioDevice="Använd anpassad ljudenhet" AudioDevice="Ljudenhet" Buffering="Buffrar" diff --git a/plugins/win-dshow/data/locale/uk-UA.ini b/plugins/win-dshow/data/locale/uk-UA.ini index 9bdf2633f9f2ea..7e99202b54edfc 100644 --- a/plugins/win-dshow/data/locale/uk-UA.ini +++ b/plugins/win-dshow/data/locale/uk-UA.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="Виводити як Системне аудіо (Wave UseCustomAudioDevice="Інший пристрій для аудіо" AudioDevice="Аудіопристрій" Buffering="Буферизація" +Buffering.ToolTip="Коли Увімкнено, буферизує відео/аудіо данні задля плавного та точного\nвідтворення, яке тільки можливе, але за рахунок збільшення затримки.\nЯкщо буферизація використовується з картою захоплення, то для кращого\nрезультату рекомендується встановити однакову частоту кадрів\nдля програми і самого пристрою.\n\nЯкщо Вимкнено, данні програються з найменшою затримкою,\nале за рахунок плавності відтворення. Це зручно для фронтальних\nкамер, або у випадку використання вікна Перегляду програми\nдля гри у консольні ігри.\n\nАвтовизначення (за замовчанням) встановлює стан Увімкнено\nдля пристроїв з внутрішньою затримкою, і стан Вимкнено,\nякщо пристрій не має внутрішньої затримки." Buffering.AutoDetect="Автовизначення" Buffering.Enable="Увімкнути" Buffering.Disable="Вимкнено" diff --git a/plugins/win-dshow/data/locale/zh-CN.ini b/plugins/win-dshow/data/locale/zh-CN.ini index 0742ba42c886e1..94ea6083dc50bd 100644 --- a/plugins/win-dshow/data/locale/zh-CN.ini +++ b/plugins/win-dshow/data/locale/zh-CN.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="输出桌面音频(WaveOut)" UseCustomAudioDevice="使用自定义的音频设备" AudioDevice="音频设备" Buffering="正在缓冲" +Buffering.ToolTip="当启用时, 缓存视频/音频数据以确保最流畅和\n最准确的回放, 但是会增加延迟成本. 当使用\n视频卡来缓存时, 推荐设置卡和程序\n为同一个帧率来得到最好的结果.\n\n当禁用时, 确保最低的延时回放, 但是损失回放帧的准确性.\n 这是对于脸部相机是理想的, 或者当你先要使用程序的\n预览窗口来播放一个控制台.\n\n自动检测(默认) 如果设备有延时设置为启用, \n如果没有延时禁用" Buffering.AutoDetect="自动检测" Buffering.Enable="启用" Buffering.Disable="禁用" diff --git a/plugins/win-dshow/data/locale/zh-TW.ini b/plugins/win-dshow/data/locale/zh-TW.ini index 5b4793868c9723..4645531f570399 100644 --- a/plugins/win-dshow/data/locale/zh-TW.ini +++ b/plugins/win-dshow/data/locale/zh-TW.ini @@ -24,6 +24,7 @@ AudioOutputMode.WaveOut="輸出桌面音訊 (WaveOut)" UseCustomAudioDevice="使用自訂音訊裝置" AudioDevice="音訊裝置" Buffering="緩衝" +Buffering.ToolTip="啟用時,將會緩衝影音資料以確保平順且準確的播放,但將會\n增加延遲。當同時使用緩衝以及影音擷取卡時,建議將擷取卡\n與程式設定為同樣的擷取速率以獲得最好的效果。\n\n停用時,會有最少的延遲,但是畫面播放會較為不精準。建議\n使用於臉部攝影機或是利用本程式的預覽視窗來遊玩主機的情\n境。\n\n自動偵測(預設值)會在裝置有延遲時開啟,沒有延遲時關閉。" Buffering.AutoDetect="自動偵測" Buffering.Enable="啟用" Buffering.Disable="停用" diff --git a/plugins/win-mf/data/locale/pt-BR.ini b/plugins/win-mf/data/locale/pt-BR.ini index ae9598f924ea50..a97fdbfc5d4552 100644 --- a/plugins/win-mf/data/locale/pt-BR.ini +++ b/plugins/win-mf/data/locale/pt-BR.ini @@ -23,7 +23,7 @@ MF.H264.QPB="QP B-Frame" MF.H264.Profile="Perfil" MF.H264.Advanced="Avançado" -MF.H264.EncoderSWMicrosoft="Codificador da Microsoft Software H.264" +MF.H264.EncoderSWMicrosoft="Codificador H.264 da Microsoft" MF.H264.EncoderHWAMD="Motor de Codificação de Vídeo H.264 AMD (Media Foundation)" MF.H264.EncoderHWIntel="Codificador da Intel Quick Sync H.264 (Media Foundation)" MF.H264.EncoderHWNVIDIA="Codificador da NVIDIA NVENC H.264 (Media Foundation)" diff --git a/plugins/win-mf/data/locale/sv-SE.ini b/plugins/win-mf/data/locale/sv-SE.ini index 5249beb8c9e799..756083dd5b5858 100644 --- a/plugins/win-mf/data/locale/sv-SE.ini +++ b/plugins/win-mf/data/locale/sv-SE.ini @@ -4,6 +4,7 @@ Bitrate="Bithastighet" MF.H264.EncoderName="Media Foundation H264-kodare" MF.H264.Encoder="Kodarnamn" MF.H264.LowLatency="Låg latens (Avaktivera bildrute-platsförändring)" +MF.H264.BFrames="Antal efterföljande B-Frames" MF.H264.CustomBufsize="Använd anpassad buffertstorlek" MF.H264.BufferSize="Buffertstorlek" MF.H264.CustomMaxBitrate="Använd anpassad maximal bithastighet" @@ -16,10 +17,14 @@ MF.H264.VBR="VBR (varierande bithastighet)" MF.H264.CQP="CQP (konstant kvalitet)" MF.H264.MinQP="Minimal QP" MF.H264.MaxQP="Maximal QP" +MF.H264.QPI="QP I-Frame" +MF.H264.QPP="QP P-Frame" +MF.H264.QPB="QP B-Frame" MF.H264.Profile="Profil" MF.H264.Advanced="Avancerat" MF.H264.EncoderSWMicrosoft="Microsoft Software H.264-kodare" +MF.H264.EncoderHWAMD="AMD Video Coding Engine H.264-kodare (Media Foundation)" MF.H264.EncoderHWIntel="Intel Quick Sync H.264-kodare (Media Foundation)" MF.H264.EncoderHWNVIDIA="NVIDIA NVENC H.264-kodare (Media Foundation)" diff --git a/plugins/win-mf/data/locale/uk-UA.ini b/plugins/win-mf/data/locale/uk-UA.ini index f01d3fe2498ab8..dadeaafc35c19b 100644 --- a/plugins/win-mf/data/locale/uk-UA.ini +++ b/plugins/win-mf/data/locale/uk-UA.ini @@ -15,8 +15,8 @@ MF.H264.RateControl="Керування потоком" MF.H264.CBR="CBR (постійний бітрейт)" MF.H264.VBR="VBR (змінний бітрейт)" MF.H264.CQP="CQP (фіксована якість)" -MF.H264.MinQP="Мінімальне QP" -MF.H264.MaxQP="Максимальне QP" +MF.H264.MinQP="Мінімальний QP" +MF.H264.MaxQP="Максимальний QP" MF.H264.QPI="QP для I-кадрів" MF.H264.QPP="QP для P-кадрів" MF.H264.QPB="QP для B-кадрів" From cdd788c4beb489a8540d61a26dc72d76779e690e Mon Sep 17 00:00:00 2001 From: jp9000 Date: Tue, 27 Sep 2016 18:06:09 -0700 Subject: [PATCH 096/107] UI: Just use 'OK' button for license agreement dialog An "I Agree" or "I Disagree" button isn't necessary for GPL. --- UI/forms/OBSLicenseAgreement.ui | 35 +-------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/UI/forms/OBSLicenseAgreement.ui b/UI/forms/OBSLicenseAgreement.ui index f6ba2a768911d2..e9c2d4970088c5 100644 --- a/UI/forms/OBSLicenseAgreement.ui +++ b/UI/forms/OBSLicenseAgreement.ui @@ -52,16 +52,6 @@ - - - - LicenseAgreement.ClickIAgreeToContinue - - - true - - - @@ -87,14 +77,7 @@ - LicenseAgreement.IAgree - - - - - - - LicenseAgreement.Exit + OK @@ -121,21 +104,5 @@ - - decline - clicked() - OBSLicenseAgreement - reject() - - - 312 - 410 - - - 424 - 418 - - - From 071ed465cbe32665088f3bc8aa2503417ed28507 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 28 Sep 2016 01:56:10 -0700 Subject: [PATCH 097/107] UI: Fix window size/pos not saving on exit --- UI/window-basic-main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index e795d78359c5d3..c6f2e894b1a1fd 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1469,11 +1469,6 @@ OBSBasic::~OBSBasic() QList splitterSizes = ui->mainSplitter->sizes(); bool alwaysOnTop = IsAlwaysOnTop(this); - if (isVisible()) - config_set_string(App()->GlobalConfig(), - "BasicWindow", "geometry", - saveGeometry().toBase64().constData()); - config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterTop", splitterSizes[0]); config_set_int(App()->GlobalConfig(), "BasicWindow", "splitterBottom", @@ -2639,6 +2634,11 @@ void OBSBasic::ClearSceneData() void OBSBasic::closeEvent(QCloseEvent *event) { + if (isVisible()) + config_set_string(App()->GlobalConfig(), + "BasicWindow", "geometry", + saveGeometry().toBase64().constData()); + if (outputHandler && outputHandler->Active()) { SetShowing(true); From 8b877f7c3666ed91e6891b8608c7d0aabab71874 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 28 Sep 2016 01:56:29 -0700 Subject: [PATCH 098/107] libobs/util: Fix fread_utf8 not working with files < 3 bytes When it was checking for the BOM at the beginning of the file, it would just return out of the function if it didn't read at least 3 bytes. This would particularly affect text sources. --- libobs/util/platform.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libobs/util/platform.c b/libobs/util/platform.c index ffccf8ab2966bc..8d8669eccef8b7 100644 --- a/libobs/util/platform.c +++ b/libobs/util/platform.c @@ -164,7 +164,6 @@ size_t os_fread_mbs(FILE *file, char **pstr) size_t os_fread_utf8(FILE *file, char **pstr) { size_t size = 0; - size_t size_read; size_t len = 0; *pstr = NULL; @@ -177,11 +176,13 @@ size_t os_fread_utf8(FILE *file, char **pstr) char *utf8str; off_t offset; + bom[0] = 0; + bom[1] = 0; + bom[2] = 0; + /* remove the ghastly BOM if present */ fseek(file, 0, SEEK_SET); - size_read = fread(bom, 1, 3, file); - if (size_read != 3) - return 0; + fread(bom, 1, 3, file); offset = (astrcmp_n(bom, "\xEF\xBB\xBF", 3) == 0) ? 3 : 0; From 32378124af18382bb119477ff4704537353614e2 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 28 Sep 2016 01:57:44 -0700 Subject: [PATCH 099/107] obs-text: Change file update interval to 1 sec (from 2) --- plugins/obs-text/gdiplus/obs-text.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index c8681c8d848f2c..771db239a7af19 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -742,7 +742,7 @@ inline void TextSource::Tick(float seconds) update_time_elapsed += seconds; - if (update_time_elapsed >= 2.0f) { + if (update_time_elapsed >= 1.0f) { time_t t = get_modified_timestamp(file.c_str()); update_time_elapsed = 0.0f; From 6d16c51cd117a1da04d64f63562b112c38d3fd47 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 28 Sep 2016 02:05:20 -0700 Subject: [PATCH 100/107] libobs: Update to 0.16.1 --- libobs/obs-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index 28fa7e0cee91fd..bc63dbd4249068 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 0 +#define LIBOBS_API_PATCH_VER 1 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | \ From a8020b37be022ce97d50828bb8f5f9b9bc943ef4 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Wed, 28 Sep 2016 15:21:07 -0700 Subject: [PATCH 101/107] obs-ffmpeg: Fix possible NVENC crash If the codec hasn't even been fully initialized, calling these functions in the shutdown code could cause a crash. --- plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c index 4b693c02caacf7..520c6e80dfa731 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-nvenc.c @@ -54,6 +54,7 @@ struct nvenc_encoder { int height; bool first_packet; + bool initialized; }; static const char *nvenc_getname(void *unused) @@ -113,6 +114,8 @@ static bool nvenc_init_codec(struct nvenc_encoder *enc) return false; } + enc->initialized = true; + *((AVPicture*)enc->vframe) = enc->dst_picture; return true; } @@ -233,15 +236,19 @@ static bool nvenc_update(void *data, obs_data_t *settings) static void nvenc_destroy(void *data) { struct nvenc_encoder *enc = data; - AVPacket pkt = {0}; - int r_pkt = 1; - while (r_pkt) { - if (avcodec_encode_video2(enc->context, &pkt, NULL, &r_pkt) < 0) - break; + if (enc->initialized) { + AVPacket pkt = {0}; + int r_pkt = 1; + + while (r_pkt) { + if (avcodec_encode_video2(enc->context, &pkt, NULL, + &r_pkt) < 0) + break; - if (r_pkt) - av_free_packet(&pkt); + if (r_pkt) + av_free_packet(&pkt); + } } avcodec_close(enc->context); From 48f0c73f82a79203c82fa4543e59ae1fb7299d74 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Wed, 28 Sep 2016 22:26:28 -0700 Subject: [PATCH 102/107] UI: Use rect intersection test for validating position Instead of checking to see if the window's position is valid, check to see if the rectangles of the window and the monitor intersect via a rectangle intersection test. --- UI/obs-app.cpp | 12 +++++++----- UI/obs-app.hpp | 2 +- UI/window-basic-main.cpp | 4 +--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index eba032ae9d0977..e7bb8f64313504 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1518,17 +1518,19 @@ bool GetClosestUnusedFileName(std::string &path, const char *extension) return true; } -bool WindowPositionValid(int x, int y) +bool WindowPositionValid(QRect rect) { vector monitors; GetMonitors(monitors); for (auto &monitor : monitors) { - int br_x = monitor.x + monitor.cx; - int br_y = monitor.y + monitor.cy; + int left = int(monitor.x); + int top = int(monitor.y); + int right = left + int(monitor.cx); + int bottom = top + int(monitor.cy); - if (x >= monitor.x && x < br_x && - y >= monitor.y && y < br_y) + if ((rect.left() - right) < 0 && (left - rect.right()) < 0 && + (rect.top() - bottom) < 0 && (top - rect.bottom()) < 0) return true; } diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index 4964f5f9a81fc8..9c45803e0b2c6c 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -166,7 +166,7 @@ inline const char *Str(const char *lookup) {return App()->GetString(lookup);} bool GetFileSafeName(const char *name, std::string &file); bool GetClosestUnusedFileName(std::string &path, const char *extension); -bool WindowPositionValid(int x, int y); +bool WindowPositionValid(QRect rect); static inline int GetProfilePath(char *path, size_t size, const char *file) { diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index c6f2e894b1a1fd..e7754615cf614b 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -137,9 +137,7 @@ OBSBasic::OBSBasic(QWidget *parent) restoreGeometry(byteArray); QRect windowGeometry = normalGeometry(); - int posx = windowGeometry.x(); - int posy = windowGeometry.y(); - if (!WindowPositionValid(posx, posy)) { + if (!WindowPositionValid(windowGeometry)) { QRect rect = App()->desktop()->availableGeometry(); setGeometry(QStyle::alignedRect( Qt::LeftToRight, From 832c62e026acda24813e7ac9ff0ee41de0dcd927 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Thu, 29 Sep 2016 19:59:45 +0200 Subject: [PATCH 103/107] enc-amf: Update submodule to 1.3.1.0 --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index ae256c24648133..2895132644a1f7 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit ae256c246481335ed727a81802c195c2bffe289b +Subproject commit 2895132644a1f7dc69ce768a784269d608ca0c8c From 4bafe668eeba012d698759feb954cffc865e2cee Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Thu, 29 Sep 2016 18:58:08 +0200 Subject: [PATCH 104/107] obs-text: Add gradient feature Closes jp9000/obs-studio#636 --- plugins/obs-text/data/locale/en-US.ini | 4 ++ plugins/obs-text/gdiplus/obs-text.cpp | 54 +++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/plugins/obs-text/data/locale/en-US.ini b/plugins/obs-text/data/locale/en-US.ini index a1ada083a19964..d048ce794fb14a 100644 --- a/plugins/obs-text/data/locale/en-US.ini +++ b/plugins/obs-text/data/locale/en-US.ini @@ -7,6 +7,10 @@ Filter.TextFiles="Text Files" Filter.AllFiles="All Files" Color="Color" Opacity="Opacity" +Gradient="Gradient" +Gradient.Color="Gradient Color" +Gradient.Opacity="Gradient Opacity" +Gradient.Direction="Gradient Direction" BkColor="Background Color" BkOpacity="Background Opacity" Alignment="Alignment" diff --git a/plugins/obs-text/gdiplus/obs-text.cpp b/plugins/obs-text/gdiplus/obs-text.cpp index 771db239a7af19..2878b0841d1fac 100644 --- a/plugins/obs-text/gdiplus/obs-text.cpp +++ b/plugins/obs-text/gdiplus/obs-text.cpp @@ -42,6 +42,10 @@ using namespace Gdiplus; #define S_FILE "file" #define S_TEXT "text" #define S_COLOR "color" +#define S_GRADIENT "gradient" +#define S_GRADIENT_COLOR "gradient_color" +#define S_GRADIENT_DIR "gradient_dir" +#define S_GRADIENT_OPACITY "gradient_opacity" #define S_ALIGN "align" #define S_VALIGN "valign" #define S_OPACITY "opacity" @@ -73,6 +77,10 @@ using namespace Gdiplus; #define T_FILE T_("TextFile") #define T_TEXT T_("Text") #define T_COLOR T_("Color") +#define T_GRADIENT T_("Gradient") +#define T_GRADIENT_COLOR T_("Gradient.Color") +#define T_GRADIENT_DIR T_("Gradient.Direction") +#define T_GRADIENT_OPACITY T_("Gradient.Opacity") #define T_ALIGN T_("Alignment") #define T_VALIGN T_("VerticalAlignment") #define T_OPACITY T_("Opacity") @@ -195,11 +203,15 @@ struct TextSource { wstring face; int face_size = 0; uint32_t color = 0xFFFFFF; + uint32_t color2 = 0xFFFFFF; + float gradient_dir = 0; uint32_t opacity = 100; + uint32_t opacity2 = 100; uint32_t bk_color = 0; uint32_t bk_opacity = 0; Align align = Align::Left; VAlign valign = VAlign::Top; + bool gradient = false; bool bold = false; bool italic = false; bool underline = false; @@ -515,7 +527,10 @@ void TextSource::RenderText() bits.get()); Graphics graphics_bitmap(&bitmap); - SolidBrush brush(Color(get_alpha_val(opacity) | (color & 0xFFFFFF))); + LinearGradientBrush brush(RectF(0, 0, (float)size.cx, (float)size.cy), + Color(calc_color(color, opacity)), + Color(calc_color(color2, opacity2)), + gradient_dir, 1); DWORD full_bk_color = bk_color & 0xFFFFFF; if (!text.empty() || use_extents) @@ -625,6 +640,10 @@ inline void TextSource::Update(obs_data_t *s) const char *valign_str = obs_data_get_string(s, S_VALIGN); uint32_t new_color = obs_data_get_uint32(s, S_COLOR); uint32_t new_opacity = obs_data_get_uint32(s, S_OPACITY); + bool gradient = obs_data_get_bool(s, S_GRADIENT); + uint32_t new_color2 = obs_data_get_uint32(s, S_GRADIENT_COLOR); + uint32_t new_opacity2 = obs_data_get_uint32(s, S_GRADIENT_OPACITY); + float new_grad_dir = (float)obs_data_get_double(s, S_GRADIENT_DIR); bool new_vertical = obs_data_get_bool(s, S_VERTICAL); bool new_outline = obs_data_get_bool(s, S_OUTLINE); uint32_t new_o_color = obs_data_get_uint32(s, S_OUTLINE_COLOR); @@ -674,11 +693,15 @@ inline void TextSource::Update(obs_data_t *s) /* ----------------------------- */ new_color = rgb_to_bgr(new_color); + new_color2 = rgb_to_bgr(new_color2); new_o_color = rgb_to_bgr(new_o_color); new_bk_color = rgb_to_bgr(new_bk_color); color = new_color; opacity = new_opacity; + color2 = new_color2; + opacity2 = new_opacity2; + gradient_dir = new_grad_dir; vertical = new_vertical; bk_color = new_bk_color; @@ -688,6 +711,11 @@ inline void TextSource::Update(obs_data_t *s) extents_cx = n_extents_cx; extents_cy = n_extents_cy; + if (!gradient) { + color2 = color; + opacity2 = opacity; + } + read_from_file = new_use_file; chatlog_mode = new_chat_mode; @@ -807,6 +835,17 @@ static bool chatlog_mode_changed(obs_properties_t *props, obs_property_t *p, return true; } +static bool gradient_changed(obs_properties_t *props, obs_property_t *p, + obs_data_t *s) +{ + bool gradient = obs_data_get_bool(s, S_GRADIENT); + + set_vis(gradient, S_GRADIENT_COLOR, true); + set_vis(gradient, S_GRADIENT_OPACITY, true); + set_vis(gradient, S_GRADIENT_DIR, true); + return true; +} + static bool extents_modified(obs_properties_t *props, obs_property_t *p, obs_data_t *s) { @@ -855,8 +894,16 @@ static obs_properties_t *get_properties(void *data) obs_properties_add_bool(props, S_VERTICAL, T_VERTICAL); obs_properties_add_color(props, S_COLOR, T_COLOR); - obs_properties_add_int_slider(props, S_OPACITY, T_OPACITY, 0, 100, 1); + + p = obs_properties_add_bool(props, S_GRADIENT, T_GRADIENT); + obs_property_set_modified_callback(p, gradient_changed); + + obs_properties_add_color(props, S_GRADIENT_COLOR, T_GRADIENT_COLOR); + obs_properties_add_int_slider(props, S_GRADIENT_OPACITY, + T_GRADIENT_OPACITY, 0, 100, 1); + obs_properties_add_float_slider(props, S_GRADIENT_DIR, + T_GRADIENT_DIR, 0, 360, 0.1); obs_properties_add_color(props, S_BKCOLOR, T_BKCOLOR); obs_properties_add_int_slider(props, S_BKOPACITY, T_BKOPACITY, @@ -937,6 +984,9 @@ bool obs_module_load(void) obs_data_set_default_string(settings, S_VALIGN, S_VALIGN_TOP); obs_data_set_default_int(settings, S_COLOR, 0xFFFFFF); obs_data_set_default_int(settings, S_OPACITY, 100); + obs_data_set_default_int(settings, S_GRADIENT_COLOR, 0xFFFFFF); + obs_data_set_default_int(settings, S_GRADIENT_OPACITY, 100); + obs_data_set_default_double(settings, S_GRADIENT_DIR, 90.0); obs_data_set_default_int(settings, S_BKCOLOR, 0x000000); obs_data_set_default_int(settings, S_BKOPACITY, 0); obs_data_set_default_int(settings, S_OUTLINE_SIZE, 2); From 846db36d6e130e189641d1c9ca50c66e88d03a94 Mon Sep 17 00:00:00 2001 From: Michael Fabian Dirks Date: Thu, 29 Sep 2016 21:20:01 +0200 Subject: [PATCH 105/107] enc-amf: Fix warnings caused by warnings( push/pop ) --- plugins/enc-amf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/enc-amf b/plugins/enc-amf index 2895132644a1f7..cfe33a10879252 160000 --- a/plugins/enc-amf +++ b/plugins/enc-amf @@ -1 +1 @@ -Subproject commit 2895132644a1f7dc69ce768a784269d608ca0c8c +Subproject commit cfe33a10879252cb7af61147ff909c636a3448ba From 580cfc1a95a6bb2b4ea6f002c95c630ae4542ed7 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Thu, 29 Sep 2016 12:19:06 -0700 Subject: [PATCH 106/107] libobs: Update to 0.16.2 --- libobs/obs-config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libobs/obs-config.h b/libobs/obs-config.h index bc63dbd4249068..3636e76b0441b7 100644 --- a/libobs/obs-config.h +++ b/libobs/obs-config.h @@ -41,7 +41,7 @@ * * Reset to zero each major or minor version */ -#define LIBOBS_API_PATCH_VER 1 +#define LIBOBS_API_PATCH_VER 2 #define MAKE_SEMANTIC_VERSION(major, minor, patch) \ ((major << 24) | \ From 91b64657c6822ffba07cdb3e1bc1e63787bd88b3 Mon Sep 17 00:00:00 2001 From: Ryan Foster Date: Sat, 1 Oct 2016 05:07:21 -0400 Subject: [PATCH 107/107] frontend-tools: Fix crash when adding invalid regex When adding a new regex rule to the Automatic Scene Switcher, OBS would crash if the regex was invalid. This was because the regex was added to the Regex object without a test or try-catch. This commit fixes that behavior, and adds a warning message when attempting to add an invalid regex. --- .../frontend-tools/auto-scene-switcher.cpp | 21 ++++++++++++------- .../frontend-tools/data/locale/en-US.ini | 2 ++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp index 9c737bf4a7b014..999d3525c36b15 100644 --- a/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp +++ b/UI/frontend-plugins/frontend-tools/auto-scene-switcher.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "auto-scene-switcher.hpp" @@ -231,13 +232,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 lock(switcher->m); - switcher->switches.emplace_back(source, - windowName.toUtf8().constData()); + try { + lock_guard 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); diff --git a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini index 63a4673f090713..154241d4438ec5 100644 --- a/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini +++ b/UI/frontend-plugins/frontend-tools/data/locale/en-US.ini @@ -4,6 +4,8 @@ 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"