2 * Teletext decoding for ffmpeg
3 * Copyright (c) 2005-2010, 2012 Wolfram Gloger
4 * Copyright (c) 2013 Marton Balint
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "libavcodec/ass.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/bprint.h"
25 #include "libavutil/intreadwrite.h"
26 #include "libavutil/log.h"
30 #define TEXT_MAXSZ (25 * (56 + 1) * 4 + 2)
31 #define VBI_NB_COLORS 40
32 #define RGBA(r,g,b,a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
33 #define VBI_R(rgba) (((rgba) >> 0) & 0xFF)
34 #define VBI_G(rgba) (((rgba) >> 8) & 0xFF)
35 #define VBI_B(rgba) (((rgba) >> 16) & 0xFF)
36 #define VBI_A(rgba) (((rgba) >> 24) & 0xFF)
37 #define MAX_BUFFERED_PAGES 25
38 #define BITMAP_CHAR_WIDTH 12
39 #define BITMAP_CHAR_HEIGHT 10
42 typedef struct TeletextPage
44 AVSubtitleRect
*sub_rect
;
50 typedef struct TeletextContext
56 int format_id
; /* 0 = bitmap, 1 = text/ass */
58 int sub_duration
; /* in msec */
72 vbi_sliced sliced
[MAX_SLICES
];
75 static int chop_spaces_utf8(const unsigned char* t
, int len
)
79 if (*--t
!= ' ' || (len
-1 > 0 && *(t
-1) & 0x80))
86 static void subtitle_rect_free(AVSubtitleRect
**sub_rect
)
88 av_freep(&(*sub_rect
)->pict
.data
[0]);
89 av_freep(&(*sub_rect
)->pict
.data
[1]);
90 av_freep(&(*sub_rect
)->ass
);
94 static int create_ass_text(TeletextContext
*ctx
, const char *text
, char **ass
)
98 const int ts_start
= av_rescale_q(ctx
->pts
, AV_TIME_BASE_Q
, (AVRational
){1, 100});
99 const int ts_duration
= av_rescale_q(ctx
->sub_duration
, (AVRational
){1, 1000}, (AVRational
){1, 100});
101 /* First we escape the plain text into buf. */
102 av_bprint_init(&buf
, 0, AV_BPRINT_SIZE_UNLIMITED
);
103 ff_ass_bprint_text_event(&buf
, text
, strlen(text
), "", 0);
104 av_bprintf(&buf
, "\r\n");
106 if (!av_bprint_is_complete(&buf
)) {
107 av_bprint_finalize(&buf
, NULL
);
108 return AVERROR(ENOMEM
);
111 /* Then we create the ass dialog line in buf2 from the escaped text in buf. */
112 av_bprint_init(&buf2
, 0, AV_BPRINT_SIZE_UNLIMITED
);
113 ff_ass_bprint_dialog(&buf2
, buf
.str
, ts_start
, ts_duration
, 0);
114 av_bprint_finalize(&buf
, NULL
);
116 if (!av_bprint_is_complete(&buf2
)) {
117 av_bprint_finalize(&buf2
, NULL
);
118 return AVERROR(ENOMEM
);
121 if ((ret
= av_bprint_finalize(&buf2
, ass
)) < 0)
127 /* Draw a page as text */
128 static int gen_sub_text(TeletextContext
*ctx
, AVSubtitleRect
*sub_rect
, vbi_page
*page
, int chop_top
)
132 char *vbi_text
= av_malloc(TEXT_MAXSZ
);
136 return AVERROR(ENOMEM
);
138 sz
= vbi_print_page_region(page
, vbi_text
, TEXT_MAXSZ
-1, "UTF-8",
139 /*table mode*/ TRUE
, FALSE
,
141 page
->columns
, page
->rows
-chop_top
);
143 av_log(ctx
, AV_LOG_ERROR
, "vbi_print error\n");
145 return AVERROR_EXTERNAL
;
149 av_bprint_init(&buf
, 0, TEXT_MAXSZ
);
151 if (ctx
->chop_spaces
) {
155 // skip leading spaces and newlines
156 in
+= strspn(in
, " \n");
157 // compute end of row
158 for (nl
= 0; in
[nl
]; ++nl
)
159 if (in
[nl
] == '\n' && (nl
==0 || !(in
[nl
-1] & 0x80)))
163 // skip trailing spaces
164 sz
= chop_spaces_utf8(in
, nl
);
165 av_bprint_append_data(&buf
, in
, sz
);
166 av_bprintf(&buf
, "\n");
170 av_bprintf(&buf
, "%s\n", vbi_text
);
174 if (!av_bprint_is_complete(&buf
)) {
175 av_bprint_finalize(&buf
, NULL
);
176 return AVERROR(ENOMEM
);
181 sub_rect
->type
= SUBTITLE_ASS
;
182 if ((ret
= create_ass_text(ctx
, buf
.str
, &sub_rect
->ass
)) < 0) {
183 av_bprint_finalize(&buf
, NULL
);
186 av_log(ctx
, AV_LOG_DEBUG
, "subtext:%s:txetbus\n", sub_rect
->ass
);
188 sub_rect
->type
= SUBTITLE_NONE
;
190 av_bprint_finalize(&buf
, NULL
);
194 static void fix_transparency(TeletextContext
*ctx
, AVSubtitleRect
*sub_rect
, vbi_page
*page
,
195 int chop_top
, uint8_t transparent_color
, int resx
, int resy
)
199 // Hack for transparency, inspired by VLC code...
200 for (iy
= 0; iy
< resy
; iy
++) {
201 uint8_t *pixel
= sub_rect
->pict
.data
[0] + iy
* sub_rect
->pict
.linesize
[0];
202 vbi_char
*vc
= page
->text
+ (iy
/ BITMAP_CHAR_HEIGHT
+ chop_top
) * page
->columns
;
203 vbi_char
*vcnext
= vc
+ page
->columns
;
204 for (; vc
< vcnext
; vc
++) {
205 uint8_t *pixelnext
= pixel
+ BITMAP_CHAR_WIDTH
;
206 switch (vc
->opacity
) {
207 case VBI_TRANSPARENT_SPACE
:
208 memset(pixel
, transparent_color
, BITMAP_CHAR_WIDTH
);
211 case VBI_SEMI_TRANSPARENT
:
212 if (!ctx
->transparent_bg
)
214 case VBI_TRANSPARENT_FULL
:
215 for(; pixel
< pixelnext
; pixel
++)
216 if (*pixel
== vc
->background
)
217 *pixel
= transparent_color
;
225 /* Draw a page as bitmap */
226 static int gen_sub_bitmap(TeletextContext
*ctx
, AVSubtitleRect
*sub_rect
, vbi_page
*page
, int chop_top
)
228 int resx
= page
->columns
* BITMAP_CHAR_WIDTH
;
229 int resy
= (page
->rows
- chop_top
) * BITMAP_CHAR_HEIGHT
;
230 uint8_t ci
, cmax
= 0;
232 vbi_char
*vc
= page
->text
+ (chop_top
* page
->columns
);
233 vbi_char
*vcend
= page
->text
+ (page
->rows
* page
->columns
);
235 for (; vc
< vcend
; vc
++) {
236 if (vc
->opacity
!= VBI_TRANSPARENT_SPACE
) {
237 cmax
= VBI_NB_COLORS
;
243 av_log(ctx
, AV_LOG_DEBUG
, "dropping empty page %3x\n", page
->pgno
);
244 sub_rect
->type
= SUBTITLE_NONE
;
248 if ((ret
= avpicture_alloc(&sub_rect
->pict
, AV_PIX_FMT_PAL8
, resx
, resy
)) < 0)
250 // Yes, we want to allocate the palette on our own because AVSubtitle works this way
251 sub_rect
->pict
.data
[1] = NULL
;
253 vbi_draw_vt_page_region(page
, VBI_PIXFMT_PAL8
,
254 sub_rect
->pict
.data
[0], sub_rect
->pict
.linesize
[0],
255 0, chop_top
, page
->columns
, page
->rows
- chop_top
,
256 /*reveal*/ 1, /*flash*/ 1);
258 fix_transparency(ctx
, sub_rect
, page
, chop_top
, cmax
, resx
, resy
);
259 sub_rect
->x
= ctx
->x_offset
;
260 sub_rect
->y
= ctx
->y_offset
+ chop_top
* BITMAP_CHAR_HEIGHT
;
263 sub_rect
->nb_colors
= (int)cmax
+ 1;
264 sub_rect
->pict
.data
[1] = av_mallocz(AVPALETTE_SIZE
);
265 if (!sub_rect
->pict
.data
[1]) {
266 av_freep(&sub_rect
->pict
.data
[0]);
267 return AVERROR(ENOMEM
);
269 for (ci
= 0; ci
< cmax
; ci
++) {
272 r
= VBI_R(page
->color_map
[ci
]);
273 g
= VBI_G(page
->color_map
[ci
]);
274 b
= VBI_B(page
->color_map
[ci
]);
275 a
= VBI_A(page
->color_map
[ci
]);
276 ((uint32_t *)sub_rect
->pict
.data
[1])[ci
] = RGBA(r
, g
, b
, a
);
277 av_dlog(ctx
, "palette %0x\n", ((uint32_t *)sub_rect
->pict
.data
[1])[ci
]);
279 ((uint32_t *)sub_rect
->pict
.data
[1])[cmax
] = RGBA(0, 0, 0, 0);
280 sub_rect
->type
= SUBTITLE_BITMAP
;
284 static void handler(vbi_event
*ev
, void *user_data
)
286 TeletextContext
*ctx
= user_data
;
287 TeletextPage
*new_pages
;
296 snprintf(pgno_str
, sizeof pgno_str
, "%03x", ev
->ev
.ttx_page
.pgno
);
297 av_log(ctx
, AV_LOG_DEBUG
, "decoded page %s.%02x\n",
298 pgno_str
, ev
->ev
.ttx_page
.subno
& 0xFF);
300 if (strcmp(ctx
->pgno
, "*") && !strstr(ctx
->pgno
, pgno_str
))
302 if (ctx
->handler_ret
< 0)
305 res
= vbi_fetch_vt_page(ctx
->vbi
, &page
,
306 ev
->ev
.ttx_page
.pgno
,
307 ev
->ev
.ttx_page
.subno
,
308 VBI_WST_LEVEL_3p5
, 25, TRUE
);
314 fprintf(stderr
, "\nSaving res=%d dy0=%d dy1=%d...\n",
315 res
, page
.dirty
.y0
, page
.dirty
.y1
);
318 if (!vbi_export_stdio(ctx
->ex
, stderr
, &page
))
319 fprintf(stderr
, "failed: %s\n", vbi_export_errstr(ctx
->ex
));
322 vpt
= vbi_classify_page(ctx
->vbi
, ev
->ev
.ttx_page
.pgno
, &subno
, &lang
);
323 chop_top
= ctx
->chop_top
||
324 ((page
.rows
> 1) && (vpt
== VBI_SUBTITLE_PAGE
));
326 av_log(ctx
, AV_LOG_DEBUG
, "%d x %d page chop:%d\n",
327 page
.columns
, page
.rows
, chop_top
);
329 if (ctx
->nb_pages
< MAX_BUFFERED_PAGES
) {
330 if ((new_pages
= av_realloc_array(ctx
->pages
, ctx
->nb_pages
+ 1, sizeof(TeletextPage
)))) {
331 TeletextPage
*cur_page
= new_pages
+ ctx
->nb_pages
;
332 ctx
->pages
= new_pages
;
333 cur_page
->sub_rect
= av_mallocz(sizeof(*cur_page
->sub_rect
));
334 cur_page
->pts
= ctx
->pts
;
335 cur_page
->pgno
= ev
->ev
.ttx_page
.pgno
;
336 cur_page
->subno
= ev
->ev
.ttx_page
.subno
;
337 if (cur_page
->sub_rect
) {
338 res
= (ctx
->format_id
== 0) ?
339 gen_sub_bitmap(ctx
, cur_page
->sub_rect
, &page
, chop_top
) :
340 gen_sub_text (ctx
, cur_page
->sub_rect
, &page
, chop_top
);
342 av_freep(&cur_page
->sub_rect
);
343 ctx
->handler_ret
= res
;
345 ctx
->pages
[ctx
->nb_pages
++] = *cur_page
;
348 ctx
->handler_ret
= AVERROR(ENOMEM
);
351 ctx
->handler_ret
= AVERROR(ENOMEM
);
354 //TODO: If multiple packets contain more than one page, pages may got queued up, and this may happen...
355 av_log(ctx
, AV_LOG_ERROR
, "Buffered too many pages, dropping page %s.\n", pgno_str
);
356 ctx
->handler_ret
= AVERROR(ENOSYS
);
359 vbi_unref_page(&page
);
362 static inline int data_identifier_is_teletext(int data_identifier
) {
363 /* See EN 301 775 section 4.4.2. */
364 return (data_identifier
>= 0x10 && data_identifier
<= 0x1F ||
365 data_identifier
>= 0x99 && data_identifier
<= 0x9B);
368 static int slice_to_vbi_lines(TeletextContext
*ctx
, uint8_t* buf
, int size
)
371 while (size
>= 2 && lines
< MAX_SLICES
) {
372 int data_unit_id
= buf
[0];
373 int data_unit_length
= buf
[1];
374 if (data_unit_length
+ 2 > size
)
375 return AVERROR_INVALIDDATA
;
376 if (data_unit_id
== 0x02 || data_unit_id
== 0x03) {
377 if (data_unit_length
!= 0x2c)
378 return AVERROR_INVALIDDATA
;
380 int line_offset
= buf
[2] & 0x1f;
381 int field_parity
= buf
[2] & 0x20;
383 ctx
->sliced
[lines
].id
= VBI_SLICED_TELETEXT_B
;
384 ctx
->sliced
[lines
].line
= (line_offset
> 0 ? (line_offset
+ (field_parity
? 0 : 313)) : 0);
385 for (i
= 0; i
< 42; i
++)
386 ctx
->sliced
[lines
].data
[i
] = vbi_rev8(buf
[4 + i
]);
390 size
-= data_unit_length
+ 2;
391 buf
+= data_unit_length
+ 2;
394 av_log(ctx
, AV_LOG_WARNING
, "%d bytes remained after slicing data\n", size
);
398 static int teletext_decode_frame(AVCodecContext
*avctx
, void *data
, int *data_size
, AVPacket
*pkt
)
400 TeletextContext
*ctx
= avctx
->priv_data
;
401 AVSubtitle
*sub
= data
;
405 if (!(ctx
->vbi
= vbi_decoder_new()))
406 return AVERROR(ENOMEM
);
407 if (!vbi_event_handler_add(ctx
->vbi
, VBI_EVENT_TTX_PAGE
, handler
, ctx
)) {
408 vbi_decoder_delete(ctx
->vbi
);
410 return AVERROR(ENOMEM
);
414 if (avctx
->pkt_timebase
.den
&& pkt
->pts
!= AV_NOPTS_VALUE
)
415 ctx
->pts
= av_rescale_q(pkt
->pts
, avctx
->pkt_timebase
, AV_TIME_BASE_Q
);
419 const int full_pes_size
= pkt
->size
+ 45; /* PES header is 45 bytes */
421 // We allow unreasonably big packets, even if the standard only allows a max size of 1472
422 if (full_pes_size
< 184 || full_pes_size
> 65504 || full_pes_size
% 184 != 0)
423 return AVERROR_INVALIDDATA
;
425 ctx
->handler_ret
= pkt
->size
;
427 if (data_identifier_is_teletext(*pkt
->data
)) {
428 if ((lines
= slice_to_vbi_lines(ctx
, pkt
->data
+ 1, pkt
->size
- 1)) < 0)
430 av_dlog(avctx
, "ctx=%p buf_size=%d lines=%u pkt_pts=%7.3f\n",
431 ctx
, pkt
->size
, lines
, (double)pkt
->pts
/90000.0);
435 av_log(avctx
, AV_LOG_DEBUG
, "line numbers:");
436 for(i
= 0; i
< lines
; i
++)
437 av_log(avctx
, AV_LOG_DEBUG
, " %d", ctx
->sliced
[i
].line
);
438 av_log(avctx
, AV_LOG_DEBUG
, "\n");
440 vbi_decode(ctx
->vbi
, ctx
->sliced
, lines
, 0.0);
441 ctx
->lines_processed
+= lines
;
444 ctx
->pts
= AV_NOPTS_VALUE
;
445 ret
= ctx
->handler_ret
;
451 // is there a subtitle to pass?
454 sub
->format
= ctx
->format_id
;
455 sub
->start_display_time
= 0;
456 sub
->end_display_time
= ctx
->sub_duration
;
458 sub
->pts
= ctx
->pages
->pts
;
460 if (ctx
->pages
->sub_rect
->type
!= SUBTITLE_NONE
) {
461 sub
->rects
= av_malloc(sizeof(*sub
->rects
));
464 sub
->rects
[0] = ctx
->pages
->sub_rect
;
466 ret
= AVERROR(ENOMEM
);
469 av_log(avctx
, AV_LOG_DEBUG
, "sending empty sub\n");
472 if (!sub
->rects
) // no rect was passed
473 subtitle_rect_free(&ctx
->pages
->sub_rect
);
475 for (i
= 0; i
< ctx
->nb_pages
- 1; i
++)
476 ctx
->pages
[i
] = ctx
->pages
[i
+ 1];
487 static int teletext_init_decoder(AVCodecContext
*avctx
)
489 TeletextContext
*ctx
= avctx
->priv_data
;
490 unsigned int maj
, min
, rev
;
492 vbi_version(&maj
, &min
, &rev
);
493 if (!(maj
> 0 || min
> 2 || min
== 2 && rev
>= 26)) {
494 av_log(avctx
, AV_LOG_ERROR
, "decoder needs zvbi version >= 0.2.26.\n");
495 return AVERROR_EXTERNAL
;
498 if (ctx
->format_id
== 0) {
499 avctx
->width
= 41 * BITMAP_CHAR_WIDTH
;
500 avctx
->height
= 25 * BITMAP_CHAR_HEIGHT
;
504 ctx
->pts
= AV_NOPTS_VALUE
;
509 ctx
->ex
= vbi_export_new("text", &t
);
512 av_log(avctx
, AV_LOG_VERBOSE
, "page filter: %s\n", ctx
->pgno
);
513 return (ctx
->format_id
== 1) ? ff_ass_subtitle_header_default(avctx
) : 0;
516 static int teletext_close_decoder(AVCodecContext
*avctx
)
518 TeletextContext
*ctx
= avctx
->priv_data
;
520 av_dlog(avctx
, "lines_total=%u\n", ctx
->lines_processed
);
521 while (ctx
->nb_pages
)
522 subtitle_rect_free(&ctx
->pages
[--ctx
->nb_pages
].sub_rect
);
523 av_freep(&ctx
->pages
);
525 vbi_decoder_delete(ctx
->vbi
);
527 ctx
->pts
= AV_NOPTS_VALUE
;
531 static void teletext_flush(AVCodecContext
*avctx
)
533 teletext_close_decoder(avctx
);
536 #define OFFSET(x) offsetof(TeletextContext, x)
537 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
538 static const AVOption options
[] = {
539 {"txt_page", "list of teletext page numbers to decode, * is all", OFFSET(pgno
), AV_OPT_TYPE_STRING
, {.str
= "*"}, 0, 0, SD
},
540 {"txt_chop_top", "discards the top teletext line", OFFSET(chop_top
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, SD
},
541 {"txt_format", "format of the subtitles (bitmap or text)", OFFSET(format_id
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, SD
, "txt_format"},
542 {"bitmap", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 0}, 0, 0, SD
, "txt_format"},
543 {"text", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 1}, 0, 0, SD
, "txt_format"},
544 {"txt_left", "x offset of generated bitmaps", OFFSET(x_offset
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 65535, SD
},
545 {"txt_top", "y offset of generated bitmaps", OFFSET(y_offset
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 65535, SD
},
546 {"txt_chop_spaces", "chops leading and trailing spaces from text", OFFSET(chop_spaces
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, SD
},
547 {"txt_duration", "display duration of teletext pages in msecs", OFFSET(sub_duration
), AV_OPT_TYPE_INT
, {.i64
= 30000}, 0, 86400000, SD
},
548 {"txt_transparent", "force transparent background of the teletext", OFFSET(transparent_bg
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, SD
},
552 static const AVClass teletext_class
= {
553 .class_name
= "libzvbi_teletextdec",
554 .item_name
= av_default_item_name
,
556 .version
= LIBAVUTIL_VERSION_INT
,
559 AVCodec ff_libzvbi_teletext_decoder
= {
560 .name
= "libzvbi_teletextdec",
561 .long_name
= NULL_IF_CONFIG_SMALL("Libzvbi DVB teletext decoder"),
562 .type
= AVMEDIA_TYPE_SUBTITLE
,
563 .id
= AV_CODEC_ID_DVB_TELETEXT
,
564 .priv_data_size
= sizeof(TeletextContext
),
565 .init
= teletext_init_decoder
,
566 .close
= teletext_close_decoder
,
567 .decode
= teletext_decode_frame
,
568 .capabilities
= CODEC_CAP_DELAY
,
569 .flush
= teletext_flush
,
570 .priv_class
= &teletext_class
,