bare-ffmpeg 1.0.0-3 → 1.0.0-31

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 (51) hide show
  1. package/CMakeLists.txt +10 -3
  2. package/README.md +1811 -0
  3. package/binding.cc +4638 -0
  4. package/cmake/ports/ffmpeg/port.cmake +514 -0
  5. package/cmake/ports/opus/patches/01-windows-clang.patch +52 -0
  6. package/cmake/ports/opus/port.cmake +42 -0
  7. package/cmake/ports/svt-av1/port.cmake +42 -0
  8. package/cmake/ports/x264/patches/01-windows-clang.patch +33 -0
  9. package/cmake/ports/x264/port.cmake +157 -0
  10. package/index.js +22 -4
  11. package/lib/audio-fifo.js +47 -0
  12. package/lib/channel-layout.js +35 -0
  13. package/lib/codec-context.js +210 -16
  14. package/lib/codec-parameters.js +242 -8
  15. package/lib/codec.js +19 -1
  16. package/lib/constants.js +245 -9
  17. package/lib/dictionary.js +28 -15
  18. package/lib/errors.js +38 -0
  19. package/lib/filter-context.js +7 -0
  20. package/lib/filter-graph.js +47 -0
  21. package/lib/filter-inout.js +55 -0
  22. package/lib/filter.js +7 -0
  23. package/lib/format-context.js +72 -31
  24. package/lib/frame.js +88 -22
  25. package/lib/image.js +22 -0
  26. package/lib/input-format.js +36 -4
  27. package/lib/io-context.js +36 -6
  28. package/lib/log.js +16 -0
  29. package/lib/output-format.js +33 -2
  30. package/lib/packet.js +153 -12
  31. package/lib/rational.js +49 -1
  32. package/lib/resampler.js +63 -0
  33. package/lib/samples.js +96 -0
  34. package/lib/scaler.js +8 -7
  35. package/lib/stream.js +58 -14
  36. package/package.json +9 -4
  37. package/prebuilds/android-arm/bare-ffmpeg.bare +0 -0
  38. package/prebuilds/android-arm64/bare-ffmpeg.bare +0 -0
  39. package/prebuilds/android-ia32/bare-ffmpeg.bare +0 -0
  40. package/prebuilds/android-x64/bare-ffmpeg.bare +0 -0
  41. package/prebuilds/darwin-arm64/bare-ffmpeg.bare +0 -0
  42. package/prebuilds/darwin-x64/bare-ffmpeg.bare +0 -0
  43. package/prebuilds/ios-arm64/bare-ffmpeg.bare +0 -0
  44. package/prebuilds/ios-arm64-simulator/bare-ffmpeg.bare +0 -0
  45. package/prebuilds/ios-x64-simulator/bare-ffmpeg.bare +0 -0
  46. package/prebuilds/linux-arm64/bare-ffmpeg.bare +0 -0
  47. package/prebuilds/linux-x64/bare-ffmpeg.bare +0 -0
  48. package/prebuilds/win32-arm64/bare-ffmpeg.bare +0 -0
  49. package/prebuilds/win32-x64/bare-ffmpeg.bare +0 -0
  50. package/binding.c +0 -2203
  51. package/lib/reference-counted.js +0 -39
