使用 Nodejs + FFmpeg 实现的直播推流第二弹

▲ 视频作者 LiarOnce (http://www.bilibili.com/video/av9852916/

人生不止眼前的苟且,还有装逼和浪。——香菇夫斯基

故事起源于《作为程序猿(装逼汪),要不要试试用代码推流到B站直播?》,这里只是代码的升级优化,所以没什么好说的。

优化内容

  • 将函数递归调用实现的“循环推流”改成 pm2 实现的“进程守护”,从而实现进程结束自动重启、意外终结自动重启、后台管理、日志记录等功能;
  • 改写参数实现添加水印
  • 顺序播放改为随机播放

新的问题

  • B站直播修改了网页端播放器,导致断流重推后直播卡住,需要刷新才能续看。(目前与 一心月 交换了新的意见,待测试。

本次需要准备的环境和三方模块(纯净环境下

  • node.js 环境(某菇在B站发的唯一一个视频中讲到过)
  • pm2(Node模块:本次主角,用于守护进程)
  • fluent-ffmpeg(Node模块:封装了ffmpeg的api)
  • silly-datetime(Node模块:格式化时间,用于日志记录)
  • ffmpeg(用于支持推流)
  • 可以正常联网的计算机(PC、MAC、Linux、Raspberry Pi、……)

代码

var fs = require('fs');  //文件操作模块
var sd = require('silly-datetime');  //时间格式化模块
var D = './video2/';  //存放视频文件的目录
var L = './pic/tips2.png';  //水印
var R = 'rtmp://txy.live-send.acg.tv/live-txy/';  //推流地址
var C = '?streamname=live_4669771_4358269&key=a387ef5965ce24df5512345e727e89da';  //推流参数
var O = '';  //测试输出,一般填写“文件名+.格式”的形式,留空则不启用

function showTime() {
  return sd.format(new Date(), 'YYYY-MM-DD HH:mm');
}

function liveON() {
 var V = fs.readdirSync(D);
  var N = V.length;
 var ID = Math.floor(Math.random() * N);
 var ffmpeg = require('fluent-ffmpeg');
  var inputPath = D + V[ID];
  var outputPath = O == '' ? (R + C) : O;
 console.log('[' + showTime() + '] Find ' + N + ' Video Files !');
 ffmpeg(inputPath)
   .inputOptions('-re')
    .inputOptions('-ac 2')
    .addInput(L)
    .complexFilter([{
     filter: 'scale',
      options: [1280, -1],
      inputs: '[0:v]',
      outputs: 'video'
    }, {
      filter: 'scale',
      options: [220, -1],
     inputs: '[1:v]',
      outputs: 'logo'
   }, {
      filter: 'overlay',
      options: {
        x: 'main_w-overlay_w-15',
       y: 15
     },
      inputs: ['video', 'logo']
   }])
   .on('start', function(commandLine) {
      //console.log('Spawned Ffmpeg with command: ' + commandLine);
     console.log('[' + showTime() + '] Vedio "' + V[ID] + '" is Pushing !');
     console.log('[' + showTime() + '] Spawned Ffmpeg with command !');
    })
    .on('error', function(err, stdout, stderr) {
      console.log('error: ' + err.message);
     console.log('stdout: ' + stdout);
     console.log('stderr: ' + stderr);
   })
    .on('end', function() {
     console.log('[' + showTime() + '] Vedio "' + V[ID] + '" Pushing is Finished !');
    })
    .addOptions([
     '-vcodec libx264',
      '-preset veryfast',
     '-crf 22',
      '-maxrate 1000k',//1000k
      '-bufsize 3000k',
     '-acodec libmp3lame',
     '-ac 2',
      '-ar 44100',
      '-b:a 96k'
    ])
    .format('flv')
    .output(outputPath, {
     end: true
   })
    .run();
}

liveON();

截至目前发文,香菇已经把该代码放在腾讯云运行超过48小时,期间直播未间断:http://live.bilibili.com/64285

(待续)

标签: none

已有 36 条评论

  1. 唐

    最近在调试推流的功能,但是按照上面的代码总是推流不成功,请问可以看一下这个示例的源码吗,希望可以找到出现问题的地方

    1. 代码的话 就是上面的了 后续更新在这里 https://blog.siitake.cn/nodejs-bilibili-live-3.html
      如果报错 可能是由于ffmpeg或者nodejs的版本不一致造成的 可以先试试对照官方文档检查和直接运行程序生成的 ffmpeg命令行 看看有没有错误

  2. Pino Pino

    最近要写自动化测试,刚好要用mocha 和 ffmpeg来测试直播推流,帮大忙了谢谢。

    1. 哇~很荣幸我的文章对您有参考价值~

      1. Pino Pino

        话说博主有没有遇到没有推流之后没有声音的情况? 按照你的代码尝试了,推流live是没有声音的,看你上一篇博客介绍加了水印丢失了音轨,我把加水印的代码去掉了也没有声音。

        1. 我的代码第二版之后声音都没问题,你可以尝试console出ffmpeg命令后直接在命令行(cmd/terminal)中运行测试有木有声音,同时多换几个源视频文件测试,看看是否是因为原视频的编码问题导致推流无声。

          1. Pino Pino

            我把-acodec 设为aac 解决了。
            顺便一提,
            .inputOptions('-ac 2') 这一行和 后面addOptions的'-ac 2' 是不是重复了,还是故意为之?
            因为我瞄了一眼fluent-ffmpeg的文档,这个inputOptions和addOptions应该是同一个方法不同的写法而已。

            1. 哈哈哈哈哈哈问题解决了就好~

            2. Pino Pino

              oh sorry, 忽略后面那个愚蠢的问题,我看错了 orz..

  3. Kay Kay

    哇~dalao 友链否?

    1. 已添加,请回链。

      1. Kay Kay

        交易成功~♂ 居然回复错了QAQ

  4. 代码高亮全红看着好难受。。 QAQ

    1. 没有用着色高亮插件~
      因为想着如果感兴趣的话基本就会拷走在其他地方看的233

      1. 好像道理的样子喔 QWQ

  5. 话说一直没搞明白你首页的信息流是怎么排列的

    1. 就是普通的浮动布局啊并没有用流式布局哈哈哈哈

评论已关闭