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