rmov 0.1.0

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.
@@ -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
+ }