]> git.sesse.net Git - casparcg/blob - modules/newtek/consumer/newtek_ivga_consumer.cpp
feade373c45b12e5f5f7e752cb8e012cecc9e9ff
[casparcg] / modules / newtek / consumer / newtek_ivga_consumer.cpp
1 /*
2 * Copyright 2013 NewTek
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: Robert Nagy, ronag@live.com
20 */
21  
22 #include "../StdAfx.h"
23
24 #include "newtek_ivga_consumer.h"
25
26 #include <core/consumer/frame_consumer.h>
27 #include <core/video_format.h>
28 #include <core/frame/frame.h>
29 #include <core/frame/audio_channel_layout.h>
30 #include <core/mixer/audio/audio_util.h>
31 #include <core/monitor/monitor.h>
32 #include <core/help/help_sink.h>
33 #include <core/help/help_repository.h>
34
35 #include <common/assert.h>
36 #include <common/executor.h>
37 #include <common/diagnostics/graph.h>
38 #include <common/timer.h>
39 #include <common/param.h>
40
41 #include <boost/algorithm/string.hpp>
42 #include <boost/timer.hpp>
43 #include <boost/property_tree/ptree.hpp>
44
45 #include <tbb/atomic.h>
46
47 #include "../util/air_send.h"
48
49 namespace caspar { namespace newtek {
50
51 struct newtek_ivga_consumer : public core::frame_consumer
52 {
53         core::monitor::subject                          monitor_subject_;
54         std::shared_ptr<void>                           air_send_;
55         core::video_format_desc                         format_desc_;
56         core::audio_channel_layout                      channel_layout_         = core::audio_channel_layout::invalid();
57         executor                                                        executor_;
58         tbb::atomic<bool>                                       connected_;
59         spl::shared_ptr<diagnostics::graph>     graph_;
60         timer                                                           tick_timer_;
61         timer                                                           frame_timer_;
62
63 public:
64
65         newtek_ivga_consumer()
66                 : executor_(print())
67         {
68                 if (!airsend::is_available())
69                         CASPAR_THROW_EXCEPTION(not_supported() << msg_info(airsend::dll_name() + L" not available"));
70
71                 connected_ = false;
72
73                 graph_->set_text(print());
74                 graph_->set_color("frame-time", diagnostics::color(0.5f, 1.0f, 0.2f));
75                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
76                 graph_->set_color("dropped-frame", diagnostics::color(0.3f, 0.6f, 0.3f));
77                 diagnostics::register_graph(graph_);
78         }
79         
80         ~newtek_ivga_consumer()
81         {
82         }
83
84         // frame_consumer
85         
86         virtual void initialize(
87                         const core::video_format_desc& format_desc,
88                         const core::audio_channel_layout& channel_layout,
89                         int channel_index) override
90         {
91                 air_send_.reset(
92                         airsend::create(
93                                 format_desc.width,
94                                 format_desc.height,
95                                 format_desc.time_scale,
96                                 format_desc.duration,
97                                 format_desc.field_mode == core::field_mode::progressive,
98                                 static_cast<float>(format_desc.square_width) / static_cast<float>(format_desc.square_height),
99                                 true,
100                                 channel_layout.num_channels,
101                                 format_desc.audio_sample_rate),
102                                 airsend::destroy);
103
104                 CASPAR_VERIFY(air_send_);
105
106                 format_desc_    = format_desc;
107                 channel_layout_ = channel_layout;
108         }
109
110         std::future<bool> schedule_send(core::const_frame frame)
111         {
112                 return executor_.begin_invoke([=]() -> bool
113                 {
114                         graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
115                         tick_timer_.restart();
116                         frame_timer_.restart();
117
118                         // AUDIO
119
120                         auto audio_buffer = core::audio_32_to_16(frame.audio_data());
121
122                         airsend::add_audio(air_send_.get(), audio_buffer.data(), static_cast<int>(audio_buffer.size()) / channel_layout_.num_channels);
123
124                         // VIDEO
125
126                         connected_ = airsend::add_frame_bgra(air_send_.get(), frame.image_data().begin());
127
128                         graph_->set_text(print());
129                         graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);
130
131                         return true;
132                 });
133         }
134
135         virtual std::future<bool> send(core::const_frame frame) override
136         {
137                 CASPAR_VERIFY(format_desc_.height * format_desc_.width * 4 == frame.image_data().size());
138
139                 if (executor_.size() > 0 || executor_.is_currently_in_task())
140                 {
141                         graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
142
143                         return make_ready_future(true);
144                 }
145
146                 schedule_send(std::move(frame));
147
148                 return make_ready_future(true);
149         }
150
151         virtual core::monitor::subject& monitor_output() override
152         {
153                 return monitor_subject_;
154         }
155
156         virtual std::wstring print() const override
157         {
158                 return connected_ ?
159                                 L"newtek-ivga[connected]" : L"newtek-ivga[not connected]";
160         }
161
162         virtual std::wstring name() const override
163         {
164                 return L"newtek-ivga";
165         }
166
167         virtual boost::property_tree::wptree info() const override
168         {
169                 boost::property_tree::wptree info;
170                 info.add(L"type", L"newtek-ivga-consumer");
171                 info.add(L"connected", connected_ ? L"true" : L"false");
172                 return info;
173         }
174
175         virtual int buffer_depth() const override
176         {
177                 return -1;
178         }
179         
180         virtual int index() const override
181         {
182                 return 900;
183         }
184
185         virtual int64_t presentation_frame_age_millis() const override
186         {
187                 return 0;
188         }
189
190         virtual bool has_synchronization_clock() const override
191         {
192                 return false;
193         }
194 };      
195
196 void describe_ivga_consumer(core::help_sink& sink, const core::help_repository& repo)
197 {
198         sink.short_description(L"A consumer for streaming a channel to a NewTek TriCaster via iVGA/AirSend protocol.");
199         sink.syntax(L"NEWTEK_IVGA");
200         sink.para()->text(L"A consumer for streaming a channel to a NewTek TriCaster via iVGA/AirSend protocol.");
201         sink.para()->text(L"Examples:");
202         sink.example(L">> ADD 1 NEWTEK_IVGA");
203 }
204
205 spl::shared_ptr<core::frame_consumer> create_ivga_consumer(const std::vector<std::wstring>& params, core::interaction_sink*)
206 {
207         if (params.size() < 1 || !boost::iequals(params.at(0), L"NEWTEK_IVGA"))
208                 return core::frame_consumer::empty();
209
210         return spl::make_shared<newtek_ivga_consumer>();
211 }
212
213 spl::shared_ptr<core::frame_consumer> create_preconfigured_ivga_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink*)
214 {       
215         return spl::make_shared<newtek_ivga_consumer>();
216 }
217
218 }}