mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-25 04:11:44 +00:00
Implemented getting the pixel aspect ratio from MPEG-4 layer 10 (AVC) header data.
This commit is contained in:
parent
f6702eca8e
commit
1a0e346644
@ -21,6 +21,7 @@
|
||||
#include "os.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "mm_io.h"
|
||||
#include "mpeg4_common.h"
|
||||
|
||||
/** Extract the pixel aspect ratio from a MPEG4 video frame
|
||||
@ -175,239 +176,34 @@ mpeg4_find_frame_types(const unsigned char *buffer,
|
||||
}
|
||||
}
|
||||
|
||||
#define NAL_SPS 7
|
||||
static int64_t
|
||||
read_golomb_ue(bit_cursor_c &bits) {
|
||||
int i;
|
||||
int64_t value;
|
||||
|
||||
/** \brief identifies the exact end of the bitstream
|
||||
* @return the length of the trailing, or 0 if damaged
|
||||
*/
|
||||
static int
|
||||
decode_rbsp_trailing(const uint8_t *src) {
|
||||
int v;
|
||||
int r;
|
||||
|
||||
v = *src;
|
||||
for (r = 1; r < 9; r++) {
|
||||
if (v & 1)
|
||||
return r;
|
||||
v >>= 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** \brief Decodes a network abstraction layer unit.
|
||||
* @param consumed is the number of bytes used as input
|
||||
* @param length is the length of the array
|
||||
* @param dst_length is the number of decoded bytes
|
||||
* @returns decoded bytes, might be src+1 if no escapes
|
||||
*/
|
||||
static uint8_t *
|
||||
decode_nal(const uint8_t *src,
|
||||
int length,
|
||||
int &dst_length,
|
||||
int &consumed,
|
||||
int &nal_unit_type) {
|
||||
int i, si, di;
|
||||
uint8_t *dst;
|
||||
|
||||
nal_unit_type = src[0] & 0x1F;
|
||||
|
||||
src++;
|
||||
length--;
|
||||
for (i = 0; (i + 1) < length; i += 2) {
|
||||
if (src[i] != 0)
|
||||
continue;
|
||||
if ((i > 0) && (src[i - 1] == 0))
|
||||
i--;
|
||||
if (((i + 2) < length) && (src[i + 1] == 0) && (src[i + 2] <= 3)) {
|
||||
if (src[i + 2] != 3) {
|
||||
/* startcode, so we must be past the end */
|
||||
length = i;
|
||||
}
|
||||
i = 0;
|
||||
while (i < 64) {
|
||||
bool bit;
|
||||
if (!bits.get_bit(bit))
|
||||
throw false;
|
||||
if (bit)
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i >= (length - 1)) { //no escaped 0
|
||||
dst_length = length;
|
||||
consumed = length + 1; //+1 for the header
|
||||
return (uint8_t *)safememdup(src, length);
|
||||
}
|
||||
if (!bits.get_bits(i, value))
|
||||
throw false;
|
||||
|
||||
dst = (uint8_t *)safemalloc(length);
|
||||
|
||||
si = 0;
|
||||
di = 0;
|
||||
while (si < length) {
|
||||
//remove escapes (very rare 1:2^22)
|
||||
if (((si + 2) < length) && (src[si] == 0) && (src[si + 1] == 0) &&
|
||||
(src[si + 2] <= 3)) {
|
||||
if (src[si + 2] == 3) { //escape
|
||||
dst[di++] = 0;
|
||||
dst[di++] = 0;
|
||||
si += 3;
|
||||
continue;
|
||||
} else //next start code
|
||||
break;
|
||||
}
|
||||
|
||||
dst[di++] = src[si++];
|
||||
}
|
||||
|
||||
dst_length = di;
|
||||
consumed = si + 1; //+1 for the header
|
||||
return dst;
|
||||
return ((1 << i) - 1) + value;
|
||||
}
|
||||
|
||||
static const uint8_t golomb_vlc_len[512] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
|
||||
};
|
||||
static int64_t
|
||||
read_golomb_se(bit_cursor_c &bits) {
|
||||
int64_t value;
|
||||
|
||||
static int
|
||||
ilog2(uint32_t value) {
|
||||
int n;
|
||||
value = read_golomb_ue(bits);
|
||||
|
||||
n = 0;
|
||||
if (value & 0xffff0000) {
|
||||
value >>= 16;
|
||||
n += 16;
|
||||
}
|
||||
if (value & 0xff00) {
|
||||
value >>= 8;
|
||||
n += 8;
|
||||
}
|
||||
if (value >= 0x80)
|
||||
return n + 7;
|
||||
else if (value >= 0x40)
|
||||
return n + 6;
|
||||
else if (value >= 0x20)
|
||||
return n + 5;
|
||||
else if (value >= 0x10)
|
||||
return n + 4;
|
||||
else if (value >= 0x08)
|
||||
return n + 3;
|
||||
else if (value >= 0x04)
|
||||
return n + 2;
|
||||
else if (value >= 0x02)
|
||||
return n + 1;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool
|
||||
skip_golomb_ue(bit_cursor_c &bits) {
|
||||
uint32_t buf;
|
||||
bool ok;
|
||||
|
||||
ok = bits.get_bits(32, buf);
|
||||
if (buf >= (1 << 27)) {
|
||||
buf >>= 23;
|
||||
ok &= bits.skip_bits(golomb_vlc_len[buf]);
|
||||
} else {
|
||||
int log;
|
||||
|
||||
log = 2 * ilog2(buf) - 31;
|
||||
ok &= bits.skip_bits(32 - log);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_seq_parameter_set(bit_cursor_c &bits,
|
||||
uint32_t &par_num,
|
||||
uint32_t &par_den) {
|
||||
bool ok;
|
||||
|
||||
ok = true;
|
||||
ok &= bits.skip_bits(8); // profile_idc
|
||||
ok &= bits.skip_bits(4); // constraint_setX_flag
|
||||
ok &= bits.skip_bits(4); // reserved
|
||||
ok &= bits.skip_bits(8); // level_idc
|
||||
ok &= skip_golomb_ue(bits); // sps_id
|
||||
ok &= skip_golomb_ue(bits); // sps: log2_max_frame_num
|
||||
ok &= skip_golomb_ue(bits); // sps: poc_type
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
decode_nal_units(const uint8_t *buffer,
|
||||
int buf_size,
|
||||
uint32_t &par_num,
|
||||
uint32_t &par_den) {
|
||||
int nal_length_size;
|
||||
int buf_index;
|
||||
|
||||
nal_length_size = 2;
|
||||
buf_index = 0;
|
||||
|
||||
while (true) {
|
||||
int consumed;
|
||||
int dst_length;
|
||||
int bit_length;
|
||||
uint8_t *ptr;
|
||||
int i, nalsize = 0;
|
||||
int nal_unit_type;
|
||||
|
||||
if (buf_index >= buf_size)
|
||||
break;
|
||||
nalsize = 0;
|
||||
for (i = 0; i < nal_length_size; i++)
|
||||
nalsize = (nalsize << 8) | buffer[buf_index++];
|
||||
|
||||
nal_unit_type = -1;
|
||||
ptr = decode_nal(buffer + buf_index, buf_size - buf_index,
|
||||
dst_length, consumed, nal_unit_type);
|
||||
|
||||
if (ptr[dst_length - 1] == 0)
|
||||
dst_length--;
|
||||
bit_length = 8 * dst_length - decode_rbsp_trailing(ptr + dst_length - 1);
|
||||
|
||||
if (nal_unit_type == NAL_SPS) {
|
||||
bit_cursor_c bc(ptr, (bit_length + 7) / 8);
|
||||
bool result;
|
||||
|
||||
result = decode_seq_parameter_set(bc, par_num, par_den);
|
||||
safefree(ptr);
|
||||
return result;
|
||||
}
|
||||
|
||||
buf_index += consumed;
|
||||
safefree(ptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
return (value & 0x01) != 0x00 ? (value + 1) / 2 : -(value / 2);
|
||||
}
|
||||
|
||||
/** Extract the pixel aspect ratio from the MPEG4 layer 10 (AVC) codec data
|
||||
@ -429,18 +225,112 @@ mpeg4_l10_extract_par(const uint8_t *buffer,
|
||||
int buf_size,
|
||||
uint32_t &par_num,
|
||||
uint32_t &par_den) {
|
||||
const uint8_t *p;
|
||||
int cnt, i, nalsize;
|
||||
try {
|
||||
mm_mem_io_c avcc(buffer, buf_size);
|
||||
int num_sps, sps;
|
||||
|
||||
p = buffer;
|
||||
// Decode sps from avcC
|
||||
cnt = *(p + 5) & 0x1f; // Number of sps
|
||||
p += 6;
|
||||
for (i = 0; i < cnt; i++) {
|
||||
nalsize = get_uint16_be(p) + 2;
|
||||
if (decode_nal_units(p, nalsize, par_num, par_den))
|
||||
return true;
|
||||
p += nalsize;
|
||||
avcc.skip(5);
|
||||
num_sps = avcc.read_uint8();
|
||||
mxverb(4, "mpeg4_l10_extract_par: num_sps %d\n", num_sps);
|
||||
|
||||
for (sps = 0; sps < num_sps; sps++) {
|
||||
int length, poc_type, ar_type, nal_unit_type;
|
||||
bool ok, flag;
|
||||
|
||||
length = avcc.read_uint16_be();
|
||||
bit_cursor_c bits(&buffer[avcc.getFilePointer()],
|
||||
(length + avcc.getFilePointer()) > buf_size ?
|
||||
buf_size - avcc.getFilePointer() : length);
|
||||
ok = bits.get_bits(8, nal_unit_type);
|
||||
if (!ok)
|
||||
throw false;
|
||||
nal_unit_type &= 0x1f;
|
||||
mxverb(4, "mpeg4_l10_extract_par: nal_unit_type %d\n", nal_unit_type);
|
||||
if (nal_unit_type != 7) // 7 = SPS
|
||||
continue;
|
||||
|
||||
ok &= bits.skip_bits(16);
|
||||
read_golomb_ue(bits);
|
||||
read_golomb_ue(bits);
|
||||
poc_type = read_golomb_ue(bits);
|
||||
|
||||
if (poc_type > 1) {
|
||||
mxverb(4, "mpeg4_l10_extract_par: poc_type %d\n", poc_type);
|
||||
throw false;
|
||||
}
|
||||
|
||||
if (poc_type == 0)
|
||||
read_golomb_ue(bits);
|
||||
else {
|
||||
int cycles, i;
|
||||
|
||||
bits.skip_bits(1);
|
||||
read_golomb_se(bits);
|
||||
read_golomb_se(bits);
|
||||
cycles = read_golomb_ue(bits);
|
||||
for (i = 0; i < cycles; ++i)
|
||||
read_golomb_se(bits);
|
||||
}
|
||||
|
||||
read_golomb_ue(bits);
|
||||
ok &= bits.skip_bits(1);
|
||||
read_golomb_ue(bits);
|
||||
read_golomb_ue(bits);
|
||||
ok &= bits.get_bit(flag); // MB only
|
||||
if (!flag)
|
||||
ok &= bits.skip_bits(1);
|
||||
ok &= bits.skip_bits(1);
|
||||
ok &= bits.get_bit(flag); // cropping
|
||||
if (flag) {
|
||||
read_golomb_ue(bits);
|
||||
read_golomb_ue(bits);
|
||||
read_golomb_ue(bits);
|
||||
read_golomb_ue(bits);
|
||||
}
|
||||
ok &= bits.get_bit(flag); // VUI
|
||||
if (!flag) {
|
||||
mxverb(4, "mpeg4_l10_extract_par: !VUI\n");
|
||||
throw false;
|
||||
}
|
||||
ok &= bits.get_bit(flag); // AR
|
||||
if (!flag) {
|
||||
mxverb(4, "mpeg4_l10_extract_par: !AR\n");
|
||||
throw false;
|
||||
}
|
||||
|
||||
ok &= bits.get_bits(8, ar_type);
|
||||
if (!ok) {
|
||||
mxverb(4, "mpeg4_l10_extract_par: no ar_type\n");
|
||||
throw false;
|
||||
}
|
||||
if ((ar_type != 0xff) && // custom AR
|
||||
(ar_type > 13)) {
|
||||
mxverb(4, "mpeg4_l10_extract_par: wrong ar_type %d\n", ar_type);
|
||||
throw false;
|
||||
}
|
||||
|
||||
if (ar_type <= 13) {
|
||||
static const int par_nums[14] = {
|
||||
0, 1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160
|
||||
};
|
||||
static const int par_denoms[14] = {
|
||||
0, 1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99
|
||||
};
|
||||
|
||||
par_num = par_nums[ar_type];
|
||||
par_den = par_denoms[ar_type];
|
||||
mxverb(4, "mpeg4_l10_extract_par: ar_type %d num %d den %d\n",
|
||||
ar_type, par_num, par_den);
|
||||
return true;
|
||||
}
|
||||
|
||||
ok &= bits.get_bits(16, par_num);
|
||||
ok &= bits.get_bits(16, par_den);
|
||||
mxverb(4, "mpeg4_l10_extract_par: ar_type %d ok %d num %d den %d\n",
|
||||
ar_type, ok, par_num, par_den);
|
||||
return ok;
|
||||
} // for
|
||||
} catch(...) {
|
||||
}
|
||||
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user