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.
28 #include "libmpdemux/demuxer.h"
32 #include "stream/stream.h"
56 static int find_edl_source(struct edl_source
*sources
, int num_sources
,
59 for (int i
= 0; i
< num_sources
; i
++)
60 if (!bstrcmp(sources
[i
].id
, name
))
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");
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");
84 for (int i
= 1; i
< linec
; i
++) {
85 if (bstr_startswith(lines
[i
], file_prefix
)) {
88 int comment
= bstrchr(lines
[i
], '#');
90 lines
[i
] = bstr_splice(lines
[i
], 0, comment
);
91 if (bstr_strip(lines
[i
]).len
)
96 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "No parts in timeline!\n");
100 // Parse source filename definitions
102 struct edl_source
*edl_ids
= talloc_array_ptrtype(tmpmem
, edl_ids
,
105 for (int i
= 1; i
< linec
; i
++) {
106 struct bstr line
= lines
[i
];
107 if (!bstr_startswith(line
, file_prefix
))
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",
116 if (!isalpha(*id
.start
)) {
117 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Invalid ID on line %d!\n",
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);
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
;
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
))
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
;
147 struct bstr t
= bstr_split(line
, WHITESPACE
, &line
);
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);
156 if (isalpha(*t
.start
)) {
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);
170 struct bstr arg
= bstr_split(t
, "+-", &next
);
172 next
= bstr_split(line
, WHITESPACE
, &line
);
173 arg
= bstr_split(next
, "+-", &next
);
178 if (!bstrcmp(arg
, bstr("*")))
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);
188 for (; len
< 9; len
++)
195 if (isdigit(c
) || c
== '*') {
197 p
->implied_start
= true;
200 } else if (c
== '-') {
202 p
->implied_end
= true;
205 } else if (c
== '+') {
209 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: zero duration "
210 "on line %d!\n", i
+1);
213 parts
[num_parts
].duration
= val
;
223 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Malformed line %d!\n", i
+1);
227 // Fill in implied start/stop/duration values
229 int64_t *times
= talloc_zero_array(tmpmem
, int64_t, num_sources
);
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)
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
241 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Inconsistent line "
242 "%d!\n", parts
[i
].lineno
);
247 if (p
->implied_start
)
249 if (*t
>= 0 && parts
[i
].duration
)
250 *t
+= parts
[i
].duration
;
257 for (int i
= 0; i
< num_sources
; i
++)
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)
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
);
276 if (*t
>= 0 && parts
[i
].duration
) {
277 *t
-= parts
[i
].duration
;
279 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Negative time "
280 "on line %d!\n", parts
[i
].lineno
);
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
)
298 duration
= duration2
;
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
) {
306 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Inconsistent line "
310 duration
= duration2
;
313 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: duration <= 0 on "
314 "line %d!\n", parts
[i
].lineno
);
318 if (parts
[i
].id
== -1)
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
;
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
);
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
);
349 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
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
++) {
358 struct stream
*s
= open_stream(edl_ids
[i
].filename
, &mpctx
->opts
,
362 struct demuxer
*d
= demux_open(&mpctx
->opts
, s
, format
,
363 mpctx
->opts
.audio_id
,
364 mpctx
->opts
.video_id
,
366 edl_ids
[i
].filename
);
370 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Could not open source "
371 "file on line %d!\n", edl_ids
[i
].lineno
);
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
,
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
;
394 mpctx
->timeline
= timeline
;
395 mpctx
->num_timeline_parts
= num_parts
- 1;