bare-ffmpeg 1.0.0-8 → 1.0.0

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.
Files changed (58) hide show
  1. package/CMakeLists.txt +32 -10
  2. package/README.md +65 -769
  3. package/binding.cc +5372 -0
  4. package/cmake/ports/ffmpeg/port.cmake +513 -0
  5. package/cmake/ports/libdrm/port.cmake +32 -0
  6. package/cmake/ports/libva/port.cmake +53 -0
  7. package/cmake/ports/opus/patches/01-windows-clang.patch +52 -0
  8. package/cmake/ports/opus/port.cmake +42 -0
  9. package/cmake/ports/svt-av1/port.cmake +42 -0
  10. package/cmake/ports/x264/patches/01-windows-clang.patch +33 -0
  11. package/cmake/ports/x264/port.cmake +162 -0
  12. package/index.js +28 -9
  13. package/lib/audio-fifo.js +47 -0
  14. package/lib/channel-layout.js +35 -0
  15. package/lib/codec-context.js +185 -7
  16. package/lib/codec-parameters.js +252 -4
  17. package/lib/codec.js +17 -0
  18. package/lib/constants.js +340 -12
  19. package/lib/dictionary.js +20 -0
  20. package/lib/errors.js +26 -0
  21. package/lib/filter-context.js +7 -0
  22. package/lib/filter-graph.js +42 -0
  23. package/lib/filter-inout.js +52 -0
  24. package/lib/filter.js +7 -0
  25. package/lib/format-context.js +43 -23
  26. package/lib/frame.js +81 -12
  27. package/lib/hw-device-context.js +31 -0
  28. package/lib/hw-frames-constraints.js +54 -0
  29. package/lib/hw-frames-context.js +92 -0
  30. package/lib/image.js +14 -3
  31. package/lib/input-format.js +36 -2
  32. package/lib/io-context.js +29 -4
  33. package/lib/log.js +16 -0
  34. package/lib/output-format.js +33 -2
  35. package/lib/packet.js +143 -1
  36. package/lib/rational.js +46 -1
  37. package/lib/resampler.js +23 -17
  38. package/lib/samples.js +85 -0
  39. package/lib/scaler.js +3 -9
  40. package/lib/stream.js +51 -14
  41. package/package.json +9 -5
  42. package/prebuilds/android-arm/bare-ffmpeg.bare +0 -0
  43. package/prebuilds/android-arm64/bare-ffmpeg.bare +0 -0
  44. package/prebuilds/android-ia32/bare-ffmpeg.bare +0 -0
  45. package/prebuilds/android-x64/bare-ffmpeg.bare +0 -0
  46. package/prebuilds/darwin-arm64/bare-ffmpeg.bare +0 -0
  47. package/prebuilds/darwin-x64/bare-ffmpeg.bare +0 -0
  48. package/prebuilds/ios-arm64/bare-ffmpeg.bare +0 -0
  49. package/prebuilds/ios-arm64-simulator/bare-ffmpeg.bare +0 -0
  50. package/prebuilds/ios-x64-simulator/bare-ffmpeg.bare +0 -0
  51. package/prebuilds/linux-arm64/bare-ffmpeg/libva-drm.so.2 +0 -0
  52. package/prebuilds/linux-arm64/bare-ffmpeg/libva.so.2 +0 -0
  53. package/prebuilds/linux-arm64/bare-ffmpeg.bare +0 -0
  54. package/prebuilds/linux-x64/bare-ffmpeg/libva-drm.so.2 +0 -0
  55. package/prebuilds/linux-x64/bare-ffmpeg/libva.so.2 +0 -0
  56. package/prebuilds/linux-x64/bare-ffmpeg.bare +0 -0
  57. package/prebuilds/win32-arm64/bare-ffmpeg.bare +0 -0
  58. package/prebuilds/win32-x64/bare-ffmpeg.bare +0 -0
package/lib/io-context.js CHANGED
@@ -1,11 +1,28 @@
1
1
  const binding = require('../binding')
2
2
 
