This commit is contained in:
Devaev Maxim
2021-01-04 12:55:25 +03:00
parent 6eb5e62aae
commit 944bd89b4e
17 changed files with 322 additions and 292 deletions

View File

@@ -33,11 +33,10 @@ _USTR_SRCS = $(shell ls \
src/ustreamer/encoders/hw/*.c \ 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 \ _REC_SRCS = $(shell ls \
src/libs/common/*.c \ src/libs/common/*.c \
src/libs/memsink/*.c \ src/libs/memsink/*.c \
src/libs/h264/*.c \
src/recorder/*.c \ src/recorder/*.c \
) )
@@ -47,17 +46,14 @@ $(filter $(shell echo $(1) | tr A-Z a-z), yes on 1)
endef 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)),) 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) 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 endif
@@ -83,13 +79,6 @@ override CFLAGS += -DWITH_SETPROCTITLE
endif endif
ifneq ($(call optbool,$(WITH_MEMSINK)),)
ifneq ($(call optbool,$(WITH_OMX)),)
_ENABLE_REC = 1
endif
endif
# ===== # =====
all: $(USTR) $(REC) all: $(USTR) $(REC)
@@ -98,14 +87,14 @@ install: $(USTR) $(REC)
install -Dm755 $(USTR) $(DESTDIR)$(PREFIX)/bin/$(USTR) install -Dm755 $(USTR) $(DESTDIR)$(PREFIX)/bin/$(USTR)
install -Dm644 $(USTR).1 $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1 install -Dm644 $(USTR).1 $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
gzip $(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) # install -Dm755 $(DESTDIR)$(PREFIX)/bin/$(REC)
#endif #endif
install-strip: install install-strip: install
strip $(DESTDIR)$(PREFIX)/bin/$(USTR) strip $(DESTDIR)$(PREFIX)/bin/$(USTR)
#ifneq ($(_ENABLE_REC),) #ifneq ($(call optbool,$(WITH_OMX)),)
# strip $(DESTDIR)$(PREFIX)/bin/$(REC) # strip $(DESTDIR)$(PREFIX)/bin/$(REC)
#endif #endif
@@ -113,6 +102,9 @@ install-strip: install
uninstall: uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/$(USTR) \ rm -f $(DESTDIR)$(PREFIX)/bin/$(USTR) \
$(DESTDIR)$(MANPREFIX)/man1/$(USTR).1 $(DESTDIR)$(MANPREFIX)/man1/$(USTR).1
#ifneq ($(call optbool,$(WITH_OMX)),)
# rm -f $(DESTDIR)$(PREFIX)/bin/$(REC)
#endif
regen: regen:
@@ -121,22 +113,22 @@ regen:
$(USTR): $(_USTR_SRCS:%.c=$(BUILD)/%.o) $(USTR): $(_USTR_SRCS:%.c=$(BUILD)/%.o)
$(info -- LD $@) $(info == LD $@)
@ $(CC) $^ -o $@ $(LDFLAGS) $(_USTR_LIBS) @ $(CC) $^ -o $@ $(LDFLAGS) $(_USTR_LIBS)
$(info == CC = $(CC)) $(info :: CC = $(CC))
$(info == LIBS = $(_USTR_LIBS)) $(info :: LIBS = $(_USTR_LIBS))
$(info == CFLAGS = $(CFLAGS)) $(info :: CFLAGS = $(CFLAGS))
$(info == LDFLAGS = $(LDFLAGS)) $(info :: LDFLAGS = $(LDFLAGS))
ifneq ($(_ENABLE_REC),) ifneq ($(call optbool,$(WITH_OMX)),)
$(REC): $(_REC_SRCS:%.c=$(BUILD)/%.o) $(REC): $(_REC_SRCS:%.c=$(BUILD)/%.o)
$(info -- LD $@) $(info == LD $@)
@ $(CC) $^ -o $@ $(LDFLAGS) $(_REC_LIBS) @ $(CC) $^ -o $@ $(LDFLAGS) $(_REC_LIBS)
$(info == CC = $(CC)) $(info :: CC = $(CC))
$(info == LIBS = $(_REC_LIBS)) $(info :: LIBS = $(_REC_LIBS))
$(info == CFLAGS = $(CFLAGS)) $(info :: CFLAGS = $(CFLAGS))
$(info == LDFLAGS = $(LDFLAGS)) $(info :: LDFLAGS = $(LDFLAGS))
else else
$(REC): $(REC):
@ true @ 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 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 rm -rf $(USTR) $(REC) $(BUILD) vgcore.* *.sock
.PHONY: linters .PHONY: linters

View File

@@ -18,7 +18,6 @@ commands = cppcheck \
--suppress=variableScope \ --suppress=variableScope \
--inline-suppr \ --inline-suppr \
-DCHAR_BIT=8 \ -DCHAR_BIT=8 \
-DWITH_MEMSINK \
-DWITH_OMX \ -DWITH_OMX \
-DWITH_GPIO \ -DWITH_GPIO \
src src

View File

@@ -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_init(const char *name, const char *prefix, bool server, mode_t mode, bool rm, unsigned timeout) {
memsink_s *memsink; memsink_s *sink;
A_CALLOC(memsink, 1); A_CALLOC(sink, 1);
memsink->name = name; sink->name = name;
memsink->server = server; sink->server = server;
memsink->rm = rm; sink->rm = rm;
memsink->timeout = timeout; sink->timeout = timeout;
memsink->fd = -1; sink->fd = -1;
memsink->mem = MAP_FAILED; sink->mem = MAP_FAILED;
memsink->sig_sem = SEM_FAILED; sink->sig_sem = SEM_FAILED;
A_CALLOC(memsink->mem_name, strlen(prefix) + 8); A_CALLOC(sink->mem_name, strlen(prefix) + 8);
A_CALLOC(memsink->sig_name, strlen(prefix) + 8); A_CALLOC(sink->sig_name, strlen(prefix) + 8);
sprintf(memsink->mem_name, "%s.mem", prefix); sprintf(sink->mem_name, "%s.mem", prefix);
sprintf(memsink->sig_name, "%s.sig", 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); const int flags = (server ? O_RDWR | O_CREAT : O_RDWR);
# define OPEN_SIGNAL { \ # 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); \ LOG_PERROR("Can't open %s sink signal semaphore", name); \
goto error; \ goto error; \
} \ } \
@@ -59,22 +59,22 @@ memsink_s *memsink_init(const char *name, const char *prefix, bool server, mode_
} }
{ // Shared memory { // 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); LOG_PERROR("Can't open %s sink memory", name);
goto error; 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); LOG_PERROR("Can't truncate %s sink memory", name);
goto error; goto error;
} }
if ((memsink->mem = mmap( if ((sink->mem = mmap(
NULL, NULL,
sizeof(memsink_shared_s), sizeof(memsink_shared_s),
PROT_READ | PROT_WRITE, PROT_READ | PROT_WRITE,
MAP_SHARED, MAP_SHARED,
memsink->fd, sink->fd,
0 0
)) == MAP_FAILED) { )) == MAP_FAILED) {
LOG_PERROR("Can't mmap %s sink memory", name); 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 # undef OPEN_SIGNAL
return memsink; return sink;
error: error:
memsink_destroy(memsink); memsink_destroy(sink);
return NULL; return NULL;
} }
void memsink_destroy(memsink_s *memsink) { void memsink_destroy(memsink_s *sink) {
if (memsink->sig_sem != SEM_FAILED) { if (sink->sig_sem != SEM_FAILED) {
if (sem_close(memsink->sig_sem) < 0) { if (sem_close(sink->sig_sem) < 0) {
LOG_PERROR("Can't close %s sink signal semaphore", memsink->name); 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) { 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 (sink->mem != MAP_FAILED) {
if (munmap(memsink->mem, sizeof(memsink_shared_s)) < 0) { if (munmap(sink->mem, sizeof(memsink_shared_s)) < 0) {
LOG_PERROR("Can't unmap %s sink memory", memsink->name); LOG_PERROR("Can't unmap %s sink memory", sink->name);
} }
} }
if (memsink->fd >= 0) { if (sink->fd >= 0) {
if (close(memsink->fd) < 0) { if (close(sink->fd) < 0) {
LOG_PERROR("Can't close %s sink fd", memsink->name); 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) { 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(sink->sig_name);
free(memsink->mem_name); free(sink->mem_name);
free(memsink); free(sink);
} }
int memsink_server_put(memsink_s *memsink, const frame_s *frame) { int memsink_server_put(memsink_s *sink, const frame_s *frame) {
assert(memsink->server); assert(sink->server);
const long double now = get_now_monotonic(); const long double now = get_now_monotonic();
if (frame->used > MEMSINK_MAX_DATA) { if (frame->used > MEMSINK_MAX_DATA) {
LOG_ERROR("%s sink: Can't put frame: is too big (%zu > %zu)", 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 return 0; // -2
} }
if (_flock_timedwait_monotonic(memsink->fd, 1) == 0) { if (_flock_timedwait_monotonic(sink->fd, 1) == 0) {
LOG_PERF("%s sink: >>>>> Exposing new frame ...", memsink->name); LOG_PERF("%s sink: >>>>> Exposing new frame ...", sink->name);
if (sem_trywait(memsink->sig_sem) < 0 && errno != EAGAIN) { if (sem_trywait(sink->sig_sem) < 0 && errno != EAGAIN) {
LOG_PERROR("%s sink: Can't wait signal semaphore", memsink->name); LOG_PERROR("%s sink: Can't wait signal semaphore", sink->name);
return -1; return -1;
} }
# define COPY(_field) memsink->mem->_field = frame->_field # define COPY(_field) sink->mem->_field = frame->_field
COPY(used); COPY(used);
COPY(width); COPY(width);
COPY(height); COPY(height);
@@ -157,49 +157,49 @@ int memsink_server_put(memsink_s *memsink, const frame_s *frame) {
COPY(grab_ts); COPY(grab_ts);
COPY(encode_begin_ts); COPY(encode_begin_ts);
COPY(encode_end_ts); COPY(encode_end_ts);
memcpy(memsink->mem->data, frame->data, frame->used); memcpy(sink->mem->data, frame->data, frame->used);
# undef COPY # undef COPY
if (sem_post(memsink->sig_sem) < 0) { if (sem_post(sink->sig_sem) < 0) {
LOG_PERROR("%s sink: Can't post signal semaphore", memsink->name); LOG_PERROR("%s sink: Can't post signal semaphore", sink->name);
return -1; return -1;
} }
if (flock(memsink->fd, LOCK_UN) < 0) { if (flock(sink->fd, LOCK_UN) < 0) {
LOG_PERROR("%s sink: Can't unlock memory", memsink->name); LOG_PERROR("%s sink: Can't unlock memory", sink->name);
return -1; return -1;
} }
LOG_VERBOSE("%s sink: Exposed new frame; full exposition time = %Lf", 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) { } 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 { } else {
LOG_PERROR("%s sink: Can't lock memory", memsink->name); LOG_PERROR("%s sink: Can't lock memory", sink->name);
return -1; return -1;
} }
return 0; return 0;
} }
int memsink_client_get(memsink_s *memsink, frame_s *frame) { // cppcheck-suppress unusedFunction int memsink_client_get(memsink_s *sink, frame_s *frame) { // cppcheck-suppress unusedFunction
assert(!memsink->server); // Client only 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) { if (errno == EAGAIN) {
return -2; 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; return -1;
} }
if (_flock_timedwait_monotonic(memsink->fd, memsink->timeout) < 0) { if (_flock_timedwait_monotonic(sink->fd, sink->timeout) < 0) {
if (errno == EWOULDBLOCK) { if (errno == EWOULDBLOCK) {
return -2; return -2;
} }
LOG_PERROR("%s src: Can't lock memory", memsink->name); LOG_PERROR("%s sink: Can't lock memory", sink->name);
return -1; return -1;
} }
# define COPY(_field) frame->_field = memsink->mem->_field # define COPY(_field) frame->_field = sink->mem->_field
COPY(width); COPY(width);
COPY(height); COPY(height);
COPY(format); COPY(format);
@@ -207,11 +207,11 @@ int memsink_client_get(memsink_s *memsink, frame_s *frame) { // cppcheck-suppres
COPY(grab_ts); COPY(grab_ts);
COPY(encode_begin_ts); COPY(encode_begin_ts);
COPY(encode_end_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 # undef COPY
if (flock(memsink->fd, LOCK_UN) < 0) { if (flock(sink->fd, LOCK_UN) < 0) {
LOG_PERROR("%s src: Can't unlock memory", memsink->name); LOG_PERROR("%s sink: Can't unlock memory", sink->name);
return -1; return -1;
} }
return 0; return 0;

View File

@@ -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); 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_server_put(memsink_s *sink, const frame_s *frame);
int memsink_client_get(memsink_s *memsink, frame_s *frame); int memsink_client_get(memsink_s *sink, frame_s *frame);

View File

@@ -1,35 +1,26 @@
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <bcm_host.h>
#include "../libs/common/logging.h" #include "../libs/common/logging.h"
#include "../libs/common/frame.h" #include "../libs/common/frame.h"
#include "../libs/memsink/memsink.h" #include "../libs/memsink/memsink.h"
#include "../libs/h264/encoder.h"
int main(void) { int main(void) {
LOGGING_INIT; LOGGING_INIT;
log_level = 3; log_level = 3;
bcm_host_init(); frame_s *frame = frame_init("h264");
memsink_s *sink = memsink_init("h264", "test", false, 0, 0, 0.1);
frame_s *src = frame_init("src"); assert(sink);
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);
FILE *fp = fopen("test.h264", "wb"); FILE *fp = fopen("test.h264", "wb");
assert(fp); assert(fp);
int error = 0; int error = 0;
while ((error = memsink_client_get(memsink, src)) != -1) { while ((error = memsink_client_get(sink, frame)) != -1) {
if (error == 0 /*|| (error == -2 && src->used > 0)*/) { if (error == 0 /*|| (error == -2 && frame->used > 0)*/) {
if (!h264_encoder_compress(encoder, src, dest)) { LOG_INFO("frame %Lf", get_now_monotonic() - frame->grab_ts);
LOG_INFO("frame %Lf", get_now_monotonic() - dest->grab_ts); fwrite(frame->data, 1, frame->used, fp);
fwrite(dest->data, 1, dest->used, fp); fflush(fp);
fflush(fp);
}
} }
} }
fclose(fp); fclose(fp);

