]> git.sesse.net Git - nageru/commitdiff
Unify mux.cpp between Nageru and Futatabi.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 2 Dec 2018 16:24:43 +0000 (17:24 +0100)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sun, 2 Dec 2018 16:25:52 +0000 (17:25 +0100)
30 files changed:
futatabi/meson.build
futatabi/mux.cpp [deleted file]
futatabi/player.cpp
futatabi/video_stream.cpp
nageru/audio_encoder.cpp
nageru/audio_mixer.cpp
nageru/basic_stats.cpp
nageru/decklink_output.cpp
nageru/defs.h
nageru/disk_space_estimator.cpp
nageru/httpd.cpp
nageru/kaeru.cpp
nageru/meson.build
nageru/mixer.cpp
nageru/mux.h [deleted file]
nageru/print_latency.cpp
nageru/print_latency.h
nageru/quicksync_encoder.cpp
nageru/video_encoder.cpp
nageru/video_encoder.h
nageru/x264_encoder.cpp
nageru/x264_encoder.h
nageru/x264_speed_control.cpp
nageru/x264_speed_control.h
shared/meson.build
shared/metrics.cpp [moved from nageru/metrics.cpp with 99% similarity]
shared/metrics.h [moved from nageru/metrics.h with 100% similarity]
shared/mux.cpp [moved from nageru/mux.cpp with 88% similarity]
shared/mux.h [moved from futatabi/mux.h with 88% similarity]
shared/mux_opts.h [new file with mode: 0644]

