k4a_result_t parse_recording_config()

in src/record/internal/matroska_read.cpp [389:762]


k4a_result_t parse_recording_config(k4a_playback_context_t *context)
{
    RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context == NULL);
    RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, context->segment_info == nullptr);

    context->timecode_scale = GetChild<KaxTimecodeScale>(*context->segment_info).GetValue();

    if (K4A_FAILED(parse_tracks(context)))
    {
        LOG_ERROR("Reading track data failed.", 0);
        return K4A_RESULT_FAILED;
    }

    context->color_track = find_track(context, "COLOR", "K4A_COLOR_TRACK");
    context->depth_track = find_track(context, "DEPTH", "K4A_DEPTH_TRACK");
    context->ir_track = find_track(context, "IR", "K4A_IR_TRACK");
    if (context->ir_track == NULL)
    {
        // Support legacy IR track naming.
        context->ir_track = find_track(context, "DEPTH_IR", NULL);
    }
    context->imu_track = find_track(context, "IMU", "K4A_IMU_TRACK");

    // Read device calibration attachment
    context->calibration_attachment = get_attachment_by_tag(context, "K4A_CALIBRATION_FILE");
    if (context->calibration_attachment == NULL)
    {
        context->calibration_attachment = get_attachment_by_name(context, "calibration.json");
    }
    if (context->calibration_attachment == NULL)
    {
        // The rest of the recording can still be read if no device calibration blob exists.
        LOG_WARNING("Device calibration is missing from recording.", 0);
    }

    uint64_t frame_period_ns = 0;
    if (context->color_track)
    {
        if (context->color_track->type != track_video)
        {
            LOG_ERROR("Color track is not a video track.", 0);
            return K4A_RESULT_FAILED;
        }

        frame_period_ns = context->color_track->frame_period_ns;

        RETURN_IF_ERROR(read_bitmap_info_header(context->color_track));
        context->record_config.color_resolution = K4A_COLOR_RESOLUTION_OFF;
        for (size_t i = 0; i < arraysize(color_resolutions); i++)
        {
            uint32_t width, height;
            if (k4a_convert_resolution_to_width_height(color_resolutions[i], &width, &height))
            {
                if (context->color_track->width == width && context->color_track->height == height)
                {
                    context->record_config.color_resolution = color_resolutions[i];
                    break;
                }
            }
        }

        if (context->record_config.color_resolution == K4A_COLOR_RESOLUTION_OFF)
        {
            LOG_WARNING("The color resolution is not officially supported: %dx%d. You cannot get the calibration "
                        "information for this color resolution",
                        context->color_track->width,
                        context->color_track->height);
        }

        context->record_config.color_track_enabled = true;
        context->record_config.color_format = context->color_track->format;
        context->color_format_conversion = context->color_track->format;
    }
    else
    {
        context->record_config.color_resolution = K4A_COLOR_RESOLUTION_OFF;
        // Set to a default color format if color track is disabled.
        context->record_config.color_format = K4A_IMAGE_FORMAT_CUSTOM;
        context->color_format_conversion = K4A_IMAGE_FORMAT_CUSTOM;
    }

    KaxTag *depth_mode_tag = get_tag(context, "K4A_DEPTH_MODE");
    if (depth_mode_tag == NULL && (context->depth_track || context->ir_track))
    {
        LOG_ERROR("K4A_DEPTH_MODE tag is missing.", 0);
        return K4A_RESULT_FAILED;
    }

    std::string depth_mode_str;
    uint32_t depth_width = 0;
    uint32_t depth_height = 0;
    context->record_config.depth_mode = K4A_DEPTH_MODE_OFF;

    if (depth_mode_tag != NULL)
    {
        depth_mode_str = get_tag_string(depth_mode_tag);
        for (size_t i = 0; i < arraysize(depth_modes); i++)
        {
            if (depth_mode_str == depth_modes[i].second)
            {
                if (k4a_convert_depth_mode_to_width_height(depth_modes[i].first, &depth_width, &depth_height))
                {
                    context->record_config.depth_mode = depth_modes[i].first;
                    break;
                }
            }
        }

        if (context->record_config.depth_mode == K4A_DEPTH_MODE_OFF)
        {
            // Try to find the mode matching strings in the legacy modes
            for (size_t i = 0; i < arraysize(legacy_depth_modes); i++)
            {
                if (depth_mode_str == legacy_depth_modes[i].second)
                {
                    if (k4a_convert_depth_mode_to_width_height(legacy_depth_modes[i].first,
                                                               &depth_width,
                                                               &depth_height))
                    {
                        context->record_config.depth_mode = legacy_depth_modes[i].first;
                        break;
                    }
                }
            }
        }
        if (context->record_config.depth_mode == K4A_DEPTH_MODE_OFF)
        {
            LOG_ERROR("Unsupported depth mode: %s", depth_mode_str.c_str());
            return K4A_RESULT_FAILED;
        }
    }

    if (context->depth_track)
    {
        if (context->depth_track->type != track_video)
        {
            LOG_ERROR("Depth track is not a video track.", 0);
            return K4A_RESULT_FAILED;
        }

        if (frame_period_ns == 0)
        {
            frame_period_ns = context->depth_track->frame_period_ns;
        }
        else if (frame_period_ns != context->depth_track->frame_period_ns)
        {
            LOG_ERROR("Track frame durations don't match (Depth): %llu ns != %llu ns",
                      frame_period_ns,
                      context->depth_track->frame_period_ns);
            return K4A_RESULT_FAILED;
        }

        if (context->depth_track->width != depth_width || context->depth_track->height != depth_height)
        {
            LOG_ERROR("Unsupported depth resolution / mode: %dx%d (%s)",
                      context->depth_track->width,
                      context->depth_track->height,
                      depth_mode_str.c_str());
            return K4A_RESULT_FAILED;
        }

        RETURN_IF_ERROR(read_bitmap_info_header(context->depth_track));

        context->record_config.depth_track_enabled = true;
    }

    if (context->ir_track)
    {
        if (context->ir_track->type != track_video)
        {
            LOG_ERROR("IR track is not a video track.", 0);
            return K4A_RESULT_FAILED;
        }

        if (frame_period_ns == 0)
        {
            frame_period_ns = context->ir_track->frame_period_ns;
        }
        else if (frame_period_ns != context->ir_track->frame_period_ns)
        {
            LOG_ERROR("Track frame durations don't match (IR): %llu ns != %llu ns",
                      frame_period_ns,
                      context->ir_track->frame_period_ns);
            return K4A_RESULT_FAILED;
        }

        if (context->depth_track)
        {
            if (context->ir_track->width != context->depth_track->width ||
                context->ir_track->height != context->depth_track->height)
            {
                LOG_ERROR("Depth and IR track have different resolutions: Depth %dx%d, IR %dx%d",
                          context->depth_track->width,
                          context->depth_track->height,
                          context->ir_track->width,
                          context->ir_track->height);
                return K4A_RESULT_FAILED;
            }
        }
        else if (context->ir_track->width != depth_width || context->ir_track->height != depth_height)
        {
            LOG_ERROR("Unsupported IR resolution / depth mode: %dx%d (%s)",
                      context->ir_track->width,
                      context->ir_track->height,
                      depth_mode_str.c_str());
            return K4A_RESULT_FAILED;
        }

        RETURN_IF_ERROR(read_bitmap_info_header(context->ir_track));
        if (context->ir_track->format == K4A_IMAGE_FORMAT_DEPTH16)
        {
            context->ir_track->format = K4A_IMAGE_FORMAT_IR16;
        }

        context->record_config.ir_track_enabled = true;
    }

    context->sync_period_ns = frame_period_ns;
    if (frame_period_ns > 0)
    {
        switch (1_s / frame_period_ns)
        {
        case 5:
            context->record_config.camera_fps = K4A_FRAMES_PER_SECOND_5;
            break;
        case 15:
            context->record_config.camera_fps = K4A_FRAMES_PER_SECOND_15;
            break;
        case 30:
            context->record_config.camera_fps = K4A_FRAMES_PER_SECOND_30;
            break;
        default:
            LOG_ERROR("Unsupported recording frame period: %llu ns (%llu fps)",
                      frame_period_ns,
                      (1_s / frame_period_ns));
            return K4A_RESULT_FAILED;
        }
    }
    else
    {
        // Default to 30 fps if no video tracks are enabled.
        context->record_config.camera_fps = K4A_FRAMES_PER_SECOND_30;
    }

    // Read depth_delay_off_color_usec and set offsets for each builtin track accordingly.
    KaxTag *depth_delay_tag = get_tag(context, "K4A_DEPTH_DELAY_NS");
    if (depth_delay_tag != NULL)
    {
        int64_t depth_delay_ns;
        std::istringstream depth_delay_str(get_tag_string(depth_delay_tag));
        depth_delay_str >> depth_delay_ns;
        if (!depth_delay_str.fail())
        {
            assert(depth_delay_ns / 1000 <= INT32_MAX);
            context->record_config.depth_delay_off_color_usec = (int32_t)(depth_delay_ns / 1000);

            // Only set positive delays so that we don't wrap around near 0.
            if (depth_delay_ns > 0 && context->color_track)
            {
                context->color_track->sync_delay_ns = (uint64_t)depth_delay_ns;
            }
            else if (depth_delay_ns < 0)
            {
                if (context->depth_track)
                    context->depth_track->sync_delay_ns = (uint64_t)(-depth_delay_ns);
                if (context->ir_track)
                    context->ir_track->sync_delay_ns = (uint64_t)(-depth_delay_ns);
            }
        }
        else
        {
            LOG_ERROR("Tag K4A_DEPTH_DELAY_NS contains invalid value: %s", get_tag_string(depth_delay_tag).c_str());
            return K4A_RESULT_FAILED;
        }
    }
    else
    {
        context->record_config.depth_delay_off_color_usec = 0;
    }

    if (context->imu_track)
    {
        if (context->imu_track->type == track_subtitle)
        {
            context->record_config.imu_track_enabled = true;
        }
        else
        {
            LOG_WARNING("IMU track is not correct type, treating as a custom track.", 0);
            context->imu_track = nullptr;
        }
    }

    // Read wired_sync_mode and subordinate_delay_off_master_usec.
    KaxTag *sync_mode_tag = get_tag(context, "K4A_WIRED_SYNC_MODE");
    if (sync_mode_tag != NULL)
    {
        bool sync_mode_found = false;
        std::string sync_mode_str = get_tag_string(sync_mode_tag);
        for (size_t i = 0; i < arraysize(external_sync_modes); i++)
        {
            if (sync_mode_str == external_sync_modes[i].second)
            {
                context->record_config.wired_sync_mode = external_sync_modes[i].first;
                sync_mode_found = true;
                break;
            }
        }
        if (!sync_mode_found)
        {
            LOG_ERROR("Unsupported wired sync mode: %s", sync_mode_str.c_str());
            return K4A_RESULT_FAILED;
        }

        if (context->record_config.wired_sync_mode == K4A_WIRED_SYNC_MODE_SUBORDINATE)
        {
            KaxTag *subordinate_delay_tag = get_tag(context, "K4A_SUBORDINATE_DELAY_NS");
            if (subordinate_delay_tag != NULL)
            {
                uint64_t subordinate_delay_ns;
                std::istringstream subordinate_delay_str(get_tag_string(subordinate_delay_tag));
                subordinate_delay_str >> subordinate_delay_ns;
                if (!subordinate_delay_str.fail())
                {
                    assert(subordinate_delay_ns / 1000 <= UINT32_MAX);
                    context->record_config.subordinate_delay_off_master_usec = (uint32_t)(subordinate_delay_ns / 1000);
                }
                else
                {
                    LOG_ERROR("Tag K4A_SUBORDINATE_DELAY_NS contains invalid value: %s",
                              get_tag_string(subordinate_delay_tag).c_str());
                    return K4A_RESULT_FAILED;
                }
            }
            else
            {
                context->record_config.subordinate_delay_off_master_usec = 0;
            }
        }
        else
        {
            context->record_config.subordinate_delay_off_master_usec = 0;
        }
    }
    else
    {
        context->record_config.wired_sync_mode = K4A_WIRED_SYNC_MODE_STANDALONE;
        context->record_config.subordinate_delay_off_master_usec = 0;
    }

    KaxTag *start_offset_tag = get_tag(context, "K4A_START_OFFSET_NS");
    if (start_offset_tag != NULL)
    {
        uint64_t start_offset_ns;
        std::istringstream start_offset_str(get_tag_string(start_offset_tag));
        start_offset_str >> start_offset_ns;
        if (!start_offset_str.fail())
        {
            assert(start_offset_ns / 1000 <= UINT32_MAX);
            context->record_config.start_timestamp_offset_usec = (uint32_t)(start_offset_ns / 1000);
        }
        else
        {
            LOG_ERROR("Tag K4A_START_OFFSET_NS contains invalid value: %s", get_tag_string(start_offset_tag).c_str());
            return K4A_RESULT_FAILED;
        }
    }
    else
    {
        context->record_config.start_timestamp_offset_usec = 0;
    }

    return K4A_RESULT_SUCCEEDED;
}