00001
00032
00033
00034
00035
00036 #include "llviewerprecompiledheaders.h"
00037
00038 #include "moviemaker.h"
00039 #include <memory>
00040 #include "llmemtype.h"
00041
00042 #if LL_WINDOWS
00043
00044 #include <windowsx.h>
00045
00046 HANDLE MakeDib( HBITMAP hbitmap, UINT bits );
00047 HBITMAP LoadBMPFromFB( int w, int h );
00048
00049
00050
00051
00052
00053
00054
00055
00056 MovieMaker::MovieMaker()
00057 {
00058 snprintf( fname, sizeof(fname), "movie.avi" );
00059 width = -1;
00060 height = -1;
00061
00062 bOK = true;
00063 nFrames = 0;
00064
00065 pfile = NULL;
00066 ps = NULL;
00067 psCompressed = NULL;
00068 psText = NULL;
00069 aopts[0] = &opts;
00070
00071
00072 WORD wVer = HIWORD( VideoForWindowsVersion() );
00073 if ( wVer < 0x010A )
00074 {
00075 fprintf( stderr, "VFW version is too old.\n" );
00076 exit( -1 );
00077 }
00078 else
00079 {
00080 AVIFileInit();
00081 }
00082 }
00083
00084
00085 MovieMaker::~MovieMaker()
00086 {
00087 if (ps)
00088 AVIStreamClose(ps);
00089
00090 if (psCompressed)
00091 AVIStreamClose(psCompressed);
00092
00093 if (psText)
00094 AVIStreamClose(psText);
00095
00096 if (pfile)
00097 {
00098 AVIFileClose(pfile);
00099 }
00100
00101 WORD wVer = HIWORD(VideoForWindowsVersion());
00102 if (wVer >= 0x010A)
00103 {
00104 AVIFileExit();
00105 }
00106 }
00107
00108 void MovieMaker::StartCapture( char *name , int x, int y)
00109 {
00110 strncpy( fname, name, sizeof(fname) -1 );
00111 fname[sizeof(fname) -1] = '\0';
00112
00113
00114 width = x;
00115 height = y;
00116
00117 fprintf( stderr, "Starting %d x %d capture to file: %s\n", width, height, fname );
00118
00119 bOK = TRUE;
00120
00121 nFrames = 0;
00122
00123 }
00124
00125 void MovieMaker::EndCapture()
00126 {
00127 fprintf( stderr, "\n" );
00128 if (ps)
00129 {
00130 AVIStreamClose(ps);
00131 ps = NULL;
00132 }
00133
00134 if (psCompressed)
00135 {
00136 AVIStreamClose(psCompressed);
00137 psCompressed = NULL;
00138 }
00139
00140 if (psText)
00141 {
00142 AVIStreamClose(psText);
00143 psText = NULL;
00144 }
00145
00146 if (pfile)
00147 {
00148 AVIFileClose(pfile);
00149 pfile = NULL;
00150 }
00151
00152 WORD wVer = HIWORD(VideoForWindowsVersion());
00153 if (wVer >= 0x010A)
00154 {
00155 AVIFileExit();
00156 }
00157
00158 }
00159
00160 bool MovieMaker::Snap()
00161 {
00162 HRESULT hr;
00163
00164 if (!bOK)
00165 return false;
00166
00167
00168 HBITMAP bmp;
00169 bmp = LoadBMPFromFB( width, height );
00170
00171 LPBITMAPINFOHEADER alpbi = (LPBITMAPINFOHEADER)GlobalLock(MakeDib(bmp, 32));
00172 DeleteObject( bmp );
00173
00174 if (alpbi == NULL)
00175 {
00176 bOK = false;
00177 return false;
00178 }
00179 if (width>=0 && width != alpbi->biWidth)
00180 {
00181 GlobalFreePtr(alpbi);
00182 bOK = false;
00183 return false;
00184 }
00185 if (height>=0 && height != alpbi->biHeight)
00186 {
00187 GlobalFreePtr(alpbi);
00188 bOK = false;
00189 return false;
00190 }
00191 width = alpbi->biWidth;
00192 height = alpbi->biHeight;
00193 if (nFrames == 0)
00194 {
00195 hr = AVIFileOpenA(&pfile,
00196 fname,
00197 OF_WRITE | OF_CREATE,
00198 NULL);
00199
00200 if (hr != AVIERR_OK)
00201 {
00202 GlobalFreePtr(alpbi);
00203 bOK = false;
00204 return false;
00205 }
00206 _fmemset(&strhdr, 0, sizeof(strhdr));
00207 strhdr.fccType = streamtypeVIDEO;
00208 strhdr.fccHandler = 0;
00209 strhdr.dwScale = 1;
00210 strhdr.dwRate = 15;
00211 strhdr.dwSuggestedBufferSize = alpbi->biSizeImage;
00212 SetRect(&strhdr.rcFrame, 0, 0,
00213 (int) alpbi->biWidth,
00214 (int) alpbi->biHeight);
00215
00216
00217 hr = AVIFileCreateStream(pfile,
00218 &ps,
00219 &strhdr);
00220 if (hr != AVIERR_OK)
00221 {
00222 GlobalFreePtr(alpbi);
00223 bOK = false;
00224 return false;
00225 }
00226
00227 _fmemset(&opts, 0, sizeof(opts));
00228
00229 if (!AVISaveOptions(NULL, ICMF_CHOOSE_KEYFRAME, 1, &ps, (LPAVICOMPRESSOPTIONS FAR *) &aopts))
00230 {
00231 fprintf( stderr, "AVISaveOptions failed.\n" );
00232 GlobalFreePtr(alpbi);
00233 bOK = false;
00234 return false;
00235 }
00236
00237 hr = AVIMakeCompressedStream(&psCompressed, ps, &opts, NULL);
00238 if (hr != AVIERR_OK)
00239 {
00240 fprintf( stderr, "AVIMakeCompressedStream failed.\n" );
00241 GlobalFreePtr(alpbi);
00242 bOK = false;
00243 return false;
00244 }
00245
00246 hr = AVIStreamSetFormat(psCompressed, 0,
00247 alpbi,
00248 alpbi->biSize +
00249 alpbi->biClrUsed * sizeof(RGBQUAD));
00250 if (hr != AVIERR_OK)
00251 {
00252 fprintf( stderr, "AVIStreamSetFormat failed.\n" );
00253 GlobalFreePtr(alpbi);
00254 bOK = false;
00255 return false;
00256 }
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289 }
00290
00291
00292 hr = AVIStreamWrite(psCompressed,
00293 nFrames * 1,
00294 1,
00295 (LPBYTE) alpbi +
00296 alpbi->biSize +
00297 alpbi->biClrUsed * sizeof(RGBQUAD),
00298 alpbi->biSizeImage,
00299 AVIIF_KEYFRAME,
00300 NULL,
00301 NULL);
00302 if (hr != AVIERR_OK)
00303 {
00304 fprintf( stderr, "AVIStreamWrite failed.\n" );
00305 GlobalFreePtr(alpbi);
00306 bOK = false;
00307 return false;
00308 }
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333 GlobalFreePtr(alpbi);
00334
00335 nFrames++;
00336
00337 fprintf( stderr, "Wrote frame %d.\r", nFrames );
00338
00339 return true;
00340 }
00341
00342 static HANDLE MakeDib( HBITMAP hbitmap, UINT bits )
00343 {
00344 HANDLE hdib ;
00345 HDC hdc ;
00346 BITMAP bitmap ;
00347 UINT wLineLen ;
00348 DWORD dwSize ;
00349 DWORD wColSize ;
00350 LPBITMAPINFOHEADER lpbi ;
00351 LPBYTE lpBits ;
00352
00353 GetObject(hbitmap,sizeof(BITMAP),&bitmap) ;
00354
00355
00356
00357
00358
00359
00360 wLineLen = (bitmap.bmWidth*bits+31)/32 * 4;
00361 wColSize = sizeof(RGBQUAD)*((bits <= 8) ? 1<<bits : 0);
00362 dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
00363 (DWORD)(UINT)wLineLen*(DWORD)(UINT)bitmap.bmHeight;
00364
00365
00366
00367
00368 hdib = GlobalAlloc(GHND,dwSize);
00369 if (!hdib)
00370 return hdib ;
00371
00372 lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib) ;
00373
00374 lpbi->biSize = sizeof(BITMAPINFOHEADER) ;
00375 lpbi->biWidth = bitmap.bmWidth ;
00376 lpbi->biHeight = bitmap.bmHeight ;
00377 lpbi->biPlanes = 1 ;
00378 lpbi->biBitCount = (WORD) bits ;
00379 lpbi->biCompression = BI_RGB ;
00380 lpbi->biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize ;
00381 lpbi->biXPelsPerMeter = 0 ;
00382 lpbi->biYPelsPerMeter = 0 ;
00383 lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
00384 lpbi->biClrImportant = 0 ;
00385
00386
00387
00388
00389 lpBits = (LPBYTE)(lpbi+1)+wColSize ;
00390
00391 hdc = CreateCompatibleDC(NULL) ;
00392
00393 GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,lpBits,(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
00394
00395
00396 lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
00397
00398 DeleteDC(hdc) ;
00399 GlobalUnlock(hdib);
00400
00401 return hdib ;
00402 }
00403
00404
00405 static HBITMAP LoadBMPFromFB( int w, int h )
00406 {
00407
00408
00409
00410
00411
00412 HDC hdcScreen = wglGetCurrentDC();
00413 HDC hdcCompatible = CreateCompatibleDC(hdcScreen);
00414
00415
00416
00417 HBITMAP hbmScreen = CreateCompatibleBitmap(hdcScreen,
00418
00419
00420 w,
00421 h );
00422
00423 if (hbmScreen == 0)
00424 {
00425 fprintf( stderr, "hbmScreen == NULL\nExiting.\n" );
00426 exit( -1 );
00427
00428 }
00429
00430
00431
00432 if (!SelectObject(hdcCompatible, hbmScreen))
00433 {
00434 fprintf( stderr, "Couldn't SelectObject()\nExiting.\n" );
00435 exit( -1 );
00436
00437 }
00438
00439
00440
00441
00442
00443
00444
00445
00446 if (!BitBlt(hdcCompatible,
00447 0,0,
00448 w, h,
00449 hdcScreen,
00450
00451 0, 0,
00452 SRCCOPY))
00453 {
00454 fprintf( stderr, "Screen to Compat Blt Failed\nExiting.\n" );
00455 exit( -1 );
00456
00457 }
00458
00459
00460
00461
00462 DeleteDC( hdcCompatible );
00463
00464 return( hbmScreen );
00465 }
00466
00467 #elif LL_DARWIN
00468
00469 #include <AGL/agl.h>
00470 #include <AGL/gl.h>
00471 #include <AGL/glu.h>
00472
00473
00474 #ifdef verify
00475 #undef verify
00476 #endif
00477
00478 #include "llviewerwindow.h"
00479 #include "llworld.h"
00480
00481 MovieMaker::MovieMaker()
00482 {
00483 movie = NULL;
00484 movieResRef = 0;
00485 track = NULL;
00486 media = NULL;
00487 width = 0;
00488 height = 0;
00489 bufferSize = 0;
00490 rowBytes = 0;
00491 buffer = NULL;
00492 invertedBuffer = NULL;
00493 ci = NULL;
00494 gworld = NULL;
00495 idh = NULL;
00496 }
00497
00498 MovieMaker::~MovieMaker()
00499 {
00500 EndCapture();
00501 }
00502
00503 void MovieMaker::StartCapture( char *name , int x, int y)
00504 {
00505 strncpy( fname, name, sizeof(fname));
00506 width = x;
00507 height = y;
00508
00509 setupMovie();
00510 }
00511
00512 OSStatus MovieMaker::setupMovie()
00513 {
00514 OSStatus error = noErr;
00515 FSRef fileRef;
00516 FSSpec fileSpec;
00517
00518 rowBytes = width * 4;
00519 bufferSize = height * rowBytes;
00520 LLMemType mt(LLMemType::MTYPE_SCRIPT);
00521 buffer = (char*) new char(bufferSize);
00522 invertedBuffer = (char*) new char(bufferSize);
00523
00524 rect.left = 0;
00525 rect.top = 0;
00526 rect.right = width;
00527 rect.bottom = height;
00528
00529 error = NewGWorldFromPtr(&gworld, k32ARGBPixelFormat, &rect, 0, 0, 0, buffer, rowBytes);
00530
00531 if (error == noErr)
00532 {
00533 LockPixels(GetGWorldPixMap(gworld));
00534 }
00535
00536
00537
00538
00539
00540
00541
00542
00543 if (error == noErr)
00544 {
00545 error = EnterMovies();
00546 }
00547
00548 if (error == noErr)
00549 {
00550 ci = OpenDefaultComponent(StandardCompressionType,StandardCompressionSubType);
00551 if(ci == NULL)
00552 error = paramErr;
00553 }
00554
00555 if (error == noErr)
00556 {
00557 long flags;
00558
00559 SCGetInfo(ci,scPreferenceFlagsType,&flags);
00560 flags &= ~scShowBestDepth;
00561 flags |= scAllowZeroFrameRate;
00562 SCSetInfo(ci,scPreferenceFlagsType,&flags);
00563 }
00564
00565 if (error == noErr)
00566 {
00567 send_agent_pause();
00568 gViewerWindow->mWindow->beforeDialog();
00569
00570 error = SCRequestSequenceSettings(ci);
00571
00572 gViewerWindow->mWindow->afterDialog();
00573 send_agent_resume();
00574
00575 if (error == scUserCancelled)
00576 {
00577
00578 EndCapture();
00579 }
00580 }
00581
00582 if (error == noErr)
00583 {
00584
00585 FILE* file = LLFile::fopen(fname, "w");
00586 if (file)
00587 {
00588 fclose(file);
00589
00590 error = FSPathMakeRef((UInt8*)fname, &fileRef, NULL);
00591 if (error == noErr)
00592 error = FSGetCatalogInfo(&fileRef, 0, NULL, NULL, &fileSpec, NULL);
00593 }
00594 else
00595 {
00596 error = paramErr;
00597 }
00598 }
00599
00600 if (error == noErr)
00601 {
00602 error = CreateMovieFile(&fileSpec, 'TVOD', smCurrentScript, createMovieFileDeleteCurFile | createMovieFileDontCreateResFile, &movieResRef, &movie);
00603 }
00604
00605 if (error == noErr)
00606 {
00607 track = NewMovieTrack(movie, FixRatio(width, 1), FixRatio(height, 1), kNoVolume);
00608 error = GetMoviesError();
00609 }
00610
00611 if (error == noErr)
00612 {
00613 media = NewTrackMedia(track, VideoMediaType, 600, NULL, 0);
00614 error = GetMoviesError();
00615 }
00616
00617 if (error == noErr)
00618 {
00619 Microseconds(&lastFrameTime);
00620 error = grabFrame();
00621 }
00622
00623 if (error == noErr)
00624 {
00625 error = SCCompressSequenceBegin(ci,GetPortPixMap(gworld),nil,&idh);
00626 }
00627
00628 if (error == noErr)
00629 {
00630 error = BeginMediaEdits(media);
00631 }
00632
00633 if (error != noErr)
00634 {
00635 media = NULL;
00636 }
00637
00638 return error;
00639 }
00640
00641 void MovieMaker::EndCapture()
00642 {
00643 OSStatus error = noErr;
00644
00645 if (movie && movieResRef)
00646 {
00647 if (media && track)
00648 {
00649
00650 (void)addFrame();
00651
00652 error = EndMediaEdits(media);
00653 if (error == noErr)
00654 {
00655 error = SCCompressSequenceEnd(ci);
00656 }
00657
00658 if (error == noErr)
00659 {
00660 error = InsertMediaIntoTrack(track, 0, 0, GetMediaDuration(media), fixed1);
00661 }
00662 media = NULL;
00663 track = NULL;
00664 }
00665
00666 short resId = movieInDataForkResID;
00667 error = AddMovieResource(movie, movieResRef, &resId, "\pSecond Life");
00668 CloseMovieFile(movieResRef);
00669 movieResRef = 0;
00670 movie = NULL;
00671 }
00672
00673
00674 idh = NULL;
00675
00676 if(ci)
00677 {
00678 CloseComponent(ci);
00679 ci = NULL;
00680 }
00681
00682 if(gworld)
00683 {
00684 DisposeGWorld(gworld);
00685 gworld = NULL;
00686 }
00687
00688 if(buffer)
00689 {
00690 delete(buffer);
00691 buffer = NULL;
00692 }
00693
00694 if(invertedBuffer)
00695 {
00696 delete(invertedBuffer);
00697 invertedBuffer = NULL;
00698 }
00699 }
00700
00701 OSStatus MovieMaker::grabFrame()
00702 {
00703 OSStatus error = noErr;
00704 GLenum glerr;
00705
00706
00707 glReadBuffer(GL_BACK);
00708 glReadPixels(0 ,0, width, height,
00709 #ifdef LL_BIG_ENDIAN
00710
00711 GL_BGRA,
00712 GL_UNSIGNED_INT_8_8_8_8_REV,
00713 #else
00714
00715 GL_BGRA,
00716 GL_UNSIGNED_INT_8_8_8_8,
00717 #endif
00718 invertedBuffer);
00719 glerr = glGetError();
00720
00721
00722 if (glerr == GL_NO_ERROR)
00723 {
00724 long i, j;
00725
00726 i = j = 0;
00727
00728
00729 for (i = 0, j = bufferSize - rowBytes; i < bufferSize; i += rowBytes, j -= rowBytes)
00730 BlockMoveData(&invertedBuffer[i], &buffer[j], rowBytes);
00731 }
00732 else
00733 {
00734 error = paramErr;
00735 }
00736
00737 return error;
00738 }
00739
00740 OSStatus MovieMaker::addFrame()
00741 {
00742 OSStatus error = noErr;
00743 Handle compressedData;
00744 short syncFlag;
00745 long dataSize;
00746 UnsignedWide now;
00747
00748 CGrafPtr oldPort;
00749 GDHandle oldGDeviceH;
00750
00751 GetGWorld(&oldPort, &oldGDeviceH);
00752 SetGWorld(gworld, nil);
00753
00754
00755
00756 error = SCCompressSequenceFrame(ci,GetPortPixMap(gworld),&rect,&compressedData,&dataSize,&syncFlag);
00757
00758 Microseconds(&now);
00759
00760 if (error == noErr)
00761 {
00762 double duration = (now.lo - lastFrameTime.lo);
00763 duration *= GetMovieTimeScale(movie);
00764 duration *= 1.0 / 1000000.0;
00765
00766 error = AddMediaSample(
00767 media,
00768 compressedData,
00769 0,
00770 dataSize,
00771 (TimeValue)duration,
00772 (SampleDescriptionHandle)idh,
00773 1,
00774 syncFlag,
00775 nil);
00776
00777 }
00778
00779 lastFrameTime = now;
00780
00781 SetGWorld(oldPort, oldGDeviceH);
00782
00783 return error;
00784 }
00785
00786 bool MovieMaker::Snap()
00787 {
00788 bool result = false;
00789
00790 if (movie && movieResRef && media && track)
00791 {
00792 OSStatus error = noErr;
00793
00794 error = addFrame();
00795
00796 if (error == noErr)
00797 {
00798 error = grabFrame();
00799 }
00800
00801 if (error == noErr)
00802 {
00803 result = true;
00804 }
00805 }
00806
00807 return result;
00808 }
00809
00810 #endif
00811