Logo Search packages:      
Sourcecode: blender version File versions

quicktime_import.c

/**
 * $Id: quicktime_import.c,v 1.13 2004/12/19 13:45:03 phase Exp $
 *
 * quicktime_import.c
 *
 * Code to use Quicktime to load images/movies as texture.
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 *
 * The Original Code is written by Rob Haarsma (phase)
 *
 * Contributor(s): Stefan Gartner (sgefant)
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */
#ifdef WITH_QUICKTIME

#if defined(_WIN32) || defined(__APPLE__)

#include "IMB_anim.h"
#include "BLO_sys_types.h"
#include "BKE_global.h"
#include "BLI_dynstr.h"

#ifdef __APPLE__
#include <QuickTime/Movies.h>
#include <QuickTime/QuickTimeComponents.h>
#endif

#ifdef _WIN32
#include <Movies.h>
#include <QTML.h>
#include <TextUtils.h>
#include <QuickTimeComponents.h>
#endif /* _WIN32 */


#include "quicktime_import.h"
#include "quicktime_export.h"


#define     RECT_WIDTH(r)     (r.right-r.left)
#define     RECT_HEIGHT(r)    (r.bottom-r.top)

#define QTIME_DEBUG 0


void quicktime_init(void)
{
#ifdef _WIN32
      if (InitializeQTML(0) != noErr)
            G.have_quicktime = FALSE;
      else
            G.have_quicktime = TRUE;
#endif /* _WIN32 */

      /* Initialize QuickTime */
#if defined(_WIN32) || defined (__APPLE__)
      if (EnterMovies() != noErr)
            G.have_quicktime = FALSE;
      else
#endif /* _WIN32 || __APPLE__ */
#ifdef __linux__
      /* inititalize quicktime codec registry */
            lqt_registry_init();
#endif
      G.have_quicktime = TRUE;
}


void quicktime_exit(void)
{
#if defined(_WIN32) || defined(__APPLE__)
#ifdef WITH_QUICKTIME
      if(G.have_quicktime) {
            free_qtcodecdataExt();
            ExitMovies();
#ifdef _WIN32
            TerminateQTML();
#endif /* _WIN32 */
      }
#endif /* WITH_QUICKTIME */
#endif /* _WIN32 || __APPLE__ */
}


#ifdef _WIN32
char *get_valid_qtname(char *name)
{
      TCHAR Buffer[MAX_PATH];
      DWORD dwRet;
      char *qtname;
      DynStr *ds= BLI_dynstr_new();

      dwRet = GetCurrentDirectory(MAX_PATH, Buffer);

      if(name[1] != ':') {
            char drive[2];

            if(name[0] == '/' || name[0] == '\\') {
                  drive[0] = Buffer[0];
                  drive[1] = '\0';

                  BLI_dynstr_append(ds, drive);
                  BLI_dynstr_append(ds, ":");
                  BLI_dynstr_append(ds, name);
            } else {
                  BLI_dynstr_append(ds, Buffer);
                  BLI_dynstr_append(ds, "/");
                  BLI_dynstr_append(ds, name);
            }
      } else {
            BLI_dynstr_append(ds, name);
      }

      qtname= BLI_dynstr_get_cstring(ds);
      BLI_dynstr_free(ds);

      return qtname;
}
#endif /* _WIN32 */


