mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-23 19:31:44 +00:00
MP4 files may contain other atoms than "avcC" in the video track headers; so look for "avcC" and don't rely on it being the first one. Added limited support for edit lists in MP4/QuickTime files.
This commit is contained in:
parent
ccb05d396f
commit
be157d9fd3
@ -1,3 +1,12 @@
|
|||||||
|
2005-11-18 Moritz Bunkus <moritz@bunkus.org>
|
||||||
|
|
||||||
|
* mkvmerge: new feature: Added limited support for edit lists in
|
||||||
|
MP4/QuickTime files. Fixes Anthill bug #151.
|
||||||
|
|
||||||
|
* mkvmerge: bug fix: MP4/QuickTime files which contain another
|
||||||
|
atom before the 'avcC' atom in the video track headers weren't
|
||||||
|
correctly remuxed.
|
||||||
|
|
||||||
2005-11-16 Moritz Bunkus <moritz@bunkus.org>
|
2005-11-16 Moritz Bunkus <moritz@bunkus.org>
|
||||||
|
|
||||||
* mkvmerge: bug fix: mkvmerge will now refuse to append AVC/h.264
|
* mkvmerge: bug fix: mkvmerge will now refuse to append AVC/h.264
|
||||||
|
@ -88,7 +88,11 @@ qtmp4_reader_c::probe_file(mm_io_c *in,
|
|||||||
|
|
||||||
qtmp4_reader_c::qtmp4_reader_c(track_info_c &_ti)
|
qtmp4_reader_c::qtmp4_reader_c(track_info_c &_ti)
|
||||||
throw (error_c):
|
throw (error_c):
|
||||||
generic_reader_c(_ti) {
|
generic_reader_c(_ti),
|
||||||
|
io(NULL), file_size(0), mdat_pos(-1), mdat_size(0),
|
||||||
|
time_scale(1), compression_algorithm(0),
|
||||||
|
main_dmx(-1) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
io = new mm_file_io_c(ti.fname);
|
io = new mm_file_io_c(ti.fname);
|
||||||
io->setFilePointer(0, seek_end);
|
io->setFilePointer(0, seek_end);
|
||||||
@ -115,18 +119,21 @@ qtmp4_reader_c::~qtmp4_reader_c() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qt_atom_t
|
qt_atom_t
|
||||||
qtmp4_reader_c::read_atom() {
|
qtmp4_reader_c::read_atom(mm_io_c *read_from) {
|
||||||
qt_atom_t a;
|
qt_atom_t a;
|
||||||
|
|
||||||
a.pos = io->getFilePointer();
|
if (NULL == read_from)
|
||||||
a.size = io->read_uint32_be();
|
read_from = io;
|
||||||
a.fourcc = io->read_uint32_be();
|
|
||||||
|
a.pos = read_from->getFilePointer();
|
||||||
|
a.size = read_from->read_uint32_be();
|
||||||
|
a.fourcc = read_from->read_uint32_be();
|
||||||
a.hsize = 8;
|
a.hsize = 8;
|
||||||
if (a.size == 1) {
|
if (a.size == 1) {
|
||||||
a.size = io->read_uint64_be();
|
a.size = read_from->read_uint64_be();
|
||||||
a.hsize += 8;
|
a.hsize += 8;
|
||||||
} else if (a.size == 0)
|
} else if (a.size == 0)
|
||||||
a.size = file_size - io->getFilePointer() + 8;
|
a.size = file_size - read_from->getFilePointer() + 8;
|
||||||
if (a.size < a.hsize)
|
if (a.size < a.hsize)
|
||||||
mxerror(PFX "Invalid chunk size " LLU " at " LLU ".\n", a.size, a.pos);
|
mxerror(PFX "Invalid chunk size " LLU " at " LLU ".\n", a.size, a.pos);
|
||||||
|
|
||||||
@ -145,7 +152,6 @@ qtmp4_reader_c::parse_headers() {
|
|||||||
io->setFilePointer(0);
|
io->setFilePointer(0);
|
||||||
|
|
||||||
headers_parsed = false;
|
headers_parsed = false;
|
||||||
mdat_pos = -1;
|
|
||||||
do {
|
do {
|
||||||
atom = read_atom();
|
atom = read_atom();
|
||||||
mxverb(2, PFX "'%c%c%c%c' atom, size " LLD ", at " LLD "\n", BE2STR(atom),
|
mxverb(2, PFX "'%c%c%c%c' atom, size " LLD ", at " LLD "\n", BE2STR(atom),
|
||||||
@ -385,56 +391,129 @@ qtmp4_reader_c::update_tables(qtmp4_demuxer_ptr &dmx) {
|
|||||||
}
|
}
|
||||||
mxverb(3, PFX "Frame offset table: %u entries\n",
|
mxverb(3, PFX "Frame offset table: %u entries\n",
|
||||||
(unsigned int)dmx->frame_offset_table.size());
|
(unsigned int)dmx->frame_offset_table.size());
|
||||||
|
|
||||||
|
update_editlist_table(dmx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also taken from mplayer's demux_mov.c file.
|
||||||
|
void
|
||||||
|
qtmp4_reader_c::update_editlist_table(qtmp4_demuxer_ptr &dmx) {
|
||||||
|
if (dmx->editlist_table.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int frame = 0, e_pts = 0, i;
|
||||||
|
|
||||||
|
mxverb(4, "qtmp4: Updating edit list table for track %u\n", dmx->id);
|
||||||
|
|
||||||
|
for (i = 0; dmx->editlist_table.size() > i; ++i) {
|
||||||
|
qt_editlist_t &el = dmx->editlist_table[i];
|
||||||
|
int sample = 0, pts = el.pos;
|
||||||
|
|
||||||
|
el.start_frame = frame;
|
||||||
|
|
||||||
|
if (pts < 0) {
|
||||||
|
// skip!
|
||||||
|
el.frames = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find start sample
|
||||||
|
for (; dmx->sample_table.size() > sample; ++sample)
|
||||||
|
if (pts <= dmx->sample_table[sample].pts)
|
||||||
|
break;
|
||||||
|
el.start_sample = sample;
|
||||||
|
|
||||||
|
el.pts_offset = ((int64_t)e_pts * (int64_t)dmx->time_scale) /
|
||||||
|
(int64_t)time_scale - (int64_t)dmx->sample_table[sample].pts;
|
||||||
|
pts += ((int64_t)el.duration * (int64_t)dmx->time_scale) /
|
||||||
|
(int64_t)time_scale;
|
||||||
|
e_pts += el.duration;
|
||||||
|
|
||||||
|
// find end sample
|
||||||
|
for (; dmx->sample_table.size() > sample; ++sample)
|
||||||
|
if (pts <= dmx->sample_table[sample].pts)
|
||||||
|
break;
|
||||||
|
|
||||||
|
el.frames = sample - el.start_sample;
|
||||||
|
frame += el.frames;
|
||||||
|
|
||||||
|
mxverb(4, " %d: pts: %u 1st_sample: " LLU " frames: %u (%5.3fs) "
|
||||||
|
"pts_offset: " LLD "\n", i,
|
||||||
|
el.pos, el.start_sample, el.frames,
|
||||||
|
(float)(el.duration) / (float)time_scale, el.pts_offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
qtmp4_reader_c::parse_header_priv_atoms(qtmp4_demuxer_ptr &dmx,
|
qtmp4_reader_c::parse_video_header_priv_atoms(qtmp4_demuxer_ptr &dmx,
|
||||||
unsigned char *mem,
|
unsigned char *mem,
|
||||||
int size,
|
int size,
|
||||||
int level) {
|
int level) {
|
||||||
mm_mem_io_c mio(mem, size);
|
mm_mem_io_c mio(mem, size);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (!mio.eof()) {
|
while (!mio.eof() && (mio.getFilePointer() < size)) {
|
||||||
uint32_t add_atom, add_atom_size;
|
qt_atom_t atom;
|
||||||
|
|
||||||
add_atom_size = mio.read_uint32_be();
|
atom = read_atom(&mio);
|
||||||
add_atom = mio.read_uint32_be();
|
mxverb(2, PFX "%*sVideo private data size: %u, type: "
|
||||||
add_atom_size -= 8;
|
"'%c%c%c%c'\n", (level + 1) * 2, "", (unsigned int)atom.size,
|
||||||
mxverb(2, PFX "%*s%s private data size: %u, type: "
|
BE2STR(atom.fourcc));
|
||||||
"'%c%c%c%c'\n", (level + 1) * 2, "", dmx->type == 'a' ? "Audio" :
|
|
||||||
"Video", add_atom_size + 8, BE2STR(add_atom));
|
|
||||||
if (dmx->priv == NULL) {
|
|
||||||
uint32_t patom_size, patom;
|
|
||||||
|
|
||||||
dmx->priv_size = add_atom_size;
|
if ((FOURCC('e', 's', 'd', 's') == atom.fourcc) ||
|
||||||
dmx->priv = (unsigned char *)safemalloc(dmx->priv_size);
|
(FOURCC('a', 'v', 'c', 'C') == atom.fourcc)) {
|
||||||
if (mio.read(dmx->priv, dmx->priv_size) != dmx->priv_size)
|
if (NULL == dmx->priv) {
|
||||||
throw error_c("end-of-file");
|
dmx->priv_size = atom.size - atom.hsize;
|
||||||
|
dmx->priv = (unsigned char *)safemalloc(dmx->priv_size);
|
||||||
mm_mem_io_c memio(dmx->priv, dmx->priv_size);
|
if (mio.read(dmx->priv, dmx->priv_size) != dmx->priv_size) {
|
||||||
|
safefree(dmx->priv);
|
||||||
if (add_atom == FOURCC('e', 's', 'd', 's')) {
|
dmx->priv = NULL;
|
||||||
if (!dmx->esds_parsed)
|
dmx->priv_size = 0;
|
||||||
dmx->esds_parsed = parse_esds_atom(memio, dmx, level + 1);
|
return;
|
||||||
} else {
|
|
||||||
while (!memio.eof()) {
|
|
||||||
patom_size = memio.read_uint32_be();
|
|
||||||
if (patom_size <= 8)
|
|
||||||
break;
|
|
||||||
patom = memio.read_uint32_be();
|
|
||||||
mxverb(2, PFX "%*sAtom size: %u, atom: '%c%c%c%c'\n",
|
|
||||||
(level + 2) * 2, "", patom_size, BE2STR(patom));
|
|
||||||
if ((patom == FOURCC('e', 's', 'd', 's')) && !dmx->esds_parsed) {
|
|
||||||
memio.save_pos();
|
|
||||||
dmx->esds_parsed = parse_esds_atom(memio, dmx, level + 2);
|
|
||||||
memio.restore_pos();
|
|
||||||
}
|
|
||||||
memio.skip(patom_size - 8);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
mio.skip(add_atom_size);
|
if ((FOURCC('e', 's', 'd', 's') == atom.fourcc) && !dmx->esds_parsed) {
|
||||||
|
mm_mem_io_c memio(dmx->priv, dmx->priv_size);
|
||||||
|
dmx->esds_parsed = parse_esds_atom(memio, dmx, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mio.setFilePointer(atom.pos + atom.size);
|
||||||
|
}
|
||||||
|
} catch(...) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
qtmp4_reader_c::parse_audio_header_priv_atoms(qtmp4_demuxer_ptr &dmx,
|
||||||
|
unsigned char *mem,
|
||||||
|
int size,
|
||||||
|
int level) {
|
||||||
|
mm_mem_io_c mio(mem, size);
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (!mio.eof() && (mio.getFilePointer() < (size - 8))) {
|
||||||
|
qt_atom_t atom;
|
||||||
|
|
||||||
|
atom = read_atom(&mio);
|
||||||
|
|
||||||
|
if (FOURCC('e', 's', 'd', 's') != atom.fourcc) {
|
||||||
|
mio.setFilePointer(atom.pos + 4);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mxverb(2, PFX "%*sAudio private data size: %u, type: "
|
||||||
|
"'%c%c%c%c'\n", (level + 1) * 2, "", (unsigned int)atom.size,
|
||||||
|
BE2STR(atom.fourcc));
|
||||||
|
|
||||||
|
if (!dmx->esds_parsed) {
|
||||||
|
mm_mem_io_c memio(mem + atom.pos + atom.hsize,
|
||||||
|
atom.size - atom.hsize);
|
||||||
|
dmx->esds_parsed = parse_esds_atom(memio, dmx, level + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mio.setFilePointer(atom.pos + atom.size);
|
||||||
}
|
}
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
}
|
}
|
||||||
@ -606,7 +685,7 @@ qtmp4_reader_c::handle_mdhd_atom(qtmp4_demuxer_ptr &new_dmx,
|
|||||||
mxverb(2, PFX "%*s Time scale: %u, duration: %u\n", level * 2, "",
|
mxverb(2, PFX "%*s Time scale: %u, duration: %u\n", level * 2, "",
|
||||||
get_uint32_be(&mdhd.time_scale),
|
get_uint32_be(&mdhd.time_scale),
|
||||||
get_uint32_be(&mdhd.duration));
|
get_uint32_be(&mdhd.duration));
|
||||||
new_dmx->timescale = get_uint32_be(&mdhd.time_scale);
|
new_dmx->time_scale = get_uint32_be(&mdhd.time_scale);
|
||||||
new_dmx->global_duration = get_uint32_be(&mdhd.duration);
|
new_dmx->global_duration = get_uint32_be(&mdhd.duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,8 +778,8 @@ qtmp4_reader_c::handle_mvhd_atom(qt_atom_t atom,
|
|||||||
"size: " LLD ".\n", (unsigned int)sizeof(mvhd_atom_t), atom.size);
|
"size: " LLD ".\n", (unsigned int)sizeof(mvhd_atom_t), atom.size);
|
||||||
if (io->read(&mvhd, sizeof(mvhd_atom_t)) != sizeof(mvhd_atom_t))
|
if (io->read(&mvhd, sizeof(mvhd_atom_t)) != sizeof(mvhd_atom_t))
|
||||||
throw error_c("end-of-file");
|
throw error_c("end-of-file");
|
||||||
mxverb(2, PFX "%*s Time scale: %u\n", level * 2, "",
|
time_scale = get_uint32_be(&mvhd.time_scale);
|
||||||
get_uint32_be(&mvhd.time_scale));
|
mxverb(2, PFX "%*s Time scale: %u\n", level * 2, "", time_scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -903,9 +982,14 @@ qtmp4_reader_c::handle_stsd_atom(qtmp4_demuxer_ptr &new_dmx,
|
|||||||
stsd_size = sizeof(video_stsd_atom_t);
|
stsd_size = sizeof(video_stsd_atom_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((stsd_size > 0) && (stsd_size < size))
|
if ((stsd_size > 0) && (stsd_size < size)) {
|
||||||
parse_header_priv_atoms(new_dmx, priv + stsd_size, size -
|
if ('v' == new_dmx->type)
|
||||||
stsd_size, level);
|
parse_video_header_priv_atoms(new_dmx, priv + stsd_size, size -
|
||||||
|
stsd_size, level);
|
||||||
|
else if ('a' == new_dmx->type)
|
||||||
|
parse_audio_header_priv_atoms(new_dmx, priv + stsd_size, size -
|
||||||
|
stsd_size, level);
|
||||||
|
}
|
||||||
safefree(priv);
|
safefree(priv);
|
||||||
|
|
||||||
io->setFilePointer(pos + size);
|
io->setFilePointer(pos + size);
|
||||||
@ -996,6 +1080,50 @@ qtmp4_reader_c::handle_stts_atom(qtmp4_demuxer_ptr &new_dmx,
|
|||||||
count);
|
count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
qtmp4_reader_c::handle_edts_atom(qtmp4_demuxer_ptr &new_dmx,
|
||||||
|
qt_atom_t parent,
|
||||||
|
int level) {
|
||||||
|
while (parent.size > 0) {
|
||||||
|
qt_atom_t atom;
|
||||||
|
|
||||||
|
atom = read_atom();
|
||||||
|
mxverb(2, PFX "%*s'%c%c%c%c' atom, size " LLD ", at " LLD "\n", 2 * level,
|
||||||
|
"", BE2STR(atom.fourcc), atom.size, atom.pos);
|
||||||
|
|
||||||
|
if (atom.fourcc == FOURCC('e', 'l', 's', 't'))
|
||||||
|
handle_elst_atom(new_dmx, atom.to_parent(), level + 1);
|
||||||
|
|
||||||
|
skip_atom();
|
||||||
|
parent.size -= atom.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
qtmp4_reader_c::handle_elst_atom(qtmp4_demuxer_ptr &new_dmx,
|
||||||
|
qt_atom_t atom,
|
||||||
|
int level) {
|
||||||
|
uint32_t count, i;
|
||||||
|
|
||||||
|
io->skip(1 + 3); // version & flags
|
||||||
|
count = io->read_uint32_be();
|
||||||
|
new_dmx->editlist_table.resize(count);
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
qt_editlist_t &editlist = new_dmx->editlist_table[i];
|
||||||
|
|
||||||
|
editlist.duration = io->read_uint32_be();
|
||||||
|
editlist.pos = io->read_uint32_be();
|
||||||
|
editlist.speed = io->read_uint32_be();
|
||||||
|
}
|
||||||
|
|
||||||
|
mxverb(2, PFX "%*sEdit list table: %u entries\n", level * 2, "",
|
||||||
|
count);
|
||||||
|
for (i = 0; i < count; ++i)
|
||||||
|
mxverb(4, PFX "%*s%u: duration %u pos %u speed %u\n", (level + 1) * 2, "",
|
||||||
|
i, new_dmx->editlist_table[i].duration,
|
||||||
|
new_dmx->editlist_table[i].pos, new_dmx->editlist_table[i].speed);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
qtmp4_reader_c::handle_tkhd_atom(qtmp4_demuxer_ptr &new_dmx,
|
qtmp4_reader_c::handle_tkhd_atom(qtmp4_demuxer_ptr &new_dmx,
|
||||||
qt_atom_t atom,
|
qt_atom_t atom,
|
||||||
@ -1029,6 +1157,9 @@ qtmp4_reader_c::handle_trak_atom(qtmp4_demuxer_ptr &new_dmx,
|
|||||||
else if (atom.fourcc == FOURCC('m', 'd', 'i', 'a'))
|
else if (atom.fourcc == FOURCC('m', 'd', 'i', 'a'))
|
||||||
handle_mdia_atom(new_dmx, atom.to_parent(), level + 1);
|
handle_mdia_atom(new_dmx, atom.to_parent(), level + 1);
|
||||||
|
|
||||||
|
else if (atom.fourcc == FOURCC('e', 'd', 't', 's'))
|
||||||
|
handle_edts_atom(new_dmx, atom.to_parent(), level + 1);
|
||||||
|
|
||||||
skip_atom();
|
skip_atom();
|
||||||
parent.size -= atom.size;
|
parent.size -= atom.size;
|
||||||
}
|
}
|
||||||
@ -1049,11 +1180,11 @@ qtmp4_reader_c::handle_video_with_bframes(qtmp4_demuxer_ptr &dmx,
|
|||||||
|
|
||||||
if (dmx->pos == 0)
|
if (dmx->pos == 0)
|
||||||
dmx->v_dts_offset = (int64_t)dmx->frame_offset_table[0] *
|
dmx->v_dts_offset = (int64_t)dmx->frame_offset_table[0] *
|
||||||
1000000000 / dmx->timescale;
|
1000000000 / dmx->time_scale;
|
||||||
|
|
||||||
old_timecode = timecode;
|
old_timecode = timecode;
|
||||||
timecode += (int64_t)dmx->frame_offset_table[dmx->pos] *
|
timecode += (int64_t)dmx->frame_offset_table[dmx->pos] *
|
||||||
1000000000 / dmx->timescale - dmx->v_dts_offset;
|
1000000000 / dmx->time_scale - dmx->v_dts_offset;
|
||||||
|
|
||||||
PTZR(dmx->ptzr)->process(new packet_t(mem, timecode, duration, bref, fref));
|
PTZR(dmx->ptzr)->process(new packet_t(mem, timecode, duration, bref, fref));
|
||||||
}
|
}
|
||||||
@ -1081,7 +1212,7 @@ qtmp4_reader_c::read(generic_packetizer_c *ptzr,
|
|||||||
io->setFilePointer(dmx->chunk_table[dmx->pos].pos);
|
io->setFilePointer(dmx->chunk_table[dmx->pos].pos);
|
||||||
timecode = 1000000000 *
|
timecode = 1000000000 *
|
||||||
((uint64_t)dmx->chunk_table[dmx->pos].samples *
|
((uint64_t)dmx->chunk_table[dmx->pos].samples *
|
||||||
(uint64_t)dmx->duration) / (uint64_t)dmx->timescale;
|
(uint64_t)dmx->duration) / (uint64_t)dmx->time_scale;
|
||||||
|
|
||||||
if (dmx->sample_size != 1) {
|
if (dmx->sample_size != 1) {
|
||||||
if (!dmx->warning_printed) {
|
if (!dmx->warning_printed) {
|
||||||
@ -1130,7 +1261,7 @@ qtmp4_reader_c::read(generic_packetizer_c *ptzr,
|
|||||||
duration = 1000000000 *
|
duration = 1000000000 *
|
||||||
((uint64_t)dmx->chunk_table[dmx->pos + 1].samples *
|
((uint64_t)dmx->chunk_table[dmx->pos + 1].samples *
|
||||||
(uint64_t)dmx->duration) /
|
(uint64_t)dmx->duration) /
|
||||||
(uint64_t)dmx->timescale - timecode;
|
(uint64_t)dmx->time_scale - timecode;
|
||||||
else
|
else
|
||||||
duration = dmx->avg_duration;
|
duration = dmx->avg_duration;
|
||||||
dmx->avg_duration = (dmx->avg_duration * dmx->pos + duration) /
|
dmx->avg_duration = (dmx->avg_duration * dmx->pos + duration) /
|
||||||
@ -1152,11 +1283,37 @@ qtmp4_reader_c::read(generic_packetizer_c *ptzr,
|
|||||||
|
|
||||||
frame = dmx->pos;
|
frame = dmx->pos;
|
||||||
|
|
||||||
timecode = (int64_t)dmx->sample_table[frame].pts * 1000000000 /
|
if (('v' == dmx->type) && !dmx->editlist_table.empty()) {
|
||||||
dmx->timescale;
|
// find the right editlist_table entry:
|
||||||
|
if (frame < dmx->editlist_table[dmx->editlist_pos].start_frame)
|
||||||
|
dmx->editlist_pos = 0;
|
||||||
|
|
||||||
|
while (((dmx->editlist_table.size() - 1) > dmx->editlist_pos) &&
|
||||||
|
(frame >=
|
||||||
|
dmx->editlist_table[dmx->editlist_pos + 1].start_frame))
|
||||||
|
++dmx->editlist_pos;
|
||||||
|
|
||||||
|
if ((dmx->editlist_table[dmx->editlist_pos].start_frame +
|
||||||
|
dmx->editlist_table[dmx->editlist_pos].frames) <= frame)
|
||||||
|
continue; // EOF
|
||||||
|
|
||||||
|
// calc real frame index:
|
||||||
|
frame -= dmx->editlist_table[dmx->editlist_pos].start_frame;
|
||||||
|
frame += dmx->editlist_table[dmx->editlist_pos].start_sample;
|
||||||
|
|
||||||
|
// calc pts:
|
||||||
|
timecode =
|
||||||
|
(dmx->sample_table[frame].pts +
|
||||||
|
dmx->editlist_table[dmx->editlist_pos].pts_offset) *
|
||||||
|
1000000000ll / dmx->time_scale;
|
||||||
|
|
||||||
|
} else
|
||||||
|
timecode = (int64_t)dmx->sample_table[frame].pts * 1000000000 /
|
||||||
|
dmx->time_scale;
|
||||||
|
|
||||||
if ((frame + 1) < dmx->sample_table.size())
|
if ((frame + 1) < dmx->sample_table.size())
|
||||||
duration = (int64_t)dmx->sample_table[frame + 1].pts * 1000000000 /
|
duration = (int64_t)dmx->sample_table[frame + 1].pts * 1000000000 /
|
||||||
dmx->timescale - timecode;
|
dmx->time_scale - timecode;
|
||||||
else
|
else
|
||||||
duration = dmx->avg_duration;
|
duration = dmx->avg_duration;
|
||||||
dmx->avg_duration = (dmx->avg_duration * frame + duration) /
|
dmx->avg_duration = (dmx->avg_duration * frame + duration) /
|
||||||
@ -1167,7 +1324,7 @@ qtmp4_reader_c::read(generic_packetizer_c *ptzr,
|
|||||||
else {
|
else {
|
||||||
is_keyframe = false;
|
is_keyframe = false;
|
||||||
for (k = 0; k < dmx->keyframe_table.size(); k++)
|
for (k = 0; k < dmx->keyframe_table.size(); k++)
|
||||||
if (dmx->keyframe_table[k] == (frame + 1)) {
|
if (dmx->keyframe_table[k] == (dmx->pos + 1)) {
|
||||||
is_keyframe = true;
|
is_keyframe = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1574,7 +1731,7 @@ qtmp4_demuxer_t::calculate_fps() {
|
|||||||
if ((1 == durmap_table.size()) && (0 != durmap_table[0].duration) &&
|
if ((1 == durmap_table.size()) && (0 != durmap_table[0].duration) &&
|
||||||
((0 != sample_size) || (0 == frame_offset_table.size()))) {
|
((0 != sample_size) || (0 == frame_offset_table.size()))) {
|
||||||
// Constant FPS. Let's set the default duration.
|
// Constant FPS. Let's set the default duration.
|
||||||
fps = (double)timescale / (double)durmap_table[0].duration;
|
fps = (double)time_scale / (double)durmap_table[0].duration;
|
||||||
mxverb(3, PFX "calculate_fps: case 1: %f\n", fps);
|
mxverb(3, PFX "calculate_fps: case 1: %f\n", fps);
|
||||||
return fps;
|
return fps;
|
||||||
}
|
}
|
||||||
@ -1594,7 +1751,7 @@ qtmp4_demuxer_t::calculate_fps() {
|
|||||||
int64_t timecode;
|
int64_t timecode;
|
||||||
|
|
||||||
timecode = ((int64_t)sample_table[i].pts +
|
timecode = ((int64_t)sample_table[i].pts +
|
||||||
(int64_t)frame_offset_table[pos]) * 1000000000 / timescale;
|
(int64_t)frame_offset_table[pos]) * 1000000000 / time_scale;
|
||||||
if (timecode > max_tc)
|
if (timecode > max_tc)
|
||||||
max_tc = timecode;
|
max_tc = timecode;
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,9 @@ struct qt_editlist_t {
|
|||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
uint32_t speed;
|
uint32_t speed;
|
||||||
uint32_t frames;
|
uint32_t frames;
|
||||||
uint32_t start_sample;
|
uint64_t start_sample;
|
||||||
uint32_t start_frame;
|
uint64_t start_frame;
|
||||||
uint32_t pts_offset;
|
int64_t pts_offset;
|
||||||
|
|
||||||
qt_editlist_t():
|
qt_editlist_t():
|
||||||
duration(0), pos(0), speed(0), frames(0),
|
duration(0), pos(0), speed(0), frames(0),
|
||||||
@ -94,7 +94,7 @@ struct qtmp4_demuxer_t {
|
|||||||
char fourcc[4];
|
char fourcc[4];
|
||||||
uint32_t pos;
|
uint32_t pos;
|
||||||
|
|
||||||
uint32_t timescale;
|
uint32_t time_scale;
|
||||||
uint32_t global_duration;
|
uint32_t global_duration;
|
||||||
uint32_t avg_duration;
|
uint32_t avg_duration;
|
||||||
uint32_t sample_size;
|
uint32_t sample_size;
|
||||||
@ -110,6 +110,8 @@ struct qtmp4_demuxer_t {
|
|||||||
vector<qt_frame_offset_t> raw_frame_offset_table;
|
vector<qt_frame_offset_t> raw_frame_offset_table;
|
||||||
vector<int32_t> frame_offset_table;
|
vector<int32_t> frame_offset_table;
|
||||||
|
|
||||||
|
int editlist_pos;
|
||||||
|
|
||||||
esds_t esds;
|
esds_t esds;
|
||||||
bool esds_parsed;
|
bool esds_parsed;
|
||||||
|
|
||||||
@ -133,9 +135,9 @@ struct qtmp4_demuxer_t {
|
|||||||
|
|
||||||
qtmp4_demuxer_t():
|
qtmp4_demuxer_t():
|
||||||
ok(false), type('?'), id(0), pos(0),
|
ok(false), type('?'), id(0), pos(0),
|
||||||
timescale(1), global_duration(0), avg_duration(0), sample_size(0),
|
time_scale(1), global_duration(0), avg_duration(0), sample_size(0),
|
||||||
duration(0),
|
duration(0),
|
||||||
esds_parsed(false),
|
editlist_pos(0), esds_parsed(false),
|
||||||
v_stsd(NULL), v_stsd_size(0),
|
v_stsd(NULL), v_stsd_size(0),
|
||||||
v_width(0), v_height(0), v_bitdepth(0), v_dts_offset(0),
|
v_width(0), v_height(0), v_bitdepth(0), v_dts_offset(0),
|
||||||
avc_use_bframes(false),
|
avc_use_bframes(false),
|
||||||
@ -185,7 +187,7 @@ private:
|
|||||||
mm_io_c *io;
|
mm_io_c *io;
|
||||||
vector<qtmp4_demuxer_ptr> demuxers;
|
vector<qtmp4_demuxer_ptr> demuxers;
|
||||||
int64_t file_size, mdat_pos, mdat_size;
|
int64_t file_size, mdat_pos, mdat_size;
|
||||||
uint32_t compression_algorithm;
|
uint32_t time_scale, compression_algorithm;
|
||||||
int main_dmx;
|
int main_dmx;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -203,10 +205,13 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void parse_headers();
|
virtual void parse_headers();
|
||||||
virtual qt_atom_t read_atom();
|
virtual qt_atom_t read_atom(mm_io_c *read_from = NULL);
|
||||||
virtual void parse_header_priv_atoms(qtmp4_demuxer_ptr &dmx,
|
virtual void parse_video_header_priv_atoms(qtmp4_demuxer_ptr &dmx,
|
||||||
unsigned char *mem, int size,
|
unsigned char *mem, int size,
|
||||||
int level);
|
int level);
|
||||||
|
virtual void parse_audio_header_priv_atoms(qtmp4_demuxer_ptr &dmx,
|
||||||
|
unsigned char *mem, int size,
|
||||||
|
int level);
|
||||||
virtual bool parse_esds_atom(mm_mem_io_c &memio, qtmp4_demuxer_ptr &dmx,
|
virtual bool parse_esds_atom(mm_mem_io_c &memio, qtmp4_demuxer_ptr &dmx,
|
||||||
int level);
|
int level);
|
||||||
virtual uint32_t read_esds_descr_len(mm_mem_io_c &memio);
|
virtual uint32_t read_esds_descr_len(mm_mem_io_c &memio);
|
||||||
@ -253,8 +258,13 @@ protected:
|
|||||||
int level);
|
int level);
|
||||||
virtual void handle_trak_atom(qtmp4_demuxer_ptr &new_dmx, qt_atom_t parent,
|
virtual void handle_trak_atom(qtmp4_demuxer_ptr &new_dmx, qt_atom_t parent,
|
||||||
int level);
|
int level);
|
||||||
|
virtual void handle_edts_atom(qtmp4_demuxer_ptr &new_dmx, qt_atom_t parent,
|
||||||
|
int level);
|
||||||
|
virtual void handle_elst_atom(qtmp4_demuxer_ptr &new_dmx, qt_atom_t parent,
|
||||||
|
int level);
|
||||||
|
|
||||||
virtual void update_tables(qtmp4_demuxer_ptr &dmx);
|
virtual void update_tables(qtmp4_demuxer_ptr &dmx);
|
||||||
|
virtual void update_editlist_table(qtmp4_demuxer_ptr &dmx);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __R_QTMP4_H
|
#endif // __R_QTMP4_H
|
||||||
|
@ -62,3 +62,4 @@ T_212ssa_attachments:7555c28c8b18536880248bb88253fa49-9a5a5ed80808c9d448ca5b44b6
|
|||||||
T_213mp4_broken_pixel_dimensions:aad69e63948415b4b6407d493005b774:passed:20050919-094831
|
T_213mp4_broken_pixel_dimensions:aad69e63948415b4b6407d493005b774:passed:20050919-094831
|
||||||
T_214one_frame_avi:bea975277e91ded1a4085043e89608ce:passed:20051004-192755
|
T_214one_frame_avi:bea975277e91ded1a4085043e89608ce:passed:20051004-192755
|
||||||
T_215X_codec_extradata_avi:78d669fc6511b6b931cd0981e2e1cca9-3fabc505fdd377c05ce4557b1bc13545:passed:20051004-194707
|
T_215X_codec_extradata_avi:78d669fc6511b6b931cd0981e2e1cca9-3fabc505fdd377c05ce4557b1bc13545:passed:20051004-194707
|
||||||
|
T_216mp4_editlists:30ba8cadde2b266917ab105f5c754cb7:passed:20051118-191453
|
||||||
|
13
tests/test-216mp4_editlists.rb
Normal file
13
tests/test-216mp4_editlists.rb
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/ruby -w
|
||||||
|
|
||||||
|
class T_216mp4_editlists < Test
|
||||||
|
def description
|
||||||
|
return "mkvmerge / edit lists in MP4 and 'colr' before 'avcC' / in(MP4)"
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
merge("-A -S data/mp4/oops.mov")
|
||||||
|
return hash_tmp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user