Video

Video

Video is an electronic medium for the recording, copying, and broadcasting of moving visual images. Use more specific topic when relevant, such as for questions related to trimming and modifying videos, for questions related to editing videos into any format, and for questions related to processing videos with filtering video frames.

Control YouTube's Video Player with JavaScript

YouTube has become the standard way for delivering high quality video on the web. Sometimes, when you embed a video in your web application or landing page, you need a great deal of control on what and how is displayed. This is why we are going to show you how you can use the YouTube JavaScript Player API.

Initializing the player

The first step is to add a placeholder for the player and include the YouTube API.

<div id="video-placeholder"></div>

<script src="https://www.youtube.com/iframe_api"></script>

When the API is fully loaded, it looks for a global function called onYouTubeIframeAPIReady() which you should define. Inside it we will create a new instance of YouTube player. The first argument is the id of an HTML element we want to be replaced by the player, in our case that's video-placeholder. The second one is an object containing the player options:

  • The width and height of the player. These can be overwritten by applying CSS to #video-placeholder.
  • The id of the video we want to be embedded when the player loads. You can get this id from any YouTube link by taking the string after ?v= (e.g. youtube.com/watch?v=WwoKkq685Hk)
  • The playerVars object is a set of parameters. We made the color of the player white and created a playlist by providing two additional videos ids, separated by a coma. You can see a list of all available properties here.
  • The events object consists of event listeners and the functions they call. The API passes down an event object as the only attribute, containing the target and data. You can read more about events here.

The whole code look something like this:

var player;

function onYouTubeIframeAPIReady() {
    player = new YT.Player('video-placeholder', {
        width: 600,
        height: 400,
        videoId: 'Xa0Q0J5tOP0',
        playerVars: {
            color: 'white',
            playlist: 'taJ60kskkns,FG0fTKAqZ5g'
        },
        events: {
            onReady: initialize
        }
    });
}

The initialize() function will be called when the player fully loads. It will start an interval, updating some of our controls every second.

function initialize(){

    // Update the controls on load
    updateTimerDisplay();
    updateProgressBar();

    // Clear any old interval.
    clearInterval(time_update_interval);

    // Start interval to update elapsed time display and
    // the elapsed part of the progress bar every second.
    time_update_interval = setInterval(function () {
        updateTimerDisplay();
        updateProgressBar();
    }, 1000)

}

Displaying current time and video duration

This is done by the updateTimerDisplay(), one of the function called every second. It takes advantage of the API's methods to give us adequate information about the video length.

// This function is called by initialize()
function updateTimerDisplay(){
    // Update current time text display.
    $('#current-time').text(formatTime( player.getCurrentTime() ));
    $('#duration').text(formatTime( player.getDuration() ));
}

function formatTime(time){
    time = Math.round(time);

    var minutes = Math.floor(time / 60),
    seconds = time - minutes * 60;

    seconds = seconds < 10 ? '0' + seconds : seconds;

    return minutes + ":" + seconds;
}

Methods are called using the player object we created in the begging. We can get how many seconds into the video we are in with getCurrentTime(), and the total duration of the video with getDuration(). Both function will return second which we format correctly to look like time and then write into the DOM.

Progress Bar

This is done using the player.seekTo(sec) function, which jumps the video to the seconds provided in the parameter.

To demonstrate this we've made our own version of YouTube's progress bar, using an input field of type range. When we click anywhere on it, we take the inputs value, witch gives us a percentage. We then use this percentage to calculate what progress we want made to the video and skip to the according seconds.

$('#progress-bar').on('mouseup touchend', function (e) {

    // Calculate the new time for the video.
    // new time in seconds = total duration in seconds * ( value of range input / 100 )
    var newTime = player.getDuration() * (e.target.value / 100);

    // Skip video to new time.
    player.seekTo(newTime);

});

The code above allows us to control the video, but we also want the progress bar to move automatically as the video progresses. To understand how we do this, go back to the initialize() function and more specifically its every-second interval and updateProgressBar().

// This function is called by initialize()
function updateProgressBar(){
    // Update the value of our progress bar accordingly.
    $('#progress-bar').val((player.getCurrentTime() / player.getDuration()) * 100);
}

Playback Controls

Nothing out of the ordinary here. Just make two buttons and call the needed method on click.

$('#play').on('click', function () {
    player.playVideo();
});

$('#pause').on('click', function () {
    player.pauseVideo();
});

Sound Options

We can create a mute toggle button using the provided getter and setter methods of the player.

$('#mute-toggle').on('click', function() {
    var mute_toggle = $(this);

    if(player.isMuted()){
        player.unMute();
        mute_toggle.text('volume_up');
    }
    else{
        player.mute();
        mute_toggle.text('volume_off');
    }
});

If we want to set the volume using a percentage we can use a number input field and the setVolume() method. It will automatically validate the provided parameter, so we don't have to worry about passing it floating values or numbers out of the [0 : 100] interval.

$('#volume-input').on('change', function () {
    player.setVolume($(this).val());
});

Other Player Settings

Changing Playback Speed

The player.setPlaybackRate() method expects one of the following as its parameter:

  • 0.25
  • 0.5
  • 1
  • 1.5
  • 2

Create a <select> element in the HTML and set the speeds as it <option> children. User interaction with the select will result in change of the speed rate only for the currently playing video and will be reset to the default (speed of 1) when the next one starts.

$('#speed').on('change', function () {
    player.setPlaybackRate($(this).val());
});

Changing Video Quality

Altering the video quality works in a very similar way to altering the speed. The method for this is setPlaybackQuality() and the argument it expects is one of these strings:

  • highres
  • hd1080
  • hd720
  • large
  • medium
  • small

Note that this function only suggest what quality should be used as that highly depends on the internet connection and video itself.

$('#quality').on('change', function () {
    player.setPlaybackQuality($(this).val());
});

Playlists

We can play the next or previous video in a playlist using these methods form the API.

$('#next').on('click', function () {
    player.nextVideo()
});

$('#prev').on('click', function () {
    player.previousVideo()
});

If you want to play a specific video from the playlist, use player.playVideoAt(index), where index is an integer specifying which video to play, 0 being the first one.

Queue Video Dynamically

The last thing we are going to demonstrate, is how to dynamically add new videos to the player. If you check our our demo, in the bottom you'll see three thumbnails for cat videos. We added their YouTube links as data-attributes and when any of them gets clicked, the chosen video will be loaded into the player.

$('.thumbnail').on('click', function () {

    var url = $(this).attr('data-video-id');

    player.cueVideoById(url);

});

Conclusion

This wraps up our tutorial! We hope that you found this tutorial useful. If you wish to learn more about the YouTube API, check out these resources:

  • YouTube Iframe Player API Reference - here.
  • YouTube Player supported parameters - here.
  • YouTube Developers Live: Embedded Web Player Customization - here.

Original article source at: https://tutorialzine.com/

#youtube #javascript 

Control YouTube's Video Player with JavaScript
Hunter  Krajcik

Hunter Krajcik

1668588728

How to Install Openshot Video Editor on Linux Mint 21

Openshot is a popular, award-winning video editing tool used to edit videos on different operating systems including Linux. It offers an attractive and easy-to-use interface that helps to make amazing videos. It comes with different features like title templates, video transitions, support of different audio/video/image formats, unlimited tracks, 3D animation titles, time mapping, and many more.

Install Openshot on Linux Mint 21

Multiple ways are introduced by Linux systems to install the Openshot tool on Linux Mint 21 system, which are as following:

Method 1: Install Openshot Through Default Repository

To install Openshot from the default apt repository is quite an easy way. However, before getting started, you must update the system repository first through the below-given command:

$ sudo apt update

Now, run the following command to install Openshot on Linux Mint 21 system:

$ sudo apt install openshot-qt


Execute the version command to confirm the Openshot application installation:

$ openshot-qt --version

You can open the application on your system from the All Applications menu:

However, if you want to remove Openshot from the Linux Mint 21 system, you can type the following command:

$ sudo apt remove openshot-qt

Method 2: Install Openshot Using Software Manager

Visit Software Manager and search “Openshot” there. You will find multiple results there; pick the one with better rating:

Click to Install button to let it install on the Linux Mint system:

This requires sudo privileges to run Openshot application on Linux Mint 21 system. Type the Linux Mint password and Authenticate it to start the installation.

The process takes some time to install the application on Linux Mint.

Click the Launch button to get it on screen:

You can also remove the application by hitting on the Remove button.

Conclusion

Openshot is an open-source, powerful video editing tool used to create animation, and edit videos. It has a vast range of features including several animation tools, support of different video editing formats, digital effects, video transitions and many more. This article covered how to install Openshot on Linux Mint 21 system using terminal and GUI methods.

Original article source at: https://linuxhint.com/

#linux #install #video #editor 

How to Install Openshot Video Editor on Linux Mint 21
Rupert  Beatty

Rupert Beatty

1667594340

Gallery: Your Next Favorite Image and Video Picker

Gallery 

Your next favorite image and video picker

Description

We all love image pickers, don't we? You may already know of ImagePicker, the all in one solution for capturing pictures and selecting images. Well, it has a sibling too, called Gallery. Based on the same engine that powers ImagePicker, Gallery has a clearer flow based on albums and focuses on the use case of selecting video. If this suits your need, give it a try 😉

Gallery has 3 tabs with easy navigation through swipe gesture

  • Images: select albums and images. Handle selection with hightlighted numbers so your users don't forget the order
  • Camera: your photographer skill goes here
  • Videos: display all videos and select. For now the use case is to select one video at a time

And, it has zero dependencies 😎

Usage

Presenting

GalleryController is the main entry point, just instantiate and give it the delegate

let gallery = GalleryController()
gallery.delegate = self
present(gallery, animated: true, completion: nil)

The content controller is not loaded until the users navigate to, which offers a much faster experience.

Delegate

The GalleryControllerDelegate requires you to implement some delegate methods in order to interact with the picker

func galleryController(_ controller: GalleryController, didSelectImages images: [Image])
func galleryController(_ controller: GalleryController, didSelectVideo video: Video)
func galleryController(_ controller: GalleryController, requestLightbox images: [Image])
func galleryControllerDidCancel(_ controller: GalleryController)

The lightbox delegate method is your chance to display selected images. If you're looking for a nice solution, here is the Lightbox that we use and love

Resolving

The delegate methods give you Image and Video, which are just wrappers around PHAsset. To get the actual asset informations, we offer many convenient methods. See example

Image

  • Use instance method resolve to get the actual UIImage
  • Use static method Image.resolve to resolve a list of images

Video

  • Use instance method fetchDuration, fetchPlayerItem, fetchAVAsset, fetchThumbnail to get more information about the selected video.

Permission

Gallery handles permissions for you. It checks and askes for photo and camera usage permissions at first launch. As of iOS 10, we need to explicitly declare usage descriptions in plist files

<key>NSCameraUsageDescription</key>
<string>This app requires access to camera</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to photo library</string>

Configuration

There are lots of customization points in Config structs. For example

Config.Permission.image = UIImage(named: ImageList.Gallery.cameraIcon)
Config.Font.Text.bold = UIFont(name: FontList.OpenSans.bold, size: 14)!
Config.Camera.recordLocation = true
Config.tabsToShow = [.imageTab, .cameraTab]

Video Editor

Gallery cares more about video with its editing functionalities. We have VideoEditor and AdvancedVideoEditor to trim, resize, scale and define quality of the selected video

func galleryController(_ controller: GalleryController, didSelectVideo video: Video) {
  controller.dismiss(animated: true, completion: nil)

  let editor = VideoEditor()
  editor.edit(video: video) { (editedVideo: Video?, tempPath: URL?) in
    DispatchQueue.main.async {
      if let tempPath = tempPath {
        let controller = AVPlayerViewController()
        controller.player = AVPlayer(url: tempPath)

        self.present(controller, animated: true, completion: nil)
      }
    }
  }
}

With the Video object, you can fetchPlayerItem, fetchAVAsset and fetchThumbnail as well

And, of course, you have the ability to customize it

Config.VideoEditor.maximumDuration = 30
Config.VideoEditor.savesEditedVideoToLibrary = true

Installation

Gallery is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Gallery'

Gallery is also available through Carthage. To install just write into your Cartfile:

github "hyperoslo/Gallery"

Gallery can also be installed manually. Just download and drop Sources folders in your project.

Contributing