index d9ec0c3d6868849281e864d903096b08948f3a8c..04e003a34d944978a0a0b2964f731e515d278fbd 100644 (file)
@@ -34,7 +34,7 @@ moc_files = qt5.preprocess(
 srcs = ['flow.cpp', 'gpu_timers.cpp']
 
 # All the other files.
-srcs += ['main.cpp', 'player.cpp', 'httpd.cpp', 'mux.cpp', 'video_stream.cpp', 'context.cpp', 'chroma_subsampler.cpp']
+srcs += ['main.cpp', 'player.cpp', 'httpd.cpp', 'video_stream.cpp', 'context.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/mux.cpp b/futatabi/mux.cpp
deleted file mode 100644 (file)
index 34a2bdd..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-#include "mux.h"
-
-#include <algorithm>
-#include <assert.h>
-#include <mutex>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-extern "C" {
-#include <libavformat/avio.h>
-#include <libavutil/avutil.h>
-#include <libavutil/dict.h>
-#include <libavutil/mathematics.h>
-#include <libavutil/mem.h>
-#include <libavutil/pixfmt.h>
-#include <libavutil/rational.h>
-}
-
-#include "defs.h"
-#include "shared/timebase.h"
-
-using namespace std;
-
-struct PacketBefore {
-       PacketBefore(const AVFormatContext *ctx) : ctx(ctx) {}
-
-       bool operator() (const Mux::QueuedPacket &a_qp, const Mux::QueuedPacket &b_qp) const {
-               const AVPacket *a = a_qp.pkt;
-               const AVPacket *b = b_qp.pkt;
-               int64_t a_dts = (a->dts == AV_NOPTS_VALUE ? a->pts : a->dts);
-               int64_t b_dts = (b->dts == AV_NOPTS_VALUE ? b->pts : b->dts);
-               AVRational a_timebase = ctx->streams[a->stream_index]->time_base;
-               AVRational b_timebase = ctx->streams[b->stream_index]->time_base;
-               if (av_compare_ts(a_dts, a_timebase, b_dts, b_timebase) != 0) {
-                       return av_compare_ts(a_dts, a_timebase, b_dts, b_timebase) < 0;
-               } else {
-                       return av_compare_ts(a->pts, a_timebase, b->pts, b_timebase) < 0;
-               }
-       }
-
-       const AVFormatContext * const ctx;
-};
-
-Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const string &video_extradata, const AVCodecParameters *audio_codecpar, int time_base, std::function<void(int64_t)> write_callback, WriteStrategy write_strategy, const vector<MuxMetrics *> &metrics)
-       : write_strategy(write_strategy), avctx(avctx), write_callback(write_callback), metrics(metrics)
-{
-       avstream_video = avformat_new_stream(avctx, nullptr);
-       if (avstream_video == nullptr) {
-               fprintf(stderr, "avformat_new_stream() failed\n");
-               exit(1);
-       }
-       avstream_video->time_base = AVRational{1, time_base};
-       avstream_video->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
-       if (video_codec == CODEC_H264) {
-               avstream_video->codecpar->codec_id = AV_CODEC_ID_H264;
-       } else if (video_codec == CODEC_NV12) {
-               avstream_video->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
-               avstream_video->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag(AV_PIX_FMT_NV12);
-       } else {
-               assert(video_codec == CODEC_MJPEG);
-               avstream_video->codecpar->codec_id = AV_CODEC_ID_MJPEG;
-       }
-       avstream_video->codecpar->width = width;
-       avstream_video->codecpar->height = height;
-
-       // Colorspace details. Closely correspond to settings in EffectChain_finalize,
-       // as noted in each comment.
-       // Note that the H.264 stream also contains this information and depending on the
-       // mux, this might simply get ignored. See sps_rbsp().
-       // Note that there's no way to change this per-frame as the H.264 stream
-       // would like to be able to.
-       avstream_video->codecpar->color_primaries = AVCOL_PRI_BT709;  // RGB colorspace (inout_format.color_space).
-       avstream_video->codecpar->color_trc = AVCOL_TRC_IEC61966_2_1;  // Gamma curve (inout_format.gamma_curve).
-       // YUV colorspace (output_ycbcr_format.luma_coefficients).
-       avstream_video->codecpar->color_space = AVCOL_SPC_BT709;
-       avstream_video->codecpar->color_range = AVCOL_RANGE_MPEG;  // Full vs. limited range (output_ycbcr_format.full_range).
-       avstream_video->codecpar->chroma_location = AVCHROMA_LOC_LEFT;  // Chroma sample location. See chroma_offset_0[] in Mixer::subsample_chroma().
-       avstream_video->codecpar->field_order = AV_FIELD_PROGRESSIVE;
-
-       if (!video_extradata.empty()) {
-               avstream_video->codecpar->extradata = (uint8_t *)av_malloc(video_extradata.size());
-               avstream_video->codecpar->extradata_size = video_extradata.size();
-               memcpy(avstream_video->codecpar->extradata, video_extradata.data(), video_extradata.size());
-       }
-
-       avstream_audio = nullptr;
-#if 0
-       avstream_audio = avformat_new_stream(avctx, nullptr);
-       if (avstream_audio == nullptr) {
-               fprintf(stderr, "avformat_new_stream() failed\n");
-               exit(1);
-       }
-       avstream_audio->time_base = AVRational{1, time_base};
-       if (avcodec_parameters_copy(avstream_audio->codecpar, audio_codecpar) < 0) {
-               fprintf(stderr, "avcodec_parameters_copy() failed\n");
-               exit(1);
-       }
-#endif
-
-       AVDictionary *options = NULL;
-       vector<pair<string, string>> opts = MUX_OPTS;
-       for (pair<string, string> opt : opts) {
-               av_dict_set(&options, opt.first.c_str(), opt.second.c_str(), 0);
-       }
-       if (avformat_write_header(avctx, &options) < 0) {
-               fprintf(stderr, "avformat_write_header() failed\n");
-               exit(1);
-       }
-       for (MuxMetrics *metric : metrics) {
-               metric->metric_written_bytes += avctx->pb->pos;
-       }
-
-       // Make sure the header is written before the constructor exits.
-       avio_flush(avctx->pb);
-
-       if (write_strategy == WRITE_BACKGROUND) {
-               writer_thread = thread(&Mux::thread_func, this);
-       }
-}
-
-Mux::~Mux()
-{
-       assert(plug_count == 0);
-       if (write_strategy == WRITE_BACKGROUND) {
-               writer_thread_should_quit = true;
-               packet_queue_ready.notify_all();
-               writer_thread.join();
-       }
-       int64_t old_pos = avctx->pb->pos;
-       av_write_trailer(avctx);
-       for (MuxMetrics *metric : metrics) {
-               metric->metric_written_bytes += avctx->pb->pos - old_pos;
-       }
-
-       if (!(avctx->oformat->flags & AVFMT_NOFILE) &&
-           !(avctx->flags & AVFMT_FLAG_CUSTOM_IO)) {
-               avio_closep(&avctx->pb);
-       }
-       avformat_free_context(avctx);
-}
-
-void Mux::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, AVRational timebase, int stream_index_override)
-{
-       AVPacket pkt_copy;
-       av_init_packet(&pkt_copy);
-       if (av_packet_ref(&pkt_copy, &pkt) < 0) {
-               fprintf(stderr, "av_copy_packet() failed\n");
-               exit(1);
-       }
-       if (stream_index_override != -1) {
-               pkt_copy.stream_index = stream_index_override;
-       }
-       if (pkt_copy.stream_index == 0) {
-               pkt_copy.pts = av_rescale_q(pts, timebase, avstream_video->time_base);
-               pkt_copy.dts = av_rescale_q(dts, timebase, avstream_video->time_base);
-               pkt_copy.duration = av_rescale_q(pkt.duration, timebase, avstream_video->time_base);
-       } else if (pkt_copy.stream_index == 1) {
-               pkt_copy.pts = av_rescale_q(pts, timebase, avstream_audio->time_base);
-               pkt_copy.dts = av_rescale_q(dts, timebase, avstream_audio->time_base);
-               pkt_copy.duration = av_rescale_q(pkt.duration, timebase, avstream_audio->time_base);
-       } else {
-               assert(false);
-       }
-
-       {
-               lock_guard<mutex> lock(mu);
-               if (write_strategy == WriteStrategy::WRITE_BACKGROUND) {
-                       packet_queue.push_back(QueuedPacket{ av_packet_clone(&pkt_copy), pts });
-                       if (plug_count == 0)
-                               packet_queue_ready.notify_all();
-               } else if (plug_count > 0) {
-                       packet_queue.push_back(QueuedPacket{ av_packet_clone(&pkt_copy), pts });
-               } else {
-                       write_packet_or_die(pkt_copy, pts);
-               }
-       }
-
-       av_packet_unref(&pkt_copy);
-}
-
-void Mux::write_packet_or_die(const AVPacket &pkt, int64_t unscaled_pts)
-{
-       for (MuxMetrics *metric : metrics) {
-               if (pkt.stream_index == 0) {
-                       metric->metric_video_bytes += pkt.size;
-               } else if (pkt.stream_index == 1) {
-                       metric->metric_audio_bytes += pkt.size;
-               } else {
-                       assert(false);
-               }
-       }
-       int64_t old_pos = avctx->pb->pos;
-       if (av_interleaved_write_frame(avctx, const_cast<AVPacket *>(&pkt)) < 0) {
-               fprintf(stderr, "av_interleaved_write_frame() failed\n");
-               abort();
-       }
-       avio_flush(avctx->pb);
-       for (MuxMetrics *metric : metrics) {
-               metric->metric_written_bytes += avctx->pb->pos - old_pos;
-       }
-
-       if (pkt.stream_index == 0 && write_callback != nullptr) {
-               write_callback(unscaled_pts);
-       }
-}
-
-void Mux::plug()
-{
-       lock_guard<mutex> lock(mu);
-       ++plug_count;
-}
-
-void Mux::unplug()
-{
-       lock_guard<mutex> lock(mu);
-       if (--plug_count > 0) {
-               return;
-       }
-       assert(plug_count >= 0);
-
-       sort(packet_queue.begin(), packet_queue.end(), PacketBefore(avctx));
-
-       if (write_strategy == WRITE_BACKGROUND) {
-               packet_queue_ready.notify_all();
-       } else {
-               for (QueuedPacket &qp : packet_queue) {
-                       write_packet_or_die(*qp.pkt, qp.unscaled_pts);
-                       av_packet_free(&qp.pkt);
-               }
-               packet_queue.clear();
-       }
-}
-
-void Mux::thread_func()
-{
-       pthread_setname_np(pthread_self(), "Mux");
-
-       unique_lock<mutex> lock(mu);
-       for ( ;; ) {
-               packet_queue_ready.wait(lock, [this]() {
-                       return writer_thread_should_quit || (!packet_queue.empty() && plug_count == 0);
-               });
-               if (writer_thread_should_quit && packet_queue.empty()) {
-                       // All done.
-                       break;
-               }
-
-               assert(!packet_queue.empty() && plug_count == 0);
-               vector<QueuedPacket> packets;
-               swap(packets, packet_queue);
-
-               lock.unlock();
-               for (QueuedPacket &qp : packets) {
-                       write_packet_or_die(*qp.pkt, qp.unscaled_pts);
-                       av_packet_free(&qp.pkt);
-               }
-               lock.lock();
-       }
-}
-
-void MuxMetrics::init(const vector<pair<string, string>> &labels)
-{
-       // TODO: See if we want to reintroduce these.
-}
index 19b04826138d39cdb3101f43cfed1ca91e2a0c6c..99edd865ed95f97b73880b8dac63076442ae02d9 100644 (file)
@@ -7,7 +7,7 @@
 #include "frame_on_disk.h"
 #include "httpd.h"
 #include "jpeg_frame_view.h"
