qtkit : improve thread safety, enforce build on OSX 10.5+
[blender.git] / source / blender / quicktime / apple / qtkit_export.m
1 /**
2  * $Id$
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
64 #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
65 #error OSX 10.5 minimum is needed for QTKit
66 #endif
67
68 #endif /* __APPLE__ */
69
70 typedef struct QuicktimeExport {
71         QTMovie *movie;
72         
73         NSString *filename;
74
75         QTTime frameDuration;
76         NSDictionary *frameAttributes;
77 } QuicktimeExport;
78
79 static struct QuicktimeExport *qtexport;
80
81
82 static NSString *stringWithCodecType(int codecType) {
83         switch (codecType) {
84                 case QT_CODECTYPE_RAW:
85                         return @"raw ";
86                 case QT_CODECTYPE_MJPEGA:
87                         return @"mjpa";
88                 case QT_CODECTYPE_MJPEGB:
89                         return @"mjpb";
90                 case QT_CODECTYPE_DVCPAL:
91                         return @"dvcp";
92                 case QT_CODECTYPE_DVCNTSC:
93                         return @"dvc ";
94                 case QT_CODECTYPE_MPEG4:
95                         return @"mp4v";
96                 case QT_CODECTYPE_H263:
97                         return @"h263";
98                 case QT_CODECTYPE_H264:
99                         return @"avc1";
100                 case QT_CODECTYPE_DVCPROHD720p:
101                         return @"dvhp";
102                 case QT_CODECTYPE_DVCPROHD1080i50:
103                         return @"dvh5";
104                 case QT_CODECTYPE_DVCPROHD1080i60:
105                         return @"dvh6";
106                         
107                 case QT_CODECTYPE_JPEG:
108                 default:
109                         return @"jpeg"; 
110         }
111 }
112
113 void makeqtstring (RenderData *rd, char *string) {
114         char txt[64];
115
116         strcpy(string, rd->pic);
117         BLI_convertstringcode(string, G.sce);
118
119         BLI_make_existing_file(string);
120
121         if (BLI_strcasecmp(string + strlen(string) - 4, ".mov")) {
122                 sprintf(txt, "%04d_%04d.mov", (rd->sfra) , (rd->efra) );
123                 strcat(string, txt);
124         }
125 }
126
127
128 void start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty)
129 {
130         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
131         NSError *error;
132         char name[2048];
133
134         
135         if (G.afbreek != 1) {
136                 
137                 if(qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
138                 
139                 [QTMovie enterQTKitOnThread];           
140                 
141                 /* Check first if the QuickTime 7.2.1 initToWritableFile: method is available */
142                 if ([[[[QTMovie alloc] init] autorelease] respondsToSelector:@selector(initToWritableFile:error:)] != YES) {
143                         G.afbreek = 1;
144                         fprintf(stderr, "\nUnable to create quicktime movie, need Quicktime rev 7.2.1 or later");
145                 }
146                 else {
147                         makeqtstring(rd, name);
148                         qtexport->filename = [NSString stringWithCString:name
149                                                                           encoding:[NSString defaultCStringEncoding]];
150                         qtexport->movie = [[QTMovie alloc] initToWritableFile:qtexport->filename error:&error];
151                                 
152                         if(qtexport->movie == nil) {
153                                 G.afbreek = 1;
154                                 NSLog(@"Unable to create quicktime movie : %@",[error localizedDescription]);
155                                 [QTMovie exitQTKitOnThread];
156                         } else {
157                                 [qtexport->movie retain];
158                                 [qtexport->filename retain];
159                                 [qtexport->movie setAttribute:[NSNumber numberWithBool:YES] forKey:QTMovieEditableAttribute];
160                                 [qtexport->movie setAttribute:@"Made with Blender" forKey:QTMovieCopyrightAttribute];
161                                 
162                                 qtexport->frameDuration = QTMakeTime(rd->frs_sec_base*1000, rd->frs_sec*1000);
163                                 
164                                 /* specifying the codec attributes : try to retrieve them from render data first*/
165                                 if (rd->qtcodecsettings.codecType) {
166                                         qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
167                                                                                                  stringWithCodecType(rd->qtcodecsettings.codecType),
168                                                                                                  QTAddImageCodecType,
169                                                                                                  [NSNumber numberWithLong:((rd->qtcodecsettings.codecSpatialQuality)*codecLosslessQuality)/100],
170                                                                                                  QTAddImageCodecQuality,
171                                                                                                  nil];
172                                 }
173                                 else {
174                                         qtexport->frameAttributes = [NSDictionary dictionaryWithObjectsAndKeys:@"jpeg",
175                                                                                                  QTAddImageCodecType,
176                                                                                                  [NSNumber numberWithLong:codecHighQuality],
177                                                                                                  QTAddImageCodecQuality,
178                                                                                                  nil];
179                                 }
180                                 [qtexport->frameAttributes retain];
181                         }
182                 }
183         }
184         
185         [pool drain];
186 }
187
188
189 void append_qt(struct RenderData *rd, int frame, int *pixels, int rectx, int recty)
190 {
191         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
192         NSBitmapImageRep *blBitmapFormatImage;
193         NSImage *frameImage;
194         unsigned char *from_Ptr,*to_Ptr;
195         int y,from_i,to_i;
196         
197         
198         /* Create bitmap image rep in blender format (32bit RGBA) */
199         blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
200                                                                                                                                   pixelsWide:rectx 
201                                                                                                                                   pixelsHigh:recty
202                                                                                                                            bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO
203                                                                                                                           colorSpaceName:NSCalibratedRGBColorSpace 
204                                                                                                                                 bitmapFormat:NSAlphaNonpremultipliedBitmapFormat
205                                                                                                                                  bytesPerRow:rectx*4
206                                                                                                                                 bitsPerPixel:32];
207         if (!blBitmapFormatImage) {
208                 [pool drain];
209                 return;
210         }
211         
212         from_Ptr = (unsigned char*)pixels;
213         to_Ptr = (unsigned char*)[blBitmapFormatImage bitmapData];
214         for (y = 0; y < recty; y++) {
215                 to_i = (recty-y-1)*rectx;
216                 from_i = y*rectx;
217                 memcpy(to_Ptr+4*to_i, from_Ptr+4*from_i, 4*rectx);
218         }
219         
220         frameImage = [[NSImage alloc] initWithSize:NSMakeSize(rectx, recty)];
221         [frameImage addRepresentation:blBitmapFormatImage];
222         
223         /* Add the image to the movie clip */
224         [qtexport->movie addImage:frameImage
225                                   forDuration:qtexport->frameDuration
226                            withAttributes:qtexport->frameAttributes];
227
228         [blBitmapFormatImage release];
229         [frameImage release];
230         [pool drain];   
231 }
232
233
234 void end_qt(void)
235 {
236         if (qtexport->movie) {
237                 /* Flush update of the movie file */
238                 [qtexport->movie updateMovieFile];
239                 
240                 [qtexport->movie invalidate];
241                 
242                 /* Clean up movie structure */
243                 [qtexport->filename release];
244                 [qtexport->frameAttributes release];
245                 [qtexport->movie release];
246                 }
247         
248         [QTMovie exitQTKitOnThread];
249
250         if(qtexport) {
251                 MEM_freeN(qtexport);
252                 qtexport = NULL;
253         }
254 }
255
256
257 void free_qtcomponentdata(void) {
258 }
259
260 void quicktime_verify_image_type(RenderData *rd)
261 {
262         if (rd->imtype == R_QUICKTIME) {
263                 if ((rd->qtcodecsettings.codecType<= 0) ||
264                         (rd->qtcodecsettings.codecSpatialQuality <0) ||
265                         (rd->qtcodecsettings.codecSpatialQuality > 100)) {
266                         
267                         rd->qtcodecsettings.codecType = QT_CODECTYPE_JPEG;
268                         rd->qtcodecsettings.codecSpatialQuality = (codecHighQuality*100)/codecLosslessQuality;
269                 }
270         }
271 }
272
273 #endif /* _WIN32 || __APPLE__ */
274 #endif /* WITH_QUICKTIME */
275