UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
bluefish444.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2013-2023 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
43#ifdef HAVE_CONFIG_H
44#include "config.h"
45#include "config_unix.h"
46#include "config_win32.h"
47#endif
48
49#include "video_display.h"
50
51#include <iomanip>
52#include <iostream>
53#include <queue>
54#include <sstream>
55#include <stdexcept>
56#include <string>
57#include <vector>
58
59#include "bluefish444_common.h"
60
61#include "audio/types.h"
62#include "debug.h"
63#include "host.h"
64#include "lib_common.h"
65#include "tv.h"
66#include "utils/ring_buffer.h"
67#include "video.h"
68#include "video_display.h"
69
70#define BLUEFISH444_MAGIC 0x15b75db8
71
72#define BUFFER_COUNT 4
73
74#define MAX_BLUE_OUT_CHANNELS 4
75
76using namespace std;
77
78static void InitOutputChannel(BLUEVELVETC_HANDLE pSDK, ULONG DefaultOutputChannel,
79 ULONG UpdateFormat, ULONG MemoryFormat, ULONG VideoEngine);
80static void RouteChannel(BLUEVELVETC_HANDLE pSDK, ULONG Source, ULONG Destination,
81 ULONG LinkType);
82
84{
85 av_buffer(int TilesCount, int GoldenSize) :
86 pVideoBuffer(TilesCount), BufferId(0), mGoldenSize(GoldenSize)
87 {
88 for(int i = 0; i < TilesCount; ++i) {
89 pVideoBuffer[i] = (unsigned int *) bfAlloc(GoldenSize);
90 }
91 }
92 virtual ~av_buffer() {
93 for(unsigned int i = 0; i < pVideoBuffer.size(); ++i) {
94 bfFree(mGoldenSize, pVideoBuffer[i]);
95 }
96 }
97 vector<unsigned int *> pVideoBuffer;
98 unsigned int BufferId;
100};
101
103 public:
104 display_bluefish444_state(unsigned int flags, int deviceId = 1)
105 noexcept(false);
106 virtual ~display_bluefish444_state() noexcept;
107 struct video_frame *getf() noexcept;
108 void putf(struct video_frame *) noexcept(false);
109 void reconfigure(struct video_desc) noexcept(false);
110 void cleanup() noexcept;
111 static void *playback_thread(void *arg) noexcept;
112 void *playback_loop() noexcept;
113
114#ifdef HAVE_BLUE_AUDIO
116 void reconfigure_audio(int quant_samples, int channels,
117 int sample_rate) noexcept(false);
118 void put_audio_frame(const struct audio_frame *) noexcept;
119#endif
120 private:
121 uint32_t m_magic;
122
124
126 BLUEVELVETC_HANDLE m_pSDK[MAX_BLUE_OUT_CHANNELS];
128 BLUE_S32 m_CardType;
132
135 BLUE_U32 m_GoldenSize;
136 unsigned long int m_LastFieldCount;
137
138 pthread_mutex_t m_lock;
139 pthread_cond_t m_WorkerCv;
140 pthread_cond_t m_BossCv;
141 pthread_t m_thread;
142 queue<struct av_buffer *> m_ReadyFrameQueue;
144 queue<struct av_buffer *> m_FreeFrameQueue;
146
147 // this is temporary storage between getf/putf calls
149#ifdef HAVE_BLUE_AUDIO
150 struct audio_desc m_AudioDesc;
151 hanc_stream_info_struct m_HancInfo;
152 pthread_spinlock_t m_AudioSpinLock;
153 struct ring_buffer *m_AudioRingBuffer;
154#endif
156 };
157
159 int deviceId) noexcept(false) :
160 m_magic(BLUEFISH444_MAGIC),
161 m_frame(NULL),
162 m_deviceId(deviceId),
163 m_AttachedDevicesCount(0),
164 m_GoldenSize(0),
165 m_LastFieldCount(0),
166 m_pPlayingBuffer(NULL),
167#ifdef HAVE_BLUE_AUDIO
168 m_AudioRingBuffer(ring_buffer_init(48000*4*16*8)),
169#endif
170 m_PlayAudio(false)
171{
172 BLUE_S32 iDevices = 0;
173 BLUE_U32 val32;
174
175 if(flags) {
176#ifdef HAVE_BLUE_AUDIO
177 m_PlayAudio = true;
178#else
179 throw runtime_error("Audio support missing in this build");
180#endif
181 }
182 pthread_mutex_init(&m_lock, NULL);
183 pthread_cond_init(&m_WorkerCv, NULL);
184 pthread_cond_init(&m_BossCv, NULL);
185
186 BLUEVELVETC_HANDLE pSDK = bfcFactory();
187 bfcEnumerate(pSDK, &iDevices);
188 if(iDevices < 1) {
189 bfcDestroy(pSDK);
190 throw runtime_error("No Bluefish card detected");
191 }
192
193 bfcQueryCardType(pSDK, &m_CardType, m_deviceId);
194 bfcQueryCardProperty32(pSDK, INVALID_VIDEO_MODE_FLAG, &val32);
195 m_InvalidVideoModeFlag = val32;
196
197 if(BLUE_FAIL(bfcAttach(pSDK, m_deviceId))) {
198 bfcDestroy(pSDK);
199 throw runtime_error("Unable to attach card");
200 }
201
202 bfcQueryCardProperty32(pSDK, EPOCH_GET_PRODUCT_ID, &val32);
203
204 m_CardFirmware = val32;
205 m_deviceId = deviceId;
206
207 bfcDetach(pSDK);
208 bfcDestroy(pSDK);
209
210#ifdef HAVE_BLUE_AUDIO
211 memset(&m_AudioDesc, 0, sizeof(m_AudioDesc));
212 memset(&m_HancInfo, 0, sizeof(m_HancInfo));
213 for(int i = 0; i < 4; ++i) {
214 m_HancInfo.AudioDBNArray[i] = -1;
215 }
216 m_HancInfo.hanc_data_ptr = (unsigned int *) bfAlloc(MAX_HANC_SIZE);
217 pthread_spin_init(&m_AudioSpinLock, 0);
218#endif
219
220 pthread_create(&m_thread, NULL, playback_thread, this);
221}
222
224{
225 assert(m_magic == BLUEFISH444_MAGIC);
226 // Kill thread
227 pthread_mutex_lock(&m_lock);
228 m_ReadyFrameQueue.push(NULL);
229 pthread_cond_signal(&m_WorkerCv);
230 pthread_mutex_unlock(&m_lock);
231 unsigned long int FieldCount;
232 //cards must be genlocked; only then all for output channels are completely in synch
233 bfcWaitVideoOutputSync(m_pSDK[0], UPD_FMT_FRAME, &FieldCount);
234
235 pthread_join(m_thread, NULL);
236
237 cleanup();
238
239 pthread_mutex_destroy(&m_lock);
240 pthread_cond_destroy(&m_WorkerCv);
241 pthread_cond_destroy(&m_BossCv);
242
243#ifdef HAVE_BLUE_AUDIO
244 ring_buffer_destroy(m_AudioRingBuffer);
245 bfFree(MAX_HANC_SIZE, m_HancInfo.hanc_data_ptr);
246 pthread_spin_destroy(&m_AudioSpinLock);
247#endif
248}
249
251{
254 return s->playback_loop();
255}
256
258{
259 uint32_t FrameCount = 0;
260 struct timeval t0;
261#ifdef HAVE_BLUE_AUDIO
262 uint32_t EmbAudioFlag = blue_enable_hanc_timestamp_pkt | blue_emb_audio_enable; // enable timecode
263#endif
264
265 gettimeofday(&t0, NULL);
266
267#ifdef _WIN32
268 OVERLAPPED Overlapped[MAX_BLUE_OUT_CHANNELS];
269 for (int i = 0;i < MAX_BLUE_OUT_CHANNELS; ++ i) {
270 Overlapped[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
271 }
272#endif
273
274 while(1) {
275 struct av_buffer *frame;
276 pthread_mutex_lock(&m_lock);
277 while(m_ReadyFrameQueue.empty()) {
278 m_WorkerWaiting = true;
279 pthread_cond_wait(&m_WorkerCv, &m_lock);
280 m_WorkerWaiting = false;
281 }
282
283 frame = m_ReadyFrameQueue.front();
284 m_ReadyFrameQueue.pop();
285 pthread_mutex_unlock(&m_lock);
286
287 if(frame == NULL) {
288 return NULL;
289 }
290
291 unsigned long int FieldCount = 0;
292
293 // for more than one input, cards must be genlocked
294 bfcWaitVideoOutputSync(m_pSDK[0], UPD_FMT_FRAME, &FieldCount);
295 if(m_pPlayingBuffer) {
296 pthread_mutex_lock(&m_lock);
298 pthread_cond_signal(&m_BossCv);
299 pthread_mutex_unlock(&m_lock);
300 }
301
302 bool WaitResult = true;
303
304 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
305 unsigned char *videoBuffer;
306#ifdef _WIN32
307 OVERLAPPED *OverlapCh = &Overlapped[i];
308#endif
310 videoBuffer = (unsigned char *) frame->pVideoBuffer[i];
311 } else { // untiled 4K
312 videoBuffer = (unsigned char *) frame->pVideoBuffer[0];
313 }
314#ifdef _WIN32
315 int err = bfcSystemBufferWriteAsync(
316#else
317 int err = bfcSystemBufferWrite(
318#endif
319 m_pSDK[i], videoBuffer + m_Offset[i], m_GoldenSize,
320#ifdef _WIN32
321 OverlapCh,
322#endif
323 (m_PlayAudio && i == 0 ?
324 BlueImage_HANC_DMABuffer(frame->BufferId, BLUE_DATA_IMAGE) :
325 BlueImage_DMABuffer(frame->BufferId, BLUE_DATA_IMAGE)), 0
326 );
327 if(!BLUE_OK(err)) {
328 cerr << "Write failed (Channel " << (char) ('A' + i) << ")" << endl;
329 WaitResult = false;
330 }
331 }
332#ifdef _WIN32
333 if(WaitResult) {
334 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
335 DWORD BytesReturned = 0;
336 GetOverlappedResult(bfcGetHandle(m_pSDK[i]), &Overlapped[i], &BytesReturned, TRUE);
337 ResetEvent(Overlapped[i].hEvent);
338 }
339 }
340#else
341 UNUSED(WaitResult);
342#endif
343
344#ifdef HAVE_BLUE_AUDIO
345 if(m_PlayAudio) {
346 m_HancInfo.time_code = 0LL; //RP188 VITC time code
347 m_HancInfo.rp188_ltc_time_code = 0LL; //RP188 LTC time code
348 m_HancInfo.ltc_time_code = 0LL; //external LTC time code
349 m_HancInfo.sd_vitc_time_code = 0LL; //this field is only valid for SD video signal
350 m_HancInfo.video_mode = m_CurrentVideoMode;
351
352 uint32_t NrSamples = 0;
353 pthread_spin_lock(&m_AudioSpinLock);
354 uint32_t NumberAudioChannels = m_AudioDesc.ch_count;
355 char audio_data[2002*4*16];
356 unsigned int nSampleType;
357 ULONG EmbAudioProp = 0;
358
359 if(NumberAudioChannels > 0) {
361 FrameCount);
362 switch(m_AudioDesc.bps) {
363 case 2:
364 nSampleType = AUDIO_CHANNEL_16BIT; // AUDIO_CHANNEL_LITTLEENDIAN
365 break;
366 case 3:
367 nSampleType = AUDIO_CHANNEL_24BIT;
368 break;
369 default:
370 fprintf(stderr, "[Blue] Unhandled audio sample type!\n");
371 abort();
372 }
373
374 if(NumberAudioChannels > 0)
375 EmbAudioProp |= (blue_emb_audio_group1_enable);
376 if(NumberAudioChannels > 4)
377 EmbAudioProp |= (blue_emb_audio_group2_enable);
378 if(NumberAudioChannels > 8)
379 EmbAudioProp |= (blue_emb_audio_group3_enable);
380 if(NumberAudioChannels > 12)
381 EmbAudioProp |= (blue_emb_audio_group4_enable);
382
383 int bytes = ring_buffer_read(m_AudioRingBuffer, audio_data, NrSamples *
384 m_AudioDesc.bps *m_AudioDesc.ch_count);
385 NrSamples = bytes / m_AudioDesc.bps / m_AudioDesc.ch_count;
386 }
387 pthread_spin_unlock(&m_AudioSpinLock);
388
389 if(NrSamples > 0) {
390 bfcEncodeHancFrameEx(m_pSDK[0], m_CardType, &m_HancInfo, audio_data,
391 m_AudioDesc.ch_count, NrSamples,
392 nSampleType, EmbAudioProp | EmbAudioFlag);
393
394 //wait for both DMA transfers to be finished
395 //GetOverlappedResult(pSDK->m_hDevice, &OverlapChA, &BytesReturnedChA, TRUE);
396 //ResetEvent(OverlapChA.hEvent);
397
398 //now we can DMA the HANC frame
399#ifdef _WIN32
400 bfcSystemBufferWriteAsync(m_pSDK[0], (unsigned char *) m_HancInfo.hanc_data_ptr,
402 NULL, BlueImage_HANC_DMABuffer(frame->BufferId, BLUE_DATA_HANC));
403#else
404 bfcSystemBufferWrite(m_pSDK[0], (unsigned char *) m_HancInfo.hanc_data_ptr,
406 BlueImage_HANC_DMABuffer(frame->BufferId, BLUE_DATA_HANC), 0);
407#endif
408 }
409 }
410#endif
411 //tell the card to playback the frames on the next interrupt
412 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
413 bfcRenderBufferUpdate(m_pSDK[i],
414 (m_PlayAudio && i == 0 ?
415 BlueBuffer_Image_HANC(frame->BufferId) :
416 BlueBuffer_Image(frame->BufferId))
417 );
418 }
419
420 if(FieldCount != m_LastFieldCount + 2) {
421 cerr << "[Blue444 disp] " << "Dropped " <<
422 ((FieldCount - m_LastFieldCount + 2)/2) <<
423 " frames." << endl;
424 }
425
426 m_LastFieldCount = FieldCount;
427
428 FrameCount++;
429
430 struct timeval t;
431 gettimeofday(&t, NULL);
432 double seconds = tv_diff(t, t0);
433 if (seconds >= 5) {
434 float fps = FrameCount / seconds;
435 log_msg(LOG_LEVEL_INFO, "[Blue disp] %d frames in %g seconds = %g FPS\n",
436 FrameCount, seconds, fps);
437 t0 = t;
438 FrameCount = 0;
439 }
440
442 }
443
444#ifdef _WIN32
445 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
446 CloseHandle(Overlapped[i].hEvent);
447 }
448#endif
449
450 return NULL;
451}
452
454{
455 //turn on black generator (unless we want to keep displaying the last rendered frame)
456 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
457 uint32_t value;
458
459 value = ENUM_BLACKGENERATOR_ON;
460 bfcSetCardProperty32(m_pSDK[i], VIDEO_BLACKGENERATOR, value);
461
462 bfcVideoPlaybackStop(m_pSDK[i], 1, 1);
463 }
464
465 while(!m_FreeFrameQueue.empty()) {
466 struct av_buffer *frame = m_FreeFrameQueue.front();
467 m_FreeFrameQueue.pop();
468 delete frame;
469 }
470
471 if(m_pPlayingBuffer) {
472 delete m_pPlayingBuffer;
473 m_pPlayingBuffer = NULL;
474 }
475
477 m_frame = NULL;
478
479 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
480 bfcDetach(m_pSDK[i]);
481 bfcDestroy(m_pSDK[i]);
482 }
483
485}
486
488 noexcept(false)
489{
490 ULONG VideoMode;
491 BLUE_U32 val32;
492 bool is4K;
493 bool isTiled4K;
494 int tile_width, tile_height;
495
496 pthread_mutex_lock(&m_lock);
497 while(!m_ReadyFrameQueue.empty() ||
498 !m_WorkerWaiting)
499 pthread_cond_wait(&m_BossCv, &m_lock);
500 pthread_mutex_unlock(&m_lock);
501
502 cleanup();
503
504 tile_width = desc.width;
505 tile_height = desc.height;
506
507 // 4K
508 if((desc.width == 4096 || desc.width == 3840) && desc.height == 2160) {
509 is4K = true;
510 isTiled4K = false;
511 tile_width /= 2;
512 tile_height /= 2;
513 } else if(desc.tile_count == 4 &&
514 (desc.width == 2048 || desc.width == 1920) &&
515 desc.height == 1080) {
516 // tiled 4K
517 is4K = true;
518 isTiled4K = true;
519 } else {
520 is4K = false;
521 isTiled4K = false;
522 }
523
524 m_TileCount = desc.tile_count;
525
526 if(is4K) {
527 // we need a SuperNova QuadOut card
528 if(m_CardType != CRD_BLUE_SUPER_NOVA) {
529 throw runtime_error("We need a SuperNova card for 4K mode! "
530 "Let us know if you need a different combination "
531 "(eg. 2x Bluefish card");
532 }
533
534 if(m_CardFirmware != ORAC_4SDIOUTPUT_FIRMWARE_PRODUCTID)
535 {
536 throw runtime_error("Wrong firmware; need 4 output channels (QuadOut)");
537 }
538
539 m_AttachedDevicesCount = 4;
540 } else {
541 m_AttachedDevicesCount = 1;
542 }
543
544 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
545 m_pSDK[i] = bfcFactory();
546
547 //Attach the SDK object to a specific card, in this case card 1
548 if(BLUE_FAIL(bfcAttach(m_pSDK[i], m_deviceId)))
549 {
550 throw runtime_error("Error on device attach");
551 }
552 }
553
554 VideoMode = m_InvalidVideoModeFlag;
555
556 for(int i = 0; i < bluefish_frame_modes_count; ++i) {
557 if((int) bluefish_frame_modes[i].width == tile_width &&
558 (int) bluefish_frame_modes[i].height == tile_height &&
559 fabs(bluefish_frame_modes[i].fps - desc.fps) < 0.01 &&
561 {
562 VideoMode =
564 }
565 }
566
567 if(VideoMode == m_InvalidVideoModeFlag) {
568 ostringstream msg_ss;
569 msg_ss << "Unable to find appropriate video format for " <<
570 tile_width << "x" << tile_height <<
572 " @" << fixed << setprecision(2) << desc.fps;
573 throw runtime_error(msg_ss.str());
574 }
575
576 m_CurrentVideoMode = VideoMode;
577
578 ULONG UpdateFormat = UPD_FMT_FRAME;
579 ULONG MemoryFormat;
580 switch(desc.color_spec) {
581 case UYVY:
582 MemoryFormat = MEM_FMT_2VUY;
583 break;
584 case v210:
585 MemoryFormat = MEM_FMT_V210;
586 break;
587 case RGBA:
588 MemoryFormat = MEM_FMT_RGBA;
589 break;
590 // case DPX10:
591 // MemoryFormat = MEM_FMT_CINEON_LITTLE_ENDIAN;
592 // break;
593 default:
594 throw logic_error("Unknown video format!!!");
595 }
596
597 ULONG VideoEngine = VIDEO_ENGINE_FRAMESTORE;
598
599 ULONG OutputChannels[4] = {
600 BLUE_VIDEO_OUTPUT_CHANNEL_A,
601 BLUE_VIDEO_OUTPUT_CHANNEL_B,
602 BLUE_VIDEO_OUTPUT_CHANNEL_C,
603 BLUE_VIDEO_OUTPUT_CHANNEL_D
604 };
605 ULONG Sources[4] = { EPOCH_SRC_OUTPUT_MEM_INTERFACE_CHA,
606 EPOCH_SRC_OUTPUT_MEM_INTERFACE_CHB,
607 EPOCH_SRC_OUTPUT_MEM_INTERFACE_CHC,
608 EPOCH_SRC_OUTPUT_MEM_INTERFACE_CHD
609 };
610 ULONG Destinations[4] = { EPOCH_DEST_SDI_OUTPUT_A,
611 EPOCH_DEST_SDI_OUTPUT_B,
612 EPOCH_DEST_SDI_OUTPUT_C,
613 EPOCH_DEST_SDI_OUTPUT_D
614 };
615
616 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
617 InitOutputChannel(m_pSDK[i], OutputChannels[i],
618 UpdateFormat, MemoryFormat, VideoEngine);
619 RouteChannel(m_pSDK[i], Sources[i],
620 Destinations[i],
621 BLUE_CONNECTOR_PROP_SINGLE_LINK);
622 }
623
624 if(m_PlayAudio) {
625 //route the same audio to AES as well (AES only supports 8 channels, though)
626 val32 = EPOCH_SET_ROUTING(EPOCH_SRC_OUTPUT_MEM_INTERFACE_CHA,
627 EPOCH_DEST_AES_ANALOG_AUDIO_OUTPUT, BLUE_CONNECTOR_PROP_SINGLE_LINK);
628 bfcSetCardProperty32(m_pSDK[0], MR2_ROUTING, val32);
629
630 val32 = blue_emb_audio_group1_enable | blue_emb_audio_enable | blue_enable_hanc_timestamp_pkt;
631 bfcSetCardProperty32(m_pSDK[0], EMBEDDED_AUDIO_OUTPUT, val32);
632 }
633
634 //Set the required video mode
635 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
636 val32 = VideoMode;
637 bfcSetCardProperty32(m_pSDK[i], VIDEO_MODE, val32);
638 bfcQueryCardProperty32(m_pSDK[i], VIDEO_MODE, &val32);
639 if(val32 != VideoMode)
640 {
641 throw logic_error("Can't set video mode; FIFO running already?");
642 }
643 }
644
645 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
646 val32 = vc_get_linesize(tile_width, desc.color_spec);
647 bfcSetCardProperty32(m_pSDK[i], VIDEO_IMAGE_WIDTH, val32);
648 val32 = tile_height;
649 bfcSetCardProperty32(m_pSDK[i], VIDEO_IMAGE_HEIGHT, val32);
651 bfcSetCardProperty32(m_pSDK[i], VIDEO_IMAGE_PITCH, val32);
652 }
653
654 BLUE_U32 Width, Height, BytesPerLine, BytesPerFrame, GoldenSize;
655 bfcGetVideoInfo(VideoMode, UpdateFormat, MemoryFormat, &Width, &Height, &BytesPerLine, &BytesPerFrame, &GoldenSize);
656
657 ULONG FrameSize = GoldenSize;
658 if(is4K && !isTiled4K) {
659 BytesPerFrame = vc_get_linesize(desc.width, desc.color_spec) *
660 desc.height;
661 // 4 * GoldenSize should be AFAIK correct but it keeps failing during
662 // DMA transfer of last segment of 4K video
663 FrameSize = 5 * GoldenSize;
664 }
665
666 cout << "Video Golden: " << GoldenSize << endl;
667 cout << "Video Pixels per line: " << Width << endl;
668 cout << "Video lines: " << Height << endl;
669 cout << "Video Bytes per frame: " << BytesPerFrame << endl;
670 cout << "Video Bytes per line: " << BytesPerLine << endl;
671
672 for(int i = 0; i < BUFFER_COUNT; ++i) {
673 av_buffer *new_buffer = new av_buffer(desc.tile_count, FrameSize);
674 m_FreeFrameQueue.push(new_buffer);
675 }
676
677 m_frame = vf_alloc_desc(desc);
678 for(unsigned int i = 0; i < desc.tile_count; ++i) {
679 m_frame->tiles[i].data_len = BytesPerFrame;
680 }
681
682 if(!is4K || isTiled4K) {
683 m_GoldenSize = GoldenSize;
684 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
685 m_Offset[i] = 0;
686 }
687 } else {
688 m_GoldenSize = vc_get_linesize(desc.width, desc.color_spec) * tile_height;
689 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
690 int x = i % 2;
691 int y = i / 2;
692 m_Offset[i] = y * m_GoldenSize + x *
693 vc_get_linesize(tile_width, desc.color_spec);
694 }
695 }
696
697 for(int i = 0; i < m_AttachedDevicesCount; ++i) {
698 bfcVideoPlaybackStart(m_pSDK[i], 0, 0);
699 }
700}
701
703{
704 pthread_mutex_lock(&m_lock);
705 while(m_FreeFrameQueue.empty()) {
706 pthread_cond_wait(&m_BossCv, &m_lock);
707 }
709 m_FreeFrameQueue.pop();
710 for(int i = 0; i < m_TileCount; ++i) {
711 m_frame->tiles[i].data = (char *) m_pTmpFrame->pVideoBuffer[i];
712 }
713 pthread_mutex_unlock(&m_lock);
714
715 return m_frame;
716}
717
719{
720 if (!frame)
721 return;
722
723 if (frame->tiles[0].data !=
724 (char *) m_pTmpFrame->pVideoBuffer[0]) {
725 throw invalid_argument("Invalid frame supplied!");
726 }
727
728 pthread_mutex_lock(&m_lock);
729 for(int i = 0; i < m_TileCount; ++i) {
730 m_pTmpFrame->pVideoBuffer[i] = (unsigned int *)(void *) m_frame->tiles[i].data;
731 }
732 m_ReadyFrameQueue.push(m_pTmpFrame);
733 pthread_cond_signal(&m_WorkerCv);
734 pthread_mutex_unlock(&m_lock);
735}
736
737/*
738 * Utility functions - from SDK
739 */
740static void InitOutputChannel(BLUEVELVETC_HANDLE pSDK, ULONG DefaultOutputChannel, ULONG UpdateFormat,
741 ULONG MemoryFormat, ULONG VideoEngine)
742{
743 uint32_t val32;
744
745 //MOST IMPORTANT: as the first step set the channel that we want to work with
746 val32 = DefaultOutputChannel;
747 bfcSetCardProperty32(pSDK, DEFAULT_VIDEO_OUTPUT_CHANNEL, val32);
748
749 //make sure the FIFO hasn't been left running (e.g. application crash before), otherwise we can't change card properties
750 bfcVideoPlaybackStop(pSDK, 1, 1);
751
752 val32 = UpdateFormat;
753 bfcSetCardProperty32(pSDK, VIDEO_UPDATE_TYPE, val32);
754
755 val32 = MemoryFormat;
756 bfcSetCardProperty32(pSDK, VIDEO_MEMORY_FORMAT, val32);
757
758 //Only set the Video Engine after setting up the required video mode, update type and memory format
759 val32 = VideoEngine;
760 bfcSetCardProperty32(pSDK, VIDEO_OUTPUT_ENGINE, val32);
761
762 val32 = ENUM_BLACKGENERATOR_OFF;
763 bfcSetCardProperty32(pSDK, VIDEO_BLACKGENERATOR, val32);
764}
765
766static void RouteChannel(BLUEVELVETC_HANDLE pSDK, ULONG Source, ULONG Destination, ULONG LinkType)
767{
768 uint32_t val32;
769
770 val32 = EPOCH_SET_ROUTING(Source, Destination, LinkType);
771 bfcSetCardProperty32(pSDK, MR2_ROUTING, val32);
772}
773
774/*
775 * AUDIO
776 */
777#ifdef HAVE_BLUE_AUDIO
778void display_bluefish444_state::reconfigure_audio(int quant_samples, int channels,
779 int sample_rate) noexcept(false)
780{
781 if(quant_samples <= 0 || channels <= 0 || sample_rate <= 0) {
782 throw logic_error("Wrong audio attributes");
783 }
784
785 if(sample_rate != 48000) {
786 throw runtime_error("Audio: only 48 kHz audio supported");
787 }
788 if(channels > 16) {
789 throw runtime_error("Audio: more than 16 channels not supported");
790 }
791 // TODO: fix if we want more this needs some property reset
792 if(channels > 4) {
793 throw runtime_error("Audio: more than 4 channels not supported");
794 }
795 if(quant_samples != 16 && quant_samples != 24) {
796 throw runtime_error("Audio: only 16 or 24 bits per second supported");
797 }
798
799 pthread_spin_lock(&m_AudioSpinLock);
800 m_AudioDesc.bps = quant_samples / 8;
801 m_AudioDesc.ch_count = channels;
802 m_AudioDesc.sample_rate = sample_rate;
803 ring_buffer_flush(m_AudioRingBuffer);
804 pthread_spin_unlock(&m_AudioSpinLock);
805}
806
807void display_bluefish444_state::put_audio_frame(const struct audio_frame *frame) noexcept
808{
809 if(!m_PlayAudio)
810 return;
811 ring_buffer_write(m_AudioRingBuffer, frame->data, frame->data_len);
812}
813#endif // defined HAVE_BLUE_AUDIO
814
815/*
816 * API
817 */
818static void show_help(void);
819
820static void show_help(void)
821{
822 BLUE_S32 iDevices;
823 BLUEVELVETC_HANDLE pSDK = bfcFactory();
824 bfcEnumerate(pSDK, &iDevices);
825 bfcDestroy(pSDK);
826
827 cout << "bluefish444 (output) options:" << endl
828 << "\tbluefish444[:device=<device_id>]" << endl
829 << "\t\tID of the Bluefish device (if more present)" << endl
830 << "\t\t" << iDevices << " Bluefish devices found in this system" << endl;
831 if(iDevices == 1) {
832 cout << " (default)" << endl;
833 } else if(iDevices > 1) {
834 cout << ", valid indices [1," << iDevices << "]" << endl;
835 cout << "default is 1" << endl;
836 }
837}
838
839static void display_bluefish444_probe(struct device_info **available_cards, int *count, void (**deleter)(void *))
840{
842 BLUE_S32 iDevices;
843 BLUEVELVETC_HANDLE pSDK = bfcFactory();
844 bfcEnumerate(pSDK, &iDevices);
845 bfcDestroy(pSDK);
846
847 *available_cards = (struct device_info *) calloc(iDevices, sizeof(struct device_info));
848 *count = iDevices;
849 for (int i = 0; i < iDevices; i++) {
850 snprintf((*available_cards)[i].dev, sizeof (*available_cards)[i].dev, ":device=%d", iDevices);
851 snprintf((*available_cards)[i].name, sizeof (*available_cards)[i].name, "Bluefish444 card #%d", iDevices);
852 }
853}
854
855static void *display_bluefish444_init(struct module *parent, const char *fmt, unsigned int flags)
856{
857 UNUSED(parent);
858 int deviceId = 1;
859 if(fmt){
860 if(strcmp(fmt, "help") == 0) {
861 show_help();
862 return NULL;
863 } else if(strncasecmp(fmt, "device=", strlen("device=")) == 0) {
864 deviceId = atoi(fmt + strlen("device="));
865 } else {
866 cerr << "[Blue444 disp] Unknown parameter: " << fmt << endl;
867 return NULL;
868 }
869 }
870
871 void *state = NULL;
872
873 try {
874 state = new display_bluefish444_state(flags, deviceId);
875 } catch(runtime_error &e) {
876 cerr << "[Blue444 disp] " << e.what() << endl;
877 }
878 return state;
879}
880
881static struct video_frame *
883{
886 struct video_frame *frame = NULL;
887
888 try {
889 frame = s->getf();
890 } catch(runtime_error &e) {
891 cerr << "[Blue444 disp] " << e.what() << endl;
892 }
893
894 return frame;
895}
896
897static bool display_bluefish444_putf(void *state, struct video_frame *frame, long long nonblock)
898{
902 try {
903 s->putf(frame);
904 return true;
905 } catch(runtime_error &e) {
906 cerr << "[Blue444 disp] " << e.what() << endl;
907 }
908 return false;
909}
910
911static bool
913{
916
917 try {
918 s->reconfigure(desc);
919 } catch(runtime_error &e) {
920 cerr << "[Blue444 disp] " << e.what() << endl;
921 return false;
922 }
923
924 return true;
925}
926
928{
931
932 delete s;
933}
934
935static bool display_bluefish444_get_property(void *state, int property, void *val, size_t *len)
936{
937 UNUSED(state);
938 codec_t codecs[] = { UYVY };
940 int rgb_shift[] = {0, 8, 16};
941
942 switch (property) {
944 if(sizeof(codecs) <= *len) {
945 memcpy(val, codecs, sizeof(codecs));
946 } else {
947 return false;
948 }
949
950 *len = sizeof(codecs);
951 break;
953 if(sizeof(rgb_shift) > *len) {
954 return false;
955 }
956 memcpy(val, rgb_shift, sizeof(rgb_shift));
957 *len = sizeof(rgb_shift);
958 break;
960 *(int *) val = PITCH_DEFAULT;
961 *len = sizeof(int);
962 break;
964 if(sizeof(supported_il_modes) <= *len) {
965 memcpy(val, supported_il_modes, sizeof(supported_il_modes));
966 } else {
967 return false;
968 }
969 *len = sizeof(supported_il_modes);
970 break;
973 break;
975 {
976 assert(*len == sizeof(struct audio_desc));
977 struct audio_desc *desc = (struct audio_desc *) val;
978 desc->sample_rate = 48000;
979 desc->ch_count = max(desc->ch_count, 4);
980 desc->codec = AC_PCM;
981 desc->bps = desc->bps < 3 ? 2 : 3;
982 }
983 break;
984 default:
985 return false;
986 }
987 return true;
988}
989
991 int sample_rate)
992{
993#ifdef HAVE_BLUE_AUDIO
996 try {
997 s->reconfigure_audio(quant_samples, channels, sample_rate);
998 } catch(runtime_error &e) {
999 cerr << "[Blue444 disp] " << e.what() << endl;
1000 return false;
1001 }
1002
1003 return true;
1004#else
1005 return false;
1006#endif
1007}
1008
1010{
1011#ifdef HAVE_BLUE_AUDIO
1014
1015 try {
1016 s->put_audio_frame(frame);
1017 } catch(runtime_error &e) {
1018 cerr << "[Blue444 disp] " << e.what() << endl;
1019 }
1020#endif
1021}
1022
1036
1038
#define UNUSED(x)
void ** state
struct video_frame long long nonblock
int void * val
struct video_desc desc
int int int sample_rate
int int channels
struct video_frame * frame
const char * fmt
int void size_t * len
const char unsigned int flags
int void(** deleter)(void *))
int property
int quant_samples
void vf_free(struct video_frame *buf)
Frees video_frame structure.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
This file contains definition of some common audio types.
@ AC_PCM
Definition types.h:53
#define HAVE_BLUE_AUDIO
#define MAX_HANC_SIZE
static const int bluefish_frame_modes_count
static uint32_t GetNumberOfAudioSamplesPerFrame(uint32_t VideoMode, uint32_t FrameNumber) __attribute__((unused))
static const struct bluefish_frame_mode_t bluefish_frame_modes[]
#define TRUE
#define FALSE
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define PITCH_DEFAULT
default pitch, i. e. respective linesize
#define VIDEO_DISPLAY_ABI_VERSION
#define DISPLAY_NO_GENERIC_FPS_INDICATOR
@ DISPLAY_PROPERTY_BUF_PITCH
requested framebuffer pitch - int (bytes), may be PITCH_DEFAULT
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_SUPPORTED_IL_MODES
display supported interlacing modes - enum interlacing_t[]
@ DISPLAY_PROPERTY_VIDEO_MODE
requested video mode - int (one of display_prop_vid_mode)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
@ DISPLAY_PROPERTY_AUDIO_FORMAT
@ DISPLAY_PROPERTY_VIDEO_SEPARATE_TILES
framebuffer consists of separate tiles
int count
Definition hd-rum.c:44
@ LIBRARY_CLASS_VIDEO_DISPLAY
Definition lib_common.h:79
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
void ring_buffer_destroy(struct ring_buffer *ring)
struct ring_buffer * ring_buffer_init(int size)
void ring_buffer_flush(struct ring_buffer *buf)
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)
#define max(a, b)
Definition rtp.c:98
static void cleanup()
Definition sdp.c:195
const char * parent
Definition settings.cpp:198
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
av_buffer(int TilesCount, int GoldenSize)
virtual ~av_buffer()
unsigned int BufferId
vector< unsigned int * > pVideoBuffer
enum interlacing_t interlacing
char dev[1024]
Definition types.h:383
queue< struct av_buffer * > m_FreeFrameQueue
struct video_frame * getf() noexcept
unsigned long int m_LastFieldCount
display_bluefish444_state(unsigned int flags, int deviceId=1) noexcept(false)
queue< struct av_buffer * > m_ReadyFrameQueue
int m_Offset[MAX_BLUE_OUT_CHANNELS]
static void * playback_thread(void *arg) noexcept
struct video_frame * m_frame
BLUEVELVETC_HANDLE m_pSDK[MAX_BLUE_OUT_CHANNELS]
void reconfigure(struct video_desc) noexcept(false)
void putf(struct video_frame *) noexcept(false)
struct av_buffer * m_pPlayingBuffer
virtual ~display_bluefish444_state() noexcept
void * playback_loop() noexcept
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int tile_count
Definition types.h:185
codec_t color_spec
Definition types.h:182
enum interlacing_t interlacing
Definition types.h:184
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
double tv_diff(struct timeval curr_time, struct timeval prev_time)
Definition tv.c:97
interlacing_t
Definition types.h:163
@ PROGRESSIVE
progressive frame
Definition types.h:164
@ SEGMENTED_FRAME
Segmented frame. Contains the same data as progressive frame.
Definition types.h:168
@ INTERLACED_MERGED
Columngs of both fields are interlaced together.
Definition types.h:167
codec_t
Definition types.h:65
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
@ v210
YCbCr 422 10-bit - xVY0U|xY2UY1|xUY3V|xY5VY4, little-endian (2 bit padding /'x'/),...
Definition types.h:75
This is an umbrella header for video functions.
static struct @76 codecs[]
static bool display_bluefish444_reconfigure(void *state, struct video_desc desc)
static void display_bluefish444_done(void *state)
#define BLUEFISH444_MAGIC
static const struct video_display_info display_bluefish444_info
static bool display_bluefish444_reconfigure_audio(void *state, int quant_samples, int channels, int sample_rate)
static void RouteChannel(BLUEVELVETC_HANDLE pSDK, ULONG Source, ULONG Destination, ULONG LinkType)
static bool display_bluefish444_get_property(void *state, int property, void *val, size_t *len)
#define BUFFER_COUNT
static void * display_bluefish444_init(struct module *parent, const char *fmt, unsigned int flags)
static void InitOutputChannel(BLUEVELVETC_HANDLE pSDK, ULONG DefaultOutputChannel, ULONG UpdateFormat, ULONG MemoryFormat, ULONG VideoEngine)
static struct video_frame * display_bluefish444_getf(void *state)
static bool display_bluefish444_putf(void *state, struct video_frame *frame, long long nonblock)
static void display_bluefish444_put_audio_frame(void *state, const struct audio_frame *frame)
static void display_bluefish444_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
#define MAX_BLUE_OUT_CHANNELS
static void show_help(void)
const char * get_interlacing_suffix(enum interlacing_t interlacing)
Returns suffix describing interlacing Eg. p, i or psf.