6 * Code to create QuickTime Movies with Blender
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 *****
36 - fix saving of compressionsettings
38 This should be fixed because:
40 - currently only one quicktime codec is used for all scenes
41 - this single codecsetting gets only initialised from the codec dialog
43 - the codecsettings have to get stored in the blendfile for background
44 rendering. blender -b crashes when it wants to popup the qt codec dialog
45 to retrieve a valid codec.
46 (quicktime isnt initialised yet when blender -b requests a rendering codec)
48 Some references to store the codec settings are placed at the end of this file.
52 * structurize file & compression data
54 * fix 23.98, 29.97, 59.94 framerates
55 * fix framerate button
56 * fix mac compatibility
58 * fix fallthrough to codecselector // buttons.c
59 * fix playback qt movie // playanim.c
60 * fix setting fps thru blenderbutton as well as codec dialog
66 #if defined(_WIN32) || defined(__APPLE__)
68 /************************************************************
72 *************************************************************/
74 #include "BKE_global.h"
75 #include "BKE_scene.h"
76 #include "BLI_blenlib.h"
77 #include "BLO_sys_types.h"
78 #include "IMB_imbuf.h"
79 #include "IMB_imbuf_types.h"
80 #include "MEM_guardedalloc.h"
83 #include "quicktime_export.h"
88 #include <TextUtils.h>
90 #include <QuicktimeComponents.h>
91 #include <MoviesFormat.h>
96 #include <QuickTime/Movies.h>
97 #include <QuickTime/QuicktimeComponents.h>
98 #include <fcntl.h> /* open() */
99 #include <unistd.h> /* close() */
100 #include <sys/stat.h> /* file permissions */
101 #endif /* __APPLE__ */
104 /************************************************************
106 * FUNCTION PROTOTYPES *
108 *************************************************************/
110 static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame);
111 static void QT_DoAddVideoSamplesToMedia (int frame);
112 static void QT_EndAddVideoSamplesToMedia (void);
113 static void QT_CreateMyVideoTrack (void);
114 static void QT_EndCreateMyVideoTrack (void);
116 static void check_renderbutton_framerate(void);
118 /************************************************************
122 *************************************************************/
124 typedef struct _QuicktimeExport {
137 PixMapHandle thePixMap;
139 ImageDescription **anImageDescription;
140 ImageSequence anImageSequence;
142 ImBuf *ibuf; //for Qtime's Gworld
143 ImBuf *ibuf2; //copy of renderdata, to be Y-flipped
147 typedef struct _QuicktimeCodecDataExt {
149 ComponentInstance theComponent;
150 SCTemporalSettings gTemporalSettings;
151 SCSpatialSettings gSpatialSettings;
152 SCDataRateSettings aDataRateSetting;
154 long kVideoTimeScale;
156 } QuicktimeCodecDataExt;
158 struct _QuicktimeExport *qte;
159 struct _QuicktimeCodecDataExt *qcdx;
161 /************************************************************
165 *************************************************************/
167 #define kMyCreatorType FOUR_CHAR_CODE('TVOD')
168 #define kPixelDepth 32 /* use 32-bit depth */
169 #define kTrackStart 0
170 #define kMediaStart 0
177 /************************************************************
179 * CheckError(OSErr err, char *msg) *
181 * prints errors in console, doesnt interrupt Blender *
183 *************************************************************/
185 void CheckError(OSErr err, char *msg)
187 if(err != noErr) printf("%s: %d\n", msg, err);
191 /************************************************************
193 * QT_CreateMyVideoTrack() *
194 * QT_EndCreateMyVideoTrack() *
196 * Creates/finishes a video track for the QuickTime movie *
198 *************************************************************/
200 static void QT_CreateMyVideoTrack(void)
207 trackFrame.bottom = R.recty;
208 trackFrame.right = R.rectx;
210 qte->theTrack = NewMovieTrack (qte->theMovie,
211 FixRatio(trackFrame.right,1),
212 FixRatio(trackFrame.bottom,1),
214 CheckError( GetMoviesError(), "NewMovieTrack error" );
216 qte->theMedia = NewTrackMedia (qte->theTrack,
218 qcdx->kVideoTimeScale,
221 CheckError( GetMoviesError(), "NewTrackMedia error" );
223 err = BeginMediaEdits (qte->theMedia);
224 CheckError( err, "BeginMediaEdits error" );
226 QT_StartAddVideoSamplesToMedia (&trackFrame);
230 static void QT_EndCreateMyVideoTrack(void)
234 QT_EndAddVideoSamplesToMedia ();
236 err = EndMediaEdits (qte->theMedia);
237 CheckError( err, "EndMediaEdits error" );
239 err = InsertMediaIntoTrack (qte->theTrack,
240 kTrackStart,/* track start time */
241 kMediaStart,/* media start time */
242 GetMediaDuration (qte->theMedia),
244 CheckError( err, "InsertMediaIntoTrack error" );
248 /************************************************************
250 * QT_StartAddVideoSamplesToMedia() *
251 * QT_DoAddVideoSamplesToMedia() *
252 * QT_EndAddVideoSamplesToMedia() *
254 * Creates video samples for the media in a track *
256 *************************************************************/
258 static void QT_StartAddVideoSamplesToMedia (const Rect *trackFrame)
262 qte->ibuf = IMB_allocImBuf (R.rectx, R.recty, 32, IB_rect, 0);
263 qte->ibuf2 = IMB_allocImBuf (R.rectx, R.recty, 32, IB_rect, 0);
265 err = NewGWorldFromPtr( &qte->theGWorld,
269 (unsigned char *)qte->ibuf->rect,
271 CheckError (err, "NewGWorldFromPtr error");
273 qte->thePixMap = GetGWorldPixMap(qte->theGWorld);
274 LockPixels(qte->thePixMap);
276 SCDefaultPixMapSettings (qcdx->theComponent, qte->thePixMap, true);
278 SCSetInfo(qcdx->theComponent, scTemporalSettingsType, &qcdx->gTemporalSettings);
279 SCSetInfo(qcdx->theComponent, scSpatialSettingsType, &qcdx->gSpatialSettings);
280 SCSetInfo(qcdx->theComponent, scDataRateSettingsType, &qcdx->aDataRateSetting);
282 err = SCCompressSequenceBegin(qcdx->theComponent, qte->thePixMap, NULL, &qte->anImageDescription);
283 CheckError (err, "SCCompressSequenceBegin error" );
287 static void QT_DoAddVideoSamplesToMedia (int frame)
293 register int boxsize;
294 register uint32_t *readPos;
295 register uint32_t *changePos;
300 Handle compressedData;
302 // copy and flip the renderdata
304 memcpy(qte->ibuf2->rect, R.rectot, 4*R.rectx*R.recty);
305 IMB_flipy(qte->ibuf2);
308 //get pointers to parse bitmapdata
309 myPtr = GetPixBaseAddr(qte->thePixMap);
310 imageRect = (**qte->thePixMap).bounds;
312 boxsize = R.rectx * R.recty;
313 readPos = (uint32_t *) qte->ibuf2->rect;
314 changePos = (uint32_t *) myPtr;
317 // Swap alpha byte to the end, so ARGB become RGBA; note this is big endian-centric.
318 for( index = 0; index < boxsize; index++, changePos++, readPos++ )
319 *( changePos ) = ( ( *readPos & 0xFFFFFFFF ) >> 8 ) |
320 ( ( *readPos << 24 ) & 0xFF );
324 // poked around a little... this seems to work for windows, dunno if it's legal
325 for( index = 0; index < boxsize; index++, changePos++, readPos++ )
326 *( changePos ) = ( ( *readPos & 0xFFFFFFFF ) << 8 ) |
327 ( ( *readPos >> 24 ) & 0xFF ); // & ( ( *readPos << 8 ) & 0xFF );
330 err = SCCompressSequenceFrame(qcdx->theComponent,
336 CheckError(err, "SCCompressSequenceFrame error");
338 err = AddMediaSample(qte->theMedia,
343 (SampleDescriptionHandle)qte->anImageDescription,
347 CheckError(err, "AddMediaSample error");
349 printf ("added frame %3d (frame %3d in movie): ", frame, frame-sframe);
353 static void QT_EndAddVideoSamplesToMedia (void)
355 SCCompressSequenceEnd(qcdx->theComponent);
357 UnlockPixels(qte->thePixMap);
358 if (qte->theGWorld) DisposeGWorld (qte->theGWorld);
359 if (qte->ibuf) IMB_freeImBuf(qte->ibuf);
361 IMB_freeImBuf(qte->ibuf2);
366 /************************************************************
368 * makeqtstring (char *string) *
370 * Function to generate output filename *
372 *************************************************************/
374 void makeqtstring (char *string) {
377 if (string==0) return;
379 strcpy(string, G.scene->r.pic);
380 BLI_convertstringcode(string, G.sce, G.scene->r.cfra);
382 RE_make_existing_file(string);
384 if (strcasecmp(string + strlen(string) - 4, ".mov")) {
385 sprintf(txt, "%04d_%04d.mov", (G.scene->r.sfra) , (G.scene->r.efra) );
391 /************************************************************
394 * append_qt(int frame) *
395 * end_qt(int frame) *
397 * Quicktime Export functions for Blender's initrender.c *
399 ************************************************************/
401 void start_qt(void) {
405 char theFullPath[255];
412 if(qte == NULL) qte = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
413 if(qcdx == NULL || have_qtcodec == FALSE) get_qtcodec_settings();
415 if (G.afbreek != 1) {
416 sframe = (G.scene->r.sfra);
419 sprintf(theFullPath, "%s", name);
422 /* hack: create an empty file to make FSPathMakeRef() happy */
423 myFile = open(theFullPath, O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRUSR|S_IWUSR);
425 printf("error while creating file!\n");
429 err = FSPathMakeRef(theFullPath, &myRef, 0);
430 CheckError(err, "FsPathMakeRef error");
431 err = FSGetCatalogInfo(&myRef, kFSCatInfoNone, NULL, NULL, &qte->theSpec, NULL);
432 CheckError(err, "FsGetCatalogInfoRef error");
434 CopyCStringToPascal(theFullPath, qte->qtfilename);
435 err = FSMakeFSSpec(0, 0L, qte->qtfilename, &qte->theSpec);
438 err = CreateMovieFile (&qte->theSpec,
441 createMovieFileDeleteCurFile | createMovieFileDontCreateResFile,
444 CheckError(err, "CreateMovieFile error");
446 printf("Created QuickTime movie: %s\n", name);
448 check_renderbutton_framerate();
450 QT_CreateMyVideoTrack();
455 void append_qt(int frame) {
456 QT_DoAddVideoSamplesToMedia(frame);
463 QT_EndCreateMyVideoTrack ();
465 qte->resId = movieInDataForkResID;
466 err = AddMovieResource (qte->theMovie, qte->resRefNum, &qte->resId, qte->qtfilename);
467 CheckError(err, "AddMovieResource error");
469 if (qte->resRefNum) CloseMovieFile (qte->resRefNum);
471 DisposeMovie (qte->theMovie);
481 /************************************************************
483 * free_qtcodecdataExt(void) *
485 * Function to release codec memory, since it remains *
486 * resident after allocation. *
488 *************************************************************/
490 void free_qtcodecdataExt(void) {
498 /************************************************************
500 * check_renderbutton_framerate ( void ) *
502 * To keep float framerates consistent between the codec *
503 * dialog and frs/sec button. *
505 *************************************************************/
507 static void check_renderbutton_framerate(void) {
510 err = SCGetInfo(qcdx->theComponent, scTemporalSettingsType, &qcdx->gTemporalSettings);
511 CheckError(err, "SCGetInfo error");
513 if( (G.scene->r.frs_sec == 24 || G.scene->r.frs_sec == 30 || G.scene->r.frs_sec == 60) &&
514 (qcdx->gTemporalSettings.frameRate == 1571553 ||
515 qcdx->gTemporalSettings.frameRate == 1964113 ||
516 qcdx->gTemporalSettings.frameRate == 3928227)) {;} else
517 qcdx->gTemporalSettings.frameRate = G.scene->r.frs_sec << 16;
519 err = SCSetInfo(qcdx->theComponent, scTemporalSettingsType, &qcdx->gTemporalSettings);
520 CheckError( err, "SCSetInfo error" );
522 if(qcdx->gTemporalSettings.frameRate == 1571553) { // 23.98 fps
523 qcdx->kVideoTimeScale = 2398;
524 qcdx->duration = 100;
525 } else if (qcdx->gTemporalSettings.frameRate == 1964113) { // 29.97 fps
526 qcdx->kVideoTimeScale = 2997;
527 qcdx->duration = 100;
528 } else if (qcdx->gTemporalSettings.frameRate == 3928227) { // 59.94 fps
529 qcdx->kVideoTimeScale = 5994;
530 qcdx->duration = 100;
532 qcdx->kVideoTimeScale = (qcdx->gTemporalSettings.frameRate >> 16) * 100;
533 qcdx->duration = 100;
536 /********************************************************************
538 * get_qtcodec_settings() *
540 * Displays Codec Dialog and retrieves Quicktime Codec settings. *
542 ********************************************************************/
544 int get_qtcodec_settings(void)
548 // ComponentDescription cd;
552 // cd.componentType = StandardCompressionType;
553 // cd.componentSubType = StandardCompressionSubType;
554 // cd.componentManufacturer = 0;
555 // cd.componentFlags = 0;
556 // cd.componentFlagsMask = 0;
559 qcdx = MEM_callocN(sizeof(QuicktimeCodecDataExt), "QuicktimeCodecDataExt");
560 have_qtcodec = FALSE;
563 // configure the standard image compression dialog box
565 if (qcdx->theComponent == NULL) {
566 qcdx->theComponent = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
567 // c = FindNextComponent(c, &cd);
568 // qcdx->theComponent = OpenComponent(c);
570 // qcdx->gSpatialSettings.codecType = nil;
571 qcdx->gSpatialSettings.codec = anyCodec;
572 // qcdx->gSpatialSettings.depth;
573 qcdx->gSpatialSettings.spatialQuality = codecMaxQuality;
575 qcdx->gTemporalSettings.temporalQuality = codecMaxQuality;
576 // qcdx->gTemporalSettings.frameRate;
577 qcdx->gTemporalSettings.keyFrameRate = 25;
579 qcdx->aDataRateSetting.dataRate = 90 * 1024;
580 // qcdx->aDataRateSetting.frameDuration;
581 // qcdx->aDataRateSetting.minSpatialQuality;
582 // qcdx->aDataRateSetting.minTemporalQuality;
585 err = SCSetInfo(qcdx->theComponent, scTemporalSettingsType, &qcdx->gTemporalSettings);
586 CheckError(err, "SCSetInfo1 error");
587 err = SCSetInfo(qcdx->theComponent, scSpatialSettingsType, &qcdx->gSpatialSettings);
588 CheckError(err, "SCSetInfo2 error");
589 err = SCSetInfo(qcdx->theComponent, scDataRateSettingsType, &qcdx->aDataRateSetting);
590 CheckError(err, "SCSetInfo3 error");
593 check_renderbutton_framerate();
595 // put up the dialog box
597 err = SCRequestSequenceSettings(qcdx->theComponent);
599 if (err == scUserCancelled) {
606 // get user selected data
608 SCGetInfo(qcdx->theComponent, scTemporalSettingsType, &qcdx->gTemporalSettings);
609 SCGetInfo(qcdx->theComponent, scSpatialSettingsType, &qcdx->gSpatialSettings);
610 SCGetInfo(qcdx->theComponent, scDataRateSettingsType, &qcdx->aDataRateSetting);
612 GetCodecInfo (&ci, qcdx->gSpatialSettings.codecType, 0);
613 CopyPascalStringToC(ci.typeName, str);
614 sprintf(qtcdname,"Codec: %s", str);
616 // framerate jugglin'
618 if(qcdx->gTemporalSettings.frameRate == 1571553) { // 23.98 fps
619 qcdx->kVideoTimeScale = 2398;
620 qcdx->duration = 100;
622 G.scene->r.frs_sec = 24;
623 } else if (qcdx->gTemporalSettings.frameRate == 1964113) { // 29.97 fps
624 qcdx->kVideoTimeScale = 2997;
625 qcdx->duration = 100;
627 G.scene->r.frs_sec = 30;
628 } else if (qcdx->gTemporalSettings.frameRate == 3928227) { // 59.94 fps
629 qcdx->kVideoTimeScale = 5994;
630 qcdx->duration = 100;
632 G.scene->r.frs_sec = 60;
634 qcdx->kVideoTimeScale = 600;
635 qcdx->duration = qcdx->kVideoTimeScale / (qcdx->gTemporalSettings.frameRate / 65536);
637 G.scene->r.frs_sec = (qcdx->gTemporalSettings.frameRate / 65536);
643 #endif /* _WIN32 || __APPLE__ */
645 #endif /* WITH_QUICKTIME */
651 /************************************************************
653 * References for future codec handling *
655 *************************************************************/
657 these are from mplayer sourcecode:
659 struct ComponentRecord {
662 typedef struct ComponentRecord ComponentRecord;
663 typedef ComponentRecord * Component;
666 typedef long ComponentResult;
670 these are from quicktime:
673 typedef Component CodecComponent;
674 typedef OSType CodecType;
675 typedef unsigned short CodecFlags;
676 typedef unsigned long CodecQ;
679 CodecType codecType; /* compressor type */
680 CodecComponent codec; /* compressor */
681 short depth; /* pixel depth */
682 CodecQ spatialQuality; /* desired quality */
685 /* temporal options structure with the temporal settings request */
687 CodecQ temporalQuality; /* desired quality */
688 Fixed frameRate; /* frame rate */
689 long keyFrameRate; /* key frame rate */
690 } SCTemporalSettings;
692 /* data rate options with the data rate settings request */
694 long dataRate; /* desired data rate */
695 long frameDuration; /* frame duration */
696 CodecQ minSpatialQuality; /* minimum value */
697 CodecQ minTemporalQuality; /* minimum value */
698 } SCDataRateSettings;
701 would look like this ???
704 int codecType; /* compressor type */
706 long *codec; /* compressor */
707 short depth; /* pixel depth */
708 unsigned long spatialQuality; /* desired quality */
711 /* temporal options structure with the temporal settings request */
713 unsigned long temporalQuality; /* desired quality */
714 long frameRate; /* frame rate */
715 long keyFrameRate; /* key frame rate */
716 } SCTemporalSettings;
718 /* data rate options with the data rate settings request */
720 long dataRate; /* desired data rate */
721 long frameDuration; /* frame duration */
722 unsigned long minSpatialQuality; /* minimum value */
723 unsigned long minTemporalQuality; /* minimum value */
724 } SCDataRateSettings;
727 stuff to use quicktime Atoms (doesnt work) heh
731 QTAtomContainer myContainer = NULL;
733 QTAtomContainer SettingsAtom;
736 SCSetSettingsFromAtomContainer(qcdx->theComponent, SettingsAtom);
739 SCGetSettingsAsAtomContainer(qcdx->theComponent, SettingsAtom);
741 QTCopyAtomDataToPtr(container, atom, 0, size, &targetPtr, &actualsize);
742 QTGetAtomDataPtr(container, atom, &size, &ptr);
744 kParentAtomIsContainer