diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/moviemaker.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/newview/moviemaker.cpp')
-rw-r--r-- | linden/indra/newview/moviemaker.cpp | 806 |
1 files changed, 806 insertions, 0 deletions
diff --git a/linden/indra/newview/moviemaker.cpp b/linden/indra/newview/moviemaker.cpp new file mode 100644 index 0000000..1054f1a --- /dev/null +++ b/linden/indra/newview/moviemaker.cpp | |||
@@ -0,0 +1,806 @@ | |||
1 | /** | ||
2 | * @file moviemaker.cpp | ||
3 | * @brief MovieMaker implementation | ||
4 | * | ||
5 | * Copyright (c) 2003-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | // =============================================== | ||
29 | // MovieMaker.cpp | ||
30 | // =============================================== | ||
31 | |||
32 | #include "llviewerprecompiledheaders.h" | ||
33 | |||
34 | #include "moviemaker.h" | ||
35 | #include <stdio.h> | ||
36 | #include <stdlib.h> | ||
37 | #include <memory.h> | ||
38 | |||
39 | #if LL_WINDOWS | ||
40 | |||
41 | #include <windowsx.h> | ||
42 | |||
43 | HANDLE MakeDib( HBITMAP hbitmap, UINT bits ); | ||
44 | HBITMAP LoadBMPFromFB( int w, int h ); | ||
45 | |||
46 | /* | ||
47 | =============================================== | ||
48 | Constructors, Destructor | ||
49 | =============================================== | ||
50 | */ | ||
51 | |||
52 | |||
53 | MovieMaker::MovieMaker() | ||
54 | { | ||
55 | sprintf( fname, "movie.avi" ); | ||
56 | width = -1; | ||
57 | height = -1; | ||
58 | |||
59 | bOK = true; | ||
60 | nFrames = 0; | ||
61 | |||
62 | pfile = NULL; | ||
63 | ps = NULL; | ||
64 | psCompressed = NULL; | ||
65 | psText = NULL; | ||
66 | aopts[0] = &opts; | ||
67 | |||
68 | // Check VFW version. | ||
69 | WORD wVer = HIWORD( VideoForWindowsVersion() ); | ||
70 | if ( wVer < 0x010A ) | ||
71 | { | ||
72 | fprintf( stderr, "VFW version is too old.\n" ); | ||
73 | exit( -1 ); | ||
74 | } | ||
75 | else | ||
76 | { | ||
77 | AVIFileInit(); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | |||
82 | MovieMaker::~MovieMaker() | ||
83 | { | ||
84 | if (ps) | ||
85 | AVIStreamClose(ps); | ||
86 | |||
87 | if (psCompressed) | ||
88 | AVIStreamClose(psCompressed); | ||
89 | |||
90 | if (psText) | ||
91 | AVIStreamClose(psText); | ||
92 | |||
93 | if (pfile) | ||
94 | { | ||
95 | AVIFileClose(pfile); | ||
96 | } | ||
97 | |||
98 | WORD wVer = HIWORD(VideoForWindowsVersion()); | ||
99 | if (wVer >= 0x010A) | ||
100 | { | ||
101 | AVIFileExit(); | ||
102 | } | ||
103 | } | ||
104 | |||
105 | void MovieMaker::StartCapture( char *name , int x, int y) | ||
106 | { | ||
107 | strcpy( fname, name ); | ||
108 | |||
109 | // Get the width and height. | ||
110 | width = x; | ||
111 | height = y; | ||
112 | |||
113 | fprintf( stderr, "Starting %d x %d capture to file: %s\n", width, height, fname ); | ||
114 | |||
115 | bOK = TRUE; | ||
116 | |||
117 | nFrames = 0; | ||
118 | |||
119 | } | ||
120 | |||
121 | void MovieMaker::EndCapture() | ||
122 | { | ||
123 | fprintf( stderr, "\n" ); | ||
124 | if (ps) | ||
125 | { | ||
126 | AVIStreamClose(ps); | ||
127 | ps = NULL; | ||
128 | } | ||
129 | |||
130 | if (psCompressed) | ||
131 | { | ||
132 | AVIStreamClose(psCompressed); | ||
133 | psCompressed = NULL; | ||
134 | } | ||
135 | |||
136 | if (psText) | ||
137 | { | ||
138 | AVIStreamClose(psText); | ||
139 | psText = NULL; | ||
140 | } | ||
141 | |||
142 | if (pfile) | ||
143 | { | ||
144 | AVIFileClose(pfile); | ||
145 | pfile = NULL; | ||
146 | } | ||
147 | |||
148 | WORD wVer = HIWORD(VideoForWindowsVersion()); | ||
149 | if (wVer >= 0x010A) | ||
150 | { | ||
151 | AVIFileExit(); | ||
152 | } | ||
153 | |||
154 | } | ||
155 | |||
156 | bool MovieMaker::Snap() | ||
157 | { | ||
158 | HRESULT hr; | ||
159 | |||
160 | if (!bOK) | ||
161 | return false; | ||
162 | |||
163 | // Get an image and stuff it into a bitmap. | ||
164 | HBITMAP bmp; | ||
165 | bmp = LoadBMPFromFB( width, height ); | ||
166 | |||
167 | LPBITMAPINFOHEADER alpbi = (LPBITMAPINFOHEADER)GlobalLock(MakeDib(bmp, 32)); | ||
168 | DeleteObject( bmp ); | ||
169 | |||
170 | if (alpbi == NULL) | ||
171 | { | ||
172 | bOK = false; | ||
173 | return false; | ||
174 | } | ||
175 | if (width>=0 && width != alpbi->biWidth) | ||
176 | { | ||
177 | GlobalFreePtr(alpbi); | ||
178 | bOK = false; | ||
179 | return false; | ||
180 | } | ||
181 | if (height>=0 && height != alpbi->biHeight) | ||
182 | { | ||
183 | GlobalFreePtr(alpbi); | ||
184 | bOK = false; | ||
185 | return false; | ||
186 | } | ||
187 | width = alpbi->biWidth; | ||
188 | height = alpbi->biHeight; | ||
189 | if (nFrames == 0) | ||
190 | { | ||
191 | hr = AVIFileOpenA(&pfile, // returned file pointer | ||
192 | fname, // file name | ||
193 | OF_WRITE | OF_CREATE, // mode to open file with | ||
194 | NULL); // use handler determined | ||
195 | // from file extension.... | ||
196 | if (hr != AVIERR_OK) | ||
197 | { | ||
198 | GlobalFreePtr(alpbi); | ||
199 | bOK = false; | ||
200 | return false; | ||
201 | } | ||
202 | _fmemset(&strhdr, 0, sizeof(strhdr)); | ||
203 | strhdr.fccType = streamtypeVIDEO;// stream type | ||
204 | strhdr.fccHandler = 0; | ||
205 | strhdr.dwScale = 1; | ||
206 | strhdr.dwRate = 15; | ||
207 | strhdr.dwSuggestedBufferSize = alpbi->biSizeImage; | ||
208 | SetRect(&strhdr.rcFrame, 0, 0, // rectangle for stream | ||
209 | (int) alpbi->biWidth, | ||
210 | (int) alpbi->biHeight); | ||
211 | |||
212 | // And create the stream; | ||
213 | hr = AVIFileCreateStream(pfile, // file pointer | ||
214 | &ps, // returned stream pointer | ||
215 | &strhdr); // stream header | ||
216 | if (hr != AVIERR_OK) | ||
217 | { | ||
218 | GlobalFreePtr(alpbi); | ||
219 | bOK = false; | ||
220 | return false; | ||
221 | } | ||
222 | |||
223 | _fmemset(&opts, 0, sizeof(opts)); | ||
224 | |||
225 | if (!AVISaveOptions(NULL, ICMF_CHOOSE_KEYFRAME, 1, &ps, (LPAVICOMPRESSOPTIONS FAR *) &aopts)) | ||
226 | { | ||
227 | fprintf( stderr, "AVISaveOptions failed.\n" ); | ||
228 | GlobalFreePtr(alpbi); | ||
229 | bOK = false; | ||
230 | return false; | ||
231 | } | ||
232 | |||
233 | hr = AVIMakeCompressedStream(&psCompressed, ps, &opts, NULL); | ||
234 | if (hr != AVIERR_OK) | ||
235 | { | ||
236 | fprintf( stderr, "AVIMakeCompressedStream failed.\n" ); | ||
237 | GlobalFreePtr(alpbi); | ||
238 | bOK = false; | ||
239 | return false; | ||
240 | } | ||
241 | |||
242 | hr = AVIStreamSetFormat(psCompressed, 0, | ||
243 | alpbi, // stream format | ||
244 | alpbi->biSize + // format size | ||
245 | alpbi->biClrUsed * sizeof(RGBQUAD)); | ||
246 | if (hr != AVIERR_OK) | ||
247 | { | ||
248 | fprintf( stderr, "AVIStreamSetFormat failed.\n" ); | ||
249 | GlobalFreePtr(alpbi); | ||
250 | bOK = false; | ||
251 | return false; | ||
252 | } | ||
253 | |||
254 | // Fill in the stream header for the text stream.... | ||
255 | |||
256 | // The text stream is in 60ths of a second.... | ||
257 | /* | ||
258 | _fmemset(&strhdr, 0, sizeof(strhdr)); | ||
259 | strhdr.fccType = streamtypeTEXT; | ||
260 | strhdr.fccHandler = mmioFOURCC('D', 'R', 'A', 'W'); | ||
261 | strhdr.dwScale = 1; | ||
262 | strhdr.dwRate = 60; | ||
263 | strhdr.dwSuggestedBufferSize = sizeof(szText); | ||
264 | SetRect(&strhdr.rcFrame, 0, (int) alpbi->biHeight, | ||
265 | (int) alpbi->biWidth, (int) alpbi->biHeight + TEXT_HEIGHT); | ||
266 | |||
267 | // ....and create the stream. | ||
268 | hr = AVIFileCreateStream(pfile, &psText, &strhdr); | ||
269 | if (hr != AVIERR_OK) | ||
270 | { | ||
271 | GlobalFreePtr(alpbi); | ||
272 | bOK = false; | ||
273 | return false; | ||
274 | } | ||
275 | |||
276 | dwTextFormat = sizeof(dwTextFormat); | ||
277 | hr = AVIStreamSetFormat(psText, 0, &dwTextFormat, sizeof(dwTextFormat)); | ||
278 | if (hr != AVIERR_OK) | ||
279 | { | ||
280 | GlobalFreePtr(alpbi); | ||
281 | bOK = false; | ||
282 | return false; | ||
283 | } | ||
284 | */ | ||
285 | } | ||
286 | |||
287 | // Now actual writing | ||
288 | hr = AVIStreamWrite(psCompressed, // stream pointer | ||
289 | nFrames * 1, // 10, // time of this frame | ||
290 | 1, // number to write | ||
291 | (LPBYTE) alpbi + // pointer to data | ||
292 | alpbi->biSize + | ||
293 | alpbi->biClrUsed * sizeof(RGBQUAD), | ||
294 | alpbi->biSizeImage, // size of this frame | ||
295 | AVIIF_KEYFRAME, // flags.... | ||
296 | NULL, | ||
297 | NULL); | ||
298 | if (hr != AVIERR_OK) | ||
299 | { | ||
300 | fprintf( stderr, "AVIStreamWrite failed.\n" ); | ||
301 | GlobalFreePtr(alpbi); | ||
302 | bOK = false; | ||
303 | return false; | ||
304 | } | ||
305 | |||
306 | // Make some text to put in the file ... | ||
307 | //LoadString(hInstance, IDS_TEXTFORMAT, szMessage, BUFSIZE ); | ||
308 | /* | ||
309 | strcpy(szMessage, "This is frame #%d"); | ||
310 | |||
311 | int iLen = wsprintf(szText, szMessage, (int)(nFrames + 1)); | ||
312 | |||
313 | // ... and write it as well. | ||
314 | hr = AVIStreamWrite(psText, | ||
315 | nFrames * 40, | ||
316 | 1, | ||
317 | szText, | ||
318 | iLen + 1, | ||
319 | AVIIF_KEYFRAME, | ||
320 | NULL, | ||
321 | NULL); | ||
322 | if (hr != AVIERR_OK) | ||
323 | { | ||
324 | GlobalFreePtr(alpbi); | ||
325 | bOK = false; | ||
326 | return false; | ||
327 | } | ||
328 | */ | ||
329 | GlobalFreePtr(alpbi); | ||
330 | |||
331 | nFrames++; | ||
332 | |||
333 | fprintf( stderr, "Wrote frame %d.\r", nFrames ); | ||
334 | |||
335 | return true; | ||
336 | } | ||
337 | |||
338 | static HANDLE MakeDib( HBITMAP hbitmap, UINT bits ) | ||
339 | { | ||
340 | HANDLE hdib ; | ||
341 | HDC hdc ; | ||
342 | BITMAP bitmap ; | ||
343 | UINT wLineLen ; | ||
344 | DWORD dwSize ; | ||
345 | DWORD wColSize ; | ||
346 | LPBITMAPINFOHEADER lpbi ; | ||
347 | LPBYTE lpBits ; | ||
348 | |||
349 | GetObject(hbitmap,sizeof(BITMAP),&bitmap) ; | ||
350 | |||
351 | // | ||
352 | // DWORD align the width of the DIB | ||
353 | // Figure out the size of the colour table | ||
354 | // Calculate the size of the DIB | ||
355 | // | ||
356 | wLineLen = (bitmap.bmWidth*bits+31)/32 * 4; | ||
357 | wColSize = sizeof(RGBQUAD)*((bits <= 8) ? 1<<bits : 0); | ||
358 | dwSize = sizeof(BITMAPINFOHEADER) + wColSize + | ||
359 | (DWORD)(UINT)wLineLen*(DWORD)(UINT)bitmap.bmHeight; | ||
360 | |||
361 | // | ||
362 | // Allocate room for a DIB and set the LPBI fields | ||
363 | // | ||
364 | hdib = GlobalAlloc(GHND,dwSize); | ||
365 | if (!hdib) | ||
366 | return hdib ; | ||
367 | |||
368 | lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib) ; | ||
369 | |||
370 | lpbi->biSize = sizeof(BITMAPINFOHEADER) ; | ||
371 | lpbi->biWidth = bitmap.bmWidth ; | ||
372 | lpbi->biHeight = bitmap.bmHeight ; | ||
373 | lpbi->biPlanes = 1 ; | ||
374 | lpbi->biBitCount = (WORD) bits ; | ||
375 | lpbi->biCompression = BI_RGB ; | ||
376 | lpbi->biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize ; | ||
377 | lpbi->biXPelsPerMeter = 0 ; | ||
378 | lpbi->biYPelsPerMeter = 0 ; | ||
379 | lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0; | ||
380 | lpbi->biClrImportant = 0 ; | ||
381 | |||
382 | // | ||
383 | // Get the bits from the bitmap and stuff them after the LPBI | ||
384 | // | ||
385 | lpBits = (LPBYTE)(lpbi+1)+wColSize ; | ||
386 | |||
387 | hdc = CreateCompatibleDC(NULL) ; | ||
388 | |||
389 | GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,lpBits,(LPBITMAPINFO)lpbi, DIB_RGB_COLORS); | ||
390 | |||
391 | // Fix this if GetDIBits messed it up.... | ||
392 | lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0; | ||
393 | |||
394 | DeleteDC(hdc) ; | ||
395 | GlobalUnlock(hdib); | ||
396 | |||
397 | return hdib ; | ||
398 | } | ||
399 | |||
400 | |||
401 | static HBITMAP LoadBMPFromFB( int w, int h ) | ||
402 | { | ||
403 | // Create a normal DC and a memory DC for the entire screen. The | ||
404 | // normal DC provides a "snapshot" of the screen contents. The | ||
405 | // memory DC keeps a copy of this "snapshot" in the associated | ||
406 | // bitmap. | ||
407 | |||
408 | HDC hdcScreen = wglGetCurrentDC(); | ||
409 | HDC hdcCompatible = CreateCompatibleDC(hdcScreen); | ||
410 | |||
411 | // Create a compatible bitmap for hdcScreen. | ||
412 | |||
413 | HBITMAP hbmScreen = CreateCompatibleBitmap(hdcScreen, | ||
414 | // GetDeviceCaps(hdcScreen, HORZRES), | ||
415 | // GetDeviceCaps(hdcScreen, VERTRES)); | ||
416 | w, | ||
417 | h ); | ||
418 | |||
419 | if (hbmScreen == 0) | ||
420 | { | ||
421 | fprintf( stderr, "hbmScreen == NULL\nExiting.\n" ); | ||
422 | exit( -1 ); | ||
423 | //errhandler("hbmScreen", hwnd); | ||
424 | } | ||
425 | |||
426 | // Select the bitmaps into the compatible DC. | ||
427 | |||
428 | if (!SelectObject(hdcCompatible, hbmScreen)) | ||
429 | { | ||
430 | fprintf( stderr, "Couldn't SelectObject()\nExiting.\n" ); | ||
431 | exit( -1 ); | ||
432 | //errhandler("Compatible Bitmap Selection", hwnd); | ||
433 | } | ||
434 | |||
435 | // Hide the application window. | ||
436 | |||
437 | // ShowWindow(hwnd, SW_HIDE); | ||
438 | |||
439 | //Copy color data for the entire display into a | ||
440 | //bitmap that is selected into a compatible DC. | ||
441 | |||
442 | if (!BitBlt(hdcCompatible, | ||
443 | 0,0, | ||
444 | w, h, | ||
445 | hdcScreen, | ||
446 | // 512,512, | ||
447 | 0, 0, | ||
448 | SRCCOPY)) | ||
449 | { | ||
450 | fprintf( stderr, "Screen to Compat Blt Failed\nExiting.\n" ); | ||
451 | exit( -1 ); | ||
452 | //errhandler("Screen to Compat Blt Failed", hwnd); | ||
453 | } | ||
454 | |||
455 | // Redraw the application window. | ||
456 | //ShowWindow(hwnd, SW_SHOW); | ||
457 | |||
458 | DeleteDC( hdcCompatible ); | ||
459 | |||
460 | return( hbmScreen ); | ||
461 | } | ||
462 | |||
463 | #elif LL_DARWIN | ||
464 | |||
465 | #include <AGL/agl.h> | ||
466 | #include <AGL/gl.h> | ||
467 | #include <AGL/glu.h> | ||
468 | |||
469 | // BAD Apple. BAD! | ||
470 | #ifdef verify | ||
471 | #undef verify | ||
472 | #endif | ||
473 | |||
474 | #include "llviewerwindow.h" | ||
475 | #include "llworld.h" | ||
476 | |||
477 | MovieMaker::MovieMaker() | ||
478 | { | ||
479 | movie = NULL; | ||
480 | movieResRef = 0; | ||
481 | track = NULL; | ||
482 | media = NULL; | ||
483 | width = 0; | ||
484 | height = 0; | ||
485 | bufferSize = 0; | ||
486 | rowBytes = 0; | ||
487 | buffer = NULL; | ||
488 | invertedBuffer = NULL; | ||
489 | ci = NULL; | ||
490 | gworld = NULL; | ||
491 | idh = NULL; | ||
492 | } | ||
493 | |||
494 | MovieMaker::~MovieMaker() | ||
495 | { | ||
496 | EndCapture(); | ||
497 | } | ||
498 | |||
499 | void MovieMaker::StartCapture( char *name , int x, int y) | ||
500 | { | ||
501 | strncpy( fname, name, sizeof(fname)); | ||
502 | width = x; | ||
503 | height = y; | ||
504 | |||
505 | setupMovie(); | ||
506 | } | ||
507 | |||
508 | OSStatus MovieMaker::setupMovie() | ||
509 | { | ||
510 | OSStatus error = noErr; | ||
511 | FSRef fileRef; | ||
512 | FSSpec fileSpec; | ||
513 | |||
514 | rowBytes = width * 4; | ||
515 | bufferSize = height * rowBytes; | ||
516 | buffer = (char*)malloc(bufferSize); | ||
517 | invertedBuffer = (char*)malloc(bufferSize); | ||
518 | |||
519 | rect.left = 0; | ||
520 | rect.top = 0; | ||
521 | rect.right = width; | ||
522 | rect.bottom = height; | ||
523 | |||
524 | error = NewGWorldFromPtr(&gworld, k32ARGBPixelFormat, &rect, 0, 0, 0, buffer, rowBytes); | ||
525 | |||
526 | if (error == noErr) | ||
527 | { | ||
528 | LockPixels(GetGWorldPixMap(gworld)); | ||
529 | } | ||
530 | |||
531 | // MBW -- I think this needs to happen after all the dialogs, etc. | ||
532 | // if (error == noErr) | ||
533 | // { | ||
534 | // Microseconds(&lastFrameTime); | ||
535 | // error = grabFrame(); | ||
536 | // } | ||
537 | |||
538 | if (error == noErr) | ||
539 | { | ||
540 | error = EnterMovies(); | ||
541 | } | ||
542 | |||
543 | if (error == noErr) | ||
544 | { | ||
545 | ci = OpenDefaultComponent(StandardCompressionType,StandardCompressionSubType); | ||
546 | if(ci == NULL) | ||
547 | error = paramErr; | ||
548 | } | ||
549 | |||
550 | if (error == noErr) | ||
551 | { | ||
552 | long flags; | ||
553 | |||
554 | SCGetInfo(ci,scPreferenceFlagsType,&flags); | ||
555 | flags &= ~scShowBestDepth; | ||
556 | flags |= scAllowZeroFrameRate; | ||
557 | SCSetInfo(ci,scPreferenceFlagsType,&flags); | ||
558 | } | ||
559 | |||
560 | if (error == noErr) | ||
561 | { | ||
562 | send_agent_pause(); | ||
563 | gViewerWindow->mWindow->beforeDialog(); | ||
564 | |||
565 | error = SCRequestSequenceSettings(ci); | ||
566 | |||
567 | gViewerWindow->mWindow->afterDialog(); | ||
568 | send_agent_resume(); | ||
569 | |||
570 | if (error == scUserCancelled) | ||
571 | { | ||
572 | // deal with user cancelling. | ||
573 | EndCapture(); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | if (error == noErr) | ||
578 | { | ||
579 | // This is stoopid. I have to take the passed full path, create the file so I can get an FSRef, and Get Info to get the FSSpec for QuickTime. Could Apple make this any more difficult... | ||
580 | FILE *file = LLFile::fopen(fname, "w"); | ||
581 | if (file) | ||
582 | { | ||
583 | fclose(file); | ||
584 | |||
585 | error = FSPathMakeRef((UInt8*)fname, &fileRef, NULL); | ||
586 | if (error == noErr) | ||
587 | error = FSGetCatalogInfo(&fileRef, 0, NULL, NULL, &fileSpec, NULL); | ||
588 | } | ||
589 | else | ||
590 | { | ||
591 | error = paramErr; | ||
592 | } | ||
593 | } | ||
594 | |||
595 | if (error == noErr) | ||
596 | { | ||
597 | error = CreateMovieFile(&fileSpec, 'TVOD', smCurrentScript, createMovieFileDeleteCurFile | createMovieFileDontCreateResFile, &movieResRef, &movie); | ||
598 | } | ||
599 | |||
600 | if (error == noErr) | ||
601 | { | ||
602 | track = NewMovieTrack(movie, FixRatio(width, 1), FixRatio(height, 1), kNoVolume); | ||
603 | error = GetMoviesError(); | ||
604 | } | ||
605 | |||
606 | if (error == noErr) | ||
607 | { | ||
608 | media = NewTrackMedia(track, VideoMediaType, 600, NULL, 0); | ||
609 | error = GetMoviesError(); | ||
610 | } | ||
611 | |||
612 | if (error == noErr) | ||
613 | { | ||
614 | Microseconds(&lastFrameTime); | ||
615 | error = grabFrame(); | ||
616 | } | ||
617 | |||
618 | if (error == noErr) | ||
619 | { | ||
620 | error = SCCompressSequenceBegin(ci,GetPortPixMap(gworld),nil,&idh); | ||
621 | } | ||
622 | |||
623 | if (error == noErr) | ||
624 | { | ||
625 | error = BeginMediaEdits(media); | ||
626 | } | ||
627 | |||
628 | if (error != noErr) | ||
629 | { | ||
630 | media = NULL; | ||
631 | } | ||
632 | |||
633 | return error; | ||
634 | } | ||
635 | |||
636 | void MovieMaker::EndCapture() | ||
637 | { | ||
638 | OSStatus error = noErr; | ||
639 | |||
640 | if (movie && movieResRef) | ||
641 | { | ||
642 | if (media && track) | ||
643 | { | ||
644 | // Errors adding the frame aren't too important here. | ||
645 | (void)addFrame(); | ||
646 | |||
647 | error = EndMediaEdits(media); | ||
648 | if (error == noErr) | ||
649 | { | ||
650 | error = SCCompressSequenceEnd(ci); | ||
651 | } | ||
652 | |||
653 | if (error == noErr) | ||
654 | { | ||
655 | error = InsertMediaIntoTrack(track, 0, 0, GetMediaDuration(media), fixed1); | ||
656 | } | ||
657 | media = NULL; | ||
658 | track = NULL; | ||
659 | } | ||
660 | |||
661 | short resId = movieInDataForkResID; | ||
662 | error = AddMovieResource(movie, movieResRef, &resId, "\pSecond Life"); | ||
663 | CloseMovieFile(movieResRef); | ||
664 | movieResRef = 0; | ||
665 | movie = NULL; | ||
666 | } | ||
667 | |||
668 | // NOTE: idh is disposed by SCCompressSequenceEnd. | ||
669 | idh = NULL; | ||
670 | |||
671 | if(ci) | ||
672 | { | ||
673 | CloseComponent(ci); | ||
674 | ci = NULL; | ||
675 | } | ||
676 | |||
677 | if(gworld) | ||
678 | { | ||
679 | DisposeGWorld(gworld); | ||
680 | gworld = NULL; | ||
681 | } | ||
682 | |||
683 | if(buffer) | ||
684 | { | ||
685 | free(buffer); | ||
686 | buffer = NULL; | ||
687 | } | ||
688 | |||
689 | if(invertedBuffer) | ||
690 | { | ||
691 | free(invertedBuffer); | ||
692 | invertedBuffer = NULL; | ||
693 | } | ||
694 | } | ||
695 | |||
696 | OSStatus MovieMaker::grabFrame() | ||
697 | { | ||
698 | OSStatus error = noErr; | ||
699 | GLenum glerr; | ||
700 | |||
701 | // Grab a frome from GL | ||
702 | glReadBuffer(GL_BACK); | ||
703 | glReadPixels(0 ,0, width, height, | ||
704 | #ifdef LL_BIG_ENDIAN | ||
705 | // PowerPC Mac | ||
706 | GL_BGRA, | ||
707 | GL_UNSIGNED_INT_8_8_8_8_REV, | ||
708 | #else | ||
709 | // Intel Mac | ||
710 | GL_BGRA, | ||
711 | GL_UNSIGNED_INT_8_8_8_8, | ||
712 | #endif | ||
713 | invertedBuffer); | ||
714 | glerr = glGetError(); | ||
715 | |||
716 | // Invert the lines top to bottom | ||
717 | if (glerr == GL_NO_ERROR) | ||
718 | { | ||
719 | long i, j; | ||
720 | |||
721 | i = j = 0; | ||
722 | |||
723 | // Copy rows into tmp buffer one at a time, reversing their order | ||
724 | for (i = 0, j = bufferSize - rowBytes; i < bufferSize; i += rowBytes, j -= rowBytes) | ||
725 | BlockMoveData(&invertedBuffer[i], &buffer[j], rowBytes); | ||
726 | } | ||
727 | else | ||
728 | { | ||
729 | error = paramErr; | ||
730 | } | ||
731 | |||
732 | return error; | ||
733 | } | ||
734 | |||
735 | OSStatus MovieMaker::addFrame() | ||
736 | { | ||
737 | OSStatus error = noErr; | ||
738 | Handle compressedData; | ||
739 | short syncFlag; | ||
740 | long dataSize; | ||
741 | UnsignedWide now; | ||
742 | |||
743 | CGrafPtr oldPort; | ||
744 | GDHandle oldGDeviceH; | ||
745 | |||
746 | GetGWorld(&oldPort, &oldGDeviceH); | ||
747 | SetGWorld(gworld, nil); | ||
748 | |||
749 | // Compress the frame and add it to the movie | ||
750 | |||
751 | error = SCCompressSequenceFrame(ci,GetPortPixMap(gworld),&rect,&compressedData,&dataSize,&syncFlag); | ||
752 | |||
753 | Microseconds(&now); | ||
754 | |||
755 | if (error == noErr) | ||
756 | { | ||
757 | double duration = (now.lo - lastFrameTime.lo); // duration in microseconds | ||
758 | duration *= GetMovieTimeScale(movie); | ||
759 | duration *= 1.0 / 1000000.0; | ||
760 | |||
761 | error = AddMediaSample( | ||
762 | media, | ||
763 | compressedData, | ||
764 | 0, | ||
765 | dataSize, | ||
766 | (TimeValue)duration, | ||
767 | (SampleDescriptionHandle)idh, | ||
768 | 1, | ||
769 | syncFlag, | ||
770 | nil); | ||
771 | |||
772 | } | ||
773 | |||
774 | lastFrameTime = now; | ||
775 | |||
776 | SetGWorld(oldPort, oldGDeviceH); | ||
777 | |||
778 | return error; | ||
779 | } | ||
780 | |||
781 | bool MovieMaker::Snap() | ||
782 | { | ||
783 | bool result = false; | ||
784 | |||
785 | if (movie && movieResRef && media && track) | ||
786 | { | ||
787 | OSStatus error = noErr; | ||
788 | |||
789 | error = addFrame(); | ||
790 | |||
791 | if (error == noErr) | ||
792 | { | ||
793 | error = grabFrame(); | ||
794 | } | ||
795 | |||
796 | if (error == noErr) | ||
797 | { | ||
798 | result = true; | ||
799 | } | ||
800 | } | ||
801 | |||
802 | return result; | ||
803 | } | ||
804 | |||
805 | #endif | ||
806 | |||