[vlc-devel] [PATCH] access module for BlackMagic SDI cards

Steinar H. Gunderson sgunderson at bigfoot.com
Mon Sep 27 13:56:01 CEST 2010


On Mon, Sep 27, 2010 at 11:00:48AM +0200, Jean-Baptiste Kempf wrote:
>> +dnl special access module for BlackMagic SDI cards
>> +dnl
>> +AC_ARG_ENABLE(sdi,
>> +  [  --enable-sdi            BlackMagic SDI access module (default disabled)])
> Why default disabled and not default automatic detection of headers?

I was in doubt what the policy was, and copied from the Hauppage drivers. :-)
In any case, I guess this would break the Windows build currently (if the
user has the SDK installed), so I don't think it's an unreasonable default.

> missing po/POTFILES.in entry, missing NEWS and modules/LIST entries.

Fixed.

>> +#ifndef INT64_C
>> +#define INT64_C(c) c ## LL
>> +#endif
> Is this necessary ?

Yes, it is, given that the module is C++ and the INT64_C stuff is C99.

>> +#include "DeckLinkAPI.h"
>> +#include "DeckLinkAPIDispatch.cpp"
> Shouldn't this be < > ?

Fixed.

>> +    /* Note: AddRef() and Release() here are not thread safe. */
> This is an issue, no?

