mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-23 19:31:44 +00:00
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:
parent
b1e6f2bd55
commit
ceeced6ffc
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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++)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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::
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user