提交 6839ff76 编写于 作者: J jp9000

libobs: Implement transition sources

Transition sources are implemented by registering a source type as
OBS_SOURCE_TYPE_TRANSITION.  They're automatically marked as video
composite sources, and video_render/audio_render callbacks must be set
when registering the source.  get_width and get_height callbacks are
unused for these types of sources, as transitions automatically handle
width/height behind the scenes with the transition settings.

In the video_render callback, the helper function
obs_transition_video_render is used to assist in automatically
processing and rendering the audio.  A render callback is passed to the
function, which in turn passes to/from textures that are automatically
rendered in the back-end.

Similarly, in the audio_render callback, the helper function
obs_transition_audio_render is used to assist in automatically
processing and rendering the audio.  Two mix callbacks are used to
handle how the source/destination sources are mixed together.  To ensure
the best possible quality, audio processing is per-sample.

Transitions can be set to automatically resize, or they can be set to
have a fixed size.  Sources within transitions can be made to scale to
the transition size (with or without aspect ratio), or to not scale
unless they're bigger than the transition.  They can have a specific
alignment within the transition, or they just default to top-left.
These features are implemented for the purpose of extending transitions
to also act as "switch" sources later, where you can switch to/from two
different sources using the transition animation.

Planned (but not yet implemented and lower priority) features:

- "Switch" transitions which allow the ability to switch back and forth
  between two sources with a transitioning animation without discarding
  the references

- Easing options to allow the option to transition with a bezier or
  custom curve

- Manual transitioning to allow the front-end/user to manually control
  the transition offset
