Live Streaming
Broadcast live audio and video to your Web3 community with support for chat, reactions, and token-gating.
Creating a Live Stream
Basic Stream
const stream = await dial.streaming.create({
title: 'Weekly AMA',
description: 'Ask me anything about Web3',
isPublic: true
});
console.log('Stream URL:', stream.url);
console.log('Stream Key:', stream.streamKey);Gated Stream
const stream = await dial.streaming.create({
title: 'Exclusive Holder Stream',
description: 'NFT holders only',
isPublic: false,
gate: {
type: 'nft',
collection: '0x...',
chain: 'ethereum'
}
});Stream Object
interface LiveStream {
id: string;
title: string;
description: string;
host: string; // Wallet address
streamKey: string;
url: string;
playbackUrl: string;
status: 'idle' | 'live' | 'ended';
viewerCount: number;
startedAt?: Date;
endedAt?: Date;
isPublic: boolean;
gate?: GateConfig;
}Starting a Stream
Browser-Based Streaming
// Start streaming from browser
await dial.streaming.start({
streamId: stream.id,
video: true,
audio: true,
quality: '1080p'
});External Encoder (OBS, etc.)
// Get RTMP details for external encoders
const rtmpDetails = await dial.streaming.getRTMPDetails(stream.id);
console.log('RTMP URL:', rtmpDetails.url);
console.log('Stream Key:', rtmpDetails.streamKey);
// Configure in OBS:
// Server: rtmpDetails.url
// Stream Key: rtmpDetails.streamKeyWatching a Stream
// Get stream details
const stream = await dial.streaming.get(streamId);
// Check if live
if (stream.status === 'live') {
// Load player
const player = dial.streaming.createPlayer({
streamId: stream.id,
container: document.getElementById('player'),
autoplay: true
});
}Viewer Management
Get Viewers
const viewers = await dial.streaming.getViewers(streamId);
console.log(`${viewers.length} watching`);Listen for Viewer Events
dial.on('stream:viewer:joined', ({ viewer }) => {
console.log(`${viewer.displayName} joined`);
});
dial.on('stream:viewer:left', ({ viewer }) => {
console.log(`${viewer.displayName} left`);
});
dial.on('stream:viewer:count', ({ count }) => {
console.log(`${count} viewers`);
});Live Chat
Send Message
await dial.streaming.sendChatMessage(streamId, {
content: 'Great stream!',
type: 'text'
});Receive Messages
dial.on('stream:chat:message', ({ message, sender }) => {
console.log(`${sender.displayName}: ${message.content}`);
});Moderate Chat
// Delete message (host/moderator only)
await dial.streaming.deleteChatMessage(streamId, messageId);
// Ban user from chat
await dial.streaming.banFromChat(streamId, walletAddress);
// Enable slow mode
await dial.streaming.setChatSlowMode(streamId, {
enabled: true,
interval: 5 // seconds between messages
});Reactions
Send Reaction
await dial.streaming.sendReaction(streamId, {
emoji: '🔥'
});Listen for Reactions
dial.on('stream:reaction', ({ emoji, sender }) => {
console.log(`${sender.displayName} reacted with ${emoji}`);
});Stream Settings
Update Stream Info
await dial.streaming.update(streamId, {
title: 'Updated Title',
description: 'New description'
});Stream Quality Settings
await dial.streaming.setQuality(streamId, {
resolution: '1080p', // '720p', '1080p', '4k'
frameRate: 60,
bitrate: 6000 // kbps
});Enable DVR
// Allow viewers to rewind
await dial.streaming.enableDVR(streamId, {
enabled: true,
bufferDuration: 3600 // 1 hour in seconds
});Recording
Auto-Record
const stream = await dial.streaming.create({
title: 'Recorded Stream',
autoRecord: true
});Manual Recording
// Start recording
await dial.streaming.startRecording(streamId);
// Stop recording
const recording = await dial.streaming.stopRecording(streamId);
console.log('Recording URL:', recording.url);Get Recordings
const recordings = await dial.streaming.getRecordings(streamId);
recordings.forEach(rec => {
console.log(`Recording: ${rec.url}`);
console.log(`Duration: ${rec.duration}s`);
});Ending a Stream
await dial.streaming.end(streamId);Complete Example: React Live Stream Component
import { useState, useEffect } from 'react';
import { DialClient, LiveStream } from '@dial/sdk';
export function LiveStreamHost({ dial }) {
const [stream, setStream] = useState<LiveStream | null>(null);
const [isLive, setIsLive] = useState(false);
const [viewerCount, setViewerCount] = useState(0);
const [chatMessages, setChatMessages] = useState([]);
useEffect(() => {
if (stream) {
setupEventListeners();
}
return () => {
if (stream && isLive) {
dial.streaming.end(stream.id);
}
};
}, [stream]);
const setupEventListeners = () => {
dial.on('stream:viewer:count', ({ count }) => {
setViewerCount(count);
});
dial.on('stream:chat:message', ({ message, sender }) => {
setChatMessages(prev => [...prev, { message, sender }]);
});
};
const createStream = async () => {
const newStream = await dial.streaming.create({
title: 'My Live Stream',
description: 'Going live!',
isPublic: true
});
setStream(newStream);
};
const goLive = async () => {
if (!stream) return;
await dial.streaming.start({
streamId: stream.id,
video: true,
audio: true,
quality: '1080p'
});
setIsLive(true);
};
const endStream = async () => {
if (!stream) return;
await dial.streaming.end(stream.id);
setIsLive(false);
};
if (!stream) {
return (
<button onClick={createStream}>
Create Stream
</button>
);
}
return (
<div className="stream-host">
<div className="stream-preview">
<video
ref={(el) => {
if (el && isLive) {
const localStream = dial.streaming.getLocalStream(stream.id);
el.srcObject = localStream;
}
}}
autoPlay
muted
/>
{isLive && (
<div className="live-indicator">
🔴 LIVE • {viewerCount} viewers
</div>
)}
</div>
<div className="stream-controls">
{!isLive ? (
<button onClick={goLive}>Go Live</button>
) : (
<button onClick={endStream} className="danger">
End Stream
</button>
)}
</div>
<div className="stream-chat">
<h3>Live Chat</h3>
<div className="messages">
{chatMessages.map((msg, i) => (
<div key={i} className="message">
<strong>{msg.sender.displayName}:</strong>
{msg.message.content}
</div>
))}
</div>
</div>
<div className="stream-info">
<h3>Stream Details</h3>
<p>URL: {stream.url}</p>
<p>Stream Key: {stream.streamKey}</p>
</div>
</div>
);
}
export function LiveStreamViewer({ dial, streamId }) {
const [stream, setStream] = useState<LiveStream | null>(null);
const [chatInput, setChatInput] = useState('');
useEffect(() => {
loadStream();
}, [streamId]);
const loadStream = async () => {
const streamData = await dial.streaming.get(streamId);
setStream(streamData);
};
const sendMessage = async () => {
if (!chatInput.trim()) return;
await dial.streaming.sendChatMessage(streamId, {
content: chatInput,
type: 'text'
});
setChatInput('');
};
const sendReaction = async (emoji: string) => {
await dial.streaming.sendReaction(streamId, { emoji });
};
if (!stream) return <div>Loading...</div>;
return (
<div className="stream-viewer">
<div
id="player"
className="player-container"
/>
<div className="reactions">
<button onClick={() => sendReaction('🔥')}>🔥</button>
<button onClick={() => sendReaction('❤️')}>❤️</button>
<button onClick={() => sendReaction('👍')}>👍</button>
<button onClick={() => sendReaction('😂')}>😂</button>
</div>
<div className="chat">
<input
value={chatInput}
onChange={(e) => setChatInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Send a message..."
/>
<button onClick={sendMessage}>Send</button>
</div>
</div>
);
}Analytics
const analytics = await dial.streaming.getAnalytics(streamId);
console.log('Total views:', analytics.totalViews);
console.log('Peak viewers:', analytics.peakViewers);
console.log('Average watch time:', analytics.avgWatchTime);
console.log('Chat messages:', analytics.chatMessageCount);
console.log('Reactions:', analytics.reactionCount);Multi-Stream (Simulcast)
Stream to multiple platforms simultaneously:
await dial.streaming.enableSimulcast(streamId, [
{
platform: 'youtube',
streamKey: 'your-youtube-key'
},
{
platform: 'twitch',
streamKey: 'your-twitch-key'
}
]);Next Steps
Last updated on