k4a_result_t parse_mkv()

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;
}