3
3
  module.exports = class FFmpegIOContext {
4
- constructor(buffer = Buffer.alloc(0)) {
4
+ constructor(buffer, opts = {}) {
5
+ let offset = 0
6
+ let len = 0
7
+
8
+ if (typeof buffer === 'number') {
9
+ len = buffer
10
+ buffer = undefined
11
+ } else if (buffer) {
12
+ offset = buffer.byteOffset
13
+ len = buffer.byteLength
14
+ buffer = buffer.buffer
15
+ } else {
16
+ buffer = Buffer.alloc(0)
17
+ }
18
+
5
19
  this._handle = binding.initIOContext(
6
- buffer.buffer,
7
- buffer.byteOffset,
8
- buffer.byteLength
20
+ buffer,
21
+ offset,
22
+ len,
23
+ opts.onwrite && onwriteWrapper.bind(null, opts.onwrite),
24
+ opts.onread && onreadWrapper.bind(null, opts.onread),
25
+ opts.onseek
9
26
  )
10
27
  }
11
28
 
@@ -18,3 +35,11 @@ module.exports = class FFmpegIOContext {
18
35
  this.destroy()
19
36
  }
20
37
  }
38
+
39
+ function onwriteWrapper(target, arraybuffer) {
40
+ return target(Buffer.from(arraybuffer))
41
+ }
42
+
43
+ function onreadWrapper(target, arraybuffer, requestedLen) {
44
+ return target(Buffer.from(arraybuffer), requestedLen)
45
+ }
package/lib/log.js ADDED
@@ -0,0 +1,16 @@
1
+ const binding = require('../binding')
2
+ const constants = require('./constants')
3
+
4
+ module.exports = {
5
+ ...constants.logLevels
6
+ }
7
+
8
+ Object.defineProperty(module.exports, 'level', {
9
+ get() {
10
+ return binding.getLogLevel()
11
+ },
12
+
13
+ set(level) {
14
+ return binding.setLogLevel(level)
15
+ }
16
+ })
@@ -1,7 +1,38 @@
1
1
  const binding = require('../binding')
2
2
 
3
3
  module.exports = class FFmpegOutputFormat {
4
- constructor(name) {
5
- this._handle = binding.initOutputFormat(name)
4
+ constructor(name, handle) {
5
+ if (handle) this._handle = handle
6
+ else this._handle = binding.initOutputFormat(name)
7
+ }
8
+
9
+ static from(handle) {
10
+ return new FFmpegOutputFormat(null, handle)
11
+ }
12
+
13
+ get flags() {
14
+ return binding.getOutputFormatFlags(this._handle)
15
+ }
16
+
17
+ get extensions() {
18
+ return binding.getOutputFormatExtensions(this._handle)
19
+ }
20
+
21
+ get mimeType() {
22
+ return binding.getOutputFormatMimeType(this._handle)
23
+ }
24
+
25
+ get name() {
26
+ return binding.getOutputFormatName(this._handle)
27
+ }
28
+
29
+ [Symbol.for('bare.inspect')]() {
30
+ return {
31
+ __proto__: { constructor: FFmpegOutputFormat },
32
+ name: this.name,
33
+ flags: this.flags,
34
+ extensions: this.extensions,
35
+ mimeType: this.mimeType
36
+ }
6
37
  }
7
38
  }
package/lib/packet.js CHANGED
@@ -1,4 +1,41 @@
1
1
  const binding = require('../binding')
2
+ const Rational = require('./rational')
3
+
4
+ class SideData {
5
+ constructor(handle, opts = {}) {
6
+ this._handle = handle
7
+ this._type = opts.type || null
8
+ this._data = opts.data || null
9
+ }
10
+
11
+ static fromData(data, type) {
12
+ return new SideData(null, { data, type })
13
+ }
14
+
15
+ get type() {
16
+ if (this._type) return this._type
17
+ return binding.getSideDataType(this._handle)
18
+ }
19
+
20
+ get name() {
21
+ if (!this._handle) return null
22
+ return binding.getSideDataName(this._handle)
23
+ }
24
+
25
+ get data() {
26
+ if (this._data) return this._data
27
+ return Buffer.from(binding.getSideDataBuffer(this._handle))
28
+ }
29
+
30
+ [Symbol.for('bare.inspect')]() {
31
+ return {
32
+ __proto__: { constructor: SideData },
33
+ type: this.type,
34
+ name: this.name,
35
+ data: this.data
36
+ }
37
+ }
38
+ }
2
39
 
3
40
  module.exports = class FFmpegPacket {
4
41
  constructor(buffer) {
@@ -14,7 +51,7 @@ module.exports = class FFmpegPacket {
14
51
  }
15
52
 
16
53
  destroy() {
17
- binding.unrefPacket(this._handle)
54
+ binding.destroyPacket(this._handle)
18
55
  this._handle = null
19
56
  }
20
57
 
@@ -26,11 +63,116 @@ module.exports = class FFmpegPacket {
26
63
  return binding.getPacketStreamIndex(this._handle)
27
64
  }
28
65
 
66
+ set streamIndex(value) {
67
+ return binding.setPacketStreamIndex(this._handle, value)
68
+ }
69
+
29
70
  get data() {
30
71
  return Buffer.from(binding.getPacketData(this._handle))
31
72
  }
32
73
 
74
+ set data(value) {
75
+ binding.setPacketData(this._handle, value.buffer, value.byteOffset, value.byteLength)
76
+ }
77
+
78
+ get sideData() {
79
+ const handles = binding.getPacketSideData(this._handle)
80
+ return handles.map((handle) => new SideData(handle))
81
+ }
82
+
83
+ set sideData(values) {
84
+ binding.setPacketSideData(
85
+ this._handle,
86
+ values.map((value) => ({
87
+ buffer: value.data.buffer,
88
+ offset: value.data.byteOffset,
89
+ length: value.data.byteLength,
90
+ type: value.type
91
+ }))
92
+ )
93
+ }
94
+
95
+ get isKeyframe() {
96
+ return binding.isPacketKeyframe(this._handle)
97
+ }
98
+
99
+ set isKeyframe(value) {
100
+ binding.setPacketIsKeyFrame(this._handle, value)
101
+ }
102
+
103
+ get dts() {
104
+ return binding.getPacketDTS(this._handle)
105
+ }
106
+
107
+ set dts(value) {
108
+ return binding.setPacketDTS(this._handle, value)
109
+ }
110
+
111
+ get pts() {
112
+ return binding.getPacketPTS(this._handle)
113
+ }
114
+
115
+ set pts(value) {
116
+ return binding.setPacketPTS(this._handle, value)
117
+ }
118
+
119
+ get timeBase() {
120
+ const view = new Int32Array(binding.getPacketTimeBase(this._handle))
121
+ return new Rational(view[0], view[1])
122
+ }
123
+
124
+ set timeBase(value) {
125
+ binding.setPacketTimeBase(this._handle, value.numerator, value.denominator)
126
+ }
127
+
128
+ rescaleTimestamps(srcTimeBase, dstTimeBase) {
129
+ return binding.rescalePacketTimestamps(
130
+ this._handle,
131
+ srcTimeBase.numerator,
132
+ srcTimeBase.denominator,
133
+ dstTimeBase.numerator,
134
+ dstTimeBase.denominator
135
+ )
136
+ }
137
+
138
+ copyPropsFrom(sourcePacket) {
139
+ binding.copyPacketProps(this._handle, sourcePacket._handle)
140
+ }
141
+
142
+ get duration() {
143
+ return binding.getPacketDuration(this._handle)
144
+ }
145
+
146
+ set duration(value) {
147
+ return binding.setPacketDuration(this._handle, value)
148
+ }
149
+
150
+ get flags() {
151
+ return binding.getPacketFlags(this._handle)
152
+ }
153
+
154
+ set flags(value) {
155
+ return binding.setPacketFlags(this._handle, value)
156
+ }
157
+
158
+ [Symbol.for('bare.inspect')]() {
159
+ return {
160
+ __proto__: { constructor: FFmpegPacket },
161
+ streamIndex: this.streamIndex,
162
+ flags: this.flags,
163
+ isKeyframe: this.isKeyframe,
164
+ timeBase: this.timeBase,
165
+ dts: this.dts,
166
+ pts: this.pts,
167
+ duration: this.duration,
168
+ data: this.data,
169
+ sideData: this.sideData
170
+ }
171
+ }
172
+
33
173
  [Symbol.dispose]() {
34
174
  this.destroy()
35
175
  }
36
176
  }
177
+
178
+ module.exports.SideData = SideData
package/lib/rational.js CHANGED
@@ -1,6 +1,51 @@
1
+ const binding = require('../binding')
2
+ const { AV_ROUND_NEAR_INF } = require('./constants').rounding
3
+
1
4
  module.exports = class FFmpegRational {
2
- constructor(numerator, denominator) {
5
+ constructor(numerator = 0, denominator = 1) {
3
6
  this.numerator = numerator
4
7
  this.denominator = denominator
5
8
  }
9
+
10
+ get valid() {
11
+ // don't support negative denominators for now
12
+ if (this.denominator < 0) return false
13
+
14
+ const n = this.toNumber()
15
+
16
+ if (Number.isNaN(n) || n === 0) return false
17
+
18
+ return true
19
+ }
20
+
21
+ get uninitialized() {
22
+ // common initial value for AVRational(0, 1)
23
+ return this.numerator === 0 && this.denominator === 1
24
+ }
25
+
26
+ toNumber() {
27
+ return this.numerator / this.denominator
28
+ }
29
+
30
+ equals(other) {
31
+ return this.numerator === other.numerator && this.denominator === other.denominator
32
+ }
33
+
34
+ static from(num) {
35
+ const view = new Int32Array(binding.rationalD2Q(num))
36
+ return new FFmpegRational(view[0], view[1])
37
+ }
38
+
39
+ static rescaleQ(n, src, dst, round = AV_ROUND_NEAR_INF) {
40
+ if (src.equals(dst)) return n
41
+
42
+ return binding.rationalRescaleQ(
43
+ n,
44
+ src.numerator,
45
+ src.denominator,
46
+ dst.numerator,
47
+ dst.denominator,
48
+ round
49
+ )
50
+ }
6
51
  }
package/lib/resampler.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const binding = require('../binding')
2
+ const ChannelLayout = require('./channel-layout')
2
3
 
3
- class Resampler {
4
+ class FFmpegResampler {
4
5
  constructor(
5
6
  inputSampleRate,
6
7
  inputChannelLayout,
@@ -9,29 +10,36 @@ class Resampler {
9
10
  outputChannelLayout,
10
11
  outputFormat
11
12
  ) {
13
+ inputChannelLayout = ChannelLayout.from(inputChannelLayout)
14
+ outputChannelLayout = ChannelLayout.from(outputChannelLayout)
15
+
16
+ this._inputSampleRate = inputSampleRate
17
+ this._outputSampleRate = outputSampleRate
18
+
12
19
  this._handle = binding.initResampler(
13
20
  inputSampleRate,
14
21
  inputFormat,
15
- inputChannelLayout,
22
+ inputChannelLayout._handle,
16
23
  outputSampleRate,
17
24
  outputFormat,
18
- outputChannelLayout
25
+ outputChannelLayout._handle
19
26
  )
27
+ }
20
28
 
21
- this.inputSampleRate = inputSampleRate
22
- this.outputSampleRate = outputSampleRate
29
+ get inputSampleRate() {
30
+ return this._inputSampleRate
23
31
  }
24
32
 
25
- convert(inputFrame, outputFrame) {
26
- return binding.convertResampler(
27
- this._handle,
28
- inputFrame._handle,
29
- outputFrame._handle
30
- )
33
+ get outputSampleRate() {
34
+ return this._outputSampleRate
31
35
  }
32
36
 
33
37
  get delay() {
34
- return binding.getResamplerDelay(this._handle, this.inputSampleRate)
38
+ return binding.getResamplerDelay(this._handle, this._inputSampleRate)
39
+ }
40
+
41
+ convert(inputFrame, outputFrame) {
42
+ return binding.convertResampler(this._handle, inputFrame._handle, outputFrame._handle)
35
43
  }
36
44
 
37
45
  flush(outputFrame) {
@@ -39,10 +47,8 @@ class Resampler {
39
47
  }
40
48
 
41
49
  destroy() {
42
- if (this._handle) {
43
- binding.destroyResampler(this._handle)
44
- this._handle = null
45
- }
50
+ binding.destroyResampler(this._handle)
51
+ this._handle = null
46
52
  }
47
53
 
48
54
  [Symbol.dispose]() {
@@ -50,4 +56,4 @@ class Resampler {
50
56
  }
51
57
  }
52
58
 
53
- module.exports = Resampler
59
+ module.exports = FFmpegResampler
package/lib/samples.js ADDED
@@ -0,0 +1,85 @@
1
+ const binding = require('../binding')
2
+
3
+ module.exports = class FFmpegSamples {
4
+ _data
5
+ _frame
6
+
7
+ constructor({ noAlignment = false } = {}) {
8
+ this._noAlignment = noAlignment
9
+ }
10
+
11
+ get data() {
12
+ return this._data
13
+ }
14
+
15
+ get format() {
16
+ return this._frame.format
17
+ }
18
+
19
+ get channelLayout() {
20
+ return this._frame.channelLayout
21
+ }
22
+
23
+ get nbSamples() {
24
+ return this._frame.nbSamples
25
+ }
26
+
27
+ get nbChannels() {
28
+ return this.channelLayout && this.channelLayout.nbChannels
29
+ }
30
+
31
+ get pts() {
32
+ return this._frame.pts
33
+ }
34
+
35
+ fill(frame) {
36
+ const { format, channelLayout, nbSamples } = frame
37
+
38
+ const len = FFmpegSamples.bufferSize(
39
+ format,
40
+ channelLayout.nbChannels,
41
+ nbSamples,
42
+ this._noAlignment
43
+ )
44
+
45
+ if (!this._data || len !== this._data.length) {
46
+ this._data = Buffer.allocUnsafe(len)
47
+ }
48
+
49
+ const res = binding.fillSamples(
50
+ frame._handle,
51
+ this._data.buffer,
52
+ this._data.byteOffset,
53
+ this._noAlignment
54
+ )
55
+
56
+ this._frame = frame
57
+
58
+ return res
59
+ }
60
+
61
+ read(frame) {
62
+ const { format, channelLayout, nbSamples } = frame
63
+
64
+ const len = FFmpegSamples.bufferSize(
65
+ format,
66
+ channelLayout.nbChannels,
67
+ nbSamples,
68
+ this._noAlignment
69
+ )
70
+
71
+ if (!this._data || len !== this._data.length) {
72
+ this._data = Buffer.allocUnsafe(len)
73
+ }
74
+
75
+ binding.readSamples(frame._handle, this._data.buffer, this._data.byteOffset, this._noAlignment)
76
+ }
77
+
78
+ copy(frame, dstOffset = 0, srcOffset = 0) {
79
+ binding.copySamples(frame._handle, this._frame._handle, dstOffset, srcOffset, this.nbSamples)
80
+ }
81
+
82
+ static bufferSize(format, nbChannels, nbSamples, noAlignment = false) {
83
+ return binding.samplesBufferSize(format, nbChannels, nbSamples, noAlignment)
84
+ }
85
+ }
package/lib/scaler.js CHANGED
@@ -39,19 +39,13 @@ module.exports = class FFmpegScaler {
39
39
  if (typeof y !== 'number') {
40
40
  target = y
41
41
  y = 0
42
- height = this._targetHeight
42
+ height = source.height
43
43
  } else if (typeof height !== 'number') {
44
44
  target = height
45
- height = this._targetHeight
45
+ height = source.height
46
46
  }
47
47
 
48
- return binding.scaleScaler(
49
- this._handle,
50
- source._handle,
51
- y,
52
- height,
53
- target._handle
54
- )
48
+ return binding.scaleScaler(this._handle, source._handle, y, height, target._handle)
55
49
  }
56
50
 
57
51
  [Symbol.dispose]() {
package/lib/stream.js CHANGED
@@ -2,44 +2,81 @@ const binding = require('../binding')
2
2
  const Codec = require('./codec')
3
3
  const CodecContext = require('./codec-context')
4
4
  const CodecParameters = require('./codec-parameters')
5
+ const Rational = require('./rational')
5
6
 
6
7
  module.exports = class FFmpegStream {
7
8
  constructor(handle) {
8
9
  this._handle = handle
9
10
 
10
- this._codec = Codec.for(binding.getStreamCodec(this._handle))
11
+ this._codecParameters = new CodecParameters(binding.getStreamCodecParameters(this._handle))
12
+ }
13
+
14
+ get id() {
15
+ return binding.getStreamId(this._handle)
16
+ }
11
17
 
12
- this._codecParameters = new CodecParameters(
13
- binding.getStreamCodecParameters(this._handle)
14
- )
18
+ set id(value) {
19
+ binding.setStreamId(this._handle, value)
15
20
  }
16
21
 
17
- destroy() {
18
- this._codecParameters.destroy()
19
- this._codecParameters = null
22
+ get index() {
23
+ return binding.getStreamIndex(this._handle)
20
24
  }
21
25
 
22
26
  get codec() {
23
- return this._codec
27
+ return Codec.for(this.codecParameters.id)
24
28
  }
25
29
 
26
30
  get codecParameters() {
27
31
  return this._codecParameters
28
32
  }
29
33
 
34
+ get timeBase() {
35
+ const view = new Int32Array(binding.getStreamTimeBase(this._handle))
36
+ return new Rational(view[0], view[1])
37
+ }
38
+
39
+ set timeBase(value) {
40
+ binding.setStreamTimeBase(this._handle, value.numerator, value.denominator)
41
+ }
42
+
43
+ get avgFramerate() {
44
+ const view = new Int32Array(binding.getStreamAverageFramerate(this._handle))
45
+ return new Rational(view[0], view[1])
46
+ }
47
+
48
+ set avgFramerate(value) {
49
+ binding.setStreamAverageFramerate(this._handle, value.numerator, value.denominator)
50
+ }
51
+
52
+ get duration() {
53
+ return binding.getStreamDuration(this._handle)
54
+ }
55
+
56
+ set duration(value) {
57
+ binding.setStreamDuration(this._handle, value)
58
+ }
59
+
30
60
  decoder() {
31
- const context = new CodecContext(this._codec.decoder)
61
+ const context = new CodecContext(this.codec.decoder)
32
62
  this._codecParameters.toContext(context)
33
- return context.open()
63
+ return context
34
64
  }
35
65
 
36
66
  encoder() {
37
- const context = new CodecContext(this._codec.encoder)
67
+ const context = new CodecContext(this.codec.encoder)
38
68
  this._codecParameters.toContext(context)
39
- return context.open()
69
+ return context
40
70
  }
41
71
 
42
- [Symbol.dispose]() {
43
- this.destroy()
72
+ [Symbol.for('bare.inspect')]() {
73
+ return {
74
+ __proto__: { constructor: FFmpegStream },
75
+ id: this.id,
76
+ index: this.index,
77
+ timeBase: this.timeBase,
78
+ avgFramerate: this.avgFramerate,
79
+ codecParameters: this.codecParameters
80
+ }
44
81
  }
45
82
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-ffmpeg",
3
- "version": "1.0.0-8",
3
+ "version": "1.0.0",
4
4
  "description": "Low-level FFmpeg bindings for Bare",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -8,15 +8,18 @@
8
8
  },
9
9
  "files": [
10
10
  "index.js",
11
- "binding.c",
11
+ "binding.cc",
12
12
  "binding.js",
13
13
  "CMakeLists.txt",
14
14
  "lib",
15
- "prebuilds"
15
+ "prebuilds",
16
+ "cmake"
16
17
  ],
17
18
  "addon": true,
18
19
  "scripts": {
19
- "test": "prettier . --check && bare test.js"
20
+ "test": "prettier . --check && bare test.js",
21
+ "format": "prettier . --write && clang-format -i binding.cc",
22
+ "example": "bare ./examples/index.js"
20
23
  },
21
24
  "repository": {
22
25
  "type": "git",
@@ -29,11 +32,12 @@
29
32
  },
30
33
  "homepage": "https://github.com/holepunchto/bare-ffmpeg#readme",
31
34
  "devDependencies": {
35
+ "bare-assert": "^1.1.0",
32
36
  "brittle": "^3.4.0",
33
37
  "cmake-bare": "^1.1.2",
34
38
  "cmake-harden": "^1.0.2",
35
39
  "cmake-ports": "^1.5.0",
36
40
  "prettier": "^3.3.3",
37
- "prettier-config-standard": "^7.0.0"
41
+ "prettier-config-holepunch": "^2.0.0"
38
42
  }
39
43
  }