View File

@@ -48,10 +48,6 @@
#include "xioctl.h" #include "xioctl.h"
#ifdef WITH_MEMSINK
# include "../libs/memsink/memsink.h"
#endif
#define VIDEO_MIN_WIDTH ((unsigned)160) #define VIDEO_MIN_WIDTH ((unsigned)160)
#define VIDEO_MAX_WIDTH ((unsigned)10240) #define VIDEO_MAX_WIDTH ((unsigned)10240)

View File

@@ -32,9 +32,7 @@ static const struct {
# ifdef WITH_OMX # ifdef WITH_OMX
{"OMX", ENCODER_TYPE_OMX}, {"OMX", ENCODER_TYPE_OMX},
# endif # endif
# ifdef WITH_MEMSINK
{"NOOP", ENCODER_TYPE_NOOP}, {"NOOP", ENCODER_TYPE_NOOP},
# endif
}; };
@@ -151,12 +149,10 @@ void encoder_prepare(encoder_s *encoder, device_s *dev) {
} }
} }
# endif # endif
# ifdef WITH_MEMSINK
else if (type == ENCODER_TYPE_NOOP) { else if (type == ENCODER_TYPE_NOOP) {
ER(n_workers) = 1; ER(n_workers) = 1;
quality = 0; quality = 0;
} }
# endif
goto ok; goto ok;
@@ -172,12 +168,9 @@ void encoder_prepare(encoder_s *encoder, device_s *dev) {
quality = dev->jpeg_quality; quality = dev->jpeg_quality;
ok: ok:
# ifdef WITH_MEMSINK
if (type == ENCODER_TYPE_NOOP) { if (type == ENCODER_TYPE_NOOP) {
LOG_INFO("Using JPEG NOOP encoder"); LOG_INFO("Using JPEG NOOP encoder");
} else } else if (quality == 0) {
# endif
if (quality == 0) {
LOG_INFO("Using JPEG quality: encoder default"); LOG_INFO("Using JPEG quality: encoder default");
} else { } else {
LOG_INFO("Using JPEG quality: %u%%", quality); 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 # endif
# ifdef WITH_MEMSINK
else if (ER(type) == ENCODER_TYPE_NOOP) { else if (ER(type) == ENCODER_TYPE_NOOP) {
LOG_VERBOSE("Compressing buffer using NOOP (do nothing)"); LOG_VERBOSE("Compressing buffer using NOOP (do nothing)");
usleep(5000); // Просто чтобы работала логика desired_fps usleep(5000); // Просто чтобы работала логика desired_fps
} }
# endif
dest->encode_end_ts = get_now_monotonic(); dest->encode_end_ts = get_now_monotonic();
return 0; return 0;

View File

@@ -51,17 +51,11 @@
# define ENCODER_TYPES_OMX_HINT "" # define ENCODER_TYPES_OMX_HINT ""
#endif #endif
#ifdef WITH_MEMSINK
# define ENCODER_TYPES_NOOP_HINT ", NOOP"
#else
# define ENCODER_TYPES_NOOP_HINT ""
#endif
#define ENCODER_TYPES_STR \ #define ENCODER_TYPES_STR \
"CPU, HW" \ "CPU, HW" \
ENCODER_TYPES_OMX_HINT \ ENCODER_TYPES_OMX_HINT \
ENCODER_TYPES_NOOP_HINT ", NOOP"
typedef enum { typedef enum {
ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main() ENCODER_TYPE_UNKNOWN, // Only for encoder_parse_type() and main()
@@ -70,9 +64,7 @@ typedef enum {
# ifdef WITH_OMX # ifdef WITH_OMX
ENCODER_TYPE_OMX, ENCODER_TYPE_OMX,
# endif # endif
# ifdef WITH_MEMSINK
ENCODER_TYPE_NOOP, ENCODER_TYPE_NOOP,
# endif
} encoder_type_e; } encoder_type_e;
typedef struct { typedef struct {

View File

@@ -59,11 +59,11 @@ omx_encoder_s *omx_encoder_init(void) {
// - https://bitbucket.org/bensch128/omxjpegencode/src/master/jpeg_encoder.cpp // - https://bitbucket.org/bensch128/omxjpegencode/src/master/jpeg_encoder.cpp
// - http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/image_encode.html // - http://home.nouwen.name/RaspberryPi/documentation/ilcomponents/image_encode.html
LOG_INFO("Initializing OMX encoder ...");
omx_encoder_s *omx; omx_encoder_s *omx;
A_CALLOC(omx, 1); A_CALLOC(omx, 1);
LOG_INFO("Initializing OMX encoder ...");
if (vcos_semaphore_create(&omx->handler_sem, "handler_sem", 0) != VCOS_SUCCESS) { if (vcos_semaphore_create(&omx->handler_sem, "handler_sem", 0) != VCOS_SUCCESS) {
LOG_ERROR("Can't create VCOS semaphore"); LOG_ERROR("Can't create VCOS semaphore");
goto error; 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) { static int _omx_setup_input(omx_encoder_s *omx, device_s *dev) {
LOG_DEBUG("Setting up OMX JPEG input port ...");
OMX_ERRORTYPE error; OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef; OMX_PARAM_PORTDEFINITIONTYPE portdef;
LOG_DEBUG("Setting up OMX JPEG input port ...");
if (omx_component_get_portdef(&omx->encoder, &portdef, _INPUT_PORT) < 0) { if (omx_component_get_portdef(&omx->encoder, &portdef, _INPUT_PORT) < 0) {
LOG_ERROR("... first"); LOG_ERROR("... first");
return -1; 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) { static int _omx_setup_output(omx_encoder_s *omx, unsigned quality) {
LOG_DEBUG("Setting up OMX JPEG output port ...");
OMX_ERRORTYPE error; OMX_ERRORTYPE error;
OMX_PARAM_PORTDEFINITIONTYPE portdef; OMX_PARAM_PORTDEFINITIONTYPE portdef;
LOG_DEBUG("Setting up OMX JPEG output port ...");
if (omx_component_get_portdef(&omx->encoder, &portdef, _OUTPUT_PORT) < 0) { if (omx_component_get_portdef(&omx->encoder, &portdef, _OUTPUT_PORT) < 0) {
LOG_ERROR("... first"); LOG_ERROR("... first");
return -1; return -1;

View File

@@ -40,27 +40,29 @@ static const char *_mmal_error_to_string(MMAL_STATUS_T error);
h264_encoder_s *h264_encoder_init(void) { h264_encoder_s *h264_encoder_init(void) {
LOG_INFO("H264: Initializing MMAL encoder ...");
h264_encoder_runtime_s *run; h264_encoder_runtime_s *run;
A_CALLOC(run, 1); A_CALLOC(run, 1);
run->unjpegged = frame_init("h264_unjpegged_src"); run->tmp = frame_init("h264_tmp");
run->last_online = -1; run->last_online = -1;
h264_encoder_s *encoder; h264_encoder_s *encoder;
A_CALLOC(encoder, 1); A_CALLOC(encoder, 1);
encoder->gop = 60; encoder->gop = 45; // 60
encoder->bps = 5000 * 1000; // Kbps * 1000 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; encoder->run = run;
if (vcos_semaphore_create(&run->handler_sem, "h264_handler_sem", 0) != VCOS_SUCCESS) { 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; goto error;
} }
run->i_handler_sem = true; run->i_handler_sem = true;
MMAL_STATUS_T error = mmal_wrapper_create(&run->wrapper, MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER); MMAL_STATUS_T error = mmal_wrapper_create(&run->wrapper, MMAL_COMPONENT_DEFAULT_VIDEO_ENCODER);
if (error != MMAL_SUCCESS) { 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; run->wrapper = NULL;
goto error; goto error;
} }
@@ -75,12 +77,14 @@ h264_encoder_s *h264_encoder_init(void) {
} }
void h264_encoder_destroy(h264_encoder_s *encoder) { void h264_encoder_destroy(h264_encoder_s *encoder) {
LOG_INFO("H264: Destroying MMAL encoder ...");
_h264_encoder_cleanup(encoder); _h264_encoder_cleanup(encoder);
if (RUN(wrapper)) { if (RUN(wrapper)) {
MMAL_STATUS_T error = mmal_wrapper_destroy(RUN(wrapper)); MMAL_STATUS_T error = mmal_wrapper_destroy(RUN(wrapper));
if (error != MMAL_SUCCESS) { 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)); vcos_semaphore_delete(&RUN(handler_sem));
} }
frame_destroy(RUN(unjpegged)); frame_destroy(RUN(tmp));
free(encoder->run); free(encoder->run);
free(encoder); 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->height > 0);
assert(src->format > 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) { if (src->format == V4L2_PIX_FMT_MJPEG || src->format == V4L2_PIX_FMT_JPEG) {
LOG_DEBUG("Input frame format is JPEG; decoding ..."); LOG_DEBUG("H264: Input frame format is JPEG; decoding ...");
if (unjpeg(src, RUN(unjpegged), true) < 0) { if (unjpeg(src, RUN(tmp), true) < 0) {
return -1; 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 (RUN(i_width) != src->width || RUN(i_height) != src->height || RUN(i_format) != src->format) {
if (_h264_encoder_configure(encoder, src) < 0) { 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; 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); _h264_encoder_cleanup(encoder);
return -1; 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; RUN(last_online) = src->online;
return 0; return 0;
} }
static int _h264_encoder_configure(h264_encoder_s *encoder, const frame_s *frame) { static int _h264_encoder_configure(h264_encoder_s *encoder, const frame_s *frame) {
LOG_INFO("H264: Reconfiguring MMAL encoder ...");
MMAL_STATUS_T error; MMAL_STATUS_T error;
# define PREPARE_PORT(_id) { \ # define PREPARE_PORT(_id) { \
RUN(_id##_port) = RUN(wrapper->_id[0]); \ RUN(_id##_port) = RUN(wrapper->_id[0]); \
if (RUN(_id##_port->is_enabled)) { \ if (RUN(_id##_port->is_enabled)) { \
if ((error = mmal_wrapper_port_disable(RUN(_id##_port))) != MMAL_SUCCESS) { \ 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; \ goto error; \
} \ } \
} \ } \
@@ -138,21 +159,21 @@ static int _h264_encoder_configure(h264_encoder_s *encoder, const frame_s *frame
# define COMMIT_PORT(_id) { \ # define COMMIT_PORT(_id) { \
if ((error = mmal_port_format_commit(RUN(_id##_port))) != MMAL_SUCCESS) { \ 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; \ goto error; \
} \ } \
} }
# define SET_PORT_PARAM(_id, _type, _key, _value) { \ # define SET_PORT_PARAM(_id, _type, _key, _value) { \
if ((error = mmal_port_parameter_set_##_type(RUN(_id##_port), MMAL_PARAMETER_##_key, _value)) != MMAL_SUCCESS) { \ 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; \ goto error; \
} \ } \
} }
# define ENABLE_PORT(_id) { \ # define ENABLE_PORT(_id) { \
if ((error = mmal_wrapper_port_enable(RUN(_id##_port), MMAL_WRAPPER_FLAG_PAYLOAD_ALLOCATE)) != MMAL_SUCCESS) { \ 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; \ 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].profile = MMAL_VIDEO_PROFILE_H264_CONSTRAINED_BASELINE;
profile.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // Supports 1080p profile.profile[0].level = MMAL_VIDEO_LEVEL_H264_4; // Supports 1080p
if ((error = mmal_port_parameter_set(RUN(output_port), &profile.hdr)) != MMAL_SUCCESS) { 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; goto error;
} }
} }
@@ -256,7 +277,7 @@ static void _h264_encoder_cleanup(h264_encoder_s *encoder) {
# define DISABLE_PORT(_id) { \ # define DISABLE_PORT(_id) { \
if (RUN(_id##_port)) { \ if (RUN(_id##_port)) { \
if ((error = mmal_wrapper_port_disable(RUN(_id##_port))) != MMAL_SUCCESS) { \ 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; \ 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->height == RUN(i_height));
assert(src->format == RUN(i_format)); assert(src->format == RUN(i_format));
LOG_DEBUG("H264: Compressing new frame; force_key=%d ...", force_key);
MMAL_STATUS_T error; 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 (force_key) {
if ((error = mmal_port_parameter_set_boolean( if ((error = mmal_port_parameter_set_boolean(
RUN(output_port), RUN(output_port),
MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME,
MMAL_TRUE MMAL_TRUE
)) != MMAL_SUCCESS) { )) != MMAL_SUCCESS) {
LOG_ERROR_MMAL(error, "Can't request keyframe"); LOG_ERROR_MMAL(error, "H264: Can't request keyframe");
return -1; return -1;
} }
} }
@@ -303,11 +319,13 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
bool eos = false; bool eos = false;
bool sent = false; bool sent = false;
dest->used = 0;
while (!eos) { while (!eos) {
out = NULL; out = NULL;
while (mmal_wrapper_buffer_get_empty(RUN(output_port), &out, 0) == MMAL_SUCCESS) { 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) { 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; return -1;
} }
} }
@@ -319,7 +337,7 @@ static int _h264_encoder_compress_raw(h264_encoder_s *encoder, const frame_s *sr
in->offset = 0; in->offset = 0;
in->flags = MMAL_BUFFER_HEADER_FLAG_EOS; in->flags = MMAL_BUFFER_HEADER_FLAG_EOS;
if ((error = mmal_port_send_buffer(RUN(input_port), in)) != MMAL_SUCCESS) { 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; return -1;
} }
sent = true; 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)); vcos_semaphore_wait(&RUN(handler_sem));
continue; continue;
} else if (error != MMAL_SUCCESS) { } 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; 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) { 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; return 0;
} }

View File

@@ -34,10 +34,10 @@
#include <interface/mmal/util/mmal_component_wrapper.h> #include <interface/mmal/util/mmal_component_wrapper.h>
#include <interface/mmal/util/mmal_util_params.h> #include <interface/mmal/util/mmal_util_params.h>
#include "../common/tools.h" #include "../../libs/common/tools.h"
#include "../common/logging.h" #include "../../libs/common/logging.h"
#include "../common/frame.h" #include "../../libs/common/frame.h"
#include "../common/unjpeg.h" #include "../../libs/common/unjpeg.h"
typedef struct { typedef struct {
@@ -47,7 +47,7 @@ typedef struct {
VCOS_SEMAPHORE_T handler_sem; VCOS_SEMAPHORE_T handler_sem;
bool i_handler_sem; bool i_handler_sem;
frame_s *unjpegged; frame_s *tmp;
int last_online; int last_online;
unsigned i_width; unsigned i_width;

View File

@@ -82,11 +82,11 @@ enum _OPT_VALUES {
_O_TCP_NODELAY, _O_TCP_NODELAY,
_O_SERVER_TIMEOUT, _O_SERVER_TIMEOUT,
#ifdef WITH_MEMSINK #ifdef WITH_OMX
_O_RAW_SINK, _O_H264_SINK,
_O_RAW_SINK_MODE, _O_H264_SINK_MODE,
_O_RAW_SINK_RM, _O_H264_SINK_RM,
_O_RAW_SINK_TIMEOUT, _O_H264_SINK_TIMEOUT,
#endif #endif
#ifdef WITH_GPIO #ifdef WITH_GPIO
@@ -167,11 +167,11 @@ static const struct option _LONG_OPTS[] = {
{"tcp-nodelay", no_argument, NULL, _O_TCP_NODELAY}, {"tcp-nodelay", no_argument, NULL, _O_TCP_NODELAY},
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT}, {"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
#ifdef WITH_MEMSINK #ifdef WITH_OMX
{"raw-sink", required_argument, NULL, _O_RAW_SINK}, {"h264-sink", required_argument, NULL, _O_H264_SINK},
{"raw-sink-mode", required_argument, NULL, _O_RAW_SINK_MODE}, {"h264-sink-mode", required_argument, NULL, _O_H264_SINK_MODE},
{"raw-sink-rm", no_argument, NULL, _O_RAW_SINK_RM}, {"h264-sink-rm", no_argument, NULL, _O_H264_SINK_RM},
{"raw-sink-timeout", required_argument, NULL, _O_RAW_SINK_TIMEOUT}, {"h264-sink-timeout", required_argument, NULL, _O_H264_SINK_TIMEOUT},
#endif #endif
#ifdef WITH_GPIO #ifdef WITH_GPIO
@@ -228,9 +228,9 @@ options_s *options_init(unsigned argc, char *argv[]) {
} }
void options_destroy(options_s *options) { void options_destroy(options_s *options) {
# ifdef WITH_MEMSINK # ifdef WITH_OMX
if (options->raw_sink) { if (options->h264_sink) {
memsink_destroy(options->raw_sink); memsink_destroy(options->h264_sink);
} }
# endif # endif
if (options->blank) { if (options->blank) {
@@ -313,11 +313,11 @@ int options_parse(options_s *options, device_s *dev, encoder_s *encoder, stream_
char *blank_path = NULL; char *blank_path = NULL;
# ifdef WITH_MEMSINK # ifdef WITH_OMX
char *raw_sink_name = NULL; char *h264_sink_name = NULL;
mode_t raw_sink_mode = 0660; mode_t h264_sink_mode = 0660;
bool raw_sink_rm = false; bool h264_sink_rm = false;
unsigned raw_sink_timeout = 1; unsigned h264_sink_timeout = 1;
# endif # endif
# ifdef WITH_SETPROCTITLE # 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_TCP_NODELAY: OPT_SET(server->tcp_nodelay, true);
case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0); case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
# ifdef WITH_MEMSINK # ifdef WITH_OMX
case _O_RAW_SINK: OPT_SET(raw_sink_name, optarg); case _O_H264_SINK: OPT_SET(h264_sink_name, optarg);
case _O_RAW_SINK_MODE: OPT_NUMBER("--raw-sink-mode", raw_sink_mode, INT_MIN, INT_MAX, 8); case _O_H264_SINK_MODE: OPT_NUMBER("--h264-sink-mode", h264_sink_mode, INT_MIN, INT_MAX, 8);
case _O_RAW_SINK_RM: OPT_SET(raw_sink_rm, true); case _O_H264_SINK_RM: OPT_SET(h264_sink_rm, true);
case _O_RAW_SINK_TIMEOUT: OPT_NUMBER("--raw-sink-timeout", raw_sink_timeout, 1, 60, 0); case _O_H264_SINK_TIMEOUT: OPT_NUMBER("--h264-sink-timeout", h264_sink_timeout, 1, 60, 0);
# endif # endif
# ifdef WITH_GPIO # 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); options->blank = blank_frame_init(blank_path);
stream->blank = options->blank; stream->blank = options->blank;
# ifdef WITH_MEMSINK # ifdef WITH_OMX
if (raw_sink_name && raw_sink_name[0] != '\0') { if (h264_sink_name && h264_sink_name[0] != '\0') {
options->raw_sink = memsink_init( options->h264_sink = memsink_init(
"RAW", "h264",
raw_sink_name, h264_sink_name,
true, true,
raw_sink_mode, h264_sink_mode,
raw_sink_rm, h264_sink_rm,
raw_sink_timeout h264_sink_timeout
); );
} }
stream->raw_sink = options->raw_sink; stream->h264_sink = options->h264_sink;
# endif # endif
# ifdef WITH_SETPROCTITLE # ifdef WITH_SETPROCTITLE
@@ -561,12 +561,6 @@ static void _features(void) {
puts("- WITH_OMX"); puts("- WITH_OMX");
# endif # endif
# ifdef WITH_MEMSINK
puts("+ WITH_MEMSINK");
# else
puts("- WITH_MEMSINK");
# endif
# ifdef WITH_GPIO # ifdef WITH_GPIO
puts("+ WITH_GPIO"); puts("+ WITH_GPIO");
# else # 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"); printf(" * OMX ── GPU hardware accelerated MJPG encoding with OpenMax;\n");
# endif # endif
printf(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware.\n"); printf(" * HW ─── Use pre-encoded MJPG frames directly from camera hardware.\n");
# ifdef WITH_MEMSINK printf(" * NOOP ─ Don't compress MJPG stream (do nothing).\n\n");
printf(" * NOOP ─ Don't compress stream. Useful for the RAW sink.\n\n");
# endif
# ifdef WITH_OMX # ifdef WITH_OMX
printf(" -g|--glitched-resolutions <WxH,...> ─ Comma-separated list of resolutions that require forced\n"); 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"); 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(" Default: disabled.\n\n");
printf(" --allow-origin <str> ─────── Set Access-Control-Allow-Origin header. 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); printf(" --server-timeout <sec> ───── Timeout for client connections. Default: %u.\n\n", server->timeout);
#ifdef WITH_MEMSINK #ifdef WITH_OMX
printf("RAW sink options:\n"); printf("H264 sink options:\n");
printf("═════════════════\n"); printf("═════════════════\n");
printf(" --raw-sink <name> ──────── Use the shared memory to sink RAW frames before encoding.\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(" 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(" --h264-sink-mode <mode> ─── Set H264 sink permissions (like 777). Default: 660.\n\n");
printf(" --raw-sink-rm ──────────── Remove shared memory on stop. Default: disabled.\n\n"); printf(" --h264-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-timeout <sec> ─ Timeout for lock. Default: 1.\n\n");
#endif #endif
#ifdef WITH_GPIO #ifdef WITH_GPIO
printf("GPIO options:\n"); printf("GPIO options:\n");

View File

@@ -37,7 +37,7 @@
#include "../libs/common/logging.h" #include "../libs/common/logging.h"
#include "../libs/common/process.h" #include "../libs/common/process.h"
#include "../libs/common/frame.h" #include "../libs/common/frame.h"
#ifdef WITH_MEMSINK #ifdef WITH_OMX
# include "../libs/memsink/memsink.h" # include "../libs/memsink/memsink.h"
#endif #endif
@@ -56,8 +56,8 @@ typedef struct {
char **argv; char **argv;
char **argv_copy; char **argv_copy;
frame_s *blank; frame_s *blank;
# ifdef WITH_MEMSINK # ifdef WITH_OMX
memsink_s *raw_sink; memsink_s *h264_sink;
# endif # endif
} options_s; } options_s;

View File

@@ -24,7 +24,6 @@
typedef struct { typedef struct {
device_s *dev;
encoder_s *encoder; encoder_s *encoder;
hw_buffer_s *hw; hw_buffer_s *hw;
char *dest_role; 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 workers_pool_s *_stream_init_one(stream_s *stream);
static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned captured_fps); 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 void _worker_job_destroy(void *v_job);
static bool _worker_run_job(worker_s *wr); 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) { stream_s *stream_init(device_s *dev, encoder_s *encoder) {
process_s *proc; 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 V4L2 device: %s", stream->dev->path);
LOG_INFO("Using desired FPS: %u", stream->dev->desired_fps); 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;) { for (workers_pool_s *pool; (pool = _stream_init_loop(stream)) != NULL;) {
long double grab_after = 0; long double grab_after = 0;
unsigned fluency_passed = 0; unsigned fluency_passed = 0;
@@ -90,16 +101,24 @@ void stream_loop(stream_s *stream) {
LOG_DEBUG("Waiting for worker ..."); LOG_DEBUG("Waiting for worker ...");
worker_s *ready_wr = workers_pool_wait(pool); worker_s *ready_wr = workers_pool_wait(pool);
_job_s *ready_job = (_job_s *)(ready_wr->job);
if (!ready_wr->job_failed) { if (ready_job->hw) {
if (ready_wr->job_timely) { if (device_release_buffer(stream->dev, ready_job->hw) < 0) {
_stream_expose_frame(stream, ((_job_s *)(ready_wr->job))->dest, captured_fps); ready_wr->job_failed = true;
LOG_PERF("##### Encoded frame exposed; worker=%s", ready_wr->name); }
} else { ready_job->hw = NULL;
LOG_PERF("----- Encoded frame dropped; worker=%s", ready_wr->name);
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)) { if (atomic_load(&stream->proc->stop)) {
@@ -161,18 +180,15 @@ void stream_loop(stream_s *stream) {
grab_after = now + fluency_delay; grab_after = now + fluency_delay;
LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after); LOG_VERBOSE("Fluency: delay=%.03Lf, grab_after=%.03Lf", fluency_delay, grab_after);
# ifdef WITH_MEMSINK ready_job->hw = hw;
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;
workers_pool_assign(pool, ready_wr); workers_pool_assign(pool, ready_wr);
LOG_DEBUG("Assigned new frame in buffer %d to worker %s", buf_index, ready_wr->name); 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 } else if (buf_index != -2) { // -2 for broken frame
break; break;
@@ -201,6 +217,12 @@ void stream_loop(stream_s *stream) {
gpio_set_stream_online(false); gpio_set_stream_online(false);
# endif # endif
} }
# ifdef WITH_OMX
if (stream->h264) {
_h264_stream_destroy(stream->h264);
}
# endif
} }
void stream_loop_break(stream_s *stream) { 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) { static workers_pool_s *_stream_init_loop(stream_s *stream) {
workers_pool_s *pool = NULL; workers_pool_s *pool = NULL;
int access_error = 0; int access_error = 0;
@@ -219,12 +242,9 @@ static workers_pool_s *_stream_init_loop(stream_s *stream) {
while (!atomic_load(&stream->proc->stop)) { while (!atomic_load(&stream->proc->stop)) {
if (_stream_expose_frame(stream, NULL, 0)) { if (_stream_expose_frame(stream, NULL, 0)) {
# ifdef WITH_MEMSINK # ifdef WITH_OMX
if (stream->raw_sink) { if (stream->h264) {
if (memsink_server_put(stream->raw_sink, stream->blank) < 0) { _h264_stream_process(stream->h264, stream->blank);
stream->raw_sink = NULL;
LOG_ERROR("RAW sink completely disabled due error");
}
} }
# endif # endif
} }
@@ -340,12 +360,11 @@ static bool _stream_expose_frame(stream_s *stream, frame_s *frame, unsigned capt
# undef VID # 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; _job_s *job;
A_CALLOC(job, 1); A_CALLOC(job, 1);
stream_s *stream = (stream_s *)arg;
job->dev = stream->dev;
job->encoder = stream->encoder; job->encoder = stream->encoder;
const size_t dest_role_len = strlen(wr->name) + 16; 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); 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); bool ok = !encoder_compress(job->encoder, wr->number, &job->hw->raw, job->dest);
if (ok) {
if (device_release_buffer(job->dev, job->hw) == 0) { LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u",
if (ok) { job->dest->used,
LOG_VERBOSE("Compressed new JPEG: size=%zu, time=%0.3Lf, worker=%s, buffer=%u", job->dest->encode_end_ts - job->dest->encode_begin_ts,
job->dest->used, wr->name,
job->dest->encode_end_ts - job->dest->encode_begin_ts, job->hw->buf_info.index);
wr->name,
job->hw->buf_info.index);
} else {
LOG_VERBOSE("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf_info.index);
}
} else { } else {
ok = false; LOG_VERBOSE("Compression failed: worker=%s, buffer=%u", wr->name, job->hw->buf_info.index);
} }
return ok; 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

View File

@@ -41,7 +41,8 @@
#include "device.h" #include "device.h"
#include "encoder.h" #include "encoder.h"
#include "workers.h" #include "workers.h"
#ifdef WITH_MEMSINK #ifdef WITH_OMX
# include "h264/encoder.h"
# include "../libs/memsink/memsink.h" # include "../libs/memsink/memsink.h"
#endif #endif
#ifdef WITH_GPIO #ifdef WITH_GPIO
@@ -62,6 +63,14 @@ typedef struct {
pthread_mutex_t mutex; pthread_mutex_t mutex;
} video_s; } video_s;
#ifdef WITH_OMX
typedef struct {
h264_encoder_s *encoder;
frame_s *dest;
memsink_s *sink;
} h264_stream_s;
#endif
typedef struct { typedef struct {
int last_as_blank; int last_as_blank;
unsigned error_delay; unsigned error_delay;
@@ -69,12 +78,15 @@ typedef struct {
device_s *dev; device_s *dev;
encoder_s *encoder; encoder_s *encoder;
frame_s *blank; frame_s *blank;
# ifdef WITH_MEMSINK # ifdef WITH_OMX
memsink_s *raw_sink; memsink_s *h264_sink;
# endif # endif
process_s *proc; process_s *proc;
video_s *video; video_s *video;
# ifdef WITH_OMX
h264_stream_s *h264;
# endif
} stream_s; } stream_s;

View File

@@ -32,7 +32,7 @@ workers_pool_s *workers_pool_init(
void (*job_destroy)(void *), void (*job_destroy)(void *),
bool (*run_job)(worker_s *)) { 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; workers_pool_s *pool;
A_CALLOC(pool, 1); A_CALLOC(pool, 1);
@@ -74,7 +74,7 @@ workers_pool_s *workers_pool_init(
} }
void workers_pool_destroy(workers_pool_s *pool) { 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); atomic_store(&pool->stop, true);
for (unsigned number = 0; number < pool->n_workers; ++number) { for (unsigned number = 0; number < pool->n_workers; ++number) {

View File

@@ -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. 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 .TP
.BR \-g\ \fIWxH,... ", " \-\-glitched\-resolutions\ \fIWxH,... .BR \-g\ \fIWxH,... ", " \-\-glitched\-resolutions\ \fIWxH,...
Comma-separated list of resolutions that require forced Comma-separated list of resolutions that require forced
@@ -196,20 +196,20 @@ Set Access\-Control\-Allow\-Origin header. Default: disabled.
.BR \-\-server\-timeout\ \fIsec .BR \-\-server\-timeout\ \fIsec
Timeout for client connections. Default: 10. Timeout for client connections. Default: 10.
.SS "RAW sink options" .SS "H264 sink options"
Available only if \fBWITH_MEMSINK\fR feature enabled. Available only if \fBWITH_OMX\fR feature enabled.
.TP .TP
.BR \-\-raw\-sink\ \fIname .BR \-\-h264\-sink\ \fIname
Use the specified shared memory object to sink RAW frames before encoding. Use the specified shared memory object to sink H264 frames encoded by MMAL.
Most likely you will never need it. Default: disabled. Most likely you will never need it. Default: disabled.
.TP .TP
.BR \-\-raw\-sink\-mode\ \fImode .BR \-\-h264\-sink\-mode\ \fImode
Set RAW sink permissions (like 777). Default: 660. Set H264 sink permissions (like 777). Default: 660.
.TP .TP
.BR \-\-raw\-sink\-rm .BR \-\-h264\-sink\-rm
Remove shared memory on stop. Default: disabled. Remove shared memory on stop. Default: disabled.
.TP .TP
.BR \-\-raw\-sink\-timeout\ \fIsec .BR \-\-h264\-sink\-timeout\ \fIsec
Timeout for lock. Default: 1. Timeout for lock. Default: 1.
.SS "Process options" .SS "Process options"