munim-ffmpeg 0.1.0 → 0.1.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 CHANGED
@@ -1,33 +1,220 @@
1
- # munim-ffmpeg
1
+ <p align="center">
2
+ <a href="https://github.com/munimtechnologies/munim-ffmpeg">
3
+ <img alt="Munim Technologies" height="128" src="https://raw.githubusercontent.com/munimtechnologies/munim-ffmpeg/main/.github/resources/banner.png?v=1" />
4
+ <h1 align="center">munim-ffmpeg</h1>
5
+ </a>
6
+ </p>
2
7
 
3
- Fast FFmpeg and FFprobe for Expo and React Native, powered by [Nitro Modules](https://nitro.margelo.com/).
8
+ <p align="center">
9
+ <a aria-label="Package version" href="https://www.npmjs.com/package/munim-ffmpeg">
10
+ <img alt="Package version" src="https://img.shields.io/npm/v/munim-ffmpeg.svg?style=flat-square&label=Version&labelColor=000000&color=0066CC" />
11
+ </a>
12
+ <a aria-label="Package is free to use" href="https://github.com/munimtechnologies/munim-ffmpeg/blob/main/LICENSE">
13
+ <img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache%202.0-success.svg?style=flat-square&color=33CC12" />
14
+ </a>
15
+ <a aria-label="Monthly package downloads" href="https://www.npmtrends.com/munim-ffmpeg">
16
+ <img alt="Monthly downloads" src="https://img.shields.io/npm/dm/munim-ffmpeg.svg?style=flat-square&labelColor=gray&color=33CC12&label=Downloads" />
17
+ </a>
18
+ <a aria-label="Total package downloads" href="https://www.npmjs.com/package/munim-ffmpeg">
19
+ <img alt="Total downloads" src="https://img.shields.io/npm/dt/munim-ffmpeg.svg?style=flat-square&labelColor=gray&color=0066CC&label=Total%20Downloads" />
20
+ </a>
21
+ <a aria-label="Expo development builds" href="https://docs.expo.dev/develop/development-builds/introduction/">
22
+ <img alt="Expo development builds" src="https://img.shields.io/badge/Expo-Development%20Build-000020?style=flat-square&logo=expo&logoColor=white" />
23
+ </a>
24
+ <a aria-label="iOS 15.1 or newer" href="https://developer.apple.com/ios/">
25
+ <img alt="iOS 15.1+" src="https://img.shields.io/badge/iOS-15.1%2B-000000?style=flat-square&logo=apple&logoColor=white" />
26
+ </a>
27
+ <a aria-label="Android API 24 or newer" href="https://developer.android.com/">
28
+ <img alt="Android API 24+" src="https://img.shields.io/badge/Android-API%2024%2B-3DDC84?style=flat-square&logo=android&logoColor=white" />
29
+ </a>
30
+ <a aria-label="Powered by Nitro Modules" href="https://nitro.margelo.com/">
31
+ <img alt="Nitro Modules 0.36.5+" src="https://img.shields.io/badge/Nitro%20Modules-0.36.5%2B-7C3AED?style=flat-square" />
32
+ </a>
33
+ </p>
4
34
 
5
- ## Features
35
+ <p align="center">
36
+ <a aria-label="Works with Expo" href="https://docs.expo.dev/develop/development-builds/introduction/"><b>Works with Expo development builds</b></a>
37
+ &ensp;•&ensp;
38
+ <a aria-label="Documentation" href="https://www.munimtech.com/opensource/munim-ffmpeg">Read the Documentation</a>
39
+ &ensp;•&ensp;
40
+ <a aria-label="Report issues" href="https://github.com/munimtechnologies/munim-ffmpeg/issues">Report Issues</a>
41
+ </p>
6
42
 
7
- - Execute FFmpeg and FFprobe with argument arrays—no shell-string escaping.
8
- - Receive FFmpeg logs and encoding statistics in JavaScript.
9
- - Inspect media as parsed FFprobe JSON.
10
- - Cancel one session or all active sessions.
11
- - Autolinks in Expo development builds and bare React Native apps.
43
+ <h6 align="center">Follow Munim Technologies</h6>
44
+ <p align="center">
45
+ <a aria-label="Munim Technologies on GitHub" href="https://github.com/munimtechnologies">
46
+ <img alt="Munim Technologies on GitHub" src="https://img.shields.io/badge/GitHub-222222?style=for-the-badge&logo=github&logoColor=white" />
47
+ </a>&nbsp;
48
+ <a aria-label="Munim Technologies on LinkedIn" href="https://linkedin.com/in/sheehanmunim">
49
+ <img alt="Munim Technologies on LinkedIn" src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" />
50
+ </a>&nbsp;
51
+ <a aria-label="Munim Technologies website" href="https://www.munimtech.com">
52
+ <img alt="Munim Technologies website" src="https://img.shields.io/badge/Website-0066CC?style=for-the-badge&logo=googlechrome&logoColor=white" />
53
+ </a>
54
+ </p>
12
55
 
13
- ## Installation
56
+ ## Introduction
14
57
 
15
- ```sh
58
+ **munim-ffmpeg** provides fast, typed FFmpeg and FFprobe access for Expo and React Native. It executes argument arrays directly through native FFmpeg libraries, streams logs and encoding statistics to JavaScript, returns structured session results, inspects media with FFprobe, and cancels one running session or every active session.
59
+
60
+ **Built with React Native Nitro Modules.** One TypeScript specification generates the C++, Swift, and Kotlin bindings used by the package.
61
+
62
+ **Designed for Expo development builds and bare React Native.** This package contains native code and cannot run in Expo Go.
63
+
64
+ > **Licensing note:** The JavaScript and Nitro bridge are Apache-2.0. The bundled Android artifact is GPL-enabled and includes x264/x265; distributing it can trigger GPLv3 obligations. The native dependencies and FFmpeg retain their own licenses. Review [Native dependencies and licensing](#native-dependencies-and-licensing) before distributing an app.
65
+
66
+ ## Table of contents
67
+
68
+ - [📚 Documentation](#-documentation)
69
+ - [🚀 Features](#-features)
70
+ - [Platform support matrix](#platform-support-matrix)
71
+ - [📦 Installation](#-installation)
72
+ - [Working with media paths](#working-with-media-paths)
73
+ - [⚡ Quick start](#-quick-start)
74
+ - [🔧 API reference](#-api-reference)
75
+ - [📖 Usage examples](#-usage-examples)
76
+ - [Native dependencies and licensing](#native-dependencies-and-licensing)
77
+ - [🔍 Troubleshooting](#-troubleshooting)
78
+ - [Development](#development)
79
+ - [👏 Contributing](#-contributing)
80
+ - [📄 License](#-license)
81
+
82
+ ## 📚 Documentation
83
+
84
+ - [Munim Technologies package documentation](https://www.munimtech.com/opensource/munim-ffmpeg)
85
+ - [Installation](#-installation)
86
+ - [API reference](#-api-reference)
87
+ - [Usage examples](#-usage-examples)
88
+ - [Troubleshooting](#-troubleshooting)
89
+
90
+ ## 🚀 Features
91
+
92
+ ### FFmpeg execution
93
+
94
+ - 🎬 **Argument-array commands:** Avoid platform-specific shell parsing and quoting
95
+ - ⚡ **Asynchronous sessions:** Keep the React Native thread responsive during native work
96
+ - 📝 **Live logs:** Receive FFmpeg output as it is produced
97
+ - 📈 **Encoding statistics:** Track time, size, bitrate, speed, frames, FPS, and quality
98
+ - 🎯 **Targeted cancellation:** Capture a native session ID immediately and cancel only that command
99
+ - 🛑 **Global cancellation:** Stop all active sessions during workflow or screen cleanup
100
+
101
+ ### FFprobe and media inspection
102
+
103
+ - 🔎 **FFprobe execution:** Run custom probing commands with the same typed session result
104
+ - 🧾 **Parsed media information:** Inspect format, streams, chapters, codecs, duration, and metadata as JSON
105
+ - ✅ **Structured completion:** Read return code, state, duration, output, cancellation state, and failure details
106
+
107
+ ### React Native integration
108
+
109
+ - 📱 **iOS and Android:** Native implementations in Swift and Kotlin
110
+ - 🧬 **Nitro Modules:** Generated high-performance native bindings
111
+ - 🚀 **Expo compatible:** Autolinking, config plugin, and an Expo development example
112
+ - 🎯 **TypeScript:** Complete public callback and result types
113
+ - 🗂️ **16 KB Android support:** Uses a maintained FFmpegKit-compatible Android artifact with 16 KB page-size support
114
+
115
+ ## Platform support matrix
116
+
117
+ | Capability | iOS | Android | Notes |
118
+ | ---------------------------- | --------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
119
+ | FFmpeg argument execution | ✅ | ✅ | Commands run asynchronously through the native compatibility library. |
120
+ | FFprobe argument execution | ✅ | ✅ | Custom FFprobe arguments return `FFmpegSessionResult`. |
121
+ | Parsed media information | ✅ | ✅ | `getMediaInformation()` returns parsed FFprobe JSON. |
122
+ | Log callback | ✅ | ✅ | Logs are delivered while a session is active. |
123
+ | Encoding-statistics callback | ✅ | ✅ | Available for FFmpeg execution. |
124
+ | Immediate session ID | ✅ | ✅ | `onSessionCreated` fires after the native session is created. |
125
+ | Cancel one FFmpeg session | ✅ | ✅ | Pass the positive safe-integer ID received by `execute`'s `onSessionCreated`. The native dependency does not expose FFprobe cancellation. |
126
+ | Cancel all FFmpeg sessions | ✅ | ✅ | Use `cancelAll()` or call `cancel()` without an ID. |
127
+ | Expo Go | ❌ | ❌ | A native development build is required. |
128
+ | Remote HTTP(S) inputs | Build-dependent | Build-dependent | The bundled variants include HTTPS support, but remote server behavior and protocol support can vary. Prefer local files for predictable app workflows. |
129
+
130
+ Codec availability is determined by the native FFmpeg builds listed below. Do not assume every FFmpeg codec or external library is bundled.
131
+
132
+ ## 📦 Installation
133
+
134
+ ### React Native CLI
135
+
136
+ ```bash
137
+ npm install munim-ffmpeg react-native-nitro-modules
138
+ # or
139
+ yarn add munim-ffmpeg react-native-nitro-modules
140
+ ```
141
+
142
+ Install iOS pods after adding the dependency:
143
+
144
+ ```bash
145
+ cd ios
146
+ pod install
147
+ cd ..
148
+ ```
149
+
150
+ FFmpegKit and React Native both provide `libc++_shared.so`. Resolve that duplicate in the Android application module:
151
+
152
+ ```groovy
153
+ android {
154
+ packagingOptions {
155
+ jniLibs {
156
+ pickFirsts += ['**/libc++_shared.so']
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Expo
163
+
164
+ ```bash
16
165
  npx expo install munim-ffmpeg react-native-nitro-modules
17
166
  ```
18
167
 
19
- This package contains native code, so it does not run in Expo Go. Create a development build after installing it:
168
+ The package includes an Expo config plugin. If your project manages its plugin list explicitly, add it to `app.json`:
169
+
170
+ ```json
171
+ {
172
+ "expo": {
173
+ "plugins": ["munim-ffmpeg"]
174
+ }
175
+ }
176
+ ```
177
+
178
+ The plugin also configures Android to select one shared C++ runtime when React Native and FFmpegKit contribute the same `libc++_shared.so` path.
20
179
 
21
- ```sh
180
+ Create a native development build after installation:
181
+
182
+ ```bash
22
183
  npx expo prebuild
23
184
  npx expo run:ios
24
185
  # or
25
186
  npx expo run:android
26
187
  ```
27
188
 
28
- ## Usage
189
+ You can also create an [EAS development build](https://docs.expo.dev/develop/development-builds/create-a-build/).
190
+
191
+ > **Important:** `munim-ffmpeg` cannot run in Expo Go because Expo Go does not include this package's native libraries.
192
+
193
+ ### Requirements
194
+
195
+ - React Native with the New Architecture enabled
196
+ - `react-native-nitro-modules` 0.36.5 or newer
197
+ - iOS 15.1 or newer
198
+ - Android API 24 or newer
199
+ - An Expo development build or bare React Native app
200
+
201
+ No camera, microphone, photo-library, or storage permission is added automatically. Declare only the permissions required by the locations and capture APIs your application uses.
202
+
203
+ ## Working with media paths
204
+
205
+ FFmpeg runs natively and needs a path or URI the native process can access.
206
+
207
+ - Prefer files inside your app's document, cache, or temporary directory.
208
+ - `file://` URIs and plain local paths are the most predictable inputs and outputs.
209
+ - On Android, copy a `content://` document into application storage before processing when the native library cannot open it directly.
210
+ - Copy photo-library or document-picker assets when the provider gives temporary or security-scoped access.
211
+ - Ensure the output directory already exists.
212
+ - Use a unique output filename or pass `-y` when replacing an existing file is intentional.
213
+ - Do not include the `ffmpeg` or `ffprobe` executable name in the argument array.
214
+
215
+ ## ⚡ Quick start
29
216
 
30
- ```ts
217
+ ```typescript
31
218
  import {
32
219
  cancel,
33
220
  execute,
@@ -36,60 +223,334 @@ import {
36
223
  probe,
37
224
  } from 'munim-ffmpeg'
38
225
 
226
+ console.log('FFmpeg:', getFFmpegVersion())
227
+
39
228
  let activeSessionId: number | undefined
40
229
 
41
230
  const execution = execute(
42
- ['-i', inputUri, '-c:v', 'mpeg4', '-c:a', 'aac', outputUri],
231
+ ['-y', '-i', inputPath, '-c:v', 'mpeg4', '-c:a', 'aac', outputPath],
43
232
  (message) => console.log(message),
44
- (timeMs, sizeBytes, bitrateKbits, speed) => {
45
- console.log({ timeMs, sizeBytes, bitrateKbits, speed })
233
+ (timeMs, sizeBytes, bitrateKbits, speed, frame, fps, quality) => {
234
+ console.log({
235
+ timeMs,
236
+ sizeBytes,
237
+ bitrateKbits,
238
+ speed,
239
+ frame,
240
+ fps,
241
+ quality,
242
+ })
46
243
  },
47
244
  (sessionId) => {
48
245
  activeSessionId = sessionId
49
246
  }
50
247
  )
51
248
 
52
- // From a cancel button while the command is running:
53
- if (activeSessionId !== undefined) cancel(activeSessionId)
249
+ // Call this from a cancel button while the command is running.
250
+ if (activeSessionId !== undefined) {
251
+ cancel(activeSessionId)
252
+ }
54
253
 
55
254
  const result = await execution
56
255
 
256
+ if (!result.success && !result.cancelled) {
257
+ throw new Error(result.failStackTrace ?? result.output)
258
+ }
259
+
260
+ const probeResult = await probe([
261
+ '-v',
262
+ 'error',
263
+ '-show_format',
264
+ '-show_streams',
265
+ inputPath,
266
+ ])
267
+
268
+ const mediaInformation = await getMediaInformation(inputPath)
269
+ ```
270
+
271
+ ## 🔧 API reference
272
+
273
+ ### `execute(arguments, onLog?, onStatistics?, onSessionCreated?)`
274
+
275
+ Starts an asynchronous FFmpeg session.
276
+
277
+ ```typescript
278
+ function execute(
279
+ arguments_: string[],
280
+ onLog?: (message: string) => void,
281
+ onStatistics?: (
282
+ timeMs: number,
283
+ sizeBytes: number,
284
+ bitrateKbits: number,
285
+ speed: number,
286
+ videoFrameNumber: number,
287
+ fps: number,
288
+ quality: number
289
+ ) => void,
290
+ onSessionCreated?: (sessionId: number) => void
291
+ ): Promise<FFmpegSessionResult>
292
+ ```
293
+
294
+ The `onSessionCreated` callback receives the ID before the command completes, allowing targeted cancellation while work is running.
295
+
296
+ ### `probe(arguments, onLog?, onSessionCreated?)`
297
+
298
+ Starts an asynchronous FFprobe session.
299
+
300
+ ```typescript
301
+ function probe(
302
+ arguments_: string[],
303
+ onLog?: (message: string) => void,
304
+ onSessionCreated?: (sessionId: number) => void
305
+ ): Promise<FFmpegSessionResult>
306
+ ```
307
+
308
+ `onSessionCreated` can be used to correlate the native probe session with its eventual result. The bundled native dependency does not expose FFprobe cancellation.
309
+
310
+ ### `getMediaInformation(path)`
311
+
312
+ Runs FFprobe for the format, streams, and chapters at a local media path, then parses its JSON response.
313
+
314
+ ```typescript
315
+ function getMediaInformation(path: string): Promise<unknown>
316
+ ```
317
+
318
+ Applications should validate or narrow the returned JSON shape before using fields from it.
319
+
320
+ ### `cancel(sessionId?)`
321
+
322
+ Cancels the given native FFmpeg execution session. Calling `cancel()` without an ID cancels all active FFmpeg sessions. FFprobe cancellation is not exposed by the bundled native dependency.
323
+
324
+ ```typescript
325
+ function cancel(sessionId?: number): void
326
+ ```
327
+
328
+ Session IDs must be positive safe integers received from `onSessionCreated` or `FFmpegSessionResult`.
329
+
330
+ ### `cancelAll()`
331
+
332
+ Cancels every active FFmpeg session.
333
+
334
+ ```typescript
335
+ function cancelAll(): void
336
+ ```
337
+
338
+ ### `getFFmpegVersion()`
339
+
340
+ Returns the version reported by the bundled native FFmpeg library.
341
+
342
+ ```typescript
343
+ function getFFmpegVersion(): string
344
+ ```
345
+
346
+ ### `FFmpegSessionResult`
347
+
348
+ ```typescript
349
+ type FFmpegSessionResult = {
350
+ sessionId: number
351
+ returnCode: number
352
+ success: boolean
353
+ cancelled: boolean
354
+ state: string
355
+ durationMs: number
356
+ output: string
357
+ failStackTrace?: string
358
+ }
359
+ ```
360
+
361
+ Always check `success` or `cancelled`; Promise resolution means the native session completed, not necessarily that FFmpeg returned a success code.
362
+
363
+ ## 📖 Usage examples
364
+
365
+ ### Inspect media metadata
366
+
367
+ ```typescript
368
+ import { getMediaInformation } from 'munim-ffmpeg'
369
+
370
+ const information = await getMediaInformation(inputPath)
371
+ console.log(JSON.stringify(information, null, 2))
372
+ ```
373
+
374
+ ### Extract an audio track
375
+
376
+ ```typescript
377
+ import { execute } from 'munim-ffmpeg'
378
+
379
+ const result = await execute([
380
+ '-y',
381
+ '-i',
382
+ inputPath,
383
+ '-vn',
384
+ '-c:a',
385
+ 'aac',
386
+ outputAudioPath,
387
+ ])
388
+
57
389
  if (!result.success) {
58
390
  throw new Error(result.failStackTrace ?? result.output)
59
391
  }
392
+ ```
393
+
394
+ ### Generate a thumbnail
395
+
396
+ ```typescript
397
+ import { execute } from 'munim-ffmpeg'
398
+
399
+ const result = await execute([
400
+ '-y',
401
+ '-ss',
402
+ '00:00:01.000',
403
+ '-i',
404
+ inputPath,
405
+ '-frames:v',
406
+ '1',
407
+ outputImagePath,
408
+ ])
409
+ ```
410
+
411
+ ### Cancel a long-running command
412
+
413
+ ```typescript
414
+ import { cancel, execute } from 'munim-ffmpeg'
415
+
416
+ let sessionId: number | undefined
417
+
418
+ const execution = execute(
419
+ ['-i', inputPath, '-c:v', 'mpeg4', outputPath],
420
+ undefined,
421
+ undefined,
422
+ (createdSessionId) => {
423
+ sessionId = createdSessionId
424
+ }
425
+ )
426
+
427
+ const cancelCurrentCommand = () => {
428
+ if (sessionId !== undefined) {
429
+ cancel(sessionId)
430
+ }
431
+ }
60
432
 
61
- const information = await getMediaInformation(inputUri)
62
- const probeResult = await probe(['-v', 'error', '-show_streams', inputUri])
63
- const version = getFFmpegVersion()
433
+ const result = await execution
434
+ console.log(result.cancelled)
64
435
  ```
65
436
 
66
- Use native filesystem paths or `file://` URIs that the app can access. Android `content://` input may need to be copied into application storage first.
437
+ ### Run a custom FFprobe query
438
+
439
+ ```typescript
440
+ import { probe } from 'munim-ffmpeg'
67
441
 
68
- ## API
442
+ const result = await probe([
443
+ '-v',
444
+ 'error',
445
+ '-select_streams',
446
+ 'v:0',
447
+ '-show_entries',
448
+ 'stream=codec_name,width,height,duration',
449
+ '-of',
450
+ 'json',
451
+ inputPath,
452
+ ])
69
453
 
70
- - `execute(arguments, onLog?, onStatistics?, onSessionCreated?)` returns `Promise<FFmpegSessionResult>`.
71
- - `probe(arguments, onLog?, onSessionCreated?)` returns `Promise<FFmpegSessionResult>`.
72
- - `getMediaInformation(path)` returns parsed FFprobe JSON.
73
- - `cancel(sessionId?)` cancels one running session using the ID from `onSessionCreated`, or all sessions when no ID is supplied.
74
- - `cancelAll()` cancels all sessions.
75
- - `getFFmpegVersion()` returns the bundled FFmpeg version.
454
+ if (result.success) {
455
+ console.log(result.output)
456
+ }
457
+ ```
76
458
 
77
459
  ## Native dependencies and licensing
78
460
 
79
- The JavaScript and native bridge code in this repository is Apache-2.0 licensed. The distributed native FFmpeg dependencies are separate works with their own licenses:
461
+ The JavaScript, TypeScript, Swift, Kotlin, and generated Nitro bridge code in this repository are Apache-2.0 licensed. Native FFmpeg binaries are supplied by separate compatibility packages:
80
462
 
81
- - Android: `io.github.jamaismagic.ffmpeg:ffmpeg-kit-main-16kb:6.1.4`
82
- - iOS: `ffmpeg-kit-ios-https-alt` 6.0
463
+ | Platform | Native dependency | Version |
464
+ | -------- | --------------------------------------------------- | ------- |
465
+ | iOS | `ffmpeg-kit-ios-https-alt` | 6.0 |
466
+ | Android | `io.github.jamaismagic.ffmpeg:ffmpeg-kit-main-16kb` | 6.1.4 |
83
467
 
84
- Those packages are FFmpegKit-compatible builds. FFmpeg and optional linked libraries may impose LGPL, GPL, attribution, or source-offer obligations depending on the build and codecs you distribute. Review the dependency licenses for your application before shipping.
468
+ The bundled Android 6.1.4 artifact is GPL-enabled and includes x264 and x265. Distributing an Android application with this dependency can trigger GPLv3 source, license, and redistribution obligations. Its published Maven metadata does not fully communicate that posture, so assess the binaries and their notices rather than relying only on the POM license field.
85
469
 
86
- ## Requirements
470
+ FFmpeg's effective license depends on the enabled libraries, codecs, and build configuration. Before distributing an application:
87
471
 
88
- - Expo development build or bare React Native app with the New Architecture enabled
89
- - React Native Nitro Modules
90
- - iOS 15.1 or newer (as determined by the React Native minimum)
91
- - Android API 24 or newer
472
+ 1. Review the license and notices shipped by each native dependency.
473
+ 2. Identify the codecs and linked libraries used by your product.
474
+ 3. Follow the applicable LGPL, GPL, attribution, relinking, and source-offer requirements.
475
+ 4. Treat the current Android build as GPL-enabled, and reassess licensing again if you replace either native dependency.
476
+
477
+ See [FFmpeg legal guidance](https://ffmpeg.org/legal.html). This section is an engineering reminder, not legal advice.
478
+
479
+ ## 🔍 Troubleshooting
480
+
481
+ ### `munim-ffmpeg` is unavailable in Expo Go
482
+
483
+ This is expected. Install the package and create an Expo development build with `npx expo run:ios`, `npx expo run:android`, or EAS Build.
484
+
485
+ ### Nitro reports that `MunimFfmpeg` cannot be found
486
+
487
+ Rebuild the native app after installing both `munim-ffmpeg` and `react-native-nitro-modules`. Restarting Metro alone cannot add a native module to an existing binary.
488
+
489
+ ### FFmpeg cannot open an input or output
490
+
491
+ - Confirm the file exists and the app can read it.
492
+ - Confirm the output directory already exists and is writable.
493
+ - Copy Android `content://` inputs into app storage.
494
+ - Copy temporary picker or photo-library assets when necessary.
495
+ - Log the exact argument array and native output while removing private path data from bug reports.
496
+
497
+ ### A codec or filter is missing
498
+
499
+ Native FFmpeg variants do not bundle every codec, filter, or third-party library. Check `getFFmpegVersion()` and the session output, then choose a bundled codec or replace the native dependency with a build whose licensing and features fit your application.
500
+
501
+ ### The Promise resolved but the command failed
502
+
503
+ Inspect `result.success`, `result.cancelled`, `result.returnCode`, `result.output`, and `result.failStackTrace`. A resolved Promise represents a completed native session; FFmpeg can still finish with a nonzero return code.
504
+
505
+ ### iOS pod or build errors
506
+
507
+ Run `pod install` after installation and rebuild from a clean native development build. The package includes a narrowly scoped Xcode 26 compatibility patch for the FFmpegKit `Level` enum used by Nitro's Swift/C++ bridge.
508
+
509
+ ### Android build errors
510
+
511
+ Use Android API 24 or newer, JDK 17, and the React Native New Architecture. Clear stale Gradle build output after changing native dependency versions.
512
+
513
+ ## Development
514
+
515
+ ```bash
516
+ npm install
517
+ npm run codegen
518
+ npm run typecheck
519
+ npm run typecheck:example
520
+ npm run build
521
+ npm run pack:dry-run
522
+ ```
523
+
524
+ Run the Expo example with:
525
+
526
+ ```bash
527
+ npm run example:ios
528
+ # or
529
+ npm run example:android
530
+ ```
531
+
532
+ Releases are validated and published manually; this repository does not use GitHub Actions:
533
+
534
+ ```bash
535
+ npm run check
536
+ cd packages/munim-ffmpeg
537
+ npm publish --access public
538
+ ```
539
+
540
+ Nitrogen output under `packages/munim-ffmpeg/nitrogen/generated` is committed. Change the `.nitro.ts` specification and rerun codegen instead of editing generated files directly.
541
+
542
+ ## 👏 Contributing
543
+
544
+ Issues and pull requests are welcome. Include the following when reporting a problem:
545
+
546
+ - iOS or Android version and device architecture
547
+ - Expo SDK and React Native versions
548
+ - `react-native-nitro-modules` version
549
+ - Command arguments with private paths and URLs removed
550
+ - `FFmpegSessionResult` and relevant native logs
551
+
552
+ Please keep licensing implications explicit when proposing a new FFmpeg binary variant, codec, or linked library.
92
553
 
93
- ## License
554
+ ## 📄 License
94
555
 
95
- Apache-2.0
556
+ The `munim-ffmpeg` source is available under the [Apache License 2.0](./LICENSE). Native FFmpeg dependencies are licensed separately as described above.
@@ -43,8 +43,8 @@ class HybridMunimFfmpeg : HybridMunimFfmpegSpec() {
43
43
  statistics.bitrate,
44
44
  statistics.speed,
45
45
  statistics.videoFrameNumber.toDouble(),
46
- statistics.videoFps,
47
- statistics.videoQuality,
46
+ statistics.videoFps.toDouble(),
47
+ statistics.videoQuality.toDouble(),
48
48
  )
49
49
  },
50
50
  )
package/app.plugin.js CHANGED
@@ -1,5 +1,37 @@
1
+ const { withGradleProperties } = require('expo/config-plugins')
2
+
3
+ const PICK_FIRSTS_PROPERTY = 'android.packagingOptions.pickFirsts'
4
+ const CXX_SHARED_LIBRARY = '**/libc++_shared.so'
5
+
6
+ function withAndroidPackaging(config) {
7
+ return withGradleProperties(config, (gradleConfig) => {
8
+ const existing = gradleConfig.modResults.find(
9
+ (item) => item.type === 'property' && item.key === PICK_FIRSTS_PROPERTY
10
+ )
11
+
12
+ if (existing) {
13
+ const values = new Set(
14
+ existing.value
15
+ .split(',')
16
+ .map((value) => value.trim())
17
+ .filter(Boolean)
18
+ )
19
+ values.add(CXX_SHARED_LIBRARY)
20
+ existing.value = [...values].join(',')
21
+ } else {
22
+ gradleConfig.modResults.push({
23
+ type: 'property',
24
+ key: PICK_FIRSTS_PROPERTY,
25
+ value: CXX_SHARED_LIBRARY,
26
+ })
27
+ }
28
+
29
+ return gradleConfig
30
+ })
31
+ }
32
+
1
33
  module.exports = function withMunimFfmpeg(config) {
2
- return config
34
+ return withAndroidPackaging(config)
3
35
  }
4
36
 
5
37
  module.exports.default = module.exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "munim-ffmpeg",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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",
@@ -53,6 +53,12 @@
53
53
  "typescript",
54
54
  "nitro",
55
55
  "nitro-modules",
56
+ "react-native-ffmpeg",
57
+ "expo-plugin",
58
+ "video-processing",
59
+ "audio-processing",
60
+ "media-processing",
61
+ "transcoding",
56
62
  "munim-technologies"
57
63
  ],
58
64
  "repository": {