@@ -0,0 +1,47 @@
1
+ const binding = require('../binding')
2
+
3
+ module.exports = class FilterGraph {
4
+ constructor() {
5
+ this._handle = binding.initFilterGraph()
6
+ }
7
+
8
+ destroy() {
9
+ binding.destroyFilterGraph(this._handle)
10
+ this._handle = null
11
+ }
12
+
13
+ createFilter(context, filter, name, args) {
14
+ return binding.createFilterGraphFilter(
15
+ this._handle,
16
+ context._handle,
17
+ filter._handle,
18
+ name,
19
+ args ?? undefined
20
+ )
21
+ }
22
+
23
+ parse(filterDescription, inputs, outputs) {
24
+ binding.parseFilterGraph(
25
+ this._handle,
26
+ inputs._handle,
27
+ outputs._handle,
28
+ filterDescription
29
+ )
30
+ }
31
+
32
+ configure() {
33
+ binding.configureFilterGraph(this._handle)
34
+ }
35
+
36
+ pushFrame(ctx, frame) {
37
+ return binding.pushFilterGraphFrame(ctx._handle, frame._handle)
38
+ }
39
+
40
+ pullFrame(ctx, frame) {
41
+ return binding.pullFilterGraphFrame(ctx._handle, frame._handle)
42
+ }
43
+
44
+ [Symbol.dispose]() {
45
+ this.destroy()
46
+ }
47
+ }
@@ -0,0 +1,55 @@
1
+ const binding = require('../binding')
2
+
3
+ module.exports = class FilterInOut {
4
+ constructor(handle = binding.initFilterInout()) {
5
+ this._handle = handle
6
+ this._next = null
7
+ this._filterContext = null
8
+ }
9
+
10
+ destroy() {
11
+ binding.destroyFilterInOut(this._handle)
12
+ this._handle = null
13
+ }
14
+
15
+ get name() {
16
+ return binding.getFilterInOutName(this._handle)
17
+ }
18
+
19
+ set name(value) {
20
+ return binding.setFilterInOutName(this._handle, value)
21
+ }
22
+
23
+ get filterContext() {
24
+ return this._filterContext
25
+ }
26
+
27
+ set filterContext(value) {
28
+ this._filterContext = value
29
+ return binding.setFilterInOutFilterContext(
30
+ this._handle,
31
+ this._filterContext._handle
32
+ )
33
+ }
34
+
35
+ get padIdx() {
36
+ return binding.getFilterInOutPadIdx(this._handle)
37
+ }
38
+
39
+ set padIdx(value) {
40
+ return binding.setFilterInOutPadIdx(this._handle, value)
41
+ }
42
+
43
+ get next() {
44
+ return this._next
45
+ }
46
+
47
+ set next(value) {
48
+ this._next = value
49
+ return binding.setFilterInOutNext(this._handle, this._next._handle)
50
+ }
51
+
52
+ [Symbol.dispose]() {
53
+ this.destroy()
54
+ }
55
+ }
package/lib/filter.js ADDED
@@ -0,0 +1,7 @@
1
+ const binding = require('../binding')
2
+
3
+ module.exports = class Filter {
4
+ constructor(name) {
5
+ this._handle = binding.getFilterByName(name)
6
+ }
7
+ }
@@ -1,26 +1,22 @@
1
1
  const binding = require('../binding')
2
2
  const Stream = require('./stream.js')
3
- const ReferenceCounted = require('./reference-counted')
4
3
  const OutputFormat = require('./output-format')
5
4
  const IOContext = require('./io-context')
6
5
  const InputFormat = require('./input-format')
6
+ const Dictionary = require('./dictionary')
7
+ /** @typedef {import('./packet')} Packet */
7
8
 
