c961472318b5f4318170f6dae91a5d1f3786c096
[blender.git] / source / blender / quicktime / apple / quicktime_import.c
1 /**
2  * $Id$
3  *
4  * quicktime_import.c
5  *
6  * Code to use Quicktime to load images/movies as texture.
7  *
8  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version. The Blender
13  * Foundation also sells licenses for use in proprietary software under
14  * the Blender License.  See http://www.blender.org/BL/ for information
15  * about this.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  *
26  *
27  * The Original Code is written by Rob Haarsma (phase)
28  *
29  * Contributor(s): Stefan Gartner (sgefant)
30  *
31  * ***** END GPL/BL DUAL LICENSE BLOCK *****
32  */
33 #ifdef WITH_QUICKTIME
34
35 #if defined(_WIN32) || defined(__APPLE__)
36
37 #include "IMB_anim.h"
38 #include "BLO_sys_types.h"
39
40 #ifdef __APPLE__
41 #include <QuickTime/Movies.h>
42 #include <QuickTime/QuickTimeComponents.h>
43 #endif
44
45 #ifdef _WIN32
46 #include <Movies.h>
47 #include <QTML.h>
48 #include <TextUtils.h>
49 #include <QuickTimeComponents.h>
50 #endif /* _WIN32 */
51
52 #include "quicktime_import.h"
53
54
55 #define RECT_WIDTH(r)   (r.right-r.left)
56 #define RECT_HEIGHT(r)  (r.bottom-r.top)
57
58 #define QTIME_DEBUG 0
59
60
61 int anim_is_quicktime (char *name)
62 {
63         FSSpec  theFSSpec;
64         Str255  dst;
65         char    theFullPath[255];
66
67         Boolean                                         isMovieFile = false;
68         AliasHandle                                     myAlias = NULL;
69         Component                                       myImporter = NULL;
70 #ifdef __APPLE__
71         FInfo                                           myFinderInfo;
72         FSRef                                           myRef;
73 #endif
74         OSErr                                           err = noErr;
75                         
76         // dont let quicktime movie import handle these
77         if( BLI_testextensie(name, ".swf") ||
78                 BLI_testextensie(name, ".txt") ||
79                 BLI_testextensie(name, ".mpg") ||
80                 BLI_testextensie(name, ".avi") ||       // wouldnt be appropriate ;)
81                 BLI_testextensie(name, ".wav") ||
82                 BLI_testextensie(name, ".zip") ||
83                 BLI_testextensie(name, ".mp3")) return 0;
84
85         if(QTIME_DEBUG) printf("qt: checking as movie\n");
86
87         sprintf(theFullPath, "%s", name);
88 #ifdef __APPLE__
89         err = FSPathMakeRef(theFullPath, &myRef, 0);
90         err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
91 #else
92         CopyCStringToPascal(theFullPath, dst);
93         err = FSMakeFSSpec(0, 0L, dst, &theFSSpec);
94 #endif
95
96 #ifdef __APPLE__
97         // see whether the file type is MovieFileType; to do this, get the Finder information
98         err = FSpGetFInfo(&theFSSpec, &myFinderInfo);
99         if (err == noErr) {
100                 if (myFinderInfo.fdType == kQTFileTypeMovie) {
101                         return(true);
102                 }
103         }
104 #endif
105
106 /* on mac os x this results in using quicktime for other formats as well
107  * not sure whether this is intended
108  */
109         // if it isn't a movie file, see whether the file can be imported as a movie
110         err = QTNewAlias(&theFSSpec, &myAlias, true);
111         if (err == noErr) {
112                 if (myAlias != NULL) {
113                         err = GetMovieImporterForDataRef(rAliasType, (Handle)myAlias, kGetMovieImporterDontConsiderGraphicsImporters, &myImporter);
114                         DisposeHandle((Handle)myAlias);
115                 }
116         }
117         
118         if ((err == noErr) && (myImporter != NULL)) {           // this file is a movie file
119                 isMovieFile = true;
120         }
121
122         return(isMovieFile);
123 }
124
125
126 void free_anim_quicktime (struct anim *anim) {
127         if (anim == NULL) return;
128         if (anim->qtime == NULL) return;
129
130         UnlockPixels(anim->qtime->offscreenPixMap);
131
132         if(anim->qtime->have_gw)
133                 DisposeGWorld( anim->qtime->offscreenGWorld );
134         if(anim->qtime->ibuf)
135                 IMB_freeImBuf(anim->qtime->ibuf);
136
137         DisposeMovie( anim->qtime->movie );
138         CloseMovieFile( anim->qtime->movieRefNum );
139
140         if(anim->qtime->frameIndex) MEM_freeN (anim->qtime->frameIndex);
141         if(anim->qtime) MEM_freeN (anim->qtime);
142
143         anim->qtime = NULL;
144
145         anim->duration = 0;
146 }
147
148
149 OSErr QT_get_frameIndexes(struct anim *anim)
150 {
151         int i;
152         OSErr   anErr = noErr;
153         OSType  media = VideoMediaType;
154         TimeValue nextTime = 0;
155         TimeValue       startPoint;
156         TimeValue       tmpstartPoint;
157
158         startPoint = -1;
159
160         GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample+nextTimeEdgeOK, (TimeValue)1, &media, 0, 
161                                                                 1, &startPoint, NULL);
162
163         tmpstartPoint = startPoint;
164
165         anim->qtime->framecount = 0;
166
167         while(tmpstartPoint != -1) {
168                 nextTime = 0;
169                 GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, tmpstartPoint, 0, &nextTime, NULL);
170                 tmpstartPoint = nextTime;
171                 anim->qtime->framecount ++;
172         }
173         anim->qtime->frameIndex = (TimeValue *) MEM_callocN(sizeof(TimeValue) * anim->qtime->framecount, "qtframeindex");
174
175         //rewind
176         GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, (TimeValue)1, 0, &tmpstartPoint, NULL);
177
178         anim->qtime->frameIndex[0] = startPoint;
179         for(i = 1; i < anim->qtime->framecount; i++) {
180                 nextTime = 0;
181                 GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, startPoint, 0, &nextTime, NULL);
182                 startPoint = nextTime;
183                 anim->qtime->frameIndex[i] = nextTime;
184         }
185
186         anErr = GetMoviesError();
187         return anErr;
188 }
189
190
191 ImBuf * qtime_fetchibuf (struct anim *anim, int position)
192 {
193         PixMapHandle                    myPixMap = NULL;
194         Ptr                                             myPtr;
195
196         register int            index;
197         register int            boxsize;
198
199         register uint32_t       *readPos;
200         register uint32_t       *changePos;
201
202         ImBuf *ibuf = NULL;
203         unsigned int *rect;
204         unsigned char *crect;
205
206         if (anim == NULL) {
207                 return (NULL);
208         }
209
210         ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect, 0);
211         rect = ibuf->rect;
212
213         SetMovieTimeValue(anim->qtime->movie, anim->qtime->frameIndex[position]);
214         UpdateMovie(anim->qtime->movie);
215         MoviesTask(anim->qtime->movie, 0);
216
217
218         myPixMap = GetGWorldPixMap(anim->qtime->offscreenGWorld);
219         myPtr = GetPixBaseAddr(myPixMap);
220
221         if (myPtr == NULL) {
222                 printf ("Error reading frame from Quicktime");
223                 IMB_freeImBuf (ibuf);
224                 return NULL;
225         }
226
227         boxsize = anim->x * anim->y;
228         readPos = (uint32_t *) myPtr;
229         changePos = (uint32_t *) rect; //textureIMBuf *THE* data pointerrr
230
231 #ifdef __APPLE__
232         // Swap alpha byte to the end, so ARGB become RGBA; note this is
233  // big endian-centric.
234         for( index = 0; index < boxsize; index++, changePos++, readPos++ )
235                 *( changePos ) = ( ( *readPos & 0xFFFFFF ) << 8 ) |
236                         ( ( *readPos >> 24 ) & 0xFF );
237 #endif
238
239 #ifdef _WIN32
240         for( index = 0; index < boxsize; index++, changePos++, readPos++ )
241                 *( changePos ) =  *(readPos );
242
243         if(anim->qtime->depth < 32) {
244                 //add alpha to ibuf
245                 boxsize = anim->x * anim->y * 4;
246                 crect = (unsigned char *) rect;
247                 for( index = 0; index < boxsize; index+=4, crect+=4 )
248                          crect[3] = 0xFF;
249         }
250 #endif
251
252         IMB_flipy(ibuf);
253         return ibuf;
254 }
255
256
257 // following two functions only here to get movie pixeldepth
258
259 int GetFirstVideoMedia(struct anim *anim)
260 {
261         long    numTracks;
262         OSType  mediaType;
263
264         numTracks = GetMovieTrackCount(anim->qtime->movie);
265
266         for (anim->qtime->trackIndex=1; anim->qtime->trackIndex<=numTracks; (anim->qtime->trackIndex)++) {
267                 anim->qtime->theTrack = GetMovieIndTrack(anim->qtime->movie, anim->qtime->trackIndex);
268
269                 if (anim->qtime->theTrack)
270                         anim->qtime->theMedia = GetTrackMedia(anim->qtime->theTrack);
271
272                 if (anim->qtime->theMedia)
273                         GetMediaHandlerDescription(anim->qtime->theMedia,&mediaType, nil, nil);
274                 if (mediaType = VideoMediaType) return 1;
275         }
276
277         anim->qtime->trackIndex = 0;  // trackIndex can't be 0
278         return 0;      // went through all tracks and no video
279 }
280
281 short GetFirstVideoTrackPixelDepth(struct anim *anim)
282 {
283         SampleDescriptionHandle imageDescH =    (SampleDescriptionHandle)NewHandle(sizeof(Handle));
284         long    trackIndex = 0;
285         
286         if(!GetFirstVideoMedia(anim))
287                 return -1;
288
289         if (!anim->qtime->trackIndex || !anim->qtime->theMedia) return -1;  // we need both
290         GetMediaSampleDescription(anim->qtime->theMedia, anim->qtime->trackIndex, imageDescH);
291
292         return (*(ImageDescriptionHandle)imageDescH)->depth;
293 }
294
295
296 int startquicktime (struct anim *anim)
297 {
298         FSSpec          theFSSpec;
299
300         OSErr           err = noErr;
301         Str255          dst;
302         char            theFullPath[255];
303 #ifdef __APPLE__
304         FSRef           myRef;
305 #endif
306
307         anim->qtime = MEM_callocN (sizeof(QuicktimeMovie),"animqt");
308         anim->qtime->have_gw = FALSE;
309
310         if (anim->qtime == NULL) {
311                 if(QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
312                 return -1;
313         }
314
315         if(QTIME_DEBUG) printf("qt: attempting to load as movie %s\n", anim->name);
316         sprintf(theFullPath, "%s", anim->name);
317         
318 #ifdef __APPLE__
319         err = FSPathMakeRef(theFullPath, &myRef, 0);
320         err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
321 #else
322         CopyCStringToPascal(theFullPath, dst);
323         FSMakeFSSpec(0, 0L, dst, &theFSSpec);
324 #endif
325         
326         err = OpenMovieFile(&theFSSpec, &anim->qtime->movieRefNum, fsRdPerm);
327
328         if (err == noErr) {
329                 if(QTIME_DEBUG) printf("qt: movie opened\n");
330                 err = NewMovieFromFile(&anim->qtime->movie,
331                                                    anim->qtime->movieRefNum,
332                                                    &anim->qtime->movieResId, NULL, newMovieActive, NULL);
333         }
334
335         if (err) {
336                 if(QTIME_DEBUG) printf("qt: bad movie\n", anim->name);
337                 if (anim->qtime->movie) {
338                         DisposeMovie(anim->qtime->movie);
339                         MEM_freeN(anim->qtime);
340                         if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
341                         return -1;
342                 }
343         }
344
345         GetMovieBox(anim->qtime->movie, &anim->qtime->movieBounds);
346         anim->x = anim->qtime->movWidth = RECT_WIDTH(anim->qtime->movieBounds);
347         anim->y = anim->qtime->movHeight = RECT_HEIGHT(anim->qtime->movieBounds);
348         if(QTIME_DEBUG) printf("qt: got bounds\n", anim->name);
349
350         if(anim->x == 0 && anim->y == 0) {
351                 if(QTIME_DEBUG) printf("qt: error, no dimensions\n");
352                 free_anim_quicktime(anim);
353                 return -1;
354         }
355
356         anim->qtime->ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect, 0);
357
358 #ifdef _WIN32
359         err = NewGWorldFromPtr(&anim->qtime->offscreenGWorld,
360                  k32RGBAPixelFormat,
361                  &anim->qtime->movieBounds,
362                  NULL, NULL, 0,
363                 (unsigned char *)anim->qtime->ibuf->rect,
364                 anim->x * 4);
365 #else
366         err = NewGWorldFromPtr(&anim->qtime->offscreenGWorld,
367                  k32ARGBPixelFormat,
368                  &anim->qtime->movieBounds,
369                  NULL, NULL, 0,
370                 (unsigned char *)anim->qtime->ibuf->rect,
371                 anim->x * 4);
372 #endif /* _WIN32 */
373
374         if(err == noErr) {
375                 anim->qtime->have_gw = TRUE;
376
377                 SetMovieGWorld(anim->qtime->movie,
378                                  anim->qtime->offscreenGWorld,
379                                  GetGWorldDevice(anim->qtime->offscreenGWorld));
380
381                 QT_get_frameIndexes(anim);
382         }
383
384         anim->qtime->offscreenPixMap = GetGWorldPixMap(anim->qtime->offscreenGWorld);
385         LockPixels(anim->qtime->offscreenPixMap);
386
387         //fill blender's anim struct
388         anim->qtime->depth = GetFirstVideoTrackPixelDepth(anim);
389         
390         anim->duration = anim->qtime->framecount;
391         anim->params = 0;
392
393         anim->interlacing = 0;
394         anim->orientation = 0;
395         anim->framesize = anim->x * anim->y * 4;
396
397         anim->curposition = 0;
398
399         if(QTIME_DEBUG) printf("qt: load %s %dx%dx%d frames %d\n", anim->name, anim->qtime->movWidth,
400                 anim->qtime->movHeight, anim->qtime->depth, anim->qtime->framecount);
401
402         return 0;
403 }
404
405 int imb_is_a_quicktime (char *name)
406 {
407         GraphicsImportComponent         theImporter = NULL;
408
409         FSSpec  theFSSpec;
410         Str255  dst;
411         char    theFullPath[255];
412
413         Boolean                                         isMovieFile = false;
414         AliasHandle                                     myAlias = NULL;
415         Component                                       myImporter = NULL;
416 #ifdef __APPLE__
417         FInfo                                           myFinderInfo;
418         FSRef                                           myRef;
419 #endif
420         OSErr                                           err = noErr;
421
422         if(QTIME_DEBUG) printf("qt: checking as image %s\n", name);
423
424         // dont let quicktime image import handle these
425         if( BLI_testextensie(name, ".swf") ||
426                 BLI_testextensie(name, ".txt") ||
427                 BLI_testextensie(name, ".mpg") ||
428                 BLI_testextensie(name, ".wav") ||
429                 BLI_testextensie(name, ".mov") ||       // not as image, doesn't work
430                 BLI_testextensie(name, ".avi") ||
431                 BLI_testextensie(name, ".mp3")) return 0;
432
433         sprintf(theFullPath, "%s", name);
434 #ifdef __APPLE__
435         err = FSPathMakeRef(theFullPath, &myRef, 0);
436         err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
437 #else
438         CopyCStringToPascal(theFullPath, dst);
439         err = FSMakeFSSpec(0, 0L, dst, &theFSSpec);
440 #endif
441
442         GetGraphicsImporterForFile(&theFSSpec, &theImporter);
443
444         if (theImporter != NULL) {
445                 if(QTIME_DEBUG) printf("qt: %s valid\n", name);
446                 CloseComponent(theImporter);
447                 return 1;
448         }
449
450         return 0;
451 }
452
453 ImBuf  *imb_quicktime_decode(unsigned char *mem, int size, int flags)
454 {
455         Rect                                            myRect;
456         OSErr                                           err = noErr;
457         GraphicsImportComponent         gImporter = NULL;
458
459         ImageDescriptionHandle          desc;
460
461         ComponentInstance                       dataHandler;
462         PointerDataRef dataref = (PointerDataRef)NewHandle(sizeof(PointerDataRefRecord));
463
464         int x, y, depth;
465         int have_gw = FALSE;
466         ImBuf *ibuf = NULL;
467         ImBuf *imbuf = NULL;
468         GWorldPtr       offGWorld;
469         PixMapHandle            myPixMap = NULL;
470
471 #ifdef __APPLE__
472         Ptr                                     myPtr;
473
474         register int            index;
475         register int            boxsize;
476
477         register uint32_t       *readPos;
478         register uint32_t       *changePos;
479
480         ImBuf *wbuf = NULL;
481         unsigned int *rect;
482 #endif
483
484         if (mem == NULL)
485                 goto bail;
486         
487         if(QTIME_DEBUG) printf("qt: attempt to load mem as image\n");
488
489         (**dataref).data = mem;
490         (**dataref).dataLength = size;
491
492         err = OpenADataHandler(dataref,
493                                                         PointerDataHandlerSubType,
494                                                         nil,
495                                                         (OSType)0,
496                                                         nil,
497                                                         kDataHCanRead,
498                                                         &dataHandler);
499         if (err != noErr) {
500                 if(QTIME_DEBUG) printf("no datahandler\n");
501                 goto bail;
502         }
503
504         err = GetGraphicsImporterForDataRef(dataref, PointerDataHandlerSubType, &gImporter);
505         if (err != noErr) {
506                 if(QTIME_DEBUG) printf("no graphimport\n");
507                 goto bail;
508         }
509
510         err = GraphicsImportGetNaturalBounds(gImporter, &myRect);
511         if (err != noErr) {
512                 if(QTIME_DEBUG) printf("no bounds\n");
513                 goto bail;
514         }
515
516         err = GraphicsImportGetImageDescription (gImporter, &desc );
517         if (err != noErr) {
518                 if(QTIME_DEBUG) printf("no imagedescription\n");
519                 goto bail;
520         }
521
522         x = RECT_WIDTH(myRect);
523         y = RECT_HEIGHT(myRect);
524         depth = (**desc).depth;
525
526         if (flags & IB_test) {
527                 ibuf = IMB_allocImBuf(x, y, depth, 0, 0);
528                 ibuf->ftype = QUICKTIME;
529                 DisposeHandle((Handle)dataref);
530                 if (gImporter != NULL)  CloseComponent(gImporter);
531                 return ibuf;
532         }
533
534 #ifdef __APPLE__
535         ibuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);
536         wbuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);
537
538         err = NewGWorldFromPtr(&offGWorld,
539                                                 k32ARGBPixelFormat,
540                                                 &myRect, NULL, NULL, 0,
541                                                 (unsigned char *)wbuf->rect, x * 4);
542 #else
543
544         ibuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);   
545
546         err = NewGWorldFromPtr(&offGWorld,
547                                                         k32RGBAPixelFormat,
548                                                         &myRect, NULL, NULL, 0,
549                                                         (unsigned char *)ibuf->rect, x * 4);
550 #endif
551         
552         if (err != noErr) {
553                 if(QTIME_DEBUG) printf("no newgworld\n");
554                 goto bail;
555         } else {
556                 have_gw = TRUE;
557         }
558
559         GraphicsImportSetGWorld(gImporter, offGWorld, NULL);
560         GraphicsImportDraw(gImporter);
561
562 #ifdef __APPLE__
563         rect = ibuf->rect;
564
565         myPixMap = GetGWorldPixMap(offGWorld);
566         LockPixels(myPixMap);
567         myPtr = GetPixBaseAddr(myPixMap);
568
569         if (myPtr == NULL) {
570                 printf ("Error reading frame from Quicktime");
571                 IMB_freeImBuf (ibuf);
572                 return NULL;
573         }
574
575         boxsize = x * y;
576         readPos = (uint32_t *) myPtr;
577         changePos = (uint32_t *) rect;
578
579         for( index = 0; index < boxsize; index++, changePos++, readPos++ )
580                 *( changePos ) = ( ( *readPos & 0xFFFFFF ) << 8 ) |
581                         ( ( *readPos >> 24 ) & 0xFF );
582 #endif
583
584 bail:
585
586         DisposeHandle((Handle)dataref);
587         UnlockPixels(myPixMap);
588         if(have_gw) DisposeGWorld(offGWorld);
589
590 #ifdef __APPLE__
591         if (wbuf) {
592                 IMB_freeImBuf (wbuf);
593                 wbuf = NULL;
594         }
595 #endif
596
597         if (gImporter != NULL)  CloseComponent(gImporter);
598
599         if (err != noErr) {
600                 if(QTIME_DEBUG) printf("quicktime import unsuccesfull\n");
601                 if (ibuf) {
602                         IMB_freeImBuf (ibuf);
603                         ibuf = NULL;
604                 }
605         }
606
607         if(ibuf) {
608 #ifdef _WIN32
609                 // add alpha layer, might also be nescessary for OSX
610                 int i;
611                 int box = x * y;
612                 unsigned char *arect = (unsigned char *) ibuf->rect;
613
614                 if(depth < 32)
615                         for(i = 0; i < box; i++, arect+=4)
616                                  arect[3] = 0xFF;
617 #endif
618                 IMB_flipy(ibuf);
619                 ibuf->ftype = QUICKTIME;
620         }
621         return ibuf;
622 }
623
624 #endif /* _WIN32 || __APPLE__ */
625
626 #endif /* WITH_QUICKTIME */
627
628
629 #if 0
630
631 struct ImageDescription {
632      long         idSize;
633      CodecType    cType;
634      long         resvd1;
635      short        resvd2;
636      short        dataRefIndex;
637      short        version;
638      short        revisionLevel;
639      long         vendor;
640      CodecQ       temporalQuality;
641      CodecQ       spatialQuality;
642      short        width;
643      short        height;
644      Fixed        hRes;
645      Fixed        vRes;
646      long         dataSize;
647      short        frameCount;
648      Str31        name;
649      short        depth;
650      short        clutID;
651 };
652
653 #endif // 0