mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-18 02:55:46 +00:00
big refactoring
This commit is contained in:
parent
f0e070be5b
commit
3c7564da19
@ -99,16 +99,16 @@ static void *_common_thread(void *v_client, bool video) {
|
||||
atomic_load(&client->transmit)
|
||||
&& (video || atomic_load(&client->transmit_audio))
|
||||
) {
|
||||
janus_plugin_rtp packet = {0};
|
||||
packet.video = rtp->video;
|
||||
packet.buffer = (char *)rtp->datagram;
|
||||
packet.length = rtp->used;
|
||||
# if JANUS_PLUGIN_API_VERSION >= 100
|
||||
// The uStreamer Janus plugin places video in stream index 0 and audio
|
||||
// (if available) in stream index 1.
|
||||
packet.mindex = (rtp->video ? 0 : 1);
|
||||
# endif
|
||||
|
||||
janus_plugin_rtp packet = {
|
||||
.video = rtp->video,
|
||||
.buffer = (char *)rtp->datagram,
|
||||
.length = rtp->used,
|
||||
# if JANUS_PLUGIN_API_VERSION >= 100
|
||||
// The uStreamer Janus plugin places video in stream index 0 and audio
|
||||
// (if available) in stream index 1.
|
||||
.mindex = (rtp->video ? 0 : 1),
|
||||
# endif
|
||||
};
|
||||
janus_plugin_rtp_extensions_reset(&packet.extensions);
|
||||
/*if (rtp->zero_playout_delay) {
|
||||
// https://github.com/pikvm/pikvm/issues/784
|
||||
|
||||
@ -59,13 +59,14 @@ us_config_s *us_config_init(const char *config_dir_path) {
|
||||
}
|
||||
|
||||
goto ok;
|
||||
error:
|
||||
us_config_destroy(config);
|
||||
config = NULL;
|
||||
ok:
|
||||
US_DELETE(jcfg, janus_config_destroy);
|
||||
free(config_file_path);
|
||||
return config;
|
||||
|
||||
error:
|
||||
US_DELETE(config, us_config_destroy);
|
||||
|
||||
ok:
|
||||
US_DELETE(jcfg, janus_config_destroy);
|
||||
free(config_file_path);
|
||||
return config;
|
||||
}
|
||||
|
||||
void us_config_destroy(us_config_s *config) {
|
||||
|
||||
@ -66,8 +66,7 @@ us_frame_s *us_memsink_fd_get_frame(int fd, us_memsink_shared_s *mem, uint64_t *
|
||||
ok = false;
|
||||
}
|
||||
if (!ok) {
|
||||
us_frame_destroy(frame);
|
||||
frame = NULL;
|
||||
US_DELETE(frame, us_frame_destroy);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
@ -169,15 +169,11 @@ static void *_video_sink_thread(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
close_memsink:
|
||||
if (mem != NULL) {
|
||||
US_JLOG_INFO("video", "Memsink closed");
|
||||
us_memsink_shared_unmap(mem);
|
||||
}
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
sleep(1); // error_delay
|
||||
close_memsink:
|
||||
US_DELETE(mem, us_memsink_shared_unmap);
|
||||
US_CLOSE_FD(fd, close);
|
||||
US_JLOG_INFO("video", "Memsink closed");
|
||||
sleep(1); // error_delay
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -237,9 +233,9 @@ static void *_audio_thread(void *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
close_audio:
|
||||
US_DELETE(audio, us_audio_destroy);
|
||||
sleep(1); // error_delay
|
||||
close_audio:
|
||||
US_DELETE(audio, us_audio_destroy);
|
||||
sleep(1); // error_delay
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -501,9 +497,9 @@ static struct janus_plugin_result *_plugin_handle_message(
|
||||
PUSH_ERROR(405, "Not implemented");
|
||||
}
|
||||
|
||||
ok_wait:
|
||||
FREE_MSG_JSEP;
|
||||
return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
|
||||
ok_wait:
|
||||
FREE_MSG_JSEP;
|
||||
return janus_plugin_result_new(JANUS_PLUGIN_OK_WAIT, NULL, NULL);
|
||||
|
||||
# undef PUSH_STATUS
|
||||
# undef PUSH_ERROR
|
||||
|
||||
@ -55,7 +55,8 @@ void us_rtp_write_header(us_rtp_s *rtp, uint32_t pts, bool marked) {
|
||||
word0 |= rtp->seq;
|
||||
++rtp->seq;
|
||||
|
||||
# define WRITE_BE_U32(_offset, _value) *((uint32_t *)(rtp->datagram + _offset)) = __builtin_bswap32(_value)
|
||||
# define WRITE_BE_U32(x_offset, x_value) \
|
||||
*((uint32_t *)(rtp->datagram + x_offset)) = __builtin_bswap32(x_value)
|
||||
WRITE_BE_U32(0, word0);
|
||||
WRITE_BE_U32(4, pts);
|
||||
WRITE_BE_U32(8, rtp->ssrc);
|
||||
|
||||
@ -37,7 +37,7 @@ void us_rtpa_destroy(us_rtpa_s *rtpa) {
|
||||
}
|
||||
|
||||
char *us_rtpa_make_sdp(us_rtpa_s *rtpa) {
|
||||
# define PAYLOAD rtpa->rtp->payload
|
||||
const unsigned pl = rtpa->rtp->payload;
|
||||
char *sdp;
|
||||
US_ASPRINTF(sdp,
|
||||
"m=audio 1 RTP/SAVPF %u" RN
|
||||
@ -49,10 +49,9 @@ char *us_rtpa_make_sdp(us_rtpa_s *rtpa) {
|
||||
"a=rtcp-fb:%u goog-remb" RN
|
||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||
"a=sendonly" RN,
|
||||
PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD, // PAYLOAD,
|
||||
pl, pl, pl, pl, pl, // pl,
|
||||
rtpa->rtp->ssrc
|
||||
);
|
||||
# undef PAYLOAD
|
||||
return sdp;
|
||||
}
|
||||
|
||||
|
||||
@ -45,9 +45,9 @@ void us_rtpv_destroy(us_rtpv_s *rtpv) {
|
||||
}
|
||||
|
||||
char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
|
||||
# define PAYLOAD rtpv->rtp->payload
|
||||
// https://tools.ietf.org/html/rfc6184
|
||||
// https://github.com/meetecho/janus-gateway/issues/2443
|
||||
const unsigned pl = rtpv->rtp->payload;
|
||||
char *sdp;
|
||||
US_ASPRINTF(sdp,
|
||||
"m=video 1 RTP/SAVPF %u" RN
|
||||
@ -61,12 +61,11 @@ char *us_rtpv_make_sdp(us_rtpv_s *rtpv) {
|
||||
"a=ssrc:%" PRIu32 " cname:ustreamer" RN
|
||||
"a=extmap:1 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay" RN
|
||||
"a=sendonly" RN,
|
||||
PAYLOAD, PAYLOAD, PAYLOAD, PAYLOAD,
|
||||
PAYLOAD, PAYLOAD, PAYLOAD,
|
||||
pl, pl, pl, pl,
|
||||
pl, pl, pl,
|
||||
rtpv->rtp->ssrc
|
||||
);
|
||||
return sdp;
|
||||
# undef PAYLOAD
|
||||
}
|
||||
|
||||
#define _PRE 3 // Annex B prefix length
|
||||
@ -110,14 +109,13 @@ void us_rtpv_wrap(us_rtpv_s *rtpv, const us_frame_s *frame, bool zero_playout_de
|
||||
}
|
||||
|
||||
void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint32_t pts, bool marked) {
|
||||
# define DG rtpv->rtp->datagram
|
||||
|
||||
const unsigned ref_idc = (data[0] >> 5) & 3;
|
||||
const unsigned type = data[0] & 0x1F;
|
||||
uint8_t *dg = rtpv->rtp->datagram;
|
||||
|
||||
if (size + US_RTP_HEADER_SIZE <= US_RTP_DATAGRAM_SIZE) {
|
||||
us_rtp_write_header(rtpv->rtp, pts, marked);
|
||||
memcpy(DG + US_RTP_HEADER_SIZE, data, size);
|
||||
memcpy(dg + US_RTP_HEADER_SIZE, data, size);
|
||||
rtpv->rtp->used = size + US_RTP_HEADER_SIZE;
|
||||
rtpv->callback(rtpv->rtp);
|
||||
return;
|
||||
@ -138,7 +136,7 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint3
|
||||
|
||||
us_rtp_write_header(rtpv->rtp, pts, (marked && last));
|
||||
|
||||
DG[US_RTP_HEADER_SIZE] = 28 | (ref_idc << 5);
|
||||
dg[US_RTP_HEADER_SIZE] = 28 | (ref_idc << 5);
|
||||
|
||||
uint8_t fu = type;
|
||||
if (first) {
|
||||
@ -147,9 +145,9 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint3
|
||||
if (last) {
|
||||
fu |= 0x40;
|
||||
}
|
||||
DG[US_RTP_HEADER_SIZE + 1] = fu;
|
||||
dg[US_RTP_HEADER_SIZE + 1] = fu;
|
||||
|
||||
memcpy(DG + fu_overhead, src, frag_size);
|
||||
memcpy(dg + fu_overhead, src, frag_size);
|
||||
rtpv->rtp->used = fu_overhead + frag_size;
|
||||
rtpv->callback(rtpv->rtp);
|
||||
|
||||
@ -157,8 +155,6 @@ void _rtpv_process_nalu(us_rtpv_s *rtpv, const uint8_t *data, size_t size, uint3
|
||||
remaining -= frag_size;
|
||||
first = false;
|
||||
}
|
||||
|
||||
# undef DG
|
||||
}
|
||||
|
||||
static ssize_t _find_annexb(const uint8_t *data, size_t size) {
|
||||
|
||||
@ -44,8 +44,7 @@ int us_tc358743_read_info(const char *path, us_tc358743_info_s *info) {
|
||||
}
|
||||
|
||||
# define READ_CID(x_cid, x_field) { \
|
||||
struct v4l2_control m_ctl = {0}; \
|
||||
m_ctl.id = x_cid; \
|
||||
struct v4l2_control m_ctl = {.id = x_cid}; \
|
||||
if (us_xioctl(fd, VIDIOC_G_CTRL, &m_ctl) < 0) { \
|
||||
US_JLOG_PERROR("audio", "Can't get value of " #x_cid); \
|
||||
close(fd); \
|
||||
@ -53,10 +52,8 @@ int us_tc358743_read_info(const char *path, us_tc358743_info_s *info) {
|
||||
} \
|
||||
info->x_field = m_ctl.value; \
|
||||
}
|
||||
|
||||
READ_CID(TC358743_CID_AUDIO_PRESENT, has_audio);
|
||||
READ_CID(TC358743_CID_AUDIO_SAMPLING_RATE, audio_hz);
|
||||
|
||||
# undef READ_CID
|
||||
|
||||
close(fd);
|
||||
|
||||
@ -19,7 +19,7 @@ commands = cppcheck \
|
||||
--inline-suppr \
|
||||
--library=python \
|
||||
--include=linters/cppcheck.h \
|
||||
src python/*.? janus/*.?
|
||||
src python/src/*.? janus/src/*.?
|
||||
|
||||
[testenv:flake8]
|
||||
allowlist_externals = bash
|
||||
|
||||
@ -35,23 +35,10 @@ typedef struct {
|
||||
} _MemsinkObject;
|
||||
|
||||
|
||||
#define _MEM(x_next) self->mem->x_next
|
||||
#define _FRAME(x_next) self->frame->x_next
|
||||
|
||||
|
||||
static void _MemsinkObject_destroy_internals(_MemsinkObject *self) {
|
||||
if (self->mem != NULL) {
|
||||
us_memsink_shared_unmap(self->mem);
|
||||
self->mem = NULL;
|
||||
}
|
||||
if (self->fd >= 0) {
|
||||
close(self->fd);
|
||||
self->fd = -1;
|
||||
}
|
||||
if (self->frame != NULL) {
|
||||
us_frame_destroy(self->frame);
|
||||
self->frame = NULL;
|
||||
}
|
||||
US_DELETE(self->mem, us_memsink_shared_unmap);
|
||||
US_CLOSE_FD(self->fd, close);
|
||||
US_DELETE(self->frame, us_frame_destroy);
|
||||
}
|
||||
|
||||
static int _MemsinkObject_init(_MemsinkObject *self, PyObject *args, PyObject *kwargs) {
|
||||
@ -65,17 +52,15 @@ static int _MemsinkObject_init(_MemsinkObject *self, PyObject *args, PyObject *k
|
||||
return -1;
|
||||
}
|
||||
|
||||
# define SET_DOUBLE(_field, _cond) { \
|
||||
if (!(self->_field _cond)) { \
|
||||
PyErr_SetString(PyExc_ValueError, #_field " must be " #_cond); \
|
||||
# define SET_DOUBLE(x_field, x_cond) { \
|
||||
if (!(self->x_field x_cond)) { \
|
||||
PyErr_SetString(PyExc_ValueError, #x_field " must be " #x_cond); \
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
SET_DOUBLE(lock_timeout, > 0);
|
||||
SET_DOUBLE(wait_timeout, > 0);
|
||||
SET_DOUBLE(drop_same_frames, >= 0);
|
||||
|
||||
# undef SET_DOUBLE
|
||||
|
||||
self->frame = us_frame_init();
|
||||
@ -84,17 +69,15 @@ static int _MemsinkObject_init(_MemsinkObject *self, PyObject *args, PyObject *k
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((self->mem = us_memsink_shared_map(self->fd)) == NULL) {
|
||||
PyErr_SetFromErrno(PyExc_OSError);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
_MemsinkObject_destroy_internals(self);
|
||||
return -1;
|
||||
error:
|
||||
_MemsinkObject_destroy_internals(self);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static PyObject *_MemsinkObject_repr(_MemsinkObject *self) {
|
||||
@ -142,14 +125,15 @@ static int _wait_frame(_MemsinkObject *self) {
|
||||
RETURN_OS_ERROR;
|
||||
|
||||
} else if (retval == 0) {
|
||||
if (_MEM(magic) == US_MEMSINK_MAGIC && _MEM(version) == US_MEMSINK_VERSION && _MEM(id) != self->frame_id) {
|
||||
us_memsink_shared_s *mem = self->mem;
|
||||
if (mem->magic == US_MEMSINK_MAGIC && mem->version == US_MEMSINK_VERSION && mem->id != self->frame_id) {
|
||||
if (self->drop_same_frames > 0) {
|
||||
if (
|
||||
US_FRAME_COMPARE_META_USED_NOTS(self->mem, self->frame)
|
||||
&& (self->frame_ts + self->drop_same_frames > now)
|
||||
&& !memcmp(_FRAME(data), _MEM(data), _MEM(used))
|
||||
&& !memcmp(self->frame->data, mem->data, mem->used)
|
||||
) {
|
||||
self->frame_id = _MEM(id);
|
||||
self->frame_id = mem->id;
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
@ -163,22 +147,18 @@ static int _wait_frame(_MemsinkObject *self) {
|
||||
}
|
||||
}
|
||||
|
||||
drop:
|
||||
|
||||
drop:
|
||||
if (usleep(1000) < 0) {
|
||||
RETURN_OS_ERROR;
|
||||
}
|
||||
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
if (PyErr_CheckSignals() < 0) {
|
||||
return -1;
|
||||
}
|
||||
} while (now < deadline_ts);
|
||||
|
||||
# undef RETURN_OS_ERROR
|
||||
|
||||
return -2;
|
||||
# undef RETURN_OS_ERROR
|
||||
}
|
||||
|
||||
static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args, PyObject *kwargs) {
|
||||
@ -199,13 +179,14 @@ static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args,
|
||||
default: return NULL;
|
||||
}
|
||||
|
||||
us_frame_set_data(self->frame, _MEM(data), _MEM(used));
|
||||
us_memsink_shared_s *mem = self->mem;
|
||||
us_frame_set_data(self->frame, mem->data, mem->used);
|
||||
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();
|
||||
_MEM(last_client_ts) = self->frame_ts;
|
||||
mem->last_client_ts = self->frame_ts;
|
||||
if (key_required) {
|
||||
_MEM(key_requested) = true;
|
||||
mem->key_requested = true;
|
||||
}
|
||||
|
||||
if (flock(self->fd, LOCK_UN) < 0) {
|
||||
@ -217,18 +198,18 @@ static PyObject *_MemsinkObject_wait_frame(_MemsinkObject *self, PyObject *args,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
# define SET_VALUE(_key, _maker) { \
|
||||
PyObject *_tmp = _maker; \
|
||||
if (_tmp == NULL) { \
|
||||
# define SET_VALUE(x_key, x_maker) { \
|
||||
PyObject *m_tmp = x_maker; \
|
||||
if (m_tmp == NULL) { \
|
||||
return NULL; \
|
||||
} \
|
||||
if (PyDict_SetItemString(dict_frame, _key, _tmp) < 0) { \
|
||||
Py_DECREF(_tmp); \
|
||||
if (PyDict_SetItemString(dict_frame, x_key, m_tmp) < 0) { \
|
||||
Py_DECREF(m_tmp); \
|
||||
return NULL; \
|
||||
} \
|
||||
Py_DECREF(_tmp); \
|
||||
Py_DECREF(m_tmp); \
|
||||
}
|
||||
# define SET_NUMBER(_key, _from, _to) SET_VALUE(#_key, Py##_to##_From##_from(_FRAME(_key)))
|
||||
# define SET_NUMBER(x_key, x_from, x_to) SET_VALUE(#x_key, Py##x_to##_From##x_from(self->frame->x_key))
|
||||
|
||||
SET_NUMBER(width, Long, Long);
|
||||
SET_NUMBER(height, Long, Long);
|
||||
@ -240,7 +221,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 *)_FRAME(data), _FRAME(used)));
|
||||
SET_VALUE("data", PyBytes_FromStringAndSize((const char *)self->frame->data, self->frame->used));
|
||||
|
||||
# undef SET_NUMBER
|
||||
# undef SET_VALUE
|
||||
@ -252,21 +233,19 @@ static PyObject *_MemsinkObject_is_opened(_MemsinkObject *self, PyObject *Py_UNU
|
||||
return PyBool_FromLong(self->mem != NULL && self->fd > 0);
|
||||
}
|
||||
|
||||
#define FIELD_GETTER(_field, _from, _to) \
|
||||
static PyObject *_MemsinkObject_getter_##_field(_MemsinkObject *self, void *Py_UNUSED(closure)) { \
|
||||
return Py##_to##_From##_from(self->_field); \
|
||||
#define FIELD_GETTER(x_field, x_from, x_to) \
|
||||
static PyObject *_MemsinkObject_getter_##x_field(_MemsinkObject *self, void *Py_UNUSED(closure)) { \
|
||||
return Py##x_to##_From##x_from(self->x_field); \
|
||||
}
|
||||
|
||||
FIELD_GETTER(obj, String, Unicode)
|
||||
FIELD_GETTER(lock_timeout, Double, Float)
|
||||
FIELD_GETTER(wait_timeout, Double, Float)
|
||||
FIELD_GETTER(drop_same_frames, Double, Float)
|
||||
|
||||
#undef FIELD_GETTER
|
||||
|
||||
static PyMethodDef _MemsinkObject_methods[] = {
|
||||
# define ADD_METHOD(_name, _method, _flags) \
|
||||
{.ml_name = _name, .ml_meth = (PyCFunction)_MemsinkObject_##_method, .ml_flags = (_flags)}
|
||||
# define ADD_METHOD(x_name, x_method, x_flags) \
|
||||
{.ml_name = x_name, .ml_meth = (PyCFunction)_MemsinkObject_##x_method, .ml_flags = (x_flags)}
|
||||
ADD_METHOD("close", close, METH_NOARGS),
|
||||
ADD_METHOD("__enter__", enter, METH_NOARGS),
|
||||
ADD_METHOD("__exit__", exit, METH_VARARGS),
|
||||
@ -277,7 +256,7 @@ static PyMethodDef _MemsinkObject_methods[] = {
|
||||
};
|
||||
|
||||
static PyGetSetDef _MemsinkObject_getsets[] = {
|
||||
# define ADD_GETTER(_field) {.name = #_field, .get = (getter)_MemsinkObject_getter_##_field}
|
||||
# define ADD_GETTER(x_field) {.name = #x_field, .get = (getter)_MemsinkObject_getter_##x_field}
|
||||
ADD_GETTER(obj),
|
||||
ADD_GETTER(lock_timeout),
|
||||
ADD_GETTER(wait_timeout),
|
||||
|
||||
@ -231,6 +231,8 @@ static int _dump_sink(
|
||||
bool key_required,
|
||||
_output_context_s *ctx) {
|
||||
|
||||
int retval = -1;
|
||||
|
||||
if (count == 0) {
|
||||
count = -1;
|
||||
}
|
||||
@ -300,18 +302,13 @@ static int _dump_sink(
|
||||
}
|
||||
}
|
||||
|
||||
int retval = 0;
|
||||
goto ok;
|
||||
retval = 0;
|
||||
|
||||
error:
|
||||
retval = -1;
|
||||
|
||||
ok:
|
||||
US_DELETE(sink, us_memsink_destroy);
|
||||
us_frame_destroy(frame);
|
||||
|
||||
US_LOG_INFO("Bye-bye");
|
||||
return retval;
|
||||
error:
|
||||
US_DELETE(sink, us_memsink_destroy);
|
||||
us_frame_destroy(frame);
|
||||
US_LOG_INFO("Bye-bye");
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void _help(FILE *fp) {
|
||||
|
||||
@ -84,11 +84,6 @@ static const char *_standard_to_string(v4l2_std_id standard);
|
||||
static const char *_io_method_to_string_supported(enum v4l2_memory io_method);
|
||||
|
||||
|
||||
#define _RUN(x_next) dev->run->x_next
|
||||
#define _D_XIOCTL(...) us_xioctl(_RUN(fd), __VA_ARGS__)
|
||||
#define _D_IS_MPLANE (_RUN(capture_type) == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
|
||||
|
||||
|
||||
us_device_s *us_device_init(void) {
|
||||
us_device_runtime_s *run;
|
||||
US_CALLOC(run, 1);
|
||||
@ -143,11 +138,13 @@ int us_device_parse_io_method(const char *str) {
|
||||
}
|
||||
|
||||
int us_device_open(us_device_s *dev) {
|
||||
if ((_RUN(fd) = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) {
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
if ((run->fd = open(dev->path, O_RDWR|O_NONBLOCK)) < 0) {
|
||||
US_LOG_PERROR("Can't open device");
|
||||
goto error;
|
||||
}
|
||||
US_LOG_INFO("Device fd=%d opened", _RUN(fd));
|
||||
US_LOG_INFO("Device fd=%d opened", run->fd);
|
||||
|
||||
if (_device_open_check_cap(dev) < 0) {
|
||||
goto error;
|
||||
@ -168,117 +165,106 @@ int us_device_open(us_device_s *dev) {
|
||||
}
|
||||
_device_apply_controls(dev);
|
||||
|
||||
US_LOG_DEBUG("Device fd=%d initialized", _RUN(fd));
|
||||
US_LOG_DEBUG("Device fd=%d initialized", run->fd);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
us_device_close(dev);
|
||||
return -1;
|
||||
error:
|
||||
us_device_close(dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void us_device_close(us_device_s *dev) {
|
||||
_RUN(persistent_timeout_reported) = false;
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
if (_RUN(hw_bufs) != NULL) {
|
||||
run->persistent_timeout_reported = false;
|
||||
|
||||
if (run->hw_bufs != NULL) {
|
||||
US_LOG_DEBUG("Releasing device buffers ...");
|
||||
for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
|
||||
# define HW(x_next) _RUN(hw_bufs)[index].x_next
|
||||
for (unsigned index = 0; index < run->n_bufs; ++index) {
|
||||
us_hw_buffer_s *hw = &run->hw_bufs[index];
|
||||
|
||||
if (HW(dma_fd) >= 0) {
|
||||
close(HW(dma_fd));
|
||||
HW(dma_fd) = -1;
|
||||
}
|
||||
US_CLOSE_FD(hw->dma_fd, close);
|
||||
|
||||
if (dev->io_method == V4L2_MEMORY_MMAP) {
|
||||
if (HW(raw.allocated) > 0 && HW(raw.data) != NULL) {
|
||||
if (munmap(HW(raw.data), HW(raw.allocated)) < 0) {
|
||||
if (hw->raw.allocated > 0 && hw->raw.data != NULL) {
|
||||
if (munmap(hw->raw.data, hw->raw.allocated) < 0) {
|
||||
US_LOG_PERROR("Can't unmap device buffer=%u", index);
|
||||
}
|
||||
}
|
||||
} else { // V4L2_MEMORY_USERPTR
|
||||
US_DELETE(HW(raw.data), free);
|
||||
US_DELETE(hw->raw.data, free);
|
||||
}
|
||||
|
||||
if (_D_IS_MPLANE) {
|
||||
free(HW(buf.m.planes));
|
||||
if (run->capture_mplane) {
|
||||
free(hw->buf.m.planes);
|
||||
}
|
||||
|
||||
# undef HW
|
||||
}
|
||||
_RUN(n_bufs) = 0;
|
||||
free(_RUN(hw_bufs));
|
||||
_RUN(hw_bufs) = NULL;
|
||||
US_DELETE(run->hw_bufs, free);
|
||||
run->n_bufs = 0;
|
||||
}
|
||||
|
||||
if (_RUN(fd) >= 0) {
|
||||
if (run->fd >= 0) {
|
||||
US_LOG_DEBUG("Closing device ...");
|
||||
if (close(_RUN(fd)) < 0) {
|
||||
US_LOG_PERROR("Can't close device fd=%d", _RUN(fd));
|
||||
if (close(run->fd) < 0) {
|
||||
US_LOG_PERROR("Can't close device fd=%d", run->fd);
|
||||
} else {
|
||||
US_LOG_INFO("Device fd=%d closed", _RUN(fd));
|
||||
US_LOG_INFO("Device fd=%d closed", run->fd);
|
||||
}
|
||||
_RUN(fd) = -1;
|
||||
run->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int us_device_export_to_dma(us_device_s *dev) {
|
||||
# define DMA_FD _RUN(hw_bufs[index].dma_fd)
|
||||
|
||||
for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
|
||||
struct v4l2_exportbuffer exp = {0};
|
||||
exp.type = _RUN(capture_type);
|
||||
exp.index = index;
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
for (unsigned index = 0; index < run->n_bufs; ++index) {
|
||||
struct v4l2_exportbuffer exp = {
|
||||
.type = run->capture_type,
|
||||
.index = index,
|
||||
};
|
||||
US_LOG_DEBUG("Exporting device buffer=%u to DMA ...", index);
|
||||
if (_D_XIOCTL(VIDIOC_EXPBUF, &exp) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_EXPBUF, &exp) < 0) {
|
||||
US_LOG_PERROR("Can't export device buffer=%u to DMA", index);
|
||||
goto error;
|
||||
}
|
||||
DMA_FD = exp.fd;
|
||||
run->hw_bufs[index].dma_fd = exp.fd;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
|
||||
if (DMA_FD >= 0) {
|
||||
close(DMA_FD);
|
||||
DMA_FD = -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
# undef DMA_FD
|
||||
error:
|
||||
for (unsigned index = 0; index < run->n_bufs; ++index) {
|
||||
US_CLOSE_FD(run->hw_bufs[index].dma_fd, close);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int us_device_switch_capturing(us_device_s *dev, bool enable) {
|
||||
if (enable != _RUN(capturing)) {
|
||||
enum v4l2_buf_type type = _RUN(capture_type);
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
if (enable != run->capturing) {
|
||||
enum v4l2_buf_type type = run->capture_type;
|
||||
|
||||
US_LOG_DEBUG("%s device capturing ...", (enable ? "Starting" : "Stopping"));
|
||||
if (_D_XIOCTL((enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
|
||||
if (us_xioctl(run->fd, (enable ? VIDIOC_STREAMON : VIDIOC_STREAMOFF), &type) < 0) {
|
||||
US_LOG_PERROR("Can't %s capturing", (enable ? "start" : "stop"));
|
||||
if (enable) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
_RUN(capturing) = enable;
|
||||
run->capturing = enable;
|
||||
US_LOG_INFO("Capturing %s", (enable ? "started" : "stopped"));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *has_error) {
|
||||
int retval;
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
# define INIT_FD_SET(x_set) \
|
||||
fd_set x_set; FD_ZERO(&x_set); FD_SET(_RUN(fd), &x_set);
|
||||
|
||||
fd_set x_set; FD_ZERO(&x_set); FD_SET(run->fd, &x_set);
|
||||
INIT_FD_SET(read_fds);
|
||||
INIT_FD_SET(write_fds);
|
||||
INIT_FD_SET(error_fds);
|
||||
|
||||
# undef INIT_FD_SET
|
||||
|
||||
struct timeval timeout;
|
||||
@ -287,11 +273,11 @@ int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *ha
|
||||
|
||||
US_LOG_DEBUG("Calling select() on video device ...");
|
||||
|
||||
retval = select(_RUN(fd) + 1, &read_fds, &write_fds, &error_fds, &timeout);
|
||||
int retval = select(run->fd + 1, &read_fds, &write_fds, &error_fds, &timeout);
|
||||
if (retval > 0) {
|
||||
*has_read = FD_ISSET(_RUN(fd), &read_fds);
|
||||
*has_write = FD_ISSET(_RUN(fd), &write_fds);
|
||||
*has_error = FD_ISSET(_RUN(fd), &error_fds);
|
||||
*has_read = FD_ISSET(run->fd, &read_fds);
|
||||
*has_write = FD_ISSET(run->fd, &write_fds);
|
||||
*has_error = FD_ISSET(run->fd, &error_fds);
|
||||
} else {
|
||||
*has_read = false;
|
||||
*has_write = false;
|
||||
@ -300,12 +286,12 @@ int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *ha
|
||||
US_LOG_DEBUG("Device select() --> %d", retval);
|
||||
|
||||
if (retval > 0) {
|
||||
_RUN(persistent_timeout_reported) = false;
|
||||
run->persistent_timeout_reported = false;
|
||||
} else if (retval == 0) {
|
||||
if (dev->persistent) {
|
||||
if (!_RUN(persistent_timeout_reported)) {
|
||||
if (!run->persistent_timeout_reported) {
|
||||
US_LOG_ERROR("Persistent device timeout (unplugged)");
|
||||
_RUN(persistent_timeout_reported) = true;
|
||||
run->persistent_timeout_reported = true;
|
||||
}
|
||||
} else {
|
||||
// Если устройство не персистентное, то таймаут является ошибкой
|
||||
@ -316,11 +302,13 @@ int us_device_select(us_device_s *dev, bool *has_read, bool *has_write, bool *ha
|
||||
}
|
||||
|
||||
int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
*hw = NULL;
|
||||
|
||||
struct v4l2_buffer buf = {0};
|
||||
struct v4l2_plane buf_planes[VIDEO_MAX_PLANES] = {0};
|
||||
if (_D_IS_MPLANE) {
|
||||
if (run->capture_mplane) {
|
||||
// Just for _v4l2_buffer_copy(), buf.length is not needed here
|
||||
buf.m.planes = buf_planes;
|
||||
}
|
||||
@ -334,23 +322,23 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
do {
|
||||
struct v4l2_buffer new = {0};
|
||||
struct v4l2_plane new_planes[VIDEO_MAX_PLANES] = {0};
|
||||
new.type = _RUN(capture_type);
|
||||
new.type = run->capture_type;
|
||||
new.memory = dev->io_method;
|
||||
if (_D_IS_MPLANE) {
|
||||
if (run->capture_mplane) {
|
||||
new.length = VIDEO_MAX_PLANES;
|
||||
new.m.planes = new_planes;
|
||||
}
|
||||
|
||||
const bool new_got = (_D_XIOCTL(VIDIOC_DQBUF, &new) >= 0);
|
||||
const bool new_got = (us_xioctl(run->fd, VIDIOC_DQBUF, &new) >= 0);
|
||||
|
||||
if (new_got) {
|
||||
if (new.index >= _RUN(n_bufs)) {
|
||||
US_LOG_ERROR("V4L2 error: grabbed invalid device buffer=%u, n_bufs=%u", new.index, _RUN(n_bufs));
|
||||
if (new.index >= run->n_bufs) {
|
||||
US_LOG_ERROR("V4L2 error: grabbed invalid device buffer=%u, n_bufs=%u", new.index, run->n_bufs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
# define GRABBED(x_buf) _RUN(hw_bufs)[x_buf.index].grabbed
|
||||
# define FRAME_DATA(x_buf) _RUN(hw_bufs)[x_buf.index].raw.data
|
||||
# define GRABBED(x_buf) run->hw_bufs[x_buf.index].grabbed
|
||||
# define FRAME_DATA(x_buf) run->hw_bufs[x_buf.index].raw.data
|
||||
|
||||
if (GRABBED(new)) {
|
||||
US_LOG_ERROR("V4L2 error: grabbed device buffer=%u is already used", new.index);
|
||||
@ -358,14 +346,14 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
}
|
||||
GRABBED(new) = true;
|
||||
|
||||
if (_D_IS_MPLANE) {
|
||||
if (run->capture_mplane) {
|
||||
new.bytesused = new.m.planes[0].bytesused;
|
||||
}
|
||||
|
||||
broken = !_device_is_buffer_valid(dev, &new, FRAME_DATA(new));
|
||||
if (broken) {
|
||||
US_LOG_DEBUG("Releasing device buffer=%u (broken frame) ...", new.index);
|
||||
if (_D_XIOCTL(VIDIOC_QBUF, &new) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_QBUF, &new) < 0) {
|
||||
US_LOG_PERROR("Can't release device buffer=%u (broken frame)", new.index);
|
||||
return -1;
|
||||
}
|
||||
@ -374,7 +362,7 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
}
|
||||
|
||||
if (buf_got) {
|
||||
if (_D_XIOCTL(VIDIOC_QBUF, &buf) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_QBUF, &buf) < 0) {
|
||||
US_LOG_PERROR("Can't release device buffer=%u (skipped frame)", buf.index);
|
||||
return -1;
|
||||
}
|
||||
@ -402,29 +390,26 @@ int us_device_grab_buffer(us_device_s *dev, us_hw_buffer_s **hw) {
|
||||
}
|
||||
} while (true);
|
||||
|
||||
# define HW(x_next) _RUN(hw_bufs)[buf.index].x_next
|
||||
HW(raw.dma_fd) = HW(dma_fd);
|
||||
HW(raw.used) = buf.bytesused;
|
||||
HW(raw.width) = _RUN(width);
|
||||
HW(raw.height) = _RUN(height);
|
||||
HW(raw.format) = _RUN(format);
|
||||
HW(raw.stride) = _RUN(stride);
|
||||
HW(raw.online) = true;
|
||||
_v4l2_buffer_copy(&buf, &HW(buf));
|
||||
HW(raw.grab_ts) = (long double)((buf.timestamp.tv_sec * (uint64_t)1000) + (buf.timestamp.tv_usec / 1000)) / 1000;
|
||||
US_LOG_DEBUG("Grabbed new frame: buffer=%u, bytesused=%u, grab_ts=%.3Lf, latency=%.3Lf, skipped=%u",
|
||||
buf.index, buf.bytesused, HW(raw.grab_ts), us_get_now_monotonic() - HW(raw.grab_ts), skipped);
|
||||
# undef HW
|
||||
*hw = &run->hw_bufs[buf.index];
|
||||
(*hw)->raw.dma_fd = (*hw)->dma_fd;
|
||||
(*hw)->raw.used = buf.bytesused;
|
||||
(*hw)->raw.width = run->width;
|
||||
(*hw)->raw.height = run->height;
|
||||
(*hw)->raw.format = run->format;
|
||||
(*hw)->raw.stride = run->stride;
|
||||
(*hw)->raw.online = true;
|
||||
_v4l2_buffer_copy(&buf, &(*hw)->buf);
|
||||
(*hw)->raw.grab_ts = (long double)((buf.timestamp.tv_sec * (uint64_t)1000) + (buf.timestamp.tv_usec / 1000)) / 1000;
|
||||
|
||||
*hw = &_RUN(hw_bufs[buf.index]);
|
||||
US_LOG_DEBUG("Grabbed new frame: buffer=%u, bytesused=%u, grab_ts=%.3Lf, latency=%.3Lf, skipped=%u",
|
||||
buf.index, buf.bytesused, (*hw)->raw.grab_ts, us_get_now_monotonic() - (*hw)->raw.grab_ts, skipped);
|
||||
return buf.index;
|
||||
}
|
||||
|
||||
int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw) {
|
||||
const unsigned index = hw->buf.index;
|
||||
US_LOG_DEBUG("Releasing device buffer=%u ...", index);
|
||||
|
||||
if (_D_XIOCTL(VIDIOC_QBUF, &hw->buf) < 0) {
|
||||
if (us_xioctl(dev->run->fd, VIDIOC_QBUF, &hw->buf) < 0) {
|
||||
US_LOG_PERROR("Can't release device buffer=%u", index);
|
||||
return -1;
|
||||
}
|
||||
@ -434,9 +419,8 @@ int us_device_release_buffer(us_device_s *dev, us_hw_buffer_s *hw) {
|
||||
|
||||
int us_device_consume_event(us_device_s *dev) {
|
||||
struct v4l2_event event;
|
||||
|
||||
US_LOG_DEBUG("Consuming V4L2 event ...");
|
||||
if (_D_XIOCTL(VIDIOC_DQEVENT, &event) == 0) {
|
||||
if (us_xioctl(dev->run->fd, VIDIOC_DQEVENT, &event) == 0) {
|
||||
switch (event.type) {
|
||||
case V4L2_EVENT_SOURCE_CHANGE:
|
||||
US_LOG_INFO("Got V4L2_EVENT_SOURCE_CHANGE: source changed");
|
||||
@ -501,19 +485,22 @@ bool _device_is_buffer_valid(us_device_s *dev, const struct v4l2_buffer *buf, co
|
||||
}
|
||||
|
||||
static int _device_open_check_cap(us_device_s *dev) {
|
||||
struct v4l2_capability cap = {0};
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
struct v4l2_capability cap = {0};
|
||||
US_LOG_DEBUG("Querying device capabilities ...");
|
||||
if (_D_XIOCTL(VIDIOC_QUERYCAP, &cap) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_QUERYCAP, &cap) < 0) {
|
||||
US_LOG_PERROR("Can't query device capabilities");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
|
||||
_RUN(capture_type) = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
run->capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
run->capture_mplane = false;
|
||||
US_LOG_INFO("Using capture type: single-planar");
|
||||
} else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
|
||||
_RUN(capture_type) = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
run->capture_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
|
||||
run->capture_mplane = true;
|
||||
US_LOG_INFO("Using capture type: multi-planar");
|
||||
} else {
|
||||
US_LOG_ERROR("Video capture is not supported by device");
|
||||
@ -525,10 +512,10 @@ static int _device_open_check_cap(us_device_s *dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!_D_IS_MPLANE) {
|
||||
if (!run->capture_mplane) {
|
||||
int input = dev->input; // Needs a pointer to int for ioctl()
|
||||
US_LOG_INFO("Using input channel: %d", input);
|
||||
if (_D_XIOCTL(VIDIOC_S_INPUT, &input) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_S_INPUT, &input) < 0) {
|
||||
US_LOG_ERROR("Can't set input channel");
|
||||
return -1;
|
||||
}
|
||||
@ -536,7 +523,7 @@ static int _device_open_check_cap(us_device_s *dev) {
|
||||
|
||||
if (dev->standard != V4L2_STD_UNKNOWN) {
|
||||
US_LOG_INFO("Using TV standard: %s", _standard_to_string(dev->standard));
|
||||
if (_D_XIOCTL(VIDIOC_S_STD, &dev->standard) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_S_STD, &dev->standard) < 0) {
|
||||
US_LOG_ERROR("Can't set video standard");
|
||||
return -1;
|
||||
}
|
||||
@ -555,11 +542,9 @@ static int _device_open_dv_timings(us_device_s *dev) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct v4l2_event_subscription sub = {0};
|
||||
sub.type = V4L2_EVENT_SOURCE_CHANGE;
|
||||
|
||||
struct v4l2_event_subscription sub = {.type = V4L2_EVENT_SOURCE_CHANGE};
|
||||
US_LOG_DEBUG("Subscribing to DV-timings events ...")
|
||||
if (_D_XIOCTL(VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) {
|
||||
if (us_xioctl(dev->run->fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) {
|
||||
US_LOG_PERROR("Can't subscribe to DV-timings events");
|
||||
return -1;
|
||||
}
|
||||
@ -568,10 +553,12 @@ static int _device_open_dv_timings(us_device_s *dev) {
|
||||
}
|
||||
|
||||
static int _device_apply_dv_timings(us_device_s *dev) {
|
||||
us_device_runtime_s *const run = dev->run; // cppcheck-suppress constVariablePointer
|
||||
|
||||
struct v4l2_dv_timings dv = {0};
|
||||
|
||||
US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERY_DV_TIMINGS) ...");
|
||||
if (_D_XIOCTL(VIDIOC_QUERY_DV_TIMINGS, &dv) == 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_QUERY_DV_TIMINGS, &dv) == 0) {
|
||||
if (dv.type == V4L2_DV_BT_656_1120) {
|
||||
// See v4l2_print_dv_timings() in the kernel
|
||||
const unsigned htot = V4L2_DV_BT_FRAME_WIDTH(&dv.bt);
|
||||
@ -587,7 +574,7 @@ static int _device_apply_dv_timings(us_device_s *dev) {
|
||||
}
|
||||
|
||||
US_LOG_DEBUG("Calling us_xioctl(VIDIOC_S_DV_TIMINGS) ...");
|
||||
if (_D_XIOCTL(VIDIOC_S_DV_TIMINGS, &dv) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_S_DV_TIMINGS, &dv) < 0) {
|
||||
US_LOG_PERROR("Failed to set DV-timings");
|
||||
return -1;
|
||||
}
|
||||
@ -598,9 +585,9 @@ static int _device_apply_dv_timings(us_device_s *dev) {
|
||||
|
||||
} else {
|
||||
US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERYSTD) ...");
|
||||
if (_D_XIOCTL(VIDIOC_QUERYSTD, &dev->standard) == 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_QUERYSTD, &dev->standard) == 0) {
|
||||
US_LOG_INFO("Applying the new VIDIOC_S_STD: %s ...", _standard_to_string(dev->standard));
|
||||
if (_D_XIOCTL(VIDIOC_S_STD, &dev->standard) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_S_STD, &dev->standard) < 0) {
|
||||
US_LOG_PERROR("Can't set video standard");
|
||||
return -1;
|
||||
}
|
||||
@ -609,21 +596,23 @@ static int _device_apply_dv_timings(us_device_s *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _device_open_format(us_device_s *dev, bool first) { // FIXME
|
||||
const unsigned stride = us_align_size(_RUN(width), 32) << 1;
|
||||
static int _device_open_format(us_device_s *dev, bool first) {
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
const unsigned stride = us_align_size(run->width, 32) << 1;
|
||||
|
||||
struct v4l2_format fmt = {0};
|
||||
fmt.type = _RUN(capture_type);
|
||||
if (_D_IS_MPLANE) {
|
||||
fmt.fmt.pix_mp.width = _RUN(width);
|
||||
fmt.fmt.pix_mp.height = _RUN(height);
|
||||
fmt.type = run->capture_type;
|
||||
if (run->capture_mplane) {
|
||||
fmt.fmt.pix_mp.width = run->width;
|
||||
fmt.fmt.pix_mp.height = run->height;
|
||||
fmt.fmt.pix_mp.pixelformat = dev->format;
|
||||
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix_mp.flags = 0;
|
||||
fmt.fmt.pix_mp.num_planes = 1;
|
||||
} else {
|
||||
fmt.fmt.pix.width = _RUN(width);
|
||||
fmt.fmt.pix.height = _RUN(height);
|
||||
fmt.fmt.pix.width = run->width;
|
||||
fmt.fmt.pix.height = run->height;
|
||||
fmt.fmt.pix.pixelformat = dev->format;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
fmt.fmt.pix.bytesperline = stride;
|
||||
@ -631,24 +620,24 @@ static int _device_open_format(us_device_s *dev, bool first) { // FIXME
|
||||
|
||||
// Set format
|
||||
US_LOG_DEBUG("Probing device format=%s, stride=%u, resolution=%ux%u ...",
|
||||
_format_to_string_supported(dev->format), stride, _RUN(width), _RUN(height));
|
||||
if (_D_XIOCTL(VIDIOC_S_FMT, &fmt) < 0) {
|
||||
_format_to_string_supported(dev->format), stride, run->width, run->height);
|
||||
if (us_xioctl(run->fd, VIDIOC_S_FMT, &fmt) < 0) {
|
||||
US_LOG_PERROR("Can't set device format");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fmt.type != _RUN(capture_type)) {
|
||||
if (fmt.type != run->capture_type) {
|
||||
US_LOG_ERROR("Capture format mismatch, please report to the developer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
# define FMT(x_next) (_D_IS_MPLANE ? fmt.fmt.pix_mp.x_next : fmt.fmt.pix.x_next)
|
||||
# define FMTS(x_next) (_D_IS_MPLANE ? fmt.fmt.pix_mp.plane_fmt[0].x_next : fmt.fmt.pix.x_next)
|
||||
# define FMT(x_next) (run->capture_mplane ? fmt.fmt.pix_mp.x_next : fmt.fmt.pix.x_next)
|
||||
# define FMTS(x_next) (run->capture_mplane ? fmt.fmt.pix_mp.plane_fmt[0].x_next : fmt.fmt.pix.x_next)
|
||||
|
||||
// Check resolution
|
||||
bool retry = false;
|
||||
if (FMT(width) != _RUN(width) || FMT(height) != _RUN(height)) {
|
||||
US_LOG_ERROR("Requested resolution=%ux%u is unavailable", _RUN(width), _RUN(height));
|
||||
if (FMT(width) != run->width || FMT(height) != run->height) {
|
||||
US_LOG_ERROR("Requested resolution=%ux%u is unavailable", run->width, run->height);
|
||||
retry = true;
|
||||
}
|
||||
if (_device_apply_resolution(dev, FMT(width), FMT(height)) < 0) {
|
||||
@ -657,7 +646,7 @@ static int _device_open_format(us_device_s *dev, bool first) { // FIXME
|
||||
if (first && retry) {
|
||||
return _device_open_format(dev, false);
|
||||
}
|
||||
US_LOG_INFO("Using resolution: %ux%u", _RUN(width), _RUN(height));
|
||||
US_LOG_INFO("Using resolution: %ux%u", run->width, run->height);
|
||||
|
||||
// Check format
|
||||
if (FMT(pixelformat) != dev->format) {
|
||||
@ -676,12 +665,12 @@ static int _device_open_format(us_device_s *dev, bool first) { // FIXME
|
||||
}
|
||||
}
|
||||
|
||||
_RUN(format) = FMT(pixelformat);
|
||||
US_LOG_INFO("Using format: %s", _format_to_string_supported(_RUN(format)));
|
||||
run->format = FMT(pixelformat);
|
||||
US_LOG_INFO("Using format: %s", _format_to_string_supported(run->format));
|
||||
|
||||
|
||||
_RUN(stride) = FMTS(bytesperline);
|
||||
_RUN(raw_size) = FMTS(sizeimage); // Only for userptr
|
||||
run->stride = FMTS(bytesperline);
|
||||
run->raw_size = FMTS(sizeimage); // Only for userptr
|
||||
|
||||
# undef FMTS
|
||||
# undef FMT
|
||||
@ -690,13 +679,13 @@ static int _device_open_format(us_device_s *dev, bool first) { // FIXME
|
||||
}
|
||||
|
||||
static void _device_open_hw_fps(us_device_s *dev) {
|
||||
_RUN(hw_fps) = 0;
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
struct v4l2_streamparm setfps = {0};
|
||||
setfps.type = _RUN(capture_type);
|
||||
run->hw_fps = 0;
|
||||
|
||||
struct v4l2_streamparm setfps = {.type = run->capture_type};
|
||||
US_LOG_DEBUG("Querying HW FPS ...");
|
||||
if (_D_XIOCTL(VIDIOC_G_PARM, &setfps) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_G_PARM, &setfps) < 0) {
|
||||
if (errno == ENOTTY) { // Quiet message for TC358743
|
||||
US_LOG_INFO("Querying HW FPS changing is not supported");
|
||||
} else {
|
||||
@ -713,11 +702,11 @@ static void _device_open_hw_fps(us_device_s *dev) {
|
||||
# define SETFPS_TPF(x_next) setfps.parm.capture.timeperframe.x_next
|
||||
|
||||
US_MEMSET_ZERO(setfps);
|
||||
setfps.type = _RUN(capture_type);
|
||||
setfps.type = run->capture_type;
|
||||
SETFPS_TPF(numerator) = 1;
|
||||
SETFPS_TPF(denominator) = (dev->desired_fps == 0 ? 255 : dev->desired_fps);
|
||||
|
||||
if (_D_XIOCTL(VIDIOC_S_PARM, &setfps) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_S_PARM, &setfps) < 0) {
|
||||
US_LOG_PERROR("Can't set HW FPS");
|
||||
return;
|
||||
}
|
||||
@ -732,35 +721,33 @@ static void _device_open_hw_fps(us_device_s *dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
_RUN(hw_fps) = SETFPS_TPF(denominator);
|
||||
if (dev->desired_fps != _RUN(hw_fps)) {
|
||||
US_LOG_INFO("Using HW FPS: %u -> %u (coerced)", dev->desired_fps, _RUN(hw_fps));
|
||||
run->hw_fps = SETFPS_TPF(denominator);
|
||||
if (dev->desired_fps != run->hw_fps) {
|
||||
US_LOG_INFO("Using HW FPS: %u -> %u (coerced)", dev->desired_fps, run->hw_fps);
|
||||
} else {
|
||||
US_LOG_INFO("Using HW FPS: %u", _RUN(hw_fps));
|
||||
US_LOG_INFO("Using HW FPS: %u", run->hw_fps);
|
||||
}
|
||||
|
||||
# undef SETFPS_TPF
|
||||
}
|
||||
|
||||
static void _device_open_jpeg_quality(us_device_s *dev) {
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
unsigned quality = 0;
|
||||
|
||||
if (us_is_jpeg(_RUN(format))) {
|
||||
if (us_is_jpeg(run->format)) {
|
||||
struct v4l2_jpegcompression comp = {0};
|
||||
|
||||
if (_D_XIOCTL(VIDIOC_G_JPEGCOMP, &comp) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_G_JPEGCOMP, &comp) < 0) {
|
||||
US_LOG_ERROR("Device doesn't support setting of HW encoding quality parameters");
|
||||
} else {
|
||||
comp.quality = dev->jpeg_quality;
|
||||
if (_D_XIOCTL(VIDIOC_S_JPEGCOMP, &comp) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_S_JPEGCOMP, &comp) < 0) {
|
||||
US_LOG_ERROR("Can't change MJPEG quality for JPEG source with HW pass-through encoder");
|
||||
} else {
|
||||
quality = dev->jpeg_quality;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_RUN(jpeg_quality) = quality;
|
||||
run->jpeg_quality = quality;
|
||||
}
|
||||
|
||||
static int _device_open_io_method(us_device_s *dev) {
|
||||
@ -774,13 +761,15 @@ static int _device_open_io_method(us_device_s *dev) {
|
||||
}
|
||||
|
||||
static int _device_open_io_method_mmap(us_device_s *dev) {
|
||||
struct v4l2_requestbuffers req = {0};
|
||||
req.count = dev->n_bufs;
|
||||
req.type = _RUN(capture_type);
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
struct v4l2_requestbuffers req = {
|
||||
.count = dev->n_bufs,
|
||||
.type = run->capture_type,
|
||||
.memory = V4L2_MEMORY_MMAP,
|
||||
};
|
||||
US_LOG_DEBUG("Requesting %u device buffers for MMAP ...", req.count);
|
||||
if (_D_XIOCTL(VIDIOC_REQBUFS, &req) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_REQBUFS, &req) < 0) {
|
||||
US_LOG_PERROR("Device '%s' doesn't support MMAP method", dev->path);
|
||||
return -1;
|
||||
}
|
||||
@ -794,64 +783,60 @@ static int _device_open_io_method_mmap(us_device_s *dev) {
|
||||
|
||||
US_LOG_DEBUG("Allocating device buffers ...");
|
||||
|
||||
US_CALLOC(_RUN(hw_bufs), req.count);
|
||||
for (_RUN(n_bufs) = 0; _RUN(n_bufs) < req.count; ++_RUN(n_bufs)) {
|
||||
US_CALLOC(run->hw_bufs, req.count);
|
||||
|
||||
for (run->n_bufs = 0; run->n_bufs < req.count; ++run->n_bufs) {
|
||||
struct v4l2_buffer buf = {0};
|
||||
struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
|
||||
buf.type = _RUN(capture_type);
|
||||
buf.type = run->capture_type;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = _RUN(n_bufs);
|
||||
if (_D_IS_MPLANE) {
|
||||
buf.index = run->n_bufs;
|
||||
if (run->capture_mplane) {
|
||||
buf.m.planes = planes;
|
||||
buf.length = VIDEO_MAX_PLANES;
|
||||
}
|
||||
|
||||
US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERYBUF) for device buffer=%u ...", _RUN(n_bufs));
|
||||
if (_D_XIOCTL(VIDIOC_QUERYBUF, &buf) < 0) {
|
||||
US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QUERYBUF) for device buffer=%u ...", run->n_bufs);
|
||||
if (us_xioctl(run->fd, VIDIOC_QUERYBUF, &buf) < 0) {
|
||||
US_LOG_PERROR("Can't VIDIOC_QUERYBUF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
# define HW(x_next) _RUN(hw_bufs)[_RUN(n_bufs)].x_next
|
||||
us_hw_buffer_s *hw = &run->hw_bufs[run->n_bufs];
|
||||
const size_t buf_size = (run->capture_mplane ? buf.m.planes[0].length : buf.length);
|
||||
const off_t buf_offset = (run->capture_mplane ? buf.m.planes[0].m.mem_offset : buf.m.offset);
|
||||
|
||||
HW(dma_fd) = -1;
|
||||
|
||||
const size_t buf_size = (_D_IS_MPLANE ? buf.m.planes[0].length : buf.length);
|
||||
const off_t buf_offset = (_D_IS_MPLANE ? buf.m.planes[0].m.mem_offset : buf.m.offset);
|
||||
|
||||
US_LOG_DEBUG("Mapping device buffer=%u ...", _RUN(n_bufs));
|
||||
if ((HW(raw.data) = mmap(
|
||||
NULL,
|
||||
buf_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
_RUN(fd),
|
||||
buf_offset
|
||||
US_LOG_DEBUG("Mapping device buffer=%u ...", run->n_bufs);
|
||||
if ((hw->raw.data = mmap(
|
||||
NULL, buf_size,
|
||||
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
run->fd, buf_offset
|
||||
)) == MAP_FAILED) {
|
||||
US_LOG_PERROR("Can't map device buffer=%u", _RUN(n_bufs));
|
||||
US_LOG_PERROR("Can't map device buffer=%u", run->n_bufs);
|
||||
return -1;
|
||||
}
|
||||
assert(HW(raw.data) != NULL);
|
||||
assert(hw->raw.data != NULL);
|
||||
hw->raw.allocated = buf_size;
|
||||
|
||||
HW(raw.allocated) = buf_size;
|
||||
|
||||
if (_D_IS_MPLANE) {
|
||||
US_CALLOC(HW(buf.m.planes), VIDEO_MAX_PLANES);
|
||||
if (run->capture_mplane) {
|
||||
US_CALLOC(hw->buf.m.planes, VIDEO_MAX_PLANES);
|
||||
}
|
||||
|
||||
# undef HW
|
||||
hw->dma_fd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _device_open_io_method_userptr(us_device_s *dev) {
|
||||
struct v4l2_requestbuffers req = {0};
|
||||
req.count = dev->n_bufs;
|
||||
req.type = _RUN(capture_type);
|
||||
req.memory = V4L2_MEMORY_USERPTR;
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
struct v4l2_requestbuffers req = {
|
||||
.count = dev->n_bufs,
|
||||
.type = run->capture_type,
|
||||
.memory = V4L2_MEMORY_USERPTR,
|
||||
};
|
||||
US_LOG_DEBUG("Requesting %u device buffers for USERPTR ...", req.count);
|
||||
if (_D_XIOCTL(VIDIOC_REQBUFS, &req) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_REQBUFS, &req) < 0) {
|
||||
US_LOG_PERROR("Device '%s' doesn't support USERPTR method", dev->path);
|
||||
return -1;
|
||||
}
|
||||
@ -865,32 +850,33 @@ static int _device_open_io_method_userptr(us_device_s *dev) {
|
||||
|
||||
US_LOG_DEBUG("Allocating device buffers ...");
|
||||
|
||||
US_CALLOC(_RUN(hw_bufs), req.count);
|
||||
US_CALLOC(run->hw_bufs, req.count);
|
||||
|
||||
const unsigned page_size = getpagesize();
|
||||
const unsigned buf_size = us_align_size(_RUN(raw_size), page_size);
|
||||
const unsigned buf_size = us_align_size(run->raw_size, page_size);
|
||||
|
||||
for (_RUN(n_bufs) = 0; _RUN(n_bufs) < req.count; ++_RUN(n_bufs)) {
|
||||
# define HW(x_next) _RUN(hw_bufs)[_RUN(n_bufs)].x_next
|
||||
assert((HW(raw.data) = aligned_alloc(page_size, buf_size)) != NULL);
|
||||
memset(HW(raw.data), 0, buf_size);
|
||||
HW(raw.allocated) = buf_size;
|
||||
if (_D_IS_MPLANE) {
|
||||
US_CALLOC(HW(buf.m.planes), VIDEO_MAX_PLANES);
|
||||
for (run->n_bufs = 0; run->n_bufs < req.count; ++run->n_bufs) {
|
||||
us_hw_buffer_s *hw = &run->hw_bufs[run->n_bufs];
|
||||
assert((hw->raw.data = aligned_alloc(page_size, buf_size)) != NULL);
|
||||
memset(hw->raw.data, 0, buf_size);
|
||||
hw->raw.allocated = buf_size;
|
||||
if (run->capture_mplane) {
|
||||
US_CALLOC(hw->buf.m.planes, VIDEO_MAX_PLANES);
|
||||
}
|
||||
# undef HW
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _device_open_queue_buffers(us_device_s *dev) {
|
||||
for (unsigned index = 0; index < _RUN(n_bufs); ++index) {
|
||||
us_device_runtime_s *const run = dev->run;
|
||||
|
||||
for (unsigned index = 0; index < run->n_bufs; ++index) {
|
||||
struct v4l2_buffer buf = {0};
|
||||
struct v4l2_plane planes[VIDEO_MAX_PLANES] = {0};
|
||||
buf.type = _RUN(capture_type);
|
||||
buf.type = run->capture_type;
|
||||
buf.memory = dev->io_method;
|
||||
buf.index = index;
|
||||
if (_D_IS_MPLANE) {
|
||||
if (run->capture_mplane) {
|
||||
buf.m.planes = planes;
|
||||
buf.length = 1;
|
||||
}
|
||||
@ -898,12 +884,12 @@ static int _device_open_queue_buffers(us_device_s *dev) {
|
||||
if (dev->io_method == V4L2_MEMORY_USERPTR) {
|
||||
// I am not sure, may be this is incorrect for mplane device,
|
||||
// but i don't have one which supports V4L2_MEMORY_USERPTR
|
||||
buf.m.userptr = (unsigned long)_RUN(hw_bufs)[index].raw.data;
|
||||
buf.length = _RUN(hw_bufs)[index].raw.allocated;
|
||||
buf.m.userptr = (unsigned long)run->hw_bufs[index].raw.data;
|
||||
buf.length = run->hw_bufs[index].raw.allocated;
|
||||
}
|
||||
|
||||
US_LOG_DEBUG("Calling us_xioctl(VIDIOC_QBUF) for buffer=%u ...", index);
|
||||
if (_D_XIOCTL(VIDIOC_QBUF, &buf) < 0) {
|
||||
if (us_xioctl(run->fd, VIDIOC_QBUF, &buf) < 0) {
|
||||
US_LOG_PERROR("Can't VIDIOC_QBUF");
|
||||
return -1;
|
||||
}
|
||||
@ -922,8 +908,8 @@ static int _device_apply_resolution(us_device_s *dev, unsigned width, unsigned h
|
||||
width, height, US_VIDEO_MAX_WIDTH, US_VIDEO_MAX_HEIGHT);
|
||||
return -1;
|
||||
}
|
||||
_RUN(width) = width;
|
||||
_RUN(height) = height;
|
||||
dev->run->width = width;
|
||||
dev->run->height = height;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -991,7 +977,7 @@ static int _device_query_control(
|
||||
US_MEMSET_ZERO(*query);
|
||||
query->id = cid;
|
||||
|
||||
if (_D_XIOCTL(VIDIOC_QUERYCTRL, query) < 0 || query->flags & V4L2_CTRL_FLAG_DISABLED) {
|
||||
if (us_xioctl(dev->run->fd, VIDIOC_QUERYCTRL, query) < 0 || query->flags & V4L2_CTRL_FLAG_DISABLED) {
|
||||
if (!quiet) {
|
||||
US_LOG_ERROR("Changing control %s is unsupported", name);
|
||||
}
|
||||
@ -1012,11 +998,11 @@ static void _device_set_control(
|
||||
return;
|
||||
}
|
||||
|
||||
struct v4l2_control ctl = {0};
|
||||
ctl.id = cid;
|
||||
ctl.value = value;
|
||||
|
||||
if (_D_XIOCTL(VIDIOC_S_CTRL, &ctl) < 0) {
|
||||
struct v4l2_control ctl = {
|
||||
.id = cid,
|
||||
.value = value,
|
||||
};
|
||||
if (us_xioctl(dev->run->fd, VIDIOC_S_CTRL, &ctl) < 0) {
|
||||
if (!quiet) {
|
||||
US_LOG_PERROR("Can't set control %s", name);
|
||||
}
|
||||
@ -41,12 +41,12 @@
|
||||
#include <linux/videodev2.h>
|
||||
#include <linux/v4l2-controls.h>
|
||||
|
||||
#include "../libs/tools.h"
|
||||
#include "../libs/array.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/threading.h"
|
||||
#include "../libs/frame.h"
|
||||
#include "../libs/xioctl.h"
|
||||
#include "tools.h"
|
||||
#include "array.h"
|
||||
#include "logging.h"
|
||||
#include "threading.h"
|
||||
#include "frame.h"
|
||||
#include "xioctl.h"
|
||||
|
||||
|
||||
#define US_VIDEO_MIN_WIDTH ((unsigned)160)
|
||||
@ -86,6 +86,7 @@ typedef struct {
|
||||
unsigned n_bufs;
|
||||
us_hw_buffer_s *hw_bufs;
|
||||
enum v4l2_buf_type capture_type;
|
||||
bool capture_mplane;
|
||||
bool capturing;
|
||||
bool persistent_timeout_reported;
|
||||
} us_device_runtime_s;
|
||||
@ -58,12 +58,11 @@ us_memsink_s *us_memsink_init(
|
||||
US_LOG_PERROR("%s-sink: Can't mmap shared memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return sink;
|
||||
|
||||
error:
|
||||
us_memsink_destroy(sink);
|
||||
return NULL;
|
||||
error:
|
||||
us_memsink_destroy(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void us_memsink_destroy(us_memsink_s *sink) {
|
||||
@ -176,6 +175,7 @@ int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *const key
|
||||
}
|
||||
|
||||
int retval = -2; // Not updated
|
||||
|
||||
if (sink->mem->magic == US_MEMSINK_MAGIC) {
|
||||
if (sink->mem->version != US_MEMSINK_VERSION) {
|
||||
US_LOG_ERROR("%s-sink: Protocol version mismatch: sink=%u, required=%u",
|
||||
@ -196,10 +196,10 @@ int us_memsink_client_get(us_memsink_s *sink, us_frame_s *frame, bool *const key
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
return retval;
|
||||
done:
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
US_LOG_PERROR("%s-sink: Can't unlock memory", sink->name);
|
||||
retval = -1;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -59,7 +59,8 @@
|
||||
|
||||
#define US_CALLOC(x_dest, x_nmemb) assert(((x_dest) = calloc((x_nmemb), sizeof(*(x_dest)))) != NULL)
|
||||
#define US_REALLOC(x_dest, x_nmemb) assert(((x_dest) = realloc((x_dest), (x_nmemb) * sizeof(*(x_dest)))) != NULL)
|
||||
#define US_DELETE(x_dest, x_free) { if (x_dest) { x_free(x_dest); } }
|
||||
#define US_DELETE(x_dest, x_free) { if (x_dest) { x_free(x_dest); x_dest = NULL; } }
|
||||
#define US_CLOSE_FD(x_dest, x_close) { if (x_dest >= 0) { x_close(x_dest); x_dest = -1; } }
|
||||
#define US_MEMSET_ZERO(x_obj) memset(&(x_obj), 0, sizeof(x_obj))
|
||||
|
||||
#define US_SNPRINTF(x_dest, x_size, x_fmt, ...) assert(snprintf((x_dest), (x_size), (x_fmt), ##__VA_ARGS__) > 0)
|
||||
|
||||
@ -77,9 +77,9 @@ int us_unjpeg(const us_frame_s *src, us_frame_s *dest, bool decode) {
|
||||
jpeg_finish_decompress(&jpeg);
|
||||
}
|
||||
|
||||
done:
|
||||
jpeg_destroy_decompress(&jpeg);
|
||||
return retval;
|
||||
done:
|
||||
jpeg_destroy_decompress(&jpeg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void _jpeg_error_handler(j_common_ptr jpeg) {
|
||||
|
||||
@ -63,16 +63,16 @@ static us_frame_s *_init_external(const char *path) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
# define CHUNK_SIZE ((size_t)(100 * 1024))
|
||||
const size_t chunk_size = 100 * 1024;
|
||||
while (true) {
|
||||
if (blank->used + CHUNK_SIZE >= blank->allocated) {
|
||||
us_frame_realloc_data(blank, blank->used + CHUNK_SIZE * 2);
|
||||
if (blank->used + chunk_size >= blank->allocated) {
|
||||
us_frame_realloc_data(blank, blank->used + chunk_size * 2);
|
||||
}
|
||||
|
||||
const size_t readed = fread(blank->data + blank->used, 1, CHUNK_SIZE, fp);
|
||||
const size_t readed = fread(blank->data + blank->used, 1, chunk_size, fp);
|
||||
blank->used += readed;
|
||||
|
||||
if (readed < CHUNK_SIZE) {
|
||||
if (readed < chunk_size) {
|
||||
if (feof(fp)) {
|
||||
break;
|
||||
} else {
|
||||
@ -81,7 +81,6 @@ static us_frame_s *_init_external(const char *path) {
|
||||
}
|
||||
}
|
||||
}
|
||||
# undef CHUNK_SIZE
|
||||
|
||||
us_frame_s *const decoded = us_frame_init();
|
||||
if (us_unjpeg(blank, decoded, false) < 0) {
|
||||
@ -94,12 +93,10 @@ static us_frame_s *_init_external(const char *path) {
|
||||
|
||||
goto ok;
|
||||
|
||||
error:
|
||||
us_frame_destroy(blank);
|
||||
blank = NULL;
|
||||
|
||||
ok:
|
||||
US_DELETE(fp, fclose);
|
||||
error:
|
||||
US_DELETE(blank, us_frame_destroy);
|
||||
|
||||
ok:
|
||||
US_DELETE(fp, fclose);
|
||||
return blank;
|
||||
}
|
||||
|
||||
@ -35,8 +35,8 @@
|
||||
#include "../libs/threading.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/frame.h"
|
||||
#include "../libs/device.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "workers.h"
|
||||
#include "m2m.h"
|
||||
|
||||
|
||||
@ -34,7 +34,6 @@ char *us_bufferevent_format_reason(short what) {
|
||||
strncat(reason, perror_str, 1023);
|
||||
free(perror_str);
|
||||
strcat(reason, " (");
|
||||
|
||||
# define FILL_REASON(x_bev, x_name) { \
|
||||
if (what & x_bev) { \
|
||||
if (first) { \
|
||||
@ -51,7 +50,6 @@ char *us_bufferevent_format_reason(short what) {
|
||||
FILL_REASON(BEV_EVENT_ERROR, "error");
|
||||
FILL_REASON(BEV_EVENT_TIMEOUT, "timeout");
|
||||
FILL_REASON(BEV_EVENT_EOF, "eof"); // cppcheck-suppress unreadVariable
|
||||
|
||||
# undef FILL_REASON
|
||||
|
||||
strcat(reason, ")");
|
||||
|
||||
@ -51,10 +51,8 @@ static char *_http_get_client_hostport(struct evhttp_request *request);
|
||||
#define _A_EVBUFFER_ADD(x_buf, x_data, x_size) assert(!evbuffer_add(x_buf, x_data, x_size))
|
||||
#define _A_EVBUFFER_ADD_PRINTF(x_buf, x_fmt, ...) assert(evbuffer_add_printf(x_buf, x_fmt, ##__VA_ARGS__) >= 0)
|
||||
|
||||
#define _RUN(x_next) server->run->x_next
|
||||
#define _STREAM(x_next) _RUN(stream->x_next)
|
||||
#define _VID(x_next) _STREAM(run->video->x_next)
|
||||
#define _EX(x_next) _RUN(exposed->x_next)
|
||||
#define _VID(x_next) server->run->stream->run->video->x_next
|
||||
#define _EX(x_next) server->run->exposed->x_next
|
||||
|
||||
|
||||
us_server_s *us_server_init(us_stream_s *stream) {
|
||||
@ -64,6 +62,7 @@ us_server_s *us_server_init(us_stream_s *stream) {
|
||||
|
||||
us_server_runtime_s *run;
|
||||
US_CALLOC(run, 1);
|
||||
run->ext_fd = -1;
|
||||
run->stream = stream;
|
||||
run->exposed = exposed;
|
||||
|
||||
@ -88,78 +87,81 @@ us_server_s *us_server_init(us_stream_s *stream) {
|
||||
}
|
||||
|
||||
void us_server_destroy(us_server_s *server) {
|
||||
if (_RUN(refresher) != NULL) {
|
||||
event_del(_RUN(refresher));
|
||||
event_free(_RUN(refresher));
|
||||
us_server_runtime_s *const run = server->run;
|
||||
|
||||
if (run->refresher != NULL) {
|
||||
event_del(run->refresher);
|
||||
event_free(run->refresher);
|
||||
}
|
||||
|
||||
if (_RUN(request_watcher) != NULL) {
|
||||
event_del(_RUN(request_watcher));
|
||||
event_free(_RUN(request_watcher));
|
||||
if (run->request_watcher != NULL) {
|
||||
event_del(run->request_watcher);
|
||||
event_free(run->request_watcher);
|
||||
}
|
||||
|
||||
evhttp_free(_RUN(http));
|
||||
if (_RUN(ext_fd) >= 0) {
|
||||
close(_RUN(ext_fd));
|
||||
}
|
||||
event_base_free(_RUN(base));
|
||||
evhttp_free(run->http);
|
||||
US_CLOSE_FD(run->ext_fd, close);
|
||||
event_base_free(run->base);
|
||||
|
||||
# if LIBEVENT_VERSION_NUMBER >= 0x02010100
|
||||
libevent_global_shutdown();
|
||||
# endif
|
||||
|
||||
US_LIST_ITERATE(_RUN(stream_clients), client, {
|
||||
US_LIST_ITERATE(run->stream_clients, client, { // cppcheck-suppress constStatement
|
||||
free(client->key);
|
||||
free(client->hostport);
|
||||
free(client);
|
||||
});
|
||||
|
||||
US_DELETE(_RUN(auth_token), free);
|
||||
US_DELETE(run->auth_token, free);
|
||||
|
||||
us_frame_destroy(_EX(frame));
|
||||
free(_RUN(exposed));
|
||||
us_frame_destroy(run->exposed->frame);
|
||||
free(run->exposed);
|
||||
free(server->run);
|
||||
free(server);
|
||||
}
|
||||
|
||||
int us_server_listen(us_server_s *server) {
|
||||
us_server_runtime_s *const run = server->run;
|
||||
us_stream_s *const stream = run->stream;
|
||||
|
||||
{
|
||||
if (server->static_path[0] != '\0') {
|
||||
US_LOG_INFO("Enabling HTTP file server: %s", server->static_path);
|
||||
evhttp_set_gencb(_RUN(http), _http_callback_static, (void *)server);
|
||||
evhttp_set_gencb(run->http, _http_callback_static, (void *)server);
|
||||
} else {
|
||||
assert(!evhttp_set_cb(_RUN(http), "/", _http_callback_root, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/favicon.ico", _http_callback_favicon, (void *)server));
|
||||
assert(!evhttp_set_cb(run->http, "/", _http_callback_root, (void *)server));
|
||||
assert(!evhttp_set_cb(run->http, "/favicon.ico", _http_callback_favicon, (void *)server));
|
||||
}
|
||||
assert(!evhttp_set_cb(_RUN(http), "/state", _http_callback_state, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/snapshot", _http_callback_snapshot, (void *)server));
|
||||
assert(!evhttp_set_cb(_RUN(http), "/stream", _http_callback_stream, (void *)server));
|
||||
assert(!evhttp_set_cb(run->http, "/state", _http_callback_state, (void *)server));
|
||||
assert(!evhttp_set_cb(run->http, "/snapshot", _http_callback_snapshot, (void *)server));
|
||||
assert(!evhttp_set_cb(run->http, "/stream", _http_callback_stream, (void *)server));
|
||||
}
|
||||
|
||||
us_frame_copy(_STREAM(blank), _EX(frame));
|
||||
us_frame_copy(stream->blank, _EX(frame));
|
||||
_EX(notify_last_width) = _EX(frame->width);
|
||||
_EX(notify_last_height) = _EX(frame->height);
|
||||
|
||||
if (server->exit_on_no_clients > 0) {
|
||||
_RUN(last_request_ts) = us_get_now_monotonic();
|
||||
run->last_request_ts = us_get_now_monotonic();
|
||||
struct timeval interval = {0};
|
||||
interval.tv_usec = 100000;
|
||||
assert((_RUN(request_watcher) = event_new(_RUN(base), -1, EV_PERSIST, _http_request_watcher, server)) != NULL);
|
||||
assert(!event_add(_RUN(request_watcher), &interval));
|
||||
assert((run->request_watcher = event_new(run->base, -1, EV_PERSIST, _http_request_watcher, server)) != NULL);
|
||||
assert(!event_add(run->request_watcher, &interval));
|
||||
}
|
||||
|
||||
{
|
||||
struct timeval interval = {0};
|
||||
if (_STREAM(dev->desired_fps) > 0) {
|
||||
interval.tv_usec = 1000000 / (_STREAM(dev->desired_fps) * 2);
|
||||
if (stream->dev->desired_fps > 0) {
|
||||
interval.tv_usec = 1000000 / (stream->dev->desired_fps * 2);
|
||||
} else {
|
||||
interval.tv_usec = 16000; // ~60fps
|
||||
}
|
||||
assert((_RUN(refresher) = event_new(_RUN(base), -1, EV_PERSIST, _http_refresher, server)) != NULL);
|
||||
assert(!event_add(_RUN(refresher), &interval));
|
||||
assert((run->refresher = event_new(run->base, -1, EV_PERSIST, _http_refresher, server)) != NULL);
|
||||
assert(!event_add(run->refresher, &interval));
|
||||
}
|
||||
|
||||
evhttp_set_timeout(_RUN(http), server->timeout);
|
||||
evhttp_set_timeout(run->http, server->timeout);
|
||||
|
||||
if (server->user[0] != '\0') {
|
||||
char *encoded_token = NULL;
|
||||
@ -169,7 +171,7 @@ int us_server_listen(us_server_s *server) {
|
||||
us_base64_encode((uint8_t *)raw_token, strlen(raw_token), &encoded_token, NULL);
|
||||
free(raw_token);
|
||||
|
||||
US_ASPRINTF(_RUN(auth_token), "Basic %s", encoded_token);
|
||||
US_ASPRINTF(run->auth_token, "Basic %s", encoded_token);
|
||||
free(encoded_token);
|
||||
|
||||
US_LOG_INFO("Using HTTP basic auth");
|
||||
@ -177,8 +179,8 @@ int us_server_listen(us_server_s *server) {
|
||||
|
||||
if (server->unix_path[0] != '\0') {
|
||||
US_LOG_DEBUG("Binding HTTP to UNIX socket '%s' ...", server->unix_path);
|
||||
if ((_RUN(ext_fd) = us_evhttp_bind_unix(
|
||||
_RUN(http),
|
||||
if ((run->ext_fd = us_evhttp_bind_unix(
|
||||
run->http,
|
||||
server->unix_path,
|
||||
server->unix_rm,
|
||||
server->unix_mode)) < 0
|
||||
@ -190,7 +192,7 @@ int us_server_listen(us_server_s *server) {
|
||||
# ifdef WITH_SYSTEMD
|
||||
} else if (server->systemd) {
|
||||
US_LOG_DEBUG("Binding HTTP to systemd socket ...");
|
||||
if ((_RUN(ext_fd) = us_evhttp_bind_systemd(_RUN(http))) < 0) {
|
||||
if ((run->ext_fd = us_evhttp_bind_systemd(run->http)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
US_LOG_INFO("Listening systemd socket ...");
|
||||
@ -198,7 +200,7 @@ int us_server_listen(us_server_s *server) {
|
||||
|
||||
} else {
|
||||
US_LOG_DEBUG("Binding HTTP to [%s]:%u ...", server->host, server->port);
|
||||
if (evhttp_bind_socket(_RUN(http), server->host, server->port) < 0) {
|
||||
if (evhttp_bind_socket(run->http, server->host, server->port) < 0) {
|
||||
US_LOG_PERROR("Can't bind HTTP on [%s]:%u", server->host, server->port)
|
||||
return -1;
|
||||
}
|
||||
@ -210,18 +212,20 @@ int us_server_listen(us_server_s *server) {
|
||||
|
||||
void us_server_loop(us_server_s *server) {
|
||||
US_LOG_INFO("Starting HTTP eventloop ...");
|
||||
event_base_dispatch(_RUN(base));
|
||||
event_base_dispatch(server->run->base);
|
||||
US_LOG_INFO("HTTP eventloop stopped");
|
||||
}
|
||||
|
||||
void us_server_loop_break(us_server_s *server) {
|
||||
event_base_loopbreak(_RUN(base));
|
||||
event_base_loopbreak(server->run->base);
|
||||
}
|
||||
|
||||
#define ADD_HEADER(x_key, x_value) assert(!evhttp_add_header(evhttp_request_get_output_headers(request), x_key, x_value))
|
||||
|
||||
static int _http_preprocess_request(struct evhttp_request *request, us_server_s *server) {
|
||||
_RUN(last_request_ts) = us_get_now_monotonic();
|
||||
us_server_runtime_s *const run = server->run;
|
||||
|
||||
run->last_request_ts = us_get_now_monotonic();
|
||||
|
||||
if (server->allow_origin[0] != '\0') {
|
||||
const char *const cors_headers = _http_get_header(request, "Access-Control-Request-Headers");
|
||||
@ -242,10 +246,10 @@ static int _http_preprocess_request(struct evhttp_request *request, us_server_s
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_RUN(auth_token) != NULL) {
|
||||
if (run->auth_token != NULL) {
|
||||
const char *const token = _http_get_header(request, "Authorization");
|
||||
|
||||
if (token == NULL || strcmp(token, _RUN(auth_token)) != 0) {
|
||||
if (token == NULL || strcmp(token, run->auth_token) != 0) {
|
||||
ADD_HEADER("WWW-Authenticate", "Basic realm=\"Restricted area\"");
|
||||
evhttp_send_reply(request, 401, "Unauthorized", NULL);
|
||||
return -1;
|
||||
@ -269,24 +273,22 @@ static int _http_preprocess_request(struct evhttp_request *request, us_server_s
|
||||
static int _http_check_run_compat_action(struct evhttp_request *request, void *v_server) {
|
||||
// MJPG-Streamer compatibility layer
|
||||
|
||||
struct evkeyvalq params;
|
||||
int error = 0;
|
||||
int retval = -1;
|
||||
|
||||
struct evkeyvalq params;
|
||||
evhttp_parse_query(evhttp_request_get_uri(request), ¶ms);
|
||||
const char *const action = evhttp_find_header(¶ms, "action");
|
||||
|
||||
if (action && !strcmp(action, "snapshot")) {
|
||||
_http_callback_snapshot(request, v_server);
|
||||
goto ok;
|
||||
retval = 0;
|
||||
} else if (action && !strcmp(action, "stream")) {
|
||||
_http_callback_stream(request, v_server);
|
||||
goto ok;
|
||||
retval = 0;
|
||||
}
|
||||
|
||||
error = -1;
|
||||
ok:
|
||||
evhttp_clear_headers(¶ms);
|
||||
return error;
|
||||
evhttp_clear_headers(¶ms);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#define COMPAT_REQUEST { \
|
||||
@ -338,14 +340,12 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
|
||||
{
|
||||
const char *uri_path;
|
||||
|
||||
if ((uri = evhttp_uri_parse(evhttp_request_get_uri(request))) == NULL) {
|
||||
goto bad_request;
|
||||
}
|
||||
if ((uri_path = (char *)evhttp_uri_get_path(uri)) == NULL) {
|
||||
uri_path = "/";
|
||||
}
|
||||
|
||||
if ((decoded_path = evhttp_uridecode(uri_path, 0, NULL)) == NULL) {
|
||||
goto bad_request;
|
||||
}
|
||||
@ -383,34 +383,34 @@ static void _http_callback_static(struct evhttp_request *request, void *v_server
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bad_request:
|
||||
evhttp_send_error(request, HTTP_BADREQUEST, NULL);
|
||||
goto cleanup;
|
||||
bad_request:
|
||||
evhttp_send_error(request, HTTP_BADREQUEST, NULL);
|
||||
goto cleanup;
|
||||
|
||||
not_found:
|
||||
evhttp_send_error(request, HTTP_NOTFOUND, NULL);
|
||||
goto cleanup;
|
||||
not_found:
|
||||
evhttp_send_error(request, HTTP_NOTFOUND, NULL);
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
if (fd >= 0) {
|
||||
close(fd);
|
||||
}
|
||||
US_DELETE(static_path, free);
|
||||
US_DELETE(buf, evbuffer_free);
|
||||
US_DELETE(decoded_path, free);
|
||||
US_DELETE(uri, evhttp_uri_free);
|
||||
cleanup:
|
||||
US_CLOSE_FD(fd, close); // cppcheck-suppress unreadVariable
|
||||
US_DELETE(static_path, free);
|
||||
US_DELETE(buf, evbuffer_free);
|
||||
US_DELETE(decoded_path, free);
|
||||
US_DELETE(uri, evhttp_uri_free);
|
||||
}
|
||||
|
||||
#undef COMPAT_REQUEST
|
||||
|
||||
static void _http_callback_state(struct evhttp_request *request, void *v_server) {
|
||||
us_server_s *const server = (us_server_s *)v_server;
|
||||
us_server_runtime_s *const run = server->run;
|
||||
us_stream_s *const stream = run->stream;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
us_encoder_type_e enc_type;
|
||||
unsigned enc_quality;
|
||||
us_encoder_get_runtime_params(_STREAM(enc), &enc_type, &enc_quality);
|
||||
us_encoder_get_runtime_params(stream->enc, &enc_type, &enc_quality);
|
||||
|
||||
struct evbuffer *buf;
|
||||
_A_EVBUFFER_NEW(buf);
|
||||
@ -424,28 +424,28 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
enc_quality
|
||||
);
|
||||
|
||||
if (_STREAM(run->h264) != NULL) {
|
||||
if (stream->run->h264 != NULL) {
|
||||
_A_EVBUFFER_ADD_PRINTF(buf,
|
||||
" \"h264\": {\"bitrate\": %u, \"gop\": %u, \"online\": %s},",
|
||||
_STREAM(h264_bitrate),
|
||||
_STREAM(h264_gop),
|
||||
us_bool_to_string(atomic_load(&_STREAM(run->h264->online)))
|
||||
stream->h264_bitrate,
|
||||
stream->h264_gop,
|
||||
us_bool_to_string(atomic_load(&stream->run->h264->online))
|
||||
);
|
||||
}
|
||||
|
||||
if (_STREAM(sink) != NULL || _STREAM(h264_sink) != NULL) {
|
||||
if (stream->sink != NULL || stream->h264_sink != NULL) {
|
||||
_A_EVBUFFER_ADD_PRINTF(buf, " \"sinks\": {");
|
||||
if (_STREAM(sink) != NULL) {
|
||||
if (stream->sink != NULL) {
|
||||
_A_EVBUFFER_ADD_PRINTF(buf,
|
||||
"\"jpeg\": {\"has_clients\": %s}",
|
||||
us_bool_to_string(atomic_load(&_STREAM(sink->has_clients)))
|
||||
us_bool_to_string(atomic_load(&stream->sink->has_clients))
|
||||
);
|
||||
}
|
||||
if (_STREAM(h264_sink) != NULL) {
|
||||
if (stream->h264_sink != NULL) {
|
||||
_A_EVBUFFER_ADD_PRINTF(buf,
|
||||
"%s\"h264\": {\"has_clients\": %s}",
|
||||
(_STREAM(sink) ? ", " : ""),
|
||||
us_bool_to_string(atomic_load(&_STREAM(h264_sink->has_clients)))
|
||||
(stream->sink ? ", " : ""),
|
||||
us_bool_to_string(atomic_load(&stream->h264_sink->has_clients))
|
||||
);
|
||||
}
|
||||
_A_EVBUFFER_ADD_PRINTF(buf, "},");
|
||||
@ -458,13 +458,13 @@ static void _http_callback_state(struct evhttp_request *request, void *v_server)
|
||||
(server->fake_width ? server->fake_width : _EX(frame->width)),
|
||||
(server->fake_height ? server->fake_height : _EX(frame->height)),
|
||||
us_bool_to_string(_EX(frame->online)),
|
||||
_STREAM(dev->desired_fps),
|
||||
stream->dev->desired_fps,
|
||||
_EX(captured_fps),
|
||||
_EX(queued_fps),
|
||||
_RUN(stream_clients_count)
|
||||
run->stream_clients_count
|
||||
);
|
||||
|
||||
US_LIST_ITERATE(_RUN(stream_clients), client, {
|
||||
US_LIST_ITERATE(run->stream_clients, client, { // cppcheck-suppress constStatement
|
||||
_A_EVBUFFER_ADD_PRINTF(buf,
|
||||
"\"%" PRIx64 "\": {\"fps\": %u, \"extra_headers\": %s, \"advance_headers\": %s,"
|
||||
" \"dual_final_frames\": %s, \"zero_data\": %s, \"key\": \"%s\"}%s",
|
||||
@ -544,6 +544,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
// https://github.com/libevent/libevent/blob/29cc8386a2f7911eaa9336692a2c5544d8b4734f/http.c#L1458
|
||||
|
||||
us_server_s *const server = (us_server_s *)v_server;
|
||||
us_server_runtime_s *const run = server->run;
|
||||
|
||||
PREPROCESS_REQUEST;
|
||||
|
||||
@ -570,9 +571,9 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
client->hostport = _http_get_client_hostport(request);
|
||||
client->id = us_get_now_id();
|
||||
|
||||
US_LIST_APPEND_C(_RUN(stream_clients), client, _RUN(stream_clients_count));
|
||||
US_LIST_APPEND_C(run->stream_clients, client, run->stream_clients_count);
|
||||
|
||||
if (_RUN(stream_clients_count) == 1) {
|
||||
if (run->stream_clients_count == 1) {
|
||||
atomic_store(&_VID(has_clients), true);
|
||||
# ifdef WITH_GPIO
|
||||
us_gpio_set_has_http_clients(true);
|
||||
@ -580,10 +581,10 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
}
|
||||
|
||||
US_LOG_INFO("HTTP: NEW client (now=%u): %s, id=%" PRIx64,
|
||||
_RUN(stream_clients_count), client->hostport, client->id);
|
||||
run->stream_clients_count, client->hostport, client->id);
|
||||
|
||||
struct bufferevent *const buf_event = evhttp_connection_get_bufferevent(conn);
|
||||
if (server->tcp_nodelay && !_RUN(ext_fd)) {
|
||||
if (server->tcp_nodelay && run->ext_fd >= 0) {
|
||||
US_LOG_DEBUG("HTTP: Setting up TCP_NODELAY to the client %s ...", client->hostport);
|
||||
const evutil_socket_t fd = bufferevent_getfd(buf_event);
|
||||
assert(fd >= 0);
|
||||
@ -602,8 +603,6 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
#undef PREPROCESS_REQUEST
|
||||
|
||||
static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_client) {
|
||||
# define BOUNDARY "boundarydonotcross"
|
||||
|
||||
us_stream_client_s *const client = (us_stream_client_s *)v_client;
|
||||
us_server_s *const server = client->server;
|
||||
|
||||
@ -639,6 +638,8 @@ static void _http_callback_stream_write(struct bufferevent *buf_event, void *v_c
|
||||
// Кроме того, advance_headers форсит отключение заголовков X-UStreamer-*
|
||||
// по тем же причинам, по которым у нас нет Content-Length.
|
||||
|
||||
# define BOUNDARY "boundarydonotcross"
|
||||
|
||||
# define ADD_ADVANCE_HEADERS \
|
||||
_A_EVBUFFER_ADD_PRINTF(buf, \
|
||||
"Content-Type: image/jpeg" RN "X-Timestamp: %.06Lf" RN RN, us_get_now_real())
|
||||
@ -753,10 +754,11 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
|
||||
|
||||
us_stream_client_s *const client = (us_stream_client_s *)v_client;
|
||||
us_server_s *const server = client->server;
|
||||
us_server_runtime_s *const run = server->run;
|
||||
|
||||
US_LIST_REMOVE_C(_RUN(stream_clients), client, _RUN(stream_clients_count));
|
||||
US_LIST_REMOVE_C(run->stream_clients, client, run->stream_clients_count);
|
||||
|
||||
if (_RUN(stream_clients_count) == 0) {
|
||||
if (run->stream_clients_count == 0) {
|
||||
atomic_store(&_VID(has_clients), false);
|
||||
# ifdef WITH_GPIO
|
||||
us_gpio_set_has_http_clients(false);
|
||||
@ -765,10 +767,10 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
|
||||
|
||||
char *const reason = us_bufferevent_format_reason(what);
|
||||
US_LOG_INFO("HTTP: DEL client (now=%u): %s, id=%" PRIx64 ", %s",
|
||||
_RUN(stream_clients_count), client->hostport, client->id, reason);
|
||||
run->stream_clients_count, client->hostport, client->id, reason);
|
||||
free(reason);
|
||||
|
||||
struct evhttp_connection *const conn = evhttp_request_get_connection(client->request);
|
||||
struct evhttp_connection *conn = evhttp_request_get_connection(client->request);
|
||||
US_DELETE(conn, evhttp_connection_free);
|
||||
|
||||
free(client->key);
|
||||
@ -777,10 +779,12 @@ static void _http_callback_stream_error(struct bufferevent *buf_event, short wha
|
||||
}
|
||||
|
||||
static void _http_queue_send_stream(us_server_s *server, bool stream_updated, bool frame_updated) {
|
||||
us_server_runtime_s *const run = server->run;
|
||||
|
||||
bool has_clients = false;
|
||||
bool queued = false;
|
||||
|
||||
US_LIST_ITERATE(_RUN(stream_clients), client, {
|
||||
US_LIST_ITERATE(run->stream_clients, client, { // cppcheck-suppress constStatement
|
||||
struct evhttp_connection *const conn = evhttp_request_get_connection(client->request);
|
||||
if (conn != NULL) {
|
||||
// Фикс для бага WebKit. При включенной опции дропа одинаковых фреймов,
|
||||
@ -832,16 +836,17 @@ static void _http_request_watcher(int fd, short what, void *v_server) {
|
||||
(void)fd;
|
||||
(void)what;
|
||||
|
||||
us_server_s *server = (us_server_s *)v_server;
|
||||
us_server_s *const server = (us_server_s *)v_server;
|
||||
us_server_runtime_s *const run = server->run;
|
||||
const long double now = us_get_now_monotonic();
|
||||
|
||||
if (us_stream_has_clients(_RUN(stream))) {
|
||||
_RUN(last_request_ts) = now;
|
||||
} else if (_RUN(last_request_ts) + server->exit_on_no_clients < now) {
|
||||
if (us_stream_has_clients(run->stream)) {
|
||||
run->last_request_ts = now;
|
||||
} else if (run->last_request_ts + server->exit_on_no_clients < now) {
|
||||
US_LOG_INFO("HTTP: No requests or HTTP/sink clients found in last %u seconds, exiting ...",
|
||||
server->exit_on_no_clients);
|
||||
us_process_suicide();
|
||||
_RUN(last_request_ts) = now;
|
||||
run->last_request_ts = now;
|
||||
}
|
||||
}
|
||||
|
||||
@ -923,10 +928,11 @@ static bool _expose_new_frame(us_server_s *server) {
|
||||
_EX(frame->online), _EX(expose_end_ts) - _EX(expose_begin_ts));
|
||||
|
||||
updated = true;
|
||||
not_updated:
|
||||
atomic_store(&_VID(updated), false);
|
||||
US_MUTEX_UNLOCK(_VID(mutex));
|
||||
return updated;
|
||||
|
||||
not_updated:
|
||||
atomic_store(&_VID(updated), false);
|
||||
US_MUTEX_UNLOCK(_VID(mutex));
|
||||
return updated;
|
||||
}
|
||||
|
||||
static const char *_http_get_header(struct evhttp_request *request, const char *key) {
|
||||
|
||||
@ -125,7 +125,7 @@ typedef struct {
|
||||
|
||||
struct event *refresher;
|
||||
us_stream_s *stream;
|
||||
us_exposed_s *exposed;
|
||||
us_exposed_s *exposed;
|
||||
|
||||
us_stream_client_s *stream_clients;
|
||||
unsigned stream_clients_count;
|
||||
|
||||
@ -42,14 +42,12 @@ char *us_find_static_file_path(const char *root_path, const char *request_path)
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
LOAD_STAT;
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
US_LOG_VERBOSE("HTTP: Requested static path %s is a directory, trying %s/index.html", path, path);
|
||||
strcat(path, "/index.html");
|
||||
LOAD_STAT;
|
||||
}
|
||||
|
||||
# undef LOAD_STAT
|
||||
|
||||
if (!S_ISREG(st.st_mode)) {
|
||||
@ -64,12 +62,10 @@ char *us_find_static_file_path(const char *root_path, const char *request_path)
|
||||
|
||||
goto ok;
|
||||
|
||||
error:
|
||||
US_DELETE(path, free);
|
||||
path = NULL;
|
||||
|
||||
ok:
|
||||
free(simplified_path);
|
||||
error:
|
||||
US_DELETE(path, free);
|
||||
|
||||
ok:
|
||||
free(simplified_path);
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -25,19 +25,16 @@
|
||||
|
||||
evutil_socket_t us_evhttp_bind_unix(struct evhttp *http, const char *path, bool rm, mode_t mode) {
|
||||
struct sockaddr_un addr = {0};
|
||||
const size_t max_sun_path = sizeof(addr.sun_path) - 1;
|
||||
|
||||
# define MAX_SUN_PATH (sizeof(addr.sun_path) - 1)
|
||||
|
||||
if (strlen(path) > MAX_SUN_PATH) {
|
||||
US_LOG_ERROR("UNIX socket path is too long; max=%zu", MAX_SUN_PATH);
|
||||
if (strlen(path) > max_sun_path) {
|
||||
US_LOG_ERROR("UNIX socket path is too long; max=%zu", max_sun_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
strncpy(addr.sun_path, path, MAX_SUN_PATH);
|
||||
strncpy(addr.sun_path, path, max_sun_path);
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
# undef MAX_SUN_PATH
|
||||
|
||||
const evutil_socket_t fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
assert(fd >= 0);
|
||||
assert(!evutil_make_socket_nonblocking(fd));
|
||||
|
||||
@ -29,9 +29,9 @@
|
||||
#include "../libs/tools.h"
|
||||
#include "../libs/threading.h"
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/device.h"
|
||||
|
||||
#include "options.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#include "stream.h"
|
||||
#include "http/server.h"
|
||||
|
||||
@ -38,8 +38,8 @@
|
||||
#include "../libs/frame.h"
|
||||
#include "../libs/memsink.h"
|
||||
#include "../libs/options.h"
|
||||
#include "../libs/device.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#include "blank.h"
|
||||
#include "stream.h"
|
||||
|
||||
@ -37,9 +37,9 @@
|
||||
#include "../libs/logging.h"
|
||||
#include "../libs/frame.h"
|
||||
#include "../libs/memsink.h"
|
||||
#include "../libs/device.h"
|
||||
|
||||
#include "blank.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#include "workers.h"
|
||||
#include "h264.h"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user