]> git.sesse.net Git - casparcg/blobdiff - modules/ffmpeg/consumer/streaming_consumer.cpp
[streaming_consumer] Added default cropping/scaling/pixel format selection/bitrate...
[casparcg] / modules / ffmpeg / consumer / streaming_consumer.cpp
index 95fa9c0eb8f23e23d75ee21f58b538eea2fc60d5..d0cf1aa72df82a2983bfe7c6eb25bac27a531304 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "../ffmpeg_error.h"
 #include "../producer/util/util.h"
+#include "../producer/filter/filter.h"
 
 #include <common/except.h>
 #include <common/executor.h>
@@ -67,6 +68,75 @@ extern "C"
 
 namespace caspar { namespace ffmpeg { namespace {
 
+void set_pixel_format(AVFilterContext* sink, AVPixelFormat pix_fmt)
+{
+#pragma warning (push)
+#pragma warning (disable : 4245)
+
+       FF(av_opt_set_int_list(
+               sink,
+               "pix_fmts",
+               std::vector<AVPixelFormat>({ pix_fmt, AVPixelFormat::AV_PIX_FMT_NONE }).data(),
+               -1,
+               AV_OPT_SEARCH_CHILDREN));
+
+#pragma warning (pop)
+}
+
+void adjust_video_filter(const AVCodec& codec, const core::video_format_desc& in_format, AVFilterContext* sink, std::string& filter)
+{
+       switch (codec.id)
+       {
+       case AV_CODEC_ID_DVVIDEO:
+               // Crop
+               if (in_format.format == core::video_format::ntsc)
+                       filter = u8(append_filter(u16(filter), L"crop=720:480:0:2"));
+
+               // Pixel format selection
+               if (in_format.format == core::video_format::ntsc)
+                       set_pixel_format(sink, AVPixelFormat::AV_PIX_FMT_YUV411P);
+               else if (in_format.format == core::video_format::pal)
+                       set_pixel_format(sink, AVPixelFormat::AV_PIX_FMT_YUV420P);
+               else
+                       set_pixel_format(sink, AVPixelFormat::AV_PIX_FMT_YUV422P);
+
+               // Scale
+               if (in_format.height == 1080)
+                       filter = u8(append_filter(u16(filter), in_format.duration == 1001
+                               ? L"scale=1280:1080"
+                               : L"scale=1440:1080"));
+               else if (in_format.height == 720)
+                       filter = u8(append_filter(u16(filter), L"scale=960:720"));
+
+               break;
+       }
+}
+
+void setup_codec_defaults(AVCodecContext& encoder)
+{
+       static const int MEGABIT = 1000000;
+
+       switch (encoder.codec_id)
+       {
+       case AV_CODEC_ID_DNXHD:
+               encoder.bit_rate = 220 * MEGABIT;
+
+               break;
+       case AV_CODEC_ID_PRORES:
+               encoder.bit_rate = encoder.width < 1280
+                               ?  63 * MEGABIT
+                               : 220 * MEGABIT;
+
+               break;
+       case AV_CODEC_ID_H264:
+               av_opt_set(encoder.priv_data,   "preset",       "ultrafast",    0);
+               av_opt_set(encoder.priv_data,   "tune",         "fastdecode",   0);
+               av_opt_set(encoder.priv_data,   "crf",          "5",                    0);
+
+               break;
+       }
+}
+
 bool is_pcm_s24le_not_supported(const AVFormatContext& container)
 {
        auto name = std::string(container.oformat->name);
@@ -85,7 +155,8 @@ public:
 private:
        const spl::shared_ptr<diagnostics::graph>       graph_;
        core::monitor::subject                                          subject_;
-       boost::filesystem::path                                         path_;
+       std::string                                                                     path_;
+       boost::filesystem::path                                         full_path_;
 
        std::map<std::string, std::string>                      options_;
 
@@ -98,8 +169,8 @@ private:
        std::shared_ptr<AVStream>                                       video_st_;
        std::shared_ptr<AVStream>                                       audio_st_;
 
-       std::int64_t                                                            video_pts_;
-       std::int64_t                                                            audio_pts_;
+       std::int64_t                                                            video_pts_                                      = 0;
+       std::int64_t                                                            audio_pts_                                      = 0;
 
     AVFilterContext*                                                   audio_graph_in_;
     AVFilterContext*                                                   audio_graph_out_;
@@ -124,8 +195,7 @@ public:
                        std::string path,
                        std::string options)
                : path_(path)
-               , video_pts_(0)
-               , audio_pts_(0)
+               , full_path_(path)
                , audio_encoder_executor_(print() + L" audio_encoder")
                , video_encoder_executor_(print() + L" video_encoder")
                , write_executor_(print() + L" io")
@@ -195,19 +265,21 @@ public:
                        static boost::regex prot_exp("^.+:.*" );
 
                        if(!boost::regex_match(
-                                       path_.string(),
+                                       path_,
                                        prot_exp))
                        {
-                               if(!path_.is_complete())
+                               if(!full_path_.is_complete())
                                {
-                                       path_ =
+                                       full_path_ =
                                                u8(
                                                        env::media_folder()) +
-                                                       path_.string();
+                                                       path_;
                                }
 
-                               if(boost::filesystem::exists(path_))
-                                       boost::filesystem::remove(path_);
+                               if(boost::filesystem::exists(full_path_))
+                                       boost::filesystem::remove(full_path_);
+
+                               boost::filesystem::create_directories(full_path_.parent_path());
                        }
 
                        graph_->set_color("frame-time", diagnostics::color(0.1f, 1.0f, 0.1f));
@@ -226,7 +298,7 @@ public:
                                &oc,
                                nullptr,
                                oformat_name && !oformat_name->empty() ? oformat_name->c_str() : nullptr,
-                               path_.string().c_str()));
+                               full_path_.string().c_str()));
 
                        oc_.reset(
                                oc,
@@ -334,7 +406,7 @@ public:
                                {
                                        FF(avio_open2(
                                                &oc_->pb,
-                                               path_.string().c_str(),
+                                               full_path_.string().c_str(),
                                                AVIO_FLAG_WRITE,
                                                &oc_->interrupt_callback,
                                                &av_opts));
@@ -421,7 +493,7 @@ public:
 
        std::wstring print() const
        {
-               return L"ffmpeg_consumer[" + u16(path_.string()) + L"]";
+               return L"ffmpeg_consumer[" + u16(path_) + L"]";
        }
 
        int64_t presentation_frame_age_millis() const
@@ -479,6 +551,8 @@ private:
                        }
                }
 
