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