xiaohongshu
xiaohongshu
Real-time video broadcasting using RTMP, HLS, WebRTC protocols with streaming servers and cloud platforms for low-latency live video delivery.
Full skill instructions
Current Level: Advanced
Domain: Video Streaming / Media
Live streaming enables real-time video broadcasting. This guide covers RTMP, HLS, WebRTC, streaming servers, and cloud platforms for building scalable live streaming solutions with appropriate latency and quality.
| Protocol | Latency | Use Case | Browser Support |
|---|---|---|---|
| RTMP | 5-10s | Ingestion | No (Flash deprecated) |
| HLS | 10-30s | Playback | Yes (iOS native) |
| WebRTC | <1s | Low-latency | Yes |
| DASH | 10-30s | Playback | Yes (with player) |
# nginx.conf
rtmp {
server {
listen 1935;
chunk_size 4096;
application live {
live on;
record off;
# HLS
hls on;
hls_path /tmp/hls;
hls_fragment 3;
hls_playlist_length 60;
# DASH
dash on;
dash_path /tmp/dash;
# Allow publish from localhost only
allow publish 127.0.0.1;
deny publish all;
# Allow play from anywhere
allow play all;
# Webhook on publish
on_publish http://localhost:3000/api/stream/publish;
on_publish_done http://localhost:3000/api/stream/done;
}
application recording {
live on;
record all;
record_path /var/recordings;
record_suffix -%Y-%m-%d-%H-%M-%S.flv;
}
}
}
http {
server {
listen 8080;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /tmp;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *;
}
location /dash {
root /tmp;
add_header Cache-Control no-cache;
add_header Access-Control-Allow-Origin *;
}
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
}
}
// services/stream-key.service.ts
export class StreamKeyService {
async generateStreamKey(userId: string): Promise<StreamKey> {
const key = crypto.randomBytes(16).toString('hex');
const streamKey = await db.streamKey.create({
data: {
userId,
key,
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
}
});
return streamKey;
}
async validateStreamKey(key: string): Promise<boolean> {
const streamKey = await db.streamKey.findUnique({
where: { key }
});
if (!streamKey) return false;
if (streamKey.expiresAt < new Date()) {
return false;
}
return true;
}
getStreamUrl(streamKey: string): StreamUrls {
return {
rtmpUrl: `rtmp://${process.env.STREAM_SERVER}/live`,
streamKey,
playbackUrl: `https://${process.env.CDN_DOMAIN}/hls/${streamKey}.m3u8`
};
}
}
interface StreamUrls {
rtmpUrl: string;
streamKey: string;
playbackUrl: string;
}
// OBS Settings for users
const obsSettings = {
server: 'rtmp://stream.example.com/live',
streamKey: 'user-stream-key-here',
encoder: 'x264',
bitrate: 2500, // kbps
keyframeInterval: 2,
preset: 'veryfast',
profile: 'main',
tune: 'zerolatency'
};
// services/webrtc-stream.service.ts
import { RTCPeerConnection, RTCSessionDescription } from 'wrtc';
export class WebRTCStreamService {
private peerConnections = new Map<string, RTCPeerConnection>();
async createOffer(streamId: string): Promise<RTCSessionDescriptionInit> {
const pc = new RTCPeerConnection({
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' },
{
urls: 'turn:turn.example.com:3478',
username: process.env.TURN_USERNAME,
credential: process.env.TURN_PASSWORD
}
]
});
this.peerConnections.set(streamId, pc);
// Add transceivers for video and audio
pc.addTransceiver('video', { direction: 'recvonly' });
pc.addTransceiver('audio', { direction: 'recvonly' });
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
return offer;
}
async handleAnswer(streamId: string, answer: RTCSessionDescriptionInit): Promise<void> {
const pc = this.peerConnections.get(streamId);
if (!pc) throw new Error('Peer connection not found');
await pc.setRemoteDescription(new RTCSessionDescription(answer));
}
async addIceCandidate(streamId: string, candidate: RTCIceCandidateInit): Promise<void> {
const pc = this.peerConnections.get(streamId);
if (!pc) throw new Error('Peer connection not found');
await pc.addIceCandidate(candidate);
}
closePeerConnection(streamId: string): void {
const pc = this.peerConnections.get(streamId);
if (pc) {
pc.close();
this.peerConnections.delete(streamId);
}
}
}
// Client-side WebRTC player
async function playWebRTCStream(streamId: string) {
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});
pc.ontrack = (event) => {
const video = document.getElementById('video') as HTMLVideoElement;
video.srcObject = event.streams[0];
};
// Get offer from server
const response = await fetch(`/api/stream/${streamId}/offer`, {
method: 'POST'
});
const { offer } = await response.json();
await pc.setRemoteDescription(offer);
const answer = await pc.createAnswer();
await pc.setLocalDescription(answer);
// Send answer to server
await fetch(`/api/stream/${streamId}/answer`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ answer })
});
}
// services/stream-chat.service.ts
import { Server as SocketIOServer } from 'socket.io';
export class StreamChatService {
private io: SocketIOServer;
constructor(io: SocketIOServer) {
this.io = io;
this.setupHandlers();
}
private setupHandlers(): void {
this.io.on('connection', (socket) => {
socket.on('join-stream', async (streamId: string) => {
socket.join(`stream:${streamId}`);
// Increment viewer count
await this.incrementViewerCount(streamId);
// Broadcast viewer count
this.broadcastViewerCount(streamId);
});
socket.on('leave-stream', async (streamId: string) => {
socket.leave(`stream:${streamId}`);
await this.decrementViewerCount(streamId);
this.broadcastViewerCount(streamId);
});
socket.on('chat-message', async (data: ChatMessage) => {
// Save message
await db.chatMessage.create({
data: {
streamId: data.streamId,
userId: data.userId,
message: data.message
}
});
// Broadcast to all viewers
this.io.to(`stream:${data.streamId}`).emit('chat-message', {
username: data.username,
message: data.message,
timestamp: Date.now()
});
});
socket.on('disconnect', () => {
// Handle disconnect
});
});
}
private async incrementViewerCount(streamId: string): Promise<void> {
await db.stream.update({
where: { id: streamId },
data: { viewerCount: { increment: 1 } }
});
}
private async decrementViewerCount(streamId: string): Promise<void> {
await db.stream.update({
where: { id: streamId },
data: { viewerCount: { decrement: 1 } }
});
}
private async broadcastViewerCount(streamId: string): Promise<void> {
const stream = await db.stream.findUnique({ where: { id: streamId } });
this.io.to(`stream:${streamId}`).emit('viewer-count', {
count: stream?.viewerCount || 0
});
}
}
interface ChatMessage {
streamId: string;
userId: string;
username: string;
message: string;
}
// services/stream-recording.service.ts
import ffmpeg from 'fluent-ffmpeg';
export class StreamRecordingService {
async startRecording(streamId: string, hlsUrl: string): Promise<void> {
const outputPath = `/recordings/${streamId}-${Date.now()}.mp4`;
ffmpeg(hlsUrl)
.outputOptions([
'-c copy',
'-bsf:a aac_adtstoasc'
])
.output(outputPath)
.on('start', () => {
console.log('Recording started:', streamId);
})
.on('end', async () => {
console.log('Recording ended:', streamId);
await this.uploadRecording(streamId, outputPath);
})
.on('error', (err) => {
console.error('Recording error:', err);
})
.run();
await db.stream.update({
where: { id: streamId },
data: { recording: true }
});
}
private async uploadRecording(streamId: string, filePath: string): Promise<void> {
// Upload to S3
const s3Key = `recordings/${streamId}.mp4`;
await uploadToS3(filePath, s3Key, 'video/mp4');
// Save to database
await db.recording.create({
data: {
streamId,
s3Key,
url: `${process.env.CDN_URL}/${s3Key}`
}
});
}
}
// services/aws-ivs.service.ts
import { IVSClient, CreateChannelCommand, GetStreamCommand } from '@aws-sdk/client-ivs';
export class AWSIVSService {
private client: IVSClient;
constructor() {
this.client = new IVSClient({ region: process.env.AWS_REGION! });
}
async createChannel(name: string): Promise<IVSChannel> {
const command = new CreateChannelCommand({
name,
latencyMode: 'LOW', // or 'NORMAL'
type: 'STANDARD',
authorized: true
});
const response = await this.client.send(command);
return {
arn: response.channel?.arn!,
ingestEndpoint: response.channel?.ingestEndpoint!,
playbackUrl: response.channel?.playbackUrl!,
streamKey: response.streamKey?.value!
};
}
async getStreamStatus(channelArn: string): Promise<StreamStatus> {
const command = new GetStreamCommand({
channelArn
});
const response = await this.client.send(command);
return {
state: response.stream?.state!,
health: response.stream?.health!,
viewerCount: response.stream?.viewerCount || 0
};
}
}
interface IVSChannel {
arn: string;
ingestEndpoint: string;
playbackUrl: string;
streamKey: string;
}
interface StreamStatus {
state: string;
health: string;
viewerCount: number;
}
# FFmpeg command for adaptive streaming
ffmpeg -i input.mp4 \
-c:v libx264 -c:a aac \
-b:v:0 5000k -s:v:0 1920x1080 -profile:v:0 high \
-b:v:1 3000k -s:v:1 1280x720 -profile:v:1 main \
-b:v:2 1500k -s:v:2 854x480 -profile:v:2 main \
-b:v:3 800k -s:v:3 640x360 -profile:v:3 baseline \
-b:a 128k \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0 v:3,a:0" \
-master_pl_name master.m3u8 \
-f hls -hls_time 6 -hls_list_size 0 \
-hls_segment_filename "stream_%v/segment_%03d.ts" \
stream_%v/playlist.m3u8
# Stream to RTMP server
ffmpeg -re -i input.mp4 \
-c:v libx264 -preset veryfast -maxrate 3000k \
-bufsize 6000k -pix_fmt yuv420p -g 50 \
-c:a aac -b:a 160k -ac 2 -ar 44100 \
-f flv rtmp://server/live/stream_key
<!-- HLS.js for browser playback -->
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<video id="video"></video>
<script>
const video = document.getElementById('video')
const hls = new Hls()
hls.loadSource('https://server/stream.m3u8')
hls.attachMedia(video)
</script>
# ❌ Bad - Public stream key
rtmp://server/live/public_key # Anyone can stream!
# ✅ Good - Secure stream key
rtmp://server/live/secure-random-key-12345
# Rotate keys regularly
# ❌ Bad - One quality only
ffmpeg -i input.mp4 -b:v 3000k output.m3u8
# ✅ Good - Adaptive bitrate
ffmpeg -i input.mp4 \
-b:v:0 3000k -b:v:1 1500k -b:v:2 800k \
-var_stream_map "v:0 v:1 v:2" \
output.m3u8
// ❌ Bad - No stream health checks
stream.on('data', (chunk) => {
// No monitoring!
})
// ✅ Good - Monitor stream health
stream.on('data', (chunk) => {
metrics.increment('stream.bytes')
metrics.gauge('stream.bitrate', calculateBitrate(chunk))
})
// Alert on issues
if (bitrate < threshold) {
alert('Low bitrate detected')
}
37-video-streaming/cdn-delivery/) - CDN setup37-video-streaming/adaptive-bitrate/) - ABR streaming37-video-streaming/video-analytics/) - Viewer metricsxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience