FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ass.c
Go to the documentation of this file.
1 /*
2  * SSA/ASS common functions
3  * Copyright (c) 2010 Aurelien Jacobs <aurel@gnuage.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/avassert.h"
25 #include "libavutil/avstring.h"
26 #include "libavutil/bprint.h"
27 #include "libavutil/common.h"
28 
30  const char *font, int font_size,
31  int color, int back_color,
32  int bold, int italic, int underline,
33  int alignment)
34 {
36  "[Script Info]\r\n"
37  "; Script generated by FFmpeg/Lavc%s\r\n"
38  "ScriptType: v4.00+\r\n"
39  "PlayResX: 384\r\n"
40  "PlayResY: 288\r\n"
41  "\r\n"
42  "[V4+ Styles]\r\n"
43 
44  /* ASSv4 header */
45  "Format: Name, "
46  "Fontname, Fontsize, "
47  "PrimaryColour, SecondaryColour, OutlineColour, BackColour, "
48  "Bold, Italic, Underline, StrikeOut, "
49  "ScaleX, ScaleY, "
50  "Spacing, Angle, "
51  "BorderStyle, Outline, Shadow, "
52  "Alignment, MarginL, MarginR, MarginV, "
53  "Encoding\r\n"
54 
55  "Style: "
56  "Default," /* Name */
57  "%s,%d," /* Font{name,size} */
58  "&H%x,&H%x,&H%x,&H%x," /* {Primary,Secondary,Outline,Back}Colour */
59  "%d,%d,%d,0," /* Bold, Italic, Underline, StrikeOut */
60  "100,100," /* Scale{X,Y} */
61  "0,0," /* Spacing, Angle */
62  "1,1,0," /* BorderStyle, Outline, Shadow */
63  "%d,10,10,10," /* Alignment, Margin[LRV] */
64  "0\r\n" /* Encoding */
65 
66  "\r\n"
67  "[Events]\r\n"
68  "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\r\n",
70  font, font_size, color, color, back_color, back_color,
71  -bold, -italic, -underline, alignment);
72 
73  if (!avctx->subtitle_header)
74  return AVERROR(ENOMEM);
75  avctx->subtitle_header_size = strlen(avctx->subtitle_header);
76  return 0;
77 }
78 
80 {
89 }
90 
91 static void insert_ts(AVBPrint *buf, int ts)
92 {
93  if (ts == -1) {
94  av_bprintf(buf, "9:59:59.99,");
95  } else {
96  int h, m, s;
97 
98  h = ts/360000; ts -= 360000*h;
99  m = ts/ 6000; ts -= 6000*m;
100  s = ts/ 100; ts -= 100*s;
101  av_bprintf(buf, "%d:%02d:%02d.%02d,", h, m, s, ts);
102  }
103 }
104 
105 int ff_ass_bprint_dialog(AVBPrint *buf, const char *dialog,
106  int ts_start, int duration, int raw)
107 {
108  int dlen;
109 
110  if (!raw || raw == 2) {
111  long int layer = 0;
112 
113  if (raw == 2) {
114  /* skip ReadOrder */
115  dialog = strchr(dialog, ',');
116  if (!dialog)
117  return AVERROR_INVALIDDATA;
118  dialog++;
119 
120  /* extract Layer or Marked */
121  layer = strtol(dialog, (char**)&dialog, 10);
122  if (*dialog != ',')
123  return AVERROR_INVALIDDATA;
124  dialog++;
125  }
126  av_bprintf(buf, "Dialogue: %ld,", layer);
127  insert_ts(buf, ts_start);
128  insert_ts(buf, duration == -1 ? -1 : ts_start + duration);
129  if (raw != 2)
130  av_bprintf(buf, "Default,,0,0,0,,");
131  }
132 
133  dlen = strcspn(dialog, "\n");
134  dlen += dialog[dlen] == '\n';
135 
136  av_bprintf(buf, "%.*s", dlen, dialog);
137  if (raw == 2)
138  av_bprintf(buf, "\r\n");
139 
140  return dlen;
141 }
142 
143 int ff_ass_add_rect(AVSubtitle *sub, const char *dialog,
144  int ts_start, int duration, int raw)
145 {
146  AVBPrint buf;
147  int ret, dlen;
148  AVSubtitleRect **rects;
149 
151  if ((ret = ff_ass_bprint_dialog(&buf, dialog, ts_start, duration, raw)) < 0)
152  goto err;
153  dlen = ret;
154  if (!av_bprint_is_complete(&buf))
155  goto errnomem;
156 
157  rects = av_realloc(sub->rects, (sub->num_rects+1) * sizeof(*sub->rects));
158  if (!rects)
159  goto errnomem;
160  sub->rects = rects;
161  sub->end_display_time = FFMAX(sub->end_display_time, 10 * duration);
162  rects[sub->num_rects] = av_mallocz(sizeof(*rects[0]));
163  rects[sub->num_rects]->type = SUBTITLE_ASS;
164  ret = av_bprint_finalize(&buf, &rects[sub->num_rects]->ass);
165  if (ret < 0)
166  goto err;
167  sub->num_rects++;
168  return dlen;
169 
170 errnomem:
171  ret = AVERROR(ENOMEM);
172 err:
173  av_bprint_finalize(&buf, NULL);
174  return ret;
175 }
176 
178  int ts_start, int duration)
179 {
180  av_bprintf(buf, "\r\n");
181  if (!av_bprint_is_complete(buf))
182  return AVERROR(ENOMEM);
183  return ff_ass_add_rect(sub, buf->str, ts_start, duration, 0);
184 }
185 
186 void ff_ass_bprint_text_event(AVBPrint *buf, const char *p, int size,
187  const char *linebreaks, int keep_ass_markup)
188 {
189  const char *p_end = p + size;
190 
191  for (; p < p_end && *p; p++) {
192 
193  /* forced custom line breaks, not accounted as "normal" EOL */
194  if (linebreaks && strchr(linebreaks, *p)) {
195  av_bprintf(buf, "\\N");
196 
197  /* standard ASS escaping so random characters don't get mis-interpreted
198  * as ASS */
199  } else if (!keep_ass_markup && strchr("{}\\", *p)) {
200  av_bprintf(buf, "\\%c", *p);
201 
202  /* some packets might end abruptly (no \0 at the end, like for example
203  * in some cases of demuxing from a classic video container), some
204  * might be terminated with \n or \r\n which we have to remove (for
205  * consistency with those who haven't), and we also have to deal with
206  * evil cases such as \r at the end of the buffer (and no \0 terminated
207  * character) */
208  } else if (p[0] == '\n') {
209  /* some stuff left so we can insert a line break */
210  if (p < p_end - 1)
211  av_bprintf(buf, "\\N");
212  } else if (p[0] == '\r' && p < p_end - 1 && p[1] == '\n') {
213  /* \r followed by a \n, we can skip it. We don't insert the \N yet
214  * because we don't know if it is followed by more text */
215  continue;
216 
217  /* finally, a sane character */
218  } else {
219  av_bprint_chars(buf, *p, 1);
220  }
221  }
222 }