mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 12:16:31 +00:00
unified picture_t with api
This commit is contained in:
38
src/device.c
38
src/device.c
@@ -133,28 +133,6 @@ v4l2_std_id device_parse_standard(const char *str) {
|
||||
return STANDARD_UNKNOWN;
|
||||
}
|
||||
|
||||
void device_copy_picture(const struct picture_t *src, struct picture_t *dest) {
|
||||
# define COPY(_field) dest->_field = src->_field
|
||||
|
||||
if (dest->allocated < src->allocated) {
|
||||
A_REALLOC(dest->data, src->allocated);
|
||||
COPY(allocated);
|
||||
}
|
||||
|
||||
memcpy(dest->data, src->data, src->used);
|
||||
|
||||
COPY(used);
|
||||
|
||||
COPY(width);
|
||||
COPY(height);
|
||||
|
||||
COPY(grab_time);
|
||||
COPY(encode_begin_time);
|
||||
COPY(encode_end_time);
|
||||
|
||||
# undef COPY
|
||||
}
|
||||
|
||||
int device_open(struct device_t *dev) {
|
||||
if ((dev->run->fd = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) {
|
||||
LOG_PERROR("Can't open device");
|
||||
@@ -196,9 +174,8 @@ void device_close(struct device_t *dev) {
|
||||
|
||||
if (dev->run->pictures) {
|
||||
LOG_DEBUG("Releasing picture buffers ...");
|
||||
for (unsigned index = 0; index < dev->run->n_buffers && dev->run->pictures[index].data; ++index) {
|
||||
free(dev->run->pictures[index].data);
|
||||
dev->run->pictures[index].data = NULL;
|
||||
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
|
||||
picture_destroy(dev->run->pictures[index]);
|
||||
}
|
||||
free(dev->run->pictures);
|
||||
dev->run->pictures = NULL;
|
||||
@@ -305,7 +282,7 @@ int device_grab_buffer(struct device_t *dev) {
|
||||
|
||||
dev->run->hw_buffers[buf_info.index].used = buf_info.bytesused;
|
||||
memcpy(&dev->run->hw_buffers[buf_info.index].buf_info, &buf_info, sizeof(struct v4l2_buffer));
|
||||
dev->run->pictures[buf_info.index].grab_time = get_now_monotonic();
|
||||
dev->run->pictures[buf_info.index]->grab_time = get_now_monotonic();
|
||||
return buf_info.index;
|
||||
}
|
||||
|
||||
@@ -600,14 +577,15 @@ static int _device_open_queue_buffers(struct device_t *dev) {
|
||||
}
|
||||
|
||||
static void _device_open_alloc_picbufs(struct device_t *dev) {
|
||||
size_t picture_size = picture_get_generous_size(dev->run->width, dev->run->height);
|
||||
|
||||
LOG_DEBUG("Allocating picture buffers ...");
|
||||
A_CALLOC(dev->run->pictures, dev->run->n_buffers);
|
||||
|
||||
dev->run->max_raw_image_size = ((dev->run->width * dev->run->height) << 1) * 2;
|
||||
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
|
||||
LOG_DEBUG("Allocating picture buffer %u sized %zu bytes... ", index, dev->run->max_raw_image_size);
|
||||
A_CALLOC(dev->run->pictures[index].data, dev->run->max_raw_image_size);
|
||||
dev->run->pictures[index].allocated = dev->run->max_raw_image_size;
|
||||
dev->run->pictures[index] = picture_init();
|
||||
LOG_DEBUG("Pre-allocating picture buffer %u sized %zu bytes... ", index, picture_size);
|
||||
picture_realloc_data(dev->run->pictures[index], picture_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
18
src/device.h
18
src/device.h
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "picture.h"
|
||||
|
||||
|
||||
#define VIDEO_MIN_WIDTH 160
|
||||
#define VIDEO_MAX_WIDTH 10240
|
||||
@@ -50,17 +52,6 @@ struct hw_buffer_t {
|
||||
struct v4l2_buffer buf_info;
|
||||
};
|
||||
|
||||
struct picture_t {
|
||||
unsigned char *data;
|
||||
size_t used;
|
||||
size_t allocated;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
long double grab_time;
|
||||
long double encode_begin_time;
|
||||
long double encode_end_time;
|
||||
};
|
||||
|
||||
struct device_runtime_t {
|
||||
int fd;
|
||||
unsigned width;
|
||||
@@ -69,8 +60,7 @@ struct device_runtime_t {
|
||||
unsigned n_buffers;
|
||||
unsigned n_workers;
|
||||
struct hw_buffer_t *hw_buffers;
|
||||
struct picture_t *pictures;
|
||||
size_t max_raw_image_size;
|
||||
struct picture_t **pictures;
|
||||
bool capturing;
|
||||
};
|
||||
|
||||
@@ -120,8 +110,6 @@ void device_destroy(struct device_t *dev);
|
||||
int device_parse_format(const char *str);
|
||||
v4l2_std_id device_parse_standard(const char *str);
|
||||
|
||||
void device_copy_picture(const struct picture_t *src, struct picture_t *dest);
|
||||
|
||||
int device_open(struct device_t *dev);
|
||||
void device_close(struct device_t *dev);
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
|
||||
|
||||
assert(encoder->run->type != ENCODER_TYPE_UNKNOWN);
|
||||
|
||||
dev->run->pictures[buf_index].encode_begin_time = get_now_monotonic();
|
||||
dev->run->pictures[buf_index]->encode_begin_time = get_now_monotonic();
|
||||
|
||||
if (encoder->run->type == ENCODER_TYPE_CPU) {
|
||||
cpu_encoder_compress_buffer(dev, buf_index, encoder->run->quality);
|
||||
@@ -215,10 +215,10 @@ int encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, uns
|
||||
}
|
||||
# endif
|
||||
|
||||
dev->run->pictures[buf_index].encode_end_time = get_now_monotonic();
|
||||
dev->run->pictures[buf_index]->encode_end_time = get_now_monotonic();
|
||||
|
||||
dev->run->pictures[buf_index].width = dev->run->width;
|
||||
dev->run->pictures[buf_index].height = dev->run->height;
|
||||
dev->run->pictures[buf_index]->width = dev->run->width;
|
||||
dev->run->pictures[buf_index]->height = dev->run->height;
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "../../tools.h"
|
||||
#include "../../picture.h"
|
||||
#include "../../device.h"
|
||||
|
||||
|
||||
@@ -43,7 +44,6 @@ struct _jpeg_dest_manager_t {
|
||||
struct jpeg_destination_mgr mgr; // Default manager
|
||||
JOCTET *buffer; // Start of buffer
|
||||
struct picture_t *picture;
|
||||
unsigned char *picture_data_cursor;
|
||||
};
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
|
||||
jpeg.err = jpeg_std_error(&jpeg_error);
|
||||
jpeg_create_compress(&jpeg);
|
||||
|
||||
_jpeg_set_picture(&jpeg, &dev->run->pictures[index]);
|
||||
_jpeg_set_picture(&jpeg, dev->run->pictures[index]);
|
||||
|
||||
jpeg.image_width = dev->run->width;
|
||||
jpeg.image_height = dev->run->height;
|
||||
@@ -108,7 +108,7 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
|
||||
jpeg_finish_compress(&jpeg);
|
||||
jpeg_destroy_compress(&jpeg);
|
||||
|
||||
assert(dev->run->pictures[index].used > 0);
|
||||
assert(dev->run->pictures[index]->used > 0);
|
||||
}
|
||||
|
||||
static void _jpeg_set_picture(j_compress_ptr jpeg, struct picture_t *picture) {
|
||||
@@ -125,7 +125,6 @@ static void _jpeg_set_picture(j_compress_ptr jpeg, struct picture_t *picture) {
|
||||
dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
|
||||
dest->mgr.term_destination = _jpeg_term_destination;
|
||||
dest->picture = picture;
|
||||
dest->picture_data_cursor = picture->data;
|
||||
|
||||
picture->used = 0;
|
||||
}
|
||||
@@ -277,13 +276,8 @@ static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
|
||||
// Called whenever local jpeg buffer fills up
|
||||
|
||||
struct _jpeg_dest_manager_t *dest = (struct _jpeg_dest_manager_t *)jpeg->dest;
|
||||
size_t new_used = dest->picture->used + JPEG_OUTPUT_BUFFER_SIZE;
|
||||
|
||||
assert(new_used <= dest->picture->allocated);
|
||||
|
||||
memcpy(dest->picture_data_cursor, dest->buffer, JPEG_OUTPUT_BUFFER_SIZE);
|
||||
dest->picture_data_cursor += JPEG_OUTPUT_BUFFER_SIZE;
|
||||
dest->picture->used = new_used;
|
||||
picture_append_data(dest->picture, dest->buffer, JPEG_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
dest->mgr.next_output_byte = dest->buffer;
|
||||
dest->mgr.free_in_buffer = JPEG_OUTPUT_BUFFER_SIZE;
|
||||
@@ -293,18 +287,13 @@ static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
|
||||
|
||||
static void _jpeg_term_destination(j_compress_ptr jpeg) {
|
||||
// Called by jpeg_finish_compress after all data has been written.
|
||||
// Usually needs to flush buffer
|
||||
// Usually needs to flush buffer.
|
||||
|
||||
struct _jpeg_dest_manager_t *dest = (struct _jpeg_dest_manager_t *)jpeg->dest;
|
||||
size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
|
||||
size_t new_used = dest->picture->used + final;
|
||||
|
||||
assert(new_used <= dest->picture->allocated);
|
||||
|
||||
// Write any data remaining in the buffer
|
||||
memcpy(dest->picture_data_cursor, dest->buffer, final);
|
||||
dest->picture_data_cursor += final;
|
||||
dest->picture->used = new_used;
|
||||
// Write any data remaining in the buffer.
|
||||
picture_append_data(dest->picture, dest->buffer, final);
|
||||
}
|
||||
|
||||
#undef JPEG_OUTPUT_BUFFER_SIZE
|
||||
|
||||
@@ -36,13 +36,14 @@
|
||||
#include "../../tools.h"
|
||||
#include "../../logging.h"
|
||||
#include "../../xioctl.h"
|
||||
#include "../../picture.h"
|
||||
#include "../../device.h"
|
||||
|
||||
#include "huffman.h"
|
||||
|
||||
|
||||
void _copy_plus_huffman(const struct hw_buffer_t *src, struct picture_t *dest);
|
||||
static bool _is_huffman(const unsigned char *data);
|
||||
static size_t _memcpy_with_huffman(unsigned char *dest, const unsigned char *src, size_t size);
|
||||
|
||||
|
||||
int hw_encoder_prepare(struct device_t *dev, unsigned quality) {
|
||||
@@ -66,15 +67,30 @@ void hw_encoder_compress_buffer(struct device_t *dev, unsigned index) {
|
||||
if (dev->run->format != V4L2_PIX_FMT_MJPEG && dev->run->format != V4L2_PIX_FMT_JPEG) {
|
||||
assert(0 && "Unsupported input format for HW encoder");
|
||||
}
|
||||
_copy_plus_huffman(&dev->run->hw_buffers[index], dev->run->pictures[index]);
|
||||
}
|
||||
|
||||
# define PICTURE(_next) dev->run->pictures[index]._next
|
||||
# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next
|
||||
void _copy_plus_huffman(const struct hw_buffer_t *src, struct picture_t *dest) {
|
||||
if (!_is_huffman(src->data)) {
|
||||
const unsigned char *src_ptr = src->data;
|
||||
const unsigned char *src_end = src->data + src->used;
|
||||
size_t paste;
|
||||
|
||||
assert(PICTURE(allocated) >= HW_BUFFER(used) + sizeof(HUFFMAN_TABLE));
|
||||
PICTURE(used) = _memcpy_with_huffman(PICTURE(data), HW_BUFFER(data), HW_BUFFER(used));
|
||||
while ((((src_ptr[0] << 8) | src_ptr[1]) != 0xFFC0) && (src_ptr < src_end)) {
|
||||
src_ptr += 1;
|
||||
}
|
||||
if (src_ptr >= src_end) {
|
||||
dest->used = 0; // Error
|
||||
return;
|
||||
}
|
||||
paste = src_ptr - src->data;
|
||||
|
||||
# undef HW_BUFFER
|
||||
# undef PICTURE
|
||||
picture_set_data(dest, src->data, paste);
|
||||
picture_append_data(dest, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE));
|
||||
picture_append_data(dest, src_ptr, src->used - paste);
|
||||
} else {
|
||||
picture_set_data(dest, src->data, src->used);
|
||||
}
|
||||
}
|
||||
|
||||
static bool _is_huffman(const unsigned char *data) {
|
||||
@@ -91,27 +107,3 @@ static bool _is_huffman(const unsigned char *data) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t _memcpy_with_huffman(unsigned char *dest, const unsigned char *src, size_t size) {
|
||||
if (!_is_huffman(src)) {
|
||||
const unsigned char *src_ptr = src;
|
||||
const unsigned char *src_end = src + size;
|
||||
size_t paste;
|
||||
|
||||
while ((((src_ptr[0] << 8) | src_ptr[1]) != 0xFFC0) && (src_ptr < src_end)) {
|
||||
src_ptr += 1;
|
||||
}
|
||||
if (src_ptr >= src_end) {
|
||||
return 0;
|
||||
}
|
||||
paste = src_ptr - src;
|
||||
|
||||
memcpy(dest, src, paste);
|
||||
memcpy(dest + paste, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE));
|
||||
memcpy(dest + paste + sizeof(HUFFMAN_TABLE), src_ptr, size - paste);
|
||||
return (size + sizeof(HUFFMAN_TABLE));
|
||||
} else {
|
||||
memcpy(dest, src, size);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include "../../logging.h"
|
||||
#include "../../tools.h"
|
||||
#include "../../picture.h"
|
||||
#include "../../device.h"
|
||||
|
||||
#include "formatters.h"
|
||||
@@ -174,7 +175,6 @@ int omx_encoder_prepare(struct omx_encoder_t *omx, struct device_t *dev, unsigne
|
||||
}
|
||||
|
||||
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, unsigned index) {
|
||||
# define PICTURE(_next) dev->run->pictures[index]._next
|
||||
# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next
|
||||
# define IN(_next) omx->input_buffer->_next
|
||||
# define OUT(_next) omx->output_buffer->_next
|
||||
@@ -188,7 +188,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
|
||||
return -1;
|
||||
}
|
||||
|
||||
PICTURE(used) = 0;
|
||||
dev->run->pictures[index]->used = 0;
|
||||
omx->output_available = false;
|
||||
omx->input_required = true;
|
||||
|
||||
@@ -200,9 +200,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
|
||||
if (omx->output_available) {
|
||||
omx->output_available = false;
|
||||
|
||||
assert(PICTURE(used) + OUT(nFilledLen) <= PICTURE(allocated));
|
||||
memcpy(PICTURE(data) + PICTURE(used), OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen));
|
||||
PICTURE(used) += OUT(nFilledLen);
|
||||
picture_append_data(dev->run->pictures[index], OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen));
|
||||
|
||||
if (OUT(nFlags) & OMX_BUFFERFLAG_ENDOFFRAME) {
|
||||
OUT(nFlags) = 0;
|
||||
@@ -244,7 +242,6 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
|
||||
# undef OUT
|
||||
# undef IN
|
||||
# undef HW_BUFFER
|
||||
# undef PICTURE
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -313,7 +310,7 @@ static int _omx_setup_input(struct omx_encoder_t *omx, struct device_t *dev) {
|
||||
# undef ALIGN_HEIGHT
|
||||
portdef.format.image.bFlagErrorConcealment = OMX_FALSE;
|
||||
portdef.format.image.eCompressionFormat = OMX_IMAGE_CodingUnused;
|
||||
portdef.nBufferSize = dev->run->max_raw_image_size;
|
||||
portdef.nBufferSize = picture_get_generous_size(dev->run->width, dev->run->height);
|
||||
|
||||
# define MAP_FORMAT(_v4l2_format, _omx_format) \
|
||||
case _v4l2_format: { portdef.format.image.eColorFormat = _omx_format; break; }
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
|
||||
#include "../tools.h"
|
||||
#include "../logging.h"
|
||||
#include "../picture.h"
|
||||
#include "../device.h"
|
||||
|
||||
#include "data/blank_jpeg.h"
|
||||
@@ -42,55 +43,44 @@ struct _jpeg_error_manager_t {
|
||||
};
|
||||
|
||||
|
||||
static struct picture_t *_blank_init_internal(void);
|
||||
static struct picture_t *_blank_init_external(const char *path);
|
||||
static struct picture_t *_init_internal(void);
|
||||
static struct picture_t *_init_external(const char *path);
|
||||
|
||||
static int _jpeg_read_geometry(FILE *fp, unsigned *width, unsigned *height);
|
||||
static void _jpeg_error_handler(j_common_ptr jpeg);
|
||||
|
||||
|
||||
struct picture_t *blank_init(const char *path) {
|
||||
struct picture_t *blank_picture_init(const char *path) {
|
||||
struct picture_t *blank = NULL;
|
||||
|
||||
if (path) {
|
||||
blank = _blank_init_external(path);
|
||||
blank = _init_external(path);
|
||||
}
|
||||
|
||||
if (blank) {
|
||||
LOG_INFO("Using external blank placeholder: %s", path);
|
||||
} else {
|
||||
blank = _blank_init_internal();
|
||||
blank = _init_internal();
|
||||
LOG_INFO("Using internal blank placeholder");
|
||||
}
|
||||
return blank;
|
||||
}
|
||||
|
||||
void blank_destroy(struct picture_t *blank) {
|
||||
free(blank->data);
|
||||
free(blank);
|
||||
}
|
||||
|
||||
static struct picture_t *_blank_init_internal(void) {
|
||||
static struct picture_t *_init_internal(void) {
|
||||
struct picture_t *blank;
|
||||
|
||||
A_CALLOC(blank, 1);
|
||||
|
||||
A_CALLOC(blank->data, ARRAY_LEN(BLANK_JPEG_DATA));
|
||||
memcpy(blank->data, BLANK_JPEG_DATA, ARRAY_LEN(BLANK_JPEG_DATA));
|
||||
|
||||
blank->used = ARRAY_LEN(BLANK_JPEG_DATA);
|
||||
blank->allocated = ARRAY_LEN(BLANK_JPEG_DATA);
|
||||
|
||||
blank = picture_init();
|
||||
picture_set_data(blank, BLANK_JPEG_DATA, ARRAY_LEN(BLANK_JPEG_DATA));
|
||||
blank->width = BLANK_JPEG_WIDTH;
|
||||
blank->height = BLANK_JPEG_HEIGHT;
|
||||
|
||||
return blank;
|
||||
}
|
||||
|
||||
static struct picture_t *_blank_init_external(const char *path) {
|
||||
static struct picture_t *_init_external(const char *path) {
|
||||
FILE *fp = NULL;
|
||||
struct picture_t *blank;
|
||||
|
||||
A_CALLOC(blank, 1);
|
||||
blank = picture_init();
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
LOG_PERROR("Can't open blank placeholder '%s'", path);
|
||||
@@ -109,8 +99,7 @@ static struct picture_t *_blank_init_external(const char *path) {
|
||||
# define CHUNK_SIZE (100 * 1024)
|
||||
while (true) {
|
||||
if (blank->used + CHUNK_SIZE >= blank->allocated) {
|
||||
blank->allocated = blank->used + CHUNK_SIZE * 2;
|
||||
A_REALLOC(blank->data, blank->allocated);
|
||||
picture_realloc_data(blank, blank->used + CHUNK_SIZE * 2);
|
||||
}
|
||||
|
||||
size_t readed = fread(blank->data + blank->used, 1, CHUNK_SIZE, fp);
|
||||
@@ -128,8 +117,7 @@ static struct picture_t *_blank_init_external(const char *path) {
|
||||
# undef CHUNK_SIZE
|
||||
|
||||
error:
|
||||
free(blank->data);
|
||||
free(blank);
|
||||
picture_destroy(blank);
|
||||
blank = NULL;
|
||||
|
||||
ok:
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../device.h"
|
||||
#include "../picture.h"
|
||||
|
||||
|
||||
struct picture_t *blank_init(const char *path);
|
||||
void blank_destroy(struct picture_t *blank);
|
||||
struct picture_t *blank_picture_init(const char *path);
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
|
||||
#include "../tools.h"
|
||||
#include "../logging.h"
|
||||
#include "../picture.h"
|
||||
#include "../encoder.h"
|
||||
#include "../stream.h"
|
||||
#ifdef WITH_GPIO
|
||||
@@ -91,6 +92,7 @@ struct http_server_t *http_server_init(struct stream_t *stream) {
|
||||
struct exposed_t *exposed;
|
||||
|
||||
A_CALLOC(exposed, 1);
|
||||
exposed->picture = picture_init();
|
||||
|
||||
A_CALLOC(run, 1);
|
||||
run->stream = stream;
|
||||
@@ -143,10 +145,10 @@ void http_server_destroy(struct http_server_t *server) {
|
||||
}
|
||||
|
||||
if (server->run->blank) {
|
||||
blank_destroy(server->run->blank);
|
||||
picture_destroy(server->run->blank);
|
||||
}
|
||||
|
||||
free(server->run->exposed->picture.data);
|
||||
picture_destroy(server->run->exposed->picture);
|
||||
free(server->run->exposed);
|
||||
free(server->run);
|
||||
free(server);
|
||||
@@ -165,7 +167,7 @@ int http_server_listen(struct http_server_t *server) {
|
||||
}
|
||||
|
||||
server->run->drop_same_frames_blank = max_u(server->drop_same_frames, server->run->drop_same_frames_blank);
|
||||
server->run->blank = blank_init(server->blank_path);
|
||||
server->run->blank = blank_picture_init(server->blank_path);
|
||||
_expose_blank_picture(server);
|
||||
|
||||
{
|
||||
@@ -436,8 +438,8 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
" \"stream\": {\"queued_fps\": %u, \"clients\": %u, \"clients_stat\": {",
|
||||
encoder_type_to_string(encoder_run_type),
|
||||
encoder_run_quality,
|
||||
(server->fake_width ? server->fake_width : server->run->exposed->picture.width),
|
||||
(server->fake_height ? server->fake_height : server->run->exposed->picture.height),
|
||||
(server->fake_width ? server->fake_width : server->run->exposed->picture->width),
|
||||
(server->fake_height ? server->fake_height : server->run->exposed->picture->height),
|
||||
bool_to_string(server->run->exposed->online),
|
||||
server->run->stream->dev->desired_fps,
|
||||
server->run->exposed->captured_fps,
|
||||
@@ -476,7 +478,7 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
|
||||
# define EXPOSED(_next) server->run->exposed->_next
|
||||
|
||||
assert((buf = evbuffer_new()));
|
||||
assert(!evbuffer_add(buf, (const void *)EXPOSED(picture.data), EXPOSED(picture.used)));
|
||||
assert(!evbuffer_add(buf, (const void *)EXPOSED(picture->data), EXPOSED(picture->used)));
|
||||
|
||||
ADD_HEADER("Access-Control-Allow-Origin:", "*");
|
||||
ADD_HEADER("Cache-Control", "no-store, no-cache, must-revalidate, proxy-revalidate, pre-check=0, post-check=0, max-age=0");
|
||||
@@ -497,11 +499,11 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
|
||||
|
||||
ADD_HEADER("X-UStreamer-Online", bool_to_string(EXPOSED(online)));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Dropped", EXPOSED(dropped));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Width", EXPOSED(picture.width));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Height", EXPOSED(picture.height));
|
||||
ADD_TIME_HEADER("X-UStreamer-Grab-Time", EXPOSED(picture.grab_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Time", EXPOSED(picture.encode_begin_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture.encode_end_time));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Width", EXPOSED(picture->width));
|
||||
ADD_UNSIGNED_HEADER("X-UStreamer-Height", EXPOSED(picture->height));
|
||||
ADD_TIME_HEADER("X-UStreamer-Grab-Time", EXPOSED(picture->grab_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-Begin-Time", EXPOSED(picture->encode_begin_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Encode-End-Time", EXPOSED(picture->encode_end_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-Begin-Time", EXPOSED(expose_begin_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-Cmp-Time", EXPOSED(expose_cmp_time));
|
||||
ADD_TIME_HEADER("X-UStreamer-Expose-End-Time", EXPOSED(expose_end_time));
|
||||
@@ -663,7 +665,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
"Content-Length: %zu" RN
|
||||
"X-Timestamp: %.06Lf" RN
|
||||
"%s",
|
||||
EXPOSED(picture.used),
|
||||
EXPOSED(picture->used),
|
||||
get_now_real(),
|
||||
(client->extra_headers ? "" : RN)
|
||||
));
|
||||
@@ -684,12 +686,12 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
RN,
|
||||
bool_to_string(EXPOSED(online)),
|
||||
EXPOSED(dropped),
|
||||
EXPOSED(picture.width),
|
||||
EXPOSED(picture.height),
|
||||
EXPOSED(picture->width),
|
||||
EXPOSED(picture->height),
|
||||
client->fps,
|
||||
EXPOSED(picture.grab_time),
|
||||
EXPOSED(picture.encode_begin_time),
|
||||
EXPOSED(picture.encode_end_time),
|
||||
EXPOSED(picture->grab_time),
|
||||
EXPOSED(picture->encode_begin_time),
|
||||
EXPOSED(picture->encode_end_time),
|
||||
EXPOSED(expose_begin_time),
|
||||
EXPOSED(expose_cmp_time),
|
||||
EXPOSED(expose_end_time),
|
||||
@@ -698,7 +700,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
}
|
||||
}
|
||||
|
||||
assert(!evbuffer_add(buf, (void *)EXPOSED(picture.data), EXPOSED(picture.used)));
|
||||
assert(!evbuffer_add(buf, (void *)EXPOSED(picture->data), EXPOSED(picture->used)));
|
||||
assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN));
|
||||
|
||||
if (client->advance_headers) {
|
||||
@@ -853,8 +855,8 @@ static bool _expose_new_picture_unsafe(struct http_server_t *server) {
|
||||
if (
|
||||
EXPOSED(online)
|
||||
&& EXPOSED(dropped) < server->drop_same_frames
|
||||
&& EXPOSED(picture.used) == STREAM(picture.used)
|
||||
&& !memcmp(EXPOSED(picture.data), STREAM(picture.data), STREAM(picture.used))
|
||||
&& EXPOSED(picture->used) == STREAM(picture->used)
|
||||
&& !memcmp(EXPOSED(picture->data), STREAM(picture->data), STREAM(picture->used))
|
||||
) {
|
||||
EXPOSED(expose_cmp_time) = get_now_monotonic();
|
||||
EXPOSED(expose_end_time) = EXPOSED(expose_cmp_time);
|
||||
@@ -869,7 +871,7 @@ static bool _expose_new_picture_unsafe(struct http_server_t *server) {
|
||||
}
|
||||
}
|
||||
|
||||
device_copy_picture(&STREAM(picture), &EXPOSED(picture));
|
||||
picture_copy(STREAM(picture), EXPOSED(picture));
|
||||
|
||||
EXPOSED(online) = true;
|
||||
EXPOSED(dropped) = 0;
|
||||
@@ -890,9 +892,8 @@ static bool _expose_blank_picture(struct http_server_t *server) {
|
||||
EXPOSED(expose_begin_time) = get_now_monotonic();
|
||||
EXPOSED(expose_cmp_time) = EXPOSED(expose_begin_time);
|
||||
|
||||
if (EXPOSED(online) || EXPOSED(picture.used) == 0) {
|
||||
device_copy_picture(server->run->blank, &EXPOSED(picture));
|
||||
|
||||
if (EXPOSED(online) || EXPOSED(picture->used) == 0) {
|
||||
picture_copy(server->run->blank, EXPOSED(picture));
|
||||
EXPOSED(captured_fps) = 0;
|
||||
EXPOSED(online) = false;
|
||||
goto updated;
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <event2/util.h>
|
||||
|
||||
#include "../tools.h"
|
||||
#include "../picture.h"
|
||||
#include "../stream.h"
|
||||
|
||||
#include "blank.h"
|
||||
@@ -58,7 +59,7 @@ struct stream_client_t {
|
||||
};
|
||||
|
||||
struct exposed_t {
|
||||
struct picture_t picture;
|
||||
struct picture_t *picture;
|
||||
unsigned captured_fps;
|
||||
unsigned queued_fps;
|
||||
bool online;
|
||||
|
||||
91
src/picture.c
Normal file
91
src/picture.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*****************************************************************************
|
||||
# #
|
||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||
# #
|
||||
# Copyright (C) 2018 Maxim Devaev <mdevaev@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 3 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 <https://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "picture.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tools.h"
|
||||
#include "logging.h"
|
||||
|
||||
|
||||
struct picture_t *picture_init(void) {
|
||||
struct picture_t *picture;
|
||||
|
||||
A_CALLOC(picture, 1);
|
||||
return picture;
|
||||
}
|
||||
|
||||
void picture_destroy(struct picture_t *picture) {
|
||||
if (picture->data) {
|
||||
free(picture->data);
|
||||
}
|
||||
free(picture);
|
||||
}
|
||||
|
||||
size_t picture_get_generous_size(unsigned width, unsigned height) {
|
||||
return ((width * height) << 1) * 2;
|
||||
}
|
||||
|
||||
void picture_realloc_data(struct picture_t *picture, size_t size) {
|
||||
if (picture->allocated < size) {
|
||||
LOG_DEBUG("Increasing picture 0x%p buffer: %zu -> %zu (+%zu)",
|
||||
picture, picture->allocated, size, size - picture->allocated);
|
||||
A_REALLOC(picture->data, size);
|
||||
picture->allocated = size;
|
||||
}
|
||||
}
|
||||
|
||||
void picture_set_data(struct picture_t *picture, const unsigned char *data, size_t size) {
|
||||
picture_realloc_data(picture, size);
|
||||
memcpy(picture->data, data, size);
|
||||
picture->used = size;
|
||||
}
|
||||
|
||||
void picture_append_data(struct picture_t *picture, const unsigned char *data, size_t size) {
|
||||
size_t new_used = picture->used + size;
|
||||
|
||||
picture_realloc_data(picture, new_used);
|
||||
memcpy(picture->data + picture->used, data, size);
|
||||
picture->used = new_used;
|
||||
}
|
||||
|
||||
void picture_copy(const struct picture_t *src, struct picture_t *dest) {
|
||||
assert(src->allocated);
|
||||
|
||||
picture_set_data(dest, src->data, src->allocated);
|
||||
|
||||
# define COPY(_field) dest->_field = src->_field
|
||||
|
||||
COPY(used);
|
||||
|
||||
COPY(width);
|
||||
COPY(height);
|
||||
|
||||
COPY(grab_time);
|
||||
COPY(encode_begin_time);
|
||||
COPY(encode_end_time);
|
||||
|
||||
# undef COPY
|
||||
}
|
||||
49
src/picture.h
Normal file
49
src/picture.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*****************************************************************************
|
||||
# #
|
||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||
# #
|
||||
# Copyright (C) 2018 Maxim Devaev <mdevaev@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 3 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 <https://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
struct picture_t {
|
||||
unsigned char *data;
|
||||
size_t used;
|
||||
size_t allocated;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
long double grab_time;
|
||||
long double encode_begin_time;
|
||||
long double encode_end_time;
|
||||
};
|
||||
|
||||
|
||||
struct picture_t *picture_init(void);
|
||||
void picture_destroy(struct picture_t *picture);
|
||||
|
||||
size_t picture_get_generous_size(unsigned width, unsigned height);
|
||||
|
||||
void picture_realloc_data(struct picture_t *picture, size_t size);
|
||||
void picture_set_data(struct picture_t *picture, const unsigned char *data, size_t size);
|
||||
void picture_append_data(struct picture_t *picture, const unsigned char *data, size_t size);
|
||||
|
||||
void picture_copy(const struct picture_t *src, struct picture_t *dest);
|
||||
17
src/stream.c
17
src/stream.c
@@ -36,6 +36,7 @@
|
||||
#include "tools.h"
|
||||
#include "logging.h"
|
||||
#include "xioctl.h"
|
||||
#include "picture.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#ifdef WITH_GPIO
|
||||
@@ -111,6 +112,7 @@ struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) {
|
||||
atomic_init(&proc->slowdown, false);
|
||||
|
||||
A_CALLOC(stream, 1);
|
||||
stream->picture = picture_init();
|
||||
stream->dev = dev;
|
||||
stream->encoder = encoder;
|
||||
atomic_init(&stream->updated, false);
|
||||
@@ -121,9 +123,7 @@ struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) {
|
||||
|
||||
void stream_destroy(struct stream_t *stream) {
|
||||
A_MUTEX_DESTROY(&stream->mutex);
|
||||
if (stream->picture.data) {
|
||||
free(stream->picture.data);
|
||||
}
|
||||
picture_destroy(stream->picture);
|
||||
free(stream->proc);
|
||||
free(stream);
|
||||
}
|
||||
@@ -144,6 +144,9 @@ void stream_loop(struct stream_t *stream) {
|
||||
|
||||
LOG_INFO("Capturing ...");
|
||||
|
||||
LOG_DEBUG("Pre-allocating memory for stream picture ...");
|
||||
picture_realloc_data(stream->picture, picture_get_generous_size(stream->dev->run->width, stream->dev->run->height));
|
||||
|
||||
while (!atomic_load(&stream->proc->stop)) {
|
||||
struct _worker_t *ready_worker;
|
||||
|
||||
@@ -335,19 +338,15 @@ static struct _workers_pool_t *_stream_init(struct stream_t *stream) {
|
||||
}
|
||||
|
||||
static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index, unsigned captured_fps) {
|
||||
# define PICTURE(_next) stream->dev->run->pictures[buf_index]._next
|
||||
|
||||
A_MUTEX_LOCK(&stream->mutex);
|
||||
|
||||
device_copy_picture(&stream->dev->run->pictures[buf_index], &stream->picture);
|
||||
picture_copy(stream->dev->run->pictures[buf_index], stream->picture);
|
||||
|
||||
stream->online = true;
|
||||
stream->captured_fps = captured_fps;
|
||||
atomic_store(&stream->updated, true);
|
||||
|
||||
A_MUTEX_UNLOCK(&stream->mutex);
|
||||
|
||||
# undef PICTURE
|
||||
}
|
||||
|
||||
static struct _workers_pool_t *_workers_pool_init(struct stream_t *stream) {
|
||||
@@ -443,7 +442,7 @@ static void *_worker_thread(void *v_worker) {
|
||||
A_MUTEX_UNLOCK(&worker->has_job_mutex);
|
||||
|
||||
if (!atomic_load(worker->workers_stop)) {
|
||||
# define PICTURE(_next) worker->dev->run->pictures[worker->buf_index]._next
|
||||
# define PICTURE(_next) worker->dev->run->pictures[worker->buf_index]->_next
|
||||
|
||||
LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", worker->number, worker->buf_index);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "picture.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
|
||||
@@ -34,7 +35,7 @@ struct process_t {
|
||||
};
|
||||
|
||||
struct stream_t {
|
||||
struct picture_t picture;
|
||||
struct picture_t *picture;
|
||||
bool online;
|
||||
unsigned captured_fps;
|
||||
atomic_bool updated;
|
||||
|
||||
Reference in New Issue
Block a user