]> git.sesse.net Git - casparcg/blob - core/consumer/syncto/syncto_consumer.cpp
Created a consumer that provides sync to a channel based on the pace of another chann...
[casparcg] / core / consumer / syncto / syncto_consumer.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
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.
10 *
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.
15 *
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/>.
18 *
19 * Author: Helge Norberg, helge.norberg@svt.se
20 */
21
22 #include "../../StdAfx.h"
23
24 #include "syncto_consumer.h"
25
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"
33
34 #include <common/semaphore.h>
35
36 #include <boost/lexical_cast.hpp>
37 #include <boost/property_tree/ptree.hpp>
38
39 #include <future>
40
41 namespace caspar { namespace core { namespace syncto {
42
43 void verify_cyclic_reference(int self_channel_index, const spl::shared_ptr<video_channel>& other_channel);
44
45 class syncto_consumer : public frame_consumer
46 {
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;
52 public:
53         syncto_consumer(spl::shared_ptr<video_channel> other_channel)
54                 : other_channel_(std::move(other_channel))
55         {
56         }
57
58         void initialize(const video_format_desc& format_desc, const audio_channel_layout& channel_layout, int channel_index) override
59         {
60                 verify_cyclic_reference(channel_index, other_channel_);
61
62                 self_channel_index_     = channel_index;
63                 tick_subscription_      = other_channel_->add_tick_listener([=]
64                 {
65                         frames_to_render_.release();
66                 });
67         }
68
69         std::future<bool> send(const_frame frame) override
70         {
71                 auto task = spl::make_shared<std::packaged_task<bool ()>>([=] { return true; });
72
73                 frames_to_render_.acquire(1, [task]
74                 {
75                         (*task)();
76                 });
77
78                 return task->get_future();
79         }
80
81         monitor::subject& monitor_output() override
82         {
83                 return monitor_subject_;
84         }
85
86         std::wstring print() const override
87         {
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"]";
90                 else
91                         return L"syncto[" + boost::lexical_cast<std::wstring>(other_channel_->index()) + L"]";
92         }
93
94         std::wstring name() const override
95         {
96                 return L"syncto";
97         }
98
99         boost::property_tree::wptree info() const override
100         {
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());
104                 return info;
105         }
106
107         bool has_synchronization_clock() const override
108         {
109                 return true;
110         }
111
112         int buffer_depth() const override
113         {
114                 return -1;
115         }
116
117         int index() const override
118         {
119                 return 70000;
120         }
121
122         int64_t presentation_frame_age_millis() const override
123         {
124                 return 0;
125         }
126
127         spl::shared_ptr<video_channel> other_channel() const
128         {
129                 return other_channel_;
130         }
131 };
132
133 void verify_cyclic_reference(int self_channel_index, const spl::shared_ptr<video_channel>& other_channel)
134 {
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"));
138
139         for (auto& consumer : other_channel->output().get_consumers())
140         {
141                 auto raw_consumer       = consumer->unwrapped();
142                 auto syncto                     = dynamic_cast<const syncto_consumer*>(raw_consumer);
143
144                 if (syncto)
145                         verify_cyclic_reference(self_channel_index, syncto->other_channel());
146         }
147 }
148
149 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
150 {
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");
156 }
157
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)
162 {
163         if (params.size() < 1 || !boost::iequals(params.at(0), L"SYNCTO"))
164                 return core::frame_consumer::empty();
165
166         auto channel_id = boost::lexical_cast<int>(params.at(1));
167         auto channel    = channels.at(channel_id - 1);
168
169         return spl::make_shared<syncto_consumer>(channel);
170 }
171
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)
176 {
177         auto channel_id = ptree.get<int>(L"channel-id");
178
179         return spl::make_shared<syncto_consumer>(channels.at(channel_id - 1));
180 }
181
182 void init(module_dependencies dependencies)
183 {
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);
186 }
187
188 }}}