]> 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 81bf198f12edfb1738c3981b06b7b0b0fb99acf5..5e264cd2900a0f4ec269df4e1d9e468228c10e2e 100644 (file)
 #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/future.h>
+#include <common/semaphore.h>
 
 #include <boost/format.hpp>
 
 namespace caspar { namespace reroute {
 
 class layer_consumer : public core::write_frame_consumer
-{      
+{
        tbb::concurrent_bounded_queue<core::draw_frame> frame_buffer_;
-       std::promise<void>                                                              first_frame_promise_;
-       std::future<void>                                                               first_frame_available_;
-       bool                                                                                    first_frame_reported_;
+       semaphore                                                                               frames_available_ { 0 };
+       int                                                                                             frames_delay_;
 
 public:
-       layer_consumer()
-               : first_frame_available_(first_frame_promise_.get_future())
-               , first_frame_reported_(false)
+       layer_consumer(int frames_delay)
+               : frames_delay_(frames_delay)
        {
-               frame_buffer_.set_capacity(2);
+               frame_buffer_.set_capacity(2 + frames_delay);
        }
 
        ~layer_consumer()
@@ -65,11 +64,8 @@ public:
        {
                bool pushed = frame_buffer_.try_push(src_frame);
 
-               if (pushed && !first_frame_reported_)
-               {
-                       first_frame_promise_.set_value();
-                       first_frame_reported_ = true;
-               }
+               if (pushed)
+                       frames_available_.release();
        }
 
        std::wstring print() const override
@@ -89,12 +85,57 @@ public:
 
        void block_until_first_frame_available()
        {
-               if (first_frame_available_.wait_for(std::chrono::seconds(2)) == std::future_status::timeout)
+               if (!frames_available_.try_acquire(1 + frames_delay_, boost::chrono::seconds(2)))
                        CASPAR_LOG(warning)
                                        << print() << L" Timed out while waiting for first frame";
        }
 };
 
+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_;
@@ -103,42 +144,72 @@ 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
+       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::empty())
-               , frame_number_(0)
+               , last_frame_(core::draw_frame::late())
        {
                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";
        }
 
        // frame_producer
-                       
+
        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
+       {
+               return last_frame_;
+       }
 
        std::wstring print() const override
        {
@@ -166,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)
+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);
+       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);
 }
 
-}}
\ No newline at end of file
+}}