biigle/core

Streamed annotation responses

Closed this issue · 1 comments

mzur commented

We had a case of a long video where the request to get all video annotations ran into the memory limit. To avoid this we can send a streamed JSON response and fetch the annotations lazily in chunks. Here is a proof of concept for the VideoAnnotationController:

public function index(Request $request, $id)
{
    $video = Video::findOrFail($id);
    $this->authorize('access', $video);

    $user = $request->user();
    $session = $video->volume->getActiveAnnotationSession($user);
    $load = ['labels.label', 'labels.user'];

    if ($session) {
        return $session->getVolumeFileAnnotations($video, $user)->load($load);
    }

    $yieldAnnotations = function () use ($video, $load): \Generator {
        foreach ($video->annotations()->with($load)->lazy() as $annotation) {
            yield $annotation;
        }
    };

    return response()->streamJson($yieldAnnotations());
}

The same could be done in the ImageAnnotationController.

In the proof of concept above the streaming does not work if there is an active annotation session. The getVolumeFileAnnotations() method of an annotation session has to be updated to support streaming like that.

mzur commented

Here is an example how a streamed response can be tested:

$response = $this
    ->getJson("/api/v1/videos/{$this->video->id}/annotations")
    ->assertStatus(200);

ob_start();
$response->sendContent();
$content = ob_get_clean();
$response = new \Illuminate\Testing\TestResponse(
    new \Symfony\Component\HttpFoundation\Response($content,
        $response->baseResponse->getStatusCode(),
        $response->baseResponse->headers->all()
    )
);

$response->assertJsonFragment(['frames' => [1.0]])
    ->assertJsonFragment(['points' => [[10, 20]]])
    ->assertJsonFragment(['color' => 'bada55'])
    ->assertJsonFragment(['name' => 'My label']);