Moved the MPEG-4 handling from the video_packetizer_c into two new packetizers for MPEG-4 layer 2 and MPEG-4 layer 10. Added functions to the MPEG-1/-2 packetizer to extract the aspect ratio and to create the codec private data in case the reader does not supply those (like the Qt/MP4 reader).

This commit is contained in:
Moritz Bunkus 2005-01-16 10:58:49 +00:00
parent 8e10873a44
commit 3e33089390
10 changed files with 548 additions and 319 deletions

View File

@ -383,6 +383,68 @@ mpeg1_2_extract_fps_idx(const unsigned char *buffer,
return buffer[idx] & 0x0f;
}
/** \brief Extract the aspect ratio from a MPEG video sequence header
This function looks for a MPEG sequence header in a buffer containing
a MPEG1 or MPEG2 video frame. If such a header is found its
aspect ratio is extracted and returned.
\param buffer The buffer to search for the header.
\param 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,
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: sequence header too small\n");
return -1;
}
marker = get_uint32_be(buffer);
idx = 4;
while ((idx < size) && (marker != MPEGVIDEO_SEQUENCE_START_CODE)) {
marker <<= 8;
marker |= buffer[idx];
idx++;
}
if (idx >= size) {
mxverb(3, "mpeg_video_ar: no sequence header start code found\n");
return -1;
}
mxverb(3, "mpeg_video_ar: found sequence header start code at %d\n",
idx - 4);
idx += 3; // width and height
if (idx >= size) {
mxverb(3, "mpeg_video_ar: sequence header too small\n");
return -1;
}
switch (buffer[idx] & 0xf0) {
case MPEGVIDEO_AR_1_1:
ar = 1.0f;
break;
case MPEGVIDEO_AR_4_3:
ar = 4.0f / 3.0f;
break;
case MPEGVIDEO_AR_16_9:
ar = 16.0f / 9.0f;
break;
case MPEGVIDEO_AR_2_21:
ar = 2.21f;
break;
default:
ar = -1.0f;
}
return true;
}
/** \brief Get the number of frames per second
Converts the index returned by ::mpeg_video_extract_fps_idx to a number.

View File

@ -56,6 +56,22 @@
/** MPEG-1/-2 frame rate: 60 frames per second */
#define MPEGVIDEO_FPS_60 0x08
/** MPEG-1/-2 aspect ratio 1:1 */
#define MPEGVIDEO_AR_1_1 0x10
/** MPEG-1/-2 aspect ratio 4:3 */
#define MPEGVIDEO_AR_4_3 0x20
/** MPEG-1/-2 aspect ratio 16:9 */
#define MPEGVIDEO_AR_16_9 0x30
/** MPEG-1/-2 aspect ratio 2.21 */
#define MPEGVIDEO_AR_2_21 0x40
#define IS_MPEG4_L2_FOURCC(s) \
(!strncasecmp((s), "DIVX", 4) || !strncasecmp((s), "XVID", 4) || \
!strncasecmp((s), "DX5", 3))
#define IS_MPEG4_L2_CODECID(s) \
(((s) == MKV_V_MPEG4_SP) || ((s) == MKV_V_MPEG4_AP) || \
((s) == MKV_V_MPEG4_ASP))
enum mpeg_video_type_e {
MPEG_VIDEO_NONE = 0,
MPEG_VIDEO_V1,
@ -73,7 +89,7 @@ enum mpeg_video_type_e {
such packed frames can be analyzed. The results are stored in these
structures: one structure for one frame in the analyzed chunk.
*/
typedef struct {
struct video_frame_t {
/** The beginning of the frame data. This is a pointer into an existing
buffer handed over to ::mpeg4_find_frame_types. */
unsigned char *data;
@ -97,7 +113,11 @@ typedef struct {
\link video_frame_t::timecode timecode \endlink.
This value is only set for B frames. */
int64_t fref;
} video_frame_t;
video_frame_t():
data(NULL), size(0), pos(0), type('?'), priv(NULL),
timecode(0), duration(0), bref(0), fref(0) {};
};
bool MTX_DLL_API mpeg4_extract_par(const unsigned char *buffer, int size,
uint32_t &par_num, uint32_t &par_den);
@ -110,5 +130,7 @@ bool MTX_DLL_API mpeg4_l10_extract_par(const uint8_t *buffer, int buf_size,
int MTX_DLL_API mpeg1_2_extract_fps_idx(const unsigned char *buffer,
int 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);
#endif /* __MPEG4_COMMON_H */

View File

@ -172,14 +172,27 @@ avi_reader_c::create_packetizer(int64_t tid) {
if (ti->private_data != NULL)
ti->private_size = get_uint32_le(&avi->bitmap_info_header->bi_size);
ti->id = 0; // ID for the video track.
vptzr = add_packetizer(new video_packetizer_c(this, NULL,
AVI_frame_rate(avi),
AVI_video_width(avi),
AVI_video_height(avi),
false, ti));
if (verbose)
mxinfo(FMT_TID "Using the video output module for the video track.\n",
ti->fname.c_str(), (int64_t)0);
if (is_divx == RAVI_MPEG4) {
vptzr =
add_packetizer(new mpeg4_l2_video_packetizer_c(this,
AVI_frame_rate(avi),
AVI_video_width(avi),
AVI_video_height(avi),
false,
ti));
if (verbose)
mxinfo(FMT_TID "Using the MPEG-4 layer 2 video output module for "
"this track.\n", ti->fname.c_str(), (int64_t)0);
} else {
vptzr = add_packetizer(new video_packetizer_c(this, NULL,
AVI_frame_rate(avi),
AVI_video_width(avi),
AVI_video_height(avi),
ti));
if (verbose)
mxinfo(FMT_TID "Using the video output module for the video track.\n",
ti->fname.c_str(), (int64_t)0);
}
}
if (tid == 0)
return;

View File

@ -1591,15 +1591,72 @@ kax_reader_c::create_packetizer(int64_t tid) {
if (starts_with(t->codec_id, "V_MPEG4", 7) ||
(t->codec_id == MKV_V_MSCOMP) ||
starts_with(t->codec_id, "V_REAL", 6) ||
(t->codec_id == MKV_V_QUICKTIME)) {
mxinfo(FMT_TID "Using the video output module.\n", ti->fname.c_str(),
(int64_t)t->tnum);
t->ptzr = add_packetizer(new video_packetizer_c(this,
t->codec_id.c_str(),
t->v_frate,
t->v_width,
t->v_height,
t->v_bframes, nti));
(t->codec_id == MKV_V_QUICKTIME) ||
(t->codec_id == MKV_V_MPEG1) ||
(t->codec_id == MKV_V_MPEG2)) {
const char *fourcc;
if ((t->codec_id == MKV_V_MSCOMP) &&
(t->private_data != NULL) &&
(t->private_size >= sizeof(alBITMAPINFOHEADER)))
fourcc = (const char *)
&((alBITMAPINFOHEADER *)t->private_data)->bi_compression;
else
fourcc = NULL;
if ((t->codec_id == MKV_V_MPEG1) || (t->codec_id == MKV_V_MPEG2)) {
int version;
version = t->codec_id[6] - '0';
mxinfo(FMT_TID "Using the MPEG-%d video output module.\n",
ti->fname.c_str(), (int64_t)t->tnum, version);
t->ptzr =
add_packetizer(new mpeg1_2_video_packetizer_c(this,
version,
t->v_frate,
t->v_width,
t->v_height,
t->v_dwidth,
t->v_dheight,
true, nti));
} else if (IS_MPEG4_L2_CODECID(t->codec_id) ||
((fourcc != NULL) && IS_MPEG4_L2_FOURCC(fourcc))) {
bool is_native;
mxinfo(FMT_TID "Using the MPEG-4 layer 2 video output module.\n",
ti->fname.c_str(), (int64_t)t->tnum);
is_native = IS_MPEG4_L2_CODECID(t->codec_id);
t->ptzr =
add_packetizer(new mpeg4_l2_video_packetizer_c(this,
t->v_frate,
t->v_width,
t->v_height,
is_native,
nti));
} else if (t->codec_id == MKV_V_MPEG4_AVC) {
mxinfo(FMT_TID "Using the MPEG-4 layer 10 (AVC) video output "
"module.\n", ti->fname.c_str(), (int64_t)t->tnum);
t->ptzr =
add_packetizer(new mpeg4_l10_video_packetizer_c(this,
t->v_frate,
t->v_width,
t->v_height,
nti));
} else {
mxinfo(FMT_TID "Using the video output module.\n",
ti->fname.c_str(), (int64_t)t->tnum);
t->ptzr =
add_packetizer(new video_packetizer_c(this,
t->codec_id.c_str(),
t->v_frate,
t->v_width,
t->v_height,
nti));
}
if (!PTZR(t->ptzr)->ti->aspect_ratio_given) {
// The user hasn't set it.
if (t->v_dwidth != 0)

View File

@ -149,9 +149,9 @@ mpeg_es_reader_c::create_packetizer(int64_t) {
if (NPTZR() != 0)
return;
add_packetizer(new mpeg_12_video_packetizer_c(this, version, frame_rate,
add_packetizer(new mpeg1_2_video_packetizer_c(this, version, frame_rate,
width, height, dwidth, dheight,
ti));
false, ti));
mxinfo(FMT_TID "Using the MPEG-1/2 video output module.\n",
ti->fname.c_str(), (int64_t)0);
@ -801,15 +801,16 @@ mpeg_ps_reader_c::create_packetizer(int64_t id) {
} else { // if (track->type == 'a')
if ((track->fourcc == FOURCC('m', 'p', 'g', '1')) ||
(track->fourcc == FOURCC('m', 'p', 'g', '2'))) {
mpeg_12_video_packetizer_c *ptzr;
mpeg1_2_video_packetizer_c *ptzr;
ti->private_data = track->raw_seq_hdr;
ti->private_size = track->raw_seq_hdr_size;
ptzr =
new mpeg_12_video_packetizer_c(this, track->v_version,
new mpeg1_2_video_packetizer_c(this, track->v_version,
track->v_frame_rate,
track->v_width, track->v_height,
track->v_dwidth, track->v_dheight, ti);
track->v_dwidth, track->v_dheight,
false, ti);
track->ptzr = add_packetizer(ptzr);
ti->private_data = NULL;
ti->private_size = 0;

View File

@ -429,7 +429,7 @@ ogm_reader_c::create_packetizer(int64_t tid) {
get_uint64_le(&sth->time_unit),
get_uint32_le(&sth->sh.video.width),
get_uint32_le(&sth->sh.video.height),
false, ti);
ti);
mxinfo(FMT_TID "Using the video output module.\n", ti->fname.c_str(),
(int64_t)tid);

View File

@ -1381,21 +1381,27 @@ qtmp4_reader_c::create_packetizer(int64_t tid) {
ti->private_size = sizeof(alBITMAPINFOHEADER);
ti->private_data = (unsigned char *)bih;
dmx->ptzr =
add_packetizer(new video_packetizer_c(this, MKV_V_MSCOMP, 0.0,
dmx->v_width, dmx->v_height,
false, ti));
add_packetizer(new mpeg4_l2_video_packetizer_c(this, 0.0,
dmx->v_width,
dmx->v_height, true,
ti));
safefree(bih);
ti->private_data = NULL;
mxinfo(FMT_TID "Using the MPEG-4 layer 2 video output module.\n",
ti->fname.c_str(), (int64_t)dmx->id);
} else if (!strncasecmp(dmx->fourcc, "mpg1", 4) ||
!strncasecmp(dmx->fourcc, "mpg2", 4)) {
string codec_id;
int version;
codec_id = mxsprintf("V_MPEG%c", dmx->fourcc[3]);
version = dmx->fourcc[3] - '0';
dmx->ptzr =
add_packetizer(new video_packetizer_c(this, codec_id.c_str(),
-1.0, dmx->v_width,
dmx->v_height, false, ti));
add_packetizer(new mpeg1_2_video_packetizer_c(this, version,
-1.0, dmx->v_width,
dmx->v_height, 0, 0,
false, ti));
mxinfo(FMT_TID "Using the MPEG-%d video output module.\n",
ti->fname.c_str(), (int64_t)dmx->id, version);
} else if (!strncasecmp(dmx->fourcc, "avc1", 4)) {
double fps;
@ -1449,15 +1455,17 @@ qtmp4_reader_c::create_packetizer(int64_t tid) {
ti->private_size = dmx->priv_size;
ti->private_data = dmx->priv;
dmx->ptzr =
add_packetizer(new video_packetizer_c(this, MKV_V_MPEG4_AVC, fps,
dmx->v_width, dmx->v_height,
false, ti));
add_packetizer(new mpeg4_l10_video_packetizer_c(this, fps,
dmx->v_width,
dmx->v_height, ti));
ti->private_data = NULL;
if (hack_engaged(ENGAGE_AVC_USE_BFRAMES))
dmx->avc_use_bframes = true;
else
PTZR(dmx->ptzr)->relaxed_timecode_checking = true;
mxinfo(FMT_TID "Using the MPEG-4 layer 10 (AVC) video output "
"module.\n", ti->fname.c_str(), (int64_t)dmx->id);
} else {
ti->private_size = dmx->v_stsd_size;
@ -1465,11 +1473,11 @@ qtmp4_reader_c::create_packetizer(int64_t tid) {
dmx->ptzr =
add_packetizer(new video_packetizer_c(this, MKV_V_QUICKTIME, 0.0,
dmx->v_width, dmx->v_height,
false, ti));
ti));
ti->private_data = NULL;
mxinfo(FMT_TID "Using the video output module (FourCC: %.4s).\n",
ti->fname.c_str(), (int64_t)dmx->id, dmx->fourcc);
}
mxinfo(FMT_TID "Using the video output module (FourCC: %.4s).\n",
ti->fname.c_str(), (int64_t)dmx->id, dmx->fourcc);
} else {
if (!strncasecmp(dmx->fourcc, "QDMC", 4) ||

View File

@ -260,8 +260,7 @@ real_reader_c::create_packetizer(int64_t tid) {
mxprints(buffer, "V_REAL/%s", dmx->fourcc);
dmx->ptzr =
add_packetizer(new video_packetizer_c(this, buffer, dmx->fps,
dmx->width, dmx->height, false,
ti));
dmx->width, dmx->height, ti));
if ((dmx->fourcc[0] != 'R') || (dmx->fourcc[1] != 'V') ||
(dmx->fourcc[2] != '4') || (dmx->fourcc[3] != '0'))
dmx->rv_dimensions = true;

View File

@ -32,82 +32,42 @@ extern "C" {
using namespace libmatroska;
video_packetizer_c::video_packetizer_c(generic_reader_c *nreader,
const char *ncodec_id,
double nfps,
int nwidth,
int nheight,
bool nbframes,
track_info_c *nti)
throw (error_c) : generic_packetizer_c(nreader, nti) {
char *fourcc;
video_packetizer_c::video_packetizer_c(generic_reader_c *_reader,
const char *_codec_id,
double _fps,
int _width,
int _height,
track_info_c *_ti):
generic_packetizer_c(_reader, _ti),
fps(_fps), width(_width), height(_height),
frames_output(0), ref_timecode(-1), duration_shift(0) {
fps = nfps;
width = nwidth;
height = nheight;
frames_output = 0;
bframes = nbframes;
ref_timecode = -1;
if (get_cue_creation() == CUE_STRATEGY_UNSPECIFIED)
set_cue_creation(CUE_STRATEGY_IFRAMES);
duration_shift = 0;
bref_frame.type = '?';
fref_frame.type = '?';
aspect_ratio_extracted = false;
set_track_type(track_video);
mpeg_video = MPEG_VIDEO_NONE;
if ((ti->private_data != NULL) &&
(ti->private_size >= sizeof(alBITMAPINFOHEADER))) {
fourcc = (char *)&((alBITMAPINFOHEADER *)ti->private_data)->bi_compression;
if (!strncasecmp(fourcc, "DIVX", 4) ||
!strncasecmp(fourcc, "XVID", 4) ||
!strncasecmp(fourcc, "DX5", 3))
mpeg_video = MPEG_VIDEO_V4_LAYER_2;
}
if ((mpeg_video == MPEG_VIDEO_NONE) && (ncodec_id != NULL) &&
!strncmp(ncodec_id, MKV_V_MPEG4_SP, strlen(MKV_V_MPEG4_SP) - 2)) {
if (!strcmp(ncodec_id, MKV_V_MPEG4_AVC))
mpeg_video = MPEG_VIDEO_V4_LAYER_10;
else
mpeg_video = MPEG_VIDEO_V4_LAYER_2;
}
if ((mpeg_video == MPEG_VIDEO_NONE) && (ncodec_id != NULL) &&
(!strcmp(ncodec_id, MKV_V_MPEG1) || !strcmp(ncodec_id, MKV_V_MPEG2)))
mpeg_video = (ncodec_id[6] == '1') ? MPEG_VIDEO_V1 : MPEG_VIDEO_V2;
if ((mpeg_video == MPEG_VIDEO_V4_LAYER_2) &&
hack_engaged(ENGAGE_NATIVE_MPEG4))
set_codec_id(MKV_V_MPEG4_ASP);
else if (ncodec_id != NULL)
set_codec_id(ncodec_id);
if (_codec_id != NULL)
set_codec_id(_codec_id);
else
set_codec_id(MKV_V_MSCOMP);
if (((mpeg_video != MPEG_VIDEO_V4_LAYER_2) ||
!hack_engaged(ENGAGE_NATIVE_MPEG4)) &&
(hcodec_id != "") && (hcodec_id == MKV_V_MSCOMP) &&
set_codec_private(ti->private_data, ti->private_size);
check_fourcc();
}
void
video_packetizer_c::check_fourcc() {
if ((hcodec_id == MKV_V_MSCOMP) &&
(ti->private_data != NULL) &&
(ti->private_size >= sizeof(alBITMAPINFOHEADER)) &&
(ti->fourcc[0] != 0))
memcpy(&((alBITMAPINFOHEADER *)ti->private_data)->bi_compression,
ti->fourcc, 4);
if ((mpeg_video != MPEG_VIDEO_V4_LAYER_2) ||
!hack_engaged(ENGAGE_NATIVE_MPEG4))
set_codec_private(ti->private_data, ti->private_size);
}
void
video_packetizer_c::set_headers() {
// Set MinCache to 1 for I- and P-frames. If you only
// have I-frames then it can be set to 0 (e.g. MJPEG). 2 is needed
// if there are B-frames as well.
if (bframes)
set_track_min_cache(2);
else
set_track_min_cache(1);
if (fps > 0.0)
set_track_default_duration((int64_t)(1000000000.0 / fps));
@ -134,87 +94,6 @@ video_packetizer_c::process(memory_c &mem,
int64_t bref,
int64_t fref) {
int64_t timecode;
vector<video_frame_t> frames;
uint32_t i;
debug_enter("video_packetizer_c::process");
if (!aspect_ratio_extracted) {
if (mpeg_video == MPEG_VIDEO_V4_LAYER_2)
extract_mpeg4_aspect_ratio(mem.data, mem.size);
else if (mpeg_video == MPEG_VIDEO_V4_LAYER_10)
extract_mpeg4_aspect_ratio(ti->private_data, ti->private_size);
aspect_ratio_extracted = true;
}
if ((fps < 0.0) &&
((mpeg_video == MPEG_VIDEO_V1) || (mpeg_video == MPEG_VIDEO_V2)))
extract_mpeg1_2_fps(mem.data, mem.size);
if ((mpeg_video == MPEG_VIDEO_V4_LAYER_2) &&
hack_engaged(ENGAGE_NATIVE_MPEG4))
mpeg4_find_frame_types(mem.data, mem.size, frames);
if ((mpeg_video == MPEG_VIDEO_V4_LAYER_2) &&
hack_engaged(ENGAGE_NATIVE_MPEG4) && (fps != 0.0)) {
for (i = 0; i < frames.size(); i++) {
if ((frames[i].type == 'I') ||
((frames[i].type != 'B') && (fref_frame.type != '?')))
flush_frames(frames[i].type);
if (old_timecode == -1)
timecode = (int64_t)(1000000000.0 * frames_output / fps) +
duration_shift;
else
timecode = old_timecode;
if ((duration == -1) || (duration == (int64_t)(1000.0 / fps)))
duration = (int64_t)(1000000000.0 / fps);
else
duration_shift += duration - (int64_t)(1000000000.0 / fps);
frames_output++;
frames[i].timecode = timecode;
frames[i].duration = duration;
frames[i].data = (unsigned char *)safememdup(&mem.data[frames[i].pos],
frames[i].size);
if (frames[i].type == 'I') {
frames[i].bref = -1;
frames[i].fref = -1;
if (bref_frame.type == '?') {
bref_frame = frames[i];
memory_c mem(frames[i].data, frames[i].size, false);
add_packet(mem, frames[i].timecode, frames[i].duration);
} else
fref_frame = frames[i];
} else if (frames[i].type != 'B') {
frames_output--;
if (bref_frame.type == '?')
mxerror("video_packetizer: Found a P frame but no I frame. This "
"should not have happened. Either this is a bug in mkvmerge "
"or the video stream is damaged.\n");
frames[i].bref = bref_frame.timecode;
frames[i].fref = -1;
fref_frame = frames[i];
} else {
if (!bframes) {
set_codec_id(MKV_V_MPEG4_ASP);
set_track_min_cache(2);
rerender_track_headers();
}
bframes = true;
queued_frames.push_back(frames[i]);
}
}
debug_leave("video_packetizer_c::process");
return FILE_STATUS_MOREDATA;
}
if (old_timecode == -1)
timecode = (int64_t)(1000000000.0 * frames_output / fps) + duration_shift;
@ -240,14 +119,9 @@ video_packetizer_c::process(memory_c &mem,
ref_timecode = timecode;
}
debug_leave("video_packetizer_c::process");
return FILE_STATUS_MOREDATA;
}
video_packetizer_c::~video_packetizer_c() {
}
void
video_packetizer_c::dump_debug_info() {
mxdebug("video_packetizer_c: queue: %d; frames_output: %d; "
@ -255,39 +129,125 @@ video_packetizer_c::dump_debug_info() {
ref_timecode);
}
void
video_packetizer_c::flush() {
flush_frames(true);
connection_result_e
video_packetizer_c::can_connect_to(generic_packetizer_c *src) {
video_packetizer_c *vsrc;
vsrc = dynamic_cast<video_packetizer_c *>(src);
if (vsrc == NULL)
return CAN_CONNECT_NO_FORMAT;
if ((width != vsrc->width) || (height != vsrc->height) ||
(fps != vsrc->fps) || (hcodec_id != vsrc->hcodec_id))
return CAN_CONNECT_NO_PARAMETERS;
if (((ti->private_data == NULL) && (vsrc->ti->private_data != NULL)) ||
((ti->private_data != NULL) && (vsrc->ti->private_data == NULL)) ||
(ti->private_size != vsrc->ti->private_size))
return CAN_CONNECT_NO_PARAMETERS;
if ((ti->private_data != NULL) &&
memcmp(ti->private_data, vsrc->ti->private_data, ti->private_size))
return CAN_CONNECT_NO_PARAMETERS;
return CAN_CONNECT_YES;
}
void
video_packetizer_c::extract_mpeg4_aspect_ratio(const unsigned char *buffer,
int size) {
uint32_t num, den;
// ----------------------------------------------------------------
aspect_ratio_extracted = true;
if (ti->aspect_ratio_given || ti->display_dimensions_given)
return;
mpeg1_2_video_packetizer_c::
mpeg1_2_video_packetizer_c(generic_reader_c *_reader,
int _version,
double _fps,
int _width,
int _height,
int _dwidth,
int _dheight,
bool _framed,
track_info_c *_ti):
video_packetizer_c(_reader, "V_MPEG1", _fps, _width, _height, _ti),
framed(_framed), aspect_ratio_extracted(false) {
if (((mpeg_video == MPEG_VIDEO_V4_LAYER_2) &&
mpeg4_extract_par(buffer, size, num, den))
||
((mpeg_video == MPEG_VIDEO_V4_LAYER_10) &&
mpeg4_l10_extract_par(buffer, size, num, den))) {
ti->aspect_ratio_given = true;
ti->aspect_ratio = (float)hvideo_pixel_width /
(float)hvideo_pixel_height * (float)num / (float)den;
generic_packetizer_c::set_headers();
rerender_track_headers();
mxinfo("Track %lld of '%s': Extracted the aspect ratio information "
"from the MPEG4 video data and set the display dimensions to "
"%u/%u.\n", (int64_t)ti->id, ti->fname.c_str(),
(uint32_t)ti->display_width, (uint32_t)ti->display_height);
set_codec_id(mxsprintf("V_MPEG%d", _version));
if (!ti->aspect_ratio_given && !ti->display_dimensions_given) {
if ((_dwidth > 0) && (_dheight > 0)) {
ti->display_dimensions_given = true;
ti->display_width = _dwidth;
ti->display_height = _dheight;
}
} else
aspect_ratio_extracted = true;
}
int
mpeg1_2_video_packetizer_c::process(memory_c &mem,
int64_t timecode,
int64_t duration,
int64_t bref,
int64_t fref) {
unsigned char *data_ptr;
int new_bytes, state;
if (fps < 0.0)
extract_fps(mem.data, mem.size);
if (!aspect_ratio_extracted)
extract_aspect_ratio(mem.data, mem.size);
if (framed) {
assert(0);
}
state = parser.GetState();
if ((state == MPV_PARSER_STATE_EOS) ||
(state == MPV_PARSER_STATE_ERROR))
return FILE_STATUS_DONE;
data_ptr = mem.data;
new_bytes = mem.size;
do {
int bytes_to_add;
bytes_to_add = (parser.GetFreeBufferSpace() < new_bytes) ?
parser.GetFreeBufferSpace() : new_bytes;
if (bytes_to_add > 0) {
parser.WriteData(data_ptr, bytes_to_add);
data_ptr += bytes_to_add;
new_bytes -= bytes_to_add;
}
state = parser.GetState();
while (state == MPV_PARSER_STATE_FRAME) {
MPEGFrame *frame;
frame = parser.ReadFrame();
if (frame == NULL)
break;
if (hcodec_private == NULL)
create_private_data();
memory_c new_mem(frame->data, frame->size, true);
video_packetizer_c::process(new_mem, frame->timecode, frame->duration,
frame->firstRef, frame->secondRef);
frame->data = NULL;
delete frame;
state = parser.GetState();
}
} while (new_bytes > 0);
return FILE_STATUS_MOREDATA;
}
void
video_packetizer_c::extract_mpeg1_2_fps(const unsigned char *buffer,
mpeg1_2_video_packetizer_c::flush() {
memory_c dummy((unsigned char *)"", 0, false);
parser.SetEOS();
process(dummy);
video_packetizer_c::flush();
}
void
mpeg1_2_video_packetizer_c::extract_fps(const unsigned char *buffer,
int size) {
int idx;
@ -303,8 +263,128 @@ video_packetizer_c::extract_mpeg1_2_fps(const unsigned char *buffer,
}
void
video_packetizer_c::flush_frames(char next_frame,
bool flush_all) {
mpeg1_2_video_packetizer_c::extract_aspect_ratio(const unsigned char *buffer,
int size) {
float ar;
if (ti->aspect_ratio_given || ti->display_dimensions_given)
return;
if (mpeg1_2_extract_ar(buffer, size, ar)) {
ti->display_dimensions_given = true;
if ((ar <= 0) || (ar == 1))
set_video_display_width(width);
else
set_video_display_width((int)(height * ar));
set_video_display_height(height);
rerender_track_headers();
aspect_ratio_extracted = true;
}
}
void
mpeg1_2_video_packetizer_c::create_private_data() {
MPEGChunk *raw_seq_hdr;
raw_seq_hdr = parser.GetRealSequenceHeader();
if (raw_seq_hdr != NULL) {
set_codec_private(raw_seq_hdr->GetPointer(), raw_seq_hdr->GetSize());
rerender_track_headers();
}
}
// ----------------------------------------------------------------
mpeg4_l2_video_packetizer_c::
mpeg4_l2_video_packetizer_c(generic_reader_c *_reader,
double _fps,
int _width,
int _height,
bool _input_is_native,
track_info_c *_ti):
video_packetizer_c(_reader, MKV_V_MPEG4_ASP, _fps, _width, _height, _ti),
aspect_ratio_extracted(false), input_is_native(_input_is_native) {
assert(!input_is_native);
if (!hack_engaged(ENGAGE_NATIVE_MPEG4) || (_fps == 0.0)) {
set_codec_id(MKV_V_MSCOMP);
check_fourcc();
}
}
int
mpeg4_l2_video_packetizer_c::process(memory_c &mem,
int64_t old_timecode,
int64_t duration,
int64_t bref,
int64_t fref) {
int64_t timecode;
vector<video_frame_t> frames;
int i;
if (!aspect_ratio_extracted)
extract_aspect_ratio(mem.data, mem.size);
if (!hack_engaged(ENGAGE_NATIVE_MPEG4) || (fps == 0.0)) {
video_packetizer_c::process(mem, old_timecode, duration, bref, fref);
return FILE_STATUS_MOREDATA;
}
mpeg4_find_frame_types(mem.data, mem.size, frames);
for (i = 0; i < frames.size(); i++) {
if ((frames[i].type == 'I') ||
((frames[i].type != 'B') && (fref_frame.type != '?')))
flush_frames(frames[i].type);
if (old_timecode == -1)
timecode = (int64_t)(1000000000.0 * frames_output / fps) +
duration_shift;
else
timecode = old_timecode;
if ((duration == -1) || (duration == (int64_t)(1000.0 / fps)))
duration = (int64_t)(1000000000.0 / fps);
else
duration_shift += duration - (int64_t)(1000000000.0 / fps);
frames_output++;
frames[i].timecode = timecode;
frames[i].duration = duration;
frames[i].data = (unsigned char *)safememdup(&mem.data[frames[i].pos],
frames[i].size);
if (frames[i].type == 'I') {
frames[i].bref = -1;
frames[i].fref = -1;
if (bref_frame.type == '?') {
bref_frame = frames[i];
memory_c mem(frames[i].data, frames[i].size, false);
add_packet(mem, frames[i].timecode, frames[i].duration);
} else
fref_frame = frames[i];
} else if (frames[i].type != 'B') {
frames_output--;
if (bref_frame.type == '?')
mxerror("video_packetizer: Found a P frame but no I frame. This "
"should not have happened. Either this is a bug in mkvmerge "
"or the video stream is damaged.\n");
frames[i].bref = bref_frame.timecode;
frames[i].fref = -1;
fref_frame = frames[i];
} else
queued_frames.push_back(frames[i]);
}
return FILE_STATUS_MOREDATA;
}
void
mpeg4_l2_video_packetizer_c::flush_frames(char next_frame,
bool flush_all) {
uint32_t i;
if (bref_frame.type == '?') {
@ -363,102 +443,61 @@ video_packetizer_c::flush_frames(char next_frame,
bref_frame.type = '?';
}
connection_result_e
video_packetizer_c::can_connect_to(generic_packetizer_c *src) {
video_packetizer_c *vsrc;
void
mpeg4_l2_video_packetizer_c::flush() {
flush_frames(true);
}
vsrc = dynamic_cast<video_packetizer_c *>(src);
if (vsrc == NULL)
return CAN_CONNECT_NO_FORMAT;
if ((width != vsrc->width) || (height != vsrc->height) ||
(fps != vsrc->fps) || (hcodec_id != vsrc->hcodec_id))
return CAN_CONNECT_NO_PARAMETERS;
if (((ti->private_data == NULL) && (vsrc->ti->private_data != NULL)) ||
((ti->private_data != NULL) && (vsrc->ti->private_data == NULL)) ||
(ti->private_size != vsrc->ti->private_size))
return CAN_CONNECT_NO_PARAMETERS;
if ((ti->private_data != NULL) &&
memcmp(ti->private_data, vsrc->ti->private_data, ti->private_size))
return CAN_CONNECT_NO_PARAMETERS;
return CAN_CONNECT_YES;
void
mpeg4_l2_video_packetizer_c::extract_aspect_ratio(const unsigned char *buffer,
int size) {
uint32_t num, den;
aspect_ratio_extracted = true;
if (ti->aspect_ratio_given || ti->display_dimensions_given)
return;
if (mpeg4_extract_par(buffer, size, num, den)) {
ti->aspect_ratio_given = true;
ti->aspect_ratio = (float)hvideo_pixel_width /
(float)hvideo_pixel_height * (float)num / (float)den;
generic_packetizer_c::set_headers();
rerender_track_headers();
mxinfo("Track %lld of '%s': Extracted the aspect ratio information "
"from the MPEG4 layer 2 video data and set the display dimensions "
"to %u/%u.\n", (int64_t)ti->id, ti->fname.c_str(),
(uint32_t)ti->display_width, (uint32_t)ti->display_height);
}
}
// ----------------------------------------------------------------
mpeg_12_video_packetizer_c::
mpeg_12_video_packetizer_c(generic_reader_c *_reader,
int _version,
double _fps,
int _width,
int _height,
int _dwidth,
int _dheight,
track_info_c *_ti):
video_packetizer_c(_reader, "V_MPEG1", _fps, _width, _height, true, _ti) {
mpeg4_l10_video_packetizer_c::
mpeg4_l10_video_packetizer_c(generic_reader_c *_reader,
double _fps,
int _width,
int _height,
track_info_c *_ti):
video_packetizer_c(_reader, MKV_V_MPEG4_AVC, _fps, _width, _height, _ti) {
mpeg_video = (_version == 1) ? MPEG_VIDEO_V1 : MPEG_VIDEO_V2;
set_codec_id(mxsprintf("V_MPEG%d", _version));
if (!ti->aspect_ratio_given && !ti->display_dimensions_given) {
ti->display_dimensions_given = true;
ti->display_width = _dwidth;
ti->display_height = _dheight;
}
}
int
mpeg_12_video_packetizer_c::process(memory_c &mem,
int64_t,
int64_t,
int64_t,
int64_t) {
unsigned char *data_ptr;
int new_bytes, state;
state = parser.GetState();
if ((state == MPV_PARSER_STATE_EOS) ||
(state == MPV_PARSER_STATE_ERROR))
return FILE_STATUS_DONE;
data_ptr = mem.data;
new_bytes = mem.size;
do {
int bytes_to_add;
bytes_to_add = (parser.GetFreeBufferSpace() < new_bytes) ?
parser.GetFreeBufferSpace() : new_bytes;
if (bytes_to_add > 0) {
parser.WriteData(data_ptr, bytes_to_add);
data_ptr += bytes_to_add;
new_bytes -= bytes_to_add;
}
state = parser.GetState();
while (state == MPV_PARSER_STATE_FRAME) {
MPEGFrame *frame;
frame = parser.ReadFrame();
if (frame == NULL)
break;
memory_c new_mem(frame->data, frame->size, true);
video_packetizer_c::process(new_mem, frame->timecode, frame->duration,
frame->firstRef, frame->secondRef);
frame->data = NULL;
delete frame;
state = parser.GetState();
}
} while (new_bytes > 0);
return FILE_STATUS_MOREDATA;
if ((ti->private_data != NULL) && (ti->private_size > 0))
extract_aspect_ratio();
}
void
mpeg_12_video_packetizer_c::flush() {
memory_c dummy((unsigned char *)"", 0, false);
mpeg4_l10_video_packetizer_c::extract_aspect_ratio() {
uint32_t num, den;
parser.SetEOS();
process(dummy);
video_packetizer_c::flush();
if (ti->aspect_ratio_given || ti->display_dimensions_given)
return;
if (mpeg4_l10_extract_par(ti->private_data, ti->private_size, num, den)) {
ti->aspect_ratio_given = true;
ti->aspect_ratio = (float)hvideo_pixel_width /
(float)hvideo_pixel_height * (float)num / (float)den;
mxinfo("Track %lld of '%s': Extracted the aspect ratio information "
"from the MPEG-4 layer 10 (AVC) video data and set the display "
"dimensions to %u/%u.\n", (int64_t)ti->id, ti->fname.c_str(),
(uint32_t)ti->display_width, (uint32_t)ti->display_height);
}
}

View File

@ -30,26 +30,19 @@
class video_packetizer_c: public generic_packetizer_c {
protected:
double fps;
int width, height, bpp, frames_output;
int width, height, frames_output;
int64_t ref_timecode, duration_shift;
bool avi_compat_mode, bframes, pass_through;
bool aspect_ratio_extracted;
mpeg_video_type_e mpeg_video;
vector<video_frame_t> queued_frames;
video_frame_t bref_frame, fref_frame;
bool pass_through;
public:
video_packetizer_c(generic_reader_c *nreader, const char *ncodec_id,
double nfps, int nwidth, int nheight, bool nbframes,
track_info_c *nti)
throw (error_c);
virtual ~video_packetizer_c();
video_packetizer_c(generic_reader_c *_reader, const char *_codec_id,
double _fps, int _width, int _height,
track_info_c *_ti);
virtual int process(memory_c &mem, int64_t old_timecode = -1,
int64_t duration = -1, int64_t bref = VFT_IFRAME,
int64_t fref = VFT_NOBFRAME);
virtual void set_headers();
virtual void flush();
virtual void dump_debug_info();
@ -59,25 +52,60 @@ public:
virtual connection_result_e can_connect_to(generic_packetizer_c *src);
protected:
virtual void flush_frames(char next_frame = '?', bool flush_all = false);
virtual void extract_mpeg4_aspect_ratio(const unsigned char *buffer,
int size);
virtual void extract_mpeg1_2_fps(const unsigned char *buffer, int size);
virtual void check_fourcc();
};
class mpeg_12_video_packetizer_c: public video_packetizer_c {
class mpeg1_2_video_packetizer_c: public video_packetizer_c {
protected:
M2VParser parser;
bool framed, aspect_ratio_extracted;
public:
mpeg_12_video_packetizer_c(generic_reader_c *_reader, int _version,
mpeg1_2_video_packetizer_c(generic_reader_c *_reader, int _version,
double _fps, int _width, int _height,
int _dwidth, int _dheight, track_info_c *_ti);
int _dwidth, int _dheight, bool _framed,
track_info_c *_ti);
virtual int process(memory_c &mem, int64_t old_timecode = -1,
int64_t duration = -1, int64_t bref = VFT_IFRAME,
int64_t fref = VFT_NOBFRAME);
virtual void flush();
protected:
virtual void extract_fps(const unsigned char *buffer, int size);
virtual void extract_aspect_ratio(const unsigned char *buffer, int size);
virtual void create_private_data();
};
class mpeg4_l2_video_packetizer_c: public video_packetizer_c {
protected:
vector<video_frame_t> queued_frames;
video_frame_t bref_frame, fref_frame;
bool aspect_ratio_extracted, input_is_native;
public:
mpeg4_l2_video_packetizer_c(generic_reader_c *_reader,
double _fps, int _width, int _height,
bool _input_is_native, track_info_c *_ti);
virtual int process(memory_c &mem, int64_t old_timecode = -1,
int64_t duration = -1, int64_t bref = VFT_IFRAME,
int64_t fref = VFT_NOBFRAME);
virtual void flush();
protected:
virtual void flush_frames(char next_frame = '?', bool flush_all = false);
virtual void extract_aspect_ratio(const unsigned char *buffer, int size);
};
class mpeg4_l10_video_packetizer_c: public video_packetizer_c {
public:
mpeg4_l10_video_packetizer_c(generic_reader_c *_reader,
double _fps, int _width, int _height,
track_info_c *_ti);
protected:
virtual void extract_aspect_ratio();
};
#endif // __P_VIDEO_H