FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/movtextenc.c
Date: 2021-09-26 18:22:30
Exec Total Coverage
Lines: 204 335 60.9%
Branches: 60 154 39.0%

Line Branch Exec Source
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/opt.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 #include "bytestream.h"
32 #include "internal.h"
33
34 #define STYLE_FLAG_BOLD (1<<0)
35 #define STYLE_FLAG_ITALIC (1<<1)
36 #define STYLE_FLAG_UNDERLINE (1<<2)
37 #define STYLE_RECORD_SIZE 12
38 #define SIZE_ADD 10
39
40 #define STYL_BOX (1<<0)
41 #define HLIT_BOX (1<<1)
42 #define HCLR_BOX (1<<2)
43
44 #define DEFAULT_STYLE_FONT_ID 0x01
45 #define DEFAULT_STYLE_FONTSIZE 0x12
46 #define DEFAULT_STYLE_COLOR 0xffffffff
47 #define DEFAULT_STYLE_FLAG 0x00
48
49 #define BGR_TO_RGB(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((uint32_t)(c) >> 16) & 0xff))
50 #define FONTSIZE_SCALE(s,fs) ((fs) * (s)->font_scale_factor + 0.5)
51 #define av_bprint_append_any(buf, data, size) av_bprint_append_data(buf, ((const char*)data), size)
52
53 typedef struct {
54 uint16_t style_start;
55 uint16_t style_end;
56 uint8_t style_flag;
57 uint16_t style_fontID;
58 uint8_t style_fontsize;
59 uint32_t style_color;
60 } StyleBox;
61
62 typedef struct {
63 uint16_t start;
64 uint16_t end;
65 } HighlightBox;
66
67 typedef struct {
68 uint32_t color;
69 } HilightcolorBox;
70
71 typedef struct {
72 AVClass *class;
73 AVCodecContext *avctx;
74
75 ASSSplitContext *ass_ctx;
76 ASSStyle *ass_dialog_style;
77 StyleBox *style_attributes;
78 unsigned count;
79 unsigned style_attributes_bytes_allocated;
80 StyleBox style_attributes_temp;
81 AVBPrint buffer;
82 HighlightBox hlit;
83 HilightcolorBox hclr;
84 uint8_t box_flags;
85 StyleBox d;
86 uint16_t text_pos;
87 uint16_t byte_count;
88 char **fonts;
89 int font_count;
90 double font_scale_factor;
91 int frame_height;
92 } MovTextContext;
93
94 typedef struct {
95 void (*encode)(MovTextContext *s);
96 } Box;
97
98 3 static void mov_text_cleanup(MovTextContext *s)
99 {
100 3 s->count = 0;
101 3 s->style_attributes_temp = s->d;
102 3 }
103
104 3 static void encode_styl(MovTextContext *s)
105 {
106
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if ((s->box_flags & STYL_BOX) && s->count) {
107 uint8_t buf[12], *p = buf;
108
109 bytestream_put_be32(&p, s->count * STYLE_RECORD_SIZE + SIZE_ADD);
110 bytestream_put_be32(&p, MKBETAG('s','t','y','l'));
111 bytestream_put_be16(&p, s->count);
112 /*The above three attributes are hard coded for now
113 but will come from ASS style in the future*/
114 av_bprint_append_any(&s->buffer, buf, 10);
115 for (unsigned j = 0; j < s->count; j++) {
116 const StyleBox *style = &s->style_attributes[j];
117
118 p = buf;
119 bytestream_put_be16(&p, style->style_start);
120 bytestream_put_be16(&p, style->style_end);
121 bytestream_put_be16(&p, style->style_fontID);
122 bytestream_put_byte(&p, style->style_flag);
123 bytestream_put_byte(&p, style->style_fontsize);
124 bytestream_put_be32(&p, style->style_color);
125
126 av_bprint_append_any(&s->buffer, buf, 12);
127 }
128 }
129 3 mov_text_cleanup(s);
130 3 }
131
132 3 static void encode_hlit(MovTextContext *s)
133 {
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (s->box_flags & HLIT_BOX) {
135 uint8_t buf[12], *p = buf;
136
137 bytestream_put_be32(&p, 12);
138 bytestream_put_be32(&p, MKBETAG('h','l','i','t'));
139 bytestream_put_be16(&p, s->hlit.start);
140 bytestream_put_be16(&p, s->hlit.end);
141
142 av_bprint_append_any(&s->buffer, buf, 12);
143 }
144 3 }
145
146 3 static void encode_hclr(MovTextContext *s)
147 {
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (s->box_flags & HCLR_BOX) {
149 uint8_t buf[12], *p = buf;
150
151 bytestream_put_be32(&p, 12);
152 bytestream_put_be32(&p, MKBETAG('h','c','l','r'));
153 bytestream_put_be32(&p, s->hclr.color);
154
155 av_bprint_append_any(&s->buffer, buf, 12);
156 }
157 3 }
158
159 static const Box box_types[] = {
160 { encode_styl },
161 { encode_hlit },
162 { encode_hclr },
163 };
164
165 const static size_t box_count = FF_ARRAY_ELEMS(box_types);
166
167 1 static int mov_text_encode_close(AVCodecContext *avctx)
168 {
169 1 MovTextContext *s = avctx->priv_data;
170
171 1 ff_ass_split_free(s->ass_ctx);
172 1 av_freep(&s->style_attributes);
173 1 av_freep(&s->fonts);
174 1 av_bprint_finalize(&s->buffer, NULL);
175 1 return 0;
176 }
177
178 1 static int encode_sample_description(AVCodecContext *avctx)
179 {
180 ASS *ass;
181 ASSStyle *style;
182 int i, j;
183 1 uint32_t back_color = 0;
184 1 int font_names_total_len = 0;
185 1 MovTextContext *s = avctx->priv_data;
186 1 uint8_t buf[30], *p = buf;
187
188 // 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags
189 // 0x01, // int8_t horizontal-justification
190 // 0xFF, // int8_t vertical-justification
191 // 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4]
192 // BoxRecord {
193 // 0x00, 0x00, // int16_t top
194 // 0x00, 0x00, // int16_t left
195 // 0x00, 0x00, // int16_t bottom
196 // 0x00, 0x00, // int16_t right
197 // };
198 // StyleRecord {
199 // 0x00, 0x00, // uint16_t startChar
200 // 0x00, 0x00, // uint16_t endChar
201 // 0x00, 0x01, // uint16_t font-ID
202 // 0x00, // uint8_t face-style-flags
203 // 0x12, // uint8_t font-size
204 // 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4]
205 // };
206 // FontTableBox {
207 // 0x00, 0x00, 0x00, 0x12, // uint32_t size
208 // 'f', 't', 'a', 'b', // uint8_t name[4]
209 // 0x00, 0x01, // uint16_t entry-count
210 // FontRecord {
211 // 0x00, 0x01, // uint16_t font-ID
212 // 0x05, // uint8_t font-name-length
213 // 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length]
214 // };
215 // };
216
217 // Populate sample description from ASS header
218 1 ass = (ASS*)s->ass_ctx;
219 // Compute font scaling factor based on (optionally) provided
220 // output video height and ASS script play_res_y
221
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (s->frame_height && ass->script_info.play_res_y)
222 s->font_scale_factor = (double)s->frame_height / ass->script_info.play_res_y;
223 else
224 1 s->font_scale_factor = 1;
225
226 1 style = ff_ass_style_get(s->ass_ctx, "Default");
227
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1 if (!style && ass->styles_count) {
228 style = &ass->styles[0];
229 }
230 1 s->d.style_fontID = DEFAULT_STYLE_FONT_ID;
231 1 s->d.style_fontsize = DEFAULT_STYLE_FONTSIZE;
232 1 s->d.style_color = DEFAULT_STYLE_COLOR;
233 1 s->d.style_flag = DEFAULT_STYLE_FLAG;
234
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (style) {
235 1 s->d.style_fontsize = FONTSIZE_SCALE(s, style->font_size);
236 1 s->d.style_color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8 |
237 1 255 - ((uint32_t)style->primary_color >> 24);
238 1 s->d.style_flag = (!!style->bold * STYLE_FLAG_BOLD) |
239
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
2 (!!style->italic * STYLE_FLAG_ITALIC) |
240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 (!!style->underline * STYLE_FLAG_UNDERLINE);
241 1 back_color = (BGR_TO_RGB(style->back_color & 0xffffff) << 8) |
242 1 (255 - ((uint32_t)style->back_color >> 24));
243 }
244
245 1 bytestream_put_be32(&p, 0); // displayFlags
246 1 bytestream_put_be16(&p, 0x01FF); // horizontal/vertical justification (2x int8_t)
247 1 bytestream_put_be32(&p, back_color);
248 1 bytestream_put_be64(&p, 0); // BoxRecord - 4xint16_t: top, left, bottom, right
249 // StyleRecord {
250 1 bytestream_put_be16(&p, s->d.style_start);
251 1 bytestream_put_be16(&p, s->d.style_end);
252 1 bytestream_put_be16(&p, s->d.style_fontID);
253 1 bytestream_put_byte(&p, s->d.style_flag);
254 1 bytestream_put_byte(&p, s->d.style_fontsize);
255 1 bytestream_put_be32(&p, s->d.style_color);
256 // };
257 1 av_bprint_append_any(&s->buffer, buf, 30);
258
259 // Build font table
260 // We can't build a complete font table since that would require
261 // scanning all dialogs first. But we can at least fill in what
262 // is avaiable in the ASS header
263
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 if (style && ass->styles_count) {
264 // Find unique font names
265
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (style->font_name) {
266 1 av_dynarray_add(&s->fonts, &s->font_count, style->font_name);
267 1 font_names_total_len += strlen(style->font_name);
268 }
269
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (i = 0; i < ass->styles_count; i++) {
270 1 int found = 0;
271
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!ass->styles[i].font_name)
272 continue;
273
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 for (j = 0; j < s->font_count; j++) {
274
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (!strcmp(s->fonts[j], ass->styles[i].font_name)) {
275 1 found = 1;
276 1 break;
277 }
278 }
279
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!found) {
280 av_dynarray_add(&s->fonts, &s->font_count,
281 ass->styles[i].font_name);
282 font_names_total_len += strlen(ass->styles[i].font_name);
283 }
284 }
285 } else
286 av_dynarray_add(&s->fonts, &s->font_count, (char*)"Serif");
287
288 // FontTableBox {
289 1 p = buf;
290 1 bytestream_put_be32(&p, SIZE_ADD + 3 * s->font_count + font_names_total_len); // Size
291 1 bytestream_put_be32(&p, MKBETAG('f','t','a','b'));
292 1 bytestream_put_be16(&p, s->font_count);
293
294 1 av_bprint_append_any(&s->buffer, buf, 10);
295 // FontRecord {
296
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (i = 0; i < s->font_count; i++) {
297 1 size_t len = strlen(s->fonts[i]);
298
299 1 p = buf;
300 1 bytestream_put_be16(&p, i + 1); //fontID
301 1 bytestream_put_byte(&p, len);
302
303 1 av_bprint_append_any(&s->buffer, buf, 3);
304 1 av_bprint_append_any(&s->buffer, s->fonts[i], len);
305 }
306 // };
307 // };
308
309
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (!av_bprint_is_complete(&s->buffer)) {
310 return AVERROR(ENOMEM);
311 }
312
313 1 avctx->extradata_size = s->buffer.len;
314 1 avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!avctx->extradata) {
316 return AVERROR(ENOMEM);
317 }
318
319 1 memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size);
320 1 av_bprint_clear(&s->buffer);
321
322 1 return 0;
323 }
324
325 1 static av_cold int mov_text_encode_init(AVCodecContext *avctx)
326 {
327 int ret;
328 1 MovTextContext *s = avctx->priv_data;
329 1 s->avctx = avctx;
330
331 1 av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
332
333 1 s->ass_ctx = ff_ass_split(avctx->subtitle_header);
334
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!s->ass_ctx)
335 return AVERROR_INVALIDDATA;
336 1 ret = encode_sample_description(avctx);
337
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
338 return ret;
339
340 1 return 0;
341 }
342
343 // Start a new style box if needed
344 7 static int mov_text_style_start(MovTextContext *s)
345 {
346 // there's an existing style entry
347
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if (s->style_attributes_temp.style_start == s->text_pos)
348 // Still at same text pos, use same entry
349 4 return 1;
350
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (s->style_attributes_temp.style_flag != s->d.style_flag ||
351
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 s->style_attributes_temp.style_color != s->d.style_color ||
352
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 s->style_attributes_temp.style_fontID != s->d.style_fontID ||
353
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 s->style_attributes_temp.style_fontsize != s->d.style_fontsize) {
354 StyleBox *tmp;
355
356 // last style != defaults, end the style entry and start a new one
357 if (s->count + 1 > FFMIN(SIZE_MAX / sizeof(*s->style_attributes), UINT16_MAX) ||
358 !(tmp = av_fast_realloc(s->style_attributes,
359 &s->style_attributes_bytes_allocated,
360 (s->count + 1) * sizeof(*s->style_attributes)))) {
361 mov_text_cleanup(s);
362 av_bprint_clear(&s->buffer);
363 s->box_flags &= ~STYL_BOX;
364 return 0;
365 }
366 s->style_attributes = tmp;
367 s->style_attributes_temp.style_end = s->text_pos;
368 s->style_attributes[s->count++] = s->style_attributes_temp;
369 s->box_flags |= STYL_BOX;
370 s->style_attributes_temp = s->d;
371 s->style_attributes_temp.style_start = s->text_pos;
372 } else { // style entry matches defaults, drop entry
373 3 s->style_attributes_temp = s->d;
374 3 s->style_attributes_temp.style_start = s->text_pos;
375 }
376 3 return 1;
377 }
378
379 static uint8_t mov_text_style_to_flag(const char style)
380 {
381 uint8_t style_flag = 0;
382
383 switch (style){
384 case 'b':
385 style_flag = STYLE_FLAG_BOLD;
386 break;
387 case 'i':
388 style_flag = STYLE_FLAG_ITALIC;
389 break;
390 case 'u':
391 style_flag = STYLE_FLAG_UNDERLINE;
392 break;
393 }
394 return style_flag;
395 }
396
397 3 static void mov_text_style_set(MovTextContext *s, uint8_t style_flags)
398 {
399
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (!((s->style_attributes_temp.style_flag & style_flags) ^ style_flags)) {
400 // setting flags that that are already set
401 3 return;
402 }
403 if (mov_text_style_start(s))
404 s->style_attributes_temp.style_flag |= style_flags;
405 }
406
407 static void mov_text_style_cb(void *priv, const char style, int close)
408 {
409 MovTextContext *s = priv;
410 uint8_t style_flag = mov_text_style_to_flag(style);
411
412 if (!!(s->style_attributes_temp.style_flag & style_flag) != close) {
413 // setting flag that is already set
414 return;
415 }
416 if (mov_text_style_start(s)) {
417 if (!close)
418 s->style_attributes_temp.style_flag |= style_flag;
419 else
420 s->style_attributes_temp.style_flag &= ~style_flag;
421 }
422 }
423
424 3 static void mov_text_color_set(MovTextContext *s, uint32_t color)
425 {
426
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if ((s->style_attributes_temp.style_color & 0xffffff00) == color) {
427 // color hasn't changed
428 2 return;
429 }
430
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (mov_text_style_start(s))
431 1 s->style_attributes_temp.style_color = (color & 0xffffff00) |
432 1 (s->style_attributes_temp.style_color & 0xff);
433 }
434
435 static void mov_text_color_cb(void *priv, unsigned int color, unsigned int color_id)
436 {
437 MovTextContext *s = priv;
438
439 color = BGR_TO_RGB(color) << 8;
440 if (color_id == 1) { //primary color changes
441 mov_text_color_set(s, color);
442 } else if (color_id == 2) { //secondary color changes
443 if (!(s->box_flags & HCLR_BOX))
444 // Highlight alpha not set yet, use current primary alpha
445 s->hclr.color = s->style_attributes_temp.style_color;
446 if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) {
447 s->box_flags |= HCLR_BOX;
448 s->box_flags |= HLIT_BOX;
449 s->hlit.start = s->text_pos;
450 s->hclr.color = color | (s->hclr.color & 0xFF);
451 }
452 else //close tag
453 s->hlit.end = s->text_pos;
454 /* If there are more than one secondary color changes in ASS,
455 take start of first section and end of last section. Movtext
456 allows only one highlight box per sample.
457 */
458 }
459 // Movtext does not support changes to other color_id (outline, background)
460 }
461
462 3 static void mov_text_alpha_set(MovTextContext *s, uint8_t alpha)
463 {
464
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if ((s->style_attributes_temp.style_color & 0xff) == alpha) {
465 // color hasn't changed
466 2 return;
467 }
468
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (mov_text_style_start(s))
469 1 s->style_attributes_temp.style_color =
470 1 (s->style_attributes_temp.style_color & 0xffffff00) | alpha;
471 }
472
473 static void mov_text_alpha_cb(void *priv, int alpha, int alpha_id)
474 {
475 MovTextContext *s = priv;
476
477 alpha = 255 - alpha;
478 if (alpha_id == 1) // primary alpha changes
479 mov_text_alpha_set(s, alpha);
480 else if (alpha_id == 2) { //secondary alpha changes
481 if (!(s->box_flags & HCLR_BOX))
482 // Highlight color not set yet, use current primary color
483 s->hclr.color = s->style_attributes_temp.style_color;
484 if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) {
485 s->box_flags |= HCLR_BOX;
486 s->box_flags |= HLIT_BOX;
487 s->hlit.start = s->text_pos;
488 s->hclr.color = (s->hclr.color & 0xffffff00) | alpha;
489 }
490 else //close tag
491 s->hlit.end = s->text_pos;
492 }
493 // Movtext does not support changes to other alpha_id (outline, background)
494 }
495
496 3 static uint16_t find_font_id(MovTextContext *s, const char *name)
497 {
498 int i;
499
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for (i = 0; i < s->font_count; i++) {
500
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (!strcmp(name, s->fonts[i]))
501 3 return i + 1;
502 }
503 return 1;
504 }
505
506 3 static void mov_text_font_name_set(MovTextContext *s, const char *name)
507 {
508 3 int fontID = find_font_id(s, name);
509
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (s->style_attributes_temp.style_fontID == fontID) {
510 // color hasn't changed
511 2 return;
512 }
513
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (mov_text_style_start(s))
514 1 s->style_attributes_temp.style_fontID = fontID;
515 }
516
517 static void mov_text_font_name_cb(void *priv, const char *name)
518 {
519 mov_text_font_name_set((MovTextContext*)priv, name);
520 }
521
522 3 static void mov_text_font_size_set(MovTextContext *s, int size)
523 {
524 3 size = FONTSIZE_SCALE(s, size);
525
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (s->style_attributes_temp.style_fontsize == size) {
526 // color hasn't changed
527 2 return;
528 }
529
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (mov_text_style_start(s))
530 1 s->style_attributes_temp.style_fontsize = size;
531 }
532
533 static void mov_text_font_size_cb(void *priv, int size)
534 {
535 mov_text_font_size_set((MovTextContext*)priv, size);
536 }
537
538 3 static void mov_text_end_cb(void *priv)
539 {
540 // End of text, close any open style record
541 3 mov_text_style_start((MovTextContext*)priv);
542 3 }
543
544 3 static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
545 {
546 uint8_t style_flags, alpha;
547 uint32_t color;
548
549
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (style) {
550 3 style_flags = (!!style->bold * STYLE_FLAG_BOLD) |
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
6 (!!style->italic * STYLE_FLAG_ITALIC) |
552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 (!!style->underline * STYLE_FLAG_UNDERLINE);
553 3 mov_text_style_set(s, style_flags);
554 3 color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8;
555 3 mov_text_color_set(s, color);
556 3 alpha = 255 - ((uint32_t)style->primary_color >> 24);
557 3 mov_text_alpha_set(s, alpha);
558 3 mov_text_font_size_set(s, style->font_size);
559 3 mov_text_font_name_set(s, style->font_name);
560 } else {
561 // End current style record, go back to defaults
562 mov_text_style_start(s);
563 }
564 3 }
565
566 3 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
567 {
568 3 ASSStyle *style = ff_ass_style_get(s->ass_ctx, dialog->style);
569
570 3 s->ass_dialog_style = style;
571 3 mov_text_ass_style_set(s, style);
572 3 }
573
574 static void mov_text_cancel_overrides_cb(void *priv, const char *style_name)
575 {
576 MovTextContext *s = priv;
577 ASSStyle *style;
578
579 if (!style_name || !*style_name)
580 style = s->ass_dialog_style;
581 else
582 style= ff_ass_style_get(s->ass_ctx, style_name);
583
584 mov_text_ass_style_set(s, style);
585 }
586
587 5 static uint16_t utf8_strlen(const char *text, int len)
588 {
589 5 uint16_t i = 0, ret = 0;
590
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 5 times.
48 while (i < len) {
591 43 char c = text[i];
592
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 if ((c & 0x80) == 0)
593 43 i += 1;
594 else if ((c & 0xE0) == 0xC0)
595 i += 2;
596 else if ((c & 0xF0) == 0xE0)
597 i += 3;
598 else if ((c & 0xF8) == 0xF0)
599 i += 4;
600 else
601 return 0;
602 43 ret++;
603 }
604 5 return ret;
605 }
606
607 5 static void mov_text_text_cb(void *priv, const char *text, int len)
608 {
609 5 uint16_t utf8_len = utf8_strlen(text, len);
610 5 MovTextContext *s = priv;
611 5 av_bprint_append_data(&s->buffer, text, len);
612 // If it's not utf-8, just use the byte length
613
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 s->text_pos += utf8_len ? utf8_len : len;
614 5 s->byte_count += len;
615 5 }
616
617 2 static void mov_text_new_line_cb(void *priv, int forced)
618 {
619 2 MovTextContext *s = priv;
620 2 av_bprint_append_data(&s->buffer, "\n", 1);
621 2 s->text_pos += 1;
622 2 s->byte_count += 1;
623 2 }
624
625 static const ASSCodesCallbacks mov_text_callbacks = {
626 .text = mov_text_text_cb,
627 .new_line = mov_text_new_line_cb,
628 .style = mov_text_style_cb,
629 .color = mov_text_color_cb,
630 .alpha = mov_text_alpha_cb,
631 .font_name = mov_text_font_name_cb,
632 .font_size = mov_text_font_size_cb,
633 .cancel_overrides = mov_text_cancel_overrides_cb,
634 .end = mov_text_end_cb,
635 };
636
637 3 static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
638 int bufsize, const AVSubtitle *sub)
639 {
640 3 MovTextContext *s = avctx->priv_data;
641 ASSDialog *dialog;
642 int i, length;
643 size_t j;
644
645 3 s->byte_count = 0;
646 3 s->text_pos = 0;
647 3 s->count = 0;
648 3 s->box_flags = 0;
649
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 for (i = 0; i < sub->num_rects; i++) {
650 3 const char *ass = sub->rects[i]->ass;
651
652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (sub->rects[i]->type != SUBTITLE_ASS) {
653 av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
654 return AVERROR(EINVAL);
655 }
656
657 3 dialog = ff_ass_split_dialog(s->ass_ctx, ass);
658
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!dialog)
659 return AVERROR(ENOMEM);
660 3 mov_text_dialog(s, dialog);
661 3 ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
662 3 ff_ass_free_dialog(&dialog);
663
664
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 for (j = 0; j < box_count; j++) {
665 9 box_types[j].encode(s);
666 }
667 }
668
669 3 AV_WB16(buf, s->byte_count);
670 3 buf += 2;
671
672
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if (!av_bprint_is_complete(&s->buffer)) {
673 length = AVERROR(ENOMEM);
674 goto exit;
675 }
676
677
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!s->buffer.len) {
678 length = 0;
679 goto exit;
680 }
681
682
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (s->buffer.len > bufsize - 3) {
683 av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
684 length = AVERROR_BUFFER_TOO_SMALL;
685 goto exit;
686 }
687
688 3 memcpy(buf, s->buffer.str, s->buffer.len);
689 3 length = s->buffer.len + 2;
690
691 3 exit:
692 3 av_bprint_clear(&s->buffer);
693 3 return length;
694 }
695
696 #define OFFSET(x) offsetof(MovTextContext, x)
697 #define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM
698 static const AVOption options[] = {
699 { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
700 { NULL },
701 };
702
703 static const AVClass mov_text_encoder_class = {
704 .class_name = "MOV text enoder",
705 .item_name = av_default_item_name,
706 .option = options,
707 .version = LIBAVUTIL_VERSION_INT,
708 };
709
710 const AVCodec ff_movtext_encoder = {
711 .name = "mov_text",
712 .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
713 .type = AVMEDIA_TYPE_SUBTITLE,
714 .id = AV_CODEC_ID_MOV_TEXT,
715 .priv_data_size = sizeof(MovTextContext),
716 .priv_class = &mov_text_encoder_class,
717 .init = mov_text_encode_init,
718 .encode_sub = mov_text_encode_frame,
719 .close = mov_text_encode_close,
720 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
721 };
722