puremotion 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +1 -0
- data/README.md +69 -0
- data/Rakefile +23 -11
- data/examples/progress_reporting.rb +26 -0
- data/examples/simple.rb +22 -0
- data/ext/puremotion/audio.c +38 -0
- data/ext/puremotion/extconf.rb +34 -0
- data/ext/puremotion/frame.c +176 -0
- data/ext/puremotion/media.c +175 -0
- data/ext/puremotion/puremotion.c +26 -0
- data/ext/puremotion/puremotion.h +38 -0
- data/ext/puremotion/stream.c +128 -0
- data/ext/puremotion/stream_collection.c +44 -0
- data/ext/puremotion/utils.c +81 -0
- data/ext/puremotion/utils.h +6 -0
- data/ext/puremotion/video.c +141 -0
- data/lib/{puremotion/events → events}/event.rb +0 -0
- data/lib/{puremotion/events → events}/generator.rb +0 -0
- data/lib/media.rb +89 -0
- data/lib/preset/audio/audio.rb +42 -0
- data/lib/preset/file.rb +19 -0
- data/lib/preset/general.rb +28 -0
- data/lib/preset/metadata.rb +41 -0
- data/lib/preset/preset.rb +120 -0
- data/lib/preset/video/crop.rb +29 -0
- data/lib/preset/video/pad.rb +31 -0
- data/lib/preset/video/video.rb +130 -0
- data/lib/puremotion.rb +20 -12
- data/lib/puremotion_native.so +0 -0
- data/lib/threading.rb +54 -0
- data/lib/{puremotion/tools → tools}/ffmpeg.rb +12 -50
- data/lib/transcode/transcode.rb +142 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/units/media_spec.rb +13 -0
- data/spec/units/preset_spec.rb +91 -0
- metadata +52 -44
- data/.document +0 -5
- data/.gitignore +0 -21
- data/README.rdoc +0 -18
- data/VERSION +0 -1
- data/lib/puremotion/codecs.rb +0 -59
- data/lib/puremotion/media.rb +0 -490
- data/lib/puremotion/media/stream.rb +0 -4
- data/lib/puremotion/media/stream/audio.rb +0 -0
- data/lib/puremotion/media/stream/base.rb +0 -7
- data/lib/puremotion/media/stream/collection.rb +0 -5
- data/lib/puremotion/media/stream/video.rb +0 -61
- data/lib/puremotion/recipes/ipod.yml +0 -12
- data/lib/puremotion/thread.rb +0 -153
- data/lib/puremotion/transcode/recipe.rb +0 -250
- data/lib/puremotion/transcode/transcode.rb +0 -153
- data/puremotion.gemspec +0 -68
- data/test/helper.rb +0 -10
- data/test/test_puremotion.rb +0 -7
File without changes
|
File without changes
|
data/lib/media.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
def Media(*args, &block)
|
2
|
+
|
3
|
+
raise ArgumentError, "No arguments given" unless args.length > 0
|
4
|
+
raise ArgumentError, "Invalid file parameter" unless args[0].is_a?(String)
|
5
|
+
|
6
|
+
path = args[0]
|
7
|
+
|
8
|
+
# PureMotion::Media will handle testing if the file exists
|
9
|
+
media = PureMotion::Media.new(path)
|
10
|
+
|
11
|
+
media.instance_eval(&block) if block_given?
|
12
|
+
|
13
|
+
media
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module PureMotion
|
18
|
+
|
19
|
+
class Media
|
20
|
+
|
21
|
+
# Has the media at least one video stream?
|
22
|
+
#
|
23
|
+
# @return [Boolean] Existance of a video stream
|
24
|
+
def video?
|
25
|
+
has_stream_of :video
|
26
|
+
end
|
27
|
+
|
28
|
+
# Has the media at least one audio stream?
|
29
|
+
#
|
30
|
+
# @return [Boolean] Existance of an audio stream
|
31
|
+
def audio?
|
32
|
+
has_stream_of :audio
|
33
|
+
end
|
34
|
+
|
35
|
+
# Convience method to return the first video stream in the media.
|
36
|
+
#
|
37
|
+
# @return [PureMotion::Streams::Stream, nil] Video stream object or nil if no video stream is found.
|
38
|
+
def video
|
39
|
+
first_stream_of :video
|
40
|
+
end
|
41
|
+
|
42
|
+
# Convience method to return the first audio stream in the media.
|
43
|
+
#
|
44
|
+
# @return [PureMotion::Streams::Stream, nil] Audio stream object or nil if no audio stream is found.
|
45
|
+
def audio
|
46
|
+
first_stream_of :audio
|
47
|
+
end
|
48
|
+
|
49
|
+
# Determines if the file cannot be read by FFmpeg
|
50
|
+
# @return [Boolean]
|
51
|
+
def invalid?
|
52
|
+
!valid?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the first stream of `type`
|
56
|
+
# @param [Symbol] type The type of stream - `:video` or `:audio`
|
57
|
+
# @return [PureMotion::Streams::Stream, nil] First stream of `type` or nil if one is not found
|
58
|
+
def first_stream_of( type )
|
59
|
+
streams.each do |stream| return stream if stream.type == type end
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
|
63
|
+
# Has the media a stream of `type`?
|
64
|
+
# @param [Symbol] type The type of stream - `:video` or `:audio`
|
65
|
+
# @return [Boolean]
|
66
|
+
def has_stream_of( type )
|
67
|
+
has = false
|
68
|
+
streams.each do |stream| has = true if stream.type == type end
|
69
|
+
return has
|
70
|
+
end
|
71
|
+
|
72
|
+
def transcode(&block)
|
73
|
+
raise ArgumentError, "No block given for transcode parameters" unless block_given?
|
74
|
+
|
75
|
+
media = self
|
76
|
+
setup = lambda { input media }
|
77
|
+
|
78
|
+
t = Transcode do
|
79
|
+
instance_eval(&setup)
|
80
|
+
instance_eval(&block)
|
81
|
+
end
|
82
|
+
|
83
|
+
t
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
module Audio
|
6
|
+
|
7
|
+
def channels(n)
|
8
|
+
add :ac => n
|
9
|
+
end
|
10
|
+
|
11
|
+
def frames(frames)
|
12
|
+
add :aframes => frame
|
13
|
+
end
|
14
|
+
|
15
|
+
def sampling(rate)
|
16
|
+
add :ar => rate
|
17
|
+
end
|
18
|
+
|
19
|
+
alias :sample_rate :sampling
|
20
|
+
|
21
|
+
def disable
|
22
|
+
add :an
|
23
|
+
end
|
24
|
+
|
25
|
+
def codec(codec)
|
26
|
+
add :acodec => codec
|
27
|
+
end
|
28
|
+
|
29
|
+
def language(code)
|
30
|
+
add :alang => code
|
31
|
+
end
|
32
|
+
|
33
|
+
def bitrate(rate)
|
34
|
+
fail ArgumentError, "Invalid bitrate" unless rate =~ regexp[:bitrate]
|
35
|
+
add :ab => rate
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/preset/file.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
module General
|
6
|
+
|
7
|
+
def overwrite!
|
8
|
+
add "-y"
|
9
|
+
end
|
10
|
+
|
11
|
+
def seek(position)
|
12
|
+
add :ss => "#{position}"
|
13
|
+
# TODO : If input file is given check that seek distance is not greater than length
|
14
|
+
end
|
15
|
+
|
16
|
+
def duration(time)
|
17
|
+
add :t => time
|
18
|
+
end
|
19
|
+
|
20
|
+
def offset(by)
|
21
|
+
add :itsoffset => by
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
module Metadata
|
6
|
+
|
7
|
+
def title(title)
|
8
|
+
add :title => title
|
9
|
+
end
|
10
|
+
|
11
|
+
def author(author)
|
12
|
+
add :author => author
|
13
|
+
end
|
14
|
+
|
15
|
+
alias :artist :author
|
16
|
+
|
17
|
+
def copyright(copyright)
|
18
|
+
add :copyright => copyright
|
19
|
+
end
|
20
|
+
|
21
|
+
def album(album)
|
22
|
+
add :album => album
|
23
|
+
end
|
24
|
+
|
25
|
+
def year(year)
|
26
|
+
add :year => year
|
27
|
+
end
|
28
|
+
|
29
|
+
def track(track)
|
30
|
+
add :track => track
|
31
|
+
end
|
32
|
+
|
33
|
+
def comment(comment)
|
34
|
+
add :comment => comment
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
def self.build(&block)
|
6
|
+
|
7
|
+
preset = self.new(:dsl)
|
8
|
+
preset.instance_eval(&block) if block_given?
|
9
|
+
|
10
|
+
preset
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :event_handlers
|
15
|
+
|
16
|
+
def initialize(form=nil)
|
17
|
+
@input = nil
|
18
|
+
@output = nil
|
19
|
+
@log = nil
|
20
|
+
@previous = :general
|
21
|
+
@current = :general
|
22
|
+
@commands = []
|
23
|
+
@event_handlers = {}
|
24
|
+
@dsls = [:general, :file, :video, :audio, :metadata ,:crop, :pad]
|
25
|
+
extend_for(:general) if form == :dsl
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(name, *args, &block)
|
29
|
+
raise NoMethodError, "No parameter or context '#{name}' in context '#{@current.nil? ? @previous.capitalize : @current.capitalize} transcode parameters'" unless @dsls.include?(name) or args.length != 1
|
30
|
+
return false unless block_given?
|
31
|
+
extend_for(name)
|
32
|
+
instance_eval(&block)
|
33
|
+
extend_for @previous
|
34
|
+
end
|
35
|
+
|
36
|
+
def arguments
|
37
|
+
@commands.join(" ")
|
38
|
+
end
|
39
|
+
|
40
|
+
def event(name, &block)
|
41
|
+
@event_handlers[name] = block
|
42
|
+
end
|
43
|
+
|
44
|
+
def event_handler(name)
|
45
|
+
@event_handlers[name]
|
46
|
+
end
|
47
|
+
|
48
|
+
def log(log=nil)
|
49
|
+
return @log if log.nil?
|
50
|
+
@log = log
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def output(output=nil)
|
55
|
+
return @output if output.nil?
|
56
|
+
return false unless @output.nil?
|
57
|
+
output = "\"#{output}\"" if output =~ /\s/ and !(output =~ /^".*"$/)
|
58
|
+
@output = output
|
59
|
+
add @output
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def input(input=nil)
|
64
|
+
return @input if input.nil?
|
65
|
+
return false unless @input.nil?
|
66
|
+
if input.is_a?(String) then
|
67
|
+
if ::File.exists?(input) then # Ruby's File class is hidden by Preset::File
|
68
|
+
@input = PureMotion::Media.new(input)
|
69
|
+
else
|
70
|
+
raise ArgumentError, "Input file '#{input}' not found"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
if input.is_a?(PureMotion::Media)
|
74
|
+
@input = input
|
75
|
+
else
|
76
|
+
raise ArgumentError, "Invalid input"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
raise ArgumentError, "Invalid media input '#{@input.filename}'" unless @input.valid?
|
80
|
+
add :i => @input.filename
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def regexp
|
87
|
+
{
|
88
|
+
:bitrate => /^(\d*)(\S*)$/,
|
89
|
+
:resolution => /(\d*)x(\d*)/
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def extend_for(dsl)
|
94
|
+
return false unless @dsls.include?(dsl)
|
95
|
+
@previous = @current
|
96
|
+
@current = dsl
|
97
|
+
eval("extend(Preset::#{dsl.to_s.capitalize})")
|
98
|
+
end
|
99
|
+
|
100
|
+
def add(command)
|
101
|
+
@commands << command.strip if command.is_a?(String)
|
102
|
+
command = { command => '' } if command.is_a?(Symbol)
|
103
|
+
if command.is_a?(Hash) then
|
104
|
+
command.each_pair do |switch, value|
|
105
|
+
begin
|
106
|
+
value = value.to_s.strip
|
107
|
+
switch = switch.to_s.strip
|
108
|
+
value = "\"#{value}\"" if (value =~ /\s/)
|
109
|
+
rescue
|
110
|
+
throw ArgumentError, "Parameter enable to be cast to a string"
|
111
|
+
end
|
112
|
+
@commands << "-#{switch} #{value}".strip
|
113
|
+
end
|
114
|
+
else false end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
module Crop
|
6
|
+
|
7
|
+
# TODO : Check if crop amounts leave no video showing?
|
8
|
+
|
9
|
+
def top(amount)
|
10
|
+
add :croptop => amount
|
11
|
+
end
|
12
|
+
|
13
|
+
def right(amount)
|
14
|
+
add :cropright => amount
|
15
|
+
end
|
16
|
+
|
17
|
+
def bottom(amount)
|
18
|
+
add :cropbottom => amount
|
19
|
+
end
|
20
|
+
|
21
|
+
def left(amount)
|
22
|
+
add :cropleft => amount
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
module Pad
|
6
|
+
|
7
|
+
def top(amount)
|
8
|
+
add :padtop => amount
|
9
|
+
end
|
10
|
+
|
11
|
+
def right(amount)
|
12
|
+
add :padright => amount
|
13
|
+
end
|
14
|
+
|
15
|
+
def bottom(amount)
|
16
|
+
add :padbottom => amount
|
17
|
+
end
|
18
|
+
|
19
|
+
def left(amount)
|
20
|
+
add :padleft => amount
|
21
|
+
end
|
22
|
+
|
23
|
+
def color(color)
|
24
|
+
add :padcolor => color
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module PureMotion
|
2
|
+
|
3
|
+
class Preset
|
4
|
+
|
5
|
+
module Video
|
6
|
+
|
7
|
+
def buffer(size)
|
8
|
+
add :bufsize => size
|
9
|
+
end
|
10
|
+
|
11
|
+
def disable
|
12
|
+
add :vn
|
13
|
+
end
|
14
|
+
|
15
|
+
def frames(frames)
|
16
|
+
# TODO : Check whole number etc
|
17
|
+
add :vframes => frames
|
18
|
+
end
|
19
|
+
|
20
|
+
def framerate(fps)
|
21
|
+
add :r => fps
|
22
|
+
end
|
23
|
+
|
24
|
+
alias :fps :framerate
|
25
|
+
|
26
|
+
def aspect_ratio(ratio)
|
27
|
+
if ratio.is_a?(String)
|
28
|
+
ratio = ratio.split(':')[0..1]
|
29
|
+
fail ArgumentError, "Invalid aspect ratio" unless ratio.length == 2
|
30
|
+
begin
|
31
|
+
ratio = ratio[0].to_f / ratio[1].to_f
|
32
|
+
rescue
|
33
|
+
fail(ArgumentError, "Invalid aspect ratio")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
if ratio.is_a?(Fixnum)
|
37
|
+
fail ArgumentError, "Invalid aspect ratio" unless ratio >= 1 and ratio <= 2
|
38
|
+
else fail ArgumentError, "Invalid aspect ratio" end
|
39
|
+
|
40
|
+
add :aspect => ratio
|
41
|
+
end
|
42
|
+
|
43
|
+
def same_quality
|
44
|
+
add "-sameq"
|
45
|
+
end
|
46
|
+
|
47
|
+
def codec(codec)
|
48
|
+
add :vc => "#{codec}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def bitrate(rate)
|
52
|
+
fail ArgumentError, "Invalid bitrate" unless rate.to_s =~ regexp[:bitrate]
|
53
|
+
add :vb => rate
|
54
|
+
end
|
55
|
+
|
56
|
+
def resolution(*res)
|
57
|
+
res_regexp = regexp[:resolution]
|
58
|
+
res_invalid = ArgumentError.new("Resolution invalid")
|
59
|
+
presets = {
|
60
|
+
'sqcif' => [128, 96 ],
|
61
|
+
'qcif' => [176, 144 ],
|
62
|
+
'cif' => [352, 288 ],
|
63
|
+
'4cif' => [704, 576 ],
|
64
|
+
'qqvga' => [160, 120 ],
|
65
|
+
'qvga' => [320, 240 ],
|
66
|
+
'vga' => [640, 480 ],
|
67
|
+
'svga' => [800, 600 ],
|
68
|
+
'ga' => [1024, 768 ],
|
69
|
+
'uga' => [1600, 1200],
|
70
|
+
'qga' => [2048, 1536],
|
71
|
+
'sga' => [1280, 1024],
|
72
|
+
'qsga' => [2560, 2048],
|
73
|
+
'hsga' => [5120, 4096],
|
74
|
+
'wvga' => [852, 480 ],
|
75
|
+
'wga' => [1366, 768 ],
|
76
|
+
'wsga' => [1600, 1024],
|
77
|
+
'wuga' => [1920, 1200],
|
78
|
+
'woga' => [2560, 1600],
|
79
|
+
'wqsga' => [3200, 2048],
|
80
|
+
'wquga' => [3840, 2400],
|
81
|
+
'whsga' => [6400, 4096],
|
82
|
+
'whuga' => [7680, 4800],
|
83
|
+
'cga' => [320, 200 ],
|
84
|
+
'ega' => [640, 350 ],
|
85
|
+
'hd480' => [852, 480 ],
|
86
|
+
'hd720' => [1280, 720 ],
|
87
|
+
'hd1080' => [1920, 1080]
|
88
|
+
}
|
89
|
+
raise ArgumentError, "No resolution given" if res.empty?
|
90
|
+
|
91
|
+
# Transform things like resolution(:hd1080)
|
92
|
+
if res.length == 1 then
|
93
|
+
res = "#{res[0]}" if res[0].is_a?(String) or res[0].is_a?(Symbol)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Handle call like - resolution(320, 240)
|
97
|
+
res = "#{res[0]}x#{res[1]}" if res.length == 2
|
98
|
+
|
99
|
+
# Handle call like - resolution([320, 240])
|
100
|
+
res = res[0] if res[0].is_a?(Array)
|
101
|
+
|
102
|
+
# Handle strings like 'vga' or '320x240'
|
103
|
+
if res.is_a?(String) then
|
104
|
+
if presets.include?(res) then
|
105
|
+
res = presets[res]
|
106
|
+
else
|
107
|
+
res = res_regexp.match(res).captures[0..1] if res =~ res_regexp
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
fail res_invalid unless res.is_a?(Array)
|
112
|
+
fail res_invalid unless res.length == 2
|
113
|
+
|
114
|
+
begin
|
115
|
+
res = res.collect { |dimension| dimension.to_i }
|
116
|
+
rescue
|
117
|
+
fail res_invalid
|
118
|
+
end
|
119
|
+
|
120
|
+
fail res_invalid unless res[0].is_a?(Integer) and res[1].is_a?(Integer)
|
121
|
+
|
122
|
+
add :s => res.join("x")
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|