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"
35 static char **find_files(const char *original_file
, const char *suffix
)
37 void *tmpmem
= talloc_new(NULL
);
38 char *basename
= mp_basename(original_file
);
39 struct bstr directory
= mp_dirname(original_file
);
40 char **results
= talloc_size(NULL
, 0);
41 char *dir_zero
= bstrdup0(tmpmem
, directory
);
42 DIR *dp
= opendir(dir_zero
);
48 char ***names_by_matchlen
= talloc_zero_array(tmpmem
, char **,
49 strlen(basename
) + 1);
51 while ((ep
= readdir(dp
))) {
52 int suffix_offset
= strlen(ep
->d_name
) - strlen(suffix
);
53 // name must end with suffix
54 if (suffix_offset
< 0 || strcmp(ep
->d_name
+ suffix_offset
, suffix
))
56 // don't list the original name
57 if (!strcmp(ep
->d_name
, basename
))
60 char *name
= mp_path_join(results
, directory
, bstr(ep
->d_name
));
61 char *s1
= ep
->d_name
;
64 while (*s1
&& *s1
++ == *s2
++)
66 int oldcount
= MP_TALLOC_ELEMS(names_by_matchlen
[matchlen
]);
67 names_by_matchlen
[matchlen
] = talloc_realloc(names_by_matchlen
,
68 names_by_matchlen
[matchlen
],
69 char *, oldcount
+ 1);
70 names_by_matchlen
[matchlen
][oldcount
] = name
;
74 results
= talloc_realloc(NULL
, results
, char *, num_results
);
75 char **resptr
= results
;
76 for (int i
= strlen(basename
); i
>= 0; i
--) {
77 char **p
= names_by_matchlen
[i
];
78 for (int j
= 0; j
< talloc_get_size(p
) / sizeof(char *); j
++)
81 assert(resptr
== results
+ num_results
);
86 static int find_ordered_chapter_sources(struct MPContext
*mpctx
,
87 struct content_source
*sources
,
89 unsigned char uid_map
[][16])
91 int num_filenames
= 0;
92 char **filenames
= NULL
;
93 if (num_sources
> 1) {
94 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "This file references data from "
96 if (mpctx
->stream
->type
!= STREAMTYPE_FILE
) {
97 mp_msg(MSGT_CPLAYER
, MSGL_WARN
, "Playback source is not a "
98 "normal disk file. Will not search for related files.\n");
100 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Will scan other files in the "
101 "same directory to find referenced sources.\n");
102 filenames
= find_files(mpctx
->demuxer
->filename
, ".mkv");
103 num_filenames
= MP_TALLOC_ELEMS(filenames
);
107 int num_left
= num_sources
- 1;
108 for (int i
= 0; i
< num_filenames
&& num_left
> 0; i
++) {
109 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "Checking file %s\n",
110 filename_recode(filenames
[i
]));
112 struct stream
*s
= open_stream(filenames
[i
], &mpctx
->opts
, &format
);
115 struct demuxer
*d
= demux_open_withparams(&mpctx
->opts
, s
,
116 DEMUXER_TYPE_MATROSKA
, mpctx
->opts
.audio_id
,
117 mpctx
->opts
.video_id
, mpctx
->opts
.sub_id
, filenames
[i
],
118 &(struct demuxer_params
){.matroska_wanted_uids
= uid_map
});
125 if (d
->file_format
== DEMUXER_TYPE_MATROSKA
) {
126 for (int i
= 1; i
< num_sources
; i
++) {
127 if (sources
[i
].demuxer
)
129 if (!memcmp(uid_map
[i
], d
->matroska_data
.segment_uid
, 16)) {
130 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,"Match for source %d: %s\n",
131 i
, filename_recode(d
->filename
));
132 sources
[i
].stream
= s
;
133 sources
[i
].demuxer
= d
;
145 talloc_free(filenames
);
147 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "Failed to find ordered chapter part!\n"
148 "There will be parts MISSING from the video!\n");
149 for (int i
= 1, j
= 1; i
< num_sources
; i
++)
150 if (sources
[i
].demuxer
) {
151 sources
[j
] = sources
[i
];
152 memcpy(uid_map
[j
], uid_map
[i
], 16);
156 return num_sources
- num_left
;
159 void build_ordered_chapter_timeline(struct MPContext
*mpctx
)
161 struct MPOpts
*opts
= &mpctx
->opts
;
163 if (!opts
->ordered_chapters
) {
164 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, but "
165 "you have disabled support for them. Ignoring.\n");
169 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, will build "
172 struct demuxer
*demuxer
= mpctx
->demuxer
;
173 struct matroska_data
*m
= &demuxer
->matroska_data
;
175 // +1 because sources/uid_map[0] is original file even if all chapters
176 // actually use other sources and need separate entries
177 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
178 m
->num_ordered_chapters
+1);
179 sources
[0].stream
= mpctx
->stream
;
180 sources
[0].demuxer
= mpctx
->demuxer
;
181 unsigned char (*uid_map
)[16] = talloc_array_ptrtype(NULL
, uid_map
,
182 m
->num_ordered_chapters
+ 1);
184 memcpy(uid_map
[0], m
->segment_uid
, 16);
186 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
187 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
188 if (!c
->has_segment_uid
)
189 memcpy(c
->segment_uid
, m
->segment_uid
, 16);
191 for (int j
= 0; j
< num_sources
; j
++)
192 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
194 memcpy(uid_map
[num_sources
], c
->segment_uid
, 16);
195 sources
[num_sources
] = (struct content_source
){};
201 num_sources
= find_ordered_chapter_sources(mpctx
, sources
, num_sources
,
205 // +1 for terminating chapter with start time marking end of last real one
206 struct timeline_part
*timeline
= talloc_array_ptrtype(NULL
, timeline
,
207 m
->num_ordered_chapters
+ 1);
208 struct chapter
*chapters
= talloc_array_ptrtype(NULL
, chapters
,
209 m
->num_ordered_chapters
);
210 uint64_t starttime
= 0;
211 uint64_t missing_time
= 0;
213 int num_chapters
= 0;
214 uint64_t prev_part_offset
= 0;
215 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
216 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
219 for (j
= 0; j
< num_sources
; j
++) {
220 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
223 missing_time
+= c
->end
- c
->start
;
226 /* Only add a separate part if the time or file actually changes.
227 * Matroska files have chapter divisions that are redundant from
228 * timeline point of view because the same chapter structure is used
229 * both to specify the timeline and for normal chapter information.
230 * Removing a missing inserted external chapter can also cause this.
231 * We allow for a configurable fudge factor because of files which
232 * specify chapter end times that are one frame too early;
233 * we don't want to try seeking over a one frame gap. */
234 int64_t join_diff
= c
->start
- starttime
- prev_part_offset
;
236 || FFABS(join_diff
) > opts
->chapter_merge_threshold
* 1000000
237 || sources
+ j
!= timeline
[part_count
- 1].source
) {
238 timeline
[part_count
].source
= sources
+ j
;
239 timeline
[part_count
].start
= starttime
/ 1e9
;
240 timeline
[part_count
].source_start
= c
->start
/ 1e9
;
241 prev_part_offset
= c
->start
- starttime
;
243 } else if (part_count
> 0 && join_diff
) {
244 /* Chapter was merged at an inexact boundary;
245 * adjust timestamps to match. */
246 mp_msg(MSGT_CPLAYER
, MSGL_V
, "Merging timeline part %d with "
247 "offset %g ms.\n", i
, join_diff
/ 1e6
);
248 starttime
+= join_diff
;
250 chapters
[num_chapters
].start
= starttime
/ 1e9
;
251 chapters
[num_chapters
].name
= talloc_strdup(chapters
, c
->name
);
252 starttime
+= c
->end
- c
->start
;
255 timeline
[part_count
].start
= starttime
/ 1e9
;
256 talloc_free(uid_map
);
259 // None of the parts come from the file itself???
260 talloc_free(sources
);
261 talloc_free(timeline
);
262 talloc_free(chapters
);
267 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "There are %.3f seconds missing "
268 "from the timeline!\n", missing_time
/ 1e9
);
269 mpctx
->sources
= sources
;
270 mpctx
->num_sources
= num_sources
;
271 mpctx
->timeline
= timeline
;
272 mpctx
->num_timeline_parts
= part_count
;
273 mpctx
->num_chapters
= num_chapters
;
274 mpctx
->chapters
= chapters
;