mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-03-03 06:11:42 +00:00
refactoring
This commit is contained in:
@@ -25,7 +25,6 @@
|
||||
|
||||
frame_s *frame_init(const char *role) {
|
||||
frame_s *frame;
|
||||
|
||||
A_CALLOC(frame, 1);
|
||||
frame->role = role;
|
||||
frame->managed = true;
|
||||
@@ -60,9 +59,7 @@ void frame_set_data(frame_s *frame, const uint8_t *data, size_t size) {
|
||||
|
||||
void frame_append_data(frame_s *frame, const uint8_t *data, size_t size) {
|
||||
assert(frame->managed);
|
||||
|
||||
size_t new_used = frame->used + size;
|
||||
|
||||
frame_realloc_data(frame, new_used);
|
||||
memcpy(frame->data + frame->used, data, size);
|
||||
frame->used = new_used;
|
||||
|
||||
@@ -98,9 +98,7 @@ INLINE long double get_now_real(void) {
|
||||
}
|
||||
|
||||
INLINE unsigned get_cores_available(void) {
|
||||
long cores_sysconf;
|
||||
|
||||
cores_sysconf = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
long cores_sysconf = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
cores_sysconf = (cores_sysconf < 0 ? 0 : cores_sysconf);
|
||||
return max_u(min_u(cores_sysconf, 4), 1);
|
||||
}
|
||||
|
||||
@@ -40,11 +40,10 @@ static const char *_mmal_error_to_string(MMAL_STATUS_T error);
|
||||
|
||||
h264_encoder_s *h264_encoder_init(void) {
|
||||
h264_encoder_runtime_s *run;
|
||||
h264_encoder_s *encoder;
|
||||
|
||||
A_CALLOC(run, 1);
|
||||
run->tmp = frame_init("h264_tmp");
|
||||
|
||||
h264_encoder_s *encoder;
|
||||
A_CALLOC(encoder, 1);
|
||||
encoder->gop = 60;
|
||||
encoder->bps = 5000 * 1000; // Kbps * 1000
|
||||
@@ -280,17 +279,13 @@ static void _h264_encoder_cleanup(h264_encoder_s *encoder) {
|
||||
}
|
||||
|
||||
static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *src, frame_s *dest, bool force_key) {
|
||||
MMAL_STATUS_T error;
|
||||
MMAL_BUFFER_HEADER_T *out = NULL;
|
||||
MMAL_BUFFER_HEADER_T *in = NULL;
|
||||
bool eos = false;
|
||||
bool sent = false;
|
||||
|
||||
assert(src->used > 0);
|
||||
assert(src->width == encoder->width);
|
||||
assert(src->height == encoder->height);
|
||||
assert(src->format == encoder->format);
|
||||
|
||||
MMAL_STATUS_T error;
|
||||
|
||||
LOG_DEBUG("Compressing new H264 frame; force_key=%d ...", force_key);
|
||||
|
||||
frame_copy_meta(src, dest);
|
||||
@@ -309,6 +304,11 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
|
||||
}
|
||||
}
|
||||
|
||||
MMAL_BUFFER_HEADER_T *out = NULL;
|
||||
MMAL_BUFFER_HEADER_T *in = NULL;
|
||||
bool eos = false;
|
||||
bool sent = false;
|
||||
|
||||
while (!eos) {
|
||||
out = NULL;
|
||||
while (mmal_wrapper_buffer_get_empty(RUN(output_port), &out, 0) == MMAL_SUCCESS) {
|
||||
|
||||
@@ -33,20 +33,19 @@ static void _jpeg_error_handler(j_common_ptr jpeg);
|
||||
|
||||
|
||||
int unjpeg(const frame_s *src, frame_s *dest) {
|
||||
struct jpeg_decompress_struct jpeg;
|
||||
_jpeg_error_manager_s jpeg_error;
|
||||
JSAMPARRAY scanlines;
|
||||
unsigned row_stride;
|
||||
volatile int retval = 0;
|
||||
|
||||
struct jpeg_decompress_struct jpeg;
|
||||
jpeg_create_decompress(&jpeg);
|
||||
|
||||
frame_realloc_data(dest, ((src->width * src->height) << 1) * 2);
|
||||
frame_copy_meta(src, dest);
|
||||
dest->format = V4L2_PIX_FMT_RGB24;
|
||||
dest->used = 0;
|
||||
|
||||
jpeg_create_decompress(&jpeg);
|
||||
|
||||
// https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg
|
||||
|
||||
_jpeg_error_manager_s jpeg_error;
|
||||
jpeg.err = jpeg_std_error((struct jpeg_error_mgr *)&jpeg_error);
|
||||
jpeg_error.mgr.error_exit = _jpeg_error_handler;
|
||||
if (setjmp(jpeg_error.jmp) < 0) {
|
||||
@@ -59,7 +58,9 @@ int unjpeg(const frame_s *src, frame_s *dest) {
|
||||
jpeg.out_color_space = JCS_RGB;
|
||||
|
||||
jpeg_start_decompress(&jpeg);
|
||||
row_stride = jpeg.output_width * jpeg.output_components;
|
||||
const unsigned row_stride = jpeg.output_width * jpeg.output_components;
|
||||
|
||||
JSAMPARRAY scanlines;
|
||||
scanlines = (*jpeg.mem->alloc_sarray)((j_common_ptr) &jpeg, JPOOL_IMAGE, row_stride, 1);
|
||||
|
||||
while (jpeg.output_scanline < jpeg.output_height) {
|
||||
|
||||
@@ -29,8 +29,6 @@ static int _flock_timedwait_monotonic(int fd, long double timeout);
|
||||
|
||||
memsink_s *memsink_open(const char *role, const char *name, bool server, mode_t mode, bool rm, unsigned timeout) {
|
||||
memsink_s *memsink;
|
||||
int flags = (server ? O_RDWR | O_CREAT : O_RDWR);
|
||||
|
||||
A_CALLOC(memsink, 1);
|
||||
memsink->role = role;
|
||||
memsink->server = server;
|
||||
@@ -48,6 +46,7 @@ memsink_s *memsink_open(const char *role, const char *name, bool server, mode_t
|
||||
|
||||
LOG_INFO("Using %s sink: %s.{mem,sig}", role, name);
|
||||
|
||||
const int flags = (server ? O_RDWR | O_CREAT : O_RDWR);
|
||||
# define OPEN_SIGNAL { \
|
||||
if ((memsink->sig_sem = sem_open(memsink->sig_name, flags, mode, 0)) == SEM_FAILED) { \
|
||||
LOG_PERROR("Can't open %s sink signal semaphore", role); \
|
||||
@@ -130,11 +129,11 @@ void memsink_close(memsink_s *memsink) {
|
||||
free(memsink);
|
||||
}
|
||||
|
||||
int memsink_server_put(memsink_s *memsink, frame_s *frame) {
|
||||
long double now = get_now_monotonic();
|
||||
|
||||
int memsink_server_put(memsink_s *memsink, const frame_s *frame) {
|
||||
assert(memsink->server);
|
||||
|
||||
const long double now = get_now_monotonic();
|
||||
|
||||
if (frame->used > MEMSINK_MAX_DATA) {
|
||||
LOG_ERROR("%s sink: Can't put frame: is too big (%zu > %zu)",
|
||||
memsink->role, frame->used, MEMSINK_MAX_DATA);
|
||||
|
||||
@@ -76,5 +76,5 @@ typedef struct {
|
||||
memsink_s *memsink_open(const char *role, const char *name, bool server, mode_t mode, bool rm, unsigned timeout);
|
||||
void memsink_close(memsink_s *memsink);
|
||||
|
||||
int memsink_server_put(memsink_s *memsink, frame_s *frame);
|
||||
int memsink_server_put(memsink_s *memsink, const frame_s *frame);
|
||||
int memsink_client_get(memsink_s *memsink, frame_s *frame);
|
||||
|
||||
@@ -53,9 +53,7 @@ frame_s *blank_frame_init(const char *path) {
|
||||
}
|
||||
|
||||
static frame_s *_init_internal(void) {
|
||||
frame_s *blank;
|
||||
|
||||
blank = frame_init("blank_internal");
|
||||
frame_s *blank = frame_init("blank_internal");
|
||||
frame_set_data(blank, BLANK_JPEG_DATA, BLANK_JPEG_DATA_SIZE);
|
||||
blank->width = BLANK_JPEG_WIDTH;
|
||||
blank->height = BLANK_JPEG_HEIGHT;
|
||||
@@ -65,9 +63,8 @@ static frame_s *_init_internal(void) {
|
||||
|
||||
static frame_s *_init_external(const char *path) {
|
||||
FILE *fp = NULL;
|
||||
frame_s *blank;
|
||||
|
||||
blank = frame_init("blank_external");
|
||||
frame_s *blank = frame_init("blank_external");
|
||||
blank->format = V4L2_PIX_FMT_JPEG;
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
@@ -118,11 +115,10 @@ static frame_s *_init_external(const char *path) {
|
||||
|
||||
static int _jpeg_read_geometry(FILE *fp, unsigned *width, unsigned *height) {
|
||||
struct jpeg_decompress_struct jpeg;
|
||||
_jpeg_error_manager_s jpeg_error;
|
||||
|
||||
jpeg_create_decompress(&jpeg);
|
||||
|
||||
// https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg
|
||||
_jpeg_error_manager_s jpeg_error;
|
||||
jpeg.err = jpeg_std_error((struct jpeg_error_mgr *)&jpeg_error);
|
||||
jpeg_error.mgr.error_exit = _jpeg_error_handler;
|
||||
if (setjmp(jpeg_error.jmp) < 0) {
|
||||
|
||||
@@ -84,11 +84,10 @@ static const char *_io_method_to_string_supported(enum v4l2_memory io_method);
|
||||
|
||||
device_s *device_init(void) {
|
||||
device_runtime_s *run;
|
||||
device_s *dev;
|
||||
|
||||
A_CALLOC(run, 1);
|
||||
run->fd = -1;
|
||||
|
||||
device_s *dev;
|
||||
A_CALLOC(dev, 1);
|
||||
dev->path = "/dev/video0";
|
||||
dev->width = 640;
|
||||
@@ -226,7 +225,6 @@ int device_switch_capturing(device_s *dev, bool enable) {
|
||||
}
|
||||
|
||||
int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_error) {
|
||||
struct timeval timeout;
|
||||
int retval;
|
||||
|
||||
# define INIT_FD_SET(_set) \
|
||||
@@ -238,6 +236,7 @@ int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_erro
|
||||
|
||||
# undef INIT_FD_SET
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = dev->timeout;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
@@ -273,7 +272,6 @@ int device_select(device_s *dev, bool *has_read, bool *has_write, bool *has_erro
|
||||
|
||||
int device_grab_buffer(device_s *dev) {
|
||||
struct v4l2_buffer buf_info;
|
||||
|
||||
MEMSET_ZERO(buf_info);
|
||||
buf_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf_info.memory = dev->io_method;
|
||||
@@ -371,8 +369,6 @@ int device_consume_event(device_s *dev) {
|
||||
|
||||
static int _device_open_check_cap(device_s *dev) {
|
||||
struct v4l2_capability cap;
|
||||
int input = dev->input; // Needs pointer to int for ioctl()
|
||||
|
||||
MEMSET_ZERO(cap);
|
||||
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QUERYCAP) ...");
|
||||
@@ -391,6 +387,7 @@ static int _device_open_check_cap(device_s *dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int input = dev->input; // Needs a pointer to int for ioctl()
|
||||
LOG_INFO("Using input channel: %d", input);
|
||||
if (xioctl(RUN(fd), VIDIOC_S_INPUT, &input) < 0) {
|
||||
LOG_ERROR("Can't set input channel");
|
||||
@@ -434,7 +431,6 @@ static int _device_open_dv_timings(device_s *dev) {
|
||||
|
||||
static int _device_apply_dv_timings(device_s *dev) {
|
||||
struct v4l2_dv_timings dv;
|
||||
|
||||
MEMSET_ZERO(dv);
|
||||
|
||||
LOG_DEBUG("Calling ioctl(VIDIOC_QUERY_DV_TIMINGS) ...");
|
||||
@@ -467,7 +463,6 @@ static int _device_apply_dv_timings(device_s *dev) {
|
||||
|
||||
static int _device_open_format(device_s *dev) {
|
||||
struct v4l2_format fmt;
|
||||
|
||||
MEMSET_ZERO(fmt);
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.width = RUN(width);
|
||||
@@ -518,10 +513,9 @@ static int _device_open_format(device_s *dev) {
|
||||
}
|
||||
|
||||
static void _device_open_hw_fps(device_s *dev) {
|
||||
struct v4l2_streamparm setfps;
|
||||
|
||||
RUN(hw_fps) = 0;
|
||||
|
||||
struct v4l2_streamparm setfps;
|
||||
MEMSET_ZERO(setfps);
|
||||
setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
@@ -584,7 +578,6 @@ static int _device_open_io_method(device_s *dev) {
|
||||
|
||||
static int _device_open_io_method_mmap(device_s *dev) {
|
||||
struct v4l2_requestbuffers req;
|
||||
|
||||
MEMSET_ZERO(req);
|
||||
req.count = dev->n_buffers;
|
||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
@@ -608,7 +601,6 @@ static int _device_open_io_method_mmap(device_s *dev) {
|
||||
A_CALLOC(RUN(hw_buffers), req.count);
|
||||
for (RUN(n_buffers) = 0; RUN(n_buffers) < req.count; ++RUN(n_buffers)) {
|
||||
struct v4l2_buffer buf_info;
|
||||
|
||||
MEMSET_ZERO(buf_info);
|
||||
buf_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf_info.memory = V4L2_MEMORY_MMAP;
|
||||
@@ -645,9 +637,6 @@ static int _device_open_io_method_mmap(device_s *dev) {
|
||||
|
||||
static int _device_open_io_method_userptr(device_s *dev) {
|
||||
struct v4l2_requestbuffers req;
|
||||
unsigned page_size = getpagesize();
|
||||
unsigned buf_size = align_size(RUN(raw_size), page_size);
|
||||
|
||||
MEMSET_ZERO(req);
|
||||
req.count = dev->n_buffers;
|
||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
@@ -669,13 +658,15 @@ static int _device_open_io_method_userptr(device_s *dev) {
|
||||
LOG_DEBUG("Allocating device buffers ...");
|
||||
|
||||
A_CALLOC(RUN(hw_buffers), req.count);
|
||||
|
||||
const unsigned page_size = getpagesize();
|
||||
const unsigned buf_size = align_size(RUN(raw_size), page_size);
|
||||
|
||||
for (RUN(n_buffers) = 0; RUN(n_buffers) < req.count; ++RUN(n_buffers)) {
|
||||
# define HW(_next) RUN(hw_buffers)[RUN(n_buffers)]._next
|
||||
|
||||
assert(HW(raw.data) = aligned_alloc(page_size, buf_size));
|
||||
memset(HW(raw.data), 0, buf_size);
|
||||
HW(raw.allocated) = buf_size;
|
||||
|
||||
# undef HW
|
||||
}
|
||||
return 0;
|
||||
@@ -684,7 +675,6 @@ static int _device_open_io_method_userptr(device_s *dev) {
|
||||
static int _device_open_queue_buffers(device_s *dev) {
|
||||
for (unsigned index = 0; index < RUN(n_buffers); ++index) {
|
||||
struct v4l2_buffer buf_info;
|
||||
|
||||
MEMSET_ZERO(buf_info);
|
||||
buf_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf_info.memory = dev->io_method;
|
||||
@@ -795,8 +785,6 @@ static void _device_set_control(
|
||||
device_s *dev, struct v4l2_queryctrl *query,
|
||||
const char *name, unsigned cid, int value, bool quiet) {
|
||||
|
||||
struct v4l2_control ctl;
|
||||
|
||||
if (value < query->minimum || value > query->maximum || value % query->step != 0) {
|
||||
if (!quiet) {
|
||||
LOG_ERROR("Invalid value %d of control %s: min=%d, max=%d, default=%d, step=%u",
|
||||
@@ -805,6 +793,7 @@ static void _device_set_control(
|
||||
return;
|
||||
}
|
||||
|
||||
struct v4l2_control ctl;
|
||||
MEMSET_ZERO(ctl);
|
||||
ctl.id = cid;
|
||||
ctl.value = value;
|
||||
|
||||
@@ -44,13 +44,12 @@ static const struct {
|
||||
|
||||
encoder_s *encoder_init(void) {
|
||||
encoder_runtime_s *run;
|
||||
encoder_s *encoder;
|
||||
|
||||
A_CALLOC(run, 1);
|
||||
run->type = ENCODER_TYPE_CPU;
|
||||
run->quality = 80;
|
||||
A_MUTEX_INIT(&run->mutex);
|
||||
|
||||
encoder_s *encoder;
|
||||
A_CALLOC(encoder, 1);
|
||||
encoder->type = run->type;
|
||||
encoder->quality = run->quality;
|
||||
|
||||
@@ -37,21 +37,10 @@ typedef struct {
|
||||
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame);
|
||||
|
||||
static void _jpeg_write_scanlines_yuyv(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height);
|
||||
|
||||
static void _jpeg_write_scanlines_uyvy(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height);
|
||||
|
||||
static void _jpeg_write_scanlines_rgb565(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height);
|
||||
|
||||
static void _jpeg_write_scanlines_rgb24(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height);
|
||||
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame);
|
||||
|
||||
static void _jpeg_init_destination(j_compress_ptr jpeg);
|
||||
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
|
||||
@@ -80,7 +69,7 @@ void cpu_encoder_compress(frame_s *src, frame_s *dest, unsigned quality) {
|
||||
jpeg_start_compress(&jpeg, TRUE);
|
||||
|
||||
# define WRITE_SCANLINES(_format, _func) \
|
||||
case _format: { _func(&jpeg, src->data, src->width, src->height); break; }
|
||||
case _format: { _func(&jpeg, src); break; }
|
||||
|
||||
switch (src->format) {
|
||||
// https://www.fourcc.org/yuv.php
|
||||
@@ -100,15 +89,13 @@ void cpu_encoder_compress(frame_s *src, frame_s *dest, unsigned quality) {
|
||||
}
|
||||
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
|
||||
_jpeg_dest_manager_s *dest;
|
||||
|
||||
if (jpeg->dest == NULL) {
|
||||
assert((jpeg->dest = (struct jpeg_destination_mgr *)(*jpeg->mem->alloc_small)(
|
||||
(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
|
||||
)));
|
||||
}
|
||||
|
||||
dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
_jpeg_dest_manager_s *dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
dest->mgr.init_destination = _jpeg_init_destination;
|
||||
dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
|
||||
dest->mgr.term_destination = _jpeg_term_destination;
|
||||
@@ -122,20 +109,17 @@ static void _jpeg_set_dest_frame(j_compress_ptr jpeg, frame_s *frame) {
|
||||
#define YUV_B(_y, _u, _) (((_y) + (454 * (_u))) >> 8)
|
||||
#define NORM_COMPONENT(_x) (((_x) > 255) ? 255 : (((_x) < 0) ? 0 : (_x)))
|
||||
|
||||
static void _jpeg_write_scanlines_yuyv(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height) {
|
||||
|
||||
static void _jpeg_write_scanlines_yuyv(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
uint8_t *line_buffer;
|
||||
JSAMPROW scanlines[1];
|
||||
A_CALLOC(line_buffer, frame->width * 3);
|
||||
|
||||
const uint8_t *data = frame->data;
|
||||
unsigned z = 0;
|
||||
|
||||
A_CALLOC(line_buffer, width * 3);
|
||||
|
||||
while (jpeg->next_scanline < height) {
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
uint8_t *ptr = line_buffer;
|
||||
|
||||
for (unsigned x = 0; x < width; ++x) {
|
||||
for (unsigned x = 0; x < frame->width; ++x) {
|
||||
int y = (!z ? data[0] << 8 : data[2] << 8);
|
||||
int u = data[1] - 128;
|
||||
int v = data[3] - 128;
|
||||
@@ -154,6 +138,7 @@ static void _jpeg_write_scanlines_yuyv(
|
||||
}
|
||||
}
|
||||
|
||||
JSAMPROW scanlines[1];
|
||||
scanlines[0] = line_buffer;
|
||||
jpeg_write_scanlines(jpeg, scanlines, 1);
|
||||
}
|
||||
@@ -161,20 +146,17 @@ static void _jpeg_write_scanlines_yuyv(
|
||||
free(line_buffer);
|
||||
}
|
||||
|
||||
static void _jpeg_write_scanlines_uyvy(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height) {
|
||||
|
||||
static void _jpeg_write_scanlines_uyvy(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
uint8_t *line_buffer;
|
||||
JSAMPROW scanlines[1];
|
||||
A_CALLOC(line_buffer, frame->width * 3);
|
||||
|
||||
const uint8_t *data = frame->data;
|
||||
unsigned z = 0;
|
||||
|
||||
A_CALLOC(line_buffer, width * 3);
|
||||
|
||||
while (jpeg->next_scanline < height) {
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
uint8_t *ptr = line_buffer;
|
||||
|
||||
for(unsigned x = 0; x < width; ++x) {
|
||||
for(unsigned x = 0; x < frame->width; ++x) {
|
||||
int y = (!z ? data[1] << 8 : data[3] << 8);
|
||||
int u = data[0] - 128;
|
||||
int v = data[2] - 128;
|
||||
@@ -193,6 +175,7 @@ static void _jpeg_write_scanlines_uyvy(
|
||||
}
|
||||
}
|
||||
|
||||
JSAMPROW scanlines[1];
|
||||
scanlines[0] = line_buffer;
|
||||
jpeg_write_scanlines(jpeg, scanlines, 1);
|
||||
}
|
||||
@@ -205,19 +188,16 @@ static void _jpeg_write_scanlines_uyvy(
|
||||
#undef YUV_G
|
||||
#undef YUV_R
|
||||
|
||||
static void _jpeg_write_scanlines_rgb565(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height) {
|
||||
|
||||
static void _jpeg_write_scanlines_rgb565(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
uint8_t *line_buffer;
|
||||
JSAMPROW scanlines[1];
|
||||
A_CALLOC(line_buffer, frame->width * 3);
|
||||
|
||||
A_CALLOC(line_buffer, width * 3);
|
||||
const uint8_t *data = frame->data;
|
||||
|
||||
while (jpeg->next_scanline < height) {
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
uint8_t *ptr = line_buffer;
|
||||
|
||||
for(unsigned x = 0; x < width; ++x) {
|
||||
for(unsigned x = 0; x < frame->width; ++x) {
|
||||
unsigned int two_byte = (data[1] << 8) + data[0];
|
||||
|
||||
*(ptr++) = data[1] & 248; // Red
|
||||
@@ -227,6 +207,7 @@ static void _jpeg_write_scanlines_rgb565(
|
||||
data += 2;
|
||||
}
|
||||
|
||||
JSAMPROW scanlines[1];
|
||||
scanlines[0] = line_buffer;
|
||||
jpeg_write_scanlines(jpeg, scanlines, 1);
|
||||
}
|
||||
@@ -234,14 +215,10 @@ static void _jpeg_write_scanlines_rgb565(
|
||||
free(line_buffer);
|
||||
}
|
||||
|
||||
static void _jpeg_write_scanlines_rgb24(
|
||||
struct jpeg_compress_struct *jpeg, const uint8_t *data,
|
||||
unsigned width, unsigned height) {
|
||||
|
||||
JSAMPROW scanlines[1];
|
||||
|
||||
while (jpeg->next_scanline < height) {
|
||||
scanlines[0] = (uint8_t *)(data + jpeg->next_scanline * width * 3);
|
||||
static void _jpeg_write_scanlines_rgb24(struct jpeg_compress_struct *jpeg, const frame_s *frame) {
|
||||
while (jpeg->next_scanline < frame->height) {
|
||||
JSAMPROW scanlines[1];
|
||||
scanlines[0] = (uint8_t *)(frame->data + jpeg->next_scanline * frame->width * 3);
|
||||
jpeg_write_scanlines(jpeg, scanlines, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ static bool _is_huffman(const uint8_t *data);
|
||||
|
||||
int hw_encoder_prepare(device_s *dev, unsigned quality) {
|
||||
struct v4l2_jpegcompression comp;
|
||||
|
||||
MEMSET_ZERO(comp);
|
||||
|
||||
if (xioctl(dev->run->fd, VIDIOC_G_JPEGCOMP, &comp) < 0) {
|
||||
@@ -60,7 +59,6 @@ void _copy_plus_huffman(const frame_s *src, frame_s *dest) {
|
||||
if (!_is_huffman(src->data)) {
|
||||
const uint8_t *src_ptr = src->data;
|
||||
const uint8_t *src_end = src->data + src->used;
|
||||
size_t paste;
|
||||
|
||||
while ((((src_ptr[0] << 8) | src_ptr[1]) != 0xFFC0) && (src_ptr < src_end)) {
|
||||
src_ptr += 1;
|
||||
@@ -69,7 +67,8 @@ void _copy_plus_huffman(const frame_s *src, frame_s *dest) {
|
||||
dest->used = 0; // Error
|
||||
return;
|
||||
}
|
||||
paste = src_ptr - src->data;
|
||||
|
||||
const size_t paste = src_ptr - src->data;
|
||||
|
||||
frame_set_data(dest, src->data, paste);
|
||||
frame_append_data(dest, HUFFMAN_TABLE, sizeof(HUFFMAN_TABLE));
|
||||
|
||||
@@ -78,10 +78,10 @@ int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYP
|
||||
int component_set_state(OMX_HANDLETYPE *component, OMX_STATETYPE state) {
|
||||
const char *state_str = omx_state_to_string(state);
|
||||
OMX_ERRORTYPE error;
|
||||
int retries = 50;
|
||||
|
||||
LOG_DEBUG("Switching component state to %s ...", state_str);
|
||||
|
||||
int retries = 50;
|
||||
do {
|
||||
error = OMX_SendCommand(*component, OMX_CommandStateSet, state, NULL);
|
||||
if (error == OMX_ErrorNone) {
|
||||
@@ -103,12 +103,12 @@ int component_set_state(OMX_HANDLETYPE *component, OMX_STATETYPE state) {
|
||||
|
||||
static int _component_wait_port_changed(OMX_HANDLETYPE *component, OMX_U32 port, OMX_BOOL enabled) {
|
||||
OMX_ERRORTYPE error;
|
||||
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
||||
int retries = 50;
|
||||
|
||||
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
||||
OMX_INIT_STRUCTURE(portdef);
|
||||
portdef.nPortIndex = port;
|
||||
|
||||
int retries = 50;
|
||||
do {
|
||||
if ((error = OMX_GetParameter(*component, OMX_IndexParamPortDefinition, &portdef)) != OMX_ErrorNone) {
|
||||
LOG_ERROR_OMX(error, "Can't get OMX port %u definition for waiting", port);
|
||||
@@ -129,8 +129,8 @@ static int _component_wait_port_changed(OMX_HANDLETYPE *component, OMX_U32 port,
|
||||
static int _component_wait_state_changed(OMX_HANDLETYPE *component, OMX_STATETYPE wanted) {
|
||||
OMX_ERRORTYPE error;
|
||||
OMX_STATETYPE state;
|
||||
int retries = 50;
|
||||
|
||||
int retries = 50;
|
||||
do {
|
||||
if ((error = OMX_GetState(*component, &state)) != OMX_ErrorNone) {
|
||||
LOG_ERROR_OMX(error, "Failed to get OMX component state");
|
||||
|
||||
@@ -63,8 +63,6 @@ omx_encoder_s *omx_encoder_init(void) {
|
||||
// - http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/image_encode.html
|
||||
|
||||
omx_encoder_s *omx;
|
||||
OMX_ERRORTYPE error;
|
||||
|
||||
A_CALLOC(omx, 1);
|
||||
|
||||
assert(_i_omx >= 0);
|
||||
@@ -73,7 +71,7 @@ omx_encoder_s *omx_encoder_init(void) {
|
||||
bcm_host_init();
|
||||
|
||||
LOG_INFO("Initializing OMX ...");
|
||||
if ((error = OMX_Init()) != OMX_ErrorNone) {
|
||||
if ((OMX_ERRORTYPE error = OMX_Init()) != OMX_ErrorNone) {
|
||||
LOG_ERROR_OMX(error, "Can't initialize OMX");
|
||||
goto error;
|
||||
}
|
||||
@@ -104,8 +102,6 @@ omx_encoder_s *omx_encoder_init(void) {
|
||||
}
|
||||
|
||||
void omx_encoder_destroy(omx_encoder_s *omx) {
|
||||
OMX_ERRORTYPE error;
|
||||
|
||||
LOG_INFO("Destroying OMX encoder ...");
|
||||
|
||||
component_set_state(&omx->encoder, OMX_StateIdle);
|
||||
@@ -117,7 +113,7 @@ void omx_encoder_destroy(omx_encoder_s *omx) {
|
||||
}
|
||||
|
||||
if (omx->i_encoder) {
|
||||
if ((error = OMX_FreeHandle(omx->encoder)) != OMX_ErrorNone) {
|
||||
if ((OMX_ERRORTYPE error = OMX_FreeHandle(omx->encoder)) != OMX_ErrorNone) {
|
||||
LOG_ERROR_OMX(error, "Can't free OMX.broadcom.image_encode");
|
||||
}
|
||||
}
|
||||
@@ -159,8 +155,6 @@ int omx_encoder_compress(omx_encoder_s *omx, frame_s *src, frame_s *dest) {
|
||||
# define OUT(_next) omx->output_buffer->_next
|
||||
|
||||
OMX_ERRORTYPE error;
|
||||
size_t slice_size = (IN(nAllocLen) < src->used ? IN(nAllocLen) : src->used);
|
||||
size_t pos = 0;
|
||||
|
||||
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
|
||||
LOG_ERROR_OMX(error, "Failed to request filling of the output buffer on encoder");
|
||||
@@ -171,6 +165,9 @@ int omx_encoder_compress(omx_encoder_s *omx, frame_s *src, frame_s *dest) {
|
||||
omx->output_available = false;
|
||||
omx->input_required = true;
|
||||
|
||||
const size_t slice_size = (IN(nAllocLen) < src->used ? IN(nAllocLen) : src->used);
|
||||
size_t pos = 0;
|
||||
|
||||
while (true) {
|
||||
if (omx->failed) {
|
||||
return -1;
|
||||
|
||||
@@ -40,8 +40,8 @@ static const unsigned _MOD_TABLE[] = {0, 2, 1};
|
||||
char *base64_encode(const uint8_t *str) {
|
||||
size_t str_len = strlen((const char *)str);
|
||||
size_t encoded_size = 4 * ((str_len + 2) / 3) + 1; // +1 for '\0'
|
||||
char *encoded;
|
||||
|
||||
char *encoded;
|
||||
A_CALLOC(encoded, encoded_size);
|
||||
|
||||
for (unsigned str_index = 0, encoded_index = 0; str_index < str_len;) {
|
||||
|
||||
@@ -47,16 +47,13 @@ static const struct {
|
||||
|
||||
|
||||
const char *guess_mime_type(const char *path) {
|
||||
char *dot;
|
||||
char *ext;
|
||||
|
||||
// FIXME: false-positive cppcheck
|
||||
dot = strrchr(path, '.'); // cppcheck-suppress ctunullpointer
|
||||
char *dot = strrchr(path, '.'); // cppcheck-suppress ctunullpointer
|
||||
if (dot == NULL || strchr(dot, '/') != NULL) {
|
||||
goto misc;
|
||||
}
|
||||
|
||||
ext = dot + 1;
|
||||
char *ext = dot + 1;
|
||||
for (unsigned index = 0; index < ARRAY_LEN(_MIME_TYPES); ++index) {
|
||||
if (!evutil_ascii_strcasecmp(ext, _MIME_TYPES[index].ext)) {
|
||||
return _MIME_TYPES[index].mime;
|
||||
|
||||
@@ -48,17 +48,16 @@ static void _format_bufferevent_reason(short what, char *reason);
|
||||
|
||||
|
||||
server_s *server_init(stream_s *stream) {
|
||||
server_runtime_s *run;
|
||||
server_s *server;
|
||||
exposed_s *exposed;
|
||||
|
||||
A_CALLOC(exposed, 1);
|
||||
exposed->frame = frame_init("http_exposed");
|
||||
|
||||
server_runtime_s *run;
|
||||
A_CALLOC(run, 1);
|
||||
run->stream = stream;
|
||||
run->exposed = exposed;
|
||||
|
||||
server_s *server;
|
||||
A_CALLOC(server, 1);
|
||||
server->host = "127.0.0.1";
|
||||
server->port = 8080;
|
||||
@@ -95,7 +94,6 @@ void server_destroy(server_s *server) {
|
||||
|
||||
for (stream_client_s *client = RUN(stream_clients); client != NULL;) {
|
||||
stream_client_s *next = client->next;
|
||||
|
||||
free(client->key);
|
||||
free(client);
|
||||
client = next;
|
||||
@@ -233,20 +231,19 @@ static int _http_preprocess_request(struct evhttp_request *request, server_s *se
|
||||
|
||||
static void _http_callback_root(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
struct evbuffer *buf;
|
||||
struct evkeyvalq params; // For mjpg-streamer compatibility
|
||||
const char *action; // Ditto
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
struct evkeyvalq params; // For mjpg-streamer compatibility
|
||||
evhttp_parse_query(evhttp_request_get_uri(request), ¶ms);
|
||||
action = evhttp_find_header(¶ms, "action");
|
||||
const char *action = evhttp_find_header(¶ms, "action");
|
||||
|
||||
if (action && !strcmp(action, "snapshot")) {
|
||||
_http_callback_snapshot(request, v_server);
|
||||
} else if (action && !strcmp(action, "stream")) {
|
||||
_http_callback_stream(request, v_server);
|
||||
} else {
|
||||
struct evbuffer *buf;
|
||||
assert((buf = evbuffer_new()));
|
||||
assert(evbuffer_add_printf(buf, "%s", HTML_INDEX_PAGE));
|
||||
ADD_HEADER("Content-Type", "text/html");
|
||||
@@ -261,23 +258,25 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
server_s *server = (server_s *)v_server;
|
||||
struct evbuffer *buf = NULL;
|
||||
struct evhttp_uri *uri = NULL;
|
||||
char *uri_path;
|
||||
char *decoded_path = NULL;
|
||||
char *static_path = NULL;
|
||||
int fd = -1;
|
||||
struct stat st;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
if ((uri = evhttp_uri_parse(evhttp_request_get_uri(request))) == NULL) {
|
||||
goto bad_request;
|
||||
}
|
||||
if ((uri_path = (char *)evhttp_uri_get_path(uri)) == NULL) {
|
||||
uri_path = "/";
|
||||
}
|
||||
{
|
||||
char *uri_path;
|
||||
|
||||
if ((decoded_path = evhttp_uridecode(uri_path, 0, NULL)) == NULL) {
|
||||
goto bad_request;
|
||||
if ((uri = evhttp_uri_parse(evhttp_request_get_uri(request))) == NULL) {
|
||||
goto bad_request;
|
||||
}
|
||||
if ((uri_path = (char *)evhttp_uri_get_path(uri)) == NULL) {
|
||||
uri_path = "/";
|
||||
}
|
||||
|
||||
if ((decoded_path = evhttp_uridecode(uri_path, 0, NULL)) == NULL) {
|
||||
goto bad_request;
|
||||
}
|
||||
}
|
||||
|
||||
assert((buf = evbuffer_new()));
|
||||
@@ -291,18 +290,22 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
if (fstat(fd, &st) < 0) {
|
||||
LOG_PERROR("HTTP: Can't stat() found static file %s", static_path);
|
||||
goto not_found;
|
||||
}
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (st.st_size > 0 && evbuffer_add_file(buf, fd, 0, st.st_size) < 0) {
|
||||
LOG_ERROR("HTTP: Can't serve static file %s", static_path);
|
||||
goto not_found;
|
||||
if (fstat(fd, &st) < 0) {
|
||||
LOG_PERROR("HTTP: Can't stat() found static file %s", static_path);
|
||||
goto not_found;
|
||||
}
|
||||
if (st.st_size > 0 && evbuffer_add_file(buf, fd, 0, st.st_size) < 0) {
|
||||
LOG_ERROR("HTTP: Can't serve static file %s", static_path);
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
ADD_HEADER("Content-Type", guess_mime_type(static_path));
|
||||
evhttp_send_reply(request, HTTP_OK, "OK", buf);
|
||||
goto cleanup;
|
||||
}
|
||||
ADD_HEADER("Content-Type", guess_mime_type(static_path));
|
||||
evhttp_send_reply(request, HTTP_OK, "OK", buf);
|
||||
goto cleanup;
|
||||
|
||||
bad_request:
|
||||
evhttp_send_error(request, HTTP_BADREQUEST, NULL);
|
||||
@@ -332,14 +335,14 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
|
||||
static void _http_callback_state(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
struct evbuffer *buf;
|
||||
encoder_type_e encoder_type;
|
||||
unsigned encoder_quality;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
encoder_type_e encoder_type;
|
||||
unsigned encoder_quality;
|
||||
encoder_get_runtime_params(STREAM(encoder), &encoder_type, &encoder_quality);
|
||||
|
||||
struct evbuffer *buf;
|
||||
assert((buf = evbuffer_new()));
|
||||
|
||||
assert(evbuffer_add_printf(buf,
|
||||
@@ -380,11 +383,10 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
|
||||
static void _http_callback_snapshot(struct evhttp_request *request, void *v_server) {
|
||||
server_s *server = (server_s *)v_server;
|
||||
struct evbuffer *buf;
|
||||
char header_buf[64];
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
struct evbuffer *buf;
|
||||
assert((buf = evbuffer_new()));
|
||||
assert(!evbuffer_add(buf, (const void *)EX(frame->data), EX(frame->used)));
|
||||
|
||||
@@ -395,6 +397,8 @@ static void _http_callback_snapshot(struct evhttp_request *request, void *v_serv
|
||||
ADD_HEADER("Pragma", "no-cache");
|
||||
ADD_HEADER("Expires", "Mon, 3 Jan 2000 12:34:56 GMT");
|
||||
|
||||
char header_buf[256];
|
||||
|
||||
# define ADD_TIME_HEADER(_key, _value) { \
|
||||
sprintf(header_buf, "%.06Lf", _value); \
|
||||
ADD_HEADER(_key, header_buf); \
|
||||
@@ -438,24 +442,21 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
// https://github.com/libevent/libevent/blob/29cc8386a2f7911eaa9336692a2c5544d8b4734f/http.c#L1458
|
||||
|
||||
server_s *server = (server_s *)v_server;
|
||||
struct evhttp_connection *conn;
|
||||
struct evkeyvalq params;
|
||||
struct bufferevent *buf_event;
|
||||
stream_client_s *client;
|
||||
char *client_addr;
|
||||
unsigned short client_port;
|
||||
uuid_t uuid;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
struct evhttp_connection *conn;
|
||||
conn = evhttp_request_get_connection(request);
|
||||
|
||||
if (conn) {
|
||||
stream_client_s *client;
|
||||
A_CALLOC(client, 1);
|
||||
client->server = server;
|
||||
client->request = request;
|
||||
client->need_initial = true;
|
||||
client->need_first_frame = true;
|
||||
|
||||
struct evkeyvalq params;
|
||||
evhttp_parse_query(evhttp_request_get_uri(request), ¶ms);
|
||||
client->key = uri_get_string(¶ms, "key");
|
||||
client->extra_headers = uri_get_true(¶ms, "extra_headers");
|
||||
@@ -463,6 +464,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
client->dual_final_frames = uri_get_true(¶ms, "dual_final_frames");
|
||||
evhttp_clear_headers(¶ms);
|
||||
|
||||
uuid_t uuid;
|
||||
uuid_generate(uuid);
|
||||
uuid_unparse_lower(uuid, client->id);
|
||||
|
||||
@@ -487,11 +489,15 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
# endif
|
||||
}
|
||||
|
||||
char *client_addr;
|
||||
unsigned short client_port;
|
||||
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
||||
|
||||
LOG_INFO("HTTP: Registered client: [%s]:%u, id=%s; clients now: %u",
|
||||
client_addr, client_port, client->id, RUN(stream_clients_count));
|
||||
|
||||
buf_event = evhttp_connection_get_bufferevent(conn);
|
||||
|
||||
struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
|
||||
if (server->tcp_nodelay && !RUN(unix_fd)) {
|
||||
evutil_socket_t fd;
|
||||
int on = 1;
|
||||
@@ -517,7 +523,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
|
||||
stream_client_s *client = (stream_client_s *)v_client;
|
||||
server_s *server = client->server;
|
||||
struct evbuffer *buf;
|
||||
|
||||
long double now = get_now_monotonic();
|
||||
long long now_second = floor_ms(now);
|
||||
|
||||
@@ -528,6 +534,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
}
|
||||
client->fps_accum += 1;
|
||||
|
||||
struct evbuffer *buf;
|
||||
assert((buf = evbuffer_new()));
|
||||
|
||||
// В хроме и его производных есть фундаментальный баг: он отрисовывает
|
||||
@@ -640,11 +647,8 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UNUSED short what, void *v_client) {
|
||||
stream_client_s *client = (stream_client_s *)v_client;
|
||||
server_s *server = client->server;
|
||||
struct evhttp_connection *conn;
|
||||
char *client_addr = "???";
|
||||
unsigned short client_port = 0;
|
||||
char reason[2048] = {0};
|
||||
|
||||
char reason[2048] = {0};
|
||||
_format_bufferevent_reason(what, reason);
|
||||
|
||||
assert(RUN(stream_clients_count) > 0);
|
||||
@@ -660,13 +664,17 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
||||
# endif
|
||||
}
|
||||
|
||||
conn = evhttp_request_get_connection(client->request);
|
||||
char *client_addr = "???";
|
||||
unsigned short client_port = 0;
|
||||
|
||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||
if (conn) {
|
||||
evhttp_connection_get_peer(conn, &client_addr, &client_port);
|
||||
}
|
||||
|
||||
LOG_INFO("HTTP: Disconnected client: [%s]:%u, id=%s, %s; clients now: %u",
|
||||
client_addr, client_port, client->id, reason, RUN(stream_clients_count));
|
||||
|
||||
if (conn) {
|
||||
evhttp_connection_free(conn);
|
||||
}
|
||||
@@ -684,14 +692,11 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
||||
}
|
||||
|
||||
static void _http_queue_send_stream(server_s *server, bool stream_updated, bool frame_updated) {
|
||||
struct evhttp_connection *conn;
|
||||
struct bufferevent *buf_event;
|
||||
long long now;
|
||||
bool has_clients = false;
|
||||
bool queued = false;
|
||||
|
||||
for (stream_client_s *client = RUN(stream_clients); client != NULL; client = client->next) {
|
||||
conn = evhttp_request_get_connection(client->request);
|
||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||
if (conn) {
|
||||
// Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
|
||||
// WebKit отрисовывает последний фрейм в серии с некоторой задержкой,
|
||||
@@ -708,7 +713,7 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool
|
||||
);
|
||||
|
||||
if (dual_update || frame_updated || client->need_first_frame) {
|
||||
buf_event = evhttp_connection_get_bufferevent(conn);
|
||||
struct bufferevent *buf_event = evhttp_connection_get_bufferevent(conn);
|
||||
bufferevent_setcb(buf_event, NULL, _http_callback_stream_write, _http_callback_stream_error, (void *)client);
|
||||
bufferevent_enable(buf_event, EV_READ|EV_WRITE);
|
||||
|
||||
@@ -726,8 +731,8 @@ static void _http_queue_send_stream(server_s *server, bool stream_updated, bool
|
||||
if (queued) {
|
||||
static unsigned queued_fps_accum = 0;
|
||||
static long long queued_fps_second = 0;
|
||||
|
||||
if ((now = floor_ms(get_now_monotonic())) != queued_fps_second) {
|
||||
long long now = floor_ms(get_now_monotonic());
|
||||
if (now != queued_fps_second) {
|
||||
EX(queued_fps) = queued_fps_accum;
|
||||
queued_fps_accum = 0;
|
||||
queued_fps_second = now;
|
||||
|
||||
@@ -24,11 +24,9 @@
|
||||
|
||||
|
||||
char *find_static_file_path(const char *root_path, const char *request_path) {
|
||||
char *simplified_path;
|
||||
char *path = NULL;
|
||||
struct stat st;
|
||||
|
||||
simplified_path = simplify_request_path(request_path);
|
||||
char *simplified_path = simplify_request_path(request_path);
|
||||
if (simplified_path[0] == '\0') {
|
||||
LOG_VERBOSE("HTTP: Invalid request path %s to static", request_path);
|
||||
goto error;
|
||||
@@ -37,6 +35,7 @@ char *find_static_file_path(const char *root_path, const char *request_path) {
|
||||
A_CALLOC(path, strlen(root_path) + strlen(simplified_path) + 32);
|
||||
sprintf(path, "%s/%s", root_path, simplified_path);
|
||||
|
||||
struct stat st;
|
||||
# define LOAD_STAT { \
|
||||
if (lstat(path, &st) < 0) { \
|
||||
LOG_VERBOSE_PERROR("HTTP: Can't stat() static path %s", path); \
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
|
||||
evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode) {
|
||||
evutil_socket_t fd = -1;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
# define MAX_SUN_PATH (sizeof(addr.sun_path) - 1)
|
||||
@@ -40,6 +39,7 @@ evutil_socket_t evhttp_my_bind_unix(struct evhttp *http, const char *path, bool
|
||||
|
||||
# undef MAX_SUN_PATH
|
||||
|
||||
evutil_socket_t fd = -1;
|
||||
assert((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
|
||||
assert(!evutil_make_socket_nonblocking(fd));
|
||||
|
||||
|
||||
@@ -24,9 +24,8 @@
|
||||
|
||||
|
||||
bool uri_get_true(struct evkeyvalq *params, const char *key) {
|
||||
const char *value_str;
|
||||
|
||||
if ((value_str = evhttp_find_header(params, key)) != NULL) {
|
||||
const char *value_str = evhttp_find_header(params, key);
|
||||
if (value_str != NULL) {
|
||||
if (
|
||||
value_str[0] == '1'
|
||||
|| !evutil_ascii_strcasecmp(value_str, "true")
|
||||
@@ -39,9 +38,8 @@ bool uri_get_true(struct evkeyvalq *params, const char *key) {
|
||||
}
|
||||
|
||||
char *uri_get_string(struct evkeyvalq *params, const char *key) {
|
||||
const char *value_str;
|
||||
|
||||
if ((value_str = evhttp_find_header(params, key)) != NULL) {
|
||||
const char *value_str = evhttp_find_header(params, key);
|
||||
if (value_str != NULL) {
|
||||
return evhttp_encode_uri(value_str);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
@@ -87,8 +87,8 @@ static void _signal_handler(int signum) {
|
||||
|
||||
static void _install_signal_handlers(void) {
|
||||
struct sigaction sig_act;
|
||||
|
||||
MEMSET_ZERO(sig_act);
|
||||
|
||||
assert(!sigemptyset(&sig_act.sa_mask));
|
||||
sig_act.sa_handler = _signal_handler;
|
||||
assert(!sigaddset(&sig_act.sa_mask, SIGINT));
|
||||
@@ -105,21 +105,18 @@ static void _install_signal_handlers(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
options_s *options;
|
||||
device_s *dev;
|
||||
encoder_s *encoder;
|
||||
stream_s *stream;
|
||||
server_s *server;
|
||||
int exit_code = 0;
|
||||
assert(argc >= 0);
|
||||
|
||||
LOGGING_INIT;
|
||||
A_THREAD_RENAME("main");
|
||||
options = options_init(argc, argv);
|
||||
|
||||
dev = device_init();
|
||||
encoder = encoder_init();
|
||||
stream = stream_init(dev, encoder);
|
||||
server = server_init(stream);
|
||||
options_s *options = options_init(argc, argv);
|
||||
device_s *dev = device_init();
|
||||
encoder_s *encoder = encoder_init();
|
||||
stream_s *stream = stream_init(dev, encoder);
|
||||
server_s *server = server_init(stream);
|
||||
|
||||
int exit_code = 0;
|
||||
|
||||
if ((exit_code = options_parse(options, dev, encoder, stream, server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
@@ -128,10 +125,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
_install_signal_handlers();
|
||||
|
||||
pthread_t stream_loop_tid;
|
||||
pthread_t server_loop_tid;
|
||||
_main_context_s ctx;
|
||||
|
||||
ctx.stream = stream;
|
||||
ctx.server = server;
|
||||
_ctx = &ctx;
|
||||
@@ -141,6 +135,8 @@ int main(int argc, char *argv[]) {
|
||||
gpio_set_prog_running(true);
|
||||
# endif
|
||||
|
||||
pthread_t stream_loop_tid;
|
||||
pthread_t server_loop_tid;
|
||||
A_THREAD_CREATE(&stream_loop_tid, _stream_loop_thread, NULL);
|
||||
A_THREAD_CREATE(&server_loop_tid, _server_loop_thread, NULL);
|
||||
A_THREAD_JOIN(server_loop_tid);
|
||||
|
||||
@@ -214,18 +214,16 @@ static void _features(void);
|
||||
static void _help(device_s *dev, encoder_s *encoder, stream_s *stream, server_s *server);
|
||||
|
||||
|
||||
options_s *options_init(int argc, char *argv[]) {
|
||||
options_s *options_init(unsigned argc, char *argv[]) {
|
||||
options_s *options;
|
||||
|
||||
A_CALLOC(options, 1);
|
||||
options->argc = argc;
|
||||
options->argv = argv;
|
||||
|
||||
A_CALLOC(options->argv_copy, argc);
|
||||
for (int index = 0; index < argc; ++index) {
|
||||
for (unsigned index = 0; index < argc; ++index) {
|
||||
assert(options->argv_copy[index] = strdup(argv[index]));
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -238,7 +236,7 @@ void options_destroy(options_s *options) {
|
||||
if (options->blank) {
|
||||
frame_destroy(options->blank);
|
||||
}
|
||||
for (int index = 0; index < options->argc; ++index) {
|
||||
for (unsigned index = 0; index < options->argc; ++index) {
|
||||
free(options->argv_copy[index]);
|
||||
}
|
||||
free(options->argv_copy);
|
||||
@@ -313,11 +311,6 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
break; \
|
||||
}
|
||||
|
||||
int ch;
|
||||
int short_index;
|
||||
int opt_index;
|
||||
char short_opts[1024] = {0};
|
||||
|
||||
char *blank_path = NULL;
|
||||
|
||||
# ifdef WITH_MEMSINK
|
||||
@@ -331,7 +324,9 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
char *process_name_prefix = NULL;
|
||||
# endif
|
||||
|
||||
for (short_index = 0, opt_index = 0; _LONG_OPTS[opt_index].name != NULL; ++opt_index) {
|
||||
char short_opts[1024] = {0};
|
||||
|
||||
for (int short_index = 0, opt_index = 0; _LONG_OPTS[opt_index].name != NULL; ++opt_index) {
|
||||
if (isalpha(_LONG_OPTS[opt_index].val)) {
|
||||
short_opts[short_index] = _LONG_OPTS[opt_index].val;
|
||||
++short_index;
|
||||
@@ -342,7 +337,7 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
}
|
||||
}
|
||||
|
||||
while ((ch = getopt_long(options->argc, options->argv_copy, short_opts, _LONG_OPTS, NULL)) >= 0) {
|
||||
for (int ch; (ch = getopt_long(options->argc, options->argv_copy, short_opts, _LONG_OPTS, NULL)) >= 0;) {
|
||||
switch (ch) {
|
||||
case _O_DEVICE: OPT_SET(dev->path, optarg);
|
||||
case _O_INPUT: OPT_NUMBER("--input", dev->input, 0, 128, 0);
|
||||
@@ -494,7 +489,6 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
static int _parse_resolution(const char *str, unsigned *width, unsigned *height, bool limited) {
|
||||
unsigned tmp_width;
|
||||
unsigned tmp_height;
|
||||
|
||||
if (sscanf(str, "%ux%u", &tmp_width, &tmp_height) != 2) {
|
||||
return -1;
|
||||
}
|
||||
@@ -514,20 +508,19 @@ static int _parse_resolution(const char *str, unsigned *width, unsigned *height,
|
||||
#ifdef WITH_OMX
|
||||
static int _parse_glitched_resolutions(const char *str, encoder_s *encoder) {
|
||||
char *str_copy;
|
||||
char *ptr;
|
||||
unsigned count = 0;
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
|
||||
assert((str_copy = strdup(str)) != NULL);
|
||||
|
||||
ptr = strtok(str_copy, ",;:\n\t ");
|
||||
char *ptr = strtok(str_copy, ",;:\n\t ");
|
||||
unsigned count = 0;
|
||||
|
||||
while (ptr != NULL) {
|
||||
if (count >= MAX_GLITCHED_RESOLUTIONS) {
|
||||
printf("Too big '--glitched-resolutions' list: maxlen=%u\n", MAX_GLITCHED_RESOLUTIONS);
|
||||
goto error;
|
||||
}
|
||||
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
switch (_parse_resolution(ptr, &width, &height, true)) {
|
||||
case -1:
|
||||
printf("Invalid resolution format of '%s' in '--glitched-resolutions=%s\n", ptr, str_copy);
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
int argc;
|
||||
unsigned argc;
|
||||
char **argv;
|
||||
char **argv_copy;
|
||||
frame_s *blank;
|
||||
@@ -62,7 +62,7 @@ typedef struct {
|
||||
} options_s;
|
||||
|
||||
|
||||
options_s *options_init(int argc, char *argv[]);
|
||||
options_s *options_init(unsigned argc, char *argv[]);
|
||||
void options_destroy(options_s *options);
|
||||
|
||||
int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_s *stream, server_s *server);
|
||||
|
||||
@@ -85,18 +85,17 @@ static long double _workers_pool_get_fluency_delay(_pool_s *pool, _worker_s *rea
|
||||
|
||||
stream_s *stream_init(device_s *dev, encoder_s *encoder) {
|
||||
process_s *proc;
|
||||
video_s *video;
|
||||
stream_s *stream;
|
||||
|
||||
A_CALLOC(proc, 1);
|
||||
atomic_init(&proc->stop, false);
|
||||
atomic_init(&proc->slowdown, false);
|
||||
|
||||
video_s *video;
|
||||
A_CALLOC(video, 1);
|
||||
video->frame = frame_init("stream_video");
|
||||
atomic_init(&video->updated, false);
|
||||
A_MUTEX_INIT(&video->mutex);
|
||||
|
||||
stream_s *stream;
|
||||
A_CALLOC(stream, 1);
|
||||
stream->last_as_blank = -1;
|
||||
stream->error_delay = 1;
|
||||
@@ -117,12 +116,11 @@ void stream_destroy(stream_s *stream) {
|
||||
|
||||
void stream_loop(stream_s *stream) {
|
||||
# define DEV(_next) stream->dev->_next
|
||||
_pool_s *pool;
|
||||
|
||||
LOG_INFO("Using V4L2 device: %s", DEV(path));
|
||||
LOG_INFO("Using desired FPS: %u", DEV(desired_fps));
|
||||
|
||||
while ((pool = _stream_init_loop(stream)) != NULL) {
|
||||
for (_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
|
||||
long double grab_after = 0;
|
||||
unsigned fluency_passed = 0;
|
||||
unsigned captured_fps = 0;
|
||||
@@ -132,12 +130,10 @@ void stream_loop(stream_s *stream) {
|
||||
LOG_INFO("Capturing ...");
|
||||
|
||||
while (!atomic_load(&stream->proc->stop)) {
|
||||
_worker_s *ready_wr;
|
||||
|
||||
SEP_DEBUG('-');
|
||||
LOG_DEBUG("Waiting for worker ...");
|
||||
|
||||
ready_wr = _workers_pool_wait(pool);
|
||||
_worker_s *ready_wr = _workers_pool_wait(pool);
|
||||
|
||||
if (!ready_wr->job_failed) {
|
||||
if (ready_wr->job_timely) {
|
||||
@@ -373,10 +369,9 @@ static _pool_s *_workers_pool_init(stream_s *stream) {
|
||||
# define DEV(_next) stream->dev->_next
|
||||
# define RUN(_next) stream->dev->run->_next
|
||||
|
||||
_pool_s *pool;
|
||||
|
||||
LOG_INFO("Creating pool with %u workers ...", stream->encoder->run->n_workers);
|
||||
|
||||
_pool_s *pool;
|
||||
A_CALLOC(pool, 1);
|
||||
|
||||
pool->n_workers = stream->encoder->run->n_workers;
|
||||
@@ -470,7 +465,6 @@ static void *_worker_thread(void *v_worker) {
|
||||
# define PIC(_next) wr->frame->_next
|
||||
|
||||
LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", wr->number, wr->buf_index);
|
||||
|
||||
wr->job_failed = (bool)encoder_compress(
|
||||
wr->stream->encoder,
|
||||
wr->number,
|
||||
@@ -482,7 +476,6 @@ static void *_worker_thread(void *v_worker) {
|
||||
if (!wr->job_failed) {
|
||||
wr->job_start_ts = PIC(encode_begin_ts);
|
||||
wr->last_comp_time = PIC(encode_end_ts) - wr->job_start_ts;
|
||||
|
||||
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%u, buffer=%u",
|
||||
PIC(used), wr->last_comp_time, wr->number, wr->buf_index);
|
||||
} else {
|
||||
@@ -567,17 +560,14 @@ static void _workers_pool_assign(_pool_s *pool, _worker_s *ready_wr, unsigned bu
|
||||
}
|
||||
|
||||
static long double _workers_pool_get_fluency_delay(_pool_s *pool, _worker_s *ready_wr) {
|
||||
long double approx_comp_time;
|
||||
long double min_delay;
|
||||
|
||||
approx_comp_time = pool->approx_comp_time * 0.9 + ready_wr->last_comp_time * 0.1;
|
||||
const long double approx_comp_time = pool->approx_comp_time * 0.9 + ready_wr->last_comp_time * 0.1;
|
||||
|
||||
LOG_VERBOSE("Correcting approx_comp_time: %.3Lf -> %.3Lf (last_comp_time=%.3Lf)",
|
||||
pool->approx_comp_time, approx_comp_time, ready_wr->last_comp_time);
|
||||
|
||||
pool->approx_comp_time = approx_comp_time;
|
||||
|
||||
min_delay = pool->approx_comp_time / pool->n_workers; // Среднее время работы размазывается на N воркеров
|
||||
const long double min_delay = pool->approx_comp_time / pool->n_workers; // Среднее время работы размазывается на N воркеров
|
||||
|
||||
if (pool->desired_frames_interval > 0 && min_delay > 0 && pool->desired_frames_interval > min_delay) {
|
||||
// Искусственное время задержки на основе желаемого FPS, если включен --desired-fps
|
||||
|
||||
Reference in New Issue
Block a user