int write_token(token *buf) { open_card(); memcpy(data, buf, LEN); close_card(); return 1; }
int read_token(token *buf) { open_card(); memcpy(buf, data, LEN); close_card(); return 1; }
static void close_thread( void *handle ) { struct decklink_status *status = (decklink_status *)handle; if( status->decklink_opts ) { close_card( status->decklink_opts ); free( status->decklink_opts ); } free( status->input ); }
void shutdown_alsa_module(void) { ALSA_CARD *card, *next; card = cards; if (card == NULL) return; log_msg("ALSA: close all cards\n"); while (card != NULL) { next = card->link; close_card(card); card = next; } cards = NULL; return; }
static SCM close_card_scm(SCM smob) { ALSA_CARD *card; card = (ALSA_CARD *)((SOURCE_HANDLE *)SCM_SMOB_DATA(smob))->body; close_card(card); return SCM_UNSPECIFIED; }
static SCM open_card(SCM device) { // soundcard acquisition and configuration char *dname; snd_pcm_t *handle; ALSA_CARD *card; snd_pcm_sw_params_t *sparams; SOURCE_HANDLE *src; snd_pcm_format_t f, format; SCM smob; int i, ret, dir; unsigned int rate, buffer_time; dname = scm_to_locale_string(device); for (i = 0; i < 10; i++) { ret = snd_pcm_open(&handle, dname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (ret >= 0) break; } if (ret < 0) { log_msg("ALSA: can't open %s (%s)\n", dname, snd_strerror(ret)); free(dname); return SCM_BOOL_F; } card = (ALSA_CARD *)my_malloc(sizeof(ALSA_CARD), "alsa card"); card->device = dname; card->name = card_name(card->device); card->ringbuf = NULL; init_source(&card->base); card->handle = handle; card->running = 0; snd_pcm_hw_params_malloc(&card->hparams); snd_pcm_hw_params_any(card->handle, card->hparams); for (f = format = 0; f < SND_PCM_FORMAT_LAST; f++) { ret = snd_pcm_hw_params_test_format(card->handle, card->hparams, f); if (ret == 0) { log_msg("ALSA: - %s\n", snd_pcm_format_name(f)); format = f; } } ret = snd_pcm_hw_params_set_access(card->handle, card->hparams, SND_PCM_ACCESS_RW_INTERLEAVED); if (ret < 0) log_msg("ALSA: access %s\n", snd_strerror(ret)); ret = snd_pcm_hw_params_set_format(card->handle, card->hparams, format); log_msg("ALSA: format: %s\n", snd_pcm_format_description(format)); if (ret < 0) log_msg("ALSA: format error %s\n", snd_strerror(ret)); snd_pcm_hw_params_get_buffer_time_max(card->hparams, &buffer_time, 0); rate = sampling_rate; ret = snd_pcm_hw_params_set_rate(card->handle, card->hparams, rate, 0); log_msg("ALSA: rate %d\n", rate); if (ret < 0) log_msg("ALSA: rate error %s\n", snd_strerror(ret)); snd_pcm_hw_params_set_channels(card->handle, card->hparams, QMX_CHANNELS); card->blocksize = BLOCKSIZE; snd_pcm_hw_params_set_period_size(card->handle, card->hparams, card->blocksize, 0); log_msg("ALSA: period %ld\n", card->blocksize); snd_pcm_hw_params_set_buffer_time_near(card->handle, card->hparams, &buffer_time, &dir); log_msg("ALSA: buffer time %u\n", buffer_time); if ((ret = snd_pcm_hw_params(card->handle, card->hparams)) < 0) { log_msg("ALSA: can't set hardware: %s\n", snd_strerror(ret)); close_card(card); return SCM_BOOL_F; } else log_msg("ALSA: hardware configd\n"); snd_pcm_sw_params_malloc(&sparams); snd_pcm_sw_params_current(card->handle, sparams); snd_pcm_sw_params_set_avail_min(card->handle, sparams, card->blocksize); snd_pcm_sw_params_set_start_threshold(card->handle, sparams, 0U); if ((ret = snd_pcm_sw_params(card->handle, sparams)) < 0) { log_msg("ALSA: can't set software: %s\n", snd_strerror(ret)); } else log_msg("ALSA: software configd\n"); snd_pcm_sw_params_free(sparams); card->link = cards; cards = card; src = (SOURCE_HANDLE *)my_gc_malloc(sizeof(SOURCE_HANDLE), "alsa_card", "alsa card handle"); src->body = (void *)card; src->src = &card->base; log_msg("ALSA: opened %s '%s'\n", card->device, card->name); SCM_NEWSMOB(smob, alsa_card_tag, src); return smob; }
static void *probe_stream( void *ptr ) { obe_input_probe_t *probe_ctx = (obe_input_probe_t*)ptr; obe_t *h = probe_ctx->h; obe_input_t *user_opts = &probe_ctx->user_opts; obe_device_t *device; obe_int_input_stream_t *streams[MAX_STREAMS]; int cur_stream = 2; obe_sdi_non_display_data_t *non_display_parser; decklink_ctx_t *decklink_ctx; decklink_opts_t *decklink_opts = (decklink_opts_t*)calloc( 1, sizeof(*decklink_opts) ); if( !decklink_opts ) { fprintf( stderr, "Malloc failed\n" ); goto finish; } non_display_parser = &decklink_opts->decklink_ctx.non_display_parser; /* TODO: support multi-channel */ decklink_opts->num_channels = 16; decklink_opts->card_idx = user_opts->card_idx; decklink_opts->video_conn = user_opts->video_connection; decklink_opts->audio_conn = user_opts->audio_connection; decklink_opts->video_format = user_opts->video_format; decklink_opts->probe = non_display_parser->probe = 1; decklink_ctx = &decklink_opts->decklink_ctx; decklink_ctx->h = h; decklink_ctx->last_frame_time = -1; decklink_ctx = &decklink_opts->decklink_ctx; if( open_card( decklink_opts ) < 0 ) goto finish; sleep( 1 ); close_card( decklink_opts ); if( !decklink_opts->probe_success ) { fprintf( stderr, "[decklink] No valid frames received - check input format\n" ); goto finish; } /* TODO: probe for SMPTE 337M */ /* TODO: factor some of the code below out */ for( int i = 0; i < 2; i++ ) { streams[i] = (obe_int_input_stream_t*)calloc( 1, sizeof(*streams[i]) ); if( !streams[i] ) goto finish; /* TODO: make it take a continuous set of stream-ids */ pthread_mutex_lock( &h->device_list_mutex ); streams[i]->input_stream_id = h->cur_input_stream_id++; pthread_mutex_unlock( &h->device_list_mutex ); if( i == 0 ) { streams[i]->stream_type = STREAM_TYPE_VIDEO; streams[i]->stream_format = VIDEO_UNCOMPRESSED; streams[i]->width = decklink_opts->width; streams[i]->height = decklink_opts->height; streams[i]->timebase_num = decklink_opts->timebase_num; streams[i]->timebase_den = decklink_opts->timebase_den; streams[i]->csp = PIX_FMT_YUV422P10; streams[i]->interlaced = decklink_opts->interlaced; streams[i]->tff = decklink_opts->tff; streams[i]->sar_num = streams[i]->sar_den = 1; /* The user can choose this when encoding */ if( add_non_display_services( non_display_parser, streams[i], USER_DATA_LOCATION_FRAME ) < 0 ) goto finish; } else if( i == 1 ) { streams[i]->stream_type = STREAM_TYPE_AUDIO; streams[i]->stream_format = AUDIO_PCM; streams[i]->num_channels = 16; streams[i]->sample_format = AV_SAMPLE_FMT_S32P; /* TODO: support other sample rates */ streams[i]->sample_rate = 48000; } } if( non_display_parser->has_vbi_frame ) { streams[cur_stream] = (obe_int_input_stream_t*)calloc( 1, sizeof(*streams[cur_stream]) ); if( !streams[cur_stream] ) goto finish; pthread_mutex_lock( &h->device_list_mutex ); streams[cur_stream]->input_stream_id = h->cur_input_stream_id++; pthread_mutex_unlock( &h->device_list_mutex ); streams[cur_stream]->stream_type = STREAM_TYPE_MISC; streams[cur_stream]->stream_format = VBI_RAW; streams[cur_stream]->vbi_ntsc = decklink_opts->video_format == INPUT_VIDEO_FORMAT_NTSC; if( add_non_display_services( non_display_parser, streams[cur_stream], USER_DATA_LOCATION_DVB_STREAM ) < 0 ) goto finish; cur_stream++; } if( non_display_parser->has_ttx_frame ) { streams[cur_stream] = (obe_int_input_stream_t*)calloc( 1, sizeof(*streams[cur_stream]) ); if( !streams[cur_stream] ) goto finish; pthread_mutex_lock( &h->device_list_mutex ); streams[cur_stream]->input_stream_id = h->cur_input_stream_id++; pthread_mutex_unlock( &h->device_list_mutex ); streams[cur_stream]->stream_type = STREAM_TYPE_MISC; streams[cur_stream]->stream_format = MISC_TELETEXT; if( add_teletext_service( non_display_parser, streams[cur_stream] ) < 0 ) goto finish; cur_stream++; } if( non_display_parser->num_frame_data ) free( non_display_parser->frame_data ); device = new_device(); if( !device ) goto finish; device->num_input_streams = cur_stream; memcpy( device->streams, streams, device->num_input_streams * sizeof(obe_int_input_stream_t**) ); device->device_type = INPUT_DEVICE_DECKLINK; memcpy( &device->user_opts, user_opts, sizeof(*user_opts) ); /* add device */ add_device( h, device ); finish: if( decklink_opts ) free( decklink_opts ); free( probe_ctx ); return NULL; }
static int open_card( decklink_opts_t *decklink_opts ) { decklink_ctx_t *decklink_ctx = &decklink_opts->decklink_ctx; int found_mode; int ret = 0; int i; int cpu_flags; const int sample_rate = 48000; const char *model_name; BMDDisplayMode wanted_mode_id; IDeckLinkDisplayModeIterator *p_display_iterator = NULL; IDeckLinkIterator *decklink_iterator = NULL; HRESULT result; avcodec_register_all(); decklink_ctx->dec = avcodec_find_decoder( AV_CODEC_ID_V210 ); if( !decklink_ctx->dec ) { fprintf( stderr, "[decklink] Could not find v210 decoder\n" ); goto finish; } decklink_ctx->codec = avcodec_alloc_context3( decklink_ctx->dec ); if( !decklink_ctx->codec ) { fprintf( stderr, "[decklink] Could not allocate AVCodecContext\n" ); goto finish; } decklink_ctx->codec->get_buffer = obe_get_buffer; decklink_ctx->codec->release_buffer = obe_release_buffer; decklink_ctx->codec->reget_buffer = obe_reget_buffer; decklink_ctx->codec->flags |= CODEC_FLAG_EMU_EDGE; /* TODO: setup custom strides */ if( avcodec_open2( decklink_ctx->codec, decklink_ctx->dec, NULL ) < 0 ) { fprintf( stderr, "[decklink] Could not open libavcodec\n" ); goto finish; } decklink_iterator = CreateDeckLinkIteratorInstance(); if( !decklink_iterator ) { fprintf( stderr, "[decklink] DeckLink drivers not found\n" ); ret = -1; goto finish; } if( decklink_opts->card_idx < 0 ) { fprintf( stderr, "[decklink] Invalid card index %d \n", decklink_opts->card_idx ); ret = -1; goto finish; } for( i = 0; i <= decklink_opts->card_idx; ++i ) { if( decklink_ctx->p_card ) decklink_ctx->p_card->Release(); result = decklink_iterator->Next( &decklink_ctx->p_card ); if( result != S_OK ) break; } if( result != S_OK ) { fprintf( stderr, "[decklink] DeckLink PCI card %d not found\n", decklink_opts->card_idx ); ret = -1; goto finish; } result = decklink_ctx->p_card->GetModelName( &model_name ); if( result != S_OK ) { fprintf( stderr, "[decklink] Could not get model name\n" ); ret = -1; goto finish; } syslog( LOG_INFO, "Opened DeckLink PCI card %d (%s)", decklink_opts->card_idx, model_name ); if( decklink_ctx->p_card->QueryInterface( IID_IDeckLinkInput, (void**)&decklink_ctx->p_input) != S_OK ) { fprintf( stderr, "[decklink] Card has no inputs\n" ); ret = -1; goto finish; } /* Set up the video and audio sources. */ if( decklink_ctx->p_card->QueryInterface( IID_IDeckLinkConfiguration, (void**)&decklink_ctx->p_config) != S_OK ) { fprintf( stderr, "[decklink] Failed to get configuration interface\n" ); ret = -1; goto finish; } /* Setup video connection */ for( i = 0; video_conn_tab[i].obe_name != -1; i++ ) { if( video_conn_tab[i].obe_name == decklink_opts->video_conn ) break; } if( video_conn_tab[i].obe_name == -1 ) { fprintf( stderr, "[decklink] Unsupported video input connection\n" ); ret = -1; goto finish; } result = decklink_ctx->p_config->SetInt( bmdDeckLinkConfigVideoInputConnection, video_conn_tab[i].bmd_name ); if( result != S_OK ) { fprintf( stderr, "[decklink] Failed to set video input connection\n" ); ret = -1; goto finish; } /* Setup audio connection */ for( i = 0; audio_conn_tab[i].obe_name != -1; i++ ) { if( audio_conn_tab[i].obe_name == decklink_opts->audio_conn ) break; } if( audio_conn_tab[i].obe_name == -1 ) { fprintf( stderr, "[decklink] Unsupported audio input connection\n" ); ret = -1; goto finish; } result = decklink_ctx->p_config->SetInt( bmdDeckLinkConfigAudioInputConnection, audio_conn_tab[i].bmd_name ); if( result != S_OK ) { fprintf( stderr, "[decklink] Failed to set audio input connection\n" ); ret = -1; goto finish; } /* Get the list of display modes. */ result = decklink_ctx->p_input->GetDisplayModeIterator( &p_display_iterator ); if( result != S_OK ) { fprintf( stderr, "[decklink] Failed to enumerate display modes\n" ); ret = -1; goto finish; } for( i = 0; video_format_tab[i].obe_name != -1; i++ ) { if( video_format_tab[i].obe_name == decklink_opts->video_format ) break; } if( video_format_tab[i].obe_name == -1 ) { fprintf( stderr, "[decklink] Unsupported video format\n" ); ret = -1; goto finish; } wanted_mode_id = video_format_tab[i].bmd_name; found_mode = false; decklink_opts->timebase_num = video_format_tab[i].timebase_num; decklink_opts->timebase_den = video_format_tab[i].timebase_den; for (;;) { IDeckLinkDisplayMode *p_display_mode; result = p_display_iterator->Next( &p_display_mode ); if( result != S_OK || !p_display_mode ) break; BMDDisplayMode mode_id = p_display_mode->GetDisplayMode(); BMDTimeValue frame_duration, time_scale; result = p_display_mode->GetFrameRate( &frame_duration, &time_scale ); if( result != S_OK ) { fprintf( stderr, "[decklink] Failed to get frame rate\n" ); ret = -1; p_display_mode->Release(); goto finish; } if( wanted_mode_id == mode_id ) { found_mode = true; decklink_opts->width = p_display_mode->GetWidth(); decklink_opts->coded_height = p_display_mode->GetHeight(); switch( p_display_mode->GetFieldDominance() ) { case bmdProgressiveFrame: decklink_opts->interlaced = 0; decklink_opts->tff = 0; break; case bmdProgressiveSegmentedFrame: /* Assume tff interlaced - this mode should not be used in broadcast */ decklink_opts->interlaced = 1; decklink_opts->tff = 1; break; case bmdUpperFieldFirst: decklink_opts->interlaced = 1; decklink_opts->tff = 1; break; case bmdLowerFieldFirst: decklink_opts->interlaced = 1; decklink_opts->tff = 0; break; case bmdUnknownFieldDominance: default: /* Assume progressive */ decklink_opts->interlaced = 0; decklink_opts->tff = 0; break; } } p_display_mode->Release(); } decklink_opts->height = decklink_opts->coded_height; if( decklink_opts->coded_height == 486 ) decklink_opts->height = 480; if( !found_mode ) { fprintf( stderr, "[decklink] Unsupported video mode\n" ); ret = -1; goto finish; } cpu_flags = av_get_cpu_flags(); /* Setup VBI and VANC unpack functions */ if( IS_SD( decklink_opts->video_format ) ) { decklink_ctx->unpack_line = obe_v210_line_to_uyvy_c; decklink_ctx->downscale_line = obe_downscale_line_c; decklink_ctx->blank_line = obe_blank_line_uyvy_c; if( cpu_flags & AV_CPU_FLAG_MMX ) decklink_ctx->downscale_line = obe_downscale_line_mmx; if( cpu_flags & AV_CPU_FLAG_SSE2 ) decklink_ctx->downscale_line = obe_downscale_line_sse2; } else { decklink_ctx->unpack_line = obe_v210_line_to_nv20_c; decklink_ctx->blank_line = obe_blank_line_nv20_c; } result = decklink_ctx->p_input->EnableVideoInput( wanted_mode_id, bmdFormat10BitYUV, 0 ); if( result != S_OK ) { fprintf( stderr, "[decklink] Failed to enable video input\n" ); ret = -1; goto finish; } /* Set up audio. */ result = decklink_ctx->p_input->EnableAudioInput( sample_rate, bmdAudioSampleType32bitInteger, decklink_opts->num_channels ); if( result != S_OK ) { fprintf( stderr, "[decklink] Failed to enable audio input\n" ); ret = -1; goto finish; } if( !decklink_opts->probe ) { decklink_ctx->avr = avresample_alloc_context(); if( !decklink_ctx->avr ) { fprintf( stderr, "[decklink-sdiaudio] couldn't setup sample rate conversion \n" ); ret = -1; goto finish; } /* Give libavresample a made up channel map */ av_opt_set_int( decklink_ctx->avr, "in_channel_layout", (1 << decklink_opts->num_channels) - 1, 0 ); av_opt_set_int( decklink_ctx->avr, "in_sample_fmt", AV_SAMPLE_FMT_S32, 0 ); av_opt_set_int( decklink_ctx->avr, "in_sample_rate", 48000, 0 ); av_opt_set_int( decklink_ctx->avr, "out_channel_layout", (1 << decklink_opts->num_channels) - 1, 0 ); av_opt_set_int( decklink_ctx->avr, "out_sample_fmt", AV_SAMPLE_FMT_S32P, 0 ); if( avresample_open( decklink_ctx->avr ) < 0 ) { fprintf( stderr, "Could not open AVResample\n" ); goto finish; } } decklink_ctx->p_delegate = new DeckLinkCaptureDelegate( decklink_opts ); decklink_ctx->p_input->SetCallback( decklink_ctx->p_delegate ); result = decklink_ctx->p_input->StartStreams(); if( result != S_OK ) { fprintf( stderr, "[decklink] Could not start streaming from card\n" ); ret = -1; goto finish; } ret = 0; finish: if( decklink_iterator ) decklink_iterator->Release(); if( p_display_iterator ) p_display_iterator->Release(); if( ret ) close_card( decklink_opts ); return ret; }