Android'e Doğru
Android'e Doğru

Reputation: 21

Node Media Server [rtmp play] Close stream

This is my app.js file:

const NodeMediaServer = require('./');

const config = {
  rtmp: {
    port: 1935,
    chunk_size: 60000,
    gop_cache: true,
    ping: 60,
    ping_timeout: 30,
    /*
    ssl: {
      port: 443,
      key: './privatekey.pem',
      cert: './certificate.pem',
    }
    */
  },
  relay: {
  ffmpeg: '/usr/bin/ffmpeg',
  tasks: [
    {
      app: 'live',
      mode: 'static',
      edge: 'rtsp://11.11.11.2:8554/live',
      name: 'cam1',
      rtsp_transport : 'tcp' 
    },
 {
      app: 'live',
      mode: 'static',
      edge: 'rtsp://11.11.11.3:8554/live',
      name: 'cam2',
      rtsp_transport : 'tcp'
    },
 {
      app: 'live',
      mode: 'static',
      edge: 'rtsp://11.11.11.4:8554/live',
      name: 'cam3',
      rtsp_transport : 'tcp'
    },
 {
      app: 'live',
      mode: 'static',
      edge: 'rtsp://11.11.11.5:8554/live',
      name: 'cam4',
      rtsp_transport : 'tcp'
    }



  ]
},
  http: {
    port: 8000,
    mediaroot: '/home/pi/Node-Media-Server/media',
    webroot: './www',
    allow_origin: '*',
    api: true
  },
  auth: {
    api: true,
    api_user: 'admin',
    api_pass: 'admin',
    play: false,
    publish: false,
    secret: 'nodemedia2017privatekey'
  },
  trans: {
    ffmpeg: '/usr/bin/ffmpeg',
    tasks: [
      {
        app: 'live',
        mp4: true,
        //mp4Flags: '[movflags=frag_keyframe+empty_moov]',
      }
    ]
  }
};


let nms = new NodeMediaServer(config)
nms.run();

nms.on('preConnect', (id, args) => {
  console.log('[NodeEvent on preConnect]', `id=${id} args=${JSON.stringify(args)}`);
  // let session = nms.getSession(id);
  // session.reject();
});

nms.on('postConnect', (id, args) => {
  console.log('[NodeEvent on postConnect]', `id=${id} args=${JSON.stringify(args)}`);
});

nms.on('doneConnect', (id, args) => {
  console.log('[NodeEvent on doneConnect]', `id=${id} args=${JSON.stringify(args)}`);
});

