one-k-rmov 0.1.4

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/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