one-k-rmov 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
data/ext/movie.c ADDED
@@ -0,0 +1,457 @@
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
+ call-seq: new() -> movie
24
+
25
+ Creates a new movie instance. Generally you want to go through
26
+ Movie.open or Movie.empty to load or create a new movie respectively.
27
+ If you do no then you will need to load the movie with load_empty or
28
+ load_from_file before you can accomplish anything.
29
+ */
30
+ static VALUE movie_new(VALUE klass)
31
+ {
32
+ struct RMovie *rMovie;
33
+ return Data_Make_Struct(klass, struct RMovie, movie_mark, movie_free, rMovie);
34
+ }
35
+
36
+ /*
37
+ call-seq: dispose()
38
+
39
+ Dispose of the loaded QuickTime movie. This will automatically be done
40
+ when this movie instance is garbage collected. However if you are
41
+ iterating through many movies it is often helpful to dispose of it
42
+ as soon as you're done with it.
43
+ */
44
+ static VALUE movie_dispose(VALUE obj)
45
+ {
46
+ if (MOVIE(obj)) {
47
+ DisposeMovie(MOVIE(obj));
48
+ RMOVIE(obj)->movie = NULL;
49
+ }
50
+ return obj;
51
+ }
52
+
53
+ /*
54
+ call-seq: load_from_file(filepath)
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
+ static VALUE movie_load_from_file(VALUE obj, VALUE filepath)
61
+ {
62
+ if (MOVIE(obj)) {
63
+ rb_raise(eQuickTime, "Movie has already been loaded.");
64
+ } else {
65
+ OSErr err;
66
+ FSSpec fs;
67
+ short frefnum = -1;
68
+ short movie_resid = 0;
69
+ Movie *movie = ALLOC(Movie);
70
+
71
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
72
+ if (err != 0)
73
+ rb_raise(eQuickTime, "Error %d occurred while reading file at %s", err, RSTRING(filepath)->ptr);
74
+
75
+ err = OpenMovieFile(&fs, &frefnum, fsRdPerm);
76
+ if (err != 0)
77
+ rb_raise(eQuickTime, "Error %d occurred while opening movie at %s", err, RSTRING(filepath)->ptr);
78
+
79
+ err = NewMovieFromFile(movie, frefnum, &movie_resid, 0, newMovieActive, 0);
80
+ if (err != 0)
81
+ rb_raise(eQuickTime, "Error %d occurred while loading movie at %s", err, RSTRING(filepath)->ptr);
82
+
83
+ err = CloseMovieFile(frefnum);
84
+ if (err != 0)
85
+ rb_raise(eQuickTime, "Error %d occurred while closing movie file at %s", err, RSTRING(filepath)->ptr);
86
+
87
+ RMOVIE(obj)->movie = *movie;
88
+
89
+ return obj;
90
+ }
91
+ }
92
+
93
+ /*
94
+ call-seq: load_empty()
95
+
96
+ Loads a new, empty QuickTime movie. Should only be called if no movie
97
+ has been loaded (or it has been disposed). Usually you go through
98
+ Movie.empty.
99
+ */
100
+ static VALUE movie_load_empty(VALUE obj)
101
+ {
102
+ if (MOVIE(obj)) {
103
+ rb_raise(eQuickTime, "Movie has already been loaded.");
104
+ } else {
105
+ RMOVIE(obj)->movie = NewMovie(0);
106
+ return obj;
107
+ }
108
+ }
109
+
110
+ /*
111
+ call-seq: raw_duration() -> duration_int
112
+
113
+ Returns the raw duration of the movie. Combine this with time_scale to
114
+ reach the duration in seconds.
115
+ */
116
+ static VALUE movie_raw_duration(VALUE obj)
117
+ {
118
+ return INT2NUM(GetMovieDuration(MOVIE(obj)));
119
+ }
120
+
121
+ /*
122
+ call-seq: time_scale() -> scale_int
123
+
124
+ Returns the time scale of the movie. Usually only needed when working
125
+ with raw_duration.
126
+ */
127
+ static VALUE movie_time_scale(VALUE obj)
128
+ {
129
+ return INT2NUM(GetMovieTimeScale(MOVIE(obj)));
130
+ }
131
+
132
+ /*
133
+ call-seq: bounds() -> bounds_hash
134
+
135
+ Returns a hash of boundaries. The hash contains four keys: :left, :top,
136
+ :right, :bottom. Each holds an integer representing the pixel value.
137
+ */
138
+ static VALUE movie_bounds(VALUE obj)
139
+ {
140
+ VALUE bounds_hash = rb_hash_new();
141
+ Rect bounds;
142
+ GetMovieBox(MOVIE(obj), &bounds);
143
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("left")), INT2NUM(bounds.left));
144
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("top")), INT2NUM(bounds.top));
145
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("right")), INT2NUM(bounds.right));
146
+ rb_hash_aset(bounds_hash, ID2SYM(rb_intern("bottom")), INT2NUM(bounds.bottom));
147
+ return bounds_hash;
148
+ }
149
+
150
+ /*
151
+ call-seq: track_count() -> count
152
+
153
+ Returns the number of tracks in the movie.
154
+ */
155
+ static VALUE movie_track_count(VALUE obj)
156
+ {
157
+ return INT2NUM(GetMovieTrackCount(MOVIE(obj)));
158
+ }
159
+
160
+ /*
161
+ call-seq: composite_movie(movie, position)
162
+
163
+ Adds the tracks of given movie into called movie at given position (in seconds).
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.
168
+ */
169
+ static VALUE movie_composite_movie(VALUE obj, VALUE src, VALUE position)
170
+ {
171
+ if (rb_block_given_p())
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
+
180
+ return obj;
181
+ }
182
+
183
+ /*
184
+ call-seq: append_movie(movie, position)
185
+
186
+ Inserts given movie into called movie at given position (in seconds).
187
+
188
+ You can track the progress of this operation by passing a block to this
189
+ method. It will be called regularly during the process and pass the
190
+ percentage complete (0.0 to 1.0) as an argument to the block.
191
+ */
192
+ static VALUE movie_insert_movie(VALUE obj, VALUE src, VALUE position)
193
+ {
194
+ if (rb_block_given_p())
195
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
196
+
197
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, position), 0);
198
+ PasteMovieSelection(MOVIE(obj), MOVIE(src));
199
+
200
+ if (rb_block_given_p())
201
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
202
+
203
+ return obj;
204
+ }
205
+
206
+ /*
207
+ call-seq: append_movie(movie)
208
+
209
+ Adds given movie to the end of movie which this method is called on.
210
+
211
+ You can track the progress of this operation by passing a block to this
212
+ method. It will be called regularly during the process and pass the
213
+ percentage complete (0.0 to 1.0) as an argument to the block.
214
+ */
215
+ static VALUE movie_append_movie(VALUE obj, VALUE src)
216
+ {
217
+ if (rb_block_given_p())
218
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
219
+
220
+ SetMovieSelection(MOVIE(obj), GetMovieDuration(MOVIE(obj)), 0);
221
+ PasteMovieSelection(MOVIE(obj), MOVIE(src));
222
+
223
+ if (rb_block_given_p())
224
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
225
+
226
+ return obj;
227
+ }
228
+
229
+ /*
230
+ call-seq: delete_section(start_time, duration)
231
+
232
+ Deletes given section from movie. Both start_time and duration
233
+ should be floats representing seconds.
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
244
+
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
+ You can track the progress of this operation by passing a block to this
250
+ method. It will be called regularly during the process and pass the
251
+ percentage complete (0.0 to 1.0) as an argument to the block.
252
+ */
253
+ static VALUE movie_clone_section(VALUE obj, VALUE start, VALUE duration)
254
+ {
255
+ VALUE new_movie_obj = rb_obj_alloc(cMovie);
256
+
257
+ if (rb_block_given_p())
258
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
259
+
260
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
261
+ RMOVIE(new_movie_obj)->movie = CopyMovieSelection(MOVIE(obj));
262
+
263
+ if (rb_block_given_p())
264
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
265
+
266
+ return new_movie_obj;
267
+ }
268
+
269
+ /*
270
+ call-seq: clip_section(start_time, duration) -> movie
271
+
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
+ You can track the progress of this operation by passing a block to this
277
+ method. It will be called regularly during the process and pass the
278
+ percentage complete (0.0 to 1.0) as an argument to the block.
279
+ */
280
+ static VALUE movie_clip_section(VALUE obj, VALUE start, VALUE duration)
281
+ {
282
+ VALUE new_movie_obj = rb_obj_alloc(cMovie);
283
+
284
+ if (rb_block_given_p())
285
+ SetMovieProgressProc(MOVIE(obj), (MovieProgressUPP)movie_progress_proc, rb_block_proc());
286
+
287
+ SetMovieSelection(MOVIE(obj), MOVIE_TIME(obj, start), MOVIE_TIME(obj, duration));
288
+ RMOVIE(new_movie_obj)->movie = CutMovieSelection(MOVIE(obj));
289
+
290
+ if (rb_block_given_p())
291
+ SetMovieProgressProc(MOVIE(obj), 0, 0);
292
+
293
+ return new_movie_obj;
294
+ }
295
+
296
+ /*
297
+ call-seq: changed?() -> bool
298
+
299
+ Determine if a movie has changed since opening. Returns true/false.
300
+ See reset_changed_status to reset this value.
301
+ */
302
+ static VALUE movie_changed(VALUE obj)
303
+ {
304
+ if (HasMovieChanged(MOVIE(obj))) {
305
+ return Qtrue;
306
+ } else {
307
+ return Qfalse;
308
+ }
309
+ }
310
+
311
+ /*
312
+ call-seq: clear_changed_status()
313
+
314
+ Resets the "changed?" status. Does not revert the movie itself.
315
+ */
316
+ static VALUE movie_clear_changed_status(VALUE obj)
317
+ {
318
+ ClearMovieChanged(MOVIE(obj));
319
+ return Qnil;
320
+ }
321
+
322
+
323
+ /*
324
+ call-seq: flatten(filepath)
325
+
326
+ Saves the movie to the given filepath by flattening it.
327
+ */
328
+ static VALUE movie_flatten(VALUE obj, VALUE filepath)
329
+ {
330
+ OSErr err;
331
+ FSSpec fs;
332
+ VALUE new_movie_obj = rb_obj_alloc(cMovie);
333
+
334
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
335
+ if (err != fnfErr)
336
+ rb_raise(eQuickTime, "Error %d occurred while opening file for export at %s", err, RSTRING(filepath)->ptr);
337
+
338
+ // TODO make these flags settable through an options hash
339
+ RMOVIE(new_movie_obj)->movie = FlattenMovieData(MOVIE(obj),
340
+ flattenDontInterleaveFlatten
341
+ | flattenCompressMovieResource
342
+ | flattenAddMovieToDataFork
343
+ | flattenForceMovieResourceBeforeMovieData,
344
+ &fs, 'TVOD', smSystemScript, createMovieFileDontCreateResFile);
345
+ return new_movie_obj;
346
+ }
347
+
348
+ /*
349
+ call-seq: export_image_type(filepath, time, ostype)
350
+
351
+ Exports an image as the given ostype. It is best to use export_image
352
+ instead if the ostype can be determined from the filepath extension.
353
+ */
354
+ static VALUE movie_export_image_type(VALUE obj, VALUE filepath, VALUE frame_time, VALUE ostype_obj)
355
+ {
356
+ GraphicsImportComponent component;
357
+ PicHandle picture;
358
+ Handle handle;
359
+ FSSpec fs;
360
+ OSErr err;
361
+
362
+ picture = GetMoviePict(MOVIE(obj), MOVIE_TIME(obj, frame_time));
363
+
364
+ err = NativePathNameToFSSpec(RSTRING(filepath)->ptr, &fs, 0);
365
+ if (err != fnfErr)
366
+ rb_raise(eQuickTime, "Error %d occurred while opening file for export at %s.", err, RSTRING(filepath)->ptr);
367
+
368
+ // Convert the picture handle into a PICT file (still in a handle)
369
+ // by adding a 512-byte header to the start.
370
+ handle = NewHandleClear(512);
371
+ err = HandAndHand((Handle)picture, handle);
372
+ if (err != noErr)
373
+ rb_raise(eQuickTime, "Error %d occurred while converting handle for pict export %s.", err, RSTRING(filepath)->ptr);
374
+
375
+ err = OpenADefaultComponent(GraphicsImporterComponentType, kQTFileTypePicture, &component);
376
+ if (err != noErr)
377
+ rb_raise(eQuickTime, "Error %d occurred while opening picture component for %s.", err, RSTRING(filepath)->ptr);
378
+
379
+ err = GraphicsImportSetDataHandle(component, handle);
380
+ if (err != noErr)
381
+ rb_raise(eQuickTime, "Error %d occurred while setting graphics importer data handle for %s.", err, RSTRING(filepath)->ptr);
382
+
383
+ err = GraphicsImportExportImageFile(component, OSTYPE(RSTRING(ostype_obj)->ptr), 0, &fs, smSystemScript);
384
+ if (err != noErr)
385
+ rb_raise(eQuickTime, "Error %d occurred while exporting pict to file %s.", err, RSTRING(filepath)->ptr);
386
+
387
+ CloseComponent(component);
388
+ DisposeHandle(handle);
389
+ DisposeHandle((Handle)picture);
390
+
391
+ return Qnil;
392
+ }
393
+
394
+ /*
395
+ call-seq: poster_time() -> seconds
396
+
397
+ Returns the poster time of the movie (in seconds).
398
+ */
399
+ static VALUE movie_get_poster_time(VALUE obj)
400
+ {
401
+ return rb_float_new((double)GetMoviePosterTime(MOVIE(obj))/GetMovieTimeScale(MOVIE(obj)));
402
+ }
403
+
404
+ /*
405
+ call-seq: poster_time=(seconds)
406
+
407
+ Sets the poster_time of the movie (in seconds).
408
+ */
409
+ static VALUE movie_set_poster_time(VALUE obj, VALUE seconds)
410
+ {
411
+ SetMoviePosterTime(MOVIE(obj), MOVIE_TIME(obj, seconds));
412
+ return Qnil;
413
+ }
414
+
415
+ /*
416
+ call-seq: new_track(width, height) -> track
417
+
418
+ Creates a new track with the given width/height on the movie and returns it.
419
+
420
+ This method is generally not called directly. Instead you should call
421
+ new_video_track or new_audio_track. If you call method make sure to
422
+ call new_media on track to setup the media.
423
+ */
424
+ static VALUE movie_new_track(VALUE obj, VALUE width, VALUE height)
425
+ {
426
+ VALUE track_obj = rb_obj_alloc(cTrack);
427
+ RTRACK(track_obj)->track = NewMovieTrack(MOVIE(obj), NUM2INT(width), NUM2INT(height), kFullVolume);
428
+ return track_obj;
429
+ }
430
+
431
+ void Init_quicktime_movie()
432
+ {
433
+ VALUE mQuickTime;
434
+ mQuickTime = rb_define_module("QuickTime");
435
+ cMovie = rb_define_class_under(mQuickTime, "Movie", rb_cObject);
436
+ rb_define_alloc_func(cMovie, movie_new);
437
+ rb_define_method(cMovie, "load_from_file", movie_load_from_file, 1);
438
+ rb_define_method(cMovie, "load_empty", movie_load_empty, 0);
439
+ rb_define_method(cMovie, "raw_duration", movie_raw_duration, 0);
440
+ rb_define_method(cMovie, "time_scale", movie_time_scale, 0);
441
+ rb_define_method(cMovie, "bounds", movie_bounds, 0);
442
+ rb_define_method(cMovie, "track_count", movie_track_count, 0);
443
+ rb_define_method(cMovie, "composite_movie", movie_composite_movie, 2);
444
+ rb_define_method(cMovie, "insert_movie", movie_insert_movie, 2);
445
+ rb_define_method(cMovie, "append_movie", movie_append_movie, 1);
446
+ rb_define_method(cMovie, "delete_section", movie_delete_section, 2);
447
+ rb_define_method(cMovie, "clone_section", movie_clone_section, 2);
448
+ rb_define_method(cMovie, "clip_section", movie_clip_section, 2);
449
+ rb_define_method(cMovie, "changed?", movie_changed, 0);
450
+ rb_define_method(cMovie, "clear_changed_status", movie_clear_changed_status, 0);
451
+ rb_define_method(cMovie, "flatten", movie_flatten, 1);
452
+ rb_define_method(cMovie, "export_image_type", movie_export_image_type, 3);
453
+ rb_define_method(cMovie, "dispose", movie_dispose, 0);
454
+ rb_define_method(cMovie, "poster_time", movie_get_poster_time, 0);
455
+ rb_define_method(cMovie, "poster_time=", movie_set_poster_time, 1);
456
+ rb_define_method(cMovie, "new_track", movie_new_track, 2);
457
+ }
data/ext/rmov_ext.c ADDED
@@ -0,0 +1,16 @@
1
+ #include "rmov_ext.h"
2
+
3
+ VALUE eQuickTime;
4
+
5
+ void Init_rmov_ext()
6
+ {
7
+ VALUE mQuickTime;
8
+
9
+ EnterMovies(); // Enables the QuickTime framework
10
+
11
+ mQuickTime = rb_define_module("QuickTime");
12
+ eQuickTime = rb_define_class_under(mQuickTime, "Error", rb_eStandardError);
13
+ Init_quicktime_movie();
14
+ Init_quicktime_track();
15
+ Init_quicktime_exporter();
16
+ }
data/ext/rmov_ext.h ADDED
@@ -0,0 +1,45 @@
1
+ #include <ruby.h>
2
+ #include <QuickTime/QuickTime.h>
3
+
4
+ extern VALUE eQuickTime, cMovie, cTrack, cExporter;
5
+
6
+
7
+ #define OSTYPE(str) ((str[0] << 24) | (str[1] << 16) | (str[2] << 8) | str[3])
8
+
9
+ /*** MOVIE ***/
10
+
11
+ void Init_quicktime_movie();
12
+ OSErr movie_progress_proc(Movie movie, short message, short operation, Fixed percent, VALUE proc);
13
+
14
+ #define RMOVIE(obj) (Check_Type(obj, T_DATA), (struct RMovie*)DATA_PTR(obj))
15
+ #define MOVIE(obj) (RMOVIE(obj)->movie)
16
+ #define MOVIE_TIME(obj, seconds) (floor(NUM2DBL(seconds)*GetMovieTimeScale(MOVIE(obj))))
17
+
18
+ struct RMovie {
19
+ Movie movie;
20
+ };
21
+
22
+
23
+ /*** TRACK ***/
24
+
25
+ void Init_quicktime_track();
26
+
27
+ #define RTRACK(obj) (Check_Type(obj, T_DATA), (struct RTrack*)DATA_PTR(obj))
28
+ #define TRACK(obj) (RTRACK(obj)->track)
29
+ #define TRACK_MEDIA(obj) (GetTrackMedia(TRACK(obj)))
30
+ #define TRACK_TIME(obj, seconds) (floor(NUM2DBL(seconds)*GetMediaTimeScale(TRACK_MEDIA(obj))))
31
+
32
+ struct RTrack {
33
+ Track track;
34
+ };
35
+
36
+
37
+ /*** EXPORTER ***/
38
+
39
+ void Init_quicktime_exporter();
40
+
41
+ #define REXPORTER(obj) (Check_Type(obj, T_DATA), (struct RExporter*)DATA_PTR(obj))
42
+
43
+ struct RExporter {
44
+ QTAtomContainer settings;
45
+ };