Skip to content

Commit

Permalink
Merge pull request #66 from Fede14it/custom-Headers
Browse files Browse the repository at this point in the history
  • Loading branch information
dank074 authored Feb 13, 2024
2 parents 8eb6206 + b9f6547 commit 3094aba
Showing 1 changed file with 70 additions and 69 deletions.
139 changes: 70 additions & 69 deletions src/media/streamLivestreamVideo.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ffmpeg from 'fluent-ffmpeg';
import { IvfTransformer }from "./IvfReader";
import { IvfTransformer } from "./IvfReader";
import prism from "prism-media";
import { AudioStream } from "./AudioStream";
import { MediaUdp } from '../client/voice/MediaUdp';
Expand All @@ -11,99 +11,100 @@ import { VideoStream } from './VideoStream';

export let command: ffmpeg.FfmpegCommand;

export function streamLivestreamVideo(input: string | Readable, mediaUdp: MediaUdp, includeAudio = true) {
export function streamLivestreamVideo(input: string | Readable, mediaUdp: MediaUdp, includeAudio = true, customHeaders?: map) {
return new Promise<string>((resolve, reject) => {
const videoStream: VideoStream = new VideoStream(mediaUdp, streamOpts.fps);

let videoOutput: Transform;

if(streamOpts.video_codec === 'H264') {
if (streamOpts.video_codec === 'H264') {
videoOutput = new H264NalSplitter();
} else {
videoOutput = new IvfTransformer();
}

const headers: map = {
let headers: map = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.3",
"Connection": "keep-alive"
}

headers = { ...headers, ...(customHeaders ?? {}) };

let isHttpUrl = false;
let isHls = false;

if(typeof input === "string")
{
if (typeof input === "string") {
isHttpUrl = input.startsWith('http') || input.startsWith('https');
isHls = input.includes('m3u');
}
}

try {
command = ffmpeg(input)
.addOption('-loglevel', '0')
.addOption('-fflags', 'nobuffer')
.addOption('-analyzeduration', '0')
.on('end', () => {
command = undefined;
resolve("video ended")
})
.on("error", (err, stdout, stderr) => {
command = undefined;
reject('cannot play video ' + err.message)
})
.on('stderr', console.error);

if(streamOpts.video_codec === 'VP8') {
.addOption('-loglevel', '0')
.addOption('-fflags', 'nobuffer')
.addOption('-analyzeduration', '0')
.on('end', () => {
command = undefined;
resolve("video ended")
})
.on("error", (err, stdout, stderr) => {
command = undefined;
reject('cannot play video ' + err.message)
})
.on('stderr', console.error);

if (streamOpts.video_codec === 'VP8') {
command.output(StreamOutput(videoOutput).url, { end: false })
.noAudio()
.size(`${streamOpts.width}x${streamOpts.height}`)
.fpsOutput(streamOpts.fps)
.videoBitrate(`${streamOpts.bitrateKbps}k`)
.format('ivf')
.outputOption('-deadline', 'realtime');
.noAudio()
.size(`${streamOpts.width}x${streamOpts.height}`)
.fpsOutput(streamOpts.fps)
.videoBitrate(`${streamOpts.bitrateKbps}k`)
.format('ivf')
.outputOption('-deadline', 'realtime');
} else {
command.output(StreamOutput(videoOutput).url, { end: false })
.noAudio()
.size(`${streamOpts.width}x${streamOpts.height}`)
.fpsOutput(streamOpts.fps)
.videoBitrate(`${streamOpts.bitrateKbps}k`)
.format('h264')
.outputOptions([
'-tune zerolatency',
'-pix_fmt yuv420p',
'-preset ultrafast',
'-profile:v baseline',
`-g ${streamOpts.fps}`,
`-x264-params keyint=${streamOpts.fps}:min-keyint=${streamOpts.fps}`,
'-bsf:v h264_metadata=aud=insert'
]);
.noAudio()
.size(`${streamOpts.width}x${streamOpts.height}`)
.fpsOutput(streamOpts.fps)
.videoBitrate(`${streamOpts.bitrateKbps}k`)
.format('h264')
.outputOptions([
'-tune zerolatency',
'-pix_fmt yuv420p',
'-preset ultrafast',
'-profile:v baseline',
`-g ${streamOpts.fps}`,
`-x264-params keyint=${streamOpts.fps}:min-keyint=${streamOpts.fps}`,
'-bsf:v h264_metadata=aud=insert'
]);
}

videoOutput.pipe(videoStream, { end: false});
if(includeAudio) {
videoOutput.pipe(videoStream, { end: false });

if (includeAudio) {
const audioStream: AudioStream = new AudioStream(mediaUdp);

// make opus stream
const opus = new prism.opus.Encoder({ channels: 2, rate: 48000, frameSize: 960 });

command
.output(StreamOutput(opus).url, { end: false})
.noVideo()
.audioChannels(2)
.audioFrequency(48000)
//.audioBitrate('128k')
.format('s16le');

opus.pipe(audioStream, {end: false});
.output(StreamOutput(opus).url, { end: false })
.noVideo()
.audioChannels(2)
.audioFrequency(48000)
//.audioBitrate('128k')
.format('s16le');

opus.pipe(audioStream, { end: false });
}
if(streamOpts.hardware_acceleration) command.inputOption('-hwaccel', 'auto');
if(isHttpUrl) {
command.inputOption('-headers',

if (streamOpts.hardware_acceleration) command.inputOption('-hwaccel', 'auto');

if (isHttpUrl) {
command.inputOption('-headers',
Object.keys(headers).map(key => key + ": " + headers[key]).join("\r\n")
);
if(!isHls) {
if (!isHls) {
command.inputOptions([
'-reconnect 1',
'-reconnect_at_eof 1',
Expand All @@ -112,9 +113,9 @@ export function streamLivestreamVideo(input: string | Readable, mediaUdp: MediaU
]);
}
}

command.run();
} catch(e) {
} catch (e) {
//audioStream.end();
//videoStream.end();
command = undefined;
Expand All @@ -124,11 +125,11 @@ export function streamLivestreamVideo(input: string | Readable, mediaUdp: MediaU
}

export function getInputMetadata(input: string | Readable): Promise<ffmpeg.FfprobeData> {
return new Promise((resolve,reject) => {
return new Promise((resolve, reject) => {
const instance = ffmpeg(input).on('error', (err, stdout, stderr) => reject(err));

instance.ffprobe((err, metadata) => {
if(err) reject(err);
if (err) reject(err);
instance.removeAllListeners();
resolve(metadata);
instance.kill('SIGINT');
Expand All @@ -137,13 +138,13 @@ export function getInputMetadata(input: string | Readable): Promise<ffmpeg.Ffpro
}

export function inputHasAudio(metadata: ffmpeg.FfprobeData) {
return metadata.streams.some( (value) => value.codec_type === 'audio');
return metadata.streams.some((value) => value.codec_type === 'audio');
}

export function inputHasVideo(metadata: ffmpeg.FfprobeData) {
return metadata.streams.some( (value) => value.codec_type === 'video');
return metadata.streams.some((value) => value.codec_type === 'video');
}

type map = {
[key: string]: string;
};
};

0 comments on commit 3094aba

Please sign in to comment.