8
- class FFmpegFormatContext extends ReferenceCounted {
9
+ class FFmpegFormatContext {
9
10
  constructor(io) {
10
- super()
11
-
12
- this._io = io ? io._ref() : null
11
+ this._io = io
13
12
  this._streams = []
14
13
  }
15
14
 
16
- _destroy() {
15
+ destroy() {
17
16
  if (this._io) {
18
- this._io._unref()
17
+ this._io.destroy()
19
18
  this._io = null
20
19
  }
21
-
22
- for (const stream of this._streams) stream.destroy()
23
- this._streams = []
24
20
  }
25
21
 
26
22
  get io() {
@@ -32,23 +28,24 @@ class FFmpegFormatContext extends ReferenceCounted {
32
28
  }
33
29
 
34
30
  readFrame(packet) {
35
- if (packet._refs !== 0) {
36
- throw new Error('Cannot read into packet with active references')
37
- }
31
+ return binding.readFormatContextFrame(this._handle, packet._handle)
32
+ }
38
33
 
39
- const result = binding.readFormatContextFrame(this._handle, packet._handle)
40
- if (result) packet._ref()
41
- return result
34
+ getBestStreamIndex(type) {
35
+ return binding.getFormatContextBestStreamIndex(this._handle, type)
42
36
  }
43
37
 
44
- getBestStream(type) {
45
- if (typeof type !== 'number') {
46
- throw new TypeError(
47
- `Type parameter should be of type number but got: ${typeof type}`
48
- )
38
+ getStream(index) {
39
+ if (index < 0 || index >= this._streams.length) {
40
+ return null
49
41
  }
50
42
 
51
- if (this._streams.length == 0) {
43
+ return this._streams[index]
44
+ }
45
+
46
+ /** @returns {Stream|null} */
47
+ getBestStream(type) {
48
+ if (this._streams.length === 0) {
52
49
  return null
53
50
  }
54
51
 
@@ -63,16 +60,23 @@ class FFmpegFormatContext extends ReferenceCounted {
63
60
 
64
61
  return this._streams[bestIndex]
65
62
  }
63
+
64
+ [Symbol.dispose]() {
65
+ this.destroy()
66
+ }
66
67
  }
67
68
 
68
69
  let defaultURL = null
69
70
 
70
71
  switch (Bare.platform) {
71
72
  case 'darwin':
72
- defaultURL = '0'
73
+ case 'ios':
74
+ defaultURL = '0:0'
73
75
  break
74
76
  case 'linux':
75
77
  // TODO: test on real machine
78
+ // we might need separate contexts for linux
79
+ // video: V4L2 and audio: ALSA/PulseAudio
76
80
  defaultURL = '/dev/video0'
77
81
  break
78
82
  case 'win32':
@@ -86,14 +90,20 @@ exports.InputFormatContext = class FFmpegInputFormatContext extends (
86
90
  constructor(io, options, url = defaultURL) {
87
91
  if (io instanceof IOContext) {
88
92
  super(io)
93
+
89
94
  this._handle = binding.openInputFormatContextWithIO(io._handle)
90
95
  } else if (io instanceof InputFormat) {
91
96
  super()
97
+
98
+ options = options || new Dictionary()
99
+
92
100
  this._handle = binding.openInputFormatContextWithFormat(
93
101
  io._handle,
94
102
  options._handle,
95
103
  url
96
104
  )
105
+
106
+ options.destroy()
97
107
  }
98
108
 
99
109
  for (const handle of binding.getFormatContextStreams(this._handle)) {
@@ -101,12 +111,21 @@ exports.InputFormatContext = class FFmpegInputFormatContext extends (
101
111
  }
102
112
  }
103
113
 
104
- _destroy() {
105
- super._destroy()
114
+ destroy() {
115
+ super.destroy()
106
116
 
107
117
  binding.closeInputFormatContext(this._handle)
108
118
  this._handle = null
109
119
  }
120
+
121
+ dump(printIdx = 0, printUrl = '') {
122
+ binding.dumpFormatContext(this._handle, false, printIdx, printUrl)
123
+ }
124
+
125
+ get inputFormat() {
126
+ const handle = binding.getFormatContextInputFormat(this._handle)
127
+ if (handle) return InputFormat.from(handle)
128
+ }
110
129
  }
111
130
 
112
131
  exports.OutputFormatContext = class FFmpegOutputFormatContext extends (
@@ -118,22 +137,44 @@ exports.OutputFormatContext = class FFmpegOutputFormatContext extends (
118
137
  if (typeof format === 'string') format = new OutputFormat(format)
119
138
 
120
139
  this._handle = binding.openOutputFormatContext(format._handle, io._handle)
140
+ this._isOutput = true
121
141
  }
122
142
 
123
- _destroy() {
124
- super._destroy()
143
+ destroy() {
144
+ super.destroy()
125
145
 
126
146
  binding.closeOutputFormatContext(this._handle)
127
147
  this._handle = null
128
148
  }
129
149
 
130
- createStream(codec) {
131
- const stream = new Stream(
132
- binding.createFormatContextStream(this._handle, codec._handle)
133
- )
150
+ createStream() {
151
+ const stream = new Stream(binding.createFormatContextStream(this._handle))
134
152
 
135
153
  this._streams.push(stream)
136
154
 
137
155
  return stream
138
156
  }
157
+
158
+ /** @param {Dictionary} [options] format options */
159
+ writeHeader(options) {
160
+ binding.writeFormatContextHeader(this._handle, options?._handle)
161
+ }
162
+
163
+ /** @param {Packet} packet */
164
+ writeFrame(packet) {
165
+ binding.writeFormatContextFrame(this._handle, packet._handle)
166
+ }
167
+
168
+ writeTrailer() {
169
+ binding.writeFormatContextTrailer(this._handle)
170
+ }
171
+
172
+ dump(printIdx = 0, printUrl = '') {
173
+ binding.dumpFormatContext(this._handle, true, printIdx, printUrl)
174
+ }
175
+
176
+ get outputFormat() {
177
+ const handle = binding.getFormatContextOutputFormat(this._handle)
178
+ if (handle) return OutputFormat.from(handle)
179
+ }
139
180
  }
package/lib/frame.js CHANGED
@@ -1,59 +1,125 @@
1
1
  const binding = require('../binding')
2
- const ReferenceCounted = require('./reference-counted')
2
+ const ChannelLayout = require('./channel-layout')
3
+ const Rational = require('./rational')
3
4
 
4
- module.exports = class FFmpegFrame extends ReferenceCounted {
5
+ module.exports = class FFmpegFrame {
5
6
  constructor() {
6
- super()
7
-
8
7
  this._handle = binding.initFrame()
9
8
  }
10
9
 
11
- _destroy() {
10
+ destroy() {
12
11
  binding.destroyFrame(this._handle)
13
12
  this._handle = null
14
13
  }
15
14
 
16
- channel(i) {
17
- if (i < 0 || i > 7) {
18
- throw new RangeError(`Channel index must be between 0 and 7`)
19
- }
15
+ unref() {
16
+ binding.unrefFrame(this._handle)
17
+ }
20
18
 
21
- return Buffer.from(binding.getFrameChannel(this._handle, i))
19
+ get width() {
20
+ return binding.getFrameWidth(this._handle)
22
21
  }
23
22
 
24
23
  set width(value) {
25
24
  binding.setFrameWidth(this._handle, value)
26
25
  }
27
26
 
28
- get width() {
29
- return binding.getFrameWidth(this._handle)
27
+ get height() {
28
+ return binding.getFrameHeight(this._handle)
30
29
  }
31
30
 
32
31
  set height(value) {
33
32
  binding.setFrameHeight(this._handle, value)
34
33
  }
35
34
 
36
- get height() {
37
- return binding.getFrameHeight(this._handle)
35
+ get format() {
36
+ return binding.getFrameFormat(this._handle)
37
+ }
38
+
39
+ set format(value) {
40
+ binding.setFrameFormat(this._handle, value)
41
+ }
42
+
43
+ get channelLayout() {
44
+ return new ChannelLayout(binding.getFrameChannelLayout(this._handle))
45
+ }
46
+
47
+ set channelLayout(value) {
48
+ binding.setFrameChannelLayout(
49
+ this._handle,
50
+ ChannelLayout.from(value)._handle
51
+ )
52
+ }
53
+
54
+ get nbSamples() {
55
+ return binding.getFrameNbSamples(this._handle)
38
56
  }
39
57
 
40
- set pixelFormat(value) {
41
- binding.setFramePixelFormat(this._handle, value)
58
+ set nbSamples(value) {
59
+ binding.setFrameNbSamples(this._handle, value)
42
60
  }
43
61
 
44
- get pixelFormat() {
45
- return binding.getFramePixelFormat(this._handle)
62
+ get pictType() {
63
+ return binding.getFramePictType(this._handle)
46
64
  }
47
65
 
48
- lineSize(channel) {
49
- return binding.getFrameLineSize(this._handle, channel)
66
+ get pts() {
67
+ return binding.getFramePTS(this._handle)
68
+ }
69
+
70
+ set pts(value) {
71
+ return binding.setFramePTS(this._handle, value)
72
+ }
73
+
74
+ get packetDTS() {
75
+ return binding.getFramePacketDTS(this._handle)
76
+ }
77
+
78
+ set packetDTS(value) {
79
+ return binding.setFramePacketDTS(this._handle, value)
80
+ }
81
+
82
+ get timeBase() {
83
+ const view = new Int32Array(binding.getFrameTimeBase(this._handle))
84
+ return new Rational(view[0], view[1])
85
+ }
86
+
87
+ set timeBase(value) {
88
+ binding.setFrameTimeBase(this._handle, value.numerator, value.denominator)
89
+ }
90
+
91
+ get sampleRate() {
92
+ return binding.getFrameSampleRate(this._handle)
93
+ }
94
+
95
+ set sampleRate(value) {
96
+ binding.setFrameSampleRate(this._handle, value)
97
+ }
98
+
99
+ copyProperties(source) {
100
+ binding.copyFrameProperties(this._handle, source._handle)
50
101
  }
51
102
 
52
103
  alloc() {
53
104
  binding.allocFrame(this._handle, 32)
54
105
  }
55
106
 
56
- get data() {
57
- return Buffer.from(binding.getFrameData(this._handle))
107
+ [Symbol.dispose]() {
108
+ this.destroy()
109
+ }
110
+
111
+ [Symbol.for('bare.inspect')]() {
112
+ return {
113
+ __proto__: { constructor: FFmpegFrame },
114
+ width: this.width,
115
+ height: this.height,
116
+ format: this.format,
117
+ channelLayout: this.channelLayout,
118
+ nbSamples: this.nbSamples,
119
+ pictType: this.pictType,
120
+ pts: this.pts,
121
+ packetDTS: this.packetDTS,
122
+ timeBase: this.timeBase
123
+ }
58
124
  }
59
125
  }
package/lib/image.js CHANGED
@@ -40,8 +40,30 @@ module.exports = class FFmpegImage {
40
40
  this._pixelFormat,
41
41
  this._width,
42
42
  this._height,
43
+ this._align,
43
44
  this._data.buffer,
45
+ this._data.byteOffset,
44
46
  frame._handle
45
47
  )
46
48
  }
49
+
50
+ read(frame) {
51
+ binding.readImage(
52
+ this._pixelFormat,
53
+ this._width,
54
+ this._height,
55
+ this._align,
56
+ this._data.buffer,
57
+ this._data.byteOffset,
58
+ frame._handle
59
+ )
60
+ }
61
+
62
+ lineSize(plane = 0) {
63
+ return binding.getImageLineSize(this._pixelFormat, this._width, plane)
64
+ }
65
+
66
+ static lineSize(pixelFormat, width, plane = 0) {
67
+ return binding.getImageLineSize(pixelFormat, width, plane)
68
+ }
47
69
  }
@@ -3,7 +3,11 @@ const binding = require('../binding')
3
3
  let defaultName = null
4
4
 
5
5
  switch (Bare.platform) {
6
+ case 'android':
7
+ defaultName = 'android_camera'
8
+ break
6
9
  case 'darwin':
10
+ case 'ios':
7
11
  defaultName = 'avfoundation'
8
12
  break
9
13
  case 'linux':
@@ -15,10 +19,38 @@ switch (Bare.platform) {
15
19
  }
16
20
 
17
21
  module.exports = class FFmpegInputFormat {
18
- constructor(name = defaultName) {
19
- if (typeof name !== 'string' || name.length < 1) {
20
- throw new TypeError('Input format name should be a non empty string')
22
+ constructor(name = defaultName, handle) {
23
+ if (handle) this._handle = handle
24
+ else this._handle = binding.initInputFormat(name)
25
+ }
26
+
27
+ static from(handle) {
28
+ return new FFmpegInputFormat(null, handle)
29
+ }
30
+
31
+ get flags() {
32
+ return binding.getInputFormatFlags(this._handle)
33
+ }
34
+
35
+ get extensions() {
36
+ return binding.getInputFormatExtensions(this._handle)
37
+ }
38
+
39
+ get mimeType() {
40
+ return binding.getInputFormatMimeType(this._handle)
41
+ }
42
+
43
+ get name() {
44
+ return binding.getInputFormatName(this._handle)
45
+ }
46
+
47
+ [Symbol.for('bare.inspect')]() {
48
+ return {
49
+ __proto__: { constructor: FFmpegInputFormat },
50
+ name: this.name,
51
+ flags: this.flags,
52
+ extensions: this.extensions,
53
+ mimeType: this.mimeType
21
54
  }
22
- this._handle = binding.initInputFormat(name)
23
55
  }
24
56
  }
package/lib/io-context.js CHANGED
@@ -1,15 +1,45 @@
1
1
  const binding = require('../binding')
2
- const ReferenceCounted = require('./reference-counted')
3
2
 
4
- module.exports = class FFmpegIOContext extends ReferenceCounted {
5
- constructor(buffer = Buffer.alloc(0)) {
6
- super()
3
+ module.exports = class FFmpegIOContext {
4
+ constructor(buffer, opts = {}) {
5
+ let offset = 0
6
+ let len = 0
7
7
 
8
- this._handle = binding.initIOContext(buffer)
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
+
19
+ this._handle = binding.initIOContext(
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
26
+ )
9
27
  }
10
28
 
11
- _destroy() {
29
+ destroy() {
12
30
  binding.destroyIOContext(this._handle)
13
31
  this._handle = null
14
32
  }
33
+
34
+ [Symbol.dispose]() {
35
+ this.destroy()
36
+ }
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)
15
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
  }