We would love you to contribute to Gallery, check the CONTRIBUTING file for more info.

Download Details:

Author: Hyperoslo
Source Code: https://github.com/hyperoslo/Gallery 
License: View license

#swift #ios #video #image

Gallery: Your Next Favorite Image and Video Picker

Alltube: Web GUI for Youtube-dl

AllTube Download

HTML GUI for youtube-dl

Screenshot

Setup

From a release package

You can download the latest release package here.

You just have to unzip it on your server and it should be ready to use.

From Git

In order to get AllTube working, you need to use Composer:

composer install

This will download all the required dependencies.

You should also ensure that the templates_c folder has the right permissions:

chmod 770 templates_c/

(You need to adapt this to your permission model. You can find more information about this in the Smarty documentation.)

If your web server is Apache, you need to set the AllowOverride setting to All or FileInfo.

Update

When updating from Git, you need to run Composer again:

git pull
composer install

On Heroku

Deploy

On Cloudron

Cloudron is a complete solution for running apps on your server and keeping them up-to-date and secure.

Install

The source code for the package can be found here.

Config

If you want to use a custom config, you need to create a config file:

cp config/config.example.yml config/config.yml

PHP requirements

You will need PHP 7.2 (or higher) and the following PHP modules:

  • intl
  • mbstring
  • gmp

Web server configuration

If you want to serve the application under a basepath and/or with a different internal than external port (scenario: nginx->docker setup) Alltube supports the following X-Forwarded headers:

  • X-Forwarded-Host (ex. another.domain.com)
  • X-Forwarded-Path (ex: /alltube)
  • X-Forwarded-Port (ex: 5555)
  • X-Forwarded-Proto (ex: https)

Apache

The following modules are recommended:

  • mod_mime
  • mod_rewrite
  • mod_expires
  • mod_filter
  • mod_deflate
  • mod_headers

Nginx

Here is an example Nginx configuration:

server {
        server_name localhost;
        listen 443 ssl;

        root /var/www/path/to/alltube;
        index index.php;

        access_log  /var/log/nginx/alltube.access.log;
        error_log   /var/log/nginx/alltube.error.log;

        types {
                text/html   html htm shtml;
                text/css    css;
                text/xml    xml;
                application/x-web-app-manifest+json   webapp;
        }

        # Deny access to dotfiles
        location ~ /\. {
                deny all;
        }

        location / {
                try_files $uri /index.php?$args;
        }

        location ~ \.php$ {
                try_files $uri /index.php?$args;

                fastcgi_param     PATH_INFO $fastcgi_path_info;
                fastcgi_param     PATH_TRANSLATED $document_root$fastcgi_path_info;
                fastcgi_param     SCRIPT_FILENAME $document_root$fastcgi_script_name;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_intercept_errors off;

                fastcgi_buffer_size 16k;
                fastcgi_buffers 4 16k;

                include fastcgi_params;
        }
}

Other dependencies

You need ffmpeg in order to enable conversions. (Conversions are disabled by default.)

On Debian-based systems:

sudo apt-get install ffmpeg

If your ffmpeg binary is not installed at /usr/bin/ffmpeg, you also need to edit the ffmpeg variable in config.yml.

Use as a library

The Video class is now available as a separate package so that you can reuse it in your projects.

JSON API

We also provide a JSON API that you can use like this: /json?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ

It returns a JSON object generated by youtube-dl. You can find a list of all the properties in the youtube-dl documentation.

FAQ

Please read the FAQ before reporting any issue.

Download Details:

Author: Rudloff
Source Code: https://github.com/Rudloff/alltube 
License: GPL-3.0 license

#php #youtube #vimeo #video 

Alltube: Web GUI for Youtube-dl
Rupert  Beatty

Rupert Beatty

1667095020

A Video Player for IOS, Based on AVPlayer, Support The Horizontal

BMPlayer

A video player for iOS, based on AVPlayer, support the horizontal, vertical screen. support adjust volume, brightness and seek by slide, support subtitles.

中文说明

Features

  •  Support for horizontal and vertical play mode
  •  Support play online URL and local file
  •  Adjust brightness by slide vertical at left side of screen
  •  Adjust volume by slide vertical at right side of screen
  •  Slide horizontal to fast forward and rewind
  •  Support multi-definition video
  •  Custom playrate
  •  Add Http header and other options to AVURLAsset
  •  Easy to customize
  •  Supporting show local and online subtitles
  •  Swift 5

Requirements

  • iOS 10.0+
  • Xcode 10.0+
  • Swift 4+

Supporting the project

You can support the project by checking out our sponsor page. It takes only one click:

Sponsor banner 
This advert was placed by GitAds 

Installation

CocoaPods

Swiftpodfile
Swift 5.0pod 'BMPlayer', '~> 1.3.0'
Swift 4.2pod 'BMPlayer', '~> 1.2.0'
Swift 4.0pod 'BMPlayer', '~> 1.0.0'
Swift 3.0pod 'BMPlayer', '~> 0.9.1'
Swift 2.2pod 'BMPlayer', '~> 0.3.3'

To test the experimental caching support with VIMediaCache, use

pod 'BMPlayer/CacheSupport', :git => 'https://github.com/BrikerMan/BMPlayer.git'

Carthage

Add BMPlayer in your Cartfile.

github "BrikerMan/BMPlayer"

Run carthage to build the framework and drag the built BMPlayer.framework into your Xcode project.

Demo

run pod install at Example folder before run the demo.

Usage (Support IB and code)

Set status bar color

Please add the View controller-based status bar appearance field in info.plist and change it to NO

IB usage

Direct drag IB to UIView, the aspect ratio for the 16:9 constraint (priority to 750, lower than the 1000 line), the code section only needs to achieve. See more detail on the demo.

import BMPlayer

player.playWithURL(URL(string: url)!)

player.backBlock = { [unowned self] (isFullScreen) in
    if isFullScreen == true { return }
    let _ = self.navigationController?.popViewController(animated: true)
}

Code implementation by SnapKit

import BMPlayer

player = BMPlayer()
view.addSubview(player)
player.snp.makeConstraints { (make) in
    make.top.equalTo(self.view).offset(20)
    make.left.right.equalTo(self.view)
    // Note here, the aspect ratio 16:9 priority is lower than 1000 on the line, because the 4S iPhone aspect ratio is not 16:9
    make.height.equalTo(player.snp.width).multipliedBy(9.0/16.0).priority(750)
}
// Back button event
player.backBlock = { [unowned self] (isFullScreen) in
    if isFullScreen == true { return }
    let _ = self.navigationController?.popViewController(animated: true)
}

Set video url

let asset = BMPlayerResource(url: URL(string: "http://baobab.wdjcdn.com/14525705791193.mp4")!,
                             name: "风格互换:原来你我相爱")
player.setVideo(resource: asset)

multi-definition video with cover

let res0 = BMPlayerResourceDefinition(url: URL(string: "http://baobab.wdjcdn.com/1457162012752491010143.mp4")!,
                                      definition: "高清")
let res1 = BMPlayerResourceDefinition(url: URL(string: "http://baobab.wdjcdn.com/1457162012752491010143.mp4")!,
                                      definition: "标清")

let asset = BMPlayerResource(name: "周末号外丨中国第一高楼",
                             definitions: [res0, res1],
                             cover: URL(string: "http://img.wdjimg.com/image/video/447f973848167ee5e44b67c8d4df9839_0_0.jpeg"))

player.setVideo(resource: asset)

Add HTTP header for request

let header = ["User-Agent":"BMPlayer"]
let options = ["AVURLAssetHTTPHeaderFieldsKey":header]

let definition = BMPlayerResourceDefinition(url: URL(string: "http://baobab.wdjcdn.com/1457162012752491010143.mp4")!,
                                            definition: "高清",
                                            options: options)

let asset = BMPlayerResource(name: "Video Name",
                             definitions: [definition])

Listening to player state changes

See more detail from the Example project

Block

//Listen to when the player is playing or stopped
player?.playStateDidChange = { (isPlaying: Bool) in
    print("playStateDidChange \(isPlaying)")
}

//Listen to when the play time changes
player?.playTimeDidChange = { (currentTime: TimeInterval, totalTime: TimeInterval) in
    print("playTimeDidChange currentTime: \(currentTime) totalTime: \(totalTime)")
}

Delegate

protocol BMPlayerDelegate {
    func bmPlayer(player: BMPlayer ,playerStateDidChange state: BMPlayerState) { }
    func bmPlayer(player: BMPlayer ,loadedTimeDidChange loadedDuration: TimeInterval, totalDuration: TimeInterval)  { }
    func bmPlayer(player: BMPlayer ,playTimeDidChange currentTime : TimeInterval, totalTime: TimeInterval)  { }
    func bmPlayer(player: BMPlayer ,playerIsPlaying playing: Bool)  { }
}

Customize player

Needs to change before the player alloc.

// should print log, default false
BMPlayerConf.allowLog = false
// should auto play, default true
BMPlayerConf.shouldAutoPlay = true
// main tint color, default whiteColor
BMPlayerConf.tintColor = UIColor.whiteColor()
// options to show header view (which include the back button, title and definition change button) , default .Always,options: .Always, .HorizantalOnly and .None
BMPlayerConf.topBarShowInCase = .Always
// loader type, see detail:https://github.com/ninjaprox/NVActivityIndicatorView
BMPlayerConf.loaderType  = NVActivityIndicatorType.BallRotateChase
// enable setting the brightness by touch gesture in the player
BMPlayerConf.enableBrightnessGestures = true
// enable setting the volume by touch gesture in the player
BMPlayerConf.enableVolumeGestures = true
// enable setting the playtime by touch gesture in the player
BMPlayerConf.enablePlaytimeGestures = true

Advanced Customize

  • Subclass BMPlayerControlView to create your personal control UI, check the Example.
  • Use the BMPlayerLayer with your own player control view.

Demonstration

gif

Reference:

This project heavily reference the Objective-C version of this project ZFPlayer, thanks for the generous help of ZFPlayer's author.

Contact me:

Contributors

You are welcome to fork and submit pull requests.

Download Details:

Author: BrikerMan
Source Code: https://github.com/BrikerMan/BMPlayer 
License: MIT license

#swift #video #players 

A Video Player for IOS, Based on AVPlayer, Support The Horizontal

Video Library for Flutter

Flutter Video.js player

Flutter plugin for use Video.js in flutter web

Installation

Add it to your package's pubspec.yaml file

dependencies:
  video_js: ^0.1.2

Web

To implement you need to include Video.js library in the index.html of web section

  <link id="videojscss" rel="stylesheet" href="https://unpkg.com/video.js/dist/video-js.css">
    <script src="https://unpkg.com/video.js/dist/video.js"></script>

To support HLS formats you need to add this script too

  <script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>

Example:


<head>
	<base href="$FLUTTER_BASE_HREF">

	<meta charset="UTF-8">
	<meta content="IE=Edge" http-equiv="X-UA-Compatible">
	<meta name="description" content="A new Flutter project.">

	<!-- iOS meta tags & icons -->
	<meta name="apple-mobile-web-app-capable" content="yes">
	<meta name="apple-mobile-web-app-status-bar-style" content="black">
	<meta name="apple-mobile-web-app-title" content="example">
	<link rel="apple-touch-icon" href="icons/Icon-192.png">

	<title>example</title>

	<link rel="manifest" href="manifest.json">
	<link id="videojscss" rel="stylesheet" href="https://unpkg.com/video.js/dist/video-js.css">    <!-- Add this line-->
	<script src="https://unpkg.com/video.js/dist/video.js"></script>                               <!-- Add this line-->
	<script src="https://unpkg.com/videojs-contrib-hls/dist/videojs-contrib-hls.js"></script>      <!-- Add this line-->
</head>

Note See usage example in video_js plugin

Then do this in main method :

void main() {
  // this line need for javascript's call backs
  VideoJsResults().init();
  runApp(MyApp());
}

Example

import 'package:flutter/material.dart';
import 'package:videojs/videojs.dart';

void main(){
  VideoJsResults().init();
  runApp(VideoApp());
}

class VideoApp extends StatefulWidget {
  @override
  _VideoAppState createState() => _VideoAppState();
}

class _VideoAppState extends State<VideoApp> {
  late VideoJsController _videoJsController;