上级 c1227b34
......@@ -273,6 +273,7 @@ set(libobs_libobs_SOURCES
obs-encoder.c
obs-service.c
obs-source.c
obs-source-transition.c
obs-output.c
obs-output-delay.c
obs.c
......
......@@ -28,6 +28,7 @@
#include "callback/proc.h"
#include "graphics/graphics.h"
#include "graphics/matrix4.h"
#include "media-io/audio-resampler.h"
#include "media-io/video-io.h"
......@@ -596,6 +597,27 @@ struct obs_source {
uint64_t push_to_mute_stop_time;
uint64_t push_to_talk_delay;
uint64_t push_to_talk_stop_time;
/* transitions */
uint64_t transition_start_time;
uint64_t transition_duration;
pthread_mutex_t transition_tex_mutex;
gs_texrender_t *transition_texrender[2];
pthread_mutex_t transition_mutex;
obs_source_t *transition_sources[2];
bool transitioning_video;
bool transitioning_audio;
bool transition_source_active[2];
uint32_t transition_alignment;
uint32_t transition_actual_cx;
uint32_t transition_actual_cy;
uint32_t transition_cx;
uint32_t transition_cy;
uint32_t transition_fixed_duration;
bool transition_use_fixed_duration : 1;
enum obs_transition_mode transition_mode;
enum obs_transition_scale_type transition_scale_type;
struct matrix4 transition_matrices[2];
};
extern const struct obs_source_info *get_source_info(const char *id);
......@@ -606,6 +628,14 @@ extern bool obs_source_init_context(struct obs_source *source,
extern void obs_source_save(obs_source_t *source);
extern void obs_source_load(obs_source_t *source);
extern bool obs_transition_init(obs_source_t *transition);
extern void obs_transition_free(obs_source_t *transition);
extern void obs_transition_tick(obs_source_t *transition);
extern void obs_transition_enum_sources(obs_source_t *transition,
obs_source_enum_proc_t enum_callback, void *param);
extern void obs_transition_save(obs_source_t *source, obs_data_t *data);
extern void obs_transition_load(obs_source_t *source, obs_data_t *data);
extern void obs_source_destroy(struct obs_source *source);
enum view_type {
......
此差异已折叠。
......@@ -70,6 +70,9 @@ static const char *source_signals[] = {
"void filter_add(ptr source, ptr filter)",
"void filter_remove(ptr source, ptr filter)",
"void reorder_filters(ptr source)",
"void transition_start(ptr source)",
"void transition_video_stop(ptr source)",
"void transition_stop(ptr source)",
NULL
};
......@@ -161,6 +164,11 @@ bool obs_source_init(struct obs_source *source)
pthread_mutex_unlock(&obs->data.audio_sources_mutex);
}
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {
if (!obs_transition_init(source))
return false;
}
source->control = bzalloc(sizeof(obs_weak_source_t));
source->control->source = source;
source->audio_mixers = 0xF;
......@@ -351,6 +359,9 @@ void obs_source_destroy(struct obs_source *source)
if (!obs_source_valid(source, "obs_source_destroy"))
return;
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
obs_transition_clear(source);
pthread_mutex_lock(&obs->data.audio_sources_mutex);
if (source->prev_next_audio_source) {
*source->prev_next_audio_source = source->next_audio_source;
......@@ -400,6 +411,9 @@ void obs_source_destroy(struct obs_source *source)
audio_resampler_destroy(source->resampler);
bfree(source->audio_output_buf[0][0]);
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
obs_transition_free(source);
da_free(source->audio_actions);
da_free(source->async_cache);
da_free(source->async_frames);
......@@ -793,6 +807,9 @@ void obs_source_video_tick(obs_source_t *source, float seconds)
if (!obs_source_valid(source, "obs_source_video_tick"))
return;
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
obs_transition_tick(source);
if ((source->info.output_flags & OBS_SOURCE_ASYNC) != 0) {
uint64_t sys_time = obs->video.video_time;
......@@ -1548,7 +1565,10 @@ static uint32_t get_base_width(const obs_source_t *source)
{
bool is_filter = (source->info.type == OBS_SOURCE_TYPE_FILTER);
if (source->info.get_width && (!is_filter || source->enabled)) {
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {
return source->enabled ? source->transition_actual_cx : 0;
} else if (source->info.get_width && (!is_filter || source->enabled)) {
return source->info.get_width(source->context.data);
} else if (source->info.type == OBS_SOURCE_TYPE_FILTER) {
......@@ -1562,7 +1582,10 @@ static uint32_t get_base_height(const obs_source_t *source)
{
bool is_filter = (source->info.type == OBS_SOURCE_TYPE_FILTER);
if (source->info.get_height && (!is_filter || source->enabled)) {
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION) {
return source->enabled ? source->transition_actual_cy : 0;
} else if (source->info.get_height && (!is_filter || source->enabled)) {
return source->info.get_height(source->context.data);
} else if (is_filter) {
......@@ -2703,15 +2726,21 @@ void obs_source_enum_active_sources(obs_source_t *source,
obs_source_enum_proc_t enum_callback,
void *param)
{
bool is_transition;
if (!data_valid(source, "obs_source_enum_active_sources"))
return;
if (!source->info.enum_active_sources)
is_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;
if (!is_transition && !source->info.enum_active_sources)
return;
obs_source_addref(source);
source->info.enum_active_sources(source->context.data, enum_callback,
param);
if (is_transition)
obs_transition_enum_sources(source, enum_callback, param);
if (source->info.enum_active_sources)
source->info.enum_active_sources(source->context.data,
enum_callback, param);
obs_source_release(source);
}
......@@ -2721,17 +2750,23 @@ void obs_source_enum_active_tree(obs_source_t *source,
void *param)
{
struct source_enum_data data = {enum_callback, param};
bool is_transition;
if (!data_valid(source, "obs_source_enum_active_tree"))
return;
if (!source->info.enum_active_sources)
is_transition = source->info.type == OBS_SOURCE_TYPE_TRANSITION;
if (!is_transition && !source->info.enum_active_sources)
return;
obs_source_addref(source);
source->info.enum_active_sources(source->context.data,
enum_source_tree_callback,
&data);
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
obs_transition_enum_sources(source, enum_source_tree_callback,
&data);
if (source->info.enum_active_sources)
source->info.enum_active_sources(source->context.data,
enum_source_tree_callback, &data);
obs_source_release(source);
}
......
......@@ -587,6 +587,9 @@ static const char *obs_signals[] = {
"void source_volume(ptr source, in out float volume)",
"void source_volume_level(ptr source, float level, float magnitude, "
"float peak)",
"void source_transition_start(ptr source)",
"void source_transition_video_stop(ptr source)",
"void source_transition_stop(ptr source)",
"void channel_change(int channel, in out ptr source, ptr prev_source)",
"void master_volume(in out float volume)",
......@@ -1508,8 +1511,17 @@ void obs_load_sources(obs_data_array_t *array)
}
/* tell sources that we want to load */
for (i = 0; i < sources.num; i++)
obs_source_load(sources.array[i]);
for (i = 0; i < sources.num; i++) {
obs_source_t *source = sources.array[i];
obs_data_t *source_data = obs_data_array_item(array, i);
if (source) {
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
obs_transition_load(source, source_data);
obs_source_load(source);
}
obs_data_release(source_data);
}
for (i = 0; i < sources.num; i++)
obs_source_release(sources.array[i]);
......@@ -1562,6 +1574,9 @@ obs_data_t *obs_save_source(obs_source_t *source)
obs_data_set_int (source_data, "push-to-talk-delay", ptt_delay);
obs_data_set_obj (source_data, "hotkeys", hotkey_data);
if (source->info.type == OBS_SOURCE_TYPE_TRANSITION)
obs_transition_save(source, source_data);
pthread_mutex_lock(&source->filter_mutex);
if (source->filters.num) {
......
......@@ -976,6 +976,80 @@ EXPORT uint64_t obs_source_get_audio_timestamp(const obs_source_t *source);
EXPORT void obs_source_get_audio_mix(const obs_source_t *source,
struct obs_source_audio_mix *audio);
/* ------------------------------------------------------------------------- */
/* Transition-specific functions */
enum obs_transition_target {
OBS_TRANSITION_SOURCE_A,
OBS_TRANSITION_SOURCE_B
};
EXPORT obs_source_t *obs_transition_get_source(obs_source_t *transition,
enum obs_transition_target target);
EXPORT void obs_transition_clear(obs_source_t *transition);
EXPORT obs_source_t *obs_transition_get_active_source(obs_source_t *transition);
enum obs_transition_mode {
OBS_TRANSITION_MODE_AUTO,
};
EXPORT bool obs_transition_start(obs_source_t *transition,
enum obs_transition_mode mode, uint32_t duration_ms,
obs_source_t *dest);
EXPORT void obs_transition_set(obs_source_t *transition, obs_source_t *source);
enum obs_transition_scale_type {
OBS_TRANSITION_SCALE_MAX_ONLY,
OBS_TRANSITION_SCALE_ASPECT,
OBS_TRANSITION_SCALE_STRETCH,
};
EXPORT void obs_transition_set_scale_type(obs_source_t *transition,
enum obs_transition_scale_type type);
EXPORT enum obs_transition_scale_type obs_transition_get_scale_type(
const obs_source_t *transition);
EXPORT void obs_transition_set_alignment(obs_source_t *transition,
uint32_t alignment);
EXPORT uint32_t obs_transition_get_alignment(const obs_source_t *transition);
EXPORT void obs_transition_set_size(obs_source_t *transition,
uint32_t cx, uint32_t cy);
EXPORT void obs_transition_get_size(const obs_source_t *transition,
uint32_t *cx, uint32_t *cy);
/* function used by transitions */
/**
* Enables fixed transitions (videos or specific types of transitions that
* are of fixed duration and linearly interpolated
*/
EXPORT void obs_transition_enable_fixed(obs_source_t *transition, bool enable,
uint32_t duration_ms);
EXPORT bool obs_transition_fixed(obs_source_t *transition);
typedef void (*obs_transition_video_render_callback_t)(void *data,
gs_texture_t *a, gs_texture_t *b, float t,
uint32_t cx, uint32_t cy);
typedef float (*obs_transition_audio_mix_callback_t)(void *data, float t);
EXPORT void obs_transition_video_render(obs_source_t *transition,
obs_transition_video_render_callback_t callback);
EXPORT bool obs_transition_audio_render(obs_source_t *transition,
uint64_t *ts_out, struct obs_source_audio_mix *audio,
uint32_t mixers, size_t channels, size_t sample_rate,
obs_transition_audio_mix_callback_t mix_a_callback,
obs_transition_audio_mix_callback_t mix_b_callback);
/* swaps transition sources and textures as an optimization and to reduce
* memory usage when switching between transitions */
EXPORT void obs_transition_swap_begin(obs_source_t *tr_dest,
obs_source_t *tr_source);
EXPORT void obs_transition_swap_end(obs_source_t *tr_dest,
obs_source_t *tr_source);
/* ------------------------------------------------------------------------- */
/* Scenes */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册