jp9000 02a07ea0a0 Add preliminary streaming code for testing
- Add some temporary streaming code using FFmpeg.  FFmpeg itself is not
   very ideal for streaming; lack of direct control of the sockets and
   no framedrop handling means that FFmpeg is definitely not something
   you want to use without wrapper code.  I'd prefer writing my own
   network framework in this particular case just because you give away
   so much control of the network interface.  Wasted an entire day
   trying to go through FFmpeg issues.

   There's just no way FFmpeg should be used for real streaming (at
   least without being patched or submitting some sort of patch, but I'm
   sort of feeling "meh" on that idea)

   I had to end up writing multiple threads just to handle both
   connecting and writing, because av_interleaved_write_frame blocks
   every call, stalling the main encoder thread, and thus also stalling
   draw signals.

 - Add some temporary user interface for streaming settings.  This is
   just temporary for the time being.  It's in the outputs section of
   the basic-mode settings

 - Make it so that dynamic arrays do not free all their data when the
   size just happens to be reduced to 0.  This prevents constant
   reallocation when an array keeps going from 1 item to 0 items.  Also,
   it was bad to become dependent upon that functionality.  You must now
   always explicitly call "free" on it to ensure the data is free, and
   that's how it should be.  Implicit functionality can lead to
   confusion and maintainability issues.
2014-03-10 13:10:35 -07:00

539 lines
13 KiB
C

