munim-ffmpeg 0.7.0 β 0.8.0
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 +61 -8
- package/android/src/main/java/com/margelo/nitro/munimffmpeg/FFmpegNative.kt +11 -0
- package/android/src/main/java/com/margelo/nitro/munimffmpeg/HybridMunimFfmpeg.kt +94 -13
- package/ios/HybridMunimFfmpeg.swift +102 -14
- package/ios/munim_ffmpeg_core.h +35 -0
- package/lib/index.d.ts +16 -2
- package/lib/index.js +20 -0
- package/lib/specs/MunimFfmpeg.nitro.d.ts +15 -0
- package/nitrogen/generated/android/c++/JFFmpegSessionState.hpp +73 -0
- package/nitrogen/generated/android/c++/JHybridMunimFfmpegSpec.cpp +19 -0
- package/nitrogen/generated/android/c++/JHybridMunimFfmpegSpec.hpp +3 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimffmpeg/FFmpegSessionState.kt +28 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimffmpeg/HybridMunimFfmpegSpec.kt +12 -0
- package/nitrogen/generated/ios/NitroMunimFfmpeg-Swift-Cxx-Bridge.hpp +21 -0
- package/nitrogen/generated/ios/NitroMunimFfmpeg-Swift-Cxx-Umbrella.hpp +3 -0
- package/nitrogen/generated/ios/c++/HybridMunimFfmpegSpecSwift.hpp +27 -0
- package/nitrogen/generated/ios/swift/FFmpegSessionState.swift +60 -0
- package/nitrogen/generated/ios/swift/HybridMunimFfmpegSpec.swift +3 -0
- package/nitrogen/generated/ios/swift/HybridMunimFfmpegSpec_cxx.swift +36 -0
- package/nitrogen/generated/shared/c++/FFmpegSessionState.hpp +96 -0
- package/nitrogen/generated/shared/c++/HybridMunimFfmpegSpec.cpp +3 -0
- package/nitrogen/generated/shared/c++/HybridMunimFfmpegSpec.hpp +6 -0
- package/package.json +2 -2
- package/scripts/binaries.json +2 -2
- package/src/index.ts +25 -0
- package/src/specs/MunimFfmpeg.nitro.ts +26 -0
package/README.md
CHANGED
|
@@ -94,12 +94,13 @@
|
|
|
94
94
|
### FFmpeg execution
|
|
95
95
|
|
|
96
96
|
- π¬ **Argument-array commands:** FFmpeg's own CLI code paths, without shell parsing or quoting
|
|
97
|
-
- π **FFmpeg 9.0.
|
|
97
|
+
- π **FFmpeg 9.0.2:** The current upstream release, identical on both platforms
|
|
98
98
|
- β‘ **Asynchronous sessions:** Keep the React Native thread responsive during native work
|
|
99
99
|
- π **Live logs:** Receive FFmpeg output as it is produced
|
|
100
100
|
- π **Encoding statistics:** Track time, size, bitrate, speed, frames, FPS, and quality
|
|
101
101
|
- π― **Targeted cancellation:** Capture a native session ID immediately and cancel only that command
|
|
102
102
|
- π **Global cancellation:** Stop all active sessions during workflow or screen cleanup
|
|
103
|
+
- βΈοΈ **Pause and resume:** Hold a running or queued session with no CPU use and continue exactly where it stopped
|
|
103
104
|
|
|
104
105
|
### FFprobe and media inspection
|
|
105
106
|
|
|
@@ -132,6 +133,8 @@
|
|
|
132
133
|
| Immediate session ID | β
| β
| `onSessionCreated` fires after the native session is created. |
|
|
133
134
|
| Cancel one FFmpeg session | β
| β
| Pass the positive safe-integer ID received by `execute`'s `onSessionCreated`. The native dependency does not expose FFprobe cancellation. |
|
|
134
135
|
| Cancel all FFmpeg sessions | β
| β
| Use `cancelAll()` or call `cancel()` without an ID. |
|
|
136
|
+
| Pause and resume a session | β
| β
| `pause()` / `resume()` with an `execute` session ID, running or queued. FFprobe sessions cannot be paused. |
|
|
137
|
+
| Session state | β
| β
| `getSessionState()` reports `queued`, `running`, `paused`, `completed`, `failed`, or `cancelled`. |
|
|
135
138
|
| Expo Go | β | β | A native development build is required. |
|
|
136
139
|
| Capability discovery | β
| β
| `listEncoders()`, `listDecoders()`, `listMuxers()`, `listDemuxers()`, `listFilters()`, `listProtocols()`, and `pickEncoder()` report what the bundled build supports. |
|
|
137
140
|
| Subtitle burn-in | β
| β
| libass with system fonts: Core Text on iOS, fontconfig over `/system/fonts` on Android. |
|
|
@@ -144,13 +147,12 @@ Codec availability is determined by the native FFmpeg builds described in [Bundl
|
|
|
144
147
|
|
|
145
148
|
## Where this is verified
|
|
146
149
|
|
|
147
|
-
Every release runs the example's
|
|
150
|
+
Every release runs the example's device suite (33 checks, including pause/resume). For 0.8.x:
|
|
148
151
|
|
|
149
152
|
| Target | Result |
|
|
150
153
|
| -------------------------- | ---------------------------------------------------------------- |
|
|
151
|
-
| iPad Air (M3),
|
|
152
|
-
|
|
|
153
|
-
| Galaxy A14 5G, arm64-v8a | 25/25 |
|
|
154
|
+
| iPad Air (M3), iPadOS 27 | 33/33 |
|
|
155
|
+
| Galaxy A14 5G, arm64-v8a | 33/33 |
|
|
154
156
|
| Android emulator, arm64 | Software encoding passes; hardware encoding does not β see below |
|
|
155
157
|
| Android emulator, `x86_64` | Same: `libopenh264` passes, MediaCodec does not |
|
|
156
158
|
| Android `armeabi-v7a` | Built and statically checked, not executed |
|
|
@@ -183,11 +185,11 @@ await execute([
|
|
|
183
185
|
|
|
184
186
|
## Bundled FFmpeg builds
|
|
185
187
|
|
|
186
|
-
Both platforms run **FFmpeg 9.0.
|
|
188
|
+
Both platforms run **FFmpeg 9.0.2**, built from [ffmpeg.org](https://www.ffmpeg.org/) by the scripts in [`scripts/ffmpeg/`](./scripts/ffmpeg). There is no FFmpegKit here: that project was retired in 2025 and pinned to FFmpeg 6.0.
|
|
187
189
|
|
|
188
190
|
| | iOS | Android |
|
|
189
191
|
| --------------- | ------------------------------------------------------- | ------------------------------- |
|
|
190
|
-
| FFmpeg | 9.0.
|
|
192
|
+
| FFmpeg | 9.0.2 | 9.0.2 |
|
|
191
193
|
| Architectures | arm64 device, arm64 + x86_64 simulator | arm64-v8a, armeabi-v7a, x86_64 |
|
|
192
194
|
| Hardware codecs | VideoToolbox, AudioToolbox | MediaCodec |
|
|
193
195
|
| TLS | SecureTransport | mbedTLS |
|
|
@@ -473,6 +475,57 @@ Cancels every active FFmpeg session.
|
|
|
473
475
|
function cancelAll(): void
|
|
474
476
|
```
|
|
475
477
|
|
|
478
|
+
### `pause(sessionId)` and `resume(sessionId)`
|
|
479
|
+
|
|
480
|
+
Pauses a session started with `execute()`, whether it is running or still queued behind another one, and resumes it later.
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
function pause(sessionId: number): boolean
|
|
484
|
+
function resume(sessionId: number): boolean
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
FFmpeg itself has no pause, so munim-ffmpeg stops the threads that read its inputs. Everything downstream finishes the frames it already has and then waits, so a paused session uses no CPU. Output files stay open, and `resume()` carries on from the exact packet where reading stopped. Nothing is re-encoded, skipped or duplicated, and the `execute()` promise settles once, when the work is done. `-re` and `-readrate` inputs have their clocks shifted by the time spent paused, so they do not rush to catch up afterwards.
|
|
488
|
+
|
|
489
|
+
`pause()` returns `false` if the session is unknown, has already finished, or is an FFprobe session. `resume()` returns `false` if the session was not paused. `cancel()` and `cancelAll()` work while paused.
|
|
490
|
+
|
|
491
|
+
Notes:
|
|
492
|
+
|
|
493
|
+
- The statistics callback keeps firing while paused, reporting the same `timeMs`. `speed` includes the paused time.
|
|
494
|
+
- Live network inputs are not read while paused. Servers that drop idle connections may end the session when it resumes.
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
import { execute, pause, resume } from 'munim-ffmpeg'
|
|
498
|
+
|
|
499
|
+
let sessionId: number | undefined
|
|
500
|
+
const done = execute(args, undefined, undefined, (id) => {
|
|
501
|
+
sessionId = id
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
// Later, from a pause button:
|
|
505
|
+
pause(sessionId!)
|
|
506
|
+
// β¦and from a resume button:
|
|
507
|
+
resume(sessionId!)
|
|
508
|
+
|
|
509
|
+
const result = await done
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
### `getSessionState(sessionId)`
|
|
513
|
+
|
|
514
|
+
```typescript
|
|
515
|
+
type FFmpegSessionState =
|
|
516
|
+
| 'queued'
|
|
517
|
+
| 'running'
|
|
518
|
+
| 'paused'
|
|
519
|
+
| 'completed'
|
|
520
|
+
| 'failed'
|
|
521
|
+
| 'cancelled'
|
|
522
|
+
| 'unknown'
|
|
523
|
+
|
|
524
|
+
function getSessionState(sessionId: number): FFmpegSessionState
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
`queued` sessions are waiting for the one running ahead of them, since FFmpeg runs one execution at a time. `unknown` means the ID was never issued, or it finished long enough ago that its record was dropped (the last 512 finished sessions are kept).
|
|
528
|
+
|
|
476
529
|
### `getFFmpegVersion()`
|
|
477
530
|
|
|
478
531
|
Returns the version reported by the bundled native FFmpeg library.
|
|
@@ -882,7 +935,7 @@ if (result.success) {
|
|
|
882
935
|
|
|
883
936
|
The JavaScript, TypeScript, Swift, Kotlin, C core, and generated Nitro bridge in this repository are Apache-2.0.
|
|
884
937
|
|
|
885
|
-
The bundled FFmpeg 9.0.
|
|
938
|
+
The bundled FFmpeg 9.0.2 is **LGPLv3**, on both platforms. It is configured without `--enable-gpl`, so no x264, x265, xvid, or vid.stab. The external libraries it links are LAME (LGPL), Opus (BSD), libvpx (BSD), dav1d (BSD), libaom (BSD 2-clause with the Alliance for Open Media patent licence), openh264 (BSD 2-clause), libass (ISC), FreeType (FTL, BSD-style with credit), HarfBuzz (MIT-style), FriBidi (LGPL), and, on Android only, mbedTLS (Apache-2.0), fontconfig (MIT-style), and expat (MIT). None of them change the LGPL story.
|
|
886
939
|
|
|
887
940
|
> **A note on H.264 patents.** Hardware encoders are covered by the licences device manufacturers already pay for. Software H.264 encoding through `libopenh264` is not: Cisco's royalty coverage applies to _their_ prebuilt binary, and this package builds openh264 from source. If you ship software H.264 encoding at scale, check where you stand with AVC licensing. Hardware encoders avoid the question entirely, which is why `pickEncoder` should list them first.
|
|
888
941
|
|
|
@@ -79,16 +79,27 @@ object FFmpegNative {
|
|
|
79
79
|
arguments: Array<String>,
|
|
80
80
|
stdoutPath: String,
|
|
81
81
|
session: FFmpegSession,
|
|
82
|
+
sessionId: Long,
|
|
82
83
|
): Int
|
|
83
84
|
|
|
84
85
|
external fun nativeExecuteProbe(
|
|
85
86
|
arguments: Array<String>,
|
|
86
87
|
outputPath: String,
|
|
87
88
|
session: FFmpegSession,
|
|
89
|
+
sessionId: Long,
|
|
88
90
|
): Int
|
|
89
91
|
|
|
90
92
|
external fun nativeCancel()
|
|
91
93
|
|
|
94
|
+
external fun nativePause(sessionId: Long): Boolean
|
|
95
|
+
|
|
96
|
+
external fun nativeResume(sessionId: Long): Boolean
|
|
97
|
+
|
|
98
|
+
external fun nativeIsPaused(sessionId: Long): Boolean
|
|
99
|
+
|
|
100
|
+
/** Id of the execution or probe currently running, or 0. */
|
|
101
|
+
external fun nativeRunningSession(): Long
|
|
102
|
+
|
|
92
103
|
/**
|
|
93
104
|
* libass discovers fonts through fontconfig, and Android has no fonts.conf,
|
|
94
105
|
* so one pointing at the system font directories is written to the app's
|
|
@@ -16,6 +16,7 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
16
16
|
get() = FFmpegNative.nativeVersion()
|
|
17
17
|
|
|
18
18
|
private val sessions = AtomicLong(0)
|
|
19
|
+
private val registry = SessionRegistry()
|
|
19
20
|
|
|
20
21
|
private fun nextSession() = sessions.incrementAndGet().toDouble()
|
|
21
22
|
|
|
@@ -72,6 +73,7 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
72
73
|
onSessionCreated: ((sessionId: Double) -> Unit)?,
|
|
73
74
|
): Promise<FFmpegSessionResult> {
|
|
74
75
|
val sessionId = nextSession()
|
|
76
|
+
registry.register(sessionId, SessionRegistry.Kind.EXECUTE)
|
|
75
77
|
onSessionCreated?.invoke(sessionId)
|
|
76
78
|
|
|
77
79
|
return runOnFfmpegThread {
|
|
@@ -84,7 +86,11 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
84
86
|
logSink = { message -> onLog?.invoke(message) },
|
|
85
87
|
statisticsSink = onStatistics,
|
|
86
88
|
)
|
|
87
|
-
val returnCode =
|
|
89
|
+
val returnCode =
|
|
90
|
+
FFmpegNative.nativeExecute(arguments_, stdout.absolutePath, session, sessionId.toLong())
|
|
91
|
+
// A pause that raced the end of the run would otherwise stay listed.
|
|
92
|
+
FFmpegNative.nativeResume(sessionId.toLong())
|
|
93
|
+
registry.finish(sessionId, returnCode)
|
|
88
94
|
|
|
89
95
|
val printed = runCatching { stdout.readText() }.getOrDefault("")
|
|
90
96
|
stdout.delete()
|
|
@@ -99,11 +105,13 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
99
105
|
onSessionCreated: ((sessionId: Double) -> Unit)?,
|
|
100
106
|
): Promise<FFmpegSessionResult> {
|
|
101
107
|
val sessionId = nextSession()
|
|
108
|
+
registry.register(sessionId, SessionRegistry.Kind.PROBE)
|
|
102
109
|
onSessionCreated?.invoke(sessionId)
|
|
103
110
|
|
|
104
111
|
return runOnFfmpegThread {
|
|
105
112
|
val startedAt = System.currentTimeMillis()
|
|
106
|
-
val (returnCode, report) = runProbe(arguments_, onLog)
|
|
113
|
+
val (returnCode, report) = runProbe(arguments_, onLog, sessionId.toLong())
|
|
114
|
+
registry.finish(sessionId, returnCode)
|
|
107
115
|
result(sessionId, returnCode, report, System.currentTimeMillis() - startedAt)
|
|
108
116
|
}
|
|
109
117
|
}
|
|
@@ -141,6 +149,7 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
141
149
|
private fun runProbe(
|
|
142
150
|
arguments: Array<String>,
|
|
143
151
|
onLog: ((message: String) -> Unit)?,
|
|
152
|
+
sessionId: Long = 0,
|
|
144
153
|
): Pair<Int, String> {
|
|
145
154
|
val destination = File.createTempFile("munim-ffprobe", ".txt")
|
|
146
155
|
try {
|
|
@@ -149,7 +158,7 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
149
158
|
statisticsSink = null,
|
|
150
159
|
)
|
|
151
160
|
val returnCode =
|
|
152
|
-
FFmpegNative.nativeExecuteProbe(arguments, destination.absolutePath, session)
|
|
161
|
+
FFmpegNative.nativeExecuteProbe(arguments, destination.absolutePath, session, sessionId)
|
|
153
162
|
|
|
154
163
|
val report = destination.readText()
|
|
155
164
|
return returnCode to report.ifEmpty { session.output }
|
|
@@ -158,17 +167,19 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
158
167
|
}
|
|
159
168
|
}
|
|
160
169
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
sessionId
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"Invalid FFmpeg session ID: $sessionId. Expected a positive safe integer."
|
|
170
|
-
}
|
|
170
|
+
private fun validate(sessionId: Double) {
|
|
171
|
+
require(
|
|
172
|
+
sessionId.isFinite() &&
|
|
173
|
+
sessionId > 0 &&
|
|
174
|
+
sessionId % 1.0 == 0.0 &&
|
|
175
|
+
sessionId <= 9_007_199_254_740_991.0,
|
|
176
|
+
) {
|
|
177
|
+
"Invalid FFmpeg session ID: $sessionId. Expected a positive safe integer."
|
|
171
178
|
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
override fun cancel(sessionId: Double?) {
|
|
182
|
+
if (sessionId != null) validate(sessionId)
|
|
172
183
|
// Only one execution runs at a time, so a targeted cancel and cancelAll()
|
|
173
184
|
// are the same operation.
|
|
174
185
|
FFmpegNative.nativeCancel()
|
|
@@ -178,6 +189,36 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
178
189
|
FFmpegNative.nativeCancel()
|
|
179
190
|
}
|
|
180
191
|
|
|
192
|
+
override fun pause(sessionId: Double): Boolean {
|
|
193
|
+
validate(sessionId)
|
|
194
|
+
if (registry.activeKind(sessionId) != SessionRegistry.Kind.EXECUTE) return false
|
|
195
|
+
FFmpegNative.nativePause(sessionId.toLong())
|
|
196
|
+
// The run may have ended between the check and the pause.
|
|
197
|
+
if (registry.activeKind(sessionId) == null) {
|
|
198
|
+
FFmpegNative.nativeResume(sessionId.toLong())
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
return true
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
override fun resume(sessionId: Double): Boolean {
|
|
205
|
+
validate(sessionId)
|
|
206
|
+
return FFmpegNative.nativeResume(sessionId.toLong())
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
override fun getSessionState(sessionId: Double): FFmpegSessionState {
|
|
210
|
+
validate(sessionId)
|
|
211
|
+
registry.finishedState(sessionId)?.let { return it }
|
|
212
|
+
if (registry.activeKind(sessionId) == null) return FFmpegSessionState.UNKNOWN
|
|
213
|
+
val id = sessionId.toLong()
|
|
214
|
+
if (FFmpegNative.nativeIsPaused(id)) return FFmpegSessionState.PAUSED
|
|
215
|
+
return if (FFmpegNative.nativeRunningSession() == id) {
|
|
216
|
+
FFmpegSessionState.RUNNING
|
|
217
|
+
} else {
|
|
218
|
+
FFmpegSessionState.QUEUED
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
181
222
|
private companion object {
|
|
182
223
|
private val threadCounter = AtomicInteger(0)
|
|
183
224
|
|
|
@@ -191,3 +232,43 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
|
|
|
191
232
|
)
|
|
192
233
|
}
|
|
193
234
|
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* What the module remembers about each session id. Whether an unfinished
|
|
238
|
+
* session is queued, running or paused is asked of the native core, which is
|
|
239
|
+
* the only place that knows.
|
|
240
|
+
*/
|
|
241
|
+
private class SessionRegistry {
|
|
242
|
+
enum class Kind { EXECUTE, PROBE }
|
|
243
|
+
|
|
244
|
+
private val kinds = HashMap<Double, Kind>()
|
|
245
|
+
private val finished = LinkedHashMap<Double, FFmpegSessionState>()
|
|
246
|
+
|
|
247
|
+
@Synchronized
|
|
248
|
+
fun register(sessionId: Double, kind: Kind) {
|
|
249
|
+
kinds[sessionId] = kind
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@Synchronized
|
|
253
|
+
fun finish(sessionId: Double, returnCode: Int) {
|
|
254
|
+
kinds.remove(sessionId)
|
|
255
|
+
finished[sessionId] = when (returnCode) {
|
|
256
|
+
FFmpegNative.CANCELLED -> FFmpegSessionState.CANCELLED
|
|
257
|
+
0 -> FFmpegSessionState.COMPLETED
|
|
258
|
+
else -> FFmpegSessionState.FAILED
|
|
259
|
+
}
|
|
260
|
+
// Finished sessions are forgotten, oldest first, beyond this many.
|
|
261
|
+
while (finished.size > FINISHED_LIMIT) finished.remove(finished.keys.first())
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** null once finished or never issued. */
|
|
265
|
+
@Synchronized
|
|
266
|
+
fun activeKind(sessionId: Double): Kind? = kinds[sessionId]
|
|
267
|
+
|
|
268
|
+
@Synchronized
|
|
269
|
+
fun finishedState(sessionId: Double): FFmpegSessionState? = finished[sessionId]
|
|
270
|
+
|
|
271
|
+
private companion object {
|
|
272
|
+
const val FINISHED_LIMIT = 512
|
|
273
|
+
}
|
|
274
|
+
}
|
|
@@ -55,8 +55,57 @@ private let installCallbacks: Void = munim_ffmpeg_set_callbacks({ context, messa
|
|
|
55
55
|
session.onStatistics?(timeMs, sizeBytes, bitrate, speed, frame, fps, quality)
|
|
56
56
|
}, nil)
|
|
57
57
|
|
|
58
|
+
/// What the module remembers about each session id. Whether an unfinished
|
|
59
|
+
/// session is queued, running or paused is asked of the C core, which is the
|
|
60
|
+
/// only place that knows.
|
|
61
|
+
private final class SessionRegistry {
|
|
62
|
+
enum Kind { case execute, probe }
|
|
63
|
+
|
|
64
|
+
private var kinds: [Double: Kind] = [:]
|
|
65
|
+
private var finished: [Double: FFmpegSessionState] = [:]
|
|
66
|
+
private var finishedOrder: [Double] = []
|
|
67
|
+
private let lock = NSLock()
|
|
68
|
+
/// Finished sessions are forgotten, oldest first, beyond this many.
|
|
69
|
+
private let finishedLimit = 512
|
|
70
|
+
|
|
71
|
+
func register(_ sessionId: Double, _ kind: Kind) {
|
|
72
|
+
lock.lock()
|
|
73
|
+
kinds[sessionId] = kind
|
|
74
|
+
lock.unlock()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func finish(_ sessionId: Double, returnCode: Int32) {
|
|
78
|
+
let state: FFmpegSessionState =
|
|
79
|
+
returnCode == Int32(MUNIM_FFMPEG_CANCELLED) ? .cancelled
|
|
80
|
+
: returnCode == 0 ? .completed
|
|
81
|
+
: .failed
|
|
82
|
+
lock.lock()
|
|
83
|
+
kinds[sessionId] = nil
|
|
84
|
+
finished[sessionId] = state
|
|
85
|
+
finishedOrder.append(sessionId)
|
|
86
|
+
if finishedOrder.count > finishedLimit {
|
|
87
|
+
finished[finishedOrder.removeFirst()] = nil
|
|
88
|
+
}
|
|
89
|
+
lock.unlock()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// nil once finished or never issued.
|
|
93
|
+
func activeKind(_ sessionId: Double) -> Kind? {
|
|
94
|
+
lock.lock()
|
|
95
|
+
defer { lock.unlock() }
|
|
96
|
+
return kinds[sessionId]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func finishedState(_ sessionId: Double) -> FFmpegSessionState? {
|
|
100
|
+
lock.lock()
|
|
101
|
+
defer { lock.unlock() }
|
|
102
|
+
return finished[sessionId]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
58
106
|
final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
59
107
|
private var sessionCounter: Double = 0
|
|
108
|
+
private let registry = SessionRegistry()
|
|
60
109
|
|
|
61
110
|
var ffmpegVersion: String {
|
|
62
111
|
String(cString: munim_ffmpeg_version())
|
|
@@ -99,6 +148,7 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
99
148
|
) throws -> Promise<FFmpegSessionResult> {
|
|
100
149
|
let promise = Promise<FFmpegSessionResult>()
|
|
101
150
|
let sessionId = nextSession()
|
|
151
|
+
registry.register(sessionId, .execute)
|
|
102
152
|
onSessionCreated?(sessionId)
|
|
103
153
|
|
|
104
154
|
executionQueue.async {
|
|
@@ -112,14 +162,18 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
112
162
|
|
|
113
163
|
let returnCode = withExtendedLifetime(session) {
|
|
114
164
|
withArrayOfCStrings(arguments_) { argv in
|
|
115
|
-
|
|
165
|
+
munim_ffmpeg_execute_session(
|
|
116
166
|
Int32(arguments_.count),
|
|
117
167
|
argv,
|
|
118
168
|
printedPath,
|
|
119
|
-
Unmanaged.passUnretained(session).toOpaque()
|
|
169
|
+
Unmanaged.passUnretained(session).toOpaque(),
|
|
170
|
+
Int64(sessionId)
|
|
120
171
|
)
|
|
121
172
|
}
|
|
122
173
|
}
|
|
174
|
+
// A pause that raced the end of the run would otherwise stay listed.
|
|
175
|
+
_ = munim_ffmpeg_resume(Int64(sessionId))
|
|
176
|
+
self.registry.finish(sessionId, returnCode: returnCode)
|
|
123
177
|
|
|
124
178
|
let printed = (try? String(contentsOfFile: printedPath, encoding: .utf8)) ?? ""
|
|
125
179
|
try? FileManager.default.removeItem(atPath: printedPath)
|
|
@@ -144,11 +198,13 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
144
198
|
) throws -> Promise<FFmpegSessionResult> {
|
|
145
199
|
let promise = Promise<FFmpegSessionResult>()
|
|
146
200
|
let sessionId = nextSession()
|
|
201
|
+
registry.register(sessionId, .probe)
|
|
147
202
|
onSessionCreated?(sessionId)
|
|
148
203
|
|
|
149
204
|
executionQueue.async {
|
|
150
205
|
let startedAt = Date()
|
|
151
|
-
let (returnCode, report) = Self.runProbe(arguments_, onLog: onLog)
|
|
206
|
+
let (returnCode, report) = Self.runProbe(arguments_, onLog: onLog, sessionId: sessionId)
|
|
207
|
+
self.registry.finish(sessionId, returnCode: returnCode)
|
|
152
208
|
promise.resolve(
|
|
153
209
|
withResult: self.result(
|
|
154
210
|
sessionId: sessionId,
|
|
@@ -195,7 +251,8 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
195
251
|
/// so it is pointed at a temporary file with `-o` and read back.
|
|
196
252
|
private static func runProbe(
|
|
197
253
|
_ arguments: [String],
|
|
198
|
-
onLog: ((String) -> Void)
|
|
254
|
+
onLog: ((String) -> Void)?,
|
|
255
|
+
sessionId: Double = 0
|
|
199
256
|
) -> (Int32, String) {
|
|
200
257
|
let destination = temporaryFile()
|
|
201
258
|
|
|
@@ -204,11 +261,12 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
204
261
|
|
|
205
262
|
let returnCode = withExtendedLifetime(session) {
|
|
206
263
|
withArrayOfCStrings(arguments) { argv in
|
|
207
|
-
|
|
264
|
+
munim_ffmpeg_probe_session(
|
|
208
265
|
Int32(arguments.count),
|
|
209
266
|
argv,
|
|
210
267
|
destination,
|
|
211
|
-
Unmanaged.passUnretained(session).toOpaque()
|
|
268
|
+
Unmanaged.passUnretained(session).toOpaque(),
|
|
269
|
+
Int64(sessionId)
|
|
212
270
|
)
|
|
213
271
|
}
|
|
214
272
|
}
|
|
@@ -219,16 +277,20 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
219
277
|
return (returnCode, report.isEmpty ? session.output : report)
|
|
220
278
|
}
|
|
221
279
|
|
|
280
|
+
private static func validate(_ sessionId: Double) throws {
|
|
281
|
+
guard
|
|
282
|
+
sessionId.isFinite,
|
|
283
|
+
sessionId > 0,
|
|
284
|
+
sessionId.rounded(.towardZero) == sessionId,
|
|
285
|
+
sessionId <= 9_007_199_254_740_991
|
|
286
|
+
else {
|
|
287
|
+
throw MunimFfmpegError.invalidSessionId(sessionId)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
222
291
|
func cancel(sessionId: Double?) throws {
|
|
223
292
|
if let sessionId {
|
|
224
|
-
|
|
225
|
-
sessionId.isFinite,
|
|
226
|
-
sessionId > 0,
|
|
227
|
-
sessionId.rounded(.towardZero) == sessionId,
|
|
228
|
-
sessionId <= 9_007_199_254_740_991
|
|
229
|
-
else {
|
|
230
|
-
throw MunimFfmpegError.invalidSessionId(sessionId)
|
|
231
|
-
}
|
|
293
|
+
try Self.validate(sessionId)
|
|
232
294
|
}
|
|
233
295
|
// One execution runs at a time, so cancelling a specific session and
|
|
234
296
|
// cancelling everything are the same operation.
|
|
@@ -238,6 +300,32 @@ final class HybridMunimFfmpeg: HybridMunimFfmpegSpec {
|
|
|
238
300
|
func cancelAll() throws {
|
|
239
301
|
munim_ffmpeg_cancel()
|
|
240
302
|
}
|
|
303
|
+
|
|
304
|
+
func pause(sessionId: Double) throws -> Bool {
|
|
305
|
+
try Self.validate(sessionId)
|
|
306
|
+
guard registry.activeKind(sessionId) == .execute else { return false }
|
|
307
|
+
_ = munim_ffmpeg_pause(Int64(sessionId))
|
|
308
|
+
// The run may have ended between the check and the pause; the core drops
|
|
309
|
+
// the pause of a finished session, so report what actually happened.
|
|
310
|
+
if registry.activeKind(sessionId) == nil {
|
|
311
|
+
_ = munim_ffmpeg_resume(Int64(sessionId))
|
|
312
|
+
return false
|
|
313
|
+
}
|
|
314
|
+
return true
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
func resume(sessionId: Double) throws -> Bool {
|
|
318
|
+
try Self.validate(sessionId)
|
|
319
|
+
return munim_ffmpeg_resume(Int64(sessionId)) != 0
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
func getSessionState(sessionId: Double) throws -> FFmpegSessionState {
|
|
323
|
+
try Self.validate(sessionId)
|
|
324
|
+
if let state = registry.finishedState(sessionId) { return state }
|
|
325
|
+
guard registry.activeKind(sessionId) != nil else { return .unknown }
|
|
326
|
+
if munim_ffmpeg_is_paused(Int64(sessionId)) != 0 { return .paused }
|
|
327
|
+
return munim_ffmpeg_running_session() == Int64(sessionId) ? .running : .queued
|
|
328
|
+
}
|
|
241
329
|
}
|
|
242
330
|
|
|
243
331
|
/// Builds a C `argv` that stays valid for the duration of `body`.
|
package/ios/munim_ffmpeg_core.h
CHANGED
|
@@ -61,6 +61,41 @@ int munim_ffmpeg_probe_ctx(int argc, const char *const *argv,
|
|
|
61
61
|
*/
|
|
62
62
|
void munim_ffmpeg_cancel(void);
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Like munim_ffmpeg_execute_ctx, for a run identified by `session_id` (> 0) so
|
|
66
|
+
* it can be paused and resumed. munim_ffmpeg_execute_ctx runs with id 0, which
|
|
67
|
+
* cannot be paused.
|
|
68
|
+
*/
|
|
69
|
+
int munim_ffmpeg_execute_session(int argc, const char *const *argv,
|
|
70
|
+
const char *stdout_path, void *context,
|
|
71
|
+
long long session_id);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Pauses the execution with `session_id`. If it is running, its input threads
|
|
75
|
+
* stop reading and the rest of the pipeline idles once it has drained; if it is
|
|
76
|
+
* still queued, it will start paused. Output files stay open and
|
|
77
|
+
* munim_ffmpeg_resume() continues exactly where it stopped. Cancelling works
|
|
78
|
+
* as usual and also clears every pause. Returns 1 if the session is running.
|
|
79
|
+
*/
|
|
80
|
+
int munim_ffmpeg_pause(long long session_id);
|
|
81
|
+
|
|
82
|
+
/** Resumes `session_id`. Returns 1 if it was paused. */
|
|
83
|
+
int munim_ffmpeg_resume(long long session_id);
|
|
84
|
+
|
|
85
|
+
/** Returns 1 while `session_id` is paused. */
|
|
86
|
+
int munim_ffmpeg_is_paused(long long session_id);
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Id of the execution or probe currently running, or 0. Probes started through
|
|
90
|
+
* munim_ffmpeg_probe_session report their id here but cannot be paused.
|
|
91
|
+
*/
|
|
92
|
+
long long munim_ffmpeg_running_session(void);
|
|
93
|
+
|
|
94
|
+
/** Like munim_ffmpeg_probe_ctx, for a run identified by `session_id`. */
|
|
95
|
+
int munim_ffmpeg_probe_session(int argc, const char *const *argv,
|
|
96
|
+
const char *output_path, void *context,
|
|
97
|
+
long long session_id);
|
|
98
|
+
|
|
64
99
|
/** Return code reported when a run was cancelled. */
|
|
65
100
|
#define MUNIM_FFMPEG_CANCELLED 255
|
|
66
101
|
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { FFmpegLogCallback, FFmpegSessionResult, FFmpegSessionCreatedCallback, FFmpegStatisticsCallback, MunimFfmpeg as MunimFfmpegSpec } from './specs/MunimFfmpeg.nitro';
|
|
1
|
+
import type { FFmpegLogCallback, FFmpegSessionResult, FFmpegSessionCreatedCallback, FFmpegSessionState, FFmpegStatisticsCallback, MunimFfmpeg as MunimFfmpegSpec } from './specs/MunimFfmpeg.nitro';
|
|
2
2
|
declare const MunimFfmpeg: MunimFfmpegSpec;
|
|
3
|
-
export type { FFmpegLogCallback, FFmpegSessionResult, FFmpegSessionCreatedCallback, FFmpegStatisticsCallback, MunimFfmpegSpec, };
|
|
3
|
+
export type { FFmpegLogCallback, FFmpegSessionResult, FFmpegSessionCreatedCallback, FFmpegSessionState, FFmpegStatisticsCallback, MunimFfmpegSpec, };
|
|
4
4
|
/**
|
|
5
5
|
* Converts a `file://` URI into the plain path FFmpeg expects.
|
|
6
6
|
*
|
|
@@ -98,6 +98,20 @@ export declare function getMediaInformation(path: string): Promise<MediaInformat
|
|
|
98
98
|
export declare function getMediaDuration(information: MediaInformation): number | undefined;
|
|
99
99
|
export declare function cancel(sessionId?: number): void;
|
|
100
100
|
export declare function cancelAll(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Pauses a session started with `execute()`, whether it is running or still
|
|
103
|
+
* queued. FFmpeg stops reading its inputs and the rest of the pipeline idles
|
|
104
|
+
* once it has drained, so a paused session costs no CPU. Output files stay
|
|
105
|
+
* open, and `resume()` continues from exactly where it stopped: nothing is
|
|
106
|
+
* re-encoded or skipped, and the `execute()` promise settles once, when the
|
|
107
|
+
* work finishes. `cancel()` still works while paused.
|
|
108
|
+
*
|
|
109
|
+
* Returns false if the session is unknown, has finished, or is a probe.
|
|
110
|
+
*/
|
|
111
|
+
export declare function pause(sessionId: number): boolean;
|
|
112
|
+
/** Resumes a paused session. Returns false if it was not paused. */
|
|
113
|
+
export declare function resume(sessionId: number): boolean;
|
|
114
|
+
export declare function getSessionState(sessionId: number): FFmpegSessionState;
|
|
101
115
|
export declare function getFFmpegVersion(): string;
|
|
102
116
|
/** Encoder names the bundled FFmpeg build can write, e.g. `libx264`. */
|
|
103
117
|
export declare function listEncoders(): Promise<string[]>;
|
package/lib/index.js
CHANGED
|
@@ -46,6 +46,26 @@ export function cancel(sessionId) {
|
|
|
46
46
|
export function cancelAll() {
|
|
47
47
|
MunimFfmpeg.cancelAll();
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Pauses a session started with `execute()`, whether it is running or still
|
|
51
|
+
* queued. FFmpeg stops reading its inputs and the rest of the pipeline idles
|
|
52
|
+
* once it has drained, so a paused session costs no CPU. Output files stay
|
|
53
|
+
* open, and `resume()` continues from exactly where it stopped: nothing is
|
|
54
|
+
* re-encoded or skipped, and the `execute()` promise settles once, when the
|
|
55
|
+
* work finishes. `cancel()` still works while paused.
|
|
56
|
+
*
|
|
57
|
+
* Returns false if the session is unknown, has finished, or is a probe.
|
|
58
|
+
*/
|
|
59
|
+
export function pause(sessionId) {
|
|
60
|
+
return MunimFfmpeg.pause(sessionId);
|
|
61
|
+
}
|
|
62
|
+
/** Resumes a paused session. Returns false if it was not paused. */
|
|
63
|
+
export function resume(sessionId) {
|
|
64
|
+
return MunimFfmpeg.resume(sessionId);
|
|
65
|
+
}
|
|
66
|
+
export function getSessionState(sessionId) {
|
|
67
|
+
return MunimFfmpeg.getSessionState(sessionId);
|
|
68
|
+
}
|
|
49
69
|
export function getFFmpegVersion() {
|
|
50
70
|
return MunimFfmpeg.ffmpegVersion;
|
|
51
71
|
}
|
|
@@ -9,6 +9,12 @@ export type FFmpegSessionResult = {
|
|
|
9
9
|
output: string;
|
|
10
10
|
failStackTrace?: string;
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Where a session is in its life. `queued` sessions wait for the one running
|
|
14
|
+
* ahead of them (FFmpeg runs one execution at a time); `unknown` means the id
|
|
15
|
+
* was never issued or its record has been dropped.
|
|
16
|
+
*/
|
|
17
|
+
export type FFmpegSessionState = 'queued' | 'running' | 'paused' | 'completed' | 'failed' | 'cancelled' | 'unknown';
|
|
12
18
|
export type FFmpegLogCallback = (message: string) => void;
|
|
13
19
|
export type FFmpegSessionCreatedCallback = (sessionId: number) => void;
|
|
14
20
|
export type FFmpegStatisticsCallback = (timeMs: number, sizeBytes: number, bitrateKbits: number, speed: number, videoFrameNumber: number, fps: number, quality: number) => void;
|
|
@@ -22,4 +28,13 @@ export interface MunimFfmpeg extends HybridObject<{
|
|
|
22
28
|
getMediaInformation(path: string): Promise<string>;
|
|
23
29
|
cancel(sessionId?: number): void;
|
|
24
30
|
cancelAll(): void;
|
|
31
|
+
/**
|
|
32
|
+
* Pauses an `execute()` session, running or still queued. Input stops being
|
|
33
|
+
* read and the pipeline idles; output files stay open. Returns false if the
|
|
34
|
+
* session is unknown, finished, or a probe (probes cannot be paused).
|
|
35
|
+
*/
|
|
36
|
+
pause(sessionId: number): boolean;
|
|
37
|
+
/** Resumes a paused session. Returns false if it was not paused. */
|
|
38
|
+
resume(sessionId: number): boolean;
|
|
39
|
+
getSessionState(sessionId: number): FFmpegSessionState;
|
|
25
40
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// JFFmpegSessionState.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright Β© Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <fbjni/fbjni.h>
|
|
11
|
+
#include "FFmpegSessionState.hpp"
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro::munimffmpeg {
|
|
14
|
+
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The C++ JNI bridge between the C++ enum "FFmpegSessionState" and the Kotlin enum "FFmpegSessionState".
|
|
19
|
+
*/
|
|
20
|
+
struct JFFmpegSessionState final: public jni::JavaClass<JFFmpegSessionState> {
|
|
21
|
+
public:
|
|
22
|
+
static constexpr auto kJavaDescriptor = "Lcom/margelo/nitro/munimffmpeg/FFmpegSessionState;";
|
|
23
|
+
|
|
24
|
+
public:
|
|
25
|
+
/**
|
|
26
|
+
* Convert this Java/Kotlin-based enum to the C++ enum FFmpegSessionState.
|
|
27
|
+
*/
|
|
28
|
+
[[maybe_unused]]
|
|
29
|
+
[[nodiscard]]
|
|
30
|
+
FFmpegSessionState toCpp() const {
|
|
31
|
+
static const auto clazz = javaClassStatic();
|
|
32
|
+
static const auto fieldOrdinal = clazz->getField<int>("value");
|
|
33
|
+
int ordinal = this->getFieldValue(fieldOrdinal);
|
|
34
|
+
return static_cast<FFmpegSessionState>(ordinal);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public:
|
|
38
|
+
/**
|
|
39
|
+
* Create a Java/Kotlin-based enum with the given C++ enum's value.
|
|
40
|
+
*/
|
|
41
|
+
[[maybe_unused]]
|
|
42
|
+
static jni::alias_ref<JFFmpegSessionState> fromCpp(FFmpegSessionState value) {
|
|
43
|
+
static const auto clazz = javaClassStatic();
|
|
44
|
+
switch (value) {
|
|
45
|
+
case FFmpegSessionState::QUEUED:
|
|
46
|
+
static const auto fieldQUEUED = clazz->getStaticField<JFFmpegSessionState>("QUEUED");
|
|
47
|
+
return clazz->getStaticFieldValue(fieldQUEUED);
|
|
48
|
+
case FFmpegSessionState::RUNNING:
|
|
49
|
+
static const auto fieldRUNNING = clazz->getStaticField<JFFmpegSessionState>("RUNNING");
|
|
50
|
+
return clazz->getStaticFieldValue(fieldRUNNING);
|
|
51
|
+
case FFmpegSessionState::PAUSED:
|
|
52
|
+
static const auto fieldPAUSED = clazz->getStaticField<JFFmpegSessionState>("PAUSED");
|
|
53
|
+
return clazz->getStaticFieldValue(fieldPAUSED);
|
|
54
|
+
case FFmpegSessionState::COMPLETED:
|
|
55
|
+
static const auto fieldCOMPLETED = clazz->getStaticField<JFFmpegSessionState>("COMPLETED");
|
|
56
|
+
return clazz->getStaticFieldValue(fieldCOMPLETED);
|
|
57
|
+
case FFmpegSessionState::FAILED:
|
|
58
|
+
static const auto fieldFAILED = clazz->getStaticField<JFFmpegSessionState>("FAILED");
|
|
59
|
+
return clazz->getStaticFieldValue(fieldFAILED);
|
|
60
|
+
case FFmpegSessionState::CANCELLED:
|
|
61
|
+
static const auto fieldCANCELLED = clazz->getStaticField<JFFmpegSessionState>("CANCELLED");
|
|
62
|
+
return clazz->getStaticFieldValue(fieldCANCELLED);
|
|
63
|
+
case FFmpegSessionState::UNKNOWN:
|
|
64
|
+
static const auto fieldUNKNOWN = clazz->getStaticField<JFFmpegSessionState>("UNKNOWN");
|
|
65
|
+
return clazz->getStaticFieldValue(fieldUNKNOWN);
|
|
66
|
+
default:
|
|
67
|
+
std::string stringValue = std::to_string(static_cast<int>(value));
|
|
68
|
+
throw std::invalid_argument("Invalid enum value (" + stringValue + "!");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
} // namespace margelo::nitro::munimffmpeg
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
// Forward declaration of `FFmpegSessionResult` to properly resolve imports.
|
|
11
11
|
namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
12
|
+
// Forward declaration of `FFmpegSessionState` to properly resolve imports.
|
|
13
|
+
namespace margelo::nitro::munimffmpeg { enum class FFmpegSessionState; }
|
|
12
14
|
|
|
13
15
|
#include <string>
|
|
14
16
|
#include "FFmpegSessionResult.hpp"
|
|
@@ -16,6 +18,8 @@ namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
|
16
18
|
#include <NitroModules/JPromise.hpp>
|
|
17
19
|
#include "JFFmpegSessionResult.hpp"
|
|
18
20
|
#include <optional>
|
|
21
|
+
#include "FFmpegSessionState.hpp"
|
|
22
|
+
#include "JFFmpegSessionState.hpp"
|
|
19
23
|
#include <vector>
|
|
20
24
|
#include <functional>
|
|
21
25
|
#include "JFunc_void_std__string.hpp"
|
|
@@ -134,5 +138,20 @@ namespace margelo::nitro::munimffmpeg {
|
|
|
134
138
|
static const auto method = _javaPart->javaClassStatic()->getMethod<void()>("cancelAll");
|
|
135
139
|
method(_javaPart);
|
|
136
140
|
}
|
|
141
|
+
bool JHybridMunimFfmpegSpec::pause(double sessionId) {
|
|
142
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean(double /* sessionId */)>("pause");
|
|
143
|
+
auto __result = method(_javaPart, sessionId);
|
|
144
|
+
return static_cast<bool>(__result);
|
|
145
|
+
}
|
|
146
|
+
bool JHybridMunimFfmpegSpec::resume(double sessionId) {
|
|
147
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jboolean(double /* sessionId */)>("resume");
|
|
148
|
+
auto __result = method(_javaPart, sessionId);
|
|
149
|
+
return static_cast<bool>(__result);
|
|
150
|
+
}
|
|
151
|
+
FFmpegSessionState JHybridMunimFfmpegSpec::getSessionState(double sessionId) {
|
|
152
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JFFmpegSessionState>(double /* sessionId */)>("getSessionState");
|
|
153
|
+
auto __result = method(_javaPart, sessionId);
|
|
154
|
+
return __result->toCpp();
|
|
155
|
+
}
|
|
137
156
|
|
|
138
157
|
} // namespace margelo::nitro::munimffmpeg
|
|
@@ -59,6 +59,9 @@ namespace margelo::nitro::munimffmpeg {
|
|
|
59
59
|
std::shared_ptr<Promise<std::string>> getMediaInformation(const std::string& path) override;
|
|
60
60
|
void cancel(std::optional<double> sessionId) override;
|
|
61
61
|
void cancelAll() override;
|
|
62
|
+
bool pause(double sessionId) override;
|
|
63
|
+
bool resume(double sessionId) override;
|
|
64
|
+
FFmpegSessionState getSessionState(double sessionId) override;
|
|
62
65
|
|
|
63
66
|
private:
|
|
64
67
|
jni::global_ref<JHybridMunimFfmpegSpec::JavaPart> _javaPart;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// FFmpegSessionState.kt
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright Β© Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
package com.margelo.nitro.munimffmpeg
|
|
9
|
+
|
|
10
|
+
import androidx.annotation.Keep
|
|
11
|
+
import com.facebook.proguard.annotations.DoNotStrip
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents the JavaScript enum/union "FFmpegSessionState".
|
|
15
|
+
*/
|
|
16
|
+
@DoNotStrip
|
|
17
|
+
@Keep
|
|
18
|
+
enum class FFmpegSessionState(@DoNotStrip @Keep val value: Int) {
|
|
19
|
+
QUEUED(0),
|
|
20
|
+
RUNNING(1),
|
|
21
|
+
PAUSED(2),
|
|
22
|
+
COMPLETED(3),
|
|
23
|
+
FAILED(4),
|
|
24
|
+
CANCELLED(5),
|
|
25
|
+
UNKNOWN(6);
|
|
26
|
+
|
|
27
|
+
companion object
|
|
28
|
+
}
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimffmpeg/HybridMunimFfmpegSpec.kt
CHANGED
|
@@ -61,6 +61,18 @@ abstract class HybridMunimFfmpegSpec: HybridObject() {
|
|
|
61
61
|
@DoNotStrip
|
|
62
62
|
@Keep
|
|
63
63
|
abstract fun cancelAll(): Unit
|
|
64
|
+
|
|
65
|
+
@DoNotStrip
|
|
66
|
+
@Keep
|
|
67
|
+
abstract fun pause(sessionId: Double): Boolean
|
|
68
|
+
|
|
69
|
+
@DoNotStrip
|
|
70
|
+
@Keep
|
|
71
|
+
abstract fun resume(sessionId: Double): Boolean
|
|
72
|
+
|
|
73
|
+
@DoNotStrip
|
|
74
|
+
@Keep
|
|
75
|
+
abstract fun getSessionState(sessionId: Double): FFmpegSessionState
|
|
64
76
|
|
|
65
77
|
// Default implementation of `HybridObject.toString()`
|
|
66
78
|
override fun toString(): String {
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
// Forward declarations of C++ defined types
|
|
11
11
|
// Forward declaration of `FFmpegSessionResult` to properly resolve imports.
|
|
12
12
|
namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
13
|
+
// Forward declaration of `FFmpegSessionState` to properly resolve imports.
|
|
14
|
+
namespace margelo::nitro::munimffmpeg { enum class FFmpegSessionState; }
|
|
13
15
|
// Forward declaration of `HybridMunimFfmpegSpec` to properly resolve imports.
|
|
14
16
|
namespace margelo::nitro::munimffmpeg { class HybridMunimFfmpegSpec; }
|
|
15
17
|
|
|
@@ -19,6 +21,7 @@ namespace NitroMunimFfmpeg { class HybridMunimFfmpegSpec_cxx; }
|
|
|
19
21
|
|
|
20
22
|
// Include C++ defined types
|
|
21
23
|
#include "FFmpegSessionResult.hpp"
|
|
24
|
+
#include "FFmpegSessionState.hpp"
|
|
22
25
|
#include "HybridMunimFfmpegSpec.hpp"
|
|
23
26
|
#include <NitroModules/Promise.hpp>
|
|
24
27
|
#include <NitroModules/PromiseHolder.hpp>
|
|
@@ -294,5 +297,23 @@ namespace margelo::nitro::munimffmpeg::bridge::swift {
|
|
|
294
297
|
inline Result_void_ create_Result_void_(const std::exception_ptr& error) noexcept {
|
|
295
298
|
return Result<void>::withError(error);
|
|
296
299
|
}
|
|
300
|
+
|
|
301
|
+
// pragma MARK: Result<bool>
|
|
302
|
+
using Result_bool_ = Result<bool>;
|
|
303
|
+
inline Result_bool_ create_Result_bool_(bool value) noexcept {
|
|
304
|
+
return Result<bool>::withValue(std::move(value));
|
|
305
|
+
}
|
|
306
|
+
inline Result_bool_ create_Result_bool_(const std::exception_ptr& error) noexcept {
|
|
307
|
+
return Result<bool>::withError(error);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// pragma MARK: Result<FFmpegSessionState>
|
|
311
|
+
using Result_FFmpegSessionState_ = Result<FFmpegSessionState>;
|
|
312
|
+
inline Result_FFmpegSessionState_ create_Result_FFmpegSessionState_(FFmpegSessionState value) noexcept {
|
|
313
|
+
return Result<FFmpegSessionState>::withValue(std::move(value));
|
|
314
|
+
}
|
|
315
|
+
inline Result_FFmpegSessionState_ create_Result_FFmpegSessionState_(const std::exception_ptr& error) noexcept {
|
|
316
|
+
return Result<FFmpegSessionState>::withError(error);
|
|
317
|
+
}
|
|
297
318
|
|
|
298
319
|
} // namespace margelo::nitro::munimffmpeg::bridge::swift
|
|
@@ -10,11 +10,14 @@
|
|
|
10
10
|
// Forward declarations of C++ defined types
|
|
11
11
|
// Forward declaration of `FFmpegSessionResult` to properly resolve imports.
|
|
12
12
|
namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
13
|
+
// Forward declaration of `FFmpegSessionState` to properly resolve imports.
|
|
14
|
+
namespace margelo::nitro::munimffmpeg { enum class FFmpegSessionState; }
|
|
13
15
|
// Forward declaration of `HybridMunimFfmpegSpec` to properly resolve imports.
|
|
14
16
|
namespace margelo::nitro::munimffmpeg { class HybridMunimFfmpegSpec; }
|
|
15
17
|
|
|
16
18
|
// Include C++ defined types
|
|
17
19
|
#include "FFmpegSessionResult.hpp"
|
|
20
|
+
#include "FFmpegSessionState.hpp"
|
|
18
21
|
#include "HybridMunimFfmpegSpec.hpp"
|
|
19
22
|
#include <NitroModules/Promise.hpp>
|
|
20
23
|
#include <NitroModules/Result.hpp>
|
|
@@ -14,6 +14,8 @@ namespace NitroMunimFfmpeg { class HybridMunimFfmpegSpec_cxx; }
|
|
|
14
14
|
|
|
15
15
|
// Forward declaration of `FFmpegSessionResult` to properly resolve imports.
|
|
16
16
|
namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
17
|
+
// Forward declaration of `FFmpegSessionState` to properly resolve imports.
|
|
18
|
+
namespace margelo::nitro::munimffmpeg { enum class FFmpegSessionState; }
|
|
17
19
|
|
|
18
20
|
#include <string>
|
|
19
21
|
#include "FFmpegSessionResult.hpp"
|
|
@@ -21,6 +23,7 @@ namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
|
21
23
|
#include <optional>
|
|
22
24
|
#include <vector>
|
|
23
25
|
#include <functional>
|
|
26
|
+
#include "FFmpegSessionState.hpp"
|
|
24
27
|
|
|
25
28
|
#include "NitroMunimFfmpeg-Swift-Cxx-Umbrella.hpp"
|
|
26
29
|
|
|
@@ -111,6 +114,30 @@ namespace margelo::nitro::munimffmpeg {
|
|
|
111
114
|
std::rethrow_exception(__result.error());
|
|
112
115
|
}
|
|
113
116
|
}
|
|
117
|
+
inline bool pause(double sessionId) override {
|
|
118
|
+
auto __result = _swiftPart.pause(std::forward<decltype(sessionId)>(sessionId));
|
|
119
|
+
if (__result.hasError()) [[unlikely]] {
|
|
120
|
+
std::rethrow_exception(__result.error());
|
|
121
|
+
}
|
|
122
|
+
auto __value = std::move(__result.value());
|
|
123
|
+
return __value;
|
|
124
|
+
}
|
|
125
|
+
inline bool resume(double sessionId) override {
|
|
126
|
+
auto __result = _swiftPart.resume(std::forward<decltype(sessionId)>(sessionId));
|
|
127
|
+
if (__result.hasError()) [[unlikely]] {
|
|
128
|
+
std::rethrow_exception(__result.error());
|
|
129
|
+
}
|
|
130
|
+
auto __value = std::move(__result.value());
|
|
131
|
+
return __value;
|
|
132
|
+
}
|
|
133
|
+
inline FFmpegSessionState getSessionState(double sessionId) override {
|
|
134
|
+
auto __result = _swiftPart.getSessionState(std::forward<decltype(sessionId)>(sessionId));
|
|
135
|
+
if (__result.hasError()) [[unlikely]] {
|
|
136
|
+
std::rethrow_exception(__result.error());
|
|
137
|
+
}
|
|
138
|
+
auto __value = std::move(__result.value());
|
|
139
|
+
return __value;
|
|
140
|
+
}
|
|
114
141
|
|
|
115
142
|
private:
|
|
116
143
|
NitroMunimFfmpeg::HybridMunimFfmpegSpec_cxx _swiftPart;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// FFmpegSessionState.swift
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright Β© Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Represents the JS union `FFmpegSessionState`, backed by a C++ enum.
|
|
10
|
+
*/
|
|
11
|
+
public typealias FFmpegSessionState = margelo.nitro.munimffmpeg.FFmpegSessionState
|
|
12
|
+
|
|
13
|
+
public extension FFmpegSessionState {
|
|
14
|
+
/**
|
|
15
|
+
* Get a FFmpegSessionState for the given String value, or
|
|
16
|
+
* return `nil` if the given value was invalid/unknown.
|
|
17
|
+
*/
|
|
18
|
+
init?(fromString string: String) {
|
|
19
|
+
switch string {
|
|
20
|
+
case "queued":
|
|
21
|
+
self = .queued
|
|
22
|
+
case "running":
|
|
23
|
+
self = .running
|
|
24
|
+
case "paused":
|
|
25
|
+
self = .paused
|
|
26
|
+
case "completed":
|
|
27
|
+
self = .completed
|
|
28
|
+
case "failed":
|
|
29
|
+
self = .failed
|
|
30
|
+
case "cancelled":
|
|
31
|
+
self = .cancelled
|
|
32
|
+
case "unknown":
|
|
33
|
+
self = .unknown
|
|
34
|
+
default:
|
|
35
|
+
return nil
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Get the String value this FFmpegSessionState represents.
|
|
41
|
+
*/
|
|
42
|
+
var stringValue: String {
|
|
43
|
+
switch self {
|
|
44
|
+
case .queued:
|
|
45
|
+
return "queued"
|
|
46
|
+
case .running:
|
|
47
|
+
return "running"
|
|
48
|
+
case .paused:
|
|
49
|
+
return "paused"
|
|
50
|
+
case .completed:
|
|
51
|
+
return "completed"
|
|
52
|
+
case .failed:
|
|
53
|
+
return "failed"
|
|
54
|
+
case .cancelled:
|
|
55
|
+
return "cancelled"
|
|
56
|
+
case .unknown:
|
|
57
|
+
return "unknown"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -18,6 +18,9 @@ public protocol HybridMunimFfmpegSpec_protocol: HybridObject {
|
|
|
18
18
|
func getMediaInformation(path: String) throws -> Promise<String>
|
|
19
19
|
func cancel(sessionId: Double?) throws -> Void
|
|
20
20
|
func cancelAll() throws -> Void
|
|
21
|
+
func pause(sessionId: Double) throws -> Bool
|
|
22
|
+
func resume(sessionId: Double) throws -> Bool
|
|
23
|
+
func getSessionState(sessionId: Double) throws -> FFmpegSessionState
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
public extension HybridMunimFfmpegSpec_protocol {
|
|
@@ -274,4 +274,40 @@ open class HybridMunimFfmpegSpec_cxx {
|
|
|
274
274
|
return bridge.create_Result_void_(__exceptionPtr)
|
|
275
275
|
}
|
|
276
276
|
}
|
|
277
|
+
|
|
278
|
+
@inline(__always)
|
|
279
|
+
public final func pause(sessionId: Double) -> bridge.Result_bool_ {
|
|
280
|
+
do {
|
|
281
|
+
let __result = try self.__implementation.pause(sessionId: sessionId)
|
|
282
|
+
let __resultCpp = __result
|
|
283
|
+
return bridge.create_Result_bool_(__resultCpp)
|
|
284
|
+
} catch (let __error) {
|
|
285
|
+
let __exceptionPtr = __error.toCpp()
|
|
286
|
+
return bridge.create_Result_bool_(__exceptionPtr)
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@inline(__always)
|
|
291
|
+
public final func resume(sessionId: Double) -> bridge.Result_bool_ {
|
|
292
|
+
do {
|
|
293
|
+
let __result = try self.__implementation.resume(sessionId: sessionId)
|
|
294
|
+
let __resultCpp = __result
|
|
295
|
+
return bridge.create_Result_bool_(__resultCpp)
|
|
296
|
+
} catch (let __error) {
|
|
297
|
+
let __exceptionPtr = __error.toCpp()
|
|
298
|
+
return bridge.create_Result_bool_(__exceptionPtr)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
@inline(__always)
|
|
303
|
+
public final func getSessionState(sessionId: Double) -> bridge.Result_FFmpegSessionState_ {
|
|
304
|
+
do {
|
|
305
|
+
let __result = try self.__implementation.getSessionState(sessionId: sessionId)
|
|
306
|
+
let __resultCpp = __result
|
|
307
|
+
return bridge.create_Result_FFmpegSessionState_(__resultCpp)
|
|
308
|
+
} catch (let __error) {
|
|
309
|
+
let __exceptionPtr = __error.toCpp()
|
|
310
|
+
return bridge.create_Result_FFmpegSessionState_(__exceptionPtr)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
277
313
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
///
|
|
2
|
+
/// FFmpegSessionState.hpp
|
|
3
|
+
/// This file was generated by nitrogen. DO NOT MODIFY THIS FILE.
|
|
4
|
+
/// https://github.com/mrousavy/nitro
|
|
5
|
+
/// Copyright Β© Marc Rousavy @ Margelo
|
|
6
|
+
///
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#if __has_include(<NitroModules/NitroHash.hpp>)
|
|
11
|
+
#include <NitroModules/NitroHash.hpp>
|
|
12
|
+
#else
|
|
13
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
14
|
+
#endif
|
|
15
|
+
#if __has_include(<NitroModules/JSIConverter.hpp>)
|
|
16
|
+
#include <NitroModules/JSIConverter.hpp>
|
|
17
|
+
#else
|
|
18
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
19
|
+
#endif
|
|
20
|
+
#if __has_include(<NitroModules/NitroDefines.hpp>)
|
|
21
|
+
#include <NitroModules/NitroDefines.hpp>
|
|
22
|
+
#else
|
|
23
|
+
#error NitroModules cannot be found! Are you sure you installed NitroModules properly?
|
|
24
|
+
#endif
|
|
25
|
+
|
|
26
|
+
namespace margelo::nitro::munimffmpeg {
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* An enum which can be represented as a JavaScript union (FFmpegSessionState).
|
|
30
|
+
*/
|
|
31
|
+
enum class FFmpegSessionState {
|
|
32
|
+
QUEUED SWIFT_NAME(queued) = 0,
|
|
33
|
+
RUNNING SWIFT_NAME(running) = 1,
|
|
34
|
+
PAUSED SWIFT_NAME(paused) = 2,
|
|
35
|
+
COMPLETED SWIFT_NAME(completed) = 3,
|
|
36
|
+
FAILED SWIFT_NAME(failed) = 4,
|
|
37
|
+
CANCELLED SWIFT_NAME(cancelled) = 5,
|
|
38
|
+
UNKNOWN SWIFT_NAME(unknown) = 6,
|
|
39
|
+
} CLOSED_ENUM;
|
|
40
|
+
|
|
41
|
+
} // namespace margelo::nitro::munimffmpeg
|
|
42
|
+
|
|
43
|
+
namespace margelo::nitro {
|
|
44
|
+
|
|
45
|
+
// C++ FFmpegSessionState <> JS FFmpegSessionState (union)
|
|
46
|
+
template <>
|
|
47
|
+
struct JSIConverter<margelo::nitro::munimffmpeg::FFmpegSessionState> final {
|
|
48
|
+
static inline margelo::nitro::munimffmpeg::FFmpegSessionState fromJSI(jsi::Runtime& runtime, const jsi::Value& arg) {
|
|
49
|
+
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, arg);
|
|
50
|
+
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
51
|
+
case hashString("queued"): return margelo::nitro::munimffmpeg::FFmpegSessionState::QUEUED;
|
|
52
|
+
case hashString("running"): return margelo::nitro::munimffmpeg::FFmpegSessionState::RUNNING;
|
|
53
|
+
case hashString("paused"): return margelo::nitro::munimffmpeg::FFmpegSessionState::PAUSED;
|
|
54
|
+
case hashString("completed"): return margelo::nitro::munimffmpeg::FFmpegSessionState::COMPLETED;
|
|
55
|
+
case hashString("failed"): return margelo::nitro::munimffmpeg::FFmpegSessionState::FAILED;
|
|
56
|
+
case hashString("cancelled"): return margelo::nitro::munimffmpeg::FFmpegSessionState::CANCELLED;
|
|
57
|
+
case hashString("unknown"): return margelo::nitro::munimffmpeg::FFmpegSessionState::UNKNOWN;
|
|
58
|
+
default: [[unlikely]]
|
|
59
|
+
throw std::invalid_argument("Cannot convert \"" + unionValue + "\" to enum FFmpegSessionState - invalid value!");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
static inline jsi::Value toJSI(jsi::Runtime& runtime, margelo::nitro::munimffmpeg::FFmpegSessionState arg) {
|
|
63
|
+
switch (arg) {
|
|
64
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::QUEUED: return JSIConverter<std::string>::toJSI(runtime, "queued");
|
|
65
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::RUNNING: return JSIConverter<std::string>::toJSI(runtime, "running");
|
|
66
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::PAUSED: return JSIConverter<std::string>::toJSI(runtime, "paused");
|
|
67
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::COMPLETED: return JSIConverter<std::string>::toJSI(runtime, "completed");
|
|
68
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::FAILED: return JSIConverter<std::string>::toJSI(runtime, "failed");
|
|
69
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::CANCELLED: return JSIConverter<std::string>::toJSI(runtime, "cancelled");
|
|
70
|
+
case margelo::nitro::munimffmpeg::FFmpegSessionState::UNKNOWN: return JSIConverter<std::string>::toJSI(runtime, "unknown");
|
|
71
|
+
default: [[unlikely]]
|
|
72
|
+
throw std::invalid_argument("Cannot convert FFmpegSessionState to JS - invalid value: "
|
|
73
|
+
+ std::to_string(static_cast<int>(arg)) + "!");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
|
|
77
|
+
if (!value.isString()) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
std::string unionValue = JSIConverter<std::string>::fromJSI(runtime, value);
|
|
81
|
+
switch (hashString(unionValue.c_str(), unionValue.size())) {
|
|
82
|
+
case hashString("queued"):
|
|
83
|
+
case hashString("running"):
|
|
84
|
+
case hashString("paused"):
|
|
85
|
+
case hashString("completed"):
|
|
86
|
+
case hashString("failed"):
|
|
87
|
+
case hashString("cancelled"):
|
|
88
|
+
case hashString("unknown"):
|
|
89
|
+
return true;
|
|
90
|
+
default:
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
} // namespace margelo::nitro
|
|
@@ -20,6 +20,9 @@ namespace margelo::nitro::munimffmpeg {
|
|
|
20
20
|
prototype.registerHybridMethod("getMediaInformation", &HybridMunimFfmpegSpec::getMediaInformation);
|
|
21
21
|
prototype.registerHybridMethod("cancel", &HybridMunimFfmpegSpec::cancel);
|
|
22
22
|
prototype.registerHybridMethod("cancelAll", &HybridMunimFfmpegSpec::cancelAll);
|
|
23
|
+
prototype.registerHybridMethod("pause", &HybridMunimFfmpegSpec::pause);
|
|
24
|
+
prototype.registerHybridMethod("resume", &HybridMunimFfmpegSpec::resume);
|
|
25
|
+
prototype.registerHybridMethod("getSessionState", &HybridMunimFfmpegSpec::getSessionState);
|
|
23
26
|
});
|
|
24
27
|
}
|
|
25
28
|
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
|
|
16
16
|
// Forward declaration of `FFmpegSessionResult` to properly resolve imports.
|
|
17
17
|
namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
18
|
+
// Forward declaration of `FFmpegSessionState` to properly resolve imports.
|
|
19
|
+
namespace margelo::nitro::munimffmpeg { enum class FFmpegSessionState; }
|
|
18
20
|
|
|
19
21
|
#include <string>
|
|
20
22
|
#include "FFmpegSessionResult.hpp"
|
|
@@ -22,6 +24,7 @@ namespace margelo::nitro::munimffmpeg { struct FFmpegSessionResult; }
|
|
|
22
24
|
#include <vector>
|
|
23
25
|
#include <functional>
|
|
24
26
|
#include <optional>
|
|
27
|
+
#include "FFmpegSessionState.hpp"
|
|
25
28
|
|
|
26
29
|
namespace margelo::nitro::munimffmpeg {
|
|
27
30
|
|
|
@@ -59,6 +62,9 @@ namespace margelo::nitro::munimffmpeg {
|
|
|
59
62
|
virtual std::shared_ptr<Promise<std::string>> getMediaInformation(const std::string& path) = 0;
|
|
60
63
|
virtual void cancel(std::optional<double> sessionId) = 0;
|
|
61
64
|
virtual void cancelAll() = 0;
|
|
65
|
+
virtual bool pause(double sessionId) = 0;
|
|
66
|
+
virtual bool resume(double sessionId) = 0;
|
|
67
|
+
virtual FFmpegSessionState getSessionState(double sessionId) = 0;
|
|
62
68
|
|
|
63
69
|
protected:
|
|
64
70
|
// Hybrid Setup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "munim-ffmpeg",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Fast FFmpeg and FFprobe for Expo and React Native, powered by Nitro Modules",
|
|
5
5
|
"main": "lib/index",
|
|
6
6
|
"module": "lib/index",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"nitrogen": "0.36.5",
|
|
119
119
|
"prettier": "^3.8.3",
|
|
120
120
|
"react": "19.2.3",
|
|
121
|
-
"react-native": "0.86.
|
|
121
|
+
"react-native": "0.86.3",
|
|
122
122
|
"react-native-nitro-modules": "0.36.5",
|
|
123
123
|
"semantic-release": "^25.0.9",
|
|
124
124
|
"typescript": "^6.0.3",
|
package/scripts/binaries.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
FFmpegLogCallback,
|
|
4
4
|
FFmpegSessionResult,
|
|
5
5
|
FFmpegSessionCreatedCallback,
|
|
6
|
+
FFmpegSessionState,
|
|
6
7
|
FFmpegStatisticsCallback,
|
|
7
8
|
MunimFfmpeg as MunimFfmpegSpec,
|
|
8
9
|
} from './specs/MunimFfmpeg.nitro'
|
|
@@ -14,6 +15,7 @@ export type {
|
|
|
14
15
|
FFmpegLogCallback,
|
|
15
16
|
FFmpegSessionResult,
|
|
16
17
|
FFmpegSessionCreatedCallback,
|
|
18
|
+
FFmpegSessionState,
|
|
17
19
|
FFmpegStatisticsCallback,
|
|
18
20
|
MunimFfmpegSpec,
|
|
19
21
|
}
|
|
@@ -177,6 +179,29 @@ export function cancelAll(): void {
|
|
|
177
179
|
MunimFfmpeg.cancelAll()
|
|
178
180
|
}
|
|
179
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Pauses a session started with `execute()`, whether it is running or still
|
|
184
|
+
* queued. FFmpeg stops reading its inputs and the rest of the pipeline idles
|
|
185
|
+
* once it has drained, so a paused session costs no CPU. Output files stay
|
|
186
|
+
* open, and `resume()` continues from exactly where it stopped: nothing is
|
|
187
|
+
* re-encoded or skipped, and the `execute()` promise settles once, when the
|
|
188
|
+
* work finishes. `cancel()` still works while paused.
|
|
189
|
+
*
|
|
190
|
+
* Returns false if the session is unknown, has finished, or is a probe.
|
|
191
|
+
*/
|
|
192
|
+
export function pause(sessionId: number): boolean {
|
|
193
|
+
return MunimFfmpeg.pause(sessionId)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Resumes a paused session. Returns false if it was not paused. */
|
|
197
|
+
export function resume(sessionId: number): boolean {
|
|
198
|
+
return MunimFfmpeg.resume(sessionId)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getSessionState(sessionId: number): FFmpegSessionState {
|
|
202
|
+
return MunimFfmpeg.getSessionState(sessionId)
|
|
203
|
+
}
|
|
204
|
+
|
|
180
205
|
export function getFFmpegVersion(): string {
|
|
181
206
|
return MunimFfmpeg.ffmpegVersion
|
|
182
207
|
}
|
|
@@ -11,6 +11,20 @@ export type FFmpegSessionResult = {
|
|
|
11
11
|
failStackTrace?: string
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Where a session is in its life. `queued` sessions wait for the one running
|
|
16
|
+
* ahead of them (FFmpeg runs one execution at a time); `unknown` means the id
|
|
17
|
+
* was never issued or its record has been dropped.
|
|
18
|
+
*/
|
|
19
|
+
export type FFmpegSessionState =
|
|
20
|
+
| 'queued'
|
|
21
|
+
| 'running'
|
|
22
|
+
| 'paused'
|
|
23
|
+
| 'completed'
|
|
24
|
+
| 'failed'
|
|
25
|
+
| 'cancelled'
|
|
26
|
+
| 'unknown'
|
|
27
|
+
|
|
14
28
|
export type FFmpegLogCallback = (message: string) => void
|
|
15
29
|
|
|
16
30
|
export type FFmpegSessionCreatedCallback = (sessionId: number) => void
|
|
@@ -49,4 +63,16 @@ export interface MunimFfmpeg extends HybridObject<{
|
|
|
49
63
|
cancel(sessionId?: number): void
|
|
50
64
|
|
|
51
65
|
cancelAll(): void
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Pauses an `execute()` session, running or still queued. Input stops being
|
|
69
|
+
* read and the pipeline idles; output files stay open. Returns false if the
|
|
70
|
+
* session is unknown, finished, or a probe (probes cannot be paused).
|
|
71
|
+
*/
|
|
72
|
+
pause(sessionId: number): boolean
|
|
73
|
+
|
|
74
|
+
/** Resumes a paused session. Returns false if it was not paused. */
|
|
75
|
+
resume(sessionId: number): boolean
|
|
76
|
+
|
|
77
|
+
getSessionState(sessionId: number): FFmpegSessionState
|
|
52
78
|
}
|