From 995c7a6f5ad480d706b22e97609e2cce2f305e4b Mon Sep 17 00:00:00 2001 From: Marton Balint Date: Tue, 12 Jan 2016 03:09:37 +0100 Subject: [PATCH] lavd/decklink_dec: add support for teletext It uses the libzvbi slicer, therefore teletext capture requires libzvbi. Reviewed-by: Deti Fliegl Signed-off-by: Marton Balint --- doc/indevs.texi | 10 ++++ libavdevice/decklink_common.h | 2 + libavdevice/decklink_common_c.h | 1 + libavdevice/decklink_dec.cpp | 99 +++++++++++++++++++++++++++++++++ libavdevice/decklink_dec_c.c | 3 + libavdevice/version.h | 2 +- 6 files changed, 116 insertions(+), 1 deletion(-) diff --git a/doc/indevs.texi b/doc/indevs.texi index 6cf626d104..a3ed0e7405 100644 --- a/doc/indevs.texi +++ b/doc/indevs.texi @@ -236,6 +236,16 @@ Defaults to @option{false}. If set to @samp{1}, video is captured in 10 bit v210 instead of uyvy422. Not all Blackmagic devices support this option. +@item teletext_lines +If set to nonzero, an additional teletext stream will be captured from the +vertical ancillary data. This option is a bitmask of the VBI lines checked, +specifically lines 6 to 22, and lines 318 to 335. Line 6 is the LSB in the mask. +Selected lines which do not contain teletext information will be ignored. You +can use the special @option{all} constant to select all possible lines, or +@option{standard} to skip lines 6, 318 and 319, which are not compatible with all +receivers. Capturing teletext only works for SD PAL sources in 8 bit mode. +To use this option, ffmpeg needs to be compiled with @code{--enable-libzvbi}. + @end table @subsection Examples diff --git a/libavdevice/decklink_common.h b/libavdevice/decklink_common.h index 3bc30f062c..076c62506b 100644 --- a/libavdevice/decklink_common.h +++ b/libavdevice/decklink_common.h @@ -67,10 +67,12 @@ struct decklink_ctx { unsigned int dropped; AVStream *audio_st; AVStream *video_st; + AVStream *teletext_st; /* Options */ int list_devices; int list_formats; + int64_t teletext_lines; double preroll; int frames_preroll; diff --git a/libavdevice/decklink_common_c.h b/libavdevice/decklink_common_c.h index fb2b788628..2b1a579a3a 100644 --- a/libavdevice/decklink_common_c.h +++ b/libavdevice/decklink_common_c.h @@ -27,6 +27,7 @@ struct decklink_cctx { /* Options */ int list_devices; int list_formats; + int64_t teletext_lines; double preroll; int v210; }; diff --git a/libavdevice/decklink_dec.cpp b/libavdevice/decklink_dec.cpp index 6c5bc5dbed..9a721c9ffa 100644 --- a/libavdevice/decklink_dec.cpp +++ b/libavdevice/decklink_dec.cpp @@ -25,14 +25,47 @@ #include extern "C" { +#include "config.h" #include "libavformat/avformat.h" #include "libavformat/internal.h" #include "libavutil/imgutils.h" +#if CONFIG_LIBZVBI +#include +#endif } #include "decklink_common.h" #include "decklink_dec.h" +#if CONFIG_LIBZVBI +static uint8_t calc_parity_and_line_offset(int line) +{ + uint8_t ret = (line < 313) << 5; + if (line >= 7 && line <= 22) + ret += line; + if (line >= 320 && line <= 335) + ret += (line - 313); + return ret; +} + +int teletext_data_unit_from_vbi_data(int line, uint8_t *src, uint8_t *tgt) +{ + vbi_bit_slicer slicer; + + vbi_bit_slicer_init(&slicer, 720, 13500000, 6937500, 6937500, 0x00aaaae4, 0xffff, 18, 6, 42 * 8, VBI_MODULATION_NRZ_MSB, VBI_PIXFMT_UYVY); + + if (vbi_bit_slice(&slicer, src, tgt + 4) == FALSE) + return -1; + + tgt[0] = 0x02; // data_unit_id + tgt[1] = 0x2c; // data_unit_length + tgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offset + tgt[3] = 0xe4; // framing code + + return 0; +} +#endif + static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q) { memset(q, 0, sizeof(AVPacketQueue)); @@ -277,6 +310,50 @@ HRESULT decklink_input_callback::VideoInputFrameArrived( pkt.size = videoFrame->GetRowBytes() * videoFrame->GetHeight(); //fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts); + +#if CONFIG_LIBZVBI + if (!no_video && ctx->teletext_lines && videoFrame->GetPixelFormat() == bmdFormat8BitYUV && videoFrame->GetWidth() == 720) { + IDeckLinkVideoFrameAncillary *vanc; + AVPacket txt_pkt; + uint8_t txt_buf0[1611]; // max 35 * 46 bytes decoded teletext lines + 1 byte data_identifier + uint8_t *txt_buf = txt_buf0; + + if (videoFrame->GetAncillaryData(&vanc) == S_OK) { + int i; + int64_t line_mask = 1; + txt_buf[0] = 0x10; // data_identifier - EBU_data + txt_buf++; + for (i = 6; i < 336; i++, line_mask <<= 1) { + uint8_t *buf; + if ((ctx->teletext_lines & line_mask) && vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) { + if (teletext_data_unit_from_vbi_data(i, buf, txt_buf) >= 0) + txt_buf += 46; + } + if (i == 22) + i = 317; + } + vanc->Release(); + if (txt_buf - txt_buf0 > 1) { + int stuffing_units = (4 - ((45 + txt_buf - txt_buf0) / 46) % 4) % 4; + while (stuffing_units--) { + memset(txt_buf, 0xff, 46); + txt_buf[1] = 0x2c; // data_unit_length + txt_buf += 46; + } + av_init_packet(&txt_pkt); + txt_pkt.pts = pkt.pts; + txt_pkt.dts = pkt.dts; + txt_pkt.stream_index = ctx->teletext_st->index; + txt_pkt.data = txt_buf0; + txt_pkt.size = txt_buf - txt_buf0; + if (avpacket_queue_put(&ctx->queue, &txt_pkt) < 0) { + ++ctx->dropped; + } + } + } + } +#endif + c->frame_number++; if (avpacket_queue_put(&ctx->queue, &pkt) < 0) { ++ctx->dropped; @@ -378,9 +455,17 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) return AVERROR(ENOMEM); ctx->list_devices = cctx->list_devices; ctx->list_formats = cctx->list_formats; + ctx->teletext_lines = cctx->teletext_lines; ctx->preroll = cctx->preroll; cctx->ctx = ctx; +#if !CONFIG_LIBZVBI + if (ctx->teletext_lines) { + av_log(avctx, AV_LOG_ERROR, "Libzvbi support is needed for capturing teletext, please recompile FFmpeg.\n"); + return AVERROR(ENOSYS); + } +#endif + iter = CreateDeckLinkIteratorInstance(); if (!iter) { av_log(avctx, AV_LOG_ERROR, "Could not create DeckLink iterator\n"); @@ -488,6 +573,20 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx) ctx->video_st=st; + if (ctx->teletext_lines) { + st = avformat_new_stream(avctx, NULL); + if (!st) { + av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n"); + goto error; + } + st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE; + st->codec->time_base.den = ctx->bmd_tb_den; + st->codec->time_base.num = ctx->bmd_tb_num; + st->codec->codec_id = AV_CODEC_ID_DVB_TELETEXT; + avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ + ctx->teletext_st = st; + } + result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2); if (result != S_OK) { diff --git a/libavdevice/decklink_dec_c.c b/libavdevice/decklink_dec_c.c index b1a65e6877..e5d6bc040f 100644 --- a/libavdevice/decklink_dec_c.c +++ b/libavdevice/decklink_dec_c.c @@ -32,6 +32,9 @@ static const AVOption options[] = { { "list_devices", "list available devices" , OFFSET(list_devices), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, { "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, { "bm_v210", "v210 10 bit per channel" , OFFSET(v210), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC }, + { "teletext_lines", "teletext lines bitmask", OFFSET(teletext_lines), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 0x7ffffffffLL, DEC, "teletext_lines"}, + { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, "teletext_lines"}, + { "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"}, { NULL }, }; diff --git a/libavdevice/version.h b/libavdevice/version.h index 45c9e8d4db..b226a76120 100644 --- a/libavdevice/version.h +++ b/libavdevice/version.h @@ -29,7 +29,7 @@ #define LIBAVDEVICE_VERSION_MAJOR 57 #define LIBAVDEVICE_VERSION_MINOR 0 -#define LIBAVDEVICE_VERSION_MICRO 100 +#define LIBAVDEVICE_VERSION_MICRO 101 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \ LIBAVDEVICE_VERSION_MINOR, \