avcodec/movtextdec: check that ftab has been allocated before dereferencing it
[ffmpeg.git] / 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/avstring.h"
25 #include "libavutil/common.h"
26 #include "libavutil/bprint.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/mem.h"
29
30 #define STYLE_FLAG_BOLD         (1<<0)
31 #define STYLE_FLAG_ITALIC       (1<<1)
32 #define STYLE_FLAG_UNDERLINE    (1<<2)
33
34 #define BOX_SIZE_INITIAL    40
35
36 #define STYL_BOX   (1<<0)
37 #define HLIT_BOX   (1<<1)
38 #define HCLR_BOX   (1<<2)
39
40 typedef struct {
41     uint16_t fontID;
42     char *font;
43 } FontRecord;
44
45 typedef struct {
46     uint16_t style_start;
47     uint16_t style_end;
48     uint8_t style_flag;
49     uint8_t fontsize;
50     uint16_t style_fontID;
51 } StyleBox;
52
53 typedef struct {
54     uint16_t hlit_start;
55     uint16_t hlit_end;
56 } HighlightBox;
57
58 typedef struct {
59    uint8_t hlit_color[4];
60 } HilightcolorBox;
61
62 typedef struct {
63     StyleBox **s;
64     StyleBox *s_temp;
65     HighlightBox h;
66     HilightcolorBox c;
67     FontRecord **ftab;
68     FontRecord *ftab_temp;
69     uint8_t box_flags;
70     uint16_t style_entries, ftab_entries;
71     uint64_t tracksize;
72     int size_var;
73     int count_s, count_f;
74 } MovTextContext;
75
76 typedef struct {
77     uint32_t type;
78     size_t base_size;
79     int (*decode)(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt);
80 } Box;
81
82 static void mov_text_cleanup(MovTextContext *m)
83 {
84     int i;
85     if (m->box_flags & STYL_BOX) {
86         for(i = 0; i < m->count_s; i++) {
87             av_freep(&m->s[i]);
88         }
89         av_freep(&m->s);
90     }
91 }
92
93 static void mov_text_cleanup_ftab(MovTextContext *m)
94 {
95     int i;
96     if (m->ftab) {
97         for(i = 0; i < m->count_f; i++) {
98             av_freep(&m->ftab[i]->font);
99             av_freep(&m->ftab[i]);
100         }
101     }
102     av_freep(&m->ftab);
103 }
104
105 static int mov_text_tx3g(AVCodecContext *avctx, MovTextContext *m)
106 {
107     char *tx3g_ptr = avctx->extradata;
108     int i, box_size, font_length;
109
110     m->ftab_entries = 0;
111     box_size = BOX_SIZE_INITIAL; /* Size till ftab_entries */
112     if (avctx->extradata_size < box_size)
113         return -1;
114
115     // Display Flags
116     tx3g_ptr += 4;
117     // Alignment
118     tx3g_ptr += 2;
119     // Background Color
120     tx3g_ptr += 4;
121     // BoxRecord
122     tx3g_ptr += 8;
123     // StyleRecord
124     tx3g_ptr += 12;
125     // FontRecord
126     // FontRecord Size
127     tx3g_ptr += 4;
128     // ftab
129     tx3g_ptr += 4;
130
131     m->ftab_entries = AV_RB16(tx3g_ptr);
132     tx3g_ptr += 2;
133
134     for (i = 0; i < m->ftab_entries; i++) {
135
136         box_size += 3;
137         if (avctx->extradata_size < box_size) {
138             mov_text_cleanup_ftab(m);
139             m->ftab_entries = 0;
140             return -1;
141         }
142         m->ftab_temp = av_malloc(sizeof(*m->ftab_temp));
143         if (!m->ftab_temp) {
144             mov_text_cleanup_ftab(m);
145             return AVERROR(ENOMEM);
146         }
147         m->ftab_temp->fontID = AV_RB16(tx3g_ptr);
148         tx3g_ptr += 2;
149         font_length = *tx3g_ptr++;
150
151         box_size = box_size + font_length;
152         if (avctx->extradata_size < box_size) {
153             mov_text_cleanup_ftab(m);
154             m->ftab_entries = 0;
155             return -1;
156         }
157         m->ftab_temp->font = av_malloc(font_length + 1);
158         if (!m->ftab_temp->font) {
159             mov_text_cleanup_ftab(m);
160             return AVERROR(ENOMEM);
161         }
162         memcpy(m->ftab_temp->font, tx3g_ptr, font_length);
163         m->ftab_temp->font[font_length] = '\0';
164         av_dynarray_add(&m->ftab, &m->count_f, m->ftab_temp);
165         if (!m->ftab) {
166             mov_text_cleanup_ftab(m);
167             return AVERROR(ENOMEM);
168         }
169         tx3g_ptr = tx3g_ptr + font_length;
170     }
171     return 0;
172 }
173
174 static int decode_hlit(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
175 {
176     m->box_flags |= HLIT_BOX;
177     m->h.hlit_start = AV_RB16(tsmb);
178     tsmb += 2;
179     m->h.hlit_end = AV_RB16(tsmb);
180     tsmb += 2;
181     return 0;
182 }
183
184 static int decode_hclr(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
185 {
186     m->box_flags |= HCLR_BOX;
187     memcpy(m->c.hlit_color, tsmb, 4);
188     tsmb += 4;
189     return 0;
190 }
191
192 static int decode_styl(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
193 {
194     int i;
195     m->style_entries = AV_RB16(tsmb);
196     tsmb += 2;
197     // A single style record is of length 12 bytes.
198     if (m->tracksize + m->size_var + 2 + m->style_entries * 12 > avpkt->size)
199         return -1;
200
201     m->box_flags |= STYL_BOX;
202     for(i = 0; i < m->style_entries; i++) {
203         m->s_temp = av_malloc(sizeof(*m->s_temp));
204         if (!m->s_temp) {
205             mov_text_cleanup(m);
206             return AVERROR(ENOMEM);
207         }
208         m->s_temp->style_start = AV_RB16(tsmb);
209         tsmb += 2;
210         m->s_temp->style_end = AV_RB16(tsmb);
211         tsmb += 2;
212         m->s_temp->style_fontID = AV_RB16(tsmb);
213         tsmb += 2;
214         m->s_temp->style_flag = AV_RB8(tsmb);
215         tsmb++;
216         m->s_temp->fontsize = AV_RB8(tsmb);
217         av_dynarray_add(&m->s, &m->count_s, m->s_temp);
218         if(!m->s) {
219             mov_text_cleanup(m);
220             return AVERROR(ENOMEM);
221         }
222         tsmb++;
223         // text-color-rgba
224         tsmb += 4;
225     }
226     return 0;
227 }
228
229 static const Box box_types[] = {
230     { MKBETAG('s','t','y','l'), 2, decode_styl },
231     { MKBETAG('h','l','i','t'), 4, decode_hlit },
232     { MKBETAG('h','c','l','r'), 4, decode_hclr }
233 };
234
235 const static size_t box_count = FF_ARRAY_ELEMS(box_types);
236
237 static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end,
238                         MovTextContext *m)
239 {
240     int i = 0;
241     int j = 0;
242     int text_pos = 0;
243     while (text < text_end) {
244         if (m->box_flags & STYL_BOX) {
245             for (i = 0; i < m->style_entries; i++) {
246                 if (m->s[i]->style_flag && text_pos == m->s[i]->style_end) {
247                     av_bprintf(buf, "{\\r}");
248                 }
249             }
250             for (i = 0; i < m->style_entries; i++) {
251                 if (m->s[i]->style_flag && text_pos == m->s[i]->style_start) {
252                     if (m->s[i]->style_flag & STYLE_FLAG_BOLD)
253                         av_bprintf(buf, "{\\b1}");
254                     if (m->s[i]->style_flag & STYLE_FLAG_ITALIC)
255                         av_bprintf(buf, "{\\i1}");
256                     if (m->s[i]->style_flag & STYLE_FLAG_UNDERLINE)
257                         av_bprintf(buf, "{\\u1}");
258                     av_bprintf(buf, "{\\fs%d}", m->s[i]->fontsize);
259                     for (j = 0; j < m->ftab_entries; j++) {
260                         if (m->s[i]->style_fontID == m->ftab[j]->fontID)
261                             av_bprintf(buf, "{\\fn%s}", m->ftab[j]->font);
262                     }
263                 }
264             }
265         }
266         if (m->box_flags & HLIT_BOX) {
267             if (text_pos == m->h.hlit_start) {
268                 /* If hclr box is present, set the secondary color to the color
269                  * specified. Otherwise, set primary color to white and secondary
270                  * color to black. These colors will come from TextSampleModifier
271                  * boxes in future and inverse video technique for highlight will
272                  * be implemented.
273                  */
274                 if (m->box_flags & HCLR_BOX) {
275                     av_bprintf(buf, "{\\2c&H%02x%02x%02x&}", m->c.hlit_color[2],
276                                 m->c.hlit_color[1], m->c.hlit_color[0]);
277                 } else {
278                     av_bprintf(buf, "{\\1c&H000000&}{\\2c&HFFFFFF&}");
279                 }
280             }
281             if (text_pos == m->h.hlit_end) {
282                 if (m->box_flags & HCLR_BOX) {
283                     av_bprintf(buf, "{\\2c&H000000&}");
284                 } else {
285                     av_bprintf(buf, "{\\1c&HFFFFFF&}{\\2c&H000000&}");
286                 }
287             }
288         }
289
290         switch (*text) {
291         case '\r':
292             break;
293         case '\n':
294             av_bprintf(buf, "\\N");
295             break;
296         default:
297             av_bprint_chars(buf, *text, 1);
298             break;
299         }
300         text++;
301         text_pos++;
302     }
303
304     return 0;
305 }
306
307 static int mov_text_init(AVCodecContext *avctx) {
308     /*
309      * TODO: Handle the default text style.
310      * NB: Most players ignore styles completely, with the result that
311      * it's very common to find files where the default style is broken
312      * and respecting it results in a worse experience than ignoring it.
313      */
314     MovTextContext *m = avctx->priv_data;
315     mov_text_tx3g(avctx, m);
316     return ff_ass_subtitle_header_default(avctx);
317 }
318
319 static int mov_text_decode_frame(AVCodecContext *avctx,
320                             void *data, int *got_sub_ptr, AVPacket *avpkt)
321 {
322     AVSubtitle *sub = data;
323     MovTextContext *m = avctx->priv_data;
324     int ret, ts_start, ts_end;
325     AVBPrint buf;
326     char *ptr = avpkt->data;
327     char *end;
328     int text_length, tsmb_type, ret_tsmb;
329     uint64_t tsmb_size;
330     const uint8_t *tsmb;
331
332     if (!ptr || avpkt->size < 2)
333         return AVERROR_INVALIDDATA;
334
335     /*
336      * A packet of size two with value zero is an empty subtitle
337      * used to mark the end of the previous non-empty subtitle.
338      * We can just drop them here as we have duration information
339      * already. If the value is non-zero, then it's technically a
340      * bad packet.
341      */
342     if (avpkt->size == 2)
343         return AV_RB16(ptr) == 0 ? 0 : AVERROR_INVALIDDATA;
344
345     /*
346      * The first two bytes of the packet are the length of the text string
347      * In complex cases, there are style descriptors appended to the string
348      * so we can't just assume the packet size is the string size.
349      */
350     text_length = AV_RB16(ptr);
351     end = ptr + FFMIN(2 + text_length, avpkt->size);
352     ptr += 2;
353
354     ts_start = av_rescale_q(avpkt->pts,
355                             avctx->time_base,
356                             (AVRational){1,100});
357     ts_end   = av_rescale_q(avpkt->pts + avpkt->duration,
358                             avctx->time_base,
359                             (AVRational){1,100});
360
361     tsmb_size = 0;
362     m->tracksize = 2 + text_length;
363     m->style_entries = 0;
364     m->box_flags = 0;
365     m->count_s = 0;
366     m->count_f = 0;
367     // Note that the spec recommends lines be no longer than 2048 characters.
368     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
369     if (text_length + 2 != avpkt->size) {
370         while (m->tracksize + 8 <= avpkt->size) {
371             // A box is a minimum of 8 bytes.
372             tsmb = ptr + m->tracksize - 2;
373             tsmb_size = AV_RB32(tsmb);
374             tsmb += 4;
375             tsmb_type = AV_RB32(tsmb);
376             tsmb += 4;
377
378             if (tsmb_size == 1) {
379                 if (m->tracksize + 16 > avpkt->size)
380                     break;
381                 tsmb_size = AV_RB64(tsmb);
382                 tsmb += 8;
383                 m->size_var = 16;
384             } else
385                 m->size_var = 8;
386             //size_var is equal to 8 or 16 depending on the size of box
387
388             if (m->tracksize + tsmb_size > avpkt->size)
389                 break;
390
391             for (size_t i = 0; i < box_count; i++) {
392                 if (tsmb_type == box_types[i].type) {
393                     if (m->tracksize + m->size_var + box_types[i].base_size > avpkt->size)
394                         break;
395                     ret_tsmb = box_types[i].decode(tsmb, m, avpkt);
396                     if (ret_tsmb == -1)
397                         break;
398                 }
399             }
400             m->tracksize = m->tracksize + tsmb_size;
401         }
402         text_to_ass(&buf, ptr, end, m);
403         mov_text_cleanup(m);
404     } else
405         text_to_ass(&buf, ptr, end, m);
406
407     ret = ff_ass_add_rect_bprint(sub, &buf, ts_start, ts_end - ts_start);
408     av_bprint_finalize(&buf, NULL);
409     if (ret < 0)
410         return ret;
411     *got_sub_ptr = sub->num_rects > 0;
412     return avpkt->size;
413 }
414
415 static int mov_text_decode_close(AVCodecContext *avctx)
416 {
417     MovTextContext *m = avctx->priv_data;
418     mov_text_cleanup_ftab(m);
419     return 0;
420 }
421
422 AVCodec ff_movtext_decoder = {
423     .name         = "mov_text",
424     .long_name    = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
425     .type         = AVMEDIA_TYPE_SUBTITLE,
426     .id           = AV_CODEC_ID_MOV_TEXT,
427     .priv_data_size = sizeof(MovTextContext),
428     .init         = mov_text_init,
429     .decode       = mov_text_decode_frame,
430     .close        = mov_text_decode_close,
431 };