added intial async audio/video code, fixed a few bugs, improved thread safety, and made a few other minor adjustments

master
jp9000 2013-10-24 00:57:55 -07:00
parent 0a11dbabef
commit ac2c08927f
11 changed files with 786 additions and 79 deletions

2
README
View File

@ -14,7 +14,7 @@ What's the goal of rewriting OBS?
necessary for user interface. It also means allowing the use of OpenGL as
well as Direct3D.
- Separate the application from the core, allowing custom appliction of
- Separate the application from the core, allowing custom application of
the core if desired, and easier extending of the user interface.
- Simplify complex systems to not only make it easier to use, but easier to

View File

@ -0,0 +1,37 @@
uniform float4x4 ViewProj;
uniform float4x4 yuv_matrix;
texture2d tex;
sampler_state def_sampler {
Filter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
struct VertInOut {
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
VertInOut VSConvert(VertInOut vert_in)
{
VertInOut vert_out;
vert_out.pos = mul(vert_in.pos, ViewProj);
vert_out.uv = vert_in.uv;
return vert_out;
}
float4 PSConvert(VertInOut vert_in)
{
float4 yuv = tex.Sample(def_sampler, vert_in.uv);
return saturate(mul(float4(yuv.xyz, 1.0), yuv_matrix));
}
technique ConvertYUV
{
pass
{
vertex_shader = VSConvert(vert_in);
pixel_shader = PSConvert(vert_in);
}
}

View File

@ -17,26 +17,56 @@
#include "../util/threading.h"
#include "../util/darray.h"
#include "../util/circlebuf.h"
#include "../util/platform.h"
#include "audio-io.h"
/* TODO: Incomplete */
struct audio_output {
struct audio_info info;
media_t media;
media_output_t output;
struct audio_line {
struct audio_output *audio;
struct circlebuf buffer;
uint64_t base_timestamp;
uint64_t last_timestamp;
pthread_t thread;
pthread_mutex_t data_mutex;
event_t stop_event;
struct darray pending_frames;
bool initialized;
/* states whether this line is still being used. if not, then when the
* buffer is depleted, it's destroyed */
bool alive;
};
static inline void audio_line_destroy_data(struct audio_line *line)
{
circlebuf_free(&line->buffer);
bfree(line);
}
struct audio_output {
struct audio_info info;
size_t block_size;
media_t media;
media_output_t output;
pthread_t thread;
event_t stop_event;
DARRAY(uint8_t) pending_bytes;
bool initialized;
pthread_mutex_t line_mutex;
DARRAY(struct audio_line*) lines;
};
static inline void audio_output_removeline(struct audio_output *audio,
struct audio_line *line)
{
pthread_mutex_lock(&audio->line_mutex);
da_erase_item(audio->lines, &line);
pthread_mutex_unlock(&audio->line_mutex);
audio_line_destroy_data(line);
}
/* ------------------------------------------------------------------------- */
static void *audio_thread(void *param)
@ -44,7 +74,6 @@ static void *audio_thread(void *param)
struct audio_output *audio = param;
while (event_try(&audio->stop_event) == EAGAIN) {
os_sleep_ms(5);
/* TODO */
}
@ -55,8 +84,8 @@ static void *audio_thread(void *param)
static inline bool valid_audio_params(struct audio_info *info)
{
return info->channels > 0 && info->format && info->name &&
info->samples_per_sec > 0 && info->speakers > 0;
return info->format && info->name && info->samples_per_sec > 0 &&
info->speakers > 0;
}
static inline bool ao_add_to_media(audio_t audio)
@ -85,9 +114,12 @@ int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
memset(out, 0, sizeof(struct audio_output));
memcpy(&out->info, info, sizeof(struct audio_info));
pthread_mutex_init_value(&out->line_mutex);
out->media = media;
out->block_size = get_audio_channels(info->speakers) *
get_audio_bytes_per_channel(info->type);
if (pthread_mutex_init(&out->data_mutex, NULL) != 0)
if (pthread_mutex_init(&out->line_mutex, NULL) != 0)
goto fail;
if (event_init(&out->stop_event, true) != 0)
goto fail;
@ -105,16 +137,27 @@ fail:
return AUDIO_OUTPUT_FAIL;
}
void audio_output_data(audio_t audio, struct audio_data *data)
audio_line_t audio_output_createline(audio_t audio)
{
pthread_mutex_lock(&audio->data_mutex);
/* TODO */
pthread_mutex_unlock(&audio->data_mutex);
struct audio_line *line = bmalloc(sizeof(struct audio_line));
memset(line, 0, sizeof(struct audio_line));
line->alive = true;
pthread_mutex_lock(&audio->line_mutex);
da_push_back(audio->lines, &line);
pthread_mutex_unlock(&audio->line_mutex);
return line;
}
const struct audio_info *audio_output_getinfo(audio_t audio)
{
return &audio->info;
}
void audio_output_close(audio_t audio)
{
void *thread_ret;
size_t i;
if (!audio)
return;
@ -124,8 +167,50 @@ void audio_output_close(audio_t audio)
pthread_join(audio->thread, &thread_ret);
}
for (i = 0; i < audio->lines.num; i++)
audio_line_destroy_data(audio->lines.array[i]);
da_free(audio->lines);
media_remove_output(audio->media, audio->output);
event_destroy(&audio->stop_event);
pthread_mutex_destroy(&audio->data_mutex);
pthread_mutex_destroy(&audio->line_mutex);
bfree(audio);
}
void audio_line_destroy(struct audio_line *line)
{
if (line) {
if (!line->buffer.size)
audio_output_removeline(line->audio, line);
else
line->alive = false;
}
}
size_t audio_output_blocksize(audio_t audio)
{
return audio->block_size;
}
static inline uint64_t convert_to_sample_offset(audio_t audio, uint64_t offset)
{
return (uint64_t)((double)offset *
(1000000000.0 / (double)audio->info.samples_per_sec));
}
void audio_line_output(audio_line_t line, const struct audio_data *data)
{
if (!line->buffer.size) {
line->base_timestamp = data->timestamp;
circlebuf_push_back(&line->buffer, data->data,
data->frames * line->audio->block_size);
} else {
uint64_t position = data->timestamp - line->base_timestamp;
position = convert_to_sample_offset(line->audio, position);
position *= line->audio->block_size;
circlebuf_place(&line->buffer, position, data->data,
data->frames * line->audio->block_size);
}
}

View File

@ -30,7 +30,9 @@ extern "C" {
*/
struct audio_output;
struct audio_line;
typedef struct audio_output *audio_t;
typedef struct audio_line *audio_line_t;
enum audio_type {
AUDIO_FORMAT_UNKNOWN,
@ -56,11 +58,8 @@ enum speaker_setup {
};
struct audio_data {
void *data;
const void *data;
uint32_t frames;
uint32_t speakers;
uint32_t samples_per_sec;
enum audio_type type;
uint64_t timestamp;
};
@ -68,21 +67,65 @@ struct audio_info {
const char *name;
const char *format;
uint32_t channels;
uint32_t samples_per_sec;
enum audio_type type;
enum speaker_setup speakers;
};
static inline uint32_t get_audio_channels(enum speaker_setup speakers)
{
switch (speakers) {
case SPEAKERS_MONO: return 1;
case SPEAKERS_STEREO: return 2;
case SPEAKERS_2POINT1: return 3;
case SPEAKERS_SURROUND:
case SPEAKERS_QUAD: return 4;
case SPEAKERS_4POINT1: return 5;
case SPEAKERS_5POINT1:
case SPEAKERS_5POINT1_SURROUND: return 6;
case SPEAKERS_7POINT1: return 8;
case SPEAKERS_7POINT1_SURROUND: return 8;
default:
case SPEAKERS_UNKNOWN: return 0;
}
}
static inline size_t get_audio_bytes_per_channel(enum audio_type type)
{
switch (type) {
case AUDIO_FORMAT_8BIT: return 1;
case AUDIO_FORMAT_16BIT: return 2;
case AUDIO_FORMAT_24BIT: return 3;
case AUDIO_FORMAT_FLOAT:
case AUDIO_FORMAT_32BIT: return 4;
default:
case AUDIO_FORMAT_UNKNOWN: return 0;
}
}
static inline size_t get_audio_size(enum audio_type type,
enum speaker_setup speakers, uint32_t frames)
{
return get_audio_channels(speakers) *
get_audio_bytes_per_channel(type) *
frames;
}
#define AUDIO_OUTPUT_SUCCESS 0
#define AUDIO_OUTPUT_INVALIDPARAM -1
#define AUDIO_OUTPUT_FAIL -2
EXPORT int audio_output_open(audio_t *audio, media_t media,
struct audio_info *info);
EXPORT void audio_output_data(audio_t audio, struct audio_data *data);
EXPORT audio_line_t audio_output_createline(audio_t audio);
EXPORT size_t audio_output_blocksize(audio_t audio);
EXPORT const struct audio_info *audio_output_getinfo(audio_t audio);
EXPORT void audio_output_close(audio_t audio);
EXPORT void audio_line_destroy(audio_line_t line);
EXPORT void audio_line_output(audio_line_t line, const struct audio_data *data);
#ifdef __cplusplus
}
#endif

View File

@ -32,6 +32,16 @@ extern "C" {
struct video_output;
typedef struct video_output *video_t;
enum video_type {
VIDEO_FORMAT_UNKNOWN,
VIDEO_FORMAT_YUV444,
VIDEO_FORMAT_YUV422,
VIDEO_FORMAT_YUV420,
VIDEO_FORMAT_RGBA,
VIDEO_FORMAT_BGRA,
VIDEO_FORMAT_BGRX,
};
struct video_frame {
const void *data;
uint32_t row_size;
@ -39,13 +49,14 @@ struct video_frame {
};
struct video_info {
const char *name;
const char *format;
const char *name;
const char *format;
uint32_t fps_num; /* numerator */
uint32_t fps_den; /* denominator */
uint32_t width;
uint32_t height;
enum video_type type;
uint32_t fps_num; /* numerator */
uint32_t fps_den; /* denominator */
uint32_t width;
uint32_t height;
};
#define VIDEO_OUTPUT_SUCCESS 0

View File

@ -22,6 +22,6 @@
#define MODULE_FILENOTFOUND -2
#define MODULE_FUNCTIONNOTFOUND -3
#define SOURCE_VIDEO (1<<0)
#define SOURCE_AUDIO (1<<1)
#define SOURCE_ASYNC (1<<2)
#define SOURCE_VIDEO (1<<0)
#define SOURCE_AUDIO (1<<1)
#define SOURCE_ASYNC_VIDEO (1<<2)

View File

@ -41,7 +41,7 @@ static void scene_destroy(void *data)
static uint32_t scene_get_output_flags(void *data)
{
return SOURCE_VIDEO | SOURCE_AUDIO;
return SOURCE_VIDEO;
}
static void scene_video_render(void *data)
@ -111,6 +111,8 @@ obs_scene_t obs_scene_create(void)
struct obs_source *source = bmalloc(sizeof(struct obs_source));
struct obs_scene *scene = scene_create(NULL, source);
memset(source, 0, sizeof(struct obs_source));
source->data = scene;
if (!source->data) {
bfree(source);
@ -118,7 +120,7 @@ obs_scene_t obs_scene_create(void)
}
scene->source = source;
obs_source_init(source);
obs_source_init(source, NULL, &scene_info);
memcpy(&source->callbacks, &scene_info, sizeof(struct source_info));
return scene;
}

View File

@ -15,6 +15,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
#include "util/platform.h"
#include "obs.h"
#include "obs-data.h"
@ -77,17 +79,39 @@ static inline const struct source_info *find_source(struct darray *list,
return NULL;
}
void obs_source_init(struct obs_source *source)
bool obs_source_init(struct obs_source *source, const char *settings,
const struct source_info *info)
{
source->filter_target = NULL;
source->rendering_filter = false;
uint32_t flags = info->get_output_flags(source->data);
dstr_init(&source->settings);
da_init(source->filters);
pthread_mutex_init_value(&source->filter_mutex);
pthread_mutex_init_value(&source->video_mutex);
pthread_mutex_init_value(&source->audio_mutex);
dstr_copy(&source->settings, settings);
memcpy(&source->callbacks, info, sizeof(struct source_info));
if (pthread_mutex_init(&source->filter_mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&source->audio_mutex, NULL) != 0)
return false;
if (pthread_mutex_init(&source->video_mutex, NULL) != 0)
return false;
if (flags & SOURCE_AUDIO) {
source->audio_line = audio_output_createline(obs->audio);
if (!source->audio_line) {
blog(LOG_ERROR, "Failed to create audio line for "
"source");
return false;
}
}
source->valid = true;
pthread_mutex_lock(&obs->source_list_mutex);
da_push_back(obs->sources, &source);
pthread_mutex_unlock(&obs->source_list_mutex);
return true;
}
obs_source_t obs_source_create(enum obs_source_type type, const char *name,
@ -113,27 +137,57 @@ obs_source_t obs_source_create(enum obs_source_type type, const char *name,
}
source = bmalloc(sizeof(struct obs_source));
source->data = info->create(settings, source);
if (!source->data) {
bfree(source);
return NULL;
}
memset(source, 0, sizeof(struct obs_source));
source->data = info->create(settings, source);
if (!source->data)
goto fail;
if (!obs_source_init(source, settings, info))
goto fail;
obs_source_init(source);
dstr_copy(&source->settings, settings);
memcpy(&source->callbacks, info, sizeof(struct source_info));
return source;
fail:
blog(LOG_ERROR, "obs_source_create failed");
obs_source_destroy(source);
return NULL;
}
void obs_source_destroy(obs_source_t source)
{
if (source) {
pthread_mutex_lock(&obs->source_list_mutex);
da_erase_item(obs->sources, &source);
pthread_mutex_unlock(&obs->source_list_mutex);
size_t i;
if (source->filter_parent)
obs_source_filter_remove(source->filter_parent, source);
for (i = 0; i < source->filters.num; i++)
obs_source_destroy(source->filters.array[i]);
if (source->valid) {
pthread_mutex_lock(&obs->source_list_mutex);
da_erase_item(obs->sources, &source);
pthread_mutex_unlock(&obs->source_list_mutex);
}
for (i = 0; i < source->audio_buffer.num; i++)
audiobuf_free(source->audio_buffer.array+i);
for (i = 0; i < source->video_frames.num; i++)
source_frame_destroy(source->video_frames.array[i]);
gs_entercontext(obs->graphics);
texture_destroy(source->output_texture);
gs_leavecontext();
da_free(source->filters);
source->callbacks.destroy(source->data);
if (source->data)
source->callbacks.destroy(source->data);
audio_line_destroy(source->audio_line);
pthread_mutex_destroy(&source->filter_mutex);
pthread_mutex_destroy(&source->audio_mutex);
pthread_mutex_destroy(&source->video_mutex);
dstr_free(&source->settings);
bfree(source);
}
@ -173,6 +227,74 @@ void obs_source_video_tick(obs_source_t source, float seconds)
source->callbacks.video_tick(source->data, seconds);
}
#define MAX_VARIANCE 2000000000ULL
static void source_output_audio_line(obs_source_t source,
const struct audio_data *data)
{
struct audio_data in = *data;
if (!in.timestamp) {
in.timestamp = os_gettime_ns();
if (!source->timing_set) {
source->timing_set = true;
source->timing_adjust = 0;
}
}
if (!source->timing_set) {
source->timing_set = true;
source->timing_adjust = in.timestamp - os_gettime_ns();
/* detects 'directly' set timestamps as long as they're within
* a certain threashold */
if ((source->timing_adjust+MAX_VARIANCE) < MAX_VARIANCE*2)
source->timing_adjust = 0;
}
in.timestamp += source->timing_adjust;
audio_line_output(source->audio_line, &in);
}
static void obs_source_flush_audio_buffer(obs_source_t source)
{
size_t i;
pthread_mutex_lock(&source->audio_mutex);
source->timing_set = true;
for (i = 0; i < source->audio_buffer.num; i++) {
struct audiobuf *buf = source->audio_buffer.array+i;
struct audio_data data;
data.data = buf->data;
data.frames = buf->frames;
data.timestamp = buf->timestamp + source->timing_adjust;
audio_line_output(source->audio_line, &data);
audiobuf_free(buf);
}
da_free(source->audio_buffer);
pthread_mutex_unlock(&source->audio_mutex);
}
static void obs_source_render_async_video(obs_source_t source)
{
struct source_frame *frame = obs_source_getframe(source);
if (!frame)
return;
source->timing_adjust = frame->timestamp - os_gettime_ns();
if (!source->timing_set && source->audio_buffer.num)
obs_source_flush_audio_buffer(source);
if (!source->output_texture) {
/* TODO */
}
obs_source_releaseframe(source, frame);
}
void obs_source_video_render(obs_source_t source)
{
if (source->callbacks.video_render) {
@ -183,6 +305,12 @@ void obs_source_video_render(obs_source_t source)
} else {
source->callbacks.video_render(source->data);
}
} else if (source->filter_target) {
obs_source_video_render(source->filter_target);
} else {
obs_source_render_async_video(source);
}
}
@ -231,6 +359,8 @@ obs_source_t obs_filter_gettarget(obs_source_t filter)
void obs_source_filter_add(obs_source_t source, obs_source_t filter)
{
pthread_mutex_lock(&source->filter_mutex);
if (da_find(source->filters, &filter, 0) != DARRAY_INVALID) {
blog(LOG_WARNING, "Tried to add a filter that was already "
"present on the source");
@ -243,12 +373,20 @@ void obs_source_filter_add(obs_source_t source, obs_source_t filter)
}
da_push_back(source->filters, &filter);
pthread_mutex_unlock(&source->filter_mutex);
filter->filter_parent = source;
filter->filter_target = source;
}
void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
{
size_t idx = da_find(source->filters, &filter, 0);
size_t idx;
pthread_mutex_lock(&source->filter_mutex);
idx = da_find(source->filters, &filter, 0);
if (idx == DARRAY_INVALID)
return;
@ -258,6 +396,10 @@ void obs_source_filter_remove(obs_source_t source, obs_source_t filter)
}
da_erase(source->filters, idx);
pthread_mutex_unlock(&source->filter_mutex);
filter->filter_parent = NULL;
filter->filter_target = NULL;
}
@ -308,12 +450,112 @@ void obs_source_save_settings(obs_source_t source, const char *settings)
dstr_copy(&source->settings, settings);
}
void obs_source_output_video(obs_source_t source, struct video_frame *frame)
static inline struct filter_frame *filter_async_video(obs_source_t source,
struct filter_frame *in)
{
/* TODO */
size_t i;
for (i = source->filters.num; i > 0; i--) {
struct obs_source *filter = source->filters.array[i-1];
if (filter->callbacks.filter_video) {
in = filter->callbacks.filter_video(filter->data, in);
if (!in)
return NULL;
}
}
return in;
}
void obs_source_output_audio(obs_source_t source, struct audio_data *audio)
static struct filter_frame *process_video(obs_source_t source,
const struct source_video *frame)
{
/* TODO: convert to YUV444 or RGB */
return NULL;
}
void obs_source_output_video(obs_source_t source,
const struct source_video *frame)
{
struct filter_frame *output;
output = process_video(source, frame);
pthread_mutex_lock(&source->filter_mutex);
output = filter_async_video(source, output);
pthread_mutex_unlock(&source->filter_mutex);
pthread_mutex_lock(&source->video_mutex);
da_push_back(source->video_frames, &output);
pthread_mutex_unlock(&source->video_mutex);
}
static inline const struct audio_data *filter_async_audio(obs_source_t source,
const struct audio_data *in)
{
size_t i;
for (i = source->filters.num; i > 0; i--) {
struct obs_source *filter = source->filters.array[i-1];
if (filter->callbacks.filter_audio) {
in = filter->callbacks.filter_audio(filter->data, in);
if (!in)
return NULL;
}
}
return in;
}
static struct audio_data *process_audio(obs_source_t source,
const struct source_audio *audio)
{
/* TODO: upmix/downmix/resample */
return NULL;
}
void obs_source_output_audio(obs_source_t source,
const struct source_audio *audio)
{
uint32_t flags = obs_source_get_output_flags(source);
struct audio_data *data;
const struct audio_data *output;
data = process_audio(source, audio);
pthread_mutex_lock(&source->filter_mutex);
output = filter_async_audio(source, data);
if (output) {
pthread_mutex_lock(&source->audio_mutex);
if (!source->timing_set && flags & SOURCE_ASYNC_VIDEO) {
struct audiobuf newbuf;
size_t audio_size = audio_output_blocksize(obs->audio) *
output->frames;
newbuf.data = bmalloc(audio_size);
newbuf.frames = output->frames;
newbuf.timestamp = output->timestamp;
memcpy(newbuf.data, output->data, audio_size);
da_push_back(source->audio_buffer, &newbuf);
} else {
source_output_audio_line(source, output);
}
pthread_mutex_unlock(&source->audio_mutex);
}
pthread_mutex_unlock(&source->filter_mutex);
}
struct source_frame *obs_source_getframe(obs_source_t source)
{
/* TODO */
return NULL;
}
void obs_source_releaseframe(obs_source_t source, struct source_frame *frame)
{
/* TODO */
}

