Quicktime for Cocoa : export part
[blender.git] / source / blender / quicktime / apple / qtkit_export.m
1 /**
2  * $Id: qtkit_export.m 24424 2009-11-09 17:06:48Z damien78 $
3  *
4  * qtkit_export.m
5  *
6  * Code to create QuickTime Movies with Blender
7  *
8  * ***** BEGIN GPL 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.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  *
24  * The Original Code is written by Rob Haarsma (phase)
25  *
26  * Contributor(s): Stefan Gartner (sgefant)
27  *                                 Damien Plisson 11/2009
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #ifdef WITH_QUICKTIME
33 #if defined(_WIN32) || defined(__APPLE__)
34
35 #include <stdio.h>
36 #include <string.h>
37
38 #include "DNA_scene_types.h"
39
40 #include "BKE_global.h"
41 #include "BKE_scene.h"
42
43 #include "BLI_blenlib.h"
44
45 #include "BLO_sys_types.h"
46
47 #include "IMB_imbuf.h"
48 #include "IMB_imbuf_types.h"
49
50 #include "MEM_guardedalloc.h"
51
52 #include "quicktime_import.h"
53 #include "quicktime_export.h"
54
55
56 #ifdef __APPLE__
57 /* evil */
58 #ifndef __AIFF__
59 #define __AIFF__
60 #endif
61 #import <Cocoa/Cocoa.h>
62 #import <QTKit/QTKit.h>
63 #endif /* __APPLE__ */
64
65 typedef struct QuicktimeExport {
66         QTMovie *movie;
67         
68         NSString *filename;
69
70         QTTime frameDuration;
71         NSDictionary *frameAttributes;
72 } QuicktimeExport;
73
74 static struct QuicktimeExport *qtexport;
75
76
77 void makeqtstring (RenderData *rd, char *string) {
78         char txt[64];
79
80         strcpy(string, rd->pic);
81         BLI_convertstringcode(string, G.sce);
82
83         BLI_make_existing_file(string);
84
85         if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
86                 sprintf(txt, "%04d_%04d.mov", (rd->sfra) , (rd->efra) );
87                 strcat(string, txt);
88         }
89 }
90
91
92 void start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty)
93 {
94         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
95         NSError *error;
96         char name[2048];
97
98         
99         if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
100
101         if (G.afbreek != 1) {
102                 /* Check first if the QuickTime 7.2.1 initToWritableFile: method is available */
103                 if ([[[[QTMovie alloc] init] autorelease] respondsToSelector:@selector(initToWritableFile:error:)] != YES) {
104                         G.afbreek = 1;
105                         fprintf(stderr, "\nUnable to create quicktime movie, need Quicktime rev 7.2.1 or later");
106                 }
107                 else {
108                         makeqtstring(rd, name);
109                         qtexport->filename = [NSString stringWithCString:name
110                                                                           encoding:[NSString defaultCStringEncoding]];
111                         qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
112                                 
113                         if(qtexport->movie == nil) {
114                                 G.afbreek = 1;
115                                 NSLog(@"Unable to create quicktime movie : %@",[error localizedDescription]);
116                         } else {
117                                 [qtexport->movie retain];
118                                 [qtexport->filename retain];
119                                 [qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
120                                 [qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute];
121                                 
122                                 qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000);
123                                 
124                                 /* specifying the codec attributes
125                                 TODO: get these values from RenderData/scene*/
126                                 qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg",
127                                                                                          QTAddImageCodecType,
128                                                                                          [NSNumber numberWithLong:codecHighQuality],
129                                                                                          QTAddImageCodecQuality,
130                                                                                          nil];
131                                 [qtexport->frameAttributes retain];
132                         }
133                 }
134         }
135         
136         [pool drain];
137 }
138
139
140 void append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty)
141 {
142         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
143         NSBitmapImageRep *blBitmapFormatImage;
144         NSImage *frameImage;
145         unsigned char *from_Ptr,*to_Ptr;
146         int y,from_i,to_i;
147         
148         
149         /* Create bitmap image rep in blender format (32bit RGBA) */
150         blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
151                                                                                                                                   pixelsWide:rectx 
152                                                                                                                                   pixelsHigh:recty
153                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
154                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace 
155                                                                                                                                 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
156                                                                                                                                  bytesPerRow:rectx*4
157                                                                                                                                 bitsPerPixel:32];
158         if (!blBitmapFormatImage) {
159                 [pool drain];
160                 return;
161         }
162         
163         from_Ptr = (unsigned char*)pixels;
164         to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData];
165         for (y = 0; y < recty; y++) {
166                 to_i = (recty-y-1)*rectx;
167                 from_i = y*rectx;
168                 memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx);
169         }
170         
171         frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)];
172         [frameImage addRepresentation:blBitmapFormatImage];
173         
174         /* Add the image to the movie clip */
175         [qtexport->movie addImage:frameImage
176                                   forDuration:qtexport->frameDuration
177                            withAttributes:qtexport->frameAttributes];
178
179         [blBitmapFormatImage release];
180         [frameImage release];
181         [pool drain];   
182 }
183
184
185 void end_qt(void)
186 {
187         if (qtexport->movie) {
188                 /* Flush update of the movie file */
189                 [qtexport->movie updateMovieFile];
190                                 
191                 /* Clean up movie structure */
192                 [qtexport->filename release];
193                 [qtexport->frameAttributes release];
194                 [qtexport->movie release];
195                 }
196
197         if(qtexport) {
198                 MEM_freeN(qtexport);
199                 qtexport = NULL;
200         }
201 }
202
203
204 void free_qtcomponentdata(void) {
205 }
206
207
208 int get_qtcodec_settings(RenderData *rd) 
209 {
210 /*
211         // get previous selected codecsetting, if any 
212         if(rd->qtcodecdata && rd->qtcodecdata->cdParms) {
213                 QT_GetCodecSettingsFromScene(rd);
214                 check_renderbutton_framerate(rd);
215         } else {
216                 // configure the standard image compression dialog box
217                 // set some default settings
218                 qtdata->gSpatialSettings.codec = anyCodec;         
219                 qtdata->gSpatialSettings.spatialQuality = codecMaxQuality;
220                 qtdata->gTemporalSettings.temporalQuality = codecMaxQuality;
221                 qtdata->gTemporalSettings.keyFrameRate = 25;   
222                 qtdata->aDataRateSetting.dataRate = 90 * 1024;          
223
224                 err = SCSetInfo(qtdata->theComponent, scTemporalSettingsType,   &qtdata->gTemporalSettings);
225                 CheckError(err, "SCSetInfo1 error");
226                 err = SCSetInfo(qtdata->theComponent, scSpatialSettingsType,    &qtdata->gSpatialSettings);
227                 CheckError(err, "SCSetInfo2 error");
228                 err = SCSetInfo(qtdata->theComponent, scDataRateSettingsType,   &qtdata->aDataRateSetting);
229                 CheckError(err, "SCSetInfo3 error");
230         }
231
232         check_renderbutton_framerate(rd);
233
234         // put up the dialog box - it needs to be called from the main thread
235         err = SCRequestSequenceSettings(qtdata->theComponent);
236  
237         if (err == scUserCancelled) {
238                 G.afbreek = 1;
239                 return 0;
240         }
241
242         // get user selected data
243         SCGetInfo(qtdata->theComponent, scTemporalSettingsType, &qtdata->gTemporalSettings);
244         SCGetInfo(qtdata->theComponent, scSpatialSettingsType,  &qtdata->gSpatialSettings);
245         SCGetInfo(qtdata->theComponent, scDataRateSettingsType, &qtdata->aDataRateSetting);
246
247         QT_SaveCodecSettingsToScene(rd);
248
249         // framerate jugglin'
250         switch (qtexport->frameRate) {
251                 case 1571553: // 23.98 fps
252                         qtexport->frameDuration = QTMakeTime(1001, 24000);
253                         rd->frs_sec = 24;
254                         rd->frs_sec_base = 1.001;
255                         break;
256                 case 1964113: // 29.97 fps
257                         qtexport->frameDuration = QTMakeTime(1001, 30000);
258                         rd->frs_sec = 30;
259                         rd->frs_sec_base = 1.001;
260                         break;
261                 case 3928227: // 59.94 fps
262                         qtexport->frameDuration = QTMakeTime(1001, 60000);
263                         rd->frs_sec = 60;
264                         rd->frs_sec_base = 1.001;
265                         break;
266                 default:
267                 {
268                         double fps = qtexport->frameRate;
269                         qtexport->frameDuration = QTMakeTime(60000/(qtexport->frameRate / 65536), 60000);
270                         
271                         if ((qtexport->frameRate & 0xffff) == 0) {
272                                 rd->frs_sec = fps / 65536;
273                                 rd->frs_sec_base = 1;
274                         } else {
275                                 // we do our very best... 
276                                 rd->frs_sec = (fps * 10000 / 65536);
277                                 rd->frs_sec_base = 10000;
278                         }
279                 }
280                         break;
281         }
282 */
283         return 1;
284 }
285
286 #endif /* _WIN32 || __APPLE__ */
287 #endif /* WITH_QUICKTIME */
288