Update translations from 2.2.x branch
[vlc.git] / modules / video_output / decklink.cpp
blob7992b344c3f28417c85b1340b82f6d2cca76bdd8
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
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include <vlc_fixups.h>
34 #include <cinttypes>
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>
45 #include <vlc_aout.h>
46 #include <arpa/inet.h>
48 #include <DeckLinkAPI.h>
49 #include <DeckLinkAPIDispatch.cpp>
51 #define FRAME_SIZE 1920
52 #define CHANNELS_MAX 6
54 #if 0
55 static const int pi_channels_maps[CHANNELS_MAX+1] =
58 AOUT_CHAN_CENTER,
59 AOUT_CHANS_STEREO,
60 AOUT_CHANS_3_0,
61 AOUT_CHANS_4_0,
62 AOUT_CHANS_5_0,
63 AOUT_CHANS_5_1,
65 #endif
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[] = {
125 "sdi",
126 "hdmi",
127 "opticalsdi",
128 "component",
129 "composite",
130 "svideo"
132 static const char *const ppsz_videoconns_text[] = {
133 "SDI",
134 "HDMI",
135 "Optical SDI",
136 "Component",
137 "Composite",
138 "S-video",
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[] = {
156 "0: Undefined",
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[] = {
171 0, 1,
173 static const char * const rgsz_ar_text[] = {
174 "0: 4:3",
175 "1: 16:9",
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
184 /* With LOCK */
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.
192 vlc_mutex_t lock;
193 vlc_cond_t cond;
194 uint8_t users;
195 bool b_videomodule;
196 bool b_recycling;
198 //int i_channels;
199 int i_rate;
201 BMDTimeScale timescale;
202 BMDTimeValue frameduration;
204 /* XXX: workaround card clock drift */
205 mtime_t offset;
207 /* !With LOCK */
209 /* single video module exclusive */
210 struct
212 video_format_t currentfmt;
213 picture_pool_t *pool;
214 bool tenbits;
215 uint8_t afd, ar;
216 int nosignal_delay;
217 picture_t *pic_nosignal;
218 } video;
219 } decklink_sys_t;
221 /*****************************************************************************
222 * Local prototypes.
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 /*****************************************************************************
231 * Module descriptor
232 *****************************************************************************/
234 vlc_module_begin()
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)
241 add_submodule ()
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)
269 add_submodule ()
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)
281 vlc_module_end ()
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);
289 decklink_sys_t *sys;
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");
296 sys->users++;
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);
309 else
311 sys = (decklink_sys_t*)malloc(sizeof(*sys));
312 if (sys) {
313 sys->p_output = NULL;
314 sys->offset = 0;
315 sys->users = 1;
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");
319 if(sys->i_rate > 0)
320 sys->i_rate = -1;
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);
329 return sys;
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);
345 if (sys->p_output) {
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 */
353 if (sys->video.pool)
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);
359 free(sys);
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");
375 if (psz)
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];
382 break;
385 free(psz);
387 else /* Pick one as default connection */
389 conn = ctz(mask);
390 conn = conn ? ( 1 << conn ) : bmdVideoConnectionSDI;
392 return conn;
395 /*****************************************************************************
397 *****************************************************************************/
399 static struct
401 long i_return_code;
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;
423 return NULL;
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);
431 if (!img)
433 msg_Err(p_this, "Could not create image converter");
434 return NULL;
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);
445 if (png)
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);
453 p_pic = NULL;
455 picture_Release(png);
457 image_HandlerDelete(img);
458 video_format_Clean(&in);
459 video_format_Clean(&dummy);
461 return p_pic;
464 static IDeckLinkDisplayMode * MatchDisplayMode(vout_display_t *vd,
465 IDeckLinkOutput *output,
466 const video_format_t *fmt,
467 BMDDisplayMode forcedmode = bmdDisplayModeNotSupported)
469 HRESULT result;
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);
480 if(result == S_OK)
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, &timescale) == S_OK &&
491 p_mode->GetName(&psz_mode_name) == S_OK)
493 BMDDisplayMode modenl = htonl(mode_id);
494 if(i==0)
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(),
500 (char *)&field,
501 double(timescale) / frameduration,
502 timescale, frameduration);
505 else
507 p_mode->Release();
508 continue;
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)
516 p_selected = p_mode;
517 else
518 p_mode->Release();
519 continue;
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");
536 p_selected = p_mode;
537 continue;
542 p_mode->Release();
544 p_iterator->Release();
547 return p_selected;
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); \
556 if(psz_err)\
557 msg_Err(vd, message ": %s", psz_err); \
558 else \
559 msg_Err(vd, message ": 0x%X", result); \
560 goto error; \
562 } while(0)
564 HRESULT 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");
582 if(mode)
584 size_t len = strlen(mode);
585 if (len > 4)
587 free(mode);
588 msg_Err(vd, "Invalid mode %s", mode);
589 goto error;
591 memset(&wanted_mode_id, ' ', 4);
592 strncpy((char*)&wanted_mode_id, mode, 4);
593 wanted_mode_id = ntohl(wanted_mode_id);
594 free(mode);
597 if (i_card_index < 0)
599 msg_Err(vd, "Invalid card index %d", i_card_index);
600 goto error;
603 decklink_iterator = CreateDeckLinkIteratorInstance();
604 if (!decklink_iterator)
606 msg_Err(vd, "DeckLink drivers not found.");
607 goto error;
610 for(int i = 0; i <= i_card_index; ++i)
612 if (p_card)
613 p_card->Release();
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");
629 int64_t vconn;
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);
635 CHECK("No outputs");
637 result = p_card->QueryInterface(IID_IDeckLinkConfiguration,
638 (void**)&p_config);
639 CHECK("Could not get config interface");
641 /* Now configure card */
643 vconn = getVConn(vd, (BMDVideoConnection) vconn);
644 if (vconn == 0)
646 msg_Err(vd, "Invalid video connection specified");
647 goto error;
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");
658 goto error;
660 else
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");
684 goto error;
687 if (p_display_mode->GetWidth() <= 0 || p_display_mode->GetWidth() & 1)
689 msg_Err(vd, "Unknown video mode specified.");
690 goto error;
693 result = p_display_mode->GetFrameRate(&sys->frameduration,
694 &sys->timescale);
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();
704 fmt->i_x_offset = 0;
705 fmt->i_y_offset = 0;
706 fmt->i_sar_num = 0;
707 fmt->i_sar_den = 0;
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(
716 sys->i_rate,
717 bmdAudioSampleType16bitInteger,
718 /*decklink_sys->i_channels*/ 2,
719 bmdAudioOutputStreamTimestamped);
720 CHECK("Could not start audio output");
723 /* start */
724 result = sys->p_output->StartScheduledPlayback(
725 (mdate() * sys->timescale) / CLOCK_FREQ, sys->timescale, 1.0);
726 CHECK("Could not start playback");
728 p_config->Release();
729 p_display_mode->Release();
730 p_card->Release();
731 p_attributes->Release();
732 decklink_iterator->Release();
734 vlc_mutex_unlock(&sys->lock);
736 vout_display_DeleteWindow(vd, NULL);
738 return VLC_SUCCESS;
740 error:
741 if (sys->p_output) {
742 sys->p_output->Release();
743 sys->p_output = NULL;
745 if (p_card)
746 p_card->Release();
747 if (p_config)
748 p_config->Release();
749 if (p_attributes)
750 p_attributes->Release();
751 if (decklink_iterator)
752 decklink_iterator->Release();
753 if (p_display_mode)
754 p_display_mode->Release();
756 vlc_mutex_unlock(&sys->lock);
758 return VLC_EGENERIC;
759 #undef CHECK
762 /*****************************************************************************
763 * Video
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)
776 SetDWLE(*p, d);
777 (*p) += 4;
780 static inline int clip(int a)
782 if (a < 4) return 4;
783 else if (a > 1019) return 1019;
784 else return a;
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;
792 int h, w;
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) \
800 do { \
801 val = clip(*a++); \
802 val |= (clip(*b++) << 10) | \
803 (clip(*c++) << 20); \
804 put_le32(&data, val); \
805 } while (0)
807 for (h = 0; h < height; h++) {
808 uint32_t val = 0;
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);
815 if (w < width - 1) {
816 WRITE_PIXELS(u, y, v);
818 val = clip(*y++);
819 if (w == width - 2)
820 put_le32(&data, val);
821 #undef WRITE_PIXELS
823 if (w < width - 3) {
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
845 uint16_t afd[s];
847 afd[0] = 0x000;
848 afd[1] = 0x3ff;
849 afd[2] = 0x3ff;
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;
867 /* parity bit */
868 for (size_t i = 3; i < len - 1; i++)
869 afd[i] |= parity(afd[i]) ? 0x100 : 0x200;
871 /* vanc checksum */
872 uint16_t vanc_sum = 0;
873 for (size_t i = 3; i < len - 1; i++) {
874 vanc_sum += afd[i];
875 vanc_sum &= 0x1ff;
878 afd[len - 1] = vanc_sum | ((~vanc_sum & 0x100) << 1);
880 /* pad */
881 for (size_t i = len; i < s; i++)
882 afd[i] = 0x040;
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();
898 if (!picture)
899 return;
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;
905 } else {
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
914 data[j] = 0x200;
916 } else { // UYVY
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;
924 picture->date = now;
927 HRESULT result;
928 int w, h, stride, length;
929 w = vd->fmt.i_width;
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;
940 goto end;
943 void *frame_bytes;
944 pDLVideoFrame->GetBytes((void**)&frame_bytes);
945 stride = pDLVideoFrame->GetRowBytes();
947 if (sys->video.tenbits) {
948 IDeckLinkVideoFrameAncillary *vanc;
949 int line;
950 void *buf;
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);
956 goto end;
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);
963 goto end;
965 send_AFD(sys->video.afd, sys->video.ar, (uint8_t*)buf);
967 v210_convert(frame_bytes, picture, stride);
969 result = pDLVideoFrame->SetAncillaryData(vanc);
970 vanc->Release();
971 if (result != S_OK) {
972 msg_Err(vd, "Failed to set vanc: %d", result);
973 goto end;
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);
994 goto end;
997 now = mdate() - sys->offset;
999 BMDTimeValue decklink_now;
1000 double speed;
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);
1009 end:
1010 if (pDLVideoFrame)
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);
1029 if(!sys)
1030 return VLC_ENOMEM;
1032 vd->sys = (vout_display_sys_t*) sys;
1034 bool b_init;
1035 vlc_mutex_lock(&sys->lock);
1036 b_init = !sys->b_recycling;
1037 vlc_mutex_unlock(&sys->lock);
1039 if( b_init )
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)
1051 CloseVideo(p_this);
1052 return VLC_EGENERIC;
1055 char *pic_file = var_InheritString(p_this, VIDEO_CFG_PREFIX "nosignal-image");
1056 if (pic_file)
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");
1061 free(pic_file);
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;
1074 return VLC_SUCCESS;
1077 static void CloseVideo(vlc_object_t *p_this)
1079 ReleaseDLSys(p_this, VIDEO_ES);
1082 /*****************************************************************************
1083 * Audio
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);
1092 if (!p_output)
1093 return;
1095 if (drain) {
1096 uint32_t samples;
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 */
1106 return -1;
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;
1125 return VLC_SUCCESS;
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);
1135 if (!p_output) {
1136 block_Release(audio);
1137 return;
1140 uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
1141 uint32_t written;
1142 HRESULT result = p_output->ScheduleAudioSamples(
1143 audio->p_buffer, sampleFrameCount, audio->i_pts, CLOCK_FREQ, &written);
1145 if (result != S_OK)
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);
1157 if(!sys)
1158 return VLC_ENOMEM;
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;
1173 aout->pause = NULL;
1174 aout->stop = NULL;
1175 aout->mute_set = NULL;
1176 aout->volume_set= NULL;
1178 return VLC_SUCCESS;
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);