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