ffmpeg-stream-manager
v1.0.3
Published
🎥 A powerful TypeScript library for managing multiple simultaneous RTMP streams using FFmpeg. Perfect for streaming to platforms like YouTube Live, Twitch, and others.
Downloads
8
Maintainers
Readme
🎥 FFmpegStreamManager
Uma biblioteca poderosa em TypeScript para gerenciar múltiplas transmissões RTMP simultâneas usando FFmpeg. Ideal para streaming para plataformas como YouTube Live, Twitch e outras que suportam RTMP.
📖 Índice
- Características
- Instalação
- Uso Rápido
- API Completa
- Exemplos Detalhados
- Configurações
- Eventos
- Tratamento de Erros
- Logs e Monitoramento
- Tipos TypeScript
- Configurações Pré-definidas
- Requisitos do Sistema
- FAQ
- Contribuição
- Licença
🚀 Características
✨ Funcionalidades Principais
- 🔄 Múltiplas transmissões simultâneas: Gerencie até N streams em paralelo
- 🎯 Suporte completo ao YouTube Live: Configurações otimizadas para RTMP
- 📹 Tipos de entrada flexíveis:
- Arquivos de vídeo (.mp4, .avi, .mov, .mkv, etc.) - ÚNICO ou MÚLTIPLOS
- Arquivos de áudio (.mp3, .wav, .aac, .flac) com imagem estática - PLAYLIST
- Geradores FFmpeg (testsrc, anullsrc, color, etc.)
- 🎵 Playlists automáticas: Múltiplos arquivos em sequência com loop infinito
- ♾️ Loop infinito: Reinicialização automática quando o arquivo termina
- 🔄 Reinício automático: Recuperação automática de falhas
- ⚙️ Gerenciamento completo: Iniciar, parar, pausar, retomar e atualizar configurações
- 📊 Logs detalhados: Captura completa de stdout/stderr do FFmpeg
- 🛡️ TypeScript 100%: Tipagem forte e segura
- 🔍 Monitoramento em tempo real: Status, uptime, logs por stream
- 🚫 Sem dependências web: Biblioteca pura para uso interno
🎛️ Controles Avançados
- Configuração dinâmica de bitrate, resolução e codec
- Eventos em tempo real para todos os estados do stream
- Filtros de log por stream ID e nível
- Configurações otimizadas para diferentes plataformas
- Gerenciamento automático de recursos
📦 Instalação
npm install ffmpeg-stream-managerPré-requisitos:
- Node.js: 16.0 ou superior
- FFmpeg: 4.0 ou superior instalado no sistema
- TypeScript: 4.5+ (para desenvolvimento)
Instalação do FFmpeg
Ubuntu/Debian:
sudo apt update && sudo apt install ffmpegCentOS/RHEL/Fedora:
sudo dnf install ffmpegmacOS:
brew install ffmpegWindows:
- Baixe de https://ffmpeg.org/download.html
- Adicione ao PATH do sistema
🔧 Uso Rápido
import { FFmpegStreamManager } from 'ffmpeg-stream-manager';
async function exemploRapido() {
// Criar gerenciador
const manager = new FFmpegStreamManager({
maxConcurrentStreams: 5,
autoRestart: true,
logLevel: 'info'
});
// Configurar eventos
manager.on('stream-started', (streamId) => {
console.log(`✅ Stream iniciado: ${streamId}`);
});
manager.on('stream-error', (streamId, error) => {
console.error(`❌ Erro: ${error.message}`);
});
try {
// Iniciar stream para YouTube
const streamId = await manager.startYouTubeStream(
{
inputType: 'video',
inputPath: '/caminho/para/video.mp4',
staticImagePath: undefined,
loop: true,
...FFmpegStreamManager.getDefaultConfigs().youTube1080p
},
{
streamKey: 'sua-chave-youtube',
server: undefined // usa servidor padrão
}
);
console.log(`🎥 Stream ativo: ${streamId}`);
// Monitorar status
const status = manager.getStreamStatus(streamId);
console.log(`Status: ${status.status}, Uptime: ${status.uptime}s`);
} catch (error) {
console.error('Erro:', error);
} finally {
// Limpar recursos
await manager.destroy();
}
}📚 API Completa
FFmpegStreamManager
Constructor
new FFmpegStreamManager(options?: StreamManagerOptions)Parâmetros:
interface StreamManagerOptions {
maxConcurrentStreams?: number; // Limite de streams (padrão: 10)
autoRestart?: boolean; // Auto-restart em falhas (padrão: true)
restartDelay?: number; // Delay entre restarts em ms (padrão: 5000)
logLevel?: 'debug' | 'info' | 'warning' | 'error'; // Nível de log (padrão: 'info')
}Métodos Principais
startStream(config: StreamConfig): Promise<string>
Inicia um novo stream com configuração personalizada.
const streamId = await manager.startStream({
rtmpUrl: 'rtmp://servidor.com/live/chave',
inputType: 'video',
inputPath: '/caminho/video.mp4',
staticImagePath: undefined,
loop: true,
videoConfig: {
width: 1920,
height: 1080,
bitrate: '4500k',
framerate: 30,
codec: 'libx264',
profile: 'high',
level: '4.1'
},
audioConfig: {
codec: 'aac',
bitrate: '128k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: 'zerolatency',
bufsize: '9000k',
maxrate: '4500k'
}
});startYouTubeStream(config, youtubeConfig): Promise<string>
Inicia um stream otimizado para YouTube Live.
const streamId = await manager.startYouTubeStream(
{
inputType: 'audio',
inputPath: '/caminho/audio.mp3',
staticImagePath: '/caminho/imagem.jpg',
loop: true,
...FFmpegStreamManager.getDefaultConfigs().youTube720p
},
{
streamKey: 'sua-chave-youtube',
server: 'rtmp://a.rtmp.youtube.com/live2/' // opcional
}
);stopStream(streamId: string): Promise<void>
Para um stream específico.
restartStream(streamId: string): Promise<void>
Reinicia um stream.
updateStreamConfig(streamId: string, updates: StreamUpdate): void
Atualiza configurações (stream deve estar parado).
getStreamStatus(streamId: string): StreamStatus
Obtém status detalhado de um stream.
getAllStreamStatuses(): StreamStatus[]
Obtém status de todos os streams.
getActiveStreamIds(): string[]
Lista IDs dos streams ativos.
stopAllStreams(): Promise<void>
Para todos os streams.
destroy(): Promise<void>
Limpa todos os recursos.
Configurações Pré-definidas
const configs = FFmpegStreamManager.getDefaultConfigs();
// Disponíveis:
// - youTube1080p: 1920x1080 @ 4500k
// - youTube720p: 1280x720 @ 2500k
// - youTube480p: 854x480 @ 1000k🎬 Exemplos Detalhados
Stream de Vídeo para YouTube
import { FFmpegStreamManager } from 'ffmpeg-stream-manager';
async function streamVideoYoutube() {
const manager = new FFmpegStreamManager();
const streamId = await manager.startYouTubeStream(
{
inputType: 'video',
inputPath: '/videos/meu-video.mp4',
staticImagePath: undefined,
loop: true,
...FFmpegStreamManager.getDefaultConfigs().youTube1080p
},
{
streamKey: 'abcd-efgh-ijkl-mnop'
}
);
console.log(`Stream iniciado: ${streamId}`);
}Stream de Áudio com Imagem Estática
async function streamAudioComImagem() {
const manager = new FFmpegStreamManager();
const streamId = await manager.startStream({
rtmpUrl: 'rtmp://live.twitch.tv/live/sua-chave',
inputType: 'audio',
inputPath: '/audio/musica.mp3',
staticImagePath: '/imagens/capa.jpg',
loop: true,
videoConfig: {
width: 1280,
height: 720,
bitrate: '2500k',
framerate: 30,
codec: 'libx264',
profile: 'main',
level: '3.1'
},
audioConfig: {
codec: 'aac',
bitrate: '128k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: 'zerolatency',
bufsize: '5000k',
maxrate: '2500k'
}
});
return streamId;
}Múltiplos Streams Simultâneos
async function multiplosStreams() {
const manager = new FFmpegStreamManager({
maxConcurrentStreams: 3
});
const streams = await Promise.allSettled([
manager.startYouTubeStream(configYoutube1, { streamKey: 'key1' }),
manager.startYouTubeStream(configYoutube2, { streamKey: 'key2' }),
manager.startStream(configTwitch)
]);
streams.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Stream ${index + 1} iniciado: ${result.value}`);
} else {
console.error(`Stream ${index + 1} falhou: ${result.reason}`);
}
});
}Stream com Múltiplos Arquivos
async function streamMultiplosArquivos() {
const manager = new FFmpegStreamManager();
// Múltiplos vídeos em sequência (playlist)
const videoStreamId = await manager.startYouTubeStream({
inputType: 'video',
inputPath: [
'/videos/intro.mp4',
'/videos/episodio1.mp4',
'/videos/episodio2.mp4',
'/videos/outro.mp4'
], // Array de arquivos
staticImagePath: undefined,
loop: true, // Repete a playlist infinitamente
...FFmpegStreamManager.getDefaultConfigs().youTube1080p
}, {
streamKey: 'sua-chave-youtube'
});
// Múltiplos áudios com imagem estática
const audioStreamId = await manager.startStream({
rtmpUrl: 'rtmp://live.twitch.tv/live/chave',
inputType: 'audio',
inputPath: [
'/musicas/track1.mp3',
'/musicas/track2.mp3',
'/musicas/track3.mp3'
], // Playlist de músicas
staticImagePath: '/imagens/capa-album.jpg',
loop: true, // Radio 24/7
videoConfig: {
width: 1280,
height: 720,
bitrate: '2500k',
framerate: 30,
codec: 'libx264',
profile: 'main',
level: '3.1'
},
audioConfig: {
codec: 'aac',
bitrate: '128k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: 'zerolatency',
bufsize: '5000k',
maxrate: '2500k'
}
});
return { videoStreamId, audioStreamId };
}Stream com Gerador FFmpeg
async function streamGenerado() {
const manager = new FFmpegStreamManager();
// Stream de cor sólida com áudio silencioso
const streamId = await manager.startStream({
rtmpUrl: 'rtmp://servidor.com/live/test',
inputType: 'video',
inputPath: 'color=blue:size=1920x1080:rate=30',
staticImagePath: undefined,
loop: true,
...FFmpegStreamManager.getDefaultConfigs().youTube720p
});
return streamId;
}⚙️ Configurações
StreamConfig Interface
interface StreamConfig {
id: string; // Auto-gerado (UUID v4)
rtmpUrl: string; // URL RTMP de destino
inputType: 'video' | 'audio'; // Tipo de entrada
inputPath: string | string[]; // Arquivo único ou múltiplos (playlist)
staticImagePath: string | undefined; // Imagem para streams de áudio
loop: boolean; // Loop infinito
videoConfig: VideoConfig; // Configurações de vídeo
audioConfig: AudioConfig; // Configurações de áudio
presetConfig: PresetConfig; // Configurações de preset
}VideoConfig Interface
interface VideoConfig {
width: number; // Largura em pixels
height: number; // Altura em pixels
bitrate: string; // Taxa de bits (ex: "4500k")
framerate: number; // FPS (ex: 30)
codec: string; // Codec (ex: "libx264")
profile: string | undefined; // Perfil H.264 (ex: "high")
level: string | undefined; // Nível H.264 (ex: "4.1")
}AudioConfig Interface
interface AudioConfig {
codec: string; // Codec (ex: "aac")
bitrate: string; // Taxa de bits (ex: "128k")
sampleRate: number; // Taxa de amostragem (ex: 44100)
channels: number; // Canais (ex: 2 para estéreo)
}PresetConfig Interface
interface PresetConfig {
preset: string; // Preset FFmpeg (ex: "fast")
tune: string | undefined; // Tunning (ex: "zerolatency")
bufsize: string | undefined; // Buffer size (ex: "5000k")
maxrate: string | undefined; // Taxa máxima (ex: "2500k")
}📡 Eventos
Configurando Event Listeners
const manager = new FFmpegStreamManager();
// Stream iniciado
manager.on('stream-started', (streamId: string) => {
console.log(`✅ Stream iniciado: ${streamId}`);
});
// Stream parado
manager.on('stream-stopped', (streamId: string) => {
console.log(`⏹️ Stream parado: ${streamId}`);
});
// Erro no stream
manager.on('stream-error', (streamId: string, error: Error) => {
console.error(`❌ Erro no stream ${streamId}: ${error.message}`);
});
// Stream reiniciado
manager.on('stream-restarted', (streamId: string) => {
console.log(`🔄 Stream reiniciado: ${streamId}`);
});
// Logs do FFmpeg
manager.on('log', (log: FFmpegLog) => {
console.log(`[${log.level}] [${log.streamId}] ${log.message}`);
});Filtrando Logs por Stream
// Logs de um stream específico
manager.on('log', (log) => {
if (log.streamId === 'meu-stream-id') {
console.log(`Log específico: ${log.message}`);
}
});
// Apenas logs de erro
manager.on('log', (log) => {
if (log.level === 'error') {
console.error(`❌ Erro: ${log.message}`);
}
});
// Excluir logs do manager
manager.on('log', (log) => {
if (log.streamId !== 'manager') {
console.log(`Stream log: ${log.message}`);
}
});🚨 Tratamento de Erros
Try-Catch Básico
try {
const streamId = await manager.startStream(config);
console.log('Stream iniciado com sucesso');
} catch (error) {
if (error instanceof StreamError) {
console.error(`Erro no stream ${error.streamId}: ${error.message}`);
} else {
console.error('Erro desconhecido:', error);
}
}Event Listeners para Erros
manager.on('stream-error', (streamId, error) => {
console.error(`Stream ${streamId} falhou: ${error.message}`);
// Tentar reiniciar após 5 segundos
setTimeout(async () => {
try {
await manager.restartStream(streamId);
console.log(`Stream ${streamId} reiniciado com sucesso`);
} catch (restartError) {
console.error(`Falha ao reiniciar stream ${streamId}:`, restartError);
}
}, 5000);
});Validação de Configuração
import { FFmpegCommandBuilder } from 'ffmpeg-stream-manager';
try {
FFmpegCommandBuilder.validateConfig(config);
console.log('Configuração válida');
} catch (error) {
console.error('Configuração inválida:', error.message);
}📊 Logs e Monitoramento
Monitoramento em Tempo Real
// Monitorar todos os streams a cada 30 segundos
setInterval(() => {
const statuses = manager.getAllStreamStatuses();
console.log(`\n📊 Status dos Streams (${statuses.length} total):`);
statuses.forEach(status => {
const emoji = status.status === 'running' ? '🟢' :
status.status === 'error' ? '🔴' : '🟡';
console.log(`${emoji} ${status.id}:`);
console.log(` Status: ${status.status}`);
console.log(` Uptime: ${status.uptime}s`);
console.log(` Restarts: ${status.restartCount}`);
if (status.lastError) {
console.log(` Last Error: ${status.lastError}`);
}
});
}, 30000);Sistema de Logs Personalizado
class StreamLogger {
private logFile: string;
constructor(logFile: string) {
this.logFile = logFile;
}
setupLogging(manager: FFmpegStreamManager) {
manager.on('log', (log) => {
const timestamp = log.timestamp.toISOString();
const logLine = `${timestamp} [${log.level}] [${log.streamId}] ${log.message}\n`;
// Escrever no arquivo (usando fs)
fs.appendFileSync(this.logFile, logLine);
// Console apenas para erros
if (log.level === 'error') {
console.error(logLine.trim());
}
});
manager.on('stream-started', (streamId) => {
this.logEvent('STARTED', streamId);
});
manager.on('stream-stopped', (streamId) => {
this.logEvent('STOPPED', streamId);
});
}
private logEvent(event: string, streamId: string) {
const timestamp = new Date().toISOString();
const logLine = `${timestamp} [EVENT] [${streamId}] ${event}\n`;
fs.appendFileSync(this.logFile, logLine);
}
}
// Uso
const logger = new StreamLogger('./streams.log');
logger.setupLogging(manager);🔧 Tipos TypeScript
StreamStatus Interface
interface StreamStatus {
id: string; // ID único do stream
status: StreamState; // Estado atual
startTime: Date | undefined; // Hora de início
uptime: number | undefined; // Tempo ativo em segundos
restartCount: number; // Número de reinicializações
lastError: string | undefined; // Último erro
ffmpegPid: number | undefined; // PID do processo FFmpeg
config: StreamConfig; // Configuração do stream
}StreamState Enum
enum StreamState {
STOPPED = 'stopped',
STARTING = 'starting',
RUNNING = 'running',
PAUSED = 'paused',
ERROR = 'error',
RESTARTING = 'restarting'
}FFmpegLog Interface
interface FFmpegLog {
streamId: string; // ID do stream
timestamp: Date; // Timestamp do log
level: 'info' | 'error' | 'warning'; // Nível do log
message: string; // Mensagem
source: 'stdout' | 'stderr'; // Origem do log
}StreamUpdate Interface
interface StreamUpdate {
rtmpUrl?: string; // Nova URL RTMP
inputPath?: string | string[]; // Novos arquivos (playlist)
videoConfig?: Partial<VideoConfig>; // Atualizações de vídeo
audioConfig?: Partial<AudioConfig>; // Atualizações de áudio
presetConfig?: Partial<PresetConfig>; // Atualizações de preset
loop?: boolean; // Alterar loop
}🎛️ Configurações Pré-definidas
YouTube Presets
const configs = FFmpegStreamManager.getDefaultConfigs();
// 1080p - Alta qualidade
configs.youTube1080p = {
videoConfig: {
width: 1920,
height: 1080,
bitrate: '4500k',
framerate: 30,
codec: 'libx264',
profile: 'high',
level: '4.1'
},
audioConfig: {
codec: 'aac',
bitrate: '128k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: 'zerolatency',
bufsize: '9000k',
maxrate: '4500k'
}
};
// 720p - Qualidade média
configs.youTube720p = {
videoConfig: {
width: 1280,
height: 720,
bitrate: '2500k',
framerate: 30,
codec: 'libx264',
profile: 'high',
level: '3.1'
},
audioConfig: {
codec: 'aac',
bitrate: '128k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: 'zerolatency',
bufsize: '5000k',
maxrate: '2500k'
}
};
// 480p - Baixa qualidade
configs.youTube480p = {
videoConfig: {
width: 854,
height: 480,
bitrate: '1000k',
framerate: 30,
codec: 'libx264',
profile: 'main',
level: '3.0'
},
audioConfig: {
codec: 'aac',
bitrate: '96k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: 'zerolatency',
bufsize: '2000k',
maxrate: '1000k'
}
};Configurações Personalizadas
// Para Twitch
const twitchConfig = {
videoConfig: {
width: 1920,
height: 1080,
bitrate: '6000k', // Twitch permite até 6Mbps
framerate: 60, // 60 FPS para jogos
codec: 'libx264',
profile: 'main',
level: '4.1'
},
audioConfig: {
codec: 'aac',
bitrate: '160k', // Áudio de alta qualidade
sampleRate: 48000, // 48kHz recomendado
channels: 2
},
presetConfig: {
preset: 'veryfast', // Preset mais rápido para menos latência
tune: 'zerolatency',
bufsize: '12000k',
maxrate: '6000k'
}
};
// Para Facebook Live
const facebookConfig = {
videoConfig: {
width: 1280,
height: 720,
bitrate: '4000k',
framerate: 30,
codec: 'libx264',
profile: 'baseline', // Facebook prefere baseline
level: '3.1'
},
audioConfig: {
codec: 'aac',
bitrate: '128k',
sampleRate: 44100,
channels: 2
},
presetConfig: {
preset: 'fast',
tune: undefined, // Sem tune específico
bufsize: '8000k',
maxrate: '4000k'
}
};📋 Requisitos do Sistema
Requisitos Mínimos
- Node.js: 16.0.0 ou superior
- FFmpeg: 4.0 ou superior
- RAM: 512MB disponível
- CPU: Depende da resolução e número de streams
- Largura de banda: Conforme bitrate configurado
Requisitos Recomendados
- Node.js: 18.0.0 ou superior
- FFmpeg: 5.0 ou superior (melhor performance)
- RAM: 2GB+ para múltiplos streams
- CPU: Processador multi-core
- SSD: Para melhor I/O de arquivos
Performance Guidelines
| Resolução | Streams Simultâneos | CPU Recomendado | RAM Recomendada | |-----------|---------------------|-----------------|-----------------| | 480p | 1-3 | 2 cores | 1GB | | 720p | 1-2 | 4 cores | 2GB | | 1080p | 1 | 6+ cores | 4GB |
Estimativa de Uso de CPU
// Exemplo de monitoramento de recursos
async function monitorarRecursos() {
const manager = new FFmpegStreamManager();
manager.on('stream-started', (streamId) => {
const status = manager.getStreamStatus(streamId);
console.log(`Stream ${streamId} - PID: ${status.ffmpegPid}`);
// Monitorar CPU do processo (usando bibliotecas como 'pidusage')
// pidusage(status.ffmpegPid, (err, stats) => {
// console.log(`CPU: ${stats.cpu}%, Memory: ${stats.memory} bytes`);
// });
});
}❓ FAQ
Q: Como funciona o ID dos streams?
A: O streamID é gerado automaticamente usando UUID v4, garantindo unicidade global. Formato: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.
Q: Como usar múltiplos arquivos em loop?
A: Use um array de strings no inputPath:
{
inputPath: [
'/videos/parte1.mp4',
'/videos/parte2.mp4',
'/videos/parte3.mp4'
],
loop: true // Repete a sequência infinitamente
}Q: Os arquivos são reproduzidos em ordem?
A: Sim, os arquivos são reproduzidos na ordem exata do array. Para ordem aleatória, embaralhe o array antes:
const files = ['/video1.mp4', '/video2.mp4', '/video3.mp4'];
const shuffled = files.sort(() => Math.random() - 0.5);Q: Posso misturar diferentes formatos?
A: Sim, mas todos devem ser do mesmo tipo (vídeo ou áudio). FFmpeg normalizará automaticamente:
inputPath: [
'/videos/arquivo.mp4',
'/videos/outro.avi',
'/videos/terceiro.mov'
]Q: Posso usar arquivos em rede (URLs HTTP)?
A: Sim, o FFmpeg suporta URLs HTTP/HTTPS como entrada:
{
inputPath: 'https://exemplo.com/video.mp4',
// ...
}Q: Como otimizar para low latency?
A: Use estas configurações:
{
presetConfig: {
preset: 'ultrafast',
tune: 'zerolatency',
bufsize: '1000k' // Buffer menor
}
}Q: Posso fazer stream sem arquivo (webcam/tela)?
A: Esta biblioteca é focada em arquivos. Para webcam/tela, use geradores FFmpeg:
{
inputPath: ':0.0+10,10', // Tela no Windows
// ou
inputPath: '/dev/video0' // Webcam no Linux
}Q: Como tratar streams que param inesperadamente?
A: Use auto-restart e monitore eventos:
const manager = new FFmpegStreamManager({
autoRestart: true,
restartDelay: 3000
});
manager.on('stream-error', async (streamId, error) => {
console.error(`Stream ${streamId} erro: ${error.message}`);
// Lógica personalizada de restart
if (error.message.includes('Connection refused')) {
// Aguardar mais tempo para reconnect
await new Promise(resolve => setTimeout(resolve, 10000));
await manager.restartStream(streamId);
}
});Q: Qual codec usar para melhor qualidade?
A: Depende do caso:
- H.264 (libx264): Mais compatível, boa qualidade
- H.265 (libx265): Melhor compressão, requer mais CPU
- Para YouTube: H.264 é recomendado
- Para baixa latência: H.264 com tune zerolatency
Q: Como verificar se FFmpeg está instalado?
A:
ffmpeg -versionSe não retornar versão, instale conforme seu OS.
Q: Posso usar com Docker?
A: Sim, use uma imagem com Node.js + FFmpeg:
FROM node:18
RUN apt-get update && apt-get install -y ffmpeg
COPY . .
RUN npm install
CMD ["npm", "start"]Q: Como fazer debug de problemas?
A:
- Ative logs debug:
logLevel: 'debug' - Monitore eventos de erro
- Teste comando FFmpeg manualmente
- Verifique permissões de arquivo
🤝 Contribuição
Contribuições são muito bem-vindas!
Como Contribuir
- Fork o repositório
- Crie uma branch para sua feature (
git checkout -b feature/AmazingFeature) - Commit suas mudanças (
git commit -m 'Add some AmazingFeature') - Push para a branch (
git push origin feature/AmazingFeature) - Abra um Pull Request
Diretrizes
- ✅ Mantenha 100% cobertura de tipos TypeScript
- ✅ Adicione testes para novas funcionalidades
- ✅ Siga o padrão de código existente
- ✅ Documente mudanças no README
- ✅ Teste com múltiplas versões do Node.js
Desenvolvimento Local
# Clone o repositório
git clone https://github.com/seu-usuario/ffmpeg-stream-manager.git
cd ffmpeg-stream-manager
# Instale dependências
npm install
# Desenvolvimento com watch
npm run dev
# Executar testes
npm test
# Build
npm run build
# Lint
npm run lint📄 Licença
Este projeto está licenciado sob a MIT License - veja o arquivo LICENSE para detalhes.
🔗 Links Úteis
- NPM: https://www.npmjs.com/package/ffmpeg-stream-manager
- GitHub: https://github.com/seu-usuario/ffmpeg-stream-manager
- Issues: https://github.com/seu-usuario/ffmpeg-stream-manager/issues
- FFmpeg Docs: https://ffmpeg.org/documentation.html
- YouTube Live: https://support.google.com/youtube/answer/2907883
📊 Estatísticas
Desenvolvido com ❤️ por [Seu Nome]
Se esta biblioteca foi útil para você, considere dar uma ⭐ no GitHub!
