]> git.sesse.net Git - ffmpeg/blob - libavcodec/movtextenc.c
avcodec/mpeg4videodec: Check P cbpy
[ffmpeg] / 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 av_bprint_append_any(buf, data, size)   av_bprint_append_data(buf, ((const char*)data), size)
39
40 typedef struct {
41     uint16_t style_start;
42     uint16_t style_end;
43     uint8_t style_flag;
44 } StyleBox;
45
46 typedef struct {
47     ASSSplitContext *ass_ctx;
48     AVBPrint buffer;
49     StyleBox **style_attributes;
50     StyleBox *style_attributes_temp;
51     int count;
52     uint8_t style_box_flag;
53     uint32_t tsmb_size;
54     uint32_t tsmb_type;
55     uint16_t style_entries;
56     uint16_t style_fontID;
57     uint8_t style_fontsize;
58     uint32_t style_color;
59     uint16_t text_pos;
60 } MovTextContext;
61
62
63 static av_cold int mov_text_encode_init(AVCodecContext *avctx)
64 {
65     /*
66      * For now, we'll use a fixed default style. When we add styling
67      * support, this will be generated from the ASS style.
68      */
69     static const uint8_t text_sample_entry[] = {
70         0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags
71         0x01,                   // int8_t horizontal-justification
72         0xFF,                   // int8_t vertical-justification
73         0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4]
74         // BoxRecord {
75         0x00, 0x00,             // int16_t top
76         0x00, 0x00,             // int16_t left
77         0x00, 0x00,             // int16_t bottom
78         0x00, 0x00,             // int16_t right
79         // };
80         // StyleRecord {
81         0x00, 0x00,             // uint16_t startChar
82         0x00, 0x00,             // uint16_t endChar
83         0x00, 0x01,             // uint16_t font-ID
84         0x00,                   // uint8_t face-style-flags
85         0x12,                   // uint8_t font-size
86         0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4]
87         // };
88         // FontTableBox {
89         0x00, 0x00, 0x00, 0x12, // uint32_t size
90         'f', 't', 'a', 'b',     // uint8_t name[4]
91         0x00, 0x01,             // uint16_t entry-count
92         // FontRecord {
93         0x00, 0x01,             // uint16_t font-ID
94         0x05,                   // uint8_t font-name-length
95         'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length]
96         // };
97         // };
98     };
99
100     MovTextContext *s = avctx->priv_data;
101
102     avctx->extradata_size = sizeof text_sample_entry;
103     avctx->extradata = av_mallocz(avctx->extradata_size + FF_INPUT_BUFFER_PADDING_SIZE);
104     if (!avctx->extradata)
105         return AVERROR(ENOMEM);
106
107     av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
108
109     memcpy(avctx->extradata, text_sample_entry, avctx->extradata_size);
110
111     s->ass_ctx = ff_ass_split(avctx->subtitle_header);
112     return s->ass_ctx ? 0 : AVERROR_INVALIDDATA;
113 }
114
115 static void mov_text_style_cb(void *priv, const char style, int close)
116 {
117     MovTextContext *s = priv;
118     if (!close) {
119         if (s->style_box_flag == 0) {   //first style entry
120
121             s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
122
123             if (!s->style_attributes_temp) {
124                 av_bprint_clear(&s->buffer);
125                 s->style_box_flag = 0;
126                 return;
127             }
128
129             s->style_attributes_temp->style_flag = 0;
130             s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
131         } else {
132             if (s->style_attributes_temp->style_flag) { //break the style record here and start a new one
133                 s->style_attributes_temp->style_end = AV_RB16(&s->text_pos);
134                 av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp);
135
136                 s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
137
138                 if (!s->style_attributes_temp) {
139                     av_bprint_clear(&s->buffer);
140                     s->style_box_flag = 0;
141                     return;
142                 }
143
144                 s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag;
145                 s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
146             } else {
147                 s->style_attributes_temp->style_flag = 0;
148                 s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
149             }
150         }
151         switch (style){
152         case 'b':
153             s->style_attributes_temp->style_flag |= STYLE_FLAG_BOLD;
154             break;
155         case 'i':
156             s->style_attributes_temp->style_flag |= STYLE_FLAG_ITALIC;
157             break;
158         case 'u':
159             s->style_attributes_temp->style_flag |= STYLE_FLAG_UNDERLINE;
160             break;
161         }
162     } else {
163         s->style_attributes_temp->style_end = AV_RB16(&s->text_pos);
164         av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp);
165
166         s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
167
168         if (!s->style_attributes_temp) {
169             av_bprint_clear(&s->buffer);
170             s->style_box_flag = 0;
171             return;
172         }
173
174         s->style_attributes_temp->style_flag = s->style_attributes[s->count - 1]->style_flag;
175         switch (style){
176         case 'b':
177             s->style_attributes_temp->style_flag &= ~STYLE_FLAG_BOLD;
178             break;
179         case 'i':
180             s->style_attributes_temp->style_flag &= ~STYLE_FLAG_ITALIC;
181             break;
182         case 'u':
183             s->style_attributes_temp->style_flag &= ~STYLE_FLAG_UNDERLINE;
184             break;
185         }
186         if (s->style_attributes_temp->style_flag) { //start of new style record
187             s->style_attributes_temp->style_start = AV_RB16(&s->text_pos);
188         }
189     }
190     s->style_box_flag = 1;
191 }
192
193 static void mov_text_text_cb(void *priv, const char *text, int len)
194 {
195     MovTextContext *s = priv;
196     av_bprint_append_data(&s->buffer, text, len);
197     s->text_pos += len;
198 }
199
200 static void mov_text_new_line_cb(void *priv, int forced)
201 {
202     MovTextContext *s = priv;
203     av_bprint_append_data(&s->buffer, "\n", 1);
204     s->text_pos += 1;
205 }
206
207 static const ASSCodesCallbacks mov_text_callbacks = {
208     .text     = mov_text_text_cb,
209     .new_line = mov_text_new_line_cb,
210     .style    = mov_text_style_cb,
211 };
212
213 static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
214                                  int bufsize, const AVSubtitle *sub)
215 {
216     MovTextContext *s = avctx->priv_data;
217     ASSDialog *dialog;
218     int i, j, num, length;
219
220     s->text_pos = 0;
221     s->count = 0;
222     s->style_box_flag = 0;
223     s->style_entries = 0;
224
225     for (i = 0; i < sub->num_rects; i++) {
226
227         if (sub->rects[i]->type != SUBTITLE_ASS) {
228             av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
229             return AVERROR(ENOSYS);
230         }
231
232         dialog = ff_ass_split_dialog(s->ass_ctx, sub->rects[i]->ass, 0, &num);
233         for (; dialog && num--; dialog++) {
234             ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
235         }
236         if (s->style_box_flag) {
237             s->tsmb_size = s->count * STYLE_RECORD_SIZE + SIZE_ADD;  //size of one style record is 12 bytes
238             s->tsmb_size = AV_RB32(&s->tsmb_size);
239             s->tsmb_type = MKTAG('s','t','y','l');
240             s->style_entries = AV_RB16(&s->count);
241             s->style_fontID = 0x00 | 0x01<<8;
242             s->style_fontsize = 0x12;
243             s->style_color = MKTAG(0xFF, 0xFF, 0xFF, 0xFF);
244             /*The above three attributes are hard coded for now
245             but will come from ASS style in the future*/
246             av_bprint_append_any(&s->buffer, &s->tsmb_size, 4);
247             av_bprint_append_any(&s->buffer, &s->tsmb_type, 4);
248             av_bprint_append_any(&s->buffer, &s->style_entries, 2);
249             for (j = 0; j < s->count; j++) {
250                 av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_start, 2);
251                 av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_end, 2);
252                 av_bprint_append_any(&s->buffer, &s->style_fontID, 2);
253                 av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_flag, 1);
254                 av_bprint_append_any(&s->buffer, &s->style_fontsize, 1);
255                 av_bprint_append_any(&s->buffer, &s->style_color, 4);
256             }
257             for (j = 0; j < s->count; j++) {
258                 av_freep(&s->style_attributes[j]);
259             }
260             av_freep(&s->style_attributes);
261             av_freep(&s->style_attributes_temp);
262         }
263     }
264
265     AV_WB16(buf, s->text_pos);
266     buf += 2;
267
268     if (!av_bprint_is_complete(&s->buffer)) {
269         length = AVERROR(ENOMEM);
270         goto exit;
271     }
272
273     if (!s->buffer.len) {
274         length = 0;
275         goto exit;
276     }
277
278     if (s->buffer.len > bufsize - 3) {
279         av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
280         length = AVERROR(EINVAL);
281         goto exit;
282     }
283
284     memcpy(buf, s->buffer.str, s->buffer.len);
285     length = s->buffer.len + 2;
286
287 exit:
288     av_bprint_clear(&s->buffer);
289     return length;
290 }
291
292 static int mov_text_encode_close(AVCodecContext *avctx)
293 {
294     MovTextContext *s = avctx->priv_data;
295     ff_ass_split_free(s->ass_ctx);
296     av_bprint_finalize(&s->buffer, NULL);
297     return 0;
298 }
299
300 AVCodec ff_movtext_encoder = {
301     .name           = "mov_text",
302     .long_name      = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
303     .type           = AVMEDIA_TYPE_SUBTITLE,
304     .id             = AV_CODEC_ID_MOV_TEXT,
305     .priv_data_size = sizeof(MovTextContext),
306     .init           = mov_text_encode_init,
307     .encode_sub     = mov_text_encode_frame,
308     .close          = mov_text_encode_close,
309 };