avcodec/aacdec_fixed: fix invalid shift in predict()
[ffmpeg.git] / libavcodec / movtextenc.c
1 /*
2  * 3GPP TS 26.245 Timed Text encoder
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 <stdarg.h>
23 #include "avcodec.h"
24 #include "libavutil/avassert.h"
25 #include "libavutil/avstring.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/mem.h"
28 #include "libavutil/common.h"
29 #include "ass_split.h"
30 #include "ass.h"
31
32 #define STYLE_FLAG_BOLD         (1<<0)
33 #define STYLE_FLAG_ITALIC       (1<<1)
34 #define STYLE_FLAG_UNDERLINE    (1<<2)
35 #define STYLE_RECORD_SIZE       12
36 #define SIZE_ADD                10
37
38 #define STYL_BOX   (1<<0)
39 #define HLIT_BOX   (1<<1)
40 #define HCLR_BOX   (1<<2)
41
42 #define av_bprint_append_any(buf, data, size)   av_bprint_append_data(buf, ((const char*)data), size)
43
44 typedef struct {
45     uint16_t style_start;
46     uint16_t style_end;
47     uint8_t style_flag;
48 } StyleBox;
49
50 typedef struct {
51     uint16_t start;
52     uint16_t end;
53 } HighlightBox;
54
55 typedef struct {
56    uint32_t color;
57 } HilightcolorBox;
58
59 typedef struct {
60     AVCodecContext *avctx;
61
62     ASSSplitContext *ass_ctx;
63     AVBPrint buffer;
64     StyleBox **style_attributes;
65     StyleBox *style_attributes_temp;
66     HighlightBox hlit;
67     HilightcolorBox hclr;
68     int count;
69     uint8_t box_flags;
70     uint16_t style_entries;
71     uint16_t style_fontID;
72     uint8_t style_fontsize;
73     uint32_t style_color;
74     uint16_t text_pos;
75 } MovTextContext;
76
77 typedef struct {
78     uint32_t type;
79     void (*encode)(MovTextContext *s, uint32_t tsmb_type);
80 } Box;
81
82 static void mov_text_cleanup(MovTextContext *s)
83 {
84     int j;
85     if (s->box_flags & STYL_BOX) {
86         for (j = 0; j < s->count; j++) {
87             av_freep(&s->style_attributes[j]);
88         }
89         av_freep(&s->style_attributes);
90     }
91 }
92
93 static void encode_styl(MovTextContext *s, uint32_t tsmb_type)
94 {
95     int j;
96     uint32_t tsmb_size;
97     if (s->box_flags & STYL_BOX) {
98         tsmb_size = s->count * STYLE_RECORD_SIZE + SIZE_ADD;
99         tsmb_size = AV_RB32(&tsmb_size);
100         s->style_entries = AV_RB16(&s->count);
101         s->style_fontID = 0x00 | 0x01<<8;
102         s->style_fontsize = 0x12;
103         s->style_color = MKTAG(0xFF, 0xFF, 0xFF, 0xFF);
104         /*The above three attributes are hard coded for now
105         but will come from ASS style in the future*/
106         av_bprint_append_any(&s->buffer, &tsmb_size, 4);
107         av_bprint_append_any(&s->buffer, &tsmb_type, 4);
108         av_bprint_append_any(&s->buffer, &s->style_entries, 2);
109         for (j = 0; j < s->count; j++) {
110             av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_start, 2);
111             av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_end, 2);
112             av_bprint_append_any(&s->buffer, &s->style_fontID, 2);
113             av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_flag, 1);
114             av_bprint_append_any(&s->buffer, &s->style_fontsize, 1);
115             av_bprint_append_any(&s->buffer, &s->style_color, 4);
116         }
117         mov_text_cleanup(s);
118     }
119 }
120
121 static void encode_hlit(MovTextContext *s, uint32_t tsmb_type)
122 {
123     uint32_t tsmb_size;
124     if (s->box_flags & HLIT_BOX) {
125         tsmb_size = 12;
126         tsmb_size = AV_RB32(&tsmb_size);
127         av_bprint_append_any(&s->buffer, &tsmb_size, 4);
128         av_bprint_append_any(&s->buffer, &tsmb_type, 4);
129         av_bprint_append_any(&s->buffer, &s->hlit.start, 2);
130         av_bprint_append_any(&s->buffer, &s->hlit.end, 2);
131     }
132 }
133
134 static void encode_hclr(MovTextContext *s, uint32_t tsmb_type)
135 {
136     uint32_t tsmb_size;
137     if (s->box_flags & HCLR_BOX) {
138         tsmb_size = 12;
139         tsmb_size = AV_RB32(&tsmb_size);
140         av_bprint_append_any(&s->buffer, &tsmb_size, 4);
141         av_bprint_append_any(&s->buffer, &tsmb_type, 4);
142         av_bprint_append_any(&s->buffer, &s->hclr.color, 4);
143     }
144 }
145
146 static const Box box_types[] = {
147     { MKTAG('s','t','y','l'), encode_styl },
148     { MKTAG('h','l','i','t'), encode_hlit },
149     { MKTAG('h','c','l','r'), encode_hclr },
150 };
151
152 const static size_t box_count = FF_ARRAY_ELEMS(box_types);
153
154 static av_cold int mov_text_encode_init(AVCodecContext *avctx)
155 {
156     /*
157      * For now, we'll use a fixed default style. When we add styling
158      * support, this will be generated from the ASS style.
159      */
160     static const uint8_t text_sample_entry[] = {
161         0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags
162         0x01,                   // int8_t horizontal-justification
163         0xFF,                   // int8_t vertical-justification
164         0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4]
165         // BoxRecord {
166         0x00, 0x00,             // int16_t top
167         0x00, 0x00,             // int16_t left
168         0x00, 0x00,             // int16_t bottom
169         0x00, 0x00,             // int16_t right
170         // };
171         // StyleRecord {
172         0x00, 0x00,             // uint16_t startChar
173         0x00, 0x00,             // uint16_t endChar
174         0x00, 0x01,             // uint16_t font-ID
175         0x00,                   // uint8_t face-style-flags
176         0x12,                   // uint8_t font-size
177         0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4]
178         // };
179         // FontTableBox {
180         0x00, 0x00, 0x00, 0x12, // uint32_t size
181         'f', 't', 'a', 'b',     // uint8_t name[4]
182         0x00, 0x01,             // uint16_t entry-count
183         // FontRecord {
184         0x00, 0x01,             // uint16_t font-ID
185         0x05,                   // uint8_t font-name-length
186         'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length]
187         // };
188         // };
189     };
190
191     MovTextContext *s = avctx->priv_data;
192     s->avctx = avctx;
193
194     avctx->extradata_size = sizeof text_sample_entry;
195     avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
196     if (!avctx->extradata)
197         return AVERROR(ENOMEM);
198
199     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
200
201     memcpy(avctx->extradata, text_sample_entry, avctx->extradata_size);
202
203     s->ass_ctx = ff_ass_split(avctx->subtitle_header);
204     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
205 }
206
207 static void mov_text_style_cb(void *priv, const char style, int close)
208 {
209     MovTextContext *s = priv;
210     if (!close) {
211         if (!(s->box_flags & STYL_BOX)) {   //first style entry
212
213             s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
214
215             if (!s->style_attributes_temp) {
216                 av_bprint_clear(&s->buffer);
217                 s->box_flags &= ~STYL_BOX;
218                 return;
219             }
220
221             s->style_attributes_temp->style_flag = 0;
222             s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
223         } else {
224             if (s->style_attributes_temp->style_flag) { //break the style record here and start a new one
225                 s->style_attributes_temp->style_end = AV_RB16(&s->text_pos);
226                 av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp);
227                 s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
228                 if (!s->style_attributes_temp) {
229                     mov_text_cleanup(s);
230                     av_bprint_clear(&s->buffer);
231                     s->box_flags &= ~STYL_BOX;
232                     return;
233                 }
234
235                 s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag;
236                 s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
237             } else {
238                 s->style_attributes_temp->style_flag = 0;
239                 s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
240             }
241         }
242         switch (style){
243         case 'b':
244             s->style_attributes_temp->style_flag |= STYLE_FLAG_BOLD;
245             break;
246         case 'i':
247             s->style_attributes_temp->style_flag |= STYLE_FLAG_ITALIC;
248             break;
249         case 'u':
250             s->style_attributes_temp->style_flag |= STYLE_FLAG_UNDERLINE;
251             break;
252         }
253     } else if (!s->style_attributes_temp) {
254         av_log(s->avctx, AV_LOG_WARNING, "Ignoring unmatched close tag\n");
255         return;
256     } else {
257         s->style_attributes_temp->style_end = AV_RB16(&s->text_pos);
258         av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp);
259
260         s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
261
262         if (!s->style_attributes_temp) {
263             mov_text_cleanup(s);
264             av_bprint_clear(&s->buffer);
265             s->box_flags &= ~STYL_BOX;
266             return;
267         }
268
269         s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag;
270         switch (style){
271         case 'b':
272             s->style_attributes_temp->style_flag &= ~STYLE_FLAG_BOLD;
273             break;
274         case 'i':
275             s->style_attributes_temp->style_flag &= ~STYLE_FLAG_ITALIC;
276             break;
277         case 'u':
278             s->style_attributes_temp->style_flag &= ~STYLE_FLAG_UNDERLINE;
279             break;
280         }
281         if (s->style_attributes_temp->style_flag) { //start of new style record
282             s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
283         }
284     }
285     s->box_flags |= STYL_BOX;
286 }
287
288 static void mov_text_color_cb(void *priv, unsigned int color, unsigned int color_id)
289 {
290     MovTextContext *s = priv;
291     if (color_id == 2) {    //secondary color changes
292         if (s->box_flags & HLIT_BOX) {  //close tag
293             s->hlit.end = AV_RB16(&s->text_pos);
294         } else {
295             s->box_flags |= HCLR_BOX;
296             s->box_flags |= HLIT_BOX;
297             s->hlit.start = AV_RB16(&s->text_pos);
298             s->hclr.color = color | (0xFF << 24);  //set alpha value to FF
299         }
300     }
301     /* If there are more than one secondary color changes in ASS, take start of
302        first section and end of last section. Movtext allows only one
303        highlight box per sample.
304      */
305 }
306
307 static void mov_text_text_cb(void *priv, const char *text, int len)
308 {
309     MovTextContext *s = priv;
310     av_bprint_append_data(&s->buffer, text, len);
311     s->text_pos += len;
312 }
313
314 static void mov_text_new_line_cb(void *priv, int forced)
315 {
316     MovTextContext *s = priv;
317     av_bprint_append_data(&s->buffer, "\n", 1);
318     s->text_pos += 1;
319 }
320
321 static const ASSCodesCallbacks mov_text_callbacks = {
322     .text     = mov_text_text_cb,
323     .new_line = mov_text_new_line_cb,
324     .style    = mov_text_style_cb,
325     .color    = mov_text_color_cb,
326 };
327
328 static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
329                                  int bufsize, const AVSubtitle *sub)
330 {
331     MovTextContext *s = avctx->priv_data;
332     ASSDialog *dialog;
333     int i, length;
334     size_t j;
335
336     s->text_pos = 0;
337     s->count = 0;
338     s->box_flags = 0;
339     s->style_entries = 0;
340     for (i = 0; i < sub->num_rects; i++) {
341         const char *ass = sub->rects[i]->ass;
342
343         if (sub->rects[i]->type != SUBTITLE_ASS) {
344             av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
345             return AVERROR(ENOSYS);
346         }
347
348 #if FF_API_ASS_TIMING
349         if (!strncmp(ass, "Dialogue: ", 10)) {
350             int num;
351             dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num);
352             for (; dialog && num--; dialog++) {
353                 ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
354             }
355         } else {
356 #endif
357             dialog = ff_ass_split_dialog2(s->ass_ctx, ass);
358             if (!dialog)
359                 return AVERROR(ENOMEM);
360             ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
361             ff_ass_free_dialog(&dialog);
362 #if FF_API_ASS_TIMING
363         }
364 #endif
365
366         for (j = 0; j < box_count; j++) {
367             box_types[j].encode(s, box_types[j].type);
368         }
369     }
370
371     AV_WB16(buf, s->text_pos);
372     buf += 2;
373
374     if (!av_bprint_is_complete(&s->buffer)) {
375         length = AVERROR(ENOMEM);
376         goto exit;
377     }
378
379     if (!s->buffer.len) {
380         length = 0;
381         goto exit;
382     }
383
384     if (s->buffer.len > bufsize - 3) {
385         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
386         length = AVERROR(EINVAL);
387         goto exit;
388     }
389
390     memcpy(buf, s->buffer.str, s->buffer.len);
391     length = s->buffer.len + 2;
392
393 exit:
394     av_bprint_clear(&s->buffer);
395     return length;
396 }
397
398 static int mov_text_encode_close(AVCodecContext *avctx)
399 {
400     MovTextContext *s = avctx->priv_data;
401     ff_ass_split_free(s->ass_ctx);
402     av_bprint_finalize(&s->buffer, NULL);
403     return 0;
404 }
405
406 AVCodec ff_movtext_encoder = {
407     .name           = "mov_text",
408     .long_name      = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
409     .type           = AVMEDIA_TYPE_SUBTITLE,
410     .id             = AV_CODEC_ID_MOV_TEXT,
411     .priv_data_size = sizeof(MovTextContext),
412     .init           = mov_text_encode_init,
413     .encode_sub     = mov_text_encode_frame,
414     .close          = mov_text_encode_close,
415 };