2 * 3GPP TS 26.245 Timed Text encoder
3 * Copyright (c) 2012 Philip Langdale <philipl@overt.org>
5 * This file is part of FFmpeg.
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.
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.
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
24 #include "libavutil/opt.h"
25 #include "libavutil/avassert.h"
26 #include "libavutil/avstring.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/mem.h"
29 #include "libavutil/common.h"
30 #include "ass_split.h"
33 #define STYLE_FLAG_BOLD (1<<0)
34 #define STYLE_FLAG_ITALIC (1<<1)
35 #define STYLE_FLAG_UNDERLINE (1<<2)
36 #define STYLE_RECORD_SIZE 12
39 #define STYL_BOX (1<<0)
40 #define HLIT_BOX (1<<1)
41 #define HCLR_BOX (1<<2)
43 #define DEFAULT_STYLE_FONT_ID 0x01
44 #define DEFAULT_STYLE_FONTSIZE 0x12
45 #define DEFAULT_STYLE_COLOR 0xffffffff
46 #define DEFAULT_STYLE_FLAG 0x00
48 #define BGR_TO_RGB(c) (((c) & 0xff) << 16 | ((c) & 0xff00) | (((c) >> 16) & 0xff))
49 #define FONTSIZE_SCALE(s,fs) ((fs) * (s)->font_scale_factor + 0.5)
50 #define av_bprint_append_any(buf, data, size) av_bprint_append_data(buf, ((const char*)data), size)
56 uint16_t style_fontID;
57 uint8_t style_fontsize;
72 AVCodecContext *avctx;
74 ASSSplitContext *ass_ctx;
75 ASSStyle *ass_dialog_style;
77 StyleBox **style_attributes;
78 StyleBox *style_attributes_temp;
88 double font_scale_factor;
94 void (*encode)(MovTextContext *s, uint32_t tsmb_type);
97 static void mov_text_cleanup(MovTextContext *s)
100 if (s->box_flags & STYL_BOX) {
101 for (j = 0; j < s->count; j++) {
102 av_freep(&s->style_attributes[j]);
104 av_freep(&s->style_attributes);
106 if (s->style_attributes_temp) {
107 *s->style_attributes_temp = s->d;
111 static void encode_styl(MovTextContext *s, uint32_t tsmb_type)
115 uint16_t style_entries;
116 if ((s->box_flags & STYL_BOX) && s->count) {
117 tsmb_size = s->count * STYLE_RECORD_SIZE + SIZE_ADD;
118 tsmb_size = AV_RB32(&tsmb_size);
119 style_entries = AV_RB16(&s->count);
120 /*The above three attributes are hard coded for now
121 but will come from ASS style in the future*/
122 av_bprint_append_any(&s->buffer, &tsmb_size, 4);
123 av_bprint_append_any(&s->buffer, &tsmb_type, 4);
124 av_bprint_append_any(&s->buffer, &style_entries, 2);
125 for (j = 0; j < s->count; j++) {
126 uint16_t style_start, style_end, style_fontID;
127 uint32_t style_color;
129 style_start = AV_RB16(&s->style_attributes[j]->style_start);
130 style_end = AV_RB16(&s->style_attributes[j]->style_end);
131 style_color = AV_RB32(&s->style_attributes[j]->style_color);
132 style_fontID = AV_RB16(&s->style_attributes[j]->style_fontID);
134 av_bprint_append_any(&s->buffer, &style_start, 2);
135 av_bprint_append_any(&s->buffer, &style_end, 2);
136 av_bprint_append_any(&s->buffer, &style_fontID, 2);
137 av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_flag, 1);
138 av_bprint_append_any(&s->buffer, &s->style_attributes[j]->style_fontsize, 1);
139 av_bprint_append_any(&s->buffer, &style_color, 4);
145 static void encode_hlit(MovTextContext *s, uint32_t tsmb_type)
149 if (s->box_flags & HLIT_BOX) {
151 tsmb_size = AV_RB32(&tsmb_size);
152 start = AV_RB16(&s->hlit.start);
153 end = AV_RB16(&s->hlit.end);
154 av_bprint_append_any(&s->buffer, &tsmb_size, 4);
155 av_bprint_append_any(&s->buffer, &tsmb_type, 4);
156 av_bprint_append_any(&s->buffer, &start, 2);
157 av_bprint_append_any(&s->buffer, &end, 2);
161 static void encode_hclr(MovTextContext *s, uint32_t tsmb_type)
163 uint32_t tsmb_size, color;
164 if (s->box_flags & HCLR_BOX) {
166 tsmb_size = AV_RB32(&tsmb_size);
167 color = AV_RB32(&s->hclr.color);
168 av_bprint_append_any(&s->buffer, &tsmb_size, 4);
169 av_bprint_append_any(&s->buffer, &tsmb_type, 4);
170 av_bprint_append_any(&s->buffer, &color, 4);
174 static const Box box_types[] = {
175 { MKTAG('s','t','y','l'), encode_styl },
176 { MKTAG('h','l','i','t'), encode_hlit },
177 { MKTAG('h','c','l','r'), encode_hclr },
180 const static size_t box_count = FF_ARRAY_ELEMS(box_types);
182 static int mov_text_encode_close(AVCodecContext *avctx)
184 MovTextContext *s = avctx->priv_data;
187 ff_ass_split_free(s->ass_ctx);
188 if (s->style_attributes) {
189 for (i = 0; i < s->count; i++) {
190 av_freep(&s->style_attributes[i]);
192 av_freep(&s->style_attributes);
195 av_freep(&s->style_attributes_temp);
196 av_bprint_finalize(&s->buffer, NULL);
200 static int encode_sample_description(AVCodecContext *avctx)
205 uint32_t tsmb_size, tsmb_type, back_color, style_color;
206 uint16_t style_start, style_end, fontID, count;
207 int font_names_total_len = 0;
208 MovTextContext *s = avctx->priv_data;
210 static const uint8_t display_and_justification[] = {
211 0x00, 0x00, 0x00, 0x00, // uint32_t displayFlags
212 0x01, // int8_t horizontal-justification
213 0xFF, // int8_t vertical-justification
215 // 0x00, 0x00, 0x00, 0x00, // uint8_t background-color-rgba[4]
216 static const uint8_t box_record[] = {
218 0x00, 0x00, // int16_t top
219 0x00, 0x00, // int16_t left
220 0x00, 0x00, // int16_t bottom
221 0x00, 0x00, // int16_t right
225 // 0x00, 0x00, // uint16_t startChar
226 // 0x00, 0x00, // uint16_t endChar
227 // 0x00, 0x01, // uint16_t font-ID
228 // 0x00, // uint8_t face-style-flags
229 // 0x12, // uint8_t font-size
230 // 0xFF, 0xFF, 0xFF, 0xFF, // uint8_t text-color-rgba[4]
233 // 0x00, 0x00, 0x00, 0x12, // uint32_t size
234 // 'f', 't', 'a', 'b', // uint8_t name[4]
235 // 0x00, 0x01, // uint16_t entry-count
237 // 0x00, 0x01, // uint16_t font-ID
238 // 0x05, // uint8_t font-name-length
239 // 'S', 'e', 'r', 'i', 'f',// uint8_t font[font-name-length]
243 // Populate sample description from ASS header
244 ass = (ASS*)s->ass_ctx;
245 // Compute font scaling factor based on (optionally) provided
246 // output video height and ASS script play_res_y
247 if (s->frame_height && ass->script_info.play_res_y)
248 s->font_scale_factor = (double)s->frame_height / ass->script_info.play_res_y;
250 s->font_scale_factor = 1;
252 style = ff_ass_style_get(s->ass_ctx, "Default");
253 if (!style && ass->styles_count) {
254 style = &ass->styles[0];
256 s->d.style_fontID = DEFAULT_STYLE_FONT_ID;
257 s->d.style_fontsize = DEFAULT_STYLE_FONTSIZE;
258 s->d.style_color = DEFAULT_STYLE_COLOR;
259 s->d.style_flag = DEFAULT_STYLE_FLAG;
261 s->d.style_fontsize = FONTSIZE_SCALE(s, style->font_size);
262 s->d.style_color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8 |
263 255 - ((uint32_t)style->primary_color >> 24);
264 s->d.style_flag = (!!style->bold * STYLE_FLAG_BOLD) |
265 (!!style->italic * STYLE_FLAG_ITALIC) |
266 (!!style->underline * STYLE_FLAG_UNDERLINE);
267 back_color = (BGR_TO_RGB(style->back_color & 0xffffff) << 8) |
268 (255 - ((uint32_t)style->back_color >> 24));
271 av_bprint_append_any(&s->buffer, display_and_justification,
272 sizeof(display_and_justification));
273 back_color = AV_RB32(&back_color);
274 av_bprint_append_any(&s->buffer, &back_color, 4);
276 av_bprint_append_any(&s->buffer, box_record, sizeof(box_record));
279 style_start = AV_RB16(&s->d.style_start);
280 style_end = AV_RB16(&s->d.style_end);
281 fontID = AV_RB16(&s->d.style_fontID);
282 style_color = AV_RB32(&s->d.style_color);
283 av_bprint_append_any(&s->buffer, &style_start, 2);
284 av_bprint_append_any(&s->buffer, &style_end, 2);
285 av_bprint_append_any(&s->buffer, &fontID, 2);
286 av_bprint_append_any(&s->buffer, &s->d.style_flag, 1);
287 av_bprint_append_any(&s->buffer, &s->d.style_fontsize, 1);
288 av_bprint_append_any(&s->buffer, &style_color, 4);
292 // We can't build a complete font table since that would require
293 // scanning all dialogs first. But we can at least fill in what
294 // is avaiable in the ASS header
295 if (style && ass->styles_count) {
296 // Find unique font names
297 av_dynarray_add(&s->fonts, &s->font_count, style->font_name);
298 font_names_total_len += strlen(style->font_name);
299 for (i = 0; i < ass->styles_count; i++) {
301 for (j = 0; j < s->font_count; j++) {
302 if (!strcmp(s->fonts[j], ass->styles[i].font_name)) {
308 av_dynarray_add(&s->fonts, &s->font_count,
309 ass->styles[i].font_name);
310 font_names_total_len += strlen(ass->styles[i].font_name);
314 av_dynarray_add(&s->fonts, &s->font_count, (char*)"Serif");
317 tsmb_size = SIZE_ADD + 3 * s->font_count + font_names_total_len;
318 tsmb_size = AV_RB32(&tsmb_size);
319 tsmb_type = MKTAG('f','t','a','b');
320 count = AV_RB16(&s->font_count);
321 av_bprint_append_any(&s->buffer, &tsmb_size, 4);
322 av_bprint_append_any(&s->buffer, &tsmb_type, 4);
323 av_bprint_append_any(&s->buffer, &count, 2);
325 for (i = 0; i < s->font_count; i++) {
328 fontID = AV_RB16(&fontID);
329 av_bprint_append_any(&s->buffer, &fontID, 2);
330 len = strlen(s->fonts[i]);
331 av_bprint_append_any(&s->buffer, &len, 1);
332 av_bprint_append_any(&s->buffer, s->fonts[i], len);
337 if (!av_bprint_is_complete(&s->buffer)) {
338 return AVERROR(ENOMEM);
341 avctx->extradata_size = s->buffer.len;
342 avctx->extradata = av_mallocz(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
343 if (!avctx->extradata) {
344 return AVERROR(ENOMEM);
347 memcpy(avctx->extradata, s->buffer.str, avctx->extradata_size);
348 av_bprint_clear(&s->buffer);
353 static av_cold int mov_text_encode_init(AVCodecContext *avctx)
356 MovTextContext *s = avctx->priv_data;
359 av_bprint_init(&s->buffer, 0, AV_BPRINT_SIZE_UNLIMITED);
361 s->style_attributes_temp = av_mallocz(sizeof(*s->style_attributes_temp));
362 if (!s->style_attributes_temp) {
363 ret = AVERROR(ENOMEM);
367 s->ass_ctx = ff_ass_split(avctx->subtitle_header);
369 ret = AVERROR_INVALIDDATA;
372 ret = encode_sample_description(avctx);
379 mov_text_encode_close(avctx);
383 // Start a new style box if needed
384 static int mov_text_style_start(MovTextContext *s)
386 // there's an existing style entry
387 if (s->style_attributes_temp->style_start == s->text_pos)
388 // Still at same text pos, use same entry
390 if (s->style_attributes_temp->style_flag != s->d.style_flag ||
391 s->style_attributes_temp->style_color != s->d.style_color ||
392 s->style_attributes_temp->style_fontID != s->d.style_fontID ||
393 s->style_attributes_temp->style_fontsize != s->d.style_fontsize) {
394 // last style != defaults, end the style entry and start a new one
395 s->box_flags |= STYL_BOX;
396 s->style_attributes_temp->style_end = s->text_pos;
397 av_dynarray_add(&s->style_attributes, &s->count, s->style_attributes_temp);
398 s->style_attributes_temp = av_malloc(sizeof(*s->style_attributes_temp));
399 if (!s->style_attributes_temp) {
401 av_bprint_clear(&s->buffer);
402 s->box_flags &= ~STYL_BOX;
406 *s->style_attributes_temp = s->d;
407 s->style_attributes_temp->style_start = s->text_pos;
408 } else { // style entry matches defaults, drop entry
409 *s->style_attributes_temp = s->d;
410 s->style_attributes_temp->style_start = s->text_pos;
415 static uint8_t mov_text_style_to_flag(const char style)
417 uint8_t style_flag = 0;
421 style_flag = STYLE_FLAG_BOLD;
424 style_flag = STYLE_FLAG_ITALIC;
427 style_flag = STYLE_FLAG_UNDERLINE;
433 static void mov_text_style_set(MovTextContext *s, uint8_t style_flags)
435 if (!s->style_attributes_temp ||
436 !((s->style_attributes_temp->style_flag & style_flags) ^ style_flags)) {
437 // setting flags that that are already set
440 if (mov_text_style_start(s))
441 s->style_attributes_temp->style_flag |= style_flags;
444 static void mov_text_style_cb(void *priv, const char style, int close)
446 MovTextContext *s = priv;
447 uint8_t style_flag = mov_text_style_to_flag(style);
449 if (!s->style_attributes_temp ||
450 !!(s->style_attributes_temp->style_flag & style_flag) != close) {
451 // setting flag that is already set
454 if (mov_text_style_start(s)) {
456 s->style_attributes_temp->style_flag |= style_flag;
458 s->style_attributes_temp->style_flag &= ~style_flag;
462 static void mov_text_color_set(MovTextContext *s, uint32_t color)
464 if (!s->style_attributes_temp ||
465 (s->style_attributes_temp->style_color & 0xffffff00) == color) {
466 // color hasn't changed
469 if (mov_text_style_start(s))
470 s->style_attributes_temp->style_color = (color & 0xffffff00) |
471 (s->style_attributes_temp->style_color & 0xff);
474 static void mov_text_color_cb(void *priv, unsigned int color, unsigned int color_id)
476 MovTextContext *s = priv;
478 color = BGR_TO_RGB(color) << 8;
479 if (color_id == 1) { //primary color changes
480 mov_text_color_set(s, color);
481 } else if (color_id == 2) { //secondary color changes
482 if (!(s->box_flags & HCLR_BOX))
483 // Highlight alpha not set yet, use current primary alpha
484 s->hclr.color = s->style_attributes_temp->style_color;
485 if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) {
486 s->box_flags |= HCLR_BOX;
487 s->box_flags |= HLIT_BOX;
488 s->hlit.start = s->text_pos;
489 s->hclr.color = color | (s->hclr.color & 0xFF);
492 s->hlit.end = s->text_pos;
493 /* If there are more than one secondary color changes in ASS,
494 take start of first section and end of last section. Movtext
495 allows only one highlight box per sample.
498 // Movtext does not support changes to other color_id (outline, background)
501 static void mov_text_alpha_set(MovTextContext *s, uint8_t alpha)
503 if (!s->style_attributes_temp ||
504 (s->style_attributes_temp->style_color & 0xff) == alpha) {
505 // color hasn't changed
508 if (mov_text_style_start(s))
509 s->style_attributes_temp->style_color =
510 (s->style_attributes_temp->style_color & 0xffffff00) | alpha;
513 static void mov_text_alpha_cb(void *priv, int alpha, int alpha_id)
515 MovTextContext *s = priv;
518 if (alpha_id == 1) // primary alpha changes
519 mov_text_alpha_set(s, alpha);
520 else if (alpha_id == 2) { //secondary alpha changes
521 if (!(s->box_flags & HCLR_BOX))
522 // Highlight color not set yet, use current primary color
523 s->hclr.color = s->style_attributes_temp->style_color;
524 if (!(s->box_flags & HLIT_BOX) || s->hlit.start == s->text_pos) {
525 s->box_flags |= HCLR_BOX;
526 s->box_flags |= HLIT_BOX;
527 s->hlit.start = s->text_pos;
528 s->hclr.color = (s->hclr.color & 0xffffff00) | alpha;
531 s->hlit.end = s->text_pos;
533 // Movtext does not support changes to other alpha_id (outline, background)
536 static uint16_t find_font_id(MovTextContext * s, const char * name)
539 for (i = 0; i < s->font_count; i++) {
540 if (!strcmp(name, s->fonts[i]))
546 static void mov_text_font_name_set(MovTextContext *s, const char *name)
548 int fontID = find_font_id(s, name);
549 if (!s->style_attributes_temp ||
550 s->style_attributes_temp->style_fontID == fontID) {
551 // color hasn't changed
554 if (mov_text_style_start(s))
555 s->style_attributes_temp->style_fontID = fontID;
558 static void mov_text_font_name_cb(void *priv, const char *name)
560 mov_text_font_name_set((MovTextContext*)priv, name);
563 static void mov_text_font_size_set(MovTextContext *s, int size)
565 size = FONTSIZE_SCALE(s, size);
566 if (!s->style_attributes_temp ||
567 s->style_attributes_temp->style_fontsize == size) {
568 // color hasn't changed
571 if (mov_text_style_start(s))
572 s->style_attributes_temp->style_fontsize = size;
575 static void mov_text_font_size_cb(void *priv, int size)
577 mov_text_font_size_set((MovTextContext*)priv, size);
580 static void mov_text_end_cb(void *priv)
582 // End of text, close any open style record
583 mov_text_style_start((MovTextContext*)priv);
586 static void mov_text_ass_style_set(MovTextContext *s, ASSStyle *style)
588 uint8_t style_flags, alpha;
592 style_flags = (!!style->bold * STYLE_FLAG_BOLD) |
593 (!!style->italic * STYLE_FLAG_ITALIC) |
594 (!!style->underline * STYLE_FLAG_UNDERLINE);
595 mov_text_style_set(s, style_flags);
596 color = BGR_TO_RGB(style->primary_color & 0xffffff) << 8;
597 mov_text_color_set(s, color);
598 alpha = 255 - ((uint32_t)style->primary_color >> 24);
599 mov_text_alpha_set(s, alpha);
600 mov_text_font_size_set(s, style->font_size);
601 mov_text_font_name_set(s, style->font_name);
603 // End current style record, go back to defaults
604 mov_text_style_start(s);
608 static void mov_text_dialog(MovTextContext *s, ASSDialog *dialog)
610 ASSStyle * style = ff_ass_style_get(s->ass_ctx, dialog->style);
612 s->ass_dialog_style = style;
613 mov_text_ass_style_set(s, style);
616 static void mov_text_cancel_overrides_cb(void *priv, const char * style_name)
618 MovTextContext *s = priv;
621 if (!style_name || !*style_name)
622 style = s->ass_dialog_style;
624 style= ff_ass_style_get(s->ass_ctx, style_name);
626 mov_text_ass_style_set(s, style);
629 static uint16_t utf8_strlen(const char *text, int len)
631 uint16_t i = 0, ret = 0;
636 else if ((c & 0xE0) == 0xC0)
638 else if ((c & 0xF0) == 0xE0)
640 else if ((c & 0xF8) == 0xF0)
649 static void mov_text_text_cb(void *priv, const char *text, int len)
651 uint16_t utf8_len = utf8_strlen(text, len);
652 MovTextContext *s = priv;
653 av_bprint_append_data(&s->buffer, text, len);
654 // If it's not utf-8, just use the byte length
655 s->text_pos += utf8_len ? utf8_len : len;
656 s->byte_count += len;
659 static void mov_text_new_line_cb(void *priv, int forced)
661 MovTextContext *s = priv;
662 av_bprint_append_data(&s->buffer, "\n", 1);
667 static const ASSCodesCallbacks mov_text_callbacks = {
668 .text = mov_text_text_cb,
669 .new_line = mov_text_new_line_cb,
670 .style = mov_text_style_cb,
671 .color = mov_text_color_cb,
672 .alpha = mov_text_alpha_cb,
673 .font_name = mov_text_font_name_cb,
674 .font_size = mov_text_font_size_cb,
675 .cancel_overrides = mov_text_cancel_overrides_cb,
676 .end = mov_text_end_cb,
679 static int mov_text_encode_frame(AVCodecContext *avctx, unsigned char *buf,
680 int bufsize, const AVSubtitle *sub)
682 MovTextContext *s = avctx->priv_data;
691 for (i = 0; i < sub->num_rects; i++) {
692 const char *ass = sub->rects[i]->ass;
694 if (sub->rects[i]->type != SUBTITLE_ASS) {
695 av_log(avctx, AV_LOG_ERROR, "Only SUBTITLE_ASS type supported.\n");
696 return AVERROR(EINVAL);
699 #if FF_API_ASS_TIMING
700 if (!strncmp(ass, "Dialogue: ", 10)) {
702 dialog = ff_ass_split_dialog(s->ass_ctx, ass, 0, &num);
703 for (; dialog && num--; dialog++) {
704 mov_text_dialog(s, dialog);
705 ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
709 dialog = ff_ass_split_dialog2(s->ass_ctx, ass);
711 return AVERROR(ENOMEM);
712 mov_text_dialog(s, dialog);
713 ff_ass_split_override_codes(&mov_text_callbacks, s, dialog->text);
714 ff_ass_free_dialog(&dialog);
715 #if FF_API_ASS_TIMING
719 for (j = 0; j < box_count; j++) {
720 box_types[j].encode(s, box_types[j].type);
724 AV_WB16(buf, s->byte_count);
727 if (!av_bprint_is_complete(&s->buffer)) {
728 length = AVERROR(ENOMEM);
732 if (!s->buffer.len) {
737 if (s->buffer.len > bufsize - 3) {
738 av_log(avctx, AV_LOG_ERROR, "Buffer too small for ASS event.\n");
739 length = AVERROR_BUFFER_TOO_SMALL;
743 memcpy(buf, s->buffer.str, s->buffer.len);
744 length = s->buffer.len + 2;
747 av_bprint_clear(&s->buffer);
751 #define OFFSET(x) offsetof(MovTextContext, x)
752 #define FLAGS AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_SUBTITLE_PARAM
753 static const AVOption options[] = {
754 { "height", "Frame height, usually video height", OFFSET(frame_height), AV_OPT_TYPE_INT, {.i64=0}, 0, INT_MAX, FLAGS },
758 static const AVClass mov_text_encoder_class = {
759 .class_name = "MOV text enoder",
760 .item_name = av_default_item_name,
762 .version = LIBAVUTIL_VERSION_INT,
765 AVCodec ff_movtext_encoder = {
767 .long_name = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
768 .type = AVMEDIA_TYPE_SUBTITLE,
769 .id = AV_CODEC_ID_MOV_TEXT,
770 .priv_data_size = sizeof(MovTextContext),
771 .priv_class = &mov_text_encoder_class,
772 .init = mov_text_encode_init,
773 .encode_sub = mov_text_encode_frame,
774 .close = mov_text_encode_close,