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"
55 static int find_edl_source(struct edl_source
*sources
, int num_sources
,
58 for (int i
= 0; i
< num_sources
; i
++)
59 if (!bstrcmp(sources
[i
].id
, name
))
64 void build_edl_timeline(struct MPContext
*mpctx
)
66 const struct bstr file_prefix
= BSTR("<");
67 void *tmpmem
= talloc_new(NULL
);
69 struct bstr
*lines
= bstr_splitlines(tmpmem
, mpctx
->demuxer
->file_contents
);
70 int linec
= MP_TALLOC_ELEMS(lines
);
71 struct bstr header
= BSTR("mplayer EDL file, version ");
72 if (!linec
|| !bstr_startswith(lines
[0], header
)) {
73 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Bad EDL header!\n");
76 struct bstr version
= bstr_strip(bstr_cut(lines
[0], header
.len
));
77 if (bstrcmp(BSTR("2"), version
)) {
78 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Unsupported EDL file version!\n");
83 for (int i
= 1; i
< linec
; i
++) {
84 if (bstr_startswith(lines
[i
], file_prefix
)) {
87 int comment
= bstrchr(lines
[i
], '#');
89 lines
[i
] = bstr_splice(lines
[i
], 0, comment
);
90 if (bstr_strip(lines
[i
]).len
)
95 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "No parts in timeline!\n");
99 // Parse source filename definitions
101 struct edl_source
*edl_ids
= talloc_array_ptrtype(tmpmem
, edl_ids
,
104 for (int i
= 1; i
< linec
; i
++) {
105 struct bstr line
= lines
[i
];
106 if (!bstr_startswith(line
, file_prefix
))
108 line
= bstr_cut(line
, file_prefix
.len
);
109 struct bstr id
= bstr_split(line
, WHITESPACE
, &line
);
110 if (find_edl_source(edl_ids
, num_sources
, id
) >= 0) {
111 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Repeated ID on line %d!\n",
115 if (!isalpha(*id
.start
)) {
116 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Invalid ID on line %d!\n",
120 char *filename
= mp_basename(bstrdup0(tmpmem
, bstr_strip(line
)));
121 if (!strlen(filename
)) {
122 mp_msg(MSGT_CPLAYER
, MSGL_ERR
,
123 "EDL: Invalid filename on line %d!\n", i
+1);
126 struct bstr dirname
= mp_dirname(mpctx
->demuxer
->filename
);
127 char *fullname
= mp_path_join(tmpmem
, dirname
, BSTR(filename
));
128 edl_ids
[num_sources
++] = (struct edl_source
){id
, fullname
, i
+1};
131 // Parse timeline part definitions
133 struct edl_part
*parts
= talloc_array_ptrtype(tmpmem
, parts
, num_parts
);
134 int total_parts
= num_parts
;
136 for (int i
= 1; i
< linec
; i
++) {
137 struct bstr line
= bstr_strip(lines
[i
]);
138 if (!line
.len
|| bstr_startswith(line
, file_prefix
))
140 parts
[num_parts
] = (struct edl_part
){{-1, -1}, {-1, -1}, 0, -1};
141 parts
[num_parts
].lineno
= i
+ 1;
142 for (int s
= 0; s
< 2; s
++) {
143 struct edl_time
*p
= !s
? &parts
[num_parts
].tl
:
144 &parts
[num_parts
].src
;
146 struct bstr t
= bstr_split(line
, WHITESPACE
, &line
);
148 if (!s
&& num_parts
< total_parts
- 1) {
149 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: missing source "
150 "identifier on line %d (not last)!\n", i
+1);
155 if (isalpha(*t
.start
)) {
158 parts
[num_parts
].id
= find_edl_source(edl_ids
, num_sources
,
160 if (parts
[num_parts
].id
< 0) {
161 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Undefined source "
162 "identifier on line %d!\n", i
+1);
169 struct bstr arg
= bstr_split(t
, "+-", &next
);
171 next
= bstr_split(line
, WHITESPACE
, &line
);
172 arg
= bstr_split(next
, "+-", &next
);
177 if (!bstrcmp(arg
, BSTR("*")))
179 else if (isdigit(*arg
.start
)) {
180 val
= bstrtoll(arg
, &arg
, 10) * 1000000000;
181 if (arg
.len
&& *arg
.start
== '.') {
182 int len
= arg
.len
- 1;
183 arg
= bstr_splice(arg
, 1, 10);
184 int64_t val2
= bstrtoll(arg
, &arg
, 10);
187 for (; len
< 9; len
++)
194 if (isdigit(c
) || c
== '*') {
196 p
->implied_start
= true;
199 } else if (c
== '-') {
201 p
->implied_end
= true;
204 } else if (c
== '+') {
208 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: zero duration "
209 "on line %d!\n", i
+1);
212 parts
[num_parts
].duration
= val
;
222 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Malformed line %d!\n", i
+1);
226 // Fill in implied start/stop/duration values
228 int64_t *times
= talloc_zero_array(tmpmem
, int64_t, num_sources
);
231 for (int i
= 0; i
< num_parts
; i
++) {
232 for (int s
= 0; s
< 2; s
++) {
233 struct edl_time
*p
= s
? &parts
[i
].tl
: &parts
[i
].src
;
234 if (!s
&& parts
[i
].id
== -1)
236 int64_t *t
= s
? &time
: times
+ parts
[i
].id
;
237 p
->implied_start
|= s
&& *t
>= 0;
238 if (p
->implied_start
&& p
->start
>= 0 && *t
>= 0
240 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Inconsistent line "
241 "%d!\n", parts
[i
].lineno
);
246 if (p
->implied_start
)
248 if (*t
>= 0 && parts
[i
].duration
)
249 *t
+= parts
[i
].duration
;
256 for (int i
= 0; i
< num_sources
; i
++)
259 for (int i
= num_parts
- 1; i
>= 0; i
--) {
260 for (int s
= 0; s
< 2; s
++) {
261 struct edl_time
*p
= s
? &parts
[i
].tl
: &parts
[i
].src
;
262 if (!s
&& parts
[i
].id
== -1)
264 int64_t *t
= s
? &time
: times
+ parts
[i
].id
;
265 p
->implied_end
|= s
&& *t
>= 0;
266 if (p
->implied_end
&& p
->end
>= 0 && *t
>=0 && p
->end
!= *t
) {
267 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Inconsistent line "
268 "%d!\n", parts
[i
].lineno
);
275 if (*t
>= 0 && parts
[i
].duration
) {
276 *t
-= parts
[i
].duration
;
278 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Negative time "
279 "on line %d!\n", parts
[i
].lineno
);
288 int missing_duration
= -1;
289 int missing_srcstart
= -1;
290 bool anything_done
= false;
291 for (int i
= 0; i
< num_parts
; i
++) {
292 int64_t duration
= parts
[i
].duration
;
293 if (parts
[i
].tl
.start
>= 0 && parts
[i
].tl
.end
>= 0) {
294 int64_t duration2
= parts
[i
].tl
.end
- parts
[i
].tl
.start
;
295 if (duration
&& duration
!= duration2
)
297 duration
= duration2
;
301 if (parts
[i
].src
.start
>= 0 && parts
[i
].src
.end
>= 0) {
302 int64_t duration2
= parts
[i
].src
.end
- parts
[i
].src
.start
;
303 if (duration
&& duration
!= duration2
) {
305 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Inconsistent line "
309 duration
= duration2
;
312 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: duration <= 0 on "
313 "line %d!\n", parts
[i
].lineno
);
317 if (parts
[i
].id
== -1)
320 missing_duration
= i
;
321 else if (!parts
[i
].duration
)
322 anything_done
= true;
323 parts
[i
].duration
= duration
;
324 if (duration
&& parts
[i
].src
.start
< 0)
325 if (parts
[i
].src
.end
< 0)
326 missing_srcstart
= i
;
328 parts
[i
].src
.start
= parts
[i
].src
.end
- duration
;
330 if (!anything_done
) {
331 if (missing_duration
>= 0) {
332 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Could not determine "
333 "duration for line %d!\n",
334 parts
[missing_duration
].lineno
);
337 if (missing_srcstart
>= 0) {
338 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: no source start time for "
339 "line %d!\n", parts
[missing_srcstart
].lineno
);
348 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
350 mpctx
->sources
= sources
;
351 sources
[0].stream
= mpctx
->stream
;
352 sources
[0].demuxer
= mpctx
->demuxer
;
353 mpctx
->num_sources
= 1;
355 for (int i
= 0; i
< num_sources
; i
++) {
357 struct stream
*s
= open_stream(edl_ids
[i
].filename
, &mpctx
->opts
,
361 struct demuxer
*d
= demux_open(&mpctx
->opts
, s
, format
,
362 mpctx
->opts
.audio_id
,
363 mpctx
->opts
.video_id
,
365 edl_ids
[i
].filename
);
369 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "EDL: Could not open source "
370 "file on line %d!\n", edl_ids
[i
].lineno
);
373 sources
[mpctx
->num_sources
].stream
= s
;
374 sources
[mpctx
->num_sources
].demuxer
= d
;
375 mpctx
->num_sources
++;
378 // Write final timeline structure
380 struct timeline_part
*timeline
= talloc_array_ptrtype(NULL
, timeline
,
382 int64_t starttime
= 0;
383 for (int i
= 0; i
< num_parts
; i
++) {
384 timeline
[i
].start
= starttime
/ 1e9
;
385 starttime
+= parts
[i
].duration
;
386 timeline
[i
].source_start
= parts
[i
].src
.start
/ 1e9
;
387 timeline
[i
].source
= sources
+ parts
[i
].id
+ 1;
389 if (parts
[num_parts
- 1].id
!= -1) {
390 timeline
[num_parts
].start
= starttime
/ 1e9
;
393 mpctx
->timeline
= timeline
;
394 mpctx
->num_timeline_parts
= num_parts
- 1;