munim-ffmpeg 0.5.0 → 0.5.1
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 +7 -6
- package/android/src/main/java/com/margelo/nitro/munimffmpeg/FFmpegNative.kt +68 -66
- package/android/src/main/java/com/margelo/nitro/munimffmpeg/HybridMunimFfmpeg.kt +13 -33
- package/ios/HybridMunimFfmpeg.swift +65 -41
- package/ios/munim_ffmpeg_core.h +14 -1
- package/lib/index.js +3 -3
- package/package.json +1 -1
- package/scripts/binaries.json +1 -1
- package/src/index.ts +3 -3
package/README.md
CHANGED
|
@@ -132,8 +132,8 @@
|
|
|
132
132
|
| Expo Go | ❌ | ❌ | A native development build is required. |
|
|
133
133
|
| Capability discovery | ✅ | ✅ | `listEncoders()`, `listDecoders()`, `listMuxers()`, `listDemuxers()`, `listFilters()`, `listProtocols()`, and `pickEncoder()` report what the bundled build supports. |
|
|
134
134
|
| Subtitle burn-in | ✅ | ✅ | libass with system fonts: Core Text on iOS, fontconfig over `/system/fonts` on Android. |
|
|
135
|
-
| H.264 encoding | VideoToolbox |
|
|
136
|
-
| Remote HTTP(S) inputs | ✅ | ✅ |
|
|
135
|
+
| H.264 encoding | VideoToolbox | MediaCodec | Hardware on both, `libopenh264` as the software fallback; use `pickEncoder(['h264_videotoolbox', 'h264_mediacodec', 'libopenh264'])` instead of hard-coding an encoder. |
|
|
136
|
+
| Remote HTTP(S) inputs | ✅ | ✅ | iOS links SecureTransport, Android links mbedTLS. Remote server behaviour still varies; prefer local files for predictable app workflows. |
|
|
137
137
|
|
|
138
138
|
Codec availability is determined by the native FFmpeg builds described in [Bundled FFmpeg builds](#bundled-ffmpeg-builds). Do not assume every FFmpeg codec or external library is present.
|
|
139
139
|
|
|
@@ -567,11 +567,12 @@ if (!result.success) {
|
|
|
567
567
|
```typescript
|
|
568
568
|
import { execute, pickEncoder } from 'munim-ffmpeg'
|
|
569
569
|
|
|
570
|
-
const encoder = await pickEncoder(['
|
|
570
|
+
const encoder = await pickEncoder(['h264_videotoolbox', 'h264_mediacodec', 'libopenh264'])
|
|
571
571
|
if (!encoder) throw new Error('No H.264 encoder available in this build')
|
|
572
572
|
|
|
573
|
-
//
|
|
574
|
-
const
|
|
573
|
+
// MediaCodec wants NV12 input; the others take planar YUV.
|
|
574
|
+
const pixelFormat = encoder === 'h264_mediacodec' ? 'nv12' : 'yuv420p'
|
|
575
|
+
const quality = ['-b:v', '2M', '-pix_fmt', pixelFormat]
|
|
575
576
|
|
|
576
577
|
const result = await execute([
|
|
577
578
|
'-y',
|
|
@@ -741,7 +742,7 @@ Rebuild the native app after installing both `munim-ffmpeg` and `react-native-ni
|
|
|
741
742
|
|
|
742
743
|
### A codec or filter is missing
|
|
743
744
|
|
|
744
|
-
Native FFmpeg variants do not bundle every codec, filter, or third-party library, and the iOS and Android builds are not identical. Call `listEncoders()` or `
|
|
745
|
+
Native FFmpeg variants do not bundle every codec, filter, or third-party library, and the iOS and Android builds are not identical. Call `listEncoders()`, `listDecoders()`, `listMuxers()`, `listDemuxers()`, `listFilters()`, or `listProtocols()` to see what the running build actually has, and prefer `pickEncoder()` over a hard-coded name. There is no `libx264` in these LGPL builds — H.264 comes from the platform's hardware encoder or `libopenh264` — see [Bundled FFmpeg builds](#bundled-ffmpeg-builds).
|
|
745
746
|
|
|
746
747
|
### The Promise resolved but the command failed
|
|
747
748
|
|
|
@@ -5,6 +5,55 @@ import androidx.annotation.Keep
|
|
|
5
5
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
6
6
|
import java.io.File
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Per-run callback target. The JNI bridge holds a global reference to this
|
|
10
|
+
* object for the duration of its execution and the native core only routes
|
|
11
|
+
* callbacks to whichever session actually holds the execution lock, so
|
|
12
|
+
* concurrently submitted sessions never see each other's logs.
|
|
13
|
+
*/
|
|
14
|
+
@Keep
|
|
15
|
+
@DoNotStrip
|
|
16
|
+
class FFmpegSession(
|
|
17
|
+
private val logSink: ((String) -> Unit)?,
|
|
18
|
+
private val statisticsSink: ((
|
|
19
|
+
timeMs: Double,
|
|
20
|
+
sizeBytes: Double,
|
|
21
|
+
bitrateKbits: Double,
|
|
22
|
+
speed: Double,
|
|
23
|
+
videoFrameNumber: Double,
|
|
24
|
+
fps: Double,
|
|
25
|
+
quality: Double,
|
|
26
|
+
) -> Unit)?,
|
|
27
|
+
) {
|
|
28
|
+
private val buffer = StringBuilder()
|
|
29
|
+
|
|
30
|
+
val output: String
|
|
31
|
+
get() = synchronized(buffer) { buffer.toString() }
|
|
32
|
+
|
|
33
|
+
// FFmpeg logs from several of its own threads at once, so the buffer needs
|
|
34
|
+
// the lock even though each session belongs to a single execution.
|
|
35
|
+
@Keep
|
|
36
|
+
@DoNotStrip
|
|
37
|
+
fun onLog(message: String) {
|
|
38
|
+
synchronized(buffer) { buffer.append(message) }
|
|
39
|
+
logSink?.invoke(message)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@Keep
|
|
43
|
+
@DoNotStrip
|
|
44
|
+
fun onStatistics(
|
|
45
|
+
timeMs: Double,
|
|
46
|
+
sizeBytes: Double,
|
|
47
|
+
bitrateKbits: Double,
|
|
48
|
+
speed: Double,
|
|
49
|
+
videoFrameNumber: Double,
|
|
50
|
+
fps: Double,
|
|
51
|
+
quality: Double,
|
|
52
|
+
) {
|
|
53
|
+
statisticsSink?.invoke(timeMs, sizeBytes, bitrateKbits, speed, videoFrameNumber, fps, quality)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
8
57
|
/**
|
|
9
58
|
* Thin wrapper over FFmpeg 9's own command-line tools, compiled to run inside
|
|
10
59
|
* the app process.
|
|
@@ -21,6 +70,25 @@ object FFmpegNative {
|
|
|
21
70
|
System.loadLibrary("munimffmpeg9")
|
|
22
71
|
}
|
|
23
72
|
|
|
73
|
+
/** Return code the tools report when a run was cancelled. */
|
|
74
|
+
const val CANCELLED = 255
|
|
75
|
+
|
|
76
|
+
external fun nativeVersion(): String
|
|
77
|
+
|
|
78
|
+
external fun nativeExecute(
|
|
79
|
+
arguments: Array<String>,
|
|
80
|
+
stdoutPath: String,
|
|
81
|
+
session: FFmpegSession,
|
|
82
|
+
): Int
|
|
83
|
+
|
|
84
|
+
external fun nativeExecuteProbe(
|
|
85
|
+
arguments: Array<String>,
|
|
86
|
+
outputPath: String,
|
|
87
|
+
session: FFmpegSession,
|
|
88
|
+
): Int
|
|
89
|
+
|
|
90
|
+
external fun nativeCancel()
|
|
91
|
+
|
|
24
92
|
/**
|
|
25
93
|
* libass discovers fonts through fontconfig, and Android has no fonts.conf,
|
|
26
94
|
* so one pointing at the system font directories is written to the app's
|
|
@@ -53,70 +121,4 @@ object FFmpegNative {
|
|
|
53
121
|
Os.setenv("FONTCONFIG_FILE", configuration.absolutePath, true)
|
|
54
122
|
}
|
|
55
123
|
}
|
|
56
|
-
|
|
57
|
-
/** Return code the tools report when a run was cancelled. */
|
|
58
|
-
const val CANCELLED = 255
|
|
59
|
-
|
|
60
|
-
external fun nativeVersion(): String
|
|
61
|
-
|
|
62
|
-
external fun nativeExecute(arguments: Array<String>, stdoutPath: String): Int
|
|
63
|
-
|
|
64
|
-
external fun nativeExecuteProbe(arguments: Array<String>, outputPath: String): Int
|
|
65
|
-
|
|
66
|
-
external fun nativeCancel()
|
|
67
|
-
|
|
68
|
-
data class Statistics(
|
|
69
|
-
val timeMs: Double,
|
|
70
|
-
val sizeBytes: Double,
|
|
71
|
-
val bitrateKbits: Double,
|
|
72
|
-
val speed: Double,
|
|
73
|
-
val videoFrameNumber: Double,
|
|
74
|
-
val fps: Double,
|
|
75
|
-
val quality: Double,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
@Volatile
|
|
79
|
-
private var logSink: ((String) -> Unit)? = null
|
|
80
|
-
|
|
81
|
-
@Volatile
|
|
82
|
-
private var statisticsSink: ((Statistics) -> Unit)? = null
|
|
83
|
-
|
|
84
|
-
fun <T> withCallbacks(
|
|
85
|
-
onLog: ((String) -> Unit)?,
|
|
86
|
-
onStatistics: ((Statistics) -> Unit)?,
|
|
87
|
-
body: () -> T,
|
|
88
|
-
): T {
|
|
89
|
-
logSink = onLog
|
|
90
|
-
statisticsSink = onStatistics
|
|
91
|
-
try {
|
|
92
|
-
return body()
|
|
93
|
-
} finally {
|
|
94
|
-
logSink = null
|
|
95
|
-
statisticsSink = null
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
@JvmStatic
|
|
100
|
-
@Keep
|
|
101
|
-
@DoNotStrip
|
|
102
|
-
fun onLog(message: String) {
|
|
103
|
-
logSink?.invoke(message)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
@JvmStatic
|
|
107
|
-
@Keep
|
|
108
|
-
@DoNotStrip
|
|
109
|
-
fun onStatistics(
|
|
110
|
-
timeMs: Double,
|
|
111
|
-
sizeBytes: Double,
|
|
112
|
-
bitrateKbits: Double,
|
|
113
|
-
speed: Double,
|
|
114
|
-
videoFrameNumber: Double,
|
|
115
|
-
fps: Double,
|
|
116
|
-
quality: Double,
|
|
117
|
-
) {
|
|
118
|
-
statisticsSink?.invoke(
|
|
119
|
-
Statistics(timeMs, sizeBytes, bitrateKbits, speed, videoFrameNumber, fps, quality)
|
|
120
|
-
)
|
|
121
|
-
}
|
|
122
124
|
}
|
|
@@ -53,37 +53,21 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
53
53
|
onSessionCreated?.invoke(sessionId)
|
|
54
54
|
|
|
55
55
|
return Promise.async {
|
|
56
|
-
val output = StringBuilder()
|
|
57
56
|
val startedAt = System.currentTimeMillis()
|
|
58
57
|
// ffmpeg prints reports such as -encoders and -protocols to stdout rather
|
|
59
58
|
// than through its logger, so it is captured to a file and appended.
|
|
60
59
|
val stdout = File.createTempFile("munim-ffmpeg", ".txt")
|
|
61
60
|
|
|
62
|
-
val
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
onStatistics = { statistics ->
|
|
68
|
-
onStatistics?.invoke(
|
|
69
|
-
statistics.timeMs,
|
|
70
|
-
statistics.sizeBytes,
|
|
71
|
-
statistics.bitrateKbits,
|
|
72
|
-
statistics.speed,
|
|
73
|
-
statistics.videoFrameNumber,
|
|
74
|
-
statistics.fps,
|
|
75
|
-
statistics.quality,
|
|
76
|
-
)
|
|
77
|
-
},
|
|
78
|
-
) {
|
|
79
|
-
FFmpegNative.nativeExecute(arguments_, stdout.absolutePath)
|
|
80
|
-
}
|
|
61
|
+
val session = FFmpegSession(
|
|
62
|
+
logSink = { message -> onLog?.invoke(message) },
|
|
63
|
+
statisticsSink = onStatistics,
|
|
64
|
+
)
|
|
65
|
+
val returnCode = FFmpegNative.nativeExecute(arguments_, stdout.absolutePath, session)
|
|
81
66
|
|
|
82
67
|
val printed = runCatching { stdout.readText() }.getOrDefault("")
|
|
83
68
|
stdout.delete()
|
|
84
|
-
output.append(printed)
|
|
85
69
|
|
|
86
|
-
result(sessionId, returnCode, output
|
|
70
|
+
result(sessionId, returnCode, session.output + printed, System.currentTimeMillis() - startedAt)
|
|
87
71
|
}
|
|
88
72
|
}
|
|
89
73
|
|
|
@@ -138,19 +122,15 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
138
122
|
): Pair<Int, String> {
|
|
139
123
|
val destination = File.createTempFile("munim-ffprobe", ".txt")
|
|
140
124
|
try {
|
|
141
|
-
val
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
onStatistics = null,
|
|
148
|
-
) {
|
|
149
|
-
FFmpegNative.nativeExecuteProbe(arguments, destination.absolutePath)
|
|
150
|
-
}
|
|
125
|
+
val session = FFmpegSession(
|
|
126
|
+
logSink = { message -> onLog?.invoke(message) },
|
|
127
|
+
statisticsSink = null,
|
|
128
|
+
)
|
|
129
|
+
val returnCode =
|
|
130
|
+
FFmpegNative.nativeExecuteProbe(arguments, destination.absolutePath, session)
|
|
151
131
|
|
|
152
132
|
val report = destination.readText()
|
|
153
|
-
return returnCode to report.ifEmpty {
|
|
133
|
+
return returnCode to report.ifEmpty { session.output }
|
|
154
134
|
} finally {
|
|
155
135
|
destination.delete()
|
|
156
136
|
}
|
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import NitroModules
|
|
3
3
|
|
|
4
|
-
///
|
|
5
|
-
///
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
4
|
+
/// Per-run callback target, handed to the core as the context pointer. The
|
|
5
|
+
/// core only routes callbacks to whichever session actually holds its
|
|
6
|
+
/// execution lock, so concurrently submitted sessions never see each other's
|
|
7
|
+
/// logs.
|
|
8
|
+
private final class Session {
|
|
9
|
+
let onLog: ((String) -> Void)?
|
|
10
|
+
let onStatistics: ((Double, Double, Double, Double, Double, Double, Double) -> Void)?
|
|
11
|
+
private var buffer = ""
|
|
12
|
+
// FFmpeg logs from several of its own threads at once, so the buffer needs
|
|
13
|
+
// the lock even though each session belongs to a single execution.
|
|
14
|
+
private let lock = NSLock()
|
|
15
|
+
|
|
16
|
+
init(
|
|
17
|
+
onLog: ((String) -> Void)?,
|
|
18
|
+
onStatistics: ((Double, Double, Double, Double, Double, Double, Double) -> Void)? = nil
|
|
19
|
+
) {
|
|
20
|
+
self.onLog = onLog
|
|
21
|
+
self.onStatistics = onStatistics
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func append(_ text: String) {
|
|
25
|
+
lock.lock()
|
|
26
|
+
buffer += text
|
|
27
|
+
lock.unlock()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var output: String {
|
|
31
|
+
lock.lock()
|
|
32
|
+
defer { lock.unlock() }
|
|
33
|
+
return buffer
|
|
15
34
|
}
|
|
16
35
|
}
|
|
17
36
|
|
|
18
|
-
private let active = ActiveSession()
|
|
19
37
|
// Concurrent on purpose: the C core serialises executions behind its own lock,
|
|
20
38
|
// and a request that is waiting there can still be cancelled. A serial queue
|
|
21
39
|
// would hold the second call outside the core, where cancelAll() cannot see it.
|
|
@@ -25,16 +43,17 @@ private let executionQueue = DispatchQueue(
|
|
|
25
43
|
attributes: .concurrent
|
|
26
44
|
)
|
|
27
45
|
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
private let installCallbacks: Void = munim_ffmpeg_set_callbacks({ context, message in
|
|
47
|
+
guard let context, let message else { return }
|
|
48
|
+
let session = Unmanaged<Session>.fromOpaque(context).takeUnretainedValue()
|
|
49
|
+
let text = String(cString: message)
|
|
50
|
+
session.append(text)
|
|
51
|
+
session.onLog?(text)
|
|
52
|
+
}, { context, timeMs, sizeBytes, bitrate, speed, frame, fps, quality in
|
|
53
|
+
guard let context else { return }
|
|
54
|
+
let session = Unmanaged<Session>.fromOpaque(context).takeUnretainedValue()
|
|
55
|
+
session.onStatistics?(timeMs, sizeBytes, bitrate, speed, frame, fps, quality)
|
|
56
|
+
}, nil)
|
|
38
57
|
|
|
39
58
|
final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
40
59
|
private var sessionCounter: Double = 0
|
|
@@ -88,26 +107,28 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
88
107
|
// its logger, so it is captured to a file and appended to the output.
|
|
89
108
|
let printedPath = Self.temporaryFile()
|
|
90
109
|
|
|
91
|
-
installCallbacks
|
|
92
|
-
|
|
93
|
-
active.onLog = onLog
|
|
94
|
-
active.onStatistics = onStatistics
|
|
110
|
+
_ = installCallbacks
|
|
111
|
+
let session = Session(onLog: onLog, onStatistics: onStatistics)
|
|
95
112
|
|
|
96
|
-
let returnCode =
|
|
97
|
-
|
|
113
|
+
let returnCode = withExtendedLifetime(session) {
|
|
114
|
+
withArrayOfCStrings(arguments_) { argv in
|
|
115
|
+
munim_ffmpeg_execute_ctx(
|
|
116
|
+
Int32(arguments_.count),
|
|
117
|
+
argv,
|
|
118
|
+
printedPath,
|
|
119
|
+
Unmanaged.passUnretained(session).toOpaque()
|
|
120
|
+
)
|
|
121
|
+
}
|
|
98
122
|
}
|
|
99
123
|
|
|
100
124
|
let printed = (try? String(contentsOfFile: printedPath, encoding: .utf8)) ?? ""
|
|
101
125
|
try? FileManager.default.removeItem(atPath: printedPath)
|
|
102
126
|
|
|
103
|
-
let output = active.output + printed
|
|
104
|
-
active.reset()
|
|
105
|
-
|
|
106
127
|
promise.resolve(
|
|
107
128
|
withResult: self.result(
|
|
108
129
|
sessionId: sessionId,
|
|
109
130
|
returnCode: returnCode,
|
|
110
|
-
output: output,
|
|
131
|
+
output: session.output + printed,
|
|
111
132
|
startedAt: startedAt
|
|
112
133
|
)
|
|
113
134
|
)
|
|
@@ -178,21 +199,24 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
178
199
|
) -> (Int32, String) {
|
|
179
200
|
let destination = temporaryFile()
|
|
180
201
|
|
|
181
|
-
installCallbacks
|
|
182
|
-
|
|
183
|
-
active.onLog = onLog
|
|
202
|
+
_ = installCallbacks
|
|
203
|
+
let session = Session(onLog: onLog)
|
|
184
204
|
|
|
185
|
-
let returnCode =
|
|
186
|
-
|
|
205
|
+
let returnCode = withExtendedLifetime(session) {
|
|
206
|
+
withArrayOfCStrings(arguments) { argv in
|
|
207
|
+
munim_ffmpeg_probe_ctx(
|
|
208
|
+
Int32(arguments.count),
|
|
209
|
+
argv,
|
|
210
|
+
destination,
|
|
211
|
+
Unmanaged.passUnretained(session).toOpaque()
|
|
212
|
+
)
|
|
213
|
+
}
|
|
187
214
|
}
|
|
188
215
|
|
|
189
216
|
let report = (try? String(contentsOfFile: destination, encoding: .utf8)) ?? ""
|
|
190
217
|
try? FileManager.default.removeItem(atPath: destination)
|
|
191
218
|
|
|
192
|
-
|
|
193
|
-
active.reset()
|
|
194
|
-
|
|
195
|
-
return (returnCode, report.isEmpty ? logs : report)
|
|
219
|
+
return (returnCode, report.isEmpty ? session.output : report)
|
|
196
220
|
}
|
|
197
221
|
|
|
198
222
|
func cancel(sessionId: Double?) throws {
|
package/ios/munim_ffmpeg_core.h
CHANGED
|
@@ -21,7 +21,12 @@ typedef void (*munim_statistics_callback)(void *context, double time_ms,
|
|
|
21
21
|
/** Version string of the linked FFmpeg, e.g. "9.0.1". */
|
|
22
22
|
const char *munim_ffmpeg_version(void);
|
|
23
23
|
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Installs the callback functions. `context` is the default value handed to
|
|
26
|
+
* them; an execution started through one of the `_ctx` variants overrides it
|
|
27
|
+
* for exactly as long as that execution holds the core's lock, which is how
|
|
28
|
+
* callers waiting concurrently keep their callbacks apart.
|
|
29
|
+
*/
|
|
25
30
|
void munim_ffmpeg_set_callbacks(munim_log_callback on_log,
|
|
26
31
|
munim_statistics_callback on_statistics,
|
|
27
32
|
void *context);
|
|
@@ -38,10 +43,18 @@ void munim_ffmpeg_set_callbacks(munim_log_callback on_log,
|
|
|
38
43
|
int munim_ffmpeg_execute(int argc, const char *const *argv,
|
|
39
44
|
const char *stdout_path);
|
|
40
45
|
|
|
46
|
+
/** Like munim_ffmpeg_execute, with a per-run callback context. */
|
|
47
|
+
int munim_ffmpeg_execute_ctx(int argc, const char *const *argv,
|
|
48
|
+
const char *stdout_path, void *session);
|
|
49
|
+
|
|
41
50
|
/** Runs `ffprobe`, writing its report to `output_path` via `-o`. */
|
|
42
51
|
int munim_ffmpeg_probe(int argc, const char *const *argv,
|
|
43
52
|
const char *output_path);
|
|
44
53
|
|
|
54
|
+
/** Like munim_ffmpeg_probe, with a per-run callback context. */
|
|
55
|
+
int munim_ffmpeg_probe_ctx(int argc, const char *const *argv,
|
|
56
|
+
const char *output_path, void *session);
|
|
57
|
+
|
|
45
58
|
/**
|
|
46
59
|
* Requests cancellation of the running execution, and of any execution already
|
|
47
60
|
* queued behind it.
|
package/lib/index.js
CHANGED
|
@@ -39,9 +39,9 @@ export function cancelAll() {
|
|
|
39
39
|
export function getFFmpegVersion() {
|
|
40
40
|
return MunimFfmpeg.ffmpegVersion;
|
|
41
41
|
}
|
|
42
|
-
// The bundled FFmpeg builds differ per platform: Android ships
|
|
43
|
-
// iOS ships
|
|
44
|
-
//
|
|
42
|
+
// The bundled FFmpeg builds differ per platform: Android ships the MediaCodec
|
|
43
|
+
// hardware encoders, iOS ships VideoToolbox. Asking the binary what it
|
|
44
|
+
// supports is more reliable than hard-coding a per-platform table.
|
|
45
45
|
const codecCache = new Map();
|
|
46
46
|
function listCodecs(flag) {
|
|
47
47
|
const cached = codecCache.get(flag);
|
package/package.json
CHANGED
package/scripts/binaries.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -84,9 +84,9 @@ export function getFFmpegVersion(): string {
|
|
|
84
84
|
return MunimFfmpeg.ffmpegVersion
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
// The bundled FFmpeg builds differ per platform: Android ships
|
|
88
|
-
// iOS ships
|
|
89
|
-
//
|
|
87
|
+
// The bundled FFmpeg builds differ per platform: Android ships the MediaCodec
|
|
88
|
+
// hardware encoders, iOS ships VideoToolbox. Asking the binary what it
|
|
89
|
+
// supports is more reliable than hard-coding a per-platform table.
|
|
90
90
|
const codecCache = new Map<string, Promise<string[]>>()
|
|
91
91
|
|
|
92
92
|
function listCodecs(flag: '-encoders' | '-decoders'): Promise<string[]> {
|