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);
105 if (!av_bprint_is_complete(&buf
)) {
106 av_bprint_finalize(&buf
, NULL
);
107 return AVERROR(ENOMEM
);
110 /* Then we create the ass dialog line in buf2 from the escaped text in buf. */
111 av_bprint_init(&buf2
, 0, AV_BPRINT_SIZE_UNLIMITED
);
112 ff_ass_bprint_dialog(&buf2
, buf
.str
, ts_start
, ts_duration
, 0);
113 av_bprint_finalize(&buf
, NULL
);
115 if (!av_bprint_is_complete(&buf2
)) {
116 av_bprint_finalize(&buf2
, NULL
);
117 return AVERROR(ENOMEM
);
120 if ((ret
= av_bprint_finalize(&buf2
, ass
)) < 0)
126 /* Draw a page as text */
127 static int gen_sub_text(TeletextContext
*ctx
, AVSubtitleRect
*sub_rect
, vbi_page
*page
, int chop_top
)
131 char *vbi_text
= av_malloc(TEXT_MAXSZ
);
135 return AVERROR(ENOMEM
);
137 sz
= vbi_print_page_region(page
, vbi_text
, TEXT_MAXSZ
-1, "UTF-8",
138 /*table mode*/ TRUE
, FALSE
,
140 page
->columns
, page
->rows
-chop_top
);
142 av_log(ctx
, AV_LOG_ERROR
, "vbi_print error\n");
144 return AVERROR_EXTERNAL
;
148 av_bprint_init(&buf
, 0, TEXT_MAXSZ
);
150 if (ctx
->chop_spaces
) {
154 // skip leading spaces and newlines
155 in
+= strspn(in
, " \n");
156 // compute end of row
157 for (nl
= 0; in
[nl
]; ++nl
)
158 if (in
[nl
] == '\n' && (nl
==0 || !(in
[nl
-1] & 0x80)))
162 // skip trailing spaces
163 sz
= chop_spaces_utf8(in
, nl
);
164 av_bprint_append_data(&buf
, in
, sz
);
165 av_bprintf(&buf
, "\n");
169 av_bprintf(&buf
, "%s\n", vbi_text
);
173 if (!av_bprint_is_complete(&buf
)) {
174 av_bprint_finalize(&buf
, NULL
);
175 return AVERROR(ENOMEM
);
180 sub_rect
->type
= SUBTITLE_ASS
;
181 if ((ret
= create_ass_text(ctx
, buf
.str
, &sub_rect
->ass
)) < 0) {
182 av_bprint_finalize(&buf
, NULL
);
185 av_log(ctx
, AV_LOG_DEBUG
, "subtext:%s:txetbus\n", sub_rect
->ass
);
187 sub_rect
->type
= SUBTITLE_NONE
;
189 av_bprint_finalize(&buf
, NULL
);
193 static void fix_transparency(TeletextContext
*ctx
, AVSubtitleRect
*sub_rect
, vbi_page
*page
,
194 int chop_top
, uint8_t transparent_color
, int resx
, int resy
)
198 // Hack for transparency, inspired by VLC code...
199 for (iy
= 0; iy
< resy
; iy
++) {
200 uint8_t *pixel
= sub_rect
->pict
.data
[0] + iy
* sub_rect
->pict
.linesize
[0];
201 vbi_char
*vc
= page
->text
+ (iy
/ BITMAP_CHAR_HEIGHT
+ chop_top
) * page
->columns
;
202 vbi_char
*vcnext
= vc
+ page
->columns
;
203 for (; vc
< vcnext
; vc
++) {
204 uint8_t *pixelnext
= pixel
+ BITMAP_CHAR_WIDTH
;
205 switch (vc
->opacity
) {
206 case VBI_TRANSPARENT_SPACE
:
207 memset(pixel
, transparent_color
, BITMAP_CHAR_WIDTH
);
210 case VBI_SEMI_TRANSPARENT
:
211 if (!ctx
->transparent_bg
)
213 case VBI_TRANSPARENT_FULL
:
214 for(; pixel
< pixelnext
; pixel
++)
215 if (*pixel
== vc
->background
)
216 *pixel
= transparent_color
;
224 /* Draw a page as bitmap */
225 static int gen_sub_bitmap(TeletextContext
*ctx
, AVSubtitleRect
*sub_rect
, vbi_page
*page
, int chop_top
)
227 int resx
= page
->columns
* BITMAP_CHAR_WIDTH
;
228 int resy
= (page
->rows
- chop_top
) * BITMAP_CHAR_HEIGHT
;
229 uint8_t ci
, cmax
= 0;
231 vbi_char
*vc
= page
->text
+ (chop_top
* page
->columns
);
232 vbi_char
*vcend
= page
->text
+ (page
->rows
* page
->columns
);
234 for (; vc
< vcend
; vc
++) {
235 if (vc
->opacity
!= VBI_TRANSPARENT_SPACE
) {
236 cmax
= VBI_NB_COLORS
;
242 av_log(ctx
, AV_LOG_DEBUG
, "dropping empty page %3x\n", page
->pgno
);
243 sub_rect
->type
= SUBTITLE_NONE
;
247 if ((ret
= avpicture_alloc(&sub_rect
->pict
, AV_PIX_FMT_PAL8
, resx
, resy
)) < 0)
249 // Yes, we want to allocate the palette on our own because AVSubtitle works this way
250 sub_rect
->pict
.data
[1] = NULL
;
252 vbi_draw_vt_page_region(page
, VBI_PIXFMT_PAL8
,
253 sub_rect
->pict
.data
[0], sub_rect
->pict
.linesize
[0],
254 0, chop_top
, page
->columns
, page
->rows
- chop_top
,
255 /*reveal*/ 1, /*flash*/ 1);
257 fix_transparency(ctx
, sub_rect
, page
, chop_top
, cmax
, resx
, resy
);
258 sub_rect
->x
= ctx
->x_offset
;
259 sub_rect
->y
= ctx
->y_offset
+ chop_top
* BITMAP_CHAR_HEIGHT
;
262 sub_rect
->nb_colors
= (int)cmax
+ 1;
263 sub_rect
->pict
.data
[1] = av_mallocz(AVPALETTE_SIZE
);
264 if (!sub_rect
->pict
.data
[1]) {
265 av_freep(&sub_rect
->pict
.data
[0]);
266 return AVERROR(ENOMEM
);
268 for (ci
= 0; ci
< cmax
; ci
++) {
271 r
= VBI_R(page
->color_map
[ci
]);
272 g
= VBI_G(page
->color_map
[ci
]);
273 b
= VBI_B(page
->color_map
[ci
]);
274 a
= VBI_A(page
->color_map
[ci
]);
275 ((uint32_t *)sub_rect
->pict
.data
[1])[ci
] = RGBA(r
, g
, b
, a
);
276 av_dlog(ctx
, "palette %0x\n", ((uint32_t *)sub_rect
->pict
.data
[1])[ci
]);
278 ((uint32_t *)sub_rect
->pict
.data
[1])[cmax
] = RGBA(0, 0, 0, 0);
279 sub_rect
->type
= SUBTITLE_BITMAP
;
283 static void handler(vbi_event
*ev
, void *user_data
)
285 TeletextContext
*ctx
= user_data
;
286 TeletextPage
*new_pages
;
295 snprintf(pgno_str
, sizeof pgno_str
, "%03x", ev
->ev
.ttx_page
.pgno
);
296 av_log(ctx
, AV_LOG_DEBUG
, "decoded page %s.%02x\n",
297 pgno_str
, ev
->ev
.ttx_page
.subno
& 0xFF);
299 if (strcmp(ctx
->pgno
, "*") && !strstr(ctx
->pgno
, pgno_str
))
301 if (ctx
->handler_ret
< 0)
304 res
= vbi_fetch_vt_page(ctx
->vbi
, &page
,
305 ev
->ev
.ttx_page
.pgno
,
306 ev
->ev
.ttx_page
.subno
,
307 VBI_WST_LEVEL_3p5
, 25, TRUE
);
313 fprintf(stderr
, "\nSaving res=%d dy0=%d dy1=%d...\n",
314 res
, page
.dirty
.y0
, page
.dirty
.y1
);
317 if (!vbi_export_stdio(ctx
->ex
, stderr
, &page
))
318 fprintf(stderr
, "failed: %s\n", vbi_export_errstr(ctx
->ex
));
321 vpt
= vbi_classify_page(ctx
->vbi
, ev
->ev
.ttx_page
.pgno
, &subno
, &lang
);
322 chop_top
= ctx
->chop_top
||
323 ((page
.rows
> 1) && (vpt
== VBI_SUBTITLE_PAGE
));
325 av_log(ctx
, AV_LOG_DEBUG
, "%d x %d page chop:%d\n",
326 page
.columns
, page
.rows
, chop_top
);
328 if (ctx
->nb_pages
< MAX_BUFFERED_PAGES
) {
329 if ((new_pages
= av_realloc_array(ctx
->pages
, ctx
->nb_pages
+ 1, sizeof(TeletextPage
)))) {
330 TeletextPage
*cur_page
= new_pages
+ ctx
->nb_pages
;
331 ctx
->pages
= new_pages
;
332 cur_page
->sub_rect
= av_mallocz(sizeof(*cur_page
->sub_rect
));
333 cur_page
->pts
= ctx
->pts
;
334 cur_page
->pgno
= ev
->ev
.ttx_page
.pgno
;
335 cur_page
->subno
= ev
->ev
.ttx_page
.subno
;
336 if (cur_page
->sub_rect
) {
337 res
= (ctx
->format_id
== 0) ?
338 gen_sub_bitmap(ctx
, cur_page
->sub_rect
, &page
, chop_top
) :
339 gen_sub_text (ctx
, cur_page
->sub_rect
, &page
, chop_top
);
341 av_freep(&cur_page
->sub_rect
);
342 ctx
->handler_ret
= res
;
344 ctx
->pages
[ctx
->nb_pages
++] = *cur_page
;
347 ctx
->handler_ret
= AVERROR(ENOMEM
);
350 ctx
->handler_ret
= AVERROR(ENOMEM
);
353 //TODO: If multiple packets contain more than one page, pages may got queued up, and this may happen...
354 av_log(ctx
, AV_LOG_ERROR
, "Buffered too many pages, dropping page %s.\n", pgno_str
);
355 ctx
->handler_ret
= AVERROR(ENOSYS
);
358 vbi_unref_page(&page
);
361 static inline int data_identifier_is_teletext(int data_identifier
) {
362 /* See EN 301 775 section 4.4.2. */
363 return (data_identifier
>= 0x10 && data_identifier
<= 0x1F ||
364 data_identifier
>= 0x99 && data_identifier
<= 0x9B);
367 static int slice_to_vbi_lines(TeletextContext
*ctx
, uint8_t* buf
, int size
)
370 while (size
>= 2 && lines
< MAX_SLICES
) {
371 int data_unit_id
= buf
[0];
372 int data_unit_length
= buf
[1];
373 if (data_unit_length
+ 2 > size
)
374 return AVERROR_INVALIDDATA
;
375 if (data_unit_id
== 0x02 || data_unit_id
== 0x03) {
376 if (data_unit_length
!= 0x2c)
377 return AVERROR_INVALIDDATA
;
379 int line_offset
= buf
[2] & 0x1f;
380 int field_parity
= buf
[2] & 0x20;
382 ctx
->sliced
[lines
].id
= VBI_SLICED_TELETEXT_B
;
383 ctx
->sliced
[lines
].line
= (line_offset
> 0 ? (line_offset
+ (field_parity
? 0 : 313)) : 0);
384 for (i
= 0; i
< 42; i
++)
385 ctx
->sliced
[lines
].data
[i
] = vbi_rev8(buf
[4 + i
]);
389 size
-= data_unit_length
+ 2;
390 buf
+= data_unit_length
+ 2;
393 av_log(ctx
, AV_LOG_WARNING
, "%d bytes remained after slicing data\n", size
);
397 static int teletext_decode_frame(AVCodecContext
*avctx
, void *data
, int *data_size
, AVPacket
*pkt
)
399 TeletextContext
*ctx
= avctx
->priv_data
;
400 AVSubtitle
*sub
= data
;
404 if (!(ctx
->vbi
= vbi_decoder_new()))
405 return AVERROR(ENOMEM
);
406 if (!vbi_event_handler_add(ctx
->vbi
, VBI_EVENT_TTX_PAGE
, handler
, ctx
)) {
407 vbi_decoder_delete(ctx
->vbi
);
409 return AVERROR(ENOMEM
);
413 if (avctx
->pkt_timebase
.den
&& pkt
->pts
!= AV_NOPTS_VALUE
)
414 ctx
->pts
= av_rescale_q(pkt
->pts
, avctx
->pkt_timebase
, AV_TIME_BASE_Q
);
418 const int full_pes_size
= pkt
->size
+ 45; /* PES header is 45 bytes */
420 // We allow unreasonably big packets, even if the standard only allows a max size of 1472
421 if (full_pes_size
< 184 || full_pes_size
> 65504 || full_pes_size
% 184 != 0)
422 return AVERROR_INVALIDDATA
;
424 ctx
->handler_ret
= pkt
->size
;
426 if (data_identifier_is_teletext(*pkt
->data
)) {
427 if ((lines
= slice_to_vbi_lines(ctx
, pkt
->data
+ 1, pkt
->size
- 1)) < 0)
429 av_dlog(avctx
, "ctx=%p buf_size=%d lines=%u pkt_pts=%7.3f\n",
430 ctx
, pkt
->size
, lines
, (double)pkt
->pts
/90000.0);
434 av_log(avctx
, AV_LOG_DEBUG
, "line numbers:");
435 for(i
= 0; i
< lines
; i
++)
436 av_log(avctx
, AV_LOG_DEBUG
, " %d", ctx
->sliced
[i
].line
);
437 av_log(avctx
, AV_LOG_DEBUG
, "\n");
439 vbi_decode(ctx
->vbi
, ctx
->sliced
, lines
, 0.0);
440 ctx
->lines_processed
+= lines
;
443 ctx
->pts
= AV_NOPTS_VALUE
;
444 ret
= ctx
->handler_ret
;
450 // is there a subtitle to pass?
453 sub
->format
= ctx
->format_id
;
454 sub
->start_display_time
= 0;
455 sub
->end_display_time
= ctx
->sub_duration
;
457 sub
->pts
= ctx
->pages
->pts
;
459 if (ctx
->pages
->sub_rect
->type
!= SUBTITLE_NONE
) {
460 sub
->rects
= av_malloc(sizeof(*sub
->rects
));
463 sub
->rects
[0] = ctx
->pages
->sub_rect
;
465 ret
= AVERROR(ENOMEM
);
468 av_log(avctx
, AV_LOG_DEBUG
, "sending empty sub\n");
471 if (!sub
->rects
) // no rect was passed
472 subtitle_rect_free(&ctx
->pages
->sub_rect
);
474 for (i
= 0; i
< ctx
->nb_pages
- 1; i
++)
475 ctx
->pages
[i
] = ctx
->pages
[i
+ 1];
486 static int teletext_init_decoder(AVCodecContext
*avctx
)
488 TeletextContext
*ctx
= avctx
->priv_data
;
489 unsigned int maj
, min
, rev
;
491 vbi_version(&maj
, &min
, &rev
);
492 if (!(maj
> 0 || min
> 2 || min
== 2 && rev
>= 26)) {
493 av_log(avctx
, AV_LOG_ERROR
, "decoder needs zvbi version >= 0.2.26.\n");
494 return AVERROR_EXTERNAL
;
497 if (ctx
->format_id
== 0) {
498 avctx
->width
= 41 * BITMAP_CHAR_WIDTH
;
499 avctx
->height
= 25 * BITMAP_CHAR_HEIGHT
;
503 ctx
->pts
= AV_NOPTS_VALUE
;
508 ctx
->ex
= vbi_export_new("text", &t
);
511 av_log(avctx
, AV_LOG_VERBOSE
, "page filter: %s\n", ctx
->pgno
);
512 return (ctx
->format_id
== 1) ? ff_ass_subtitle_header_default(avctx
) : 0;
515 static int teletext_close_decoder(AVCodecContext
*avctx
)
517 TeletextContext
*ctx
= avctx
->priv_data
;
519 av_dlog(avctx
, "lines_total=%u\n", ctx
->lines_processed
);
520 while (ctx
->nb_pages
)
521 subtitle_rect_free(&ctx
->pages
[--ctx
->nb_pages
].sub_rect
);
522 av_freep(&ctx
->pages
);
524 vbi_decoder_delete(ctx
->vbi
);
526 ctx
->pts
= AV_NOPTS_VALUE
;
530 static void teletext_flush(AVCodecContext
*avctx
)
532 teletext_close_decoder(avctx
);
535 #define OFFSET(x) offsetof(TeletextContext, x)
536 #define SD AV_OPT_FLAG_SUBTITLE_PARAM | AV_OPT_FLAG_DECODING_PARAM
537 static const AVOption options
[] = {
538 {"txt_page", "list of teletext page numbers to decode, * is all", OFFSET(pgno
), AV_OPT_TYPE_STRING
, {.str
= "*"}, 0, 0, SD
},
539 {"txt_chop_top", "discards the top teletext line", OFFSET(chop_top
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, SD
},
540 {"txt_format", "format of the subtitles (bitmap or text)", OFFSET(format_id
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, SD
, "txt_format"},
541 {"bitmap", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 0}, 0, 0, SD
, "txt_format"},
542 {"text", NULL
, 0, AV_OPT_TYPE_CONST
, {.i64
= 1}, 0, 0, SD
, "txt_format"},
543 {"txt_left", "x offset of generated bitmaps", OFFSET(x_offset
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 65535, SD
},
544 {"txt_top", "y offset of generated bitmaps", OFFSET(y_offset
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 65535, SD
},
545 {"txt_chop_spaces", "chops leading and trailing spaces from text", OFFSET(chop_spaces
), AV_OPT_TYPE_INT
, {.i64
= 1}, 0, 1, SD
},
546 {"txt_duration", "display duration of teletext pages in msecs", OFFSET(sub_duration
), AV_OPT_TYPE_INT
, {.i64
= 30000}, 0, 86400000, SD
},
547 {"txt_transparent", "force transparent background of the teletext", OFFSET(transparent_bg
), AV_OPT_TYPE_INT
, {.i64
= 0}, 0, 1, SD
},
551 static const AVClass teletext_class
= {
552 .class_name
= "libzvbi_teletextdec",
553 .item_name
= av_default_item_name
,
555 .version
= LIBAVUTIL_VERSION_INT
,
558 AVCodec ff_libzvbi_teletext_decoder
= {
559 .name
= "libzvbi_teletextdec",
560 .long_name
= NULL_IF_CONFIG_SMALL("Libzvbi DVB teletext decoder"),
561 .type
= AVMEDIA_TYPE_SUBTITLE
,
562 .id
= AV_CODEC_ID_DVB_TELETEXT
,
563 .priv_data_size
= sizeof(TeletextContext
),
564 .init
= teletext_init_decoder
,
565 .close
= teletext_close_decoder
,
566 .decode
= teletext_decode_frame
,
567 .capabilities
= CODEC_CAP_DELAY
,
568 .flush
= teletext_flush
,
569 .priv_class
= &teletext_class
,