# Render videos in PHP

> Render a video template as an MP4 and follow its progress with the PHP SDK.

Canonical: https://bannerify.co/docs/sdk/php/apis/render-video

A video template renders as an MP4. Each second of the video counts as one generation, and as two at 60 fps. [Generate videos](https://bannerify.co/docs/essentials/generate-videos) explains how to design one.

`renderVideo` and `renderAndWaitVideo` take a template id and an options array.

<ParamField body="templateId" type="string" required>
  The id of a video template, for example `tpl_xxx`.
</ParamField>

<ParamField body="modifications" type="array">
  The values to put into the template's layers. A video layer takes a `src`, and an audio track takes a `src` with its name. [Modifications](https://bannerify.co/docs/essentials/modifications) lists the fields each layer accepts.
</ParamField>

## Render and wait for the URL

`renderAndWaitVideo` returns `['result' => $url]` or `['error' => [...]]`.

```php
$result = $client->renderAndWaitVideo('tpl_xxx', [
    'modifications' => [
        ['name' => 'headline', 'text' => 'Summer sale'],
        ['name' => 'music', 'src' => 'https://example.com/song.mp3'],
    ],
]);

if (isset($result['error'])) {
    throw new RuntimeException($result['error']['message']);
}

echo $result['result'];
```

## Follow the progress

`renderVideo` returns a `Generator` of event arrays. The last event is `done` or `error`.

```php
foreach ($client->renderVideo('tpl_xxx', ['modifications' => [['name' => 'headline', 'text' => 'Summer sale']]]) as $event) {
    match ($event['type']) {
        'created' => printf("Video %s\n", $event['id']),
        'queued' => printf("%d videos before this one\n", $event['ahead']),
        'progress' => printf("%d%%\n", $event['progress'] * 100),
        'done' => print($event['url'] . "\n"),
        'error' => print($event['error']['message'] . "\n"),
    };
}
```

| Event | Fields |
| --- | --- |
| `created` | `id`: the id of the video, for example `vid_xxx` |
| `queued` | `ahead`: the number of videos before this one |
| `progress` | `frame`, `totalFrames`, and `progress` from 0 to 1 |
| `done` | `url`: the URL of the MP4 |
| `error` | `error`: `code`, `message`, and `requestId` |

A refused request, such as an invalid API key or a used-up quota, gives one `error` event. The client timeout applies only to the connection, because a video can wait in line for minutes.

## Reconnect after a dropped connection

The video renders to its end, even when the connection drops. Then `renderVideo` reads the same video again by the id from the `created` event, and it continues to yield events. It never sends the render again, so you do not pay for a second video.

| Attempt | Wait before it |
| --- | --- |
| 1 | 1 second |
| 2 | 2 seconds |
| 3 | 4 seconds |
| 4 | 8 seconds |

When the attempts run out, or the connection drops before the `created` event, the last event is an `error` with the code `TIMEOUT_ERROR`. `renderAndWaitVideo` reconnects in the same way.

## Start a video without waiting

`createVideo` starts the video and returns its id at once. It does not wait for the render. Then read the video with `getVideo` or `watchVideo`.

```php
$result = $client->createVideo('tpl_xxxxxxxxx', [['name' => 'title', 'text' => 'Hi']]);

if (isset($result['error'])) {
    throw new RuntimeException($result['error']['message']);
}

echo $result['result']['id']; // vid_xxx, with the status queued
```

`createVideo` sends the render only once. A second request renders a second video.

## Read a video again

Keep the id from the `created` event. `getVideo` returns the status of the video, and its URL when it is done.

```php
$result = $client->getVideo('vid_xxx');

if (isset($result['error'])) {
    throw new RuntimeException($result['error']['message']);
}

echo $result['result']['status'] . ' ' . ($result['result']['url'] ?? '');
```

`status` is `queued`, `rendering`, `done`, or `failed`. A failed video has an `error` string. An unknown id gives an error with the code `NOT_FOUND`.

`watchVideo` yields the events of the video again, from the latest one. It has no `created` event, and it reconnects as `renderVideo` does.

```php
foreach ($client->watchVideo('vid_xxx') as $event) {
    if ($event['type'] === 'done') {
        echo $event['url'] . "\n";
    }
}
```

## Related

- [Previous: Create images and PDFs in PHP](https://bannerify.co/docs/sdk/php/apis/create-image)
- [Next: Signed URLs in PHP](https://bannerify.co/docs/sdk/php/apis/signed-url)
- [Documentation index](https://bannerify.co/docs/llms.txt)
