2 * Copyright 2013 NewTek
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: Robert Nagy, ronag@live.com
22 #include "../StdAfx.h"
24 #include "newtek_ivga_consumer.h"
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>
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>
41 #include <boost/algorithm/string.hpp>
42 #include <boost/timer.hpp>
43 #include <boost/property_tree/ptree.hpp>
45 #include <tbb/atomic.h>
47 #include "../util/air_send.h"
49 namespace caspar { namespace newtek {
51 struct newtek_ivga_consumer : public core::frame_consumer
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();
58 tbb::atomic<bool> connected_;
59 spl::shared_ptr<diagnostics::graph> graph_;
65 newtek_ivga_consumer()
68 if (!airsend::is_available())
69 CASPAR_THROW_EXCEPTION(not_supported() << msg_info(airsend::dll_name() + L" not available"));
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_);
80 ~newtek_ivga_consumer()
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
95 format_desc.time_scale,
97 format_desc.field_mode == core::field_mode::progressive,
98 static_cast<float>(format_desc.square_width) / static_cast<float>(format_desc.square_height),
100 channel_layout.num_channels,
101 format_desc.audio_sample_rate),
104 CASPAR_VERIFY(air_send_);
106 format_desc_ = format_desc;
107 channel_layout_ = channel_layout;
110 std::future<bool> schedule_send(core::const_frame frame)
112 return executor_.begin_invoke([=]() -> bool
114 graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
115 tick_timer_.restart();
116 frame_timer_.restart();
120 auto audio_buffer = core::audio_32_to_16(frame.audio_data());
122 airsend::add_audio(air_send_.get(), audio_buffer.data(), static_cast<int>(audio_buffer.size()) / channel_layout_.num_channels);
126 connected_ = airsend::add_frame_bgra(air_send_.get(), frame.image_data().begin());
128 graph_->set_text(print());
129 graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);
135 virtual std::future<bool> send(core::const_frame frame) override
137 CASPAR_VERIFY(format_desc_.height * format_desc_.width * 4 == frame.image_data().size());
139 if (executor_.size() > 0 || executor_.is_currently_in_task())
141 graph_->set_tag(diagnostics::tag_severity::WARNING, "dropped-frame");
143 return make_ready_future(true);
146 schedule_send(std::move(frame));
148 return make_ready_future(true);
151 virtual core::monitor::subject& monitor_output() override
153 return monitor_subject_;
156 virtual std::wstring print() const override
159 L"newtek-ivga[connected]" : L"newtek-ivga[not connected]";
162 virtual std::wstring name() const override
164 return L"newtek-ivga";
167 virtual boost::property_tree::wptree info() const override
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");
175 virtual int buffer_depth() const override
180 virtual int index() const override
185 virtual int64_t presentation_frame_age_millis() const override
190 virtual bool has_synchronization_clock() const override
196 void describe_ivga_consumer(core::help_sink& sink, const core::help_repository& repo)
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");
205 spl::shared_ptr<core::frame_consumer> create_ivga_consumer(const std::vector<std::wstring>& params, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
207 if (params.size() < 1 || !boost::iequals(params.at(0), L"NEWTEK_IVGA"))
208 return core::frame_consumer::empty();
210 return spl::make_shared<newtek_ivga_consumer>();
213 spl::shared_ptr<core::frame_consumer> create_preconfigured_ivga_consumer(const boost::property_tree::wptree& ptree, core::interaction_sink*, std::vector<spl::shared_ptr<core::video_channel>> channels)
215 return spl::make_shared<newtek_ivga_consumer>();