mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-18 02:55:46 +00:00
omx prototype
This commit is contained in:
parent
4d8881bacc
commit
c2b82312ec
9
Makefile
9
Makefile
@ -13,6 +13,13 @@ PROG = ustreamer
|
||||
all: $(SOURCES) $(PROG)
|
||||
|
||||
|
||||
rpi:
|
||||
make all \
|
||||
SOURCES="$(SOURCES) $(shell ls src/omx/*.c)" \
|
||||
LIBS="$(LIBS) -lbcm_host -lvcos -lopenmaxil -L/opt/vc/lib" \
|
||||
CFLAGS="$(CFLAGS) -DOMX_ENCODER -DOMX_SKIP64BIT -I/opt/vc/include"
|
||||
|
||||
|
||||
install: $(PROG)
|
||||
install -Dm755 $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG)
|
||||
|
||||
@ -30,4 +37,4 @@ $(PROG): $(OBJECTS)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f src/*.o vgcore.* $(PROG)
|
||||
rm -f src/*.o src/{jpeg,omx}/*.o vgcore.* $(PROG)
|
||||
|
||||
@ -138,6 +138,8 @@ int device_open(struct device_t *dev) {
|
||||
}
|
||||
_device_open_alloc_picbufs(dev);
|
||||
|
||||
dev->run->n_workers = dev->n_workers;
|
||||
|
||||
LOG_DEBUG("Device fd=%d initialized", dev->run->fd);
|
||||
return 0;
|
||||
|
||||
|
||||
@ -49,6 +49,7 @@ struct device_runtime_t {
|
||||
unsigned height;
|
||||
unsigned format;
|
||||
unsigned n_buffers;
|
||||
unsigned n_workers;
|
||||
struct hw_buffer_t *hw_buffers;
|
||||
struct picture_t *pictures;
|
||||
unsigned long max_picture_size;
|
||||
|
||||
105
src/encoder.c
Normal file
105
src/encoder.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*****************************************************************************
|
||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||
# #
|
||||
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#include "tools.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
|
||||
#include "jpeg/encoder.h"
|
||||
|
||||
#ifdef OMX_ENCODER
|
||||
# include "omx/encoder.h"
|
||||
#endif
|
||||
|
||||
struct encoder_t *encoder_init(enum encoder_type_t type) {
|
||||
struct encoder_t *encoder;
|
||||
|
||||
A_CALLOC(encoder, 1);
|
||||
encoder->type = type;
|
||||
|
||||
# ifdef OMX_ENCODER
|
||||
if (type == ENCODER_TYPE_OMX) {
|
||||
if ((encoder->omx = omx_encoder_init()) == NULL) {
|
||||
goto use_fallback;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
return encoder;
|
||||
|
||||
# pragma GCC diagnostic ignored "-Wunused-label"
|
||||
# pragma GCC diagnostic push
|
||||
use_fallback:
|
||||
LOG_ERROR("Can't initialize selected encoder, using CPU instead it");
|
||||
encoder->type = ENCODER_TYPE_CPU;
|
||||
return encoder;
|
||||
# pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
void encoder_destroy(struct encoder_t *encoder) {
|
||||
# ifdef OMX_ENCODER
|
||||
if (encoder->omx) {
|
||||
omx_encoder_destroy(encoder->omx);
|
||||
}
|
||||
# endif
|
||||
free(encoder);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#pragma GCC diagnostic push
|
||||
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev) {
|
||||
#pragma GCC diagnostic pop
|
||||
# ifdef OMX_ENCODER
|
||||
if (encoder->type == ENCODER_TYPE_OMX) {
|
||||
if (omx_encoder_prepare(encoder->omx, dev) < 0) {
|
||||
goto use_fallback;
|
||||
}
|
||||
}
|
||||
if (dev->run->n_workers > 1) {
|
||||
LOG_INFO("OMX encoder can only work with one worker thread; forcing n_workers to 1");
|
||||
dev->run->n_workers = 1;
|
||||
}
|
||||
# endif
|
||||
|
||||
return;
|
||||
|
||||
# pragma GCC diagnostic ignored "-Wunused-label"
|
||||
# pragma GCC diagnostic push
|
||||
use_fallback:
|
||||
LOG_ERROR("Can't prepare selected encoder, falling back to CPU");
|
||||
encoder->type = ENCODER_TYPE_CPU;
|
||||
# pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
void encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, int index) {
|
||||
# ifdef OMX_ENCODER
|
||||
if (encoder->type == ENCODER_TYPE_OMX) {
|
||||
if (omx_encoder_compress_buffer(encoder->omx, dev, index) < 0) {
|
||||
LOG_INFO("OMX compressor error, falling back to CPU method");
|
||||
encoder->type = ENCODER_TYPE_CPU;
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
if (encoder->type == ENCODER_TYPE_CPU) {
|
||||
jpeg_encoder_compress_buffer(dev, index);
|
||||
}
|
||||
}
|
||||
53
src/encoder.h
Normal file
53
src/encoder.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*****************************************************************************
|
||||
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
|
||||
# #
|
||||
# Copyright (C) 2018 Maxim Devaev <mdevaev@gmail.com> #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tools.h"
|
||||
#include "device.h"
|
||||
|
||||
#ifdef OMX_ENCODER
|
||||
# include "omx/encoder.h"
|
||||
#endif
|
||||
|
||||
|
||||
enum encoder_type_t {
|
||||
ENCODER_TYPE_CPU,
|
||||
|
||||
#ifdef OMX_ENCODER
|
||||
ENCODER_TYPE_OMX,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct encoder_t {
|
||||
enum encoder_type_t type;
|
||||
|
||||
#ifdef OMX_ENCODER
|
||||
struct omx_encoder_t *omx;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct encoder_t *encoder_init(enum encoder_type_t type);
|
||||
void encoder_destroy(struct encoder_t *encoder);
|
||||
|
||||
void encoder_prepare(struct encoder_t *encoder, struct device_t *dev);
|
||||
void encoder_compress_buffer(struct encoder_t *encoder, struct device_t *dev, int index);
|
||||
@ -68,7 +68,7 @@ static boolean _jpeg_empty_output_buffer(j_compress_ptr jpeg);
|
||||
static void _jpeg_term_destination(j_compress_ptr jpeg);
|
||||
|
||||
|
||||
int jpeg_compress_buffer(struct device_t *dev, int index) {
|
||||
void jpeg_encoder_compress_buffer(struct device_t *dev, int index) {
|
||||
// This function based on compress_image_to_jpeg() from mjpg-streamer
|
||||
|
||||
struct jpeg_compress_struct jpeg;
|
||||
@ -110,7 +110,6 @@ int jpeg_compress_buffer(struct device_t *dev, int index) {
|
||||
jpeg_destroy_compress(&jpeg);
|
||||
free(line_buffer);
|
||||
assert(dev->run->pictures[index].size > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _jpeg_set_dest_picture(j_compress_ptr jpeg, unsigned char *picture, unsigned long *written) {
|
||||
|
||||
@ -27,4 +27,4 @@
|
||||
#include "../device.h"
|
||||
|
||||
|
||||
int jpeg_compress_buffer(struct device_t *dev, int index);
|
||||
void jpeg_encoder_compress_buffer(struct device_t *dev, int index);
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
#include "tools.h"
|
||||
#include "logging.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#include "stream.h"
|
||||
#include "http.h"
|
||||
|
||||
@ -218,6 +219,7 @@ static void _install_signal_handlers() {
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct device_t *dev;
|
||||
struct encoder_t *encoder;
|
||||
struct stream_t *stream;
|
||||
struct http_server_t *server;
|
||||
int exit_code = 0;
|
||||
@ -225,7 +227,8 @@ int main(int argc, char *argv[]) {
|
||||
LOGGING_INIT;
|
||||
|
||||
dev = device_init();
|
||||
stream = stream_init(dev);
|
||||
encoder = encoder_init(ENCODER_TYPE_CPU);
|
||||
stream = stream_init(dev, encoder);
|
||||
server = http_server_init(stream);
|
||||
|
||||
if ((exit_code = _parse_options(argc, argv, dev, server)) == 0) {
|
||||
@ -249,6 +252,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
http_server_destroy(server);
|
||||
stream_destroy(stream);
|
||||
encoder_destroy(encoder);
|
||||
device_destroy(dev);
|
||||
|
||||
LOGGING_DESTROY;
|
||||
|
||||
@ -52,7 +52,7 @@ int component_get_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYP
|
||||
int component_set_portdef(OMX_HANDLETYPE *component, OMX_PARAM_PORTDEFINITIONTYPE *portdef) {
|
||||
OMX_ERRORTYPE error;
|
||||
|
||||
LOG_DEBUG("Writing OMX port %u definition ...", port);
|
||||
LOG_DEBUG("Writing OMX port %u definition ...", portdef->nPortIndex);
|
||||
if ((error = OMX_SetParameter(*component, OMX_IndexParamPortDefinition, portdef)) != OMX_ErrorNone) {
|
||||
LOG_OMX_ERROR(error, "Can't set OMX port %u definition", portdef->nPortIndex);
|
||||
return -1;
|
||||
|
||||
@ -113,7 +113,7 @@ void omx_encoder_destroy(struct omx_encoder_t *omx) {
|
||||
free(omx);
|
||||
}
|
||||
|
||||
int omx_encoder_set_device(struct omx_encoder_t *omx, struct device_t *dev) {
|
||||
int omx_encoder_prepare(struct omx_encoder_t *omx, struct device_t *dev) {
|
||||
if (component_set_state(&omx->encoder, OMX_StateIdle) < 0) {
|
||||
return -1;
|
||||
}
|
||||
@ -132,13 +132,13 @@ int omx_encoder_set_device(struct omx_encoder_t *omx, struct device_t *dev) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, int index) {
|
||||
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, int index) {
|
||||
OMX_ERRORTYPE error;
|
||||
bool loaded = false;
|
||||
|
||||
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
|
||||
LOG_OMX_ERROR(error, "Failed to request filling of the output buffer on encoder");
|
||||
assert(0); // TODO
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->run->pictures[index].size = 0;
|
||||
@ -146,7 +146,9 @@ void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev
|
||||
omx->input_required = true;
|
||||
|
||||
while (true) {
|
||||
assert(!omx->failed); // FIXME
|
||||
if (omx->failed) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (omx->output_available) {
|
||||
omx->output_available = false;
|
||||
@ -165,7 +167,7 @@ void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev
|
||||
|
||||
if ((error = OMX_FillThisBuffer(omx->encoder, omx->output_buffer)) != OMX_ErrorNone) {
|
||||
LOG_OMX_ERROR(error, "Failed to request filling of the output buffer on encoder");
|
||||
assert(0); // TODO
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -182,12 +184,13 @@ void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev
|
||||
|
||||
if ((error = OMX_EmptyThisBuffer(omx->encoder, omx->input_buffer)) != OMX_ErrorNone) {
|
||||
LOG_OMX_ERROR(error, "Failed to request emptying of the input buffer on encoder");
|
||||
assert(0); // TODO
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
vcos_semaphore_wait(&omx->handler_lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _omx_init_component(struct omx_encoder_t *omx) {
|
||||
|
||||
@ -28,5 +28,5 @@ struct omx_encoder_t {
|
||||
struct omx_encoder_t *omx_encoder_init();
|
||||
void omx_encoder_destroy(struct omx_encoder_t *omx);
|
||||
|
||||
int omx_encoder_set_device(struct omx_encoder_t *omx, struct device_t *dev);
|
||||
void omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, int index);
|
||||
int omx_encoder_prepare(struct omx_encoder_t *omx, struct device_t *dev);
|
||||
int omx_encoder_compress_buffer(struct omx_encoder_t *omx, struct device_t *dev, int index);
|
||||
|
||||
26
src/stream.c
26
src/stream.c
@ -32,6 +32,7 @@
|
||||
#include "tools.h"
|
||||
#include "logging.h"
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
#include "stream.h"
|
||||
|
||||
#include "jpeg/encoder.h"
|
||||
@ -53,11 +54,12 @@ static int _stream_release_buffer(struct device_t *dev, struct v4l2_buffer *buf_
|
||||
static int _stream_handle_event(struct device_t *dev);
|
||||
|
||||
|
||||
struct stream_t *stream_init(struct device_t *dev) {
|
||||
struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder) {
|
||||
struct stream_t *stream;
|
||||
|
||||
A_CALLOC(stream, 1);
|
||||
stream->dev = dev;
|
||||
stream->encoder = encoder;
|
||||
A_PTHREAD_M_INIT(&stream->mutex);
|
||||
return stream;
|
||||
}
|
||||
@ -72,6 +74,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
bool workers_stop;
|
||||
|
||||
MEMSET_ZERO(pool);
|
||||
pool.encoder = stream->encoder;
|
||||
pool.workers_stop = &workers_stop;
|
||||
|
||||
LOG_INFO("Using V4L2 device: %s", stream->dev->path);
|
||||
@ -111,7 +114,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
|
||||
LOG_PERF("##### ACCEPT : %u", free_worker_number);
|
||||
} else {
|
||||
for (unsigned number = 0; number < stream->dev->n_workers; ++number) {
|
||||
for (unsigned number = 0; number < stream->dev->run->n_workers; ++number) {
|
||||
if (!pool.workers[number].has_job && (free_worker_number == -1
|
||||
|| pool.workers[free_worker_number].job_start_time < pool.workers[number].job_start_time
|
||||
)) {
|
||||
@ -301,7 +304,7 @@ static void _stream_expose_picture(struct stream_t *stream, unsigned buf_index)
|
||||
static long double _stream_get_fluency_delay(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
long double delay = 0;
|
||||
|
||||
for (unsigned number = 0; number < dev->n_workers; ++number) {
|
||||
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
|
||||
A_PTHREAD_M_LOCK(&pool->workers[number].last_comp_time_mutex);
|
||||
if (pool->workers[number].last_comp_time > 0) {
|
||||
delay += pool->workers[number].last_comp_time;
|
||||
@ -309,7 +312,7 @@ static long double _stream_get_fluency_delay(struct device_t *dev, struct worker
|
||||
A_PTHREAD_M_UNLOCK(&pool->workers[number].last_comp_time_mutex);
|
||||
}
|
||||
// Среднее арифметическое деленное на количество воркеров
|
||||
return delay / dev->n_workers / dev->n_workers;
|
||||
return delay / dev->run->n_workers / dev->run->n_workers;
|
||||
}
|
||||
|
||||
static int _stream_init_loop(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
@ -340,6 +343,9 @@ static int _stream_init(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
if (_stream_control(dev, true) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
encoder_prepare(pool->encoder, dev);
|
||||
|
||||
_stream_init_workers(dev, pool);
|
||||
|
||||
return 0;
|
||||
@ -350,15 +356,15 @@ static int _stream_init(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
}
|
||||
|
||||
static void _stream_init_workers(struct device_t *dev, struct workers_pool_t *pool) {
|
||||
LOG_INFO("Spawning %d workers ...", dev->n_workers);
|
||||
LOG_INFO("Spawning %d workers ...", dev->run->n_workers);
|
||||
|
||||
*pool->workers_stop = false;
|
||||
A_CALLOC(pool->workers, dev->n_workers);
|
||||
A_CALLOC(pool->workers, dev->run->n_workers);
|
||||
|
||||
A_PTHREAD_M_INIT(&pool->free_workers_mutex);
|
||||
A_PTHREAD_C_INIT(&pool->free_workers_cond);
|
||||
|
||||
for (unsigned number = 0; number < dev->n_workers; ++number) {
|
||||
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
|
||||
pool->free_workers += 1;
|
||||
|
||||
A_PTHREAD_M_INIT(&pool->workers[number].has_job_mutex);
|
||||
@ -369,6 +375,8 @@ static void _stream_init_workers(struct device_t *dev, struct workers_pool_t *po
|
||||
pool->workers[number].ctx.dev_stop = (sig_atomic_t *volatile)&dev->stop;
|
||||
pool->workers[number].ctx.workers_stop = pool->workers_stop;
|
||||
|
||||
pool->workers[number].ctx.encoder = pool->encoder;
|
||||
|
||||
pool->workers[number].ctx.last_comp_time_mutex = &pool->workers[number].last_comp_time_mutex;
|
||||
pool->workers[number].ctx.last_comp_time = &pool->workers[number].last_comp_time;
|
||||
|
||||
@ -405,7 +413,7 @@ static void *_stream_worker_thread(void *v_ctx) {
|
||||
|
||||
LOG_DEBUG("Worker %u compressing JPEG from buffer %d ...", ctx->number, ctx->buf_index);
|
||||
|
||||
jpeg_compress_buffer(ctx->dev, ctx->buf_index);
|
||||
encoder_compress_buffer(ctx->encoder, ctx->dev, ctx->buf_index);
|
||||
|
||||
if (_stream_release_buffer(ctx->dev, &ctx->buf_info) == 0) {
|
||||
*ctx->job_start_time = start_time;
|
||||
@ -442,7 +450,7 @@ static void _stream_destroy_workers(struct device_t *dev, struct workers_pool_t
|
||||
LOG_INFO("Destroying workers ...");
|
||||
|
||||
*pool->workers_stop = true;
|
||||
for (unsigned number = 0; number < dev->n_workers; ++number) {
|
||||
for (unsigned number = 0; number < dev->run->n_workers; ++number) {
|
||||
A_PTHREAD_M_LOCK(&pool->workers[number].has_job_mutex);
|
||||
pool->workers[number].has_job = true; // Final job: die
|
||||
A_PTHREAD_M_UNLOCK(&pool->workers[number].has_job_mutex);
|
||||
|
||||
18
src/stream.h
18
src/stream.h
@ -27,6 +27,7 @@
|
||||
#include <pthread.h>
|
||||
|
||||
#include "device.h"
|
||||
#include "encoder.h"
|
||||
|
||||
|
||||
struct worker_context_t {
|
||||
@ -37,6 +38,8 @@ struct worker_context_t {
|
||||
sig_atomic_t *volatile dev_stop;
|
||||
bool *workers_stop;
|
||||
|
||||
struct encoder_t *encoder;
|
||||
|
||||
pthread_mutex_t *last_comp_time_mutex;
|
||||
long double *last_comp_time;
|
||||
|
||||
@ -69,12 +72,14 @@ struct worker_t {
|
||||
};
|
||||
|
||||
struct workers_pool_t {
|
||||
struct worker_t *workers;
|
||||
bool *workers_stop;
|
||||
struct worker_t*workers;
|
||||
bool *workers_stop;
|
||||
|
||||
pthread_mutex_t free_workers_mutex;
|
||||
unsigned free_workers;
|
||||
pthread_cond_t free_workers_cond;
|
||||
pthread_mutex_t free_workers_mutex;
|
||||
unsigned free_workers;
|
||||
pthread_cond_t free_workers_cond;
|
||||
|
||||
struct encoder_t *encoder;
|
||||
};
|
||||
|
||||
struct stream_t {
|
||||
@ -84,10 +89,11 @@ struct stream_t {
|
||||
bool updated;
|
||||
pthread_mutex_t mutex;
|
||||
struct device_t *dev;
|
||||
struct encoder_t *encoder;
|
||||
};
|
||||
|
||||
|
||||
struct stream_t *stream_init(struct device_t *dev);
|
||||
struct stream_t *stream_init(struct device_t *dev, struct encoder_t *encoder);
|
||||
void stream_destroy(struct stream_t *stream);
|
||||
|
||||
void stream_loop(struct stream_t *stream);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user