/*
* Copyright (c) 2013 Hugh Bailey <obs.jim@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include "c99defs.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include "bmem.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Dynamic array.
*
* NOTE: Not type-safe when using directly.
* Specifying size per call with inline maximizes compiler optimizations
*
* See DARRAY macro at the bottom of thhe file for slightly safer usage.
*/
#define DARRAY_INVALID ((size_t)-1)
struct darray {
void *array;
size_t num;
size_t capacity;
};
static inline void darray_init(struct darray *dst)
{
dst->array = NULL;
dst->num = 0;
dst->capacity = 0;
}
static inline void darray_free(struct darray *dst)
{
bfree(dst->array);
dst->array = NULL;
dst->num = 0;
dst->capacity = 0;
}
static inline size_t darray_alloc_size(const size_t element_size,
const struct darray *da)
{
return element_size*da->num;
}
static inline void *darray_item(const size_t element_size,
const struct darray *da, size_t idx)
{
return (void*)(((uint8_t*)da->array) + element_size*idx);
}
static inline void *darray_end(const size_t element_size,
const struct darray *da)
{
if (!da->num)
return NULL;
return darray_item(element_size, da, da->num-1);
}
static inline void darray_reserve(const size_t element_size,
struct darray *dst, const size_t capacity)
{
void *ptr;
if (capacity == 0 || capacity <= dst->num)
return;
ptr = bmalloc(element_size*capacity);
if (dst->num)
memcpy(ptr, dst->array, element_size*dst->num);
if (dst->array)
bfree(dst->array);
dst->array = ptr;
dst->capacity = capacity;
}
static inline void darray_ensure_capacity(const size_t element_size,
struct darray *dst, const size_t new_size)
{
size_t new_cap;
void *ptr;
if (new_size <= dst->capacity)
return;
new_cap = (!dst->capacity) ? new_size : dst->capacity*2;
if (new_size > new_cap)
new_cap = new_size;
ptr = bmalloc(element_size*new_cap);
if (dst->capacity)
memcpy(ptr, dst->array, element_size*dst->capacity);
if (dst->array)
bfree(dst->array);
dst->array = ptr;
dst->capacity = new_cap;
}
static inline void darray_resize(const size_t element_size,
struct darray *dst, const size_t size)
{
int b_clear;
size_t old_num;
if (size == dst->num) {
return;
} else if (size == 0) {
dst->num = 0;
return;
}
b_clear = size > dst->num;
old_num = dst->num;
darray_ensure_capacity(element_size, dst, size);
dst->num = size;
if (b_clear)
memset(darray_item(element_size, dst, old_num), 0,
element_size * (dst->num-old_num));
}
static inline void darray_copy(const size_t element_size, struct darray *dst,
const struct darray *da)
{
assert(element_size == element_size);
if (da->num == 0) {
darray_free(dst);
} else {
darray_resize(element_size, dst, da->num);
memcpy(dst->array, da->array, element_size*da->num);
}
}
static inline void darray_copy_array(const size_t element_size,
struct darray *dst, const void *array, const size_t num)
{
darray_resize(element_size, dst, num);
memcpy(dst->array, array, element_size*dst->num);
}
static inline void darray_move(struct darray *dst, struct darray *src)
{
darray_free(dst);
memcpy(dst, src, sizeof(struct darray));
src->array = NULL;
src->capacity = 0;
src->num = 0;
}
static inline size_t darray_find(const size_t element_size,
const struct darray *da, const void *item, const size_t idx)
{
size_t i;
assert(idx <= da->num);
for (i = idx; i < da->num; i++) {
void *compare = darray_item(element_size, da, i);
if (memcmp(compare, item, element_size) == 0)
return i;
}
return DARRAY_INVALID;
}
static inline size_t darray_push_back(const size_t element_size,
struct darray *dst, const void *item)
{
darray_ensure_capacity(element_size, dst, ++dst->num);
memcpy(darray_end(element_size, dst), item, element_size);
return dst->num-1;
}
static inline void *darray_push_back_new(const size_t element_size,
struct darray *dst)
{
void *last;
darray_ensure_capacity(element_size, dst, ++dst->num);
last = darray_end(element_size, dst);
memset(last, 0, element_size);
return last;
}
static inline size_t darray_push_back_array(const size_t element_size,
struct darray *dst, const void *array, const size_t num)
{
size_t old_num = dst->num;
assert(array != NULL);
assert(num != 0);
darray_resize(element_size, dst, dst->num+num);
memcpy(darray_item(element_size, dst, old_num), array,
element_size*num);
return old_num;
}
static inline size_t darray_push_back_darray(const size_t element_size,
struct darray *dst, const struct darray *da)
{
return darray_push_back_array(element_size, dst, da->array, da->num);
}
static inline void darray_insert(const size_t element_size, struct darray *dst,
const size_t idx, const void *item)
{
void *new_item;
size_t move_count;
assert(idx <= dst->num);
if (idx == dst->num) {
darray_push_back(element_size, dst, item);
return;
}
move_count = dst->num - idx;
darray_ensure_capacity(element_size, dst, ++dst->num);
new_item = darray_item(element_size, dst, idx);
memmove(darray_item(element_size, dst, idx+1), new_item,
move_count*element_size);
memcpy(new_item, item, element_size);
}
static inline void *darray_insert_new(const size_t element_size,
struct darray *dst, const size_t idx)
{
void *item;
size_t move_count;
assert(idx <= dst->num);
if (idx == dst->num)
return darray_push_back_new(element_size, dst);
item = darray_item(element_size, dst, idx);
move_count = dst->num - idx;
darray_ensure_capacity(element_size, dst, ++dst->num);
memmove(darray_item(element_size, dst, idx+1), item,
move_count*element_size);
memset(item, 0, element_size);
return item;
}
static inline void darray_insert_array(const size_t element_size,
struct darray *dst, const size_t idx,
const void *array, const size_t num)
{
size_t old_num;
assert(array != NULL);
assert(num != 0);
assert(idx < dst->num);
old_num = dst->num;
darray_resize(element_size, dst, dst->num+num);
memmove(darray_item(element_size, dst, idx+num),
darray_item(element_size, dst, idx),
element_size*(old_num-idx));
memcpy(darray_item(element_size, dst, idx), array, element_size*num);
}
static inline void darray_insert_darray(const size_t element_size,
struct darray *dst, const size_t idx, const struct darray *da)
{
darray_insert_array(element_size, dst, idx, da->array, da->num);
}
static inline void darray_erase(const size_t element_size, struct darray *dst,
const size_t idx)
{
assert(idx < dst->num);
if (idx >= dst->num)
return;
if (!--dst->num) {
dst->num = 0;
return;
}
memcpy(darray_item(element_size, dst, idx),
darray_item(element_size, dst, idx+1),
element_size*(dst->num-idx));
}
static inline void darray_erase_item(const size_t element_size,
struct darray *dst, const void *item)
{
size_t idx = darray_find(element_size, dst, item, 0);
if (idx != DARRAY_INVALID)
darray_erase(element_size, dst, idx);
}
static inline void darray_erase_range(const size_t element_size,
struct darray *dst, const size_t start, const size_t end)
{
size_t count, move_count;
assert(start <= dst->num);
assert(end <= dst->num);
assert(end > start);
count = end-start;
if (count == 1) {
darray_erase(element_size, dst, start);
return;
} else if (count == dst->num) {
dst->num = 0;
return;
}
move_count = dst->num - end;
if (move_count)
memmove(darray_item(element_size, dst, start),
darray_item(element_size, dst, end),
move_count);
dst->num -= count;
}
static inline void darray_pop_back(const size_t element_size,
struct darray *dst)
{
assert(dst->num != 0);
if (dst->num)
darray_erase(element_size, dst, dst->num-1);
}
static inline void darray_join(const size_t element_size, struct darray *dst,
struct darray *da)
{
assert(element_size == element_size);
darray_push_back_darray(element_size, dst, da);
darray_free(da);
}
static inline void darray_split(const size_t element_size, struct darray *dst1,
struct darray *dst2, const struct darray *da, const size_t idx)
{
struct darray temp;
assert(da->num >= idx);
assert(dst1 != dst2);
darray_init(&temp);
darray_copy(element_size, &temp, da);
darray_free(dst1);
darray_free(dst2);
if (da->num) {
if (idx)
darray_copy_array(element_size, dst1, temp.array,
temp.num);
if (idx < temp.num-1)
darray_copy_array(element_size, dst2,
darray_item(element_size, &temp, idx),
temp.num-idx);
}
darray_free(&temp);
}
static inline void darray_move_item(const size_t element_size,
struct darray *dst, const size_t from, const size_t to)
{
void *temp, *p_from, *p_to;
if (from == to)
return;
temp = bmalloc(element_size);
p_from = darray_item(element_size, dst, from);
p_to = darray_item(element_size, dst, to);
memcpy(temp, p_from, element_size);
if (to < from)
memmove(darray_item(element_size, dst, to+1), p_to,
element_size*(from-to));
else
memcpy(p_from, darray_item(element_size, dst, from+1),
element_size*(to-from));
memcpy(p_to, temp, element_size);
bfree(temp);
}
static inline void darray_swap(const size_t element_size,
struct darray *dst, const size_t a, const size_t b)
{
void *temp, *a_ptr, *b_ptr;
assert(a < dst->num);
assert(b < dst->num);
if (a == b)
return;
temp = bmalloc(element_size);
a_ptr = darray_item(element_size, dst, a);
b_ptr = darray_item(element_size, dst, b);
memcpy(temp, a_ptr, element_size);
memcpy(a_ptr, b_ptr, element_size);
memcpy(b_ptr, temp, element_size);
bfree(temp);
}
/*
* Defines to make dynamic arrays more type-safe.
* Note: Still not 100% type-safe but much better than using darray directly
* Makes it a little easier to use as well.
*
* I did -not- want to use a gigantic macro to generate a crapload of
* typsafe inline functions per type. It just feels like a mess to me.
*/
#define DARRAY(type) \
union { \
struct darray da; \
struct { \
type *array; \
size_t num; \
size_t capacity; \
}; \
}
#define da_init(v) darray_init(&v.da)
#define da_free(v) darray_free(&v.da)
#define da_alloc_size(v) (sizeof(*v.array)*v.num)
#define da_end(v) darray_end(sizeof(*v.array), &v.da)
#define da_reserve(v, capacity) \
darray_reserve(sizeof(*v.array), &v.da, capacity)
#define da_resize(v, size) darray_resize(sizeof(*v.array), &v.da, size)
#define da_copy(dst, src) \
darray_copy(sizeof(*dst.array), &dst.da, &src.da)
#define da_copy_array(dst, src_array, n) \
darray_copy_array(sizeof(*dst.array), &dst.da, src_array, n)
#define da_move(dst, src) darray_move(&dst.da, &src.da)
#define da_find(v, item, idx) \
darray_find(sizeof(*v.array), &v.da, item, idx)
#define da_push_back(v, item) darray_push_back(sizeof(*v.array), &v.da, item)
#define da_push_back_new(v) darray_push_back_new(sizeof(*v.array), &v.da)
#define da_push_back_array(dst, src_array, n) \
darray_push_back_array(sizeof(*dst.array), &dst.da, src_array, n)
#define da_push_back_da(dst, src) \
darray_push_back_darray(sizeof(*dst.array), &dst.da, &src.da)
#define da_insert(v, idx, item) \
darray_insert(sizeof(*v.array), &v.da, idx, item)
#define da_insert_new(v, idx) \
darray_insert_new(sizeof(*v.array), &v.da, idx)
#define da_insert_array(dst, idx, src_array, n) \
darray_insert_array(sizeof(*dst.array), &dst.da, idx, \
src_array, n)
#define da_insert_da(dst, idx, src) \
darray_insert_darray(sizeof(*dst.array), &dst.da, idx, \
&src.da)
#define da_erase(dst, idx) \
darray_erase(sizeof(*dst.array), &dst.da, idx)
#define da_erase_item(dst, item) \
darray_erase_item(sizeof(*dst.array), &dst.da, item)
#define da_erase_range(dst, from, to) \
darray_erase_range(sizeof(*dst.array), &dst.da, from, to)
#define da_pop_back(dst) \
darray_pop_back(sizeof(*dst.array), &dst.da);
#define da_join(dst, src) \
darray_join(sizeof(*dst.array), &dst.da, &src.da)
#define da_split(dst1, dst2, src, idx) \
darray_split(sizeof(*src.array), &dst1.da, &dst2.da, \
&src.da, idx)
#define da_move_item(v, from, to) \
darray_move_item(sizeof(*v.array), &v.da, from, to)
#define da_swap_item(v, idx1, idx2) \
darray_swap(sizeof(v.array), &v.da, idx1, idx2)
#ifdef __cplusplus
}
#endif