-#include "mux.h"
+#include "shared/mux.h"
 #include "shared/timebase.h"
 #include "video_stream.h"
 
index d425ed190b6fa3187a6f3311c3c980d7ff48c031..3df272ac08b5836b6f49c3277bff6de5504c3ef6 100644 (file)
@@ -12,7 +12,7 @@ extern "C" {
 #include "httpd.h"
 #include "jpeg_frame_view.h"
 #include "movit/util.h"
-#include "mux.h"
+#include "shared/mux.h"
 #include "player.h"
 #include "util.h"
 #include "ycbcr_converter.h"
@@ -248,8 +248,9 @@ void VideoStream::start()
        string video_extradata;
 
        constexpr int width = 1280, height = 720;  // Doesn't matter for MJPEG.
-       stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, /*audio_codec_parameters=*/nullptr, COARSE_TIMEBASE,
-               /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {}));
+       stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, /*audio_codec_parameters=*/nullptr,
+               AVCOL_SPC_BT709, Mux::WITHOUT_AUDIO,
+               COARSE_TIMEBASE, /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, {}));
 
 
        encode_thread = thread(&VideoStream::encode_thread_func, this);
index daeb8a81b72eb2e7a13a8f6a7b3c9833003d9805..f9faf42c349fdd926eb9f54263eb2f04d81240c7 100644 (file)
@@ -22,7 +22,7 @@ extern "C" {
 #include <vector>
 
 #include "defs.h"
-#include "mux.h"
+#include "shared/mux.h"
 #include "shared/timebase.h"
 
 using namespace std;
index 193ae6fdaf7bcfa641a6f4188448c407d300a7ad..7bb1ddd8e4cb9b1a3d02deeca9c401e115660373 100644 (file)
@@ -20,7 +20,7 @@
 
 #include "db.h"
 #include "flags.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "state.pb.h"
 #include "shared/timebase.h"
 
index 937e3021c5cdf1188a23c4b086d90e894bb75b01..9e07a1c417bfce08d64e703c7b1d0f127bd48199 100644 (file)
@@ -1,5 +1,5 @@
 #include "basic_stats.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 
 #include <assert.h>
 #include <sys/resource.h>
index 8d7c47f34a1000fb066262d764aa702a2498aaa5..bd59b32a5a3abb0eb762c89ed49fcc8991bc75fc 100644 (file)
@@ -12,7 +12,7 @@
 #include "decklink_output.h"
 #include "decklink_util.h"
 #include "flags.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "print_latency.h"
 #include "shared/timebase.h"
 #include "v210_converter.h"
index 7b8cc696de1120de66ce04a1b2d29138089d024c..ae07be004ff16de3120c732466b7323232fdd4a3 100644 (file)
@@ -3,13 +3,6 @@
 
 #include <libavformat/version.h>
 
-// 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)
-#define MUX_SKIP_TRAILER "+skip_trailer"
-#else
-#define MUX_SKIP_TRAILER ""
-#endif
-
 #define OUTPUT_FREQUENCY 48000  // Currently needs to be exactly 48000, since bmusb outputs in that.
 #define MAX_FPS 60
 #define FAKE_FPS 25  // Must be an integer.
 #define LOCAL_DUMP_SUFFIX ".nut"
 #define DEFAULT_STREAM_MUX_NAME "nut"  // Only for HTTP. Local dump guesses from LOCAL_DUMP_SUFFIX.
 #define DEFAULT_HTTPD_PORT 9095
-#define MUX_OPTS { \
-       /* Make seekable .mov files, and keep MP4 muxer from using unlimited amounts of memory. */ \
-       { "movflags", "empty_moov+frag_keyframe+default_base_moof" MUX_SKIP_TRAILER }, \
-       \
-       /* Make for somewhat less bursty stream output when using .mov. */ \
-       { "frag_duration", "125000" }, \
-       \
-       /* Keep nut muxer from using unlimited amounts of memory. */ \
-       { "write_index", "0" } \
-}
+
+#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),
index 5c4c41bc021fa7306b35db7bea6b9b2e3aad8b58..42fdfc24a04647d36b79dbc848d2f65000e4c3eb 100644 (file)
@@ -5,7 +5,7 @@
 #include <sys/statfs.h>
 #include <memory>
 
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "shared/timebase.h"
 
 DiskSpaceEstimator::DiskSpaceEstimator(DiskSpaceEstimator::callback_t callback)
