New upstream version 18.0.1+dfsg1

This commit is contained in:
Sebastian Ramacher 2017-04-19 21:54:15 +02:00
parent 6efda2859e
commit f2cf6cce50
1337 changed files with 41178 additions and 84670 deletions

View file

@ -15,95 +15,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include <obs-module.h>
#include <obs-avc.h>
#include <util/platform.h>
#include <util/circlebuf.h>
#include <util/dstr.h>
#include <util/threading.h>
#include <inttypes.h>
#include "librtmp/rtmp.h"
#include "librtmp/log.h"
#include "flv-mux.h"
#include "net-if.h"
#ifdef _WIN32
#include <Iphlpapi.h>
#else
#include <sys/ioctl.h>
#endif
#define do_log(level, format, ...) \
blog(level, "[rtmp 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_PFRAME_DROP_THRESHOLD "pframe_drop_threshold_ms"
#define OPT_MAX_SHUTDOWN_TIME_SEC "max_shutdown_time_sec"
#define OPT_BIND_IP "bind_ip"
//#define TEST_FRAMEDROPS
#ifdef TEST_FRAMEDROPS
#define DROPTEST_MAX_KBPS 3000
#define DROPTEST_MAX_BYTES (DROPTEST_MAX_KBPS * 1000 / 8)
struct droptest_info {
uint64_t ts;
size_t size;
};
#endif
struct rtmp_stream {
obs_output_t *output;
pthread_mutex_t packets_mutex;
struct circlebuf packets;
bool sent_headers;
volatile bool connecting;
pthread_t connect_thread;
volatile bool active;
volatile bool disconnected;
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, key;
struct dstr username, password;
struct dstr encoder_name;
struct dstr bind_ip;
/* frame drop variables */
int64_t drop_threshold_usec;
int64_t min_drop_dts_usec;
int64_t pframe_drop_threshold_usec;
int64_t pframe_min_drop_dts_usec;
int min_priority;
int64_t last_dts_usec;
uint64_t total_bytes_sent;
int dropped_frames;
#ifdef TEST_FRAMEDROPS
struct circlebuf droptest_info;
size_t droptest_size;
#endif
RTMP rtmp;
};
#include "rtmp-stream.h"
static const char *rtmp_stream_getname(void *unused)
{
@ -134,7 +46,7 @@ static inline void free_packets(struct rtmp_stream *stream)
while (stream->packets.size) {
struct encoder_packet packet;
circlebuf_pop_front(&stream->packets, &packet, sizeof(packet));
obs_free_encoder_packet(&packet);
obs_encoder_packet_release(&packet);
}
pthread_mutex_unlock(&stream->packets_mutex);
}
@ -180,23 +92,30 @@ static void rtmp_stream_destroy(void *data)
}
}
if (stream) {
free_packets(stream);
dstr_free(&stream->path);
dstr_free(&stream->key);
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);
circlebuf_free(&stream->packets);
free_packets(stream);
dstr_free(&stream->path);
dstr_free(&stream->key);
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);
circlebuf_free(&stream->packets);
#ifdef TEST_FRAMEDROPS
circlebuf_free(&stream->droptest_info);
circlebuf_free(&stream->droptest_info);
#endif
bfree(stream);
}
os_event_destroy(stream->buffer_space_available_event);
os_event_destroy(stream->buffer_has_data_event);
os_event_destroy(stream->socket_available_event);
os_event_destroy(stream->send_thread_signaled_exit);
pthread_mutex_destroy(&stream->write_buf_mutex);
if (stream->write_buf)
bfree(stream->write_buf);
bfree(stream);
}
static void *rtmp_stream_create(obs_data_t *settings, obs_output_t *output)
@ -214,6 +133,32 @@ static void *rtmp_stream_create(obs_data_t *settings, obs_output_t *output)
if (os_event_init(&stream->stop_event, OS_EVENT_TYPE_MANUAL) != 0)
goto fail;
if (pthread_mutex_init(&stream->write_buf_mutex, NULL) != 0) {
warn("Failed to initialize write buffer mutex");
goto fail;
}
if (os_event_init(&stream->buffer_space_available_event,
OS_EVENT_TYPE_AUTO) != 0) {
warn("Failed to initialize write buffer event");
goto fail;
}
if (os_event_init(&stream->buffer_has_data_event,
OS_EVENT_TYPE_AUTO) != 0) {
warn("Failed to initialize data buffer event");
goto fail;
}
if (os_event_init(&stream->socket_available_event,
OS_EVENT_TYPE_AUTO) != 0) {
warn("Failed to initialize socket buffer event");
goto fail;
}
if (os_event_init(&stream->send_thread_signaled_exit,
OS_EVENT_TYPE_MANUAL) != 0) {
warn("Failed to initialize socket exit event");
goto fail;
}
UNUSED_PARAMETER(settings);
return stream;
@ -346,6 +291,38 @@ static void droptest_cap_data_rate(struct rtmp_stream *stream, size_t size)
}
#endif
static int socket_queue_data(RTMPSockBuf *sb, const char *data, int len, void *arg)
{
struct rtmp_stream *stream = arg;
retry_send:
if (!RTMP_IsConnected(&stream->rtmp))
return 0;
pthread_mutex_lock(&stream->write_buf_mutex);
if (stream->write_buf_len + len > stream->write_buf_size) {
pthread_mutex_unlock(&stream->write_buf_mutex);
if (os_event_wait(stream->buffer_space_available_event)) {
return 0;
}
goto retry_send;
}
memcpy(stream->write_buf + stream->write_buf_len, data, len);
stream->write_buf_len += len;
pthread_mutex_unlock(&stream->write_buf_mutex);
os_event_signal (stream->buffer_has_data_event);
return len;
}
static int send_packet(struct rtmp_stream *stream,
struct encoder_packet *packet, bool is_header, size_t idx)
{
@ -354,16 +331,18 @@ static int send_packet(struct rtmp_stream *stream,
int recv_size = 0;
int ret = 0;
if (!stream->new_socket_loop) {
#ifdef _WIN32
ret = ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONREAD,
(u_long*)&recv_size);
ret = ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONREAD,
(u_long*)&recv_size);
#else
ret = ioctl(stream->rtmp.m_sb.sb_socket, FIONREAD, &recv_size);
ret = ioctl(stream->rtmp.m_sb.sb_socket, FIONREAD, &recv_size);
#endif
if (ret >= 0 && recv_size > 0) {
if (!discard_recv_data(stream, (size_t)recv_size))
return -1;
if (ret >= 0 && recv_size > 0) {
if (!discard_recv_data(stream, (size_t)recv_size))
return -1;
}
}
flv_packet_mux(packet, &data, &size, is_header);
@ -375,7 +354,10 @@ static int send_packet(struct rtmp_stream *stream,
ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size, (int)idx);
bfree(data);
obs_free_encoder_packet(packet);
if (is_header)
bfree(packet->data);
else
obs_encoder_packet_release(packet);
stream->total_bytes_sent += size;
return ret;
@ -414,7 +396,7 @@ static void *send_thread(void *data)
if (stopping(stream)) {
if (can_shutdown_stream(stream, &packet)) {
obs_free_encoder_packet(&packet);
obs_encoder_packet_release(&packet);
break;
}
}
@ -438,6 +420,14 @@ static void *send_thread(void *data)
info("User stopped the stream");
}
if (stream->new_socket_loop) {
os_event_signal(stream->send_thread_signaled_exit);
os_event_signal(stream->buffer_has_data_event);
pthread_join(stream->socket_thread, NULL);
stream->socket_thread_active = false;
stream->rtmp.m_bCustomSend = false;
}
RTMP_Close(&stream->rtmp);
if (!stopping(stream)) {
@ -577,6 +567,78 @@ static int init_send(struct rtmp_stream *stream)
return OBS_OUTPUT_ERROR;
}
if (stream->new_socket_loop) {
int one = 1;
#ifdef _WIN32
if (ioctlsocket(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
#else
if (ioctl(stream->rtmp.m_sb.sb_socket, FIONBIO, &one)) {
#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)) {
@ -676,8 +738,11 @@ static int try_connect(struct rtmp_stream *stream)
bool success = netif_str_to_addr(&stream->rtmp.m_bindIP.addr,
&stream->rtmp.m_bindIP.addrLen,
stream->bind_ip.array);
if (success)
info("Binding to IP");
if (success) {
int len = stream->rtmp.m_bindIP.addrLen;
bool ipv6 = len == sizeof(struct sockaddr_in6);
info("Binding to IPv%d", ipv6 ? 6 : 4);
}
}
RTMP_AddStream(&stream->rtmp, stream->key.array);
@ -720,8 +785,9 @@ static bool init_connect(struct rtmp_stream *stream)
int64_t drop_p;
int64_t drop_b;
if (stopping(stream))
if (stopping(stream)) {
pthread_join(stream->send_thread, NULL);
}
free_packets(stream);
@ -756,6 +822,11 @@ static bool init_connect(struct rtmp_stream *stream)
bind_ip = obs_data_get_string(settings, OPT_BIND_IP);
dstr_copy(&stream->bind_ip, bind_ip);
stream->new_socket_loop = obs_data_get_bool(settings,
OPT_NEWSOCKETLOOP_ENABLED);
stream->low_latency_mode = obs_data_get_bool(settings,
OPT_LOWLATENCY_ENABLED);
obs_data_release(settings);
return true;
}
@ -842,7 +913,7 @@ static void drop_frames(struct rtmp_stream *stream, const char *name,
} else {
num_frames_dropped++;
obs_free_encoder_packet(&packet);
obs_encoder_packet_release(&packet);
}
}
@ -878,8 +949,11 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
stream->pframe_drop_threshold_usec :
stream->drop_threshold_usec;
if (num_packets < 5)
if (num_packets < 5) {
if (!pframes)
stream->congestion = 0.0f;
return;
}
circlebuf_peek_front(&stream->packets, &first, sizeof(first));
@ -891,8 +965,13 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
* 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: %lld", buffer_duration_usec);
debug("buffer_duration_usec: %" PRId64, buffer_duration_usec);
drop_frames(stream, name, priority, p_min_dts_usec);
}
}
@ -905,7 +984,7 @@ static bool add_video_packet(struct rtmp_stream *stream,
/* if currently dropping frames, drop packets until it reaches the
* desired priority */
if (packet->priority < stream->min_priority) {
if (packet->drop_priority < stream->min_priority) {
stream->dropped_frames++;
return false;
} else {
@ -927,7 +1006,7 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet)
if (packet->type == OBS_ENCODER_VIDEO)
obs_parse_avc_packet(&new_packet, packet);
else
obs_duplicate_encoder_packet(&new_packet, packet);
obs_encoder_packet_ref(&new_packet, packet);
pthread_mutex_lock(&stream->packets_mutex);
@ -942,15 +1021,17 @@ static void rtmp_stream_data(void *data, struct encoder_packet *packet)
if (added_packet)
os_sem_post(stream->send_sem);
else
obs_free_encoder_packet(&new_packet);
obs_encoder_packet_release(&new_packet);
}
static void rtmp_stream_defaults(obs_data_t *defaults)
{
obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 500);
obs_data_set_default_int(defaults, OPT_PFRAME_DROP_THRESHOLD, 800);
obs_data_set_default_int(defaults, OPT_DROP_THRESHOLD, 700);
obs_data_set_default_int(defaults, OPT_PFRAME_DROP_THRESHOLD, 900);
obs_data_set_default_int(defaults, OPT_MAX_SHUTDOWN_TIME_SEC, 30);
obs_data_set_default_string(defaults, OPT_BIND_IP, "default");
obs_data_set_default_bool(defaults, OPT_NEWSOCKETLOOP_ENABLED, false);
obs_data_set_default_bool(defaults, OPT_LOWLATENCY_ENABLED, false);
}
static obs_properties_t *rtmp_stream_properties(void *unused)
@ -978,6 +1059,11 @@ static obs_properties_t *rtmp_stream_properties(void *unused)
}
netif_saddr_data_free(&addrs);
obs_properties_add_bool(props, OPT_NEWSOCKETLOOP_ENABLED,
obs_module_text("RTMPStream.NewSocketLoop"));
obs_properties_add_bool(props, OPT_LOWLATENCY_ENABLED,
obs_module_text("RTMPStream.LowLatencyMode"));
return props;
}
@ -993,6 +1079,17 @@ static int rtmp_stream_dropped_frames(void *data)
return stream->dropped_frames;
}
static float rtmp_stream_congestion(void *data)
{
struct rtmp_stream *stream = data;
if (stream->new_socket_loop)
return (float)stream->write_buf_len /
(float)stream->write_buf_size;
else
return stream->min_priority > 0 ? 1.0f : stream->congestion;
}
struct obs_output_info rtmp_output_info = {
.id = "rtmp_output",
.flags = OBS_OUTPUT_AV |
@ -1008,5 +1105,6 @@ struct obs_output_info rtmp_output_info = {
.get_defaults = rtmp_stream_defaults,
.get_properties = rtmp_stream_properties,
.get_total_bytes = rtmp_stream_total_bytes_sent,
.get_congestion = rtmp_stream_congestion,
.get_dropped_frames = rtmp_stream_dropped_frames
};