mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-19 16:26:30 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
408157c82b | ||
|
|
7356dea737 | ||
|
|
87a75a816a | ||
|
|
b6a2332207 | ||
|
|
34c0dcb1ce |
@@ -1,7 +1,7 @@
|
||||
[bumpversion]
|
||||
commit = True
|
||||
tag = True
|
||||
current_version = 6.4
|
||||
current_version = 6.5
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||
serialize =
|
||||
{major}.{minor}
|
||||
|
||||
@@ -214,7 +214,7 @@ static _enc_buffer_s *_enc_buffer_init(void) {
|
||||
static void *_pcm_thread(void *v_audio) {
|
||||
US_THREAD_SETTLE("us_a_pcm");
|
||||
|
||||
us_audio_s *const audio = (us_audio_s *)v_audio;
|
||||
us_audio_s *const audio = v_audio;
|
||||
u8 in[_MAX_BUF8];
|
||||
|
||||
while (!atomic_load(&audio->stop)) {
|
||||
@@ -244,7 +244,7 @@ static void *_pcm_thread(void *v_audio) {
|
||||
static void *_encoder_thread(void *v_audio) {
|
||||
US_THREAD_SETTLE("us_a_enc");
|
||||
|
||||
us_audio_s *const audio = (us_audio_s *)v_audio;
|
||||
us_audio_s *const audio = v_audio;
|
||||
s16 in_res[_MAX_BUF16];
|
||||
|
||||
while (!atomic_load(&audio->stop)) {
|
||||
|
||||
@@ -104,7 +104,7 @@ static void *_audio_thread(void *v_client) {
|
||||
}
|
||||
|
||||
static void *_common_thread(void *v_client, bool video) {
|
||||
us_janus_client_s *const client = (us_janus_client_s *)v_client;
|
||||
us_janus_client_s *const client = v_client;
|
||||
us_ring_s *const ring = (video ? client->video_ring : client->audio_ring);
|
||||
assert(ring != NULL); // Audio may be NULL
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ int us_memsink_fd_wait_frame(int fd, us_memsink_shared_s *mem, u64 last_id) {
|
||||
}
|
||||
|
||||
int us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, us_frame_s *frame, u64 *frame_id, bool key_required) {
|
||||
us_frame_set_data(frame, mem->data, mem->used);
|
||||
us_frame_set_data(frame, us_memsink_get_data(mem), mem->used);
|
||||
US_FRAME_COPY_META(mem, frame);
|
||||
*frame_id = mem->id;
|
||||
mem->last_client_ts = us_get_now_monotonic();
|
||||
|
||||
@@ -136,12 +136,18 @@ static void *_video_sink_thread(void *arg) {
|
||||
int fd = -1;
|
||||
us_memsink_shared_s *mem = NULL;
|
||||
|
||||
const uz data_size = us_memsink_calculate_size(_g_config->video_sink_name);
|
||||
if (data_size == 0) {
|
||||
US_ONCE({ US_JLOG_ERROR("video", "Invalid memsink object suffix"); });
|
||||
goto close_memsink;
|
||||
}
|
||||
|
||||
if ((fd = shm_open(_g_config->video_sink_name, O_RDWR, 0)) <= 0) {
|
||||
US_ONCE({ US_JLOG_PERROR("video", "Can't open memsink"); });
|
||||
goto close_memsink;
|
||||
}
|
||||
|
||||
if ((mem = us_memsink_shared_map(fd)) == NULL) {
|
||||
if ((mem = us_memsink_shared_map(fd, data_size)) == NULL) {
|
||||
US_ONCE({ US_JLOG_PERROR("video", "Can't map memsink"); });
|
||||
goto close_memsink;
|
||||
}
|
||||
@@ -178,7 +184,10 @@ static void *_video_sink_thread(void *arg) {
|
||||
}
|
||||
|
||||
close_memsink:
|
||||
US_DELETE(mem, us_memsink_shared_unmap);
|
||||
if (mem != NULL) {
|
||||
us_memsink_shared_unmap(mem, data_size);
|
||||
mem = NULL;
|
||||
}
|
||||
US_CLOSE_FD(fd);
|
||||
US_JLOG_INFO("video", "Memsink closed");
|
||||
sleep(1); // error_delay
|
||||
|
||||
@@ -57,7 +57,7 @@ void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked) {
|
||||
++rtp->seq;
|
||||
|
||||
# define WRITE_BE_U32(x_offset, x_value) \
|
||||
*((u32 *)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
|
||||
*((u32*)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
|
||||
WRITE_BE_U32(0, word0);
|
||||
WRITE_BE_U32(4, pts);
|
||||
WRITE_BE_U32(8, rtp->ssrc);
|
||||
|
||||
1
janus/src/uslibs/memsinksh.c
Symbolic link
1
janus/src/uslibs/memsinksh.c
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../src/libs/memsinksh.c
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Manpage for ustreamer-dump.
|
||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||
.TH USTREAMER-DUMP 1 "version 6.4" "January 2021"
|
||||
.TH USTREAMER-DUMP 1 "version 6.5" "January 2021"
|
||||
|
||||
.SH NAME
|
||||
ustreamer-dump \- Dump uStreamer's memory sink to file
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" Manpage for ustreamer.
|
||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||
.TH USTREAMER 1 "version 6.4" "November 2020"
|
||||
.TH USTREAMER 1 "version 6.5" "November 2020"
|
||||
|
||||
.SH NAME
|
||||
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
||||
@@ -212,25 +212,25 @@ Timeout for client connections. Default: 10.
|
||||
.SS "JPEG sink options"
|
||||
With shared memory sink you can write a stream to a file. See \fBustreamer-dump\fR(1) for more info.
|
||||
.TP
|
||||
.BR \-\-sink\ \fIname
|
||||
Use the specified shared memory object to sink JPEG frames. Default: disabled.
|
||||
.BR \-\-jpeg\-sink\ \fIname
|
||||
Use the specified shared memory object to sink JPEG frames. The name should end with a suffix ".jpeg" or ":jpeg". Default: disabled.
|
||||
.TP
|
||||
.BR \-\-sink\-mode\ \fImode
|
||||
.BR \-\-jpeg\-sink\-mode\ \fImode
|
||||
Set JPEG sink permissions (like 777). Default: 660.
|
||||
.TP
|
||||
.BR \-\-sink\-rm
|
||||
.BR \-\-jpeg\-sink\-rm
|
||||
Remove shared memory on stop. Default: disabled.
|
||||
.TP
|
||||
.BR \-\-sink\-client\-ttl\ \fIsec
|
||||
.BR \-\-jpeg\-sink\-client\-ttl\ \fIsec
|
||||
Client TTL. Default: 10.
|
||||
.TP
|
||||
.BR \-\-sink\-timeout\ \fIsec
|
||||
.BR \-\-jpeg\-sink\-timeout\ \fIsec
|
||||
Timeout for lock. Default: 1.
|
||||
|
||||
.SS "H264 sink options"
|
||||
.TP
|
||||
.BR \-\-h264\-sink\ \fIname
|
||||
Use the specified shared memory object to sink H264 frames. Default: disabled.
|
||||
Use the specified shared memory object to sink H264 frames. The name should end with a suffix ".h264" or ":h264". Default: disabled.
|
||||
.TP
|
||||
.BR \-\-h264\-sink\-mode\ \fImode
|
||||
Set H264 sink permissions (like 777). Default: 660.
|
||||
@@ -253,6 +253,22 @@ Interval between keyframes. Default: 30.
|
||||
.BR \-\-h264\-m2m\-device\ \fI/dev/path
|
||||
Path to V4L2 mem-to-mem encoder device. Default: auto-select.
|
||||
|
||||
.SS "RAW sink options"
|
||||
.TP
|
||||
.BR \-\-raw\-sink\ \fIname
|
||||
Use the specified shared memory object to sink RAW frames. The name should end with a suffix ".raw" or ":raw". Default: disabled.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-mode\ \fImode
|
||||
Set RAW sink permissions (like 777). Default: 660.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-rm
|
||||
Remove shared memory on stop. Default: disabled.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-client\-ttl\ \fIsec
|
||||
Client TTL. Default: 10.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-timeout\ \fIsec
|
||||
Timeout for lock. Default: 1.
|
||||
|
||||
.SS "Process options"
|
||||
.TP
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
pkgname=ustreamer
|
||||
pkgver=6.4
|
||||
pkgver=6.5
|
||||
pkgrel=1
|
||||
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
||||
url="https://github.com/pikvm/ustreamer"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=ustreamer
|
||||
PKG_VERSION:=6.4
|
||||
PKG_VERSION:=6.5
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ def _find_sources(suffix: str) -> list[str]:
|
||||
if __name__ == "__main__":
|
||||
setup(
|
||||
name="ustreamer",
|
||||
version="6.4",
|
||||
version="6.5",
|
||||
description="uStreamer tools",
|
||||
author="Maxim Devaev",
|
||||
author_email="mdevaev@gmail.com",
|
||||
|
||||
1
python/src/uslibs/memsinksh.c
Symbolic link
1
python/src/uslibs/memsinksh.c
Symbolic link
@@ -0,0 +1 @@
|
||||
../../../src/libs/memsinksh.c
|
||||
@@ -26,6 +26,7 @@ typedef struct {
|
||||
double lock_timeout;
|
||||
double wait_timeout;
|
||||
double drop_same_frames;
|
||||
uz data_size;
|
||||
|
||||
int fd;
|
||||
us_memsink_shared_s *mem;
|
||||
@@ -37,7 +38,10 @@ typedef struct {
|
||||
|
||||
|
||||
static void _MemsinkObject_destroy_internals(_MemsinkObject *self) {
|
||||
US_DELETE(self->mem, us_memsink_shared_unmap);
|
||||
if (self->mem != NULL) {
|
||||
us_memsink_shared_unmap(self->mem, self->data_size);
|
||||
self->mem = NULL;
|
||||
}
|
||||
US_CLOSE_FD(self->fd);
|
||||
US_DELETE(self->frame, us_frame_destroy);
|
||||
}
|
||||
@@ -64,13 +68,18 @@ static int _MemsinkObject_init(_MemsinkObject *self, PyObject *args, PyObject *k
|
||||
SET_DOUBLE(drop_same_frames, >= 0);
|
||||
# undef SET_DOUBLE
|
||||
|
||||
if ((self->data_size = us_memsink_calculate_size(self->obj)) == 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "Invalid memsink object suffix");
|
||||
return -1;
|
||||
}
|
||||
|
||||
self->frame = us_frame_init();
|
||||
|
||||
if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
goto error;
|
||||
}
|
||||
if ((self->mem = us_memsink_shared_map(self->fd)) == NULL) {
|
||||
if ((self->mem = us_memsink_shared_map(self->fd, self->data_size)) == NULL) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
goto error;
|
||||
}
|
||||
@@ -99,11 +108,11 @@ static PyObject *_MemsinkObject_close(_MemsinkObject *self, PyObject *Py_UNUSED(
|
||||
|
||||
static PyObject *_MemsinkObject_enter(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
|
||||
Py_INCREF(self);
|
||||
return (PyObject *)self;
|
||||
return (PyObject*)self;
|
||||
}
|
||||
|
||||
static PyObject *_MemsinkObject_exit(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
|
||||
return PyObject_CallMethod((PyObject *)self, "close", "");
|
||||
return PyObject_CallMethod((PyObject*)self, "close", "");
|
||||
}
|
||||
|
||||
static int _wait_frame(_MemsinkObject *self) {
|
||||
@@ -139,7 +148,7 @@ static int _wait_frame(_MemsinkObject *self) {
|
||||
if (
|
||||
US_FRAME_COMPARE_GEOMETRY(self->mem, self->frame)
|
||||
&& (self->frame_ts + self->drop_same_frames > now_ts)
|
||||
&& !memcmp(self->frame->data, mem->data, mem->used)
|
||||
&& !memcmp(self->frame->data, us_memsink_get_data(mem), mem->used)
|
||||
) {
|
||||
self->frame_id = mem->id;
|
||||
goto retry;
|
||||
@@ -190,7 +199,7 @@ static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args,
|
||||
}
|
||||
|
||||
us_memsink_shared_s *mem = self->mem;
|
||||
us_frame_set_data(self->frame, mem->data, mem->used);
|
||||
us_frame_set_data(self->frame, us_memsink_get_data(mem), mem->used);
|
||||
US_FRAME_COPY_META(self->mem, self->frame);
|
||||
self->frame_id = mem->id;
|
||||
self->frame_ts = us_get_now_monotonic();
|
||||
@@ -230,7 +239,7 @@ static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args,
|
||||
SET_NUMBER(grab_ts, Double, Float);
|
||||
SET_NUMBER(encode_begin_ts, Double, Float);
|
||||
SET_NUMBER(encode_end_ts, Double, Float);
|
||||
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)self->frame->data, self->frame->used));
|
||||
SET_VALUE("data", PyBytes_FromStringAndSize((const char*)self->frame->data, self->frame->used));
|
||||
|
||||
# undef SET_NUMBER
|
||||
# undef SET_VALUE
|
||||
@@ -305,7 +314,7 @@ PyMODINIT_FUNC PyInit_ustreamer(void) {
|
||||
|
||||
Py_INCREF(&_MemsinkType);
|
||||
|
||||
if (PyModule_AddObject(module, "Memsink", (PyObject *)&_MemsinkType) < 0) {
|
||||
if (PyModule_AddObject(module, "Memsink", (PyObject*)&_MemsinkType) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ us_output_file_s *us_output_file_init(const char *path, bool json) {
|
||||
}
|
||||
|
||||
void us_output_file_write(void *v_output, const us_frame_s *frame) {
|
||||
us_output_file_s *output = (us_output_file_s *)v_output;
|
||||
us_output_file_s *output = v_output;
|
||||
if (output->json) {
|
||||
us_base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
|
||||
fprintf(output->fp,
|
||||
@@ -66,7 +66,7 @@ void us_output_file_write(void *v_output, const us_frame_s *frame) {
|
||||
}
|
||||
|
||||
void us_output_file_destroy(void *v_output) {
|
||||
us_output_file_s *output = (us_output_file_s *)v_output;
|
||||
us_output_file_s *output = v_output;
|
||||
US_DELETE(output->base64_data, free);
|
||||
if (output->fp && output->fp != stdout) {
|
||||
if (fclose(output->fp) < 0) {
|
||||
|
||||
@@ -182,7 +182,7 @@ int main(int argc, char *argv[]) {
|
||||
_output_context_s ctx = {0};
|
||||
|
||||
if (output_path && output_path[0] != '\0') {
|
||||
if ((ctx.v_output = (void *)us_output_file_init(output_path, output_json)) == NULL) {
|
||||
if ((ctx.v_output = (void*)us_output_file_init(output_path, output_json)) == NULL) {
|
||||
return 1;
|
||||
}
|
||||
ctx.write = us_output_file_write;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
|
||||
#define US_VERSION_MAJOR 6
|
||||
#define US_VERSION_MINOR 4
|
||||
#define US_VERSION_MINOR 5
|
||||
|
||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_major, _minor)
|
||||
|
||||
@@ -56,6 +56,11 @@ us_memsink_s *us_memsink_init(
|
||||
|
||||
US_LOG_INFO("Using %s-sink: %s", name, obj);
|
||||
|
||||
if ((sink->data_size = us_memsink_calculate_size(obj)) == 0) {
|
||||
US_LOG_ERROR("%s-sink: Invalid object suffix", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
const mode_t mask = umask(0);
|
||||
sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode);
|
||||
umask(mask);
|
||||
@@ -65,12 +70,12 @@ us_memsink_s *us_memsink_init(
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (sink->server && ftruncate(sink->fd, sizeof(us_memsink_shared_s)) < 0) {
|
||||
if (sink->server && ftruncate(sink->fd, sizeof(us_memsink_shared_s) + sink->data_size) < 0) {
|
||||
US_LOG_PERROR("%s-sink: Can't truncate shared memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((sink->mem = us_memsink_shared_map(sink->fd)) == NULL) {
|
||||
if ((sink->mem = us_memsink_shared_map(sink->fd, sink->data_size)) == NULL) {
|
||||
US_LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
||||
goto error;
|
||||
}
|
||||
@@ -83,7 +88,7 @@ error:
|
||||
|
||||
void us_memsink_destroy(us_memsink_s *sink) {
|
||||
if (sink->mem != NULL) {
|
||||
if (us_memsink_shared_unmap(sink->mem) < 0) {
|
||||
if (us_memsink_shared_unmap(sink->mem, sink->data_size) < 0) {
|
||||
US_LOG_PERROR("%s-sink: Can't unmap shared memory", sink->name);
|
||||
}
|
||||
}
|
||||
@@ -160,9 +165,9 @@ int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *key
|
||||
|
||||
const ldf now = us_get_now_monotonic();
|
||||
|
||||
if (frame->used > US_MEMSINK_MAX_DATA) {
|
||||
if (frame->used > sink->data_size) {
|
||||
US_LOG_ERROR("%s-sink: Can't put frame: is too big (%zu > %zu)",
|
||||
sink->name, frame->used, US_MEMSINK_MAX_DATA);
|
||||
sink->name, frame->used, sink->data_size);
|
||||
return 0; // -2
|
||||
}
|
||||
|
||||
@@ -177,7 +182,7 @@ int us_memsink_server_put(us_memsink_s *sink, const us_frame_s *frame, bool *key
|
||||
*key_requested = sink->mem->key_requested;
|
||||
}
|
||||
|
||||
memcpy(sink->mem->data, frame->data, frame->used);
|
||||
memcpy(us_memsink_get_data(sink->mem), frame->data, frame->used);
|
||||
sink->mem->used = frame->used;
|
||||
US_FRAME_COPY_META(frame, sink->mem);
|
||||
|
||||
@@ -236,7 +241,7 @@ int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *key_reque
|
||||
}
|
||||
|
||||
sink->last_readed_id = sink->mem->id;
|
||||
us_frame_set_data(frame, sink->mem->data, sink->mem->used);
|
||||
us_frame_set_data(frame, us_memsink_get_data(sink->mem), sink->mem->used);
|
||||
US_FRAME_COPY_META(sink->mem, frame);
|
||||
if (key_requested != NULL) { // We don't need it for non-H264 sinks
|
||||
*key_requested = sink->mem->key_requested;
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *obj;
|
||||
uz data_size;
|
||||
bool server;
|
||||
bool rm;
|
||||
uint client_ttl; // Only for server
|
||||
|
||||
72
src/libs/memsinksh.c
Normal file
72
src/libs/memsinksh.c
Normal file
@@ -0,0 +1,72 @@
|
||||
/*****************************************************************************
|
||||
# #
|
||||
# uStreamer - Lightweight and fast MJPEG-HTTP streamer. #
|
||||
# #
|
||||
# Copyright (C) 2018-2023 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 "memsinksh.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
us_memsink_shared_s *us_memsink_shared_map(int fd, uz data_size) {
|
||||
us_memsink_shared_s *mem = mmap(
|
||||
NULL,
|
||||
sizeof(us_memsink_shared_s) + data_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
fd, 0);
|
||||
if (mem == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
assert(mem != NULL);
|
||||
return mem;
|
||||
}
|
||||
|
||||
int us_memsink_shared_unmap(us_memsink_shared_s *mem, uz data_size) {
|
||||
assert(mem != NULL);
|
||||
return munmap(mem, sizeof(us_memsink_shared_s) + data_size);
|
||||
}
|
||||
|
||||
uz us_memsink_calculate_size(const char *obj) {
|
||||
const char *ptr = strrchr(obj, ':');
|
||||
if (ptr == NULL) {
|
||||
ptr = strrchr(obj, '.');
|
||||
}
|
||||
if (ptr != NULL) {
|
||||
ptr += 1;
|
||||
if (!strcasecmp(ptr, "jpeg")) {
|
||||
return 4 * 1024 * 1024;
|
||||
} else if (!strcasecmp(ptr, "h264")) {
|
||||
return 2 * 1024 * 1024;
|
||||
} else if (!strcasecmp(ptr, "raw")) {
|
||||
return 1920 * 1200 * 3; // RGB
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 *us_memsink_get_data(us_memsink_shared_s *mem) {
|
||||
return (u8*)(mem + sizeof(us_memsink_shared_s));
|
||||
}
|
||||
@@ -22,18 +22,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "types.h"
|
||||
|
||||
|
||||
#define US_MEMSINK_MAGIC ((u64)0xCAFEBABECAFEBABE)
|
||||
#define US_MEMSINK_VERSION ((u32)4)
|
||||
|
||||
#ifndef US_CFG_MEMSINK_MAX_DATA
|
||||
# define US_CFG_MEMSINK_MAX_DATA 33554432
|
||||
#endif
|
||||
#define US_MEMSINK_MAX_DATA ((uz)(US_CFG_MEMSINK_MAX_DATA))
|
||||
#define US_MEMSINK_VERSION ((u32)5)
|
||||
|
||||
|
||||
typedef struct {
|
||||
@@ -57,28 +50,11 @@ typedef struct {
|
||||
|
||||
ldf last_client_ts;
|
||||
bool key_requested;
|
||||
|
||||
u8 data[US_MEMSINK_MAX_DATA];
|
||||
} us_memsink_shared_s;
|
||||
|
||||
|
||||
INLINE us_memsink_shared_s *us_memsink_shared_map(int fd) {
|
||||
us_memsink_shared_s *mem = mmap(
|
||||
NULL,
|
||||
sizeof(us_memsink_shared_s),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
fd,
|
||||
0
|
||||
);
|
||||
if (mem == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
assert(mem != NULL);
|
||||
return mem;
|
||||
}
|
||||
us_memsink_shared_s *us_memsink_shared_map(int fd, uz data_size);
|
||||
int us_memsink_shared_unmap(us_memsink_shared_s *mem, uz data_size);
|
||||
|
||||
INLINE int us_memsink_shared_unmap(us_memsink_shared_s *mem) {
|
||||
assert(mem != NULL);
|
||||
return munmap(mem, sizeof(us_memsink_shared_s));
|
||||
}
|
||||
uz us_memsink_calculate_size(const char *obj);
|
||||
u8 *us_memsink_get_data(us_memsink_shared_s *mem);
|
||||
|
||||
@@ -84,7 +84,7 @@ INLINE void us_thread_set_name(const char *name) {
|
||||
# elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
# elif defined(__NetBSD__)
|
||||
pthread_setname_np(pthread_self(), "%s", (void *)name);
|
||||
pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||
# else
|
||||
# error us_thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
||||
# endif
|
||||
|
||||
@@ -166,7 +166,7 @@ void us_encoder_open(us_encoder_s *enc, us_device_s *dev) {
|
||||
|
||||
enc->run->pool = us_workers_pool_init(
|
||||
"JPEG", "jw", n_workers, desired_interval,
|
||||
_worker_job_init, (void *)enc,
|
||||
_worker_job_init, (void*)enc,
|
||||
_worker_job_destroy,
|
||||
_worker_run_job);
|
||||
|
||||
@@ -188,19 +188,19 @@ void us_encoder_get_runtime_params(us_encoder_s *enc, us_encoder_type_e *type, u
|
||||
static void *_worker_job_init(void *v_enc) {
|
||||
us_encoder_job_s *job;
|
||||
US_CALLOC(job, 1);
|
||||
job->enc = (us_encoder_s *)v_enc;
|
||||
job->enc = (us_encoder_s*)v_enc;
|
||||
job->dest = us_frame_init();
|
||||
return (void *)job;
|
||||
return (void*)job;
|
||||
}
|
||||
|
||||
static void _worker_job_destroy(void *v_job) {
|
||||
us_encoder_job_s *job = (us_encoder_job_s *)v_job;
|
||||
us_encoder_job_s *job = v_job;
|
||||
us_frame_destroy(job->dest);
|
||||
free(job);
|
||||
}
|
||||
|
||||
static bool _worker_run_job(us_worker_s *wr) {
|
||||
us_encoder_job_s *job = (us_encoder_job_s *)wr->job;
|
||||
us_encoder_job_s *job = wr->job;
|
||||
us_encoder_s *enc = job->enc; // Just for _ER()
|
||||
const us_frame_s *src = &job->hw->raw;
|
||||
us_frame_s *dest = job->dest;
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
struct jpeg_destination_mgr mgr; // Default manager
|
||||
JOCTET *buf; // Start of buffer
|
||||
struct jpeg_destination_mgr mgr; // Default manager
|
||||
JOCTET *buf; // Start of buffer
|
||||
us_frame_s *frame;
|
||||
} _jpeg_dest_manager_s;
|
||||
|
||||
@@ -63,7 +63,12 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned q
|
||||
jpeg.image_width = src->width;
|
||||
jpeg.image_height = src->height;
|
||||
jpeg.input_components = 3;
|
||||
jpeg.in_color_space = ((src->format == V4L2_PIX_FMT_YUYV || src->format == V4L2_PIX_FMT_UYVY) ? JCS_YCbCr : JCS_RGB);
|
||||
switch (src->format) {
|
||||
case V4L2_PIX_FMT_YUYV:
|
||||
case V4L2_PIX_FMT_YVYU:
|
||||
case V4L2_PIX_FMT_UYVY: jpeg.in_color_space = JCS_YCbCr; break;
|
||||
default: jpeg.in_color_space = JCS_RGB; break;
|
||||
}
|
||||
|
||||
jpeg_set_defaults(&jpeg);
|
||||
jpeg_set_quality(&jpeg, quality, TRUE);
|
||||
@@ -89,12 +94,12 @@ void us_cpu_encoder_compress(const us_frame_s *src, us_frame_s *dest, unsigned q
|
||||
|
||||
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame) {
|
||||
if (jpeg->dest == NULL) {
|
||||
assert((jpeg->dest = (struct jpeg_destination_mgr *)(*jpeg->mem->alloc_small)(
|
||||
assert((jpeg->dest = (struct jpeg_destination_mgr*)(*jpeg->mem->alloc_small)(
|
||||
(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
|
||||
)) != NULL);
|
||||
}
|
||||
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
_jpeg_dest_manager_s *const 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;
|
||||
@@ -221,10 +226,10 @@ static void _jpeg_write_scanlines_bgr24(struct jpeg_compress_struct *jpeg, const
|
||||
#define JPEG_OUTPUT_BUFFER_SIZE ((size_t)4096)
|
||||
|
||||
static void _jpeg_init_destination(j_compress_ptr jpeg) {
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s*)jpeg->dest;
|
||||
|
||||
// Allocate the output buffer - it will be released when done with image
|
||||
assert((dest->buf = (JOCTET *)(*jpeg->mem->alloc_small)(
|
||||
assert((dest->buf = (JOCTET*)(*jpeg->mem->alloc_small)(
|
||||
(j_common_ptr) jpeg, JPOOL_IMAGE, JPEG_OUTPUT_BUFFER_SIZE * sizeof(JOCTET)
|
||||
)) != NULL);
|
||||
|
||||
@@ -235,7 +240,7 @@ static void _jpeg_init_destination(j_compress_ptr jpeg) {
|
||||
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
|
||||
// Called whenever local jpeg buffer fills up
|
||||
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s*)jpeg->dest;
|
||||
|
||||
us_frame_append_data(dest->frame, dest->buf, JPEG_OUTPUT_BUFFER_SIZE);
|
||||
|
||||
@@ -249,7 +254,7 @@ 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.
|
||||
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s *)jpeg->dest;
|
||||
_jpeg_dest_manager_s *const dest = (_jpeg_dest_manager_s*)jpeg->dest;
|
||||
const size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
|
||||
|
||||
// Write any data remaining in the buffer.
|
||||
|
||||
@@ -92,7 +92,7 @@ enum _US_OPT_VALUES {
|
||||
_O_##x_prefix##_RM, \
|
||||
_O_##x_prefix##_CLIENT_TTL, \
|
||||
_O_##x_prefix##_TIMEOUT,
|
||||
ADD_SINK(SINK)
|
||||
ADD_SINK(JPEG_SINK)
|
||||
ADD_SINK(RAW_SINK)
|
||||
ADD_SINK(H264_SINK)
|
||||
_O_H264_BITRATE,
|
||||
@@ -184,18 +184,25 @@ static const struct option _LONG_OPTS[] = {
|
||||
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
||||
|
||||
# define ADD_SINK(x_opt, x_prefix) \
|
||||
{x_opt "sink", required_argument, NULL, _O_##x_prefix}, \
|
||||
{x_opt "sink-mode", required_argument, NULL, _O_##x_prefix##_MODE}, \
|
||||
{x_opt "sink-rm", no_argument, NULL, _O_##x_prefix##_RM}, \
|
||||
{x_opt "sink-client-ttl", required_argument, NULL, _O_##x_prefix##_CLIENT_TTL}, \
|
||||
{x_opt "sink-timeout", required_argument, NULL, _O_##x_prefix##_TIMEOUT},
|
||||
ADD_SINK("", SINK)
|
||||
ADD_SINK("raw-", RAW_SINK)
|
||||
ADD_SINK("h264-", H264_SINK)
|
||||
{x_opt "-sink", required_argument, NULL, _O_##x_prefix}, \
|
||||
{x_opt "-sink-mode", required_argument, NULL, _O_##x_prefix##_MODE}, \
|
||||
{x_opt "-sink-rm", no_argument, NULL, _O_##x_prefix##_RM}, \
|
||||
{x_opt "-sink-client-ttl", required_argument, NULL, _O_##x_prefix##_CLIENT_TTL}, \
|
||||
{x_opt "-sink-timeout", required_argument, NULL, _O_##x_prefix##_TIMEOUT},
|
||||
ADD_SINK("jpeg", JPEG_SINK)
|
||||
ADD_SINK("raw", RAW_SINK)
|
||||
ADD_SINK("h264", H264_SINK)
|
||||
# undef ADD_SINK
|
||||
// Extra opts for H.264
|
||||
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
|
||||
{"h264-gop", required_argument, NULL, _O_H264_GOP},
|
||||
{"h264-m2m-device", required_argument, NULL, _O_H264_M2M_DEVICE},
|
||||
# undef ADD_SINK
|
||||
// Compatibility
|
||||
{"sink", required_argument, NULL, _O_JPEG_SINK},
|
||||
{"sink-mode", required_argument, NULL, _O_JPEG_SINK_MODE},
|
||||
{"sink-rm", no_argument, NULL, _O_JPEG_SINK_RM},
|
||||
{"sink-client-ttl", required_argument, NULL, _O_JPEG_SINK_CLIENT_TTL},
|
||||
{"sink-timeout", required_argument, NULL, _O_JPEG_SINK_TIMEOUT},
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
{"gpio-device", required_argument, NULL, _O_GPIO_DEVICE},
|
||||
@@ -432,17 +439,17 @@ int options_parse(us_options_s *options, us_device_s *dev, us_encoder_s *enc, us
|
||||
|
||||
# define ADD_SINK(x_opt, x_lp, x_up) \
|
||||
case _O_##x_up: OPT_SET(x_lp##_name, optarg); \
|
||||
case _O_##x_up##_MODE: OPT_NUMBER("--" #x_opt "sink-mode", x_lp##_mode, INT_MIN, INT_MAX, 8); \
|
||||
case _O_##x_up##_MODE: OPT_NUMBER("--" #x_opt "-sink-mode", x_lp##_mode, INT_MIN, INT_MAX, 8); \
|
||||
case _O_##x_up##_RM: OPT_SET(x_lp##_rm, true); \
|
||||
case _O_##x_up##_CLIENT_TTL: OPT_NUMBER("--" #x_opt "sink-client-ttl", x_lp##_client_ttl, 1, 60, 0); \
|
||||
case _O_##x_up##_TIMEOUT: OPT_NUMBER("--" #x_opt "sink-timeout", x_lp##_timeout, 1, 60, 0);
|
||||
ADD_SINK("", jpeg_sink, SINK)
|
||||
ADD_SINK("raw-", raw_sink, RAW_SINK)
|
||||
ADD_SINK("h264-", h264_sink, H264_SINK)
|
||||
case _O_##x_up##_CLIENT_TTL: OPT_NUMBER("--" #x_opt "-sink-client-ttl", x_lp##_client_ttl, 1, 60, 0); \
|
||||
case _O_##x_up##_TIMEOUT: OPT_NUMBER("--" #x_opt "-sink-timeout", x_lp##_timeout, 1, 60, 0);
|
||||
ADD_SINK("jpeg", jpeg_sink, JPEG_SINK)
|
||||
ADD_SINK("raw", raw_sink, RAW_SINK)
|
||||
ADD_SINK("h264", h264_sink, H264_SINK)
|
||||
# undef ADD_SINK
|
||||
case _O_H264_BITRATE: OPT_NUMBER("--h264-bitrate", stream->h264_bitrate, 25, 20000, 0);
|
||||
case _O_H264_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
|
||||
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
|
||||
# undef ADD_SINK
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
case _O_GPIO_DEVICE: OPT_SET(us_g_gpio.path, optarg);
|
||||
@@ -677,18 +684,20 @@ static void _help(FILE *fp, const us_device_s *dev, const us_encoder_s *enc, con
|
||||
# define ADD_SINK(x_name, x_opt) \
|
||||
SAY(x_name " sink options:"); \
|
||||
SAY("══════════════════"); \
|
||||
SAY(" --" x_opt "sink <name> ──────────── Use the shared memory to sink " x_name " frames. Default: disabled.\n"); \
|
||||
SAY(" --" x_opt "sink-mode <mode> ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
|
||||
SAY(" --" x_opt "sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
||||
SAY(" --" x_opt "sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
||||
SAY(" --" x_opt "sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
||||
ADD_SINK("JPEG", "")
|
||||
ADD_SINK("RAW", "raw-")
|
||||
ADD_SINK("H264", "h264-")
|
||||
SAY(" --" x_opt "-sink <name> ──────────── Use the shared memory to sink " x_name " frames. Default: disabled."); \
|
||||
SAY(" The name should end with a suffix \"." x_opt "\" or \"." x_opt "\"."); \
|
||||
SAY(" Default: disabled.\n"); \
|
||||
SAY(" --" x_opt "-sink-mode <mode> ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
|
||||
SAY(" --" x_opt "-sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
||||
SAY(" --" x_opt "-sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
||||
SAY(" --" x_opt "-sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
||||
ADD_SINK("JPEG", "jpeg")
|
||||
ADD_SINK("RAW", "raw")
|
||||
ADD_SINK("H264", "h264")
|
||||
# undef ADD_SINK
|
||||
SAY(" --h264-bitrate <kbps> ───────── H264 bitrate in Kbps. Default: %u.\n", stream->h264_bitrate);
|
||||
SAY(" --h264-gop <N> ──────────────── Interval between keyframes. Default: %u.\n", stream->h264_gop);
|
||||
SAY(" --h264-m2m-device </dev/path> ─ Path to V4L2 M2M encoder device. Default: auto select.\n");
|
||||
# undef ADD_SINK
|
||||
# ifdef WITH_GPIO
|
||||
SAY("GPIO options:");
|
||||
SAY("═════════════");
|
||||
|
||||
@@ -62,7 +62,7 @@ us_workers_pool_s *us_workers_pool_init(
|
||||
WR(pool) = pool;
|
||||
WR(job) = job_init(job_init_arg);
|
||||
|
||||
US_THREAD_CREATE(WR(tid), _worker_thread, (void *)&(pool->workers[number]));
|
||||
US_THREAD_CREATE(WR(tid), _worker_thread, (void*)&(pool->workers[number]));
|
||||
pool->free_workers += 1;
|
||||
|
||||
# undef WR
|
||||
@@ -176,7 +176,7 @@ long double us_workers_pool_get_fluency_delay(us_workers_pool_s *pool, const us_
|
||||
}
|
||||
|
||||
static void *_worker_thread(void *v_worker) {
|
||||
us_worker_s *wr = (us_worker_s *)v_worker;
|
||||
us_worker_s *wr = v_worker;
|
||||
|
||||
US_THREAD_SETTLE("%s", wr->name);
|
||||
US_LOG_DEBUG("Hello! I am a worker %s ^_^", wr->name);
|
||||
|
||||
@@ -274,7 +274,7 @@ static void *_follower_thread(void *v_unix_follow) {
|
||||
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
const bool online = !connect(fd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
const bool online = !connect(fd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
atomic_store(&_g_ustreamer_online, online);
|
||||
US_CLOSE_FD(fd); // cppcheck-suppress unreadVariable
|
||||
|
||||
|
||||
Reference in New Issue
Block a user