int anim_is_quicktime (char *name)
{
      FSSpec      theFSSpec;
      char  theFullPath[255];

      Boolean                                   isMovieFile = false;
      AliasHandle                         myAlias = NULL;
      Component                           myImporter = NULL;
#ifdef __APPLE__
      FInfo                               myFinderInfo;
      FSRef                               myRef;
#else
      char *qtname;
      Str255  dst;
#endif
      OSErr                               err = noErr;
                  
      // dont let quicktime movie import handle these
      if( BLI_testextensie(name, ".swf") ||
            BLI_testextensie(name, ".txt") ||
            BLI_testextensie(name, ".mpg") ||
            BLI_testextensie(name, ".avi") ||   // wouldnt be appropriate ;)
            BLI_testextensie(name, ".tga") ||
            BLI_testextensie(name, ".png") ||
            BLI_testextensie(name, ".jpg") ||
            BLI_testextensie(name, ".wav") ||
            BLI_testextensie(name, ".zip") ||
            BLI_testextensie(name, ".mp3")) return 0;

      if(QTIME_DEBUG) printf("qt: checking as movie: %s\n", name);

#ifdef __APPLE__
      sprintf(theFullPath, "%s", name);

      err = FSPathMakeRef(theFullPath, &myRef, 0);
      err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
#else
      qtname = get_valid_qtname(name);
      sprintf(theFullPath, "%s", qtname);
      MEM_freeN(qtname);

      CopyCStringToPascal(theFullPath, dst);
      err = FSMakeFSSpec(0, 0L, dst, &theFSSpec);
#endif

#ifdef __APPLE__
      // see whether the file type is MovieFileType; to do this, get the Finder information
      err = FSpGetFInfo(&theFSSpec, &myFinderInfo);
      if (err == noErr) {
            if (myFinderInfo.fdType == kQTFileTypeMovie) {
                  return(true);
            }
      }
#endif

/* on mac os x this results in using quicktime for other formats as well
 * not sure whether this is intended
 */
      // if it isn't a movie file, see whether the file can be imported as a movie
      err = QTNewAlias(&theFSSpec, &myAlias, true);
      if (err == noErr) {
            if (myAlias != NULL) {
                  err = GetMovieImporterForDataRef(rAliasType, (Handle)myAlias, kGetMovieImporterDontConsiderGraphicsImporters, &myImporter);
                  DisposeHandle((Handle)myAlias);
            }
      }
      
      if ((err == noErr) && (myImporter != NULL)) {         // this file is a movie file
            isMovieFile = true;
      }

      return(isMovieFile);
}


void free_anim_quicktime (struct anim *anim) {
      if (anim == NULL) return;
      if (anim->qtime == NULL) return;

      UnlockPixels(anim->qtime->offscreenPixMap);

      if(anim->qtime->have_gw)
            DisposeGWorld( anim->qtime->offscreenGWorld );
      if(anim->qtime->ibuf)
            IMB_freeImBuf(anim->qtime->ibuf);

      DisposeMovie( anim->qtime->movie );
      CloseMovieFile( anim->qtime->movieRefNum );

      if(anim->qtime->frameIndex) MEM_freeN (anim->qtime->frameIndex);
      if(anim->qtime) MEM_freeN (anim->qtime);

      anim->qtime = NULL;

      anim->duration = 0;
}


OSErr QT_get_frameIndexes(struct anim *anim)
{
      int i;
      OSErr anErr = noErr;
      OSType      media = VideoMediaType;
      TimeValue nextTime = 0;
      TimeValue   startPoint;
      TimeValue   tmpstartPoint;

      startPoint = -1;

      GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample+nextTimeEdgeOK, (TimeValue)1, &media, 0, 
                                                1, &startPoint, NULL);

      tmpstartPoint = startPoint;

      anim->qtime->framecount = 0;

      while(tmpstartPoint != -1) {
            nextTime = 0;
            GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, tmpstartPoint, 0, &nextTime, NULL);
            tmpstartPoint = nextTime;
            anim->qtime->framecount ++;
      }
      anim->qtime->frameIndex = (TimeValue *) MEM_callocN(sizeof(TimeValue) * anim->qtime->framecount, "qtframeindex");

      //rewind
      GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, (TimeValue)1, 0, &tmpstartPoint, NULL);

      anim->qtime->frameIndex[0] = startPoint;
      for(i = 1; i < anim->qtime->framecount; i++) {
            nextTime = 0;
            GetMovieNextInterestingTime(anim->qtime->movie, nextTimeMediaSample, 1, &media, startPoint, 0, &nextTime, NULL);
            startPoint = nextTime;
            anim->qtime->frameIndex[i] = nextTime;
      }

      anErr = GetMoviesError();
      return anErr;
}


