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>
30 #include "libmpdemux/demuxer.h"
34 #include "stream/stream.h"
37 static char **find_files(const char *original_file
, const char *suffix
)
39 void *tmpmem
= talloc_new(NULL
);
40 char *basename
= mp_basename(original_file
);
41 struct bstr directory
= mp_dirname(original_file
);
42 char **results
= talloc_size(NULL
, 0);
43 char *dir_zero
= bstrdup0(tmpmem
, directory
);
44 DIR *dp
= opendir(dir_zero
);
50 char ***names_by_matchlen
= talloc_zero_array(tmpmem
, char **,
51 strlen(basename
) + 1);
53 while ((ep
= readdir(dp
))) {
54 int suffix_offset
= strlen(ep
->d_name
) - strlen(suffix
);
55 // name must end with suffix
56 if (suffix_offset
< 0 || strcmp(ep
->d_name
+ suffix_offset
, suffix
))
58 // don't list the original name
59 if (!strcmp(ep
->d_name
, basename
))
62 char *name
= mp_path_join(results
, directory
, bstr(ep
->d_name
));
63 char *s1
= ep
->d_name
;
66 while (*s1
&& *s1
++ == *s2
++)
68 int oldcount
= MP_TALLOC_ELEMS(names_by_matchlen
[matchlen
]);
69 names_by_matchlen
[matchlen
] = talloc_realloc(names_by_matchlen
,
70 names_by_matchlen
[matchlen
],
71 char *, oldcount
+ 1);
72 names_by_matchlen
[matchlen
][oldcount
] = name
;
76 results
= talloc_realloc(NULL
, results
, char *, num_results
);
77 char **resptr
= results
;
78 for (int i
= strlen(basename
); i
>= 0; i
--) {
79 char **p
= names_by_matchlen
[i
];
80 for (int j
= 0; j
< talloc_get_size(p
) / sizeof(char *); j
++)
83 assert(resptr
== results
+ num_results
);
88 static int find_ordered_chapter_sources(struct MPContext
*mpctx
,
89 struct content_source
*sources
,
91 unsigned char uid_map
[][16])
93 int num_filenames
= 0;
94 char **filenames
= NULL
;
95 if (num_sources
> 1) {
96 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "This file references data from "
98 if (mpctx
->stream
->type
!= STREAMTYPE_FILE
) {
99 mp_msg(MSGT_CPLAYER
, MSGL_WARN
, "Playback source is not a "
100 "normal disk file. Will not search for related files.\n");
102 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Will scan other files in the "
103 "same directory to find referenced sources.\n");
104 filenames
= find_files(mpctx
->demuxer
->filename
, ".mkv");
105 num_filenames
= MP_TALLOC_ELEMS(filenames
);
109 int num_left
= num_sources
- 1;
110 for (int i
= 0; i
< num_filenames
&& num_left
> 0; i
++) {
111 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Checking file %s\n",
112 filename_recode(filenames
[i
]));
114 struct stream
*s
= open_stream(filenames
[i
], &mpctx
->opts
, &format
);
117 struct demuxer
*d
= demux_open_withparams(&mpctx
->opts
, s
,
118 DEMUXER_TYPE_MATROSKA
, mpctx
->opts
.audio_id
,
119 mpctx
->opts
.video_id
, mpctx
->opts
.sub_id
, filenames
[i
],
120 &(struct demuxer_params
){.matroska_wanted_uids
= uid_map
});
127 if (d
->file_format
== DEMUXER_TYPE_MATROSKA
) {
128 for (int i
= 1; i
< num_sources
; i
++) {
129 if (sources
[i
].demuxer
)
131 if (!memcmp(uid_map
[i
], d
->matroska_data
.segment_uid
, 16)) {
132 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,"Match for source %d: %s\n",
133 i
, filename_recode(d
->filename
));
134 sources
[i
].stream
= s
;
135 sources
[i
].demuxer
= d
;
147 talloc_free(filenames
);
149 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "Failed to find ordered chapter part!\n"
150 "There will be parts MISSING from the video!\n");
151 for (int i
= 1, j
= 1; i
< num_sources
; i
++)
152 if (sources
[i
].demuxer
) {
153 sources
[j
] = sources
[i
];
154 memcpy(uid_map
[j
], uid_map
[i
], 16);
158 return num_sources
- num_left
;
161 void build_ordered_chapter_timeline(struct MPContext
*mpctx
)
163 struct MPOpts
*opts
= &mpctx
->opts
;
165 if (!opts
->ordered_chapters
) {
166 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, but "
167 "you have disabled support for them. Ignoring.\n");
171 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, will build "
174 struct demuxer
*demuxer
= mpctx
->demuxer
;
175 struct matroska_data
*m
= &demuxer
->matroska_data
;
177 // +1 because sources/uid_map[0] is original file even if all chapters
178 // actually use other sources and need separate entries
179 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
180 m
->num_ordered_chapters
+1);
181 sources
[0].stream
= mpctx
->stream
;
182 sources
[0].demuxer
= mpctx
->demuxer
;
183 unsigned char (*uid_map
)[16] = talloc_array_ptrtype(NULL
, uid_map
,
184 m
->num_ordered_chapters
+ 1);
186 memcpy(uid_map
[0], m
->segment_uid
, 16);
188 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
189 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
190 if (!c
->has_segment_uid
)
191 memcpy(c
->segment_uid
, m
->segment_uid
, 16);
193 for (int j
= 0; j
< num_sources
; j
++)
194 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
196 memcpy(uid_map
[num_sources
], c
->segment_uid
, 16);
197 sources
[num_sources
] = (struct content_source
){};
203 num_sources
= find_ordered_chapter_sources(mpctx
, sources
, num_sources
,
207 // +1 for terminating chapter with start time marking end of last real one
208 struct timeline_part
*timeline
= talloc_array_ptrtype(NULL
, timeline
,
209 m
->num_ordered_chapters
+ 1);
210 struct chapter
*chapters
= talloc_array_ptrtype(NULL
, chapters
,
211 m
->num_ordered_chapters
);
212 uint64_t starttime
= 0;
213 uint64_t missing_time
= 0;
215 int num_chapters
= 0;
216 uint64_t prev_part_offset
= 0;
217 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
218 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
221 for (j
= 0; j
< num_sources
; j
++) {
222 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
225 missing_time
+= c
->end
- c
->start
;
228 /* Only add a separate part if the time or file actually changes.
229 * Matroska files have chapter divisions that are redundant from
230 * timeline point of view because the same chapter structure is used
231 * both to specify the timeline and for normal chapter information.
232 * Removing a missing inserted external chapter can also cause this.
233 * We allow for a configurable fudge factor because of files which
234 * specify chapter end times that are one frame too early;
235 * we don't want to try seeking over a one frame gap. */
236 int64_t join_diff
= c
->start
- starttime
- prev_part_offset
;
238 || FFABS(join_diff
) > opts
->chapter_merge_threshold
* 1000000
239 || sources
+ j
!= timeline
[part_count
- 1].source
) {
240 timeline
[part_count
].source
= sources
+ j
;
241 timeline
[part_count
].start
= starttime
/ 1e9
;
242 timeline
[part_count
].source_start
= c
->start
/ 1e9
;
243 prev_part_offset
= c
->start
- starttime
;
245 } else if (part_count
> 0 && join_diff
) {
246 /* Chapter was merged at an inexact boundary;
247 * adjust timestamps to match. */
248 mp_msg(MSGT_CPLAYER
, MSGL_V
, "Merging timeline part %d with "
249 "offset %g ms.\n", i
, join_diff
/ 1e6
);
250 starttime
+= join_diff
;
252 chapters
[num_chapters
].start
= starttime
/ 1e9
;
253 chapters
[num_chapters
].name
= talloc_strdup(chapters
, c
->name
);
254 starttime
+= c
->end
- c
->start
;
257 timeline
[part_count
].start
= starttime
/ 1e9
;
258 talloc_free(uid_map
);
261 // None of the parts come from the file itself???
262 talloc_free(sources
);
263 talloc_free(timeline
);
264 talloc_free(chapters
);
269 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "There are %.3f seconds missing "
270 "from the timeline!\n", missing_time
/ 1e9
);
271 mpctx
->sources
= sources
;
272 mpctx
->num_sources
= num_sources
;
273 mpctx
->timeline
= timeline
;
274 mpctx
->num_timeline_parts
= part_count
;
275 mpctx
->num_chapters
= num_chapters
;
276 mpctx
->chapters
= chapters
;