qt: playlist: use item title if available
[vlc.git] / modules / video_output / decklink.cpp
blob4f25dc6d0163b0a59e5a5f5db8a2cf5adaba95e2
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>
42 #include <vlc_block.h>
43 #include <vlc_aout.h>
44 #include <vlc_cxx_helpers.hpp>
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
48 #endif
50 #include "../access/vlc_decklink.h"
51 #include "../stream_out/sdi/V210.hpp"
52 #include "../stream_out/sdi/Ancillary.hpp"
53 #include "../stream_out/sdi/DBMHelper.hpp"
54 #include "../stream_out/sdi/SDIGenerator.hpp"
55 #include <DeckLinkAPIDispatch.cpp>
56 #include <DeckLinkAPIVersion.h>
57 #if BLACKMAGIC_DECKLINK_API_VERSION < 0x0b010000
58 #define IID_IDeckLinkProfileAttributes IID_IDeckLinkAttributes
59 #define IDeckLinkProfileAttributes IDeckLinkAttributes
60 #endif
62 #define FRAME_SIZE 1920
63 #define CHANNELS_MAX 6
65 #if 0
66 static const int pi_channels_maps[CHANNELS_MAX+1] =
69 AOUT_CHAN_CENTER,
70 AOUT_CHANS_STEREO,
71 AOUT_CHANS_3_0,
72 AOUT_CHANS_4_0,
73 AOUT_CHANS_5_0,
74 AOUT_CHANS_5_1,
76 #endif
78 #define NOSIGNAL_INDEX_TEXT N_("Timelength after which we assume there is no signal.")
79 #define NOSIGNAL_INDEX_LONGTEXT N_(\
80 "Timelength after which we assume there is no signal.\n"\
81 "After this delay we black out the video."\
84 #define AFD_INDEX_TEXT N_("Active Format Descriptor value")
86 #define AR_INDEX_TEXT N_("Aspect Ratio")
87 #define AR_INDEX_LONGTEXT N_("Aspect Ratio of the source picture.")
89 #define AFDLINE_INDEX_TEXT N_("Active Format Descriptor line")
90 #define AFDLINE_INDEX_LONGTEXT N_("VBI line on which to output Active Format Descriptor.")
92 #define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss")
93 #define NOSIGNAL_IMAGE_LONGTEXT NOSIGNAL_IMAGE_TEXT
95 #define CARD_INDEX_TEXT N_("Output card")
96 #define CARD_INDEX_LONGTEXT N_(\
97 "DeckLink output card, if multiple exist. " \
98 "The cards are numbered from 0.")
100 #define MODE_TEXT N_("Desired output mode")
101 #define MODE_LONGTEXT N_(\
102 "Desired output mode for DeckLink output. " \
103 "This value should be a FOURCC code in textual " \
104 "form, e.g. \"ntsc\".")
106 #define AUDIO_CONNECTION_TEXT N_("Audio connection")
107 #define AUDIO_CONNECTION_LONGTEXT N_(\
108 "Audio connection for DeckLink output.")
111 #define RATE_TEXT N_("Audio samplerate (Hz)")
112 #define RATE_LONGTEXT N_(\
113 "Audio sampling rate (in hertz) for DeckLink output. " \
114 "0 disables audio output.")
116 #define CHANNELS_TEXT N_("Number of audio channels")
117 #define CHANNELS_LONGTEXT N_(\
118 "Number of output channels for DeckLink output. " \
119 "Must be 2, 8 or 16. 0 disables audio output.")
121 #define VIDEO_CONNECTION_TEXT N_("Video connection")
122 #define VIDEO_CONNECTION_LONGTEXT N_(\
123 "Video connection for DeckLink output.")
125 #define VIDEO_TENBITS_TEXT N_("10 bits")
126 #define VIDEO_TENBITS_LONGTEXT N_(\
127 "Use 10 bits per pixel for video frames.")
129 #define CFG_PREFIX "decklink-output-"
130 #define VIDEO_CFG_PREFIX "decklink-vout-"
131 #define AUDIO_CFG_PREFIX "decklink-aout-"
133 /* Video Connections */
134 static const char *const ppsz_videoconns[] = {
135 "sdi",
136 "hdmi",
137 "opticalsdi",
138 "component",
139 "composite",
140 "svideo"
142 static const char *const ppsz_videoconns_text[] = {
143 "SDI",
144 "HDMI",
145 "Optical SDI",
146 "Component",
147 "Composite",
148 "S-video",
150 static const BMDVideoConnection rgbmd_videoconns[] =
152 bmdVideoConnectionSDI,
153 bmdVideoConnectionHDMI,
154 bmdVideoConnectionOpticalSDI,
155 bmdVideoConnectionComponent,
156 bmdVideoConnectionComposite,
157 bmdVideoConnectionSVideo,
159 static_assert(ARRAY_SIZE(rgbmd_videoconns) == ARRAY_SIZE(ppsz_videoconns), "videoconn arrays messed up");
160 static_assert(ARRAY_SIZE(rgbmd_videoconns) == ARRAY_SIZE(ppsz_videoconns_text), "videoconn arrays messed up");
162 static const int rgi_afd_values[] = {
163 0, 2, 3, 4, 8, 9, 10, 11, 13, 14, 15,
165 static const char * const rgsz_afd_text[] = {
166 "0: Undefined",
167 "2: Box 16:9 (top aligned)",
168 "3: Box 14:9 (top aligned)",
169 "4: Box > 16:9 (centre aligned)",
170 "8: Same as coded frame (full frame)",
171 "9: 4:3 (centre aligned)",
172 "10: 16:9 (centre aligned)",
173 "11: 14:9 (centre aligned)",
174 "13: 4:3 (with shoot and protect 14:9 centre)",
175 "14: 16:9 (with shoot and protect 14:9 centre)",
176 "15: 16:9 (with shoot and protect 4:3 centre)",
178 static_assert(ARRAY_SIZE(rgi_afd_values) == ARRAY_SIZE(rgsz_afd_text), "afd arrays messed up");
180 static const int rgi_ar_values[] = {
181 0, 1,
183 static const char * const rgsz_ar_text[] = {
184 "0: 4:3",
185 "1: 16:9",
187 static_assert(ARRAY_SIZE(rgi_ar_values) == ARRAY_SIZE(rgsz_ar_text), "afd arrays messed up");
189 namespace {
191 /* Only one audio output module and one video output module
192 * can be used per process.
193 * We use a static mutex in audio/video submodules entry points. */
194 struct decklink_sys_t
196 /* With LOCK */
197 IDeckLinkOutput *p_output;
200 * Synchronizes aout and vout modules:
201 * vout module waits until aout has been initialized.
202 * That means video-only output is NOT supported.
204 vlc_mutex_t lock;
205 vlc_cond_t cond;
206 uint8_t users;
207 bool b_videomodule;
208 bool b_recycling;
210 //int i_channels;
211 int i_rate;
213 BMDTimeScale timescale;
214 BMDTimeValue frameduration;
216 /* XXX: workaround card clock drift */
217 vlc_tick_t offset;
219 /* !With LOCK */
221 /* single video module exclusive */
222 struct
224 bool tenbits;
225 uint8_t afd, ar;
226 int nosignal_delay;
227 picture_t *pic_nosignal;
228 } video;
231 } // namespace
233 /*****************************************************************************
234 * Local prototypes.
235 *****************************************************************************/
237 static int OpenVideo (vout_display_t *, const vout_display_cfg_t *,
238 video_format_t *, vlc_video_context *);
239 static void CloseVideo (vout_display_t *);
240 static int OpenAudio (vlc_object_t *);
241 static void CloseAudio (vlc_object_t *);
243 /*****************************************************************************
244 * Module descriptor
245 *****************************************************************************/
247 vlc_module_begin()
248 set_shortname(N_("DecklinkOutput"))
249 set_description(N_("Output module to write to Blackmagic SDI card"))
250 set_section(N_("DeckLink General Options"), NULL)
251 add_integer(CFG_PREFIX "card-index", 0,
252 CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT, true)
254 add_submodule ()
255 set_description (N_("DeckLink Video Output module"))
256 set_category(CAT_VIDEO)
257 set_subcategory(SUBCAT_VIDEO_VOUT)
258 set_callback_display(OpenVideo, 0)
259 set_section(N_("DeckLink Video Options"), NULL)
260 add_string(VIDEO_CFG_PREFIX "video-connection", "sdi",
261 VIDEO_CONNECTION_TEXT, VIDEO_CONNECTION_LONGTEXT, true)
262 change_string_list(ppsz_videoconns, ppsz_videoconns_text)
263 add_string(VIDEO_CFG_PREFIX "mode", "",
264 MODE_TEXT, MODE_LONGTEXT, true)
265 add_bool(VIDEO_CFG_PREFIX "tenbits", true,
266 VIDEO_TENBITS_TEXT, VIDEO_TENBITS_LONGTEXT, true)
267 add_integer(VIDEO_CFG_PREFIX "nosignal-delay", 5,
268 NOSIGNAL_INDEX_TEXT, NOSIGNAL_INDEX_LONGTEXT, true)
269 add_integer(VIDEO_CFG_PREFIX "afd-line", 16,
270 AFDLINE_INDEX_TEXT, AFDLINE_INDEX_LONGTEXT, true)
271 add_integer_with_range(VIDEO_CFG_PREFIX "afd", 8, 0, 16,
272 AFD_INDEX_TEXT, AFD_INDEX_TEXT, true)
273 change_integer_list(rgi_afd_values, rgsz_afd_text)
274 add_integer_with_range(VIDEO_CFG_PREFIX "ar", 1, 0, 1,
275 AR_INDEX_TEXT, AR_INDEX_LONGTEXT, true)
276 change_integer_list(rgi_ar_values, rgsz_ar_text)
277 add_loadfile(VIDEO_CFG_PREFIX "nosignal-image", NULL,
278 NOSIGNAL_IMAGE_TEXT, NOSIGNAL_IMAGE_LONGTEXT)
281 add_submodule ()
282 set_description (N_("DeckLink Audio Output module"))
283 set_category(CAT_AUDIO)
284 set_subcategory(SUBCAT_AUDIO_AOUT)
285 set_capability("audio output", 0)
286 set_callbacks (OpenAudio, CloseAudio)
287 set_section(N_("DeckLink Audio Options"), NULL)
288 add_obsolete_string("audio-connection")
289 add_integer(AUDIO_CFG_PREFIX "audio-rate", 48000,
290 RATE_TEXT, RATE_LONGTEXT, true)
291 add_integer(AUDIO_CFG_PREFIX "audio-channels", 2,
292 CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
293 vlc_module_end ()
295 /* Protects decklink_sys_t creation/deletion */
296 static vlc::threads::mutex sys_lock;
298 static decklink_sys_t *HoldDLSys(vlc_object_t *obj, int i_cat)
300 vlc_object_t *libvlc = VLC_OBJECT(vlc_object_instance(obj));
301 decklink_sys_t *sys;
303 sys_lock.lock();
305 if (var_Type(libvlc, "decklink-sys") == VLC_VAR_ADDRESS)
307 sys = (decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
308 sys->users++;
310 if(i_cat == VIDEO_ES)
312 while(sys->b_videomodule)
314 sys_lock.unlock();
315 msg_Info(obj, "Waiting for previous vout module to exit");
316 vlc_tick_sleep(VLC_TICK_FROM_MS(100));
317 sys_lock.lock();
321 else
323 sys = (decklink_sys_t*)malloc(sizeof(*sys));
324 if (sys) {
325 sys->p_output = NULL;
326 sys->offset = 0;
327 sys->users = 1;
328 sys->b_videomodule = (i_cat == VIDEO_ES);
329 sys->b_recycling = false;
330 sys->i_rate = var_InheritInteger(obj, AUDIO_CFG_PREFIX "audio-rate");
331 if(sys->i_rate > 0)
332 sys->i_rate = -1;
333 vlc_mutex_init(&sys->lock);
334 vlc_cond_init(&sys->cond);
335 var_Create(libvlc, "decklink-sys", VLC_VAR_ADDRESS);
336 var_SetAddress(libvlc, "decklink-sys", (void*)sys);
340 sys_lock.unlock();
341 return sys;
344 static void ReleaseDLSys(vlc_object_t *obj, int i_cat)
346 vlc_object_t *libvlc = VLC_OBJECT(vlc_object_instance(obj));
348 sys_lock.lock();
350 struct decklink_sys_t *sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
352 if (--sys->users == 0) {
353 msg_Dbg(obj, "Destroying decklink data");
355 if (sys->p_output) {
356 sys->p_output->StopScheduledPlayback(0, NULL, 0);
357 sys->p_output->DisableVideoOutput();
358 sys->p_output->DisableAudioOutput();
359 sys->p_output->Release();
362 /* Clean video specific */
363 if (sys->video.pic_nosignal)
364 picture_Release(sys->video.pic_nosignal);
366 free(sys);
367 var_Destroy(libvlc, "decklink-sys");
369 else if (i_cat == VIDEO_ES)
371 sys->b_videomodule = false;
372 sys->b_recycling = true;
375 sys_lock.unlock();
378 static BMDVideoConnection getVConn(vout_display_t *vd, BMDVideoConnection mask)
380 BMDVideoConnection conn = 0;
381 char *psz = var_InheritString(vd, VIDEO_CFG_PREFIX "video-connection");
382 if (psz)
384 for(size_t i=0; i<ARRAY_SIZE(rgbmd_videoconns); i++)
386 if (!strcmp(psz, ppsz_videoconns[i]) && (mask & rgbmd_videoconns[i]))
388 conn = rgbmd_videoconns[i];
389 break;
392 free(psz);
394 else /* Pick one as default connection */
396 conn = vlc_ctz(mask);
397 conn = conn ? ( 1 << conn ) : bmdVideoConnectionSDI;
399 return conn;
402 /*****************************************************************************
404 *****************************************************************************/
405 static int OpenDecklink(vout_display_t *vd, decklink_sys_t *sys, video_format_t *fmt)
407 #define CHECK(message) do { \
408 if (result != S_OK) \
410 const char *psz_err = Decklink::Helper::ErrorToString(result); \
411 if(psz_err)\
412 msg_Err(vd, message ": %s", psz_err); \
413 else \
414 msg_Err(vd, message ": 0x%X", result); \
415 goto error; \
417 } while(0)
419 HRESULT result;
420 IDeckLinkIterator *decklink_iterator = NULL;
421 IDeckLinkDisplayMode *p_display_mode = NULL;
422 IDeckLinkConfiguration *p_config = NULL;
423 IDeckLinkProfileAttributes *p_attributes = NULL;
424 IDeckLink *p_card = NULL;
425 BMDDisplayMode wanted_mode_id = bmdModeUnknown;
427 vlc_mutex_lock(&sys->lock);
429 /* wait until aout is ready */
430 msg_Info(vd, "Waiting for DeckLink audio input module to start");
431 while (sys->i_rate == -1)
432 vlc_cond_wait(&sys->cond, &sys->lock);
434 int i_card_index = var_InheritInteger(vd, CFG_PREFIX "card-index");
435 char *mode = var_InheritString(vd, VIDEO_CFG_PREFIX "mode");
437 if(mode)
439 size_t len = strlen(mode);
440 if (len > 4)
442 free(mode);
443 msg_Err(vd, "Invalid mode %s", mode);
444 goto error;
446 memset(&wanted_mode_id, ' ', 4);
447 strncpy((char*)&wanted_mode_id, mode, 4);
448 wanted_mode_id = ntohl(wanted_mode_id);
449 free(mode);
452 if (i_card_index < 0)
454 msg_Err(vd, "Invalid card index %d", i_card_index);
455 goto error;
458 decklink_iterator = CreateDeckLinkIteratorInstance();
459 if (!decklink_iterator)
461 msg_Err(vd, "DeckLink drivers not found.");
462 goto error;
465 for(int i = 0; i <= i_card_index; ++i)
467 if (p_card)
468 p_card->Release();
469 result = decklink_iterator->Next(&p_card);
470 CHECK("Card not found");
473 decklink_str_t tmp_name;
474 char *psz_model_name;
475 result = p_card->GetModelName(&tmp_name);
476 CHECK("Unknown model name");
477 psz_model_name = DECKLINK_STRDUP(tmp_name);
478 DECKLINK_FREE(tmp_name);
480 msg_Dbg(vd, "Opened DeckLink PCI card %s", psz_model_name);
481 free(psz_model_name);
483 /* Read attributes */
485 result = p_card->QueryInterface(IID_IDeckLinkProfileAttributes, (void**)&p_attributes);
486 CHECK("Could not get IDeckLinkAttributes");
488 int64_t vconn;
489 result = p_attributes->GetInt(BMDDeckLinkVideoOutputConnections, &vconn); /* reads mask */
490 CHECK("Could not get BMDDeckLinkVideoOutputConnections");
492 result = p_card->QueryInterface(IID_IDeckLinkOutput,
493 (void**)&sys->p_output);
494 CHECK("No outputs");
496 result = p_card->QueryInterface(IID_IDeckLinkConfiguration,
497 (void**)&p_config);
498 CHECK("Could not get config interface");
500 /* Now configure card */
502 vconn = getVConn(vd, (BMDVideoConnection) vconn);
503 if (vconn == 0)
505 msg_Err(vd, "Invalid video connection specified");
506 goto error;
509 result = p_config->SetInt(bmdDeckLinkConfigVideoOutputConnection, (BMDVideoConnection) vconn);
510 CHECK("Could not set video output connection");
512 p_display_mode = Decklink::Helper::MatchDisplayMode(VLC_OBJECT(vd), sys->p_output,
513 vd->source, wanted_mode_id);
514 if(p_display_mode == NULL)
516 msg_Err(vd, "Could not negociate a compatible display mode");
517 goto error;
519 else
521 BMDDisplayMode mode_id = p_display_mode->GetDisplayMode();
522 BMDDisplayMode modenl = htonl(mode_id);
523 msg_Dbg(vd, "Selected mode '%4.4s'", (char *) &modenl);
525 BMDPixelFormat pixelFormat = sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV;
526 BMDVideoOutputFlags flags = bmdVideoOutputVANC;
527 if (mode_id == bmdModeNTSC ||
528 mode_id == bmdModeNTSC2398 ||
529 mode_id == bmdModePAL)
531 flags = bmdVideoOutputVITC;
533 bool supported;
534 #if BLACKMAGIC_DECKLINK_API_VERSION < 0x0b010000
535 BMDDisplayModeSupport support = bmdDisplayModeNotSupported;
536 result = sys->p_output->DoesSupportVideoMode(mode_id,
537 pixelFormat,
538 flags,
539 &support,
540 NULL);
541 supported = (support != bmdDisplayModeNotSupported);
542 #else
543 result = sys->p_output->DoesSupportVideoMode(vconn,
544 mode_id,
545 pixelFormat,
546 bmdSupportedVideoModeDefault,
547 NULL,
548 &supported);
549 #endif
550 CHECK("Does not support video mode");
551 if (!supported)
553 msg_Err(vd, "Video mode not supported");
554 goto error;
557 if (p_display_mode->GetWidth() <= 0 || p_display_mode->GetWidth() & 1)
559 msg_Err(vd, "Unknown video mode specified.");
560 goto error;
563 result = p_display_mode->GetFrameRate(&sys->frameduration,
564 &sys->timescale);
565 CHECK("Could not read frame rate");
567 result = sys->p_output->EnableVideoOutput(mode_id, flags);
568 CHECK("Could not enable video output");
570 video_format_Copy(fmt, vd->source);
571 fmt->i_width = fmt->i_visible_width = p_display_mode->GetWidth();
572 fmt->i_height = fmt->i_visible_height = p_display_mode->GetHeight();
573 fmt->i_x_offset = 0;
574 fmt->i_y_offset = 0;
575 fmt->i_sar_num = 0;
576 fmt->i_sar_den = 0;
577 fmt->i_chroma = !sys->video.tenbits ? VLC_CODEC_UYVY : VLC_CODEC_I422_10L; /* we will convert to v210 */
578 fmt->i_frame_rate = (unsigned) sys->frameduration;
579 fmt->i_frame_rate_base = (unsigned) sys->timescale;
582 if (/*decklink_sys->i_channels > 0 &&*/ sys->i_rate > 0)
584 result = sys->p_output->EnableAudioOutput(
585 sys->i_rate,
586 bmdAudioSampleType16bitInteger,
587 /*decklink_sys->i_channels*/ 2,
588 bmdAudioOutputStreamTimestamped);
589 CHECK("Could not start audio output");
592 /* start */
593 result = sys->p_output->StartScheduledPlayback(
594 samples_from_vlc_tick(vlc_tick_now(), sys->timescale), sys->timescale, 1.0);
595 CHECK("Could not start playback");
597 p_config->Release();
598 p_display_mode->Release();
599 p_card->Release();
600 p_attributes->Release();
601 decklink_iterator->Release();
603 vlc_mutex_unlock(&sys->lock);
605 return VLC_SUCCESS;
607 error:
608 if (sys->p_output) {
609 sys->p_output->Release();
610 sys->p_output = NULL;
612 if (p_card)
613 p_card->Release();
614 if (p_config)
615 p_config->Release();
616 if (p_attributes)
617 p_attributes->Release();
618 if (decklink_iterator)
619 decklink_iterator->Release();
620 if (p_display_mode)
621 p_display_mode->Release();
622 video_format_Clean(fmt);
624 vlc_mutex_unlock(&sys->lock);
626 return VLC_EGENERIC;
627 #undef CHECK
630 /*****************************************************************************
631 * Video
632 *****************************************************************************/
633 static void PrepareVideo(vout_display_t *vd, picture_t *picture, subpicture_t *,
634 vlc_tick_t date)
636 decklink_sys_t *sys = (decklink_sys_t *) vd->sys;
637 vlc_tick_t now = vlc_tick_now();
639 if (!picture)
640 return;
642 if (now - date > vlc_tick_from_sec( sys->video.nosignal_delay )) {
643 msg_Dbg(vd, "no signal");
644 if (sys->video.pic_nosignal) {
645 picture = sys->video.pic_nosignal;
646 } else {
647 if (sys->video.tenbits) { // I422_10L
648 plane_t *y = &picture->p[0];
649 memset(y->p_pixels, 0x0, y->i_lines * y->i_pitch);
650 for (int i = 1; i < picture->i_planes; i++) {
651 plane_t *p = &picture->p[i];
652 size_t len = p->i_lines * p->i_pitch / 2;
653 int16_t *data = (int16_t*)p->p_pixels;
654 for (size_t j = 0; j < len; j++) // XXX: SIMD
655 data[j] = 0x200;
657 } else { // UYVY
658 size_t len = picture->p[0].i_lines * picture->p[0].i_pitch;
659 for (size_t i = 0; i < len; i+= 2) { // XXX: SIMD
660 picture->p[0].p_pixels[i+0] = 0x80;
661 picture->p[0].p_pixels[i+1] = 0;
665 date = now;
668 HRESULT result;
669 int w, h, stride, length;
670 w = vd->fmt->i_width;
671 h = vd->fmt->i_height;
673 IDeckLinkMutableVideoFrame *pDLVideoFrame;
674 result = sys->p_output->CreateVideoFrame(w, h, w*3,
675 sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
676 bmdFrameFlagDefault, &pDLVideoFrame);
678 if (result != S_OK) {
679 msg_Err(vd, "Failed to create video frame: 0x%X", result);
680 pDLVideoFrame = NULL;
681 goto end;
684 void *frame_bytes;
685 pDLVideoFrame->GetBytes((void**)&frame_bytes);
686 stride = pDLVideoFrame->GetRowBytes();
688 if (sys->video.tenbits) {
689 IDeckLinkVideoFrameAncillary *vanc;
690 int line;
691 void *buf;
693 result = sys->p_output->CreateAncillaryData(
694 sys->video.tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV, &vanc);
695 if (result != S_OK) {
696 msg_Err(vd, "Failed to create vanc: %d", result);
697 goto end;
700 line = var_InheritInteger(vd, VIDEO_CFG_PREFIX "afd-line");
701 result = vanc->GetBufferForVerticalBlankingLine(line, &buf);
702 if (result != S_OK) {
703 msg_Err(vd, "Failed to get VBI line %d: %d", line, result);
704 goto end;
707 sdi::AFD afd(sys->video.afd, sys->video.ar);
708 afd.FillBuffer(reinterpret_cast<uint8_t*>(buf), stride);
710 sdi::V210::Convert(picture, stride, frame_bytes);
712 result = pDLVideoFrame->SetAncillaryData(vanc);
713 vanc->Release();
714 if (result != S_OK) {
715 msg_Err(vd, "Failed to set vanc: %d", result);
716 goto end;
719 else for(int y = 0; y < h; ++y) {
720 uint8_t *dst = (uint8_t *)frame_bytes + stride * y;
721 const uint8_t *src = (const uint8_t *)picture->p[0].p_pixels +
722 picture->p[0].i_pitch * y;
723 memcpy(dst, src, w * 2 /* bpp */);
727 // compute frame duration in CLOCK_FREQ units
728 length = (sys->frameduration * CLOCK_FREQ) / sys->timescale;
730 date -= sys->offset;
731 result = sys->p_output->ScheduleVideoFrame(pDLVideoFrame,
732 date, length, CLOCK_FREQ);
734 if (result != S_OK) {
735 msg_Err(vd, "Dropped Video frame %" PRId64 ": 0x%x", date, result);
736 goto end;
739 now = vlc_tick_now() - sys->offset;
741 BMDTimeValue decklink_now;
742 double speed;
743 sys->p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);
745 if ((now - decklink_now) > 400000) {
746 /* XXX: workaround card clock drift */
747 sys->offset += 50000;
748 msg_Err(vd, "Delaying: offset now %" PRId64, sys->offset);
751 end:
752 if (pDLVideoFrame)
753 pDLVideoFrame->Release();
756 static int ControlVideo(vout_display_t *vd, int query)
758 (void) vd;
760 switch (query) {
761 case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
762 case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
763 case VOUT_DISPLAY_CHANGE_ZOOM:
764 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
765 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
766 return VLC_SUCCESS;
768 return VLC_EGENERIC;
771 static const struct vlc_display_operations ops = {
772 CloseVideo, PrepareVideo, NULL, ControlVideo, NULL, NULL,
775 static int OpenVideo(vout_display_t *vd, const vout_display_cfg_t *cfg,
776 video_format_t *fmtp, vlc_video_context *context)
778 VLC_UNUSED(cfg); VLC_UNUSED(context);
779 decklink_sys_t *sys = HoldDLSys(VLC_OBJECT(vd), VIDEO_ES);
780 if(!sys)
781 return VLC_ENOMEM;
783 bool b_init;
784 vlc_mutex_lock(&sys->lock);
785 b_init = !sys->b_recycling;
786 vlc_mutex_unlock(&sys->lock);
788 if( b_init )
790 sys->video.tenbits = var_InheritBool(vd, VIDEO_CFG_PREFIX "tenbits");
791 sys->video.nosignal_delay = var_InheritInteger(vd, VIDEO_CFG_PREFIX "nosignal-delay");
792 sys->video.afd = var_InheritInteger(vd, VIDEO_CFG_PREFIX "afd");
793 sys->video.ar = var_InheritInteger(vd, VIDEO_CFG_PREFIX "ar");
794 sys->video.pic_nosignal = NULL;
796 if (OpenDecklink(vd, sys, fmtp) != VLC_SUCCESS)
798 CloseVideo(vd);
799 return VLC_EGENERIC;
802 char *pic_file = var_InheritString(vd, VIDEO_CFG_PREFIX "nosignal-image");
803 if (pic_file)
805 sys->video.pic_nosignal = sdi::Generator::Picture(VLC_OBJECT(vd), pic_file, fmtp);
806 if (!sys->video.pic_nosignal)
807 msg_Err(vd, "Could not create no signal picture");
808 free(pic_file);
812 vd->ops = &ops;
814 vd->sys = (vout_display_sys_t*) sys;
816 return VLC_SUCCESS;
819 static void CloseVideo(vout_display_t *vd)
821 ReleaseDLSys(VLC_OBJECT(vd), VIDEO_ES);
824 /*****************************************************************************
825 * Audio
826 *****************************************************************************/
828 static void Flush(audio_output_t *aout)
830 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
831 vlc_mutex_lock(&sys->lock);
832 IDeckLinkOutput *p_output = sys->p_output;
833 vlc_mutex_unlock(&sys->lock);
834 if (!p_output)
835 return;
837 if (sys->p_output->FlushBufferedAudioSamples() == E_FAIL)
838 msg_Err(aout, "Flush failed");
841 static void Drain(audio_output_t *aout)
843 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
844 vlc_mutex_lock(&sys->lock);
845 IDeckLinkOutput *p_output = sys->p_output;
846 vlc_mutex_unlock(&sys->lock);
847 if (!p_output)
848 return;
850 uint32_t samples;
851 sys->p_output->GetBufferedAudioSampleFrameCount(&samples);
852 vlc_tick_sleep(vlc_tick_from_samples(samples, sys->i_rate));
856 static int TimeGet(audio_output_t *, vlc_tick_t* restrict)
858 /* synchronization is handled by the card */
859 return -1;
862 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
864 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
866 if (sys->i_rate == 0)
867 return VLC_EGENERIC;
869 fmt->i_format = VLC_CODEC_S16N;
870 fmt->i_channels = 2; //decklink_sys->i_channels;
871 fmt->i_physical_channels = AOUT_CHANS_STEREO; //pi_channels_maps[fmt->i_channels];
872 fmt->channel_type = AUDIO_CHANNEL_TYPE_BITMAP;
873 fmt->i_rate = sys->i_rate;
874 fmt->i_bitspersample = 16;
875 fmt->i_blockalign = fmt->i_channels * fmt->i_bitspersample /8 ;
876 fmt->i_frame_length = FRAME_SIZE;
878 return VLC_SUCCESS;
881 static void PlayAudio(audio_output_t *aout, block_t *audio, vlc_tick_t systempts)
883 decklink_sys_t *sys = (decklink_sys_t *) aout->sys;
884 vlc_mutex_lock(&sys->lock);
885 IDeckLinkOutput *p_output = sys->p_output;
886 audio->i_pts -= sys->offset;
887 vlc_mutex_unlock(&sys->lock);
888 if (!p_output) {
889 block_Release(audio);
890 return;
893 uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
894 uint32_t written;
895 HRESULT result = p_output->ScheduleAudioSamples(
896 audio->p_buffer, sampleFrameCount, systempts, CLOCK_FREQ, &written);
898 if (result != S_OK)
899 msg_Err(aout, "Failed to schedule audio sample: 0x%X", result);
900 else if (sampleFrameCount != written)
901 msg_Err(aout, "Written only %d samples out of %d", written, sampleFrameCount);
903 block_Release(audio);
906 static int OpenAudio(vlc_object_t *p_this)
908 audio_output_t *aout = (audio_output_t *)p_this;
909 decklink_sys_t *sys = HoldDLSys(p_this, AUDIO_ES);
910 if(!sys)
911 return VLC_ENOMEM;
913 aout->sys = sys;
915 vlc_mutex_lock(&sys->lock);
916 //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
917 sys->i_rate = var_InheritInteger(aout, AUDIO_CFG_PREFIX "audio-rate");
918 vlc_cond_signal(&sys->cond);
919 vlc_mutex_unlock(&sys->lock);
921 aout->play = PlayAudio;
922 aout->start = Start;
923 aout->flush = Flush;
924 aout->drain = Drain;
925 aout->time_get = TimeGet;
927 aout->pause = aout_PauseDefault;
928 aout->stop = NULL;
929 aout->mute_set = NULL;
930 aout->volume_set= NULL;
932 return VLC_SUCCESS;
935 static void CloseAudio(vlc_object_t *p_this)
937 decklink_sys_t *sys = (decklink_sys_t *) ((audio_output_t *)p_this)->sys;
938 vlc_mutex_lock(&sys->lock);
939 vlc_mutex_unlock(&sys->lock);
940 ReleaseDLSys(p_this, AUDIO_ES);