From 539609eb56b496e6eff8a5e0a92fa0325936a5d7 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Mon, 3 Dec 2018 09:32:51 +0100 Subject: [PATCH] Unify httpd.cpp from Nageru and Futatabi (whitespace differences, and metrics support). --- futatabi/httpd.cpp | 264 --------------------------- futatabi/main.cpp | 2 +- futatabi/meson.build | 3 +- futatabi/player.cpp | 2 +- futatabi/video_stream.cpp | 2 +- nageru/defs.h | 8 +- nageru/httpd.h | 115 ------------ nageru/meson.build | 5 +- nageru/mixer.h | 2 +- nageru/video_encoder.cpp | 2 +- {nageru => shared}/httpd.cpp | 14 +- {futatabi => shared}/httpd.h | 6 +- shared/meson.build | 5 +- shared/mux.cpp | 2 +- shared/{mux_opts.h => shared_defs.h} | 12 +- 15 files changed, 32 insertions(+), 412 deletions(-) delete mode 100644 futatabi/httpd.cpp delete mode 100644 nageru/httpd.h rename {nageru => shared}/httpd.cpp (96%) rename {futatabi => shared}/httpd.h (95%) rename shared/{mux_opts.h => shared_defs.h} (62%) diff --git a/futatabi/httpd.cpp b/futatabi/httpd.cpp deleted file mode 100644 index c9399d8..0000000 --- a/futatabi/httpd.cpp +++ /dev/null @@ -1,264 +0,0 @@ -#include "httpd.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -extern "C" { -#include -} - -#include "defs.h" -#include "shared/metacube2.h" - -struct MHD_Connection; -struct MHD_Response; - -using namespace std; - -HTTPD::HTTPD() -{ -} - -HTTPD::~HTTPD() -{ - stop(); -} - -void HTTPD::start(int port) -{ - mhd = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL_INTERNALLY | MHD_USE_DUAL_STACK, - port, - nullptr, nullptr, - &answer_to_connection_thunk, this, - MHD_OPTION_NOTIFY_COMPLETED, nullptr, this, - MHD_OPTION_END); - if (mhd == nullptr) { - fprintf(stderr, "Warning: Could not open HTTP server. (Port already in use?)\n"); - } -} - -void HTTPD::stop() -{ - if (mhd) { - MHD_quiesce_daemon(mhd); - for (Stream *stream : streams) { - stream->stop(); - } - MHD_stop_daemon(mhd); - mhd = nullptr; - } -} - -void HTTPD::add_data(const char *buf, size_t size, bool keyframe, int64_t time, AVRational timebase) -{ - unique_lock lock(streams_mutex); - for (Stream *stream : streams) { - stream->add_data(buf, size, keyframe ? Stream::DATA_TYPE_KEYFRAME : Stream::DATA_TYPE_OTHER, time, timebase); - } -} - -int HTTPD::answer_to_connection_thunk(void *cls, MHD_Connection *connection, - const char *url, const char *method, - const char *version, const char *upload_data, - size_t *upload_data_size, void **con_cls) -{ - HTTPD *httpd = (HTTPD *)cls; - return httpd->answer_to_connection(connection, url, method, version, upload_data, upload_data_size, con_cls); -} - -int HTTPD::answer_to_connection(MHD_Connection *connection, - const char *url, const char *method, - const char *version, const char *upload_data, - size_t *upload_data_size, void **con_cls) -{ - // See if the URL ends in “.metacube”. - HTTPD::Stream::Framing framing; - if (strstr(url, ".metacube") == url + strlen(url) - strlen(".metacube")) { - framing = HTTPD::Stream::FRAMING_METACUBE; - } else { - framing = HTTPD::Stream::FRAMING_RAW; - } - - if (endpoints.count(url)) { - pair contents_and_type = endpoints[url].callback(); - MHD_Response *response = MHD_create_response_from_buffer( - contents_and_type.first.size(), &contents_and_type.first[0], MHD_RESPMEM_MUST_COPY); - MHD_add_response_header(response, "Content-type", contents_and_type.second.c_str()); - if (endpoints[url].cors_policy == ALLOW_ALL_ORIGINS) { - MHD_add_response_header(response, "Access-Control-Allow-Origin", "*"); - } - int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. - return ret; - } - - // Small hack; reject unknown /channels/foo. - if (string(url).find("/channels/") == 0) { - string contents = "Not found."; - MHD_Response *response = MHD_create_response_from_buffer( - contents.size(), &contents[0], MHD_RESPMEM_MUST_COPY); - MHD_add_response_header(response, "Content-type", "text/plain"); - int ret = MHD_queue_response(connection, MHD_HTTP_NOT_FOUND, response); - MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. - return ret; - } - - HTTPD::Stream *stream = new HTTPD::Stream(this, framing); - stream->add_data(header.data(), header.size(), Stream::DATA_TYPE_HEADER, AV_NOPTS_VALUE, AVRational{ 1, 0 }); - { - unique_lock lock(streams_mutex); - streams.insert(stream); - } - ++metric_num_connected_clients; - *con_cls = stream; - - // Does not strictly have to be equal to MUX_BUFFER_SIZE. - MHD_Response *response = MHD_create_response_from_callback( - (size_t)-1, MUX_BUFFER_SIZE, &HTTPD::Stream::reader_callback_thunk, stream, &HTTPD::free_stream); - // TODO: Content-type? - if (framing == HTTPD::Stream::FRAMING_METACUBE) { - MHD_add_response_header(response, "Content-encoding", "metacube"); - } - - int ret = MHD_queue_response(connection, MHD_HTTP_OK, response); - MHD_destroy_response(response); // Only decreases the refcount; actual free is after the request is done. - - return ret; -} - -void HTTPD::free_stream(void *cls) -{ - HTTPD::Stream *stream = (HTTPD::Stream *)cls; - HTTPD *httpd = stream->get_parent(); - { - unique_lock lock(httpd->streams_mutex); - delete stream; - httpd->streams.erase(stream); - } - --httpd->metric_num_connected_clients; -} - -ssize_t HTTPD::Stream::reader_callback_thunk(void *cls, uint64_t pos, char *buf, size_t max) -{ - HTTPD::Stream *stream = (HTTPD::Stream *)cls; - return stream->reader_callback(pos, buf, max); -} - -ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max) -{ - unique_lock lock(buffer_mutex); - has_buffered_data.wait(lock, [this] { return should_quit || !buffered_data.empty(); }); - if (should_quit) { - return 0; - } - - ssize_t ret = 0; - while (max > 0 && !buffered_data.empty()) { - const string &s = buffered_data.front(); - assert(s.size() > used_of_buffered_data); - size_t len = s.size() - used_of_buffered_data; - if (max >= len) { - // Consume the entire (rest of the) string. - memcpy(buf, s.data() + used_of_buffered_data, len); - buf += len; - ret += len; - max -= len; - buffered_data.pop_front(); - used_of_buffered_data = 0; - } else { - // We don't need the entire string; just use the first part of it. - memcpy(buf, s.data() + used_of_buffered_data, max); - buf += max; - used_of_buffered_data += max; - ret += max; - max = 0; - } - } - - return ret; -} - -void HTTPD::Stream::add_data(const char *buf, size_t buf_size, HTTPD::Stream::DataType data_type, int64_t time, AVRational timebase) -{ - if (buf_size == 0) { - return; - } - if (data_type == DATA_TYPE_KEYFRAME) { - seen_keyframe = true; - } else if (data_type == DATA_TYPE_OTHER && !seen_keyframe) { - // Start sending only once we see a keyframe. - return; - } - - unique_lock lock(buffer_mutex); - - if (framing == FRAMING_METACUBE) { - int flags = 0; - if (data_type == DATA_TYPE_HEADER) { - flags |= METACUBE_FLAGS_HEADER; - } else if (data_type == DATA_TYPE_OTHER) { - flags |= METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START; - } - - // If we're about to send a keyframe, send a pts metadata block - // to mark its time. - if ((flags & METACUBE_FLAGS_NOT_SUITABLE_FOR_STREAM_START) == 0 && time != AV_NOPTS_VALUE) { - metacube2_pts_packet packet; - packet.type = htobe64(METACUBE_METADATA_TYPE_NEXT_BLOCK_PTS); - packet.pts = htobe64(time); - packet.timebase_num = htobe64(timebase.num); - packet.timebase_den = htobe64(timebase.den); - - metacube2_block_header hdr; - memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); - hdr.size = htonl(sizeof(packet)); - hdr.flags = htons(METACUBE_FLAGS_METADATA); - hdr.csum = htons(metacube2_compute_crc(&hdr)); - buffered_data.emplace_back((char *)&hdr, sizeof(hdr)); - buffered_data.emplace_back((char *)&packet, sizeof(packet)); - } - - metacube2_block_header hdr; - memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); - hdr.size = htonl(buf_size); - hdr.flags = htons(flags); - hdr.csum = htons(metacube2_compute_crc(&hdr)); - buffered_data.emplace_back((char *)&hdr, sizeof(hdr)); - } - buffered_data.emplace_back(buf, buf_size); - - // Send a Metacube2 timestamp every keyframe. - if (framing == FRAMING_METACUBE && data_type == DATA_TYPE_KEYFRAME) { - timespec now; - clock_gettime(CLOCK_REALTIME, &now); - - metacube2_timestamp_packet packet; - packet.type = htobe64(METACUBE_METADATA_TYPE_ENCODER_TIMESTAMP); - packet.tv_sec = htobe64(now.tv_sec); - packet.tv_nsec = htobe64(now.tv_nsec); - - metacube2_block_header hdr; - memcpy(hdr.sync, METACUBE2_SYNC, sizeof(hdr.sync)); - hdr.size = htonl(sizeof(packet)); - hdr.flags = htons(METACUBE_FLAGS_METADATA); - hdr.csum = htons(metacube2_compute_crc(&hdr)); - buffered_data.emplace_back((char *)&hdr, sizeof(hdr)); - buffered_data.emplace_back((char *)&packet, sizeof(packet)); - } - - has_buffered_data.notify_all(); -} - -void HTTPD::Stream::stop() -{ - unique_lock lock(buffer_mutex); - should_quit = true; - has_buffered_data.notify_all(); -} diff --git a/futatabi/main.cpp b/futatabi/main.cpp index b885c81..46fb200 100644 --- a/futatabi/main.cpp +++ b/futatabi/main.cpp @@ -27,7 +27,7 @@ extern "C" { #include "flags.h" #include "frame_on_disk.h" #include "frame.pb.h" -#include "httpd.h" +#include "shared/httpd.h" #include "mainwindow.h" #include "player.h" #include "shared/post_to_main_thread.h" diff --git a/futatabi/meson.build b/futatabi/meson.build index 3f82d8d..aa58291 100644 --- a/futatabi/meson.build +++ b/futatabi/meson.build @@ -6,7 +6,6 @@ libavcodecdep = dependency('libavcodec') libavformatdep = dependency('libavformat') libavutildep = dependency('libavutil') libjpegdep = dependency('libjpeg') -libmicrohttpddep = dependency('libmicrohttpd') libswscaledep = dependency('libswscale') movitdep = dependency('movit') protobufdep = dependency('protobuf') @@ -34,7 +33,7 @@ moc_files = qt5.preprocess( srcs = ['flow.cpp', 'gpu_timers.cpp'] # All the other files. -srcs += ['main.cpp', 'player.cpp', 'httpd.cpp', 'video_stream.cpp', 'chroma_subsampler.cpp'] +srcs += ['main.cpp', 'player.cpp', 'video_stream.cpp', 'chroma_subsampler.cpp'] srcs += ['vaapi_jpeg_decoder.cpp', 'db.cpp', 'disk_space_estimator.cpp', 'ycbcr_converter.cpp', 'flags.cpp'] srcs += ['mainwindow.cpp', 'jpeg_frame_view.cpp', 'clip_list.cpp', 'frame_on_disk.cpp'] srcs += moc_files diff --git a/futatabi/player.cpp b/futatabi/player.cpp index 816f72d..b88aa56 100644 --- a/futatabi/player.cpp +++ b/futatabi/player.cpp @@ -5,7 +5,7 @@ #include "defs.h" #include "shared/ffmpeg_raii.h" #include "frame_on_disk.h" -#include "httpd.h" +#include "shared/httpd.h" #include "jpeg_frame_view.h" #include "shared/mux.h" #include "shared/timebase.h" diff --git a/futatabi/video_stream.cpp b/futatabi/video_stream.cpp index 52612e2..8738dc5 100644 --- a/futatabi/video_stream.cpp +++ b/futatabi/video_stream.cpp @@ -9,7 +9,7 @@ extern "C" { #include "shared/context.h" #include "flags.h" #include "flow.h" -#include "httpd.h" +#include "shared/httpd.h" #include "jpeg_frame_view.h" #include "movit/util.h" #include "shared/mux.h" diff --git a/nageru/defs.h b/nageru/defs.h index ae07be0..a990330 100644 --- a/nageru/defs.h +++ b/nageru/defs.h @@ -22,13 +22,7 @@ #define DEFAULT_STREAM_MUX_NAME "nut" // Only for HTTP. Local dump guesses from LOCAL_DUMP_SUFFIX. #define DEFAULT_HTTPD_PORT 9095 -#include "shared/mux_opts.h" - -// In bytes. Beware, if too small, stream clients will start dropping data. -// For mov, you want this at 10MB or so (for the reason mentioned above), -// but for nut, there's no flushing, so such a large mux buffer would cause -// the output to be very uneven. -#define MUX_BUFFER_SIZE 10485760 +#include "shared/shared_defs.h" // In number of frames. Comes in addition to any internal queues in x264 // (frame threading, lookahead, etc.). diff --git a/nageru/httpd.h b/nageru/httpd.h deleted file mode 100644 index 57c649b..0000000 --- a/nageru/httpd.h +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef _HTTPD_H -#define _HTTPD_H - -// A class dealing with stream output to HTTP. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern "C" { -#include -} - -struct MHD_Connection; -struct MHD_Daemon; - -class HTTPD { -public: - // Returns a pair of content and content-type. - using EndpointCallback = std::function()>; - - HTTPD(); - ~HTTPD(); - - // Should be called before start(). - void set_header(const std::string &data) { - header = data; - } - - // Should be called before start() (due to threading issues). - enum CORSPolicy { - NO_CORS_POLICY, - ALLOW_ALL_ORIGINS - }; - void add_endpoint(const std::string &url, const EndpointCallback &callback, CORSPolicy cors_policy) { - endpoints[url] = Endpoint{ callback, cors_policy }; - } - - void start(int port); - void stop(); - void add_data(const char *buf, size_t size, bool keyframe, int64_t time, AVRational timebase); - int64_t get_num_connected_clients() const { - return metric_num_connected_clients.load(); - } - -private: - static int answer_to_connection_thunk(void *cls, MHD_Connection *connection, - const char *url, const char *method, - const char *version, const char *upload_data, - size_t *upload_data_size, void **con_cls); - - int answer_to_connection(MHD_Connection *connection, - const char *url, const char *method, - const char *version, const char *upload_data, - size_t *upload_data_size, void **con_cls); - - static void free_stream(void *cls); - - - class Stream { - public: - enum Framing { - FRAMING_RAW, - FRAMING_METACUBE - }; - Stream(HTTPD *parent, Framing framing) : parent(parent), framing(framing) {} - - static ssize_t reader_callback_thunk(void *cls, uint64_t pos, char *buf, size_t max); - ssize_t reader_callback(uint64_t pos, char *buf, size_t max); - - enum DataType { - DATA_TYPE_HEADER, - DATA_TYPE_KEYFRAME, - DATA_TYPE_OTHER - }; - void add_data(const char *buf, size_t size, DataType data_type, int64_t time, AVRational timebase); - void stop(); - HTTPD *get_parent() const { return parent; } - - private: - HTTPD *parent; - Framing framing; - - std::mutex buffer_mutex; - bool should_quit = false; // Under . - std::condition_variable has_buffered_data; - std::deque buffered_data; // Protected by . - size_t used_of_buffered_data = 0; // How many bytes of the first element of that is already used. Protected by . - size_t seen_keyframe = false; - }; - - MHD_Daemon *mhd = nullptr; - std::mutex streams_mutex; - std::set streams; // Not owned. - struct Endpoint { - EndpointCallback callback; - CORSPolicy cors_policy; - }; - std::unordered_map endpoints; - std::string header; - - // Metrics. - std::atomic metric_num_connected_clients{0}; -}; - -#endif // !defined(_HTTPD_H) diff --git a/nageru/meson.build b/nageru/meson.build index 1fb60e6..737d889 100644 --- a/nageru/meson.build +++ b/nageru/meson.build @@ -13,7 +13,6 @@ libavformatdep = dependency('libavformat') libavresampledep = dependency('libavresample') libavutildep = dependency('libavutil') libjpegdep = dependency('libjpeg') -libmicrohttpddep = dependency('libmicrohttpd') libswscaledep = dependency('libswscale') libusbdep = dependency('libusb-1.0') luajitdep = dependency('luajit') @@ -29,7 +28,7 @@ x264dep = dependency('x264') zitaresamplerdep = cxx.find_library('zita-resampler') srcs = [] -nageru_deps = [shareddep, qt5deps, libjpegdep, movitdep, libmicrohttpddep, protobufdep, +nageru_deps = [shareddep, qt5deps, libjpegdep, movitdep, protobufdep, vax11dep, vadrmdep, x11dep, libavformatdep, libavresampledep, libavcodecdep, libavutildep, libswscaledep, libusbdep, luajitdep, dldep, x264dep, alsadep, zitaresamplerdep, qcustomplotdep, threaddep] @@ -166,7 +165,7 @@ srcs += ['chroma_subsampler.cpp', 'v210_converter.cpp', 'mixer.cpp', 'pbo_frame_ # Streaming and encoding objects (largely the set that is shared between Nageru and Kaeru). stream_srcs = ['quicksync_encoder.cpp', 'x264_encoder.cpp', 'x264_dynamic.cpp', 'x264_speed_control.cpp', 'video_encoder.cpp', - 'audio_encoder.cpp', 'ffmpeg_util.cpp', 'httpd.cpp', 'ffmpeg_capture.cpp', + 'audio_encoder.cpp', 'ffmpeg_util.cpp', 'ffmpeg_capture.cpp', 'print_latency.cpp', 'basic_stats.cpp', 'ref_counted_frame.cpp'] stream = static_library('stream', stream_srcs, dependencies: nageru_deps, include_directories: nageru_include_dirs) nageru_link_with += stream diff --git a/nageru/mixer.h b/nageru/mixer.h index 2c39449..3ed6c5f 100644 --- a/nageru/mixer.h +++ b/nageru/mixer.h @@ -28,7 +28,7 @@ #include "audio_mixer.h" #include "bmusb/bmusb.h" #include "defs.h" -#include "httpd.h" +#include "shared/httpd.h" #include "input_state.h" #include "libusb.h" #include "pbo_frame_allocator.h" diff --git a/nageru/video_encoder.cpp b/nageru/video_encoder.cpp index 5f5b975..6749f35 100644 --- a/nageru/video_encoder.cpp +++ b/nageru/video_encoder.cpp @@ -15,7 +15,7 @@ extern "C" { #include "defs.h" #include "shared/ffmpeg_raii.h" #include "flags.h" -#include "httpd.h" +#include "shared/httpd.h" #include "shared/mux.h" #include "quicksync_encoder.h" #include "shared/timebase.h" diff --git a/nageru/httpd.cpp b/shared/httpd.cpp similarity index 96% rename from nageru/httpd.cpp rename to shared/httpd.cpp index eebf6eb..28ed783 100644 --- a/nageru/httpd.cpp +++ b/shared/httpd.cpp @@ -1,20 +1,20 @@ -#include "httpd.h" +#include "shared/httpd.h" #include #include #include +#include #include #include #include #include #include #include -#include extern "C" { #include } -#include "defs.h" +#include "shared/shared_defs.h" #include "shared/metacube2.h" #include "shared/metrics.h" @@ -77,8 +77,8 @@ int HTTPD::answer_to_connection_thunk(void *cls, MHD_Connection *connection, int HTTPD::answer_to_connection(MHD_Connection *connection, const char *url, const char *method, - const char *version, const char *upload_data, - size_t *upload_data_size, void **con_cls) + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) { // See if the URL ends in “.metacube”. HTTPD::Stream::Framing framing; @@ -165,7 +165,7 @@ ssize_t HTTPD::Stream::reader_callback_thunk(void *cls, uint64_t pos, char *buf, ssize_t HTTPD::Stream::reader_callback(uint64_t pos, char *buf, size_t max) { unique_lock lock(buffer_mutex); - has_buffered_data.wait(lock, [this]{ return should_quit || !buffered_data.empty(); }); + has_buffered_data.wait(lock, [this] { return should_quit || !buffered_data.empty(); }); if (should_quit) { return 0; } @@ -264,7 +264,7 @@ void HTTPD::Stream::add_data(const char *buf, size_t buf_size, HTTPD::Stream::Da buffered_data.emplace_back((char *)&packet, sizeof(packet)); } - has_buffered_data.notify_all(); + has_buffered_data.notify_all(); } void HTTPD::Stream::stop() diff --git a/futatabi/httpd.h b/shared/httpd.h similarity index 95% rename from futatabi/httpd.h rename to shared/httpd.h index 9901814..5b2b266 100644 --- a/futatabi/httpd.h +++ b/shared/httpd.h @@ -68,14 +68,14 @@ private: static void free_stream(void *cls); - class Stream { public: enum Framing { FRAMING_RAW, FRAMING_METACUBE }; - Stream(HTTPD *parent, Framing framing) : parent(parent), framing(framing) {} + Stream(HTTPD *parent, Framing framing) + : parent(parent), framing(framing) {} static ssize_t reader_callback_thunk(void *cls, uint64_t pos, char *buf, size_t max); ssize_t reader_callback(uint64_t pos, char *buf, size_t max); @@ -112,7 +112,7 @@ private: std::string header; // Metrics. - std::atomic metric_num_connected_clients{0}; + std::atomic metric_num_connected_clients{ 0 }; }; #endif // !defined(_HTTPD_H) diff --git a/shared/meson.build b/shared/meson.build index 8d1b203..1bb183b 100644 --- a/shared/meson.build +++ b/shared/meson.build @@ -1,8 +1,9 @@ qt5 = import('qt5') qt5deps = dependency('qt5', modules: ['OpenGL']) +libmicrohttpddep = dependency('libmicrohttpd') -srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp', 'context.cpp'] -shared = static_library('shared', srcs, include_directories: top_include, dependencies: qt5deps) +srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp', 'context.cpp', 'httpd.cpp'] +shared = static_library('shared', srcs, include_directories: top_include, dependencies: [qt5deps, libmicrohttpddep]) shareddep = declare_dependency( include_directories: top_include, link_with: shared) diff --git a/shared/mux.cpp b/shared/mux.cpp index da618e0..4970bce 100644 --- a/shared/mux.cpp +++ b/shared/mux.cpp @@ -22,7 +22,7 @@ extern "C" { } #include "shared/metrics.h" -#include "shared/mux_opts.h" +#include "shared/shared_defs.h" #include "shared/timebase.h" using namespace std; diff --git a/shared/mux_opts.h b/shared/shared_defs.h similarity index 62% rename from shared/mux_opts.h rename to shared/shared_defs.h index 0315fb8..fc3daa1 100644 --- a/shared/mux_opts.h +++ b/shared/shared_defs.h @@ -1,5 +1,5 @@ -#ifndef _MUX_OPTS_H -#define _MUX_OPTS_H 1 +#ifndef _SHARED_DEFS_H +#define _SHARED_DEFS_H 1 // This flag is only supported in FFmpeg 3.3 and up, and we only require 3.1. #if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 71, 100) @@ -19,4 +19,10 @@ { "write_index", "0" } \ } -#endif // !defined(_MUX_OPTS_H) +// In bytes. Beware, if too small, stream clients will start dropping data. +// For mov, you want this at 10MB or so (for the reason mentioned above), +// but for nut, there's no flushing, so such a large mux buffer would cause +// the output to be very uneven. +#define MUX_BUFFER_SIZE 10485760 + +#endif // !defined(_SHARED_DEFS_H) -- 2.39.2