From d5275cacf7af8a355bad3a26bcd5fd72d761c7d2 Mon Sep 17 00:00:00 2001 From: Devaev Maxim Date: Thu, 7 Jan 2021 09:44:39 +0300 Subject: [PATCH] dump as json --- src/dump/main.c | 66 +++++++++++++++++++-------- src/{ustreamer/http => libs}/base64.c | 40 +++++++++------- src/{ustreamer/http => libs}/base64.h | 6 ++- src/ustreamer/http/server.c | 4 +- src/ustreamer/http/server.h | 2 +- 5 files changed, 76 insertions(+), 42 deletions(-) rename src/{ustreamer/http => libs}/base64.c (68%) rename src/{ustreamer/http => libs}/base64.h (92%) diff --git a/src/dump/main.c b/src/dump/main.c index 449063d..64ec78c 100644 --- a/src/dump/main.c +++ b/src/dump/main.c @@ -11,12 +11,14 @@ #include "../libs/logging.h" #include "../libs/frame.h" #include "../libs/memsink.h" +#include "../libs/base64.h" enum _OPT_VALUES { _O_SINK = 's', - _O_TIMEOUT = 't', + _O_SINK_TIMEOUT = 't', _O_OUTPUT = 'o', + _O_OUTPUT_JSON = 'j', _O_HELP = 'h', _O_VERSION = 'v', @@ -31,8 +33,9 @@ enum _OPT_VALUES { static const struct option _LONG_OPTS[] = { {"sink", required_argument, NULL, _O_SINK}, - {"output", no_argument, NULL, _O_OUTPUT}, - {"timeout", required_argument, NULL, _O_TIMEOUT}, + {"sink-timeout", required_argument, NULL, _O_SINK_TIMEOUT}, + {"output", required_argument, NULL, _O_OUTPUT}, + {"output-json", no_argument, NULL, _O_OUTPUT_JSON}, {"log-level", required_argument, NULL, _O_LOG_LEVEL}, {"perf", no_argument, NULL, _O_PERF}, @@ -53,7 +56,9 @@ volatile bool stop = false; static void _signal_handler(int signum); static void _install_signal_handlers(void); -static int _dump_sink(const char *sink_name, const char *output_path, unsigned timeout); + +static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *output_path, bool output_json); + static void _help(FILE *fp); @@ -62,8 +67,9 @@ int main(int argc, char *argv[]) { A_THREAD_RENAME("main"); char *sink_name = NULL; + unsigned sink_timeout = 1; char *output_path = NULL; - unsigned timeout = 1; + bool output_json = false; # define OPT_SET(_dest, _value) { \ _dest = _value; \ @@ -80,11 +86,12 @@ int main(int argc, char *argv[]) { break; \ } - for (int ch; (ch = getopt_long(argc, argv, "s:o:t:hv", _LONG_OPTS, NULL)) >= 0;) { + for (int ch; (ch = getopt_long(argc, argv, "s:t:o:jhv", _LONG_OPTS, NULL)) >= 0;) { switch (ch) { - case _O_SINK: OPT_SET(sink_name, optarg); - case _O_OUTPUT: OPT_SET(output_path, optarg); - case _O_TIMEOUT: OPT_NUMBER("--timeout", timeout, 1, 60, 0); + case _O_SINK: OPT_SET(sink_name, optarg); + case _O_SINK_TIMEOUT: OPT_NUMBER("--sink-timeout", sink_timeout, 1, 60, 0); + case _O_OUTPUT: OPT_SET(output_path, optarg); + case _O_OUTPUT_JSON: OPT_SET(output_json, true); case _O_LOG_LEVEL: OPT_NUMBER("--log-level", log_level, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, 0); case _O_PERF: OPT_SET(log_level, LOG_LEVEL_PERF); @@ -110,7 +117,7 @@ int main(int argc, char *argv[]) { } _install_signal_handlers(); - return abs(_dump_sink(sink_name, output_path, timeout)); + return abs(_dump_sink(sink_name, sink_timeout, output_path, output_json)); } @@ -144,10 +151,12 @@ static void _install_signal_handlers(void) { assert(!sigaction(SIGPIPE, &sig_act, NULL)); } -static int _dump_sink(const char *sink_name, const char *output_path, unsigned timeout) { +static int _dump_sink(const char *sink_name, unsigned sink_timeout, const char *output_path, bool output_json) { frame_s *frame = frame_init("input"); - FILE *output_fp = NULL; 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, "-")) { @@ -162,7 +171,7 @@ static int _dump_sink(const char *sink_name, const char *output_path, unsigned t } } - if ((sink = memsink_init("input", sink_name, false, 0, false, timeout)) == NULL) { + if ((sink = memsink_init("input", sink_name, false, 0, false, sink_timeout)) == NULL) { goto error; } @@ -191,7 +200,20 @@ static int _dump_sink(const char *sink_name, const char *output_path, unsigned t fps_accum += 1; if (output_fp) { - fwrite(frame->data, 1, frame->used, 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); } } else if (error != -2) { @@ -206,14 +228,17 @@ static int _dump_sink(const char *sink_name, const char *output_path, unsigned t retval = -1; ok: - if (sink) { - memsink_destroy(sink); + 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); + } frame_destroy(frame); LOG_INFO("Bye-bye"); @@ -228,13 +253,14 @@ static void _help(FILE *fp) { SAY("Copyright (C) 2018 Maxim Devaev \n"); SAY("Example:"); SAY("════════"); - SAY(" ustreamer-dump --sink test -o - \\"); + SAY(" ustreamer-dump --sink test --output - \\"); SAY(" | ffmpeg -use_wallclock_as_timestamps 1 -i pipe: -c:v libx264 test.mp4\n"); SAY("Sink options:"); SAY("═════════════"); - SAY(" -s|--sink ─── Memory sink ID. No default.\n"); - SAY(" -t|--timeout ─ Timeout for the upcoming frame. Default: 1.\n"); - SAY(" -o|--output ──────── Filename to dump. Use '-' for stdout. Default: just consume the sink.\n"); + SAY(" -s|--sink ──────── Memory sink ID. No default.\n"); + SAY(" -t|--sink-timeout ─ Timeout for the upcoming frame. Default: 1.\n"); + SAY(" -o|--output ───────────── Filename to dump. Use '-' for stdout. Default: just consume the sink.\n"); + SAY(" -j|--output-json ──────── Format output as JSON. Required option --output.\n"); SAY("Logging options:"); SAY("════════════════"); SAY(" --log-level ──── Verbosity level of messages from 0 (info) to 3 (debug)."); diff --git a/src/ustreamer/http/base64.c b/src/libs/base64.c similarity index 68% rename from src/ustreamer/http/base64.c rename to src/libs/base64.c index 6b13aa2..2cfce27 100644 --- a/src/ustreamer/http/base64.c +++ b/src/libs/base64.c @@ -37,30 +37,36 @@ static const char _ENCODING_TABLE[] = { static const unsigned _MOD_TABLE[] = {0, 2, 1}; -char *base64_encode(const uint8_t *str) { - size_t str_len = strlen((const char *)str); - size_t encoded_size = 4 * ((str_len + 2) / 3) + 1; // +1 for '\0' +void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated) { + const size_t encoded_size = 4 * ((size + 2) / 3) + 1; // +1 for '\0' - char *encoded; - A_CALLOC(encoded, encoded_size); + if (*encoded == NULL || (allocated && *allocated < encoded_size)) { + A_REALLOC(*encoded, encoded_size); + if (allocated) { + *allocated = encoded_size; + } + } - for (unsigned str_index = 0, encoded_index = 0; str_index < str_len;) { - unsigned octet_a = (str_index < str_len ? (uint8_t)str[str_index++] : 0); - unsigned octet_b = (str_index < str_len ? (uint8_t)str[str_index++] : 0); - unsigned octet_c = (str_index < str_len ? (uint8_t)str[str_index++] : 0); + for (unsigned data_index = 0, encoded_index = 0; data_index < size;) { +# define OCTET(_name) unsigned _name = (data_index < size ? (uint8_t)data[data_index++] : 0) + OCTET(octet_a); + OCTET(octet_b); + OCTET(octet_c); +# undef OCTET unsigned triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; - encoded[encoded_index++] = _ENCODING_TABLE[(triple >> 3 * 6) & 0x3F]; - encoded[encoded_index++] = _ENCODING_TABLE[(triple >> 2 * 6) & 0x3F]; - encoded[encoded_index++] = _ENCODING_TABLE[(triple >> 1 * 6) & 0x3F]; - encoded[encoded_index++] = _ENCODING_TABLE[(triple >> 0 * 6) & 0x3F]; +# define ENCODE(_offset) (*encoded)[encoded_index++] = _ENCODING_TABLE[(triple >> _offset * 6) & 0x3F] + ENCODE(3); + ENCODE(2); + ENCODE(1); + ENCODE(0); +# undef ENCODE } - for (unsigned index = 0; index < _MOD_TABLE[str_len % 3]; index++) { - encoded[encoded_size - 2 - index] = '='; + for (unsigned index = 0; index < _MOD_TABLE[size % 3]; index++) { + (*encoded)[encoded_size - 2 - index] = '='; } - encoded[encoded_size - 1] = '\0'; - return encoded; + (*encoded)[encoded_size - 1] = '\0'; } diff --git a/src/ustreamer/http/base64.h b/src/libs/base64.h similarity index 92% rename from src/ustreamer/http/base64.h rename to src/libs/base64.h index 9a63426..467764e 100644 --- a/src/ustreamer/http/base64.h +++ b/src/libs/base64.h @@ -26,7 +26,9 @@ #include #include -#include "../../libs/tools.h" +#include + +#include "tools.h" -char *base64_encode(const uint8_t *str); +void base64_encode(const uint8_t *data, size_t size, char **encoded, size_t *allocated); diff --git a/src/ustreamer/http/server.c b/src/ustreamer/http/server.c index 8bfc96d..f27056f 100644 --- a/src/ustreamer/http/server.c +++ b/src/ustreamer/http/server.c @@ -149,11 +149,11 @@ int server_listen(server_s *server) { if (server->user[0] != '\0') { char *raw_token; - char *encoded_token; + char *encoded_token = NULL; A_CALLOC(raw_token, strlen(server->user) + strlen(server->passwd) + 2); sprintf(raw_token, "%s:%s", server->user, server->passwd); - encoded_token = base64_encode((uint8_t *)raw_token); + base64_encode((uint8_t *)raw_token, strlen(raw_token), &encoded_token, NULL); free(raw_token); A_CALLOC(RUN(auth_token), strlen(encoded_token) + 16); diff --git a/src/ustreamer/http/server.h b/src/ustreamer/http/server.h index 8795c57..1581d94 100644 --- a/src/ustreamer/http/server.h +++ b/src/ustreamer/http/server.h @@ -58,6 +58,7 @@ #include "../../libs/logging.h" #include "../../libs/process.h" #include "../../libs/frame.h" +#include "../../libs/base64.h" #include "../data/index_html.h" #include "../encoder.h" #include "../stream.h" @@ -68,7 +69,6 @@ #include "bev.h" #include "unix.h" #include "uri.h" -#include "base64.h" #include "mime.h" #include "static.h"