index 842952f02fcc91951481fb4fbf477c8bbd2aa196..eebf6eba8d5a56678a9d18a738bd06c88dc393ff 100644 (file)
@@ -16,7 +16,7 @@ extern "C" {
 
 #include "defs.h"
 #include "shared/metacube2.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 
 struct MHD_Connection;
 struct MHD_Response;
index 0e555862969234246087ed4f2436015d95db35db..b6ba231c43d3be0dd07c9f7f5ebdc53f6fc1499f 100644 (file)
@@ -6,7 +6,7 @@
 #include "flags.h"
 #include "ffmpeg_capture.h"
 #include "mixer.h"
-#include "mux.h"
+#include "shared/mux.h"
 #include "quittable_sleeper.h"
 #include "shared/timebase.h"
 #include "x264_encoder.h"
@@ -68,7 +68,8 @@ unique_ptr<Mux> create_mux(HTTPD *httpd, AVOutputFormat *oformat, X264Encoder *x
        string video_extradata = x264_encoder->get_global_headers();
 
        unique_ptr<Mux> mux;
-       mux.reset(new Mux(avctx, global_flags.width, global_flags.height, Mux::CODEC_H264, video_extradata, audio_encoder->get_codec_parameters().get(), COARSE_TIMEBASE,
+       mux.reset(new Mux(avctx, global_flags.width, global_flags.height, Mux::CODEC_H264, video_extradata, audio_encoder->get_codec_parameters().get(),
+               get_color_space(global_flags.ycbcr_rec709_coefficients), Mux::WITH_AUDIO, COARSE_TIMEBASE,
                /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, { &stream_mux_metrics }));
        stream_mux_metrics.init({{ "destination", "http" }});
        return mux;
index 6d984e87a027853e99c84359d65580ddec50bf2f..0c12be47d290d47d799ffb09b320f30140b58a69 100644 (file)
@@ -149,7 +149,7 @@ srcs += ['glwidget.cpp', 'mainwindow.cpp', 'vumeter.cpp', 'lrameter.cpp', 'compr
        'nonlinear_fader.cpp', 'context_menus.cpp', 'vu_common.cpp', 'piecewise_interpolator.cpp', 'midi_mapper.cpp']
 
 # Auxiliary objects used for nearly everything.
-aux_srcs = ['metrics.cpp', 'flags.cpp']
+aux_srcs = ['flags.cpp']
 aux = static_library('aux', aux_srcs, dependencies: nageru_deps, include_directories: nageru_include_dirs)
 nageru_link_with += aux
 
@@ -166,7 +166,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',
-       'mux.cpp', 'audio_encoder.cpp', 'ffmpeg_util.cpp', 'httpd.cpp', 'ffmpeg_capture.cpp',
+       'audio_encoder.cpp', 'ffmpeg_util.cpp', 'httpd.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
index fa944944caf1b49274d32accb09124252e92fb54..2f6a8ebafc1018ddbc90ce7db9368b5594070f15 100644 (file)
@@ -45,7 +45,7 @@
 #include "ffmpeg_capture.h"
 #include "flags.h"
 #include "input_mapping.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "pbo_frame_allocator.h"
 #include "shared/ref_counted_gl_sync.h"
 #include "resampling_queue.h"
diff --git a/nageru/mux.h b/nageru/mux.h
deleted file mode 100644 (file)
index ed742ff..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-#ifndef _MUX_H
-#define _MUX_H 1
-
-// Wrapper around an AVFormat mux.
-
-extern "C" {
-#include <libavcodec/avcodec.h>
-#include <libavformat/avformat.h>
-}
-
-#include <sys/types.h>
-#include <atomic>
-#include <condition_variable>
-#include <functional>
-#include <mutex>
-#include <string>
-#include <utility>
-#include <thread>
-#include <vector>
-
-#include "shared/timebase.h"
-
-struct MuxMetrics {
-       // “written” will usually be equal video + audio + mux overhead,
-       // except that there could be buffered packets that count in audio or video
-       // but not yet in written.
-       std::atomic<int64_t> metric_video_bytes{0}, metric_audio_bytes{0}, metric_written_bytes{0};
-
-       // Registers in global_metrics.
-       void init(const std::vector<std::pair<std::string, std::string>> &labels);
-
-       void reset()
-       {
-               metric_video_bytes = 0;
-               metric_audio_bytes = 0;
-               metric_written_bytes = 0;
-       }
-};
-
-class Mux {
-public:
-       enum Codec {
-               CODEC_H264,
-               CODEC_NV12,  // Uncompressed 4:2:0.
-       };
-       enum WriteStrategy {
-               // add_packet() will write the packet immediately, unless plugged.
-               WRITE_FOREGROUND,
-
-               // All writes will happen on a separate thread, so add_packet()
-               // won't block. Use this if writing to a file and you might be
-               // holding a mutex (because blocking I/O with a mutex held is
-               // not good). Note that this will clone every packet, so it has
-               // higher overhead.
-               WRITE_BACKGROUND,
-       };
-
-       // Takes ownership of avctx. <write_callback> will be called every time
-       // a write has been made to the video stream (id 0), with the pts of
-       // the just-written frame. (write_callback can be nullptr.)
-       // Does not take ownership of <metrics>; elements in there, if any,
-       // will be added to.
-       Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const std::string &video_extradata, const AVCodecParameters *audio_codecpar, int time_base, std::function<void(int64_t)> write_callback, WriteStrategy write_strategy, const std::vector<MuxMetrics *> &metrics);
-       ~Mux();
-       void add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, AVRational timebase = { 1, TIMEBASE }, int stream_index_override = -1);
-
-       // As long as the mux is plugged, it will not actually write anything to disk,
-       // just queue the packets. Once it is unplugged, the packets are reordered by pts
-       // and written. This is primarily useful if you might have two different encoders
-       // writing to the mux at the same time (because one is shutting down), so that
-       // pts might otherwise come out-of-order.
-       //
-       // You can plug and unplug multiple times; only when the plug count reaches zero,
-       // something will actually happen.
-       void plug();
-       void unplug();
-
-private:
-       // If write_strategy == WRITE_FOREGORUND, Must be called with <mu> held.
-       void write_packet_or_die(const AVPacket &pkt, int64_t unscaled_pts);
-       void thread_func();
-
-       WriteStrategy write_strategy;
-
-       std::mutex mu;
-
-       // These are only in use if write_strategy == WRITE_BACKGROUND.
-       std::atomic<bool> writer_thread_should_quit{false};
-       std::thread writer_thread;
-
-       AVFormatContext *avctx;  // Protected by <mu>, iff write_strategy == WRITE_BACKGROUND.
-       int plug_count = 0;  // Protected by <mu>.
-
-       // Protected by <mu>. If write_strategy == WRITE_FOREGROUND,
-       // this is only in use when plugging.
-       struct QueuedPacket {
-               AVPacket *pkt;
-               int64_t unscaled_pts;
-       };
-       std::vector<QueuedPacket> packet_queue;
-       std::condition_variable packet_queue_ready;
-
-       AVStream *avstream_video, *avstream_audio;
-
-       std::function<void(int64_t)> write_callback;
-       std::vector<MuxMetrics *> metrics;
-
-       friend struct PacketBefore;
-};
-
-#endif  // !defined(_MUX_H)
index 72440ae0fe41a3ec13df76c595450ca409090aec..21098a7a5997fac8743008fe285c412566ae0e57 100644 (file)
@@ -1,7 +1,7 @@
 #include "print_latency.h"
 
 #include "flags.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "mixer.h"
 
 #include <stdio.h>
index d80ac88e96a25ac04f6f64a27f921edd9c45ff59..a9fb267949437773969685fb401856c384d14c9e 100644 (file)
@@ -10,7 +10,7 @@
 #include <vector>
 
 #include "ref_counted_frame.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 
 // Since every output frame is based on multiple input frames, we need
 // more than one start timestamp; one for each input.
index cc3818d336b0dd061f5a91f35a89c61a0e9ed405..58f4172a900a84fa4b421306aeb117c54743c55b 100644 (file)
@@ -50,7 +50,7 @@ extern "C" {
 #include "disk_space_estimator.h"
 #include "shared/ffmpeg_raii.h"
 #include "flags.h"
-#include "mux.h"
+#include "shared/mux.h"
 #include "print_latency.h"
 #include "quicksync_encoder_impl.h"
 #include "ref_counted_frame.h"
@@ -1813,7 +1813,7 @@ void QuickSyncEncoderImpl::open_output_file(const std::string &filename)
        {
                lock_guard<mutex> lock(file_audio_encoder_mutex);
                AVCodecParametersWithDeleter audio_codecpar = file_audio_encoder->get_codec_parameters();
-               file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, video_extradata, audio_codecpar.get(), TIMEBASE,
+               file_mux.reset(new Mux(avctx, frame_width, frame_height, Mux::CODEC_H264, video_extradata, audio_codecpar.get(), get_color_space(global_flags.ycbcr_rec709_coefficients), Mux::WITH_AUDIO, TIMEBASE,
                        std::bind(&DiskSpaceEstimator::report_write, disk_space_estimator, filename, _1),
                        Mux::WRITE_BACKGROUND,
                        { &current_file_mux_metrics, &total_mux_metrics }));
index e7dd8a74cb92adff59918067c822e318ce1d3708..5f5b975feb175497316f3590b407bab42b5a0e22 100644 (file)
@@ -16,7 +16,7 @@ extern "C" {
 #include "shared/ffmpeg_raii.h"
 #include "flags.h"
 #include "httpd.h"
-#include "mux.h"
+#include "shared/mux.h"
 #include "quicksync_encoder.h"
 #include "shared/timebase.h"
 #include "x264_encoder.h"
@@ -193,7 +193,9 @@ void VideoEncoder::open_output_stream()
                video_extradata = x264_encoder->get_global_headers();
        }
 
-       stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, stream_audio_encoder->get_codec_parameters().get(), COARSE_TIMEBASE,
+       stream_mux.reset(new Mux(avctx, width, height, video_codec, video_extradata, stream_audio_encoder->get_codec_parameters().get(),
+               get_color_space(global_flags.ycbcr_rec709_coefficients),
+               Mux::WITH_AUDIO, COARSE_TIMEBASE,
                /*write_callback=*/nullptr, Mux::WRITE_FOREGROUND, { &stream_mux_metrics }));
        stream_mux_metrics.init({{ "destination", "http" }});
 }
index 5c42ef81f1a6c94fd1a68d4be4e755f3a5fd63df..76dd92fa12cb12ef7dc2d439f9f335d75dcaf6b4 100644 (file)
@@ -20,7 +20,7 @@ extern "C" {
 #include <libavformat/avio.h>
 }
 
-#include "mux.h"
+#include "shared/mux.h"
 #include "shared/ref_counted_gl_sync.h"
 
 class AudioEncoder;
index e061a58b9d6157a79cb040fe53ab1fdcd06896d1..52710088d1e722fce482a06b3f555c9afe3b6862 100644 (file)
@@ -14,8 +14,8 @@
 
 #include "defs.h"
 #include "flags.h"
-#include "metrics.h"
-#include "mux.h"
+#include "shared/metrics.h"
+#include "shared/mux.h"
 #include "print_latency.h"
 #include "shared/timebase.h"
 #include "x264_dynamic.h"
index 687bf718679c316a3b2e6a24a41c8e449d79c776..7b8751715250d5f34147f7f11d9a8d83df409037 100644 (file)
@@ -33,7 +33,7 @@ extern "C" {
 #include <movit/image_format.h>
 
 #include "defs.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "print_latency.h"
 #include "x264_dynamic.h"
 
index 719cf28d3ef5a47773bd133206a283ba18b1579f..5240347d8375806647ca347be6d0154fb5836da3 100644 (file)
@@ -11,7 +11,7 @@
 #include <type_traits>
 
 #include "flags.h"
-#include "metrics.h"
+#include "shared/metrics.h"
 
 using namespace std;
 using namespace std::chrono;
index b0a1739d022e462c48c7e80e7b73b905fcdb6411..7559b87318c2064f56031c5abdcf3e23a534984b 100644 (file)
@@ -55,7 +55,7 @@ extern "C" {
 #include <x264.h>
 }
 
-#include "metrics.h"
+#include "shared/metrics.h"
 #include "x264_dynamic.h"
 
 class X264SpeedControl {
index 09bf99027fa63a6c31aecd0a52e739289e8097ed..2afff9e5ead674b3cd5fee1571d1baffe659c0f2 100644 (file)
@@ -1,7 +1,6 @@
 
-srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp']
+srcs = ['memcpy_interleaved.cpp', 'metacube2.cpp', 'ffmpeg_raii.cpp', 'mux.cpp', 'metrics.cpp']
 shared = static_library('shared', srcs, include_directories: top_include)
 shareddep = declare_dependency(
    include_directories: top_include,
-   link_with: shared,
-   sources: srcs)
+   link_with: shared)
similarity index 99%
rename from nageru/metrics.cpp
rename to shared/metrics.cpp
index 86c3d591872a4a38d53d34a55216b66e4d6d1405..24b61fe56de7cb0574dab74670101393ae645dab 100644 (file)
@@ -1,4 +1,4 @@
-#include "metrics.h"
+#include "shared/metrics.h"
 
 #include <assert.h>
 #include <math.h>
similarity index 100%
rename from nageru/metrics.h
rename to shared/metrics.h
similarity index 88%
rename from nageru/mux.cpp
rename to shared/mux.cpp
index cb4d0bad5179786feffa919f2fbb515b701a6313..da618e0c6bbc1066ef4c3db110f935c48c0bde45 100644 (file)
@@ -1,12 +1,12 @@
-#include "mux.h"
+#include "shared/mux.h"
 
+#include <algorithm>
 #include <assert.h>
+#include <mutex>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <algorithm>
-#include <mutex>
 #include <string>
 #include <utility>
 #include <vector>
@@ -21,9 +21,8 @@ extern "C" {
 #include <libavutil/rational.h>
 }
 
-#include "defs.h"
-#include "flags.h"
-#include "metrics.h"
+#include "shared/metrics.h"
+#include "shared/mux_opts.h"
 #include "shared/timebase.h"
 
 using namespace std;
@@ -48,7 +47,7 @@ struct PacketBefore {
        const AVFormatContext * const ctx;
 };
 
-Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const string &video_extradata, const AVCodecParameters *audio_codecpar, int time_base, std::function<void(int64_t)> write_callback, WriteStrategy write_strategy, const vector<MuxMetrics *> &metrics)
+Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const string &video_extradata, const AVCodecParameters *audio_codecpar, AVColorSpace color_space, WithAudio with_audio, int time_base, function<void(int64_t)> write_callback, WriteStrategy write_strategy, const vector<MuxMetrics *> &metrics)
        : write_strategy(write_strategy), avctx(avctx), write_callback(write_callback), metrics(metrics)
 {
        avstream_video = avformat_new_stream(avctx, nullptr);
@@ -60,10 +59,12 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const
        avstream_video->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
        if (video_codec == CODEC_H264) {
                avstream_video->codecpar->codec_id = AV_CODEC_ID_H264;
-       } else {
-               assert(video_codec == CODEC_NV12);
+       } else if (video_codec == CODEC_NV12) {
                avstream_video->codecpar->codec_id = AV_CODEC_ID_RAWVIDEO;
                avstream_video->codecpar->codec_tag = avcodec_pix_fmt_to_codec_tag(AV_PIX_FMT_NV12);
+       } else {
+               assert(video_codec == CODEC_MJPEG);
+               avstream_video->codecpar->codec_id = AV_CODEC_ID_MJPEG;
        }
        avstream_video->codecpar->width = width;
        avstream_video->codecpar->height = height;
@@ -77,11 +78,7 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const
        avstream_video->codecpar->color_primaries = AVCOL_PRI_BT709;  // RGB colorspace (inout_format.color_space).
        avstream_video->codecpar->color_trc = AVCOL_TRC_IEC61966_2_1;  // Gamma curve (inout_format.gamma_curve).
        // YUV colorspace (output_ycbcr_format.luma_coefficients).
-       if (global_flags.ycbcr_rec709_coefficients) {
-               avstream_video->codecpar->color_space = AVCOL_SPC_BT709;
-       } else {
-               avstream_video->codecpar->color_space = AVCOL_SPC_SMPTE170M;
-       }
+       avstream_video->codecpar->color_space = color_space;
        avstream_video->codecpar->color_range = AVCOL_RANGE_MPEG;  // Full vs. limited range (output_ycbcr_format.full_range).
        avstream_video->codecpar->chroma_location = AVCHROMA_LOC_LEFT;  // Chroma sample location. See chroma_offset_0[] in Mixer::subsample_chroma().
        avstream_video->codecpar->field_order = AV_FIELD_PROGRESSIVE;
@@ -92,15 +89,20 @@ Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const
                memcpy(avstream_video->codecpar->extradata, video_extradata.data(), video_extradata.size());
        }
 
-       avstream_audio = avformat_new_stream(avctx, nullptr);
-       if (avstream_audio == nullptr) {
-               fprintf(stderr, "avformat_new_stream() failed\n");
-               exit(1);
-       }
-       avstream_audio->time_base = AVRational{1, time_base};
-       if (avcodec_parameters_copy(avstream_audio->codecpar, audio_codecpar) < 0) {
-               fprintf(stderr, "avcodec_parameters_copy() failed\n");
-               exit(1);
+       if (with_audio == WITH_AUDIO) {
+               avstream_audio = avformat_new_stream(avctx, nullptr);
+               if (avstream_audio == nullptr) {
+                       fprintf(stderr, "avformat_new_stream() failed\n");
+                       exit(1);
+               }
+               avstream_audio->time_base = AVRational{1, time_base};
+               if (avcodec_parameters_copy(avstream_audio->codecpar, audio_codecpar) < 0) {
+                       fprintf(stderr, "avcodec_parameters_copy() failed\n");
+                       exit(1);
+               }
+       } else {
+               assert(with_audio == WITHOUT_AUDIO);
+               avstream_audio = nullptr;
        }
 
        AVDictionary *options = NULL;
@@ -172,7 +174,8 @@ void Mux::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, AVRational t
                lock_guard<mutex> lock(mu);
                if (write_strategy == WriteStrategy::WRITE_BACKGROUND) {
                        packet_queue.push_back(QueuedPacket{ av_packet_clone(&pkt_copy), pts });
-                       if (plug_count == 0) packet_queue_ready.notify_all();
+                       if (plug_count == 0)
+                               packet_queue_ready.notify_all();
                } else if (plug_count > 0) {
                        packet_queue.push_back(QueuedPacket{ av_packet_clone(&pkt_copy), pts });
                } else {
@@ -197,7 +200,7 @@ void Mux::write_packet_or_die(const AVPacket &pkt, int64_t unscaled_pts)
        int64_t old_pos = avctx->pb->pos;
        if (av_interleaved_write_frame(avctx, const_cast<AVPacket *>(&pkt)) < 0) {
                fprintf(stderr, "av_interleaved_write_frame() failed\n");
-               exit(1);
+               abort();
        }
        avio_flush(avctx->pb);
        for (MuxMetrics *metric : metrics) {
@@ -238,6 +241,8 @@ void Mux::unplug()
 
 void Mux::thread_func()
 {
+       pthread_setname_np(pthread_self(), "Mux");
+
        unique_lock<mutex> lock(mu);
        for ( ;; ) {
                packet_queue_ready.wait(lock, [this]() {
similarity index 88%
rename from futatabi/mux.h
rename to shared/mux.h
index dca6be55fc8f030a49aa3208ae6fb2ab605819d7..62cd37cf80791f061182ebf50498756948f24a78 100644 (file)
@@ -37,6 +37,15 @@ struct MuxMetrics {
        }
 };
 
+inline AVColorSpace get_color_space(bool ycbcr_rec709_coefficients)
+{
+       if (ycbcr_rec709_coefficients) {
+               return AVCOL_SPC_BT709;
+       } else {
+               return AVCOL_SPC_SMPTE170M;
+       }
+}
+
 class Mux {
 public:
        enum Codec {
@@ -44,6 +53,10 @@ public:
                CODEC_NV12,  // Uncompressed 4:2:0.
                CODEC_MJPEG
        };
+       enum WithAudio {
+               WITH_AUDIO,
+               WITHOUT_AUDIO
+       };
        enum WriteStrategy {
                // add_packet() will write the packet immediately, unless plugged.
                WRITE_FOREGROUND,
@@ -61,7 +74,7 @@ public:
        // the just-written frame. (write_callback can be nullptr.)
        // Does not take ownership of <metrics>; elements in there, if any,
        // will be added to.
-       Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const std::string &video_extradata, const AVCodecParameters *audio_codecpar, int time_base, std::function<void(int64_t)> write_callback, WriteStrategy write_strategy, const std::vector<MuxMetrics *> &metrics);
+       Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, const std::string &video_extradata, const AVCodecParameters *audio_codecpar, AVColorSpace color_space, WithAudio with_audio, int time_base, std::function<void(int64_t)> write_callback, WriteStrategy write_strategy, const std::vector<MuxMetrics *> &metrics);
        ~Mux();
        void add_packet(const AVPacket &pkt, int64_t pts, int64_t dts, AVRational timebase = { 1, TIMEBASE }, int stream_index_override = -1);
 
diff --git a/shared/mux_opts.h b/shared/mux_opts.h
new file mode 100644 (file)
index 0000000..0315fb8
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _MUX_OPTS_H
+#define _MUX_OPTS_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)
+#define MUX_SKIP_TRAILER "+skip_trailer"
+#else
+#define MUX_SKIP_TRAILER ""
+#endif
+
+#define MUX_OPTS { \
+       /* Make seekable .mov files, and keep MP4 muxer from using unlimited amounts of memory. */ \
+       { "movflags", "empty_moov+frag_keyframe+default_base_moof" MUX_SKIP_TRAILER }, \
+       \
+       /* Make for somewhat less bursty stream output when using .mov. */ \
+       { "frag_duration", "125000" }, \
+       \
+       /* Keep nut muxer from using unlimited amounts of memory. */ \
+       { "write_index", "0" } \
+}
+
+#endif  // !defined(_MUX_OPTS_H)