pluggable outputs for the future

This commit is contained in:
Devaev Maxim
2021-01-15 12:38:32 +03:00
parent 66e0bb0a2c
commit de41c9653e
3 changed files with 160 additions and 47 deletions

79
src/dump/file.c Normal file
View File

@@ -0,0 +1,79 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 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 "file.h"
output_file_s *output_file_init(const char *path, bool json) {
output_file_s *output;
A_CALLOC(output, 1);
if (!strcmp(path, "-")) {
LOG_INFO("Using output: <stdout>");
output->fp = stdout;
} else {
LOG_INFO("Using output: %s", path);
if ((output->fp = fopen(path, "wb")) == NULL) {
LOG_PERROR("Can't open output file");
goto error;
}
}
output->json = json;
return output;
error:
output_file_destroy(output);
return NULL;
}
void output_file_write(void *v_output, const frame_s *frame) {
output_file_s *output = (output_file_s *)v_output;
if (output->json) {
base64_encode(frame->data, frame->used, &output->base64_data, &output->base64_allocated);
fprintf(output->fp,
"{\"size\": %zu, \"width\": %u, \"height\": %u,"
" \"format\": %u, \"stride\": %u, \"online\": %u,"
" \"grab_ts\": %.3Lf, \"encode_begin_ts\": %.3Lf, \"encode_end_ts\": %.3Lf,"
" \"data\": \"%s\"}\n",
frame->used, frame->width, frame->height,
frame->format, frame->stride, frame->online,
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts,
output->base64_data);
} else {
fwrite(frame->data, 1, frame->used, output->fp);
}
fflush(output->fp);
}
void output_file_destroy(void *v_output) {
output_file_s *output = (output_file_s *)v_output;
if (output->base64_data) {
free(output->base64_data);
}
if (output->fp && output->fp != stdout) {
if (fclose(output->fp) < 0) {
LOG_PERROR("Can't close output file");
}
}
free(output);
}

49
src/dump/file.h Normal file
View File

@@ -0,0 +1,49 @@
/*****************************************************************************
# #
# uStreamer - Lightweight and fast MJPG-HTTP streamer. #
# #
# Copyright (C) 2018-2021 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 <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include "../libs/tools.h"
#include "../libs/logging.h"
#include "../libs/frame.h"
#include "../libs/base64.h"
typedef struct {
const char *path;
bool json;
FILE *fp;
char *base64_data;
size_t base64_allocated;
} output_file_s;
output_file_s *output_file_init(const char *path, bool json);
void output_file_write(void *v_output, const frame_s *frame);
void output_file_destroy(void *v_output);

View File

@@ -33,7 +33,8 @@
#include "../libs/logging.h"
#include "../libs/frame.h"
#include "../libs/memsink.h"
#include "../libs/base64.h"
#include "file.h"
enum _OPT_VALUES {
@@ -73,13 +74,20 @@ static const struct option _LONG_OPTS[] = {
};
volatile bool stop = false;
volatile bool global_stop = false;
typedef struct {
void *v_output;
void (*write)(void *v_output, const frame_s *frame);
void (*destroy)(void *v_output);
} _output_context_s;
static void _signal_handler(int signum);
static void _install_signal_handlers(void);
static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *output_path, bool output_json);
static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_context_s *ctx);
static void _help(FILE *fp);
@@ -138,8 +146,23 @@ int main(int argc, char *argv[]) {
return 1;
}
_output_context_s ctx;
MEMSET_ZERO(ctx);
if (output_path && output_path[0] != '\0') {
if ((ctx.v_output = (void *)output_file_init(output_path, output_json)) == NULL) {
return 1;
}
ctx.write = output_file_write;
ctx.destroy = output_file_destroy;
}
_install_signal_handlers();
return abs(_dump_sink(sink_name, sink_timeout, output_path, output_json));
int retval = abs(_dump_sink(sink_name, sink_timeout, &ctx));
if (ctx.v_output && ctx.destroy) {
ctx.destroy(ctx.v_output);
}
return retval;
}
@@ -150,7 +173,7 @@ static void _signal_handler(int signum) {
case SIGPIPE: LOG_INFO_NOLOCK("===== Stopping by SIGPIPE ====="); break;
default: LOG_INFO_NOLOCK("===== Stopping by %d =====", signum); break;
}
stop = true;
global_stop = true;
}
static void _install_signal_handlers(void) {
@@ -173,25 +196,9 @@ static void _install_signal_handlers(void) {
assert(!sigaction(SIGPIPE, &sig_act, NULL));
}
static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *output_path, bool output_json) {
static int _dump_sink(const char *sink_name, unsigned sink_timeout, _output_context_s *ctx) {
frame_s *frame = frame_init("input");
memsink_s *sink = NULL;
FILE *output_fp = NULL;
char *base64_data = NULL;
size_t base64_allocated = 0;
if (output_path && output_path[0] != '\0') {
if (!strcmp(output_path, "-")) {
LOG_INFO("Using output: <stdout>");
output_fp = stdout;
} else {
LOG_INFO("Using output: %s", output_path);
if ((output_fp = fopen(output_path, "wb")) == NULL) {
LOG_PERROR("Can't open output file");
goto error;
}
}
}
if ((sink = memsink_init("input", sink_name, false, 0, false, sink_timeout)) == NULL) {
goto error;
@@ -201,7 +208,7 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *
unsigned fps_accum = 0;
long long fps_second = 0;
while (!stop) {
while (!global_stop) {
int error = memsink_client_get(sink, frame);
if (error == 0) {
const long double now = get_now_monotonic();
@@ -225,22 +232,8 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *
}
fps_accum += 1;
if (output_fp) {
if (output_json) {
base64_encode(frame->data, frame->used, &base64_data, &base64_allocated);
fprintf(output_fp,
"{\"size\": %zu, \"width\": %u, \"height\": %u,"
" \"format\": %u, \"stride\": %u, \"online\": %u,"
" \"grab_ts\": %.3Lf, \"encode_begin_ts\": %.3Lf, \"encode_end_ts\": %.3Lf,"
" \"data\": \"%s\"}\n",
frame->used, frame->width, frame->height,
frame->format, frame->stride, frame->online,
frame->grab_ts, frame->encode_begin_ts, frame->encode_end_ts,
base64_data);
} else {
fwrite(frame->data, 1, frame->used, output_fp);
}
fflush(output_fp);
if (ctx->v_output) {
ctx->write(ctx->v_output, frame);
}
} else if (error != -2) {
goto error;
@@ -254,14 +247,6 @@ static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *
retval = -1;
ok:
if (base64_data) {
free(base64_data);
}
if (output_fp && output_fp != stdout) {
if (fclose(output_fp) < 0) {
LOG_PERROR("Can't close output file");
}
}
if (sink) {
memsink_destroy(sink);
}