vbi_capture * vbi_capture_bktr_new (const char * dev_name, int scanning, unsigned int * services, int strict, char ** errstr, vbi_bool trace) { char *error = NULL; char *driver_name = _("BKTR driver"); vbi_capture_bktr *v; pthread_once (&vbi_init_once, vbi_init); assert(services && *services != 0); if (!errstr) errstr = &error; *errstr = NULL; printv ("Try to open bktr vbi device, " "libzvbi interface rev.\n %s\n", rcsid); if (!(v = (vbi_capture_bktr *) calloc(1, sizeof(*v)))) { asprintf(errstr, _("Virtual memory exhausted.")); errno = ENOMEM; goto failure; } vbi_raw_decoder_init (&v->dec); v->capture.parameters = bktr_parameters; v->capture._delete = bktr_delete; v->capture.get_fd = bktr_fd; v->fd = device_open (v->capture.sys_log_fp, dev_name, O_RDONLY, 0); if (-1 == v->fd) { asprintf(errstr, _("Cannot open '%s': %s."), dev_name, strerror(errno)); goto io_error; } printv("Opened %s\n", dev_name); /* * XXX * Can we somehow verify this really is /dev/vbiN (bktr) and not * /dev/hcfr (halt and catch fire on read) ? */ v->dec.bytes_per_line = 2048; v->dec.interlaced = FALSE; v->dec.synchronous = TRUE; v->dec.count[0] = 16; v->dec.count[1] = 16; switch (scanning) { default: /* fall through */ case 625: /* Not confirmed */ v->dec.scanning = 625; v->dec.sampling_rate = 35468950; v->dec.offset = (int)(10.2e-6 * 35468950); v->dec.start[0] = 22 + 1 - v->dec.count[0]; v->dec.start[1] = 335 + 1 - v->dec.count[1]; break; case 525: /* Not confirmed */ v->dec.scanning = 525; v->dec.sampling_rate = 28636363; v->dec.offset = (int)(9.2e-6 * 28636363); v->dec.start[0] = 10; v->dec.start[1] = 273; break; } v->time_per_frame = (v->dec.scanning == 625) ? 1.0 / 25 : 1001.0 / 30000; v->select = FALSE; /* XXX ? */ printv("Guessed videostandard %d\n", v->dec.scanning); v->dec.sampling_format = VBI_PIXFMT_YUV420; if (*services & ~(VBI_SLICED_VBI_525 | VBI_SLICED_VBI_625)) { *services = vbi_raw_decoder_add_services (&v->dec, *services, strict); if (*services == 0) { asprintf(errstr, _("Sorry, %s (%s) cannot " "capture any of " "the requested data services."), dev_name, driver_name); goto failure; } v->sliced_buffer.data = malloc((v->dec.count[0] + v->dec.count[1]) * sizeof(vbi_sliced)); if (!v->sliced_buffer.data) { asprintf(errstr, _("Virtual memory exhausted.")); errno = ENOMEM; goto failure; } } printv("Will decode services 0x%08x\n", *services); /* Read mode */ if (!v->select) printv("Warning: no read select, reading will block\n"); v->capture.read = bktr_read; v->raw_buffer = calloc(1, sizeof(v->raw_buffer[0])); if (!v->raw_buffer) { asprintf(errstr, _("Virtual memory exhausted.")); errno = ENOMEM; goto failure; } v->raw_buffer[0].size = (v->dec.count[0] + v->dec.count[1]) * v->dec.bytes_per_line; v->raw_buffer[0].data = malloc(v->raw_buffer[0].size); if (!v->raw_buffer[0].data) { asprintf(errstr, _("Not enough memory to allocate " "vbi capture buffer (%d KB)."), (v->raw_buffer[0].size + 1023) >> 10); goto failure; }
HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived( IDeckLinkVideoInputFrame *videoframe, IDeckLinkAudioInputPacket *audioframe ) { decklink_ctx_t *decklink_ctx = &decklink_opts_->decklink_ctx; obe_raw_frame_t *raw_frame = NULL; AVPacket pkt; AVFrame *frame = NULL; void *frame_bytes, *anc_line; obe_t *h = decklink_ctx->h; int finished = 0, ret, num_anc_lines = 0, anc_line_stride, lines_read = 0, first_line = 0, last_line = 0, line, num_vbi_lines, vii_line; uint32_t *frame_ptr; uint16_t *anc_buf, *anc_buf_pos; uint8_t *vbi_buf; int anc_lines[DECKLINK_VANC_LINES]; IDeckLinkVideoFrameAncillary *ancillary; BMDTimeValue stream_time, frame_duration; if( decklink_opts_->probe_success ) return S_OK; av_init_packet( &pkt ); if( videoframe ) { if( videoframe->GetFlags() & bmdFrameHasNoInputSource ) { syslog( LOG_ERR, "Decklink card index %i: No input signal detected", decklink_opts_->card_idx ); return S_OK; } else if( decklink_opts_->probe ) decklink_opts_->probe_success = 1; /* use SDI ticks as clock source */ videoframe->GetStreamTime( &stream_time, &frame_duration, OBE_CLOCK ); obe_clock_tick( h, (int64_t)stream_time ); if( decklink_ctx->last_frame_time == -1 ) decklink_ctx->last_frame_time = obe_mdate(); else { int64_t cur_frame_time = obe_mdate(); if( cur_frame_time - decklink_ctx->last_frame_time >= SDI_MAX_DELAY ) { syslog( LOG_WARNING, "Decklink card index %i: No frame received for %"PRIi64" ms", decklink_opts_->card_idx, (cur_frame_time - decklink_ctx->last_frame_time) / 1000 ); pthread_mutex_lock( &h->drop_mutex ); h->encoder_drop = h->mux_drop = 1; pthread_mutex_unlock( &h->drop_mutex ); } decklink_ctx->last_frame_time = cur_frame_time; } const int width = videoframe->GetWidth(); const int height = videoframe->GetHeight(); const int stride = videoframe->GetRowBytes(); videoframe->GetBytes( &frame_bytes ); /* TODO: support format switching (rare in SDI) */ int j; for( j = 0; first_active_line[j].format != -1; j++ ) { if( decklink_opts_->video_format == first_active_line[j].format ) break; } videoframe->GetAncillaryData( &ancillary ); /* NTSC starts on line 4 */ line = decklink_opts_->video_format == INPUT_VIDEO_FORMAT_NTSC ? 4 : 1; anc_line_stride = FFALIGN( (width * 2 * sizeof(uint16_t)), 16 ); /* Overallocate slightly for VANC buffer * Some VBI services stray into the active picture so allocate some extra space */ anc_buf = anc_buf_pos = (uint16_t*)av_malloc( DECKLINK_VANC_LINES * anc_line_stride ); if( !anc_buf ) { syslog( LOG_ERR, "Malloc failed\n" ); goto end; } while( 1 ) { /* Some cards have restrictions on what lines can be accessed so try them all * Some buggy decklink cards will randomly refuse access to a particular line so * work around this issue by blanking the line */ if( ancillary->GetBufferForVerticalBlankingLine( line, &anc_line ) == S_OK ) decklink_ctx->unpack_line( (uint32_t*)anc_line, anc_buf_pos, width ); else decklink_ctx->blank_line( anc_buf_pos, width ); anc_buf_pos += anc_line_stride / 2; anc_lines[num_anc_lines++] = line; if( !first_line ) first_line = line; last_line = line; lines_read++; line = sdi_next_line( decklink_opts_->video_format, line ); if( line == first_active_line[j].line ) break; } ancillary->Release(); if( !decklink_opts_->probe ) { raw_frame = new_raw_frame(); if( !raw_frame ) { syslog( LOG_ERR, "Malloc failed\n" ); goto end; } } anc_buf_pos = anc_buf; for( int i = 0; i < num_anc_lines; i++ ) { parse_vanc_line( h, &decklink_ctx->non_display_parser, raw_frame, anc_buf_pos, width, anc_lines[i] ); anc_buf_pos += anc_line_stride / 2; } if( IS_SD( decklink_opts_->video_format ) && first_line != last_line ) { /* Add a some VBI lines to the ancillary buffer */ frame_ptr = (uint32_t*)frame_bytes; /* NTSC starts from line 283 so add an extra line */ num_vbi_lines = NUM_ACTIVE_VBI_LINES + ( decklink_opts_->video_format == INPUT_VIDEO_FORMAT_NTSC ); for( int i = 0; i < num_vbi_lines; i++ ) { decklink_ctx->unpack_line( frame_ptr, anc_buf_pos, width ); anc_buf_pos += anc_line_stride / 2; frame_ptr += stride / 4; last_line = sdi_next_line( decklink_opts_->video_format, last_line ); } num_anc_lines += num_vbi_lines; vbi_buf = (uint8_t*)av_malloc( width * 2 * num_anc_lines ); if( !vbi_buf ) { syslog( LOG_ERR, "Malloc failed\n" ); goto end; } /* Scale the lines from 10-bit to 8-bit */ decklink_ctx->downscale_line( anc_buf, vbi_buf, num_anc_lines ); anc_buf_pos = anc_buf; /* Handle Video Index information */ int tmp_line = first_line; vii_line = decklink_opts_->video_format == INPUT_VIDEO_FORMAT_NTSC ? NTSC_VIDEO_INDEX_LINE : PAL_VIDEO_INDEX_LINE; while( tmp_line < vii_line ) { anc_buf_pos += anc_line_stride / 2; tmp_line++; } if( decode_video_index_information( h, &decklink_ctx->non_display_parser, anc_buf_pos, raw_frame, vii_line ) < 0 ) goto fail; if( !decklink_ctx->has_setup_vbi ) { vbi_raw_decoder_init( &decklink_ctx->non_display_parser.vbi_decoder ); decklink_ctx->non_display_parser.ntsc = decklink_opts_->video_format == INPUT_VIDEO_FORMAT_NTSC; decklink_ctx->non_display_parser.vbi_decoder.start[0] = first_line; decklink_ctx->non_display_parser.vbi_decoder.start[1] = sdi_next_line( decklink_opts_->video_format, first_line ); decklink_ctx->non_display_parser.vbi_decoder.count[0] = last_line - decklink_ctx->non_display_parser.vbi_decoder.start[1] + 1; decklink_ctx->non_display_parser.vbi_decoder.count[1] = decklink_ctx->non_display_parser.vbi_decoder.count[0]; if( setup_vbi_parser( &decklink_ctx->non_display_parser ) < 0 ) goto fail; decklink_ctx->has_setup_vbi = 1; } if( decode_vbi( h, &decklink_ctx->non_display_parser, vbi_buf, raw_frame ) < 0 ) goto fail; av_free( vbi_buf ); } av_free( anc_buf ); if( !decklink_opts_->probe ) { frame = avcodec_alloc_frame(); if( !frame ) { syslog( LOG_ERR, "[decklink]: Could not allocate video frame\n" ); goto end; } decklink_ctx->codec->width = width; decklink_ctx->codec->height = height; pkt.data = (uint8_t*)frame_bytes; pkt.size = stride * height; ret = avcodec_decode_video2( decklink_ctx->codec, frame, &finished, &pkt ); if( ret < 0 || !finished ) { syslog( LOG_ERR, "[decklink]: Could not decode video frame\n" ); goto end; } raw_frame->release_data = obe_release_video_data; raw_frame->release_frame = obe_release_frame; memcpy( raw_frame->alloc_img.stride, frame->linesize, sizeof(raw_frame->alloc_img.stride) ); memcpy( raw_frame->alloc_img.plane, frame->data, sizeof(raw_frame->alloc_img.plane) ); avcodec_free_frame( &frame ); raw_frame->alloc_img.csp = (int)decklink_ctx->codec->pix_fmt; raw_frame->alloc_img.planes = av_pix_fmt_descriptors[raw_frame->alloc_img.csp].nb_components; raw_frame->alloc_img.width = width; raw_frame->alloc_img.height = height; raw_frame->alloc_img.format = decklink_opts_->video_format; raw_frame->timebase_num = decklink_opts_->timebase_num; raw_frame->timebase_den = decklink_opts_->timebase_den; memcpy( &raw_frame->img, &raw_frame->alloc_img, sizeof(raw_frame->alloc_img) ); if( IS_SD( decklink_opts_->video_format ) ) { if( raw_frame->alloc_img.height == 486 ) raw_frame->img.height = 480; raw_frame->img.first_line = first_active_line[j].line; } /* If AFD is present and the stream is SD this will be changed in the video filter */ raw_frame->sar_width = raw_frame->sar_height = 1; raw_frame->pts = stream_time; for( int i = 0; i < decklink_ctx->device->num_input_streams; i++ ) { if( decklink_ctx->device->streams[i]->stream_format == VIDEO_UNCOMPRESSED ) raw_frame->input_stream_id = decklink_ctx->device->streams[i]->input_stream_id; } if( add_to_filter_queue( h, raw_frame ) < 0 ) goto fail; if( send_vbi_and_ttx( h, &decklink_ctx->non_display_parser, raw_frame->pts ) < 0 ) goto fail; decklink_ctx->non_display_parser.num_vbi = 0; decklink_ctx->non_display_parser.num_anc_vbi = 0; } } /* TODO: probe SMPTE 337M audio */ if( audioframe && !decklink_opts_->probe ) { audioframe->GetBytes( &frame_bytes ); raw_frame = new_raw_frame(); if( !raw_frame ) { syslog( LOG_ERR, "Malloc failed\n" ); goto end; } raw_frame->audio_frame.num_samples = audioframe->GetSampleFrameCount(); raw_frame->audio_frame.num_channels = decklink_opts_->num_channels; raw_frame->audio_frame.sample_fmt = AV_SAMPLE_FMT_S32P; if( av_samples_alloc( raw_frame->audio_frame.audio_data, &raw_frame->audio_frame.linesize, decklink_opts_->num_channels, raw_frame->audio_frame.num_samples, (AVSampleFormat)raw_frame->audio_frame.sample_fmt, 0 ) < 0 ) { syslog( LOG_ERR, "Malloc failed\n" ); return -1; } if( avresample_convert( decklink_ctx->avr, raw_frame->audio_frame.audio_data, raw_frame->audio_frame.linesize, raw_frame->audio_frame.num_samples, (uint8_t**)&frame_bytes, 0, raw_frame->audio_frame.num_samples ) < 0 ) { syslog( LOG_ERR, "[decklink] Sample format conversion failed\n" ); return -1; } BMDTimeValue packet_time; audioframe->GetPacketTime( &packet_time, OBE_CLOCK ); raw_frame->pts = packet_time; raw_frame->release_data = obe_release_audio_data; raw_frame->release_frame = obe_release_frame; for( int i = 0; i < decklink_ctx->device->num_input_streams; i++ ) { if( decklink_ctx->device->streams[i]->stream_format == AUDIO_PCM ) raw_frame->input_stream_id = decklink_ctx->device->streams[i]->input_stream_id; } if( add_to_filter_queue( decklink_ctx->h, raw_frame ) < 0 ) goto fail; } end: if( frame ) avcodec_free_frame( &frame ); av_free_packet( &pkt ); return S_OK; fail: if( raw_frame ) { raw_frame->release_data( raw_frame ); raw_frame->release_frame( raw_frame ); } return S_OK; }