Added a function for extracting the pixel width & height from the MPEG-4 part 2 bitstream. This is a workaround for buggy muxers writing wrong values to the source container, e.g. to MP4 files. Fixes bug 149.

This commit is contained in:
Moritz Bunkus 2005-09-18 11:10:56 +00:00
parent b1e6f2bd55
commit ceeced6ffc
7 changed files with 194 additions and 35 deletions

View File

@ -1,3 +1,11 @@
2005-09-18 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: new feature: MPEG-4 part 2 streams are searched for
the pixel width/height values. Those are taken if they differ from
those values in the source container. This is a work-around for
buggy muxers, e.g. broken video camera firmwares writing bad MP4
files. Fixes bug 149.
2005-09-15 Moritz Bunkus <moritz@bunkus.org>
* mkvmerge: bug fix: Appending files with RealVideo was

View File

@ -1012,6 +1012,18 @@ round_to_nearest_pow2(uint32_t value) {
return best_value;
}
int
int_log2(uint32_t value) {
uint32_t highest, bit, mask;
highest = 0;
for (mask = 1, bit = 0; 31 >= bit; mask <<= 1, ++bit)
if (value & mask)
highest = bit;
return highest;
}
bool
parse_int(const char *s,
int64_t &value) {

View File

@ -281,6 +281,8 @@ bool MTX_DLL_API parse_double(const char *s, double &value);
int MTX_DLL_API get_arg_len(const char *fmt, ...);
int MTX_DLL_API get_varg_len(const char *fmt, va_list ap);
int MTX_DLL_API int_log2(uint32_t value);
extern int MTX_DLL_API verbose;
#define foreach(it, vec) for (it = (vec).begin(); it != (vec).end(); it++)

View File

@ -26,17 +26,9 @@
#include "mm_io.h"
#include "mpeg4_common.h"
bool
mpeg4_p2_extract_par_internal(const unsigned char *buffer,
int buffer_size,
uint32_t &par_num,
uint32_t &par_den) {
const uint32_t ar_nums[16] = {0, 1, 12, 10, 16, 40, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
const uint32_t ar_dens[16] = {1, 1, 11, 11, 11, 33, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1};
uint32_t marker, aspect_ratio_info, num, den;
bit_cursor_c bits(buffer, buffer_size);
static bool
mpeg4_p2_find_vol_header(bit_cursor_c &bits) {
uint32_t marker;
while (!bits.eof()) {
marker = bits.peek_bits(32);
@ -52,37 +44,151 @@ mpeg4_p2_extract_par_internal(const unsigned char *buffer,
continue;
}
mxverb(2, "mpeg4 AR: found VOL header at %u\n",
bits.get_bit_position() / 8);
bits.skip_bits(32);
return true;
}
// VOL header
bits.skip_bits(1); // random access
bits.skip_bits(8); // vo_type
if (bits.get_bit()) { // is_old_id
bits.skip_bits(4); // vo_ver_id
bits.skip_bits(3); // vo_priority
return false;
}
static bool
mpeg4_p2_extract_size_internal(const unsigned char *buffer,
int buffer_size,
uint32_t &width,
uint32_t &height) {
bit_cursor_c bits(buffer, buffer_size);
int shape, time_base_den;
if (!mpeg4_p2_find_vol_header(bits))
return false;
mxverb(2, "mpeg4 size: found VOL header at %u\n",
bits.get_bit_position() / 8);
bits.skip_bits(32);
// VOL header
bits.skip_bits(1); // random access
bits.skip_bits(8); // vo_type
if (bits.get_bit()) { // is_old_id
bits.skip_bits(4); // vo_ver_id
bits.skip_bits(3); // vo_priority
}
if (15 == bits.get_bits(4)) // ASPECT_EXTENDED
bits.skip_bits(16);
if (1 == bits.get_bit()) { // vol control parameter
bits.skip_bits(2); // chroma format
bits.skip_bits(1); // low delay
if (1 == bits.get_bit()) { // vbv parameters
bits.skip_bits(15 + 1); // first half bitrate, marker
bits.skip_bits(15 + 1); // latter half bitrate, marker
bits.skip_bits(15 + 1); // first half vbv buffer size, marker
bits.skip_bits(3); // latter half vbv buffer size
bits.skip_bits(11 + 1); // first half vbv occupancy, marker
bits.skip_bits(15 + 1); // latter half vbv oocupancy, marker
}
}
aspect_ratio_info = bits.get_bits(4);
mxverb(2, "mpeg4 AR: aspect_ratio_info: %u\n", aspect_ratio_info);
if (aspect_ratio_info == 15) { // ASPECT_EXTENDED
num = bits.get_bits(8);
den = bits.get_bits(8);
} else {
num = ar_nums[aspect_ratio_info];
den = ar_dens[aspect_ratio_info];
}
mxverb(2, "mpeg4 AR: %u den: %u\n", num, den);
shape = bits.get_bits(2);
if (3 == shape) // GRAY_SHAPE
bits.skip_bits(4); // video object layer shape extension
if ((num != 0) && (den != 0) && ((num != 1) || (den != 1)) &&
(((float)num / (float)den) != 1.0)) {
par_num = num;
par_den = den;
bits.skip_bits(1); // marker
time_base_den = bits.get_bits(16); // time base den
bits.skip_bits(1); // marker
if (1 == bits.get_bit()) { // fixed vop rate
int time_increment_bits = int_log2(time_base_den - 1) + 1;
bits.skip_bits(time_increment_bits); // time base num
}
if (0 == shape) { // RECT_SHAPE
uint32_t tmp_width, tmp_height;
bits.skip_bits(1);
tmp_width = bits.get_bits(13);
bits.skip_bits(1);
tmp_height = bits.get_bits(13);
if ((0 != tmp_width) && (0 != tmp_height)) {
width = tmp_width;
height = tmp_height;
return true;
}
}
return false;
}
/** Extract the widht and height from a MPEG4 video frame
This function searches a buffer containing a MPEG4 video frame
for the width and height.
\param buffer The buffer containing the MPEG4 video frame.
\param buffer_size The size of the buffer in bytes.
\param width The width, if found, is stored in this variable.
\param height The height, if found, is stored in this variable.
\return \c true if width and height were found and \c false
otherwise.
*/
bool
mpeg4_p2_extract_size(const unsigned char *buffer,
int buffer_size,
uint32_t &width,
uint32_t &height) {
try {
return mpeg4_p2_extract_size_internal(buffer, buffer_size, width, height);
} catch (...) {
return false;
}
}
static bool
mpeg4_p2_extract_par_internal(const unsigned char *buffer,
int buffer_size,
uint32_t &par_num,
uint32_t &par_den) {
const uint32_t ar_nums[16] = {0, 1, 12, 10, 16, 40, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0};
const uint32_t ar_dens[16] = {1, 1, 11, 11, 11, 33, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1};
uint32_t aspect_ratio_info, num, den;
bit_cursor_c bits(buffer, buffer_size);
if (!mpeg4_p2_find_vol_header(bits))
return false;
mxverb(2, "mpeg4 AR: found VOL header at %u\n",
bits.get_bit_position() / 8);
bits.skip_bits(32);
// VOL header
bits.skip_bits(1); // random access
bits.skip_bits(8); // vo_type
if (bits.get_bit()) { // is_old_id
bits.skip_bits(4); // vo_ver_id
bits.skip_bits(3); // vo_priority
}
aspect_ratio_info = bits.get_bits(4);
mxverb(2, "mpeg4 AR: aspect_ratio_info: %u\n", aspect_ratio_info);
if (aspect_ratio_info == 15) { // ASPECT_EXTENDED
num = bits.get_bits(8);
den = bits.get_bits(8);
} else {
num = ar_nums[aspect_ratio_info];
den = ar_dens[aspect_ratio_info];
}
mxverb(2, "mpeg4 AR: %u den: %u\n", num, den);
if ((num != 0) && (den != 0) && ((num != 1) || (den != 1)) &&
(((float)num / (float)den) != 1.0)) {
par_num = num;
par_den = den;
return true;
}
return false;
}

View File

@ -141,6 +141,9 @@ struct video_frame_t {
bool MTX_DLL_API mpeg4_p2_extract_par(const unsigned char *buffer,
int buffer_size,
uint32_t &par_num, uint32_t &par_den);
bool MTX_DLL_API mpeg4_p2_extract_size(const unsigned char *buffer,
int buffer_size,
uint32_t &width, uint32_t &height);
void MTX_DLL_API mpeg4_p2_find_frame_types(const unsigned char *buffer,
int buffer_size,
vector<video_frame_t> &frames);

View File

@ -358,7 +358,8 @@ mpeg4_p2_video_packetizer_c(generic_reader_c *_reader,
video_packetizer_c(_reader, MKV_V_MPEG4_ASP, _fps, _width, _height, _ti),
timecodes_generated(0), last_i_p_frame(0), previous_timecode(0),
aspect_ratio_extracted(false), input_is_native(_input_is_native),
output_is_native(hack_engaged(ENGAGE_NATIVE_MPEG4)) {
output_is_native(hack_engaged(ENGAGE_NATIVE_MPEG4)),
size_extracted(false) {
if (input_is_native)
output_is_native = true;
@ -381,6 +382,8 @@ mpeg4_p2_video_packetizer_c(generic_reader_c *_reader,
int
mpeg4_p2_video_packetizer_c::process(packet_cptr packet) {
if (!size_extracted)
extract_size(packet->memory->data, packet->memory->size);
if (!aspect_ratio_extracted)
extract_aspect_ratio(packet->memory->data, packet->memory->size);
@ -671,6 +674,29 @@ mpeg4_p2_video_packetizer_c::extract_aspect_ratio(const unsigned char *buffer,
aspect_ratio_extracted = true;
}
void
mpeg4_p2_video_packetizer_c::extract_size(const unsigned char *buffer,
int size) {
uint32_t width, height;
if (mpeg4_p2_extract_size(buffer, size, width, height)) {
size_extracted = true;
if ((width != hvideo_pixel_width) || (height != hvideo_pixel_height)) {
set_video_pixel_width(width);
set_video_pixel_height(height);
generic_packetizer_c::set_headers();
rerender_track_headers();
mxinfo("Track %lld of '%s': The extracted values for video width and "
"height from the MPEG4 layer 2 video data bitstream differ from "
"what the values in the source container. The ones from the "
"video data bitstream (%ux%u) will be used.\n",
(int64_t)ti.id, ti.fname.c_str(), width, height);
}
} else if (50 <= frames_output)
aspect_ratio_extracted = true;
}
// ----------------------------------------------------------------
mpeg4_p10_video_packetizer_c::

View File

@ -80,6 +80,7 @@ protected:
deque<int64_t> available_timecodes, available_durations;
int64_t timecodes_generated, last_i_p_frame, previous_timecode;
bool aspect_ratio_extracted, input_is_native, output_is_native;
bool size_extracted;
public:
mpeg4_p2_video_packetizer_c(generic_reader_c *_reader,
@ -95,6 +96,7 @@ protected:
virtual void flush_frames_maybe(frame_type_e next_frame);
virtual void flush_frames(bool end_of_file);
virtual void extract_aspect_ratio(const unsigned char *buffer, int size);
virtual void extract_size(const unsigned char *buffer, int size);
virtual void fix_codec_string();
virtual void handle_missing_timecodes(bool end_of_file);
};