npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mgcrea/react-native-video-frames

v0.5.3

Published

React Native module to extract video frames using AVAssetImageGenerator (iOS) and MediaMetadataRetriever (Android)

Readme

React Native Video Frames

Overview

React Native Video Frames is a lightweight native module for extracting image frames from video files at specified timestamps. Built with React Native's new Turbo Module architecture for optimal performance.

  • Leverages native APIs: AVAssetImageGenerator (iOS) and MediaMetadataRetriever (Android)
  • Extract frames at precise timestamps with millisecond accuracy
  • Configure output dimensions while maintaining aspect ratio
  • Configurable JPEG quality (0.0 - 1.0) for optimal file size vs. quality tradeoff
  • Production-ready with comprehensive error handling

Features

  • ⚡ Fast & Efficient — Uses native video processing APIs for optimal performance
  • 🎯 Precise Timing — Extract frames at exact millisecond timestamps
  • 📐 Flexible Sizing — Configure output width/height with automatic aspect ratio preservation
  • 🔄 Async by Design — Background processing with Promise-based API
  • 💪 Type-Safe — Full TypeScript support with exported types
  • 🎨 Quality Control — Configurable JPEG compression quality (0.0-1.0)
  • ✅ Production Ready — Comprehensive validation and error handling

Installation

Prerequisites

  • React Native >= 0.74
  • iOS >= 14.0
  • Android SDK >= 24 (Android 7.0)
  • Node.js >= 18

Install Package

npm install @mgcrea/react-native-video-frames
pnpm add @mgcrea/react-native-video-frames
yarn add @mgcrea/react-native-video-frames

iOS Setup

cd ios && pod install

Android Setup

Android uses autolinking, so no additional setup is required. Just rebuild your app:

cd android && ./gradlew assembleDebug

Usage

Basic Example

import { NativeVideoFrames } from "@mgcrea/react-native-video-frames";

// Extract frames at 1s, 2s, and 3s
const frameUris = await NativeVideoFrames.extractFrames(
  "file:///path/to/video.mp4",
  [1000, 2000, 3000], // timestamps in milliseconds
);

// frameUris: ['file:///tmp/vf-1000.jpg', 'file:///tmp/vf-2000.jpg', ...]

With Options

import { NativeVideoFrames } from "@mgcrea/react-native-video-frames";

// Extract frames with custom dimensions and quality
const frameUris = await NativeVideoFrames.extractFrames("file:///path/to/video.mp4", [1000, 2000, 3000], {
  width: 400, // Optional: specify width
  height: 300, // Optional: specify height
  quality: 0.8, // Optional: JPEG quality (0.0-1.0, default: 0.9)
});

Complete Integration Example

import { useState } from "react";
import { View, Button, Image, ScrollView } from "react-native";
import { launchImageLibrary } from "react-native-image-picker";
import { NativeVideoFrames } from "@mgcrea/react-native-video-frames";

function VideoFrameExtractor() {
  const [frames, setFrames] = useState<string[]>([]);

  const selectAndExtract = async () => {
    // Pick a video
    const result = await launchImageLibrary({ mediaType: "video" });
    const videoUri = result.assets?.[0]?.uri;

    if (!videoUri) return;

    // Extract frames at 1s intervals for first 5 seconds
    const timestamps = [1000, 2000, 3000, 4000, 5000];
    const frameUris = await NativeVideoFrames.extractFrames(videoUri, timestamps, {
      width: 400, // Resize to 400px width
      quality: 0.85, // Reduce quality for smaller files
    });

    setFrames(frameUris);
  };

  return (
    <View>
      <Button title="Select Video & Extract Frames" onPress={selectAndExtract} />
      <ScrollView>
        {frames.map((uri, index) => (
          <Image key={uri} source={{ uri }} style={{ width: "100%", height: 200 }} />
        ))}
      </ScrollView>
    </View>
  );
}

API Reference

extractFrames(videoPath, times, options?)

Extracts image frames from a video file at specified timestamps.

Parameters

| Parameter | Type | Required | Description | | ----------- | ---------------------- | -------- | --------------------------------------------- | | videoPath | string | ✅ | Local file path or file:// URI to the video | | times | number[] | ✅ | Array of timestamps in milliseconds | | options | ExtractFramesOptions | ❌ | Optional sizing configuration |

Options

type ExtractFramesOptions = {
  width?: number; // Output width in pixels
  height?: number; // Output height in pixels
  quality?: number; // JPEG compression quality (0.0-1.0, default: 0.9)
  precise?: boolean; // Extract exact frames vs nearest keyframe (default: false)
};

