libis-format 0.9.38 → 0.9.39
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +13 -13
- data/data/types.yml +85 -9
- data/lib/libis/format.rb +1 -0
- data/lib/libis/format/config.rb +2 -0
- data/lib/libis/format/converter/audio_converter.rb +181 -0
- data/lib/libis/format/converter/video_converter.rb +194 -0
- data/lib/libis/format/ffmpeg.rb +41 -0
- data/lib/libis/format/identifier.rb +1 -0
- data/lib/libis/format/pdf_to_pdfa.rb +1 -3
- data/lib/libis/format/pdfa_validator.rb +1 -1
- data/lib/libis/format/version.rb +1 -1
- data/libis-format.gemspec +1 -0
- data/spec/converter_spec.rb +116 -20
- data/spec/data/audio/carlin_disappointed.aac +0 -0
- data/spec/data/audio/carlin_disappointed.ac3 +0 -0
- data/spec/data/audio/carlin_disappointed.aiff +0 -0
- data/spec/data/audio/carlin_disappointed.au +0 -0
- data/spec/data/audio/carlin_disappointed.flac +0 -0
- data/spec/data/audio/carlin_disappointed.m4a +0 -0
- data/spec/data/audio/carlin_disappointed.mka +0 -0
- data/spec/data/audio/carlin_disappointed.mp3 +0 -0
- data/spec/data/audio/carlin_disappointed.ogg +0 -0
- data/spec/data/audio/carlin_disappointed.ra +0 -0
- data/spec/data/audio/carlin_disappointed.voc +0 -0
- data/spec/data/audio/carlin_disappointed.wav +0 -0
- data/spec/data/audio/carlin_disappointed.wma +0 -0
- data/spec/data/audio/greensleeves.aac +0 -0
- data/spec/data/audio/greensleeves.ac3 +0 -0
- data/spec/data/audio/greensleeves.aiff +0 -0
- data/spec/data/audio/greensleeves.au +0 -0
- data/spec/data/audio/greensleeves.flac +0 -0
- data/spec/data/audio/greensleeves.m4a +0 -0
- data/spec/data/audio/greensleeves.mka +0 -0
- data/spec/data/audio/greensleeves.mp3 +0 -0
- data/spec/data/audio/greensleeves.ogg +0 -0
- data/spec/data/audio/greensleeves.ra +0 -0
- data/spec/data/audio/greensleeves.voc +0 -0
- data/spec/data/audio/greensleeves.wav +0 -0
- data/spec/data/audio/greensleeves.wma +0 -0
- data/spec/data/audio/king_nonviolence.aac +0 -0
- data/spec/data/audio/king_nonviolence.ac3 +0 -0
- data/spec/data/audio/king_nonviolence.aiff +0 -0
- data/spec/data/audio/king_nonviolence.au +0 -0
- data/spec/data/audio/king_nonviolence.flac +0 -0
- data/spec/data/audio/king_nonviolence.m4a +0 -0
- data/spec/data/audio/king_nonviolence.mka +0 -0
- data/spec/data/audio/king_nonviolence.mp3 +0 -0
- data/spec/data/audio/king_nonviolence.ogg +0 -0
- data/spec/data/audio/king_nonviolence.ra +0 -0
- data/spec/data/audio/king_nonviolence.voc +0 -0
- data/spec/data/audio/king_nonviolence.wav +0 -0
- data/spec/data/audio/king_nonviolence.wma +0 -0
- data/spec/data/audio/top_gun_secret.aac +0 -0
- data/spec/data/audio/top_gun_secret.ac3 +0 -0
- data/spec/data/audio/top_gun_secret.aiff +0 -0
- data/spec/data/audio/top_gun_secret.au +0 -0
- data/spec/data/audio/top_gun_secret.flac +0 -0
- data/spec/data/audio/top_gun_secret.m4a +0 -0
- data/spec/data/audio/top_gun_secret.mka +0 -0
- data/spec/data/audio/top_gun_secret.mp3 +0 -0
- data/spec/data/audio/top_gun_secret.ogg +0 -0
- data/spec/data/audio/top_gun_secret.ra +0 -0
- data/spec/data/audio/top_gun_secret.voc +0 -0
- data/spec/data/audio/top_gun_secret.wav +0 -0
- data/spec/data/audio/top_gun_secret.wma +0 -0
- data/spec/data/video/SampleVideo_1080x720_2mb.flv +0 -0
- data/spec/data/video/SampleVideo_1080x720_2mb.mkv +0 -0
- data/spec/data/video/SampleVideo_1080x720_2mb.mp4 +0 -0
- data/spec/data/video/SampleVideo_176x144_2mb.3gp +0 -0
- data/spec/data/video/SampleVideo_320x240_2mb.3gp +0 -0
- data/spec/data/video/SampleVideo_360x240_2mb.flv +0 -0
- data/spec/data/video/SampleVideo_360x240_2mb.mkv +0 -0
- data/spec/data/video/SampleVideo_360x240_2mb.mp4 +0 -0
- data/spec/data/video/sample.avi +0 -0
- data/spec/data/video/sample.flv +0 -0
- data/spec/data/video/sample.mkv +0 -0
- data/spec/data/video/sample.mov +0 -0
- data/spec/data/video/sample.mp4 +0 -0
- data/spec/data/video/sample.mpg +0 -0
- data/spec/data/video/sample.swf +0 -0
- data/spec/data/video/sample.webm +0 -0
- data/spec/data/video/sample.wmv +0 -0
- data/spec/identifier_spec.rb +29 -39
- data/spec/spec_helper.rb +50 -1
- metadata +157 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d41852f4f00f61ffa6458f3ba0f9ce58215573d8
|
4
|
+
data.tar.gz: 96158848af69ff1ad5ffafc198d6f04b30a94f9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7db9b6c77b4429fa91ec099d8a10c55799050e6ca345b1af0f2273c5645841b2edb33b5be9cfe2967eedf52d0438e6bbe92e0ad499f2be3460ec8e53d7e076b1
|
7
|
+
data.tar.gz: 6399b4b84f80b7e48c2c85c3390e34fe39b70c8ea1c4a096dfd33981bbcc2eca2ba88f8be9e4738ff424f0e12446f5b898b1db248206ebd080fff098c585af6b
|
data/.travis.yml
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
|
+
dist: trusty
|
3
4
|
cache: bundler
|
4
5
|
rvm:
|
5
6
|
- 2.1.0
|
@@ -7,31 +8,25 @@ rvm:
|
|
7
8
|
- ruby-head
|
8
9
|
- jruby-9.0.1.0
|
9
10
|
jdk:
|
10
|
-
-
|
11
|
-
- oraclejdk7
|
11
|
+
- openjdk8
|
12
12
|
- oraclejdk8
|
13
13
|
matrix:
|
14
14
|
exclude:
|
15
|
-
- rvm: 2.1.0
|
16
|
-
jdk: oraclejdk7
|
17
15
|
- rvm: 2.1.0
|
18
16
|
jdk: oraclejdk8
|
19
|
-
- rvm: 2.2.0
|
20
|
-
jdk: oraclejdk7
|
21
17
|
- rvm: 2.2.0
|
22
18
|
jdk: oraclejdk8
|
23
|
-
- rvm: ruby-head
|
24
|
-
jdk: oraclejdk7
|
25
19
|
- rvm: ruby-head
|
26
20
|
jdk: oraclejdk8
|
27
|
-
allow_failures:
|
28
|
-
- rvm: 2.1.0
|
29
|
-
- rvm: 2.2.0
|
30
|
-
- rvm: ruby-head
|
31
|
-
- rvm: jruby-9.0.1.0
|
32
21
|
branches:
|
33
22
|
only:
|
34
23
|
- master
|
24
|
+
before_install:
|
25
|
+
- sudo add-apt-repository ppa:mc3man/trusty-media -y
|
26
|
+
- sudo apt-get update -y
|
27
|
+
- sudo apt-get install -y ffmpeg gstreamer0.10-ffmpeg
|
28
|
+
- sudo pip install opf-fido --upgrade --ignore-installed six
|
29
|
+
|
35
30
|
addons:
|
36
31
|
apt:
|
37
32
|
packages:
|
@@ -39,3 +34,8 @@ addons:
|
|
39
34
|
- imagemagick
|
40
35
|
- libreoffice
|
41
36
|
- ghostscript
|
37
|
+
- sox
|
38
|
+
- libsox-dev
|
39
|
+
- libsox-fmt-all
|
40
|
+
- libchromaprint-dev
|
41
|
+
- libchromaprint-tools
|
data/data/types.yml
CHANGED
@@ -70,66 +70,142 @@ IMAGE:
|
|
70
70
|
|
71
71
|
AUDIO:
|
72
72
|
|
73
|
-
WAV:
|
74
|
-
NAME: Waveform Audio File Format (WAVE)
|
75
|
-
MIME: audio/x-wav
|
76
|
-
EXTENSIONS: wav
|
77
|
-
|
78
73
|
MP3:
|
79
74
|
NAME: MPEG-1 or MPEG-2 Audio Layer III (MP3)
|
80
75
|
MIME: audio/mpeg
|
76
|
+
PUID: fmt/134
|
81
77
|
EXTENSIONS: mp3
|
82
78
|
|
83
79
|
FLAC:
|
84
80
|
NAME: Free Lossless Audio Codec (FLAC)
|
85
81
|
MIME: audio/flac
|
82
|
+
PUID: fmt/279
|
86
83
|
EXTENSIONS: flac
|
87
84
|
|
85
|
+
AC3:
|
86
|
+
NAME: Dolby Digital AC-3
|
87
|
+
MIME: audio/ac3
|
88
|
+
PUID: fmt/735
|
89
|
+
EXTENSIONS: ac3
|
90
|
+
|
91
|
+
AAC:
|
92
|
+
NAME: Advanced Audio Coding (AAC)
|
93
|
+
MIME: audio/aac
|
94
|
+
EXTENSIONS: aac
|
95
|
+
|
96
|
+
WMA:
|
97
|
+
NAME: Windows Media Audio (WMA)
|
98
|
+
MIME: audio/x-ms-wma
|
99
|
+
PUID: fmt/132
|
100
|
+
EXTENSIONS: wma
|
101
|
+
|
102
|
+
ALAC:
|
103
|
+
NAME: Apple Lossless Audio Codec (ALAC)
|
104
|
+
MIME: audio/alac
|
105
|
+
EXTENSIONS: alac
|
106
|
+
|
107
|
+
WAV:
|
108
|
+
NAME: Waveform Audio File Format (WAVE)
|
109
|
+
MIME: audio/x-wav
|
110
|
+
PUID: fmt/6, fmt/141, fmt/142, fmt/143, fmt/1, fmt/703, fmt/706, fmt/709, fmt/2, fmt/704, fmt/707, fmt/710, fmt/527, fmt/705, fmt/708, fmt/711, x-fmt/397, x-fmt/389, x-fmt/396, fmt/712, fmt/713
|
111
|
+
EXTENSIONS: wav
|
112
|
+
|
113
|
+
AIFF:
|
114
|
+
NAME: Audio Interchange File Format (AIFF)
|
115
|
+
MIME: audio/aiff
|
116
|
+
PUID: fmt/414, x-fmt/135, x-fmt/136
|
117
|
+
EXTENSIONS: aif aiff aifc
|
118
|
+
|
119
|
+
AMR:
|
120
|
+
NAME: Adaptive Multi-Rate Audio (AMR)
|
121
|
+
MIME: audio/amr
|
122
|
+
PUID: fmt/954
|
123
|
+
EXTENSIONS: amr
|
124
|
+
|
125
|
+
AU:
|
126
|
+
NAME: NeXT/Sun sound (AU)
|
127
|
+
MIME: audio/basic
|
128
|
+
PUID: x-fmt/139
|
129
|
+
EXTENSIONS: au
|
130
|
+
|
131
|
+
M4A:
|
132
|
+
NAME: MPEG-4 Media File (M4A)
|
133
|
+
MIME: audio/mp4
|
134
|
+
PUID: fmt/199, fmt/596
|
135
|
+
EXTENSIONS: m4a f4a
|
136
|
+
|
88
137
|
VIDEO:
|
89
138
|
|
90
|
-
|
139
|
+
MPG:
|
91
140
|
NAME: Moving Picture Experts Group (MPEG-1/MPEG-2)
|
92
141
|
MIME: video/mpeg
|
142
|
+
PUID: x-fmt/385 x-fmt/386 fmt/425 fmt/649 fmt/640
|
93
143
|
EXTENSIONS: mpg mpeg mp1 mp2 mp3 m1v m1a m2a mpa mpv
|
94
144
|
|
95
|
-
|
145
|
+
MP4:
|
96
146
|
NAME: Moving Picture Experts Group (MPEG-4)
|
97
|
-
MIME: video/mp4
|
98
|
-
|
147
|
+
MIME: video/mp4, application/mp4
|
148
|
+
PUID: fmt/199
|
149
|
+
EXTENSIONS: mp4,m4v,mpeg4
|
150
|
+
|
151
|
+
WEBM:
|
152
|
+
NAME: Webm
|
153
|
+
PUID: fmt/573
|
154
|
+
MIME: video/webm
|
155
|
+
EXTENSIONS: webm
|
156
|
+
|
157
|
+
FLASH:
|
158
|
+
NAME: Macromedia Flash
|
159
|
+
MIME: application/x-shockwave-flash
|
160
|
+
PUID: fmt/104 fmt/105 fmt/106 fmt/107 fmt/108 fmt/109 fmt/110
|
161
|
+
EXTENSIONS: swf
|
162
|
+
|
163
|
+
MKV:
|
164
|
+
NAME: Matroska Multimedia Container (MKV)
|
165
|
+
MIME: video/x-matroska
|
166
|
+
PUID: fmt/569
|
167
|
+
EXTENSIONS: mkv, mk3d, mka, mks
|
99
168
|
|
100
169
|
MJP2:
|
101
170
|
NAME: Motion JPEG 2000 (MJP2)
|
102
171
|
MIME: video/mj2
|
172
|
+
PUID: fmt/337
|
103
173
|
EXTENSIONS: mj2, mjp2
|
104
174
|
|
105
175
|
QTFF:
|
106
176
|
NAME: QuickTime File Format (QTFF)
|
107
177
|
MIME: video/quicktime
|
178
|
+
PUID: x-fmt/384
|
108
179
|
EXTENSIONS: mov, qt
|
109
180
|
|
110
181
|
AVI:
|
111
182
|
NAME: Audio Video Interleave (AVI)
|
112
183
|
MIME: video/x-msvideo
|
184
|
+
PUID: fmt/5
|
113
185
|
EXTENSIONS: avi
|
114
186
|
|
115
187
|
OGGV:
|
116
188
|
NAME: OGG Video (OGGV)
|
117
189
|
MIME: video/ogg
|
190
|
+
PUID: fmt/945
|
118
191
|
EXTENSIONS: ogv
|
119
192
|
|
120
193
|
WMV:
|
121
194
|
NAME: Windows Media Video (WMV)
|
122
195
|
MIME: video/x-ms-wmv
|
196
|
+
PUID: fmt/133 fmt/441
|
123
197
|
EXTENSIONS: wmv
|
124
198
|
|
125
199
|
DV:
|
126
200
|
NAME: Digital Video (DV)
|
127
201
|
MIME: video/dv
|
202
|
+
PUID: x-fmt/152
|
128
203
|
EXTENSIONS: dv
|
129
204
|
|
130
205
|
FLASH:
|
131
206
|
NAME: Flash video (FLV)
|
132
207
|
MIME: video/x-flv
|
208
|
+
PUID: x-fmt/382
|
133
209
|
EXTENSIONS: flv
|
134
210
|
|
135
211
|
TEXT:
|
data/lib/libis/format.rb
CHANGED
@@ -19,6 +19,7 @@ module Libis
|
|
19
19
|
autoload :PdfSplit, 'libis/format/pdf_split'
|
20
20
|
autoload :PdfToPdfa, 'libis/format/pdf_to_pdfa'
|
21
21
|
autoload :PdfaValidator, 'libis/format/pdfa_validator'
|
22
|
+
autoload :FFMpeg, 'libis/format/ffmpeg'
|
22
23
|
|
23
24
|
autoload :Converter, 'libis/format/converter'
|
24
25
|
|
data/lib/libis/format/config.rb
CHANGED
@@ -15,11 +15,13 @@ module Libis
|
|
15
15
|
Config[:ghostscript_path] = 'gs'
|
16
16
|
Config[:droid_path] = '/opt/droid/droid.sh'
|
17
17
|
Config[:fido_path] = '/usr/local/bin/fido'
|
18
|
+
Config[:ffmpeg_path] = 'ffmpeg'
|
18
19
|
data_dir = File.absolute_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'data'))
|
19
20
|
Config[:fido_formats] = [(File.join(data_dir, 'lias_formats.xml'))]
|
20
21
|
# noinspection RubyStringKeysInHashInspection
|
21
22
|
Config[:xml_validations] = [['archive/ead', File.join(data_dir, 'ead.xsd')]]
|
22
23
|
Config[:type_database] = File.join(data_dir, 'types.yml')
|
24
|
+
Config[:raw_audio_convert_cmd] = 'sox %s -e signed -b 16 -t wav %s rate %d channels %d'
|
23
25
|
|
24
26
|
end
|
25
27
|
end
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require 'libis/format/identifier'
|
3
|
+
require 'chromaprint'
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Libis
|
8
|
+
module Format
|
9
|
+
module Converter
|
10
|
+
|
11
|
+
class AudioConverter < Libis::Format::Converter::Base
|
12
|
+
|
13
|
+
def self.input_types
|
14
|
+
[:MP3, :FLAC, :AC3, :AAC, :WMA, :ALAC, :WAV, :AIFF, :AMR, :AU, :M4A]
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.output_types(format = nil)
|
18
|
+
return [] unless input_types.include?(format)
|
19
|
+
[:MP3, :FLAC, :AC3, :AAC, :WMA, :ALAC, :WAV, :AIFF, :AMR, :AU, :M4A]
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
def quiet(v)
|
27
|
+
@flags[:quiet] = !!v
|
28
|
+
end
|
29
|
+
|
30
|
+
def format(format)
|
31
|
+
@options[:format] = format
|
32
|
+
end
|
33
|
+
|
34
|
+
def codec(codec)
|
35
|
+
@options[:codec] = codec
|
36
|
+
end
|
37
|
+
|
38
|
+
def start(seconds)
|
39
|
+
@options[:start] = seconds.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def duration(seconds)
|
43
|
+
@options[:duration] = seconds.to_s
|
44
|
+
end
|
45
|
+
|
46
|
+
def quality(value)
|
47
|
+
@options[:quality] = value.to_s
|
48
|
+
end
|
49
|
+
|
50
|
+
def bit_rate(value)
|
51
|
+
@options[:bit_rate] = value.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def sampling_freq(value)
|
55
|
+
@options[:sampling_freq] = value.to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
def channels(value)
|
59
|
+
@options[:channels] = value.to_s
|
60
|
+
end
|
61
|
+
|
62
|
+
def web_stream(value)
|
63
|
+
if value
|
64
|
+
@options[:codec] = 'mp3'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def preset(stream, name)
|
69
|
+
(@options[:preset] ||= {})[stream] = name
|
70
|
+
end
|
71
|
+
|
72
|
+
# def encoder(value)
|
73
|
+
# @options[:encoder] = value
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# def encoder_options(value)
|
77
|
+
# @options[:encoder_options] = value
|
78
|
+
# end
|
79
|
+
|
80
|
+
def convert(source, target, _format, opts = {})
|
81
|
+
super
|
82
|
+
|
83
|
+
FileUtils.mkpath(File.dirname(target))
|
84
|
+
|
85
|
+
if source.is_a? Array
|
86
|
+
|
87
|
+
assemble_and_convert(source, target)
|
88
|
+
|
89
|
+
elsif File.directory?(source)
|
90
|
+
|
91
|
+
sources = Dir[File.join(source, '**', '*')].reject {|p| File.directory? p}
|
92
|
+
assemble_and_convert(sources, target)
|
93
|
+
|
94
|
+
else
|
95
|
+
|
96
|
+
convert_file(source, target)
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
target
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def assemble_and_convert(sources, target)
|
105
|
+
Tempfile.create(%w(list .txt)) do |f|
|
106
|
+
sources.each {|src| f.puts src}
|
107
|
+
opts[:global] ||= []
|
108
|
+
opts[:global] += %w(-f concat)
|
109
|
+
f.close
|
110
|
+
target = convert_file(f.to_path, target)
|
111
|
+
end
|
112
|
+
target
|
113
|
+
end
|
114
|
+
|
115
|
+
def sounds_like(file1, file2, threshold, rate, channels)
|
116
|
+
rate ||= 96000
|
117
|
+
channels ||= 2
|
118
|
+
threshold ||= 0.85
|
119
|
+
|
120
|
+
if File.exists?(file1) && File.exists?(file2)
|
121
|
+
# Convert input files into raw 16-bit signed audio (WAV) to process with Chramaprint
|
122
|
+
file1_raw = File.join('', 'tmp', File.basename(file1) + '.wav')
|
123
|
+
file2_raw = File.join('', 'tmp', File.basename(file2) + '.wav')
|
124
|
+
FileUtils.rm(file1_raw, force: true)
|
125
|
+
FileUtils.rm(file2_raw, force: true)
|
126
|
+
cvt_cmd = Libis::Format::Config[:raw_audio_convert_cmd]
|
127
|
+
%x"#{cvt_cmd % [file1, file1_raw, rate, channels]}"
|
128
|
+
%x"#{cvt_cmd % [file2, file2_raw, rate, channels]}"
|
129
|
+
file1_audio = File.binread(file1_raw)
|
130
|
+
file2_audio = File.binread(file2_raw)
|
131
|
+
|
132
|
+
# Get audio fingerprints
|
133
|
+
chromaprint = Chromaprint::Context.new(rate, channels, Chromaprint::ALGORITHM_TEST3)
|
134
|
+
file1_fp = chromaprint.get_fingerprint(file1_audio)
|
135
|
+
file2_fp = chromaprint.get_fingerprint(file2_audio)
|
136
|
+
|
137
|
+
# Cleanup files
|
138
|
+
FileUtils.rm(file1_raw, force: true)
|
139
|
+
FileUtils.rm(file2_raw, force: true)
|
140
|
+
|
141
|
+
# Compare fingerprints and compare result against threshold
|
142
|
+
cmp = file1_fp.compare(file2_fp)
|
143
|
+
# puts "Threshold[#{File.basename(exp_file)},#{File.basename(tgt_file)}: #{cmp}"
|
144
|
+
cmp > threshold
|
145
|
+
else
|
146
|
+
false
|
147
|
+
end
|
148
|
+
|
149
|
+
rescue Exception => e
|
150
|
+
error "Error comparing sound file #{file1} and #{file2}: #{e.message} @ #{e.backtrace.first}"
|
151
|
+
false
|
152
|
+
end
|
153
|
+
|
154
|
+
protected
|
155
|
+
|
156
|
+
def convert_file(source, target)
|
157
|
+
opts = {global: [], input: [], filter: [], output: []}
|
158
|
+
opts[:global] << '-hide_banner'
|
159
|
+
opts[:global] << '-loglevel' << (@options[:quiet] ? 'fatal' : 'warning')
|
160
|
+
opts[:output] << '-vn' # disable input video stream in case it exists
|
161
|
+
opts[:output] << '-codec:a' << @options[:codec] if @options[:codec]
|
162
|
+
opts[:output] << '-map_metadata:g' << '0:g' # Copy global metadata
|
163
|
+
opts[:output] << '-map_metadata:s:a' << '0:s:a' # Copy audio metadata
|
164
|
+
opts[:input] << '-accurate_seek' << (@options[:start].to_i < 0 ? '-sseof' : '-ss') << @options[:start] if @options[:start]
|
165
|
+
opts[:input] << '-t' << @options[:duration] if @options[:duration]
|
166
|
+
opts[:output] << '-q:a' << @options[:quality] if @options[:quality]
|
167
|
+
opts[:output] << '-b:a' << @options[:bit_rate] if @options[:bit_rate]
|
168
|
+
opts[:output] << '-ar' << @options[:sampling_freq] if @options[:sampling_freq]
|
169
|
+
opts[:output] << '-ac' << @options[:channels] if @options[:channels]
|
170
|
+
opts[:output] << '-f' << @options[:format] if @options[:format]
|
171
|
+
result = Libis::Format::FFMpeg.run(source, target, opts)
|
172
|
+
info "FFMpeg output: #{result}"
|
173
|
+
result
|
174
|
+
target
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require 'libis/format/ffmpeg'
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Libis
|
7
|
+
module Format
|
8
|
+
module Converter
|
9
|
+
|
10
|
+
class VideoConverter < Libis::Format::Converter::Base
|
11
|
+
|
12
|
+
def self.input_types
|
13
|
+
[:WEBM, :M4V, :MPEG, :MJP2, :QTFF, :AVI, :OGGV, :WMV, :DV, :FLASH]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.output_types(format = nil)
|
17
|
+
return [] unless input_types.include?(format)
|
18
|
+
[:GIF, :AVI, :FLV, :MKV, :MOV, :MP4, :MPG, :OGGV, :SWF, :WEBM, :WMV]
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
25
|
+
def quiet(v)
|
26
|
+
@flags[:quiet] = !!v
|
27
|
+
end
|
28
|
+
|
29
|
+
def format(format)
|
30
|
+
@options[:format] = format
|
31
|
+
end
|
32
|
+
|
33
|
+
def audio_channels(value)
|
34
|
+
@options[:audio_channels] = value.to_s
|
35
|
+
end
|
36
|
+
|
37
|
+
def audio_codec(codec)
|
38
|
+
@options[:audio_codec] = codec
|
39
|
+
end
|
40
|
+
|
41
|
+
def video_codec(codec)
|
42
|
+
@options[:video_codec] = codec
|
43
|
+
end
|
44
|
+
|
45
|
+
def start(seconds)
|
46
|
+
@options[:start] = seconds.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def duration(seconds)
|
50
|
+
@options[:duration] = seconds.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def audio_quality(value)
|
54
|
+
@options[:audio_quality] = value.to_s
|
55
|
+
end
|
56
|
+
|
57
|
+
def video_quality(value)
|
58
|
+
@options[:video_quality] = value.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def audio_bitrate(value)
|
62
|
+
@options[:audio_bitrate] = value.to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
def video_bitrate(value)
|
66
|
+
@options[:video_bitrate] = value.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def constant_rate_factor(value)
|
70
|
+
@options[:crf] = value.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
def frame_rate(value)
|
74
|
+
@options[:frame_rate] = value.to_s
|
75
|
+
end
|
76
|
+
|
77
|
+
def sampling_freq(value)
|
78
|
+
@options[:sampling_freq] = value.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
def scale(width_x_height)
|
82
|
+
@options[:scale] = width_x_height
|
83
|
+
end
|
84
|
+
|
85
|
+
def web_stream(value)
|
86
|
+
if value
|
87
|
+
@options[:video_codec] = 'h264'
|
88
|
+
@options[:audio_codec] = 'acc'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def preset(name)
|
93
|
+
@options[:preset] = name
|
94
|
+
end
|
95
|
+
|
96
|
+
def audio_preset(name)
|
97
|
+
@options[:audio_preset] = name
|
98
|
+
end
|
99
|
+
|
100
|
+
def video_preset(name)
|
101
|
+
@options[:video_preset] = name
|
102
|
+
end
|
103
|
+
|
104
|
+
# def video_encoder(value)
|
105
|
+
# @options[:video_encoder] = value
|
106
|
+
# end
|
107
|
+
#
|
108
|
+
# def audio_encoder(value)
|
109
|
+
# @options[:audio_encoder] = value
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# def encoder_options(value)
|
113
|
+
# @options[:encoder_options] = value
|
114
|
+
# end
|
115
|
+
|
116
|
+
def convert(source, target, _format, opts = {})
|
117
|
+
super
|
118
|
+
|
119
|
+
FileUtils.mkpath(File.dirname(target))
|
120
|
+
|
121
|
+
if source.is_a? Array
|
122
|
+
|
123
|
+
assemble_and_convert(source, target)
|
124
|
+
|
125
|
+
elsif File.directory?(source)
|
126
|
+
|
127
|
+
sources = Dir[File.join(source, '**', '*')].reject {|p| File.directory? p}
|
128
|
+
assemble_and_convert(sources, target)
|
129
|
+
|
130
|
+
else
|
131
|
+
|
132
|
+
convert_file(source, target)
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
target
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
def assemble_and_convert(sources, target)
|
141
|
+
Tempfile.create(%w(list .txt)) do |f|
|
142
|
+
sources.each {|src| f.puts src}
|
143
|
+
opts[:global] ||= []
|
144
|
+
opts[:global] += %w(-f concat)
|
145
|
+
f.close
|
146
|
+
target = convert_file(f.to_path, target)
|
147
|
+
end
|
148
|
+
target
|
149
|
+
end
|
150
|
+
|
151
|
+
protected
|
152
|
+
|
153
|
+
def convert_file(source, target)
|
154
|
+
# FLV special: only supports aac and speex audio codecs
|
155
|
+
format = (@options[:format] || File.extname(target)[1..-1]).to_s.downcase
|
156
|
+
@options[:audio_codec] ||= 'aac' if %w'flv'.include?(format)
|
157
|
+
|
158
|
+
# FLV special: only supports aac and speex audio codecs
|
159
|
+
format = (@options[:format] || File.extname(target)[1..-1]).to_s.downcase
|
160
|
+
@options[:audio_codec] ||= 'mp3' if %w'swf'.include?(format)
|
161
|
+
|
162
|
+
# Set up FFMpeg command line parameters
|
163
|
+
opts = {global: [], input: [], filter: [], output: []}
|
164
|
+
opts[:global] << '-hide_banner'
|
165
|
+
opts[:global] << '-loglevel' << (@options[:quiet] ? 'fatal' : 'warning')
|
166
|
+
opts[:output] << '-ac' << @options[:audio_channels] if @options[:audio_channels]
|
167
|
+
opts[:output] << '-c:a' << @options[:audio_codec] if @options[:audio_codec]
|
168
|
+
opts[:output] << '-c:v' << @options[:video_codec] if @options[:video_codec]
|
169
|
+
opts[:output] << '-b:a' << @options[:audio_bitrate] if @options[:audio_bitrate]
|
170
|
+
opts[:output] << '-b:v' << @options[:video_bitrate] if @options[:video_bitrate]
|
171
|
+
opts[:output] << '-crf' << @options[:crf] if @options[:crf]
|
172
|
+
opts[:output] << '-map_metadata:g' << '0:g' # Copy global metadata
|
173
|
+
opts[:output] << '-map_metadata:s:a' << '0:s:a' # Copy audio metadata
|
174
|
+
opts[:output] << '-map_metadata:s:v' << '0:s:v' # Copy video metadata
|
175
|
+
opts[:input] << '-accurate_seek' << (@options[:start].to_i < 0 ? '-sseof' : '-ss') << @options[:start] if @options[:start]
|
176
|
+
opts[:input] << '-t' << @options[:duration] if @options[:duration]
|
177
|
+
opts[:output] << '-qscale' << @options[:video_quality] if @options[:video_quality]
|
178
|
+
opts[:output] << '-q:a' << @options[:audio_quality] if @options[:audio_quality]
|
179
|
+
opts[:output] << '-r' << @options[:frame_rate] if @options[:frame_rate]
|
180
|
+
opts[:output] << '-ar' << @options[:sampling_freq] if @options[:sampling_freq]
|
181
|
+
opts[:output] << '-f' << @options[:format] if @options[:format]
|
182
|
+
opts[:output] << '-pre' << @options[:preset] if @options[:preset]
|
183
|
+
opts[:output] << '-apre' << @options[:audio_preset] if @options[:audio_preset]
|
184
|
+
opts[:output] << '-vpre' << @options[:video_preset] if @options[:video_preset]
|
185
|
+
result = Libis::Format::FFMpeg.run(source, target, opts)
|
186
|
+
info "FFMpeg output: #{result}"
|
187
|
+
target
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|