k4a_result_t k4a_record_create()

in src/record/sdk/record.cpp [17:363]


k4a_result_t k4a_record_create(const char *path,
                               k4a_device_t device,
                               const k4a_device_configuration_t device_config,
                               k4a_record_t *recording_handle)
{
    RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, path == NULL);
    RETURN_VALUE_IF_ARG(K4A_RESULT_FAILED, recording_handle == NULL);
    k4a_record_context_t *context = NULL;
    k4a_result_t result = K4A_RESULT_SUCCEEDED;

    context = k4a_record_t_create(recording_handle);
    result = K4A_RESULT_FROM_BOOL(context != NULL);

    if (K4A_SUCCEEDED(result))
    {
        context->file_path = path;

        try
        {
            context->ebml_file = make_unique<LargeFileIOCallback>(path, MODE_CREATE);
        }
        catch (std::ios_base::failure &e)
        {
            LOG_ERROR("Unable to open file '%s': %s", path, e.what());
            result = K4A_RESULT_FAILED;
        }
    }

    if (K4A_SUCCEEDED(result))
    {
        context->device_config = device_config;

        context->timecode_scale = MATROSKA_TIMESCALE_NS;
        context->camera_fps = k4a_convert_fps_to_uint(device_config.camera_fps);
        if (context->camera_fps == 0)
        {
            // Set camera FPS to 30 if no cameras are enabled so IMU can still be written.
            context->camera_fps = 30;
        }
    }

    uint32_t color_width = 0;
    uint32_t color_height = 0;
    if (K4A_SUCCEEDED(result) && device_config.color_resolution != K4A_COLOR_RESOLUTION_OFF)
    {
        if (!k4a_convert_resolution_to_width_height(device_config.color_resolution, &color_width, &color_height))
        {
            LOG_ERROR("Unsupported color_resolution specified in recording: %d", device_config.color_resolution);
            result = K4A_RESULT_FAILED;
        }
    }

    std::ostringstream color_mode_str;
    if (K4A_SUCCEEDED(result))
    {
        if (device_config.color_resolution != K4A_COLOR_RESOLUTION_OFF)
        {
            switch (device_config.color_format)
            {
            case K4A_IMAGE_FORMAT_COLOR_NV12:
                color_mode_str << "NV12_" << color_height << "P";
                break;
            case K4A_IMAGE_FORMAT_COLOR_YUY2:
                color_mode_str << "YUY2_" << color_height << "P";
                break;
            case K4A_IMAGE_FORMAT_COLOR_MJPG:
                color_mode_str << "MJPG_" << color_height << "P";
                break;
            case K4A_IMAGE_FORMAT_COLOR_BGRA32:
                color_mode_str << "BGRA_" << color_height << "P";
                break;
            default:
                LOG_ERROR("Unsupported color_format specified in recording: %d", device_config.color_format);
                result = K4A_RESULT_FAILED;
            }
        }
        else
        {
            color_mode_str << "OFF";
        }
    }

    const char *depth_mode_str = "OFF";
    uint32_t depth_width = 0;
    uint32_t depth_height = 0;
    if (K4A_SUCCEEDED(result))
    {
        if (device_config.depth_mode != K4A_DEPTH_MODE_OFF)
        {
            for (size_t i = 0; i < arraysize(depth_modes); i++)
            {
                if (device_config.depth_mode == depth_modes[i].first)
                {
                    if (!k4a_convert_depth_mode_to_width_height(depth_modes[i].first, &depth_width, &depth_height))
                    {
                        LOG_ERROR("Unsupported depth_mode specified in recording: %d", device_config.depth_mode);
                        result = K4A_RESULT_FAILED;
                    }
                    depth_mode_str = depth_modes[i].second.c_str();
                    break;
                }
            }
            if (depth_width == 0 || depth_height == 0)
            {
                LOG_ERROR("Unsupported depth_mode specified in recording: %d", device_config.depth_mode);
                result = K4A_RESULT_FAILED;
            }
        }
    }

    if (K4A_SUCCEEDED(result))
    {
        context->file_segment = make_unique<KaxSegment>();

        { // Setup segment info
            auto &segment_info = GetChild<KaxInfo>(*context->file_segment);

            GetChild<KaxTimecodeScale>(segment_info).SetValue(context->timecode_scale);
            GetChild<KaxMuxingApp>(segment_info).SetValue(L"libmatroska-1.4.9");
            std::ostringstream version_str;
            version_str << "k4arecord-" << K4A_VERSION_STR;
            GetChild<KaxWritingApp>(segment_info).SetValueUTF8(version_str.str());
            GetChild<KaxDateUTC>(segment_info).SetEpochDate(time(0));
            GetChild<KaxTitle>(segment_info).SetValue(L"Azure Kinect");
        }

        auto &tags = GetChild<KaxTags>(*context->file_segment);
        tags.EnableChecksum();
    }

    if (K4A_SUCCEEDED(result) && device_config.color_resolution != K4A_COLOR_RESOLUTION_OFF)
    {
        BITMAPINFOHEADER codec_info = {};
        result = TRACE_CALL(
            populate_bitmap_info_header(&codec_info, color_width, color_height, device_config.color_format));

        context->color_track = add_track(context,
                                         K4A_TRACK_NAME_COLOR,
                                         track_video,
                                         "V_MS/VFW/FOURCC",
                                         reinterpret_cast<uint8_t *>(&codec_info),
                                         sizeof(codec_info));
        if (context->color_track != nullptr)
        {
            set_track_info_video(context->color_track, color_width, color_height, context->camera_fps);

            uint64_t track_uid = GetChild<KaxTrackUID>(*context->color_track->track).GetValue();
            std::ostringstream track_uid_str;
            track_uid_str << track_uid;
            add_tag(context, "K4A_COLOR_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
            add_tag(context, "K4A_COLOR_MODE", color_mode_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
        }
        else
        {
            LOG_ERROR("Failed to add color track.", 0);
            result = K4A_RESULT_FAILED;
        }
    }

    if (K4A_SUCCEEDED(result))
    {
        if (device_config.depth_mode == K4A_DEPTH_MODE_PASSIVE_IR)
        {
            add_tag(context, "K4A_DEPTH_MODE", depth_mode_str);
        }
        else if (device_config.depth_mode != K4A_DEPTH_MODE_OFF)
        {
            // Depth track
            BITMAPINFOHEADER codec_info = {};
            result = TRACE_CALL(
                populate_bitmap_info_header(&codec_info, depth_width, depth_height, K4A_IMAGE_FORMAT_DEPTH16));

            context->depth_track = add_track(context,
                                             K4A_TRACK_NAME_DEPTH,
                                             track_video,
                                             "V_MS/VFW/FOURCC",
                                             reinterpret_cast<uint8_t *>(&codec_info),
                                             sizeof(codec_info));
            if (context->depth_track != nullptr)
            {
                set_track_info_video(context->depth_track, depth_width, depth_height, context->camera_fps);

                uint64_t track_uid = GetChild<KaxTrackUID>(*context->depth_track->track).GetValue();
                std::ostringstream track_uid_str;
                track_uid_str << track_uid;
                add_tag(context, "K4A_DEPTH_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
                add_tag(context, "K4A_DEPTH_MODE", depth_mode_str, TAG_TARGET_TYPE_TRACK, track_uid);
            }
            else
            {
                LOG_ERROR("Failed to add depth track.", 0);
                result = K4A_RESULT_FAILED;
            }
        }
    }

    if (K4A_SUCCEEDED(result) && device_config.depth_mode != K4A_DEPTH_MODE_OFF)
    {
        // IR Track
        BITMAPINFOHEADER codec_info = {};
        result = TRACE_CALL(populate_bitmap_info_header(&codec_info, depth_width, depth_height, K4A_IMAGE_FORMAT_IR16));

        context->ir_track = add_track(context,
                                      K4A_TRACK_NAME_IR,
                                      track_video,
                                      "V_MS/VFW/FOURCC",
                                      reinterpret_cast<uint8_t *>(&codec_info),
                                      sizeof(codec_info));
        if (context->ir_track != nullptr)
        {
            set_track_info_video(context->ir_track, depth_width, depth_height, context->camera_fps);

            uint64_t track_uid = GetChild<KaxTrackUID>(*context->ir_track->track).GetValue();
            std::ostringstream track_uid_str;
            track_uid_str << track_uid;
            add_tag(context, "K4A_IR_TRACK", track_uid_str.str().c_str(), TAG_TARGET_TYPE_TRACK, track_uid);
            add_tag(context,
                    "K4A_IR_MODE",
                    device_config.depth_mode == K4A_DEPTH_MODE_PASSIVE_IR ? "PASSIVE" : "ACTIVE",
                    TAG_TARGET_TYPE_TRACK,
                    track_uid);
        }
        else
        {
            LOG_ERROR("Failed to add ir track.", 0);
            result = K4A_RESULT_FAILED;
        }
    }

    if (K4A_SUCCEEDED(result) && device_config.color_resolution != K4A_COLOR_RESOLUTION_OFF &&
        device_config.depth_mode != K4A_DEPTH_MODE_OFF)
    {
        std::ostringstream delay_str;
        delay_str << device_config.depth_delay_off_color_usec * 1000;
        add_tag(context, "K4A_DEPTH_DELAY_NS", delay_str.str().c_str());
    }

    if (K4A_SUCCEEDED(result))
    {
        switch (device_config.wired_sync_mode)
        {
        case K4A_WIRED_SYNC_MODE_STANDALONE:
            add_tag(context, "K4A_WIRED_SYNC_MODE", "STANDALONE");
            break;
        case K4A_WIRED_SYNC_MODE_MASTER:
            add_tag(context, "K4A_WIRED_SYNC_MODE", "MASTER");
            break;
        case K4A_WIRED_SYNC_MODE_SUBORDINATE:
            add_tag(context, "K4A_WIRED_SYNC_MODE", "SUBORDINATE");

            std::ostringstream delay_str;
            delay_str << device_config.subordinate_delay_off_master_usec * 1000;
            add_tag(context, "K4A_SUBORDINATE_DELAY_NS", delay_str.str().c_str());
            break;
        }
    }

    if (K4A_SUCCEEDED(result) && device != NULL)
    {
        // Add the firmware version and device serial number to the recording
        k4a_hardware_version_t version_info;
        result = TRACE_CALL(k4a_device_get_version(device, &version_info));

        std::ostringstream color_firmware_str;
        color_firmware_str << version_info.rgb.major << "." << version_info.rgb.minor << "."
                           << version_info.rgb.iteration;
        std::ostringstream depth_firmware_str;
        depth_firmware_str << version_info.depth.major << "." << version_info.depth.minor << "."
                           << version_info.depth.iteration;
        add_tag(context, "K4A_COLOR_FIRMWARE_VERSION", color_firmware_str.str().c_str());
        add_tag(context, "K4A_DEPTH_FIRMWARE_VERSION", depth_firmware_str.str().c_str());

        char serial_number_buffer[256];
        size_t serial_number_buffer_size = sizeof(serial_number_buffer);
        // If reading the device serial number fails, just log the error and continue. The recording is still valid.
        if (TRACE_BUFFER_CALL(k4a_device_get_serialnum(device, serial_number_buffer, &serial_number_buffer_size)) ==
            K4A_BUFFER_RESULT_SUCCEEDED)
        {
            add_tag(context, "K4A_DEVICE_SERIAL_NUMBER", serial_number_buffer);
        }
    }

    if (K4A_SUCCEEDED(result) && device != NULL)
    {
        // Add calibration.json to the recording
        size_t calibration_size = 0;
        k4a_buffer_result_t buffer_result = TRACE_BUFFER_CALL(
            k4a_device_get_raw_calibration(device, NULL, &calibration_size));
        if (buffer_result == K4A_BUFFER_RESULT_TOO_SMALL)
        {
            std::vector<uint8_t> calibration_buffer = std::vector<uint8_t>(calibration_size);
            buffer_result = TRACE_BUFFER_CALL(
                k4a_device_get_raw_calibration(device, calibration_buffer.data(), &calibration_size));
            if (buffer_result == K4A_BUFFER_RESULT_SUCCEEDED)
            {
                // Remove the null-terminated byte from the file before writing it.
                if (calibration_size > 0 && calibration_buffer[calibration_size - 1] == '\0')
                {
                    calibration_size--;
                }
                KaxAttached *attached = add_attachment(context,
                                                       "calibration.json",
                                                       "application/octet-stream",
                                                       calibration_buffer.data(),
                                                       calibration_size);
                add_tag(context,
                        "K4A_CALIBRATION_FILE",
                        "calibration.json",
                        TAG_TARGET_TYPE_ATTACHMENT,
                        get_attachment_uid(attached));
            }
            else
            {
                result = K4A_RESULT_FAILED;
            }
        }
        else
        {
            result = K4A_RESULT_FAILED;
        }
    }

    if (K4A_SUCCEEDED(result))
    {
        auto &cues = GetChild<KaxCues>(*context->file_segment);
        cues.SetGlobalTimecodeScale(context->timecode_scale);
    }
    else
    {
        if (context && context->ebml_file)
        {
            try
            {
                context->ebml_file->close();
            }
            catch (std::ios_base::failure &)
            {
                // The file is empty at this point, ignore any close failures.
            }
        }

        k4a_record_t_destroy(*recording_handle);
        *recording_handle = NULL;
    }

    return result;
}