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"
39 static char **find_files(const char *original_file
, const char *suffix
)
41 void *tmpmem
= talloc_new(NULL
);
42 char *basename
= mp_basename(original_file
);
43 struct bstr directory
= mp_dirname(original_file
);
44 char **results
= talloc_size(NULL
, 0);
45 char *dir_zero
= bstrdup0(tmpmem
, directory
);
46 DIR *dp
= opendir(dir_zero
);
52 char ***names_by_matchlen
= talloc_zero_array(tmpmem
, char **,
53 strlen(basename
) + 1);
55 while ((ep
= readdir(dp
))) {
56 int suffix_offset
= strlen(ep
->d_name
) - strlen(suffix
);
57 // name must end with suffix
58 if (suffix_offset
< 0 || strcmp(ep
->d_name
+ suffix_offset
, suffix
))
60 // don't list the original name
61 if (!strcmp(ep
->d_name
, basename
))
64 char *name
= mp_path_join(results
, directory
, bstr(ep
->d_name
));
65 char *s1
= ep
->d_name
;
68 while (*s1
&& *s1
++ == *s2
++)
70 int oldcount
= MP_TALLOC_ELEMS(names_by_matchlen
[matchlen
]);
71 names_by_matchlen
[matchlen
] = talloc_realloc(names_by_matchlen
,
72 names_by_matchlen
[matchlen
],
73 char *, oldcount
+ 1);
74 names_by_matchlen
[matchlen
][oldcount
] = name
;
78 results
= talloc_realloc(NULL
, results
, char *, num_results
);
79 char **resptr
= results
;
80 for (int i
= strlen(basename
); i
>= 0; i
--) {
81 char **p
= names_by_matchlen
[i
];
82 for (int j
= 0; j
< talloc_get_size(p
) / sizeof(char *); j
++)
85 assert(resptr
== results
+ num_results
);
90 static int find_ordered_chapter_sources(struct MPContext
*mpctx
,
91 struct content_source
*sources
,
93 unsigned char uid_map
[][16])
95 int num_filenames
= 0;
96 char **filenames
= NULL
;
97 if (num_sources
> 1) {
98 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "This file references data from "
100 if (mpctx
->stream
->type
!= STREAMTYPE_FILE
) {
101 mp_msg(MSGT_CPLAYER
, MSGL_WARN
, "Playback source is not a "
102 "normal disk file. Will not search for related files.\n");
104 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Will scan other files in the "
105 "same directory to find referenced sources.\n");
106 filenames
= find_files(mpctx
->demuxer
->filename
, ".mkv");
107 num_filenames
= MP_TALLOC_ELEMS(filenames
);
111 int num_left
= num_sources
- 1;
112 for (int i
= 0; i
< num_filenames
&& num_left
> 0; i
++) {
113 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Checking file %s\n",
114 filename_recode(filenames
[i
]));
116 struct stream
*s
= open_stream(filenames
[i
], &mpctx
->opts
, &format
);
119 struct demuxer
*d
= demux_open_withparams(&mpctx
->opts
, s
,
120 DEMUXER_TYPE_MATROSKA
, mpctx
->opts
.audio_id
,
121 mpctx
->opts
.video_id
, mpctx
->opts
.sub_id
, filenames
[i
],
122 &(struct demuxer_params
){.matroska_wanted_uids
= uid_map
});
129 if (d
->file_format
== DEMUXER_TYPE_MATROSKA
) {
130 for (int i
= 1; i
< num_sources
; i
++) {
131 if (sources
[i
].demuxer
)
133 if (!memcmp(uid_map
[i
], d
->matroska_data
.segment_uid
, 16)) {
134 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,"Match for source %d: %s\n",
135 i
, filename_recode(d
->filename
));
136 sources
[i
].stream
= s
;
137 sources
[i
].demuxer
= d
;
149 talloc_free(filenames
);
151 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "Failed to find ordered chapter part!\n"
152 "There will be parts MISSING from the video!\n");
153 for (int i
= 1, j
= 1; i
< num_sources
; i
++)
154 if (sources
[i
].demuxer
) {
155 sources
[j
] = sources
[i
];
156 memcpy(uid_map
[j
], uid_map
[i
], 16);
160 return num_sources
- num_left
;
163 void build_ordered_chapter_timeline(struct MPContext
*mpctx
)
165 struct MPOpts
*opts
= &mpctx
->opts
;
167 if (!opts
->ordered_chapters
) {
168 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, but "
169 "you have disabled support for them. Ignoring.\n");
173 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, will build "
176 struct demuxer
*demuxer
= mpctx
->demuxer
;
177 struct matroska_data
*m
= &demuxer
->matroska_data
;
179 // +1 because sources/uid_map[0] is original file even if all chapters
180 // actually use other sources and need separate entries
181 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
182 m
->num_ordered_chapters
+1);
183 sources
[0].stream
= mpctx
->stream
;
184 sources
[0].demuxer
= mpctx
->demuxer
;
185 unsigned char (*uid_map
)[16] = talloc_array_ptrtype(NULL
, uid_map
,
186 m
->num_ordered_chapters
+ 1);
188 memcpy(uid_map
[0], m
->segment_uid
, 16);
190 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
191 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
192 if (!c
->has_segment_uid
)
193 memcpy(c
->segment_uid
, m
->segment_uid
, 16);
195 for (int j
= 0; j
< num_sources
; j
++)
196 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
198 memcpy(uid_map
[num_sources
], c
->segment_uid
, 16);
199 sources
[num_sources
] = (struct content_source
){};
205 num_sources
= find_ordered_chapter_sources(mpctx
, sources
, num_sources
,
209 // +1 for terminating chapter with start time marking end of last real one
210 struct timeline_part
*timeline
= talloc_array_ptrtype(NULL
, timeline
,
211 m
->num_ordered_chapters
+ 1);
212 struct chapter
*chapters
= talloc_array_ptrtype(NULL
, chapters
,
213 m
->num_ordered_chapters
);
214 uint64_t starttime
= 0;
215 uint64_t missing_time
= 0;
217 int num_chapters
= 0;
218 uint64_t prev_part_offset
= 0;
219 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
220 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
223 for (j
= 0; j
< num_sources
; j
++) {
224 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
227 missing_time
+= c
->end
- c
->start
;
230 /* Only add a separate part if the time or file actually changes.
231 * Matroska files have chapter divisions that are redundant from
232 * timeline point of view because the same chapter structure is used
233 * both to specify the timeline and for normal chapter information.
234 * Removing a missing inserted external chapter can also cause this.
235 * We allow for a configurable fudge factor because of files which
236 * specify chapter end times that are one frame too early;
237 * we don't want to try seeking over a one frame gap. */
238 int64_t join_diff
= c
->start
- starttime
- prev_part_offset
;
240 || FFABS(join_diff
) > opts
->chapter_merge_threshold
* 1000000
241 || sources
+ j
!= timeline
[part_count
- 1].source
) {
242 timeline
[part_count
].source
= sources
+ j
;
243 timeline
[part_count
].start
= starttime
/ 1e9
;
244 timeline
[part_count
].source_start
= c
->start
/ 1e9
;
245 prev_part_offset
= c
->start
- starttime
;
247 } else if (part_count
> 0 && join_diff
) {
248 /* Chapter was merged at an inexact boundary;
249 * adjust timestamps to match. */
250 mp_msg(MSGT_CPLAYER
, MSGL_V
, "Merging timeline part %d with "
251 "offset %g ms.\n", i
, join_diff
/ 1e6
);
252 starttime
+= join_diff
;
254 chapters
[num_chapters
].start
= starttime
/ 1e9
;
255 chapters
[num_chapters
].name
= talloc_strdup(chapters
, c
->name
);
256 starttime
+= c
->end
- c
->start
;
259 timeline
[part_count
].start
= starttime
/ 1e9
;
260 talloc_free(uid_map
);
263 // None of the parts come from the file itself???
264 talloc_free(sources
);
265 talloc_free(timeline
);
266 talloc_free(chapters
);
271 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "There are %.3f seconds missing "
272 "from the timeline!\n", missing_time
/ 1e9
);
273 mpctx
->sources
= sources
;
274 mpctx
->num_sources
= num_sources
;
275 mpctx
->timeline
= timeline
;
276 mpctx
->num_timeline_parts
= part_count
;
277 mpctx
->num_chapters
= num_chapters
;
278 mpctx
->chapters
= chapters
;