X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fffmpeg%2Fconsumer%2Fstreaming_consumer.cpp;h=d0cf1aa72df82a2983bfe7c6eb25bac27a531304;hb=49f6906495afac6548207a49a0f3c8a9e92709d6;hp=95fa9c0eb8f23e23d75ee21f58b538eea2fc60d5;hpb=c4edb05bbed7276f5956e8d2995c40d71a9f065f;p=casparcg diff --git a/modules/ffmpeg/consumer/streaming_consumer.cpp b/modules/ffmpeg/consumer/streaming_consumer.cpp index 95fa9c0eb..d0cf1aa72 100644 --- a/modules/ffmpeg/consumer/streaming_consumer.cpp +++ b/modules/ffmpeg/consumer/streaming_consumer.cpp @@ -4,6 +4,7 @@ #include "../ffmpeg_error.h" #include "../producer/util/util.h" +#include "../producer/filter/filter.h" #include #include @@ -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({ 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 graph_; core::monitor::subject subject_; - boost::filesystem::path path_; + std::string path_; + boost::filesystem::path full_path_; std::map options_; @@ -98,8 +169,8 @@ private: std::shared_ptr video_st_; std::shared_ptr 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 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(); }