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 #define __STDC_FORMAT_MACROS
30 #define __STDC_CONSTANT_MACROS
38 #include <vlc_common.h>
39 #include <vlc_plugin.h>
40 #include <vlc_threads.h>
42 #include <vlc_vout_display.h>
43 #include <vlc_picture_pool.h>
45 #include <vlc_block.h>
46 #include <vlc_image.h>
47 #include <vlc_atomic.h>
49 #include <arpa/inet.h>
51 #include <DeckLinkAPI.h>
52 #include <DeckLinkAPIDispatch.cpp>
54 #define FRAME_SIZE 1920
55 #define CHANNELS_MAX 6
58 static const int pi_channels_maps[CHANNELS_MAX+1] =
70 #define NOSIGNAL_INDEX_TEXT N_("Timelength after which we assume there is no signal.")
71 #define NOSIGNAL_INDEX_LONGTEXT N_(\
72 "Timelength after which we assume there is no signal.\n"\
73 "After this delay we black out the video."\
76 #define NOSIGNAL_IMAGE_TEXT N_("Picture to display on input signal loss.")
77 #define NOSIGNAL_IMAGE_LONGTEXT NOSIGNAL_IMAGE_TEXT
79 #define CARD_INDEX_TEXT N_("Output card")
80 #define CARD_INDEX_LONGTEXT N_(\
81 "DeckLink output card, if multiple exist. " \
82 "The cards are numbered from 0.")
84 #define MODE_TEXT N_("Desired output mode")
85 #define MODE_LONGTEXT N_(\
86 "Desired output mode for DeckLink output. " \
87 "This value should be a FOURCC code in textual " \
88 "form, e.g. \"ntsc\".")
90 #define AUDIO_CONNECTION_TEXT N_("Audio connection")
91 #define AUDIO_CONNECTION_LONGTEXT N_(\
92 "Audio connection for DeckLink output.")
95 #define RATE_TEXT N_("Audio samplerate (Hz)")
96 #define RATE_LONGTEXT N_(\
97 "Audio sampling rate (in hertz) for DeckLink output. " \
98 "0 disables audio output.")
100 #define CHANNELS_TEXT N_("Number of audio channels")
101 #define CHANNELS_LONGTEXT N_(\
102 "Number of output channels for DeckLink output. " \
103 "Must be 2, 8 or 16. 0 disables audio output.")
105 #define VIDEO_CONNECTION_TEXT N_("Video connection")
106 #define VIDEO_CONNECTION_LONGTEXT N_(\
107 "Video connection for DeckLink output.")
109 #define VIDEO_TENBITS_TEXT N_("10 bits")
110 #define VIDEO_TENBITS_LONGTEXT N_(\
111 "Use 10 bits per pixel for video frames.")
113 #define CFG_PREFIX "decklink-output-"
114 #define VIDEO_CFG_PREFIX "decklink-vout-"
115 #define AUDIO_CFG_PREFIX "decklink-aout-"
119 static const char *const ppsz_videoconns[] = {
120 "sdi", "hdmi", "opticalsdi", "component", "composite", "svideo"
122 static const char *const ppsz_videoconns_text[] = {
123 N_("SDI"), N_("HDMI"), N_("Optical SDI"), N_("Component"), N_("Composite"), N_("S-video")
126 struct vout_display_sys_t
128 picture_pool_t *pool;
131 picture_t *pic_nosignal;
134 /* Only one audio output module and one video output module
135 * can be used per process.
136 * We use a static mutex in audio/video submodules entry points. */
137 struct decklink_sys_t
139 IDeckLinkOutput *p_output;
142 * Synchronizes aout and vout modules:
143 * vout module waits until aout has been initialized.
144 * That means video-only output is NOT supported.
156 BMDTimeScale timescale;
157 BMDTimeValue frameduration;
159 /* XXX: workaround card clock drift */
163 /*****************************************************************************
165 *****************************************************************************/
167 static int OpenVideo (vlc_object_t *);
168 static void CloseVideo (vlc_object_t *);
169 static int OpenAudio (vlc_object_t *);
170 static void CloseAudio (vlc_object_t *);
172 /*****************************************************************************
174 *****************************************************************************/
177 set_shortname(N_("DecklinkOutput"))
178 set_description(N_("output module to write to Blackmagic SDI card"))
179 set_section(N_("Decklink General Options"), NULL)
180 add_integer(CFG_PREFIX "card-index", 0,
181 CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT, true)
184 set_description (N_("Decklink Video Output module"))
185 set_category(CAT_VIDEO)
186 set_subcategory(SUBCAT_VIDEO_VOUT)
187 set_capability("vout display", 0)
188 set_callbacks (OpenVideo, CloseVideo)
189 set_section(N_("Decklink Video Options"), NULL)
190 add_string(VIDEO_CFG_PREFIX "video-connection", "sdi",
191 VIDEO_CONNECTION_TEXT, VIDEO_CONNECTION_LONGTEXT, true)
192 change_string_list(ppsz_videoconns, ppsz_videoconns_text)
193 add_string(VIDEO_CFG_PREFIX "mode", "pal ",
194 MODE_TEXT, MODE_LONGTEXT, true)
195 add_bool(VIDEO_CFG_PREFIX "tenbits", false,
196 VIDEO_TENBITS_TEXT, VIDEO_TENBITS_LONGTEXT, true)
197 add_integer(VIDEO_CFG_PREFIX "nosignal-delay", 5,
198 NOSIGNAL_INDEX_TEXT, NOSIGNAL_INDEX_LONGTEXT, true)
199 add_loadfile(VIDEO_CFG_PREFIX "nosignal-image", NULL,
200 NOSIGNAL_IMAGE_TEXT, NOSIGNAL_IMAGE_LONGTEXT, true)
204 set_description (N_("Decklink Audio Output module"))
205 set_category(CAT_AUDIO)
206 set_subcategory(SUBCAT_AUDIO_AOUT)
207 set_capability("audio output", 0)
208 set_callbacks (OpenAudio, CloseAudio)
209 set_section(N_("Decklink Audio Options"), NULL)
210 add_obsolete_string("audio-connection")
211 add_integer(AUDIO_CFG_PREFIX "audio-rate", 48000,
212 RATE_TEXT, RATE_LONGTEXT, true)
213 add_integer(AUDIO_CFG_PREFIX "audio-channels", 2,
214 CHANNELS_TEXT, CHANNELS_LONGTEXT, true)
217 /* Protects decklink_sys_t creation/deletion */
218 static vlc_mutex_t sys_lock = VLC_STATIC_MUTEX;
220 static struct decklink_sys_t *GetDLSys(vlc_object_t *obj)
222 vlc_object_t *libvlc = VLC_OBJECT(obj->p_libvlc);
223 struct decklink_sys_t *sys;
225 vlc_mutex_lock(&sys_lock);
227 if (var_Type(libvlc, "decklink-sys") == VLC_VAR_ADDRESS)
228 sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
230 sys = (struct decklink_sys_t*)malloc(sizeof(*sys));
232 sys->p_output = NULL;
236 vlc_mutex_init(&sys->lock);
237 vlc_cond_init(&sys->cond);
238 var_Create(libvlc, "decklink-sys", VLC_VAR_ADDRESS);
239 var_SetAddress(libvlc, "decklink-sys", (void*)sys);
243 vlc_mutex_unlock(&sys_lock);
247 static void ReleaseDLSys(vlc_object_t *obj)
249 vlc_object_t *libvlc = VLC_OBJECT(obj->p_libvlc);
251 vlc_mutex_lock(&sys_lock);
253 struct decklink_sys_t *sys = (struct decklink_sys_t*)var_GetAddress(libvlc, "decklink-sys");
255 if (--sys->users == 0) {
256 msg_Dbg(obj, "Destroying decklink data");
257 vlc_mutex_destroy(&sys->lock);
258 vlc_cond_destroy(&sys->cond);
261 sys->p_output->StopScheduledPlayback(0, NULL, 0);
262 sys->p_output->DisableVideoOutput();
263 sys->p_output->DisableAudioOutput();
264 sys->p_output->Release();
268 var_Destroy(libvlc, "decklink-sys");
271 vlc_mutex_unlock(&sys_lock);
274 static BMDVideoConnection getVConn(vout_display_t *vd)
276 BMDVideoConnection conn = bmdVideoConnectionSDI;
277 char *psz = var_InheritString(vd, VIDEO_CFG_PREFIX "video-connection");
281 if (!strcmp(psz, "sdi"))
282 conn = bmdVideoConnectionSDI;
283 else if (!strcmp(psz, "hdmi"))
284 conn = bmdVideoConnectionHDMI;
285 else if (!strcmp(psz, "opticalsdi"))
286 conn = bmdVideoConnectionOpticalSDI;
287 else if (!strcmp(psz, "component"))
288 conn = bmdVideoConnectionComponent;
289 else if (!strcmp(psz, "composite"))
290 conn = bmdVideoConnectionComposite;
291 else if (!strcmp(psz, "svideo"))
292 conn = bmdVideoConnectionSVideo;
299 /*****************************************************************************
301 *****************************************************************************/
303 static struct decklink_sys_t *OpenDecklink(vout_display_t *vd)
305 vout_display_sys_t *sys = vd->sys;
306 #define CHECK(message) do { \
307 if (result != S_OK) \
309 msg_Err(vd, message ": 0x%X", result); \
315 IDeckLinkIterator *decklink_iterator = NULL;
316 IDeckLinkDisplayMode *p_display_mode = NULL;
317 IDeckLinkDisplayModeIterator *p_display_iterator = NULL;
318 IDeckLinkConfiguration *p_config = NULL;
319 IDeckLink *p_card = NULL;
321 struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(vd));
322 vlc_mutex_lock(&decklink_sys->lock);
323 decklink_sys->users++;
325 /* wait until aout is ready */
326 while (decklink_sys->i_rate == -1)
327 vlc_cond_wait(&decklink_sys->cond, &decklink_sys->lock);
329 int i_card_index = var_InheritInteger(vd, CFG_PREFIX "card-index");
330 BMDVideoConnection vconn = getVConn(vd);
331 char *mode = var_InheritString(vd, VIDEO_CFG_PREFIX "mode");
332 size_t len = mode ? strlen(mode) : 0;
333 if (!mode || len > 4)
336 msg_Err(vd, "Missing or invalid mode");
340 BMDDisplayMode wanted_mode_id;
341 memset(&wanted_mode_id, ' ', 4);
342 strncpy((char*)&wanted_mode_id, mode, 4);
345 if (i_card_index < 0)
347 msg_Err(vd, "Invalid card index %d", i_card_index);
351 decklink_iterator = CreateDeckLinkIteratorInstance();
352 if (!decklink_iterator)
354 msg_Err(vd, "DeckLink drivers not found.");
358 for(int i = 0; i <= i_card_index; ++i)
362 result = decklink_iterator->Next(&p_card);
363 CHECK("Card not found");
366 const char *psz_model_name;
367 result = p_card->GetModelName(&psz_model_name);
368 CHECK("Unknown model name");
370 msg_Dbg(vd, "Opened DeckLink PCI card %s", psz_model_name);
372 result = p_card->QueryInterface(IID_IDeckLinkOutput,
373 (void**)&decklink_sys->p_output);
376 result = p_card->QueryInterface(IID_IDeckLinkConfiguration,
378 CHECK("Could not get config interface");
382 result = p_config->SetInt(
383 bmdDeckLinkConfigVideoOutputConnection, vconn);
384 CHECK("Could not set video output connection");
387 result = decklink_sys->p_output->GetDisplayModeIterator(&p_display_iterator);
388 CHECK("Could not enumerate display modes");
390 for (; ; p_display_mode->Release())
393 result = p_display_iterator->Next(&p_display_mode);
397 BMDDisplayMode mode_id = ntohl(p_display_mode->GetDisplayMode());
399 const char *psz_mode_name;
400 result = p_display_mode->GetName(&psz_mode_name);
401 CHECK("Could not get display mode name");
403 result = p_display_mode->GetFrameRate(&decklink_sys->frameduration,
404 &decklink_sys->timescale);
405 CHECK("Could not get frame rate");
407 w = p_display_mode->GetWidth();
408 h = p_display_mode->GetHeight();
409 msg_Dbg(vd, "Found mode '%4.4s': %s (%dx%d, %.3f fps)",
410 (char*)&mode_id, psz_mode_name, w, h,
411 double(decklink_sys->timescale) / decklink_sys->frameduration);
412 msg_Dbg(vd, "scale %d dur %d", (int)decklink_sys->timescale,
413 (int)decklink_sys->frameduration);
415 if (wanted_mode_id != mode_id)
418 decklink_sys->i_width = w;
419 decklink_sys->i_height = h;
421 mode_id = htonl(mode_id);
423 BMDVideoOutputFlags flags = bmdVideoOutputVANC;
424 if (mode_id == bmdModeNTSC ||
425 mode_id == bmdModeNTSC2398 ||
426 mode_id == bmdModePAL)
428 flags = bmdVideoOutputVITC;
431 BMDDisplayModeSupport support;
432 IDeckLinkDisplayMode *resultMode;
434 result = decklink_sys->p_output->DoesSupportVideoMode(mode_id,
435 sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
436 flags, &support, &resultMode);
437 CHECK("Does not support video mode");
438 if (support == bmdDisplayModeNotSupported)
440 msg_Err(vd, "Video mode not supported");
444 result = decklink_sys->p_output->EnableVideoOutput(mode_id, flags);
445 CHECK("Could not enable video output");
450 if (decklink_sys->i_width < 0 || decklink_sys->i_width & 1)
452 msg_Err(vd, "Unknown video mode specified.");
456 if (/*decklink_sys->i_channels > 0 &&*/ decklink_sys->i_rate > 0)
458 result = decklink_sys->p_output->EnableAudioOutput(
459 decklink_sys->i_rate,
460 bmdAudioSampleType16bitInteger,
461 /*decklink_sys->i_channels*/ 2,
462 bmdAudioOutputStreamTimestamped);
464 CHECK("Could not start audio output");
467 result = decklink_sys->p_output->StartScheduledPlayback(
468 (mdate() * decklink_sys->timescale) / CLOCK_FREQ, decklink_sys->timescale, 1.0);
469 CHECK("Could not start playback");
472 p_display_mode->Release();
473 p_display_iterator->Release();
475 decklink_iterator->Release();
477 vlc_mutex_unlock(&decklink_sys->lock);
482 if (decklink_sys->p_output) {
483 decklink_sys->p_output->Release();
484 decklink_sys->p_output = NULL;
490 if (p_display_iterator)
491 p_display_iterator->Release();
492 if (decklink_iterator)
493 decklink_iterator->Release();
495 p_display_mode->Release();
497 vlc_mutex_unlock(&decklink_sys->lock);
498 ReleaseDLSys(VLC_OBJECT(vd));
504 /*****************************************************************************
506 *****************************************************************************/
508 static picture_pool_t *PoolVideo(vout_display_t *vd, unsigned requested_count)
510 vout_display_sys_t *sys = vd->sys;
512 sys->pool = picture_pool_NewFromFormat(&vd->fmt, requested_count);
516 static inline void put_le32(uint8_t **p, uint32_t d)
522 static inline int clip(int a)
525 else if (a > 1019) return 1019;
529 static void v210_convert(void *frame_bytes, picture_t *pic, int dst_stride)
531 int width = pic->format.i_width;
532 int height = pic->format.i_height;
533 int line_padding = dst_stride - ((width * 8 + 11) / 12) * 4;
535 uint8_t *data = (uint8_t*)frame_bytes;
537 const uint16_t *y = (const uint16_t*)pic->p[0].p_pixels;
538 const uint16_t *u = (const uint16_t*)pic->p[1].p_pixels;
539 const uint16_t *v = (const uint16_t*)pic->p[2].p_pixels;
541 #define WRITE_PIXELS(a, b, c) \
544 val |= (clip(*b++) << 10) | \
545 (clip(*c++) << 20); \
546 put_le32(&data, val); \
549 for (h = 0; h < height; h++) {
551 for (w = 0; w < width - 5; w += 6) {
552 WRITE_PIXELS(u, y, v);
553 WRITE_PIXELS(y, u, y);
554 WRITE_PIXELS(v, y, u);
555 WRITE_PIXELS(y, v, y);
558 WRITE_PIXELS(u, y, v);
562 put_le32(&data, val);
566 val |= (clip(*u++) << 10) | (clip(*y++) << 20);
567 put_le32(&data, val);
569 val = clip(*v++) | (clip(*y++) << 10);
570 put_le32(&data, val);
573 memset(data, 0, line_padding);
574 data += line_padding;
576 y += pic->p[0].i_pitch / 2 - width;
577 u += pic->p[1].i_pitch / 2 - width / 2;
578 v += pic->p[2].i_pitch / 2 - width / 2;
582 static void DisplayVideo(vout_display_t *vd, picture_t *picture, subpicture_t *)
584 vout_display_sys_t *sys = vd->sys;
585 struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(vd));
586 mtime_t now = mdate();
591 picture_t *orig_picture = picture;
593 if (now - picture->date > sys->nosignal_delay * CLOCK_FREQ) {
594 msg_Dbg(vd, "no signal");
595 if (sys->pic_nosignal) {
596 picture = sys->pic_nosignal;
598 if (sys->tenbits) { // I422_10L
599 plane_t *y = &picture->p[0];
600 memset(y->p_pixels, 0x0, y->i_lines * y->i_pitch);
601 for (int i = 1; i < picture->i_planes; i++) {
602 plane_t *p = &picture->p[i];
603 size_t len = p->i_lines * p->i_pitch / 2;
604 int16_t *data = (int16_t*)p->p_pixels;
605 for (size_t j = 0; j < len; j++) // XXX: SIMD
609 size_t len = picture->p[0].i_lines * picture->p[0].i_pitch;
610 for (size_t i = 0; i < len; i+= 2) { // XXX: SIMD
611 picture->p[0].p_pixels[i+0] = 0x80;
612 picture->p[0].p_pixels[i+1] = 0;
620 int w, h, stride, length;
621 w = decklink_sys->i_width;
622 h = decklink_sys->i_height;
624 IDeckLinkMutableVideoFrame *pDLVideoFrame;
625 result = decklink_sys->p_output->CreateVideoFrame(w, h, w*3,
626 sys->tenbits ? bmdFormat10BitYUV : bmdFormat8BitYUV,
627 bmdFrameFlagDefault, &pDLVideoFrame);
629 if (result != S_OK) {
630 msg_Err(vd, "Failed to create video frame: 0x%X", result);
631 pDLVideoFrame = NULL;
636 pDLVideoFrame->GetBytes((void**)&frame_bytes);
637 stride = pDLVideoFrame->GetRowBytes();
640 v210_convert(frame_bytes, picture, stride);
641 else for(int y = 0; y < h; ++y) {
642 uint8_t *dst = (uint8_t *)frame_bytes + stride * y;
643 const uint8_t *src = (const uint8_t *)picture->p[0].p_pixels +
644 picture->p[0].i_pitch * y;
645 memcpy(dst, src, w * 2 /* bpp */);
649 // compute frame duration in CLOCK_FREQ units
650 length = (decklink_sys->frameduration * CLOCK_FREQ) / decklink_sys->timescale;
652 picture->date -= decklink_sys->offset;
653 result = decklink_sys->p_output->ScheduleVideoFrame(pDLVideoFrame,
654 picture->date, length, CLOCK_FREQ);
656 if (result != S_OK) {
657 msg_Err(vd, "Dropped Video frame %"PRId64 ": 0x%x",
658 picture->date, result);
662 now = mdate() - decklink_sys->offset;
664 BMDTimeValue decklink_now;
666 decklink_sys->p_output->GetScheduledStreamTime (CLOCK_FREQ, &decklink_now, &speed);
668 if ((now - decklink_now) > 400000) {
669 /* XXX: workaround card clock drift */
670 decklink_sys->offset += 50000;
671 msg_Err(vd, "Delaying: offset now %"PRId64"", decklink_sys->offset);
676 pDLVideoFrame->Release();
677 picture_Release(orig_picture);
680 static int ControlVideo(vout_display_t *vd, int query, va_list args)
683 const vout_display_cfg_t *cfg;
686 case VOUT_DISPLAY_CHANGE_FULLSCREEN:
687 cfg = va_arg(args, const vout_display_cfg_t *);
688 return cfg->is_fullscreen ? VLC_EGENERIC : VLC_SUCCESS;
694 static int OpenVideo(vlc_object_t *p_this)
696 vout_display_t *vd = (vout_display_t *)p_this;
697 vout_display_sys_t *sys;
698 struct decklink_sys_t *decklink_sys;
700 vd->sys = sys = (vout_display_sys_t*)malloc(sizeof(*sys));
704 sys->tenbits = var_InheritBool(p_this, VIDEO_CFG_PREFIX "tenbits");
705 sys->nosignal_delay = var_InheritInteger(p_this, VIDEO_CFG_PREFIX "nosignal-delay");
706 sys->pic_nosignal = NULL;
708 decklink_sys = OpenDecklink(vd);
710 if (sys->pic_nosignal)
711 picture_Release(sys->pic_nosignal);
718 vd->fmt.i_chroma = sys->tenbits
719 ? VLC_CODEC_I422_10L /* we will convert to v210 */
721 //video_format_FixRgb(&(vd->fmt));
723 vd->fmt.i_width = decklink_sys->i_width;
724 vd->fmt.i_height = decklink_sys->i_height;
726 char *pic_file = var_InheritString(p_this, VIDEO_CFG_PREFIX "nosignal-image");
728 image_handler_t *img = image_HandlerCreate(p_this);
730 msg_Err(p_this, "Could not create image converter");
732 video_format_t in, dummy;
734 video_format_Init(&in, 0);
735 video_format_Setup(&in, 0, vd->fmt.i_width, vd->fmt.i_height,
736 vd->fmt.i_width, vd->fmt.i_height, 1, 1);
738 video_format_Init(&dummy, 0);
740 picture_t *png = image_ReadUrl(img, pic_file, &dummy, &in);
742 msg_Err(p_this, "Converting");
743 sys->pic_nosignal = image_Convert(img, png, &in, &vd->fmt);
744 picture_Release(png);
747 image_HandlerDelete(img);
751 if (!sys->pic_nosignal) {
753 msg_Err(p_this, "Could not create no signal picture");
757 vd->info.has_hide_mouse = true;
758 vd->pool = PoolVideo;
760 vd->display = DisplayVideo;
761 vd->control = ControlVideo;
763 vout_display_SendEventFullscreen(vd, false);
768 static void CloseVideo(vlc_object_t *p_this)
770 vout_display_t *vd = (vout_display_t *)p_this;
771 vout_display_sys_t *sys = vd->sys;
774 picture_pool_Delete(sys->pool);
776 if (sys->pic_nosignal)
777 picture_Release(sys->pic_nosignal);
781 ReleaseDLSys(p_this);
784 /*****************************************************************************
786 *****************************************************************************/
788 static void Flush (audio_output_t *aout, bool drain)
790 struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
791 vlc_mutex_lock(&decklink_sys->lock);
792 IDeckLinkOutput *p_output = decklink_sys->p_output;
793 vlc_mutex_unlock(&decklink_sys->lock);
799 decklink_sys->p_output->GetBufferedAudioSampleFrameCount(&samples);
800 msleep(CLOCK_FREQ * samples / decklink_sys->i_rate);
801 } else if (decklink_sys->p_output->FlushBufferedAudioSamples() == E_FAIL)
802 msg_Err(aout, "Flush failed");
805 static int TimeGet(audio_output_t *, mtime_t* restrict)
807 /* synchronization is handled by the card */
811 static int Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
813 struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
815 if (decklink_sys->i_rate == 0)
818 fmt->i_format = VLC_CODEC_S16N;
819 fmt->i_channels = 2; //decklink_sys->i_channels;
820 fmt->i_physical_channels = AOUT_CHANS_STEREO; //pi_channels_maps[fmt->i_channels];
821 fmt->i_rate = decklink_sys->i_rate;
822 fmt->i_bitspersample = 16;
823 fmt->i_blockalign = fmt->i_channels * fmt->i_bitspersample /8 ;
824 fmt->i_frame_length = FRAME_SIZE;
829 static void PlayAudio(audio_output_t *aout, block_t *audio)
831 struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
832 vlc_mutex_lock(&decklink_sys->lock);
833 IDeckLinkOutput *p_output = decklink_sys->p_output;
834 vlc_mutex_unlock(&decklink_sys->lock);
836 block_Release(audio);
840 audio->i_pts -= decklink_sys->offset;
842 uint32_t sampleFrameCount = audio->i_buffer / (2 * 2 /*decklink_sys->i_channels*/);
844 HRESULT result = decklink_sys->p_output->ScheduleAudioSamples(
845 audio->p_buffer, sampleFrameCount, audio->i_pts, CLOCK_FREQ, &written);
848 msg_Err(aout, "Failed to schedule audio sample: 0x%X", result);
849 else if (sampleFrameCount != written)
850 msg_Err(aout, "Written only %d samples out of %d", written, sampleFrameCount);
852 block_Release(audio);
855 static int OpenAudio(vlc_object_t *p_this)
857 audio_output_t *aout = (audio_output_t *)p_this;
858 struct decklink_sys_t *decklink_sys = GetDLSys(VLC_OBJECT(aout));
860 vlc_mutex_lock(&decklink_sys->lock);
861 //decklink_sys->i_channels = var_InheritInteger(vd, AUDIO_CFG_PREFIX "audio-channels");
862 decklink_sys->i_rate = var_InheritInteger(aout, AUDIO_CFG_PREFIX "audio-rate");
863 decklink_sys->users++;
864 vlc_cond_signal(&decklink_sys->cond);
865 vlc_mutex_unlock(&decklink_sys->lock);
867 aout->play = PlayAudio;
870 aout->time_get = TimeGet;
874 aout->mute_set = NULL;
875 aout->volume_set= NULL;
880 static void CloseAudio(vlc_object_t *p_this)
882 struct decklink_sys_t *decklink_sys = GetDLSys(p_this);
883 vlc_mutex_lock(&decklink_sys->lock);
884 vlc_mutex_unlock(&decklink_sys->lock);
885 ReleaseDLSys(p_this);