mirror of
https://github.com/obsproject/obs-studio.git
synced 2026-09-24 23:31:36 +08:00
Simplify media i/o interfaces
Completely revamped the entire media i/o data and handlers. The original idea was to have a system that would have connecting media inputs and outputs, but at a certain point I realized that this was an unnecessary complexity for what we wanted to do. (Also, it reminded me of directshow filters, and I HATE directshow with a passion, and wouldn't wish it upon my greatest enemy) Now, audio/video outputs are connected to directly, with better callback handlers, and will eventually have the ability to automatically handle conversions such as 4:4:4 to 4:2:0 when connecting to an input that uses them. Doing this will allow the video/audio i/o handlers to also prevent duplicate conversion, as well as make it easier/simple to use. My true goal for this is to make output and encoder plugins as simple to create as possible. I want to be able to be able to create an output plugin with almost no real hassle of having to worry about image conversions, media inputs/outputs, etc. A plugin developer shouldn't have to handle that sort of stuff when he/she doesn't really need to. Plugins will be able to simply create a callback via obs_video() and/or obs_audio(), and they will automatically receive the audio/video data in the formats requested via a simple callback, without needing to do almost anything else at all.
This commit is contained in:
@@ -56,7 +56,6 @@ add_library(libobs SHARED
|
|||||||
media-io/audio-io.c
|
media-io/audio-io.c
|
||||||
media-io/format-conversion.c
|
media-io/format-conversion.c
|
||||||
media-io/audio-resampler-ffmpeg.c
|
media-io/audio-resampler-ffmpeg.c
|
||||||
media-io/media-io.c
|
|
||||||
media-io/video-io.c
|
media-io/video-io.c
|
||||||
graphics/axisang.c
|
graphics/axisang.c
|
||||||
graphics/bounds.c
|
graphics/bounds.c
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ libobs_la_SOURCES = obs.c \
|
|||||||
media-io/audio-io.c \
|
media-io/audio-io.c \
|
||||||
media-io/format-conversion.c \
|
media-io/format-conversion.c \
|
||||||
media-io/audio-resampler-ffmpeg.c \
|
media-io/audio-resampler-ffmpeg.c \
|
||||||
media-io/media-io.c \
|
|
||||||
media-io/video-io.c \
|
media-io/video-io.c \
|
||||||
graphics/axisang.c \
|
graphics/axisang.c \
|
||||||
graphics/bounds.c \
|
graphics/bounds.c \
|
||||||
|
|||||||
+132
-68
@@ -22,7 +22,11 @@
|
|||||||
|
|
||||||
#include "audio-io.h"
|
#include "audio-io.h"
|
||||||
|
|
||||||
/* TODO: Incomplete */
|
struct audio_input {
|
||||||
|
struct audio_info format;
|
||||||
|
void (*callback)(void *param, const struct audio_data *data);
|
||||||
|
void *param;
|
||||||
|
};
|
||||||
|
|
||||||
struct audio_line {
|
struct audio_line {
|
||||||
char *name;
|
char *name;
|
||||||
@@ -52,11 +56,9 @@ static inline void audio_line_destroy_data(struct audio_line *line)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct audio_output {
|
struct audio_output {
|
||||||
struct audio_info info;
|
struct audio_output_info info;
|
||||||
size_t block_size;
|
size_t block_size;
|
||||||
size_t channels;
|
size_t channels;
|
||||||
media_t media;
|
|
||||||
media_output_t output;
|
|
||||||
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
event_t stop_event;
|
event_t stop_event;
|
||||||
@@ -69,6 +71,9 @@ struct audio_output {
|
|||||||
|
|
||||||
pthread_mutex_t line_mutex;
|
pthread_mutex_t line_mutex;
|
||||||
struct audio_line *first_line;
|
struct audio_line *first_line;
|
||||||
|
|
||||||
|
pthread_mutex_t input_mutex;
|
||||||
|
DARRAY(struct audio_input) inputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void audio_output_removeline(struct audio_output *audio,
|
static inline void audio_output_removeline(struct audio_output *audio,
|
||||||
@@ -76,18 +81,25 @@ static inline void audio_output_removeline(struct audio_output *audio,
|
|||||||
{
|
{
|
||||||
pthread_mutex_lock(&audio->line_mutex);
|
pthread_mutex_lock(&audio->line_mutex);
|
||||||
*line->prev_next = line->next;
|
*line->prev_next = line->next;
|
||||||
|
if (line->next)
|
||||||
|
line->next->prev_next = line->prev_next;
|
||||||
pthread_mutex_unlock(&audio->line_mutex);
|
pthread_mutex_unlock(&audio->line_mutex);
|
||||||
|
|
||||||
audio_line_destroy_data(line);
|
audio_line_destroy_data(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t convert_to_sample_offset(audio_t audio, uint64_t offset)
|
static inline size_t time_to_frames(audio_t audio, uint64_t offset)
|
||||||
{
|
{
|
||||||
double audio_offset_d = (double)offset;
|
double audio_offset_d = (double)offset;
|
||||||
audio_offset_d /= 1000000000.0;
|
audio_offset_d /= 1000000000.0;
|
||||||
audio_offset_d *= (double)audio->info.samples_per_sec;
|
audio_offset_d *= (double)audio->info.samples_per_sec;
|
||||||
|
|
||||||
return (size_t)audio_offset_d * audio->block_size;
|
return (size_t)audio_offset_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t time_to_bytes(audio_t audio, uint64_t offset)
|
||||||
|
{
|
||||||
|
return time_to_frames(audio, offset) * audio->block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
@@ -120,7 +132,7 @@ static inline void mix_audio_line(struct audio_output *audio,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t time_offset = convert_to_sample_offset(audio,
|
size_t time_offset = time_to_bytes(audio,
|
||||||
line->base_timestamp - timestamp);
|
line->base_timestamp - timestamp);
|
||||||
if (time_offset > size)
|
if (time_offset > size)
|
||||||
return;
|
return;
|
||||||
@@ -133,15 +145,34 @@ static inline void mix_audio_line(struct audio_output *audio,
|
|||||||
pop_size);
|
pop_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mix_audio_lines(struct audio_output *audio, uint64_t audio_time,
|
static inline void do_audio_output(struct audio_output *audio,
|
||||||
|
uint64_t timestamp, size_t frames)
|
||||||
|
{
|
||||||
|
struct audio_data data;
|
||||||
|
data.data = audio->mix_buffer.array;
|
||||||
|
data.frames = frames;
|
||||||
|
data.timestamp = timestamp;
|
||||||
|
data.volume = 1.0f;
|
||||||
|
|
||||||
|
/* TODO: conversion */
|
||||||
|
pthread_mutex_lock(&audio->input_mutex);
|
||||||
|
for (size_t i = 0; i < audio->inputs.num; i++) {
|
||||||
|
struct audio_input *input = audio->inputs.array+i;
|
||||||
|
input->callback(input->param, &data);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&audio->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mix_and_output(struct audio_output *audio, uint64_t audio_time,
|
||||||
uint64_t prev_time)
|
uint64_t prev_time)
|
||||||
{
|
{
|
||||||
struct audio_line *line = audio->first_line;
|
struct audio_line *line = audio->first_line;
|
||||||
uint64_t time_offset = audio_time - prev_time;
|
uint64_t time_offset = audio_time - prev_time;
|
||||||
size_t byte_offset = convert_to_sample_offset(audio, time_offset);
|
size_t frames = time_to_frames(audio, time_offset);
|
||||||
|
size_t bytes = frames * audio->block_size;
|
||||||
|
|
||||||
da_resize(audio->mix_buffer, byte_offset);
|
da_resize(audio->mix_buffer, bytes);
|
||||||
memset(audio->mix_buffer.array, 0, byte_offset);
|
memset(audio->mix_buffer.array, 0, bytes);
|
||||||
|
|
||||||
while (line) {
|
while (line) {
|
||||||
struct audio_line *next = line->next;
|
struct audio_line *next = line->next;
|
||||||
@@ -152,14 +183,12 @@ static void mix_audio_lines(struct audio_output *audio, uint64_t audio_time,
|
|||||||
line->base_timestamp = prev_time;
|
line->base_timestamp = prev_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
mix_audio_line(audio, line, byte_offset, prev_time);
|
mix_audio_line(audio, line, bytes, prev_time);
|
||||||
line->base_timestamp = audio_time;
|
line->base_timestamp = audio_time;
|
||||||
line = next;
|
line = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO - not good enough */
|
do_audio_output(audio, prev_time, frames);
|
||||||
/*if (audio->output)
|
|
||||||
media_output_data(audio->output, audio->mix_buffer.array);*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sample audio 40 times a second */
|
/* sample audio 40 times a second */
|
||||||
@@ -178,7 +207,7 @@ static void *audio_thread(void *param)
|
|||||||
pthread_mutex_lock(&audio->line_mutex);
|
pthread_mutex_lock(&audio->line_mutex);
|
||||||
|
|
||||||
audio_time = os_gettime_ns() - buffer_time;
|
audio_time = os_gettime_ns() - buffer_time;
|
||||||
mix_audio_lines(audio, audio_time, prev_time);
|
mix_and_output(audio, audio_time, prev_time);
|
||||||
prev_time = audio_time;
|
prev_time = audio_time;
|
||||||
|
|
||||||
pthread_mutex_unlock(&audio->line_mutex);
|
pthread_mutex_unlock(&audio->line_mutex);
|
||||||
@@ -189,28 +218,66 @@ static void *audio_thread(void *param)
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static inline bool valid_audio_params(struct audio_info *info)
|
static size_t audio_get_input_idx(audio_t video,
|
||||||
|
void (*callback)(void *param, const struct audio_data *data),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||||
|
struct audio_input *input = video->inputs.array+i;
|
||||||
|
if (input->callback == callback && input->param == param)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DARRAY_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_output_connect(audio_t audio, struct audio_info *format,
|
||||||
|
void (*callback)(void *param, const struct audio_data *data),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&audio->input_mutex);
|
||||||
|
|
||||||
|
if (audio_get_input_idx(audio, callback, param) != DARRAY_INVALID) {
|
||||||
|
struct audio_input input;
|
||||||
|
input.callback = callback;
|
||||||
|
input.param = param;
|
||||||
|
|
||||||
|
/* TODO: conversion */
|
||||||
|
if (format) {
|
||||||
|
input.format = *format;
|
||||||
|
} else {
|
||||||
|
input.format.format = audio->info.format;
|
||||||
|
input.format.speakers = audio->info.speakers;
|
||||||
|
input.format.samples_per_sec =
|
||||||
|
audio->info.samples_per_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
da_push_back(audio->inputs, &input);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&audio->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_output_disconnect(audio_t audio,
|
||||||
|
void (*callback)(void *param, const struct audio_data *data),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&audio->input_mutex);
|
||||||
|
|
||||||
|
size_t idx = audio_get_input_idx(audio, callback, param);
|
||||||
|
if (idx != DARRAY_INVALID)
|
||||||
|
da_erase(audio->inputs, idx);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&audio->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool valid_audio_params(struct audio_output_info *info)
|
||||||
{
|
{
|
||||||
return info->format && info->name && info->samples_per_sec > 0 &&
|
return info->format && info->name && info->samples_per_sec > 0 &&
|
||||||
info->speakers > 0;
|
info->speakers > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ao_add_to_media(audio_t audio)
|
int audio_output_open(audio_t *audio, struct audio_output_info *info)
|
||||||
{
|
|
||||||
struct media_output_info oi;
|
|
||||||
oi.obj = audio;
|
|
||||||
oi.connect = NULL;
|
|
||||||
oi.format = NULL; /* TODO */
|
|
||||||
|
|
||||||
audio->output = media_output_create(&oi);
|
|
||||||
if (!audio->output)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
media_add_output(audio->media, audio->output);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
|
|
||||||
{
|
{
|
||||||
struct audio_output *out;
|
struct audio_output *out;
|
||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
@@ -221,9 +288,8 @@ int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
|
|||||||
out = bmalloc(sizeof(struct audio_output));
|
out = bmalloc(sizeof(struct audio_output));
|
||||||
memset(out, 0, sizeof(struct audio_output));
|
memset(out, 0, sizeof(struct audio_output));
|
||||||
|
|
||||||
memcpy(&out->info, info, sizeof(struct audio_info));
|
memcpy(&out->info, info, sizeof(struct audio_output_info));
|
||||||
pthread_mutex_init_value(&out->line_mutex);
|
pthread_mutex_init_value(&out->line_mutex);
|
||||||
out->media = media;
|
|
||||||
out->channels = get_audio_channels(info->speakers);
|
out->channels = get_audio_channels(info->speakers);
|
||||||
out->block_size = out->channels *
|
out->block_size = out->channels *
|
||||||
get_audio_bytes_per_channel(info->format);
|
get_audio_bytes_per_channel(info->format);
|
||||||
@@ -234,9 +300,9 @@ int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
|
|||||||
goto fail;
|
goto fail;
|
||||||
if (pthread_mutex_init(&out->line_mutex, &attr) != 0)
|
if (pthread_mutex_init(&out->line_mutex, &attr) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (event_init(&out->stop_event, EVENT_TYPE_MANUAL) != 0)
|
if (pthread_mutex_init(&out->input_mutex, NULL) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (!ao_add_to_media(out))
|
if (event_init(&out->stop_event, EVENT_TYPE_MANUAL) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (pthread_create(&out->thread, NULL, audio_thread, out) != 0)
|
if (pthread_create(&out->thread, NULL, audio_thread, out) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -250,6 +316,33 @@ fail:
|
|||||||
return AUDIO_OUTPUT_FAIL;
|
return AUDIO_OUTPUT_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void audio_output_close(audio_t audio)
|
||||||
|
{
|
||||||
|
void *thread_ret;
|
||||||
|
struct audio_line *line;
|
||||||
|
|
||||||
|
if (!audio)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (audio->initialized) {
|
||||||
|
event_signal(&audio->stop_event);
|
||||||
|
pthread_join(audio->thread, &thread_ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
line = audio->first_line;
|
||||||
|
while (line) {
|
||||||
|
struct audio_line *next = line->next;
|
||||||
|
audio_line_destroy_data(line);
|
||||||
|
line = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
da_free(audio->mix_buffer);
|
||||||
|
da_free(audio->pending_bytes);
|
||||||
|
event_destroy(&audio->stop_event);
|
||||||
|
pthread_mutex_destroy(&audio->line_mutex);
|
||||||
|
bfree(audio);
|
||||||
|
}
|
||||||
|
|
||||||
audio_line_t audio_output_createline(audio_t audio, const char *name)
|
audio_line_t audio_output_createline(audio_t audio, const char *name)
|
||||||
{
|
{
|
||||||
struct audio_line *line = bmalloc(sizeof(struct audio_line));
|
struct audio_line *line = bmalloc(sizeof(struct audio_line));
|
||||||
@@ -280,40 +373,11 @@ audio_line_t audio_output_createline(audio_t audio, const char *name)
|
|||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct audio_info *audio_output_getinfo(audio_t audio)
|
const struct audio_output_info *audio_output_getinfo(audio_t audio)
|
||||||
{
|
{
|
||||||
return &audio->info;
|
return &audio->info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_output_close(audio_t audio)
|
|
||||||
{
|
|
||||||
void *thread_ret;
|
|
||||||
struct audio_line *line;
|
|
||||||
|
|
||||||
if (!audio)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (audio->initialized) {
|
|
||||||
event_signal(&audio->stop_event);
|
|
||||||
pthread_join(audio->thread, &thread_ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
line = audio->first_line;
|
|
||||||
while (line) {
|
|
||||||
struct audio_line *next = line->next;
|
|
||||||
audio_line_destroy_data(line);
|
|
||||||
line = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
da_free(audio->mix_buffer);
|
|
||||||
da_free(audio->pending_bytes);
|
|
||||||
media_remove_output(audio->media, audio->output);
|
|
||||||
media_output_destroy(audio->output);
|
|
||||||
event_destroy(&audio->stop_event);
|
|
||||||
pthread_mutex_destroy(&audio->line_mutex);
|
|
||||||
bfree(audio);
|
|
||||||
}
|
|
||||||
|
|
||||||
void audio_line_destroy(struct audio_line *line)
|
void audio_line_destroy(struct audio_line *line)
|
||||||
{
|
{
|
||||||
if (line) {
|
if (line) {
|
||||||
@@ -436,7 +500,7 @@ static inline void audio_line_place_data(struct audio_line *line,
|
|||||||
const struct audio_data *data)
|
const struct audio_data *data)
|
||||||
{
|
{
|
||||||
uint64_t time_offset = data->timestamp - line->base_timestamp;
|
uint64_t time_offset = data->timestamp - line->base_timestamp;
|
||||||
size_t pos = convert_to_sample_offset(line->audio, time_offset);
|
size_t pos = time_to_bytes(line->audio, time_offset);
|
||||||
|
|
||||||
audio_line_place_data_pos(line, data, pos);
|
audio_line_place_data_pos(line, data, pos);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../util/c99defs.h"
|
#include "../util/c99defs.h"
|
||||||
#include "media-io.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -63,16 +62,21 @@ struct audio_data {
|
|||||||
float volume;
|
float volume;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audio_info {
|
struct audio_output_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
uint32_t samples_per_sec;
|
uint32_t samples_per_sec;
|
||||||
enum audio_format format;
|
enum audio_format format;
|
||||||
enum speaker_layout speakers;
|
enum speaker_layout speakers;
|
||||||
|
|
||||||
uint64_t buffer_ms;
|
uint64_t buffer_ms;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct audio_info {
|
||||||
|
uint32_t samples_per_sec;
|
||||||
|
enum audio_format format;
|
||||||
|
enum speaker_layout speakers;
|
||||||
|
};
|
||||||
|
|
||||||
static inline uint32_t get_audio_channels(enum speaker_layout speakers)
|
static inline uint32_t get_audio_channels(enum speaker_layout speakers)
|
||||||
{
|
{
|
||||||
switch (speakers) {
|
switch (speakers) {
|
||||||
@@ -117,13 +121,20 @@ static inline size_t get_audio_size(enum audio_format type,
|
|||||||
#define AUDIO_OUTPUT_INVALIDPARAM -1
|
#define AUDIO_OUTPUT_INVALIDPARAM -1
|
||||||
#define AUDIO_OUTPUT_FAIL -2
|
#define AUDIO_OUTPUT_FAIL -2
|
||||||
|
|
||||||
EXPORT int audio_output_open(audio_t *audio, media_t media,
|
EXPORT int audio_output_open(audio_t *audio, struct audio_output_info *info);
|
||||||
struct audio_info *info);
|
|
||||||
EXPORT audio_line_t audio_output_createline(audio_t audio, const char *name);
|
|
||||||
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_output_close(audio_t audio);
|
||||||
|
|
||||||
|
EXPORT void audio_output_connect(audio_t video, struct audio_info *format,
|
||||||
|
void (*callback)(void *param, const struct audio_data *data),
|
||||||
|
void *param);
|
||||||
|
EXPORT void audio_output_disconnect(audio_t video,
|
||||||
|
void (*callback)(void *param, const struct audio_data *data),
|
||||||
|
void *param);
|
||||||
|
|
||||||
|
EXPORT size_t audio_output_blocksize(audio_t audio);
|
||||||
|
EXPORT const struct audio_output_info *audio_output_getinfo(audio_t audio);
|
||||||
|
|
||||||
|
EXPORT audio_line_t audio_output_createline(audio_t audio, const char *name);
|
||||||
EXPORT void audio_line_destroy(audio_line_t line);
|
EXPORT void audio_line_destroy(audio_line_t line);
|
||||||
EXPORT void audio_line_output(audio_line_t line, const struct audio_data *data);
|
EXPORT void audio_line_output(audio_line_t line, const struct audio_data *data);
|
||||||
|
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
|
||||||
|
|
||||||
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
|
|
||||||
the Free Software Foundation, either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#include "../util/threading.h"
|
|
||||||
#include "../util/darray.h"
|
|
||||||
#include "../util/bmem.h"
|
|
||||||
|
|
||||||
#include "media-io.h"
|
|
||||||
|
|
||||||
/* TODO: Incomplete */
|
|
||||||
|
|
||||||
struct media_input {
|
|
||||||
struct media_input_info info;
|
|
||||||
struct media_output *connection;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct media_output {
|
|
||||||
struct media_output_info info;
|
|
||||||
DARRAY(media_input_t) connections;
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct media_data {
|
|
||||||
DARRAY(media_output_t) outputs;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
media_input_t media_input_create(struct media_input_info *info)
|
|
||||||
{
|
|
||||||
struct media_input *input;
|
|
||||||
|
|
||||||
if (!info || !info->format || !info->on_input)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
input = bmalloc(sizeof(struct media_input));
|
|
||||||
input->connection = NULL;
|
|
||||||
memcpy(&input->info, info, sizeof(struct media_input_info));
|
|
||||||
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_input_destroy(media_input_t input)
|
|
||||||
{
|
|
||||||
if (input)
|
|
||||||
bfree(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
media_output_t media_output_create(struct media_output_info *info)
|
|
||||||
{
|
|
||||||
struct media_output *output;
|
|
||||||
|
|
||||||
/* TODO: handle format */
|
|
||||||
if (!info) // || !info->format)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
output = bmalloc(sizeof(struct media_output));
|
|
||||||
da_init(output->connections);
|
|
||||||
memcpy(&output->info, info, sizeof(struct media_output_info));
|
|
||||||
|
|
||||||
if (pthread_mutex_init(&output->mutex, NULL) != 0) {
|
|
||||||
bfree(output);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_output_data(media_output_t output, const void *data)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&output->mutex);
|
|
||||||
|
|
||||||
for (i = 0; i < output->connections.num; i++) {
|
|
||||||
media_input_t input = output->connections.array[i];
|
|
||||||
input->info.on_input(input->info.obj, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&output->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_output_destroy(media_output_t output)
|
|
||||||
{
|
|
||||||
if (output) {
|
|
||||||
da_free(output->connections);
|
|
||||||
pthread_mutex_destroy(&output->mutex);
|
|
||||||
bfree(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
|
||||||
|
|
||||||
media_t media_open(void)
|
|
||||||
{
|
|
||||||
struct media_data *media = bmalloc(sizeof(struct media_data));
|
|
||||||
da_init(media->outputs);
|
|
||||||
|
|
||||||
return media;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool media_add_input(media_t media, media_input_t input)
|
|
||||||
{
|
|
||||||
media_output_t *outputs = media->outputs.array;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < media->outputs.num; i++) {
|
|
||||||
media_output_t output = outputs[i];
|
|
||||||
|
|
||||||
if (strcmp(output->info.format, input->info.format) == 0) {
|
|
||||||
da_push_back(output->connections, input);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_add_output(media_t media, media_output_t output)
|
|
||||||
{
|
|
||||||
da_push_back(media->outputs, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_remove_input(media_t media, media_input_t input)
|
|
||||||
{
|
|
||||||
if (!input->connection)
|
|
||||||
return;
|
|
||||||
|
|
||||||
da_erase_item(input->connection->connections, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_remove_output(media_t media, media_output_t output)
|
|
||||||
{
|
|
||||||
da_erase_item(media->outputs, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
void media_close(media_t media)
|
|
||||||
{
|
|
||||||
if (media) {
|
|
||||||
da_free(media->outputs);
|
|
||||||
bfree(media);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
|
||||||
|
|
||||||
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
|
|
||||||
the Free Software Foundation, either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Media input/output components used for connecting media outputs/inputs
|
|
||||||
* together. An input requests a connection to an output and the output
|
|
||||||
* sends frames to it through the callbacks in media_data_in structure.
|
|
||||||
*
|
|
||||||
* The id member should indicate the format/parameters used in text form.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* opaque data types */
|
|
||||||
struct media_input;
|
|
||||||
struct media_output;
|
|
||||||
struct media_data;
|
|
||||||
typedef struct media_input *media_input_t;
|
|
||||||
typedef struct media_output *media_output_t;
|
|
||||||
typedef struct media_data *media_t;
|
|
||||||
|
|
||||||
#include "../util/c99defs.h"
|
|
||||||
#include "video-io.h"
|
|
||||||
#include "audio-io.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct media_input_info {
|
|
||||||
const char *format;
|
|
||||||
|
|
||||||
void *obj;
|
|
||||||
void (*on_input)(void *obj, const void *data);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct media_output_info {
|
|
||||||
const char *format;
|
|
||||||
|
|
||||||
void *obj;
|
|
||||||
bool (*connect)(void *obj, media_input_t input);
|
|
||||||
};
|
|
||||||
|
|
||||||
EXPORT media_input_t media_input_create(struct media_input_info *info);
|
|
||||||
EXPORT void media_input_destroy(media_input_t input);
|
|
||||||
|
|
||||||
EXPORT media_output_t media_output_create(struct media_output_info *info);
|
|
||||||
EXPORT void media_output_data(media_output_t out, const void *data);
|
|
||||||
EXPORT void media_output_destroy(media_output_t output);
|
|
||||||
|
|
||||||
EXPORT media_t media_open(void);
|
|
||||||
EXPORT bool media_add_input(media_t media, media_input_t input);
|
|
||||||
EXPORT void media_add_output(media_t media, media_output_t output);
|
|
||||||
EXPORT void media_remove_input(media_t media, media_input_t input);
|
|
||||||
EXPORT void media_remove_output(media_t media, media_output_t output);
|
|
||||||
EXPORT void media_close(media_t media);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
+109
-52
@@ -19,25 +19,33 @@
|
|||||||
#include "../util/bmem.h"
|
#include "../util/bmem.h"
|
||||||
#include "../util/platform.h"
|
#include "../util/platform.h"
|
||||||
#include "../util/threading.h"
|
#include "../util/threading.h"
|
||||||
|
#include "../util/darray.h"
|
||||||
|
|
||||||
#include "video-io.h"
|
#include "video-io.h"
|
||||||
|
|
||||||
|
struct video_input {
|
||||||
|
struct video_info format;
|
||||||
|
void (*callback)(void *param, struct video_frame *frame);
|
||||||
|
void *param;
|
||||||
|
};
|
||||||
|
|
||||||
struct video_output {
|
struct video_output {
|
||||||
struct video_info info;
|
struct video_output_info info;
|
||||||
media_t media;
|
|
||||||
media_output_t output;
|
|
||||||
|
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
pthread_mutex_t data_mutex;
|
pthread_mutex_t data_mutex;
|
||||||
event_t stop_event;
|
event_t stop_event;
|
||||||
|
|
||||||
struct video_frame *cur_frame;
|
struct video_frame *cur_frame;
|
||||||
struct video_frame *next_frame;
|
struct video_frame *next_frame;
|
||||||
event_t update_event;
|
event_t update_event;
|
||||||
uint64_t frame_time;
|
uint64_t frame_time;
|
||||||
volatile uint64_t cur_video_time;
|
volatile uint64_t cur_video_time;
|
||||||
|
|
||||||
bool initialized;
|
bool initialized;
|
||||||
|
|
||||||
|
pthread_mutex_t input_mutex;
|
||||||
|
DARRAY(struct video_input) inputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
@@ -54,6 +62,22 @@ static inline void video_swapframes(struct video_output *video)
|
|||||||
pthread_mutex_unlock(&video->data_mutex);
|
pthread_mutex_unlock(&video->data_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void video_output_cur_frame(struct video_output *video)
|
||||||
|
{
|
||||||
|
if (!video->cur_frame)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&video->input_mutex);
|
||||||
|
|
||||||
|
/* TODO: conversion */
|
||||||
|
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||||
|
struct video_input *input = video->inputs.array+i;
|
||||||
|
input->callback(input->param, video->cur_frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&video->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static void *video_thread(void *param)
|
static void *video_thread(void *param)
|
||||||
{
|
{
|
||||||
struct video_output *video = param;
|
struct video_output *video = param;
|
||||||
@@ -68,8 +92,7 @@ static void *video_thread(void *param)
|
|||||||
/* wait another half a frame, swap and output frames */
|
/* wait another half a frame, swap and output frames */
|
||||||
os_sleepto_ns(cur_time += (video->frame_time/2));
|
os_sleepto_ns(cur_time += (video->frame_time/2));
|
||||||
video_swapframes(video);
|
video_swapframes(video);
|
||||||
if (video->cur_frame)
|
video_output_cur_frame(video);
|
||||||
media_output_data(video->output, video->cur_frame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -77,27 +100,13 @@ static void *video_thread(void *param)
|
|||||||
|
|
||||||
/* ------------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------------- */
|
||||||
|
|
||||||
static inline bool valid_video_params(struct video_info *info)
|
static inline bool valid_video_params(struct video_output_info *info)
|
||||||
{
|
{
|
||||||
return info->height != 0 && info->width != 0 && info->fps_den != 0 &&
|
return info->height != 0 && info->width != 0 && info->fps_den != 0 &&
|
||||||
info->fps_num != 0;
|
info->fps_num != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool vo_add_to_media(video_t video)
|
int video_output_open(video_t *video, struct video_output_info *info)
|
||||||
{
|
|
||||||
struct media_output_info oi;
|
|
||||||
oi.obj = video;
|
|
||||||
oi.connect = NULL;
|
|
||||||
|
|
||||||
video->output = media_output_create(&oi);
|
|
||||||
if (!video->output)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
media_add_output(video->media, video->output);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int video_output_open(video_t *video, media_t media, struct video_info *info)
|
|
||||||
{
|
{
|
||||||
struct video_output *out;
|
struct video_output *out;
|
||||||
|
|
||||||
@@ -110,17 +119,16 @@ int video_output_open(video_t *video, media_t media, struct video_info *info)
|
|||||||
memcpy(&out->info, info, sizeof(struct video_info));
|
memcpy(&out->info, info, sizeof(struct video_info));
|
||||||
out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
|
out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
|
||||||
(double)info->fps_num);
|
(double)info->fps_num);
|
||||||
out->media = media;
|
|
||||||
out->initialized = false;
|
out->initialized = false;
|
||||||
|
|
||||||
if (pthread_mutex_init(&out->data_mutex, NULL) != 0)
|
if (pthread_mutex_init(&out->data_mutex, NULL) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
if (pthread_mutex_init(&out->input_mutex, NULL) != 0)
|
||||||
|
goto fail;
|
||||||
if (event_init(&out->stop_event, EVENT_TYPE_MANUAL) != 0)
|
if (event_init(&out->stop_event, EVENT_TYPE_MANUAL) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (event_init(&out->update_event, EVENT_TYPE_AUTO) != 0)
|
if (event_init(&out->update_event, EVENT_TYPE_AUTO) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (!vo_add_to_media(out))
|
|
||||||
goto fail;
|
|
||||||
if (pthread_create(&out->thread, NULL, video_thread, out) != 0)
|
if (pthread_create(&out->thread, NULL, video_thread, out) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@@ -133,7 +141,74 @@ fail:
|
|||||||
return VIDEO_OUTPUT_FAIL;
|
return VIDEO_OUTPUT_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct video_info *video_output_getinfo(video_t video)
|
void video_output_close(video_t video)
|
||||||
|
{
|
||||||
|
if (!video)
|
||||||
|
return;
|
||||||
|
|
||||||
|
video_output_stop(video);
|
||||||
|
|
||||||
|
da_free(video->inputs);
|
||||||
|
event_destroy(&video->update_event);
|
||||||
|
event_destroy(&video->stop_event);
|
||||||
|
pthread_mutex_destroy(&video->data_mutex);
|
||||||
|
pthread_mutex_destroy(&video->input_mutex);
|
||||||
|
bfree(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t video_get_input_idx(video_t video,
|
||||||
|
void (*callback)(void *param, struct video_frame *frame),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < video->inputs.num; i++) {
|
||||||
|
struct video_input *input = video->inputs.array+i;
|
||||||
|
if (input->callback == callback && input->param == param)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DARRAY_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void video_output_connect(video_t video, struct video_info *format,
|
||||||
|
void (*callback)(void *param, struct video_frame *frame),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&video->input_mutex);
|
||||||
|
|
||||||
|
if (video_get_input_idx(video, callback, param) != DARRAY_INVALID) {
|
||||||
|
struct video_input input;
|
||||||
|
input.callback = callback;
|
||||||
|
input.param = param;
|
||||||
|
|
||||||
|
/* TODO: conversion */
|
||||||
|
if (format) {
|
||||||
|
input.format = *format;
|
||||||
|
} else {
|
||||||
|
input.format.type = video->info.type;
|
||||||
|
input.format.height = video->info.height;
|
||||||
|
input.format.width = video->info.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
da_push_back(video->inputs, &input);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&video->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void video_output_disconnect(video_t video,
|
||||||
|
void (*callback)(void *param, struct video_frame *frame),
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&video->input_mutex);
|
||||||
|
|
||||||
|
size_t idx = video_get_input_idx(video, callback, param);
|
||||||
|
if (idx != DARRAY_INVALID)
|
||||||
|
da_erase(video->inputs, idx);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&video->input_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct video_output_info *video_output_getinfo(video_t video)
|
||||||
{
|
{
|
||||||
return &video->info;
|
return &video->info;
|
||||||
}
|
}
|
||||||
@@ -174,21 +249,3 @@ void video_output_stop(video_t video)
|
|||||||
event_signal(&video->update_event);
|
event_signal(&video->update_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void video_output_close(video_t video)
|
|
||||||
{
|
|
||||||
if (!video)
|
|
||||||
return;
|
|
||||||
|
|
||||||
video_output_stop(video);
|
|
||||||
|
|
||||||
if (video->output) {
|
|
||||||
media_remove_output(video->media, video->output);
|
|
||||||
media_output_destroy(video->output);
|
|
||||||
}
|
|
||||||
|
|
||||||
event_destroy(&video->update_event);
|
|
||||||
event_destroy(&video->stop_event);
|
|
||||||
pthread_mutex_destroy(&video->data_mutex);
|
|
||||||
bfree(video);
|
|
||||||
}
|
|
||||||
|
|||||||
+24
-14
@@ -18,16 +18,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../util/c99defs.h"
|
#include "../util/c99defs.h"
|
||||||
#include "media-io.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/* Base video output component. Use this to create an video output track. */
|
||||||
* Base video output component. Use this to create an video output track
|
|
||||||
* for the media.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct video_output;
|
struct video_output;
|
||||||
typedef struct video_output *video_t;
|
typedef struct video_output *video_t;
|
||||||
@@ -37,7 +33,7 @@ enum video_format {
|
|||||||
|
|
||||||
/* planar 420 format */
|
/* planar 420 format */
|
||||||
VIDEO_FORMAT_I420, /* three-plane */
|
VIDEO_FORMAT_I420, /* three-plane */
|
||||||
VIDEO_FORMAT_NV12, /* two-plane, lum and packed chroma */
|
VIDEO_FORMAT_NV12, /* two-plane, luma and packed chroma */
|
||||||
|
|
||||||
/* packed 422 formats */
|
/* packed 422 formats */
|
||||||
VIDEO_FORMAT_YVYU,
|
VIDEO_FORMAT_YVYU,
|
||||||
@@ -54,33 +50,47 @@ enum video_format {
|
|||||||
|
|
||||||
struct video_frame {
|
struct video_frame {
|
||||||
const void *data;
|
const void *data;
|
||||||
uint32_t row_size; /* for RGB/BGR formats and UYVX */
|
uint32_t row_size; /* for RGB/BGR formats and UYVX */
|
||||||
uint64_t timestamp;
|
uint64_t timestamp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct video_info {
|
struct video_output_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
enum video_format type;
|
enum video_format type;
|
||||||
uint32_t fps_num; /* numerator */
|
uint32_t fps_num;
|
||||||
uint32_t fps_den; /* denominator */
|
uint32_t fps_den;
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct video_info {
|
||||||
|
enum video_format type;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
uint32_t row_size; /* if any */
|
||||||
|
};
|
||||||
|
|
||||||
#define VIDEO_OUTPUT_SUCCESS 0
|
#define VIDEO_OUTPUT_SUCCESS 0
|
||||||
#define VIDEO_OUTPUT_INVALIDPARAM -1
|
#define VIDEO_OUTPUT_INVALIDPARAM -1
|
||||||
#define VIDEO_OUTPUT_FAIL -2
|
#define VIDEO_OUTPUT_FAIL -2
|
||||||
|
|
||||||
EXPORT int video_output_open(video_t *video, media_t media,
|
EXPORT int video_output_open(video_t *video, struct video_output_info *info);
|
||||||
struct video_info *info);
|
EXPORT void video_output_close(video_t video);
|
||||||
EXPORT const struct video_info *video_output_getinfo(video_t video);
|
|
||||||
|
EXPORT void video_output_connect(video_t video, struct video_info *format,
|
||||||
|
void (*callback)(void *param, struct video_frame *frame),
|
||||||
|
void *param);
|
||||||
|
EXPORT void video_output_disconnect(video_t video,
|
||||||
|
void (*callback)(void *param, struct video_frame *frame),
|
||||||
|
void *param);
|
||||||
|
|
||||||
|
EXPORT const struct video_output_info *video_output_getinfo(video_t video);
|
||||||
EXPORT void video_output_frame(video_t video, struct video_frame *frame);
|
EXPORT void video_output_frame(video_t video, struct video_frame *frame);
|
||||||
EXPORT bool video_output_wait(video_t video);
|
EXPORT bool video_output_wait(video_t video);
|
||||||
EXPORT uint64_t video_getframetime(video_t video);
|
EXPORT uint64_t video_getframetime(video_t video);
|
||||||
EXPORT uint64_t video_gettime(video_t video);
|
EXPORT uint64_t video_gettime(video_t video);
|
||||||
EXPORT void video_output_stop(video_t video);
|
EXPORT void video_output_stop(video_t video);
|
||||||
EXPORT void video_output_close(video_t video);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include "graphics/graphics.h"
|
#include "graphics/graphics.h"
|
||||||
|
|
||||||
#include "media-io/media-io.h"
|
|
||||||
#include "media-io/video-io.h"
|
#include "media-io/video-io.h"
|
||||||
#include "media-io/audio-io.h"
|
#include "media-io/audio-io.h"
|
||||||
|
|
||||||
@@ -86,8 +85,6 @@ struct obs_subsystem {
|
|||||||
DARRAY(struct output_info) output_types;
|
DARRAY(struct output_info) output_types;
|
||||||
DARRAY(struct service_info) service_types;
|
DARRAY(struct service_info) service_types;
|
||||||
|
|
||||||
media_t media;
|
|
||||||
|
|
||||||
signal_handler_t signals;
|
signal_handler_t signals;
|
||||||
proc_handler_t procs;
|
proc_handler_t procs;
|
||||||
|
|
||||||
|
|||||||
+6
-3
@@ -323,8 +323,11 @@ void obs_source_video_tick(obs_source_t source, float seconds)
|
|||||||
|
|
||||||
static inline uint64_t conv_frames_to_time(obs_source_t source, size_t frames)
|
static inline uint64_t conv_frames_to_time(obs_source_t source, size_t frames)
|
||||||
{
|
{
|
||||||
const struct audio_info *info = audio_output_getinfo(obs->audio.audio);
|
const struct audio_output_info *info;
|
||||||
double sps_to_ns = 1000000000.0 / (double)info->samples_per_sec;
|
double sps_to_ns;
|
||||||
|
|
||||||
|
info = audio_output_getinfo(obs->audio.audio);
|
||||||
|
sps_to_ns = 1000000000.0 / (double)info->samples_per_sec;
|
||||||
return (uint64_t)((double)frames * sps_to_ns);
|
return (uint64_t)((double)frames * sps_to_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,7 +786,7 @@ static inline struct filtered_audio *filter_async_audio(obs_source_t source,
|
|||||||
static inline void reset_resampler(obs_source_t source,
|
static inline void reset_resampler(obs_source_t source,
|
||||||
const struct source_audio *audio)
|
const struct source_audio *audio)
|
||||||
{
|
{
|
||||||
const struct audio_info *obs_info;
|
const struct audio_output_info *obs_info;
|
||||||
struct resample_info output_info;
|
struct resample_info output_info;
|
||||||
|
|
||||||
obs_info = audio_output_getinfo(obs->audio.audio);
|
obs_info = audio_output_getinfo(obs->audio.audio);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#include "util/darray.h"
|
#include "util/darray.h"
|
||||||
#include "util/dstr.h"
|
#include "util/dstr.h"
|
||||||
#include "util/threading.h"
|
#include "util/threading.h"
|
||||||
#include "media-io/media-io.h"
|
|
||||||
#include "media-io/audio-resampler.h"
|
#include "media-io/audio-resampler.h"
|
||||||
#include "callback/signal.h"
|
#include "callback/signal.h"
|
||||||
#include "callback/proc.h"
|
#include "callback/proc.h"
|
||||||
|
|||||||
+19
-23
@@ -37,7 +37,7 @@ static inline void make_gs_init_data(struct gs_init_data *gid,
|
|||||||
gid->adapter = ovi->adapter;
|
gid->adapter = ovi->adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void make_video_info(struct video_info *vi,
|
static inline void make_video_info(struct video_output_info *vi,
|
||||||
struct obs_video_info *ovi)
|
struct obs_video_info *ovi)
|
||||||
{
|
{
|
||||||
vi->name = "video";
|
vi->name = "video";
|
||||||
@@ -120,11 +120,11 @@ static bool obs_init_graphics(struct obs_video_info *ovi)
|
|||||||
static bool obs_init_video(struct obs_video_info *ovi)
|
static bool obs_init_video(struct obs_video_info *ovi)
|
||||||
{
|
{
|
||||||
struct obs_video *video = &obs->video;
|
struct obs_video *video = &obs->video;
|
||||||
struct video_info vi;
|
struct video_output_info vi;
|
||||||
int errorcode;
|
int errorcode;
|
||||||
|
|
||||||
make_video_info(&vi, ovi);
|
make_video_info(&vi, ovi);
|
||||||
errorcode = video_output_open(&video->video, obs->media, &vi);
|
errorcode = video_output_open(&video->video, &vi);
|
||||||
|
|
||||||
if (errorcode != VIDEO_OUTPUT_SUCCESS) {
|
if (errorcode != VIDEO_OUTPUT_SUCCESS) {
|
||||||
if (errorcode == VIDEO_OUTPUT_INVALIDPARAM)
|
if (errorcode == VIDEO_OUTPUT_INVALIDPARAM)
|
||||||
@@ -195,14 +195,14 @@ static void obs_free_graphics()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool obs_init_audio(struct audio_info *ai)
|
static bool obs_init_audio(struct audio_output_info *ai)
|
||||||
{
|
{
|
||||||
struct obs_audio *audio = &obs->audio;
|
struct obs_audio *audio = &obs->audio;
|
||||||
int errorcode;
|
int errorcode;
|
||||||
|
|
||||||
/* TODO: sound subsystem */
|
/* TODO: sound subsystem */
|
||||||
|
|
||||||
errorcode = audio_output_open(&audio->audio, obs->media, ai);
|
errorcode = audio_output_open(&audio->audio, ai);
|
||||||
if (errorcode == AUDIO_OUTPUT_SUCCESS)
|
if (errorcode == AUDIO_OUTPUT_SUCCESS)
|
||||||
return true;
|
return true;
|
||||||
else if (errorcode == AUDIO_OUTPUT_INVALIDPARAM)
|
else if (errorcode == AUDIO_OUTPUT_INVALIDPARAM)
|
||||||
@@ -277,18 +277,10 @@ static inline bool obs_init_handlers(void)
|
|||||||
static bool obs_init(void)
|
static bool obs_init(void)
|
||||||
{
|
{
|
||||||
obs = bmalloc(sizeof(struct obs_subsystem));
|
obs = bmalloc(sizeof(struct obs_subsystem));
|
||||||
|
|
||||||
memset(obs, 0, sizeof(struct obs_subsystem));
|
memset(obs, 0, sizeof(struct obs_subsystem));
|
||||||
|
|
||||||
obs_init_data();
|
obs_init_data();
|
||||||
|
return obs_init_handlers();
|
||||||
if (!obs_init_handlers())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
obs->media = media_open();
|
|
||||||
if (!obs->media)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool obs_startup()
|
bool obs_startup()
|
||||||
@@ -324,7 +316,6 @@ void obs_shutdown(void)
|
|||||||
obs_free_video();
|
obs_free_video();
|
||||||
obs_free_graphics();
|
obs_free_graphics();
|
||||||
obs_free_audio();
|
obs_free_audio();
|
||||||
media_close(obs->media);
|
|
||||||
proc_handler_destroy(obs->procs);
|
proc_handler_destroy(obs->procs);
|
||||||
signal_handler_destroy(obs->signals);
|
signal_handler_destroy(obs->signals);
|
||||||
|
|
||||||
@@ -353,7 +344,7 @@ bool obs_reset_video(struct obs_video_info *ovi)
|
|||||||
return obs_init_video(ovi);
|
return obs_init_video(ovi);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool obs_reset_audio(struct audio_info *ai)
|
bool obs_reset_audio(struct audio_output_info *ai)
|
||||||
{
|
{
|
||||||
obs_free_audio();
|
obs_free_audio();
|
||||||
if(!ai)
|
if(!ai)
|
||||||
@@ -365,7 +356,7 @@ bool obs_reset_audio(struct audio_info *ai)
|
|||||||
bool obs_get_video_info(struct obs_video_info *ovi)
|
bool obs_get_video_info(struct obs_video_info *ovi)
|
||||||
{
|
{
|
||||||
struct obs_video *video = &obs->video;
|
struct obs_video *video = &obs->video;
|
||||||
const struct video_info *info;
|
const struct video_output_info *info;
|
||||||
|
|
||||||
if (!obs || !video->graphics)
|
if (!obs || !video->graphics)
|
||||||
return false;
|
return false;
|
||||||
@@ -384,16 +375,16 @@ bool obs_get_video_info(struct obs_video_info *ovi)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool obs_get_audio_info(struct audio_info *ai)
|
bool obs_get_audio_info(struct audio_output_info *aoi)
|
||||||
{
|
{
|
||||||
struct obs_audio *audio = &obs->audio;
|
struct obs_audio *audio = &obs->audio;
|
||||||
const struct audio_info *info;
|
const struct audio_output_info *info;
|
||||||
|
|
||||||
if (!obs || !audio->audio)
|
if (!obs || !audio->audio)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
info = audio_output_getinfo(audio->audio);
|
info = audio_output_getinfo(audio->audio);
|
||||||
memcpy(ai, info, sizeof(struct audio_info));
|
memcpy(aoi, info, sizeof(struct audio_output_info));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -435,9 +426,14 @@ graphics_t obs_graphics(void)
|
|||||||
return (obs != NULL) ? obs->video.graphics : NULL;
|
return (obs != NULL) ? obs->video.graphics : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
media_t obs_media(void)
|
audio_t obs_audio(void)
|
||||||
{
|
{
|
||||||
return obs->media;
|
return (obs != NULL) ? obs->audio.audio : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
video_t obs_video(void)
|
||||||
|
{
|
||||||
|
return (obs != NULL) ? obs->video.video : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool obs_add_source(obs_source_t source)
|
bool obs_add_source(obs_source_t source)
|
||||||
|
|||||||
+6
-5
@@ -21,7 +21,8 @@
|
|||||||
#include "util/bmem.h"
|
#include "util/bmem.h"
|
||||||
#include "graphics/graphics.h"
|
#include "graphics/graphics.h"
|
||||||
#include "graphics/vec2.h"
|
#include "graphics/vec2.h"
|
||||||
#include "media-io/media-io.h"
|
#include "media-io/audio-io.h"
|
||||||
|
#include "media-io/video-io.h"
|
||||||
#include "callback/signal.h"
|
#include "callback/signal.h"
|
||||||
#include "callback/proc.h"
|
#include "callback/proc.h"
|
||||||
|
|
||||||
@@ -173,13 +174,13 @@ EXPORT bool obs_reset_video(struct obs_video_info *ovi);
|
|||||||
*
|
*
|
||||||
* NOTE: Cannot reset base audio if currently streaming/recording.
|
* NOTE: Cannot reset base audio if currently streaming/recording.
|
||||||
*/
|
*/
|
||||||
EXPORT bool obs_reset_audio(struct audio_info *ai);
|
EXPORT bool obs_reset_audio(struct audio_output_info *ai);
|
||||||
|
|
||||||
/** Gets the current video settings, returns false if none */
|
/** Gets the current video settings, returns false if none */
|
||||||
EXPORT bool obs_get_video_info(struct obs_video_info *ovi);
|
EXPORT bool obs_get_video_info(struct obs_video_info *ovi);
|
||||||
|
|
||||||
/** Gets the current audio settings, returns false if none */
|
/** Gets the current audio settings, returns false if none */
|
||||||
EXPORT bool obs_get_audio_info(struct audio_info *ai);
|
EXPORT bool obs_get_audio_info(struct audio_output_info *ai);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a plugin module
|
* Loads a plugin module
|
||||||
@@ -225,8 +226,8 @@ EXPORT bool obs_enum_output_types(size_t idx, const char **id);
|
|||||||
/** Gets the graphics context for this OBS context */
|
/** Gets the graphics context for this OBS context */
|
||||||
EXPORT graphics_t obs_graphics(void);
|
EXPORT graphics_t obs_graphics(void);
|
||||||
|
|
||||||
/** Get the media context for this OBS context (used for outputs) */
|
EXPORT audio_t obs_audio(void);
|
||||||
EXPORT media_t obs_media(void);
|
EXPORT video_t obs_video(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a source to the user source list and increments the reference counter
|
* Adds a source to the user source list and increments the reference counter
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ bool OBSBasic::InitGraphics()
|
|||||||
bool OBSBasic::InitAudio()
|
bool OBSBasic::InitAudio()
|
||||||
{
|
{
|
||||||
/* TODO: load audio settings from config */
|
/* TODO: load audio settings from config */
|
||||||
struct audio_info ai;
|
struct audio_output_info ai;
|
||||||
ai.name = "test";
|
ai.name = "test";
|
||||||
ai.samples_per_sec = 44100;
|
ai.samples_per_sec = 44100;
|
||||||
ai.format = AUDIO_FORMAT_16BIT;
|
ai.format = AUDIO_FORMAT_16BIT;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "obs-outputs.h"
|
#include "obs-outputs.h"
|
||||||
|
|
||||||
static const char *outputs[1] = {"rtmp_stream"};
|
static const char *outputs[] = {"rtmp_stream"};
|
||||||
|
|
||||||
const char *enum_outputs(size_t idx)
|
const char *enum_outputs(size_t idx)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,7 +42,6 @@
|
|||||||
<ClInclude Include="..\..\..\libobs\media-io\audio-io.h" />
|
<ClInclude Include="..\..\..\libobs\media-io\audio-io.h" />
|
||||||
<ClInclude Include="..\..\..\libobs\media-io\audio-resampler.h" />
|
<ClInclude Include="..\..\..\libobs\media-io\audio-resampler.h" />
|
||||||
<ClInclude Include="..\..\..\libobs\media-io\format-conversion.h" />
|
<ClInclude Include="..\..\..\libobs\media-io\format-conversion.h" />
|
||||||
<ClInclude Include="..\..\..\libobs\media-io\media-io.h" />
|
|
||||||
<ClInclude Include="..\..\..\libobs\media-io\video-io.h" />
|
<ClInclude Include="..\..\..\libobs\media-io\video-io.h" />
|
||||||
<ClInclude Include="..\..\..\libobs\obs-data.h" />
|
<ClInclude Include="..\..\..\libobs\obs-data.h" />
|
||||||
<ClInclude Include="..\..\..\libobs\obs-defs.h" />
|
<ClInclude Include="..\..\..\libobs\obs-defs.h" />
|
||||||
@@ -90,7 +89,6 @@
|
|||||||
<ClCompile Include="..\..\..\libobs\media-io\audio-io.c" />
|
<ClCompile Include="..\..\..\libobs\media-io\audio-io.c" />
|
||||||
<ClCompile Include="..\..\..\libobs\media-io\audio-resampler-ffmpeg.c" />
|
<ClCompile Include="..\..\..\libobs\media-io\audio-resampler-ffmpeg.c" />
|
||||||
<ClCompile Include="..\..\..\libobs\media-io\format-conversion.c" />
|
<ClCompile Include="..\..\..\libobs\media-io\format-conversion.c" />
|
||||||
<ClCompile Include="..\..\..\libobs\media-io\media-io.c" />
|
|
||||||
<ClCompile Include="..\..\..\libobs\media-io\video-io.c" />
|
<ClCompile Include="..\..\..\libobs\media-io\video-io.c" />
|
||||||
<ClCompile Include="..\..\..\libobs\obs-display.c" />
|
<ClCompile Include="..\..\..\libobs\obs-display.c" />
|
||||||
<ClCompile Include="..\..\..\libobs\obs-module.c" />
|
<ClCompile Include="..\..\..\libobs\obs-module.c" />
|
||||||
|
|||||||
@@ -87,9 +87,6 @@
|
|||||||
<ClInclude Include="..\..\..\libobs\media-io\audio-io.h">
|
<ClInclude Include="..\..\..\libobs\media-io\audio-io.h">
|
||||||
<Filter>media-io\Header Files</Filter>
|
<Filter>media-io\Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\libobs\media-io\media-io.h">
|
|
||||||
<Filter>media-io\Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\..\libobs\media-io\video-io.h">
|
<ClInclude Include="..\..\..\libobs\media-io\video-io.h">
|
||||||
<Filter>media-io\Header Files</Filter>
|
<Filter>media-io\Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -227,9 +224,6 @@
|
|||||||
<ClCompile Include="..\..\..\libobs\media-io\audio-io.c">
|
<ClCompile Include="..\..\..\libobs\media-io\audio-io.c">
|
||||||
<Filter>media-io\Source Files</Filter>
|
<Filter>media-io\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="..\..\..\libobs\media-io\media-io.c">
|
|
||||||
<Filter>media-io\Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="..\..\..\libobs\util\config-file.c">
|
<ClCompile Include="..\..\..\libobs\util\config-file.c">
|
||||||
<Filter>util\Source Files</Filter>
|
<Filter>util\Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
|||||||
Reference in New Issue
Block a user