bare-ffmpeg 1.0.0-2 → 1.0.0-21

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 (47) hide show
  1. package/CMakeLists.txt +10 -3
  2. package/README.md +1097 -0
  3. package/binding.cc +3330 -0
  4. package/cmake/ports/ffmpeg/port.cmake +513 -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 +14 -4
  11. package/lib/audio-fifo.js +47 -0
  12. package/lib/channel-layout.js +35 -0
  13. package/lib/codec-context.js +92 -15
  14. package/lib/codec-parameters.js +230 -8
  15. package/lib/codec.js +19 -0
  16. package/lib/constants.js +163 -8
  17. package/lib/dictionary.js +18 -15
  18. package/lib/errors.js +38 -0
  19. package/lib/format-context.js +62 -31
  20. package/lib/frame.js +87 -16
  21. package/lib/image.js +22 -0
  22. package/lib/input-format.js +8 -3
  23. package/lib/io-context.js +29 -6
  24. package/lib/log.js +16 -0
  25. package/lib/output-format.js +4 -0
  26. package/lib/packet.js +88 -13
  27. package/lib/rational.js +35 -1
  28. package/lib/resampler.js +63 -0
  29. package/lib/samples.js +49 -0
  30. package/lib/scaler.js +8 -7
  31. package/lib/stream.js +37 -12
  32. package/package.json +7 -4
  33. package/prebuilds/android-arm/bare-ffmpeg.bare +0 -0
  34. package/prebuilds/android-arm64/bare-ffmpeg.bare +0 -0
  35. package/prebuilds/android-ia32/bare-ffmpeg.bare +0 -0
  36. package/prebuilds/android-x64/bare-ffmpeg.bare +0 -0
  37. package/prebuilds/darwin-arm64/bare-ffmpeg.bare +0 -0
  38. package/prebuilds/darwin-x64/bare-ffmpeg.bare +0 -0
  39. package/prebuilds/ios-arm64/bare-ffmpeg.bare +0 -0
  40. package/prebuilds/ios-arm64-simulator/bare-ffmpeg.bare +0 -0
  41. package/prebuilds/ios-x64-simulator/bare-ffmpeg.bare +0 -0
  42. package/prebuilds/linux-arm64/bare-ffmpeg.bare +0 -0
  43. package/prebuilds/linux-x64/bare-ffmpeg.bare +0 -0
  44. package/prebuilds/win32-arm64/bare-ffmpeg.bare +0 -0
  45. package/prebuilds/win32-x64/bare-ffmpeg.bare +0 -0
  46. package/binding.c +0 -2128
  47. package/lib/reference-counted.js +0 -39
package/lib/codec.js CHANGED
@@ -1,6 +1,7 @@
1
1
  const Decoder = require('./decoder')
2
2
  const Encoder = require('./encoder')
3
3
  const constants = require('./constants')
4
+ const binding = require('../binding')
4
5
 
5
6
  const codecs = new Map()
6
7
 
@@ -25,6 +26,21 @@ module.exports = class FFmpegCodec {
25
26
  return this._encoder
26
27
  }
27
28
 
