abrizer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +91 -0
- data/Rakefile +10 -0
- data/Vagrantfile +76 -0
- data/abrizer.gemspec +31 -0
- data/ansible/development-playbook.retry +1 -0
- data/ansible/development-playbook.yml +19 -0
- data/ansible/development.ini +2 -0
- data/ansible/roles/basic-setup/tasks/main.yml +39 -0
- data/ansible/roles/bento4/tasks/main.yml +14 -0
- data/ansible/roles/ffmpeg/defaults/main.yml +6 -0
- data/ansible/roles/ffmpeg/tasks/ff-libraries.yml +21 -0
- data/ansible/roles/ffmpeg/tasks/ffmpeg.yml +18 -0
- data/ansible/roles/ffmpeg/tasks/libfdk_aac.yml +19 -0
- data/ansible/roles/ffmpeg/tasks/libmp3lame.yml +20 -0
- data/ansible/roles/ffmpeg/tasks/libogg.yml +18 -0
- data/ansible/roles/ffmpeg/tasks/libopus.yml +20 -0
- data/ansible/roles/ffmpeg/tasks/libvorbis.yml +18 -0
- data/ansible/roles/ffmpeg/tasks/libvpx.yml +18 -0
- data/ansible/roles/ffmpeg/tasks/main.yml +80 -0
- data/ansible/roles/ffmpeg/tasks/x264.yml +18 -0
- data/ansible/roles/ffmpeg/tasks/x265.yml +14 -0
- data/ansible/roles/ffmpeg/tasks/yasm.yml +19 -0
- data/ansible/roles/franklinkim.environment/.clog.toml +4 -0
- data/ansible/roles/franklinkim.environment/.editorconfig +16 -0
- data/ansible/roles/franklinkim.environment/.gitignore +2 -0
- data/ansible/roles/franklinkim.environment/.travis.yml +17 -0
- data/ansible/roles/franklinkim.environment/CHANGELOG.md +14 -0
- data/ansible/roles/franklinkim.environment/LICENSE +22 -0
- data/ansible/roles/franklinkim.environment/Makefile +11 -0
- data/ansible/roles/franklinkim.environment/README.md +100 -0
- data/ansible/roles/franklinkim.environment/Vagrantfile +23 -0
- data/ansible/roles/franklinkim.environment/defaults/main.yml +13 -0
- data/ansible/roles/franklinkim.environment/meta/.galaxy_install_info +1 -0
- data/ansible/roles/franklinkim.environment/meta/main.yml +143 -0
- data/ansible/roles/franklinkim.environment/meta/readme.yml +11 -0
- data/ansible/roles/franklinkim.environment/tasks/config.yml +21 -0
- data/ansible/roles/franklinkim.environment/tasks/main.yml +8 -0
- data/ansible/roles/franklinkim.environment/tests/main.yml +8 -0
- data/ansible/roles/rvm_io.rvm1-ruby/.gitignore +6 -0
- data/ansible/roles/rvm_io.rvm1-ruby/.travis.yml +52 -0
- data/ansible/roles/rvm_io.rvm1-ruby/LICENSE +22 -0
- data/ansible/roles/rvm_io.rvm1-ruby/README.md +142 -0
- data/ansible/roles/rvm_io.rvm1-ruby/defaults/main.yml +44 -0
- data/ansible/roles/rvm_io.rvm1-ruby/meta/main.yml +21 -0
- data/ansible/roles/rvm_io.rvm1-ruby/tasks/main.yml +4 -0
- data/ansible/roles/rvm_io.rvm1-ruby/tasks/rubies.yml +78 -0
- data/ansible/roles/rvm_io.rvm1-ruby/tasks/rvm.yml +55 -0
- data/ansible/roles/rvm_io.rvm1-ruby/tests/inventory +1 -0
- data/ansible/roles/rvm_io.rvm1-ruby/tests/test.yml +14 -0
- data/ansible/roles/rvm_io.rvm1-ruby/vars/main.yml +22 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/abrizer +5 -0
- data/lib/abrizer/adaptation.rb +53 -0
- data/lib/abrizer/adaptation_finder.rb +59 -0
- data/lib/abrizer/all.rb +20 -0
- data/lib/abrizer/cleaner.rb +37 -0
- data/lib/abrizer/cli.rb +84 -0
- data/lib/abrizer/ffmpeg_processor.rb +59 -0
- data/lib/abrizer/ffprobe_informer.rb +69 -0
- data/lib/abrizer/filepath_helpers.rb +33 -0
- data/lib/abrizer/package_dash_bento.rb +35 -0
- data/lib/abrizer/package_dash_shaka.rb +49 -0
- data/lib/abrizer/package_hls_bento.rb +35 -0
- data/lib/abrizer/package_hls_shaka.rb +50 -0
- data/lib/abrizer/processor.rb +8 -0
- data/lib/abrizer/progressive_mp4.rb +37 -0
- data/lib/abrizer/progressive_vp9.rb +57 -0
- data/lib/abrizer/sprites.rb +24 -0
- data/lib/abrizer/version.rb +3 -0
- data/lib/abrizer.rb +22 -0
- metadata +232 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
---
|
2
|
+
|
3
|
+
# Install 1 or more versions of ruby
|
4
|
+
# The last ruby listed will be set as the default ruby
|
5
|
+
rvm1_rubies:
|
6
|
+
- 'ruby-2.2.2'
|
7
|
+
|
8
|
+
# Delete a specific version of ruby (ie. ruby-2.1.0)
|
9
|
+
rvm1_delete_ruby:
|
10
|
+
|
11
|
+
# Install path for rvm (defaults to system wide)
|
12
|
+
rvm1_install_path: '/usr/local/rvm'
|
13
|
+
|
14
|
+
# Add or remove any install flags
|
15
|
+
# NOTE: If you are doing a USER BASED INSTALL then
|
16
|
+
# make sure you ADD the --user-install flag below
|
17
|
+
rvm1_install_flags: '--auto-dotfiles'
|
18
|
+
|
19
|
+
# Add additional ruby install flags
|
20
|
+
rvm1_ruby_install_flags:
|
21
|
+
|
22
|
+
# Set the owner for the rvm directory
|
23
|
+
rvm1_user: 'root'
|
24
|
+
|
25
|
+
# URL for the latest installer script
|
26
|
+
rvm1_rvm_latest_installer: 'https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer'
|
27
|
+
|
28
|
+
# rvm version to use
|
29
|
+
rvm1_rvm_version: 'stable'
|
30
|
+
|
31
|
+
# Check and update rvm, disabling this will force rvm to never update
|
32
|
+
rvm1_rvm_check_for_updates: True
|
33
|
+
|
34
|
+
# GPG key verification, use an empty string if you want to skip this
|
35
|
+
# Note: Unless you know what you're doing, just keep it as is
|
36
|
+
# Identity proof: https://keybase.io/mpapis
|
37
|
+
# PGP message: https://rvm.io/mpapis.asc
|
38
|
+
rvm1_gpg_keys: 'D39DC0E3'
|
39
|
+
|
40
|
+
# The GPG key server
|
41
|
+
rvm1_gpg_key_server: 'hkp://keys.gnupg.net'
|
42
|
+
|
43
|
+
# autolib mode, see https://rvm.io/rvm/autolibs
|
44
|
+
rvm1_autolib_mode: 3
|
@@ -0,0 +1,21 @@
|
|
1
|
+
---
|
2
|
+
galaxy_info:
|
3
|
+
author: Nick Janetakis
|
4
|
+
description: The official rvm role to install and manage your ruby versions.
|
5
|
+
company:
|
6
|
+
license: license (MIT)
|
7
|
+
min_ansible_version: 1.5
|
8
|
+
|
9
|
+
platforms:
|
10
|
+
- name: EL
|
11
|
+
versions:
|
12
|
+
- all
|
13
|
+
- name: Ubuntu
|
14
|
+
versions:
|
15
|
+
- all
|
16
|
+
|
17
|
+
categories:
|
18
|
+
- development
|
19
|
+
- web
|
20
|
+
|
21
|
+
dependencies: []
|
@@ -0,0 +1,78 @@
|
|
1
|
+
---
|
2
|
+
|
3
|
+
- name: Detect if rubies are installed
|
4
|
+
command: '{{ rvm1_rvm }} {{ item }} do true'
|
5
|
+
changed_when: False
|
6
|
+
failed_when: False
|
7
|
+
register: detect_rubies
|
8
|
+
with_items: '{{ rvm1_rubies }}'
|
9
|
+
when: rvm1_rubies
|
10
|
+
|
11
|
+
- name: Install rubies
|
12
|
+
command: '{{ rvm1_rvm }} install {{ item.item }} {{ rvm1_ruby_install_flags }}'
|
13
|
+
when: rvm1_rubies and item.rc|default(0) != 0
|
14
|
+
with_items: '{{ detect_rubies.results }}'
|
15
|
+
become: yes
|
16
|
+
become_user: '{{ rvm1_user }}'
|
17
|
+
|
18
|
+
- name: Detect default ruby version
|
19
|
+
command: '{{ rvm1_rvm }} alias list default'
|
20
|
+
changed_when: False
|
21
|
+
register: detect_default_ruby_version
|
22
|
+
become: yes
|
23
|
+
become_user: '{{ rvm1_user }}'
|
24
|
+
|
25
|
+
- name: Select default ruby
|
26
|
+
command: '{{ rvm1_rvm }} alias create default {{ rvm1_default_ruby_version }}'
|
27
|
+
when: detect_default_ruby_version.stdout|default() == '' or
|
28
|
+
rvm1_default_ruby_version not in detect_default_ruby_version.stdout
|
29
|
+
become: yes
|
30
|
+
become_user: '{{ rvm1_user }}'
|
31
|
+
|
32
|
+
- name: Detect installed ruby patch number
|
33
|
+
shell: >
|
34
|
+
{{ rvm1_rvm }} list strings | grep {{ item }} | tail -n 1
|
35
|
+
with_items: '{{ rvm1_rubies }}'
|
36
|
+
changed_when: False
|
37
|
+
register: ruby_patch
|
38
|
+
always_run: yes # Run even when in --check mode (http://docs.ansible.com/ansible/playbooks_checkmode.html)
|
39
|
+
become: yes
|
40
|
+
become_user: '{{ rvm1_user }}'
|
41
|
+
|
42
|
+
- name: Install bundler if not installed
|
43
|
+
shell: >
|
44
|
+
ls {{ rvm1_install_path }}/wrappers/{{ item.stdout }}
|
45
|
+
| if ! grep "^bundler " ; then {{ rvm1_install_path }}/wrappers/{{ item.stdout }}/gem install bundler ; fi
|
46
|
+
args:
|
47
|
+
creates: '{{ rvm1_install_path }}/wrappers/{{ item.stdout }}/bundler'
|
48
|
+
with_items: '{{ ruby_patch.results }}'
|
49
|
+
register: bundler_install
|
50
|
+
changed_when: '"Successfully installed bundler" in bundler_install.stdout'
|
51
|
+
become: yes
|
52
|
+
become_user: '{{ rvm1_user }}'
|
53
|
+
|
54
|
+
- name: Symlink ruby related binaries on the system path
|
55
|
+
file:
|
56
|
+
state: 'link'
|
57
|
+
src: '{{ rvm1_install_path }}/wrappers/default/{{ item }}'
|
58
|
+
dest: '{{ rvm1_symlink_to }}/{{ item }}'
|
59
|
+
owner: 'root'
|
60
|
+
group: 'root'
|
61
|
+
when: not '--user-install' in rvm1_install_flags
|
62
|
+
with_items: '{{ rvm1_symlink_binaries }}'
|
63
|
+
|
64
|
+
- name: Detect if ruby version can be deleted
|
65
|
+
command: '{{ rvm1_rvm }} {{ rvm1_delete_ruby }} do true'
|
66
|
+
changed_when: False
|
67
|
+
failed_when: False
|
68
|
+
register: detect_delete_ruby
|
69
|
+
when: rvm1_delete_ruby
|
70
|
+
become: yes
|
71
|
+
become_user: '{{ rvm1_user }}'
|
72
|
+
|
73
|
+
- name: Delete ruby version
|
74
|
+
command: '{{ rvm1_rvm }} remove {{ rvm1_delete_ruby }}'
|
75
|
+
changed_when: False
|
76
|
+
when: rvm1_delete_ruby and detect_delete_ruby.rc == 0
|
77
|
+
become: yes
|
78
|
+
become_user: '{{ rvm1_user }}'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
---
|
2
|
+
|
3
|
+
- name: Detect rvm binary
|
4
|
+
stat: path='{{ rvm1_rvm }}'
|
5
|
+
register: rvm_binary
|
6
|
+
|
7
|
+
- name: Detect rvm installer
|
8
|
+
stat: path='{{ rvm1_temp_download_path }}/rvm-installer.sh'
|
9
|
+
register: rvm_installer
|
10
|
+
|
11
|
+
- name: Detect current rvm version
|
12
|
+
command: '{{ rvm1_rvm}} version'
|
13
|
+
changed_when: False
|
14
|
+
register: rvm_current_version
|
15
|
+
when: rvm_binary.stat.exists
|
16
|
+
|
17
|
+
- name: Install rvm installer
|
18
|
+
get_url:
|
19
|
+
url: '{{ rvm1_rvm_latest_installer }}'
|
20
|
+
dest: '{{ rvm1_temp_download_path }}/rvm-installer.sh'
|
21
|
+
when: not rvm_installer.stat.exists
|
22
|
+
|
23
|
+
- name: Configure rvm installer
|
24
|
+
file:
|
25
|
+
path: '{{ rvm1_temp_download_path }}/rvm-installer.sh'
|
26
|
+
mode: 0755
|
27
|
+
when: not rvm_binary.stat.exists
|
28
|
+
|
29
|
+
- name: Import GPG keys
|
30
|
+
command: 'gpg --keyserver {{ rvm1_gpg_key_server }} --recv-keys {{ rvm1_gpg_keys }}'
|
31
|
+
changed_when: False
|
32
|
+
when: rvm1_gpg_keys != ''
|
33
|
+
become: yes
|
34
|
+
become_user: '{{ rvm1_user }}'
|
35
|
+
|
36
|
+
- name: Install rvm
|
37
|
+
command: >
|
38
|
+
{{ rvm1_temp_download_path }}/rvm-installer.sh {{ rvm1_rvm_version }}
|
39
|
+
--path {{ rvm1_install_path }} {{ rvm1_install_flags }}
|
40
|
+
when: not rvm_binary.stat.exists
|
41
|
+
become: yes
|
42
|
+
become_user: '{{ rvm1_user }}'
|
43
|
+
|
44
|
+
- name: Update rvm
|
45
|
+
shell: '{{ rvm1_rvm }} get {{ rvm1_rvm_version }} && {{ rvm1_rvm }} reload'
|
46
|
+
changed_when: False
|
47
|
+
when: rvm_binary.stat.exists and rvm1_rvm_check_for_updates
|
48
|
+
become: yes
|
49
|
+
become_user: '{{ rvm1_user }}'
|
50
|
+
|
51
|
+
- name: Configure rvm
|
52
|
+
command: '{{ rvm1_rvm }} autolibs {{ rvm1_autolib_mode }}'
|
53
|
+
when: not rvm_binary.stat.exists
|
54
|
+
become: yes
|
55
|
+
become_user: '{{ rvm1_user }}'
|
@@ -0,0 +1 @@
|
|
1
|
+
localhost
|
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
|
3
|
+
rvm1_temp_download_path: '/tmp'
|
4
|
+
|
5
|
+
rvm1_default_ruby_version: '{{ rvm1_rubies | last if rvm1_rubies and rvm1_rubies is iterable else "" }}'
|
6
|
+
|
7
|
+
rvm1_rvm: '{{ rvm1_install_path }}/bin/rvm'
|
8
|
+
|
9
|
+
rvm1_symlink_binaries:
|
10
|
+
- 'bundle'
|
11
|
+
- 'bundler'
|
12
|
+
- 'erb'
|
13
|
+
- 'executable-hooks-uninstaller'
|
14
|
+
- 'gem'
|
15
|
+
- 'irb'
|
16
|
+
- 'rake'
|
17
|
+
- 'rdoc'
|
18
|
+
- 'ri'
|
19
|
+
- 'ruby'
|
20
|
+
- 'testrb'
|
21
|
+
|
22
|
+
rvm1_symlink_to: '/usr/local/bin'
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "abrizer"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/abrizer
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Abrizer
|
2
|
+
class Adaptation
|
3
|
+
|
4
|
+
include FilepathHelpers
|
5
|
+
|
6
|
+
attr_reader :width, :height, :bitrate
|
7
|
+
|
8
|
+
def initialize(width:, height:, bitrate:)
|
9
|
+
@width = width
|
10
|
+
@height = height
|
11
|
+
@bitrate = bitrate
|
12
|
+
end
|
13
|
+
|
14
|
+
def ffmpeg_cmd(input, output_directory, pass)
|
15
|
+
cmd = %Q|ffmpeg -y -i #{input} -vf \
|
16
|
+
scale='#{width}:trunc(#{width}/dar/2)*2',setsar=1 \
|
17
|
+
-an -c:v libx264 -x264opts 'keyint=48:min-keyint=48:no-scenecut' \
|
18
|
+
-b:v #{bitrate}k -preset faster |
|
19
|
+
if pass == 2
|
20
|
+
cmd += %Q| -maxrate #{constrained_bitrate}k -bufsize #{bitrate}k -pass 2 #{filepath(input, output_directory)} |
|
21
|
+
else
|
22
|
+
cmd += " -pass 1 -f mp4 /dev/null "
|
23
|
+
end
|
24
|
+
cmd
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO: make the constrained bitrate (maxrate) value configurable
|
28
|
+
def constrained_bitrate
|
29
|
+
@bitrate * 1.1
|
30
|
+
end
|
31
|
+
|
32
|
+
def outfile_basename(input)
|
33
|
+
extname = File.extname input
|
34
|
+
basename = File.basename input, extname
|
35
|
+
"#{basename}-#{width}x#{height}-#{bitrate}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def filepath(input, output_directory)
|
39
|
+
name = "#{outfile_basename(input)}.mp4"
|
40
|
+
File.join output_directory, name
|
41
|
+
end
|
42
|
+
|
43
|
+
def filepath_fragmented(input, output_directory)
|
44
|
+
name = "#{outfile_basename(input)}-frag.mp4"
|
45
|
+
File.join output_directory, name
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"Width: #{@width}, Height: #{@height}, Bitrate: #{@bitrate}"
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Abrizer
|
2
|
+
# TODO: AdaptationFinder is incomplete. Basically what we want to do is to
|
3
|
+
# find out the height, width, and aspect ratio of the original and then
|
4
|
+
# determine which adaptations to create. So for a 3:2 video we'll have 4
|
5
|
+
# adaptations but ought to only create ones that are the same size and smaller
|
6
|
+
# than the input file. Adaptations that are larger should not be created.
|
7
|
+
# So first we find the aspect ratio to see the value adaptations we could
|
8
|
+
# apply and then we select the ones that are the same width and smaller.
|
9
|
+
# If there is not an exact match for the
|
10
|
+
# All of the aspect ratios here are given based on real files that have come
|
11
|
+
# through our workflow, so there might be some missing.
|
12
|
+
class AdaptationFinder
|
13
|
+
attr_reader :adaptations, :info
|
14
|
+
def initialize(filename)
|
15
|
+
@filename = filename
|
16
|
+
@informer = Abrizer::FfprobeInformer.new(filename)
|
17
|
+
find_adaptations
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO: analyze the incoming file and determine which preset to use
|
21
|
+
def find_adaptations
|
22
|
+
raw_adaptations = case @informer.display_aspect_ratio
|
23
|
+
when "4:3"
|
24
|
+
ar_4_3_adaptations
|
25
|
+
when "16:9"
|
26
|
+
ar_16_9_adaptations
|
27
|
+
else
|
28
|
+
puts "Unable to find appropriate adaptation set!"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
adaptations = raw_adaptations.map{|adaptation| Abrizer::Adaptation.new(adaptation)}
|
32
|
+
@adaptations = adaptations.select{|adaptation| adaptation.width <= @informer.width}
|
33
|
+
end
|
34
|
+
|
35
|
+
# The bitrates here are based on H.264 encoding.
|
36
|
+
def ar_4_3_adaptations
|
37
|
+
[
|
38
|
+
{width: 224, height: 168, bitrate: 200},
|
39
|
+
{width: 448, height: 336, bitrate: 400},
|
40
|
+
{width: 640, height: 480, bitrate: 800},
|
41
|
+
{width: 720, height: 540, bitrate: 1000},
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
# The bitrates here are based on H.264 encoding.
|
46
|
+
def ar_16_9_adaptations
|
47
|
+
# Average video bitrate from here: https://bitmovin.com/video-bitrate-streaming-hls-dash/
|
48
|
+
[
|
49
|
+
{width: 256, height: 144, bitrate: 200},
|
50
|
+
{width: 512, height: 288, bitrate: 400},
|
51
|
+
{width: 768, height: 432, bitrate: 800},
|
52
|
+
{width: 1024, height: 576, bitrate: 1200},
|
53
|
+
{width: 1280, height: 720, bitrate: 2400},
|
54
|
+
{width: 1920, height: 1080, bitrate: 4800},
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/lib/abrizer/all.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Abrizer
|
2
|
+
class All
|
3
|
+
|
4
|
+
def initialize(filename, output_dir=nil)
|
5
|
+
@filename = filename
|
6
|
+
@output_directory = output_dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
Abrizer::Processor.process(@filename, @output_directory)
|
11
|
+
Abrizer::ProgressiveMp4.new(@filename, @output_directory).create
|
12
|
+
Abrizer::ProgressiveVp9.new(@filename, @output_directory).create
|
13
|
+
Abrizer::PackageDashBento.new(@filename, @output_directory).package
|
14
|
+
Abrizer::PackageHlsBento.new(@filename, @output_directory).package
|
15
|
+
Abrizer::Sprites.new(@filename, @output_directory).create
|
16
|
+
Abrizer::Cleaner.new(@filename, @output_directory).clean
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Abrizer
|
2
|
+
class Cleaner
|
3
|
+
|
4
|
+
include FilepathHelpers
|
5
|
+
|
6
|
+
def initialize(filename, output_dir=nil)
|
7
|
+
@filename = filename
|
8
|
+
@output_directory = output_dir
|
9
|
+
@adaptations = Abrizer::AdaptationFinder.new(@filename).adaptations
|
10
|
+
end
|
11
|
+
|
12
|
+
def clean
|
13
|
+
delete_adaptations(@adaptations)
|
14
|
+
clean_audio_file
|
15
|
+
remove_pass1_log_files
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete_adaptations(adapts)
|
19
|
+
adapts.map do |adaptation|
|
20
|
+
filepath = adaptation.filepath_fragmented(@filename, output_directory)
|
21
|
+
FileUtils.rm filepath if File.exist? filepath
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def clean_audio_file
|
26
|
+
FileUtils.rm audio_filepath_fragmented if File.exist? audio_filepath_fragmented
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove_pass1_log_files
|
30
|
+
glob = File.join output_directory, "ffmpeg2pass*"
|
31
|
+
Dir.glob(glob).each do |log_filepath|
|
32
|
+
FileUtils.rm log_filepath
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/abrizer/cli.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'thor'
|
2
|
+
module Abrizer
|
3
|
+
class CLI < Thor
|
4
|
+
|
5
|
+
desc 'all <filepath> <output_directory>', 'Runn all processes including creating ABR streams, progressive download versions, and images and video sprites'
|
6
|
+
def all(filepath, output_dir=nil)
|
7
|
+
filepath = File.expand_path filepath
|
8
|
+
output_dir = File.expand_path output_dir
|
9
|
+
Abrizer::All.new(filepath, output_dir).run
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'abr <filepath> <output_directory>', 'From file create ABR streams, includes processing MP4 adaptations for packaging'
|
13
|
+
def abr(filepath, output_dir=nil)
|
14
|
+
filepath = File.expand_path filepath
|
15
|
+
output_dir = File.expand_path output_dir
|
16
|
+
Abrizer::Processor.process(filepath, output_dir)
|
17
|
+
Abrizer::PackageDashBento.new(filepath, output_dir).package
|
18
|
+
Abrizer::PackageHlsBento.new(filepath, output_dir).package
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'process <filepath> <output_directory>', 'From mezzanine or preservation file create intermediary adaptations'
|
22
|
+
def process(filepath, output_dir=nil)
|
23
|
+
filepath = File.expand_path filepath
|
24
|
+
output_dir = File.expand_path output_dir
|
25
|
+
Abrizer::Processor.process(filepath, output_dir)
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'mp4 <filepath> <output_directory>', 'Create a single progressive download version as an MP4 from the next to largest adaptation and audio. The adaptation and audio file must already exist.'
|
29
|
+
def mp4(filepath, output_dir=nil)
|
30
|
+
filepath = File.expand_path filepath
|
31
|
+
output_dir = File.expand_path output_dir
|
32
|
+
Abrizer::ProgressiveMp4.new(filepath, output_dir).create
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'vp9 <filepath> <output_directory>', 'Create a single VP9 progressive download version from the original video.'
|
36
|
+
def vp9(filepath, output_dir=nil)
|
37
|
+
filepath = File.expand_path filepath
|
38
|
+
output_dir = File.expand_path output_dir
|
39
|
+
Abrizer::ProgressiveVp9.new(filepath, output_dir).create
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'adaptations <filepath>', 'Display which adaptations will be created from input file'
|
43
|
+
def adaptations(filepath)
|
44
|
+
adaptations = Abrizer::AdaptationFinder.new(filepath).adaptations
|
45
|
+
puts adaptations
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'inform <filepath>', 'Display information about the video/audio file'
|
49
|
+
def inform(filepath)
|
50
|
+
informer = FfprobeInformer.new(filepath)
|
51
|
+
puts informer.json_result
|
52
|
+
puts informer
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'package <dash_or_hls> <filepath> <output_directory>', "Package dash or hls from adaptations"
|
56
|
+
def package(dash_or_hls, filepath, output_dir=nil)
|
57
|
+
filepath = File.expand_path filepath
|
58
|
+
output_dir = File.expand_path output_dir
|
59
|
+
case dash_or_hls
|
60
|
+
when "dash"
|
61
|
+
Abrizer::PackageDashBento.new(filepath, output_dir).package
|
62
|
+
when "hls"
|
63
|
+
Abrizer::PackageHlsBento.new(filepath, output_dir).package
|
64
|
+
when "all"
|
65
|
+
Abrizer::PackageDashBento.new(filepath, output_dir).package
|
66
|
+
Abrizer::PackageHlsBento.new(filepath, output_dir).package
|
67
|
+
else
|
68
|
+
puts "Not a valid packaging value. Try dash or hls."
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
desc 'sprites <filepath> <output_directory>', 'Create image sprites and metadata WebVTT file'
|
73
|
+
def sprites(filepath, output_dir=nil)
|
74
|
+
filepath = File.expand_path filepath
|
75
|
+
output_dir = File.expand_path output_dir
|
76
|
+
Abrizer::Sprites.new(filepath, output_dir).create
|
77
|
+
end
|
78
|
+
|
79
|
+
desc 'clean <filepath> <output_directory>', 'Clean up intermediary files'
|
80
|
+
def clean(filepath, output_dir=nil)
|
81
|
+
Abrizer::Cleaner.new(filepath, output_dir).clean
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Abrizer
|
2
|
+
class FfmpegProcessor
|
3
|
+
|
4
|
+
include FilepathHelpers
|
5
|
+
|
6
|
+
def initialize(filename, output_dir=nil)
|
7
|
+
@filename = filename
|
8
|
+
@output_directory = output_dir
|
9
|
+
@adaptation_finder = Abrizer::AdaptationFinder.new(@filename)
|
10
|
+
end
|
11
|
+
|
12
|
+
def process
|
13
|
+
make_directory
|
14
|
+
Dir.chdir output_directory
|
15
|
+
process_first_pass
|
16
|
+
process_second_passes
|
17
|
+
process_audio
|
18
|
+
end
|
19
|
+
|
20
|
+
def make_directory
|
21
|
+
FileUtils.mkdir output_directory unless File.exist? output_directory
|
22
|
+
end
|
23
|
+
|
24
|
+
def first_pass_adaptation
|
25
|
+
adaptations = Abrizer::AdaptationFinder.new(@filename).adaptations
|
26
|
+
sorted = adaptations.sort_by do |adaptation|
|
27
|
+
adaptation.width
|
28
|
+
end
|
29
|
+
sorted[-2]
|
30
|
+
end
|
31
|
+
|
32
|
+
def first_pass_cmd
|
33
|
+
first_pass_adaptation.ffmpeg_cmd(@filename, output_directory, 1)
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_first_pass
|
37
|
+
puts first_pass_cmd
|
38
|
+
`#{first_pass_cmd}`
|
39
|
+
end
|
40
|
+
|
41
|
+
# Creates a file per adaptation based on aspect ratio and resolution
|
42
|
+
def process_second_passes
|
43
|
+
@adaptation_finder.adaptations.each do |adaptation|
|
44
|
+
cmd = adaptation.ffmpeg_cmd(@filename, output_directory, 2)
|
45
|
+
puts cmd
|
46
|
+
`#{cmd}`
|
47
|
+
`mp4fragment #{adaptation.filepath(@filename, output_directory)} #{adaptation.filepath_fragmented(@filename, output_directory)}`
|
48
|
+
FileUtils.rm adaptation.filepath(@filename, output_directory)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_audio
|
53
|
+
`ffmpeg -y -i #{@filename} -c:a libfdk_aac -b:a 128k -vn #{audio_filepath}`
|
54
|
+
`mp4fragment #{audio_filepath} #{audio_filepath_fragmented}`
|
55
|
+
FileUtils.rm audio_filepath
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Abrizer
|
2
|
+
class FfprobeInformer
|
3
|
+
attr_reader :json_result, :info
|
4
|
+
def initialize(filename)
|
5
|
+
@filename = filename
|
6
|
+
get_info
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_info
|
10
|
+
@json_result = `#{ffmpeg_info_cmd}`
|
11
|
+
@info = JSON.parse @json_result
|
12
|
+
end
|
13
|
+
|
14
|
+
def width
|
15
|
+
video_stream['width'] if video_stream
|
16
|
+
end
|
17
|
+
|
18
|
+
def height
|
19
|
+
video_stream['height'] if video_stream
|
20
|
+
end
|
21
|
+
|
22
|
+
def display_aspect_ratio #dar
|
23
|
+
dar = video_stream['display_aspect_ratio']
|
24
|
+
sar = video_stream['sample_aspect_ratio']
|
25
|
+
if dar == "0:1" #&& sar == "0:1"
|
26
|
+
calculate_aspect_ratio_from_wh
|
27
|
+
else
|
28
|
+
dar
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def calculate_aspect_ratio_from_wh
|
33
|
+
new_width = width
|
34
|
+
new_height = height
|
35
|
+
w = width
|
36
|
+
h = height
|
37
|
+
while h != 0
|
38
|
+
rem = w % h
|
39
|
+
w = h
|
40
|
+
h = rem
|
41
|
+
end
|
42
|
+
new_height = new_height / w
|
43
|
+
new_width = new_width / w
|
44
|
+
"#{new_width}:#{new_height}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def video_stream
|
48
|
+
@info['streams'].find do |stream|
|
49
|
+
stream['codec_type'] == 'video'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def audio_stream
|
54
|
+
@info['streams'].find do |stream|
|
55
|
+
stream['codec_type'] == 'audio'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def ffmpeg_info_cmd
|
60
|
+
"ffprobe -v error -print_format json -show_format -show_streams #{@filename}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
ffmpeg_info_cmd + "\n" +
|
65
|
+
"#{width}x#{height} DAR:#{display_aspect_ratio}"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|