@lumen5/beamcoder 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +41 -0
- package/.circleci/images/testbeam10-4.1/Dockerfile +12 -0
- package/.circleci/test_image/Dockerfile +14 -0
- package/.circleci/test_image/build.md +13 -0
- package/.eslintrc.js +27 -0
- package/.github/workflows/publish-npm.yml +33 -0
- package/LICENSE +674 -0
- package/README.md +1221 -0
- package/beamstreams.js +692 -0
- package/binding.gyp +103 -0
- package/examples/encode_h264.js +92 -0
- package/examples/jpeg_app.js +55 -0
- package/examples/jpeg_filter_app.js +101 -0
- package/examples/make_mp4.js +123 -0
- package/images/beamcoder_small.jpg +0 -0
- package/index.d.ts +83 -0
- package/index.js +44 -0
- package/install_ffmpeg.js +240 -0
- package/package.json +45 -0
- package/scratch/decode_aac.js +38 -0
- package/scratch/decode_avci.js +50 -0
- package/scratch/decode_hevc.js +38 -0
- package/scratch/decode_pcm.js +39 -0
- package/scratch/make_a_mux.js +68 -0
- package/scratch/muxer.js +74 -0
- package/scratch/read_wav.js +35 -0
- package/scratch/simple_mux.js +39 -0
- package/scratch/stream_avci.js +127 -0
- package/scratch/stream_mp4.js +78 -0
- package/scratch/stream_mux.js +47 -0
- package/scratch/stream_pcm.js +82 -0
- package/scratch/stream_wav.js +62 -0
- package/scripts/install_beamcoder_dependencies.sh +25 -0
- package/src/adaptor.h +202 -0
- package/src/beamcoder.cc +937 -0
- package/src/beamcoder_util.cc +1129 -0
- package/src/beamcoder_util.h +206 -0
- package/src/codec.cc +7386 -0
- package/src/codec.h +44 -0
- package/src/codec_par.cc +1818 -0
- package/src/codec_par.h +40 -0
- package/src/decode.cc +569 -0
- package/src/decode.h +75 -0
- package/src/demux.cc +584 -0
- package/src/demux.h +88 -0
- package/src/encode.cc +496 -0
- package/src/encode.h +72 -0
- package/src/filter.cc +1888 -0
- package/src/filter.h +30 -0
- package/src/format.cc +5287 -0
- package/src/format.h +77 -0
- package/src/frame.cc +2681 -0
- package/src/frame.h +52 -0
- package/src/governor.cc +286 -0
- package/src/governor.h +30 -0
- package/src/hwcontext.cc +378 -0
- package/src/hwcontext.h +35 -0
- package/src/log.cc +186 -0
- package/src/log.h +20 -0
- package/src/mux.cc +834 -0
- package/src/mux.h +106 -0
- package/src/packet.cc +762 -0
- package/src/packet.h +49 -0
- package/test/codecParamsSpec.js +148 -0
- package/test/decoderSpec.js +56 -0
- package/test/demuxerSpec.js +41 -0
- package/test/encoderSpec.js +69 -0
- package/test/filtererSpec.js +47 -0
- package/test/formatSpec.js +343 -0
- package/test/frameSpec.js +145 -0
- package/test/introspectionSpec.js +73 -0
- package/test/muxerSpec.js +34 -0
- package/test/packetSpec.js +122 -0
- package/types/Beamstreams.d.ts +98 -0
- package/types/Codec.d.ts +123 -0
- package/types/CodecContext.d.ts +555 -0
- package/types/CodecPar.d.ts +108 -0
- package/types/Decoder.d.ts +137 -0
- package/types/Demuxer.d.ts +113 -0
- package/types/Encoder.d.ts +94 -0
- package/types/Filter.d.ts +324 -0
- package/types/FormatContext.d.ts +380 -0
- package/types/Frame.d.ts +295 -0
- package/types/HWContext.d.ts +62 -0
- package/types/Muxer.d.ts +121 -0
- package/types/Packet.d.ts +82 -0
- package/types/PrivClass.d.ts +25 -0
- package/types/Stream.d.ts +165 -0
package/binding.gyp
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [{
|
|
3
|
+
"target_name" : "beamcoder",
|
|
4
|
+
"sources" : [ "src/beamcoder.cc", "src/beamcoder_util.cc",
|
|
5
|
+
"src/log.cc" ,
|
|
6
|
+
"src/governor.cc", "src/demux.cc",
|
|
7
|
+
"src/decode.cc", "src/filter.cc",
|
|
8
|
+
"src/encode.cc", "src/mux.cc",
|
|
9
|
+
"src/packet.cc", "src/frame.cc",
|
|
10
|
+
"src/codec_par.cc", "src/format.cc",
|
|
11
|
+
"src/codec.cc", "src/hwcontext.cc"],
|
|
12
|
+
"conditions": [
|
|
13
|
+
['OS!="win"', {
|
|
14
|
+
"defines": [
|
|
15
|
+
"__STDC_CONSTANT_MACROS"
|
|
16
|
+
],
|
|
17
|
+
"cflags_cc!": [
|
|
18
|
+
"-fno-rtti",
|
|
19
|
+
"-fno-exceptions"
|
|
20
|
+
],
|
|
21
|
+
"cflags_cc": [
|
|
22
|
+
"-std=c++11",
|
|
23
|
+
"-fexceptions"
|
|
24
|
+
]
|
|
25
|
+
}],
|
|
26
|
+
['OS!="win" and OS!="linux"', {
|
|
27
|
+
"link_settings": {
|
|
28
|
+
"libraries": [
|
|
29
|
+
"-lavcodec",
|
|
30
|
+
"-lavdevice",
|
|
31
|
+
"-lavfilter",
|
|
32
|
+
"-lavformat",
|
|
33
|
+
"-lavutil",
|
|
34
|
+
"-lpostproc",
|
|
35
|
+
"-lswresample",
|
|
36
|
+
"-lswscale"
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
}],
|
|
40
|
+
['OS=="win"', {
|
|
41
|
+
"configurations": {
|
|
42
|
+
"Release": {
|
|
43
|
+
"msvs_settings": {
|
|
44
|
+
"VCCLCompilerTool": {
|
|
45
|
+
"RuntimeTypeInfo": "true"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"include_dirs" : [
|
|
51
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/include"
|
|
52
|
+
],
|
|
53
|
+
"libraries": [
|
|
54
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/avcodec",
|
|
55
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/avdevice",
|
|
56
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/avfilter",
|
|
57
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/avformat",
|
|
58
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/avutil",
|
|
59
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/postproc",
|
|
60
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/swresample",
|
|
61
|
+
"-l../ffmpeg/ffmpeg-5.x-win64-shared/lib/swscale"
|
|
62
|
+
],
|
|
63
|
+
"copies": [
|
|
64
|
+
{
|
|
65
|
+
"destination": "build/Release/",
|
|
66
|
+
"files": [
|
|
67
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/avcodec-59.dll",
|
|
68
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/avdevice-59.dll",
|
|
69
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/avfilter-8.dll",
|
|
70
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/avformat-59.dll",
|
|
71
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/avutil-57.dll",
|
|
72
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/postproc-56.dll",
|
|
73
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/swresample-4.dll",
|
|
74
|
+
"ffmpeg/ffmpeg-5.x-win64-shared/bin/swscale-6.dll"
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
}],
|
|
79
|
+
['OS=="linux"', {
|
|
80
|
+
"libraries": [
|
|
81
|
+
"<!(pkg-config --libs libavcodec)",
|
|
82
|
+
"<!(pkg-config --libs libavdevice)",
|
|
83
|
+
"<!(pkg-config --libs libavfilter)",
|
|
84
|
+
"<!(pkg-config --libs libavformat)",
|
|
85
|
+
"<!(pkg-config --libs libavutil)",
|
|
86
|
+
"<!(pkg-config --libs libpostproc)",
|
|
87
|
+
"<!(pkg-config --libs libswresample)",
|
|
88
|
+
"<!(pkg-config --libs libswscale)"
|
|
89
|
+
]
|
|
90
|
+
}],
|
|
91
|
+
['OS=="mac"', {
|
|
92
|
+
"include_dirs" : [
|
|
93
|
+
"/usr/local/Cellar/ffmpeg@5/5.1.3/include/",
|
|
94
|
+
"/opt/homebrew/Cellar/ffmpeg@5/5.1.3/include/",
|
|
95
|
+
],
|
|
96
|
+
"library_dirs": [
|
|
97
|
+
"/usr/local/Cellar/ffmpeg@5/5.1.3/lib/",
|
|
98
|
+
"/opt/homebrew/Cellar/ffmpeg@5/5.1.3/lib/",
|
|
99
|
+
]
|
|
100
|
+
}],
|
|
101
|
+
]
|
|
102
|
+
}]
|
|
103
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings for FFmpeg.
|
|
3
|
+
Copyright (C) 2019 Streampunk Media Ltd.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
https://www.streampunk.media/ mailto:furnace@streampunk.media
|
|
19
|
+
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* Generate a 200 frame test pattern and encode it as a raw H.264 file.
|
|
23
|
+
|
|
24
|
+
Usage: node encode_h264.js <filename.h264>
|
|
25
|
+
|
|
26
|
+
Output can be viewed in VLC. Make sure "All Files" is selected to see the file.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const beamcoder = require('../index.js'); // Use require('beamcoder') externally
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
|
|
32
|
+
let endcode = Buffer.from([0, 0, 1, 0xb7]);
|
|
33
|
+
|
|
34
|
+
async function run() {
|
|
35
|
+
let start = process.hrtime();
|
|
36
|
+
let encParams = {
|
|
37
|
+
name: 'libx264',
|
|
38
|
+
width: 1920,
|
|
39
|
+
height: 1080,
|
|
40
|
+
bit_rate: 2000000,
|
|
41
|
+
time_base: [1, 25],
|
|
42
|
+
framerate: [25, 1],
|
|
43
|
+
gop_size: 10,
|
|
44
|
+
max_b_frames: 1,
|
|
45
|
+
pix_fmt: 'yuv420p',
|
|
46
|
+
priv_data: { preset: 'slow' }
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let encoder = beamcoder.encoder(encParams);
|
|
50
|
+
console.log('Encoder', encoder);
|
|
51
|
+
|
|
52
|
+
let outFile = fs.createWriteStream(process.argv[2]);
|
|
53
|
+
|
|
54
|
+
for ( let i = 0 ; i < 200 ; i++ ) {
|
|
55
|
+
let frame = beamcoder.frame({
|
|
56
|
+
width: encParams.width,
|
|
57
|
+
height: encParams.height,
|
|
58
|
+
format: encParams.pix_fmt
|
|
59
|
+
}).alloc();
|
|
60
|
+
|
|
61
|
+
let linesize = frame.linesize;
|
|
62
|
+
let [ ydata, bdata, cdata ] = frame.data;
|
|
63
|
+
frame.pts = i;
|
|
64
|
+
|
|
65
|
+
for ( let y = 0 ; y < frame.height ; y++ ) {
|
|
66
|
+
for ( let x = 0 ; x < linesize[0] ; x++ ) {
|
|
67
|
+
ydata[y * linesize[0] + x] = x + y + i * 3;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for ( let y = 0 ; y < frame.height / 2 ; y++) {
|
|
72
|
+
for ( let x = 0; x < linesize[1] ; x++) {
|
|
73
|
+
bdata[y * linesize[1] + x] = 128 + y + i * 2;
|
|
74
|
+
cdata[y * linesize[1] + x] = 64 + x + i * 5;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let packets = await encoder.encode(frame);
|
|
79
|
+
if ( i % 10 === 0) console.log('Encoding frame', i);
|
|
80
|
+
packets.packets.forEach(x => outFile.write(x.data));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let p2 = await encoder.flush();
|
|
84
|
+
console.log('Flushing', p2.packets.length, 'frames.');
|
|
85
|
+
p2.packets.forEach(x => outFile.write(x.data));
|
|
86
|
+
outFile.end(endcode);
|
|
87
|
+
|
|
88
|
+
console.log('Total time ', process.hrtime(start));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (typeof process.argv[2] === 'string') { run(); }
|
|
92
|
+
else { console.error('Error: Please provide a file name.'); }
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings for FFmpeg.
|
|
3
|
+
Copyright (C) 2019 Streampunk Media Ltd.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
https://www.streampunk.media/ mailto:furnace@streampunk.media
|
|
19
|
+
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* Main example from the README.md. Run in a folder of media files.
|
|
23
|
+
|
|
24
|
+
Only supports 8-bit YUV 4:2:2 or 4:2:0 pixel formats.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const beamcoder = require('../index.js'); // Use require('beamcoder') externally
|
|
28
|
+
const Koa = require('koa'); // Add koa to package.json dependencies
|
|
29
|
+
const app = new Koa();
|
|
30
|
+
|
|
31
|
+
app.use(async (ctx) => { // Assume HTTP GET with path /<file_name>/<time_in_s>
|
|
32
|
+
let parts = ctx.path.split('/'); // Split the path into filename and time
|
|
33
|
+
if ((parts.length < 3) || (isNaN(+parts[2]))) return; // Ignore favicon etc..
|
|
34
|
+
let dm = await beamcoder.demuxer('file:' + parts[1]); // Probe the file
|
|
35
|
+
await dm.seek({ time: +parts[2] }); // Seek to the closest keyframe to time
|
|
36
|
+
let packet = await dm.read(); // Find the next video packet (assumes stream 0)
|
|
37
|
+
for ( ; packet.stream_index !== 0 ; packet = await dm.read() );
|
|
38
|
+
let dec = beamcoder.decoder({ demuxer: dm, stream_index: 0 }); // Create a decoder
|
|
39
|
+
let decResult = await dec.decode(packet); // Decode the frame
|
|
40
|
+
if (decResult.frames.length === 0) // Frame may be buffered, so flush it out
|
|
41
|
+
decResult = await dec.flush();
|
|
42
|
+
// Filtering could be used to transform the picture here, e.g. scaling
|
|
43
|
+
let enc = beamcoder.encoder({ // Create an encoder for JPEG data
|
|
44
|
+
name : 'mjpeg', // FFmpeg does not have an encoder called 'jpeg'
|
|
45
|
+
width : dec.width,
|
|
46
|
+
height: dec.height,
|
|
47
|
+
pix_fmt: dec.pix_fmt.indexOf('422') >= 0 ? 'yuvj422p' : 'yuvj420p',
|
|
48
|
+
time_base: [1, 1] });
|
|
49
|
+
let jpegResult = await enc.encode(decResult.frames[0]); // Encode the frame
|
|
50
|
+
await enc.flush(); // Tidy the encoder
|
|
51
|
+
ctx.type = 'image/jpeg'; // Set the Content-Type of the data
|
|
52
|
+
ctx.body = jpegResult.packets[0].data; // Return the JPEG image data
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
app.listen(3000); // Start the server on port 3000
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings for FFmpeg.
|
|
3
|
+
Copyright (C) 2019 Streampunk Media Ltd.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
https://www.streampunk.media/ mailto:furnace@streampunk.media
|
|
19
|
+
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* Main example from the README.md with added filtering. Run in a folder of media files.
|
|
23
|
+
|
|
24
|
+
Will convert source pixel formats to 8-bit YUV 4:2:2
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
const beamcoder = require('../index.js'); // Use require('beamcoder') externally
|
|
28
|
+
const Koa = require('koa'); // Add koa to package.json dependencies
|
|
29
|
+
const app = new Koa();
|
|
30
|
+
|
|
31
|
+
app.use(async (ctx) => { // Assume HTTP GET with path /<file_name>/<time_in_s>
|
|
32
|
+
let parts = ctx.path.split('/'); // Split the path into filename and time
|
|
33
|
+
if ((parts.length < 3) || (isNaN(+parts[2]))) return; // Ignore favicon etc..
|
|
34
|
+
let dm = await beamcoder.demuxer('file:' + parts[1]); // Probe the file
|
|
35
|
+
await dm.seek({ time: +parts[2] }); // Seek to the closest keyframe to time
|
|
36
|
+
let packet = await dm.read(); // Find the next video packet (assumes stream 0)
|
|
37
|
+
for ( ; packet.stream_index !== 0 ; packet = await dm.read() );
|
|
38
|
+
let dec = beamcoder.decoder({ demuxer: dm, stream_index: 0 }); // Create a decoder
|
|
39
|
+
let decResult = await dec.decode(packet); // Decode the frame
|
|
40
|
+
if (decResult.frames.length === 0) // Frame may be buffered, so flush it out
|
|
41
|
+
decResult = await dec.flush();
|
|
42
|
+
|
|
43
|
+
// audio test
|
|
44
|
+
const aindex = 2;
|
|
45
|
+
const audStr = dm.streams[aindex];
|
|
46
|
+
// console.log(audStr);
|
|
47
|
+
let adec = beamcoder.decoder({ demuxer: dm, stream_index: aindex }); // Create a decoder
|
|
48
|
+
// console.log(adec);
|
|
49
|
+
let apkt = await dm.read();
|
|
50
|
+
let afrm = await adec.decode(apkt);
|
|
51
|
+
console.log(afrm.frames);
|
|
52
|
+
const audEnc = beamcoder.encoder({
|
|
53
|
+
name: 'aac',
|
|
54
|
+
sample_fmt: 'fltp',
|
|
55
|
+
sample_rate: 48000,
|
|
56
|
+
channels: 1,
|
|
57
|
+
channel_layout: 'mono', });
|
|
58
|
+
|
|
59
|
+
const audFilt = await beamcoder.filterer({ // Create a filterer for audio
|
|
60
|
+
filterType: 'audio',
|
|
61
|
+
inputParams: [{
|
|
62
|
+
sampleRate: audStr.codecpar.sample_rate,
|
|
63
|
+
sampleFormat: adec.sample_fmt,
|
|
64
|
+
channelLayout: audStr.codecpar.channel_layout,
|
|
65
|
+
timeBase: audStr.time_base }],
|
|
66
|
+
outputParams: [{
|
|
67
|
+
sampleRate: 1024,
|
|
68
|
+
sampleFormat: 'fltp',
|
|
69
|
+
channelLayout: 'mono' }],
|
|
70
|
+
filterSpec: 'aresample=1024' });
|
|
71
|
+
|
|
72
|
+
const audFiltPkt = await audFilt.filter([{ frames: afrm }]);
|
|
73
|
+
const encPkt = await audEnc.encode(audFiltPkt[0].frames[0]);
|
|
74
|
+
console.log(encPkt);
|
|
75
|
+
|
|
76
|
+
let vstr = dm.streams[0]; // Select the video stream (assumes stream 0)
|
|
77
|
+
let filt = await beamcoder.filterer({ // Create a filterer for video
|
|
78
|
+
filterType: 'video',
|
|
79
|
+
inputParams: [{
|
|
80
|
+
width: vstr.codecpar.width,
|
|
81
|
+
height: vstr.codecpar.height,
|
|
82
|
+
pixelFormat: vstr.codecpar.format,
|
|
83
|
+
timeBase: vstr.time_base,
|
|
84
|
+
pixelAspect: vstr.sample_aspect_ratio }],
|
|
85
|
+
outputParams: [{ pixelFormat: 'yuv422p' }],
|
|
86
|
+
filterSpec: 'scale=640:360, colorspace=range=jpeg:all=bt709' });
|
|
87
|
+
let filtResult = await filt.filter([{ frames: decResult }]); // Filter the frame
|
|
88
|
+
let filtFrame = filtResult[0].frames[0];
|
|
89
|
+
let enc = beamcoder.encoder({ // Create an encoder for JPEG data
|
|
90
|
+
name : 'mjpeg', // FFmpeg does not have an encoder called 'jpeg'
|
|
91
|
+
width : filtFrame.width,
|
|
92
|
+
height: filtFrame.height,
|
|
93
|
+
pix_fmt: 'yuvj422p',
|
|
94
|
+
time_base: [1, 1] });
|
|
95
|
+
let jpegResult = await enc.encode(filtFrame); // Encode the filtered frame
|
|
96
|
+
await enc.flush(); // Tidy the encoder
|
|
97
|
+
ctx.type = 'image/jpeg'; // Set the Content-Type of the data
|
|
98
|
+
ctx.body = jpegResult.packets[0].data; // Return the JPEG image data
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
app.listen(3000); // Start the server on port 3000
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings for FFmpeg.
|
|
3
|
+
Copyright (C) 2019 Streampunk Media Ltd.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
https://www.streampunk.media/ mailto:furnace@streampunk.media
|
|
19
|
+
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/* Generate a 200 frame test pattern and encode it as a raw H.264 file.
|
|
23
|
+
|
|
24
|
+
Usage: node encode_h264.js <filename.h264>
|
|
25
|
+
|
|
26
|
+
Output can be viewed in VLC. Make sure "All Files" is selected to see the file.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const beamcoder = require('../index.js'); // Use require('beamcoder') externally
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
|
|
32
|
+
let endcode = Buffer.from([0, 0, 1, 0xb7]);
|
|
33
|
+
|
|
34
|
+
async function run() {
|
|
35
|
+
let start = process.hrtime();
|
|
36
|
+
let encParams = {
|
|
37
|
+
name: 'libx264',
|
|
38
|
+
width: 1920,
|
|
39
|
+
height: 1080,
|
|
40
|
+
bit_rate: 2000000,
|
|
41
|
+
time_base: [1, 25],
|
|
42
|
+
framerate: [25, 1],
|
|
43
|
+
gop_size: 10,
|
|
44
|
+
max_b_frames: 1,
|
|
45
|
+
pix_fmt: 'yuv420p',
|
|
46
|
+
priv_data: { preset: 'slow' }
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
let encoder = await beamcoder.encoder(encParams);
|
|
50
|
+
console.log('Encoder', encoder);
|
|
51
|
+
|
|
52
|
+
const mux = beamcoder.muxer({ format_name: 'mp4' });
|
|
53
|
+
let vstr = mux.newStream({
|
|
54
|
+
name: 'h264',
|
|
55
|
+
time_base: [1, 90000],
|
|
56
|
+
interleaved: true }); // Set to false for manual interleaving, true for automatic
|
|
57
|
+
Object.assign(vstr.codecpar, {
|
|
58
|
+
width: 1920,
|
|
59
|
+
height: 1080,
|
|
60
|
+
format: 'yuv420p'
|
|
61
|
+
});
|
|
62
|
+
console.log(vstr);
|
|
63
|
+
await mux.openIO({
|
|
64
|
+
url: 'file:test.mp4'
|
|
65
|
+
});
|
|
66
|
+
await mux.writeHeader();
|
|
67
|
+
|
|
68
|
+
let outFile = fs.createWriteStream(process.argv[2]);
|
|
69
|
+
|
|
70
|
+
for ( let i = 0 ; i < 200 ; i++ ) {
|
|
71
|
+
let frame = beamcoder.frame({
|
|
72
|
+
width: encParams.width,
|
|
73
|
+
height: encParams.height,
|
|
74
|
+
format: encParams.pix_fmt
|
|
75
|
+
}).alloc();
|
|
76
|
+
|
|
77
|
+
let linesize = frame.linesize;
|
|
78
|
+
let [ ydata, bdata, cdata ] = frame.data;
|
|
79
|
+
frame.pts = i+100;
|
|
80
|
+
|
|
81
|
+
for ( let y = 0 ; y < frame.height ; y++ ) {
|
|
82
|
+
for ( let x = 0 ; x < linesize[0] ; x++ ) {
|
|
83
|
+
ydata[y * linesize[0] + x] = x + y + i * 3;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for ( let y = 0 ; y < frame.height / 2 ; y++) {
|
|
88
|
+
for ( let x = 0; x < linesize[1] ; x++) {
|
|
89
|
+
bdata[y * linesize[1] + x] = 128 + y + i * 2;
|
|
90
|
+
cdata[y * linesize[1] + x] = 64 + x + i * 5;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let packets = await encoder.encode(frame);
|
|
95
|
+
if ( i % 10 === 0) console.log('Encoding frame', i);
|
|
96
|
+
for (const pkt of packets.packets) {
|
|
97
|
+
pkt.duration = 1;
|
|
98
|
+
pkt.stream_index = vstr.index;
|
|
99
|
+
pkt.pts = pkt.pts * 90000/25;
|
|
100
|
+
pkt.dts = pkt.dts * 90000/25;
|
|
101
|
+
await mux.writeFrame(pkt);
|
|
102
|
+
outFile.write(pkt.data);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
let p2 = await encoder.flush();
|
|
107
|
+
console.log('Flushing', p2.packets.length, 'frames.');
|
|
108
|
+
for (const pkt of p2.packets) {
|
|
109
|
+
pkt.duration = 1;
|
|
110
|
+
pkt.stream_index = vstr.index;
|
|
111
|
+
pkt.pts = pkt.pts * 90000/25;
|
|
112
|
+
pkt.dts = pkt.dts * 90000/25;
|
|
113
|
+
await mux.writeFrame(pkt);
|
|
114
|
+
outFile.write(pkt.data);
|
|
115
|
+
}
|
|
116
|
+
await mux.writeTrailer();
|
|
117
|
+
outFile.end(endcode);
|
|
118
|
+
|
|
119
|
+
console.log('Total time ', process.hrtime(start));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (typeof process.argv[2] === 'string') { run(); }
|
|
123
|
+
else { console.error('Error: Please provide a file name.'); }
|
|
Binary file
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export * from "./types/CodecPar"
|
|
2
|
+
export * from "./types/Packet"
|
|
3
|
+
export * from "./types/Frame"
|
|
4
|
+
export * from "./types/Stream"
|
|
5
|
+
export * from "./types/Codec"
|
|
6
|
+
export * from "./types/FormatContext"
|
|
7
|
+
export * from "./types/Demuxer"
|
|
8
|
+
export * from "./types/Decoder"
|
|
9
|
+
export * from "./types/Filter"
|
|
10
|
+
export * from "./types/Encoder"
|
|
11
|
+
export * from "./types/Muxer"
|
|
12
|
+
export * from "./types/Beamstreams"
|
|
13
|
+
export * from "./types/HWContext"
|
|
14
|
+
|
|
15
|
+
export const AV_NOPTS_VALUE: number
|
|
16
|
+
|
|
17
|
+
/** The LIBAV**_VERSION_INT for each FFmpeg library */
|
|
18
|
+
export function versions(): {
|
|
19
|
+
avcodec: number
|
|
20
|
+
avdevice: number
|
|
21
|
+
avfilter: number
|
|
22
|
+
avformat: number
|
|
23
|
+
avutil: number
|
|
24
|
+
postproc: number
|
|
25
|
+
swresample: number
|
|
26
|
+
swscale: number
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* FFmpeg version string. This usually is the actual release
|
|
30
|
+
* version number or a git commit description. This string has no fixed format
|
|
31
|
+
* and can change any time. It should never be parsed by code.
|
|
32
|
+
*/
|
|
33
|
+
export function avVersionInfo(): string
|
|
34
|
+
/** Informative version strings for each FFmpeg library */
|
|
35
|
+
export function versionStrings(): {
|
|
36
|
+
avcodec: string
|
|
37
|
+
avdevice: string
|
|
38
|
+
avfilter: string
|
|
39
|
+
avformat: string
|
|
40
|
+
avutil: string
|
|
41
|
+
postproc: string
|
|
42
|
+
swresample: string
|
|
43
|
+
swscale: string
|
|
44
|
+
}
|
|
45
|
+
/** Build configuration strings for each FFmpeg library */
|
|
46
|
+
export function configurations(): {
|
|
47
|
+
avcodec: string
|
|
48
|
+
avdevice: string
|
|
49
|
+
avfilter: string
|
|
50
|
+
avformat: string
|
|
51
|
+
avutil: string
|
|
52
|
+
postproc: string
|
|
53
|
+
swresample: string
|
|
54
|
+
swscale: string
|
|
55
|
+
}
|
|
56
|
+
/** License strings for each FFmpeg library */
|
|
57
|
+
export function licenses(): {
|
|
58
|
+
avcodec: string
|
|
59
|
+
avdevice: string
|
|
60
|
+
avfilter: string
|
|
61
|
+
avformat: string
|
|
62
|
+
avutil: string
|
|
63
|
+
postproc: string
|
|
64
|
+
swresample: string
|
|
65
|
+
swscale: string
|
|
66
|
+
}
|
|
67
|
+
/** List the available protocols */
|
|
68
|
+
export function protocols(): { inputs: Array<string>, outputs: Array<string> }
|
|
69
|
+
|
|
70
|
+
/** Read or set the logging level
|
|
71
|
+
* `quiet` - print no output.
|
|
72
|
+
* `panic` - something went really wrong - crash will follow
|
|
73
|
+
* `fatal` - recovery not possible
|
|
74
|
+
* `error` - lossless recovery not possible
|
|
75
|
+
* `warning` - something doesn't look correct
|
|
76
|
+
* `info` - standard information - the default
|
|
77
|
+
* `verbose` - detailed information
|
|
78
|
+
* `debug` - stuff which is only useful for libav* developers
|
|
79
|
+
* `trace` - extremely verbose debugging for libav* developers
|
|
80
|
+
*/
|
|
81
|
+
export function logging(level?: string): string | undefined
|
|
82
|
+
|
|
83
|
+
export as namespace Beamcoder
|
package/index.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Aerostat Beam Coder - Node.js native bindings to FFmpeg
|
|
3
|
+
Copyright (C) 2019 Streampunk Media Ltd.
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
|
|
18
|
+
https://www.streampunk.media/ mailto:furnace@streampunk.media
|
|
19
|
+
14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const beamcoder = require('bindings')('beamcoder');
|
|
23
|
+
const beamstreams = require('./beamstreams.js');
|
|
24
|
+
|
|
25
|
+
// Provide useful debug on segfault-related crash
|
|
26
|
+
const SegfaultHandler = require('segfault-handler');
|
|
27
|
+
SegfaultHandler.registerHandler('crash.log');
|
|
28
|
+
|
|
29
|
+
const splash = `Aerostat Beam Coder Copyright (C) 2019 Streampunk Media Ltd
|
|
30
|
+
GPL v3.0 or later license. This program comes with ABSOLUTELY NO WARRANTY.
|
|
31
|
+
This is free software, and you are welcome to redistribute it
|
|
32
|
+
under certain conditions. Conditions and warranty at:
|
|
33
|
+
https://github.com/Streampunk/beamcoder/blob/master/LICENSE`;
|
|
34
|
+
|
|
35
|
+
console.log(splash);
|
|
36
|
+
console.log('Using FFmpeg version', beamcoder.avVersionInfo());
|
|
37
|
+
|
|
38
|
+
beamcoder.demuxerStream = beamstreams.demuxerStream;
|
|
39
|
+
beamcoder.muxerStream = beamstreams.muxerStream;
|
|
40
|
+
|
|
41
|
+
beamcoder.makeSources = beamstreams.makeSources;
|
|
42
|
+
beamcoder.makeStreams = beamstreams.makeStreams;
|
|
43
|
+
|
|
44
|
+
module.exports = beamcoder;
|