+               setup_codec_defaults(*enc);
+
                if(oc_->oformat->flags & AVFMT_GLOBALHEADER)
                        enc->flags |= CODEC_FLAG_GLOBAL_HEADER;
 
@@ -546,7 +620,7 @@ private:
 
        void configure_video_filters(
                        const AVCodec& codec,
-                       const std::string& filtergraph)
+                       std::string filtergraph)
        {
                video_graph_.reset(
                                avfilter_graph_alloc(),
@@ -568,7 +642,7 @@ private:
 
                const auto vsrc_options = (boost::format("video_size=%1%x%2%:pix_fmt=%3%:time_base=%4%/%5%:pixel_aspect=%6%/%7%:frame_rate=%8%/%9%")
                        % in_video_format_.width % in_video_format_.height
-                       % AV_PIX_FMT_BGRA
+                       % AVPixelFormat::AV_PIX_FMT_BGRA
                        % in_video_format_.duration     % in_video_format_.time_scale
                        % sample_aspect_ratio.numerator() % sample_aspect_ratio.denominator()
                        % in_video_format_.time_scale % in_video_format_.duration).str();
@@ -603,6 +677,8 @@ private:
 
 #pragma warning (pop)
 
+               adjust_video_filter(codec, in_video_format_, filt_vsink, filtergraph);
+
                configure_filtergraph(
                                *video_graph_,
                                filtergraph,
@@ -718,51 +794,51 @@ private:
                AVFilterInOut* outputs = nullptr;
                AVFilterInOut* inputs = nullptr;
 
-               try
+               if(!filtergraph.empty())
                {
-                       if(!filtergraph.empty())
-                       {
-                               outputs = avfilter_inout_alloc();
-                               inputs  = avfilter_inout_alloc();
+                       outputs = avfilter_inout_alloc();
+                       inputs  = avfilter_inout_alloc();
 
+                       try
+                       {
                                CASPAR_VERIFY(outputs && inputs);
 
-                               outputs->name       = av_strdup("in");
-                               outputs->filter_ctx = &source_ctx;
-                               outputs->pad_idx    = 0;
-                               outputs->next       = nullptr;
+                               outputs->name           = av_strdup("in");
+                               outputs->filter_ctx     = &source_ctx;
+                               outputs->pad_idx                = 0;
+                               outputs->next           = nullptr;
 
-                               inputs->name        = av_strdup("out");
-                               inputs->filter_ctx  = &sink_ctx;
-                               inputs->pad_idx     = 0;
-                               inputs->next        = nullptr;
+                               inputs->name                    = av_strdup("out");
+                               inputs->filter_ctx      = &sink_ctx;
+                               inputs->pad_idx         = 0;
+                               inputs->next                    = nullptr;
+                       }
+                       catch (...)
+                       {
+                               avfilter_inout_free(&outputs);
+                               avfilter_inout_free(&inputs);
+                               throw;
+                       }
 
-                               FF(avfilter_graph_parse(
+                       FF(avfilter_graph_parse(
                                        &graph,
                                        filtergraph.c_str(),
                                        inputs,
                                        outputs,
                                        nullptr));
-                       }
-                       else
-                       {
-                               FF(avfilter_link(
+               }
+               else
+               {
+                       FF(avfilter_link(
                                        &source_ctx,
                                        0,
                                        &sink_ctx,
                                        0));
-                       }
+               }
 
-                       FF(avfilter_graph_config(
+               FF(avfilter_graph_config(
                                &graph,
                                nullptr));
-               }
-               catch(...)
-               {
-                       avfilter_inout_free(&outputs);
-                       avfilter_inout_free(&inputs);
-                       throw;
-               }
        }
 
        void encode_video(core::const_frame frame_ptr, std::shared_ptr<void> token)
@@ -784,7 +860,7 @@ private:
                                        in_video_format_.width,
                                        in_video_format_.height);
 
-                       src_av_frame->format                                            = AV_PIX_FMT_BGRA;
+                       src_av_frame->format                                            = AVPixelFormat::AV_PIX_FMT_BGRA;
                        src_av_frame->width                                             = in_video_format_.width;
                        src_av_frame->height                                            = in_video_format_.height;
                        src_av_frame->sample_aspect_ratio.num   = sample_aspect_ratio.numerator();
@@ -793,6 +869,11 @@ private:
 
                        video_pts_ += 1;
 
+                       subject_
+                                       << core::monitor::message("/frame")     % video_pts_
+                                       << core::monitor::message("/path")      % path_
+                                       << core::monitor::message("/fps")       % in_video_format_.fps;
+
                        FF(av_image_fill_arrays(
                                src_av_frame->data,
                                src_av_frame->linesize,
@@ -1205,7 +1286,7 @@ public:
                return compatibility_mode_ ? 200 : 100000 + consumer_index_offset_;
        }
 
-       core::monitor::subject& monitor_output()
+       core::monitor::subject& monitor_output() override
        {
                return consumer_->monitor_output();
        }