New upstream version 26.1.0+dfsg1
This commit is contained in:
parent
040dcc3fc2
commit
013818c4af
594 changed files with 19576 additions and 4478 deletions
|
|
@ -11,6 +11,10 @@ if (NOT "${FFMPEG_AVCODEC_LIBRARIES}" STREQUAL "")
|
|||
list(REMOVE_ITEM FFMPEG_LIBRARIES ${FFMPEG_AVCODEC_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(DEBUG_FFMPEG_MUX)
|
||||
add_definitions(-DSHOW_SUBPROCESSES)
|
||||
endif()
|
||||
|
||||
if(UNIX)
|
||||
if (NOT APPLE)
|
||||
find_package(X11 REQUIRED)
|
||||
|
|
@ -139,7 +143,6 @@ elseif(APPLE)
|
|||
|
||||
set_source_files_properties(${libobs_PLATFORM_SOURCES}
|
||||
PROPERTIES
|
||||
LANGUAGE C
|
||||
COMPILE_FLAGS "-fobjc-arc")
|
||||
|
||||
find_library(COCOA Cocoa)
|
||||
|
|
@ -361,7 +364,8 @@ set(libobs_util_SOURCES
|
|||
util/crc32.c
|
||||
util/text-lookup.c
|
||||
util/cf-parser.c
|
||||
util/profiler.c)
|
||||
util/profiler.c
|
||||
util/bitstream.c)
|
||||
set(libobs_util_HEADERS
|
||||
util/curl/curl-helper.h
|
||||
util/sse-intrin.h
|
||||
|
|
@ -388,7 +392,8 @@ set(libobs_util_HEADERS
|
|||
util/lexer.h
|
||||
util/platform.h
|
||||
util/profiler.h
|
||||
util/profiler.hpp)
|
||||
util/profiler.hpp
|
||||
util/bitstream.h)
|
||||
|
||||
set(libobs_libobs_SOURCES
|
||||
${libobs_PLATFORM_SOURCES}
|
||||
|
|
@ -467,12 +472,10 @@ source_group("util\\Header Files" FILES ${libobs_util_HEADERS})
|
|||
source_group("audio-monitoring\\Source Files" FILES ${libobs_audio_monitoring_SOURCES})
|
||||
source_group("audio-monitoring\\Header Files" FILES ${libobs_audio_monitoring_HEADERS})
|
||||
|
||||
if(BUILD_CAPTIONS)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/libcaption)
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${libobs_PLATFORM_DEPS}
|
||||
caption)
|
||||
endif()
|
||||
include_directories(${CMAKE_SOURCE_DIR}/deps/libcaption)
|
||||
set(libobs_PLATFORM_DEPS
|
||||
${libobs_PLATFORM_DEPS}
|
||||
caption)
|
||||
|
||||
add_library(libobs SHARED ${libobs_SOURCES} ${libobs_HEADERS})
|
||||
if(UNIX AND NOT APPLE)
|
||||
|
|
|
|||
|
|
@ -183,8 +183,10 @@ static void do_stream_write(void *param)
|
|||
if (bytesToFill > data->bytesRemaining)
|
||||
bytesToFill = data->bytesRemaining;
|
||||
|
||||
pulseaudio_lock();
|
||||
pa_stream_begin_write(data->stream, (void **)&buffer,
|
||||
&bytesToFill);
|
||||
pulseaudio_unlock();
|
||||
|
||||
circlebuf_pop_front(&data->new_data, buffer, bytesToFill);
|
||||
|
||||
|
|
@ -331,8 +333,20 @@ skip:
|
|||
static void pulseaudio_stop_playback(struct audio_monitor *monitor)
|
||||
{
|
||||
if (monitor->stream) {
|
||||
/* Stop the stream */
|
||||
pulseaudio_lock();
|
||||
pa_stream_disconnect(monitor->stream);
|
||||
pulseaudio_unlock();
|
||||
|
||||
/* Remove the callbacks, to ensure we no longer try to do anything
|
||||
* with this stream object */
|
||||
pulseaudio_write_callback(monitor->stream, NULL, NULL);
|
||||
pulseaudio_set_underflow_callback(monitor->stream, NULL, NULL);
|
||||
|
||||
/* Unreference the stream and drop it. PA will free it when it can. */
|
||||
pulseaudio_lock();
|
||||
pa_stream_unref(monitor->stream);
|
||||
pulseaudio_unlock();
|
||||
monitor->stream = NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ bool load_graphics_imports(struct gs_exports *exports, void *module,
|
|||
GRAPHICS_IMPORT_OPTIONAL(gs_texture_release_dc);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_open_shared);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_get_shared_handle);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_wrap_obj);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_acquire_sync);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_release_sync);
|
||||
GRAPHICS_IMPORT_OPTIONAL(device_texture_create_nv12);
|
||||
|
|
|
|||
|
|
@ -299,6 +299,8 @@ struct gs_exports {
|
|||
gs_texture_t *(*device_texture_open_shared)(gs_device_t *device,
|
||||
uint32_t handle);
|
||||
uint32_t (*device_texture_get_shared_handle)(gs_texture_t *tex);
|
||||
gs_texture_t *(*device_texture_wrap_obj)(gs_device_t *device,
|
||||
void *obj);
|
||||
int (*device_texture_acquire_sync)(gs_texture_t *tex, uint64_t key,
|
||||
uint32_t ms);
|
||||
int (*device_texture_release_sync)(gs_texture_t *tex, uint64_t key);
|
||||
|
|
|
|||
|
|
@ -2885,6 +2885,18 @@ uint32_t gs_texture_get_shared_handle(gs_texture_t *tex)
|
|||
return GS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
gs_texture_t *gs_texture_wrap_obj(void *obj)
|
||||
{
|
||||
graphics_t *graphics = thread_graphics;
|
||||
if (!gs_valid("gs_texture_wrap_obj"))
|
||||
return NULL;
|
||||
|
||||
if (graphics->exports.device_texture_wrap_obj)
|
||||
return graphics->exports.device_texture_wrap_obj(
|
||||
graphics->device, obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int gs_texture_acquire_sync(gs_texture_t *tex, uint64_t key, uint32_t ms)
|
||||
{
|
||||
graphics_t *graphics = thread_graphics;
|
||||
|
|
|
|||
|
|
@ -867,6 +867,8 @@ EXPORT gs_texture_t *gs_texture_open_shared(uint32_t handle);
|
|||
#define GS_INVALID_HANDLE (uint32_t) - 1
|
||||
EXPORT uint32_t gs_texture_get_shared_handle(gs_texture_t *tex);
|
||||
|
||||
EXPORT gs_texture_t *gs_texture_wrap_obj(void *obj);
|
||||
|
||||
#define GS_WAIT_INFINITE (uint32_t) - 1
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -224,6 +224,12 @@ static inline int process_packets(media_remux_job_t job,
|
|||
if (ret < 0) {
|
||||
blog(LOG_ERROR, "media_remux: Error muxing packet: %s",
|
||||
av_err2str(ret));
|
||||
|
||||
/* Treat "Invalid data found when processing input" and
|
||||
* "Invalid argument" as non-fatal */
|
||||
if (ret == AVERROR_INVALIDDATA || ret == EINVAL)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ static bool discard_if_stopped(obs_source_t *source, size_t channels)
|
|||
blog(LOG_DEBUG, "doing pending stop trick: '%s'",
|
||||
source->context.name);
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t ch = 0; ch < channels; ch++)
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@
|
|||
*
|
||||
* Reset to zero each major version
|
||||
*/
|
||||
#define LIBOBS_API_MINOR_VER 0
|
||||
#define LIBOBS_API_MINOR_VER 1
|
||||
|
||||
/*
|
||||
* Increment if backward-compatible bug fix
|
||||
*
|
||||
* Reset to zero each major or minor version
|
||||
*/
|
||||
#define LIBOBS_API_PATCH_VER 2
|
||||
#define LIBOBS_API_PATCH_VER 0
|
||||
|
||||
#define MAKE_SEMANTIC_VERSION(major, minor, patch) \
|
||||
((major << 24) | (minor << 16) | patch)
|
||||
|
|
|
|||
|
|
@ -1762,12 +1762,12 @@ bool obs_data_item_get_default_bool(obs_data_item_t *item)
|
|||
|
||||
obs_data_t *obs_data_item_get_default_obj(obs_data_item_t *item)
|
||||
{
|
||||
return data_item_get_obj(item, get_item_obj);
|
||||
return data_item_get_obj(item, get_item_default_obj);
|
||||
}
|
||||
|
||||
obs_data_array_t *obs_data_item_get_default_array(obs_data_item_t *item)
|
||||
{
|
||||
return data_item_get_array(item, get_item_array);
|
||||
return data_item_get_array(item, get_item_default_array);
|
||||
}
|
||||
|
||||
const char *obs_data_item_get_autoselect_string(obs_data_item_t *item)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
|
||||
#include "obs.h"
|
||||
|
||||
#include <caption/caption.h>
|
||||
|
||||
#define NUM_TEXTURES 2
|
||||
#define NUM_CHANNELS 3
|
||||
#define MICROSECOND_DEN 1000000
|
||||
|
|
@ -587,6 +589,11 @@ struct audio_cb_info {
|
|||
void *param;
|
||||
};
|
||||
|
||||
struct caption_cb_info {
|
||||
obs_source_caption_t callback;
|
||||
void *param;
|
||||
};
|
||||
|
||||
struct obs_source {
|
||||
struct obs_context_data context;
|
||||
struct obs_source_info info;
|
||||
|
|
@ -690,6 +697,9 @@ struct obs_source {
|
|||
uint32_t async_convert_width[MAX_AV_PLANES];
|
||||
uint32_t async_convert_height[MAX_AV_PLANES];
|
||||
|
||||
pthread_mutex_t caption_cb_mutex;
|
||||
DARRAY(struct caption_cb_info) caption_cb_list;
|
||||
|
||||
/* async video deinterlacing */
|
||||
uint64_t deinterlace_offset;
|
||||
uint64_t deinterlace_frame_ts;
|
||||
|
|
@ -977,6 +987,8 @@ struct obs_output {
|
|||
struct caption_text *caption_head;
|
||||
struct caption_text *caption_tail;
|
||||
|
||||
struct circlebuf caption_data;
|
||||
|
||||
bool valid;
|
||||
|
||||
uint64_t active_delay_ns;
|
||||
|
|
|
|||
|
|
@ -23,10 +23,12 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__FreeBSD__)
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#if !defined(__OpenBSD__)
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
#include <sys/utsname.h>
|
||||
#include <xcb/xcb.h>
|
||||
#if USE_XINPUT
|
||||
|
|
@ -155,9 +157,10 @@ static void log_processor_info(void)
|
|||
dstr_free(&proc_speed);
|
||||
free(line);
|
||||
}
|
||||
#elif defined(__FreeBSD__)
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
static void log_processor_speed(void)
|
||||
{
|
||||
#ifndef __OpenBSD__
|
||||
char *line = NULL;
|
||||
size_t linecap = 0;
|
||||
FILE *fp;
|
||||
|
|
@ -187,6 +190,7 @@ static void log_processor_speed(void)
|
|||
fclose(fp);
|
||||
dstr_free(&proc_speed);
|
||||
free(line);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void log_processor_name(void)
|
||||
|
|
@ -218,6 +222,19 @@ static void log_processor_info(void)
|
|||
|
||||
static void log_memory_info(void)
|
||||
{
|
||||
#if defined(__OpenBSD__)
|
||||
int mib[2];
|
||||
size_t len;
|
||||
int64_t mem;
|
||||
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_PHYSMEM64;
|
||||
len = sizeof(mem);
|
||||
|
||||
if (sysctl(mib, 2, &mem, &len, NULL, 0) >= 0)
|
||||
blog(LOG_INFO, "Physical Memory: %" PRIi64 "MB Total",
|
||||
mem / 1024 / 1024);
|
||||
#else
|
||||
struct sysinfo info;
|
||||
if (sysinfo(&info) < 0)
|
||||
return;
|
||||
|
|
@ -227,6 +244,7 @@ static void log_memory_info(void)
|
|||
(uint64_t)info.totalram * info.mem_unit / 1024 / 1024,
|
||||
((uint64_t)info.freeram + (uint64_t)info.bufferram) *
|
||||
info.mem_unit / 1024 / 1024);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void log_kernel_version(void)
|
||||
|
|
@ -312,6 +330,14 @@ static void log_distribution_info(void)
|
|||
dstr_free(&distro);
|
||||
free(line);
|
||||
}
|
||||
|
||||
static void log_desktop_session_info(void)
|
||||
{
|
||||
char *session_ptr = getenv("XDG_SESSION_TYPE");
|
||||
if (session_ptr) {
|
||||
blog(LOG_INFO, "Session Type: %s", session_ptr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void log_system_info(void)
|
||||
|
|
@ -324,6 +350,7 @@ void log_system_info(void)
|
|||
log_kernel_version();
|
||||
#if defined(__linux__)
|
||||
log_distribution_info();
|
||||
log_desktop_session_info();
|
||||
#endif
|
||||
log_x_info();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,8 @@
|
|||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
#if BUILD_CAPTIONS
|
||||
#include <caption/caption.h>
|
||||
#include <caption/mpeg.h>
|
||||
#endif
|
||||
|
||||
static inline bool active(const struct obs_output *output)
|
||||
{
|
||||
|
|
@ -227,6 +225,7 @@ void obs_output_destroy(obs_output_t *output)
|
|||
os_event_destroy(output->reconnect_stop_event);
|
||||
obs_context_data_free(&output->context);
|
||||
circlebuf_free(&output->delay_data);
|
||||
circlebuf_free(&output->caption_data);
|
||||
if (output->owns_info_id)
|
||||
bfree((void *)output->info.id);
|
||||
if (output->last_error_message)
|
||||
|
|
@ -267,6 +266,10 @@ bool obs_output_actual_start(obs_output_t *output)
|
|||
os_atomic_dec_long(&output->delay_restart_refs);
|
||||
|
||||
output->caption_timestamp = 0;
|
||||
|
||||
circlebuf_free(&output->caption_data);
|
||||
circlebuf_init(&output->caption_data);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
@ -1201,13 +1204,11 @@ static inline bool has_higher_opposing_ts(struct obs_output *output,
|
|||
return output->highest_video_ts > packet->dts_usec;
|
||||
}
|
||||
|
||||
#if BUILD_CAPTIONS
|
||||
static const uint8_t nal_start[4] = {0, 0, 0, 1};
|
||||
|
||||
static bool add_caption(struct obs_output *output, struct encoder_packet *out)
|
||||
{
|
||||
struct encoder_packet backup = *out;
|
||||
caption_frame_t cf;
|
||||
sei_t sei;
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
|
|
@ -1224,10 +1225,62 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
|
|||
da_push_back_array(out_data, &ref, sizeof(ref));
|
||||
da_push_back_array(out_data, out->data, out->size);
|
||||
|
||||
caption_frame_init(&cf);
|
||||
caption_frame_from_text(&cf, &output->caption_head->text[0]);
|
||||
if (output->caption_data.size > 0) {
|
||||
|
||||
sei_from_caption_frame(&sei, &cf);
|
||||
cea708_t cea708;
|
||||
cea708_init(&cea708, 0); // set up a new popon frame
|
||||
void *caption_buf = bzalloc(3 * sizeof(uint8_t));
|
||||
|
||||
while (output->caption_data.size > 0) {
|
||||
circlebuf_pop_front(&output->caption_data, caption_buf,
|
||||
3 * sizeof(uint8_t));
|
||||
|
||||
if ((((uint8_t *)caption_buf)[0] & 0x3) != 0) {
|
||||
// only send cea 608
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t captionData = ((uint8_t *)caption_buf)[1];
|
||||
captionData = captionData << 8;
|
||||
captionData += ((uint8_t *)caption_buf)[2];
|
||||
|
||||
// padding
|
||||
if (captionData == 0x8080) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (captionData == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!eia608_parity_varify(captionData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cea708_add_cc_data(&cea708, 1,
|
||||
((uint8_t *)caption_buf)[0] & 0x3,
|
||||
captionData);
|
||||
}
|
||||
|
||||
bfree(caption_buf);
|
||||
|
||||
sei_message_t *msg =
|
||||
sei_message_new(sei_type_user_data_registered_itu_t_t35,
|
||||
0, CEA608_MAX_SIZE);
|
||||
msg->size = cea708_render(&cea708, sei_message_data(msg),
|
||||
sei_message_size(msg));
|
||||
sei_message_append(&sei, msg);
|
||||
} else if (output->caption_head) {
|
||||
caption_frame_t cf;
|
||||
caption_frame_init(&cf);
|
||||
caption_frame_from_text(&cf, &output->caption_head->text[0]);
|
||||
|
||||
sei_from_caption_frame(&sei, &cf);
|
||||
|
||||
struct caption_text *next = output->caption_head->next;
|
||||
bfree(output->caption_head);
|
||||
output->caption_head = next;
|
||||
}
|
||||
|
||||
data = malloc(sei_render_size(&sei));
|
||||
size = sei_render(&sei, data);
|
||||
|
|
@ -1244,12 +1297,10 @@ static bool add_caption(struct obs_output *output, struct encoder_packet *out)
|
|||
|
||||
sei_free(&sei);
|
||||
|
||||
struct caption_text *next = output->caption_head->next;
|
||||
bfree(output->caption_head);
|
||||
output->caption_head = next;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
double last_caption_timestamp = 0;
|
||||
|
||||
static inline void send_interleaved(struct obs_output *output)
|
||||
{
|
||||
|
|
@ -1266,7 +1317,6 @@ static inline void send_interleaved(struct obs_output *output)
|
|||
if (out.type == OBS_ENCODER_VIDEO) {
|
||||
output->total_frames++;
|
||||
|
||||
#if BUILD_CAPTIONS
|
||||
pthread_mutex_lock(&output->caption_mutex);
|
||||
|
||||
double frame_timestamp =
|
||||
|
|
@ -1286,8 +1336,14 @@ static inline void send_interleaved(struct obs_output *output)
|
|||
}
|
||||
}
|
||||
|
||||
if (output->caption_data.size > 0) {
|
||||
if (last_caption_timestamp < frame_timestamp) {
|
||||
last_caption_timestamp = frame_timestamp;
|
||||
add_caption(output, &out);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&output->caption_mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
output->info.encoded_packet(output->context.data, &out);
|
||||
|
|
@ -2471,7 +2527,18 @@ const char *obs_output_get_id(const obs_output_t *output)
|
|||
: NULL;
|
||||
}
|
||||
|
||||
#if BUILD_CAPTIONS
|
||||
void obs_output_caption(obs_output_t *output,
|
||||
const struct obs_source_cea_708 *captions)
|
||||
{
|
||||
pthread_mutex_lock(&output->caption_mutex);
|
||||
for (size_t i = 0; i < captions->packets; i++) {
|
||||
circlebuf_push_back(&output->caption_data,
|
||||
captions->data + (i * 3),
|
||||
3 * sizeof(uint8_t));
|
||||
}
|
||||
pthread_mutex_unlock(&output->caption_mutex);
|
||||
}
|
||||
|
||||
static struct caption_text *caption_text_new(const char *text, size_t bytes,
|
||||
struct caption_text *tail,
|
||||
struct caption_text **head,
|
||||
|
|
@ -2518,7 +2585,6 @@ void obs_output_output_caption_text2(obs_output_t *output, const char *text,
|
|||
|
||||
pthread_mutex_unlock(&output->caption_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
float obs_output_get_congestion(obs_output_t *output)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -418,3 +418,51 @@ const char *obs_service_get_output_type(const obs_service_t *service)
|
|||
return service->info.get_output_type(service->context.data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void obs_service_get_supported_resolutions(
|
||||
const obs_service_t *service,
|
||||
struct obs_service_resolution **resolutions, size_t *count)
|
||||
{
|
||||
if (!obs_service_valid(service, "obs_service_supported_resolutions"))
|
||||
return;
|
||||
if (!obs_ptr_valid(resolutions, "obs_service_supported_resolutions"))
|
||||
return;
|
||||
if (!obs_ptr_valid(count, "obs_service_supported_resolutions"))
|
||||
return;
|
||||
|
||||
*resolutions = NULL;
|
||||
*count = 0;
|
||||
|
||||
if (service->info.get_supported_resolutions)
|
||||
service->info.get_supported_resolutions(service->context.data,
|
||||
resolutions, count);
|
||||
}
|
||||
|
||||
void obs_service_get_max_fps(const obs_service_t *service, int *fps)
|
||||
{
|
||||
if (!obs_service_valid(service, "obs_service_get_max_fps"))
|
||||
return;
|
||||
if (!obs_ptr_valid(fps, "obs_service_get_max_fps"))
|
||||
return;
|
||||
|
||||
*fps = 0;
|
||||
|
||||
if (service->info.get_max_fps)
|
||||
service->info.get_max_fps(service->context.data, fps);
|
||||
}
|
||||
|
||||
void obs_service_get_max_bitrate(const obs_service_t *service,
|
||||
int *video_bitrate, int *audio_bitrate)
|
||||
{
|
||||
if (video_bitrate)
|
||||
*video_bitrate = 0;
|
||||
if (audio_bitrate)
|
||||
*audio_bitrate = 0;
|
||||
|
||||
if (!obs_service_valid(service, "obs_service_get_max_bitrate"))
|
||||
return;
|
||||
|
||||
if (service->info.get_max_bitrate)
|
||||
service->info.get_max_bitrate(service->context.data,
|
||||
video_bitrate, audio_bitrate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct obs_service_resolution {
|
||||
int cx;
|
||||
int cy;
|
||||
};
|
||||
|
||||
struct obs_service_info {
|
||||
/* required */
|
||||
const char *id;
|
||||
|
|
@ -74,7 +79,13 @@ struct obs_service_info {
|
|||
|
||||
const char *(*get_output_type)(void *data);
|
||||
|
||||
/* TODO: more stuff later */
|
||||
void (*get_supported_resolutions)(
|
||||
void *data, struct obs_service_resolution **resolutions,
|
||||
size_t *count);
|
||||
void (*get_max_fps)(void *data, int *fps);
|
||||
|
||||
void (*get_max_bitrate)(void *data, int *video_bitrate,
|
||||
int *audio_bitrate);
|
||||
};
|
||||
|
||||
EXPORT void obs_register_service_s(const struct obs_service_info *info,
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include "obs.h"
|
||||
#include "obs-internal.h"
|
||||
|
||||
static bool filter_compatible(obs_source_t *source, obs_source_t *filter);
|
||||
|
||||
static inline bool data_valid(const struct obs_source *source, const char *f)
|
||||
{
|
||||
return obs_source_valid(source, f) && source->context.data;
|
||||
|
|
@ -182,6 +184,7 @@ static bool obs_source_init(struct obs_source *source)
|
|||
pthread_mutex_init_value(&source->audio_mutex);
|
||||
pthread_mutex_init_value(&source->audio_buf_mutex);
|
||||
pthread_mutex_init_value(&source->audio_cb_mutex);
|
||||
pthread_mutex_init_value(&source->caption_cb_mutex);
|
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0)
|
||||
return false;
|
||||
|
|
@ -199,6 +202,8 @@ static bool obs_source_init(struct obs_source *source)
|
|||
return false;
|
||||
if (pthread_mutex_init(&source->async_mutex, NULL) != 0)
|
||||
return false;
|
||||
if (pthread_mutex_init(&source->caption_cb_mutex, NULL) != 0)
|
||||
return false;
|
||||
|
||||
if (is_audio_source(source) || is_composite_source(source))
|
||||
allocate_audio_output_buffer(source);
|
||||
|
|
@ -493,6 +498,32 @@ void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src)
|
|||
duplicate_filters(dst, src, dst->context.private);
|
||||
}
|
||||
|
||||
static void duplicate_filter(obs_source_t *dst, obs_source_t *filter)
|
||||
{
|
||||
if (!filter_compatible(dst, filter))
|
||||
return;
|
||||
|
||||
char *new_name = get_new_filter_name(dst, filter->context.name);
|
||||
bool enabled = obs_source_enabled(filter);
|
||||
|
||||
obs_source_t *dst_filter = obs_source_duplicate(filter, new_name, true);
|
||||
obs_source_set_enabled(dst_filter, enabled);
|
||||
|
||||
bfree(new_name);
|
||||
obs_source_filter_add(dst, dst_filter);
|
||||
obs_source_release(dst_filter);
|
||||
}
|
||||
|
||||
void obs_source_copy_single_filter(obs_source_t *dst, obs_source_t *filter)
|
||||
{
|
||||
if (!obs_source_valid(dst, "obs_source_copy_single_filter"))
|
||||
return;
|
||||
if (!obs_source_valid(filter, "obs_source_copy_single_filter"))
|
||||
return;
|
||||
|
||||
duplicate_filter(dst, filter);
|
||||
}
|
||||
|
||||
obs_source_t *obs_source_duplicate(obs_source_t *source, const char *new_name,
|
||||
bool create_private)
|
||||
{
|
||||
|
|
@ -655,6 +686,7 @@ void obs_source_destroy(struct obs_source *source)
|
|||
|
||||
da_free(source->audio_actions);
|
||||
da_free(source->audio_cb_list);
|
||||
da_free(source->caption_cb_list);
|
||||
da_free(source->async_cache);
|
||||
da_free(source->async_frames);
|
||||
da_free(source->filters);
|
||||
|
|
@ -663,6 +695,7 @@ void obs_source_destroy(struct obs_source *source)
|
|||
pthread_mutex_destroy(&source->audio_buf_mutex);
|
||||
pthread_mutex_destroy(&source->audio_cb_mutex);
|
||||
pthread_mutex_destroy(&source->audio_mutex);
|
||||
pthread_mutex_destroy(&source->caption_cb_mutex);
|
||||
pthread_mutex_destroy(&source->async_mutex);
|
||||
obs_data_release(source->private_settings);
|
||||
obs_context_data_free(&source->context);
|
||||
|
|
@ -2870,6 +2903,51 @@ void obs_source_set_async_rotation(obs_source_t *source, long rotation)
|
|||
source->async_rotation = rotation;
|
||||
}
|
||||
|
||||
void obs_source_output_cea708(obs_source_t *source,
|
||||
const struct obs_source_cea_708 *captions)
|
||||
{
|
||||
if (!captions) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&source->caption_cb_mutex);
|
||||
|
||||
for (size_t i = source->caption_cb_list.num; i > 0; i--) {
|
||||
struct caption_cb_info info =
|
||||
source->caption_cb_list.array[i - 1];
|
||||
info.callback(info.param, source, captions);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&source->caption_cb_mutex);
|
||||
}
|
||||
|
||||
void obs_source_add_caption_callback(obs_source_t *source,
|
||||
obs_source_caption_t callback, void *param)
|
||||
{
|
||||
struct caption_cb_info info = {callback, param};
|
||||
|
||||
if (!obs_source_valid(source, "obs_source_add_caption_callback"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&source->caption_cb_mutex);
|
||||
da_push_back(source->caption_cb_list, &info);
|
||||
pthread_mutex_unlock(&source->caption_cb_mutex);
|
||||
}
|
||||
|
||||
void obs_source_remove_caption_callback(obs_source_t *source,
|
||||
obs_source_caption_t callback,
|
||||
void *param)
|
||||
{
|
||||
struct caption_cb_info info = {callback, param};
|
||||
|
||||
if (!obs_source_valid(source, "obs_source_remove_caption_callback"))
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&source->caption_cb_mutex);
|
||||
da_erase_item(source->caption_cb_list, &info);
|
||||
pthread_mutex_unlock(&source->caption_cb_mutex);
|
||||
}
|
||||
|
||||
static inline bool preload_frame_changed(obs_source_t *source,
|
||||
const struct obs_source_frame *in)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -186,6 +186,11 @@ enum obs_media_state {
|
|||
*/
|
||||
#define OBS_SOURCE_CONTROLLABLE_MEDIA (1 << 13)
|
||||
|
||||
/**
|
||||
* Source type provides cea708 data
|
||||
*/
|
||||
#define OBS_SOURCE_CEA_708 (1 << 14)
|
||||
|
||||
/** @} */
|
||||
|
||||
typedef void (*obs_source_enum_proc_t)(obs_source_t *parent,
|
||||
|
|
|
|||
34
libobs/obs.h
34
libobs/obs.h
|
|
@ -212,6 +212,12 @@ struct obs_source_audio {
|
|||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct obs_source_cea_708 {
|
||||
const uint8_t *data;
|
||||
uint32_t packets;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
/**
|
||||
* Source asynchronous video output structure. Used with
|
||||
* obs_source_output_video to output asynchronous video. Video is buffered as
|
||||
|
|
@ -1085,6 +1091,8 @@ EXPORT obs_source_t *obs_source_get_filter_by_name(obs_source_t *source,
|
|||
const char *name);
|
||||
|
||||
EXPORT void obs_source_copy_filters(obs_source_t *dst, obs_source_t *src);
|
||||
EXPORT void obs_source_copy_single_filter(obs_source_t *dst,
|
||||
obs_source_t *filter);
|
||||
|
||||
EXPORT bool obs_source_enabled(const obs_source_t *source);
|
||||
EXPORT void obs_source_set_enabled(obs_source_t *source, bool enabled);
|
||||
|
|
@ -1115,6 +1123,16 @@ EXPORT void obs_source_add_audio_capture_callback(
|
|||
EXPORT void obs_source_remove_audio_capture_callback(
|
||||
obs_source_t *source, obs_source_audio_capture_t callback, void *param);
|
||||
|
||||
typedef void (*obs_source_caption_t)(void *param, obs_source_t *source,
|
||||
const struct obs_source_cea_708 *captions);
|
||||
|
||||
EXPORT void obs_source_add_caption_callback(obs_source_t *source,
|
||||
obs_source_caption_t callback,
|
||||
void *param);
|
||||
EXPORT void obs_source_remove_caption_callback(obs_source_t *source,
|
||||
obs_source_caption_t callback,
|
||||
void *param);
|
||||
|
||||
enum obs_deinterlace_mode {
|
||||
OBS_DEINTERLACE_MODE_DISABLE,
|
||||
OBS_DEINTERLACE_MODE_DISCARD,
|
||||
|
|
@ -1206,6 +1224,9 @@ EXPORT void obs_source_output_video2(obs_source_t *source,
|
|||
|
||||
EXPORT void obs_source_set_async_rotation(obs_source_t *source, long rotation);
|
||||
|
||||
EXPORT void obs_source_output_cea708(obs_source_t *source,
|
||||
const struct obs_source_cea_708 *captions);
|
||||
|
||||
/**
|
||||
* Preloads asynchronous video data to allow instantaneous playback
|
||||
*
|
||||
|
|
@ -1882,13 +1903,14 @@ EXPORT uint32_t obs_output_get_height(const obs_output_t *output);
|
|||
|
||||
EXPORT const char *obs_output_get_id(const obs_output_t *output);
|
||||
|
||||
#if BUILD_CAPTIONS
|
||||
EXPORT void obs_output_caption(obs_output_t *output,
|
||||
const struct obs_source_cea_708 *captions);
|
||||
|
||||
EXPORT void obs_output_output_caption_text1(obs_output_t *output,
|
||||
const char *text);
|
||||
EXPORT void obs_output_output_caption_text2(obs_output_t *output,
|
||||
const char *text,
|
||||
double display_duration);
|
||||
#endif
|
||||
|
||||
EXPORT float obs_output_get_congestion(obs_output_t *output);
|
||||
EXPORT int obs_output_get_connect_time_ms(obs_output_t *output);
|
||||
|
|
@ -2209,6 +2231,14 @@ EXPORT void *obs_service_get_type_data(obs_service_t *service);
|
|||
|
||||
EXPORT const char *obs_service_get_id(const obs_service_t *service);
|
||||
|
||||
EXPORT void obs_service_get_supported_resolutions(
|
||||
const obs_service_t *service,
|
||||
struct obs_service_resolution **resolutions, size_t *count);
|
||||
EXPORT void obs_service_get_max_fps(const obs_service_t *service, int *fps);
|
||||
|
||||
EXPORT void obs_service_get_max_bitrate(const obs_service_t *service,
|
||||
int *video_bitrate, int *audio_bitrate);
|
||||
|
||||
/* NOTE: This function is temporary and should be removed/replaced at a later
|
||||
* date. */
|
||||
EXPORT const char *obs_service_get_output_type(const obs_service_t *service);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#define OBS_PLUGIN_DESTINATION "@OBS_PLUGIN_DESTINATION@"
|
||||
#define OBS_RELATIVE_PREFIX "@OBS_RELATIVE_PREFIX@"
|
||||
#define OBS_UNIX_STRUCTURE @OBS_UNIX_STRUCTURE@
|
||||
#define BUILD_CAPTIONS @BUILD_CAPTIONS@
|
||||
#define HAVE_DBUS @HAVE_DBUS@
|
||||
#define HAVE_PULSEAUDIO @HAVE_PULSEAUDIO@
|
||||
#define USE_XINPUT @USE_XINPUT@
|
||||
|
|
|
|||
52
libobs/util/bitstream.c
Normal file
52
libobs/util/bitstream.c
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include "bitstream.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void bitstream_reader_init(struct bitstream_reader *r, uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
memset(r, 0, sizeof(struct bitstream_reader));
|
||||
r->buf = data;
|
||||
r->subPos = 0x80;
|
||||
r->len = len;
|
||||
}
|
||||
|
||||
uint8_t bitstream_reader_read_bit(struct bitstream_reader *r)
|
||||
{
|
||||
if (r->pos >= r->len)
|
||||
return 0;
|
||||
|
||||
uint8_t bit = (*(r->buf + r->pos) & r->subPos) == r->subPos ? 1 : 0;
|
||||
|
||||
r->subPos >>= 0x1;
|
||||
if (r->subPos == 0) {
|
||||
r->subPos = 0x80;
|
||||
r->pos++;
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
uint8_t bitstream_reader_read_bits(struct bitstream_reader *r, int bits)
|
||||
{
|
||||
uint8_t res = 0;
|
||||
|
||||
for (int i = 1; i <= bits; i++) {
|
||||
res <<= 1;
|
||||
res |= bitstream_reader_read_bit(r);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t bitstream_reader_r8(struct bitstream_reader *r)
|
||||
{
|
||||
return bitstream_reader_read_bits(r, 8);
|
||||
}
|
||||
|
||||
uint16_t bitstream_reader_r16(struct bitstream_reader *r)
|
||||
{
|
||||
uint8_t b = bitstream_reader_read_bits(r, 8);
|
||||
return ((uint16_t)b << 8) | bitstream_reader_read_bits(r, 8);
|
||||
}
|
||||
29
libobs/util/bitstream.h
Normal file
29
libobs/util/bitstream.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "c99defs.h"
|
||||
|
||||
/*
|
||||
* General programmable serialization functions. (A shared interface to
|
||||
* various reading/writing to/from different inputs/outputs)
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct bitstream_reader {
|
||||
uint8_t pos;
|
||||
uint8_t subPos;
|
||||
uint8_t *buf;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
EXPORT void bitstream_reader_init(struct bitstream_reader *r, uint8_t *data,
|
||||
size_t len);
|
||||
EXPORT uint8_t bitstream_reader_read_bits(struct bitstream_reader *r, int bits);
|
||||
EXPORT uint8_t bitstream_reader_r8(struct bitstream_reader *r);
|
||||
EXPORT uint16_t bitstream_reader_r16(struct bitstream_reader *r);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -57,11 +57,15 @@ static inline bool create_process(const char *cmd_line, HANDLE stdin_handle,
|
|||
si.hStdOutput = stdout_handle;
|
||||
si.hStdError = stderr_handle;
|
||||
|
||||
DWORD flags = 0;
|
||||
#ifndef SHOW_SUBPROCESSES
|
||||
flags = CREATE_NO_WINDOW;
|
||||
#endif
|
||||
|
||||
os_utf8_to_wcs_ptr(cmd_line, 0, &cmd_line_w);
|
||||
if (cmd_line_w) {
|
||||
success = !!CreateProcessW(NULL, cmd_line_w, NULL, NULL, true,
|
||||
CREATE_NO_WINDOW, NULL, NULL, &si,
|
||||
&pi);
|
||||
flags, NULL, NULL, &si, &pi);
|
||||
|
||||
if (success) {
|
||||
*process = pi.hProcess;
|
||||
|
|
|
|||
|
|
@ -34,14 +34,16 @@
|
|||
#include <sys/times.h>
|
||||
#include <sys/wait.h>
|
||||
#include <libgen.h>
|
||||
#ifdef __FreeBSD__
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/user.h>
|
||||
#include <unistd.h>
|
||||
#if defined(__FreeBSD__)
|
||||
#include <libprocstat.h>
|
||||
#endif
|
||||
#else
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
|
@ -272,6 +274,62 @@ char *os_get_program_data_path_ptr(const char *name)
|
|||
return str;
|
||||
}
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
// a bit modified version of https://stackoverflow.com/a/31495527
|
||||
ssize_t os_openbsd_get_executable_path(char *epath)
|
||||
{
|
||||
int mib[4];
|
||||
char **argv;
|
||||
size_t len;
|
||||
const char *comm;
|
||||
int ok = 0;
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC_ARGS;
|
||||
mib[2] = getpid();
|
||||
mib[3] = KERN_PROC_ARGV;
|
||||
|
||||
if (sysctl(mib, 4, NULL, &len, NULL, 0) < 0)
|
||||
abort();
|
||||
|
||||
if (!(argv = malloc(len)))
|
||||
abort();
|
||||
|
||||
if (sysctl(mib, 4, argv, &len, NULL, 0) < 0)
|
||||
abort();
|
||||
|
||||
comm = argv[0];
|
||||
|
||||
if (*comm == '/' || *comm == '.') {
|
||||
if (realpath(comm, epath))
|
||||
ok = 1;
|
||||
} else {
|
||||
char *sp;
|
||||
char *xpath = strdup(getenv("PATH"));
|
||||
char *path = strtok_r(xpath, ":", &sp);
|
||||
struct stat st;
|
||||
|
||||
if (!xpath)
|
||||
abort();
|
||||
|
||||
while (path) {
|
||||
snprintf(epath, PATH_MAX, "%s/%s", path, comm);
|
||||
|
||||
if (!stat(epath, &st) && (st.st_mode & S_IXUSR)) {
|
||||
ok = 1;
|
||||
break;
|
||||
}
|
||||
path = strtok_r(NULL, ":", &sp);
|
||||
}
|
||||
|
||||
free(xpath);
|
||||
}
|
||||
|
||||
free(argv);
|
||||
return ok ? (ssize_t)strlen(epath) : -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
char *os_get_executable_path_ptr(const char *name)
|
||||
{
|
||||
char exe[PATH_MAX];
|
||||
|
|
@ -286,6 +344,8 @@ char *os_get_executable_path_ptr(const char *name)
|
|||
return NULL;
|
||||
}
|
||||
count = pathlen;
|
||||
#elif defined(__OpenBSD__)
|
||||
ssize_t count = os_openbsd_get_executable_path(exe);
|
||||
#else
|
||||
ssize_t count = readlink("/proc/self/exe", exe, PATH_MAX - 1);
|
||||
if (count >= 0) {
|
||||
|
|
|
|||
|
|
@ -1058,7 +1058,11 @@ bool profiler_snapshot_dump_csv_gz(const profiler_snapshot_t *snap,
|
|||
|
||||
profiler_snapshot_dump(snap, dump_csv_gzwrite, gz);
|
||||
|
||||
#ifdef _WIN32
|
||||
gzclose_w(gz);
|
||||
#else
|
||||
gzclose(gz);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue