Improved the support for native MPEG-4 (it actually works for three files now). Renamed a couple of function parameters in the process and moved the memory_c class into the "common" subdir.

This commit is contained in:
Moritz Bunkus 2005-05-02 12:36:51 +00:00
parent 4ea7287e08
commit b53de99588
5 changed files with 184 additions and 143 deletions

View File

@ -0,0 +1,66 @@
/*
mkvmerge -- utility for splicing together matroska files
from component media subtypes
Distributed under the GPL
see the file COPYING for details
or visit http://www.gnu.org/copyleft/gpl.html
$Id$
class definition for memory handling classes
Written by Moritz Bunkus <moritz@bunkus.org>.
*/
#ifndef __COMMON_MEMORY_H
#define __COMMON_MEMORY_H
#include "os.h"
#include <vector>
#include "common.h"
class MTX_DLL_API memory_c {
public:
unsigned char *data;
uint32_t size;
bool is_free;
public:
memory_c(unsigned char *ndata, uint32_t nsize, bool nis_free):
data(ndata), size(nsize), is_free(nis_free) {
if (data == NULL)
die("memory_c::memory_c: data = %p, size = %u\n", data, size);
}
memory_c(const memory_c &src) {
die("memory_c::memory_c(const memory_c &) called\n");
}
~memory_c() {
release();
}
int lock() {
is_free = false;
return 0;
}
unsigned char *grab() {
if (is_free) {
is_free = false;
return data;
}
return (unsigned char *)safememdup(data, size);
}
int release() {
if (is_free) {
safefree(data);
data = NULL;
is_free = false;
}
return 0;
}
};
typedef std::vector<memory_c *> memories_c;
#endif // __COMMON_MEMORY_H

View File

