void UVCCameraReader::Callback()

in src/color/uvc_camerareader.cpp [1118:1289]


void UVCCameraReader::Callback(uvc_frame_t *frame)
{
    std::lock_guard<std::mutex> lock(m_mutex);

    if (m_streaming && frame)
    {
        void *context = nullptr;
        k4a_image_t image = NULL;
        uint8_t *buffer = nullptr;
        size_t buffer_size = 0;
        int stride = 0;
        uint64_t framePTS = 0;
        uint64_t exposure_time = 0;
        uint32_t iso_speed = 0;
        uint32_t white_balance = 0;
        bool decodeMJPEG = false;
        bool drop_image = false;

        // Parse metadata
        size_t bufferLeft = (size_t)frame->metadata_bytes;
        if (bufferLeft >= sizeof(KSCAMERA_METADATA_ITEMHEADER))
        {
            PKSCAMERA_METADATA_ITEMHEADER pItem = (PKSCAMERA_METADATA_ITEMHEADER)frame->metadata;
            while (bufferLeft > 0)
            {
                switch (pItem->MetadataId)
                {
                case MetadataId_FrameAlignInfo:
                {
                    PKSCAMERA_CUSTOM_METADATA_FrameAlignInfo pFrameAlignInfo =
                        (PKSCAMERA_CUSTOM_METADATA_FrameAlignInfo)pItem;
                    framePTS = pFrameAlignInfo->FramePTS;
                }
                break;
                case MetadataId_CaptureStats:
                {
                    PKSCAMERA_METADATA_CAPTURESTATS pCaptureStats = (PKSCAMERA_METADATA_CAPTURESTATS)pItem;
                    if (pCaptureStats->Flags & KSCAMERA_METADATA_CAPTURESTATS_FLAG_EXPOSURETIME)
                    {
                        exposure_time = pCaptureStats->ExposureTime / 10; // hns to micro-second
                    }
                    if (pCaptureStats->Flags & KSCAMERA_METADATA_CAPTURESTATS_FLAG_ISOSPEED)
                    {
                        iso_speed = pCaptureStats->IsoSpeed;
                    }
                    if (pCaptureStats->Flags & KSCAMERA_METADATA_CAPTURESTATS_FLAG_WHITEBALANCE)
                    {
                        white_balance = pCaptureStats->WhiteBalance;
                    }
                }
                break;
                default:
                    break;
                }

                if (pItem->Size == 0)
                {
                    LOG_WARNING("frame metadata id %d has zero buffer size", pItem->MetadataId);
                    break;
                }

                bufferLeft -= (size_t)pItem->Size;
                if (bufferLeft < sizeof(KSCAMERA_METADATA_ITEMHEADER))
                {
                    break;
                }

                pItem = reinterpret_cast<PKSCAMERA_METADATA_ITEMHEADER>(reinterpret_cast<uint8_t *>(pItem) +
                                                                        pItem->Size);
            }
        }
        if (framePTS == 0)
        {
            // Drop 0 time stamped frame
            return;
        }

        if (m_input_image_format == K4A_IMAGE_FORMAT_COLOR_MJPG &&
            m_output_image_format == K4A_IMAGE_FORMAT_COLOR_BGRA32)
        {
            stride = (int)frame->width * 4;
            buffer_size = (size_t)stride * frame->height;
            decodeMJPEG = true;
        }
        else
        {
            stride = (int)frame->step;
            buffer_size = frame->data_bytes;
        }

        // Allocate K4A Color buffer
        buffer = allocator_alloc(ALLOCATION_SOURCE_COLOR, buffer_size);
        k4a_result_t result = K4A_RESULT_FROM_BOOL(buffer != NULL);

        if (K4A_SUCCEEDED(result))
        {
            if (decodeMJPEG)
            {
                // Decode MJPG into BRGA32
                result = DecodeMJPEGtoBGRA32((uint8_t *)frame->data, frame->data_bytes, buffer, buffer_size);
                if (K4A_FAILED(result))
                {
                    drop_image = true;
                }
            }
            else
            {
                // Copy to K4A buffer
                memcpy(buffer, frame->data, buffer_size);
            }
        }

        if (K4A_SUCCEEDED(result))
        {
            // The buffer size may be larger than the height * stride for some formats
            // so we must use image_create_from_buffer rather than image_create
            result = TRACE_CALL(image_create_from_buffer(m_output_image_format,
                                                         (int)m_width_pixels,
                                                         (int)m_height_pixels,
                                                         stride,
                                                         buffer,
                                                         buffer_size,
                                                         uvc_camerareader_free_allocation,
                                                         context,
                                                         &image));
        }
        else
        {
            // cleanup if there was an error
            allocator_free(buffer);
        }

        k4a_capture_t capture = NULL;
        if (K4A_SUCCEEDED(result))
        {
            result = TRACE_CALL(capture_create(&capture));
        }

        if (K4A_SUCCEEDED(result))
        {
            // Set metadata
            uint64_t ts = (uint64_t)frame->capture_time_finished.tv_sec * 1000000000;
            ts += (uint64_t)frame->capture_time_finished.tv_nsec;
            image_set_system_timestamp_nsec(image, ts);
            image_set_device_timestamp_usec(image, K4A_90K_HZ_TICK_TO_USEC(framePTS));
            image_set_exposure_usec(image, exposure_time);
            image_set_iso_speed(image, iso_speed);
            image_set_white_balance(image, white_balance);

            // Set image
            capture_set_color_image(capture, image);
        }

        if (!drop_image)
        {
            // Calback to color
            m_pCallback(result, capture, m_pCallbackContext);
        }

        if (image)
        {
            image_dec_ref(image);
        }

        if (capture)
        {
            // We guarantee that capture is valid for the duration of the callback function, if someone
            // needs it to live longer, then they need to add a ref
            capture_dec_ref(capture);
        }
    }
}