mirror of
https://github.com/pikvm/ustreamer.git
synced 2025-12-23 18:50:00 +00:00
h264
This commit is contained in:
parent
6eb5e62aae
commit
944bd89b4e
55
Makefile
55
Makefile
@ -33,11 +33,10 @@ _USTR_SRCS = $(shell ls \
|
||||
src/ustreamer/encoders/hw/*.c \
|
||||
)
|
||||
|
||||
_REC_LIBS = $(_COMMON_LIBS) -lrt -lbcm_host -lvcos -lmmal -lmmal_core -lmmal_util -lmmal_vc_client -lmmal_components -L$(RPI_VC_LIBS)
|
||||
_REC_LIBS = $(_COMMON_LIBS) -lrt
|
||||
_REC_SRCS = $(shell ls \
|
||||
src/libs/common/*.c \
|
||||
src/libs/memsink/*.c \
|
||||
src/libs/h264/*.c \
|
||||
src/recorder/*.c \
|
||||
)
|
||||
|
||||
@ -47,17 +46,14 @@ $(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
|
||||
endef
|
||||
|
||||
|
||||
ifneq ($(call optbool,$(WITH_MEMSINK)),)
|
||||
_USTR_LIBS += -lrt
|
||||
override CFLAGS += -DWITH_MEMSINK
|
||||
_USTR_SRCS += $(shell ls src/libs/memsink/*.c)
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(call optbool,$(WITH_OMX)),)
|
||||
_USTR_LIBS += -lbcm_host -lvcos -lopenmaxil -L$(RPI_VC_LIBS)
|
||||
_USTR_LIBS += -lrt -lbcm_host -lvcos -lopenmaxil -lmmal -lmmal_core -lmmal_util -lmmal_vc_client -lmmal_components -L$(RPI_VC_LIBS)
|
||||
override CFLAGS += -DWITH_OMX -DOMX_SKIP64BIT -I$(RPI_VC_HEADERS)
|
||||
_USTR_SRCS += $(shell ls src/ustreamer/encoders/omx/*.c)
|
||||
_USTR_SRCS += $(shell ls \
|
||||
src/libs/memsink/*.c \
|
||||
src/ustreamer/encoders/omx/*.c \
|
||||
src/ustreamer/h264/*.c \
|
||||
)
|
||||
endif
|
||||
|
||||
|
||||
@ -83,13 +79,6 @@ override CFLAGS += -DWITH_SETPROCTITLE
|
||||
endif
|
||||
|
||||
|
||||
ifneq ($(call optbool,$(WITH_MEMSINK)),)
|
||||
ifneq ($(call optbool,$(WITH_OMX)),)
|
||||
_ENABLE_REC = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
|
||||
# =====
|
||||
all: $(USTR) $(REC)
|
||||
|
||||
@ -98,14 +87,14 @@ install: $(USTR) $(REC)
|
||||
install -Dm755 $(USTR) $(DESTDIR)$(PREFIX)/bin/$(USTR)
|
||||
install -Dm644 $(USTR).1 $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
|
||||
gzip $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
|
||||
#ifneq ($(_ENABLE_REC),)
|
||||
#ifneq ($(call optbool,$(WITH_OMX)),)
|
||||
# install -Dm755 $(DESTDIR)$(PREFIX)/bin/$(REC)
|
||||
#endif
|
||||
|
||||
|
||||
install-strip: install
|
||||
strip $(DESTDIR)$(PREFIX)/bin/$(USTR)
|
||||
#ifneq ($(_ENABLE_REC),)
|
||||
#ifneq ($(call optbool,$(WITH_OMX)),)
|
||||
# strip $(DESTDIR)$(PREFIX)/bin/$(REC)
|
||||
#endif
|
||||
|
||||
@ -113,6 +102,9 @@ install-strip: install
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/$(USTR) \
|
||||
$(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
|
||||
#ifneq ($(call optbool,$(WITH_OMX)),)
|
||||
# rm -f $(DESTDIR)$(PREFIX)/bin/$(REC)
|
||||
#endif
|
||||
|
||||
|
||||
regen:
|
||||
@ -121,22 +113,22 @@ regen:
|
||||
|
||||
|
||||
$(USTR): $(_USTR_SRCS:%.c=$(BUILD)/%.o)
|
||||
$(info -- LD $@)
|
||||
$(info == LD $@)
|
||||
@ $(CC) $^ -o $@ $(LDFLAGS) $(_USTR_LIBS)
|
||||
$(info == CC = $(CC))
|
||||
$(info == LIBS = $(_USTR_LIBS))
|
||||
$(info == CFLAGS = $(CFLAGS))
|
||||
$(info == LDFLAGS = $(LDFLAGS))
|
||||
$(info :: CC = $(CC))
|
||||
$(info :: LIBS = $(_USTR_LIBS))
|
||||
$(info :: CFLAGS = $(CFLAGS))
|
||||
$(info :: LDFLAGS = $(LDFLAGS))
|
||||
|
||||
|
||||
ifneq ($(_ENABLE_REC),)
|
||||
ifneq ($(call optbool,$(WITH_OMX)),)
|
||||
$(REC): $(_REC_SRCS:%.c=$(BUILD)/%.o)
|
||||
$(info -- LD $@)
|
||||
$(info == LD $@)
|
||||
@ $(CC) $^ -o $@ $(LDFLAGS) $(_REC_LIBS)
|
||||
$(info == CC = $(CC))
|
||||
$(info == LIBS = $(_REC_LIBS))
|
||||
$(info == CFLAGS = $(CFLAGS))
|
||||
$(info == LDFLAGS = $(LDFLAGS))
|
||||
$(info :: CC = $(CC))
|
||||
$(info :: LIBS = $(_REC_LIBS))
|
||||
$(info :: CFLAGS = $(CFLAGS))
|
||||
$(info :: LDFLAGS = $(LDFLAGS))
|
||||
else
|
||||
$(REC):
|
||||
@ true
|
||||
@ -193,4 +185,5 @@ clean:
|
||||
rm -rf pkg/arch/pkg pkg/arch/src pkg/arch/v*.tar.gz pkg/arch/ustreamer-*.pkg.tar.{xz,zst}
|
||||
rm -rf $(USTR) $(REC) $(BUILD) vgcore.* *.sock
|
||||
|
||||
|
||||
.PHONY: linters
|
||||
|
||||
@ -18,7 +18,6 @@ commands = cppcheck \
|
||||
--suppress=variableScope \
|
||||
--inline-suppr \
|
||||
-DCHAR_BIT=8 \
|
||||
-DWITH_MEMSINK \
|
||||
-DWITH_OMX \
|
||||
-DWITH_GPIO \
|
||||
src
|
||||
|
||||
@ -28,27 +28,27 @@ static int _flock_timedwait_monotonic(int fd, long double timeout);
|
||||
|
||||
|
||||
memsink_s *memsink_init(const char *name, const char *prefix, bool server, mode_t mode, bool rm, unsigned timeout) {
|
||||
memsink_s *memsink;
|
||||
A_CALLOC(memsink, 1);
|
||||
memsink->name = name;
|
||||
memsink->server = server;
|
||||
memsink->rm = rm;
|
||||
memsink->timeout = timeout;
|
||||
memsink->fd = -1;
|
||||
memsink->mem = MAP_FAILED;
|
||||
memsink->sig_sem = SEM_FAILED;
|
||||
memsink_s *sink;
|
||||
A_CALLOC(sink, 1);
|
||||
sink->name = name;
|
||||
sink->server = server;
|
||||
sink->rm = rm;
|
||||
sink->timeout = timeout;
|
||||
sink->fd = -1;
|
||||
sink->mem = MAP_FAILED;
|
||||
sink->sig_sem = SEM_FAILED;
|
||||
|
||||
A_CALLOC(memsink->mem_name, strlen(prefix) + 8);
|
||||
A_CALLOC(memsink->sig_name, strlen(prefix) + 8);
|
||||
A_CALLOC(sink->mem_name, strlen(prefix) + 8);
|
||||
A_CALLOC(sink->sig_name, strlen(prefix) + 8);
|
||||
|
||||
sprintf(memsink->mem_name, "%s.mem", prefix);
|
||||
sprintf(memsink->sig_name, "%s.sig", prefix);
|
||||
sprintf(sink->mem_name, "%s.mem", prefix);
|
||||
sprintf(sink->sig_name, "%s.sig", prefix);
|
||||
|
||||
LOG_INFO("Using %s sink: %s.{mem,sig}", name, prefix);
|
||||
LOG_INFO("Using '%s' sink: %s.{mem,sig}", name, prefix);
|
||||
|
||||
const int flags = (server ? O_RDWR | O_CREAT : O_RDWR);
|
||||
# define OPEN_SIGNAL { \
|
||||
if ((memsink->sig_sem = sem_open(memsink->sig_name, flags, mode, 0)) == SEM_FAILED) { \
|
||||
if ((sink->sig_sem = sem_open(sink->sig_name, flags, mode, 0)) == SEM_FAILED) { \
|
||||
LOG_PERROR("Can't open %s sink signal semaphore", name); \
|
||||
goto error; \
|
||||
} \
|
||||
@ -59,22 +59,22 @@ memsink_s *memsink_init(const char *name, const char *prefix, bool server, mode_
|
||||
}
|
||||
|
||||
{ // Shared memory
|
||||
if ((memsink->fd = shm_open(memsink->mem_name, flags, mode)) == -1) {
|
||||
if ((sink->fd = shm_open(sink->mem_name, flags, mode)) == -1) {
|
||||
LOG_PERROR("Can't open %s sink memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (memsink->server && ftruncate(memsink->fd, sizeof(memsink_shared_s)) < 0) {
|
||||
if (sink->server && ftruncate(sink->fd, sizeof(memsink_shared_s)) < 0) {
|
||||
LOG_PERROR("Can't truncate %s sink memory", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((memsink->mem = mmap(
|
||||
if ((sink->mem = mmap(
|
||||
NULL,
|
||||
sizeof(memsink_shared_s),
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED,
|
||||
memsink->fd,
|
||||
sink->fd,
|
||||
0
|
||||
)) == MAP_FAILED) {
|
||||
LOG_PERROR("Can't mmap %s sink memory", name);
|
||||
@ -88,67 +88,67 @@ memsink_s *memsink_init(const char *name, const char *prefix, bool server, mode_
|
||||
|
||||
# undef OPEN_SIGNAL
|
||||
|
||||
return memsink;
|
||||
return sink;
|
||||
|
||||
error:
|
||||
memsink_destroy(memsink);
|
||||
memsink_destroy(sink);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void memsink_destroy(memsink_s *memsink) {
|
||||
if (memsink->sig_sem != SEM_FAILED) {
|
||||
if (sem_close(memsink->sig_sem) < 0) {
|
||||
LOG_PERROR("Can't close %s sink signal semaphore", memsink->name);
|
||||
void memsink_destroy(memsink_s *sink) {
|
||||
if (sink->sig_sem != SEM_FAILED) {
|
||||
if (sem_close(sink->sig_sem) < 0) {
|
||||
LOG_PERROR("Can't close %s sink signal semaphore", sink->name);
|
||||
}
|
||||
if (memsink->rm && sem_unlink(memsink->sig_name) < 0) {
|
||||
if (sink->rm && sem_unlink(sink->sig_name) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_PERROR("Can't remove %s sink signal semaphore", memsink->name);
|
||||
LOG_PERROR("Can't remove %s sink signal semaphore", sink->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memsink->mem != MAP_FAILED) {
|
||||
if (munmap(memsink->mem, sizeof(memsink_shared_s)) < 0) {
|
||||
LOG_PERROR("Can't unmap %s sink memory", memsink->name);
|
||||
if (sink->mem != MAP_FAILED) {
|
||||
if (munmap(sink->mem, sizeof(memsink_shared_s)) < 0) {
|
||||
LOG_PERROR("Can't unmap %s sink memory", sink->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (memsink->fd >= 0) {
|
||||
if (close(memsink->fd) < 0) {
|
||||
LOG_PERROR("Can't close %s sink fd", memsink->name);
|
||||
if (sink->fd >= 0) {
|
||||
if (close(sink->fd) < 0) {
|
||||
LOG_PERROR("Can't close %s sink fd", sink->name);
|
||||
}
|
||||
if (memsink->rm && shm_unlink(memsink->mem_name) < 0) {
|
||||
if (sink->rm && shm_unlink(sink->mem_name) < 0) {
|
||||
if (errno != ENOENT) {
|
||||
LOG_PERROR("Can't remove %s sink memory", memsink->name);
|
||||
LOG_PERROR("Can't remove %s sink memory", sink->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(memsink->sig_name);
|
||||
free(memsink->mem_name);
|
||||
free(memsink);
|
||||
free(sink->sig_name);
|
||||
free(sink->mem_name);
|
||||
free(sink);
|
||||
}
|
||||
|
||||
int memsink_server_put(memsink_s *memsink, const frame_s *frame) {
|
||||
assert(memsink->server);
|
||||
int memsink_server_put(memsink_s *sink, const frame_s *frame) {
|
||||
assert(sink->server);
|
||||
|
||||
const long double now = get_now_monotonic();
|
||||
|
||||
if (frame->used > MEMSINK_MAX_DATA) {
|
||||
LOG_ERROR("%s sink: Can't put frame: is too big (%zu > %zu)",
|
||||
memsink->name, frame->used, MEMSINK_MAX_DATA);
|
||||
sink->name, frame->used, MEMSINK_MAX_DATA);
|
||||
return 0; // -2
|
||||
}
|
||||
|
||||
if (_flock_timedwait_monotonic(memsink->fd, 1) == 0) {
|
||||
LOG_PERF("%s sink: >>>>> Exposing new frame ...", memsink->name);
|
||||
if (_flock_timedwait_monotonic(sink->fd, 1) == 0) {
|
||||
LOG_PERF("%s sink: >>>>> Exposing new frame ...", sink->name);
|
||||
|
||||
if (sem_trywait(memsink->sig_sem) < 0 && errno != EAGAIN) {
|
||||
LOG_PERROR("%s sink: Can't wait signal semaphore", memsink->name);
|
||||
if (sem_trywait(sink->sig_sem) < 0 && errno != EAGAIN) {
|
||||
LOG_PERROR("%s sink: Can't wait signal semaphore", sink->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
# define COPY(_field) memsink->mem->_field = frame->_field
|
||||
# define COPY(_field) sink->mem->_field = frame->_field
|
||||
COPY(used);
|
||||
COPY(width);
|
||||
COPY(height);
|
||||
@ -157,49 +157,49 @@ int memsink_server_put(memsink_s *memsink, const frame_s *frame) {
|
||||
COPY(grab_ts);
|
||||
COPY(encode_begin_ts);
|
||||
COPY(encode_end_ts);
|
||||
memcpy(memsink->mem->data, frame->data, frame->used);
|
||||
memcpy(sink->mem->data, frame->data, frame->used);
|
||||
# undef COPY
|
||||
|
||||
if (sem_post(memsink->sig_sem) < 0) {
|
||||
LOG_PERROR("%s sink: Can't post signal semaphore", memsink->name);
|
||||
if (sem_post(sink->sig_sem) < 0) {
|
||||
LOG_PERROR("%s sink: Can't post signal semaphore", sink->name);
|
||||
return -1;
|
||||
}
|
||||
if (flock(memsink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s sink: Can't unlock memory", memsink->name);
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s sink: Can't unlock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
LOG_VERBOSE("%s sink: Exposed new frame; full exposition time = %Lf",
|
||||
memsink->name, get_now_monotonic() - now);
|
||||
sink->name, get_now_monotonic() - now);
|
||||
|
||||
} else if (errno == EWOULDBLOCK) {
|
||||
LOG_PERF("%s sink: ===== Shared memory is busy now; frame skipped", memsink->name);
|
||||
LOG_PERF("%s sink: ===== Shared memory is busy now; frame skipped", sink->name);
|
||||
|
||||
} else {
|
||||
LOG_PERROR("%s sink: Can't lock memory", memsink->name);
|
||||
LOG_PERROR("%s sink: Can't lock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int memsink_client_get(memsink_s *memsink, frame_s *frame) { // cppcheck-suppress unusedFunction
|
||||
assert(!memsink->server); // Client only
|
||||
int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress unusedFunction
|
||||
assert(!sink->server); // Client only
|
||||
|
||||
if (_sem_timedwait_monotonic(memsink->sig_sem, memsink->timeout) < 0) {
|
||||
if (_sem_timedwait_monotonic(sink->sig_sem, sink->timeout) < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
return -2;
|
||||
}
|
||||
LOG_PERROR("%s src: Can't wait signal semaphore", memsink->name);
|
||||
LOG_PERROR("%s sink: Can't wait signal semaphore", sink->name);
|
||||
return -1;
|
||||
}
|
||||
if (_flock_timedwait_monotonic(memsink->fd, memsink->timeout) < 0) {
|
||||
if (_flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
|
||||
if (errno == EWOULDBLOCK) {
|
||||
return -2;
|
||||
}
|
||||
LOG_PERROR("%s src: Can't lock memory", memsink->name);
|
||||
LOG_PERROR("%s sink: Can't lock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
# define COPY(_field) frame->_field = memsink->mem->_field
|
||||
# define COPY(_field) frame->_field = sink->mem->_field
|
||||
COPY(width);
|
||||
COPY(height);
|
||||
COPY(format);
|
||||
@ -207,11 +207,11 @@ int memsink_client_get(memsink_s *memsink, frame_s *frame) { // cppcheck-suppres
|
||||
COPY(grab_ts);
|
||||
COPY(encode_begin_ts);
|
||||
COPY(encode_end_ts);
|
||||
frame_set_data(frame, memsink->mem->data, memsink->mem->used);
|
||||
frame_set_data(frame, sink->mem->data, sink->mem->used);
|
||||
# undef COPY
|
||||
|
||||
if (flock(memsink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s src: Can't unlock memory", memsink->name);
|
||||
if (flock(sink->fd, LOCK_UN) < 0) {
|
||||
LOG_PERROR("%s sink: Can't unlock memory", sink->name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@ -74,7 +74,7 @@ typedef struct {
|
||||
|
||||
|
||||
memsink_s *memsink_init(const char *name, const char *prefix, bool server, mode_t mode, bool rm, unsigned timeout);
|
||||
void memsink_destroy(memsink_s *memsink);
|
||||
void memsink_destroy(memsink_s *sink);
|
||||
|
||||
int memsink_server_put(memsink_s *memsink, const frame_s *frame);
|
||||
int memsink_client_get(memsink_s *memsink, frame_s *frame);
|
||||
int memsink_server_put(memsink_s *sink, const frame_s *frame);
|
||||
int memsink_client_get(memsink_s *sink, frame_s *frame);
|
||||
|
||||
@ -1,35 +1,26 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <bcm_host.h>
|
||||
|
||||
#include "../libs/common/logging.h"
|
||||
#include "../libs/common/frame.h"
|
||||
#include "../libs/memsink/memsink.h"
|
||||
#include "../libs/h264/encoder.h"
|
||||
|
||||
int main(void) {
|
||||
LOGGING_INIT;
|
||||
log_level = 3;
|
||||
|
||||
bcm_host_init();
|
||||
|
||||
frame_s *src = frame_init("src");
|
||||
frame_s *dest = frame_init("dest");
|
||||
h264_encoder_s *encoder = h264_encoder_init();
|
||||
memsink_s *memsink = memsink_init("RAW", "test", false, 0, 0, (long double)encoder->fps / (long double)encoder->gop);
|
||||
assert(memsink);
|
||||
frame_s *frame = frame_init("h264");
|
||||
memsink_s *sink = memsink_init("h264", "test", false, 0, 0, 0.1);
|
||||
assert(sink);
|
||||
FILE *fp = fopen("test.h264", "wb");
|
||||
assert(fp);
|
||||
|
||||
int error = 0;
|
||||
while ((error = memsink_client_get(memsink, src)) != -1) {
|
||||
if (error == 0 /*|| (error == -2 && src->used > 0)*/) {
|
||||
if (!h264_encoder_compress(encoder, src, dest)) {
|
||||
LOG_INFO("frame %Lf", get_now_monotonic() - dest->grab_ts);
|
||||
fwrite(dest->data, 1, dest->used, fp);
|
||||
fflush(fp);
|
||||
}
|
||||
while ((error = memsink_client_get(sink, frame)) != -1) {
|
||||
if (error == 0 /*|| (error == -2 && frame->used > 0)*/) {
|
||||
LOG_INFO("frame %Lf", get_now_monotonic() - frame->grab_ts);
|
||||
fwrite(frame->data, 1, frame->used, fp);
|
||||
fflush(fp);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
@ -48,10 +48,6 @@
|
||||
|
||||
#include "xioctl.h"
|
||||
|
||||
#ifdef WITH_MEMSINK
|
||||
# include "../libs/memsink/memsink.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define VIDEO_MIN_WIDTH ((unsigned)160)
|
||||
#define VIDEO_MAX_WIDTH ((unsigned)10240)
|
||||
|
||||
@ -32,9 +32,7 @@ static const struct {
|
||||
# ifdef WITH_OMX
|
||||
{"OMX", ENCODER_TYPE_OMX},
|
||||
# endif
|
||||
# ifdef WITH_MEMSINK
|
||||
{"NOOP", ENCODER_TYPE_NOOP},
|
||||
# endif
|
||||
};
|
||||
|
||||
|
||||
@ -151,12 +149,10 @@ void encoder_prepare(encoder_s *encoder, device_s *dev) {
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# ifdef WITH_MEMSINK
|
||||
else if (type == ENCODER_TYPE_NOOP) {
|
||||
ER(n_workers) = 1;
|
||||
quality = 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
goto ok;
|
||||
|
||||
@ -172,12 +168,9 @@ void encoder_prepare(encoder_s *encoder, device_s *dev) {
|
||||
quality = dev->jpeg_quality;
|
||||
|
||||
ok:
|
||||
# ifdef WITH_MEMSINK
|
||||
if (type == ENCODER_TYPE_NOOP) {
|
||||
LOG_INFO("Using JPEG NOOP encoder");
|
||||
} else
|
||||
# endif
|
||||
if (quality == 0) {
|
||||
} else if (quality == 0) {
|
||||
LOG_INFO("Using JPEG quality: encoder default");
|
||||
} else {
|
||||
LOG_INFO("Using JPEG quality: %u%%", quality);
|
||||
@ -227,12 +220,10 @@ int encoder_compress(encoder_s *encoder, unsigned worker_number, frame_s *src, f
|
||||
}
|
||||
}
|
||||
# endif
|
||||
# ifdef WITH_MEMSINK
|
||||
else if (ER(type) == ENCODER_TYPE_NOOP) {
|
||||
LOG_VERBOSE("Compressing buffer using NOOP (do nothing)");
|
||||
usleep(5000); // Просто чтобы работала логика desired_fps
|
||||
}
|
||||
# endif
|
||||
|
||||
dest->encode_end_ts = get_now_monotonic();
|
||||
return 0;
|
||||
|
||||
@ -51,17 +51,11 @@
|
||||
# define ENCODER_TYPES_OMX_HINT ""
|
||||
#endif
|
||||
|
||||
#ifdef WITH_MEMSINK
|
||||
# define ENCODER_TYPES_NOOP_HINT ", NOOP"
|
||||
#else
|
||||
# define ENCODER_TYPES_NOOP_HINT ""
|
||||
#endif
|
||||
|
||||
|
||||
#define ENCODER_TYPES_STR \
|
||||
"CPU, HW" \
|
||||
ENCODER_TYPES_OMX_HINT \
|
||||
ENCODER_TYPES_NOOP_HINT
|
||||
", NOOP"
|
||||
|
||||
typedef enum {
|
||||
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
|
||||
@ -70,9 +64,7 @@ typedef enum {
|
||||
# ifdef WITH_OMX
|
||||
ENCODER_TYPE_OMX,
|
||||
# endif
|
||||
# ifdef WITH_MEMSINK
|
||||
ENCODER_TYPE_NOOP,
|
||||
# endif
|
||||
} encoder_type_e;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -59,11 +59,11 @@ omx_encoder_s *omx_encoder_init(void) {
|
||||
// - https://bitbucket.org/bensch128/omxjpegencode/src/master/jpeg_encoder.cpp
|
||||
// - http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/image_encode.html
|
||||
|
||||
LOG_INFO("Initializing OMX encoder ...");
|
||||
|
||||
omx_encoder_s *omx;
|
||||
A_CALLOC(omx, 1);
|
||||
|
||||
LOG_INFO("Initializing OMX encoder ...");
|
||||
|
||||
if (vcos_semaphore_create(&omx->handler_sem, "handler_sem", 0) != VCOS_SUCCESS) {
|
||||
LOG_ERROR("Can't create VCOS semaphore");
|
||||
goto error;
|
||||
@ -278,11 +278,11 @@ static int _omx_init_disable_ports(omx_encoder_s *omx) {
|
||||
}
|
||||
|
||||
static int _omx_setup_input(omx_encoder_s *omx, device_s *dev) {
|
||||
LOG_DEBUG("Setting up OMX JPEG input port ...");
|
||||
|
||||
OMX_ERRORTYPE error;
|
||||
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
||||
|
||||
LOG_DEBUG("Setting up OMX JPEG input port ...");
|
||||
|
||||
if (omx_component_get_portdef(&omx->encoder, &portdef, _INPUT_PORT) < 0) {
|
||||
LOG_ERROR("... first");
|
||||
return -1;
|
||||
@ -333,11 +333,11 @@ static int _omx_setup_input(omx_encoder_s *omx, device_s *dev) {
|
||||
}
|
||||
|
||||
static int _omx_setup_output(omx_encoder_s *omx, unsigned quality) {
|
||||
LOG_DEBUG("Setting up OMX JPEG output port ...");
|
||||
|
||||
OMX_ERRORTYPE error;
|
||||
OMX_PARAM_PORTDEFINITIONTYPE portdef;
|
||||
|
||||
LOG_DEBUG("Setting up OMX JPEG output port ...");
|
||||
|
||||
if (omx_component_get_portdef(&omx->encoder, &portdef, _OUTPUT_PORT) < 0) {
|
||||
LOG_ERROR("... first");
|
||||
return -1;
|
||||
|
||||
@ -40,27 +40,29 @@ static const char *_mmal_error_to_string(MMAL_STATUS_T error);
|
||||
|
||||
|
||||
h264_encoder_s *h264_encoder_init(void) {
|
||||
LOG_INFO("H264: Initializing MMAL encoder ...");
|
||||
|
||||
h264_encoder_runtime_s *run;
|
||||
A_CALLOC(run, 1);
|
||||
run->unjpegged = frame_init("h264_unjpegged_src");
|
||||
run->tmp = frame_init("h264_tmp");
|
||||
run->last_online = -1;
|
||||
|
||||
h264_encoder_s *encoder;
|
||||
A_CALLOC(encoder, 1);
|
||||
encoder->gop = 60;
|
||||
encoder->gop = 45; // 60
|
||||
encoder->bps = 5000 * 1000; // Kbps * 1000
|
||||
encoder->fps = 30;
|
||||
encoder->fps = 0; // FIXME: 30 or 0? https://github.com/6by9/yavta/blob/master/yavta.c#L210
|
||||
encoder->run = run;
|
||||
|
||||
if (vcos_semaphore_create(&run->handler_sem, "h264_handler_sem", 0) != VCOS_SUCCESS) {
|
||||
LOG_PERROR("Can't create VCOS semaphore");
|
||||
LOG_PERROR("H264: Can't create VCOS semaphore");
|
||||
goto error;
|
||||
}
|
||||
run->i_handler_sem = true;
|
||||
|
||||
MMAL_STATUS_T error = mmal_wrapper_create(&run->wrapper, MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER);
|
||||
if (error != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't create MMAL wrapper");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't create MMAL wrapper");
|
||||
run->wrapper = NULL;
|
||||
goto error;
|
||||
}
|
||||
@ -75,12 +77,14 @@ h264_encoder_s *h264_encoder_init(void) {
|
||||
}
|
||||
|
||||
void h264_encoder_destroy(h264_encoder_s *encoder) {
|
||||
LOG_INFO("H264: Destroying MMAL encoder ...");
|
||||
|
||||
_h264_encoder_cleanup(encoder);
|
||||
|
||||
if (RUN(wrapper)) {
|
||||
MMAL_STATUS_T error = mmal_wrapper_destroy(RUN(wrapper));
|
||||
if (error != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't destroy MMAL encoder");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't destroy MMAL encoder");
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,7 +92,7 @@ void h264_encoder_destroy(h264_encoder_s *encoder) {
|
||||
vcos_semaphore_delete(&RUN(handler_sem));
|
||||
}
|
||||
|
||||
frame_destroy(RUN(unjpegged));
|
||||
frame_destroy(RUN(tmp));
|
||||
free(encoder->run);
|
||||
free(encoder);
|
||||
}
|
||||
@ -99,13 +103,22 @@ int h264_encoder_compress(h264_encoder_s *encoder, const frame_s *src, frame_s *
|
||||
assert(src->height > 0);
|
||||
assert(src->format > 0);
|
||||
|
||||
frame_copy_meta(src, dest);
|
||||
dest->encode_begin_ts = get_now_monotonic();
|
||||
dest->format = V4L2_PIX_FMT_H264;
|
||||
|
||||
if (src->format == V4L2_PIX_FMT_MJPEG || src->format == V4L2_PIX_FMT_JPEG) {
|
||||
LOG_DEBUG("Input frame format is JPEG; decoding ...");
|
||||
if (unjpeg(src, RUN(unjpegged), true) < 0) {
|
||||
LOG_DEBUG("H264: Input frame format is JPEG; decoding ...");
|
||||
if (unjpeg(src, RUN(tmp), true) < 0) {
|
||||
return -1;
|
||||
}
|
||||
src = RUN(unjpegged);
|
||||
LOG_VERBOSE("H264: JPEG decoded; time=%Lf", get_now_monotonic() - dest->encode_begin_ts);
|
||||
} else {
|
||||
LOG_DEBUG("H264: Copying source to tmp buffer ...");
|
||||
frame_copy(src, RUN(tmp));
|
||||
LOG_VERBOSE("H264: Source copied; time=%Lf", get_now_monotonic() - dest->encode_begin_ts);
|
||||
}
|
||||
src = RUN(tmp);
|
||||
|
||||
if (RUN(i_width) != src->width || RUN(i_height) != src->height || RUN(i_format) != src->format) {
|
||||
if (_h264_encoder_configure(encoder, src) < 0) {
|
||||
@ -114,23 +127,31 @@ int h264_encoder_compress(h264_encoder_s *encoder, const frame_s *src, frame_s *
|
||||
RUN(last_online) = -1;
|
||||
}
|
||||
|
||||
if (_h264_encoder_compress_raw(encoder, src, dest, (RUN(last_online) != src->online)) < 0) {
|
||||
bool force_key = (RUN(last_online) != src->online);
|
||||
|
||||
if (_h264_encoder_compress_raw(encoder, src, dest, force_key) < 0) {
|
||||
_h264_encoder_cleanup(encoder);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dest->encode_end_ts = get_now_monotonic();
|
||||
LOG_VERBOSE("H264: Compressed new frame: size=%zu, time=%0.3Lf, force_key=%d",
|
||||
dest->used, dest->encode_end_ts - dest->encode_begin_ts, force_key);
|
||||
|
||||
RUN(last_online) = src->online;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _h264_encoder_configure(h264_encoder_s *encoder, const frame_s *frame) {
|
||||
LOG_INFO("H264: Reconfiguring MMAL encoder ...");
|
||||
|
||||
MMAL_STATUS_T error;
|
||||
|
||||
# define PREPARE_PORT(_id) { \
|
||||
RUN(_id##_port) = RUN(wrapper->_id[0]); \
|
||||
if (RUN(_id##_port->is_enabled)) { \
|
||||
if ((error = mmal_wrapper_port_disable(RUN(_id##_port))) != MMAL_SUCCESS) { \
|
||||
LOG_ERROR_MMAL(error, "Can't disable MMAL %s port while configuring", #_id); \
|
||||
LOG_ERROR_MMAL(error, "H264: Can't disable MMAL %s port while configuring", #_id); \
|
||||
goto error; \
|
||||
} \
|
||||
} \
|
||||
@ -138,21 +159,21 @@ static int _h264_encoder_configure(h264_encoder_s *encoder, const frame_s *frame
|
||||
|
||||
# define COMMIT_PORT(_id) { \
|
||||
if ((error = mmal_port_format_commit(RUN(_id##_port))) != MMAL_SUCCESS) { \
|
||||
LOG_ERROR_MMAL(error, "Can't commit MMAL %s port", #_id); \
|
||||
LOG_ERROR_MMAL(error, "H264: Can't commit MMAL %s port", #_id); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
# define SET_PORT_PARAM(_id, _type, _key, _value) { \
|
||||
if ((error = mmal_port_parameter_set_##_type(RUN(_id##_port), MMAL_PARAMETER_##_key, _value)) != MMAL_SUCCESS) { \
|
||||
LOG_ERROR_MMAL(error, "Can't set %s for the %s port", #_key, #_id); \
|
||||
LOG_ERROR_MMAL(error, "H264: Can't set %s for the %s port", #_key, #_id); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
|
||||
# define ENABLE_PORT(_id) { \
|
||||
if ((error = mmal_wrapper_port_enable(RUN(_id##_port), MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE)) != MMAL_SUCCESS) { \
|
||||
LOG_ERROR_MMAL(error, "Can't enable MMAL %s port", #_id); \
|
||||
LOG_ERROR_MMAL(error, "H264: Can't enable MMAL %s port", #_id); \
|
||||
goto error; \
|
||||
} \
|
||||
}
|
||||
@ -211,7 +232,7 @@ static int _h264_encoder_configure(h264_encoder_s *encoder, const frame_s *frame
|
||||
profile.profile[0].profile = MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE;
|
||||
profile.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // Supports 1080p
|
||||
if ((error = mmal_port_parameter_set(RUN(output_port), &profile.hdr)) != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't set MMAL_PARAMETER_PROFILE for the output port");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't set MMAL_PARAMETER_PROFILE for the output port");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
@ -256,7 +277,7 @@ static void _h264_encoder_cleanup(h264_encoder_s *encoder) {
|
||||
# define DISABLE_PORT(_id) { \
|
||||
if (RUN(_id##_port)) { \
|
||||
if ((error = mmal_wrapper_port_disable(RUN(_id##_port))) != MMAL_SUCCESS) { \
|
||||
LOG_ERROR_MMAL(error, "Can't disable MMAL %s port", #_id); \
|
||||
LOG_ERROR_MMAL(error, "H264: Can't disable MMAL %s port", #_id); \
|
||||
} \
|
||||
RUN(_id##_port) = NULL; \
|
||||
} \
|
||||
@ -278,22 +299,17 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
|
||||
assert(src->height == RUN(i_height));
|
||||
assert(src->format == RUN(i_format));
|
||||
|
||||
LOG_DEBUG("H264: Compressing new frame; force_key=%d ...", force_key);
|
||||
|
||||
MMAL_STATUS_T error;
|
||||
|
||||
LOG_DEBUG("Compressing new H264 frame; force_key=%d ...", force_key);
|
||||
|
||||
frame_copy_meta(src, dest);
|
||||
dest->format = V4L2_PIX_FMT_H264;
|
||||
dest->encode_begin_ts = get_now_monotonic();
|
||||
dest->used = 0;
|
||||
|
||||
if (force_key) {
|
||||
if ((error = mmal_port_parameter_set_boolean(
|
||||
RUN(output_port),
|
||||
MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME,
|
||||
MMAL_TRUE
|
||||
)) != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't request keyframe");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't request keyframe");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -303,11 +319,13 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
|
||||
bool eos = false;
|
||||
bool sent = false;
|
||||
|
||||
dest->used = 0;
|
||||
|
||||
while (!eos) {
|
||||
out = NULL;
|
||||
while (mmal_wrapper_buffer_get_empty(RUN(output_port), &out, 0) == MMAL_SUCCESS) {
|
||||
if ((error = mmal_port_send_buffer(RUN(output_port), out)) != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't send MMAL output buffer");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't send MMAL output buffer");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -319,7 +337,7 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
|
||||
in->offset = 0;
|
||||
in->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
|
||||
if ((error = mmal_port_send_buffer(RUN(input_port), in)) != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't send MMAL input buffer");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't send MMAL input buffer");
|
||||
return -1;
|
||||
}
|
||||
sent = true;
|
||||
@ -330,7 +348,7 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
|
||||
vcos_semaphore_wait(&RUN(handler_sem));
|
||||
continue;
|
||||
} else if (error != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't get MMAL output buffer");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't get MMAL output buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -341,12 +359,8 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
|
||||
}
|
||||
|
||||
if ((error = mmal_port_flush(RUN(output_port))) != MMAL_SUCCESS) {
|
||||
LOG_ERROR_MMAL(error, "Can't flush MMAL output buffer; ignored");
|
||||
LOG_ERROR_MMAL(error, "H264: Can't flush MMAL output buffer; ignored");
|
||||
}
|
||||
|
||||
dest->encode_end_ts = get_now_monotonic();
|
||||
LOG_VERBOSE("Compressed new H264 frame: size=%zu, time=%0.3Lf, force_key=%d",
|
||||
dest->used, dest->encode_end_ts - dest->encode_begin_ts, force_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -34,10 +34,10 @@
|
||||
#include <interface/mmal/util/mmal_component_wrapper.h>
|
||||
#include <interface/mmal/util/mmal_util_params.h>
|
||||
|
||||
#include "../common/tools.h"
|
||||
#include "../common/logging.h"
|
||||
#include "../common/frame.h"
|
||||
#include "../common/unjpeg.h"
|
||||
#include "../../libs/common/tools.h"
|
||||
#include "../../libs/common/logging.h"
|
||||
#include "../../libs/common/frame.h"
|
||||
#include "../../libs/common/unjpeg.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
@ -47,7 +47,7 @@ typedef struct {
|
||||
VCOS_SEMAPHORE_T handler_sem;
|
||||
bool i_handler_sem;
|
||||
|
||||
frame_s *unjpegged;
|
||||
frame_s *tmp;
|
||||
int last_online;
|
||||
|
||||
unsigned i_width;
|
||||
@ -82,11 +82,11 @@ enum _OPT_VALUES {
|
||||
_O_TCP_NODELAY,
|
||||
_O_SERVER_TIMEOUT,
|
||||
|
||||
#ifdef WITH_MEMSINK
|
||||
_O_RAW_SINK,
|
||||
_O_RAW_SINK_MODE,
|
||||
_O_RAW_SINK_RM,
|
||||
_O_RAW_SINK_TIMEOUT,
|
||||
#ifdef WITH_OMX
|
||||
_O_H264_SINK,
|
||||
_O_H264_SINK_MODE,
|
||||
_O_H264_SINK_RM,
|
||||
_O_H264_SINK_TIMEOUT,
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GPIO
|
||||
@ -167,11 +167,11 @@ static const struct option _LONG_OPTS[] = {
|
||||
{"tcp-nodelay", no_argument, NULL, _O_TCP_NODELAY},
|
||||
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
||||
|
||||
#ifdef WITH_MEMSINK
|
||||
{"raw-sink", required_argument, NULL, _O_RAW_SINK},
|
||||
{"raw-sink-mode", required_argument, NULL, _O_RAW_SINK_MODE},
|
||||
{"raw-sink-rm", no_argument, NULL, _O_RAW_SINK_RM},
|
||||
{"raw-sink-timeout", required_argument, NULL, _O_RAW_SINK_TIMEOUT},
|
||||
#ifdef WITH_OMX
|
||||
{"h264-sink", required_argument, NULL, _O_H264_SINK},
|
||||
{"h264-sink-mode", required_argument, NULL, _O_H264_SINK_MODE},
|
||||
{"h264-sink-rm", no_argument, NULL, _O_H264_SINK_RM},
|
||||
{"h264-sink-timeout", required_argument, NULL, _O_H264_SINK_TIMEOUT},
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GPIO
|
||||
@ -228,9 +228,9 @@ options_s *options_init(unsigned argc, char *argv[]) {
|
||||
}
|
||||
|
||||
void options_destroy(options_s *options) {
|
||||
# ifdef WITH_MEMSINK
|
||||
if (options->raw_sink) {
|
||||
memsink_destroy(options->raw_sink);
|
||||
# ifdef WITH_OMX
|
||||
if (options->h264_sink) {
|
||||
memsink_destroy(options->h264_sink);
|
||||
}
|
||||
# endif
|
||||
if (options->blank) {
|
||||
@ -313,11 +313,11 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
|
||||
char *blank_path = NULL;
|
||||
|
||||
# ifdef WITH_MEMSINK
|
||||
char *raw_sink_name = NULL;
|
||||
mode_t raw_sink_mode = 0660;
|
||||
bool raw_sink_rm = false;
|
||||
unsigned raw_sink_timeout = 1;
|
||||
# ifdef WITH_OMX
|
||||
char *h264_sink_name = NULL;
|
||||
mode_t h264_sink_mode = 0660;
|
||||
bool h264_sink_rm = false;
|
||||
unsigned h264_sink_timeout = 1;
|
||||
# endif
|
||||
|
||||
# ifdef WITH_SETPROCTITLE
|
||||
@ -410,11 +410,11 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
case _O_TCP_NODELAY: OPT_SET(server->tcp_nodelay, true);
|
||||
case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
|
||||
|
||||
# ifdef WITH_MEMSINK
|
||||
case _O_RAW_SINK: OPT_SET(raw_sink_name, optarg);
|
||||
case _O_RAW_SINK_MODE: OPT_NUMBER("--raw-sink-mode", raw_sink_mode, INT_MIN, INT_MAX, 8);
|
||||
case _O_RAW_SINK_RM: OPT_SET(raw_sink_rm, true);
|
||||
case _O_RAW_SINK_TIMEOUT: OPT_NUMBER("--raw-sink-timeout", raw_sink_timeout, 1, 60, 0);
|
||||
# ifdef WITH_OMX
|
||||
case _O_H264_SINK: OPT_SET(h264_sink_name, optarg);
|
||||
case _O_H264_SINK_MODE: OPT_NUMBER("--h264-sink-mode", h264_sink_mode, INT_MIN, INT_MAX, 8);
|
||||
case _O_H264_SINK_RM: OPT_SET(h264_sink_rm, true);
|
||||
case _O_H264_SINK_TIMEOUT: OPT_NUMBER("--h264-sink-timeout", h264_sink_timeout, 1, 60, 0);
|
||||
# endif
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
@ -456,18 +456,18 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
|
||||
options->blank = blank_frame_init(blank_path);
|
||||
stream->blank = options->blank;
|
||||
|
||||
# ifdef WITH_MEMSINK
|
||||
if (raw_sink_name && raw_sink_name[0] != '\0') {
|
||||
options->raw_sink = memsink_init(
|
||||
"RAW",
|
||||
raw_sink_name,
|
||||
# ifdef WITH_OMX
|
||||
if (h264_sink_name && h264_sink_name[0] != '\0') {
|
||||
options->h264_sink = memsink_init(
|
||||
"h264",
|
||||
h264_sink_name,
|
||||
true,
|
||||
raw_sink_mode,
|
||||
raw_sink_rm,
|
||||
raw_sink_timeout
|
||||
h264_sink_mode,
|
||||
h264_sink_rm,
|
||||
h264_sink_timeout
|
||||
);
|
||||
}
|
||||
stream->raw_sink = options->raw_sink;
|
||||
stream->h264_sink = options->h264_sink;
|
||||
# endif
|
||||
|
||||
# ifdef WITH_SETPROCTITLE
|
||||
@ -561,12 +561,6 @@ static void _features(void) {
|
||||
puts("- WITH_OMX");
|
||||
# endif
|
||||
|
||||
# ifdef WITH_MEMSINK
|
||||
puts("+ WITH_MEMSINK");
|
||||
# else
|
||||
puts("- WITH_MEMSINK");
|
||||
# endif
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
puts("+ WITH_GPIO");
|
||||
# else
|
||||
@ -633,9 +627,7 @@ static void _help(device_s *dev, encoder_s *encoder, stream_s *stream, server_s
|
||||
printf(" * OMX ── GPU hardware accelerated MJPG encoding with OpenMax;\n");
|
||||
# endif
|
||||
printf(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware.\n");
|
||||
# ifdef WITH_MEMSINK
|
||||
printf(" * NOOP ─ Don't compress stream. Useful for the RAW sink.\n\n");
|
||||
# endif
|
||||
printf(" * NOOP ─ Don't compress MJPG stream (do nothing).\n\n");
|
||||
# ifdef WITH_OMX
|
||||
printf(" -g|--glitched-resolutions <WxH,...> ─ Comma-separated list of resolutions that require forced\n");
|
||||
printf(" encoding on CPU instead of OMX. Default: disabled.\n\n");
|
||||
@ -687,14 +679,14 @@ static void _help(device_s *dev, encoder_s *encoder, stream_s *stream, server_s
|
||||
printf(" Default: disabled.\n\n");
|
||||
printf(" --allow-origin <str> ─────── Set Access-Control-Allow-Origin header. Default: disabled.\n\n");
|
||||
printf(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n\n", server->timeout);
|
||||
#ifdef WITH_MEMSINK
|
||||
printf("RAW sink options:\n");
|
||||
#ifdef WITH_OMX
|
||||
printf("H264 sink options:\n");
|
||||
printf("═════════════════\n");
|
||||
printf(" --raw-sink <name> ──────── Use the shared memory to sink RAW frames before encoding.\n");
|
||||
printf(" Most likely you will never need it. Default: disabled.\n\n");
|
||||
printf(" --raw-sink-mode <mode> ─── Set RAW sink permissions (like 777). Default: 660.\n\n");
|
||||
printf(" --raw-sink-rm ──────────── Remove shared memory on stop. Default: disabled.\n\n");
|
||||
printf(" --raw-sink-timeout <sec> ─ Timeout for lock. Default: 1.\n\n");
|
||||
printf(" --h264-sink <name> ──────── Use the shared memory to sink H264 frames encoded by MMAL.\n");
|
||||
printf(" Most likely you will never need it. Default: disabled.\n\n");
|
||||
printf(" --h264-sink-mode <mode> ─── Set H264 sink permissions (like 777). Default: 660.\n\n");
|
||||
printf(" --h264-sink-rm ──────────── Remove shared memory on stop. Default: disabled.\n\n");
|
||||
printf(" --h264-sink-timeout <sec> ─ Timeout for lock. Default: 1.\n\n");
|
||||
#endif
|
||||
#ifdef WITH_GPIO
|
||||
printf("GPIO options:\n");
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
#include "../libs/common/logging.h"
|
||||
#include "../libs/common/process.h"
|
||||
#include "../libs/common/frame.h"
|
||||
#ifdef WITH_MEMSINK
|
||||
#ifdef WITH_OMX
|
||||
# include "../libs/memsink/memsink.h"
|
||||
#endif
|
||||
|
||||
@ -56,8 +56,8 @@ typedef struct {
|
||||
char **argv;
|
||||
char **argv_copy;
|
||||
frame_s *blank;
|
||||
# ifdef WITH_MEMSINK
|
||||
memsink_s *raw_sink;
|
||||
# ifdef WITH_OMX
|
||||
memsink_s *h264_sink;
|
||||
# endif
|
||||
} options_s;
|
||||
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
|
||||
|
||||
typedef struct {
|
||||
device_s *dev;
|
||||
encoder_s *encoder;
|
||||
hw_buffer_s *hw;
|
||||
char *dest_role;
|
||||
@ -36,10 +35,16 @@ static workers_pool_s *_stream_init_loop(stream_s *stream);
|
||||
static workers_pool_s *_stream_init_one(stream_s *stream);
|
||||
static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps);
|
||||
|
||||
static void *_worker_job_init(worker_s *wr, void *arg);
|
||||
static void *_worker_job_init(worker_s *wr, void *v_arg);
|
||||
static void _worker_job_destroy(void *v_job);
|
||||
static bool _worker_run_job(worker_s *wr);
|
||||
|
||||
#ifdef WITH_OMX
|
||||
static h264_stream_s *_h264_stream_init(memsink_s *sink);
|
||||
static void _h264_stream_destroy(h264_stream_s *h264);
|
||||
static void _h264_stream_process(h264_stream_s *h264, const frame_s *frame);
|
||||
#endif
|
||||
|
||||
|
||||
stream_s *stream_init(device_s *dev, encoder_s *encoder) {
|
||||
process_s *proc;
|
||||
@ -76,6 +81,12 @@ void stream_loop(stream_s *stream) {
|
||||
LOG_INFO("Using V4L2 device: %s", stream->dev->path);
|
||||
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps);
|
||||
|
||||
# ifdef WITH_OMX
|
||||
if (stream->h264_sink) {
|
||||
stream->h264 = _h264_stream_init(stream->h264_sink);
|
||||
}
|
||||
# endif
|
||||
|
||||
for (workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
|
||||
long double grab_after = 0;
|
||||
unsigned fluency_passed = 0;
|
||||
@ -90,16 +101,24 @@ void stream_loop(stream_s *stream) {
|
||||
LOG_DEBUG("Waiting for worker ...");
|
||||
|
||||
worker_s *ready_wr = workers_pool_wait(pool);
|
||||
_job_s *ready_job = (_job_s *)(ready_wr->job);
|
||||
|
||||
if (!ready_wr->job_failed) {
|
||||
if (ready_wr->job_timely) {
|
||||
_stream_expose_frame(stream, ((_job_s *)(ready_wr->job))->dest, captured_fps);
|
||||
LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name);
|
||||
} else {
|
||||
LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
|
||||
if (ready_job->hw) {
|
||||
if (device_release_buffer(stream->dev, ready_job->hw) < 0) {
|
||||
ready_wr->job_failed = true;
|
||||
}
|
||||
ready_job->hw = NULL;
|
||||
|
||||
if (!ready_wr->job_failed) {
|
||||
if (ready_wr->job_timely) {
|
||||
_stream_expose_frame(stream, ready_job->dest, captured_fps);
|
||||
LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name);
|
||||
} else {
|
||||
LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (atomic_load(&stream->proc->stop)) {
|
||||
@ -161,18 +180,15 @@ void stream_loop(stream_s *stream) {
|
||||
grab_after = now + fluency_delay;
|
||||
LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after);
|
||||
|
||||
# ifdef WITH_MEMSINK
|
||||
if (stream->raw_sink) {
|
||||
if (memsink_server_put(stream->raw_sink, &hw->raw) < 0) {
|
||||
stream->raw_sink = NULL;
|
||||
LOG_ERROR("RAW sink completely disabled due error");
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
((_job_s *)ready_wr->job)->hw = hw;
|
||||
ready_job->hw = hw;
|
||||
workers_pool_assign(pool, ready_wr);
|
||||
LOG_DEBUG("Assigned new frame in buffer %d to worker %s", buf_index, ready_wr->name);
|
||||
|
||||
# ifdef WITH_OMX
|
||||
if (stream->h264) {
|
||||
_h264_stream_process(stream->h264, &hw->raw);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
} else if (buf_index != -2) { // -2 for broken frame
|
||||
break;
|
||||
@ -201,6 +217,12 @@ void stream_loop(stream_s *stream) {
|
||||
gpio_set_stream_online(false);
|
||||
# endif
|
||||
}
|
||||
|
||||
# ifdef WITH_OMX
|
||||
if (stream->h264) {
|
||||
_h264_stream_destroy(stream->h264);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
void stream_loop_break(stream_s *stream) {
|
||||
@ -212,6 +234,7 @@ void stream_switch_slowdown(stream_s *stream, bool slowdown) {
|
||||
}
|
||||
|
||||
static workers_pool_s *_stream_init_loop(stream_s *stream) {
|
||||
|
||||
workers_pool_s *pool = NULL;
|
||||
int access_error = 0;
|
||||
|
||||
@ -219,12 +242,9 @@ static workers_pool_s *_stream_init_loop(stream_s *stream) {
|
||||
|
||||
while (!atomic_load(&stream->proc->stop)) {
|
||||
if (_stream_expose_frame(stream, NULL, 0)) {
|
||||
# ifdef WITH_MEMSINK
|
||||
if (stream->raw_sink) {
|
||||
if (memsink_server_put(stream->raw_sink, stream->blank) < 0) {
|
||||
stream->raw_sink = NULL;
|
||||
LOG_ERROR("RAW sink completely disabled due error");
|
||||
}
|
||||
# ifdef WITH_OMX
|
||||
if (stream->h264) {
|
||||
_h264_stream_process(stream->h264, stream->blank);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
@ -340,12 +360,11 @@ static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
|
||||
# undef VID
|
||||
}
|
||||
|
||||
static void *_worker_job_init(worker_s *wr, void *arg) {
|
||||
static void *_worker_job_init(worker_s *wr, void *v_stream) {
|
||||
stream_s *stream = (stream_s *)v_stream;
|
||||
|
||||
_job_s *job;
|
||||
A_CALLOC(job, 1);
|
||||
|
||||
stream_s *stream = (stream_s *)arg;
|
||||
job->dev = stream->dev;
|
||||
job->encoder = stream->encoder;
|
||||
|
||||
const size_t dest_role_len = strlen(wr->name) + 16;
|
||||
@ -367,19 +386,50 @@ static bool _worker_run_job(worker_s *wr) {
|
||||
|
||||
LOG_DEBUG("Worker %s compressing JPEG from buffer %u ...", wr->name, job->hw->buf_info.index);
|
||||
bool ok = !encoder_compress(job->encoder, wr->number, &job->hw->raw, job->dest);
|
||||
|
||||
if (device_release_buffer(job->dev, job->hw) == 0) {
|
||||
if (ok) {
|
||||
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
|
||||
job->dest->used,
|
||||
job->dest->encode_end_ts - job->dest->encode_begin_ts,
|
||||
wr->name,
|
||||
job->hw->buf_info.index);
|
||||
} else {
|
||||
LOG_VERBOSE("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf_info.index);
|
||||
}
|
||||
if (ok) {
|
||||
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
|
||||
job->dest->used,
|
||||
job->dest->encode_end_ts - job->dest->encode_begin_ts,
|
||||
wr->name,
|
||||
job->hw->buf_info.index);
|
||||
} else {
|
||||
ok = false;
|
||||
LOG_VERBOSE("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf_info.index);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
#ifdef WITH_OMX
|
||||
static h264_stream_s *_h264_stream_init(memsink_s *sink) {
|
||||
h264_stream_s *h264;
|
||||
A_CALLOC(h264, 1);
|
||||
|
||||
if ((h264->encoder = h264_encoder_init()) == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
h264->dest = frame_init("h264_dest");
|
||||
h264->sink = sink;
|
||||
|
||||
return h264;
|
||||
|
||||
error:
|
||||
_h264_stream_destroy(h264);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _h264_stream_destroy(h264_stream_s *h264) {
|
||||
if (h264->encoder) {
|
||||
h264_encoder_destroy(h264->encoder);
|
||||
}
|
||||
if (h264->dest) {
|
||||
frame_destroy(h264->dest);
|
||||
}
|
||||
free(h264);
|
||||
}
|
||||
|
||||
static void _h264_stream_process(h264_stream_s *h264, const frame_s *frame) {
|
||||
if (h264_encoder_compress(h264->encoder, frame, h264->dest) == 0) {
|
||||
memsink_server_put(h264->sink, h264->dest);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -41,7 +41,8 @@
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#include "workers.h"
|
||||
#ifdef WITH_MEMSINK
|
||||
#ifdef WITH_OMX
|
||||
# include "h264/encoder.h"
|
||||
# include "../libs/memsink/memsink.h"
|
||||
#endif
|
||||
#ifdef WITH_GPIO
|
||||
@ -62,6 +63,14 @@ typedef struct {
|
||||
pthread_mutex_t mutex;
|
||||
} video_s;
|
||||
|
||||
#ifdef WITH_OMX
|
||||
typedef struct {
|
||||
h264_encoder_s *encoder;
|
||||
frame_s *dest;
|
||||
memsink_s *sink;
|
||||
} h264_stream_s;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
int last_as_blank;
|
||||
unsigned error_delay;
|
||||
@ -69,12 +78,15 @@ typedef struct {
|
||||
device_s *dev;
|
||||
encoder_s *encoder;
|
||||
frame_s *blank;
|
||||
# ifdef WITH_MEMSINK
|
||||
memsink_s *raw_sink;
|
||||
# ifdef WITH_OMX
|
||||
memsink_s *h264_sink;
|
||||
# endif
|
||||
|
||||
process_s *proc;
|
||||
video_s *video;
|
||||
process_s *proc;
|
||||
video_s *video;
|
||||
# ifdef WITH_OMX
|
||||
h264_stream_s *h264;
|
||||
# endif
|
||||
} stream_s;
|
||||
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ workers_pool_s *workers_pool_init(
|
||||
void (*job_destroy)(void *),
|
||||
bool (*run_job)(worker_s *)) {
|
||||
|
||||
LOG_INFO("Creating pool %s with %u workers ...", name, n_workers);
|
||||
LOG_INFO("Creating pool '%s' with %u workers ...", name, n_workers);
|
||||
|
||||
workers_pool_s *pool;
|
||||
A_CALLOC(pool, 1);
|
||||
@ -74,7 +74,7 @@ workers_pool_s *workers_pool_init(
|
||||
}
|
||||
|
||||
void workers_pool_destroy(workers_pool_s *pool) {
|
||||
LOG_INFO("Destroying pool %s ...", pool->name);
|
||||
LOG_INFO("Destroying workers pool '%s' ...", pool->name);
|
||||
|
||||
atomic_store(&pool->stop, true);
|
||||
for (unsigned number = 0; number < pool->n_workers; ++number) {
|
||||
|
||||
18
ustreamer.1
18
ustreamer.1
@ -94,7 +94,7 @@ OMX ─ GPU hardware accelerated MJPG encoding with OpenMax (required \fBWITH_OM
|
||||
|
||||
HW ─ Use pre-encoded MJPG frames directly from camera hardware.
|
||||
|
||||
NOOP ─ Don't compress stream. Useful for the RAW sink.
|
||||
NOOP ─ Don't compress MJPG stream (do nothing).
|
||||
.TP
|
||||
.BR \-g\ \fIWxH,... ", " \-\-glitched\-resolutions\ \fIWxH,...
|
||||
Comma-separated list of resolutions that require forced
|
||||
@ -196,20 +196,20 @@ Set Access\-Control\-Allow\-Origin header. Default: disabled.
|
||||
.BR \-\-server\-timeout\ \fIsec
|
||||
Timeout for client connections. Default: 10.
|
||||
|
||||
.SS "RAW sink options"
|
||||
Available only if \fBWITH_MEMSINK\fR feature enabled.
|
||||
.SS "H264 sink options"
|
||||
Available only if \fBWITH_OMX\fR feature enabled.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\ \fIname
|
||||
Use the specified shared memory object to sink RAW frames before encoding.
|
||||
.BR \-\-h264\-sink\ \fIname
|
||||
Use the specified shared memory object to sink H264 frames encoded by MMAL.
|
||||
Most likely you will never need it. Default: disabled.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-mode\ \fImode
|
||||
Set RAW sink permissions (like 777). Default: 660.
|
||||
.BR \-\-h264\-sink\-mode\ \fImode
|
||||
Set H264 sink permissions (like 777). Default: 660.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-rm
|
||||
.BR \-\-h264\-sink\-rm
|
||||
Remove shared memory on stop. Default: disabled.
|
||||
.TP
|
||||
.BR \-\-raw\-sink\-timeout\ \fIsec
|
||||
.BR \-\-h264\-sink\-timeout\ \fIsec
|
||||
Timeout for lock. Default: 1.
|
||||
|
||||
.SS "Process options"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user