bare-ffmpeg 1.0.0-25 → 1.0.0-27

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/index.js CHANGED
@@ -10,6 +10,10 @@ const {
10
10
  InputFormatContext,
11
11
  OutputFormatContext
12
12
  } = require('./lib/format-context')
13
+ const Filter = require('./lib/filter')
14
+ const FilterContext = require('./lib/filter-context')
15
+ const FilterGraph = require('./lib/filter-graph')
16
+ const FilterInOut = require('./lib/filter-inout')
13
17
  const Frame = require('./lib/frame')
14
18
  const IOContext = require('./lib/io-context')
15
19
  const Image = require('./lib/image')
@@ -31,6 +35,10 @@ exports.CodecParameters = CodecParameters
31
35
  exports.Decoder = Decoder
32
36
  exports.Dictionary = Dictionary
33
37
  exports.Encoder = Encoder
38
+ exports.Filter = Filter
39
+ exports.FilterContext = FilterContext
40
+ exports.FilterGraph = FilterGraph
41
+ exports.FilterInOut = FilterInOut
34
42
  exports.Frame = Frame
35
43
  exports.IOContext = IOContext
36
44
  exports.Image = Image
@@ -1,6 +1,7 @@
1
1
  const binding = require('../binding')
2
2
  const Rational = require('./rational')
3
3
  const ChannelLayout = require('./channel-layout')
4
+ const { codecConfig } = require('./constants')
4
5
 
5
6
  module.exports = class FFmpegCodecContext {
6
7
  constructor(codec) {
@@ -107,6 +108,27 @@ module.exports = class FFmpegCodecContext {
107
108
  binding.setCodecContextFlags(this._handle, value)
108
109
  }
109
110
 
111
+ get extraData() {
112
+ return Buffer.from(binding.getCodecContextExtraData(this._handle))
113
+ }
114
+
115
+ set extraData(value) {
116
+ binding.setCodecContextExtraData(
117
+ this._handle,
118
+ value.buffer,
119
+ value.byteOffset,
120
+ value.byteLength
121
+ )
122
+ }
123
+
124
+ get frameSize() {
125
+ return binding.getCodecContextFrameSize(this._handle)
126
+ }
127
+
128
+ get frameNum() {
129
+ return binding.getCodecContextFrameNum(this._handle)
130
+ }
131
+
110
132
  open(options) {
111
133
  if (this._opened) return
112
134
  this._opened = true
@@ -138,6 +160,45 @@ module.exports = class FFmpegCodecContext {
138
160
  return binding.receiveCodecContextFrame(this._handle, frame._handle)
139
161
  }
140
162
 
163
+ getSupportedConfig(config) {
164
+ if (config === codecConfig.FRAME_RATE) {
165
+ const data = binding.getSupportedFrameRates(
166
+ this._handle,
167
+ this._codec._handle
168
+ )
169
+ if (!data || data.length === 0) return null
170
+
171
+ const view = new Int32Array(data)
172
+ const rates = []
173
+ for (let i = 0; i < view.length; i += 2) {
174
+ rates.push(new Rational(view[i], view[i + 1]))
175
+ }
176
+ return rates
177
+ }
178
+
179
+ if (config === codecConfig.CHANNEL_LAYOUT) {
180
+ const handles = binding.getSupportedChannelLayouts(
181
+ this._handle,
182
+ this._codec._handle
183
+ )
184
+ if (!handles || handles.length === 0) return null
185
+
186
+ return handles.map((handle) => new ChannelLayout(handle))
187
+ }
188
+
189
+ const data = binding.getSupportedConfig(
190
+ this._handle,
191
+ this._codec._handle,
192
+ config
193
+ )
194
+
195
+ if (config === codecConfig.SAMPLE_RATE && !data) {
196
+ return null
197
+ }
198
+
199
+ return new Int32Array(data)
200
+ }
201
+
141
202
  [Symbol.dispose]() {
142
203
  this.destroy()
143
204
  }
@@ -155,7 +216,8 @@ module.exports = class FFmpegCodecContext {
155
216
  sampleRate: this.sampleRate,
156
217
  timeBase: this.timeBase,
157
218
  channelLayout: this.channelLayout,
158
- gopSize: this.gopSize
219
+ gopSize: this.gopSize,
220
+ extraData: this.extraData
159
221
  }
160
222
  }
161
223
  }
@@ -72,6 +72,14 @@ module.exports = class FFmpegCodecParameters {
72
72
  )
73
73
  }
