1 /** \brief output handling
2
3 mkvmerge -- utility for splicing together matroska files
4 from component media subtypes
5
6 Distributed under the GPL v2
7 see the file COPYING for details
8 or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9
10 \file
11
12 \author Written by Moritz Bunkus <moritz@bunkus.org>.
13 \author Modified by Steve Lhomme <steve.lhomme@free.fr>.
14 */
15
16 #include "common/common_pch.h"
17
18 #include <cmath>
19 #include <iostream>
20 #if defined(SYS_UNIX) || defined(SYS_APPLE)
21 # include <signal.h>
22 #endif
23 #include <typeinfo>
24
25 #include <QDateTime>
26 #include <QRegularExpression>
27
28 #include <ebml/EbmlHead.h>
29 #include <ebml/EbmlSubHead.h>
30 #include <ebml/EbmlVersion.h>
31 #include <ebml/EbmlVoid.h>
32
33 #include <matroska/FileKax.h>
34 #include <matroska/KaxAttached.h>
35 #include <matroska/KaxAttachments.h>
36 #include <matroska/KaxBlock.h>
37 #include <matroska/KaxChapters.h>
38 #include <matroska/KaxCluster.h>
39 #include <matroska/KaxClusterData.h>
40 #include <matroska/KaxCues.h>
41 #include <matroska/KaxCuesData.h>
42 #include <matroska/KaxInfo.h>
43 #include <matroska/KaxInfoData.h>
44 #include <matroska/KaxSeekHead.h>
45 #include <matroska/KaxSegment.h>
46 #include <matroska/KaxTags.h>
47 #include <matroska/KaxTag.h>
48 #include <matroska/KaxTracks.h>
49 #include <matroska/KaxTrackEntryData.h>
50 #include <matroska/KaxTrackAudio.h>
51 #include <matroska/KaxTrackVideo.h>
52 #include <matroska/KaxVersion.h>
53
54 #include "common/chapters/chapters.h"
55 #include "common/command_line.h"
56 #include "common/construct.h"
57 #include "common/container.h"
58 #include "common/date_time.h"
59 #include "common/debugging.h"
60 #include "common/doc_type_version_handler.h"
61 #include "common/ebml.h"
62 #include "common/fs_sys_helpers.h"
63 #include "common/hacks.h"
64 #include "common/list_utils.h"
65 #include "common/mm_io_x.h"
66 #include "common/mm_null_io.h"
67 #include "common/mm_proxy_io.h"
68 #include "common/mm_write_buffer_io.h"
69 #include "common/path.h"
70 #include "common/qt.h"
71 #include "common/strings/formatting.h"
72 #include "common/tags/tags.h"
73 #include "common/translation.h"
74 #include "common/unique_numbers.h"
75 #include "common/version.h"
76 #include "merge/cluster_helper.h"
77 #include "merge/cues.h"
78 #include "merge/filelist.h"
79 #include "merge/generic_packetizer.h"
80 #include "merge/generic_reader.h"
81 #include "merge/output_control.h"
82 #include "merge/webm.h"
83
84 using namespace libmatroska;
85 using namespace mtx::construct;
86
87 namespace libmatroska {
88
89 class KaxMyDuration: public KaxDuration {
90 public:
KaxMyDuration(const EbmlFloat::Precision prec)91 KaxMyDuration(const EbmlFloat::Precision prec): KaxDuration() {
92 SetPrecision(prec);
93 }
94 };
95 }
96
97 std::vector<packetizer_t> g_packetizers;
98 std::vector<filelist_cptr> g_files;
99 std::vector<attachment_cptr> g_attachments;
100 std::vector<track_order_t> g_track_order;
101 std::vector<append_spec_t> g_append_mapping;
102 std::unordered_map<int64_t, generic_packetizer_c *> g_packetizers_by_track_num;
103 family_uids_c g_segfamily_uids;
104
105 int64_t g_attachment_sizes_first = 0;
106 int64_t g_attachment_sizes_others = 0;
107
108 kax_info_cptr g_kax_info_chap;
109
110 // Variables set by the command line parser.
111 std::string g_outfile;
112 int64_t g_file_sizes = 0;
113 int g_max_blocks_per_cluster = 65535;
114 int64_t g_max_ns_per_cluster = 5000000000ll;
115 bool g_write_cues = true;
116 bool g_cue_writing_requested = false;
117 generic_packetizer_c *g_video_packetizer = nullptr;
118 bool g_write_meta_seek_for_clusters = false;
119 bool g_no_lacing = false;
120 bool g_no_linking = true;
121 bool g_use_durations = false;
122 bool g_no_track_statistics_tags = false;
123 bool g_write_date = true;
124
125 double g_timestamp_scale = TIMESTAMP_SCALE;
126 timestamp_scale_mode_e g_timestamp_scale_mode = timestamp_scale_mode_e{TIMESTAMP_SCALE_MODE_NORMAL};
127
128 double g_video_fps = -1.0;
129
130 bool g_identifying = false;
131 identification_output_format_e g_identification_output_format = identification_output_format_e::text;
132
133 std::unique_ptr<KaxSegment> g_kax_segment;
134 std::unique_ptr<KaxTracks> g_kax_tracks;
135 KaxTrackEntry *g_kax_last_entry = nullptr;
136 std::unique_ptr<KaxSeekHead> g_kax_sh_main;
137 std::unique_ptr<KaxSeekHead> g_kax_sh_cues;
138 mtx::chapters::kax_cptr g_kax_chapters;
139
140 std::unique_ptr<KaxTags> g_tags_from_cue_chapters;
141
142 std::string g_chapter_file_name;
143 mtx::bcp47::language_c g_chapter_language;
144 std::string g_chapter_charset;
145
146 std::string g_segmentinfo_file_name;
147
148 std::string g_segment_title;
149 bool g_segment_title_set = false;
150
151 std::string g_segment_filename, g_previous_segment_filename, g_next_segment_filename;
152
153 int64_t g_tags_size = 0;
154
155 int g_file_num = 1;
156
157 int g_split_max_num_files = 65535;
158 bool g_splitting_by_all_chapters = false;
159 std::unordered_map<unsigned int, int> g_splitting_by_chapter_numbers;
160
161 append_mode_e g_append_mode = APPEND_MODE_FILE_BASED;
162 bool s_appending_files = false;
163 auto s_debug_appending = debugging_option_c{"append|appending"};
164 auto s_debug_rerender_track_headers = debugging_option_c{"rerender|rerender_track_headers"};
165 auto s_debug_splitting_chapters = debugging_option_c{"splitting_chapters"};
166
167 mtx::bcp47::language_c g_default_language;
168
169 mtx::bits::value_cptr g_seguid_link_previous;
170 mtx::bits::value_cptr g_seguid_link_next;
171 std::deque<mtx::bits::value_cptr> g_forced_seguids;
172
173 std::unique_ptr<KaxInfo> s_kax_infos;
174 static KaxMyDuration *s_kax_duration;
175
176 static std::unique_ptr<KaxTags> s_kax_tags;
177 static mtx::chapters::kax_cptr s_chapters_in_this_file;
178
179 static std::unique_ptr<KaxAttachments> s_kax_as;
180
181 static std::unique_ptr<EbmlVoid> s_kax_sh_void;
182 static std::unique_ptr<EbmlVoid> s_kax_chapters_void;
183 static int64_t s_max_chapter_size = 0;
184 static std::unique_ptr<EbmlVoid> s_void_after_track_headers;
185
186 static std::vector<std::tuple<timestamp_c, std::string, mtx::bcp47::language_c>> s_additional_chapter_atoms;
187
188 static mm_io_cptr s_out;
189
190 static mtx::bits::value_c s_seguid_prev(128), s_seguid_current(128), s_seguid_next(128);
191
192 static std::unique_ptr<EbmlHead> s_head;
193
194 static std::string s_muxing_app, s_writing_app;
195 static QDateTime s_writing_date;
196
197 static std::optional<int64_t> s_maximum_progress;
198 int64_t s_current_progress{};
199
200 std::unique_ptr<mtx::doc_type_version_handler_c> g_doc_type_version_handler;
201
202 bool g_deterministic{};
203
204 /** \brief Add a segment family UID to the list if it doesn't exist already.
205
206 \param family This segment family element is converted to a 128 bit
207 element which is added to the list of segment family UIDs.
208 */
209 bool
add_family_uid(const KaxSegmentFamily & family)210 family_uids_c::add_family_uid(const KaxSegmentFamily &family) {
211 mtx::bits::value_c new_uid(family);
212
213 // look for the same UID
214 family_uids_c::const_iterator it;
215 for (it = begin(); it != end(); it++)
216 if (new_uid == *it)
217 return false;
218
219 push_back(new_uid);
220
221 return true;
222 }
223
224 static int64_t
calculate_file_duration()225 calculate_file_duration() {
226 return std::llround(static_cast<double>(g_cluster_helper->get_duration()) / static_cast<double>(g_timestamp_scale));
227 }
228
229 static void
update_ebml_head()230 update_ebml_head() {
231 if (g_cluster_helper->discarding())
232 return;
233
234 auto result = g_doc_type_version_handler->update_ebml_head(*s_out);
235 if (mtx::included_in(result, mtx::doc_type_version_handler_c::update_result_e::ok_updated, mtx::doc_type_version_handler_c::update_result_e::ok_no_update_needed))
236 return;
237
238 auto details = mtx::doc_type_version_handler_c::update_result_e::err_no_head_found == result ? Y("No 'EBML head' element was found.")
239 : mtx::doc_type_version_handler_c::update_result_e::err_not_enough_space == result ? Y("There's not enough space at the beginning of the file to fit the updated 'EBML head' element in.")
240 : Y("A generic read or write failure occurred.");
241
242 mxwarn(fmt::format("{0} {1}\n", Y("Updating the 'document type version' or 'document type read version' header fields failed."), details));
243 }
244
245 /** \brief Fix the file after mkvmerge has been interrupted
246
247 On Unix like systems mkvmerge will install a signal handler. On \c SIGUSR1
248 all debug information will be dumped to \c stdout if mkvmerge has been
249 compiled with \c -DDEBUG.
250
251 On \c SIGINT mkvmerge will try to sanitize the current output file
252 by writing the cues, the meta seek information and by updating the
253 segment duration and the segment length.
254 */
255 #if defined(SYS_UNIX) || defined(SYS_APPLE)
256 void
reraise_sigint()257 reraise_sigint() {
258 mxmsg(MXMSG_ERROR, Y("mkvmerge was interrupted by a SIGINT (Ctrl+C?)\n"));
259 signal(SIGINT, SIG_DFL);
260 kill(getpid(), SIGINT);
261 }
262
263 void
sighandler(int)264 sighandler(int /* signum */) {
265 if (!s_out)
266 reraise_sigint();
267
268 mxwarn(Y("\nmkvmerge received a SIGINT (probably because the user pressed "
269 "Ctrl+C). Trying to sanitize the file. If mkvmerge hangs during "
270 "this process you'll have to kill it manually.\n"));
271
272 mxinfo(Y("The file is being fixed, part 1/4..."));
273 // Render the cues.
274 if (g_write_cues && g_cue_writing_requested)
275 cues_c::get().write(*s_out, *g_kax_sh_main);
276 mxinfo(Y(" done\n"));
277
278 mxinfo(Y("The file is being fixed, part 2/4..."));
279 // Now re-render the kax_duration and fill in the biggest timestamp
280 // as the file's duration.
281 s_out->save_pos(s_kax_duration->GetElementPosition());
282 s_kax_duration->SetValue(calculate_file_duration());
283 g_doc_type_version_handler->render(*s_kax_duration, *s_out);
284 update_ebml_head();
285 s_out->restore_pos();
286 mxinfo(Y(" done\n"));
287
288 mxinfo(Y("The file is being fixed, part 3/4..."));
289 if ((g_kax_sh_main->ListSize() > 0) && !mtx::hacks::is_engaged(mtx::hacks::NO_META_SEEK)) {
290 g_kax_sh_main->UpdateSize();
291 if (s_kax_sh_void->ReplaceWith(*g_kax_sh_main, *s_out, true) == INVALID_FILEPOS_T)
292 mxwarn(fmt::format(Y("This should REALLY not have happened. The space reserved for the first meta seek element was too small. {0}\n"), BUGMSG));
293 }
294 mxinfo(Y(" done\n"));
295
296 mxinfo(Y("The file is being fixed, part 4/4..."));
297 // Set the correct size for the segment.
298 if (g_kax_segment->ForceSize(s_out->getFilePointer() - g_kax_segment->GetElementPosition() - g_kax_segment->HeadSize()))
299 g_kax_segment->OverwriteHead(*s_out);
300
301 mxinfo(Y(" done\n"));
302
303 // Manually close s_out because cleanup() will discard any remaining
304 // write buffer content in s_out.
305 s_out->close();
306
307 cleanup();
308
309 reraise_sigint();
310 }
311 #endif
312
313 void
add_to_progress(int64_t num_bytes_processed)314 add_to_progress(int64_t num_bytes_processed) {
315 s_current_progress += num_bytes_processed;
316 }
317
318 static int64_t
get_maximum_progress()319 get_maximum_progress() {
320 if (!s_maximum_progress)
321 s_maximum_progress = std::accumulate(g_files.begin(), g_files.end(), 0ull, [](int64_t num, auto const &file) { return num + file->reader->get_maximum_progress(); });
322
323 return *s_maximum_progress;
324 }
325
326 /** \brief Selects a reader for displaying its progress information
327 */
328 static void
display_progress(bool is_100percent=false)329 display_progress(bool is_100percent = false) {
330 static auto s_no_progress = debugging_option_c{"no_progress"};
331 static int64_t s_previous_progress_on = 0;
332 static int s_previous_percentage = -1;
333
334 if (s_no_progress)
335 return;
336
337 if (is_100percent) {
338 if (mtx::cli::g_gui_mode)
339 mxinfo(fmt::format("#GUI#progress 100%\n"));
340 else
341 mxinfo(fmt::format(Y("Progress: 100%{0}"), "\r"));
342 return;
343 }
344
345 bool display_progress = false;
346 auto maximum_progress = get_maximum_progress();
347 int current_percentage = maximum_progress ? (s_current_progress * 100) / maximum_progress : 0;
348 int64_t current_time = mtx::sys::get_current_time_millis();
349
350 if ( (-1 == s_previous_percentage)
351 || ((100 == current_percentage) && (100 > s_previous_percentage))
352 || ((current_percentage != s_previous_percentage) && ((current_time - s_previous_progress_on) >= 500)))
353 display_progress = true;
354
355 if (!display_progress)
356 return;
357
358 // if (2 < current_percentage)
359 // exit(42);
360
361 if (mtx::cli::g_gui_mode)
362 mxinfo(fmt::format("#GUI#progress {0}%\n", current_percentage));
363 else
364 mxinfo(fmt::format(Y("Progress: {0}%{1}"), current_percentage, "\r"));
365
366 s_previous_percentage = current_percentage;
367 s_previous_progress_on = current_time;
368 }
369
370 /** \brief Add some tags to the list of all tags
371 */
372 void
add_tags(KaxTag & tags)373 add_tags(KaxTag &tags) {
374 if (tags.ListSize() == 0) {
375 delete &tags;
376 return;
377 }
378
379 if (!s_kax_tags)
380 s_kax_tags = std::make_unique<KaxTags>();
381
382 s_kax_tags->PushElement(tags);
383 }
384
385 /** \brief Add an attachment
386
387 \param attachment The attachment specification to add
388 \return The attachment UID created for this attachment.
389 */
390 int64_t
add_attachment(attachment_cptr const & attachment)391 add_attachment(attachment_cptr const &attachment) {
392 // If the attachment is coming from an existing file then we should
393 // check if we already have another attachment stored. This can happen
394 // if we're concatenating files.
395 if (0 != attachment->id) {
396 for (auto &ex_attachment : g_attachments)
397 if (( (ex_attachment->id == attachment->id)
398 && !mtx::hacks::is_engaged(mtx::hacks::NO_VARIABLE_DATA))
399 ||
400 ( (ex_attachment->name == attachment->name)
401 && (ex_attachment->description == attachment->description)
402 && (ex_attachment->data->get_size() == attachment->data->get_size())
403 && (ex_attachment->source_file != attachment->source_file)
404 && !attachment->source_file.empty()))
405 return attachment->id;
406
407 add_unique_number(attachment->id, UNIQUE_ATTACHMENT_IDS);
408
409 } else
410 // No ID yet. Let's assign one.
411 attachment->id = create_unique_number(UNIQUE_ATTACHMENT_IDS);
412
413 g_attachments.push_back(attachment);
414
415 return attachment->id;
416 }
417
418 /** \brief Add a packetizer to the list of packetizers
419 */
420 void
add_packetizer_globally(generic_packetizer_c * packetizer)421 add_packetizer_globally(generic_packetizer_c *packetizer) {
422 packetizer_t pack;
423 pack.packetizer = packetizer;
424 pack.orig_packetizer = packetizer;
425 pack.status = FILE_STATUS_MOREDATA;
426 pack.old_status = pack.status;
427 pack.file = -1;
428
429 int idx = 0;
430 for (auto &file : g_files)
431 if (file->reader.get() == packetizer->m_reader) {
432 pack.file = idx;
433 pack.orig_file = pack.file;
434 break;
435 } else
436 ++idx;
437
438 if (-1 == pack.file)
439 mxerror(fmt::format(Y("filelist_t not found for generic_packetizer_c. {0}\n"), BUGMSG));
440
441 g_packetizers.push_back(pack);
442 }
443
444 static void
set_timestamp_scale()445 set_timestamp_scale() {
446 bool video_present = false;
447 bool audio_present = false;
448 int64_t highest_sample_rate = 0;
449
450 for (auto &ptzr : g_packetizers)
451 if (ptzr.packetizer->get_track_type() == track_video)
452 video_present = true;
453
454 else if (ptzr.packetizer->get_track_type() == track_audio) {
455 audio_present = true;
456 highest_sample_rate = std::max(static_cast<int64_t>(ptzr.packetizer->get_audio_sampling_freq()), highest_sample_rate);
457 }
458
459 bool debug = debugging_c::requested("set_timestamp_scale|timestamp_scale");
460 mxdebug_if(debug,
461 fmt::format("timestamp_scale: {0} audio present: {1} video present: {2} highest sample rate: {3}\n",
462 TIMESTAMP_SCALE_MODE_NORMAL == g_timestamp_scale_mode ? "normal"
463 : TIMESTAMP_SCALE_MODE_FIXED == g_timestamp_scale_mode ? "fixed"
464 : TIMESTAMP_SCALE_MODE_AUTO == g_timestamp_scale_mode ? "auto"
465 : "unknown",
466 audio_present, video_present, highest_sample_rate));
467
468 if ( (TIMESTAMP_SCALE_MODE_FIXED != g_timestamp_scale_mode)
469 && audio_present
470 && (0 < highest_sample_rate)
471 && ( !video_present
472 || (TIMESTAMP_SCALE_MODE_AUTO == g_timestamp_scale_mode))) {
473 auto new_timestamp_scale = static_cast<int64_t>(1'000'000'000.0 / highest_sample_rate - 1.0);
474 if (new_timestamp_scale >= 1)
475 g_timestamp_scale = new_timestamp_scale;
476 }
477
478 g_max_ns_per_cluster = std::min<int64_t>(32700 * g_timestamp_scale, g_max_ns_per_cluster);
479 GetChild<KaxTimecodeScale>(*s_kax_infos).SetValue(g_timestamp_scale);
480
481 mxdebug_if(debug, fmt::format("timestamp_scale: {0} max ns per cluster: {1}\n", g_timestamp_scale, g_max_ns_per_cluster));
482 }
483
484 static void
render_ebml_head(mm_io_c * out)485 render_ebml_head(mm_io_c *out) {
486 if (!s_head)
487 s_head = std::make_unique<EbmlHead>();
488
489 GetChild<EDocType >(*s_head).SetValue(outputting_webm() ? "webm" : "matroska");
490 GetChild<EDocTypeVersion >(*s_head).SetValue(1);
491 GetChild<EDocTypeReadVersion>(*s_head).SetValue(1);
492
493 s_head->Render(*out, true);
494 }
495
496 static void
generate_segment_uids()497 generate_segment_uids() {
498 if (g_cluster_helper->discarding())
499 return;
500
501 // Generate the segment UIDs.
502 if (mtx::hacks::is_engaged(mtx::hacks::NO_VARIABLE_DATA)) {
503 s_seguid_current.zero_content();
504 s_seguid_next.zero_content();
505 s_seguid_prev.zero_content();
506
507 return;
508 }
509
510 if (1 == g_file_num) {
511 if (g_forced_seguids.empty())
512 s_seguid_current.generate_random();
513 else {
514 s_seguid_current = *g_forced_seguids.front();
515 g_forced_seguids.pop_front();
516 }
517 s_seguid_next.generate_random();
518
519 return;
520 }
521
522 s_seguid_prev = s_seguid_current;
523 if (g_forced_seguids.empty())
524 s_seguid_current = s_seguid_next;
525 else {
526 s_seguid_current = *g_forced_seguids.front();
527 g_forced_seguids.pop_front();
528 }
529 s_seguid_next.generate_random();
530 }
531
532 /** \brief Render the basic EBML and Matroska headers
533
534 Renders the segment information and track headers. Also reserves
535 some space with EBML Void elements so that the headers can be
536 overwritten safely by the rerender_headers function.
537 */
538 static void
render_headers(mm_io_c * out)539 render_headers(mm_io_c *out) {
540 try {
541 render_ebml_head(out);
542
543 s_kax_infos = std::make_unique<KaxInfo>();
544
545 s_kax_duration = new KaxMyDuration{ !g_video_packetizer || (TIMESTAMP_SCALE_MODE_AUTO == g_timestamp_scale_mode) ? EbmlFloat::FLOAT_64 : EbmlFloat::FLOAT_32};
546
547 s_kax_duration->SetValue(0.0);
548 s_kax_infos->PushElement(*s_kax_duration);
549
550 if (s_muxing_app.empty()) {
551 auto info_data = get_default_segment_info_data("mkvmerge");
552 s_muxing_app = info_data.muxing_app;
553 s_writing_app = info_data.writing_app;
554 s_writing_date = info_data.writing_date;
555 }
556
557 GetChild<KaxMuxingApp >(*s_kax_infos).SetValueUTF8(s_muxing_app);
558 GetChild<KaxWritingApp>(*s_kax_infos).SetValueUTF8(s_writing_app);
559
560 if (g_write_date)
561 GetChild<KaxDateUTC>(*s_kax_infos).SetEpochDate(s_writing_date.toSecsSinceEpoch());
562 else
563 DeleteChildren<KaxDateUTC>(*s_kax_infos);
564
565 if (!g_segment_title.empty())
566 GetChild<KaxTitle>(*s_kax_infos).SetValueUTF8(g_segment_title.c_str());
567
568 bool first_file = (1 == g_file_num);
569
570 generate_segment_uids();
571
572 if (!outputting_webm()) {
573 // Set the segment UIDs.
574 GetChild<KaxSegmentUID>(*s_kax_infos).CopyBuffer(s_seguid_current.data(), 128 / 8);
575
576 // Set the segment family
577 if (!g_segfamily_uids.empty()) {
578 size_t i;
579 for (i = 0; i < g_segfamily_uids.size(); i++)
580 AddNewChild<KaxSegmentFamily>(*s_kax_infos).CopyBuffer(g_segfamily_uids[i].data(), 128 / 8);
581 }
582
583 // Set the chaptertranslate elements
584 if (g_kax_info_chap) {
585 // copy the KaxChapterTranslates in the current KaxInfo
586 KaxChapterTranslate *chapter_translate = FindChild<KaxChapterTranslate>(g_kax_info_chap.get());
587 while (chapter_translate) {
588 s_kax_infos->PushElement(*new KaxChapterTranslate(*chapter_translate));
589 chapter_translate = FindNextChild(*g_kax_info_chap, *chapter_translate);
590 }
591 }
592
593 if (first_file && g_seguid_link_previous)
594 GetChild<KaxPrevUID>(*s_kax_infos).CopyBuffer(g_seguid_link_previous->data(), 128 / 8);
595
596 // The next segment UID is also set in finish_file(). This is not
597 // redundant! It is set here as well in order to reserve enough space
598 // for the KaxInfo structure in the file. If it is removed later then
599 // an EbmlVoid element will be used for the freed space.
600 if (g_seguid_link_next)
601 GetChild<KaxNextUID>(*s_kax_infos).CopyBuffer(g_seguid_link_next->data(), 128 / 8);
602
603 if (!g_no_linking && g_cluster_helper->splitting()) {
604 GetChild<KaxNextUID>(*s_kax_infos).CopyBuffer(s_seguid_next.data(), 128 / 8);
605
606 if (!first_file)
607 GetChild<KaxPrevUID>(*s_kax_infos).CopyBuffer(s_seguid_prev.data(), 128 / 8);
608 }
609
610 if (!g_segment_filename.empty())
611 GetChild<KaxSegmentFilename>(*s_kax_infos).SetValueUTF8(g_segment_filename);
612
613 if (!g_next_segment_filename.empty())
614 GetChild<KaxNextFilename>(*s_kax_infos).SetValueUTF8(g_next_segment_filename);
615
616 if (!g_previous_segment_filename.empty())
617 GetChild<KaxPrevFilename>(*s_kax_infos).SetValueUTF8(g_previous_segment_filename);
618
619 g_segment_filename.clear();
620 g_next_segment_filename.clear();
621 g_previous_segment_filename.clear();
622 }
623
624 g_kax_segment->WriteHead(*out, 8);
625
626 // Reserve some space for the meta seek stuff.
627 g_kax_sh_main = std::make_unique<KaxSeekHead>();
628 s_kax_sh_void = std::make_unique<EbmlVoid>();
629 s_kax_sh_void->SetSize(4096);
630 s_kax_sh_void->Render(*out);
631
632 if (g_write_meta_seek_for_clusters)
633 g_kax_sh_cues = std::make_unique<KaxSeekHead>();
634
635 if (first_file) {
636 g_kax_last_entry = nullptr;
637
638 size_t i;
639 for (i = 0; i < g_track_order.size(); i++)
640 if ((g_track_order[i].file_id >= 0) && (g_track_order[i].file_id < static_cast<int>(g_files.size())) && !g_files[g_track_order[i].file_id]->appending)
641 g_files[g_track_order[i].file_id]->reader->set_headers_for_track(g_track_order[i].track_id);
642
643 for (i = 0; i < g_files.size(); i++)
644 if (!g_files[i]->appending)
645 g_files[i]->reader->set_headers();
646
647 set_timestamp_scale();
648
649 for (i = 0; i < g_packetizers.size(); i++)
650 if (g_packetizers[i].packetizer)
651 g_packetizers[i].packetizer->fix_headers();
652
653 } else
654 set_timestamp_scale();
655
656 g_doc_type_version_handler->render(*s_kax_infos, *out, true);
657 g_kax_sh_main->IndexThis(*s_kax_infos, *g_kax_segment);
658
659 if (!g_packetizers.empty()) {
660 g_kax_tracks->UpdateSize(true);
661 uint64_t full_header_size = g_kax_tracks->ElementSize(true);
662 g_kax_tracks->UpdateSize(false);
663
664 g_doc_type_version_handler->render(*g_kax_tracks, *out);
665 g_kax_sh_main->IndexThis(*g_kax_tracks, *g_kax_segment);
666
667 // Reserve some small amount of space for header changes by the
668 // packetizers.
669 s_void_after_track_headers = std::make_unique<EbmlVoid>();
670 s_void_after_track_headers->SetSize(1024 + full_header_size - g_kax_tracks->ElementSize(false));
671 s_void_after_track_headers->Render(*out);
672 }
673
674 } catch (...) {
675 mxerror(fmt::format(Y("The track headers could not be rendered correctly. {0}\n"), BUGMSG));
676 }
677 }
678
679 static void
adjust_cluster_seekhead_positions(uint64_t data_start_pos,uint64_t delta)680 adjust_cluster_seekhead_positions(uint64_t data_start_pos,
681 uint64_t delta) {
682 auto relative_data_start_pos = g_kax_segment->GetRelativePosition(data_start_pos);
683
684 for (auto sh_child : g_kax_sh_cues->GetElementList()) {
685 auto seek_entry = dynamic_cast<KaxSeek *>(sh_child);
686 if (!seek_entry)
687 continue;
688
689 auto seek_position = FindChild<KaxSeekPosition>(*seek_entry);
690 if (!seek_position)
691 continue;
692
693 auto old_value = seek_position->GetValue();
694 if (old_value >= relative_data_start_pos)
695 seek_position->SetValue(old_value + delta);
696 }
697 }
698
699 static void
adjust_cue_and_seekhead_positions(uint64_t data_start_pos,uint64_t delta)700 adjust_cue_and_seekhead_positions(uint64_t data_start_pos,
701 uint64_t delta) {
702 if (!delta)
703 return;
704
705 if (g_cue_writing_requested)
706 cues_c::get().adjust_positions(g_kax_segment->GetRelativePosition(data_start_pos), delta);
707
708 if (g_write_meta_seek_for_clusters)
709 adjust_cluster_seekhead_positions(data_start_pos, delta);
710 }
711
712 static void
relocate_written_data(uint64_t data_start_pos,uint64_t delta)713 relocate_written_data(uint64_t data_start_pos,
714 uint64_t delta) {
715 if (g_cluster_helper->discarding())
716 return;
717
718 auto rel_pos_from_end = s_out->get_size() - s_out->getFilePointer();
719 auto const block_size = 1024llu * 1024;
720 auto to_relocate = s_out->get_size() - data_start_pos;
721 auto relocated = 0llu;
722 auto af_buffer = memory_c::alloc(block_size);
723 auto buffer = af_buffer->get_buffer();
724
725 mxdebug_if(s_debug_rerender_track_headers,
726 fmt::format("[rerender] relocate_written_data: void pos {0} void size {1} = data_start_pos {2} s_out size {3} delta {4} to_relocate {5} rel_pos_from_end {6}\n",
727 s_void_after_track_headers->GetElementPosition(), s_void_after_track_headers->ElementSize(true), data_start_pos, s_out->get_size(), delta, to_relocate, rel_pos_from_end));
728
729 // Extend the file's size. Setting the file pointer to beyond the
730 // end and starting to write from there won't work with most of the
731 // mm_io_c-derived classes.
732 s_out->save_pos(s_out->get_size());
733 auto dummy_data = std::make_unique<std::string>(delta, '\0');
734 s_out->write(dummy_data->c_str(), dummy_data->length());
735 s_out->restore_pos();
736
737 // Copy the data from back to front in order not to overwrite
738 // existing data in case it overlaps which is likely.
739 while (relocated < to_relocate) {
740 auto to_copy = std::min(block_size, to_relocate - relocated);
741 auto src_pos = data_start_pos + to_relocate - relocated - to_copy;
742 auto dst_pos = src_pos + delta;
743
744 mxdebug_if(s_debug_rerender_track_headers, fmt::format("[rerender] relocating {0} bytes from {1} to {2}\n", to_copy, src_pos, dst_pos));
745
746 s_out->setFilePointer(src_pos);
747 auto num_read = s_out->read(buffer, to_copy);
748
749 if (num_read != to_copy) {
750 mxinfo(fmt::format(Y("Error reading from the file '{0}'.\n"), s_out->get_file_name()));
751 mxdebug_if(s_debug_rerender_track_headers, fmt::format("[rerender] relocation failed; read only {0} bytes\n", num_read));
752 }
753
754 s_out->setFilePointer(dst_pos);
755 auto num_written = s_out->write(buffer, num_read);
756
757 if (num_written != num_read)
758 mxdebug_if(s_debug_rerender_track_headers, fmt::format("[rerender] relocation failed; wrote only {0} of {1} bytes\n", num_written, num_read));
759
760 relocated += to_copy;
761 }
762
763 if (s_kax_as) {
764 mxdebug_if(s_debug_rerender_track_headers, fmt::format("[rerender] re-writing attachments; old position {0} new {1}\n", s_kax_as->GetElementPosition(), s_kax_as->GetElementPosition() + delta));
765 s_out->setFilePointer(s_kax_as->GetElementPosition() + delta);
766 g_doc_type_version_handler->render(*s_kax_as, *s_out);
767 }
768
769 if (s_kax_chapters_void) {
770 mxdebug_if(s_debug_rerender_track_headers, fmt::format("[rerender] re-writing chapter placeholder; old position {0} new {1}\n", s_kax_chapters_void->GetElementPosition(), s_kax_chapters_void->GetElementPosition() + delta));
771 s_out->setFilePointer(s_kax_chapters_void->GetElementPosition() + delta);
772 s_kax_chapters_void->Render(*s_out);
773 }
774
775 s_out->setFilePointer(rel_pos_from_end, seek_end);
776
777 adjust_cue_and_seekhead_positions(data_start_pos, delta);
778 }
779
780 static void
render_void(int64_t new_size)781 render_void(int64_t new_size) {
782 auto actual_size = new_size;
783
784 s_void_after_track_headers = std::make_unique<EbmlVoid>();
785 s_void_after_track_headers->SetSize(new_size);
786 s_void_after_track_headers->UpdateSize();
787
788 while (static_cast<int64_t>(s_void_after_track_headers->ElementSize()) > new_size)
789 s_void_after_track_headers->SetSize(--actual_size);
790
791 if (static_cast<int64_t>(s_void_after_track_headers->ElementSize()) < new_size)
792 s_void_after_track_headers->SetSizeLength(new_size - actual_size - 1);
793
794 mxdebug_if(s_debug_rerender_track_headers, fmt::format("[rerender] render_void new_size {0} actual_size {1} size_length {2}\n", new_size, actual_size, new_size - actual_size - 1));
795
796 s_void_after_track_headers->Render(*s_out);
797 }
798
799 static void
shrink_void_and_rerender_track_headers(int64_t new_void_size)800 shrink_void_and_rerender_track_headers(int64_t new_void_size) {
801 auto old_void_pos = s_void_after_track_headers->GetElementPosition();
802 auto projected_new_void_pos = g_kax_tracks->GetElementPosition() + g_kax_tracks->ElementSize();
803
804 s_out->setFilePointer(g_kax_tracks->GetElementPosition());
805
806 g_doc_type_version_handler->render(*g_kax_tracks, *s_out);
807 render_void(new_void_size);
808
809 s_out->setFilePointer(0, seek_end);
810
811 mxdebug_if(s_debug_rerender_track_headers,
812 fmt::format("[rerender] Normal case, only shrinking void down to {0}, new position {1} projected {8} new full size {2} new end {3} s_out size {4} old void start pos {5} tracks pos {6} tracks size {7}\n",
813 new_void_size,
814 s_void_after_track_headers->GetElementPosition(),
815 s_void_after_track_headers->ElementSize(),
816 s_void_after_track_headers->GetElementPosition() + s_void_after_track_headers->ElementSize(),
817 s_out->get_size(),
818 old_void_pos,
819 g_kax_tracks->GetElementPosition(),
820 g_kax_tracks->ElementSize(),
821 projected_new_void_pos));
822 }
823
824 /** \brief Overwrites the track headers with current values
825
826 Can be used by packetizers that have to modify their headers
827 depending on the track contents.
828 */
829 void
rerender_track_headers()830 rerender_track_headers() {
831 g_kax_tracks->UpdateSize(false);
832
833 auto position_before = s_out->getFilePointer();
834 auto file_size_before = s_out->get_size();
835 auto new_tracks_end_pos = g_kax_tracks->GetElementPosition() + g_kax_tracks->ElementSize();
836 auto data_start_pos = s_void_after_track_headers->GetElementPosition() + s_void_after_track_headers->ElementSize(true);
837 auto data_size = file_size_before - data_start_pos;
838 auto new_void_size = data_start_pos >= (new_tracks_end_pos + 4) ? data_start_pos - new_tracks_end_pos : 1024;
839
840 mxdebug_if(s_debug_rerender_track_headers,
841 fmt::format("[rerender] track_headers: new_tracks_end_pos {0} data_start_pos {1} data_size {2} old void at {3} size {4} new_void_size {5}\n",
842 new_tracks_end_pos, data_start_pos, data_size, s_void_after_track_headers->GetElementPosition(), s_void_after_track_headers->ElementSize(true), new_void_size));
843
844 if (data_size && (new_tracks_end_pos >= (data_start_pos - 3))) {
845 auto delta = 1024 + new_tracks_end_pos - data_start_pos;
846 data_start_pos += delta;
847 new_void_size = 1024;
848
849 relocate_written_data(data_start_pos - delta, delta);
850 }
851
852 shrink_void_and_rerender_track_headers(new_void_size);
853
854 mxdebug_if(s_debug_rerender_track_headers,
855 fmt::format("[rerender] track_headers: position_before {0} file_size_before {1} (diff {2}) position_after {3} file_size_after {4} (diff {5}) void now at {6} size {7}\n",
856 position_before, file_size_before, file_size_before - position_before, s_out->getFilePointer(), s_out->get_size(), s_out->get_size() - s_out->getFilePointer(),
857 s_void_after_track_headers->GetElementPosition(), s_void_after_track_headers->ElementSize(true)));
858 }
859
860 /** \brief Render all attachments into the output file at the current position
861
862 This function also makes sure that no duplicates are output. This might
863 happen when appending files.
864 */
865 static void
render_attachments(mm_io_c & out)866 render_attachments(mm_io_c &out) {
867 s_kax_as = std::make_unique<KaxAttachments>();
868 auto kax_a = static_cast<KaxAttached *>(nullptr);
869
870 for (auto &attachment_p : g_attachments) {
871 auto attch = *attachment_p;
872
873 if ((1 == g_file_num) || attch.to_all_files) {
874 kax_a = !kax_a ? &GetChild<KaxAttached>(*s_kax_as) : &GetNextChild<KaxAttached>(*s_kax_as, *kax_a);
875
876 if (attch.description != "")
877 GetChild<KaxFileDescription>(kax_a).SetValueUTF8(attch.description);
878
879 if (attch.mime_type != "")
880 GetChild<KaxMimeType>(kax_a).SetValue(attch.mime_type);
881
882 std::string name;
883 if (attch.stored_name == "")
884 name = mtx::fs::to_path(attch.name).filename().u8string();
885
886 else
887 name = attch.stored_name;
888
889 GetChild<KaxFileName>(kax_a).SetValueUTF8(name);
890 GetChild<KaxFileUID >(kax_a).SetValue(attch.id);
891
892 GetChild<KaxFileData>(*kax_a).CopyBuffer(attch.data->get_buffer(), attch.data->get_size());
893 }
894 }
895
896 if (s_kax_as->ListSize() != 0)
897 g_doc_type_version_handler->render(*s_kax_as, out);
898 else
899 // Delete the kax_as pointer so that it won't be referenced in a seek head.
900 s_kax_as.reset();
901 }
902
903 /** \brief Check the complete append mapping mechanism
904
905 Each entry given with '--append-to' has to be checked for validity.
906 For files that aren't managed with '--append-to' default entries have
907 to be created.
908 */
909 void
check_append_mapping()910 check_append_mapping() {
911 for (auto &amap : g_append_mapping) {
912 // Check each mapping entry for validity.
913
914 // 1. Is there a file with the src_file_id?
915 if (g_files.size() <= amap.src_file_id)
916 mxerror(fmt::format(Y("There is no file with the ID '{0}'. The argument for '--append-to' was invalid.\n"), amap.src_file_id));
917
918 // 2. Is the "source" file in "append mode", meaning does its file name
919 // start with a '+'?
920 auto src_file = g_files.begin() + amap.src_file_id;
921 if (!(*src_file)->appending)
922 mxerror(fmt::format(Y("The file no. {0} ('{1}') is not being appended. The argument for '--append-to' was invalid.\n"), amap.src_file_id, (*src_file)->name));
923
924 // 3. Is there a file with the dst_file_id?
925 if (g_files.size() <= amap.dst_file_id)
926 mxerror(fmt::format(Y("There is no file with the ID '{0}'. The argument for '--append-to' was invalid.\n"), amap.dst_file_id));
927
928 // 4. G_Files cannot be appended to itself.
929 if (amap.src_file_id == amap.dst_file_id)
930 mxerror(Y("Files cannot be appended to themselves. The argument for '--append-to' was invalid.\n"));
931 }
932
933 // Now let's check each appended file if there are NO append to mappings
934 // available (in which case we fill in default ones) or if there are fewer
935 // mappings than tracks that are to be copied (which is an error).
936 for (auto &src_file : g_files) {
937 if (!src_file->appending)
938 continue;
939
940 size_t count = std::accumulate(g_append_mapping.begin(), g_append_mapping.end(), 0, [&src_file](auto count, auto const &e) { return count + (e.src_file_id == src_file->id ? 1 : 0); });
941
942 if ((0 < count) && (src_file-> reader->m_used_track_ids.size() > count))
943 mxerror(fmt::format(Y("Only partial append mappings were given for the file no. {0} ('{1}'). Either don't specify any mapping (in which case the "
944 "default mapping will be used) or specify a mapping for all tracks that are to be copied.\n"), src_file-> id, src_file-> name));
945 else if (0 == count) {
946 std::string missing_mappings;
947
948 // Default mapping.
949 for (auto id : src_file-> reader->m_used_track_ids) {
950 append_spec_t new_amap;
951
952 new_amap.src_file_id = src_file-> id;
953 new_amap.src_track_id = id;
954 new_amap.dst_file_id = src_file-> id - 1;
955 new_amap.dst_track_id = id;
956 g_append_mapping.push_back(new_amap);
957
958 if (!missing_mappings.empty())
959 missing_mappings += ",";
960 missing_mappings += fmt::format("{0}:{1}:{2}:{3}", new_amap.src_file_id, new_amap.src_track_id, new_amap.dst_file_id, new_amap.dst_track_id);
961 }
962 mxinfo(fmt::format(Y("No append mapping was given for the file no. {0} ('{1}'). A default mapping of {2} will be used instead. "
963 "Please keep that in mind if mkvmerge aborts with an error message regarding invalid '--append-to' options.\n"),
964 src_file-> id, src_file-> name, missing_mappings));
965 }
966 }
967
968 // Some more checks.
969 for (auto &amap : g_append_mapping) {
970 auto src_file = g_files.begin() + amap.src_file_id;
971 auto dst_file = g_files.begin() + amap.dst_file_id;
972
973 // 5. Does the "source" file have a track with the src_track_id, and is
974 // that track selected for copying?
975 if (!mtx::includes((*src_file)->reader->m_used_track_ids, amap.src_track_id))
976 mxerror(fmt::format(Y("The file no. {0} ('{1}') does not contain a track with the ID {2}, or that track is not to be copied. "
977 "The argument for '--append-to' was invalid.\n"), amap.src_file_id, (*src_file)->name, amap.src_track_id));
978
979 // 6. Does the "destination" file have a track with the dst_track_id, and
980 // that track selected for copying?
981 if (!mtx::includes((*dst_file)->reader->m_used_track_ids, amap.dst_track_id))
982 mxerror(fmt::format(Y("The file no. {0} ('{1}') does not contain a track with the ID {2}, or that track is not to be copied. Therefore no "
983 "track can be appended to it. The argument for '--append-to' was invalid.\n"), amap.dst_file_id, (*dst_file)->name, amap.dst_track_id));
984
985 // 7. Is this track already mapped to somewhere else?
986 for (auto &cmp_amap : g_append_mapping) {
987 if (cmp_amap == amap)
988 continue;
989
990 if ( (cmp_amap.src_file_id == amap.src_file_id)
991 && (cmp_amap.src_track_id == amap.src_track_id))
992 mxerror(fmt::format(Y("The track {0} from file no. {1} ('{2}') is to be appended more than once. The argument for '--append-to' was invalid.\n"),
993 amap.src_track_id, amap.src_file_id, (*src_file)->name));
994 }
995
996 // 8. Is there another track that is being appended to the dst_track_id?
997 for (auto &cmp_amap : g_append_mapping) {
998 if (cmp_amap == amap)
999 continue;
1000
1001 if ( (cmp_amap.dst_file_id == amap.dst_file_id)
1002 && (cmp_amap.dst_track_id == amap.dst_track_id))
1003 mxerror(fmt::format(Y("More than one track is to be appended to the track {0} from file no. {1} ('{2}'). The argument for '--append-to' was invalid.\n"),
1004 amap.dst_track_id, amap.dst_file_id, (*dst_file)->name));
1005 }
1006 }
1007
1008 // Finally see if the packetizers can be connected and connect them if they
1009 // can.
1010 for (auto &amap : g_append_mapping) {
1011 auto src_file = g_files.begin() + amap.src_file_id;
1012 auto dst_file = g_files.begin() + amap.dst_file_id;
1013
1014 auto src_ptzr = (*src_file)->reader->find_packetizer_by_id(amap.src_track_id);
1015 auto dst_ptzr = (*dst_file)->reader->find_packetizer_by_id(amap.dst_track_id);
1016
1017 if (!src_ptzr || !dst_ptzr)
1018 mxerror(fmt::format("(!src_ptzr || !dst_ptzr). {0}\n", BUGMSG));
1019
1020 // And now try to connect the packetizers.
1021 std::string error_message;
1022 auto result = src_ptzr->can_connect_to(dst_ptzr, error_message);
1023 if (CAN_CONNECT_MAYBE_CODECPRIVATE == result)
1024 mxwarn(fmt::format(Y("The track number {0} from the file '{1}' can probably not be appended correctly to the track number {2} from the file '{3}': {4} "
1025 "Please make sure that the resulting file plays correctly the whole time. "
1026 "The author of this program will probably not give support for playback issues with the resulting file.\n"),
1027 amap.src_track_id, g_files[amap.src_file_id]->name,
1028 amap.dst_track_id, g_files[amap.dst_file_id]->name,
1029 error_message));
1030
1031 else if (CAN_CONNECT_YES != result) {
1032 if (error_message.empty())
1033 error_message = result == CAN_CONNECT_NO_FORMAT ? Y("The formats do not match.")
1034 : result == CAN_CONNECT_NO_PARAMETERS ? Y("The track parameters do not match.")
1035 : result == CAN_CONNECT_NO_UNSUPPORTED ? Y("Appending tracks of this type is not supported.")
1036 : Y("The reason is unknown.");
1037
1038 mxerror(fmt::format(Y("The track number {0} from the file '{1}' cannot be appended to the track number {2} from the file '{3}'. {4}\n"),
1039 amap.src_track_id, g_files[amap.src_file_id]->name,
1040 amap.dst_track_id, g_files[amap.dst_file_id]->name,
1041 error_message));
1042 }
1043
1044 src_ptzr->connect(dst_ptzr);
1045 (*dst_file)->appended_to = true;
1046 }
1047
1048 // Calculate the "longest path" -- meaning the maximum number of
1049 // concatenated files. This is needed for displaying the progress.
1050 for (auto amap = g_append_mapping.begin(), amap_end = g_append_mapping.end(); amap != amap_end; ++amap) {
1051 // Is this the first in a chain?
1052 auto cmp_amap = std::find_if(g_append_mapping.begin(), g_append_mapping.end(), [&amap](auto const &e) {
1053 return (*amap != e)
1054 && (amap->dst_file_id == e.src_file_id)
1055 && (amap->dst_track_id == e.src_track_id);
1056 });
1057
1058 if (cmp_amap != g_append_mapping.end())
1059 continue;
1060
1061 // Find consecutive mappings.
1062 auto trav_amap = amap;
1063 int path_length = 2;
1064 do {
1065 for (cmp_amap = g_append_mapping.begin(); cmp_amap != amap_end; ++cmp_amap)
1066 if ( (trav_amap->src_file_id == cmp_amap->dst_file_id)
1067 && (trav_amap->src_track_id == cmp_amap->dst_track_id)) {
1068 trav_amap = cmp_amap;
1069 path_length++;
1070 break;
1071 }
1072 } while (cmp_amap != amap_end);
1073 }
1074 }
1075
1076 void
check_split_support()1077 check_split_support() {
1078 if (!g_cluster_helper->splitting())
1079 return;
1080
1081 for (auto &ptzr_cont : g_packetizers) {
1082 std::string error_message;
1083
1084 auto &packetizer = *ptzr_cont.packetizer;
1085 auto result = packetizer.can_be_split(error_message);
1086
1087 if (CAN_SPLIT_YES == result)
1088 continue;
1089
1090 if (error_message.empty())
1091 error_message = Y("Splitting tracks of this type is not supported.");
1092
1093 mxerror(fmt::format(Y("The track ID {0} from the file '{1}' cannot be split. {2}\n"), packetizer.m_ti.m_id, packetizer.m_ti.m_fname, error_message));
1094 }
1095 }
1096
1097 /** \brief Add chapters from the readers and calculate the max size
1098
1099 The reader do not add their chapters to the global chapter pool.
1100 This has to be done after creating the readers. Only the chapters
1101 of readers that aren't appended are put into the pool right away.
1102 The other chapters are added when a packetizer is appended because
1103 the chapter timestamps have to be adjusted by the length of the file
1104 the packetizer is appended to.
1105 This function also calculates the sum of all chapter sizes so that
1106 enough space can be allocated at the start of each output file.
1107 */
1108 void
calc_max_chapter_size()1109 calc_max_chapter_size() {
1110 // Step 1: Add all chapters from files that are not being appended.
1111 for (auto &file : g_files) {
1112 if (file->appending)
1113 continue;
1114
1115 if (!file->reader->m_chapters)
1116 continue;
1117
1118 if (!g_kax_chapters)
1119 g_kax_chapters = std::make_shared<KaxChapters>();
1120
1121 mtx::chapters::move_by_edition(*g_kax_chapters, *file->reader->m_chapters);
1122 file->reader->m_chapters.reset();
1123 }
1124
1125 // Step 2: Fix the mandatory elements and count the size of all chapters.
1126 s_max_chapter_size = 0;
1127 if (g_kax_chapters) {
1128 mtx::chapters::unify_legacy_and_bcp47_languages_and_countries(*g_kax_chapters);
1129 fix_mandatory_elements(g_kax_chapters.get());
1130 g_kax_chapters->UpdateSize(true);
1131 s_max_chapter_size += g_kax_chapters->ElementSize();
1132 }
1133
1134 for (auto &file : g_files) {
1135 auto chapters = file->reader->m_chapters.get();
1136 if (!chapters)
1137 continue;
1138
1139 fix_mandatory_elements(chapters);
1140 chapters->UpdateSize(true);
1141 s_max_chapter_size += chapters->ElementSize();
1142 }
1143 }
1144
1145 void
calc_attachment_sizes()1146 calc_attachment_sizes() {
1147 // Calculate the size of all attachments for split control.
1148 for (auto &att : g_attachments) {
1149 g_attachment_sizes_first += att->data->get_size();
1150 if (att->to_all_files)
1151 g_attachment_sizes_others += att->data->get_size();
1152 }
1153 }
1154
1155 static void
run_before_file_finished_packetizer_hooks()1156 run_before_file_finished_packetizer_hooks() {
1157 for (auto &ptzr : g_packetizers)
1158 ptzr.packetizer->before_file_finished();
1159 }
1160
1161 static void
run_after_file_created_packetizer_hooks()1162 run_after_file_created_packetizer_hooks() {
1163 for (auto &ptzr : g_packetizers)
1164 ptzr.packetizer->after_file_created();
1165 }
1166
1167 void
create_packetizers()1168 create_packetizers() {
1169 // Create the packetizers.
1170 for (auto &file : g_files) {
1171 file->reader->m_appending = file->appending;
1172 file->reader->create_packetizers();
1173
1174 if (!s_appending_files)
1175 s_appending_files = file->appending;
1176 }
1177 }
1178
1179 void
check_track_id_validity()1180 check_track_id_validity() {
1181 // Check if all track IDs given on the command line are actually
1182 // present.
1183 for (auto &file : g_files) {
1184 file->reader->check_track_ids_and_packetizers();
1185 file->num_unfinished_packetizers = file->reader->m_reader_packetizers.size();
1186 file->old_num_unfinished_packetizers = file->num_unfinished_packetizers;
1187 }
1188 }
1189
1190 static std::string
get_first_chapter_name_in_this_file()1191 get_first_chapter_name_in_this_file() {
1192 if (!s_chapters_in_this_file)
1193 return {};
1194
1195 std::optional<std::pair<int64_t, std::string>> first_chapter;
1196
1197 for (auto const &chapters_child : *s_chapters_in_this_file) {
1198 if (!dynamic_cast<KaxEditionEntry *>(chapters_child))
1199 continue;
1200
1201 for (auto const &edition_child : *static_cast<KaxEditionEntry *>(chapters_child)) {
1202 if (!dynamic_cast<KaxChapterAtom *>(edition_child))
1203 continue;
1204
1205 auto &chapter_atom = *static_cast<KaxChapterAtom *>(edition_child);
1206 auto chapter_timestamp = static_cast<int64_t>(FindChildValue<KaxChapterTimeStart>(chapter_atom));
1207 auto chapter_display = FindChild<KaxChapterDisplay>(chapter_atom);
1208
1209 if ( !chapter_display
1210 || ( first_chapter
1211 && (chapter_timestamp >= first_chapter->first)))
1212 continue;
1213
1214 first_chapter = std::make_pair(chapter_timestamp, to_utf8(FindChildValue<KaxChapterString>(*chapter_display)));
1215 }
1216 }
1217
1218 return first_chapter ? first_chapter->second : ""s;
1219 }
1220
1221 /** \brief Transform the output filename and insert the current file number
1222
1223 Rules and search order:
1224 \arg %d
1225 \arg %[0-9]+d
1226 \arg . ("-%03d" will be inserted before the .)
1227 \arg "-%03d" will be appended
1228 */
1229 std::string
create_output_name()1230 create_output_name() {
1231 std::string s = g_outfile;
1232 // First possibility: %d
1233 int p = s.find("%d");
1234 if (0 <= p) {
1235 s.replace(p, 2, fmt::to_string(g_file_num));
1236
1237 return s;
1238 }
1239
1240 // Now search for something like %02d
1241 auto converted = to_utf8(Q(s).replace(QRegularExpression{"%(\\d+)d"}, "{0:\\1}"));
1242 if (converted != s)
1243 return fmt::format(converted, g_file_num);
1244
1245 // If chapter names are requested, don't force a numeric suffix. The
1246 // chapter name template is replaced when closing the file.
1247 if (s.find("%c") != std::string::npos)
1248 return s;
1249
1250 std::string buffer = fmt::format("-{0:03}", g_file_num);
1251
1252 // See if we can find a '.'.
1253 p = s.rfind(".");
1254 if (0 <= p)
1255 s.insert(p, buffer);
1256 else
1257 s.append(buffer);
1258
1259 return s;
1260 }
1261
1262 void
add_tags_from_cue_chapters()1263 add_tags_from_cue_chapters() {
1264 if (!g_tags_from_cue_chapters || ptzrs_in_header_order.empty())
1265 return;
1266
1267 bool found = false;
1268 int tuid = 0;
1269 size_t i;
1270 for (i = 0; ptzrs_in_header_order.size() > i; ++i)
1271 if (ptzrs_in_header_order[i]->get_track_type() == 'v') {
1272 found = true;
1273 tuid = ptzrs_in_header_order[i]->get_uid();
1274 break;
1275 }
1276
1277 if (!found)
1278 for (i = 0; ptzrs_in_header_order.size() > i; ++i)
1279 if (ptzrs_in_header_order[i]->get_track_type() == 'a') {
1280 found = true;
1281 tuid = ptzrs_in_header_order[i]->get_uid();
1282 break;
1283 }
1284
1285 if (!found)
1286 tuid = ptzrs_in_header_order[0]->get_uid();
1287
1288 for (auto tag : *g_tags_from_cue_chapters)
1289 GetChild<KaxTagTrackUID>(GetChild<KaxTagTargets>(*static_cast<KaxTag *>(tag))).SetValue(tuid);
1290
1291 if (!s_kax_tags)
1292 s_kax_tags.swap(g_tags_from_cue_chapters);
1293
1294 else {
1295 while (g_tags_from_cue_chapters->ListSize() > 0) {
1296 s_kax_tags->PushElement(*(*g_tags_from_cue_chapters)[0]);
1297 g_tags_from_cue_chapters->Remove(0);
1298 }
1299
1300 g_tags_from_cue_chapters.reset();
1301 }
1302 }
1303
1304 /** \brief Render an EbmlVoid element as a placeholder for chapters
1305
1306 Chapters cannot be rendered at the start of the file until the
1307 file's actual length is known during \c finish_file(). However,
1308 the maximum size of chapters is know. So we reserve space at the
1309 beginning of the file for all of the chapters.
1310 */
1311 static void
render_chapter_void_placeholder()1312 render_chapter_void_placeholder() {
1313 if ((0 >= s_max_chapter_size) && (chapter_generation_mode_e::none == g_cluster_helper->get_chapter_generation_mode()))
1314 return;
1315
1316 auto size = s_max_chapter_size + (chapter_generation_mode_e::none == g_cluster_helper->get_chapter_generation_mode() ? 100 : 1000);
1317 s_kax_chapters_void = std::make_unique<EbmlVoid>();
1318 s_kax_chapters_void->SetSize(size);
1319 s_kax_chapters_void->Render(*s_out);
1320 }
1321
1322 /** \brief Prepare tag elements for rendering
1323
1324 Adds missing mandatory elements to the tag structures and sorts
1325 them. Also determines the maximum size needed for rendering the
1326 tags.
1327
1328 WebM compliant files support tags, but only a limited subset. All
1329 unsupported elements will be removed silently.
1330 */
1331 static void
prepare_tags_for_rendering()1332 prepare_tags_for_rendering() {
1333 if (!s_kax_tags)
1334 return;
1335
1336 if (outputting_webm())
1337 mtx::tags::remove_elements_unsupported_by_webm(*s_kax_tags);
1338
1339 fix_mandatory_elements(s_kax_tags.get());
1340 sort_ebml_master(s_kax_tags.get());
1341 if (!s_kax_tags->CheckMandatory())
1342 mxerror(fmt::format(Y("Some tag elements are missing (this error should not have occurred - another similar error should have occurred earlier). {0}\n"), BUGMSG));
1343
1344 s_kax_tags->UpdateSize();
1345 g_tags_size = s_kax_tags->ElementSize();
1346 }
1347
1348 /** \brief Creates the next output file
1349
1350 Creates a new file name depending on the split settings. Opens that
1351 file for writing and calls \c render_headers(). Also renders
1352 attachments if they exist and the chapters if no splitting is used.
1353 */
1354 void
create_next_output_file()1355 create_next_output_file() {
1356 g_doc_type_version_handler.reset(new mtx::doc_type_version_handler_c);
1357
1358 auto s_debug = debugging_option_c{"splitting"};
1359 mxdebug_if(s_debug, fmt::format("splitting: Create next destination file; splitting? {0} discarding? {1}\n", g_cluster_helper->splitting(), g_cluster_helper->discarding()));
1360
1361 auto this_outfile = g_cluster_helper->split_mode_produces_many_files() ? create_output_name() : g_outfile;
1362 g_kax_segment = std::make_unique<KaxSegment>();
1363
1364 // Open the output file.
1365 try {
1366 s_out = !g_cluster_helper->discarding() ? mm_write_buffer_io_c::open(this_outfile, 20 * 1024 * 1024) : mm_io_cptr{ new mm_null_io_c{this_outfile} };
1367 } catch (mtx::mm_io::exception &ex) {
1368 mxerror(fmt::format(Y("The file '{0}' could not be opened for writing: {1}.\n"), this_outfile, ex));
1369 }
1370
1371 if (verbose && !g_cluster_helper->discarding())
1372 mxinfo(fmt::format(Y("The file '{0}' has been opened for writing.\n"), this_outfile));
1373
1374 g_cluster_helper->set_output(s_out.get());
1375
1376 render_headers(s_out.get());
1377 render_attachments(*s_out);
1378 render_chapter_void_placeholder();
1379 add_tags_from_cue_chapters();
1380 prepare_tags_for_rendering();
1381
1382 if (g_cluster_helper->discarding())
1383 return;
1384
1385 s_chapters_in_this_file.reset();
1386
1387 run_after_file_created_packetizer_hooks();
1388
1389 ++g_file_num;
1390 }
1391
1392 void
add_split_points_from_remainig_chapter_numbers()1393 add_split_points_from_remainig_chapter_numbers() {
1394 if (g_splitting_by_chapter_numbers.empty() && !g_splitting_by_all_chapters)
1395 return;
1396
1397 if (!g_kax_chapters)
1398 return;
1399
1400 std::vector<int64_t> chapter_start_timestamps;
1401 for (auto element : *g_kax_chapters) {
1402 auto edition = dynamic_cast<KaxEditionEntry *>(element);
1403 if (!edition)
1404 continue;
1405
1406 for (auto sub_element : *edition) {
1407 auto atom = dynamic_cast<KaxChapterAtom *>(sub_element);
1408 if (!atom)
1409 continue;
1410
1411 chapter_start_timestamps.push_back(FindChildValue<KaxChapterTimeStart, uint64_t>(atom, 0));
1412 }
1413 }
1414
1415 std::sort(chapter_start_timestamps.begin(), chapter_start_timestamps.end());
1416
1417 std::vector<std::pair<split_point_c, unsigned int>> new_split_points;
1418
1419 for (auto current_number = 1u; current_number <= chapter_start_timestamps.size(); ++current_number) {
1420 auto use_chapter = g_splitting_by_all_chapters || (g_splitting_by_chapter_numbers[current_number] == 1);
1421 if (!use_chapter)
1422 continue;
1423
1424 if (g_splitting_by_chapter_numbers[current_number] != 2) {
1425 auto split_after = chapter_start_timestamps[current_number - 1];
1426 if (split_after)
1427 new_split_points.emplace_back(std::make_pair(split_point_c{split_after, split_point_c::timestamp, true}, current_number));
1428 }
1429
1430 g_splitting_by_chapter_numbers[current_number] = 2;
1431 }
1432
1433 for (auto &split_point : new_split_points) {
1434 mxdebug_if(s_debug_splitting_chapters, fmt::format("Adding split point for chapter number {} at {}\n", split_point.second, split_point.first.m_point));
1435 g_cluster_helper->add_split_point(split_point.first);
1436 }
1437 }
1438
1439 static void
add_chapters_for_current_part()1440 add_chapters_for_current_part() {
1441 mxdebug_if(s_debug_splitting_chapters, fmt::format("Adding chapters. have_global? {0} splitting? {1}\n", !!g_kax_chapters, g_cluster_helper->splitting()));
1442
1443 if (!g_cluster_helper->splitting()) {
1444 s_chapters_in_this_file = clone(g_kax_chapters);
1445 mtx::chapters::merge_entries(*s_chapters_in_this_file);
1446 if (mtx::bcp47::language_c::is_disabled())
1447 remove_ietf_language_elements(*s_chapters_in_this_file);
1448 sort_ebml_master(s_chapters_in_this_file.get());
1449 return;
1450 }
1451
1452 int64_t start = g_cluster_helper->get_first_timestamp_in_part();
1453 int64_t end = g_cluster_helper->get_max_timestamp_in_file(); // start + g_cluster_helper->get_duration();
1454 int64_t offset = g_no_linking ? g_cluster_helper->get_first_timestamp_in_file() + g_cluster_helper->get_discarded_duration() : 0;
1455
1456 auto chapters_here = clone(g_kax_chapters);
1457 bool have_chapters_in_timeframe = mtx::chapters::select_in_timeframe(chapters_here.get(), start, end, offset);
1458
1459 mxdebug_if(s_debug_splitting_chapters, fmt::format("offset {0} start {1} end {2} have chapters in timeframe? {3} chapters in this file? {4}\n", offset, start, end, have_chapters_in_timeframe, !!s_chapters_in_this_file));
1460
1461 if (!have_chapters_in_timeframe)
1462 return;
1463
1464 if (!s_chapters_in_this_file)
1465 s_chapters_in_this_file = chapters_here;
1466 else
1467 mtx::chapters::move_by_edition(*s_chapters_in_this_file, *chapters_here);
1468
1469 mtx::chapters::merge_entries(*s_chapters_in_this_file);
1470 sort_ebml_master(s_chapters_in_this_file.get());
1471 }
1472
1473 void
add_chapter_atom(timestamp_c const & start_timestamp,std::string const & name,mtx::bcp47::language_c const & language)1474 add_chapter_atom(timestamp_c const &start_timestamp,
1475 std::string const &name,
1476 mtx::bcp47::language_c const &language) {
1477 s_additional_chapter_atoms.emplace_back(start_timestamp, name, language);
1478 }
1479
1480 static void
prepare_additional_chapter_atoms_for_rendering()1481 prepare_additional_chapter_atoms_for_rendering() {
1482 if (s_additional_chapter_atoms.empty())
1483 return;
1484
1485 if (!s_chapters_in_this_file)
1486 s_chapters_in_this_file = std::make_shared<KaxChapters>();
1487
1488 static timestamp_c s_first_file_minimum_timestamp;
1489
1490 if (!s_first_file_minimum_timestamp.valid())
1491 s_first_file_minimum_timestamp = timestamp_c::ns(g_no_linking ? g_cluster_helper->get_first_timestamp_in_file() + g_cluster_helper->get_discarded_duration() : 0);
1492
1493 auto offset = timestamp_c::ns(g_no_linking ? g_cluster_helper->get_first_timestamp_in_file() + g_cluster_helper->get_discarded_duration() : 0) - s_first_file_minimum_timestamp;
1494 auto &edition = GetChild<KaxEditionEntry>(*s_chapters_in_this_file);
1495
1496 if (!FindChild<KaxEditionUID>(edition))
1497 GetChild<KaxEditionUID>(edition).SetValue(create_unique_number(UNIQUE_EDITION_IDS));
1498
1499 for (auto const &[ch_timestamp, ch_name, ch_language] : s_additional_chapter_atoms) {
1500 auto atom = cons<KaxChapterAtom>(new KaxChapterUID, create_unique_number(UNIQUE_CHAPTER_IDS),
1501 new KaxChapterTimeStart, (ch_timestamp - offset).to_ns());
1502
1503 if (!ch_name.empty()) {
1504 auto &display = GetChild<KaxChapterDisplay>(*atom);
1505
1506 GetChild<KaxChapterString>(display).SetValueUTF8(ch_name);
1507
1508 if (ch_language.is_valid()) {
1509 GetChild<KaxChapterLanguage>(display).SetValue(ch_language.get_iso639_2_alpha_3_code_or("und"));
1510 if (!mtx::bcp47::language_c::is_disabled())
1511 GetChild<KaxChapLanguageIETF>(display).SetValue(ch_language.format());
1512 else
1513 DeleteChildren<KaxChapLanguageIETF>(display);
1514 }
1515 }
1516
1517 edition.PushElement(*atom);
1518 }
1519
1520 s_additional_chapter_atoms.clear();
1521 }
1522
1523 static void
render_chapters()1524 render_chapters() {
1525 prepare_additional_chapter_atoms_for_rendering();
1526
1527 if (!s_chapters_in_this_file) {
1528 s_kax_chapters_void.reset();
1529 return;
1530 }
1531
1532 fix_mandatory_elements(s_chapters_in_this_file.get());
1533 mtx::chapters::fix_country_codes(*s_chapters_in_this_file);
1534
1535 if (outputting_webm())
1536 mtx::chapters::remove_elements_unsupported_by_webm(*s_chapters_in_this_file);
1537
1538 auto replaced = false;
1539 if (s_kax_chapters_void)
1540 replaced = s_kax_chapters_void->ReplaceWith(*s_chapters_in_this_file, *s_out, true, true);
1541
1542 if (!replaced) {
1543 s_out->setFilePointer(0, seek_end);
1544 g_doc_type_version_handler->render(*s_chapters_in_this_file, *s_out);
1545 }
1546
1547 s_kax_chapters_void.reset();
1548 }
1549
1550 static KaxTags *
set_track_statistics_tags(KaxTags * tags)1551 set_track_statistics_tags(KaxTags *tags) {
1552 if (g_no_track_statistics_tags || outputting_webm())
1553 return tags;
1554
1555 if (!tags)
1556 tags = new KaxTags;
1557
1558 g_cluster_helper->create_tags_for_track_statistics(*tags, s_writing_app, s_writing_date);
1559
1560 return tags;
1561 }
1562
1563 static void
insert_chapter_name_in_output_file_name(std::filesystem::path const & original_file_name,std::string const & chapter_name)1564 insert_chapter_name_in_output_file_name(std::filesystem::path const &original_file_name,
1565 std::string const &chapter_name) {
1566 #if defined(SYS_WINDOWS)
1567 static QRegularExpression s_invalid_char_re{R"([\\/:<>"|?*]+)"};
1568 #else
1569 static QRegularExpression s_invalid_char_re{"/+"};
1570 #endif
1571
1572 // When splitting replace %c in file names with current chapter name.
1573 if (!g_cluster_helper->split_mode_produces_many_files())
1574 return;
1575
1576 // auto chapter_name = get_current_chapter_name();
1577 auto cleaned_chapter_name = Q(chapter_name).replace(s_invalid_char_re, "-");
1578 auto new_file_name = original_file_name.parent_path() / mtx::fs::to_path(Q(original_file_name.filename()).replace(QRegularExpression{"%c"}, cleaned_chapter_name));
1579
1580 mxdebug_if(s_debug_splitting_chapters, fmt::format("insert_chapter_name_in_output_file_name: cleaned name {0} old {1} new {2}\n", to_utf8(cleaned_chapter_name), original_file_name.u8string(), new_file_name.u8string()));
1581
1582 if (original_file_name == new_file_name)
1583 return;
1584
1585 try {
1586 std::filesystem::rename(original_file_name, new_file_name);
1587 mxinfo(fmt::format(Y("The file '{0}' was renamed to '{1}'.\n"), original_file_name.u8string(), new_file_name.u8string()));
1588 } catch (std::filesystem::filesystem_error &) {
1589 mxerror(fmt::format(Y("The file '{0}' could not be renamed to '{1}'.\n"), original_file_name.u8string(), new_file_name.u8string()));
1590 }
1591 }
1592
1593 /** \brief Finishes and closes the current file
1594
1595 Renders the data that is generated during the muxing run. The cues
1596 and meta seek information are rendered at the end. If splitting is
1597 active the chapters are stripped to those that actually lie in this
1598 file and rendered at the front. The segment duration and the
1599 segment size are set to their actual values.
1600 */
1601 void
1602 finish_file(bool last_file,
1603 bool create_new_file,
1604 bool previously_discarding) {
1605 if (g_kax_chapters && !previously_discarding)
1606 add_chapters_for_current_part();
1607
1608 if (!last_file && !create_new_file)
1609 return;
1610
1611 run_before_file_finished_packetizer_hooks();
1612
1613 bool do_output = verbose && !dynamic_cast<mm_null_io_c *>(s_out.get());
1614 if (do_output)
1615 mxinfo("\n");
1616
1617 // Render the track headers a second time if the user has requested that.
1618 if (mtx::hacks::is_engaged(mtx::hacks::WRITE_HEADERS_TWICE)) {
1619 auto second_tracks = clone(g_kax_tracks);
1620 g_doc_type_version_handler->render(*second_tracks, *s_out);
1621 g_kax_sh_main->IndexThis(*second_tracks, *g_kax_segment);
1622 }
1623
1624 // Render the cues.
1625 if (g_write_cues && g_cue_writing_requested) {
1626 if (do_output)
1627 mxinfo(Y("The cue entries (the index) are being written...\n"));
1628 cues_c::get().write(*s_out, *g_kax_sh_main);
1629 }
1630
1631 // Now re-render the s_kax_duration and fill in the biggest timestamp
1632 // as the file's duration.
1633 s_out->save_pos(s_kax_duration->GetElementPosition());
1634 s_kax_duration->SetValue(calculate_file_duration());
1635 g_doc_type_version_handler->render(*s_kax_duration, *s_out);
1636
1637 // If splitting is active and this is the last part then handle the
1638 // 'next segment UID'. If it was given on the command line then set it here.
1639 // Otherwise remove an existing one (e.g. from file linking during
1640 // splitting).
1641
1642 s_kax_infos->UpdateSize(true);
1643 int64_t info_size = s_kax_infos->ElementSize();
1644 int changed = 0;
1645
1646 if (last_file && g_seguid_link_next) {
1647 GetChild<KaxNextUID>(*s_kax_infos).CopyBuffer(g_seguid_link_next->data(), 128 / 8);
1648 changed = 1;
1649
1650 } else if (last_file || g_no_linking) {
1651 size_t i;
1652 for (i = 0; s_kax_infos->ListSize() > i; ++i)
1653 if (Is<KaxNextUID>((*s_kax_infos)[i])) {
1654 delete (*s_kax_infos)[i];
1655 s_kax_infos->Remove(i);
1656 changed = 2;
1657 break;
1658 }
1659 }
1660
1661 if (0 != changed) {
1662 s_out->setFilePointer(s_kax_infos->GetElementPosition());
1663 s_kax_infos->UpdateSize(true);
1664 info_size -= s_kax_infos->ElementSize();
1665 g_doc_type_version_handler->render(*s_kax_infos, *s_out, true);
1666 if (2 == changed) {
1667 if (2 < info_size) {
1668 EbmlVoid void_after_infos;
1669 void_after_infos.SetSize(info_size);
1670 void_after_infos.UpdateSize();
1671 void_after_infos.SetSize(info_size - void_after_infos.HeadSize());
1672 void_after_infos.Render(*s_out);
1673
1674 } else if (0 < info_size) {
1675 char zero[2] = {0, 0};
1676 s_out->write(zero, info_size);
1677 }
1678 }
1679 }
1680 s_out->restore_pos();
1681
1682 // Render the segment info a second time if the user has requested that.
1683 if (mtx::hacks::is_engaged(mtx::hacks::WRITE_HEADERS_TWICE)) {
1684 g_doc_type_version_handler->render(*s_kax_infos, *s_out);
1685 g_kax_sh_main->IndexThis(*s_kax_infos, *g_kax_segment);
1686 }
1687
1688 render_chapters();
1689
1690 // Render the meta seek information with the cues
1691 if (g_write_meta_seek_for_clusters && (g_kax_sh_cues->ListSize() > 0) && !mtx::hacks::is_engaged(mtx::hacks::NO_META_SEEK)) {
1692 g_kax_sh_cues->UpdateSize();
1693 g_doc_type_version_handler->render(*g_kax_sh_cues, *s_out);
1694 g_kax_sh_main->IndexThis(*g_kax_sh_cues, *g_kax_segment);
1695 }
1696
1697 // Set the tags for track statistics and render all tags for this
1698 // file.
1699 KaxTags *tags_here = nullptr;
1700 if (s_kax_tags) {
1701 if (!s_chapters_in_this_file) {
1702 KaxChapters temp_chapters;
1703 tags_here = mtx::tags::select_for_chapters(*s_kax_tags, temp_chapters);
1704 } else
1705 tags_here = mtx::tags::select_for_chapters(*s_kax_tags, *s_chapters_in_this_file);
1706 }
1707
1708 tags_here = set_track_statistics_tags(tags_here);
1709
1710 if (tags_here && (0 == mtx::tags::count_simple(*tags_here))) {
1711 delete tags_here;
1712 tags_here = nullptr;
1713 }
1714
1715 if (tags_here) {
1716 fix_mandatory_elements(tags_here);
1717 if (mtx::bcp47::language_c::is_disabled())
1718 remove_ietf_language_elements(*tags_here);
1719 remove_mandatory_elements_set_to_their_default(*tags_here);
1720 tags_here->UpdateSize();
1721 g_doc_type_version_handler->render(*tags_here, *s_out, true);
1722
1723 g_kax_sh_main->IndexThis(*tags_here, *g_kax_segment);
1724 delete tags_here;
1725 }
1726
1727 auto first_chapter_name = get_first_chapter_name_in_this_file();
1728
1729 if (s_chapters_in_this_file) {
1730 if (!mtx::hacks::is_engaged(mtx::hacks::NO_CHAPTERS_IN_META_SEEK))
1731 g_kax_sh_main->IndexThis(*s_chapters_in_this_file, *g_kax_segment);
1732 s_chapters_in_this_file.reset();
1733 }
1734
1735 if (s_kax_as) {
1736 g_kax_sh_main->IndexThis(*s_kax_as, *g_kax_segment);
1737 s_kax_as.reset();
1738 }
1739
1740 if ((g_kax_sh_main->ListSize() > 0) && !mtx::hacks::is_engaged(mtx::hacks::NO_META_SEEK)) {
1741 g_kax_sh_main->UpdateSize();
1742 if (s_kax_sh_void->ReplaceWith(*g_kax_sh_main, *s_out, true) == INVALID_FILEPOS_T)
1743 mxwarn(fmt::format(Y("This should REALLY not have happened. The space reserved for the first meta seek element was too small. Size needed: {0}. {1}\n"),
1744 g_kax_sh_main->ElementSize(), BUGMSG));
1745 }
1746
1747 // Set the correct size for the segment.
1748 int64_t final_file_size = s_out->getFilePointer();
1749 if (g_kax_segment->ForceSize(final_file_size - g_kax_segment->GetElementPosition() - g_kax_segment->HeadSize()))
1750 g_kax_segment->OverwriteHead(*s_out);
1751
1752 update_ebml_head();
1753
1754 auto original_file_name = mtx::fs::to_path(s_out->get_file_name());
1755
1756 s_out.reset();
1757
1758 g_kax_segment.reset();
1759 s_kax_sh_void.reset();
1760 g_kax_sh_main.reset();
1761 s_void_after_track_headers.reset();
1762 g_kax_sh_cues.reset();
1763 s_head.reset();
1764 g_doc_type_version_handler.reset();
1765
1766 insert_chapter_name_in_output_file_name(original_file_name, first_chapter_name);
1767 }
1768
1769 void
1770 force_close_output_file() {
1771 if (!s_out)
1772 return;
1773
1774 auto wb_out = dynamic_cast<mm_write_buffer_io_c *>(s_out.get());
1775 if (wb_out)
1776 wb_out->discard_buffer();
1777
1778 s_out.reset();
1779 }
1780
1781 static void establish_deferred_connections(filelist_t &file);
1782
1783 static void
1784 append_chapters_for_track(filelist_t &src_file,
1785 int64_t timestamp_adjustment) {
1786 // Append some more chapters and adjust their timestamps by the highest
1787 // timestamp seen in the previous file/the track that we've been searching
1788 // for above.
1789 auto chapters = src_file.reader->m_chapters.get();
1790 if (!chapters)
1791 return;
1792
1793 if (!g_kax_chapters)
1794 g_kax_chapters = std::make_unique<KaxChapters>();
1795 else
1796 mtx::chapters::align_uids(*g_kax_chapters, *chapters);
1797
1798 mtx::chapters::adjust_timestamps(*chapters, timestamp_adjustment);
1799 mtx::chapters::move_by_edition(*g_kax_chapters, *chapters);
1800 src_file.reader->m_chapters.reset();
1801 }
1802
1803 /** \brief Append a packetizer to another one
1804
1805 Appends a packetizer to another one. Finds the packetizer that is
1806 to replace the current one, informs the user about the action,
1807 connects the two packetizers and changes the structs to reflect
1808 the switch.
1809
1810 \param ptzr The packetizer that is to be replaced.
1811 \param amap The append specification the replacement is based upon.
1812 */
1813 void
1814 append_track(packetizer_t &ptzr,
1815 const append_spec_t &amap,
1816 filelist_t *deferred_file = nullptr) {
1817 auto &src_file = *g_files[amap.src_file_id];
1818 auto &dst_file = *g_files[amap.dst_file_id];
1819
1820 if (deferred_file)
1821 src_file.deferred_max_timestamp_seen = deferred_file->reader->m_max_timestamp_seen;
1822
1823 // Find the generic_packetizer_c that we will be appending to the one
1824 // stored in ptzr.
1825 auto gptzr = std::find_if(src_file.reader->m_reader_packetizers.begin(), src_file.reader->m_reader_packetizers.end(), [&amap](auto p) -> bool {
1826 return amap.src_track_id == static_cast<size_t>(p->m_ti.m_id);
1827 });
1828
1829 if (src_file.reader->m_reader_packetizers.end() == gptzr)
1830 mxerror(fmt::format(Y("Could not find gptzr when appending. {0}\n"), BUGMSG));
1831
1832 // If we're dealing with a subtitle track or if the appending file contains
1833 // chapters then we have to suck the previous file dry. See below for the
1834 // reason (short version: we need all max_timestamp_seen values).
1835 if ( !dst_file.done
1836 && ( (APPEND_MODE_FILE_BASED == g_append_mode)
1837 || ((*gptzr)->get_track_type() == track_subtitle)
1838 || src_file.reader->m_chapters)) {
1839 dst_file.reader->read_all();
1840 dst_file.num_unfinished_packetizers = 0;
1841 dst_file.old_num_unfinished_packetizers = 0;
1842 dst_file.done = true;
1843 establish_deferred_connections(dst_file);
1844 }
1845
1846 if ( !ptzr.deferred
1847 && (track_subtitle == (*gptzr)->get_track_type())
1848 && ( 0 == dst_file.reader->m_num_video_tracks)
1849 && ( -1 == src_file.deferred_max_timestamp_seen)
1850 && g_video_packetizer) {
1851
1852 for (auto &file : g_files) {
1853 if (file->done)
1854 continue;
1855
1856 auto vptzr = std::find_if(file->reader->m_reader_packetizers.begin(), file->reader->m_reader_packetizers.end(), [](auto p) { return p->get_track_type() == track_video; });
1857 if (vptzr == file->reader->m_reader_packetizers.end())
1858 continue;
1859
1860 filelist_t::deferred_connection_t new_def_con;
1861
1862 ptzr.deferred = true;
1863 new_def_con.amap = amap;
1864 new_def_con.ptzr = &ptzr;
1865 file->deferred_connections.push_back(new_def_con);
1866
1867 return;
1868 }
1869 }
1870
1871 if (s_debug_appending) {
1872 mxdebug(fmt::format("appending: reader m_max_timestamp_seen {0} and ptzr to append is {1} ptzr appended to is {2} src_file.appending {3} src_file.appended_to {4} dst_file.appending {5} dst_file.appended_to {6}\n",
1873 dst_file.reader->m_max_timestamp_seen, static_cast<void *>(gptzr->get()), static_cast<void *>(ptzr.packetizer), src_file.appending, src_file.appended_to, dst_file.appending, dst_file.appended_to));
1874 for (auto &rep_ptzr : dst_file.reader->m_reader_packetizers)
1875 mxdebug(fmt::format(" ptzr @ {0} connected_to {1} max_timestamp_seen {2}\n", static_cast<void *>(rep_ptzr.get()), rep_ptzr->m_connected_to, rep_ptzr->m_max_timestamp_seen));
1876 }
1877
1878 // In rare cases (e.g. empty tracks) a whole file could be skipped
1879 // without having gotten a single packet through to the timecoding
1880 // code. Therefore the reader's m_max_timestamp_seen field would
1881 // still be 0. Therefore we must ensure that each packetizer from a
1882 // file we're trying to use m_max_timestamp_seen from has already
1883 // been connected fully. The very first file in a chain (meaning
1884 // files that are not in "appending to other file mode",
1885 // filelist_t.appending == false) would be OK as well.
1886 if (dst_file.appending) {
1887 std::list<generic_packetizer_c *> not_connected_ptzrs;
1888 for (auto &check_ptzr : dst_file.reader->m_reader_packetizers)
1889 if ((check_ptzr.get() != ptzr.packetizer) && (2 != check_ptzr->m_connected_to))
1890 not_connected_ptzrs.push_back(check_ptzr.get());
1891
1892 if (s_debug_appending) {
1893 std::string result;
1894 for (auto &out_ptzr : not_connected_ptzrs)
1895 result += fmt::format(" {0}", static_cast<void *>(out_ptzr));
1896
1897 mxdebug(fmt::format("appending: check for connection on dst file's packetizers; these are not connected: {0}\n", result));
1898 }
1899
1900 if (!not_connected_ptzrs.empty())
1901 return;
1902 }
1903
1904 mxinfo(fmt::format(Y("Appending track {0} from file no. {1} ('{2}') to track {3} from file no. {4} ('{5}').\n"),
1905 (*gptzr)->m_ti.m_id, amap.src_file_id, (*gptzr)->m_ti.m_fname, ptzr.packetizer->m_ti.m_id, amap.dst_file_id, ptzr.packetizer->m_ti.m_fname));
1906
1907 // Also fix the ptzr structure and reset the ptzr's state to "I want more".
1908 generic_packetizer_c *old_packetizer = ptzr.packetizer;
1909 ptzr.packetizer = (*gptzr).get();
1910 ptzr.file = amap.src_file_id;
1911 ptzr.status = FILE_STATUS_MOREDATA;
1912
1913 // Fix the globally stored video packetizer reference so that
1914 // decisions based on a packet's source such as when to render a new
1915 // cluster continue working.
1916 if (old_packetizer == g_video_packetizer)
1917 g_video_packetizer = ptzr.packetizer;
1918
1919 // If we're dealing with a subtitle track or if the appending file contains
1920 // chapters then we have to do some magic. During splitting timestamps are
1921 // offset by a certain amount. This amount is NOT the duration of the
1922 // previous file! That's why we cannot use
1923 // dst_file.reader->max_timestamp_seen. Instead we have to find the first
1924 // packet in the appending file because its original timestamp during the
1925 // split phase was the offset. If we have that we can find the corresponding
1926 // packetizer and use its max_timestamp_seen.
1927 //
1928 // All this only applies to gapless tracks. Good luck with other files.
1929 // Some files types also allow access to arbitrary tracks and packets
1930 // (e.g. AVI and Quicktime). Those files will not work correctly for this.
1931 // But then again I don't expect that people will try to concatenate such
1932 // files if they've been split before.
1933 int64_t timestamp_adjustment = dst_file.reader->m_max_timestamp_seen;
1934 if ((APPEND_MODE_FILE_BASED == g_append_mode)
1935 && ( (track_subtitle != ptzr.packetizer->get_track_type())
1936 || !dst_file.reader->is_simple_subtitle_container()))
1937 // Intentionally left empty.
1938 ;
1939
1940 else if (ptzr.deferred && deferred_file)
1941 timestamp_adjustment = src_file.deferred_max_timestamp_seen;
1942
1943 else if ( (track_subtitle == ptzr.packetizer->get_track_type())
1944 && (-1 < src_file.deferred_max_timestamp_seen))
1945 timestamp_adjustment = src_file.deferred_max_timestamp_seen;
1946
1947 else if ( (ptzr.packetizer->get_track_type() == track_subtitle)
1948 || (src_file.reader->m_chapters)) {
1949 if (!src_file.reader->m_ptzr_first_packet)
1950 ptzr.status = ptzr.packetizer->read(false);
1951
1952 if (src_file.reader->m_ptzr_first_packet) {
1953 auto cmp_amap = std::find_if(g_append_mapping.begin(), g_append_mapping.end(), [&amap, &src_file](append_spec_t const &m) {
1954 return (m.src_file_id == amap.src_file_id)
1955 && (m.src_track_id == static_cast<size_t>(src_file.reader->m_ptzr_first_packet->m_ti.m_id))
1956 && (m.dst_file_id == amap.dst_file_id);
1957 });
1958
1959 if (g_append_mapping.end() != cmp_amap) {
1960 auto gptzr_adjustment = dst_file.reader->find_packetizer_by_id(cmp_amap->dst_track_id);
1961 if (gptzr_adjustment)
1962 timestamp_adjustment = gptzr_adjustment->m_max_timestamp_seen;
1963 }
1964 }
1965 }
1966
1967 if ((APPEND_MODE_FILE_BASED == g_append_mode) || (ptzr.packetizer->get_track_type() == track_subtitle)) {
1968 mxdebug_if(s_debug_appending, fmt::format("appending: new timestamp_adjustment for append_mode == FILE_BASED or subtitle track: {0} for {1}\n", mtx::string::format_timestamp(timestamp_adjustment), ptzr.packetizer->m_ti.m_id));
1969 // The actual connection.
1970 ptzr.packetizer->connect(old_packetizer, timestamp_adjustment);
1971
1972 } else {
1973 mxdebug_if(s_debug_appending, fmt::format("appending: new timestamp_adjustment for append_mode == TRACK_BASED and NON subtitle track: {0} for {1}\n", mtx::string::format_timestamp(timestamp_adjustment), ptzr.packetizer->m_ti.m_id));
1974 // The actual connection.
1975 ptzr.packetizer->connect(old_packetizer);
1976 }
1977
1978 append_chapters_for_track(src_file, timestamp_adjustment);
1979
1980 ptzr.deferred = false;
1981 }
1982
1983 /** \brief Decide if packetizers have to be appended
1984
1985 Iterates over all current packetizers and decides if the next one
1986 should be appended now. This is the case if the current packetizer
1987 has finished and there is another packetizer waiting to be appended.
1988
1989 \return true if at least one track has been appended to another one.
1990 */
1991 bool
1992 append_tracks_maybe() {
1993 bool appended_a_track = false;
1994
1995 for (auto &ptzr : g_packetizers) {
1996 if (ptzr.deferred)
1997 continue;
1998
1999 if (!g_files[ptzr.orig_file]->appended_to)
2000 continue;
2001
2002 if (FILE_STATUS_DONE_AND_DRY != ptzr.status)
2003 continue;
2004
2005 append_spec_t *amap = nullptr;
2006 for (auto &amap_idx : g_append_mapping)
2007 if ((amap_idx.dst_file_id == static_cast<size_t>(ptzr.file)) && (amap_idx.dst_track_id == static_cast<size_t>(ptzr.packetizer->m_ti.m_id))) {
2008 amap = &amap_idx;
2009 break;
2010 }
2011
2012 if (!amap)
2013 continue;
2014
2015 append_track(ptzr, *amap);
2016 appended_a_track = true;
2017 }
2018
2019 return appended_a_track;
2020 }
2021
2022 /** \brief Establish deferred packetizer connections
2023
2024 In some cases (e.g. subtitle only files being appended) establishing the
2025 connections is deferred until a file containing a video track has
2026 finished, too. This is necessary because the subtitle files themselves
2027 are usually "shorter" than the movie they belong to. This is not the
2028 case if the subs are already embedded with a movie in a single file.
2029
2030 This function iterates over all deferred connections and establishes
2031 them.
2032
2033 \param file All connections that have been deferred until this file has
2034 finished are established.
2035 */
2036 static void
2037 establish_deferred_connections(filelist_t &file) {
2038 auto def_cons = file.deferred_connections;
2039 file.deferred_connections.clear();
2040
2041 for (auto &def_con : def_cons)
2042 append_track(*def_con.ptzr, def_con.amap, &file);
2043
2044 // \todo Select a new file that the subs will defer to.
2045 }
2046
2047 static void
2048 check_and_handle_end_of_input_after_pulling(packetizer_t &ptzr) {
2049 if (!ptzr.pack && (FILE_STATUS_DONE == ptzr.status))
2050 ptzr.status = FILE_STATUS_DONE_AND_DRY;
2051
2052 // Has this packetizer changed its status from "data available" to
2053 // "file done" during this loop? If so then decrease the number of
2054 // unfinished packetizers in the corresponding file structure.
2055 if ( (FILE_STATUS_DONE_AND_DRY != ptzr.status)
2056 || (ptzr.old_status == ptzr.status))
2057 return;
2058
2059 auto &file = *g_files[ptzr.file];
2060 file.num_unfinished_packetizers--;
2061
2062 // If all packetizers for a file have finished then establish the
2063 // deferred connections.
2064 if ((0 >= file.num_unfinished_packetizers) && (0 < file.old_num_unfinished_packetizers)) {
2065 establish_deferred_connections(file);
2066 file.done = true;
2067 }
2068 file.old_num_unfinished_packetizers = file.num_unfinished_packetizers;
2069 }
2070
2071 static bool
2072 force_pull_packetizers_of_fully_held_files() {
2073 std::unordered_map<generic_reader_c *, bool> fully_held_files;
2074
2075 for (auto &ptzr : g_packetizers) {
2076 auto reader = ptzr.packetizer->m_reader;
2077 auto pos = fully_held_files.find(reader);
2078
2079 if (pos == fully_held_files.end())
2080 fully_held_files[reader] = true;
2081
2082 if (FILE_STATUS_HOLDING != ptzr.status)
2083 fully_held_files[reader] = false;
2084 }
2085
2086 auto force_pulled = false;
2087 for (auto &ptzr : g_packetizers)
2088 if (fully_held_files[ptzr.packetizer->m_reader] && !ptzr.packetizer->packet_available()) {
2089 ptzr.old_status = ptzr.status;
2090 ptzr.status = ptzr.packetizer->read(true);
2091 force_pulled = true;
2092
2093 if (!ptzr.pack)
2094 ptzr.pack = ptzr.packetizer->get_packet();
2095
2096 check_and_handle_end_of_input_after_pulling(ptzr);
2097 }
2098
2099 return force_pulled;
2100 }
2101
2102 static void
2103 pull_packetizers_for_packets() {
2104 for (auto &ptzr : g_packetizers) {
2105 if (FILE_STATUS_HOLDING == ptzr.status)
2106 ptzr.status = FILE_STATUS_MOREDATA;
2107
2108 ptzr.old_status = ptzr.status;
2109
2110 while ( !ptzr.pack
2111 && (FILE_STATUS_MOREDATA == ptzr.status)
2112 && !ptzr.packetizer->packet_available())
2113 ptzr.status = ptzr.packetizer->read(false);
2114
2115 if ( (FILE_STATUS_MOREDATA != ptzr.status)
2116 && (FILE_STATUS_MOREDATA == ptzr.old_status))
2117 ptzr.packetizer->force_duration_on_last_packet();
2118
2119 if (!ptzr.pack)
2120 ptzr.pack = ptzr.packetizer->get_packet();
2121
2122 check_and_handle_end_of_input_after_pulling(ptzr);
2123 }
2124 }
2125
2126 static packetizer_t *
2127 select_winning_packetizer() {
2128 packetizer_t *winner = nullptr;
2129
2130 for (auto &ptzr : g_packetizers) {
2131 if (!ptzr.pack)
2132 continue;
2133
2134 if (!winner || !winner->pack)
2135 winner = &ptzr;
2136
2137 else if (ptzr.pack && (ptzr.pack->output_order_timestamp < winner->pack->output_order_timestamp))
2138 winner = &ptzr;
2139 }
2140
2141 return winner;
2142 }
2143
2144 static void
2145 discard_queued_packets() {
2146 for (auto &ptzr : g_packetizers)
2147 ptzr.packetizer->discard_queued_packets();
2148
2149 g_cluster_helper->discard_queued_packets();
2150 }
2151
2152 /** \brief Request packets and handle the next one
2153
2154 Requests packets from each packetizer, selects the packet with the
2155 lowest timestamp and hands it over to the cluster helper for
2156 rendering. Also displays the progress.
2157 */
2158 void
2159 main_loop() {
2160 // Let's go!
2161 while (1) {
2162 // Step 1: Make sure a packet is available for each output
2163 // as long we haven't already processed the last one.
2164 pull_packetizers_for_packets();
2165 auto force_pulled = force_pull_packetizers_of_fully_held_files();
2166
2167 // Step 2: Pick the packet with the lowest timestamp and
2168 // stuff it into the Matroska file.
2169 auto winner = select_winning_packetizer();
2170
2171 // Append the next track if appending is wanted.
2172 bool appended_a_track = s_appending_files && append_tracks_maybe();
2173
2174 if (winner && winner->pack) {
2175 packet_cptr pack = winner->pack;
2176
2177 // Step 3: Add the winning packet to a cluster. Full clusters will be
2178 // rendered automatically.
2179 g_cluster_helper->add_packet(pack);
2180
2181 winner->pack.reset();
2182
2183 add_split_points_from_remainig_chapter_numbers();
2184
2185 // If splitting by parts is active and the last part has been
2186 // processed fully then we can finish up.
2187 if (g_cluster_helper->is_splitting_and_processed_fully()) {
2188 discard_queued_packets();
2189 break;
2190 }
2191
2192 // display some progress information
2193 if (1 <= verbose)
2194 display_progress();
2195
2196 } else if (!appended_a_track && !force_pulled) // exit if there are no more packets
2197 break;
2198 }
2199
2200 // Render all remaining packets (if there are any).
2201 if (g_cluster_helper && (0 < g_cluster_helper->get_packet_count()))
2202 g_cluster_helper->render();
2203
2204 if (1 <= verbose)
2205 display_progress(true);
2206 }
2207
2208 /** \brief Deletes the file readers and other associated objects
2209 */
2210 static void
2211 destroy_readers() {
2212 g_files.clear();
2213 g_packetizers.clear();
2214 }
2215
2216 /** \brief Uninitialization
2217
2218 Frees memory and shuts down the readers.
2219 */
2220 void
2221 cleanup() {
2222 if (s_out) {
2223 // If cleanup was called as a result of an exception during
2224 // writing due to the file system being full, the destructor would
2225 // normally write remaining buffered before closing the file. This
2226 // would lead to another exception and another error
2227 // message. However, the regular paths all close the file
2228 // manually. Therefore any buffered content remaining at this
2229 // point can only be due to an error having occurred. The content
2230 // can therefore be discarded.
2231 dynamic_cast<mm_write_buffer_io_c &>(*s_out).discard_buffer();
2232 s_out.reset();
2233 }
2234
2235 g_cluster_helper.reset();
2236
2237 destroy_readers();
2238 g_attachments.clear();
2239
2240 s_kax_tags.reset();
2241 g_tags_from_cue_chapters.reset();
2242 g_kax_chapters.reset();
2243 s_kax_as.reset();
2244 g_kax_info_chap.reset();
2245 g_forced_seguids.clear();
2246 g_kax_tracks.reset();
2247 g_kax_segment.reset();
2248 g_kax_sh_main.reset();
2249 g_kax_sh_cues.reset();
2250 s_kax_infos.reset();
2251 s_head.reset();
2252
2253 s_chapters_in_this_file.reset();
2254 s_kax_sh_void.reset();
2255 s_kax_chapters_void.reset();
2256 s_void_after_track_headers.reset();
2257
2258 g_packetizers.clear();
2259 g_files.clear();
2260 g_attachments.clear();
2261 g_track_order.clear();
2262 g_append_mapping.clear();
2263
2264 g_seguid_link_previous.reset();
2265 g_seguid_link_next.reset();
2266 g_forced_seguids.clear();
2267 }
2268