nms.on('prePublish', (id, StreamPath, args) => {
  console.log('[NodeEvent on prePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
  // let session = nms.getSession(id);
  // session.reject();
});

nms.on('postPublish', (id, StreamPath, args) => {
  console.log('[NodeEvent on postPublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
});

nms.on('donePublish', (id, StreamPath, args) => {
  console.log('[NodeEvent on donePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
});

nms.on('prePlay', (id, StreamPath, args) => {
  console.log('[NodeEvent on prePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
  // let session = nms.getSession(id);
  // session.reject();
});

nms.on('postPlay', (id, StreamPath, args) => {
  console.log('[NodeEvent on postPlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
});

nms.on('donePlay', (id, StreamPath, args) => {
  console.log('[NodeEvent on donePlay]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
});

This is my node_trans_session.js file:

//
//  Created by Mingliang Chen on 18/3/9.
//  illuspas[a]gmail.com
//  Copyright (c) 2018 Nodemedia. All rights reserved.
//
const Logger = require('./node_core_logger');

const EventEmitter = require('events');
const { spawn } = require('child_process');
const dateFormat = require('dateformat');
const mkdirp = require('mkdirp');
const fs = require('fs');

class NodeTransSession extends EventEmitter {
    constructor(conf) {
        super();
        this.conf = conf;
        this.intervalState = 0;
        this.interval = null
    }
    run() {
        let fastStart = 'faststart';
        let vc = this.conf.vc || 'copy';
        let ac = this.conf.ac || 'copy';
        let inPath = 'rtmp://127.0.0.1:' + this.conf.rtmpPort + this.conf.streamPath;
        let ouPath = `/home/pi/Node-Media-Server/media/${this.conf.streamApp}/${this.conf.streamName}`;
        let ouPath2 = `/home/pi/Node-Media-Server/media/liveHLS/${this.conf.streamName}`;
        let mapStr = '';
        mkdirp.sync(ouPath);
        if (this.conf.rtmp && this.conf.rtmpApp) {
            if (this.conf.rtmpApp === this.conf.streamApp) {
                Logger.error('[Transmuxing RTMP] Cannot output to the same app.');
            } else {
                let rtmpOutput = `rtmp://127.0.0.1:${this.conf.rtmpPort}/${this.conf.rtmpApp}/${this.conf.streamName}`;
                mapStr += `[f=flv]${rtmpOutput}|`;
                Logger.log('[Transmuxing RTMP] ' + this.conf.streamPath + ' to ' + rtmpOutput);
            }
        }
        if (this.conf.mp4) {
            this.conf.mp4Flags = this.conf.mp4Flags ? this.conf.mp4Flags : '';
            let mp4FileDate = dateFormat('yyyy-mm-dd');
            let mp4FileName = dateFormat('HH-MM-ss') + '.mp4';
            let mapMp4 = `${this.conf.mp4Flags}${ouPath}/${mp4FileDate}/${mp4FileName}|`;
            mapStr += mapMp4;
            let ouPathWithDate = `/home/pi/Node-Media-Server/media/${this.conf.streamApp}/${this.conf.streamName}/${mp4FileDate}`
            mkdirp.sync(ouPathWithDate);
            Logger.log(ouPathWithDate);
            Logger.log('[Transmuxing MP4] ' + this.conf.streamPath + ' to ' + ouPath + '/' + mp4FileDate + '/' + mp4FileName);
        }
        if (this.conf.hls) {
            this.conf.hlsFlags = this.conf.hlsFlags ? this.conf.hlsFlags : '';
            let hlsFileName = 'index.m3u8';
            let mapHls = `${this.conf.hlsFlags}${ouPath2}/${hlsFileName}|`;
            mapStr += mapHls;
            Logger.log('[Transmuxing HLS] ' + this.conf.streamPath + ' to ' + ouPath2 + '/' + hlsFileName);
        }
        if (this.conf.dash) {
            this.conf.dashFlags = this.conf.dashFlags ? this.conf.dashFlags : '';
            let dashFileName = 'index.mpd';
            let mapDash = `${this.conf.dashFlags}${ouPath}/${dashFileName}`;
            mapStr += mapDash;
            Logger.log('[Transmuxing DASH] ' + this.conf.streamPath + ' to ' + ouPath + '/' + dashFileName);
        }
        let argv = ['-y', '-i', inPath];
        Array.prototype.push.apply(argv, ['-c:v', vc]);
        Array.prototype.push.apply(argv, this.conf.vcParam);
        Array.prototype.push.apply(argv, ['-movflags', fastStart]);
        Array.prototype.push.apply(argv, ['-c:a', ac]);
        Array.prototype.push.apply(argv, this.conf.acParam);
        Array.prototype.push.apply(argv, ['-f', 'tee', '-map', '0:a?', '-map', '0:v?', mapStr]);
        console.log('mapStr', mapStr)
        argv = argv.filter((n) => { return n }); //去空


        this.ffmpeg_exec = spawn(this.conf.ffmpeg, argv);
        this.ffmpeg_exec.on('error', (e) => {
            Logger.ffdebug(e);
        });

        this.ffmpeg_exec.stdout.on('data', (data) => {
            Logger.ffdebug(`FF输出:${data}`);
        });

        this.ffmpeg_exec.stderr.on('data', (data) => {
            Logger.ffdebug(`FF输出:${data}`);
        });

        this.ffmpeg_exec.on('close', (code) => {
            Logger.log('[Transmuxing end] UST ' + this.conf.streamPath);
            //    this.emit('end');
            fs.readdir(ouPath, function (err, files) {
                if (!err) {
                    files.forEach((filename) => {
                        if (filename.endsWith('.ts')
                            || filename.endsWith('.m3u8')
                            || filename.endsWith('.mpd')
                            || filename.endsWith('.m4s')
                            || filename.endsWith('.tmp')) {
                            fs.unlinkSync(ouPath + '/' + filename);
                        }
                    })
                }
            });
        });

        this.interval = setInterval(async () => {
            if (new Date().getMinutes().toString() === "0" && (new Date().getHours().toString() !== "0" && new Date().getHours().toString() !== "12")) {
                if (this.intervalState === 0) {
                    this.intervalState = 1
                    console.log('BBBBBBBBBBBBBBBBBBBBBB', new Date().getMinutes().toString(), new Date().getMinutes().toString() === "00")
                    this.ffmpeg_exec.kill()
                    let mp4FileDate = dateFormat('yyyy-mm-dd');
                    let mp4FileName = dateFormat('HH-MM-ss') + '.mp4';
                    try {
                        await fs.accessSync(ouPath + '/' + mp4FileDate);
                    } catch (err) {
                        if (err && err.code === 'ENOENT') {
                            mkdirp.sync(ouPath + '/' + mp4FileDate);
                        }
                    }
                    let mapMp4 = `${this.conf.mp4Flags}${ouPath}/${mp4FileDate}/${mp4FileName}|`;
                    argv.pop()
                    argv.push(mapMp4)

                    this.ffmpeg_exec = spawn(this.conf.ffmpeg, argv);
                    this.ffmpeg_exec.on('error', (e) => {
                        Logger.ffdebug("BBBBBBBBBBBBBBBBBBBBBB" + e);
                    });

                    this.ffmpeg_exec.stdout.on('data', (data) => {
                        Logger.ffdebug(`FF输出:${data}`);
                    });

                    this.ffmpeg_exec.stderr.on('data', (data) => {
                        Logger.ffdebug(`FF输出:${data}`);
                    });

                    this.ffmpeg_exec.on('close', (code) => {
                        Logger.log('[Transmuxing end] ALT ' + this.conf.streamPath);
                        fs.readdir(ouPath, function (err, files) {
                            if (!err) {
                                files.forEach((filename) => {
                                    if (filename.endsWith('.ts')
                                        || filename.endsWith('.m3u8')
                                        || filename.endsWith('.mpd')
                                        || filename.endsWith('.m4s')
                                        || filename.endsWith('.tmp')) {
                                        fs.unlinkSync(ouPath + '/' + filename);
                                    }
                                })
                            }
                        });
                    });

                }
            } else {
                this.intervalState = 0;
            }
        }, 20000);
    }
    end() {
        this.ffmpeg_exec.kill();
        this.emit('end');
        clearInterval(this.interval)
    }
}
module.exports = NodeTransSession;

I am trying build simple home security stream and record server via Node-Media-Server.

I am using RPi A+ for server, Rpi Zero W for camera. I have 4 cameras(RPi Zero W).

My build do that:

  1. Live Stream
  2. Record Stream

Sometimes I can't see my some cameras on Node-Media-Server Stream Panel. And I look my /var/log/syslog file. I saw that:

(root) CMD (   cd / && run-parts --report /etc/cron.hourly)
Oct 21 14:22:37 raspberrypi bash[6638]: [NodeEvent on donePlay] id=FCNE53CU StreamPath=/live/cam4 args={}
Oct 21 14:22:37 raspberrypi bash[6638]: 21/10/2021 14:22:37 6642 [INFO] [rtmp play] Close stream. id=FCNE53CU streamPath=/live/cam4 streamId=1
Oct 21 14:22:37 raspberrypi bash[6638]: 21/10/2021 14:22:37 6642 [INFO] [rtmp disconnect] id=FCNE53CU
Oct 21 14:22:37 raspberrypi bash[6638]: [NodeEvent on doneConnect] id=FCNE53CU args={"app":"live","flashVer":"LNX 9,0,124,2","tcUrl":"rtmp://127.0.0.1:1935/live","fpad":false,"capabilities":15,"audioCodecs":4071,"videoCodecs":252,"videoFunction":1}
Oct 21 14:22:41 raspberrypi bash[6638]: 21/10/2021 14:22:41 6642 [INFO] [Transmuxing end] /live/cam4

And sometimes FFMPEG script don't closed. And I see my stream on panel but it don't play.Just 2 or 3 second playing.

Upvotes: 1

Views: 1988

Answers (0)

Related Questions