mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-27 12:16:31 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
408157c82b | ||
|
|
7356dea737 | ||
|
|
87a75a816a | ||
|
|
b6a2332207 | ||
|
|
34c0dcb1ce | ||
|
|
283f31a5a6 | ||
|
|
2f1264c916 | ||
|
|
69e7cbf746 |
@@ -1,7 +1,7 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
current_version = 6.3
|
current_version = 6.5
|
||||||
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
parse = (?P<major>\d+)\.(?P<minor>\d+)
|
||||||
serialize =
|
serialize =
|
||||||
{major}.{minor}
|
{major}.{minor}
|
||||||
|
|||||||
@@ -22,6 +22,23 @@
|
|||||||
|
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <speex/speex_resampler.h>
|
||||||
|
#include <opus/opus.h>
|
||||||
|
|
||||||
|
#include "uslibs/types.h"
|
||||||
|
#include "uslibs/tools.h"
|
||||||
|
#include "uslibs/array.h"
|
||||||
|
#include "uslibs/ring.h"
|
||||||
|
#include "uslibs/threading.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
|
||||||
#define _JLOG_PERROR_ALSA(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, snd_strerror(_err))
|
#define _JLOG_PERROR_ALSA(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, snd_strerror(_err))
|
||||||
#define _JLOG_PERROR_RES(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, speex_resampler_strerror(_err))
|
#define _JLOG_PERROR_RES(_err, _prefix, _msg, ...) US_JLOG_ERROR(_prefix, _msg ": %s", ##__VA_ARGS__, speex_resampler_strerror(_err))
|
||||||
@@ -31,7 +48,7 @@
|
|||||||
// - https://github.com/xiph/opus/blob/7b05f44/src/opus_demo.c#L368
|
// - https://github.com/xiph/opus/blob/7b05f44/src/opus_demo.c#L368
|
||||||
#define _HZ_TO_FRAMES(_hz) (6 * (_hz) / 50) // 120ms
|
#define _HZ_TO_FRAMES(_hz) (6 * (_hz) / 50) // 120ms
|
||||||
#define _HZ_TO_BUF16(_hz) (_HZ_TO_FRAMES(_hz) * 2) // One stereo frame = (16bit L) + (16bit R)
|
#define _HZ_TO_BUF16(_hz) (_HZ_TO_FRAMES(_hz) * 2) // One stereo frame = (16bit L) + (16bit R)
|
||||||
#define _HZ_TO_BUF8(_hz) (_HZ_TO_BUF16(_hz) * sizeof(int16_t))
|
#define _HZ_TO_BUF8(_hz) (_HZ_TO_BUF16(_hz) * sizeof(s16))
|
||||||
|
|
||||||
#define _MIN_PCM_HZ 8000
|
#define _MIN_PCM_HZ 8000
|
||||||
#define _MAX_PCM_HZ 192000
|
#define _MAX_PCM_HZ 192000
|
||||||
@@ -41,13 +58,13 @@
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int16_t data[_MAX_BUF16];
|
s16 data[_MAX_BUF16];
|
||||||
} _pcm_buffer_s;
|
} _pcm_buffer_s;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t data[_MAX_BUF8]; // Worst case
|
u8 data[_MAX_BUF8]; // Worst case
|
||||||
size_t used;
|
uz used;
|
||||||
uint64_t pts;
|
u64 pts;
|
||||||
} _enc_buffer_s;
|
} _enc_buffer_s;
|
||||||
|
|
||||||
|
|
||||||
@@ -71,7 +88,7 @@ bool us_audio_probe(const char *name) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
us_audio_s *us_audio_init(const char *name, unsigned pcm_hz) {
|
us_audio_s *us_audio_init(const char *name, uint pcm_hz) {
|
||||||
us_audio_s *audio;
|
us_audio_s *audio;
|
||||||
US_CALLOC(audio, 1);
|
US_CALLOC(audio, 1);
|
||||||
audio->pcm_hz = pcm_hz;
|
audio->pcm_hz = pcm_hz;
|
||||||
@@ -162,7 +179,7 @@ void us_audio_destroy(us_audio_s *audio) {
|
|||||||
free(audio);
|
free(audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
int us_audio_get_encoded(us_audio_s *audio, uint8_t *data, size_t *size, uint64_t *pts) {
|
int us_audio_get_encoded(us_audio_s *audio, u8 *data, uz *size, u64 *pts) {
|
||||||
if (atomic_load(&audio->stop)) {
|
if (atomic_load(&audio->stop)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -197,8 +214,8 @@ static _enc_buffer_s *_enc_buffer_init(void) {
|
|||||||
static void *_pcm_thread(void *v_audio) {
|
static void *_pcm_thread(void *v_audio) {
|
||||||
US_THREAD_SETTLE("us_a_pcm");
|
US_THREAD_SETTLE("us_a_pcm");
|
||||||
|
|
||||||
us_audio_s *const audio = (us_audio_s *)v_audio;
|
us_audio_s *const audio = v_audio;
|
||||||
uint8_t in[_MAX_BUF8];
|
u8 in[_MAX_BUF8];
|
||||||
|
|
||||||
while (!atomic_load(&audio->stop)) {
|
while (!atomic_load(&audio->stop)) {
|
||||||
const int frames = snd_pcm_readi(audio->pcm, in, audio->pcm_frames);
|
const int frames = snd_pcm_readi(audio->pcm, in, audio->pcm_frames);
|
||||||
@@ -227,8 +244,8 @@ static void *_pcm_thread(void *v_audio) {
|
|||||||
static void *_encoder_thread(void *v_audio) {
|
static void *_encoder_thread(void *v_audio) {
|
||||||
US_THREAD_SETTLE("us_a_enc");
|
US_THREAD_SETTLE("us_a_enc");
|
||||||
|
|
||||||
us_audio_s *const audio = (us_audio_s *)v_audio;
|
us_audio_s *const audio = v_audio;
|
||||||
int16_t in_res[_MAX_BUF16];
|
s16 in_res[_MAX_BUF16];
|
||||||
|
|
||||||
while (!atomic_load(&audio->stop)) {
|
while (!atomic_load(&audio->stop)) {
|
||||||
const int in_ri = us_ring_consumer_acquire(audio->pcm_ring, 0.1);
|
const int in_ri = us_ring_consumer_acquire(audio->pcm_ring, 0.1);
|
||||||
@@ -237,11 +254,11 @@ static void *_encoder_thread(void *v_audio) {
|
|||||||
}
|
}
|
||||||
_pcm_buffer_s *const in = audio->pcm_ring->items[in_ri];
|
_pcm_buffer_s *const in = audio->pcm_ring->items[in_ri];
|
||||||
|
|
||||||
int16_t *in_ptr;
|
s16 *in_ptr;
|
||||||
if (audio->res != NULL) {
|
if (audio->res != NULL) {
|
||||||
assert(audio->pcm_hz != _ENCODER_INPUT_HZ);
|
assert(audio->pcm_hz != _ENCODER_INPUT_HZ);
|
||||||
uint32_t in_count = audio->pcm_frames;
|
u32 in_count = audio->pcm_frames;
|
||||||
uint32_t out_count = _HZ_TO_FRAMES(_ENCODER_INPUT_HZ);
|
u32 out_count = _HZ_TO_FRAMES(_ENCODER_INPUT_HZ);
|
||||||
speex_resampler_process_interleaved_int(audio->res, in->data, &in_count, in_res, &out_count);
|
speex_resampler_process_interleaved_int(audio->res, in->data, &in_count, in_res, &out_count);
|
||||||
in_ptr = in_res;
|
in_ptr = in_res;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -22,50 +22,40 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
#include <speex/speex_resampler.h>
|
#include <speex/speex_resampler.h>
|
||||||
#include <opus/opus.h>
|
#include <opus/opus.h>
|
||||||
|
|
||||||
#include "uslibs/tools.h"
|
#include "uslibs/types.h"
|
||||||
#include "uslibs/array.h"
|
|
||||||
#include "uslibs/ring.h"
|
#include "uslibs/ring.h"
|
||||||
#include "uslibs/threading.h"
|
|
||||||
|
|
||||||
#include "logging.h"
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
snd_pcm_t *pcm;
|
snd_pcm_t *pcm;
|
||||||
unsigned pcm_hz;
|
uint pcm_hz;
|
||||||
unsigned pcm_frames;
|
uint pcm_frames;
|
||||||
size_t pcm_size;
|
uz pcm_size;
|
||||||
snd_pcm_hw_params_t *pcm_params;
|
snd_pcm_hw_params_t *pcm_params;
|
||||||
SpeexResamplerState *res;
|
SpeexResamplerState *res;
|
||||||
OpusEncoder *enc;
|
OpusEncoder *enc;
|
||||||
|
|
||||||
us_ring_s *pcm_ring;
|
us_ring_s *pcm_ring;
|
||||||
us_ring_s *enc_ring;
|
us_ring_s *enc_ring;
|
||||||
uint32_t pts;
|
u32 pts;
|
||||||
|
|
||||||
pthread_t pcm_tid;
|
pthread_t pcm_tid;
|
||||||
pthread_t enc_tid;
|
pthread_t enc_tid;
|
||||||
bool tids_created;
|
bool tids_created;
|
||||||
atomic_bool stop;
|
atomic_bool stop;
|
||||||
} us_audio_s;
|
} us_audio_s;
|
||||||
|
|
||||||
|
|
||||||
bool us_audio_probe(const char *name);
|
bool us_audio_probe(const char *name);
|
||||||
|
|
||||||
us_audio_s *us_audio_init(const char *name, unsigned pcm_hz);
|
us_audio_s *us_audio_init(const char *name, uint pcm_hz);
|
||||||
void us_audio_destroy(us_audio_s *audio);
|
void us_audio_destroy(us_audio_s *audio);
|
||||||
|
|
||||||
int us_audio_get_encoded(us_audio_s *audio, uint8_t *data, size_t *size, uint64_t *pts);
|
int us_audio_get_encoded(us_audio_s *audio, u8 *data, uz *size, u64 *pts);
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ us_janus_client_s *us_janus_client_init(janus_callbacks *gw, janus_plugin_sessio
|
|||||||
client->session = session;
|
client->session = session;
|
||||||
atomic_init(&client->transmit, false);
|
atomic_init(&client->transmit, false);
|
||||||
atomic_init(&client->transmit_audio, false);
|
atomic_init(&client->transmit_audio, false);
|
||||||
|
atomic_init(&client->video_orient, 0);
|
||||||
|
|
||||||
atomic_init(&client->stop, false);
|
atomic_init(&client->stop, false);
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ static void *_audio_thread(void *v_client) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void *_common_thread(void *v_client, bool video) {
|
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);
|
us_ring_s *const ring = (video ? client->video_ring : client->audio_ring);
|
||||||
assert(ring != NULL); // Audio may be NULL
|
assert(ring != NULL); // Audio may be NULL
|
||||||
|
|
||||||
@@ -131,6 +132,7 @@ static void *_common_thread(void *v_client, bool video) {
|
|||||||
# endif
|
# endif
|
||||||
};
|
};
|
||||||
janus_plugin_rtp_extensions_reset(&packet.extensions);
|
janus_plugin_rtp_extensions_reset(&packet.extensions);
|
||||||
|
|
||||||
/*if (rtp->zero_playout_delay) {
|
/*if (rtp->zero_playout_delay) {
|
||||||
// https://github.com/pikvm/pikvm/issues/784
|
// https://github.com/pikvm/pikvm/issues/784
|
||||||
packet.extensions.min_delay = 0;
|
packet.extensions.min_delay = 0;
|
||||||
@@ -142,6 +144,13 @@ static void *_common_thread(void *v_client, bool video) {
|
|||||||
packet.extensions.max_delay = 300; // == 3s, i.e. 10ms granularity
|
packet.extensions.max_delay = 300; // == 3s, i.e. 10ms granularity
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
if (rtp.video) {
|
||||||
|
const uint video_orient = atomic_load(&client->video_orient);
|
||||||
|
if (video_orient != 0) {
|
||||||
|
packet.extensions.video_rotation = video_orient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
client->gw->relay_rtp(client->session, &packet);
|
client->gw->relay_rtp(client->session, &packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ typedef struct us_janus_client_sx {
|
|||||||
janus_plugin_session *session;
|
janus_plugin_session *session;
|
||||||
atomic_bool transmit;
|
atomic_bool transmit;
|
||||||
atomic_bool transmit_audio;
|
atomic_bool transmit_audio;
|
||||||
|
atomic_uint video_orient;
|
||||||
|
|
||||||
pthread_t video_tid;
|
pthread_t video_tid;
|
||||||
pthread_t audio_tid;
|
pthread_t audio_tid;
|
||||||
|
|||||||
@@ -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) {
|
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);
|
US_FRAME_COPY_META(mem, frame);
|
||||||
*frame_id = mem->id;
|
*frame_id = mem->id;
|
||||||
mem->last_client_ts = us_get_now_monotonic();
|
mem->last_client_ts = us_get_now_monotonic();
|
||||||
|
|||||||
@@ -136,12 +136,18 @@ static void *_video_sink_thread(void *arg) {
|
|||||||
int fd = -1;
|
int fd = -1;
|
||||||
us_memsink_shared_s *mem = NULL;
|
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) {
|
if ((fd = shm_open(_g_config->video_sink_name, O_RDWR, 0)) <= 0) {
|
||||||
US_ONCE({ US_JLOG_PERROR("video", "Can't open memsink"); });
|
US_ONCE({ US_JLOG_PERROR("video", "Can't open memsink"); });
|
||||||
goto close_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"); });
|
US_ONCE({ US_JLOG_PERROR("video", "Can't map memsink"); });
|
||||||
goto close_memsink;
|
goto close_memsink;
|
||||||
}
|
}
|
||||||
@@ -178,7 +184,10 @@ static void *_video_sink_thread(void *arg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
close_memsink:
|
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_CLOSE_FD(fd);
|
||||||
US_JLOG_INFO("video", "Memsink closed");
|
US_JLOG_INFO("video", "Memsink closed");
|
||||||
sleep(1); // error_delay
|
sleep(1); // error_delay
|
||||||
@@ -453,12 +462,25 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
|
|
||||||
} else if (!strcmp(request_str, "watch")) {
|
} else if (!strcmp(request_str, "watch")) {
|
||||||
bool with_audio = false;
|
bool with_audio = false;
|
||||||
|
uint video_orient = 0;
|
||||||
{
|
{
|
||||||
json_t *const params = json_object_get(msg, "params");
|
json_t *const params = json_object_get(msg, "params");
|
||||||
if (params != NULL) {
|
if (params != NULL) {
|
||||||
json_t *const audio = json_object_get(params, "audio");
|
{
|
||||||
if (audio != NULL && json_is_boolean(audio)) {
|
json_t *const obj = json_object_get(params, "audio");
|
||||||
with_audio = (_g_rtpa != NULL && json_boolean_value(audio));
|
if (obj != NULL && json_is_boolean(obj)) {
|
||||||
|
with_audio = (_g_rtpa != NULL && json_boolean_value(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
json_t *const obj = json_object_get(params, "orientation");
|
||||||
|
if (obj != NULL && json_is_integer(obj)) {
|
||||||
|
video_orient = json_integer_value(obj);
|
||||||
|
switch (video_orient) {
|
||||||
|
case 90: case 180: case 270: break;
|
||||||
|
default: video_orient = 0; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,6 +520,7 @@ static struct janus_plugin_result *_plugin_handle_message(
|
|||||||
US_LIST_ITERATE(_g_clients, client, {
|
US_LIST_ITERATE(_g_clients, client, {
|
||||||
if (client->session == session) {
|
if (client->session == session) {
|
||||||
atomic_store(&client->transmit_audio, with_audio);
|
atomic_store(&client->transmit_audio, with_audio);
|
||||||
|
atomic_store(&client->video_orient, video_orient);
|
||||||
}
|
}
|
||||||
has_listeners = (has_listeners || atomic_load(&client->transmit_audio));
|
has_listeners = (has_listeners || atomic_load(&client->transmit_audio));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ void us_rtp_write_header(us_rtp_s *rtp, u32 pts, bool marked) {
|
|||||||
++rtp->seq;
|
++rtp->seq;
|
||||||
|
|
||||||
# define WRITE_BE_U32(x_offset, x_value) \
|
# 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(0, word0);
|
||||||
WRITE_BE_U32(4, pts);
|
WRITE_BE_U32(4, pts);
|
||||||
WRITE_BE_U32(8, rtp->ssrc);
|
WRITE_BE_U32(8, rtp->ssrc);
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
|
|||||||
"a=rtcp-fb:%u goog-remb" RN
|
"a=rtcp-fb:%u goog-remb" RN
|
||||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||||
"a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN
|
"a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN
|
||||||
|
"a=extmap:2 urn:3gpp:video-orientation" RN
|
||||||
"a=sendonly" RN,
|
"a=sendonly" RN,
|
||||||
pl, pl, pl, pl,
|
pl, pl, pl, pl,
|
||||||
pl, pl, pl,
|
pl, pl, pl,
|
||||||
|
|||||||
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.
|
.\" Manpage for ustreamer-dump.
|
||||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||||
.TH USTREAMER-DUMP 1 "version 6.3" "January 2021"
|
.TH USTREAMER-DUMP 1 "version 6.5" "January 2021"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer-dump \- Dump uStreamer's memory sink to file
|
ustreamer-dump \- Dump uStreamer's memory sink to file
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" Manpage for ustreamer.
|
.\" Manpage for ustreamer.
|
||||||
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
.\" Open an issue or pull request to https://github.com/pikvm/ustreamer to correct errors or typos
|
||||||
.TH USTREAMER 1 "version 6.3" "November 2020"
|
.TH USTREAMER 1 "version 6.5" "November 2020"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
ustreamer \- stream MJPEG video from any V4L2 device to the network
|
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"
|
.SS "JPEG sink options"
|
||||||
With shared memory sink you can write a stream to a file. See \fBustreamer-dump\fR(1) for more info.
|
With shared memory sink you can write a stream to a file. See \fBustreamer-dump\fR(1) for more info.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-sink\ \fIname
|
.BR \-\-jpeg\-sink\ \fIname
|
||||||
Use the specified shared memory object to sink JPEG frames. Default: disabled.
|
Use the specified shared memory object to sink JPEG frames. The name should end with a suffix ".jpeg" or ":jpeg". Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-sink\-mode\ \fImode
|
.BR \-\-jpeg\-sink\-mode\ \fImode
|
||||||
Set JPEG sink permissions (like 777). Default: 660.
|
Set JPEG sink permissions (like 777). Default: 660.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-sink\-rm
|
.BR \-\-jpeg\-sink\-rm
|
||||||
Remove shared memory on stop. Default: disabled.
|
Remove shared memory on stop. Default: disabled.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-sink\-client\-ttl\ \fIsec
|
.BR \-\-jpeg\-sink\-client\-ttl\ \fIsec
|
||||||
Client TTL. Default: 10.
|
Client TTL. Default: 10.
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-sink\-timeout\ \fIsec
|
.BR \-\-jpeg\-sink\-timeout\ \fIsec
|
||||||
Timeout for lock. Default: 1.
|
Timeout for lock. Default: 1.
|
||||||
|
|
||||||
.SS "H264 sink options"
|
.SS "H264 sink options"
|
||||||
.TP
|
.TP
|
||||||
.BR \-\-h264\-sink\ \fIname
|
.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
|
.TP
|
||||||
.BR \-\-h264\-sink\-mode\ \fImode
|
.BR \-\-h264\-sink\-mode\ \fImode
|
||||||
Set H264 sink permissions (like 777). Default: 660.
|
Set H264 sink permissions (like 777). Default: 660.
|
||||||
@@ -253,6 +253,22 @@ Interval between keyframes. Default: 30.
|
|||||||
.BR \-\-h264\-m2m\-device\ \fI/dev/path
|
.BR \-\-h264\-m2m\-device\ \fI/dev/path
|
||||||
Path to V4L2 mem-to-mem encoder device. Default: auto-select.
|
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"
|
.SS "Process options"
|
||||||
.TP
|
.TP
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
pkgname=ustreamer
|
pkgname=ustreamer
|
||||||
pkgver=6.3
|
pkgver=6.5
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
pkgdesc="Lightweight and fast MJPEG-HTTP streamer"
|
||||||
url="https://github.com/pikvm/ustreamer"
|
url="https://github.com/pikvm/ustreamer"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=ustreamer
|
PKG_NAME:=ustreamer
|
||||||
PKG_VERSION:=6.3
|
PKG_VERSION:=6.5
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
PKG_MAINTAINER:=Maxim Devaev <mdevaev@gmail.com>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def _find_sources(suffix: str) -> list[str]:
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
setup(
|
setup(
|
||||||
name="ustreamer",
|
name="ustreamer",
|
||||||
version="6.3",
|
version="6.5",
|
||||||
description="uStreamer tools",
|
description="uStreamer tools",
|
||||||
author="Maxim Devaev",
|
author="Maxim Devaev",
|
||||||
author_email="mdevaev@gmail.com",
|
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 lock_timeout;
|
||||||
double wait_timeout;
|
double wait_timeout;
|
||||||
double drop_same_frames;
|
double drop_same_frames;
|
||||||
|
uz data_size;
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
us_memsink_shared_s *mem;
|
us_memsink_shared_s *mem;
|
||||||
@@ -37,7 +38,10 @@ typedef struct {
|
|||||||
|
|
||||||
|
|
||||||
static void _MemsinkObject_destroy_internals(_MemsinkObject *self) {
|
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_CLOSE_FD(self->fd);
|
||||||
US_DELETE(self->frame, us_frame_destroy);
|
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);
|
SET_DOUBLE(drop_same_frames, >= 0);
|
||||||
# undef SET_DOUBLE
|
# 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();
|
self->frame = us_frame_init();
|
||||||
|
|
||||||
if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
|
if ((self->fd = shm_open(self->obj, O_RDWR, 0)) == -1) {
|
||||||
PyErr_SetFromErrno(PyExc_OSError);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
goto error;
|
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);
|
PyErr_SetFromErrno(PyExc_OSError);
|
||||||
goto error;
|
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)) {
|
static PyObject *_MemsinkObject_enter(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
|
||||||
Py_INCREF(self);
|
Py_INCREF(self);
|
||||||
return (PyObject *)self;
|
return (PyObject*)self;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *_MemsinkObject_exit(_MemsinkObject *self, PyObject *Py_UNUSED(ignored)) {
|
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) {
|
static int _wait_frame(_MemsinkObject *self) {
|
||||||
@@ -139,7 +148,7 @@ static int _wait_frame(_MemsinkObject *self) {
|
|||||||
if (
|
if (
|
||||||
US_FRAME_COMPARE_GEOMETRY(self->mem, self->frame)
|
US_FRAME_COMPARE_GEOMETRY(self->mem, self->frame)
|
||||||
&& (self->frame_ts + self->drop_same_frames > now_ts)
|
&& (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;
|
self->frame_id = mem->id;
|
||||||
goto retry;
|
goto retry;
|
||||||
@@ -190,7 +199,7 @@ static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
us_memsink_shared_s *mem = self->mem;
|
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);
|
US_FRAME_COPY_META(self->mem, self->frame);
|
||||||
self->frame_id = mem->id;
|
self->frame_id = mem->id;
|
||||||
self->frame_ts = us_get_now_monotonic();
|
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(grab_ts, Double, Float);
|
||||||
SET_NUMBER(encode_begin_ts, Double, Float);
|
SET_NUMBER(encode_begin_ts, Double, Float);
|
||||||
SET_NUMBER(encode_end_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_NUMBER
|
||||||
# undef SET_VALUE
|
# undef SET_VALUE
|
||||||
@@ -305,7 +314,7 @@ PyMODINIT_FUNC PyInit_ustreamer(void) {
|
|||||||
|
|
||||||
Py_INCREF(&_MemsinkType);
|
Py_INCREF(&_MemsinkType);
|
||||||
|
|
||||||
if (PyModule_AddObject(module, "Memsink", (PyObject *)&_MemsinkType) < 0) {
|
if (PyModule_AddObject(module, "Memsink", (PyObject*)&_MemsinkType) < 0) {
|
||||||
return NULL;
|
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) {
|
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) {
|
if (output->json) {
|
||||||
us_base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
|
us_base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
|
||||||
fprintf(output->fp,
|
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) {
|
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);
|
US_DELETE(output->base64_data, free);
|
||||||
if (output->fp && output->fp != stdout) {
|
if (output->fp && output->fp != stdout) {
|
||||||
if (fclose(output->fp) < 0) {
|
if (fclose(output->fp) < 0) {
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ int main(int argc, char *argv[]) {
|
|||||||
_output_context_s ctx = {0};
|
_output_context_s ctx = {0};
|
||||||
|
|
||||||
if (output_path && output_path[0] != '\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;
|
return 1;
|
||||||
}
|
}
|
||||||
ctx.write = us_output_file_write;
|
ctx.write = us_output_file_write;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#define US_VERSION_MAJOR 6
|
#define US_VERSION_MAJOR 6
|
||||||
#define US_VERSION_MINOR 3
|
#define US_VERSION_MINOR 5
|
||||||
|
|
||||||
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
#define US_MAKE_VERSION2(_major, _minor) #_major "." #_minor
|
||||||
#define US_MAKE_VERSION1(_major, _minor) US_MAKE_VERSION2(_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);
|
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);
|
const mode_t mask = umask(0);
|
||||||
sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode);
|
sink->fd = shm_open(sink->obj, (server ? O_RDWR | O_CREAT : O_RDWR), mode);
|
||||||
umask(mask);
|
umask(mask);
|
||||||
@@ -65,12 +70,12 @@ us_memsink_s *us_memsink_init(
|
|||||||
goto error;
|
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);
|
US_LOG_PERROR("%s-sink: Can't truncate shared memory", name);
|
||||||
goto error;
|
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);
|
US_LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -83,7 +88,7 @@ error:
|
|||||||
|
|
||||||
void us_memsink_destroy(us_memsink_s *sink) {
|
void us_memsink_destroy(us_memsink_s *sink) {
|
||||||
if (sink->mem != NULL) {
|
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);
|
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();
|
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)",
|
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
|
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;
|
*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;
|
sink->mem->used = frame->used;
|
||||||
US_FRAME_COPY_META(frame, sink->mem);
|
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;
|
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);
|
US_FRAME_COPY_META(sink->mem, frame);
|
||||||
if (key_requested != NULL) { // We don't need it for non-H264 sinks
|
if (key_requested != NULL) { // We don't need it for non-H264 sinks
|
||||||
*key_requested = sink->mem->key_requested;
|
*key_requested = sink->mem->key_requested;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *obj;
|
const char *obj;
|
||||||
|
uz data_size;
|
||||||
bool server;
|
bool server;
|
||||||
bool rm;
|
bool rm;
|
||||||
uint client_ttl; // Only for server
|
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
|
#pragma once
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
#define US_MEMSINK_MAGIC ((u64)0xCAFEBABECAFEBABE)
|
#define US_MEMSINK_MAGIC ((u64)0xCAFEBABECAFEBABE)
|
||||||
#define US_MEMSINK_VERSION ((u32)4)
|
#define US_MEMSINK_VERSION ((u32)5)
|
||||||
|
|
||||||
#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))
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -57,28 +50,11 @@ typedef struct {
|
|||||||
|
|
||||||
ldf last_client_ts;
|
ldf last_client_ts;
|
||||||
bool key_requested;
|
bool key_requested;
|
||||||
|
|
||||||
u8 data[US_MEMSINK_MAX_DATA];
|
|
||||||
} us_memsink_shared_s;
|
} us_memsink_shared_s;
|
||||||
|
|
||||||
|
|
||||||
INLINE us_memsink_shared_s *us_memsink_shared_map(int fd) {
|
us_memsink_shared_s *us_memsink_shared_map(int fd, uz data_size);
|
||||||
us_memsink_shared_s *mem = mmap(
|
int us_memsink_shared_unmap(us_memsink_shared_s *mem, uz data_size);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
INLINE int us_memsink_shared_unmap(us_memsink_shared_s *mem) {
|
uz us_memsink_calculate_size(const char *obj);
|
||||||
assert(mem != NULL);
|
u8 *us_memsink_get_data(us_memsink_shared_s *mem);
|
||||||
return munmap(mem, sizeof(us_memsink_shared_s));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ INLINE void us_thread_set_name(const char *name) {
|
|||||||
# elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
# elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||||
pthread_set_name_np(pthread_self(), name);
|
pthread_set_name_np(pthread_self(), name);
|
||||||
# elif defined(__NetBSD__)
|
# elif defined(__NetBSD__)
|
||||||
pthread_setname_np(pthread_self(), "%s", (void *)name);
|
pthread_setname_np(pthread_self(), "%s", (void*)name);
|
||||||
# else
|
# else
|
||||||
# error us_thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
# error us_thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0
|
||||||
# endif
|
# endif
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ void us_encoder_open(us_encoder_s *enc, us_device_s *dev) {
|
|||||||
|
|
||||||
enc->run->pool = us_workers_pool_init(
|
enc->run->pool = us_workers_pool_init(
|
||||||
"JPEG", "jw", n_workers, desired_interval,
|
"JPEG", "jw", n_workers, desired_interval,
|
||||||
_worker_job_init, (void *)enc,
|
_worker_job_init, (void*)enc,
|
||||||
_worker_job_destroy,
|
_worker_job_destroy,
|
||||||
_worker_run_job);
|
_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) {
|
static void *_worker_job_init(void *v_enc) {
|
||||||
us_encoder_job_s *job;
|
us_encoder_job_s *job;
|
||||||
US_CALLOC(job, 1);
|
US_CALLOC(job, 1);
|
||||||
job->enc = (us_encoder_s *)v_enc;
|
job->enc = (us_encoder_s*)v_enc;
|
||||||
job->dest = us_frame_init();
|
job->dest = us_frame_init();
|
||||||
return (void *)job;
|
return (void*)job;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _worker_job_destroy(void *v_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);
|
us_frame_destroy(job->dest);
|
||||||
free(job);
|
free(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _worker_run_job(us_worker_s *wr) {
|
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()
|
us_encoder_s *enc = job->enc; // Just for _ER()
|
||||||
const us_frame_s *src = &job->hw->raw;
|
const us_frame_s *src = &job->hw->raw;
|
||||||
us_frame_s *dest = job->dest;
|
us_frame_s *dest = job->dest;
|
||||||
|
|||||||
@@ -29,8 +29,8 @@
|
|||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct jpeg_destination_mgr mgr; // Default manager
|
struct jpeg_destination_mgr mgr; // Default manager
|
||||||
JOCTET *buf; // Start of buffer
|
JOCTET *buf; // Start of buffer
|
||||||
us_frame_s *frame;
|
us_frame_s *frame;
|
||||||
} _jpeg_dest_manager_s;
|
} _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_width = src->width;
|
||||||
jpeg.image_height = src->height;
|
jpeg.image_height = src->height;
|
||||||
jpeg.input_components = 3;
|
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_defaults(&jpeg);
|
||||||
jpeg_set_quality(&jpeg, quality, TRUE);
|
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) {
|
static void _jpeg_set_dest_frame(j_compress_ptr jpeg, us_frame_s *frame) {
|
||||||
if (jpeg->dest == NULL) {
|
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)
|
(j_common_ptr) jpeg, JPOOL_PERMANENT, sizeof(_jpeg_dest_manager_s)
|
||||||
)) != NULL);
|
)) != 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.init_destination = _jpeg_init_destination;
|
||||||
dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
|
dest->mgr.empty_output_buffer = _jpeg_empty_output_buffer;
|
||||||
dest->mgr.term_destination = _jpeg_term_destination;
|
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)
|
#define JPEG_OUTPUT_BUFFER_SIZE ((size_t)4096)
|
||||||
|
|
||||||
static void _jpeg_init_destination(j_compress_ptr jpeg) {
|
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
|
// 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)
|
(j_common_ptr) jpeg, JPOOL_IMAGE, JPEG_OUTPUT_BUFFER_SIZE * sizeof(JOCTET)
|
||||||
)) != NULL);
|
)) != NULL);
|
||||||
|
|
||||||
@@ -235,7 +240,7 @@ static void _jpeg_init_destination(j_compress_ptr jpeg) {
|
|||||||
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
|
static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg) {
|
||||||
// Called whenever local jpeg buffer fills up
|
// 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);
|
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.
|
// Called by jpeg_finish_compress after all data has been written.
|
||||||
// Usually needs to flush buffer.
|
// 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;
|
const size_t final = JPEG_OUTPUT_BUFFER_SIZE - dest->mgr.free_in_buffer;
|
||||||
|
|
||||||
// Write any data remaining in the buffer.
|
// Write any data remaining in the buffer.
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ enum _US_OPT_VALUES {
|
|||||||
_O_##x_prefix##_RM, \
|
_O_##x_prefix##_RM, \
|
||||||
_O_##x_prefix##_CLIENT_TTL, \
|
_O_##x_prefix##_CLIENT_TTL, \
|
||||||
_O_##x_prefix##_TIMEOUT,
|
_O_##x_prefix##_TIMEOUT,
|
||||||
ADD_SINK(SINK)
|
ADD_SINK(JPEG_SINK)
|
||||||
ADD_SINK(RAW_SINK)
|
ADD_SINK(RAW_SINK)
|
||||||
ADD_SINK(H264_SINK)
|
ADD_SINK(H264_SINK)
|
||||||
_O_H264_BITRATE,
|
_O_H264_BITRATE,
|
||||||
@@ -184,18 +184,25 @@ static const struct option _LONG_OPTS[] = {
|
|||||||
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
||||||
|
|
||||||
# define ADD_SINK(x_opt, x_prefix) \
|
# define ADD_SINK(x_opt, x_prefix) \
|
||||||
{x_opt "sink", required_argument, NULL, _O_##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-mode", required_argument, NULL, _O_##x_prefix##_MODE}, \
|
||||||
{x_opt "sink-rm", no_argument, NULL, _O_##x_prefix##_RM}, \
|
{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-client-ttl", required_argument, NULL, _O_##x_prefix##_CLIENT_TTL}, \
|
||||||
{x_opt "sink-timeout", required_argument, NULL, _O_##x_prefix##_TIMEOUT},
|
{x_opt "-sink-timeout", required_argument, NULL, _O_##x_prefix##_TIMEOUT},
|
||||||
ADD_SINK("", SINK)
|
ADD_SINK("jpeg", JPEG_SINK)
|
||||||
ADD_SINK("raw-", RAW_SINK)
|
ADD_SINK("raw", RAW_SINK)
|
||||||
ADD_SINK("h264-", H264_SINK)
|
ADD_SINK("h264", H264_SINK)
|
||||||
|
# undef ADD_SINK
|
||||||
|
// Extra opts for H.264
|
||||||
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
|
{"h264-bitrate", required_argument, NULL, _O_H264_BITRATE},
|
||||||
{"h264-gop", required_argument, NULL, _O_H264_GOP},
|
{"h264-gop", required_argument, NULL, _O_H264_GOP},
|
||||||
{"h264-m2m-device", required_argument, NULL, _O_H264_M2M_DEVICE},
|
{"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
|
# ifdef WITH_GPIO
|
||||||
{"gpio-device", required_argument, NULL, _O_GPIO_DEVICE},
|
{"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) \
|
# define ADD_SINK(x_opt, x_lp, x_up) \
|
||||||
case _O_##x_up: OPT_SET(x_lp##_name, optarg); \
|
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##_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##_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);
|
case _O_##x_up##_TIMEOUT: OPT_NUMBER("--" #x_opt "-sink-timeout", x_lp##_timeout, 1, 60, 0);
|
||||||
ADD_SINK("", jpeg_sink, SINK)
|
ADD_SINK("jpeg", jpeg_sink, JPEG_SINK)
|
||||||
ADD_SINK("raw-", raw_sink, RAW_SINK)
|
ADD_SINK("raw", raw_sink, RAW_SINK)
|
||||||
ADD_SINK("h264-", h264_sink, H264_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_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_GOP: OPT_NUMBER("--h264-gop", stream->h264_gop, 0, 60, 0);
|
||||||
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
|
case _O_H264_M2M_DEVICE: OPT_SET(stream->h264_m2m_path, optarg);
|
||||||
# undef ADD_SINK
|
|
||||||
|
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
case _O_GPIO_DEVICE: OPT_SET(us_g_gpio.path, optarg);
|
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) \
|
# define ADD_SINK(x_name, x_opt) \
|
||||||
SAY(x_name " sink options:"); \
|
SAY(x_name " sink options:"); \
|
||||||
SAY("══════════════════"); \
|
SAY("══════════════════"); \
|
||||||
SAY(" --" x_opt "sink <name> ──────────── Use the shared memory to sink " x_name " frames. Default: disabled.\n"); \
|
SAY(" --" x_opt "-sink <name> ──────────── Use the shared memory to sink " x_name " frames. Default: disabled."); \
|
||||||
SAY(" --" x_opt "sink-mode <mode> ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
|
SAY(" The name should end with a suffix \"." x_opt "\" or \"." x_opt "\"."); \
|
||||||
SAY(" --" x_opt "sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
SAY(" Default: disabled.\n"); \
|
||||||
SAY(" --" x_opt "sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
SAY(" --" x_opt "-sink-mode <mode> ─────── Set " x_name " sink permissions (like 777). Default: 660.\n"); \
|
||||||
SAY(" --" x_opt "sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
SAY(" --" x_opt "-sink-rm ──────────────── Remove shared memory on stop. Default: disabled.\n"); \
|
||||||
ADD_SINK("JPEG", "")
|
SAY(" --" x_opt "-sink-client-ttl <sec> ── Client TTL. Default: 10.\n"); \
|
||||||
ADD_SINK("RAW", "raw-")
|
SAY(" --" x_opt "-sink-timeout <sec> ───── Timeout for lock. Default: 1.\n");
|
||||||
ADD_SINK("H264", "h264-")
|
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-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-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");
|
SAY(" --h264-m2m-device </dev/path> ─ Path to V4L2 M2M encoder device. Default: auto select.\n");
|
||||||
# undef ADD_SINK
|
|
||||||
# ifdef WITH_GPIO
|
# ifdef WITH_GPIO
|
||||||
SAY("GPIO options:");
|
SAY("GPIO options:");
|
||||||
SAY("═════════════");
|
SAY("═════════════");
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ us_workers_pool_s *us_workers_pool_init(
|
|||||||
WR(pool) = pool;
|
WR(pool) = pool;
|
||||||
WR(job) = job_init(job_init_arg);
|
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;
|
pool->free_workers += 1;
|
||||||
|
|
||||||
# undef WR
|
# 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) {
|
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_THREAD_SETTLE("%s", wr->name);
|
||||||
US_LOG_DEBUG("Hello! I am a worker %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);
|
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||||||
addr.sun_family = AF_UNIX;
|
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);
|
atomic_store(&_g_ustreamer_online, online);
|
||||||
US_CLOSE_FD(fd); // cppcheck-suppress unreadVariable
|
US_CLOSE_FD(fd); // cppcheck-suppress unreadVariable
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user