]> git.sesse.net Git - ffmpeg/blob - libavcodec/movtextdec.c
avformat/argo_asf: initialise file header inline
[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     StyleBox *s_temp;
103     HighlightBox h;
104     HilightcolorBox c;
105     FontRecord **ftab;
106     FontRecord *ftab_temp;
107     TextWrapBox w;
108     MovTextDefault d;
109     uint8_t box_flags;
110     uint16_t style_entries, ftab_entries;
111     uint64_t tracksize;
112     int size_var;
113     int count_s, count_f;
114     int readorder;
115     int frame_width;
116     int frame_height;
117 } MovTextContext;
118
119 typedef struct {
120     uint32_t type;
121     size_t base_size;
122     int (*decode)(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt);
123 } Box;
124
125 static void mov_text_cleanup(MovTextContext *m)
126 {
127     int i;
128     if (m->box_flags & STYL_BOX) {
129         for(i = 0; i < m->count_s; i++) {
130             av_freep(&m->s[i]);
131         }
132         av_freep(&m->s);
133         m->count_s = 0;
134         m->style_entries = 0;
135     }
136 }
137
138 static void mov_text_cleanup_ftab(MovTextContext *m)
139 {
140     int i;
141     if (m->ftab_temp)
142         av_freep(&m->ftab_temp->font);
143     av_freep(&m->ftab_temp);
144     if (m->ftab) {
145         for(i = 0; i < m->count_f; i++) {
146             av_freep(&m->ftab[i]->font);
147             av_freep(&m->ftab[i]);
148         }
149     }
150     av_freep(&m->ftab);
151 }
152
153 static int mov_text_tx3g(AVCodecContext *avctx, MovTextContext *m)
154 {
155     uint8_t *tx3g_ptr = avctx->extradata;
156     int i, box_size, font_length;
157     int8_t v_align, h_align;
158     StyleBox s_default;
159
160     m->count_f = 0;
161     m->ftab_entries = 0;
162     box_size = BOX_SIZE_INITIAL; /* Size till ftab_entries */
163     if (avctx->extradata_size < box_size)
164         return -1;
165
166     // Display Flags
167     tx3g_ptr += 4;
168     // Alignment
169     h_align = *tx3g_ptr++;
170     v_align = *tx3g_ptr++;
171     if (h_align == 0) {
172         if (v_align == 0)
173             m->d.alignment = TOP_LEFT;
174         if (v_align == 1)
175             m->d.alignment = MIDDLE_LEFT;
176         if (v_align == -1)
177             m->d.alignment = BOTTOM_LEFT;
178     }
179     if (h_align == 1) {
180         if (v_align == 0)
181             m->d.alignment = TOP_CENTER;
182         if (v_align == 1)
183             m->d.alignment = MIDDLE_CENTER;
184         if (v_align == -1)
185             m->d.alignment = BOTTOM_CENTER;
186     }
187     if (h_align == -1) {
188         if (v_align == 0)
189             m->d.alignment = TOP_RIGHT;
190         if (v_align == 1)
191             m->d.alignment = MIDDLE_RIGHT;
192         if (v_align == -1)
193             m->d.alignment = BOTTOM_RIGHT;
194     }
195     // Background Color
196     m->d.back_color = AV_RB24(tx3g_ptr);
197     tx3g_ptr += 3;
198     m->d.back_alpha = AV_RB8(tx3g_ptr);
199     tx3g_ptr += 1;
200     // BoxRecord
201     tx3g_ptr += 8;
202     // StyleRecord
203     tx3g_ptr += 4;
204     // fontID
205     m->d.fontID = AV_RB16(tx3g_ptr);
206     tx3g_ptr += 2;
207     // face-style-flags
208     s_default.style_flag = *tx3g_ptr++;
209     m->d.bold = !!(s_default.style_flag & STYLE_FLAG_BOLD);
210     m->d.italic = !!(s_default.style_flag & STYLE_FLAG_ITALIC);
211     m->d.underline = !!(s_default.style_flag & STYLE_FLAG_UNDERLINE);
212     // fontsize
213     m->d.fontsize = *tx3g_ptr++;
214     // Primary color
215     m->d.color = AV_RB24(tx3g_ptr);
216     tx3g_ptr += 3;
217     m->d.alpha = AV_RB8(tx3g_ptr);
218     tx3g_ptr += 1;
219     // FontRecord
220     // FontRecord Size
221     tx3g_ptr += 4;
222     // ftab
223     tx3g_ptr += 4;
224
225     m->ftab_entries = AV_RB16(tx3g_ptr);
226     tx3g_ptr += 2;
227
228     for (i = 0; i < m->ftab_entries; i++) {
229
230         box_size += 3;
231         if (avctx->extradata_size < box_size) {
232             mov_text_cleanup_ftab(m);
233             m->ftab_entries = 0;
234             return -1;
235         }
236         m->ftab_temp = av_mallocz(sizeof(*m->ftab_temp));
237         if (!m->ftab_temp) {
238             mov_text_cleanup_ftab(m);
239             return AVERROR(ENOMEM);
240         }
241         m->ftab_temp->fontID = AV_RB16(tx3g_ptr);
242         tx3g_ptr += 2;
243         font_length = *tx3g_ptr++;
244
245         box_size = box_size + font_length;
246         if (avctx->extradata_size < box_size) {
247             mov_text_cleanup_ftab(m);
248             m->ftab_entries = 0;
249             return -1;
250         }
251         m->ftab_temp->font = av_malloc(font_length + 1);
252         if (!m->ftab_temp->font) {
253             mov_text_cleanup_ftab(m);
254             return AVERROR(ENOMEM);
255         }
256         memcpy(m->ftab_temp->font, tx3g_ptr, font_length);
257         m->ftab_temp->font[font_length] = '\0';
258         av_dynarray_add(&m->ftab, &m->count_f, m->ftab_temp);
259         if (!m->ftab) {
260             mov_text_cleanup_ftab(m);
261             return AVERROR(ENOMEM);
262         }
263         m->ftab_temp = NULL;
264         tx3g_ptr = tx3g_ptr + font_length;
265     }
266     // In case of broken header, init default font
267     m->d.font = ASS_DEFAULT_FONT;
268     for (i = 0; i < m->ftab_entries; i++) {
269         if (m->d.fontID == m->ftab[i]->fontID)
270             m->d.font = m->ftab[i]->font;
271     }
272     return 0;
273 }
274
275 static int decode_twrp(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
276 {
277     m->box_flags |= TWRP_BOX;
278     m->w.wrap_flag = *tsmb++;
279     return 0;
280 }
281
282 static int decode_hlit(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
283 {
284     m->box_flags |= HLIT_BOX;
285     m->h.hlit_start = AV_RB16(tsmb);
286     tsmb += 2;
287     m->h.hlit_end = AV_RB16(tsmb);
288     tsmb += 2;
289     return 0;
290 }
291
292 static int decode_hclr(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
293 {
294     m->box_flags |= HCLR_BOX;
295     memcpy(m->c.hlit_color, tsmb, 4);
296     tsmb += 4;
297     return 0;
298 }
299
300 static int decode_styl(const uint8_t *tsmb, MovTextContext *m, AVPacket *avpkt)
301 {
302     int i;
303     int style_entries = AV_RB16(tsmb);
304     tsmb += 2;
305     // A single style record is of length 12 bytes.
306     if (m->tracksize + m->size_var + 2 + style_entries * 12 > avpkt->size)
307         return -1;
308
309     m->style_entries = style_entries;
310
311     m->box_flags |= STYL_BOX;
312     for(i = 0; i < m->style_entries; i++) {
313         m->s_temp = av_malloc(sizeof(*m->s_temp));
314         if (!m->s_temp) {
315             mov_text_cleanup(m);
316             return AVERROR(ENOMEM);
317         }
318         m->s_temp->style_start = AV_RB16(tsmb);
319         tsmb += 2;
320         m->s_temp->style_end = AV_RB16(tsmb);
321
322         if (   m->s_temp->style_end < m->s_temp->style_start
323             || (m->count_s && m->s_temp->style_start < m->s[m->count_s - 1]->style_end)) {
324             av_freep(&m->s_temp);
325             mov_text_cleanup(m);
326             return AVERROR(ENOMEM);
327         }
328
329         tsmb += 2;
330         m->s_temp->style_fontID = AV_RB16(tsmb);
331         tsmb += 2;
332         m->s_temp->style_flag = AV_RB8(tsmb);
333         m->s_temp->bold = !!(m->s_temp->style_flag & STYLE_FLAG_BOLD);
334         m->s_temp->italic = !!(m->s_temp->style_flag & STYLE_FLAG_ITALIC);
335         m->s_temp->underline = !!(m->s_temp->style_flag & STYLE_FLAG_UNDERLINE);
336         tsmb++;
337         m->s_temp->fontsize = AV_RB8(tsmb);
338         tsmb++;
339         m->s_temp->color = AV_RB24(tsmb);
340         tsmb += 3;
341         m->s_temp->alpha = AV_RB8(tsmb);
342         tsmb++;
343         av_dynarray_add(&m->s, &m->count_s, m->s_temp);
344         if(!m->s) {
345             mov_text_cleanup(m);
346             return AVERROR(ENOMEM);
347         }
348     }
349     return 0;
350 }
351
352 static const Box box_types[] = {
353     { MKBETAG('s','t','y','l'), 2, decode_styl },
354     { MKBETAG('h','l','i','t'), 4, decode_hlit },
355     { MKBETAG('h','c','l','r'), 4, decode_hclr },
356     { MKBETAG('t','w','r','p'), 1, decode_twrp }
357 };
358
359 const static size_t box_count = FF_ARRAY_ELEMS(box_types);
360
361 // Return byte length of the UTF-8 sequence starting at text[0]. 0 on error.
362 static int get_utf8_length_at(const char *text, const char *text_end)
363 {
364     const char *start = text;
365     int err = 0;
366     uint32_t c;
367     GET_UTF8(c, text < text_end ? (uint8_t)*text++ : (err = 1, 0), goto error;);
368     if (err)
369         goto error;
370     return text - start;
371 error:
372     return 0;
373 }
374
375 static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end,
376                        AVCodecContext *avctx)
377 {
378     MovTextContext *m = avctx->priv_data;
379     int i = 0;
380     int text_pos = 0;
381     int style_active = 0;
382     int entry = 0;
383     int color = m->d.color;
384
385     if (text < text_end && m->box_flags & TWRP_BOX) {
386         if (m->w.wrap_flag == 1) {
387             av_bprintf(buf, "{\\q1}"); /* End of line wrap */
388         } else {
389             av_bprintf(buf, "{\\q2}"); /* No wrap */
390         }
391     }
392
393     while (text < text_end) {
394         int len;
395
396         if ((m->box_flags & STYL_BOX) && entry < m->style_entries) {
397             if (text_pos == m->s[entry]->style_start) {
398                 style_active = 1;
399                 if (m->s[entry]->bold ^ m->d.bold)
400                     av_bprintf(buf, "{\\b%d}", m->s[entry]->bold);
401                 if (m->s[entry]->italic ^ m->d.italic)
402                     av_bprintf(buf, "{\\i%d}", m->s[entry]->italic);
403                 if (m->s[entry]->underline ^ m->d.underline)
404                     av_bprintf(buf, "{\\u%d}", m->s[entry]->underline);
405                 if (m->s[entry]->fontsize != m->d.fontsize)
406                     av_bprintf(buf, "{\\fs%d}", m->s[entry]->fontsize);
407                 if (m->s[entry]->style_fontID != m->d.fontID)
408                     for (i = 0; i < m->ftab_entries; i++) {
409                         if (m->s[entry]->style_fontID == m->ftab[i]->fontID)
410                             av_bprintf(buf, "{\\fn%s}", m->ftab[i]->font);
411                     }
412                 if (m->d.color != m->s[entry]->color) {
413                     color = m->s[entry]->color;
414                     av_bprintf(buf, "{\\1c&H%X&}", RGB_TO_BGR(color));
415                 }
416                 if (m->d.alpha != m->s[entry]->alpha)
417                     av_bprintf(buf, "{\\1a&H%02X&}", 255 - m->s[entry]->alpha);
418             }
419             if (text_pos == m->s[entry]->style_end) {
420                 if (style_active) {
421                     av_bprintf(buf, "{\\r}");
422                     style_active = 0;
423                     color = m->d.color;
424                 }
425                 entry++;
426             }
427         }
428         if (m->box_flags & HLIT_BOX) {
429             if (text_pos == m->h.hlit_start) {
430                 /* If hclr box is present, set the secondary color to the color
431                  * specified. Otherwise, set primary color to white and secondary
432                  * color to black. These colors will come from TextSampleModifier
433                  * boxes in future and inverse video technique for highlight will
434                  * be implemented.
435                  */
436                 if (m->box_flags & HCLR_BOX) {
437                     av_bprintf(buf, "{\\2c&H%02x%02x%02x&}", m->c.hlit_color[2],
438                                 m->c.hlit_color[1], m->c.hlit_color[0]);
439                 } else {
440                     av_bprintf(buf, "{\\1c&H000000&}{\\2c&HFFFFFF&}");
441                 }
442             }
443             if (text_pos == m->h.hlit_end) {
444                 if (m->box_flags & HCLR_BOX) {
445                     av_bprintf(buf, "{\\2c&H%X&}", RGB_TO_BGR(m->d.color));
446                 } else {
447                     av_bprintf(buf, "{\\1c&H%X&}{\\2c&H%X&}",
448                                RGB_TO_BGR(color), RGB_TO_BGR(m->d.color));
449                 }
450             }
451         }
452
453         len = get_utf8_length_at(text, text_end);
454         if (len < 1) {
455             av_log(avctx, AV_LOG_ERROR, "invalid UTF-8 byte in subtitle\n");
456             len = 1;
457         }
458         for (i = 0; i < len; i++) {
459             switch (*text) {
460             case '\r':
461                 break;
462             case '\n':
463                 av_bprintf(buf, "\\N");
464                 break;
465             default:
466                 av_bprint_chars(buf, *text, 1);
467                 break;
468             }
469             text++;
470         }
471         text_pos++;
472     }
473
474     return 0;
475 }
476
477 static int mov_text_init(AVCodecContext *avctx) {
478     /*
479      * TODO: Handle the default text style.
480      * NB: Most players ignore styles completely, with the result that
481      * it's very common to find files where the default style is broken
482      * and respecting it results in a worse experience than ignoring it.
483      */
484     int ret;
485     MovTextContext *m = avctx->priv_data;
486     ret = mov_text_tx3g(avctx, m);
487     if (ret == 0) {
488         if (!m->frame_width || !m->frame_height) {
489             m->frame_width = ASS_DEFAULT_PLAYRESX;
490             m->frame_height = ASS_DEFAULT_PLAYRESY;
491         }
492         return ff_ass_subtitle_header_full(avctx,
493                     m->frame_width, m->frame_height,
494                     m->d.font, m->d.fontsize,
495                     (255U - m->d.alpha) << 24 | RGB_TO_BGR(m->d.color),
496                     (255U - m->d.alpha) << 24 | RGB_TO_BGR(m->d.color),
497                     (255U - m->d.back_alpha) << 24 | RGB_TO_BGR(m->d.back_color),
498                     (255U - m->d.back_alpha) << 24 | RGB_TO_BGR(m->d.back_color),
499                     m->d.bold, m->d.italic, m->d.underline,
500                     ASS_DEFAULT_BORDERSTYLE, m->d.alignment);
501     } else
502         return ff_ass_subtitle_header_default(avctx);
503 }
504
505 static int mov_text_decode_frame(AVCodecContext *avctx,
506                             void *data, int *got_sub_ptr, AVPacket *avpkt)
507 {
508     AVSubtitle *sub = data;
509     MovTextContext *m = avctx->priv_data;
510     int ret;
511     AVBPrint buf;
512     char *ptr = avpkt->data;
513     char *end;
514     int text_length, tsmb_type, ret_tsmb;
515     uint64_t tsmb_size;
516     const uint8_t *tsmb;
517     size_t i;
518
519     if (!ptr || avpkt->size < 2)
520         return AVERROR_INVALIDDATA;
521
522     /*
523      * A packet of size two with value zero is an empty subtitle
524      * used to mark the end of the previous non-empty subtitle.
525      * We can just drop them here as we have duration information
526      * already. If the value is non-zero, then it's technically a
527      * bad packet.
528      */
529     if (avpkt->size == 2)
530         return AV_RB16(ptr) == 0 ? 0 : AVERROR_INVALIDDATA;
531
532     /*
533      * The first two bytes of the packet are the length of the text string
534      * In complex cases, there are style descriptors appended to the string
535      * so we can't just assume the packet size is the string size.
536      */
537     text_length = AV_RB16(ptr);
538     end = ptr + FFMIN(2 + text_length, avpkt->size);
539     ptr += 2;
540
541     mov_text_cleanup(m);
542
543     tsmb_size = 0;
544     m->tracksize = 2 + text_length;
545     m->style_entries = 0;
546     m->box_flags = 0;
547     m->count_s = 0;
548     // Note that the spec recommends lines be no longer than 2048 characters.
549     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
550     if (text_length + 2 != avpkt->size) {
551         while (m->tracksize + 8 <= avpkt->size) {
552             // A box is a minimum of 8 bytes.
553             tsmb = ptr + m->tracksize - 2;
554             tsmb_size = AV_RB32(tsmb);
555             tsmb += 4;
556             tsmb_type = AV_RB32(tsmb);
557             tsmb += 4;
558
559             if (tsmb_size == 1) {
560                 if (m->tracksize + 16 > avpkt->size)
561                     break;
562                 tsmb_size = AV_RB64(tsmb);
563                 tsmb += 8;
564                 m->size_var = 16;
565             } else
566                 m->size_var = 8;
567             //size_var is equal to 8 or 16 depending on the size of box
568
569             if (tsmb_size == 0) {
570                 av_log(avctx, AV_LOG_ERROR, "tsmb_size is 0\n");
571                 return AVERROR_INVALIDDATA;
572             }
573
574             if (tsmb_size > avpkt->size - m->tracksize)
575                 break;
576
577             for (i = 0; i < box_count; i++) {
578                 if (tsmb_type == box_types[i].type) {
579                     if (m->tracksize + m->size_var + box_types[i].base_size > avpkt->size)
580                         break;
581                     ret_tsmb = box_types[i].decode(tsmb, m, avpkt);
582                     if (ret_tsmb == -1)
583                         break;
584                 }
585             }
586             m->tracksize = m->tracksize + tsmb_size;
587         }
588         text_to_ass(&buf, ptr, end, avctx);
589         mov_text_cleanup(m);
590     } else
591         text_to_ass(&buf, ptr, end, avctx);
592
593     ret = ff_ass_add_rect(sub, buf.str, m->readorder++, 0, NULL, NULL);
594     av_bprint_finalize(&buf, NULL);
595     if (ret < 0)
596         return ret;
597     *got_sub_ptr = sub->num_rects > 0;
598     return avpkt->size;
599 }
600
601 static int mov_text_decode_close(AVCodecContext *avctx)
602 {
603     MovTextContext *m = avctx->priv_data;
604     mov_text_cleanup_ftab(m);
605     mov_text_cleanup(m);
606     return 0;
607 }
608
609 static void mov_text_flush(AVCodecContext *avctx)
610 {
611     MovTextContext *m = avctx->priv_data;
612     if (!(avctx->flags2 & AV_CODEC_FLAG2_RO_FLUSH_NOOP))
613         m->readorder = 0;
614 }
615
616 #define OFFSET(x) offsetof(MovTextContext, x)
617 #define FLAGS AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM
618 static const AVOption options[] = {
619     { "width", "Frame width, usually video width", OFFSET(frame_width), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
620     { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
621     { NULL },
622 };
623
624 static const AVClass mov_text_decoder_class = {
625     .class_name = "MOV text decoder",
626     .item_name  = av_default_item_name,
627     .option     = options,
628     .version    = LIBAVUTIL_VERSION_INT,
629 };
630
631 AVCodec ff_movtext_decoder = {
632     .name         = "mov_text",
633     .long_name    = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
634     .type         = AVMEDIA_TYPE_SUBTITLE,
635     .id           = AV_CODEC_ID_MOV_TEXT,
636     .priv_data_size = sizeof(MovTextContext),
637     .priv_class   = &mov_text_decoder_class,
638     .init         = mov_text_init,
639     .decode       = mov_text_decode_frame,
640     .close        = mov_text_decode_close,
641     .flush        = mov_text_flush,
642 };