-
-
Notifications
You must be signed in to change notification settings - Fork 33
/
server.js
126 lines (113 loc) · 3.24 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
const express = require('express');
const app = express();
const compression = require('compression');
const cors = require('cors');
const path = require('path');
const fs = require('fs');
const spawn = require('child_process').spawn;
const ffmpeg = require('fluent-ffmpeg');
const https = require('https');
module.exports = (
directory,
format,
width,
height,
framerate,
horizontalFlip,
verticalFlip,
compressionLevel,
time,
listSize,
storageSize,
port,
secure,
certificatePath,
keyPath
) => {
// Create the camera output directory if it doesn't already exist
// Sync, because this is only run once at startup and everything depends on it
if (fs.existsSync(directory) === false) fs.mkdirSync(directory);
// Start the camera stream
const raspividOptions = ['-o', '-', '-t', '0', '-w', width, '-h', height, '-fps', framerate, '-g', framerate, '-n'];
if (horizontalFlip) raspividOptions.push('-hf');
if (verticalFlip) raspividOptions.push('-vf');
const cameraStream = spawn('raspivid', raspividOptions);
// Setup up a special shutdown function that's called when encountering an error
// so that we always shut down the camera stream properly
const kill = (err) => {
cameraStream.kill();
throw err;
};
// Set up camera stream conversion
let conversionStream = ffmpeg(cameraStream.stdout)
.inputFPS(framerate)
.noAudio();
if (format === 'hls') {
const outputOptions = [
'-hls_time',
time,
'-hls_list_size',
listSize,
'-hls_delete_threshold',
storageSize,
'-hls_flags',
'split_by_time+delete_segments+second_level_segment_index',
'-strftime',
1,
'-hls_segment_filename',
path.join(directory, '%s-%%d.m4s'),
'-hls_segment_type',
'fmp4'
];
conversionStream
.videoCodec('copy')
.format('hls')
.inputOptions(['-re'])
.outputOptions(outputOptions)
.output(path.join(directory, 'livestream.m3u8'));
} else if (format === 'dash') {
const outputOptions = [
'-seg_duration',
time,
'-window_size',
listSize,
'-extra_window_size',
storageSize,
'-init_seg_name',
'init.m4s',
'-media_seg_name',
'$Time$-$Number$.m4s'
];
conversionStream
.videoCodec('copy')
.format('dash')
.inputOptions(['-re'])
.outputOptions(outputOptions)
.output(path.join(directory, 'livestream.mpd'));
} else {
kill(Error('unsupported format'));
}
// Start stream processing
conversionStream
.on('error', (err, stdout, stderr) => kill(err))
.on('start', (commandLine) => console.log('started video processing: ' + commandLine))
.on('stderr', (stderrLine) => console.log('conversion: ' + stderrLine))
.run();
// Endpoint the streaming files will be available on
const endpoint = '/camera';
// Setup express server
app.use(cors());
app.use(compression({ level: compressionLevel }));
app.use(endpoint, express.static(directory));
if (secure) {
https
.createServer({
cert: fs.readFileSync(certificatePath, 'utf8'),
key: fs.readFileSync(keyPath, 'utf8')
}, app)
.listen(port);
} else {
app.listen(port);
}
console.log('camera stream server started');
};