From 29dbd73d394a83ffef452072d57813325efad286 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sun, 17 Apr 2016 17:24:05 +0200 Subject: [PATCH] Add an option --http-coarse-timebase for streaming to MP4. --- flags.cpp | 6 ++++++ flags.h | 1 + httpd.cpp | 20 +++++++++++--------- httpd.h | 4 ++-- timebase.h | 7 +++++++ 5 files changed, 27 insertions(+), 11 deletions(-) diff --git a/flags.cpp b/flags.cpp index 0fe2236..0c01e23 100644 --- a/flags.cpp +++ b/flags.cpp @@ -17,6 +17,8 @@ void usage() fprintf(stderr, " ($DISPLAY spec or /dev/dri/render* path)\n"); fprintf(stderr, " --http-uncompressed-video send uncompressed NV12 video to HTTP clients\n"); fprintf(stderr, " --http-mux=NAME mux to use for HTTP streams (default " DEFAULT_STREAM_MUX_NAME ")\n"); + fprintf(stderr, " --http-coarse-timebase use less timebase for HTTP (recommended for muxers\n"); + fprintf(stderr, " that handle large pts poorly, like e.g. MP4)\n"); fprintf(stderr, " --flat-audio start with most audio processing turned off\n"); fprintf(stderr, " --no-flush-pbos do not explicitly signal texture data uploads\n"); fprintf(stderr, " (will give display corruption, but makes it\n"); @@ -32,6 +34,7 @@ void parse_flags(int argc, char * const argv[]) { "va-display", required_argument, 0, 1000 }, { "http-uncompressed-video", no_argument, 0, 1001 }, { "http-mux", required_argument, 0, 1004 }, + { "http-coarse-timebase", no_argument, 0, 1005 }, { "flat-audio", no_argument, 0, 1002 }, { "no-flush-pbos", no_argument, 0, 1003 }, { 0, 0, 0, 0 } @@ -59,6 +62,9 @@ void parse_flags(int argc, char * const argv[]) case 1004: global_flags.stream_mux_name = optarg; break; + case 1005: + global_flags.stream_coarse_timebase = true; + break; case 1002: global_flags.flat_audio = true; break; diff --git a/flags.h b/flags.h index 308e310..b2dedb6 100644 --- a/flags.h +++ b/flags.h @@ -13,6 +13,7 @@ struct Flags { bool flat_audio = false; bool flush_pbos = true; std::string stream_mux_name = DEFAULT_STREAM_MUX_NAME; + bool stream_coarse_timebase = false; }; extern Flags global_flags; diff --git a/httpd.cpp b/httpd.cpp index f6664b3..d860a58 100644 --- a/httpd.cpp +++ b/httpd.cpp @@ -70,7 +70,7 @@ void HTTPD::open_output_file(const string &filename) exit(1); } - file_mux.reset(new Mux(avctx, width, height, Mux::CODEC_H264)); + file_mux.reset(new Mux(avctx, width, height, Mux::CODEC_H264, TIMEBASE)); } void HTTPD::close_output_file() @@ -94,7 +94,9 @@ int HTTPD::answer_to_connection(MHD_Connection *connection, { AVOutputFormat *oformat = av_guess_format(global_flags.stream_mux_name.c_str(), nullptr, nullptr); assert(oformat != nullptr); - HTTPD::Stream *stream = new HTTPD::Stream(oformat, width, height); + + int time_base = global_flags.stream_coarse_timebase ? COARSE_TIMEBASE : TIMEBASE; + HTTPD::Stream *stream = new HTTPD::Stream(oformat, width, height, time_base); { unique_lock lock(streams_mutex); streams.insert(stream); @@ -138,7 +140,7 @@ void HTTPD::request_completed(struct MHD_Connection *connection, void **con_cls, } } -HTTPD::Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec) +HTTPD::Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, int time_base) : avctx(avctx) { AVCodec *codec_video = avcodec_find_encoder((video_codec == CODEC_H264) ? AV_CODEC_ID_H264 : AV_CODEC_ID_RAWVIDEO); @@ -147,7 +149,7 @@ HTTPD::Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec fprintf(stderr, "avformat_new_stream() failed\n"); exit(1); } - avstream_video->time_base = AVRational{1, TIMEBASE}; + avstream_video->time_base = AVRational{1, time_base}; avstream_video->codec->codec_type = AVMEDIA_TYPE_VIDEO; if (video_codec == CODEC_H264) { avstream_video->codec->codec_id = AV_CODEC_ID_H264; @@ -158,7 +160,7 @@ HTTPD::Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec } avstream_video->codec->width = width; avstream_video->codec->height = height; - avstream_video->codec->time_base = AVRational{1, TIMEBASE}; + avstream_video->codec->time_base = AVRational{1, time_base}; avstream_video->codec->ticks_per_frame = 1; // or 2? // Colorspace details. Closely correspond to settings in EffectChain_finalize, @@ -181,13 +183,13 @@ HTTPD::Mux::Mux(AVFormatContext *avctx, int width, int height, Codec video_codec fprintf(stderr, "avformat_new_stream() failed\n"); exit(1); } - avstream_audio->time_base = AVRational{1, TIMEBASE}; + avstream_audio->time_base = AVRational{1, time_base}; avstream_audio->codec->bit_rate = AUDIO_OUTPUT_BIT_RATE; avstream_audio->codec->sample_rate = OUTPUT_FREQUENCY; avstream_audio->codec->sample_fmt = AUDIO_OUTPUT_SAMPLE_FMT; avstream_audio->codec->channels = 2; avstream_audio->codec->channel_layout = AV_CH_LAYOUT_STEREO; - avstream_audio->codec->time_base = AVRational{1, TIMEBASE}; + avstream_audio->codec->time_base = AVRational{1, time_base}; if (avctx->oformat->flags & AVFMT_GLOBALHEADER) { avstream_audio->codec->flags = AV_CODEC_FLAG_GLOBAL_HEADER; } @@ -239,7 +241,7 @@ void HTTPD::Mux::add_packet(const AVPacket &pkt, int64_t pts, int64_t dts) av_packet_unref(&pkt_copy); } -HTTPD::Stream::Stream(AVOutputFormat *oformat, int width, int height) +HTTPD::Stream::Stream(AVOutputFormat *oformat, int width, int height, int time_base) { AVFormatContext *avctx = avformat_alloc_context(); avctx->oformat = oformat; @@ -255,7 +257,7 @@ HTTPD::Stream::Stream(AVOutputFormat *oformat, int width, int height) avctx->flags = AVFMT_FLAG_CUSTOM_IO; - mux.reset(new Mux(avctx, width, height, video_codec)); + mux.reset(new Mux(avctx, width, height, video_codec, time_base)); } ssize_t HTTPD::Stream::reader_callback_thunk(void *cls, uint64_t pos, char *buf, size_t max) diff --git a/httpd.h b/httpd.h index 695c708..e6f12d9 100644 --- a/httpd.h +++ b/httpd.h @@ -67,7 +67,7 @@ private: CODEC_NV12, // Uncompressed 4:2:0. }; - Mux(AVFormatContext *avctx, int width, int height, Codec video_codec); // Takes ownership of avctx. + Mux(AVFormatContext *avctx, int width, int height, Codec video_codec, int time_base); // Takes ownership of avctx. ~Mux(); void add_packet(const AVPacket &pkt, int64_t pts, int64_t dts); @@ -79,7 +79,7 @@ private: class Stream { public: - Stream(AVOutputFormat *oformat, int width, int height); + Stream(AVOutputFormat *oformat, int width, int height, int time_base); 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); diff --git a/timebase.h b/timebase.h index be2362b..3d335fd 100644 --- a/timebase.h +++ b/timebase.h @@ -14,4 +14,11 @@ // going to 44100000; probably a bit excessive. #define TIMEBASE 60000 +// Some muxes, like MP4 (or at least avformat's implementation of it), +// are not too fond of values above 2^31. At timebase 60000, that's only +// about ten hours or so, so we define a coarser timebase that doesn't +// get 59.94 precisely (so there will be a marginal amount of pts jitter), +// but can do at least 50 and 60 precisely, and months of streaming. +#define COARSE_TIMEBASE 300 + #endif // !defined(_TIMEBASE_H) -- 2.39.2