# fMP4 Recording in FadCam: Code Examples and Implementation Guide

> Explore fMP4 recording in FadCam with code examples. Learn how to implement crash-safe, stream-ready recordings using FragmentedMp4Muxer for automatic 2-second fragments.

- Repository: [Faded/FadCam](https://github.com/anonfaded/FadCam)
- Tags: how-to-guide
- Published: 2026-05-13

---

**FadCam records fragmented MP4 (fMP4) videos using the Media3 FragmentedMp4Muxer wrapped in a custom FragmentedMp4MuxerWrapper class, enabling crash-safe, stream-ready recordings with automatic 2-second fragments.**

FadCam is an open-source Android camera app that implements fragmented MP4 recording through the Media3 ExoPlayer library. Unlike traditional MP4 containers that require header finalization at the end of recording, fMP4 writes self-contained `moof`/`mdat` fragments on-the-fly, making videos resilient to crashes and immediately compatible with HTTP Live Streaming (HLS). The implementation centers around a custom wrapper class that handles track registration, timestamp normalization, and seamless segment roll-over for continuous recording.

## What Is fMP4 Recording and Why FadCam Uses It

Fragmented MP4 (fMP4) splits video data into independent segments containing their own metadata headers. Each fragment is typically 2 seconds long and includes all information necessary for decoding, eliminating the need for a central index that traditional MP4 files require at the end of the file.

**Key advantages for FadCam users:**

- **Crash safety:** If the app terminates unexpectedly, recorded fragments remain playable because each fragment is self-contained.
- **Live streaming readiness:** The 2-second fragments align with HLS segment requirements, enabling real-time streaming capabilities.
- **Seekable recordings:** Fragment boundaries allow the player to seek without scanning the entire file.

## Core Architecture for fMP4 Recording

The fMP4 implementation in FadCam spans three primary components: the muxer wrapper that interfaces with Media3, the OpenGL recording pipeline that manages encoders, and the service layer that orchestrates the recording lifecycle.

### The FragmentedMp4MuxerWrapper Class

Located at [`app/src/main/java/com/fadcam/media/FragmentedMp4MuxerWrapper.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/media/FragmentedMp4MuxerWrapper.java), this class encapsulates `androidx.media3.muxer.FragmentedMp4Muxer` and adds Android-specific conveniences for track management and metadata handling.

The wrapper configures **2-second fragments** via `.setFragmentDurationMs(2000)` (lines 80‑88), which ensures stable streaming and proper `tfdt` (fragment decode time) timestamps. It also handles **timestamp normalization** (lines 58‑62) by tracking per-track offsets to restart timestamps from 0 ms for each new recording segment, preventing decode errors during playback.

Additional responsibilities include:

- Converting `MediaFormat` objects to Media3 `Format` instances in `addTrack()` (lines 45‑58)
- Injecting orientation hints and GPS location metadata before calling `start()` (lines 82‑99)

### GLRecordingPipeline Integration

The `GLRecordingPipeline` class at [`app/src/main/java/com/fadcam/opengl/GLRecordingPipeline.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/opengl/GLRecordingPipeline.java) instantiates the muxer wrapper and drives the recording process. The `setupMuxer()` method (lines 46‑56) creates a new `FragmentedMp4MuxerWrapper` instance for each output file or file descriptor, passing location metadata when available.

During the render loop, the pipeline drains encoder output buffers and writes samples to the muxer. When segment size limits are reached or manual roll-over is requested, the pipeline finalizes the current fragment and reinitializes the wrapper (lines 76‑84), preserving monotonic timestamps across segments.

### RecordingService Orchestration

[`RecordingService.java`](https://github.com/anonfaded/FadCam/blob/main/RecordingService.java) at [`app/src/main/java/com/fadcam/services/RecordingService.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/services/RecordingService.java) manages the lifecycle of the GL pipeline, ensuring the fMP4 muxer is utilized for both camera and screen recording paths. The service handles resource cleanup and guarantees that the muxer receives stop signals even if the recording ends abruptly.

## fMP4 Recording Code Examples

The following Java snippets demonstrate the essential steps for recording fragmented MP4 video in FadCam, extracted from the actual source code.

### Initializing the Fragmented Muxer

The `setupMuxer()` method in `GLRecordingPipeline` creates the wrapper and attaches GPS metadata:

```java
// Called for each new segment
private void setupMuxer() throws IOException {
    // Use FragmentedMp4MuxerWrapper for fMP4 output
    if (currentOutputFd != null) {
        mediaMuxer = new FragmentedMp4MuxerWrapper(currentOutputFd);
    } else {
        mediaMuxer = new FragmentedMp4MuxerWrapper(currentOutputFilePath);
    }

    // Attach location metadata if available
    if (locationLatitude != null && locationLongitude != null) {
        mediaMuxer.setLocation(locationLatitude.floatValue(),
                               locationLongitude.floatValue());
    }

    // Reset track indices for the new segment
    audioTrackIndex = -1;
    videoTrackIndex = -1;
}

```

### Registering Audio and Video Tracks

After creating the muxer, the pipeline registers encoder formats and starts the fragmented container:

```java
int videoTrack = mediaMuxer.addTrack(videoFormat);   // videoFormat = MediaFormat from MediaCodec
int audioTrack = mediaMuxer.addTrack(audioFormat);   // audioFormat = MediaFormat from AudioRecorder
mediaMuxer.start();   // Begins writing fragmented MP4

```

### Writing Samples in the Render Loop

Each iteration drains encoder buffers and writes timestamped samples to the appropriate track:

```java
// Drain video encoder
ByteBuffer videoBuf = videoEncoder.getOutputBuffer(bufIndex);
long pts = videoEncoder.getOutputBufferInfo(bufIndex).presentationTimeUs;
mediaMuxer.writeSampleData(videoTrack, videoBuf, pts, false, false);

// Drain audio encoder (similar)
ByteBuffer audioBuf = audioEncoder.getOutputBuffer(bufIndex);
long pts = audioEncoder.getOutputBufferInfo(bufIndex).presentationTimeUs;
mediaMuxer.writeSampleData(audioTrack, audioBuf, pts, false, false);

```

### Handling Segment Roll-over

When splitting recordings (due to size limits or user request), the pipeline finalizes the current fragment and creates a fresh muxer:

```java
if (shouldSplitSegment()) {
    // Finalize current fragment
    mediaMuxer.stop();      // Flushes the last fragment
    // Create a fresh muxer for the next segment
    setupMuxer();
    // Re‑add tracks and start again
    videoTrack = mediaMuxer.addTrack(videoFormat);
    audioTrack = mediaMuxer.addTrack(audioFormat);
    mediaMuxer.start();
}

```

## Exporting and Remuxing fMP4 Files

While fMP4 is optimal for recording and streaming, some players require traditional seekable MP4 containers. FadCam includes `FragmentedMp4Remuxer` at [`app/src/main/java/com/fadcam/playback/FragmentedMp4Remuxer.java`](https://github.com/anonfaded/FadCam/blob/main/app/src/main/java/com/fadcam/playback/FragmentedMp4Remuxer.java) to convert recordings:

```java
FragmentedMp4Remuxer remuxer = new FragmentedMp4Remuxer();
remuxer.remux(fmp4Path, mp4OutputPath);

```

This utility reads the fragmented input and produces a standard MP4 with centralized moov headers, enabling compatibility with editing software and older media players.

## Key Files in the fMP4 Pipeline

Understanding the complete recording flow requires familiarity with these source files:

- **[`FragmentedMp4MuxerWrapper.java`](https://github.com/anonfaded/FadCam/blob/main/FragmentedMp4MuxerWrapper.java)** – Core wrapper around Media3's fragmented muxer; handles track addition, orientation hints, and timestamp normalization.
- **[`GLRecordingPipeline.java`](https://github.com/anonfaded/FadCam/blob/main/GLRecordingPipeline.java)** – Manages MediaCodec encoders, creates the muxer per segment, and drives sample writing in the render thread.
- **[`RecordingService.java`](https://github.com/anonfaded/FadCam/blob/main/RecordingService.java)** – Service that orchestrates the GL pipeline, handles start/stop commands, and ensures fMP4 muxer usage for all recording modes.
- **[`FragmentedMp4Remuxer.java`](https://github.com/anonfaded/FadCam/blob/main/FragmentedMp4Remuxer.java)** – Utility class for converting fMP4 recordings to standard MP4 format for export.
- **[`FragmentedMp4IndexBuilder.java`](https://github.com/anonfaded/FadCam/blob/main/FragmentedMp4IndexBuilder.java)** – Builds timestamp indexes for fast seeking within fMP4 files without requiring `sidx` boxes.
- **[`SeekableFragmentedMp4MediaSourceFactory.java`](https://github.com/anonfaded/FadCam/blob/main/SeekableFragmentedMp4MediaSourceFactory.java)** – Factory for creating ExoPlayer media sources that support seeking within fragmented recordings during playback.

## Summary

- **FadCam implements fMP4 recording** through a custom `FragmentedMp4MuxerWrapper` that encapsulates Media3's `FragmentedMp4Muxer` with Android-specific metadata handling.
- **2-second fragments** are configured via `setFragmentDurationMs(2000)`, producing self-contained `moof`/`mdat` boxes that ensure crash safety and HLS compatibility.
- **Segment roll-over** is handled by the `GLRecordingPipeline` finalizing the current muxer and instantiating a new wrapper, with timestamp normalization preventing decode gaps.
- **Source code locations** include [`FragmentedMp4MuxerWrapper.java`](https://github.com/anonfaded/FadCam/blob/main/FragmentedMp4MuxerWrapper.java) for muxer logic and [`GLRecordingPipeline.java`](https://github.com/anonfaded/FadCam/blob/main/GLRecordingPipeline.java) for the recording workflow.
- **Export capability** is provided by [`FragmentedMp4Remuxer.java`](https://github.com/anonfaded/FadCam/blob/main/FragmentedMp4Remuxer.java), which converts fMP4 to standard MP4 for compatibility with non-streaming players.

## Frequently Asked Questions

### What makes fMP4 recording crash-safe compared to traditional MP4?

Traditional MP4 files write a central `moov` header at the end of recording containing all sample offsets; if the app crashes before finalization, the file becomes unplayable. FadCam's fMP4 implementation writes self-contained 2-second fragments with embedded `moof` headers, so each fragment is independently playable even if the recording terminates unexpectedly.

### How does FadCam handle video segmentation during long recordings?

The `GLRecordingPipeline` monitors recording duration and file size, triggering a segment roll-over when limits are reached. This process calls `mediaMuxer.stop()` to flush the current fragment, then creates a new `FragmentedMp4MuxerWrapper` instance via `setupMuxer()`, re-registers tracks, and continues recording with normalized timestamps starting from zero.

### Can fMP4 recordings from FadCam be played in standard video players?

Most modern players and browsers support fMP4 playback, but older software may require conversion. FadCam includes `FragmentedMp4Remuxer`, which remuxes the fragmented container into a traditional MP4 with centralized headers, ensuring compatibility with legacy editors and players that expect non-fragmented structures.

### What is the default fragment duration in FadCam's fMP4 implementation?

The default fragment duration is **2 seconds** (2000 milliseconds), configured in [`FragmentedMp4MuxerWrapper.java`](https://github.com/anonfaded/FadCam/blob/main/FragmentedMp4MuxerWrapper.java) via the builder method `.setFragmentDurationMs(2000)`. This duration balances streaming latency with encoding efficiency, providing stable `tfdt` timestamps suitable for HLS delivery.