]> git.sesse.net Git - casparcg/blob - modules/newtek/consumer/newtek_ivga_consumer.cpp
Added graph to iVGA consumer
[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/parameters/parameters.h>
28 #include <core/video_format.h>
29 #include <core/mixer/read_frame.h>
30
31 #include <common/utility/assert.h>
32 #include <common/concurrency/executor.h>
33 #include <common/diagnostics/graph.h>
34
35 #include <boost/algorithm/string.hpp>
36 #include <boost/timer.hpp>
37
38 #include <tbb/atomic.h>
39
40 #include "../util/air_send.h"
41
42 namespace caspar { namespace newtek {
43
44 struct newtek_ivga_consumer : public core::frame_consumer
45 {
46         std::shared_ptr<void>                   air_send_;
47         core::video_format_desc                 format_desc_;
48         core::channel_layout                    channel_layout_;
49         executor                                                executor_;
50         tbb::atomic<bool>                               connected_;
51
52         safe_ptr<diagnostics::graph>    graph_;
53         boost::timer                                    tick_timer_;
54         boost::timer                                    frame_timer_;
55
56 public:
57
58         newtek_ivga_consumer(core::channel_layout channel_layout)
59                 : executor_(print())
60                 , channel_layout_(channel_layout)
61         {
62                 if (!airsend::is_available())
63                         BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(narrow(airsend::dll_name()) + " not available"));
64
65                 connected_ = false;
66
67                 graph_->set_text(print());
68                 graph_->set_color("frame-time", diagnostics::color(0.5f, 1.0f, 0.2f));
69                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));
70                 diagnostics::register_graph(graph_);
71         }
72         
73         ~newtek_ivga_consumer()
74         {
75         }
76
77         // frame_consumer
78         
79         virtual void initialize(const core::video_format_desc& format_desc, int channel_index) override
80         {
81                 air_send_.reset(
82                         airsend::create(
83                                 format_desc.width,
84                                 format_desc.height,
85                                 format_desc.time_scale,
86                                 format_desc.duration,
87                                 format_desc.field_mode == core::field_mode::progressive,
88                                 static_cast<float>(format_desc.square_width) / static_cast<float>(format_desc.square_height),
89                                 true,
90                                 channel_layout_.num_channels,
91                                 format_desc.audio_sample_rate),
92                                 airsend::destroy);
93
94                 CASPAR_VERIFY(air_send_);
95
96                 format_desc_ = format_desc;
97
98                 CASPAR_LOG(info) << print() << L" Successfully Initialized.";   
99         }
100
101         virtual boost::unique_future<bool> send(const safe_ptr<core::read_frame>& frame) override
102         {
103                 CASPAR_VERIFY(format_desc_.height * format_desc_.width * 4 == static_cast<unsigned>(frame->image_data().size()));
104
105                 return executor_.begin_invoke([=]() -> bool
106                 {                       
107                         graph_->set_value("tick-time", tick_timer_.elapsed() * format_desc_.fps * 0.5);
108                         tick_timer_.restart();
109                         frame_timer_.restart();
110
111                         // AUDIO
112
113                         std::vector<int16_t, tbb::cache_aligned_allocator<int16_t>> audio_buffer;
114
115                         if (core::needs_rearranging(
116                                         frame->multichannel_view(),
117                                         channel_layout_,
118                                         channel_layout_.num_channels))
119                         {
120                                 core::audio_buffer downmixed;
121
122                                 downmixed.resize(
123                                                 frame->multichannel_view().num_samples() 
124                                                                 * channel_layout_.num_channels,
125                                                 0);
126
127                                 auto dest_view = core::make_multichannel_view<int32_t>(
128                                                 downmixed.begin(), downmixed.end(), channel_layout_);
129
130                                 core::rearrange_or_rearrange_and_mix(
131                                                 frame->multichannel_view(),
132                                                 dest_view,
133                                                 core::default_mix_config_repository());
134
135                                 audio_buffer = core::audio_32_to_16(downmixed);
136                         }
137                         else
138                         {
139                                 audio_buffer = core::audio_32_to_16(frame->audio_data());
140                         }
141
142                         airsend::add_audio(air_send_.get(), audio_buffer.data(), audio_buffer.size() / channel_layout_.num_channels);
143
144                         // VIDEO
145
146                         connected_ = airsend::add_frame_bgra(air_send_.get(), frame->image_data().begin());
147
148                         graph_->set_text(print());
149                         graph_->set_value("frame-time", frame_timer_.elapsed() * format_desc_.fps * 0.5);
150                         
151                         return true;
152                 });
153         }
154                 
155         virtual std::wstring print() const override
156         {
157                 return connected_ ?
158                                 L"newtek-ivga[connected]" : L"newtek-ivga[not connected]";
159         }
160
161         virtual boost::property_tree::wptree info() const override
162         {
163                 boost::property_tree::wptree info;
164                 info.add(L"type", L"newtek-ivga-consumer");
165                 info.add(L"connected", connected_ ? L"true" : L"false");
166                 return info;
167         }
168
169         virtual size_t buffer_depth() const override
170         {
171                 return 0;
172         }
173         
174         virtual int index() const override
175         {
176                 return 900;
177         }
178
179         virtual int64_t presentation_frame_age_millis() const override
180         {
181                 return 0;
182         }
183
184         virtual bool has_synchronization_clock() const override
185         {
186                 return connected_;
187         }
188 };      
189
190 safe_ptr<core::frame_consumer> create_ivga_consumer(const core::parameters& params)
191 {
192         if(params.size() < 1 || params[0] != L"NEWTEK_IVGA")
193                 return core::frame_consumer::empty();
194                 
195         const auto channel_layout = core::default_channel_layout_repository()
196                 .get_by_name(
197                         params.get(L"CHANNEL_LAYOUT", L"STEREO"));
198
199         return make_safe<newtek_ivga_consumer>(channel_layout);
200 }
201
202 safe_ptr<core::frame_consumer> create_ivga_consumer(const boost::property_tree::wptree& ptree) 
203 {       
204         const auto channel_layout =
205                 core::default_channel_layout_repository()
206                         .get_by_name(
207                                 boost::to_upper_copy(ptree.get(L"channel-layout", L"STEREO")));
208
209         return make_safe<newtek_ivga_consumer>(channel_layout);
210 }
211
212 }}