]> git.sesse.net Git - casparcg/blob - modules/bluefish/consumer/bluefish_consumer.cpp
50529d9f10360d15ad3e52d0e352795424d59134
[casparcg] / modules / bluefish / consumer / bluefish_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: Robert Nagy, ronag89@gmail.com
20 */
21  
22 #include "../StdAfx.h"
23
24 #include "bluefish_consumer.h"
25 #include "../util/blue_velvet.h"
26 #include "../util/memory.h"
27
28 #include <core/video_format.h>
29 #include <core/frame/frame.h>
30 #include <core/frame/audio_channel_layout.h>
31 #include <core/help/help_repository.h>
32 #include <core/help/help_sink.h>
33
34 #include <common/executor.h>
35 #include <common/diagnostics/graph.h>
36 #include <common/array.h>
37 #include <common/memshfl.h>
38 #include <common/param.h>
39
40 #include <core/consumer/frame_consumer.h>
41 #include <core/mixer/audio/audio_util.h>
42
43 #include <tbb/concurrent_queue.h>
44 #include <tbb/atomic.h>
45
46 #include <common/assert.h>
47 #include <boost/lexical_cast.hpp>
48 #include <boost/timer.hpp>
49 #include <boost/range/algorithm.hpp>
50 #include <boost/property_tree/ptree.hpp>
51 #include <boost/algorithm/string.hpp>
52
53 #include <asmlib.h>
54
55 #include <memory>
56 #include <array>
57
58 namespace caspar { namespace bluefish { 
59                         
60 struct bluefish_consumer : boost::noncopyable
61 {
62         spl::shared_ptr<CBlueVelvet4>                                           blue_;
63         const unsigned int                                                                      device_index_;
64         const core::video_format_desc                                           format_desc_;
65         const core::audio_channel_layout                                        channel_layout_;
66         core::audio_channel_remapper                                            channel_remapper_;
67         const int                                                                                       channel_index_;
68
69         const std::wstring                                                                      model_name_;
70
71         spl::shared_ptr<diagnostics::graph>                                     graph_;
72         boost::timer                                                                            frame_timer_;
73         boost::timer                                                                            tick_timer_;
74         boost::timer                                                                            sync_timer_;    
75                         
76         unsigned int                                                                            vid_fmt_;
77
78         std::array<blue_dma_buffer_ptr, 4>                                      reserved_frames_;       
79         tbb::concurrent_bounded_queue<core::const_frame>        frame_buffer_;
80         tbb::atomic<int64_t>                                                            presentation_delay_millis_;
81         core::const_frame                                                                       previous_frame_                         = core::const_frame::empty();
82
83         const bool                                                                                      embedded_audio_;
84         const bool                                                                                      key_only_;
85                 
86         executor                                                                                        executor_;
87 public:
88         bluefish_consumer(
89                         const core::video_format_desc& format_desc,
90                         const core::audio_channel_layout& in_channel_layout,
91                         const core::audio_channel_layout& out_channel_layout,
92                         int device_index,
93                         bool embedded_audio,
94                         bool key_only,
95                         int channel_index)
96                 : blue_(create_blue(device_index))
97                 , device_index_(device_index)
98                 , format_desc_(format_desc)
99                 , channel_layout_(out_channel_layout)
100                 , channel_remapper_(in_channel_layout, out_channel_layout)
101                 , channel_index_(channel_index)
102                 , model_name_(get_card_desc(*blue_))
103                 , vid_fmt_(get_video_mode(*blue_, format_desc))
104                 , embedded_audio_(embedded_audio)
105                 , key_only_(key_only)
106                 , executor_(print())
107         {
108                 executor_.set_capacity(1);
109                 presentation_delay_millis_ = 0;
110
111                 graph_->set_color("tick-time", diagnostics::color(0.0f, 0.6f, 0.9f));   
112                 graph_->set_color("sync-time", diagnostics::color(1.0f, 0.0f, 0.0f));
113                 graph_->set_color("frame-time", diagnostics::color(0.5f, 1.0f, 0.2f));
114                 graph_->set_text(print());
115                 diagnostics::register_graph(graph_);
116                         
117                 //Setting output Video mode
118                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_MODE, vid_fmt_))) 
119                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to set videomode."));
120
121                 //Select Update Mode for output
122                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_UPDATE_TYPE, UPD_FMT_FRAME))) 
123                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to set update type."));
124         
125                 disable_video_output();
126
127                 //Enable dual link output
128                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_DUAL_LINK_OUTPUT, 1)))
129                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to enable dual link."));
130
131                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_DUAL_LINK_OUTPUT_SIGNAL_FORMAT_TYPE, Signal_FormatType_4224)))
132                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to set dual link format type to 4:2:2:4."));
133                         
134                 //Select output memory format
135                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_MEMORY_FORMAT, MEM_FMT_ARGB_PC))) 
136                         CASPAR_THROW_EXCEPTION(caspar_exception() << msg_info(print() + L" Failed to set memory format."));
137                 
138                 //Select image orientation
139                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_IMAGE_ORIENTATION, ImageOrientation_Normal)))
140                         CASPAR_LOG(warning) << print() << L" Failed to set image orientation to normal.";       
141
142                 // Select data range
143                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_RGB_DATA_RANGE, CGR_RANGE))) 
144                         CASPAR_LOG(warning) << print() << L" Failed to set RGB data range to CGR.";     
145                 
146                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_PREDEFINED_COLOR_MATRIX, vid_fmt_ == VID_FMT_PAL ? MATRIX_601_CGR : MATRIX_709_CGR)))
147                         CASPAR_LOG(warning) << print() << L" Failed to set colormatrix to " << (vid_fmt_ == VID_FMT_PAL ? L"601 CGR" : L"709 CGR") << L".";
148
149                 if(!embedded_audio_)
150                 {
151                         if(BLUE_FAIL(set_card_property(blue_, EMBEDEDDED_AUDIO_OUTPUT, 0))) 
152                                 CASPAR_LOG(warning) << TEXT("BLUECARD ERROR: Failed to disable embedded audio.");                       
153                         CASPAR_LOG(info) << print() << TEXT(" Disabled embedded-audio.");
154                 }
155                 else
156                 {
157                         ULONG audio_value =
158                                 EMBEDDED_AUDIO_OUTPUT | blue_emb_audio_group1_enable;
159
160                         if (channel_layout_.num_channels > 4)
161                                 audio_value |= blue_emb_audio_group2_enable;
162
163                         if (channel_layout_.num_channels > 8)
164                                 audio_value |= blue_emb_audio_group3_enable;
165
166                         if (channel_layout_.num_channels > 12)
167                                 audio_value |= blue_emb_audio_group4_enable;
168
169                         if(BLUE_FAIL(set_card_property(blue_, EMBEDEDDED_AUDIO_OUTPUT, audio_value)))
170                                 CASPAR_LOG(warning) << print() << TEXT(" Failed to enable embedded audio.");                    
171                         CASPAR_LOG(info) << print() << TEXT(" Enabled embedded-audio.");
172                 }
173                 
174                 if (blue_->has_output_key()) 
175                 {
176                         int dummy = TRUE; int v4444 = FALSE; int invert = FALSE; int white = FALSE;
177                         blue_->set_output_key(dummy, v4444, invert, white);
178                 }
179
180                 if(blue_->GetHDCardType(device_index_) != CRD_HD_INVALID) 
181                         blue_->Set_DownConverterSignalType(vid_fmt_ == VID_FMT_PAL ? SD_SDI : HD_SDI);  
182         
183                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_OUTPUT_ENGINE, VIDEO_ENGINE_FRAMESTORE))) 
184                         CASPAR_LOG(warning) << print() << TEXT(" Failed to set video engine."); 
185                 
186                 enable_video_output();
187                                                 
188                 int n = 0;
189                 boost::range::generate(reserved_frames_, [&]{return std::make_shared<blue_dma_buffer>(static_cast<int>(format_desc_.size), n++);});
190         }
191
192         ~bluefish_consumer()
193         {
194                 try
195                 {
196                         executor_.invoke([&]
197                         {
198                                 disable_video_output();
199                                 blue_->device_detach();         
200                         });
201                 }
202                 catch(...)
203                 {
204                         CASPAR_LOG_CURRENT_EXCEPTION();
205                 }
206         }
207         
208         void enable_video_output()
209         {
210                 if(!BLUE_PASS(set_card_property(blue_, VIDEO_BLACKGENERATOR, 0)))
211                         CASPAR_LOG(error) << print() << TEXT(" Failed to disable video output.");       
212         }
213
214         void disable_video_output()
215         {
216                 blue_->video_playback_stop(0,0);
217                 if(!BLUE_PASS(set_card_property(blue_, VIDEO_BLACKGENERATOR, 1)))
218                         CASPAR_LOG(error)<< print() << TEXT(" Failed to disable video output.");                
219         }
220         
221         std::future<bool> send(core::const_frame& frame)
222         {                                       
223                 return executor_.begin_invoke([=]() -> bool
224                 {
225                         try
226                         {       
227                                 display_frame(frame);                           
228                                 graph_->set_value("tick-time", static_cast<float>(tick_timer_.elapsed()*format_desc_.fps*0.5));
229                                 tick_timer_.restart();
230                         }
231                         catch(...)
232                         {
233                                 CASPAR_LOG_CURRENT_EXCEPTION();
234                         }
235
236                         return true;
237                 });
238         }
239
240         void display_frame(core::const_frame frame)
241         {
242                 // Sync
243
244                 sync_timer_.restart();
245                 unsigned long n_field = 0;
246                 blue_->wait_output_video_synch(UPD_FMT_FRAME, n_field);
247                 graph_->set_value("sync-time", sync_timer_.elapsed()*format_desc_.fps*0.5);
248                 
249                 frame_timer_.restart();         
250
251                 if (previous_frame_ != core::const_frame::empty())
252                         presentation_delay_millis_ = previous_frame_.get_age_millis();
253
254                 previous_frame_ = frame;
255
256                 // Copy to local buffers
257                 
258                 if(!frame.image_data().empty())
259                 {
260                         if(key_only_)                                           
261                                 aligned_memshfl(reserved_frames_.front()->image_data(), frame.image_data().begin(), frame.image_data().size(), 0x0F0F0F0F, 0x0B0B0B0B, 0x07070707, 0x03030303);
262                         else
263                                 A_memcpy(reserved_frames_.front()->image_data(), frame.image_data().begin(), frame.image_data().size());
264                 }
265                 else
266                         A_memset(reserved_frames_.front()->image_data(), 0, reserved_frames_.front()->image_size());
267                                                                 
268
269                 // Send and display
270
271                 if(embedded_audio_)
272                 {
273                         auto remapped_audio     = channel_remapper_.mix_and_rearrange(frame.audio_data());
274                         auto frame_audio        = core::audio_32_to_24(remapped_audio);
275                         encode_hanc(reinterpret_cast<BLUE_UINT32*>(reserved_frames_.front()->hanc_data()), 
276                                                 frame_audio.data(), 
277                                                 static_cast<int>(frame.audio_data().size()/channel_layout_.num_channels), 
278                                                 static_cast<int>(channel_layout_.num_channels));
279                                                                 
280                         blue_->system_buffer_write_async(const_cast<uint8_t*>(reserved_frames_.front()->image_data()), 
281                                                                                         static_cast<unsigned long>(reserved_frames_.front()->image_size()), 
282                                                                                         nullptr, 
283                                                                                         BlueImage_HANC_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_IMAGE));
284
285                         blue_->system_buffer_write_async(reserved_frames_.front()->hanc_data(),
286                                                                                         static_cast<unsigned long>(reserved_frames_.front()->hanc_size()), 
287                                                                                         nullptr,                 
288                                                                                         BlueImage_HANC_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_HANC));
289
290                         if(BLUE_FAIL(blue_->render_buffer_update(BlueBuffer_Image_HANC(reserved_frames_.front()->id()))))
291                                 CASPAR_LOG(warning) << print() << TEXT(" render_buffer_update failed.");
292                 }
293                 else
294                 {
295                         blue_->system_buffer_write_async(const_cast<uint8_t*>(reserved_frames_.front()->image_data()),
296                                                                                         static_cast<unsigned long>(reserved_frames_.front()->image_size()), 
297                                                                                         nullptr,                 
298                                                                                         BlueImage_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_IMAGE));
299                         
300                         if(BLUE_FAIL(blue_->render_buffer_update(BlueBuffer_Image(reserved_frames_.front()->id()))))
301                                 CASPAR_LOG(warning) << print() << TEXT(" render_buffer_update failed.");
302                 }
303
304                 boost::range::rotate(reserved_frames_, std::begin(reserved_frames_)+1);
305                 
306                 graph_->set_value("frame-time", static_cast<float>(frame_timer_.elapsed()*format_desc_.fps*0.5));
307         }
308
309         void encode_hanc(BLUE_UINT32* hanc_data, void* audio_data, int audio_samples, int audio_nchannels)
310         {       
311                 const auto sample_type = AUDIO_CHANNEL_24BIT | AUDIO_CHANNEL_LITTLEENDIAN;
312                 auto emb_audio_flag = blue_emb_audio_enable | blue_emb_audio_group1_enable;
313
314                 if (audio_nchannels > 4)
315                         emb_audio_flag |= blue_emb_audio_group2_enable;
316
317                 if (audio_nchannels > 8)
318                         emb_audio_flag |= blue_emb_audio_group3_enable;
319
320                 if (audio_nchannels > 12)
321                         emb_audio_flag |= blue_emb_audio_group4_enable;
322                 
323                 hanc_stream_info_struct hanc_stream_info;
324                 memset(&hanc_stream_info, 0, sizeof(hanc_stream_info));
325                 
326                 hanc_stream_info.AudioDBNArray[0] = -1;
327                 hanc_stream_info.AudioDBNArray[1] = -1;
328                 hanc_stream_info.AudioDBNArray[2] = -1;
329                 hanc_stream_info.AudioDBNArray[3] = -1;
330                 hanc_stream_info.hanc_data_ptr    = hanc_data;
331                 hanc_stream_info.video_mode               = vid_fmt_;           
332                 
333                 if (!is_epoch_card(*blue_))
334                         encode_hanc_frame(&hanc_stream_info, audio_data, audio_nchannels, audio_samples, sample_type, emb_audio_flag);  
335                 else
336                         encode_hanc_frame_ex(blue_->has_video_cardtype(), &hanc_stream_info, audio_data, audio_nchannels, audio_samples, sample_type, emb_audio_flag);
337         }
338         
339         std::wstring print() const
340         {
341                 return model_name_ + L" [" + boost::lexical_cast<std::wstring>(channel_index_) + L"-" + 
342                         boost::lexical_cast<std::wstring>(device_index_) + L"|" +  format_desc_.name + L"]";
343         }
344
345         int64_t presentation_delay_millis() const
346         {
347                 return presentation_delay_millis_;
348         }
349 };
350
351 struct bluefish_consumer_proxy : public core::frame_consumer
352 {
353         core::monitor::subject                          monitor_subject_;
354
355         std::unique_ptr<bluefish_consumer>      consumer_;
356         const int                                                       device_index_;
357         const bool                                                      embedded_audio_;
358         const bool                                                      key_only_;
359
360         std::vector<int>                                        audio_cadence_;
361         core::video_format_desc                         format_desc_;
362         core::audio_channel_layout                      in_channel_layout_              = core::audio_channel_layout::invalid();
363         core::audio_channel_layout                      out_channel_layout_;
364
365 public:
366
367         bluefish_consumer_proxy(int device_index, bool embedded_audio, bool key_only, const core::audio_channel_layout& out_channel_layout)
368                 : device_index_(device_index)
369                 , embedded_audio_(embedded_audio)
370                 , key_only_(key_only)
371                 , out_channel_layout_(out_channel_layout)
372         {
373         }
374         
375         // frame_consumer
376         
377         void initialize(const core::video_format_desc& format_desc, const core::audio_channel_layout& channel_layout, int channel_index) override
378         {
379                 format_desc_            = format_desc;
380                 in_channel_layout_      = channel_layout;
381                 audio_cadence_          = format_desc.audio_cadence;
382
383                 if (out_channel_layout_ == core::audio_channel_layout::invalid())
384                         out_channel_layout_ = in_channel_layout_;
385
386                 consumer_.reset();
387                 consumer_.reset(new bluefish_consumer(format_desc, in_channel_layout_, out_channel_layout_, device_index_, embedded_audio_, key_only_, channel_index));
388         }
389         
390         std::future<bool> send(core::const_frame frame) override
391         {
392                 CASPAR_VERIFY(audio_cadence_.front() * in_channel_layout_.num_channels == static_cast<size_t>(frame.audio_data().size()));
393                 boost::range::rotate(audio_cadence_, std::begin(audio_cadence_)+1);
394                 return consumer_->send(frame);
395         }
396                 
397         std::wstring print() const override
398         {
399                 return consumer_ ? consumer_->print() : L"[bluefish_consumer]";
400         }
401
402         std::wstring name() const override
403         {
404                 return L"bluefish";
405         }
406
407         boost::property_tree::wptree info() const override
408         {
409                 boost::property_tree::wptree info;
410                 info.add(L"type", L"bluefish");
411                 info.add(L"key-only", key_only_);
412                 info.add(L"device", device_index_);
413                 info.add(L"embedded-audio", embedded_audio_);
414                 info.add(L"presentation-frame-age", presentation_frame_age_millis());
415                 return info;
416         }
417
418         int buffer_depth() const override
419         {
420                 return 1;
421         }
422         
423         int index() const override
424         {
425                 return 400 + device_index_;
426         }
427
428         int64_t presentation_frame_age_millis() const override
429         {
430                 return consumer_ ? consumer_->presentation_delay_millis() : 0;
431         }
432
433         core::monitor::subject& monitor_output()
434         {
435                 return monitor_subject_;
436         }
437 };      
438
439
440 void describe_consumer(core::help_sink& sink, const core::help_repository& repo)
441 {
442         sink.short_description(L"Sends video on an SDI output using Bluefish video cards.");
443         sink.syntax(L"BLUEFISH {[device_index:int]|1} {[embedded_audio:EMBEDDED_AUDIO]} {[key_only:KEY_ONLY]} {CHANNEL_LAYOUT [channel_layout:string]}");
444         sink.para()
445                 ->text(L"Sends video on an SDI output using Bluefish video cards. Multiple video cards can be ")
446                 ->text(L"installed in the same machine and used at the same time, they will be addressed via ")
447                 ->text(L"different ")->code(L"device_index")->text(L" parameters.");
448         sink.para()->text(L"Specify ")->code(L"embedded_audio")->text(L" to embed audio into the SDI signal.");
449         sink.para()
450                 ->text(L"Specifying ")->code(L"key_only")->text(L" will extract only the alpha channel from the ")
451                 ->text(L"channel. This is useful when you have two SDI video cards, and neither has native support ")
452                 ->text(L"for separate fill/key output");
453         sink.para()->text(L"Specify ")->code(L"channel_layout")->text(L" to output a different audio channel layout than the channel uses.");
454         sink.para()->text(L"Examples:");
455         sink.example(L">> ADD 1 BLUEFISH", L"uses the default device_index of 1.");
456         sink.example(L">> ADD 1 BLUEFISH 2", L"for device_index 2.");
457         sink.example(
458                 L">> ADD 1 BLUEFISH 1 EMBEDDED_AUDIO\n"
459                 L">> ADD 1 BLUEFISH 2 KEY_ONLY", L"uses device with index 1 as fill output with audio and device with index 2 as key output.");
460 }
461
462 spl::shared_ptr<core::frame_consumer> create_consumer(
463                 const std::vector<std::wstring>& params, core::interaction_sink*)
464 {
465         if(params.size() < 1 || !boost::iequals(params.at(0), L"BLUEFISH"))
466                 return core::frame_consumer::empty();
467
468         const auto device_index = params.size() > 1 ? boost::lexical_cast<int>(params.at(1)) : 1;
469
470         const auto embedded_audio       = contains_param(       L"EMBEDDED_AUDIO",      params);
471         const auto key_only                     = contains_param(       L"KEY_ONLY",            params);
472         const auto channel_layout       = get_param(            L"CHANNEL_LAYOUT",      params);
473
474         auto layout = core::audio_channel_layout::invalid();
475
476         if (!channel_layout.empty())
477         {
478                 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(channel_layout);
479
480                 if (!found_layout)
481                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + channel_layout + L" not found"));
482
483                 layout = *found_layout;
484         }
485
486         return spl::make_shared<bluefish_consumer_proxy>(device_index, embedded_audio, key_only, layout);
487 }
488
489 spl::shared_ptr<core::frame_consumer> create_preconfigured_consumer(
490                 const boost::property_tree::wptree& ptree, core::interaction_sink*)
491 {       
492         const auto device_index         = ptree.get(                                            L"device",                      1);
493         const auto embedded_audio       = ptree.get(                                            L"embedded-audio",      false);
494         const auto key_only                     = ptree.get(                                            L"key-only",            false);
495         const auto channel_layout       = ptree.get_optional<std::wstring>(     L"channel-layout");
496
497         auto layout = core::audio_channel_layout::invalid();
498
499         if (channel_layout)
500         {
501                 CASPAR_SCOPED_CONTEXT_MSG("/channel-layout")
502
503                 auto found_layout = core::audio_channel_layout_repository::get_default()->get_layout(*channel_layout);
504
505                 if (!found_layout)
506                         CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Channel layout " + *channel_layout + L" not found"));
507
508                 layout = *found_layout;
509         }
510
511         return spl::make_shared<bluefish_consumer_proxy>(device_index, embedded_audio, key_only, layout);
512 }
513
514 }}