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 ¤t_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 ¤t) { 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 ×tamp : 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