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.
Files changed (54) hide show
  1. data/.yardopts +1 -0
  2. data/README.md +69 -0
  3. data/Rakefile +23 -11
  4. data/examples/progress_reporting.rb +26 -0
  5. data/examples/simple.rb +22 -0
  6. data/ext/puremotion/audio.c +38 -0
  7. data/ext/puremotion/extconf.rb +34 -0
  8. data/ext/puremotion/frame.c +176 -0
  9. data/ext/puremotion/media.c +175 -0
  10. data/ext/puremotion/puremotion.c +26 -0
  11. data/ext/puremotion/puremotion.h +38 -0
  12. data/ext/puremotion/stream.c +128 -0
  13. data/ext/puremotion/stream_collection.c +44 -0
  14. data/ext/puremotion/utils.c +81 -0
  15. data/ext/puremotion/utils.h +6 -0
  16. data/ext/puremotion/video.c +141 -0
  17. data/lib/{puremotion/events → events}/event.rb +0 -0
  18. data/lib/{puremotion/events → events}/generator.rb +0 -0
  19. data/lib/media.rb +89 -0
  20. data/lib/preset/audio/audio.rb +42 -0
  21. data/lib/preset/file.rb +19 -0
  22. data/lib/preset/general.rb +28 -0
  23. data/lib/preset/metadata.rb +41 -0
  24. data/lib/preset/preset.rb +120 -0
  25. data/lib/preset/video/crop.rb +29 -0
  26. data/lib/preset/video/pad.rb +31 -0
  27. data/lib/preset/video/video.rb +130 -0
  28. data/lib/puremotion.rb +20 -12
  29. data/lib/puremotion_native.so +0 -0
  30. data/lib/threading.rb +54 -0
  31. data/lib/{puremotion/tools → tools}/ffmpeg.rb +12 -50
  32. data/lib/transcode/transcode.rb +142 -0
  33. data/spec/spec_helper.rb +7 -0
  34. data/spec/units/media_spec.rb +13 -0
  35. data/spec/units/preset_spec.rb +91 -0
  36. metadata +52 -44
  37. data/.document +0 -5
  38. data/.gitignore +0 -21
  39. data/README.rdoc +0 -18
  40. data/VERSION +0 -1
  41. data/lib/puremotion/codecs.rb +0 -59
  42. data/lib/puremotion/media.rb +0 -490
  43. data/lib/puremotion/media/stream.rb +0 -4
  44. data/lib/puremotion/media/stream/audio.rb +0 -0
  45. data/lib/puremotion/media/stream/base.rb +0 -7
  46. data/lib/puremotion/media/stream/collection.rb +0 -5
  47. data/lib/puremotion/media/stream/video.rb +0 -61
  48. data/lib/puremotion/recipes/ipod.yml +0 -12
  49. data/lib/puremotion/thread.rb +0 -153
  50. data/lib/puremotion/transcode/recipe.rb +0 -250
  51. data/lib/puremotion/transcode/transcode.rb +0 -153
  52. data/puremotion.gemspec +0 -68
  53. data/test/helper.rb +0 -10
  54. data/test/test_puremotion.rb +0 -7
@@ -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
@@ -0,0 +1,19 @@
1
+ module PureMotion
2
+
3
+ class Preset
4
+
5
+ module File
6
+
7
+ def overwrite!
8
+ add :y
9
+ end
10
+
11
+ def size_limit(size)
12
+ add :fs => size
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -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