6 * Code to use Quicktime to load images/movies as texture.
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
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.
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.
27 * The Original Code is written by Rob Haarsma (phase)
29 * Contributor(s): Stefan Gartner (sgefant)
31 * ***** END GPL/BL DUAL LICENSE BLOCK *****
35 #if defined(_WIN32) || defined(__APPLE__)
38 #include "BLO_sys_types.h"
39 #include "BKE_global.h"
40 #include "BLI_dynstr.h"
43 #include <QuickTime/Movies.h>
44 #include <QuickTime/QuickTimeComponents.h>
50 #include <TextUtils.h>
51 #include <QuickTimeComponents.h>
55 #include "quicktime_import.h"
56 #include "quicktime_export.h"
59 #define RECT_WIDTH(r) (r.right-r.left)
60 #define RECT_HEIGHT(r) (r.bottom-r.top)
65 void quicktime_init(void)
68 if (InitializeQTML(0) != noErr)
69 G.have_quicktime = FALSE;
71 G.have_quicktime = TRUE;
74 /* Initialize QuickTime */
75 #if defined(_WIN32) || defined (__APPLE__)
76 if (EnterMovies() != noErr)
77 G.have_quicktime = FALSE;
79 #endif /* _WIN32 || __APPLE__ */
81 /* inititalize quicktime codec registry */
84 G.have_quicktime = TRUE;
88 void quicktime_exit(void)
90 #if defined(_WIN32) || defined(__APPLE__)
92 if(G.have_quicktime) {
93 free_qtcomponentdata();
99 #endif /* WITH_QUICKTIME */
100 #endif /* _WIN32 || __APPLE__ */
105 char *get_valid_qtname(char *name)
107 TCHAR Buffer[MAX_PATH];
110 DynStr *ds= BLI_dynstr_new();
112 dwRet = GetCurrentDirectory(MAX_PATH, Buffer);
117 if(name[0] == '/' || name[0] == '\\') {
118 drive[0] = Buffer[0];
121 BLI_dynstr_append(ds, drive);
122 BLI_dynstr_append(ds, ":");
123 BLI_dynstr_append(ds, name);
125 BLI_dynstr_append(ds, Buffer);
126 BLI_dynstr_append(ds, "/");
127 BLI_dynstr_append(ds, name);
130 BLI_dynstr_append(ds, name);
133 qtname= BLI_dynstr_get_cstring(ds);
141 int anim_is_quicktime (char *name)
144 char theFullPath[255];
146 Boolean isMovieFile = false;
147 AliasHandle myAlias = NULL;
148 Component myImporter = NULL;
158 // dont let quicktime movie import handle these
159 if( BLI_testextensie(name, ".swf") ||
160 BLI_testextensie(name, ".txt") ||
161 BLI_testextensie(name, ".mpg") ||
162 BLI_testextensie(name, ".avi") || // wouldnt be appropriate ;)
163 BLI_testextensie(name, ".tga") ||
164 BLI_testextensie(name, ".png") ||
165 BLI_testextensie(name, ".jpg") ||
166 BLI_testextensie(name, ".wav") ||
167 BLI_testextensie(name, ".zip") ||
168 BLI_testextensie(name, ".mp3")) return 0;
170 if(QTIME_DEBUG) printf("qt: checking as movie: %s\n", name);
173 sprintf(theFullPath, "%s", name);
175 err = FSPathMakeRef(theFullPath, &myRef, 0);
176 err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
178 qtname = get_valid_qtname(name);
179 sprintf(theFullPath, "%s", qtname);
182 CopyCStringToPascal(theFullPath, dst);
183 err = FSMakeFSSpec(0, 0L, dst, &theFSSpec);
187 // see whether the file type is MovieFileType; to do this, get the Finder information
188 err = FSpGetFInfo(&theFSSpec, &myFinderInfo);
190 if (myFinderInfo.fdType == kQTFileTypeMovie) {
196 /* on mac os x this results in using quicktime for other formats as well
197 * not sure whether this is intended
199 // if it isn't a movie file, see whether the file can be imported as a movie
200 err = QTNewAlias(&theFSSpec, &myAlias, true);
202 if (myAlias != NULL) {
203 err = GetMovieImporterForDataRef(rAliasType, (Handle)myAlias, kGetMovieImporterDontConsiderGraphicsImporters, &myImporter);
204 DisposeHandle((Handle)myAlias);
208 if ((err == noErr) && (myImporter != NULL)) { // this file is a movie file
216 void free_anim_quicktime (struct anim *anim) {
217 if (anim == NULL) return;
218 if (anim->qtime == NULL) return;
220 UnlockPixels(anim->qtime->offscreenPixMap);
222 if(anim->qtime->have_gw)
223 DisposeGWorld( anim->qtime->offscreenGWorld );
224 if(anim->qtime->ibuf)
225 IMB_freeImBuf(anim->qtime->ibuf);
227 DisposeMovie( anim->qtime->movie );
228 CloseMovieFile( anim->qtime->movieRefNum );
230 if(anim->qtime->frameIndex) MEM_freeN (anim->qtime->frameIndex);
231 if(anim->qtime) MEM_freeN (anim->qtime);
239 OSErr QT_get_frameIndexes(struct anim *anim)
243 OSType media = VideoMediaType;
244 TimeValue nextTime = 0;
245 TimeValue startPoint;
246 TimeValue tmpstartPoint;
250 GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample+nextTimeEdgeOK, (TimeValue)1, &media, 0,
251 1, &startPoint, NULL);
253 tmpstartPoint = startPoint;
255 anim->qtime->framecount = 0;
257 while(tmpstartPoint != -1) {
259 GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, tmpstartPoint, 0, &nextTime, NULL);
260 tmpstartPoint = nextTime;
261 anim->qtime->framecount ++;
263 anim->qtime->frameIndex = (TimeValue *) MEM_callocN(sizeof(TimeValue) * anim->qtime->framecount, "qtframeindex");
266 GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, (TimeValue)1, 0, &tmpstartPoint, NULL);
268 anim->qtime->frameIndex[0] = startPoint;
269 for(i = 1; i < anim->qtime->framecount; i++) {
271 GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, startPoint, 0, &nextTime, NULL);
272 startPoint = nextTime;
273 anim->qtime->frameIndex[i] = nextTime;
276 anErr = GetMoviesError();
281 ImBuf * qtime_fetchibuf (struct anim *anim, int position)
283 PixMapHandle myPixMap = NULL;
287 register int boxsize;
289 register uint32_t *readPos;
290 register uint32_t *changePos;
295 unsigned char *crect;
302 ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect, 0);
305 SetMovieTimeValue(anim->qtime->movie, anim->qtime->frameIndex[position]);
306 UpdateMovie(anim->qtime->movie);
307 MoviesTask(anim->qtime->movie, 0);
310 myPixMap = GetGWorldPixMap(anim->qtime->offscreenGWorld);
311 myPtr = GetPixBaseAddr(myPixMap);
314 printf ("Error reading frame from Quicktime");
315 IMB_freeImBuf (ibuf);
319 boxsize = anim->x * anim->y;
320 readPos = (uint32_t *) myPtr;
321 changePos = (uint32_t *) rect; //textureIMBuf *THE* data pointerrr
324 // Swap alpha byte to the end, so ARGB become RGBA; note this is
325 // big endian-centric.
326 for( index = 0; index < boxsize; index++, changePos++, readPos++ )
327 *( changePos ) = ( ( *readPos & 0xFFFFFF ) << 8 ) |
328 ( ( *readPos >> 24 ) & 0xFF );
332 for( index = 0; index < boxsize; index++, changePos++, readPos++ )
333 *( changePos ) = *(readPos );
335 if(anim->qtime->depth < 32) {
337 boxsize = anim->x * anim->y * 4;
338 crect = (unsigned char *) rect;
339 for( index = 0; index < boxsize; index+=4, crect+=4 )
349 // following two functions only here to get movie pixeldepth
351 int GetFirstVideoMedia(struct anim *anim)
356 numTracks = GetMovieTrackCount(anim->qtime->movie);
358 for (anim->qtime->trackIndex=1; anim->qtime->trackIndex<=numTracks; (anim->qtime->trackIndex)++) {
359 anim->qtime->theTrack = GetMovieIndTrack(anim->qtime->movie, anim->qtime->trackIndex);
361 if (anim->qtime->theTrack)
362 anim->qtime->theMedia = GetTrackMedia(anim->qtime->theTrack);
364 if (anim->qtime->theMedia)
365 GetMediaHandlerDescription(anim->qtime->theMedia,&mediaType, nil, nil);
366 if (mediaType == VideoMediaType) return 1;
369 anim->qtime->trackIndex = 0; // trackIndex can't be 0
370 return 0; // went through all tracks and no video
373 short GetFirstVideoTrackPixelDepth(struct anim *anim)
375 SampleDescriptionHandle imageDescH = (SampleDescriptionHandle)NewHandle(sizeof(Handle));
378 if(!GetFirstVideoMedia(anim))
381 if (!anim->qtime->trackIndex || !anim->qtime->theMedia) return -1; // we need both
382 GetMediaSampleDescription(anim->qtime->theMedia, anim->qtime->trackIndex, imageDescH);
384 return (*(ImageDescriptionHandle)imageDescH)->depth;
388 int startquicktime (struct anim *anim)
393 char theFullPath[255];
401 anim->qtime = MEM_callocN (sizeof(QuicktimeMovie),"animqt");
402 anim->qtime->have_gw = FALSE;
404 if (anim->qtime == NULL) {
405 if(QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
409 if(QTIME_DEBUG) printf("qt: attempting to load as movie %s\n", anim->name);
412 sprintf(theFullPath, "%s", anim->name);
414 err = FSPathMakeRef(theFullPath, &myRef, 0);
415 err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
417 qtname = get_valid_qtname(anim->name);
418 sprintf(theFullPath, "%s", qtname);
421 CopyCStringToPascal(theFullPath, dst);
422 FSMakeFSSpec(0, 0L, dst, &theFSSpec);
425 err = OpenMovieFile(&theFSSpec, &anim->qtime->movieRefNum, fsRdPerm);
428 if(QTIME_DEBUG) printf("qt: movie opened\n");
429 err = NewMovieFromFile(&anim->qtime->movie,
430 anim->qtime->movieRefNum,
431 &anim->qtime->movieResId, NULL, newMovieActive, NULL);
435 if(QTIME_DEBUG) printf("qt: bad movie %s\n", anim->name);
436 if (anim->qtime->movie) {
437 DisposeMovie(anim->qtime->movie);
438 MEM_freeN(anim->qtime);
439 if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
444 GetMovieBox(anim->qtime->movie, &anim->qtime->movieBounds);
445 anim->x = anim->qtime->movWidth = RECT_WIDTH(anim->qtime->movieBounds);
446 anim->y = anim->qtime->movHeight = RECT_HEIGHT(anim->qtime->movieBounds);
447 if(QTIME_DEBUG) printf("qt: got bounds %s\n", anim->name);
449 if(anim->x == 0 && anim->y == 0) {
450 if(QTIME_DEBUG) printf("qt: error, no dimensions\n");
451 free_anim_quicktime(anim);
455 anim->qtime->ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect, 0);
458 err = NewGWorldFromPtr(&anim->qtime->offscreenGWorld,
460 &anim->qtime->movieBounds,
462 (unsigned char *)anim->qtime->ibuf->rect,
465 err = NewGWorldFromPtr(&anim->qtime->offscreenGWorld,
467 &anim->qtime->movieBounds,
469 (unsigned char *)anim->qtime->ibuf->rect,
474 anim->qtime->have_gw = TRUE;
476 SetMovieGWorld(anim->qtime->movie,
477 anim->qtime->offscreenGWorld,
478 GetGWorldDevice(anim->qtime->offscreenGWorld));
479 SetMoviePlayHints(anim->qtime->movie, hintsHighQuality, hintsHighQuality);
481 QT_get_frameIndexes(anim);
484 anim->qtime->offscreenPixMap = GetGWorldPixMap(anim->qtime->offscreenGWorld);
485 LockPixels(anim->qtime->offscreenPixMap);
487 //fill blender's anim struct
488 anim->qtime->depth = GetFirstVideoTrackPixelDepth(anim);
490 anim->duration = anim->qtime->framecount;
493 anim->interlacing = 0;
494 anim->orientation = 0;
495 anim->framesize = anim->x * anim->y * 4;
497 anim->curposition = 0;
499 if(QTIME_DEBUG) printf("qt: load %s %dx%dx%d frames %d\n", anim->name, anim->qtime->movWidth,
500 anim->qtime->movHeight, anim->qtime->depth, anim->qtime->framecount);
505 int imb_is_a_quicktime (char *name)
507 GraphicsImportComponent theImporter = NULL;
511 char theFullPath[255];
513 Boolean isMovieFile = false;
514 AliasHandle myAlias = NULL;
515 Component myImporter = NULL;
522 if(QTIME_DEBUG) printf("qt: checking as image %s\n", name);
524 // dont let quicktime image import handle these
525 if( BLI_testextensie(name, ".swf") ||
526 BLI_testextensie(name, ".txt") ||
527 BLI_testextensie(name, ".mpg") ||
528 BLI_testextensie(name, ".wav") ||
529 BLI_testextensie(name, ".mov") || // not as image, doesn't work
530 BLI_testextensie(name, ".avi") ||
531 BLI_testextensie(name, ".mp3")) return 0;
533 sprintf(theFullPath, "%s", name);
535 err = FSPathMakeRef(theFullPath, &myRef, 0);
536 err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
538 CopyCStringToPascal(theFullPath, dst);
539 err = FSMakeFSSpec(0, 0L, dst, &theFSSpec);
542 GetGraphicsImporterForFile(&theFSSpec, &theImporter);
544 if (theImporter != NULL) {
545 if(QTIME_DEBUG) printf("qt: %s valid\n", name);
546 CloseComponent(theImporter);
553 ImBuf *imb_quicktime_decode(unsigned char *mem, int size, int flags)
557 GraphicsImportComponent gImporter = NULL;
559 ImageDescriptionHandle desc;
561 ComponentInstance dataHandler;
562 PointerDataRef dataref = (PointerDataRef)NewHandle(sizeof(PointerDataRefRecord));
569 PixMapHandle myPixMap = NULL;
575 register int boxsize;
577 register uint32_t *readPos;
578 register uint32_t *changePos;
587 if(QTIME_DEBUG) printf("qt: attempt to load mem as image\n");
589 (**dataref).data = mem;
590 (**dataref).dataLength = size;
592 err = OpenADataHandler((Handle)dataref,
593 PointerDataHandlerSubType,
600 if(QTIME_DEBUG) printf("no datahandler\n");
604 err = GetGraphicsImporterForDataRef((Handle)dataref, PointerDataHandlerSubType, &gImporter);
606 if(QTIME_DEBUG) printf("no graphimport\n");
610 err = GraphicsImportGetNaturalBounds(gImporter, &myRect);
612 if(QTIME_DEBUG) printf("no bounds\n");
616 err = GraphicsImportGetImageDescription (gImporter, &desc );
618 if(QTIME_DEBUG) printf("no imagedescription\n");
622 x = RECT_WIDTH(myRect);
623 y = RECT_HEIGHT(myRect);
624 depth = (**desc).depth;
626 if (flags & IB_test) {
627 ibuf = IMB_allocImBuf(x, y, depth, 0, 0);
628 ibuf->ftype = QUICKTIME;
629 DisposeHandle((Handle)dataref);
630 if (gImporter != NULL) CloseComponent(gImporter);
635 ibuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);
636 wbuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);
638 err = NewGWorldFromPtr(&offGWorld,
640 &myRect, NULL, NULL, 0,
641 (unsigned char *)wbuf->rect, x * 4);
644 ibuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);
646 err = NewGWorldFromPtr(&offGWorld,
648 &myRect, NULL, NULL, 0,
649 (unsigned char *)ibuf->rect, x * 4);
653 if(QTIME_DEBUG) printf("no newgworld\n");
659 GraphicsImportSetGWorld(gImporter, offGWorld, NULL);
660 GraphicsImportDraw(gImporter);
665 myPixMap = GetGWorldPixMap(offGWorld);
666 LockPixels(myPixMap);
667 myPtr = GetPixBaseAddr(myPixMap);
670 printf ("Error reading frame from Quicktime");
671 IMB_freeImBuf (ibuf);
676 readPos = (uint32_t *) myPtr;
677 changePos = (uint32_t *) rect;
679 for( index = 0; index < boxsize; index++, changePos++, readPos++ )
680 *( changePos ) = ( ( *readPos & 0xFFFFFF ) << 8 ) |
681 ( ( *readPos >> 24 ) & 0xFF );
686 DisposeHandle((Handle)dataref);
687 UnlockPixels(myPixMap);
688 if(have_gw) DisposeGWorld(offGWorld);
692 IMB_freeImBuf (wbuf);
697 if (gImporter != NULL) CloseComponent(gImporter);
700 if(QTIME_DEBUG) printf("quicktime import unsuccesfull\n");
702 IMB_freeImBuf (ibuf);
710 // add non transparent alpha layer, so images without alpha show up in the sequence editor
711 // exception for GIF images since these can be transparent without being 32 bit
712 // (might also be nescessary for OSX)
715 unsigned char *arect = (unsigned char *) ibuf->rect;
717 if( depth < 32 && (**desc).cType != kGIFCodecType) {
718 for(i = 0; i < box; i++, arect+=4)
724 ibuf->ftype = QUICKTIME;
729 #endif /* _WIN32 || __APPLE__ */
731 #endif /* WITH_QUICKTIME */
736 struct ImageDescription {
745 CodecQ temporalQuality;
746 CodecQ spatialQuality;