  @override
  void initState() {
    super.initState();
    _videoJsController = VideoJsController("videoId", videoJsOptions: VideoJsOptions(
        controls: true,
        loop: false,
        muted: false,
        poster: 'https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_100kB.jpg',
        aspectRatio: '16:9',
        fluid: false,
        language: 'en',
        liveui: false,
        notSupportedMessage: 'this movie type not supported',
        playbackRates: [1, 2, 3],
        preferFullWindow: false,
        responsive: false,
        sources: [Source("https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", "video/mp4")],
        suppressNotSupportedError: false));
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Video JS Demo',
      home: Scaffold(
        body: Center(
            child: VideoJsWidget(
              videoJsController: _videoJsController,
              height: MediaQuery.of(context).size.width / 2.5,
              width: MediaQuery.of(context).size.width / 1.5,
            )
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            _videoJsController.isPaused((isPlaying) {
              isPlaying != 'true'
                  ? _videoJsController.pause()
                  : _videoJsController.play();
            });
          },
         child: const Icon(Icons.play_arrow,),
        ),
      ),
    );
  }
}

Note: This plugin is still under development, and some APIs might not be available yet. Feedback welcome and Pull Requests are most welcome!

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add video_js

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  video_js: ^0.1.3

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

Import it

Now in your Dart code, you can use:

import 'package:video_js/video_js.dart'; 

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:video_js/video_js.dart';

void main() {
  // this line need for javascript's call backs
  VideoJsResults().init();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const OptionsPage(),
    );
  }
}

class OptionsPage extends StatefulWidget {
  const OptionsPage({Key? key}) : super(key: key);

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

class OptionsPageState extends State<OptionsPage> {
  bool? controls = true;
  bool? loop = false;
  bool? muted = false;
  String? poster =
      'https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_100kB.jpg';
  String? aspectRatio = '16:9';
  bool? fluid = false;
  String? language = 'en';
  bool? liveui = false;
  String? notSupportedMessage = 'this movie type not supported';
  String? source =
      "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
  String? sourceType = "video/mp4";
  List<double>? playbackRates = [1, 2, 3];
  bool? preferFullWindow = false;
  bool? responsive = false;