Actually I don't think so. The only point where they are AddRef()-ed is from
Open(), and the only point where they are Release()-ed is in Close() (after
we've shut down the SDI worker threads). Granted, you could say that the SDK
might AddRef() and Release() the delegate, but BlackMagic's own example code
actually just has "return 1;" everywhere here, so I don't think it's a big
problem in practice.

Does VLC have incremental inc and dec functions I could use?

>> +HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame, IDeckLinkAudioInputPacket* audioFrame)
>> +{
>> +
>> +        p_video_frame = block_New( p_demux_, i_width * i_height * i_bpp );
>> +        if( !p_video_frame )
>> +        {
>> +            msg_Err( p_demux_, "Could not allocate memory for video frame" );
>> +            return S_OK;
> Is this normal?

Yes, it is. All functions in the SDK need to return a HRESULT (since the API
is based on COM), but the documentation states that the return value from
this function is ignored. The best thing we can do is drop the frame and go
on, as far as I can see (unless we wanted to kill all of VLC).

>> +    if( audioFrame )
>> +    {
>> +        const int i_bytes = audioFrame->GetSampleFrameCount() * sizeof(int16_t) * p_sys->i_channels;
>> +
>> +        p_audio_frame = block_New( p_demux_, i_bytes );
>> +        if( !p_audio_frame )
>> +        {
>> +            msg_Err( p_demux_, "Could not allocate memory for audio frame" );
>> +            return S_OK;
> Same as above. Could this leak p_video_frame?

It could. Fixed.

>> +    /* Only when selected */
>> +    if( *p_demux->psz_access == '\0' )
>> +        return VLC_EGENERIC;
> Are you strict enough?

This part was copied from some other driver (v4l2, I think). I have no idea
what it actually means. Care to explain?

>> +    int i_card_index = var_CreateGetInteger( p_demux, "sdi-card-index" );
> Shouldn't you use inherit here?

Please explain (or point to an example). I have no idea how this stuff works :-)

>> +    strcpy(sz_display_mode_padded, "    ");
> strncpy, maybe?

strncpy() will add a null byte, which is not what we want. I could always use
memcpy?

>> +            p_display_mode->Release();
>> +            p_display_iterator->Release();
>> +            Close( p_this );
>> +            return VLC_EGENERIC;
> Can't you factor a bit thise code? like a goto error ?

I wondered what to do about this one, and it's really easy to miss one or two
releases in some path. I wonder if the cleanest thing is to put _everything_
in Close(), even though, say, p_display_iterator is never used outside of
Open(). What would you prefer -- a goto or putting stuff in Close()?

>> +        msg_Err( p_demux, "Failed to start streams" );
> What does this mean?

It means the card refused to start streaming for some reason.
The documentation lists the following return codes:

  E_FAIL           Failure
  S_OK             Success
  E_INVALIDARG     Is returned on invalid mode or video flags
  E_ACCESSDENIED   Unable to access the hardware or input stream currently
                   active
  E_OUTOFMEMORY    Unable to create a new frame

Do you want me to reword the message?

> Beware of trailing spaces in this patch

Fixed.

Updated patch below.

/* Steinar */

diff --git a/NEWS b/NEWS
index a5390ef..4fab873 100644
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ Access
  * DShow: support for freq and video standard selection
  * Support for VDR recordings
  * Blu-Ray integration with libbluray
+ * Blackmagic DeckLink SDI card input support (Linux only currently)
 
 Codecs
  * You can now use ffmpeg-mt in conjunction with vlc
diff --git a/configure.ac b/configure.ac
index 2894880..f0db148 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2134,6 +2134,29 @@ then
 fi
 
 dnl
+dnl special access module for Blackmagic SDI cards
+dnl
+AC_ARG_ENABLE(sdi,
+  [  --enable-sdi            Blackmagic SDI access module (default disabled)])
+if test "${enable_sdi}" = "yes"
+then
+  AC_ARG_WITH(sdi_sdk,
+    [  --with-sdi-sdk=DIR      Location of Blackmagic SDI SDK],[],[])
+  if test "${with_sdi_sdk}" != "no" -a -n "${with_sdi_sdk}"
+  then
+    VLC_ADD_CPPFLAGS([sdi],[-I${with_sdi_sdk}/include])
+  fi
+  CPPFLAGS="${CPPFLAGS_save} ${CPPFLAGS_sdi}"
+  AC_LANG_PUSH(C++)
+  AC_CHECK_HEADERS(DeckLinkAPIDispatch.cpp, [
+    VLC_ADD_PLUGIN([sdi])
+  ],[AC_MSG_WARN(Blackmagic SDI include files not found, sdi disabled)])
+  AC_LANG_POP(C++)
+  CPPFLAGS="${CPPFLAGS_save}"
+fi
+
+
+dnl
 dnl  gnomeVFS access module
 dnl
 PKG_ENABLE_MODULES_VLC([GNOMEVFS], [access_gnomevfs], [gnome-vfs-2.0], [GnomeVFS access module], [auto])
diff --git a/modules/LIST b/modules/LIST
index 6ceb6ef..8a0314e 100644
--- a/modules/LIST
+++ b/modules/LIST
@@ -264,6 +264,7 @@ $Id$
  * schroedinger: Schroedinger video decoder
  * screen: a input module that takes screenshots of the primary monitor
  * sdl_image: SDL-based image decoder
+ * sdi: input module to read from a Blackmagic SDI card
  * sharpen: Sharpen video filter
  * shine: MP3 encoder using Shine, a fixed point implementation
  * simple_channel_mixer: channel mixer
diff --git a/modules/access/Modules.am b/modules/access/Modules.am
index 0b9f94a..0539577 100644
--- a/modules/access/Modules.am
+++ b/modules/access/Modules.am
@@ -52,6 +52,7 @@ SOURCES_access_avio = avio.c avio.h
 SOURCES_access_attachment = attachment.c
 SOURCES_access_vdr = vdr.c
 SOURCES_libbluray = bluray.c
+SOURCES_sdi = sdi.cpp
 
 SOURCES_access_rar = rar/rar.c rar/rar.h rar/access.c
 SOURCES_stream_filter_rar = rar/rar.c rar/rar.h rar/stream.c
diff --git a/modules/access/sdi.cpp b/modules/access/sdi.cpp
new file mode 100644
index 0000000..ec93c27
--- /dev/null
+++ b/modules/access/sdi.cpp
@@ -0,0 +1,705 @@
+/* Blackmagic SDI driver */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef INT64_C
+#define INT64_C(c) c ## LL
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_input.h>
+#include <vlc_demux.h>
+#include <vlc_access.h>
+#include <vlc_picture.h>
+#include <vlc_charset.h>
+#include <vlc_fs.h>
+
+#include <arpa/inet.h>
+
+#include <DeckLinkAPI.h>
+#include <DeckLinkAPIDispatch.cpp>
+
+static int  Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+
+#define CARD_INDEX_TEXT N_("Input card to use")
+#define CARD_INDEX_LONGTEXT N_( \
+    "SDI capture card to use, if multiple exist. " \
+    "The cards are numbered from 0." )
+
+#define MODE_TEXT N_("Desired input video mode")
+#define MODE_LONGTEXT N_( \
+    "Desired input video mode for SDI captures. " \
+    "This value should be a FOURCC code in textual " \
+    "form, e.g. \"ntsc\"." )
+
+#define CACHING_TEXT N_("Caching value in ms")
+#define CACHING_LONGTEXT N_( \
+    "Caching value for SDI captures. This " \
+    "value should be set in milliseconds." )
+
+#define AUDIO_CONNECTION_TEXT N_("Audio connection")
+#define AUDIO_CONNECTION_LONGTEXT N_( \
+    "Audio connection to use for SDI captures. " \
+    "Valid choices: embedded, aesebu, analog. " \
+    "Leave blank for card default." )
+
+#define RATE_TEXT N_("Audio sampling rate in Hz")
+#define RATE_LONGTEXT N_( \
+    "Audio sampling rate (in hertz) for SDI captures. " \
+    "0 disables audio input." )
+
+#define CHANNELS_TEXT N_("Number of audio channels")
+#define CHANNELS_LONGTEXT N_( \
+    "Number of input audio channels for SDI captures. " \
+    "Must be 2, 8 or 16. 0 disables audio input." )
+
+#define VIDEO_CONNECTION_TEXT N_("Video connection")
+#define VIDEO_CONNECTION_LONGTEXT N_( \
+    "Video connection to use for SDI captures. " \
+    "Valid choices: sdi, hdmi, opticalsdi, component, " \
+    "composite, svideo. " \
+    "Leave blank for card default." )
+
+#define ASPECT_RATIO_TEXT N_("Aspect ratio")
+#define ASPECT_RATIO_LONGTEXT N_( \
+    "Aspect ratio (4:3, 16:9). Default assumes square pixels." )
+
+vlc_module_begin ()
+    set_shortname( N_("SDI") )
+    set_description( N_("Blackmagic SDI input") )
+    set_category( CAT_INPUT )
+    set_subcategory( SUBCAT_INPUT_ACCESS )
+
+    add_integer( "sdi-card-index", 0, NULL,
+                 CARD_INDEX_TEXT, CARD_INDEX_LONGTEXT, true )
+    add_string( "sdi-mode", "pal ", NULL,
+                 MODE_TEXT, MODE_LONGTEXT, true )
+    add_integer( "sdi-caching", DEFAULT_PTS_DELAY / 1000, NULL,
+                 CACHING_TEXT, CACHING_LONGTEXT, true )
+    add_string( "sdi-audio-connection", 0, NULL,
+                 AUDIO_CONNECTION_TEXT, AUDIO_CONNECTION_LONGTEXT, true )
+    add_integer( "sdi-audio-rate", 48000, NULL,
+                 RATE_TEXT, RATE_LONGTEXT, true )
+    add_integer( "sdi-audio-channels", 2, NULL,
+                 CHANNELS_TEXT, CHANNELS_LONGTEXT, true )
+    add_string( "sdi-video-connection", 0, NULL,
+                 VIDEO_CONNECTION_TEXT, VIDEO_CONNECTION_LONGTEXT, true )
+    add_string( "sdi-aspect-ratio", NULL, NULL,
+                ASPECT_RATIO_TEXT, ASPECT_RATIO_LONGTEXT, true )
+
+    add_shortcut( "sdi" )
+    set_capability( "access_demux", 10 )
+    set_callbacks( Open, Close )
+vlc_module_end ()
+
+static int Demux  ( demux_t * );
+static int Control( demux_t *, int, va_list );
+
+class DeckLinkCaptureDelegate;
+
+struct demux_sys_t
+{
+    IDeckLink *p_card;
+    IDeckLinkInput *p_input;
+    DeckLinkCaptureDelegate *p_delegate;
+
+    es_out_id_t *p_video_es;
+    es_out_id_t *p_audio_es;
+    bool b_first_frame;
+    int i_last_pts;
+
+    int i_width, i_height, i_fps_num, i_fps_den;
+    uint32_t i_dominance_flags;
+
+    int i_rate, i_channels;
+
+    vlc_mutex_t frame_lock;
+    block_t *p_video_frame;  /* protected by <frame_lock> */
+    block_t *p_audio_frame;  /* protected by <frame_lock> */
+    vlc_cond_t has_frame;  /* related to <frame_lock> */
+};
+
+class DeckLinkCaptureDelegate : public IDeckLinkInputCallback
+{
+public:
+    DeckLinkCaptureDelegate( demux_t *p_demux ) : m_ref_(1), p_demux_(p_demux) {}
+
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }
+
+    /* Note: AddRef() and Release() here are not thread safe. */
+
+    virtual ULONG STDMETHODCALLTYPE AddRef(void)
+    {
+        return ++m_ref_;
+    }
+
+    virtual ULONG STDMETHODCALLTYPE Release(void)
+    {
+        if ( --m_ref_ == 0 )
+        {
+            delete this;
+            return 0;
+        }
+        return m_ref_;
+    }
+
+    virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags);
+    virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
+
+private:
+    int m_ref_;
+    demux_t *p_demux_;
+};
+
+HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags)
+{
+    msg_Dbg( p_demux_, "Video input format changed" );
+    return S_OK;
+}
+
+HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(IDeckLinkVideoInputFrame* videoFrame, IDeckLinkAudioInputPacket* audioFrame)
+{
+    demux_sys_t *p_sys = p_demux_->p_sys;
+    block_t *p_video_frame = NULL;
+    block_t *p_audio_frame = NULL;
+
+    if( videoFrame )
+    {
+        if( videoFrame->GetFlags() & bmdFrameHasNoInputSource )
+        {
+            msg_Warn( p_demux_, "No input signal detected" );
+            return S_OK;
+        }
+
+        const int i_width = videoFrame->GetWidth();
+        const int i_height = videoFrame->GetHeight();
+        const int i_stride = videoFrame->GetRowBytes();
+        const int i_bpp = 2;
+
+        p_video_frame = block_New( p_demux_, i_width * i_height * i_bpp );
+        if( !p_video_frame )
+        {
+            msg_Err( p_demux_, "Could not allocate memory for video frame" );
+            return S_OK;
+        }
+
+        void *frame_bytes;
+        videoFrame->GetBytes( &frame_bytes );
+        for( int y = 0; y < i_height; ++y )
+        {
+            const uint8_t *src = (const uint8_t *)frame_bytes + i_stride * y;
+            uint8_t *dst = (uint8_t *)p_video_frame->p_buffer + i_width * i_bpp * y;
+            memcpy( dst, src, i_width * i_bpp );
+        }
+
+        BMDTimeValue stream_time, frame_duration;
+        videoFrame->GetStreamTime( &stream_time, &frame_duration, CLOCK_FREQ );
+        p_video_frame->i_flags = BLOCK_FLAG_TYPE_I | p_sys->i_dominance_flags;
+        if( p_sys->b_first_frame )
+        {
+            p_video_frame->i_flags |= BLOCK_FLAG_DISCONTINUITY;
+            p_sys->b_first_frame = false;
+        }
+        p_video_frame->i_pts = p_video_frame->i_dts = VLC_TS_0 + stream_time;
+    }
+
+    if( audioFrame )
+    {
+        const int i_bytes = audioFrame->GetSampleFrameCount() * sizeof(int16_t) * p_sys->i_channels;
+
+        p_audio_frame = block_New( p_demux_, i_bytes );
+        if( !p_audio_frame )
+        {
+            msg_Err( p_demux_, "Could not allocate memory for audio frame" );
+            if( p_video_frame )
+                block_Release( p_video_frame );
+            return S_OK;
+        }
+
+        void *frame_bytes;
+        audioFrame->GetBytes( &frame_bytes );
+        memcpy( p_audio_frame->p_buffer, frame_bytes, i_bytes );
+
+        BMDTimeValue packet_time;
+        audioFrame->GetPacketTime( &packet_time, CLOCK_FREQ );
+        p_audio_frame->i_pts = p_audio_frame->i_dts = VLC_TS_0 + packet_time;
+    }
+
+    if( p_video_frame || p_audio_frame )
+    {
+        vlc_mutex_lock( &p_sys->frame_lock );
+        if( p_video_frame )
+        {
+            if( p_sys->p_video_frame )
+                block_Release( p_sys->p_video_frame );
+            p_sys->p_video_frame = p_video_frame;
+        }
+        if( p_audio_frame )
+        {
+            if( p_sys->p_audio_frame )
+                block_Release( p_sys->p_audio_frame );
+            p_sys->p_audio_frame = p_audio_frame;
+        }
+        vlc_cond_signal( &p_sys->has_frame );
+        vlc_mutex_unlock( &p_sys->frame_lock );
+    }
+
+    return S_OK;
+}
+
+static int Open( vlc_object_t *p_this )
+{
+    demux_t     *p_demux = (demux_t*)p_this;
+    demux_sys_t *p_sys;
+
+    /* Only when selected */
+    if( *p_demux->psz_access == '\0' )
+        return VLC_EGENERIC;
+
+    /* Set up p_demux */
+    p_demux->pf_demux = Demux;
+    p_demux->pf_control = Control;
+    p_demux->info.i_update = 0;
+    p_demux->info.i_title = 0;
+    p_demux->info.i_seekpoint = 0;
+    p_demux->p_sys = p_sys = (demux_sys_t*)calloc( 1, sizeof( demux_sys_t ) );
+    if( !p_sys )
+        return VLC_ENOMEM;
+
+    vlc_mutex_init( &p_sys->frame_lock );
+    vlc_cond_init( &p_sys->has_frame );
+    p_sys->b_first_frame = true;
+
+    IDeckLinkIterator *decklink_iterator = CreateDeckLinkIteratorInstance();
+    if( !decklink_iterator )
+    {
+        msg_Err( p_demux, "DeckLink drivers not found." );
+        decklink_iterator->Release();
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    HRESULT result;
+
+    int i_card_index = var_CreateGetInteger( p_demux, "sdi-card-index" );
+    for( int i = 0; i <= i_card_index; ++i )
+    {
+        if( p_sys->p_card )
+            p_sys->p_card->Release();
+        result = decklink_iterator->Next( &p_sys->p_card );
+        if( result != S_OK )
+            break;
+    }
+
+    decklink_iterator->Release();
+
+    if( result != S_OK )
+    {
+        msg_Err( p_demux, "DeckLink PCI card %d not found", i_card_index );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    const char *psz_model_name;
+    result = p_sys->p_card->GetModelName( &psz_model_name );
+
+    if( result != S_OK )
+    {
+        msg_Err( p_demux, "Could not get model name" );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    msg_Dbg( p_demux, "Opened DeckLink PCI card %d (%s)", i_card_index, psz_model_name );
+
+    if( p_sys->p_card->QueryInterface( IID_IDeckLinkInput, (void**)&p_sys->p_input) != S_OK )
+    {
+        msg_Err( p_demux, "Card has no inputs" );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    /* Set up the video and audio sources. */
+    IDeckLinkConfiguration *p_config;
+    if( p_sys->p_card->QueryInterface( IID_IDeckLinkConfiguration, (void**)&p_config) != S_OK )
+    {
+        msg_Err( p_demux, "Failed to get configuration interface" );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    char *psz_tmp = var_CreateGetNonEmptyString( p_demux, "sdi-video-connection" );
+    if( psz_tmp )
+    {
+        BMDVideoConnection conn;
+        if ( !strcmp( psz_tmp, "sdi" ) )
+            conn = bmdVideoConnectionSDI;
+        else if ( !strcmp( psz_tmp, "hdmi" ) )
+            conn = bmdVideoConnectionHDMI;
+        else if ( !strcmp( psz_tmp, "opticalsdi" ) )
+            conn = bmdVideoConnectionOpticalSDI;
+        else if ( !strcmp( psz_tmp, "component" ) )
+            conn = bmdVideoConnectionComponent;
+        else if ( !strcmp( psz_tmp, "composite" ) )
+            conn = bmdVideoConnectionComposite;
+        else if ( !strcmp( psz_tmp, "svideo" ) )
+            conn = bmdVideoConnectionSVideo;
+        else
+        {
+            msg_Err( p_demux, "Invalid --sdi-video-connection specified; choose one of " \
+                              "sdi, hdmi, opticalsdi, component, composite, or svideo." );
+            p_config->Release();
+            free( psz_tmp );
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+        free( psz_tmp );
+
+        msg_Dbg( p_demux, "Setting video input format to 0x%x", conn);
+        result = p_config->SetVideoInputFormat( conn );
+        if( result != S_OK )
+        {
+            msg_Err( p_demux, "Failed to set video input connection" );
+            p_config->Release();
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+    }
+
+    psz_tmp = var_CreateGetNonEmptyString( p_demux, "sdi-audio-connection" );
+    if( psz_tmp )
+    {
+        BMDAudioConnection conn;
+        if ( !strcmp( psz_tmp, "embedded" ) )
+            conn = bmdAudioConnectionEmbedded;
+        else if ( !strcmp( psz_tmp, "aesebu" ) )
+            conn = bmdAudioConnectionAESEBU;
+        else if ( !strcmp( psz_tmp, "analog" ) )
+            conn = bmdAudioConnectionAnalog;
+        else
+        {
+            msg_Err( p_demux, "Invalid --sdi-audio-connection specified; choose one of " \
+                              "embedded, aesebu, or analog." );
+            free( psz_tmp );
+            p_config->Release();
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+        free( psz_tmp );
+
+        msg_Dbg( p_demux, "Setting audio input format to 0x%x", conn);
+        result = p_config->SetAudioInputFormat( conn );
+        if( result != S_OK )
+        {
+            msg_Err( p_demux, "Failed to set audio input connection" );
+            p_config->Release();
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+    }
+
+    p_config->Release();
+
+    /* Get the list of display modes. */
+    IDeckLinkDisplayModeIterator *p_display_iterator;
+    result = p_sys->p_input->GetDisplayModeIterator( &p_display_iterator );
+    if( result != S_OK )
+    {
+        msg_Err( p_demux, "Failed to enumerate display modes" );
+        p_display_iterator->Release();
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    char *psz_display_mode = var_CreateGetString( p_demux, "sdi-mode" );
+    if( !psz_display_mode || strlen( psz_display_mode ) == 0 || strlen( psz_display_mode ) > 4 ) {
+        msg_Err( p_demux, "Missing or invalid --sdi-mode string" );
+        free( psz_display_mode );
+        p_display_iterator->Release();
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    /*
+     * Pad the --sdi-mode string to four characters, so the user can specify e.g. "pal"
+     * without having to add the trailing space.
+     */
+    char sz_display_mode_padded[5];
+    strcpy(sz_display_mode_padded, "    ");
+    for( int i = 0; i < strlen( psz_display_mode ); ++i )
+        sz_display_mode_padded[i] = psz_display_mode[i];
+
+    free( psz_display_mode );
+
+    BMDDisplayMode wanted_mode_id;
+    memcpy( &wanted_mode_id, &sz_display_mode_padded, sizeof(wanted_mode_id) );
+
+    bool b_found_mode = false;
+
+    for (;;)
+    {
+        IDeckLinkDisplayMode *p_display_mode;
+        result = p_display_iterator->Next( &p_display_mode );
+        if( result != S_OK || !p_display_mode )
+            break;
+
+        char sz_mode_id_text[5] = {0};
+        BMDDisplayMode mode_id = ntohl( p_display_mode->GetDisplayMode() );
+        memcpy( sz_mode_id_text, &mode_id, sizeof(mode_id) );
+
+        const char *psz_mode_name;
+        result = p_display_mode->GetName( &psz_mode_name );
+        if( result != S_OK )
+        {
+            msg_Err( p_demux, "Failed to get display mode name" );
+            p_display_mode->Release();
+            p_display_iterator->Release();
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+
+        BMDTimeValue frame_duration, time_scale;
+        result = p_display_mode->GetFrameRate( &frame_duration, &time_scale );
+        if( result != S_OK )
+        {
+            msg_Err( p_demux, "Failed to get frame rate" );
+            p_display_mode->Release();
+            p_display_iterator->Release();
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+
+        const char *psz_field_dominance;
+        uint32_t i_dominance_flags = 0;
+        switch( p_display_mode->GetFieldDominance() )
+        {
+        case bmdProgressiveFrame:
+            psz_field_dominance = "";
+            break;
+        case bmdProgressiveSegmentedFrame:
+            psz_field_dominance = ", segmented";
+            break;
+        case bmdLowerFieldFirst:
+            psz_field_dominance = ", interlaced [BFF]";
+            i_dominance_flags = BLOCK_FLAG_BOTTOM_FIELD_FIRST;
+            break;
+        case bmdUpperFieldFirst:
+            psz_field_dominance = ", interlaced [TFF]";
+            i_dominance_flags = BLOCK_FLAG_TOP_FIELD_FIRST;
+            break;
+        case bmdUnknownFieldDominance:
+        default:
+            psz_field_dominance = ", unknown field dominance";
+            break;
+        }
+
+        msg_Dbg( p_demux, "Found mode '%s': %s (%dx%d, %.3f fps%s)",
+                 sz_mode_id_text, psz_mode_name,
+                 p_display_mode->GetWidth(), p_display_mode->GetHeight(),
+                 double(time_scale) / frame_duration, psz_field_dominance );
+
+        if( wanted_mode_id == mode_id )
+        {
+            b_found_mode = true;
+            p_sys->i_width = p_display_mode->GetWidth();
+            p_sys->i_height = p_display_mode->GetHeight();
+            p_sys->i_fps_num = time_scale;
+            p_sys->i_fps_den = frame_duration;
+            p_sys->i_dominance_flags = i_dominance_flags;
+        }
+
+        p_display_mode->Release();
+    }
+
+    p_display_iterator->Release();
+
+    if( !b_found_mode )
+    {
+        msg_Err( p_demux, "Unknown SDI mode specified. " \
+                          "Run VLC with -v --verbose-objects=-all,+sdi " \
+                          "to get a list of supported modes." );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    result = p_sys->p_input->EnableVideoInput( htonl( wanted_mode_id ), bmdFormat8BitYUV, 0 );
+    if( result != S_OK )
+    {
+        msg_Err( p_demux, "Failed to enable video input" );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    /* Set up audio. */
+    p_sys->i_rate = var_CreateGetInteger( p_demux, "sdi-audio-rate" );
+    p_sys->i_channels = var_CreateGetInteger( p_demux, "sdi-audio-channels" );
+    if( p_sys->i_rate > 0 && p_sys->i_channels > 0 )
+    {
+        result = p_sys->p_input->EnableAudioInput( p_sys->i_rate, bmdAudioSampleType16bitInteger, p_sys->i_channels );
+        if( result != S_OK )
+        {
+            msg_Err( p_demux, "Failed to enable audio input" );
+            Close( p_this );
+            return VLC_EGENERIC;
+        }
+    }
+
+    p_sys->p_delegate = new DeckLinkCaptureDelegate( p_demux );
+    p_sys->p_input->SetCallback( p_sys->p_delegate );
+
+    result = p_sys->p_input->StartStreams();
+    if( result != S_OK )
+    {
+        msg_Err( p_demux, "Failed to start streams" );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    /* Declare elementary streams */
+    es_format_t video_fmt;
+    es_format_Init( &video_fmt, VIDEO_ES, VLC_CODEC_UYVY );
+    video_fmt.video.i_width = p_sys->i_width;
+    video_fmt.video.i_height = p_sys->i_height;
+    video_fmt.video.i_sar_num = 1;
+    video_fmt.video.i_sar_den = 1;
+    video_fmt.video.i_frame_rate = p_sys->i_fps_num;
+    video_fmt.video.i_frame_rate_base = p_sys->i_fps_den;
+    video_fmt.i_bitrate = video_fmt.video.i_width * video_fmt.video.i_height * video_fmt.video.i_frame_rate * 2 * 8;
+
+    psz_tmp = var_CreateGetNonEmptyString( p_demux, "sdi-aspect-ratio" );
+    if( psz_tmp )
+    {
+        char *psz_denominator = strchr( psz_tmp, ':' );
+        if( psz_denominator )
+        {
+            *psz_denominator++ = '\0';
+            video_fmt.video.i_sar_num = atoi( psz_tmp )         * video_fmt.video.i_height;
+            video_fmt.video.i_sar_den = atoi( psz_denominator ) * video_fmt.video.i_width;
+        }
+        free( psz_tmp );
+    }
+
+    msg_Dbg( p_demux, "added new video es %4.4s %dx%d",
+             (char*)&video_fmt.i_codec, video_fmt.video.i_width, video_fmt.video.i_height );
+    p_sys->p_video_es = es_out_Add( p_demux->out, &video_fmt );
+
+    es_format_t audio_fmt;
+    es_format_Init( &audio_fmt, AUDIO_ES, VLC_CODEC_S16N );
+    audio_fmt.audio.i_channels = p_sys->i_channels;
+    audio_fmt.audio.i_rate = p_sys->i_rate;
+    audio_fmt.audio.i_bitspersample = 16;
+    audio_fmt.audio.i_blockalign = audio_fmt.audio.i_channels * audio_fmt.audio.i_bitspersample / 8;
+    audio_fmt.i_bitrate = audio_fmt.audio.i_channels * audio_fmt.audio.i_rate * audio_fmt.audio.i_bitspersample;
+
+    msg_Dbg( p_demux, "added new audio es %4.4s %dHz %dbpp %dch",
+             (char*)&audio_fmt.i_codec, audio_fmt.audio.i_rate, audio_fmt.audio.i_bitspersample, audio_fmt.audio.i_channels);
+    p_sys->p_audio_es = es_out_Add( p_demux->out, &audio_fmt );
+
+    /* Update default_pts to a suitable value for access */
+    var_Create( p_demux, "sdi-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
+
+    return VLC_SUCCESS;
+}
+
+static void Close( vlc_object_t *p_this )
+{
+    demux_t     *p_demux = (demux_t *)p_this;
+    demux_sys_t *p_sys   = p_demux->p_sys;
+
+    if( p_sys->p_input )
+    {
+        p_sys->p_input->StopStreams();
+        p_sys->p_input->Release();
+    }
+
+    if( p_sys->p_card )
+        p_sys->p_card->Release();
+
+    if( p_sys->p_delegate )
+        p_sys->p_delegate->Release();
+
+    if( p_sys->p_video_frame )
+        block_Release( p_sys->p_video_frame );
+
+    if( p_sys->p_audio_frame )
+        block_Release( p_sys->p_audio_frame );
+
+    free( p_sys );
+}
+
+static int Control( demux_t *p_demux, int i_query, va_list args )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    bool *pb;
+    int64_t    *pi64;
+
+    switch( i_query )
+    {
+        /* Special for access_demux */
+        case DEMUX_CAN_PAUSE:
+        case DEMUX_CAN_SEEK:
+        case DEMUX_CAN_CONTROL_PACE:
+            pb = (bool*)va_arg( args, bool * );
+            *pb = false;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_PTS_DELAY:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = var_GetInteger( p_demux, "sdi-caching" ) * 1000;
+            return VLC_SUCCESS;
+
+        case DEMUX_GET_TIME:
+            pi64 = (int64_t*)va_arg( args, int64_t * );
+            *pi64 = p_sys->i_last_pts;
+            return VLC_SUCCESS;
+
+        /* TODO implement others */
+        default:
+            return VLC_EGENERIC;
+    }
+
+    return VLC_EGENERIC;
+}
+
+static int Demux( demux_t *p_demux )
+{
+    demux_sys_t *p_sys = p_demux->p_sys;
+    block_t *p_video_block = NULL;
+    block_t *p_audio_block = NULL;
+
+    vlc_mutex_lock( &p_sys->frame_lock );
+
+    while( !p_sys->p_video_frame && !p_sys->p_audio_frame )
+        vlc_cond_wait( &p_sys->has_frame, &p_sys->frame_lock );
+
+    p_video_block = p_sys->p_video_frame;
+    p_sys->p_video_frame = NULL;
+
+    p_audio_block = p_sys->p_audio_frame;
+    p_sys->p_audio_frame = NULL;
+
+    vlc_mutex_unlock( &p_sys->frame_lock );
+
+    if( p_video_block )
+    {
+        if( p_video_block->i_pts > p_sys->i_last_pts )
+            p_sys->i_last_pts = p_video_block->i_pts;
+        es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_video_block->i_pts );
+        es_out_Send( p_demux->out, p_sys->p_video_es, p_video_block );
+    }
+
+    if( p_audio_block )
+    {
+        if( p_audio_block->i_pts > p_sys->i_last_pts )
+            p_sys->i_last_pts = p_audio_block->i_pts;
+        es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_audio_block->i_pts );
+        es_out_Send( p_demux->out, p_sys->p_audio_es, p_audio_block );
+    }
+
+    return 1;
+}
+
diff --git a/po/POTFILES.in b/po/POTFILES.in
index a45b25b..747dd50 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -271,6 +271,7 @@ modules/access/screen/screen.c
 modules/access/screen/screen.h
 modules/access/screen/win32.c
 modules/access/screen/xcb.c
+modules/access/sdi.cpp
 modules/access/sftp.c
 modules/access/smb.c
 modules/access/tcp.c

-- 
Homepage: http://www.sesse.net/



More information about the vlc-devel mailing list