74
74
 
75
+ get frameSize() {
76
+ return binding.getCodecParametersFrameSize(this._handle)
77
+ }
78
+
79
+ set frameSize(size) {
80
+ binding.setCodecParametersFrameSize(this._handle, size)
81
+ }
82
+
75
83
  get videoDelay() {
76
84
  return binding.getCodecParametersVideoDelay(this._handle)
77
85
  }
@@ -235,22 +243,26 @@ module.exports = class FFmpegCodecParameters {
235
243
  type: this.type,
236
244
  id: this.id,
237
245
  format: this.format,
246
+ tag: this.tag,
247
+ frameRate: this.frameRate,
248
+ videoDelay: this.videoDelay,
238
249
  profile: this.profile,
239
250
  level: this.level,
240
251
  width: this.width,
241
252
  height: this.height,
253
+ sampleAsectRatio: this.sampleAspectRatio,
242
254
  bitRate: this.bitRate,
243
255
  bitsPerCodedSample: this.bitsPerCodedSample,
244
256
  bitsPerRawSample: this.bitsPerRawSample,
245
257
  sampleRate: this.sampleRate,
246
258
  nbChannels: this.nbChannels,
247
259
  channelLayout: this.channelLayout,
248
- frameRate: this.frameRate,
249
260
  extraData: this.extraData,
250
261
  blockAlign: this.blockAlign,
251
262
  initialPadding: this.initialPadding,
252
263
  trailingPadding: this.trailingPadding,
253
- seekPreroll: this.seekPreroll
264
+ seekPreroll: this.seekPreroll,
265
+ frameSize: this.frameSize
254
266
  }
255
267
  }
256
268
 
