]> git.sesse.net Git - casparcg/blob - core/consumer/bluefish/bluefish_consumer.cpp
git-svn-id: https://casparcg.svn.sourceforge.net/svnroot/casparcg/server/branches...
[casparcg] / core / consumer / bluefish / bluefish_consumer.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\r
6 *    CasparCG is free software: you can redistribute it and/or modify\r
7 *    it under the terms of the GNU General Public License as published by\r
8 *    the Free Software Foundation, either version 3 of the License, or\r
9 *    (at your option) any later version.\r
10 *\r
11 *    CasparCG is distributed in the hope that it will be useful,\r
12 *    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 *    GNU General Public License for more details.\r
15 \r
16 *    You should have received a copy of the GNU General Public License\r
17 *    along with CasparCG.  If not, see <http://www.gnu.org/licenses/>.\r
18 *\r
19 */\r
20  \r
21 #include "../../StdAfx.h"\r
22 \r
23 #include "bluefish_consumer.h"\r
24 #include "util.h"\r
25 #include "memory.h"\r
26 \r
27 #include "../../mixer/frame/read_frame.h"\r
28 \r
29 #include <common/concurrency/executor.h>\r
30 \r
31 #include <tbb/concurrent_queue.h>\r
32 \r
33 #include <BlueVelvet4.h>\r
34 #include <BlueHancUtils.h>\r
35 \r
36 #include <windows.h>\r
37 \r
38 #include <boost/thread/once.hpp>\r
39 \r
40 #include <memory>\r
41 \r
42 namespace caspar { namespace core {\r
43         \r
44 CBlueVelvet4* (*BlueVelvetFactory4)();\r
45 BLUE_UINT32 (*encode_hanc_frame)(struct hanc_stream_info_struct * hanc_stream_ptr, void * audio_pcm_ptr,BLUE_UINT32 no_audio_ch,BLUE_UINT32 no_audio_samples,BLUE_UINT32 nTypeOfSample,BLUE_UINT32 emb_audio_flag);\r
46 BLUE_UINT32 (*encode_hanc_frame_ex)(BLUE_UINT32 card_type, struct hanc_stream_info_struct * hanc_stream_ptr, void * audio_pcm_ptr, BLUE_UINT32 no_audio_ch,     BLUE_UINT32 no_audio_samples, BLUE_UINT32 nTypeOfSample, BLUE_UINT32 emb_audio_flag);\r
47 \r
48 void blue_velvet_initialize()\r
49 {\r
50 #ifdef _DEBUG\r
51         auto module = LoadLibrary(L"BlueVelvet3_d.dll");\r
52 #else\r
53         auto module = LoadLibrary(L"BlueVelvet3.dll");\r
54 #endif\r
55         if(!module)\r
56                 BOOST_THROW_EXCEPTION(file_not_found() << msg_info("Could not find BlueVelvet3.dll"));\r
57         static std::shared_ptr<void> lib(module, FreeLibrary);\r
58         BlueVelvetFactory4 = reinterpret_cast<decltype(BlueVelvetFactory4)>(GetProcAddress(module, "BlueVelvetFactory4"));\r
59 }\r
60 \r
61 void blue_hanc_initialize()\r
62 {\r
63 #ifdef _DEBUG\r
64         auto module = LoadLibrary(L"BlueHancUtils_d.dll");\r
65 #else\r
66         auto module = LoadLibrary(L"BlueHancUtils.dll");\r
67 #endif\r
68         if(!module)\r
69                 BOOST_THROW_EXCEPTION(file_not_found() << msg_info("Could not find BlueHancUtils.dll"));\r
70         static std::shared_ptr<void> lib(module, FreeLibrary);\r
71         encode_hanc_frame = reinterpret_cast<decltype(encode_hanc_frame)>(GetProcAddress(module, "encode_hanc_frame"));\r
72         encode_hanc_frame_ex = reinterpret_cast<decltype(encode_hanc_frame_ex)>(GetProcAddress(module, "encode_hanc_frame_ex"));\r
73 }\r
74 \r
75 void blue_initialize()\r
76 {\r
77         blue_velvet_initialize();\r
78         blue_hanc_initialize();\r
79 }\r
80                 \r
81 struct bluefish_consumer::implementation : boost::noncopyable\r
82 {\r
83         boost::unique_future<void> active_;\r
84         executor executor_;\r
85                         \r
86         std::shared_ptr<CBlueVelvet4> blue_;\r
87         \r
88         const unsigned int device_index_;\r
89         const video_format_desc format_desc_;\r
90                 \r
91         unsigned long   mem_fmt_;\r
92         unsigned long   upd_fmt_;\r
93         EVideoMode              vid_fmt_; \r
94         unsigned long   res_fmt_; \r
95         unsigned long   engine_mode_;\r
96         \r
97         std::array<blue_dma_buffer_ptr, 3> reserved_frames_;    \r
98 \r
99         const bool embed_audio_;\r
100 \r
101 public:\r
102         implementation::implementation(const video_format_desc& format_desc, unsigned int device_index, bool embed_audio) \r
103                 : device_index_(device_index)\r
104                 , format_desc_(format_desc)\r
105                 , mem_fmt_(MEM_FMT_ARGB_PC)\r
106                 , upd_fmt_(UPD_FMT_FRAME)\r
107                 , vid_fmt_(VID_FMT_INVALID) \r
108                 , res_fmt_(RES_FMT_NORMAL) \r
109                 , engine_mode_(VIDEO_ENGINE_FRAMESTORE)         \r
110                 , embed_audio_(embed_audio)\r
111         {\r
112                 static boost::once_flag flag = BOOST_ONCE_INIT;\r
113                 boost::call_once(blue_initialize, flag);        \r
114                 \r
115                 blue_.reset(BlueVelvetFactory4());\r
116 \r
117                 if(BLUE_FAIL(blue_->device_attach(device_index_, FALSE))) \r
118                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to attach device."));\r
119         \r
120                 int videoCardType = blue_->has_video_cardtype();\r
121                 CASPAR_LOG(info) << TEXT("BLUECARD INFO: Card type: ") << get_card_desc(videoCardType) << TEXT(". (device ") << device_index_ << TEXT(")");\r
122         \r
123                 //void* pBlueDevice = blue_attach_to_device(1);\r
124                 //EBlueConnectorPropertySetting video_routing[1];\r
125                 //auto channel = BLUE_VIDEO_OUTPUT_CHANNEL_A;\r
126                 //video_routing[0].channel = channel;   \r
127                 //video_routing[0].propType = BLUE_CONNECTOR_PROP_SINGLE_LINK;\r
128                 //video_routing[0].connector = channel == BLUE_VIDEO_OUTPUT_CHANNEL_A ? BLUE_CONNECTOR_SDI_OUTPUT_A : BLUE_CONNECTOR_SDI_OUTPUT_B;\r
129                 //blue_set_connector_property(pBlueDevice, 1, video_routing);\r
130                 //blue_detach_from_device(&pBlueDevice);\r
131                 \r
132                 auto desiredVideoFormat = vid_fmt_from_video_format(format_desc_.format);\r
133                 int videoModeCount = blue_->count_video_mode();\r
134                 for(int videoModeIndex = 1; videoModeIndex <= videoModeCount; ++videoModeIndex) \r
135                 {\r
136                         EVideoMode videoMode = blue_->enum_video_mode(videoModeIndex);\r
137                         if(videoMode == desiredVideoFormat) \r
138                                 vid_fmt_ = videoMode;                   \r
139                 }\r
140                 if(vid_fmt_ == VID_FMT_INVALID)\r
141                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to set videomode."));\r
142                 \r
143                 // Set default video output channel\r
144                 //if(BLUE_FAIL(set_card_property(blue_, DEFAULT_VIDEO_OUTPUT_CHANNEL, channel)))\r
145                 //      CASPAR_LOG(error) << TEXT("BLUECARD ERROR: Failed to set default channel. (device ") << device_index_ << TEXT(")");\r
146 \r
147                 //Setting output Video mode\r
148                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_MODE, vid_fmt_))) \r
149                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to set videomode."));\r
150 \r
151                 //Select Update Mode for output\r
152                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_UPDATE_TYPE, upd_fmt_))) \r
153                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to set update type. "));\r
154         \r
155                 disable_video_output();\r
156 \r
157                 //Enable dual link output\r
158                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_DUAL_LINK_OUTPUT, 1)))\r
159                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to enable dual link."));\r
160 \r
161                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_DUAL_LINK_OUTPUT_SIGNAL_FORMAT_TYPE, Signal_FormatType_4224)))\r
162                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to set dual link format type to 4:2:2:4. (device " + boost::lexical_cast<std::string>(device_index_) + ")"));\r
163                         \r
164                 //Select output memory format\r
165                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_MEMORY_FORMAT, mem_fmt_))) \r
166                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to set memory format."));\r
167                 \r
168                 //Select image orientation\r
169                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_IMAGE_ORIENTATION, ImageOrientation_Normal)))\r
170                         CASPAR_LOG(error) << TEXT("BLUECARD ERROR: Failed to set image orientation to normal. (device ") << device_index_ << TEXT(")"); \r
171 \r
172                 // Select data range\r
173                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_RGB_DATA_RANGE, CGR_RANGE))) \r
174                         CASPAR_LOG(error) << TEXT("BLUECARD ERROR: Failed to set RGB data range to CGR. (device ") << device_index_ << TEXT(")");       \r
175                 \r
176                 if(BLUE_FAIL(set_card_property(blue_, VIDEO_PREDEFINED_COLOR_MATRIX, vid_fmt_ == VID_FMT_PAL ? MATRIX_601_CGR : MATRIX_709_CGR)))\r
177                         CASPAR_LOG(error) << TEXT("BLUECARD ERROR: Failed to set colormatrix to ") << (vid_fmt_ == VID_FMT_PAL ? TEXT("601 CGR") : TEXT("709 CGR")) << TEXT(". (device ") << device_index_ << TEXT(")");\r
178 \r
179                 if(!embed_audio_)\r
180                 {\r
181                         if(BLUE_FAIL(set_card_property(blue_, EMBEDEDDED_AUDIO_OUTPUT, 0))) \r
182                                 CASPAR_LOG(error) << TEXT("BLUECARD ERROR: Failed to disable embedded audio. (device ") << device_index_ << TEXT(")");                  \r
183                         CASPAR_LOG(info) << TEXT("BLUECARD INFO: Disabled embedded-audio. device ") << device_index_ << TEXT(")");\r
184                 }\r
185                 else\r
186                 {\r
187                         if(BLUE_FAIL(set_card_property(blue_, EMBEDEDDED_AUDIO_OUTPUT, blue_emb_audio_enable | blue_emb_audio_group1_enable))) \r
188                                 CASPAR_LOG(error) << TEXT("BLUECARD ERROR: Failed to enable embedded audio. (device ") << device_index_ << TEXT(")");                   \r
189                         CASPAR_LOG(info) << TEXT("BLUECARD INFO: Enabled embedded-audio. device ") << device_index_ << TEXT(")");\r
190                 }\r
191 \r
192                 CASPAR_LOG(info) << TEXT("BLUECARD INFO: Successfully configured bluecard for ") << format_desc_ << TEXT(". (device ") << device_index_ << TEXT(")");\r
193 \r
194                 if (blue_->has_output_key()) \r
195                 {\r
196                         int dummy = TRUE; int v4444 = FALSE; int invert = FALSE; int white = FALSE;\r
197                         blue_->set_output_key(dummy, v4444, invert, white);\r
198                 }\r
199 \r
200                 if(blue_->GetHDCardType(device_index_) != CRD_HD_INVALID) \r
201                         blue_->Set_DownConverterSignalType(vid_fmt_ == VID_FMT_PAL ? SD_SDI : HD_SDI);  \r
202         \r
203                 if(BLUE_FAIL(blue_->set_video_engine(engine_mode_)))\r
204                         BOOST_THROW_EXCEPTION(bluefish_exception() << msg_info("BLUECARD ERROR: Failed to set vido engine."));\r
205 \r
206                 enable_video_output();\r
207                                                 \r
208                 for(size_t n = 0; n < reserved_frames_.size(); ++n)\r
209                         reserved_frames_[n] = std::make_shared<blue_dma_buffer>(format_desc_.size, n);          \r
210                                 \r
211                 executor_.start();\r
212 \r
213                 CASPAR_LOG(info) << TEXT("BLUECARD INFO: Successfully initialized device ") << device_index_;\r
214 \r
215                 active_ = executor_.begin_invoke([]{});\r
216         }\r
217 \r
218         ~implementation()\r
219         {\r
220                 disable_video_output();\r
221 \r
222                 if(blue_)\r
223                         blue_->device_detach();         \r
224 \r
225                 CASPAR_LOG(info) << "BLUECARD INFO: Successfully released device " << device_index_;\r
226         }\r
227         \r
228         void enable_video_output()\r
229         {\r
230                 if(!BLUE_PASS(set_card_property(blue_, VIDEO_BLACKGENERATOR, 0)))\r
231                         CASPAR_LOG(error) << "BLUECARD ERROR: Failed to disable video output. (device " << device_index_ << TEXT(")");  \r
232         }\r
233 \r
234         void disable_video_output()\r
235         {\r
236                 if(!BLUE_PASS(set_card_property(blue_, VIDEO_BLACKGENERATOR, 1)))\r
237                         CASPAR_LOG(error) << "BLUECARD ERROR: Failed to disable video output. (device " << device_index_ << TEXT(")");          \r
238         }\r
239 \r
240         virtual void send(const safe_ptr<const read_frame>& frame)\r
241         {                       \r
242                 static std::vector<short> silence(MAX_HANC_BUFFER_SIZE, 0);\r
243                 \r
244                 size_t audio_samples = static_cast<size_t>(48000.0 / format_desc_.fps);\r
245                 size_t audio_nchannels = 2;\r
246                 \r
247                 active_.get();\r
248                 active_ = executor_.begin_invoke([=]\r
249                 {\r
250                         try\r
251                         {\r
252                                 std::copy_n(frame->image_data().begin(), frame->image_data().size(), reserved_frames_.front()->image_data());\r
253 \r
254                                 unsigned long n_field = 0;\r
255                                 blue_->wait_output_video_synch(UPD_FMT_FRAME, n_field);\r
256                                 \r
257                                 if(embed_audio_)\r
258                                 {               \r
259                                         auto frame_audio_data = frame->audio_data().empty() ? silence.data() : const_cast<short*>(frame->audio_data().begin());\r
260 \r
261                                         encode_hanc(reinterpret_cast<BLUE_UINT32*>(reserved_frames_.front()->hanc_data()), frame_audio_data, audio_samples, audio_nchannels);\r
262                                                                 \r
263                                         blue_->system_buffer_write_async(const_cast<unsigned char*>(reserved_frames_.front()->image_data()), \r
264                                                                                                         reserved_frames_.front()->image_size(), \r
265                                                                                                         nullptr, \r
266                                                                                                         BlueImage_HANC_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_IMAGE));\r
267 \r
268                                         blue_->system_buffer_write_async(reserved_frames_.front()->hanc_data(),\r
269                                                                                                         reserved_frames_.front()->hanc_size(), \r
270                                                                                                         nullptr,                 \r
271                                                                                                         BlueImage_HANC_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_HANC));\r
272 \r
273                                         if(BLUE_FAIL(blue_->render_buffer_update(BlueBuffer_Image_HANC(reserved_frames_.front()->id()))))\r
274                                                 CASPAR_LOG(trace) << TEXT("BLUEFISH: render_buffer_update failed");\r
275                                 }\r
276                                 else\r
277                                 {\r
278                                         blue_->system_buffer_write_async(const_cast<unsigned char*>(reserved_frames_.front()->image_data()),\r
279                                                                                                         reserved_frames_.front()->image_size(), \r
280                                                                                                         nullptr,                 \r
281                                                                                                         BlueImage_DMABuffer(reserved_frames_.front()->id(), BLUE_DATA_IMAGE));\r
282                         \r
283                                         if(BLUE_FAIL(blue_->render_buffer_update(BlueBuffer_Image(reserved_frames_.front()->id()))))\r
284                                                 CASPAR_LOG(trace) << TEXT("BLUEFISH: render_buffer_update failed");\r
285                                 }\r
286 \r
287                                 std::rotate(reserved_frames_.begin(), reserved_frames_.begin() + 1, reserved_frames_.end());\r
288                         }\r
289                         catch(...)\r
290                         {\r
291                                 CASPAR_LOG_CURRENT_EXCEPTION();\r
292                         }\r
293                 });\r
294         }\r
295 \r
296         virtual size_t buffer_depth() const{return 1;}\r
297 \r
298         void encode_hanc(BLUE_UINT32* hanc_data, void* audio_data, size_t audio_samples, size_t audio_nchannels)\r
299         {       \r
300                 auto card_type = blue_->has_video_cardtype();\r
301                 auto sample_type = (AUDIO_CHANNEL_16BIT | AUDIO_CHANNEL_LITTLEENDIAN);\r
302                 \r
303                 hanc_stream_info_struct hanc_stream_info;\r
304                 memset(&hanc_stream_info, 0, sizeof(hanc_stream_info));\r
305                 \r
306                 hanc_stream_info.AudioDBNArray[0] = -1;\r
307                 hanc_stream_info.AudioDBNArray[1] = -1;\r
308                 hanc_stream_info.AudioDBNArray[2] = -1;\r
309                 hanc_stream_info.AudioDBNArray[3] = -1;\r
310                 hanc_stream_info.hanc_data_ptr = hanc_data;\r
311                 hanc_stream_info.video_mode = vid_fmt_;\r
312                 \r
313                 auto emb_audio_flag = (blue_emb_audio_enable | blue_emb_audio_group1_enable);\r
314 \r
315                 if (!is_epoch_card(card_type))\r
316                         encode_hanc_frame(&hanc_stream_info, audio_data, audio_nchannels, audio_samples, sample_type, emb_audio_flag);  \r
317                 else\r
318                         encode_hanc_frame_ex(card_type, &hanc_stream_info, audio_data, audio_nchannels, audio_samples, sample_type, emb_audio_flag);\r
319         }\r
320 };\r
321 \r
322 bluefish_consumer::bluefish_consumer(bluefish_consumer&& other) : impl_(std::move(other.impl_)){}\r
323 bluefish_consumer::bluefish_consumer(const video_format_desc& format_desc, unsigned int device_index, bool embed_audio) : impl_(new implementation(format_desc, device_index, embed_audio)){}   \r
324 void bluefish_consumer::send(const safe_ptr<const read_frame>& frame){impl_->send(frame);}\r
325 size_t bluefish_consumer::buffer_depth() const{return impl_->buffer_depth();}\r
326         \r
327 safe_ptr<frame_consumer> create_bluefish_consumer(const std::vector<std::wstring>& params)\r
328 {\r
329         if(params.size() < 2 || params[0] != L"BLUEFISH")\r
330                 return frame_consumer::empty();\r
331 \r
332         auto format_desc = video_format_desc::get(params[1]);\r
333         if(format_desc.format == video_format::invalid)\r
334                 return frame_consumer::empty();\r
335         \r
336         int device_index = 1;\r
337         bool embed_audio = false;\r
338 \r
339         try{if(params.size() > 2) device_index = boost::lexical_cast<int>(params[2]);}\r
340         catch(boost::bad_lexical_cast&){}\r
341         try{if(params.size() > 3) embed_audio = boost::lexical_cast<bool>(params[3]);}\r
342         catch(boost::bad_lexical_cast&){}\r
343 \r
344         return make_safe<bluefish_consumer>(format_desc, device_index, embed_audio);\r
345 }\r
346 \r
347 }}