rmov 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ 0.1.0 (October 2nd, 2008)
2
+
3
+ * initial release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Ryan Bates
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,26 @@
1
+ CHANGELOG
2
+ ext/exporter.c
3
+ ext/extconf.rb
4
+ ext/movie.c
5
+ ext/rmov_ext.c
6
+ ext/rmov_ext.h
7
+ ext/track.c
8
+ lib/quicktime/exporter.rb
9
+ lib/quicktime/movie.rb
10
+ lib/quicktime/track.rb
11
+ lib/rmov.rb
12
+ LICENSE
13
+ Rakefile
14
+ README
15
+ spec/fixtures/settings.st
16
+ spec/output/example.pct
17
+ spec/output/saved_settings.st
18
+ spec/quicktime/exporter_spec.rb
19
+ spec/quicktime/movie_spec.rb
20
+ spec/quicktime/track_spec.rb
21
+ spec/spec.opts
22
+ spec/spec_helper.rb
23
+ tasks/setup.rake
24
+ tasks/spec.rake
25
+ TODO
26
+ Manifest
data/README ADDED
@@ -0,0 +1,73 @@
1
+ = RMov
2
+
3
+ Open, edit, and export QuickTime movies all within Ruby! This is an
4
+ unofficial wrapper around Apple's QuickTime C API. Mac OS X required.
5
+
6
+
7
+ == Install
8
+
9
+ Install the gem:
10
+
11
+ gem install rmov
12
+
13
+ And then load it in your project:
14
+
15
+ require 'rmov'
16
+
17
+
18
+ == Usage
19
+
20
+ Use this gem to open QuickTime movies and edit them to your liking.
21
+
22
+ movie1 = QuickTime::Movie.open("path/to/movie.mov")
23
+ movie2 = QuickTime::Movie.open("path/to/another_movie.mov")
24
+
25
+ # add movie2 to the end of movie1
26
+ movie1.append_movie(movie2)
27
+
28
+ # make a new movie out of a section of movie 1
29
+ # this will delete 5 seconds out of the movie at 2 seconds in
30
+ movie3 = movie1.clip_section(movie1, 2, 5)
31
+
32
+ # You can insert that part back into the movie at 8 seconds in
33
+ movie1.insert_movie(movie3, 8)
34
+
35
+ Now you can export the movie. Usually this is done through a user
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.
39
+
40
+ exporter = movie1.exporter
41
+
42
+ # if we already have saved the settings, load those
43
+ if File.exist? "settings.st"
44
+ exporter.load_settings("settings.st")
45
+ else
46
+ # otherwise open the QuickTime GUI settings dialog
47
+ exporter.open_settings_dialog
48
+
49
+ # save settings to a file so we don't have to bother user next time
50
+ exporter.save_settings("settings.st")
51
+ end
52
+
53
+ # export the movie to a file and report the progress along the way
54
+ exporter.export("movie.mov") do |progress|
55
+ percent = (progress*100).round
56
+ puts "#{percent}% complete"
57
+ end
58
+
59
+ See Quicktime::Movie in the RDoc for more information.
60
+
61
+ http://rmov.rubyforge.org
62
+
63
+
64
+ == Development
65
+
66
+ This project can be found on github at the following URL.
67
+
68
+ http://github.com/ryanb/rmov
69
+
70
+ If you find a bug, please send me a message on GitHub.
71
+
72
+ If you would like to contribute to this project, please fork the
73
+ repository and send me a pull request.
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('rmov', '0.1.0') do |p|
6
+ p.summary = "Ruby wrapper for the QuickTime C API."
7
+ p.description = "Ruby wrapper for the QuickTime C API."
8
+ p.url = "http://github.com/ryanb/rmov"
9
+ p.author = 'Ryan Bates'
10
+ p.email = "ryan (at) railscasts (dot) com"
11
+ p.ignore_pattern = ["script/*", "tmp/*", "output/*", "**/*.o", "**/*.bundle", "**/*.mov"]
12
+ p.extensions = ["ext/extconf.rb"]
13
+ p.development_dependencies = []
14
+ end
15
+
16
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/TODO ADDED
@@ -0,0 +1,19 @@
1
+ Fixes
2
+ - check if movie/track really exists each time we call it
3
+ - add rdocs
4
+ - fill README
5
+
6
+ Features
7
+ - export a single frame as pict
8
+ - resize movie
9
+ - export image sequence
10
+ - import image sequence
11
+ - import other file formats (mpeg, aiff, mp3, etc.)
12
+ - get movie metadata (album, author, artist, etc.)
13
+ - set movie metadata
14
+ - programatically adjust export settings (framerate, codec, etc.)
15
+
16
+ Possible
17
+ - add text track to movie
18
+ - add chapters to movie
19
+ - time remapping
@@ -0,0 +1,200 @@
1
+ #include "rmov_ext.h"
2
+
3
+ VALUE cExporter;
4
+
5
+ static void exporter_free(struct RExporter *rExporter)
6
+ {
7
+ if (rExporter->settings) {
8
+ QTDisposeAtomContainer(rExporter->settings);
9
+ }
10
+ }
11
+
12
+ static void exporter_mark(struct RExporter *rExporter)
13
+ {
14
+ }
15
+
16
+ /*
17
+ Creates a new exporter instance. Usually this is done through movie.exporter.
18
+
19
+ call-seq:
20
+ new(movie) -> exporter
21
+ */
22
+ static VALUE exporter_new(VALUE klass)
23
+ {
24
+ struct RExporter *rExporter;
25
+ return Data_Make_Struct(klass, struct RExporter, exporter_mark, exporter_free, rExporter);
26
+ }
27
+
28
+ static ComponentInstance exporter_component(VALUE obj)
29
+ {
30
+ ComponentInstance component = OpenDefaultComponent('spit', 'MooV');
31
+ if (REXPORTER(obj)->settings) {
32
+ MovieExportSetSettingsFromAtomContainer(component, REXPORTER(obj)->settings);
33
+ }
34
+ return component;
35
+ }
36
+
37
+ /*
38
+ Exports a movie to the given filepath. This will use either the
39
+ settings you set beforehand, or QuickTime's defaults.
40
+
41
+ You can track the progress of this operation by passing a block to this
42
+ method. It will be called regularly during the process and pass the
43
+ percentage complete (0.0 to 1.0) as an argument to the block.
44
+
45
+ call-seq:
46
+ export_to_file(filepath)
47
+ */
48
+ static VALUE exporter_export_to_file(VALUE obj, VALUE filepath)
49
+ {
50
+ OSErr err;
51
+ FSSpec fs;
52
+ Movie movie = MOVIE(rb_iv_get(obj, "@movie"));
53
+ ComponentInstance component = exporter_component(obj);
54
+
55
+ if (rb_block_given_p())
56
+ SetMovieProgressProc(movie, (MovieProgressUPP)movie_progress_proc, rb_block_proc());
57
+
58
+ // Activate so QuickTime doesn't export a white frame
59
+ SetMovieActive(movie, TRUE);
60
+
61
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
62
+ if (err != fnfErr)
63
+ rb_raise(eQuicktime, "Error %d occurred while opening file for export at %s.", err, RSTRING(filepath)->ptr);
64
+
65
+ // TODO use exporter settings when converting movie
66
+ err = ConvertMovieToFile(movie, 0, &fs, 'MooV', 'TVOD', 0, 0, 0, component);
67
+ if (err != noErr)
68
+ rb_raise(eQuicktime, "Error %d occurred while attempting to export movie to file %s.", err, RSTRING(filepath)->ptr);
69
+
70
+ if (rb_block_given_p())
71
+ SetMovieProgressProc(movie, 0, 0);
72
+
73
+ CloseComponent(component);
74
+
75
+ return Qnil;
76
+ }
77
+
78
+ /*
79
+ Opens the offical QuickTime GUI settings dialog. The process will be
80
+ suspended until the user closes the dialogue. If the user clicks Okay
81
+ the settings will be applied to this Exporter. You can then use
82
+ save_settings to save them to a file, and load_settings to load them
83
+ back again.
84
+
85
+ call-seq:
86
+ open_settings_dialog()
87
+ */
88
+ static VALUE exporter_open_settings_dialog(VALUE obj)
89
+ {
90
+ Boolean canceled;
91
+ OSErr err;
92
+ ProcessSerialNumber current_process = {0, kCurrentProcess};
93
+ Movie movie = MOVIE(rb_iv_get(obj, "@movie"));
94
+ ComponentInstance component = exporter_component(obj);
95
+
96
+ // Bring this process to the front
97
+ if (!IsProcessVisible(&current_process)) {
98
+ err = TransformProcessType(&current_process, kProcessTransformToForegroundApplication);
99
+ if (err != noErr) {
100
+ rb_raise(eQuicktime, "Error %d occurred while brining this application to the forground.", err);
101
+ }
102
+ }
103
+ SetFrontProcess(&current_process);
104
+
105
+ // Show export dialog and save settings
106
+ err = MovieExportDoUserDialog(component, movie, 0, 0, GetMovieDuration(movie), &canceled);
107
+ if (err != noErr) {
108
+ rb_raise(eQuicktime, "Error %d occurred while opening export dialog.", err);
109
+ }
110
+
111
+ if (!canceled) {
112
+ // Clear existing settings if there are any
113
+ if (REXPORTER(obj)->settings) {
114
+ QTDisposeAtomContainer(REXPORTER(obj)->settings);
115
+ }
116
+ MovieExportGetSettingsAsAtomContainer(component, &REXPORTER(obj)->settings);
117
+ }
118
+
119
+ CloseComponent(component);
120
+
121
+ if (canceled) {
122
+ return Qfalse;
123
+ } else {
124
+ return Qtrue;
125
+ }
126
+ }
127
+
128
+ /*
129
+ Loads the settings at the given filepath. See save_settings.
130
+
131
+ call-seq:
132
+ load_settings(filepath)
133
+ */
134
+ static VALUE exporter_load_settings(VALUE obj, VALUE filepath)
135
+ {
136
+ FILE *file;
137
+ long length, read_length;
138
+
139
+ file = fopen(RSTRING(filepath)->ptr, "r+b");
140
+ if (!file) {
141
+ rb_raise(eQuicktime, "Unable to open file for loading at %s.", RSTRING(filepath)->ptr);
142
+ }
143
+
144
+ // obtain file size:
145
+ fseek(file , 0, SEEK_END);
146
+ length = ftell(file);
147
+ rewind(file);
148
+
149
+ // clear existing settings if there are any
150
+ if (REXPORTER(obj)->settings) {
151
+ QTDisposeAtomContainer(REXPORTER(obj)->settings);
152
+ }
153
+
154
+ // load the file into settings
155
+ REXPORTER(obj)->settings = (QTAtomContainer)NewHandleClear(length);
156
+ read_length = fread(*(Handle)REXPORTER(obj)->settings, 1, length, file);
157
+ if (read_length != length) {
158
+ rb_raise(eQuicktime, "Unable to read entire file at %s.", RSTRING(filepath)->ptr);
159
+ }
160
+
161
+ fclose(file);
162
+
163
+ return Qnil;
164
+ }
165
+
166
+ /*
167
+ Saves the settings to the given filepath (usually with .st extension).
168
+ See open_settings_dialog and load_settings.
169
+
170
+ call-seq:
171
+ save_settings(filepath)
172
+ */
173
+ static VALUE exporter_save_settings(VALUE obj, VALUE filepath)
174
+ {
175
+ FILE *file;
176
+ QTAtomContainer settings = REXPORTER(obj)->settings;
177
+
178
+ if (!settings) {
179
+ rb_raise(eQuicktime, "Unable to save settings because no settings are specified.");
180
+ }
181
+
182
+ file = fopen(RSTRING(filepath)->ptr, "wb");
183
+ if (!file) {
184
+ rb_raise(eQuicktime, "Unable to open file for saving at %s.", RSTRING(filepath)->ptr);
185
+ }
186
+ fwrite(&settings, GetHandleSize((Handle)settings), 1, file);
187
+ fclose(file);
188
+
189
+ return Qnil;
190
+ }
191
+
192
+ void Init_quicktime_exporter()
193
+ {
194
+ cExporter = rb_define_class_under(mQuicktime, "Exporter", rb_cObject);
195
+ rb_define_alloc_func(cExporter, exporter_new);
196
+ rb_define_method(cExporter, "export", exporter_export_to_file, 1);
197
+ rb_define_method(cExporter, "open_settings_dialog", exporter_open_settings_dialog, 0);
198
+ rb_define_method(cExporter, "load_settings", exporter_load_settings, 1);
199
+ rb_define_method(cExporter, "save_settings", exporter_save_settings, 1);
200
+ }
@@ -0,0 +1,4 @@
1
+ require 'mkmf'
2
+
3
+ $LDFLAGS = '-framework Quicktime'
4
+ create_makefile('rmov_ext')
@@ -0,0 +1,435 @@
1
+ #include "rmov_ext.h"
2
+
3
+ VALUE cMovie;
4
+
5
+ OSErr movie_progress_proc(Movie movie, short message, short operation, Fixed percent, VALUE proc)
6
+ {
7
+ rb_funcall(proc, rb_intern("call"), 1, rb_float_new(FixedToFloat(percent)));
8
+ return 0;
9
+ }
10
+
11
+ static void movie_free(struct RMovie *rMovie)
12
+ {
13
+ if (rMovie->movie) {
14
+ DisposeMovie(rMovie->movie);
15
+ }
16
+ }
17
+
18
+ static void movie_mark(struct RMovie *rMovie)
19
+ {
20
+ }
21
+
22
+ /*
23
+ Creates a new movie instance. Generally you want to go through
24
+ Movie.open or Movie.empty to load or create a new movie respectively.
25
+ If you do no then you will need to load the movie with load_empty or
26
+ load_from_file before you can accomplish anything.
27
+
28
+ call-seq:
29
+ new() -> movie
30
+ */
31
+ static VALUE movie_new(VALUE klass)
32
+ {
33
+ struct RMovie *rMovie;
34
+ return Data_Make_Struct(klass, struct RMovie, movie_mark, movie_free, rMovie);
35
+ }
36
+
37
+ /*
38
+ Dispose of the loaded QuickTime movie. This will automatically be done
39
+ when this movie instance is garbage collected. However if you are
40
+ iterating through many movies it is often helpful to dispose of it
41
+ as soon as you're done with it.
42
+
43
+ call-seq:
44
+ dispose()
45
+ */
46
+ static VALUE movie_dispose(VALUE obj)
47
+ {
48
+ if (MOVIE(obj)) {
49
+ DisposeMovie(MOVIE(obj));
50
+ RMOVIE(obj)->movie = NULL;
51
+ }
52
+ return obj;
53
+ }
54
+
55
+ /*
56
+ Loads a new, empty QuickTime movie at given filepath. Should only be
57
+ called if no movie has been loaded (or it has been disposed). Usually
58
+ you go through Movie.open.
59
+
60
+ call-seq:
61
+ load_from_file(filepath)
62
+ */
63
+ static VALUE movie_load_from_file(VALUE obj, VALUE filepath)
64
+ {
65
+ if (MOVIE(obj)) {
66
+ rb_raise(eQuicktime, "Movie has already been loaded.");
67
+ } else {
68
+ OSErr err;
69
+ FSSpec fs;
70
+ short frefnum = -1;
71
+ short movie_resid = 0;
72
+ Movie *movie = ALLOC(Movie);
73
+
74
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
75
+ if (err != 0)
76
+ rb_raise(eQuicktime, "Error %d occurred while reading file at %s", err, RSTRING(filepath)->ptr);
77
+
78
+ err = OpenMovieFile(&fs, &frefnum, fsRdPerm);
79
+ if (err != 0)
80
+ rb_raise(eQuicktime, "Error %d occurred while opening movie at %s", err, RSTRING(filepath)->ptr);
81
+
82
+ err = NewMovieFromFile(movie, frefnum, &movie_resid, 0, newMovieActive, 0);
83
+ if (err != 0)
84
+ rb_raise(eQuicktime, "Error %d occurred while loading movie at %s", err, RSTRING(filepath)->ptr);
85
+
86
+ err = CloseMovieFile(frefnum);
87
+ if (err != 0)
88
+ rb_raise(eQuicktime, "Error %d occurred while closing movie file at %s", err, RSTRING(filepath)->ptr);
89
+
90
+ RMOVIE(obj)->movie = *movie;
91
+
92
+ return obj;
93
+ }
94
+ }
95
+
96
+ /*
97
+ Loads a new, empty QuickTime movie. Should only be called if no movie
98
+ has been loaded (or it has been disposed). Usually you go through
99
+ Movie.empty.
100
+
101
+ call-seq:
102
+ load_empty()
103
+ */
104
+ static VALUE movie_load_empty(VALUE obj)
105
+ {
106
+ if (MOVIE(obj)) {
107
+ rb_raise(eQuicktime, "Movie has already been loaded.");
108
+ } else {
109
+ RMOVIE(obj)->movie = NewMovie(0);
110
+ return obj;
111
+ }
112
+ }
113
+
114
+ /*
115
+ Returns the raw duration of the movie. Combine this with time_scale to
116
+ reach the duration in seconds.
117
+
118
+ call-seq:
119
+ raw_duration() -> duration_int
120
+ */
121
+ static VALUE movie_raw_duration(VALUE obj)
122
+ {
123
+ return INT2NUM(GetMovieDuration(MOVIE(obj)));
124
+ }
125
+
126
+ /*
127
+ Returns the time scale of the movie. Usually only needed when working
128
+ with raw_duration.
129
+
130
+ call-seq:
131
+ time_scale() -> scale_int
132
+ */
133
+ static VALUE movie_time_scale(VALUE obj)
134
+ {
135
+ return INT2NUM(GetMovieTimeScale(MOVIE(obj)));
136
+ }
137
+
138
+ /*
139
+ Returns the number of tracks in the movie.
140
+
141
+ call-seq:
142
+ track_count() -> count
143
+ */
144
+ static VALUE movie_bounds(VALUE obj)
145
+ {
146
+ VALUE bounds_hash = rb_hash_new();
147
+ Rect bounds;
148
+ GetMovieBox(MOVIE(obj), &bounds);
149
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("left")), INT2NUM(bounds.left));
150
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("top")), INT2NUM(bounds.top));
151
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("right")), INT2NUM(bounds.right));
152
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("bottom")), INT2NUM(bounds.bottom));
153
+ return bounds_hash;
154
+ }
155
+
156
+ /*
157
+ Returns the number of tracks in the movie.
158
+
159
+ call-seq:
160
+ track_count -> count
161
+ */
162
+ static VALUE movie_track_count(VALUE obj)
163
+ {
164
+ return INT2NUM(GetMovieTrackCount(MOVIE(obj)));
165
+ }
166
+
167
+ /*
168
+ Adds the tracks of given movie into called movie at given position (in seconds).
169
+
170
+ You can track the progress of this operation by passing a block to this
171
+ method. It will be called regularly during the process and pass the
172
+ percentage complete (0.0 to 1.0) as an argument to the block.
173
+
174
+ call-seq:
175
+ composite_movie(movie, position)
176
+ */
177
+ static VALUE movie_composite_movie(VALUE obj, VALUE src, VALUE position)
178
+ {
179
+ if (rb_block_given_p())
180
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
181
+
182
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, position), 0);
183
+ AddMovieSelection(MOVIE(obj), MOVIE(src));
184
+
185
+ if (rb_block_given_p())
186
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
187
+
188
+ return obj;
189
+ }
190
+
191
+ /*
192
+ Inserts given movie into called movie at given position (in seconds).
193
+
194
+ You can track the progress of this operation by passing a block to this
195
+ method. It will be called regularly during the process and pass the
196
+ percentage complete (0.0 to 1.0) as an argument to the block.
197
+
198
+ call-seq:
199
+ append_movie(movie, position)
200
+ */
201
+ static VALUE movie_insert_movie(VALUE obj, VALUE src, VALUE position)
202
+ {
203
+ if (rb_block_given_p())
204
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
205
+
206
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, position), 0);
207
+ PasteMovieSelection(MOVIE(obj), MOVIE(src));
208
+
209
+ if (rb_block_given_p())
210
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
211
+
212
+ return obj;
213
+ }
214
+
215
+ /*
216
+ Adds given movie to the end of movie which this method is called on.
217
+
218
+ You can track the progress of this operation by passing a block to this
219
+ method. It will be called regularly during the process and pass the
220
+ percentage complete (0.0 to 1.0) as an argument to the block.
221
+
222
+ call-seq:
223
+ append_movie(movie)
224
+ */
225
+ static VALUE movie_append_movie(VALUE obj, VALUE src)
226
+ {
227
+ if (rb_block_given_p())
228
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
229
+
230
+ SetMovieSelection(MOVIE(obj), GetMovieDuration(MOVIE(obj)), 0);
231
+ PasteMovieSelection(MOVIE(obj), MOVIE(src));
232
+
233
+ if (rb_block_given_p())
234
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
235
+
236
+ return obj;
237
+ }
238
+
239
+ /*
240
+ Deletes given section from movie. Both start_time and duration
241
+ should be floats representing seconds.
242
+
243
+ call-seq:
244
+ delete_section(start_time, duration)
245
+ */
246
+ static VALUE movie_delete_section(VALUE obj, VALUE start, VALUE duration)
247
+ {
248
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
249
+ ClearMovieSelection(MOVIE(obj));
250
+ return obj;
251
+ }
252
+
253
+ /*
254
+ Returns a new movie in the given section. Does not modify original
255
+ movie. Both start_time and duration should be floats representing
256
+ seconds.
257
+
258
+ You can track the progress of this operation by passing a block to this
259
+ method. It will be called regularly during the process and pass the
260
+ percentage complete (0.0 to 1.0) as an argument to the block.
261
+
262
+ call-seq:
263
+ clone_section(start_time, duration) -> movie
264
+ */
265
+ static VALUE movie_clone_section(VALUE obj, VALUE start, VALUE duration)
266
+ {
267
+ VALUE new_movie_obj = rb_obj_alloc(cMovie);
268
+
269
+ if (rb_block_given_p())
270
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
271
+
272
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
273
+ RMOVIE(new_movie_obj)->movie = CopyMovieSelection(MOVIE(obj));
274
+
275
+ if (rb_block_given_p())
276
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
277
+
278
+ return new_movie_obj;
279
+ }
280
+
281
+ /*
282
+ Deletes given section on movie and returns a new movie with that
283
+ section. Both start_time and duration should be floats representing
284
+ seconds.
285
+
286
+ You can track the progress of this operation by passing a block to this
287
+ method. It will be called regularly during the process and pass the
288
+ percentage complete (0.0 to 1.0) as an argument to the block.
289
+
290
+ call-seq:
291
+ clip_section(start_time, duration) -> movie
292
+ */
293
+ static VALUE movie_clip_section(VALUE obj, VALUE start, VALUE duration)
294
+ {
295
+ VALUE new_movie_obj = rb_obj_alloc(cMovie);
296
+
297
+ if (rb_block_given_p())
298
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
299
+
300
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
301
+ RMOVIE(new_movie_obj)->movie = CutMovieSelection(MOVIE(obj));
302
+
303
+ if (rb_block_given_p())
304
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
305
+
306
+ return new_movie_obj;
307
+ }
308
+
309
+ /*
310
+ Determine if a movie has changed since opening. Returns true/false.
311
+ See reset_changed_status to reset this value.
312
+
313
+ call-seq:
314
+ changed?() -> bool
315
+ */
316
+ static VALUE movie_changed(VALUE obj)
317
+ {
318
+ if (HasMovieChanged(MOVIE(obj))) {
319
+ return Qtrue;
320
+ } else {
321
+ return Qfalse;
322
+ }
323
+ }
324
+
325
+ /*
326
+ Resets the "changed?" status. Does not revert the movie itself.
327
+
328
+ call-seq:
329
+ clear_changed_status()
330
+ */
331
+ static VALUE movie_clear_changed_status(VALUE obj)
332
+ {
333
+ ClearMovieChanged(MOVIE(obj));
334
+ return Qnil;
335
+ }
336
+
337
+
338
+ /*
339
+ Saves the movie to the given filepath by flattening it.
340
+
341
+ call-seq:
342
+ flatten(filepath)
343
+ */
344
+
345
+ static VALUE movie_flatten(VALUE obj, VALUE filepath)
346
+ {
347
+ OSErr err;
348
+ FSSpec fs;
349
+ VALUE new_movie_obj = rb_obj_alloc(cMovie);
350
+
351
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
352
+ if (err != fnfErr)
353
+ rb_raise(eQuicktime, "Error %d occurred while opening file for export at %s", err, RSTRING(filepath)->ptr);
354
+
355
+ // TODO make these flags settable through an options hash
356
+ RMOVIE(new_movie_obj)->movie = FlattenMovieData(MOVIE(obj),
357
+ flattenDontInterleaveFlatten
358
+ | flattenCompressMovieResource
359
+ | flattenAddMovieToDataFork
360
+ | flattenForceMovieResourceBeforeMovieData,
361
+ &fs, 'TVOD', smSystemScript, createMovieFileDontCreateResFile);
362
+ return new_movie_obj;
363
+ }
364
+
365
+ /*
366
+ Exports a PICT file to given filepath (should end in .pct) at the given
367
+ time. Time should be a floating point in seconds.
368
+
369
+ call-seq:
370
+ export_pict(filepath, time)
371
+
372
+ */
373
+
374
+ static VALUE movie_export_pict(VALUE obj, VALUE filepath, VALUE frame_time)
375
+ {
376
+ GraphicsImportComponent component;
377
+ PicHandle picture;
378
+ Handle handle;
379
+ FSSpec fs;
380
+ OSErr err;
381
+
382
+ picture = GetMoviePict(MOVIE(obj), MOVIE_TIME(obj, frame_time));
383
+
384
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
385
+ if (err != fnfErr)
386
+ rb_raise(eQuicktime, "Error %d occurred while opening file for export at %s.", err, RSTRING(filepath)->ptr);
387
+
388
+ // Convert the picture handle into a PICT file (still in a handle)
389
+ // by adding a 512-byte header to the start.
390
+ handle = NewHandleClear(512);
391
+ err = HandAndHand((Handle)picture, handle);
392
+ if (err != noErr)
393
+ rb_raise(eQuicktime, "Error %d occurred while converting handle for pict export %s.", err, RSTRING(filepath)->ptr);
394
+
395
+ err = OpenADefaultComponent(GraphicsImporterComponentType, kQTFileTypePicture, &component);
396
+ if (err != noErr)
397
+ rb_raise(eQuicktime, "Error %d occurred while opening picture component for %s.", err, RSTRING(filepath)->ptr);
398
+
399
+ err = GraphicsImportSetDataHandle(component, handle);
400
+ if (err != noErr)
401
+ rb_raise(eQuicktime, "Error %d occurred while setting graphics importer data handle for %s.", err, RSTRING(filepath)->ptr);
402
+
403
+ err = GraphicsImportExportImageFile(component, 0, 0, &fs, smSystemScript);
404
+ if (err != noErr)
405
+ rb_raise(eQuicktime, "Error %d occurred while exporting pict to file %s.", err, RSTRING(filepath)->ptr);
406
+
407
+ CloseComponent(component);
408
+ DisposeHandle(handle);
409
+ DisposeHandle((Handle)picture);
410
+
411
+ return Qnil;
412
+ }
413
+
414
+ void Init_quicktime_movie()
415
+ {
416
+ cMovie = rb_define_class_under(mQuicktime, "Movie", rb_cObject);
417
+ rb_define_alloc_func(cMovie, movie_new);
418
+ rb_define_method(cMovie, "load_from_file", movie_load_from_file, 1);
419
+ rb_define_method(cMovie, "load_empty", movie_load_empty, 0);
420
+ rb_define_method(cMovie, "raw_duration", movie_raw_duration, 0);
421
+ rb_define_method(cMovie, "time_scale", movie_time_scale, 0);
422
+ rb_define_method(cMovie, "bounds", movie_bounds, 0);
423
+ rb_define_method(cMovie, "track_count", movie_track_count, 0);
424
+ rb_define_method(cMovie, "composite_movie", movie_composite_movie, 2);
425
+ rb_define_method(cMovie, "insert_movie", movie_insert_movie, 2);
426
+ rb_define_method(cMovie, "append_movie", movie_append_movie, 1);
427
+ rb_define_method(cMovie, "delete_section", movie_delete_section, 2);
428
+ rb_define_method(cMovie, "clone_section", movie_clone_section, 2);
429
+ rb_define_method(cMovie, "clip_section", movie_clip_section, 2);
430
+ rb_define_method(cMovie, "changed?", movie_changed, 0);
431
+ rb_define_method(cMovie, "clear_changed_status", movie_clear_changed_status, 0);
432
+ rb_define_method(cMovie, "flatten", movie_flatten, 1);
433
+ rb_define_method(cMovie, "export_pict", movie_export_pict, 2);
434
+ rb_define_method(cMovie, "dispose", movie_dispose, 0);
435
+ }