]> git.sesse.net Git - ffmpeg/blob - libavcodec/movtextdec.c
avcodec/movtextdec: Fix leaks on (re)allocation failure
[ffmpeg] / libavcodec / movtextdec.c
1 /*
2  * 3GPP TS 26.245 Timed Text decoder
3  * Copyright (c) 2012  Philip Langdale <philipl@overt.org>
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21
22 #include "avcodec.h"
23 #include "ass.h"
24 #include "libavutil/opt.h"
25 #include "libavutil/avstring.h"
26 #include "libavutil/common.h"
27 #include "libavutil/bprint.h"
28 #include "libavutil/intreadwrite.h"
29 #include "libavutil/mem.h"
30
31 #define STYLE_FLAG_BOLD         (1<<0)
32 #define STYLE_FLAG_ITALIC       (1<<1)
33 #define STYLE_FLAG_UNDERLINE    (1<<2)
34
35 #define BOX_SIZE_INITIAL    40
36
37 #define STYL_BOX   (1<<0)
38 #define HLIT_BOX   (1<<1)
39 #define HCLR_BOX   (1<<2)
40 #define TWRP_BOX   (1<<3)
41
42 #define BOTTOM_LEFT     1
43 #define BOTTOM_CENTER   2
44 #define BOTTOM_RIGHT    3
45 #define MIDDLE_LEFT     4
46 #define MIDDLE_CENTER   5
47 #define MIDDLE_RIGHT    6
48 #define TOP_LEFT        7
49 #define TOP_CENTER      8
50 #define TOP_RIGHT       9
51
52 #define RGB_TO_BGR(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((c) >> 16) & 0xff))
53
54 typedef struct {
55     uint16_t fontID;
56     const char *font;
57     uint8_t fontsize;
58     int color;
59     uint8_t alpha;
60     int back_color;
61     uint8_t back_alpha;
62     uint8_t bold;
63     uint8_t italic;
64     uint8_t underline;
65     int alignment;
66 } MovTextDefault;
67
68 typedef struct {
69     uint16_t fontID;
70     char *font;
71 } FontRecord;
72
73 typedef struct {
74     uint16_t style_start;
75     uint16_t style_end;
76     uint8_t style_flag;
77     uint8_t bold;
78     uint8_t italic;
79     uint8_t underline;
80     int color;
81     uint8_t alpha;
82     uint8_t fontsize;
83     uint16_t style_fontID;
84 } StyleBox;
85
86 typedef struct {
87     uint16_t hlit_start;
88     uint16_t hlit_end;
89 } HighlightBox;
90
91 typedef struct {
92    uint8_t hlit_color[4];
93 } HilightcolorBox;
94
95 typedef struct {
96     uint8_t wrap_flag;
97 } TextWrapBox;
98
99 typedef struct {
100     AVClass *class;
101     StyleBox *s;
102     HighlightBox h;
103     HilightcolorBox c;
104     FontRecord *ftab;
105     TextWrapBox w;
106     MovTextDefault d;
107     uint8_t box_flags;
108     uint16_t style_entries, ftab_entries;
109     uint64_t tracksize;
110     int size_var;
111     int readorder;
112     int frame_width;
113     int frame_height;
114 } MovTextContext;
115
116 typedef struct {
117     uint32_t type;
118     size_t base_size;
119     int (*decode)(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt);
120 } Box;
121
122 static void mov_text_cleanup(MovTextContext *m)
123 {
124     if (m->box_flags & STYL_BOX) {
125         av_freep(&m->s);
126         m->style_entries = 0;
127     }
128 }
129
130 static void mov_text_cleanup_ftab(MovTextContext *m)
131 {
132     for (unsigned i = 0; i < m->ftab_entries; i++)
133         av_freep(&m->ftab[i].font);
134     av_freep(&m->ftab);
135     m->ftab_entries = 0;
136 }
137
138 static int mov_text_tx3g(AVCodecContext *avctx, MovTextContext *m)
139 {
140     uint8_t *tx3g_ptr = avctx->extradata;
141     int i, j = -1, font_length, remaining = avctx->extradata_size - BOX_SIZE_INITIAL;
142     int8_t v_align, h_align;
143     unsigned ftab_entries;
144     StyleBox s_default;
145
146     m->ftab_entries = 0;
147     if (remaining < 0)
148         return -1;
149
150     // Display Flags
151     tx3g_ptr += 4;
152     // Alignment
153     h_align = *tx3g_ptr++;
154     v_align = *tx3g_ptr++;
155     if (h_align == 0) {
156         if (v_align == 0)
157             m->d.alignment = TOP_LEFT;
158         if (v_align == 1)
159             m->d.alignment = MIDDLE_LEFT;
160         if (v_align == -1)
161             m->d.alignment = BOTTOM_LEFT;
162     }
163     if (h_align == 1) {
164         if (v_align == 0)
165             m->d.alignment = TOP_CENTER;
166         if (v_align == 1)
167             m->d.alignment = MIDDLE_CENTER;
168         if (v_align == -1)
169             m->d.alignment = BOTTOM_CENTER;
170     }
171     if (h_align == -1) {
172         if (v_align == 0)
173             m->d.alignment = TOP_RIGHT;
174         if (v_align == 1)
175             m->d.alignment = MIDDLE_RIGHT;
176         if (v_align == -1)
177             m->d.alignment = BOTTOM_RIGHT;
178     }
179     // Background Color
180     m->d.back_color = AV_RB24(tx3g_ptr);
181     tx3g_ptr += 3;
182     m->d.back_alpha = AV_RB8(tx3g_ptr);
183     tx3g_ptr += 1;
184     // BoxRecord
185     tx3g_ptr += 8;
186     // StyleRecord
187     tx3g_ptr += 4;
188     // fontID
189     m->d.fontID = AV_RB16(tx3g_ptr);
190     tx3g_ptr += 2;
191     // face-style-flags
192     s_default.style_flag = *tx3g_ptr++;
193     m->d.bold = !!(s_default.style_flag & STYLE_FLAG_BOLD);
194     m->d.italic = !!(s_default.style_flag & STYLE_FLAG_ITALIC);
195     m->d.underline = !!(s_default.style_flag & STYLE_FLAG_UNDERLINE);
196     // fontsize
197     m->d.fontsize = *tx3g_ptr++;
198     // Primary color
199     m->d.color = AV_RB24(tx3g_ptr);
200     tx3g_ptr += 3;
201     m->d.alpha = AV_RB8(tx3g_ptr);
202     tx3g_ptr += 1;
203     // FontRecord
204     // FontRecord Size
205     tx3g_ptr += 4;
206     // ftab
207     tx3g_ptr += 4;
208
209     // In case of broken header, init default font
210     m->d.font = ASS_DEFAULT_FONT;
211
212     ftab_entries = AV_RB16(tx3g_ptr);
213     if (!ftab_entries)
214         return 0;
215     remaining   -= 3 * ftab_entries;
216     if (remaining < 0)
217         return AVERROR_INVALIDDATA;
218     m->ftab = av_calloc(ftab_entries, sizeof(*m->ftab));
219     if (!m->ftab)
220         return AVERROR(ENOMEM);
221     m->ftab_entries = ftab_entries;
222     tx3g_ptr += 2;
223
224     for (i = 0; i < m->ftab_entries; i++) {
225         m->ftab[i].fontID = AV_RB16(tx3g_ptr);
226         if (m->ftab[i].fontID == m->d.fontID)
227             j = i;
228         tx3g_ptr += 2;
229         font_length = *tx3g_ptr++;
230
231         remaining  -= font_length;
232         if (remaining < 0) {
233             mov_text_cleanup_ftab(m);
234             return -1;
235         }
236         m->ftab[i].font = av_malloc(font_length + 1);
237         if (!m->ftab[i].font) {
238             mov_text_cleanup_ftab(m);
239             return AVERROR(ENOMEM);
240         }
241         memcpy(m->ftab[i].font, tx3g_ptr, font_length);
242         m->ftab[i].font[font_length] = '\0';
243         tx3g_ptr = tx3g_ptr + font_length;
244     }
245     if (j >= 0)
246         m->d.font = m->ftab[j].font;
247     return 0;
248 }
249
250 static int decode_twrp(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
251 {
252     m->box_flags |= TWRP_BOX;
253     m->w.wrap_flag = *tsmb++;
254     return 0;
255 }
256
257 static int decode_hlit(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
258 {
259     m->box_flags |= HLIT_BOX;
260     m->h.hlit_start = AV_RB16(tsmb);
261     tsmb += 2;
262     m->h.hlit_end = AV_RB16(tsmb);
263     tsmb += 2;
264     return 0;
265 }
266
267 static int decode_hclr(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
268 {
269     m->box_flags |= HCLR_BOX;
270     memcpy(m->c.hlit_color, tsmb, 4);
271     tsmb += 4;
272     return 0;
273 }
274
275 static int decode_styl(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
276 {
277     int i;
278     int style_entries = AV_RB16(tsmb);
279     StyleBox *tmp;
280     tsmb += 2;
281     // A single style record is of length 12 bytes.
282     if (m->tracksize + m->size_var + 2 + style_entries * 12 > avpkt->size)
283         return -1;
284
285     tmp = av_realloc_array(m->s, style_entries, sizeof(*m->s));
286     if (!tmp)
287         return AVERROR(ENOMEM);
288     m->s             = tmp;
289     m->style_entries = style_entries;
290
291     m->box_flags |= STYL_BOX;
292     for(i = 0; i < m->style_entries; i++) {
293         StyleBox *style = &m->s[i];
294         style->style_start = AV_RB16(tsmb);
295         tsmb += 2;
296         style->style_end = AV_RB16(tsmb);
297
298         if (   style->style_end < style->style_start
299             || (i && style->style_start < m->s[i - 1].style_end)) {
300             mov_text_cleanup(m);
301             return AVERROR(ENOMEM);
302         }
303
304         tsmb += 2;
305         style->style_fontID = AV_RB16(tsmb);
306         tsmb += 2;
307         style->style_flag = AV_RB8(tsmb);
308         style->bold      = !!(style->style_flag & STYLE_FLAG_BOLD);
309         style->italic    = !!(style->style_flag & STYLE_FLAG_ITALIC);
310         style->underline = !!(style->style_flag & STYLE_FLAG_UNDERLINE);
311         tsmb++;
312         style->fontsize = AV_RB8(tsmb);
313         tsmb++;
314         style->color = AV_RB24(tsmb);
315         tsmb += 3;
316         style->alpha = AV_RB8(tsmb);
317         tsmb++;
318     }
319     return 0;
320 }
321
322 static const Box box_types[] = {
323     { MKBETAG('s','t','y','l'), 2, decode_styl },
324     { MKBETAG('h','l','i','t'), 4, decode_hlit },
325     { MKBETAG('h','c','l','r'), 4, decode_hclr },
326     { MKBETAG('t','w','r','p'), 1, decode_twrp }
327 };
328
329 const static size_t box_count = FF_ARRAY_ELEMS(box_types);
330
331 // Return byte length of the UTF-8 sequence starting at text[0]. 0 on error.
332 static int get_utf8_length_at(const char *text, const char *text_end)
333 {
334     const char *start = text;
335     int err = 0;
336     uint32_t c;
337     GET_UTF8(c, text < text_end ? (uint8_t)*text++ : (err = 1, 0), goto error;);
338     if (err)
339         goto error;
340     return text - start;
341 error:
342     return 0;
343 }
344
345 static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end,
346                        AVCodecContext *avctx)
347 {
348     MovTextContext *m = avctx->priv_data;
349     int i = 0;
350     int text_pos = 0;
351     int style_active = 0;
352     int entry = 0;
353     int color = m->d.color;
354
355     if (text < text_end && m->box_flags & TWRP_BOX) {
356         if (m->w.wrap_flag == 1) {
357             av_bprintf(buf, "{\\q1}"); /* End of line wrap */
358         } else {
359             av_bprintf(buf, "{\\q2}"); /* No wrap */
360         }
361     }
362
363     while (text < text_end) {
364         int len;
365
366         if ((m->box_flags & STYL_BOX) && entry < m->style_entries) {
367             const StyleBox *style = &m->s[entry];
368             if (text_pos == style->style_start) {
369                 style_active = 1;
370                 if (style->bold ^ m->d.bold)
371                     av_bprintf(buf, "{\\b%d}", style->bold);
372                 if (style->italic ^ m->d.italic)
373                     av_bprintf(buf, "{\\i%d}", style->italic);
374                 if (style->underline ^ m->d.underline)
375                     av_bprintf(buf, "{\\u%d}", style->underline);
376                 if (style->fontsize != m->d.fontsize)
377                     av_bprintf(buf, "{\\fs%d}", style->fontsize);
378                 if (style->style_fontID != m->d.fontID)
379                     for (i = 0; i < m->ftab_entries; i++) {
380                         if (style->style_fontID == m->ftab[i].fontID)
381                             av_bprintf(buf, "{\\fn%s}", m->ftab[i].font);
382                     }
383                 if (m->d.color != style->color) {
384                     color = style->color;
385                     av_bprintf(buf, "{\\1c&H%X&}", RGB_TO_BGR(color));
386                 }
387                 if (m->d.alpha != style->alpha)
388                     av_bprintf(buf, "{\\1a&H%02X&}", 255 - style->alpha);
389             }
390             if (text_pos == style->style_end) {
391                 if (style_active) {
392                     av_bprintf(buf, "{\\r}");
393                     style_active = 0;
394                     color = m->d.color;
395                 }
396                 entry++;
397             }
398         }
399         if (m->box_flags & HLIT_BOX) {
400             if (text_pos == m->h.hlit_start) {
401                 /* If hclr box is present, set the secondary color to the color
402                  * specified. Otherwise, set primary color to white and secondary
403                  * color to black. These colors will come from TextSampleModifier
404                  * boxes in future and inverse video technique for highlight will
405                  * be implemented.
406                  */
407                 if (m->box_flags & HCLR_BOX) {
408                     av_bprintf(buf, "{\\2c&H%02x%02x%02x&}", m->c.hlit_color[2],
409                                 m->c.hlit_color[1], m->c.hlit_color[0]);
410                 } else {
411                     av_bprintf(buf, "{\\1c&H000000&}{\\2c&HFFFFFF&}");
412                 }
413             }
414             if (text_pos == m->h.hlit_end) {
415                 if (m->box_flags & HCLR_BOX) {
416                     av_bprintf(buf, "{\\2c&H%X&}", RGB_TO_BGR(m->d.color));
417                 } else {
418                     av_bprintf(buf, "{\\1c&H%X&}{\\2c&H%X&}",
419                                RGB_TO_BGR(color), RGB_TO_BGR(m->d.color));
420                 }
421             }
422         }
423
424         len = get_utf8_length_at(text, text_end);
425         if (len < 1) {
426             av_log(avctx, AV_LOG_ERROR, "invalid UTF-8 byte in subtitle\n");
427             len = 1;
428         }
429         for (i = 0; i < len; i++) {
430             switch (*text) {
431             case '\r':
432                 break;
433             case '\n':
434                 av_bprintf(buf, "\\N");
435                 break;
436             default:
437                 av_bprint_chars(buf, *text, 1);
438                 break;
439             }
440             text++;
441         }
442         text_pos++;
443     }
444
445     return 0;
446 }
447
448 static int mov_text_init(AVCodecContext *avctx) {
449     /*
450      * TODO: Handle the default text style.
451      * NB: Most players ignore styles completely, with the result that
452      * it's very common to find files where the default style is broken
453      * and respecting it results in a worse experience than ignoring it.
454      */
455     int ret;
456     MovTextContext *m = avctx->priv_data;
457     ret = mov_text_tx3g(avctx, m);
458     if (ret == 0) {
459         if (!m->frame_width || !m->frame_height) {
460             m->frame_width = ASS_DEFAULT_PLAYRESX;
461             m->frame_height = ASS_DEFAULT_PLAYRESY;
462         }
463         return ff_ass_subtitle_header_full(avctx,
464                     m->frame_width, m->frame_height,
465                     m->d.font, m->d.fontsize,
466                     (255U - m->d.alpha) << 24 | RGB_TO_BGR(m->d.color),
467                     (255U - m->d.alpha) << 24 | RGB_TO_BGR(m->d.color),
468                     (255U - m->d.back_alpha) << 24 | RGB_TO_BGR(m->d.back_color),
469                     (255U - m->d.back_alpha) << 24 | RGB_TO_BGR(m->d.back_color),
470                     m->d.bold, m->d.italic, m->d.underline,
471                     ASS_DEFAULT_BORDERSTYLE, m->d.alignment);
472     } else
473         return ff_ass_subtitle_header_default(avctx);
474 }
475
476 static int mov_text_decode_frame(AVCodecContext *avctx,
477                             void *data, int *got_sub_ptr, AVPacket *avpkt)
478 {
479     AVSubtitle *sub = data;
480     MovTextContext *m = avctx->priv_data;
481     int ret;
482     AVBPrint buf;
483     char *ptr = avpkt->data;
484     char *end;
485     int text_length, tsmb_type, ret_tsmb;
486     uint64_t tsmb_size;
487     const uint8_t *tsmb;
488     size_t i;
489
490     if (!ptr || avpkt->size < 2)
491         return AVERROR_INVALIDDATA;
492
493     /*
494      * A packet of size two with value zero is an empty subtitle
495      * used to mark the end of the previous non-empty subtitle.
496      * We can just drop them here as we have duration information
497      * already. If the value is non-zero, then it's technically a
498      * bad packet.
499      */
500     if (avpkt->size == 2)
501         return AV_RB16(ptr) == 0 ? 0 : AVERROR_INVALIDDATA;
502
503     /*
504      * The first two bytes of the packet are the length of the text string
505      * In complex cases, there are style descriptors appended to the string
506      * so we can't just assume the packet size is the string size.
507      */
508     text_length = AV_RB16(ptr);
509     end = ptr + FFMIN(2 + text_length, avpkt->size);
510     ptr += 2;
511
512     mov_text_cleanup(m);
513
514     tsmb_size = 0;
515     m->tracksize = 2 + text_length;
516     m->style_entries = 0;
517     m->box_flags = 0;
518     // Note that the spec recommends lines be no longer than 2048 characters.
519     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
520     if (text_length + 2 != avpkt->size) {
521         while (m->tracksize + 8 <= avpkt->size) {
522             // A box is a minimum of 8 bytes.
523             tsmb = ptr + m->tracksize - 2;
524             tsmb_size = AV_RB32(tsmb);
525             tsmb += 4;
526             tsmb_type = AV_RB32(tsmb);
527             tsmb += 4;
528
529             if (tsmb_size == 1) {
530                 if (m->tracksize + 16 > avpkt->size)
531                     break;
532                 tsmb_size = AV_RB64(tsmb);
533                 tsmb += 8;
534                 m->size_var = 16;
535             } else
536                 m->size_var = 8;
537             //size_var is equal to 8 or 16 depending on the size of box
538
539             if (tsmb_size == 0) {
540                 av_log(avctx, AV_LOG_ERROR, "tsmb_size is 0\n");
541                 return AVERROR_INVALIDDATA;
542             }
543
544             if (tsmb_size > avpkt->size - m->tracksize)
545                 break;
546
547             for (i = 0; i < box_count; i++) {
548                 if (tsmb_type == box_types[i].type) {
549                     if (m->tracksize + m->size_var + box_types[i].base_size > avpkt->size)
550                         break;
551                     ret_tsmb = box_types[i].decode(tsmb, m, avpkt);
552                     if (ret_tsmb == -1)
553                         break;
554                 }
555             }
556             m->tracksize = m->tracksize + tsmb_size;
557         }
558         text_to_ass(&buf, ptr, end, avctx);
559         mov_text_cleanup(m);
560     } else
561         text_to_ass(&buf, ptr, end, avctx);
562
563     ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
564     av_bprint_finalize(&buf, NULL);
565     if (ret < 0)
566         return ret;
567     *got_sub_ptr = sub->num_rects > 0;
568     return avpkt->size;
569 }
570
571 static int mov_text_decode_close(AVCodecContext *avctx)
572 {
573     MovTextContext *m = avctx->priv_data;
574     mov_text_cleanup_ftab(m);
575     mov_text_cleanup(m);
576     return 0;
577 }
578
579 static void mov_text_flush(AVCodecContext *avctx)
580 {
581     MovTextContext *m = avctx->priv_data;
582     if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
583         m->readorder = 0;
584 }
585
586 #define OFFSET(x) offsetof(MovTextContext, x)
587 #define FLAGS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM
588 static const AVOption options[] = {
589     { "width", "Frame width, usually video width", OFFSET(frame_width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
590     { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
591     { NULL },
592 };
593
594 static const AVClass mov_text_decoder_class = {
595     .class_name = "MOV text decoder",
596     .item_name  = av_default_item_name,
597     .option     = options,
598     .version    = LIBAVUTIL_VERSION_INT,
599 };
600
601 AVCodec ff_movtext_decoder = {
602     .name         = "mov_text",
603     .long_name    = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
604     .type         = AVMEDIA_TYPE_SUBTITLE,
605     .id           = AV_CODEC_ID_MOV_TEXT,
606     .priv_data_size = sizeof(MovTextContext),
607     .priv_class   = &mov_text_decoder_class,
608     .init         = mov_text_init,
609     .decode       = mov_text_decode_frame,
610     .close        = mov_text_decode_close,
611     .flush        = mov_text_flush,
612 };