提交 4461281a 编写于 作者: Z Zachary Lund

Merge branch 'master' of https://github.com/jp9000/obs-studio

......@@ -89,7 +89,7 @@ static inline void required_extension_error(const char *extension)
{
}
static bool gl_init_extensions(struct gs_device* device)
static bool gl_init_extensions(struct gs_device* device)
{
if (!ogl_IsVersionGEQ(2, 1)) {
blog(LOG_ERROR, "obs-studio requires OpenGL version 2.1 or "
......
......@@ -32,9 +32,9 @@ struct audio_line {
char *name;
struct audio_output *audio;
struct circlebuf buffer;
struct circlebuf buffers[MAX_AUDIO_PLANES];
pthread_mutex_t mutex;
DARRAY(uint8_t) volume_buffer;
DARRAY(uint8_t) volume_buffers[MAX_AUDIO_PLANES];
uint64_t base_timestamp;
uint64_t last_timestamp;
......@@ -48,8 +48,11 @@ struct audio_line {
static inline void audio_line_destroy_data(struct audio_line *line)
{
circlebuf_free(&line->buffer);
da_free(line->volume_buffer);
for (size_t i = 0; i < MAX_AUDIO_PLANES; i++) {
circlebuf_free(&line->buffers[i]);
da_free(line->volume_buffers[i]);
}
pthread_mutex_destroy(&line->mutex);
bfree(line->name);
bfree(line);
......@@ -59,13 +62,12 @@ struct audio_output {
struct audio_output_info info;
size_t block_size;
size_t channels;
size_t planes;
pthread_t thread;
event_t stop_event;
DARRAY(uint8_t) pending_bytes;
DARRAY(uint8_t) mix_buffer;
DARRAY(uint8_t) mix_buffers[MAX_AUDIO_PLANES];
bool initialized;
......@@ -107,14 +109,16 @@ static inline size_t time_to_bytes(audio_t audio, uint64_t offset)
static inline void clear_excess_audio_data(struct audio_line *line,
uint64_t size)
{
if (size > line->buffer.size)
size = line->buffer.size;
for (size_t i = 0; i < line->audio->planes; i++) {
size_t clear_size = (size > line->buffers[i].size) ?
(size_t)size : line->buffers[i].size;
circlebuf_pop_front(&line->buffers[i], NULL, clear_size);
}
blog(LOG_WARNING, "Excess audio data for audio line '%s', somehow "
"audio data went back in time by %llu bytes",
line->name, size);
circlebuf_pop_front(&line->buffer, NULL, (size_t)size);
}
static inline uint64_t min_uint64(uint64_t a, uint64_t b)
......@@ -125,8 +129,8 @@ static inline uint64_t min_uint64(uint64_t a, uint64_t b)
static inline void mix_audio_line(struct audio_output *audio,
struct audio_line *line, size_t size, uint64_t timestamp)
{
/* TODO: this just overwrites, handle actual mixing */
if (!line->buffer.size) {
/* TODO: this just overwrites. handle actual mixing */
if (!line->buffers[0].size) {
if (!line->alive)
audio_output_removeline(audio, line);
return;
......@@ -139,17 +143,22 @@ static inline void mix_audio_line(struct audio_output *audio,
size -= time_offset;
size_t pop_size = (size_t)min_uint64(size, line->buffer.size);
circlebuf_pop_front(&line->buffer,
audio->mix_buffer.array + time_offset,
pop_size);
for (size_t i = 0; i < audio->planes; i++) {
size_t pop_size;
pop_size = (size_t)min_uint64(size, line->buffers[i].size);
circlebuf_pop_front(&line->buffers[i],
audio->mix_buffers[i].array + time_offset,
pop_size);
}
}
static inline void do_audio_output(struct audio_output *audio,
uint64_t timestamp, uint32_t frames)
{
struct audio_data data;
data.data = audio->mix_buffer.array;
for (size_t i = 0; i < MAX_AUDIO_PLANES; i++)
data.data[i] = audio->mix_buffers[i].array;
data.frames = frames;
data.timestamp = timestamp;
data.volume = 1.0f;
......@@ -171,13 +180,15 @@ static void mix_and_output(struct audio_output *audio, uint64_t audio_time,
uint32_t frames = time_to_frames(audio, time_offset);
size_t bytes = frames * audio->block_size;
da_resize(audio->mix_buffer, bytes);
memset(audio->mix_buffer.array, 0, bytes);
for (size_t i = 0; i < audio->planes; i++) {
da_resize(audio->mix_buffers[i], bytes);
memset(audio->mix_buffers[i].array, 0, bytes);
}
while (line) {
struct audio_line *next = line->next;
if (line->buffer.size && line->base_timestamp < prev_time) {
if (line->buffers[0].size && line->base_timestamp < prev_time) {
clear_excess_audio_data(line,
prev_time - line->base_timestamp);
line->base_timestamp = prev_time;
......@@ -238,7 +249,7 @@ void audio_output_connect(audio_t audio,
{
pthread_mutex_lock(&audio->input_mutex);
if (audio_get_input_idx(audio, callback, param) != DARRAY_INVALID) {
if (audio_get_input_idx(audio, callback, param) == DARRAY_INVALID) {
struct audio_input input;
input.callback = callback;
input.param = param;
......@@ -282,6 +293,7 @@ int audio_output_open(audio_t *audio, struct audio_output_info *info)
{
struct audio_output *out;
pthread_mutexattr_t attr;
bool planar = is_audio_planar(info->format);
if (!valid_audio_params(info))
return AUDIO_OUTPUT_INVALIDPARAM;
......@@ -291,8 +303,9 @@ int audio_output_open(audio_t *audio, struct audio_output_info *info)
memcpy(&out->info, info, sizeof(struct audio_output_info));
pthread_mutex_init_value(&out->line_mutex);
out->channels = get_audio_channels(info->speakers);
out->block_size = out->channels *
out->channels = get_audio_channels(info->speakers);
out->planes = planar ? out->channels : 1;
out->block_size = (planar ? 1 : out->channels) *
get_audio_bytes_per_channel(info->format);
if (pthread_mutexattr_init(&attr) != 0)
......@@ -337,8 +350,9 @@ void audio_output_close(audio_t audio)
line = next;
}
da_free(audio->mix_buffer);
da_free(audio->pending_bytes);
for (size_t i = 0; i < MAX_AUDIO_PLANES; i++)
da_free(audio->mix_buffers[i]);
event_destroy(&audio->stop_event);
pthread_mutex_destroy(&audio->line_mutex);
bfree(audio);
......@@ -382,7 +396,7 @@ const struct audio_output_info *audio_output_getinfo(audio_t audio)
void audio_line_destroy(struct audio_line *line)
{
if (line) {
if (!line->buffer.size)
if (!line->buffers[0].size)
audio_output_removeline(line->audio, line);
else
line->alive = false;
......@@ -394,10 +408,21 @@ size_t audio_output_blocksize(audio_t audio)
return audio->block_size;
}
static inline void mul_vol_u8bit(struct audio_line *line, float volume,
size_t total_num)
size_t audio_output_planes(audio_t audio)
{
uint8_t *vals = line->volume_buffer.array;
return audio->planes;
}
size_t audio_output_channels(audio_t audio)
{
return audio->channels;
}
/* TODO: Optimization of volume multiplication functions */
static inline void mul_vol_u8bit(void *array, float volume, size_t total_num)
{
uint8_t *vals = array;
int16_t vol = (int16_t)(volume * 127.0f);
for (size_t i = 0; i < total_num; i++) {
......@@ -406,10 +431,9 @@ static inline void mul_vol_u8bit(struct audio_line *line, float volume,
}
}
static inline void mul_vol_16bit(struct audio_line *line, float volume,
size_t total_num)
static inline void mul_vol_16bit(void *array, float volume, size_t total_num)
{
uint16_t *vals = (uint16_t*)line->volume_buffer.array;
uint16_t *vals = array;
int32_t vol = (int32_t)(volume * 32767.0f);
for (size_t i = 0; i < total_num; i++)
......@@ -436,10 +460,9 @@ static inline void conv_float_to_24bit(float fval, uint8_t *vals)
vals[2] = (val >> 16) & 0xFF;
}
static inline void mul_vol_24bit(struct audio_line *line, float volume,
size_t total_num)
static inline void mul_vol_24bit(void *array, float volume, size_t total_num)
{
uint8_t *vals = line->volume_buffer.array;
uint8_t *vals = array;
for (size_t i = 0; i < total_num; i++) {
float val = conv_24bit_to_float(vals) * volume;
......@@ -448,10 +471,9 @@ static inline void mul_vol_24bit(struct audio_line *line, float volume,
}
}
static inline void mul_vol_32bit(struct audio_line *line, float volume,
size_t total_num)
static inline void mul_vol_32bit(void *array, float volume, size_t total_num)
{
int32_t *vals = (int32_t*)line->volume_buffer.array;
int32_t *vals = array;
for (size_t i = 0; i < total_num; i++) {
float val = (float)vals[i] / 2147483647.0f;
......@@ -459,10 +481,9 @@ static inline void mul_vol_32bit(struct audio_line *line, float volume,
}
}
static inline void mul_vol_float(struct audio_line *line, float volume,
size_t total_num)
static inline void mul_vol_float(void *array, float volume, size_t total_num)
{
float *vals = (float*)line->volume_buffer.array;
float *vals = array;
for (size_t i = 0; i < total_num; i++)
vals[i] *= volume;
......@@ -471,30 +492,42 @@ static inline void mul_vol_float(struct audio_line *line, float volume,
static void audio_line_place_data_pos(struct audio_line *line,
const struct audio_data *data, size_t position)
{
size_t total_num = data->frames * line->audio->channels;
bool planar = line->audio->planes > 1;
size_t total_num = data->frames * planar ? 1 : line->audio->channels;
size_t total_size = data->frames * line->audio->block_size;
da_copy_array(line->volume_buffer, data->data, total_size);
switch (line->audio->info.format) {
case AUDIO_FORMAT_U8BIT:
mul_vol_u8bit(line, data->volume, total_num);
break;
case AUDIO_FORMAT_16BIT:
mul_vol_16bit(line, data->volume, total_num);
break;
case AUDIO_FORMAT_32BIT:
mul_vol_32bit(line, data->volume, total_num);
break;
case AUDIO_FORMAT_FLOAT:
mul_vol_float(line, data->volume, total_num);
break;
case AUDIO_FORMAT_UNKNOWN:
break;
}
for (size_t i = 0; i < line->audio->planes; i++) {
da_copy_array(line->volume_buffers[i], data->data[i],
total_size);
uint8_t *array = line->volume_buffers[i].array;
switch (line->audio->info.format) {
case AUDIO_FORMAT_U8BIT:
case AUDIO_FORMAT_U8BIT_PLANAR:
mul_vol_u8bit(array, data->volume, total_num);
break;
case AUDIO_FORMAT_16BIT:
case AUDIO_FORMAT_16BIT_PLANAR:
mul_vol_16bit(array, data->volume, total_num);
break;
case AUDIO_FORMAT_32BIT:
case AUDIO_FORMAT_32BIT_PLANAR:
mul_vol_32bit(array, data->volume, total_num);
break;
case AUDIO_FORMAT_FLOAT:
case AUDIO_FORMAT_FLOAT_PLANAR:
mul_vol_float(array, data->volume, total_num);
break;
case AUDIO_FORMAT_UNKNOWN:
blog(LOG_ERROR, "audio_line_place_data_pos: "
"Unknown format");
break;
}
circlebuf_place(&line->buffer, position, line->volume_buffer.array,
total_size);
circlebuf_place(&line->buffers[i], position,
line->volume_buffers[i].array, total_size);
}
}
static inline void audio_line_place_data(struct audio_line *line,
......@@ -513,7 +546,7 @@ void audio_line_output(audio_line_t line, const struct audio_data *data)
pthread_mutex_lock(&line->mutex);
if (!line->buffer.size) {
if (!line->buffers[0].size) {
line->base_timestamp = data->timestamp;
audio_line_place_data_pos(line, data, 0);
......
......@@ -28,6 +28,8 @@ extern "C" {
* for the media.
*/
#define MAX_AUDIO_PLANES 8
struct audio_output;
struct audio_line;
typedef struct audio_output *audio_t;
......@@ -35,10 +37,16 @@ typedef struct audio_line *audio_line_t;
enum audio_format {
AUDIO_FORMAT_UNKNOWN,
AUDIO_FORMAT_U8BIT,
AUDIO_FORMAT_16BIT,
AUDIO_FORMAT_32BIT,
AUDIO_FORMAT_FLOAT,
AUDIO_FORMAT_U8BIT_PLANAR,
AUDIO_FORMAT_16BIT_PLANAR,
AUDIO_FORMAT_32BIT_PLANAR,
AUDIO_FORMAT_FLOAT_PLANAR,
};
enum speaker_layout {
......@@ -56,7 +64,7 @@ enum speaker_layout {
};
struct audio_data {
const void *data;
const uint8_t *data[MAX_AUDIO_PLANES];
uint32_t frames;
uint64_t timestamp;
float volume;
......@@ -99,16 +107,49 @@ static inline uint32_t get_audio_channels(enum speaker_layout speakers)
static inline size_t get_audio_bytes_per_channel(enum audio_format type)
{
switch (type) {
case AUDIO_FORMAT_U8BIT: return 1;
case AUDIO_FORMAT_16BIT: return 2;
case AUDIO_FORMAT_U8BIT:
case AUDIO_FORMAT_U8BIT_PLANAR:
return 1;
case AUDIO_FORMAT_16BIT:
case AUDIO_FORMAT_16BIT_PLANAR:
return 2;
case AUDIO_FORMAT_FLOAT:
case AUDIO_FORMAT_32BIT: return 4;
case AUDIO_FORMAT_UNKNOWN: return 0;
case AUDIO_FORMAT_FLOAT_PLANAR:
case AUDIO_FORMAT_32BIT:
case AUDIO_FORMAT_32BIT_PLANAR:
return 4;
case AUDIO_FORMAT_UNKNOWN:
return 0;
}
return 0;
}
static inline size_t is_audio_planar(enum audio_format type)
{
switch (type) {
case AUDIO_FORMAT_U8BIT:
case AUDIO_FORMAT_16BIT:
case AUDIO_FORMAT_32BIT:
case AUDIO_FORMAT_FLOAT:
return false;
case AUDIO_FORMAT_U8BIT_PLANAR:
case AUDIO_FORMAT_FLOAT_PLANAR:
case AUDIO_FORMAT_16BIT_PLANAR:
case AUDIO_FORMAT_32BIT_PLANAR:
return true;
case AUDIO_FORMAT_UNKNOWN:
return false;
}
return false;
}
static inline size_t get_audio_size(enum audio_format type,
enum speaker_layout speakers, uint32_t frames)
{
......@@ -133,6 +174,8 @@ EXPORT void audio_output_disconnect(audio_t video,
void *param);
EXPORT size_t audio_output_blocksize(audio_t audio);
EXPORT size_t audio_output_planes(audio_t audio);
EXPORT size_t audio_output_channels(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);
......
......@@ -17,6 +17,7 @@
#include "../util/bmem.h"
#include "audio-resampler.h"
#include "audio-io.h"
#include <libavutil/opt.h>
#include <libavutil/channel_layout.h>
#include <libswresample/swresample.h>
......@@ -29,22 +30,27 @@ struct audio_resampler {
uint64_t input_layout;
enum AVSampleFormat input_format;
uint8_t *output_buffer;
uint8_t *output_buffer[MAX_AUDIO_PLANES];
uint64_t output_layout;
enum AVSampleFormat output_format;
int output_size;
uint32_t output_ch;
uint32_t output_freq;
uint32_t output_planes;
};
static inline enum AVSampleFormat convert_audio_format(enum audio_format format)
{
switch (format) {
case AUDIO_FORMAT_UNKNOWN: return AV_SAMPLE_FMT_S16;
case AUDIO_FORMAT_U8BIT: return AV_SAMPLE_FMT_U8;
case AUDIO_FORMAT_16BIT: return AV_SAMPLE_FMT_S16;
case AUDIO_FORMAT_32BIT: return AV_SAMPLE_FMT_S32;
case AUDIO_FORMAT_FLOAT: return AV_SAMPLE_FMT_FLT;
case AUDIO_FORMAT_UNKNOWN: return AV_SAMPLE_FMT_S16;
case AUDIO_FORMAT_U8BIT: return AV_SAMPLE_FMT_U8;
case AUDIO_FORMAT_16BIT: return AV_SAMPLE_FMT_S16;
case AUDIO_FORMAT_32BIT: return AV_SAMPLE_FMT_S32;
case AUDIO_FORMAT_FLOAT: return AV_SAMPLE_FMT_FLT;
case AUDIO_FORMAT_U8BIT_PLANAR: return AV_SAMPLE_FMT_U8P;
case AUDIO_FORMAT_16BIT_PLANAR: return AV_SAMPLE_FMT_S16P;
case AUDIO_FORMAT_32BIT_PLANAR: return AV_SAMPLE_FMT_S32P;
case AUDIO_FORMAT_FLOAT_PLANAR: return AV_SAMPLE_FMT_FLTP;
}
/* shouldn't get here */
......@@ -77,16 +83,17 @@ audio_resampler_t audio_resampler_create(struct resample_info *dst,
struct audio_resampler *rs = bmalloc(sizeof(struct audio_resampler));
int errcode;
memset(rs, 0, sizeof(struct audio_resampler));
rs->opened = false;
rs->input_freq = src->samples_per_sec;
rs->input_layout = convert_speaker_layout(src->speakers);
rs->input_format = convert_audio_format(src->format);
rs->output_buffer = NULL;
rs->output_size = 0;
rs->output_ch = get_audio_channels(dst->speakers);
rs->output_freq = dst->samples_per_sec;
rs->output_layout = convert_speaker_layout(dst->speakers);
rs->output_format = convert_audio_format(dst->format);
rs->output_planes = is_audio_planar(dst->format) ? rs->output_ch : 1;
rs->context = swr_alloc_set_opts(NULL,
rs->output_layout, rs->output_format, dst->samples_per_sec,
......@@ -116,47 +123,50 @@ void audio_resampler_destroy(audio_resampler_t rs)
if (rs->context)
swr_free(&rs->context);
if (rs->output_buffer)
av_freep(&rs->output_buffer);
av_freep(&rs->output_buffer[0]);
bfree(rs);
}
}
bool audio_resampler_resample(audio_resampler_t rs,
void **output, uint32_t *out_frames,
const void *input, uint32_t in_frames,
uint64_t *timestamp_offset)
uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset,
const uint8_t *const input[], uint32_t in_frames)
{
struct SwrContext *context = rs->context;
int ret;
int64_t delay = swr_get_delay(context, rs->input_freq);
int estimated = (int)av_rescale_rnd(
delay + (int64_t)in_frames,
(int64_t)rs->output_freq, (int64_t)rs->input_freq,
AV_ROUND_UP);
*timestamp_offset = (uint64_t)swr_get_delay(context, 1000000000);
*ts_offset = (uint64_t)swr_get_delay(context, 1000000000);
/* resize the buffer if bigger */
if (estimated > rs->output_size) {
if (rs->output_buffer)
av_freep(&rs->output_buffer);
av_samples_alloc(&rs->output_buffer, NULL, rs->output_ch,
if (rs->output_buffer[0])
av_freep(&rs->output_buffer[0]);
av_samples_alloc(rs->output_buffer, NULL, rs->output_ch,
estimated, rs->output_format, 0);
rs->output_size = estimated;
}
ret = swr_convert(context,
&rs->output_buffer, rs->output_size,
(const uint8_t**)&input, in_frames);
rs->output_buffer, rs->output_size,
(const uint8_t**)input, in_frames);
if (ret < 0) {
blog(LOG_ERROR, "swr_convert failed: %d", ret);
return false;
}
*output = rs->output_buffer;
for (uint32_t i = 0; i < rs->output_planes; i++)
output[i] = rs->output_buffer[i];
*out_frames = (uint32_t)ret;
return true;
}
......@@ -38,9 +38,8 @@ EXPORT audio_resampler_t audio_resampler_create(struct resample_info *dst,
EXPORT void audio_resampler_destroy(audio_resampler_t resampler);
EXPORT bool audio_resampler_resample(audio_resampler_t resampler,
void **output, uint32_t *out_frames,
const void *input, uint32_t in_frames,
uint64_t *timestamp_offset);
uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset,
const uint8_t *const input[], uint32_t in_frames);
#ifdef __cplusplus
}
......
......@@ -85,34 +85,34 @@ static inline void pack_chroma_2plane(uint8_t *u_plane, uint8_t *v_plane,
*(uint16_t*)(v_plane+chroma_pos) = (uint16_t)(packed_vals>>16);
}
void compress_uyvx_to_i420(const void *input_v, uint32_t width, uint32_t height,
uint32_t row_bytes, uint32_t start_y, uint32_t end_y,
void **output)
void compress_uyvx_to_i420(
const uint8_t *input, uint32_t in_row_bytes,
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output[], const uint32_t out_row_bytes[])
{
const uint8_t *input = input_v;
uint8_t *lum_plane = output[0];
uint8_t *u_plane = output[1];
uint8_t *v_plane = output[2];
uint32_t chroma_pitch = width >> 1;
uint32_t y;
__m128i lum_mask = _mm_set1_epi32(0x0000FF00);
__m128i uv_mask = _mm_set1_epi16(0x00FF);
for (y = start_y; y < end_y; y += 2) {
uint32_t y_pos = y * row_bytes;
uint32_t chroma_y_pos = (y>>1) * chroma_pitch;
uint32_t lum_y_pos = y * width;
uint32_t y_pos = y * in_row_bytes;
uint32_t chroma_y_pos = (y>>1) * out_row_bytes[1];
uint32_t lum_y_pos = y * out_row_bytes[0];
uint32_t x;
for (x = 0; x < width; x += 4) {
const uint8_t *img = input + y_pos + x*4;
uint32_t lum_pos0 = lum_y_pos + x;
uint32_t lum_pos1 = lum_pos0 + width;
uint32_t lum_pos1 = lum_pos0 + out_row_bytes[0];
__m128i line1 = _mm_load_si128((const __m128i*)img);
__m128i line2 = _mm_load_si128(
(const __m128i*)(img + row_bytes));
(const __m128i*)(img + in_row_bytes));
pack_lum(lum_plane, lum_pos0, lum_pos1,
line1, line2, lum_mask);
......@@ -123,10 +123,11 @@ void compress_uyvx_to_i420(const void *input_v, uint32_t width, uint32_t height,
}
}
static inline void _compress_uyvx_to_nv12(const uint8_t *input,
uint32_t width, uint32_t height, uint32_t pitch,
uint32_t start_y, uint32_t end_y, uint32_t row_bytes_out,
void **output)
void compress_uyvx_to_nv12(
const uint8_t *input, uint32_t in_row_bytes,
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output[], const uint32_t out_row_bytes[])
{
uint8_t *lum_plane = output[0];
uint8_t *chroma_plane = output[1];
......@@ -136,19 +137,19 @@ static inline void _compress_uyvx_to_nv12(const uint8_t *input,
__m128i uv_mask = _mm_set1_epi16(0x00FF);
for (y = start_y; y < end_y; y += 2) {
uint32_t y_pos = y * pitch;
uint32_t chroma_y_pos = (y>>1) * row_bytes_out;
uint32_t lum_y_pos = y * row_bytes_out;
uint32_t y_pos = y * in_row_bytes;
uint32_t chroma_y_pos = (y>>1) * out_row_bytes[1];
uint32_t lum_y_pos = y * out_row_bytes[0];
uint32_t x;
for (x = 0; x < width; x += 4) {
const uint8_t *img = input + y_pos + x*4;
uint32_t lum_pos0 = lum_y_pos + x;
uint32_t lum_pos1 = lum_pos0 + row_bytes_out;
uint32_t lum_pos1 = lum_pos0 + out_row_bytes[0];
__m128i line1 = _mm_load_si128((const __m128i*)img);
__m128i line2 = _mm_load_si128(
(const __m128i*)(img + pitch));
(const __m128i*)(img + in_row_bytes));
pack_lum(lum_plane, lum_pos0, lum_pos1,
line1, line2, lum_mask);
......@@ -158,48 +159,28 @@ static inline void _compress_uyvx_to_nv12(const uint8_t *input,
}
}
void compress_uyvx_to_nv12(const void *input, uint32_t width, uint32_t height,
uint32_t row_bytes, uint32_t start_y, uint32_t end_y,
void **output)
void decompress_420(
const uint8_t *const input[], const uint32_t in_row_bytes[],
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output, uint32_t out_row_bytes)
{
_compress_uyvx_to_nv12(input, width, height, row_bytes,
start_y, end_y, width, output);
}
void compress_uyvx_to_nv12_aligned(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
uint32_t start_y, uint32_t end_y, uint32_t row_bytes_out,
void **output)
{
_compress_uyvx_to_nv12(input, width, height, row_bytes,
start_y, end_y, row_bytes_out, output);
}
void decompress_420(const void *input_v, uint32_t width, uint32_t height,
uint32_t row_bytes, uint32_t start_y, uint32_t end_y,
void *output_v)
{
uint8_t *output = output_v;
const uint8_t *input = input_v;
const uint8_t *input2 = input + width * height;
const uint8_t *input3 = input2 + width * height / 4;
uint32_t start_y_d2 = start_y/2;
uint32_t width_d2 = width/2;
uint32_t height_d2 = end_y/2;
uint32_t y;
for (y = start_y_d2; y < height_d2; y++) {
const uint8_t *chroma0 = input2 + y * width_d2;
const uint8_t *chroma1 = input3 + y * width_d2;
const uint8_t *chroma0 = input[1] + y * in_row_bytes[1];
const uint8_t *chroma1 = input[2] + y * in_row_bytes[2];
register const uint8_t *lum0, *lum1;
register uint32_t *output0, *output1;
uint32_t x;
lum0 = input + y * 2*width;
lum0 = input[0] + y * 2*width;
lum1 = lum0 + width;
output0 = (uint32_t*)(output + y * 2*row_bytes);
output1 = (uint32_t*)((uint8_t*)output0 + row_bytes);
output0 = (uint32_t*)(output + y * 2 * in_row_bytes[0]);
output1 = (uint32_t*)((uint8_t*)output0 + in_row_bytes[0]);
for (x = 0; x < width_d2; x++) {
uint32_t out;
......@@ -214,29 +195,28 @@ void decompress_420(const void *input_v, uint32_t width, uint32_t height,
}
}
void decompress_nv12(const void *input_v, uint32_t width, uint32_t height,
uint32_t row_bytes, uint32_t start_y, uint32_t end_y,
void *output_v)
void decompress_nv12(
const uint8_t *const input[], const uint32_t in_row_bytes[],
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output, uint32_t out_row_bytes)
{
uint8_t *output = output_v;
const uint8_t *input = input_v;
const uint8_t *input2 = input + width * height;
uint32_t start_y_d2 = start_y/2;
uint32_t width_d2 = width/2;
uint32_t height_d2 = end_y/2;
uint32_t y;
for (y = start_y_d2; y < height_d2; y++) {
const uint16_t *chroma = (uint16_t*)(input2 + y * width);
const uint16_t *chroma;
register const uint8_t *lum0, *lum1;
register uint32_t *output0, *output1;
uint32_t x;
lum0 = input + y * 2*width;
lum1 = lum0 + width;
output0 = (uint32_t*)(output + y * 2*row_bytes);
output1 = (uint32_t*)((uint8_t*)output0 + row_bytes);
chroma = (const uint16_t*)(input[1] + y * in_row_bytes[1]);
lum0 = input[0] + y*2 * in_row_bytes[0];
lum1 = lum0 + in_row_bytes[0];
output0 = (uint32_t*)(output + y*2 * out_row_bytes);
output1 = (uint32_t*)((uint8_t*)output0 + out_row_bytes);
for (x = 0; x < width_d2; x++) {
uint32_t out = *(chroma++) << 8;
......@@ -250,15 +230,14 @@ void decompress_nv12(const void *input_v, uint32_t width, uint32_t height,
}
}
void decompress_422(const void *input_v, uint32_t width, uint32_t height,
uint32_t row_bytes, uint32_t start_y, uint32_t end_y,
void *output_v, bool leading_lum)
void decompress_422(
const uint8_t *input, uint32_t in_row_bytes,
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output, uint32_t out_row_bytes,
bool leading_lum)
{
const uint8_t *input = input_v;
uint8_t *output = output_v;
uint32_t width_d2 = width >> 1;
uint32_t line_size = width * 2;
uint32_t width_d2 = width >> 1;
uint32_t y;
register const uint32_t *input32;
......@@ -267,9 +246,9 @@ void decompress_422(const void *input_v, uint32_t width, uint32_t height,
if (leading_lum) {
for (y = start_y; y < end_y; y++) {
input32 = (uint32_t*)(input + y*line_size);
input32 = (const uint32_t*)(input + y*in_row_bytes);
input32_end = input32 + width_d2;
output32 = (uint32_t*)(output + y*row_bytes);
output32 = (uint32_t*)(output + y*out_row_bytes);
while(input32 < input32_end) {
register uint32_t dw = *input32;
......@@ -285,9 +264,9 @@ void decompress_422(const void *input_v, uint32_t width, uint32_t height,
}
} else {
for (y = start_y; y < end_y; y++) {
input32 = (uint32_t*)(input + y*line_size);
input32 = (const uint32_t*)(input + y*in_row_bytes);
input32_end = input32 + width_d2;
output32 = (uint32_t*)(output + y*row_bytes);
output32 = (uint32_t*)(output + y*out_row_bytes);
while (input32 < input32_end) {
register uint32_t dw = *input32;
......
......@@ -23,32 +23,40 @@
extern "C" {
#endif
EXPORT void compress_uyvx_to_i420(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
uint32_t start_y, uint32_t end_y, void **output);
/*
* Functions for converting to and from packed 444 YUV
*/
EXPORT void compress_uyvx_to_nv12(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
uint32_t start_y, uint32_t end_y, void **output);
EXPORT void compress_uyvx_to_i420(
const uint8_t *input, uint32_t in_row_bytes,
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output[], const uint32_t out_row_bytes[]);
EXPORT void decompress_nv12(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
uint32_t start_y, uint32_t end_y, void *output);
EXPORT void compress_uyvx_to_nv12(
const uint8_t *input, uint32_t in_row_bytes,
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output[], const uint32_t out_row_bytes[]);
EXPORT void decompress_420(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
uint32_t start_y, uint32_t end_y, void *output);
EXPORT void decompress_nv12(
const uint8_t *const input[], const uint32_t in_row_bytes[],
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output, uint32_t out_row_bytes);
EXPORT void decompress_422(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
uint32_t start_y, uint32_t end_y, void *output,
bool leading_lum);
EXPORT void decompress_420(
const uint8_t *const input[], const uint32_t in_row_bytes[],
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint8_t *output, uint32_t out_row_bytes);
/* special case for quicksync */
EXPORT void compress_uyvx_to_nv12_aligned(const void *input,
uint32_t width, uint32_t height, uint32_t row_bytes,
EXPORT void decompress_422(
const uint8_t *input, uint32_t in_row_bytes,
uint32_t width, uint32_t height,
uint32_t start_y, uint32_t end_y,
uint32_t row_bytes_out, void **output);
uint8_t *output, uint32_t out_row_bytes,
bool leading_lum);
#ifdef __cplusplus
}
......
......@@ -21,6 +21,7 @@
#include "../util/threading.h"
#include "../util/darray.h"
#include "format-conversion.h"
#include "video-io.h"
struct video_input {
......@@ -36,8 +37,10 @@ struct video_output {
pthread_mutex_t data_mutex;
event_t stop_event;
struct video_frame *cur_frame;
struct video_frame *next_frame;
struct video_frame cur_frame;
struct video_frame next_frame;
bool new_frame;
event_t update_event;
uint64_t frame_time;
volatile uint64_t cur_video_time;
......@@ -54,9 +57,9 @@ static inline void video_swapframes(struct video_output *video)
{
pthread_mutex_lock(&video->data_mutex);
if (video->next_frame) {
if (video->new_frame) {
video->cur_frame = video->next_frame;
video->next_frame = NULL;
video->new_frame = false;
}
pthread_mutex_unlock(&video->data_mutex);
......@@ -64,15 +67,36 @@ static inline void video_swapframes(struct video_output *video)
static inline void video_output_cur_frame(struct video_output *video)
{
if (!video->cur_frame)
size_t width = video->info.width;
size_t height = video->info.height;
if (!video->cur_frame.data[0])
return;
pthread_mutex_lock(&video->input_mutex);
/* TEST CODE */
/*static struct video_frame frame = {0};
if (!frame.data[0]) {
frame.data[0] = bmalloc(width * height);
frame.data[1] = bmalloc((width/2) * (height/2));
frame.data[2] = bmalloc((width/2) * (height/2));
frame.row_size[0] = width;
frame.row_size[1] = width/2;
frame.row_size[2] = width/2;
}
compress_uyvx_to_i420(
video->cur_frame.data[0], video->cur_frame.row_size[0],
width, height, 0, height,
(uint8_t**)frame.data, (uint32_t*)frame.row_size);*/
/* 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);
input->callback(input->param, &video->cur_frame);//&frame);
}
pthread_mutex_unlock(&video->input_mutex);
......@@ -176,7 +200,7 @@ void video_output_connect(video_t video,
{
pthread_mutex_lock(&video->input_mutex);
if (video_get_input_idx(video, callback, param) != DARRAY_INVALID) {
if (video_get_input_idx(video, callback, param) == DARRAY_INVALID) {
struct video_input input;
input.callback = callback;
input.param = param;
......@@ -223,7 +247,8 @@ const struct video_output_info *video_output_getinfo(video_t video)
void video_output_frame(video_t video, struct video_frame *frame)
{
pthread_mutex_lock(&video->data_mutex);
video->next_frame = frame;
video->next_frame = *frame;
video->new_frame = true;
pthread_mutex_unlock(&video->data_mutex);
}
......
......@@ -25,6 +25,8 @@ extern "C" {
/* Base video output component. Use this to create an video output track. */
#define MAX_VIDEO_PLANES 8
struct video_output;
typedef struct video_output *video_t;
......@@ -49,8 +51,8 @@ enum video_format {
};
struct video_frame {
const void *data;
uint32_t row_size; /* for RGB/BGR formats and UYVX */
const uint8_t *data[MAX_VIDEO_PLANES];
uint32_t row_size[MAX_VIDEO_PLANES];
uint64_t timestamp;
};
......
......@@ -79,6 +79,11 @@ obs_output_t obs_output_create(const char *id, const char *name,
void obs_output_destroy(obs_output_t output)
{
if (output) {
if (output->callbacks.active) {
if (output->callbacks.active(output->data))
output->callbacks.stop(output->data);
}
pthread_mutex_lock(&obs->data.outputs_mutex);
da_erase_item(obs->data.outputs, &output);
pthread_mutex_unlock(&obs->data.outputs_mutex);
......
......@@ -180,6 +180,84 @@ fail:
return NULL;
}
#define ALIGN_SIZE(size, align) \
size = (((size)+(align-1)) & (~(align-1)))
static void alloc_frame_data(struct source_frame *frame,
enum video_format format, uint32_t width, uint32_t height)
{
size_t size;
size_t offsets[MAX_VIDEO_PLANES];
memset(offsets, 0, sizeof(offsets));
switch (format) {
case VIDEO_FORMAT_NONE:
return;
case VIDEO_FORMAT_I420:
size = width * height;
ALIGN_SIZE(size, 32);
offsets[0] = size;
size += (width/2) * (height/2);
ALIGN_SIZE(size, 32);
offsets[1] = size;
size += (width/2) * (height/2);
ALIGN_SIZE(size, 32);
frame->data[0] = bmalloc(size);
frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
frame->data[2] = (uint8_t*)frame->data[0] + offsets[1];
frame->row_bytes[0] = width;
frame->row_bytes[1] = width/2;
frame->row_bytes[2] = width/2;
break;
case VIDEO_FORMAT_NV12:
size = width * height;
ALIGN_SIZE(size, 32);
offsets[0] = size;
size += (width/2) * (height/2) * 2;
ALIGN_SIZE(size, 32);
frame->data[0] = bmalloc(size);
frame->data[1] = (uint8_t*)frame->data[0] + offsets[0];
frame->row_bytes[0] = width;
frame->row_bytes[1] = width;
break;
case VIDEO_FORMAT_YVYU:
case VIDEO_FORMAT_YUY2:
case VIDEO_FORMAT_UYVY:
size = width * height * 2;
ALIGN_SIZE(size, 32);
frame->data[0] = bmalloc(size);
frame->row_bytes[0] = width*2;
break;
case VIDEO_FORMAT_YUVX:
case VIDEO_FORMAT_UYVX:
case VIDEO_FORMAT_RGBA:
case VIDEO_FORMAT_BGRA:
case VIDEO_FORMAT_BGRX:
size = width * height * 4;
ALIGN_SIZE(size, 32);
frame->data[0] = bmalloc(size);
frame->row_bytes[0] = width*4;
break;
}
}
struct source_frame *source_frame_alloc(enum video_format format,
uint32_t width, uint32_t height)
{
struct source_frame *frame = bmalloc(sizeof(struct source_frame));
memset(frame, 0, sizeof(struct source_frame));
alloc_frame_data(frame, format, width, height);
frame->format = format;
frame->width = width;
frame->height = height;
return frame;
}
static void obs_source_destroy(obs_source_t source)
{
size_t i;
......@@ -202,7 +280,9 @@ static void obs_source_destroy(obs_source_t source)
if (source->data)
source->callbacks.destroy(source->data);
bfree(source->audio_data.data);
for (i = 0; i < MAX_AUDIO_PLANES; i++)
bfree(source->audio_data.data[i]);
audio_line_destroy(source->audio_line);
audio_resampler_destroy(source->resampler);
......@@ -454,7 +534,8 @@ static bool upload_frame(texture_t tex, const struct source_frame *frame)
enum convert_type type = get_convert_type(frame->format);
if (type == CONVERT_NONE) {
texture_setimage(tex, frame->data, frame->row_bytes, false);
texture_setimage(tex, frame->data[0], frame->row_bytes[0],
false);
return true;
}
......@@ -462,20 +543,24 @@ static bool upload_frame(texture_t tex, const struct source_frame *frame)
return false;
if (type == CONVERT_420)
decompress_420(frame->data, frame->width, frame->height,
frame->row_bytes, 0, frame->height, ptr);
decompress_420(frame->data, frame->row_bytes,
frame->width, frame->height, 0, frame->height,
ptr, row_bytes);
else if (type == CONVERT_NV12)
decompress_nv12(frame->data, frame->width, frame->height,
frame->row_bytes, 0, frame->height, ptr);
decompress_nv12(frame->data, frame->row_bytes,
frame->width, frame->height, 0, frame->height,
ptr, row_bytes);
else if (type == CONVERT_422_Y)
decompress_422(frame->data, frame->width, frame->height,
frame->row_bytes, 0, frame->height, ptr, true);
decompress_422(frame->data[0], frame->row_bytes[0],
frame->width, frame->height, 0, frame->height,
ptr, row_bytes, true);
else if (type == CONVERT_422_U)
decompress_422(frame->data, frame->width, frame->height,
frame->row_bytes, 0, frame->height, ptr, false);
decompress_422(frame->data[0], frame->row_bytes[0],
frame->width, frame->height, 0, frame->height,
ptr, row_bytes, false);
texture_unmap(tex);
return true;
......@@ -704,14 +789,68 @@ static inline struct source_frame *filter_async_video(obs_source_t source,
return in;
}
static inline void copy_frame_data_line(struct source_frame *dst,
const struct source_frame *src, uint32_t plane, uint32_t y)
{
uint32_t pos_src = y * src->row_bytes[plane];
uint32_t pos_dst = y * dst->row_bytes[plane];
uint32_t bytes = dst->row_bytes[plane] < src->row_bytes[plane] ?
dst->row_bytes[plane] : src->row_bytes[plane];
memcpy(dst->data[plane] + pos_dst, src->data[plane] + pos_src, bytes);
}
static inline void copy_frame_data_plane(struct source_frame *dst,
const struct source_frame *src, uint32_t plane, uint32_t lines)
{
if (dst->row_bytes != src->row_bytes)
for (uint32_t y = 0; y < lines; y++)
copy_frame_data_line(dst, src, plane, y);
else
memcpy(dst->data[plane], src->data[plane],
dst->row_bytes[plane] * lines);
}
static void copy_frame_data(struct source_frame *dst,
const struct source_frame *src)
{
dst->flip = src->flip;
dst->timestamp = src->timestamp;
memcpy(dst->color_matrix, src->color_matrix, sizeof(float) * 16);
switch (dst->format) {
case VIDEO_FORMAT_I420:
copy_frame_data_plane(dst, src, 0, dst->height);
copy_frame_data_plane(dst, src, 1, dst->height/2);
copy_frame_data_plane(dst, src, 2, dst->height/2);
break;
case VIDEO_FORMAT_NV12:
copy_frame_data_plane(dst, src, 0, dst->height);
copy_frame_data_plane(dst, src, 1, dst->height/2);
break;
case VIDEO_FORMAT_YVYU:
case VIDEO_FORMAT_YUY2:
case VIDEO_FORMAT_UYVY:
case VIDEO_FORMAT_NONE:
case VIDEO_FORMAT_YUVX:
case VIDEO_FORMAT_UYVX:
case VIDEO_FORMAT_RGBA:
case VIDEO_FORMAT_BGRA:
case VIDEO_FORMAT_BGRX:
copy_frame_data_plane(dst, src, 0, dst->height);
}
}
static inline struct source_frame *cache_video(obs_source_t source,
const struct source_frame *frame)
{
/* TODO: use an actual cache */
struct source_frame *new_frame = bmalloc(sizeof(struct source_frame));
memcpy(new_frame, frame, sizeof(struct source_frame));
new_frame->data = bmalloc(frame->row_bytes * frame->height);
struct source_frame *new_frame = source_frame_alloc(frame->format,
frame->width, frame->height);
copy_frame_data(new_frame, frame);
return new_frame;
}
......@@ -780,21 +919,28 @@ static inline void reset_resampler(obs_source_t source,
}
static inline void copy_audio_data(obs_source_t source,
const void *data, uint32_t frames, uint64_t timestamp)
const void *const data[], uint32_t frames, uint64_t timestamp)
{
size_t planes = audio_output_planes(obs->audio.audio);
size_t blocksize = audio_output_blocksize(obs->audio.audio);
size_t size = (size_t)frames * blocksize;
/* ensure audio storage capacity */
if (source->audio_storage_size < size) {
bfree(source->audio_data.data);
source->audio_data.data = bmalloc(size);
source->audio_storage_size = size;
}
size_t size = (size_t)frames * blocksize;
bool resize = source->audio_storage_size < size;
source->audio_data.frames = frames;
source->audio_data.timestamp = timestamp;
memcpy(source->audio_data.data, data, size);
for (size_t i = 0; i < planes; i++) {
/* ensure audio storage capacity */
if (resize) {
bfree(source->audio_data.data[i]);
source->audio_data.data[i] = bmalloc(size);
}
memcpy(source->audio_data.data[i], data[i], size);
}
if (resize)
source->audio_storage_size = size;
}
/* resamples/remixes new audio to the designated main audio output format */
......@@ -809,12 +955,15 @@ static void process_audio(obs_source_t source, const struct source_audio *audio)
return;
if (source->resampler) {
void *output;
uint8_t *output[MAX_AUDIO_PLANES];
uint32_t frames;
uint64_t offset;
audio_resampler_resample(source->resampler, &output, &frames,
audio->data, audio->frames, &offset);
memset(output, 0, sizeof(output));
audio_resampler_resample(source->resampler,
output, &frames, &offset,
audio->data, audio->frames);
copy_audio_data(source, output, frames,
audio->timestamp - offset);
......@@ -843,7 +992,10 @@ void obs_source_output_audio(obs_source_t source,
* have a base for sync */
if (source->timing_set || (flags & SOURCE_ASYNC_VIDEO) == 0) {
struct audio_data data;
data.data = output->data;
for (int i = 0; i < MAX_AUDIO_PLANES; i++)
data.data[i] = output->data[i];
data.frames = output->frames;
data.timestamp = output->timestamp;
source_output_audio_line(source, &data);
......
......@@ -231,9 +231,10 @@ static inline void output_video(struct obs_core_video *video, int cur_texture,
if (!video->textures_copied[prev_texture])
return;
memset(&frame, 0, sizeof(struct video_frame));
frame.timestamp = timestamp;
if (stagesurface_map(surface, &frame.data, &frame.row_size)) {
if (stagesurface_map(surface, &frame.data[0], &frame.row_size[0])) {
video->mapped_surface = surface;
video_output_frame(video->video, &frame);
}
......
......@@ -96,13 +96,13 @@ struct obs_video_info {
};
struct filtered_audio {
void *data;
uint8_t *data[MAX_AUDIO_PLANES];
uint32_t frames;
uint64_t timestamp;
};
struct source_audio {
const void *data;
const uint8_t *data[MAX_AUDIO_PLANES];
uint32_t frames;
/* audio will be automatically resampled/upmixed/downmixed */
......@@ -115,10 +115,10 @@ struct source_audio {
};
struct source_frame {
void *data;
uint8_t *data[MAX_VIDEO_PLANES];
uint32_t row_bytes[MAX_VIDEO_PLANES];
uint32_t width;
uint32_t height;
uint32_t row_bytes;
uint64_t timestamp;
enum video_format format;
......@@ -126,12 +126,13 @@ struct source_frame {
bool flip;
};
EXPORT struct source_frame *source_frame_alloc(enum video_format format,
uint32_t width, uint32_t height);
static inline void source_frame_destroy(struct source_frame *frame)
{
if (frame) {
bfree(frame->data);
bfree(frame);
}
bfree(frame->data[0]);
bfree(frame);
}
enum packet_priority {
......
......@@ -15,7 +15,6 @@
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
......@@ -93,6 +92,11 @@ void bcrash(const char *format, ...)
va_end(args);
}
void blogva(enum log_type type, const char *format, va_list args)
{
log_handler(type, format, args);
}
void blog(enum log_type type, const char *format, ...)
{
va_list args;
......
......@@ -16,7 +16,6 @@
#pragma once
#include <wctype.h>
#include <stdarg.h>
#include "c99defs.h"
......@@ -40,6 +39,8 @@ EXPORT void base_set_log_handler(
void (*handler)(enum log_type, const char *, va_list));
EXPORT void base_set_crash_handler(void (*handler)(const char *, va_list));
EXPORT void blogva(enum log_type type, const char *format, va_list args);
#ifndef _MSC_VER
#define PRINTFATTR(f, a) __attribute__((__format__(__printf__, f, a)))
#else
......
......@@ -62,6 +62,11 @@ void OBSBasic::OBSInit()
/* TODO: this is a test */
obs_load_module("test-input");
/*obs_load_module("obs-ffmpeg");
obs_output_t output = obs_output_create("ffmpeg_output", "test",
NULL);
obs_output_start(output);*/
/* HACK: fixes a qt bug with native widgets with native repaint */
ui->previewContainer->repaint();
......@@ -302,7 +307,7 @@ bool OBSBasic::InitAudio()
struct audio_output_info ai;
ai.name = "test";
ai.samples_per_sec = 44100;
ai.format = AUDIO_FORMAT_16BIT;
ai.format = AUDIO_FORMAT_FLOAT_PLANAR;
ai.speakers = SPEAKERS_STEREO;
ai.buffer_ms = 700;
......
......@@ -22,6 +22,9 @@
#define FILENAME_TODO "D:\\test.mp4"
#define SPS_TODO 44100
/* NOTE: much of this stuff is test stuff that was more or less copied from
* the muxing.c ffmpeg example */
static inline enum AVPixelFormat obs_to_ffmpeg_video_format(
enum video_format format)
{
......@@ -94,17 +97,22 @@ static bool open_video_codec(struct ffmpeg_data *data,
return false;
}
if (context->pix_fmt != AV_PIX_FMT_YUV420P) {
ret = avpicture_alloc(&data->src_picture, AV_PIX_FMT_YUV420P,
context->width, context->height);
if (ret < 0) {
blog(LOG_ERROR, "Failed to allocate src_picture: %s",
av_err2str(ret));
return false;
}
*((AVPicture*)data->vframe) = data->dst_picture;
return true;
}
static bool init_swscale(struct ffmpeg_data *data, AVCodecContext *context)
{
data->swscale = sws_getContext(
context->width, context->height, AV_PIX_FMT_YUV420P,
context->width, context->height, context->pix_fmt,
SWS_BICUBIC, NULL, NULL, NULL);
if (!data->swscale) {
blog(LOG_ERROR, "Could not initialize swscale");
return false;
}
*((AVPicture*)data->vframe) = data->dst_picture;
return true;
}
......@@ -117,6 +125,7 @@ static bool create_video_stream(struct ffmpeg_data *data)
blog(LOG_ERROR, "No active video");
return false;
}
if (!new_stream(data, &data->video, &data->vcodec,
data->output->oformat->video_codec))
return false;
......@@ -134,7 +143,14 @@ static bool create_video_stream(struct ffmpeg_data *data)
if (data->output->oformat->flags & AVFMT_GLOBALHEADER)
context->flags |= CODEC_FLAG_GLOBAL_HEADER;
return open_video_codec(data, &ovi);
if (!open_video_codec(data, &ovi))
return false;
if (context->pix_fmt != AV_PIX_FMT_YUV420P)
if (!init_swscale(data, context))
return false;
return true;
}
static bool open_audio_codec(struct ffmpeg_data *data,
......@@ -149,6 +165,8 @@ static bool open_audio_codec(struct ffmpeg_data *data,
return false;
}
context->strict_std_compliance = -2;
ret = avcodec_open2(context, data->acodec, NULL);
if (ret < 0) {
blog(LOG_ERROR, "Failed to open audio codec: %s",
......@@ -168,14 +186,17 @@ static bool create_audio_stream(struct ffmpeg_data *data)
blog(LOG_ERROR, "No active audio");
return false;
}
if (!new_stream(data, &data->audio, &data->acodec,
data->output->oformat->audio_codec))
return false;
context = data->audio->codec;
context = data->audio->codec;
context->bit_rate = 128000;
context->channels = get_audio_channels(aoi.speakers);
context->sample_rate = aoi.samples_per_sec;
context->sample_fmt = data->acodec->sample_fmts ?
data->acodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
if (data->output->oformat->flags & AVFMT_GLOBALHEADER)
context->flags |= CODEC_FLAG_GLOBAL_HEADER;
......@@ -187,15 +208,13 @@ static inline bool init_streams(struct ffmpeg_data *data)
{
AVOutputFormat *format = data->output->oformat;
if (format->video_codec != AV_CODEC_ID_NONE) {
if (format->video_codec != AV_CODEC_ID_NONE)
if (!create_video_stream(data))
return false;
}
if (format->audio_codec != AV_CODEC_ID_NONE) {
if (format->audio_codec != AV_CODEC_ID_NONE)
if (!create_audio_stream(data))
return false;
}
return true;
}
......@@ -228,17 +247,14 @@ static inline bool open_output_file(struct ffmpeg_data *data)
static void close_video(struct ffmpeg_data *data)
{
avcodec_close(data->video->codec);
av_free(data->src_picture.data[0]);
av_free(data->dst_picture.data[0]);
avpicture_free(&data->dst_picture);
av_frame_free(&data->vframe);
}
static void close_audio(struct ffmpeg_data *data)
{
av_freep(&data->samples[0]);
avcodec_close(data->audio->codec);
av_free(data->samples[0]);
av_free(data->samples);
av_frame_free(&data->aframe);
}
......@@ -255,6 +271,8 @@ static void ffmpeg_data_free(struct ffmpeg_data *data)
avio_close(data->output->pb);
avformat_free_context(data->output);
memset(data, 0, sizeof(struct ffmpeg_data));
}
static bool ffmpeg_data_init(struct ffmpeg_data *data)
......@@ -265,7 +283,7 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data)
/* TODO: settings */
avformat_alloc_output_context2(&data->output, NULL, NULL,
"D:\\test.mp4");
FILENAME_TODO);
if (!data->output) {
blog(LOG_ERROR, "Couldn't create avformat context");
goto fail;
......@@ -293,7 +311,12 @@ const char *ffmpeg_output_getname(const char *locale)
return "FFmpeg file output";
}
struct ffmpeg_output *ffmpeg_output_create(obs_data_t settings,
void test_callback(void *param, int bla, const char *format, va_list args)
{
blogva(LOG_INFO, format, args);
}
struct ffmpeg_output *ffmpeg_output_create(const char *settings,
obs_output_t output)
{
struct ffmpeg_output *data = bmalloc(sizeof(struct ffmpeg_output));
......@@ -301,27 +324,159 @@ struct ffmpeg_output *ffmpeg_output_create(obs_data_t settings,
data->output = output;
av_log_set_callback(test_callback);
return data;
}
void ffmpeg_output_destroy(struct ffmpeg_output *data)
{
if (data) {
ffmpeg_data_free(&data->ff_data);
if (data->active)
ffmpeg_data_free(&data->ff_data);
bfree(data);
}
}
void ffmpeg_output_update(struct ffmpeg_output *data, obs_data_t settings)
void ffmpeg_output_update(struct ffmpeg_output *data, const char *settings)
{
}
static inline int64_t rescale_ts(int64_t val, AVCodecContext *context,
AVStream *stream)
{
return av_rescale_q_rnd(val, context->time_base,
stream->time_base,
AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX);
}
#define YUV420_PLANES 3
static inline void copy_data(AVPicture *pic, const struct video_frame *frame,
int height)
{
for (int plane = 0; plane < YUV420_PLANES; plane++) {
int frame_rowsize = (int)frame->row_size[plane];
int pic_rowsize = pic->linesize[plane];
int bytes = frame_rowsize < pic_rowsize ?
frame_rowsize : pic_rowsize;
int plane_height = plane == 0 ? height : height/2;
for (int y = 0; y < plane_height; y++) {
int pos_frame = y * frame_rowsize;
int pos_pic = y * pic_rowsize;
memcpy(pic->data[plane] + pos_pic,
frame->data[plane] + pos_frame,
bytes);
}
}
}
static void receive_video(void *param, const struct video_frame *frame)
{
struct ffmpeg_output *output = param;
struct ffmpeg_data *data = &output->ff_data;
AVCodecContext *context = data->video->codec;
AVPacket packet = {0};
int ret, got_packet;
av_init_packet(&packet);
if (context->pix_fmt != AV_PIX_FMT_YUV420P)
sws_scale(data->swscale, frame->data, frame->row_size,
0, context->height, data->dst_picture.data,
data->dst_picture.linesize);
else
copy_data(&data->dst_picture, frame, context->height);
if (data->output->flags & AVFMT_RAWPICTURE) {
packet.flags |= AV_PKT_FLAG_KEY;
packet.stream_index = data->video->index;
packet.data = data->dst_picture.data[0];
packet.size = sizeof(AVPicture);
ret = av_interleaved_write_frame(data->output, &packet);
} else {
data->vframe->pts = data->total_frames;
ret = avcodec_encode_video2(context, &packet, data->vframe,
&got_packet);
if (ret < 0) {
blog(LOG_ERROR, "receive_video: Error encoding "
"video: %s", av_err2str(ret));
return;
}
if (!ret && got_packet && packet.size) {
packet.pts = rescale_ts(packet.pts, context,
data->video);
packet.dts = rescale_ts(packet.dts, context,
data->video);
packet.duration = (int)av_rescale_q(packet.duration,
context->time_base,
data->video->time_base);
ret = av_interleaved_write_frame(data->output, &packet);
} else {
ret = 0;
}
}
if (ret != 0) {
blog(LOG_ERROR, "receive_video: Error writing video: %s",
av_err2str(ret));
}
data->total_frames++;
}
static void receive_audio(void *param, const struct audio_data *frame)
{
struct ffmpeg_output *output = param;
struct ffmpeg_data *data = &output->ff_data;
AVCodecContext *context = data->audio->codec;
AVPacket packet = {0};
int channels = (int)audio_output_channels(obs_audio());
size_t planes = audio_output_planes(obs_audio());
int ret, got_packet;
data->aframe->nb_samples = frame->frames;
data->aframe->pts = av_rescale_q(data->total_samples,
(AVRational){1, context->sample_rate},
context->time_base);
if (!data->samples[0])
av_samples_alloc(data->samples, NULL, channels,
frame->frames, context->sample_fmt, 0);
for (size_t i = 0; i < planes; i++) {
/* TODO */
}
data->total_samples += frame->frames;
ret = avcodec_encode_audio2(context, &packet, data->aframe,
&got_packet);
if (ret < 0) {
blog(LOG_ERROR, "receive_audio: Error encoding audio: %s",
av_err2str(ret));
return;
}
if (!got_packet)
return;
packet.pts = rescale_ts(packet.pts, context, data->audio);
packet.dts = rescale_ts(packet.dts, context, data->audio);
packet.duration = (int)av_rescale_q(packet.duration, context->time_base,
data->audio->time_base);
packet.stream_index = data->audio->index;
ret = av_interleaved_write_frame(data->output, &packet);
if (ret != 0)
blog(LOG_ERROR, "receive_audio: Error writing audio: %s",
av_err2str(ret));
}
bool ffmpeg_output_start(struct ffmpeg_output *data)
......@@ -340,7 +495,7 @@ bool ffmpeg_output_start(struct ffmpeg_output *data)
struct audio_convert_info aci;
aci.samples_per_sec = SPS_TODO;
aci.format = AUDIO_FORMAT_16BIT;
aci.format = AUDIO_FORMAT_FLOAT;
aci.speakers = SPEAKERS_STEREO;
struct video_convert_info vci;
......@@ -349,8 +504,8 @@ bool ffmpeg_output_start(struct ffmpeg_output *data)
vci.height = 0;
vci.row_align = 1;
video_output_connect(video, &vci, receive_video, data);
audio_output_connect(audio, &aci, receive_audio, data);
//video_output_connect(video, &vci, receive_video, data);
//audio_output_connect(audio, &aci, receive_audio, data);
data->active = true;
return true;
......
......@@ -18,6 +18,7 @@
#pragma once
#include <util/c99defs.h>
#include <media-io/audio-io.h>
#include <media-io/video-io.h>
#include <libavformat/avformat.h>
......@@ -31,12 +32,13 @@ struct ffmpeg_data {
AVFormatContext *output;
struct SwsContext *swscale;
AVFrame *vframe;
AVPicture src_picture;
AVPicture dst_picture;
AVFrame *vframe;
int total_frames;
uint8_t *samples[MAX_AUDIO_PLANES];
AVFrame *aframe;
uint8_t **samples;
int total_samples;
bool initialized;
};
......@@ -49,12 +51,12 @@ struct ffmpeg_output {
EXPORT const char *ffmpeg_output_getname(const char *locale);
EXPORT struct ffmpeg_output *ffmpeg_output_create(obs_data_t settings,
EXPORT struct ffmpeg_output *ffmpeg_output_create(const char *settings,
obs_output_t output);
EXPORT void ffmpeg_output_destroy(struct ffmpeg_output *data);
EXPORT void ffmpeg_output_update(struct ffmpeg_output *data,
obs_data_t settings);
const char *settings);
EXPORT bool ffmpeg_output_start(struct ffmpeg_output *data);
EXPORT void ffmpeg_output_stop(struct ffmpeg_output *data);
......
#include <string.h>
#include <util/c99defs.h>
#include <obs.h>
EXPORT const char *enum_outputs(size_t idx);
EXPORT bool enum_outputs(size_t idx, const char **name);
EXPORT uint32_t module_version(uint32_t in_version);
static const char *outputs[] = {"obs_ffmpeg"};
static const char *outputs[] = {"ffmpeg_output"};
const char *enum_outputs(size_t idx)
uint32_t module_version(uint32_t in_version)
{
return LIBOBS_API_VER;
}
bool enum_outputs(size_t idx, const char **name)
{
if (idx >= sizeof(outputs)/sizeof(const char*))
return NULL;
return false;
return outputs[idx];
*name = outputs[idx];
return true;
}
......@@ -26,7 +26,7 @@ static void *sinewave_thread(void *pdata)
}
struct source_audio data;
data.data = bytes;
data.data[0] = bytes;
data.frames = 480;
data.speakers = SPEAKERS_MONO;
data.samples_per_sec = 48000;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册