aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/moviemaker.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/newview/moviemaker.cpp
parentREADME.txt (diff)
downloadmeta-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.cpp806
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
43HANDLE MakeDib( HBITMAP hbitmap, UINT bits );
44HBITMAP LoadBMPFromFB( int w, int h );
45
46/*
47 ===============================================
48 Constructors, Destructor
49 ===============================================
50*/
51
52
53MovieMaker::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
82MovieMaker::~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
105void 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
121void 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
156bool 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
338static 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
401static 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
477MovieMaker::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
494MovieMaker::~MovieMaker()
495{
496 EndCapture();
497}
498
499void 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
508OSStatus 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
636void 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
696OSStatus 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
735OSStatus 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
781bool 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