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 N_("Active Format Descriptor value")
75 #define AR_INDEX_TEXT N_("Aspect Ratio")
76 #define AR_INDEX_LONGTEXT N_("Aspect Ratio of the source picture")
78 #define AFDLINE_INDEX_TEXT N_("Active Format Descriptor line.")
79 #define AFDLINE_INDEX_LONGTEXT N_("VBI line on which to output Active Format Descriptor.")
81 #define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss.")
82 #define NOSIGNAL_IMAGE_LONGTEXT NOSIGNAL_IMAGE_TEXT
84 #define CARD_INDEX_TEXT N_("Output card")
85 #define CARD_INDEX_LONGTEXT N_(\
86 "DeckLink output card, if multiple exist. " \
87 "The cards are numbered from 0.")
89 #define MODE_TEXT N_("Desired output mode")
90 #define MODE_LONGTEXT N_(\
91 "Desired output mode for DeckLink output. " \
92 "This value should be a FOURCC code in textual " \
93 "form, e.g. \"ntsc\".")
95 #define AUDIO_CONNECTION_TEXT N_("Audio connection")
96 #define AUDIO_CONNECTION_LONGTEXT N_(\
97 "Audio connection for DeckLink output.")
100 #define RATE_TEXT N_("Audio samplerate (Hz)")
101 #define RATE_LONGTEXT N_(\
102 "Audio sampling rate (in hertz) for DeckLink output. " \
103 "0 disables audio output.")
105 #define CHANNELS_TEXT N_("Number of audio channels")
106 #define CHANNELS_LONGTEXT N_(\
107 "Number of output channels for DeckLink output. " \
108 "Must be 2, 8 or 16. 0 disables audio output.")
110 #define VIDEO_CONNECTION_TEXT N_("Video connection")
111 #define VIDEO_CONNECTION_LONGTEXT N_(\
112 "Video connection for DeckLink output.")
114 #define VIDEO_TENBITS_TEXT N_("10 bits")
115 #define VIDEO_TENBITS_LONGTEXT N_(\
116 "Use 10 bits per pixel for video frames.")
118 #define CFG_PREFIX "decklink-output-"
119 #define VIDEO_CFG_PREFIX "decklink-vout-"
120 #define AUDIO_CFG_PREFIX "decklink-aout-"
122 /* Video Connections */
123 static const char *const ppsz_videoconns
[] = {
131 static const char *const ppsz_videoconns_text
[] = {
139 static const BMDVideoConnection rgbmd_videoconns
[] =
141 bmdVideoConnectionSDI
,
142 bmdVideoConnectionHDMI
,
143 bmdVideoConnectionOpticalSDI
,
144 bmdVideoConnectionComponent
,
145 bmdVideoConnectionComposite
,
146 bmdVideoConnectionSVideo
,
148 static_assert(ARRAY_SIZE(rgbmd_videoconns
) == ARRAY_SIZE(ppsz_videoconns
), "videoconn arrays messed up");
149 static_assert(ARRAY_SIZE(rgbmd_videoconns
) == ARRAY_SIZE(ppsz_videoconns_text
), "videoconn arrays messed up");
151 static const int rgi_afd_values
[] = {
152 0, 2, 3, 4, 8, 9, 10, 11, 13, 14, 15,
154 static const char * const rgsz_afd_text
[] = {
156 "2: Box 16:9 (top aligned)",
157 "3: Box 14:9 (top aligned)",
158 "4: Box > 16:9 (centre aligned)",
159 "8: Same as coded frame (full frame)",
160 "9: 4:3 (centre aligned)",
161 "10: 16:9 (centre aligned)",
162 "11: 14:9 (centre aligned)",
163 "13: 4:3 (with shoot and protect 14:9 centre)",
164 "14: 16:9 (with shoot and protect 14:9 centre)",
165 "15: 16:9 (with shoot and protect 4:3 centre)",
167 static_assert(ARRAY_SIZE(rgi_afd_values
) == ARRAY_SIZE(rgsz_afd_text
), "afd arrays messed up");
169 static const int rgi_ar_values
[] = {
172 static const char * const rgsz_ar_text
[] = {
176 static_assert(ARRAY_SIZE(rgi_ar_values
) == ARRAY_SIZE(rgsz_ar_text
), "afd arrays messed up");
178 /* Only one audio output module and one video output module
179 * can be used per process.
180 * We use a static mutex in audio/video submodules entry points. */
181 typedef struct decklink_sys_t
184 IDeckLinkOutput
*p_output
;
187 * Synchronizes aout and vout modules:
188 * vout module waits until aout has been initialized.
189 * That means video-only output is NOT supported.
200 BMDTimeScale timescale
;
201 BMDTimeValue frameduration
;
203 /* XXX: workaround card clock drift */
208 /* single video module exclusive */
211 video_format_t currentfmt
;
212 picture_pool_t
*pool
;
216 picture_t
*pic_nosignal
;
220 /*****************************************************************************
222 *****************************************************************************/
224 static int OpenVideo (vlc_object_t
*);
225 static void CloseVideo (vlc_object_t
*);
226 static int OpenAudio (vlc_object_t
*);
227 static void CloseAudio (vlc_object_t
*);
229 /*****************************************************************************
231 *****************************************************************************/
234 set_shortname(N_("DecklinkOutput"))
235 set_description(N_("output module to write to Blackmagic SDI card"))
236 set_section(N_("DeckLink General Options"), NULL
)
237 add_integer(CFG_PREFIX
"card-index", 0,
238 CARD_INDEX_TEXT
, CARD_INDEX_LONGTEXT
, true)
241 set_description (N_("DeckLink Video Output module"))
242 set_category(CAT_VIDEO
)
243 set_subcategory(SUBCAT_VIDEO_VOUT
)
244 set_capability("vout display", 0)
245 set_callbacks (OpenVideo
, CloseVideo
)
246 set_section(N_("DeckLink Video Options"), NULL
)
247 add_string(VIDEO_CFG_PREFIX
"video-connection", "sdi",
248 VIDEO_CONNECTION_TEXT
, VIDEO_CONNECTION_LONGTEXT
, true)
249 change_string_list(ppsz_videoconns
, ppsz_videoconns_text
)
250 add_string(VIDEO_CFG_PREFIX
"mode", "",
251 MODE_TEXT
, MODE_LONGTEXT
, true)
252 add_bool(VIDEO_CFG_PREFIX
"tenbits", true,
253 VIDEO_TENBITS_TEXT
, VIDEO_TENBITS_LONGTEXT
, true)
254 add_integer(VIDEO_CFG_PREFIX
"nosignal-delay", 5,
255 NOSIGNAL_INDEX_TEXT
, NOSIGNAL_INDEX_LONGTEXT
, true)
256 add_integer(VIDEO_CFG_PREFIX
"afd-line", 16,
257 AFDLINE_INDEX_TEXT
, AFDLINE_INDEX_LONGTEXT
, true)
258 add_integer_with_range(VIDEO_CFG_PREFIX
"afd", 8, 0, 16,
259 AFD_INDEX_TEXT
, AFD_INDEX_TEXT
, true)
260 change_integer_list(rgi_afd_values
, rgsz_afd_text
)
261 add_integer_with_range(VIDEO_CFG_PREFIX
"ar", 1, 0, 1,
262 AR_INDEX_TEXT
, AR_INDEX_LONGTEXT
, true)
263 change_integer_list(rgi_ar_values
, rgsz_ar_text
)
264 add_loadfile(VIDEO_CFG_PREFIX
"nosignal-image", NULL
,
265 NOSIGNAL_IMAGE_TEXT
, NOSIGNAL_IMAGE_LONGTEXT
, true)
269 set_description (N_("DeckLink Audio Output module"))
270 set_category(CAT_AUDIO
)
271 set_subcategory(SUBCAT_AUDIO_AOUT
)
272 set_capability("audio output", 0)
273 set_callbacks (OpenAudio
, CloseAudio
)
274 set_section(N_("DeckLink Audio Options"), NULL
)
275 add_obsolete_string("audio-connection")
276 add_integer(AUDIO_CFG_PREFIX
"audio-rate", 48000,
277 RATE_TEXT
, RATE_LONGTEXT
, true)
278 add_integer(AUDIO_CFG_PREFIX
"audio-channels", 2,
279 CHANNELS_TEXT
, CHANNELS_LONGTEXT
, true)
282 /* Protects decklink_sys_t creation/deletion */
283 static vlc_mutex_t sys_lock
= VLC_STATIC_MUTEX
;
285 static decklink_sys_t
*HoldDLSys(vlc_object_t
*obj
, int i_cat
)
287 vlc_object_t
*libvlc
= VLC_OBJECT(obj
->obj
.libvlc
);
290 vlc_mutex_lock(&sys_lock
);
292 if (var_Type(libvlc
, "decklink-sys") == VLC_VAR_ADDRESS
)
294 sys
= (decklink_sys_t
*)var_GetAddress(libvlc
, "decklink-sys");
297 if(i_cat
== VIDEO_ES
)
299 while(sys
->b_videomodule
)
301 vlc_mutex_unlock(&sys_lock
);
302 msg_Info(obj
, "Waiting for previous vout module to exit");
303 msleep(CLOCK_FREQ
/ 10);
304 vlc_mutex_lock(&sys_lock
);
310 sys
= (decklink_sys_t
*)malloc(sizeof(*sys
));
312 sys
->p_output
= NULL
;
315 sys
->b_videomodule
= (i_cat
== VIDEO_ES
);
316 sys
->b_recycling
= false;
317 sys
->i_rate
= var_InheritInteger(obj
, AUDIO_CFG_PREFIX
"audio-rate");
320 vlc_mutex_init(&sys
->lock
);
321 vlc_cond_init(&sys
->cond
);
322 var_Create(libvlc
, "decklink-sys", VLC_VAR_ADDRESS
);
323 var_SetAddress(libvlc
, "decklink-sys", (void*)sys
);
327 vlc_mutex_unlock(&sys_lock
);
331 static void ReleaseDLSys(vlc_object_t
*obj
, int i_cat
)
333 vlc_object_t
*libvlc
= VLC_OBJECT(obj
->obj
.libvlc
);
335 vlc_mutex_lock(&sys_lock
);
337 struct decklink_sys_t
*sys
= (struct decklink_sys_t
*)var_GetAddress(libvlc
, "decklink-sys");
339 if (--sys
->users
== 0) {
340 msg_Dbg(obj
, "Destroying decklink data");
341 vlc_mutex_destroy(&sys
->lock
);
342 vlc_cond_destroy(&sys
->cond
);
345 sys
->p_output
->StopScheduledPlayback(0, NULL
, 0);
346 sys
->p_output
->DisableVideoOutput();
347 sys
->p_output
->DisableAudioOutput();
348 sys
->p_output
->Release();
351 /* Clean video specific */
353 picture_pool_Release(sys
->video
.pool
);
354 if (sys
->video
.pic_nosignal
)
355 picture_Release(sys
->video
.pic_nosignal
);
356 video_format_Clean(&sys
->video
.currentfmt
);
359 var_Destroy(libvlc
, "decklink-sys");
361 else if (i_cat
== VIDEO_ES
)
363 sys
->b_videomodule
= false;
364 sys
->b_recycling
= true;
367 vlc_mutex_unlock(&sys_lock
);
370 static BMDVideoConnection
getVConn(vout_display_t
*vd
, BMDVideoConnection mask
)
372 BMDVideoConnection conn
= 0;
373 char *psz
= var_InheritString(vd
, VIDEO_CFG_PREFIX
"video-connection");
376 for(size_t i
=0; i
<ARRAY_SIZE(rgbmd_videoconns
); i
++)
378 if (!strcmp(psz
, ppsz_videoconns
[i
]) && (mask
& rgbmd_videoconns
[i
]))
380 conn
= rgbmd_videoconns
[i
];
386 else /* Pick one as default connection */
389 conn
= conn
? ( 1 << conn
) : bmdVideoConnectionSDI
;
394 /*****************************************************************************
396 *****************************************************************************/
401 const char * const psz_string
;
402 } const errors_to_string
[] = {
403 { E_UNEXPECTED
, "Unexpected error" },
404 { E_NOTIMPL
, "Not implemented" },
405 { E_OUTOFMEMORY
, "Out of memory" },
406 { E_INVALIDARG
, "Invalid argument" },
407 { E_NOINTERFACE
, "No interface" },
408 { E_POINTER
, "Invalid pointer" },
409 { E_HANDLE
, "Invalid handle" },
410 { E_ABORT
, "Aborted" },
411 { E_FAIL
, "Failed" },
412 { E_ACCESSDENIED
,"Access denied" }
415 static const char * lookup_error_string(long i_code
)
417 for(size_t i
=0; i
<ARRAY_SIZE(errors_to_string
); i
++)
419 if(errors_to_string
[i
].i_return_code
== i_code
)
420 return errors_to_string
[i
].psz_string
;
425 static picture_t
* CreateNoSignalPicture(vlc_object_t
*p_this
, const video_format_t
*fmt
,
426 const char *psz_file
)
428 picture_t
*p_pic
= NULL
;
429 image_handler_t
*img
= image_HandlerCreate(p_this
);
432 msg_Err(p_this
, "Could not create image converter");
436 video_format_t in
, dummy
;
437 video_format_Init(&dummy
, 0);
438 video_format_Init(&in
, 0);
439 video_format_Setup(&in
, 0,
440 fmt
->i_width
, fmt
->i_height
,
441 fmt
->i_width
, fmt
->i_height
, 1, 1);
443 picture_t
*png
= image_ReadUrl(img
, psz_file
, &dummy
, &in
);
446 video_format_Clean(&dummy
);
447 video_format_Copy(&dummy
, fmt
);
448 p_pic
= image_Convert(img
, png
, &in
, &dummy
);
449 if(!video_format_IsSimilar(&dummy
, fmt
))
451 picture_Release(p_pic
);
454 picture_Release(png
);
456 image_HandlerDelete(img
);
457 video_format_Clean(&in
);
458 video_format_Clean(&dummy
);
463 static IDeckLinkDisplayMode
* MatchDisplayMode(vout_display_t
*vd
,
464 IDeckLinkOutput
*output
,
465 const video_format_t
*fmt
,
466 BMDDisplayMode forcedmode
= bmdDisplayModeNotSupported
)
469 IDeckLinkDisplayMode
*p_selected
= NULL
;
470 IDeckLinkDisplayModeIterator
*p_iterator
= NULL
;
472 for(int i
=0; i
<4 && p_selected
==NULL
; i
++)
474 int i_width
= (i
% 2 == 0) ? fmt
->i_width
: fmt
->i_visible_width
;
475 int i_height
= (i
% 2 == 0) ? fmt
->i_height
: fmt
->i_visible_height
;
476 int i_div
= (i
> 2) ? 4 : 0;
478 result
= output
->GetDisplayModeIterator(&p_iterator
);
481 IDeckLinkDisplayMode
*p_mode
= NULL
;
482 while(p_iterator
->Next(&p_mode
) == S_OK
)
484 BMDDisplayMode mode_id
= p_mode
->GetDisplayMode();
485 BMDTimeValue frameduration
;
486 BMDTimeScale timescale
;
487 const char *psz_mode_name
;
489 if(p_mode
->GetFrameRate(&frameduration
, ×cale
) == S_OK
&&
490 p_mode
->GetName(&psz_mode_name
) == S_OK
)
492 BMDDisplayMode modenl
= htonl(mode_id
);
495 BMDFieldDominance field
= htonl(p_mode
->GetFieldDominance());
496 msg_Dbg(vd
, "Found mode '%4.4s': %s (%ldx%ld, %.3f fps, %4.4s, scale %ld dur %ld)",
497 (char*)&modenl
, psz_mode_name
,
498 p_mode
->GetWidth(), p_mode
->GetHeight(),
500 double(timescale
) / frameduration
,
501 timescale
, frameduration
);
510 if(forcedmode
!= bmdDisplayModeNotSupported
&& unlikely(!p_selected
))
512 BMDDisplayMode modenl
= htonl(forcedmode
);
513 msg_Dbg(vd
, "Forced mode '%4.4s'", (char *)&modenl
);
514 if(forcedmode
== mode_id
)
521 if(p_selected
== NULL
&& forcedmode
== bmdDisplayModeNotSupported
)
523 if(i_width
>> i_div
== p_mode
->GetWidth() >> i_div
&&
524 i_height
>> i_div
== p_mode
->GetHeight() >> i_div
)
526 unsigned int num_deck
, den_deck
;
527 unsigned int num_stream
, den_stream
;
528 vlc_ureduce(&num_deck
, &den_deck
, timescale
, frameduration
, 0);
529 vlc_ureduce(&num_stream
, &den_stream
,
530 fmt
->i_frame_rate
, fmt
->i_frame_rate_base
, 0);
532 if (num_deck
== num_stream
&& den_deck
== den_stream
)
534 msg_Info(vd
, "Matches incoming stream");
543 p_iterator
->Release();
549 static int OpenDecklink(vout_display_t
*vd
, decklink_sys_t
*sys
)
551 #define CHECK(message) do { \
552 if (result != S_OK) \
554 const char *psz_err = lookup_error_string(result); \
556 msg_Err(vd, message ": %s", psz_err); \
558 msg_Err(vd, message ": 0x%X", result); \
564 IDeckLinkIterator
*decklink_iterator
= NULL
;
565 IDeckLinkDisplayMode
*p_display_mode
= NULL
;
566 IDeckLinkConfiguration
*p_config
= NULL
;
567 IDeckLinkAttributes
*p_attributes
= NULL
;
568 IDeckLink
*p_card
= NULL
;
569 BMDDisplayMode wanted_mode_id
= bmdDisplayModeNotSupported
;
571 vlc_mutex_lock(&sys
->lock
);
573 /* wait until aout is ready */
574 msg_Info(vd
, "Waiting for DeckLink audio input module to start");
575 while (sys
->i_rate
== -1)
576 vlc_cond_wait(&sys
->cond
, &sys
->lock
);
578 int i_card_index
= var_InheritInteger(vd
, CFG_PREFIX
"card-index");
579 char *mode
= var_InheritString(vd
, VIDEO_CFG_PREFIX
"mode");
583 size_t len
= strlen(mode
);
587 msg_Err(vd
, "Invalid mode %s", mode
);
590 memset(&wanted_mode_id
, ' ', 4);
591 strncpy((char*)&wanted_mode_id
, mode
, 4);
592 wanted_mode_id
= ntohl(wanted_mode_id
);
596 if (i_card_index
< 0)
598 msg_Err(vd
, "Invalid card index %d", i_card_index
);
602 decklink_iterator
= CreateDeckLinkIteratorInstance();
603 if (!decklink_iterator
)
605 msg_Err(vd
, "DeckLink drivers not found.");
609 for(int i
= 0; i
<= i_card_index
; ++i
)
613 result
= decklink_iterator
->Next(&p_card
);
614 CHECK("Card not found");
617 const char *psz_model_name
;
618 result
= p_card
->GetModelName(&psz_model_name
);
619 CHECK("Unknown model name");
621 msg_Dbg(vd
, "Opened DeckLink PCI card %s", psz_model_name
);
623 /* Read attributes */
625 result
= p_card
->QueryInterface(IID_IDeckLinkAttributes
, (void**)&p_attributes
);
626 CHECK("Could not get IDeckLinkAttributes");
629 result
= p_attributes
->GetInt(BMDDeckLinkVideoOutputConnections
, &vconn
); /* reads mask */
630 CHECK("Could not get BMDDeckLinkVideoOutputConnections");
632 result
= p_card
->QueryInterface(IID_IDeckLinkOutput
,
633 (void**)&sys
->p_output
);
636 result
= p_card
->QueryInterface(IID_IDeckLinkConfiguration
,
638 CHECK("Could not get config interface");
640 /* Now configure card */
642 vconn
= getVConn(vd
, (BMDVideoConnection
) vconn
);
645 msg_Err(vd
, "Invalid video connection specified");
649 result
= p_config
->SetInt(bmdDeckLinkConfigVideoOutputConnection
, (BMDVideoConnection
) vconn
);
650 CHECK("Could not set video output connection");
652 p_display_mode
= MatchDisplayMode(vd
, sys
->p_output
,
653 &vd
->fmt
, wanted_mode_id
);
654 if(p_display_mode
== NULL
)
656 msg_Err(vd
, "Could not negociate a compatible display mode");
661 BMDDisplayMode mode_id
= p_display_mode
->GetDisplayMode();
662 BMDDisplayMode modenl
= htonl(mode_id
);
663 msg_Dbg(vd
, "Selected mode '%4.4s'", (char *) &modenl
);
665 BMDVideoOutputFlags flags
= bmdVideoOutputVANC
;
666 if (mode_id
== bmdModeNTSC
||
667 mode_id
== bmdModeNTSC2398
||
668 mode_id
== bmdModePAL
)
670 flags
= bmdVideoOutputVITC
;
673 BMDDisplayModeSupport support
;
674 IDeckLinkDisplayMode
*resultMode
;
676 result
= sys
->p_output
->DoesSupportVideoMode(mode_id
,
677 sys
->video
.tenbits
? bmdFormat10BitYUV
: bmdFormat8BitYUV
,
678 flags
, &support
, &resultMode
);
679 CHECK("Does not support video mode");
680 if (support
== bmdDisplayModeNotSupported
)
682 msg_Err(vd
, "Video mode not supported");
686 if (p_display_mode
->GetWidth() <= 0 || p_display_mode
->GetWidth() & 1)
688 msg_Err(vd
, "Unknown video mode specified.");
692 result
= p_display_mode
->GetFrameRate(&sys
->frameduration
,
694 CHECK("Could not read frame rate");
696 result
= sys
->p_output
->EnableVideoOutput(mode_id
, flags
);
697 CHECK("Could not enable video output");
699 video_format_t
*fmt
= &sys
->video
.currentfmt
;
700 video_format_Copy(fmt
, &vd
->fmt
);
701 fmt
->i_width
= fmt
->i_visible_width
= p_display_mode
->GetWidth();
702 fmt
->i_height
= fmt
->i_visible_height
= p_display_mode
->GetHeight();
707 fmt
->i_chroma
= !sys
->video
.tenbits
? VLC_CODEC_UYVY
: VLC_CODEC_I422_10L
; /* we will convert to v210 */
708 fmt
->i_frame_rate
= (unsigned) sys
->frameduration
;
709 fmt
->i_frame_rate_base
= (unsigned) sys
->timescale
;
712 if (/*decklink_sys->i_channels > 0 &&*/ sys
->i_rate
> 0)
714 result
= sys
->p_output
->EnableAudioOutput(
716 bmdAudioSampleType16bitInteger
,
717 /*decklink_sys->i_channels*/ 2,
718 bmdAudioOutputStreamTimestamped
);
719 CHECK("Could not start audio output");
723 result
= sys
->p_output
->StartScheduledPlayback(
724 (mdate() * sys
->timescale
) / CLOCK_FREQ
, sys
->timescale
, 1.0);
725 CHECK("Could not start playback");
728 p_display_mode
->Release();
730 p_attributes
->Release();
731 decklink_iterator
->Release();
733 vlc_mutex_unlock(&sys
->lock
);
735 vout_display_DeleteWindow(vd
, NULL
);
741 sys
->p_output
->Release();
742 sys
->p_output
= NULL
;
749 p_attributes
->Release();
750 if (decklink_iterator
)
751 decklink_iterator
->Release();
753 p_display_mode
->Release();
755 vlc_mutex_unlock(&sys
->lock
);
761 /*****************************************************************************
763 *****************************************************************************/
765 static picture_pool_t
*PoolVideo(vout_display_t
*vd
, unsigned requested_count
)
767 struct decklink_sys_t
*sys
= (struct decklink_sys_t
*) vd
->sys
;
768 if (!sys
->video
.pool
)
769 sys
->video
.pool
= picture_pool_NewFromFormat(&vd
->fmt
, requested_count
);
770 return sys
->video
.pool
;
773 static inline void put_le32(uint8_t **p
, uint32_t d
)
779 static inline int clip(int a
)
782 else if (a
> 1019) return 1019;
786 static void v210_convert(void *frame_bytes
, picture_t
*pic
, int dst_stride
)
788 int width
= pic
->format
.i_width
;
789 int height
= pic
->format
.i_height
;
790 int line_padding
= dst_stride
- ((width
* 8 + 11) / 12) * 4;
792 uint8_t *data
= (uint8_t*)frame_bytes
;
794 const uint16_t *y
= (const uint16_t*)pic
->p
[0].p_pixels
;
795 const uint16_t *u
= (const uint16_t*)pic
->p
[1].p_pixels
;
796 const uint16_t *v
= (const uint16_t*)pic
->p
[2].p_pixels
;
798 #define WRITE_PIXELS(a, b, c) \
801 val |= (clip(*b++) << 10) | \
802 (clip(*c++) << 20); \
803 put_le32(&data, val); \
806 for (h
= 0; h
< height
; h
++) {
808 for (w
= 0; w
< width
- 5; w
+= 6) {
809 WRITE_PIXELS(u
, y
, v
);
810 WRITE_PIXELS(y
, u
, y
);
811 WRITE_PIXELS(v
, y
, u
);
812 WRITE_PIXELS(y
, v
, y
);
815 WRITE_PIXELS(u
, y
, v
);
819 put_le32(&data
, val
);
823 val
|= (clip(*u
++) << 10) | (clip(*y
++) << 20);
824 put_le32(&data
, val
);
826 val
= clip(*v
++) | (clip(*y
++) << 10);
827 put_le32(&data
, val
);
830 memset(data
, 0, line_padding
);
831 data
+= line_padding
;
833 y
+= pic
->p
[0].i_pitch
/ 2 - width
;
834 u
+= pic
->p
[1].i_pitch
/ 2 - width
/ 2;
835 v
+= pic
->p
[2].i_pitch
/ 2 - width
/ 2;
839 static void send_AFD(uint8_t afdcode
, uint8_t ar
, uint8_t *buf
)
841 const size_t len
= 6 /* vanc header */ + 8 /* AFD data */ + 1 /* csum */;
842 const size_t s
= ((len
+ 5) / 6) * 6; // align for v210
849 afd
[3] = 0x41; // DID
850 afd
[4] = 0x05; // SDID
851 afd
[5] = 8; // Data Count
853 int bar_data_flags
= 0;
854 int bar_data_val1
= 0;
855 int bar_data_val2
= 0;
857 afd
[ 6] = ((afdcode
& 0x0F) << 3) | ((ar
& 0x01) << 2); /* SMPTE 2016-1 */
858 afd
[ 7] = 0; // reserved
859 afd
[ 8] = 0; // reserved
860 afd
[ 9] = bar_data_flags
<< 4;
861 afd
[10] = bar_data_val1
<< 8;
862 afd
[11] = bar_data_val1
& 0xff;
863 afd
[12] = bar_data_val2
<< 8;
864 afd
[13] = bar_data_val2
& 0xff;
867 for (size_t i
= 3; i
< len
- 1; i
++)
868 afd
[i
] |= parity(afd
[i
]) ? 0x100 : 0x200;
871 uint16_t vanc_sum
= 0;
872 for (size_t i
= 3; i
< len
- 1; i
++) {
877 afd
[len
- 1] = vanc_sum
| ((~vanc_sum
& 0x100) << 1);
880 for (size_t i
= len
; i
< s
; i
++)
883 /* convert to v210 and write into VANC */
884 for (size_t w
= 0; w
< s
/ 6 ; w
++) {
885 put_le32(&buf
, afd
[w
*6+0] << 10);
886 put_le32(&buf
, afd
[w
*6+1] | (afd
[w
*6+2] << 20));
887 put_le32(&buf
, afd
[w
*6+3] << 10);
888 put_le32(&buf
, afd
[w
*6+4] | (afd
[w
*6+5] << 20));
892 static void PrepareVideo(vout_display_t
*vd
, picture_t
*picture
, subpicture_t
*)
894 decklink_sys_t
*sys
= (decklink_sys_t
*) vd
->sys
;
895 mtime_t now
= mdate();
900 if (now
- picture
->date
> sys
->video
.nosignal_delay
* CLOCK_FREQ
) {
901 msg_Dbg(vd
, "no signal");
902 if (sys
->video
.pic_nosignal
) {
903 picture
= sys
->video
.pic_nosignal
;
905 if (sys
->video
.tenbits
) { // I422_10L
906 plane_t
*y
= &picture
->p
[0];
907 memset(y
->p_pixels
, 0x0, y
->i_lines
* y
->i_pitch
);
908 for (int i
= 1; i
< picture
->i_planes
; i
++) {
909 plane_t
*p
= &picture
->p
[i
];
910 size_t len
= p
->i_lines
* p
->i_pitch
/ 2;
911 int16_t *data
= (int16_t*)p
->p_pixels
;
912 for (size_t j
= 0; j
< len
; j
++) // XXX: SIMD
916 size_t len
= picture
->p
[0].i_lines
* picture
->p
[0].i_pitch
;
917 for (size_t i
= 0; i
< len
; i
+= 2) { // XXX: SIMD
918 picture
->p
[0].p_pixels
[i
+0] = 0x80;
919 picture
->p
[0].p_pixels
[i
+1] = 0;
927 int w
, h
, stride
, length
;
929 h
= vd
->fmt
.i_height
;
931 IDeckLinkMutableVideoFrame
*pDLVideoFrame
;
932 result
= sys
->p_output
->CreateVideoFrame(w
, h
, w
*3,
933 sys
->video
.tenbits
? bmdFormat10BitYUV
: bmdFormat8BitYUV
,
934 bmdFrameFlagDefault
, &pDLVideoFrame
);
936 if (result
!= S_OK
) {
937 msg_Err(vd
, "Failed to create video frame: 0x%X", result
);
938 pDLVideoFrame
= NULL
;
943 pDLVideoFrame
->GetBytes((void**)&frame_bytes
);
944 stride
= pDLVideoFrame
->GetRowBytes();
946 if (sys
->video
.tenbits
) {
947 IDeckLinkVideoFrameAncillary
*vanc
;
951 result
= sys
->p_output
->CreateAncillaryData(
952 sys
->video
.tenbits
? bmdFormat10BitYUV
: bmdFormat8BitYUV
, &vanc
);
953 if (result
!= S_OK
) {
954 msg_Err(vd
, "Failed to create vanc: %d", result
);
958 line
= var_InheritInteger(vd
, VIDEO_CFG_PREFIX
"afd-line");
959 result
= vanc
->GetBufferForVerticalBlankingLine(line
, &buf
);
960 if (result
!= S_OK
) {
961 msg_Err(vd
, "Failed to get VBI line %d: %d", line
, result
);
964 send_AFD(sys
->video
.afd
, sys
->video
.ar
, (uint8_t*)buf
);
966 v210_convert(frame_bytes
, picture
, stride
);
968 result
= pDLVideoFrame
->SetAncillaryData(vanc
);
970 if (result
!= S_OK
) {
971 msg_Err(vd
, "Failed to set vanc: %d", result
);
975 else for(int y
= 0; y
< h
; ++y
) {
976 uint8_t *dst
= (uint8_t *)frame_bytes
+ stride
* y
;
977 const uint8_t *src
= (const uint8_t *)picture
->p
[0].p_pixels
+
978 picture
->p
[0].i_pitch
* y
;
979 memcpy(dst
, src
, w
* 2 /* bpp */);
983 // compute frame duration in CLOCK_FREQ units
984 length
= (sys
->frameduration
* CLOCK_FREQ
) / sys
->timescale
;
986 picture
->date
-= sys
->offset
;
987 result
= sys
->p_output
->ScheduleVideoFrame(pDLVideoFrame
,
988 picture
->date
, length
, CLOCK_FREQ
);
990 if (result
!= S_OK
) {
991 msg_Err(vd
, "Dropped Video frame %" PRId64
": 0x%x",
992 picture
->date
, result
);
996 now
= mdate() - sys
->offset
;
998 BMDTimeValue decklink_now
;
1000 sys
->p_output
->GetScheduledStreamTime (CLOCK_FREQ
, &decklink_now
, &speed
);
1002 if ((now
- decklink_now
) > 400000) {
1003 /* XXX: workaround card clock drift */
1004 sys
->offset
+= 50000;
1005 msg_Err(vd
, "Delaying: offset now %" PRId64
, sys
->offset
);
1010 pDLVideoFrame
->Release();
1013 static void DisplayVideo(vout_display_t
*, picture_t
*picture
, subpicture_t
*)
1015 picture_Release(picture
);
1018 static int ControlVideo(vout_display_t
*vd
, int query
, va_list args
)
1020 (void) vd
; (void) query
; (void) args
;
1021 return VLC_EGENERIC
;
1024 static int OpenVideo(vlc_object_t
*p_this
)
1026 vout_display_t
*vd
= (vout_display_t
*)p_this
;
1027 decklink_sys_t
*sys
= HoldDLSys(p_this
, VIDEO_ES
);
1031 vd
->sys
= (vout_display_sys_t
*) sys
;
1034 vlc_mutex_lock(&sys
->lock
);
1035 b_init
= !sys
->b_recycling
;
1036 vlc_mutex_unlock(&sys
->lock
);
1040 sys
->video
.tenbits
= var_InheritBool(p_this
, VIDEO_CFG_PREFIX
"tenbits");
1041 sys
->video
.nosignal_delay
= var_InheritInteger(p_this
, VIDEO_CFG_PREFIX
"nosignal-delay");
1042 sys
->video
.afd
= var_InheritInteger(p_this
, VIDEO_CFG_PREFIX
"afd");
1043 sys
->video
.ar
= var_InheritInteger(p_this
, VIDEO_CFG_PREFIX
"ar");
1044 sys
->video
.pic_nosignal
= NULL
;
1045 sys
->video
.pool
= NULL
;
1046 video_format_Init( &sys
->video
.currentfmt
, 0 );
1048 if (OpenDecklink(vd
, sys
) != VLC_SUCCESS
)
1051 return VLC_EGENERIC
;
1054 char *pic_file
= var_InheritString(p_this
, VIDEO_CFG_PREFIX
"nosignal-image");
1057 sys
->video
.pic_nosignal
= CreateNoSignalPicture(p_this
, &vd
->fmt
, pic_file
);
1058 if (!sys
->video
.pic_nosignal
)
1059 msg_Err(p_this
, "Could not create no signal picture");
1064 /* vout must adapt */
1065 video_format_Clean( &vd
->fmt
);
1066 video_format_Copy( &vd
->fmt
, &sys
->video
.currentfmt
);
1068 vd
->pool
= PoolVideo
;
1069 vd
->prepare
= PrepareVideo
;
1070 vd
->display
= DisplayVideo
;
1071 vd
->control
= ControlVideo
;
1076 static void CloseVideo(vlc_object_t
*p_this
)
1078 ReleaseDLSys(p_this
, VIDEO_ES
);
1081 /*****************************************************************************
1083 *****************************************************************************/
1085 static void Flush (audio_output_t
*aout
, bool drain
)
1087 decklink_sys_t
*sys
= (decklink_sys_t
*) aout
->sys
;
1088 vlc_mutex_lock(&sys
->lock
);
1089 IDeckLinkOutput
*p_output
= sys
->p_output
;
1090 vlc_mutex_unlock(&sys
->lock
);
1096 sys
->p_output
->GetBufferedAudioSampleFrameCount(&samples
);
1097 msleep(CLOCK_FREQ
* samples
/ sys
->i_rate
);
1098 } else if (sys
->p_output
->FlushBufferedAudioSamples() == E_FAIL
)
1099 msg_Err(aout
, "Flush failed");
1102 static int TimeGet(audio_output_t
*, mtime_t
* restrict
)
1104 /* synchronization is handled by the card */
1108 static int Start(audio_output_t
*aout
, audio_sample_format_t
*restrict fmt
)
1110 decklink_sys_t
*sys
= (decklink_sys_t
*) aout
->sys
;
1112 if (sys
->i_rate
== 0)
1113 return VLC_EGENERIC
;
1115 fmt
->i_format
= VLC_CODEC_S16N
;
1116 fmt
->i_channels
= 2; //decklink_sys->i_channels;
1117 fmt
->i_physical_channels
= AOUT_CHANS_STEREO
; //pi_channels_maps[fmt->i_channels];
1118 fmt
->channel_type
= AUDIO_CHANNEL_TYPE_BITMAP
;
1119 fmt
->i_rate
= sys
->i_rate
;
1120 fmt
->i_bitspersample
= 16;
1121 fmt
->i_blockalign
= fmt
->i_channels
* fmt
->i_bitspersample
/8 ;
1122 fmt
->i_frame_length
= FRAME_SIZE
;
1127 static void PlayAudio(audio_output_t
*aout
, block_t
*audio
)
1129 decklink_sys_t
*sys
= (decklink_sys_t
*) aout
->sys
;
1130 vlc_mutex_lock(&sys
->lock
);
1131 IDeckLinkOutput
*p_output
= sys
->p_output
;
1132 audio
->i_pts
-= sys
->offset
;
1133 vlc_mutex_unlock(&sys
->lock
);
1135 block_Release(audio
);
1139 uint32_t sampleFrameCount
= audio
->i_buffer
/ (2 * 2 /*decklink_sys->i_channels*/);
1141 HRESULT result
= p_output
->ScheduleAudioSamples(
1142 audio
->p_buffer
, sampleFrameCount
, audio
->i_pts
, CLOCK_FREQ
, &written
);
1145 msg_Err(aout
, "Failed to schedule audio sample: 0x%X", result
);
1146 else if (sampleFrameCount
!= written
)
1147 msg_Err(aout
, "Written only %d samples out of %d", written
, sampleFrameCount
);
1149 block_Release(audio
);
1152 static int OpenAudio(vlc_object_t
*p_this
)
1154 audio_output_t
*aout
= (audio_output_t
*)p_this
;
1155 decklink_sys_t
*sys
= HoldDLSys(p_this
, AUDIO_ES
);
1159 aout
->sys
= (aout_sys_t
*) sys
;
1161 vlc_mutex_lock(&sys
->lock
);
1162 //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
1163 sys
->i_rate
= var_InheritInteger(aout
, AUDIO_CFG_PREFIX
"audio-rate");
1164 vlc_cond_signal(&sys
->cond
);
1165 vlc_mutex_unlock(&sys
->lock
);
1167 aout
->play
= PlayAudio
;
1168 aout
->start
= Start
;
1169 aout
->flush
= Flush
;
1170 aout
->time_get
= TimeGet
;
1174 aout
->mute_set
= NULL
;
1175 aout
->volume_set
= NULL
;
1180 static void CloseAudio(vlc_object_t
*p_this
)
1182 decklink_sys_t
*sys
= (decklink_sys_t
*) ((audio_output_t
*)p_this
)->sys
;
1183 vlc_mutex_lock(&sys
->lock
);
1184 vlc_mutex_unlock(&sys
->lock
);
1185 ReleaseDLSys(p_this
, AUDIO_ES
);