avcodec/x86/h264_idct: Fix ff_h264_luma_dc_dequant_idct_sse2 checkasm failures
[ffmpeg.git] / libavformat / assenc.c
1 /*
2 * SSA/ASS muxer
3 * Copyright (c) 2008 Michael Niedermayer
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 "libavutil/avstring.h"
23 #include "libavutil/mem.h"
24 #include "avformat.h"
25 #include "avio_internal.h"
26 #include "internal.h"
27 #include "mux.h"
28
29 #include "libavutil/opt.h"
30
31 typedef struct DialogueLine {
32 int readorder;
33 char *line;
34 struct DialogueLine *prev, *next;
35 } DialogueLine;
36
37 typedef struct ASSContext {
38 const AVClass *class;
39 int expected_readorder;
40 DialogueLine *dialogue_cache;
41 DialogueLine *last_added_dialogue;
42 int cache_size;
43 int ssa_mode;
44 int ignore_readorder;
45 uint8_t *trailer;
46 size_t trailer_size;
47 } ASSContext;
48
49 static int write_header(AVFormatContext *s)
50 {
51 ASSContext *ass = s->priv_data;
52 AVCodecParameters *par = s->streams[0]->codecpar;
53
54 avpriv_set_pts_info(s->streams[0], 64, 1, 100);
55 if (par->extradata_size > 0) {
56 size_t header_size = par->extradata_size;
57 uint8_t *trailer = strstr(par->extradata, "\n[Events]");
58
59 if (trailer)
60 trailer = strstr(trailer, "Format:");
61 if (trailer)
62 trailer = strstr(trailer, "\n");
63
64 if (trailer) {
65 header_size = (++trailer - par->extradata);
66 ass->trailer_size = par->extradata_size - header_size;
67 if (ass->trailer_size)
68 ass->trailer = trailer;
69 }
70
71 ffio_write_lines(s->pb, par->extradata, header_size, NULL);
72
73 ass->ssa_mode = !strstr(par->extradata, "\n[V4+ Styles]");
74 if (!strstr(par->extradata, "\n[Events]"))
75 avio_printf(s->pb, "[Events]\nFormat: %s, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n",
76 ass->ssa_mode ? "Marked" : "Layer");
77 }
78
79 return 0;
80 }
81
82 static void purge_dialogues(AVFormatContext *s, int force)
83 {
84 int n = 0;
85 ASSContext *ass = s->priv_data;
86 DialogueLine *dialogue = ass->dialogue_cache;
87
88 while (dialogue && (dialogue->readorder == ass->expected_readorder || force)) {
89 DialogueLine *next = dialogue->next;
90 if (dialogue->readorder != ass->expected_readorder) {
91 av_log(s, AV_LOG_WARNING, "ReadOrder gap found between %d and %d\n",
92 ass->expected_readorder, dialogue->readorder);
93 ass->expected_readorder = dialogue->readorder;
94 }
95 avio_print(s->pb, "Dialogue: ", dialogue->line, "\n");
96 if (dialogue == ass->last_added_dialogue)
97 ass->last_added_dialogue = next;
98 av_freep(&dialogue->line);
99 av_free(dialogue);
100 if (next)
101 next->prev = NULL;
102 dialogue = ass->dialogue_cache = next;
103 ass->expected_readorder++;
104 n++;
105 }
106 ass->cache_size -= n;
107 if (n > 1)
108 av_log(s, AV_LOG_DEBUG, "wrote %d ASS lines, cached dialogues: %d, waiting for event id %d\n",
109 n, ass->cache_size, ass->expected_readorder);
110 }
111
112 static void insert_dialogue(ASSContext *ass, DialogueLine *dialogue)
113 {
114 DialogueLine *cur, *next = NULL, *prev = NULL;
115
116 /* from the last added to the end of the list */
117 if (ass->last_added_dialogue) {
118 for (cur = ass->last_added_dialogue; cur; cur = cur->next) {
119 if (cur->readorder > dialogue->readorder)
120 break;
121 prev = cur;
122 next = cur->next;
123 }
124 }
125
126 /* from the beginning to the last one added */
127 if (!prev) {
128 next = ass->dialogue_cache;
129 for (cur = next; cur != ass->last_added_dialogue; cur = cur->next) {
130 if (cur->readorder > dialogue->readorder)
131 break;
132 prev = cur;
133 next = cur->next;
134 }
135 }
136
137 if (prev) {
138 prev->next = dialogue;
139 dialogue->prev = prev;
140 } else {
141 dialogue->prev = ass->dialogue_cache;
142 ass->dialogue_cache = dialogue;
143 }
144 if (next) {
145 next->prev = dialogue;
146 dialogue->next = next;
147 }
148 ass->cache_size++;
149 ass->last_added_dialogue = dialogue;
150 }
151
152 static int write_packet(AVFormatContext *s, AVPacket *pkt)
153 {
154 ASSContext *ass = s->priv_data;
155
156 long int layer;
157 int text_len;
158 char *p = pkt->data;
159 int64_t start = pkt->pts;
160 int64_t end = start + pkt->duration;
161 int hh1, mm1, ss1, ms1;
162 int hh2, mm2, ss2, ms2;
163 DialogueLine *dialogue = av_mallocz(sizeof(*dialogue));
164
165 if (!dialogue)
166 return AVERROR(ENOMEM);
167
168 dialogue->readorder = strtol(p, &p, 10);
169 if (dialogue->readorder < ass->expected_readorder)
170 av_log(s, AV_LOG_WARNING, "Unexpected ReadOrder %d\n",
171 dialogue->readorder);
172 if (*p == ',')
173 p++;
174
175 if (ass->ssa_mode && !strncmp(p, "Marked=", 7))
176 p += 7;
177
178 layer = strtol(p, &p, 10);
179 if (*p == ',')
180 p++;
181 hh1 = (int)(start / 360000); mm1 = (int)(start / 6000) % 60;
182 hh2 = (int)(end / 360000); mm2 = (int)(end / 6000) % 60;
183 ss1 = (int)(start / 100) % 60; ms1 = (int)(start % 100);
184 ss2 = (int)(end / 100) % 60; ms2 = (int)(end % 100);
185 if (hh1 > 9) hh1 = 9, mm1 = 59, ss1 = 59, ms1 = 99;
186 if (hh2 > 9) hh2 = 9, mm2 = 59, ss2 = 59, ms2 = 99;
187
188 text_len = strlen(p);
189 while (text_len > 0 && p[text_len - 1] == '\r' || p[text_len - 1] == '\n')
190 text_len--;
191
192 dialogue->line = av_asprintf("%s%ld,%d:%02d:%02d.%02d,%d:%02d:%02d.%02d,%.*s",
193 ass->ssa_mode ? "Marked=" : "",
194 layer, hh1, mm1, ss1, ms1, hh2, mm2, ss2, ms2, text_len, p);
195 if (!dialogue->line) {
196 av_free(dialogue);
197 return AVERROR(ENOMEM);
198 }
199 insert_dialogue(ass, dialogue);
200 purge_dialogues(s, ass->ignore_readorder);
201
202 return 0;
203 }
204
205 static int write_trailer(AVFormatContext *s)
206 {
207 ASSContext *ass = s->priv_data;
208
209 purge_dialogues(s, 1);
210
211 if (ass->trailer) {
212 ffio_write_lines(s->pb, ass->trailer, ass->trailer_size, NULL);
213 }
214
215 return 0;
216 }
217
218 #define OFFSET(x) offsetof(ASSContext, x)
219 #define E AV_OPT_FLAG_ENCODING_PARAM
220 static const AVOption options[] = {
221 { "ignore_readorder", "write events immediately, even if they're out-of-order", OFFSET(ignore_readorder), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E },
222 { NULL },
223 };
224
225 static const AVClass ass_class = {
226 .class_name = "ass muxer",
227 .item_name = av_default_item_name,
228 .option = options,
229 .version = LIBAVUTIL_VERSION_INT,
230 };
231
232 const FFOutputFormat ff_ass_muxer = {
233 .p.name = "ass",
234 .p.long_name = NULL_IF_CONFIG_SMALL("SSA (SubStation Alpha) subtitle"),
235 .p.mime_type = "text/x-ass",
236 .p.extensions = "ass,ssa",
237 .p.audio_codec = AV_CODEC_ID_NONE,
238 .p.video_codec = AV_CODEC_ID_NONE,
239 .p.subtitle_codec = AV_CODEC_ID_ASS,
240 .p.flags = AVFMT_GLOBALHEADER | AVFMT_NOTIMESTAMPS | AVFMT_TS_NONSTRICT,
241 .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH |
242 FF_OFMT_FLAG_ONLY_DEFAULT_CODECS,
243 .p.priv_class = &ass_class,
244 .priv_data_size = sizeof(ASSContext),
245 .write_header = write_header,
246 .write_packet = write_packet,
247 .write_trailer = write_trailer,
248 };