react-native-nitro-media-kit
v0.3.0
Published
Media Kit Library
Readme
react-native-nitro-media-kit
High-performance image-and-video processing for React Native powered by Nitro Modules.
✨ Features
| Feature | Android | iOS | Notes |
| ---------------------- | ------- | --- | ------------------------------------------------------------------------- |
| getMediaInfo | ✔︎ | ✔︎ | Reads basic metadata (duration, dimensions, type). |
| convertImageToVideo | ✔︎ | ✔︎ | Turns any local file or remote URL into an H.264 MP4 (typically ~30 fps). |
| mergeVideos | ✔︎ | ✔︎ | Concatenates an arbitrary list of MP4s, normalising size/FPS when needed. |
| watermarkVideo | ✔︎ | ✔︎ | Places a text watermark in 4 corners or centre – remote URLs supported. |
| Hardware-backed codecs | ✔︎ | ✔︎ | Uses MediaCodec / AVFoundation surfaces for efficient rendering. |
| Remote-URL fallback | ✔︎ | ✔︎ | Automatically downloads HTTP/HTTPS sources to a temp cache. |
| Pause-free JS thread | ✔︎ | ✔︎ | Heavy work happens in Kotlin/Swift on background queues. |
📦 Installation
# 1) Library + peer dependency
npm i react-native-nitro-media-kit react-native-nitro-modules
# 2) iOS pods
cd ios && pod installreact-native-nitro-modules is required (this library is a Nitro Module).
Android requirements
- Android builds use CMake/NDK (via Nitro). Make sure your RN Android toolchain is set up with the NDK installed.
🏁 Quick-start
import {
convertImageToVideo,
mergeVideos,
watermarkVideo,
} from 'react-native-nitro-media-kit';
// 1️⃣ Turn a PNG into a 5-second clip
const result1 = await convertImageToVideo('https://example.com/banner.png', 5);
if (!result1.ok) throw new Error(result1.error?.message ?? 'Convert failed');
const video1 = result1.outputUri!;
// 2️⃣ Add a watermark bottom-right
const result2 = await watermarkVideo(
video1,
'© ACME Corp',
'bottom-right' // top-left | top-right | bottom-left | bottom-right | center
);
if (!result2.ok) throw new Error(result2.error?.message ?? 'Watermark failed');
const watermarked = result2.outputUri!;
// 3️⃣ Merge with an existing clip
const result3 = await mergeVideos([
watermarked,
'/storage/emulated/0/DCIM/clip.mp4',
]);
if (!result3.ok) throw new Error(result3.error?.message ?? 'Merge failed');
const final = result3.outputUri!;
console.log('Done! ->', final);🧾 Return Type
All methods return the same shape: Promise<MediaInfoResult>.
export type MediaInfoResult = {
ok: boolean;
/** What operation produced this result */
operation: 'getMediaInfo' | 'convert' | 'watermark' | 'merge';
/** Media type */
type: 'image' | 'video';
/** Input media */
inputUri?: string;
/** Output media (if generated) */
outputUri?: string;
/** Core media info (images will only provide width/height) */
media?: {
durationMs?: number; // video only
width?: number;
height?: number;
fps?: number; // video only
};
/** Error info when ok = false */
error?: {
code: string;
message: string;
};
};Notes:
- For images,
durationMsandfpsare omitted (leftundefined) — onlywidth/heightare returned. - For operations that generate files (
convert,watermark,merge),outputUricontains the created file path.
🗂️ API Reference
getMediaInfo(inputUri: string): Promise<MediaInfoResult>
Reads metadata for images or videos and returns a MediaInfoResult.
| Param | Type | Description |
| ---------- | -------- | --------------------------------------------------- |
| inputUri | string | Local absolute path or remote URL (http/https). |
Returns: Promise<MediaInfoResult>.
convertImageToVideo(image: string, duration: number): Promise<MediaInfoResult>
| Param | Type | Description |
| ---------- | -------- | --------------------------------------------------- |
| image | string | Local absolute path or remote URL (http/https). |
| duration | number | Length of the generated video in seconds. |
Returns: Promise<MediaInfoResult> — outputUri holds the absolute path to the newly created MP4.
Under the hood the image is resized to the nearest supported H.264 resolution while preserving aspect ratio.
mergeVideos(videos: string[]): Promise<MediaInfoResult>
Concatenates multiple MP4s into a single file. Videos with mismatched width/height/fps are re-encoded transparently.
| Param | Type | Description |
| -------- | ---------- | ----------------------------- |
| videos | string[] | Array of local paths or URLs. |
Returns: Promise<MediaInfoResult> — outputUri holds the path to the merged file.
watermarkVideo(video: string, watermark: string, position: string): Promise<MediaInfoResult>
Adds a text watermark to any corner (or centre).
| Param | Type | Description |
| ----------- | -------- | ------------------------------------------------------------------------ |
| video | string | Source MP4 path or URL. |
| watermark | string | The text to render. |
| position | string | One of top-left, top-right, bottom-left, bottom-right, center. |
Returns: Promise<MediaInfoResult> — outputUri holds the path to the water-marked file.
⚠️ Troubleshooting
| Symptom | Fix |
| ------------------------------------------------- | -------------------------------------------------------------------------------------- |
| IllegalArgumentException: No video track found… | Ensure the input is an MP4 (H.264). Other containers aren’t supported yet. |
| Black video on Android | Some hardware codecs dislike odd dimensions. Prefer even-number sizes (e.g. 1280×720). |
| iOS export stuck at 0% | Check free disk space — AVFoundation can fail when temp space is low. |
🗺️ Roadmap
- Arbitrary X/Y watermark coordinates.
- Font size / style props for
watermarkVideo. - Contributions & PRs welcome!
🤝 Contributing
We follow the Nitro Modules contributing guide. Run examples locally with yarn example ios or yarn example android.
📄 License
MIT © 2025 — Sameer Ather
Made with ❤️ by Sameer Ather & create-react-native-library.
