mpeg

mpeg is a small Dart wrapper around the system ffmpeg and ffprobe binaries. It uses dart:io processes directly, so it works well in server-side Dart apps, Docker containers, and macOS environments where FFmpeg is installed on the host.

Features

  • Check whether ffmpeg and ffprobe are available with MpegClient.isSupported()
  • Read parsed ffmpeg and ffprobe version information
  • Run ffmpeg and ffprobe commands with Process.run or Process.start
  • Decode ffprobe JSON output with probeJson()
  • Pull out parsed format metadata, streams, duration, bitrate, and primary video/audio streams
  • Parse ffmpeg -formats / ffprobe -formats output into structured MpegFormat objects
  • Resolve common supported video file extensions from the detected format list for both input and output support
  • Use low-friction helpers for generic transcode, remux, and still-frame extraction
  • Configure executable paths, base arguments, environment variables, and shell behavior

Installation

Add the package:

dart pub add mpeg

You also need FFmpeg installed on the machine that runs your Dart code.

Docker Linux

For Debian/Ubuntu-based Docker images, install FFmpeg like this:

RUN apt-get update && apt-get install -y ffmpeg

That installs both ffmpeg and ffprobe.

macOS

Install FFmpeg with Homebrew:

brew install ffmpeg

You can verify the install with:

ffmpeg -version
ffprobe -version

Usage

Check whether FFmpeg is available

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  bool supported = await MpegClient.isSupported();
  if (!supported) {
    throw StateError('ffmpeg/ffprobe are not installed or not on PATH.');
  }
}

Inspect supported formats

getFormats() parses the real -formats output from ffprobe first, then falls back to ffmpeg if needed.

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  MpegClient client = MpegClient();

  List<MpegFormat> formats = await client.getFormats();
  for (MpegFormat format in formats.take(5)) {
    print(
      '${format.primaryName}: demux=${format.canDemux}, mux=${format.canMux}, '
      'device=${format.isDevice}',
    );
  }

  List<String> inputExtensions =
      await client.getSupportedVideoInputExtensions();
  List<String> outputExtensions =
      await client.getSupportedVideoOutputExtensions();
  print('Inputs: $inputExtensions');
  print('Outputs: $outputExtensions');
}

getSupportedVideoExtensions() resolves common video file extensions from the parsed format names. The input/output convenience methods are useful when you want to answer questions like "which video extensions can this machine decode?" versus "which containers can it mux?"

Read ffmpeg version information

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  MpegClient client = MpegClient();

  MpegVersionInfo ffmpegVersion = await client.getFfmpegVersionInfo();
  MpegVersionInfo ffprobeVersion = await client.getFfprobeVersionInfo();

  print(ffmpegVersion.version);
  print(ffprobeVersion.configurationLine);
}

Probe targeted metadata helpers

If you do not want to manually walk the full JSON payload, the client exposes small convenience helpers:

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  MpegClient client = MpegClient();

  Duration? duration = await client.probeDuration(inputPath: 'output.mp4');
  int? bitRate = await client.probeBitRate(inputPath: 'output.mp4');
  Map<String, dynamic>? videoStream = await client.probePrimaryVideoStream(
    inputPath: 'output.mp4',
  );

  print(duration);
  print(bitRate);
  print(videoStream?['width']);
}

Transcode a file and probe the result

import 'dart:convert';
import 'dart:io';

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  MpegClient client = MpegClient();
  File inputFile = File('input.mov');
  File outputFile = File('output.mp4');

  ProcessResult result = await client.runFfmpeg([
    '-y',
    '-i',
    inputFile.path,
    '-vf',
    'scale=1280:720',
    '-c:v',
    'libx264',
    '-pix_fmt',
    'yuv420p',
    '-movflags',
    '+faststart',
    outputFile.path,
  ]);

  if (result.exitCode != 0) {
    stderr.writeln(result.stderr);
    return;
  }

  Map<String, dynamic> metadata = await client.probeJson(
    inputPath: outputFile.path,
  );
  print(const JsonEncoder.withIndent('  ').convert(metadata));
}

Use the convenience operations

These are just thin wrappers around common ffmpeg command shapes, so you still control the real ffmpeg flags.

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  MpegClient client = MpegClient();

  await client.transcode(
    inputPath: 'input.mov',
    outputPath: 'output.mp4',
    ffmpegArguments: [
      '-vf',
      'scale=1280:720',
      '-c:v',
      'libx264',
      '-pix_fmt',
      'yuv420p',
    ],
  );

  await client.remux(
    inputPath: 'input.mov',
    outputPath: 'faststart.mp4',
    ffmpegArguments: ['-movflags', '+faststart'],
  );

  await client.extractFrame(
    inputPath: 'input.mov',
    outputPath: 'thumbnail.png',
    position: const Duration(milliseconds: 500),
    width: 320,
    height: 180,
  );
}

Stream process output with Process.start

If you want progress events or long-running streaming output, use startFfmpeg() or startFfprobe():

import 'dart:convert';
import 'dart:io';

import 'package:mpeg/mpeg.dart';

Future<void> main() async {
  MpegClient client = MpegClient();
  Process process = await client.startFfmpeg(
    arguments: [
      '-i',
      'input.mov',
      '-progress',
      'pipe:2',
      'output.mp4',
    ],
  );

  await for (String line in process.stderr
      .transform(utf8.decoder)
      .transform(const LineSplitter())) {
    print(line);
  }

  int exitCode = await process.exitCode;
  print('ffmpeg exited with $exitCode');
}

Configuration

You can point the client at custom binary paths or wrapper commands:

import 'package:mpeg/mpeg.dart';

MpegClient client = MpegClient(
  ffmpegExecutable: '/usr/local/bin/ffmpeg',
  ffprobeExecutable: '/usr/local/bin/ffprobe',
  workingDirectory: '/tmp',
  environment: {'FFREPORT': 'file=ffreport.log:level=32'},
);

API Overview

  • MpegClient.isSupported() checks whether ffmpeg and optionally ffprobe can be started
  • runFfmpeg() and runFfprobe() wrap Process.run
  • startFfmpeg() and startFfprobe() wrap Process.start
  • probeJson() returns decoded ffprobe JSON
  • probeFormat(), probeStreams(), probeDuration(), probeBitRate(), probePrimaryVideoStream(), and probePrimaryAudioStream() provide targeted probe helpers
  • getFormats() returns parsed MpegFormat rows from -formats
  • getSupportedVideoExtensions(), getSupportedVideoInputExtensions(), and getSupportedVideoOutputExtensions() return common video extensions inferred from the detected formats
  • getFfmpegVersionInfo() and getFfprobeVersionInfo() parse version output into MpegVersionInfo
  • transcode(), remux(), and extractFrame() wrap common ffmpeg operations
  • MpegException includes the command, arguments, exit code, stdout, stderr, and original cause when available

Libraries

mpeg
A lightweight Dart wrapper around ffmpeg and ffprobe.