ImBuf * qtime_fetchibuf (struct anim *anim, int position)
{
      PixMapHandle                  myPixMap = NULL;
      Ptr                                 myPtr;

      register int            index;
      register int            boxsize;

      register uint32_t *readPos;
      register uint32_t *changePos;

      ImBuf *ibuf = NULL;
      unsigned int *rect;
#ifdef _WIN32
      unsigned char *crect;
#endif

      if (anim == NULL) {
            return (NULL);
      }

      ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect, 0);
      rect = ibuf->rect;

      SetMovieTimeValue(anim->qtime->movie, anim->qtime->frameIndex[position]);
      UpdateMovie(anim->qtime->movie);
      MoviesTask(anim->qtime->movie, 0);


      myPixMap = GetGWorldPixMap(anim->qtime->offscreenGWorld);
      myPtr = GetPixBaseAddr(myPixMap);

      if (myPtr == NULL) {
            printf ("Error reading frame from Quicktime");
            IMB_freeImBuf (ibuf);
            return NULL;
      }

      boxsize = anim->x * anim->y;
      readPos = (uint32_t *) myPtr;
      changePos = (uint32_t *) rect; //textureIMBuf *THE* data pointerrr

#ifdef __APPLE__
      // Swap alpha byte to the end, so ARGB become RGBA; note this is
 // big endian-centric.
      for( index = 0; index < boxsize; index++, changePos++, readPos++ )
            *( changePos ) = ( ( *readPos & 0xFFFFFF ) << 8 ) |
                  ( ( *readPos >> 24 ) & 0xFF );
#endif

#ifdef _WIN32
      for( index = 0; index < boxsize; index++, changePos++, readPos++ )
            *( changePos ) =  *(readPos );

      if(anim->qtime->depth < 32) {
            //add alpha to ibuf
            boxsize = anim->x * anim->y * 4;
            crect = (unsigned char *) rect;
            for( index = 0; index < boxsize; index+=4, crect+=4 )
                   crect[3] = 0xFF;
      }
#endif

      IMB_flipy(ibuf);
      return ibuf;
}


// following two functions only here to get movie pixeldepth

int GetFirstVideoMedia(struct anim *anim)
{
      long    numTracks;
      OSType  mediaType;

      numTracks = GetMovieTrackCount(anim->qtime->movie);

      for (anim->qtime->trackIndex=1; anim->qtime->trackIndex<=numTracks; (anim->qtime->trackIndex)++) {
            anim->qtime->theTrack = GetMovieIndTrack(anim->qtime->movie, anim->qtime->trackIndex);

            if (anim->qtime->theTrack)
                  anim->qtime->theMedia = GetTrackMedia(anim->qtime->theTrack);

            if (anim->qtime->theMedia)
                  GetMediaHandlerDescription(anim->qtime->theMedia,&mediaType, nil, nil);
            if (mediaType == VideoMediaType) return 1;
      }

      anim->qtime->trackIndex = 0;  // trackIndex can't be 0
      return 0;      // went through all tracks and no video
}

short GetFirstVideoTrackPixelDepth(struct anim *anim)
{
      SampleDescriptionHandle imageDescH =      (SampleDescriptionHandle)NewHandle(sizeof(Handle));
      long  trackIndex = 0;
      
      if(!GetFirstVideoMedia(anim))
            return -1;

      if (!anim->qtime->trackIndex || !anim->qtime->theMedia) return -1;  // we need both
      GetMediaSampleDescription(anim->qtime->theMedia, anim->qtime->trackIndex, imageDescH);

      return (*(ImageDescriptionHandle)imageDescH)->depth;
}


