one-k-rmov 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/ext/track.c ADDED
@@ -0,0 +1,391 @@
1
+ #include "rmov_ext.h"
2
+
3
+ VALUE cTrack;
4
+
5
+ static void track_free(struct RTrack *rTrack)
6
+ {
7
+ }
8
+
9
+ static void track_mark(struct RTrack *rTrack)
10
+ {
11
+ }
12
+
13
+ /*
14
+ call-seq: new() -> track
15
+
16
+ Creates a new track instance. Generally you will do this through
17
+ movie.tracks to fetch the Track instances for a given movie.
18
+ */
19
+ static VALUE track_new(VALUE klass)
20
+ {
21
+ struct RTrack *rTrack;
22
+ return Data_Make_Struct(klass, struct RTrack, track_mark, track_free, rTrack);
23
+ }
24
+
25
+ /*
26
+ call-seq: load(movie, index)
27
+
28
+ Loads a QuickTime track from a given movie. This is done automatically
29
+ when calling movie.tracks.
30
+ */
31
+ static VALUE track_load(VALUE obj, VALUE movie_obj, VALUE index_obj)
32
+ {
33
+ RTRACK(obj)->track = GetMovieIndTrack(MOVIE(movie_obj), NUM2INT(index_obj));
34
+ if (!RTRACK(obj)->track)
35
+ rb_raise(eQuickTime, "Unable to fetch track for movie at index %d", NUM2INT(index_obj));
36
+
37
+ return obj;
38
+ }
39
+
40
+ /*
41
+ call-seq: raw_duration() -> duration_int
42
+
43
+ Returns the raw duration of the track. Combine this with time_scale to
44
+ reach the duration in seconds.
45
+ */
46
+ static VALUE track_raw_duration(VALUE obj)
47
+ {
48
+ return INT2NUM(GetMediaDuration(TRACK_MEDIA(obj)));
49
+ }
50
+
51
+ /*
52
+ call-seq: time_scale() -> scale_int
53
+
54
+ Returns the time scale of the track. Usually only needed when working
55
+ with raw_duration.
56
+ */
57
+ static VALUE track_time_scale(VALUE obj)
58
+ {
59
+ return INT2NUM(GetMediaTimeScale(TRACK_MEDIA(obj)));
60
+ }
61
+
62
+ /*
63
+ call-seq: frame_count() -> count
64
+
65
+ Returns the number of frames in the track.
66
+ */
67
+ static VALUE track_frame_count(VALUE obj)
68
+ {
69
+ return INT2NUM(GetMediaSampleCount(TRACK_MEDIA(obj)));
70
+ }
71
+
72
+ /*
73
+ call-seq: media_type() -> media_type_sym
74
+
75
+ Returns either :audio or :video depending on the type of track this is.
76
+ */
77
+ static VALUE track_media_type(VALUE obj)
78
+ {
79
+ OSType media_type;
80
+
81
+ GetMediaHandlerDescription(TRACK_MEDIA(obj), &media_type, 0, 0);
82
+ if (media_type == SoundMediaType) {
83
+ return ID2SYM(rb_intern("audio"));
84
+ } else if (media_type == VideoMediaType) {
85
+ return ID2SYM(rb_intern("video"));
86
+ } else if (media_type == TextMediaType) {
87
+ return ID2SYM(rb_intern("text"));
88
+ } else {
89
+ return Qnil;
90
+ }
91
+ }
92
+
93
+ /* returns the ImageDescriptionHandle for the track.
94
+ If it's not a video track, return NULL
95
+ */
96
+ static ImageDescriptionHandle track_image_description(VALUE obj)
97
+ {
98
+ Media track_media = TRACK_MEDIA(obj);
99
+ OSType media_type;
100
+ OSErr osErr = noErr;
101
+
102
+ /* restrict reporting to video track */
103
+ GetMediaHandlerDescription(track_media, &media_type, 0, 0);
104
+ if (media_type != VideoMediaType)
105
+ return NULL;
106
+
107
+ SampleDescriptionHandle sample_description = NULL;
108
+ sample_description = (SampleDescriptionHandle)NewHandle(sizeof(SampleDescription));
109
+ if (LMGetMemErr() != noErr) {
110
+ rb_raise(eQuickTime, "Memory Error %d when determining image description", LMGetMemErr());
111
+ return NULL;
112
+ }
113
+
114
+ GetMediaSampleDescription(track_media, 1, sample_description);
115
+ osErr = GetMoviesError();
116
+ if (osErr != noErr) {
117
+ rb_raise(eQuickTime, "Movie Error %d when determining image description", osErr);
118
+ DisposeHandle((Handle)sample_description);
119
+ return NULL;
120
+ }
121
+
122
+ return (ImageDescriptionHandle)sample_description;
123
+ }
124
+
125
+
126
+ /*
127
+ call-seq: track_codec() -> codec_string
128
+
129
+ Returns the name of the codec. Only valid for video tracks. Others return nil.
130
+ */
131
+ static VALUE track_codec(VALUE obj)
132
+ {
133
+ ImageDescriptionHandle image_description = track_image_description(obj);
134
+
135
+ if (image_description == NULL)
136
+ return Qnil;
137
+
138
+ UInt8 *codecStr = (*image_description)->name;
139
+
140
+ VALUE out_str = rb_str_new( (char*)codecStr+1, (UInt8)codecStr[0] );
141
+
142
+ DisposeHandle((Handle)image_description);
143
+
144
+ return out_str;
145
+ }
146
+
147
+
148
+ /*
149
+ call-seq: track_width() -> width_in_pixels
150
+
151
+ Returns the width of track data. Only valid for video tracks. Others return nil.
152
+ */
153
+ static VALUE track_width(VALUE obj)
154
+ {
155
+ ImageDescriptionHandle image_description = track_image_description(obj);
156
+
157
+ if (image_description == NULL)
158
+ return Qnil;
159
+
160
+ short width = (*image_description)->width;
161
+
162
+ DisposeHandle((Handle)image_description);
163
+
164
+ return INT2NUM(width);
165
+ }
166
+
167
+ /*
168
+ call-seq: track_height() -> height_in_pixels
169
+
170
+ Returns the height of track data. Only valid for video tracks. Others return nil.
171
+ */
172
+ static VALUE track_height(VALUE obj)
173
+ {
174
+ ImageDescriptionHandle image_description = track_image_description(obj);
175
+
176
+ if (image_description == NULL)
177
+ return Qnil;
178
+
179
+ short height = (*image_description)->height;
180
+
181
+ DisposeHandle((Handle)image_description);
182
+
183
+ return INT2NUM(height);
184
+ }
185
+
186
+
187
+ /*
188
+ call-seq: id() -> quicktime_track_id_int
189
+
190
+ Returns either id number QuickTime uses to reference this track.
191
+ Usually only used internally.
192
+ */
193
+ static VALUE track_id(VALUE obj)
194
+ {
195
+ return INT2NUM(GetTrackID(TRACK(obj)));
196
+ }
197
+
198
+ /*
199
+ call-seq: delete()
200
+
201
+ Removes the track from its movie and deletes it from memory.
202
+ */
203
+ static VALUE track_delete(VALUE obj)
204
+ {
205
+ DisposeMovieTrack(TRACK(obj));
206
+ return Qnil;
207
+ }
208
+
209
+ /*
210
+ call-seq: disable()
211
+
212
+ Disables the track. See enabled? to determine if it's disabled already.
213
+ */
214
+ static VALUE track_disable(VALUE obj, VALUE boolean)
215
+ {
216
+ SetTrackEnabled(TRACK(obj), FALSE);
217
+ return obj;
218
+ }
219
+
220
+ /*
221
+ call-seq: enable()
222
+
223
+ Enables the track. See enabled? to determine if it's enabled already.
224
+ */
225
+ static VALUE track_enable(VALUE obj, VALUE boolean)
226
+ {
227
+ SetTrackEnabled(TRACK(obj), TRUE);
228
+ return obj;
229
+ }
230
+
231
+ /*
232
+ call-seq: enabled?() -> bool
233
+
234
+ Returns true/false depending on if the track is enabled.
235
+ */
236
+ static VALUE track_enabled(VALUE obj, VALUE boolean)
237
+ {
238
+ if (GetTrackEnabled(TRACK(obj)) == TRUE) {
239
+ return Qtrue;
240
+ } else {
241
+ return Qfalse;
242
+ }
243
+ }
244
+
245
+ /*
246
+ call-seq: volume() -> volume_float
247
+
248
+ Returns the volume of the audio from 0.0 to 1.0.
249
+ */
250
+ static VALUE track_get_volume(VALUE obj)
251
+ {
252
+ return rb_float_new((double)GetTrackVolume(TRACK(obj))/0x0100);
253
+ }
254
+
255
+ /*
256
+ call-seq: volume=(volume_float)
257
+
258
+ Sets the volume to the given value (0.0 to 1.0)
259
+ */
260
+ static VALUE track_set_volume(VALUE obj, VALUE volume_obj)
261
+ {
262
+ SetTrackVolume(TRACK(obj), (short)(0x0100*NUM2DBL(volume_obj)));
263
+ return Qnil;
264
+ }
265
+
266
+
267
+ /* returns the AudioStreamBasicDescription for the track.
268
+ If it's not an audio track, return NULL
269
+ */
270
+ static SoundDescriptionHandle track_audio_description(VALUE obj)
271
+ {
272
+ Media track_media = TRACK_MEDIA(obj);
273
+ OSType media_type;
274
+ OSErr osErr = noErr;
275
+
276
+ /* restrict reporting to audio track */
277
+ GetMediaHandlerDescription(track_media, &media_type, 0, 0);
278
+ if (media_type != SoundMediaType)
279
+ return NULL;
280
+
281
+ SoundDescriptionHandle sample_description = NULL;
282
+ sample_description = (SoundDescriptionHandle)NewHandle(sizeof(SampleDescription));
283
+ if (LMGetMemErr() != noErr) {
284
+ rb_raise(eQuickTime, "Memory Error %d when determining audio description", LMGetMemErr());
285
+ return NULL;
286
+ }
287
+
288
+ GetMediaSampleDescription(track_media, 1, (SampleDescriptionHandle)sample_description);
289
+ osErr = GetMoviesError();
290
+ if (osErr != noErr) {
291
+ rb_raise(eQuickTime, "GetMediaSampleDescription Error %d when determining audio description", osErr);
292
+ DisposeHandle((Handle)sample_description);
293
+ return NULL;
294
+ }
295
+
296
+ return sample_description;
297
+ }
298
+
299
+ /*
300
+ call-seq: offset() -> seconds
301
+
302
+ Returns the offset of the track from the beginning of the movie (in seconds).
303
+ */
304
+ static VALUE track_get_offset(VALUE obj)
305
+ {
306
+ return rb_float_new((double)GetTrackOffset(TRACK(obj))/GetMediaTimeScale(TRACK_MEDIA(obj)));
307
+ }
308
+
309
+ /*
310
+ call-seq: offset=(seconds)
311
+
312
+ Sets the offset of the track from the start of the movie (in seconds).
313
+ */
314
+ static VALUE track_set_offset(VALUE obj, VALUE seconds)
315
+ {
316
+ SetTrackOffset(TRACK(obj), TRACK_TIME(obj, seconds));
317
+ return Qnil;
318
+ }
319
+
320
+ /*
321
+ call-seq: new_video_media
322
+
323
+ Creates a new video media for this track.
324
+
325
+ Generally this method is not called directly, instead you can make a
326
+ new video track using Movie#new_video_track.
327
+ */
328
+ static VALUE track_new_video_media(VALUE obj)
329
+ {
330
+ NewTrackMedia(TRACK(obj), VideoMediaType, 600, 0, 0);
331
+ return obj;
332
+ }
333
+
334
+ /*
335
+ call-seq: new_audio_media
336
+
337
+ Creates a new audio media for this track.
338
+
339
+ Generally this method is not called directly, instead you can make a
340
+ new audio track using Movie#new_audio_track.
341
+ */
342
+ static VALUE track_new_audio_media(VALUE obj)
343
+ {
344
+ NewTrackMedia(TRACK(obj), SoundMediaType, 44100, 0, 0);
345
+ return obj;
346
+ }
347
+
348
+ /*
349
+ call-seq: new_text_media
350
+
351
+ Creates a new text media for this track.
352
+
353
+ Generally this method is not called directly, instead you can make a
354
+ new text track using Movie#new_text_track.
355
+ */
356
+ static VALUE track_new_text_media(VALUE obj)
357
+ {
358
+ NewTrackMedia(TRACK(obj), TextMediaType, 600, 0, 0);
359
+ return obj;
360
+ }
361
+
362
+
363
+ void Init_quicktime_track()
364
+ {
365
+ VALUE mQuickTime;
366
+ mQuickTime = rb_define_module("QuickTime");
367
+ cTrack = rb_define_class_under(mQuickTime, "Track", rb_cObject);
368
+ rb_define_alloc_func(cTrack, track_new);
369
+ rb_define_method(cTrack, "load_from_movie", track_load, 2);
370
+ rb_define_method(cTrack, "raw_duration", track_raw_duration, 0);
371
+ rb_define_method(cTrack, "time_scale", track_time_scale, 0);
372
+ rb_define_method(cTrack, "frame_count", track_frame_count, 0);
373
+ rb_define_method(cTrack, "media_type", track_media_type, 0);
374
+
375
+ rb_define_method(cTrack, "codec", track_codec, 0);
376
+ rb_define_method(cTrack, "width", track_width, 0);
377
+ rb_define_method(cTrack, "height", track_height, 0);
378
+
379
+ rb_define_method(cTrack, "id", track_id, 0);
380
+ rb_define_method(cTrack, "delete", track_delete, 0);
381
+ rb_define_method(cTrack, "enabled?", track_enabled, 0);
382
+ rb_define_method(cTrack, "enable", track_enable, 0);
383
+ rb_define_method(cTrack, "disable", track_disable, 0);
384
+ rb_define_method(cTrack, "volume", track_get_volume, 0);
385
+ rb_define_method(cTrack, "volume=", track_set_volume, 1);
386
+ rb_define_method(cTrack, "offset", track_get_offset, 0);
387
+ rb_define_method(cTrack, "offset=", track_set_offset, 1);
388
+ rb_define_method(cTrack, "new_video_media", track_new_video_media, 0);
389
+ rb_define_method(cTrack, "new_audio_media", track_new_audio_media, 0);
390
+ rb_define_method(cTrack, "new_text_media", track_new_text_media, 0);
391
+ }
@@ -0,0 +1,10 @@
1
+ module QuickTime
2
+ # see ext/exporter.c for additional methods
3
+ class Exporter
4
+ attr_reader :movie
5
+
6
+ def initialize(movie)
7
+ @movie = movie
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,102 @@
1
+ module QuickTime
2
+ # see ext/movie.c for additional methods
3
+ class Movie
4
+ # Opens a movie at filepath.
5
+ def self.open(filepath)
6
+ new.load_from_file(filepath)
7
+ end
8
+
9
+ # Returns a new, empty movie.
10
+ def self.empty
11
+ new.load_empty
12
+ end
13
+
14
+ # Returns the length of this movie in seconds
15
+ # using raw_duration and time_scale.
16
+ def duration
17
+ raw_duration.to_f/time_scale
18
+ end
19
+
20
+ # Returns the bounding width of this movie in number of pixels.
21
+ def width
22
+ bounds[:right] - bounds[:left]
23
+ end
24
+
25
+ # Returns the bounding height of this movie in number of pixels.
26
+ def height
27
+ bounds[:bottom] - bounds[:top]
28
+ end
29
+
30
+ # Returns an array of tracks in this movie.
31
+ def tracks
32
+ (1..track_count).map do |i|
33
+ Track.new.load_from_movie(self, i)
34
+ end
35
+ end
36
+
37
+ # Returns an array of audio tracks in this movie.
38
+ def audio_tracks
39
+ tracks.select { |t| t.audio? }
40
+ end
41
+
42
+ # Returns an array of video tracks in this movie.
43
+ def video_tracks
44
+ tracks.select { |t| t.video? }
45
+ end
46
+
47
+ # Returns an array of text tracks in this movie.
48
+ def text_tracks
49
+ tracks.select { |t| t.text? }
50
+ end
51
+
52
+ # Returns an Exporter instance for this movie.
53
+ def exporter
54
+ Exporter.new(self)
55
+ end
56
+
57
+ # Convenience method for exporting the movie. See Exporter::export.
58
+ def export(*args, &block)
59
+ exporter.export(*args, &block)
60
+ end
61
+
62
+ # Creates a new video track with given width/height on movie and returns it.
63
+ def new_video_track(width, height)
64
+ track = new_track(width, height)
65
+ track.new_video_media
66
+ track
67
+ end
68
+
69
+ # Creates a new audio track with given width/height on movie and returns it.
70
+ def new_audio_track(width, height)
71
+ track = new_track(width, height)
72
+ track.new_audio_media
73
+ track
74
+ end
75
+
76
+ # Creates a new text track with given width/height on movie and returns it.
77
+ def new_text_track(width, height)
78
+ track = new_track(width, height)
79
+ track.new_text_media
80
+ track
81
+ end
82
+
83
+ # Exports a frame of the movie at the given time (in seconds) to the given file.
84
+ # The image format is automatically determined from the file extension. If this
85
+ # cannot be determined from the extension then you can use export_image_type to
86
+ # specify the ostype manually.
87
+ def export_image(filepath, seconds)
88
+ # TODO support more file types
89
+ type = case File.extname(filepath).downcase
90
+ when '.pct', '.pict' then 'PICT'
91
+ when '.tif', '.tiff' then 'TIFF'
92
+ when '.jpg', '.jpeg' then 'JPEG'
93
+ when '.png' then 'PNGf'
94
+ when '.tga' then 'TPIC'
95
+ when '.bmp' then 'BMPf'
96
+ when '.psd' then '8BPS'
97
+ else raise QuickTime::Error, "Unable to guess ostype from file extension of #{filepath}"
98
+ end
99
+ export_image_type(filepath, seconds, type)
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,31 @@
1
+ module QuickTime
2
+ # see ext/track.c for additional methods
3
+ class Track
4
+ # Returns the length of this track in seconds
5
+ # using raw_duration and time_scale.
6
+ def duration
7
+ raw_duration.to_f/time_scale
8
+ end
9
+
10
+ # The average frame_rate for this track. May not be exact.
11
+ def frame_rate # what about odd frame rates such as 29.97?
12
+ frame_count/duration
13
+ end
14
+
15
+ # Returns true/false depending on if track is an audio track.
16
+ def audio?
17
+ media_type == :audio
18
+ end
19
+
20
+ # Returns true/false depending on if track is a video track.
21
+ def video?
22
+ media_type == :video
23
+ end
24
+
25
+ # Returns true/false depending on if track is a text track.
26
+ def text?
27
+ media_type == :text
28
+ end
29
+
30
+ end
31
+ end
data/lib/rmov.rb ADDED
@@ -0,0 +1,12 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
3
+ require File.dirname(__FILE__) + '/../ext/rmov_ext'
4
+
5
+ require 'quicktime/movie'
6
+ require 'quicktime/track'
7
+ require 'quicktime/exporter'
8
+
9
+
10
+ # RMov is made up of several parts. To start, see QuickTime::Movie.
11
+ module QuickTime
12
+ end
data/rmov.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{rmov}
5
+ s.version = "0.1.4"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Ryan Bates"]
9
+ s.date = %q{2009-02-24}
10
+ s.description = %q{Ruby wrapper for the QuickTime C API.}
11
+ s.email = %q{ryan (at) railscasts (dot) com}
12
+ s.extensions = ["ext/extconf.rb"]
13
+ s.extra_rdoc_files = ["CHANGELOG", "ext/exporter.c", "ext/extconf.rb", "ext/movie.c", "ext/rmov_ext.c", "ext/rmov_ext.h", "ext/track.c", "lib/quicktime/exporter.rb", "lib/quicktime/movie.rb", "lib/quicktime/track.rb", "lib/rmov.rb", "LICENSE", "README.rdoc", "tasks/setup.rake", "tasks/spec.rake", "TODO"]
14
+ s.files = ["CHANGELOG", "ext/exporter.c", "ext/extconf.rb", "ext/movie.c", "ext/rmov_ext.c", "ext/rmov_ext.h", "ext/track.c", "lib/quicktime/exporter.rb", "lib/quicktime/movie.rb", "lib/quicktime/track.rb", "lib/rmov.rb", "LICENSE", "Manifest", "Rakefile", "README.rdoc", "spec/fixtures/settings.st", "spec/quicktime/exporter_spec.rb", "spec/quicktime/movie_spec.rb", "spec/quicktime/track_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/setup.rake", "tasks/spec.rake", "TODO", "rmov.gemspec"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://github.com/ryanb/rmov}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rmov", "--main", "README.rdoc"]
18
+ s.require_paths = ["lib", "ext"]
19
+ s.rubyforge_project = %q{rmov}
20
+ s.rubygems_version = %q{1.3.1}
21
+ s.summary = %q{Ruby wrapper for the QuickTime C API.}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 2
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ else
29
+ end
30
+ else
31
+ end
32
+ end
Binary file
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+
3
+ describe QuickTime::Exporter do
4
+ it "should raise error when saving with no settings" do
5
+ lambda { QuickTime::Exporter.new(nil).save_settings('foo') }.should raise_error(QuickTime::Error)
6
+ end
7
+
8
+ it "should raise error when atempting to load no file" do
9
+ lambda { QuickTime::Exporter.new(nil).load_settings('foo/bar/baz') }.should raise_error(QuickTime::Error)
10
+ end
11
+
12
+ describe "loaded settings.st" do
13
+ before(:each) do
14
+ @load_path = File.dirname(__FILE__) + '/../fixtures/settings.st'
15
+ @exporter = QuickTime::Exporter.new(nil)
16
+ @exporter.load_settings(@load_path)
17
+ end
18
+
19
+ it "should be able to save settings to file" do
20
+ save_path = File.dirname(__FILE__) + '/../output/saved_settings.st'
21
+ @exporter.save_settings(save_path)
22
+ File.size(save_path).should == File.size(@load_path)
23
+ end
24
+
25
+ it "should complain when attempting to save to an invalid file" do
26
+ lambda { @exporter.save_settings('foo/bar/baz') }.should raise_error(QuickTime::Error)
27
+ end
28
+ end
29
+ end