@ -32,7 +32,7 @@
and the denominator are returned.
\param buffer The buffer containing the MPEG4 video frame.
\param size The size of the buffer in bytes.
\param buffer_size The size of the buffer in bytes.
\param par_num The numerator, if found, is stored in this variable.
\param par_den The denominator, if found, is stored in this variable.
@ -41,7 +41,7 @@
*/
bool
mpeg4_p2_extract_par(const unsigned char *buffer,
int size,
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,
@ -50,7 +50,7 @@ mpeg4_p2_extract_par(const unsigned char *buffer,
1, 1, 1, 1, 1, 1, 1, 1};
uint32_t marker, aspect_ratio_info, num, den;
bool b;
bit_cursor_c bits(buffer, size);
bit_cursor_c bits(buffer, buffer_size);
while (!bits.eof()) {
if (!bits.peek_bits(32, marker))
@ -110,7 +110,7 @@ mpeg4_p2_extract_par(const unsigned char *buffer,
if they're glued to another frame like they are in AVI files.
\param buffer The buffer containing the MPEG4 video frame(s).
\param size The size of the buffer in bytes.
\param buffer_size The size of the buffer in bytes.
\param frames The data for each frame that is found is put into this
variable. See ::video_frame_t
@ -119,24 +119,22 @@ mpeg4_p2_extract_par(const unsigned char *buffer,
*/
void
mpeg4_p2_find_frame_types(const unsigned char *buffer,
int size,
int buffer_size,
vector<video_frame_t> &frames) {
mm_mem_io_c bytes(buffer, size);
mm_mem_io_c bytes(buffer, buffer_size);
uint32_t marker, frame_type;
int first_frame_start;
bool first_frame;
bool frame_found;
video_frame_t frame;
vector<video_frame_t>::iterator fit;
frames.clear();
mxverb(3, "\nmpeg4_frames: start search in %d bytes\n", size);
mxverb(3, "\nmpeg4_frames: start search in %d bytes\n", buffer_size);
if (4 > size)
if (4 > buffer_size)
return;
frame.pos = 0;
first_frame = true;
first_frame_start = 0;
frame_found = false;
try {
marker = bytes.read_uint32_be();
@ -147,37 +145,26 @@ mpeg4_p2_find_frame_types(const unsigned char *buffer,
continue;
}
mxverb(3, "mpeg4_frames: found start code at %lld\n",
bytes.getFilePointer() - 4);
if (marker == MPEGVIDEO_OBJECT_PLAIN_START_CODE) {
if (0 > first_frame_start)
first_frame_start = bytes.getFilePointer() - 4;
frame_type = bytes.read_uint8() >> 6;
if (!first_frame) {
frame.size = bytes.getFilePointer() - 5 - frame.pos;
mxverb(3, "mpeg4_frames: found start code at %lld: 0x%02x\n",
bytes.getFilePointer() - 4, marker & 0xff);
if (marker == MPEGVIDEO_VOP_START_CODE) {
if (frame_found) {
frame.size = bytes.getFilePointer() - 4 - frame.pos;
frames.push_back(frame);
frame.pos = bytes.getFilePointer() - 5;
} else {
first_frame = false;
frame.pos = first_frame_start;
}
} else
frame_found = true;
frame.pos = bytes.getFilePointer() - 4;
frame_type = bytes.read_uint8() >> 6;
frame.type = 0 == frame_type ? FRAME_TYPE_I :
2 == frame_type ? FRAME_TYPE_B : FRAME_TYPE_P;
} else if (first_frame &&
((MPEGVIDEO_VOS_START_CODE == marker) ||
(MPEGVIDEO_VISUAL_OBJECT_START_CODE == marker) ||
(0x00000140 > marker)))
first_frame_start = -1;
else
first_frame_start = bytes.getFilePointer() - 4;
}
marker = bytes.read_uint32_be();
}
if (!first_frame) {
frame.size = size - frame.pos;
if (frame_found) {
frame.size = buffer_size - frame.pos;
frames.push_back(frame);
}
@ -208,31 +195,35 @@ mpeg4_p2_find_frame_types(const unsigned char *buffer,
contains this only in the ESDS' decoder_config.
\param buffer The buffer to be searched.
\param size The size of the buffer in bytes.
\param buffer_size The size of the buffer in bytes.
\param data_pos This is set to the start position of the configuration
data inside the \c buffer if such data was found.
\param data_pos This is set to the length of the configuration
data inside the \c buffer if such data was found.
\return \c true if such configuration data was found and \c false
otherwise.
\return \c NULL if no configuration data was found and a pointer to
a memory_c object otherwise. This object has to be deleted manually.
*/
bool
mpeg4_p2_find_config_data(const unsigned char *buffer,
int size,
uint32_t &data_pos,
uint32_t &data_size) {
memory_c *
mpeg4_p2_parse_config_data(const unsigned char *buffer,
int buffer_size) {
memory_c *mem;
const unsigned char *p, *end;
uint32_t marker;
bool start_found;
unsigned char *dst;
uint32_t marker, size;
int vos_offset;
if (buffer_size < 5)
return NULL;
mxverb(3, "\nmpeg4_config_data: start search in %d bytes\n", buffer_size);
if (size < 5)
return false;
start_found = false;
mxverb(3, "\nmpeg4_config_data: start search in %d bytes\n", size);
marker = get_uint32_be(buffer) >> 8;
p = buffer + 3;
end = buffer + size;
end = buffer + buffer_size;
vos_offset = -1;
size = 0;
while (p < end) {
marker = (marker << 8) | *p;
++p;
@ -240,27 +231,41 @@ mpeg4_p2_find_config_data(const unsigned char *buffer,
continue;
mxverb(3, "mpeg4_config_data: found start code at %d: 0x%02x\n",
p - buffer, marker & 0xff);
if (MPEGVIDEO_VOS_START_CODE == marker) {
start_found = true;
data_pos = p - 4 - buffer;
} else if (start_found &&
(MPEGVIDEO_VISUAL_OBJECT_START_CODE != marker) &&
(0x00000140 < marker)) {
p -= 4;
p - buffer - 4, marker & 0xff);
if (MPEGVIDEO_VOS_START_CODE == marker)
vos_offset = p - 4 - buffer;
else if ((MPEGVIDEO_VOP_START_CODE == marker) ||
(MPEGVIDEO_GOP_START_CODE == marker)) {
size = p - 4 - buffer;
break;
}
}
if (start_found) {
data_size = p - buffer - data_pos;
mxverb(3, "mpeg4_config_data: found GOOD config at %u with size %u\n",
data_pos, data_size);
return true;
if (0 == size)
return NULL;
if (-1 == vos_offset) {
mem = new memory_c((unsigned char *)safemalloc(size + 5), size + 5, true);
dst = mem->data;
put_uint32_be(dst, MPEGVIDEO_VOS_START_CODE);
dst[4] = 0xf5;
memcpy(dst + 5, buffer, size);
} else {
mem = new memory_c((unsigned char *)safemalloc(size), size, true);
dst = mem->data;
put_uint32_be(dst, MPEGVIDEO_VOS_START_CODE);
if (3 >= buffer[vos_offset + 4])
dst[4] = 0xf5;
else
dst[4] = buffer[vos_offset + 4];
memcpy(dst + 5, buffer, vos_offset);
memcpy(dst + 5 + vos_offset, buffer + vos_offset + 5,
size - vos_offset - 5);
}
return false;
mxverb(3, "mpeg4_config_data: found GOOD config with size %u\n", size);
return mem;
}
static int64_t
@ -300,7 +305,7 @@ read_golomb_se(bit_cursor_c &bits) {
numerator and the denominator are returned.
\param buffer The buffer containing the MPEG4 layer 10 codec data.
\param size The size of the buffer in bytes.
\param buffer_size The size of the buffer in bytes.
\param par_num The numerator, if found, is stored in this variable.
\param par_den The denominator, if found, is stored in this variable.
@ -309,11 +314,11 @@ read_golomb_se(bit_cursor_c &bits) {
*/
bool
mpeg4_p10_extract_par(const uint8_t *buffer,
int buf_size,
int buffer_size,
uint32_t &par_num,
uint32_t &par_den) {
try {
mm_mem_io_c avcc(buffer, buf_size);
mm_mem_io_c avcc(buffer, buffer_size);
int num_sps, sps;
avcc.skip(5);
@ -326,8 +331,8 @@ mpeg4_p10_extract_par(const uint8_t *buffer,
length = avcc.read_uint16_be();
bit_cursor_c bits(&buffer[avcc.getFilePointer()],
(length + avcc.getFilePointer()) > buf_size ?
buf_size - avcc.getFilePointer() : length);
(length + avcc.getFilePointer()) > buffer_size ?
buffer_size - avcc.getFilePointer() : length);
ok = bits.get_bits(8, nal_unit_type);
if (!ok)
throw false;
@ -432,30 +437,30 @@ mpeg4_p10_extract_par(const uint8_t *buffer,
::mpeg_video_get_fps
\param buffer The buffer to search for the header.
\param size The buffer size.
\param buffer_size The buffer size.
\return The index or \c -1 if no MPEG sequence header was found or
if the buffer was too small.
*/
int
mpeg1_2_extract_fps_idx(const unsigned char *buffer,
int size) {
int buffer_size) {
uint32_t marker;
int idx;
mxverb(3, "mpeg_video_fps: start search in %d bytes\n", size);
if (size < 8) {
mxverb(3, "mpeg_video_fps: start search in %d bytes\n", buffer_size);
if (buffer_size < 8) {
mxverb(3, "mpeg_video_fps: sequence header too small\n");
return -1;
}
marker = get_uint32_be(buffer);
idx = 4;
while ((idx < size) && (marker != MPEGVIDEO_SEQUENCE_START_CODE)) {
while ((idx < buffer_size) && (marker != MPEGVIDEO_SEQUENCE_START_CODE)) {
marker <<= 8;
marker |= buffer[idx];
idx++;
}
if (idx >= size) {
if (idx >= buffer_size) {
mxverb(3, "mpeg_video_fps: no sequence header start code found\n");
return -1;
}
@ -463,7 +468,7 @@ mpeg1_2_extract_fps_idx(const unsigned char *buffer,
mxverb(3, "mpeg_video_fps: found sequence header start code at %d\n",
idx - 4);
idx += 3; // width and height
if (idx >= size) {
if (idx >= buffer_size) {
mxverb(3, "mpeg_video_fps: sequence header too small\n");
return -1;
}
@ -477,30 +482,30 @@ mpeg1_2_extract_fps_idx(const unsigned char *buffer,
aspect ratio is extracted and returned.
\param buffer The buffer to search for the header.
\param size The buffer size.
\param buffer_size The buffer size.
\return \c true if a MPEG sequence header was found and \c false otherwise.
*/
bool
mpeg1_2_extract_ar(const unsigned char *buffer,
int size,
int buffer_size,
float &ar) {
uint32_t marker;
int idx;
mxverb(3, "mpeg_video_ar: start search in %d bytes\n", size);
if (size < 8) {
mxverb(3, "mpeg_video_ar: start search in %d bytes\n", buffer_size);
if (buffer_size < 8) {
mxverb(3, "mpeg_video_ar: sequence header too small\n");
return -1;
}
marker = get_uint32_be(buffer);
idx = 4;
while ((idx < size) && (marker != MPEGVIDEO_SEQUENCE_START_CODE)) {
while ((idx < buffer_size) && (marker != MPEGVIDEO_SEQUENCE_START_CODE)) {
marker <<= 8;
marker |= buffer[idx];
idx++;
}
if (idx >= size) {
if (idx >= buffer_size) {
mxverb(3, "mpeg_video_ar: no sequence header start code found\n");
return -1;
}
@ -508,7 +513,7 @@ mpeg1_2_extract_ar(const unsigned char *buffer,
mxverb(3, "mpeg_video_ar: found sequence header start code at %d\n",
idx - 4);
idx += 3; // width and height
if (idx >= size) {
if (idx >= buffer_size) {
mxverb(3, "mpeg_video_ar: sequence header too small\n");
return -1;
}

View File

@ -16,9 +16,13 @@
#ifndef __MPEG4_COMMON_H
#define __MPEG4_COMMON_H
/** Start code for a MPEG4 (?) video object plain */
#define MPEGVIDEO_OBJECT_PLAIN_START_CODE 0x000001b6
/** Start code for a MPEG-1 and -2 sequence header */
#include "common_memory.h"
/** Start code for a MPEG-4 part 2 (?) video object plain */
#define MPEGVIDEO_VOP_START_CODE 0x000001b6
/** Strt code for a MPEG-4 part 2 group of pictures */
#define MPEGVIDEO_GOP_START_CODE 0x000001b3
/** Start code for a MPEG-1/-2 sequence header */
#define MPEGVIDEO_SEQUENCE_START_CODE 0x000001b3
/** Start code for a MPEG-1 and -2 packet */
#define MPEGVIDEO_PACKET_START_CODE 0x000001ba
@ -134,22 +138,22 @@ struct video_frame_t {
timecode(0), duration(0), bref(0), fref(0) {};
};
bool MTX_DLL_API mpeg4_p2_extract_par(const unsigned char *buffer, int size,
bool MTX_DLL_API mpeg4_p2_extract_par(const unsigned char *buffer,
int buffer_size,
uint32_t &par_num, uint32_t &par_den);
void MTX_DLL_API mpeg4_p2_find_frame_types(const unsigned char *buffer,
int size,
int buffer_size,
vector<video_frame_t> &frames);
bool MTX_DLL_API mpeg4_p2_find_config_data(const unsigned char *buffer,
int size, uint32_t &data_pos,
uint32_t &data_size);
memory_c * MTX_DLL_API
mpeg4_p2_parse_config_data(const unsigned char *buffer, int buffer_size);
bool MTX_DLL_API mpeg4_p10_extract_par(const uint8_t *buffer, int buf_size,
bool MTX_DLL_API mpeg4_p10_extract_par(const uint8_t *buffer, int buffer_size,
uint32_t &par_num, uint32_t &par_den);
int MTX_DLL_API mpeg1_2_extract_fps_idx(const unsigned char *buffer,
int size);
int buffer_size);
double MTX_DLL_API mpeg1_2_get_fps(int idx);
bool MTX_DLL_API mpeg1_2_extract_ar(const unsigned char *buffer, int size,
float &ar);
bool MTX_DLL_API mpeg1_2_extract_ar(const unsigned char *buffer,
int buffer_size, float &ar);
#endif /* __MPEG4_COMMON_H */

View File

@ -29,6 +29,7 @@
#include <matroska/KaxTracks.h>
#include <matroska/KaxTags.h>
#include "common_memory.h"
#include "compression.h"
#include "error.h"
#include "mm_io.h"
@ -67,47 +68,6 @@ enum file_status_e {
FILE_STATUS_MOREDATA
};
class memory_c {
public:
unsigned char *data;
uint32_t size;
bool is_free;
public:
memory_c(unsigned char *ndata, uint32_t nsize, bool nis_free):
data(ndata), size(nsize), is_free(nis_free) {
if (data == NULL)
die("memory_c::memory_c: data = %p, size = %u\n", data, size);
}
memory_c(const memory_c &src) {
die("memory_c::memory_c(const memory_c &) called\n");
}
~memory_c() {
release();
}
int lock() {
is_free = false;
return 0;
}
unsigned char *grab() {
if (is_free) {
is_free = false;
return data;
}
return (unsigned char *)safememdup(data, size);
}
int release() {
if (is_free) {
safefree(data);
data = NULL;
is_free = false;
}
return 0;
}
};
typedef std::vector<memory_c *> memories_c;
struct audio_sync_t {
int64_t displacement;
double linear;

View File

@ -358,14 +358,20 @@ mpeg4_p2_video_packetizer_c::process_non_native(memory_c &mem,
vector<video_frame_t>::iterator frame;
if (NULL == ti.private_data) {
uint32_t pos, size;
memory_c *config_data;
if (mpeg4_p2_find_config_data(mem.data, mem.size, pos, size)) {
ti.private_data = (unsigned char *)safememdup(&mem.data[pos], size);
ti.private_size = size;
config_data = mpeg4_p2_parse_config_data(mem.data, mem.size);
if (NULL != config_data) {
ti.private_data = config_data->grab();
ti.private_size = config_data->size;
delete config_data;
set_codec_private(ti.private_data, ti.private_size);
rerender_track_headers();
}
} else
mxerror("Could not find the codec configuration data in the first "
"MPEG-4 part 2 video frame. This track cannot be stored in "
"native mode.\n");
}
mpeg4_p2_find_frame_types(mem.data, mem.size, frames);