int startquicktime (struct anim *anim)
{
      FSSpec            theFSSpec;

      OSErr       err = noErr;
      char        theFullPath[255];
#ifdef __APPLE__
      FSRef       myRef;
#else
      char        *qtname;
      Str255            dst;
#endif

      anim->qtime = MEM_callocN (sizeof(QuicktimeMovie),"animqt");
      anim->qtime->have_gw = FALSE;

      if (anim->qtime == NULL) {
            if(QTIME_DEBUG) printf("Can't alloc qtime: %s\n", anim->name);
            return -1;
      }

      if(QTIME_DEBUG) printf("qt: attempting to load as movie %s\n", anim->name);
      
#ifdef __APPLE__
      sprintf(theFullPath, "%s", anim->name);

      err = FSPathMakeRef(theFullPath, &myRef, 0);
      err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
#else
      qtname = get_valid_qtname(anim->name);
      sprintf(theFullPath, "%s", qtname);
      MEM_freeN(qtname);

      CopyCStringToPascal(theFullPath, dst);
      FSMakeFSSpec(0, 0L, dst, &theFSSpec);
#endif
      
      err = OpenMovieFile(&theFSSpec, &anim->qtime->movieRefNum, fsRdPerm);

      if (err == noErr) {
            if(QTIME_DEBUG) printf("qt: movie opened\n");
            err = NewMovieFromFile(&anim->qtime->movie,
                                       anim->qtime->movieRefNum,
                                       &anim->qtime->movieResId, NULL, newMovieActive, NULL);
      }

      if (err) {
            if(QTIME_DEBUG) printf("qt: bad movie %s\n", anim->name);
            if (anim->qtime->movie) {
                  DisposeMovie(anim->qtime->movie);
                  MEM_freeN(anim->qtime);
                  if(QTIME_DEBUG) printf("qt: can't load %s\n", anim->name);
                  return -1;
            }
      }

      GetMovieBox(anim->qtime->movie, &anim->qtime->movieBounds);
      anim->x = anim->qtime->movWidth = RECT_WIDTH(anim->qtime->movieBounds);
      anim->y = anim->qtime->movHeight = RECT_HEIGHT(anim->qtime->movieBounds);
      if(QTIME_DEBUG) printf("qt: got bounds %s\n", anim->name);

      if(anim->x == 0 && anim->y == 0) {
            if(QTIME_DEBUG) printf("qt: error, no dimensions\n");
            free_anim_quicktime(anim);
            return -1;
      }

      anim->qtime->ibuf = IMB_allocImBuf (anim->x, anim->y, 32, IB_rect, 0);

#ifdef _WIN32
      err = NewGWorldFromPtr(&anim->qtime->offscreenGWorld,
             k32RGBAPixelFormat,
             &anim->qtime->movieBounds,
             NULL, NULL, 0,
            (unsigned char *)anim->qtime->ibuf->rect,
            anim->x * 4);
#else
      err = NewGWorldFromPtr(&anim->qtime->offscreenGWorld,
             k32ARGBPixelFormat,
             &anim->qtime->movieBounds,
             NULL, NULL, 0,
            (unsigned char *)anim->qtime->ibuf->rect,
            anim->x * 4);
#endif /* _WIN32 */

      if(err == noErr) {
            anim->qtime->have_gw = TRUE;

            SetMovieGWorld(anim->qtime->movie,
                         anim->qtime->offscreenGWorld,
                         GetGWorldDevice(anim->qtime->offscreenGWorld));
            SetMoviePlayHints(anim->qtime->movie, hintsHighQuality, hintsHighQuality);

            QT_get_frameIndexes(anim);
      }

      anim->qtime->offscreenPixMap = GetGWorldPixMap(anim->qtime->offscreenGWorld);
      LockPixels(anim->qtime->offscreenPixMap);

      //fill blender's anim struct
      anim->qtime->depth = GetFirstVideoTrackPixelDepth(anim);
      
      anim->duration = anim->qtime->framecount;
      anim->params = 0;

      anim->interlacing = 0;
      anim->orientation = 0;
      anim->framesize = anim->x * anim->y * 4;

      anim->curposition = 0;

      if(QTIME_DEBUG) printf("qt: load %s %dx%dx%d frames %d\n", anim->name, anim->qtime->movWidth,
            anim->qtime->movHeight, anim->qtime->depth, anim->qtime->framecount);

      return 0;
}

