puremotion 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,4 +0,0 @@
1
- module PureMotion
2
-
3
-
4
- end
File without changes
@@ -1,7 +0,0 @@
1
- class PureMotion::Stream::Base
2
-
3
- def initialize
4
-
5
- end
6
-
7
- end
@@ -1,5 +0,0 @@
1
- class PureMotion::Stream::Collection < Array
2
-
3
-
4
-
5
- end
@@ -1,61 +0,0 @@
1
- class PureMotion::Stream::Video
2
-
3
- @raw = ''
4
- @media = nil
5
-
6
- def initialize raw
7
- @raw = raw
8
-
9
- @matches = {
10
- :stream => /Stream\s*(.*?)[,|:|\(|\[].*?\s*Video:\s*(.*?),\s*(.*?),\s*(\d*)x(\d*)/,
11
- :bitrate => /Stream\s*.*?[,|:|\(|\[].*?\s*Video:\s*.*?,\s*.*?,\s*\d*x\d*[^,]*,\s*([0-9\.]+)\s*(.*\/.),/,
12
- :frame_rate => /Stream\s*.*?[,|:|\(|\[].*?\s*Video:\s\S*,\s\S*,\s\d+x\d+[^,]+,\s\S*\s\S*,\s([^,\s]*)/,
13
- :raw_duration => /Duration:\s*([0-9\:\.]+),/
14
- }
15
- end
16
-
17
- def valid?
18
- return (@raw =~ match(:stream))
19
- end
20
-
21
- def fps
22
- fps = test(:frame_rate, 1)
23
-
24
- return fps.to_f unless fps.to_f == 0.0
25
- return nil
26
- end
27
-
28
- def resolution
29
- res = {
30
- :width => test(:stream, 4).to_i,
31
- :height => test(:stream, 5).to_i
32
- }
33
-
34
- res.each_pair { |k, v| if v == 0 then res[k] = nil end }
35
-
36
- Struct.new("Resolution", :width, :height)
37
- Struct::Resolution.new(
38
- res[:width],
39
- res[:height]
40
- )
41
- end
42
-
43
- #def detail
44
- #
45
- #end
46
-
47
- private
48
-
49
- def match name
50
- return @matches[name]
51
- end
52
-
53
- def test(name = nil, n = -1)
54
- match = @matches[name]
55
- return nil if match.nil?
56
-
57
- return match.match(@raw) unless n >= 0
58
- return match.match(@raw)[n]
59
- end
60
-
61
- end
@@ -1,12 +0,0 @@
1
- name: iPod
2
- desc: Encodes a video suitable for iPod
3
- recipe:
4
- video:
5
- codec: libx264
6
- bitrate: 512
7
- resolution: 320x240
8
- fps: 20
9
- audio:
10
- codec: aac
11
- bitrate: 128
12
- channels: mono
@@ -1,153 +0,0 @@
1
- # Author:: Jared Kuolt (mailto:me@superjared.com)
2
- # Copyright:: Copyright (c) 2009 Jared Kuolt
3
- # License:: MIT License
4
- #
5
- # See README.rdoc[link:files/README_rdoc.html] for usage
6
- #
7
- require 'logger'
8
- require 'timeout'
9
-
10
- module PureMotion
11
- class Thread
12
- # The Thread object, brah
13
- attr_reader :thread
14
- # If the Thread takes a poopie...
15
- attr_reader :exception
16
- # An identifier
17
- attr_accessor :label
18
-
19
- # Create a new RobustThread (see README)
20
- def initialize(opts={}, &block)
21
- self.class.send :init_exit_handler
22
- args = (opts[:args] or [])
23
- self.class.send :do_before_init
24
- @thread = ::Thread.new(*args) do |*targs|
25
- begin
26
- self.class.send :do_before_yield
27
- block.call(*targs)
28
- self.class.send :do_after_yield
29
- rescue Exception => e
30
- @exception = e
31
- self.class.send :handle_exception, e
32
- end
33
- self.class.log "#{self.label.inspect} exited cleanly"
34
- end
35
- self.label = opts[:label] || @thread.inspect
36
- self.class.group << self
37
- end
38
-
39
- ## Class methods and attributes
40
- class << self
41
- attr_accessor :logger, :say_goodnight, :exit_handler_initialized, :callbacks
42
- VALID_CALLBACKS = [:before_init, :before_yield, :after_yield, :after_join, :before_exit]
43
-
44
- # Logger object (see README)
45
- def logger
46
- @logger ||= Logger.new(STDOUT)
47
- end
48
-
49
- # Simple log interface
50
- def log(msg, level=:info)
51
- #self.logger.send level, "#{self}: " + msg
52
- end
53
-
54
- # The collection of RobustThread objects
55
- def group
56
- @group ||= []
57
- end
58
-
59
- # Loop an activity and exit it cleanly (see README)
60
- def loop(opts={}, &block)
61
- sleep_seconds = opts.delete(:seconds) || 2
62
- self.new(opts) do |*args|
63
- Kernel.loop do
64
- break if self.say_goodnight
65
- block.call(*args)
66
- # We want to sleep for the right amount of time, but we also don't
67
- # want to wait until the sleep is done if our exit handler has been
68
- # called so we iterate over a loop, sleeping only 0.1 and checking
69
- # each iteration whether we need to die, and the timeout is a noop
70
- # indicating we need to continue.
71
- begin
72
- Timeout.timeout(sleep_seconds) do
73
- Kernel.loop do
74
- break if self.say_goodnight
75
- sleep 0.1
76
- end
77
- end
78
- rescue Timeout::Error
79
- # OK
80
- end
81
- end
82
- end
83
- end
84
-
85
- # Set exception handler
86
- def exception_handler(&block)
87
- unless block.arity == 1
88
- raise ArgumentError, "Bad arity for exception handler. It may only accept a single argument"
89
- end
90
- @exception_handler = block
91
- end
92
-
93
- # Add a callback
94
- public
95
- def add_callback(sym, &block)
96
- sym = sym.to_sym
97
- raise ArgumentError, "Invalid callback #{sym.inspect}" unless VALID_CALLBACKS.include? sym
98
- self.callbacks ||= {}
99
- self.callbacks[sym] ||= []
100
- self.callbacks[sym] << block
101
- end
102
-
103
- private
104
- # Calls exception handler if set (see RobustThread.exception_handler)
105
- def handle_exception(exc)
106
- if @exception_handler.is_a? Proc
107
- @exception_handler.call(exc)
108
- else
109
- log("Unhandled exception:\n#{exc.message} " \
110
- "(#{exc.class}): \n\t#{exc.backtrace.join("\n\t")}", :error)
111
- end
112
- end
113
-
114
- # Sets up the exit_handler unless exit_handler_initialized
115
- def init_exit_handler
116
- return if self.exit_handler_initialized
117
- self.say_goodnight = false
118
- at_exit do
119
- self.say_goodnight = true
120
- begin
121
- self.group.each do |rt|
122
- log "waiting on #{rt.label.inspect}" if rt.thread.alive?
123
- rt.thread.join
124
- rt.class.send :do_after_join
125
- end
126
- self.send :do_before_exit
127
- log "exited cleanly"
128
- rescue Interrupt
129
- log "prematurely killed by interrupt!", :error
130
- end
131
- end
132
- self.exit_handler_initialized = true
133
- end
134
-
135
- def perform_callback(sym)
136
- raise ArgumentError, "Cannot perform invalid callback #{sym.inspect}" unless VALID_CALLBACKS.include? sym
137
- return unless self.callbacks and self.callbacks[sym]
138
- self.callbacks[sym].reverse.each do |callback|
139
- callback.call
140
- end
141
- end
142
-
143
- # Performs callback, if possible
144
- def method_missing(sym, *args)
145
- if sym.to_s =~ /^do_(.*)$/
146
- perform_callback($1.to_sym)
147
- else
148
- raise NoMethodError, "RobustThread method_missing: #{sym.inspect}"
149
- end
150
- end
151
- end
152
- end
153
- end
@@ -1,250 +0,0 @@
1
- require 'yaml'
2
-
3
- module PureMotion::Transcode
4
-
5
- class Recipe
6
-
7
- def self.from_file(file)
8
-
9
- file.sub!(/\.yml/, '') if file =~ /\.yml/
10
-
11
- built_in_path = File.join( File.expand_path(File.dirname(__FILE__)), '../recipes', file + '.yml')
12
-
13
- where = nil
14
- path = nil
15
-
16
- if File.exists? built_in_path then where = :internal end
17
- if File.exists? file then where = :external end
18
-
19
- raise(ArgumentError, "Recipe file '#{file}' not found") if where.nil?
20
-
21
- case where
22
- when :internal
23
- path = built_in_path
24
- when :external
25
- path = file
26
- end
27
-
28
- return self.new({ :source => :file, :recipe => path })
29
- end
30
-
31
- def self.from_name(name)
32
-
33
- n = name.to_s
34
-
35
- raise(ArgumentError, 'Invalid recipe name') if not n.is_a?(String)
36
-
37
- return from_file(n)
38
-
39
- end
40
-
41
- def self.build(hash)
42
- return self.new({ :source => :hash, :recipe => hash})
43
- end
44
-
45
- @recipe = {
46
- 'format' => :skip
47
- }
48
- @video = {}
49
- @audio = {}
50
- @container = nil
51
-
52
- attr_accessor :audio, :video, :arg_string, :args
53
-
54
- def initialize(opts)
55
-
56
- if not opts.is_a? Hash then raise(ArgumentError, "opts should be a hash") end
57
-
58
- defaults = {
59
- :source => :hash,
60
- :recipe => nil
61
- }
62
-
63
- @opts = defaults.merge!(opts)
64
-
65
- @recipe = @opts[:recipe]
66
-
67
- if @recipe.nil? then raise(ArgumentError, "No recipe given") end
68
-
69
- case @opts[:source]
70
- when :hash
71
- hash_build
72
- when :file
73
- file_build @recipe
74
- when :name
75
- from_name @recipe
76
- else
77
- raise(ArgumentError, 'Invalid recipe source')
78
- end
79
-
80
- end
81
-
82
- def parse o
83
-
84
- recipe = nil
85
-
86
- recipe = o['recipe'] if o.has_key? 'recipe'
87
- recipe = o if o.has_key? 'video' or o.has_key? 'audio'
88
-
89
- raise ArgumentError, 'Bad recipe' if recipe.nil?
90
-
91
- video = recipe['video']
92
- audio = recipe['audio']
93
-
94
- parse_video = lambda do |source_video|
95
-
96
- @video = {}
97
- @video.merge! source_video
98
-
99
- # Video codec
100
-
101
- # End video codec
102
-
103
- # Video bitrate
104
- if @video['bitrate'].nil? then @video['bitrate'] = :skip else
105
- begin
106
- @video['bitrate'] = @video['bitrate'].to_i if not @video['bitrate'].is_a? Integer
107
- rescue
108
- raise ArgumentError, 'Video bitrate must be a number'
109
- end
110
-
111
- @video['bitrate'] *= 1024
112
- raise ArgumentError, 'Recipe: video bitrate too low' if @video['bitrate'] < 1024
113
- end
114
- # End video
115
-
116
- # Video resolution
117
- if @video['resolution'].nil? then @video['resolution'] = :skip else
118
- if not @video['resolution'] =~ /\d*x\d*/ then
119
- raise ArgumentError, "Invalid resolution"
120
- end
121
- end
122
- # End video resolution
123
-
124
-
125
-
126
- end
127
-
128
- parse_audio = lambda do |source_audio|
129
-
130
- @audio = {
131
- 'codec' => nil,
132
- 'bitrate' => nil,
133
- }
134
-
135
- @audio.merge! source_audio
136
-
137
- # Audio codec
138
- if @audio['codec'].nil? then @audio['codec'] = :skip else
139
- raise ArgumentError, 'Recipe: Invalid Audio Codec' unless PureMotion::Codecs.valid? @audio['codec'], :audio
140
- @audio['codec'] = PureMotion::Codecs.find(@audio['codec']).ffmpeg_name
141
- end
142
- # End Audio codec
143
-
144
- # Audio bitrate
145
- if @audio['bitrate'].nil? then @audio['bitrate'] = :skip else
146
- begin
147
- @audio['bitrate'] = @audio['bitrate'].to_i if not @audio['bitrate'].is_a? Integer
148
- rescue
149
- raise ArgumentError, 'Audio bitrate must be a number'
150
- end
151
-
152
- @audio['bitrate'] *= 1024
153
- raise ArgumentError, 'Recipe: Audio bitrate too low' if @audio['bitrate'] < 1024
154
- end
155
- # End Audio Bitrate
156
-
157
- # Audio channels
158
- if @audio['channels'].nil? then @audio['channels'] = :skip else
159
- channel_types = { 'mono' => 1, 'stereo' => 2 }
160
- if @audio['channels'].is_a? String then
161
- @audio['channels'].downcase!
162
- if channel_types[@audio['channels']].nil? then
163
- @audio['channels'] = :skip
164
- else
165
- @audio['channels'] = channel_types[@audio['channels']]
166
- end
167
- end
168
- end
169
- # End audio channels
170
-
171
- end
172
-
173
- parse_video.call video unless video.nil?
174
- parse_audio.call audio unless audio.nil?
175
-
176
- @recipe = {}
177
-
178
- if recipe['format'].nil? then recipe['format'] = :skip else
179
- @recipe.store('format', recipe['format'])
180
- end
181
-
182
- end
183
-
184
- def valid?
185
- valid = true
186
- if audio? and !valid_audio? then valid = false end
187
- if video? and !valid_video? then valid = false end
188
- valid
189
- end
190
-
191
- def valid_video?
192
-
193
- end
194
-
195
- def valid_audio?
196
-
197
- end
198
-
199
- def to_args
200
-
201
- args = []
202
- arg_strings = []
203
-
204
- args[0] = ['f', @recipe['format'] ]
205
- args[1] = ['acodec', @audio['codec'] ]
206
- args[2] = ['vcodec', @video['codec'] ]
207
- args[3] = ['vb', @video['bitrate'] ]
208
- args[4] = ['s', @video['resolution']]
209
- args[5] = ['r', @video['fps'] ]
210
- args[6] = ['ab', @audio['bitrate'] ]
211
- args[7] = ['ac', @audio['channels'] ]
212
-
213
- required_value_args = ['f', 'vcodec', 'acodec', 'vb', 's', 'r', 'ab', 'ac']
214
-
215
- args.each do |arg|
216
- next if required_value_args.include? arg[0] and arg[1].nil?
217
- arg_strings << format_arg(arg[0], arg[1])
218
- end
219
-
220
- @args = arg_strings.join(' ')
221
-
222
- @args
223
-
224
- end
225
-
226
- private
227
-
228
- def format_arg name, value
229
- name = '' if name.nil?
230
- value = '' if value.nil?
231
- value = value.to_s
232
- value.gsub! '"', ''
233
- value = '"' + value + '"' if value =~ /\s/
234
- return value if name == :non
235
- return name if value == :non
236
- '-' + name.to_s + ' ' + value.to_s
237
- end
238
-
239
- def hash_build
240
-
241
- end
242
-
243
- def file_build(file)
244
- @yaml = ::YAML.load( File.read(file) )
245
- parse @yaml
246
- end
247
-
248
- end
249
-
250
- end