我正在使用 Node.js 创建一个流媒体系统,它使用 ffmpeg 将视频和文本流发送到本地 RTMP 服务器,然后组合这些流并将它们发送到 Twitch。
我正在使用画布创建具有透明背景的文本图像,每次播放列表中的新视频开始时我都需要更改该文本。
Currently in stream 我只看到我视频的视频流,看不到文字。但是,如果我通过 VLC 去查看更多分开的内容,我会看到它们
但是,我遇到了文本流没有出现在 Twitch 上的最终视频流中的问题。此外,我收到以下错误消息:
Combine stderr: [NULL @ 0x1407069f0] Unable to find a suitable output format for 'rtmp://live.twitch.tv/app/streamKey'
rtmp://live.twitch.tv/app/streamKey: Invalid argument
这是我当前的 Node.js 代码:
const createTextImage = (runner) => {
return new Promise((resolve, reject) => {
const canvas = createCanvas(1920, 1080);
const context = canvas.getContext('2d');
// Fill the background with transparency
context.fillStyle = 'rgba(0,0,0,0)';
context.fillRect(0, 0, canvas.width, canvas.height);
// Set the text options
context.fillStyle = '#ffffff';
context.font = '24px Arial';
context.textAlign = 'start';
context.textBaseline = 'middle';
// Draw the text
context.fillText(`Speedrun by ${runner}`, canvas.width / 2, canvas.height / 2);
// Define the images directory
const imagesDir = path.join(__dirname, 'images', 'runners');
// Ensure the images directory exists
fs.mkdirSync(imagesDir, { recursive: true });
// Define the file path
const filePath = path.join(imagesDir, runner + '.png');
// Create the write stream
const out = fs.createWriteStream(filePath);
// Create the PNG stream
const stream = canvas.createPNGStream();
// Pipe the PNG stream to the write stream
stream.pipe(out);
out.on('finish', () => {
console.log('The PNG file was created.');
resolve();
});
out.on('error', reject);
});
}
const streamVideo = (video) => {
ffmpegLibrary.ffprobe(video.video, function (err, metadata) {
if (err) {
console.error(err);
return;
}
currentVideoDuration = metadata.format.duration;
// Annulez le délai précédent avant d'en créer un nouveau
if (nextVideoTimeoutId) {
clearTimeout(nextVideoTimeoutId);
}
// Déplacez votre appel setTimeout ici
nextVideoTimeoutId = setTimeout(() => {
console.log('Fin de la vidéo, passage à la suivante...');
nextVideo();
}, currentVideoDuration * 1000 + 10000);
})
ffmpegVideo = childProcess.spawn('ffmpeg', [
'-nostdin', '-re', '-f', 'concat', '-safe', '0', '-i', 'playlist.txt',
'-vcodec', 'libx264',
'-s', '1920x1080',
'-r', '30',
'-b:v', '5000k',
'-acodec', 'aac',
'-preset', 'veryfast',
'-f', 'flv',
`rtmp://localhost:1935/live/video` // envoie le flux vidéo au serveur rtmp local
]);
createTextImage(video.runner).then(() => {
ffmpegText = childProcess.spawn('ffmpeg', [
'-nostdin', '-re',
'-loop', '1', '-i', `images/runners/${video.runner}.png`, // Utilise l'image créée par Puppeteer
'-vcodec', 'libx264rgb', // Utilise le codec PNG pour conserver la transparence
'-s', '1920x1080',
'-r', '30',
'-b:v', '5000k',
'-acodec', 'aac',
'-preset', 'veryfast',
'-f', 'flv',
`rtmp://localhost:1935/live/text` // envoie le flux de texte au serveur rtmp local
]);
ffmpegText.stdout.on('data', (data) => {
console.log(`text stdout: ${data}`);
});
ffmpegText.stderr.on('data', (data) => {
console.error(`text stderr: ${data}`);
});
}).catch(error => {
console.error(`Erreur lors de la création de l'image de texte: ${error}`);
});
ffmpegCombine = childProcess.spawn('ffmpeg', [
'-i', 'rtmp://localhost:1935/live/video',
'-i', 'rtmp://localhost:1935/live/text',
'-filter_complex', '[0:v][1:v]overlay=main_w-overlay_w:0',
'-s', '1920x1080',
'-r', '30',
'-vcodec', 'libx264',
'-b:v', '5000k',
'-acodec', 'aac',
'-preset', 'veryfast',
'-f', 'flv',
`rtmp://live.twitch.tv/app/${twitchStreamKey}` // envoie le flux combiné à Twitch
]);
ffmpegVideo.stdout.on('data', (data) => {
console.log(`video stdout: ${data}`);
});
ffmpegVideo.stderr.on('data', (data) => {
console.error(`video stderr: ${data}`);
});
ffmpegCombine.stdout.on('data', (data) => {
console.log(`Combine stdout: ${data}`);
});
ffmpegCombine.stderr.on('data', (data) => {
console.error(`Combine stderr: ${data}`);
});
ffmpegCombine.on('close', (code) => {
console.log(`ffmpeg exited with code ${code}`);
if (currentIndex >= playlist.length) {
console.log('End of playlist');
currentIndex = 0;
}
});
}
本地我使用nginx和rtmp模块来管理多流并合并为一个发送到twitch
在 NGINX 中,它是我的 nginx.conf 模块:
rtmp {
server {
listen 1935; # le port pour le protocole RTMP
application live {
live on; # active le streaming en direct
record off; # désactive l'enregistrement du flux
# définit l'endroit où les flux doivent être envoyés
push rtmp://live.twitch.tv/app/liveKey;
}
application text {
live on; # active le streaming en direct
record off; # désactive l'enregistrement du flux
}
}
}
我已检查两个流的编解码器、分辨率和帧速率是否相同。我还使用 -filter_complex 命令将文本流覆盖在视频流之上,但我不确定它是否正常工作。
每个流必须有相同的参数吗?
我想知道是否有人知道可能导致此问题的原因以及解决方法。我应该为 Twitch 的输出流使用不同的格式吗?或者我应该考虑另一种方法来将动态文本流分层到视频流上吗?
此外,我想知道当视频发生变化时我是否正在正确处理更新文本流。目前,每次视频更改时,我都会使用 Canvas 创建一个新的文本图像,然后为文本流创建一个新的 ffmpeg 进程。这是正确的方法,还是有更好的方法来处理这个问题?
提前感谢您的任何帮助或建议。