int imb_is_a_quicktime (char *name)
{
      GraphicsImportComponent       theImporter = NULL;

      FSSpec      theFSSpec;
      Str255  dst;
      char  theFullPath[255];

      Boolean                                   isMovieFile = false;
      AliasHandle                         myAlias = NULL;
      Component                           myImporter = NULL;
#ifdef __APPLE__
      FInfo                               myFinderInfo;
      FSRef                               myRef;
#endif
      OSErr                               err = noErr;

      if(QTIME_DEBUG) printf("qt: checking as image %s\n", name);

      // dont let quicktime image import handle these
      if( BLI_testextensie(name, ".swf") ||
            BLI_testextensie(name, ".txt") ||
            BLI_testextensie(name, ".mpg") ||
            BLI_testextensie(name, ".wav") ||
            BLI_testextensie(name, ".mov") ||   // not as image, doesn't work
            BLI_testextensie(name, ".avi") ||
            BLI_testextensie(name, ".mp3")) return 0;

      sprintf(theFullPath, "%s", name);
#ifdef __APPLE__
      err = FSPathMakeRef(theFullPath, &myRef, 0);
      err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &theFSSpec, NULL);
#else
      CopyCStringToPascal(theFullPath, dst);
      err = FSMakeFSSpec(0, 0L, dst, &theFSSpec);
#endif

      GetGraphicsImporterForFile(&theFSSpec, &theImporter);

      if (theImporter != NULL) {
            if(QTIME_DEBUG) printf("qt: %s valid\n", name);
            CloseComponent(theImporter);
            return 1;
      }

      return 0;
}

ImBuf  *imb_quicktime_decode(unsigned char *mem, int size, int flags)
{
      Rect                                myRect;
      OSErr                               err = noErr;
      GraphicsImportComponent       gImporter = NULL;

      ImageDescriptionHandle        desc;

      ComponentInstance             dataHandler;
      PointerDataRef dataref = (PointerDataRef)NewHandle(sizeof(PointerDataRefRecord));

      int x, y, depth;
      int have_gw = FALSE;
      ImBuf *ibuf = NULL;
      ImBuf *imbuf = NULL;
      GWorldPtr   offGWorld;
      PixMapHandle            myPixMap = NULL;

#ifdef __APPLE__
      Ptr                           myPtr;

      register int            index;
      register int            boxsize;

      register uint32_t *readPos;
      register uint32_t *changePos;

      ImBuf *wbuf = NULL;
      unsigned int *rect;
#endif

      if (mem == NULL)
            goto bail;
      
      if(QTIME_DEBUG) printf("qt: attempt to load mem as image\n");

      (**dataref).data = mem;
      (**dataref).dataLength = size;

      err = OpenADataHandler((Handle)dataref,
                                          PointerDataHandlerSubType,
                                          nil,
                                          (OSType)0,
                                          nil,
                                          kDataHCanRead,
                                          &dataHandler);
      if (err != noErr) {
            if(QTIME_DEBUG) printf("no datahandler\n");
            goto bail;
      }

      err = GetGraphicsImporterForDataRef((Handle)dataref, PointerDataHandlerSubType, &gImporter);
      if (err != noErr) {
            if(QTIME_DEBUG) printf("no graphimport\n");
            goto bail;
      }

      err = GraphicsImportGetNaturalBounds(gImporter, &myRect);
      if (err != noErr) {
            if(QTIME_DEBUG) printf("no bounds\n");
            goto bail;
      }

      err = GraphicsImportGetImageDescription (gImporter, &desc );
      if (err != noErr) {
            if(QTIME_DEBUG) printf("no imagedescription\n");
            goto bail;
      }

      x = RECT_WIDTH(myRect);
      y = RECT_HEIGHT(myRect);
      depth = (**desc).depth;

      if (flags & IB_test) {
            ibuf = IMB_allocImBuf(x, y, depth, 0, 0);
            ibuf->ftype = QUICKTIME;
            DisposeHandle((Handle)dataref);
            if (gImporter != NULL)  CloseComponent(gImporter);
            return ibuf;
      }

#ifdef __APPLE__
      ibuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);
      wbuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);

      err = NewGWorldFromPtr(&offGWorld,
                                    k32ARGBPixelFormat,
                                    &myRect, NULL, NULL, 0,
                                    (unsigned char *)wbuf->rect, x * 4);
