k4a_stream_result_t get_capture()

in src/record/internal/matroska_read.cpp [1919:2088]


k4a_stream_result_t get_capture(k4a_playback_context_t *context, k4a_capture_t *capture_handle, bool next)
{
    RETURN_VALUE_IF_ARG(K4A_STREAM_RESULT_FAILED, context == NULL);
    RETURN_VALUE_IF_ARG(K4A_STREAM_RESULT_FAILED, capture_handle == NULL);

    track_reader_t *blocks[] = { context->color_track, context->depth_track, context->ir_track };
    std::shared_ptr<block_info_t> next_blocks[arraysize(blocks)];

    uint64_t timestamp_start_ns = UINT64_MAX;
    uint64_t timestamp_end_ns = 0;

    // Find the next block for each track
    int enabled_tracks = 0;
    for (size_t i = 0; i < arraysize(blocks); i++)
    {
        if (blocks[i] != NULL)
        {
            enabled_tracks++;

            // If the current block is NULL, find the next block before/after the seek timestamp.
            if (blocks[i]->current_block == nullptr)
            {
                next_blocks[i] = find_block(context, blocks[i], context->seek_timestamp_ns);
                if (!next && next_blocks[i])
                {
                    next_blocks[i] = next_block(context, next_blocks[i].get(), false);
                }
            }
            else
            {
                next_blocks[i] = next_block(context, blocks[i]->current_block.get(), next);
            }
            if (next_blocks[i] && next_blocks[i]->block)
            {
                // Find the lowest and highest timestamp out of the next blocks
                if (next_blocks[i]->sync_timestamp_ns < timestamp_start_ns)
                {
                    timestamp_start_ns = next_blocks[i]->sync_timestamp_ns;
                }
                if (next_blocks[i]->sync_timestamp_ns > timestamp_end_ns)
                {
                    timestamp_end_ns = next_blocks[i]->sync_timestamp_ns;
                }
            }
            else
            {
                LOG_TRACE("%s of recording reached", next ? "End" : "Beginning");
                blocks[i]->current_block = next_blocks[i];
            }
        }
    }

    // Count how many of the blocks are within the sync window
    int valid_blocks = 0;
    if (enabled_tracks > 0)
    {
        if (next)
        {
            timestamp_end_ns = timestamp_start_ns;
        }
        else
        {
            timestamp_start_ns = timestamp_end_ns;
        }

        for (size_t i = 0; i < arraysize(blocks); i++)
        {
            if (next_blocks[i] && next_blocks[i]->block)
            {
                // If we're seeking forward, check the start timestamp, otherwise check the end timestamp
                if (next && (next_blocks[i]->sync_timestamp_ns - timestamp_start_ns < context->sync_period_ns / 2))
                {
                    valid_blocks++;
                    if (next_blocks[i]->sync_timestamp_ns > timestamp_end_ns)
                    {
                        timestamp_end_ns = next_blocks[i]->sync_timestamp_ns;
                    }
                }
                else if (!next && (timestamp_end_ns - next_blocks[i]->sync_timestamp_ns < context->sync_period_ns / 2))
                {
                    valid_blocks++;
                    if (next_blocks[i]->sync_timestamp_ns < timestamp_start_ns)
                    {
                        timestamp_start_ns = next_blocks[i]->sync_timestamp_ns;
                    }
                }
                else
                {
                    // Don't use this block as part of the capture
                    next_blocks[i] = nullptr;
                }
            }
        }

        if (valid_blocks < enabled_tracks)
        {
            // Try filling in any blocks that were missed due to a seek
            bool filled = false;
            for (size_t i = 0; i < arraysize(blocks); i++)
            {
                if (blocks[i] != NULL && next_blocks[i] == nullptr && blocks[i]->current_block == nullptr)
                {
                    std::shared_ptr<block_info_t> test_block = find_block(context,
                                                                          blocks[i],
                                                                          context->seek_timestamp_ns);
                    if (next)
                    {
                        test_block = next_block(context, test_block.get(), false);
                    }
                    if (test_block && test_block->block)
                    {
                        if (next && (timestamp_end_ns - test_block->sync_timestamp_ns < context->sync_period_ns / 2))
                        {
                            valid_blocks++;
                            next_blocks[i] = test_block;
                            filled = true;
                        }
                        else if (!next &&
                                 (test_block->sync_timestamp_ns - timestamp_start_ns < context->sync_period_ns / 2))
                        {
                            valid_blocks++;
                            next_blocks[i] = test_block;
                            filled = true;
                        }
                    }
                }
            }
            if (!next && filled)
            {
                // We seeked to the middle of a capture and then called previous capture, the current state is actually
                // for next_capture. Save the state and make a call to previous capture.
                for (size_t i = 0; i < arraysize(blocks); i++)
                {
                    if (next_blocks[i])
                    {
                        blocks[i]->current_block = next_blocks[i];
                    }
                }

                return get_capture(context, capture_handle, false);
            }
        }
    }

    LOG_TRACE("Valid blocks: %d/%d, Start: %llu ms, End: %llu ms",
              valid_blocks,
              enabled_tracks,
              timestamp_start_ns / 1_ms,
              timestamp_end_ns / 1_ms);

    *capture_handle = NULL;
    for (size_t i = 0; i < arraysize(blocks); i++)
    {
        if (next_blocks[i] && next_blocks[i]->block)
        {
            blocks[i]->current_block = next_blocks[i];
            k4a_result_t result = TRACE_CALL(new_capture(context, blocks[i]->current_block.get(), capture_handle));
            if (K4A_FAILED(result))
            {
                if (*capture_handle != NULL)
                {
                    k4a_capture_release(*capture_handle);
                    *capture_handle = NULL;
                }
                return K4A_STREAM_RESULT_FAILED;
            }
        }
    }
    return valid_blocks == 0 ? K4A_STREAM_RESULT_EOF : K4A_STREAM_RESULT_SUCCEEDED;
}