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

Laurent Aimar fenrir at elivagar.org
Sat Oct 2 16:15:01 CEST 2010


Hi,

On Sat, Oct 02, 2010 at 02:11:59AM +0200, Steinar H. Gunderson wrote:
> Do I need to send a signal to reset the PCR, though? I see some comment says
> it, but lots of modules don't follow it...
 ES_OUT_RESET_PCR should be used when the relation between the local clock (mdate())
and the value you return as PCR is changed. In your module, it doesn't seems to me
that it can happen.
 BLOCK_FLAG_DISCONTINUITY signals a discontinuity in PTS/DTS or in data for a
elementary streams. I don't think you need it in your module.

> >> +    if( audioFrame )
> >> +    {
> >> +        const int i_bytes = audioFrame->GetSampleFrameCount() * sizeof(int16_t) * p_sys->i_channels;
> >  Using audioFrame->GetBytes() seems simpler.
> 
> GetBytes() returns a pointer to the buffer. It does not return the number of
> bytes.
 Ah sorry.

> >> +        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.
> 
> Hm. Why is dc1394.c written in this way, then? (In general, what kind of
> restrictions are there on which threads I can send stuff from?)
 Historical reasons, the mode I advised you to use is really recent. One of
the module using it is modules/access/rtp/

> How quickly will sending the data return -- can it block? I'm not sure what
> demands there are on this callback; I am in the driver's thread, after all.
 In this mode, it should be fast, basically es_out_* will only push in the
decoder fifo and updates a few informations/statistics.

 Could you give it a try and see if you don't have any issues ?
 
> >> +    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).
> 
> Again, this is copied from other sources, but sure, changed.
 I know, recent addition too.


> +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;
 Not needed anymore.
> +    int i_last_pts;
> +
> +    uint32_t i_dominance_flags;
> +    int 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> */
> +};

> +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;
> +        }
 Why ? What happens when no signal is detected ?
 If the card emulates empty audio *and* video, then I think it would be better
to let them pass, this will allow the sout to properly work.


> +static int Open( vlc_object_t *p_this )
> +{
> +    demux_t     *p_demux = (demux_t*)p_this;
> +    demux_sys_t *p_sys;
> +    int         ret = VLC_EGENERIC;
> +    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;
> +    int         i_width, i_height, i_fps_num, i_fps_den;
> +    int         i_rate;
> +    unsigned    u_aspect_num, u_aspect_den;
> +
> +    /* 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." );
> +        goto finish;
> +    }
> +
> +    HRESULT result;
> +
> +    i_card_index = var_InheritInteger( p_demux, "decklink-card-index" );
> +    if( i_card_index < 0 )
> +    {
> +        msg_Err( p_demux, "Invalid card index %d", i_card_index );
> +        goto finish;
> +    }
> +
> +    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;
> +    }
> +
> +    if( result != S_OK )
> +    {
> +        msg_Err( p_demux, "DeckLink PCI card %d not found", i_card_index );
> +        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" );
> +        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" );
> +        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" );
> +        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." );
> +            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" );
> +            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." );
> +            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" );
> +            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" );
> +        goto finish;
> +    }
> +
> +    psz_display_mode = var_CreateGetNonEmptyString( p_demux, "decklink-mode" );
> +    if( !psz_display_mode || strlen( psz_display_mode ) > 4 ) {
 The { placement is inconsistant.
> +        msg_Err( p_demux, "Missing or invalid --decklink-mode string" );
> +        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];
 Did you dislike the snprintf solution ?

> +    /* Declare elementary streams */
> +    es_format_t video_fmt;
> +    es_format_Init( &video_fmt, VIDEO_ES, VLC_CODEC_UYVY );
> +    video_fmt.video.i_width = i_width;
> +    video_fmt.video.i_height = i_height;
> +    video_fmt.video.i_sar_num = 1;
> +    video_fmt.video.i_sar_den = 1;
> +    video_fmt.video.i_frame_rate = i_fps_num;
> +    video_fmt.video.i_frame_rate_base = 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;
> +
> +    if ( !var_InheritURational( p_demux, &u_aspect_num, &u_aspect_den, "decklink-aspect-ratio" ) &&
> +         u_aspect_num > 0 && u_aspect_den > 0 ) {
 The { placement is inconsistant.

> +        video_fmt.video.i_sar_num = u_aspect_num * video_fmt.video.i_height;
> +        video_fmt.video.i_sar_den = u_aspect_den * video_fmt.video.i_width;
> +    }
> +
> +    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 = 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 );
> +
> +    ret = VLC_SUCCESS;
> +
> +finish:
> +    if( decklink_iterator )
> +        decklink_iterator->Release();
> +
> +    if( p_config )
> +        p_config->Release();
> +
> +    free( psz_video_connection );
> +    free( psz_audio_connection );
> +    free( psz_display_mode );
> +
> +    if( p_display_iterator )
> +        p_display_iterator->Release();
> +
> +    if( ret != VLC_SUCCESS )
> +        Close( p_this );
> +
> +    return ret;
> +}

> +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_InheritInteger( p_demux, "decklink-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 */
 In fact, I don't think you have anymore controls to implement for this module.
> +        default:
> +            return VLC_EGENERIC;
> +    }
> +
> +    return VLC_EGENERIC;
> +}

Regards,

-- 
fenrir




More information about the vlc-devel mailing list