ao_pulse: support native mute control
[mplayer.git] / timeline / tl_edl.c
blob8bd5c18f3e2bbe76fe9745a835ff90ebd1bb8570
1 /*
2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include <stdlib.h>
20 #include <stdbool.h>
21 #include <inttypes.h>
22 #include <ctype.h>
24 #include "talloc.h"
26 #include "mp_core.h"
27 #include "mp_msg.h"
28 #include "libmpdemux/demuxer.h"
29 #include "path.h"
30 #include "bstr.h"
31 #include "mpcommon.h"
32 #include "stream/stream.h"
35 struct edl_source {
36 struct bstr id;
37 char *filename;
38 int lineno;
41 struct edl_time {
42 int64_t start;
43 int64_t end;
44 bool implied_start;
45 bool implied_end;
48 struct edl_part {
49 struct edl_time tl;
50 struct edl_time src;
51 int64_t duration;
52 int id;
53 int lineno;
56 static int find_edl_source(struct edl_source *sources, int num_sources,
57 struct bstr name)
59 for (int i = 0; i < num_sources; i++)
60 if (!bstrcmp(sources[i].id, name))
61 return i;
62 return -1;
65 void build_edl_timeline(struct MPContext *mpctx)
67 const struct bstr file_prefix = bstr("<");
68 void *tmpmem = talloc_new(NULL);
70 struct bstr *lines = bstr_splitlines(tmpmem, mpctx->demuxer->file_contents);
71 int linec = MP_TALLOC_ELEMS(lines);
72 struct bstr header = bstr("mplayer EDL file, version ");
73 if (!linec || !bstr_startswith(lines[0], header)) {
74 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Bad EDL header!\n");
75 goto out;
77 struct bstr version = bstr_strip(bstr_cut(lines[0], header.len));
78 if (bstrcmp(bstr("2"), version)) {
79 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Unsupported EDL file version!\n");
80 goto out;
82 int num_sources = 0;
83 int num_parts = 0;
84 for (int i = 1; i < linec; i++) {
85 if (bstr_startswith(lines[i], file_prefix)) {
86 num_sources++;
87 } else {
88 int comment = bstrchr(lines[i], '#');
89 if (comment >= 0)
90 lines[i] = bstr_splice(lines[i], 0, comment);
91 if (bstr_strip(lines[i]).len)
92 num_parts++;
95 if (!num_parts) {
96 mp_msg(MSGT_CPLAYER, MSGL_ERR, "No parts in timeline!\n");
97 goto out;
100 // Parse source filename definitions
102 struct edl_source *edl_ids = talloc_array_ptrtype(tmpmem, edl_ids,
103 num_sources);
104 num_sources = 0;
105 for (int i = 1; i < linec; i++) {
106 struct bstr line = lines[i];
107 if (!bstr_startswith(line, file_prefix))
108 continue;
109 line = bstr_cut(line, file_prefix.len);
110 struct bstr id = bstr_split(line, WHITESPACE, &line);
111 if (find_edl_source(edl_ids, num_sources, id) >= 0) {
112 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Repeated ID on line %d!\n",
113 i+1);
114 goto out;
116 if (!isalpha(*id.start)) {
117 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Invalid ID on line %d!\n",
118 i+1);
119 goto out;
121 char *filename = mp_basename(bstrdup0(tmpmem, bstr_strip(line)));
122 if (!strlen(filename)) {
123 mp_msg(MSGT_CPLAYER, MSGL_ERR,
124 "EDL: Invalid filename on line %d!\n", i+1);
125 goto out;
127 struct bstr dirname = mp_dirname(mpctx->demuxer->filename);
128 char *fullname = mp_path_join(tmpmem, dirname, bstr(filename));
129 edl_ids[num_sources++] = (struct edl_source){id, fullname, i+1};
132 // Parse timeline part definitions
134 struct edl_part *parts = talloc_array_ptrtype(tmpmem, parts, num_parts);
135 int total_parts = num_parts;
136 num_parts = 0;
137 for (int i = 1; i < linec; i++) {
138 struct bstr line = bstr_strip(lines[i]);
139 if (!line.len || bstr_startswith(line, file_prefix))
140 continue;
141 parts[num_parts] = (struct edl_part){{-1, -1}, {-1, -1}, 0, -1};
142 parts[num_parts].lineno = i + 1;
143 for (int s = 0; s < 2; s++) {
144 struct edl_time *p = !s ? &parts[num_parts].tl :
145 &parts[num_parts].src;
146 while (1) {
147 struct bstr t = bstr_split(line, WHITESPACE, &line);
148 if (!t.len) {
149 if (!s && num_parts < total_parts - 1) {
150 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: missing source "
151 "identifier on line %d (not last)!\n", i+1);
152 goto out;
154 break;
156 if (isalpha(*t.start)) {
157 if (s)
158 goto bad;
159 parts[num_parts].id = find_edl_source(edl_ids, num_sources,
161 if (parts[num_parts].id < 0) {
162 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Undefined source "
163 "identifier on line %d!\n", i+1);
164 goto out;
166 break;
168 while (t.len) {
169 struct bstr next;
170 struct bstr arg = bstr_split(t, "+-", &next);
171 if (!arg.len) {
172 next = bstr_split(line, WHITESPACE, &line);
173 arg = bstr_split(next, "+-", &next);
175 if (!arg.len)
176 goto bad;
177 int64_t val;
178 if (!bstrcmp(arg, bstr("*")))
179 val = -1;
180 else if (isdigit(*arg.start)) {
181 val = bstrtoll(arg, &arg, 10) * 1000000000;
182 if (arg.len && *arg.start == '.') {
183 int len = arg.len - 1;
184 arg = bstr_splice(arg, 1, 10);
185 int64_t val2 = bstrtoll(arg, &arg, 10);
186 if (arg.len)
187 goto bad;
188 for (; len < 9; len++)
189 val2 *= 10;
190 val += val2;
192 } else
193 goto bad;
194 int c = *t.start;
195 if (isdigit(c) || c == '*') {
196 if (val < 0)
197 p->implied_start = true;
198 else
199 p->start = val;
200 } else if (c == '-') {
201 if (val < 0)
202 p->implied_end = true;
203 else
204 p->end = val;
205 } else if (c == '+') {
206 if (val < 0)
207 goto bad;
208 if (val == 0) {
209 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: zero duration "
210 "on line %d!\n", i+1);
211 goto out;
213 parts[num_parts].duration = val;
214 } else
215 goto bad;
216 t = next;
220 num_parts++;
221 continue;
222 bad:
223 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Malformed line %d!\n", i+1);
224 goto out;
227 // Fill in implied start/stop/duration values
229 int64_t *times = talloc_zero_array(tmpmem, int64_t, num_sources);
230 while (1) {
231 int64_t time = 0;
232 for (int i = 0; i < num_parts; i++) {
233 for (int s = 0; s < 2; s++) {
234 struct edl_time *p = s ? &parts[i].tl : &parts[i].src;
235 if (!s && parts[i].id == -1)
236 continue;
237 int64_t *t = s ? &time : times + parts[i].id;
238 p->implied_start |= s && *t >= 0;
239 if (p->implied_start && p->start >= 0 && *t >= 0
240 && p->start != *t) {
241 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Inconsistent line "
242 "%d!\n", parts[i].lineno);
243 goto out;
245 if (p->start >= 0)
246 *t = p->start;
247 if (p->implied_start)
248 p->start = *t;
249 if (*t >= 0 && parts[i].duration)
250 *t += parts[i].duration;
251 else
252 *t = -1;
253 if (p->end >= 0)
254 *t = p->end;
257 for (int i = 0; i < num_sources; i++)
258 times[i] = -1;
259 time = -1;
260 for (int i = num_parts - 1; i >= 0; i--) {
261 for (int s = 0; s < 2; s++) {
262 struct edl_time *p = s ? &parts[i].tl : &parts[i].src;
263 if (!s && parts[i].id == -1)
264 continue;
265 int64_t *t = s ? &time : times + parts[i].id;
266 p->implied_end |= s && *t >= 0;
267 if (p->implied_end && p->end >= 0 && *t >=0 && p->end != *t) {
268 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Inconsistent line "
269 "%d!\n", parts[i].lineno);
270 goto out;
272 if (p->end >= 0)
273 *t = p->end;
274 if (p->implied_end)
275 p->end = *t;
276 if (*t >= 0 && parts[i].duration) {
277 *t -= parts[i].duration;
278 if (t < 0) {
279 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Negative time "
280 "on line %d!\n", parts[i].lineno);
281 goto out;
283 } else
284 *t = -1;
285 if (p->start >= 0)
286 *t = p->start;
289 int missing_duration = -1;
290 int missing_srcstart = -1;
291 bool anything_done = false;
292 for (int i = 0; i < num_parts; i++) {
293 int64_t duration = parts[i].duration;
294 if (parts[i].tl.start >= 0 && parts[i].tl.end >= 0) {
295 int64_t duration2 = parts[i].tl.end - parts[i].tl.start;
296 if (duration && duration != duration2)
297 goto incons;
298 duration = duration2;
299 if (duration <= 0)
300 goto neg;
302 if (parts[i].src.start >= 0 && parts[i].src.end >= 0) {
303 int64_t duration2 = parts[i].src.end - parts[i].src.start;
304 if (duration && duration != duration2) {
305 incons:
306 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Inconsistent line "
307 "%d!\n", i+1);
308 goto out;
310 duration = duration2;
311 if (duration <= 0) {
312 neg:
313 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: duration <= 0 on "
314 "line %d!\n", parts[i].lineno);
315 goto out;
318 if (parts[i].id == -1)
319 continue;
320 if (!duration)
321 missing_duration = i;
322 else if (!parts[i].duration)
323 anything_done = true;
324 parts[i].duration = duration;
325 if (duration && parts[i].src.start < 0)
326 if (parts[i].src.end < 0)
327 missing_srcstart = i;
328 else
329 parts[i].src.start = parts[i].src.end - duration;
331 if (!anything_done) {
332 if (missing_duration >= 0) {
333 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Could not determine "
334 "duration for line %d!\n",
335 parts[missing_duration].lineno);
336 goto out;
338 if (missing_srcstart >= 0) {
339 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: no source start time for "
340 "line %d!\n", parts[missing_srcstart].lineno);
341 goto out;
343 break;
347 // Open source files
349 struct content_source *sources = talloc_array_ptrtype(NULL, sources,
350 num_sources + 1);
351 mpctx->sources = sources;
352 sources[0].stream = mpctx->stream;
353 sources[0].demuxer = mpctx->demuxer;
354 mpctx->num_sources = 1;
356 for (int i = 0; i < num_sources; i++) {
357 int format = 0;
358 struct stream *s = open_stream(edl_ids[i].filename, &mpctx->opts,
359 &format);
360 if (!s)
361 goto openfail;
362 struct demuxer *d = demux_open(&mpctx->opts, s, format,
363 mpctx->opts.audio_id,
364 mpctx->opts.video_id,
365 mpctx->opts.sub_id,
366 edl_ids[i].filename);
367 if (!d) {
368 free_stream(s);
369 openfail:
370 mp_msg(MSGT_CPLAYER, MSGL_ERR, "EDL: Could not open source "
371 "file on line %d!\n", edl_ids[i].lineno);
372 goto out;
374 sources[mpctx->num_sources].stream = s;
375 sources[mpctx->num_sources].demuxer = d;
376 mpctx->num_sources++;
379 // Write final timeline structure
381 struct timeline_part *timeline = talloc_array_ptrtype(NULL, timeline,
382 num_parts + 1);
383 int64_t starttime = 0;
384 for (int i = 0; i < num_parts; i++) {
385 timeline[i].start = starttime / 1e9;
386 starttime += parts[i].duration;
387 timeline[i].source_start = parts[i].src.start / 1e9;
388 timeline[i].source = sources + parts[i].id + 1;
390 if (parts[num_parts - 1].id != -1) {
391 timeline[num_parts].start = starttime / 1e9;
392 num_parts++;
394 mpctx->timeline = timeline;
395 mpctx->num_timeline_parts = num_parts - 1;
397 out:
398 talloc_free(tmpmem);