1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * Copyright (c) 2012 Clément Bœsch
3cabdff1aSopenharmony_ci *
4cabdff1aSopenharmony_ci * This file is part of FFmpeg.
5cabdff1aSopenharmony_ci *
6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
10cabdff1aSopenharmony_ci *
11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14cabdff1aSopenharmony_ci * Lesser General Public License for more details.
15cabdff1aSopenharmony_ci *
16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19cabdff1aSopenharmony_ci */
20cabdff1aSopenharmony_ci
21cabdff1aSopenharmony_ci/**
22cabdff1aSopenharmony_ci * @file
23cabdff1aSopenharmony_ci * MicroDVD subtitle decoder
24cabdff1aSopenharmony_ci *
25cabdff1aSopenharmony_ci * Based on the specifications found here:
26cabdff1aSopenharmony_ci * https://trac.videolan.org/vlc/ticket/1825#comment:6
27cabdff1aSopenharmony_ci */
28cabdff1aSopenharmony_ci
29cabdff1aSopenharmony_ci#include "libavutil/avstring.h"
30cabdff1aSopenharmony_ci#include "libavutil/parseutils.h"
31cabdff1aSopenharmony_ci#include "libavutil/bprint.h"
32cabdff1aSopenharmony_ci#include "avcodec.h"
33cabdff1aSopenharmony_ci#include "ass.h"
34cabdff1aSopenharmony_ci#include "codec_internal.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_cistatic int indexof(const char *s, int c)
37cabdff1aSopenharmony_ci{
38cabdff1aSopenharmony_ci    char *f = strchr(s, c);
39cabdff1aSopenharmony_ci    return f ? (f - s) : -1;
40cabdff1aSopenharmony_ci}
41cabdff1aSopenharmony_ci
42cabdff1aSopenharmony_cistruct microdvd_tag {
43cabdff1aSopenharmony_ci    char key;
44cabdff1aSopenharmony_ci    int persistent;
45cabdff1aSopenharmony_ci    uint32_t data1;
46cabdff1aSopenharmony_ci    uint32_t data2;
47cabdff1aSopenharmony_ci    char *data_string;
48cabdff1aSopenharmony_ci    int data_string_len;
49cabdff1aSopenharmony_ci};
50cabdff1aSopenharmony_ci
51cabdff1aSopenharmony_ci#define MICRODVD_PERSISTENT_OFF     0
52cabdff1aSopenharmony_ci#define MICRODVD_PERSISTENT_ON      1
53cabdff1aSopenharmony_ci#define MICRODVD_PERSISTENT_OPENED  2
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_ci// Color, Font, Size, cHarset, stYle, Position, cOordinate
56cabdff1aSopenharmony_ci#define MICRODVD_TAGS "cfshyYpo"
57cabdff1aSopenharmony_ci
58cabdff1aSopenharmony_cistatic void microdvd_set_tag(struct microdvd_tag *tags, struct microdvd_tag tag)
59cabdff1aSopenharmony_ci{
60cabdff1aSopenharmony_ci    int tag_index = indexof(MICRODVD_TAGS, tag.key);
61cabdff1aSopenharmony_ci
62cabdff1aSopenharmony_ci    if (tag_index < 0)
63cabdff1aSopenharmony_ci        return;
64cabdff1aSopenharmony_ci    memcpy(&tags[tag_index], &tag, sizeof(tag));
65cabdff1aSopenharmony_ci}
66cabdff1aSopenharmony_ci
67cabdff1aSopenharmony_ci// italic, bold, underline, strike-through
68cabdff1aSopenharmony_ci#define MICRODVD_STYLES "ibus"
69cabdff1aSopenharmony_ci
70cabdff1aSopenharmony_ci/* some samples have lines that start with a / indicating non persistent italic
71cabdff1aSopenharmony_ci * marker */
72cabdff1aSopenharmony_cistatic char *check_for_italic_slash_marker(struct microdvd_tag *tags, char *s)
73cabdff1aSopenharmony_ci{
74cabdff1aSopenharmony_ci    if (*s == '/') {
75cabdff1aSopenharmony_ci        struct microdvd_tag tag = tags[indexof(MICRODVD_TAGS, 'y')];
76cabdff1aSopenharmony_ci        tag.key = 'y';
77cabdff1aSopenharmony_ci        tag.data1 |= 1 << 0 /* 'i' position in MICRODVD_STYLES */;
78cabdff1aSopenharmony_ci        microdvd_set_tag(tags, tag);
79cabdff1aSopenharmony_ci        s++;
80cabdff1aSopenharmony_ci    }
81cabdff1aSopenharmony_ci    return s;
82cabdff1aSopenharmony_ci}
83cabdff1aSopenharmony_ci
84cabdff1aSopenharmony_cistatic char *microdvd_load_tags(struct microdvd_tag *tags, char *s)
85cabdff1aSopenharmony_ci{
86cabdff1aSopenharmony_ci    s = check_for_italic_slash_marker(tags, s);
87cabdff1aSopenharmony_ci
88cabdff1aSopenharmony_ci    while (*s == '{') {
89cabdff1aSopenharmony_ci        char *start = s;
90cabdff1aSopenharmony_ci        char tag_char = *(s + 1);
91cabdff1aSopenharmony_ci        struct microdvd_tag tag = {0};
92cabdff1aSopenharmony_ci
93cabdff1aSopenharmony_ci        if (!tag_char || *(s + 2) != ':')
94cabdff1aSopenharmony_ci            break;
95cabdff1aSopenharmony_ci        s += 3;
96cabdff1aSopenharmony_ci
97cabdff1aSopenharmony_ci        switch (tag_char) {
98cabdff1aSopenharmony_ci
99cabdff1aSopenharmony_ci        /* Style */
100cabdff1aSopenharmony_ci        case 'Y':
101cabdff1aSopenharmony_ci            tag.persistent = MICRODVD_PERSISTENT_ON;
102cabdff1aSopenharmony_ci        case 'y':
103cabdff1aSopenharmony_ci            while (*s && *s != '}' && s - start < 256) {
104cabdff1aSopenharmony_ci                int style_index = indexof(MICRODVD_STYLES, *s);
105cabdff1aSopenharmony_ci
106cabdff1aSopenharmony_ci                if (style_index >= 0)
107cabdff1aSopenharmony_ci                    tag.data1 |= (1 << style_index);
108cabdff1aSopenharmony_ci                s++;
109cabdff1aSopenharmony_ci            }
110cabdff1aSopenharmony_ci            if (*s != '}')
111cabdff1aSopenharmony_ci                break;
112cabdff1aSopenharmony_ci            /* We must distinguish persistent and non-persistent styles
113cabdff1aSopenharmony_ci             * to handle this kind of style tags: {y:ib}{Y:us} */
114cabdff1aSopenharmony_ci            tag.key = tag_char;
115cabdff1aSopenharmony_ci            break;
116cabdff1aSopenharmony_ci
117cabdff1aSopenharmony_ci        /* Color */
118cabdff1aSopenharmony_ci        case 'C':
119cabdff1aSopenharmony_ci            tag.persistent = MICRODVD_PERSISTENT_ON;
120cabdff1aSopenharmony_ci        case 'c':
121cabdff1aSopenharmony_ci            while (*s == '$' || *s == '#')
122cabdff1aSopenharmony_ci                s++;
123cabdff1aSopenharmony_ci            tag.data1 = strtol(s, &s, 16) & 0x00ffffff;
124cabdff1aSopenharmony_ci            if (*s != '}')
125cabdff1aSopenharmony_ci                break;
126cabdff1aSopenharmony_ci            tag.key = 'c';
127cabdff1aSopenharmony_ci            break;
128cabdff1aSopenharmony_ci
129cabdff1aSopenharmony_ci        /* Font name */
130cabdff1aSopenharmony_ci        case 'F':
131cabdff1aSopenharmony_ci            tag.persistent = MICRODVD_PERSISTENT_ON;
132cabdff1aSopenharmony_ci        case 'f': {
133cabdff1aSopenharmony_ci            int len = indexof(s, '}');
134cabdff1aSopenharmony_ci            if (len < 0)
135cabdff1aSopenharmony_ci                break;
136cabdff1aSopenharmony_ci            tag.data_string = s;
137cabdff1aSopenharmony_ci            tag.data_string_len = len;
138cabdff1aSopenharmony_ci            s += len;
139cabdff1aSopenharmony_ci            tag.key = 'f';
140cabdff1aSopenharmony_ci            break;
141cabdff1aSopenharmony_ci        }
142cabdff1aSopenharmony_ci
143cabdff1aSopenharmony_ci        /* Font size */
144cabdff1aSopenharmony_ci        case 'S':
145cabdff1aSopenharmony_ci            tag.persistent = MICRODVD_PERSISTENT_ON;
146cabdff1aSopenharmony_ci        case 's':
147cabdff1aSopenharmony_ci            tag.data1 = strtol(s, &s, 10);
148cabdff1aSopenharmony_ci            if (*s != '}')
149cabdff1aSopenharmony_ci                break;
150cabdff1aSopenharmony_ci            tag.key = 's';
151cabdff1aSopenharmony_ci            break;
152cabdff1aSopenharmony_ci
153cabdff1aSopenharmony_ci        /* Charset */
154cabdff1aSopenharmony_ci        case 'H': {
155cabdff1aSopenharmony_ci            //TODO: not yet handled, just parsed.
156cabdff1aSopenharmony_ci            int len = indexof(s, '}');
157cabdff1aSopenharmony_ci            if (len < 0)
158cabdff1aSopenharmony_ci                break;
159cabdff1aSopenharmony_ci            tag.data_string = s;
160cabdff1aSopenharmony_ci            tag.data_string_len = len;
161cabdff1aSopenharmony_ci            s += len;
162cabdff1aSopenharmony_ci            tag.key = 'h';
163cabdff1aSopenharmony_ci            break;
164cabdff1aSopenharmony_ci        }
165cabdff1aSopenharmony_ci
166cabdff1aSopenharmony_ci        /* Position */
167cabdff1aSopenharmony_ci        case 'P':
168cabdff1aSopenharmony_ci            if (!*s)
169cabdff1aSopenharmony_ci                break;
170cabdff1aSopenharmony_ci            tag.persistent = MICRODVD_PERSISTENT_ON;
171cabdff1aSopenharmony_ci            tag.data1 = (*s++ == '1');
172cabdff1aSopenharmony_ci            if (*s != '}')
173cabdff1aSopenharmony_ci                break;
174cabdff1aSopenharmony_ci            tag.key = 'p';
175cabdff1aSopenharmony_ci            break;
176cabdff1aSopenharmony_ci
177cabdff1aSopenharmony_ci        /* Coordinates */
178cabdff1aSopenharmony_ci        case 'o':
179cabdff1aSopenharmony_ci            tag.persistent = MICRODVD_PERSISTENT_ON;
180cabdff1aSopenharmony_ci            tag.data1 = strtol(s, &s, 10);
181cabdff1aSopenharmony_ci            if (*s != ',')
182cabdff1aSopenharmony_ci                break;
183cabdff1aSopenharmony_ci            s++;
184cabdff1aSopenharmony_ci            tag.data2 = strtol(s, &s, 10);
185cabdff1aSopenharmony_ci            if (*s != '}')
186cabdff1aSopenharmony_ci                break;
187cabdff1aSopenharmony_ci            tag.key = 'o';
188cabdff1aSopenharmony_ci            break;
189cabdff1aSopenharmony_ci
190cabdff1aSopenharmony_ci        default:    /* Unknown tag, we consider it's text */
191cabdff1aSopenharmony_ci            break;
192cabdff1aSopenharmony_ci        }
193cabdff1aSopenharmony_ci
194cabdff1aSopenharmony_ci        if (tag.key == 0)
195cabdff1aSopenharmony_ci            return start;
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_ci        microdvd_set_tag(tags, tag);
198cabdff1aSopenharmony_ci        s++;
199cabdff1aSopenharmony_ci    }
200cabdff1aSopenharmony_ci    return check_for_italic_slash_marker(tags, s);
201cabdff1aSopenharmony_ci}
202cabdff1aSopenharmony_ci
203cabdff1aSopenharmony_cistatic void microdvd_open_tags(AVBPrint *new_line, struct microdvd_tag *tags)
204cabdff1aSopenharmony_ci{
205cabdff1aSopenharmony_ci    int i, sidx;
206cabdff1aSopenharmony_ci    for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
207cabdff1aSopenharmony_ci        if (tags[i].persistent == MICRODVD_PERSISTENT_OPENED)
208cabdff1aSopenharmony_ci            continue;
209cabdff1aSopenharmony_ci        switch (tags[i].key) {
210cabdff1aSopenharmony_ci        case 'Y':
211cabdff1aSopenharmony_ci        case 'y':
212cabdff1aSopenharmony_ci            for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++)
213cabdff1aSopenharmony_ci                if (tags[i].data1 & (1 << sidx))
214cabdff1aSopenharmony_ci                    av_bprintf(new_line, "{\\%c1}", MICRODVD_STYLES[sidx]);
215cabdff1aSopenharmony_ci            break;
216cabdff1aSopenharmony_ci
217cabdff1aSopenharmony_ci        case 'c':
218cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\c&H%06"PRIX32"&}", tags[i].data1);
219cabdff1aSopenharmony_ci            break;
220cabdff1aSopenharmony_ci
221cabdff1aSopenharmony_ci        case 'f':
222cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\fn%.*s}",
223cabdff1aSopenharmony_ci                       tags[i].data_string_len, tags[i].data_string);
224cabdff1aSopenharmony_ci            break;
225cabdff1aSopenharmony_ci
226cabdff1aSopenharmony_ci        case 's':
227cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\fs%"PRId32"}", tags[i].data1);
228cabdff1aSopenharmony_ci            break;
229cabdff1aSopenharmony_ci
230cabdff1aSopenharmony_ci        case 'p':
231cabdff1aSopenharmony_ci            if (tags[i].data1 == 0)
232cabdff1aSopenharmony_ci                av_bprintf(new_line, "{\\an8}");
233cabdff1aSopenharmony_ci            break;
234cabdff1aSopenharmony_ci
235cabdff1aSopenharmony_ci        case 'o':
236cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\pos(%"PRId32",%"PRId32")}",
237cabdff1aSopenharmony_ci                       tags[i].data1, tags[i].data2);
238cabdff1aSopenharmony_ci            break;
239cabdff1aSopenharmony_ci        }
240cabdff1aSopenharmony_ci        if (tags[i].persistent == MICRODVD_PERSISTENT_ON)
241cabdff1aSopenharmony_ci            tags[i].persistent = MICRODVD_PERSISTENT_OPENED;
242cabdff1aSopenharmony_ci    }
243cabdff1aSopenharmony_ci}
244cabdff1aSopenharmony_ci
245cabdff1aSopenharmony_cistatic void microdvd_close_no_persistent_tags(AVBPrint *new_line,
246cabdff1aSopenharmony_ci                                              struct microdvd_tag *tags)
247cabdff1aSopenharmony_ci{
248cabdff1aSopenharmony_ci    int i, sidx;
249cabdff1aSopenharmony_ci
250cabdff1aSopenharmony_ci    for (i = sizeof(MICRODVD_TAGS) - 2; i >= 0; i--) {
251cabdff1aSopenharmony_ci        if (tags[i].persistent != MICRODVD_PERSISTENT_OFF)
252cabdff1aSopenharmony_ci            continue;
253cabdff1aSopenharmony_ci        switch (tags[i].key) {
254cabdff1aSopenharmony_ci
255cabdff1aSopenharmony_ci        case 'y':
256cabdff1aSopenharmony_ci            for (sidx = sizeof(MICRODVD_STYLES) - 2; sidx >= 0; sidx--)
257cabdff1aSopenharmony_ci                if (tags[i].data1 & (1 << sidx))
258cabdff1aSopenharmony_ci                    av_bprintf(new_line, "{\\%c0}", MICRODVD_STYLES[sidx]);
259cabdff1aSopenharmony_ci            break;
260cabdff1aSopenharmony_ci
261cabdff1aSopenharmony_ci        case 'c':
262cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\c}");
263cabdff1aSopenharmony_ci            break;
264cabdff1aSopenharmony_ci
265cabdff1aSopenharmony_ci        case 'f':
266cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\fn}");
267cabdff1aSopenharmony_ci            break;
268cabdff1aSopenharmony_ci
269cabdff1aSopenharmony_ci        case 's':
270cabdff1aSopenharmony_ci            av_bprintf(new_line, "{\\fs}");
271cabdff1aSopenharmony_ci            break;
272cabdff1aSopenharmony_ci        }
273cabdff1aSopenharmony_ci        tags[i].key = 0;
274cabdff1aSopenharmony_ci    }
275cabdff1aSopenharmony_ci}
276cabdff1aSopenharmony_ci
277cabdff1aSopenharmony_cistatic int microdvd_decode_frame(AVCodecContext *avctx, AVSubtitle *sub,
278cabdff1aSopenharmony_ci                                 int *got_sub_ptr, const AVPacket *avpkt)
279cabdff1aSopenharmony_ci{
280cabdff1aSopenharmony_ci    AVBPrint new_line;
281cabdff1aSopenharmony_ci    char *line = avpkt->data;
282cabdff1aSopenharmony_ci    char *end = avpkt->data + avpkt->size;
283cabdff1aSopenharmony_ci    FFASSDecoderContext *s = avctx->priv_data;
284cabdff1aSopenharmony_ci    struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
285cabdff1aSopenharmony_ci
286cabdff1aSopenharmony_ci    if (avpkt->size <= 0)
287cabdff1aSopenharmony_ci        return avpkt->size;
288cabdff1aSopenharmony_ci
289cabdff1aSopenharmony_ci    av_bprint_init(&new_line, 0, 2048);
290cabdff1aSopenharmony_ci
291cabdff1aSopenharmony_ci    // subtitle content
292cabdff1aSopenharmony_ci    while (line < end && *line) {
293cabdff1aSopenharmony_ci
294cabdff1aSopenharmony_ci        // parse MicroDVD tags, and open them in ASS
295cabdff1aSopenharmony_ci        line = microdvd_load_tags(tags, line);
296cabdff1aSopenharmony_ci        microdvd_open_tags(&new_line, tags);
297cabdff1aSopenharmony_ci
298cabdff1aSopenharmony_ci        // simple copy until EOL or forced carriage return
299cabdff1aSopenharmony_ci        while (line < end && *line && *line != '|') {
300cabdff1aSopenharmony_ci            av_bprint_chars(&new_line, *line, 1);
301cabdff1aSopenharmony_ci            line++;
302cabdff1aSopenharmony_ci        }
303cabdff1aSopenharmony_ci
304cabdff1aSopenharmony_ci        // line split
305cabdff1aSopenharmony_ci        if (line < end && *line == '|') {
306cabdff1aSopenharmony_ci            microdvd_close_no_persistent_tags(&new_line, tags);
307cabdff1aSopenharmony_ci            av_bprintf(&new_line, "\\N");
308cabdff1aSopenharmony_ci            line++;
309cabdff1aSopenharmony_ci        }
310cabdff1aSopenharmony_ci    }
311cabdff1aSopenharmony_ci    if (new_line.len) {
312cabdff1aSopenharmony_ci        int ret = ff_ass_add_rect(sub, new_line.str, s->readorder++, 0, NULL, NULL);
313cabdff1aSopenharmony_ci        av_bprint_finalize(&new_line, NULL);
314cabdff1aSopenharmony_ci        if (ret < 0)
315cabdff1aSopenharmony_ci            return ret;
316cabdff1aSopenharmony_ci    }
317cabdff1aSopenharmony_ci
318cabdff1aSopenharmony_ci    *got_sub_ptr = sub->num_rects > 0;
319cabdff1aSopenharmony_ci    return avpkt->size;
320cabdff1aSopenharmony_ci}
321cabdff1aSopenharmony_ci
322cabdff1aSopenharmony_cistatic int microdvd_init(AVCodecContext *avctx)
323cabdff1aSopenharmony_ci{
324cabdff1aSopenharmony_ci    int i, sidx;
325cabdff1aSopenharmony_ci    AVBPrint font_buf;
326cabdff1aSopenharmony_ci    int font_size    = ASS_DEFAULT_FONT_SIZE;
327cabdff1aSopenharmony_ci    int color        = ASS_DEFAULT_COLOR;
328cabdff1aSopenharmony_ci    int bold         = ASS_DEFAULT_BOLD;
329cabdff1aSopenharmony_ci    int italic       = ASS_DEFAULT_ITALIC;
330cabdff1aSopenharmony_ci    int underline    = ASS_DEFAULT_UNDERLINE;
331cabdff1aSopenharmony_ci    int alignment    = ASS_DEFAULT_ALIGNMENT;
332cabdff1aSopenharmony_ci    struct microdvd_tag tags[sizeof(MICRODVD_TAGS) - 1] = {{0}};
333cabdff1aSopenharmony_ci
334cabdff1aSopenharmony_ci    av_bprint_init(&font_buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
335cabdff1aSopenharmony_ci    av_bprintf(&font_buf, "%s", ASS_DEFAULT_FONT);
336cabdff1aSopenharmony_ci
337cabdff1aSopenharmony_ci    if (avctx->extradata) {
338cabdff1aSopenharmony_ci        microdvd_load_tags(tags, avctx->extradata);
339cabdff1aSopenharmony_ci        for (i = 0; i < sizeof(MICRODVD_TAGS) - 1; i++) {
340cabdff1aSopenharmony_ci            switch (av_tolower(tags[i].key)) {
341cabdff1aSopenharmony_ci            case 'y':
342cabdff1aSopenharmony_ci                for (sidx = 0; sidx < sizeof(MICRODVD_STYLES) - 1; sidx++) {
343cabdff1aSopenharmony_ci                    if (tags[i].data1 & (1 << sidx)) {
344cabdff1aSopenharmony_ci                        switch (MICRODVD_STYLES[sidx]) {
345cabdff1aSopenharmony_ci                        case 'i': italic    = 1; break;
346cabdff1aSopenharmony_ci                        case 'b': bold      = 1; break;
347cabdff1aSopenharmony_ci                        case 'u': underline = 1; break;
348cabdff1aSopenharmony_ci                        }
349cabdff1aSopenharmony_ci                    }
350cabdff1aSopenharmony_ci                }
351cabdff1aSopenharmony_ci                break;
352cabdff1aSopenharmony_ci
353cabdff1aSopenharmony_ci            case 'c': color     = tags[i].data1; break;
354cabdff1aSopenharmony_ci            case 's': font_size = tags[i].data1; break;
355cabdff1aSopenharmony_ci            case 'p': alignment =             8; break;
356cabdff1aSopenharmony_ci
357cabdff1aSopenharmony_ci            case 'f':
358cabdff1aSopenharmony_ci                av_bprint_clear(&font_buf);
359cabdff1aSopenharmony_ci                av_bprintf(&font_buf, "%.*s",
360cabdff1aSopenharmony_ci                           tags[i].data_string_len, tags[i].data_string);
361cabdff1aSopenharmony_ci                break;
362cabdff1aSopenharmony_ci            }
363cabdff1aSopenharmony_ci        }
364cabdff1aSopenharmony_ci    }
365cabdff1aSopenharmony_ci    return ff_ass_subtitle_header(avctx, font_buf.str, font_size, color,
366cabdff1aSopenharmony_ci                                  ASS_DEFAULT_BACK_COLOR, bold, italic,
367cabdff1aSopenharmony_ci                                  underline, ASS_DEFAULT_BORDERSTYLE,
368cabdff1aSopenharmony_ci                                  alignment);
369cabdff1aSopenharmony_ci}
370cabdff1aSopenharmony_ci
371cabdff1aSopenharmony_ciconst FFCodec ff_microdvd_decoder = {
372cabdff1aSopenharmony_ci    .p.name       = "microdvd",
373cabdff1aSopenharmony_ci    .p.long_name  = NULL_IF_CONFIG_SMALL("MicroDVD subtitle"),
374cabdff1aSopenharmony_ci    .p.type       = AVMEDIA_TYPE_SUBTITLE,
375cabdff1aSopenharmony_ci    .p.id         = AV_CODEC_ID_MICRODVD,
376cabdff1aSopenharmony_ci    .init         = microdvd_init,
377cabdff1aSopenharmony_ci    FF_CODEC_DECODE_SUB_CB(microdvd_decode_frame),
378cabdff1aSopenharmony_ci    .flush        = ff_ass_decoder_flush,
379cabdff1aSopenharmony_ci    .priv_data_size = sizeof(FFASSDecoderContext),
380cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
381cabdff1aSopenharmony_ci};
382