mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-20 18:11:27 -07:00
196 lines
5.1 KiB
Markdown
196 lines
5.1 KiB
Markdown
# Stream Live Streaming API
|
|
|
|
Live input creation, status checking, simulcast, and WebRTC streaming.
|
|
|
|
## Create Live Input
|
|
|
|
### Using Cloudflare SDK
|
|
|
|
```typescript
|
|
import Cloudflare from 'cloudflare';
|
|
|
|
const client = new Cloudflare({ apiToken: env.CF_API_TOKEN });
|
|
|
|
const liveInput = await client.stream.liveInputs.create({
|
|
account_id: env.CF_ACCOUNT_ID,
|
|
recording: { mode: 'automatic', timeoutSeconds: 30 },
|
|
deleteRecordingAfterDays: 30
|
|
});
|
|
|
|
// Returns: { uid, rtmps, srt, webRTC }
|
|
```
|
|
|
|
### Raw fetch API
|
|
|
|
```typescript
|
|
async function createLiveInput(accountId: string, apiToken: string) {
|
|
const response = await fetch(
|
|
`https://api.cloudflare.com/client/v4/accounts/${accountId}/stream/live_inputs`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
recording: { mode: 'automatic', timeoutSeconds: 30 },
|
|
deleteRecordingAfterDays: 30
|
|
})
|
|
}
|
|
);
|
|
const { result } = await response.json();
|
|
return {
|
|
uid: result.uid,
|
|
rtmps: { url: result.rtmps.url, streamKey: result.rtmps.streamKey },
|
|
srt: { url: result.srt.url, streamId: result.srt.streamId, passphrase: result.srt.passphrase },
|
|
webRTC: result.webRTC
|
|
};
|
|
}
|
|
```
|
|
|
|
## Check Live Status
|
|
|
|
```typescript
|
|
async function getLiveStatus(accountId: string, liveInputId: string, apiToken: string) {
|
|
const response = await fetch(
|
|
`https://api.cloudflare.com/client/v4/accounts/${accountId}/stream/live_inputs/${liveInputId}`,
|
|
{ headers: { 'Authorization': `Bearer ${apiToken}` } }
|
|
);
|
|
const { result } = await response.json();
|
|
return {
|
|
isLive: result.status?.current?.state === 'connected',
|
|
recording: result.recording,
|
|
status: result.status
|
|
};
|
|
}
|
|
```
|
|
|
|
## Simulcast (Live Outputs)
|
|
|
|
### Create Output
|
|
|
|
```typescript
|
|
async function createLiveOutput(
|
|
accountId: string, liveInputId: string, apiToken: string,
|
|
outputUrl: string, streamKey: string
|
|
) {
|
|
return fetch(
|
|
`https://api.cloudflare.com/client/v4/accounts/${accountId}/stream/live_inputs/${liveInputId}/outputs`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Authorization': `Bearer ${apiToken}`, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
url: `${outputUrl}/${streamKey}`,
|
|
enabled: true,
|
|
streamKey // For platforms like YouTube, Twitch
|
|
})
|
|
}
|
|
).then(r => r.json());
|
|
}
|
|
```
|
|
|
|
### Example: Simulcast to YouTube + Twitch
|
|
|
|
```typescript
|
|
const liveInput = await createLiveInput(accountId, apiToken);
|
|
|
|
// Add YouTube output
|
|
await createLiveOutput(
|
|
accountId, liveInput.uid, apiToken,
|
|
'rtmp://a.rtmp.youtube.com/live2',
|
|
'your-youtube-stream-key'
|
|
);
|
|
|
|
// Add Twitch output
|
|
await createLiveOutput(
|
|
accountId, liveInput.uid, apiToken,
|
|
'rtmp://live.twitch.tv/app',
|
|
'your-twitch-stream-key'
|
|
);
|
|
```
|
|
|
|
## WebRTC Streaming (WHIP/WHEP)
|
|
|
|
### Browser to Stream (WHIP)
|
|
|
|
```typescript
|
|
async function startWebRTCBroadcast(liveInputId: string) {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
// Add local media tracks
|
|
const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
|
|
stream.getTracks().forEach(track => pc.addTrack(track, stream));
|
|
|
|
// Create offer
|
|
const offer = await pc.createOffer();
|
|
await pc.setLocalDescription(offer);
|
|
|
|
// Send to Stream via WHIP
|
|
const response = await fetch(
|
|
`https://customer-<CODE>.cloudflarestream.com/${liveInputId}/webRTC/publish`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/sdp' },
|
|
body: offer.sdp
|
|
}
|
|
);
|
|
|
|
const answer = await response.text();
|
|
await pc.setRemoteDescription({ type: 'answer', sdp: answer });
|
|
}
|
|
```
|
|
|
|
### Stream to Browser (WHEP)
|
|
|
|
```typescript
|
|
async function playWebRTCStream(videoId: string) {
|
|
const pc = new RTCPeerConnection();
|
|
|
|
pc.addTransceiver('video', { direction: 'recvonly' });
|
|
pc.addTransceiver('audio', { direction: 'recvonly' });
|
|
|
|
const offer = await pc.createOffer();
|
|
await pc.setLocalDescription(offer);
|
|
|
|
const response = await fetch(
|
|
`https://customer-<CODE>.cloudflarestream.com/${videoId}/webRTC/play`,
|
|
{
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/sdp' },
|
|
body: offer.sdp
|
|
}
|
|
);
|
|
|
|
const answer = await response.text();
|
|
await pc.setRemoteDescription({ type: 'answer', sdp: answer });
|
|
|
|
return pc;
|
|
}
|
|
```
|
|
|
|
## Recording Settings
|
|
|
|
| Mode | Behavior |
|
|
|------|----------|
|
|
| `automatic` | Record all live streams |
|
|
| `off` | No recording |
|
|
| `timeoutSeconds` | Stop recording after N seconds of inactivity |
|
|
|
|
```typescript
|
|
const recordingConfig = {
|
|
mode: 'automatic',
|
|
timeoutSeconds: 30, // Auto-stop 30s after stream ends
|
|
requireSignedURLs: true, // Require token for VOD playback
|
|
allowedOrigins: ['https://yourdomain.com']
|
|
};
|
|
```
|
|
|
|
## In This Reference
|
|
|
|
- [README.md](./README.md) - Overview and quick start
|
|
- [api.md](./api.md) - On-demand video APIs
|
|
- [configuration.md](./configuration.md) - Setup and config
|
|
- [patterns.md](./patterns.md) - Full-stack flows, best practices
|
|
- [gotchas.md](./gotchas.md) - Error codes, troubleshooting
|
|
|
|
## See Also
|
|
|
|
- [workers](../workers/) - Deploy live APIs in Workers
|