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