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