clang-format: Apply formatting
Code submissions have continually suffered from formatting inconsistencies that constantly have to be addressed. Using clang-format simplifies this by making code formatting more consistent, and allows automation of the code formatting so that maintainers can focus more on the code itself instead of code formatting.
This commit is contained in:
@@ -56,16 +56,16 @@ void write_file_info(FILE *file, int64_t duration_ms, int64_t size)
|
||||
fwrite(buf, 1, enc - buf, file);
|
||||
}
|
||||
|
||||
static bool build_flv_meta_data(obs_output_t *context,
|
||||
uint8_t **output, size_t *size, size_t a_idx)
|
||||
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);
|
||||
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);
|
||||
char *end = enc + sizeof(buf);
|
||||
struct dstr encoder_name = {0};
|
||||
|
||||
if (a_idx > 0 && !aencoder)
|
||||
@@ -74,57 +74,48 @@ static bool build_flv_meta_data(obs_output_t *context,
|
||||
enc_str(&enc, end, "onMetaData");
|
||||
|
||||
*enc++ = AMF_ECMA_ARRAY;
|
||||
enc = AMF_EncodeInt32(enc, end, a_idx == 0 ? 20 : 15);
|
||||
enc = AMF_EncodeInt32(enc, end, a_idx == 0 ? 20 : 15);
|
||||
|
||||
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));
|
||||
(double)obs_encoder_get_width(vencoder));
|
||||
enc_num_val(&enc, end, "height",
|
||||
(double)obs_encoder_get_height(vencoder));
|
||||
(double)obs_encoder_get_height(vencoder));
|
||||
|
||||
enc_str_val(&enc, end, "videocodecid", "avc1");
|
||||
enc_num_val(&enc, end, "videodatarate",
|
||||
encoder_bitrate(vencoder));
|
||||
encoder_bitrate(vencoder));
|
||||
enc_num_val(&enc, end, "framerate",
|
||||
video_output_get_frame_rate(video));
|
||||
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));
|
||||
(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));
|
||||
(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);
|
||||
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);
|
||||
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);
|
||||
dstr_catf(&encoder_name, "%d.%d.%d", LIBOBS_API_MAJOR_VER,
|
||||
LIBOBS_API_MINOR_VER, LIBOBS_API_PATCH_VER);
|
||||
#endif
|
||||
|
||||
dstr_cat(&encoder_name, ")");
|
||||
@@ -132,28 +123,28 @@ static bool build_flv_meta_data(obs_output_t *context,
|
||||
enc_str_val(&enc, end, "encoder", encoder_name.array);
|
||||
dstr_free(&encoder_name);
|
||||
|
||||
*enc++ = 0;
|
||||
*enc++ = 0;
|
||||
*enc++ = AMF_OBJECT_END;
|
||||
*enc++ = 0;
|
||||
*enc++ = 0;
|
||||
*enc++ = AMF_OBJECT_END;
|
||||
|
||||
*size = enc-buf;
|
||||
*size = enc - buf;
|
||||
*output = bmemdup(buf, *size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
|
||||
bool write_header, size_t audio_idx)
|
||||
bool write_header, size_t audio_idx)
|
||||
{
|
||||
struct array_output_data data;
|
||||
struct serializer s;
|
||||
uint8_t *meta_data = NULL;
|
||||
size_t meta_data_size;
|
||||
size_t meta_data_size;
|
||||
uint32_t start_pos;
|
||||
|
||||
array_output_serializer_init(&s, &data);
|
||||
|
||||
if (!build_flv_meta_data(context, &meta_data, &meta_data_size,
|
||||
audio_idx)) {
|
||||
audio_idx)) {
|
||||
bfree(meta_data);
|
||||
return false;
|
||||
}
|
||||
@@ -179,7 +170,7 @@ bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
|
||||
s_wb32(&s, (uint32_t)serializer_get_pos(&s) - start_pos - 1);
|
||||
|
||||
*output = data.bytes.array;
|
||||
*size = data.bytes.num;
|
||||
*size = data.bytes.num;
|
||||
|
||||
bfree(meta_data);
|
||||
return true;
|
||||
@@ -190,9 +181,9 @@ static int32_t last_time = 0;
|
||||
#endif
|
||||
|
||||
static void flv_video(struct serializer *s, int32_t dts_offset,
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
{
|
||||
int64_t offset = packet->pts - packet->dts;
|
||||
int64_t offset = packet->pts - packet->dts;
|
||||
int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset;
|
||||
|
||||
if (!packet->data || !packet->size)
|
||||
@@ -225,7 +216,7 @@ static void flv_video(struct serializer *s, int32_t dts_offset,
|
||||
}
|
||||
|
||||
static void flv_audio(struct serializer *s, int32_t dts_offset,
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
{
|
||||
int32_t time_ms = get_ms_time(packet, packet->dts) - dts_offset;
|
||||
|
||||
@@ -258,7 +249,7 @@ static void flv_audio(struct serializer *s, int32_t dts_offset,
|
||||
}
|
||||
|
||||
void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset,
|
||||
uint8_t **output, size_t *size, bool is_header)
|
||||
uint8_t **output, size_t *size, bool is_header)
|
||||
{
|
||||
struct array_output_data data;
|
||||
struct serializer s;
|
||||
@@ -271,5 +262,5 @@ void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset,
|
||||
flv_audio(&s, dts_offset, packet, is_header);
|
||||
|
||||
*output = data.bytes.array;
|
||||
*size = data.bytes.num;
|
||||
*size = data.bytes.num;
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
#include <obs.h>
|
||||
|
||||
#define MILLISECOND_DEN 1000
|
||||
#define MILLISECOND_DEN 1000
|
||||
|
||||
static int32_t get_ms_time(struct encoder_packet *packet, int64_t val)
|
||||
{
|
||||
@@ -29,6 +29,6 @@ static int32_t get_ms_time(struct encoder_packet *packet, int64_t val)
|
||||
extern void write_file_info(FILE *file, int64_t duration_ms, int64_t size);
|
||||
|
||||
extern bool flv_meta_data(obs_output_t *context, uint8_t **output, size_t *size,
|
||||
bool write_header, size_t audio_idx);
|
||||
bool write_header, size_t audio_idx);
|
||||
extern void flv_packet_mux(struct encoder_packet *packet, int32_t dts_offset,
|
||||
uint8_t **output, size_t *size, bool is_header);
|
||||
uint8_t **output, size_t *size, bool is_header);
|
||||
|
@@ -24,27 +24,27 @@
|
||||
#include <inttypes.h>
|
||||
#include "flv-mux.h"
|
||||
|
||||
#define do_log(level, format, ...) \
|
||||
#define do_log(level, format, ...) \
|
||||
blog(level, "[flv output: '%s'] " format, \
|
||||
obs_output_get_name(stream->output), ##__VA_ARGS__)
|
||||
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 warn(format, ...) do_log(LOG_WARNING, format, ##__VA_ARGS__)
|
||||
#define info(format, ...) do_log(LOG_INFO, format, ##__VA_ARGS__)
|
||||
|
||||
struct flv_output {
|
||||
obs_output_t *output;
|
||||
struct dstr path;
|
||||
FILE *file;
|
||||
volatile bool active;
|
||||
volatile bool stopping;
|
||||
uint64_t stop_ts;
|
||||
bool sent_headers;
|
||||
int64_t last_packet_ts;
|
||||
obs_output_t *output;
|
||||
struct dstr path;
|
||||
FILE *file;
|
||||
volatile bool active;
|
||||
volatile bool stopping;
|
||||
uint64_t stop_ts;
|
||||
bool sent_headers;
|
||||
int64_t last_packet_ts;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
bool got_first_video;
|
||||
int32_t start_dts_offset;
|
||||
bool got_first_video;
|
||||
int32_t start_dts_offset;
|
||||
};
|
||||
|
||||
static inline bool stopping(struct flv_output *stream)
|
||||
@@ -85,16 +85,16 @@ static void *flv_output_create(obs_data_t *settings, obs_output_t *output)
|
||||
}
|
||||
|
||||
static int write_packet(struct flv_output *stream,
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
int ret = 0;
|
||||
size_t size;
|
||||
int ret = 0;
|
||||
|
||||
stream->last_packet_ts = get_ms_time(packet, packet->dts);
|
||||
|
||||
flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset,
|
||||
&data, &size, is_header);
|
||||
flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset, &data,
|
||||
&size, is_header);
|
||||
fwrite(data, 1, size, stream->file);
|
||||
bfree(data);
|
||||
|
||||
@@ -104,7 +104,7 @@ static int write_packet(struct flv_output *stream,
|
||||
static void write_meta_data(struct flv_output *stream)
|
||||
{
|
||||
uint8_t *meta_data;
|
||||
size_t meta_data_size;
|
||||
size_t meta_data_size;
|
||||
|
||||
flv_meta_data(stream->output, &meta_data, &meta_data_size, true, 0);
|
||||
fwrite(meta_data, 1, meta_data_size, stream->file);
|
||||
@@ -113,13 +113,11 @@ static void write_meta_data(struct flv_output *stream)
|
||||
|
||||
static void write_audio_header(struct flv_output *stream)
|
||||
{
|
||||
obs_output_t *context = stream->output;
|
||||
obs_output_t *context = stream->output;
|
||||
obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 0);
|
||||
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_AUDIO,
|
||||
.timebase_den = 1
|
||||
};
|
||||
struct encoder_packet packet = {.type = OBS_ENCODER_AUDIO,
|
||||
.timebase_den = 1};
|
||||
|
||||
obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size);
|
||||
write_packet(stream, &packet, true);
|
||||
@@ -127,16 +125,13 @@ static void write_audio_header(struct flv_output *stream)
|
||||
|
||||
static void write_video_header(struct flv_output *stream)
|
||||
{
|
||||
obs_output_t *context = stream->output;
|
||||
obs_output_t *context = stream->output;
|
||||
obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
|
||||
uint8_t *header;
|
||||
size_t size;
|
||||
uint8_t *header;
|
||||
size_t size;
|
||||
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_VIDEO,
|
||||
.timebase_den = 1,
|
||||
.keyframe = true
|
||||
};
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_VIDEO, .timebase_den = 1, .keyframe = true};
|
||||
|
||||
obs_encoder_get_extra_data(vencoder, &header, &size);
|
||||
packet.size = obs_parse_avc_header(&packet.data, header, size);
|
||||
@@ -214,7 +209,7 @@ static void flv_output_actual_stop(struct flv_output *stream, int code)
|
||||
|
||||
static void flv_output_data(void *data, struct encoder_packet *packet)
|
||||
{
|
||||
struct flv_output *stream = data;
|
||||
struct flv_output *stream = data;
|
||||
struct encoder_packet parsed_packet;
|
||||
|
||||
pthread_mutex_lock(&stream->mutex);
|
||||
@@ -264,21 +259,21 @@ static obs_properties_t *flv_output_properties(void *unused)
|
||||
obs_properties_t *props = obs_properties_create();
|
||||
|
||||
obs_properties_add_text(props, "path",
|
||||
obs_module_text("FLVOutput.FilePath"),
|
||||
OBS_TEXT_DEFAULT);
|
||||
obs_module_text("FLVOutput.FilePath"),
|
||||
OBS_TEXT_DEFAULT);
|
||||
return props;
|
||||
}
|
||||
|
||||
struct obs_output_info flv_output_info = {
|
||||
.id = "flv_output",
|
||||
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED,
|
||||
.id = "flv_output",
|
||||
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED,
|
||||
.encoded_video_codecs = "h264",
|
||||
.encoded_audio_codecs = "aac",
|
||||
.get_name = flv_output_getname,
|
||||
.create = flv_output_create,
|
||||
.destroy = flv_output_destroy,
|
||||
.start = flv_output_start,
|
||||
.stop = flv_output_stop,
|
||||
.encoded_packet = flv_output_data,
|
||||
.get_properties = flv_output_properties
|
||||
.get_name = flv_output_getname,
|
||||
.create = flv_output_create,
|
||||
.destroy = flv_output_destroy,
|
||||
.start = flv_output_start,
|
||||
.stop = flv_output_stop,
|
||||
.encoded_packet = flv_output_data,
|
||||
.get_properties = flv_output_properties,
|
||||
};
|
||||
|
@@ -33,13 +33,13 @@
|
||||
#define INFINITE 0xFFFFFFFF
|
||||
#endif
|
||||
|
||||
#define do_log(level, format, ...) \
|
||||
#define do_log(level, format, ...) \
|
||||
blog(level, "[ftl stream: '%s'] " format, \
|
||||
obs_output_get_name(stream->output), ##__VA_ARGS__)
|
||||
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 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"
|
||||
@@ -59,56 +59,56 @@ typedef struct _frame_of_nalus_t {
|
||||
} frame_of_nalus_t;
|
||||
|
||||
struct ftl_stream {
|
||||
obs_output_t *output;
|
||||
obs_output_t *output;
|
||||
|
||||
pthread_mutex_t packets_mutex;
|
||||
pthread_mutex_t packets_mutex;
|
||||
struct circlebuf packets;
|
||||
bool sent_headers;
|
||||
int64_t frames_sent;
|
||||
bool sent_headers;
|
||||
int64_t frames_sent;
|
||||
|
||||
volatile bool connecting;
|
||||
pthread_t connect_thread;
|
||||
pthread_t status_thread;
|
||||
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;
|
||||
volatile bool active;
|
||||
volatile bool disconnected;
|
||||
volatile bool encode_error;
|
||||
pthread_t send_thread;
|
||||
|
||||
int max_shutdown_time_sec;
|
||||
int max_shutdown_time_sec;
|
||||
|
||||
os_sem_t *send_sem;
|
||||
os_event_t *stop_event;
|
||||
uint64_t stop_ts;
|
||||
uint64_t shutdown_timeout_ts;
|
||||
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;
|
||||
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 drop_threshold_usec;
|
||||
int64_t pframe_drop_threshold_usec;
|
||||
int min_priority;
|
||||
float congestion;
|
||||
|
||||
int64_t last_dts_usec;
|
||||
int64_t last_dts_usec;
|
||||
|
||||
uint64_t total_bytes_sent;
|
||||
uint64_t dropped_frames;
|
||||
uint64_t last_nack_count;
|
||||
uint64_t total_bytes_sent;
|
||||
uint64_t dropped_frames;
|
||||
uint64_t last_nack_count;
|
||||
|
||||
ftl_handle_t ftl_handle;
|
||||
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;
|
||||
int peak_kbps;
|
||||
uint32_t scale_width, scale_height, width, height;
|
||||
frame_of_nalus_t coded_pic_buffer;
|
||||
};
|
||||
|
||||
static void log_libftl_messages(ftl_log_severity_t log_level,
|
||||
const char *message);
|
||||
const char *message);
|
||||
static int init_connect(struct ftl_stream *stream);
|
||||
static void *connect_thread(void *data);
|
||||
static void *status_thread(void *data);
|
||||
@@ -261,7 +261,8 @@ static void ftl_stream_stop(void *data, uint64_t ts)
|
||||
stream->stop_ts = ts / 1000ULL;
|
||||
|
||||
if (ts) {
|
||||
stream->shutdown_timeout_ts = ts +
|
||||
stream->shutdown_timeout_ts =
|
||||
ts +
|
||||
(uint64_t)stream->max_shutdown_time_sec * 1000000000ULL;
|
||||
}
|
||||
|
||||
@@ -275,14 +276,14 @@ static void ftl_stream_stop(void *data, uint64_t ts)
|
||||
}
|
||||
|
||||
static inline bool get_next_packet(struct ftl_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
struct encoder_packet *packet)
|
||||
{
|
||||
bool new_packet = false;
|
||||
|
||||
pthread_mutex_lock(&stream->packets_mutex);
|
||||
if (stream->packets.size) {
|
||||
circlebuf_pop_front(&stream->packets, packet,
|
||||
sizeof(struct encoder_packet));
|
||||
sizeof(struct encoder_packet));
|
||||
new_packet = true;
|
||||
}
|
||||
pthread_mutex_unlock(&stream->packets_mutex);
|
||||
@@ -291,7 +292,7 @@ static inline bool get_next_packet(struct ftl_stream *stream,
|
||||
}
|
||||
|
||||
static int avc_get_video_frame(struct ftl_stream *stream,
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
{
|
||||
int consumed = 0;
|
||||
int len = (int)packet->size;
|
||||
@@ -301,15 +302,15 @@ static int avc_get_video_frame(struct ftl_stream *stream,
|
||||
|
||||
while ((size_t)consumed < packet->size) {
|
||||
size_t total_max = sizeof(stream->coded_pic_buffer.nalus) /
|
||||
sizeof(stream->coded_pic_buffer.nalus[0]);
|
||||
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];
|
||||
nalu = &stream->coded_pic_buffer
|
||||
.nalus[stream->coded_pic_buffer.total];
|
||||
|
||||
if (is_header) {
|
||||
if (consumed == 0) {
|
||||
@@ -327,16 +328,13 @@ static int avc_get_video_frame(struct ftl_stream *stream,
|
||||
video_stream += 2;
|
||||
consumed += 2;
|
||||
} else {
|
||||
len = video_stream[0] << 24 |
|
||||
video_stream[1] << 16 |
|
||||
video_stream[2] << 8 |
|
||||
video_stream[3];
|
||||
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));
|
||||
"has %d left",
|
||||
len, (int)(packet->size - consumed));
|
||||
}
|
||||
|
||||
consumed += 4;
|
||||
@@ -367,8 +365,8 @@ static int avc_get_video_frame(struct ftl_stream *stream,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_packet(struct ftl_stream *stream,
|
||||
struct encoder_packet *packet, bool is_header)
|
||||
static int send_packet(struct ftl_stream *stream, struct encoder_packet *packet,
|
||||
bool is_header)
|
||||
{
|
||||
int bytes_sent = 0;
|
||||
int ret = 0;
|
||||
@@ -381,12 +379,9 @@ static int send_packet(struct ftl_stream *stream,
|
||||
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);
|
||||
&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++;
|
||||
@@ -395,19 +390,15 @@ static int send_packet(struct ftl_stream *stream,
|
||||
|
||||
} 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);
|
||||
&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 {
|
||||
} else {
|
||||
obs_encoder_packet_release(packet);
|
||||
}
|
||||
|
||||
@@ -422,58 +413,56 @@ static void set_peak_bitrate(struct ftl_stream *stream)
|
||||
speed_test_t results;
|
||||
ftl_status_t status_code;
|
||||
|
||||
status_code = ftl_ingest_speed_test_ex(
|
||||
&stream->ftl_handle,
|
||||
speedtest_kbps,
|
||||
speedtest_duration,
|
||||
&results);
|
||||
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;
|
||||
(float)results.pkts_sent;
|
||||
} else {
|
||||
warn("Speed test failed with: %s",
|
||||
ftl_status_code_to_string(status_code));
|
||||
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_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");
|
||||
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);
|
||||
"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;
|
||||
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)
|
||||
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);
|
||||
stream->max_shutdown_time_sec);
|
||||
|
||||
return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts;
|
||||
}
|
||||
@@ -552,17 +541,15 @@ static void *send_thread(void *data)
|
||||
|
||||
static bool send_video_header(struct ftl_stream *stream, int64_t dts_usec)
|
||||
{
|
||||
obs_output_t *context = stream->output;
|
||||
obs_output_t *context = stream->output;
|
||||
obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
|
||||
uint8_t *header;
|
||||
size_t size;
|
||||
uint8_t *header;
|
||||
size_t size;
|
||||
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_VIDEO,
|
||||
.timebase_den = 1,
|
||||
.keyframe = true,
|
||||
.dts_usec = dts_usec
|
||||
};
|
||||
struct encoder_packet packet = {.type = OBS_ENCODER_VIDEO,
|
||||
.timebase_den = 1,
|
||||
.keyframe = true,
|
||||
.dts_usec = dts_usec};
|
||||
|
||||
obs_encoder_get_extra_data(vencoder, &header, &size);
|
||||
packet.size = obs_parse_avc_header(&packet.data, header, size);
|
||||
@@ -626,12 +613,12 @@ static int try_connect(struct ftl_stream *stream)
|
||||
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));
|
||||
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);
|
||||
ftl_status_code_to_string(status_code),
|
||||
status_code);
|
||||
return _ftl_error_to_obs_error(status_code);
|
||||
}
|
||||
}
|
||||
@@ -653,7 +640,8 @@ static bool ftl_stream_start(void *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_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);
|
||||
@@ -669,14 +657,14 @@ static bool ftl_stream_start(void *data)
|
||||
os_atomic_set_bool(&stream->connecting, true);
|
||||
|
||||
return pthread_create(&stream->connect_thread, NULL, connect_thread,
|
||||
stream) == 0;
|
||||
stream) == 0;
|
||||
}
|
||||
|
||||
static inline bool add_packet(struct ftl_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
struct encoder_packet *packet)
|
||||
{
|
||||
circlebuf_push_back(&stream->packets, packet,
|
||||
sizeof(struct encoder_packet));
|
||||
sizeof(struct encoder_packet));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -686,12 +674,12 @@ static inline size_t num_buffered_packets(struct ftl_stream *stream)
|
||||
}
|
||||
|
||||
static void drop_frames(struct ftl_stream *stream, const char *name,
|
||||
int highest_priority, bool pframes)
|
||||
int highest_priority, bool pframes)
|
||||
{
|
||||
UNUSED_PARAMETER(pframes);
|
||||
|
||||
struct circlebuf new_buf = {0};
|
||||
int num_frames_dropped = 0;
|
||||
struct circlebuf new_buf = {0};
|
||||
int num_frames_dropped = 0;
|
||||
|
||||
#ifdef _DEBUG
|
||||
int start_packets = (int)num_buffered_packets(stream);
|
||||
@@ -706,7 +694,7 @@ static void drop_frames(struct ftl_stream *stream, const char *name,
|
||||
circlebuf_pop_front(&stream->packets, &packet, sizeof(packet));
|
||||
|
||||
/* do not drop audio data or video keyframes */
|
||||
if (packet.type == OBS_ENCODER_AUDIO ||
|
||||
if (packet.type == OBS_ENCODER_AUDIO ||
|
||||
packet.drop_priority >= highest_priority) {
|
||||
circlebuf_push_back(&new_buf, &packet, sizeof(packet));
|
||||
|
||||
@@ -726,21 +714,19 @@ static void drop_frames(struct ftl_stream *stream, const char *name,
|
||||
|
||||
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));
|
||||
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)
|
||||
struct encoder_packet *first)
|
||||
{
|
||||
size_t count = stream->packets.size / sizeof(*first);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
struct encoder_packet *cur = circlebuf_data(&stream->packets,
|
||||
i * sizeof(*first));
|
||||
struct encoder_packet *cur =
|
||||
circlebuf_data(&stream->packets, i * sizeof(*first));
|
||||
if (cur->type == OBS_ENCODER_VIDEO && !cur->keyframe) {
|
||||
*first = *cur;
|
||||
return true;
|
||||
@@ -756,11 +742,10 @@ static void check_to_drop_frames(struct ftl_stream *stream, bool pframes)
|
||||
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;
|
||||
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)
|
||||
@@ -776,8 +761,8 @@ static void check_to_drop_frames(struct ftl_stream *stream, bool pframes)
|
||||
buffer_duration_usec = stream->last_dts_usec - first.dts_usec;
|
||||
|
||||
if (!pframes) {
|
||||
stream->congestion = (float)buffer_duration_usec /
|
||||
(float)drop_threshold;
|
||||
stream->congestion =
|
||||
(float)buffer_duration_usec / (float)drop_threshold;
|
||||
}
|
||||
|
||||
if (buffer_duration_usec > drop_threshold) {
|
||||
@@ -787,7 +772,7 @@ static void check_to_drop_frames(struct ftl_stream *stream, bool pframes)
|
||||
}
|
||||
|
||||
static bool add_video_packet(struct ftl_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
struct encoder_packet *packet)
|
||||
{
|
||||
check_to_drop_frames(stream, false);
|
||||
check_to_drop_frames(stream, true);
|
||||
@@ -805,13 +790,12 @@ static bool add_video_packet(struct ftl_stream *stream,
|
||||
return add_packet(stream, packet);
|
||||
}
|
||||
|
||||
|
||||
static void ftl_stream_data(void *data, struct encoder_packet *packet)
|
||||
{
|
||||
struct ftl_stream *stream = data;
|
||||
struct ftl_stream *stream = data;
|
||||
|
||||
struct encoder_packet new_packet;
|
||||
bool added_packet = false;
|
||||
bool added_packet = false;
|
||||
|
||||
if (disconnected(stream) || !active(stream))
|
||||
return;
|
||||
@@ -831,9 +815,9 @@ static void ftl_stream_data(void *data, struct encoder_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);
|
||||
added_packet = (packet->type == OBS_ENCODER_VIDEO)
|
||||
? add_video_packet(stream, &new_packet)
|
||||
: add_packet(stream, &new_packet);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stream->packets_mutex);
|
||||
@@ -849,20 +833,18 @@ 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);
|
||||
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;
|
||||
@@ -889,13 +871,13 @@ enum ret_type {
|
||||
};
|
||||
|
||||
static enum ret_type ftl_event(struct ftl_stream *stream,
|
||||
ftl_status_msg_t status)
|
||||
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));
|
||||
ftl_status_code_to_string(status.msg.event.error_code));
|
||||
|
||||
if (status.msg.event.reason == FTL_STATUS_EVENT_REASON_API_REQUEST) {
|
||||
return RET_BREAK;
|
||||
@@ -916,7 +898,7 @@ static void *status_thread(void *data)
|
||||
|
||||
while (!disconnected(stream)) {
|
||||
status_code = ftl_ingest_get_status(&stream->ftl_handle,
|
||||
&status, 1000);
|
||||
&status, 1000);
|
||||
|
||||
if (status_code == FTL_STATUS_TIMEOUT ||
|
||||
status_code == FTL_QUEUE_EMPTY) {
|
||||
@@ -932,24 +914,25 @@ static void *status_thread(void *data)
|
||||
else if (ret_type == RET_BREAK)
|
||||
break;
|
||||
|
||||
} else if(status.type == FTL_STATUS_LOG) {
|
||||
} else if (status.type == FTL_STATUS_LOG) {
|
||||
blog(LOG_INFO, "[%d] %s", status.msg.log.log_level,
|
||||
status.msg.log.string);
|
||||
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;
|
||||
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);
|
||||
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 =
|
||||
@@ -957,34 +940,38 @@ static void *status_thread(void *data)
|
||||
|
||||
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);
|
||||
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;
|
||||
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);
|
||||
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: Got Status message of type "
|
||||
"%d",
|
||||
status.type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1022,7 +1009,7 @@ static void *connect_thread(void *data)
|
||||
}
|
||||
|
||||
static void log_libftl_messages(ftl_log_severity_t log_level,
|
||||
const char * message)
|
||||
const char *message)
|
||||
{
|
||||
UNUSED_PARAMETER(log_level);
|
||||
blog(LOG_WARNING, "[libftl] %s", message);
|
||||
@@ -1049,15 +1036,14 @@ static int init_connect(struct ftl_stream *stream)
|
||||
|
||||
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;
|
||||
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);
|
||||
obs_data_t *video_settings = obs_encoder_get_settings(video_encoder);
|
||||
|
||||
dstr_copy(&stream->path, obs_service_get_url(service));
|
||||
key = obs_service_get_key(service);
|
||||
@@ -1070,14 +1056,14 @@ static int init_connect(struct ftl_stream *stream)
|
||||
peak_bitrate = target_bitrate;
|
||||
}
|
||||
|
||||
stream->params.stream_key = (char*)key;
|
||||
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_VERSION;
|
||||
stream->params.peak_kbps =
|
||||
stream->peak_kbps < 0 ? 0 : stream->peak_kbps;
|
||||
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;
|
||||
@@ -1087,12 +1073,11 @@ static int init_connect(struct ftl_stream *stream)
|
||||
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));
|
||||
ftl_status_code_to_string(status_code));
|
||||
return OBS_OUTPUT_INVALID_STREAM;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
blog(LOG_ERROR, "Failed to create ingest handle (%s)",
|
||||
ftl_status_code_to_string(status_code));
|
||||
ftl_status_code_to_string(status_code));
|
||||
return OBS_OUTPUT_ERROR;
|
||||
}
|
||||
}
|
||||
@@ -1161,21 +1146,19 @@ static int _ftl_error_to_obs_error(int status)
|
||||
}
|
||||
|
||||
struct obs_output_info ftl_output_info = {
|
||||
.id = "ftl_output",
|
||||
.flags = OBS_OUTPUT_AV |
|
||||
OBS_OUTPUT_ENCODED |
|
||||
OBS_OUTPUT_SERVICE,
|
||||
.id = "ftl_output",
|
||||
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE,
|
||||
.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
|
||||
.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,
|
||||
};
|
||||
|
@@ -24,12 +24,13 @@
|
||||
#define do_log(level, format, ...) \
|
||||
blog(level, "[net if] " format, ##__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 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__)
|
||||
|
||||
static inline void netif_saddr_data_push_back(struct netif_saddr_data *sd,
|
||||
const char *ip, const char *adapter)
|
||||
const char *ip,
|
||||
const char *adapter)
|
||||
{
|
||||
struct netif_saddr_item item;
|
||||
struct dstr full_name = {0};
|
||||
@@ -47,32 +48,33 @@ static inline void netif_saddr_data_push_back(struct netif_saddr_data *sd,
|
||||
}
|
||||
|
||||
static void netif_convert_to_string(char *dest,
|
||||
struct sockaddr_storage *byte_address)
|
||||
struct sockaddr_storage *byte_address)
|
||||
{
|
||||
int family = byte_address->ss_family;
|
||||
char temp_char[INET6_ADDRSTRLEN] = {0};
|
||||
|
||||
#ifndef _WIN32
|
||||
if (family == AF_INET)
|
||||
inet_ntop(family, &(((struct sockaddr_in*)byte_address)->sin_addr),
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
inet_ntop(family,
|
||||
&(((struct sockaddr_in *)byte_address)->sin_addr),
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
else if (family == AF_INET6)
|
||||
inet_ntop(family, &(((struct sockaddr_in*)byte_address)->sin_addr),
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
inet_ntop(family,
|
||||
&(((struct sockaddr_in *)byte_address)->sin_addr),
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
#else
|
||||
if (family == AF_INET)
|
||||
InetNtopA(family, &(((SOCKADDR_IN *)byte_address)->sin_addr),
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
else if (family == AF_INET6)
|
||||
InetNtopA(family, &(((SOCKADDR_IN6 *)byte_address)->sin6_addr),
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
temp_char, INET6_ADDRSTRLEN);
|
||||
#endif
|
||||
strncpy(dest, temp_char, INET6_ADDRSTRLEN);
|
||||
}
|
||||
|
||||
static void netif_push(struct sockaddr *copy_source,
|
||||
struct netif_saddr_data *saddr_d,
|
||||
const char *adapter)
|
||||
struct netif_saddr_data *saddr_d, const char *adapter)
|
||||
{
|
||||
char temp_char[INET6_ADDRSTRLEN] = {0};
|
||||
struct sockaddr_storage sa = {0};
|
||||
@@ -88,12 +90,12 @@ static void netif_push(struct sockaddr *copy_source,
|
||||
|
||||
void netif_log_saddrs(struct netif_saddr_data *sd)
|
||||
{
|
||||
for(size_t i = 0; i < sd->addrs.num; i++)
|
||||
for (size_t i = 0; i < sd->addrs.num; i++)
|
||||
info("\t\t%s", sd->addrs.array[i].name);
|
||||
}
|
||||
|
||||
bool netif_str_to_addr(struct sockaddr_storage *out, int *addr_len,
|
||||
const char *addr)
|
||||
const char *addr)
|
||||
{
|
||||
bool ipv6;
|
||||
|
||||
@@ -109,16 +111,15 @@ bool netif_str_to_addr(struct sockaddr_storage *out, int *addr_len,
|
||||
|
||||
#ifdef _WIN32
|
||||
int ret = WSAStringToAddressA((LPSTR)addr, out->ss_family, NULL,
|
||||
(LPSOCKADDR)out, addr_len);
|
||||
(LPSOCKADDR)out, addr_len);
|
||||
if (ret == SOCKET_ERROR)
|
||||
warn("Could not parse address, error code: %d", GetLastError());
|
||||
return ret != SOCKET_ERROR;
|
||||
#else
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)out;
|
||||
if (inet_pton(out->ss_family, addr, &sin->sin_addr)) {
|
||||
*addr_len = ipv6 ?
|
||||
sizeof(struct sockaddr_in6) :
|
||||
sizeof(struct sockaddr_in);
|
||||
*addr_len = ipv6 ? sizeof(struct sockaddr_in6)
|
||||
: sizeof(struct sockaddr_in);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -152,13 +153,14 @@ static inline void netif_get_addrs_nix(struct netif_saddr_data *ifaddrs)
|
||||
|
||||
if ((family == AF_INET) || (family == AF_INET6)) {
|
||||
s = getnameinfo(ifa->ifa_addr,
|
||||
(family == AF_INET) ?
|
||||
sizeof(struct sockaddr_in) :
|
||||
sizeof(struct sockaddr_in6),
|
||||
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
||||
(family == AF_INET)
|
||||
? sizeof(struct sockaddr_in)
|
||||
: sizeof(struct sockaddr_in6),
|
||||
host, NI_MAXHOST, NULL, 0,
|
||||
NI_NUMERICHOST);
|
||||
if (s != 0) {
|
||||
warn("getnameinfo() failed: %s",
|
||||
gai_strerror(s));
|
||||
gai_strerror(s));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -176,20 +178,18 @@ static inline PIP_ADAPTER_ADDRESSES get_adapters(void)
|
||||
PIP_ADAPTER_ADDRESSES adapter = NULL;
|
||||
unsigned long ret = 0;
|
||||
unsigned long out_buf_len = 16384;
|
||||
unsigned long flags =
|
||||
GAA_FLAG_SKIP_ANYCAST |
|
||||
GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER;
|
||||
unsigned long flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
|
||||
GAA_FLAG_SKIP_DNS_SERVER;
|
||||
const int max_tries = 3;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
adapter = (IP_ADAPTER_ADDRESSES*)bmalloc(out_buf_len);
|
||||
adapter = (IP_ADAPTER_ADDRESSES *)bmalloc(out_buf_len);
|
||||
if (!adapter)
|
||||
return NULL;
|
||||
|
||||
ret = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, adapter,
|
||||
&out_buf_len);
|
||||
&out_buf_len);
|
||||
if (ret == ERROR_BUFFER_OVERFLOW) {
|
||||
bfree(adapter);
|
||||
adapter = NULL;
|
||||
@@ -205,16 +205,15 @@ static inline PIP_ADAPTER_ADDRESSES get_adapters(void)
|
||||
bfree(adapter);
|
||||
adapter = NULL;
|
||||
|
||||
FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, ret,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
msg_buf, 0, NULL);
|
||||
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, ret,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
msg_buf, 0, NULL);
|
||||
if (msg_buf) {
|
||||
warn("Call to GetAdaptersAddresses failed: %s (%d)",
|
||||
msg_buf, ret);
|
||||
msg_buf, ret);
|
||||
LocalFree(msg_buf);
|
||||
}
|
||||
}
|
||||
@@ -250,7 +249,7 @@ static inline void netif_get_addrs_win32(struct netif_saddr_data *ifaddrs)
|
||||
family = socket_addr.lpSockaddr->sa_family;
|
||||
if (family == AF_INET || family == AF_INET6)
|
||||
netif_push(socket_addr.lpSockaddr, ifaddrs,
|
||||
adap_name);
|
||||
adap_name);
|
||||
}
|
||||
|
||||
bfree(adap_name);
|
||||
|
@@ -20,36 +20,36 @@
|
||||
#include <util/darray.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <ws2tcpip.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2ipdef.h>
|
||||
# include <iphlpapi.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2ipdef.h>
|
||||
#include <iphlpapi.h>
|
||||
#else
|
||||
|
||||
# ifdef __linux__
|
||||
# include <linux/if_link.h>
|
||||
# elif __FreeBSD__
|
||||
# include <netinet/in.h>
|
||||
# ifndef _GNU_SOURCE
|
||||
# define _GNU_SOURCE
|
||||
# define __NET_IF_GNU_SOURCE__
|
||||
# endif //_GNU_SOURCE
|
||||
# endif //__FreeBSD__
|
||||
#ifdef __linux__
|
||||
#include <linux/if_link.h>
|
||||
#elif __FreeBSD__
|
||||
#include <netinet/in.h>
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define __NET_IF_GNU_SOURCE__
|
||||
#endif //_GNU_SOURCE
|
||||
#endif //__FreeBSD__
|
||||
|
||||
# include <ifaddrs.h>
|
||||
# include <netdb.h>
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <unistd.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <sys/socket.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
# ifdef __FreeBSD__
|
||||
# ifdef ___NET_IF_GNU_SOURCE__
|
||||
# undef ___NET_IF_GNU_SOURCE__
|
||||
# undef _GNU_SOURCE
|
||||
# endif
|
||||
# endif
|
||||
#ifdef __FreeBSD__
|
||||
#ifdef ___NET_IF_GNU_SOURCE__
|
||||
#undef ___NET_IF_GNU_SOURCE__
|
||||
#undef _GNU_SOURCE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -72,6 +72,6 @@ static inline void netif_saddr_data_free(struct netif_saddr_data *data)
|
||||
}
|
||||
|
||||
extern bool netif_str_to_addr(struct sockaddr_storage *out, int *addr_len,
|
||||
const char *addr);
|
||||
const char *addr);
|
||||
extern void netif_get_addrs(struct netif_saddr_data *ifaddrs);
|
||||
extern void netif_log_saddrs(struct netif_saddr_data *sd);
|
||||
|
@@ -77,7 +77,8 @@ static void null_output_stop(void *data, uint64_t ts)
|
||||
UNUSED_PARAMETER(ts);
|
||||
|
||||
context->stop_thread_active = pthread_create(&context->stop_thread,
|
||||
NULL, stop_thread, data) == 0;
|
||||
NULL, stop_thread,
|
||||
data) == 0;
|
||||
}
|
||||
|
||||
static void null_output_data(void *data, struct encoder_packet *packet)
|
||||
@@ -87,13 +88,12 @@ static void null_output_data(void *data, struct encoder_packet *packet)
|
||||
}
|
||||
|
||||
struct obs_output_info null_output_info = {
|
||||
.id = "null_output",
|
||||
.flags = OBS_OUTPUT_AV |
|
||||
OBS_OUTPUT_ENCODED,
|
||||
.get_name = null_output_getname,
|
||||
.create = null_output_create,
|
||||
.destroy = null_output_destroy,
|
||||
.start = null_output_start,
|
||||
.stop = null_output_stop,
|
||||
.encoded_packet = null_output_data
|
||||
.id = "null_output",
|
||||
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED,
|
||||
.get_name = null_output_getname,
|
||||
.create = null_output_create,
|
||||
.destroy = null_output_destroy,
|
||||
.start = null_output_start,
|
||||
.stop = null_output_stop,
|
||||
.encoded_packet = null_output_data,
|
||||
};
|
||||
|
@@ -21,32 +21,31 @@
|
||||
|
||||
static inline AVal *flv_str(AVal *out, const char *str)
|
||||
{
|
||||
out->av_val = (char*)str;
|
||||
out->av_val = (char *)str;
|
||||
out->av_len = (int)strlen(str);
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline void enc_num_val(char **enc, char *end, const char *name,
|
||||
double val)
|
||||
double val)
|
||||
{
|
||||
AVal s;
|
||||
*enc = AMF_EncodeNamedNumber(*enc, end, flv_str(&s, name), val);
|
||||
}
|
||||
|
||||
static inline void enc_bool_val(char **enc, char *end, const char *name,
|
||||
bool val)
|
||||
bool val)
|
||||
{
|
||||
AVal s;
|
||||
*enc = AMF_EncodeNamedBoolean(*enc, end, flv_str(&s, name), val);
|
||||
}
|
||||
|
||||
static inline void enc_str_val(char **enc, char *end, const char *name,
|
||||
const char *val)
|
||||
const char *val)
|
||||
{
|
||||
AVal s1, s2;
|
||||
*enc = AMF_EncodeNamedString(*enc, end,
|
||||
flv_str(&s1, name),
|
||||
flv_str(&s2, val));
|
||||
*enc = AMF_EncodeNamedString(*enc, end, flv_str(&s1, name),
|
||||
flv_str(&s2, val));
|
||||
}
|
||||
|
||||
static inline void enc_str(char **enc, char *end, const char *str)
|
||||
|
@@ -140,22 +140,22 @@ static void *rtmp_stream_create(obs_data_t *settings, obs_output_t *output)
|
||||
}
|
||||
|
||||
if (os_event_init(&stream->buffer_space_available_event,
|
||||
OS_EVENT_TYPE_AUTO) != 0) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
OS_EVENT_TYPE_MANUAL) != 0) {
|
||||
warn("Failed to initialize socket exit event");
|
||||
goto fail;
|
||||
}
|
||||
@@ -181,7 +181,8 @@ static void rtmp_stream_stop(void *data, uint64_t ts)
|
||||
stream->stop_ts = ts / 1000ULL;
|
||||
|
||||
if (ts)
|
||||
stream->shutdown_timeout_ts = ts +
|
||||
stream->shutdown_timeout_ts =
|
||||
ts +
|
||||
(uint64_t)stream->max_shutdown_time_sec * 1000000000ULL;
|
||||
|
||||
if (active(stream)) {
|
||||
@@ -195,27 +196,27 @@ static void rtmp_stream_stop(void *data, uint64_t ts)
|
||||
|
||||
static inline void set_rtmp_str(AVal *val, const char *str)
|
||||
{
|
||||
bool valid = (str && *str);
|
||||
val->av_val = valid ? (char*)str : NULL;
|
||||
bool valid = (str && *str);
|
||||
val->av_val = valid ? (char *)str : NULL;
|
||||
val->av_len = valid ? (int)strlen(str) : 0;
|
||||
}
|
||||
|
||||
static inline void set_rtmp_dstr(AVal *val, struct dstr *str)
|
||||
{
|
||||
bool valid = !dstr_is_empty(str);
|
||||
val->av_val = valid ? str->array : NULL;
|
||||
bool valid = !dstr_is_empty(str);
|
||||
val->av_val = valid ? str->array : NULL;
|
||||
val->av_len = valid ? (int)str->len : 0;
|
||||
}
|
||||
|
||||
static inline bool get_next_packet(struct rtmp_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
struct encoder_packet *packet)
|
||||
{
|
||||
bool new_packet = false;
|
||||
|
||||
pthread_mutex_lock(&stream->packets_mutex);
|
||||
if (stream->packets.size) {
|
||||
circlebuf_pop_front(&stream->packets, packet,
|
||||
sizeof(struct encoder_packet));
|
||||
sizeof(struct encoder_packet));
|
||||
new_packet = true;
|
||||
}
|
||||
pthread_mutex_unlock(&stream->packets_mutex);
|
||||
@@ -251,7 +252,7 @@ static bool discard_recv_data(struct rtmp_stream *stream, size_t size)
|
||||
#endif
|
||||
if (ret < 0) {
|
||||
do_log(LOG_ERROR, "recv error: %d (%d bytes)",
|
||||
error, (int)size);
|
||||
error, (int)size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -273,8 +274,8 @@ static void droptest_cap_data_rate(struct rtmp_stream *stream, size_t size)
|
||||
stream->droptest_size += size;
|
||||
|
||||
if (stream->droptest_info.size) {
|
||||
circlebuf_peek_front(&stream->droptest_info,
|
||||
&info, sizeof(info));
|
||||
circlebuf_peek_front(&stream->droptest_info, &info,
|
||||
sizeof(info));
|
||||
|
||||
if (stream->droptest_size > DROPTEST_MAX_BYTES) {
|
||||
uint64_t elapsed = ts - info.ts;
|
||||
@@ -286,7 +287,7 @@ static void droptest_cap_data_rate(struct rtmp_stream *stream, size_t size)
|
||||
|
||||
while (stream->droptest_size > DROPTEST_MAX_BYTES) {
|
||||
circlebuf_pop_front(&stream->droptest_info,
|
||||
&info, sizeof(info));
|
||||
&info, sizeof(info));
|
||||
stream->droptest_size -= info.size;
|
||||
}
|
||||
}
|
||||
@@ -294,7 +295,8 @@ 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)
|
||||
static int socket_queue_data(RTMPSockBuf *sb, const char *data, int len,
|
||||
void *arg)
|
||||
{
|
||||
UNUSED_PARAMETER(sb);
|
||||
|
||||
@@ -323,23 +325,24 @@ retry_send:
|
||||
|
||||
pthread_mutex_unlock(&stream->write_buf_mutex);
|
||||
|
||||
os_event_signal (stream->buffer_has_data_event);
|
||||
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)
|
||||
struct encoder_packet *packet, bool is_header,
|
||||
size_t idx)
|
||||
{
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
int recv_size = 0;
|
||||
int ret = 0;
|
||||
size_t size;
|
||||
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);
|
||||
(u_long *)&recv_size);
|
||||
#else
|
||||
ret = ioctl(stream->rtmp.m_sb.sb_socket, FIONREAD, &recv_size);
|
||||
#endif
|
||||
@@ -350,14 +353,14 @@ static int send_packet(struct rtmp_stream *stream,
|
||||
}
|
||||
}
|
||||
|
||||
flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset,
|
||||
&data, &size, is_header);
|
||||
flv_packet_mux(packet, is_header ? 0 : stream->start_dts_offset, &data,
|
||||
&size, is_header);
|
||||
|
||||
#ifdef TEST_FRAMEDROPS
|
||||
droptest_cap_data_rate(stream, size);
|
||||
#endif
|
||||
|
||||
ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size, (int)idx);
|
||||
ret = RTMP_Write(&stream->rtmp, (char *)data, (int)size, (int)idx);
|
||||
bfree(data);
|
||||
|
||||
if (is_header)
|
||||
@@ -372,14 +375,14 @@ static int send_packet(struct rtmp_stream *stream,
|
||||
static inline bool send_headers(struct rtmp_stream *stream);
|
||||
|
||||
static inline bool can_shutdown_stream(struct rtmp_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
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);
|
||||
stream->max_shutdown_time_sec);
|
||||
|
||||
return timeout || packet->sys_dts_usec >= (int64_t)stream->stop_ts;
|
||||
}
|
||||
@@ -388,8 +391,7 @@ static void set_output_error(struct rtmp_stream *stream)
|
||||
{
|
||||
const char *msg = NULL;
|
||||
#ifdef _WIN32
|
||||
switch (stream->rtmp.last_error_code)
|
||||
{
|
||||
switch (stream->rtmp.last_error_code) {
|
||||
case WSAETIMEDOUT:
|
||||
msg = obs_module_text("ConnectionTimedOut");
|
||||
break;
|
||||
@@ -413,8 +415,7 @@ static void set_output_error(struct rtmp_stream *stream)
|
||||
break;
|
||||
}
|
||||
#else
|
||||
switch (stream->rtmp.last_error_code)
|
||||
{
|
||||
switch (stream->rtmp.last_error_code) {
|
||||
case ETIMEDOUT:
|
||||
msg = obs_module_text("ConnectionTimedOut");
|
||||
break;
|
||||
@@ -527,15 +528,15 @@ static void *send_thread(void *data)
|
||||
static bool send_meta_data(struct rtmp_stream *stream, size_t idx, bool *next)
|
||||
{
|
||||
uint8_t *meta_data;
|
||||
size_t meta_data_size;
|
||||
bool success = true;
|
||||
size_t meta_data_size;
|
||||
bool success = true;
|
||||
|
||||
*next = flv_meta_data(stream->output, &meta_data,
|
||||
&meta_data_size, false, idx);
|
||||
*next = flv_meta_data(stream->output, &meta_data, &meta_data_size,
|
||||
false, idx);
|
||||
|
||||
if (*next) {
|
||||
success = RTMP_Write(&stream->rtmp, (char*)meta_data,
|
||||
(int)meta_data_size, (int)idx) >= 0;
|
||||
success = RTMP_Write(&stream->rtmp, (char *)meta_data,
|
||||
(int)meta_data_size, (int)idx) >= 0;
|
||||
bfree(meta_data);
|
||||
}
|
||||
|
||||
@@ -543,16 +544,14 @@ static bool send_meta_data(struct rtmp_stream *stream, size_t idx, bool *next)
|
||||
}
|
||||
|
||||
static bool send_audio_header(struct rtmp_stream *stream, size_t idx,
|
||||
bool *next)
|
||||
bool *next)
|
||||
{
|
||||
obs_output_t *context = stream->output;
|
||||
obs_output_t *context = stream->output;
|
||||
obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, idx);
|
||||
uint8_t *header;
|
||||
uint8_t *header;
|
||||
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_AUDIO,
|
||||
.timebase_den = 1
|
||||
};
|
||||
struct encoder_packet packet = {.type = OBS_ENCODER_AUDIO,
|
||||
.timebase_den = 1};
|
||||
|
||||
if (!aencoder) {
|
||||
*next = false;
|
||||
@@ -566,16 +565,13 @@ static bool send_audio_header(struct rtmp_stream *stream, size_t idx,
|
||||
|
||||
static bool send_video_header(struct rtmp_stream *stream)
|
||||
{
|
||||
obs_output_t *context = stream->output;
|
||||
obs_output_t *context = stream->output;
|
||||
obs_encoder_t *vencoder = obs_output_get_video_encoder(context);
|
||||
uint8_t *header;
|
||||
size_t size;
|
||||
uint8_t *header;
|
||||
size_t size;
|
||||
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_VIDEO,
|
||||
.timebase_den = 1,
|
||||
.keyframe = true
|
||||
};
|
||||
struct encoder_packet packet = {
|
||||
.type = OBS_ENCODER_VIDEO, .timebase_den = 1, .keyframe = true};
|
||||
|
||||
obs_encoder_get_extra_data(vencoder, &header, &size);
|
||||
packet.size = obs_parse_avc_header(&packet.data, header, size);
|
||||
@@ -619,12 +615,12 @@ static void adjust_sndbuf_size(struct rtmp_stream *stream, int new_size)
|
||||
socklen_t int_size = sizeof(int);
|
||||
|
||||
getsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF,
|
||||
(char*)&cur_sendbuf_size, &int_size);
|
||||
(char *)&cur_sendbuf_size, &int_size);
|
||||
|
||||
if (cur_sendbuf_size < new_size) {
|
||||
cur_sendbuf_size = new_size;
|
||||
setsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET, SO_SNDBUF,
|
||||
(const char*)&cur_sendbuf_size, int_size);
|
||||
(const char *)&cur_sendbuf_size, int_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -670,18 +666,19 @@ static int init_send(struct rtmp_stream *stream)
|
||||
bfree(stream->write_buf);
|
||||
|
||||
int total_bitrate = 0;
|
||||
obs_output_t *context = stream->output;
|
||||
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");
|
||||
int bitrate =
|
||||
obs_data_get_int(params, "bitrate");
|
||||
if (!bitrate) {
|
||||
warn ("Video encoder didn't return a "
|
||||
"valid bitrate, new network "
|
||||
"code may function poorly. "
|
||||
"Low latency mode disabled.");
|
||||
warn("Video encoder didn't return a "
|
||||
"valid bitrate, new network "
|
||||
"code may function poorly. "
|
||||
"Low latency mode disabled.");
|
||||
stream->low_latency_mode = false;
|
||||
bitrate = 10000;
|
||||
}
|
||||
@@ -690,11 +687,13 @@ static int init_send(struct rtmp_stream *stream)
|
||||
}
|
||||
}
|
||||
|
||||
obs_encoder_t *aencoder = obs_output_get_audio_encoder(context, 0);
|
||||
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");
|
||||
int bitrate =
|
||||
obs_data_get_int(params, "bitrate");
|
||||
if (!bitrate)
|
||||
bitrate = 160;
|
||||
total_bitrate += bitrate;
|
||||
@@ -713,7 +712,7 @@ static int init_send(struct rtmp_stream *stream)
|
||||
|
||||
#ifdef _WIN32
|
||||
ret = pthread_create(&stream->socket_thread, NULL,
|
||||
socket_thread_windows, stream);
|
||||
socket_thread_windows, stream);
|
||||
#else
|
||||
warn("New socket loop not supported on this platform");
|
||||
return OBS_OUTPUT_ERROR;
|
||||
@@ -764,13 +763,13 @@ static void win32_log_interface_type(struct rtmp_stream *stream)
|
||||
if (!h)
|
||||
return;
|
||||
|
||||
dest_addr = *(uint32_t*)h->h_addr_list[0];
|
||||
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;
|
||||
source_addr = (*(struct sockaddr_in *)&rtmp->m_bindIP)
|
||||
.sin_addr.S_un.S_addr;
|
||||
else
|
||||
return;
|
||||
|
||||
@@ -780,7 +779,7 @@ static void win32_log_interface_type(struct rtmp_stream *stream)
|
||||
row.dwIndex = route.dwForwardIfIndex;
|
||||
|
||||
if (!GetIfEntry(&row)) {
|
||||
uint32_t speed =row.dwSpeed / 1000000;
|
||||
uint32_t speed = row.dwSpeed / 1000000;
|
||||
char *type;
|
||||
struct dstr other = {0};
|
||||
|
||||
@@ -794,7 +793,7 @@ static void win32_log_interface_type(struct rtmp_stream *stream)
|
||||
}
|
||||
|
||||
info("Interface: %s (%s, %lu mbps)", row.bDescr, type,
|
||||
speed);
|
||||
speed);
|
||||
|
||||
dstr_free(&other);
|
||||
}
|
||||
@@ -819,18 +818,19 @@ static int try_connect(struct rtmp_stream *stream)
|
||||
|
||||
dstr_copy(&stream->encoder_name, "FMLE/3.0 (compatible; FMSc/1.0)");
|
||||
|
||||
set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username);
|
||||
set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username);
|
||||
set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password);
|
||||
set_rtmp_dstr(&stream->rtmp.Link.flashVer, &stream->encoder_name);
|
||||
set_rtmp_dstr(&stream->rtmp.Link.flashVer, &stream->encoder_name);
|
||||
stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl;
|
||||
|
||||
if (dstr_is_empty(&stream->bind_ip) ||
|
||||
dstr_cmp(&stream->bind_ip, "default") == 0) {
|
||||
memset(&stream->rtmp.m_bindIP, 0, sizeof(stream->rtmp.m_bindIP));
|
||||
memset(&stream->rtmp.m_bindIP, 0,
|
||||
sizeof(stream->rtmp.m_bindIP));
|
||||
} else {
|
||||
bool success = netif_str_to_addr(&stream->rtmp.m_bindIP.addr,
|
||||
&stream->rtmp.m_bindIP.addrLen,
|
||||
stream->bind_ip.array);
|
||||
&stream->rtmp.m_bindIP.addrLen,
|
||||
stream->bind_ip.array);
|
||||
if (success) {
|
||||
int len = stream->rtmp.m_bindIP.addrLen;
|
||||
bool ipv6 = len == sizeof(struct sockaddr_in6);
|
||||
@@ -841,8 +841,8 @@ static int try_connect(struct rtmp_stream *stream)
|
||||
RTMP_AddStream(&stream->rtmp, stream->key.array);
|
||||
|
||||
for (size_t idx = 1;; idx++) {
|
||||
obs_encoder_t *encoder = obs_output_get_audio_encoder(
|
||||
stream->output, idx);
|
||||
obs_encoder_t *encoder =
|
||||
obs_output_get_audio_encoder(stream->output, idx);
|
||||
const char *encoder_name;
|
||||
|
||||
if (!encoder)
|
||||
@@ -852,9 +852,9 @@ static int try_connect(struct rtmp_stream *stream)
|
||||
RTMP_AddStream(&stream->rtmp, encoder_name);
|
||||
}
|
||||
|
||||
stream->rtmp.m_outChunkSize = 4096;
|
||||
stream->rtmp.m_outChunkSize = 4096;
|
||||
stream->rtmp.m_bSendChunkSizeInfo = true;
|
||||
stream->rtmp.m_bUseNagle = true;
|
||||
stream->rtmp.m_bUseNagle = true;
|
||||
|
||||
#ifdef _WIN32
|
||||
win32_log_interface_type(stream);
|
||||
@@ -894,13 +894,13 @@ static bool init_connect(struct rtmp_stream *stream)
|
||||
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;
|
||||
stream->got_first_video = false;
|
||||
stream->dropped_frames = 0;
|
||||
stream->min_priority = 0;
|
||||
stream->got_first_video = false;
|
||||
|
||||
settings = obs_output_get_settings(stream->output);
|
||||
dstr_copy(&stream->path, obs_service_get_url(service));
|
||||
dstr_copy(&stream->key, obs_service_get_key(service));
|
||||
dstr_copy(&stream->path, obs_service_get_url(service));
|
||||
dstr_copy(&stream->key, obs_service_get_key(service));
|
||||
dstr_copy(&stream->username, obs_service_get_username(service));
|
||||
dstr_copy(&stream->password, obs_service_get_password(service));
|
||||
dstr_depad(&stream->path);
|
||||
@@ -919,10 +919,10 @@ 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);
|
||||
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;
|
||||
@@ -965,14 +965,14 @@ static bool rtmp_stream_start(void *data)
|
||||
|
||||
os_atomic_set_bool(&stream->connecting, true);
|
||||
return pthread_create(&stream->connect_thread, NULL, connect_thread,
|
||||
stream) == 0;
|
||||
stream) == 0;
|
||||
}
|
||||
|
||||
static inline bool add_packet(struct rtmp_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
struct encoder_packet *packet)
|
||||
{
|
||||
circlebuf_push_back(&stream->packets, packet,
|
||||
sizeof(struct encoder_packet));
|
||||
sizeof(struct encoder_packet));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -982,12 +982,12 @@ static inline size_t num_buffered_packets(struct rtmp_stream *stream)
|
||||
}
|
||||
|
||||
static void drop_frames(struct rtmp_stream *stream, const char *name,
|
||||
int highest_priority, bool pframes)
|
||||
int highest_priority, bool pframes)
|
||||
{
|
||||
UNUSED_PARAMETER(pframes);
|
||||
|
||||
struct circlebuf new_buf = {0};
|
||||
int num_frames_dropped = 0;
|
||||
struct circlebuf new_buf = {0};
|
||||
int num_frames_dropped = 0;
|
||||
|
||||
#ifdef _DEBUG
|
||||
int start_packets = (int)num_buffered_packets(stream);
|
||||
@@ -1002,7 +1002,7 @@ static void drop_frames(struct rtmp_stream *stream, const char *name,
|
||||
circlebuf_pop_front(&stream->packets, &packet, sizeof(packet));
|
||||
|
||||
/* do not drop audio data or video keyframes */
|
||||
if (packet.type == OBS_ENCODER_AUDIO ||
|
||||
if (packet.type == OBS_ENCODER_AUDIO ||
|
||||
packet.drop_priority >= highest_priority) {
|
||||
circlebuf_push_back(&new_buf, &packet, sizeof(packet));
|
||||
|
||||
@@ -1022,21 +1022,19 @@ static void drop_frames(struct rtmp_stream *stream, const char *name,
|
||||
|
||||
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));
|
||||
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 rtmp_stream *stream,
|
||||
struct encoder_packet *first)
|
||||
struct encoder_packet *first)
|
||||
{
|
||||
size_t count = stream->packets.size / sizeof(*first);
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
struct encoder_packet *cur = circlebuf_data(&stream->packets,
|
||||
i * sizeof(*first));
|
||||
struct encoder_packet *cur =
|
||||
circlebuf_data(&stream->packets, i * sizeof(*first));
|
||||
if (cur->type == OBS_ENCODER_VIDEO && !cur->keyframe) {
|
||||
*first = *cur;
|
||||
return true;
|
||||
@@ -1052,11 +1050,10 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
|
||||
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;
|
||||
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)
|
||||
@@ -1072,8 +1069,8 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
|
||||
buffer_duration_usec = stream->last_dts_usec - first.dts_usec;
|
||||
|
||||
if (!pframes) {
|
||||
stream->congestion = (float)buffer_duration_usec /
|
||||
(float)drop_threshold;
|
||||
stream->congestion =
|
||||
(float)buffer_duration_usec / (float)drop_threshold;
|
||||
}
|
||||
|
||||
if (buffer_duration_usec > drop_threshold) {
|
||||
@@ -1083,7 +1080,7 @@ static void check_to_drop_frames(struct rtmp_stream *stream, bool pframes)
|
||||
}
|
||||
|
||||
static bool add_video_packet(struct rtmp_stream *stream,
|
||||
struct encoder_packet *packet)
|
||||
struct encoder_packet *packet)
|
||||
{
|
||||
check_to_drop_frames(stream, false);
|
||||
check_to_drop_frames(stream, true);
|
||||
@@ -1103,9 +1100,9 @@ static bool add_video_packet(struct rtmp_stream *stream,
|
||||
|
||||
static void rtmp_stream_data(void *data, struct encoder_packet *packet)
|
||||
{
|
||||
struct rtmp_stream *stream = data;
|
||||
struct rtmp_stream *stream = data;
|
||||
struct encoder_packet new_packet;
|
||||
bool added_packet = false;
|
||||
bool added_packet = false;
|
||||
|
||||
if (disconnected(stream) || !active(stream))
|
||||
return;
|
||||
@@ -1132,9 +1129,9 @@ static void rtmp_stream_data(void *data, struct encoder_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);
|
||||
added_packet = (packet->type == OBS_ENCODER_VIDEO)
|
||||
? add_video_packet(stream, &new_packet)
|
||||
: add_packet(stream, &new_packet);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&stream->packets_mutex);
|
||||
@@ -1164,12 +1161,13 @@ static obs_properties_t *rtmp_stream_properties(void *unused)
|
||||
obs_property_t *p;
|
||||
|
||||
obs_properties_add_int(props, OPT_DROP_THRESHOLD,
|
||||
obs_module_text("RTMPStream.DropThreshold"),
|
||||
200, 10000, 100);
|
||||
obs_module_text("RTMPStream.DropThreshold"), 200,
|
||||
10000, 100);
|
||||
|
||||
p = obs_properties_add_list(props, OPT_BIND_IP,
|
||||
obs_module_text("RTMPStream.BindIP"),
|
||||
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING);
|
||||
obs_module_text("RTMPStream.BindIP"),
|
||||
OBS_COMBO_TYPE_LIST,
|
||||
OBS_COMBO_FORMAT_STRING);
|
||||
|
||||
obs_property_list_add_string(p, obs_module_text("Default"), "default");
|
||||
|
||||
@@ -1181,9 +1179,9 @@ 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_module_text("RTMPStream.NewSocketLoop"));
|
||||
obs_properties_add_bool(props, OPT_LOWLATENCY_ENABLED,
|
||||
obs_module_text("RTMPStream.LowLatencyMode"));
|
||||
obs_module_text("RTMPStream.LowLatencyMode"));
|
||||
|
||||
return props;
|
||||
}
|
||||
@@ -1206,7 +1204,7 @@ static float rtmp_stream_congestion(void *data)
|
||||
|
||||
if (stream->new_socket_loop)
|
||||
return (float)stream->write_buf_len /
|
||||
(float)stream->write_buf_size;
|
||||
(float)stream->write_buf_size;
|
||||
else
|
||||
return stream->min_priority > 0 ? 1.0f : stream->congestion;
|
||||
}
|
||||
@@ -1218,23 +1216,21 @@ static int rtmp_stream_connect_time(void *data)
|
||||
}
|
||||
|
||||
struct obs_output_info rtmp_output_info = {
|
||||
.id = "rtmp_output",
|
||||
.flags = OBS_OUTPUT_AV |
|
||||
OBS_OUTPUT_ENCODED |
|
||||
OBS_OUTPUT_SERVICE |
|
||||
OBS_OUTPUT_MULTI_TRACK,
|
||||
.id = "rtmp_output",
|
||||
.flags = OBS_OUTPUT_AV | OBS_OUTPUT_ENCODED | OBS_OUTPUT_SERVICE |
|
||||
OBS_OUTPUT_MULTI_TRACK,
|
||||
.encoded_video_codecs = "h264",
|
||||
.encoded_audio_codecs = "aac",
|
||||
.get_name = rtmp_stream_getname,
|
||||
.create = rtmp_stream_create,
|
||||
.destroy = rtmp_stream_destroy,
|
||||
.start = rtmp_stream_start,
|
||||
.stop = rtmp_stream_stop,
|
||||
.encoded_packet = rtmp_stream_data,
|
||||
.get_defaults = rtmp_stream_defaults,
|
||||
.get_properties = rtmp_stream_properties,
|
||||
.get_total_bytes = rtmp_stream_total_bytes_sent,
|
||||
.get_congestion = rtmp_stream_congestion,
|
||||
.get_connect_time_ms = rtmp_stream_connect_time,
|
||||
.get_dropped_frames = rtmp_stream_dropped_frames
|
||||
.get_name = rtmp_stream_getname,
|
||||
.create = rtmp_stream_create,
|
||||
.destroy = rtmp_stream_destroy,
|
||||
.start = rtmp_stream_start,
|
||||
.stop = rtmp_stream_stop,
|
||||
.encoded_packet = rtmp_stream_data,
|
||||
.get_defaults = rtmp_stream_defaults,
|
||||
.get_properties = rtmp_stream_properties,
|
||||
.get_total_bytes = rtmp_stream_total_bytes_sent,
|
||||
.get_congestion = rtmp_stream_congestion,
|
||||
.get_connect_time_ms = rtmp_stream_connect_time,
|
||||
.get_dropped_frames = rtmp_stream_dropped_frames,
|
||||
};
|
||||
|
@@ -16,13 +16,13 @@
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
|
||||
#define do_log(level, format, ...) \
|
||||
#define do_log(level, format, ...) \
|
||||
blog(level, "[rtmp stream: '%s'] " format, \
|
||||
obs_output_get_name(stream->output), ##__VA_ARGS__)
|
||||
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 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"
|
||||
@@ -45,66 +45,66 @@ struct droptest_info {
|
||||
#endif
|
||||
|
||||
struct rtmp_stream {
|
||||
obs_output_t *output;
|
||||
obs_output_t *output;
|
||||
|
||||
pthread_mutex_t packets_mutex;
|
||||
pthread_mutex_t packets_mutex;
|
||||
struct circlebuf packets;
|
||||
bool sent_headers;
|
||||
bool sent_headers;
|
||||
|
||||
bool got_first_video;
|
||||
int64_t start_dts_offset;
|
||||
bool got_first_video;
|
||||
int64_t start_dts_offset;
|
||||
|
||||
volatile bool connecting;
|
||||
pthread_t connect_thread;
|
||||
volatile bool connecting;
|
||||
pthread_t connect_thread;
|
||||
|
||||
volatile bool active;
|
||||
volatile bool disconnected;
|
||||
volatile bool encode_error;
|
||||
pthread_t send_thread;
|
||||
volatile bool active;
|
||||
volatile bool disconnected;
|
||||
volatile bool encode_error;
|
||||
pthread_t send_thread;
|
||||
|
||||
int max_shutdown_time_sec;
|
||||
int max_shutdown_time_sec;
|
||||
|
||||
os_sem_t *send_sem;
|
||||
os_event_t *stop_event;
|
||||
uint64_t stop_ts;
|
||||
uint64_t shutdown_timeout_ts;
|
||||
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;
|
||||
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 pframe_drop_threshold_usec;
|
||||
int min_priority;
|
||||
float congestion;
|
||||
int64_t drop_threshold_usec;
|
||||
int64_t pframe_drop_threshold_usec;
|
||||
int min_priority;
|
||||
float congestion;
|
||||
|
||||
int64_t last_dts_usec;
|
||||
int64_t last_dts_usec;
|
||||
|
||||
uint64_t total_bytes_sent;
|
||||
int dropped_frames;
|
||||
uint64_t total_bytes_sent;
|
||||
int dropped_frames;
|
||||
|
||||
#ifdef TEST_FRAMEDROPS
|
||||
struct circlebuf droptest_info;
|
||||
size_t droptest_size;
|
||||
size_t droptest_size;
|
||||
#endif
|
||||
|
||||
RTMP rtmp;
|
||||
RTMP rtmp;
|
||||
|
||||
bool new_socket_loop;
|
||||
bool low_latency_mode;
|
||||
bool disable_send_window_optimization;
|
||||
bool socket_thread_active;
|
||||
pthread_t socket_thread;
|
||||
uint8_t *write_buf;
|
||||
size_t write_buf_len;
|
||||
size_t write_buf_size;
|
||||
pthread_mutex_t write_buf_mutex;
|
||||
os_event_t *buffer_space_available_event;
|
||||
os_event_t *buffer_has_data_event;
|
||||
os_event_t *socket_available_event;
|
||||
os_event_t *send_thread_signaled_exit;
|
||||
bool new_socket_loop;
|
||||
bool low_latency_mode;
|
||||
bool disable_send_window_optimization;
|
||||
bool socket_thread_active;
|
||||
pthread_t socket_thread;
|
||||
uint8_t *write_buf;
|
||||
size_t write_buf_len;
|
||||
size_t write_buf_size;
|
||||
pthread_mutex_t write_buf_mutex;
|
||||
os_event_t *buffer_space_available_event;
|
||||
os_event_t *buffer_has_data_event;
|
||||
os_event_t *socket_available_event;
|
||||
os_event_t *send_thread_signaled_exit;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@@ -11,17 +11,18 @@ static void fatal_sock_shutdown(struct rtmp_stream *stream)
|
||||
}
|
||||
|
||||
static bool socket_event(struct rtmp_stream *stream, bool *can_write,
|
||||
uint64_t last_send_time)
|
||||
uint64_t last_send_time)
|
||||
{
|
||||
WSANETWORKEVENTS net_events;
|
||||
bool success;
|
||||
|
||||
success = !WSAEnumNetworkEvents(stream->rtmp.m_sb.sb_socket, NULL,
|
||||
&net_events);
|
||||
&net_events);
|
||||
if (!success) {
|
||||
blog(LOG_ERROR, "socket_thread_windows: Aborting due to "
|
||||
"WSAEnumNetworkEvents failure, %d",
|
||||
WSAGetLastError());
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: Aborting due to "
|
||||
"WSAEnumNetworkEvents failure, %d",
|
||||
WSAGetLastError());
|
||||
fatal_sock_shutdown(stream);
|
||||
return false;
|
||||
}
|
||||
@@ -34,24 +35,26 @@ static bool socket_event(struct rtmp_stream *stream, bool *can_write,
|
||||
uint32_t diff =
|
||||
(os_gettime_ns() / 1000000) - last_send_time;
|
||||
|
||||
blog(LOG_ERROR, "socket_thread_windows: Received "
|
||||
"FD_CLOSE, %u ms since last send "
|
||||
"(buffer: %d / %d)",
|
||||
diff,
|
||||
stream->write_buf_len,
|
||||
stream->write_buf_size);
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: Received "
|
||||
"FD_CLOSE, %u ms since last send "
|
||||
"(buffer: %d / %d)",
|
||||
diff, stream->write_buf_len,
|
||||
stream->write_buf_size);
|
||||
}
|
||||
|
||||
if (os_event_try(stream->stop_event) != EAGAIN)
|
||||
blog(LOG_ERROR, "socket_thread_windows: Aborting due "
|
||||
"to FD_CLOSE during shutdown, "
|
||||
"%d bytes lost, error %d",
|
||||
stream->write_buf_len,
|
||||
net_events.iErrorCode[FD_CLOSE_BIT]);
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: Aborting due "
|
||||
"to FD_CLOSE during shutdown, "
|
||||
"%d bytes lost, error %d",
|
||||
stream->write_buf_len,
|
||||
net_events.iErrorCode[FD_CLOSE_BIT]);
|
||||
else
|
||||
blog(LOG_ERROR, "socket_thread_windows: Aborting due "
|
||||
"to FD_CLOSE, error %d",
|
||||
net_events.iErrorCode[FD_CLOSE_BIT]);
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: Aborting due "
|
||||
"to FD_CLOSE, error %d",
|
||||
net_events.iErrorCode[FD_CLOSE_BIT]);
|
||||
|
||||
fatal_sock_shutdown(stream);
|
||||
return false;
|
||||
@@ -63,8 +66,8 @@ static bool socket_event(struct rtmp_stream *stream, bool *can_write,
|
||||
bool fatal = false;
|
||||
|
||||
for (;;) {
|
||||
int ret = recv(stream->rtmp.m_sb.sb_socket,
|
||||
discard, sizeof(discard), 0);
|
||||
int ret = recv(stream->rtmp.m_sb.sb_socket, discard,
|
||||
sizeof(discard), 0);
|
||||
if (ret == -1) {
|
||||
err_code = WSAGetLastError();
|
||||
if (err_code == WSAEWOULDBLOCK)
|
||||
@@ -77,10 +80,11 @@ static bool socket_event(struct rtmp_stream *stream, bool *can_write,
|
||||
}
|
||||
|
||||
if (fatal) {
|
||||
blog(LOG_ERROR, "socket_thread_windows: "
|
||||
"Socket error, recv() returned "
|
||||
"%d, GetLastError() %d",
|
||||
ret, err_code);
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: "
|
||||
"Socket error, recv() returned "
|
||||
"%d, GetLastError() %d",
|
||||
ret, err_code);
|
||||
stream->rtmp.last_error_code = err_code;
|
||||
fatal_sock_shutdown(stream);
|
||||
return false;
|
||||
@@ -92,62 +96,55 @@ static bool socket_event(struct rtmp_stream *stream, bool *can_write,
|
||||
}
|
||||
|
||||
static void ideal_send_backlog_event(struct rtmp_stream *stream,
|
||||
bool *can_write)
|
||||
bool *can_write)
|
||||
{
|
||||
ULONG ideal_send_backlog;
|
||||
int ret;
|
||||
|
||||
ret = idealsendbacklogquery(
|
||||
stream->rtmp.m_sb.sb_socket,
|
||||
&ideal_send_backlog);
|
||||
ret = idealsendbacklogquery(stream->rtmp.m_sb.sb_socket,
|
||||
&ideal_send_backlog);
|
||||
if (ret == 0) {
|
||||
int cur_tcp_bufsize;
|
||||
int size = sizeof(cur_tcp_bufsize);
|
||||
|
||||
ret = getsockopt(stream->rtmp.m_sb.sb_socket,
|
||||
SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
(char *)&cur_tcp_bufsize,
|
||||
&size);
|
||||
ret = getsockopt(stream->rtmp.m_sb.sb_socket, SOL_SOCKET,
|
||||
SO_SNDBUF, (char *)&cur_tcp_bufsize, &size);
|
||||
if (ret == 0) {
|
||||
if (cur_tcp_bufsize < (int)ideal_send_backlog) {
|
||||
int bufsize = (int)ideal_send_backlog;
|
||||
setsockopt(stream->rtmp.m_sb.sb_socket,
|
||||
SOL_SOCKET,
|
||||
SO_SNDBUF,
|
||||
(const char *)&bufsize,
|
||||
sizeof(bufsize));
|
||||
SOL_SOCKET, SO_SNDBUF,
|
||||
(const char *)&bufsize,
|
||||
sizeof(bufsize));
|
||||
|
||||
blog(LOG_INFO, "socket_thread_windows: "
|
||||
"Increasing send buffer to "
|
||||
"ISB %d (buffer: %d / %d)",
|
||||
ideal_send_backlog,
|
||||
stream->write_buf_len,
|
||||
stream->write_buf_size);
|
||||
blog(LOG_INFO,
|
||||
"socket_thread_windows: "
|
||||
"Increasing send buffer to "
|
||||
"ISB %d (buffer: %d / %d)",
|
||||
ideal_send_backlog, stream->write_buf_len,
|
||||
stream->write_buf_size);
|
||||
}
|
||||
} else {
|
||||
blog(LOG_ERROR, "socket_thread_windows: Got "
|
||||
"send_backlog_event but "
|
||||
"getsockopt() returned %d",
|
||||
WSAGetLastError());
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: Got "
|
||||
"send_backlog_event but "
|
||||
"getsockopt() returned %d",
|
||||
WSAGetLastError());
|
||||
}
|
||||
} else {
|
||||
blog(LOG_ERROR, "socket_thread_windows: Got "
|
||||
"send_backlog_event but WSAIoctl() "
|
||||
"returned %d",
|
||||
WSAGetLastError());
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: Got "
|
||||
"send_backlog_event but WSAIoctl() "
|
||||
"returned %d",
|
||||
WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
enum data_ret {
|
||||
RET_BREAK,
|
||||
RET_FATAL,
|
||||
RET_CONTINUE
|
||||
};
|
||||
enum data_ret { RET_BREAK, RET_FATAL, RET_CONTINUE };
|
||||
|
||||
static enum data_ret write_data(struct rtmp_stream *stream, bool *can_write,
|
||||
uint64_t *last_send_time, size_t latency_packet_size,
|
||||
int delay_time)
|
||||
uint64_t *last_send_time,
|
||||
size_t latency_packet_size, int delay_time)
|
||||
{
|
||||
bool exit_loop = false;
|
||||
|
||||
@@ -170,19 +167,18 @@ static enum data_ret write_data(struct rtmp_stream *stream, bool *can_write,
|
||||
min(latency_packet_size, stream->write_buf_len);
|
||||
|
||||
ret = RTMPSockBuf_Send(&stream->rtmp.m_sb,
|
||||
(const char *)stream->write_buf,
|
||||
(int)send_len);
|
||||
(const char *)stream->write_buf,
|
||||
(int)send_len);
|
||||
} else {
|
||||
ret = RTMPSockBuf_Send(&stream->rtmp.m_sb,
|
||||
(const char *)stream->write_buf,
|
||||
(int)stream->write_buf_len);
|
||||
(const char *)stream->write_buf,
|
||||
(int)stream->write_buf_len);
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
if (stream->write_buf_len - ret)
|
||||
memmove(stream->write_buf,
|
||||
stream->write_buf + ret,
|
||||
stream->write_buf_len - ret);
|
||||
memmove(stream->write_buf, stream->write_buf + ret,
|
||||
stream->write_buf_len - ret);
|
||||
stream->write_buf_len -= ret;
|
||||
|
||||
*last_send_time = os_gettime_ns() / 1000000;
|
||||
@@ -210,10 +206,11 @@ static enum data_ret write_data(struct rtmp_stream *stream, bool *can_write,
|
||||
if (fatal_err) {
|
||||
/* connection closed, or connection was aborted /
|
||||
* socket closed / etc, that's a fatal error. */
|
||||
blog(LOG_ERROR, "socket_thread_windows: "
|
||||
"Socket error, send() returned %d, "
|
||||
"GetLastError() %d",
|
||||
ret, err_code);
|
||||
blog(LOG_ERROR,
|
||||
"socket_thread_windows: "
|
||||
"Socket error, send() returned %d, "
|
||||
"GetLastError() %d",
|
||||
ret, err_code);
|
||||
|
||||
pthread_mutex_unlock(&stream->write_buf_mutex);
|
||||
stream->rtmp.last_error_code = err_code;
|
||||
@@ -250,14 +247,15 @@ static inline void socket_thread_windows_internal(struct rtmp_stream *stream)
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
|
||||
WSAEventSelect(stream->rtmp.m_sb.sb_socket,
|
||||
stream->socket_available_event,
|
||||
FD_READ|FD_WRITE|FD_CLOSE);
|
||||
stream->socket_available_event,
|
||||
FD_READ | FD_WRITE | FD_CLOSE);
|
||||
|
||||
send_backlog_event = CreateEvent(NULL, true, false, NULL);
|
||||
|
||||
if (stream->low_latency_mode) {
|
||||
delay_time = 1000 / LATENCY_FACTOR;
|
||||
latency_packet_size = stream->write_buf_size / (LATENCY_FACTOR - 2);
|
||||
latency_packet_size =
|
||||
stream->write_buf_size / (LATENCY_FACTOR - 2);
|
||||
} else {
|
||||
latency_packet_size = stream->write_buf_size;
|
||||
delay_time = 0;
|
||||
@@ -265,13 +263,13 @@ static inline void socket_thread_windows_internal(struct rtmp_stream *stream)
|
||||
|
||||
if (!stream->disable_send_window_optimization) {
|
||||
memset(&send_backlog_overlapped, 0,
|
||||
sizeof(send_backlog_overlapped));
|
||||
sizeof(send_backlog_overlapped));
|
||||
send_backlog_overlapped.hEvent = send_backlog_event;
|
||||
idealsendbacklognotify(stream->rtmp.m_sb.sb_socket,
|
||||
&send_backlog_overlapped, NULL);
|
||||
&send_backlog_overlapped, NULL);
|
||||
} else {
|
||||
blog(LOG_INFO, "socket_thread_windows: Send window "
|
||||
"optimization disabled by user.");
|
||||
"optimization disabled by user.");
|
||||
}
|
||||
|
||||
HANDLE objs[3];
|
||||
@@ -286,7 +284,8 @@ static inline void socket_thread_windows_internal(struct rtmp_stream *stream)
|
||||
if (stream->write_buf_len == 0) {
|
||||
//blog(LOG_DEBUG, "Exiting on empty buffer");
|
||||
pthread_mutex_unlock(&stream->write_buf_mutex);
|
||||
os_event_reset(stream->send_thread_signaled_exit);
|
||||
os_event_reset(
|
||||
stream->send_thread_signaled_exit);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -312,18 +311,15 @@ static inline void socket_thread_windows_internal(struct rtmp_stream *stream)
|
||||
|
||||
ResetEvent(send_backlog_event);
|
||||
idealsendbacklognotify(stream->rtmp.m_sb.sb_socket,
|
||||
&send_backlog_overlapped, NULL);
|
||||
&send_backlog_overlapped, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (can_write) {
|
||||
for (;;) {
|
||||
enum data_ret ret = write_data(
|
||||
stream,
|
||||
&can_write,
|
||||
&last_send_time,
|
||||
latency_packet_size,
|
||||
delay_time);
|
||||
stream, &can_write, &last_send_time,
|
||||
latency_packet_size, delay_time);
|
||||
|
||||
switch (ret) {
|
||||
case RET_BREAK:
|
||||
@@ -334,12 +330,12 @@ static inline void socket_thread_windows_internal(struct rtmp_stream *stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
exit_write_loop:;
|
||||
exit_write_loop:;
|
||||
}
|
||||
|
||||
if (stream->rtmp.m_sb.sb_socket != INVALID_SOCKET)
|
||||
WSAEventSelect(stream->rtmp.m_sb.sb_socket,
|
||||
stream->socket_available_event, 0);
|
||||
stream->socket_available_event, 0);
|
||||
|
||||
blog(LOG_INFO, "socket_thread_windows: Normal exit");
|
||||
}
|
||||
|
Reference in New Issue
Block a user