package/lib/constants.js CHANGED
@@ -140,6 +140,59 @@ module.exports = exports = {
140
140
  CUR: binding.SEEK_CUR,
141
141
  SET: binding.SEEK_SET,
142
142
  END: binding.SEEK_END
143
+ },
144
+ packetSideDataType: {
145
+ PALETTE: binding.AV_PKT_DATA_PALETTE,
146
+ NEW_EXTRADATA: binding.AV_PKT_DATA_NEW_EXTRADATA,
147
+ PARAM_CHANGE: binding.AV_PKT_DATA_PARAM_CHANGE,
148
+ H263_MB_INFO: binding.AV_PKT_DATA_H263_MB_INFO,
149
+ REPLAYGAIN: binding.AV_PKT_DATA_REPLAYGAIN,
150
+ DISPLAYMATRIX: binding.AV_PKT_DATA_DISPLAYMATRIX,
151
+ STEREO3D: binding.AV_PKT_DATA_STEREO3D,
152
+ AUDIO_SERVICE_TYPE: binding.AV_PKT_DATA_AUDIO_SERVICE_TYPE,
153
+ QUALITY_STATS: binding.AV_PKT_DATA_QUALITY_STATS,
154
+ FALLBACK_TRACK: binding.AV_PKT_DATA_FALLBACK_TRACK,
155
+ CPB_PROPERTIES: binding.AV_PKT_DATA_CPB_PROPERTIES,
156
+ SKIP_SAMPLES: binding.AV_PKT_DATA_SKIP_SAMPLES,
157
+ JP_DUALMONO: binding.AV_PKT_DATA_JP_DUALMONO,
158
+ STRINGS_METADATA: binding.AV_PKT_DATA_STRINGS_METADATA,
159
+ SUBTITLE_POSITION: binding.AV_PKT_DATA_SUBTITLE_POSITION,
160
+ MATROSKA_BLOCKADDITIONAL: binding.AV_PKT_DATA_MATROSKA_BLOCKADDITIONAL,
161
+ WEBVTT_IDENTIFIER: binding.AV_PKT_DATA_WEBVTT_IDENTIFIER,
162
+ WEBVTT_SETTINGS: binding.AV_PKT_DATA_WEBVTT_SETTINGS,
163
+ METADATA_UPDATE: binding.AV_PKT_DATA_METADATA_UPDATE,
164
+ MPEGTS_STREAM_ID: binding.AV_PKT_DATA_MPEGTS_STREAM_ID,
165
+ MASTERING_DISPLAY_METADATA: binding.AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
166
+ SPHERICAL: binding.AV_PKT_DATA_SPHERICAL,
167
+ CONTENT_LIGHT_LEVEL: binding.AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
168
+ A53_CC: binding.AV_PKT_DATA_A53_CC,
169
+ ENCRYPTION_INIT_INFO: binding.AV_PKT_DATA_ENCRYPTION_INIT_INFO,
170
+ ENCRYPTION_INFO: binding.AV_PKT_DATA_ENCRYPTION_INFO,
171
+ AFD: binding.AV_PKT_DATA_AFD,
172
+ PRFT: binding.AV_PKT_DATA_PRFT,
173
+ ICC_PROFILE: binding.AV_PKT_DATA_ICC_PROFILE,
174
+ DOVI_CONF: binding.AV_PKT_DATA_DOVI_CONF,
175
+ S12M_TIMECODE: binding.AV_PKT_DATA_S12M_TIMECODE,
176
+ DYNAMIC_HDR10_PLUS: binding.AV_PKT_DATA_DYNAMIC_HDR10_PLUS,
177
+ IAMF_MIX_GAIN_PARAM: binding.AV_PKT_DATA_IAMF_MIX_GAIN_PARAM,
178
+ IAMF_DEMIXING_INFO_PARAM: binding.AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM,
179
+ IAMF_RECON_GAIN_INFO_PARAM: binding.AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM,
180
+ AMBIENT_VIEWING_ENVIRONMENT:
181
+ binding.AV_PKT_DATA_AMBIENT_VIEWING_ENVIRONMENT,
182
+ FRAME_CROPPING: binding.AV_PKT_DATA_FRAME_CROPPING,
183
+ LCEVC: binding.AV_PKT_DATA_LCEVC,
184
+ '3D_REFERENCE_DISPLAYS': binding.AV_PKT_DATA_3D_REFERENCE_DISPLAYS,
185
+ RTCP_SR: binding.AV_PKT_DATA_RTCP_SR,
186
+ NB: binding.AV_PKT_DATA_NB
187
+ },
188
+ codecConfig: {
189
+ PIX_FORMAT: binding.AV_CODEC_CONFIG_PIX_FORMAT,
190
+ FRAME_RATE: binding.AV_CODEC_CONFIG_FRAME_RATE,
191
+ SAMPLE_RATE: binding.AV_CODEC_CONFIG_SAMPLE_RATE,
192
+ SAMPLE_FORMAT: binding.AV_CODEC_CONFIG_SAMPLE_FORMAT,
193
+ CHANNEL_LAYOUT: binding.AV_CODEC_CONFIG_CHANNEL_LAYOUT,
194
+ COLOR_RANGE: binding.AV_CODEC_CONFIG_COLOR_RANGE,
195
+ COLOR_SPACE: binding.AV_CODEC_CONFIG_COLOR_SPACE
143
196
  }
144
197
  }
145
198
 
@@ -190,3 +243,7 @@ exports.toChannelLayout = function toChannelLayout(layout) {
190
243
  `Channel layout must be a number or string. Received ${typeof layout} (${layout})`
191
244
  )
192
245
  }