Sizing Behavior:

  • Both dimensions: Frames fit within the bounding box, maintaining aspect ratio
  • Width only: Height scales proportionally
  • Height only: Width scales proportionally
  • No options: Original video dimensions

Quality Behavior:

  • Range: 0.0 (maximum compression, smallest file) to 1.0 (minimum compression, best quality)
  • Default: 0.9 (high quality with reasonable file size)
  • Lower values result in smaller files but reduced image quality
  • Higher values preserve quality but increase file size

Precise Mode:

  • false (default): Fast extraction, snaps to nearest keyframe
  • true: Exact frame extraction at specified timestamp (slower)
  • On Android, precise mode requires API 28+ (OPTION_CLOSEST); falls back to keyframe on older devices

Returns

Promise<string[]> — Array of file:// URIs pointing to extracted JPEG frames in the temporary directory.

Errors

The promise rejects with an error object containing:

| Code | Description | | --------------------- | ---------------------------------------------------------- | | E_INVALID_URL | Invalid video file URL | | E_INVALID_ASSET | Could not load video, no video tracks, or zero duration | | E_INVALID_ARGS | Invalid options (quality out of range, invalid dimensions) | | E_EXTRACTION_FAILED | All frame extractions failed |

Example

try {
  const frames = await NativeVideoFrames.extractFrames("file:///path/to/video.mp4", [1000, 5000, 10000], {
    width: 800,
    quality: 0.95, // Higher quality for important frames
  });
  console.log("Extracted frames:", frames);
} catch (error) {
  if (error.code === "E_INVALID_URL") {
    console.error("Invalid video URL");
  }
}

Architecture

Turbo Module

This library uses React Native's new Turbo Module architecture for optimal performance:

  • Type-Safe Bridge — Codegen generates type-safe native bindings from TypeScript
  • Synchronous Initialization — Faster module loading compared to legacy NativeModules
  • C++ Core — Direct JSI integration without JSON serialization overhead

iOS Implementation

Three-Layer Architecture:

  1. TypeScript Layer (src/NativeVideoFrames.ts)

    • Defines the TurboModule interface
    • Exported types for TypeScript consumers
  2. Objective-C++ Bridge (ios/RNVideoFrames.mm)

    • Converts JavaScript types to native types
    • Bridges to Swift implementation
  3. Swift Implementation (ios/NativeVideoFrames.swift)

    • Uses AVAssetImageGenerator for frame extraction
    • Background processing with DispatchQueue
    • Memory-efficient with autoreleasepool

Key Implementation Details:

  • Aspect Ratio PreservationAVAssetImageGenerator.maximumSize automatically maintains aspect ratio
  • Rotation HandlingappliesPreferredTrackTransform ensures correct frame orientation
  • Memory Managementautoreleasepool prevents memory buildup during batch extraction
  • Error Handling — Gracefully skips failed frames instead of rejecting the entire operation

Android Implementation

Pure Kotlin TurboModule:

  1. TypeScript Layer — Same interface as iOS
  2. Codegen-Generated Java Spec — Abstract base class for the module
  3. Kotlin Implementation (android/src/main/java/io/mgcrea/rnvideoframes/NativeVideoFramesModule.kt)
    • Uses MediaMetadataRetriever for frame extraction
    • Background processing with ExecutorService
    • Memory-efficient with bitmap.recycle()

Key Implementation Details:

  • Aspect Ratio Preservation — Calculates target dimensions maintaining original ratio
  • Efficient Scaling — Uses getScaledFrameAtTime() on API 27+ for native scaling
  • Precise ModeOPTION_CLOSEST (API 28+) for exact frames, OPTION_CLOSEST_SYNC fallback
  • URI Support — Handles file://, content://, and absolute paths
  • Memory Management — Explicit bitmap.recycle() after each frame to prevent OOM

Development

Setup

# Clone the repository
git clone https://github.com/mgcrea/react-native-video-frames.git
cd react-native-video-frames

# Install dependencies
pnpm install

# Install iOS dependencies
cd ios && pod install && cd ..

# Build the module
pnpm build

Running the Example App

# Start Metro bundler
pnpm dev

# In another terminal, run iOS
pnpm run install:ios

# Or run Android
cd example/android && ./gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apk

Testing

# Type checking
pnpm check

# Linting
pnpm lint

# Run tests
pnpm test

Roadmap

  • [x] Android support using MediaMetadataRetriever
  • [x] Configurable JPEG quality option
  • [x] Precise frame extraction mode
  • [ ] Support for PNG output format
  • [ ] Batch extraction progress callbacks
  • [ ] Video thumbnail generation helper

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Authors

License

The MIT License

Copyright (c) 2025 Olivier Louvignes <[email protected]>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.