1 /*****************************************************************************
2 * decklink.cpp: BlackMagic DeckLink SDI output module
3 *****************************************************************************
4 * Copyright (C) 2012-2013 Rafaël Carré
5 * Copyright (C) 2009 Michael Niedermayer <michaelni@gmx.at>
6 * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com>
8 * Authors: Rafaël Carré <funman@videolan.org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 * TODO: test non stereo audio
33 #include <vlc_fixups.h>
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_threads.h>
40 #include <vlc_vout_display.h>
41 #include <vlc_picture_pool.h>
43 #include <vlc_block.h>
44 #include <vlc_image.h>
46 #include <arpa/inet.h>
48 #include <DeckLinkAPI.h>
49 #include <DeckLinkAPIDispatch.cpp>
51 #define FRAME_SIZE 1920
52 #define CHANNELS_MAX 6
55 static const int pi_channels_maps
[CHANNELS_MAX
+1] =
67 #define NOSIGNAL_INDEX_TEXT N_("Timelength after which we assume there is no signal.")
68 #define NOSIGNAL_INDEX_LONGTEXT N_(\
69 "Timelength after which we assume there is no signal.\n"\
70 "After this delay we black out the video."\
73 #define AFD_INDEX_TEXT "Active Format Descriptor"
74 #define AFD_INDEX_LONGTEXT AFD_INDEX_TEXT " value"
76 #define AR_INDEX_TEXT "Aspect Ratio"
77 #define AR_INDEX_LONGTEXT AR_INDEX_TEXT " of the source picture"
79 #define AFDLINE_INDEX_TEXT N_("Active Format Descriptor line.")
80 #define AFDLINE_INDEX_LONGTEXT N_("VBI line on which to output Active Format Descriptor.")
82 #define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss.")
83 #define NOSIGNAL_IMAGE_LONGTEXT NOSIGNAL_IMAGE_TEXT
85 #define CARD_INDEX_TEXT N_("Output card")
86 #define CARD_INDEX_LONGTEXT N_(\
87 "DeckLink output card, if multiple exist. " \
88 "The cards are numbered from 0.")
90 #define MODE_TEXT N_("Desired output mode")
91 #define MODE_LONGTEXT N_(\
92 "Desired output mode for DeckLink output. " \
93 "This value should be a FOURCC code in textual " \
94 "form, e.g. \"ntsc\".")
96 #define AUDIO_CONNECTION_TEXT N_("Audio connection")
97 #define AUDIO_CONNECTION_LONGTEXT N_(\
98 "Audio connection for DeckLink output.")
101 #define RATE_TEXT N_("Audio samplerate (Hz)")
102 #define RATE_LONGTEXT N_(\
103 "Audio sampling rate (in hertz) for DeckLink output. " \
104 "0 disables audio output.")
106 #define CHANNELS_TEXT N_("Number of audio channels")
107 #define CHANNELS_LONGTEXT N_(\
108 "Number of output channels for DeckLink output. " \
109 "Must be 2, 8 or 16. 0 disables audio output.")
111 #define VIDEO_CONNECTION_TEXT N_("Video connection")
112 #define VIDEO_CONNECTION_LONGTEXT N_(\
113 "Video connection for DeckLink output.")
115 #define VIDEO_TENBITS_TEXT N_("10 bits")
116 #define VIDEO_TENBITS_LONGTEXT N_(\
117 "Use 10 bits per pixel for video frames.")
119 #define CFG_PREFIX "decklink-output-"
120 #define VIDEO_CFG_PREFIX "decklink-vout-"
121 #define AUDIO_CFG_PREFIX "decklink-aout-"
123 /* Video Connections */
124 static const char *const ppsz_videoconns
[] = {
132 static const char *const ppsz_videoconns_text
[] = {
140 static const BMDVideoConnection rgbmd_videoconns
[] =
142 bmdVideoConnectionSDI
,
143 bmdVideoConnectionHDMI
,
144 bmdVideoConnectionOpticalSDI
,
145 bmdVideoConnectionComponent
,
146 bmdVideoConnectionComposite
,
147 bmdVideoConnectionSVideo
,
149 static_assert(ARRAY_SIZE(rgbmd_videoconns
) == ARRAY_SIZE(ppsz_videoconns
), "videoconn arrays messed up");
150 static_assert(ARRAY_SIZE(rgbmd_videoconns
) == ARRAY_SIZE(ppsz_videoconns_text
), "videoconn arrays messed up");
152 static const int rgi_afd_values
[] = {
153 0, 2, 3, 4, 8, 9, 10, 11, 13, 14, 15,
155 static const char * const rgsz_afd_text
[] = {
157 "2: Box 16:9 (top aligned)",
158 "3: Box 14:9 (top aligned)",
159 "4: Box > 16:9 (centre aligned)",
160 "8: Same as coded frame (full frame)",
161 "9: 4:3 (centre aligned)",
162 "10: 16:9 (centre aligned)",
163 "11: 14:9 (centre aligned)",
164 "13: 4:3 (with shoot and protect 14:9 centre)",
165 "14: 16:9 (with shoot and protect 14:9 centre)",
166 "15: 16:9 (with shoot and protect 4:3 centre)",
168 static_assert(ARRAY_SIZE(rgi_afd_values
) == ARRAY_SIZE(rgsz_afd_text
), "afd arrays messed up");
170 static const int rgi_ar_values
[] = {
173 static const char * const rgsz_ar_text
[] = {
177 static_assert(ARRAY_SIZE(rgi_ar_values
) == ARRAY_SIZE(rgsz_ar_text
), "afd arrays messed up");
179 /* Only one audio output module and one video output module
180 * can be used per process.
181 * We use a static mutex in audio/video submodules entry points. */
182 typedef struct decklink_sys_t
185 IDeckLinkOutput
*p_output
;
188 * Synchronizes aout and vout modules:
189 * vout module waits until aout has been initialized.
190 * That means video-only output is NOT supported.
201 BMDTimeScale timescale
;
202 BMDTimeValue frameduration
;
204 /* XXX: workaround card clock drift */
209 /* single video module exclusive */
212 video_format_t currentfmt
;
213 picture_pool_t
*pool
;
217 picture_t
*pic_nosignal
;
221 /*****************************************************************************
223 *****************************************************************************/
225 static int OpenVideo (vlc_object_t
*);
226 static void CloseVideo (vlc_object_t
*);
227 static int OpenAudio (vlc_object_t
*);
228 static void CloseAudio (vlc_object_t
*);
230 /*****************************************************************************
232 *****************************************************************************/
235 set_shortname(N_("DecklinkOutput"))
236 set_description(N_("output module to write to Blackmagic SDI card"))
237 set_section(N_("DeckLink General Options"), NULL
)
238 add_integer(CFG_PREFIX
"card-index", 0,
239 CARD_INDEX_TEXT
, CARD_INDEX_LONGTEXT
, true)
242 set_description (N_("DeckLink Video Output module"))
243 set_category(CAT_VIDEO
)
244 set_subcategory(SUBCAT_VIDEO_VOUT
)
245 set_capability("vout display", 0)
246 set_callbacks (OpenVideo
, CloseVideo
)
247 set_section(N_("DeckLink Video Options"), NULL
)
248 add_string(VIDEO_CFG_PREFIX
"video-connection", "sdi",
249 VIDEO_CONNECTION_TEXT
, VIDEO_CONNECTION_LONGTEXT
, true)
250 change_string_list(ppsz_videoconns
, ppsz_videoconns_text
)
251 add_string(VIDEO_CFG_PREFIX
"mode", "",
252 MODE_TEXT
, MODE_LONGTEXT
, true)
253 add_bool(VIDEO_CFG_PREFIX
"tenbits", true,
254 VIDEO_TENBITS_TEXT
, VIDEO_TENBITS_LONGTEXT
, true)
255 add_integer(VIDEO_CFG_PREFIX
"nosignal-delay", 5,
256 NOSIGNAL_INDEX_TEXT
, NOSIGNAL_INDEX_LONGTEXT
, true)
257 add_integer(VIDEO_CFG_PREFIX
"afd-line", 16,
258 AFDLINE_INDEX_TEXT
, AFDLINE_INDEX_LONGTEXT
, true)
259 add_integer_with_range(VIDEO_CFG_PREFIX
"afd", 8, 0, 16,
260 AFD_INDEX_TEXT
, AFD_INDEX_LONGTEXT
, true)
261 change_integer_list(rgi_afd_values
, rgsz_afd_text
)
262 add_integer_with_range(VIDEO_CFG_PREFIX
"ar", 1, 0, 1,
263 AR_INDEX_TEXT
, AR_INDEX_LONGTEXT
, true)
264 change_integer_list(rgi_ar_values
, rgsz_ar_text
)
265 add_loadfile(VIDEO_CFG_PREFIX
"nosignal-image", NULL
,
266 NOSIGNAL_IMAGE_TEXT
, NOSIGNAL_IMAGE_LONGTEXT
, true)
270 set_description (N_("DeckLink Audio Output module"))
271 set_category(CAT_AUDIO
)
272 set_subcategory(SUBCAT_AUDIO_AOUT
)
273 set_capability("audio output", 0)
274 set_callbacks (OpenAudio
, CloseAudio
)
275 set_section(N_("DeckLink Audio Options"), NULL
)
276 add_obsolete_string("audio-connection")
277 add_integer(AUDIO_CFG_PREFIX
"audio-rate", 48000,
278 RATE_TEXT
, RATE_LONGTEXT
, true)
279 add_integer(AUDIO_CFG_PREFIX
"audio-channels", 2,
280 CHANNELS_TEXT
, CHANNELS_LONGTEXT
, true)
283 /* Protects decklink_sys_t creation/deletion */
284 static vlc_mutex_t sys_lock
= VLC_STATIC_MUTEX
;
286 static decklink_sys_t
*HoldDLSys(vlc_object_t
*obj
, int i_cat
)
288 vlc_object_t
*libvlc
= VLC_OBJECT(obj
->obj
.libvlc
);
291 vlc_mutex_lock(&sys_lock
);
293 if (var_Type(libvlc
, "decklink-sys") == VLC_VAR_ADDRESS
)
295 sys
= (decklink_sys_t
*)var_GetAddress(libvlc
, "decklink-sys");
298 if(i_cat
== VIDEO_ES
)
300 while(sys
->b_videomodule
)
302 vlc_mutex_unlock(&sys_lock
);
303 msg_Info(obj
, "Waiting for previous vout module to exit");
304 msleep(CLOCK_FREQ
/ 10);
305 vlc_mutex_lock(&sys_lock
);
311 sys
= (decklink_sys_t
*)malloc(sizeof(*sys
));
313 sys
->p_output
= NULL
;
316 sys
->b_videomodule
= (i_cat
== VIDEO_ES
);
317 sys
->b_recycling
= false;
318 sys
->i_rate
= var_InheritInteger(obj
, AUDIO_CFG_PREFIX
"audio-rate");
321 vlc_mutex_init(&sys
->lock
);
322 vlc_cond_init(&sys
->cond
);
323 var_Create(libvlc
, "decklink-sys", VLC_VAR_ADDRESS
);
324 var_SetAddress(libvlc
, "decklink-sys", (void*)sys
);
328 vlc_mutex_unlock(&sys_lock
);
332 static void ReleaseDLSys(vlc_object_t
*obj
, int i_cat
)
334 vlc_object_t
*libvlc
= VLC_OBJECT(obj
->obj
.libvlc
);
336 vlc_mutex_lock(&sys_lock
);
338 struct decklink_sys_t
*sys
= (struct decklink_sys_t
*)var_GetAddress(libvlc
, "decklink-sys");
340 if (--sys
->users
== 0) {
341 msg_Dbg(obj
, "Destroying decklink data");
342 vlc_mutex_destroy(&sys
->lock
);
343 vlc_cond_destroy(&sys
->cond
);
346 sys
->p_output
->StopScheduledPlayback(0, NULL
, 0);
347 sys
->p_output
->DisableVideoOutput();
348 sys
->p_output
->DisableAudioOutput();
349 sys
->p_output
->Release();
352 /* Clean video specific */
354 picture_pool_Release(sys
->video
.pool
);
355 if (sys
->video
.pic_nosignal
)
356 picture_Release(sys
->video
.pic_nosignal
);
357 video_format_Clean(&sys
->video
.currentfmt
);
360 var_Destroy(libvlc
, "decklink-sys");
362 else if (i_cat
== VIDEO_ES
)
364 sys
->b_videomodule
= false;
365 sys
->b_recycling
= true;
368 vlc_mutex_unlock(&sys_lock
);
371 static BMDVideoConnection
getVConn(vout_display_t
*vd
, BMDVideoConnection mask
)
373 BMDVideoConnection conn
= 0;
374 char *psz
= var_InheritString(vd
, VIDEO_CFG_PREFIX
"video-connection");
377 for(size_t i
=0; i
<ARRAY_SIZE(rgbmd_videoconns
); i
++)
379 if (!strcmp(psz
, ppsz_videoconns
[i
]) && (mask
& rgbmd_videoconns
[i
]))
381 conn
= rgbmd_videoconns
[i
];
387 else /* Pick one as default connection */
390 conn
= conn
? ( 1 << conn
) : bmdVideoConnectionSDI
;
395 /*****************************************************************************
397 *****************************************************************************/
402 const char * const psz_string
;
403 } const errors_to_string
[] = {
404 { E_UNEXPECTED
, "Unexpected error" },
405 { E_NOTIMPL
, "Not implemented" },
406 { E_OUTOFMEMORY
, "Out of memory" },
407 { E_INVALIDARG
, "Invalid argument" },
408 { E_NOINTERFACE
, "No interface" },
409 { E_POINTER
, "Invalid pointer" },
410 { E_HANDLE
, "Invalid handle" },
411 { E_ABORT
, "Aborted" },
412 { E_FAIL
, "Failed" },
413 { E_ACCESSDENIED
,"Access denied" }
416 static const char * lookup_error_string(long i_code
)
418 for(size_t i
=0; i
<ARRAY_SIZE(errors_to_string
); i
++)
420 if(errors_to_string
[i
].i_return_code
== i_code
)
421 return errors_to_string
[i
].psz_string
;
426 static picture_t
* CreateNoSignalPicture(vlc_object_t
*p_this
, const video_format_t
*fmt
,
427 const char *psz_file
)
429 picture_t
*p_pic
= NULL
;
430 image_handler_t
*img
= image_HandlerCreate(p_this
);
433 msg_Err(p_this
, "Could not create image converter");
437 video_format_t in
, dummy
;
438 video_format_Init(&dummy
, 0);
439 video_format_Init(&in
, 0);
440 video_format_Setup(&in
, 0,
441 fmt
->i_width
, fmt
->i_height
,
442 fmt
->i_width
, fmt
->i_height
, 1, 1);
444 picture_t
*png
= image_ReadUrl(img
, psz_file
, &dummy
, &in
);
447 video_format_Clean(&dummy
);
448 video_format_Copy(&dummy
, fmt
);
449 p_pic
= image_Convert(img
, png
, &in
, &dummy
);
450 if(!video_format_IsSimilar(&dummy
, fmt
))
452 picture_Release(p_pic
);
455 picture_Release(png
);
457 image_HandlerDelete(img
);
458 video_format_Clean(&in
);
459 video_format_Clean(&dummy
);
464 static IDeckLinkDisplayMode
* MatchDisplayMode(vout_display_t
*vd
,
465 IDeckLinkOutput
*output
,
466 const video_format_t
*fmt
,
467 BMDDisplayMode forcedmode
= bmdDisplayModeNotSupported
)
470 IDeckLinkDisplayMode
*p_selected
= NULL
;
471 IDeckLinkDisplayModeIterator
*p_iterator
= NULL
;
473 for(int i
=0; i
<4 && p_selected
==NULL
; i
++)
475 int i_width
= (i
% 2 == 0) ? fmt
->i_width
: fmt
->i_visible_width
;
476 int i_height
= (i
% 2 == 0) ? fmt
->i_height
: fmt
->i_visible_height
;
477 int i_div
= (i
> 2) ? 4 : 0;
479 result
= output
->GetDisplayModeIterator(&p_iterator
);
482 IDeckLinkDisplayMode
*p_mode
= NULL
;
483 while(p_iterator
->Next(&p_mode
) == S_OK
)
485 BMDDisplayMode mode_id
= p_mode
->GetDisplayMode();
486 BMDTimeValue frameduration
;
487 BMDTimeScale timescale
;
488 const char *psz_mode_name
;
490 if(p_mode
->GetFrameRate(&frameduration
, ×cale
) == S_OK
&&
491 p_mode
->GetName(&psz_mode_name
) == S_OK
)
493 BMDDisplayMode modenl
= htonl(mode_id
);
496 BMDFieldDominance field
= htonl(p_mode
->GetFieldDominance());
497 msg_Dbg(vd
, "Found mode '%4.4s': %s (%ldx%ld, %.3f fps, %4.4s, scale %ld dur %ld)",
498 (char*)&modenl
, psz_mode_name
,
499 p_mode
->GetWidth(), p_mode
->GetHeight(),
501 double(timescale
) / frameduration
,
502 timescale
, frameduration
);
511 if(forcedmode
!= bmdDisplayModeNotSupported
&& unlikely(!p_selected
))
513 BMDDisplayMode modenl
= htonl(forcedmode
);
514 msg_Dbg(vd
, "Forced mode '%4.4s'", (char *)&modenl
);
515 if(forcedmode
== mode_id
)
522 if(p_selected
== NULL
&& forcedmode
== bmdDisplayModeNotSupported
)
524 if(i_width
>> i_div
== p_mode
->GetWidth() >> i_div
&&
525 i_height
>> i_div
== p_mode
->GetHeight() >> i_div
)
527 unsigned int num_deck
, den_deck
;
528 unsigned int num_stream
, den_stream
;
529 vlc_ureduce(&num_deck
, &den_deck
, timescale
, frameduration
, 0);
530 vlc_ureduce(&num_stream
, &den_stream
,
531 fmt
->i_frame_rate
, fmt
->i_frame_rate_base
, 0);
533 if (num_deck
== num_stream
&& den_deck
== den_stream
)
535 msg_Info(vd
, "Matches incoming stream");
544 p_iterator
->Release();
550 static int OpenDecklink(vout_display_t
*vd
, decklink_sys_t
*sys
)
552 #define CHECK(message) do { \
553 if (result != S_OK) \
555 const char *psz_err = lookup_error_string(result); \
557 msg_Err(vd, message ": %s", psz_err); \
559 msg_Err(vd, message ": 0x%X", result); \
565 IDeckLinkIterator
*decklink_iterator
= NULL
;
566 IDeckLinkDisplayMode
*p_display_mode
= NULL
;
567 IDeckLinkConfiguration
*p_config
= NULL
;
568 IDeckLinkAttributes
*p_attributes
= NULL
;
569 IDeckLink
*p_card
= NULL
;
570 BMDDisplayMode wanted_mode_id
= bmdDisplayModeNotSupported
;
572 vlc_mutex_lock(&sys
->lock
);
574 /* wait until aout is ready */
575 msg_Info(vd
, "Waiting for DeckLink audio input module to start");
576 while (sys
->i_rate
== -1)
577 vlc_cond_wait(&sys
->cond
, &sys
->lock
);
579 int i_card_index
= var_InheritInteger(vd
, CFG_PREFIX
"card-index");
580 char *mode
= var_InheritString(vd
, VIDEO_CFG_PREFIX
"mode");
584 size_t len
= strlen(mode
);
588 msg_Err(vd
, "Invalid mode %s", mode
);
591 memset(&wanted_mode_id
, ' ', 4);
592 strncpy((char*)&wanted_mode_id
, mode
, 4);
593 wanted_mode_id
= ntohl(wanted_mode_id
);
597 if (i_card_index
< 0)
599 msg_Err(vd
, "Invalid card index %d", i_card_index
);
603 decklink_iterator
= CreateDeckLinkIteratorInstance();
604 if (!decklink_iterator
)
606 msg_Err(vd
, "DeckLink drivers not found.");
610 for(int i
= 0; i
<= i_card_index
; ++i
)
614 result
= decklink_iterator
->Next(&p_card
);
615 CHECK("Card not found");
618 const char *psz_model_name
;
619 result
= p_card
->GetModelName(&psz_model_name
);
620 CHECK("Unknown model name");
622 msg_Dbg(vd
, "Opened DeckLink PCI card %s", psz_model_name
);
624 /* Read attributes */
626 result
= p_card
->QueryInterface(IID_IDeckLinkAttributes
, (void**)&p_attributes
);
627 CHECK("Could not get IDeckLinkAttributes");
630 result
= p_attributes
->GetInt(BMDDeckLinkVideoOutputConnections
, &vconn
); /* reads mask */
631 CHECK("Could not get BMDDeckLinkVideoOutputConnections");
633 result
= p_card
->QueryInterface(IID_IDeckLinkOutput
,
634 (void**)&sys
->p_output
);
637 result
= p_card
->QueryInterface(IID_IDeckLinkConfiguration
,
639 CHECK("Could not get config interface");
641 /* Now configure card */
643 vconn
= getVConn(vd
, (BMDVideoConnection
) vconn
);
646 msg_Err(vd
, "Invalid video connection specified");
650 result
= p_config
->SetInt(bmdDeckLinkConfigVideoOutputConnection
, (BMDVideoConnection
) vconn
);
651 CHECK("Could not set video output connection");
653 p_display_mode
= MatchDisplayMode(vd
, sys
->p_output
,
654 &vd
->fmt
, wanted_mode_id
);
655 if(p_display_mode
== NULL
)
657 msg_Err(vd
, "Could not negociate a compatible display mode");
662 BMDDisplayMode mode_id
= p_display_mode
->GetDisplayMode();
663 BMDDisplayMode modenl
= htonl(mode_id
);
664 msg_Dbg(vd
, "Selected mode '%4.4s'", (char *) &modenl
);
666 BMDVideoOutputFlags flags
= bmdVideoOutputVANC
;
667 if (mode_id
== bmdModeNTSC
||
668 mode_id
== bmdModeNTSC2398
||
669 mode_id
== bmdModePAL
)
671 flags
= bmdVideoOutputVITC
;
674 BMDDisplayModeSupport support
;
675 IDeckLinkDisplayMode
*resultMode
;
677 result
= sys
->p_output
->DoesSupportVideoMode(mode_id
,
678 sys
->video
.tenbits
? bmdFormat10BitYUV
: bmdFormat8BitYUV
,
679 flags
, &support
, &resultMode
);
680 CHECK("Does not support video mode");
681 if (support
== bmdDisplayModeNotSupported
)
683 msg_Err(vd
, "Video mode not supported");
687 if (p_display_mode
->GetWidth() <= 0 || p_display_mode
->GetWidth() & 1)
689 msg_Err(vd
, "Unknown video mode specified.");
693 result
= p_display_mode
->GetFrameRate(&sys
->frameduration
,
695 CHECK("Could not read frame rate");
697 result
= sys
->p_output
->EnableVideoOutput(mode_id
, flags
);
698 CHECK("Could not enable video output");
700 video_format_t
*fmt
= &sys
->video
.currentfmt
;
701 video_format_Copy(fmt
, &vd
->fmt
);
702 fmt
->i_width
= fmt
->i_visible_width
= p_display_mode
->GetWidth();
703 fmt
->i_height
= fmt
->i_visible_height
= p_display_mode
->GetHeight();
708 fmt
->i_chroma
= !sys
->video
.tenbits
? VLC_CODEC_UYVY
: VLC_CODEC_I422_10L
; /* we will convert to v210 */
709 fmt
->i_frame_rate
= (unsigned) sys
->frameduration
;
710 fmt
->i_frame_rate_base
= (unsigned) sys
->timescale
;
713 if (/*decklink_sys->i_channels > 0 &&*/ sys
->i_rate
> 0)
715 result
= sys
->p_output
->EnableAudioOutput(
717 bmdAudioSampleType16bitInteger
,
718 /*decklink_sys->i_channels*/ 2,
719 bmdAudioOutputStreamTimestamped
);
720 CHECK("Could not start audio output");
724 result
= sys
->p_output
->StartScheduledPlayback(
725 (mdate() * sys
->timescale
) / CLOCK_FREQ
, sys
->timescale
, 1.0);
726 CHECK("Could not start playback");
729 p_display_mode
->Release();
731 p_attributes
->Release();
732 decklink_iterator
->Release();
734 vlc_mutex_unlock(&sys
->lock
);
736 vout_display_DeleteWindow(vd
, NULL
);
742 sys
->p_output
->Release();
743 sys
->p_output
= NULL
;
750 p_attributes
->Release();
751 if (decklink_iterator
)
752 decklink_iterator
->Release();
754 p_display_mode
->Release();
756 vlc_mutex_unlock(&sys
->lock
);
762 /*****************************************************************************
764 *****************************************************************************/
766 static picture_pool_t
*PoolVideo(vout_display_t
*vd
, unsigned requested_count
)
768 struct decklink_sys_t
*sys
= (struct decklink_sys_t
*) vd
->sys
;
769 if (!sys
->video
.pool
)
770 sys
->video
.pool
= picture_pool_NewFromFormat(&vd
->fmt
, requested_count
);
771 return sys
->video
.pool
;
774 static inline void put_le32(uint8_t **p
, uint32_t d
)
780 static inline int clip(int a
)
783 else if (a
> 1019) return 1019;
787 static void v210_convert(void *frame_bytes
, picture_t
*pic
, int dst_stride
)
789 int width
= pic
->format
.i_width
;
790 int height
= pic
->format
.i_height
;
791 int line_padding
= dst_stride
- ((width
* 8 + 11) / 12) * 4;
793 uint8_t *data
= (uint8_t*)frame_bytes
;
795 const uint16_t *y
= (const uint16_t*)pic
->p
[0].p_pixels
;
796 const uint16_t *u
= (const uint16_t*)pic
->p
[1].p_pixels
;
797 const uint16_t *v
= (const uint16_t*)pic
->p
[2].p_pixels
;
799 #define WRITE_PIXELS(a, b, c) \
802 val |= (clip(*b++) << 10) | \
803 (clip(*c++) << 20); \
804 put_le32(&data, val); \
807 for (h
= 0; h
< height
; h
++) {
809 for (w
= 0; w
< width
- 5; w
+= 6) {
810 WRITE_PIXELS(u
, y
, v
);
811 WRITE_PIXELS(y
, u
, y
);
812 WRITE_PIXELS(v
, y
, u
);
813 WRITE_PIXELS(y
, v
, y
);
816 WRITE_PIXELS(u
, y
, v
);
820 put_le32(&data
, val
);
824 val
|= (clip(*u
++) << 10) | (clip(*y
++) << 20);
825 put_le32(&data
, val
);
827 val
= clip(*v
++) | (clip(*y
++) << 10);
828 put_le32(&data
, val
);
831 memset(data
, 0, line_padding
);
832 data
+= line_padding
;
834 y
+= pic
->p
[0].i_pitch
/ 2 - width
;
835 u
+= pic
->p
[1].i_pitch
/ 2 - width
/ 2;
836 v
+= pic
->p
[2].i_pitch
/ 2 - width
/ 2;
840 static void send_AFD(uint8_t afdcode
, uint8_t ar
, uint8_t *buf
)
842 const size_t len
= 6 /* vanc header */ + 8 /* AFD data */ + 1 /* csum */;
843 const size_t s
= ((len
+ 5) / 6) * 6; // align for v210
850 afd
[3] = 0x41; // DID
851 afd
[4] = 0x05; // SDID
852 afd
[5] = 8; // Data Count
854 int bar_data_flags
= 0;
855 int bar_data_val1
= 0;
856 int bar_data_val2
= 0;
858 afd
[ 6] = ((afdcode
& 0x0F) << 3) | ((ar
& 0x01) << 2); /* SMPTE 2016-1 */
859 afd
[ 7] = 0; // reserved
860 afd
[ 8] = 0; // reserved
861 afd
[ 9] = bar_data_flags
<< 4;
862 afd
[10] = bar_data_val1
<< 8;
863 afd
[11] = bar_data_val1
& 0xff;
864 afd
[12] = bar_data_val2
<< 8;
865 afd
[13] = bar_data_val2
& 0xff;
868 for (size_t i
= 3; i
< len
- 1; i
++)
869 afd
[i
] |= parity(afd
[i
]) ? 0x100 : 0x200;
872 uint16_t vanc_sum
= 0;
873 for (size_t i
= 3; i
< len
- 1; i
++) {
878 afd
[len
- 1] = vanc_sum
| ((~vanc_sum
& 0x100) << 1);
881 for (size_t i
= len
; i
< s
; i
++)
884 /* convert to v210 and write into VANC */
885 for (size_t w
= 0; w
< s
/ 6 ; w
++) {
886 put_le32(&buf
, afd
[w
*6+0] << 10);
887 put_le32(&buf
, afd
[w
*6+1] | (afd
[w
*6+2] << 20));
888 put_le32(&buf
, afd
[w
*6+3] << 10);
889 put_le32(&buf
, afd
[w
*6+4] | (afd
[w
*6+5] << 20));
893 static void PrepareVideo(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*)
895 decklink_sys_t
*sys
= (decklink_sys_t
*) vd
->sys
;
896 mtime_t now
= mdate();
901 if (now
- picture
->date
> sys
->video
.nosignal_delay
* CLOCK_FREQ
) {
902 msg_Dbg(vd
, "no signal");
903 if (sys
->video
.pic_nosignal
) {
904 picture
= sys
->video
.pic_nosignal
;
906 if (sys
->video
.tenbits
) { // I422_10L
907 plane_t
*y
= &picture
->p
[0];
908 memset(y
->p_pixels
, 0x0, y
->i_lines
* y
->i_pitch
);
909 for (int i
= 1; i
< picture
->i_planes
; i
++) {
910 plane_t
*p
= &picture
->p
[i
];
911 size_t len
= p
->i_lines
* p
->i_pitch
/ 2;
912 int16_t *data
= (int16_t*)p
->p_pixels
;
913 for (size_t j
= 0; j
< len
; j
++) // XXX: SIMD
917 size_t len
= picture
->p
[0].i_lines
* picture
->p
[0].i_pitch
;
918 for (size_t i
= 0; i
< len
; i
+= 2) { // XXX: SIMD
919 picture
->p
[0].p_pixels
[i
+0] = 0x80;
920 picture
->p
[0].p_pixels
[i
+1] = 0;
928 int w
, h
, stride
, length
;
930 h
= vd
->fmt
.i_height
;
932 IDeckLinkMutableVideoFrame
*pDLVideoFrame
;
933 result
= sys
->p_output
->CreateVideoFrame(w
, h
, w
*3,
934 sys
->video
.tenbits
? bmdFormat10BitYUV
: bmdFormat8BitYUV
,
935 bmdFrameFlagDefault
, &pDLVideoFrame
);
937 if (result
!= S_OK
) {
938 msg_Err(vd
, "Failed to create video frame: 0x%X", result
);
939 pDLVideoFrame
= NULL
;
944 pDLVideoFrame
->GetBytes((void**)&frame_bytes
);
945 stride
= pDLVideoFrame
->GetRowBytes();
947 if (sys
->video
.tenbits
) {
948 IDeckLinkVideoFrameAncillary
*vanc
;
952 result
= sys
->p_output
->CreateAncillaryData(
953 sys
->video
.tenbits
? bmdFormat10BitYUV
: bmdFormat8BitYUV
, &vanc
);
954 if (result
!= S_OK
) {
955 msg_Err(vd
, "Failed to create vanc: %d", result
);
959 line
= var_InheritInteger(vd
, VIDEO_CFG_PREFIX
"afd-line");
960 result
= vanc
->GetBufferForVerticalBlankingLine(line
, &buf
);
961 if (result
!= S_OK
) {
962 msg_Err(vd
, "Failed to get VBI line %d: %d", line
, result
);
965 send_AFD(sys
->video
.afd
, sys
->video
.ar
, (uint8_t*)buf
);
967 v210_convert(frame_bytes
, picture
, stride
);
969 result
= pDLVideoFrame
->SetAncillaryData(vanc
);
971 if (result
!= S_OK
) {
972 msg_Err(vd
, "Failed to set vanc: %d", result
);
976 else for(int y
= 0; y
< h
; ++y
) {
977 uint8_t *dst
= (uint8_t *)frame_bytes
+ stride
* y
;
978 const uint8_t *src
= (const uint8_t *)picture
->p
[0].p_pixels
+
979 picture
->p
[0].i_pitch
* y
;
980 memcpy(dst
, src
, w
* 2 /* bpp */);
984 // compute frame duration in CLOCK_FREQ units
985 length
= (sys
->frameduration
* CLOCK_FREQ
) / sys
->timescale
;
987 picture
->date
-= sys
->offset
;
988 result
= sys
->p_output
->ScheduleVideoFrame(pDLVideoFrame
,
989 picture
->date
, length
, CLOCK_FREQ
);
991 if (result
!= S_OK
) {
992 msg_Err(vd
, "Dropped Video frame %" PRId64
": 0x%x",
993 picture
->date
, result
);
997 now
= mdate() - sys
->offset
;
999 BMDTimeValue decklink_now
;
1001 sys
->p_output
->GetScheduledStreamTime (CLOCK_FREQ
, &decklink_now
, &speed
);
1003 if ((now
- decklink_now
) > 400000) {
1004 /* XXX: workaround card clock drift */
1005 sys
->offset
+= 50000;
1006 msg_Err(vd
, "Delaying: offset now %" PRId64
, sys
->offset
);
1011 pDLVideoFrame
->Release();
1014 static void DisplayVideo(vout_display_t
*, picture_t
*picture
, subpicture_t
*)
1016 picture_Release(picture
);
1019 static int ControlVideo(vout_display_t
*vd
, int query
, va_list args
)
1021 (void) vd
; (void) query
; (void) args
;
1022 return VLC_EGENERIC
;
1025 static int OpenVideo(vlc_object_t
*p_this
)
1027 vout_display_t
*vd
= (vout_display_t
*)p_this
;
1028 decklink_sys_t
*sys
= HoldDLSys(p_this
, VIDEO_ES
);
1032 vd
->sys
= (vout_display_sys_t
*) sys
;
1035 vlc_mutex_lock(&sys
->lock
);
1036 b_init
= !sys
->b_recycling
;
1037 vlc_mutex_unlock(&sys
->lock
);
1041 sys
->video
.tenbits
= var_InheritBool(p_this
, VIDEO_CFG_PREFIX
"tenbits");
1042 sys
->video
.nosignal_delay
= var_InheritInteger(p_this
, VIDEO_CFG_PREFIX
"nosignal-delay");
1043 sys
->video
.afd
= var_InheritInteger(p_this
, VIDEO_CFG_PREFIX
"afd");
1044 sys
->video
.ar
= var_InheritInteger(p_this
, VIDEO_CFG_PREFIX
"ar");
1045 sys
->video
.pic_nosignal
= NULL
;
1046 sys
->video
.pool
= NULL
;
1047 video_format_Init( &sys
->video
.currentfmt
, 0 );
1049 if (OpenDecklink(vd
, sys
) != VLC_SUCCESS
)
1052 return VLC_EGENERIC
;
1055 char *pic_file
= var_InheritString(p_this
, VIDEO_CFG_PREFIX
"nosignal-image");
1058 sys
->video
.pic_nosignal
= CreateNoSignalPicture(p_this
, &vd
->fmt
, pic_file
);
1059 if (!sys
->video
.pic_nosignal
)
1060 msg_Err(p_this
, "Could not create no signal picture");
1065 /* vout must adapt */
1066 video_format_Clean( &vd
->fmt
);
1067 video_format_Copy( &vd
->fmt
, &sys
->video
.currentfmt
);
1069 vd
->pool
= PoolVideo
;
1070 vd
->prepare
= PrepareVideo
;
1071 vd
->display
= DisplayVideo
;
1072 vd
->control
= ControlVideo
;
1077 static void CloseVideo(vlc_object_t
*p_this
)
1079 ReleaseDLSys(p_this
, VIDEO_ES
);
1082 /*****************************************************************************
1084 *****************************************************************************/
1086 static void Flush (audio_output_t
*aout
, bool drain
)
1088 decklink_sys_t
*sys
= (decklink_sys_t
*) aout
->sys
;
1089 vlc_mutex_lock(&sys
->lock
);
1090 IDeckLinkOutput
*p_output
= sys
->p_output
;
1091 vlc_mutex_unlock(&sys
->lock
);
1097 sys
->p_output
->GetBufferedAudioSampleFrameCount(&samples
);
1098 msleep(CLOCK_FREQ
* samples
/ sys
->i_rate
);
1099 } else if (sys
->p_output
->FlushBufferedAudioSamples() == E_FAIL
)
1100 msg_Err(aout
, "Flush failed");
1103 static int TimeGet(audio_output_t
*, mtime_t
* restrict
)
1105 /* synchronization is handled by the card */
1109 static int Start(audio_output_t
*aout
, audio_sample_format_t
*restrict fmt
)
1111 decklink_sys_t
*sys
= (decklink_sys_t
*) aout
->sys
;
1113 if (sys
->i_rate
== 0)
1114 return VLC_EGENERIC
;
1116 fmt
->i_format
= VLC_CODEC_S16N
;
1117 fmt
->i_channels
= 2; //decklink_sys->i_channels;
1118 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
; //pi_channels_maps[fmt->i_channels];
1119 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
1120 fmt
->i_rate
= sys
->i_rate
;
1121 fmt
->i_bitspersample
= 16;
1122 fmt
->i_blockalign
= fmt
->i_channels
* fmt
->i_bitspersample
/8 ;
1123 fmt
->i_frame_length
= FRAME_SIZE
;
1128 static void PlayAudio(audio_output_t
*aout
, block_t
*audio
)
1130 decklink_sys_t
*sys
= (decklink_sys_t
*) aout
->sys
;
1131 vlc_mutex_lock(&sys
->lock
);
1132 IDeckLinkOutput
*p_output
= sys
->p_output
;
1133 audio
->i_pts
-= sys
->offset
;
1134 vlc_mutex_unlock(&sys
->lock
);
1136 block_Release(audio
);
1140 uint32_t sampleFrameCount
= audio
->i_buffer
/ (2 * 2 /*decklink_sys->i_channels*/);
1142 HRESULT result
= p_output
->ScheduleAudioSamples(
1143 audio
->p_buffer
, sampleFrameCount
, audio
->i_pts
, CLOCK_FREQ
, &written
);
1146 msg_Err(aout
, "Failed to schedule audio sample: 0x%X", result
);
1147 else if (sampleFrameCount
!= written
)
1148 msg_Err(aout
, "Written only %d samples out of %d", written
, sampleFrameCount
);
1150 block_Release(audio
);
1153 static int OpenAudio(vlc_object_t
*p_this
)
1155 audio_output_t
*aout
= (audio_output_t
*)p_this
;
1156 decklink_sys_t
*sys
= HoldDLSys(p_this
, AUDIO_ES
);
1160 aout
->sys
= (aout_sys_t
*) sys
;
1162 vlc_mutex_lock(&sys
->lock
);
1163 //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
1164 sys
->i_rate
= var_InheritInteger(aout
, AUDIO_CFG_PREFIX
"audio-rate");
1165 vlc_cond_signal(&sys
->cond
);
1166 vlc_mutex_unlock(&sys
->lock
);
1168 aout
->play
= PlayAudio
;
1169 aout
->start
= Start
;
1170 aout
->flush
= Flush
;
1171 aout
->time_get
= TimeGet
;
1175 aout
->mute_set
= NULL
;
1176 aout
->volume_set
= NULL
;
1181 static void CloseAudio(vlc_object_t
*p_this
)
1183 decklink_sys_t
*sys
= (decklink_sys_t
*) ((audio_output_t
*)p_this
)->sys
;
1184 vlc_mutex_lock(&sys
->lock
);
1185 vlc_mutex_unlock(&sys
->lock
);
1186 ReleaseDLSys(p_this
, AUDIO_ES
);