29
+ get name() {
30
+ if (Number.isInteger(this._id)) {
31
+ return binding.getCodecNameByID(this._id)
32
+ }
33
+ }
34
+
35
+ [Symbol.for('bare.inspect')]() {
36
+ return {
37
+ __proto__: { constructor: FFmpegCodec },
38
+ id: this.id,
39
+ name: this.name
40
+ }
41
+ }
42
+
43
+ /** @return {FFmpegCodec} */
28
44
  static for(id) {
29
45
  let codec = codecs.get(id)
30
46
  if (codec === undefined) {
@@ -36,4 +52,7 @@ module.exports = class FFmpegCodec {
36
52
 
37
53
  static MJPEG = this.for(constants.codecs.MJPEG)
38
54
  static H264 = this.for(constants.codecs.H264)
55
+ static AAC = this.for(constants.codecs.AAC)
56
+ static OPUS = this.for(constants.codecs.OPUS)
57
+ static AV1 = this.for(constants.codecs.AV1)
39
58
  }
package/lib/constants.js CHANGED
@@ -1,9 +1,42 @@
1
1
  const binding = require('../binding')
2
+ const errors = require('./errors')
3
+
4
+ // Helpers
5
+ function makeTag(a, b, c, d) {
6
+ return (
7
+ a.charCodeAt(0) |
8
+ (b.charCodeAt(0) << 8) |
9
+ (c.charCodeAt(0) << 16) |
10
+ (d.charCodeAt(0) << 24)
11
+ )
12
+ }
2
13
 
3
14
  module.exports = exports = {
4
15
  codecs: {
5
16
  MJPEG: binding.AV_CODEC_ID_MJPEG,
6
- H264: binding.AV_CODEC_ID_H264
17
+ H264: binding.AV_CODEC_ID_H264,
18
+ AAC: binding.AV_CODEC_ID_AAC,
19
+ OPUS: binding.AV_CODEC_ID_OPUS,
20
+ AV1: binding.AV_CODEC_ID_AV1,
21
+ FLAC: binding.AV_CODEC_ID_FLAC,
22
+ MP3: binding.AV_CODEC_ID_MP3
23
+ },
24
+ tags: {
25
+ // Source Ref:
26
+ // https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/riff.c#L36
27
+ // https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/riff.c#L530
28
+ MJPEG: makeTag('M', 'J', 'P', 'G'),
29
+ H264: makeTag('H', '2', '6', '4'),
30
+ AV1: makeTag('A', 'V', '0', '1'),
31
+ AAC: 0x00ff,
32
+ FLAC: 0xf1ac,
33
+ MP3: 0x0055
34
+ },
35
+ profiles: {
36
+ H264_MAIN: binding.AV_PROFILE_H264_MAIN
37
+ },
38
+ levels: {
39
+ UNKNOWN: binding.AV_LEVEL_UNKNOWN
7
40
  },
8
41
  pixelFormats: {
9
42
  RGBA: binding.AV_PIX_FMT_RGBA,
@@ -12,7 +45,7 @@ module.exports = exports = {
12
45
  UYVY422: binding.AV_PIX_FMT_UYVY422,
13
46
  YUV420P: binding.AV_PIX_FMT_YUV420P
14
47
  },
15
- mediaType: {
48
+ mediaTypes: {
16
49
  UNKNOWN: binding.AVMEDIA_TYPE_UNKNOWN,
17
50
  VIDEO: binding.AVMEDIA_TYPE_VIDEO,
18
51
  AUDIO: binding.AVMEDIA_TYPE_AUDIO,
@@ -20,17 +53,139 @@ module.exports = exports = {
20
53
  SUBTITLE: binding.AVMEDIA_TYPE_SUBTITLE,
21
54
  ATTACHEMENT: binding.AVMEDIA_TYPE_ATTACHMENT,
22
55
  NB: binding.AVMEDIA_TYPE_NB
56
+ },
57
+ sampleFormats: {
58
+ NONE: binding.AV_SAMPLE_FMT_NONE,
59
+ U8: binding.AV_SAMPLE_FMT_U8,
60
+ S16: binding.AV_SAMPLE_FMT_S16,
61
+ S32: binding.AV_SAMPLE_FMT_S32,
62
+ S64: binding.AV_SAMPLE_FMT_S64,
63
+ FLT: binding.AV_SAMPLE_FMT_FLT,
64
+ DBL: binding.AV_SAMPLE_FMT_DBL,
65
+ U8P: binding.AV_SAMPLE_FMT_U8P,
66
+ S16P: binding.AV_SAMPLE_FMT_S16P,
67
+ S32P: binding.AV_SAMPLE_FMT_S32P,
68
+ S64P: binding.AV_SAMPLE_FMT_S64P,
69
+ FLTP: binding.AV_SAMPLE_FMT_FLTP,
70
+ DBLP: binding.AV_SAMPLE_FMT_DBLP,
71
+ NB: binding.AV_SAMPLE_FMT_NB
72
+ },
73
+ channelLayouts: {
74
+ MONO: binding.AV_CH_LAYOUT_MONO,
75
+ STEREO: binding.AV_CH_LAYOUT_STEREO,
76
+ QUAD: binding.AV_CH_LAYOUT_QUAD,
77
+ SURROUND: binding.AV_CH_LAYOUT_SURROUND,
78
+ 2_1: binding.AV_CH_LAYOUT_2POINT1,
79
+ 5_0: binding.AV_CH_LAYOUT_5POINT0,
80
+ 5_1: binding.AV_CH_LAYOUT_5POINT1,
81
+ 7_1: binding.AV_CH_LAYOUT_7POINT1,
82
+ // Aliases
83
+ 2.1: binding.AV_CH_LAYOUT_2POINT1,
84
+ '5.0': binding.AV_CH_LAYOUT_5POINT0,
85
+ 5.1: binding.AV_CH_LAYOUT_5POINT1,
86
+ 7.1: binding.AV_CH_LAYOUT_7POINT1
87
+ },
88
+ pictureTypes: {
89
+ NONE: binding.AV_PICTURE_TYPE_NONE,
90
+ I: binding.AV_PICTURE_TYPE_I,
91
+ P: binding.AV_PICTURE_TYPE_P,
92
+ B: binding.AV_PICTURE_TYPE_B,
93
+ S: binding.AV_PICTURE_TYPE_S,
94
+ SI: binding.AV_PICTURE_TYPE_SI,
95
+ SP: binding.AV_PICTURE_TYPE_SP,
96
+ BI: binding.AV_PICTURE_TYPE_BI
97
+ },
98
+ logLevels: {
99
+ QUIET: binding.AV_LOG_QUIET,
100
+ PANIC: binding.AV_LOG_PANIC,
101
+ FATAL: binding.AV_LOG_FATAL,
102
+ ERROR: binding.AV_LOG_ERROR,
103
+ WARNING: binding.AV_LOG_WARNING,
104
+ INFO: binding.AV_LOG_INFO,
105
+ VERBOSE: binding.AV_LOG_VERBOSE,
106
+ DEBUG: binding.AV_LOG_DEBUG,
107
+ TRACE: binding.AV_LOG_TRACE
108
+ },
109
+ codecFlags: {
110
+ COPY_OPAQUE: binding.AV_CODEC_FLAG_COPY_OPAQUE,
111
+ FRAME_DURATION: binding.AV_CODEC_FLAG_FRAME_DURATION,
112
+ PASS1: binding.AV_CODEC_FLAG_PASS1,
113
+ PASS2: binding.AV_CODEC_FLAG_PASS2,
114
+ LOOP_FILTER: binding.AV_CODEC_FLAG_LOOP_FILTER,
115
+ GRAY: binding.AV_CODEC_FLAG_GRAY,
116
+ PSNR: binding.AV_CODEC_FLAG_PSNR,
117
+ INTERLACED_DCT: binding.AV_CODEC_FLAG_INTERLACED_DCT,
118
+ LOW_DELAY: binding.AV_CODEC_FLAG_LOW_DELAY,
119
+ GLOBAL_HEADER: binding.AV_CODEC_FLAG_GLOBAL_HEADER,
120
+ BITEXACT: binding.AV_CODEC_FLAG_BITEXACT,
121
+ AC_PRED: binding.AV_CODEC_FLAG_AC_PRED,
122
+ INTERLACED_ME: binding.AV_CODEC_FLAG_INTERLACED_ME,
123
+ CLOSED_GOP: binding.AV_CODEC_FLAG_CLOSED_GOP
124
+ },
125
+ formatFlags: {
126
+ SHOW_IDS: binding.AVFMT_SHOW_IDS,
127
+ GENERIC_INDEX: binding.AVFMT_GENERIC_INDEX,
128
+ TS_DISCONT: binding.AVFMT_TS_DISCONT,
129
+ NOBINSEARCH: binding.AVFMT_NOBINSEARCH,
130
+ NOGENSEARCH: binding.AVFMT_NOGENSEARCH,
131
+ NO_BYTE_SEEK: binding.AVFMT_NO_BYTE_SEEK,
132
+ SEEK_TO_PTS: binding.AVFMT_SEEK_TO_PTS,
133
+ GLOBALHEADER: binding.AVFMT_GLOBALHEADER,
134
+ VARIABLE_FPS: binding.AVFMT_VARIABLE_FPS,
135
+ NODIMENSIONS: binding.AVFMT_NODIMENSIONS,
136
+ NOSTREAMS: binding.AVFMT_NOSTREAMS,
137
+ TS_NONSTRICT: binding.AVFMT_TS_NONSTRICT,
138
+ TS_NEGATIVE: binding.AVFMT_TS_NEGATIVE,
139
+ NOFILE: binding.AVFMT_NOFILE,
140
+ NEEDNUMBER: binding.AVFMT_NEEDNUMBER,
141
+ NOTIMESTAMPS: binding.AVFMT_NOTIMESTAMPS
142
+ }
143
+ }
144
+
145
+ exports.toPixelFormat = function toPixelFormat(format) {
146
+ if (typeof format === 'number') return format
147
+
148
+ if (typeof format === 'string') {
149
+ if (format in exports.pixelFormats === false) {
150
+ throw errors.UNKNOWN_PIXEL_FORMAT(`Unknown pixel format '${format}'`)
151
+ }
152
+
153
+ return exports.pixelFormats[format]
23
154
  }
155
+
156
+ throw new TypeError(
157
+ `Pixel format must be a number or string. Received ${typeof format} (${format})`
158
+ )
24
159
  }
25
160
 
26
- exports.toPixelFormat = function toPixelFormat(pixelFormat) {
27
- if (typeof pixelFormat === 'number') return pixelFormat
161
+ exports.toSampleFormat = function toSampleFormat(format) {
162
+ if (typeof format === 'number') return format
28
163
 
29
- if (typeof pixelFormat === 'string') {
30
- if (pixelFormat in exports.pixelFormats === false) {
31
- throw errors.UNKNOWN_PIXEL_FORMAT(`Unknown pixel format '${pixelFormat}'`)
164
+ if (typeof format === 'string') {
165
+ if (format in exports.sampleFormats === false) {
166
+ throw errors.UNKNOWN_SAMPLE_FORMAT(`Unknown sample format '${format}'`)
32
167
  }
33
168
 
34
- return exports.pixelFormats[pixelFormat]
169
+ return exports.sampleFormats[format]
35
170
  }
171
+
172
+ throw new TypeError(
173
+ `Sample format must be a number or string. Received ${typeof format} (${format})`
174
+ )
175
+ }
176
+
177
+ exports.toChannelLayout = function toChannelLayout(layout) {
178
+ if (typeof layout === 'number') return layout
179
+
180
+ if (typeof layout === 'string') {
181
+ if (layout in exports.channelLayouts === false) {
182
+ throw errors.UNKNOWN_CHANNEL_LAYOUT(`Unknown channel layout '${layout}'`)
183
+ }
184
+
185
+ return exports.channelLayouts[layout]
186
+ }
187
+
188
+ throw new TypeError(
189
+ `Channel layout must be a number or string. Received ${typeof layout} (${layout})`
190
+ )
36
191
  }
package/lib/dictionary.js CHANGED
@@ -1,33 +1,36 @@
1
1
  const binding = require('../binding')
2
- const ReferenceCounted = require('./reference-counted')
3
2
 
4
- module.exports = class FFmpegDictionary extends ReferenceCounted {
3
+ module.exports = class FFmpegDictionary {
5
4
  constructor() {
6
- super()
7
-
8
5
  this._handle = binding.initDictionary()
9
6
  }
10
7
 
11
- _destroy() {
8
+ destroy() {
12
9
  binding.destroyDictionary(this._handle)
13
10
  this._handle = null
14
11
  }
15
12
 
16
13
  get(key) {
17
- if (typeof key !== 'string' || key.length < 1) {
18
- throw new TypeError(`Key should be a non empty string`)
19
- }
20
- return binding.getDictionaryEntry(this._handle, key)
14
+ const value = binding.getDictionaryEntry(this._handle, key)
15
+ if (value === undefined) return null
16
+ return value
21
17
  }
22
18
 
23
19
  set(key, value) {
24
- if (typeof key !== 'string' || key.length < 1) {
25
- throw new TypeError(`Key should be a non empty string`)
26
- }
20
+ if (typeof value !== 'string') value = String(value)
27
21
 
28
- if (typeof value !== 'string' || value.length < 1) {
29
- throw new TypeError(`Value should be a non empty string`)
30
- }
31
22
  binding.setDictionaryEntry(this._handle, key, value)
32
23
  }
24
+
25
+ entries() {
26
+ return binding.getDictionaryEntries(this._handle)
27
+ }
28
+
29
+ *[Symbol.iterator]() {
30
+ yield* this.entries()
31
+ }
32
+
33
+ [Symbol.dispose]() {
34
+ this.destroy()
35
+ }
33
36
  }
package/lib/errors.js ADDED
@@ -0,0 +1,38 @@
1
+ module.exports = class FFmpegError extends Error {
2
+ constructor(msg, code, fn = FFmpegError) {
3
+ super(`${code}: ${msg}`)
4
+ this.code = code
5
+
6
+ if (Error.captureStackTrace) {
7
+ Error.captureStackTrace(this, fn)
8
+ }
9
+ }
10
+
11
+ get name() {
12
+ return 'FFmpegError'
13
+ }
14
+
15
+ static UNKNOWN_PIXEL_FORMAT(msg) {
16
+ return new FFmpegError(
17
+ msg,
18
+ 'UNKNOWN_PIXEL_FORMAT',
19
+ FFmpegError.UNKNOWN_PIXEL_FORMAT
20
+ )
21
+ }
22
+
23
+ static UNKNOWN_SAMPLE_FORMAT(msg) {
24
+ return new FFmpegError(
25
+ msg,
26
+ 'UNKNOWN_SAMPLE_FORMAT',
27
+ FFmpegError.UNKNOWN_SAMPLE_FORMAT
28
+ )
29
+ }
30
+
31
+ static UNKNOWN_CHANNEL_LAYOUT(msg) {
32
+ return new FFmpegError(
33
+ msg,
34
+ 'UNKNOWN_CHANNEL_LAYOUT',
35
+ FFmpegError.UNKNOWN_CHANNEL_LAYOUT
36
+ )
37
+ }
38
+ }
@@ -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,8 +111,12 @@ exports.InputFormatContext = class FFmpegInputFormatContext extends (
101
111
  }
102
112
  }
103
113
 
104
- _destroy() {
105
- super._destroy()
114
+ dump(printIdx = 0, printUrl = '') {
115
+ binding.dumpFormatContext(this._handle, false, printIdx, printUrl)
116
+ }
117
+
118
+ destroy() {
119
+ super.destroy()
106
120
 
107
121
  binding.closeInputFormatContext(this._handle)
108
122
  this._handle = null
@@ -118,22 +132,39 @@ exports.OutputFormatContext = class FFmpegOutputFormatContext extends (
118
132
  if (typeof format === 'string') format = new OutputFormat(format)
119
133
 
120
134
  this._handle = binding.openOutputFormatContext(format._handle, io._handle)
135
+ this._isOutput = true
121
136
  }
122
137
 
123
- _destroy() {
124
- super._destroy()
138
+ destroy() {
139
+ super.destroy()
125
140
 
126
141
  binding.closeOutputFormatContext(this._handle)
127
142
  this._handle = null
128
143
  }
129
144
 
130
- createStream(codec) {
131
- const stream = new Stream(
132
- binding.createFormatContextStream(this._handle, codec._handle)
133
- )
145
+ createStream() {
146
+ const stream = new Stream(binding.createFormatContextStream(this._handle))
134
147
 
135
148
  this._streams.push(stream)
136
149
 
137
150
  return stream
138
151
  }
152
+
153
+ /** @param {Dictionary} [options] format options */
154
+ writeHeader(options) {
155
+ binding.writeFormatContextHeader(this._handle, options?._handle)
156
+ }
157
+
158
+ /** @param {Packet} packet */
159
+ writeFrame(packet) {
160
+ binding.writeFormatContextFrame(this._handle, packet._handle)
161
+ }
162
+
163
+ writeTrailer() {
164
+ binding.writeFormatContextTrailer(this._handle)
165
+ }
166
+
167
+ dump(printIdx = 0, printUrl = '') {
168
+ binding.dumpFormatContext(this._handle, true, printIdx, printUrl)
169
+ }
139
170
  }
package/lib/frame.js CHANGED
@@ -1,51 +1,122 @@
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 pixelFormat() {
36
+ return binding.getFramePixelFormat(this._handle)
38
37
  }
39
38
 
40
39
  set pixelFormat(value) {
41
40
  binding.setFramePixelFormat(this._handle, value)
42
41
  }
43
42
 
44
- get pixelFormat() {
45
- return binding.getFramePixelFormat(this._handle)
43
+ get format() {
44
+ return binding.getFrameFormat(this._handle)
45
+ }
46
+
47
+ set format(value) {
48
+ binding.setFrameFormat(this._handle, value)
49
+ }
50
+
51
+ get channelLayout() {
52
+ return new ChannelLayout(binding.getFrameChannelLayout(this._handle))
53
+ }
54
+
55
+ set channelLayout(value) {
56
+ binding.setFrameChannelLayout(
57
+ this._handle,
58
+ ChannelLayout.from(value)._handle
59
+ )
60
+ }
61
+
62
+ get nbSamples() {
63
+ return binding.getFrameNbSamples(this._handle)
64
+ }
65
+
66
+ set nbSamples(value) {
67
+ binding.setFrameNbSamples(this._handle, value)
68
+ }
69
+
70
+ get pictType() {
71
+ return binding.getFramePictType(this._handle)
72
+ }
73
+
74
+ get pts() {
75
+ return binding.getFramePTS(this._handle)
76
+ }
77
+
78
+ set pts(value) {
79
+ return binding.setFramePTS(this._handle, value)
80
+ }
81
+
82
+ get packetDTS() {
83
+ return binding.getFramePacketDTS(this._handle)
84
+ }
85
+
86
+ set packetDTS(value) {
87
+ return binding.setFramePacketDTS(this._handle, value)
88
+ }
89
+
90
+ get timeBase() {
91
+ const view = new Int32Array(binding.getFrameTimeBase(this._handle))
92
+ return new Rational(view[0], view[1])
93
+ }
94
+
95
+ set timeBase(value) {
96
+ binding.setFrameTimeBase(this._handle, value.numerator, value.denominator)
46
97
  }
47
98
 
48
99
  alloc() {
49
100
  binding.allocFrame(this._handle, 32)
50
101
  }
102
+
103
+ [Symbol.dispose]() {
104
+ this.destroy()
105
+ }
106
+
107
+ [Symbol.for('bare.inspect')]() {
108
+ return {
109
+ __proto__: { constructor: FFmpegFrame },
110
+ width: this.width,
111
+ height: this.height,
112
+ pixelFormat: this.pixelFormat,
113
+ format: this.format,
114
+ channelLayout: this.channelLayout,
115
+ nbSamples: this.nbSamples,
116
+ pictType: this.pictType,
117
+ pts: this.pts,
118
+ packetDTS: this.packetDTS,
119
+ timeBase: this.timeBase
120
+ }
121
+ }
51
122
  }
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':
@@ -16,9 +20,10 @@ switch (Bare.platform) {
16
20
 
17
21
  module.exports = class FFmpegInputFormat {
18
22
  constructor(name = defaultName) {
19
- if (typeof name !== 'string' || name.length < 1) {
20
- throw new TypeError('Input format name should be a non empty string')
21
- }
22
23
  this._handle = binding.initInputFormat(name)
23
24
  }
25
+
26
+ get flags() {
27
+ return binding.getInputFormatFlags(this._handle)
28
+ }
24
29
  }