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 RealMedia demultiplexer module
10
11 Written by Moritz Bunkus <moritz@bunkus.org>.
12 */
13
14 #include "common/common_pch.h"
15
16 #include <matroska/KaxTrackVideo.h>
17
18 #include "common/bit_reader.h"
19 #include "common/codec.h"
20 #include "common/debugging.h"
21 #include "common/ebml.h"
22 #include "common/endian.h"
23 #include "common/error.h"
24 #include "common/id_info.h"
25 #include "input/r_real.h"
26 #include "merge/file_status.h"
27 #include "merge/input_x.h"
28 #include "merge/output_control.h"
29 #include "output/p_aac.h"
30 #include "output/p_ac3.h"
31 #include "output/p_passthrough.h"
32 #include "output/p_realaudio.h"
33 #include "output/p_generic_video.h"
34
35 using namespace libmatroska;
36
37 namespace {
38 debugging_option_c s_debug{"real_reader"};
39 }
40
41 /*
42 Description of the RealMedia file format:
43 http://www.pcisys.net/~melanson/codecs/rmff.htm
44 */
45
46 extern "C" {
47
48 static void *
mm_io_file_open(const char * path,int)49 mm_io_file_open(const char *path,
50 int) {
51 try {
52 return reinterpret_cast<mm_io_c *>(const_cast<char *>(path));
53 } catch(...) {
54 return nullptr;
55 }
56 }
57
58 static int
mm_io_file_close(void *)59 mm_io_file_close(void *) {
60 return 0;
61 }
62
63 static int64_t
mm_io_file_tell(void * file)64 mm_io_file_tell(void *file) {
65 return file ? static_cast<mm_io_c *>(file)->getFilePointer() : -1;
66 }
67
68 static int64_t
mm_io_file_seek(void * file,int64_t offset,int whence)69 mm_io_file_seek(void *file,
70 int64_t offset,
71 int whence) {
72 if (!file)
73 return -1;
74
75 seek_mode smode = SEEK_END == whence ? libebml::seek_end
76 : SEEK_CUR == whence ? libebml::seek_current
77 : libebml::seek_beginning;
78 return static_cast<mm_io_c *>(file)->setFilePointer2(offset, smode) ? 0 : -1;
79 }
80
81 static int64_t
mm_io_file_read(void * file,void * buffer,int64_t bytes)82 mm_io_file_read(void *file,
83 void *buffer,
84 int64_t bytes) {
85 return !file ? -1 : static_cast<mm_io_c *>(file)->read(buffer, bytes);
86 }
87
88 static int64_t
mm_io_file_write(void * file,const void * buffer,int64_t bytes)89 mm_io_file_write(void *file,
90 const void *buffer,
91 int64_t bytes) {
92 return !file ? -1 : static_cast<mm_io_c *>(file)->write(buffer, bytes);
93 }
94
95 }
96
97 mb_file_io_t mm_io_file_io = {
98 mm_io_file_open,
99 mm_io_file_close,
100 mm_io_file_read,
101 mm_io_file_write,
102 mm_io_file_tell,
103 mm_io_file_seek
104 };
105
106 bool
probe_file()107 real_reader_c::probe_file() {
108 unsigned char data[4];
109 return (m_in->read(data, 4) == 4) && (memcmp(data, ".RMF", 4) == 0);
110 }
111
112 void
read_headers()113 real_reader_c::read_headers() {
114 file = rmff_open_file_with_io(reinterpret_cast<const char *>(m_in.get()), RMFF_OPEN_MODE_READING, &mm_io_file_io);
115 if (!file) {
116 if (RMFF_ERR_NOT_RMFF == rmff_last_error)
117 throw mtx::input::invalid_format_x();
118 else
119 throw mtx::input::open_x();
120 }
121 m_in->setFilePointer(0);
122
123 done = false;
124
125 show_demuxer_info();
126
127 parse_headers();
128 get_information_from_data();
129 }
130
~real_reader_c()131 real_reader_c::~real_reader_c() {
132 rmff_close_file(file);
133 }
134
135 void
parse_headers()136 real_reader_c::parse_headers() {
137
138 if (rmff_read_headers(file) != RMFF_ERR_OK)
139 return;
140
141 int ndx;
142 for (ndx = 0; ndx < file->num_tracks; ndx++) {
143 rmff_track_t *track = file->tracks[ndx];
144
145 if ((RMFF_TRACK_TYPE_UNKNOWN == track->type) || (get_uint32_be(&track->mdpr_header.type_specific_size) == 0))
146 continue;
147 if ((RMFF_TRACK_TYPE_VIDEO == track->type) && !demuxing_requested('v', track->id))
148 continue;
149 if ((RMFF_TRACK_TYPE_AUDIO == track->type) && !demuxing_requested('a', track->id))
150 continue;
151 if ( !track->mdpr_header.mime_type
152 || ( strcmp(track->mdpr_header.mime_type, "audio/x-pn-realaudio")
153 && strcmp(track->mdpr_header.mime_type, "video/x-pn-realvideo")))
154 continue;
155
156 unsigned char *ts_data = track->mdpr_header.type_specific_data;
157 uint32_t ts_size = get_uint32_be(&track->mdpr_header.type_specific_size);
158
159 real_demuxer_cptr dmx(new real_demuxer_t(track));
160
161 if (RMFF_TRACK_TYPE_VIDEO == track->type) {
162 dmx->rvp = (real_video_props_t *)track->mdpr_header.type_specific_data;
163
164 memcpy(dmx->fourcc, &dmx->rvp->fourcc2, 4);
165 dmx->fourcc[4] = 0;
166 dmx->width = get_uint16_be(&dmx->rvp->width);
167 dmx->height = get_uint16_be(&dmx->rvp->height);
168 uint32_t i = get_uint32_be(&dmx->rvp->fps);
169 dmx->fps = static_cast<double>((i & 0xffff0000) >> 16) + (i & 0x0000ffff) / 65536.0;
170 dmx->private_data = memory_c::clone(ts_data, ts_size);
171
172 demuxers.push_back(dmx);
173
174 } else if (RMFF_TRACK_TYPE_AUDIO == track->type) {
175 bool ok = true;
176
177 dmx->ra4p = (real_audio_v4_props_t *)track->mdpr_header.type_specific_data;
178 dmx->ra5p = (real_audio_v5_props_t *)track->mdpr_header.type_specific_data;
179
180 int version = get_uint16_be(&dmx->ra4p->version1);
181
182 if (3 == version) {
183 dmx->samples_per_second = 8000;
184 dmx->channels = 1;
185 dmx->bits_per_sample = 16;
186 strcpy(dmx->fourcc, "14_4");
187
188 } else if (4 == version) {
189 dmx->samples_per_second = get_uint16_be(&dmx->ra4p->sample_rate);
190 dmx->channels = get_uint16_be(&dmx->ra4p->channels);
191 dmx->bits_per_sample = get_uint16_be(&dmx->ra4p->sample_size);
192
193 unsigned char *p = (unsigned char *)(dmx->ra4p + 1);
194 int slen = p[0];
195 p += (slen + 1);
196 slen = p[0];
197 p++;
198
199 if (4 != slen) {
200 mxwarn(fmt::format(Y("real_reader: Couldn't find RealAudio FourCC for id {0} (description length: {1}) Skipping track.\n"), track->id, slen));
201 ok = false;
202
203 } else {
204 memcpy(dmx->fourcc, p, 4);
205 dmx->fourcc[4] = 0;
206 p += 4;
207
208 if (ts_size > static_cast<unsigned int>(p - ts_data))
209 dmx->extra_data = memory_c::clone(p, ts_size - (p - ts_data));
210 }
211
212 } else if (5 == version) {
213 dmx->samples_per_second = get_uint16_be(&dmx->ra5p->sample_rate);
214 dmx->channels = get_uint16_be(&dmx->ra5p->channels);
215 dmx->bits_per_sample = get_uint16_be(&dmx->ra5p->sample_size);
216
217 memcpy(dmx->fourcc, &dmx->ra5p->fourcc3, 4);
218 dmx->fourcc[4] = 0;
219
220 if ((sizeof(real_audio_v5_props_t) + 4) < ts_size)
221 dmx->extra_data = memory_c::clone(reinterpret_cast<unsigned char *>(dmx->ra5p) + 4 + sizeof(real_audio_v5_props_t), ts_size - 4 - sizeof(real_audio_v5_props_t));
222
223 } else {
224 mxwarn(fmt::format(Y("real_reader: Only audio header versions 3, 4 and 5 are supported. Track ID {0} uses version {1} and will be skipped.\n"),
225 track->id, version));
226 ok = false;
227 }
228
229 mxdebug_if(s_debug, fmt::format("real_reader: extra_data_size: {0}\n", dmx->extra_data->get_size()));
230
231 if (ok) {
232 dmx->private_data = memory_c::clone(ts_data, ts_size);
233 demuxers.push_back(dmx);
234 }
235 }
236 }
237 }
238
239 void
create_video_packetizer(real_demuxer_cptr dmx)240 real_reader_c::create_video_packetizer(real_demuxer_cptr dmx) {
241 m_ti.m_private_data = dmx->private_data;
242 std::string codec_id = fmt::format("V_REAL/{0}", dmx->fourcc);
243 dmx->ptzr = add_packetizer(new generic_video_packetizer_c(this, m_ti, codec_id.c_str(), 0.0, dmx->width, dmx->height));
244
245 if (strcmp(dmx->fourcc, "RV40"))
246 dmx->rv_dimensions = true;
247
248 show_packetizer_info(dmx->track->id, ptzr(dmx->ptzr));
249 }
250
251 void
create_dnet_audio_packetizer(real_demuxer_cptr dmx)252 real_reader_c::create_dnet_audio_packetizer(real_demuxer_cptr dmx) {
253 dmx->ptzr = add_packetizer(new ac3_bs_packetizer_c(this, m_ti, dmx->samples_per_second, dmx->channels, dmx->bsid));
254 show_packetizer_info(dmx->track->id, ptzr(dmx->ptzr));
255 }
256
257 void
create_aac_audio_packetizer(real_demuxer_cptr dmx)258 real_reader_c::create_aac_audio_packetizer(real_demuxer_cptr dmx) {
259 auto audio_config = mtx::aac::audio_config_t{};
260 bool profile_detected = false;
261
262 int64_t tid = dmx->track->id;
263
264 if ((dmx->extra_data) && (4 < dmx->extra_data->get_size())) {
265 const unsigned char *extra_data = dmx->extra_data->get_buffer();
266 uint32_t extra_len = get_uint32_be(extra_data);
267 mxdebug_if(s_debug, fmt::format("real_reader: extra_len: {0}\n", extra_len));
268
269 if ((4 + extra_len) <= dmx->extra_data->get_size()) {
270 auto parsed_audio_config = mtx::aac::parse_audio_specific_config(&extra_data[4 + 1], extra_len - 1);
271 if (!parsed_audio_config)
272 mxerror_tid(m_ti.m_fname, tid, Y("This AAC track does not contain valid headers. Could not parse the AAC information.\n"));
273
274 audio_config = *parsed_audio_config;
275
276 mxdebug_if(s_debug, fmt::format("real_reader: 1. profile: {0}, channels: {1}, sample_rate: {2}, output_sample_rate: {3}, sbr: {4}\n", audio_config.profile, audio_config.channels, audio_config.sample_rate, audio_config.output_sample_rate, audio_config.sbr));
277
278 if (audio_config.sbr)
279 audio_config.profile = mtx::aac::PROFILE_SBR;
280
281 profile_detected = true;
282 }
283 }
284
285 if (!profile_detected) {
286 audio_config.channels = dmx->channels;
287 audio_config.sample_rate = dmx->samples_per_second;
288 if (!strcasecmp(dmx->fourcc, "racp") || (44100 > audio_config.sample_rate)) {
289 audio_config.output_sample_rate = 2 * audio_config.sample_rate;
290 audio_config.sbr = true;
291 }
292
293 } else {
294 dmx->channels = audio_config.channels;
295 dmx->samples_per_second = audio_config.sample_rate;
296 }
297
298 auto detected_profile = audio_config.profile;
299 if (audio_config.sbr)
300 audio_config.profile = mtx::aac::PROFILE_SBR;
301
302 if ( (mtx::includes(m_ti.m_all_aac_is_sbr, tid) && m_ti.m_all_aac_is_sbr[tid])
303 || (mtx::includes(m_ti.m_all_aac_is_sbr, -1) && m_ti.m_all_aac_is_sbr[-1]))
304 audio_config.profile = mtx::aac::PROFILE_SBR;
305
306 if (profile_detected
307 &&
308 ( (mtx::includes(m_ti.m_all_aac_is_sbr, tid) && !m_ti.m_all_aac_is_sbr[tid])
309 || (mtx::includes(m_ti.m_all_aac_is_sbr, -1) && !m_ti.m_all_aac_is_sbr[-1])))
310 audio_config.profile = detected_profile;
311
312 mxdebug_if(s_debug, fmt::format("real_reader: 2. profile: {0}, channels: {1}, sample_rate: {2}, output_sample_rate: {3}, sbr: {4}\n", audio_config.profile, audio_config.channels, audio_config.sample_rate, audio_config.output_sample_rate, audio_config.sbr));
313
314 dmx->is_aac = true;
315 dmx->ptzr = add_packetizer(new aac_packetizer_c(this, m_ti, audio_config, aac_packetizer_c::headerless));
316
317 show_packetizer_info(tid, ptzr(dmx->ptzr));
318
319 if (mtx::aac::PROFILE_SBR == audio_config.profile)
320 ptzr(dmx->ptzr).set_audio_output_sampling_freq(audio_config.output_sample_rate);
321
322 // AAC packetizers might need the timestamp of the first packet in order
323 // to fill in stuff. Let's misuse ref_timestamp for that.
324 dmx->ref_timestamp = -1;
325 }
326
327 void
create_audio_packetizer(real_demuxer_cptr dmx)328 real_reader_c::create_audio_packetizer(real_demuxer_cptr dmx) {
329 if (!strncmp(dmx->fourcc, "dnet", 4))
330 create_dnet_audio_packetizer(dmx);
331
332 else if (!strcasecmp(dmx->fourcc, "raac") || !strcasecmp(dmx->fourcc, "racp"))
333 create_aac_audio_packetizer(dmx);
334
335 else {
336 if (!strcasecmp(dmx->fourcc, "COOK"))
337 dmx->cook_audio_fix = true;
338
339 m_ti.m_private_data = dmx->private_data;
340 dmx->ptzr = add_packetizer(new ra_packetizer_c(this, m_ti, dmx->samples_per_second, dmx->channels, dmx->bits_per_sample, get_uint32_be(dmx->fourcc)));
341
342 show_packetizer_info(dmx->track->id, ptzr(dmx->ptzr));
343 }
344 }
345
346 void
create_packetizer(int64_t tid)347 real_reader_c::create_packetizer(int64_t tid) {
348
349 real_demuxer_cptr dmx = find_demuxer(tid);
350 if (!dmx)
351 return;
352
353 if (-1 != dmx->ptzr)
354 return;
355
356 rmff_track_t *track = dmx->track;
357 m_ti.m_id = track->id;
358 m_ti.m_private_data.reset();
359
360 if (RMFF_TRACK_TYPE_VIDEO == track->type)
361 create_video_packetizer(dmx);
362 else
363 create_audio_packetizer(dmx);
364 }
365
366 void
create_packetizers()367 real_reader_c::create_packetizers() {
368 uint32_t i;
369
370 for (i = 0; i < demuxers.size(); i++)
371 create_packetizer(demuxers[i]->track->id);
372 }
373
374 real_demuxer_cptr
find_demuxer(unsigned int id)375 real_reader_c::find_demuxer(unsigned int id) {
376 size_t i;
377
378 for (i = 0; i < demuxers.size(); i++)
379 if (demuxers[i]->track->id == id)
380 return demuxers[i];
381
382 return real_demuxer_cptr{};
383 }
384
385 file_status_e
finish()386 real_reader_c::finish() {
387 size_t i;
388
389 for (i = 0; i < demuxers.size(); i++) {
390 real_demuxer_cptr dmx = demuxers[i];
391 if (dmx && dmx->track && (dmx->track->type == RMFF_TRACK_TYPE_AUDIO) && !dmx->segments.empty())
392 deliver_audio_frames(dmx, dmx->num_packets ? dmx->last_timestamp / dmx->num_packets : 0);
393 }
394
395 done = true;
396
397 return flush_packetizers();
398 }
399
400 file_status_e
read(generic_packetizer_c *,bool)401 real_reader_c::read(generic_packetizer_c *,
402 bool) {
403 if (done)
404 return flush_packetizers();
405
406 int size = rmff_get_next_frame_size(file);
407 if (0 >= size) {
408 if (file->num_packets_read < file->num_packets_in_chunk)
409 mxwarn_fn(m_ti.m_fname, fmt::format(Y("File contains fewer frames than expected or is corrupt after frame {0}.\n"), file->num_packets_read));
410 return finish();
411 }
412
413 auto mem = memory_c::alloc(size);
414 auto frame = rmff_read_next_frame(file, mem->get_buffer());
415
416 if (!frame) {
417 if (file->num_packets_read < file->num_packets_in_chunk)
418 mxwarn_fn(m_ti.m_fname, fmt::format(Y("File contains fewer frames than expected or is corrupt after frame {0}.\n"), file->num_packets_read));
419 return finish();
420 }
421
422 int64_t timestamp = (int64_t)frame->timecode * 1000000ll;
423 real_demuxer_cptr dmx = find_demuxer(frame->id);
424
425 if (!dmx || (-1 == dmx->ptzr)) {
426 rmff_release_frame(frame);
427 return FILE_STATUS_MOREDATA;
428 }
429
430 if (dmx->cook_audio_fix && dmx->first_frame && ((frame->flags & RMFF_FRAME_FLAG_KEYFRAME) != RMFF_FRAME_FLAG_KEYFRAME))
431 dmx->force_keyframe_flag = true;
432
433 if (dmx->force_keyframe_flag && ((frame->flags & RMFF_FRAME_FLAG_KEYFRAME) == RMFF_FRAME_FLAG_KEYFRAME))
434 dmx->force_keyframe_flag = false;
435
436 if (dmx->force_keyframe_flag)
437 frame->flags |= RMFF_FRAME_FLAG_KEYFRAME;
438
439 if (RMFF_TRACK_TYPE_VIDEO == dmx->track->type)
440 assemble_video_packet(dmx, frame);
441
442 else if (dmx->is_aac) {
443 // If the first AAC packet does not start at 0 then let the AAC
444 // packetizer adjust its data accordingly.
445 if (dmx->first_frame) {
446 dmx->ref_timestamp = timestamp;
447 ptzr(dmx->ptzr).set_displacement_maybe(timestamp);
448 }
449
450 deliver_aac_frames(dmx, *mem);
451
452 } else
453 queue_audio_frames(dmx, mem, timestamp, frame->flags);
454
455 rmff_release_frame(frame);
456
457 dmx->first_frame = false;
458
459 return FILE_STATUS_MOREDATA;
460 }
461
462 void
queue_one_audio_frame(real_demuxer_cptr dmx,memory_cptr const & mem,uint64_t timestamp,uint32_t flags)463 real_reader_c::queue_one_audio_frame(real_demuxer_cptr dmx,
464 memory_cptr const &mem,
465 uint64_t timestamp,
466 uint32_t flags) {
467 rv_segment_cptr segment(new rv_segment_t);
468
469 segment->data = mem;
470 segment->flags = flags;
471 dmx->segments.push_back(segment);
472
473 dmx->last_timestamp = timestamp;
474
475 mxdebug_if(s_debug, fmt::format("'{0}' track {1}: enqueueing one length {2} timestamp {3} flags 0x{4:08x}\n", m_ti.m_fname, dmx->track->id, mem->get_size(), timestamp, flags));
476 }
477
478 void
queue_audio_frames(real_demuxer_cptr dmx,memory_cptr const & mem,uint64_t timestamp,uint32_t flags)479 real_reader_c::queue_audio_frames(real_demuxer_cptr dmx,
480 memory_cptr const &mem,
481 uint64_t timestamp,
482 uint32_t flags) {
483 // Enqueue the packets if no packets are in the queue or if the current
484 // packet's timestamp is the same as the timestamp of those before.
485 if (dmx->segments.empty() || (dmx->last_timestamp == timestamp)) {
486 queue_one_audio_frame(dmx, mem, timestamp, flags);
487 return;
488 }
489
490 // This timestamp is different. So let's push the packets out.
491 deliver_audio_frames(dmx, (timestamp - dmx->last_timestamp) / dmx->segments.size());
492
493 // Enqueue this packet.
494 queue_one_audio_frame(dmx, mem, timestamp, flags);
495 }
496
497 void
deliver_audio_frames(real_demuxer_cptr dmx,uint64_t duration)498 real_reader_c::deliver_audio_frames(real_demuxer_cptr dmx,
499 uint64_t duration) {
500 uint32_t i;
501
502 if (dmx->segments.empty() || (-1 == dmx->ptzr))
503 return;
504
505 for (i = 0; i < dmx->segments.size(); i++) {
506 rv_segment_cptr segment = dmx->segments[i];
507 mxdebug_if(s_debug, fmt::format("'{0}' track {1}: delivering audio length {2} timestamp {3} flags 0x{4:08x} duration {5}\n", m_ti.m_fname, dmx->track->id, segment->data->get_size(), dmx->last_timestamp, segment->flags, duration));
508
509 ptzr(dmx->ptzr).process(std::make_shared<packet_t>(segment->data, dmx->last_timestamp, duration, (segment->flags & RMFF_FRAME_FLAG_KEYFRAME) == RMFF_FRAME_FLAG_KEYFRAME ? -1 : dmx->ref_timestamp));
510 if ((segment->flags & 2) == 2)
511 dmx->ref_timestamp = dmx->last_timestamp;
512 }
513
514 dmx->num_packets += dmx->segments.size();
515 dmx->segments.clear();
516 }
517
518 void
deliver_aac_frames(real_demuxer_cptr dmx,memory_c & mem)519 real_reader_c::deliver_aac_frames(real_demuxer_cptr dmx,
520 memory_c &mem) {
521 unsigned char *chunk = mem.get_buffer();
522 int length = mem.get_size();
523 if (2 > length) {
524 mxwarn_tid(m_ti.m_fname, dmx->track->id, fmt::format(Y("Short AAC audio packet (length: {0} < 2)\n"), length));
525 return;
526 }
527
528 int num_sub_packets = chunk[1] >> 4;
529 mxdebug_if(s_debug, fmt::format("real_reader: num_sub_packets = {0}\n", num_sub_packets));
530 if ((2 + num_sub_packets * 2) > length) {
531 mxwarn_tid(m_ti.m_fname, dmx->track->id, fmt::format(Y("Short AAC audio packet (length: {0} < {1})\n"), length, 2 + num_sub_packets * 2));
532 return;
533 }
534
535 int i, len_check = 2 + num_sub_packets * 2;
536 for (i = 0; i < num_sub_packets; i++) {
537 int sub_length = get_uint16_be(&chunk[2 + i * 2]);
538 len_check += sub_length;
539
540 mxdebug_if(s_debug, fmt::format("real_reader: {0}: length {1}\n", i, sub_length));
541 }
542
543 if (len_check != length) {
544 mxwarn_tid(m_ti.m_fname, dmx->track->id, fmt::format(Y("Inconsistent AAC audio packet (length: {0} != {1})\n"), length, len_check));
545 return;
546 }
547
548 int data_idx = 2 + num_sub_packets * 2;
549 for (i = 0; i < num_sub_packets; i++) {
550 int sub_length = get_uint16_be(&chunk[2 + i * 2]);
551 ptzr(dmx->ptzr).process(std::make_shared<packet_t>(memory_c::borrow(&chunk[data_idx], sub_length)));
552 data_idx += sub_length;
553 }
554 }
555
556 void
identify()557 real_reader_c::identify() {
558 id_result_container();
559
560 size_t i;
561 for (i = 0; i < demuxers.size(); i++) {
562 auto info = mtx::id::info_c{};
563 auto demuxer = demuxers[i];
564 auto type = RMFF_TRACK_TYPE_AUDIO == demuxer->track->type ? ID_RESULT_TRACK_AUDIO : ID_RESULT_TRACK_VIDEO;
565
566 info.set(mtx::id::number, demuxer->track->id);
567
568 if (RMFF_TRACK_TYPE_VIDEO == demuxer->track->type)
569 info.add(mtx::id::pixel_dimensions, fmt::format("{0}x{1}", demuxer->width, demuxer->height));
570
571 else if (RMFF_TRACK_TYPE_AUDIO == demuxer->track->type) {
572 info.add(mtx::id::audio_channels, demuxer->channels);
573 info.add(mtx::id::audio_sampling_frequency, demuxer->samples_per_second);
574 info.add(mtx::id::audio_bits_per_sample, demuxer->bits_per_sample);
575 }
576
577 id_result_track(demuxer->track->id, type, codec_c::get_name(demuxer->fourcc, demuxer->fourcc), info.get());
578 }
579 }
580
581 void
assemble_video_packet(real_demuxer_cptr dmx,rmff_frame_t * frame)582 real_reader_c::assemble_video_packet(real_demuxer_cptr dmx,
583 rmff_frame_t *frame) {
584 int result = rmff_assemble_packed_video_frame(dmx->track, frame);
585 if (0 > result) {
586 mxwarn_tid(m_ti.m_fname, dmx->track->id, fmt::format(Y("Video packet assembly failed. Error code: {0} ({1})\n"), rmff_last_error, rmff_last_error_msg));
587 return;
588 }
589
590 rmff_frame_t *assembled = rmff_get_packed_video_frame(dmx->track);
591 while (assembled) {
592 if (!dmx->rv_dimensions)
593 set_dimensions(dmx, assembled->data, assembled->size);
594
595 auto packet = std::make_shared<packet_t>(memory_c::take_ownership(assembled->data, assembled->size),
596 (int64_t)assembled->timecode * 1000000,
597 0,
598 (assembled->flags & RMFF_FRAME_FLAG_KEYFRAME) == RMFF_FRAME_FLAG_KEYFRAME ? VFT_IFRAME : VFT_PFRAMEAUTOMATIC,
599 VFT_NOBFRAME);
600 ptzr(dmx->ptzr).process(packet);
601
602 assembled->allocated_by_rmff = 0;
603 rmff_release_frame(assembled);
604 assembled = rmff_get_packed_video_frame(dmx->track);
605 }
606 }
607
608 bool
get_rv_dimensions(unsigned char * buf,int size,uint32_t & width,uint32_t & height)609 real_reader_c::get_rv_dimensions(unsigned char *buf,
610 int size,
611 uint32_t &width,
612 uint32_t &height) {
613 static const uint32_t cw[8] = { 160, 176, 240, 320, 352, 640, 704, 0 };
614 static const uint32_t ch1[8] = { 120, 132, 144, 240, 288, 480, 0, 0 };
615 static const uint32_t ch2[4] = { 180, 360, 576, 0 };
616 mtx::bits::reader_c bc(buf, size);
617
618 try {
619 bc.skip_bits(13);
620 bc.skip_bits(13);
621 int v = bc.get_bits(3);
622
623 int w = cw[v];
624 if (0 == w) {
625 int c;
626 do {
627 c = bc.get_bits(8);
628 w += (c << 2);
629 } while (c == 255);
630 }
631
632 int c = bc.get_bits(3);
633 int h = ch1[c];
634 if (0 == h) {
635 v = bc.get_bits(1);
636 c = ((c << 1) | v) & 3;
637 h = ch2[c];
638 if (0 == h) {
639 do {
640 c = bc.get_bits(8);
641 h += (c << 2);
642 } while (c == 255);
643 }
644 }
645
646 width = w;
647 height = h;
648
649 return true;
650
651 } catch (...) {
652 return false;
653 }
654 }
655
656 void
set_dimensions(real_demuxer_cptr dmx,unsigned char * buffer,int size)657 real_reader_c::set_dimensions(real_demuxer_cptr dmx,
658 unsigned char *buffer,
659 int size) {
660 unsigned char *ptr = buffer;
661 ptr += 1 + 2 * 4 * (*ptr + 1);
662
663 if ((ptr + 10) >= (buffer + size))
664 return;
665
666 buffer = ptr;
667
668 uint32_t width, height;
669 if (!get_rv_dimensions(buffer, size, width, height))
670 return;
671
672 if ((dmx->width != width) || (dmx->height != height)) {
673 uint32_t disp_width = 0;
674 uint32_t disp_height = 0;
675
676 if (!m_ti.display_dimensions_or_aspect_ratio_set()) {
677 disp_width = dmx->width;
678 disp_height = dmx->height;
679
680 dmx->width = width;
681 dmx->height = height;
682
683 } else if (m_ti.m_display_dimensions_given) {
684 disp_width = m_ti.m_display_width;
685 disp_height = m_ti.m_display_height;
686
687 dmx->width = width;
688 dmx->height = height;
689
690 } else if (m_ti.m_aspect_ratio_given) {
691 dmx->width = width;
692 dmx->height = height;
693
694 if ((static_cast<double>(width) / height) < m_ti.m_aspect_ratio) {
695 disp_width = (uint32_t)(height * m_ti.m_aspect_ratio);
696 disp_height = height;
697
698 } else {
699 disp_width = width;
700 disp_height = (uint32_t)(width / m_ti.m_aspect_ratio);
701 }
702
703 }
704
705 auto video = GetChild<KaxTrackVideo>(*ptzr(dmx->ptzr).get_track_entry());
706 GetChild<KaxVideoPixelWidth>(video).SetValue(width);
707 GetChild<KaxVideoPixelHeight>(video).SetValue(height);
708
709 if ((0 != disp_width) && (0 != disp_height)) {
710 GetChild<KaxVideoDisplayWidth>(video).SetValue(disp_width);
711 GetChild<KaxVideoDisplayHeight>(video).SetValue(disp_height);
712 }
713
714 rerender_track_headers();
715 }
716
717 dmx->rv_dimensions = true;
718 }
719
720 void
get_information_from_data()721 real_reader_c::get_information_from_data() {
722 int64_t old_pos = m_in->getFilePointer();
723 bool information_found = true;
724
725 size_t i;
726 for (i = 0; i < demuxers.size(); i++) {
727 real_demuxer_cptr dmx = demuxers[i];
728 if (!strcasecmp(dmx->fourcc, "DNET")) {
729 dmx->bsid = -1;
730 information_found = false;
731 }
732 }
733
734 while (!information_found) {
735 rmff_frame_t *frame = rmff_read_next_frame(file, nullptr);
736 real_demuxer_cptr dmx = find_demuxer(frame->id);
737
738 if (!dmx) {
739 rmff_release_frame(frame);
740 continue;
741 }
742
743 if (!strcasecmp(dmx->fourcc, "DNET"))
744 dmx->bsid = frame->data[4] >> 3;
745
746 rmff_release_frame(frame);
747
748 information_found = true;
749 for (i = 0; i < demuxers.size(); i++) {
750 dmx = demuxers[i];
751 if (!strcasecmp(dmx->fourcc, "DNET") && (-1 == dmx->bsid))
752 information_found = false;
753
754 }
755 }
756
757 m_in->setFilePointer(old_pos);
758 file->num_packets_read = 0;
759 }
760
761 void
add_available_track_ids()762 real_reader_c::add_available_track_ids() {
763 size_t i;
764
765 for (i = 0; i < demuxers.size(); i++)
766 add_available_track_id(demuxers[i]->track->id);
767 }
768