Improve upon dynamic arrays- movtext subtitles
[ffmpeg.git] / libavcodec / movtextdec.c
1 /*
2  * 3GPP TS 26.245 Timed Text decoder
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 "avcodec.h"
23 #include "ass.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/common.h"
26 #include "libavutil/bprint.h"
27 #include "libavutil/intreadwrite.h"
28 #include "libavutil/mem.h"
29
30 #define STYLE_FLAG_BOLD         1
31 #define STYLE_FLAG_ITALIC       2
32 #define STYLE_FLAG_UNDERLINE    4
33
34 static int text_to_ass(AVBPrint *buf, const char *text, const char *text_end,
35                         char **style_start, char **style_end,
36                         uint8_t **style_flags, int style_entries)
37 {
38     int i = 0;
39     while (text < text_end) {
40         for (i = 0; i < style_entries; i++) {
41             if (*style_flags[i] && text == style_start[i]) {
42                 if (*style_flags[i] & STYLE_FLAG_BOLD)
43                     av_bprintf(buf, "{\\b1}");
44                 if (*style_flags[i] & STYLE_FLAG_ITALIC)
45                     av_bprintf(buf, "{\\i1}");
46                 if (*style_flags[i] & STYLE_FLAG_UNDERLINE)
47                     av_bprintf(buf, "{\\u1}");
48             }
49         }
50
51         switch (*text) {
52         case '\r':
53             break;
54         case '\n':
55             av_bprintf(buf, "\\N");
56             break;
57         default:
58             av_bprint_chars(buf, *text, 1);
59             break;
60         }
61
62         for (i = 0; i < style_entries; i++) {
63             if (*style_flags[i] && text == style_end[i]) {
64                 if (*style_flags[i] & STYLE_FLAG_BOLD)
65                     av_bprintf(buf, "{\\b0}");
66                 if (*style_flags[i] & STYLE_FLAG_ITALIC)
67                     av_bprintf(buf, "{\\i0}");
68                 if (*style_flags[i] & STYLE_FLAG_UNDERLINE)
69                     av_bprintf(buf, "{\\u0}");
70             }
71         }
72         text++;
73     }
74
75     return 0;
76 }
77
78 static int mov_text_init(AVCodecContext *avctx) {
79     /*
80      * TODO: Handle the default text style.
81      * NB: Most players ignore styles completely, with the result that
82      * it's very common to find files where the default style is broken
83      * and respecting it results in a worse experience than ignoring it.
84      */
85     return ff_ass_subtitle_header_default(avctx);
86 }
87
88 static int mov_text_decode_frame(AVCodecContext *avctx,
89                             void *data, int *got_sub_ptr, AVPacket *avpkt)
90 {
91     AVSubtitle *sub = data;
92     int ret, ts_start, ts_end;
93     AVBPrint buf;
94     char *ptr = avpkt->data;
95     char *end;
96     //char *ptr_temp;
97     int text_length, tsmb_type, style_entries;
98     uint64_t tsmb_size, tracksize;
99     char **style_start = { 0, };
100     char **style_end = { 0, };
101     uint8_t **style_flags = { 0, };
102     const uint8_t *tsmb;
103     int index, i, size_var;
104     uint8_t *flag;
105     char *style_pos;
106
107     if (!ptr || avpkt->size < 2)
108         return AVERROR_INVALIDDATA;
109
110     /*
111      * A packet of size two with value zero is an empty subtitle
112      * used to mark the end of the previous non-empty subtitle.
113      * We can just drop them here as we have duration information
114      * already. If the value is non-zero, then it's technically a
115      * bad packet.
116      */
117     if (avpkt->size == 2)
118         return AV_RB16(ptr) == 0 ? 0 : AVERROR_INVALIDDATA;
119
120     /*
121      * The first two bytes of the packet are the length of the text string
122      * In complex cases, there are style descriptors appended to the string
123      * so we can't just assume the packet size is the string size.
124      */
125     text_length = AV_RB16(ptr);
126     end = ptr + FFMIN(2 + text_length, avpkt->size);
127     ptr += 2;
128
129     ts_start = av_rescale_q(avpkt->pts,
130                             avctx->time_base,
131                             (AVRational){1,100});
132     ts_end   = av_rescale_q(avpkt->pts + avpkt->duration,
133                             avctx->time_base,
134                             (AVRational){1,100});
135
136     tsmb_size = 0;
137     tracksize = 2 + text_length;
138     // Note that the spec recommends lines be no longer than 2048 characters.
139     av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
140     if (text_length + 2 != avpkt->size) {
141         while (tracksize + 8 <= avpkt->size) {
142             // A box is a minimum of 8 bytes.
143             tsmb = ptr + tracksize - 2;
144             tsmb_size = AV_RB32(tsmb);
145             tsmb += 4;
146             tsmb_type = AV_RB32(tsmb);
147             tsmb += 4;
148
149             if (tsmb_size == 1) {
150                 if (tracksize + 16 > avpkt->size)
151                     break;
152                 tsmb_size = AV_RB64(tsmb);
153                 tsmb += 8;
154                 size_var = 18;
155             } else
156                 size_var = 10;
157             //size_var is equal to 10 or 18 depending on the size of box
158
159             if (tracksize + tsmb_size > avpkt->size)
160                 break;
161
162             if (tsmb_type == MKBETAG('s','t','y','l')) {
163                 if (tracksize + size_var > avpkt->size)
164                     break;
165                 style_entries = AV_RB16(tsmb);
166                 tsmb += 2;
167
168                 // A single style record is of length 12 bytes.
169                 if (tracksize + size_var + style_entries * 12 > avpkt->size)
170                     break;
171
172                 for(i = 0; i < style_entries; i++) {
173                     style_pos = ptr + AV_RB16(tsmb);
174                     index = i;
175                     av_dynarray_add(&style_start, &index, style_pos);
176                     tsmb += 2;
177                     style_pos = ptr + AV_RB16(tsmb);
178                     index = i;
179                     av_dynarray_add(&style_end, &index, style_pos);
180                     tsmb += 2;
181                     // fontID = AV_RB16(tsmb);
182                     tsmb += 2;
183                     flag = av_malloc(1);
184                     if (!flag)
185                         return AVERROR(ENOMEM);
186                     *flag = AV_RB8(tsmb);
187                     index = i;
188                     av_dynarray_add(&style_flags, &index, flag);
189                     //fontsize=AV_RB8(tsmb);
190                     tsmb += 2;
191                     // text-color-rgba
192                     tsmb += 4;
193                 }
194                 text_to_ass(&buf, ptr, end, style_start, style_end, style_flags, style_entries);
195
196                 for(i = 0; i < style_entries; i++) {
197                     av_freep(&style_flags[i]);
198                 }
199                 av_freep(&style_start);
200                 av_freep(&style_end);
201                 av_freep(&style_flags);
202             }
203             tracksize = tracksize + tsmb_size;
204         }
205     } else
206         text_to_ass(&buf, ptr, end, NULL, NULL, 0, 0);
207
208     ret = ff_ass_add_rect_bprint(sub, &buf, ts_start, ts_end - ts_start);
209     av_bprint_finalize(&buf, NULL);
210     if (ret < 0)
211         return ret;
212     *got_sub_ptr = sub->num_rects > 0;
213     return avpkt->size;
214 }
215
216 AVCodec ff_movtext_decoder = {
217     .name         = "mov_text",
218     .long_name    = NULL_IF_CONFIG_SMALL("3GPP Timed Text subtitle"),
219     .type         = AVMEDIA_TYPE_SUBTITLE,
220     .id           = AV_CODEC_ID_MOV_TEXT,
221     .init         = mov_text_init,
222     .decode       = mov_text_decode_frame,
223 };