How FadCam Manages Video Segments and Auto-Splitting: Architecture Explained

FadCam stores every editable video portion as a Clip object inside a Timeline, using a robust splitAt() method for manual editing and a size-monitoring RecordingService for automatic segment creation when file limits are reached.

FadCam (anonfaded/FadCam) is an open-source Android camera application that handles long recordings through intelligent segment management. Understanding how it structures video segments and performs both manual and automatic splitting is essential for developers looking to implement similar timeline-based editing or automatic file rotation features. The architecture centers on two core classes—Clip and Timeline—working alongside recording services that monitor file size thresholds.

Clip Representation: The Atomic Video Segment

Every visual element on FadCam’s editing timeline maps 1‑to‑1 to a Clip object defined in app/src/main/java/com/fadcam/ui/faditor/model/Clip.java. This class encapsulates not just the media reference, but all transformation properties that persist when a segment is split.

public class Clip {
    @NonNull private final String id;          // Unique identifier (UUID)
    @NonNull private final Uri sourceUri;      // Original video file path
    private long inPointMs;                     // Trim start in milliseconds
    private long outPointMs;                    // Trim end in milliseconds
    private long sourceDurationMs;              // Original untrimmed length
    private float speed = 1.0f;                 // Playback speed multiplier
    private float volume = 1.0f;                // Audio level (0.0 to 1.0)
    private int rotation = 0;                   // Rotation in degrees
    private boolean flipHorizontal = false;   // Mirror transform
    private RectF cropRect = new RectF(0, 0, 1, 1); // Normalized crop box
    
    // Clone constructor preserves all effects during splitting
    public Clip(@NonNull Clip other) {
        this.id = UUID.randomUUID().toString();
        this.sourceUri = other.sourceUri;
        this.inPointMs = other.inPointMs;
        this.outPointMs = other.outPointMs;
        this.sourceDurationMs = other.sourceDurationMs;
        this.speed = other.speed;
        this.volume = other.volume;
        this.rotation = other.rotation;
        this.flipHorizontal = other.flipHorizontal;
        this.cropRect = new RectF(other.cropRect);
    }
}

When FadCam converts a Clip for playback, it generates a Media3 MediaItem with a ClippingConfiguration set to the inPointMs and outPointMs. Because the clone constructor copies all effect parameters (speed, volume, rotation, etc.), splitting a clip automatically preserves the user’s creative adjustments across both resulting segments.

Timeline: The Central Segment Manager

The Timeline class in app/src/main/java/com/fadcam/ui/faditor/model/Timeline.java serves as the single source of truth for video composition. It maintains an ordered list of clips and provides the primary API for segment manipulation.

Key responsibilities of the Timeline include:

  • Ordered storage: private final List<Clip> clips maintains chronological sequence
  • Duration calculation: getTotalDurationMs() aggregates the duration of all clips, accounting for speed changes
  • Structural operations: addClip(), removeClip(), swapClips(), and moveClip() handle reordering
  • Splitting logic: splitAt(int clipIndex, long splitPointMs) executes the core segmentation algorithm

Manual Splitting Logic via splitAt()

Manual splitting occurs when a user taps the split button in the editor. The Timeline.splitAt() method enforces a 100-millisecond minimum distance from clip edges to avoid creating micro-segments, then performs a non-destructive split by cloning the original clip and adjusting its boundary points.

public int splitAt(int clipIndex, long splitPointMs) {
    // Validate index bounds
    if (clipIndex < 0 || clipIndex >= clips.size()) {
        return -1;
    }
    
    Clip original = clips.get(clipIndex);
    
    // Enforce minimum 100ms buffer from edges
    long minSplitMs = original.getInPointMs() + 100;
    long maxSplitMs = original.getOutPointMs() - 100;
    
    if (splitPointMs < minSplitMs || splitPointMs > maxSplitMs) {
        return -1; // Split point too close to edge
    }
    
    // Create two clips inheriting all effects from original
    Clip clipA = new Clip(original);
    clipA.setOutPointMs(splitPointMs);
    
    Clip clipB = new Clip(original);
    clipB.setInPointMs(splitPointMs);
    
    // Atomic replacement: remove original, insert A and B
    clips.remove(clipIndex);
    clips.add(clipIndex, clipB); // Add second part first (higher index)
    clips.add(clipIndex, clipA);  // Add first part at original index
    
    return clipIndex; // Return index of clipA (first part)
}

This approach ensures that metadata like speed adjustments or crop rectangles remain consistent across the split point, providing a seamless editing experience where segments behave as a continuous whole during playback.

UI Integration in FaditorEditorActivity

The editor interface bridges user input and the Timeline API through FaditorEditorActivity.java. When the user positions the playhead and triggers a split, the activity calculates the absolute time position and invokes the Timeline method.

