/** * The x server was changed */ static bool xshm_server_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *settings) { UNUSED_PARAMETER(p); bool advanced = obs_data_get_bool(settings, "advanced"); int_fast32_t old_screen = obs_data_get_int(settings, "screen"); const char *server = obs_data_get_string(settings, "server"); obs_property_t *screens = obs_properties_get(props, "screen"); /* we want a real NULL here in case there is no string here */ server = (advanced && *server) ? server : NULL; obs_property_list_clear(screens); Display *dpy = XOpenDisplay(server); if (!dpy) { obs_property_set_enabled(screens, false); return true; } struct dstr screen_info; dstr_init(&screen_info); bool xinerama = xinerama_is_active(dpy); int_fast32_t count = (xinerama) ? xinerama_screen_count(dpy) : XScreenCount(dpy); for (int_fast32_t i = 0; i < count; ++i) { int_fast32_t x, y, w, h; x = y = w = h = 0; if (xinerama) xinerama_screen_geo(dpy, i, &x, &y, &w, &h); else x11_screen_geo(dpy, i, &w, &h); dstr_printf(&screen_info, "Screen %"PRIuFAST32" (%"PRIuFAST32 "x%"PRIuFAST32" @ %"PRIuFAST32 ",%"PRIuFAST32")", i, w, h, x, y); obs_property_list_add_int(screens, screen_info.array, i); } /* handle missing screen */ if (old_screen + 1 > count) { dstr_printf(&screen_info, "Screen %"PRIuFAST32" (not found)", old_screen); size_t index = obs_property_list_add_int(screens, screen_info.array, old_screen); obs_property_list_item_disable(screens, index, true); } dstr_free(&screen_info); XCloseDisplay(dpy); obs_property_set_enabled(screens, true); return true; }
/* * List dv timings for the device */ static void v4l2_dv_timing_list(int dev, obs_property_t *prop) { struct v4l2_dv_timings dvt; struct dstr buf; int index = 0; dstr_init(&buf); obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); while (v4l2_enum_dv_timing(dev, &dvt, index) == 0) { /* i do not pretend to understand, this is from qv4l2 ... */ double h = (double) dvt.bt.height + dvt.bt.vfrontporch + dvt.bt.vsync + dvt.bt.vbackporch + dvt.bt.il_vfrontporch + dvt.bt.il_vsync + dvt.bt.il_vbackporch; double w = (double) dvt.bt.width + dvt.bt.hfrontporch + dvt.bt.hsync + dvt.bt.hbackporch; double i = (dvt.bt.interlaced) ? 2.0f : 1.0f; double rate = (double) dvt.bt.pixelclock / (w * (h / i)); dstr_printf(&buf, "%ux%u%c %.2f", dvt.bt.width, dvt.bt.height, (dvt.bt.interlaced) ? 'i' : 'p', rate); obs_property_list_add_int(prop, buf.array, index); index++; } dstr_free(&buf); }
/* * List framerates for device and resolution */ static void v4l2_framerate_list(int dev, uint_fast32_t pixelformat, uint_fast32_t width, uint_fast32_t height, obs_property_t *prop) { struct v4l2_frmivalenum frmival; frmival.pixel_format = pixelformat; frmival.width = width; frmival.height = height; frmival.index = 0; struct dstr buffer; dstr_init(&buffer); obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); v4l2_ioctl(dev, VIDIOC_ENUM_FRAMEINTERVALS, &frmival); switch(frmival.type) { case V4L2_FRMIVAL_TYPE_DISCRETE: while (v4l2_ioctl(dev, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) == 0) { float fps = (float) frmival.discrete.denominator / frmival.discrete.numerator; int pack = v4l2_pack_tuple(frmival.discrete.numerator, frmival.discrete.denominator); dstr_printf(&buffer, "%.2f", fps); obs_property_list_add_int(prop, buffer.array, pack); frmival.index++; } break; default: blog(LOG_INFO, "Stepwise and Continuous framerates " "are currently hardcoded"); for (const int *packed = v4l2_framerates; *packed; ++packed) { int num; int denom; v4l2_unpack_tuple(&num, &denom, *packed); float fps = (float) denom / num; dstr_printf(&buffer, "%.2f", fps); obs_property_list_add_int(prop, buffer.array, *packed); } break; } dstr_free(&buffer); }
void config_set_default_double(config_t config, const char *section, const char *name, double value) { struct dstr str; dstr_init(&str); dstr_printf(&str, "%g", value); config_set_item(&config->defaults, section, name, str.array); }
void config_set_default_uint(config_t config, const char *section, const char *name, uint64_t value) { struct dstr str; dstr_init(&str); dstr_printf(&str, "%llu", value); config_set_item(&config->defaults, section, name, str.array); }
void config_set_int(config_t *config, const char *section, const char *name, int64_t value) { struct dstr str; dstr_init(&str); dstr_printf(&str, "%lld", value); config_set_item(&config->sections, section, name, str.array); }
/* * List resolutions for device and format */ static void v4l2_resolution_list(int dev, uint_fast32_t pixelformat, obs_property_t *prop) { struct v4l2_frmsizeenum frmsize; frmsize.pixel_format = pixelformat; frmsize.index = 0; struct dstr buffer; dstr_init(&buffer); obs_property_list_clear(prop); obs_property_list_add_int(prop, obs_module_text("LeaveUnchanged"), -1); v4l2_ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &frmsize); switch(frmsize.type) { case V4L2_FRMSIZE_TYPE_DISCRETE: while (v4l2_ioctl(dev, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) { dstr_printf(&buffer, "%dx%d", frmsize.discrete.width, frmsize.discrete.height); obs_property_list_add_int(prop, buffer.array, v4l2_pack_tuple(frmsize.discrete.width, frmsize.discrete.height)); frmsize.index++; } break; default: blog(LOG_INFO, "Stepwise and Continuous framesizes " "are currently hardcoded"); for (const int *packed = v4l2_framesizes; *packed; ++packed) { int width; int height; v4l2_unpack_tuple(&width, &height, *packed); dstr_printf(&buffer, "%dx%d", width, height); obs_property_list_add_int(prop, buffer.array, *packed); } break; } dstr_free(&buffer); }
static void win32_log_interface_type(struct rtmp_stream *stream) { RTMP *rtmp = &stream->rtmp; MIB_IPFORWARDROW route; uint32_t dest_addr, source_addr; char hostname[256]; HOSTENT *h; if (rtmp->Link.hostname.av_len >= sizeof(hostname) - 1) return; strncpy(hostname, rtmp->Link.hostname.av_val, sizeof(hostname)); hostname[rtmp->Link.hostname.av_len] = 0; h = gethostbyname(hostname); if (!h) return; dest_addr = *(uint32_t*)h->h_addr_list[0]; if (rtmp->m_bindIP.addrLen == 0) source_addr = 0; else if (rtmp->m_bindIP.addr.ss_family = AF_INET) source_addr = (*(struct sockaddr_in*)&rtmp->m_bindIP) .sin_addr.S_un.S_addr; else return; if (!GetBestRoute(dest_addr, source_addr, &route)) { MIB_IFROW row; memset(&row, 0, sizeof(row)); row.dwIndex = route.dwForwardIfIndex; if (!GetIfEntry(&row)) { uint32_t speed =row.dwSpeed / 1000000; char *type; struct dstr other = {0}; if (row.dwType == IF_TYPE_ETHERNET_CSMACD) { type = "ethernet"; } else if (row.dwType == IF_TYPE_IEEE80211) { type = "802.11"; } else { dstr_printf(&other, "type %lu", row.dwType); type = other.array; } info("Interface: %s (%s, %lu mbps)", row.bDescr, type, speed); dstr_free(&other); } } }
/* ensures that names are never blank */ static inline char *dup_name(const char *name) { if (!name || !*name) { struct dstr unnamed = {0}; dstr_printf(&unnamed, "__unnamed%004lld", obs->data.unnamed_index++); return unnamed.array; } else { return bstrdup(name); } }
static inline void ca_warn(struct coreaudio_data *ca, const char *func, const char *format, ...) { va_list args; struct dstr str = {0}; va_start(args, format); dstr_printf(&str, "[%s]:[device '%s'] ", func, ca->device_name); dstr_vcatf(&str, format, args); blog(LOG_WARNING, "%s", str.array); dstr_free(&str); va_end(args); }
static inline void netif_saddr_data_push_back(struct netif_saddr_data *sd, const char *ip, const char *adapter) { struct netif_saddr_item item; struct dstr full_name = {0}; char *ip_dup = bstrdup(ip); if (adapter && *adapter) dstr_printf(&full_name, "[%s] %s", adapter, ip); else dstr_copy(&full_name, ip); item.name = full_name.array; item.addr = ip_dup; da_push_back(sd->addrs, &item); }
static char *get_new_source_name(const char *name) { struct dstr new_name = {0}; int inc = 0; dstr_copy(&new_name, name); for (;;) { obs_source_t *existing_source = obs_get_source_by_name( new_name.array); if (!existing_source) break; obs_source_release(existing_source); dstr_printf(&new_name, "%s %d", name, ++inc + 1); } return new_name.array; }
/* ugh, don't ask. I'll probably get rid of the need for this function later */ static void gl_rename_attributes(struct gl_shader_parser *glsp) { size_t i = 0, input_idx = 0, output_idx = 0; for (i = 0; i < glsp->attribs.num; i++) { struct gl_parser_attrib *attrib = glsp->attribs.array+i; struct dstr new_name = {0}; const char *prefix; size_t val; if (attrib->input) { prefix = glsp->input_prefix; val = input_idx++; } else { prefix = glsp->output_prefix; val = output_idx++; } dstr_printf(&new_name, "%s%u", prefix, val); dstr_replace(&glsp->gl_string, attrib->name.array, new_name.array); dstr_move(&attrib->name, &new_name); } }
static bool build_flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size, size_t a_idx) { obs_encoder_t *vencoder = obs_output_get_video_encoder(context); obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, a_idx); video_t *video = obs_encoder_video(vencoder); audio_t *audio = obs_encoder_audio(aencoder); char buf[4096]; char *enc = buf; char *end = enc+sizeof(buf); struct dstr encoder_name = {0}; if (a_idx > 0 && !aencoder) return false; enc_str(&enc, end, "onMetaData"); *enc++ = AMF_ECMA_ARRAY; enc = AMF_EncodeInt32(enc, end, a_idx == 0 ? 14 : 9); enc_num_val(&enc, end, "duration", 0.0); enc_num_val(&enc, end, "fileSize", 0.0); if (a_idx == 0) { enc_num_val(&enc, end, "width", (double)obs_encoder_get_width(vencoder)); enc_num_val(&enc, end, "height", (double)obs_encoder_get_height(vencoder)); enc_str_val(&enc, end, "videocodecid", "avc1"); enc_num_val(&enc, end, "videodatarate", encoder_bitrate(vencoder)); enc_num_val(&enc, end, "framerate", video_output_get_frame_rate(video)); } enc_str_val(&enc, end, "audiocodecid", "mp4a"); enc_num_val(&enc, end, "audiodatarate", encoder_bitrate(aencoder)); enc_num_val(&enc, end, "audiosamplerate", (double)obs_encoder_get_sample_rate(aencoder)); enc_num_val(&enc, end, "audiosamplesize", 16.0); enc_num_val(&enc, end, "audiochannels", (double)audio_output_get_channels(audio)); enc_bool_val(&enc, end, "stereo", audio_output_get_channels(audio) == 2); enc_bool_val(&enc, end, "2.1", audio_output_get_channels(audio) == 3); enc_bool_val(&enc, end, "3.1", audio_output_get_channels(audio) == 4); enc_bool_val(&enc, end, "4.0", audio_output_get_channels(audio) == 4); enc_bool_val(&enc, end, "4.1", audio_output_get_channels(audio) == 5); enc_bool_val(&enc, end, "5.1", audio_output_get_channels(audio) == 6); enc_bool_val(&enc, end, "7.1", audio_output_get_channels(audio) == 8); dstr_printf(&encoder_name, "%s (libobs version ", MODULE_NAME); #ifdef HAVE_OBSCONFIG_H dstr_cat(&encoder_name, OBS_VERSION); #else dstr_catf(&encoder_name, "%d.%d.%d", LIBOBS_API_MAJOR_VER, LIBOBS_API_MINOR_VER, LIBOBS_API_PATCH_VER); #endif dstr_cat(&encoder_name, ")"); enc_str_val(&enc, end, "encoder", encoder_name.array); dstr_free(&encoder_name); *enc++ = 0; *enc++ = 0; *enc++ = AMF_OBJECT_END; *size = enc-buf; *output = bmemdup(buf, *size); return true; }
static int init_send(struct rtmp_stream *stream) { int ret; size_t idx = 0; bool next = true; #if defined(_WIN32) adjust_sndbuf_size(stream, MIN_SENDBUF_SIZE); #endif reset_semaphore(stream); ret = pthread_create(&stream->send_thread, NULL, send_thread, stream); if (ret != 0) { RTMP_Close(&stream->rtmp); warn("Failed to create send thread"); return OBS_OUTPUT_ERROR; } if (stream->new_socket_loop) { int one = 1; #ifdef _WIN32 if (ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) { stream->rtmp.last_error_code = WSAGetLastError(); #else if (ioctl(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) { stream->rtmp.last_error_code = errno; #endif warn("Failed to set non-blocking socket"); return OBS_OUTPUT_ERROR; } os_event_reset(stream->send_thread_signaled_exit); info("New socket loop enabled by user"); if (stream->low_latency_mode) info("Low latency mode enabled by user"); if (stream->write_buf) bfree(stream->write_buf); int total_bitrate = 0; obs_output_t *context = stream->output; obs_encoder_t *vencoder = obs_output_get_video_encoder(context); if (vencoder) { obs_data_t *params = obs_encoder_get_settings(vencoder); if (params) { int bitrate = obs_data_get_int(params, "bitrate"); total_bitrate += bitrate; obs_data_release(params); } } obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 0); if (aencoder) { obs_data_t *params = obs_encoder_get_settings(aencoder); if (params) { int bitrate = obs_data_get_int(params, "bitrate"); total_bitrate += bitrate; obs_data_release(params); } } // to bytes/sec int ideal_buffer_size = total_bitrate * 128; if (ideal_buffer_size < 131072) ideal_buffer_size = 131072; stream->write_buf_size = ideal_buffer_size; stream->write_buf = bmalloc(ideal_buffer_size); #ifdef _WIN32 ret = pthread_create(&stream->socket_thread, NULL, socket_thread_windows, stream); #else warn("New socket loop not supported on this platform"); return OBS_OUTPUT_ERROR; #endif if (ret != 0) { RTMP_Close(&stream->rtmp); warn("Failed to create socket thread"); return OBS_OUTPUT_ERROR; } stream->socket_thread_active = true; stream->rtmp.m_bCustomSend = true; stream->rtmp.m_customSendFunc = socket_queue_data; stream->rtmp.m_customSendParam = stream; } os_atomic_set_bool(&stream->active, true); while (next) { if (!send_meta_data(stream, idx++, &next)) { warn("Disconnected while attempting to connect to " "server."); set_output_error(stream); return OBS_OUTPUT_DISCONNECTED; } } obs_output_begin_data_capture(stream->output, 0); return OBS_OUTPUT_SUCCESS; } #ifdef _WIN32 static void win32_log_interface_type(struct rtmp_stream *stream) { RTMP *rtmp = &stream->rtmp; MIB_IPFORWARDROW route; uint32_t dest_addr, source_addr; char hostname[256]; HOSTENT *h; if (rtmp->Link.hostname.av_len >= sizeof(hostname) - 1) return; strncpy(hostname, rtmp->Link.hostname.av_val, sizeof(hostname)); hostname[rtmp->Link.hostname.av_len] = 0; h = gethostbyname(hostname); if (!h) return; dest_addr = *(uint32_t*)h->h_addr_list[0]; if (rtmp->m_bindIP.addrLen == 0) source_addr = 0; else if (rtmp->m_bindIP.addr.ss_family == AF_INET) source_addr = (*(struct sockaddr_in*)&rtmp->m_bindIP) .sin_addr.S_un.S_addr; else return; if (!GetBestRoute(dest_addr, source_addr, &route)) { MIB_IFROW row; memset(&row, 0, sizeof(row)); row.dwIndex = route.dwForwardIfIndex; if (!GetIfEntry(&row)) { uint32_t speed =row.dwSpeed / 1000000; char *type; struct dstr other = {0}; if (row.dwType == IF_TYPE_ETHERNET_CSMACD) { type = "ethernet"; } else if (row.dwType == IF_TYPE_IEEE80211) { type = "802.11"; } else { dstr_printf(&other, "type %lu", row.dwType); type = other.array; } info("Interface: %s (%s, %lu mbps)", row.bDescr, type, speed); dstr_free(&other); } } }