CPPFLAGS=@CPPFLAGS@
CPPFLAGS += -Itlse -DWITH_KTLS -DNO_TLS_LEGACY_SUPPORT -DNO_SSL_COMPATIBLE_INTERFACE -DLTM_DESC -DTLS_REEXPORTABLE -DNO_TLS_WITH_CHACHA20_POLY1305
CXXFLAGS=-Wall @CXXFLAGS@ @protobuf_CFLAGS@ @libsystemd_CFLAGS@ @libtomcrypt_CFLAGS@ -pthread
+CFLAGS=-Wall @CFLAGS@
LDFLAGS=@LDFLAGS@ -pthread
LIBS=@LIBS@ @protobuf_LIBS@ @libsystemd_LIBS@ @libtomcrypt_LIBS@
+SYSTEMDSYSTEMUNITDIR=@SYSTEMDSYSTEMUNITDIR@
OBJS=main.o client.o server.o stream.o udpstream.o serverpool.o input.o input_stats.o httpinput.o udpinput.o parse.o config.o acceptor.o stats.o accesslog.o thread.o util.o log.o metacube2.o sa_compare.o timespec.o state.pb.o tlse/tlse.o
+ifeq (@have_ffmpeg@,yes)
+all: cubemap ffmpeg_metacube_hack.so
+else
all: cubemap
+endif
%.pb.cc %.pb.h : %.proto
$(PROTOC) --cpp_out=. $<
$(CXX) -MMD -MP $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<
cubemap: $(OBJS)
$(CXX) -o cubemap $(OBJS) $(LIBS) $(LDFLAGS)
+ffmpeg_metacube_hack.so: ffmpeg_metacube_hack.c
+ $(CC) -shared $(CPPFLAGS) $(CFLAGS) -fPIC -o $@ $< -ldl
DEPS=$(OBJS:.o=.d)
-include $(DEPS)
clean:
- $(RM) cubemap $(OBJS) $(DEPS) state.pb.h state.pb.cc
+ $(RM) cubemap $(OBJS) $(DEPS) state.pb.h state.pb.cc ffmpeg_metacube_hack.so
-PREFIX=@prefix@
-SYSCONFDIR=@sysconfdir@
-LOCALSTATEDIR=@localstatedir@
+prefix=@prefix@
+sysconfdir=@sysconfdir@
+localstatedir=@localstatedir@
+libdir=@libdir@
install:
$(INSTALL) -m 755 -o root -g root -d \
- $(DESTDIR)$(PREFIX)/bin \
- $(DESTDIR)$(PREFIX)/share/man/man1 \
- $(DESTDIR)$(SYSCONFDIR) \
- $(DESTDIR)$(LOCALSTATEDIR)/lib/cubemap \
- $(DESTDIR)$(LOCALSTATEDIR)/log/cubemap \
- $(DESTDIR)$(PREFIX)/share/munin/plugins \
- $(DESTDIR)/lib/systemd/system
- $(INSTALL) -m 755 -o root -g root cubemap $(DESTDIR)$(PREFIX)/bin/cubemap
- $(INSTALL) -m 755 -o root -g root munin/cubemap munin/cubemap_input $(DESTDIR)$(PREFIX)/share/munin/plugins/
- gzip -c cubemap.1 > $(DESTDIR)$(PREFIX)/share/man/man1/cubemap.1.gz
+ $(DESTDIR)$(prefix)/bin \
+ $(DESTDIR)$(prefix)/share/man/man1 \
+ $(DESTDIR)$(sysconfdir) \
+ $(DESTDIR)$(localstatedir)/lib/cubemap \
+ $(DESTDIR)$(localstatedir)/log/cubemap \
+ $(DESTDIR)$(prefix)/share/munin/plugins \
+ $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
+ $(INSTALL) -m 755 -o root -g root cubemap $(DESTDIR)$(prefix)/bin/cubemap
+ $(INSTALL) -m 755 -o root -g root munin/cubemap munin/cubemap_input $(DESTDIR)$(prefix)/share/munin/plugins/
+ifeq (@have_ffmpeg@,yes)
+ $(INSTALL) -m 755 -o root -g root -d $(DESTDIR)$(libdir)
+ $(INSTALL) -m 755 -o root -g root ffmpeg_metacube_hack.so $(DESTDIR)$(libdir)/
+endif
+ gzip -c cubemap.1 > $(DESTDIR)$(prefix)/share/man/man1/cubemap.1.gz
sed \
- -e "s,@prefix[@],$(PREFIX),g" \
- -e "s,@sysconfdir[@],$(SYSCONFDIR),g" \
- cubemap.service.in > $(DESTDIR)/lib/systemd/system/cubemap.service
+ -e "s,@prefix[@],$(prefix),g" \
+ -e "s,@sysconfdir[@],$(sysconfdir),g" \
+ cubemap.service.in > $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)/cubemap.service
# Make sure we don't overwrite an existing configuration.
- test ! -f $(DESTDIR)$(SYSCONFDIR)/cubemap.config
+ test ! -f $(DESTDIR)$(sysconfdir)/cubemap.config
sed \
- -e "s,cubemap\.stats,$(LOCALSTATEDIR)/lib/cubemap/\0,g" \
- -e "s,cubemap-input\.stats,$(LOCALSTATEDIR)/lib/cubemap/\0,g" \
- -e "s,access\.log,$(LOCALSTATEDIR)/log/cubemap/\0,g" \
- -e "s,cubemap\.log,$(LOCALSTATEDIR)/log/cubemap/\0,g" \
+ -e "s,cubemap\.stats,$(localstatedir)/lib/cubemap/\0,g" \
+ -e "s,cubemap-input\.stats,$(localstatedir)/lib/cubemap/\0,g" \
+ -e "s,access\.log,$(localstatedir)/log/cubemap/\0,g" \
+ -e "s,cubemap\.log,$(localstatedir)/log/cubemap/\0,g" \
-e 's,^stream,#\0,g' \
-e 's,^udpstream,#\0,g' \
- cubemap.config.sample > $(DESTDIR)$(SYSCONFDIR)/cubemap.config
+ cubemap.config.sample > $(DESTDIR)$(sysconfdir)/cubemap.config
.PHONY: clean install
.SUFFIXES:
+Cubemap 1.5.1, 2023-12-17
+
+ * Use systemd.pc to place systemd system units.
+ Patch by Chris Hofstaedtler.
+
+
+Cubemap 1.5.0, 2023-12-09
+
+ * Support input from pipes (subprocesses).
+
+ With a program that can output Metacube on stdout, such as a suitably
+ patched FFmpeg binary, this gives Cubemap transcoding/remuxing capabilities.
+
+ Of course, one cannot upgrade such a binary by SIGHUP-ing it, but it will
+ survive a Cubemap reload/restart.
+
+ * Add a LD_PRELOAD-able library to force Metacube output from FFmpeg.
+
+ This hooks just the right amount of functions to add Metacube output
+ to arbitrary FFmpeg programs, but is obviously very brittle.
+ (Native FFmpeg support would be better, but a patch did not
+ go through when I tried a while back.) It is only lightly tested.
+ Documentation in the README and cubemap.config.sample.
+
+ * Various bugfixes.
+
+
Cubemap 1.4.3, 2019-11-17
* Keep the HLS backlog even if the stream header changes. Useful
discontinuities).
* Various bugfixes.
-
+
Cubemap 1.4.2, 2018-12-21
-Cubemap is a high-performance, high-availability video reflector,
-specifically made for use with VLC.
+Cubemap is a high-performance, high-availability video reflector.
A short list of features:
and sending a SIGHUP; all clients will continue as if nothing had happened
(unless you delete the stream they are watching, of course).
Cubemap also survives the encoder dying and reconnecting.
- - Support for setting max pacing rate through the fq packet scheduler
- (depends on Linux 3.13 or newer).
+ - Support for setting max pacing rate through the fq packet scheduler.
- Reflects anything VLC can reflect over HTTP, even the muxes VLC
- has problems reflecting itself (in particular, FLV).
+ has problems reflecting itself (in particular, FLV). Can also
+ call out to external programs to receive or fetch streams in arbitrary
+ ways (e.g. through SRT), although of course they do not necessarily
+ have the same intrinsic high-availability reconfiguration support.
- Multicast support, both for sending and receiving (supports only protocols
that can go over UDP, e.g. MPEG-TS). Supports both ASM and SSM.
- TLS output support, through the TLSe library (requires libtomcrypt)
HOWTO:
- sudo apt install libprotobuf-dev protobuf-compiler libsystemd-dev libtomcrypt-dev
+ sudo apt install libprotobuf-dev protobuf-compiler libsystemd-dev libtomcrypt-dev pkg-config autoconf
+ sudo apt install libavformat-dev libavutil-dev # Optional
+ autoreconf -I .
./configure
make -j4
-If you want to use HTTP input (you probably want to), you want VLC 2.2.0
-or newer. Then start the VLC encoder with the “metacube” flag to the http
+Cubemap does not in itself understand the format of video streams;
+it requires HTTP input that is already delineated into headers and blocks,
+so that it knows what parts of the stream to skip to clients that
+connect after the stream has been. (The exception is self-synchronizing
+streams with no headers, such as MPEG-TS. These can also be sent or
+received over UDP) The input format is specific to Cubemap and is called
+Metacube (technically, Metacube2, but Metacube1 is long gone and nothing
+uses it anymore). Currently, you have three options to create Metacube
+video streams: VLC, Nageru or FFmpeg with a hack.
+
+For VLC, start the VLC encoder with the “metacube” flag to the http
access mux, like this:
cvlc [...] --sout '#std{access=http{metacube,mime=video/x-flv},mux=flv,dst=:4013/test.flv}'
Then look through cubemap.config.sample, copy it to cubemap.config,
compile and start cubemap.
-Nageru, my free video mixer, can also produce Metacube streams natively.
-See the manual at https://nageru.sesse.net/doc/ for more information.
+Nageru, my free video mixer, can also produce Metacube streams natively,
+and so can its included transcoder/remuxer Kaeru. See the manual
+at https://nageru.sesse.net/doc/ for more information.
+
+If you feel very adventurous, you can use LD_PRELOAD to load
+ffmpeg_metacube_hack.so into an FFmpeg-using binary. (This is
+experimental; native Metacube support is vastly preferred.)
+For instance, here's one way you can use the ffmpeg(1) binary
+to serve your webcam to Cubemap:
+
+ LD_PRELOAD=ffmpeg_metacube_hack.so ffmpeg -i /dev/video0 -f mpegts -listen 1 'http://[::]:9095'
+
To upgrade cubemap (after you've compiled a new binary), or to pick up new
config:
{
// Set back the close-on-exec flag for the socket.
// (This can't leak into a child, since we haven't been started yet.)
- fcntl(server_sock, F_SETFD, O_CLOEXEC);
+ fcntl(server_sock, F_SETFD, FD_CLOEXEC);
}
AcceptorProto Acceptor::serialize() const
{
// Set back the close-on-exec flag for the socket.
// (This can't leak into a child, since we haven't been started yet.)
- fcntl(sock, F_SETFD, O_CLOEXEC);
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
if (stream != nullptr) {
if (setsockopt(sock, SOL_SOCKET, SO_MAX_PACING_RATE, &stream->pacing_rate, sizeof(stream->pacing_rate)) == -1) {
AC_CONFIG_MACRO_DIR([m4])
-AC_INIT(cubemap, 1.4.3)
+AC_INIT(cubemap, 1.5.2-pre)
AC_CONFIG_SRCDIR(main.cpp)
PKG_CHECK_MODULES([libsystemd], [libsystemd])
PKG_CHECK_MODULES([protobuf], [protobuf])
PKG_CHECK_MODULES([libtomcrypt], [libtomcrypt])
+PKG_CHECK_VAR(SYSTEMDSYSTEMUNITDIR, systemd, systemdsystemunitdir)
+
+have_ffmpeg=yes
+PKG_CHECK_MODULES([libavformat], [libavformat], [true], [have_ffmpeg=no])
+PKG_CHECK_MODULES([libavutil], [libavutil], [true], [have_ffmpeg=no])
+AC_SUBST([have_ffmpeg])
+
+AS_IF([test "x$have_ffmpeg" = "xno" ],
+ [AC_MSG_WARN([FFmpeg libraries not found. ffmpeg_metacube_hack.so will not be built.])])
CXXFLAGS="$CXXFLAGS -std=gnu++11"
# to be some reasonable fraction of your fragment length.
stream /stream.mp4 src=http://gruessi.zrh.sesse.net:9095/test.mp4.metacube hls_playlist=/stream.m3u8 hls_frag_duration=6 backlog_size=20971520 hls_backlog_margin=1048576 allow_origin=*
+# An example of using pipe: to read from a program that generates Metacube
+# on standard output (or you can use src_encoding=raw for MPEG_TS).
+# This shows how to use FFmpeg, combined with a LD_PRELOAD hack
+# (the .so file is built with Cubemap if you have the FFmpeg development
+# libraries instealled), to receive MPEG-TS over SRT and remux it into MP4.
+# Note that using ffmpeg_metacube_hack.so is experimental and not recommended
+# if you can avoid it.
+#
+# Only one instance of the command will be started at a time (it will drive a
+# stream just like an HTTP input will). Such subprocesses will survive
+# a Cubemap reload, but can of course themselves not be live-reloaded
+# like Cubemap can. Standard error will be passed through unchanged.
+stream /srt.mp4 src=pipe:"LD_PRELOAD=ffmpeg_metacube_hack.so ffmpeg -loglevel warning -i 'srt://[::]:9710?mode=listener' -c:v copy -c:a copy -f mp4 -movflags empty_moov+frag_keyframe+default_base_moof+skip_trailer -frag_duration 125000 -bsf:a aac_adtstoasc -"
+
# UDP input. TS is the most common container to use over UDP (you cannot
# take any arbitrary container and expect it to work).
# backlog_size=<number of bytes> overrides the backlog, which is normally 10 MB.
--- /dev/null
+// A shared library that you can LD_PRELOAD into an FFmpeg-using process
+// (most likely ffmpeg(1)) to make it output Metacube. This is obviously
+// pretty hacky, since it needs to override various FFmpeg functions,
+// so there are few guarantees here. It is written in C to avoid pulling
+// in the C++ runtime into FFmpeg's C-only world.
+//
+// You should not link to this library. It does not have ABI stability.
+// It is licensed the same as the rest of Cubemap.
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <dlfcn.h>
+#include <libavformat/avformat.h>
+#include <libavformat/avio.h>
+#include <libavutil/avassert.h>
+#include <libavutil/crc.h>
+#include <libavutil/error.h>
+#include <libavutil/intreadwrite.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/signal.h>
+#include "metacube2.h"
+
+static pthread_once_t metacube2_crc_once_control = PTHREAD_ONCE_INIT;
+static AVCRC metacube2_crc_table[257];
+
+// We need to store some extra information for each context,
+// so this is where we do it. The “opaque” field in the AVIOContext
+// points to this struct, but we can also look it up by the AVIOContext
+// pointer by scanning through the singly linked list starting with
+// first_extra_data.
+struct ContextExtraData {
+ struct ContextExtraData *next; // NULL for last entry.
+ AVIOContext *ctx; // The context we are associating data with.
+ bool seen_sync_point;
+ void *old_opaque;
+ int (*old_write_data_type)(void *opaque, uint8_t * buf,
+ int buf_size,
+ enum AVIODataMarkerType type,
+ int64_t time);
+
+ // Used during avformat_write_header(), to combine adjacent header blocks
+ // into one (in particular, the MP4 mux has an unneeded avio_flush()
+ // halfway throughout).
+ bool in_header;
+ int64_t header_first_time;
+ uint8_t *buffered_header;
+ size_t buffered_header_bytes;
+};
+static struct ContextExtraData *first_extra_data = NULL;
+
+// Look up ContextExtraData for the given context, creating a new one if needed.
+static struct ContextExtraData *get_extra_data(AVIOContext * ctx)
+{
+ for (struct ContextExtraData * ed = first_extra_data; ed != NULL;
+ ed = ed->next) {
+ if (ed->ctx == ctx) {
+ return ed;
+ }
+ }
+ struct ContextExtraData *ed = (struct ContextExtraData *)
+ malloc(sizeof(struct ContextExtraData));
+ ed->ctx = ctx;
+ ed->seen_sync_point = false;
+ ed->old_write_data_type = NULL;
+ ed->in_header = false;
+ ed->buffered_header = NULL;
+ ed->buffered_header_bytes = 0;
+
+ ed->next = first_extra_data;
+ first_extra_data = ed;
+ return ed;
+}
+
+// Clear ContextExtraData for a given context (presumably before it's freed).
+static void free_extra_data(AVIOContext * ctx)
+{
+ if (first_extra_data == NULL) {
+ return;
+ }
+ if (first_extra_data->ctx == ctx) {
+ struct ContextExtraData *to_free = first_extra_data;
+ first_extra_data = to_free->next;
+ free(to_free);
+ return;
+ }
+ for (struct ContextExtraData * ed = first_extra_data; ed != NULL;
+ ed = ed->next) {
+ if (ed->next != NULL && ed->next->ctx == ctx) {
+ struct ContextExtraData *to_free = ed->next;
+ ed->next = to_free->next;
+ free(to_free);
+ return;
+ }
+ }
+}
+
+static void metacube2_crc_init_table_once(void)
+{
+ av_assert0(av_crc_init
+ (metacube2_crc_table, 0, 16, 0x8fdb,
+ sizeof(metacube2_crc_table)) >= 0);
+}
+
+static uint16_t metacube2_compute_crc_ff(const struct
+ metacube2_block_header *hdr)
+{
+ static const int data_len = sizeof(hdr->size) + sizeof(hdr->flags);
+ const uint8_t *data = (uint8_t *) & hdr->size;
+ uint16_t crc;
+
+ pthread_once(&metacube2_crc_once_control,
+ metacube2_crc_init_table_once);
+
+ // Metacube2 specifies a CRC start of 0x1234, but its pycrc-derived CRC
+ // includes a finalization step that is done somewhat differently in av_crc().
+ // 0x1234 alone sent through that finalization becomes 0x394a, and then we
+ // need a byte-swap of the CRC value (both on input and output) to account for
+ // differing conventions.
+ crc = av_crc(metacube2_crc_table, 0x4a39, data, data_len);
+ return av_bswap16(crc);
+}
+
+static int write_packet(void *opaque, uint8_t * buf, int buf_size,
+ enum AVIODataMarkerType type, int64_t time)
+{
+ if (buf_size < 0) {
+ return AVERROR(EINVAL);
+ }
+
+ struct ContextExtraData *ed = (struct ContextExtraData *) opaque;
+
+ if (ed->in_header) {
+ if (ed->buffered_header_bytes == 0) {
+ ed->header_first_time = time;
+ }
+
+ size_t new_buffered_header_bytes =
+ ed->buffered_header_bytes + buf_size;
+ if (new_buffered_header_bytes < ed->buffered_header_bytes) {
+ return AVERROR(ENOMEM);
+ }
+ ed->buffered_header =
+ (uint8_t *) realloc(ed->buffered_header,
+ new_buffered_header_bytes);
+ if (ed->buffered_header == NULL) {
+ return AVERROR(ENOMEM);
+ }
+
+ memcpy(ed->buffered_header + ed->buffered_header_bytes,
+ buf, buf_size);
+ ed->buffered_header_bytes = new_buffered_header_bytes;
+ return buf_size;
+ }
+ // Find block size if we add a Metacube2 header in front.
+ unsigned new_buf_size =
+ (unsigned) buf_size + sizeof(struct metacube2_block_header);
+ if (new_buf_size < (unsigned) buf_size
+ || new_buf_size > (unsigned) INT_MAX) {
+ // Overflow.
+ return -1;
+ }
+ // Fill in the header.
+ struct metacube2_block_header hdr;
+ int flags = 0;
+ if (type == AVIO_DATA_MARKER_SYNC_POINT)
+ ed->seen_sync_point = 1;
+ else if (type == AVIO_DATA_MARKER_HEADER)
+ // NOTE: If there are multiple blocks marked METACUBE_FLAGS_HEADER,
+ // only the last one will count. This may become a problem if the
+ // mux flushes halfway through the stream header; if so, we would
+ // need to keep track of and concatenate the different parts.
+ flags |= METACUBE_FLAGS_HEADER;
+ else if (ed->seen_sync_point)
+ flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START;
+
+ memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync));
+ AV_WB32(&hdr.size, buf_size);
+ AV_WB16(&hdr.flags, flags);
+ AV_WB16(&hdr.csum, metacube2_compute_crc_ff(&hdr));
+
+ int ret;
+ ed->ctx->opaque = ed->old_opaque;
+ if (new_buf_size < ed->ctx->max_packet_size) {
+ // Combine the two packets. (This is what we normally want.)
+ // So we allocate a new block, with a Metacube2 header in front.
+ uint8_t *buf_with_hdr = (uint8_t *) malloc(new_buf_size);
+ if (buf_with_hdr == NULL) {
+ return AVERROR(ENOMEM);
+ }
+ memcpy(buf_with_hdr, &hdr, sizeof(hdr));
+ memcpy(buf_with_hdr + sizeof(hdr), buf, buf_size);
+ if (ed->old_write_data_type) {
+ ret =
+ ed->old_write_data_type(ed->old_opaque,
+ buf_with_hdr,
+ new_buf_size, type,
+ time);
+ } else {
+ ret =
+ ed->ctx->write_packet(ed->old_opaque,
+ buf_with_hdr,
+ new_buf_size);
+ }
+ free(buf_with_hdr);
+
+ if (ret >= 0
+ && ret >= sizeof(struct metacube2_block_header)) {
+ ret -= sizeof(struct metacube2_block_header);
+ }
+ } else {
+ // Send separately. This will split a header block if it's really large,
+ // which we don't want, but that's how things are.
+ if (ed->old_write_data_type) {
+ ret =
+ ed->old_write_data_type(ed->old_opaque,
+ (uint8_t *) & hdr,
+ sizeof(hdr), type,
+ time);
+ } else {
+ ret =
+ ed->ctx->write_packet(ed->old_opaque,
+ (uint8_t *) & hdr,
+ sizeof(hdr));
+ }
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret != sizeof(hdr)) {
+ return AVERROR(EIO);
+ }
+
+ if (ed->old_write_data_type) {
+ ret =
+ ed->old_write_data_type(ed->old_opaque, buf,
+ buf_size, type, time);
+ } else {
+ ret =
+ ed->ctx->write_packet(ed->old_opaque, buf,
+ buf_size);
+ }
+ }
+
+ ed->ctx->opaque = ed;
+ return ret;
+}
+
+// Actual hooked functions below.
+
+int avformat_write_header(AVFormatContext * ctx, AVDictionary ** options)
+{
+ metacube2_crc_init_table_once();
+
+ struct ContextExtraData *ed = get_extra_data(ctx->pb);
+ ed->old_opaque = ctx->pb->opaque;
+ ed->old_write_data_type = ctx->pb->write_data_type;
+ ctx->pb->opaque = ed;
+ ctx->pb->write_data_type = write_packet;
+ ctx->pb->seek = NULL;
+ ctx->pb->seekable = 0;
+ if (ed->old_write_data_type == NULL) {
+ ctx->pb->ignore_boundary_point = 1;
+ }
+
+ int (*original_func)(AVFormatContext * ctx,
+ AVDictionary ** options);
+ original_func = dlsym(RTLD_NEXT, "avformat_write_header");
+
+ ed->in_header = true;
+ int ret = (*original_func) (ctx, options);
+ ed->in_header = false;
+
+ if (ed->buffered_header_bytes > 0) {
+ int hdr_ret = write_packet(ed, ed->buffered_header,
+ ed->buffered_header_bytes,
+ AVIO_DATA_MARKER_HEADER,
+ ed->header_first_time);
+ free(ed->buffered_header);
+ ed->buffered_header = NULL;
+
+ if (hdr_ret >= 0 && hdr_ret < ed->buffered_header_bytes) {
+ hdr_ret = AVERROR(EIO);
+ }
+ ed->buffered_header_bytes = 0;
+ if (hdr_ret < 0) {
+ return hdr_ret;
+ }
+ }
+
+ return ret;
+}
+
+void avformat_free_context(AVFormatContext * ctx)
+{
+ if (ctx == NULL) {
+ return;
+ }
+ free_extra_data(ctx->pb);
+
+ void (*original_func)(AVFormatContext * ctx);
+ original_func = dlsym(RTLD_NEXT, "avformat_free_context");
+ return (*original_func) (ctx);
+}
+
+// Hook so that we can restore opaque instead of ours being freed by the caller.
+int avio_close(AVIOContext * ctx)
+{
+ if (ctx == NULL) {
+ return 0;
+ }
+ struct ContextExtraData ed = *get_extra_data(ctx);
+ free_extra_data(ctx);
+ ctx->opaque = ed.old_opaque;
+
+ int (*original_func)(AVIOContext * ctx);
+ original_func = dlsym(RTLD_NEXT, "avio_close");
+ return (*original_func) (ctx);
+}
+
+// Identical to FFmpeg's definition, but we cannot hook avio_close()
+// when called from FFmpeg's avio_closep(), so we need to hook this one
+// as well.
+int avio_closep(AVIOContext ** s)
+{
+ int ret = avio_close(*s);
+ *s = NULL;
+ return ret;
+}
+
+
+int avio_open2(AVIOContext ** s, const char *filename, int flags,
+ const AVIOInterruptCB * int_cb, AVDictionary ** options)
+{
+ // The options, if any, are destroyed on entry, so we can add new ones
+ // pretty freely.
+ if (options && *options) {
+ AVDictionaryEntry *listen =
+ av_dict_get(*options, "listen", NULL,
+ AV_DICT_MATCH_CASE);
+ if (listen != NULL && atoi(listen->value) != 0) {
+ // If -listen is set, we'll want to add a header, too.
+ av_dict_set(options, "headers",
+ "Content-encoding: metacube\r\n",
+ AV_DICT_APPEND);
+ }
+ }
+
+ int (*original_func)(AVIOContext ** s, const char *filename,
+ int flags, const AVIOInterruptCB * int_cb,
+ AVDictionary ** options);
+ original_func = dlsym(RTLD_NEXT, "avio_open2");
+ return (*original_func) (s, filename, flags, int_cb, options);
+}
{
// Set back the close-on-exec flag for the socket.
// (This can't leak into a child, since we haven't been started yet.)
- fcntl(sock, F_SETFD, O_CLOEXEC);
+ if (sock != -1) {
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
+ }
pending_data.resize(serialized.pending_data().size());
memcpy(&pending_data[0], serialized.pending_data().data(), serialized.pending_data().size());
log(WARNING, "[%s] Lookup of '%s' failed (%s).",
url.c_str(), host.c_str(), gai_strerror(err));
}
+ freeaddrinfo(ai);
return -1;
}
bool complete = wait_for_activity(sock, POLLIN | POLLOUT, nullptr);
if (should_stop()) {
safe_close(sock);
+ freeaddrinfo(base_ai);
return -1;
}
if (complete) {
// The socket we are downloading on (or -1).
int sock = -1;
- // pid of the cihld process (or -1).
+ // pid of the child process (or -1).
pid_t child_pid = -1;
// Mutex protecting <stats>.
now = time(nullptr);
for (size_t i = 0; i < inputs.size(); ++i) {
InputStats stats = inputs[i]->get_stats();
- fprintf(fp, "%s %llu %llu", stats.url.c_str(),
+ for (const char ch : stats.url) {
+ if (isspace(ch) || !isprint(ch)) {
+ putc('_', fp);
+ } else {
+ putc(ch, fp);
+ }
+ }
+ fprintf(fp, " %llu %llu",
(long long unsigned)(stats.bytes_received),
(long long unsigned)(stats.data_bytes_received));
if (stats.connect_time == -1) {
}
for (const auto &key_and_input_with_refcount : inputs) {
key_and_input_with_refcount.second.input->stop();
+ if (stopped) {
+ key_and_input_with_refcount.second.input->close_socket();
+ }
}
servers->stop();
Server::~Server()
{
safe_close(epoll_fd);
+
+ // We're going to die soon anyway, but clean this up to keep leak checking happy.
+ for (const auto &acceptor_and_context : tls_server_contexts) {
+ tls_destroy_context(acceptor_and_context.second);
+ }
}
vector<ClientStats> Server::get_client_stats() const
void ServerPool::set_unavailable(int stream_index)
{
- assert(stream_index >= 0 && stream_index < ssize_t(num_http_streams));
-
- for (int i = 0; i < num_servers; ++i) {
- servers[i].set_unavailable(stream_index);
+ assert(stream_index >= 0 && stream_index < ssize_t(num_http_streams + udp_streams.size()));
+ if (stream_index < ssize_t(num_http_streams)) {
+ for (int i = 0; i < num_servers; ++i) {
+ servers[i].set_unavailable(stream_index);
+ }
}
}
}
// Set the close-on-exec parameter back on the backlog fd.
- fcntl(data_fd, F_SETFD, O_CLOEXEC);
+ fcntl(data_fd, F_SETFD, FD_CLOEXEC);
for (ssize_t point : serialized.suitable_starting_point()) {
if (point == -1) {
https://github.com/eduardsui/tlse
-It is patched to add and include ktls.h, since glibc does not include
-kTLS definitions yet, and then irrelevant files removed. It is embedded
-(as opposed to a regular link) since TLSe does not provide a static or
-shared library.
+It has had irrelevant files removed, and a dummy ktls.h is added to
+forward to glibc's implementation. It is embedded (as opposed to
+a regular link) since TLSe does not provide a static or shared library.
-/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
-/*
- * Copyright (c) 2016-2017, Mellanox Technologies. All rights reserved.
- *
- * This software is available to you under a choice of one of two
- * licenses. You may choose to be licensed under the terms of the GNU
- * General Public License (GPL) Version 2, available from the file
- * COPYING in the main directory of this source tree, or the
- * OpenIB.org BSD license below:
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
- * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#ifndef _UAPI_LINUX_TLS_H
-#define _UAPI_LINUX_TLS_H
-
-#include <linux/types.h>
-
-/* TLS socket options */
-#define TLS_TX 1 /* Set transmit parameters */
-#define TLS_RX 2 /* Set receive parameters */
-
-/* Supported versions */
-#define TLS_VERSION_MINOR(ver) ((ver) & 0xFF)
-#define TLS_VERSION_MAJOR(ver) (((ver) >> 8) & 0xFF)
-
-#define TLS_VERSION_NUMBER(id) ((((id##_VERSION_MAJOR) & 0xFF) << 8) | \
- ((id##_VERSION_MINOR) & 0xFF))
-
-#define TLS_1_2_VERSION_MAJOR 0x3
-#define TLS_1_2_VERSION_MINOR 0x3
-#define TLS_1_2_VERSION TLS_VERSION_NUMBER(TLS_1_2)
-
-/* Supported ciphers */
-#define TLS_CIPHER_AES_GCM_128 51
-#define TLS_CIPHER_AES_GCM_128_IV_SIZE 8
-#define TLS_CIPHER_AES_GCM_128_KEY_SIZE 16
-#define TLS_CIPHER_AES_GCM_128_SALT_SIZE 4
-#define TLS_CIPHER_AES_GCM_128_TAG_SIZE 16
-#define TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE 8
-
-#define TLS_SET_RECORD_TYPE 1
-
-struct tls_crypto_info {
- __u16 version;
- __u16 cipher_type;
-};
-
-struct tls12_crypto_info_aes_gcm_128 {
- struct tls_crypto_info info;
- unsigned char iv[TLS_CIPHER_AES_GCM_128_IV_SIZE];
- unsigned char key[TLS_CIPHER_AES_GCM_128_KEY_SIZE];
- unsigned char salt[TLS_CIPHER_AES_GCM_128_SALT_SIZE];
- unsigned char rec_seq[TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE];
-};
-
-#define SOL_TLS 282
-#define TCP_ULP 31
-
-#endif /* _UAPI_LINUX_TLS_H */
+#include <linux/tls.h>
{
// Set back the close-on-exec flag for the socket.
// (This can't leak into a child, since we haven't been started yet.)
- fcntl(sock, F_SETFD, O_CLOEXEC);
+ fcntl(sock, F_SETFD, FD_CLOEXEC);
// Should be verified by the caller.
string protocol;
#ifndef _VERSION_H
#define _VERSION_H
-// Version number. Don't expect this to change all that often.
-
-#define SERVER_VERSION "1.4.4-pre"
+#define SERVER_VERSION "1.5.2-pre"
#define SERVER_IDENTIFICATION "Cubemap/" SERVER_VERSION
#endif // !defined(_VERSION_H)