diff --git a/deps/jansson/win32/vs2010/jansson.vcxproj b/deps/jansson/win32/vs2010/jansson.vcxproj
index 829e6362c..1fe675a30 100644
--- a/deps/jansson/win32/vs2010/jansson.vcxproj
+++ b/deps/jansson/win32/vs2010/jansson.vcxproj
@@ -115,7 +115,6 @@
WIN32;_DEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)
..
4996
- MultiThreadedDebug
Windows
@@ -131,7 +130,6 @@
WIN32;_DEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)
..
4996
- MultiThreadedDebug
Windows
@@ -149,7 +147,6 @@
WIN32;NDEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)
..
4996
- MultiThreaded
Windows
@@ -169,7 +166,6 @@
WIN32;NDEBUG;_WINDOWS;_USRDLL;JANSSON_DLL_EXPORTS;%(PreprocessorDefinitions)
..
4996
- MultiThreaded
Windows
diff --git a/libobs/media-io/audio-io.h b/libobs/media-io/audio-io.h
index 876868c5c..a08ad0d20 100644
--- a/libobs/media-io/audio-io.h
+++ b/libobs/media-io/audio-io.h
@@ -103,9 +103,9 @@ static inline uint32_t get_audio_channels(enum speaker_layout speakers)
return 0;
}
-static inline size_t get_audio_bytes_per_channel(enum audio_format type)
+static inline size_t get_audio_bytes_per_channel(enum audio_format format)
{
- switch (type) {
+ switch (format) {
case AUDIO_FORMAT_U8BIT:
case AUDIO_FORMAT_U8BIT_PLANAR:
return 1;
@@ -127,9 +127,9 @@ static inline size_t get_audio_bytes_per_channel(enum audio_format type)
return 0;
}
-static inline bool is_audio_planar(enum audio_format type)
+static inline bool is_audio_planar(enum audio_format format)
{
- switch (type) {
+ switch (format) {
case AUDIO_FORMAT_U8BIT:
case AUDIO_FORMAT_16BIT:
case AUDIO_FORMAT_32BIT:
@@ -149,19 +149,19 @@ static inline bool is_audio_planar(enum audio_format type)
return false;
}
-static inline size_t get_audio_planes(enum audio_format type,
+static inline size_t get_audio_planes(enum audio_format format,
enum speaker_layout speakers)
{
- return (is_audio_planar(type) ? get_audio_channels(speakers) : 1);
+ return (is_audio_planar(format) ? get_audio_channels(speakers) : 1);
}
-static inline size_t get_audio_size(enum audio_format type,
+static inline size_t get_audio_size(enum audio_format format,
enum speaker_layout speakers, uint32_t frames)
{
- bool planar = is_audio_planar(type);
+ bool planar = is_audio_planar(format);
return (planar ? 1 : get_audio_channels(speakers)) *
- get_audio_bytes_per_channel(type) *
+ get_audio_bytes_per_channel(format) *
frames;
}
diff --git a/libobs/obs-defs.h b/libobs/obs-defs.h
index f664e1ac2..7a6060c88 100644
--- a/libobs/obs-defs.h
+++ b/libobs/obs-defs.h
@@ -18,7 +18,7 @@
#pragma once
/** Maximum number of source channels for output and per display */
-#define MAX_CHANNELS 32
+#define MAX_CHANNELS 64
#define MODULE_SUCCESS 0
#define MODULE_ERROR -1
@@ -31,3 +31,4 @@
#define OBS_OUTPUT_CONNECT_FAILED -2
#define OBS_OUTPUT_INVALID_STREAM -3
#define OBS_OUTPUT_FAIL -4
+#define OBS_OUTPUT_DISCONNECTED -5
diff --git a/libobs/obs-encoder.c b/libobs/obs-encoder.c
index 0d948b043..5740e127e 100644
--- a/libobs/obs-encoder.c
+++ b/libobs/obs-encoder.c
@@ -1,5 +1,5 @@
/******************************************************************************
- Copyright (C) 2013 by Hugh Bailey
+ Copyright (C) 2013-2014 by Hugh Bailey
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -86,7 +86,7 @@ static struct obs_encoder *create_encoder(const char *id,
return encoder;
}
-obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
+obs_encoder_t obs_video_encoder_create(const char *id, const char *name,
obs_data_t settings, video_t video)
{
const struct video_output_info *voi;
@@ -99,7 +99,7 @@ obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
voi->fps_den, voi->fps_num);
}
-obs_encoder_t obs_encoder_create_audio(const char *id, const char *name,
+obs_encoder_t obs_audio_encoder_create(const char *id, const char *name,
obs_data_t settings, audio_t audio)
{
const struct audio_output_info *aoi;
@@ -118,11 +118,21 @@ static void receive_audio(void *param, struct audio_data *data);
static inline struct audio_convert_info *get_audio_info(
struct obs_encoder *encoder, struct audio_convert_info *info)
{
- if (encoder->info.audio_info)
- if (encoder->info.audio_info(encoder->data, info))
- return info;
+ const struct audio_output_info *aoi;
+ aoi = audio_output_getinfo(encoder->media);
+ memset(info, 0, sizeof(struct audio_convert_info));
- return false;
+ if (encoder->info.audio_info)
+ encoder->info.audio_info(encoder->data, info);
+
+ if (info->format == AUDIO_FORMAT_UNKNOWN)
+ info->format = aoi->format;
+ if (!info->samples_per_sec)
+ info->samples_per_sec = aoi->samples_per_sec;
+ if (info->speakers == SPEAKERS_UNKNOWN)
+ info->speakers = aoi->speakers;
+
+ return info;
}
static inline struct video_scale_info *get_video_info(
@@ -141,10 +151,8 @@ static void add_connection(struct obs_encoder *encoder)
struct video_scale_info video_info = {0};
if (encoder->info.type == OBS_ENCODER_AUDIO) {
- struct audio_convert_info *info = NULL;
-
- info = get_audio_info(encoder, &audio_info);
- audio_output_connect(encoder->media, info, receive_audio,
+ get_audio_info(encoder, &audio_info);
+ audio_output_connect(encoder->media, &audio_info, receive_audio,
encoder);
} else {
struct video_scale_info *info = NULL;
@@ -169,6 +177,15 @@ static void remove_connection(struct obs_encoder *encoder)
encoder->active = false;
}
+static inline void free_audio_buffers(struct obs_encoder *encoder)
+{
+ for (size_t i = 0; i < MAX_AV_PLANES; i++) {
+ circlebuf_free(&encoder->audio_input_buffer[i]);
+ bfree(encoder->audio_output_buffer[i]);
+ encoder->audio_output_buffer[i] = NULL;
+ }
+}
+
static void obs_encoder_actually_destroy(obs_encoder_t encoder)
{
if (encoder) {
@@ -180,8 +197,11 @@ static void obs_encoder_actually_destroy(obs_encoder_t encoder)
da_free(encoder->outputs);
pthread_mutex_unlock(&encoder->outputs_mutex);
+ free_audio_buffers(encoder);
+
if (encoder->data)
encoder->info.destroy(encoder->data);
+ da_free(encoder->callbacks);
obs_data_release(encoder->settings);
pthread_mutex_destroy(&encoder->callbacks_mutex);
pthread_mutex_destroy(&encoder->outputs_mutex);
@@ -280,6 +300,29 @@ obs_data_t obs_encoder_get_settings(obs_encoder_t encoder)
return encoder->settings;
}
+static inline void reset_audio_buffers(struct obs_encoder *encoder)
+{
+ free_audio_buffers(encoder);
+
+ for (size_t i = 0; i < encoder->planes; i++)
+ encoder->audio_output_buffer[i] =
+ bmalloc(encoder->framesize_bytes);
+}
+
+static void intitialize_audio_encoder(struct obs_encoder *encoder)
+{
+ struct audio_convert_info info;
+ get_audio_info(encoder, &info);
+
+ encoder->samplerate = info.samples_per_sec;
+ encoder->planes = get_audio_planes(info.format, info.speakers);
+ encoder->blocksize = get_audio_size(info.format, info.speakers, 1);
+ encoder->framesize = encoder->info.frame_size(encoder->data);
+
+ encoder->framesize_bytes = encoder->blocksize * encoder->framesize;
+ reset_audio_buffers(encoder);
+}
+
bool obs_encoder_initialize(obs_encoder_t encoder)
{
if (!encoder) return false;
@@ -291,7 +334,16 @@ bool obs_encoder_initialize(obs_encoder_t encoder)
encoder->info.destroy(encoder->data);
encoder->data = encoder->info.create(encoder->settings, encoder);
- return encoder->data != NULL;
+ if (!encoder->data)
+ return false;
+
+ encoder->paired_encoder = NULL;
+ encoder->start_ts = 0;
+
+ if (encoder->info.type == OBS_ENCODER_AUDIO)
+ intitialize_audio_encoder(encoder);
+
+ return true;
}
static inline size_t get_callback_idx(
@@ -444,15 +496,16 @@ static void full_stop(struct obs_encoder *encoder)
}
static inline void do_encode(struct obs_encoder *encoder,
- struct encoder_frame *frame, struct encoder_packet *packet)
+ struct encoder_frame *frame)
{
+ struct encoder_packet pkt = {0};
bool received = false;
bool success;
- packet->timebase_num = encoder->timebase_num;
- packet->timebase_den = encoder->timebase_den;
+ pkt.timebase_num = encoder->timebase_num;
+ pkt.timebase_den = encoder->timebase_den;
- success = encoder->info.encode(encoder->data, frame, packet, &received);
+ success = encoder->info.encode(encoder->data, frame, &pkt, &received);
if (!success) {
full_stop(encoder);
blog(LOG_ERROR, "Error encoding with encoder '%s'",
@@ -466,7 +519,7 @@ static inline void do_encode(struct obs_encoder *encoder,
for (size_t i = 0; i < encoder->callbacks.num; i++) {
struct encoder_callback *cb;
cb = encoder->callbacks.array+i;
- send_packet(encoder, cb, packet);
+ send_packet(encoder, cb, &pkt);
}
pthread_mutex_unlock(&encoder->callbacks_mutex);
@@ -476,7 +529,6 @@ static inline void do_encode(struct obs_encoder *encoder,
static void receive_video(void *param, struct video_data *frame)
{
struct obs_encoder *encoder = param;
- struct encoder_packet packet = {0};
struct encoder_frame enc_frame;
memset(&enc_frame, 0, sizeof(struct encoder_frame));
@@ -486,38 +538,90 @@ static void receive_video(void *param, struct video_data *frame)
enc_frame.linesize[i] = frame->linesize[i];
}
+ if (!encoder->start_ts)
+ encoder->start_ts = frame->timestamp;
+
enc_frame.frames = 1;
enc_frame.pts = encoder->cur_pts;
- do_encode(encoder, &enc_frame, &packet);
+ do_encode(encoder, &enc_frame);
encoder->cur_pts += encoder->timebase_num;
}
-static void receive_audio(void *param, struct audio_data *data)
+static bool buffer_audio(struct obs_encoder *encoder, struct audio_data *data)
+{
+ size_t samplerate = encoder->samplerate;
+ size_t size = data->frames * encoder->blocksize;
+ size_t offset_size = 0;
+
+ if (encoder->paired_encoder && !encoder->start_ts) {
+ uint64_t end_ts = data->timestamp;
+ uint64_t v_start_ts = encoder->paired_encoder->start_ts;
+
+ /* no video yet, so don't start audio */
+ if (!v_start_ts)
+ return false;
+
+ /* audio starting point still not synced with video starting
+ * point, so don't start audio */
+ end_ts += (uint64_t)data->frames * 1000000000ULL / samplerate;
+ if (end_ts <= v_start_ts)
+ return false;
+
+ /* ready to start audio, truncate if necessary */
+ if (data->timestamp < v_start_ts) {
+ uint64_t offset = v_start_ts - data->timestamp;
+ offset = (int)(offset * samplerate / 1000000000);
+ offset_size = (size_t)offset * encoder->blocksize;
+ }
+
+ encoder->start_ts = v_start_ts;
+ }
+
+ size -= offset_size;
+
+ /* push in to the circular buffer */
+ if (size)
+ for (size_t i = 0; i < encoder->planes; i++)
+ circlebuf_push_back(&encoder->audio_input_buffer[i],
+ data->data[i] + offset_size, size);
+
+ return true;
+}
+
+static void send_audio_data(struct obs_encoder *encoder)
{
- struct obs_encoder *encoder = param;
- struct encoder_packet packet = {0};
struct encoder_frame enc_frame;
- size_t data_size;
memset(&enc_frame, 0, sizeof(struct encoder_frame));
- data_size = audio_output_blocksize(encoder->media) * data->frames;
+ for (size_t i = 0; i < encoder->planes; i++) {
+ circlebuf_pop_front(&encoder->audio_input_buffer[i],
+ encoder->audio_output_buffer[i],
+ encoder->framesize_bytes);
- for (size_t i = 0; i < MAX_AV_PLANES; i++) {
- if (data->data[i]) {
- enc_frame.data[i] = data->data[i];
- enc_frame.linesize[i] = (uint32_t)data_size;
- }
+ enc_frame.data[i] = encoder->audio_output_buffer[i];
+ enc_frame.linesize[i] = (uint32_t)encoder->framesize_bytes;
}
- enc_frame.frames = data->frames;
+ enc_frame.frames = (uint32_t)encoder->framesize;
enc_frame.pts = encoder->cur_pts;
- do_encode(encoder, &enc_frame, &packet);
+ do_encode(encoder, &enc_frame);
- encoder->cur_pts += data->frames;
+ encoder->cur_pts += encoder->framesize;
+}
+
+static void receive_audio(void *param, struct audio_data *data)
+{
+ struct obs_encoder *encoder = param;
+
+ if (!buffer_audio(encoder, data))
+ return;
+
+ while (encoder->audio_input_buffer[0].size >= encoder->framesize_bytes)
+ send_audio_data(encoder);
}
void obs_encoder_add_output(struct obs_encoder *encoder,
diff --git a/libobs/obs-encoder.h b/libobs/obs-encoder.h
index 8d3d5066a..90fcd1914 100644
--- a/libobs/obs-encoder.h
+++ b/libobs/obs-encoder.h
@@ -136,6 +136,9 @@ struct obs_encoder_info {
bool (*encode)(void *data, struct encoder_frame *frame,
struct encoder_packet *packet, bool *received_packet);
+ /** Audio encoder only: Returns the frame size for this encoder */
+ size_t (*frame_size)(void *data);
+
/* ----------------------------------------------------------------- */
/* Optional implementation */
diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h
index cb14b764c..9b5621d6a 100644
--- a/libobs/obs-internal.h
+++ b/libobs/obs-internal.h
@@ -19,6 +19,7 @@
#include "util/c99defs.h"
#include "util/darray.h"
+#include "util/circlebuf.h"
#include "util/dstr.h"
#include "util/threading.h"
#include "callback/signal.h"
@@ -187,6 +188,8 @@ struct obs_source {
signal_handler_t signals;
proc_handler_t procs;
+ bool defer_update;
+
/* ensures show/hide are only called once */
volatile long show_refs;
@@ -279,6 +282,7 @@ struct obs_output {
int64_t first_video_ts;
int64_t video_offset;
int64_t audio_offset;
+ int interleaved_wait;
pthread_mutex_t interleaved_mutex;
DARRAY(struct il_packet) interleaved_packets;
@@ -315,6 +319,12 @@ struct obs_encoder {
struct obs_encoder_info info;
obs_data_t settings;
+ uint32_t samplerate;
+ size_t planes;
+ size_t blocksize;
+ size_t framesize;
+ size_t framesize_bytes;
+
bool active;
uint32_t timebase_num;
@@ -322,6 +332,17 @@ struct obs_encoder {
int64_t cur_pts;
+ struct circlebuf audio_input_buffer[MAX_AV_PLANES];
+ uint8_t *audio_output_buffer[MAX_AV_PLANES];
+
+ /* if a video encoder is paired with an audio encoder, make it start
+ * up at the specific timestamp. if this is the audio encoder,
+ * wait_for_video makes it wait until it's ready to sync up with
+ * video */
+ bool wait_for_video;
+ struct obs_encoder *paired_encoder;
+ uint64_t start_ts;
+
pthread_mutex_t outputs_mutex;
DARRAY(obs_output_t) outputs;
@@ -334,6 +355,15 @@ struct obs_encoder {
DARRAY(struct encoder_callback) callbacks;
};
+extern bool obs_encoder_initialize(obs_encoder_t encoder);
+
+extern void obs_encoder_start(obs_encoder_t encoder,
+ void (*new_packet)(void *param, struct encoder_packet *packet),
+ void *param);
+extern void obs_encoder_stop(obs_encoder_t encoder,
+ void (*new_packet)(void *param, struct encoder_packet *packet),
+ void *param);
+
extern void obs_encoder_add_output(struct obs_encoder *encoder,
struct obs_output *output);
extern void obs_encoder_remove_output(struct obs_encoder *encoder,
diff --git a/libobs/obs-module.c b/libobs/obs-module.c
index 5b8d14af2..b449c5113 100644
--- a/libobs/obs-module.c
+++ b/libobs/obs-module.c
@@ -181,12 +181,16 @@ void obs_register_encoder_s(const struct obs_encoder_info *info, size_t size)
CHECK_REQUIRED_VAL(info, destroy, obs_register_encoder);
CHECK_REQUIRED_VAL(info, encode, obs_register_encoder);
+ if (info->type == OBS_ENCODER_AUDIO)
+ CHECK_REQUIRED_VAL(info, frame_size, obs_register_encoder);
+
REGISTER_OBS_DEF(size, obs_encoder_info, obs->encoder_types, info);
}
void obs_register_service_s(const struct obs_service_info *info, size_t size)
{
/* TODO */
+ UNUSED_PARAMETER(size);
UNUSED_PARAMETER(info);
}
diff --git a/libobs/obs-output.c b/libobs/obs-output.c
index 95fe4cb7e..a65c68d00 100644
--- a/libobs/obs-output.c
+++ b/libobs/obs-output.c
@@ -18,6 +18,8 @@
#include "obs.h"
#include "obs-internal.h"
+static inline void signal_stop(struct obs_output *output, int code);
+
static inline const struct obs_output_info *find_output(const char *id)
{
size_t i;
@@ -139,8 +141,10 @@ bool obs_output_start(obs_output_t output)
void obs_output_stop(obs_output_t output)
{
- if (output)
+ if (output) {
output->info.stop(output->data);
+ signal_stop(output, OBS_OUTPUT_SUCCESS);
+ }
}
bool obs_output_active(obs_output_t output)
@@ -411,8 +415,7 @@ static void interleave_packets(void *data, struct encoder_packet *packet)
pthread_mutex_lock(&output->interleaved_mutex);
- if (!prepare_interleaved_packet(output, &out, packet)) {
-
+ if (prepare_interleaved_packet(output, &out, packet)) {
for (idx = 0; idx < output->interleaved_packets.num; idx++) {
struct il_packet *cur_packet;
cur_packet = output->interleaved_packets.array + idx;
@@ -425,8 +428,13 @@ static void interleave_packets(void *data, struct encoder_packet *packet)
/* when both video and audio have been received, we're ready
* to start sending out packets (one at a time) */
- if (output->received_audio && output->received_video)
+ if (!output->interleaved_wait &&
+ output->received_audio &&
+ output->received_video)
send_interleaved(output);
+
+ if (output->interleaved_wait > 0)
+ output->interleaved_wait--;
}
pthread_mutex_unlock(&output->interleaved_mutex);
@@ -439,8 +447,10 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
void *param;
if (encoded) {
- output->received_video = false;
- output->received_video = false;
+ output->received_video = false;
+ output->received_video = false;
+ output->interleaved_wait = 5;
+ free_packets(output);
encoded_callback = (has_video && has_audio) ?
interleave_packets : output->info.encoded_packet;
@@ -456,28 +466,26 @@ static void hook_data_capture(struct obs_output *output, bool encoded,
if (has_video)
video_output_connect(output->video,
get_video_conversion(output),
- output->info.raw_video,
- output->data);
+ output->info.raw_video, output->data);
if (has_audio)
audio_output_connect(output->audio,
get_audio_conversion(output),
- output->info.raw_audio,
- output->data);
+ output->info.raw_audio, output->data);
}
}
-static inline void signal_start(struct obs_output *output, int code)
+static inline void signal_start(struct obs_output *output)
{
struct calldata params = {0};
- calldata_setint(¶ms, "code", code);
calldata_setptr(¶ms, "output", output);
signal_handler_signal(output->signals, "start", ¶ms);
calldata_free(¶ms);
}
-static inline void signal_stop(struct obs_output *output)
+static inline void signal_stop(struct obs_output *output, int code)
{
struct calldata params = {0};
+ calldata_setint(¶ms, "errorcode", code);
calldata_setptr(¶ms, "output", output);
signal_handler_signal(output->signals, "stop", ¶ms);
calldata_free(¶ms);
@@ -508,6 +516,32 @@ bool obs_output_can_begin_data_capture(obs_output_t output, uint32_t flags)
return can_begin_data_capture(output, encoded, has_video, has_audio);
}
+bool obs_output_initialize_encoders(obs_output_t output, uint32_t flags)
+{
+ bool encoded, has_video, has_audio;
+
+ if (!output) return false;
+ if (output->active) return false;
+
+ convert_flags(output, flags, &encoded, &has_video, &has_audio);
+
+ if (!encoded)
+ return false;
+ if (has_video && !obs_encoder_initialize(output->video_encoder))
+ return false;
+ if (has_audio && !obs_encoder_initialize(output->audio_encoder))
+ return false;
+
+ if (has_video && has_audio && !output->audio_encoder->active &&
+ !output->video_encoder->active) {
+ output->audio_encoder->wait_for_video = true;
+ output->video_encoder->paired_encoder = output->audio_encoder;
+ output->audio_encoder->paired_encoder = output->video_encoder;
+ }
+
+ return true;
+}
+
bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags)
{
bool encoded, has_video, has_audio;
@@ -522,7 +556,7 @@ bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags)
hook_data_capture(output, encoded, has_video, has_audio);
output->active = true;
- signal_start(output, OBS_OUTPUT_SUCCESS);
+ signal_start(output);
return true;
}
@@ -551,19 +585,17 @@ void obs_output_end_data_capture(obs_output_t output)
} else {
if (has_video)
video_output_disconnect(output->video,
- output->info.raw_video,
- output->data);
+ output->info.raw_video, output->data);
if (has_audio)
audio_output_disconnect(output->audio,
- output->info.raw_audio,
- output->data);
+ output->info.raw_audio, output->data);
}
output->active = false;
- signal_stop(output);
}
-void obs_output_signal_start_fail(obs_output_t output, int code)
+void obs_output_signal_stop(obs_output_t output, int code)
{
- signal_start(output, code);
+ obs_output_end_data_capture(output);
+ signal_stop(output, code);
}
diff --git a/libobs/obs-properties.h b/libobs/obs-properties.h
index 879688816..60114fb26 100644
--- a/libobs/obs-properties.h
+++ b/libobs/obs-properties.h
@@ -78,20 +78,26 @@ extern void obs_properties_apply_settings(obs_properties_t props,
EXPORT obs_property_t obs_properties_add_bool(obs_properties_t props,
const char *name, const char *description);
+
EXPORT obs_property_t obs_properties_add_int(obs_properties_t props,
const char *name, const char *description,
int min, int max, int step);
+
EXPORT obs_property_t obs_properties_add_float(obs_properties_t props,
const char *name, const char *description,
double min, double max, double step);
+
EXPORT obs_property_t obs_properties_add_text(obs_properties_t props,
const char *name, const char *description,
enum obs_text_type type);
+
EXPORT obs_property_t obs_properties_add_path(obs_properties_t props,
const char *name, const char *description);
+
EXPORT obs_property_t obs_properties_add_list(obs_properties_t props,
const char *name, const char *description,
enum obs_combo_type type, enum obs_combo_format format);
+
EXPORT obs_property_t obs_properties_add_color(obs_properties_t props,
const char *name, const char *description);
diff --git a/libobs/obs-source.c b/libobs/obs-source.c
index 1356e743f..564ccb565 100644
--- a/libobs/obs-source.c
+++ b/libobs/obs-source.c
@@ -361,14 +361,24 @@ uint32_t obs_source_get_output_flags(obs_source_t source)
return source ? source->info.output_flags : 0;
}
+static void obs_source_deferred_update(obs_source_t source)
+{
+ source->info.update(source->data, source->settings);
+ source->defer_update = false;
+}
+
void obs_source_update(obs_source_t source, obs_data_t settings)
{
if (!source) return;
obs_data_apply(source->settings, settings);
- if (source->info.update)
- source->info.update(source->data, source->settings);
+ if (source->info.update) {
+ if (source->info.output_flags & OBS_SOURCE_VIDEO)
+ source->defer_update = true;
+ else
+ source->info.update(source->data, source->settings);
+ }
}
static void activate_source(obs_source_t source)
@@ -476,6 +486,9 @@ void obs_source_video_tick(obs_source_t source, float seconds)
{
if (!source) return;
+ if (source->defer_update)
+ obs_source_deferred_update(source);
+
/* reset the filter render texture information once every frame */
if (source->filter_texrender)
texrender_reset(source->filter_texrender);
diff --git a/libobs/obs.c b/libobs/obs.c
index 96335ee09..466b6e67c 100644
--- a/libobs/obs.c
+++ b/libobs/obs.c
@@ -500,6 +500,7 @@ void obs_shutdown(void)
da_free(obs->input_types);
da_free(obs->filter_types);
+ da_free(obs->encoder_types);
da_free(obs->transition_types);
da_free(obs->output_types);
da_free(obs->service_types);
diff --git a/libobs/obs.h b/libobs/obs.h
index 2a1e5fe4e..0d9a0bba5 100644
--- a/libobs/obs.h
+++ b/libobs/obs.h
@@ -751,6 +751,9 @@ EXPORT void obs_output_set_audio_conversion(obs_output_t output,
EXPORT bool obs_output_can_begin_data_capture(obs_output_t output,
uint32_t flags);
+/** Initializes encoders (if any) */
+EXPORT bool obs_output_initialize_encoders(obs_output_t output, uint32_t flags);
+
/**
* Begins data capture from media/encoders.
*
@@ -768,8 +771,13 @@ EXPORT bool obs_output_begin_data_capture(obs_output_t output, uint32_t flags);
/** Ends data capture from media/encoders */
EXPORT void obs_output_end_data_capture(obs_output_t output);
-/** Signals that start failed */
-EXPORT void obs_output_signal_start_fail(obs_output_t output, int code);
+/**
+ * Signals that the output has stopped itself.
+ *
+ * @param output Output context
+ * @param code Error code (or OBS_OUTPUT_SUCCESS if not an error)
+ */
+EXPORT void obs_output_signal_stop(obs_output_t output, int code);
/* ------------------------------------------------------------------------- */
@@ -787,7 +795,7 @@ EXPORT const char *obs_encoder_getdisplayname(const char *id,
* @param video Video output context to encode data from
* @return The video encoder context, or NULL if failed or not found.
*/
-EXPORT obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
+EXPORT obs_encoder_t obs_video_encoder_create(const char *id, const char *name,
obs_data_t settings, video_t video);
/**
@@ -799,39 +807,12 @@ EXPORT obs_encoder_t obs_encoder_create_video(const char *id, const char *name,
* @param audio Audio output context to encode data from
* @return The video encoder context, or NULL if failed or not found.
*/
-EXPORT obs_encoder_t obs_encoder_create_audio(const char *id, const char *name,
+EXPORT obs_encoder_t obs_audio_encoder_create(const char *id, const char *name,
obs_data_t settings, audio_t audio);
/** Destroys an encoder context */
EXPORT void obs_encoder_destroy(obs_encoder_t encoder);
-/**
- * Initializes the encoder and returns whether settings are valid.
- * Must be called before obs_encoder_start
- */
-EXPORT bool obs_encoder_initialize(obs_encoder_t encoder);
-
-/**
- * Starts encoding. This function can be called more than once, and each
- * callback will receive the same encoder data.
- *
- * @param encoder Encoder context
- * @param new_packet Callback that receives encoded packets
- * @param param Callback parameter
- */
-EXPORT void obs_encoder_start(obs_encoder_t encoder,
- void (*new_packet)(void *param, struct encoder_packet *packet),
- void *param);
-
-/**
- * Stops encoding. You must use the same callback/parameter combination that
- * was used for obs_encoder_start. Only when the last callback has been
- * removed will all encoding stop.
- */
-EXPORT void obs_encoder_stop(obs_encoder_t encoder,
- void (*new_packet)(void *param, struct encoder_packet *packet),
- void *param);
-
/** Returns the codec of the encoder */
EXPORT const char *obs_encoder_get_codec(obs_encoder_t encoder);
diff --git a/libobs/util/array-serializer.c b/libobs/util/array-serializer.c
index f2dc0c259..5670cbda9 100644
--- a/libobs/util/array-serializer.c
+++ b/libobs/util/array-serializer.c
@@ -33,6 +33,7 @@ static uint64_t array_output_get_pos(void *param)
void array_output_serializer_init(struct serializer *s,
struct array_output_data *data)
{
+ memset(s, 0, sizeof(struct serializer));
memset(data, 0, sizeof(struct array_output_data));
s->data = data;
s->write = array_output_write;
diff --git a/libobs/util/dstr.c b/libobs/util/dstr.c
index aaceb8334..43dd7edc8 100644
--- a/libobs/util/dstr.c
+++ b/libobs/util/dstr.c
@@ -257,7 +257,7 @@ char **strlist_split(const char *str, char split_ch, bool include_empty)
da_push_back(list, &new_str);
}
- cur_str = next_str;
+ cur_str = next_str+1;
next_str = strchr(cur_str, split_ch);
}
diff --git a/libobs/util/serializer.h b/libobs/util/serializer.h
index 4e812bcc7..711edd4ad 100644
--- a/libobs/util/serializer.h
+++ b/libobs/util/serializer.h
@@ -61,9 +61,9 @@ static inline size_t serialize(struct serializer *s, void *data, size_t len)
{
if (s) {
if (s->write)
- return s->write(s, data, len);
+ return s->write(s->data, data, len);
else if (s->read)
- return s->read(s, data, len);
+ return s->read(s->data, data, len);
}
return 0;
@@ -73,14 +73,14 @@ static inline uint64_t serializer_seek(struct serializer *s, int64_t offset,
enum serialize_seek_type seek_type)
{
if (s && s->seek)
- return s->seek(s, offset, seek_type);
+ return s->seek(s->data, offset, seek_type);
return 0;
}
static inline uint64_t serializer_get_pos(struct serializer *s)
{
if (s && s->get_pos)
- return s->get_pos(s);
+ return s->get_pos(s->data);
return 0;
}
diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp
index c83c2c178..a7b091740 100644
--- a/obs/window-basic-main.cpp
+++ b/obs/window-basic-main.cpp
@@ -35,6 +35,8 @@
#include "ui_OBSBasic.h"
+#include
+
using namespace std;
Q_DECLARE_METATYPE(OBSScene);
@@ -42,7 +44,9 @@ Q_DECLARE_METATYPE(OBSSceneItem);
OBSBasic::OBSBasic(QWidget *parent)
: OBSMainWindow (parent),
- outputTest (NULL),
+ outputTest (nullptr),
+ aac (nullptr),
+ x264 (nullptr),
sceneChanging (false),
resizeTimer (0),
properties (nullptr),
@@ -170,6 +174,8 @@ void OBSBasic::OBSInit()
* automatically later */
obs_load_module("test-input");
obs_load_module("obs-ffmpeg");
+ obs_load_module("obs-x264");
+ obs_load_module("obs-outputs");
#ifdef __APPLE__
obs_load_module("mac-capture");
#elif _WIN32
@@ -781,33 +787,35 @@ void OBSBasic::on_actionSourceDown_triggered()
{
}
-void OBSBasic::OutputStart(int errorcode)
+void OBSBasic::OutputStop(int errorcode)
{
- if (errorcode != OBS_OUTPUT_SUCCESS) {
- obs_output_destroy(outputTest);
- outputTest = NULL;
- } else {
- ui->streamButton->setText("Stop Streaming");
- }
+ UNUSED_PARAMETER(errorcode);
+ ui->streamButton->setText("Start Streaming");
+}
- ui->streamButton->setEnabled(true);
+void OBSBasic::OutputStart()
+{
+ ui->streamButton->setText("Stop Streaming");
}
static void OBSOutputStart(void *data, calldata_t params)
{
- int code = calldata_bool(params, "errorcode");
+ UNUSED_PARAMETER(params);
+ QMetaObject::invokeMethod(static_cast(data), "OutputStart");
+}
- QMetaObject::invokeMethod(static_cast(data),
- "OutputStart", Q_ARG(int, code));
+static void OBSOutputStop(void *data, calldata_t params)
+{
+ int code = (int)calldata_int(params, "errorcode");
+ QMetaObject::invokeMethod(static_cast(data), "OutputStop",
+ Q_ARG(int, code));
}
/* TODO: lots of temporary code */
void OBSBasic::on_streamButton_clicked()
{
- if (outputTest) {
- obs_output_destroy(outputTest);
- outputTest = NULL;
- ui->streamButton->setText("Start Streaming");
+ if (obs_output_active(outputTest)) {
+ obs_output_stop(outputTest);
} else {
const char *url = config_get_string(basicConfig, "OutputTemp",
"URL");
@@ -821,6 +829,14 @@ void OBSBasic::on_streamButton_clicked()
if (!url)
return;
+ obs_output_destroy(outputTest);
+ obs_encoder_destroy(aac);
+ obs_encoder_destroy(x264);
+ outputTest = nullptr;
+ aac = nullptr;
+ x264 = nullptr;
+
+#if 0
string fullURL = url;
if (key && *key)
fullURL = fullURL + "/" + key;
@@ -832,15 +848,50 @@ void OBSBasic::on_streamButton_clicked()
outputTest = obs_output_create("ffmpeg_output", "test", data);
obs_data_release(data);
+#else
+ obs_data_t aac_settings = obs_data_create();
+ obs_data_t x264_settings = obs_data_create();
+ obs_data_t output_settings = obs_data_create();
+ stringstream ss;
- if (!outputTest)
+ ss << "filler=1:crf=0:bitrate=" << vBitrate;
+
+ obs_data_setint(aac_settings, "bitrate", aBitrate);
+
+ obs_data_setint(x264_settings, "bitrate", vBitrate);
+ obs_data_setint(x264_settings, "buffer_size", vBitrate);
+ obs_data_setint(x264_settings, "keyint_sec", 2);
+ obs_data_setstring(x264_settings, "x264opts", ss.str().c_str());
+
+ obs_data_setstring(output_settings, "path", url);
+ obs_data_setstring(output_settings, "key", key);
+
+ aac = obs_audio_encoder_create("ffmpeg_aac", "blabla1",
+ aac_settings, obs_audio());
+ x264 = obs_video_encoder_create("obs_x264", "blabla2",
+ x264_settings, obs_video());
+ outputTest = obs_output_create("rtmp_output", "test",
+ output_settings);
+
+ obs_output_set_video_encoder(outputTest, x264);
+ obs_output_set_audio_encoder(outputTest, aac);
+
+ obs_data_release(aac_settings);
+ obs_data_release(x264_settings);
+ obs_data_release(output_settings);
+#endif
+
+ if (!outputTest) {
+ OutputStop(OBS_OUTPUT_FAIL);
return;
+ }
signal_handler_connect(obs_output_signalhandler(outputTest),
"start", OBSOutputStart, this);
+ signal_handler_connect(obs_output_signalhandler(outputTest),
+ "stop", OBSOutputStop, this);
obs_output_start(outputTest);
- ui->streamButton->setEnabled(false);
}
}
diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp
index 23045c021..2ea5f8504 100644
--- a/obs/window-basic-main.hpp
+++ b/obs/window-basic-main.hpp
@@ -35,6 +35,8 @@ class OBSBasic : public OBSMainWindow {
private:
std::unordered_map sourceSceneRefs;
obs_output_t outputTest;
+ obs_encoder_t aac;
+ obs_encoder_t x264;
bool sceneChanging;
int previewX, previewY;
@@ -61,7 +63,8 @@ private:
void InsertSceneItem(obs_sceneitem_t item);
public slots:
- void OutputStart(int errorcode);
+ void OutputStart();
+ void OutputStop(int errorcode);
private slots:
void AddSceneItem(OBSSceneItem item);
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-aac.c b/plugins/obs-ffmpeg/obs-ffmpeg-aac.c
index c16bf76e4..53dcd07e7 100644
--- a/plugins/obs-ffmpeg/obs-ffmpeg-aac.c
+++ b/plugins/obs-ffmpeg/obs-ffmpeg-aac.c
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
#include
@@ -30,11 +31,12 @@ struct aac_encoder {
AVCodec *aac;
AVCodecContext *context;
- struct circlebuf buffers[MAX_AV_PLANES];
uint8_t *samples[MAX_AV_PLANES];
AVFrame *aframe;
int total_samples;
+ DARRAY(uint8_t) packet_buffer;
+
size_t audio_planes;
size_t audio_size;
@@ -63,15 +65,14 @@ static void aac_destroy(void *data)
{
struct aac_encoder *enc = data;
- for (size_t i = 0; i < MAX_AV_PLANES; i++)
- circlebuf_free(&enc->buffers[i]);
-
if (enc->samples[0])
av_freep(&enc->samples[0]);
if (enc->context)
avcodec_close(enc->context);
if (enc->aframe)
av_frame_free(&enc->aframe);
+
+ da_free(enc->packet_buffer);
bfree(enc);
}
@@ -109,6 +110,18 @@ static bool initialize_codec(struct aac_encoder *enc)
return true;
}
+static void init_sizes(struct aac_encoder *enc, audio_t audio)
+{
+ const struct audio_output_info *aoi;
+ enum audio_format format;
+
+ aoi = audio_output_getinfo(audio);
+ format = convert_ffmpeg_sample_format(enc->context->sample_fmt);
+
+ enc->audio_planes = get_audio_planes(format, aoi->speakers);
+ enc->audio_size = get_audio_size(format, aoi->speakers, 1);
+}
+
static void *aac_create(obs_data_t settings, obs_encoder_t encoder)
{
struct aac_encoder *enc;
@@ -120,6 +133,8 @@ static void *aac_create(obs_data_t settings, obs_encoder_t encoder)
return NULL;
}
+ avcodec_register_all();
+
enc = bzalloc(sizeof(struct aac_encoder));
enc->encoder = encoder;
enc->aac = avcodec_find_encoder(AV_CODEC_ID_AAC);
@@ -128,22 +143,19 @@ static void *aac_create(obs_data_t settings, obs_encoder_t encoder)
goto fail;
}
- avcodec_register(enc->aac);
-
enc->context = avcodec_alloc_context3(enc->aac);
if (!enc->context) {
aac_warn("aac_create", "Failed to create codec context");
goto fail;
}
- enc->context->bit_rate = bitrate;
+ enc->context->bit_rate = bitrate * 1000;
enc->context->channels = (int)audio_output_channels(audio);
enc->context->sample_rate = audio_output_samplerate(audio);
enc->context->sample_fmt = enc->aac->sample_fmts ?
enc->aac->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
- enc->audio_planes = audio_output_planes(audio);
- enc->audio_size = audio_output_blocksize(audio);
+ init_sizes(enc, audio);
/* enable experimental FFmpeg encoder if the only one available */
enc->context->strict_std_compliance = -2;
@@ -192,9 +204,12 @@ static bool do_aac_encode(struct aac_encoder *enc,
if (!got_packet)
return true;
+ da_resize(enc->packet_buffer, 0);
+ da_push_back_array(enc->packet_buffer, avpacket.data, avpacket.size);
+
packet->pts = rescale_ts(avpacket.pts, enc->context, time_base);
packet->dts = rescale_ts(avpacket.dts, enc->context, time_base);
- packet->data = bmemdup(avpacket.data, avpacket.size);
+ packet->data = enc->packet_buffer.array;
packet->size = avpacket.size;
packet->type = OBS_ENCODER_AUDIO;
packet->timebase_num = 1;
@@ -207,19 +222,9 @@ static bool aac_encode(void *data, struct encoder_frame *frame,
struct encoder_packet *packet, bool *received_packet)
{
struct aac_encoder *enc = data;
- size_t size = frame->frames * enc->audio_size;
for (size_t i = 0; i < enc->audio_planes; i++)
- circlebuf_push_back(&enc->buffers[i], frame->data[i], size);
-
- if (enc->buffers[0].size < (size_t)enc->frame_size_bytes) {
- *received_packet = false;
- return true;
- }
-
- for (size_t i = 0; i < enc->audio_planes; i++)
- circlebuf_pop_front(&enc->buffers[i], enc->samples[i],
- enc->frame_size_bytes);
+ memcpy(enc->samples[i], frame->data[i], enc->frame_size_bytes);
return do_aac_encode(enc, packet, received_packet);
}
@@ -234,8 +239,7 @@ static obs_properties_t aac_properties(const char *locale)
obs_properties_t props = obs_properties_create(locale);
/* TODO: locale */
- obs_properties_add_int(props, "bitrate", "Bitrate",
- 32, 320, 32);
+ obs_properties_add_int(props, "bitrate", "Bitrate", 32, 320, 32);
return props;
}
@@ -257,6 +261,12 @@ static bool aac_audio_info(void *data, struct audio_convert_info *info)
return true;
}
+static size_t aac_frame_size(void *data)
+{
+ struct aac_encoder *enc =data;
+ return enc->frame_size;
+}
+
struct obs_encoder_info aac_encoder_info = {
.id = "ffmpeg_aac",
.type = OBS_ENCODER_AUDIO,
@@ -265,6 +275,7 @@ struct obs_encoder_info aac_encoder_info = {
.create = aac_create,
.destroy = aac_destroy,
.encode = aac_encode,
+ .frame_size = aac_frame_size,
.defaults = aac_defaults,
.properties = aac_properties,
.extra_data = aac_extra_data,
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c
index 2c4579329..2eca7304a 100644
--- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c
+++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c
@@ -760,7 +760,7 @@ static void *start_thread(void *data)
struct ffmpeg_output *output = data;
if (!try_connect(output))
- obs_output_signal_start_fail(output->output,
+ obs_output_signal_stop(output->output,
OBS_OUTPUT_CONNECT_FAILED);
output->connecting = false;
diff --git a/plugins/obs-ffmpeg/obs-ffmpeg.c b/plugins/obs-ffmpeg/obs-ffmpeg.c
index b00cc2b46..7e6addc0f 100644
--- a/plugins/obs-ffmpeg/obs-ffmpeg.c
+++ b/plugins/obs-ffmpeg/obs-ffmpeg.c
@@ -2,11 +2,13 @@
OBS_DECLARE_MODULE()
-extern struct obs_output_info ffmpeg_output;
+extern struct obs_output_info ffmpeg_output;
+extern struct obs_encoder_info aac_encoder_info;
bool obs_module_load(uint32_t obs_version)
{
obs_register_output(&ffmpeg_output);
+ obs_register_encoder(&aac_encoder_info);
UNUSED_PARAMETER(obs_version);
return true;
diff --git a/plugins/obs-outputs/flv-mux.c b/plugins/obs-outputs/flv-mux.c
index 76df55a2f..5a13f8d9b 100644
--- a/plugins/obs-outputs/flv-mux.c
+++ b/plugins/obs-outputs/flv-mux.c
@@ -21,6 +21,11 @@
#include "obs-output-ver.h"
#include "rtmp-helpers.h"
+/* FIXME: this is currently hard-coded to h264 and aac! ..not that we'll
+ * use anything else for a long time. */
+
+// #define DEBUG_TIMESTAMPS
+
#define VIDEO_HEADER_SIZE 5
#define MILLISECOND_DEN 1000
@@ -44,28 +49,30 @@ static void build_flv_meta_data(obs_output_t context,
char *enc = buf;
char *end = enc+sizeof(buf);
+ enc_str(&enc, end, "onMetaData");
+
*enc++ = AMF_ECMA_ARRAY;
enc = AMF_EncodeInt32(enc, end, 14);
- enc_num(&enc, end, "duration", 0.0);
- enc_num(&enc, end, "fileSize", 0.0);
+ enc_num_val(&enc, end, "duration", 0.0);
+ enc_num_val(&enc, end, "fileSize", 0.0);
- enc_num(&enc, end, "width", (double)video_output_width(video));
- enc_num(&enc, end, "height", (double)video_output_height(video));
- enc_str(&enc, end, "videocodecid", "avc1");
- enc_num(&enc, end, "videodatarate", encoder_bitrate(vencoder));
- enc_num(&enc, end, "framerate", video_output_framerate(video));
+ enc_num_val(&enc, end, "width", (double)video_output_width(video));
+ enc_num_val(&enc, end, "height", (double)video_output_height(video));
+ enc_str_val(&enc, end, "videocodecid", "avc1");
+ enc_num_val(&enc, end, "videodatarate", encoder_bitrate(vencoder));
+ enc_num_val(&enc, end, "framerate", video_output_framerate(video));
- enc_str(&enc, end, "audiocodecid", "mp4a");
- enc_num(&enc, end, "audiodatarate", encoder_bitrate(aencoder));
- enc_num(&enc, end, "audiosamplerate",
+ enc_str_val(&enc, end, "audiocodecid", "mp4a");
+ enc_num_val(&enc, end, "audiodatarate", encoder_bitrate(aencoder));
+ enc_num_val(&enc, end, "audiosamplerate",
(double)audio_output_samplerate(audio));
- enc_num(&enc, end, "audiosamplesize", 16.0);
- enc_num(&enc, end, "audiochannels",
+ enc_num_val(&enc, end, "audiosamplesize", 16.0);
+ enc_num_val(&enc, end, "audiochannels",
(double)audio_output_channels(audio));
- enc_bool(&enc, end, "stereo", audio_output_channels(audio) == 2);
- enc_str(&enc, end, "encoder", MODULE_NAME);
+ enc_bool_val(&enc, end, "stereo", audio_output_channels(audio) == 2);
+ enc_str_val(&enc, end, "encoder", MODULE_NAME);
*enc++ = 0;
*enc++ = 0;
@@ -81,19 +88,29 @@ void flv_meta_data(obs_output_t context, uint8_t **output, size_t *size)
struct serializer s;
uint8_t *meta_data;
size_t meta_data_size;
+ uint32_t start_pos;
array_output_serializer_init(&s, &data);
build_flv_meta_data(context, &meta_data, &meta_data_size);
+ /* s_write(&s, "FLV", 3);
+ s_w8(&s, 1);
+ s_w8(&s, 5);
+ s_wb32(&s, 9);
+ s_wb32(&s, 0); */
+
+ start_pos = serializer_get_pos(&s);
+
s_w8(&s, RTMP_PACKET_TYPE_INFO);
s_wb24(&s, (uint32_t)meta_data_size);
s_wb32(&s, 0);
s_wb24(&s, 0);
+
s_write(&s, meta_data, meta_data_size);
- s_wb32(&s, (uint32_t)serializer_get_pos(&s) + 4 - 1);
+ s_wb32(&s, (uint32_t)serializer_get_pos(&s) - start_pos + 4 - 1);
*output = data.bytes.array;
*size = data.bytes.num;
@@ -109,15 +126,21 @@ static uint32_t get_ms_time(struct encoder_packet *packet, int64_t val)
static void flv_video(struct serializer *s, 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);
if (!packet->data || !packet->size)
return;
s_w8(s, RTMP_PACKET_TYPE_VIDEO);
+#ifdef DEBUG_TIMESTAMPS
+ blog(LOG_DEBUG, "Video: %lu", time_ms);
+#endif
+
s_wb24(s, (uint32_t)packet->size + 5);
- s_wb32(s, get_ms_time(packet, packet->pts));
+ s_wb24(s, time_ms);
+ s_w8(s, (time_ms >> 24) & 0x7F);
s_wb24(s, 0);
/* these are the 5 extra bytes mentioned above */
@@ -133,13 +156,20 @@ static void flv_video(struct serializer *s, struct encoder_packet *packet,
static void flv_audio(struct serializer *s, struct encoder_packet *packet,
bool is_header)
{
+ int32_t time_ms = get_ms_time(packet, packet->dts);
+
if (!packet->data || !packet->size)
return;
s_w8(s, RTMP_PACKET_TYPE_AUDIO);
+#ifdef DEBUG_TIMESTAMPS
+ blog(LOG_DEBUG, "Audio: %lu", time_ms);
+#endif
+
s_wb24(s, (uint32_t)packet->size + 2);
- s_wb32(s, get_ms_time(packet, packet->pts));
+ s_wb24(s, time_ms);
+ s_w8(s, (time_ms >> 24) & 0x7F);
s_wb24(s, 0);
/* these are the two extra bytes mentioned above */
diff --git a/plugins/obs-outputs/obs-outputs.c b/plugins/obs-outputs/obs-outputs.c
index 8ee3db037..92be62749 100644
--- a/plugins/obs-outputs/obs-outputs.c
+++ b/plugins/obs-outputs/obs-outputs.c
@@ -1,13 +1,30 @@
#include
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include
+#endif
+
OBS_DECLARE_MODULE()
extern struct obs_output_info rtmp_output_info;
bool obs_module_load(uint32_t libobs_ver)
{
+#ifdef _WIN32
+ WSADATA wsad;
+ WSAStartup(MAKEWORD(2, 2), &wsad);
+#endif
+
obs_register_output(&rtmp_output_info);
UNUSED_PARAMETER(libobs_ver);
return true;
}
+
+#ifdef _WIN32
+void obs_module_unload(void)
+{
+ WSACleanup();
+}
+#endif
diff --git a/plugins/obs-outputs/rtmp-helpers.h b/plugins/obs-outputs/rtmp-helpers.h
index 4861e9e91..69d38b1b0 100644
--- a/plugins/obs-outputs/rtmp-helpers.h
+++ b/plugins/obs-outputs/rtmp-helpers.h
@@ -24,19 +24,21 @@ static inline AVal *flv_str(AVal *out, const char *str)
return out;
}
-static inline void enc_num(char **enc, char *end, const char *name, double val)
+static inline void enc_num_val(char **enc, char *end, const char *name,
+ double val)
{
AVal s;
*enc = AMF_EncodeNamedNumber(*enc, end, flv_str(&s, name), val);
}
-static inline void enc_bool(char **enc, char *end, const char *name, bool val)
+static inline void enc_bool_val(char **enc, char *end, const char *name,
+ bool val)
{
AVal s;
*enc = AMF_EncodeNamedBoolean(*enc, end, flv_str(&s, name), val);
}
-static inline void enc_str(char **enc, char *end, const char *name,
+static inline void enc_str_val(char **enc, char *end, const char *name,
const char *val)
{
AVal s1, s2;
@@ -44,3 +46,9 @@ static inline void enc_str(char **enc, char *end, const char *name,
flv_str(&s1, name),
flv_str(&s2, val));
}
+
+static inline void enc_str(char **enc, char *end, const char *str)
+{
+ AVal s;
+ *enc = AMF_EncodeString(*enc, end, flv_str(&s, str));
+}
diff --git a/plugins/obs-outputs/rtmp-stream.c b/plugins/obs-outputs/rtmp-stream.c
index d110b6737..63bf56f7e 100644
--- a/plugins/obs-outputs/rtmp-stream.c
+++ b/plugins/obs-outputs/rtmp-stream.c
@@ -17,6 +17,7 @@
#include
#include
+#include
#include
#include
#include
@@ -24,6 +25,8 @@
#include "librtmp/log.h"
#include "flv-mux.h"
+//#define FILE_TEST
+
struct rtmp_stream {
obs_output_t output;
@@ -42,6 +45,10 @@ struct rtmp_stream {
struct dstr path, key;
struct dstr username, password;
+#ifdef FILE_TEST
+ FILE *test;
+#endif
+
RTMP rtmp;
};
@@ -68,15 +75,11 @@ static inline void free_packets(struct rtmp_stream *stream)
}
}
-static void rtmp_stream_stop(void *data);
-
static void rtmp_stream_destroy(void *data)
{
struct rtmp_stream *stream = data;
if (stream) {
- rtmp_stream_stop(stream);
-
free_packets(stream);
dstr_free(&stream->path);
dstr_free(&stream->key);
@@ -86,6 +89,7 @@ static void rtmp_stream_destroy(void *data)
os_event_destroy(stream->stop_event);
os_sem_destroy(stream->send_sem);
pthread_mutex_destroy(&stream->packets_mutex);
+ circlebuf_free(&stream->packets);
bfree(stream);
}
}
@@ -117,6 +121,10 @@ static void rtmp_stream_stop(void *data)
struct rtmp_stream *stream = data;
void *ret;
+#ifdef FILE_TEST
+ fclose(stream->test);
+#endif
+
os_event_signal(stream->stop_event);
if (stream->connecting)
@@ -133,7 +141,7 @@ static void rtmp_stream_stop(void *data)
static inline void set_rtmp_str(AVal *val, const char *str)
{
- bool valid = (!str || !*str);
+ bool valid = (str && *str);
val->av_val = valid ? (char*)str : NULL;
val->av_len = valid ? (int)strlen(str) : 0;
}
@@ -166,10 +174,14 @@ static int send_packet(struct rtmp_stream *stream,
{
uint8_t *data;
size_t size;
- int ret;
+ int ret = 0;
flv_packet_mux(packet, &data, &size, is_header);
+#ifdef FILE_TEST
+ fwrite(data, 1, size, stream->test);
+#else
ret = RTMP_Write(&stream->rtmp, (char*)data, (int)size);
+#endif
bfree(data);
obs_free_encoder_packet(packet);
@@ -211,8 +223,10 @@ static void *send_thread(void *data)
if (disconnected)
free_packets(stream);
- if (os_event_try(stream->stop_event) == EAGAIN)
+ if (os_event_try(stream->stop_event) == EAGAIN) {
pthread_detach(stream->send_thread);
+ obs_output_signal_stop(stream->output, OBS_OUTPUT_DISCONNECTED);
+ }
stream->active = false;
return NULL;
@@ -226,7 +240,11 @@ static void send_meta_data(struct rtmp_stream *stream)
size_t meta_data_size;
flv_meta_data(stream->output, &meta_data, &meta_data_size);
+#ifdef FILE_TEST
+ fwrite(meta_data, 1, meta_data_size, stream->test);
+#else
RTMP_Write(&stream->rtmp, (char*)meta_data, (int)meta_data_size);
+#endif
bfree(meta_data);
}
@@ -234,13 +252,15 @@ static void send_audio_header(struct rtmp_stream *stream)
{
obs_output_t context = stream->output;
obs_encoder_t aencoder = obs_output_get_audio_encoder(context);
+ uint8_t *header;
struct encoder_packet packet = {
- .type = OBS_ENCODER_AUDIO,
+ .type = OBS_ENCODER_AUDIO,
.timebase_den = 1
};
- obs_encoder_get_extra_data(aencoder, &packet.data, &packet.size);
+ obs_encoder_get_extra_data(aencoder, &header, &packet.size);
+ packet.data = bmemdup(header, packet.size);
send_packet(stream, &packet, true);
}
@@ -252,18 +272,21 @@ static void send_video_header(struct rtmp_stream *stream)
size_t size;
struct encoder_packet packet = {
- .type = OBS_ENCODER_VIDEO,
- .timebase_den = 1
+ .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);
send_packet(stream, &packet, true);
- obs_free_encoder_packet(&packet);
}
static void send_headers(struct rtmp_stream *stream)
{
+#ifdef FILE_TEST
+ stream->test = os_fopen("D:\\bla.flv", "wb");
+#endif
send_meta_data(stream);
send_audio_header(stream);
send_video_header(stream);
@@ -302,8 +325,10 @@ static int init_send(struct rtmp_stream *stream)
return OBS_OUTPUT_FAIL;
}
+ stream->active = true;
send_headers(stream);
obs_output_begin_data_capture(stream->output, 0);
+
return OBS_OUTPUT_SUCCESS;
}
@@ -313,6 +338,8 @@ static int try_connect(struct rtmp_stream *stream)
stream->key.array))
return OBS_OUTPUT_BAD_PATH;
+ RTMP_EnableWrite(&stream->rtmp);
+
set_rtmp_dstr(&stream->rtmp.Link.pubUser, &stream->username);
set_rtmp_dstr(&stream->rtmp.Link.pubPasswd, &stream->password);
stream->rtmp.Link.swfUrl = stream->rtmp.Link.tcUrl;
@@ -323,10 +350,12 @@ static int try_connect(struct rtmp_stream *stream)
stream->rtmp.m_bSendChunkSizeInfo = true;
stream->rtmp.m_bUseNagle = true;
+#ifndef FILE_TEST
if (!RTMP_Connect(&stream->rtmp, NULL))
return OBS_OUTPUT_CONNECT_FAILED;
if (!RTMP_ConnectStream(&stream->rtmp, 0))
return OBS_OUTPUT_INVALID_STREAM;
+#endif
return init_send(stream);
}
@@ -337,7 +366,7 @@ static void *connect_thread(void *data)
int ret = try_connect(stream);
if (ret != OBS_OUTPUT_SUCCESS)
- obs_output_signal_start_fail(stream->output, ret);
+ obs_output_signal_stop(stream->output, ret);
if (os_event_try(stream->stop_event) == EAGAIN)
pthread_detach(stream->connect_thread);
@@ -353,6 +382,8 @@ static bool rtmp_stream_start(void *data)
if (!obs_output_can_begin_data_capture(stream->output, 0))
return false;
+ if (!obs_output_initialize_encoders(stream->output, 0))
+ return false;
settings = obs_output_get_settings(stream->output);
dstr_copy(&stream->path, obs_data_getstring(settings, "path"));
@@ -362,7 +393,7 @@ static bool rtmp_stream_start(void *data)
obs_data_release(settings);
return pthread_create(&stream->connect_thread, NULL, connect_thread,
- stream) != 0;
+ stream) == 0;
}
static void rtmp_stream_data(void *data, struct encoder_packet *packet)
@@ -405,5 +436,5 @@ struct obs_output_info rtmp_output_info = {
.start = rtmp_stream_start,
.stop = rtmp_stream_stop,
.encoded_packet = rtmp_stream_data,
- .properties = rtmp_stream_properties
+ .properties = rtmp_stream_properties
};
diff --git a/plugins/obs-x264/obs-x264.c b/plugins/obs-x264/obs-x264.c
index a425bef45..d8022e591 100644
--- a/plugins/obs-x264/obs-x264.c
+++ b/plugins/obs-x264/obs-x264.c
@@ -190,8 +190,6 @@ static inline void set_param(struct obs_x264 *obsx264, const char *param)
static inline void apply_x264_profile(struct obs_x264 *obsx264,
const char *profile)
{
- if (!*profile) profile = NULL;
-
if (!obsx264->context && profile) {
int ret = x264_param_apply_profile(&obsx264->params, profile);
if (ret != 0)
@@ -203,9 +201,6 @@ static inline void apply_x264_profile(struct obs_x264 *obsx264,
static bool reset_x264_params(struct obs_x264 *obsx264,
const char *preset, const char *tune)
{
- if (!*preset) preset = NULL;
- if (!*tune) tune = NULL;
-
return x264_param_default_preset(&obsx264->params, preset, tune) == 0;
}
@@ -278,6 +273,8 @@ static bool update_settings(struct obs_x264 *obsx264, obs_data_t settings)
apply_x264_profile(obsx264, profile);
}
+ obsx264->params.b_repeat_headers = false;
+
strlist_free(paramlist);
bfree(preset);
bfree(profile);
@@ -355,18 +352,6 @@ static void *obs_x264_create(obs_data_t settings, obs_encoder_t encoder)
return obsx264;
}
-static inline int drop_priority(int priority)
-{
- switch (priority) {
- case NAL_PRIORITY_DISPOSABLE: return NAL_PRIORITY_DISPOSABLE;
- case NAL_PRIORITY_LOW: return NAL_PRIORITY_LOW;
- case NAL_PRIORITY_HIGH: return NAL_PRIORITY_HIGHEST;
- case NAL_PRIORITY_HIGHEST: return NAL_PRIORITY_HIGHEST;
- }
-
- return NAL_PRIORITY_HIGHEST;
-}
-
static void parse_packet(struct obs_x264 *obsx264,
struct encoder_packet *packet, x264_nal_t *nals,
int nal_count, x264_picture_t *pic_out)
@@ -386,9 +371,7 @@ static void parse_packet(struct obs_x264 *obsx264,
packet->type = OBS_ENCODER_VIDEO;
packet->pts = pic_out->i_pts;
packet->dts = pic_out->i_dts;
- packet->keyframe = nals[0].i_type == NAL_SLICE_IDR;
- packet->priority = nals[0].i_ref_idc;
- packet->drop_priority = drop_priority(nals[0].i_ref_idc);
+ packet->keyframe = pic_out->b_keyframe != 0;
}
static inline void init_pic_data(struct obs_x264 *obsx264, x264_picture_t *pic,
diff --git a/test/osx/test.mm b/test/osx/test.mm
index aa2e1fcb0..d2abfc925 100644
--- a/test/osx/test.mm
+++ b/test/osx/test.mm
@@ -93,6 +93,7 @@ static void AddTestItems(obs_scene_t scene, obs_source_t source)
-(void)windowWillClose:(NSNotification *)notification
{
+ (void)notification;
[NSApp stop:self];
}
@end
@@ -183,7 +184,7 @@ static void test()
obs_shutdown();
- blog(LOG_INFO, "Number of memory leaks: %llu", bnum_allocs());
+ blog(LOG_INFO, "Number of memory leaks: %lu", bnum_allocs());
}
/* --------------------------------------------------- */
diff --git a/vs/2013/libobs-d3d11/libobs-d3d11.vcxproj b/vs/2013/libobs-d3d11/libobs-d3d11.vcxproj
index fda817d1f..c5b6bb516 100644
--- a/vs/2013/libobs-d3d11/libobs-d3d11.vcxproj
+++ b/vs/2013/libobs-d3d11/libobs-d3d11.vcxproj
@@ -102,7 +102,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBSD3D11_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
Windows
@@ -122,7 +121,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBSD3D11_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
Windows
@@ -144,7 +142,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBSD3D11_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
Windows
@@ -168,7 +165,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBSD3D11_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
Windows
diff --git a/vs/2013/libobs-opengl/libobs-opengl.vcxproj b/vs/2013/libobs-opengl/libobs-opengl.vcxproj
index 2cf73044b..7d06a44c8 100644
--- a/vs/2013/libobs-opengl/libobs-opengl.vcxproj
+++ b/vs/2013/libobs-opengl/libobs-opengl.vcxproj
@@ -86,7 +86,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBSOPENGL_EXPORTS;GLEW_STATIC;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
false
@@ -107,7 +106,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBSOPENGL_EXPORTS;GLEW_STATIC;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
false
@@ -130,7 +128,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBSOPENGL_EXPORTS;GLEW_STATIC;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
false
@@ -155,7 +152,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBSOPENGL_EXPORTS;GLEW_STATIC;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
false
diff --git a/vs/2013/libobs/libobs.vcxproj b/vs/2013/libobs/libobs.vcxproj
index 726c3050d..fb1b56387 100644
--- a/vs/2013/libobs/libobs.vcxproj
+++ b/vs/2013/libobs/libobs.vcxproj
@@ -204,7 +204,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)
false
- MultiThreadedDebug
../../../libobs/util/vc
ProgramDatabase
@@ -226,7 +225,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)
false
- MultiThreadedDebug
../../../libobs/util/vc
@@ -249,7 +247,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)
false
- MultiThreaded
../../../libobs/util/vc
@@ -274,7 +271,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOBS_EXPORTS;PTW32_STATIC_LIB;%(PreprocessorDefinitions)
false
- MultiThreaded
../../../libobs/util/vc
diff --git a/vs/2013/obs-studio/obs-studio.vcxproj b/vs/2013/obs-studio/obs-studio.vcxproj
index 573c9300a..f40f4e234 100644
--- a/vs/2013/obs-studio/obs-studio.vcxproj
+++ b/vs/2013/obs-studio/obs-studio.vcxproj
@@ -485,8 +485,8 @@
../../../libobs;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;..\..\..\obs;%(AdditionalIncludeDirectories)
Disabled
ProgramDatabase
- MultiThreadedDebugDLL
true
+ MultiThreadedDebugDLL
Windows
@@ -505,8 +505,8 @@
../../../libobs;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;..\..\..\obs;%(AdditionalIncludeDirectories)
Disabled
ProgramDatabase
- MultiThreadedDebugDLL
true
+ MultiThreadedDebugDLL
Windows
@@ -524,7 +524,6 @@
UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions)
../../../libobs;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;..\..\..\obs;%(AdditionalIncludeDirectories)
- MultiThreadedDLL
true
@@ -543,7 +542,6 @@
UNICODE;WIN32;WIN64;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_WIDGETS_LIB;%(PreprocessorDefinitions)
../../../libobs;.\GeneratedFiles;.;$(QTDIR)\include;.\GeneratedFiles\$(ConfigurationName);$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtWidgets;..\..\..\obs;%(AdditionalIncludeDirectories)
- MultiThreadedDLL
true
diff --git a/vs/2013/pthreads/pthreads.vcxproj b/vs/2013/pthreads/pthreads.vcxproj
index 98d8d0c68..45667deaa 100644
--- a/vs/2013/pthreads/pthreads.vcxproj
+++ b/vs/2013/pthreads/pthreads.vcxproj
@@ -75,7 +75,6 @@
Disabled
__CLEANUP_C;WIN32;_DEBUG;_WINDOWS;PTW32_BUILD;%(PreprocessorDefinitions)
false
- MultiThreadedDebug
Windows
@@ -93,7 +92,6 @@
Disabled
__CLEANUP_C;WIN32;_DEBUG;_WINDOWS;_LIB;PTW32_BUILD;%(PreprocessorDefinitions)
false
- MultiThreadedDebug
Windows
@@ -113,7 +111,6 @@
true
__CLEANUP_C;WIN32;NDEBUG;_WINDOWS;_LIB;PTW32_BUILD;%(PreprocessorDefinitions)
false
- MultiThreaded
Windows
@@ -135,7 +132,6 @@
true
__CLEANUP_C;WIN32;NDEBUG;_WINDOWS;_LIB;PTW32_BUILD;%(PreprocessorDefinitions)
false
- MultiThreaded
Windows
diff --git a/vs/2013/test-input/test-input.vcxproj b/vs/2013/test-input/test-input.vcxproj
index 6dd8b0142..9a815d7c9 100644
--- a/vs/2013/test-input/test-input.vcxproj
+++ b/vs/2013/test-input/test-input.vcxproj
@@ -92,7 +92,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;TESTINPUT_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
Windows
@@ -112,7 +111,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;_USRDLL;TESTINPUT_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
Windows
@@ -134,7 +132,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;TESTINPUT_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
Windows
@@ -158,7 +155,6 @@
true
WIN32;NDEBUG;_WINDOWS;_USRDLL;TESTINPUT_EXPORTS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
Windows
diff --git a/vs/2013/wintest/wintest.vcxproj b/vs/2013/wintest/wintest.vcxproj
index 79935b1a6..9bfa340e4 100644
--- a/vs/2013/wintest/wintest.vcxproj
+++ b/vs/2013/wintest/wintest.vcxproj
@@ -89,7 +89,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
Windows
@@ -109,7 +108,6 @@
Disabled
WIN32;_DEBUG;_WINDOWS;GLEW_STATIC;%(PreprocessorDefinitions)
../../../libobs
- MultiThreadedDebug
Windows
@@ -131,7 +129,6 @@
true
WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
Windows
@@ -155,7 +152,6 @@
true
WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
../../../libobs
- MultiThreaded
Windows