#else

      ibuf = IMB_allocImBuf (x, y, 32, IB_rect, 0);   

      err = NewGWorldFromPtr(&offGWorld,
                                          k32RGBAPixelFormat,
                                          &myRect, NULL, NULL, 0,
                                          (unsigned char *)ibuf->rect, x * 4);
#endif
      
      if (err != noErr) {
            if(QTIME_DEBUG) printf("no newgworld\n");
            goto bail;
      } else {
            have_gw = TRUE;
      }

      GraphicsImportSetGWorld(gImporter, offGWorld, NULL);
      GraphicsImportDraw(gImporter);

#ifdef __APPLE__
      rect = ibuf->rect;

      myPixMap = GetGWorldPixMap(offGWorld);
      LockPixels(myPixMap);
      myPtr = GetPixBaseAddr(myPixMap);

      if (myPtr == NULL) {
            printf ("Error reading frame from Quicktime");
            IMB_freeImBuf (ibuf);
            return NULL;
      }

      boxsize = x * y;
      readPos = (uint32_t *) myPtr;
      changePos = (uint32_t *) rect;

      for( index = 0; index < boxsize; index++, changePos++, readPos++ )
            *( changePos ) = ( ( *readPos & 0xFFFFFF ) << 8 ) |
                  ( ( *readPos >> 24 ) & 0xFF );
#endif

bail:

      DisposeHandle((Handle)dataref);
      UnlockPixels(myPixMap);
      if(have_gw) DisposeGWorld(offGWorld);

#ifdef __APPLE__
      if (wbuf) {
            IMB_freeImBuf (wbuf);
            wbuf = NULL;
      }
#endif

      if (gImporter != NULL)  CloseComponent(gImporter);

      if (err != noErr) {
            if(QTIME_DEBUG) printf("quicktime import unsuccesfull\n");
            if (ibuf) {
                  IMB_freeImBuf (ibuf);
                  ibuf = NULL;
            }
      }

      if(ibuf) {

#ifdef _WIN32
// add non transparent alpha layer, so images without alpha show up in the sequence editor
// exception for GIF images since these can be transparent without being 32 bit
// (might also be nescessary for OSX)
            int i;
            int box = x * y;
            unsigned char *arect = (unsigned char *) ibuf->rect;

            if( depth < 32 && (**desc).cType != kGIFCodecType) {
                  for(i = 0; i < box; i++, arect+=4)
                         arect[3] = 0xFF;
            }
#endif

            IMB_flipy(ibuf);
            ibuf->ftype = QUICKTIME;
      }
      return ibuf;
}

#endif /* _WIN32 || __APPLE__ */

#endif /* WITH_QUICKTIME */


#if 0

struct ImageDescription {
     long         idSize;
     CodecType    cType;
     long         resvd1;
     short        resvd2;
     short        dataRefIndex;
     short        version;
     short        revisionLevel;
     long         vendor;
     CodecQ       temporalQuality;
     CodecQ       spatialQuality;
     short        width;
     short        height;
     Fixed        hRes;
     Fixed        vRes;
     long         dataSize;
     short        frameCount;
     Str31        name;
     short        depth;
     short        clutID;
};

#endif // 0

Generated by  Doxygen 1.6.0   Back to index