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    the generic_packetizer_c implementation
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13 
14 #include "common/common_pch.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <unordered_map>
19 
20 #include <matroska/KaxContentEncoding.h>
21 #include <matroska/KaxTag.h>
22 #include <matroska/KaxTracks.h>
23 #include <matroska/KaxTrackEntryData.h>
24 #include <matroska/KaxTrackAudio.h>
25 #include <matroska/KaxTrackVideo.h>
26 
27 #include "common/compression.h"
28 #include "common/container.h"
29 #include "common/debugging.h"
30 #include "common/ebml.h"
31 #include "common/hacks.h"
32 #include "common/strings/formatting.h"
33 #include "common/unique_numbers.h"
34 #include "common/xml/ebml_tags_converter.h"
35 #include "merge/cluster_helper.h"
36 #include "merge/filelist.h"
37 #include "merge/generic_packetizer.h"
38 #include "merge/generic_reader.h"
39 #include "merge/output_control.h"
40 #include "merge/webm.h"
41 
42 using namespace libmatroska;
43 
44 namespace {
45 
46 constexpr auto
track_type_to_deftrack_type(int type)47 track_type_to_deftrack_type(int type) {
48   return track_audio == type ? DEFTRACK_TYPE_AUDIO
49        : track_video == type ? DEFTRACK_TYPE_VIDEO
50        :                       DEFTRACK_TYPE_SUBS;
51 }
52 
53 template<typename T>
54 auto
lookup_track_id(T const & container,int64_t track_id)55 lookup_track_id(T const &container,
56                 int64_t track_id) {
57   return mtx::includes(container, track_id) ? track_id
58        : mtx::includes(container, -1)       ? -1
59        :                                      -2;
60 }
61 
62 debugging_option_c s_debug{"generic_packetizer"};
63 
64 }
65 
66 static std::unordered_map<std::string, bool> s_experimental_status_warning_shown;
67 std::vector<generic_packetizer_c *> ptzrs_in_header_order;
68 
69 int generic_packetizer_c::ms_track_number = 0;
70 
generic_packetizer_c(generic_reader_c * reader,track_info_c & ti)71 generic_packetizer_c::generic_packetizer_c(generic_reader_c *reader,
72                                            track_info_c &ti)
73   : m_num_packets{}
74   , m_next_packet_wo_assigned_timestamp{}
75   , m_free_refs{-1}
76   , m_next_free_refs{-1}
77   , m_enqueued_bytes{}
78   , m_safety_last_timestamp{}
79   , m_safety_last_duration{}
80   , m_track_entry{}
81   , m_hserialno{-1}
82   , m_htrack_type{-1}
83   , m_htrack_default_duration{-1}
84   , m_htrack_default_duration_indicates_fields{}
85   , m_default_duration_forced{true}
86   , m_huid{}
87   , m_htrack_max_add_block_ids{-1}
88   , m_haudio_sampling_freq{-1.0}
89   , m_haudio_output_sampling_freq{-1.0}
90   , m_haudio_channels{-1}
91   , m_haudio_bit_depth{-1}
92   , m_hvideo_interlaced_flag{-1}
93   , m_hvideo_pixel_width{-1}
94   , m_hvideo_pixel_height{-1}
95   , m_hvideo_display_width{-1}
96   , m_hvideo_display_height{-1}
97   , m_hvideo_display_unit{ddu_pixels}
98   , m_hcompression{COMPRESSION_UNSPECIFIED}
99   , m_timestamp_factory_application_mode{TFA_AUTOMATIC}
100   , m_last_cue_timestamp{-1}
101   , m_has_been_flushed{}
102   , m_prevent_lacing{}
103   , m_connected_successor{}
104   , m_ti{ti}
105   , m_reader{reader}
106   , m_connected_to{}
107   , m_correction_timestamp_offset{}
108   , m_append_timestamp_offset{}
109   , m_max_timestamp_seen{}
110   , m_relaxed_timestamp_checking{}
111 {
__anonbec5e4a10202(std::map<int64_t, bool> &flags, std::optional<bool> &flag) 112   auto set_bool_maybe = [this](std::map<int64_t, bool> &flags, std::optional<bool> &flag) {
113     if (mtx::includes(flags, m_ti.m_id))
114       flag = flags[m_ti.m_id];
115     else if (mtx::includes(flags, -1))
116       flag = flags[-1];
117   };
118 
119   // Let's see if the user specified timestamp sync for this track.
120   if (mtx::includes(m_ti.m_timestamp_syncs, m_ti.m_id))
121     m_ti.m_tcsync = m_ti.m_timestamp_syncs[m_ti.m_id];
122   else if (mtx::includes(m_ti.m_timestamp_syncs, -1))
123     m_ti.m_tcsync = m_ti.m_timestamp_syncs[-1];
124   if (0 == m_ti.m_tcsync.factor)
125     m_ti.m_tcsync.factor = mtx_mp_rational_t{1, 1};
126 
127   // Let's see if the user specified "reset timestamps" for this track.
128   m_ti.m_reset_timestamps = mtx::includes(m_ti.m_reset_timestamps_specs, m_ti.m_id) || mtx::includes(m_ti.m_reset_timestamps_specs, -1);
129 
130   // Let's see if the user has specified which cues he wants for this track.
131   if (mtx::includes(m_ti.m_cue_creations, m_ti.m_id))
132     m_ti.m_cues = m_ti.m_cue_creations[m_ti.m_id];
133   else if (mtx::includes(m_ti.m_cue_creations, -1))
134     m_ti.m_cues = m_ti.m_cue_creations[-1];
135 
136   set_bool_maybe(m_ti.m_default_track_flags,            m_ti.m_default_track);
137   set_bool_maybe(m_ti.m_forced_track_flags,             m_ti.m_forced_track);
138   set_bool_maybe(m_ti.m_enabled_track_flags,            m_ti.m_enabled_track);
139   set_bool_maybe(m_ti.m_fix_bitstream_frame_rate_flags, m_ti.m_fix_bitstream_frame_rate);
140   set_bool_maybe(m_ti.m_hearing_impaired_flags,         m_ti.m_hearing_impaired_flag);
141   set_bool_maybe(m_ti.m_visual_impaired_flags,          m_ti.m_visual_impaired_flag);
142   set_bool_maybe(m_ti.m_text_descriptions_flags,        m_ti.m_text_descriptions_flag);
143   set_bool_maybe(m_ti.m_original_flags,                 m_ti.m_original_flag);
144   set_bool_maybe(m_ti.m_commentary_flags,               m_ti.m_commentary_flag);
145 
146   // Let's see if the user has specified a language for this track.
147   if (mtx::includes(m_ti.m_languages, m_ti.m_id))
148     m_ti.m_language = m_ti.m_languages[m_ti.m_id];
149   else if (mtx::includes(m_ti.m_languages, -1))
150     m_ti.m_language = m_ti.m_languages[-1];
151 
152   // Let's see if the user has specified a sub charset for this track.
153   if (mtx::includes(m_ti.m_sub_charsets, m_ti.m_id))
154     m_ti.m_sub_charset = m_ti.m_sub_charsets[m_ti.m_id];
155   else if (mtx::includes(m_ti.m_sub_charsets, -1))
156     m_ti.m_sub_charset = m_ti.m_sub_charsets[-1];
157 
158   // Let's see if the user has specified a sub charset for this track.
159   if (mtx::includes(m_ti.m_all_tags, m_ti.m_id))
160     m_ti.m_tags_file_name = m_ti.m_all_tags[m_ti.m_id];
161   else if (mtx::includes(m_ti.m_all_tags, -1))
162     m_ti.m_tags_file_name = m_ti.m_all_tags[-1];
163   if (!m_ti.m_tags_file_name.empty())
164     m_ti.m_tags = mtx::xml::ebml_tags_converter_c::parse_file(m_ti.m_tags_file_name, false);
165 
166   // Let's see if the user has specified how this track should be compressed.
167   if (mtx::includes(m_ti.m_compression_list, m_ti.m_id))
168     m_ti.m_compression = m_ti.m_compression_list[m_ti.m_id];
169   else if (mtx::includes(m_ti.m_compression_list, -1))
170     m_ti.m_compression = m_ti.m_compression_list[-1];
171 
172   // Let's see if the user has specified a name for this track.
173   if (mtx::includes(m_ti.m_track_names, m_ti.m_id))
174     m_ti.m_track_name = m_ti.m_track_names[m_ti.m_id];
175   else if (mtx::includes(m_ti.m_track_names, -1))
176     m_ti.m_track_name = m_ti.m_track_names[-1];
177 
178   // Let's see if the user has specified external timestamps for this track.
179   if (mtx::includes(m_ti.m_all_ext_timestamps, m_ti.m_id))
180     m_ti.m_ext_timestamps = m_ti.m_all_ext_timestamps[m_ti.m_id];
181   else if (mtx::includes(m_ti.m_all_ext_timestamps, -1))
182     m_ti.m_ext_timestamps = m_ti.m_all_ext_timestamps[-1];
183 
184   // Let's see if the user has specified an aspect ratio or display dimensions
185   // for this track.
186   int i = lookup_track_id(m_ti.m_display_properties, m_ti.m_id);
187   if (-2 != i) {
188     display_properties_t &dprop = m_ti.m_display_properties[i];
189     if (0 > dprop.aspect_ratio) {
190       set_video_display_dimensions(dprop.width, dprop.height, generic_packetizer_c::ddu_pixels, OPTION_SOURCE_COMMAND_LINE);
191     } else {
192       set_video_aspect_ratio(dprop.aspect_ratio, dprop.ar_factor, OPTION_SOURCE_COMMAND_LINE);
193       m_ti.m_aspect_ratio_given = true;
194     }
195   }
196 
197   if (m_ti.m_aspect_ratio_given && m_ti.m_display_dimensions_given) {
198     if (m_ti.m_aspect_ratio_is_factor)
199       mxerror_tid(m_ti.m_fname, m_ti.m_id, fmt::format(Y("Both the aspect ratio factor and '--display-dimensions' were given.\n")));
200     else
201       mxerror_tid(m_ti.m_fname, m_ti.m_id, fmt::format(Y("Both the aspect ratio and '--display-dimensions' were given.\n")));
202   }
203 
204   // Let's see if the user has specified a FourCC for this track.
205   if (mtx::includes(m_ti.m_all_fourccs, m_ti.m_id))
206     m_ti.m_fourcc = m_ti.m_all_fourccs[m_ti.m_id];
207   else if (mtx::includes(m_ti.m_all_fourccs, -1))
208     m_ti.m_fourcc = m_ti.m_all_fourccs[-1];
209 
210   // Let's see if the user has specified cropping parameters for this track.
211   i = lookup_track_id(m_ti.m_pixel_crop_list, m_ti.m_id);
212   if (-2 != i)
213     set_video_pixel_cropping(m_ti.m_pixel_crop_list[i], OPTION_SOURCE_COMMAND_LINE);
214 
215   // Let's see if the user has specified colour matrix for this track.
216   i = lookup_track_id(m_ti.m_colour_matrix_coeff_list, m_ti.m_id);
217   if (-2 != i)
218     set_video_colour_matrix(m_ti.m_colour_matrix_coeff_list[i], OPTION_SOURCE_COMMAND_LINE);
219 
220   // Let's see if the user has specified bits per channel parameter for this track.
221   i = lookup_track_id(m_ti.m_bits_per_channel_list, m_ti.m_id);
222   if (-2 != i)
223     set_video_bits_per_channel(m_ti.m_bits_per_channel_list[i], OPTION_SOURCE_COMMAND_LINE);
224 
225   // Let's see if the user has specified chroma subsampling parameter for this track.
226   i = lookup_track_id(m_ti.m_chroma_subsample_list, m_ti.m_id);
227   if (-2 != i)
228     set_video_chroma_subsample(m_ti.m_chroma_subsample_list[i], OPTION_SOURCE_COMMAND_LINE);
229 
230   // Let's see if the user has specified Cb subsampling parameter for this track.
231   i = lookup_track_id(m_ti.m_cb_subsample_list, m_ti.m_id);
232   if (-2 != i)
233     set_video_cb_subsample(m_ti.m_cb_subsample_list[i], OPTION_SOURCE_COMMAND_LINE);
234 
235   // Let's see if the user has specified chroma siting parameter for this track.
236   i = lookup_track_id(m_ti.m_chroma_siting_list, m_ti.m_id);
237   if (-2 != i)
238     set_video_chroma_siting(m_ti.m_chroma_siting_list[i], OPTION_SOURCE_COMMAND_LINE);
239 
240   // Let's see if the user has specified colour range parameter for this track.
241   i = lookup_track_id(m_ti.m_colour_range_list, m_ti.m_id);
242   if (-2 != i)
243     set_video_colour_range(m_ti.m_colour_range_list[i], OPTION_SOURCE_COMMAND_LINE);
244 
245   // Let's see if the user has specified colour transfer characteristics parameter for this track.
246   i = lookup_track_id(m_ti.m_colour_transfer_list, m_ti.m_id);
247   if (-2 != i)
248     set_video_colour_transfer_character(m_ti.m_colour_transfer_list[i], OPTION_SOURCE_COMMAND_LINE);
249 
250   // Let's see if the user has specified colour primaries parameter for this track.
251   i = lookup_track_id(m_ti.m_colour_primaries_list, m_ti.m_id);
252   if (-2 != i)
253     set_video_colour_primaries(m_ti.m_colour_primaries_list[i], OPTION_SOURCE_COMMAND_LINE);
254 
255   // Let's see if the user has specified max content light parameter for this track.
256   i = lookup_track_id(m_ti.m_max_cll_list, m_ti.m_id);
257   if (-2 != i)
258     set_video_max_cll(m_ti.m_max_cll_list[i], OPTION_SOURCE_COMMAND_LINE);
259 
260   // Let's see if the user has specified max frame light parameter for this track.
261   i = lookup_track_id(m_ti.m_max_fall_list, m_ti.m_id);
262   if (-2 != i)
263     set_video_max_fall(m_ti.m_max_fall_list[i], OPTION_SOURCE_COMMAND_LINE);
264 
265   // Let's see if the user has specified chromaticity coordinates parameter for this track.
266   i = lookup_track_id(m_ti.m_chroma_coordinates_list, m_ti.m_id);
267   if (-2 != i)
268     set_video_chroma_coordinates(m_ti.m_chroma_coordinates_list[i], OPTION_SOURCE_COMMAND_LINE);
269 
270   // Let's see if the user has specified white colour coordinates parameter for this track.
271   i = lookup_track_id(m_ti.m_white_coordinates_list, m_ti.m_id);
272   if (-2 != i)
273     set_video_white_colour_coordinates(m_ti.m_white_coordinates_list[i], OPTION_SOURCE_COMMAND_LINE);
274 
275   // Let's see if the user has specified max luminance parameter for this track.
276   i = lookup_track_id(m_ti.m_max_luminance_list, m_ti.m_id);
277   if (-2 != i)
278     set_video_max_luminance(m_ti.m_max_luminance_list[i], OPTION_SOURCE_COMMAND_LINE);
279 
280   // Let's see if the user has specified min luminance parameter for this track.
281   i = lookup_track_id(m_ti.m_min_luminance_list, m_ti.m_id);
282   if (-2 != i)
283     set_video_min_luminance(m_ti.m_min_luminance_list[i], OPTION_SOURCE_COMMAND_LINE);
284 
285   i = lookup_track_id(m_ti.m_projection_type_list, m_ti.m_id);
286   if (-2 != i)
287     set_video_projection_type(m_ti.m_projection_type_list[i], OPTION_SOURCE_COMMAND_LINE);
288 
289   i = lookup_track_id(m_ti.m_projection_private_list, m_ti.m_id);
290   if (-2 != i)
291     set_video_projection_private(m_ti.m_projection_private_list[i], OPTION_SOURCE_COMMAND_LINE);
292 
293   i = lookup_track_id(m_ti.m_projection_pose_yaw_list, m_ti.m_id);
294   if (-2 != i)
295     set_video_projection_pose_yaw(m_ti.m_projection_pose_yaw_list[i], OPTION_SOURCE_COMMAND_LINE);
296 
297   i = lookup_track_id(m_ti.m_projection_pose_pitch_list, m_ti.m_id);
298   if (-2 != i)
299     set_video_projection_pose_pitch(m_ti.m_projection_pose_pitch_list[i], OPTION_SOURCE_COMMAND_LINE);
300 
301   i = lookup_track_id(m_ti.m_projection_pose_roll_list, m_ti.m_id);
302   if (-2 != i)
303     set_video_projection_pose_roll(m_ti.m_projection_pose_roll_list[i], OPTION_SOURCE_COMMAND_LINE);
304 
305   // Let's see if the user has specified a field order for this track.
306   i = lookup_track_id(m_ti.m_field_order_list, m_ti.m_id);
307   if (-2 != i)
308     set_video_field_order(m_ti.m_field_order_list[m_ti.m_id], OPTION_SOURCE_COMMAND_LINE);
309 
310   // Let's see if the user has specified a stereo mode for this track.
311   i = lookup_track_id(m_ti.m_stereo_mode_list, m_ti.m_id);
312   if (-2 != i)
313     set_video_stereo_mode(m_ti.m_stereo_mode_list[m_ti.m_id], OPTION_SOURCE_COMMAND_LINE);
314 
315   // Let's see if the user has specified a default duration for this track.
316   if (mtx::includes(m_ti.m_default_durations, m_ti.m_id)) {
317     m_htrack_default_duration                  = m_ti.m_default_durations[m_ti.m_id].first;
318     m_htrack_default_duration_indicates_fields = m_ti.m_default_durations[m_ti.m_id].second;
319 
320   } else if (mtx::includes(m_ti.m_default_durations, -1)) {
321     m_htrack_default_duration                  = m_ti.m_default_durations[-1].first;
322     m_htrack_default_duration_indicates_fields = m_ti.m_default_durations[-1].second;
323 
324   } else
325     m_default_duration_forced = false;
326 
327   // Let's see if the user has set a max_block_add_id
328   if (mtx::includes(m_ti.m_max_blockadd_ids, m_ti.m_id))
329     m_htrack_max_add_block_ids = m_ti.m_max_blockadd_ids[m_ti.m_id];
330   else if (mtx::includes(m_ti.m_max_blockadd_ids, -1))
331     m_htrack_max_add_block_ids = m_ti.m_max_blockadd_ids[-1];
332 
333   // Let's see if the user has specified a compression scheme for this track.
334   if (COMPRESSION_UNSPECIFIED != m_ti.m_compression)
335     m_hcompression = m_ti.m_compression;
336 
337   // Set default header values to 'unset'.
338   if (!m_reader->m_appending) {
339     m_hserialno                             = create_track_number();
340     g_packetizers_by_track_num[m_hserialno] = this;
341   }
342 
343   m_timestamp_factory = timestamp_factory_c::create(m_ti.m_ext_timestamps, m_ti.m_fname, m_ti.m_id);
344 
345   // If no external timestamp file but a default duration has been
346   // given then create a simple timestamp factory that generates the
347   // timestamps for the given FPS.
348   if (!m_timestamp_factory && (-1 != m_htrack_default_duration)) {
349     auto divisor        = m_htrack_default_duration_indicates_fields ? 2 : 1;
350     m_timestamp_factory = timestamp_factory_c::create_fps_factory(m_htrack_default_duration / divisor, m_ti.m_tcsync);
351   }
352 }
353 
~generic_packetizer_c()354 generic_packetizer_c::~generic_packetizer_c() {
355 }
356 
357 void
set_tag_track_uid()358 generic_packetizer_c::set_tag_track_uid() {
359   if (!m_ti.m_tags)
360     return;
361 
362   mtx::tags::convert_old(*m_ti.m_tags);
363   size_t idx_tags;
364   for (idx_tags = 0; m_ti.m_tags->ListSize() > idx_tags; ++idx_tags) {
365     KaxTag *tag = (KaxTag *)(*m_ti.m_tags)[idx_tags];
366 
367     mtx::tags::remove_track_uid_targets(tag);
368 
369     GetChild<KaxTagTrackUID>(GetChild<KaxTagTargets>(tag)).SetValue(m_huid);
370 
371     fix_mandatory_elements(tag);
372 
373     if (!tag->CheckMandatory())
374       mxerror(fmt::format(Y("The tags in '{0}' could not be parsed: some mandatory elements are missing.\n"),
375                           m_ti.m_tags_file_name != "" ? m_ti.m_tags_file_name : m_ti.m_fname));
376   }
377 }
378 
379 bool
set_uid(uint64_t uid)380 generic_packetizer_c::set_uid(uint64_t uid) {
381   if (!is_unique_number(uid, UNIQUE_TRACK_IDS))
382     return false;
383 
384   add_unique_number(uid, UNIQUE_TRACK_IDS);
385   m_huid = uid;
386   if (m_track_entry)
387     GetChild<KaxTrackUID>(m_track_entry).SetValue(m_huid);
388 
389   return true;
390 }
391 
392 void
set_track_type(int type,timestamp_factory_application_e tfa_mode)393 generic_packetizer_c::set_track_type(int type,
394                                      timestamp_factory_application_e tfa_mode) {
395   m_htrack_type = type;
396 
397   if (CUE_STRATEGY_UNSPECIFIED == get_cue_creation())
398     m_ti.m_cues = track_audio    == type ? CUE_STRATEGY_SPARSE
399                 : track_video    == type ? CUE_STRATEGY_IFRAMES
400                 : track_subtitle == type ? CUE_STRATEGY_IFRAMES
401                 :                          CUE_STRATEGY_UNSPECIFIED;
402 
403   if (track_audio == type)
404     m_reader->m_num_audio_tracks++;
405 
406   else if (track_video == type)
407     m_reader->m_num_video_tracks++;
408 
409   else
410     m_reader->m_num_subtitle_tracks++;
411 
412   g_cluster_helper->register_new_packetizer(*this);
413 
414   if (   (TFA_AUTOMATIC == tfa_mode)
415       && (TFA_AUTOMATIC == m_timestamp_factory_application_mode))
416     m_timestamp_factory_application_mode
417       = (track_video    == type) ? TFA_FULL_QUEUEING
418       : (track_subtitle == type) ? TFA_IMMEDIATE
419       : (track_buttons  == type) ? TFA_IMMEDIATE
420       :                            TFA_FULL_QUEUEING;
421 
422   else if (TFA_AUTOMATIC != tfa_mode)
423     m_timestamp_factory_application_mode = tfa_mode;
424 
425   if (m_timestamp_factory && (track_video != type) && (track_audio != type))
426     m_timestamp_factory->set_preserve_duration(true);
427 }
428 
429 void
set_track_name(const std::string & name)430 generic_packetizer_c::set_track_name(const std::string &name) {
431   m_ti.m_track_name = name;
432   if (m_track_entry && !name.empty())
433     GetChild<KaxTrackName>(m_track_entry).SetValueUTF8(m_ti.m_track_name);
434 }
435 
436 void
set_codec_id(const std::string & id)437 generic_packetizer_c::set_codec_id(const std::string &id) {
438   m_hcodec_id = id;
439   if (m_track_entry && !id.empty())
440     GetChild<KaxCodecID>(m_track_entry).SetValue(m_hcodec_id);
441 }
442 
443 void
set_codec_private(memory_cptr const & buffer)444 generic_packetizer_c::set_codec_private(memory_cptr const &buffer) {
445   if (buffer && buffer->get_size()) {
446     m_hcodec_private = buffer->clone();
447 
448     if (m_track_entry)
449       GetChild<KaxCodecPrivate>(*m_track_entry).CopyBuffer(static_cast<binary *>(m_hcodec_private->get_buffer()), m_hcodec_private->get_size());
450 
451   } else
452     m_hcodec_private.reset();
453 }
454 
455 void
set_codec_name(std::string const & name)456 generic_packetizer_c::set_codec_name(std::string const &name) {
457   m_hcodec_name = name;
458   if (m_track_entry && !name.empty())
459     GetChild<KaxCodecName>(m_track_entry).SetValueUTF8(m_hcodec_name);
460 }
461 
462 void
set_track_default_duration(int64_t def_dur,bool force)463 generic_packetizer_c::set_track_default_duration(int64_t def_dur,
464                                                  bool force) {
465   if (!force && m_default_duration_forced)
466     return;
467 
468   m_htrack_default_duration = mtx::to_int(m_ti.m_tcsync.factor * def_dur);
469 
470   if (m_track_entry) {
471     if (m_htrack_default_duration)
472       GetChild<KaxTrackDefaultDuration>(m_track_entry).SetValue(m_htrack_default_duration);
473     else
474       DeleteChildren<KaxTrackDefaultDuration>(m_track_entry);
475   }
476 }
477 
478 void
set_track_max_additionals(int max_add_block_ids)479 generic_packetizer_c::set_track_max_additionals(int max_add_block_ids) {
480   m_htrack_max_add_block_ids = max_add_block_ids;
481   if (m_track_entry)
482     GetChild<KaxMaxBlockAdditionID>(m_track_entry).SetValue(max_add_block_ids);
483 }
484 
485 int64_t
get_track_default_duration() const486 generic_packetizer_c::get_track_default_duration()
487   const {
488   return m_htrack_default_duration;
489 }
490 
491 void
set_track_default_flag(bool default_track)492 generic_packetizer_c::set_track_default_flag(bool default_track) {
493   m_ti.m_default_track = default_track;
494   if (m_track_entry)
495     GetChild<KaxTrackFlagDefault>(m_track_entry).SetValue(default_track ? 1 : 0);
496 }
497 
498 void
set_track_forced_flag(bool forced_track)499 generic_packetizer_c::set_track_forced_flag(bool forced_track) {
500   m_ti.m_forced_track = forced_track;
501   if (m_track_entry)
502     GetChild<KaxTrackFlagForced>(m_track_entry).SetValue(forced_track ? 1 : 0);
503 }
504 
505 void
set_track_enabled_flag(bool enabled_track)506 generic_packetizer_c::set_track_enabled_flag(bool enabled_track) {
507   m_ti.m_enabled_track = enabled_track;
508   if (m_track_entry)
509     GetChild<KaxTrackFlagEnabled>(m_track_entry).SetValue(enabled_track ? 1 : 0);
510 }
511 
512 void
set_hearing_impaired_flag(bool hearing_impaired_flag)513 generic_packetizer_c::set_hearing_impaired_flag(bool hearing_impaired_flag) {
514   m_ti.m_hearing_impaired_flag = hearing_impaired_flag;
515   if (m_track_entry)
516     GetChild<KaxFlagHearingImpaired>(m_track_entry).SetValue(hearing_impaired_flag ? 1 : 0);
517 }
518 
519 void
set_visual_impaired_flag(bool visual_impaired_flag)520 generic_packetizer_c::set_visual_impaired_flag(bool visual_impaired_flag) {
521   m_ti.m_visual_impaired_flag = visual_impaired_flag;
522   if (m_track_entry)
523     GetChild<KaxFlagVisualImpaired>(m_track_entry).SetValue(visual_impaired_flag ? 1 : 0);
524 }
525 
526 void
set_text_descriptions_flag(bool text_descriptions_flag)527 generic_packetizer_c::set_text_descriptions_flag(bool text_descriptions_flag) {
528   m_ti.m_text_descriptions_flag = text_descriptions_flag;
529   if (m_track_entry)
530     GetChild<KaxFlagTextDescriptions>(m_track_entry).SetValue(text_descriptions_flag ? 1 : 0);
531 }
532 
533 void
set_original_flag(bool original_flag)534 generic_packetizer_c::set_original_flag(bool original_flag) {
535   m_ti.m_original_flag = original_flag;
536   if (m_track_entry)
537     GetChild<KaxFlagOriginal>(m_track_entry).SetValue(original_flag ? 1 : 0);
538 }
539 
540 void
set_commentary_flag(bool commentary_flag)541 generic_packetizer_c::set_commentary_flag(bool commentary_flag) {
542   m_ti.m_commentary_flag = commentary_flag;
543   if (m_track_entry)
544     GetChild<KaxFlagCommentary>(m_track_entry).SetValue(commentary_flag ? 1 : 0);
545 }
546 
547 void
set_track_seek_pre_roll(timestamp_c const & seek_pre_roll)548 generic_packetizer_c::set_track_seek_pre_roll(timestamp_c const &seek_pre_roll) {
549   m_seek_pre_roll = seek_pre_roll;
550   if (m_track_entry)
551     GetChild<KaxSeekPreRoll>(m_track_entry).SetValue(seek_pre_roll.to_ns());
552 }
553 
554 void
set_codec_delay(timestamp_c const & codec_delay)555 generic_packetizer_c::set_codec_delay(timestamp_c const &codec_delay) {
556   m_codec_delay = codec_delay;
557   if (m_track_entry)
558     GetChild<KaxCodecDelay>(m_track_entry).SetValue(codec_delay.to_ns());
559 }
560 
561 void
set_audio_sampling_freq(double freq)562 generic_packetizer_c::set_audio_sampling_freq(double freq) {
563   m_haudio_sampling_freq = freq;
564   if (m_track_entry)
565     GetChild<KaxAudioSamplingFreq>(GetChild<KaxTrackAudio>(m_track_entry)).SetValue(m_haudio_sampling_freq);
566 }
567 
568 void
set_audio_output_sampling_freq(double freq)569 generic_packetizer_c::set_audio_output_sampling_freq(double freq) {
570   m_haudio_output_sampling_freq = freq;
571   if (m_track_entry)
572     GetChild<KaxAudioOutputSamplingFreq>(GetChild<KaxTrackAudio>(m_track_entry)).SetValue(m_haudio_output_sampling_freq);
573 }
574 
575 void
set_audio_channels(int channels)576 generic_packetizer_c::set_audio_channels(int channels) {
577   m_haudio_channels = channels;
578   if (m_track_entry)
579     GetChild<KaxAudioChannels>(GetChild<KaxTrackAudio>(*m_track_entry)).SetValue(m_haudio_channels);
580 }
581 
582 void
set_audio_bit_depth(int bit_depth)583 generic_packetizer_c::set_audio_bit_depth(int bit_depth) {
584   m_haudio_bit_depth = bit_depth;
585   if (m_track_entry)
586     GetChild<KaxAudioBitDepth>(GetChild<KaxTrackAudio>(*m_track_entry)).SetValue(m_haudio_bit_depth);
587 }
588 
589 void
set_video_interlaced_flag(bool interlaced)590 generic_packetizer_c::set_video_interlaced_flag(bool interlaced) {
591   m_hvideo_interlaced_flag = interlaced ? 1 : 0;
592   if (m_track_entry)
593     GetChild<KaxVideoFlagInterlaced>(GetChild<KaxTrackVideo>(*m_track_entry)).SetValue(m_hvideo_interlaced_flag);
594 }
595 
596 void
set_video_pixel_width(int width)597 generic_packetizer_c::set_video_pixel_width(int width) {
598   m_hvideo_pixel_width = width;
599   if (m_track_entry)
600     GetChild<KaxVideoPixelWidth>(GetChild<KaxTrackVideo>(*m_track_entry)).SetValue(m_hvideo_pixel_width);
601 }
602 
603 void
set_video_pixel_height(int height)604 generic_packetizer_c::set_video_pixel_height(int height) {
605   m_hvideo_pixel_height = height;
606   if (m_track_entry)
607     GetChild<KaxVideoPixelHeight>(GetChild<KaxTrackVideo>(*m_track_entry)).SetValue(m_hvideo_pixel_height);
608 }
609 
610 void
set_video_pixel_dimensions(int width,int height)611 generic_packetizer_c::set_video_pixel_dimensions(int width,
612                                                  int height) {
613   set_video_pixel_width(width);
614   set_video_pixel_height(height);
615 }
616 
617 void
set_video_display_width(int width)618 generic_packetizer_c::set_video_display_width(int width) {
619   m_hvideo_display_width = width;
620   if (m_track_entry)
621     GetChild<KaxVideoDisplayWidth>(GetChild<KaxTrackVideo>(*m_track_entry)).SetValue(m_hvideo_display_width);
622 }
623 
624 void
set_video_display_height(int height)625 generic_packetizer_c::set_video_display_height(int height) {
626   m_hvideo_display_height = height;
627   if (m_track_entry)
628     GetChild<KaxVideoDisplayHeight>(GetChild<KaxTrackVideo>(*m_track_entry)).SetValue(m_hvideo_display_height);
629 }
630 
631 void
set_video_display_unit(int unit)632 generic_packetizer_c::set_video_display_unit(int unit) {
633   m_hvideo_display_unit = unit;
634   if (   m_track_entry
635       && (   (unit != ddu_pixels)
636           || (unit != FindChildValue<KaxVideoDisplayUnit>(GetChild<KaxTrackVideo>(*m_track_entry), ddu_pixels))))
637     GetChild<KaxVideoDisplayUnit>(GetChild<KaxTrackVideo>(*m_track_entry)).SetValue(m_hvideo_display_unit);
638 }
639 
640 void
set_video_display_dimensions(int width,int height,int unit,option_source_e source)641 generic_packetizer_c::set_video_display_dimensions(int width,
642                                                    int height,
643                                                    int unit,
644                                                    option_source_e source) {
645   if (display_dimensions_or_aspect_ratio_set() && (m_ti.m_display_dimensions_source >= source))
646     return;
647 
648   m_ti.m_display_width             = width;
649   m_ti.m_display_height            = height;
650   m_ti.m_display_unit              = unit;
651   m_ti.m_display_dimensions_source = source;
652   m_ti.m_display_dimensions_given  = true;
653   m_ti.m_aspect_ratio_given        = false;
654 
655   set_video_display_width(width);
656   set_video_display_height(height);
657   set_video_display_unit(unit);
658 }
659 
660 void
set_video_aspect_ratio(double aspect_ratio,bool is_factor,option_source_e source)661 generic_packetizer_c::set_video_aspect_ratio(double aspect_ratio,
662                                              bool is_factor,
663                                              option_source_e source) {
664   if (display_dimensions_or_aspect_ratio_set() && (m_ti.m_display_dimensions_source >= source))
665     return;
666 
667   m_ti.m_aspect_ratio              = aspect_ratio;
668   m_ti.m_aspect_ratio_is_factor    = is_factor;
669   m_ti.m_display_unit              = ddu_pixels;
670   m_ti.m_display_dimensions_source = source;
671   m_ti.m_display_dimensions_given  = false;
672   m_ti.m_aspect_ratio_given        = true;
673 }
674 
675 void
set_language(mtx::bcp47::language_c const & language)676 generic_packetizer_c::set_language(mtx::bcp47::language_c const &language) {
677   if (!language.is_valid())
678     return;
679 
680   m_ti.m_language = language;
681 
682   if (!m_track_entry || !language.is_valid())
683     return;
684 
685   if (language.has_valid_iso639_2_code())
686     GetChild<KaxTrackLanguage>(m_track_entry).SetValue(language.get_iso639_alpha_3_code());
687   if (!mtx::bcp47::language_c::is_disabled())
688     GetChild<KaxLanguageIETF>(m_track_entry).SetValue(language.format());
689 }
690 
691 void
set_video_pixel_cropping(int left,int top,int right,int bottom,option_source_e source)692 generic_packetizer_c::set_video_pixel_cropping(int left,
693                                                int top,
694                                                int right,
695                                                int bottom,
696                                                option_source_e source) {
697   m_ti.m_pixel_cropping.set(pixel_crop_t{left, top, right, bottom}, source);
698 
699   if (m_track_entry) {
700     KaxTrackVideo &video = GetChild<KaxTrackVideo>(m_track_entry);
701     auto crop            = m_ti.m_pixel_cropping.get();
702 
703     GetChild<KaxVideoPixelCropLeft  >(video).SetValue(crop.left);
704     GetChild<KaxVideoPixelCropTop   >(video).SetValue(crop.top);
705     GetChild<KaxVideoPixelCropRight >(video).SetValue(crop.right);
706     GetChild<KaxVideoPixelCropBottom>(video).SetValue(crop.bottom);
707   }
708 }
709 
710 void
set_video_colour_matrix(int matrix_index,option_source_e source)711 generic_packetizer_c::set_video_colour_matrix(int matrix_index,
712                                               option_source_e source) {
713   m_ti.m_colour_matrix_coeff.set(matrix_index, source);
714   if (   m_track_entry
715       && (matrix_index >= 0)
716       && (matrix_index <= 10)) {
717     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
718     auto &color = GetChild<KaxVideoColour>(video);
719     GetChild<KaxVideoColourMatrix>(color).SetValue(m_ti.m_colour_matrix_coeff.get());
720   }
721 }
722 
723 void
set_video_bits_per_channel(int num_bits,option_source_e source)724 generic_packetizer_c::set_video_bits_per_channel(int num_bits,
725                                                  option_source_e source) {
726   m_ti.m_bits_per_channel.set(num_bits, source);
727   if (m_track_entry && (num_bits >= 0)) {
728     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
729     auto &color = GetChild<KaxVideoColour>(video);
730     GetChild<KaxVideoBitsPerChannel>(color).SetValue(m_ti.m_bits_per_channel.get());
731   }
732 }
733 
734 void
set_video_chroma_subsample(const chroma_subsample_t & subsample,option_source_e source)735 generic_packetizer_c::set_video_chroma_subsample(const chroma_subsample_t &subsample,
736                                                  option_source_e source) {
737   m_ti.m_chroma_subsample.set(chroma_subsample_t(subsample.hori, subsample.vert), source);
738   if (   m_track_entry
739       && (   (subsample.hori >= 0)
740           || (subsample.vert >= 0))) {
741     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
742     auto &color = GetChild<KaxVideoColour>(video);
743     if (subsample.hori >= 0)
744       GetChild<KaxVideoChromaSubsampHorz>(color).SetValue(subsample.hori);
745     if (subsample.vert >= 0)
746       GetChild<KaxVideoChromaSubsampVert>(color).SetValue(subsample.vert);
747   }
748 }
749 
750 void
set_video_cb_subsample(const cb_subsample_t & subsample,option_source_e source)751 generic_packetizer_c::set_video_cb_subsample(const cb_subsample_t &subsample,
752                                              option_source_e source) {
753   m_ti.m_cb_subsample.set(cb_subsample_t(subsample.hori, subsample.vert), source);
754   if (   m_track_entry
755       && (   (subsample.hori >= 0)
756           || (subsample.vert >= 0))) {
757     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
758     auto &color = GetChild<KaxVideoColour>(video);
759     if (subsample.hori >= 0)
760       GetChild<KaxVideoCbSubsampHorz>(color).SetValue(subsample.hori);
761     if (subsample.vert >= 0)
762       GetChild<KaxVideoCbSubsampVert>(color).SetValue(subsample.vert);
763   }
764 }
765 
766 void
set_video_chroma_siting(const chroma_siting_t & siting,option_source_e source)767 generic_packetizer_c::set_video_chroma_siting(const chroma_siting_t &siting,
768                                               option_source_e source) {
769   m_ti.m_chroma_siting.set(chroma_siting_t(siting.hori, siting.vert), source);
770   if (   m_track_entry
771       && (   ((siting.hori >= 0) && (siting.hori <= 2))
772           || ((siting.vert >= 0) && (siting.hori <= 2)))) {
773     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
774     auto &color = GetChild<KaxVideoColour>(video);
775     if ((siting.hori >= 0) && (siting.hori <= 2))
776       GetChild<KaxVideoChromaSitHorz>(color).SetValue(siting.hori);
777     if ((siting.vert >= 0) && (siting.hori <= 2))
778       GetChild<KaxVideoChromaSitVert>(color).SetValue(siting.vert);
779   }
780 }
781 
782 void
set_video_colour_range(int range,option_source_e source)783 generic_packetizer_c::set_video_colour_range(int range,
784                                              option_source_e source) {
785   m_ti.m_colour_range.set(range, source);
786   if (m_track_entry && (range >= 0) && (range <= 3)) {
787     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
788     auto &color = GetChild<KaxVideoColour>(video);
789     GetChild<KaxVideoColourRange>(color).SetValue(range);
790   }
791 }
792 
793 void
set_video_colour_transfer_character(int transfer_index,option_source_e source)794 generic_packetizer_c::set_video_colour_transfer_character(int transfer_index,
795                                                           option_source_e source) {
796   m_ti.m_colour_transfer.set(transfer_index, source);
797   if (m_track_entry && (transfer_index >= 0) && (transfer_index <= 18)) {
798     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
799     auto &color = GetChild<KaxVideoColour>(video);
800     GetChild<KaxVideoColourTransferCharacter>(color).SetValue(transfer_index);
801   }
802 }
803 
804 void
set_video_colour_primaries(int primary_index,option_source_e source)805 generic_packetizer_c::set_video_colour_primaries(int primary_index,
806                                                  option_source_e source) {
807   m_ti.m_colour_primaries.set(primary_index, source);
808   if (     m_track_entry
809       && (primary_index >= 0)
810       && ((primary_index <= 10) || (primary_index == 22))) {
811     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
812     auto &color = GetChild<KaxVideoColour>(video);
813     GetChild<KaxVideoColourPrimaries>(color).SetValue(primary_index);
814   }
815 }
816 
817 void
set_video_max_cll(int max_cll,option_source_e source)818 generic_packetizer_c::set_video_max_cll(int max_cll,
819                                         option_source_e source) {
820   m_ti.m_max_cll.set(max_cll, source);
821   if (m_track_entry && (max_cll >= 0)) {
822     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
823     auto &color = GetChild<KaxVideoColour>(video);
824     GetChild<KaxVideoColourMaxCLL>(color).SetValue(max_cll);
825   }
826 }
827 
828 void
set_video_max_fall(int max_fall,option_source_e source)829 generic_packetizer_c::set_video_max_fall(int max_fall,
830                                          option_source_e source) {
831   m_ti.m_max_fall.set(max_fall, source);
832   if (m_track_entry && (max_fall >= 0)) {
833     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
834     auto &color = GetChild<KaxVideoColour>(video);
835     GetChild<KaxVideoColourMaxFALL>(color).SetValue(max_fall);
836   }
837 }
838 
839 void
set_video_chroma_coordinates(chroma_coordinates_t const & coordinates,option_source_e source)840 generic_packetizer_c::set_video_chroma_coordinates(chroma_coordinates_t const &coordinates,
841                                                    option_source_e source) {
842   m_ti.m_chroma_coordinates.set(coordinates, source);
843   if (   m_track_entry
844       && (   ((coordinates.red_x   >= 0) && (coordinates.red_x   <= 1))
845           || ((coordinates.red_y   >= 0) && (coordinates.red_y   <= 1))
846           || ((coordinates.green_x >= 0) && (coordinates.green_x <= 1))
847           || ((coordinates.green_y >= 0) && (coordinates.green_y <= 1))
848           || ((coordinates.blue_x  >= 0) && (coordinates.blue_x  <= 1))
849           || ((coordinates.blue_y  >= 0) && (coordinates.blue_y  <= 1)))) {
850     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
851     auto &color = GetChild<KaxVideoColour>(video);
852     if ((coordinates.red_x >= 0) && (coordinates.red_x <= 1))
853       GetChild<KaxVideoRChromaX>(color).SetValue(coordinates.red_x);
854     if ((coordinates.red_y >= 0) && (coordinates.red_y <= 1))
855       GetChild<KaxVideoRChromaY>(color).SetValue(coordinates.red_y);
856     if ((coordinates.green_x >= 0) && (coordinates.green_x <= 1))
857       GetChild<KaxVideoGChromaX>(color).SetValue(coordinates.green_x);
858     if ((coordinates.green_y >= 0) && (coordinates.green_y <= 1))
859       GetChild<KaxVideoGChromaY>(color).SetValue(coordinates.green_y);
860     if ((coordinates.blue_x >= 0) && (coordinates.blue_x <= 1))
861       GetChild<KaxVideoBChromaX>(color).SetValue(coordinates.blue_x);
862     if ((coordinates.blue_y >= 0) && (coordinates.blue_y <= 1))
863       GetChild<KaxVideoBChromaY>(color).SetValue(coordinates.blue_y);
864   }
865 }
866 
867 void
set_video_white_colour_coordinates(white_colour_coordinates_t const & coordinates,option_source_e source)868 generic_packetizer_c::set_video_white_colour_coordinates(white_colour_coordinates_t const &coordinates,
869                                                          option_source_e source) {
870   m_ti.m_white_coordinates.set(white_colour_coordinates_t(coordinates.x, coordinates.y), source);
871   if (   m_track_entry
872       && (   ((coordinates.x >= 0) && (coordinates.x <= 1))
873           || ((coordinates.y >= 0) && (coordinates.y <= 1)))) {
874     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
875     auto &color = GetChild<KaxVideoColour>(video);
876     if ((coordinates.x >= 0) && (coordinates.x <= 1))
877       GetChild<KaxVideoWhitePointChromaX>(color).SetValue(coordinates.x);
878     if ((coordinates.y >= 0) && (coordinates.y <= 1))
879       GetChild<KaxVideoWhitePointChromaY>(color).SetValue(coordinates.y);
880   }
881 }
882 
883 void
set_video_max_luminance(double luminance,option_source_e source)884 generic_packetizer_c::set_video_max_luminance(double luminance,
885                                               option_source_e source) {
886   m_ti.m_max_luminance.set(luminance, source);
887   if (   m_track_entry
888       && (luminance >= 0)
889       && (luminance <= 9999.99)) {
890     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
891     auto &color = GetChild<KaxVideoColour>(video);
892     GetChild<KaxVideoLuminanceMax>(color).SetValue(luminance);
893   }
894 }
895 
896 void
set_video_min_luminance(double luminance,option_source_e source)897 generic_packetizer_c::set_video_min_luminance(double luminance,
898                                               option_source_e source) {
899   m_ti.m_min_luminance.set(luminance, source);
900   if (m_track_entry && (luminance >= 0) && (luminance <= 999.9999)) {
901     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
902     auto &color = GetChild<KaxVideoColour>(video);
903     GetChild<KaxVideoLuminanceMin>(color).SetValue(luminance);
904   }
905 }
906 
907 void
set_video_projection_type(uint64_t value,option_source_e source)908 generic_packetizer_c::set_video_projection_type(uint64_t value,
909                                                 option_source_e source) {
910   m_ti.m_projection_type.set(value, source);
911   if (m_track_entry) {
912     auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(GetChild<KaxTrackVideo>(m_track_entry));
913     GetChild<KaxVideoProjectionType>(projection).SetValue(value);
914   }
915 }
916 
917 void
set_video_projection_private(memory_cptr const & value,option_source_e source)918 generic_packetizer_c::set_video_projection_private(memory_cptr const &value,
919                                                    option_source_e source) {
920   m_ti.m_projection_private.set(value, source);
921   if (m_track_entry && value) {
922     auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(GetChild<KaxTrackVideo>(m_track_entry));
923     if (value->get_size())
924       GetChild<KaxVideoProjectionPrivate>(projection).CopyBuffer(value->get_buffer(), value->get_size());
925     else
926       DeleteChildren<KaxVideoProjectionPrivate>(projection);
927   }
928 }
929 
930 void
set_video_projection_pose_yaw(double value,option_source_e source)931 generic_packetizer_c::set_video_projection_pose_yaw(double value,
932                                                     option_source_e source) {
933   m_ti.m_projection_pose_yaw.set(value, source);
934   if (m_track_entry && (value >= 0)) {
935     auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(GetChild<KaxTrackVideo>(m_track_entry));
936     GetChild<KaxVideoProjectionPoseYaw>(projection).SetValue(value);
937   }
938 }
939 
940 void
set_video_projection_pose_pitch(double value,option_source_e source)941 generic_packetizer_c::set_video_projection_pose_pitch(double value,
942                                                       option_source_e source) {
943   m_ti.m_projection_pose_pitch.set(value, source);
944   if (m_track_entry && (value >= 0)) {
945     auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(GetChild<KaxTrackVideo>(m_track_entry));
946     GetChild<KaxVideoProjectionPosePitch>(projection).SetValue(value);
947   }
948 }
949 
950 void
set_video_projection_pose_roll(double value,option_source_e source)951 generic_packetizer_c::set_video_projection_pose_roll(double value,
952                                                      option_source_e source) {
953   m_ti.m_projection_pose_roll.set(value, source);
954   if (m_track_entry && (value >= 0)) {
955     auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(GetChild<KaxTrackVideo>(m_track_entry));
956     GetChild<KaxVideoProjectionPoseRoll>(projection).SetValue(value);
957   }
958 }
959 
960 void
set_video_pixel_cropping(const pixel_crop_t & cropping,option_source_e source)961 generic_packetizer_c::set_video_pixel_cropping(const pixel_crop_t &cropping,
962                                                option_source_e source) {
963   set_video_pixel_cropping(cropping.left, cropping.top, cropping.right, cropping.bottom, source);
964 }
965 
966 void
set_video_field_order(uint64_t order,option_source_e source)967 generic_packetizer_c::set_video_field_order(uint64_t order,
968                                             option_source_e source) {
969   m_ti.m_field_order.set(order, source);
970   if (m_track_entry && m_ti.m_field_order) {
971     auto &video = GetChild<KaxTrackVideo>(m_track_entry);
972     GetChild<KaxVideoFieldOrder>(video).SetValue(m_ti.m_field_order.get());
973   }
974 }
975 
976 void
set_video_stereo_mode(stereo_mode_c::mode stereo_mode,option_source_e source)977 generic_packetizer_c::set_video_stereo_mode(stereo_mode_c::mode stereo_mode,
978                                             option_source_e source) {
979   m_ti.m_stereo_mode.set(stereo_mode, source);
980 
981   if (m_track_entry && (stereo_mode_c::unspecified != m_ti.m_stereo_mode.get()))
982     set_video_stereo_mode_impl(GetChild<KaxTrackVideo>(*m_track_entry), m_ti.m_stereo_mode.get());
983 }
984 
985 void
set_video_stereo_mode_impl(EbmlMaster & video,stereo_mode_c::mode stereo_mode)986 generic_packetizer_c::set_video_stereo_mode_impl(EbmlMaster &video,
987                                                  stereo_mode_c::mode stereo_mode) {
988   GetChild<KaxVideoStereoMode>(video).SetValue(stereo_mode);
989 }
990 
991 void
set_video_colour_space(memory_cptr const & value,option_source_e source)992 generic_packetizer_c::set_video_colour_space(memory_cptr const &value,
993                                              option_source_e source) {
994   m_ti.m_colour_space.set(value, source);
995 
996   if (m_track_entry && value && value->get_size())
997     GetChild<KaxVideoColourSpace>(m_track_entry).CopyBuffer(value->get_buffer(), value->get_size());
998 }
999 
1000 void
set_block_addition_mappings(std::vector<block_addition_mapping_t> const & mappings)1001 generic_packetizer_c::set_block_addition_mappings(std::vector<block_addition_mapping_t> const &mappings) {
1002   m_block_addition_mappings = mappings;
1003   apply_block_addition_mappings();
1004 }
1005 
1006 void
set_headers()1007 generic_packetizer_c::set_headers() {
1008   if (0 < m_connected_to) {
1009     mxerror(fmt::format("generic_packetizer_c::set_headers(): connected_to > 0 (type: {0}). {1}\n", typeid(*this).name(), BUGMSG));
1010     return;
1011   }
1012 
1013   bool found = false;
1014   size_t idx;
1015   for (idx = 0; ptzrs_in_header_order.size() > idx; ++idx)
1016     if (this == ptzrs_in_header_order[idx]) {
1017       found = true;
1018       break;
1019     }
1020 
1021   if (!found)
1022     ptzrs_in_header_order.push_back(this);
1023 
1024   if (!m_track_entry) {
1025     m_track_entry    = !g_kax_last_entry ? &GetChild<KaxTrackEntry>(*g_kax_tracks) : &GetNextChild<KaxTrackEntry>(*g_kax_tracks, *g_kax_last_entry);
1026     g_kax_last_entry = m_track_entry;
1027     m_track_entry->SetGlobalTimecodeScale((int64_t)g_timestamp_scale);
1028   }
1029 
1030   GetChild<KaxTrackNumber>(m_track_entry).SetValue(m_hserialno);
1031 
1032   if (0 == m_huid)
1033     m_huid = create_unique_number(UNIQUE_TRACK_IDS);
1034 
1035   GetChild<KaxTrackUID>(m_track_entry).SetValue(m_huid);
1036 
1037   if (-1 != m_htrack_type)
1038     GetChild<KaxTrackType>(m_track_entry).SetValue(m_htrack_type);
1039 
1040   if (!m_hcodec_id.empty())
1041     GetChild<KaxCodecID>(m_track_entry).SetValue(m_hcodec_id);
1042 
1043   if (m_hcodec_private)
1044     GetChild<KaxCodecPrivate>(*m_track_entry).CopyBuffer(static_cast<binary *>(m_hcodec_private->get_buffer()), m_hcodec_private->get_size());
1045 
1046   if (!m_hcodec_name.empty())
1047     GetChild<KaxCodecName>(m_track_entry).SetValueUTF8(m_hcodec_name);
1048 
1049   if (!outputting_webm()) {
1050     if (-1 != m_htrack_max_add_block_ids)
1051       GetChild<KaxMaxBlockAdditionID>(m_track_entry).SetValue(m_htrack_max_add_block_ids);
1052   }
1053 
1054   if (m_timestamp_factory)
1055     m_htrack_default_duration = (int64_t)m_timestamp_factory->get_default_duration(m_htrack_default_duration);
1056   if (-1.0 != m_htrack_default_duration)
1057     GetChild<KaxTrackDefaultDuration>(m_track_entry).SetValue(m_htrack_default_duration);
1058 
1059   idx = track_type_to_deftrack_type(m_htrack_type);
1060 
1061   auto iso639_alpha_3_code = m_ti.m_language.has_valid_iso639_2_code() ? m_ti.m_language.get_iso639_alpha_3_code() : g_default_language.has_valid_iso639_2_code() ? g_default_language.get_iso639_alpha_3_code() : "und"s;
1062   auto language            = m_ti.m_language.is_valid()                ? m_ti.m_language                           : g_default_language;
1063   GetChild<KaxTrackLanguage>(m_track_entry).SetValue(iso639_alpha_3_code);
1064   if (!mtx::bcp47::language_c::is_disabled())
1065     GetChild<KaxLanguageIETF>(m_track_entry).SetValue(language.format());
1066 
1067   if (!m_ti.m_track_name.empty())
1068     GetChild<KaxTrackName>(m_track_entry).SetValueUTF8(m_ti.m_track_name);
1069 
1070   if (m_ti.m_default_track.has_value())
1071     GetChild<KaxTrackFlagDefault>(m_track_entry).SetValue(m_ti.m_default_track.value() ? 1 : 0);
1072 
1073   if (m_ti.m_forced_track.has_value())
1074     GetChild<KaxTrackFlagForced>(m_track_entry).SetValue(m_ti.m_forced_track.value() ? 1 : 0);
1075 
1076   if (m_ti.m_enabled_track.has_value())
1077     GetChild<KaxTrackFlagEnabled>(m_track_entry).SetValue(m_ti.m_enabled_track.value() ? 1 : 0);
1078 
1079   if (m_ti.m_hearing_impaired_flag.has_value())
1080     GetChild<KaxFlagHearingImpaired>(m_track_entry).SetValue(m_ti.m_hearing_impaired_flag.value() ? 1 : 0);
1081 
1082   if (m_ti.m_visual_impaired_flag.has_value())
1083     GetChild<KaxFlagVisualImpaired>(m_track_entry).SetValue(m_ti.m_visual_impaired_flag.value() ? 1 : 0);
1084 
1085   if (m_ti.m_text_descriptions_flag.has_value())
1086     GetChild<KaxFlagTextDescriptions>(m_track_entry).SetValue(m_ti.m_text_descriptions_flag.value() ? 1 : 0);
1087 
1088   if (m_ti.m_original_flag.has_value())
1089     GetChild<KaxFlagOriginal>(m_track_entry).SetValue(m_ti.m_original_flag.value() ? 1 : 0);
1090 
1091   if (m_ti.m_commentary_flag.has_value())
1092     GetChild<KaxFlagCommentary>(m_track_entry).SetValue(m_ti.m_commentary_flag.value() ? 1 : 0);
1093 
1094   if (m_seek_pre_roll.valid())
1095     GetChild<KaxSeekPreRoll>(m_track_entry).SetValue(m_seek_pre_roll.to_ns());
1096 
1097   if (m_codec_delay.valid())
1098     GetChild<KaxCodecDelay>(m_track_entry).SetValue(m_codec_delay.to_ns());
1099 
1100   if (track_video == m_htrack_type) {
1101     KaxTrackVideo &video = GetChild<KaxTrackVideo>(m_track_entry);
1102 
1103     if (-1 != m_hvideo_interlaced_flag)
1104       set_video_interlaced_flag(m_hvideo_interlaced_flag != 0);
1105 
1106     if ((-1 != m_hvideo_pixel_height) && (-1 != m_hvideo_pixel_width)) {
1107       if ((-1 == m_hvideo_display_width) || (-1 == m_hvideo_display_height) || m_ti.m_aspect_ratio_given || m_ti.m_display_dimensions_given) {
1108         if (m_ti.m_display_dimensions_given) {
1109           m_hvideo_display_width  = m_ti.m_display_width;
1110           m_hvideo_display_height = m_ti.m_display_height;
1111 
1112         } else {
1113           if (!m_ti.m_aspect_ratio_given)
1114             m_ti.m_aspect_ratio = static_cast<double>(m_hvideo_pixel_width)                       / m_hvideo_pixel_height;
1115 
1116           else if (m_ti.m_aspect_ratio_is_factor)
1117             m_ti.m_aspect_ratio = static_cast<double>(m_hvideo_pixel_width) * m_ti.m_aspect_ratio / m_hvideo_pixel_height;
1118 
1119           if (m_ti.m_aspect_ratio > (static_cast<double>(m_hvideo_pixel_width) / m_hvideo_pixel_height)) {
1120             m_hvideo_display_width  = std::llround(m_hvideo_pixel_height * m_ti.m_aspect_ratio);
1121             m_hvideo_display_height = m_hvideo_pixel_height;
1122 
1123           } else {
1124             m_hvideo_display_width  = m_hvideo_pixel_width;
1125             m_hvideo_display_height = std::llround(m_hvideo_pixel_width / m_ti.m_aspect_ratio);
1126           }
1127         }
1128       }
1129 
1130       GetChild<KaxVideoPixelWidth   >(video).SetValue(m_hvideo_pixel_width);
1131       GetChild<KaxVideoPixelHeight  >(video).SetValue(m_hvideo_pixel_height);
1132 
1133       GetChild<KaxVideoDisplayWidth >(video).SetValue(m_hvideo_display_width);
1134       GetChild<KaxVideoDisplayHeight>(video).SetValue(m_hvideo_display_height);
1135 
1136       GetChild<KaxVideoDisplayWidth >(video).SetDefaultSize(4);
1137       GetChild<KaxVideoDisplayHeight>(video).SetDefaultSize(4);
1138 
1139       if (m_hvideo_display_unit != ddu_pixels)
1140         GetChild<KaxVideoDisplayUnit>(video).SetValue(m_hvideo_display_unit);
1141 
1142       if (m_ti.m_colour_space)
1143         GetChild<KaxVideoColourSpace>(video).CopyBuffer(m_ti.m_colour_space.get()->get_buffer(), m_ti.m_colour_space.get()->get_size());
1144 
1145       if (m_ti.m_pixel_cropping) {
1146         auto crop = m_ti.m_pixel_cropping.get();
1147         GetChild<KaxVideoPixelCropLeft  >(video).SetValue(crop.left);
1148         GetChild<KaxVideoPixelCropTop   >(video).SetValue(crop.top);
1149         GetChild<KaxVideoPixelCropRight >(video).SetValue(crop.right);
1150         GetChild<KaxVideoPixelCropBottom>(video).SetValue(crop.bottom);
1151       }
1152 
1153       if (m_ti.m_colour_matrix_coeff) {
1154         int colour_matrix = m_ti.m_colour_matrix_coeff.get();
1155         auto &colour      = GetChild<KaxVideoColour>(video);
1156         GetChild<KaxVideoColourMatrix>(colour).SetValue(colour_matrix);
1157       }
1158 
1159       if (m_ti.m_bits_per_channel) {
1160         int bits     = m_ti.m_bits_per_channel.get();
1161         auto &colour = GetChild<KaxVideoColour>(video);
1162         GetChild<KaxVideoBitsPerChannel>(colour).SetValue(bits);
1163       }
1164 
1165       if (m_ti.m_chroma_subsample) {
1166         auto const &subsample = m_ti.m_chroma_subsample.get();
1167         auto &colour          = GetChild<KaxVideoColour>(video);
1168         GetChild<KaxVideoChromaSubsampHorz>(colour).SetValue(subsample.hori);
1169         GetChild<KaxVideoChromaSubsampVert>(colour).SetValue(subsample.vert);
1170       }
1171 
1172       if (m_ti.m_cb_subsample) {
1173         auto const &subsample = m_ti.m_cb_subsample.get();
1174         auto &colour          = GetChild<KaxVideoColour>(video);
1175         GetChild<KaxVideoCbSubsampHorz>(colour).SetValue(subsample.hori);
1176         GetChild<KaxVideoCbSubsampVert>(colour).SetValue(subsample.vert);
1177       }
1178 
1179       if (m_ti.m_chroma_siting) {
1180         auto const &siting = m_ti.m_chroma_siting.get();
1181         auto &colour       = GetChild<KaxVideoColour>(video);
1182         GetChild<KaxVideoChromaSitHorz>(colour).SetValue(siting.hori);
1183         GetChild<KaxVideoChromaSitVert>(colour).SetValue(siting.vert);
1184       }
1185 
1186       if (m_ti.m_colour_range) {
1187         int range_index = m_ti.m_colour_range.get();
1188         auto &colour    = GetChild<KaxVideoColour>(video);
1189         GetChild<KaxVideoColourRange>(colour).SetValue(range_index);
1190       }
1191 
1192       if (m_ti.m_colour_transfer) {
1193         int transfer_index = m_ti.m_colour_transfer.get();
1194         auto &colour       = GetChild<KaxVideoColour>(video);
1195         GetChild<KaxVideoColourTransferCharacter>(colour).SetValue(transfer_index);
1196       }
1197 
1198       if (m_ti.m_colour_primaries) {
1199         int primary_index = m_ti.m_colour_primaries.get();
1200         auto &colour      = GetChild<KaxVideoColour>(video);
1201         GetChild<KaxVideoColourPrimaries>(colour).SetValue(primary_index);
1202       }
1203 
1204       if (m_ti.m_max_cll) {
1205         int cll_index = m_ti.m_max_cll.get();
1206         auto &colour  = GetChild<KaxVideoColour>(video);
1207         GetChild<KaxVideoColourMaxCLL>(colour).SetValue(cll_index);
1208       }
1209 
1210       if (m_ti.m_max_fall) {
1211         int fall_index = m_ti.m_max_fall.get();
1212         auto &colour   = GetChild<KaxVideoColour>(video);
1213         GetChild<KaxVideoColourMaxFALL>(colour).SetValue(fall_index);
1214       }
1215 
1216       if (m_ti.m_chroma_coordinates) {
1217         auto const &coordinates = m_ti.m_chroma_coordinates.get();
1218         auto &colour            = GetChild<KaxVideoColour>(video);
1219         auto &master_meta       = GetChild<KaxVideoColourMasterMeta>(colour);
1220         GetChild<KaxVideoRChromaX>(master_meta).SetValue(coordinates.red_x);
1221         GetChild<KaxVideoRChromaY>(master_meta).SetValue(coordinates.red_y);
1222         GetChild<KaxVideoGChromaX>(master_meta).SetValue(coordinates.green_x);
1223         GetChild<KaxVideoGChromaY>(master_meta).SetValue(coordinates.green_y);
1224         GetChild<KaxVideoBChromaX>(master_meta).SetValue(coordinates.blue_x);
1225         GetChild<KaxVideoBChromaY>(master_meta).SetValue(coordinates.blue_y);
1226       }
1227 
1228       if (m_ti.m_white_coordinates) {
1229         auto const &coordinates = m_ti.m_white_coordinates.get();
1230         auto &colour            = GetChild<KaxVideoColour>(video);
1231         auto &master_meta       = GetChild<KaxVideoColourMasterMeta>(colour);
1232         GetChild<KaxVideoWhitePointChromaX>(master_meta).SetValue(coordinates.x);
1233         GetChild<KaxVideoWhitePointChromaY>(master_meta).SetValue(coordinates.y);
1234       }
1235 
1236       if (m_ti.m_max_luminance) {
1237         auto luminance    = m_ti.m_max_luminance.get();
1238         auto &colour      = GetChild<KaxVideoColour>(video);
1239         auto &master_meta = GetChild<KaxVideoColourMasterMeta>(colour);
1240         GetChild<KaxVideoLuminanceMax>(master_meta).SetValue(luminance);
1241       }
1242 
1243       if (m_ti.m_min_luminance) {
1244         auto luminance    = m_ti.m_min_luminance.get();
1245         auto &colour      = GetChild<KaxVideoColour>(video);
1246         auto &master_meta = GetChild<KaxVideoColourMasterMeta>(colour);
1247         GetChild<KaxVideoLuminanceMin>(master_meta).SetValue(luminance);
1248       }
1249 
1250       if (m_ti.m_projection_type) {
1251         auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(video);
1252         GetChild<KaxVideoProjectionType>(projection).SetValue(m_ti.m_projection_type.get());
1253       }
1254 
1255       if (m_ti.m_projection_private) {
1256         auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(video);
1257         GetChild<KaxVideoProjectionPrivate>(projection).CopyBuffer(m_ti.m_projection_private.get()->get_buffer(), m_ti.m_projection_private.get()->get_size());
1258       }
1259 
1260       if (m_ti.m_projection_pose_yaw) {
1261         auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(video);
1262         GetChild<KaxVideoProjectionPoseYaw>(projection).SetValue(m_ti.m_projection_pose_yaw.get());
1263       }
1264 
1265       if (m_ti.m_projection_pose_pitch) {
1266         auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(video);
1267         GetChild<KaxVideoProjectionPosePitch>(projection).SetValue(m_ti.m_projection_pose_pitch.get());
1268       }
1269 
1270       if (m_ti.m_projection_pose_roll) {
1271         auto &projection = GetChildEmptyIfNew<KaxVideoProjection>(video);
1272         GetChild<KaxVideoProjectionPoseRoll>(projection).SetValue(m_ti.m_projection_pose_roll.get());
1273       }
1274 
1275       if (m_ti.m_field_order)
1276         GetChild<KaxVideoFieldOrder>(video).SetValue(m_ti.m_field_order.get());
1277 
1278       if (m_ti.m_stereo_mode && (stereo_mode_c::unspecified != m_ti.m_stereo_mode.get()))
1279         set_video_stereo_mode_impl(video, m_ti.m_stereo_mode.get());
1280     }
1281 
1282   } else if (track_audio == m_htrack_type) {
1283     KaxTrackAudio &audio = GetChild<KaxTrackAudio>(m_track_entry);
1284 
1285     if (-1   != m_haudio_sampling_freq)
1286       GetChild<KaxAudioSamplingFreq>(audio).SetValue(m_haudio_sampling_freq);
1287 
1288     if (-1.0 != m_haudio_output_sampling_freq)
1289       GetChild<KaxAudioOutputSamplingFreq>(audio).SetValue(m_haudio_output_sampling_freq);
1290 
1291     if (-1   != m_haudio_channels)
1292       GetChild<KaxAudioChannels>(audio).SetValue(m_haudio_channels);
1293 
1294     if (-1   != m_haudio_bit_depth)
1295       GetChild<KaxAudioBitDepth>(audio).SetValue(m_haudio_bit_depth);
1296 
1297   } else if (track_buttons == m_htrack_type) {
1298     if ((-1 != m_hvideo_pixel_height) && (-1 != m_hvideo_pixel_width)) {
1299       KaxTrackVideo &video = GetChild<KaxTrackVideo>(m_track_entry);
1300 
1301       GetChild<KaxVideoPixelWidth >(video).SetValue(m_hvideo_pixel_width);
1302       GetChild<KaxVideoPixelHeight>(video).SetValue(m_hvideo_pixel_height);
1303     }
1304 
1305   }
1306 
1307   if ((COMPRESSION_UNSPECIFIED != m_hcompression) && (COMPRESSION_NONE != m_hcompression)) {
1308     KaxContentEncoding &c_encoding = GetChild<KaxContentEncoding>(GetChild<KaxContentEncodings>(m_track_entry));
1309 
1310     GetChild<KaxContentEncodingOrder>(c_encoding).SetValue(0); // First modification.
1311     GetChild<KaxContentEncodingType >(c_encoding).SetValue(0); // It's a compression.
1312     GetChild<KaxContentEncodingScope>(c_encoding).SetValue(1); // Only the frame contents have been compresed.
1313 
1314     m_compressor = compressor_c::create(m_hcompression);
1315     m_compressor->set_track_headers(c_encoding);
1316   }
1317 
1318   apply_block_addition_mappings();
1319 
1320   if (g_no_lacing)
1321     m_track_entry->EnableLacing(false);
1322 
1323   set_tag_track_uid();
1324   if (m_ti.m_tags) {
1325     while (m_ti.m_tags->ListSize() != 0) {
1326       add_tags(static_cast<KaxTag &>(*(*m_ti.m_tags)[0]));
1327       m_ti.m_tags->Remove(0);
1328     }
1329   }
1330 }
1331 
1332 void
apply_block_addition_mappings()1333 generic_packetizer_c::apply_block_addition_mappings() {
1334   if (!m_track_entry)
1335     return;
1336 
1337   DeleteChildren<KaxBlockAdditionMapping>(m_track_entry);
1338 
1339   for (auto const &mapping : m_block_addition_mappings) {
1340     if (!mapping.is_valid())
1341       continue;
1342 
1343     auto &kmapping = AddEmptyChild<KaxBlockAdditionMapping>(m_track_entry);
1344 
1345     if (!mapping.id_name.empty())
1346       GetChild<KaxBlockAddIDName>(kmapping).SetValue(mapping.id_name);
1347 
1348     if (mapping.id_type)
1349       GetChild<KaxBlockAddIDType>(kmapping).SetValue(*mapping.id_type);
1350 
1351     if (mapping.id_value)
1352       GetChild<KaxBlockAddIDValue>(kmapping).SetValue(*mapping.id_value);
1353 
1354     if (mapping.id_extra_data && mapping.id_extra_data->get_size())
1355       GetChild<KaxBlockAddIDExtraData>(kmapping).CopyBuffer(mapping.id_extra_data->get_buffer(), mapping.id_extra_data->get_size());
1356   }
1357 }
1358 
1359 void
fix_headers()1360 generic_packetizer_c::fix_headers() {
1361   m_track_entry->SetGlobalTimecodeScale((int64_t)g_timestamp_scale);
1362 }
1363 
1364 void
compress_packet(packet_t & packet)1365 generic_packetizer_c::compress_packet(packet_t &packet) {
1366   if (!m_compressor) {
1367     return;
1368   }
1369 
1370   try {
1371     packet.data = m_compressor->compress(packet.data);
1372     size_t i;
1373     for (i = 0; packet.data_adds.size() > i; ++i)
1374       packet.data_adds[i] = m_compressor->compress(packet.data_adds[i]);
1375 
1376   } catch (mtx::compression_x &e) {
1377     mxerror_tid(m_ti.m_fname, m_ti.m_id, fmt::format(Y("Compression failed: {0}\n"), e.error()));
1378   }
1379 }
1380 
1381 void
account_enqueued_bytes(packet_t & packet,int64_t factor)1382 generic_packetizer_c::account_enqueued_bytes(packet_t &packet,
1383                                              int64_t factor) {
1384   m_enqueued_bytes += packet.calculate_uncompressed_size() * factor;
1385 }
1386 
1387 void
add_packet(packet_cptr const & pack)1388 generic_packetizer_c::add_packet(packet_cptr const &pack) {
1389   if ((0 == m_num_packets) && m_ti.m_reset_timestamps)
1390     m_ti.m_tcsync.displacement = -pack->timestamp;
1391 
1392   ++m_num_packets;
1393 
1394   if (!m_reader->m_ptzr_first_packet)
1395     m_reader->m_ptzr_first_packet = this;
1396 
1397   // strip elements to be removed
1398   if (   (-1                     != m_htrack_max_add_block_ids)
1399       && (pack->data_adds.size()  > static_cast<size_t>(m_htrack_max_add_block_ids)))
1400     pack->data_adds.resize(m_htrack_max_add_block_ids);
1401 
1402   pack->data->take_ownership();
1403   for (auto &data_add : pack->data_adds)
1404     data_add->take_ownership();
1405 
1406   pack->source = this;
1407 
1408   // Although this is no longer triggered by "broken" B frames, it does not seem sensical at all
1409   if ((0 > pack->bref) && (0 <= pack->fref))
1410     std::swap(pack->bref, pack->fref);
1411 
1412   account_enqueued_bytes(*pack, +1);
1413 
1414   if (1 != m_connected_to)
1415     add_packet2(pack);
1416   else
1417     m_deferred_packets.push_back(pack);
1418 }
1419 
1420 void
add_packet2(packet_cptr const & pack)1421 generic_packetizer_c::add_packet2(packet_cptr const &pack) {
1422   auto adjust_timestamp = [this](int64_t x) {
1423     return mtx::to_int(m_ti.m_tcsync.factor * (x + m_correction_timestamp_offset + m_append_timestamp_offset)) + m_ti.m_tcsync.displacement;
1424   };
1425 
1426   pack->timestamp = adjust_timestamp(pack->timestamp);
1427   if (pack->has_bref())
1428     pack->bref = adjust_timestamp(pack->bref);
1429   if (pack->has_fref())
1430     pack->fref = adjust_timestamp(pack->fref);
1431   if (pack->has_duration()) {
1432     pack->duration = mtx::to_int(m_ti.m_tcsync.factor * pack->duration);
1433     if (pack->has_discard_padding())
1434       pack->duration -= std::min(pack->duration, pack->discard_padding.to_ns());
1435   }
1436 
1437   if (0 > pack->timestamp)
1438     return;
1439 
1440   // 'timestamp < safety_last_timestamp' may only occur for B frames. In this
1441   // case we have the coding order, e.g. IPB1B2 and the timestamps
1442   // I: 0, P: 120, B1: 40, B2: 80.
1443   if (!m_relaxed_timestamp_checking && (pack->timestamp < m_safety_last_timestamp) && (0 > pack->fref) && mtx::hacks::is_engaged(mtx::hacks::ENABLE_TIMESTAMP_WARNING)) {
1444     if (track_audio == m_htrack_type) {
1445       int64_t needed_timestamp_offset  = m_safety_last_timestamp + m_safety_last_duration - pack->timestamp;
1446       m_correction_timestamp_offset   += needed_timestamp_offset;
1447       pack->timestamp                 += needed_timestamp_offset;
1448       if (pack->has_bref())
1449         pack->bref += needed_timestamp_offset;
1450       if (pack->has_fref())
1451         pack->fref += needed_timestamp_offset;
1452 
1453       mxwarn_tid(m_ti.m_fname, m_ti.m_id,
1454                  fmt::format(Y("The current packet's timestamp is smaller than that of the previous packet. "
1455                                "This usually means that the source file is a Matroska file that has not been created 100% correctly. "
1456                                "The timestamps of all packets will be adjusted by {0}ms in order not to lose any data. "
1457                                "This may throw audio/video synchronization off, but that can be corrected with mkvmerge's \"--sync\" option. "
1458                                "If you already use \"--sync\" and you still get this warning then do NOT worry -- this is normal. "
1459                                "If this error happens more than once and you get this message more than once for a particular track "
1460                                "then either is the source file badly mastered, or mkvmerge contains a bug. "
1461                                "In this case you should contact the author Moritz Bunkus <moritz@bunkus.org>.\n"),
1462                              (needed_timestamp_offset + 500000) / 1000000));
1463 
1464     } else
1465       mxwarn_tid(m_ti.m_fname, m_ti.m_id,
1466                  fmt::format("generic_packetizer_c::add_packet2: timestamp < last_timestamp ({0} < {1}). {2}\n",
1467                              mtx::string::format_timestamp(pack->timestamp), mtx::string::format_timestamp(m_safety_last_timestamp), BUGMSG));
1468   }
1469 
1470   m_safety_last_timestamp        = pack->timestamp;
1471   m_safety_last_duration         = pack->duration;
1472   pack->timestamp_before_factory = pack->timestamp;
1473 
1474   m_packet_queue.push_back(pack);
1475   if (!m_timestamp_factory || (TFA_IMMEDIATE == m_timestamp_factory_application_mode))
1476     apply_factory_once(pack);
1477   else
1478     apply_factory();
1479 
1480   after_packet_timestamped(*pack);
1481 
1482   compress_packet(*pack);
1483 }
1484 
1485 void
process_deferred_packets()1486 generic_packetizer_c::process_deferred_packets() {
1487   for (auto &packet : m_deferred_packets)
1488     add_packet2(packet);
1489   m_deferred_packets.clear();
1490 }
1491 
1492 packet_cptr
get_packet()1493 generic_packetizer_c::get_packet() {
1494   if (m_packet_queue.empty() || !m_packet_queue.front()->factory_applied)
1495     return packet_cptr{};
1496 
1497   packet_cptr pack = m_packet_queue.front();
1498   m_packet_queue.pop_front();
1499 
1500   pack->output_order_timestamp = timestamp_c::ns(pack->assigned_timestamp - std::max(m_codec_delay.to_ns(0), m_seek_pre_roll.to_ns(0)));
1501 
1502   account_enqueued_bytes(*pack, -1);
1503 
1504   --m_next_packet_wo_assigned_timestamp;
1505   if (0 > m_next_packet_wo_assigned_timestamp)
1506     m_next_packet_wo_assigned_timestamp = 0;
1507 
1508   return pack;
1509 }
1510 
1511 void
apply_factory_once(packet_cptr const & packet)1512 generic_packetizer_c::apply_factory_once(packet_cptr const &packet) {
1513   if (!m_timestamp_factory) {
1514     packet->assigned_timestamp = packet->timestamp;
1515     packet->gap_following      = false;
1516   } else
1517     packet->gap_following      = m_timestamp_factory->get_next(*packet);
1518 
1519   packet->factory_applied      = true;
1520 
1521   mxdebug_if(s_debug, fmt::format("apply_factory_once(): source {0} t {1} tbf {2} at {3}\n", packet->source->get_source_track_num(), packet->timestamp, packet->timestamp_before_factory, packet->assigned_timestamp));
1522 
1523   m_max_timestamp_seen           = std::max(m_max_timestamp_seen, packet->assigned_timestamp + packet->duration);
1524   m_reader->m_max_timestamp_seen = std::max(m_max_timestamp_seen, m_reader->m_max_timestamp_seen);
1525 
1526   ++m_next_packet_wo_assigned_timestamp;
1527 }
1528 
1529 void
apply_factory()1530 generic_packetizer_c::apply_factory() {
1531   if (m_packet_queue.empty())
1532     return;
1533 
1534   // Find the first packet to which the factory hasn't been applied yet.
1535   packet_cptr_di p_start = m_packet_queue.begin() + m_next_packet_wo_assigned_timestamp;
1536 
1537   while ((m_packet_queue.end() != p_start) && (*p_start)->factory_applied)
1538     ++p_start;
1539 
1540   if (m_packet_queue.end() == p_start)
1541     return;
1542 
1543   if (TFA_SHORT_QUEUEING == m_timestamp_factory_application_mode)
1544     apply_factory_short_queueing(p_start);
1545 
1546   else
1547     apply_factory_full_queueing(p_start);
1548 }
1549 
1550 void
apply_factory_short_queueing(packet_cptr_di & p_start)1551 generic_packetizer_c::apply_factory_short_queueing(packet_cptr_di &p_start) {
1552   while (m_packet_queue.end() != p_start) {
1553     // Find the next packet with a timestamp bigger than the start packet's
1554     // timestamp. All packets between those two including the start packet
1555     // and excluding the end packet can be timestamped.
1556     packet_cptr_di p_end = p_start + 1;
1557     while ((m_packet_queue.end() != p_end) && ((*p_end)->timestamp_before_factory < (*p_start)->timestamp_before_factory))
1558       ++p_end;
1559 
1560     // Abort if no such packet was found, but keep on assigning if the
1561     // packetizer has been flushed already.
1562     if (!m_has_been_flushed && (m_packet_queue.end() == p_end))
1563       return;
1564 
1565     // Now assign timestamps to the ones between p_start and p_end...
1566     packet_cptr_di p_current;
1567     for (p_current = p_start + 1; p_current != p_end; ++p_current)
1568       apply_factory_once(*p_current);
1569     // ...and to p_start itself.
1570     apply_factory_once(*p_start);
1571 
1572     p_start = p_end;
1573   }
1574 }
1575 
1576 struct packet_sorter_t {
1577   int m_index;
1578   static std::deque<packet_cptr> *m_packet_queue;
1579 
packet_sorter_tpacket_sorter_t1580   packet_sorter_t(int index)
1581     : m_index(index)
1582   {
1583   }
1584 
operator <packet_sorter_t1585   bool operator <(const packet_sorter_t &cmp) const {
1586     return (*m_packet_queue)[m_index]->timestamp < (*m_packet_queue)[cmp.m_index]->timestamp;
1587   }
1588 };
1589 
1590 std::deque<packet_cptr> *packet_sorter_t::m_packet_queue = nullptr;
1591 
1592 void
apply_factory_full_queueing(packet_cptr_di & p_start)1593 generic_packetizer_c::apply_factory_full_queueing(packet_cptr_di &p_start) {
1594   packet_sorter_t::m_packet_queue = &m_packet_queue;
1595 
1596   while (m_packet_queue.end() != p_start) {
1597     // Find the next I frame packet.
1598     packet_cptr_di p_end = p_start + 1;
1599     while ((m_packet_queue.end() != p_end) && !(*p_end)->is_key_frame())
1600       ++p_end;
1601 
1602     // Abort if no such packet was found, but keep on assigning if the
1603     // packetizer has been flushed already.
1604     if (!m_has_been_flushed && (m_packet_queue.end() == p_end))
1605       return;
1606 
1607     // Now sort the frames by their timestamp as the factory has to be
1608     // applied to the packets in the same order as they're timestamped.
1609     std::vector<packet_sorter_t> sorter;
1610     bool needs_sorting         = false;
1611     int64_t previous_timestamp = 0;
1612     size_t i                   = distance(m_packet_queue.begin(), p_start);
1613 
1614     packet_cptr_di p_current;
1615     for (p_current = p_start; p_current != p_end; ++i, ++p_current) {
1616       sorter.push_back(packet_sorter_t(i));
1617       if (m_packet_queue[i]->timestamp < previous_timestamp)
1618         needs_sorting = true;
1619       previous_timestamp = m_packet_queue[i]->timestamp;
1620     }
1621 
1622     if (needs_sorting)
1623       std::sort(sorter.begin(), sorter.end());
1624 
1625     // Finally apply the factory.
1626     for (i = 0; sorter.size() > i; ++i)
1627       apply_factory_once(m_packet_queue[sorter[i].m_index]);
1628 
1629     p_start = p_end;
1630   }
1631 }
1632 
1633 void
force_duration_on_last_packet()1634 generic_packetizer_c::force_duration_on_last_packet() {
1635   if (m_packet_queue.empty()) {
1636     mxdebug_if(s_debug, fmt::format("'{0}' track {1}: force_duration_on_last_packet: packet queue is empty\n", m_ti.m_fname, m_ti.m_id));
1637     return;
1638   }
1639 
1640   packet_cptr &packet        = m_packet_queue.back();
1641   packet->duration_mandatory = true;
1642 
1643   mxdebug_if(s_debug, fmt::format("'{0}' track {1}: force_duration_on_last_packet: forcing at {2} with {3:.3f}ms\n", m_ti.m_fname, m_ti.m_id, mtx::string::format_timestamp(packet->timestamp), packet->duration / 1000.0));
1644 }
1645 
1646 int64_t
calculate_avi_audio_sync(int64_t num_bytes,int64_t samples_per_packet,int64_t packet_duration)1647 generic_packetizer_c::calculate_avi_audio_sync(int64_t num_bytes,
1648                                                int64_t samples_per_packet,
1649                                                int64_t packet_duration) {
1650   if (!m_ti.m_avi_audio_sync_enabled || mtx::hacks::is_engaged(mtx::hacks::NO_DELAY_FOR_GARBAGE_IN_AVI))
1651     return -1;
1652 
1653   if (m_ti.m_avi_audio_data_rate)
1654     return num_bytes * 1000000000 / m_ti.m_avi_audio_data_rate;
1655 
1656   return ((num_bytes + samples_per_packet - 1) / samples_per_packet) * packet_duration;
1657 }
1658 
1659 void
connect(generic_packetizer_c * src,int64_t append_timestamp_offset)1660 generic_packetizer_c::connect(generic_packetizer_c *src,
1661                               int64_t append_timestamp_offset) {
1662   m_free_refs                   = src->m_free_refs;
1663   m_next_free_refs              = src->m_next_free_refs;
1664   m_track_entry                 = src->m_track_entry;
1665   m_hserialno                   = src->m_hserialno;
1666   m_htrack_type                 = src->m_htrack_type;
1667   m_htrack_default_duration     = src->m_htrack_default_duration;
1668   m_huid                        = src->m_huid;
1669   m_hcompression                = src->m_hcompression;
1670   m_compressor                  = compressor_c::create(m_hcompression);
1671   m_last_cue_timestamp          = src->m_last_cue_timestamp;
1672   m_timestamp_factory           = src->m_timestamp_factory;
1673   m_correction_timestamp_offset = 0;
1674 
1675   if (-1 == append_timestamp_offset)
1676     m_append_timestamp_offset = src->m_max_timestamp_seen;
1677   else
1678     m_append_timestamp_offset = append_timestamp_offset;
1679 
1680   m_connected_to++;
1681   if (2 == m_connected_to) {
1682     process_deferred_packets();
1683     src->m_connected_successor = this;
1684   }
1685 }
1686 
1687 split_result_e
can_be_split(std::string &)1688 generic_packetizer_c::can_be_split(std::string &/* error_message */) {
1689   return CAN_SPLIT_YES;
1690 }
1691 
1692 void
set_displacement_maybe(int64_t displacement)1693 generic_packetizer_c::set_displacement_maybe(int64_t displacement) {
1694   if ((1 == m_ti.m_tcsync.factor) && (0 == m_ti.m_tcsync.displacement))
1695     m_ti.m_tcsync.displacement = displacement;
1696 }
1697 
1698 bool
contains_gap()1699 generic_packetizer_c::contains_gap() {
1700   return m_timestamp_factory ? m_timestamp_factory->contains_gap() : false;
1701 }
1702 
1703 void
flush()1704 generic_packetizer_c::flush() {
1705   flush_impl();
1706 
1707   m_has_been_flushed = true;
1708   apply_factory();
1709 }
1710 
1711 bool
display_dimensions_or_aspect_ratio_set()1712 generic_packetizer_c::display_dimensions_or_aspect_ratio_set() {
1713   return m_ti.display_dimensions_or_aspect_ratio_set();
1714 }
1715 
1716 bool
is_compatible_with(output_compatibility_e compatibility)1717 generic_packetizer_c::is_compatible_with(output_compatibility_e compatibility) {
1718   return OC_MATROSKA == compatibility;
1719 }
1720 
1721 void
discard_queued_packets()1722 generic_packetizer_c::discard_queued_packets() {
1723   m_packet_queue.clear();
1724   m_enqueued_bytes = 0;
1725 }
1726 
1727 bool
wants_cue_duration() const1728 generic_packetizer_c::wants_cue_duration()
1729   const {
1730   return get_track_type() == track_subtitle;
1731 }
1732 
1733 void
show_experimental_status_version(std::string const & codec_id)1734 generic_packetizer_c::show_experimental_status_version(std::string const &codec_id) {
1735   auto idx = get_format_name().get_untranslated();
1736   if (s_experimental_status_warning_shown[idx])
1737     return;
1738 
1739   s_experimental_status_warning_shown[idx] = true;
1740   mxwarn(fmt::format(Y("Note that the Matroska specifications regarding the storage of '{0}' have not been finalized yet. "
1741                        "mkvmerge's support for it is therefore subject to change and uses the CodecID '{1}/EXPERIMENTAL' instead of '{1}'. "
1742                        "This warning will be removed once the specifications have been finalized and mkvmerge has been updated accordingly.\n"),
1743                      get_format_name().get_translated(), codec_id));
1744 }
1745 
1746 int64_t
create_track_number()1747 generic_packetizer_c::create_track_number() {
1748   int file_num = -1;
1749   size_t i;
1750 
1751   // Determine current file's file number regarding --track-order.
1752   for (i = 0; i < g_files.size(); i++)
1753     if (g_files[i]->reader.get() == this->m_reader) {
1754       file_num = i;
1755       break;
1756     }
1757 
1758   if (file_num == -1)
1759     mxerror(fmt::format(Y("create_track_number: file_num not found. {0}\n"), BUGMSG));
1760 
1761   // Determine current track's track number regarding --track-order
1762   // and look up the pair in g_track_order.
1763   int tnum = -1;
1764   for (i = 0; i < g_track_order.size(); i++)
1765     if (   (g_track_order[i].file_id  == file_num)
1766         && (g_track_order[i].track_id == m_ti.m_id)) {
1767       tnum = i + 1;
1768       break;
1769     }
1770 
1771   // If the track's file/track number pair was found in g_track_order,
1772   // use the resulting track number unconditionally.
1773   if (tnum > 0)
1774     return tnum;
1775 
1776   // The file/track number pair wasn't found. Create a new track
1777   // number. Don't use numbers that might be assigned by
1778   // --track-order.
1779 
1780   ++ms_track_number;
1781   return ms_track_number + g_track_order.size();
1782 }
1783 
1784 file_status_e
read(bool force)1785 generic_packetizer_c::read(bool force) {
1786   return m_reader->read_next(this, force);
1787 }
1788 
1789 void
process(packet_cptr const & packet)1790 generic_packetizer_c::process(packet_cptr const &packet) {
1791   process_impl(packet);
1792 }
1793 
1794 void
prevent_lacing()1795 generic_packetizer_c::prevent_lacing() {
1796   m_prevent_lacing = true;
1797 }
1798 
1799 bool
is_lacing_prevented() const1800 generic_packetizer_c::is_lacing_prevented()
1801   const {
1802   return m_prevent_lacing;
1803 }
1804 
1805 void
after_packet_timestamped(packet_t &)1806 generic_packetizer_c::after_packet_timestamped(packet_t &) {
1807 }
1808 
1809 void
after_packet_rendered(packet_t const &)1810 generic_packetizer_c::after_packet_rendered(packet_t const &) {
1811 }
1812 
1813 void
before_file_finished()1814 generic_packetizer_c::before_file_finished() {
1815 }
1816 
1817 void
after_file_created()1818 generic_packetizer_c::after_file_created() {
1819 }
1820 
1821 generic_packetizer_c *
get_connected_successor() const1822 generic_packetizer_c::get_connected_successor()
1823   const {
1824   return m_connected_successor;
1825 }
1826 
1827 void
set_source_id(std::string const & source_id)1828 generic_packetizer_c::set_source_id(std::string const &source_id) {
1829   m_source_id = source_id;
1830 }
1831 
1832 std::string
get_source_id() const1833 generic_packetizer_c::get_source_id()
1834   const {
1835   return m_source_id;
1836 }
1837