1 /***********************************************************************************
2   Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3 
4   (c) Copyright 1996 - 2002  Gary Henderson (gary.henderson@ntlworld.com),
5                              Jerremy Koot (jkoot@snes9x.com)
6 
7   (c) Copyright 2002 - 2004  Matthew Kendora
8 
9   (c) Copyright 2002 - 2005  Peter Bortas (peter@bortas.org)
10 
11   (c) Copyright 2004 - 2005  Joel Yliluoma (http://iki.fi/bisqwit/)
12 
13   (c) Copyright 2001 - 2006  John Weidman (jweidman@slip.net)
14 
15   (c) Copyright 2002 - 2006  funkyass (funkyass@spam.shaw.ca),
16                              Kris Bleakley (codeviolation@hotmail.com)
17 
18   (c) Copyright 2002 - 2010  Brad Jorsch (anomie@users.sourceforge.net),
19                              Nach (n-a-c-h@users.sourceforge.net),
20 
21   (c) Copyright 2002 - 2011  zones (kasumitokoduck@yahoo.com)
22 
23   (c) Copyright 2006 - 2007  nitsuja
24 
25   (c) Copyright 2009 - 2016  BearOso,
26                              OV2
27 
28 
29   BS-X C emulator code
30   (c) Copyright 2005 - 2006  Dreamer Nom,
31                              zones
32 
33   C4 x86 assembler and some C emulation code
34   (c) Copyright 2000 - 2003  _Demo_ (_demo_@zsnes.com),
35                              Nach,
36                              zsKnight (zsknight@zsnes.com)
37 
38   C4 C++ code
39   (c) Copyright 2003 - 2006  Brad Jorsch,
40                              Nach
41 
42   DSP-1 emulator code
43   (c) Copyright 1998 - 2006  _Demo_,
44                              Andreas Naive (andreasnaive@gmail.com),
45                              Gary Henderson,
46                              Ivar (ivar@snes9x.com),
47                              John Weidman,
48                              Kris Bleakley,
49                              Matthew Kendora,
50                              Nach,
51                              neviksti (neviksti@hotmail.com)
52 
53   DSP-2 emulator code
54   (c) Copyright 2003         John Weidman,
55                              Kris Bleakley,
56                              Lord Nightmare (lord_nightmare@users.sourceforge.net),
57                              Matthew Kendora,
58                              neviksti
59 
60   DSP-3 emulator code
61   (c) Copyright 2003 - 2006  John Weidman,
62                              Kris Bleakley,
63                              Lancer,
64                              z80 gaiden
65 
66   DSP-4 emulator code
67   (c) Copyright 2004 - 2006  Dreamer Nom,
68                              John Weidman,
69                              Kris Bleakley,
70                              Nach,
71                              z80 gaiden
72 
73   OBC1 emulator code
74   (c) Copyright 2001 - 2004  zsKnight,
75                              pagefault (pagefault@zsnes.com),
76                              Kris Bleakley
77                              Ported from x86 assembler to C by sanmaiwashi
78 
79   SPC7110 and RTC C++ emulator code used in 1.39-1.51
80   (c) Copyright 2002         Matthew Kendora with research by
81                              zsKnight,
82                              John Weidman,
83                              Dark Force
84 
85   SPC7110 and RTC C++ emulator code used in 1.52+
86   (c) Copyright 2009         byuu,
87                              neviksti
88 
89   S-DD1 C emulator code
90   (c) Copyright 2003         Brad Jorsch with research by
91                              Andreas Naive,
92                              John Weidman
93 
94   S-RTC C emulator code
95   (c) Copyright 2001 - 2006  byuu,
96                              John Weidman
97 
98   ST010 C++ emulator code
99   (c) Copyright 2003         Feather,
100                              John Weidman,
101                              Kris Bleakley,
102                              Matthew Kendora
103 
104   Super FX x86 assembler emulator code
105   (c) Copyright 1998 - 2003  _Demo_,
106                              pagefault,
107                              zsKnight
108 
109   Super FX C emulator code
110   (c) Copyright 1997 - 1999  Ivar,
111                              Gary Henderson,
112                              John Weidman
113 
114   Sound emulator code used in 1.5-1.51
115   (c) Copyright 1998 - 2003  Brad Martin
116   (c) Copyright 1998 - 2006  Charles Bilyue'
117 
118   Sound emulator code used in 1.52+
119   (c) Copyright 2004 - 2007  Shay Green (gblargg@gmail.com)
120 
121   S-SMP emulator code used in 1.54+
122   (c) Copyright 2016         byuu
123 
124   SH assembler code partly based on x86 assembler code
125   (c) Copyright 2002 - 2004  Marcus Comstedt (marcus@mc.pp.se)
126 
127   2xSaI filter
128   (c) Copyright 1999 - 2001  Derek Liauw Kie Fa
129 
130   HQ2x, HQ3x, HQ4x filters
131   (c) Copyright 2003         Maxim Stepin (maxim@hiend3d.com)
132 
133   NTSC filter
134   (c) Copyright 2006 - 2007  Shay Green
135 
136   GTK+ GUI code
137   (c) Copyright 2004 - 2016  BearOso
138 
139   Win32 GUI code
140   (c) Copyright 2003 - 2006  blip,
141                              funkyass,
142                              Matthew Kendora,
143                              Nach,
144                              nitsuja
145   (c) Copyright 2009 - 2016  OV2
146 
147   Mac OS GUI code
148   (c) Copyright 1998 - 2001  John Stiles
149   (c) Copyright 2001 - 2011  zones
150 
151 
152   Specific ports contains the works of other authors. See headers in
153   individual files.
154 
155 
156   Snes9x homepage: http://www.snes9x.com/
157 
158   Permission to use, copy, modify and/or distribute Snes9x in both binary
159   and source form, for non-commercial purposes, is hereby granted without
160   fee, providing that this license information and copyright notice appear
161   with all copies and any derived work.
162 
163   This software is provided 'as-is', without any express or implied
164   warranty. In no event shall the authors be held liable for any damages
165   arising from the use of this software or it's derivatives.
166 
167   Snes9x is freeware for PERSONAL USE only. Commercial users should
168   seek permission of the copyright holders first. Commercial use includes,
169   but is not limited to, charging money for Snes9x or software derived from
170   Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171   using Snes9x as a promotion for your commercial product.
172 
173   The copyright holders request that bug fixes and improvements to the code
174   should be forwarded to them so everyone can benefit from the modifications
175   in future versions.
176 
177   Super NES and Super Nintendo Entertainment System are trademarks of
178   Nintendo Co., Limited and its subsidiary companies.
179  ***********************************************************************************/
180 
181 /***********************************************************************************
182   SNES9X for Mac OS (c) Copyright John Stiles
183 
184   Snes9x for Mac OS X
185 
186   (c) Copyright 2001 - 2011  zones
187   (c) Copyright 2002 - 2005  107
188   (c) Copyright 2002         PB1400c
189   (c) Copyright 2004         Alexander and Sander
190   (c) Copyright 2004 - 2005  Steven Seeger
191   (c) Copyright 2005         Ryan Vogt
192  ***********************************************************************************/
193 
194 
195 #include "snes9x.h"
196 #include "memmap.h"
197 #include "apu.h"
198 
199 #include <QuickTime/QuickTime.h>
200 
201 #include "mac-prefix.h"
202 #include "mac-gworld.h"
203 #include "mac-os.h"
204 #include "mac-screenshot.h"
205 #include "mac-quicktime.h"
206 
207 #define	kMovDoubleSize		(1 << 0)
208 #define	kMovExtendedHeight	(1 << 1)
209 
210 static void CheckError (OSStatus, int);
211 static void MacQTOpenVideoComponent (ComponentInstance *);
212 static void MacQTCloseVideoComponent (ComponentInstance);
213 static OSStatus WriteFrameCallBack (void *, ICMCompressionSessionRef, OSStatus, ICMEncodedFrameRef, void *);
214 
215 typedef struct
216 {
217 	Movie							movie;
218 	Track							vTrack, sTrack;
219 	Media							vMedia, sMedia;
220 	ComponentInstance				vci;
221 	SoundDescriptionHandle			soundDesc;
222 	DataHandler						dataHandler;
223 	Handle							soundBuffer;
224 	Handle							dataRef;
225 	OSType							dataRefType;
226 	CVPixelBufferPoolRef			pool;
227 	ICMCompressionSessionRef		session;
228 	ICMCompressionSessionOptionsRef	option;
229 	CGImageRef						srcImage;
230 	TimeValue64						timeStamp;
231 	long							keyFrame, keyFrameCount;
232 	long							frameSkip, frameSkipCount;
233 	int								width, height;
234 	int								soundBufferSize;
235 	int								samplesPerSec;
236 }	MacQTState;
237 
238 static MacQTState	sqt;
239 
240 
CheckError(OSStatus err,int n)241 static void CheckError (OSStatus err, int n)
242 {
243 	if (err != noErr)
244 	{
245 		char	mes[32];
246 
247 		sprintf(mes, "quicktime %02d", n);
248 		QuitWithFatalError(err, mes);
249 	}
250 }
251 
MacQTOpenVideoComponent(ComponentInstance * rci)252 static void MacQTOpenVideoComponent (ComponentInstance *rci)
253 {
254 	OSStatus			err;
255 	ComponentInstance	ci;
256 	CFDataRef			data;
257 
258 	ci = OpenDefaultComponent(StandardCompressionType, StandardCompressionSubType);
259 
260 	data = (CFDataRef) CFPreferencesCopyAppValue(CFSTR("QTVideoSetting"), kCFPreferencesCurrentApplication);
261 	if (data)
262 	{
263 		CFIndex	len;
264 		Handle	hdl;
265 
266 		len = CFDataGetLength(data);
267 		hdl = NewHandleClear((Size) len);
268 		if (MemError() == noErr)
269 		{
270 			HLock(hdl);
271 			CFDataGetBytes(data, CFRangeMake(0, len), (unsigned char *) *hdl);
272 			err = SCSetInfo(ci, scSettingsStateType, &hdl);
273 
274 			DisposeHandle(hdl);
275 		}
276 
277 		CFRelease(data);
278 	}
279 	else
280 	{
281 		SCSpatialSettings	ss;
282 		SCTemporalSettings	ts;
283 
284 		ss.codecType       = kAnimationCodecType;
285 		ss.codec           = 0;
286 		ss.depth           = 16;
287 		ss.spatialQuality  = codecMaxQuality;
288 		err = SCSetInfo(ci, scSpatialSettingsType, &ss);
289 
290 		ts.frameRate       = FixRatio(Memory.ROMFramesPerSecond, 1);
291 		ts.keyFrameRate    = Memory.ROMFramesPerSecond;
292 		ts.temporalQuality = codecMaxQuality;
293 		err = SCSetInfo(ci, scTemporalSettingsType, &ts);
294 	}
295 
296 	*rci = ci;
297 }
298 
MacQTCloseVideoComponent(ComponentInstance ci)299 static void MacQTCloseVideoComponent (ComponentInstance ci)
300 {
301 	OSStatus	err;
302 
303 	err = CloseComponent(ci);
304 }
305 
MacQTVideoConfig(void)306 void MacQTVideoConfig (void)
307 {
308 	OSStatus			err;
309 	ComponentInstance	ci;
310 
311 	MacQTOpenVideoComponent(&ci);
312 
313 	long	flag;
314 	flag = scListEveryCodec | scAllowZeroKeyFrameRate | scDisableFrameRateItem | scAllowEncodingWithCompressionSession;
315 	err = SCSetInfo(ci, scPreferenceFlagsType, &flag);
316 
317 	SCWindowSettings	ws;
318 	ws.size          = sizeof(SCWindowSettings);
319 	ws.windowRefKind = scWindowRefKindCarbon;
320 	ws.parentWindow  = NULL;
321 	err = SCSetInfo(ci, scWindowOptionsType, &ws);
322 
323 	err = SCRequestSequenceSettings(ci);
324 	if (err == noErr)
325 	{
326 		CFDataRef	data;
327 		Handle		hdl;
328 
329 		err = SCGetInfo(ci, scSettingsStateType, &hdl);
330 		if (err == noErr)
331 		{
332 			HLock(hdl);
333 			data = CFDataCreate(kCFAllocatorDefault, (unsigned char *) *hdl, GetHandleSize(hdl));
334 			if (data)
335 			{
336 				CFPreferencesSetAppValue(CFSTR("QTVideoSetting"), data, kCFPreferencesCurrentApplication);
337 				CFRelease(data);
338 			}
339 
340 			DisposeHandle(hdl);
341 		}
342 	}
343 
344 	MacQTCloseVideoComponent(ci);
345 }
346 
MacQTStartRecording(char * path)347 void MacQTStartRecording (char *path)
348 {
349 	OSStatus	err;
350 	CFStringRef str;
351 	CFURLRef	url;
352 
353 	memset(&sqt, 0, sizeof(sqt));
354 
355 	// storage
356 
357 	str = CFStringCreateWithCString(kCFAllocatorDefault, path, kCFStringEncodingUTF8);
358 	url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);
359 	err = QTNewDataReferenceFromCFURL(url, 0, &(sqt.dataRef), &(sqt.dataRefType));
360 	CheckError(err, 21);
361 	CFRelease(url);
362 	CFRelease(str);
363 
364 	err = CreateMovieStorage(sqt.dataRef, sqt.dataRefType, 'TVOD', smSystemScript, createMovieFileDeleteCurFile | newMovieActive, &(sqt.dataHandler), &(sqt.movie));
365 	CheckError(err, 22);
366 
367 	// video
368 
369 	MacQTOpenVideoComponent(&(sqt.vci));
370 
371 	long				flag;
372 	SCTemporalSettings	ts;
373 
374 	flag = scAllowEncodingWithCompressionSession;
375 	err = SCSetInfo(sqt.vci, scPreferenceFlagsType, &flag);
376 
377 	err = SCGetInfo(sqt.vci, scTemporalSettingsType, &ts);
378 	ts.frameRate = FixRatio(Memory.ROMFramesPerSecond, 1);
379 	if (ts.keyFrameRate < 1)
380 		ts.keyFrameRate = Memory.ROMFramesPerSecond;
381 	sqt.keyFrame  = sqt.keyFrameCount  = ts.keyFrameRate;
382 	sqt.frameSkip = sqt.frameSkipCount = (macQTMovFlag & 0xFF00) >> 8;
383 	err = SCSetInfo(sqt.vci, scTemporalSettingsType, &ts);
384 
385 	sqt.width  = ((macQTMovFlag & kMovDoubleSize) ? 2 : 1) * SNES_WIDTH;
386 	sqt.height = ((macQTMovFlag & kMovDoubleSize) ? 2 : 1) * ((macQTMovFlag & kMovExtendedHeight) ? SNES_HEIGHT_EXTENDED : SNES_HEIGHT);
387 
388 	sqt.srcImage = NULL;
389 	sqt.timeStamp = 0;
390 
391 	SCSpatialSettings			ss;
392 	ICMEncodedFrameOutputRecord	record;
393 	ICMMultiPassStorageRef		nullStorage = NULL;
394 
395 	err = SCCopyCompressionSessionOptions(sqt.vci, &(sqt.option));
396 	CheckError(err, 61);
397 	err = ICMCompressionSessionOptionsSetProperty(sqt.option, kQTPropertyClass_ICMCompressionSessionOptions, kICMCompressionSessionOptionsPropertyID_MultiPassStorage, sizeof(ICMMultiPassStorageRef), &nullStorage);
398 
399 	record.encodedFrameOutputCallback = WriteFrameCallBack;
400 	record.encodedFrameOutputRefCon   = NULL;
401 	record.frameDataAllocator         = NULL;
402 	err = SCGetInfo(sqt.vci, scSpatialSettingsType, &ss);
403 	err = ICMCompressionSessionCreate(kCFAllocatorDefault, sqt.width, sqt.height, ss.codecType, Memory.ROMFramesPerSecond, sqt.option, NULL, &record, &(sqt.session));
404 	CheckError(err, 62);
405 
406 	CFMutableDictionaryRef	dic;
407 	CFNumberRef				val;
408 	OSType					pix = k16BE555PixelFormat;
409 	int						row = sqt.width * 2;
410 
411 	dic = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
412 
413 	val = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pix);
414 	CFDictionaryAddValue(dic, kCVPixelBufferPixelFormatTypeKey, val);
415 	CFRelease(val);
416 
417 	val = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &(sqt.width));
418 	CFDictionaryAddValue(dic, kCVPixelBufferWidthKey, val);
419 	CFRelease(val);
420 
421 	val = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &(sqt.height));
422 	CFDictionaryAddValue(dic, kCVPixelBufferHeightKey, val);
423 	CFRelease(val);
424 
425 	val = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &row);
426 	CFDictionaryAddValue(dic, kCVPixelBufferBytesPerRowAlignmentKey, val);
427 	CFRelease(val);
428 
429 	CFDictionaryAddValue(dic, kCVPixelBufferCGImageCompatibilityKey, kCFBooleanTrue);
430 	CFDictionaryAddValue(dic, kCVPixelBufferCGBitmapContextCompatibilityKey, kCFBooleanTrue);
431 
432 	err = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, dic, &(sqt.pool));
433 	CheckError(err, 63);
434 
435 	CFRelease(dic);
436 
437 	sqt.vTrack = NewMovieTrack(sqt.movie, FixRatio(sqt.width, 1), FixRatio(sqt.height, 1), kNoVolume);
438 	CheckError(GetMoviesError(), 23);
439 
440 	sqt.vMedia = NewTrackMedia(sqt.vTrack, VideoMediaType, Memory.ROMFramesPerSecond, NULL, 0);
441 	CheckError(GetMoviesError(), 24);
442 
443 	err = BeginMediaEdits(sqt.vMedia);
444 	CheckError(err, 25);
445 
446 	// sound
447 
448 	sqt.soundDesc = (SoundDescriptionHandle) NewHandleClear(sizeof(SoundDescription));
449 	CheckError(MemError(), 26);
450 
451 	(**sqt.soundDesc).descSize    = sizeof(SoundDescription);
452 #ifdef __BIG_ENDIAN__
453 	(**sqt.soundDesc).dataFormat  = Settings.SixteenBitSound ? k16BitBigEndianFormat    : k8BitOffsetBinaryFormat;
454 #else
455 	(**sqt.soundDesc).dataFormat  = Settings.SixteenBitSound ? k16BitLittleEndianFormat : k8BitOffsetBinaryFormat;
456 #endif
457 	(**sqt.soundDesc).numChannels = Settings.Stereo ? 2 : 1;
458 	(**sqt.soundDesc).sampleSize  = Settings.SixteenBitSound ? 16 : 8;
459 	(**sqt.soundDesc).sampleRate  = (UnsignedFixed) FixRatio(Settings.SoundPlaybackRate, 1);
460 
461 	sqt.samplesPerSec = Settings.SoundPlaybackRate / Memory.ROMFramesPerSecond;
462 
463 	sqt.soundBufferSize = sqt.samplesPerSec;
464 	if (Settings.SixteenBitSound)
465 		sqt.soundBufferSize <<= 1;
466 	if (Settings.Stereo)
467 		sqt.soundBufferSize <<= 1;
468 
469 	sqt.soundBuffer = NewHandleClear(sqt.soundBufferSize);
470 	CheckError(MemError(), 27);
471 	HLock(sqt.soundBuffer);
472 
473 	sqt.sTrack = NewMovieTrack(sqt.movie, 0, 0, kFullVolume);
474 	CheckError(GetMoviesError(), 28);
475 
476 	sqt.sMedia = NewTrackMedia(sqt.sTrack, SoundMediaType, Settings.SoundPlaybackRate, NULL, 0);
477 	CheckError(GetMoviesError(), 29);
478 
479 	err = BeginMediaEdits(sqt.sMedia);
480 	CheckError(err, 30);
481 }
482 
MacQTRecordFrame(int width,int height)483 void MacQTRecordFrame (int width, int height)
484 {
485 	OSStatus	err;
486 
487 	// video
488 
489 	if (sqt.frameSkipCount == sqt.frameSkip)
490 	{
491 		CVPixelBufferRef	buf;
492 
493 		err = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, sqt.pool, &buf);
494 		if (err == noErr)
495 		{
496 			CGColorSpaceRef		color;
497 			CGContextRef		ctx;
498 			uint16				*p;
499 
500 			err = CVPixelBufferLockBaseAddress(buf, 0);
501 			p = (uint16 *) CVPixelBufferGetBaseAddress(buf);
502 
503 			color = CGColorSpaceCreateDeviceRGB();
504 			ctx = CGBitmapContextCreate((void *) p, sqt.width, sqt.height, 5, sqt.width * 2, color, kCGImageAlphaNoneSkipFirst | ((systemVersion >= 0x1040) ? kCGBitmapByteOrder16Host : 0));
505 			CGContextSetShouldAntialias(ctx, false);
506 
507 			if (sqt.srcImage)
508 				CGImageRelease(sqt.srcImage);
509 			sqt.srcImage = CreateGameScreenCGImage();
510 
511 			CGRect	dst = CGRectMake(0.0f, 0.0f, (float) sqt.width, (float) sqt.height);
512 
513 			if ((!(height % SNES_HEIGHT_EXTENDED)) && (!(macQTMovFlag & kMovExtendedHeight)))
514 			{
515 				CGRect	src;
516 
517 				src.size.width  = (float) width;
518 				src.size.height = (float) ((height > 256) ? (SNES_HEIGHT << 1) : SNES_HEIGHT);
519 				src.origin.x    = (float) 0;
520 				src.origin.y    = (float) height - src.size.height;
521 				DrawSubCGImage(ctx, sqt.srcImage, src, dst);
522 			}
523 			else
524 			if ((sqt.height << 1) % height)
525 			{
526 				CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
527 				CGContextFillRect(ctx, dst);
528 
529 				float	dh  = (float) ((sqt.height > 256) ? (SNES_HEIGHT << 1) : SNES_HEIGHT);
530 				float	ofs = (float) ((int) ((drawoverscan ? 1.0 : 0.5) * ((float) sqt.height - dh) + 0.5));
531 				dst = CGRectMake(0.0f, ofs, (float) sqt.width, dh);
532 				CGContextDrawImage(ctx, dst, sqt.srcImage);
533 			}
534 			else
535 				CGContextDrawImage(ctx, dst, sqt.srcImage);
536 
537 			CGContextRelease(ctx);
538 			CGColorSpaceRelease(color);
539 
540 		#ifndef __BIG_ENDIAN__
541 			for (int i = 0; i < sqt.width * sqt.height; i++)
542 				SWAP_WORD(p[i]);
543 		#endif
544 
545 			err = CVPixelBufferUnlockBaseAddress(buf, 0);
546 
547 			err = ICMCompressionSessionEncodeFrame(sqt.session, buf, sqt.timeStamp, 0, kICMValidTime_DisplayTimeStampIsValid, NULL, NULL, NULL);
548 
549 			CVPixelBufferRelease(buf);
550 		}
551 
552 		sqt.keyFrameCount--;
553 		if (sqt.keyFrameCount <= 0)
554 			sqt.keyFrameCount = sqt.keyFrame;
555 	}
556 
557 	sqt.frameSkipCount--;
558 	if (sqt.frameSkipCount < 0)
559 		sqt.frameSkipCount = sqt.frameSkip;
560 
561 	sqt.timeStamp++;
562 
563 	// sound
564 
565 	int	sample_count = sqt.soundBufferSize;
566 	if (Settings.SixteenBitSound)
567 		sample_count >>= 1;
568 
569 	S9xMixSamples((uint8 *) *(sqt.soundBuffer), sample_count);
570 
571 	err = AddMediaSample(sqt.sMedia, sqt.soundBuffer, 0, sqt.soundBufferSize, 1, (SampleDescriptionHandle) sqt.soundDesc, sqt.samplesPerSec, mediaSampleNotSync, NULL);
572 }
573 
WriteFrameCallBack(void * refCon,ICMCompressionSessionRef session,OSStatus r,ICMEncodedFrameRef frame,void * reserved)574 static OSStatus WriteFrameCallBack (void *refCon, ICMCompressionSessionRef session, OSStatus r, ICMEncodedFrameRef frame, void *reserved)
575 {
576 	OSStatus	err;
577 
578 	err = AddMediaSampleFromEncodedFrame(sqt.vMedia, frame, NULL);
579 	return (err);
580 }
581 
MacQTStopRecording(void)582 void MacQTStopRecording (void)
583 {
584 	OSStatus   err;
585 
586 	// video
587 
588 	err = ICMCompressionSessionCompleteFrames(sqt.session, true, 0, sqt.timeStamp);
589 	err = ExtendMediaDecodeDurationToDisplayEndTime(sqt.vMedia, NULL);
590 
591 	err = EndMediaEdits(sqt.vMedia);
592 	CheckError(err, 52);
593 
594 	err = InsertMediaIntoTrack(sqt.vTrack, 0, 0, (TimeValue) GetMediaDisplayDuration(sqt.vMedia), fixed1);
595 	CheckError(err, 58);
596 
597 	CGImageRelease(sqt.srcImage);
598 	CVPixelBufferPoolRelease(sqt.pool);
599 	ICMCompressionSessionRelease(sqt.session);
600 	ICMCompressionSessionOptionsRelease(sqt.option);
601 
602 	// sound
603 
604 	err = EndMediaEdits(sqt.sMedia);
605 	CheckError(err, 54);
606 
607 	err = InsertMediaIntoTrack(sqt.sTrack, 0, 0, GetMediaDuration(sqt.sMedia), fixed1);
608 	CheckError(err, 55);
609 
610 	DisposeHandle(sqt.soundBuffer);
611 	DisposeHandle((Handle) sqt.soundDesc);
612 
613 	// storage
614 
615 	err = AddMovieToStorage(sqt.movie, sqt.dataHandler);
616 	CheckError(err, 56);
617 
618 	MacQTCloseVideoComponent(sqt.vci);
619 
620 	err = CloseMovieStorage(sqt.dataHandler);
621 	CheckError(err, 57);
622 
623 	DisposeHandle(sqt.dataRef);
624 	DisposeMovie(sqt.movie);
625 }
626