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(&mpctx
->opts
, s
, DEMUXER_TYPE_MATROSKA
,
116 mpctx
->opts
.audio_id
,
117 mpctx
->opts
.video_id
,
118 mpctx
->opts
.sub_id
, filenames
[i
]);
123 if (d
->file_format
== DEMUXER_TYPE_MATROSKA
) {
124 for (int i
= 1; i
< num_sources
; i
++) {
125 if (sources
[i
].demuxer
)
127 if (!memcmp(uid_map
[i
], d
->matroska_data
.segment_uid
, 16)) {
128 mp_msg(MSGT_CPLAYER
, MSGL_INFO
,"Match for source %d: %s\n",
129 i
, filename_recode(d
->filename
));
130 sources
[i
].stream
= s
;
131 sources
[i
].demuxer
= d
;
143 talloc_free(filenames
);
145 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "Failed to find ordered chapter part!\n"
146 "There will be parts MISSING from the video!\n");
147 for (int i
= 1, j
= 1; i
< num_sources
; i
++)
148 if (sources
[i
].demuxer
) {
149 sources
[j
] = sources
[i
];
150 memcpy(uid_map
[j
], uid_map
[i
], 16);
154 return num_sources
- num_left
;
157 void build_ordered_chapter_timeline(struct MPContext
*mpctx
)
159 struct MPOpts
*opts
= &mpctx
->opts
;
161 if (!opts
->ordered_chapters
) {
162 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, but "
163 "you have disabled support for them. Ignoring.\n");
167 mp_msg(MSGT_CPLAYER
, MSGL_INFO
, "File uses ordered chapters, will build "
170 struct demuxer
*demuxer
= mpctx
->demuxer
;
171 struct matroska_data
*m
= &demuxer
->matroska_data
;
173 // +1 because sources/uid_map[0] is original file even if all chapters
174 // actually use other sources and need separate entries
175 struct content_source
*sources
= talloc_array_ptrtype(NULL
, sources
,
176 m
->num_ordered_chapters
+1);
177 sources
[0].stream
= mpctx
->stream
;
178 sources
[0].demuxer
= mpctx
->demuxer
;
179 unsigned char uid_map
[m
->num_ordered_chapters
+1][16];
181 memcpy(uid_map
[0], m
->segment_uid
, 16);
183 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
184 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
185 if (!c
->has_segment_uid
)
186 memcpy(c
->segment_uid
, m
->segment_uid
, 16);
188 for (int j
= 0; j
< num_sources
; j
++)
189 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
191 memcpy(uid_map
[num_sources
], c
->segment_uid
, 16);
192 sources
[num_sources
] = (struct content_source
){};
198 num_sources
= find_ordered_chapter_sources(mpctx
, sources
, num_sources
,
202 // +1 for terminating chapter with start time marking end of last real one
203 struct timeline_part
*timeline
= talloc_array_ptrtype(NULL
, timeline
,
204 m
->num_ordered_chapters
+ 1);
205 struct chapter
*chapters
= talloc_array_ptrtype(NULL
, chapters
,
206 m
->num_ordered_chapters
);
207 uint64_t starttime
= 0;
208 uint64_t missing_time
= 0;
210 int num_chapters
= 0;
211 uint64_t prev_part_offset
= 0;
212 for (int i
= 0; i
< m
->num_ordered_chapters
; i
++) {
213 struct matroska_chapter
*c
= m
->ordered_chapters
+ i
;
216 for (j
= 0; j
< num_sources
; j
++) {
217 if (!memcmp(c
->segment_uid
, uid_map
[j
], 16))
220 missing_time
+= c
->end
- c
->start
;
223 /* Only add a separate part if the time or file actually changes.
224 * Matroska files have chapter divisions that are redundant from
225 * timeline point of view because the same chapter structure is used
226 * both to specify the timeline and for normal chapter information.
227 * Removing a missing inserted external chapter can also cause this.
228 * We allow for a configurable fudge factor because of files which
229 * specify chapter end times that are one frame too early;
230 * we don't want to try seeking over a one frame gap. */
231 int64_t join_diff
= c
->start
- starttime
- prev_part_offset
;
233 || FFABS(join_diff
) > opts
->chapter_merge_threshold
* 1000000
234 || sources
+ j
!= timeline
[part_count
- 1].source
) {
235 timeline
[part_count
].source
= sources
+ j
;
236 timeline
[part_count
].start
= starttime
/ 1e9
;
237 timeline
[part_count
].source_start
= c
->start
/ 1e9
;
238 prev_part_offset
= c
->start
- starttime
;
240 } else if (part_count
> 0 && join_diff
) {
241 /* Chapter was merged at an inexact boundary;
242 * adjust timestamps to match. */
243 mp_msg(MSGT_CPLAYER
, MSGL_V
, "Merging timeline part %d with "
244 "offset %d ms.\n", i
, (int) join_diff
);
245 starttime
+= join_diff
;
247 chapters
[num_chapters
].start
= starttime
/ 1e9
;
248 chapters
[num_chapters
].name
= talloc_strdup(chapters
, c
->name
);
249 starttime
+= c
->end
- c
->start
;
252 timeline
[part_count
].start
= starttime
/ 1e9
;
255 // None of the parts come from the file itself???
256 talloc_free(sources
);
257 talloc_free(timeline
);
258 talloc_free(chapters
);
263 mp_msg(MSGT_CPLAYER
, MSGL_ERR
, "There are %.3f seconds missing "
264 "from the timeline!\n", missing_time
/ 1e9
);
265 mpctx
->sources
= sources
;
266 mpctx
->num_sources
= num_sources
;
267 mpctx
->timeline
= timeline
;
268 mpctx
->num_timeline_parts
= part_count
;
269 mpctx
->num_chapters
= num_chapters
;
270 mpctx
->chapters
= chapters
;