2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
4 * This file is part of CasparCG (www.casparcg.com).
6 * CasparCG is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * CasparCG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
19 * Author: Helge Norberg, helge.norberg@svt.se
22 #include "../../StdAfx.h"
24 #include "syncto_consumer.h"
26 #include "../frame_consumer.h"
27 #include "../../frame/frame.h"
28 #include "../../help/help_sink.h"
29 #include "../../module_dependencies.h"
30 #include "../../monitor/monitor.h"
31 #include "../../video_channel.h"
32 #include "../output.h"
34 #include <common/semaphore.h>
36 #include <boost/lexical_cast.hpp>
37 #include <boost/property_tree/ptree.hpp>
41 namespace caspar { namespace core { namespace syncto {
43 void verify_cyclic_reference(int self_channel_index, const spl::shared_ptr<video_channel>& other_channel);
45 class syncto_consumer : public frame_consumer
47 monitor::subject monitor_subject_;
48 spl::shared_ptr<video_channel> other_channel_;
49 semaphore frames_to_render_ { 0 };
50 std::shared_ptr<void> tick_subscription_;
51 int self_channel_index_ = -1;
53 syncto_consumer(spl::shared_ptr<video_channel> other_channel)
54 : other_channel_(std::move(other_channel))
58 void initialize(const video_format_desc& format_desc, const audio_channel_layout& channel_layout, int channel_index) override
60 verify_cyclic_reference(channel_index, other_channel_);
62 self_channel_index_ = channel_index;
63 tick_subscription_ = other_channel_->add_tick_listener([=]
65 frames_to_render_.release();
69 std::future<bool> send(const_frame frame) override
71 auto task = spl::make_shared<std::packaged_task<bool ()>>([=] { return true; });
73 frames_to_render_.acquire(1, [task]
78 return task->get_future();
81 monitor::subject& monitor_output() override
83 return monitor_subject_;
86 std::wstring print() const override
88 if (self_channel_index_ != -1)
89 return L"sync[" + boost::lexical_cast<std::wstring>(self_channel_index_) + L"]to[" + boost::lexical_cast<std::wstring>(other_channel_->index()) + L"]";
91 return L"syncto[" + boost::lexical_cast<std::wstring>(other_channel_->index()) + L"]";
94 std::wstring name() const override
99 boost::property_tree::wptree info() const override
101 boost::property_tree::wptree info;
102 info.add(L"type", L"syncto-consumer");
103 info.add(L"channel-to-sync-to", other_channel_->index());
107 bool has_synchronization_clock() const override
112 int buffer_depth() const override
117 int index() const override
122 int64_t presentation_frame_age_millis() const override
127 spl::shared_ptr<video_channel> other_channel() const
129 return other_channel_;
133 void verify_cyclic_reference(int self_channel_index, const spl::shared_ptr<video_channel>& other_channel)
135 if (self_channel_index == other_channel->index())
136 CASPAR_THROW_EXCEPTION(user_error() << msg_info(
137 L"Cannot create syncto consumer where source channel and destination channel is the same or indirectly related"));
139 for (auto& consumer : other_channel->output().get_consumers())
141 auto raw_consumer = consumer->unwrapped();
142 auto syncto = dynamic_cast<const syncto_consumer*>(raw_consumer);
145 verify_cyclic_reference(self_channel_index, syncto->other_channel());
149 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
151 sink.short_description(L"Lets a channel provide sync to another.");
152 sink.syntax(L"SYNCTO [other_channel:int]");
153 sink.para()->text(L"Provides sync to its own channel based on the rendering pace of the specified channel.");
154 sink.para()->text(L"Examples:");
155 sink.example(L">> ADD 1 SYNCTO 2");
158 spl::shared_ptr<core::frame_consumer> create_consumer(
159 const std::vector<std::wstring>& params,
160 core::interaction_sink*,
161 std::vector<spl::shared_ptr<video_channel>> channels)
163 if (params.size() < 1 || !boost::iequals(params.at(0), L"SYNCTO"))
164 return core::frame_consumer::empty();
166 auto channel_id = boost::lexical_cast<int>(params.at(1));
167 auto channel = channels.at(channel_id - 1);
169 return spl::make_shared<syncto_consumer>(channel);
172 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
173 const boost::property_tree::wptree& ptree,
174 core::interaction_sink*,
175 std::vector<spl::shared_ptr<video_channel>> channels)
177 auto channel_id = ptree.get<int>(L"channel-id");
179 return spl::make_shared<syncto_consumer>(channels.at(channel_id - 1));
182 void init(module_dependencies dependencies)
184 dependencies.consumer_registry->register_consumer_factory(L"syncto", &create_consumer, &describe_consumer);
185 dependencies.consumer_registry->register_preconfigured_consumer_factory(L"syncto", &create_preconfigured_consumer);