Changeset 75251 in vbox for trunk/src/VBox/Main/src-client
- Timestamp:
- Nov 5, 2018 5:55:29 PM (6 years ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
r75167 r75251 402 402 , m_pVMMDev(NULL) 403 403 , mAudioVRDE(NULL) 404 #ifdef VBOX_WITH_AUDIO_VIDEOREC405 , mAudioVideoRec(NULL)406 #endif407 404 , mNvram(NULL) 408 405 #ifdef VBOX_WITH_USB_CARDREADER … … 590 587 #endif 591 588 #ifdef VBOX_WITH_AUDIO_VIDEOREC 592 unconst( mAudioVideoRec) = new AudioVideoRec(this);593 AssertReturn( mAudioVideoRec, E_FAIL);589 unconst(Capture.mAudioVideoRec) = new AudioVideoRec(this); 590 AssertReturn(Capture.mAudioVideoRec, E_FAIL); 594 591 #endif 595 592 FirmwareType_T enmFirmwareType; … … 738 735 739 736 #ifdef VBOX_WITH_AUDIO_VIDEOREC 740 if ( mAudioVideoRec)741 { 742 delete mAudioVideoRec;743 unconst( mAudioVideoRec) = NULL;737 if (Capture.mAudioVideoRec) 738 { 739 delete Capture.mAudioVideoRec; 740 unconst(Capture.mAudioVideoRec) = NULL; 744 741 } 745 742 #endif … … 5619 5616 if (pDisplay) 5620 5617 { 5621 if (RT_BOOL(fEnable) != pDisplay->i_videoRecStarted())5618 if (RT_BOOL(fEnable) != Capture.mpVideoRecCtx->IsStarted()) 5622 5619 { 5623 5620 LogRel(("VideoRec: %s\n", fEnable ? "Enabling" : "Disabling")); … … 5629 5626 # ifdef VBOX_WITH_AUDIO_VIDEOREC 5630 5627 /* Attach the video recording audio driver if required. */ 5631 if ( pDisplay->i_videoRecGetFeatures() & VIDEORECFEATURE_AUDIO5632 && mAudioVideoRec)5628 if ( Capture.mpVideoRecCtx->IsFeatureEnabled(CaptureFeature_Audio) 5629 && Capture.mAudioVideoRec) 5633 5630 { 5634 vrc = mAudioVideoRec->applyConfiguration(pDisplay->i_videoRecGetConfig());5631 vrc = Capture.mAudioVideoRec->applyConfiguration(Capture.mpVideoRecCtx->GetConfig()); 5635 5632 if (RT_SUCCESS(vrc)) 5636 vrc = mAudioVideoRec->doAttachDriverViaEmt(mpUVM, pAutoLock);5633 vrc = Capture.mAudioVideoRec->doAttachDriverViaEmt(mpUVM, pAutoLock); 5637 5634 } 5638 5635 # endif 5639 5636 if ( RT_SUCCESS(vrc) 5640 && pDisplay->i_videoRecGetFeatures()) /* Any video recording (audio and/or video) feature enabled? */5637 && Capture.mpVideoRecCtx->IsReady()) /* Any video recording (audio and/or video) feature enabled? */ 5641 5638 { 5642 vrc = pDisplay->i_videoRecStart();5639 vrc = i_videoRecStart(); 5643 5640 } 5644 5641 } 5645 5642 else 5646 5643 { 5647 mDisplay->i_videoRecStop();5644 i_videoRecStop(); 5648 5645 # ifdef VBOX_WITH_AUDIO_VIDEOREC 5649 mAudioVideoRec->doDetachDriverViaEmt(mpUVM, pAutoLock);5646 Capture.mAudioVideoRec->doDetachDriverViaEmt(mpUVM, pAutoLock); 5650 5647 # endif 5651 5648 } … … 5662 5659 #endif /* VBOX_WITH_VIDEOREC */ 5663 5660 5664 HRESULT Console::i_on VideoCaptureChange()5661 HRESULT Console::i_onCaptureChange() 5665 5662 { 5666 5663 AutoCaller autoCaller(this); … … 5675 5672 if (ptrVM.isOk()) 5676 5673 { 5674 ComPtr<ICaptureSettings> CaptureSettings; 5675 rc = mMachine->COMGETTER(CaptureSettings)(CaptureSettings.asOutParam()); 5676 AssertComRCReturnRC(rc); 5677 5677 5678 BOOL fEnabled; 5678 rc = mMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);5679 rc = CaptureSettings->COMGETTER(Enabled)(&fEnabled); 5679 5680 AssertComRCReturnRC(rc); 5680 5681 … … 5683 5684 { 5684 5685 alock.release(); 5685 fire VideoCaptureChangedEvent(mEventSource);5686 fireCaptureChangedEvent(mEventSource); 5686 5687 } 5687 5688 … … 6867 6868 * @param uTimestampMs Timestamp (in ms) of audio data. 6868 6869 */ 6869 HRESULT Console::i_audioVideoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs) 6870 { 6871 if (mDisplay) 6872 { 6873 int rc2 = mDisplay->i_videoRecSendAudio(pvData, cbData, uTimestampMs); 6874 AssertRC(rc2); 6870 HRESULT Console::i_videoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs) 6871 { 6872 if (!Capture.mpVideoRecCtx) 6873 return S_OK; 6874 6875 if ( Capture.mpVideoRecCtx->IsStarted() 6876 && Capture.mpVideoRecCtx->IsFeatureEnabled(CaptureFeature_Audio)) 6877 { 6878 return Capture.mpVideoRecCtx->SendAudioFrame(pvData, cbData, uTimestampMs); 6875 6879 } 6876 6880 … … 6878 6882 } 6879 6883 #endif /* VBOX_WITH_AUDIO_VIDEOREC */ 6884 6885 #ifdef VBOX_WITH_VIDEOREC 6886 int Console::i_videoRecLoad(settings::CaptureSettings &Settings) 6887 { 6888 Assert(mMachine.isNotNull()); 6889 6890 ComPtr<ICaptureSettings> pCaptureSettings; 6891 HRESULT hrc = mMachine->COMGETTER(CaptureSettings)(pCaptureSettings.asOutParam()); 6892 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6893 6894 SafeIfaceArray<ICaptureScreenSettings> paCaptureScreens; 6895 hrc = pCaptureSettings->GetScreens(ComSafeArrayAsOutParam(paCaptureScreens)); 6896 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6897 6898 Settings.mapScreens.clear(); 6899 6900 for (size_t i = 0; i < paCaptureScreens.size(); ++i) 6901 { 6902 settings::CaptureScreenSettings CaptureScreenSettings; 6903 ComPtr<ICaptureScreenSettings> pCaptureScreenSettings = paCaptureScreens[i]; 6904 6905 hrc = pCaptureScreenSettings->COMGETTER(MaxTime)((ULONG *)&CaptureScreenSettings.ulMaxTimeS); 6906 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6907 hrc = pCaptureScreenSettings->COMGETTER(MaxFileSize)((ULONG *)&CaptureScreenSettings.File.ulMaxSizeMB); 6908 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6909 Bstr bstrTemp; 6910 hrc = pCaptureScreenSettings->COMGETTER(FileName)(bstrTemp.asOutParam()); 6911 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6912 CaptureScreenSettings.File.strName = bstrTemp; 6913 hrc = pCaptureScreenSettings->COMGETTER(Options)(bstrTemp.asOutParam()); 6914 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6915 CaptureScreenSettings.strOptions = bstrTemp; 6916 hrc = pCaptureScreenSettings->COMGETTER(VideoWidth)((ULONG *)&CaptureScreenSettings.Video.ulWidth); 6917 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6918 hrc = pCaptureScreenSettings->COMGETTER(VideoHeight)((ULONG *)&CaptureScreenSettings.Video.ulHeight); 6919 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6920 hrc = pCaptureScreenSettings->COMGETTER(VideoRate)((ULONG *)&CaptureScreenSettings.Video.ulRate); 6921 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6922 hrc = pCaptureScreenSettings->COMGETTER(VideoFPS)((ULONG *)&CaptureScreenSettings.Video.ulFPS); 6923 AssertComRCReturn(hrc, VERR_INVALID_PARAMETER); 6924 6925 Settings.mapScreens[i] = CaptureScreenSettings; 6926 } 6927 6928 return VINF_SUCCESS; 6929 } 6930 6931 /** 6932 * Starts capturing. Does nothing if capturing is already active. 6933 * 6934 * @returns IPRT status code. 6935 */ 6936 int Console::i_videoRecStart(void) 6937 { 6938 if (Capture.mpVideoRecCtx && Capture.mpVideoRecCtx->IsStarted()) 6939 return VINF_SUCCESS; 6940 6941 LogRel(("VideoRec: Starting ...\n")); 6942 6943 try 6944 { 6945 Capture.mpVideoRecCtx = new CaptureContext(this); 6946 } 6947 catch (std::bad_alloc &) 6948 { 6949 return VERR_NO_MEMORY; 6950 } 6951 catch (int &rc) 6952 { 6953 return rc; 6954 } 6955 6956 settings::CaptureSettings Settings; 6957 int rc = i_videoRecLoad(Settings); 6958 if (RT_SUCCESS(rc)) 6959 { 6960 rc = Capture.mpVideoRecCtx->Create(Settings); 6961 if (RT_SUCCESS(rc)) 6962 { 6963 for (unsigned uScreen = 0; uScreen < Capture.mpVideoRecCtx->GetStreamCount(); uScreen++) 6964 mDisplay->i_videoRecScreenChanged(uScreen); 6965 } 6966 } 6967 6968 if (RT_FAILURE(rc)) 6969 LogRel(("VideoRec: Failed to start video recording (%Rrc)\n", rc)); 6970 6971 return rc; 6972 } 6973 6974 /** 6975 * Stops capturing. Does nothing if capturing is not active. 6976 */ 6977 int Console::i_videoRecStop(void) 6978 { 6979 if (Capture.mpVideoRecCtx && Capture.mpVideoRecCtx->IsStarted()) 6980 return VINF_SUCCESS; 6981 6982 LogRel(("VideoRec: Stopping ...\n")); 6983 6984 const size_t cStreams = Capture.mpVideoRecCtx->GetStreamCount(); 6985 6986 for (unsigned uScreen = 0; uScreen < cStreams; ++uScreen) 6987 mDisplay->i_videoRecScreenChanged(uScreen); 6988 6989 delete Capture.mpVideoRecCtx; 6990 Capture.mpVideoRecCtx = NULL; 6991 6992 ComPtr<ICaptureSettings> pCaptureSettings; 6993 HRESULT hrc = mMachine->COMGETTER(CaptureSettings)(pCaptureSettings.asOutParam()); 6994 ComAssertComRC(hrc); 6995 hrc = pCaptureSettings->COMSETTER(Enabled)(false); 6996 ComAssertComRC(hrc); 6997 6998 LogRel(("VideoRec: Stopped\n")); 6999 7000 return VINF_SUCCESS; 7001 } 7002 #endif /* VBOX_WITH_VIDEOREC */ 6880 7003 6881 7004 /** … … 9966 10089 9967 10090 #ifdef VBOX_WITH_VIDEOREC 9968 BOOL fVideoRecEnabled = FALSE;9969 rc = pConsole->mMachine->COMGETTER( VideoCaptureEnabled)(&fVideoRecEnabled);10091 ComPtr<ICaptureSettings> CaptureSettings; 10092 rc = pConsole->mMachine->COMGETTER(CaptureSettings)(CaptureSettings.asOutParam()); 9970 10093 AssertComRCReturnVoid(rc); 9971 10094 9972 if (fVideoRecEnabled) 9973 { 9974 int vrc2 = pConsole->i_videoCaptureEnable(fVideoRecEnabled, &alock); 10095 BOOL fCaptureEnabled; 10096 rc = CaptureSettings->COMGETTER(Enabled)(&fCaptureEnabled); 10097 AssertComRCReturnVoid(rc); 10098 10099 if (fCaptureEnabled) 10100 { 10101 int vrc2 = pConsole->i_videoCaptureEnable(fCaptureEnabled, &alock); 9975 10102 if (RT_SUCCESS(vrc2)) 9976 10103 { 9977 fire VideoCaptureChangedEvent(pConsole->mEventSource);10104 fireCaptureChangedEvent(pConsole->mEventSource); 9978 10105 } 9979 10106 else -
trunk/src/VBox/Main/src-client/ConsoleImpl2.cpp
r74474 r75251 2998 2998 InsertConfigString(pLunL0, "Driver", "AUDIO"); 2999 2999 AudioDriverCfg DrvCfgVideoRec(strAudioDevice, 0 /* Instance */, uAudioLUN, "AudioVideoRec"); 3000 rc = mAudioVideoRec->InitializeConfig(&DrvCfgVideoRec);3000 rc = Capture.mAudioVideoRec->InitializeConfig(&DrvCfgVideoRec); 3001 3001 if (RT_SUCCESS(rc)) 3002 3002 uAudioLUN++; -
trunk/src/VBox/Main/src-client/DisplayImpl.cpp
r75067 r75251 150 150 AssertRC(rc); 151 151 152 mpVideoRecCtx = NULL;153 152 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++) 154 153 maVideoRecEnabled[i] = true; … … 2408 2407 #ifdef VBOX_WITH_VIDEOREC 2409 2408 /** 2410 * Returns the currently enabled video capturing features.2409 * Invalidates the capturing configuration. 2411 2410 * 2412 * @returns Enables video capturing features.2411 * @returns IPRT status code. 2413 2412 */ 2414 VIDEORECFEATURES Display::i_videoRecGetFeatures(void) 2415 { 2416 return VideoRecGetFeatures(&mVideoRecCfg); 2417 } 2418 2419 /** 2420 * Returns whether video capturing is currently is active or not. 2421 * 2422 * @returns True if video capturing is active, false if not. 2423 */ 2424 bool Display::i_videoRecStarted(void) 2425 { 2426 return VideoRecIsStarted(mpVideoRecCtx); 2427 } 2428 2429 /** 2430 * Invalidates the video recording configuration. 2431 */ 2432 void Display::i_videoRecInvalidate(void) 2433 { 2434 AssertPtr(mParent); 2435 ComPtr<IMachine> pMachine = mParent->i_machine(); 2436 Assert(pMachine.isNotNull()); 2437 2438 mVideoRecCfg.enmDst = VIDEORECDEST_FILE; /** @todo Make this configurable once we have more variations. */ 2439 2440 /* 2441 * Cache parameters from API. 2442 */ 2443 com::SafeArray<BOOL> aScreens; 2444 HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(aScreens)); 2445 AssertComRCReturnVoid(hrc); 2446 2447 mVideoRecCfg.aScreens.resize(aScreens.size()); 2448 for (size_t i = 0; i < aScreens.size(); ++i) 2449 mVideoRecCfg.aScreens[i] = aScreens[i]; 2450 2451 hrc = pMachine->COMGETTER(VideoCaptureWidth)((ULONG *)&mVideoRecCfg.Video.uWidth); 2452 AssertComRCReturnVoid(hrc); 2453 hrc = pMachine->COMGETTER(VideoCaptureHeight)((ULONG *)&mVideoRecCfg.Video.uHeight); 2454 AssertComRCReturnVoid(hrc); 2455 hrc = pMachine->COMGETTER(VideoCaptureRate)((ULONG *)&mVideoRecCfg.Video.uRate); 2456 AssertComRCReturnVoid(hrc); 2457 hrc = pMachine->COMGETTER(VideoCaptureFPS)((ULONG *)&mVideoRecCfg.Video.uFPS); 2458 AssertComRCReturnVoid(hrc); 2459 hrc = pMachine->COMGETTER(VideoCaptureFile)(mVideoRecCfg.File.strName.asOutParam()); 2460 AssertComRCReturnVoid(hrc); 2461 hrc = pMachine->COMGETTER(VideoCaptureMaxFileSize)((ULONG *)&mVideoRecCfg.File.uMaxSizeMB); 2462 AssertComRCReturnVoid(hrc); 2463 hrc = pMachine->COMGETTER(VideoCaptureMaxTime)((ULONG *)&mVideoRecCfg.uMaxTimeS); 2464 AssertComRCReturnVoid(hrc); 2465 BSTR bstrOptions; 2466 hrc = pMachine->COMGETTER(VideoCaptureOptions)(&bstrOptions); 2467 AssertComRCReturnVoid(hrc); 2468 2469 /* 2470 * Set sensible defaults. 2471 */ 2472 mVideoRecCfg.Video.fEnabled = true; /* Enabled by default. */ 2473 2474 if (!mVideoRecCfg.Video.uFPS) /* Prevent division by zero. */ 2475 mVideoRecCfg.Video.uFPS = 15; 2476 2477 #ifdef VBOX_WITH_LIBVPX 2478 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS; 2479 #endif 2480 2481 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2482 mVideoRecCfg.Audio.fEnabled = false; /* Disabled by default, unless set otherwise below. */ 2483 /* By default we use 22,5kHz, 16-bit, stereo for the audio track. */ 2484 mVideoRecCfg.Audio.uHz = 22050; 2485 mVideoRecCfg.Audio.cBits = 16; 2486 mVideoRecCfg.Audio.cChannels = 2; 2487 #endif 2488 2489 /* 2490 * Parse options string. 2491 */ 2492 com::Utf8Str strOptions(bstrOptions); 2493 size_t pos = 0; 2494 com::Utf8Str key, value; 2495 while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos) 2496 { 2497 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0) 2498 { 2499 #ifdef VBOX_WITH_LIBVPX 2500 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0) 2501 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME; 2502 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0) 2503 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS; 2504 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0) 2505 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY; 2506 else 2507 { 2508 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str())); 2509 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = value.toUInt32(); 2510 #endif 2511 } 2512 } 2513 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0) 2514 { 2515 if (value.compare("false", Utf8Str::CaseInsensitive) == 0) 2516 { 2517 mVideoRecCfg.Video.fEnabled = false; 2518 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2519 LogRel(("VideoRec: Only audio will be recorded\n")); 2520 #endif 2521 } 2522 } 2523 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0) 2524 { 2525 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2526 if (value.compare("true", Utf8Str::CaseInsensitive) == 0) 2527 { 2528 mVideoRecCfg.Audio.fEnabled = true; 2529 2530 } 2531 else 2532 LogRel(("VideoRec: Only video will be recorded\n")); 2533 #endif 2534 } 2535 else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0) 2536 { 2537 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2538 if (value.compare("low", Utf8Str::CaseInsensitive) == 0) 2539 { 2540 mVideoRecCfg.Audio.uHz = 8000; 2541 mVideoRecCfg.Audio.cBits = 16; 2542 mVideoRecCfg.Audio.cChannels = 1; 2543 } 2544 else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0) 2545 { 2546 /* Stay with the default set above. */ 2547 } 2548 else if (value.compare("high", Utf8Str::CaseInsensitive) == 0) 2549 { 2550 mVideoRecCfg.Audio.uHz = 48000; 2551 mVideoRecCfg.Audio.cBits = 16; 2552 mVideoRecCfg.Audio.cChannels = 2; 2553 } 2554 #endif 2555 } 2556 else 2557 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str())); 2558 2559 } /* while */ 2413 int Display::i_videoRecInvalidate(void) 2414 { 2415 CaptureContext *pCtx = mParent->i_videoRecGetContext(); 2416 if (!pCtx || !pCtx->IsStarted()) 2417 return VINF_SUCCESS; 2560 2418 2561 2419 /* 2562 2420 * Invalidate screens. 2563 2421 */ 2564 for (unsigned i = 0; i < mVideoRecCfg.aScreens.size(); i++) 2565 { 2566 bool fChanged = maVideoRecEnabled[i] != RT_BOOL(mVideoRecCfg.aScreens[i]); 2567 2568 maVideoRecEnabled[i] = RT_BOOL(mVideoRecCfg.aScreens[i]); 2569 2570 if (fChanged && i < mcMonitors) 2571 i_videoRecScreenChanged(i); 2572 2573 } 2574 } 2575 2576 /** 2577 * Sends belonging audio samples to the video capturing code. 2578 * Does nothing if capturing is disabled or if audio support for video capturing is disabled. 2579 * 2580 * @returns IPRT status code. 2581 * @param pvData Audio data. 2582 * @param cbData Size (in bytes) of audio data. 2583 * @param uTimestampMs Timestamp (in ms) of the audio data. 2584 */ 2585 int Display::i_videoRecSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs) 2586 { 2587 if ( VideoRecIsStarted(mpVideoRecCtx) 2588 && VideoRecGetFeatures(&mVideoRecCfg) & VIDEORECFEATURE_AUDIO) 2589 { 2590 return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs); 2422 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++) 2423 { 2424 CaptureStream *pCaptureStream = pCtx->GetStream(uScreen); 2425 2426 const bool fStreamEnabled = pCaptureStream->IsReady(); 2427 bool fChanged = maVideoRecEnabled[uScreen] != fStreamEnabled; 2428 2429 maVideoRecEnabled[uScreen] = fStreamEnabled; 2430 2431 if (fChanged && uScreen < mcMonitors) 2432 i_videoRecScreenChanged(uScreen); 2591 2433 } 2592 2434 … … 2594 2436 } 2595 2437 2596 /**2597 * Start video capturing. Does nothing if capturing is already active.2598 *2599 * @param pVideoRecCfg Video recording configuration to use.2600 * @returns IPRT status code.2601 */2602 int Display::i_videoRecStart(void)2603 {2604 if (VideoRecIsStarted(mpVideoRecCtx))2605 return VINF_SUCCESS;2606 2607 LogRel(("VideoRec: Starting ...\n"));2608 2609 int rc = VideoRecContextCreate(mcMonitors, &mVideoRecCfg, &mpVideoRecCtx);2610 if (RT_SUCCESS(rc))2611 {2612 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++)2613 {2614 int rc2 = VideoRecStreamInit(VideoRecGetStream(mpVideoRecCtx, uScreen), mpVideoRecCtx, uScreen);2615 if (RT_SUCCESS(rc2))2616 {2617 i_videoRecScreenChanged(uScreen);2618 }2619 else2620 LogRel(("VideoRec: Failed to initialize video recording context #%u (%Rrc)\n", uScreen, rc2));2621 2622 if (RT_SUCCESS(rc))2623 rc = rc2;2624 }2625 }2626 2627 if (RT_FAILURE(rc))2628 LogRel(("VideoRec: Failed to start video recording (%Rrc)\n", rc));2629 2630 return rc;2631 }2632 2633 /**2634 * Stops video capturing. Does nothing if video capturing is not active.2635 */2636 void Display::i_videoRecStop(void)2637 {2638 if (!VideoRecIsStarted(mpVideoRecCtx))2639 return;2640 2641 LogRel(("VideoRec: Stopping ...\n"));2642 2643 VideoRecContextDestroy(mpVideoRecCtx);2644 mpVideoRecCtx = NULL;2645 2646 unsigned uScreenId;2647 for (uScreenId = 0; uScreenId < mcMonitors; ++uScreenId)2648 i_videoRecScreenChanged(uScreenId);2649 }2650 2651 2438 void Display::i_videoRecScreenChanged(unsigned uScreenId) 2652 2439 { 2653 if ( !VideoRecIsStarted(mpVideoRecCtx) 2654 || !maVideoRecEnabled[uScreenId]) 2440 CaptureContext *pCtx = mParent->i_videoRecGetContext(); 2441 2442 if ( RT_LIKELY(!maVideoRecEnabled[uScreenId]) 2443 || !pCtx || !pCtx->IsStarted()) 2655 2444 { 2656 2445 /* Skip recording this screen. */ … … 3599 3388 3600 3389 #ifdef VBOX_WITH_VIDEOREC 3601 if ( VideoRecIsStarted(pDisplay->mpVideoRecCtx) 3602 && VideoRecGetFeatures(&pDisplay->mVideoRecCfg) & VIDEORECFEATURE_VIDEO) 3390 AssertPtr(pDisplay->mParent); 3391 CaptureContext *pCtx = pDisplay->mParent->i_videoRecGetContext(); 3392 3393 if ( pCtx 3394 && pCtx->IsStarted() 3395 && pCtx->IsFeatureEnabled(CaptureFeature_Video)) 3603 3396 { 3604 3397 do { … … 3638 3431 # endif /* VBOX_WITH_HGCM && VBOX_WITH_CROGL */ 3639 3432 3640 uint64_t u64Now= RTTimeProgramMilliTS();3433 uint64_t tsNowMs = RTTimeProgramMilliTS(); 3641 3434 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++) 3642 3435 { … … 3644 3437 continue; 3645 3438 3646 if ( VideoRecIsLimitReached(pDisplay->mpVideoRecCtx, uScreenId, u64Now))3439 if (pCtx->IsLimitReached(uScreenId, tsNowMs)) 3647 3440 { 3648 pDisplay->i_videoRecStop(); 3649 pDisplay->mParent->i_machine()->COMSETTER(VideoCaptureEnabled)(false); 3441 pDisplay->mParent->i_videoRecStop(); 3650 3442 break; 3651 3443 } … … 3677 3469 &bitmapFormat); 3678 3470 if (SUCCEEDED(hr) && pbAddress) 3679 rc = VideoRecSendVideoFrame(pDisplay->mpVideoRecCtx, uScreenId, 0, 0, 3680 BitmapFormat_BGR, 3681 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight, 3682 pbAddress, u64Now); 3471 rc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR, 3472 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight, 3473 pbAddress, tsNowMs); 3683 3474 else 3684 3475 rc = VERR_NOT_SUPPORTED; … … 4039 3830 } 4040 3831 4041 bool Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t u64Timestamp) 4042 { 4043 /** @todo r=bird: u64Timestamp - using the 'u64' prefix add nothing. 4044 * However, using one of the prefixes indicating the timestamp unit 4045 * would be very valuable! */ 3832 bool Display::i_handleCrVRecScreenshotBegin(uint32_t uScreen, uint64_t uTimestampMs) 3833 { 4046 3834 # ifdef VBOX_WITH_VIDEOREC 4047 return VideoRecIsReady(mpVideoRecCtx, uScreen, u64Timestamp); 3835 CaptureContext *pCtx = mParent->i_videoRecGetContext(); 3836 return ( pCtx 3837 && pCtx->IsReady(uScreen, uTimestampMs)); 4048 3838 # else 4049 RT_NOREF(uScreen, u 64Timestamp);3839 RT_NOREF(uScreen, uTimestampMs); 4050 3840 return false; 4051 3841 # endif 4052 3842 } 4053 3843 4054 void Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t u 64Timestamp)4055 { 4056 RT_NOREF(uScreen, u 64Timestamp);3844 void Display::i_handleCrVRecScreenshotEnd(uint32_t uScreen, uint64_t uTimestampMs) 3845 { 3846 RT_NOREF(uScreen, uTimestampMs); 4057 3847 } 4058 3848 … … 4061 3851 uint32_t uBitsPerPixel, uint32_t uBytesPerLine, 4062 3852 uint32_t uGuestWidth, uint32_t uGuestHeight, 4063 uint8_t *pu8BufferAddress, uint64_t u 64Timestamp)3853 uint8_t *pu8BufferAddress, uint64_t uTimestampMs) 4064 3854 { 4065 3855 Assert(mfCrOglVideoRecState == CRVREC_STATE_SUBMITTED); 4066 3856 # ifdef VBOX_WITH_VIDEOREC 4067 if ( VideoRecIsStarted(mpVideoRecCtx) 4068 && VideoRecGetFeatures(&mVideoRecCfg) & VIDEORECFEATURE_VIDEO) 4069 { 4070 int rc2 = VideoRecSendVideoFrame(mpVideoRecCtx, uScreen, x, y, 4071 uPixelFormat, 4072 uBitsPerPixel, uBytesPerLine, 4073 uGuestWidth, uGuestHeight, 4074 pu8BufferAddress, u64Timestamp); 3857 CaptureContext *pCtx = mParent->i_videoRecGetContext(); 3858 3859 if ( pCtx 3860 && pCtx->IsStarted() 3861 && pCtx->IsFeatureEnabled(CaptureFeature_Video)) 3862 { 3863 int rc2 = pCtx->SendVideoFrame(uScreen, x, y, 3864 uPixelFormat, 3865 uBitsPerPixel, uBytesPerLine, 3866 uGuestWidth, uGuestHeight, 3867 pu8BufferAddress, uTimestampMs); 4075 3868 RT_NOREF(rc2); 4076 3869 Assert(rc2 == VINF_SUCCESS /* || rc == VERR_TRY_AGAIN || rc == VINF_TRY_AGAIN*/); … … 4078 3871 # else 4079 3872 RT_NOREF(uScreen, x, y, uPixelFormat, \ 4080 uBitsPerPixel, uBytesPerLine, uGuestWidth, uGuestHeight, pu8BufferAddress, u 64Timestamp);3873 uBitsPerPixel, uBytesPerLine, uGuestWidth, uGuestHeight, pu8BufferAddress, uTimestampMs); 4081 3874 # endif /* VBOX_WITH_VIDEOREC */ 4082 3875 } … … 4706 4499 AutoWriteLock displayLock(pThis->pDisplay COMMA_LOCKVAL_SRC_POS); 4707 4500 #ifdef VBOX_WITH_VIDEOREC 4708 pThis->pDisplay-> i_videoRecStop();4501 pThis->pDisplay->mParent->i_videoRecStop(); 4709 4502 #endif 4710 4503 #ifdef VBOX_WITH_CRHGSMI -
trunk/src/VBox/Main/src-client/DrvAudioVideoRec.cpp
r75083 r75251 828 828 case AVRECCONTAINERTYPE_MAIN_CONSOLE: 829 829 { 830 HRESULT hr = pSink->Con.Main.pConsole->i_ audioVideoRecSendAudio(pStreamAV->pvDstBuf, cbDst, uPTSMs);830 HRESULT hr = pSink->Con.Main.pConsole->i_videoRecSendAudio(pStreamAV->pvDstBuf, cbDst, uPTSMs); 831 831 Assert(hr == S_OK); 832 832 RT_NOREF(hr); … … 1086 1086 * 1087 1087 * @returns IPRT status code. 1088 * @param pVideoRecCfg Pointer to video recording configuration to apply.1089 */ 1090 int AudioVideoRec::applyConfiguration(const PVIDEORECCFG pVideoRecCfg)1088 * @param Settings Capturing configuration to apply. 1089 */ 1090 int AudioVideoRec::applyConfiguration(const settings::CaptureSettings &a_Settings) 1091 1091 { 1092 1092 /** @todo Do some validation here. */ 1093 mVideoRecCfg = *pVideoRecCfg; /* Note: Does have an own copy operator. */1093 mVideoRecCfg = a_Settings; /* Note: Does have an own copy operator. */ 1094 1094 return VINF_SUCCESS; 1095 1095 } … … 1101 1101 int AudioVideoRec::configureDriver(PCFGMNODE pLunCfg) 1102 1102 { 1103 int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)mpConsole->i_ getAudioVideoRec());1103 int rc = CFGMR3InsertInteger(pLunCfg, "Object", (uintptr_t)mpConsole->i_videoRecGetAudioDrv()); 1104 1104 AssertRCReturn(rc, rc); 1105 1105 rc = CFGMR3InsertInteger(pLunCfg, "ObjectConsole", (uintptr_t)mpConsole); 1106 1106 AssertRCReturn(rc, rc); 1107 1107 1108 rc = CFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)mVideoRecCfg.enmDst); 1108 /** @todo For now we're using the configuration of the first screen here audio-wise. */ 1109 Assert(mVideoRecCfg.mapScreens.size() >= 1); 1110 const settings::CaptureScreenSettings &Screen0Settings = mVideoRecCfg.mapScreens[0]; 1111 1112 rc = CFGMR3InsertInteger(pLunCfg, "ContainerType", (uint64_t)Screen0Settings.enmDest); 1109 1113 AssertRCReturn(rc, rc); 1110 if ( mVideoRecCfg.enmDst == VIDEORECDEST_FILE)1111 { 1112 rc = CFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str( mVideoRecCfg.File.strName).c_str());1114 if (Screen0Settings.enmDest == CaptureDestination_File) 1115 { 1116 rc = CFGMR3InsertString(pLunCfg, "ContainerFileName", Utf8Str(Screen0Settings.File.strName).c_str()); 1113 1117 AssertRCReturn(rc, rc); 1114 1118 } 1115 rc = CFGMR3InsertInteger(pLunCfg, "CodecHz", mVideoRecCfg.Audio.uHz);1119 rc = CFGMR3InsertInteger(pLunCfg, "CodecHz", Screen0Settings.Audio.uHz); 1116 1120 AssertRCReturn(rc, rc); 1117 rc = CFGMR3InsertInteger(pLunCfg, "CodecBits", mVideoRecCfg.Audio.cBits);1121 rc = CFGMR3InsertInteger(pLunCfg, "CodecBits", Screen0Settings.Audio.cBits); 1118 1122 AssertRCReturn(rc, rc); 1119 rc = CFGMR3InsertInteger(pLunCfg, "CodecChannels", mVideoRecCfg.Audio.cChannels);1123 rc = CFGMR3InsertInteger(pLunCfg, "CodecChannels", Screen0Settings.Audio.cChannels); 1120 1124 AssertRCReturn(rc, rc); 1121 1125 rc = CFGMR3InsertInteger(pLunCfg, "CodecBitrate", 0); /* Let Opus decide for now. */ -
trunk/src/VBox/Main/src-client/SessionImpl.cpp
r68986 r75251 725 725 } 726 726 727 HRESULT Session::on VideoCaptureChange()728 { 729 LogFlowThisFunc(("\n")); 730 731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 732 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 733 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 734 #ifndef VBOX_COM_INPROC_API_CLIENT 735 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 736 737 return mConsole->i_on VideoCaptureChange();727 HRESULT Session::onCaptureChange() 728 { 729 LogFlowThisFunc(("\n")); 730 731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 732 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 733 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 734 #ifndef VBOX_COM_INPROC_API_CLIENT 735 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 736 737 return mConsole->i_onCaptureChange(); 738 738 #else 739 739 return S_OK; -
trunk/src/VBox/Main/src-client/VideoRec.cpp
r75068 r75251 43 43 #include <VBox/com/VirtualBox.h> 44 44 45 #include " WebMWriter.h"45 #include "ConsoleImpl.h" 46 46 #include "VideoRec.h" 47 47 #include "VideoRecInternals.h" 48 48 #include "VideoRecStream.h" 49 49 #include "VideoRecUtils.h" 50 #include "WebMWriter.h" 50 51 51 52 using namespace com; … … 103 104 104 105 106 CaptureContext::CaptureContext(Console *a_pConsole) 107 : pConsole(a_pConsole) { } 108 109 CaptureContext::CaptureContext(Console *a_pConsole, const settings::CaptureSettings &a_Settings) 110 : pConsole(a_pConsole) 111 { 112 int rc = CaptureContext::createInternal(a_Settings); 113 if (RT_FAILURE(rc)) 114 throw rc; 115 } 116 117 CaptureContext::~CaptureContext(void) 118 { 119 destroyInternal(); 120 } 121 105 122 /** 106 123 * Worker thread for all streams of a video recording context. … … 108 125 * For video frames, this also does the RGB/YUV conversion and encoding. 109 126 */ 110 static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser)111 { 112 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)pvUser;127 DECLCALLBACK(int) CaptureContext::threadMain(RTTHREAD hThreadSelf, void *pvUser) 128 { 129 CaptureContext *pThis = (CaptureContext *)pvUser; 113 130 114 131 /* Signal that we're up and rockin'. */ … … 119 136 for (;;) 120 137 { 121 int rc = RTSemEventWait(p Ctx->WaitEvent, RT_INDEFINITE_WAIT);138 int rc = RTSemEventWait(pThis->WaitEvent, RT_INDEFINITE_WAIT); 122 139 AssertRCBreak(rc); 123 140 124 Log2Func(("Processing %zu streams\n", p Ctx->vecStreams.size()));141 Log2Func(("Processing %zu streams\n", pThis->vecStreams.size())); 125 142 126 143 /** @todo r=andy This is inefficient -- as we already wake up this thread 127 144 * for every screen from Main, we here go again (on every wake up) through 128 145 * all screens. */ 129 VideoRecStreams::iterator itStream = p Ctx->vecStreams.begin();130 while (itStream != p Ctx->vecStreams.end())131 { 132 PVIDEORECSTREAMpStream = (*itStream);133 134 rc = VideoRecStreamProcess(pStream);146 VideoRecStreams::iterator itStream = pThis->vecStreams.begin(); 147 while (itStream != pThis->vecStreams.end()) 148 { 149 CaptureStream *pStream = (*itStream); 150 151 rc = pStream->Process(pThis->mapBlocksCommon); 135 152 if (RT_FAILURE(rc)) 136 153 break; … … 144 161 /* Keep going in case of errors. */ 145 162 146 if (ASMAtomicReadBool(&p Ctx->fShutdown))163 if (ASMAtomicReadBool(&pThis->fShutdown)) 147 164 { 148 165 LogFunc(("Thread is shutting down ...\n")); … … 160 177 * 161 178 * @returns IPRT status code. 162 * @param pCtx Video recording context to notify thread for. 163 */ 164 static int videoRecThreadNotify(PVIDEORECCONTEXT pCtx) 165 { 166 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 167 168 return RTSemEventSignal(pCtx->WaitEvent); 179 */ 180 int CaptureContext::threadNotify(void) 181 { 182 return RTSemEventSignal(this->WaitEvent); 169 183 } 170 184 … … 175 189 * @param cScreens Number of screens to create context for. 176 190 * @param pVideoRecCfg Pointer to video recording configuration to use. 177 * @param ppCtx Pointer to created video recording context on success. 178 */ 179 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx) 180 { 181 AssertReturn(cScreens, VERR_INVALID_PARAMETER); 182 AssertPtrReturn(pVideoRecCfg, VERR_INVALID_POINTER); 183 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER); 184 185 VIDEORECCONTEXT *pCtx = NULL; 186 try 187 { 188 pCtx = new VIDEORECCONTEXT(); 189 } 190 catch (std::bad_alloc &) 191 { 192 return VERR_NO_MEMORY; 193 } 194 195 int rc = RTCritSectInit(&pCtx->CritSect); 191 */ 192 int CaptureContext::createInternal(const settings::CaptureSettings &a_Settings) 193 { 194 int rc = RTCritSectInit(&this->CritSect); 196 195 if (RT_FAILURE(rc)) 197 {198 delete pCtx;199 196 return rc; 200 } 201 202 for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++)203 { 204 VIDEORECSTREAM*pStream = NULL;197 198 settings::CaptureScreenMap::const_iterator itScreen = a_Settings.mapScreens.begin(); 199 while (itScreen != a_Settings.mapScreens.end()) 200 { 201 CaptureStream *pStream = NULL; 205 202 try 206 203 { 207 pStream = new VIDEORECSTREAM(); 204 pStream = new CaptureStream(itScreen->first /* Screen ID */, itScreen->second); 205 this->vecStreams.push_back(pStream); 208 206 } 209 207 catch (std::bad_alloc &) … … 212 210 break; 213 211 } 214 215 rc = RTCritSectInit(&pStream->CritSect);216 if (RT_FAILURE(rc))217 break;218 219 try220 {221 pStream->uScreenID = uScreen;222 223 pCtx->vecStreams.push_back(pStream);224 225 pStream->File.pWEBM = new WebMWriter();226 }227 catch (std::bad_alloc &)228 {229 rc = VERR_NO_MEMORY;230 break;231 }232 212 } 233 213 234 214 if (RT_SUCCESS(rc)) 235 215 { 236 pCtx->tsStartMs = RTTimeMilliTS();237 pCtx->enmState = VIDEORECSTS_UNINITIALIZED;238 pCtx->fStarted = false;239 pCtx->fShutdown = false;240 241 /* Copy the configurationto our context. */242 pCtx->Cfg = *pVideoRecCfg;243 244 rc = RTSemEventCreate(& pCtx->WaitEvent);216 this->tsStartMs = RTTimeMilliTS(); 217 this->enmState = VIDEORECSTS_UNINITIALIZED; 218 this->fStarted = false; 219 this->fShutdown = false; 220 221 /* Copy the settings to our context. */ 222 this->Settings = a_Settings; 223 224 rc = RTSemEventCreate(&this->WaitEvent); 245 225 AssertRCReturn(rc, rc); 246 226 247 rc = RTThreadCreate(& pCtx->Thread, videoRecThread, (void *)pCtx, 0,227 rc = RTThreadCreate(&this->Thread, CaptureContext::threadMain, (void *)this, 0, 248 228 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "VideoRec"); 249 229 250 230 if (RT_SUCCESS(rc)) /* Wait for the thread to start. */ 251 rc = RTThreadUserWait( pCtx->Thread, 30 * RT_MS_1SEC /* 30s timeout */);231 rc = RTThreadUserWait(this->Thread, 30 * RT_MS_1SEC /* 30s timeout */); 252 232 253 233 if (RT_SUCCESS(rc)) 254 234 { 255 pCtx->enmState = VIDEORECSTS_INITIALIZED; 256 pCtx->fStarted = true; 257 258 if (ppCtx) 259 *ppCtx = pCtx; 235 this->enmState = VIDEORECSTS_INITIALIZED; 236 this->fStarted = true; 260 237 } 261 238 } … … 263 240 if (RT_FAILURE(rc)) 264 241 { 265 int rc2 = VideoRecContextDestroy(pCtx);242 int rc2 = destroyInternal(); 266 243 AssertRC(rc2); 267 244 } … … 272 249 /** 273 250 * Destroys a video recording context. 274 * 275 * @param pCtx Video recording context to destroy. 276 */ 277 int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx) 278 { 279 if (!pCtx) 280 return VINF_SUCCESS; 281 251 */ 252 int CaptureContext::destroyInternal(void) 253 { 282 254 int rc = VINF_SUCCESS; 283 255 284 if ( pCtx->enmState == VIDEORECSTS_INITIALIZED)256 if (this->enmState == VIDEORECSTS_INITIALIZED) 285 257 { 286 258 LogFunc(("Shutting down thread ...\n")); 287 259 288 260 /* Set shutdown indicator. */ 289 ASMAtomicWriteBool(& pCtx->fShutdown, true);261 ASMAtomicWriteBool(&this->fShutdown, true); 290 262 291 263 /* Signal the thread and wait for it to shut down. */ 292 rc = videoRecThreadNotify(pCtx);264 rc = threadNotify(); 293 265 if (RT_SUCCESS(rc)) 294 rc = RTThreadWait( pCtx->Thread, 30 * 1000 /* 10s timeout */, NULL);266 rc = RTThreadWait(this->Thread, 30 * 1000 /* 10s timeout */, NULL); 295 267 296 268 if (RT_SUCCESS(rc)) 297 269 { 298 270 /* Disable the context. */ 299 ASMAtomicWriteBool(& pCtx->fStarted, false);300 301 int rc2 = RTSemEventDestroy( pCtx->WaitEvent);271 ASMAtomicWriteBool(&this->fStarted, false); 272 273 int rc2 = RTSemEventDestroy(this->WaitEvent); 302 274 AssertRC(rc2); 303 275 304 pCtx->WaitEvent = NIL_RTSEMEVENT;276 this->WaitEvent = NIL_RTSEMEVENT; 305 277 } 306 278 } … … 312 284 } 313 285 314 rc = RTCritSectEnter(& pCtx->CritSect);286 rc = RTCritSectEnter(&this->CritSect); 315 287 if (RT_SUCCESS(rc)) 316 288 { 317 VideoRecStreams::iterator it = pCtx->vecStreams.begin(); 318 while (it != pCtx->vecStreams.end()) 319 { 320 PVIDEORECSTREAM pStream = (*it); 321 322 VideoRecStreamLock(pStream); 323 324 int rc2 = VideoRecStreamClose(pStream); 289 VideoRecStreams::iterator it = this->vecStreams.begin(); 290 while (it != this->vecStreams.end()) 291 { 292 CaptureStream *pStream = (*it); 293 294 int rc2 = pStream->Uninit(); 325 295 if (RT_SUCCESS(rc)) 326 296 rc = rc2; 327 297 328 rc2 = VideoRecStreamUninit(pStream);329 if (RT_SUCCESS(rc))330 rc = rc2;331 332 pCtx->vecStreams.erase(it);333 it = pCtx->vecStreams.begin();334 335 VideoRecStreamUnlock(pStream);336 337 RTCritSectDelete(&pStream->CritSect);338 339 298 delete pStream; 340 299 pStream = NULL; 300 301 this->vecStreams.erase(it); 302 it = this->vecStreams.begin(); 303 304 delete pStream; 305 pStream = NULL; 341 306 } 342 307 343 308 /* Sanity. */ 344 Assert( pCtx->vecStreams.empty());345 Assert( pCtx->mapBlocksCommon.size() == 0);346 347 int rc2 = RTCritSectLeave(& pCtx->CritSect);309 Assert(this->vecStreams.empty()); 310 Assert(this->mapBlocksCommon.size() == 0); 311 312 int rc2 = RTCritSectLeave(&this->CritSect); 348 313 AssertRC(rc2); 349 314 350 RTCritSectDelete(&pCtx->CritSect); 351 352 delete pCtx; 353 pCtx = NULL; 315 RTCritSectDelete(&this->CritSect); 354 316 } 355 317 … … 357 319 } 358 320 359 /** 360 * Returns which recording features currently are enabled for a given configuration. 361 * 362 * @returns Enabled video recording features. 363 * @param pCfg Pointer to recording configuration. 364 */ 365 VIDEORECFEATURES VideoRecGetFeatures(PVIDEORECCFG pCfg) 366 { 367 if (!pCfg) 368 return VIDEORECFEATURE_NONE; 369 370 VIDEORECFEATURES fFeatures = VIDEORECFEATURE_NONE; 371 372 if (pCfg->Video.fEnabled) 373 fFeatures |= VIDEORECFEATURE_VIDEO; 374 375 #ifdef VBOX_WITH_AUDIO_VIDEOREC 376 if (pCfg->Audio.fEnabled) 377 fFeatures |= VIDEORECFEATURE_AUDIO; 378 #endif 379 380 return fFeatures; 321 const settings::CaptureSettings &CaptureContext::GetConfig(void) const 322 { 323 return this->Settings; 324 } 325 326 CaptureStream *CaptureContext::getStreamInternal(unsigned uScreen) const 327 { 328 CaptureStream *pStream; 329 330 try 331 { 332 pStream = this->vecStreams.at(uScreen); 333 } 334 catch (std::out_of_range &) 335 { 336 pStream = NULL; 337 } 338 339 return pStream; 381 340 } 382 341 … … 385 344 * 386 345 * @returns Pointer to recording stream if found, or NULL if not found. 387 * @param pCtx Recording context to look up stream for.388 346 * @param uScreen Screen number of recording stream to look up. 389 347 */ 390 PVIDEORECSTREAM VideoRecGetStream(PVIDEORECCONTEXT pCtx, uint32_t uScreen) 391 { 392 AssertPtrReturn(pCtx, NULL); 393 394 PVIDEORECSTREAM pStream; 395 396 try 397 { 398 pStream = pCtx->vecStreams.at(uScreen); 399 } 400 catch (std::out_of_range &) 401 { 402 pStream = NULL; 403 } 404 405 return pStream; 348 CaptureStream *CaptureContext::GetStream(unsigned uScreen) const 349 { 350 return getStreamInternal(uScreen); 351 } 352 353 size_t CaptureContext::GetStreamCount(void) const 354 { 355 return this->vecStreams.size(); 356 } 357 358 int CaptureContext::Create(const settings::CaptureSettings &a_Settings) 359 { 360 return createInternal(a_Settings); 361 } 362 363 int CaptureContext::Destroy(void) 364 { 365 return destroyInternal(); 366 } 367 368 bool CaptureContext::IsFeatureEnabled(CaptureFeature_T enmFeature) const 369 { 370 VideoRecStreams::const_iterator itStream = this->vecStreams.begin(); 371 while (itStream != this->vecStreams.end()) 372 { 373 if ((*itStream)->GetConfig().isFeatureEnabled(enmFeature)) 374 return true; 375 ++itStream; 376 } 377 378 return false; 379 } 380 381 bool CaptureContext::IsReady(void) const 382 { 383 return this->fStarted; 406 384 } 407 385 … … 410 388 * 411 389 * @returns true if recording engine is ready, false if not. 412 * @param pCtx Pointer to video recording context.413 390 * @param uScreen Screen ID. 414 391 * @param uTimeStampMs Current time stamp (in ms). Currently not being used. 415 392 */ 416 bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t uTimeStampMs) 417 { 418 AssertPtrReturn(pCtx, false); 393 bool CaptureContext::IsReady(uint32_t uScreen, uint64_t uTimeStampMs) const 394 { 419 395 RT_NOREF(uTimeStampMs); 420 396 421 if ( ASMAtomicReadU32(&pCtx->enmState)!= VIDEORECSTS_INITIALIZED)397 if (this->enmState != VIDEORECSTS_INITIALIZED) 422 398 return false; 423 399 424 400 bool fIsReady = false; 425 401 426 PVIDEORECSTREAM pStream = VideoRecGetStream(pCtx,uScreen);402 const CaptureStream *pStream = GetStream(uScreen); 427 403 if (pStream) 428 { 429 VideoRecStreamLock(pStream); 430 fIsReady = pStream->fEnabled; 431 VideoRecStreamUnlock(pStream); 432 } 404 fIsReady = pStream->IsReady(); 433 405 434 406 /* Note: Do not check for other constraints like the video FPS rate here, … … 443 415 * 444 416 * @returns true if active, false if not. 445 * @param pCtx Pointer to video recording context. 446 */ 447 bool VideoRecIsStarted(PVIDEORECCONTEXT pCtx) 448 { 449 if (!pCtx) 450 return false; 451 452 return ASMAtomicReadBool(&pCtx->fStarted); 417 */ 418 bool CaptureContext::IsStarted(void) const 419 { 420 return this->fStarted; 453 421 } 454 422 … … 457 425 * 458 426 * @returns true if any limit has been reached. 459 * @param pCtx Pointer to video recording context.460 427 * @param uScreen Screen ID. 461 428 * @param tsNowMs Current time stamp (in ms). 462 429 */ 463 bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs)464 { 465 PVIDEORECSTREAM pStream = VideoRecGetStream(pCtx,uScreen);430 bool CaptureContext::IsLimitReached(uint32_t uScreen, uint64_t tsNowMs) const 431 { 432 const CaptureStream *pStream = GetStream(uScreen); 466 433 if ( !pStream 467 || !pStream->fEnabled) 468 { 469 return false; 470 } 471 472 const PVIDEORECCFG pCfg = &pCtx->Cfg; 473 474 if ( pCfg->uMaxTimeS 475 && tsNowMs >= pCtx->tsStartMs + (pCfg->uMaxTimeS * RT_MS_1SEC)) 434 || pStream->IsLimitReached(tsNowMs)) 476 435 { 477 436 return true; 478 437 } 479 438 480 if (pCfg->enmDst == VIDEORECDEST_FILE)481 {482 483 if (pCfg->File.uMaxSizeMB)484 {485 uint64_t sizeInMB = pStream->File.pWEBM->GetFileSize() / _1M;486 if(sizeInMB >= pCfg->File.uMaxSizeMB)487 return true;488 }489 490 /* Check for available free disk space */491 if ( pStream->File.pWEBM492 && pStream->File.pWEBM->GetAvailableSpace() < 0x100000) /** @todo r=andy WTF? Fix this. */493 {494 LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n"));495 return true;496 }497 }498 499 439 return false; 500 440 } … … 506 446 * 507 447 * @returns IPRT status code. 508 * @param pCtx Pointer to the video recording context.509 448 * @param pvData Audio frame data to send. 510 449 * @param cbData Size (in bytes) of (encoded) audio frame data. 511 450 * @param uTimeStampMs Time stamp (in ms) of audio playback. 512 451 */ 513 int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx,const void *pvData, size_t cbData, uint64_t uTimeStampMs)452 int CaptureContext::SendAudioFrame(const void *pvData, size_t cbData, uint64_t uTimeStampMs) 514 453 { 515 454 #ifdef VBOX_WITH_AUDIO_VIDEOREC … … 537 476 pBlock->pvData = pFrame; 538 477 pBlock->cbData = sizeof(VIDEORECAUDIOFRAME) + cbData; 539 pBlock->cRefs = (uint16_t) pCtx->vecStreams.size(); /* All streams need the same audio data. */478 pBlock->cRefs = (uint16_t)this->vecStreams.size(); /* All streams need the same audio data. */ 540 479 pBlock->uTimeStampMs = uTimeStampMs; 541 480 542 int rc = RTCritSectEnter(& pCtx->CritSect);481 int rc = RTCritSectEnter(&this->CritSect); 543 482 if (RT_FAILURE(rc)) 544 483 return rc; … … 546 485 try 547 486 { 548 VideoRecBlockMap::iterator itBlocks = pCtx->mapBlocksCommon.find(uTimeStampMs);549 if (itBlocks == pCtx->mapBlocksCommon.end())550 { 551 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks();487 VideoRecBlockMap::iterator itBlocks = this->mapBlocksCommon.find(uTimeStampMs); 488 if (itBlocks == this->mapBlocksCommon.end()) 489 { 490 CaptureBlocks *pVideoRecBlocks = new CaptureBlocks(); 552 491 pVideoRecBlocks->List.push_back(pBlock); 553 492 554 pCtx->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks));493 this->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks)); 555 494 } 556 495 else … … 563 502 } 564 503 565 int rc2 = RTCritSectLeave(& pCtx->CritSect);504 int rc2 = RTCritSectLeave(&this->CritSect); 566 505 AssertRC(rc2); 567 506 568 507 if (RT_SUCCESS(rc)) 569 rc = videoRecThreadNotify(pCtx);508 rc = threadNotify(); 570 509 571 510 return rc; … … 583 522 * 584 523 * @returns IPRT status code. 585 * @param pCtx Pointer to the video recording context. 586 * @param uScreen Screen number. 524 * @param uScreen Screen number to send video frame to. 587 525 * @param x Starting x coordinate of the video frame. 588 526 * @param y Starting y coordinate of the video frame. … … 595 533 * @param uTimeStampMs Time stamp (in ms). 596 534 */ 597 int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint32_t x, uint32_t y, 598 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine, 599 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, 600 uint64_t uTimeStampMs) 601 { 602 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 535 int CaptureContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y, 536 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine, 537 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, 538 uint64_t uTimeStampMs) 539 { 603 540 AssertReturn(uSrcWidth, VERR_INVALID_PARAMETER); 604 541 AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER); 605 542 AssertReturn(puSrcData, VERR_INVALID_POINTER); 606 543 607 int rc = RTCritSectEnter(& pCtx->CritSect);544 int rc = RTCritSectEnter(&this->CritSect); 608 545 AssertRC(rc); 609 546 610 PVIDEORECSTREAM pStream = VideoRecGetStream(pCtx,uScreen);547 CaptureStream *pStream = GetStream(uScreen); 611 548 if (!pStream) 612 549 { 613 rc = RTCritSectLeave(& pCtx->CritSect);550 rc = RTCritSectLeave(&this->CritSect); 614 551 AssertRC(rc); 615 552 … … 617 554 } 618 555 619 VideoRecStreamLock(pStream); 620 621 PVIDEORECVIDEOFRAME pFrame = NULL; 622 623 do 624 { 625 if (!pStream->fEnabled) 626 { 627 rc = VINF_TRY_AGAIN; /* Not (yet) enabled. */ 628 break; 629 } 630 631 if (uTimeStampMs < pStream->Video.uLastTimeStampMs + pStream->Video.uDelayMs) 632 { 633 rc = VINF_TRY_AGAIN; /* Respect maximum frames per second. */ 634 break; 635 } 636 637 pStream->Video.uLastTimeStampMs = uTimeStampMs; 638 639 int xDiff = ((int)pStream->Video.uWidth - (int)uSrcWidth) / 2; 640 uint32_t w = uSrcWidth; 641 if ((int)w + xDiff + (int)x <= 0) /* Nothing visible. */ 642 { 643 rc = VERR_INVALID_PARAMETER; 644 break; 645 } 646 647 uint32_t destX; 648 if ((int)x < -xDiff) 649 { 650 w += xDiff + x; 651 x = -xDiff; 652 destX = 0; 653 } 654 else 655 destX = x + xDiff; 656 657 uint32_t h = uSrcHeight; 658 int yDiff = ((int)pStream->Video.uHeight - (int)uSrcHeight) / 2; 659 if ((int)h + yDiff + (int)y <= 0) /* Nothing visible. */ 660 { 661 rc = VERR_INVALID_PARAMETER; 662 break; 663 } 664 665 uint32_t destY; 666 if ((int)y < -yDiff) 667 { 668 h += yDiff + (int)y; 669 y = -yDiff; 670 destY = 0; 671 } 672 else 673 destY = y + yDiff; 674 675 if ( destX > pStream->Video.uWidth 676 || destY > pStream->Video.uHeight) 677 { 678 rc = VERR_INVALID_PARAMETER; /* Nothing visible. */ 679 break; 680 } 681 682 if (destX + w > pStream->Video.uWidth) 683 w = pStream->Video.uWidth - destX; 684 685 if (destY + h > pStream->Video.uHeight) 686 h = pStream->Video.uHeight - destY; 687 688 pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME)); 689 AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY); 690 691 /* Calculate bytes per pixel and set pixel format. */ 692 const unsigned uBytesPerPixel = uBPP / 8; 693 if (uPixelFormat == BitmapFormat_BGR) 694 { 695 switch (uBPP) 696 { 697 case 32: 698 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB32; 699 break; 700 case 24: 701 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB24; 702 break; 703 case 16: 704 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB565; 705 break; 706 default: 707 AssertMsgFailed(("Unknown color depth (%RU32)\n", uBPP)); 708 break; 709 } 710 } 711 else 712 AssertMsgFailed(("Unknown pixel format (%RU32)\n", uPixelFormat)); 713 714 const size_t cbRGBBuf = pStream->Video.uWidth 715 * pStream->Video.uHeight 716 * uBytesPerPixel; 717 AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER); 718 719 pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf); 720 AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY); 721 pFrame->cbRGBBuf = cbRGBBuf; 722 pFrame->uWidth = uSrcWidth; 723 pFrame->uHeight = uSrcHeight; 724 725 /* If the current video frame is smaller than video resolution we're going to encode, 726 * clear the frame beforehand to prevent artifacts. */ 727 if ( uSrcWidth < pStream->Video.uWidth 728 || uSrcHeight < pStream->Video.uHeight) 729 { 730 RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf); 731 } 732 733 /* Calculate start offset in source and destination buffers. */ 734 uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel; 735 uint32_t offDst = (destY * pStream->Video.uWidth + destX) * uBytesPerPixel; 736 737 #ifdef VBOX_VIDEOREC_DUMP 738 VIDEORECBMPHDR bmpHdr; 739 RT_ZERO(bmpHdr); 740 741 VIDEORECBMPDIBHDR bmpDIBHdr; 742 RT_ZERO(bmpDIBHdr); 743 744 bmpHdr.u16Magic = 0x4d42; /* Magic */ 745 bmpHdr.u32Size = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR) + (w * h * uBytesPerPixel)); 746 bmpHdr.u32OffBits = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR)); 747 748 bmpDIBHdr.u32Size = sizeof(VIDEORECBMPDIBHDR); 749 bmpDIBHdr.u32Width = w; 750 bmpDIBHdr.u32Height = h; 751 bmpDIBHdr.u16Planes = 1; 752 bmpDIBHdr.u16BitCount = uBPP; 753 bmpDIBHdr.u32XPelsPerMeter = 5000; 754 bmpDIBHdr.u32YPelsPerMeter = 5000; 755 756 char szFileName[RTPATH_MAX]; 757 RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", uScreen); 758 759 RTFILE fh; 760 int rc2 = RTFileOpen(&fh, szFileName, 761 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 762 if (RT_SUCCESS(rc2)) 763 { 764 RTFileWrite(fh, &bmpHdr, sizeof(bmpHdr), NULL); 765 RTFileWrite(fh, &bmpDIBHdr, sizeof(bmpDIBHdr), NULL); 766 } 767 #endif 768 Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel); 769 770 /* Do the copy. */ 771 for (unsigned int i = 0; i < h; i++) 772 { 773 /* Overflow check. */ 774 Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine); 775 Assert(offDst + w * uBytesPerPixel <= pStream->Video.uHeight * pStream->Video.uWidth * uBytesPerPixel); 776 777 memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel); 778 779 #ifdef VBOX_VIDEOREC_DUMP 780 if (RT_SUCCESS(rc2)) 781 RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL); 782 #endif 783 offSrc += uBytesPerLine; 784 offDst += pStream->Video.uWidth * uBytesPerPixel; 785 } 786 787 #ifdef VBOX_VIDEOREC_DUMP 788 if (RT_SUCCESS(rc2)) 789 RTFileClose(fh); 790 #endif 791 792 } while (0); 793 794 if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */ 795 { 796 PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK)); 797 if (pBlock) 798 { 799 AssertPtr(pFrame); 800 801 pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO; 802 pBlock->pvData = pFrame; 803 pBlock->cbData = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf; 804 805 try 806 { 807 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks(); 808 pVideoRecBlocks->List.push_back(pBlock); 809 810 Assert(pStream->Blocks.Map.find(uTimeStampMs) == pStream->Blocks.Map.end()); 811 pStream->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks)); 812 } 813 catch (const std::exception &ex) 814 { 815 RT_NOREF(ex); 816 817 RTMemFree(pBlock); 818 rc = VERR_NO_MEMORY; 819 } 820 } 821 else 822 rc = VERR_NO_MEMORY; 823 } 824 825 if (RT_FAILURE(rc)) 826 VideoRecVideoFrameFree(pFrame); 827 828 VideoRecStreamUnlock(pStream); 829 830 int rc2 = RTCritSectLeave(&pCtx->CritSect); 556 rc = pStream->SendVideoFrame(x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, uTimeStampMs); 557 558 int rc2 = RTCritSectLeave(&this->CritSect); 831 559 AssertRC(rc2); 832 560 … … 834 562 && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */ 835 563 { 836 videoRecThreadNotify(pCtx);564 threadNotify(); 837 565 } 838 566 -
trunk/src/VBox/Main/src-client/VideoRecStream.cpp
r75069 r75251 42 42 43 43 44 /** 45 * Locks a recording stream. 46 * 47 * @param pStream Recording stream to lock. 48 */ 49 void VideoRecStreamLock(PVIDEORECSTREAM pStream) 50 { 51 int rc = RTCritSectEnter(&pStream->CritSect); 52 AssertRC(rc); 53 } 54 55 /** 56 * Unlocks a locked recording stream. 57 * 58 * @param pStream Recording stream to unlock. 59 */ 60 void VideoRecStreamUnlock(PVIDEORECSTREAM pStream) 61 { 62 int rc = RTCritSectLeave(&pStream->CritSect); 63 AssertRC(rc); 44 CaptureStream::CaptureStream(void) 45 : tsStartMs(0) 46 { 47 } 48 49 CaptureStream::CaptureStream(uint32_t a_uScreen, const settings::CaptureScreenSettings &a_Settings) 50 : tsStartMs(0) 51 { 52 int rc2 = initInternal(a_uScreen, a_Settings); 53 if (RT_FAILURE(rc2)) 54 throw rc2; 55 } 56 57 CaptureStream::~CaptureStream(void) 58 { 59 int rc2 = uninitInternal(); 60 AssertRC(rc2); 64 61 } 65 62 … … 68 65 * 69 66 * @returns IPRT status code. 70 * @param pStream Recording stream to open. 71 * @param pCfg Recording configuration to use. 72 */ 73 int VideoRecStreamOpen(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 74 { 75 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 76 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 77 78 Assert(pStream->enmDst == VIDEORECDEST_INVALID); 67 */ 68 int CaptureStream::open(void) 69 { 70 Assert(Settings.enmDest == CaptureDestination_None); 79 71 80 72 int rc; 81 73 82 switch ( pCfg->enmDst)83 { 84 case VIDEORECDEST_FILE:85 { 86 Assert( pCfg->File.strName.isNotEmpty());87 88 char *pszAbsPath = RTPathAbsDup( com::Utf8Str(pCfg->File.strName).c_str());74 switch (Settings.enmDest) 75 { 76 case CaptureDestination_File: 77 { 78 Assert(Settings.File.strName.isNotEmpty()); 79 80 char *pszAbsPath = RTPathAbsDup(Settings.File.strName.c_str()); 89 81 AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY); 90 82 … … 101 93 char *pszFile = NULL; 102 94 103 if ( pCfg->aScreens.size() > 1)104 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff);95 if (this->uScreenID > 0) 96 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, this->uScreenID + 1, pszSuff); 105 97 else 106 98 rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff); … … 127 119 RTTimeExplode(&time, &ts); 128 120 129 if ( pCfg->aScreens.size() > 1)121 if (this->uScreenID > 0) 130 122 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s", 131 123 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 132 124 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 133 pStream->uScreenID + 1, pszSuff);125 this->uScreenID + 1, pszSuff); 134 126 else 135 127 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s", … … 144 136 if (RT_SUCCESS(rc)) 145 137 { 146 pStream->enmDst = VIDEORECDEST_FILE; 147 pStream->File.hFile = hFile; 148 pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */ 138 this->File.hFile = hFile; 139 this->File.strName = pszFile; 149 140 } 150 141 } … … 156 147 { 157 148 LogRel(("VideoRec: Failed to open file '%s' for screen %RU32, rc=%Rrc\n", 158 pszFile ? pszFile : "<Unnamed>", pStream->uScreenID, rc));159 RTStrFree(pszFile);160 } 161 149 pszFile ? pszFile : "<Unnamed>", this->uScreenID, rc)); 150 } 151 152 RTStrFree(pszFile); 162 153 break; 163 154 } … … 170 161 LogFlowFuncLeaveRC(rc); 171 162 return rc; 163 } 164 165 int CaptureStream::parseOptionsString(const com::Utf8Str &strOptions) 166 { 167 size_t pos = 0; 168 com::Utf8Str key, value; 169 while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos) 170 { 171 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0) 172 { 173 #ifdef VBOX_WITH_LIBVPX 174 Assert(this->Settings.Video.ulFPS); 175 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0) 176 this->Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME; 177 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0) 178 this->Video.Codec.VPX.uEncoderDeadline = 1000000 / this->Settings.Video.ulFPS; 179 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0) 180 this->Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY; 181 else 182 { 183 LogRel(("VideoRec: Setting encoder deadline to '%s'\n", value.c_str())); 184 this->Video.Codec.VPX.uEncoderDeadline = value.toUInt32(); 185 #endif 186 } 187 } 188 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0) 189 { 190 if (value.compare("false", Utf8Str::CaseInsensitive) == 0) 191 { 192 this->Settings.featureMap[CaptureFeature_Video] = false; 193 #ifdef VBOX_WITH_AUDIO_VIDEOREC 194 LogRel(("VideoRec: Only audio will be recorded\n")); 195 #endif 196 } 197 } 198 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0) 199 { 200 #ifdef VBOX_WITH_AUDIO_VIDEOREC 201 if (value.compare("true", Utf8Str::CaseInsensitive) == 0) 202 { 203 this->Settings.featureMap[CaptureFeature_Audio] = true; 204 } 205 else 206 LogRel(("VideoRec: Only video will be recorded\n")); 207 #endif 208 } 209 else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0) 210 { 211 #ifdef VBOX_WITH_AUDIO_VIDEOREC 212 if (value.compare("low", Utf8Str::CaseInsensitive) == 0) 213 { 214 this->Settings.Audio.uHz = 8000; 215 this->Settings.Audio.cBits = 16; 216 this->Settings.Audio.cChannels = 1; 217 } 218 else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0) 219 { 220 /* Stay with the default set above. */ 221 } 222 else if (value.compare("high", Utf8Str::CaseInsensitive) == 0) 223 { 224 this->Settings.Audio.uHz = 48000; 225 this->Settings.Audio.cBits = 16; 226 this->Settings.Audio.cChannels = 2; 227 } 228 #endif 229 } 230 else 231 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str())); 232 233 } /* while */ 234 235 return VINF_SUCCESS; 236 } 237 238 const settings::CaptureScreenSettings &CaptureStream::GetConfig(void) const 239 { 240 return this->Settings; 241 } 242 243 /** 244 * Checks if a specified limit for recording has been reached. 245 * 246 * @returns true if any limit has been reached. 247 * @param tsNowMs Current time stamp (in ms). 248 */ 249 bool CaptureStream::IsLimitReached(uint64_t tsNowMs) const 250 { 251 if (!IsReady()) 252 return true; 253 254 if ( Settings.ulMaxTimeS 255 && tsNowMs >= this->tsStartMs + (Settings.ulMaxTimeS * RT_MS_1SEC)) 256 { 257 return true; 258 } 259 260 if (Settings.enmDest == CaptureDestination_File) 261 { 262 263 if (Settings.File.ulMaxSizeMB) 264 { 265 uint64_t sizeInMB = this->File.pWEBM->GetFileSize() / _1M; 266 if(sizeInMB >= Settings.File.ulMaxSizeMB) 267 return true; 268 } 269 270 /* Check for available free disk space */ 271 if ( this->File.pWEBM 272 && this->File.pWEBM->GetAvailableSpace() < 0x100000) /** @todo r=andy WTF? Fix this. */ 273 { 274 LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n")); 275 return true; 276 } 277 } 278 279 return false; 280 } 281 282 bool CaptureStream::IsReady(void) const 283 { 284 return this->fEnabled; 172 285 } 173 286 … … 178 291 * 179 292 * @returns IPRT status code. 180 * @param pStream Recording stream to process. 181 */ 182 int VideoRecStreamProcess(PVIDEORECSTREAM pStream) 183 { 184 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 185 186 VideoRecStreamLock(pStream); 187 188 if (!pStream->fEnabled) 189 { 190 VideoRecStreamUnlock(pStream); 293 * @param mapBlocksCommon Map of common block to process for this stream. 294 */ 295 int CaptureStream::Process(VideoRecBlockMap &mapBlocksCommon) 296 { 297 lock(); 298 299 if (!Settings.fEnabled) 300 { 301 unlock(); 191 302 return VINF_SUCCESS; 192 303 } … … 194 305 int rc = VINF_SUCCESS; 195 306 196 const PVIDEORECCONTEXT pCtx = pStream->pCtx; 197 AssertPtr(pCtx); 198 199 VideoRecBlockMap::iterator itStreamBlocks = pStream->Blocks.Map.begin(); 200 while (itStreamBlocks != pStream->Blocks.Map.end()) 307 VideoRecBlockMap::iterator itStreamBlocks = Blocks.Map.begin(); 308 while (itStreamBlocks != Blocks.Map.end()) 201 309 { 202 310 const uint64_t uTimeStampMs = itStreamBlocks->first; 203 VideoRecBlocks *pBlocks= itStreamBlocks->second;311 CaptureBlocks *pBlocks = itStreamBlocks->second; 204 312 205 313 AssertPtr(pBlocks); … … 217 325 rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat, 218 326 /* Destination */ 219 pStream->Video.Codec.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight,327 this->Video.Codec.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight, 220 328 /* Source */ 221 pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight);329 pVideoFrame->pu8RGBBuf, this->Settings.Video.ulWidth, this->Settings.Video.ulHeight); 222 330 if (RT_SUCCESS(rc)) 223 331 { 224 rc = VideoRecStreamWriteVideoVPX(pStream,uTimeStampMs, pVideoFrame);332 rc = writeVideoVPX(uTimeStampMs, pVideoFrame); 225 333 } 226 334 else … … 238 346 239 347 #ifdef VBOX_WITH_AUDIO_VIDEOREC 348 AssertPtr(pCtx); 349 240 350 /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be 241 351 * written to the screen's assigned recording stream. */ 242 VideoRecBlockMap::iterator itCommonBlocks = pCtx->mapBlocksCommon.begin();243 while (itCommonBlocks != pCtx->mapBlocksCommon.end())352 VideoRecBlockMap::iterator itCommonBlocks = mapBlocksCommon.begin(); 353 while (itCommonBlocks != mapBlocksCommon.end()) 244 354 { 245 355 VideoRecBlockList::iterator itBlock = itCommonBlocks->second->List.begin(); … … 258 368 WebMWriter::BlockData_Opus blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf, 259 369 pBlockCommon->uTimeStampMs }; 260 AssertPtr( pStream->File.pWEBM);261 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));370 AssertPtr(this->File.pWEBM); 371 rc = this->File.pWEBM->WriteBlock(this->uTrackAudio, &blockData, sizeof(blockData)); 262 372 break; 263 373 } … … 287 397 { 288 398 delete itCommonBlocks->second; 289 pCtx->mapBlocksCommon.erase(itCommonBlocks);290 itCommonBlocks = pCtx->mapBlocksCommon.begin();399 mapBlocksCommon.erase(itCommonBlocks); 400 itCommonBlocks = mapBlocksCommon.begin(); 291 401 } 292 402 else 293 403 ++itCommonBlocks; 294 404 295 LogFunc(("Common blocks: %zu\n", pCtx->mapBlocksCommon.size()));405 LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size())); 296 406 297 407 if (RT_FAILURE(rc)) … … 300 410 #endif 301 411 302 VideoRecStreamUnlock(pStream);412 unlock(); 303 413 304 414 return rc; 305 415 } 306 416 417 int CaptureStream::SendVideoFrame(uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine, 418 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, uint64_t uTimeStampMs) 419 { 420 lock(); 421 422 PVIDEORECVIDEOFRAME pFrame = NULL; 423 424 int rc = VINF_SUCCESS; 425 426 do 427 { 428 if (!this->fEnabled) 429 { 430 rc = VINF_TRY_AGAIN; /* Not (yet) enabled. */ 431 break; 432 } 433 434 if (uTimeStampMs < this->Video.uLastTimeStampMs + this->Video.uDelayMs) 435 { 436 rc = VINF_TRY_AGAIN; /* Respect maximum frames per second. */ 437 break; 438 } 439 440 this->Video.uLastTimeStampMs = uTimeStampMs; 441 442 int xDiff = ((int)this->Settings.Video.ulWidth - (int)uSrcWidth) / 2; 443 uint32_t w = uSrcWidth; 444 if ((int)w + xDiff + (int)x <= 0) /* Nothing visible. */ 445 { 446 rc = VERR_INVALID_PARAMETER; 447 break; 448 } 449 450 uint32_t destX; 451 if ((int)x < -xDiff) 452 { 453 w += xDiff + x; 454 x = -xDiff; 455 destX = 0; 456 } 457 else 458 destX = x + xDiff; 459 460 uint32_t h = uSrcHeight; 461 int yDiff = ((int)this->Settings.Video.ulHeight - (int)uSrcHeight) / 2; 462 if ((int)h + yDiff + (int)y <= 0) /* Nothing visible. */ 463 { 464 rc = VERR_INVALID_PARAMETER; 465 break; 466 } 467 468 uint32_t destY; 469 if ((int)y < -yDiff) 470 { 471 h += yDiff + (int)y; 472 y = -yDiff; 473 destY = 0; 474 } 475 else 476 destY = y + yDiff; 477 478 if ( destX > this->Settings.Video.ulWidth 479 || destY > this->Settings.Video.ulHeight) 480 { 481 rc = VERR_INVALID_PARAMETER; /* Nothing visible. */ 482 break; 483 } 484 485 if (destX + w > this->Settings.Video.ulWidth) 486 w = this->Settings.Video.ulWidth - destX; 487 488 if (destY + h > this->Settings.Video.ulHeight) 489 h = this->Settings.Video.ulHeight - destY; 490 491 pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME)); 492 AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY); 493 494 /* Calculate bytes per pixel and set pixel format. */ 495 const unsigned uBytesPerPixel = uBPP / 8; 496 if (uPixelFormat == BitmapFormat_BGR) 497 { 498 switch (uBPP) 499 { 500 case 32: 501 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB32; 502 break; 503 case 24: 504 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB24; 505 break; 506 case 16: 507 pFrame->uPixelFormat = VIDEORECPIXELFMT_RGB565; 508 break; 509 default: 510 AssertMsgFailedBreakStmt(("Unknown color depth (%RU32)\n", uBPP), rc = VERR_NOT_SUPPORTED); 511 break; 512 } 513 } 514 else 515 AssertMsgFailedBreakStmt(("Unknown pixel format (%RU32)\n", uPixelFormat), rc = VERR_NOT_SUPPORTED); 516 517 const size_t cbRGBBuf = this->Settings.Video.ulWidth 518 * this->Settings.Video.ulHeight 519 * uBytesPerPixel; 520 AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER); 521 522 pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf); 523 AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY); 524 pFrame->cbRGBBuf = cbRGBBuf; 525 pFrame->uWidth = uSrcWidth; 526 pFrame->uHeight = uSrcHeight; 527 528 /* If the current video frame is smaller than video resolution we're going to encode, 529 * clear the frame beforehand to prevent artifacts. */ 530 if ( uSrcWidth < this->Settings.Video.ulWidth 531 || uSrcHeight < this->Settings.Video.ulHeight) 532 { 533 RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf); 534 } 535 536 /* Calculate start offset in source and destination buffers. */ 537 uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel; 538 uint32_t offDst = (destY * this->Settings.Video.ulWidth + destX) * uBytesPerPixel; 539 540 #ifdef VBOX_VIDEOREC_DUMP 541 VIDEORECBMPHDR bmpHdr; 542 RT_ZERO(bmpHdr); 543 544 VIDEORECBMPDIBHDR bmpDIBHdr; 545 RT_ZERO(bmpDIBHdr); 546 547 bmpHdr.u16Magic = 0x4d42; /* Magic */ 548 bmpHdr.u32Size = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR) + (w * h * uBytesPerPixel)); 549 bmpHdr.u32OffBits = (uint32_t)(sizeof(VIDEORECBMPHDR) + sizeof(VIDEORECBMPDIBHDR)); 550 551 bmpDIBHdr.u32Size = sizeof(VIDEORECBMPDIBHDR); 552 bmpDIBHdr.u32Width = w; 553 bmpDIBHdr.u32Height = h; 554 bmpDIBHdr.u16Planes = 1; 555 bmpDIBHdr.u16BitCount = uBPP; 556 bmpDIBHdr.u32XPelsPerMeter = 5000; 557 bmpDIBHdr.u32YPelsPerMeter = 5000; 558 559 char szFileName[RTPATH_MAX]; 560 RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", this->uScreenID); 561 562 RTFILE fh; 563 int rc2 = RTFileOpen(&fh, szFileName, 564 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 565 if (RT_SUCCESS(rc2)) 566 { 567 RTFileWrite(fh, &bmpHdr, sizeof(bmpHdr), NULL); 568 RTFileWrite(fh, &bmpDIBHdr, sizeof(bmpDIBHdr), NULL); 569 } 570 #endif 571 Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel); 572 573 /* Do the copy. */ 574 for (unsigned int i = 0; i < h; i++) 575 { 576 /* Overflow check. */ 577 Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine); 578 Assert(offDst + w * uBytesPerPixel <= this->Settings.Video.ulHeight * this->Settings.Video.ulWidth * uBytesPerPixel); 579 580 memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel); 581 582 #ifdef VBOX_VIDEOREC_DUMP 583 if (RT_SUCCESS(rc2)) 584 RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL); 585 #endif 586 offSrc += uBytesPerLine; 587 offDst += this->Settings.Video.ulWidth * uBytesPerPixel; 588 } 589 590 #ifdef VBOX_VIDEOREC_DUMP 591 if (RT_SUCCESS(rc2)) 592 RTFileClose(fh); 593 #endif 594 595 } while (0); 596 597 if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */ 598 { 599 PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK)); 600 if (pBlock) 601 { 602 AssertPtr(pFrame); 603 604 pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO; 605 pBlock->pvData = pFrame; 606 pBlock->cbData = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf; 607 608 try 609 { 610 CaptureBlocks *pVideoRecBlocks = new CaptureBlocks(); 611 pVideoRecBlocks->List.push_back(pBlock); 612 613 Assert(this->Blocks.Map.find(uTimeStampMs) == this->Blocks.Map.end()); 614 this->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks)); 615 } 616 catch (const std::exception &ex) 617 { 618 RT_NOREF(ex); 619 620 RTMemFree(pBlock); 621 rc = VERR_NO_MEMORY; 622 } 623 } 624 else 625 rc = VERR_NO_MEMORY; 626 } 627 628 if (RT_FAILURE(rc)) 629 VideoRecVideoFrameFree(pFrame); 630 631 lock(); 632 633 return rc; 634 } 635 636 int CaptureStream::Init(uint32_t a_uScreen, const settings::CaptureScreenSettings &a_Settings) 637 { 638 return initInternal(a_uScreen, a_Settings); 639 } 640 307 641 /** 308 642 * Initializes a recording stream. 309 643 * 310 644 * @returns IPRT status code. 311 * @param pStream Recording stream to initialize. 312 * @param pCtx Recording context to use for initialization. 313 * @param uScreen Screen number to record. 314 */ 315 int VideoRecStreamInit(PVIDEORECSTREAM pStream, PVIDEORECCONTEXT pCtx, uint32_t uScreen) 316 { 317 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 318 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 319 320 PVIDEORECCFG pCfg = &pCtx->Cfg; 321 322 #ifdef VBOX_WITH_AUDIO_VIDEOREC 323 if (pCfg->Audio.fEnabled) 324 { 325 /* Sanity. */ 326 AssertReturn(pCfg->Audio.uHz, VERR_INVALID_PARAMETER); 327 AssertReturn(pCfg->Audio.cBits, VERR_INVALID_PARAMETER); 328 AssertReturn(pCfg->Audio.cChannels, VERR_INVALID_PARAMETER); 329 } 330 #endif 331 332 int rc = VideoRecStreamOpen(pStream, pCfg); 645 * @param uScreen Screen number to use for this recording stream. 646 * @param Cfg Recording screen configuration to use for initialization. 647 */ 648 int CaptureStream::initInternal(uint32_t a_uScreen, const settings::CaptureScreenSettings &a_Settings) 649 { 650 int rc = parseOptionsString(a_Settings.strOptions); 333 651 if (RT_FAILURE(rc)) 334 652 return rc; 335 653 336 if (pCfg->Video.fEnabled) 337 rc = VideoRecStreamInitVideo(pStream, pCfg); 338 339 switch (pStream->enmDst) 340 { 341 case VIDEORECDEST_FILE: 342 { 343 rc = pStream->File.pWEBM->OpenEx(pStream->File.pszFile, &pStream->File.hFile, 654 rc = RTCritSectInit(&this->CritSect); 655 if (RT_FAILURE(rc)) 656 return rc; 657 658 rc = open(); 659 if (RT_FAILURE(rc)) 660 return rc; 661 662 const bool fVideoEnabled = a_Settings.isFeatureEnabled(CaptureFeature_Video); 663 const bool fAudioEnabled = a_Settings.isFeatureEnabled(CaptureFeature_Audio); 664 665 if (fVideoEnabled) 666 rc = initVideo(); 667 668 if (fAudioEnabled) 669 rc = initAudio(); 670 671 switch (this->Settings.enmDest) 672 { 673 case CaptureDestination_File: 674 { 675 const char *pszFile = this->Settings.File.strName.c_str(); 676 677 rc = File.pWEBM->OpenEx(pszFile, &this->File.hFile, 344 678 #ifdef VBOX_WITH_AUDIO_VIDEOREC 345 pCfg->Audio.fEnabled ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None, 679 a_Settings.isFeatureEnabled(CaptureFeature_Audio) 680 ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None, 346 681 #else 347 WebMWriter::AudioCodec_None, 348 #endif 349 pCfg->Video.fEnabled ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None); 682 WebMWriter::AudioCodec_None, 683 #endif 684 a_Settings.isFeatureEnabled(CaptureFeature_Video) 685 ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None); 350 686 if (RT_FAILURE(rc)) 351 687 { 352 LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", p Stream->File.pszFile, rc));688 LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", pszFile, rc)); 353 689 break; 354 690 } 355 691 356 const char *pszFile = pStream->File.pszFile; 357 358 if (pCfg->Video.fEnabled) 359 { 360 rc = pStream->File.pWEBM->AddVideoTrack(pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uFPS, 361 &pStream->uTrackVideo); 692 if (fVideoEnabled) 693 { 694 rc = this->File.pWEBM->AddVideoTrack(a_Settings.Video.ulWidth, a_Settings.Video.ulHeight, 695 a_Settings.Video.ulFPS, &this->uTrackVideo); 362 696 if (RT_FAILURE(rc)) 363 697 { … … 367 701 368 702 LogRel(("VideoRec: Recording video of screen #%u with %RU32x%RU32 @ %RU32 kbps, %RU32 FPS (track #%RU8)\n", 369 uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS,370 pStream->uTrackVideo));703 this->uScreenID, a_Settings.Video.ulWidth, a_Settings.Video.ulHeight, a_Settings.Video.ulRate, 704 a_Settings.Video.ulFPS, this->uTrackVideo)); 371 705 } 372 706 373 707 #ifdef VBOX_WITH_AUDIO_VIDEOREC 374 if ( pCfg->Audio.fEnabled)375 { 376 rc = pStream->File.pWEBM->AddAudioTrack(pCfg->Audio.uHz, pCfg->Audio.cChannels, pCfg->Audio.cBits,377 &pStream->uTrackAudio);708 if (fAudioEnabled) 709 { 710 rc = this->File.pWEBM->AddAudioTrack(a_Settings.Audio.uHz, a_Settings.Audio.cChannels, a_Settings.Audio.cBits, 711 &this->uTrackAudio); 378 712 if (RT_FAILURE(rc)) 379 713 { … … 383 717 384 718 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s (track #%RU8)\n", 385 pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channels" : "channel",386 pStream->uTrackAudio));387 } 388 #endif 389 390 if ( pCfg->Video.fEnabled719 a_Settings.Audio.uHz, a_Settings.Audio.cBits, a_Settings.Audio.cChannels, 720 a_Settings.Audio.cChannels ? "channels" : "channel", this->uTrackAudio)); 721 } 722 #endif 723 724 if ( fVideoEnabled 391 725 #ifdef VBOX_WITH_AUDIO_VIDEOREC 392 || pCfg->Audio.fEnabled726 || fAudioEnabled 393 727 #endif 394 728 ) 395 729 { 396 730 char szWhat[32] = { 0 }; 397 if ( pCfg->Video.fEnabled)731 if (fVideoEnabled) 398 732 RTStrCat(szWhat, sizeof(szWhat), "video"); 399 733 #ifdef VBOX_WITH_AUDIO_VIDEOREC 400 if ( pCfg->Audio.fEnabled)734 if (fAudioEnabled) 401 735 { 402 if ( pCfg->Video.fEnabled)736 if (fVideoEnabled) 403 737 RTStrCat(szWhat, sizeof(szWhat), " + "); 404 738 RTStrCat(szWhat, sizeof(szWhat), "audio"); … … 419 753 if (RT_SUCCESS(rc)) 420 754 { 421 pStream->pCtx = pCtx; 422 pStream->fEnabled = true; 755 this->pCtx = pCtx; 756 this->fEnabled = true; 757 this->uScreenID = a_uScreen; 758 this->tsStartMs = RTTimeMilliTS(); 759 this->Settings = a_Settings; 423 760 } 424 761 else 425 762 { 426 int rc2 = VideoRecStreamClose(pStream);763 int rc2 = uninitInternal(); 427 764 AssertRC(rc2); 428 765 return rc; … … 438 775 * 439 776 * @returns IPRT status code. 440 * @param pStream Recording stream to close. 441 * 442 */ 443 int VideoRecStreamClose(PVIDEORECSTREAM pStream) 777 */ 778 int CaptureStream::close(void) 444 779 { 445 780 int rc = VINF_SUCCESS; 446 781 447 if ( pStream->fEnabled)448 { 449 switch ( pStream->enmDst)450 { 451 case VIDEORECDEST_FILE:452 { 453 if ( pStream->File.pWEBM)454 rc = pStream->File.pWEBM->Close();782 if (this->fEnabled) 783 { 784 switch (this->Settings.enmDest) 785 { 786 case CaptureDestination_File: 787 { 788 if (this->File.pWEBM) 789 rc = this->File.pWEBM->Close(); 455 790 break; 456 791 } … … 461 796 } 462 797 463 pStream->Blocks.Clear();464 465 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID));798 this->Blocks.Clear(); 799 800 LogRel(("VideoRec: Recording screen #%u stopped\n", this->uScreenID)); 466 801 } 467 802 468 803 if (RT_FAILURE(rc)) 469 804 { 470 LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", pStream->uScreenID, rc));805 LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", this->uScreenID, rc)); 471 806 return rc; 472 807 } 473 808 474 switch (pStream->enmDst) 475 { 476 case VIDEORECDEST_FILE: 477 { 478 AssertPtr(pStream->File.pszFile); 479 if (RTFileIsValid(pStream->File.hFile)) 480 { 481 rc = RTFileClose(pStream->File.hFile); 809 switch (this->Settings.enmDest) 810 { 811 case CaptureDestination_File: 812 { 813 if (RTFileIsValid(this->File.hFile)) 814 { 815 rc = RTFileClose(this->File.hFile); 482 816 if (RT_SUCCESS(rc)) 483 817 { 484 LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile));818 LogRel(("VideoRec: Closed file '%s'\n", this->Settings.File.strName.c_str())); 485 819 } 486 820 else 487 821 { 488 LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", pStream->File.pszFile, rc));822 LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", this->Settings.File.strName.c_str(), rc)); 489 823 break; 490 824 } 491 825 } 492 826 493 RTStrFree(pStream->File.pszFile); 494 pStream->File.pszFile = NULL; 495 496 if (pStream->File.pWEBM) 497 { 498 delete pStream->File.pWEBM; 499 pStream->File.pWEBM = NULL; 827 if (this->File.pWEBM) 828 { 829 delete this->File.pWEBM; 830 this->File.pWEBM = NULL; 500 831 } 501 832 break; … … 507 838 } 508 839 509 if (RT_SUCCESS(rc))510 {511 pStream->enmDst = VIDEORECDEST_INVALID;512 }513 514 840 LogFlowFuncLeaveRC(rc); 515 841 return rc; … … 520 846 * 521 847 * @returns IPRT status code. 522 * @param pStream Recording stream to uninitialize. 523 */ 524 int VideoRecStreamUninit(PVIDEORECSTREAM pStream) 525 { 526 int rc = VINF_SUCCESS; 527 528 if (pStream->pCtx->Cfg.Video.fEnabled) 529 { 530 int rc2 = VideoRecStreamUnitVideo(pStream); 848 */ 849 int CaptureStream::Uninit(void) 850 { 851 return uninitInternal(); 852 } 853 854 int CaptureStream::uninitInternal(void) 855 { 856 int rc = close(); 857 if (RT_FAILURE(rc)) 858 return rc; 859 860 if (this->Settings.isFeatureEnabled(CaptureFeature_Video)) 861 { 862 int rc2 = unitVideo(); 531 863 if (RT_SUCCESS(rc)) 532 864 rc = rc2; 533 865 } 534 866 867 RTCritSectDelete(&this->CritSect); 868 869 this->fEnabled = false; 870 535 871 return rc; 536 872 } … … 540 876 * 541 877 * @returns IPRT status code. 542 * @param pStream Recording stream to uninitialize video recording for. 543 */ 544 int VideoRecStreamUnitVideo(PVIDEORECSTREAM pStream) 878 */ 879 int CaptureStream::unitVideo(void) 545 880 { 546 881 #ifdef VBOX_WITH_LIBVPX 547 882 /* At the moment we only have VPX. */ 548 return VideoRecStreamUninitVideoVPX(pStream);883 return uninitVideoVPX(); 549 884 #else 550 885 return VERR_NOT_SUPPORTED; … … 557 892 * 558 893 * @returns IPRT status code. 559 * @param pStream Recording stream to uninitialize VPX codec for. 560 */ 561 int VideoRecStreamUninitVideoVPX(PVIDEORECSTREAM pStream) 562 { 563 vpx_img_free(&pStream->Video.Codec.VPX.RawImage); 564 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx); 894 */ 895 int CaptureStream::uninitVideoVPX(void) 896 { 897 vpx_img_free(&this->Video.Codec.VPX.RawImage); 898 vpx_codec_err_t rcv = vpx_codec_destroy(&this->Video.Codec.VPX.Ctx); 565 899 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 566 900 … … 573 907 * 574 908 * @returns IPRT status code. 575 * @param pStream Recording stream to initialize video recording for. 576 * @param pCfg Video recording configuration to use for initialization. 577 */ 578 int VideoRecStreamInitVideo(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 579 { 909 */ 910 int CaptureStream::initVideo(void) 911 { 912 /* Sanity. */ 913 AssertReturn(this->Settings.Video.ulRate, VERR_INVALID_PARAMETER); 914 AssertReturn(this->Settings.Video.ulWidth, VERR_INVALID_PARAMETER); 915 AssertReturn(this->Settings.Video.ulHeight, VERR_INVALID_PARAMETER); 916 AssertReturn(this->Settings.Video.ulFPS, VERR_INVALID_PARAMETER); 917 918 this->Video.cFailedEncodingFrames = 0; 919 this->Video.uDelayMs = RT_MS_1SEC / this->Settings.Video.ulFPS; 920 580 921 #ifdef VBOX_WITH_LIBVPX 581 922 /* At the moment we only have VPX. */ 582 return VideoRecStreamInitVideoVPX(pStream, pCfg);923 return initVideoVPX(); 583 924 #else 584 return V ERR_NOT_SUPPORTED;925 return VINF_SUCCESS; 585 926 #endif 586 927 } … … 591 932 * 592 933 * @returns IPRT status code. 593 * @param pStream Recording stream to initialize VPX codec for. 594 * @param pCfg Video recording configuration to use for initialization. 595 */ 596 int VideoRecStreamInitVideoVPX(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 597 { 598 pStream->Video.uWidth = pCfg->Video.uWidth; 599 pStream->Video.uHeight = pCfg->Video.uHeight; 600 pStream->Video.cFailedEncodingFrames = 0; 601 602 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 603 604 pStream->Video.uDelayMs = RT_MS_1SEC / pCfg->Video.uFPS; 605 606 pVC->enmType = VIDEORECVIDEOCODECTYPE_VP8; /** @todo Make this configurable. */ 607 934 */ 935 int CaptureStream::initVideoVPX(void) 936 { 608 937 # ifdef VBOX_WITH_LIBVPX_VP9 609 938 vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx(); … … 612 941 # endif 613 942 614 vpx_codec_err_t rcv = vpx_codec_enc_config_default(pCodecIface, &pVC->VPX.Cfg, 0 /* Reserved */); 943 PVIDEORECVIDEOCODEC pCodec = &this->Video.Codec; 944 945 vpx_codec_err_t rcv = vpx_codec_enc_config_default(pCodecIface, &pCodec->VPX.Cfg, 0 /* Reserved */); 615 946 if (rcv != VPX_CODEC_OK) 616 947 { … … 620 951 621 952 /* Target bitrate in kilobits per second. */ 622 p VC->VPX.Cfg.rc_target_bitrate = pCfg->Video.uRate;953 pCodec->VPX.Cfg.rc_target_bitrate = this->Settings.Video.ulRate; 623 954 /* Frame width. */ 624 p VC->VPX.Cfg.g_w = pCfg->Video.uWidth;955 pCodec->VPX.Cfg.g_w = this->Settings.Video.ulWidth; 625 956 /* Frame height. */ 626 p VC->VPX.Cfg.g_h = pCfg->Video.uHeight;957 pCodec->VPX.Cfg.g_h = this->Settings.Video.ulHeight; 627 958 /* 1ms per frame. */ 628 p VC->VPX.Cfg.g_timebase.num = 1;629 p VC->VPX.Cfg.g_timebase.den = 1000;959 pCodec->VPX.Cfg.g_timebase.num = 1; 960 pCodec->VPX.Cfg.g_timebase.den = 1000; 630 961 /* Disable multithreading. */ 631 p VC->VPX.Cfg.g_threads = 0;962 pCodec->VPX.Cfg.g_threads = 0; 632 963 633 964 /* Initialize codec. */ 634 rcv = vpx_codec_enc_init(&p VC->VPX.Ctx, pCodecIface, &pVC->VPX.Cfg, 0 /* Flags */);965 rcv = vpx_codec_enc_init(&pCodec->VPX.Ctx, pCodecIface, &pCodec->VPX.Cfg, 0 /* Flags */); 635 966 if (rcv != VPX_CODEC_OK) 636 967 { … … 639 970 } 640 971 641 if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, pCfg->Video.uWidth, pCfg->Video.uHeight, 1)) 642 { 643 LogRel(("VideoRec: Failed to allocate image %RU32x%RU32\n", pCfg->Video.uWidth, pCfg->Video.uHeight)); 972 if (!vpx_img_alloc(&pCodec->VPX.RawImage, VPX_IMG_FMT_I420, 973 this->Settings.Video.ulWidth, this->Settings.Video.ulHeight, 1)) 974 { 975 LogRel(("VideoRec: Failed to allocate image %RU32x%RU32\n", 976 this->Settings.Video.ulWidth, this->Settings.Video.ulHeight)); 644 977 return VERR_NO_MEMORY; 645 978 } 646 979 647 980 /* Save a pointer to the first raw YUV plane. */ 648 p Stream->Video.Codec.VPX.pu8YuvBuf = pVC->VPX.RawImage.planes[0];981 pCodec->VPX.pu8YuvBuf = pCodec->VPX.RawImage.planes[0]; 649 982 650 983 return VINF_SUCCESS; … … 652 985 #endif 653 986 987 int CaptureStream::initAudio(void) 988 { 989 #ifdef VBOX_WITH_AUDIO_VIDEOREC 990 if (this->Settings.isFeatureEnabled(CaptureFeature_Audio)) 991 { 992 /* Sanity. */ 993 AssertReturn(this->Settings.Audio.uHz, VERR_INVALID_PARAMETER); 994 AssertReturn(this->Settings.Audio.cBits, VERR_INVALID_PARAMETER); 995 AssertReturn(this->Settings.Audio.cChannels, VERR_INVALID_PARAMETER); 996 } 997 #endif 998 999 return VINF_SUCCESS; 1000 } 1001 654 1002 #ifdef VBOX_WITH_LIBVPX 655 1003 /** … … 657 1005 * 658 1006 * @returns IPRT status code. 659 * @param pStream Stream to encode and submit to.660 1007 * @param uTimeStampMs Absolute timestamp (PTS) of frame (in ms) to encode. 661 1008 * @param pFrame Frame to encode and submit. 662 1009 */ 663 int VideoRecStreamWriteVideoVPX(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame) 664 { 665 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 666 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); 1010 int CaptureStream::writeVideoVPX(uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame) 1011 { 1012 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); 667 1013 668 1014 int rc; 669 1015 670 AssertPtr(pStream->pCtx); 671 PVIDEORECCFG pCfg = &pStream->pCtx->Cfg; 672 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 1016 PVIDEORECVIDEOCODEC pCodec = &this->Video.Codec; 673 1017 674 1018 /* Presentation Time Stamp (PTS). */ 675 1019 vpx_codec_pts_t pts = uTimeStampMs; 676 vpx_codec_err_t rcv = vpx_codec_encode(&p VC->VPX.Ctx,677 &p VC->VPX.RawImage,678 pts 679 pStream->Video.uDelayMs/* How long to show this frame */,680 0 681 pC fg->Video.Codec.VPX.uEncoderDeadline /* Quality setting */);1020 vpx_codec_err_t rcv = vpx_codec_encode(&pCodec->VPX.Ctx, 1021 &pCodec->VPX.RawImage, 1022 pts /* Time stamp */, 1023 this->Video.uDelayMs /* How long to show this frame */, 1024 0 /* Flags */, 1025 pCodec->VPX.uEncoderDeadline /* Quality setting */); 682 1026 if (rcv != VPX_CODEC_OK) 683 1027 { 684 if ( pStream->Video.cFailedEncodingFrames++ < 64)1028 if (this->Video.cFailedEncodingFrames++ < 64) /** @todo Make this configurable. */ 685 1029 { 686 1030 LogRel(("VideoRec: Failed to encode video frame: %s\n", vpx_codec_err_to_string(rcv))); … … 689 1033 } 690 1034 691 pStream->Video.cFailedEncodingFrames = 0;1035 this->Video.cFailedEncodingFrames = 0; 692 1036 693 1037 vpx_codec_iter_t iter = NULL; … … 695 1039 for (;;) 696 1040 { 697 const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&p VC->VPX.Ctx, &iter);1041 const vpx_codec_cx_pkt_t *pPacket = vpx_codec_get_cx_data(&pCodec->VPX.Ctx, &iter); 698 1042 if (!pPacket) 699 1043 break; … … 703 1047 case VPX_CODEC_CX_FRAME_PKT: 704 1048 { 705 WebMWriter::BlockData_VP8 blockData = { &p VC->VPX.Cfg, pPacket };706 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));1049 WebMWriter::BlockData_VP8 blockData = { &pCodec->VPX.Cfg, pPacket }; 1050 rc = this->File.pWEBM->WriteBlock(this->uTrackVideo, &blockData, sizeof(blockData)); 707 1051 break; 708 1052 } … … 719 1063 #endif /* VBOX_WITH_LIBVPX */ 720 1064 1065 /** 1066 * Locks a recording stream. 1067 * 1068 * @param pStream Recording stream to lock. 1069 */ 1070 void CaptureStream::lock(void) 1071 { 1072 int rc = RTCritSectEnter(&CritSect); 1073 AssertRC(rc); 1074 } 1075 1076 /** 1077 * Unlocks a locked recording stream. 1078 * 1079 * @param pStream Recording stream to unlock. 1080 */ 1081 void CaptureStream::unlock(void) 1082 { 1083 int rc = RTCritSectLeave(&CritSect); 1084 AssertRC(rc); 1085 } 1086
Note:
See TracChangeset
for help on using the changeset viewer.