Compare commits

...

4 Commits
v0.59 ... v0.60

Author SHA1 Message Date
Devaev Maxim
c30dea20a5 Bump version: 0.59 → 0.60 2019-03-17 20:10:11 +03:00
Devaev Maxim
b31450ba41 size -> used 2019-03-17 19:44:49 +03:00
Devaev Maxim
c05457ce2f separate hw_buffer_t size and allocated 2019-03-17 19:33:56 +03:00
Devaev Maxim
9e63076ec5 fixed omx slice_size 2019-03-17 18:50:12 +03:00
12 changed files with 68 additions and 62 deletions

View File

@@ -1,7 +1,7 @@
[bumpversion]
commit = True
tag = True
current_version = 0.59
current_version = 0.60
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+)(\-(?P<release>[a-z]+))?)?
serialize =
{major}.{minor}

View File

@@ -3,7 +3,7 @@
pkgname=ustreamer
pkgver=0.59
pkgver=0.60
pkgrel=1
pkgdesc="Lightweight and fast MJPG-HTTP streamer"
url="https://github.com/pi-kvm/ustreamer"

View File

@@ -22,4 +22,4 @@
#pragma once
#define VERSION "0.59"
#define VERSION "0.60"

View File

@@ -177,8 +177,8 @@ void device_close(struct device_t *dev) {
for (unsigned index = 0; index < dev->run->n_buffers; ++index) {
# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next
if (HW_BUFFER(size) > 0 && HW_BUFFER(data) != MAP_FAILED) {
if (munmap(HW_BUFFER(data), HW_BUFFER(size)) < 0) {
if (HW_BUFFER(allocated) > 0 && HW_BUFFER(data) != MAP_FAILED) {
if (munmap(HW_BUFFER(data), HW_BUFFER(allocated)) < 0) {
LOG_PERROR("Can't unmap device buffer %u", index);
}
}
@@ -219,31 +219,37 @@ int device_switch_capturing(struct device_t *dev, bool enable) {
return 0;
}
int device_grab_buffer(struct device_t *dev, struct v4l2_buffer *buf_info) {
MEMSET_ZERO_PTR(buf_info);
buf_info->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf_info->memory = V4L2_MEMORY_MMAP;
int device_grab_buffer(struct device_t *dev) {
struct v4l2_buffer buf_info;
MEMSET_ZERO(buf_info);
buf_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf_info.memory = V4L2_MEMORY_MMAP;
LOG_DEBUG("Calling ioctl(VIDIOC_DQBUF) ...");
if (xioctl(dev->run->fd, VIDIOC_DQBUF, buf_info) < 0) {
if (xioctl(dev->run->fd, VIDIOC_DQBUF, &buf_info) < 0) {
LOG_PERROR("Unable to dequeue buffer");
return -1;
}
LOG_DEBUG("Got a new frame in buffer index=%u; bytesused=%u", buf_info->index, buf_info->bytesused);
if (buf_info->index >= dev->run->n_buffers) {
LOG_ERROR("Got invalid buffer index=%u; nbuffers=%u", buf_info->index, dev->run->n_buffers);
LOG_DEBUG("Got a new frame in buffer index=%u; bytesused=%u", buf_info.index, buf_info.bytesused);
if (buf_info.index >= dev->run->n_buffers) {
LOG_ERROR("Got invalid buffer index=%u; nbuffers=%u", buf_info.index, dev->run->n_buffers);
return -1;
}
return 0;
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));
return buf_info.index;
}
int device_release_buffer(struct device_t *dev, struct v4l2_buffer *buf_info) {
int device_release_buffer(struct device_t *dev, unsigned index) {
LOG_DEBUG("Calling ioctl(VIDIOC_QBUF) ...");
if (xioctl(dev->run->fd, VIDIOC_QBUF, buf_info) < 0) {
if (xioctl(dev->run->fd, VIDIOC_QBUF, &dev->run->hw_buffers[index].buf_info) < 0) {
LOG_PERROR("Unable to requeue buffer");
return -1;
}
dev->run->hw_buffers[index].used = 0;
return 0;
}
@@ -462,7 +468,7 @@ static int _device_open_mmap(struct device_t *dev) {
LOG_PERROR("Can't map device buffer %u", dev->run->n_buffers);
return -1;
}
HW_BUFFER(size) = buf_info.length;
HW_BUFFER(allocated) = buf_info.length;
# undef HW_BUFFER
}

View File

@@ -42,13 +42,15 @@
struct hw_buffer_t {
unsigned char *data;
size_t size;
unsigned char *data;
size_t used;
size_t allocated;
struct v4l2_buffer buf_info;
};
struct picture_t {
unsigned char *data;
size_t size;
size_t used;
size_t allocated;
long double grab_time;
long double encode_begin_time;
@@ -118,6 +120,6 @@ int device_open(struct device_t *dev);
void device_close(struct device_t *dev);
int device_switch_capturing(struct device_t *dev, bool enable);
int device_grab_buffer(struct device_t *dev, struct v4l2_buffer *buf_info);
int device_release_buffer(struct device_t *dev, struct v4l2_buffer *buf_info);
int device_grab_buffer(struct device_t *dev);
int device_release_buffer(struct device_t *dev, unsigned index);
int device_consume_event(struct device_t *dev);

View File

@@ -81,8 +81,8 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
# define PICTURE(_next) dev->run->pictures[index]._next
PICTURE(size) = 0;
_jpeg_set_dest_picture(&jpeg, PICTURE(data), &PICTURE(size));
PICTURE(used) = 0;
_jpeg_set_dest_picture(&jpeg, PICTURE(data), &PICTURE(used));
jpeg.image_width = dev->run->width;
jpeg.image_height = dev->run->height;
@@ -112,8 +112,8 @@ void cpu_encoder_compress_buffer(struct device_t *dev, unsigned index, unsigned
// https://stackoverflow.com/questions/19857766/error-handling-in-libjpeg
jpeg_finish_compress(&jpeg);
jpeg_destroy_compress(&jpeg);
assert(PICTURE(size) > 0);
assert(PICTURE(size) <= dev->run->max_picture_size);
assert(PICTURE(used) > 0);
assert(PICTURE(used) <= dev->run->max_picture_size);
}
static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, size_t *written) {

View File

@@ -69,8 +69,8 @@ void hw_encoder_compress_buffer(struct device_t *dev, unsigned index) {
# define PICTURE(_next) dev->run->pictures[index]._next
# define HW_BUFFER(_next) dev->run->hw_buffers[index]._next
assert(PICTURE(allocated) >= HW_BUFFER(size) + sizeof(HUFFMAN_TABLE));
PICTURE(size) = _memcpy_with_huffman(PICTURE(data), HW_BUFFER(data), HW_BUFFER(size));
assert(PICTURE(allocated) >= HW_BUFFER(used) + sizeof(HUFFMAN_TABLE));
PICTURE(used) = _memcpy_with_huffman(PICTURE(data), HW_BUFFER(data), HW_BUFFER(used));
# undef HW_BUFFER
# undef PICTURE

View File

@@ -178,7 +178,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
# define OUT(_next) omx->output_buffer->_next
OMX_ERRORTYPE error;
size_t slice_size = IN(nAllocLen);
size_t slice_size = (IN(nAllocLen) < HW_BUFFER(used) ? IN(nAllocLen) : HW_BUFFER(used));
size_t pos = 0;
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
@@ -186,7 +186,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
return -1;
}
PICTURE(size) = 0;
PICTURE(used) = 0;
omx->output_available = false;
omx->input_required = true;
@@ -198,9 +198,9 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
if (omx->output_available) {
omx->output_available = false;
assert(PICTURE(size) + OUT(nFilledLen) <= dev->run->max_picture_size);
memcpy(PICTURE(data) + PICTURE(size), OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen));
PICTURE(size) += OUT(nFilledLen);
assert(PICTURE(used) + OUT(nFilledLen) <= dev->run->max_picture_size);
memcpy(PICTURE(data) + PICTURE(used), OUT(pBuffer) + OUT(nOffset), OUT(nFilledLen));
PICTURE(used) += OUT(nFilledLen);
if (OUT(nFlags) & OMX_BUFFERFLAG_ENDOFFRAME) {
OUT(nFlags) = 0;
@@ -216,7 +216,7 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
if (omx->input_required) {
omx->input_required = false;
if (pos == HW_BUFFER(size)) {
if (pos == HW_BUFFER(used)) {
continue;
}
@@ -226,8 +226,8 @@ int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev,
pos += slice_size;
if (pos + slice_size > HW_BUFFER(size)) {
slice_size = HW_BUFFER(size) - pos;
if (pos + slice_size > HW_BUFFER(used)) {
slice_size = HW_BUFFER(used) - pos;
}
if ((error = OMX_EmptyThisBuffer(omx->encoder, omx->input_buffer)) != OMX_ErrorNone) {

View File

@@ -324,7 +324,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.size)));
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");
@@ -505,7 +505,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.size) * sizeof(*EXPOSED(picture.data)),
EXPOSED(picture.used) * sizeof(*EXPOSED(picture.data)),
get_now_real(), (client->extra_headers ? "" : RN)
));
if (client->extra_headers) {
@@ -541,7 +541,7 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
assert(!evbuffer_add(buf,
(void *)EXPOSED(picture.data),
EXPOSED(picture.size) * sizeof(*EXPOSED(picture.data))
EXPOSED(picture.used) * sizeof(*EXPOSED(picture.data))
));
assert(evbuffer_add_printf(buf, RN "--" BOUNDARY RN));
@@ -660,7 +660,7 @@ static void _http_exposed_refresh(UNUSED int fd, UNUSED short what, void *v_serv
if (atomic_load(&server->run->stream->updated)) {
LOG_DEBUG("Refreshing HTTP exposed ...");
A_MUTEX_LOCK(&server->run->stream->mutex);
if (server->run->stream->picture.size > 0) { // If online
if (server->run->stream->picture.used > 0) { // If online
picture_updated = _expose_new_picture(server);
UNLOCK_STREAM;
} else {
@@ -683,19 +683,19 @@ static bool _expose_new_picture(struct http_server_t *server) {
# define STREAM(_next) server->run->stream->_next
# define EXPOSED(_next) server->run->exposed->_next
assert(STREAM(picture.size) > 0);
assert(STREAM(picture.used) > 0);
EXPOSED(captured_fps) = STREAM(captured_fps);
EXPOSED(expose_begin_time) = get_now_monotonic();
# define MEM_STREAM_TO_EXPOSED \
EXPOSED(picture.data), STREAM(picture.data), \
STREAM(picture.size) * sizeof(*STREAM(picture.data))
STREAM(picture.used) * sizeof(*STREAM(picture.data))
if (server->drop_same_frames) {
if (
EXPOSED(online)
&& EXPOSED(dropped) < server->drop_same_frames
&& EXPOSED(picture.size) == STREAM(picture.size)
&& EXPOSED(picture.used) == STREAM(picture.used)
&& !memcmp(MEM_STREAM_TO_EXPOSED)
) {
EXPOSED(expose_cmp_time) = get_now_monotonic();
@@ -720,7 +720,7 @@ static bool _expose_new_picture(struct http_server_t *server) {
# undef MEM_STREAM_TO_EXPOSED
EXPOSED(picture.size) = STREAM(picture.size);
EXPOSED(picture.used) = STREAM(picture.used);
EXPOSED(picture.grab_time) = STREAM(picture.grab_time);
EXPOSED(picture.encode_begin_time) = STREAM(picture.encode_begin_time);
@@ -748,7 +748,7 @@ 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.size) == 0) {
if (EXPOSED(online) || EXPOSED(picture.used) == 0) {
if (EXPOSED(picture.allocated) < BLANK_JPEG_LEN) {
A_REALLOC(EXPOSED(picture.data), BLANK_JPEG_LEN);
EXPOSED(picture.allocated) = BLANK_JPEG_LEN;
@@ -756,7 +756,7 @@ static bool _expose_blank_picture(struct http_server_t *server) {
memcpy(EXPOSED(picture.data), BLANK_JPEG_DATA, BLANK_JPEG_LEN * sizeof(*EXPOSED(picture.data)));
EXPOSED(picture.size) = BLANK_JPEG_LEN;
EXPOSED(picture.used) = BLANK_JPEG_LEN;
EXPOSED(picture.grab_time) = 0;
EXPOSED(picture.encode_begin_time) = 0;

View File

@@ -186,22 +186,23 @@ void stream_loop(struct stream_t *stream) {
if (FD_ISSET(stream->dev->run->fd, &read_fds)) {
LOG_DEBUG("Frame is ready");
struct v4l2_buffer buf_info;
int buf_index;
long double now = get_now_monotonic();
long long now_second = floor_ms(now);
if (device_grab_buffer(stream->dev, &buf_info) < 0) {
if ((buf_index = device_grab_buffer(stream->dev)) < 0) {
break;
}
stream->dev->run->pictures[buf_info.index].grab_time = now;
stream->dev->run->pictures[buf_index].grab_time = now;
// Workaround for broken, corrupted frames:
// Under low light conditions corrupted frames may get captured.
// The good thing is such frames are quite small compared to the regular pictures.
// For example a VGA (640x480) webcam picture is normally >= 8kByte large,
// corrupted frames are smaller.
if (buf_info.bytesused < stream->dev->min_frame_size) {
LOG_DEBUG("Dropping too small frame sized %u bytes, assuming it as broken", buf_info.bytesused);
if (stream->dev->run->hw_buffers[buf_index].used < stream->dev->min_frame_size) {
LOG_DEBUG("Dropping too small frame sized %zu bytes, assuming it as broken",
stream->dev->run->hw_buffers[buf_index].used);
goto pass_frame;
}
@@ -229,8 +230,7 @@ void stream_loop(struct stream_t *stream) {
# define FREE_WORKER(_next) pool.workers[free_worker_number]._next
LOG_DEBUG("Grabbed a new frame to buffer %u", buf_info.index);
FREE_WORKER(buf_info) = buf_info;
LOG_DEBUG("Grabbed a new frame to buffer %u", buf_index);
if (!oldest_worker) {
oldest_worker = &pool.workers[free_worker_number];
@@ -249,7 +249,7 @@ void stream_loop(struct stream_t *stream) {
last_worker->order_next = NULL;
A_MUTEX_LOCK(&FREE_WORKER(has_job_mutex));
FREE_WORKER(buf_index) = buf_info.index;
FREE_WORKER(buf_index) = buf_index;
atomic_store(&FREE_WORKER(has_job), true);
A_MUTEX_UNLOCK(&FREE_WORKER(has_job_mutex));
A_COND_SIGNAL(&FREE_WORKER(has_job_cond));
@@ -264,7 +264,7 @@ void stream_loop(struct stream_t *stream) {
pass_frame:
if (device_release_buffer(stream->dev, &buf_info) < 0) {
if (device_release_buffer(stream->dev, buf_index) < 0) {
break;
}
}
@@ -286,7 +286,7 @@ void stream_loop(struct stream_t *stream) {
}
A_MUTEX_LOCK(&stream->mutex);
stream->picture.size = 0; // On stream offline
stream->picture.used = 0; // On stream offline
free(stream->picture.data);
stream->width = 0;
stream->height = 0;
@@ -312,10 +312,10 @@ static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index)
A_MUTEX_LOCK(&stream->mutex);
stream->picture.size = PICTURE(size);
stream->picture.used = PICTURE(used);
stream->picture.allocated = PICTURE(allocated);
memcpy(stream->picture.data, PICTURE(data), stream->picture.size * sizeof(*stream->picture.data));
memcpy(stream->picture.data, PICTURE(data), stream->picture.used * sizeof(*stream->picture.data));
stream->picture.grab_time = PICTURE(grab_time);
stream->picture.encode_begin_time = PICTURE(encode_begin_time);
@@ -457,7 +457,7 @@ static void *_stream_worker_thread(void *v_worker) {
}
PICTURE(encode_end_time) = get_now_monotonic();
if (device_release_buffer(worker->dev, &worker->buf_info) == 0) {
if (device_release_buffer(worker->dev, worker->buf_index) == 0) {
worker->job_start_time = PICTURE(encode_begin_time);
atomic_store(&worker->has_job, false);
@@ -468,7 +468,7 @@ static void *_stream_worker_thread(void *v_worker) {
A_MUTEX_UNLOCK(&worker->last_comp_time_mutex);
LOG_VERBOSE("Compressed JPEG size=%zu; time=%0.3Lf; worker=%u; buffer=%u",
PICTURE(size), last_comp_time, worker->number, worker->buf_index);
PICTURE(used), last_comp_time, worker->number, worker->buf_index);
} else {
worker->job_failed = true;
atomic_store(&worker->has_job, false);

View File

@@ -42,7 +42,6 @@ struct worker_t {
pthread_mutex_t has_job_mutex;
int buf_index;
struct v4l2_buffer buf_info;
atomic_bool has_job;
bool job_failed;
long double job_start_time;

View File

@@ -51,7 +51,6 @@
#define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest)))))
#define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest)))))
#define MEMSET_ZERO(_obj) memset(&(_obj), 0, sizeof(_obj))
#define MEMSET_ZERO_PTR(_ptr) memset(_ptr, 0, sizeof(*(_ptr)))
#define ARRAY_LEN(_array) (sizeof(_array) / sizeof(_array[0]))