in src/record/internal/matroska_read.cpp [130:293]
k4a_result_t parse_mkv(k4a_playback_context_t *context)
{
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context == NULL);
RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context->stream == nullptr);
// Read and verify the EBML head information from the file
auto ebmlHead = find_next<EbmlHead>(context);
if (ebmlHead)
{
if (read_element<EbmlHead>(context, ebmlHead.get()) == NULL)
{
LOG_ERROR("Failed to read EBML head.", 0);
return K4A_RESULT_FAILED;
}
EDocType &eDocType = GetChild<EDocType>(*ebmlHead);
EDocTypeVersion &eDocTypeVersion = GetChild<EDocTypeVersion>(*ebmlHead);
EDocTypeReadVersion &eDocTypeReadVersion = GetChild<EDocTypeReadVersion>(*ebmlHead);
if (eDocType.GetValue() != std::string("matroska"))
{
LOG_ERROR("DocType is not matroska: %s", eDocType.GetValue().c_str());
return K4A_RESULT_FAILED;
}
if (eDocTypeReadVersion.GetValue() > 2)
{
LOG_ERROR("DocTypeReadVersion (%d) > 2 is not supported.", eDocTypeReadVersion.GetValue());
return K4A_RESULT_FAILED;
}
if (eDocTypeVersion.GetValue() < eDocTypeReadVersion.GetValue())
{
LOG_ERROR("DocTypeVersion (%d) is great than DocTypeReadVersion (%d)",
eDocTypeVersion.GetValue(),
eDocTypeReadVersion.GetValue());
return K4A_RESULT_FAILED;
}
}
else
{
LOG_ERROR("Matroska / EBML head is missing, recording is not valid.", 0);
return K4A_RESULT_FAILED;
}
// Find the offsets for each section of the file
context->segment = find_next<KaxSegment>(context, true);
if (context->segment)
{
auto element = next_child(context, context->segment.get());
while (element != nullptr && !seek_info_ready(context))
{
EbmlId element_id(*element);
match_ebml_id(context, element_id, context->segment->GetRelativePosition(*element.get()));
if (element_id == KaxSeekHead::ClassInfos.GlobalId)
{
// Parse SeekHead offset positions
KaxSeekHead *seek_head = read_element<KaxSeekHead>(context, element.get());
KaxSeek *seek = NULL;
for (EbmlElement *e : seek_head->GetElementList())
{
if (check_element_type(e, &seek))
{
KaxSeekID &seek_id = GetChild<KaxSeekID>(*seek);
EbmlId ebml_id(seek_id.GetBuffer(), static_cast<unsigned int>(seek_id.GetSize()));
int64_t seek_location = seek->Location();
assert(seek_location >= 0);
match_ebml_id(context, ebml_id, (uint64_t)seek_location);
}
}
}
else
{
skip_element(context, element.get());
}
element = next_child(context, context->segment.get());
}
}
if (context->first_cluster_offset == 0)
{
LOG_ERROR("Recording file does not contain any frames!", 0);
return K4A_RESULT_FAILED;
}
// Populate each element from the file (minus the actual Cluster data)
RETURN_IF_ERROR(read_offset(context, context->segment_info, context->segment_info_offset));
RETURN_IF_ERROR(read_offset(context, context->tracks, context->tracks_offset));
if (context->cues_offset > 0)
RETURN_IF_ERROR(read_offset(context, context->cues, context->cues_offset));
if (context->attachments_offset > 0)
RETURN_IF_ERROR(read_offset(context, context->attachments, context->attachments_offset));
if (context->tags_offset > 0)
RETURN_IF_ERROR(read_offset(context, context->tags, context->tags_offset));
RETURN_IF_ERROR(parse_recording_config(context));
RETURN_IF_ERROR(populate_cluster_cache(context));
// Find the last timestamp in the file
context->last_file_timestamp_ns = 0;
cluster_info_t *cluster_info = find_cluster(context, UINT64_MAX);
if (cluster_info == NULL)
{
LOG_ERROR("Failed to find end of recording.", 0);
return K4A_RESULT_FAILED;
}
KaxSimpleBlock *simple_block = NULL;
KaxBlockGroup *block_group = NULL;
std::shared_ptr<KaxCluster> last_cluster = load_cluster_internal(context, cluster_info);
if (last_cluster == nullptr)
{
LOG_ERROR("Failed to load end of recording.", 0);
return K4A_RESULT_FAILED;
}
for (EbmlElement *e : last_cluster->GetElementList())
{
if (check_element_type(e, &simple_block))
{
simple_block->SetParent(*last_cluster);
uint64_t block_timestamp_ns = simple_block->GlobalTimecode();
if (block_timestamp_ns > context->last_file_timestamp_ns)
{
context->last_file_timestamp_ns = block_timestamp_ns;
}
}
else if (check_element_type(e, &block_group))
{
block_group->SetParent(*last_cluster);
KaxTrackEntry *parent_track = NULL;
for (EbmlElement *e2 : context->tracks->GetElementList())
{
if (check_element_type(e2, &parent_track))
{
if (GetChild<KaxTrackNumber>(*parent_track).GetValue() == (uint64)block_group->TrackNumber())
{
parent_track->SetGlobalTimecodeScale(context->timecode_scale);
}
}
}
uint64_t block_timestamp_ns = block_group->GlobalTimecode();
if (parent_track)
{
block_group->SetParentTrack(*parent_track);
uint64_t block_duration_ns = 0;
if (block_group->GetBlockDuration(block_duration_ns))
{
block_timestamp_ns += block_duration_ns - 1;
}
}
if (block_timestamp_ns > context->last_file_timestamp_ns)
{
context->last_file_timestamp_ns = block_timestamp_ns;
}
}
}
LOG_TRACE("Found last file timestamp: %llu", context->last_file_timestamp_ns);
return K4A_RESULT_SUCCEEDED;
}