Skip to content

Commit 995c7a6

Browse files
committed
lavd/decklink_dec: add support for teletext
It uses the libzvbi slicer, therefore teletext capture requires libzvbi. Reviewed-by: Deti Fliegl <[email protected]> Signed-off-by: Marton Balint <[email protected]>
1 parent 98e94df commit 995c7a6

File tree

6 files changed

+116
-1
lines changed

6 files changed

+116
-1
lines changed

doc/indevs.texi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,16 @@ Defaults to @option{false}.
236236
If set to @samp{1}, video is captured in 10 bit v210 instead
237237
of uyvy422. Not all Blackmagic devices support this option.
238238

239+
@item teletext_lines
240+
If set to nonzero, an additional teletext stream will be captured from the
241+
vertical ancillary data. This option is a bitmask of the VBI lines checked,
242+
specifically lines 6 to 22, and lines 318 to 335. Line 6 is the LSB in the mask.
243+
Selected lines which do not contain teletext information will be ignored. You
244+
can use the special @option{all} constant to select all possible lines, or
245+
@option{standard} to skip lines 6, 318 and 319, which are not compatible with all
246+
receivers. Capturing teletext only works for SD PAL sources in 8 bit mode.
247+
To use this option, ffmpeg needs to be compiled with @code{--enable-libzvbi}.
248+
239249
@end table
240250

241251
@subsection Examples