private void splitAtPlayhead() {
    long currentPositionMs = player.getCurrentPosition();
    int selectedClipIndex = timelineView.getCurrentClipIndex();
    
    // Convert player position to clip-local time
    long clipStartTime = timeline.getClipStartTimeMs(selectedClipIndex);
    long absoluteSplitMs = currentPositionMs - clipStartTime;
    
    int newIndex = timeline.splitAt(selectedClipIndex, absoluteSplitMs);
    
    if (newIndex < 0) {
        Toast.makeText(this, R.string.faditor_split_error, Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, R.string.faditor_split_success, Toast.LENGTH_SHORT).show();
        refreshTimelineView();
        updatePlayerClippingConfiguration();
    }
}

The UI enforces visual feedback through Toast messages defined in string resources, distinguishing between successful splits and validation errors (such as attempting to split within 100ms of a clip boundary).

Auto-Splitting Implementation

FadCam’s auto-splitting feature automatically segments long recordings into multiple files when they approach a user-defined size limit (defaulting to 2GB, with options up to 4GB). This prevents filesystem limitations and ensures compatibility with older storage formats that impose file size caps.

Configuration Layer

The feature is controlled via value_video_split_enabled in the settings layout app/src/main/res/layout/fragment_settings_video.xml. When enabled, the recording service receives the size threshold (in bytes) through shared preferences.

RecordingService Logic

The automatic splitting logic resides in RecordingService.java within the app/src/main/java/com/fadcam/service/ package. During active recording, the service monitors the output file size after each write cycle:

private void monitorRecordingSize() {
    if (!videoSplittingEnabled) return;
    
    long maxBytes = splitSizeLimitBytes; // From user settings (2GB = 2_147_483_648L)
    File currentFile = recorder.getCurrentOutputFile();
    
    if (currentFile.length() >= maxBytes) {
        // Finalize current segment
        String finalizedPath = stopCurrentRecording();
        
        // Create new clip entry in Timeline for the completed segment
        Clip completedClip = new Clip(finalizedPath);
        timeline.addClip(completedClip);
        
        // Start fresh recording seamlessly
        startNewRecordingSegment();
        
        // The new recording automatically generates the next Clip on completion
    }
}

When the size threshold triggers, the service:

  1. Finalizes the current MediaRecorder instance and closes the file
  2. Creates a new Clip object referencing the completed file, adding it to the Timeline with inPointMs at 0 and outPointMs at the actual duration
  3. Immediately initializes a new MediaRecorder targeting a fresh filename (typically timestamped or indexed)
  4. Continues capture without user-visible interruption

This process creates a sequence of Clip entries that appear as continuous content in the editor, despite residing in separate physical files on disk.

Interaction Between Manual and Automatic Splitting

Both splitting modalities converge on the same data model, ensuring architectural consistency:

  • Manual splits (editor UI) use Timeline.splitAt() to subdivide existing clips during post-production
  • Auto-splits (recording service) append new Clip instances to the Timeline when file size limits trigger during capture

Because both paths generate standard Clip objects with identical property inheritance, the export engine (ExportService) processes auto-split and manually-split segments through the same pipeline—applying transitions, mixing audio, and rendering effects without distinguishing the split origin.

Summary

  • Clip Architecture: Each segment is a Clip object in Clip.java storing trim points, effects, and source URI, with a copy constructor that preserves all properties during splits.
  • Timeline Management: The Timeline class maintains an ordered List<Clip> and provides splitAt() with 100ms boundary validation to prevent micro-segments.
  • Manual Splitting: FaditorEditorActivity.java translates playhead positions into splitAt() calls, with immediate UI feedback via Toast messages.
  • Auto-Splitting: RecordingService.java monitors file size against value_video_split_enabled preferences, automatically finalizing and restarting recordings at 2GB (or user-defined) thresholds while appending new clips to the Timeline.
  • Unified Model: Both splitting methods produce identical Clip objects, allowing seamless editing and export regardless of whether segmentation occurred during recording or post-processing.

Frequently Asked Questions

How does FadCam preserve video effects when splitting a clip?

When Timeline.splitAt() executes, it invokes the Clip copy constructor (new Clip(original)) to create two new instances. This constructor duplicates all effect parameters—including speed, volume, rotation, flipHorizontal, and cropRect—so both resulting segments inherit the exact same visual and audio adjustments as the original.

What is the minimum duration required between split points?

The splitAt() method enforces a 100-millisecond buffer from both the inPointMs and outPointMs of the original clip. If the requested split point falls within 100ms of either edge, the method returns -1 and the UI displays R.string.faditor_split_error, preventing the creation of unusable micro-segments.

Where is the auto-split file size limit configured?

Users enable automatic splitting via Settings → Video → "Enable Video Splitting", which controls the boolean preference value_video_split_enabled. The threshold size (default 2GB, maximum 4GB) is passed to RecordingService.java, where the monitorRecordingSize() method compares currentFile.length() against this limit to trigger segment rotation.

Can auto-split recordings be edited the same way as manually split videos?

Yes. Both manual and automatic splits produce identical Clip objects stored in the Timeline. Auto-split clips reference separate physical files but expose the same properties (inPointMs, outPointMs, effects) as manually split clips, allowing the FaditorEditorActivity to perform drag-and-drop reordering, trimming, and effect adjustments without distinguishing the split origin.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →