#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()
{
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
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_;
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
{
{
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
+}}