[vlc-devel] [PATCH] access module for BlackMagic SDI cards
Laurent Aimar
fenrir at elivagar.org
Sat Oct 2 01:18:58 CEST 2010
Hi,
> +/*****************************************************************************
> + * decklink.cpp: BlackMagic DeckLink SDI input module
> + *****************************************************************************
> + * Copyright (C) 2010 VideoLAN
Not correct, you can assign it to yourself.
> + *
> + * Authors: Steinar H. Gunderson <steinar+vlc at gunderson.no>
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + *
> + *****************************************************************************/
> +
> +#ifdef HAVE_CONFIG_H
> +# include "config.h"
> +#endif
> +#ifndef INT64_C
> +#define INT64_C(c) c ## LL
> +#endif
Adding
#define __STDC_CONSTANT_MACROS 1
before the config.h include should properly defines it.
> +#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 <vlc_atomic.h>
> +#include <arpa/inet.h>
Are you sure you need all of thoses headers ? (At quick glance,
vlc_access, vlc_picture are probably not needed, I haven't check the
others).
> +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;
Do you need them here ?
> + uint32_t i_dominance_flags;
> +
> + int i_rate, i_channels;
Same.
> +
> + 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 ) : p_demux_(p_demux)
> + {
> + vlc_atomic_set( &m_ref_, 1 );
> + }
> +
> + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv) { return E_NOINTERFACE; }
> +
> + virtual ULONG STDMETHODCALLTYPE AddRef(void)
> + {
> + return vlc_atomic_inc( &m_ref_ );
> + }
> +
> + virtual ULONG STDMETHODCALLTYPE Release(void)
> + {
> + uintptr_t new_ref = vlc_atomic_dec( &m_ref_ );
> + if ( new_ref == 0 )
> + delete this;
> + return new_ref;
> + }
> +
> + virtual HRESULT STDMETHODCALLTYPE VideoInputFormatChanged(BMDVideoInputFormatChangedEvents, IDeckLinkDisplayMode*, BMDDetectedVideoInputFormatFlags);
> + virtual HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(IDeckLinkVideoInputFrame*, IDeckLinkAudioInputPacket*);
> +
> +private:
> + vlc_atomic_t m_ref_;
> + demux_t *p_demux_;
Why the _ suffix ?
> +HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged(BMDVideoInputFormatChangedEvents events, IDeckLinkDisplayMode *mode, BMDDetectedVideoInputFormatFlags)
> +{
> + msg_Dbg( p_demux_, "Video input format changed" );
> + return S_OK;
> +}
Could be moved to the class definition (it's a one line).
> +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;
Useless cast.
> + 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;
> + }
I don't think you need that.
> + 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;
Using audioFrame->GetBytes() seems simpler.
> +
> + 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 );
You don't need that part. Simply call ES_OUT_SET_PCR and then
send the data to the es_out here and do not implement the Demux()
function (set demux_t::pf_demux to NULL). It will be simpler and
will avoid useless polling.
> +static int Open( vlc_object_t *p_this )
> +{
> + demux_t *p_demux = (demux_t*)p_this;
> + demux_sys_t *p_sys;
> + int ret = VLC_SUCCESS;
I think that setting it to VLC_EGENERIC by default would simplify
a bit the code.
> + char *psz_aspect;
> + char *psz_display_mode = NULL;
> + char *psz_video_connection = NULL;
> + char *psz_audio_connection = NULL;
> + bool b_found_mode;
> + int i_card_index;
> +
> + /* 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." );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + HRESULT result;
> +
> + i_card_index = var_InheritInteger( p_demux, "decklink-card-index" );
> + for( int i = 0; i <= i_card_index; ++i )
Nothing prevent i_card_index from being < 0 leading to result uninitialized.
> + {
> + if( p_sys->p_card )
> + p_sys->p_card->Release();
> + result = decklink_iterator->Next( &p_sys->p_card );
> + if( result != S_OK )
> + break;
> + }
> +
> + if( result != S_OK )
> + {
> + msg_Err( p_demux, "DeckLink PCI card %d not found", i_card_index );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + 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" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + 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" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + /* 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" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + psz_video_connection = var_CreateGetNonEmptyString( p_demux, "decklink-video-connection" );
> + if( psz_video_connection )
> + {
> + BMDVideoConnection conn;
> + if ( !strcmp( psz_video_connection, "sdi" ) )
> + conn = bmdVideoConnectionSDI;
> + else if ( !strcmp( psz_video_connection, "hdmi" ) )
> + conn = bmdVideoConnectionHDMI;
> + else if ( !strcmp( psz_video_connection, "opticalsdi" ) )
> + conn = bmdVideoConnectionOpticalSDI;
> + else if ( !strcmp( psz_video_connection, "component" ) )
> + conn = bmdVideoConnectionComponent;
> + else if ( !strcmp( psz_video_connection, "composite" ) )
> + conn = bmdVideoConnectionComposite;
> + else if ( !strcmp( psz_video_connection, "svideo" ) )
> + conn = bmdVideoConnectionSVideo;
> + else
> + {
> + msg_Err( p_demux, "Invalid --decklink-video-connection specified; choose one of " \
> + "sdi, hdmi, opticalsdi, component, composite, or svideo." );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + 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" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> + }
> +
> + psz_audio_connection = var_CreateGetNonEmptyString( p_demux, "decklink-audio-connection" );
> + if( psz_audio_connection )
> + {
> + BMDAudioConnection conn;
> + if ( !strcmp( psz_audio_connection, "embedded" ) )
> + conn = bmdAudioConnectionEmbedded;
> + else if ( !strcmp( psz_audio_connection, "aesebu" ) )
> + conn = bmdAudioConnectionAESEBU;
> + else if ( !strcmp( psz_audio_connection, "analog" ) )
> + conn = bmdAudioConnectionAnalog;
> + else
> + {
> + msg_Err( p_demux, "Invalid --decklink-audio-connection specified; choose one of " \
> + "embedded, aesebu, or analog." );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + 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" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> + }
> +
> + /* 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" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> +
> + psz_display_mode = var_InheritString( p_demux, "decklink-mode" );
var_CreateGetNonEmptyString() will simplify the following test (== 0 is then not possible).
> + if( !psz_display_mode || strlen( psz_display_mode ) == 0 || strlen( psz_display_mode ) > 4 ) {
> + msg_Err( p_demux, "Missing or invalid --decklink-mode string" );
> + ret = VLC_EGENERIC;
> + goto finish;
> + }
> + /*
> + * Pad the --decklink-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];
snprintf(sz_display_mode_padded, 5, "%-4s", psz_display_mode)
is simpler.
> + BMDDisplayMode wanted_mode_id;
> + memcpy( &wanted_mode_id, &sz_display_mode_padded, sizeof(wanted_mode_id) );
Are you sure it is actually big/little endian compatible? (Unless the SDK exists
only for little endian hardware)
> + video_fmt.video.i_sar_num = 1;
> + video_fmt.video.i_sar_den = 1;
> + psz_aspect = var_CreateGetNonEmptyString( p_demux, "decklink-aspect-ratio" );
> + if( psz_aspect )
> + {
> + char *psz_denominator = strchr( psz_aspect, ':' );
> + if( psz_denominator )
> + {
> + *psz_denominator++ = '\0';
> + video_fmt.video.i_sar_num = atoi( psz_aspect ) * video_fmt.video.i_height;
> + video_fmt.video.i_sar_den = atoi( psz_denominator ) * video_fmt.video.i_width;
> + }
> + free( psz_aspect );
> + }
Use var_InheritURational() (for an example, you can look at modules/access/imem.c).
> +
> + /* Update default_pts to a suitable value for access */
> + var_Create( p_demux, "decklink-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
You don't need to create it. A var_InheritInteger at the right place will
work.
Regards,
--
fenrir
More information about the vlc-devel
mailing list