246
+
247
+ exports.getSampleFormatName = function (sampleFormat) {
248
+ return binding.getSampleFormatNameByID(sampleFormat)
249
+ }
@@ -0,0 +1,7 @@
1
+ const binding = require('../binding')
2
+
3
+ module.exports = class FilterContext {
4
+ constructor() {
5
+ this._handle = binding.initFilterContext()
6
+ }
7
+ }
@@ -0,0 +1,54 @@
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
+ const argumentsString =
15
+ args &&
16
+ `video_size=${args.width}x${args.height}` +
17
+ `:pix_fmt=${args.pixelFormat}` +
18
+ `:time_base=${args.timeBase.numerator}/${args.timeBase.denominator}` +
19
+ `:pixel_aspect=${args.aspectRatio.numerator}/${args.aspectRatio.denominator}`
20
+
21
+ return binding.createFilterGraphFilter(
22
+ this._handle,
23
+ context._handle,
24
+ filter._handle,
25
+ name,
26
+ argumentsString
27
+ )
28
+ }
29
+
30
+ parse(filterDescription, inputs, outputs) {
31
+ binding.parseFilterGraph(
32
+ this._handle,
33
+ inputs._handle,
34
+ outputs._handle,
35
+ filterDescription
36
+ )
37
+ }
38
+
39
+ configure() {
40
+ binding.configureFilterGraph(this._handle)
41
+ }
42
+
43
+ pushFrame(ctx, frame) {
44
+ return binding.pushFilterGraphFrame(ctx._handle, frame._handle)
45
+ }
46
+
47
+ pullFrame(ctx, frame) {
48
+ return binding.pullFilterGraphFrame(ctx._handle, frame._handle)
49
+ }
50
+
51
+ [Symbol.dispose]() {
52
+ this.destroy()
53
+ }
54
+ }
@@ -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
+ }
package/lib/packet.js CHANGED
@@ -1,6 +1,42 @@
1
1
  const binding = require('../binding')
2
2
  const Rational = require('./rational')
3
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
+ }
39
+
4
40
  module.exports = class FFmpegPacket {
5
41
  constructor(buffer) {
6
42
  if (buffer) {
@@ -44,10 +80,31 @@ module.exports = class FFmpegPacket {
44
80
  )
45
81
  }
46
82
 
83
+ get sideData() {
84
+ const handles = binding.getPacketSideData(this._handle)
85
+ return handles.map((handle) => new SideData(handle))
86
+ }
87
+
88
+ set sideData(values) {
89
+ binding.setPacketSideData(
90
+ this._handle,
91
+ values.map((value) => ({
92
+ buffer: value.data.buffer,
93
+ offset: value.data.byteOffset,
94
+ length: value.data.byteLength,
95
+ type: value.type
96
+ }))
97
+ )
98
+ }
99
+
47
100
  get isKeyframe() {
48
101
  return binding.isPacketKeyframe(this._handle)
49
102
  }
50
103
 
104
+ set isKeyframe(value) {
105
+ binding.setPacketIsKeyFrame(this._handle, value)
106
+ }
107
+
51
108
  get dts() {
52
109
  return binding.getPacketDTS(this._handle)
53
110
  }
@@ -81,6 +138,10 @@ module.exports = class FFmpegPacket {
81
138
  )
82
139
  }
83
140
 
141
+ copyPropsFrom(sourcePacket) {
142
+ binding.copyPacketProps(this._handle, sourcePacket._handle)
143
+ }
144
+
84
145
  get duration() {
85
146
  return binding.getPacketDuration(this._handle)
86
147
  }
@@ -107,7 +168,8 @@ module.exports = class FFmpegPacket {
107
168
  dts: this.dts,
108
169
  pts: this.pts,
109
170
  duration: this.duration,
110
- data: this.data
171
+ data: this.data,
172
+ sideData: this.sideData
111
173
  }
112
174
  }
113
175
 
@@ -115,3 +177,5 @@ module.exports = class FFmpegPacket {
115
177
  this.destroy()
116
178
  }
117
179
  }
180
+
181
+ module.exports.SideData = SideData
package/lib/rational.js CHANGED
@@ -26,15 +26,27 @@ module.exports = class FFmpegRational {
26
26
  return this.numerator / this.denominator
27
27
  }
28
28
 
29
+ equals(other) {
30
+ return (
31
+ this.numerator === other.numerator &&
32
+ this.denominator === other.denominator
33
+ )
34
+ }
35
+
29
36
  static from(num) {
30
37
  const view = new Int32Array(binding.rationalD2Q(num))
31
38
  return new FFmpegRational(view[0], view[1])
32
39
  }
33
40
 
34
41
  static rescaleQ(n, src, dst) {
35
- return (
36
- (n * (src.numerator / src.denominator)) /
37
- (dst.numerator / dst.denominator)
42
+ if (src.equals(dst)) return n
43
+
44
+ return binding.rationalRescaleQ(
45
+ n,
46
+ src.numerator,
47
+ src.denominator,
48
+ dst.numerator,
49
+ dst.denominator
38
50
  )
39
51
  }
40
52
  }
