OSDN Git Service

0.2.1
[eliscolors/main.git] / ElisWriterLegacy.m
1 //
2 //  ElisWriterLegacy.m
3 //  Elis Colors
4 //
5 //  Created by 柳 on 09/09/23.
6 //  Copyright 2009 __MyCompanyName__. All rights reserved.
7 //
8
9 #import "ElisWriterLegacy.h"
10
11 // Handle requests for information about the output video data
12 static OSErr QTMoovProcs_VideoTrackPropertyProc (void *theRefcon, long theTrackID, OSType thePropertyType, void *thePropertyValue)
13 {
14 #pragma unused(theRefcon, theTrackID)
15     
16         OSErr   myErr = noErr;
17         
18         switch (thePropertyType) {
19                 case movieExportUseConfiguredSettings:
20                         *(Boolean *)thePropertyValue = true;
21                         break;
22                         
23                 default:
24                         myErr = paramErr;       // non-zero value means: use default value provided by export component
25                         break;
26         }
27         
28         return(myErr);
29 }
30
31
32 //--------------------------------------------------------------------------------------------------
33
34 // Provide the output audio data.
35 // ハンドラ的なもの?
36 static OSErr QTMoovProcs_VideoTrackDataProc(void *theRefcon, MovieExportGetDataParams *theParams)
37 {       
38     return [(ElisWriterLegacy*)theRefcon exportFrame:theParams];
39 }
40
41
42 @implementation ElisWriterLegacy
43
44 - (id)init
45 {
46     gamma_table = malloc(sizeof(unsigned char) * 256);
47     [NSBundle loadNibNamed:@"WriterProgress" owner:self];
48     
49     return self;
50 }
51
52 - (void)write:(NSSavePanel*)sp
53 {
54     [self reallyExportMovie:sp toPod:NO];
55 }
56
57 - (void)setMainWindow:(NSWindow*)w
58 {
59     _mainWindow = w;
60 }
61
62 - (void)setMainController:(id)c
63 {
64     _mainController = c;
65     [_mainView setMainController:c];
66 }
67
68 - (void)setMainView:(id)v
69 {
70     _mainView = v;
71 }
72
73 -(void)reallyExportMovie:(NSSavePanel *)savePanel toPod:(BOOL)exportToPod
74 {
75     MovieExportComponent        myExporter = NULL;
76     ComponentDescription        myCompDesc;
77     Boolean                     myCancelled = false;
78     long                        trackID;
79     MovieExportGetPropertyUPP   theAudioPropProcUPP = nil;
80     MovieExportGetDataUPP               theAudioDataProcUPP = nil;
81     TimeScale                   audioScale = 0;
82     void                        *audioRefCon = 0;
83     
84     OSErr                       err = noErr;
85     
86     // 下準備
87     float floatTime;
88     floatTime = [_mainController getHipTime];
89     movieDuration = QTMakeTime(floatTime*DEFAULT_FPS, DEFAULT_FPS);
90     rendering = YES;
91     [self readyGammmaTable];
92 //    NSRect originFrame = [_mainView frame];
93 //    [_mainView setFrame:NSMakeRect(originFrame.origin.x, originFrame.origin.y, ProjectMovieSize.size.width, ProjectMovieSize.size.height)];
94 //    [_mainView setFrame:NSMakeRect(0, 0, ProjectMovieSize.size.width, ProjectMovieSize.size.height)];
95     [_mainView setHidden:YES];
96 //    [_dummyWindow setFrame:*(NSRect*)&ProjectMovieSize display:YES];
97 //    [NSApp beginSheet:_dummyWindow modalForWindow:_mainWindow modalDelegate:self 
98 //       didEndSelector:nil contextInfo:nil];
99 //    [_dummyWindow setHidesOnDeactivate:YES];
100     
101     // プログレスバーを表示
102     [NSApp beginSheet:_barSheet modalForWindow:_mainWindow
103         modalDelegate:self didEndSelector:nil contextInfo:NULL];
104     
105     // Export into a Quicktime movie
106     myCompDesc.componentType = MovieExportType;
107     myCompDesc.componentSubType = MovieFileType;
108     myCompDesc.componentManufacturer = kAppleManufacturer;
109     myCompDesc.componentFlags = canMovieExportFromProcedures;
110     myCompDesc.componentFlagsMask = canMovieExportFromProcedures;
111     
112     // open the selected movie export component
113     myExporter = OpenComponent(FindNextComponent(NULL, &myCompDesc));
114     if (myExporter == NULL) {
115         NSLog(@"could not find export compontent !");
116         return;
117     }
118     
119     // Hey exporter, support modern audio features
120     Boolean useHighResolutionAudio = true;
121     QTSetComponentProperty(myExporter, kQTPropertyClass_MovieExporter,
122                            kQTMovieExporterPropertyID_EnableHighResolutionAudioFeatures,
123                            sizeof(Boolean),
124                            &useHighResolutionAudio);
125     
126     // create UPPs for the two app-defined export functions
127     MovieExportGetPropertyUPP theVideoPropProcUPP = NewMovieExportGetPropertyUPP(QTMoovProcs_VideoTrackPropertyProc);
128     MovieExportGetDataUPP         theVideoDataProcUPP = NewMovieExportGetDataUPP(QTMoovProcs_VideoTrackDataProc);
129     
130     // インスタンス変数を使ってる!
131     MovieExportAddDataSource(myExporter, VideoMediaType,
132                              movieDuration.timeScale,    // use the original timescale
133                              &trackID,
134                              theVideoPropProcUPP,
135                              theVideoDataProcUPP,
136                              self);
137     
138     // オーディオのみ書き出し
139     NSMutableArray* audioPaths = [_mainController writeAudioFiles];
140     NSMutableArray* audioMovies = [[NSMutableArray alloc] init];
141     
142     // オーディオファイルを実体化
143     for(id audioPath in audioPaths){
144         QTMovie* m = [[QTMovie alloc] initWithFile:audioPath error:nil];
145         NSLog(audioPath);
146         if(m == nil) continue;
147         [audioMovies addObject:m];
148     }
149     
150     // とりあえずいらない
151     // setup audio
152 //    NSMutableArray* audioTracks = [[NSMutableArray alloc] init];
153 //    [_mainController getSoundTrack:audioTracks];
154 //    int aui, tsize = [audioTracks count];
155 //    QTTime qtr;
156     for(QTMovie* audioMovie in audioMovies){
157         // we are setting up the audio for pass through
158         // インスタンス変数を使ってる!
159         QTTrack* t = [[audioMovie tracksOfMediaType:QTMediaTypeSound] objectAtIndex:0];
160         err = MovieExportNewGetDataAndPropertiesProcs(myExporter, SoundMediaType, 
161                                                       &audioScale, 
162                                                       [audioMovie quickTimeMovie],
163                                                       [t quickTimeTrack],       // we only use the first audio here
164                                                       0,
165                                                       [audioMovie duration].timeValue, 
166                                                       &theAudioPropProcUPP, 
167                                                       &theAudioDataProcUPP,
168                                                       &audioRefCon);
169         if (err) {
170             NSLog(@"Can't get audio for export");
171         } else {
172             MovieExportAddDataSource(myExporter, SoundMediaType, audioScale, &trackID, theAudioPropProcUPP, theAudioDataProcUPP, audioRefCon);
173         }
174     }
175     
176     // これは使う。
177     if (NO == exportToPod) {
178         // インスタンス変数を使ってる!
179         _myExporter = myExporter;
180         [self performSelectorOnMainThread:@selector(movieExportDialogMainThread) withObject:nil waitUntilDone:YES];
181 //        MovieExportDoUserDialog(myExporter, NULL, NULL, 0, movieDuration.timeValue, &myCancelled);
182         myExporter = _myExporter;
183         myCancelled = _myCancelled;
184         
185         if (myCancelled) {
186             NSLog(@"User canceled export dialog");
187             DisposeMovieExportGetPropertyUPP(theVideoPropProcUPP);
188             DisposeMovieExportGetDataUPP(theVideoDataProcUPP);
189             
190             if (theAudioPropProcUPP && theAudioDataProcUPP) {
191                 MovieExportDisposeGetDataAndPropertiesProcs(myExporter, theAudioPropProcUPP, theAudioDataProcUPP, audioRefCon);
192             }
193             
194             CloseComponent(myExporter);
195             [NSApp endSheet:_barSheet];
196             [_barSheet close];
197 //            [_mainView setHidden:NO];
198             
199             return;
200         }
201     }
202     
203     isExporting = YES;
204     cancelExport = NO;
205     
206     OSType myDataType;
207     Handle myDataRef;
208     
209     NSRect      frame = *(NSRect*)&ProjectMovieSize;
210     outputSize = frame;
211     
212     // create the readback and flipping buffers - see note about flipping in exportFrame method
213     // インスタンス変数やまもり
214     outputWidth = frame.size.width;
215     outputHeight = frame.size.height;
216     //outputWidth = 720;
217     //outputHeight = 480;
218     outputAlignment = 4;
219     contextRowBytes = outputWidth * outputAlignment;
220     contextPixels = calloc(contextRowBytes * outputHeight, sizeof(char));
221     flippedContextPixels = calloc(contextRowBytes * outputHeight, sizeof(char));
222     
223     // setup the image description for the frame compression
224     outputImageDescription = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
225     (*outputImageDescription)->idSize = sizeof(ImageDescription);
226 #ifdef __BIG_ENDIAN__
227     (*outputImageDescription)->cType = k32ARGBPixelFormat;
228 #else
229     (*outputImageDescription)->cType = k32BGRAPixelFormat;
230 #endif
231     (*outputImageDescription)->vendor = kAppleManufacturer;
232     (*outputImageDescription)->spatialQuality = codecLosslessQuality;
233     (*outputImageDescription)->width = outputWidth;
234     (*outputImageDescription)->height = outputHeight;
235     (*outputImageDescription)->hRes = 72L<<16;
236     (*outputImageDescription)->vRes = 72L<<16;
237     (*outputImageDescription)->dataSize = contextRowBytes * outputHeight;
238     (*outputImageDescription)->frameCount = 1;
239     (*outputImageDescription)->depth = 32;
240     (*outputImageDescription)->clutID = -1;
241     
242     [NSApp beginSheet:_barSheet modalForWindow:_mainWindow
243         modalDelegate:self didEndSelector:nil contextInfo:NULL];
244     
245     [[NSFileManager defaultManager] removeFileAtPath:[savePanel filename] handler:nil];
246     // export the video data to the data reference
247     QTNewDataReferenceFromCFURL((CFURLRef)[savePanel URL], 0, &myDataRef, &myDataType );
248     
249     // メインの処理はここじゃね。
250     MovieExportFromProceduresToDataRef(myExporter, myDataRef, myDataType);
251     
252     // we are done with the .mov export so lets clean up
253     // 終了処理?
254     free(contextPixels);
255     contextPixels = nil;
256     free(flippedContextPixels);
257     flippedContextPixels = nil;
258     DisposeMovieExportGetPropertyUPP(theVideoPropProcUPP);
259     DisposeMovieExportGetDataUPP(theVideoDataProcUPP);
260     
261     if (theAudioPropProcUPP && theAudioDataProcUPP) {
262         MovieExportDisposeGetDataAndPropertiesProcs(myExporter, theAudioPropProcUPP, theAudioDataProcUPP, audioRefCon);
263     }
264     
265     if (outputImageDescription) DisposeHandle((Handle)outputImageDescription);
266     outputImageDescription = NULL;
267     
268     CloseComponent(myExporter);
269     
270     // dispose the original data reference
271     DisposeHandle(myDataRef);
272     
273     for(NSString* audioPath in audioPaths)
274         [[NSFileManager defaultManager] removeFileAtPath:audioPath handler:nil];
275     
276     [_barSheet close];
277     [NSApp endSheet:_barSheet];
278     
279     [_mainView setHidden:NO];
280     [_dummyWindow close];
281     isExporting = NO;
282     rendering = NO;
283 }
284
285 // この関数が出力フレームごとに呼ばれるはず。
286 - (OSErr)exportFrame:(MovieExportGetDataParams *)theParams
287 {
288     if (cancelExport) return(userCanceledErr);
289     
290     if (theParams->requestedTime > movieDuration.timeValue) return(eofErr);
291     
292     NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];       // As the export is done in a tight loop it is a good idea to have an 
293     // autorelease pool in the render frame call so we don't acumulate 
294     // objects over the lengthy progress and therefore fill the memory
295     QTTime currentTime;
296     
297     currentTime.timeValue = theParams->requestedTime;
298     currentTime.timeScale = movieDuration.timeScale;
299     currentTime.flags = 0;
300     
301 //    [qtMovie setCurrentTime:currentTime];
302 //    MoviesTask([qtMovie quickTimeMovie], 0);  // QTKit is not doing this automatically
303     
304     // render the frame
305 //    [self updateCurrentFrame];                        
306 //    [self display];
307 //    [self performSelectorOnMainThread:@selector(renderFrame2:) withObject:[NSValue valueWithQTTime:currentTime] waitUntilDone:YES];
308     [_mainView getFrameForQTTime:currentTime];
309     
310     // read the frame from the context into our buffer
311 //    if([self readbackFrameIntoBuffer:contextPixels alignment:outputAlignment width:outputWidth height:outputHeight offsetX:0 offsetY:0]) {
312 //        NSLog(@"could not readback image!");
313 //    }
314     [_mainView getCurrentPixelData:outputSize buffer:contextPixels];
315     
316     /* WHY IS THIS memcpy ROUTINE HERE?
317      The way the pixels are read back through glReadPixels is flipped to what QuickTime expects. 
318      This can easily be worked around by rendering upside down - just switch the minY and maxY in glOrtho.
319      But since we display the exported image during the export process in this example, we don't want to do this
320      for visual purposes (because the image on the screen would end up being upside down), 
321      therefore we resort to flipping the image by hand.
322      */
323     int i = outputHeight;
324     while(--i >= 0) {
325         memcpy(flippedContextPixels + ((outputHeight - i - 1) * contextRowBytes), contextPixels + (i * contextRowBytes), contextRowBytes);
326     }
327     
328     // end flipping code
329     
330     // カラーシフトする分をガンマ補正。これはひどい。
331     [self gammaAdjust:flippedContextPixels size:outputHeight*outputWidth*4];
332     
333     // fill the return parameters for the compression
334     theParams->actualTime = theParams->requestedTime;
335     theParams->dataPtr = (void*)flippedContextPixels;
336     theParams->dataSize = (**(outputImageDescription)).dataSize;
337     theParams->desc = (SampleDescriptionHandle)outputImageDescription;
338     theParams->descType = VideoMediaType;
339     theParams->descSeed = 1;
340     theParams->actualSampleCount = 1;
341     theParams->durationPerSample = currentTime.timeScale / 30;
342     theParams->sampleFlags = 0L;
343     
344     [myPool release];
345     
346 //    NSLog(@"%f [s]", (float)currentTime.timeValue/currentTime.timeScale);
347     [self performSelectorOnMainThread:@selector(changeBarValue:) 
348                            withObject:[NSNumber numberWithDouble:(double)currentTime.timeValue/movieDuration.timeValue]
349                         waitUntilDone:YES];
350     
351     return noErr;
352 }
353
354 - (void)changeBarValue:(NSNumber*)v
355 {
356     [_bar setDoubleValue:[v doubleValue]];
357 }
358
359 - (void)renderFrame2:(NSValue*)v
360 {
361     [_mainView getFrameForQTTime:[v QTTimeValue]];
362 }
363
364 - (void)finalize
365 {
366     free(gamma_table);
367     [super finalize];
368 }
369
370 - (void)readyGammmaTable
371 {
372     int i;
373     
374     for(i = 0; i < 256; i++)
375         gamma_table[i] = (unsigned char)255.0 * pow(i/255.0, 1.0/GAMMA);
376 }
377
378 - (void)gammaAdjust:(unsigned char*)pixels size:(int)s
379 {
380     int i;
381     for(i = 0; i < s; i++){
382         if(i%4 == 3) continue;
383         pixels[i] = gamma_table[pixels[i]];
384     }
385 }
386
387 - (void)movieExportDialogMainThread
388 {
389     MovieExportDoUserDialog(_myExporter, NULL, NULL, 0, movieDuration.timeValue, &_myCancelled);
390 }
391
392 - (NSMutableArray*)getAudioTrack
393 {
394     NSMutableArray* soundTrack = [[NSMutableArray alloc] init];
395     QTTrack* track, *newTrack;
396     int i, size;
397     QTTime offset;
398     QTTimeRange mapping;
399     NSMutableArray* result = [[NSMutableArray alloc] init];
400     
401     [_mainController getSoundTrack:soundTrack];
402     size = [soundTrack count];
403     
404     for(i = 0; i < size; i += 4){
405         newTrack = [[QTTrack alloc] init];
406         track = [soundTrack objectAtIndex:i+1];
407         offset = [[soundTrack objectAtIndex:i+2] QTTimeValue];
408         mapping = [[soundTrack objectAtIndex:i+3] QTTimeRangeValue];
409         [newTrack insertSegmentOfTrack:track timeRange:QTMakeTimeRange(offset, mapping.duration) atTime:mapping.time];
410         [result addObject:[soundTrack objectAtIndex:i]];
411         [result addObject:newTrack];
412     }
413     
414     return result;
415 }
416
417 @end