rmov 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +19 -0
- data/Manifest +1 -0
- data/README.rdoc +33 -7
- data/Rakefile +1 -1
- data/ext/exporter.c +2 -2
- data/ext/movie.c +83 -64
- data/ext/rmov_ext.h +2 -0
- data/ext/track.c +104 -3
- data/lib/quicktime/movie.rb +74 -2
- data/lib/quicktime/track.rb +10 -0
- data/rmov.gemspec +27 -95
- data/spec/fixtures/dot.png +0 -0
- data/spec/fixtures/settings.st +0 -0
- data/spec/quicktime/exporter_spec.rb +18 -0
- data/spec/quicktime/movie_spec.rb +26 -1
- data/spec/quicktime/track_spec.rb +31 -3
- metadata +7 -4
data/CHANGELOG
CHANGED
@@ -1,9 +1,25 @@
|
|
1
|
+
0.1.5 (July 28, 2009)
|
2
|
+
|
3
|
+
* fixing saving export settings - closes #8
|
4
|
+
|
5
|
+
* improving how movie selection process happens providing more flexibility - closes #4 and #5
|
6
|
+
|
7
|
+
* adding enable_alpha method to enable alpha transparency for compositing - closes #3
|
8
|
+
|
9
|
+
* adding track transformations (rotation, scaling, and translation) - closes #1 and #2
|
10
|
+
|
11
|
+
* adding movie.save method to save a movie in place
|
12
|
+
|
13
|
+
* supporting .pict extension when exporting frame
|
14
|
+
|
15
|
+
|
1
16
|
0.1.4 (October 3rd, 2008)
|
2
17
|
|
3
18
|
* adding support for several export_image formats (PNG, JPEG, TIFF, TGA, BMP, PSD)
|
4
19
|
|
5
20
|
* adding movie.export_image as a generic way to export a frame to multiple formats
|
6
21
|
|
22
|
+
|
7
23
|
0.1.3 (October 3rd, 2008)
|
8
24
|
|
9
25
|
* some support for text tracks
|
@@ -12,6 +28,7 @@
|
|
12
28
|
|
13
29
|
* changing Quicktime module name to QuickTime to match proper casing
|
14
30
|
|
31
|
+
|
15
32
|
0.1.2 (October 3rd, 2008)
|
16
33
|
|
17
34
|
* movie.poster_time and movie.poster_time=(seconds) for getting and setting a movie's poster time
|
@@ -22,12 +39,14 @@
|
|
22
39
|
|
23
40
|
* QuickTime settings dialog comes into the forground properly
|
24
41
|
|
42
|
+
|
25
43
|
0.1.1 (October 3rd, 2008)
|
26
44
|
|
27
45
|
* RubyGems 1.3 compatibility (updated Echoe)
|
28
46
|
|
29
47
|
* fixing inline RDocs so call sequence is handled properly
|
30
48
|
|
49
|
+
|
31
50
|
0.1.0 (October 2nd, 2008)
|
32
51
|
|
33
52
|
* initial release
|
data/Manifest
CHANGED
data/README.rdoc
CHANGED
@@ -17,7 +17,9 @@ And then load it in your project:
|
|
17
17
|
|
18
18
|
== Usage
|
19
19
|
|
20
|
-
|
20
|
+
There are many methods for editing, compositing, and exporting movies. Here are some examples.
|
21
|
+
|
22
|
+
=== Editing
|
21
23
|
|
22
24
|
movie1 = QuickTime::Movie.open("path/to/movie.mov")
|
23
25
|
movie2 = QuickTime::Movie.open("path/to/another_movie.mov")
|
@@ -32,12 +34,33 @@ Use this gem to open QuickTime movies and edit them to your liking.
|
|
32
34
|
# You can insert that part back into the movie at 8 seconds in
|
33
35
|
movie1.insert_movie(movie3, 8)
|
34
36
|
|
35
|
-
|
36
|
-
interface the first time around. The settings can then be saved to
|
37
|
-
a file. After that you can load these settings without interfering
|
38
|
-
the user with the dialog again.
|
37
|
+
=== Compositing
|
39
38
|
|
40
|
-
|
39
|
+
movie = QuickTime::Movie.open("path/to/movie.mov")
|
40
|
+
watermark_movie = QuickTime::Movie.open("path/to/watermark.png")
|
41
|
+
|
42
|
+
# add watermark track onto the entire length of the movie
|
43
|
+
movie.composite_movie(watermark_movie, 0, movie.duration)
|
44
|
+
|
45
|
+
# grab the watermark track
|
46
|
+
watermark = movie.video_tracks.last
|
47
|
+
|
48
|
+
# enable the alpha transparency of the png
|
49
|
+
watermark.enable_alpha
|
50
|
+
|
51
|
+
# make the watermark half the size
|
52
|
+
watermark.scale(0.5, 0.5)
|
53
|
+
|
54
|
+
# offset into lower left corner
|
55
|
+
watermark.translate(10, movie.height - watermark.height - 10)
|
56
|
+
|
57
|
+
=== Exporting
|
58
|
+
|
59
|
+
Usually exporting is done through a user interface the first time.
|
60
|
+
The settings can then be saved to a file. After that you can load
|
61
|
+
these settings without interfering the user with the dialog again.
|
62
|
+
|
63
|
+
exporter = movie.exporter
|
41
64
|
|
42
65
|
# if we already have saved the settings, load those
|
43
66
|
if File.exist? "settings.st"
|
@@ -56,7 +79,10 @@ the user with the dialog again.
|
|
56
79
|
puts "#{percent}% complete"
|
57
80
|
end
|
58
81
|
|
59
|
-
|
82
|
+
|
83
|
+
== Documentation
|
84
|
+
|
85
|
+
See QuickTime::Movie and QuickTime::Track in the RDoc for more information.
|
60
86
|
|
61
87
|
http://rmov.rubyforge.org
|
62
88
|
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
require 'echoe'
|
4
4
|
|
5
|
-
Echoe.new('rmov', '0.1.
|
5
|
+
Echoe.new('rmov', '0.1.5') do |p|
|
6
6
|
p.summary = "Ruby wrapper for the QuickTime C API."
|
7
7
|
p.description = "Ruby wrapper for the QuickTime C API."
|
8
8
|
p.url = "http://github.com/ryanb/rmov"
|
data/ext/exporter.c
CHANGED
@@ -93,7 +93,7 @@ static VALUE exporter_open_settings_dialog(VALUE obj)
|
|
93
93
|
// Bring this process to the front
|
94
94
|
err = TransformProcessType(¤t_process, kProcessTransformToForegroundApplication);
|
95
95
|
if (err != noErr) {
|
96
|
-
rb_raise(eQuickTime, "Error %d occurred while
|
96
|
+
rb_raise(eQuickTime, "Error %d occurred while bringing this application to the forground.", err);
|
97
97
|
}
|
98
98
|
SetFrontProcess(¤t_process);
|
99
99
|
|
@@ -176,7 +176,7 @@ static VALUE exporter_save_settings(VALUE obj, VALUE filepath)
|
|
176
176
|
if (!file) {
|
177
177
|
rb_raise(eQuickTime, "Unable to open file for saving at %s.", RSTRING(filepath)->ptr);
|
178
178
|
}
|
179
|
-
fwrite(
|
179
|
+
fwrite(*settings, GetHandleSize((Handle)settings), 1, file);
|
180
180
|
fclose(file);
|
181
181
|
|
182
182
|
return Qnil;
|
data/ext/movie.c
CHANGED
@@ -64,27 +64,29 @@ static VALUE movie_load_from_file(VALUE obj, VALUE filepath)
|
|
64
64
|
} else {
|
65
65
|
OSErr err;
|
66
66
|
FSSpec fs;
|
67
|
-
short
|
68
|
-
short
|
67
|
+
short resRefNum = -1;
|
68
|
+
short resId = 0;
|
69
69
|
Movie *movie = ALLOC(Movie);
|
70
70
|
|
71
71
|
err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
|
72
72
|
if (err != 0)
|
73
73
|
rb_raise(eQuickTime, "Error %d occurred while reading file at %s", err, RSTRING(filepath)->ptr);
|
74
74
|
|
75
|
-
err = OpenMovieFile(&fs, &
|
75
|
+
err = OpenMovieFile(&fs, &resRefNum, fsRdPerm);
|
76
76
|
if (err != 0)
|
77
77
|
rb_raise(eQuickTime, "Error %d occurred while opening movie at %s", err, RSTRING(filepath)->ptr);
|
78
78
|
|
79
|
-
err = NewMovieFromFile(movie,
|
79
|
+
err = NewMovieFromFile(movie, resRefNum, &resId, 0, newMovieActive, 0);
|
80
80
|
if (err != 0)
|
81
81
|
rb_raise(eQuickTime, "Error %d occurred while loading movie at %s", err, RSTRING(filepath)->ptr);
|
82
82
|
|
83
|
-
err = CloseMovieFile(
|
83
|
+
err = CloseMovieFile(resRefNum);
|
84
84
|
if (err != 0)
|
85
85
|
rb_raise(eQuickTime, "Error %d occurred while closing movie file at %s", err, RSTRING(filepath)->ptr);
|
86
86
|
|
87
87
|
RMOVIE(obj)->movie = *movie;
|
88
|
+
RMOVIE(obj)->filepath = RSTRING(filepath)->ptr;
|
89
|
+
RMOVIE(obj)->resId = resId;
|
88
90
|
|
89
91
|
return obj;
|
90
92
|
}
|
@@ -158,44 +160,32 @@ static VALUE movie_track_count(VALUE obj)
|
|
158
160
|
}
|
159
161
|
|
160
162
|
/*
|
161
|
-
call-seq:
|
163
|
+
call-seq: select(position, duration)
|
162
164
|
|
163
|
-
|
164
|
-
|
165
|
-
You can track the progress of this operation by passing a block to this
|
166
|
-
method. It will be called regularly during the process and pass the
|
167
|
-
percentage complete (0.0 to 1.0) as an argument to the block.
|
165
|
+
Select a portion of a movie. Both position and duration should be
|
166
|
+
floats representing seconds.
|
168
167
|
*/
|
169
|
-
static VALUE
|
168
|
+
static VALUE movie_select(VALUE obj, VALUE position, VALUE duration)
|
170
169
|
{
|
171
|
-
|
172
|
-
SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
|
173
|
-
|
174
|
-
SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, position), 0);
|
175
|
-
AddMovieSelection(MOVIE(obj), MOVIE(src));
|
176
|
-
|
177
|
-
if (rb_block_given_p())
|
178
|
-
SetMovieProgressProc(MOVIE(obj), 0, 0);
|
179
|
-
|
170
|
+
SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, position), MOVIE_TIME(obj, duration));
|
180
171
|
return obj;
|
181
172
|
}
|
182
173
|
|
183
174
|
/*
|
184
|
-
call-seq:
|
175
|
+
call-seq: add_into_selection(movie)
|
176
|
+
|
177
|
+
Adds the tracks of given movie into called movie's current selection.
|
185
178
|
|
186
|
-
Inserts given movie into called movie at given position (in seconds).
|
187
|
-
|
188
179
|
You can track the progress of this operation by passing a block to this
|
189
180
|
method. It will be called regularly during the process and pass the
|
190
181
|
percentage complete (0.0 to 1.0) as an argument to the block.
|
191
182
|
*/
|
192
|
-
static VALUE
|
183
|
+
static VALUE movie_add_into_selection(VALUE obj, VALUE src)
|
193
184
|
{
|
194
185
|
if (rb_block_given_p())
|
195
186
|
SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
|
196
187
|
|
197
|
-
|
198
|
-
PasteMovieSelection(MOVIE(obj), MOVIE(src));
|
188
|
+
AddMovieSelection(MOVIE(obj), MOVIE(src));
|
199
189
|
|
200
190
|
if (rb_block_given_p())
|
201
191
|
SetMovieProgressProc(MOVIE(obj), 0, 0);
|
@@ -204,20 +194,19 @@ static VALUE movie_insert_movie(VALUE obj, VALUE src, VALUE position)
|
|
204
194
|
}
|
205
195
|
|
206
196
|
/*
|
207
|
-
call-seq:
|
197
|
+
call-seq: insert_into_selection(movie)
|
198
|
+
|
199
|
+
Inserts the given movie into called movie, replacing any current selection.
|
208
200
|
|
209
|
-
Adds given movie to the end of movie which this method is called on.
|
210
|
-
|
211
201
|
You can track the progress of this operation by passing a block to this
|
212
202
|
method. It will be called regularly during the process and pass the
|
213
203
|
percentage complete (0.0 to 1.0) as an argument to the block.
|
214
204
|
*/
|
215
|
-
static VALUE
|
205
|
+
static VALUE movie_insert_into_selection(VALUE obj, VALUE src)
|
216
206
|
{
|
217
207
|
if (rb_block_given_p())
|
218
208
|
SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
|
219
209
|
|
220
|
-
SetMovieSelection(MOVIE(obj), GetMovieDuration(MOVIE(obj)), 0);
|
221
210
|
PasteMovieSelection(MOVIE(obj), MOVIE(src));
|
222
211
|
|
223
212
|
if (rb_block_given_p())
|
@@ -227,37 +216,22 @@ static VALUE movie_append_movie(VALUE obj, VALUE src)
|
|
227
216
|
}
|
228
217
|
|
229
218
|
/*
|
230
|
-
call-seq:
|
219
|
+
call-seq: clone_selection()
|
231
220
|
|
232
|
-
|
233
|
-
|
234
|
-
*/
|
235
|
-
static VALUE movie_delete_section(VALUE obj, VALUE start, VALUE duration)
|
236
|
-
{
|
237
|
-
SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
|
238
|
-
ClearMovieSelection(MOVIE(obj));
|
239
|
-
return obj;
|
240
|
-
}
|
241
|
-
|
242
|
-
/*
|
243
|
-
call-seq: clone_section(start_time, duration) -> movie
|
221
|
+
Returns a new movie from the current selection. Does not modify original
|
222
|
+
movie.
|
244
223
|
|
245
|
-
Returns a new movie in the given section. Does not modify original
|
246
|
-
movie. Both start_time and duration should be floats representing
|
247
|
-
seconds.
|
248
|
-
|
249
224
|
You can track the progress of this operation by passing a block to this
|
250
225
|
method. It will be called regularly during the process and pass the
|
251
226
|
percentage complete (0.0 to 1.0) as an argument to the block.
|
252
227
|
*/
|
253
|
-
static VALUE
|
228
|
+
static VALUE movie_clone_selection(VALUE obj)
|
254
229
|
{
|
255
230
|
VALUE new_movie_obj = rb_obj_alloc(cMovie);
|
256
231
|
|
257
232
|
if (rb_block_given_p())
|
258
233
|
SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
|
259
234
|
|
260
|
-
SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
|
261
235
|
RMOVIE(new_movie_obj)->movie = CopyMovieSelection(MOVIE(obj));
|
262
236
|
|
263
237
|
if (rb_block_given_p())
|
@@ -267,24 +241,22 @@ static VALUE movie_clone_section(VALUE obj, VALUE start, VALUE duration)
|
|
267
241
|
}
|
268
242
|
|
269
243
|
/*
|
270
|
-
call-seq:
|
244
|
+
call-seq: clip_selection()
|
245
|
+
|
246
|
+
Deletes current selection on movie and returns a new movie with that
|
247
|
+
content.
|
271
248
|
|
272
|
-
Deletes given section on movie and returns a new movie with that
|
273
|
-
section. Both start_time and duration should be floats representing
|
274
|
-
seconds.
|
275
|
-
|
276
249
|
You can track the progress of this operation by passing a block to this
|
277
250
|
method. It will be called regularly during the process and pass the
|
278
251
|
percentage complete (0.0 to 1.0) as an argument to the block.
|
279
252
|
*/
|
280
|
-
static VALUE
|
253
|
+
static VALUE movie_clip_selection(VALUE obj)
|
281
254
|
{
|
282
255
|
VALUE new_movie_obj = rb_obj_alloc(cMovie);
|
283
256
|
|
284
257
|
if (rb_block_given_p())
|
285
258
|
SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
|
286
259
|
|
287
|
-
SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
|
288
260
|
RMOVIE(new_movie_obj)->movie = CutMovieSelection(MOVIE(obj));
|
289
261
|
|
290
262
|
if (rb_block_given_p())
|
@@ -293,6 +265,17 @@ static VALUE movie_clip_section(VALUE obj, VALUE start, VALUE duration)
|
|
293
265
|
return new_movie_obj;
|
294
266
|
}
|
295
267
|
|
268
|
+
/*
|
269
|
+
call-seq: delete_selection()
|
270
|
+
|
271
|
+
Removes the portion of the movie which is selected.
|
272
|
+
*/
|
273
|
+
static VALUE movie_delete_selection(VALUE obj)
|
274
|
+
{
|
275
|
+
ClearMovieSelection(MOVIE(obj));
|
276
|
+
return obj;
|
277
|
+
}
|
278
|
+
|
296
279
|
/*
|
297
280
|
call-seq: changed?() -> bool
|
298
281
|
|
@@ -345,6 +328,41 @@ static VALUE movie_flatten(VALUE obj, VALUE filepath)
|
|
345
328
|
return new_movie_obj;
|
346
329
|
}
|
347
330
|
|
331
|
+
|
332
|
+
/*
|
333
|
+
call-seq: save()
|
334
|
+
|
335
|
+
Saves the movie to the current file.
|
336
|
+
*/
|
337
|
+
static VALUE movie_save(VALUE obj)
|
338
|
+
{
|
339
|
+
OSErr err;
|
340
|
+
FSSpec fs;
|
341
|
+
short resRefNum = -1;
|
342
|
+
|
343
|
+
if (!RMOVIE(obj)->filepath || !RMOVIE(obj)->resId) {
|
344
|
+
rb_raise(eQuickTime, "Unable to save movie because it does not have an associated file.");
|
345
|
+
} else {
|
346
|
+
err = NativePathNameToFSSpec(RMOVIE(obj)->filepath, &fs, 0);
|
347
|
+
if (err != 0)
|
348
|
+
rb_raise(eQuickTime, "Error %d occurred while reading file at %s", err, RMOVIE(obj)->filepath);
|
349
|
+
|
350
|
+
err = OpenMovieFile(&fs, &resRefNum, fsWrPerm);
|
351
|
+
if (err != 0)
|
352
|
+
rb_raise(eQuickTime, "Error %d occurred while opening movie at %s", err, RMOVIE(obj)->filepath);
|
353
|
+
|
354
|
+
err = UpdateMovieResource(MOVIE(obj), resRefNum, RMOVIE(obj)->resId, 0);
|
355
|
+
if (err != 0)
|
356
|
+
rb_raise(eQuickTime, "Error %d occurred while saving movie file", err);
|
357
|
+
|
358
|
+
err = CloseMovieFile(resRefNum);
|
359
|
+
if (err != 0)
|
360
|
+
rb_raise(eQuickTime, "Error %d occurred while closing movie file at %s", err, RMOVIE(obj)->filepath);
|
361
|
+
|
362
|
+
return Qnil;
|
363
|
+
}
|
364
|
+
}
|
365
|
+
|
348
366
|
/*
|
349
367
|
call-seq: export_image_type(filepath, time, ostype)
|
350
368
|
|
@@ -440,12 +458,12 @@ void Init_quicktime_movie()
|
|
440
458
|
rb_define_method(cMovie, "time_scale", movie_time_scale, 0);
|
441
459
|
rb_define_method(cMovie, "bounds", movie_bounds, 0);
|
442
460
|
rb_define_method(cMovie, "track_count", movie_track_count, 0);
|
443
|
-
rb_define_method(cMovie, "
|
444
|
-
rb_define_method(cMovie, "
|
445
|
-
rb_define_method(cMovie, "
|
446
|
-
rb_define_method(cMovie, "
|
447
|
-
rb_define_method(cMovie, "
|
448
|
-
rb_define_method(cMovie, "
|
461
|
+
rb_define_method(cMovie, "select", movie_select, 2);
|
462
|
+
rb_define_method(cMovie, "add_into_selection", movie_add_into_selection, 1);
|
463
|
+
rb_define_method(cMovie, "insert_into_selection", movie_insert_into_selection, 1);
|
464
|
+
rb_define_method(cMovie, "clone_selection", movie_clone_selection, 0);
|
465
|
+
rb_define_method(cMovie, "clip_selection", movie_clip_selection, 0);
|
466
|
+
rb_define_method(cMovie, "delete_selection", movie_delete_selection, 0);
|
449
467
|
rb_define_method(cMovie, "changed?", movie_changed, 0);
|
450
468
|
rb_define_method(cMovie, "clear_changed_status", movie_clear_changed_status, 0);
|
451
469
|
rb_define_method(cMovie, "flatten", movie_flatten, 1);
|
@@ -454,4 +472,5 @@ void Init_quicktime_movie()
|
|
454
472
|
rb_define_method(cMovie, "poster_time", movie_get_poster_time, 0);
|
455
473
|
rb_define_method(cMovie, "poster_time=", movie_set_poster_time, 1);
|
456
474
|
rb_define_method(cMovie, "new_track", movie_new_track, 2);
|
475
|
+
rb_define_method(cMovie, "save", movie_save, 0);
|
457
476
|
}
|
data/ext/rmov_ext.h
CHANGED
data/ext/track.c
CHANGED
@@ -191,7 +191,7 @@ static VALUE track_set_offset(VALUE obj, VALUE seconds)
|
|
191
191
|
}
|
192
192
|
|
193
193
|
/*
|
194
|
-
call-seq: new_video_media
|
194
|
+
call-seq: new_video_media()
|
195
195
|
|
196
196
|
Creates a new video media for this track.
|
197
197
|
|
@@ -205,7 +205,7 @@ static VALUE track_new_video_media(VALUE obj)
|
|
205
205
|
}
|
206
206
|
|
207
207
|
/*
|
208
|
-
call-seq: new_audio_media
|
208
|
+
call-seq: new_audio_media()
|
209
209
|
|
210
210
|
Creates a new audio media for this track.
|
211
211
|
|
@@ -219,7 +219,7 @@ static VALUE track_new_audio_media(VALUE obj)
|
|
219
219
|
}
|
220
220
|
|
221
221
|
/*
|
222
|
-
call-seq: new_text_media
|
222
|
+
call-seq: new_text_media()
|
223
223
|
|
224
224
|
Creates a new text media for this track.
|
225
225
|
|
@@ -232,6 +232,101 @@ static VALUE track_new_text_media(VALUE obj)
|
|
232
232
|
return obj;
|
233
233
|
}
|
234
234
|
|
235
|
+
/*
|
236
|
+
call-seq: enable_alpha()
|
237
|
+
|
238
|
+
Enable the straight alpha graphic mode for this track.
|
239
|
+
|
240
|
+
This is best used on an overlayed video track which includes some
|
241
|
+
alpha transparency (such as in a PNG image).
|
242
|
+
*/
|
243
|
+
static VALUE track_enable_alpha(VALUE obj)
|
244
|
+
{
|
245
|
+
MediaSetGraphicsMode(GetMediaHandler(TRACK_MEDIA(obj)), graphicsModeStraightAlpha, 0);
|
246
|
+
return obj;
|
247
|
+
}
|
248
|
+
|
249
|
+
/*
|
250
|
+
call-seq: scale(width, height)
|
251
|
+
|
252
|
+
Scale the track's size by width and height respectively.
|
253
|
+
|
254
|
+
The value passed is a relative float where "1" is the current size.
|
255
|
+
*/
|
256
|
+
static VALUE track_scale(VALUE obj, VALUE width, VALUE height)
|
257
|
+
{
|
258
|
+
MatrixRecord matrix;
|
259
|
+
GetTrackMatrix(TRACK(obj), &matrix);
|
260
|
+
ScaleMatrix(&matrix, FloatToFixed(NUM2DBL(width)), FloatToFixed(NUM2DBL(height)), 0, 0);
|
261
|
+
SetTrackMatrix(TRACK(obj), &matrix);
|
262
|
+
return obj;
|
263
|
+
}
|
264
|
+
|
265
|
+
/*
|
266
|
+
call-seq: translate(x, y)
|
267
|
+
|
268
|
+
Offset a track's position by x and y values respectively.
|
269
|
+
|
270
|
+
Values should be in pixels.
|
271
|
+
*/
|
272
|
+
static VALUE track_translate(VALUE obj, VALUE x, VALUE y)
|
273
|
+
{
|
274
|
+
MatrixRecord matrix;
|
275
|
+
GetTrackMatrix(TRACK(obj), &matrix);
|
276
|
+
TranslateMatrix(&matrix, FloatToFixed(NUM2DBL(x)), FloatToFixed(NUM2DBL(y)));
|
277
|
+
SetTrackMatrix(TRACK(obj), &matrix);
|
278
|
+
return obj;
|
279
|
+
}
|
280
|
+
|
281
|
+
/*
|
282
|
+
call-seq: rotate(degrees)
|
283
|
+
|
284
|
+
Rotate the track by the given number of degrees.
|
285
|
+
*/
|
286
|
+
static VALUE track_rotate(VALUE obj, VALUE degrees)
|
287
|
+
{
|
288
|
+
MatrixRecord matrix;
|
289
|
+
GetTrackMatrix(TRACK(obj), &matrix);
|
290
|
+
RotateMatrix(&matrix, FloatToFixed(NUM2DBL(degrees)), 0, 0);
|
291
|
+
SetTrackMatrix(TRACK(obj), &matrix);
|
292
|
+
return obj;
|
293
|
+
}
|
294
|
+
|
295
|
+
/*
|
296
|
+
call-seq: bounds() -> bounds_hash
|
297
|
+
|
298
|
+
Returns a hash of boundaries. The hash contains four keys: :left, :top,
|
299
|
+
:right, :bottom. Each holds an integer representing the pixel value.
|
300
|
+
*/
|
301
|
+
static VALUE track_bounds(VALUE obj)
|
302
|
+
{
|
303
|
+
VALUE bounds_hash = rb_hash_new();
|
304
|
+
RgnHandle region;
|
305
|
+
Rect bounds;
|
306
|
+
region = GetTrackDisplayBoundsRgn(TRACK(obj));
|
307
|
+
GetRegionBounds(region, &bounds);
|
308
|
+
DisposeRgn(region);
|
309
|
+
rb_hash_aset(bounds_hash, ID2SYM(rb_intern("left")), INT2NUM(bounds.left));
|
310
|
+
rb_hash_aset(bounds_hash, ID2SYM(rb_intern("top")), INT2NUM(bounds.top));
|
311
|
+
rb_hash_aset(bounds_hash, ID2SYM(rb_intern("right")), INT2NUM(bounds.right));
|
312
|
+
rb_hash_aset(bounds_hash, ID2SYM(rb_intern("bottom")), INT2NUM(bounds.bottom));
|
313
|
+
return bounds_hash;
|
314
|
+
}
|
315
|
+
|
316
|
+
/*
|
317
|
+
call-seq: reset_transformations()
|
318
|
+
|
319
|
+
Revert any transformations (scale, translate, rotate) performed on this track.
|
320
|
+
*/
|
321
|
+
static VALUE track_reset_transformations(VALUE obj)
|
322
|
+
{
|
323
|
+
MatrixRecord matrix;
|
324
|
+
GetTrackMatrix(TRACK(obj), &matrix);
|
325
|
+
SetIdentityMatrix(&matrix);
|
326
|
+
SetTrackMatrix(TRACK(obj), &matrix);
|
327
|
+
return obj;
|
328
|
+
}
|
329
|
+
|
235
330
|
void Init_quicktime_track()
|
236
331
|
{
|
237
332
|
VALUE mQuickTime;
|
@@ -255,4 +350,10 @@ void Init_quicktime_track()
|
|
255
350
|
rb_define_method(cTrack, "new_video_media", track_new_video_media, 0);
|
256
351
|
rb_define_method(cTrack, "new_audio_media", track_new_audio_media, 0);
|
257
352
|
rb_define_method(cTrack, "new_text_media", track_new_text_media, 0);
|
353
|
+
rb_define_method(cTrack, "enable_alpha", track_enable_alpha, 0);
|
354
|
+
rb_define_method(cTrack, "scale", track_scale, 2);
|
355
|
+
rb_define_method(cTrack, "translate", track_translate, 2);
|
356
|
+
rb_define_method(cTrack, "rotate", track_rotate, 1);
|
357
|
+
rb_define_method(cTrack, "bounds", track_bounds, 0);
|
358
|
+
rb_define_method(cTrack, "reset_transformations", track_reset_transformations, 0);
|
258
359
|
}
|
data/lib/quicktime/movie.rb
CHANGED
@@ -87,10 +87,10 @@ module QuickTime
|
|
87
87
|
def export_image(filepath, seconds)
|
88
88
|
# TODO support more file types
|
89
89
|
type = case File.extname(filepath).downcase
|
90
|
-
when '.pct'
|
91
|
-
when '.png' then 'PNGf'
|
90
|
+
when '.pct', '.pict' then 'PICT'
|
92
91
|
when '.tif', '.tiff' then 'TIFF'
|
93
92
|
when '.jpg', '.jpeg' then 'JPEG'
|
93
|
+
when '.png' then 'PNGf'
|
94
94
|
when '.tga' then 'TPIC'
|
95
95
|
when '.bmp' then 'BMPf'
|
96
96
|
when '.psd' then '8BPS'
|
@@ -98,5 +98,77 @@ module QuickTime
|
|
98
98
|
end
|
99
99
|
export_image_type(filepath, seconds, type)
|
100
100
|
end
|
101
|
+
|
102
|
+
# Reset selection to beginning
|
103
|
+
def deselect
|
104
|
+
select(0, 0)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Adds the tracks of given movie into called movie. Position will default to
|
108
|
+
# beginning of movie. Duration will default to length of given movie.
|
109
|
+
#
|
110
|
+
# You can track the progress of this operation by passing a block to this
|
111
|
+
# method. It will be called regularly during the process and pass the
|
112
|
+
# percentage complete (0.0 to 1.0) as an argument to the block.
|
113
|
+
def composite_movie(movie, position = 0, duration = 0, &block)
|
114
|
+
select(position, duration)
|
115
|
+
add_into_selection(movie, &block)
|
116
|
+
deselect
|
117
|
+
end
|
118
|
+
|
119
|
+
# Adds given movie to the end of movie which this method is called on.
|
120
|
+
#
|
121
|
+
# You can track the progress of this operation by passing a block to this
|
122
|
+
# method. It will be called regularly during the process and pass the
|
123
|
+
# percentage complete (0.0 to 1.0) as an argument to the block.
|
124
|
+
def append_movie(movie, &block)
|
125
|
+
select(duration, 0)
|
126
|
+
insert_into_selection(movie, &block)
|
127
|
+
deselect
|
128
|
+
end
|
129
|
+
|
130
|
+
# Inserts given movie into called movie. The position defaults to the beginning
|
131
|
+
# of the movie. If a duration is passed, that amount of the movie will be replaced.
|
132
|
+
#
|
133
|
+
# You can track the progress of this operation by passing a block to this
|
134
|
+
# method. It will be called regularly during the process and pass the
|
135
|
+
# percentage complete (0.0 to 1.0) as an argument to the block.
|
136
|
+
def insert_movie(movie, position = 0, duration = 0, &block)
|
137
|
+
select(position, duration)
|
138
|
+
insert_into_selection(movie, &block)
|
139
|
+
deselect
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns a new movie from the specified portion of called movie.
|
143
|
+
#
|
144
|
+
# You can track the progress of this operation by passing a block to this
|
145
|
+
# method. It will be called regularly during the process and pass the
|
146
|
+
# percentage complete (0.0 to 1.0) as an argument to the block.
|
147
|
+
def clone_section(position = 0, duration = 0, &block)
|
148
|
+
select(position, duration)
|
149
|
+
movie = clone_selection(&block)
|
150
|
+
deselect
|
151
|
+
movie
|
152
|
+
end
|
153
|
+
|
154
|
+
# Deletes the specified section on movie and returns a new movie
|
155
|
+
# with that content.
|
156
|
+
#
|
157
|
+
# You can track the progress of this operation by passing a block to this
|
158
|
+
# method. It will be called regularly during the process and pass the
|
159
|
+
# percentage complete (0.0 to 1.0) as an argument to the block.
|
160
|
+
def clip_section(position = 0, duration = 0, &block)
|
161
|
+
select(position, duration)
|
162
|
+
movie = clip_selection(&block)
|
163
|
+
deselect
|
164
|
+
movie
|
165
|
+
end
|
166
|
+
|
167
|
+
# Deletes the specified section on movie.
|
168
|
+
def delete_section(position = 0, duration = 0)
|
169
|
+
select(position, duration)
|
170
|
+
delete_selection
|
171
|
+
deselect
|
172
|
+
end
|
101
173
|
end
|
102
174
|
end
|
data/lib/quicktime/track.rb
CHANGED
@@ -26,5 +26,15 @@ module QuickTime
|
|
26
26
|
def text?
|
27
27
|
media_type == :text
|
28
28
|
end
|
29
|
+
|
30
|
+
# Returns the bounding width of this track in number of pixels.
|
31
|
+
def width
|
32
|
+
bounds[:right] - bounds[:left]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the bounding height of this track in number of pixels.
|
36
|
+
def height
|
37
|
+
bounds[:bottom] - bounds[:top]
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
data/rmov.gemspec
CHANGED
@@ -1,99 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
1
2
|
|
2
|
-
|
3
|
-
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{rmov}
|
5
|
+
s.version = "0.1.5"
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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-07-28}
|
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/dot.png", "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.homepage = %q{http://github.com/ryanb/rmov}
|
16
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Rmov", "--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib", "ext"]
|
18
|
+
s.rubyforge_project = %q{rmov}
|
19
|
+
s.rubygems_version = %q{1.3.3}
|
20
|
+
s.summary = %q{Ruby wrapper for the QuickTime C API.}
|
14
21
|
|
15
|
-
|
16
|
-
|
17
|
-
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
18
25
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
extra_rdoc_files:
|
26
|
-
- CHANGELOG
|
27
|
-
- ext/exporter.c
|
28
|
-
- ext/extconf.rb
|
29
|
-
- ext/movie.c
|
30
|
-
- ext/rmov_ext.c
|
31
|
-
- ext/rmov_ext.h
|
32
|
-
- ext/track.c
|
33
|
-
- lib/quicktime/exporter.rb
|
34
|
-
- lib/quicktime/movie.rb
|
35
|
-
- lib/quicktime/track.rb
|
36
|
-
- lib/rmov.rb
|
37
|
-
- LICENSE
|
38
|
-
- README.rdoc
|
39
|
-
- tasks/setup.rake
|
40
|
-
- tasks/spec.rake
|
41
|
-
- TODO
|
42
|
-
files:
|
43
|
-
- CHANGELOG
|
44
|
-
- ext/exporter.c
|
45
|
-
- ext/extconf.rb
|
46
|
-
- ext/movie.c
|
47
|
-
- ext/rmov_ext.c
|
48
|
-
- ext/rmov_ext.h
|
49
|
-
- ext/track.c
|
50
|
-
- lib/quicktime/exporter.rb
|
51
|
-
- lib/quicktime/movie.rb
|
52
|
-
- lib/quicktime/track.rb
|
53
|
-
- lib/rmov.rb
|
54
|
-
- LICENSE
|
55
|
-
- Manifest
|
56
|
-
- Rakefile
|
57
|
-
- README.rdoc
|
58
|
-
- spec/fixtures/settings.st
|
59
|
-
- spec/quicktime/exporter_spec.rb
|
60
|
-
- spec/quicktime/movie_spec.rb
|
61
|
-
- spec/quicktime/track_spec.rb
|
62
|
-
- spec/spec.opts
|
63
|
-
- spec/spec_helper.rb
|
64
|
-
- tasks/setup.rake
|
65
|
-
- tasks/spec.rake
|
66
|
-
- TODO
|
67
|
-
- rmov.gemspec
|
68
|
-
has_rdoc: true
|
69
|
-
homepage: http://github.com/ryanb/rmov
|
70
|
-
post_install_message:
|
71
|
-
rdoc_options:
|
72
|
-
- --line-numbers
|
73
|
-
- --inline-source
|
74
|
-
- --title
|
75
|
-
- Rmov
|
76
|
-
- --main
|
77
|
-
- README.rdoc
|
78
|
-
require_paths:
|
79
|
-
- lib
|
80
|
-
- ext
|
81
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
-
requirements:
|
83
|
-
- - ">="
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: "0"
|
86
|
-
version:
|
87
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
-
requirements:
|
89
|
-
- - ">="
|
90
|
-
- !ruby/object:Gem::Version
|
91
|
-
version: "1.2"
|
92
|
-
version:
|
93
|
-
requirements: []
|
94
|
-
|
95
|
-
rubyforge_project: rmov
|
96
|
-
rubygems_version: 1.2.0
|
97
|
-
specification_version: 2
|
98
|
-
summary: Ruby wrapper for the QuickTime C API.
|
99
|
-
test_files: []
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
else
|
28
|
+
end
|
29
|
+
else
|
30
|
+
end
|
31
|
+
end
|
Binary file
|
data/spec/fixtures/settings.st
CHANGED
Binary file
|
@@ -26,4 +26,22 @@ describe QuickTime::Exporter do
|
|
26
26
|
lambda { @exporter.save_settings('foo/bar/baz') }.should raise_error(QuickTime::Error)
|
27
27
|
end
|
28
28
|
end
|
29
|
+
describe "example.mov" do
|
30
|
+
before(:each) do
|
31
|
+
@movie = QuickTime::Movie.open(File.dirname(__FILE__) + '/../fixtures/example.mov')
|
32
|
+
@exporter = @movie.exporter
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should be able to export from loaded settings.st" do
|
36
|
+
load_path = File.dirname(__FILE__) + '/../fixtures/settings.st'
|
37
|
+
path = File.dirname(__FILE__) + '/../output/loaded_exported_example.mov'
|
38
|
+
File.delete(path) rescue nil
|
39
|
+
|
40
|
+
@exporter.load_settings(load_path)
|
41
|
+
@exporter.export(path)
|
42
|
+
exported_movie = QuickTime::Movie.open(path)
|
43
|
+
exported_movie.duration.should == @movie.duration
|
44
|
+
exported_movie.tracks.size == @movie.tracks.size
|
45
|
+
end
|
46
|
+
end
|
29
47
|
end
|
@@ -111,12 +111,28 @@ describe QuickTime::Movie do
|
|
111
111
|
|
112
112
|
it "flatten should save movie into file" do
|
113
113
|
path = File.dirname(__FILE__) + '/../output/flattened_example.mov'
|
114
|
-
File.delete(path)
|
114
|
+
File.delete(path) if File.exist?(path)
|
115
115
|
@movie.flatten(path)
|
116
116
|
mov = QuickTime::Movie.open(path)
|
117
117
|
mov.duration.should == 3.1
|
118
118
|
end
|
119
119
|
|
120
|
+
it "save should update movie in current file" do
|
121
|
+
path = File.dirname(__FILE__) + '/../output/saved_example.mov'
|
122
|
+
File.delete(path) if File.exist?(path)
|
123
|
+
@movie.flatten(path)
|
124
|
+
mov = QuickTime::Movie.open(path)
|
125
|
+
mov.audio_tracks.each { |t| t.delete } # delete track to demonstrate change
|
126
|
+
mov.save
|
127
|
+
mov2 = QuickTime::Movie.open(path)
|
128
|
+
mov2.audio_tracks.should be_empty
|
129
|
+
end
|
130
|
+
|
131
|
+
it "save should raise exception when saving new movie without filepath" do
|
132
|
+
mov = QuickTime::Movie.empty
|
133
|
+
lambda { mov.save }.should raise_error
|
134
|
+
end
|
135
|
+
|
120
136
|
it "export_pict should output a pict file at a given duration" do
|
121
137
|
path = File.dirname(__FILE__) + '/../output/example.pct'
|
122
138
|
File.delete(path) rescue nil
|
@@ -137,6 +153,15 @@ describe QuickTime::Movie do
|
|
137
153
|
@movie.poster_time = 2.1
|
138
154
|
@movie.poster_time.should == 2.1
|
139
155
|
end
|
156
|
+
|
157
|
+
it "should overlay 2nd movie with transparency" do
|
158
|
+
m2 = QuickTime::Movie.open(File.dirname(__FILE__) + '/../fixtures/dot.png')
|
159
|
+
@movie.composite_movie(m2, 0)
|
160
|
+
@movie.video_tracks.last.enable_alpha
|
161
|
+
File.delete(File.dirname(__FILE__) + '/../output/transparent.mov') rescue nil
|
162
|
+
@movie.flatten(File.dirname(__FILE__) + '/../output/transparent.mov')
|
163
|
+
# this test needs to be checked manually by looking at the output movie
|
164
|
+
end
|
140
165
|
end
|
141
166
|
|
142
167
|
describe "empty movie" do
|
@@ -6,7 +6,7 @@ describe QuickTime::Track do
|
|
6
6
|
@movie = QuickTime::Movie.open(File.dirname(__FILE__) + '/../fixtures/example.mov')
|
7
7
|
end
|
8
8
|
|
9
|
-
describe "
|
9
|
+
describe "video track" do
|
10
10
|
before(:each) do
|
11
11
|
@track = @movie.video_tracks.first
|
12
12
|
end
|
@@ -49,9 +49,37 @@ describe QuickTime::Track do
|
|
49
49
|
@track.offset = 2.5
|
50
50
|
@track.offset.should == 2.5
|
51
51
|
end
|
52
|
+
|
53
|
+
it "should be able to scale size of track" do
|
54
|
+
@track.scale(0.5, 0.5)
|
55
|
+
@track.width.should == 30
|
56
|
+
@track.height.should == 25
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should be able to move (translate) position of track" do
|
60
|
+
@track.translate(10, 20)
|
61
|
+
@track.bounds[:left].should == 10
|
62
|
+
@track.bounds[:top].should == 20
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should reset transformations" do
|
66
|
+
@track.scale(0.5, 0.5)
|
67
|
+
@track.translate(10, 20)
|
68
|
+
@track.reset_transformations
|
69
|
+
@track.bounds[:left].should == 0
|
70
|
+
@track.bounds[:top].should == 0
|
71
|
+
@track.bounds[:right].should == 60
|
72
|
+
@track.bounds[:bottom].should == 50
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should rotate track" do
|
76
|
+
@track.rotate(90)
|
77
|
+
@track.width.should == 50
|
78
|
+
@track.height.should == 60
|
79
|
+
end
|
52
80
|
end
|
53
|
-
|
54
|
-
describe "
|
81
|
+
|
82
|
+
describe "audio track" do
|
55
83
|
before(:each) do
|
56
84
|
@track = @movie.audio_tracks.first
|
57
85
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rmov
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Bates
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-07-28 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -52,6 +52,7 @@ files:
|
|
52
52
|
- Manifest
|
53
53
|
- Rakefile
|
54
54
|
- README.rdoc
|
55
|
+
- spec/fixtures/dot.png
|
55
56
|
- spec/fixtures/settings.st
|
56
57
|
- spec/quicktime/exporter_spec.rb
|
57
58
|
- spec/quicktime/movie_spec.rb
|
@@ -64,6 +65,8 @@ files:
|
|
64
65
|
- rmov.gemspec
|
65
66
|
has_rdoc: true
|
66
67
|
homepage: http://github.com/ryanb/rmov
|
68
|
+
licenses: []
|
69
|
+
|
67
70
|
post_install_message:
|
68
71
|
rdoc_options:
|
69
72
|
- --line-numbers
|
@@ -90,9 +93,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
93
|
requirements: []
|
91
94
|
|
92
95
|
rubyforge_project: rmov
|
93
|
-
rubygems_version: 1.
|
96
|
+
rubygems_version: 1.3.3
|
94
97
|
signing_key:
|
95
|
-
specification_version:
|
98
|
+
specification_version: 3
|
96
99
|
summary: Ruby wrapper for the QuickTime C API.
|
97
100
|
test_files: []
|
98
101
|
|