1 /*
2    mkvmerge -- utility for splicing together matroska files
3    from component media subtypes
4 
5    Distributed under the GPL v2
6    see the file COPYING for details
7    or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
8 
9    helper functions that need libebml/libmatroska
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include <unordered_set>
17 
18 #include <ebml/EbmlFloat.h>
19 #include <ebml/EbmlSInteger.h>
20 #include <ebml/EbmlString.h>
21 #include <ebml/EbmlUInteger.h>
22 #include <ebml/EbmlUnicodeString.h>
23 #include <ebml/EbmlVoid.h>
24 
25 #include <matroska/KaxBlock.h>
26 #include <matroska/KaxChapters.h>
27 #include <matroska/KaxSegment.h>
28 #include <matroska/KaxTags.h>
29 #include <matroska/KaxTrackAudio.h>
30 #include <matroska/KaxTrackVideo.h>
31 
32 #include "common/date_time.h"
33 #include "common/ebml.h"
34 #include "common/memory.h"
35 #include "common/unique_numbers.h"
36 #include "common/version.h"
37 
38 using namespace libebml;
39 using namespace libmatroska;
40 
41 EbmlElement *
empty_ebml_master(EbmlElement * e)42 empty_ebml_master(EbmlElement *e) {
43   EbmlMaster *m;
44 
45   m = dynamic_cast<EbmlMaster *>(e);
46   if (!m)
47     return e;
48 
49   while (m->ListSize() > 0) {
50     delete (*m)[0];
51     m->Remove(0);
52   }
53 
54   return m;
55 }
56 
57 EbmlElement *
create_ebml_element(const EbmlCallbacks & callbacks,const EbmlId & id)58 create_ebml_element(const EbmlCallbacks &callbacks,
59                     const EbmlId &id) {
60   const EbmlSemanticContext &context = EBML_INFO_CONTEXT(callbacks);
61   size_t i;
62 
63 //   if (id == EbmlId(*parent))
64 //     return empty_ebml_master(&parent->Generic().Create());
65 
66   for (i = 0; i < EBML_CTX_SIZE(context); i++)
67     if (id == EBML_CTX_IDX_ID(context,i))
68       return empty_ebml_master(&EBML_SEM_CREATE(EBML_CTX_IDX(context,i)));
69 
70   for (i = 0; i < EBML_CTX_SIZE(context); i++) {
71     EbmlElement *e;
72 
73     if (!(context != EBML_SEM_CONTEXT(EBML_CTX_IDX(context,i))))
74       continue;
75 
76     e = create_ebml_element(EBML_CTX_IDX_INFO(context, i), id);
77     if (e)
78       return e;
79   }
80 
81   return nullptr;
82 }
83 
84 static EbmlCallbacks const *
do_find_ebml_callbacks(EbmlCallbacks const & base,EbmlId const & id)85 do_find_ebml_callbacks(EbmlCallbacks const &base,
86                        EbmlId const &id) {
87   const EbmlSemanticContext &context = EBML_INFO_CONTEXT(base);
88   const EbmlCallbacks *result;
89   size_t i;
90 
91   if (EBML_INFO_ID(base) == id)
92     return &base;
93 
94   for (i = 0; i < EBML_CTX_SIZE(context); i++)
95     if (id == EBML_CTX_IDX_ID(context,i))
96       return &EBML_CTX_IDX_INFO(context, i);
97 
98   for (i = 0; i < EBML_CTX_SIZE(context); i++) {
99     if (!(context != EBML_SEM_CONTEXT(EBML_CTX_IDX(context,i))))
100       continue;
101     result = do_find_ebml_callbacks(EBML_CTX_IDX_INFO(context, i), id);
102     if (result)
103       return result;
104   }
105 
106   return nullptr;
107 }
108 
109 EbmlCallbacks const *
find_ebml_callbacks(EbmlCallbacks const & base,EbmlId const & id)110 find_ebml_callbacks(EbmlCallbacks const &base,
111                     EbmlId const &id) {
112   static std::unordered_map<uint32_t, EbmlCallbacks const *> s_cache;
113 
114   auto itr = s_cache.find(id.GetValue());
115   if (itr != s_cache.end())
116     return itr->second;
117 
118   auto result            = do_find_ebml_callbacks(base, id);
119   s_cache[id.GetValue()] = result;
120 
121   return result;
122 }
123 
124 static EbmlCallbacks const *
do_find_ebml_callbacks(EbmlCallbacks const & base,char const * debug_name)125 do_find_ebml_callbacks(EbmlCallbacks const &base,
126                        char const *debug_name) {
127   const EbmlSemanticContext &context = EBML_INFO_CONTEXT(base);
128   const EbmlCallbacks *result;
129   size_t i;
130 
131   if (!strcmp(debug_name, EBML_INFO_NAME(base)))
132     return &base;
133 
134   for (i = 0; i < EBML_CTX_SIZE(context); i++)
135     if (!strcmp(debug_name, EBML_INFO_NAME(EBML_CTX_IDX_INFO(context, i))))
136       return &EBML_CTX_IDX_INFO(context, i);
137 
138   for (i = 0; i < EBML_CTX_SIZE(context); i++) {
139     if (!(context != EBML_SEM_CONTEXT(EBML_CTX_IDX(context,i))))
140       continue;
141     result = do_find_ebml_callbacks(EBML_CTX_IDX_INFO(context, i), debug_name);
142     if (result)
143       return result;
144   }
145 
146   return nullptr;
147 }
148 
149 EbmlCallbacks const *
find_ebml_callbacks(EbmlCallbacks const & base,char const * debug_name)150 find_ebml_callbacks(EbmlCallbacks const &base,
151                     char const *debug_name) {
152   static std::unordered_map<std::string, EbmlCallbacks const *> s_cache;
153 
154   auto itr = s_cache.find(debug_name);
155   if (itr != s_cache.end())
156     return itr->second;
157 
158   auto result         = do_find_ebml_callbacks(base, debug_name);
159   s_cache[debug_name] = result;
160 
161   return result;
162 }
163 
164 static EbmlCallbacks const *
do_find_ebml_parent_callbacks(EbmlCallbacks const & base,EbmlId const & id)165 do_find_ebml_parent_callbacks(EbmlCallbacks const &base,
166                               EbmlId const &id) {
167   const EbmlSemanticContext &context = EBML_INFO_CONTEXT(base);
168   const EbmlCallbacks *result;
169   size_t i;
170 
171   for (i = 0; i < EBML_CTX_SIZE(context); i++)
172     if (id == EBML_CTX_IDX_ID(context,i))
173       return &base;
174 
175   for (i = 0; i < EBML_CTX_SIZE(context); i++) {
176     if (!(context != EBML_SEM_CONTEXT(EBML_CTX_IDX(context,i))))
177       continue;
178     result = do_find_ebml_parent_callbacks(EBML_CTX_IDX_INFO(context, i), id);
179     if (result)
180       return result;
181   }
182 
183   return nullptr;
184 }
185 
186 EbmlCallbacks const *
find_ebml_parent_callbacks(EbmlCallbacks const & base,EbmlId const & id)187 find_ebml_parent_callbacks(EbmlCallbacks const &base,
188                            EbmlId const &id) {
189   static std::unordered_map<uint32_t, EbmlCallbacks const *> s_cache;
190 
191   auto itr = s_cache.find(id.GetValue());
192   if (itr != s_cache.end())
193     return itr->second;
194 
195   auto result            = do_find_ebml_parent_callbacks(base, id);
196   s_cache[id.GetValue()] = result;
197 
198   return result;
199 }
200 
201 static EbmlSemantic const *
do_find_ebml_semantic(EbmlCallbacks const & base,EbmlId const & id)202 do_find_ebml_semantic(EbmlCallbacks const &base,
203                       EbmlId const &id) {
204   const EbmlSemanticContext &context = EBML_INFO_CONTEXT(base);
205   const EbmlSemantic *result;
206   size_t i;
207 
208   for (i = 0; i < EBML_CTX_SIZE(context); i++)
209     if (id == EBML_CTX_IDX_ID(context,i))
210       return &EBML_CTX_IDX(context,i);
211 
212   for (i = 0; i < EBML_CTX_SIZE(context); i++) {
213     if (!(context != EBML_SEM_CONTEXT(EBML_CTX_IDX(context,i))))
214       continue;
215     result = do_find_ebml_semantic(EBML_CTX_IDX_INFO(context, i), id);
216     if (result)
217       return result;
218   }
219 
220   return nullptr;
221 }
222 
223 EbmlSemantic const *
find_ebml_semantic(EbmlCallbacks const & base,EbmlId const & id)224 find_ebml_semantic(EbmlCallbacks const &base,
225                    EbmlId const &id) {
226   static std::unordered_map<uint32_t, EbmlSemantic const *> s_cache;
227 
228   auto itr = s_cache.find(id.GetValue());
229   if (itr != s_cache.end())
230     return itr->second;
231 
232   auto result            = do_find_ebml_semantic(base, id);
233   s_cache[id.GetValue()] = result;
234 
235   return result;
236 }
237 
238 EbmlMaster *
sort_ebml_master(EbmlMaster * m)239 sort_ebml_master(EbmlMaster *m) {
240   if (!m)
241     return m;
242 
243   int first_element = -1;
244   int first_master  = -1;
245   size_t i;
246   for (i = 0; i < m->ListSize(); i++) {
247     if (dynamic_cast<EbmlMaster *>((*m)[i]) && (-1 == first_master))
248       first_master = i;
249     else if (!dynamic_cast<EbmlMaster *>((*m)[i]) && (-1 != first_master) && (-1 == first_element))
250       first_element = i;
251     if ((first_master != -1) && (first_element != -1))
252       break;
253   }
254 
255   if (first_master == -1)
256     return m;
257 
258   while (first_element != -1) {
259     EbmlElement *e = (*m)[first_element];
260     m->Remove(first_element);
261     m->InsertElement(*e, first_master);
262     first_master++;
263     for (first_element++; first_element < static_cast<int>(m->ListSize()); first_element++)
264       if (!dynamic_cast<EbmlMaster *>((*m)[first_element]))
265         break;
266     if (first_element >= static_cast<int>(m->ListSize()))
267       first_element = -1;
268   }
269 
270   for (i = 0; i < m->ListSize(); i++)
271     if (dynamic_cast<EbmlMaster *>((*m)[i]))
272       sort_ebml_master(dynamic_cast<EbmlMaster *>((*m)[i]));
273 
274   return m;
275 }
276 
277 void
move_children(EbmlMaster & source,EbmlMaster & destination)278 move_children(EbmlMaster &source,
279               EbmlMaster &destination) {
280   for (auto child : source)
281     destination.PushElement(*child);
282 }
283 
284 // ------------------------------------------------------------------------
285 
286 int64_t
kt_get_default_duration(KaxTrackEntry & track)287 kt_get_default_duration(KaxTrackEntry &track) {
288   return FindChildValue<KaxTrackDefaultDuration>(track);
289 }
290 
291 int64_t
kt_get_number(KaxTrackEntry & track)292 kt_get_number(KaxTrackEntry &track) {
293   return FindChildValue<KaxTrackNumber>(track);
294 }
295 
296 int64_t
kt_get_uid(KaxTrackEntry & track)297 kt_get_uid(KaxTrackEntry &track) {
298   return FindChildValue<KaxTrackUID>(track);
299 }
300 
301 std::string
kt_get_codec_id(KaxTrackEntry & track)302 kt_get_codec_id(KaxTrackEntry &track) {
303   return FindChildValue<KaxCodecID>(track);
304 }
305 
306 std::string
kt_get_language(KaxTrackEntry & track)307 kt_get_language(KaxTrackEntry &track) {
308   return FindChildValue<KaxTrackLanguage>(track);
309 }
310 
311 int
kt_get_max_blockadd_id(KaxTrackEntry & track)312 kt_get_max_blockadd_id(KaxTrackEntry &track) {
313   return FindChildValue<KaxMaxBlockAdditionID>(track);
314 }
315 
316 int
kt_get_a_channels(KaxTrackEntry & track)317 kt_get_a_channels(KaxTrackEntry &track) {
318   auto audio = FindChild<KaxTrackAudio>(track);
319   return audio ? FindChildValue<KaxAudioChannels>(audio, 1u) : 1;
320 }
321 
322 double
kt_get_a_sfreq(KaxTrackEntry & track)323 kt_get_a_sfreq(KaxTrackEntry &track) {
324   auto audio = FindChild<KaxTrackAudio>(track);
325   return audio ? FindChildValue<KaxAudioSamplingFreq>(audio, 8000.0) : 8000.0;
326 }
327 
328 double
kt_get_a_osfreq(KaxTrackEntry & track)329 kt_get_a_osfreq(KaxTrackEntry &track) {
330   auto audio = FindChild<KaxTrackAudio>(track);
331   return audio ? FindChildValue<KaxAudioOutputSamplingFreq>(audio, 8000.0) : 8000.0;
332 }
333 
334 int
kt_get_a_bps(KaxTrackEntry & track)335 kt_get_a_bps(KaxTrackEntry &track) {
336   auto audio = FindChild<KaxTrackAudio>(track);
337   return audio ? FindChildValue<KaxAudioBitDepth, int>(audio, -1) : -1;
338 }
339 
340 int
kt_get_v_pixel_width(KaxTrackEntry & track)341 kt_get_v_pixel_width(KaxTrackEntry &track) {
342   auto video = FindChild<KaxTrackVideo>(track);
343   return video ? FindChildValue<KaxVideoPixelWidth>(video) : 0;
344 }
345 
346 int
kt_get_v_pixel_height(KaxTrackEntry & track)347 kt_get_v_pixel_height(KaxTrackEntry &track) {
348   auto video = FindChild<KaxTrackVideo>(track);
349   return video ? FindChildValue<KaxVideoPixelHeight>(video) : 0;
350 }
351 
352 EbmlElement *
find_ebml_element_by_id(EbmlMaster * master,const EbmlId & id)353 find_ebml_element_by_id(EbmlMaster *master,
354                         const EbmlId &id) {
355   for (auto child : *master)
356     if (EbmlId(*child) == id)
357       return child;
358 
359   return nullptr;
360 }
361 
362 std::pair<EbmlMaster *, size_t>
find_element_in_master(EbmlMaster * master,EbmlElement * element_to_find)363 find_element_in_master(EbmlMaster *master,
364                        EbmlElement *element_to_find) {
365   if (!master || !element_to_find)
366     return std::make_pair<EbmlMaster *, size_t>(nullptr, 0);
367 
368   auto &elements = master->GetElementList();
369   auto itr       = std::find(elements.begin(), elements.end(), element_to_find);
370 
371   if (itr != elements.end())
372     return std::make_pair(master, std::distance(elements.begin(), itr));
373 
374   for (auto &sub_element : elements) {
375     auto sub_master = dynamic_cast<EbmlMaster *>(sub_element);
376     if (!sub_master)
377       continue;
378 
379     auto result = find_element_in_master(sub_master, element_to_find);
380     if (result.first)
381       return result;
382   }
383 
384   return std::make_pair<EbmlMaster *, size_t>(nullptr, 0);
385 }
386 
387 static std::unordered_map<uint32_t, bool> const &
get_deprecated_elements_by_id()388 get_deprecated_elements_by_id() {
389   static std::unordered_map<uint32_t, bool> s_elements;
390 
391   if (!s_elements.empty())
392     return s_elements;
393 
394   s_elements[KaxBlockVirtual::ClassInfos.GlobalId.GetValue()]       = true;
395   s_elements[KaxReferenceVirtual::ClassInfos.GlobalId.GetValue()]   = true;
396   s_elements[KaxSliceFrameNumber::ClassInfos.GlobalId.GetValue()]   = true;
397   s_elements[KaxSliceBlockAddID::ClassInfos.GlobalId.GetValue()]    = true;
398   s_elements[KaxSliceDelay::ClassInfos.GlobalId.GetValue()]         = true;
399   s_elements[KaxSliceDuration::ClassInfos.GlobalId.GetValue()]      = true;
400   s_elements[KaxEncryptedBlock::ClassInfos.GlobalId.GetValue()]     = true;
401   s_elements[KaxTrackTimecodeScale::ClassInfos.GlobalId.GetValue()] = true;
402   s_elements[KaxTrackOffset::ClassInfos.GlobalId.GetValue()]        = true;
403   s_elements[KaxCodecSettings::ClassInfos.GlobalId.GetValue()]      = true;
404   s_elements[KaxCodecInfoURL::ClassInfos.GlobalId.GetValue()]       = true;
405   s_elements[KaxCodecDownloadURL::ClassInfos.GlobalId.GetValue()]   = true;
406   s_elements[KaxOldStereoMode::ClassInfos.GlobalId.GetValue()]      = true;
407   s_elements[KaxVideoGamma::ClassInfos.GlobalId.GetValue()]         = true;
408   s_elements[KaxVideoFrameRate::ClassInfos.GlobalId.GetValue()]     = true;
409   s_elements[KaxAudioPosition::ClassInfos.GlobalId.GetValue()]      = true;
410   s_elements[KaxCueRefCluster::ClassInfos.GlobalId.GetValue()]      = true;
411   s_elements[KaxCueRefNumber::ClassInfos.GlobalId.GetValue()]       = true;
412   s_elements[KaxCueRefCodecState::ClassInfos.GlobalId.GetValue()]   = true;
413   s_elements[KaxFileReferral::ClassInfos.GlobalId.GetValue()]       = true;
414 
415   return s_elements;
416 }
417 
418 template<typename T>
419 void
fix_elements_set_to_default_value_if_unset(EbmlElement * e)420 fix_elements_set_to_default_value_if_unset(EbmlElement *e) {
421   static debugging_option_c s_debug{"fix_elements_in_master"};
422 
423   auto t = static_cast<T *>(e);
424 
425   if (!t->DefaultISset() || t->ValueIsSet())
426     return;
427 
428   mxdebug_if(s_debug,
429              fmt::format("fix_elements_in_master: element has default, but value is no set; setting: ID {0:08x} name {1}\n",
430                          t->Generic().GlobalId.GetValue(), t->Generic().DebugName));
431   t->SetValue(t->GetValue());
432 }
433 
434 void
fix_elements_in_master(EbmlMaster * master)435 fix_elements_in_master(EbmlMaster *master) {
436   static debugging_option_c s_debug{"fix_elements_in_master"};
437 
438   if (!master)
439     return;
440 
441   auto callbacks = find_ebml_callbacks(KaxSegment::ClassInfos, master->Generic().GlobalId);
442   if (!callbacks) {
443     mxdebug_if(s_debug, fmt::format("fix_elements_in_master: No callbacks found for ID {0:08x}\n", master->Generic().GlobalId.GetValue()));
444     return;
445   }
446 
447   std::unordered_map<uint32_t, bool> is_present;
448 
449   auto &deprecated_elements    = get_deprecated_elements_by_id();
450   auto deprecated_elements_end = deprecated_elements.end();
451   auto idx                     = 0u;
452 
453   // 1. Remove deprecated elements.
454   // 2. Record which elements are already present.
455   // 3. If the element has a default value and is currently unset, set
456   //    to the default value.
457 
458   while (idx < master->ListSize()) {
459     auto child    = (*master)[idx];
460     auto child_id = child->Generic().GlobalId.GetValue();
461     auto itr      = deprecated_elements.find(child_id);
462 
463     if (itr != deprecated_elements_end) {
464       delete child;
465       master->Remove(idx);
466       continue;
467 
468     }
469 
470     ++idx;
471     is_present[child_id] = true;
472 
473     if (dynamic_cast<EbmlMaster *>(child))
474       fix_elements_in_master(static_cast<EbmlMaster *>(child));
475 
476     else if (dynamic_cast<EbmlDate *>(child))
477       fix_elements_set_to_default_value_if_unset<EbmlDate>(child);
478 
479     else if (dynamic_cast<EbmlFloat *>(child))
480       fix_elements_set_to_default_value_if_unset<EbmlFloat>(child);
481 
482     else if (dynamic_cast<EbmlSInteger *>(child))
483       fix_elements_set_to_default_value_if_unset<EbmlSInteger>(child);
484 
485     else if (dynamic_cast<EbmlString *>(child))
486       fix_elements_set_to_default_value_if_unset<EbmlString>(child);
487 
488     else if (dynamic_cast<EbmlUInteger *>(child))
489       fix_elements_set_to_default_value_if_unset<EbmlUInteger>(child);
490 
491     else if (dynamic_cast<EbmlUnicodeString *>(child))
492       fix_elements_set_to_default_value_if_unset<EbmlUnicodeString>(child);
493   }
494 
495   // 4. Take care of certain mandatory elements without default values
496   //    that we can provide sensible values for, e.g. create UIDs
497   //    ourselves.
498 
499   // 4.1. Info
500   if (dynamic_cast<KaxInfo *>(master)) {
501     auto info_data = get_default_segment_info_data();
502 
503     if (!is_present[KaxMuxingApp::ClassInfos.GlobalId.GetValue()])
504       AddEmptyChild<KaxMuxingApp>(master).SetValueUTF8(info_data.muxing_app);
505 
506     if (!is_present[KaxWritingApp::ClassInfos.GlobalId.GetValue()])
507       AddEmptyChild<KaxWritingApp>(master).SetValueUTF8(info_data.writing_app);
508   }
509 
510   // 4.2. Tracks
511   else if (dynamic_cast<KaxTrackEntry *>(master)) {
512     if (!is_present[KaxTrackUID::ClassInfos.GlobalId.GetValue()])
513       AddEmptyChild<KaxTrackUID>(master).SetValue(create_unique_number(UNIQUE_TRACK_IDS));
514   }
515 
516   // 4.3. Chapters
517   else if (dynamic_cast<KaxEditionEntry *>(master)) {
518     if (!is_present[KaxEditionUID::ClassInfos.GlobalId.GetValue()])
519       AddEmptyChild<KaxEditionUID>(master).SetValue(create_unique_number(UNIQUE_EDITION_IDS));
520 
521 
522   } else if (dynamic_cast<KaxChapterAtom *>(master)) {
523     if (!is_present[KaxChapterUID::ClassInfos.GlobalId.GetValue()])
524       AddEmptyChild<KaxChapterUID>(master).SetValue(create_unique_number(UNIQUE_CHAPTER_IDS));
525 
526     if (!is_present[KaxChapterTimeStart::ClassInfos.GlobalId.GetValue()])
527       AddEmptyChild<KaxChapterTimeStart>(master).SetValue(0);
528 
529   } else if (dynamic_cast<KaxChapterDisplay *>(master)) {
530     if (!is_present[KaxChapterString::ClassInfos.GlobalId.GetValue()])
531       AddEmptyChild<KaxChapterString>(master).SetValueUTF8("");
532 
533   }
534 
535   // 4.4. Tags
536   else if (dynamic_cast<KaxTag *>(master)) {
537     if (!is_present[KaxTagTargets::ClassInfos.GlobalId.GetValue()])
538       fix_elements_in_master(&AddEmptyChild<KaxTagTargets>(master));
539 
540     else if (!is_present[KaxTagSimple::ClassInfos.GlobalId.GetValue()])
541       fix_elements_in_master(&AddEmptyChild<KaxTagSimple>(master));
542 
543   } else if (dynamic_cast<KaxTagTargets *>(master)) {
544     if (!is_present[KaxTagTargetTypeValue::ClassInfos.GlobalId.GetValue()])
545       AddEmptyChild<KaxTagTargetTypeValue>(master).SetValue(50); // = movie
546 
547   } else if (dynamic_cast<KaxTagSimple *>(master)) {
548     if (!is_present[KaxTagName::ClassInfos.GlobalId.GetValue()])
549       AddEmptyChild<KaxTagName>(master).SetValueUTF8("");
550 
551   }
552 
553   // 4.5. Attachments
554   else if (dynamic_cast<KaxAttached *>(master)) {
555     if (!is_present[KaxFileUID::ClassInfos.GlobalId.GetValue()])
556       AddEmptyChild<KaxFileUID>(master).SetValue(create_unique_number(UNIQUE_ATTACHMENT_IDS));
557   }
558 }
559 
560 void
fix_mandatory_elements(EbmlElement * master)561 fix_mandatory_elements(EbmlElement *master) {
562   if (dynamic_cast<EbmlMaster *>(master))
563     fix_elements_in_master(static_cast<EbmlMaster *>(master));
564 }
565 
566 void
remove_voids_from_master(EbmlElement * element)567 remove_voids_from_master(EbmlElement *element) {
568   auto master = dynamic_cast<EbmlMaster *>(element);
569   if (master)
570     DeleteChildren<EbmlVoid>(master);
571 }
572 
573 int
write_ebml_element_head(mm_io_c & out,EbmlId const & id,int64_t content_size)574 write_ebml_element_head(mm_io_c &out,
575                         EbmlId const &id,
576                         int64_t content_size) {
577 	int id_size    = EBML_ID_LENGTH(id);
578 	int coded_size = CodedSizeLength(content_size, 0);
579   uint8_t buffer[4 + 8];
580 
581 	id.Fill(buffer);
582 	CodedValueLength(content_size, coded_size, &buffer[id_size]);
583 
584   return out.write(buffer, id_size + coded_size);
585 }
586 
587 bool
remove_master_from_parent_if_empty_or_only_defaults(EbmlMaster * parent,EbmlMaster * child,std::unordered_map<EbmlMaster *,bool> & handled)588 remove_master_from_parent_if_empty_or_only_defaults(EbmlMaster *parent,
589                                                     EbmlMaster *child,
590                                                     std::unordered_map<EbmlMaster *, bool> &handled) {
591   if (!parent || !child || handled[child])
592     return false;
593 
594   if (0 < child->ListSize()) {
595     auto all_set_to_default_value = true;
596 
597     for (auto const &childs_child : *child)
598       if (   !childs_child->IsDefaultValue()
599           || !(   dynamic_cast<EbmlBinary        *>(childs_child)
600                || dynamic_cast<EbmlDate          *>(childs_child)
601                || dynamic_cast<EbmlFloat         *>(childs_child)
602                || dynamic_cast<EbmlSInteger      *>(childs_child)
603                || dynamic_cast<EbmlString        *>(childs_child)
604                || dynamic_cast<EbmlUInteger      *>(childs_child)
605                || dynamic_cast<EbmlUnicodeString *>(childs_child))) {
606         all_set_to_default_value = false;
607         break;
608       }
609 
610     if (!all_set_to_default_value)
611       return false;
612 
613     for (auto &childs_child : *child)
614       delete childs_child;
615 
616     child->RemoveAll();
617   }
618 
619   handled[child] = true;
620 
621   auto itr = std::find(parent->begin(), parent->end(), child);
622   if (itr != parent->end())
623     parent->Remove(itr);
624 
625   delete child;
626 
627   return true;
628 }
629 
630 void
remove_ietf_language_elements(libebml::EbmlMaster & master)631 remove_ietf_language_elements(libebml::EbmlMaster &master) {
632   auto idx = 0u;
633 
634   while (idx < master.ListSize()) {
635     auto e = master[idx];
636 
637     if (   dynamic_cast<KaxLanguageIETF *>(e)
638         || dynamic_cast<KaxChapLanguageIETF *>(e)
639         || dynamic_cast<KaxTagLanguageIETF *>(e)) {
640       delete e;
641       master.Remove(idx);
642       continue;
643     }
644 
645     if (dynamic_cast<EbmlMaster *>(e))
646       remove_ietf_language_elements(*static_cast<EbmlMaster *>(e));
647 
648     ++idx;
649   }
650 }
651 
652 void
remove_mandatory_elements_set_to_their_default(libebml::EbmlMaster & master)653 remove_mandatory_elements_set_to_their_default(libebml::EbmlMaster &master) {
654   std::unordered_map<uint32_t, unsigned int> num_elements_by_type;
655   std::unordered_set<libebml::EbmlElement *> elements_to_remove;
656 
657   for (int idx = 0, num_elements = master.ListSize(); idx < num_elements; ++idx) {
658     auto child = master[idx];
659 
660     if (dynamic_cast<libebml::EbmlMaster *>(child)) {
661       remove_mandatory_elements_set_to_their_default(*static_cast<libebml::EbmlMaster *>(child));
662       continue;
663     }
664 
665     ++num_elements_by_type[ EbmlId(*child).GetValue() ];
666 
667     if (!child->IsDefaultValue())
668       continue;
669 
670     auto semantic = find_ebml_semantic(KaxSegment::ClassInfos, libebml::EbmlId(*child));
671 
672     if (!semantic || !semantic->IsMandatory())
673       continue;
674 
675     elements_to_remove.insert(child);
676   }
677 
678   // Don't remove elements if there is more than one of them in the
679   // same master. For example, ChapterLanguage is mandatory with
680   // default "eng", but it is also multiple. If a single
681   // <ChapterDisplay> contains two ChapterLanguage, one of them being
682   // "eng", then that one must not be removed; otherwise information
683   // is lost.
684 
685   int idx = 0;
686   while (idx < static_cast<int>(master.ListSize())) {
687     auto child = master[idx];
688 
689     if (elements_to_remove.find(child) == elements_to_remove.end()) {
690       ++idx;
691       continue;
692     }
693 
694     if (num_elements_by_type[ EbmlId(*child).GetValue() ] == 1) {
695       delete child;
696       master.Remove(idx);
697 
698     } else
699       ++idx;
700   }
701 }
702 
703 static bool
must_be_present_in_master_by_id(EbmlId const & id)704 must_be_present_in_master_by_id(EbmlId const &id) {
705   static debugging_option_c s_debug{"must_be_present_in_master"};
706 
707   auto semantic = find_ebml_semantic(KaxSegment::ClassInfos, id);
708   if (!semantic || !semantic->IsMandatory()) {
709     mxdebug_if(s_debug, fmt::format("ID {0:08x}: 0 (either no semantic or not mandatory)\n", id.GetValue()));
710     return false;
711   }
712 
713   auto elt = std::shared_ptr<EbmlElement>(&semantic->Create());
714 
715   mxdebug_if(s_debug, fmt::format("ID {0:08x}: {1} (default is {2}set)\n", id.GetValue(), !elt->DefaultISset(), elt->DefaultISset() ? "" : "not "));
716 
717   return !elt->DefaultISset();
718 }
719 
720 bool
must_be_present_in_master(EbmlCallbacks const & callbacks)721 must_be_present_in_master(EbmlCallbacks const &callbacks) {
722   static std::unordered_map<uint32_t, bool> s_must_be_present;
723 
724   auto id  = callbacks.ClassId();
725   auto itr = s_must_be_present.find(id.GetValue());
726 
727   if (itr != s_must_be_present.end())
728     return itr->second;
729 
730   auto result                      = must_be_present_in_master_by_id(id);
731   s_must_be_present[id.GetValue()] = result;
732 
733   return result;
734 }
735 
736 
737 bool
must_be_present_in_master(EbmlElement const & element)738 must_be_present_in_master(EbmlElement const &element) {
739   return must_be_present_in_master(element.Generic());
740 }
741 
742 bool
found_in(EbmlElement & haystack,EbmlElement const * needle)743 found_in(EbmlElement &haystack,
744          EbmlElement const *needle) {
745   if (!needle)
746     return false;
747 
748   if (needle == &haystack)
749     return true;
750 
751   auto master = dynamic_cast<EbmlMaster *>(&haystack);
752   if (!master)
753     return false;
754 
755   for (auto &child : *master) {
756     if (child == needle)
757       return true;
758 
759     if (dynamic_cast<EbmlMaster *>(child) && found_in(*child, needle))
760       return true;
761   }
762 
763   return false;
764 }
765