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.
24 #include <libavutil/common.h>
32 #include "libmpdemux/demuxer.h"
36 #include "stream/stream.h"
38 static char **find_files(const char *original_file
, const char *suffix
)
40 void *tmpmem
= talloc_new(NULL
);
41 char *basename
= mp_basename(original_file
);
42 struct bstr directory
= mp_dirname(original_file
);
43 char **results
= talloc_size(NULL
, 0);
44 char *dir_zero
= bstrdup0(tmpmem
, directory
);
45 DIR *dp
= opendir(dir_zero
);
51 char ***names_by_matchlen
= talloc_zero_array(tmpmem
, char **,
52 strlen(basename
) + 1);
54 while ((ep
= readdir(dp
))) {
55 int suffix_offset
= strlen(ep
->d_name
) - strlen(suffix
);
56 // name must end with suffix
57 if (suffix_offset
< 0 || strcmp(ep
->d_name
+ suffix_offset
, suffix
))
59 // don't list the original name
60 if (!strcmp(ep
->d_name
, basename
))
63 char *name
= mp_path_join(results
, directory
, bstr(ep
->d_name
));
64 char *s1
= ep
->d_name
;
67 while (*s1
&& *s1
++ == *s2
++)
69 int oldcount
= MP_TALLOC_ELEMS(names_by_matchlen
[matchlen
]);
70 names_by_matchlen
[matchlen
] = talloc_realloc(names_by_matchlen
,
71 names_by_matchlen
[matchlen
],
72 char *, oldcount
+ 1);
73 names_by_matchlen
[matchlen
][oldcount
] = name
;
77 results
= talloc_realloc(NULL
, results
, char *, num_results
);
78 char **resptr
= results
;
79 for (int i
= strlen(basename
); i
>= 0; i
--) {
80 char **p
= names_by_matchlen
[i
];
81 for (int j
= 0; j
< talloc_get_size(p
) / sizeof(char *); j
++)
84 assert(resptr
== results
+ num_results
);
89 static struct demuxer
*open_demuxer(struct stream
*stream
,
90 struct MPContext
*mpctx
, char *filename
, unsigned char uid_map
[][16])
92 return demux_open_withparams(&mpctx
->opts
, stream
,
93 DEMUXER_TYPE_MATROSKA
, mpctx
->opts
.audio_id
,
94 mpctx
->opts
.video_id
, mpctx
->opts
.sub_id
, filename
,
95 &(struct demuxer_params
){.matroska_wanted_uids
= uid_map
});
98 static int enable_cache(struct MPContext
*mpctx
, struct stream
**stream
,
99 struct demuxer
**demuxer
, unsigned char uid_map
[][16])
101 struct MPOpts
*opts
= &mpctx
->opts
;
103 if (!(opts
->stream_cache_size
> 0 ||
104 opts
->stream_cache_size
< 0 && (*stream
)->cache_size
))
107 char *filename
= talloc_strdup(NULL
, (*demuxer
)->filename
);
108 free_demuxer(*demuxer
);
109 free_stream(*stream
);
112 *stream
= open_stream(filename
, &mpctx
->opts
, &format
);
114 talloc_free(filename
);
118 stream_enable_cache_percent(*stream
,
119 opts
->stream_cache_size
,
120 opts
->stream_cache_min_percent
,
121 opts
->stream_cache_seek_min_percent
);
123 *demuxer
= open_demuxer(*stream
, mpctx
, filename
, uid_map
);
125 talloc_free(filename
);
126 free_stream(*stream
);
130 talloc_free(filename
);
134 static int find_ordered_chapter_sources(struct MPContext
*mpctx
,
135 struct content_source
*sources
,
137 unsigned char uid_map
[][16])
139 int num_filenames
= 0;
140 char **filenames
= NULL
;
141 if (num_sources
> 1) {
142 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "This file references data from "
144 if (mpctx
->stream
->type
!= STREAMTYPE_FILE
) {
145 mp_msg(MSGT_CPLAYER
, MSGL_WARN
, "Playback source is not a "
146 "normal disk file. Will not search for related files.\n");
148 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Will scan other files in the "
149 "same directory to find referenced sources.\n");
150 filenames
= find_files(mpctx
->demuxer
->filename
, ".mkv");
151 num_filenames
= MP_TALLOC_ELEMS(filenames
);
155 int num_left
= num_sources
- 1;
156 for (int i
= 0; i
< num_filenames
&& num_left
> 0; i
++) {
157 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Checking file %s\n",
158 filename_recode(filenames
[i
]));
160 struct stream
*s
= open_stream(filenames
[i
], &mpctx
->opts
, &format
);
163 struct demuxer
*d
= open_demuxer(s
, mpctx
, filenames
[i
], uid_map
);
169 if (d
->file_format
== DEMUXER_TYPE_MATROSKA
) {
170 for (int i
= 1; i
< num_sources
; i
++) {
171 if (sources
[i
].demuxer
)
173 if (!memcmp(uid_map
[i
], d
->matroska_data
.segment_uid
, 16)) {
174 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,"Match for source %d: %s\n",
175 i
, filename_recode(d
->filename
));
177 if (enable_cache(mpctx
, &s
, &d
, uid_map
) < 0)
180 sources
[i
].stream
= s
;
181 sources
[i
].demuxer
= d
;
193 talloc_free(filenames
);
195 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "Failed to find ordered chapter part!\n"
196 "There will be parts MISSING from the video!\n");
197 for (int i
= 1, j
= 1; i
< num_sources
; i
++)
198 if (sources
[i
].demuxer
) {
199 sources
[j
] = sources
[i
];
200 memcpy(uid_map
[j
], uid_map
[i
], 16);
204 return num_sources
- num_left
;
207 void build_ordered_chapter_timeline(struct MPContext
*mpctx
)
209 struct MPOpts
*opts
= &mpctx
->opts
;
211 if (!opts
->ordered_chapters
) {
212 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, but "
213 "you have disabled support for them. Ignoring.\n");
217 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, will build "
220 struct demuxer
*demuxer
= mpctx
->demuxer
;
221 struct matroska_data
*m
= &demuxer
->matroska_data
;
223 // +1 because sources/uid_map[0] is original file even if all chapters
224 // actually use other sources and need separate entries
225 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
226 m
->num_ordered_chapters
+1);
227 sources
[0].stream
= mpctx
->stream
;
228 sources
[0].demuxer
= mpctx
->demuxer
;
229 unsigned char (*uid_map
)[16] = talloc_array_ptrtype(NULL
, uid_map
,
230 m
->num_ordered_chapters
+ 1);
232 memcpy(uid_map
[0], m
->segment_uid
, 16);
234 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
235 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
236 if (!c
->has_segment_uid
)
237 memcpy(c
->segment_uid
, m
->segment_uid
, 16);
239 for (int j
= 0; j
< num_sources
; j
++)
240 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
242 memcpy(uid_map
[num_sources
], c
->segment_uid
, 16);
243 sources
[num_sources
] = (struct content_source
){};
249 num_sources
= find_ordered_chapter_sources(mpctx
, sources
, num_sources
,
253 // +1 for terminating chapter with start time marking end of last real one
254 struct timeline_part
*timeline
= talloc_array_ptrtype(NULL
, timeline
,
255 m
->num_ordered_chapters
+ 1);
256 struct chapter
*chapters
= talloc_array_ptrtype(NULL
, chapters
,
257 m
->num_ordered_chapters
);
258 uint64_t starttime
= 0;
259 uint64_t missing_time
= 0;
261 int num_chapters
= 0;
262 uint64_t prev_part_offset
= 0;
263 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
264 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
267 for (j
= 0; j
< num_sources
; j
++) {
268 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
271 missing_time
+= c
->end
- c
->start
;
274 /* Only add a separate part if the time or file actually changes.
275 * Matroska files have chapter divisions that are redundant from
276 * timeline point of view because the same chapter structure is used
277 * both to specify the timeline and for normal chapter information.
278 * Removing a missing inserted external chapter can also cause this.
279 * We allow for a configurable fudge factor because of files which
280 * specify chapter end times that are one frame too early;
281 * we don't want to try seeking over a one frame gap. */
282 int64_t join_diff
= c
->start
- starttime
- prev_part_offset
;
284 || FFABS(join_diff
) > opts
->chapter_merge_threshold
* 1000000
285 || sources
+ j
!= timeline
[part_count
- 1].source
) {
286 timeline
[part_count
].source
= sources
+ j
;
287 timeline
[part_count
].start
= starttime
/ 1e9
;
288 timeline
[part_count
].source_start
= c
->start
/ 1e9
;
289 prev_part_offset
= c
->start
- starttime
;
291 } else if (part_count
> 0 && join_diff
) {
292 /* Chapter was merged at an inexact boundary;
293 * adjust timestamps to match. */
294 mp_msg(MSGT_CPLAYER
, MSGL_V
, "Merging timeline part %d with "
295 "offset %g ms.\n", i
, join_diff
/ 1e6
);
296 starttime
+= join_diff
;
298 chapters
[num_chapters
].start
= starttime
/ 1e9
;
299 chapters
[num_chapters
].name
= talloc_strdup(chapters
, c
->name
);
300 starttime
+= c
->end
- c
->start
;
303 timeline
[part_count
].start
= starttime
/ 1e9
;
304 talloc_free(uid_map
);
307 // None of the parts come from the file itself???
308 talloc_free(sources
);
309 talloc_free(timeline
);
310 talloc_free(chapters
);
315 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "There are %.3f seconds missing "
316 "from the timeline!\n", missing_time
/ 1e9
);
317 mpctx
->sources
= sources
;
318 mpctx
->num_sources
= num_sources
;
319 mpctx
->timeline
= timeline
;
320 mpctx
->num_timeline_parts
= part_count
;
321 mpctx
->num_chapters
= num_chapters
;
322 mpctx
->chapters
= chapters
;