1 /** MPEG video helper functions (MPEG 1, 2 and 4)
2 
3    mkvmerge -- utility for splicing together matroska files
4    from component media subtypes
5 
6    Distributed under the GPL v2
7    see the file COPYING for details
8    or visit https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
9 
10    \file
11 
12    \author Written by Moritz Bunkus <moritz@bunkus.org>.
13 */
14 
15 #include "common/common_pch.h"
16 
17 #include "common/bit_reader.h"
18 #include "common/debugging.h"
19 #include "common/endian.h"
20 #include "common/math.h"
21 #include "common/mm_io.h"
22 #include "common/mm_mem_io.h"
23 #include "common/mpeg.h"
24 #include "common/mpeg4_p2.h"
25 
26 namespace mtx::mpeg4_p2 {
27 
28 namespace {
29 
30 debugging_option_c s_debug{"mpeg4_p2"};
31 
32 bool find_vol_header(mtx::bits::reader_c &bits);
33 bool parse_vol_header(const unsigned char *buffer, int buffer_size, config_data_t &config_data);
34 bool extract_par_internal(const unsigned char *buffer, int buffer_size, uint32_t &par_num, uint32_t &par_den);
35 void parse_frame(video_frame_t &frame, const unsigned char *buffer, const config_data_t &config_data);
36 
37 bool
find_vol_header(mtx::bits::reader_c & bits)38 find_vol_header(mtx::bits::reader_c &bits) {
39   uint32_t marker;
40 
41   while (!bits.eof()) {
42     marker = bits.peek_bits(32);
43 
44     if ((marker & 0xffffff00) != 0x00000100) {
45       bits.skip_bits(8);
46       continue;
47     }
48 
49     marker &= 0xff;
50     if ((marker < 0x20) || (marker > 0x2f)) {
51       bits.skip_bits(8);
52       continue;
53     }
54 
55     return true;
56   }
57 
58   return false;
59 }
60 
61 bool
parse_vol_header(unsigned char const * buffer,int buffer_size,config_data_t & config_data)62 parse_vol_header(unsigned char const *buffer,
63                  int buffer_size,
64                  config_data_t &config_data) {
65   mtx::bits::reader_c bits(buffer, buffer_size);
66 
67   if (!find_vol_header(bits))
68     return false;
69 
70   mxdebug_if(s_debug, fmt::format("mpeg4 size: found VOL header at {0}\n", bits.get_bit_position() / 8));
71   bits.skip_bits(32);
72 
73   // VOL header
74   bits.skip_bits(1);            // random access
75   bits.skip_bits(8);            // vo_type
76   if (bits.get_bit()) {         // is_old_id
77     bits.skip_bits(4);          // vo_ver_id
78     bits.skip_bits(3);          // vo_priority
79   }
80 
81   if (15 == bits.get_bits(4))   // ASPECT_EXTENDED
82     bits.skip_bits(16);
83 
84   if (1 == bits.get_bit()) {    // vol control parameter
85     bits.skip_bits(2);          // chroma format
86     bits.skip_bits(1);          // low delay
87     if (1 == bits.get_bit()) {  // vbv parameters
88       bits.skip_bits(15 + 1);   // first half bitrate, marker
89       bits.skip_bits(15 + 1);   // latter half bitrate, marker
90       bits.skip_bits(15 + 1);   // first half vbv buffer size, marker
91       bits.skip_bits(3);        // latter half vbv buffer size
92       bits.skip_bits(11 + 1);   // first half vbv occupancy, marker
93       bits.skip_bits(15 + 1);   // latter half vbv oocupancy, marker
94     }
95   }
96 
97   int shape = bits.get_bits(2);
98   if (3 == shape)               // GRAY_SHAPE
99     bits.skip_bits(4);          // video object layer shape extension
100 
101   bits.skip_bits(1);            // marker
102 
103   int time_base_den                 = bits.get_bits(16); // time base den
104   config_data.m_time_increment_bits = mtx::math::int_log2(time_base_den - 1) + 1;
105 
106   bits.skip_bits(1);            // marker
107   if (1 == bits.get_bit())      // fixed vop rate
108     bits.skip_bits(config_data.m_time_increment_bits); // time base num
109 
110   if (0 == shape) {             // RECT_SHAPE
111     uint32_t tmp_width, tmp_height;
112 
113     bits.skip_bits(1);
114     tmp_width = bits.get_bits(13);
115     bits.skip_bits(1);
116     tmp_height = bits.get_bits(13);
117 
118     if ((0 != tmp_width) && (0 != tmp_height)) {
119       config_data.m_width              = tmp_width;
120       config_data.m_height             = tmp_height;
121       config_data.m_width_height_found = true;
122     }
123   }
124 
125   return true;
126 }
127 
128 bool
extract_par_internal(unsigned char const * buffer,int buffer_size,uint32_t & par_num,uint32_t & par_den)129 extract_par_internal(unsigned char const *buffer,
130                      int buffer_size,
131                      uint32_t &par_num,
132                      uint32_t &par_den) {
133   const uint32_t ar_nums[16] = {0, 1, 12, 10, 16, 40, 0, 0, 0, 0,  0,  0,  0,  0, 0, 0};
134   const uint32_t ar_dens[16] = {1, 1, 11, 11, 11, 33, 1, 1, 1, 1,  1,  1,  1,  1, 1, 1};
135   uint32_t aspect_ratio_info, num, den;
136   mtx::bits::reader_c bits(buffer, buffer_size);
137 
138   if (!find_vol_header(bits))
139     return false;
140 
141   mxdebug_if(s_debug, fmt::format("mpeg4 AR: found VOL header at {0}\n", bits.get_bit_position() / 8));
142   bits.skip_bits(32);
143 
144   // VOL header
145   bits.skip_bits(1);            // random access
146   bits.skip_bits(8);            // vo_type
147   if (bits.get_bit()) {         // is_old_id
148     bits.skip_bits(4);          // vo_ver_id
149     bits.skip_bits(3);          // vo_priority
150   }
151 
152   aspect_ratio_info = bits.get_bits(4);
153   mxdebug_if(s_debug, fmt::format("mpeg4 AR: aspect_ratio_info: {0}\n", aspect_ratio_info));
154   if (aspect_ratio_info == 15) { // ASPECT_EXTENDED
155     num = bits.get_bits(8);
156     den = bits.get_bits(8);
157   } else {
158     num = ar_nums[aspect_ratio_info];
159     den = ar_dens[aspect_ratio_info];
160   }
161   mxdebug_if(s_debug, fmt::format("mpeg4 AR: {0} den: {1}\n", num, den));
162 
163   if ((num != 0) && (den != 0) && ((num != 1) || (den != 1)) &&
164       ((static_cast<double>(num) / den) != 1.0)) {
165     par_num = num;
166     par_den = den;
167     return true;
168   }
169   return false;
170 }
171 
172 void
parse_frame(video_frame_t & frame,unsigned char const * buffer,config_data_t const & config_data)173 parse_frame(video_frame_t &frame,
174             unsigned char const *buffer,
175             config_data_t const &config_data) {
176   static const frame_type_e s_frame_type_map[4] = { FRAME_TYPE_I, FRAME_TYPE_P, FRAME_TYPE_B, FRAME_TYPE_P };
177 
178   mtx::bits::reader_c bc(&buffer[ frame.pos + 4 ], frame.size);
179 
180   frame.type = s_frame_type_map[ bc.get_bits(2) ];
181 
182   while (bc.get_bit())
183     ;                           // modulo time base
184   bc.skip_bits(1 + config_data.m_time_increment_bits + 1); // marker, vop time increment, marker
185 
186   frame.is_coded = bc.get_bit();
187 }
188 
189 } // anonymous namespace
190 
config_data_t()191 config_data_t::config_data_t()
192   : m_time_increment_bits(0)
193   , m_width(0)
194   , m_height(0)
195   , m_width_height_found(false)
196 {
197 }
198 
199 /** Extract the widht and height from a MPEG4 video frame
200 
201    This function searches a buffer containing a MPEG4 video frame
202    for the width and height.
203 
204    \param buffer The buffer containing the MPEG4 video frame.
205    \param buffer_size The size of the buffer in bytes.
206    \param width The width, if found, is stored in this variable.
207    \param height The height, if found, is stored in this variable.
208 
209    \return \c true if width and height were found and \c false
210      otherwise.
211 */
212 bool
extract_size(unsigned char const * buffer,int buffer_size,uint32_t & width,uint32_t & height)213 extract_size(unsigned char const *buffer,
214              int buffer_size,
215              uint32_t &width,
216              uint32_t &height) {
217   try {
218     config_data_t config_data;
219     if (!parse_vol_header(buffer, buffer_size, config_data) || !config_data.m_width_height_found)
220       return false;
221 
222     width  = config_data.m_width;
223     height = config_data.m_height;
224 
225     return true;
226   } catch (...) {
227     return false;
228   }
229 }
230 
231 /** Extract the pixel aspect ratio from a MPEG4 video frame
232 
233    This function searches a buffer containing a MPEG4 video frame
234    for the pixel aspectc ratio. If it is found then the numerator
235    and the denominator are returned.
236 
237    \param buffer The buffer containing the MPEG4 video frame.
238    \param buffer_size The size of the buffer in bytes.
239    \param par_num The numerator, if found, is stored in this variable.
240    \param par_den The denominator, if found, is stored in this variable.
241 
242    \return \c true if the pixel aspect ratio was found and \c false
243      otherwise.
244 */
245 bool
extract_par(unsigned char const * buffer,int buffer_size,uint32_t & par_num,uint32_t & par_den)246 extract_par(unsigned char const *buffer,
247             int buffer_size,
248             uint32_t &par_num,
249             uint32_t &par_den) {
250   try {
251     return extract_par_internal(buffer, buffer_size, par_num, par_den);
252   } catch (...) {
253     return false;
254   }
255 }
256 
257 /** Find frame boundaries and frame types in a packed video frame
258 
259    This function searches a buffer containing one or more MPEG4 video frames
260    for the frame boundaries and their types. This may be the case for B frames
261    if they're glued to another frame like they are in AVI files.
262 
263    \param buffer The buffer containing the MPEG4 video frame(s).
264    \param buffer_size The size of the buffer in bytes.
265    \param frames The data for each frame that is found is put into this
266      variable. See ::video_frame_t
267 
268    \return Nothing. If no frames were found (e.g. only the dummy header for
269      a dummy frame) then \a frames will contain no elements.
270 */
271 void
find_frame_types(unsigned char const * buffer,int buffer_size,std::vector<video_frame_t> & frames,config_data_t const & config_data)272 find_frame_types(unsigned char const *buffer,
273                  int buffer_size,
274                  std::vector<video_frame_t> &frames,
275                  config_data_t const &config_data) {
276   frames.clear();
277   mxdebug_if(s_debug, fmt::format("\nmpeg4_frames: start search in {0} bytes\n", buffer_size));
278 
279   if (4 > buffer_size)
280     return;
281 
282   try {
283     mm_mem_io_c bytes(buffer, buffer_size);
284     uint32_t marker  = bytes.read_uint32_be();
285     bool frame_found = false;
286     video_frame_t frame;
287 
288     while (!bytes.eof()) {
289       if ((marker & 0xffffff00) != 0x00000100) {
290         marker <<= 8;
291         marker |= bytes.read_uint8();
292         continue;
293       }
294 
295       mxdebug_if(s_debug, fmt::format("mpeg4_frames:   found start code at {0}: 0x{1:02x}\n", bytes.getFilePointer() - 4, marker & 0xff));
296       if (marker == VOP_START_CODE) {
297         if (frame_found) {
298           frame.size = bytes.getFilePointer() - 4 - frame.pos;
299           parse_frame(frame, buffer, config_data);
300           frames.push_back(frame);
301         } else
302           frame_found = true;
303 
304         frame.pos = bytes.getFilePointer() - 4;
305       }
306 
307       if (4 > (buffer_size - bytes.getFilePointer()))
308         break;
309 
310       marker = bytes.read_uint32_be();
311     }
312 
313     if (frame_found) {
314       frame.size = buffer_size - frame.pos;
315       parse_frame(frame, buffer, config_data);
316       frames.push_back(frame);
317     }
318 
319   } catch(...) {
320   }
321 
322   if (s_debug) {
323     auto info = fmt::format("mpeg4_frames:   summary: found {0} frames ", frames.size());
324     std::vector<video_frame_t>::iterator fit;
325     for (fit = frames.begin(); fit < frames.end(); fit++) {
326       auto frame_type = FRAME_TYPE_I == fit->type ? 'I' : FRAME_TYPE_P == fit->type ? 'P' : 'B';
327       info += fmt::format("'{0}' (size {1} coded {3} at {2}) ", frame_type, fit->size, fit->pos, fit->is_coded);
328     }
329     mxdebug(info + "\n");
330   }
331 }
332 
333 /** Find MPEG-4 part 2 configuration data in a chunk of memory
334 
335    This function searches a buffer for MPEG-4 part 2 configuration data.
336    AVI files usually store this in front of every key frame. MP4 however
337    contains this only in the ESDS' decoder_config.
338 
339    \param buffer The buffer to be searched.
340    \param buffer_size The size of the buffer in bytes.
341    \param data_pos This is set to the start position of the configuration
342      data inside the \c buffer if such data was found.
343    \param data_pos This is set to the length of the configuration
344      data inside the \c buffer if such data was found.
345 
346    \return \c nullptr if no configuration data was found and a pointer to
347      a memory_c object otherwise. This object has to be deleted manually.
348 */
349 memory_cptr
parse_config_data(unsigned char const * buffer,int buffer_size,config_data_t & config_data)350 parse_config_data(unsigned char const *buffer,
351                   int buffer_size,
352                   config_data_t &config_data) {
353   if (5 > buffer_size)
354     return nullptr;
355 
356   mxdebug_if(s_debug, fmt::format("\nmpeg4_config_data: start search in {0} bytes\n", buffer_size));
357 
358   uint32_t marker          = get_uint32_be(buffer) >> 8;
359   const unsigned char *p   = buffer + 3;
360   const unsigned char *end = buffer + buffer_size;
361   int vos_offset           = -1;
362   int vol_offset           = -1;
363   unsigned int size        = 0;
364 
365   while (p < end) {
366     marker = (marker << 8) | *p;
367     ++p;
368     if (!mtx::mpeg::is_start_code(marker))
369       continue;
370 
371     mxdebug_if(s_debug, fmt::format("mpeg4_config_data:   found start code at {0}: 0x{1:02x}\n", static_cast<unsigned int>(p - buffer - 4), marker & 0xff));
372     if (VOS_START_CODE == marker)
373       vos_offset = p - 4 - buffer;
374 
375     else if (VOL_START_CODE == marker)
376       vol_offset = p - 4 - buffer;
377 
378     else if ((VOP_START_CODE == marker) || (GOP_START_CODE == marker)) {
379       size = p - 4 - buffer;
380       break;
381     }
382   }
383 
384   if (0 == size)
385     return nullptr;
386 
387   if (-1 != vol_offset)
388     try {
389       config_data_t cfg_data;
390       if (parse_vol_header(&buffer[ vol_offset ], buffer_size - vol_offset, cfg_data))
391         config_data.m_time_increment_bits = cfg_data.m_time_increment_bits;
392 
393     } catch (...) {
394     }
395 
396   memory_cptr mem;
397   if (-1 == vos_offset) {
398     mem                = memory_c::alloc(size + 5);
399     unsigned char *dst = mem->get_buffer();
400     put_uint32_be(dst, VOS_START_CODE);
401     dst[4] = 0xf5;
402     memcpy(dst + 5, buffer, size);
403 
404   } else {
405     mem                = memory_c::alloc(size);
406     unsigned char *dst = mem->get_buffer();
407     put_uint32_be(dst, VOS_START_CODE);
408     if (3 >= buffer[vos_offset + 4])
409       dst[4] = 0xf5;
410     else
411       dst[4] = buffer[vos_offset + 4];
412 
413     memcpy(dst + 5, buffer, vos_offset);
414     memcpy(dst + 5 + vos_offset, buffer + vos_offset + 5, size - vos_offset - 5);
415   }
416 
417   mxdebug_if(s_debug, fmt::format("mpeg4_config_data:   found GOOD config with size {0}\n", size));
418   return mem;
419 }
420 
421 /** \brief Check whether or not a FourCC refers to MPEG-4 part 2 video
422 
423    \param fourcc A pointer to a string with four characters
424 
425    \return true if the FourCC refers to a MPEG-4 part 2 video codec.
426 */
427 bool
is_fourcc(void const * fourcc)428 is_fourcc(void const *fourcc) {
429   static const char *mpeg4_p2_fourccs[] = {
430     "MP42", "DIV2", "DIVX", "XVID", "DX50", "FMP4", "DXGM",
431     nullptr
432   };
433   int i;
434 
435   for (i = 0; mpeg4_p2_fourccs[i]; ++i)
436     if (!strncasecmp((const char *)fourcc, mpeg4_p2_fourccs[i], 4))
437       return true;
438   return false;
439 }
440 
441 /** \brief Check whether or not a FourCC refers to MPEG-4 part 2
442     version 3 video
443 
444    \param fourcc A pointer to a string with four characters
445 
446    \return true if the FourCC refers to a MPEG-4 part 2 video codec.
447 */
448 bool
is_v3_fourcc(void const * fourcc)449 is_v3_fourcc(void const *fourcc) {
450   static const char *mpeg4_p2_v3_fourccs[] = {
451     "DIV3", "MPG3", "MP43",
452     "AP41", // Angel Potion
453     nullptr
454   };
455   int i;
456 
457   for (i = 0; mpeg4_p2_v3_fourccs[i]; ++i)
458     if (!strncasecmp((const char *)fourcc, mpeg4_p2_v3_fourccs[i], 4))
459       return true;
460   return false;
461 }
462 
463 } // namespace mtx::mpeg4_p2
464