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    Quicktime and MP4 reader
10 
11    Written by Moritz Bunkus <moritz@bunkus.org>.
12    The second half of the parse_headers() function after the
13      "// process chunkmap:" comment was taken from mplayer's
14      demux_mov.c file which is distributed under the GPL as well. Thanks to
15      the original authors.
16 */
17 
18 #include "common/common_pch.h"
19 
20 #include <cstring>
21 #include <unordered_map>
22 
23 #include <boost/multiprecision/gmp.hpp>
24 
25 #include <QRegularExpression>
26 
27 #include "avilib.h"
28 #include "common/aac.h"
29 #include "common/alac.h"
30 #include "common/avc/es_parser.h"
31 #include "common/chapters/chapters.h"
32 #include "common/codec.h"
33 #include "common/endian.h"
34 #include "common/frame_timing.h"
35 #include "common/hacks.h"
36 #include "common/id_info.h"
37 #include "common/iso639.h"
38 #include "common/list_utils.h"
39 #include "common/math.h"
40 #include "common/mm_io_x.h"
41 #include "common/mm_mem_io.h"
42 #include "common/mm_proxy_io.h"
43 #include "common/mm_text_io.h"
44 #include "common/mp3.h"
45 #include "common/mp4.h"
46 #include "common/qt.h"
47 #include "common/strings/formatting.h"
48 #include "common/strings/parsing.h"
49 #include "common/vobsub.h"
50 #include "input/r_qtmp4.h"
51 #include "merge/file_status.h"
52 #include "merge/input_x.h"
53 #include "merge/output_control.h"
54 #include "output/p_aac.h"
55 #include "output/p_ac3.h"
56 #include "output/p_alac.h"
57 #include "output/p_av1.h"
58 #include "output/p_avc.h"
59 #include "output/p_dts.h"
60 #include "output/p_hevc.h"
61 #include "output/p_hevc_es.h"
62 #include "output/p_mp3.h"
63 #include "output/p_mpeg1_2.h"
64 #include "output/p_mpeg4_p2.h"
65 #include "output/p_opus.h"
66 #include "output/p_passthrough.h"
67 #include "output/p_pcm.h"
68 #include "output/p_prores.h"
69 #include "output/p_quicktime.h"
70 #include "output/p_video_for_windows.h"
71 #include "output/p_vobsub.h"
72 #include "output/p_vorbis.h"
73 #include "output/p_vpx.h"
74 
75 using namespace libmatroska;
76 
77 constexpr auto MAX_INTERLEAVING_BADNESS = 0.4;
78 
79 namespace mtx {
80 
81 class atom_chunk_size_x: public exception {
82 private:
83   uint64_t m_size, m_pos;
84 
85 public:
atom_chunk_size_x(uint64_t size,uint64_t pos)86   atom_chunk_size_x(uint64_t size, uint64_t pos)
87     : mtx::exception()
88     , m_size{size}
89     , m_pos{pos}
90   {
91   }
92 
what() const93   virtual const char *what() const throw() {
94     return "invalid atom chunk size";
95   }
96 
error() const97   virtual std::string error() const throw() {
98     return fmt::format("invalid atom chunk size {0} at {1}", m_size, m_pos);
99   }
100 };
101 
102 }
103 
104 static void
print_atom_too_small_error(std::string const & name,qt_atom_t const & atom,std::size_t actual_size)105 print_atom_too_small_error(std::string const &name,
106                            qt_atom_t const &atom,
107                            std::size_t actual_size) {
108   mxerror(fmt::format(Y("Quicktime/MP4 reader: '{0}' atom is too small. Expected size: >= {1}. Actual size: {2}.\n"), name, actual_size, atom.size));
109 }
110 
111 static std::string
space(int num)112 space(int num) {
113   return std::string(num, ' ');
114 }
115 
116 static qt_atom_t
read_qtmp4_atom(mm_io_c * read_from,bool exit_on_error=true)117 read_qtmp4_atom(mm_io_c *read_from,
118                 bool exit_on_error = true) {
119   qt_atom_t a;
120 
121   a.pos    = read_from->getFilePointer();
122   a.size   = read_from->read_uint32_be();
123   a.fourcc = fourcc_c(read_from);
124   a.hsize  = 8;
125 
126   if (1 == a.size) {
127     a.size   = read_from->read_uint64_be();
128     a.hsize += 8;
129 
130   } else if (0 == a.size)
131     a.size   = read_from->get_size() - read_from->getFilePointer() + 8;
132 
133   if (a.size < a.hsize) {
134     if (exit_on_error)
135       mxerror(fmt::format(Y("Quicktime/MP4 reader: Invalid chunk size {0} at {1}.\n"), a.size, a.pos));
136     else
137       throw mtx::atom_chunk_size_x{a.size, a.pos};
138   }
139 
140   return a;
141 }
142 
143 static std::string
displayable_esds_tag_name(uint8_t tag)144 displayable_esds_tag_name(uint8_t tag) {
145   return mtx::mp4::DESCRIPTOR_TYPE_O                 == tag ? "O"
146        : mtx::mp4::DESCRIPTOR_TYPE_IO                == tag ? "IO"
147        : mtx::mp4::DESCRIPTOR_TYPE_ES                == tag ? "ES"
148        : mtx::mp4::DESCRIPTOR_TYPE_DEC_CONFIG        == tag ? "DEC_CONFIG"
149        : mtx::mp4::DESCRIPTOR_TYPE_DEC_SPECIFIC      == tag ? "DEC_SPECIFIC"
150        : mtx::mp4::DESCRIPTOR_TYPE_SL_CONFIG         == tag ? "SL_CONFIG"
151        : mtx::mp4::DESCRIPTOR_TYPE_CONTENT_ID        == tag ? "CONTENT_ID"
152        : mtx::mp4::DESCRIPTOR_TYPE_SUPPL_CONTENT_ID  == tag ? "SUPPL_CONTENT_ID"
153        : mtx::mp4::DESCRIPTOR_TYPE_IP_PTR            == tag ? "IP_PTR"
154        : mtx::mp4::DESCRIPTOR_TYPE_IPMP_PTR          == tag ? "IPMP_PTR"
155        : mtx::mp4::DESCRIPTOR_TYPE_IPMP              == tag ? "IPMP"
156        : mtx::mp4::DESCRIPTOR_TYPE_REGISTRATION      == tag ? "REGISTRATION"
157        : mtx::mp4::DESCRIPTOR_TYPE_ESID_INC          == tag ? "ESID_INC"
158        : mtx::mp4::DESCRIPTOR_TYPE_ESID_REF          == tag ? "ESID_REF"
159        : mtx::mp4::DESCRIPTOR_TYPE_FILE_IO           == tag ? "FILE_IO"
160        : mtx::mp4::DESCRIPTOR_TYPE_FILE_O            == tag ? "FILE_O"
161        : mtx::mp4::DESCRIPTOR_TYPE_EXT_PROFILE_LEVEL == tag ? "EXT_PROFILE_LEVEL"
162        : mtx::mp4::DESCRIPTOR_TYPE_TAGS_START        == tag ? "TAGS_START"
163        : mtx::mp4::DESCRIPTOR_TYPE_TAGS_END          == tag ? "TAGS_END"
164        :                                  "unknown";
165 }
166 
167 bool
probe_file()168 qtmp4_reader_c::probe_file() {
169   while (true) {
170     uint64_t atom_pos  = m_in->getFilePointer();
171     uint64_t atom_size = m_in->read_uint32_be();
172     fourcc_c atom(*m_in);
173 
174     if (1 == atom_size)
175       atom_size = m_in->read_uint64_be();
176 
177     if (   (atom == "moov")
178         || (atom == "ftyp")
179         || (atom == "mdat")
180         || (atom == "pnot"))
181       return true;
182 
183     if ((atom != "wide") && (atom != "skip"))
184       return false;
185 
186     m_in->setFilePointer(atom_pos + atom_size);
187   }
188 }
189 
190 void
read_headers()191 qtmp4_reader_c::read_headers() {
192   try {
193     show_demuxer_info();
194 
195     parse_headers();
196 
197   } catch (mtx::mm_io::exception &) {
198     throw mtx::input::open_x();
199   }
200 }
201 
202 void
calculate_num_bytes_to_process()203 qtmp4_reader_c::calculate_num_bytes_to_process() {
204   for (auto const &dmx : m_demuxers)
205     if (demuxing_requested(dmx->type, dmx->id, dmx->language))
206       m_bytes_to_process += std::accumulate(dmx->m_index.begin(), dmx->m_index.end(), 0ull, [](int64_t num, auto const &entry) { return num + entry.size; });
207 }
208 
209 qt_atom_t
read_atom(mm_io_c * read_from,bool exit_on_error)210 qtmp4_reader_c::read_atom(mm_io_c *read_from,
211                           bool exit_on_error) {
212   return read_qtmp4_atom(read_from ? read_from : m_in.get(), exit_on_error);
213 }
214 
215 bool
resync_to_top_level_atom(uint64_t start_pos)216 qtmp4_reader_c::resync_to_top_level_atom(uint64_t start_pos) {
217   static std::vector<std::string> const s_top_level_atoms{ "ftyp", "pdin", "moov", "moof", "mfra", "mdat", "free", "skip" };
218   static auto test_atom_at = [this](uint64_t atom_pos, uint64_t expected_hsize, fourcc_c const &expected_fourcc) -> bool {
219     m_in->setFilePointer(atom_pos);
220     auto test_atom = read_atom(nullptr, false);
221     mxdebug_if(m_debug_resync, fmt::format("Test for {0}bit offset atom: {1}\n", 8 == expected_hsize ? 32 : 64, test_atom));
222 
223     if ((test_atom.fourcc == expected_fourcc) && (test_atom.hsize == expected_hsize) && ((test_atom.pos + test_atom.size) <= m_size)) {
224       mxdebug_if(m_debug_resync, fmt::format("{0}bit offset atom looks good\n", 8 == expected_hsize ? 32 : 64));
225       m_in->setFilePointer(atom_pos);
226       return true;
227     }
228 
229     return false;
230   };
231 
232   try {
233     m_in->setFilePointer(start_pos);
234     fourcc_c fourcc{m_in};
235     auto next_pos = start_pos;
236 
237     mxdebug_if(m_debug_resync, fmt::format("Starting resync at {0}, FourCC {1}\n", start_pos, fourcc));
238     while (true) {
239       m_in->setFilePointer(next_pos);
240       fourcc.shift_read(m_in);
241       next_pos = m_in->getFilePointer();
242 
243       if (!fourcc.human_readable() || !fourcc.equiv(s_top_level_atoms))
244         continue;
245 
246       auto fourcc_pos = m_in->getFilePointer() - 4;
247       mxdebug_if(m_debug_resync, fmt::format("Human readable at {0}: {1}\n", fourcc_pos, fourcc));
248 
249       if (test_atom_at(fourcc_pos - 12, 16, fourcc) || test_atom_at(fourcc_pos - 4, 8, fourcc))
250         return true;
251     }
252 
253   } catch (mtx::mm_io::exception &ex) {
254     mxdebug_if(m_debug_resync, fmt::format("I/O exception during resync: {0}\n", ex.what()));
255   } catch (mtx::atom_chunk_size_x &ex) {
256     mxdebug_if(m_debug_resync, fmt::format("Atom exception during resync: {0}\n", ex.error()));
257   }
258 
259   return false;
260 }
261 
262 void
parse_headers()263 qtmp4_reader_c::parse_headers() {
264   unsigned int idx;
265 
266   m_in->setFilePointer(0);
267 
268   bool headers_parsed = false;
269   bool mdat_found     = false;
270 
271   try {
272     while (!m_in->eof()) {
273       qt_atom_t atom = read_atom();
274       mxdebug_if(m_debug_headers, fmt::format("'{0}' atom, size {1}, at {2}–{3}, human readable? {4}\n", atom.fourcc, atom.size, atom.pos, atom.pos + atom.size, atom.fourcc.human_readable()));
275 
276       if (atom.fourcc == "ftyp") {
277         auto tmp = fourcc_c{m_in};
278         mxdebug_if(m_debug_headers, fmt::format("  File type major brand: {0}\n", tmp));
279         tmp = fourcc_c{m_in};
280         mxdebug_if(m_debug_headers, fmt::format("  File type minor brand: {0}\n", tmp));
281 
282         for (idx = 0; idx < ((atom.size - 16) / 4); ++idx) {
283           tmp = fourcc_c{m_in};
284           mxdebug_if(m_debug_headers, fmt::format("  File type compatible brands #{0}: {1}\n", idx, tmp));
285         }
286 
287       } else if (atom.fourcc == "moov") {
288         if (!headers_parsed)
289           handle_moov_atom(atom.to_parent(), 0);
290         else
291           m_in->setFilePointer(atom.pos + atom.size);
292         headers_parsed = true;
293 
294       } else if (atom.fourcc == "mdat") {
295         m_in->setFilePointer(atom.pos + atom.size);
296         mdat_found = true;
297 
298       } else if (atom.fourcc == "moof") {
299         handle_moof_atom(atom.to_parent(), 0, atom);
300 
301       } else if (atom.fourcc.human_readable())
302         m_in->setFilePointer(atom.pos + atom.size);
303 
304       else if (!resync_to_top_level_atom(atom.pos))
305         break;
306     }
307   } catch (mtx::mm_io::exception &) {
308   }
309 
310   if (!headers_parsed)
311     mxerror(Y("Quicktime/MP4 reader: Have not found any header atoms.\n"));
312 
313   if (!mdat_found)
314     mxerror(Y("Quicktime/MP4 reader: Have not found the 'mdat' atom. No movie data found.\n"));
315 
316   verify_track_parameters_and_update_indexes();
317 
318   read_chapter_track();
319 
320   m_demuxers.erase(std::remove_if(m_demuxers.begin(), m_demuxers.end(), [](qtmp4_demuxer_cptr const &dmx) { return !dmx->ok || dmx->is_chapters(); }), m_demuxers.end());
321 
322   detect_interleaving();
323 
324   if (!g_identifying) {
325     calculate_timestamps();
326     calculate_num_bytes_to_process();
327   }
328 
329   mxdebug_if(m_debug_headers, fmt::format("Number of valid tracks found: {0}\n", m_demuxers.size()));
330 }
331 
332 void
verify_track_parameters_and_update_indexes()333 qtmp4_reader_c::verify_track_parameters_and_update_indexes() {
334   for (auto &dmx : m_demuxers) {
335     if (m_chapter_track_ids[dmx->container_id])
336       dmx->type = 'C';
337 
338     else if (!demuxing_requested(dmx->type, dmx->id, dmx->language))
339       continue;
340 
341     else if (dmx->is_audio() && !dmx->verify_audio_parameters())
342       continue;
343 
344     else if (dmx->is_video() && !dmx->verify_video_parameters())
345       continue;
346 
347     else if (dmx->is_subtitles() && !dmx->verify_subtitles_parameters())
348       continue;
349 
350     else if (dmx->is_unknown())
351       continue;
352 
353     dmx->ok = dmx->update_tables();
354   }
355 }
356 
357 std::optional<int64_t>
calculate_global_min_timestamp() const358 qtmp4_reader_c::calculate_global_min_timestamp()
359   const {
360   std::optional<int64_t> global_min_timestamp;
361 
362   for (auto const &dmx : m_demuxers) {
363     if (!demuxing_requested(dmx->type, dmx->id, dmx->language)) {
364       continue;
365     }
366 
367     auto dmx_min_timestamp = dmx->min_timestamp();
368     if (dmx_min_timestamp && (!global_min_timestamp || (dmx_min_timestamp < *global_min_timestamp))) {
369       global_min_timestamp = *dmx_min_timestamp;
370     }
371 
372     mxdebug_if(m_debug_headers, fmt::format("Minimum timestamp of track {0}: {1}\n", dmx->id, dmx_min_timestamp ? mtx::string::format_timestamp(*dmx_min_timestamp) : "none"));
373   }
374 
375   mxdebug_if(m_debug_headers, fmt::format("Minimum global timestamp: {0}\n", global_min_timestamp ? mtx::string::format_timestamp(*global_min_timestamp) : "none"));
376 
377   return global_min_timestamp;
378 }
379 
380 void
calculate_timestamps()381 qtmp4_reader_c::calculate_timestamps() {
382   if (m_timestamps_calculated)
383     return;
384 
385   for (auto &dmx : m_demuxers) {
386     dmx->calculate_frame_rate();
387     dmx->calculate_timestamps();
388   }
389 
390   auto min_timestamp = calculate_global_min_timestamp();
391 
392   if (min_timestamp && (0 != *min_timestamp)) {
393     for (auto &dmx : m_demuxers) {
394       dmx->adjust_timestamps(-*min_timestamp);
395     }
396   }
397 
398   m_timestamps_calculated = true;
399 }
400 
401 void
handle_audio_encoder_delay(qtmp4_demuxer_c & dmx)402 qtmp4_reader_c::handle_audio_encoder_delay(qtmp4_demuxer_c &dmx) {
403   if ((0 == m_audio_encoder_delay_samples) || (0 == dmx.a_samplerate) || (-1 == dmx.ptzr))
404     return;
405 
406   ptzr(dmx.ptzr).m_ti.m_tcsync.displacement -= (m_audio_encoder_delay_samples * 1000000000ll) / dmx.a_samplerate;
407   m_audio_encoder_delay_samples               = 0;
408 }
409 
410 void
process_atom(qt_atom_t const & parent,int level,std::function<void (qt_atom_t const &)> const & handler)411 qtmp4_reader_c::process_atom(qt_atom_t const &parent,
412                              int level,
413                              std::function<void(qt_atom_t const &)> const &handler) {
414   auto parent_size = parent.size;
415 
416   while (8 <= parent_size) {
417     auto atom = read_atom();
418     mxdebug_if(m_debug_headers, fmt::format("{0}'{1}' atom, size {2}, at {3}–{4}\n", space(2 * level + 1), atom.fourcc, atom.size, atom.pos, atom.pos + atom.size));
419 
420     if (atom.size > parent_size)
421       break;
422 
423     handler(atom);
424 
425     m_in->setFilePointer(atom.pos + atom.size);
426     parent_size -= atom.size;
427   }
428 }
429 
430 void
handle_cmov_atom(qt_atom_t parent,int level)431 qtmp4_reader_c::handle_cmov_atom(qt_atom_t parent,
432                                  int level) {
433   m_compression_algorithm = fourcc_c{};
434 
435   process_atom(parent, level, [&](qt_atom_t const &atom) {
436     if (atom.fourcc == "dcom")
437       handle_dcom_atom(atom.to_parent(), level + 1);
438 
439     else if (atom.fourcc == "cmvd")
440       handle_cmvd_atom(atom.to_parent(), level + 1);
441   });
442 }
443 
444 void
handle_cmvd_atom(qt_atom_t atom,int level)445 qtmp4_reader_c::handle_cmvd_atom(qt_atom_t atom,
446                                  int level) {
447   uint32_t moov_size = m_in->read_uint32_be();
448   mxdebug_if(m_debug_headers, fmt::format("{0}Uncompressed size: {1}\n", space((level + 1) * 2 + 1), moov_size));
449 
450   if (m_compression_algorithm != "zlib")
451     mxerror(fmt::format(Y("Quicktime/MP4 reader: This file uses compressed headers with an unknown "
452                           "or unsupported compression algorithm '{0}'. Aborting.\n"), m_compression_algorithm));
453 
454   mm_io_cptr old_in       = m_in;
455   uint32_t cmov_size      = atom.size - atom.hsize;
456   memory_cptr af_cmov_buf = memory_c::alloc(cmov_size);
457   unsigned char *cmov_buf = af_cmov_buf->get_buffer();
458   memory_cptr af_moov_buf = memory_c::alloc(moov_size + 16);
459   unsigned char *moov_buf = af_moov_buf->get_buffer();
460 
461   if (m_in->read(cmov_buf, cmov_size) != cmov_size)
462     throw mtx::input::header_parsing_x();
463 
464   z_stream zs;
465   zs.zalloc    = static_cast<alloc_func>(nullptr);
466   zs.zfree     = static_cast<free_func>(nullptr);
467   zs.opaque    = static_cast<voidpf>(nullptr);
468   zs.next_in   = cmov_buf;
469   zs.avail_in  = cmov_size;
470   zs.next_out  = moov_buf;
471   zs.avail_out = moov_size;
472 
473   int zret = inflateInit(&zs);
474   if (Z_OK != zret)
475     mxerror(fmt::format(Y("Quicktime/MP4 reader: This file uses compressed headers, but the zlib library could not be initialized. "
476                           "Error code from zlib: {0}. Aborting.\n"), zret));
477 
478   zret = inflate(&zs, Z_NO_FLUSH);
479   if ((Z_OK != zret) && (Z_STREAM_END != zret))
480     mxerror(fmt::format(Y("Quicktime/MP4 reader: This file uses compressed headers, but they could not be uncompressed. "
481                           "Error code from zlib: {0}. Aborting.\n"), zret));
482 
483   if (moov_size != zs.total_out)
484     mxwarn(fmt::format(Y("Quicktime/MP4 reader: This file uses compressed headers, but the expected uncompressed size ({0}) "
485                          "was not what is available after uncompressing ({1}).\n"), moov_size, zs.total_out));
486 
487   zret = inflateEnd(&zs);
488 
489   m_in = mm_io_cptr(new mm_mem_io_c(moov_buf, zs.total_out));
490   while (!m_in->eof()) {
491     qt_atom_t next_atom = read_atom();
492     mxdebug_if(m_debug_headers, fmt::format("{0}'{1}' atom at {2}\n", space((level + 1) * 2 + 1), next_atom.fourcc, next_atom.pos));
493 
494     if (next_atom.fourcc == "moov")
495       handle_moov_atom(next_atom.to_parent(), level + 1);
496 
497     m_in->setFilePointer(next_atom.pos + next_atom.size);
498   }
499   m_in = old_in;
500 }
501 
502 void
handle_ctts_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)503 qtmp4_reader_c::handle_ctts_atom(qtmp4_demuxer_c &dmx,
504                                  qt_atom_t,
505                                  int level) {
506   auto version = m_in->read_uint8();
507   m_in->skip(3);                // version & flags
508 
509   auto count = m_in->read_uint32_be();
510   mxdebug_if(m_debug_headers, fmt::format("{0}Frame offset table v{2}: {1} raw entries\n", space(level * 2 + 1), count, static_cast<unsigned int>(version)));
511 
512   dmx.raw_frame_offset_table.reserve(dmx.raw_frame_offset_table.size() + count);
513 
514   size_t i;
515   for (i = 0; i < count; ++i) {
516     qt_frame_offset_t frame_offset;
517 
518     frame_offset.count  = m_in->read_uint32_be();
519     frame_offset.offset = mtx::math::to_signed(m_in->read_uint32_be());
520     dmx.raw_frame_offset_table.push_back(frame_offset);
521   }
522 
523   if (!m_debug_tables)
524     return;
525 
526   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.raw_frame_offset_table.size());
527 
528   for (auto idx = 0u; idx < end; ++idx)
529     mxdebug(fmt::format("{0}{1}: count {2} offset {3}\n", space((level + 1) * 2 + 1), idx, dmx.raw_frame_offset_table[idx].count, dmx.raw_frame_offset_table[idx].offset));
530 }
531 
532 void
handle_sgpd_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)533 qtmp4_reader_c::handle_sgpd_atom(qtmp4_demuxer_c &dmx,
534                                  qt_atom_t,
535                                  int level) {
536   auto version = m_in->read_uint8();
537   m_in->skip(3);                // flags
538 
539   auto grouping_type              = fourcc_c{m_in};
540   auto default_description_length = version == 1 ? m_in->read_uint32_be() : uint32_t{};
541 
542   if (version >= 2)
543     m_in->skip(4);              // default_sample_description_index
544 
545   auto count = m_in->read_uint32_be();
546 
547   mxdebug_if(m_debug_headers, fmt::format("{0}Sample group description table: version {1}, type '{2}', {3} raw entries\n", space(level * 2 + 2), static_cast<unsigned int>(version), grouping_type, count));
548 
549   if (grouping_type != "rap ")
550     return;
551 
552   dmx.random_access_point_table.reserve(dmx.random_access_point_table.size() + count);
553 
554   for (auto idx = 0u; idx < count; ++idx) {
555     if ((version == 1) && (default_description_length == 0))
556       m_in->skip(4);            // description_length
557 
558     auto byte                      = m_in->read_uint8();
559     auto num_leading_samples_known = (byte & 0x80) == 0x80;
560     auto num_leading_samples       = static_cast<unsigned int>(byte & 0x7f);
561 
562     dmx.random_access_point_table.emplace_back(num_leading_samples_known, num_leading_samples);
563   }
564 
565   if (!m_debug_tables)
566     return;
567 
568   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.random_access_point_table.size());
569 
570   for (auto idx = 0u; idx < end; ++idx)
571     mxdebug(fmt::format("{0}{1}: leading samples known {2} num {3}\n", space((level + 1) * 2 + 2), idx, dmx.random_access_point_table[idx].num_leading_samples_known, dmx.random_access_point_table[idx].num_leading_samples));
572 }
573 
574 void
handle_sbgp_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)575 qtmp4_reader_c::handle_sbgp_atom(qtmp4_demuxer_c &dmx,
576                                  qt_atom_t,
577                                  int level) {
578   auto version = m_in->read_uint8();
579   m_in->skip(3);                // flags
580 
581   auto grouping_type = fourcc_c{m_in};
582 
583   if (version == 1)
584     m_in->skip(4);              // grouping_type_parameter
585 
586   auto count = m_in->read_uint32_be();
587 
588   mxdebug_if(m_debug_headers, fmt::format("{0}Sample to group table: version {1}, type '{2}', {3} raw entries\n", space(level * 2 + 2), static_cast<unsigned int>(version), grouping_type, count));
589 
590   auto &table = dmx.sample_to_group_tables[grouping_type.value()];
591   table.reserve(table.size() + count);
592 
593   for (auto idx = 0u; idx < count; ++idx) {
594     auto sample_count            = m_in->read_uint32_be();
595     auto group_description_index = m_in->read_uint32_be();
596 
597     table.emplace_back(sample_count, group_description_index);
598   }
599 
600   if (!m_debug_tables)
601     return;
602 
603   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), table.size());
604 
605   for (auto idx = 0u; idx < end; ++idx)
606     mxdebug(fmt::format("{0}{1}: sample count {2} group description {3}\n", space((level + 1) * 2 + 2), idx, table[idx].sample_count, table[idx].group_description_index));
607 }
608 
609 void
handle_dcom_atom(qt_atom_t,int level)610 qtmp4_reader_c::handle_dcom_atom(qt_atom_t,
611                                  int level) {
612   m_compression_algorithm = fourcc_c{m_in->read_uint32_be()};
613   mxdebug_if(m_debug_headers, fmt::format("{0}Compression algorithm: {1}\n", space(level * 2 + 1), m_compression_algorithm));
614 }
615 
616 void
handle_hdlr_atom(qtmp4_demuxer_c & dmx,qt_atom_t atom,int level)617 qtmp4_reader_c::handle_hdlr_atom(qtmp4_demuxer_c &dmx,
618                                  qt_atom_t atom,
619                                  int level) {
620   hdlr_atom_t hdlr;
621 
622   if (sizeof(hdlr_atom_t) > atom.size)
623     print_atom_too_small_error("hdlr", atom, sizeof(hdlr_atom_t));
624 
625   if (m_in->read(&hdlr, sizeof(hdlr_atom_t)) != sizeof(hdlr_atom_t))
626     throw mtx::input::header_parsing_x();
627 
628   mxdebug_if(m_debug_headers, fmt::format("{0}Component type: {1:4s} subtype: {2:4s}\n", space(level * 2 + 1), reinterpret_cast<char const *>(&hdlr.type), reinterpret_cast<char const *>(&hdlr.subtype)));
629 
630   auto subtype = fourcc_c{reinterpret_cast<unsigned char const *>(&hdlr) + offsetof(hdlr_atom_t, subtype)};
631   if (subtype == "soun")
632     dmx.type = 'a';
633 
634   else if (subtype == "vide")
635     dmx.type = 'v';
636 
637   else if ((subtype == "text") || (subtype == "subp"))
638     dmx.type = 's';
639 }
640 
641 void
handle_mdhd_atom(qtmp4_demuxer_c & dmx,qt_atom_t atom,int level)642 qtmp4_reader_c::handle_mdhd_atom(qtmp4_demuxer_c &dmx,
643                                  qt_atom_t atom,
644                                  int level) {
645   if (1 > atom.size)
646     print_atom_too_small_error("mdhd", atom, sizeof(mdhd_atom_t));
647 
648   int version = m_in->read_uint8();
649 
650   if (0 == version) {
651     mdhd_atom_t mdhd;
652 
653     if (sizeof(mdhd_atom_t) > atom.size)
654       print_atom_too_small_error("mdhd", atom, sizeof(mdhd_atom_t));
655     if (m_in->read(&mdhd.flags, sizeof(mdhd_atom_t) - 1) != (sizeof(mdhd_atom_t) - 1))
656       throw mtx::input::header_parsing_x();
657 
658     dmx.time_scale      = get_uint32_be(&mdhd.time_scale);
659     dmx.global_duration = get_uint32_be(&mdhd.duration);
660     dmx.language        = decode_and_verify_language(get_uint16_be(&mdhd.language));
661 
662   } else if (1 == version) {
663     mdhd64_atom_t mdhd;
664 
665     if (sizeof(mdhd64_atom_t) > atom.size)
666       print_atom_too_small_error("mdhd", atom, sizeof(mdhd64_atom_t));
667     if (m_in->read(&mdhd.flags, sizeof(mdhd64_atom_t) - 1) != (sizeof(mdhd64_atom_t) - 1))
668       throw mtx::input::header_parsing_x();
669 
670     dmx.time_scale      = get_uint32_be(&mdhd.time_scale);
671     dmx.global_duration = get_uint64_be(&mdhd.duration);
672     dmx.language        = decode_and_verify_language(get_uint16_be(&mdhd.language));
673 
674   } else
675     mxerror(fmt::format(Y("Quicktime/MP4 reader: The 'media header' atom ('mdhd') uses the unsupported version {0}.\n"), version));
676 
677   mxdebug_if(m_debug_headers, fmt::format("{0}Time scale: {1}, duration: {2}, language: {3}\n", space(level * 2 + 1), dmx.time_scale, dmx.global_duration, dmx.language));
678 
679   if (0 == dmx.time_scale)
680     mxerror(Y("Quicktime/MP4 reader: The 'time scale' parameter was 0. This is not supported.\n"));
681 }
682 
683 void
handle_mdia_atom(qtmp4_demuxer_c & dmx,qt_atom_t parent,int level)684 qtmp4_reader_c::handle_mdia_atom(qtmp4_demuxer_c &dmx,
685                                  qt_atom_t parent,
686                                  int level) {
687   process_atom(parent, level, [&](qt_atom_t const &atom) {
688     if (atom.fourcc == "mdhd")
689       handle_mdhd_atom(dmx, atom.to_parent(), level + 1);
690 
691     else if (atom.fourcc == "hdlr")
692       handle_hdlr_atom(dmx, atom.to_parent(), level + 1);
693 
694     else if (atom.fourcc == "minf")
695       handle_minf_atom(dmx, atom.to_parent(), level + 1);
696   });
697 }
698 
699 void
handle_minf_atom(qtmp4_demuxer_c & dmx,qt_atom_t parent,int level)700 qtmp4_reader_c::handle_minf_atom(qtmp4_demuxer_c &dmx,
701                                  qt_atom_t parent,
702                                  int level) {
703   process_atom(parent, level, [&](qt_atom_t const &atom) {
704     if (atom.fourcc == "hdlr")
705       handle_hdlr_atom(dmx, atom.to_parent(), level + 1);
706 
707     else if (atom.fourcc == "stbl")
708       handle_stbl_atom(dmx, atom.to_parent(), level + 1);
709   });
710 }
711 
712 void
handle_moov_atom(qt_atom_t parent,int level)713 qtmp4_reader_c::handle_moov_atom(qt_atom_t parent,
714                                  int level) {
715   process_atom(parent, level, [&](qt_atom_t const &atom) {
716     if (atom.fourcc == "cmov")
717       handle_cmov_atom(atom.to_parent(), level + 1);
718 
719     else if (atom.fourcc == "mvhd")
720       handle_mvhd_atom(atom.to_parent(), level + 1);
721 
722     else if (atom.fourcc == "udta")
723       handle_udta_atom(atom.to_parent(), level + 1);
724 
725     else if (atom.fourcc == "mvex")
726       handle_mvex_atom(atom.to_parent(), level + 1);
727 
728     else if (atom.fourcc == "trak") {
729       auto dmx = std::make_shared<qtmp4_demuxer_c>(*this);
730       dmx->id  = m_demuxers.size();
731 
732       handle_trak_atom(*dmx, atom.to_parent(), level + 1);
733 
734       if ((!dmx->is_unknown() && dmx->codec) || dmx->is_subtitles() || dmx->is_video() || dmx->is_audio())
735         m_demuxers.push_back(dmx);
736     }
737   });
738 }
739 
740 void
handle_mvex_atom(qt_atom_t parent,int level)741 qtmp4_reader_c::handle_mvex_atom(qt_atom_t parent,
742                                  int level) {
743   process_atom(parent, level, [&](qt_atom_t const &atom) {
744     if (atom.fourcc == "trex")
745       handle_trex_atom(atom.to_parent(), level + 1);
746   });
747 }
748 
749 void
handle_trex_atom(qt_atom_t,int level)750 qtmp4_reader_c::handle_trex_atom(qt_atom_t,
751                                  int level) {
752   m_in->skip(1 + 3);            // Version, flags
753 
754   auto track_id                  = m_in->read_uint32_be();
755   auto &defaults                 = m_track_defaults[track_id];
756   defaults.sample_description_id = m_in->read_uint32_be();
757   defaults.sample_duration       = m_in->read_uint32_be();
758   defaults.sample_size           = m_in->read_uint32_be();
759   defaults.sample_flags          = m_in->read_uint32_be();
760 
761   mxdebug_if(m_debug_headers, fmt::format("{0}Sample defaults for track ID {1}: description idx {2} duration {3} size {4} flags {5}\n",
762                          space(level * 2 + 1), track_id, defaults.sample_description_id, defaults.sample_duration, defaults.sample_size, defaults.sample_flags));
763 }
764 
765 void
handle_moof_atom(qt_atom_t parent,int level,qt_atom_t const & moof_atom)766 qtmp4_reader_c::handle_moof_atom(qt_atom_t parent,
767                                  int level,
768                                  qt_atom_t const &moof_atom) {
769   m_moof_offset              = moof_atom.pos;
770   m_fragment_implicit_offset = moof_atom.pos;
771 
772   process_atom(parent, level, [&](qt_atom_t const &atom) {
773     if (atom.fourcc == "traf")
774       handle_traf_atom(atom.to_parent(), level + 1);
775   });
776 }
777 
778 void
handle_traf_atom(qt_atom_t parent,int level)779 qtmp4_reader_c::handle_traf_atom(qt_atom_t parent,
780                                  int level) {
781   process_atom(parent, level, [&](qt_atom_t const &atom) {
782     if (atom.fourcc == "tfhd")
783       handle_tfhd_atom(atom.to_parent(), level + 1);
784 
785     else if (atom.fourcc == "trun")
786       handle_trun_atom(atom.to_parent(), level + 1);
787 
788     else if (atom.fourcc == "edts") {
789       if (m_track_for_fragment)
790         handle_edts_atom(*m_track_for_fragment, atom.to_parent(), level + 1);
791     }
792 
793   });
794 
795   m_fragment           = nullptr;
796   m_track_for_fragment = nullptr;
797 }
798 
799 void
handle_tfhd_atom(qt_atom_t,int level)800 qtmp4_reader_c::handle_tfhd_atom(qt_atom_t,
801                                  int level) {
802   m_in->skip(1);                // Version
803 
804   auto flags     = m_in->read_uint24_be();
805   auto track_id  = m_in->read_uint32_be();
806   auto track_itr = std::find_if(m_demuxers.begin(), m_demuxers.end(), [track_id](qtmp4_demuxer_cptr const &dmx) { return dmx->container_id == track_id; });
807 
808   if (!track_id || !mtx::includes(m_track_defaults, track_id) || (m_demuxers.end() == track_itr)) {
809     mxdebug_if(m_debug_headers,
810                fmt::format("{0}tfhd atom with track_id({1}) == 0, no entry in trex for it or no track({2}) found\n",
811                            space(level * 2 + 1), track_id, static_cast<void *>(m_demuxers.end() == track_itr ? nullptr : track_itr->get())));
812     return;
813   }
814 
815   auto &defaults = m_track_defaults[track_id];
816   auto &track    = **track_itr;
817 
818   track.m_fragments.emplace_back();
819   auto &fragment = track.m_fragments.back();
820 
821   fragment.track_id              = track_id;
822   fragment.moof_offset           = m_moof_offset;
823   fragment.implicit_offset       = m_fragment_implicit_offset;
824   fragment.base_data_offset      = flags & QTMP4_TFHD_BASE_DATA_OFFSET      ? m_in->read_uint64_be()
825                                  : flags & QTMP4_TFHD_DEFAULT_BASE_IS_MOOF  ? fragment.moof_offset
826                                  :                                            fragment.implicit_offset;
827   fragment.sample_description_id = flags & QTMP4_TFHD_SAMPLE_DESCRIPTION_ID ? m_in->read_uint32_be() : defaults.sample_description_id;
828   fragment.sample_duration       = flags & QTMP4_TFHD_DEFAULT_DURATION      ? m_in->read_uint32_be() : defaults.sample_duration;
829   fragment.sample_size           = flags & QTMP4_TFHD_DEFAULT_SIZE          ? m_in->read_uint32_be() : defaults.sample_size;
830   fragment.sample_flags          = flags & QTMP4_TFHD_DEFAULT_FLAGS         ? m_in->read_uint32_be() : defaults.sample_flags;
831 
832   m_fragment           = &fragment;
833   m_track_for_fragment = &track;
834 
835   mxdebug_if(m_debug_headers,
836              fmt::format("{0}Atom flags 0x{1:05x} track_id {2} fragment content: moof_off {3} implicit_off {4} base_data_off {5} stsd_id {6} sample_duration {7} sample_size {8} sample_flags {9}\n",
837                          space(level * 2 + 1), flags, track_id,
838                          fragment.moof_offset, fragment.implicit_offset, fragment.base_data_offset, fragment.sample_description_id, fragment.sample_duration, fragment.sample_size, fragment.sample_flags));
839 }
840 
841 void
handle_trun_atom(qt_atom_t,int level)842 qtmp4_reader_c::handle_trun_atom(qt_atom_t,
843                                  int level) {
844   if (!m_fragment || !m_track_for_fragment) {
845     mxdebug_if(m_debug_headers, fmt::format("{0}No current fragment ({1}) or track for that fragment ({2})\n", space(2 * level + 1), static_cast<void *>(m_fragment), static_cast<void *>(m_track_for_fragment)));
846     return;
847   }
848 
849   m_in->skip(1);                // Version
850   auto flags   = m_in->read_uint24_be();
851   auto entries = m_in->read_uint32_be();
852   auto &track  = *m_track_for_fragment;
853 
854   if (track.raw_frame_offset_table.empty() && !track.sample_table.empty())
855     track.raw_frame_offset_table.emplace_back(track.sample_table.size(), 0);
856 
857   auto data_offset        = flags & QTMP4_TRUN_DATA_OFFSET ? m_in->read_uint32_be() : 0;
858   auto first_sample_flags = flags & QTMP4_TRUN_FIRST_SAMPLE_FLAGS ? m_in->read_uint32_be() : m_fragment->sample_flags;
859   auto offset             = m_fragment->base_data_offset + data_offset;
860 
861   auto calc_reserve_size  = [entries](auto current_size) {
862     auto constexpr reserve_chunk_size = 1024u;
863     return (((current_size + entries) / reserve_chunk_size) + 1) * reserve_chunk_size;
864   };
865 
866   track.durmap_table.reserve(calc_reserve_size(track.durmap_table.size()));
867   track.sample_table.reserve(calc_reserve_size(track.sample_table.size()));
868   track.chunk_table.reserve(calc_reserve_size(track.chunk_table.size()));
869   track.raw_frame_offset_table.reserve(calc_reserve_size(track.raw_frame_offset_table.size()));
870   track.keyframe_table.reserve(calc_reserve_size(track.keyframe_table.size()));
871 
872   std::vector<uint32_t> all_sample_flags;
873   std::vector<bool> all_keyframe_flags;
874 
875   if (m_debug_tables) {
876     all_sample_flags.reserve(entries);
877     all_keyframe_flags.reserve(entries);
878   }
879 
880   for (auto idx = 0u; idx < entries; ++idx) {
881     auto sample_duration = flags & QTMP4_TRUN_SAMPLE_DURATION   ? m_in->read_uint32_be() : m_fragment->sample_duration;
882     auto sample_size     = flags & QTMP4_TRUN_SAMPLE_SIZE       ? m_in->read_uint32_be() : m_fragment->sample_size;
883     auto sample_flags    = flags & QTMP4_TRUN_SAMPLE_FLAGS      ? m_in->read_uint32_be() : idx > 0 ? m_fragment->sample_flags : first_sample_flags;
884     auto ctts_duration   = flags & QTMP4_TRUN_SAMPLE_CTS_OFFSET ? m_in->read_uint32_be() : 0;
885     auto keyframe        = !track.is_video()                    ? true                   : !(sample_flags & (QTMP4_FRAG_SAMPLE_FLAG_IS_NON_SYNC | QTMP4_FRAG_SAMPLE_FLAG_DEPENDS_YES));
886 
887     track.durmap_table.emplace_back(1, sample_duration);
888     track.sample_table.emplace_back(sample_size);
889     track.chunk_table.emplace_back(1, offset);
890     track.raw_frame_offset_table.emplace_back(1, mtx::math::to_signed(ctts_duration));
891 
892     if (keyframe)
893       track.keyframe_table.emplace_back(track.num_frames_from_trun + 1);
894 
895     offset += sample_size;
896 
897     track.num_frames_from_trun++;
898 
899     if (!m_debug_tables)
900       continue;
901 
902     all_sample_flags.emplace_back(sample_flags);
903     all_keyframe_flags.push_back(keyframe);
904   }
905 
906   m_fragment->implicit_offset = offset;
907   m_fragment_implicit_offset  = offset;
908 
909   mxdebug_if(m_debug_headers, fmt::format("{0}Number of entries: {1}\n", space((level + 1) * 2 + 1), entries));
910 
911   if (!m_debug_tables)
912     return;
913 
914   auto spc                = space((level + 2) * 2 + 1);
915   auto durmap_start       = track.durmap_table.size()           - entries;
916   auto sample_start       = track.sample_table.size()           - entries;
917   auto chunk_start        = track.chunk_table.size()            - entries;
918   auto frame_offset_start = track.raw_frame_offset_table.size() - entries;
919   auto end                = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), entries);
920 
921   for (auto idx = 0u; idx < end; ++idx)
922     mxdebug(fmt::format("{0}{1}: duration {2} size {3} data start {4} end {5} pts offset {6} key? {7} raw flags 0x{8:08x}\n",
923                         spc, idx,
924                         track.durmap_table[durmap_start + idx].duration,
925                         track.sample_table[sample_start + idx].size,
926                         track.chunk_table[chunk_start + idx].pos,
927                         (track.sample_table[sample_start + idx].size + track.chunk_table[chunk_start + idx].pos),
928                         track.raw_frame_offset_table[frame_offset_start + idx].offset,
929                         static_cast<unsigned int>(all_keyframe_flags[idx]),
930                         all_sample_flags[idx]));
931 }
932 
933 void
handle_mvhd_atom(qt_atom_t atom,int level)934 qtmp4_reader_c::handle_mvhd_atom(qt_atom_t atom,
935                                  int level) {
936   mvhd_atom_t mvhd;
937 
938   if (sizeof(mvhd_atom_t) > (atom.size - atom.hsize))
939     print_atom_too_small_error("mvhd", atom, sizeof(mvhd_atom_t));
940   if (m_in->read(&mvhd, sizeof(mvhd_atom_t)) != sizeof(mvhd_atom_t))
941     throw mtx::input::header_parsing_x();
942 
943   m_time_scale  = get_uint32_be(&mvhd.time_scale);
944   auto duration = get_uint32_be(&mvhd.duration);
945 
946   if ((duration != std::numeric_limits<uint32_t>::max()) && (m_time_scale != 0))
947     m_duration = mtx::to_uint(mtx::rational(duration, m_time_scale) * 1'000'000'000ull);
948 
949   mxdebug_if(m_debug_headers, fmt::format("{0}Time scale: {1} duration: {2}\n", space(level * 2 + 1), m_time_scale, m_duration ? mtx::string::format_timestamp(*m_duration) : "—"s));
950 }
951 
952 void
handle_udta_atom(qt_atom_t parent,int level)953 qtmp4_reader_c::handle_udta_atom(qt_atom_t parent,
954                                  int level) {
955   process_atom(parent, level, [&](qt_atom_t const &atom) {
956     if (atom.fourcc == "chpl")
957       handle_chpl_atom(atom.to_parent(), level + 1);
958 
959     else if (atom.fourcc == "meta")
960       handle_meta_atom(atom.to_parent(), level + 1);
961   });
962 }
963 
964 void
handle_chpl_atom(qt_atom_t,int level)965 qtmp4_reader_c::handle_chpl_atom(qt_atom_t,
966                                  int level) {
967   if (m_ti.m_no_chapters || m_chapters)
968     return;
969 
970   m_in->skip(1 + 3 + 4);          // Version, flags, zero
971 
972   int count = m_in->read_uint8();
973   mxdebug_if(m_debug_chapters, fmt::format("{0}Chapter list: {1} entries\n", space(level * 2 + 1), count));
974 
975   if (0 == count)
976     return;
977 
978   std::vector<qtmp4_chapter_entry_t> entries;
979 
980   entries.reserve(count);
981 
982   int i;
983   for (i = 0; i < count; ++i) {
984     uint64_t timestamp = m_in->read_uint64_be() * 100;
985     memory_cptr buf   = memory_c::alloc(m_in->read_uint8() + 1);
986     memset(buf->get_buffer(), 0, buf->get_size());
987 
988     if (m_in->read(buf->get_buffer(), buf->get_size() - 1) != (buf->get_size() - 1))
989       break;
990 
991     entries.push_back(qtmp4_chapter_entry_t(std::string(reinterpret_cast<char *>(buf->get_buffer())), timestamp));
992   }
993 
994   recode_chapter_entries(entries);
995   process_chapter_entries(level, entries);
996 }
997 
998 void
handle_meta_atom(qt_atom_t parent,int level)999 qtmp4_reader_c::handle_meta_atom(qt_atom_t parent,
1000                                  int level) {
1001   m_in->skip(1 + 3);        // version & flags
1002 
1003   process_atom(parent, level, [&](qt_atom_t const &atom) {
1004     if (atom.fourcc == "ilst")
1005       handle_ilst_atom(atom.to_parent(), level + 1);
1006   });
1007 }
1008 
1009 void
handle_ilst_atom(qt_atom_t parent,int level)1010 qtmp4_reader_c::handle_ilst_atom(qt_atom_t parent,
1011                                  int level) {
1012   process_atom(parent, level, [&](qt_atom_t const &atom) {
1013     if (atom.fourcc == "----")
1014       handle_4dashes_atom(atom.to_parent(), level + 1);
1015 
1016     else if (atom.fourcc == "covr")
1017       handle_covr_atom(atom.to_parent(), level + 1);
1018   });
1019 }
1020 
1021 std::string
read_string_atom(qt_atom_t atom,size_t num_skipped)1022 qtmp4_reader_c::read_string_atom(qt_atom_t atom,
1023                                  size_t num_skipped) {
1024   if ((num_skipped + atom.hsize) > atom.size)
1025     return "";
1026 
1027   std::string string;
1028   size_t length = atom.size - atom.hsize - num_skipped;
1029 
1030   m_in->skip(num_skipped);
1031   m_in->read(string, length);
1032 
1033   return string;
1034 }
1035 
1036 void
handle_4dashes_atom(qt_atom_t parent,int level)1037 qtmp4_reader_c::handle_4dashes_atom(qt_atom_t parent,
1038                                     int level) {
1039   std::string name, mean, data;
1040 
1041   process_atom(parent, level, [&](qt_atom_t const &atom) {
1042     if (atom.fourcc == "name")
1043       name = read_string_atom(atom, 4);
1044 
1045     else if (atom.fourcc == "mean")
1046       mean = read_string_atom(atom, 4);
1047 
1048     else if (atom.fourcc == "data")
1049       data = read_string_atom(atom, 8);
1050   });
1051 
1052   mxdebug_if(m_debug_headers, fmt::format("{0}'----' content: name={1} mean={2} data={3}\n", space(2 * level + 1), name, mean, data));
1053 
1054   if (name == "iTunSMPB")
1055     parse_itunsmpb(data);
1056 }
1057 
1058 void
handle_covr_atom(qt_atom_t parent,int level)1059 qtmp4_reader_c::handle_covr_atom(qt_atom_t parent,
1060                                  int level) {
1061   process_atom(parent, level, [&](qt_atom_t const &atom) {
1062     if (atom.fourcc != "data")
1063       return;
1064 
1065     size_t data_size = atom.size - atom.hsize;
1066     if (data_size <= 8)
1067       return;
1068 
1069     try {
1070       auto type = m_in->read_uint32_be();
1071       if (!mtx::included_in<int>(type, mtx::mp4::ATOM_DATA_TYPE_BMP, mtx::mp4::ATOM_DATA_TYPE_JPEG, mtx::mp4::ATOM_DATA_TYPE_PNG))
1072         return;
1073 
1074       m_in->skip(4);
1075 
1076       data_size -= 8;
1077 
1078       auto attach_mode         = attachment_requested(m_attachment_id);
1079 
1080       auto attachment          = std::make_shared<attachment_t>();
1081 
1082       attachment->name         = fmt::format("cover.{}", type == mtx::mp4::ATOM_DATA_TYPE_PNG ? "png" : type == mtx::mp4::ATOM_DATA_TYPE_BMP ? "bmp" : "jpg");
1083       attachment->mime_type    = fmt::format("image/{}", type == mtx::mp4::ATOM_DATA_TYPE_PNG ? "png" : type == mtx::mp4::ATOM_DATA_TYPE_BMP ? "bmp" : "jpeg");
1084       attachment->data         = m_in->read(data_size);
1085       attachment->ui_id        = m_attachment_id++;
1086       attachment->to_all_files = ATTACH_MODE_TO_ALL_FILES == attach_mode;
1087       attachment->source_file  = m_ti.m_fname;
1088 
1089       if (ATTACH_MODE_SKIP != attach_mode)
1090         add_attachment(attachment);
1091 
1092     } catch (mtx::exception const &ex) {
1093       mxdebug_if(m_debug_headers, fmt::format("{0}exception while reading cover art: {}\n", ex.what()));
1094     }
1095   });
1096 }
1097 
1098 void
parse_itunsmpb(std::string data)1099 qtmp4_reader_c::parse_itunsmpb(std::string data) {
1100   data = to_utf8(Q(data).replace(QRegularExpression{"[^0-9da-fA-F]+"}, {}));
1101 
1102   if (16 > data.length())
1103     return;
1104 
1105   try {
1106     m_audio_encoder_delay_samples = mtx::string::from_hex(data.substr(8, 8));
1107   } catch (std::bad_cast &) {
1108   }
1109 }
1110 
1111 void
read_chapter_track()1112 qtmp4_reader_c::read_chapter_track() {
1113   if (m_ti.m_no_chapters || m_chapters)
1114     return;
1115 
1116   auto chapter_dmx_itr = std::find_if(m_demuxers.begin(), m_demuxers.end(), [](qtmp4_demuxer_cptr const &dmx) { return dmx->is_chapters(); });
1117   if (m_demuxers.end() == chapter_dmx_itr)
1118     return;
1119 
1120   if ((*chapter_dmx_itr)->sample_table.empty())
1121     return;
1122 
1123   std::vector<qtmp4_chapter_entry_t> entries;
1124   uint64_t pts_scale_gcd = std::gcd(static_cast<uint64_t>(1000000000ull), static_cast<uint64_t>((*chapter_dmx_itr)->time_scale));
1125   uint64_t pts_scale_num = 1000000000ull                                         / pts_scale_gcd;
1126   uint64_t pts_scale_den = static_cast<uint64_t>((*chapter_dmx_itr)->time_scale) / pts_scale_gcd;
1127 
1128   entries.reserve((*chapter_dmx_itr)->sample_table.size());
1129 
1130   for (auto &sample : (*chapter_dmx_itr)->sample_table) {
1131     if (2 >= sample.size)
1132       continue;
1133 
1134     m_in->setFilePointer(sample.pos);
1135     memory_cptr chunk(memory_c::alloc(sample.size));
1136     if (m_in->read(chunk->get_buffer(), sample.size) != sample.size)
1137       continue;
1138 
1139     unsigned int name_len = get_uint16_be(chunk->get_buffer());
1140     if ((name_len + 2) > sample.size)
1141       continue;
1142 
1143     entries.push_back(qtmp4_chapter_entry_t(std::string(reinterpret_cast<char *>(chunk->get_buffer()) + 2, name_len),
1144                                             sample.pts * pts_scale_num / pts_scale_den));
1145   }
1146 
1147   recode_chapter_entries(entries);
1148   process_chapter_entries(0, entries);
1149 }
1150 
1151 void
process_chapter_entries(int level,std::vector<qtmp4_chapter_entry_t> & entries)1152 qtmp4_reader_c::process_chapter_entries(int level,
1153                                         std::vector<qtmp4_chapter_entry_t> &entries) {
1154   if (entries.empty())
1155     return;
1156 
1157   mxdebug_if(m_debug_chapters, fmt::format("{0}{1} chapter(s):\n", space((level + 1) * 2 + 1), entries.size()));
1158 
1159   std::stable_sort(entries.begin(), entries.end());
1160 
1161   auto af_out = std::make_shared<mm_mem_io_c>(nullptr, 0, 1000);
1162   auto &out   = *af_out;
1163   out.set_file_name(m_ti.m_fname);
1164   out.write_bom("UTF-8");
1165 
1166   unsigned int i = 0;
1167   for (; entries.size() > i; ++i) {
1168     qtmp4_chapter_entry_t &chapter = entries[i];
1169 
1170     mxdebug_if(m_debug_chapters, fmt::format("{0}{1}: start {3} name {2}\n", space((level + 1) * 2 + 1), i, chapter.m_name, mtx::string::format_timestamp(chapter.m_timestamp)));
1171 
1172     out.puts(fmt::format("CHAPTER{0:02}={1:02}:{2:02}:{3:02}.{4:03}\n"
1173                          "CHAPTER{0:02}NAME={5}\n",
1174                          i,
1175                           chapter.m_timestamp / 60 / 60 / 1'000'000'000,
1176                          (chapter.m_timestamp      / 60 / 1'000'000'000) %    60,
1177                          (chapter.m_timestamp           / 1'000'000'000) %    60,
1178                          (chapter.m_timestamp           /     1'000'000) % 1'000,
1179                          chapter.m_name));
1180   }
1181 
1182   mm_text_io_c text_out(af_out);
1183   try {
1184     m_chapters = mtx::chapters::parse(&text_out, 0, -1, 0, m_ti.m_chapter_language, "", true);
1185     mtx::chapters::align_uids(m_chapters.get());
1186 
1187     auto const &sync = mtx::includes(m_ti.m_timestamp_syncs, track_info_c::chapter_track_id) ? m_ti.m_timestamp_syncs[track_info_c::chapter_track_id]
1188                      : mtx::includes(m_ti.m_timestamp_syncs, track_info_c::all_tracks_id)    ? m_ti.m_timestamp_syncs[track_info_c::all_tracks_id]
1189                      :                                                                         timestamp_sync_t{};
1190     mtx::chapters::adjust_timestamps(*m_chapters, sync.displacement, sync.factor);
1191 
1192   } catch (mtx::chapters::parser_x &ex) {
1193     mxerror(fmt::format(Y("The MP4 file '{0}' contains chapters whose format was not recognized. This is often the case if the chapters are not encoded in UTF-8. Use the '--chapter-charset' option in order to specify the charset to use.\n"), m_ti.m_fname));
1194   }
1195 }
1196 
1197 void
handle_stbl_atom(qtmp4_demuxer_c & dmx,qt_atom_t parent,int level)1198 qtmp4_reader_c::handle_stbl_atom(qtmp4_demuxer_c &dmx,
1199                                  qt_atom_t parent,
1200                                  int level) {
1201   process_atom(parent, level, [&](qt_atom_t const &atom) {
1202     if (atom.fourcc == "stts")
1203       handle_stts_atom(dmx, atom.to_parent(), level + 1);
1204 
1205     else if (atom.fourcc == "stsd")
1206       handle_stsd_atom(dmx, atom.to_parent(), level + 1);
1207 
1208     else if (atom.fourcc == "stss")
1209       handle_stss_atom(dmx, atom.to_parent(), level + 1);
1210 
1211     else if (atom.fourcc == "stsc")
1212       handle_stsc_atom(dmx, atom.to_parent(), level + 1);
1213 
1214     else if (atom.fourcc == "stsz")
1215       handle_stsz_atom(dmx, atom.to_parent(), level + 1);
1216 
1217     else if (atom.fourcc == "stco")
1218       handle_stco_atom(dmx, atom.to_parent(), level + 1);
1219 
1220     else if (atom.fourcc == "co64")
1221       handle_co64_atom(dmx, atom.to_parent(), level + 1);
1222 
1223     else if (atom.fourcc == "ctts")
1224       handle_ctts_atom(dmx, atom.to_parent(), level + 1);
1225 
1226     else if (atom.fourcc == "sgpd")
1227       handle_sgpd_atom(dmx, atom.to_parent(), level + 1);
1228 
1229     else if (atom.fourcc == "sbgp")
1230       handle_sbgp_atom(dmx, atom.to_parent(), level + 1);
1231   });
1232 }
1233 
1234 void
handle_stco_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1235 qtmp4_reader_c::handle_stco_atom(qtmp4_demuxer_c &dmx,
1236                                  qt_atom_t,
1237                                  int level) {
1238   m_in->skip(1 + 3);        // version & flags
1239   uint32_t count = m_in->read_uint32_be();
1240 
1241   mxdebug_if(m_debug_headers, fmt::format("{0}Chunk offset table: {1} entries\n", space(level * 2 + 1), count));
1242 
1243   dmx.chunk_table.reserve(dmx.chunk_table.size() + count);
1244 
1245   for (auto i = 0u; i < count; ++i)
1246     dmx.chunk_table.emplace_back(0, m_in->read_uint32_be());
1247 
1248   if (!m_debug_tables)
1249     return;
1250 
1251   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.chunk_table.size());
1252 
1253   for (auto idx = 0u; idx < end; ++idx)
1254     mxdebug(fmt::format("{0}  {1}\n", space(level * 2 + 1), dmx.chunk_table[idx].pos));
1255 }
1256 
1257 void
handle_co64_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1258 qtmp4_reader_c::handle_co64_atom(qtmp4_demuxer_c &dmx,
1259                                  qt_atom_t,
1260                                  int level) {
1261   m_in->skip(1 + 3);        // version & flags
1262   uint32_t count = m_in->read_uint32_be();
1263 
1264   mxdebug_if(m_debug_headers, fmt::format("{0}64bit chunk offset table: {1} entries\n", space(level * 2 + 1), count));
1265 
1266   dmx.chunk_table.reserve(dmx.chunk_table.size() + count);
1267 
1268   for (auto i = 0u; i < count; ++i)
1269     dmx.chunk_table.emplace_back(0, m_in->read_uint64_be());
1270 
1271   if (!m_debug_tables)
1272     return;
1273 
1274   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.chunk_table.size());
1275 
1276   for (auto idx = 0u; idx < end; ++idx)
1277     mxdebug(fmt::format("{0}  {1}\n", space(level * 2 + 1), dmx.chunk_table[idx].pos));
1278 }
1279 
1280 void
handle_stsc_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1281 qtmp4_reader_c::handle_stsc_atom(qtmp4_demuxer_c &dmx,
1282                                  qt_atom_t,
1283                                  int level) {
1284   m_in->skip(1 + 3);        // version & flags
1285   uint32_t count = m_in->read_uint32_be();
1286   size_t i;
1287 
1288   dmx.chunkmap_table.reserve(dmx.chunkmap_table.size() + count);
1289 
1290   for (i = 0; i < count; ++i) {
1291     qt_chunkmap_t chunkmap;
1292 
1293     chunkmap.first_chunk           = m_in->read_uint32_be() - 1;
1294     chunkmap.samples_per_chunk     = m_in->read_uint32_be();
1295     chunkmap.sample_description_id = m_in->read_uint32_be();
1296     dmx.chunkmap_table.push_back(chunkmap);
1297   }
1298 
1299   mxdebug_if(m_debug_headers, fmt::format("{0}Sample to chunk/chunkmap table: {1} entries\n", space(level * 2 + 1), count));
1300   if (!m_debug_tables)
1301     return;
1302 
1303   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.chunkmap_table.size());
1304 
1305   for (auto idx = 0u; idx < end; ++idx)
1306     mxdebug(fmt::format("{0}{1}: first_chunk {2} samples_per_chunk {3} sample_description_id {4}\n",
1307                         space((level + 1) * 2 + 1), idx, dmx.chunkmap_table[idx].first_chunk, dmx.chunkmap_table[idx].samples_per_chunk, dmx.chunkmap_table[idx].sample_description_id));
1308 }
1309 
1310 void
handle_stsd_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1311 qtmp4_reader_c::handle_stsd_atom(qtmp4_demuxer_c &dmx,
1312                                  qt_atom_t,
1313                                  int level) {
1314   m_in->skip(1 + 3);        // version & flags
1315   uint32_t count = m_in->read_uint32_be();
1316 
1317   size_t i;
1318   for (i = 0; i < count; ++i) {
1319     int64_t pos   = m_in->getFilePointer();
1320     uint32_t size = m_in->read_uint32_be();
1321 
1322     if (4 > size)
1323       mxerror(fmt::format(Y("Quicktime/MP4 reader: The 'size' field is too small in the stream description atom for track ID {0}.\n"), dmx.id));
1324 
1325     dmx.stsd = memory_c::alloc(size);
1326     auto priv     = dmx.stsd->get_buffer();
1327 
1328     put_uint32_be(priv, size);
1329     if (m_in->read(priv + sizeof(uint32_t), size - sizeof(uint32_t)) != (size - sizeof(uint32_t)))
1330       mxerror(fmt::format(Y("Quicktime/MP4 reader: Could not read the stream description atom for track ID {0}.\n"), dmx.id));
1331 
1332     dmx.handle_stsd_atom(size, level);
1333 
1334     m_in->setFilePointer(pos + size);
1335   }
1336 }
1337 
1338 void
handle_stss_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1339 qtmp4_reader_c::handle_stss_atom(qtmp4_demuxer_c &dmx,
1340                                  qt_atom_t,
1341                                  int level) {
1342   m_in->skip(1 + 3);        // version & flags
1343   uint32_t count = m_in->read_uint32_be();
1344 
1345   dmx.keyframe_table.reserve(dmx.keyframe_table.size() + count);
1346 
1347   size_t i;
1348   for (i = 0; i < count; ++i)
1349     dmx.keyframe_table.push_back(m_in->read_uint32_be());
1350 
1351   std::sort(dmx.keyframe_table.begin(), dmx.keyframe_table.end());
1352 
1353   mxdebug_if(m_debug_headers, fmt::format("{0}Sync/keyframe table: {1} entries\n", space(level * 2 + 1), count));
1354   if (!m_debug_tables)
1355     return;
1356 
1357   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.keyframe_table.size());
1358 
1359   for (auto idx = 0u; idx < end; ++idx)
1360     mxdebug(fmt::format("{0}keyframe at {1}\n", space((level + 1) * 2 + 1), dmx.keyframe_table[idx]));
1361 }
1362 
1363 void
handle_stsz_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1364 qtmp4_reader_c::handle_stsz_atom(qtmp4_demuxer_c &dmx,
1365                                  qt_atom_t,
1366                                  int level) {
1367   m_in->skip(1 + 3);        // version & flags
1368   uint32_t sample_size = m_in->read_uint32_be();
1369   uint32_t count       = m_in->read_uint32_be();
1370 
1371   if (0 == sample_size) {
1372     dmx.sample_table.reserve(dmx.sample_table.size() + count);
1373 
1374     size_t i;
1375     for (i = 0; i < count; ++i) {
1376       qt_sample_t sample;
1377 
1378       sample.size = m_in->read_uint32_be();
1379 
1380       // This is a sanity check against damaged samples. I have one of
1381       // those in which one sample was suppposed to be > 2GB big.
1382       if (sample.size >= 100 * 1024 * 1024)
1383         sample.size = 0;
1384 
1385       dmx.sample_table.push_back(sample);
1386     }
1387 
1388     mxdebug_if(m_debug_headers, fmt::format("{0}Sample size table: {1} entries\n", space(level * 2 + 1), count));
1389     if (m_debug_tables) {
1390       auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.sample_table.size());
1391 
1392       for (auto idx = 0u; idx < end; ++idx)
1393         mxdebug(fmt::format("{0}{1}: size {2}\n", space((level + 1) * 2 + 1), idx, dmx.sample_table[idx].size));
1394     }
1395 
1396   } else {
1397     dmx.sample_size = sample_size;
1398     mxdebug_if(m_debug_headers, fmt::format("{0}Sample size table; global sample size: {1}\n", space(level * 2 + 1), sample_size));
1399   }
1400 }
1401 
1402 void
handle_sttd_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1403 qtmp4_reader_c::handle_sttd_atom(qtmp4_demuxer_c &dmx,
1404                                  qt_atom_t,
1405                                  int level) {
1406   m_in->skip(1 + 3);        // version & flags
1407   uint32_t count = m_in->read_uint32_be();
1408 
1409   dmx.durmap_table.reserve(dmx.durmap_table.size() + count);
1410 
1411   size_t i;
1412   for (i = 0; i < count; ++i) {
1413     qt_durmap_t durmap;
1414 
1415     durmap.number   = m_in->read_uint32_be();
1416     durmap.duration = m_in->read_uint32_be();
1417     dmx.durmap_table.push_back(durmap);
1418   }
1419 
1420   mxdebug_if(m_debug_headers, fmt::format("{0}Sample duration table: {1} entries\n", space(level * 2 + 1), count));
1421   if (!m_debug_tables)
1422     return;
1423 
1424   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.durmap_table.size());
1425 
1426   for (auto idx = 0u; idx < end; ++idx)
1427     mxdebug(fmt::format("{0}{1}: number {2} duration {3}\n", space((level + 1) * 2 + 1), idx, dmx.durmap_table[idx].number, dmx.durmap_table[idx].duration));
1428 }
1429 
1430 void
handle_stts_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1431 qtmp4_reader_c::handle_stts_atom(qtmp4_demuxer_c &dmx,
1432                                  qt_atom_t,
1433                                  int level) {
1434   m_in->skip(1 + 3);        // version & flags
1435   uint32_t count = m_in->read_uint32_be();
1436 
1437   dmx.durmap_table.reserve(dmx.durmap_table.size() + count);
1438 
1439   size_t i;
1440   for (i = 0; i < count; ++i) {
1441     qt_durmap_t durmap;
1442 
1443     durmap.number   = m_in->read_uint32_be();
1444     durmap.duration = m_in->read_uint32_be();
1445     dmx.durmap_table.push_back(durmap);
1446   }
1447 
1448   mxdebug_if(m_debug_headers, fmt::format("{0}Sample duration table: {1} entries\n", space(level * 2 + 1), count));
1449   if (!m_debug_tables)
1450     return;
1451 
1452   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.durmap_table.size());
1453 
1454   for (auto idx = 0u; idx < end; ++idx)
1455     mxdebug(fmt::format("{0}{1}: number {2} duration {3}\n", space((level + 1) * 2 + 1), idx, dmx.durmap_table[idx].number, dmx.durmap_table[idx].duration));
1456 }
1457 
1458 void
handle_edts_atom(qtmp4_demuxer_c & dmx,qt_atom_t parent,int level)1459 qtmp4_reader_c::handle_edts_atom(qtmp4_demuxer_c &dmx,
1460                                  qt_atom_t parent,
1461                                  int level) {
1462   process_atom(parent, level, [&](qt_atom_t const &atom) {
1463     if (atom.fourcc == "elst")
1464       handle_elst_atom(dmx, atom.to_parent(), level + 1);
1465   });
1466 }
1467 
1468 void
handle_elst_atom(qtmp4_demuxer_c & dmx,qt_atom_t,int level)1469 qtmp4_reader_c::handle_elst_atom(qtmp4_demuxer_c &dmx,
1470                                  qt_atom_t,
1471                                  int level) {
1472   uint8_t version = m_in->read_uint8();
1473   m_in->skip(3);                // flags
1474   uint32_t count  = m_in->read_uint32_be();
1475   dmx.editlist_table.resize(count);
1476 
1477   size_t i;
1478   for (i = 0; i < count; ++i) {
1479     auto &editlist = dmx.editlist_table[i];
1480 
1481     if (1 == version) {
1482       editlist.segment_duration = m_in->read_uint64_be();
1483       editlist.media_time       = static_cast<int64_t>(m_in->read_uint64_be());
1484     } else {
1485       editlist.segment_duration = m_in->read_uint32_be();
1486       editlist.media_time       = static_cast<int32_t>(m_in->read_uint32_be());
1487     }
1488     editlist.media_rate_integer  = m_in->read_uint16_be();
1489     editlist.media_rate_fraction = m_in->read_uint16_be();
1490   }
1491 
1492   mxdebug_if(m_debug_headers, fmt::format("{0}Edit list table: {1} entries\n", space(level * 2 + 1), count));
1493   if (!m_debug_tables)
1494     return;
1495 
1496   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), dmx.editlist_table.size());
1497 
1498   for (auto idx = 0u; idx < end; ++idx)
1499     mxdebug(fmt::format("{0}{1}: segment duration {2} media time {3} media rate {4}.{5}\n",
1500                         space((level + 1) * 2 + 1), idx, dmx.editlist_table[idx].segment_duration, dmx.editlist_table[idx].media_time, dmx.editlist_table[idx].media_rate_integer, dmx.editlist_table[idx].media_rate_fraction));
1501 }
1502 
1503 void
handle_tkhd_atom(qtmp4_demuxer_c & dmx,qt_atom_t atom,int level)1504 qtmp4_reader_c::handle_tkhd_atom(qtmp4_demuxer_c &dmx,
1505                                  qt_atom_t atom,
1506                                  int level) {
1507   if (atom.size < 1)
1508     print_atom_too_small_error("tkhd", atom, 1);
1509 
1510   auto version       = m_in->read_uint8();
1511   auto expected_size = 4u + 2 * (version == 1 ? 8 : 4) + 4 + 4 + (version == 1 ? 8 : 4) + 2 * 4 + 3 * 2 + 2 + 9 * 4 + 2 * 4;
1512 
1513   if (atom.size < expected_size)
1514     print_atom_too_small_error("tkhd", atom, expected_size);
1515 
1516   auto flags = m_in->read_uint24_be();
1517 
1518   m_in->skip(2 * (version == 1 ? 8 : 4)); // creation_time, modification_time
1519 
1520   dmx.container_id = m_in->read_uint32_be();
1521 
1522   m_in->skip(4                        // reserved
1523              + (version == 1 ? 8 : 4) // duration
1524              + 2 * 4                  // reserved
1525              + 3 * 2                  // layer, alternate_group, volume
1526              + 2                      // reserved
1527              + 9 * 4);                // matrix
1528 
1529   dmx.m_enabled            = (flags & QTMP4_TKHD_FLAG_ENABLED) == QTMP4_TKHD_FLAG_ENABLED;
1530   dmx.v_display_width_flt  = m_in->read_uint32_be();
1531   dmx.v_display_height_flt = m_in->read_uint32_be();
1532   dmx.v_width              = dmx.v_display_width_flt  >> 16;
1533   dmx.v_height             = dmx.v_display_height_flt >> 16;
1534 
1535   mxdebug_if(m_debug_headers,
1536              fmt::format("{0}Track ID: {1} display width × height {2}.{3} × {4}.{5}\n",
1537                          space(level * 2 + 1), dmx.container_id, dmx.v_display_width_flt >> 16, dmx.v_display_width_flt & 0xffff, dmx.v_display_height_flt >> 16, dmx.v_display_height_flt & 0xffff));
1538 }
1539 
1540 void
handle_tref_atom(qtmp4_demuxer_c &,qt_atom_t parent,int level)1541 qtmp4_reader_c::handle_tref_atom(qtmp4_demuxer_c &/* dmx */,
1542                                  qt_atom_t parent,
1543                                  int level) {
1544   process_atom(parent, level, [&](qt_atom_t const &atom) {
1545     if (12 > atom.size)
1546       return;
1547 
1548     std::vector<uint32_t> track_ids;
1549     for (auto idx = (atom.size - 4) / 8; 0 < idx; --idx)
1550       track_ids.push_back(m_in->read_uint32_be());
1551 
1552     if (atom.fourcc == "chap")
1553       for (auto track_id : track_ids)
1554         m_chapter_track_ids[track_id] = true;
1555 
1556     if (!m_debug_headers)
1557       return;
1558 
1559     std::string message;
1560     for (auto track_id : track_ids)
1561       message += fmt::format(" {0}", track_id);
1562     mxdebug(fmt::format("{0}Reference type: {1}; track IDs:{2}\n", space(level * 2 + 1), atom.fourcc, message));
1563   });
1564 }
1565 
1566 void
handle_trak_atom(qtmp4_demuxer_c & dmx,qt_atom_t parent,int level)1567 qtmp4_reader_c::handle_trak_atom(qtmp4_demuxer_c &dmx,
1568                                  qt_atom_t parent,
1569                                  int level) {
1570   process_atom(parent, level, [&](qt_atom_t const &atom) {
1571     if (atom.fourcc == "tkhd")
1572       handle_tkhd_atom(dmx, atom.to_parent(), level + 1);
1573 
1574     else if (atom.fourcc == "mdia")
1575       handle_mdia_atom(dmx, atom.to_parent(), level + 1);
1576 
1577     else if (atom.fourcc == "edts")
1578       handle_edts_atom(dmx, atom.to_parent(), level + 1);
1579 
1580     else if (atom.fourcc == "tref")
1581       handle_tref_atom(dmx, atom.to_parent(), level + 1);
1582   });
1583 
1584   dmx.determine_codec();
1585   mxdebug_if(m_debug_headers, fmt::format("{0}Codec determination result: {1}\n", space(level * 2 + 1), dmx.codec));
1586 }
1587 
1588 file_status_e
read(generic_packetizer_c * packetizer,bool)1589 qtmp4_reader_c::read(generic_packetizer_c *packetizer,
1590                      bool) {
1591   size_t dmx_idx;
1592 
1593   for (dmx_idx = 0; dmx_idx < m_demuxers.size(); ++dmx_idx) {
1594     auto &dmx = *m_demuxers[dmx_idx];
1595 
1596     if ((-1 == dmx.ptzr) || (&ptzr(dmx.ptzr) != packetizer))
1597       continue;
1598 
1599     if (dmx.pos < dmx.m_index.size())
1600       break;
1601   }
1602 
1603   if (m_demuxers.size() == dmx_idx)
1604     return flush_packetizers();
1605 
1606   auto &dmx   = *m_demuxers[dmx_idx];
1607   auto &index = dmx.m_index[dmx.pos];
1608 
1609   m_in->setFilePointer(index.file_pos);
1610 
1611   int buffer_offset = 0;
1612   memory_cptr buffer;
1613 
1614   if (   dmx.is_video()
1615       && !dmx.pos
1616       && dmx.codec.is(codec_c::type_e::V_MPEG4_P2)
1617       && dmx.esds_parsed
1618       && (dmx.esds.decoder_config)) {
1619     buffer        = memory_c::alloc(index.size + dmx.esds.decoder_config->get_size());
1620     buffer_offset = dmx.esds.decoder_config->get_size();
1621 
1622     memcpy(buffer->get_buffer(), dmx.esds.decoder_config->get_buffer(), dmx.esds.decoder_config->get_size());
1623 
1624   } else {
1625     buffer = memory_c::alloc(index.size);
1626   }
1627 
1628   if (m_in->read(buffer->get_buffer() + buffer_offset, index.size) != index.size) {
1629     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Could not read chunk number {0}/{1} with size {2} from position {3}. Aborting.\n"),
1630                        dmx.pos, dmx.m_index.size(), index.size, index.file_pos));
1631     return flush_packetizers();
1632   }
1633 
1634   auto duration = dmx.m_use_frame_rate_for_duration ? *dmx.m_use_frame_rate_for_duration : index.duration;
1635   ptzr(dmx.ptzr).process(std::make_shared<packet_t>(buffer, index.timestamp, duration, index.is_keyframe ? VFT_IFRAME : VFT_PFRAMEAUTOMATIC, VFT_NOBFRAME));
1636   ++dmx.pos;
1637 
1638   m_bytes_processed += index.size;
1639 
1640   if (dmx.pos < dmx.m_index.size())
1641     return FILE_STATUS_MOREDATA;
1642 
1643   return flush_packetizers();
1644 }
1645 
1646 memory_cptr
create_bitmap_info_header(qtmp4_demuxer_c & dmx,const char * fourcc,size_t extra_size,const void * extra_data)1647 qtmp4_reader_c::create_bitmap_info_header(qtmp4_demuxer_c &dmx,
1648                                           const char *fourcc,
1649                                           size_t extra_size,
1650                                           const void *extra_data) {
1651   int full_size = sizeof(alBITMAPINFOHEADER) + extra_size;
1652   auto bih_p    = memory_c::alloc(full_size);
1653   auto bih      = reinterpret_cast<alBITMAPINFOHEADER *>(bih_p->get_buffer());
1654 
1655   memset(bih, 0, full_size);
1656   put_uint32_le(&bih->bi_size,       full_size);
1657   put_uint32_le(&bih->bi_width,      dmx.v_width);
1658   put_uint32_le(&bih->bi_height,     dmx.v_height);
1659   put_uint16_le(&bih->bi_planes,     1);
1660   put_uint16_le(&bih->bi_bit_count,  dmx.v_bitdepth);
1661   put_uint32_le(&bih->bi_size_image, dmx.v_width * dmx.v_height * 3);
1662   memcpy(&bih->bi_compression, fourcc, 4);
1663 
1664   if (0 != extra_size)
1665     memcpy(bih + 1, extra_data, extra_size);
1666 
1667   return bih_p;
1668 }
1669 
1670 bool
create_audio_packetizer_ac3(qtmp4_demuxer_c & dmx)1671 qtmp4_reader_c::create_audio_packetizer_ac3(qtmp4_demuxer_c &dmx) {
1672   auto buf = dmx.read_first_bytes(64);
1673 
1674   if (!buf || (-1 == dmx.m_ac3_header.find_in(buf))) {
1675     mxwarn_tid(m_ti.m_fname, dmx.id, Y("No AC-3 header found in first frame; track will be skipped.\n"));
1676     dmx.ok = false;
1677 
1678     return false;
1679   }
1680 
1681   dmx.ptzr = add_packetizer(new ac3_packetizer_c(this, m_ti, dmx.m_ac3_header.m_sample_rate, dmx.m_ac3_header.m_channels, dmx.m_ac3_header.m_bs_id));
1682   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1683 
1684   return true;
1685 }
1686 
1687 bool
create_audio_packetizer_alac(qtmp4_demuxer_c & dmx)1688 qtmp4_reader_c::create_audio_packetizer_alac(qtmp4_demuxer_c &dmx) {
1689   auto magic_cookie = memory_c::clone(dmx.stsd->get_buffer() + dmx.stsd_non_priv_struct_size + 12, dmx.stsd->get_size() - dmx.stsd_non_priv_struct_size - 12);
1690   dmx.ptzr          = add_packetizer(new alac_packetizer_c(this, m_ti, magic_cookie, dmx.a_samplerate, dmx.a_channels));
1691   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1692 
1693   return true;
1694 }
1695 
1696 bool
create_audio_packetizer_dts(qtmp4_demuxer_c & dmx)1697 qtmp4_reader_c::create_audio_packetizer_dts(qtmp4_demuxer_c &dmx) {
1698   dmx.ptzr = add_packetizer(new dts_packetizer_c(this, m_ti, dmx.m_dts_header));
1699   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1700 
1701   return true;
1702 }
1703 
1704 void
create_video_packetizer_svq1(qtmp4_demuxer_c & dmx)1705 qtmp4_reader_c::create_video_packetizer_svq1(qtmp4_demuxer_c &dmx) {
1706   m_ti.m_private_data = create_bitmap_info_header(dmx, "SVQ1");
1707   dmx.ptzr            = add_packetizer(new video_for_windows_packetizer_c(this, m_ti, 0, dmx.v_width, dmx.v_height));
1708 
1709   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1710 }
1711 
1712 void
create_video_packetizer_mpeg4_p2(qtmp4_demuxer_c & dmx)1713 qtmp4_reader_c::create_video_packetizer_mpeg4_p2(qtmp4_demuxer_c &dmx) {
1714   m_ti.m_private_data = create_bitmap_info_header(dmx, "DIVX");
1715   dmx.ptzr            = add_packetizer(new mpeg4_p2_video_packetizer_c(this, m_ti, 0, dmx.v_width, dmx.v_height, false));
1716 
1717   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1718 }
1719 
1720 void
create_video_packetizer_mpeg1_2(qtmp4_demuxer_c & dmx)1721 qtmp4_reader_c::create_video_packetizer_mpeg1_2(qtmp4_demuxer_c &dmx) {
1722   int version = !dmx.esds_parsed                                             ? (dmx.fourcc.value() & 0xff) - '0'
1723               : dmx.esds.object_type_id == mtx::mp4::OBJECT_TYPE_MPEG1Visual ? 1
1724               :                                                                2;
1725   dmx.ptzr    = add_packetizer(new mpeg1_2_video_packetizer_c(this, m_ti, version, -1, dmx.v_width, dmx.v_height, 0, 0, false));
1726 
1727   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1728 }
1729 
1730 void
create_video_packetizer_av1(qtmp4_demuxer_c & dmx)1731 qtmp4_reader_c::create_video_packetizer_av1(qtmp4_demuxer_c &dmx) {
1732   m_ti.m_private_data = dmx.priv.empty() || (4 > dmx.priv[0]->get_size()) ? memory_cptr{} : dmx.priv[0];
1733   dmx.ptzr            = add_packetizer(new av1_video_packetizer_c(this, m_ti));
1734 
1735   if (dmx.frame_rate)
1736     ptzr(dmx.ptzr).set_track_default_duration(mtx::to_int(mtx_mp_rational_t{boost::multiprecision::denominator(dmx.frame_rate), boost::multiprecision::numerator(dmx.frame_rate)} * 1'000'000'000ll));
1737 
1738   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1739 }
1740 
1741 void
create_video_packetizer_avc(qtmp4_demuxer_c & dmx)1742 qtmp4_reader_c::create_video_packetizer_avc(qtmp4_demuxer_c &dmx) {
1743   m_ti.m_private_data = dmx.priv.size() ? dmx.priv[0] : memory_cptr{};
1744   dmx.ptzr            = add_packetizer(new avc_video_packetizer_c(this, m_ti, dmx.frame_rate ? mtx::to_int(1'000'000'000 / dmx.frame_rate) : 0, dmx.v_width, dmx.v_height));
1745 
1746   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1747 }
1748 
1749 void
create_video_packetizer_mpegh_p2_es(qtmp4_demuxer_c & dmx)1750 qtmp4_reader_c::create_video_packetizer_mpegh_p2_es(qtmp4_demuxer_c &dmx) {
1751   m_ti.m_private_data = dmx.priv.size() && dmx.priv[0]->get_size() ? dmx.priv[0] : memory_cptr{};
1752   dmx.ptzr            = add_packetizer(new hevc_es_video_packetizer_c(this, m_ti));
1753 
1754   ptzr(dmx.ptzr).set_video_pixel_dimensions(dmx.v_width, dmx.v_height);
1755 
1756   if (boost::multiprecision::numerator(dmx.frame_rate)) {
1757     auto duration = mtx::to_int(mtx_mp_rational_t{boost::multiprecision::denominator(dmx.frame_rate), boost::multiprecision::numerator(dmx.frame_rate)} * 1'000'000'000ll);
1758     ptzr(dmx.ptzr).set_track_default_duration(duration);
1759   }
1760 
1761   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1762 }
1763 
1764 void
create_video_packetizer_mpegh_p2(qtmp4_demuxer_c & dmx)1765 qtmp4_reader_c::create_video_packetizer_mpegh_p2(qtmp4_demuxer_c &dmx) {
1766   m_ti.m_private_data = dmx.priv.size() && dmx.priv[0]->get_size() ? dmx.priv[0] : memory_cptr{};
1767   auto packetizer     = new hevc_video_packetizer_c(this, m_ti, 0, dmx.v_width, dmx.v_height);
1768   dmx.ptzr            = add_packetizer(packetizer);
1769 
1770   if (boost::multiprecision::numerator(dmx.frame_rate)) {
1771     auto duration = mtx::to_int(mtx_mp_rational_t{boost::multiprecision::denominator(dmx.frame_rate), boost::multiprecision::numerator(dmx.frame_rate)} * 1'000'000'000ll);
1772     packetizer->set_track_default_duration(duration);
1773   }
1774 
1775   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1776 }
1777 
1778 void
create_video_packetizer_prores(qtmp4_demuxer_c & dmx)1779 qtmp4_reader_c::create_video_packetizer_prores(qtmp4_demuxer_c &dmx) {
1780   m_ti.m_private_data = memory_c::clone(dmx.fourcc.str());
1781   dmx.ptzr            = add_packetizer(new prores_video_packetizer_c(this, m_ti, mtx::to_int(1'000'000'000.0 / dmx.frame_rate), dmx.v_width, dmx.v_height));
1782 
1783   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1784 }
1785 
1786 void
create_video_packetizer_standard(qtmp4_demuxer_c & dmx)1787 qtmp4_reader_c::create_video_packetizer_standard(qtmp4_demuxer_c &dmx) {
1788   m_ti.m_private_data = dmx.stsd;
1789   dmx.ptzr            = add_packetizer(new quicktime_video_packetizer_c(this, m_ti, dmx.v_width, dmx.v_height));
1790 
1791   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1792 }
1793 
1794 void
create_video_packetizer_vpx(qtmp4_demuxer_c & dmx)1795 qtmp4_reader_c::create_video_packetizer_vpx(qtmp4_demuxer_c &dmx) {
1796   m_ti.m_private_data = dmx.priv.size() && dmx.priv[0]->get_size() ? dmx.priv[0] : memory_cptr{};
1797   dmx.ptzr            = add_packetizer(new vpx_video_packetizer_c{this, m_ti, dmx.codec.get_type()});
1798 
1799   ptzr(dmx.ptzr).set_video_pixel_dimensions(dmx.v_width, dmx.v_height);
1800 
1801   if (boost::multiprecision::numerator(dmx.frame_rate)) {
1802     auto duration = mtx::to_int(mtx_mp_rational_t{boost::multiprecision::denominator(dmx.frame_rate), boost::multiprecision::numerator(dmx.frame_rate)} * 1'000'000'000ll);
1803     ptzr(dmx.ptzr).set_track_default_duration(duration);
1804   }
1805 
1806   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1807 }
1808 
1809 void
create_audio_packetizer_aac(qtmp4_demuxer_c & dmx)1810 qtmp4_reader_c::create_audio_packetizer_aac(qtmp4_demuxer_c &dmx) {
1811   m_ti.m_private_data = dmx.esds.decoder_config;
1812   dmx.ptzr            = add_packetizer(new aac_packetizer_c(this, m_ti, *dmx.a_aac_audio_config, aac_packetizer_c::headerless));
1813 
1814   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1815 }
1816 
1817 void
create_audio_packetizer_mp3(qtmp4_demuxer_c & dmx)1818 qtmp4_reader_c::create_audio_packetizer_mp3(qtmp4_demuxer_c &dmx) {
1819   dmx.ptzr = add_packetizer(new mp3_packetizer_c(this, m_ti, dmx.a_samplerate, dmx.a_channels, true));
1820   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1821 }
1822 
1823 void
create_audio_packetizer_opus(qtmp4_demuxer_c & dmx)1824 qtmp4_reader_c::create_audio_packetizer_opus(qtmp4_demuxer_c &dmx) {
1825   m_ti.m_private_data = dmx.priv[0];
1826   dmx.ptzr            = add_packetizer(new opus_packetizer_c{this, m_ti});
1827   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1828 }
1829 
1830 void
create_audio_packetizer_pcm(qtmp4_demuxer_c & dmx)1831 qtmp4_reader_c::create_audio_packetizer_pcm(qtmp4_demuxer_c &dmx) {
1832   dmx.ptzr = add_packetizer(new pcm_packetizer_c(this, m_ti, static_cast<int32_t>(dmx.a_samplerate), dmx.a_channels, dmx.a_bitdepth, dmx.m_pcm_format));
1833   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1834 }
1835 
1836 void
create_audio_packetizer_vorbis(qtmp4_demuxer_c & dmx)1837 qtmp4_reader_c::create_audio_packetizer_vorbis(qtmp4_demuxer_c &dmx) {
1838   dmx.ptzr = add_packetizer(new vorbis_packetizer_c(this, m_ti, dmx.priv));
1839   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1840 }
1841 
1842 void
create_audio_packetizer_passthrough(qtmp4_demuxer_c & dmx)1843 qtmp4_reader_c::create_audio_packetizer_passthrough(qtmp4_demuxer_c &dmx) {
1844   auto packetizer = new passthrough_packetizer_c(this, m_ti);
1845   dmx.ptzr        = add_packetizer(packetizer);
1846 
1847   packetizer->set_track_type(track_audio);
1848   packetizer->set_codec_id(MKV_A_QUICKTIME);
1849   packetizer->set_codec_private(dmx.stsd);
1850   packetizer->set_audio_sampling_freq(dmx.a_samplerate);
1851   packetizer->set_audio_channels(dmx.a_channels);
1852   packetizer->prevent_lacing();
1853 
1854   show_packetizer_info(dmx.id, *packetizer);
1855 }
1856 
1857 void
create_subtitles_packetizer_vobsub(qtmp4_demuxer_c & dmx)1858 qtmp4_reader_c::create_subtitles_packetizer_vobsub(qtmp4_demuxer_c &dmx) {
1859   std::string palette;
1860   auto format = [](int v) { return fmt::format("{0:02x}", std::min(std::max(v, 0), 255)); };
1861   auto buf    = dmx.esds.decoder_config->get_buffer();
1862   for (auto i = 0; i < 16; ++i) {
1863 		int y = static_cast<int>(buf[(i << 2) + 1]) - 0x10;
1864 		int u = static_cast<int>(buf[(i << 2) + 3]) - 0x80;
1865 		int v = static_cast<int>(buf[(i << 2) + 2]) - 0x80;
1866 		int r = (298 * y           + 409 * v + 128) >> 8;
1867 		int g = (298 * y - 100 * u - 208 * v + 128) >> 8;
1868 		int b = (298 * y + 516 * u           + 128) >> 8;
1869 
1870     if (i)
1871       palette += ", ";
1872 
1873     palette += format(r) + format(g) + format(b);
1874   }
1875 
1876   auto idx_str = mtx::vobsub::create_default_index(dmx.v_width, dmx.v_height, palette);
1877 
1878   mxdebug_if(m_debug_headers, fmt::format("VobSub IDX str:\n{0}", idx_str));
1879 
1880   m_ti.m_private_data = memory_c::clone(idx_str);
1881   dmx.ptzr = add_packetizer(new vobsub_packetizer_c(this, m_ti));
1882   show_packetizer_info(dmx.id, ptzr(dmx.ptzr));
1883 }
1884 
1885 void
create_packetizer(int64_t tid)1886 qtmp4_reader_c::create_packetizer(int64_t tid) {
1887   unsigned int i;
1888   qtmp4_demuxer_cptr dmx_ptr;
1889 
1890   for (i = 0; i < m_demuxers.size(); ++i)
1891     if (m_demuxers[i]->id == tid) {
1892       dmx_ptr = m_demuxers[i];
1893       break;
1894     }
1895 
1896   if (!dmx_ptr)
1897     return;
1898 
1899   auto &dmx = *dmx_ptr;
1900   if (!dmx.ok || !demuxing_requested(dmx.type, dmx.id, dmx.language) || (-1 != dmx.ptzr)) {
1901     return;
1902   }
1903 
1904   m_ti.m_id       = dmx.id;
1905   m_ti.m_language = dmx.language;
1906   m_ti.m_private_data.reset();
1907 
1908   bool packetizer_ok = true;
1909 
1910   if (dmx.is_video()) {
1911     if (dmx.codec.is(codec_c::type_e::V_MPEG12))
1912       create_video_packetizer_mpeg1_2(dmx);
1913 
1914     else if (dmx.codec.is(codec_c::type_e::V_MPEG4_P2))
1915       create_video_packetizer_mpeg4_p2(dmx);
1916 
1917     else if (dmx.codec.is(codec_c::type_e::V_MPEG4_P10))
1918       create_video_packetizer_avc(dmx);
1919 
1920     else if (dmx.codec.is(codec_c::type_e::V_MPEGH_P2) && dmx.m_hevc_is_annex_b)
1921       create_video_packetizer_mpegh_p2_es(dmx);
1922 
1923     else if (dmx.codec.is(codec_c::type_e::V_MPEGH_P2))
1924       create_video_packetizer_mpegh_p2(dmx);
1925 
1926     else if (dmx.codec.is(codec_c::type_e::V_AV1))
1927       create_video_packetizer_av1(dmx);
1928 
1929     else if (dmx.codec.is(codec_c::type_e::V_SVQ1))
1930       create_video_packetizer_svq1(dmx);
1931 
1932     else if (dmx.codec.is(codec_c::type_e::V_PRORES))
1933       create_video_packetizer_prores(dmx);
1934 
1935     else if (dmx.codec.is(codec_c::type_e::V_VP8) || dmx.codec.is(codec_c::type_e::V_VP9))
1936       create_video_packetizer_vpx(dmx);
1937 
1938     else
1939       create_video_packetizer_standard(dmx);
1940 
1941     dmx.set_packetizer_display_dimensions();
1942     dmx.set_packetizer_colour_properties();
1943 
1944   } else if (dmx.is_audio()) {
1945     if (dmx.codec.is(codec_c::type_e::A_AAC))
1946       create_audio_packetizer_aac(dmx);
1947 
1948     else if (dmx.codec.is(codec_c::type_e::A_MP2) || dmx.codec.is(codec_c::type_e::A_MP3))
1949       create_audio_packetizer_mp3(dmx);
1950 
1951     else if (dmx.codec.is(codec_c::type_e::A_PCM))
1952       create_audio_packetizer_pcm(dmx);
1953 
1954     else if (dmx.codec.is(codec_c::type_e::A_AC3))
1955       packetizer_ok = create_audio_packetizer_ac3(dmx);
1956 
1957     else if (dmx.codec.is(codec_c::type_e::A_ALAC))
1958       packetizer_ok = create_audio_packetizer_alac(dmx);
1959 
1960     else if (dmx.codec.is(codec_c::type_e::A_DTS))
1961       packetizer_ok = create_audio_packetizer_dts(dmx);
1962 
1963     else if (dmx.codec.is(codec_c::type_e::A_OPUS))
1964       create_audio_packetizer_opus(dmx);
1965 
1966     else if (dmx.codec.is(codec_c::type_e::A_VORBIS))
1967       create_audio_packetizer_vorbis(dmx);
1968 
1969     else
1970       create_audio_packetizer_passthrough(dmx);
1971 
1972     handle_audio_encoder_delay(dmx);
1973 
1974   } else {
1975     if (dmx.codec.is(codec_c::type_e::S_VOBSUB))
1976       create_subtitles_packetizer_vobsub(dmx);
1977   }
1978 
1979   if (packetizer_ok) {
1980     dmx.set_packetizer_block_addition_mappings();
1981 
1982     if (!m_reader_packetizers[dmx.ptzr]->m_ti.m_enabled_track.has_value())
1983       m_reader_packetizers[dmx.ptzr]->set_track_enabled_flag(dmx.m_enabled);
1984   }
1985 
1986   if (packetizer_ok && (-1 == m_main_dmx))
1987     m_main_dmx = i;
1988 }
1989 
1990 void
create_packetizers()1991 qtmp4_reader_c::create_packetizers() {
1992   unsigned int i;
1993 
1994   m_main_dmx = -1;
1995 
1996   for (i = 0; i < m_demuxers.size(); ++i)
1997     create_packetizer(m_demuxers[i]->id);
1998 }
1999 
2000 int64_t
get_progress()2001 qtmp4_reader_c::get_progress() {
2002   return m_bytes_processed;
2003 }
2004 
2005 int64_t
get_maximum_progress()2006 qtmp4_reader_c::get_maximum_progress() {
2007   return m_bytes_to_process;
2008 }
2009 
2010 void
identify()2011 qtmp4_reader_c::identify() {
2012   unsigned int i;
2013 
2014   id_result_container();
2015 
2016   for (i = 0; i < m_demuxers.size(); ++i) {
2017     auto &dmx = *m_demuxers[i];
2018     auto info = mtx::id::info_c{};
2019 
2020     info.set(mtx::id::number, dmx.container_id);
2021     info.set(mtx::id::enabled_track, dmx.m_enabled);
2022 
2023     if (dmx.codec.is(codec_c::type_e::V_MPEG4_P10))
2024       info.add(mtx::id::packetizer, mtx::id::mpeg4_p10_video);
2025 
2026     else if (dmx.codec.is(codec_c::type_e::V_MPEGH_P2))
2027       info.add(mtx::id::packetizer, dmx.m_hevc_is_annex_b ? mtx::id::mpegh_p2_es_video : mtx::id::mpegh_p2_video);
2028 
2029     info.add(mtx::id::language, dmx.language.get_iso639_alpha_3_code());
2030 
2031     if (dmx.is_video())
2032       info.add(mtx::id::pixel_dimensions, fmt::format("{0}x{1}", dmx.v_width, dmx.v_height));
2033 
2034     else if (dmx.is_audio()) {
2035       info.add(mtx::id::audio_channels,           dmx.a_channels);
2036       info.add(mtx::id::audio_sampling_frequency, static_cast<uint64_t>(dmx.a_samplerate));
2037       info.add(mtx::id::audio_bits_per_sample,    dmx.a_bitdepth);
2038     }
2039 
2040     id_result_track(dmx.id,
2041                     dmx.is_video() ? ID_RESULT_TRACK_VIDEO : dmx.is_audio() ? ID_RESULT_TRACK_AUDIO : dmx.is_subtitles() ? ID_RESULT_TRACK_SUBTITLES : ID_RESULT_TRACK_UNKNOWN,
2042                     dmx.codec.get_name(dmx.fourcc.description()),
2043                     info.get());
2044   }
2045 
2046   for (auto &attachment : g_attachments)
2047     id_result_attachment(attachment->ui_id, attachment->mime_type, attachment->data->get_size(), attachment->name, attachment->description, attachment->id);
2048 
2049   if (m_chapters)
2050     id_result_chapters(mtx::chapters::count_atoms(*m_chapters));
2051 }
2052 
2053 void
add_available_track_ids()2054 qtmp4_reader_c::add_available_track_ids() {
2055   unsigned int i;
2056 
2057   for (i = 0; i < m_demuxers.size(); ++i)
2058     add_available_track_id(m_demuxers[i]->id);
2059 
2060   if (m_chapters)
2061     add_available_track_id(track_info_c::chapter_track_id);
2062 }
2063 
2064 mtx::bcp47::language_c
decode_and_verify_language(uint16_t coded_language)2065 qtmp4_reader_c::decode_and_verify_language(uint16_t coded_language) {
2066   std::string language;
2067 
2068   for (int i = 0; 3 > i; ++i)
2069     language += static_cast<char>(((coded_language >> ((2 - i) * 5)) & 0x1f) + 0x60);
2070 
2071   return mtx::bcp47::language_c::parse(language);
2072 }
2073 
2074 void
recode_chapter_entries(std::vector<qtmp4_chapter_entry_t> & entries)2075 qtmp4_reader_c::recode_chapter_entries(std::vector<qtmp4_chapter_entry_t> &entries) {
2076   if (g_identifying) {
2077     for (auto &entry : entries)
2078       entry.m_name.clear();
2079     return;
2080   }
2081 
2082   std::string charset              = m_ti.m_chapter_charset.empty() ? "UTF-8" : m_ti.m_chapter_charset;
2083   charset_converter_cptr converter = charset_converter_c::init(m_ti.m_chapter_charset);
2084   converter->enable_byte_order_marker_detection(true);
2085 
2086   if (m_debug_chapters) {
2087     mxdebug(fmt::format("Number of chapter entries: {0}\n", entries.size()));
2088     size_t num = 0;
2089     for (auto &entry : entries) {
2090       mxdebug(fmt::format("  Chapter {0}: name length {1}\n", num++, entry.m_name.length()));
2091       debugging_c::hexdump(entry.m_name.c_str(), entry.m_name.length());
2092     }
2093   }
2094 
2095   for (auto &entry : entries)
2096     entry.m_name = converter->utf8(entry.m_name);
2097 
2098   converter->enable_byte_order_marker_detection(false);
2099 }
2100 
2101 void
detect_interleaving()2102 qtmp4_reader_c::detect_interleaving() {
2103   decltype(m_demuxers) demuxers_to_read;
2104 
2105   std::copy_if(m_demuxers.begin(), m_demuxers.end(), std::back_inserter(demuxers_to_read), [this](auto const &dmx) {
2106     return (dmx->ok && (dmx->is_audio() || dmx->is_video()) && this->demuxing_requested(dmx->type, dmx->id, dmx->language) && (dmx->sample_table.size() > 1));
2107   });
2108 
2109   if (demuxers_to_read.size() < 2) {
2110     mxdebug_if(m_debug_interleaving, fmt::format("Interleaving: Not enough tracks to care about interleaving.\n"));
2111     return;
2112   }
2113 
2114   auto cmp = [](const qt_sample_t &s1, const qt_sample_t &s2) -> uint64_t { return s1.pos < s2.pos; };
2115 
2116   std::list<double> gradients;
2117   for (auto &dmx : demuxers_to_read) {
2118     uint64_t min = std::min_element(dmx->sample_table.begin(), dmx->sample_table.end(), cmp)->pos;
2119     uint64_t max = std::max_element(dmx->sample_table.begin(), dmx->sample_table.end(), cmp)->pos;
2120     gradients.push_back(static_cast<double>(max - min) / m_in->get_size());
2121 
2122     mxdebug_if(m_debug_interleaving, fmt::format("Interleaving: Track id {0} min {1} max {2} gradient {3}\n", dmx->id, min, max, gradients.back()));
2123   }
2124 
2125   double badness = *std::max_element(gradients.begin(), gradients.end()) - *std::min_element(gradients.begin(), gradients.end());
2126   mxdebug_if(m_debug_interleaving, fmt::format("Interleaving: Badness: {0} ({1})\n", badness, MAX_INTERLEAVING_BADNESS < badness ? "badly interleaved" : "ok"));
2127 
2128   if (MAX_INTERLEAVING_BADNESS < badness)
2129     m_in->enable_buffering(false);
2130 }
2131 
2132 // ----------------------------------------------------------------------
2133 
2134 void
calculate_frame_rate()2135 qtmp4_demuxer_c::calculate_frame_rate() {
2136   if ((1 == durmap_table.size()) && (0 != durmap_table[0].duration) && ((0 != sample_size) || (0 == frame_offset_table.size()))) {
2137     // Constant frame_rate. Let's set the default duration.
2138     frame_rate = mtx::rational(time_scale, durmap_table[0].duration);
2139     mxdebug_if(m_debug_frame_rate, fmt::format("calculate_frame_rate: case 1: {0}/{1}\n", boost::multiprecision::numerator(frame_rate), boost::multiprecision::denominator(frame_rate)));
2140 
2141     return;
2142   }
2143 
2144   if (('v' == type) && time_scale && global_duration && (sample_table.size() < 2)) {
2145     frame_rate = mtx::frame_timing::determine_frame_rate(static_cast<uint64_t>(global_duration) * 1'000'000'000ull / static_cast<uint64_t>(time_scale));
2146     if (frame_rate)
2147       m_use_frame_rate_for_duration = mtx::to_int(mtx_mp_rational_t{1'000'000'000ll} / frame_rate);
2148 
2149     mxdebug_if(m_debug_frame_rate,
2150                fmt::format("calculate_frame_rate: case 2: video track with time scale {0} & duration {1} result: {2}\n",
2151                            time_scale, global_duration, frame_rate ? mtx::string::format_timestamp(*m_use_frame_rate_for_duration) : "<invalid>"s));
2152 
2153     return;
2154   }
2155 
2156   if (sample_table.size() < 2) {
2157     mxdebug_if(m_debug_frame_rate, fmt::format("calculate_frame_rate: case 3: sample table too small\n"));
2158     return;
2159   }
2160 
2161   auto max_pts = sample_table[0].pts;
2162   auto min_pts = max_pts;
2163 
2164   for (auto const &sample : sample_table) {
2165     max_pts = std::max(max_pts, sample.pts);
2166     min_pts = std::min(min_pts, sample.pts);
2167   }
2168 
2169   auto duration   = to_nsecs(max_pts - min_pts);
2170   auto num_frames = sample_table.size() - 1;
2171   frame_rate      = mtx::frame_timing::determine_frame_rate(duration / num_frames);
2172 
2173   if (frame_rate) {
2174     if ('v' == type)
2175       m_use_frame_rate_for_duration = mtx::to_int(mtx_mp_rational_t{1'000'000'000ll} / frame_rate);
2176 
2177     mxdebug_if(m_debug_frame_rate,
2178                fmt::format("calculate_frame_rate: case 4: duration {0} num_frames {1} frame_duration {2} frame_rate {3}/{4} use_frame_rate_for_duration {5}\n",
2179                            duration, num_frames, duration / num_frames, boost::multiprecision::numerator(frame_rate), boost::multiprecision::denominator(frame_rate), m_use_frame_rate_for_duration ? *m_use_frame_rate_for_duration : -1));
2180 
2181     return;
2182   }
2183 
2184   std::map<int64_t, int> duration_map;
2185 
2186   std::accumulate(sample_table.begin() + 1, sample_table.end(), sample_table[0], [&duration_map](auto const &previous_sample, auto const &current_sample) -> auto {
2187     duration_map[current_sample.pts - previous_sample.pts]++;
2188     return current_sample;
2189   });
2190 
2191   auto most_common = std::accumulate(duration_map.begin(), duration_map.end(), std::pair<int64_t, int>(*duration_map.begin()),
2192                                      [](auto const &winner, std::pair<int64_t, int> const &current) { return current.second > winner.second ? current : winner; });
2193 
2194   if (most_common.first)
2195     frame_rate = mtx::rational(1000000000ll, to_nsecs(most_common.first));
2196 
2197   mxdebug_if(m_debug_frame_rate,
2198              fmt::format("calculate_frame_rate: case 5: duration {0} num_frames {1} frame_duration {2} most_common.num_occurances {3} most_common.duration {4} frame_rate {5}/{6}\n",
2199                          duration, num_frames, duration / num_frames, most_common.second, to_nsecs(most_common.first), boost::multiprecision::numerator(frame_rate), boost::multiprecision::denominator(frame_rate)));
2200 }
2201 
2202 int64_t
to_nsecs(int64_t value,std::optional<int64_t> time_scale_to_use)2203 qtmp4_demuxer_c::to_nsecs(int64_t value,
2204                           std::optional<int64_t> time_scale_to_use) {
2205   auto actual_time_scale = time_scale_to_use ? *time_scale_to_use : time_scale;
2206   if (!actual_time_scale)
2207     return 0;
2208 
2209   auto value_mp  = static_cast<mtx_mp_int_t>(value);
2210   value_mp      *= 1'000'000'000ll;
2211   value_mp      /= actual_time_scale;
2212 
2213   return mtx::to_int(value_mp);
2214 }
2215 
2216 void
calculate_timestamps_constant_sample_size()2217 qtmp4_demuxer_c::calculate_timestamps_constant_sample_size() {
2218   int const num_frame_offsets = frame_offset_table.size();
2219   auto chunk_index            = 0;
2220 
2221   timestamps.reserve(timestamps.size() + chunk_table.size());
2222   durations.reserve(durations.size() + chunk_table.size());
2223   frame_indices.reserve(frame_indices.size() + chunk_table.size());
2224 
2225   for (auto const &chunk : chunk_table) {
2226     auto frame_offset = chunk_index < num_frame_offsets ? frame_offset_table[chunk_index] : 0;
2227 
2228     timestamps.push_back(to_nsecs(static_cast<uint64_t>(chunk.samples) * track_duration + frame_offset));
2229     durations.push_back(to_nsecs(static_cast<uint64_t>(chunk.size)     * track_duration));
2230     frame_indices.push_back(chunk_index);
2231 
2232     ++chunk_index;
2233   }
2234 }
2235 
2236 void
calculate_timestamps_variable_sample_size()2237 qtmp4_demuxer_c::calculate_timestamps_variable_sample_size() {
2238   auto const num_frame_offsets = frame_offset_table.size();
2239   auto const num_samples       = sample_table.size();
2240 
2241   std::vector<int64_t> timestamps_before_offsets;
2242 
2243   timestamps.reserve(num_samples);
2244   timestamps_before_offsets.reserve(num_samples);
2245   durations.reserve(num_samples);
2246   frame_indices.reserve(num_samples);
2247 
2248   for (int frame = 0; static_cast<int>(num_samples) > frame; ++frame) {
2249     auto timestamp = to_nsecs(sample_table[frame].pts);
2250 
2251     frame_indices.push_back(frame);
2252     timestamps_before_offsets.push_back(timestamp);
2253     timestamps.push_back(timestamp + (static_cast<unsigned int>(frame) < num_frame_offsets ? to_nsecs(frame_offset_table[frame]) : 0));
2254   }
2255 
2256   int64_t avg_duration = 0, num_good_frames = 0;
2257 
2258   for (int frame = 0; static_cast<int>(num_samples) > (frame + 1); ++frame) {
2259     int64_t diff = timestamps_before_offsets[frame + 1] - timestamps_before_offsets[frame];
2260 
2261     if (0 >= diff)
2262       durations.push_back(0);
2263     else {
2264       ++num_good_frames;
2265       avg_duration += diff;
2266       durations.push_back(diff);
2267     }
2268   }
2269 
2270   durations.push_back(0);
2271 
2272   if (num_good_frames) {
2273     avg_duration /= num_good_frames;
2274     for (auto &duration : durations)
2275       if (!duration)
2276         duration = avg_duration;
2277   }
2278 }
2279 
2280 void
calculate_timestamps()2281 qtmp4_demuxer_c::calculate_timestamps() {
2282   if (m_timestamps_calculated)
2283     return;
2284 
2285   if (0 != sample_size)
2286     calculate_timestamps_constant_sample_size();
2287   else
2288     calculate_timestamps_variable_sample_size();
2289 
2290   if (m_debug_tables) {
2291     mxdebug(fmt::format("Timestamps for track ID {0}:\n", id));
2292     auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), timestamps.size());
2293 
2294     for (auto idx = 0u; idx < end; ++idx)
2295       mxdebug(fmt::format("  {0}: pts {1}\n", idx, mtx::string::format_timestamp(timestamps[idx])));
2296   }
2297 
2298   build_index();
2299   apply_edit_list();
2300 
2301   m_timestamps_calculated = true;
2302 }
2303 
2304 void
adjust_timestamps(int64_t delta)2305 qtmp4_demuxer_c::adjust_timestamps(int64_t delta) {
2306   for (auto &timestamp : timestamps)
2307     timestamp += delta;
2308 
2309   for (auto &index : m_index)
2310     index.timestamp += delta;
2311 }
2312 
2313 std::optional<int64_t>
min_timestamp() const2314 qtmp4_demuxer_c::min_timestamp()
2315   const {
2316   if (m_index.empty()) {
2317     return {};
2318   }
2319 
2320   return std::accumulate(m_index.begin(), m_index.end(), std::numeric_limits<int64_t>::max(), [](int64_t min, auto const &entry) { return std::min(min, entry.timestamp); });
2321 }
2322 
2323 bool
update_tables()2324 qtmp4_demuxer_c::update_tables() {
2325   if (m_tables_updated)
2326     return true;
2327 
2328   uint64_t last = chunk_table.size();
2329 
2330   if (!last)
2331     return false;
2332 
2333   // process chunkmap:
2334   size_t j, i = chunkmap_table.size();
2335   while (i > 0) {
2336     --i;
2337     for (j = chunkmap_table[i].first_chunk; j < last; ++j) {
2338       auto &chunk = chunk_table[j];
2339       // Don't update chunks that were added by parsing moof atoms
2340       // (DASH). Those have their "size" field already set. Only
2341       // update chunks from the "moov" atom's "stco"/"co64" atoms.
2342       if (chunk.size != 0)
2343         continue;
2344 
2345       chunk.desc = chunkmap_table[i].sample_description_id;
2346       chunk.size = chunkmap_table[i].samples_per_chunk;
2347     }
2348 
2349     last = chunkmap_table[i].first_chunk;
2350 
2351     if (chunk_table.size() <= last)
2352       break;
2353   }
2354 
2355   // calc pts of chunks:
2356   uint64_t s = 0;
2357   for (j = 0; j < chunk_table.size(); ++j) {
2358     chunk_table[j].samples  = s;
2359     s                      += chunk_table[j].size;
2360   }
2361 
2362   // workaround for fixed-size video frames (dv and uncompressed), but
2363   // also for audio with constant sample size
2364   if (sample_table.empty() && (sample_size > 1)) {
2365     for (i = 0; i < s; ++i) {
2366       qt_sample_t sample;
2367 
2368       sample.size = sample_size;
2369       sample_table.push_back(sample);
2370     }
2371 
2372     sample_size = 0;
2373   }
2374 
2375   if (sample_table.empty()) {
2376     // constant sample size
2377     if ((1 == durmap_table.size()) || ((2 == durmap_table.size()) && (1 == durmap_table[1].number)))
2378       track_duration = durmap_table[0].duration;
2379     else
2380       mxerror(Y("Quicktime/MP4 reader: Constant sample size & variable duration not yet supported. Contact the author if you have such a sample file.\n"));
2381 
2382     m_tables_updated = true;
2383 
2384     return true;
2385   }
2386 
2387   // calc pts:
2388   auto num_samples = sample_table.size();
2389   s                = 0;
2390   uint64_t pts     = 0;
2391 
2392   for (j = 0; (j < durmap_table.size()) && (s < num_samples); ++j) {
2393     for (i = 0; (i < durmap_table[j].number) && (s < num_samples); ++i) {
2394       sample_table[s].pts  = pts;
2395       pts                 += durmap_table[j].duration;
2396       ++s;
2397     }
2398   }
2399 
2400   if (s < num_samples) {
2401     mxdebug_if(m_debug_headers, fmt::format("Track {0}: fewer timestamps assigned than entries in the sample table: {1} < {2}; dropping the excessive items\n", id, s, num_samples));
2402     sample_table.resize(s);
2403     num_samples = s;
2404   }
2405 
2406   // calc sample offsets
2407   s = 0;
2408   for (j = 0; (j < chunk_table.size()) && (s < num_samples); ++j) {
2409     uint64_t chunk_pos = chunk_table[j].pos;
2410 
2411     for (i = 0; (i < chunk_table[j].size) && (s < num_samples); ++i) {
2412       sample_table[s].pos  = chunk_pos;
2413       chunk_pos           += sample_table[s].size;
2414       ++s;
2415     }
2416   }
2417 
2418   // calc pts/dts offsets
2419   for (j = 0; j < raw_frame_offset_table.size(); ++j) {
2420     size_t k;
2421 
2422     for (k = 0; k < raw_frame_offset_table[j].count; ++k)
2423       frame_offset_table.push_back(raw_frame_offset_table[j].offset);
2424   }
2425 
2426   m_tables_updated = true;
2427 
2428   if (!m_debug_tables)
2429     return true;
2430 
2431   mxdebug(fmt::format(" Frame offset table for track ID {0}: {1} entries\n",    id, frame_offset_table.size()));
2432   mxdebug(fmt::format(" Sample table contents for track ID {0}: {1} entries\n", id, sample_table.size()));
2433 
2434   auto end = std::min<std::size_t>(!m_debug_tables_full ? 20 : std::numeric_limits<std::size_t>::max(), sample_table.size());
2435 
2436   for (auto idx = 0u; idx < end; ++idx)
2437     mxdebug(fmt::format("   {0}: pts {1} size {2} pos {3}\n", idx, sample_table[idx].pts, sample_table[idx].size, sample_table[idx].pos));
2438 
2439   return true;
2440 }
2441 
2442 void
apply_edit_list()2443 qtmp4_demuxer_c::apply_edit_list() {
2444   if (editlist_table.empty())
2445     return;
2446 
2447   mxdebug_if(m_debug_editlists,
2448              fmt::format("Applying edit list for track {0}: {1} entries; track time scale {2}, global time scale {3}\n",
2449                          id, editlist_table.size(), time_scale, m_reader.m_time_scale));
2450 
2451   std::vector<qt_index_t> edited_index;
2452 
2453   auto const num_edits         = editlist_table.size();
2454   auto const num_index_entries = m_index.size();
2455   auto const index_begin       = m_index.begin();
2456   auto const index_end         = m_index.end();
2457   auto const global_time_scale = m_reader.m_time_scale;
2458   auto timeline_cts            = int64_t{};
2459   auto entry_index             = 0u;
2460 
2461   for (auto &edit : editlist_table) {
2462     auto info = fmt::format("{0} [segment_duration {1} media_time {2} media_rate {3}/{4}]", entry_index, edit.segment_duration, edit.media_time, edit.media_rate_integer, edit.media_rate_fraction);
2463     ++entry_index;
2464 
2465     if ((edit.media_rate_integer == 0) && (edit.media_rate_fraction == 0)) {
2466       mxdebug_if(m_debug_editlists, fmt::format("  {0}: dwell at timeline CTS {1}; such entries are not supported yet\n", info, mtx::string::format_timestamp(timeline_cts)));
2467       continue;
2468     }
2469 
2470     if ((edit.media_rate_integer != 1) || (edit.media_rate_fraction != 0)) {
2471       mxdebug_if(m_debug_editlists, fmt::format("  {0}: slow down/speed up at timeline CTS {1}; such entries are not supported yet\n", info, mtx::string::format_timestamp(timeline_cts)));
2472       continue;
2473     }
2474 
2475     if ((edit.media_time < 0) && (edit.media_time != -1)) {
2476       mxdebug_if(m_debug_editlists, fmt::format("  {0}: invalid media_time (< 0 and != -1)\n", info));
2477       continue;
2478     }
2479 
2480     if (edit.media_time == -1) {
2481       timeline_cts = to_nsecs(edit.segment_duration, global_time_scale);
2482       mxdebug_if(m_debug_editlists, fmt::format("  {0}: empty edit (media_time == -1); track start offset {1}\n", info, mtx::string::format_timestamp(timeline_cts)));
2483       continue;
2484     }
2485 
2486     if ((edit.segment_duration == 0) && (edit.media_time >= 0) && (entry_index < num_edits)) {
2487       mxdebug_if(m_debug_editlists, fmt::format("  {0}: empty edit (segment_duration == 0, media_time >= 0) and more entries present; ignoring\n", info));
2488       continue;
2489     }
2490 
2491     if (num_edits == 1) {
2492       timeline_cts          = to_nsecs(edit.media_time) * -1;
2493       edit.media_time       = 0;
2494       edit.segment_duration = 0;
2495       mxdebug_if(m_debug_editlists, fmt::format("  {0}: single edit with positive media_time; track start offset {1}; change to non-edit to copy the rest\n", info, mtx::string::format_timestamp(timeline_cts)));
2496 
2497     } else if (   (num_edits   == 2)
2498                && (entry_index == 2)
2499                && (edit        == editlist_table.front())
2500                && m_reader.m_duration
2501                && ((timeline_cts - static_cast<int64_t>(*m_reader.m_duration)) >= timestamp_c::s(-60).to_ns())) {
2502       mxdebug_if(m_debug_editlists,
2503                  fmt::format("  {0}: edit list with two identical entries, each spanning the whole file; ignoring the second one; total duration: {1} timeline CTS {2}\n",
2504                              info, mtx::string::format_timestamp(timeline_cts), mtx::string::format_timestamp(*m_reader.m_duration)));
2505       continue;
2506     }
2507 
2508     // Determine first frame index whose CTS (composition timestamp)
2509     // is bigger than or equal to the current edit's start CTS.
2510     auto const edit_duration  = to_nsecs(edit.segment_duration, global_time_scale);
2511     auto const edit_start_cts = to_nsecs(edit.media_time);
2512     auto const edit_end_cts   = edit_start_cts + edit_duration;
2513     auto itr                  = std::find_if(m_index.begin(), m_index.end(), [edit_start_cts](auto const &entry) { return (entry.timestamp + entry.duration - (entry.duration > 0 ? 1 : 0)) >= edit_start_cts; });
2514     auto const frame_idx      = static_cast<uint64_t>(std::distance(index_begin, itr));
2515 
2516     mxdebug_if(m_debug_editlists,
2517                fmt::format("  {0}: normal entry; first frame {1} edit CTS {2}–{3} at timeline CTS {4}\n",
2518                            info, frame_idx >= num_index_entries ? -1 : frame_idx, mtx::string::format_timestamp(edit_start_cts), mtx::string::format_timestamp(edit_end_cts), mtx::string::format_timestamp(timeline_cts)));
2519 
2520     // Find active key frame.
2521     while ((itr != index_end) && (itr > index_begin) && !itr->is_keyframe) {
2522       --itr;
2523     }
2524 
2525     while ((itr < index_end) && (!edit_duration || (itr->timestamp < edit_end_cts))) {
2526       itr->timestamp = timeline_cts + itr->timestamp - edit_start_cts;
2527       edited_index.emplace_back(*itr);
2528 
2529       ++itr;
2530     }
2531 
2532     timeline_cts += edit_end_cts - edit_start_cts;
2533   }
2534 
2535   if (!edited_index.empty())
2536     m_index = std::move(edited_index);
2537 
2538   if (m_debug_editlists)
2539     dump_index_entries("Index after edit list");
2540 }
2541 
2542 void
dump_index_entries(std::string const & message) const2543 qtmp4_demuxer_c::dump_index_entries(std::string const &message)
2544   const {
2545   mxdebug(fmt::format("{0} for track ID {1}: {2} entries\n", message, id, m_index.size()));
2546 
2547   auto end = std::min<int>(!m_debug_indexes_full ? 10 : std::numeric_limits<int>::max(), m_index.size());
2548 
2549   for (int idx = 0; idx < end; ++idx) {
2550     auto const &entry = m_index[idx];
2551     mxdebug(fmt::format("  {0}: timestamp {1} duration {2} key? {3} file_pos {4} size {5}\n", idx, mtx::string::format_timestamp(entry.timestamp), mtx::string::format_timestamp(entry.duration), entry.is_keyframe, entry.file_pos, entry.size));
2552   }
2553 
2554   if (m_debug_indexes_full)
2555     return;
2556 
2557   auto start = m_index.size() - std::min<int>(m_index.size(), 10);
2558   end        = m_index.size();
2559 
2560   for (int idx = start; idx < end; ++idx) {
2561     auto const &entry = m_index[idx];
2562     mxdebug(fmt::format("  {0}: timestamp {1} duration {2} key? {3} file_pos {4} size {5}\n", idx, mtx::string::format_timestamp(entry.timestamp), mtx::string::format_timestamp(entry.duration), entry.is_keyframe, entry.file_pos, entry.size));
2563   }
2564 }
2565 
2566 void
build_index()2567 qtmp4_demuxer_c::build_index() {
2568   if (sample_size != 0)
2569     build_index_constant_sample_size_mode();
2570   else
2571     build_index_chunk_mode();
2572 
2573   mark_key_frames_from_key_frame_table();
2574   mark_open_gop_random_access_points_as_key_frames();
2575 
2576   if (m_debug_indexes)
2577     dump_index_entries("Index before edit list");
2578 }
2579 
2580 void
build_index_constant_sample_size_mode()2581 qtmp4_demuxer_c::build_index_constant_sample_size_mode() {
2582   auto is_audio              = 'a' == type;
2583   auto sound_stsd_atom       = reinterpret_cast<sound_v1_stsd_atom_t *>(is_audio && stsd ? stsd->get_buffer() : nullptr);
2584   auto v0_sample_size        = sound_stsd_atom       ? get_uint16_be(&sound_stsd_atom->v0.sample_size)        : 0;
2585   auto v0_audio_version      = sound_stsd_atom       ? get_uint16_be(&sound_stsd_atom->v0.version)            : 0;
2586   auto v1_bytes_per_frame    = 1 == v0_audio_version ? get_uint32_be(&sound_stsd_atom->v1.bytes_per_frame)    : 0;
2587   auto v1_samples_per_packet = 1 == v0_audio_version ? get_uint32_be(&sound_stsd_atom->v1.samples_per_packet) : 0;
2588 
2589   m_index.reserve(m_index.size() + chunk_table.size());
2590 
2591   size_t frame_idx;
2592   for (frame_idx = 0; frame_idx < chunk_table.size(); ++frame_idx) {
2593     uint64_t frame_size;
2594 
2595     if (1 != sample_size) {
2596       frame_size = chunk_table[frame_idx].size * sample_size;
2597 
2598     } else {
2599       frame_size = chunk_table[frame_idx].size;
2600 
2601       if (is_audio) {
2602         if ((0 != v1_bytes_per_frame) && (0 != v1_samples_per_packet)) {
2603           frame_size *= v1_bytes_per_frame;
2604           frame_size /= v1_samples_per_packet;
2605         } else
2606           frame_size  = frame_size * a_channels * v0_sample_size / 8;
2607       }
2608     }
2609 
2610     m_index.emplace_back(chunk_table[frame_idx].pos, frame_size, timestamps[frame_idx], durations[frame_idx], false);
2611   }
2612 }
2613 
2614 void
build_index_chunk_mode()2615 qtmp4_demuxer_c::build_index_chunk_mode() {
2616   m_index.reserve(m_index.size() + frame_indices.size());
2617 
2618   for (int frame_idx = 0, num_frames = frame_indices.size(); frame_idx < num_frames; ++frame_idx) {
2619     auto act_frame_idx = frame_indices[frame_idx];
2620     auto &sample       = sample_table[act_frame_idx];
2621 
2622     m_index.emplace_back(sample.pos, sample.size, timestamps[frame_idx], durations[frame_idx], false);
2623   }
2624 }
2625 
2626 void
mark_key_frames_from_key_frame_table()2627 qtmp4_demuxer_c::mark_key_frames_from_key_frame_table() {
2628   if (keyframe_table.empty()) {
2629     for (auto &index : m_index)
2630       index.is_keyframe = true;
2631     return;
2632   }
2633 
2634   auto num_index_entries = m_index.size();
2635 
2636   for (auto const &keyframe_number : keyframe_table)
2637     if ((keyframe_number > 0) && (keyframe_number <= num_index_entries))
2638       m_index[keyframe_number - 1].is_keyframe = true;
2639 }
2640 
2641 void
mark_open_gop_random_access_points_as_key_frames()2642 qtmp4_demuxer_c::mark_open_gop_random_access_points_as_key_frames() {
2643   // Mark samples indicated by the 'rap ' sample group to be key
2644   // frames, too.
2645   auto table_itr = sample_to_group_tables.find(fourcc_c{"rap "}.value());
2646   if (table_itr == sample_to_group_tables.end())
2647     return;
2648 
2649   int const num_index_entries         = m_index.size();
2650   auto const num_random_access_points = random_access_point_table.size();
2651   auto current_sample                 = 0;
2652 
2653   for (auto const &s2g : table_itr->second) {
2654     if (s2g.group_description_index && ((s2g.group_description_index - 1) < num_random_access_points)) {
2655       for (auto end = std::min<int>(current_sample + s2g.sample_count, num_index_entries); current_sample < end; ++current_sample)
2656         m_index[current_sample].is_keyframe = true;
2657 
2658     } else
2659       current_sample += s2g.sample_count;
2660 
2661     if (current_sample >= num_index_entries)
2662       return;
2663   }
2664 }
2665 
2666 memory_cptr
read_first_bytes(int num_bytes)2667 qtmp4_demuxer_c::read_first_bytes(int num_bytes) {
2668   if (!update_tables())
2669     return memory_cptr{};
2670 
2671   calculate_timestamps();
2672 
2673   auto buf       = memory_c::alloc(num_bytes);
2674   size_t buf_pos = 0;
2675   size_t idx_pos = 0;
2676 
2677   while ((0 < num_bytes) && (idx_pos < m_index.size())) {
2678     qt_index_t &index          = m_index[idx_pos];
2679     uint64_t num_bytes_to_read = std::min<int64_t>(num_bytes, index.size);
2680 
2681     m_reader.m_in->setFilePointer(index.file_pos);
2682     if (m_reader.m_in->read(buf->get_buffer() + buf_pos, num_bytes_to_read) < num_bytes_to_read)
2683       return memory_cptr{};
2684 
2685     num_bytes -= num_bytes_to_read;
2686     buf_pos   += num_bytes_to_read;
2687     ++idx_pos;
2688   }
2689 
2690   return 0 == num_bytes ? buf : memory_cptr{};
2691 }
2692 
2693 bool
is_audio() const2694 qtmp4_demuxer_c::is_audio()
2695   const {
2696   return 'a' == type;
2697 }
2698 
2699 bool
is_video() const2700 qtmp4_demuxer_c::is_video()
2701   const {
2702   return 'v' == type;
2703 }
2704 
2705 bool
is_subtitles() const2706 qtmp4_demuxer_c::is_subtitles()
2707   const {
2708   return 's' == type;
2709 }
2710 
2711 bool
is_chapters() const2712 qtmp4_demuxer_c::is_chapters()
2713   const {
2714   return 'C' == type;
2715 }
2716 
2717 bool
is_unknown() const2718 qtmp4_demuxer_c::is_unknown()
2719   const {
2720   return !is_audio() && !is_video() && !is_subtitles() && !is_chapters();
2721 }
2722 
2723 void
set_packetizer_display_dimensions()2724 qtmp4_demuxer_c::set_packetizer_display_dimensions() {
2725   // Set the display width/height from the track header atom ('tkhd')
2726   // if they're set and differ from the pixel dimensions. They're
2727   // stored as a 32bit fix-point number. First round them to the
2728   // nearest integer.
2729   auto display_width  = (v_display_width_flt  + 0x8000) >> 16;
2730   auto display_height = (v_display_height_flt + 0x8000) >> 16;
2731 
2732   if (   (display_width  != 0)
2733       && (display_height != 0)
2734       && (   (v_width  != display_width)
2735           || (v_height != display_height)))
2736     m_reader.m_reader_packetizers[ptzr]->set_video_display_dimensions(display_width, display_height, generic_packetizer_c::ddu_pixels, OPTION_SOURCE_CONTAINER);
2737 }
2738 
2739 void
set_packetizer_colour_properties()2740 qtmp4_demuxer_c::set_packetizer_colour_properties() {
2741   if (v_colour_primaries != 2) {
2742     m_reader.m_reader_packetizers[ptzr]->set_video_colour_primaries(v_colour_primaries, OPTION_SOURCE_CONTAINER);
2743   }
2744   if (v_colour_transfer_characteristics != 2) {
2745     m_reader.m_reader_packetizers[ptzr]->set_video_colour_transfer_character(v_colour_transfer_characteristics, OPTION_SOURCE_CONTAINER);
2746   }
2747   if (v_colour_matrix_coefficients != 2) {
2748     m_reader.m_reader_packetizers[ptzr]->set_video_colour_matrix(v_colour_matrix_coefficients, OPTION_SOURCE_CONTAINER);
2749   }
2750 }
2751 
2752 void
set_packetizer_block_addition_mappings()2753 qtmp4_demuxer_c::set_packetizer_block_addition_mappings() {
2754   m_reader.m_reader_packetizers[ptzr]->set_block_addition_mappings(m_block_addition_mappings);
2755 }
2756 
2757 void
handle_stsd_atom(uint64_t atom_size,int level)2758 qtmp4_demuxer_c::handle_stsd_atom(uint64_t atom_size,
2759                                   int level) {
2760   if (is_audio()) {
2761     handle_audio_stsd_atom(atom_size, level);
2762     if ((0 < stsd_non_priv_struct_size) && (stsd_non_priv_struct_size < atom_size))
2763       parse_audio_header_priv_atoms(atom_size, level);
2764 
2765   } else if (is_video()) {
2766     handle_video_stsd_atom(atom_size, level);
2767     if ((0 < stsd_non_priv_struct_size) && (stsd_non_priv_struct_size < atom_size))
2768       parse_video_header_priv_atoms(atom_size, level);
2769 
2770   } else if (is_subtitles()) {
2771     handle_subtitles_stsd_atom(atom_size, level);
2772     if ((0 < stsd_non_priv_struct_size) && (stsd_non_priv_struct_size < atom_size))
2773       parse_subtitles_header_priv_atoms(atom_size, level);
2774   }
2775 
2776 }
2777 
2778 void
handle_audio_stsd_atom(uint64_t atom_size,int level)2779 qtmp4_demuxer_c::handle_audio_stsd_atom(uint64_t atom_size,
2780                                         int level) {
2781   auto stsd_raw = stsd->get_buffer();
2782   auto size     = stsd->get_size();
2783 
2784   if (sizeof(sound_v0_stsd_atom_t) > atom_size)
2785     mxerror(fmt::format(Y("Quicktime/MP4 reader: Could not read the sound description atom for track ID {0}.\n"), id));
2786 
2787   sound_v1_stsd_atom_t sv1_stsd;
2788   sound_v2_stsd_atom_t sv2_stsd;
2789   memcpy(&sv1_stsd, stsd_raw, sizeof(sound_v0_stsd_atom_t));
2790   memcpy(&sv2_stsd, stsd_raw, sizeof(sound_v0_stsd_atom_t));
2791 
2792   if (fourcc)
2793     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Track ID {0} has more than one FourCC. Only using the first one ({1}) and not this one ({2}).\n"),
2794                        id, fourcc.description(), fourcc_c{sv1_stsd.v0.base.fourcc}.description()));
2795   else
2796     fourcc = fourcc_c{sv1_stsd.v0.base.fourcc};
2797 
2798   auto version = get_uint16_be(&sv1_stsd.v0.version);
2799   a_channels   = get_uint16_be(&sv1_stsd.v0.channels);
2800   a_bitdepth   = get_uint16_be(&sv1_stsd.v0.sample_size);
2801   auto tmp     = get_uint32_be(&sv1_stsd.v0.sample_rate);
2802   a_samplerate = ((tmp & 0xffff0000) >> 16) + (tmp & 0x0000ffff) / 65536.0;
2803 
2804   mxdebug_if(m_debug_headers,
2805              fmt::format("{0}[v0] FourCC: {1} channels: {2} sample size: {3} compression id: {4} sample rate: {5} version: {6}",
2806                          space(level * 2 + 1),
2807                          fourcc_c{reinterpret_cast<const unsigned char *>(sv1_stsd.v0.base.fourcc)}.description(),
2808                          a_channels,
2809                          a_bitdepth,
2810                          get_uint16_be(&sv1_stsd.v0.compression_id),
2811                          a_samplerate,
2812                          get_uint16_be(&sv1_stsd.v0.version)));
2813 
2814   if (0 == version)
2815     stsd_non_priv_struct_size = sizeof(sound_v0_stsd_atom_t);
2816 
2817   else if (1 == version) {
2818     if (sizeof(sound_v1_stsd_atom_t) > size)
2819       mxerror(fmt::format(Y("Quicktime/MP4 reader: Could not read the extended sound description atom for track ID {0}.\n"), id));
2820 
2821     stsd_non_priv_struct_size = sizeof(sound_v1_stsd_atom_t);
2822     memcpy(&sv1_stsd, stsd_raw, stsd_non_priv_struct_size);
2823 
2824     if (m_debug_headers)
2825       mxinfo(fmt::format(" [v1] samples per packet: {0} bytes per packet: {1} bytes per frame: {2} bytes_per_sample: {3}",
2826                          get_uint32_be(&sv1_stsd.v1.samples_per_packet),
2827                          get_uint32_be(&sv1_stsd.v1.bytes_per_packet),
2828                          get_uint32_be(&sv1_stsd.v1.bytes_per_frame),
2829                          get_uint32_be(&sv1_stsd.v1.bytes_per_sample)));
2830 
2831   } else if (2 == version) {
2832     if (sizeof(sound_v2_stsd_atom_t) > size)
2833       mxerror(fmt::format(Y("Quicktime/MP4 reader: Could not read the extended sound description atom for track ID {0}.\n"), id));
2834 
2835     stsd_non_priv_struct_size = sizeof(sound_v2_stsd_atom_t);
2836     memcpy(&sv2_stsd, stsd_raw, stsd_non_priv_struct_size);
2837 
2838     a_channels   = get_uint32_be(&sv2_stsd.v2.channels);
2839     a_bitdepth   = get_uint32_be(&sv2_stsd.v2.bits_per_channel);
2840     a_samplerate = mtx::math::int_to_double(get_uint64_be(&sv2_stsd.v2.sample_rate));
2841 
2842 
2843     if (m_debug_headers)
2844       mxinfo(fmt::format(" [v2] struct size: {0} sample rate: {1} channels: {2} const1: 0x{3:08x} bits per channel: {4} flags: {5} bytes per frame: {6} samples per frame: {7}",
2845                          get_uint32_be(&sv2_stsd.v2.v2_struct_size),
2846                          a_samplerate,
2847                          a_channels,
2848                          get_uint32_be(&sv2_stsd.v2.const1),
2849                          a_bitdepth,
2850                          get_uint32_be(&sv2_stsd.v2.flags),
2851                          get_uint32_be(&sv2_stsd.v2.bytes_per_frame),
2852                          get_uint32_be(&sv2_stsd.v2.samples_per_frame)));
2853   }
2854 
2855   if (m_debug_headers) {
2856     mxinfo(fmt::format(" non-priv struct size: {0} priv struct size: {1}", stsd_non_priv_struct_size, size - std::min<unsigned int>(size, stsd_non_priv_struct_size)));
2857     mxinfo("\n");
2858   }
2859 }
2860 
2861 void
handle_video_stsd_atom(uint64_t atom_size,int level)2862 qtmp4_demuxer_c::handle_video_stsd_atom(uint64_t atom_size,
2863                                         int level) {
2864   stsd_non_priv_struct_size = sizeof(video_stsd_atom_t);
2865 
2866   if (sizeof(video_stsd_atom_t) > atom_size)
2867     mxerror(fmt::format(Y("Quicktime/MP4 reader: Could not read the video description atom for track ID {0}.\n"), id));
2868 
2869   video_stsd_atom_t v_stsd;
2870   auto stsd_raw = stsd->get_buffer();
2871   memcpy(&v_stsd, stsd_raw, sizeof(video_stsd_atom_t));
2872 
2873   if (fourcc)
2874     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Track ID {0} has more than one FourCC. Only using the first one ({1}) and not this one ({2}).\n"),
2875                        id, fourcc.description(), fourcc_c{v_stsd.base.fourcc}.description()));
2876 
2877   else
2878     fourcc = fourcc_c{v_stsd.base.fourcc};
2879 
2880   codec = codec_c::look_up(fourcc);
2881 
2882   mxdebug_if(m_debug_headers,
2883              fmt::format("{0}FourCC: {1}, width: {2}, height: {3}, depth: {4}\n",
2884                          space(level * 2 + 1),
2885                          fourcc_c{v_stsd.base.fourcc}.description(),
2886                          get_uint16_be(&v_stsd.width),
2887                          get_uint16_be(&v_stsd.height),
2888                          get_uint16_be(&v_stsd.depth)));
2889 
2890   v_width    = get_uint16_be(&v_stsd.width);
2891   v_height   = get_uint16_be(&v_stsd.height);
2892   v_bitdepth = get_uint16_be(&v_stsd.depth);
2893 }
2894 
2895 void
handle_colr_atom(memory_cptr const & atom_content,int level)2896 qtmp4_demuxer_c::handle_colr_atom(memory_cptr const &atom_content,
2897                                   int level) {
2898   if (sizeof(colr_atom_t) > atom_content->get_size())
2899     return;
2900 
2901   auto &colr_atom = *reinterpret_cast<colr_atom_t *>(atom_content->get_buffer());
2902   fourcc_c colour_type{reinterpret_cast<unsigned char const *>(&colr_atom) + offsetof(colr_atom_t, colour_type)};
2903 
2904   if (!mtx::included_in(colour_type, "nclc", "nclx"))
2905     return;
2906 
2907   v_colour_primaries                = get_uint16_be(&colr_atom.colour_primaries);
2908   v_colour_transfer_characteristics = get_uint16_be(&colr_atom.transfer_characteristics);
2909   v_colour_matrix_coefficients      = get_uint16_be(&colr_atom.matrix_coefficients);
2910 
2911   mxdebug_if(m_debug_headers,
2912              fmt::format("{0}colour primaries: {1}, transfer characteristics: {2}, matrix coefficients: {3}\n",
2913                          space(level * 2 + 1), v_colour_primaries, v_colour_transfer_characteristics, v_colour_matrix_coefficients));
2914 }
2915 
2916 void
handle_subtitles_stsd_atom(uint64_t atom_size,int level)2917 qtmp4_demuxer_c::handle_subtitles_stsd_atom(uint64_t atom_size,
2918                                             int level) {
2919   auto stsd_raw = stsd->get_buffer();
2920   auto size     = stsd->get_size();
2921 
2922   if (sizeof(base_stsd_atom_t) > atom_size)
2923     return;
2924 
2925   base_stsd_atom_t base_stsd;
2926   memcpy(&base_stsd, stsd_raw, sizeof(base_stsd_atom_t));
2927 
2928  fourcc                    = fourcc_c{base_stsd.fourcc};
2929  stsd_non_priv_struct_size = sizeof(base_stsd_atom_t);
2930 
2931   if (m_debug_headers) {
2932     mxdebug(fmt::format("{0}FourCC: {1}\n", space(level * 2 + 1), fourcc.description()));
2933     debugging_c::hexdump(stsd_raw, size);
2934   }
2935 }
2936 
2937 void
parse_aac_esds_decoder_config()2938 qtmp4_demuxer_c::parse_aac_esds_decoder_config() {
2939   if (!esds.decoder_config || (2 > esds.decoder_config->get_size())) {
2940     a_aac_audio_config                     = mtx::aac::audio_config_t{};
2941     a_aac_audio_config->profile            = mtx::aac::PROFILE_MAIN;
2942     a_aac_audio_config->sample_rate        = a_samplerate;
2943     a_aac_audio_config->output_sample_rate = a_samplerate;
2944     a_aac_audio_config->channels           = a_channels;
2945     esds.decoder_config                    = mtx::aac::create_audio_specific_config(*a_aac_audio_config);
2946 
2947     mxdebug_if(m_debug_headers,
2948                fmt::format(" AAC: no decoder config in ESDS; generating default with profile: {0}, sample_rate: {1}, channels: {2}\n",
2949                            a_aac_audio_config->profile, a_aac_audio_config->sample_rate, a_aac_audio_config->channels));
2950 
2951     return;
2952   }
2953 
2954   a_aac_audio_config = mtx::aac::parse_audio_specific_config(esds.decoder_config->get_buffer(), esds.decoder_config->get_size());
2955   if (!a_aac_audio_config) {
2956     mxwarn(fmt::format(Y("Track {0}: The AAC information could not be parsed.\n"), id));
2957     return;
2958   }
2959 
2960   mxdebug_if(m_debug_headers,
2961              fmt::format(" AAC audio-specific config: profile: {0}, sample_rate: {1}, channels: {2}, output_sample_rate: {3}, sbr: {4}\n",
2962                          a_aac_audio_config->profile, a_aac_audio_config->sample_rate, a_aac_audio_config->channels, a_aac_audio_config->output_sample_rate, a_aac_audio_config->sbr));
2963 
2964   if ((a_channels != 8) || (a_aac_audio_config->channels != 7))
2965     a_channels = a_aac_audio_config->channels;
2966   a_samplerate = a_aac_audio_config->sample_rate;
2967 }
2968 
2969 void
parse_vorbis_esds_decoder_config()2970 qtmp4_demuxer_c::parse_vorbis_esds_decoder_config() {
2971   if (!esds.decoder_config || !esds.decoder_config->get_size())
2972     return;
2973 
2974   try {
2975     codec = codec_c::look_up(codec_c::type_e::A_VORBIS);
2976     priv  = unlace_memory_xiph(esds.decoder_config);
2977 
2978   } catch (mtx::mem::lacing_x &) {
2979   }
2980 }
2981 
2982 void
parse_esds_audio_header_priv_atom(mm_io_c & io,int level)2983 qtmp4_demuxer_c::parse_esds_audio_header_priv_atom(mm_io_c &io,
2984                                                    int level) {
2985   esds_parsed = parse_esds_atom(io, level);
2986 
2987   if (!esds_parsed)
2988     return;
2989 
2990   if (codec_c::look_up_object_type_id(esds.object_type_id).is(codec_c::type_e::A_AAC))
2991     parse_aac_esds_decoder_config();
2992 
2993   else if (codec_c::look_up_object_type_id(esds.object_type_id).is(codec_c::type_e::A_VORBIS))
2994     parse_vorbis_esds_decoder_config();
2995 }
2996 
2997 void
parse_dops_audio_header_priv_atom(mm_io_c & io,int level)2998 qtmp4_demuxer_c::parse_dops_audio_header_priv_atom(mm_io_c &io,
2999                                                    int level) {
3000   mxdebug_if(m_debug_headers, fmt::format("{0}Opus box size: {1}\n", space((level + 1) * 2 + 1), io.get_size()));
3001 
3002   if (io.get_size() < 11)
3003     return;
3004 
3005   auto opus_priv     = memory_c::alloc(io.get_size() + 8);
3006   auto opus_priv_ptr = opus_priv->get_buffer();
3007 
3008   std::memcpy(opus_priv_ptr, "OpusHead", 8);
3009 
3010   if (io.read(&opus_priv_ptr[8], io.get_size()) != io.get_size()) {
3011     mxdebug_if(m_debug_headers, fmt::format("{0}Opus box read failure", space((level + 1) * 2 + 1)));
3012     return;
3013   }
3014 
3015   // Data in MP4 is stored in Big Endian while Opus-in-Ogg and
3016   // therefore Opus-in-Matroska uses Little Endian in its ID header.
3017   put_uint16_le(&opus_priv_ptr[10], get_uint16_be(&opus_priv_ptr[10])); // pre-skip
3018   put_uint32_le(&opus_priv_ptr[12], get_uint32_be(&opus_priv_ptr[12])); // input sample rate
3019   put_uint16_le(&opus_priv_ptr[16], get_uint16_be(&opus_priv_ptr[16])); // output gain
3020 
3021   priv.push_back(opus_priv);
3022 }
3023 
3024 void
parse_audio_header_priv_atoms(uint64_t atom_size,int level)3025 qtmp4_demuxer_c::parse_audio_header_priv_atoms(uint64_t atom_size,
3026                                                int level) {
3027   auto mem  = stsd->get_buffer() + stsd_non_priv_struct_size;
3028   auto size = atom_size - stsd_non_priv_struct_size;
3029 
3030   mm_mem_io_c mio(mem, size);
3031 
3032   parse_audio_header_priv_atoms(mio, size, level);
3033 }
3034 
3035 void
parse_audio_header_priv_atoms(mm_mem_io_c & mio,uint64_t size,int level)3036 qtmp4_demuxer_c::parse_audio_header_priv_atoms(mm_mem_io_c &mio,
3037                                                uint64_t size,
3038                                                int level) {
3039   if (size < 8)
3040     return;
3041 
3042   try {
3043     while (!mio.eof() && (mio.getFilePointer() < (size - 8))) {
3044       qt_atom_t atom;
3045 
3046       try {
3047         atom = read_qtmp4_atom(&mio, false);
3048       } catch (...) {
3049         return;
3050       }
3051 
3052       mxdebug_if(m_debug_headers, fmt::format("{0}Audio private data size: {1}, type: '{2}'\n", space((level + 1) * 2 + 1), atom.size, atom.fourcc));
3053 
3054       mm_mem_io_c sub_io{mio.get_ro_buffer() + atom.pos + atom.hsize, std::min<uint64_t>(atom.size - atom.hsize, size - atom.pos - atom.hsize)};
3055 
3056       if ((atom.fourcc == "esds") && !esds_parsed)
3057         parse_esds_audio_header_priv_atom(sub_io, level + 1);
3058 
3059       else if (atom.fourcc == "wave")
3060         parse_audio_header_priv_atoms(sub_io, sub_io.get_size(), level + 1);
3061 
3062       else if (atom.fourcc == "dOps")
3063         parse_dops_audio_header_priv_atom(sub_io, level + 1);
3064 
3065       mio.setFilePointer(atom.pos + atom.size);
3066     }
3067   } catch(...) {
3068   }
3069 }
3070 
3071 void
add_data_as_block_addition(uint32_t atom_type,memory_cptr const & data)3072 qtmp4_demuxer_c::add_data_as_block_addition(uint32_t atom_type,
3073                                             memory_cptr const &data) {
3074   block_addition_mapping_t mapping;
3075 
3076   mapping.id_type       = atom_type;
3077   mapping.id_extra_data = data;
3078 
3079   m_block_addition_mappings.emplace_back(mapping);
3080 }
3081 
3082 void
parse_video_header_priv_atoms(uint64_t atom_size,int level)3083 qtmp4_demuxer_c::parse_video_header_priv_atoms(uint64_t atom_size,
3084                                                int level) {
3085   auto mem  = stsd->get_buffer() + stsd_non_priv_struct_size;
3086   auto size = atom_size - stsd_non_priv_struct_size;
3087 
3088   if (   !codec.is(codec_c::type_e::V_MPEG4_P10)
3089       && !codec.is(codec_c::type_e::V_MPEGH_P2)
3090       && !codec.is(codec_c::type_e::V_AV1)
3091       && size
3092       && !fourcc.equiv("mp4v")
3093       && !fourcc.equiv("xvid")) {
3094     priv.clear();
3095     priv.emplace_back(memory_c::clone(mem, size));
3096     return;
3097   }
3098 
3099   mm_mem_io_c mio(mem, size);
3100 
3101   try {
3102     while (!mio.eof() && (mio.getFilePointer() < size)) {
3103       qt_atom_t atom;
3104 
3105       try {
3106         atom = read_qtmp4_atom(&mio);
3107       } catch (...) {
3108         return;
3109       }
3110 
3111       mxdebug_if(m_debug_headers, fmt::format("{0}Video private data size: {1}, type: '{2}'\n", space((level + 1) * 2 + 1), atom.size, atom.fourcc));
3112 
3113       if (mtx::included_in(atom.fourcc, "esds", "avcC", "hvcC", "av1C")) {
3114         auto atom_data = memory_c::alloc(atom.size - atom.hsize);
3115 
3116         if (mio.read(atom_data->get_buffer(), atom_data->get_size()) != atom_data->get_size())
3117           return;
3118 
3119         if (priv.empty())
3120           priv.emplace_back(atom_data);
3121 
3122         if ((atom.fourcc == "esds") && !esds_parsed) {
3123           mm_mem_io_c memio{*atom_data};
3124           esds_parsed = parse_esds_atom(memio, level + 1);
3125         }
3126 
3127       } else if (atom.fourcc == "colr")
3128         handle_colr_atom(mio.read(atom.size - atom.hsize), level + 2);
3129 
3130       else if (mtx::included_in(atom.fourcc, "dvcC", "dvvC", "hvcE"))
3131         add_data_as_block_addition(atom.fourcc.value(), mio.read(atom.size - atom.hsize));
3132 
3133       mio.setFilePointer(atom.pos + atom.size);
3134     }
3135   } catch(...) {
3136   }
3137 }
3138 
3139 void
parse_subtitles_header_priv_atoms(uint64_t atom_size,int level)3140 qtmp4_demuxer_c::parse_subtitles_header_priv_atoms(uint64_t atom_size,
3141                                                    int level) {
3142   auto mem  = stsd->get_buffer() + stsd_non_priv_struct_size;
3143   auto size = atom_size - stsd_non_priv_struct_size;
3144 
3145   if (!fourcc.equiv("mp4s")) {
3146     priv.clear();
3147     priv.emplace_back(memory_c::clone(mem, size));
3148     return;
3149   }
3150 
3151   mm_mem_io_c mio(mem, size);
3152 
3153   try {
3154     while (!mio.eof() && (mio.getFilePointer() < size)) {
3155       qt_atom_t atom;
3156 
3157       try {
3158         atom = read_qtmp4_atom(&mio);
3159       } catch (...) {
3160         return;
3161       }
3162 
3163       mxdebug_if(m_debug_headers, fmt::format("{0}Subtitles private data size: {1}, type: '{2}'\n", space((level + 1) * 2 + 1), atom.size, atom.fourcc));
3164 
3165       if (atom.fourcc == "esds") {
3166         if (priv.empty()) {
3167           priv.emplace_back(memory_c::alloc(atom.size - atom.hsize));
3168           if (mio.read(priv[0], priv[0]->get_size()) != priv[0]->get_size()) {
3169             priv.clear();
3170             return;
3171           }
3172         }
3173 
3174         if (!esds_parsed) {
3175           mm_mem_io_c memio(priv[0]->get_buffer(), priv[0]->get_size());
3176           esds_parsed = parse_esds_atom(memio, level + 1);
3177         }
3178       }
3179 
3180       mio.setFilePointer(atom.pos + atom.size);
3181     }
3182   } catch(...) {
3183   }
3184 
3185   mxdebug_if(m_debug_headers, fmt::format("{0}Decoder config data at end of parsing: {1}\n", space((level + 1) * 2 + 1), esds.decoder_config ? esds.decoder_config->get_size() : 0));
3186 }
3187 
3188 bool
parse_esds_atom(mm_io_c & io,int level)3189 qtmp4_demuxer_c::parse_esds_atom(mm_io_c &io,
3190                                  int level) {
3191   int lsp      = (level + 1) * 2;
3192   esds.version = io.read_uint8();
3193   esds.flags   = io.read_uint24_be();
3194   auto tag     = io.read_uint8();
3195 
3196   mxdebug_if(m_debug_headers, fmt::format("{0}esds: version: {1}, flags: {2}\n", space(lsp + 1), static_cast<unsigned int>(esds.version), esds.flags));
3197 
3198   if (mtx::mp4::DESCRIPTOR_TYPE_ES == tag) {
3199     auto len             = io.read_mp4_descriptor_len();
3200     esds.esid            = io.read_uint16_be();
3201     esds.stream_priority = io.read_uint8();
3202     mxdebug_if(m_debug_headers, fmt::format("{0}esds: id: {1}, stream priority: {2}, len: {3}\n", space(lsp + 1), static_cast<unsigned int>(esds.esid), static_cast<unsigned int>(esds.stream_priority), len));
3203 
3204   } else {
3205     esds.esid = io.read_uint16_be();
3206     mxdebug_if(m_debug_headers, fmt::format("{0}esds: id: {1}\n", space(lsp + 1), static_cast<unsigned int>(esds.esid)));
3207   }
3208 
3209   tag = io.read_uint8();
3210   mxdebug_if(m_debug_headers, fmt::format("{0}tag is 0x{1:02x} ({2}).\n", space(lsp + 1), static_cast<unsigned int>(tag), displayable_esds_tag_name(tag)));
3211 
3212   if (mtx::mp4::DESCRIPTOR_TYPE_DEC_CONFIG != tag)
3213     return false;
3214 
3215   auto len                = io.read_mp4_descriptor_len();
3216 
3217   esds.object_type_id     = io.read_uint8();
3218   esds.stream_type        = io.read_uint8();
3219   esds.buffer_size_db     = io.read_uint24_be();
3220   esds.max_bitrate        = io.read_uint32_be();
3221   esds.avg_bitrate        = io.read_uint32_be();
3222   esds.decoder_config.reset();
3223 
3224   mxdebug_if(m_debug_headers,
3225              fmt::format("{0}esds: decoder config descriptor, len: {1}, object_type_id: {2}, "
3226                          "stream_type: 0x{3:2x}, buffer_size_db: {4}, max_bitrate: {5:.3f}kbit/s, avg_bitrate: {6:.3f}kbit/s\n",
3227                          space(lsp + 1),
3228                          len,
3229                          static_cast<unsigned int>(esds.object_type_id),
3230                          static_cast<unsigned int>(esds.stream_type),
3231                          esds.buffer_size_db,
3232                          esds.max_bitrate / 1000.0,
3233                          esds.avg_bitrate / 1000.0));
3234 
3235   tag = io.read_uint8();
3236   mxdebug_if(m_debug_headers, fmt::format("{0}tag is 0x{1:02x} ({2}).\n", space(lsp + 1), static_cast<unsigned int>(tag), displayable_esds_tag_name(tag)));
3237 
3238   if (mtx::mp4::DESCRIPTOR_TYPE_DEC_SPECIFIC == tag) {
3239     len = io.read_mp4_descriptor_len();
3240     if (!len)
3241       throw mtx::input::header_parsing_x();
3242 
3243     esds.decoder_config = memory_c::alloc(len);
3244     if (io.read(esds.decoder_config, len) != len) {
3245       esds.decoder_config.reset();
3246       throw mtx::input::header_parsing_x();
3247     }
3248 
3249     tag = io.read_uint8();
3250 
3251     if (m_debug_headers) {
3252       mxdebug(fmt::format("{0}esds: decoder specific descriptor, len: {1}\n", space(lsp + 1), len));
3253       mxdebug(fmt::format("{0}esds: dumping decoder specific descriptor\n", space(lsp + 3)));
3254       debugging_c::hexdump(esds.decoder_config->get_buffer(), esds.decoder_config->get_size());
3255       mxdebug(fmt::format("{0}tag is 0x{1:02x} ({2}).\n", space(lsp + 1), static_cast<unsigned int>(tag), displayable_esds_tag_name(tag)));
3256     }
3257 
3258   }
3259 
3260   if (mtx::mp4::DESCRIPTOR_TYPE_SL_CONFIG == tag) {
3261     len = io.read_mp4_descriptor_len();
3262     if (!len)
3263       throw mtx::input::header_parsing_x{};
3264 
3265     esds.sl_config     = memory_c::alloc(len);
3266     if (io.read(esds.sl_config, len) != len) {
3267       esds.sl_config.reset();
3268       throw mtx::input::header_parsing_x();
3269     }
3270 
3271     mxdebug_if(m_debug_headers, fmt::format("{0}esds: sync layer config descriptor, len: {1}\n", space(lsp + 1), len));
3272 
3273   }
3274 
3275   return true;
3276 }
3277 
3278 void
derive_track_params_from_ac3_audio_bitstream()3279 qtmp4_demuxer_c::derive_track_params_from_ac3_audio_bitstream() {
3280   auto buf    = read_first_bytes(64);
3281   auto header = mtx::ac3::frame_c{};
3282 
3283   if (!buf || (-1 == header.find_in(buf)))
3284     return;
3285 
3286   a_channels   = header.m_channels;
3287   a_samplerate = header.m_sample_rate;
3288   codec        = header.get_codec();
3289 }
3290 
3291 void
derive_track_params_from_dts_audio_bitstream()3292 qtmp4_demuxer_c::derive_track_params_from_dts_audio_bitstream() {
3293   auto buf    = read_first_bytes(16384);
3294   auto header = mtx::dts::header_t{};
3295 
3296   if (!buf || (-1 == mtx::dts::find_header(buf->get_buffer(), buf->get_size(), header, false)))
3297     return;
3298 
3299   a_channels   = header.get_total_num_audio_channels();
3300   a_samplerate = header.get_effective_sampling_frequency();
3301   a_bitdepth   = std::max(header.source_pcm_resolution, 0);
3302 }
3303 
3304 void
derive_track_params_from_mp3_audio_bitstream()3305 qtmp4_demuxer_c::derive_track_params_from_mp3_audio_bitstream() {
3306   auto buf = read_first_bytes(64);
3307   if (!buf)
3308     return;
3309 
3310   mp3_header_t header;
3311   auto offset = find_mp3_header(buf->get_buffer(), buf->get_size());
3312   if ((-1 == offset) || !decode_mp3_header(&buf->get_buffer()[offset], &header))
3313     return;
3314 
3315   a_channels   = header.channels;
3316   a_samplerate = header.sampling_frequency;
3317   codec        = header.get_codec();
3318 }
3319 
3320 bool
derive_track_params_from_opus_private_data()3321 qtmp4_demuxer_c::derive_track_params_from_opus_private_data() {
3322   if (priv.empty() || !priv[0]) {
3323     mxdebug_if(m_debug_headers, "derive_opus: no private data\n");
3324     return false;
3325   }
3326 
3327   if (priv[0]->get_size() < 19) {
3328     mxdebug_if(m_debug_headers, fmt::format("derive_opus: private data too small: {} < 19\n", priv[0]->get_size()));
3329     return false;
3330   }
3331 
3332   mxdebug_if(m_debug_headers, fmt::format("derive_opus: OK\n"));
3333 
3334   return true;
3335 }
3336 
3337 bool
derive_track_params_from_vorbis_private_data()3338 qtmp4_demuxer_c::derive_track_params_from_vorbis_private_data() {
3339   if (priv.size() != 3)
3340     return false;
3341 
3342   ogg_packet ogg_headers[3];
3343   memset(ogg_headers, 0, 3 * sizeof(ogg_packet));
3344   ogg_headers[0].packet   = priv[0]->get_buffer();
3345   ogg_headers[1].packet   = priv[1]->get_buffer();
3346   ogg_headers[2].packet   = priv[2]->get_buffer();
3347   ogg_headers[0].bytes    = priv[0]->get_size();
3348   ogg_headers[1].bytes    = priv[1]->get_size();
3349   ogg_headers[2].bytes    = priv[2]->get_size();
3350   ogg_headers[0].b_o_s    = 1;
3351   ogg_headers[1].packetno = 1;
3352   ogg_headers[2].packetno = 2;
3353   auto derived            = false;
3354 
3355   vorbis_info vi;
3356   vorbis_comment vc;
3357 
3358   vorbis_info_init(&vi);
3359   vorbis_comment_init(&vc);
3360 
3361   try {
3362     for (int i = 0; 3 > i; ++i)
3363       if (vorbis_synthesis_headerin(&vi, &vc, &ogg_headers[i]) < 0)
3364         throw false;
3365 
3366     a_channels   = vi.channels;
3367     a_samplerate = vi.rate;
3368     a_bitdepth   = 0;
3369     derived      = true;
3370 
3371   } catch (bool) {
3372   }
3373 
3374   vorbis_info_clear(&vi);
3375   vorbis_comment_clear(&vc);
3376 
3377   return derived;
3378 }
3379 
3380 void
check_for_hevc_video_annex_b_bitstream()3381 qtmp4_demuxer_c::check_for_hevc_video_annex_b_bitstream() {
3382   auto buf = read_first_bytes(4);
3383   if (!buf || (buf->get_size() < 4))
3384     return;
3385 
3386   auto value = get_uint32_be(buf->get_buffer());
3387   if (value != 0x00000001)
3388     return;
3389 
3390   auto probe_size = 128 * 1024;
3391   while (probe_size <= (1024 * 1024)) {
3392     mtx::hevc::es_parser_c parser;
3393 
3394     buf = read_first_bytes(probe_size);
3395     if (!buf)
3396       return;
3397 
3398     parser.add_bytes(buf);
3399     if (!parser.headers_parsed()) {
3400       probe_size *= 2;
3401       continue;
3402     }
3403 
3404     priv.clear();
3405     priv.emplace_back(parser.get_configuration_record());
3406 
3407     break;
3408   }
3409 
3410   m_hevc_is_annex_b = true;
3411 }
3412 
3413 bool
verify_audio_parameters()3414 qtmp4_demuxer_c::verify_audio_parameters() {
3415   if (codec.is(codec_c::type_e::A_MP2) || codec.is(codec_c::type_e::A_MP3))
3416     derive_track_params_from_mp3_audio_bitstream();
3417 
3418   else if (codec.is(codec_c::type_e::A_AC3))
3419     derive_track_params_from_ac3_audio_bitstream();
3420 
3421   else if (codec.is(codec_c::type_e::A_DTS))
3422     derive_track_params_from_dts_audio_bitstream();
3423 
3424   else if (codec.is(codec_c::type_e::A_VORBIS))
3425     return derive_track_params_from_vorbis_private_data();
3426 
3427   else if (codec.is(codec_c::type_e::A_OPUS))
3428     return derive_track_params_from_opus_private_data();
3429 
3430   else if (codec.is(codec_c::type_e::A_PCM)) {
3431     if (fourcc == "in24")
3432       a_bitdepth = 24;
3433   }
3434 
3435   if ((0 == a_channels) || (0.0 == a_samplerate)) {
3436     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Track {0} is missing some data. Broken header atoms?\n"), id));
3437     return false;
3438   }
3439 
3440   if (fourcc.equiv("MP4A") && !verify_mp4a_audio_parameters())
3441     return false;
3442 
3443   if (codec.is(codec_c::type_e::A_ALAC))
3444     return verify_alac_audio_parameters();
3445 
3446   if (codec.is(codec_c::type_e::A_DTS))
3447     return verify_dts_audio_parameters();
3448 
3449   return true;
3450 }
3451 
3452 bool
verify_alac_audio_parameters()3453 qtmp4_demuxer_c::verify_alac_audio_parameters() {
3454   if (!stsd || (stsd->get_size() < (stsd_non_priv_struct_size + 12 + sizeof(mtx::alac::codec_config_t)))) {
3455     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Track {0} is missing some data. Broken header atoms?\n"), id));
3456     return false;
3457   }
3458 
3459   auto codec_config = reinterpret_cast<mtx::alac::codec_config_t *>(stsd->get_buffer() + stsd_non_priv_struct_size + 12);
3460   a_channels        = codec_config->num_channels;
3461   a_bitdepth        = codec_config->bit_depth;
3462   a_samplerate      = get_uint32_be(&codec_config->sample_rate);
3463 
3464   return true;
3465 }
3466 
3467 bool
verify_dts_audio_parameters()3468 qtmp4_demuxer_c::verify_dts_audio_parameters() {
3469   auto buf = read_first_bytes(16384);
3470 
3471   if (!buf || (-1 == mtx::dts::find_header(buf->get_buffer(), buf->get_size(), m_dts_header, false))) {
3472     mxwarn_tid(m_reader.m_ti.m_fname, id, Y("No DTS header found in first frames; track will be skipped.\n"));
3473     return false;
3474   }
3475 
3476   codec.set_specialization(m_dts_header.get_codec_specialization());
3477 
3478   return true;
3479 }
3480 
3481 bool
verify_mp4a_audio_parameters()3482 qtmp4_demuxer_c::verify_mp4a_audio_parameters() {
3483   auto cdc = codec_c::look_up_object_type_id(esds.object_type_id);
3484   if (!cdc.is(codec_c::type_e::A_AAC) && !cdc.is(codec_c::type_e::A_DTS) && !cdc.is(codec_c::type_e::A_MP2) && !cdc.is(codec_c::type_e::A_MP3)) {
3485     mxwarn(fmt::format(Y("Quicktime/MP4 reader: The audio track {0} is using an unsupported 'object type id' of {1} in the 'esds' atom. Skipping this track.\n"), id, static_cast<unsigned int>(esds.object_type_id)));
3486     return false;
3487   }
3488 
3489   if (cdc.is(codec_c::type_e::A_AAC) && (!esds.decoder_config || !a_aac_audio_config)) {
3490     mxwarn(fmt::format(Y("Quicktime/MP4 reader: The AAC track {0} is missing the esds atom/the decoder config. Skipping this track.\n"), id));
3491     return false;
3492   }
3493 
3494   return true;
3495 }
3496 
3497 bool
verify_video_parameters()3498 qtmp4_demuxer_c::verify_video_parameters() {
3499   if (codec.is(codec_c::type_e::V_MPEGH_P2))
3500     check_for_hevc_video_annex_b_bitstream();
3501 
3502   if (!v_width || !v_height || !fourcc) {
3503     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Track {0} is missing some data. Broken header atoms?\n"), id));
3504     return false;
3505   }
3506 
3507   if (fourcc.equiv("mp4v"))
3508     return verify_mp4v_video_parameters();
3509 
3510   if (codec.is(codec_c::type_e::V_MPEG4_P10))
3511     return verify_avc_video_parameters();
3512 
3513   else if (codec.is(codec_c::type_e::V_MPEGH_P2))
3514     return verify_hevc_video_parameters();
3515 
3516   return true;
3517 }
3518 
3519 bool
derive_track_params_from_avc_bitstream()3520 qtmp4_demuxer_c::derive_track_params_from_avc_bitstream() {
3521   priv.clear();
3522 
3523   // No avcC content? Try to build one from the first frames.
3524   auto mem = read_first_bytes(10'000);
3525 
3526   mxdebug_if(m_debug_headers, fmt::format("derive_track_params_from_avc_bitstream: deriving avcC from bitstream; read {0} bytes\n", mem ? mem->get_size() : 0));
3527 
3528   if (!mem)
3529     return false;
3530 
3531   mtx::avc::es_parser_c parser;
3532 
3533   parser.add_bytes(mem->get_buffer(), mem->get_size());
3534   parser.flush();
3535 
3536   if (parser.headers_parsed())
3537     priv.emplace_back(parser.get_configuration_record());
3538 
3539   mxdebug_if(m_debug_headers, fmt::format("derive_track_params_from_avc_bitstream: avcC derived? size {0} bytes\n", !priv.empty() && priv[0] ? priv[0]->get_size() : 0));
3540 
3541   return !priv.empty() && priv[0]->get_size();
3542 }
3543 
3544 bool
verify_avc_video_parameters()3545 qtmp4_demuxer_c::verify_avc_video_parameters() {
3546   if (!priv.empty() && (4 <= priv[0]->get_size()))
3547     return true;
3548 
3549   if (derive_track_params_from_avc_bitstream())
3550     return true;
3551 
3552   mxwarn(fmt::format(Y("Quicktime/MP4 reader: MPEG4 part 10/AVC track {0} is missing its decoder config. Skipping this track.\n"), id));
3553   return false;
3554 }
3555 
3556 bool
verify_hevc_video_parameters()3557 qtmp4_demuxer_c::verify_hevc_video_parameters() {
3558   if (priv.empty() || (23 > priv[0]->get_size())) {
3559     mxwarn(fmt::format(Y("Quicktime/MP4 reader: MPEGH part 2/HEVC track {0} is missing its decoder config. Skipping this track.\n"), id));
3560     return false;
3561   }
3562 
3563   return true;
3564 }
3565 
3566 bool
verify_mp4v_video_parameters()3567 qtmp4_demuxer_c::verify_mp4v_video_parameters() {
3568   if (!esds_parsed) {
3569     mxwarn(fmt::format(Y("Quicktime/MP4 reader: The video track {0} is missing the ESDS atom. Skipping this track.\n"), id));
3570     return false;
3571   }
3572 
3573   if (codec.is(codec_c::type_e::V_MPEG4_P2) && !esds.decoder_config) {
3574     // This is MPEG4 video, and we need header data for it.
3575     mxwarn(fmt::format(Y("Quicktime/MP4 reader: MPEG4 track {0} is missing the esds atom/the decoder config. Skipping this track.\n"), id));
3576     return false;
3577   }
3578 
3579   return true;
3580 }
3581 
3582 bool
verify_subtitles_parameters()3583 qtmp4_demuxer_c::verify_subtitles_parameters() {
3584   if (codec.is(codec_c::type_e::S_VOBSUB))
3585     return verify_vobsub_subtitles_parameters();
3586 
3587   return false;
3588 }
3589 
3590 bool
verify_vobsub_subtitles_parameters()3591 qtmp4_demuxer_c::verify_vobsub_subtitles_parameters() {
3592   if (!esds.decoder_config || (64 > esds.decoder_config->get_size())) {
3593     mxwarn(fmt::format(Y("Quicktime/MP4 reader: Track {0} is missing some data. Broken header atoms?\n"), id));
3594     return false;
3595   }
3596 
3597   return true;
3598 }
3599 
3600 void
determine_codec()3601 qtmp4_demuxer_c::determine_codec() {
3602   codec = codec_c::look_up_object_type_id(esds.object_type_id);
3603 
3604   if (!codec) {
3605     codec = codec_c::look_up(fourcc);
3606 
3607     if (codec.is(codec_c::type_e::A_PCM))
3608       m_pcm_format = fourcc == "twos" ? pcm_packetizer_c::big_endian_integer : pcm_packetizer_c::little_endian_integer;
3609   }
3610 }
3611