mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 20:01:53 +00:00
Improved error resilience when reading Matroska files
This commit is contained in:
parent
98a10f6db8
commit
87815ef4af
@ -155,6 +155,7 @@ kax_reader_c::kax_reader_c(track_info_c &_ti)
|
||||
, writing_app_ver(-1)
|
||||
, m_attachment_id(0)
|
||||
, m_tags(NULL)
|
||||
, m_file_status(FILE_STATUS_MOREDATA)
|
||||
{
|
||||
init_l1_position_storage(deferred_l1_positions);
|
||||
init_l1_position_storage(handled_l1_positions);
|
||||
@ -171,8 +172,6 @@ kax_reader_c::~kax_reader_c() {
|
||||
for (i = 0; i < tracks.size(); i++)
|
||||
delete tracks[i];
|
||||
|
||||
delete saved_l1;
|
||||
delete in;
|
||||
delete es;
|
||||
delete segment;
|
||||
delete m_tags;
|
||||
@ -1090,21 +1089,22 @@ kax_reader_c::read_headers_seek_head(EbmlElement *&l0,
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
bool
|
||||
kax_reader_c::read_headers() {
|
||||
// Elements for different levels
|
||||
|
||||
bool exit_loop = false;
|
||||
KaxCluster *cluster = NULL;
|
||||
try {
|
||||
in = new mm_file_io_c(ti.fname);
|
||||
in = mm_io_cptr(new mm_file_io_c(ti.fname));
|
||||
file_size = in->get_size();
|
||||
es = new EbmlStream(*in);
|
||||
m_in_file = kax_file_cptr(new kax_file_c(in));
|
||||
|
||||
// Find the EbmlHead element. Must be the first one.
|
||||
EbmlElement *l0 = es->FindNextID(EbmlHead::ClassInfos, 0xFFFFFFFFFFFFFFFFLL);
|
||||
if (NULL == l0) {
|
||||
mxwarn(Y("matroska_reader: no EBML head found.\n"));
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't verify its data for now.
|
||||
@ -1117,12 +1117,12 @@ kax_reader_c::read_headers() {
|
||||
if (NULL == l0) {
|
||||
if (verbose)
|
||||
mxwarn(Y("matroska_reader: No segment found.\n"));
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
if (!(EbmlId(*l0) == KaxSegment::ClassInfos.GlobalId)) {
|
||||
if (verbose)
|
||||
mxwarn(Y("matroska_reader: No segment found.\n"));
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
mxverb(2, "matroska_reader: + a segment...\n");
|
||||
|
||||
@ -1130,7 +1130,6 @@ kax_reader_c::read_headers() {
|
||||
|
||||
// We've got our segment, so let's find the tracks
|
||||
int upper_lvl_el = 0;
|
||||
exit_loop = false;
|
||||
tc_scale = TIMECODE_SCALE;
|
||||
EbmlElement *l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true, 1);
|
||||
|
||||
@ -1157,13 +1156,12 @@ kax_reader_c::read_headers() {
|
||||
|
||||
else if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
|
||||
mxverb(2, "matroska_reader: |+ found cluster, headers are parsed completely\n");
|
||||
saved_l1 = l1;
|
||||
exit_loop = true;
|
||||
cluster = static_cast<KaxCluster *>(l1);
|
||||
|
||||
} else
|
||||
l1->SkipData(*es, l1->Generic().Context);
|
||||
|
||||
if (exit_loop) // we've found the first cluster, so get out
|
||||
if (NULL != cluster) // we've found the first cluster, so get out
|
||||
break;
|
||||
|
||||
if (!in_parent(l0)) {
|
||||
@ -1193,20 +1191,20 @@ kax_reader_c::read_headers() {
|
||||
} // while (l1 != NULL)
|
||||
|
||||
foreach(int64_t position, deferred_l1_positions[dl1t_tracks])
|
||||
read_headers_tracks(in, l0, position);
|
||||
read_headers_tracks(in.get_object(), l0, position);
|
||||
|
||||
if (!ti.no_attachments) {
|
||||
foreach(int64_t position, deferred_l1_positions[dl1t_attachments])
|
||||
handle_attachments(in, l0, position);
|
||||
handle_attachments(in.get_object(), l0, position);
|
||||
}
|
||||
|
||||
if (!ti.no_chapters) {
|
||||
foreach(int64_t position, deferred_l1_positions[dl1t_chapters])
|
||||
handle_chapters(in, l0, position);
|
||||
handle_chapters(in.get_object(), l0, position);
|
||||
}
|
||||
|
||||
foreach(int64_t position, deferred_l1_positions[dl1t_tags])
|
||||
handle_tags(in, l0, position);
|
||||
handle_tags(in.get_object(), l0, position);
|
||||
|
||||
if (!ti.no_global_tags)
|
||||
process_global_tags();
|
||||
@ -1215,12 +1213,15 @@ kax_reader_c::read_headers() {
|
||||
mxerror(Y("matroska_reader: caught exception\n"));
|
||||
}
|
||||
|
||||
if (!exit_loop) // We have NOT found a cluster!
|
||||
return 0;
|
||||
if (NULL == cluster) // We have NOT found a cluster!
|
||||
return false;
|
||||
|
||||
verify_tracks();
|
||||
|
||||
return 1;
|
||||
in->setFilePointer(cluster->GetElementPosition(), seek_beginning);
|
||||
delete cluster;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -1666,114 +1667,80 @@ kax_reader_c::create_mpeg4_p10_video_packetizer(kax_track_t *t,
|
||||
void
|
||||
kax_reader_c::read_first_frames(kax_track_t *t,
|
||||
unsigned num_wanted) {
|
||||
if ((t->first_frames_data.size() >= num_wanted) || (NULL == saved_l1))
|
||||
if (t->first_frames_data.size() >= num_wanted)
|
||||
return;
|
||||
|
||||
std::map<int64_t, int> frames_by_track_id;
|
||||
|
||||
in->save_pos(saved_l1->GetElementPosition());
|
||||
in->save_pos();
|
||||
|
||||
try {
|
||||
int upper_lvl_el = 0;
|
||||
EbmlElement *l1 = es->FindNextElement(segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true);
|
||||
EbmlElement *l2 = NULL;
|
||||
while (true) {
|
||||
KaxCluster *cluster = m_in_file->read_next_cluster();
|
||||
if (NULL == cluster)
|
||||
return;
|
||||
|
||||
while ((l1 != NULL) && (upper_lvl_el <= 0)) {
|
||||
if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
|
||||
cluster = (KaxCluster *)l1;
|
||||
cluster->Read(*es, KaxCluster::ClassInfos.Context, upper_lvl_el, l2, true);
|
||||
KaxClusterTimecode *ctc = static_cast<KaxClusterTimecode *> (cluster->FindFirstElt(KaxClusterTimecode::ClassInfos, false));
|
||||
if (NULL != ctc) {
|
||||
cluster_tc = uint64(*ctc);
|
||||
cluster->InitTimecode(cluster_tc, tc_scale);
|
||||
}
|
||||
|
||||
KaxClusterTimecode *ctc = static_cast<KaxClusterTimecode *> (cluster->FindFirstElt(KaxClusterTimecode::ClassInfos, false));
|
||||
if (NULL != ctc) {
|
||||
cluster_tc = uint64(*ctc);
|
||||
cluster->InitTimecode(cluster_tc, tc_scale);
|
||||
}
|
||||
int bgidx;
|
||||
for (bgidx = 0; bgidx < cluster->ListSize(); bgidx++) {
|
||||
if ((EbmlId(*(*cluster)[bgidx]) == KaxSimpleBlock::ClassInfos.GlobalId)) {
|
||||
KaxSimpleBlock *block_simple = static_cast<KaxSimpleBlock *>((*cluster)[bgidx]);
|
||||
|
||||
int bgidx;
|
||||
for (bgidx = 0; bgidx < cluster->ListSize(); bgidx++) {
|
||||
if ((EbmlId(*(*cluster)[bgidx]) == KaxSimpleBlock::ClassInfos.GlobalId)) {
|
||||
KaxSimpleBlock *block_simple = static_cast<KaxSimpleBlock *>((*cluster)[bgidx]);
|
||||
block_simple->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block_simple->TrackNum());
|
||||
|
||||
block_simple->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block_simple->TrackNum());
|
||||
if ((NULL == block_track) || (0 == block_simple->NumberFrames()))
|
||||
continue;
|
||||
|
||||
if ((NULL == block_track) || (0 == block_simple->NumberFrames()))
|
||||
for (int frame_idx = 0; block_simple->NumberFrames() > frame_idx; ++frame_idx) {
|
||||
frames_by_track_id[ block_simple->TrackNum() ]++;
|
||||
|
||||
if (frames_by_track_id[ block_simple->TrackNum() ] <= block_track->first_frames_data.size())
|
||||
continue;
|
||||
|
||||
for (int frame_idx = 0; block_simple->NumberFrames() > frame_idx; ++frame_idx) {
|
||||
frames_by_track_id[ block_simple->TrackNum() ]++;
|
||||
DataBuffer &data_buffer = block_simple->GetBuffer(frame_idx);
|
||||
block_track->first_frames_data.push_back(memory_cptr(new memory_c(data_buffer.Buffer(), data_buffer.Size())));
|
||||
block_track->content_decoder.reverse(block_track->first_frames_data.back(), CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
block_track->first_frames_data.back()->grab();
|
||||
}
|
||||
|
||||
if (frames_by_track_id[ block_simple->TrackNum() ] <= block_track->first_frames_data.size())
|
||||
continue;
|
||||
} else if ((EbmlId(*(*cluster)[bgidx]) == KaxBlockGroup::ClassInfos.GlobalId)) {
|
||||
KaxBlockGroup *block_group = static_cast<KaxBlockGroup *>((*cluster)[bgidx]);
|
||||
KaxBlock *block = static_cast<KaxBlock *>(block_group->FindFirstElt(KaxBlock::ClassInfos, false));
|
||||
|
||||
DataBuffer &data_buffer = block_simple->GetBuffer(frame_idx);
|
||||
block_track->first_frames_data.push_back(memory_cptr(new memory_c(data_buffer.Buffer(), data_buffer.Size())));
|
||||
block_track->content_decoder.reverse(block_track->first_frames_data.back(), CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
block_track->first_frames_data.back()->grab();
|
||||
}
|
||||
if (NULL == block)
|
||||
continue;
|
||||
|
||||
if ((t == block_track) && (block_track->first_frames_data.size() >= num_wanted)) {
|
||||
in->restore_pos();
|
||||
delete l1;
|
||||
return;
|
||||
}
|
||||
block->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block->TrackNum());
|
||||
|
||||
} else if ((EbmlId(*(*cluster)[bgidx]) == KaxBlockGroup::ClassInfos.GlobalId)) {
|
||||
KaxBlockGroup *block_group = static_cast<KaxBlockGroup *>((*cluster)[bgidx]);
|
||||
KaxBlock *block = static_cast<KaxBlock *>(block_group->FindFirstElt(KaxBlock::ClassInfos, false));
|
||||
if ((NULL == block_track) || (0 == block->NumberFrames()))
|
||||
continue;
|
||||
|
||||
if (NULL == block)
|
||||
for (int frame_idx = 0; block->NumberFrames() > frame_idx; ++frame_idx) {
|
||||
frames_by_track_id[ block->TrackNum() ]++;
|
||||
|
||||
if (frames_by_track_id[ block->TrackNum() ] <= block_track->first_frames_data.size())
|
||||
continue;
|
||||
|
||||
block->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block->TrackNum());
|
||||
|
||||
if ((NULL == block_track) || (0 == block->NumberFrames()))
|
||||
continue;
|
||||
|
||||
for (int frame_idx = 0; block->NumberFrames() > frame_idx; ++frame_idx) {
|
||||
frames_by_track_id[ block->TrackNum() ]++;
|
||||
|
||||
if (frames_by_track_id[ block->TrackNum() ] <= block_track->first_frames_data.size())
|
||||
continue;
|
||||
|
||||
DataBuffer &data_buffer = block->GetBuffer(frame_idx);
|
||||
block_track->first_frames_data.push_back(memory_cptr(new memory_c(data_buffer.Buffer(), data_buffer.Size())));
|
||||
block_track->content_decoder.reverse(block_track->first_frames_data.back(), CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
block_track->first_frames_data.back()->grab();
|
||||
}
|
||||
|
||||
if ((t == block_track) && (block_track->first_frames_data.size() >= num_wanted)) {
|
||||
in->restore_pos();
|
||||
delete l1;
|
||||
return;
|
||||
}
|
||||
DataBuffer &data_buffer = block->GetBuffer(frame_idx);
|
||||
block_track->first_frames_data.push_back(memory_cptr(new memory_c(data_buffer.Buffer(), data_buffer.Size())));
|
||||
block_track->content_decoder.reverse(block_track->first_frames_data.back(), CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
block_track->first_frames_data.back()->grab();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l1->SkipData(*es, l1->Generic().Context);
|
||||
delete cluster;
|
||||
|
||||
if (!in_parent(segment)) {
|
||||
delete l1;
|
||||
if (t->first_frames_data.size() >= num_wanted)
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 < upper_lvl_el) {
|
||||
upper_lvl_el--;
|
||||
if (0 < upper_lvl_el)
|
||||
break;
|
||||
delete l1;
|
||||
l1 = l2;
|
||||
break;
|
||||
|
||||
} else if (0 > upper_lvl_el)
|
||||
upper_lvl_el++;
|
||||
|
||||
delete l1;
|
||||
l1 = es->FindNextElement(segment->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true);
|
||||
} // while (l1 != NULL)
|
||||
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
@ -1783,14 +1750,9 @@ kax_reader_c::read_first_frames(kax_track_t *t,
|
||||
file_status_e
|
||||
kax_reader_c::read(generic_packetizer_c *requested_ptzr,
|
||||
bool force) {
|
||||
if (tracks.size() == 0)
|
||||
if (tracks.empty() || (FILE_STATUS_DONE == m_file_status))
|
||||
return FILE_STATUS_DONE;
|
||||
|
||||
if (NULL == saved_l1) { // We're done.
|
||||
flush_packetizers();
|
||||
return FILE_STATUS_DONE;
|
||||
}
|
||||
|
||||
int64_t num_queued_bytes = get_queued_bytes();
|
||||
if (!force && (20 * 1024 * 1024 < num_queued_bytes)) {
|
||||
kax_track_t *requested_ptzr_track = ptzr_to_track_map[requested_ptzr];
|
||||
@ -1798,299 +1760,43 @@ kax_reader_c::read(generic_packetizer_c *requested_ptzr,
|
||||
return FILE_STATUS_HOLDING;
|
||||
}
|
||||
|
||||
bool found_cluster = false;
|
||||
int upper_lvl_el = 0;
|
||||
EbmlElement *l0 = segment;
|
||||
EbmlElement *l1 = saved_l1;
|
||||
EbmlElement *l2 = NULL;
|
||||
saved_l1 = NULL;
|
||||
|
||||
try {
|
||||
while ((NULL != l1) && (0 >= upper_lvl_el)) {
|
||||
if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
|
||||
found_cluster = true;
|
||||
cluster = (KaxCluster *)l1;
|
||||
cluster->Read(*es, KaxCluster::ClassInfos.Context, upper_lvl_el, l2, true);
|
||||
KaxCluster *cluster = m_in_file->read_next_cluster();
|
||||
if (NULL == cluster) {
|
||||
flush_packetizers();
|
||||
|
||||
KaxClusterTimecode *ctc = static_cast<KaxClusterTimecode *>(cluster->FindFirstElt(KaxClusterTimecode::ClassInfos, false));
|
||||
if (NULL == ctc)
|
||||
mxerror(Y("r_matroska: Cluster does not contain a cluster timecode. File is broken. Aborting.\n"));
|
||||
m_file_status = FILE_STATUS_DONE;
|
||||
return FILE_STATUS_DONE;
|
||||
}
|
||||
|
||||
cluster_tc = uint64(*ctc);
|
||||
cluster->InitTimecode(cluster_tc, tc_scale);
|
||||
if (-1 == first_timecode) {
|
||||
first_timecode = cluster_tc * tc_scale;
|
||||
KaxClusterTimecode *ctc = static_cast<KaxClusterTimecode *>(cluster->FindFirstElt(KaxClusterTimecode::ClassInfos, false));
|
||||
if (NULL == ctc)
|
||||
mxerror(Y("r_matroska: Cluster does not contain a cluster timecode. File is broken. Aborting.\n"));
|
||||
|
||||
// If we're appending this file to another one then the core
|
||||
// needs the timecodes shifted to zero.
|
||||
if (appending && (NULL != chapters) && (0 < first_timecode))
|
||||
adjust_chapter_timecodes(*chapters, -first_timecode);
|
||||
}
|
||||
cluster_tc = uint64(*ctc);
|
||||
cluster->InitTimecode(cluster_tc, tc_scale);
|
||||
if (-1 == first_timecode) {
|
||||
first_timecode = cluster_tc * tc_scale;
|
||||
|
||||
int bgidx;
|
||||
for (bgidx = 0; bgidx < cluster->ListSize(); bgidx++) {
|
||||
int64_t block_duration = -1;
|
||||
int64_t block_bref = VFT_IFRAME;
|
||||
int64_t block_fref = VFT_NOBFRAME;
|
||||
bool bref_found = false;
|
||||
bool fref_found = false;
|
||||
// If we're appending this file to another one then the core
|
||||
// needs the timecodes shifted to zero.
|
||||
if (appending && (NULL != chapters) && (0 < first_timecode))
|
||||
adjust_chapter_timecodes(*chapters, -first_timecode);
|
||||
}
|
||||
|
||||
if ((EbmlId(*(*cluster)[bgidx]) == KaxSimpleBlock::ClassInfos.GlobalId)) {
|
||||
KaxSimpleBlock *block_simple = static_cast<KaxSimpleBlock *>((*cluster)[bgidx]);
|
||||
int bgidx;
|
||||
for (bgidx = 0; bgidx < cluster->ListSize(); bgidx++) {
|
||||
EbmlElement *element = (*cluster)[bgidx];
|
||||
|
||||
block_simple->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block_simple->TrackNum());
|
||||
if (EbmlId(*element) == KaxSimpleBlock::ClassInfos.GlobalId)
|
||||
process_simple_block(cluster, static_cast<KaxSimpleBlock *>(element));
|
||||
|
||||
if (NULL == block_track) {
|
||||
mxwarn_fn(ti.fname,
|
||||
boost::format(Y("A block was found at timestamp %1% for track number %2%. However, no headers where found for that track number. "
|
||||
"The block will be skipped.\n")) % format_timecode(block_simple->GlobalTimecode()) % block_simple->TrackNum());
|
||||
continue;
|
||||
}
|
||||
else if (EbmlId(*element) == KaxBlockGroup::ClassInfos.GlobalId)
|
||||
process_block_group(cluster, static_cast<KaxBlockGroup *>(element));
|
||||
}
|
||||
|
||||
if (0 != block_track->v_frate)
|
||||
block_duration = (int64_t)(1000000000.0 / block_track->v_frate);
|
||||
int64_t frame_duration = (block_duration == -1) ? 0 : block_duration;
|
||||
|
||||
if (('s' == block_track->type) && (-1 == block_duration))
|
||||
block_duration = 0;
|
||||
|
||||
if (block_track->ignore_duration_hack) {
|
||||
frame_duration = 0;
|
||||
if (0 < block_duration)
|
||||
block_duration = 0;
|
||||
}
|
||||
|
||||
if (!block_simple->IsKeyframe()) {
|
||||
if (block_simple->IsDiscardable()) {
|
||||
block_fref = block_track->previous_timecode;
|
||||
fref_found = true;
|
||||
} else {
|
||||
block_bref = block_track->previous_timecode;
|
||||
bref_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
last_timecode = block_simple->GlobalTimecode();
|
||||
|
||||
// If we're appending this file to another one then the core
|
||||
// needs the timecodes shifted to zero.
|
||||
if (appending)
|
||||
last_timecode -= first_timecode;
|
||||
|
||||
if ((-1 != block_track->ptzr) && block_track->passthrough) {
|
||||
// The handling for passthrough is a bit different. We don't have
|
||||
// any special cases, e.g. 0 terminating a string for the subs
|
||||
// and stuff. Just pass everything through as it is.
|
||||
int i;
|
||||
for (i = 0; i < (int)block_simple->NumberFrames(); i++) {
|
||||
DataBuffer &data = block_simple->GetBuffer(i);
|
||||
packet_t *packet = new packet_t(new memory_c((unsigned char *)data.Buffer(), data.Size(), false),
|
||||
last_timecode + i * frame_duration, block_duration, block_bref, block_fref);
|
||||
|
||||
((passthrough_packetizer_c *)PTZR(block_track->ptzr))->process(packet_cptr(packet));
|
||||
}
|
||||
|
||||
} else if (-1 != block_track->ptzr) {
|
||||
int i;
|
||||
for (i = 0; i < (int)block_simple->NumberFrames(); i++) {
|
||||
DataBuffer &data_buffer = block_simple->GetBuffer(i);
|
||||
memory_cptr data(new memory_c(data_buffer.Buffer(), data_buffer.Size(), false));
|
||||
block_track->content_decoder.reverse(data, CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
|
||||
if (('s' == block_track->type) && ('t' == block_track->sub_type)) {
|
||||
if ((2 < data->get_size()) || ((0 < data->get_size()) && (' ' != *data->get_buffer()) && (0 != *data->get_buffer()) && !iscr(*data->get_buffer()))) {
|
||||
char *lines = (char *)safemalloc(data->get_size() + 1);
|
||||
lines[data->get_size()] = 0;
|
||||
memcpy(lines, data->get_buffer(), data->get_size());
|
||||
|
||||
PTZR(block_track->ptzr)-> process(new packet_t(new memory_c((unsigned char *)lines, 0, true), last_timecode, block_duration, block_bref, block_fref));
|
||||
}
|
||||
|
||||
} else {
|
||||
packet_cptr packet(new packet_t(data, last_timecode + i * frame_duration, block_duration, block_bref, block_fref));
|
||||
PTZR(block_track->ptzr)->process(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_track->previous_timecode = (int64_t)last_timecode;
|
||||
block_track->units_processed += block_simple->NumberFrames();
|
||||
|
||||
} else if ((EbmlId(*(*cluster)[bgidx]) == KaxBlockGroup::ClassInfos.GlobalId)) {
|
||||
KaxBlockGroup *block_group = static_cast<KaxBlockGroup *>((*cluster)[bgidx]);
|
||||
KaxReferenceBlock *ref_block = static_cast<KaxReferenceBlock *>(block_group->FindFirstElt(KaxReferenceBlock::ClassInfos, false));
|
||||
|
||||
while (NULL != ref_block) {
|
||||
if (0 >= int64(*ref_block)) {
|
||||
block_bref = int64(*ref_block) * tc_scale;
|
||||
bref_found = true;
|
||||
} else {
|
||||
block_fref = int64(*ref_block) * tc_scale;
|
||||
fref_found = true;
|
||||
}
|
||||
|
||||
ref_block = static_cast<KaxReferenceBlock *>(block_group->FindNextElt(*ref_block, false));
|
||||
}
|
||||
|
||||
KaxBlock *block = static_cast<KaxBlock *>(block_group->FindFirstElt(KaxBlock::ClassInfos, false));
|
||||
if (NULL == block) {
|
||||
mxwarn_fn(ti.fname,
|
||||
boost::format(Y("A block group was found at position %1%, but no block element was found inside it. This might make mkvmerge crash.\n"))
|
||||
% block_group->GetElementPosition());
|
||||
continue;
|
||||
}
|
||||
|
||||
block->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block->TrackNum());
|
||||
|
||||
if (NULL == block_track) {
|
||||
mxwarn_fn(ti.fname,
|
||||
boost::format(Y("A block was found at timestamp %1% for track number %2%. However, no headers where found for that track number. "
|
||||
"The block will be skipped.\n")) % format_timecode(block->GlobalTimecode()) % block->TrackNum());
|
||||
continue;
|
||||
}
|
||||
|
||||
KaxBlockAdditions *blockadd = static_cast<KaxBlockAdditions *>(block_group->FindFirstElt(KaxBlockAdditions::ClassInfos, false));
|
||||
KaxBlockDuration *duration = static_cast<KaxBlockDuration *>(block_group->FindFirstElt(KaxBlockDuration::ClassInfos, false));
|
||||
|
||||
if (NULL != duration)
|
||||
block_duration = (int64_t)uint64(*duration) * tc_scale / block->NumberFrames();
|
||||
else if (0 != block_track->v_frate)
|
||||
block_duration = (int64_t)(1000000000.0 / block_track->v_frate);
|
||||
int64_t frame_duration = (block_duration == -1) ? 0 : block_duration;
|
||||
|
||||
if (('s' == block_track->type) && (-1 == block_duration))
|
||||
block_duration = 0;
|
||||
|
||||
if (block_track->ignore_duration_hack) {
|
||||
frame_duration = 0;
|
||||
if (0 < block_duration)
|
||||
block_duration = 0;
|
||||
}
|
||||
|
||||
last_timecode = block->GlobalTimecode();
|
||||
// If we're appending this file to another one then the core
|
||||
// needs the timecodes shifted to zero.
|
||||
if (appending)
|
||||
last_timecode -= first_timecode;
|
||||
|
||||
KaxCodecState *codec_state = static_cast<KaxCodecState *>(block_group->FindFirstElt(KaxCodecState::ClassInfos));
|
||||
if ((NULL != codec_state) && !hack_engaged(ENGAGE_USE_CODEC_STATE))
|
||||
mxerror_tid(ti.fname, block_track->tnum,
|
||||
Y("This track uses a Matroska feature called 'Codec state elements'. mkvmerge supports these but "
|
||||
"this feature has not been turned on with the option '--engage use_codec_state'.\n"));
|
||||
|
||||
if ((-1 != block_track->ptzr) && block_track->passthrough) {
|
||||
// The handling for passthrough is a bit different. We don't have
|
||||
// any special cases, e.g. 0 terminating a string for the subs
|
||||
// and stuff. Just pass everything through as it is.
|
||||
if (bref_found)
|
||||
block_bref += last_timecode;
|
||||
if (fref_found)
|
||||
block_fref += last_timecode;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < (int)block->NumberFrames(); i++) {
|
||||
DataBuffer &data = block->GetBuffer(i);
|
||||
packet_t *packet = new packet_t(new memory_c((unsigned char *)data.Buffer(), data.Size(), false),
|
||||
last_timecode + i * frame_duration, block_duration, block_bref, block_fref);
|
||||
packet->duration_mandatory = duration != NULL;
|
||||
|
||||
if (NULL != codec_state)
|
||||
packet->codec_state = clone_memory(codec_state->GetBuffer(), codec_state->GetSize());
|
||||
|
||||
((passthrough_packetizer_c *)PTZR(block_track->ptzr))->process(packet_cptr(packet));
|
||||
}
|
||||
|
||||
} else if (-1 != block_track->ptzr) {
|
||||
if (bref_found) {
|
||||
if (FOURCC('r', 'e', 'a', 'l') == block_track->a_formattag)
|
||||
block_bref = block_track->previous_timecode;
|
||||
else
|
||||
block_bref += (int64_t)last_timecode;
|
||||
}
|
||||
if (fref_found)
|
||||
block_fref += (int64_t)last_timecode;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < (int)block->NumberFrames(); i++) {
|
||||
DataBuffer &data_buffer = block->GetBuffer(i);
|
||||
memory_cptr data(new memory_c(data_buffer.Buffer(), data_buffer.Size(), false));
|
||||
block_track->content_decoder.reverse(data, CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
|
||||
if (('s' == block_track->type) && ('t' == block_track->sub_type)) {
|
||||
if ((2 < data->get_size()) || ((0 < data->get_size()) && (' ' != *data->get_buffer()) && (0 != *data->get_buffer()) && !iscr(*data->get_buffer()))) {
|
||||
char *lines = (char *)safemalloc(data->get_size() + 1);
|
||||
lines[data->get_size()] = 0;
|
||||
memcpy(lines, data->get_buffer(), data->get_size());
|
||||
|
||||
packet_t *packet = new packet_t(new memory_c((unsigned char *)lines, 0, true), last_timecode, block_duration, block_bref, block_fref);
|
||||
if (NULL != codec_state)
|
||||
packet->codec_state = clone_memory(codec_state->GetBuffer(), codec_state->GetSize());
|
||||
|
||||
PTZR(block_track->ptzr)->process(packet);
|
||||
}
|
||||
|
||||
} else {
|
||||
packet_cptr packet(new packet_t(data, last_timecode + i * frame_duration, block_duration, block_bref, block_fref));
|
||||
|
||||
if (NULL != codec_state)
|
||||
packet->codec_state = clone_memory(codec_state->GetBuffer(), codec_state->GetSize());
|
||||
|
||||
if (blockadd) {
|
||||
int k;
|
||||
for (k = 0; k < blockadd->ListSize(); k++) {
|
||||
if (!(is_id((*blockadd)[k], KaxBlockMore)))
|
||||
continue;
|
||||
|
||||
KaxBlockMore *blockmore = static_cast<KaxBlockMore *>((*blockadd)[k]);
|
||||
KaxBlockAdditional *blockadd_data = &GetChild<KaxBlockAdditional>(*blockmore);
|
||||
|
||||
memory_cptr blockadded(new memory_c(blockadd_data->GetBuffer(), blockadd_data->GetSize(), false));
|
||||
block_track->content_decoder.reverse(blockadded, CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
|
||||
packet->data_adds.push_back(blockadded);
|
||||
}
|
||||
}
|
||||
|
||||
PTZR(block_track->ptzr)->process(packet);
|
||||
}
|
||||
}
|
||||
|
||||
block_track->previous_timecode = (int64_t)last_timecode;
|
||||
block_track->units_processed += block->NumberFrames();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l1->SkipData(*es, l1->Generic().Context);
|
||||
|
||||
if (!in_parent(l0)) {
|
||||
delete l1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (0 < upper_lvl_el) {
|
||||
upper_lvl_el--;
|
||||
if (0 < upper_lvl_el)
|
||||
break;
|
||||
delete l1;
|
||||
saved_l1 = l2;
|
||||
break;
|
||||
|
||||
} else if (0 > upper_lvl_el)
|
||||
upper_lvl_el++;
|
||||
|
||||
delete l1;
|
||||
l1 = es->FindNextElement(l0->Generic().Context, upper_lvl_el, 0xFFFFFFFFL, true);
|
||||
if (found_cluster) {
|
||||
saved_l1 = l1;
|
||||
break;
|
||||
}
|
||||
|
||||
} // while (l1 != NULL)
|
||||
in->setFilePointer(cluster->GetElementPosition() + cluster->ElementSize(), seek_beginning);
|
||||
delete cluster;
|
||||
|
||||
} catch (...) {
|
||||
mxwarn(Y("matroska_reader: caught exception\n"));
|
||||
@ -2098,11 +1804,248 @@ kax_reader_c::read(generic_packetizer_c *requested_ptzr,
|
||||
return FILE_STATUS_DONE;
|
||||
}
|
||||
|
||||
if (found_cluster)
|
||||
return FILE_STATUS_MOREDATA;
|
||||
return FILE_STATUS_MOREDATA;
|
||||
}
|
||||
|
||||
flush_packetizers();
|
||||
return FILE_STATUS_DONE;
|
||||
void
|
||||
kax_reader_c::process_simple_block(KaxCluster *cluster,
|
||||
KaxSimpleBlock *block_simple) {
|
||||
int64_t block_duration = -1;
|
||||
int64_t block_bref = VFT_IFRAME;
|
||||
int64_t block_fref = VFT_NOBFRAME;
|
||||
bool bref_found = false;
|
||||
bool fref_found = false;
|
||||
|
||||
block_simple->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block_simple->TrackNum());
|
||||
|
||||
if (NULL == block_track) {
|
||||
mxwarn_fn(ti.fname,
|
||||
boost::format(Y("A block was found at timestamp %1% for track number %2%. However, no headers where found for that track number. "
|
||||
"The block will be skipped.\n")) % format_timecode(block_simple->GlobalTimecode()) % block_simple->TrackNum());
|
||||
return;
|
||||
}
|
||||
|
||||
if (0 != block_track->v_frate)
|
||||
block_duration = (int64_t)(1000000000.0 / block_track->v_frate);
|
||||
int64_t frame_duration = (block_duration == -1) ? 0 : block_duration;
|
||||
|
||||
if (('s' == block_track->type) && (-1 == block_duration))
|
||||
block_duration = 0;
|
||||
|
||||
if (block_track->ignore_duration_hack) {
|
||||
frame_duration = 0;
|
||||
if (0 < block_duration)
|
||||
block_duration = 0;
|
||||
}
|
||||
|
||||
if (!block_simple->IsKeyframe()) {
|
||||
if (block_simple->IsDiscardable()) {
|
||||
block_fref = block_track->previous_timecode;
|
||||
fref_found = true;
|
||||
} else {
|
||||
block_bref = block_track->previous_timecode;
|
||||
bref_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
last_timecode = block_simple->GlobalTimecode();
|
||||
|
||||
// If we're appending this file to another one then the core
|
||||
// needs the timecodes shifted to zero.
|
||||
if (appending)
|
||||
last_timecode -= first_timecode;
|
||||
|
||||
if ((-1 != block_track->ptzr) && block_track->passthrough) {
|
||||
// The handling for passthrough is a bit different. We don't have
|
||||
// any special cases, e.g. 0 terminating a string for the subs
|
||||
// and stuff. Just pass everything through as it is.
|
||||
int i;
|
||||
for (i = 0; i < (int)block_simple->NumberFrames(); i++) {
|
||||
DataBuffer &data = block_simple->GetBuffer(i);
|
||||
packet_t *packet = new packet_t(new memory_c((unsigned char *)data.Buffer(), data.Size(), false),
|
||||
last_timecode + i * frame_duration, block_duration, block_bref, block_fref);
|
||||
|
||||
((passthrough_packetizer_c *)PTZR(block_track->ptzr))->process(packet_cptr(packet));
|
||||
}
|
||||
|
||||
} else if (-1 != block_track->ptzr) {
|
||||
int i;
|
||||
for (i = 0; i < (int)block_simple->NumberFrames(); i++) {
|
||||
DataBuffer &data_buffer = block_simple->GetBuffer(i);
|
||||
memory_cptr data(new memory_c(data_buffer.Buffer(), data_buffer.Size(), false));
|
||||
block_track->content_decoder.reverse(data, CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
|
||||
if (('s' == block_track->type) && ('t' == block_track->sub_type)) {
|
||||
if ((2 < data->get_size()) || ((0 < data->get_size()) && (' ' != *data->get_buffer()) && (0 != *data->get_buffer()) && !iscr(*data->get_buffer()))) {
|
||||
char *lines = (char *)safemalloc(data->get_size() + 1);
|
||||
lines[data->get_size()] = 0;
|
||||
memcpy(lines, data->get_buffer(), data->get_size());
|
||||
|
||||
PTZR(block_track->ptzr)-> process(new packet_t(new memory_c((unsigned char *)lines, 0, true), last_timecode, block_duration, block_bref, block_fref));
|
||||
}
|
||||
|
||||
} else {
|
||||
packet_cptr packet(new packet_t(data, last_timecode + i * frame_duration, block_duration, block_bref, block_fref));
|
||||
PTZR(block_track->ptzr)->process(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
block_track->previous_timecode = (int64_t)last_timecode;
|
||||
block_track->units_processed += block_simple->NumberFrames();
|
||||
}
|
||||
|
||||
void
|
||||
kax_reader_c::process_block_group(KaxCluster *cluster,
|
||||
KaxBlockGroup *block_group) {
|
||||
int64_t block_duration = -1;
|
||||
int64_t block_bref = VFT_IFRAME;
|
||||
int64_t block_fref = VFT_NOBFRAME;
|
||||
bool bref_found = false;
|
||||
bool fref_found = false;
|
||||
KaxReferenceBlock *ref_block = static_cast<KaxReferenceBlock *>(block_group->FindFirstElt(KaxReferenceBlock::ClassInfos, false));
|
||||
|
||||
while (NULL != ref_block) {
|
||||
if (0 >= int64(*ref_block)) {
|
||||
block_bref = int64(*ref_block) * tc_scale;
|
||||
bref_found = true;
|
||||
} else {
|
||||
block_fref = int64(*ref_block) * tc_scale;
|
||||
fref_found = true;
|
||||
}
|
||||
|
||||
ref_block = static_cast<KaxReferenceBlock *>(block_group->FindNextElt(*ref_block, false));
|
||||
}
|
||||
|
||||
KaxBlock *block = static_cast<KaxBlock *>(block_group->FindFirstElt(KaxBlock::ClassInfos, false));
|
||||
if (NULL == block) {
|
||||
mxwarn_fn(ti.fname,
|
||||
boost::format(Y("A block group was found at position %1%, but no block element was found inside it. This might make mkvmerge crash.\n"))
|
||||
% block_group->GetElementPosition());
|
||||
return;
|
||||
}
|
||||
|
||||
block->SetParent(*cluster);
|
||||
kax_track_t *block_track = find_track_by_num(block->TrackNum());
|
||||
|
||||
if (NULL == block_track) {
|
||||
mxwarn_fn(ti.fname,
|
||||
boost::format(Y("A block was found at timestamp %1% for track number %2%. However, no headers where found for that track number. "
|
||||
"The block will be skipped.\n")) % format_timecode(block->GlobalTimecode()) % block->TrackNum());
|
||||
return;
|
||||
}
|
||||
|
||||
KaxBlockAdditions *blockadd = static_cast<KaxBlockAdditions *>(block_group->FindFirstElt(KaxBlockAdditions::ClassInfos, false));
|
||||
KaxBlockDuration *duration = static_cast<KaxBlockDuration *>(block_group->FindFirstElt(KaxBlockDuration::ClassInfos, false));
|
||||
|
||||
if (NULL != duration)
|
||||
block_duration = (int64_t)uint64(*duration) * tc_scale / block->NumberFrames();
|
||||
else if (0 != block_track->v_frate)
|
||||
block_duration = (int64_t)(1000000000.0 / block_track->v_frate);
|
||||
int64_t frame_duration = (block_duration == -1) ? 0 : block_duration;
|
||||
|
||||
if (('s' == block_track->type) && (-1 == block_duration))
|
||||
block_duration = 0;
|
||||
|
||||
if (block_track->ignore_duration_hack) {
|
||||
frame_duration = 0;
|
||||
if (0 < block_duration)
|
||||
block_duration = 0;
|
||||
}
|
||||
|
||||
last_timecode = block->GlobalTimecode();
|
||||
// If we're appending this file to another one then the core
|
||||
// needs the timecodes shifted to zero.
|
||||
if (appending)
|
||||
last_timecode -= first_timecode;
|
||||
|
||||
KaxCodecState *codec_state = static_cast<KaxCodecState *>(block_group->FindFirstElt(KaxCodecState::ClassInfos));
|
||||
if ((NULL != codec_state) && !hack_engaged(ENGAGE_USE_CODEC_STATE))
|
||||
mxerror_tid(ti.fname, block_track->tnum,
|
||||
Y("This track uses a Matroska feature called 'Codec state elements'. mkvmerge supports these but "
|
||||
"this feature has not been turned on with the option '--engage use_codec_state'.\n"));
|
||||
|
||||
if ((-1 != block_track->ptzr) && block_track->passthrough) {
|
||||
// The handling for passthrough is a bit different. We don't have
|
||||
// any special cases, e.g. 0 terminating a string for the subs
|
||||
// and stuff. Just pass everything through as it is.
|
||||
if (bref_found)
|
||||
block_bref += last_timecode;
|
||||
if (fref_found)
|
||||
block_fref += last_timecode;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < (int)block->NumberFrames(); i++) {
|
||||
DataBuffer &data = block->GetBuffer(i);
|
||||
packet_t *packet = new packet_t(new memory_c((unsigned char *)data.Buffer(), data.Size(), false),
|
||||
last_timecode + i * frame_duration, block_duration, block_bref, block_fref);
|
||||
packet->duration_mandatory = duration != NULL;
|
||||
|
||||
if (NULL != codec_state)
|
||||
packet->codec_state = clone_memory(codec_state->GetBuffer(), codec_state->GetSize());
|
||||
|
||||
((passthrough_packetizer_c *)PTZR(block_track->ptzr))->process(packet_cptr(packet));
|
||||
}
|
||||
|
||||
} else if (-1 != block_track->ptzr) {
|
||||
if (bref_found) {
|
||||
if (FOURCC('r', 'e', 'a', 'l') == block_track->a_formattag)
|
||||
block_bref = block_track->previous_timecode;
|
||||
else
|
||||
block_bref += (int64_t)last_timecode;
|
||||
}
|
||||
if (fref_found)
|
||||
block_fref += (int64_t)last_timecode;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < (int)block->NumberFrames(); i++) {
|
||||
DataBuffer &data_buffer = block->GetBuffer(i);
|
||||
memory_cptr data(new memory_c(data_buffer.Buffer(), data_buffer.Size(), false));
|
||||
block_track->content_decoder.reverse(data, CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
|
||||
if (('s' == block_track->type) && ('t' == block_track->sub_type)) {
|
||||
if ((2 < data->get_size()) || ((0 < data->get_size()) && (' ' != *data->get_buffer()) && (0 != *data->get_buffer()) && !iscr(*data->get_buffer()))) {
|
||||
char *lines = (char *)safemalloc(data->get_size() + 1);
|
||||
lines[data->get_size()] = 0;
|
||||
memcpy(lines, data->get_buffer(), data->get_size());
|
||||
|
||||
packet_t *packet = new packet_t(new memory_c((unsigned char *)lines, 0, true), last_timecode, block_duration, block_bref, block_fref);
|
||||
if (NULL != codec_state)
|
||||
packet->codec_state = clone_memory(codec_state->GetBuffer(), codec_state->GetSize());
|
||||
|
||||
PTZR(block_track->ptzr)->process(packet);
|
||||
}
|
||||
|
||||
} else {
|
||||
packet_cptr packet(new packet_t(data, last_timecode + i * frame_duration, block_duration, block_bref, block_fref));
|
||||
|
||||
if (NULL != codec_state)
|
||||
packet->codec_state = clone_memory(codec_state->GetBuffer(), codec_state->GetSize());
|
||||
|
||||
if (blockadd) {
|
||||
int k;
|
||||
for (k = 0; k < blockadd->ListSize(); k++) {
|
||||
if (!(is_id((*blockadd)[k], KaxBlockMore)))
|
||||
continue;
|
||||
|
||||
KaxBlockMore *blockmore = static_cast<KaxBlockMore *>((*blockadd)[k]);
|
||||
KaxBlockAdditional *blockadd_data = &GetChild<KaxBlockAdditional>(*blockmore);
|
||||
|
||||
memory_cptr blockadded(new memory_c(blockadd_data->GetBuffer(), blockadd_data->GetSize(), false));
|
||||
block_track->content_decoder.reverse(blockadded, CONTENT_ENCODING_SCOPE_BLOCK);
|
||||
|
||||
packet->data_adds.push_back(blockadded);
|
||||
}
|
||||
}
|
||||
|
||||
PTZR(block_track->ptzr)->process(packet);
|
||||
}
|
||||
}
|
||||
|
||||
block_track->previous_timecode = (int64_t)last_timecode;
|
||||
block_track->units_processed += block->NumberFrames();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "common/common.h"
|
||||
#include "common/compression.h"
|
||||
#include "common/error.h"
|
||||
#include "common/kax_file.h"
|
||||
#include "common/mm_io.h"
|
||||
#include "common/mpeg4_p10.h"
|
||||
#include "merge/pr_generic.h"
|
||||
@ -167,12 +168,13 @@ private:
|
||||
int64_t tc_scale;
|
||||
int64_t cluster_tc;
|
||||
|
||||
mm_io_c *in;
|
||||
mm_io_cptr in;
|
||||
kax_file_cptr m_in_file;
|
||||
int64_t file_size;
|
||||
|
||||
EbmlStream *es;
|
||||
EbmlElement *saved_l1, *saved_l2, *segment;
|
||||
KaxCluster *cluster;
|
||||
EbmlElement *segment;
|
||||
// KaxCluster *cluster;
|
||||
|
||||
int64_t segment_duration;
|
||||
int64_t last_timecode, first_timecode;
|
||||
@ -189,6 +191,8 @@ private:
|
||||
|
||||
KaxTags *m_tags;
|
||||
|
||||
file_status_e m_file_status;
|
||||
|
||||
public:
|
||||
kax_reader_c(track_info_c &_ti) throw (error_c);
|
||||
virtual ~kax_reader_c();
|
||||
@ -231,7 +235,10 @@ protected:
|
||||
virtual void read_headers_track_video(kax_track_t *&track, KaxTrackVideo *&ktvideo);
|
||||
virtual void read_headers_tracks(mm_io_c *io, EbmlElement *l0, int64_t position);
|
||||
virtual void read_headers_seek_head(EbmlElement *&l0, EbmlElement *&l1);
|
||||
virtual int read_headers();
|
||||
virtual bool read_headers();
|
||||
|
||||
virtual void process_simple_block(KaxCluster *cluster, KaxSimpleBlock *block_simple);
|
||||
virtual void process_block_group(KaxCluster *cluster, KaxBlockGroup *block_group);
|
||||
|
||||
virtual mpeg4::p10::avc_es_parser_cptr parse_first_mpeg4_p10_frame(kax_track_t *t, track_info_c &nti);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user