diff --git a/.github/scripts/utils.py/check-services.py b/.github/scripts/utils.py/check-services.py index 4638411c3..401bb0261 100644 --- a/.github/scripts/utils.py/check-services.py +++ b/.github/scripts/utils.py/check-services.py @@ -14,7 +14,7 @@ from collections import defaultdict MINIMUM_PURGE_AGE = 9.75 * 24 * 60 * 60 # slightly less than 10 days TIMEOUT = 10 -SKIPPED_SERVICES = {"YouNow", "SHOWROOM", "Dacast"} +SKIPPED_SERVICES = {"SHOWROOM", "Dacast"} SERVICES_FILE = "plugins/rtmp-services/data/services.json" PACKAGE_FILE = "plugins/rtmp-services/data/package.json" CACHE_FILE = "other/timestamps.json" diff --git a/.gitmodules b/.gitmodules index 4599972c5..7f21ac7ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,6 @@ [submodule "plugins/obs-browser"] path = plugins/obs-browser url = https://github.com/obsproject/obs-browser.git -[submodule "plugins/obs-outputs/ftl-sdk"] - path = plugins/obs-outputs/ftl-sdk - url = https://github.com/Mixer/ftl-sdk.git [submodule "plugins/obs-websocket"] path = plugins/obs-websocket url = https://github.com/obsproject/obs-websocket.git diff --git a/UI/window-basic-auto-config-test.cpp b/UI/window-basic-auto-config-test.cpp index 117a8bc6d..b242e1d94 100644 --- a/UI/window-basic-auto-config-test.cpp +++ b/UI/window-basic-auto-config-test.cpp @@ -228,9 +228,6 @@ void AutoConfigTestPage::TestBandwidthThread() wiz->serviceName == "Restream.io - RTMP") { string_depad_key(key); key += "?test=true"; - } else if (wiz->serviceName == "Restream.io - FTL") { - string_depad_key(key); - key += "?test"; } obs_data_set_string(service_settings, "service", diff --git a/UI/window-basic-main-outputs.cpp b/UI/window-basic-main-outputs.cpp index d77d31a69..033c5f3ca 100644 --- a/UI/window-basic-main-outputs.cpp +++ b/UI/window-basic-main-outputs.cpp @@ -20,7 +20,6 @@ volatile bool recording_paused = false; volatile bool replaybuf_active = false; volatile bool virtualcam_active = false; -#define FTL_PROTOCOL "ftl" #define RTMP_PROTOCOL "rtmp" #define SRT_PROTOCOL "srt" #define RIST_PROTOCOL "rist" diff --git a/UI/window-basic-main.cpp b/UI/window-basic-main.cpp index 94ba8633e..0acaa41f1 100644 --- a/UI/window-basic-main.cpp +++ b/UI/window-basic-main.cpp @@ -1499,9 +1499,8 @@ bool OBSBasic::LoadService() if (!service) return false; - /* Enforce Opus on FTL if needed */ - if (strcmp(obs_service_get_protocol(service), "FTL") == 0 || - strcmp(obs_service_get_protocol(service), "WHIP") == 0) { + /* Enforce Opus on WHIP if needed */ + if (strcmp(obs_service_get_protocol(service), "WHIP") == 0) { const char *option = config_get_string( basicConfig, "SimpleOutput", "StreamAudioEncoder"); if (strcmp(option, "opus") != 0) diff --git a/UI/window-basic-settings-stream.cpp b/UI/window-basic-settings-stream.cpp index 907a53222..0c34fdac8 100644 --- a/UI/window-basic-settings-stream.cpp +++ b/UI/window-basic-settings-stream.cpp @@ -746,9 +746,6 @@ QString OBSBasicSettings::FindProtocol() server.startsWith("rtmps://")) return QString("RTMPS"); - if (server.startsWith("ftl://")) - return QString("FTL"); - if (server.startsWith("srt://")) return QString("SRT"); diff --git a/build-aux/.run-format.zsh b/build-aux/.run-format.zsh index 4a0fc2173..6c54433c1 100755 --- a/build-aux/.run-format.zsh +++ b/build-aux/.run-format.zsh @@ -56,7 +56,7 @@ invoke_formatter() { if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared)/**/*.(c|cpp|h|hpp|m|mm)(.N)) - source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|obs-outputs/ftl-sdk|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#*/(obs-websocket/deps|decklink/*/decklink-sdk|mac-syphon/syphon-framework|win-dshow/libdshowcapture)/*}) local -a format_args=(-style=file -fallback-style=none) if (( _loglevel > 2 )) format_args+=(--verbose) @@ -77,7 +77,7 @@ invoke_formatter() { if (( ! #source_files )) source_files=((libobs|libobs-*|UI|plugins|deps|shared|cmake)/**/(CMakeLists.txt|*.cmake)(.N)) - source_files=(${source_files:#*/(obs-outputs/ftl-sdk|jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) + source_files=(${source_files:#*/(jansson|decklink/*/decklink-sdk|obs-websocket|obs-browser|win-dshow/libdshowcapture)/*}) local -a format_args=() if (( _loglevel > 2 )) format_args+=(--log-level debug) diff --git a/plugins/obs-outputs/CMakeLists.txt b/plugins/obs-outputs/CMakeLists.txt index 1e0a2fe41..da5300799 100644 --- a/plugins/obs-outputs/CMakeLists.txt +++ b/plugins/obs-outputs/CMakeLists.txt @@ -87,8 +87,6 @@ if(OS_WINDOWS) target_sources(obs-outputs PRIVATE obs-outputs.rc) endif() -include(cmake/ftl.cmake) - # cmake-format: off set_target_properties_obs(obs-outputs PROPERTIES FOLDER plugins/obs-outputs PREFIX "") # cmake-format: on diff --git a/plugins/obs-outputs/cmake/ftl.cmake b/plugins/obs-outputs/cmake/ftl.cmake deleted file mode 100644 index cdbeb948b..000000000 --- a/plugins/obs-outputs/cmake/ftl.cmake +++ /dev/null @@ -1,65 +0,0 @@ -find_package(CURL REQUIRED) -find_package(jansson REQUIRED) - -add_library(ftl-sdk OBJECT) -add_library(OBS::ftl-sdk ALIAS ftl-sdk) - -target_compile_definitions(ftl-sdk PUBLIC FTL_FOUND FTL_STATIC_COMPILE) - -target_link_libraries(ftl-sdk PRIVATE jansson::jansson CURL::libcurl) - -target_sources( - ftl-sdk - PRIVATE # cmake-format: sortable - $<$>:ftl-sdk/libftl/posix/socket.c> - $<$>:ftl-sdk/libftl/posix/threads.c> - $<$>:ftl-sdk/libftl/posix/threads.h> - $<$:ftl-sdk/libftl/win32/socket.c> - $<$:ftl-sdk/libftl/win32/threads.c> - $<$:ftl-sdk/libftl/win32/threads.h> - ftl-sdk/libftl/ftl-sdk.c - ftl-sdk/libftl/ftl_helpers.c - ftl-sdk/libftl/ftl_private.h - ftl-sdk/libftl/gettimeofday/gettimeofday.c - ftl-sdk/libftl/gettimeofday/gettimeofday.h - ftl-sdk/libftl/handshake.c - ftl-sdk/libftl/hmac/hmac.c - ftl-sdk/libftl/hmac/hmac.h - ftl-sdk/libftl/hmac/sha2.c - ftl-sdk/libftl/hmac/sha2.h - ftl-sdk/libftl/ingest.c - ftl-sdk/libftl/logging.c - ftl-sdk/libftl/media.c - PUBLIC ftl-sdk/libftl/ftl.h) - -target_compile_options( - ftl-sdk - PRIVATE $<$:-Wno-unused-parameter> - $<$:-Wno-unused-variable> - $<$:-Wno-sign-compare> - $<$:-Wno-pointer-sign> - $<$:-Wno-int-conversion> - $<$:-Wno-incompatible-function-pointer-types> - $<$:-Wno-implicit-int-conversion> - $<$:-Wno-shorten-64-to-32> - $<$:-Wno-macro-redefined> - $<$:-Wno-enum-conversion> - $<$:-Wno-extra> - $<$:-Wno-incompatible-pointer-types> - $<$:-Wno-builtin-macro-redefined>) - -target_include_directories( - ftl-sdk - PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl" - "$<$:${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/win32>" - "$<$>:${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/posix>") - -if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10) - target_compile_options(ftl-sdk PRIVATE -Wno-error=enum-conversion -Wno-error=maybe-uninitialized) -endif() - -target_sources(obs-outputs PRIVATE ftl-stream.c) -target_link_libraries(obs-outputs PRIVATE ftl-sdk) -target_enable_feature(obs-outputs "FTL protocol support") - -set_target_properties(ftl-sdk PROPERTIES FOLDER plugins/obs-outputs POSITION_INDEPENDENT_CODE TRUE) diff --git a/plugins/obs-outputs/cmake/legacy.cmake b/plugins/obs-outputs/cmake/legacy.cmake index bc2607ec1..fd16b035b 100644 --- a/plugins/obs-outputs/cmake/legacy.cmake +++ b/plugins/obs-outputs/cmake/legacy.cmake @@ -118,85 +118,4 @@ else() target_compile_definitions(obs-outputs PRIVATE NO_CRYPTO) endif() -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(FTL QUIET libftl) -endif() - -if(FTL_FOUND) - find_package(CURL REQUIRED) - obs_status(ENABLED "ftl outputs (system ftl-sdk)") - - target_sources(obs-outputs PRIVATE ftl-stream.c) - - target_include_directories(obs-outputs PRIVATE ${FTL_INCLUDE_DIRS}) - - target_link_libraries(obs-outputs PRIVATE ${FTL_LIBRARIES} CURL::libcurl) - - target_compile_features(obs-outputs PRIVATE c_std_11) - - target_compile_definitions(obs-outputs PRIVATE FTL_FOUND) - -elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/CMakeLists.txt") - find_package(CURL REQUIRED) - find_package(Jansson 2.5 REQUIRED) - obs_status(ENABLED "ftl ouputs (bundled ftl-sdk)") - - target_compile_definitions(obs-outputs PRIVATE FTL_STATIC_COMPILE) - - target_compile_features(obs-outputs PRIVATE c_std_11) - - target_link_libraries(obs-outputs PRIVATE Jansson::Jansson CURL::libcurl) - - target_sources( - obs-outputs - PRIVATE ftl-stream.c - ftl-sdk/libftl/ftl.h - ftl-sdk/libftl/ftl_private.h - ftl-sdk/libftl/hmac/hmac.c - ftl-sdk/libftl/hmac/hmac.h - ftl-sdk/libftl/hmac/sha2.c - ftl-sdk/libftl/hmac/sha2.h - ftl-sdk/libftl/ftl-sdk.c - ftl-sdk/libftl/handshake.c - ftl-sdk/libftl/ingest.c - ftl-sdk/libftl/ftl_helpers.c - ftl-sdk/libftl/media.c - ftl-sdk/libftl/gettimeofday/gettimeofday.c - ftl-sdk/libftl/logging.c) - - target_include_directories(obs-outputs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl) - - if(OS_WINDOWS) - target_sources( - obs-outputs PRIVATE ftl-sdk/libftl/gettimeofday/gettimeofday.c ftl-sdk/libftl/gettimeofday/gettimeofday.h - ftl-sdk/libftl/win32/socket.c ftl-sdk/libftl/win32/threads.c ftl-sdk/libftl/win32/threads.h) - - target_include_directories(obs-outputs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/win32) - elseif(OS_POSIX) - target_sources(obs-outputs PRIVATE ftl-sdk/libftl/posix/socket.c ftl-sdk/libftl/posix/threads.c - ftl-sdk/libftl/posix/threads.h) - - target_include_directories(obs-outputs PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ftl-sdk/libftl/posix) - endif() - - if(NOT MSVC) - target_compile_options( - obs-outputs - PRIVATE -Wno-error=extra - -Wno-error=sign-compare - -Wno-error=incompatible-pointer-types - -Wno-error=int-conversion - -Wno-error=unused-parameter - -Wno-error=deprecated-declarations - "$<$:-Wno-error=maybe-uninitialized>" - "$<$:-Wno-error=pointer-sign>") - if((NOT CMAKE_C_COMPILER_ID STREQUAL "GNU") OR CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "10") - target_compile_options(obs-outputs PRIVATE -Wno-error=enum-conversion) - endif() - endif() - - target_compile_definitions(obs-outputs PRIVATE FTL_FOUND) -endif() - setup_plugin_target(obs-outputs) diff --git a/plugins/obs-outputs/data/locale/en-US.ini b/plugins/obs-outputs/data/locale/en-US.ini index aabfd7945..5eded159f 100644 --- a/plugins/obs-outputs/data/locale/en-US.ini +++ b/plugins/obs-outputs/data/locale/en-US.ini @@ -27,6 +27,3 @@ AddressNotAvailable="Address not available. You may have tried to bind to an inv SSLCertVerifyFailed="The RTMP server sent an invalid SSL certificate." InvalidParameter="Invalid connection parameters. Check that the streaming service address is correct." NoRoute="Error reaching host. Make sure that the interface you have bound can access the internet and that the streaming service supports the address family you selected (see Settings → Advanced)." - -FTLStream="FTL Stream" -FTLStream.PeakBitrate="Peak Bitrate" diff --git a/plugins/obs-outputs/ftl-sdk b/plugins/obs-outputs/ftl-sdk deleted file mode 160000 index d0c8469f6..000000000 --- a/plugins/obs-outputs/ftl-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d0c8469f66806b5ea738d607f7d2b000af8b1129 diff --git a/plugins/obs-outputs/ftl-stream.c b/plugins/obs-outputs/ftl-stream.c deleted file mode 100644 index ce628697f..000000000 --- a/plugins/obs-outputs/ftl-stream.c +++ /dev/null @@ -1,1167 +0,0 @@ -/****************************************************************************** - Copyright (C) 2017 by Quinn Damerell - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include "ftl.h" -#include "flv-mux.h" -#include "net-if.h" - -#ifdef _WIN32 -#include -#else -#include -#define INFINITE 0xFFFFFFFF -#endif - -#define do_log(level, format, ...) \ - blog(level, "[ftl stream: '%s'] " format, \ - obs_output_get_name(stream->output), ##__VA_ARGS__) - -#define warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__) -#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__) -#define debug(format, ...) do_log(LOG_DEBUG, format, ##__VA_ARGS__) - -#define OPT_DROP_THRESHOLD "drop_threshold_ms" -#define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec" -#define OPT_BIND_IP "bind_ip" - -#define FTL_URL_PROTOCOL "ftl://" - -typedef struct _nalu_t { - int len; - int dts_usec; - int send_marker_bit; - uint8_t *data; -} nalu_t; - -typedef struct _frame_of_nalus_t { - nalu_t nalus[100]; - int total; - int complete_frame; -} frame_of_nalus_t; - -struct ftl_stream { - obs_output_t *output; - - pthread_mutex_t packets_mutex; - struct deque packets; - bool sent_headers; - int64_t frames_sent; - - volatile bool connecting; - pthread_t connect_thread; - pthread_t status_thread; - - volatile bool active; - volatile bool disconnected; - volatile bool encode_error; - pthread_t send_thread; - - int max_shutdown_time_sec; - - os_sem_t *send_sem; - os_event_t *stop_event; - uint64_t stop_ts; - uint64_t shutdown_timeout_ts; - - struct dstr path; - uint32_t channel_id; - struct dstr username, password; - struct dstr encoder_name; - struct dstr bind_ip; - - /* frame drop variables */ - int64_t drop_threshold_usec; - int64_t pframe_drop_threshold_usec; - int min_priority; - float congestion; - - int64_t last_dts_usec; - - uint64_t total_bytes_sent; - uint64_t dropped_frames; - uint64_t last_nack_count; - - ftl_handle_t ftl_handle; - ftl_ingest_params_t params; - int peak_kbps; - uint32_t scale_width, scale_height, width, height; - frame_of_nalus_t coded_pic_buffer; -}; - -static int init_connect(struct ftl_stream *stream); -static void *connect_thread(void *data); -static void *status_thread(void *data); -static int _ftl_error_to_obs_error(int status); - -static const char *ftl_stream_getname(void *unused) -{ - UNUSED_PARAMETER(unused); - return obs_module_text("FTLStream"); -} - -static inline size_t num_buffered_packets(struct ftl_stream *stream); - -static inline void free_packets(struct ftl_stream *stream) -{ - size_t num_packets; - - pthread_mutex_lock(&stream->packets_mutex); - - num_packets = num_buffered_packets(stream); - if (num_packets) - info("Freeing %d remaining packets", (int)num_packets); - - while (stream->packets.size) { - struct encoder_packet packet; - deque_pop_front(&stream->packets, &packet, sizeof(packet)); - obs_encoder_packet_release(&packet); - } - pthread_mutex_unlock(&stream->packets_mutex); -} - -static inline bool stopping(struct ftl_stream *stream) -{ - return os_event_try(stream->stop_event) != EAGAIN; -} - -static inline bool connecting(struct ftl_stream *stream) -{ - return os_atomic_load_bool(&stream->connecting); -} - -static inline bool active(struct ftl_stream *stream) -{ - return os_atomic_load_bool(&stream->active); -} - -static inline bool disconnected(struct ftl_stream *stream) -{ - return os_atomic_load_bool(&stream->disconnected); -} - -static void ftl_stream_destroy(void *data) -{ - struct ftl_stream *stream = data; - ftl_status_t status_code; - - info("ftl_stream_destroy"); - - if (stopping(stream) && !connecting(stream)) { - pthread_join(stream->send_thread, NULL); - - } else if (connecting(stream) || active(stream)) { - if (stream->connecting) { - info("wait for connect_thread to terminate"); - pthread_join(stream->status_thread, NULL); - pthread_join(stream->connect_thread, NULL); - info("wait for connect_thread to terminate: done"); - } - - stream->stop_ts = 0; - os_event_signal(stream->stop_event); - - if (active(stream)) { - os_sem_post(stream->send_sem); - obs_output_end_data_capture(stream->output); - pthread_join(stream->send_thread, NULL); - } - } - - info("ingest destroy"); - - status_code = ftl_ingest_destroy(&stream->ftl_handle); - if (status_code != FTL_SUCCESS) { - info("Failed to destroy from ingest %d", status_code); - } - - if (stream) { - free_packets(stream); - dstr_free(&stream->path); - dstr_free(&stream->username); - dstr_free(&stream->password); - dstr_free(&stream->encoder_name); - dstr_free(&stream->bind_ip); - os_event_destroy(stream->stop_event); - os_sem_destroy(stream->send_sem); - pthread_mutex_destroy(&stream->packets_mutex); - deque_free(&stream->packets); - bfree(stream); - } -} - -static void *ftl_stream_create(obs_data_t *settings, obs_output_t *output) -{ - struct ftl_stream *stream = bzalloc(sizeof(struct ftl_stream)); - info("ftl_stream_create"); - - stream->output = output; - pthread_mutex_init_value(&stream->packets_mutex); - - stream->peak_kbps = -1; - ftl_init(); - - if (pthread_mutex_init(&stream->packets_mutex, NULL) != 0) { - goto fail; - } - if (os_event_init(&stream->stop_event, OS_EVENT_TYPE_MANUAL) != 0) { - goto fail; - } - - stream->coded_pic_buffer.total = 0; - stream->coded_pic_buffer.complete_frame = 0; - - UNUSED_PARAMETER(settings); - return stream; - -fail: - return NULL; -} - -static void ftl_stream_stop(void *data, uint64_t ts) -{ - struct ftl_stream *stream = data; - info("ftl_stream_stop"); - - if (stopping(stream) && ts != 0) { - return; - } - - if (connecting(stream)) { - pthread_join(stream->status_thread, NULL); - pthread_join(stream->connect_thread, NULL); - } - - stream->stop_ts = ts / 1000ULL; - - if (ts) { - stream->shutdown_timeout_ts = - ts + - (uint64_t)stream->max_shutdown_time_sec * 1000000000ULL; - } - - if (active(stream)) { - os_event_signal(stream->stop_event); - if (stream->stop_ts == 0) - os_sem_post(stream->send_sem); - } else { - obs_output_signal_stop(stream->output, OBS_OUTPUT_SUCCESS); - } -} - -static inline bool get_next_packet(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - bool new_packet = false; - - pthread_mutex_lock(&stream->packets_mutex); - if (stream->packets.size) { - deque_pop_front(&stream->packets, packet, - sizeof(struct encoder_packet)); - new_packet = true; - } - pthread_mutex_unlock(&stream->packets_mutex); - - return new_packet; -} - -static int avc_get_video_frame(struct ftl_stream *stream, - struct encoder_packet *packet, bool is_header) -{ - int consumed = 0; - int len = (int)packet->size; - nalu_t *nalu; - - unsigned char *video_stream = packet->data; - - while ((size_t)consumed < packet->size) { - size_t total_max = sizeof(stream->coded_pic_buffer.nalus) / - sizeof(stream->coded_pic_buffer.nalus[0]); - - if ((size_t)stream->coded_pic_buffer.total >= total_max) { - warn("ERROR: cannot continue, nalu buffers are full"); - return -1; - } - - nalu = &stream->coded_pic_buffer - .nalus[stream->coded_pic_buffer.total]; - - if (is_header) { - if (consumed == 0) { - //first 6 bytes are some obs header with part - //of the sps - video_stream += 6; - consumed += 6; - } else { - //another spacer byte of 0x1 - video_stream += 1; - consumed += 1; - } - - len = video_stream[0] << 8 | video_stream[1]; - video_stream += 2; - consumed += 2; - } else { - len = video_stream[0] << 24 | video_stream[1] << 16 | - video_stream[2] << 8 | video_stream[3]; - - if ((size_t)len > (packet->size - (size_t)consumed)) { - warn("ERROR: got len of %d but packet only " - "has %d left", - len, (int)(packet->size - consumed)); - } - - consumed += 4; - video_stream += 4; - } - - consumed += len; - - uint8_t nalu_type = video_stream[0] & 0x1F; - uint8_t nri = (video_stream[0] >> 5) & 0x3; - - if ((nalu_type != 12 && nalu_type != 6 && nalu_type != 9) || - nri) { - nalu->data = video_stream; - nalu->len = len; - nalu->send_marker_bit = 0; - stream->coded_pic_buffer.total++; - } - - video_stream += len; - } - - if (!is_header) { - size_t idx = stream->coded_pic_buffer.total - 1; - stream->coded_pic_buffer.nalus[idx].send_marker_bit = 1; - } - - return 0; -} - -static int send_packet(struct ftl_stream *stream, struct encoder_packet *packet, - bool is_header) -{ - int bytes_sent = 0; - int ret = 0; - - if (packet->type == OBS_ENCODER_VIDEO) { - stream->coded_pic_buffer.total = 0; - avc_get_video_frame(stream, packet, is_header); - - int i; - for (i = 0; i < stream->coded_pic_buffer.total; i++) { - nalu_t *nalu = &stream->coded_pic_buffer.nalus[i]; - bytes_sent += ftl_ingest_send_media_dts( - &stream->ftl_handle, FTL_VIDEO_DATA, - packet->dts_usec, nalu->data, nalu->len, - nalu->send_marker_bit); - - if (nalu->send_marker_bit) { - stream->frames_sent++; - } - } - - } else if (packet->type == OBS_ENCODER_AUDIO) { - bytes_sent += ftl_ingest_send_media_dts( - &stream->ftl_handle, FTL_AUDIO_DATA, packet->dts_usec, - packet->data, (int)packet->size, 0); - } else { - warn("Got packet type %d", packet->type); - } - - if (is_header) { - bfree(packet->data); - } else { - obs_encoder_packet_release(packet); - } - - stream->total_bytes_sent += bytes_sent; - return ret; -} - -static void set_peak_bitrate(struct ftl_stream *stream) -{ - int speedtest_kbps = 15000; - int speedtest_duration = 1000; - speed_test_t results; - ftl_status_t status_code; - - status_code = ftl_ingest_speed_test_ex(&stream->ftl_handle, - speedtest_kbps, - speedtest_duration, &results); - - float percent_lost = 0; - - if (status_code == FTL_SUCCESS) { - percent_lost = (float)results.lost_pkts * 100.f / - (float)results.pkts_sent; - } else { - warn("Speed test failed with: %s", - ftl_status_code_to_string(status_code)); - } - - // Get what the user set the encoding bitrate to. - obs_encoder_t *video_encoder = - obs_output_get_video_encoder(stream->output); - obs_data_t *video_settings = obs_encoder_get_settings(video_encoder); - int user_desired_bitrate = - (int)obs_data_get_int(video_settings, "bitrate"); - obs_data_release(video_settings); - - // Report the results. - info("Speed test completed: User desired bitrate %d, Peak kbps %d, " - "initial rtt %d, " - "final rtt %d, %3.2f lost packets", - user_desired_bitrate, results.peak_kbps, results.starting_rtt, - results.ending_rtt, percent_lost); - - // We still want to set the peak to about 1.2x what the target bitrate is, - // even if the speed test reported it should be lower. If we don't, FTL - // will queue data on the client and start adding latency. If the internet - // connection really can't handle the bitrate the user will see either lost frame - // and recovered frame counts go up, which is reflect in the dropped_frames count. - stream->peak_kbps = stream->params.peak_kbps = - user_desired_bitrate * 12 / 10; - ftl_ingest_update_params(&stream->ftl_handle, &stream->params); -} - -static inline bool send_headers(struct ftl_stream *stream, int64_t dts_usec); - -static inline bool can_shutdown_stream(struct ftl_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 ftl_stream *stream = data; - ftl_status_t status_code; - - os_set_thread_name("ftl-stream: send_thread"); - - while (os_sem_wait(stream->send_sem) == 0) { - struct encoder_packet packet; - - if (stopping(stream) && stream->stop_ts == 0) { - break; - } - - if (!get_next_packet(stream, &packet)) - continue; - - if (stopping(stream)) { - if (can_shutdown_stream(stream, &packet)) { - obs_encoder_packet_release(&packet); - break; - } - } - - /* sends sps/pps on every key frame as this is typically - * required for webrtc */ - if (packet.keyframe) { - if (!send_headers(stream, packet.dts_usec)) { - os_atomic_set_bool(&stream->disconnected, true); - break; - } - } - - if (send_packet(stream, &packet, false) < 0) { - os_atomic_set_bool(&stream->disconnected, true); - break; - } - } - - bool encode_error = os_atomic_load_bool(&stream->encode_error); - - if (disconnected(stream)) { - info("Disconnected from %s", stream->path.array); - } else if (encode_error) { - info("Encoder error, disconnecting"); - } else { - info("User stopped the stream"); - } - - if (!stopping(stream)) { - pthread_detach(stream->send_thread); - obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); - } else if (encode_error) { - obs_output_signal_stop(stream->output, OBS_OUTPUT_ENCODE_ERROR); - } else { - obs_output_end_data_capture(stream->output); - } - - info("ingest disconnect"); - - status_code = ftl_ingest_disconnect(&stream->ftl_handle); - if (status_code != FTL_SUCCESS) { - printf("Failed to disconnect from ingest %d", status_code); - } - - free_packets(stream); - os_event_reset(stream->stop_event); - os_atomic_set_bool(&stream->active, false); - stream->sent_headers = false; - return NULL; -} - -static bool send_video_header(struct ftl_stream *stream, int64_t dts_usec) -{ - obs_output_t *context = stream->output; - obs_encoder_t *vencoder = obs_output_get_video_encoder(context); - uint8_t *header; - size_t size; - - struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO, - .timebase_den = 1, - .keyframe = true, - .dts_usec = dts_usec}; - - if (!obs_encoder_get_extra_data(vencoder, &header, &size)) - return false; - packet.size = obs_parse_avc_header(&packet.data, header, size); - return send_packet(stream, &packet, true) >= 0; -} - -static inline bool send_headers(struct ftl_stream *stream, int64_t dts_usec) -{ - stream->sent_headers = true; - - if (!send_video_header(stream, dts_usec)) - return false; - - return true; -} - -static inline bool reset_semaphore(struct ftl_stream *stream) -{ - os_sem_destroy(stream->send_sem); - return os_sem_init(&stream->send_sem, 0) == 0; -} - -#ifdef _WIN32 -#define socklen_t int -#endif - -static int init_send(struct ftl_stream *stream) -{ - int ret; - - reset_semaphore(stream); - - ret = pthread_create(&stream->send_thread, NULL, send_thread, stream); - if (ret != 0) { - warn("Failed to create send thread"); - return OBS_OUTPUT_ERROR; - } - - os_atomic_set_bool(&stream->active, true); - - obs_output_begin_data_capture(stream->output, 0); - - return OBS_OUTPUT_SUCCESS; -} - -static int try_connect(struct ftl_stream *stream) -{ - ftl_status_t status_code; - - if (dstr_is_empty(&stream->path)) { - warn("URL is empty"); - return OBS_OUTPUT_BAD_PATH; - } - - info("Connecting to FTL Ingest URL %s...", stream->path.array); - - stream->width = (int)obs_output_get_width(stream->output); - stream->height = (int)obs_output_get_height(stream->output); - - status_code = ftl_ingest_connect(&stream->ftl_handle); - if (status_code != FTL_SUCCESS) { - if (status_code == FTL_BAD_OR_INVALID_STREAM_KEY) { - blog(LOG_ERROR, "Invalid Key (%s)", - ftl_status_code_to_string(status_code)); - return OBS_OUTPUT_INVALID_STREAM; - } else { - warn("Ingest connect failed with: %s (%d)", - ftl_status_code_to_string(status_code), - status_code); - return _ftl_error_to_obs_error(status_code); - } - } - - info("Connection to %s successful", stream->path.array); - - // Always get the peak bitrate when we are starting. - set_peak_bitrate(stream); - - pthread_create(&stream->status_thread, NULL, status_thread, stream); - - return init_send(stream); -} - -static bool ftl_stream_start(void *data) -{ - struct ftl_stream *stream = data; - - info("ftl_stream_start"); - - // Mixer doesn't support bframes. So force them off. - obs_encoder_t *video_encoder = - obs_output_get_video_encoder(stream->output); - obs_data_t *video_settings = obs_encoder_get_settings(video_encoder); - obs_data_set_int(video_settings, "bf", 0); - obs_data_release(video_settings); - - if (!obs_output_can_begin_data_capture(stream->output, 0)) { - return false; - } - if (!obs_output_initialize_encoders(stream->output, 0)) { - return false; - } - - stream->frames_sent = 0; - os_atomic_set_bool(&stream->connecting, true); - - return pthread_create(&stream->connect_thread, NULL, connect_thread, - stream) == 0; -} - -static inline bool add_packet(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - deque_push_back(&stream->packets, packet, - sizeof(struct encoder_packet)); - return true; -} - -static inline size_t num_buffered_packets(struct ftl_stream *stream) -{ - return stream->packets.size / sizeof(struct encoder_packet); -} - -static void drop_frames(struct ftl_stream *stream, const char *name, - int highest_priority, bool pframes) -{ - UNUSED_PARAMETER(pframes); - - struct deque new_buf = {0}; - int num_frames_dropped = 0; - -#ifdef _DEBUG - int start_packets = (int)num_buffered_packets(stream); -#else - UNUSED_PARAMETER(name); -#endif - - deque_reserve(&new_buf, sizeof(struct encoder_packet) * 8); - - while (stream->packets.size) { - struct encoder_packet packet; - deque_pop_front(&stream->packets, &packet, sizeof(packet)); - - /* do not drop audio data or video keyframes */ - if (packet.type == OBS_ENCODER_AUDIO || - packet.drop_priority >= highest_priority) { - deque_push_back(&new_buf, &packet, sizeof(packet)); - - } else { - num_frames_dropped++; - obs_encoder_packet_release(&packet); - } - } - - deque_free(&stream->packets); - stream->packets = new_buf; - - if (stream->min_priority < highest_priority) - stream->min_priority = highest_priority; - if (!num_frames_dropped) - return; - - stream->dropped_frames += num_frames_dropped; -#ifdef _DEBUG - debug("Dropped %s, prev packet count: %d, new packet count: %d", name, - start_packets, (int)num_buffered_packets(stream)); -#endif -} - -static bool find_first_video_packet(struct ftl_stream *stream, - struct encoder_packet *first) -{ - size_t count = stream->packets.size / sizeof(*first); - - for (size_t i = 0; i < count; i++) { - struct encoder_packet *cur = - deque_data(&stream->packets, i * sizeof(*first)); - if (cur->type == OBS_ENCODER_VIDEO && !cur->keyframe) { - *first = *cur; - return true; - } - } - - return false; -} - -static void check_to_drop_frames(struct ftl_stream *stream, bool pframes) -{ - struct encoder_packet first; - int64_t buffer_duration_usec; - 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 drop_threshold = pframes ? stream->pframe_drop_threshold_usec - : stream->drop_threshold_usec; - - if (num_packets < 5) { - if (!pframes) - stream->congestion = 0.0f; - return; - } - - if (!find_first_video_packet(stream, &first)) - 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 (!pframes) { - stream->congestion = - (float)buffer_duration_usec / (float)drop_threshold; - } - - if (buffer_duration_usec > drop_threshold) { - debug("buffer_duration_usec: %" PRId64, buffer_duration_usec); - drop_frames(stream, name, priority, pframes); - } -} - -static bool add_video_packet(struct ftl_stream *stream, - struct encoder_packet *packet) -{ - check_to_drop_frames(stream, false); - check_to_drop_frames(stream, true); - - /* if currently dropping frames, drop packets until it reaches the - * desired priority */ - if (packet->priority < stream->min_priority) { - stream->dropped_frames++; - return false; - } else { - stream->min_priority = 0; - } - - stream->last_dts_usec = packet->dts_usec; - return add_packet(stream, packet); -} - -static void ftl_stream_data(void *data, struct encoder_packet *packet) -{ - struct ftl_stream *stream = data; - - struct encoder_packet new_packet; - bool added_packet = false; - - if (disconnected(stream) || !active(stream)) - return; - - /* encoder failure */ - if (!packet) { - os_atomic_set_bool(&stream->encode_error, true); - os_sem_post(stream->send_sem); - return; - } - - if (packet->type == OBS_ENCODER_VIDEO) - obs_parse_avc_packet(&new_packet, packet); - else - obs_encoder_packet_ref(&new_packet, packet); - - pthread_mutex_lock(&stream->packets_mutex); - - if (!disconnected(stream)) { - added_packet = (packet->type == OBS_ENCODER_VIDEO) - ? add_video_packet(stream, &new_packet) - : add_packet(stream, &new_packet); - } - - pthread_mutex_unlock(&stream->packets_mutex); - - if (added_packet) - os_sem_post(stream->send_sem); - else - obs_encoder_packet_release(&new_packet); -} - -static void ftl_stream_defaults(obs_data_t *defaults) -{ - UNUSED_PARAMETER(defaults); -} - -static obs_properties_t *ftl_stream_properties(void *unused) -{ - UNUSED_PARAMETER(unused); - - obs_properties_t *props = obs_properties_create(); - obs_properties_add_int(props, "peak_bitrate_kbps", - obs_module_text("FTLStream.PeakBitrate"), 1000, - 10000, 500); - - return props; -} - -static uint64_t ftl_stream_total_bytes_sent(void *data) -{ - struct ftl_stream *stream = data; - - return stream->total_bytes_sent; -} - -static int ftl_stream_dropped_frames(void *data) -{ - struct ftl_stream *stream = data; - return (int)stream->dropped_frames; -} - -static float ftl_stream_congestion(void *data) -{ - struct ftl_stream *stream = data; - return stream->min_priority > 0 ? 1.0f : stream->congestion; -} - -enum ret_type { - RET_CONTINUE, - RET_BREAK, - RET_EXIT, -}; - -static enum ret_type ftl_event(struct ftl_stream *stream, - ftl_status_msg_t status) -{ - if (status.msg.event.type != FTL_STATUS_EVENT_TYPE_DISCONNECTED) - return RET_CONTINUE; - - info("Disconnected from ingest with reason: %s", - ftl_status_code_to_string(status.msg.event.error_code)); - - if (status.msg.event.reason == FTL_STATUS_EVENT_REASON_API_REQUEST) { - return RET_BREAK; - } - - //tell OBS and it will trigger a reconnection - blog(LOG_WARNING, "Reconnecting to Ingest"); - obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED); - reset_semaphore(stream); - return RET_EXIT; -} - -static void *status_thread(void *data) -{ - struct ftl_stream *stream = data; - - ftl_status_msg_t status; - ftl_status_t status_code; - - while (!disconnected(stream)) { - status_code = ftl_ingest_get_status(&stream->ftl_handle, - &status, 1000); - - if (status_code == FTL_STATUS_TIMEOUT || - status_code == FTL_QUEUE_EMPTY) { - continue; - } else if (status_code == FTL_NOT_INITIALIZED) { - break; - } - - if (status.type == FTL_STATUS_EVENT) { - enum ret_type ret_type = ftl_event(stream, status); - if (ret_type == RET_EXIT) - return NULL; - else if (ret_type == RET_BREAK) - break; - - } else if (status.type == FTL_STATUS_LOG) { - blog(LOG_INFO, "[%d] %s", status.msg.log.log_level, - status.msg.log.string); - - } else if (status.type == FTL_STATUS_VIDEO_PACKETS) { - ftl_packet_stats_msg_t *p = &status.msg.pkt_stats; - - // Report nack requests as dropped frames - stream->dropped_frames += - p->nack_reqs - stream->last_nack_count; - stream->last_nack_count = p->nack_reqs; - - int log_level = p->nack_reqs > 0 ? LOG_INFO : LOG_DEBUG; - - blog(log_level, - "Avg packet send per second %3.1f, " - "total nack requests %d", - (float)p->sent * 1000.f / p->period, - (int)p->nack_reqs); - - } else if (status.type == FTL_STATUS_VIDEO_PACKETS_INSTANT) { - ftl_packet_stats_instant_msg_t *p = - &status.msg.ipkt_stats; - - int log_level = p->avg_rtt > 20 ? LOG_INFO : LOG_DEBUG; - - blog(log_level, - "avg transmit delay %dms " - "(min: %d, max: %d), " - "avg rtt %dms (min: %d, max: %d)", - p->avg_xmit_delay, p->min_xmit_delay, - p->max_xmit_delay, p->avg_rtt, p->min_rtt, - p->max_rtt); - - } else if (status.type == FTL_STATUS_VIDEO) { - ftl_video_frame_stats_msg_t *v = - &status.msg.video_stats; - - int log_level = v->queue_fullness > 0 ? LOG_INFO - : LOG_DEBUG; - - blog(log_level, - "Queue an average of %3.2f fps " - "(%3.1f kbps), " - "sent an average of %3.2f fps " - "(%3.1f kbps), " - "queue fullness %d, " - "max frame size %d", - (float)v->frames_queued * 1000.f / v->period, - (float)v->bytes_queued / v->period * 8, - (float)v->frames_sent * 1000.f / v->period, - (float)v->bytes_sent / v->period * 8, - v->queue_fullness, v->max_frame_size); - } else { - blog(LOG_DEBUG, - "Status: Got Status message of type " - "%d", - status.type); - } - } - - blog(LOG_DEBUG, "status_thread: Exited"); - pthread_detach(stream->status_thread); - return NULL; -} - -static void *connect_thread(void *data) -{ - struct ftl_stream *stream = data; - int ret; - - os_set_thread_name("ftl-stream: connect_thread"); - - blog(LOG_WARNING, "ftl-stream: connect thread"); - - ret = init_connect(stream); - if (ret != OBS_OUTPUT_SUCCESS) { - obs_output_signal_stop(stream->output, ret); - return NULL; - } - - ret = try_connect(stream); - if (ret != OBS_OUTPUT_SUCCESS) { - obs_output_signal_stop(stream->output, ret); - info("Connection to %s failed: %d", stream->path.array, ret); - } - - if (!stopping(stream)) - pthread_detach(stream->connect_thread); - - os_atomic_set_bool(&stream->connecting, false); - return NULL; -} - -static int init_connect(struct ftl_stream *stream) -{ - obs_service_t *service; - obs_data_t *settings; - const char *bind_ip, *key, *ingest_url; - ftl_status_t status_code; - - info("init_connect"); - - if (stopping(stream)) - pthread_join(stream->send_thread, NULL); - - free_packets(stream); - - service = obs_output_get_service(stream->output); - if (!service) { - return OBS_OUTPUT_ERROR; - } - - os_atomic_set_bool(&stream->disconnected, false); - os_atomic_set_bool(&stream->encode_error, false); - stream->total_bytes_sent = 0; - stream->dropped_frames = 0; - stream->min_priority = 0; - - settings = obs_output_get_settings(stream->output); - obs_encoder_t *video_encoder = - obs_output_get_video_encoder(stream->output); - obs_data_t *video_settings = obs_encoder_get_settings(video_encoder); - - ingest_url = obs_service_get_connect_info( - service, OBS_SERVICE_CONNECT_INFO_SERVER_URL); - if (strncmp(ingest_url, FTL_URL_PROTOCOL, strlen(FTL_URL_PROTOCOL)) == - 0) { - dstr_copy(&stream->path, ingest_url + strlen(FTL_URL_PROTOCOL)); - } else { - dstr_copy(&stream->path, ingest_url); - } - - key = obs_service_get_connect_info(service, - OBS_SERVICE_CONNECT_INFO_STREAM_KEY); - - int target_bitrate = (int)obs_data_get_int(video_settings, "bitrate"); - int peak_bitrate = (int)((float)target_bitrate * 1.1f); - - //minimum overshoot tolerance of 10% - if (peak_bitrate < target_bitrate) { - peak_bitrate = target_bitrate; - } - - stream->params.stream_key = (char *)key; - stream->params.video_codec = FTL_VIDEO_H264; - stream->params.audio_codec = FTL_AUDIO_OPUS; - stream->params.ingest_hostname = stream->path.array; - stream->params.vendor_name = "OBS Studio"; - stream->params.vendor_version = obs_get_version_string(); - stream->params.peak_kbps = stream->peak_kbps < 0 ? 0 - : stream->peak_kbps; - - //not required when using ftl_ingest_send_media_dts - stream->params.fps_num = 0; - stream->params.fps_den = 0; - - status_code = ftl_ingest_create(&stream->ftl_handle, &stream->params); - if (status_code != FTL_SUCCESS) { - if (status_code == FTL_BAD_OR_INVALID_STREAM_KEY) { - blog(LOG_ERROR, "Invalid Key (%s)", - ftl_status_code_to_string(status_code)); - return OBS_OUTPUT_INVALID_STREAM; - } else { - blog(LOG_ERROR, "Failed to create ingest handle (%s)", - ftl_status_code_to_string(status_code)); - return OBS_OUTPUT_ERROR; - } - } - - dstr_copy(&stream->username, - obs_service_get_connect_info( - service, OBS_SERVICE_CONNECT_INFO_USERNAME)); - dstr_copy(&stream->password, - obs_service_get_connect_info( - service, OBS_SERVICE_CONNECT_INFO_PASSWORD)); - dstr_depad(&stream->path); - - stream->drop_threshold_usec = - (int64_t)obs_data_get_int(settings, OPT_DROP_THRESHOLD) * 1000; - stream->max_shutdown_time_sec = - (int)obs_data_get_int(settings, OPT_MAX_SHUTDOWN_TIME_SEC); - - bind_ip = obs_data_get_string(settings, OPT_BIND_IP); - dstr_copy(&stream->bind_ip, bind_ip); - - obs_data_release(settings); - obs_data_release(video_settings); - return OBS_OUTPUT_SUCCESS; -} - -// Returns 0 on success -static int _ftl_error_to_obs_error(int status) -{ - /* Map FTL errors to OBS errors */ - - switch (status) { - case FTL_SUCCESS: - return OBS_OUTPUT_SUCCESS; - case FTL_SOCKET_NOT_CONNECTED: - case FTL_MALLOC_FAILURE: - case FTL_INTERNAL_ERROR: - case FTL_CONFIG_ERROR: - case FTL_NOT_ACTIVE_STREAM: - case FTL_NOT_CONNECTED: - case FTL_ALREADY_CONNECTED: - case FTL_STATUS_TIMEOUT: - case FTL_QUEUE_FULL: - case FTL_STATUS_WAITING_FOR_KEY_FRAME: - case FTL_QUEUE_EMPTY: - case FTL_NOT_INITIALIZED: - return OBS_OUTPUT_ERROR; - case FTL_BAD_REQUEST: - case FTL_DNS_FAILURE: - case FTL_CONNECT_ERROR: - case FTL_UNSUPPORTED_MEDIA_TYPE: - case FTL_OLD_VERSION: - case FTL_UNAUTHORIZED: - case FTL_AUDIO_SSRC_COLLISION: - case FTL_VIDEO_SSRC_COLLISION: - case FTL_STREAM_REJECTED: - case FTL_BAD_OR_INVALID_STREAM_KEY: - case FTL_CHANNEL_IN_USE: - case FTL_REGION_UNSUPPORTED: - case FTL_GAME_BLOCKED: - return OBS_OUTPUT_CONNECT_FAILED; - case FTL_NO_MEDIA_TIMEOUT: - return OBS_OUTPUT_DISCONNECTED; - case FTL_USER_DISCONNECT: - return OBS_OUTPUT_SUCCESS; - case FTL_UNKNOWN_ERROR_CODE: - default: - /* Unknown FTL error */ - return OBS_OUTPUT_ERROR; - } -} - -struct obs_output_info ftl_output_info = { - .id = "ftl_output", - .flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE, - .protocols = "FTL", - .encoded_video_codecs = "h264", - .encoded_audio_codecs = "opus", - .get_name = ftl_stream_getname, - .create = ftl_stream_create, - .destroy = ftl_stream_destroy, - .start = ftl_stream_start, - .stop = ftl_stream_stop, - .encoded_packet = ftl_stream_data, - .get_defaults = ftl_stream_defaults, - .get_properties = ftl_stream_properties, - .get_total_bytes = ftl_stream_total_bytes_sent, - .get_congestion = ftl_stream_congestion, - .get_dropped_frames = ftl_stream_dropped_frames, -}; diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c index 23ffa7da6..dde358f3e 100644 --- a/plugins/obs-outputs/obs-outputs.c +++ b/plugins/obs-outputs/obs-outputs.c @@ -9,16 +9,13 @@ OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("obs-outputs", "en-US") MODULE_EXPORT const char *obs_module_description(void) { - return "OBS core RTMP/FLV/null/FTL outputs"; + return "OBS core RTMP/FLV/null outputs"; } extern struct obs_output_info rtmp_output_info; extern struct obs_output_info null_output_info; extern struct obs_output_info flv_output_info; extern struct obs_output_info mp4_output_info; -#if defined(FTL_FOUND) -extern struct obs_output_info ftl_output_info; -#endif #if defined(_WIN32) && defined(MBEDTLS_THREADING_ALT) void mbed_mutex_init(mbedtls_threading_mutex_t *m) @@ -67,9 +64,6 @@ bool obs_module_load(void) obs_register_output(&null_output_info); obs_register_output(&flv_output_info); obs_register_output(&mp4_output_info); -#if defined(FTL_FOUND) - obs_register_output(&ftl_output_info); -#endif return true; } diff --git a/plugins/rtmp-services/CMakeLists.txt b/plugins/rtmp-services/CMakeLists.txt index 9a1c993a9..f30037847 100644 --- a/plugins/rtmp-services/CMakeLists.txt +++ b/plugins/rtmp-services/CMakeLists.txt @@ -32,9 +32,7 @@ target_sources( service-specific/showroom.c service-specific/showroom.h service-specific/twitch.c - service-specific/twitch.h - service-specific/younow.c - service-specific/younow.h) + service-specific/twitch.h) target_compile_definitions(rtmp-services PRIVATE SERVICES_URL="${RTMP_SERVICES_URL}" $<$:ENABLE_SERVICE_UPDATES>) diff --git a/plugins/rtmp-services/cmake/legacy.cmake b/plugins/rtmp-services/cmake/legacy.cmake index 88362cd75..92a925d76 100644 --- a/plugins/rtmp-services/cmake/legacy.cmake +++ b/plugins/rtmp-services/cmake/legacy.cmake @@ -21,8 +21,6 @@ target_sources( rtmp-services PRIVATE service-specific/twitch.c service-specific/twitch.h - service-specific/younow.c - service-specific/younow.h service-specific/nimotv.c service-specific/nimotv.h service-specific/showroom.c diff --git a/plugins/rtmp-services/data/package.json b/plugins/rtmp-services/data/package.json index bdd34a73a..4081f38ea 100644 --- a/plugins/rtmp-services/data/package.json +++ b/plugins/rtmp-services/data/package.json @@ -1,11 +1,11 @@ { "$schema": "schema/package-schema.json", "url": "https://obsproject.com/obs2_update/rtmp-services/v5", - "version": 256, + "version": 257, "files": [ { "name": "services.json", - "version": 256 + "version": 257 } ] } diff --git a/plugins/rtmp-services/data/schema/service-schema-v5.json b/plugins/rtmp-services/data/schema/service-schema-v5.json index 16e078483..93237e089 100644 --- a/plugins/rtmp-services/data/schema/service-schema-v5.json +++ b/plugins/rtmp-services/data/schema/service-schema-v5.json @@ -23,7 +23,6 @@ "RTMP", "RTMPS", "HLS", - "FTL", "SRT", "RIST", "WHIP" @@ -124,7 +123,6 @@ "enum": [ "rtmp_output", "ffmpeg_hls_muxer", - "ftl_output", "ffmpeg_mpegts_muxer" ] }, @@ -235,7 +233,7 @@ }, { "$comment": "Require recommended output field if protocol field is not RTMP(S)", - "if": { "required": ["protocol"], "properties": { "protocol": { "pattern": "^(HLS|SRT|RIST|FTL|WHIP)$" } } }, + "if": { "required": ["protocol"], "properties": { "protocol": { "pattern": "^(HLS|SRT|RIST|WHIP)$" } } }, "then": { "properties": { "recommended": { "required": ["output"] } } } } ] diff --git a/plugins/rtmp-services/data/services.json b/plugins/rtmp-services/data/services.json index 341de1a84..8370b1fa9 100644 --- a/plugins/rtmp-services/data/services.json +++ b/plugins/rtmp-services/data/services.json @@ -1600,25 +1600,6 @@ "h264" ] }, - { - "name": "YouNow", - "common": false, - "protocol": "FTL", - "servers": [ - { - "name": "younow.com", - "url": "https://api.younow.com/php/api/broadcast/ingest?id=" - } - ], - "recommended": { - "keyint": 2, - "output": "ftl_output", - "max audio bitrate": 160, - "max video bitrate": 7000, - "profile": "main", - "bframes": 0 - } - }, { "name": "Steam", "common": false, diff --git a/plugins/rtmp-services/rtmp-common.c b/plugins/rtmp-services/rtmp-common.c index 59899a801..c409bca6e 100644 --- a/plugins/rtmp-services/rtmp-common.c +++ b/plugins/rtmp-services/rtmp-common.c @@ -6,7 +6,6 @@ #include "rtmp-format-ver.h" #include "service-specific/twitch.h" -#include "service-specific/younow.h" #include "service-specific/nimotv.h" #include "service-specific/showroom.h" #include "service-specific/dacast.h" @@ -826,12 +825,6 @@ static const char *rtmp_common_url(void *data) } } - if (service->service && strcmp(service->service, "YouNow") == 0) { - if (service->server && service->key) { - return younow_get_ingest(service->server, service->key); - } - } - if (service->service && strcmp(service->service, "Nimo TV") == 0) { if (service->server && strcmp(service->server, "auto") == 0) { return nimotv_get_ingest(service->key); diff --git a/plugins/rtmp-services/rtmp-custom.c b/plugins/rtmp-services/rtmp-custom.c index c44d881a2..9c5b1c88d 100644 --- a/plugins/rtmp-services/rtmp-custom.c +++ b/plugins/rtmp-services/rtmp-custom.c @@ -111,7 +111,6 @@ static const char *rtmp_custom_password(void *data) } #define RTMPS_PREFIX "rtmps://" -#define FTL_PREFIX "ftl://" #define SRT_PREFIX "srt://" #define RIST_PREFIX "rist://" @@ -122,9 +121,6 @@ static const char *rtmp_custom_get_protocol(void *data) if (strncmp(service->server, RTMPS_PREFIX, strlen(RTMPS_PREFIX)) == 0) return "RTMPS"; - if (strncmp(service->server, FTL_PREFIX, strlen(FTL_PREFIX)) == 0) - return "FTL"; - if (strncmp(service->server, SRT_PREFIX, strlen(SRT_PREFIX)) == 0) return "SRT"; diff --git a/plugins/rtmp-services/service-specific/younow.c b/plugins/rtmp-services/service-specific/younow.c deleted file mode 100644 index 2914dc480..000000000 --- a/plugins/rtmp-services/service-specific/younow.c +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include - -#include -#include "util/base.h" -#include "younow.h" - -struct younow_mem_struct { - char *memory; - size_t size; -}; - -static char *current_ingest = NULL; - -static size_t younow_write_cb(void *contents, size_t size, size_t nmemb, - void *userp) -{ - size_t realsize = size * nmemb; - struct younow_mem_struct *mem = (struct younow_mem_struct *)userp; - - mem->memory = realloc(mem->memory, mem->size + realsize + 1); - if (mem->memory == NULL) { - blog(LOG_WARNING, "yyounow_write_cb: realloc returned NULL"); - return 0; - } - - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - - return realsize; -} - -const char *younow_get_ingest(const char *server, const char *key) -{ - CURL *curl_handle; - CURLcode res; - struct younow_mem_struct chunk; - struct dstr uri; - long response_code; - - // find the delimiter in stream key - const char *delim = strchr(key, '_'); - if (delim == NULL) { - blog(LOG_WARNING, - "younow_get_ingest: delimiter not found in stream key"); - return server; - } - - curl_handle = curl_easy_init(); - - chunk.memory = malloc(1); /* will be grown as needed by realloc */ - chunk.size = 0; /* no data at this point */ - - dstr_init(&uri); - dstr_copy(&uri, server); - dstr_ncat(&uri, key, delim - key); - - curl_easy_setopt(curl_handle, CURLOPT_URL, uri.array); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, true); - curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 2L); - curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 3L); - curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, younow_write_cb); - curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk); - curl_obs_set_revoke_setting(curl_handle); - - res = curl_easy_perform(curl_handle); - dstr_free(&uri); - - if (res != CURLE_OK) { - blog(LOG_WARNING, - "younow_get_ingest: curl_easy_perform() failed: %s", - curl_easy_strerror(res)); - curl_easy_cleanup(curl_handle); - free(chunk.memory); - return server; - } - - curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &response_code); - if (response_code != 200) { - blog(LOG_WARNING, - "younow_get_ingest: curl_easy_perform() returned code: %ld", - response_code); - curl_easy_cleanup(curl_handle); - free(chunk.memory); - return server; - } - - curl_easy_cleanup(curl_handle); - - if (chunk.size == 0) { - blog(LOG_WARNING, - "younow_get_ingest: curl_easy_perform() returned empty response"); - free(chunk.memory); - return server; - } - - if (current_ingest) { - free(current_ingest); - current_ingest = NULL; - } - - current_ingest = strdup(chunk.memory); - free(chunk.memory); - blog(LOG_INFO, "younow_get_ingest: returning ingest: %s", - current_ingest); - return current_ingest; -} diff --git a/plugins/rtmp-services/service-specific/younow.h b/plugins/rtmp-services/service-specific/younow.h deleted file mode 100644 index 4f181daef..000000000 --- a/plugins/rtmp-services/service-specific/younow.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -extern const char *younow_get_ingest(const char *server, const char *key);