  // List<Source>? sources = [];
  bool? suppressNotSupportedError = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("videoJs Options"),
      ),
      body: ListView(
        children: [
          CheckboxListTile(
            title: const Text("controls"),
            value: controls,
            onChanged: (val) {
              setState(() {
                controls = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("loop"),
            value: loop,
            onChanged: (val) {
              setState(() {
                loop = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("muted"),
            value: muted,
            onChanged: (val) {
              setState(() {
                muted = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("fluid"),
            value: fluid,
            onChanged: (val) {
              setState(() {
                fluid = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("liveui"),
            value: liveui,
            onChanged: (val) {
              setState(() {
                liveui = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("preferFullWindow"),
            value: preferFullWindow,
            onChanged: (val) {
              setState(() {
                preferFullWindow = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("responsive"),
            value: responsive,
            onChanged: (val) {
              setState(() {
                responsive = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          CheckboxListTile(
            title: const Text("suppressNotSupportedError"),
            value: suppressNotSupportedError,
            onChanged: (val) {
              setState(() {
                suppressNotSupportedError = val;
              });
            },
            controlAffinity:
                ListTileControlAffinity.leading, //  <-- leading Checkbox
          ),
          ListTile(
            leading: const Text("poster"),
            title: TextField(
              controller: TextEditingController(text: poster),
              onChanged: (val) {
                poster = val;
              },
            ),
          ),
          ListTile(
            leading: const Text("aspectRatio"),
            title: TextField(
              controller: TextEditingController(text: aspectRatio),
              onChanged: (val) {
                aspectRatio = val;
              },
            ),
          ),
          ListTile(
            leading: const Text("language"),
            title: TextField(
              controller: TextEditingController(text: language),
              onChanged: (val) {
                language = val;
              },
            ),
          ),
          ListTile(
            leading: const Text("notSupportedMessage"),
            title: TextField(
              controller: TextEditingController(text: notSupportedMessage),
              onChanged: (val) {
                notSupportedMessage = val;
              },
            ),
          ),
          ListTile(
            leading: const Text("source"),
            title: TextField(
              controller: TextEditingController(text: source),
              onChanged: (val) {
                source = val;
              },
            ),
          ),
          ListTile(
            leading: const Text("source type"),
            title: TextField(
              controller: TextEditingController(text: sourceType),
              onChanged: (val) {
                sourceType = val;
              },
            ),
          ),
          ListTile(
            leading: const Text("playbackRates"),
            title: TextField(
              controller: TextEditingController(text: '1,2,3'),
              onChanged: (val) {
                playbackRates!.clear();
                val.split(',').forEach((element) {
                  if (element != '')
                    playbackRates!.add(int.parse(element).toDouble());
                });
              },
            ),
          ),
          const SizedBox(
            height: 30,
          ),
          ElevatedButton(
              onPressed: () {
                Navigator.push(
                  context,
                  MaterialPageRoute(
                      builder: (context) => MyHomePage(
                            videoJsOptions: VideoJsOptions(
                                controls: controls,
                                loop: loop,
                                muted: muted,
                                poster: poster,
                                aspectRatio: aspectRatio,
                                fluid: fluid,
                                language: language,
                                liveui: liveui,
                                notSupportedMessage: notSupportedMessage,
                                playbackRates: playbackRates,
                                preferFullWindow: preferFullWindow,
                                responsive: responsive,
                                sources: [
                                  Source(
                                      "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
                                      "video/mp4")
                                ],
                                suppressNotSupportedError:
                                    suppressNotSupportedError),
                          )),
                );
              },
              child: const Text(
                "Navigate to video page",
                style: TextStyle(color: Colors.white),
              )),
          const SizedBox(
            height: 30,
          ),
        ],
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final VideoJsOptions videoJsOptions;

  const MyHomePage({Key? key, required this.videoJsOptions}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String playerId = "videoId";
  TextEditingController videoSourceController = TextEditingController();
  TextEditingController videoTypeController = TextEditingController();
  late VideoJsController videoJsController;

  @override
  void initState() {
    super.initState();
    videoSourceController.text =
        "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
    videoTypeController.text = "video/mp4";
    videoJsController =
        VideoJsController("videoId", videoJsOptions: widget.videoJsOptions);
  }

  @override
  void dispose() {
    super.dispose();
    // videoJsController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          // Here we take the value from the MyHomePage object that was created by
          // the App.build method, and use it to set our appbar title.
          title: const Text("videojs example"),
        ),
        body: SingleChildScrollView(
            child: Padding(
          padding: const EdgeInsets.all(15),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  VideoJsWidget(
                    videoJsController: videoJsController,
                    height: MediaQuery.of(context).size.width / 2.5,
                    width: MediaQuery.of(context).size.width / 1.5,
                  )
                ],
              ),
              const SizedBox(
                height: 100,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  SizedBox(
                    width: 200,
                    height: 50,
                    child: TextField(
                      controller: videoSourceController,
                      decoration: const InputDecoration(hintText: "video"),
                    ),
                  ),
                  const SizedBox(
                    width: 10,
                  ),
                  SizedBox(
                    width: 100,
                    height: 50,
                    child: TextField(
                      controller: videoTypeController,
                      decoration: const InputDecoration(hintText: "type"),
                    ),
                  ),
                  const SizedBox(
                    width: 15,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        //[type] can be video/mp4, video/webm, application/x-mpegURL (for hls videos) , ...
                        videoJsController.setSRC(
                            videoSourceController.text.toString(),
                            type: videoTypeController.text.toString());
                      },
                      child: const Text(
                        "set source",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 50,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.play();
                      },
                      child: const Text(
                        "play",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.pause();
                      },
                      child: const Text(
                        "pause",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.isPaused((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Pause status : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "is pause",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.currentTime((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Current video time : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Current video time",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.getVolume((val) =>
                            ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                                duration: const Duration(milliseconds: 500),
                                content: Text(
                                  "volume is : $val",
                                  style: const TextStyle(color: Colors.white),
                                ))));
                      },
                      child: const Text(
                        "Get volume",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.setVolume("0.5");
                      },
                      child: const Text(
                        "Set volume to 0.5",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.toggleMute();
                      },
                      child: const Text(
                        "Toggle mute",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.isMute((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Mute status : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Mute status",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.toggleFullScreen();
                      },
                      child: const Text(
                        "Toggle Full Screen",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.isFullScreen((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Full screen status : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Full screen status",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.requestFullScreen();
                      },
                      child: const Text(
                        "request full screen",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.exitFullScreen();
                      },
                      child: const Text(
                        "exite Full Screen",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.setCurrentTime("100");
                      },
                      child: const Text(
                        "Set video time to 100 sec",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.durationTime((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Duration time : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Duration",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.remainTime((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Remain time : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Remain time",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.bufferPercent((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Buffer percent : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Buffer percent",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 20,
              ),
              Row(
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.setPoster(
                            "https://file-examples-com.github.io/uploads/2017/10/file_example_JPG_100kB.jpg");
                      },
                      child: const Text(
                        "Set video poster",
                        style: TextStyle(color: Colors.white),
                      )),
                  const SizedBox(
                    width: 5,
                  ),
                  ElevatedButton(
                      onPressed: () {
                        videoJsController.getPoster((val) {
                          ScaffoldMessenger.of(context).showSnackBar(SnackBar(
                              duration: const Duration(milliseconds: 500),
                              content: Text(
                                "Video poster : $val",
                                style: const TextStyle(color: Colors.white),
                              )));
                        });
                      },
                      child: const Text(
                        "Get video poster",
                        style: TextStyle(color: Colors.white),
                      )),
                ],
              ),
              const SizedBox(
                height: 50,
              ),
            ],
          ),
        )));
  }
} 

Download Details:

Author: mojtabaghiasi

Source Code: https://github.com/mojtabaghiasi/video_js/issues

#flutter #video 

Video Library for Flutter
Rupert  Beatty

Rupert Beatty

1665949860

Gifski: Convert Videos to High-quality GIFs on Your Mac

Gifski

Convert videos to high-quality GIFs on your Mac

This is a macOS app for the gifski encoder, which converts videos to GIF animations using pngquant's fancy features for efficient cross-frame palettes and temporal dithering. It produces animated GIFs that use thousands of colors per frame and up to 50 FPS (useful for showing off design work on Dribbble).

You can also produce smaller lower quality GIFs when needed with the “Quality” slider, thanks to gifsicle.

Gifski supports all the video formats that macOS supports (.mp4 or .mov with H264, HEVC, ProRes, etc). The QuickTime Animation format is not supported. Use ProRes 4444 XQ instead. It's more efficient, more widely supported, and like QuickTime Animation, it also supports alpha channel.

Gifski has a bunch of settings like changing dimensions, speed, frame rate, quality, looping, and more.

Download

Requires macOS 11 or later.

Older versions

Features

Share extension

Gifski includes a share extension that lets you share videos to Gifski. Just select Gifski from the Share menu of any macOS app.

Tip: You can share a macOS screen recording with Gifski by clicking on the thumbnail that pops up once you are done recording and selecting “Share” from there.

System service

Gifski includes a system service that lets you quickly convert a video to GIF from the Services menu in any app that provides a compatible video file.

Bounce (yo-yo) GIF playback

Gifski includes the option to create GIFs that bounce back and forth between forward and backward playback. This is a similar effect to the bounce effect in iOS's Live Photo effects. This option doubles the number of frames in the GIF so the file size will double as well.

Tips

Quickly copy or save the GIF

After converting, press Command+C to copy the GIF or Command+S to save it.

Change GIF dimensions with the keyboard

59964494-b8519f00-952b-11e9-8d16-47c8bc103a61.gif

In the width/height input fields in the editor view, press the arrow up/down keys to change the value by 1. Hold the Option key meanwhile to change it by 10.

Screenshots

Building from source

To build the app in Xcode, you need to have Rust installed first:

curl https://sh.rustup.rs -sSf | sh brew install SwiftLint xcode-select --install

FAQ

The generated GIFs are huge!

The GIF image format is very space inefficient. It works best with short video clips. Try reducing the dimensions, FPS, or quality.

Why are 60 FPS and higher not supported?

Browsers throttle frame rates above 50 FPS, playing them at 10 FPS. Read more.

How can I convert a sequence of PNG images to a GIF?

Install FFmpeg (with Homebrew: brew install ffmpeg) and then run this command:

TMPFILE="$(mktemp /tmp/XXXXXXXXXXX).mov"; \
	ffmpeg -f image2 -framerate 30 -i image_%06d.png -c:v prores_ks -profile:v 5 "$TMPFILE" \
	&& open -a Gifski "$TMPFILE"

Ensure the images are named in the format image_000001.png and adjust the -framerate accordingly.

Command explanation.

How can I run multiple conversions at the same time?

This is unfortunately not supported. However, if you know how to run a terminal command, you could run open -na Gifski multiple times to open multiple instances of Gifski, where each instance can convert a separate video. You should not have the editor view open in multiple instances though, as changing the quality, for example, will change it in all the instances.

Can I contribute localizations?

We don't have any immediate plans to localize the app.

Can you support Windows/Linux?

No, but there's a cross-platform command-line tool available.

Press

Built with

Maintainers

Former

Related

Download Details:

Author: Sindresorhus
Source Code: https://github.com/sindresorhus/Gifski 
License: MIT license

#swift #macos #convert #video 

Gifski: Convert Videos to High-quality GIFs on Your Mac

Simple 360 Video Player Plugin (android, IOS Support)

video_360

Simple 360 video player plugin (Android, iOS support)

Getting Started

The Android uses the open source Google ExoPlayer

Google ExoPlayer Version: 2.12.1

The iOS users the open source Swifty360Player

Swifty360Player Version: 0.2.5

Installation

Add pubspec.yaml dependencies.

dependencies:
  video_360: ^0.0.3

Android Requirements

Minimum SDK Target : 16

iOS Requirements

Minimum iOS Target : 11.0
Swift Version : 5.x

How to use

importing the libray:

import 'package:video_360/video_360.dart';

Add Video360View:

Video360View(
    onVideo360ViewCreated: _onVideo360ViewCreated,
    url: YOUR_360_VIDEO_URL,
    isAutoPlay: true,   // defalut : true
    isRepeat: true, // defalut : true
    onPlayInfo: (Video360PlayInfo info) {
        // Play info Callback      
    },
)

Video360Controller Method

play() : video play
stop() : video stop
reset() : video reset
jumpTo() : video jump, parameter is milesecond
seekTo() : video seek, parameter is plus, minus milesecond

sample code:

import 'package:flutter/material.dart';
import 'package:video_360/video_360.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  late Video360Controller controller;
  
  String durationText = '';
  String totalText = '';

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

  @override
  Widget build(BuildContext context) {

    var statusBar = MediaQuery.of(context).padding.top;

    var width = MediaQuery.of(context).size.width;
    var height = MediaQuery.of(context).size.height;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Video 360 Plugin example app'),
      ),
      body: Stack(
        children: [
          Center(
            child: Container(
              width: width,
              height: height,
              child: Video360View(
                onVideo360ViewCreated: _onVideo360ViewCreated,
                url: 'https://multiplatform-f.akamaihd.net/i/multi/will/bunny/big_buck_bunny_,640x360_400,640x360_700,640x360_1000,950x540_1500,.f4v.csmil/master.m3u8',
                onPlayInfo: (Video360PlayInfo info) {
                  setState(() {
                    durationText = info.duration.toString();
                    totalText = info.total.toString();
                  });
                },
              ),
            ),
          ),
          Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  MaterialButton(
                    onPressed: () {
                      controller.play();
                    },
                    color: Colors.grey[100],
                    child: Text('Play'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller.stop();
                    },
                    color: Colors.grey[100],
                    child: Text('Stop'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller.reset();
                    },
                    color: Colors.grey[100],
                    child: Text('Reset'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller.jumpTo(80000);
                    },
                    color: Colors.grey[100],
                    child: Text('1:20'),
                  ),
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  MaterialButton(
                    onPressed: () {
                      controller.seekTo(-2000);
                    },
                    color: Colors.grey[100],
                    child: Text('<<'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller.seekTo(2000);
                    },
                    color: Colors.grey[100],
                    child: Text('>>'),
                  ),
                  Flexible(
                    child: MaterialButton(
                      onPressed: () {
                      },
                      color: Colors.grey[100],
                      child: Text(durationText + ' / ' + totalText),
                    ),
                  ),
                ],
              )
            ],
          )
        ],
      ),
    );
  }

  _onVideo360ViewCreated(Video360Controller controller) {
    this.controller = controller;
  }
}

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add video_360

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  video_360: ^0.0.8

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

Import it

Now in your Dart code, you can use:

import 'package:video_360/video_360.dart'; 

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:video_360/video_360.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MaterialApp(home: MyApp()));
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Video360Controller? controller;

  String durationText = '';
  String totalText = '';

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

  @override
  Widget build(BuildContext context) {
    var statusBar = MediaQuery.of(context).padding.top;

    var width = MediaQuery.of(context).size.width;
    var height = MediaQuery.of(context).size.height;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Video 360 Plugin example app'),
      ),
      body: Stack(
        children: [
          Center(
            child: Container(
              width: width,
              height: height,
              child: Video360View(
                onVideo360ViewCreated: _onVideo360ViewCreated,
                url:
                    'https://bitmovin-a.akamaihd.net/content/playhouse-vr/m3u8s/105560.m3u8',
                onPlayInfo: (Video360PlayInfo info) {
                  setState(() {
                    durationText = info.duration.toString();
                    totalText = info.total.toString();
                  });
                },
              ),
            ),
          ),
          Column(
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  MaterialButton(
                    onPressed: () {
                      controller?.play();
                    },
                    color: Colors.grey[100],
                    child: Text('Play'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller?.stop();
                    },
                    color: Colors.grey[100],
                    child: Text('Stop'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller?.reset();
                    },
                    color: Colors.grey[100],
                    child: Text('Reset'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller?.jumpTo(80000);
                    },
                    color: Colors.grey[100],
                    child: Text('1:20'),
                  ),
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  MaterialButton(
                    onPressed: () {
                      controller?.seekTo(-2000);
                    },
                    color: Colors.grey[100],
                    child: Text('<<'),
                  ),
                  MaterialButton(
                    onPressed: () {
                      controller?.seekTo(2000);
                    },
                    color: Colors.grey[100],
                    child: Text('>>'),
                  ),
                  Flexible(
                    child: MaterialButton(
                      onPressed: () {},
                      color: Colors.grey[100],
                      child: Text(durationText + ' / ' + totalText),
                    ),
                  ),
                ],
              )
            ],
          )
        ],
      ),
    );
  }

  _onVideo360ViewCreated(Video360Controller? controller) {
    this.controller = controller;
  }
} 

Download Details:

Author: kilroy80

Source Code: https://github.com/kilroy80/flutter_video_360

#flutter #video #ios 

Simple 360 Video Player Plugin (android, IOS Support)
Rupert  Beatty

Rupert Beatty

1665873900

WWDC: The Unofficial WWDC App for MacOS

The unofficial WWDC app for macOS

Enjoy WWDC from the comfort of your Mac with the unofficial WWDC app for macOS. Whether you're (virtually) attending or not, you can access livestreams, videos and sessions during the conference and as a year-round resource.

In partnership with CocoaHub, you can also use the app's Community tab to browse through Apple announcements, updates to the Swift language, new episodes from your favorite podcasts, community blog posts, and more.

You may also search for your WWDC content in Raycast if you have both apps installed, just enable “Allow other apps access to your WWDC content” in preferences.

⬇️ If you just want to download the latest release, go to the website.

Schedule

The schedule tab shows the schedule for the current edition of WWDC, and allows you to watch live streams for the Keynote and other sessions throughout the week.

Videos

Watch this year's videos as they're released and access videos from previous years. You can also read transcripts of sessions and easily jump to a specific point in the relevant video. Transcripts are also searchable and available in multiple languages.

videos

Video features

  • Watch videos in 0.5x, 1x, 1.25x, 1.5x, 1.75x or 2x speeds
  • Fullscreen and native picture-in-picture support
  • Navigate video contents easily with the help of transcripts

Clip Sharing

Clip Sharing allows you to share a short segment (up to 5 minutes) from a session's video. This is a great feature for quickly sharing snippets of content from the conference.

clipsharing

Chromecast

You can watch WWDC videos (both live and on-demand) on your Chromecast. Just click the Chromecast button while playing a video, choose your device from the list and control playback using the Google Home app on your phone.

chromecast

Bookmarks

Have you ever found yourself watching a WWDC session and wishing you could take notes at a specific point in the video to refer back to later on? This is now possible with bookmarks.

With bookmarks, you can create a reference point within a video and add an annotation to it. Your bookmark annotations can also be considered while using the search, so it's easier than ever to find the content you've bookmarked before.

bookmarks

Community

Browse content curated by the CocoaHub team in the Community tab.

community

iCloud Sync

Enable the iCloud sync in preferences and your favorites, bookmarks and progress in sessions will be synced across your Macs.

Sharing

You can easily share links to sessions or videos by using the share button. The links shared are universal links that redirect to Apple's developer website, so if they're opened on a Mac which has the app installed, they will open in the app. The links are also compatible with iOS devices using the Apple Developer app.

Nerdy bits 🤓

Code of Conduct

We expect all of our contributors to help uphold the values set out in our code of conduct. We fundamentally believe this will help us build a better community, and with it a better app.

Contributing

Please read the contribution guidelines before opening an issue or pull request.

External libraries

A number of third-party libraries are used by the app:

Internal libraries

  • ConfCore is the core of the app that deals with Apple's WWDC API, data storage, caching, syncing and transcripts (everything that has to do with data, basically)
  • ConfUIFoundation contains shared color, font definitions and other useful extensions used by the main app target and PlayerUI
  • PlayerUI contains the UI components for the video player and some general-purpose UI components used throughout the app
  • ThrowBack provides support for migration of user data and preferences from old versions of the app

Building the app

Building requires Xcode 12.5 or later.

Clone this branch and before opening the project, run ./bootstrap.sh to setup the environment. It will install swiftlint for you using brew if you don't have it yet.

Since the app uses CloudKit, when you build it yourself, all of the CloudKit-dependant functionality will not be available. CloudKit requires a provisioning profile and a paid developer account.

To build the app yourself without the need for a developer account and a CloudKit container, always use the WWDC target when building. The WWDC with iCloud target requires a paid developer account and a CloudKit container, which you won't be able to create because of the app's bundle identifier.

schedule

Clearing app data during development

If you need to clear the app's preferences and stored data during development, you can run ./cleardata.sh in the project folder. This will delete all of your preferences and data like favorites, bookmarks and progress in videos, so be careful.

Powered by MacStadium

Download Details:

Author: insidegui
Source Code: https://github.com/insidegui/WWDC 
License: BSD-2-Clause license

#swift #macos #apple #video 

WWDC: The Unofficial WWDC App for MacOS
Rupert  Beatty

Rupert Beatty

1665716760

IINA: The Modern Video Player for MacOS

IINA

IINA is the modern video player for macOS.


Features

  • Based on mpv, which provides the best decoding capacity on macOS
  • Designed with modern versions of macOS (10.11+) in mind
  • All the features you need for video and music: subtitles, playlists, chapters…and much, much more!
  • Force Touch, picture-in-picture and advanced Touch Bar support
  • Customizable user interface including multiple color schemes and on screen controller (OSC) layout positioning
  • Standalone Music Mode designed for audio files
  • Video thumbnails
  • Online subtitle searching and intelligent local subtitle matching
  • Unlimited playback history
  • Convenient and interactive settings for video/audio filters
  • Fully customizable keyboard, mouse, trackpad, and gesture controls
  • mpv configuration files and script system for advanced users
  • Command line tool and browser extensions provided
  • In active development

Building

IINA uses mpv for media playback. To build IINA, you can either fetch copies of these libraries we have already built (using the instructions below) or build them yourself by skipping to these instructions.

Using the pre-compiled libraries

Download pre-compiled libraries by running

./other/download_libs.sh
  • Tips:
    • Change URL in the shell script if you want to download arch-specific binaries. By default, it will download the universal ones. You can download other binaries from https://iina.io/dylibs/${ARCH}/fileList.txt where ARCH can be universal, arm64 and x86_64.
    • If you want to build an older IINA version, make sure to download the correponding dylibs. For example, https://iina.io/dylibs/1.2.0/universal/fileList.txt.

Open iina.xcodeproj in the latest public version of Xcode. IINA may not build if you use any other version.

Build the project.

Building mpv manually

Build your own copy of mpv. If you're using a package manager to manage dependencies, the steps below outline the process.

With Homebrew

Use our tap as it passes in the correct flags to mpv's configure script:

$ brew tap iina/homebrew-mpv-iina
$ brew install mpv-iina

With MacPorts

Pass in these flags when installing:

# port install mpv +uchardet -bundle -rubberband configure.args="--enable-libmpv-shared --enable-lua --enable-libarchive --enable-libbluray --disable-swift --disable-rubberband"

Copy the correponding mpv and FFmpeg header files into deps/include/, replacing the current ones. You can find them on GitHub (e.g. mpv), but it's recommended to copy them from the Homebrew or MacPorts installation. Always make sure the header files have the same version of the dylibs.

Run other/parse_doc.rb. This script will fetch the latest mpv documentation and generate MPVOption.swift, MPVCommand.swift and MPVProperty.swift. Copy them from other/ to iina/, replacing the current files. This is only needed when updating libmpv. Note that if the API changes, the player source code may also need to be changed.

Run other/change_lib_dependencies.rb. This script will deploy the dependent libraries into deps/lib. If you're using a package manager to manage dependencies, invoke it like so:

With Homebrew

$ other/change_lib_dependencies.rb "$(brew --prefix)" "$(brew --prefix mpv-iina)/lib/libmpv.dylib"

With MacPorts

$ port contents mpv | grep '\.dylib$' | xargs other/change_lib_dependencies.rb /opt/local

Open iina.xcodeproj in the latest public version of Xcode. IINA may not build if you use any other version.

Remove all of references to .dylib files from the Frameworks group in the sidebar and add all the .dylib files in deps/lib to that group by clicking "Add Files to iina..." in the context menu.

Add all the imported .dylib files into the "Copy Dylibs" phase under "Build Phases" tab of the iina target.

Make sure the necessary .dylib files present in the "Link Binary With Libraries" phase under "Build Phases". Xcode should already added all dylibs under this section.

Build the project.

Contributing

IINA is always looking for contributions, whether it's through bug reports, code, or new translations.

If you find a bug in IINA, or would like to suggest a new feature or enhancement, it'd be nice if you could search your problem first; while we don't mind duplicates, keeping issues unique helps us save time and consolidates effort. If you can't find your issue, feel free to file a new one.

If you're looking to contribute code, please read CONTRIBUTING.md–it has information on IINA's process for handling contributions, and tips on how the code is structured to make your work easier.

If you'd like to translate IINA to your language, please visit IINA's instance of Crowdin. You can create an account for free and start translating and/or approving. Please do not send pull request to this repo directly, Crowdin will automatically sync new translations with our repo. If you want to translate IINA into a new language that is currently not in the list, feel free to open an issue.

Download Details:

Author: iina
Source Code: https://github.com/iina/iina 
License: GPL-3.0 license

#swift #macos #video #hacktoberfest 

IINA: The Modern Video Player for MacOS

VideoIO.jl: Reading and Writing Of Video Files in Julia Via FFmpeg

VideoIO.jl

Reading and writing of video files in Julia.

Functionality based on a dedicated build of ffmpeg via FFMPEG.jl and the JuliaPackaging/Yggdrasil cross-compiler.

Installation

The package can be installed with the Julia package manager. From the Julia REPL, type ] to enter the Pkg REPL mode and run:

pkg> add VideoIO

Or, equivalently, via the Pkg API:

julia> import Pkg; Pkg.add("VideoIO")

Documentation

  • documentation of the most recently tagged version.
  • documentation of the in-development version.

Project Status

The package is tested against, and being developed for, Julia v1 on Linux, macOS, and Windows, for x86, x86_64, armv7 and armv8 (aarch64).

Questions and Contributions

Usage questions can be posted on the Julia Discourse forum under the videoio tag, and/or in the #video channel of the Julia Slack.

Contributions are very welcome, as are feature requests and suggestions. Please open an issue if you encounter any problems.


Docs Join the julia slack


Download Details:

Author: JuliaIO
Source Code: https://github.com/JuliaIO/VideoIO.jl 
License: View license

#julia #video 

VideoIO.jl: Reading and Writing Of Video Files in Julia Via FFmpeg

A Custom Video Player in Flutter

Custom Video Player

This package wraps the official video_player package by flutter and extends it with a fully customisable control bar and a fullscreen mode. For the control bar you can decide for each element if you want to show it and if so how it should look.


 

Top Features

  • Fullscreen Mode
  • Fully Customizable Controls
  • Fluid Progress Bar
  • Prevent Seeking in Progress Bar


 

Preview

Fullscreen

 
 

Getting started

  • To get started just create a VideoPlayerController as you would for the normal video_player and define a source to use.
  • Secondly create a CustomVideoPlayerController to access the fullscreen and control bar visibility switches by yourself. Pass the controllers to a CustomVideoPlayer widget to use all its functionality and customisation oppurtunities.
  • On the examples tab you can see all parameters you can customize to your needs.


 

Usage

class _MyHomePageState extends State<MyHomePage> {
  late VideoPlayerController videoPlayerController;
  CustomVideoPlayerController customVideoPlayerController =
      CustomVideoPlayerController();

  String videoUrl =
      "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";

  @override
  void initState() {
    super.initState();
    videoPlayerController = VideoPlayerController.network(videoUrl)
      ..initialize();
  }

  @override
  void dispose() {
    videoPlayerController.dispose();
    customVideoPlayerController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text(widget.title),
      ),
      child: SafeArea(
        child: CustomVideoPlayer(
          customVideoPlayerController: customVideoPlayerController,
          videoPlayerController: videoPlayerController,
        ),
      ),
    );
  }
}

Made with ❤ by Flutter team at Appinio GmbH

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add appinio_video_player

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  appinio_video_player: ^1.2.0

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

Import it

Now in your Dart code, you can use:

import 'package:appinio_video_player/appinio_video_player.dart'; 

example/lib/main.dart

import 'package:appinio_video_player/appinio_video_player.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const CupertinoApp(
      debugShowCheckedModeBanner: false,
      theme: CupertinoThemeData(
        brightness: Brightness.light,
      ),
      title: 'Appinio Video Player Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late VideoPlayerController _videoPlayerController,
      _videoPlayerController2,
      _videoPlayerController3;

  late CustomVideoPlayerController _customVideoPlayerController;
  late CustomVideoPlayerWebController _customVideoPlayerWebController;

  final CustomVideoPlayerSettings _customVideoPlayerSettings =
      const CustomVideoPlayerSettings();

  final CustomVideoPlayerWebSettings _customVideoPlayerWebSettings =
      CustomVideoPlayerWebSettings(
    src: longVideo,
  );

  @override
  void initState() {
    super.initState();

    _videoPlayerController = VideoPlayerController.network(
      longVideo,
    )..initialize().then((value) => setState(() {}));
    _videoPlayerController2 = VideoPlayerController.network(video240);
    _videoPlayerController3 = VideoPlayerController.network(video480);
    _customVideoPlayerController = CustomVideoPlayerController(
      context: context,
      videoPlayerController: _videoPlayerController,
      customVideoPlayerSettings: _customVideoPlayerSettings,
      additionalVideoSources: {
        "240p": _videoPlayerController2,
        "480p": _videoPlayerController3,
        "720p": _videoPlayerController,
      },
    );

    _customVideoPlayerWebController = CustomVideoPlayerWebController(
      webVideoPlayerSettings: _customVideoPlayerWebSettings,
    );
  }

  @override
  void dispose() {
    _customVideoPlayerController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text("Appinio Video Player"),
      ),
      child: SafeArea(
        child: ListView(
          children: [
            kIsWeb
                ? Expanded(
                    child: CustomVideoPlayerWeb(
                      customVideoPlayerWebController:
                          _customVideoPlayerWebController,
                    ),
                  )
                : CustomVideoPlayer(
                    customVideoPlayerController: _customVideoPlayerController,
                  ),
            CupertinoButton(
              child: const Text("Play Fullscreen"),
              onPressed: () {
                if (kIsWeb) {
                  _customVideoPlayerWebController.setFullscreen(true);
                  _customVideoPlayerWebController.play();
                } else {
                  _customVideoPlayerController.setFullscreen(true);
                  _customVideoPlayerController.videoPlayerController.play();
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

String videoUrlLandscape =
    "https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4";
String videoUrlPortrait =
    'https://assets.mixkit.co/videos/preview/mixkit-a-girl-blowing-a-bubble-gum-at-an-amusement-park-1226-large.mp4';
String longVideo =
    "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";

String video720 =
    "https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_10mb.mp4";

String video480 =
    "https://www.sample-videos.com/video123/mp4/480/big_buck_bunny_480p_10mb.mp4";

String video240 =
    "https://www.sample-videos.com/video123/mp4/240/big_buck_bunny_240p_10mb.mp4"; 

Download Details:

Author: appinioGmbH

Source Code: https://github.com/appinioGmbH/flutter_packages/tree/main/packages/appinio_video_player

#flutter #video 

A Custom Video Player in Flutter
Lawrence  Lesch

Lawrence Lesch

1664415360

CCapture.js: A Library to Capture Canvas-based animations

CCapture.js - A library to capture canvas-based animations

CCapture.js is a library to help capturing animations created with HTML5 canvas at a fixed framerate.

An example is probably worth a lot of words: CCapture.js with Game of Life 3D.

Sample

What is CCapture.js and why would I need it?

Let's say that you finally have your amazing canvas-based animation running in your browser, be it 2D or 3D with the power of WebGL. You've been working hard to keep it fast and smooth. If you're using requestAnimationFrame you're aiming for a framerate of 60fps or, in other words, each frame is taking 16ms or less to render.

Now you want to record a video of it. Not a big deal, you can fire up a screen capture software that churns out a video file and be done with it. But what if you wanted to create an HD movie of your animation, and it simply cannot be rendered at higher resolutions because frames start dropping? What if you wanted to put all the quality settings up for the video? What if you wanted to push that particle count to 10 millions?

What if, indeed. What would happen is that you'd get a choppy video at best. At higher resolutions, fillrate is a bottleneck for most canvas-based animations. High quality settings or high number of elements may be only feasible on more powerful hardware.

With CCapture.js you can record smooth videos at a fixed framerate for all these situations, because it doesn't run in realtime: it makes the animations run at a given, fixed framerate which can be specified. You can record animations at smooth and consistent 30 or 60fps even if each frame takes seconds to render. You can even take a 240fps capture and create motion blur with post-production software.

The only requirement is that you step your values per frame according to elapsed time. In other words, don't increment your variables with a fixed value each frame, but use an elapsed time delta to adjust those increments. CCapture.js works by hooking the common methods for obtaining that elapsed time: Date.now(), setTimeout, requestAnimationFrame, etc. and making them behave like a constant time step is happening, fixed by the specified framerate.

Methods supported so far:

  • Date.now, Date.prototype.getTime
  • setTimeout, clearTimeout, setInterval (clearInterval pending)
  • requestAnimationFrame
  • performance.now
  • HTMLVideoElement.prototype.currentTime, HTMLAudioElement.prototype.currentTime

CCapture.js is more or less ryg's kkapture but for JavaScript and canvas.

The library supports multiple export formats using modular encoders (`CCFrameEncoder):

  • CCWebMEncoder uses WebM Writer for JavaScript to create a WebM movie
  • CCPNGEncoder and CCJPEGEncoder export PNG and JPEG files in a TAR file, respectively
  • CCGIFEncoder uses gifjs to create animated GIFs
  • CCFFMpegServerEncoder uses ffmpegserver.js to generate video on the server

Forks, pull requests and code critiques are welcome!

Using the code

Include CCapture[.min].js and WebM Writer or gifjs.

<script src="CCapture.min.js"></script>
<!-- Include WebM Writer if you want to export WebM -->
<script src="webm-writer-0.2.0.js"></script>
<!-- Include gifjs if you want to export GIF -->
<script src="gif.js"></script>
<!-- Include tar.js if you want to export PNG or JPEG -->
<script src="tar.js"></script>
<!-- Include download.js for easier file download -->
<script src="download.js"></script>

Or include the whole pack

<script src="CCapture.all.min.js"></script>

Or use npm or bower to install the package:

npm install ccapture.js

Or use bower to install the package:

bower install ccapture.js

To create a CCapture object, write:

// Create a capturer that exports a WebM video
var capturer = new CCapture( { format: 'webm' } );

// Create a capturer that exports an animated GIF
// Notices you have to specify the path to the gif.worker.js 
var capturer = new CCapture( { format: 'gif', workersPath: 'js/' } );

// Create a capturer that exports PNG images in a TAR file
var capturer = new CCapture( { format: 'png' } );

// Create a capturer that exports JPEG images in a TAR file
var capturer = new CCapture( { format: 'jpg' } );

This creates a CCapture object to run at 60fps, non-verbose. You can tweak the object by setting parameters on the constructor:

var capturer = new CCapture( {
    framerate: 60,
    verbose: true
} );

The complete list of parameters is:

  • framerate: target framerate for the capture
  • motionBlurFrames: supersampling of frames to create a motion-blurred frame (0 or 1 make no effect)
  • format: webm/gif/png/jpg/ffmpegserver
  • quality: quality for webm/jpg
  • name: name of the files to be exported. if no name is provided, a GUID will be generated
  • verbose: dumps info on the console
  • display: adds a widget with capturing info (WIP)
  • timeLimit: automatically stops and downloads when reaching that time (seconds). Very convenient for long captures: set it and forget it (remember autoSaveTime!)
  • autoSaveTime: it will automatically download the captured data every n seconds (only available for webm/png/jpg)
  • startTime: skip to that mark (seconds)
  • workersPath: path to the gif worker script

You can decide when to start the capturer. When you call the .start() method, the hooks are set, so from that point on setTimeout, setInterval and other methods that are hooked will behave a bit differently. When you have everything ready to start capturing, and your animation loop is running, call:

capturer.start();

requestAnimationFrame, setTimeout, etc. won't work as expected after capture is started. Make sure your animation loop is running

And then, in your render() method, after the frame is been drawn, call .capture() passing the canvas you want to capture.

function render(){
    requestAnimationFrame(render);
    // rendering stuff ...
    capturer.capture( canvas );
}

render()

That's all. Once you're done with the animation, you can call .stop() and then .save(). That will compose the video and return a URL that can be previewed or downloaded.

capturer.stop();

// default save, will download automatically a file called {name}.extension (webm/gif/tar)
capturer.save();

// custom save, will get a blob in the callback
capturer.save( function( blob ) { /* ... */ } );

Note: you don't need to .stop() in order to .save(). Call capturer.save() anytime you want to get a download up to that moment.

Limitations

CCapture.js only works on browsers that have a `canvas implementation.

WebM Writer current version only works on a browser that supports the image/webp format. Exporting video is basically Chrome-only for now :( If you want to help to make it Firefox, Opera or even Internet Explorer compatible, please do!

gif.js has some performance limitations, be careful if capturing a lot of frames.

The autoSaveTime parameter

Different browsers have different issues with big files: most break for big Uint8Array allocations, or when a file to downloads is larger than 1GB, etc. I haven't been able to find a solid solution for all, so I introduced the autoSaveTime parameter, just to prevent loss of large files. If used with a webm/png/jpg capturer, it will automatically compile, download and free the captured frames every n seconds specified in the parameter. The downloaded file will have the structure {name}-part-00000n and the extension (.webm or .tar). The files inside the TAR file will have the right number of sequence.

Use an autoSaveTime value that give you a file that is small enough to not trip the browser, but large enough to not generate a thousand part files. A value between 10 and 30 seconds for a 4K capture I've found works best: just make sure the file is under 1GB. For most regular, viewport-sized or even Full-HD captures it shouldn't be an issue, but keep in mind this issue.

Memory allocation and garbage collection

There's some issues in which memory -mostly from accumulated frames- will not be freed, depending on the platform and the mood of the browser. If you run into non-sawtooth like memory profiles, and are running chrome, try running it with --js-flags="--expose-gc". This way CCapture will run gc() every frame and memory consumption should stay stable.

Gallery

cru·ci·form 4K CCapture obsidian by xplsv 4K CCapture dataworld by xplsv 4K CCapture

Credits

Contributors

Big thanks to hugohil and Greggman!

Download Details:

Author: Spite
Source Code: https://github.com/spite/ccapture.js/ 
License: MIT license

#javascript  #png #video #canvas 

CCapture.js: A Library to Capture Canvas-based animations

10 Popular Golang Libraries for Manipulating Video

In today's post we will learn about 10 Popular Golang Libraries for Manipulating Video.

What is Manipulating Video?

Video manipulation is a type of media manipulation that targets digital video using video processing and video editing techniques. The applications of these methods range from educational videos to videos aimed at (mass) manipulation and propaganda, a straightforward extension of the long-standing possibilities of photo manipulation. This form of computer-generated misinformation has contributed to fake news, and there have been instances when this technology was used during political campaigns. Other uses are less sinister; entertainment purposes and harmless pranks provide users with movie-quality artistic possibilities.

Table of contents:

  • Gmf - Go bindings for FFmpeg av* libraries.
  • Go-astisub - Manipulate subtitles in GO (.srt, .stl, .ttml, .webvtt, .ssa/.ass, teletext, .smi, etc.).
  • Go-astits - Parse and demux MPEG Transport Streams (.ts) natively in GO.
  • Go-m3u8 - Parser and generator library for Apple m3u8 playlists.
  • Go-mpd - Parser and generator library for MPEG-DASH manifest files.
  • Goav - Comprehensive Go bindings for FFmpeg.
  • Gortsplib - Pure Go RTSP server and client library.
  • Gst - Go bindings for GStreamer.
  • Libgosubs - Subtitle format support for go. Supports .srt, .ttml, and .ass.
  • Libvlc-go - Go bindings for libvlc 2.X/3.X/4.X (used by the VLC media player).

1 - Gmf:

Go bindings for FFmpeg av* libraries.

Installation

Prerequisites

Current master branch supports all major Go versions, starting from 1.6.

Build/install FFmpeg

build lastest version of ffmpeg, obtained from https://github.com/FFmpeg/FFmpeg
There is one required option, which is disabled by default, you should turn on: --enable-shared

E.g.:

./configure --prefix=/usr/local/ffmpeg --enable-shared
make
make install

Add pkgconfig path:

export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/ffmpeg/lib/pkgconfig/

Ensure, that PKG_CONFIG_PATH contains path to ffmpeg's pkgconfig folder.

# check it by running
pkg-config --libs libavformat

It should print valid path to the avformat library.

Now, just run

go get github.com/3d0c/gmf

Other methods

This package uses pkg-config way to obtain flags, includes and libraries path, so if you have ffmpeg installed, just ensure, that your installation has them (pkgconfig/ folder with proper pc files).

View on Github

2 - Go-astisub:

Manipulate subtitles in GO (.srt, .stl, .ttml, .webvtt, .ssa/.ass, teletext, .smi, etc.).

This is a Golang library to manipulate subtitles.

It allows you to manipulate srt, stl, ttml, ssa/ass, webvtt and teletext files for now.

Available operations are parsing, writing, applying linear correction, syncing, fragmenting, unfragmenting, merging and optimizing.

Installation

To install the library:

go get github.com/asticode/go-astisub

To install the CLI:

go install github.com/asticode/go-astisub/astisub        

Using the library in your code

WARNING: the code below doesn't handle errors for readibility purposes. However you SHOULD!

// Open subtitles
s1, _ := astisub.OpenFile("/path/to/example.ttml")
s2, _ := astisub.ReadFromSRT(bytes.NewReader([]byte("00:01:00.000 --> 00:02:00.000\nCredits")))

// Add a duration to every subtitles (syncing)
s1.Add(-2*time.Second)

// Fragment the subtitles
s1.Fragment(2*time.Second)

// Merge subtitles
s1.Merge(s2)

// Optimize subtitles
s1.Optimize()

// Unfragment the subtitles
s1.Unfragment()

// Apply linear correction
s1.ApplyLinearCorrection(1*time.Second, 2*time.Second, 5*time.Second, 7*time.Second)

// Write subtitles
s1.Write("/path/to/example.srt")
var buf = &bytes.Buffer{}
s2.WriteToTTML(buf)

Using the CLI

If astisub has been installed properly you can:

convert any type of subtitle to any other type of subtitle:

  astisub convert -i example.srt -o example.ttml

apply linear correction to any type of subtitle:

  astisub apply-linear-correction -i example.srt -a1 1s -d1 2s -a2 5s -d2 7s -o example.out.srt

fragment any type of subtitle:

  astisub fragment -i example.srt -f 2s -o example.out.srt

merge any type of subtitle into any other type of subtitle:

  astisub merge -i example.srt -i example.ttml -o example.out.srt

optimize any type of subtitle:

  astisub optimize -i example.srt -o example.out.srt

unfragment any type of subtitle:

  astisub unfragment -i example.srt -o example.out.srt

sync any type of subtitle:

  astisub sync -i example.srt -s "-2s" -o example.out.srt

View on Github

3 - Go-astits:

Parse and demux MPEG Transport Streams (.ts) natively in GO.

This is a Golang library to natively demux and mux MPEG Transport Streams (ts) in GO.

WARNING: this library is not yet production ready. Use at your own risks!

Installation

To install the library use the following:

go get -u github.com/asticode/go-astits/...

To install the executables use the following:

go install github.com/asticode/go-astits/cmd

Before looking at the code...

The transport stream is made of packets.
Each packet has a header, an optional adaptation field and a payload.
Several payloads can be appended and parsed as a data.

                                           TRANSPORT STREAM
 +--------------------------------------------------------------------------------------------------+
 |                                                                                                  |
 
                       PACKET                                         PACKET
 +----------------------------------------------+----------------------------------------------+----
 |                                              |                                              |
 
 +--------+---------------------------+---------+--------+---------------------------+---------+
 | HEADER | OPTIONAL ADAPTATION FIELD | PAYLOAD | HEADER | OPTIONAL ADAPTATION FIELD | PAYLOAD | ...
 +--------+---------------------------+---------+--------+---------------------------+---------+
 
                                      |         |                                    |         |
                                      +---------+                                    +---------+
                                           |                                              |
                                           +----------------------------------------------+
                                                                DATA

Using the library in your code

WARNING: the code below doesn't handle errors for readability purposes. However you SHOULD!

Demux

// Create a cancellable context in case you want to stop reading packets/data any time you want
ctx, cancel := context.WithCancel(context.Background())

// Handle SIGTERM signal
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)
go func() {
    <-ch
    cancel()
}()

// Open your file or initialize any kind of io.Reader
// Buffering using bufio.Reader is recommended for performance
f, _ := os.Open("/path/to/file.ts")
defer f.Close()

// Create the demuxer
dmx := astits.NewDemuxer(ctx, f)
for {
    // Get the next data
    d, _ := dmx.NextData()
    
    // Data is a PMT data
    if d.PMT != nil {
        // Loop through elementary streams
        for _, es := range d.PMT.ElementaryStreams {
                fmt.Printf("Stream detected: %d\n", es.ElementaryPID)
        }
        return
    }
}

View on Github

4 - Go-m3u8:

Parser and generator library for Apple m3u8 playlists.

go-m3u8 provides easy generation and parsing of m3u8 playlists defined in the HTTP Live Streaming (HLS) Internet Draft published by Apple.

  • The library completely implements version 20 of the HLS Internet Draft.
  • Provides parsing of an m3u8 playlist into an object model from any File, io.Reader or string.
  • Provides ability to write playlist to a string via String()
  • Distinction between a master and media playlist is handled automatically (single Playlist class).
  • Optionally, the library can automatically generate the audio/video codecs string used in the CODEC attribute based on specified H.264, AAC, or MP3 options (such as Profile/Level).

Installation

go get github.com/quangngotan95/go-m3u8

Usage (creating playlists)

Create a master playlist and child playlists for adaptive bitrate streaming:

import (
    "github.com/quangngotan95/go-m3u8/m3u8"
    "github.com/AlekSi/pointer"
)

playlist := m3u8.NewPlaylist()

Create a new playlist item:

item := &m3u8.PlaylistItem{
    Width:      pointer.ToInt(1920),
    Height:     pointer.ToInt(1080),
    Profile:    pointer.ToString("high"),
    Level:      pointer.ToString("4.1"),
    AudioCodec: pointer.ToString("aac-lc"),
    Bandwidth:  540,
    URI:        "test.url",
}
playlist.AppendItem(item)

Add alternate audio, camera angles, closed captions and subtitles by creating MediaItem instances and adding them to the Playlist:

item := &m3u8.MediaItem{
    Type:          "AUDIO",
    GroupID:       "audio-lo",
    Name:          "Francais",
    Language:      pointer.ToString("fre"),
    AssocLanguage: pointer.ToString("spoken"),
    AutoSelect:    pointer.ToBool(true),
    Default:       pointer.ToBool(false),
    Forced:        pointer.ToBool(true),
    URI:           pointer.ToString("frelo/prog_index.m3u8"),
}
playlist.AppendItem(item)

Create a standard playlist and add MPEG-TS segments via SegmentItem. You can also specify options for this type of playlist, however these options are ignored if playlist becomes a master playlist (anything but segments added):

playlist := &m3u8.Playlist{
    Target:   12,
    Sequence: 1,
    Version:  pointer.ToInt(1),
    Cache:    pointer.ToBool(false),
    Items: []m3u8.Item{
        &m3u8.SegmentItem{
            Duration: 11,
            Segment:  "test.ts",
        },
    },
}

You can also access the playlist as a string:

var str string
str = playlist.String()
...
fmt.Print(playlist)

Alternatively you can set codecs rather than having it generated automatically:

item := &m3u8.PlaylistItem{
    Width:     pointer.ToInt(1920),
    Height:    pointer.ToInt(1080),
    Codecs:    pointer.ToString("avc1.66.30,mp4a.40.2"),
    Bandwidth: 540,
    URI:       "test.url",
}

View on Github

5 - Go-mpd:

Parser and generator library for MPEG-DASH manifest files.

Go library for parsing and generating MPEG-DASH Media Presentation Description (MPD) files.

Usage

package main

import (
	"fmt"
	"github.com/unki2aut/go-mpd"
)

func main() {
	mpd := new(mpd.MPD)
	mpd.Decode([]byte(`<MPD type="static" mediaPresentationDuration="PT3M30S">
  <Period>
    <AdaptationSet mimeType="video/mp4" codecs="avc1.42c00d">
      <SegmentTemplate media="../video/$RepresentationID$/dash/segment_$Number$.m4s" initialization="../video/$RepresentationID$/dash/init.mp4" duration="100000" startNumber="0" timescale="25000"/>
      <Representation id="180_250000" bandwidth="250000" width="320" height="180" frameRate="25"/>
      <Representation id="270_400000" bandwidth="400000" width="480" height="270" frameRate="25"/>
      <Representation id="360_800000" bandwidth="800000" width="640" height="360" frameRate="25"/>
      <Representation id="540_1200000" bandwidth="1200000" width="960" height="540" frameRate="25"/>
      <Representation id="720_2400000" bandwidth="2400000" width="1280" height="720" frameRate="25"/>
      <Representation id="1080_4800000" bandwidth="4800000" width="1920" height="1080" frameRate="25"/>
    </AdaptationSet>
    <AdaptationSet lang="en" mimeType="audio/mp4" codecs="mp4a.40.2" bitmovin:label="English stereo">
      <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
      <SegmentTemplate media="../audio/$RepresentationID$/dash/segment_$Number$.m4s" initialization="../audio/$RepresentationID$/dash/init.mp4" duration="191472" startNumber="0" timescale="48000"/>
      <Representation id="1_stereo_128000" bandwidth="128000" audioSamplingRate="48000"/>
    </AdaptationSet>
  </Period>
</MPD>`))

	fmt.Println(mpd.MediaPresentationDuration)
}

Related links

View on Github

6 - Goav:

Comprehensive Go bindings for FFmpeg.

goav - Golang binding for FFmpeg

A comprehensive binding to the ffmpeg video/audio manipulation library.

Usage

import "github.com/giorgisio/goav/avformat"

func main() {

	filename := "sample.mp4"

	// Register all formats and codecs
	avformat.AvRegisterAll()

	ctx := avformat.AvformatAllocContext()

	// Open video file
	if avformat.AvformatOpenInput(&ctx, filename, nil, nil) != 0 {
		log.Println("Error: Couldn't open file.")
		return
	}

	// Retrieve stream information
	if ctx.AvformatFindStreamInfo(nil) < 0 {
		log.Println("Error: Couldn't find stream information.")

		// Close input file and free context
		ctx.AvformatCloseInput()
		return
	}

	//...

}

Libraries

  • avcodec corresponds to the ffmpeg library: libavcodec [provides implementation of a wider range of codecs]
  • avformat corresponds to the ffmpeg library: libavformat [implements streaming protocols, container formats and basic I/O access]
  • avutil corresponds to the ffmpeg library: libavutil [includes hashers, decompressors and miscellaneous utility functions]
  • avfilter corresponds to the ffmpeg library: libavfilter [provides a mean to alter decoded Audio and Video through chain of filters]
  • avdevice corresponds to the ffmpeg library: libavdevice [provides an abstraction to access capture and playback devices]
  • swresample corresponds to the ffmpeg library: libswresample [implements audio mixing and resampling routines]
  • swscale corresponds to the ffmpeg library: libswscale [implements color conversion and scaling routines]

Installation

FFMPEG INSTALL INSTRUCTIONS

sudo apt-get -y install autoconf automake build-essential libass-dev libfreetype6-dev libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texi2html zlib1g-dev

sudo apt install -y libavdevice-dev libavfilter-dev libswscale-dev libavcodec-dev libavformat-dev libswresample-dev libavutil-dev

sudo apt-get install yasm

export FFMPEG_ROOT=$HOME/ffmpeg
export CGO_LDFLAGS="-L$FFMPEG_ROOT/lib/ -lavcodec -lavformat -lavutil -lswscale -lswresample -lavdevice -lavfilter"
export CGO_CFLAGS="-I$FFMPEG_ROOT/include"
export LD_LIBRARY_PATH=$HOME/ffmpeg/lib
go get github.com/giorgisio/goav

View on Github

7 - Gortsplib:

Pure Go RTSP server and client library.

RTSP 1.0 client and server library for the Go programming language, written for rtsp-simple-server.

Go ≥ 1.17 is required.

Features:

  • Client
    • Query servers about available streams and tracks
    • Read
      • Read streams from servers with the UDP, UDP-multicast or TCP transport protocol
      • Read TLS-encrypted streams (TCP only)
      • Switch transport protocol automatically
      • Read only selected tracks of a stream
      • Pause or seek without disconnecting from the server
      • Generate RTCP receiver reports (UDP only)
      • Reorder incoming RTP packets (UDP only)
      • Clean up non-compliant streams (remove padding, re-encode RTP packets if they are too big)
    • Publish
      • Publish streams to servers with the UDP or TCP transport protocol
      • Publish TLS-encrypted streams (TCP only)
      • Switch transport protocol automatically
      • Pause without disconnecting from the server
      • Generate RTCP sender reports (UDP only)
  • Server
    • Handle requests from clients
    • Sessions and connections are independent
    • Publish
      • Read streams from clients with the UDP or TCP transport protocol
      • Read TLS-encrypted streams (TCP only)
      • Generate RTCP receiver reports (UDP only)
      • Reorder incoming RTP packets (UDP only)
      • Clean up non-compliant streams (remove padding, re-encode RTP packets if they are too big)
    • Read
      • Write streams to clients with the UDP, UDP-multicast or TCP transport protocol
      • Write TLS-encrypted streams
      • Compute and provide SSRC, RTP-Info to clients
      • Generate RTCP sender reports (UDP only)
  • Utilities
    • Parse RTSP elements: requests, responses, SDP
    • Parse H264 elements and formats: RTP/H264, Annex-B, AVCC, anti-competition, DTS
    • Parse AAC elements and formats: RTP/AAC, ADTS, MPEG-4 audio configurations

View on Github

8 - Gst:

Go bindings for GStreamer.

Go bindings for GStreamer at a very early stage of maturity.

This package is based on GLib bindings. It should be goinstalable. Try

$ go get github.com/ziutek/gst

Documentation

See examples directory and http://gopkgdoc.appspot.com/pkg/github.com/ziutek/gst

To run examples use go run command:

$ cd examples
$ go run simple.go

To run live WebM example use go run live_webm.go and open http://127.0.0.1:8080 with your browser. You probably need to wait a long time for video because of small bitrate of this stream and big buffer in you browser.

View on Github

9 - Libgosubs:

Subtitle format support for go. Supports .srt, .ttml, and .ass.

Golang library to read and write subtitles in the following formats

  • Advanced SubStation Alpha v4
  • SRT
  • TTML v1.0 - This is based on the spec provided by Netflix in their documentation
  • WebVTT experimental support
  • MicroDVD experimental support

notes

TTML is somewhat complex to implement in Go due to the way that Go handles XML namespaces. Until this issue is fixed, two different structs for reading and writing, as well as a lengthy conversion function will probably be necessary. See the test file for a sample (and probably poor) implementation.

todo

  • Clean up the ASSv4 format, specifically do something about the way headers are handled

updates

  • Experimental MicroDVD format support added

libgosubs project garbage

Documentation

Available via Godoc

GodocFormat
GoDocASS
GoDocSRT
GoDocTTML
GoDocWVTT
GoDocMicroDVD

View on Github

10 - Libvlc-go:

Go bindings for libvlc 2.X/3.X/4.X (used by the VLC media player).

The package can be useful for adding multimedia capabilities to applications through the provided player interfaces. It relies on Go modules in order to mirror each supported major version of libVLC.

Installation

In order to support multiple versions of libVLC, the package contains a Go module for each major version of the API. Choose an installation option depending on the version of libVLC you want to use.

libVLC v3.X

go get github.com/adrg/libvlc-go/v3

libVLC v2.X

go get github.com/adrg/libvlc-go/v2

# Build for libVLC < v2.2.0
go build -tags legacy

All versions above also work for projects which are not using Go modules. However, consider switching to modules.

Usage

package main

import (
    "log"

    vlc "github.com/adrg/libvlc-go/v3"
)

func main() {
    // Initialize libVLC. Additional command line arguments can be passed in
    // to libVLC by specifying them in the Init function.
    if err := vlc.Init("--no-video", "--quiet"); err != nil {
        log.Fatal(err)
    }
    defer vlc.Release()

    // Create a new player.
    player, err := vlc.NewPlayer()
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        player.Stop()
        player.Release()
    }()

    // Add a media file from path or from URL.
    // Set player media from path:
    // media, err := player.LoadMediaFromPath("localpath/test.mp4")
    // Set player media from URL:
    media, err := player.LoadMediaFromURL("http://stream-uk1.radioparadise.com/mp3-32")
    if err != nil {
        log.Fatal(err)
    }
    defer media.Release()

    // Retrieve player event manager.
    manager, err := player.EventManager()
    if err != nil {
        log.Fatal(err)
    }

    // Register the media end reached event with the event manager.
    quit := make(chan struct{})
    eventCallback := func(event vlc.Event, userData interface{}) {
        close(quit)
    }

    eventID, err := manager.Attach(vlc.MediaPlayerEndReached, eventCallback, nil)
    if err != nil {
        log.Fatal(err)
    }
    defer manager.Detach(eventID)

    // Start playing the media.
    err = player.Play()
    if err != nil {
        log.Fatal(err)
    }

    <-quit
}

View on Github

Thank you for following this article.

Related videos:

Golang Libraries You MUST Learn (2022)

#go #golang #video 

10 Popular Golang Libraries for Manipulating Video
Rocio  O'Keefe

Rocio O'Keefe

1660685040

Video_thumbnail: A Plugin Generates Thumbnail From Video File Or URL

video_thumbnail

This plugin generates thumbnail from video file or URL. It returns image in memory or writes into a file. It offers rich options to control the image format, resolution and quality. Supports iOS and Android. 

video-file video-url

Methods

functionparameterdescriptionreturn
thumbnailDataString [video], optional Map<String, dynamic> [headers], ImageFormat [imageFormat](JPEG/PNG/WEBP), int [maxHeight](0: for the original resolution of the video, or scaled by the source aspect ratio), [maxWidth](0: for the original resolution of the video, or scaled by the source aspect ratio), int [timeMs]generates the thumbnail from the frame around the specified millisecond, int[quality]`(0-100)generates thumbnail from [video][Future<Uint8List>]
thumbnailFileString [video], optional Map<String, dynamic> [headers], String [thumbnailPath](folder or full path where to store the thumbnail file, null to save to same folder as the video file), ImageFormat [imageFormat](JPEG/PNG/WEBP), int [maxHeight](0: for the original resolution of the video, or scaled by the source aspect ratio), int [maxWidth](0: for the original resolution of the video, or scaled by the source aspect ratio), int [timeMs] generates the thumbnail from the frame around the specified millisecond, int [quality](0-100)creates a file of the thumbnail from the [video][Future<String>]

Warning:

Giving both the maxHeight and maxWidth has different result on Android platform, it actually scales the thumbnail to the specified maxHeight and maxWidth. To generate the thumbnail from a network resource, the video must be properly URL encoded.

Usage

Installing add video_thumbnail as a dependency in your pubspec.yaml file.

dependencies:
  video_thumbnail: ^0.5.2

import

import 'package:video_thumbnail/video_thumbnail.dart';

Generate a thumbnail in memory from video file

final uint8list = await VideoThumbnail.thumbnailData(
  video: videofile.path,
  imageFormat: ImageFormat.JPEG,
  maxWidth: 128, // specify the width of the thumbnail, let the height auto-scaled to keep the source aspect ratio
  quality: 25,
);

Generate a thumbnail file from video URL

final fileName = await VideoThumbnail.thumbnailFile(
  video: "https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4",
  thumbnailPath: (await getTemporaryDirectory()).path,
  imageFormat: ImageFormat.WEBP,
  maxHeight: 64, // specify the height of the thumbnail, let the width auto-scaled to keep the source aspect ratio
  quality: 75,
);

Generate a thumbnail file from video Assets declared in pubspec.yaml

final byteData = await rootBundle.load("assets/my_video.mp4");
Directory tempDir = await getTemporaryDirectory();

File tempVideo = File("${tempDir.path}/assets/my_video.mp4")
  ..createSync(recursive: true)
  ..writeAsBytesSync(byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes));

final fileName = await VideoThumbnail.thumbnailFile(
  video: tempVideo.path,
  thumbnailPath: (await getTemporaryDirectory()).path,
  imageFormat: ImageFormat.PNG,  
  quality: 100,
);

Notes

Fork or pull requests are always welcome. Currently it seems have a little performance issue while generating WebP thumbnail by using libwebp under iOS.

Installing

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add video_thumbnail

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  video_thumbnail: ^0.5.2

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

Import it

Now in your Dart code, you can use:

import 'package:video_thumbnail/video_thumbnail.dart';

example/lib/main.dart

import 'dart:async';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'dart:io';

import 'package:video_thumbnail/video_thumbnail.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DemoHome(),
    );
  }
}

class ThumbnailRequest {
  final String video;
  final String thumbnailPath;
  final ImageFormat imageFormat;
  final int maxHeight;
  final int maxWidth;
  final int timeMs;
  final int quality;

  const ThumbnailRequest(
      {this.video,
      this.thumbnailPath,
      this.imageFormat,
      this.maxHeight,
      this.maxWidth,
      this.timeMs,
      this.quality});
}

class ThumbnailResult {
  final Image image;
  final int dataSize;
  final int height;
  final int width;
  const ThumbnailResult({this.image, this.dataSize, this.height, this.width});
}

Future<ThumbnailResult> genThumbnail(ThumbnailRequest r) async {
  //WidgetsFlutterBinding.ensureInitialized();
  Uint8List bytes;
  final Completer<ThumbnailResult> completer = Completer();
  if (r.thumbnailPath != null) {
    final thumbnailPath = await VideoThumbnail.thumbnailFile(
        video: r.video,
        headers: {
          "USERHEADER1": "user defined header1",
          "USERHEADER2": "user defined header2",
        },
        thumbnailPath: r.thumbnailPath,
        imageFormat: r.imageFormat,
        maxHeight: r.maxHeight,
        maxWidth: r.maxWidth,
        timeMs: r.timeMs,
        quality: r.quality);

    print("thumbnail file is located: $thumbnailPath");

    final file = File(thumbnailPath);
    bytes = file.readAsBytesSync();
  } else {
    bytes = await VideoThumbnail.thumbnailData(
        video: r.video,
        headers: {
          "USERHEADER1": "user defined header1",
          "USERHEADER2": "user defined header2",
        },
        imageFormat: r.imageFormat,
        maxHeight: r.maxHeight,
        maxWidth: r.maxWidth,
        timeMs: r.timeMs,
        quality: r.quality);
  }

  int _imageDataSize = bytes.length;
  print("image size: $_imageDataSize");

  final _image = Image.memory(bytes);
  _image.image
      .resolve(ImageConfiguration())
      .addListener(ImageStreamListener((ImageInfo info, bool _) {
    completer.complete(ThumbnailResult(
      image: _image,
      dataSize: _imageDataSize,
      height: info.image.height,
      width: info.image.width,
    ));
  }));
  return completer.future;
}

class GenThumbnailImage extends StatefulWidget {
  final ThumbnailRequest thumbnailRequest;

  const GenThumbnailImage({Key key, this.thumbnailRequest}) : super(key: key);

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

class _GenThumbnailImageState extends State<GenThumbnailImage> {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<ThumbnailResult>(
      future: genThumbnail(widget.thumbnailRequest),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.hasData) {
          final _image = snapshot.data.image;
          final _width = snapshot.data.width;
          final _height = snapshot.data.height;
          final _dataSize = snapshot.data.dataSize;
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: <Widget>[
              Center(
                child: Text(
                    "Image ${widget.thumbnailRequest.thumbnailPath == null ? 'data size' : 'file size'}: $_dataSize, width:$_width, height:$_height"),
              ),
              Container(
                color: Colors.grey,
                height: 1.0,
              ),
              _image,
            ],
          );
        } else if (snapshot.hasError) {
          return Container(
            padding: EdgeInsets.all(8.0),
            color: Colors.red,
            child: Text(
              "Error:\n${snapshot.error.toString()}",
            ),
          );
        } else {
          return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: <Widget>[
                Text(
                    "Generating the thumbnail for: ${widget.thumbnailRequest.video}..."),
                SizedBox(
                  height: 10.0,
                ),
                CircularProgressIndicator(),
              ]);
        }
      },
    );
  }
}

class ImageInFile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class DemoHome extends StatefulWidget {
  @override
  _DemoHomeState createState() => _DemoHomeState();
}

class _DemoHomeState extends State<DemoHome> {
  final _editNode = FocusNode();
  final _video = TextEditingController(
      text:
          "https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4");
  ImageFormat _format = ImageFormat.JPEG;
  int _quality = 50;
  int _sizeH = 0;
  int _sizeW = 0;
  int _timeMs = 0;

  GenThumbnailImage _futreImage;

  String _tempDir;

  @override
  void initState() {
    super.initState();
    getTemporaryDirectory().then((d) => _tempDir = d.path);
  }

  @override
  Widget build(BuildContext context) {
    final _settings = <Widget>[
      Slider(
        value: _sizeH * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _sizeH = v.toInt();
        }),
        max: 256.0,
        divisions: 256,
        label: "$_sizeH",
      ),
      Center(
        child: (_sizeH == 0)
            ? const Text(
                "Original of the video's height or scaled by the source aspect ratio")
            : Text("Max height: $_sizeH(px)"),
      ),
      Slider(
        value: _sizeW * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _sizeW = v.toInt();
        }),
        max: 256.0,
        divisions: 256,
        label: "$_sizeW",
      ),
      Center(
        child: (_sizeW == 0)
            ? const Text(
                "Original of the video's width or scaled by source aspect ratio")
            : Text("Max width: $_sizeW(px)"),
      ),
      Slider(
        value: _timeMs * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _timeMs = v.toInt();
        }),
        max: 10.0 * 1000,
        divisions: 1000,
        label: "$_timeMs",
      ),
      Center(
        child: (_timeMs == 0)
            ? const Text("The beginning of the video")
            : Text("The closest frame at $_timeMs(ms) of the video"),
      ),
      Slider(
        value: _quality * 1.0,
        onChanged: (v) => setState(() {
          _editNode.unfocus();
          _quality = v.toInt();
        }),
        max: 100.0,
        divisions: 100,
        label: "$_quality",
      ),
      Center(child: Text("Quality: $_quality")),
      Padding(
        padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 8.0),
        child: InputDecorator(
          decoration: InputDecoration(
            border: OutlineInputBorder(),
            filled: true,
            isDense: true,
            labelText: "Thumbnail Format",
          ),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            mainAxisSize: MainAxisSize.max,
            children: <Widget>[
              Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Radio<ImageFormat>(
                      groupValue: _format,
                      value: ImageFormat.JPEG,
                      onChanged: (v) => setState(() {
                        _format = v;
                        _editNode.unfocus();
                      }),
                    ),
                    const Text("JPEG"),
                  ]),
              Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Radio<ImageFormat>(
                      groupValue: _format,
                      value: ImageFormat.PNG,
                      onChanged: (v) => setState(() {
                        _format = v;
                        _editNode.unfocus();
                      }),
                    ),
                    const Text("PNG"),
                  ]),
              Row(
                  mainAxisAlignment: MainAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                    Radio<ImageFormat>(
                      groupValue: _format,
                      value: ImageFormat.WEBP,
                      onChanged: (v) => setState(() {
                        _format = v;
                        _editNode.unfocus();
                      }),
                    ),
                    const Text("WebP"),
                  ]),
            ],
          ),
        ),
      )
    ];
    return Scaffold(
        appBar: AppBar(
          title: const Text('Thumbnail Plugin example'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 8.0),
              child: TextField(
                decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  filled: true,
                  isDense: true,
                  labelText: "Video URI",
                ),
                maxLines: null,
                controller: _video,
                focusNode: _editNode,
                keyboardType: TextInputType.url,
                textInputAction: TextInputAction.done,
                onEditingComplete: () {
                  _editNode.unfocus();
                },
              ),
            ),
            for (var i in _settings) i,
            Expanded(
              child: Container(
                color: Colors.grey[300],
                child: Scrollbar(
                  child: ListView(
                    shrinkWrap: true,
                    children: <Widget>[
                      (_futreImage != null) ? _futreImage : SizedBox(),
                    ],
                  ),
                ),
              ),
            ),
          ],
        ),
        drawer: Drawer(
          child: Column(
            children: <Widget>[
              AppBar(
                title: const Text("Settings"),
                actions: <Widget>[
                  IconButton(
                    icon: Icon(Icons.close),
                    onPressed: () => Navigator.pop(context),
                  )
                ],
              ),
              for (var i in _settings) i,
            ],
          ),
        ),
        floatingActionButton: Row(
          mainAxisAlignment: MainAxisAlignment.end,
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            FloatingActionButton(
              onPressed: () async {
                File video =
                    await ImagePicker.pickVideo(source: ImageSource.camera);
                setState(() {
                  _video.text = video.path;
                });
              },
              child: Icon(Icons.videocam),
              tooltip: "Capture a video",
            ),
            const SizedBox(
              width: 5.0,
            ),
            FloatingActionButton(
              onPressed: () async {
                File video =
                    await ImagePicker.pickVideo(source: ImageSource.gallery);
                setState(() {
                  _video.text = video?.path;
                });
              },
              child: Icon(Icons.local_movies),
              tooltip: "Pick a video",
            ),
            const SizedBox(
              width: 20.0,
            ),
            FloatingActionButton(
              tooltip: "Generate a data of thumbnail",
              onPressed: () async {
                setState(() {
                  _futreImage = GenThumbnailImage(
                      thumbnailRequest: ThumbnailRequest(
                          video: _video.text,
                          thumbnailPath: null,
                          imageFormat: _format,
                          maxHeight: _sizeH,
                          maxWidth: _sizeW,
                          timeMs: _timeMs,
                          quality: _quality));
                });
              },
              child: const Text("Data"),
            ),
            const SizedBox(
              width: 5.0,
            ),
            FloatingActionButton(
              tooltip: "Generate a file of thumbnail",
              onPressed: () async {
                setState(() {
                  _futreImage = GenThumbnailImage(
                      thumbnailRequest: ThumbnailRequest(
                          video: _video.text,
                          thumbnailPath: _tempDir,
                          imageFormat: _format,
                          maxHeight: _sizeH,
                          maxWidth: _sizeW,
                          timeMs: _timeMs,
                          quality: _quality));
                });
              },
              child: const Text("File"),
            ),
          ],
        ));
  }
}

Download Details:

Author: justsoft
Source Code: https://github.com/justsoft/video_thumbnail 
License: MIT license

#flutter #dart #video #url 

Video_thumbnail: A Plugin Generates Thumbnail From Video File Or URL