libavdevice/decklink_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,12 @@ struct decklink_ctx {
6767
unsigned int dropped;
6868
AVStream *audio_st;
6969
AVStream *video_st;
70+
AVStream *teletext_st;
7071

7172
/* Options */
7273
int list_devices;
7374
int list_formats;
75+
int64_t teletext_lines;
7476
double preroll;
7577

7678
int frames_preroll;

libavdevice/decklink_common_c.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct decklink_cctx {
2727
/* Options */
2828
int list_devices;
2929
int list_formats;
30+
int64_t teletext_lines;
3031
double preroll;
3132
int v210;
3233
};

libavdevice/decklink_dec.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,47 @@
2525
#include <semaphore.h>
2626

2727
extern "C" {
28+
#include "config.h"
2829
#include "libavformat/avformat.h"
2930
#include "libavformat/internal.h"
3031
#include "libavutil/imgutils.h"
32+
#if CONFIG_LIBZVBI
33+
#include <libzvbi.h>
34+
#endif
3135
}
3236

3337
#include "decklink_common.h"
3438
#include "decklink_dec.h"
3539

40+
#if CONFIG_LIBZVBI
41+
static uint8_t calc_parity_and_line_offset(int line)
42+
{
43+
uint8_t ret = (line < 313) << 5;
44+
if (line >= 7 && line <= 22)
45+
ret += line;
46+
if (line >= 320 && line <= 335)
47+
ret += (line - 313);
48+
return ret;
49+
}
50+
51+
int teletext_data_unit_from_vbi_data(int line, uint8_t *src, uint8_t *tgt)
52+
{
53+
vbi_bit_slicer slicer;
54+
55+
vbi_bit_slicer_init(&slicer, 720, 13500000, 6937500, 6937500, 0x00aaaae4, 0xffff, 18, 6, 42 * 8, VBI_MODULATION_NRZ_MSB, VBI_PIXFMT_UYVY);
56+
57+
if (vbi_bit_slice(&slicer, src, tgt + 4) == FALSE)
58+
return -1;
59+
60+
tgt[0] = 0x02; // data_unit_id
61+
tgt[1] = 0x2c; // data_unit_length
62+
tgt[2] = calc_parity_and_line_offset(line); // field_parity, line_offset
63+
tgt[3] = 0xe4; // framing code
64+
65+
return 0;
66+
}
67+
#endif
68+
3669
static void avpacket_queue_init(AVFormatContext *avctx, AVPacketQueue *q)
3770
{
3871
memset(q, 0, sizeof(AVPacketQueue));
@@ -277,6 +310,50 @@ HRESULT decklink_input_callback::VideoInputFrameArrived(
277310
pkt.size = videoFrame->GetRowBytes() *
278311
videoFrame->GetHeight();
279312
//fprintf(stderr,"Video Frame size %d ts %d\n", pkt.size, pkt.pts);
313+
314+
#if CONFIG_LIBZVBI
315+
if (!no_video && ctx->teletext_lines && videoFrame->GetPixelFormat() == bmdFormat8BitYUV && videoFrame->GetWidth() == 720) {
316+
IDeckLinkVideoFrameAncillary *vanc;
317+
AVPacket txt_pkt;
318+
uint8_t txt_buf0[1611]; // max 35 * 46 bytes decoded teletext lines + 1 byte data_identifier
319+
uint8_t *txt_buf = txt_buf0;
320+
321+
if (videoFrame->GetAncillaryData(&vanc) == S_OK) {
322+
int i;
323+
int64_t line_mask = 1;
324+
txt_buf[0] = 0x10; // data_identifier - EBU_data
325+
txt_buf++;
326+
for (i = 6; i < 336; i++, line_mask <<= 1) {
327+
uint8_t *buf;
328+
if ((ctx->teletext_lines & line_mask) && vanc->GetBufferForVerticalBlankingLine(i, (void**)&buf) == S_OK) {
329+
if (teletext_data_unit_from_vbi_data(i, buf, txt_buf) >= 0)
330+
txt_buf += 46;
331+
}
332+
if (i == 22)
333+
i = 317;
334+
}
335+
vanc->Release();
336+
if (txt_buf - txt_buf0 > 1) {
337+
int stuffing_units = (4 - ((45 + txt_buf - txt_buf0) / 46) % 4) % 4;
338+
while (stuffing_units--) {
339+
memset(txt_buf, 0xff, 46);
340+
txt_buf[1] = 0x2c; // data_unit_length
341+
txt_buf += 46;
342+
}
343+
av_init_packet(&txt_pkt);
344+
txt_pkt.pts = pkt.pts;
345+
txt_pkt.dts = pkt.dts;
346+
txt_pkt.stream_index = ctx->teletext_st->index;
347+
txt_pkt.data = txt_buf0;
348+
txt_pkt.size = txt_buf - txt_buf0;
349+
if (avpacket_queue_put(&ctx->queue, &txt_pkt) < 0) {
350+
++ctx->dropped;
351+
}
352+
}
353+
}
354+
}
355+
#endif
356+
280357
c->frame_number++;
281358
if (avpacket_queue_put(&ctx->queue, &pkt) < 0) {
282359
++ctx->dropped;
@@ -378,9 +455,17 @@ av_cold int ff_decklink_read_header(AVFormatContext *avctx)
378455
return AVERROR(ENOMEM);
379456
ctx->list_devices = cctx->list_devices;
380457
ctx->list_formats = cctx->list_formats;
458+
ctx->teletext_lines = cctx->teletext_lines;
381459
ctx->preroll = cctx->preroll;
382460
cctx->ctx = ctx;
383461

462+
#if !CONFIG_LIBZVBI
463+
if (ctx->teletext_lines) {
464+
av_log(avctx, AV_LOG_ERROR, "Libzvbi support is needed for capturing teletext, please recompile FFmpeg.\n");
465+
return AVERROR(ENOSYS);
466+
}
467+
#endif
468+
384469
iter = CreateDeckLinkIteratorInstance();
385470
if (!iter) {
386471
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)
488573

489574
ctx->video_st=st;
490575

576+
if (ctx->teletext_lines) {
577+
st = avformat_new_stream(avctx, NULL);
578+
if (!st) {
579+
av_log(avctx, AV_LOG_ERROR, "Cannot add stream\n");
580+
goto error;
581+
}
582+
st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
583+
st->codec->time_base.den = ctx->bmd_tb_den;
584+
st->codec->time_base.num = ctx->bmd_tb_num;
585+
st->codec->codec_id = AV_CODEC_ID_DVB_TELETEXT;
586+
avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
587+
ctx->teletext_st = st;
588+
}
589+
491590
result = ctx->dli->EnableAudioInput(bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2);
492591

493592
if (result != S_OK) {

libavdevice/decklink_dec_c.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ static const AVOption options[] = {
3232
{ "list_devices", "list available devices" , OFFSET(list_devices), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC },
3333
{ "list_formats", "list supported formats" , OFFSET(list_formats), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC },
3434
{ "bm_v210", "v210 10 bit per channel" , OFFSET(v210), AV_OPT_TYPE_INT , { .i64 = 0 }, 0, 1, DEC },
35+
{ "teletext_lines", "teletext lines bitmask", OFFSET(teletext_lines), AV_OPT_TYPE_INT64, { .i64 = 0 }, 0, 0x7ffffffffLL, DEC, "teletext_lines"},
36+
{ "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7fff9fffeLL}, 0, 0, DEC, "teletext_lines"},
37+
{ "all", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0x7ffffffffLL}, 0, 0, DEC, "teletext_lines"},
3538
{ NULL },
3639
};
3740

libavdevice/version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
#define LIBAVDEVICE_VERSION_MAJOR 57
3131
#define LIBAVDEVICE_VERSION_MINOR 0
32-
#define LIBAVDEVICE_VERSION_MICRO 100
32+
#define LIBAVDEVICE_VERSION_MICRO 101
3333

3434
#define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
3535
LIBAVDEVICE_VERSION_MINOR, \

0 commit comments

Comments
 (0)