bare-ffmpeg 1.0.0-26 → 1.0.0-28
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/README.md +289 -0
- package/binding.cc +563 -34
- package/index.js +8 -0
- package/lib/codec-context.js +48 -0
- package/lib/codec-parameters.js +6 -2
- package/lib/constants.js +9 -0
- package/lib/filter-context.js +7 -0
- package/lib/filter-graph.js +54 -0
- package/lib/filter-inout.js +55 -0
- package/lib/filter.js +7 -0
- package/lib/frame.js +4 -0
- package/lib/input-format.js +8 -0
- package/lib/output-format.js +8 -0
- package/lib/packet.js +6 -1
- package/lib/rational.js +15 -3
- package/lib/samples.js +40 -25
- package/lib/stream.js +11 -0
- package/package.json +4 -2
- package/prebuilds/android-arm/bare-ffmpeg.bare +0 -0
- package/prebuilds/android-arm64/bare-ffmpeg.bare +0 -0
- package/prebuilds/android-ia32/bare-ffmpeg.bare +0 -0
- package/prebuilds/android-x64/bare-ffmpeg.bare +0 -0
- package/prebuilds/darwin-arm64/bare-ffmpeg.bare +0 -0
- package/prebuilds/darwin-x64/bare-ffmpeg.bare +0 -0
- package/prebuilds/ios-arm64/bare-ffmpeg.bare +0 -0
- package/prebuilds/ios-arm64-simulator/bare-ffmpeg.bare +0 -0
- package/prebuilds/ios-x64-simulator/bare-ffmpeg.bare +0 -0
- package/prebuilds/linux-arm64/bare-ffmpeg.bare +0 -0
- package/prebuilds/linux-x64/bare-ffmpeg.bare +0 -0
- package/prebuilds/win32-arm64/bare-ffmpeg.bare +0 -0
- package/prebuilds/win32-x64/bare-ffmpeg.bare +0 -0
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
|
package/lib/codec-context.js
CHANGED
|
@@ -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) {
|
|
@@ -120,6 +121,14 @@ module.exports = class FFmpegCodecContext {
|
|
|
120
121
|
)
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
get frameSize() {
|
|
125
|
+
return binding.getCodecContextFrameSize(this._handle)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get frameNum() {
|
|
129
|
+
return binding.getCodecContextFrameNum(this._handle)
|
|
130
|
+
}
|
|
131
|
+
|
|
123
132
|
open(options) {
|
|
124
133
|
if (this._opened) return
|
|
125
134
|
this._opened = true
|
|
@@ -151,6 +160,45 @@ module.exports = class FFmpegCodecContext {
|
|
|
151
160
|
return binding.receiveCodecContextFrame(this._handle, frame._handle)
|
|
152
161
|
}
|
|
153
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
|
+
|
|
154
202
|
[Symbol.dispose]() {
|
|
155
203
|
this.destroy()
|
|
156
204
|
}
|
package/lib/codec-parameters.js
CHANGED
|
@@ -243,22 +243,26 @@ module.exports = class FFmpegCodecParameters {
|
|
|
243
243
|
type: this.type,
|
|
244
244
|
id: this.id,
|
|
245
245
|
format: this.format,
|
|
246
|
+
tag: this.tag,
|
|
247
|
+
frameRate: this.frameRate,
|
|
248
|
+
videoDelay: this.videoDelay,
|
|
246
249
|
profile: this.profile,
|
|
247
250
|
level: this.level,
|
|
248
251
|
width: this.width,
|
|
249
252
|
height: this.height,
|
|
253
|
+
sampleAsectRatio: this.sampleAspectRatio,
|
|
250
254
|
bitRate: this.bitRate,
|
|
251
255
|
bitsPerCodedSample: this.bitsPerCodedSample,
|
|
252
256
|
bitsPerRawSample: this.bitsPerRawSample,
|
|
253
257
|
sampleRate: this.sampleRate,
|
|
254
258
|
nbChannels: this.nbChannels,
|
|
255
259
|
channelLayout: this.channelLayout,
|
|
256
|
-
frameRate: this.frameRate,
|
|
257
260
|
extraData: this.extraData,
|
|
258
261
|
blockAlign: this.blockAlign,
|
|
259
262
|
initialPadding: this.initialPadding,
|
|
260
263
|
trailingPadding: this.trailingPadding,
|
|
261
|
-
seekPreroll: this.seekPreroll
|
|
264
|
+
seekPreroll: this.seekPreroll,
|
|
265
|
+
frameSize: this.frameSize
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
|
package/lib/constants.js
CHANGED
|
@@ -184,6 +184,15 @@ module.exports = exports = {
|
|
|
184
184
|
'3D_REFERENCE_DISPLAYS': binding.AV_PKT_DATA_3D_REFERENCE_DISPLAYS,
|
|
185
185
|
RTCP_SR: binding.AV_PKT_DATA_RTCP_SR,
|
|
186
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
|
|
187
196
|
}
|
|
188
197
|
}
|
|
189
198
|
|
|
@@ -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
package/lib/frame.js
CHANGED
|
@@ -88,6 +88,10 @@ module.exports = class FFmpegFrame {
|
|
|
88
88
|
binding.setFrameTimeBase(this._handle, value.numerator, value.denominator)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
copyProperties(source) {
|
|
92
|
+
binding.copyFrameProperties(this._handle, source._handle)
|
|
93
|
+
}
|
|
94
|
+
|
|
91
95
|
alloc() {
|
|
92
96
|
binding.allocFrame(this._handle, 32)
|
|
93
97
|
}
|
package/lib/input-format.js
CHANGED
|
@@ -26,4 +26,12 @@ module.exports = class FFmpegInputFormat {
|
|
|
26
26
|
get flags() {
|
|
27
27
|
return binding.getInputFormatFlags(this._handle)
|
|
28
28
|
}
|
|
29
|
+
|
|
30
|
+
get extensions() {
|
|
31
|
+
return binding.getInputFormatExtensions(this._handle)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get mimeType() {
|
|
35
|
+
return binding.getInputFormatMimeType(this._handle)
|
|
36
|
+
}
|
|
29
37
|
}
|
package/lib/output-format.js
CHANGED
|
@@ -8,4 +8,12 @@ module.exports = class FFmpegOutputFormat {
|
|
|
8
8
|
get flags() {
|
|
9
9
|
return binding.getOutputFormatFlags(this._handle)
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
get extensions() {
|
|
13
|
+
return binding.getOutputFormatExtensions(this._handle)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get mimeType() {
|
|
17
|
+
return binding.getOutputFormatMimeType(this._handle)
|
|
18
|
+
}
|
|
11
19
|
}
|
package/lib/packet.js
CHANGED
|
@@ -138,6 +138,10 @@ module.exports = class FFmpegPacket {
|
|
|
138
138
|
)
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
copyPropsFrom(sourcePacket) {
|
|
142
|
+
binding.copyPacketProps(this._handle, sourcePacket._handle)
|
|
143
|
+
}
|
|
144
|
+
|
|
141
145
|
get duration() {
|
|
142
146
|
return binding.getPacketDuration(this._handle)
|
|
143
147
|
}
|
|
@@ -164,7 +168,8 @@ module.exports = class FFmpegPacket {
|
|
|
164
168
|
dts: this.dts,
|
|
165
169
|
pts: this.pts,
|
|
166
170
|
duration: this.duration,
|
|
167
|
-
data: this.data
|
|
171
|
+
data: this.data,
|
|
172
|
+
sideData: this.sideData
|
|
168
173
|
}
|
|
169
174
|
}
|
|
170
175
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
6
|
-
|
|
4
|
+
_data
|
|
5
|
+
_frame
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
this._align = align
|
|
7
|
+
constructor({ noAlignment = false } = {}) {
|
|
8
|
+
this._noAlignment = noAlignment
|
|
9
|
+
}
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
11
|
+
get data() {
|
|
12
|
+
return this._data
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
get
|
|
19
|
-
return this.
|
|
15
|
+
get format() {
|
|
16
|
+
return this._frame.format
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
get
|
|
23
|
-
return this.
|
|
19
|
+
get channelLayout() {
|
|
20
|
+
return this._frame.channelLayout
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
get nbSamples() {
|
|
27
|
-
return this.
|
|
24
|
+
return this._frame.nbSamples
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
get
|
|
31
|
-
return this.
|
|
27
|
+
get nbChannels() {
|
|
28
|
+
return this.channelLayout && this.channelLayout.nbChannels
|
|
32
29
|
}
|
|
33
30
|
|
|
34
|
-
get
|
|
35
|
-
return this.
|
|
31
|
+
get pts() {
|
|
32
|
+
return this._frame.pts
|
|
36
33
|
}
|
|
37
34
|
|
|
38
35
|
fill(frame) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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-
|
|
3
|
+
"version": "1.0.0-28",
|
|
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",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|