View File

@ -20,6 +20,7 @@
#include "util/c99defs.h"
#include "util/darray.h"
#include "util/dstr.h"
#include "util/threading.h"
#include "media-io/media-io.h"
/*
@ -135,16 +136,20 @@
* Return value: true if sources remaining, otherwise false.
*
* ---------------------------------------------------------
* void [name]_filter_video(void *data, struct video_frame *frame);
* struct filter_frame *[name]_filter_video(void *data,
* struct filter_frame *frame);
* Filters audio data. Used with audio filters.
*
* frame: Video frame data.
* returns: New video frame data (or NULL if pending)
*
* ---------------------------------------------------------
* void [name]_filter_audio(void *data, struct audio_data *audio);
* const struct audio_data *[name]_filter_audio(void *data,
* const struct audio_data *audio);
* Filters video data. Used with async video data.
*
* audio: Audio data.
* reutrns New audio data (or NULL if pending)
*/
struct obs_source;
@ -180,24 +185,53 @@ struct source_info {
bool (*enum_children)(void *data, size_t idx, obs_source_t *child);
void (*filter_video)(void *data, struct video_frame *frame);
void (*filter_audio)(void *data, struct audio_data *audio);
struct filter_frame *(*filter_video)(void *data,
struct filter_frame *frame);
const struct audio_data *(*filter_audio)(void *data,
const struct audio_data *audio);
};
struct obs_source {
void *data;
struct source_info callbacks;
struct dstr settings;
bool rendering_filter;
struct audiobuf {
void *data;
uint32_t frames;
uint64_t timestamp;
};
struct obs_source *filter_target;
DARRAY(struct obs_source*) filters;
static inline void audiobuf_free(struct audiobuf *buf)
{
bfree(buf->data);
}
struct obs_source {
void *data; /* source-specific data */
struct source_info callbacks;
struct dstr settings;
bool valid;
/* async video and audio */
bool timing_set;
uint64_t timing_adjust;
texture_t output_texture;
audio_line_t audio_line;
DARRAY(struct audiobuf) audio_buffer;
DARRAY(struct source_frame*) video_frames;
pthread_mutex_t audio_mutex;
pthread_mutex_t video_mutex;
/* filters */
struct obs_source *filter_parent;
struct obs_source *filter_target;
DARRAY(struct obs_source*) filters;
pthread_mutex_t filter_mutex;
bool rendering_filter;
};
extern bool get_source_info(void *module, const char *module_name,
const char *source_name, struct source_info *info);
extern void obs_source_init(struct obs_source *source);
extern bool obs_source_init(struct obs_source *source, const char *settings,
const struct source_info *info);
extern void obs_source_activate(obs_source_t source);
extern void obs_source_deactivate(obs_source_t source);

View File

@ -39,6 +39,8 @@ enum obs_source_type {
SOURCE_SCENE
};
/* used for changing the order of items (for example, filters in a source,
* or items in a scene */
enum order_movement {
ORDER_MOVE_UP,
ORDER_MOVE_DOWN,
@ -46,6 +48,49 @@ enum order_movement {
ORDER_MOVE_BOTTOM
};
struct source_audio {
const void *data;
uint32_t frames;
/* audio will be automatically resampled/upmixed/downmixed */
enum speaker_setup speakers;
enum audio_type type;
uint32_t samples_per_sec;
/* can be 0 if 'immediate' */
uint64_t timestamp;
};
struct source_video {
const void *data;
uint32_t width;
uint32_t height;
uint32_t row_bytes;
uint64_t timestamp;
enum video_type type;
float yuv_matrix[16];
};
struct source_frame {
void *data;
uint32_t width;
uint32_t height;
uint32_t row_bytes;
uint64_t timestamp;
bool yuv;
float yuv_matrix[16];
};
static inline void source_frame_destroy(struct source_frame *frame)
{
if (frame) {
bfree(frame->data);
bfree(frame);
}
}
/* opaque types */
struct obs_display;
struct obs_source;
@ -63,7 +108,7 @@ typedef struct obs_output *obs_output_t;
/* OBS context */
/**
* Creates the OBS context.
* Starts up and shuts down OBS
*
* Using the graphics module specified, creates an OBS context and sets the
* primary video/audio output information.
@ -214,9 +259,8 @@ EXPORT size_t obs_source_getparam(obs_source_t source, const char *param,
*/
EXPORT void obs_source_setparam(obs_source_t source, const char *param,
const void *data, size_t size);
/**
* Enumerates child sources this source has
*/
/** Enumerates child sources this source has */
EXPORT bool obs_source_enum_children(obs_source_t source, size_t idx,
obs_source_t *child);
@ -243,12 +287,19 @@ EXPORT const char *obs_source_get_settings(obs_source_t source);
EXPORT void obs_source_save_settings(obs_source_t source, const char *settings);
/** Outputs asynchronous video data */
EXPORT void obs_source_obs_output_video(obs_source_t source,
struct video_frame *frame);
EXPORT void obs_source_obs_async_video(obs_source_t source,
const struct video_frame *frame);
/** Outputs audio data (always asynchronous) */
EXPORT void obs_source_obs_output_audio(obs_source_t source,
struct audio_data *audio);
EXPORT void obs_source_obs_async_audio(obs_source_t source,
const struct source_audio *audio);
/** Gets the current async video frame */
EXPORT struct source_frame *obs_source_getframe(obs_source_t source);
/** Relases the current async frame */
EXPORT void obs_source_releaseframe(obs_source_t source,
struct source_frame *frame);
/* ------------------------------------------------------------------------- */

202
libobs/util/circlebuf.h Normal file
View File

@ -0,0 +1,202 @@
/******************************************************************************
Copyright (c) 2013 by Hugh Bailey <obs.jim@gmail.com>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
******************************************************************************/
#pragma once
#include "c99defs.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "bmem.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Dynamic circular buffer */
struct circlebuf {
void *data;
size_t size;
size_t start_pos;
size_t end_pos;
size_t capacity;
};
static inline void circlebuf_init(struct circlebuf *cb)
{
memset(cb, 0, sizeof(struct circlebuf));
}
static inline void circlebuf_free(struct circlebuf *cb)
{
bfree(cb->data);
memset(cb, 0, sizeof(struct circlebuf));
}
static inline void circlebuf_reorder_data(struct circlebuf *cb,
size_t new_capacity)
{
size_t difference;
uint8_t *data;
if (!cb->size || !cb->start_pos || cb->end_pos > cb->start_pos)
return;
difference = new_capacity - cb->capacity;
data = (uint8_t*)cb->data + cb->start_pos;
memmove(data+difference, data, cb->capacity - cb->start_pos);
cb->start_pos += difference;
}
static inline void circlebuf_ensure_capacity(struct circlebuf *cb)
{
size_t new_capacity;
if (cb->size <= cb->capacity)
return;
new_capacity = cb->capacity*2;
if (cb->size > new_capacity)
new_capacity = cb->size;
cb->data = brealloc(cb->data, new_capacity);
circlebuf_reorder_data(cb, new_capacity);
cb->capacity = new_capacity;
}
static inline void circlebuf_reserve(struct circlebuf *cb, size_t capacity)
{
if (capacity <= cb->capacity)
return;
cb->data = brealloc(cb->data, capacity);
circlebuf_reorder_data(cb, capacity);
cb->capacity = capacity;
}
static inline void circlebuf_upsize(struct circlebuf *cb, size_t size)
{
size_t add_size = size - cb->size;
size_t new_end_pos = cb->end_pos + add_size;
if (size <= cb->size)
return;
cb->size = size;
circlebuf_ensure_capacity(cb);
if (new_end_pos > cb->capacity) {
size_t back_size = cb->capacity - cb->end_pos;
size_t loop_size = add_size - back_size;
if (back_size)
memset((uint8_t*)cb->data + cb->end_pos, 0, back_size);
memset(cb->data, 0, loop_size);
new_end_pos -= cb->capacity;
} else {
memset((uint8_t*)cb->data + cb->end_pos, 0, add_size);
}
cb->end_pos = new_end_pos;
}
/** Overwrites data at a specific point in the buffer (relative). */
static inline void circlebuf_place(struct circlebuf *cb, size_t position,
const void *data, size_t size)
{
size_t end_point = position + size;
size_t data_end_pos;
if (end_point > cb->size)
circlebuf_upsize(cb, end_point);
position += cb->start_pos;
if (position >= cb->capacity)
position -= cb->capacity;
data_end_pos = position + size;
if (data_end_pos > cb->capacity) {
size_t back_size = cb->capacity - data_end_pos;
size_t loop_size = size - back_size;
if (back_size)
memcpy((uint8_t*)cb->data + position, data, back_size);
memcpy(cb->data, (uint8_t*)data + back_size, loop_size);
} else {
memcpy((uint8_t*)cb->data + position, data, size);
}
}
static inline void circlebuf_push_back(struct circlebuf *cb, const void *data,
size_t size)
{
size_t new_end_pos = cb->end_pos + size;
cb->size += size;
circlebuf_ensure_capacity(cb);
if (new_end_pos > cb->capacity) {
size_t back_size = cb->capacity - cb->end_pos;
size_t loop_size = size - back_size;
if (back_size)
memcpy((uint8_t*)cb->data + cb->end_pos, data,
back_size);
memcpy(cb->data, (uint8_t*)data + back_size, loop_size);
new_end_pos -= cb->capacity;
} else {
memcpy((uint8_t*)cb->data + cb->end_pos, data, size);
}
cb->end_pos = new_end_pos;
}
static inline void circlebuf_pop_front(struct circlebuf *cb, void *data,
size_t size)
{
size_t start_size;
assert(size <= cb->size);
start_size = cb->capacity - cb->start_pos;
if (start_size < size) {
memcpy(data, (uint8_t*)cb->data + cb->start_pos, start_size);
memcpy((uint8_t*)data + start_size, cb->data,
size - start_size);
} else {
memcpy(data, (uint8_t*)cb->data + cb->start_pos, size);
}
cb->size -= size;
cb->start_pos += size;
if (cb->start_pos >= cb->capacity)
cb->start_pos -= cb->capacity;
}
#ifdef __cplusplus
}
#endif