直播流加水印
ffmpeg -i https://outbound-production.explore.org/stream-production-319/.m3u8 -vf "movie=black_water.png[watermark]; [in][watermark] overlay=main_w-overlay_w-20:main_h-overlay_h-10 [out]" -c:v libx264 -c:a copy -f flv "rtmp://livepush.gonature.cn/live/qwee?txSecret=39a51cfacc1d5d93216add5b5e0a017e&txTime=60DD7A3B"
定时任务拉流并保存MP4文件
const ffmpeg = require('fluent-ffmpeg')
const config = require('./live_catch_tasks.json')
const fs = require('fs')
ffmpeg.setFfmpegPath('/usr/local/ffmpeg/bin/ffmpeg') // /Users/bin/Downloads/ffmpeg 本机
const oneDay = 86400000
const tasks = {} // 正在执行的任务
const checkAndRun = async function(){
console.log('check and run task...', new Date())
// 遍历任务列表
for(let i in config.tasks){
const task = config.tasks[i]
const now = new Date()
// 取当前时间,格式如 00:00
const time = ('0'+now.getHours()).slice(-2) + ':' + ('0'+now.getMinutes()).slice(-2)
const exists = fs.existsSync(config.storeDir + task.saveAs)
let overwrite = false
if(exists){
const filestat = fs.statSync(config.storeDir + task.saveAs)
// console.log('now:',now.getDay(),time,filestat.mtimeMs)
if((now.getTime() - filestat.mtimeMs) > oneDay){
overwrite = true
}
}
// console.log(now.getDay() === task.weekDay,time >= task.startTime,(!exists || (exists && overwrite)))
if(!tasks[task.name] && now.getDay() === task.weekDay // 判断是否为当天任务
&& time >= task.startTime // 判断是否已到任务开始时间
&& (!exists || (exists && overwrite))){ // 判断文件是否已存在 或 上一次更新时间距现在是否大于24小时
((t)=> {
tasks[t.name] = {
id: t.id,
name: t.name,
saveAs: t.saveAs,
status: null
}
// 创建 ffmpeg 任务
tasks[t.name].process = ffmpeg(task.source)
// .videoBitrate(1000)
.addOptions(
t.ffmpegOptions
)
.on('error', function(err, stdout, stderr) {
console.error('record live error: ' + err.message)
tasks[t.name].status = 'Error'
})
.on("start", function (commandLine) {
console.log(commandLine)
tasks[t.name].status = 'Going'
})
.on("end", function () {
// console.log('push over.')
delete tasks[t.name]
})
.save(config.storeDir + t.saveAs)
})(task)
}
}
}
// 格式化输出到控制台
function log(){
const list = Object.values(tasks).map(i=>{
return { id: i.id, name: i.name, status: i.status || '', saveAs: i.saveAs}
})
list.sort((a,b)=> a.id - b.id )
console.table(list)
}
function run(){
checkAndRun() // 首次立即执行
setInterval(checkAndRun, 30000) // 每 30 秒检查一次
setInterval(log, 5000) // 日志每 5 秒输出一次
}
run()
/*
const cmd = ffmpeg('/Users/bin/Downloads/movies/心火/Firelight.1997.1080p.WEBRip.x264-RARBG.mp4')
// .duration(60)
// .loop(10)
// .seekInput(834.5) // set start time
.videoBitrate(1000)
// .noAudio()
.addOptions(
[
'-vcodec copy',
'-acodec copy',
'-bsf:a aac_adtstoasc'
]
)
// .output('/Users/bin/Downloads/outputfile.mp4')
// .output('rtmp://livepush.gonature.cn/live/live1?txSecret=d6f5ab25a7f845391b7b58dd274ef19e&txTime=858C3C88')
.on('error', function(err, stdout, stderr) {
console.log('Cannot process video: ' + err.message);
})
.on('end', function() {
console.log('Processing finished !');
})
.format("flv")
.output('rtmp://livepush.gonature.cn/live/live1?txSecret=d6f5ab25a7f845391b7b58dd274ef19e&txTime=858C3C88', {
end: true
})
.on("start", function (commandLine) {
console.log("pushing......") //开始推流
})
.on("error", function (err, stdout, stderr) {
console.log('push error:', err) //推流失败
})
.on("end", function () {
console.log('push over.') //推流结束
});
cmd.run();
//.save('/Users/bin/Downloads/outputfile.mp4');
*/
node方式拉流推流并加水印
var ffmpeg = require('fluent-ffmpeg');
const command = ffmpeg('https://outbound-production.explore.org/stream-production-319/.m3u8')
.videoFilters("movie=black_water.png[watermark]; [in][watermark] overlay=main_w-overlay_w-20:main_h-overlay_h-10 [out]")
.addOptions([
"-vcodec libx264",
"-preset ultrafast",
"-acodec aac"
])
.format("flv")
.output('rtmp://livepush.gonature.cn/live/qwee?txSecret=39a51cfacc1d5d93216add5b5e0a017e&txTime=60DD7A3B', {
end: true
})
.on("start", function (commandLine) {
console.log("commandLine: " + commandLine);
})
.on("error", function (err, stdout, stderr) {
console.log("commandLine: " + '失败');
})
.on("end", function () {
});
command.run()
使用process_argv传递参数,执行固定的ID之间的任务
启动方式:
node manage.js 1 3
任务id 1-3,包含1和3
const ffmpeg = require('fluent-ffmpeg')
const config = require('./live_push_tasks.json')
const fs = require('fs')
// ffmpeg.setFfmpegPath('/usr/local/ffmpeg/bin/ffmpeg') // /Users/bin/Downloads/ffmpeg 本机
const oneDay = 86400000
const tasks = {} // 正在执行的任务
let begin = 1 // 开始初始值
let end = 100 // 结束初始值
process.argv.forEach(function(val, index, array) {
console.log(index + ': ' + val);
if (index === 2) {
begin = val
}
if (index === 3) {
end = val
}
});
const checkAndRun = async function(){
console.log('check and run task...', new Date())
// 遍历任务列表
for(let i in config.tasks){
const task = config.tasks[i]
const now = new Date()
// console.log(now.getDay() === task.weekDay,time >= task.startTime,(!exists || (exists && overwrite)))
if(!tasks[task.name] && begin <=task.id && end>=task.id){ // 判断文件是否已存在 或 上一次更新时间距现在是否大于24小时
((t)=> {
tasks[t.name] = {
id: t.id,
name: t.name,
saveAs: t.saveAs,
status: null
}
// 创建 ffmpeg 任务
tasks[t.name].process = ffmpeg('https://outbound-production.explore.org/stream-production-'+task.source+'/.m3u8')
.videoFilters("movie=black_water.png[watermark]; [in][watermark] overlay=main_w-overlay_w-20:main_h-overlay_h-10 [out]")
.addOptions([
"-vcodec libx264",
"-preset ultrafast",
"-acodec aac"
])
.format("flv")
.output(task.output, {
end: true
})
.on("start", function (commandLine) {
console.log(commandLine)
tasks[t.name].status = 'Going'
})
.on("error", function (err, stdout, stderr) {
console.error('record live error: ' + err.message)
tasks[t.name].status = 'Error'
})
.on("end", function () {
delete tasks[t.name]
}).run();
})(task)
}
}
}
// 格式化输出到控制台
function log(){
const list = Object.values(tasks).map(i=>{
return { id: i.id, name: i.name, status: i.status || ''}
})
list.sort((a,b)=> a.id - b.id )
console.table(list)
}
function run(){
checkAndRun() // 首次立即执行
setInterval(checkAndRun, 30000) // 每 30 秒检查一次
setInterval(log, 5000) // 日志每 5 秒输出一次
}
run()
添加码率
const ffmpeg = require('fluent-ffmpeg')
const config = require('./live_push_tasks.json')
const fs = require('fs')
// ffmpeg.setFfmpegPath('/usr/local/ffmpeg/bin/ffmpeg') // /Users/bin/Downloads/ffmpeg 本机
const oneDay = 86400000
const tasks = {} // 正在执行的任务
let begin = 1 // 开始初始值
let end = 100 // 结束初始值
process.argv.forEach(function(val, index, array) {
console.log(index + ': ' + val);
if (index === 2) {
begin = val
}
if (index === 3) {
end = val
}
});
const checkAndRun = async function(){
console.log('check and run task...', new Date())
// 遍历任务列表
for(let i in config.tasks){
const task = config.tasks[i]
const now = new Date()
// console.log(now.getDay() === task.weekDay,time >= task.startTime,(!exists || (exists && overwrite)))
if(!tasks[task.name] && begin <=task.id && end>=task.id){ // 判断文件是否已存在 或 上一次更新时间距现在是否大于24小时
((t)=> {
tasks[t.name] = {
id: t.id,
name: t.name,
saveAs: t.saveAs,
status: null
}
// 创建 ffmpeg 任务
tasks[t.name].process = ffmpeg('https://outbound-production.explore.org/stream-production-'+task.source+'/.m3u8')
.videoFilters("movie=black_water.png[watermark]; [in][watermark] overlay=main_w-overlay_w-20:main_h-overlay_h-10 [out]")
.addOptions([
"-vcodec libx264",
"-preset ultrafast",
"-acodec aac",
"-video_size 1920x1080"
])
.videoBitrate('3072k')
.format("flv")
.output(task.output, {
end: true
})
.on("start", function (commandLine) {
console.log(commandLine)
tasks[t.name].status = 'Going'
})
.on("error", function (err, stdout, stderr) {
console.error('record live error: ' + err.message)
tasks[t.name].status = 'Error'
})
.on("end", function () {
delete tasks[t.name]
}).run();
})(task)
}
}
}
// 格式化输出到控制台
function log(){
const list = Object.values(tasks).map(i=>{
return { id: i.id, name: i.name, status: i.status || ''}
})
list.sort((a,b)=> a.id - b.id )
console.table(list)
}
function run(){
checkAndRun() // 首次立即执行
setInterval(checkAndRun, 30000) // 每 30 秒检查一次
setInterval(log, 5000) // 日志每 5 秒输出一次
}
run()