]> git.sesse.net Git - casparcg/blobdiff - modules/reroute/producer/layer_producer.cpp
[layer_producer] Return the last known framerate when channel has been destroyed.
[casparcg] / modules / reroute / producer / layer_producer.cpp
index 9b5bad96c10234327b517674966cc7bd26214a69..5e264cd2900a0f4ec269df4e1d9e468228c10e2e 100644 (file)
@@ -30,6 +30,7 @@
 #include <core/frame/frame_factory.h>
 #include <core/producer/frame_producer.h>
 #include <core/producer/stage.h>
+#include <core/producer/framerate/framerate_producer.h>
 
 #include <common/except.h>
 #include <common/semaphore.h>
@@ -90,6 +91,51 @@ public:
        }
 };
 
+std::vector<core::draw_frame> extract_actual_frames(core::draw_frame original, core::field_mode field_order)
+{
+       if (field_order == core::field_mode::progressive)
+               return { std::move(original) };
+
+       struct field_extractor : public core::frame_visitor
+       {
+               std::vector<core::draw_frame>   fields;
+               core::field_mode                                field_order_first;
+               core::field_mode                                visiting_mode = core::field_mode::progressive;
+
+               field_extractor(core::field_mode field_order_)
+                       : field_order_first(field_order_)
+               {
+               }
+
+               void push(const core::frame_transform& transform) override
+               {
+                       if (visiting_mode == core::field_mode::progressive)
+                               visiting_mode = transform.image_transform.field_mode;
+               }
+
+               void visit(const core::const_frame& frame) override
+               {
+                       if (visiting_mode == field_order_first && fields.size() == 0)
+                               fields.push_back(core::draw_frame(core::const_frame(frame)));
+                       else if (visiting_mode != core::field_mode::progressive && visiting_mode != field_order_first && fields.size() == 1)
+                               fields.push_back(core::draw_frame(core::const_frame(frame)));
+               }
+
+               void pop() override
+               {
+                       visiting_mode = core::field_mode::progressive;
+               };
+       };
+
+       field_extractor extractor(field_order);
+       original.accept(extractor);
+
+       if (extractor.fields.size() == 2)
+               return std::move(extractor.fields);
+       else
+               return { std::move(original) };
+}
+
 class layer_producer : public core::frame_producer_base
 {
        core::monitor::subject                                          monitor_subject_;
@@ -98,29 +144,36 @@ class layer_producer : public core::frame_producer_base
        const spl::shared_ptr<layer_consumer>           consumer_;
 
        core::draw_frame                                                        last_frame_;
-       uint64_t                                                                        frame_number_;
+       mutable boost::rational<int>                            last_frame_rate_;
 
-       const spl::shared_ptr<core::video_channel>      channel_;
+       const std::weak_ptr<core::video_channel>        channel_;
        core::constraints                                                       pixel_constraints_;
 
+       tbb::atomic<bool>                                                       double_framerate_;
+       std::queue<core::draw_frame>                            frame_buffer_;
+
 public:
        explicit layer_producer(const spl::shared_ptr<core::video_channel>& channel, int layer, int frames_delay)
                : layer_(layer)
                , consumer_(spl::make_shared<layer_consumer>(frames_delay))
                , channel_(channel)
                , last_frame_(core::draw_frame::late())
-               , frame_number_(0)
        {
                pixel_constraints_.width.set(channel->video_format_desc().width);
                pixel_constraints_.height.set(channel->video_format_desc().height);
-               channel_->stage().add_layer_consumer(this, layer_, consumer_);
+               channel->stage().add_layer_consumer(this, layer_, consumer_);
                consumer_->block_until_first_frame_available();
+               double_framerate_ = false;
                CASPAR_LOG(info) << print() << L" Initialized";
        }
 
        ~layer_producer()
        {
-               channel_->stage().remove_layer_consumer(this, layer_);
+               auto channel = channel_.lock();
+
+               if (channel)
+                       channel->stage().remove_layer_consumer(this, layer_);
+
                CASPAR_LOG(info) << print() << L" Uninitialized";
        }
 
@@ -128,12 +181,29 @@ public:
 
        core::draw_frame receive_impl() override
        {
+               if (!frame_buffer_.empty())
+               {
+                       last_frame_ = frame_buffer_.front();
+                       frame_buffer_.pop();
+                       return last_frame_;
+               }
+
+               auto channel = channel_.lock();
+
+               if (!channel)
+                       return last_frame_;
+
                auto consumer_frame = consumer_->receive();
                if (consumer_frame == core::draw_frame::late())
                        return last_frame_;
 
-               frame_number_++;
-               return last_frame_ = consumer_frame;
+               auto actual_frames = extract_actual_frames(std::move(consumer_frame), channel->video_format_desc().field_mode);
+               double_framerate_ = actual_frames.size() == 2;
+
+               for (auto& frame : actual_frames)
+                       frame_buffer_.push(std::move(frame));
+
+               return receive_impl();
        }
 
        core::draw_frame last_frame() override
@@ -167,11 +237,36 @@ public:
        {
                return pixel_constraints_;
        }
+
+       boost::rational<int> current_framerate() const
+       {
+               auto channel = channel_.lock();
+
+               if (!channel)
+                       return last_frame_rate_;
+
+               last_frame_rate_ = double_framerate_
+                               ? channel->video_format_desc().framerate * 2
+                               : channel->video_format_desc().framerate;
+
+               return last_frame_rate_;
+       }
 };
 
-spl::shared_ptr<core::frame_producer> create_layer_producer(const spl::shared_ptr<core::video_channel>& channel, int layer, int frames_delay)
+spl::shared_ptr<core::frame_producer> create_layer_producer(
+               const spl::shared_ptr<core::video_channel>& channel,
+               int layer,
+               int frames_delay,
+               const core::video_format_desc& destination_mode)
 {
-       return spl::make_shared<layer_producer>(channel, layer, frames_delay);
+       auto producer = spl::make_shared<layer_producer>(channel, layer, frames_delay);
+
+       return core::create_framerate_producer(
+                       producer,
+                       std::bind(&layer_producer::current_framerate, producer),
+                       destination_mode.framerate,
+                       destination_mode.field_mode,
+                       destination_mode.audio_cadence);
 }
 
 }}