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