UNPKG

openradio

Version:

A simple live streaming library written in JavaScript with ffmpeg.

285 lines (265 loc) 6.64 kB
// The Openradio Core const { spawn } = require("child_process"); const { PassThrough } = require("stream"); const events = require("events"); function convert(opt = {}, url) { return spawn("ffmpeg", [ "-re", "-analyzeduration", "0", "-loglevel", "0", "-i", url || "-", "-preset", "veryfast", "-f", opt.format || "mp3", "-ar", opt.rate || "44100", "-ac", opt.channels || "2", "-ab", `${opt.bitrate || "192"}k`, "-c:a", opt.acodec || "mp3", "-map", "0:a", "-map_metadata", "-1", "pipe:1", ]); } function cvideo(opt = {}, url, rtmp_url) { if (rtmp_url) { opt.format = "flv"; opt.vcodec = "libx264"; } return spawn("ffmpeg", [ "-re", "-analyzeduration", "0", "-loglevel", "0", "-i", url || "-", "-vf", "scale=" + (opt.scale || "-1:720"), "-preset", "veryfast", "-f", opt.format || "mpegts", "-c:v", opt.vcodec || "mpeg2video", "-ar", opt.arate || "44100", "-ac", opt.achannels || "2", "-ab", `${opt.abitrate || "128"}k`, "-c:a", opt.acodec || "aac", rtmp_url ? rtmp_url : "pipe:1", ]); } function pcmconv(opt = {}, pcmopt, url) { return spawn("ffmpeg", [ "-re", "-analyzeduration", "0", "-loglevel", "0", "-f", "s16le", "-preset", "veryfast", "-ac", pcmopt.channels || 2, "-ar", pcmopt.rate || "44100", "-i", url || "-", "-f", opt.format || "mp3", "-ar", opt.rate || "44100", "-ac", opt.channels || "2", "-ab", `${opt.bitrate || "192"}k`, "-c:a", opt.acodec || "mp3", "-map", "0:a", "-map_metadata", "-1", "pipe:1", ]); } function OpenRadio_Core(opt) { let Core = new PassThrough(); let converted = null; Core.header = null; Core.playing = false; Core.finish = false; Core.stream = null; // Player Core.play = function ReadStream(readable) { let newStream = 1; if (Core.stream && !Core.stream.killed) { Core.stream.stdout.removeAllListeners("end"); Core.stream.kill(); if (Core.stream.__readableStream) Core.stream.__readableStream.destroy(); Core.__res(); } return new Promise((res, rej) => { Core.stream = convert( opt, typeof readable === "string" ? readable : null ); Core.stream.stdout .on("data", (chunk) => { if (Core.header && newStream) return (newStream = 0); if (!Core.header && newStream) { Core.header = chunk; newStream = 0; } Core.write(chunk); }) .on("end", (e) => { Core.emit("finish", e); Core.finish = true; Core.playing = false; return res(e); }); Core.stream.on("error", (err) => { if (!Core.emit("error", err)) return rej(err); }); if (typeof readable !== "string" && typeof readable.pipe === "function") { Core.stream.__readableStream = readable; readable.on("error", (err) => Core.emit("error", err)); readable.pipe(Core.stream.stdin); } Core.playing = true; Core.finish = false; Core.__res = res; }); }; // PCM Player Core.playPCM = function PCMPlayer( readable, options = { rate: 44100, channels: 2 } ) { let newStream = 1; if (Core.stream && !Core.stream.killed) { Core.stream.stdout.removeAllListeners("end"); Core.stream.kill(); if (Core.stream.__readableStream) Core.stream.__readableStream.destroy(); Core.__res(); } return new Promise((res, rej) => { Core.stream = pcmconv( opt, options, typeof readable === "string" ? readable : null ); Core.stream.stdout .on("data", (chunk) => { if (Core.header && newStream) return (newStream = 0); if (!Core.header && newStream) { Core.header = chunk; newStream = 0; } Core.write(chunk); }) .on("end", (e) => { Core.emit("finish", e); Core.finish = true; Core.playing = false; return res(e); }); Core.stream.on("error", (err) => { if (!Core.emit("error", err)) return rej(err); }); if (typeof readable !== "string" && typeof readable.pipe === "function") { Core.stream.__readableStream = readable; readable.on("error", (err) => Core.emit("error", err)); readable.pipe(Core.stream.stdin); } Core.playing = true; Core.finish = true; Core.__res = res; }); }; return Core; } function OpenRadio_Video(opt, rtmp_url) { let Core = new PassThrough(); let converted = null; Core.header = null; Core.playing = false; Core.finish = false; Core.stream = null; // Player Core.play = function ReadStream(readable) { let newStream = 1; if (Core.stream && !Core.stream.killed) { Core.stream.stdout.removeAllListeners("end"); Core.stream.kill(); if (Core.stream.__readableStream) Core.stream.__readableStream.destroy(); Core.__res(); } return new Promise((res, rej) => { Core.stream = cvideo( opt, typeof readable === "string" ? readable : null, rtmp_url ); Core.stream.stdout .on("data", (chunk) => { if (Core.header && newStream) return (newStream = 0); if (!Core.header && newStream) { Core.header = chunk; newStream = 0; } Core.write(chunk); }) .on("end", (e) => { Core.emit("finish", e); Core.finish = true; Core.playing = false; return res(e); }); Core.stream.on("error", (err) => { if (!Core.emit("error", err)) return rej(err); }); if (typeof readable !== "string" && typeof readable.pipe === "function") { Core.stream.__readableStream = readable; readable.on("error", (err) => Core.emit("error", err)); readable.pipe(Core.stream.stdin); } Core.playing = true; Core.finish = false; Core.__res = res; }); }; return Core; } function repeater(radio) { // Clients let cs = new Set(); radio.on("data", (d) => cs.forEach((c) => c.write(d, (e) => { if (e) cs.delete(c); }) ) ); return (c) => { cs.add(c); return (_) => cs.delete(c); }; } module.exports = OpenRadio_Core; module.exports.video = OpenRadio_Video; module.exports.repeater = repeater;