package/lib/samples.js CHANGED
@@ -1,49 +1,64 @@
1
1
  const binding = require('../binding')
2
- const constants = require('./constants.js')
3
2
 
4
3
  module.exports = class FFmpegSamples {
5
- constructor(sampleFormat, nbChannels, nbSamples, align = 0) {
6
- sampleFormat = constants.toSampleFormat(sampleFormat)
4
+ _data
5
+ _frame
7
6
 
8
- this._sampleFormat = sampleFormat
9
- this._nbChannels = nbChannels
10
- this._nbSamples = nbSamples
11
- this._align = align
7
+ constructor({ noAlignment = false } = {}) {
8
+ this._noAlignment = noAlignment
9
+ }
12
10
 
13
- this._data = Buffer.from(
14
- binding.initSamples(sampleFormat, nbChannels, nbSamples, align)
15
- )
11
+ get data() {
12
+ return this._data
16
13
  }
17
14
 
18
- get sampleFormat() {
19
- return this._sampleFormat
15
+ get format() {
16
+ return this._frame.format
20
17
  }
21
18
 
22
- get nbChannels() {
23
- return this._nbChannels
19
+ get channelLayout() {
20
+ return this._frame.channelLayout
24
21
  }
25
22
 
26
23
  get nbSamples() {
27
- return this._nbSamples
24
+ return this._frame.nbSamples
28
25
  }
29
26
 
30
- get align() {
31
- return this._align
27
+ get nbChannels() {
28
+ return this.channelLayout && this.channelLayout.nbChannels
32
29
  }
33
30
 
34
- get data() {
35
- return this._data
31
+ get pts() {
32
+ return this._frame.pts
36
33
  }
37
34
 
38
35
  fill(frame) {
39
- binding.fillSamples(
40
- this._sampleFormat,
41
- this._nbChannels,
42
- this._nbSamples,
43
- this._align,
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,
44
51
  this._data.buffer,
45
52
  this._data.byteOffset,
46
- frame._handle
53
+ this._noAlignment
47
54
  )
55
+
56
+ this._frame = frame
57
+
58
+ return res
59
+ }
60
+
61
+ static bufferSize(format, nbChannels, nbSamples, noAlignment = false) {
62
+ return binding.samplesBufferSize(format, nbChannels, nbSamples, noAlignment)
48
63
  }
49
64
  }
package/lib/stream.js CHANGED
@@ -66,4 +66,15 @@ module.exports = class FFmpegStream {
66
66
  this._codecParameters.toContext(context)
67
67
  return context.open()
68
68
  }
69
+
70
+ [Symbol.for('bare.inspect')]() {
71
+ return {
72
+ __proto__: { constructor: FFmpegStream },
73
+ id: this.id,
74
+ index: this.index,
75
+ timeBase: this.timeBase,
76
+ avgFramerate: this.avgFramerate,
77
+ codecParameters: this.codecParameters
78
+ }
79
+ }
69
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-ffmpeg",
3
- "version": "1.0.0-25",
3
+ "version": "1.0.0-27",
4
4
  "description": "Low-level FFmpeg bindings for Bare",
5
5
  "exports": {
6
6
  ".": "./index.js",
@@ -18,7 +18,8 @@
18
18
  "addon": true,
19
19
  "scripts": {
20
20
  "test": "prettier . --check && bare test.js",
21
- "format": "prettier . --write && clang-format -i binding.cc"
21
+ "format": "prettier . --write && clang-format -i binding.cc",
22
+ "example": "bare ./examples/index.js"
22
23
  },
23
24
  "repository": {
24
25
  "type": "git",
@@ -31,6 +32,7 @@
31
32
  },
32
33
  "homepage": "https://github.com/holepunchto/bare-ffmpeg#readme",
33
34
  "devDependencies": {
35
+ "bare-assert": "^1.1.0",
34
36
  "brittle": "^3.4.0",
35
37
  "cmake-bare": "^1.1.2",
36
38
  "cmake-harden": "^1.0.2",