video_manipulation 0.1.7

  • Readme
  • Changelog
  • Example
  • Installing
  • 71

video_manipulation #

A library for manipulating videos. This library has the following features

  • Changing a videos framerate
  • Changing a videos speed/duration
  • Adding still frames to an existing video, e.g. watermarks
  • Generate videos from images

NOTE: Currently supports iOS only

Install #

Simply add the following line to your pubspec.yaml file:

video_manipulation: ^0.1.7

Usage #

The plugin currently has one static method for all video manipulation: .generateVideo(List<String> paths, String filename, int fps, double speed).

Parameters paths list of input file paths. Can be images (.jpg or .png) or video files (.mp4) that are used to generate the new video. E.g.: ["documents/input.mp4", "documents/watermark.jpg] - in this case the plugin would add the "watermark.jpg" image to every frame of the "input.mp4" video. The image would be scaled to have the same size as the "input.mp4" video.

filename the filename of the generated video. The directory of the first input file is used as output directory.

fps frames per second of the output file.

speed a value between 0.1 (slowmotion) and 1xxx.0 (timelapse). 1.0 to keep the same speed.

The method returns the full path of the generated video when successful.

How it works #

This plugin is using AVFoundation on the iOS part to extract frames from a video and also encoding them back to an mp4 file. Android has currently no implementation. FFmpeg won`t be implemented due to licensing, extra overhead from the ffmpeg binaries and overall slow performance. A possible solution could be http://jcodec.org/

0.1.7 #

  • Added demo example
  • Updated readme

0.1.6 #

  • Fixed memory issues during frame extraction

example/lib/main.dart

import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:video_player/video_player.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';
import 'package:video_manipulation/video_manipulation.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(home: HomeScreen());
}

class HomeScreen extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<HomeScreen> {
  bool _isGenerating = false;
  String _inputAssetpath = "assets/clock.mp4";
  String _outputFilepath;
  Duration _generationTime = Duration.zero;
  String _infoText = "";

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Video Timelapsing Example App'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              RaisedButton(
                child: Text("Show original video"),
                onPressed: _showOriginalVideo,
              ),
              RaisedButton(
                child: Text("Generate timelapse video"),
                onPressed: _isGenerating ? null : _generateTimelapse,
              ),
              RaisedButton(
                child: Text("Show timelapse video"),
                onPressed: _outputFilepath == null ? null : _showGeneratedVideo,
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 40),
                child: Text(_infoText),
              ),
            ],
          ),
        ),
      ),
    );
  }

  _showOriginalVideo() => Navigator.of(this.context)
      .push(MaterialPageRoute(builder: (_) => Player(videoUrl: _inputAssetpath)));

  _showGeneratedVideo() => Navigator.of(this.context)
      .push(MaterialPageRoute(builder: (_) => Player(videoUrl: _outputFilepath)));

  _generateTimelapse() async {
    final speed = 6.0;
    final framerate = 60;
    final inputFilename = "clock.mp4";
    final outputFilename = "clock-processed.mp4";
    final inputAsset = "assets/$inputFilename";
    final docDir = (await getApplicationDocumentsDirectory()).path;

    _infoText = "Generating Video with $speed x speed...";
    setState(() => _isGenerating = true);

    print("Clean up documents directory");
    if (await File(docDir + inputFilename).exists()) await File(docDir + inputFilename).delete();
    if (await File(docDir + outputFilename).exists())
      await File(docDir + outputFilename).delete();

    print("Copy input file to documents directory");
    final data = await rootBundle.load(inputAsset);
    final bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
    final inputFilepath = join(docDir, inputFilename);
    await File(inputFilepath).writeAsBytes(bytes);

    print("Start generating video");
    final start = DateTime.now();
    _outputFilepath =
        await VideoManipulation.generateVideo([inputFilepath], outputFilename, framerate, speed);
    _generationTime = DateTime.now().difference(start);

    print("Completed video generation");
    _infoText = "Generation took ${_generationTime.inSeconds} seconds";
    if (mounted) setState(() => _isGenerating = false);
  }
}

class Player extends StatefulWidget {
  final String videoUrl;
  final double width;
  final double height;
  final bool isLooped;

  Player({this.videoUrl, this.width, this.height, this.isLooped});

  @override
  createState() => PlayerState();
}

class PlayerState extends State<Player> {
  VideoPlayerController _controller;
  var _isPlaying = true;
  var _playbackPosition = 0.0;

  @override
  initState() {
    _initialisePlayer();
    super.initState();
  }

  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: _controller == null ? Container() : _loadedContentWidget(),
    );
  }

  Widget _loadedContentWidget() {
    return Container(
      width: widget.width,
      height: widget.height,
      child: SizedBox(
        child: AspectRatio(
          aspectRatio: _controller.value.aspectRatio,
          child: VideoPlayer(_controller),
        ),
      ),
    );
  }

  _initialisePlayer() async {
    VideoPlayerController controller;
    final documentsDirectory = await getApplicationDocumentsDirectory();
    final filename = fileName(widget.videoUrl);
    final localFile = File(documentsDirectory.path + '/' + filename);
    if (await localFile.exists()) {
      print("Initialised player with local file: " + localFile.path);
      controller = VideoPlayerController.file(localFile);
    } else if (isUrl(widget.videoUrl)) {
      print("Initialised player with url: " + widget.videoUrl);
      controller = VideoPlayerController.network(widget.videoUrl);
    } else {
      print("Initialised player with asset: " + widget.videoUrl);
      controller = VideoPlayerController.asset(widget.videoUrl);
    }
    controller.setLooping(widget.isLooped);
    await controller.initialize();
    final dur = controller.value.duration.inMilliseconds.roundToDouble();
    await controller.seekTo(Duration(milliseconds: (_playbackPosition * dur).round()));
    if (_isPlaying) await controller.play();
    final oldController = _controller;
    setState(() => _controller = controller);
    await oldController?.dispose();
  }

  bool isUrl(String url) => url.contains("https://") || url.contains("http://");

  String fileName(String url) => basename(url).split('?').first;
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  video_manipulation: ^0.1.7

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:video_manipulation/video_manipulation.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
49
Health:
Code health derived from static analysis. [more]
99
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
84
Overall:
Weighted score of the above. [more]
71
Learn more about scoring.

We analyzed this package on Apr 8, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.1
  • pana: 0.13.6
  • Flutter: 1.12.13+hotfix.8

Health issues and suggestions

Document public APIs. (-1 points)

5 out of 5 API elements have no dartdoc comment.Providing good documentation for libraries, classes, functions, and other API elements improves code readability and helps developers find and use your API.

Format lib/video_manipulation.dart.

Run flutter format to format lib/video_manipulation.dart.

Maintenance suggestions

The package description is too short. (-16 points)

Add more detail to the description field of pubspec.yaml. Use 60 to 180 characters to describe the package, what it does, and its target use case.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.3.0-dev.0.1 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.11 1.14.12
meta 1.1.8
sky_engine 0.0.99
typed_data 1.1.6
vector_math 2.0.8