Changeset 68798 in vbox for trunk/src/VBox/Main
- Timestamp:
- Sep 20, 2017 10:27:16 AM (7 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/include/ConsoleImpl.h
r68485 r68798 155 155 HRESULT i_updateMachineState(MachineState_T aMachineState); 156 156 HRESULT i_getNominalState(MachineState_T &aNominalState); 157 Utf8Str i_getAudioAdapterDeviceName(IAudioAdapter *aAudioAdapter); 157 158 158 159 // events from IInternalSessionControl -
trunk/src/VBox/Main/include/DisplayImpl.h
r68534 r68798 5 5 6 6 /* 7 * Copyright (C) 2006-201 6Oracle Corporation7 * Copyright (C) 2006-2017 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 32 32 #endif 33 33 34 #ifdef VBOX_WITH_VIDEOREC 35 # include "../src-client/VideoRec.h" 36 struct VIDEORECCONTEXT; 37 #endif 38 34 39 #include "DisplaySourceBitmapWrap.h" 35 40 36 41 37 42 class Console; 38 struct VIDEORECCONTEXT;39 43 40 44 typedef struct _DISPLAYFBINFO … … 204 208 void VideoAccelFlushVMMDev(void); 205 209 206 int i_videoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens)); 210 #ifdef VBOX_WITH_VIDEOREC 211 VIDEORECFEATURES i_videoCaptureGetEnabled(void); 212 bool i_videoCaptureStarted(void); 213 int i_videoCaptureInvalidate(void); 207 214 int i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs); 208 int i_videoCaptureStart(); 209 void i_videoCaptureStop(); 210 #ifdef VBOX_WITH_VIDEOREC 211 void videoCaptureScreenChanged(unsigned uScreenId); 215 int i_videoCaptureStart(void); 216 void i_videoCaptureStop(void); 217 void i_videoCaptureScreenChanged(unsigned uScreenId); 212 218 #endif 213 219 … … 509 515 510 516 #ifdef VBOX_WITH_VIDEOREC 517 VIDEORECCFG mVideoRecCfg; 511 518 VIDEORECCONTEXT *mpVideoRecCtx; 512 519 bool maVideoRecEnabled[SchemaDefs::MaxGuestMonitors]; -
trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
r68485 r68798 82 82 #include "AutoCaller.h" 83 83 #include "ThreadTask.h" 84 85 #ifdef VBOX_WITH_VIDEOREC 86 # include "VideoRec.h" 87 #endif 84 88 85 89 #include <VBox/com/array.h> … … 4965 4969 } 4966 4970 4971 Utf8Str Console::i_getAudioAdapterDeviceName(IAudioAdapter *aAudioAdapter) 4972 { 4973 Utf8Str strDevice; 4974 4975 AudioControllerType_T audioController; 4976 HRESULT hrc = aAudioAdapter->COMGETTER(AudioController)(&audioController); 4977 AssertComRC(hrc); 4978 if (SUCCEEDED(hrc)) 4979 { 4980 switch (audioController) 4981 { 4982 case AudioControllerType_AC97: strDevice = "ichac97"; break; 4983 case AudioControllerType_SB16: strDevice = "sb16"; break; 4984 case AudioControllerType_HDA: strDevice = "hda"; break; 4985 default: break; /* None. */ 4986 } 4987 } 4988 4989 return strDevice; 4990 } 4991 4967 4992 /** 4968 4993 * Called by IInternalSessionControl::OnAudioAdapterChange(). … … 4992 5017 if (SUCCEEDED(hrc)) 4993 5018 { 4994 AudioControllerType_T audioController; 4995 hrc = aAudioAdapter->COMGETTER(AudioController)(&audioController); 4996 AssertComRC(hrc); 4997 if (SUCCEEDED(hrc)) 5019 int rc = VINF_SUCCESS; 5020 5021 for (ULONG ulLUN = 0; ulLUN < 16 /** @todo Use a define */; ulLUN++) 4998 5022 { 4999 Utf8Str strDevice; 5000 5001 switch (audioController) 5023 PPDMIBASE pBase; 5024 int rc2 = PDMR3QueryDriverOnLun(ptrVM.rawUVM(), 5025 i_getAudioAdapterDeviceName(aAudioAdapter).c_str(), 0 /* iInstance */, 5026 ulLUN, "AUDIO", &pBase); 5027 if (RT_FAILURE(rc2)) 5028 continue; 5029 5030 if (pBase) 5002 5031 { 5003 case AudioControllerType_AC97: strDevice = "ichac97"; break; 5004 case AudioControllerType_SB16: strDevice = "sb16"; break; 5005 case AudioControllerType_HDA: strDevice = "hda"; break; 5006 default: 5007 AssertFailed(); break; /* Should never happen. */ 5008 } 5009 5010 int rc = VINF_SUCCESS; 5011 5012 for (ULONG ulLUN = 0; ulLUN < 16 /** @todo Use a define */; ulLUN++) 5013 { 5014 PPDMIBASE pBase; 5015 int rc2 = PDMR3QueryDriverOnLun(ptrVM.rawUVM(), 5016 strDevice.c_str(), 0 /* iInstance */, ulLUN, "AUDIO", &pBase); 5017 if (RT_FAILURE(rc2)) 5018 continue; 5019 5020 Log(("%s: LUN#%RU32\n", strDevice.c_str(), ulLUN)); 5021 5022 if (pBase) 5032 PPDMIAUDIOCONNECTOR pAudioCon = 5033 (PPDMIAUDIOCONNECTOR)pBase->pfnQueryInterface(pBase, PDMIAUDIOCONNECTOR_IID); 5034 5035 if ( pAudioCon 5036 && pAudioCon->pfnEnable) 5023 5037 { 5024 PPDMIAUDIOCONNECTOR pAudioCon = 5025 (PPDMIAUDIOCONNECTOR)pBase->pfnQueryInterface(pBase, PDMIAUDIOCONNECTOR_IID); 5026 5027 if ( pAudioCon 5028 && pAudioCon->pfnEnable) 5029 { 5030 int rcIn = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_IN, RT_BOOL(fEnabledIn)); 5031 if (RT_FAILURE(rcIn)) 5032 LogRel(("Audio: Failed to %s input of LUN#%RU32, rc=%Rrc\n", 5033 fEnabledIn ? "enable" : "disable", ulLUN, rcIn)); 5034 5035 if (RT_SUCCESS(rc)) 5036 rc = rcIn; 5037 5038 int rcOut = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_OUT, RT_BOOL(fEnabledOut)); 5039 if (RT_FAILURE(rcOut)) 5040 LogRel(("Audio: Failed to %s output of LUN#%RU32, rc=%Rrc\n", 5041 fEnabledIn ? "enable" : "disable", ulLUN, rcOut)); 5042 5043 if (RT_SUCCESS(rc)) 5044 rc = rcOut; 5045 } 5038 int rcIn = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_IN, RT_BOOL(fEnabledIn)); 5039 if (RT_FAILURE(rcIn)) 5040 LogRel(("Audio: Failed to %s input of LUN#%RU32, rc=%Rrc\n", 5041 fEnabledIn ? "enable" : "disable", ulLUN, rcIn)); 5042 5043 if (RT_SUCCESS(rc)) 5044 rc = rcIn; 5045 5046 int rcOut = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_OUT, RT_BOOL(fEnabledOut)); 5047 if (RT_FAILURE(rcOut)) 5048 LogRel(("Audio: Failed to %s output of LUN#%RU32, rc=%Rrc\n", 5049 fEnabledIn ? "enable" : "disable", ulLUN, rcOut)); 5050 5051 if (RT_SUCCESS(rc)) 5052 rc = rcOut; 5046 5053 } 5047 5054 } 5048 5049 if (RT_SUCCESS(rc))5050 LogRel(("Audio: Status has changed (input is %s, output is %s)\n",5051 fEnabledIn ? "enabled" : "disabled", fEnabledOut ? "enabled" : "disabled"));5052 5055 } 5056 5057 if (RT_SUCCESS(rc)) 5058 LogRel(("Audio: Status has changed (input is %s, output is %s)\n", 5059 fEnabledIn ? "enabled" : "disabled", fEnabledOut ? "enabled" : "disabled")); 5053 5060 } 5054 5061 } … … 5486 5493 HRESULT rc = S_OK; 5487 5494 5488 /* don't trigger video capture changes if the VM isn't running */ 5495 #ifdef VBOX_WITH_VIDEOREC 5496 /* Don't trigger video capture changes if the VM isn't running. */ 5489 5497 SafeVMPtrQuiet ptrVM(this); 5490 5498 if (ptrVM.isOk()) 5491 5499 { 5492 BOOL fEnabled;5493 rc = mMachine->COMGETTER(VideoCaptureEnabled)(&fEnabled);5494 SafeArray<BOOL> screens;5495 if (SUCCEEDED(rc))5496 rc = mMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens));5497 5500 if (mDisplay) 5498 5501 { 5499 int vrc = VINF_SUCCESS; 5500 if (SUCCEEDED(rc)) 5501 vrc = mDisplay->i_videoCaptureEnableScreens(ComSafeArrayAsInParam(screens)); 5502 int vrc = mDisplay->i_videoCaptureInvalidate(); 5502 5503 if (RT_SUCCESS(vrc)) 5503 5504 { 5504 if (fEnabled) 5505 VIDEORECFEATURES fFeatures = mDisplay->i_videoCaptureGetEnabled(); 5506 5507 # ifdef VBOX_WITH_AUDIO_VIDEOREC 5508 ComPtr<IAudioAdapter> audioAdapter; 5509 rc = mMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); 5510 AssertComRC(rc); 5511 5512 Utf8Str strAudioDev = i_getAudioAdapterDeviceName(audioAdapter); 5513 if (!strAudioDev.isEmpty()) /* Any audio device enabled? */ 5514 { 5515 for (ULONG ulLUN = 0; ulLUN < 16 /** @todo Use a define */; ulLUN++) 5516 { 5517 PPDMIBASE pBase; 5518 int rc2 = PDMR3QueryDriverOnLun(ptrVM.rawUVM(), strAudioDev.c_str(), 5519 0 /* iInstance */, ulLUN, "AUDIO", &pBase); 5520 if (RT_FAILURE(rc2)) 5521 continue; 5522 5523 if (pBase) 5524 { 5525 PPDMIAUDIOCONNECTOR pAudioCon = 5526 (PPDMIAUDIOCONNECTOR)pBase->pfnQueryInterface(pBase, PDMIAUDIOCONNECTOR_IID); 5527 5528 if ( pAudioCon 5529 && pAudioCon->pfnEnable) 5530 { 5531 rc2 = pAudioCon->pfnEnable(pAudioCon, PDMAUDIODIR_OUT, fFeatures & VIDEORECFEATURE_AUDIO); 5532 if (RT_FAILURE(rc2)) 5533 LogRel(("VideoRec: Failed to %s audio recording, rc=%Rrc\n", 5534 fFeatures & VIDEORECFEATURE_AUDIO ? "enable" : "disable", ulLUN, rc2)); 5535 } 5536 5537 break; /* Driver found, no need to continue. */ 5538 } 5539 } 5540 } 5541 # endif /* VBOX_WITH_AUDIO_VIDEOREC */ 5542 5543 if (!mDisplay->i_videoCaptureStarted()) 5505 5544 { 5506 5545 vrc = mDisplay->i_videoCaptureStart(); … … 5514 5553 rc = setError(E_FAIL, tr("Unable to set screens for capturing (%Rrc)"), vrc); 5515 5554 } 5555 5516 5556 ptrVM.release(); 5517 5557 } 5558 #endif /* VBOX_WITH_VIDEOREC */ 5518 5559 5519 5560 /* notify console callbacks on success */ -
trunk/src/VBox/Main/src-client/DisplayImpl.cpp
r68534 r68798 56 56 # include <iprt/path.h> 57 57 # include "VideoRec.h" 58 59 # ifdef VBOX_WITH_LIBVPX 60 # include <vpx/vpx_encoder.h> 61 # endif 58 62 #endif 59 63 … … 1049 1053 1050 1054 #ifdef VBOX_WITH_VIDEOREC 1051 videoCaptureScreenChanged(uScreenId);1055 i_videoCaptureScreenChanged(uScreenId); 1052 1056 #endif 1053 1057 … … 2386 2390 } 2387 2391 2388 int Display::i_videoCaptureEnableScreens(ComSafeArrayIn(BOOL, aScreens))2389 {2390 2392 #ifdef VBOX_WITH_VIDEOREC 2391 com::SafeArray<BOOL> Screens(ComSafeArrayInArg(aScreens)); 2392 for (unsigned i = 0; i < Screens.size(); i++) 2393 { 2394 bool fChanged = maVideoRecEnabled[i] != RT_BOOL(Screens[i]); 2395 2396 maVideoRecEnabled[i] = RT_BOOL(Screens[i]); 2393 VIDEORECFEATURES Display::i_videoCaptureGetEnabled(void) 2394 { 2395 return VideoRecGetEnabled(&mVideoRecCfg); 2396 } 2397 2398 bool Display::i_videoCaptureStarted(void) 2399 { 2400 return VideoRecIsActive(mpVideoRecCtx); 2401 } 2402 2403 int Display::i_videoCaptureInvalidate(void) 2404 { 2405 AssertPtr(mParent); 2406 ComPtr<IMachine> pMachine = mParent->i_machine(); 2407 Assert(pMachine.isNotNull()); 2408 2409 mVideoRecCfg.fEnabled = true; 2410 mVideoRecCfg.enmDst = VIDEORECDEST_FILE; /** @todo Make this configurable once we have more variations. */ 2411 2412 /* 2413 * Get parameters from API. 2414 */ 2415 SafeArray<BOOL> aScreens; 2416 HRESULT rc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(aScreens)); 2417 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2418 aScreens.cloneTo(mVideoRecCfg.aScreens); 2419 2420 rc = pMachine->COMGETTER(VideoCaptureWidth)(&mVideoRecCfg.Video.uWidth); 2421 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2422 rc = pMachine->COMGETTER(VideoCaptureHeight)(&mVideoRecCfg.Video.uHeight); 2423 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2424 rc = pMachine->COMGETTER(VideoCaptureRate)(&mVideoRecCfg.Video.uRate); 2425 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2426 rc = pMachine->COMGETTER(VideoCaptureFPS)(&mVideoRecCfg.Video.uFPS); 2427 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2428 rc = pMachine->COMGETTER(VideoCaptureFile)(&mVideoRecCfg.File.strFile); 2429 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2430 rc = pMachine->COMGETTER(VideoCaptureMaxFileSize)(&mVideoRecCfg.File.uMaxSizeMB); 2431 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2432 rc = pMachine->COMGETTER(VideoCaptureMaxTime)(&mVideoRecCfg.uMaxTimeS); 2433 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2434 BSTR bstrOptions; 2435 rc = pMachine->COMGETTER(VideoCaptureOptions)(&bstrOptions); 2436 AssertComRCReturn(rc, VERR_COM_UNEXPECTED); 2437 2438 /* 2439 * Parse options string (from API). 2440 */ 2441 com::Utf8Str strOptions(bstrOptions); 2442 size_t pos = 0; 2443 2444 /* By default we only enable video recording for now. 2445 * Audio support is considered as being experimental. There be dragons! */ 2446 mVideoRecCfg.Video.fEnabled = true; 2447 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2448 mVideoRecCfg.Audio.fEnabled = false; 2449 /* By default we use 48kHz, 16-bit, stereo for the audio track. */ 2450 mVideoRecCfg.Audio.uHz = 48000; 2451 mVideoRecCfg.Audio.cBits = 16; 2452 mVideoRecCfg.Audio.cChannels = 2; 2453 #endif 2454 2455 if (!mVideoRecCfg.Video.uFPS) /* Prevent division by zero. */ 2456 mVideoRecCfg.Video.uFPS = 30; 2457 2458 /* 2459 * Parse options string. 2460 */ 2461 com::Utf8Str key, value; 2462 while ((pos = strOptions.parseKeyValue(key, value, pos)) != com::Utf8Str::npos) 2463 { 2464 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0) 2465 { 2466 #ifdef VBOX_WITH_LIBVPX 2467 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0) 2468 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_REALTIME; 2469 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0) 2470 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = 1000000 / mVideoRecCfg.Video.uFPS; 2471 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0) 2472 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY; 2473 else 2474 { 2475 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str())); 2476 mVideoRecCfg.Video.Codec.VPX.uEncoderDeadline = value.toUInt32(); 2477 #endif 2478 } 2479 } 2480 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0) 2481 { 2482 if (value.compare("false", Utf8Str::CaseInsensitive) == 0) 2483 { 2484 mVideoRecCfg.Video.fEnabled = false; 2485 LogRel(("VideoRec: Only audio will be recorded\n")); 2486 } 2487 } 2488 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0) 2489 { 2490 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2491 if (value.compare("false", Utf8Str::CaseInsensitive)) 2492 { 2493 mVideoRecCfg.Audio.fEnabled = false; 2494 LogRel(("VideoRec: Only video will be recorded\n")); 2495 } 2496 #endif 2497 } 2498 else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0) 2499 { 2500 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2501 if (value.compare("low", Utf8Str::CaseInsensitive) == 0) 2502 { 2503 mVideoRecCfg.Audio.uHz = 8000; 2504 mVideoRecCfg.Audio.cBits = 16; 2505 mVideoRecCfg.Audio.cChannels = 1; 2506 } 2507 else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0) 2508 { 2509 mVideoRecCfg.Audio.uHz = 22050; 2510 mVideoRecCfg.Audio.cBits = 16; 2511 mVideoRecCfg.Audio.cChannels = 2; 2512 } 2513 else if (value.compare("high", Utf8Str::CaseInsensitive) == 0) 2514 { 2515 /* Stay with the default set above. */ 2516 } 2517 #endif 2518 } 2519 else 2520 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str())); 2521 2522 } /* while */ 2523 2524 /* 2525 * Invalidate screens. 2526 */ 2527 for (unsigned i = 0; i < mVideoRecCfg.aScreens.size(); i++) 2528 { 2529 bool fChanged = maVideoRecEnabled[i] != RT_BOOL(mVideoRecCfg.aScreens[i]); 2530 2531 maVideoRecEnabled[i] = RT_BOOL(mVideoRecCfg.aScreens[i]); 2397 2532 2398 2533 if (fChanged && i < mcMonitors) 2399 videoCaptureScreenChanged(i); 2400 2401 } 2402 return VINF_SUCCESS; 2403 #else 2404 ComSafeArrayNoRef(aScreens); 2405 return VERR_NOT_SUPPORTED; 2406 #endif 2534 i_videoCaptureScreenChanged(i); 2535 2536 } 2537 2538 return S_OK; 2407 2539 } 2408 2540 … … 2418 2550 int Display::i_videoCaptureSendAudio(const void *pvData, size_t cbData, uint64_t uTimestampMs) 2419 2551 { 2420 #ifdef VBOX_WITH_AUDIO_VIDEOREC 2421 if (!VideoRecIsEnabled(mpVideoRecCtx)) 2422 return VINF_SUCCESS; 2423 2424 return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs); 2425 #else 2426 RT_NOREF(pvData, cbData, uTimestampMs); 2427 return VERR_NOT_SUPPORTED; 2428 #endif 2552 if ( VideoRecIsActive(mpVideoRecCtx) 2553 && VideoRecGetEnabled(&mVideoRecCfg) & VIDEORECFEATURE_AUDIO) 2554 { 2555 return VideoRecSendAudioFrame(mpVideoRecCtx, pvData, cbData, uTimestampMs); 2556 } 2557 2558 return VINF_SUCCESS; 2429 2559 } 2430 2560 … … 2432 2562 * Start video capturing. Does nothing if capturing is already active. 2433 2563 * 2564 * @param pVideoRecCfg Video recording configuration to use. 2434 2565 * @returns IPRT status code. 2435 2566 */ 2436 2567 int Display::i_videoCaptureStart(void) 2437 2568 { 2438 #ifdef VBOX_WITH_VIDEOREC 2439 if (VideoRecIsEnabled(mpVideoRecCtx)) 2569 if (VideoRecIsActive(mpVideoRecCtx)) 2440 2570 return VINF_SUCCESS; 2441 2571 2442 int rc = VideoRecContextCreate(mcMonitors, &m pVideoRecCtx);2572 int rc = VideoRecContextCreate(mcMonitors, &mVideoRecCfg, &mpVideoRecCtx); 2443 2573 if (RT_FAILURE(rc)) 2444 2574 { … … 2446 2576 return rc; 2447 2577 } 2448 ComPtr<IMachine> pMachine = mParent->i_machine(); 2449 com::SafeArray<BOOL> screens; 2450 HRESULT hrc = pMachine->COMGETTER(VideoCaptureScreens)(ComSafeArrayAsOutParam(screens)); 2451 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2452 for (unsigned i = 0; i < RT_ELEMENTS(maVideoRecEnabled); i++) 2453 maVideoRecEnabled[i] = i < screens.size() && screens[i]; 2454 ULONG ulWidth; 2455 hrc = pMachine->COMGETTER(VideoCaptureWidth)(&ulWidth); 2456 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2457 ULONG ulHeight; 2458 hrc = pMachine->COMGETTER(VideoCaptureHeight)(&ulHeight); 2459 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2460 ULONG ulRate; 2461 hrc = pMachine->COMGETTER(VideoCaptureRate)(&ulRate); 2462 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2463 ULONG ulFPS; 2464 hrc = pMachine->COMGETTER(VideoCaptureFPS)(&ulFPS); 2465 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2466 BSTR strFile; 2467 hrc = pMachine->COMGETTER(VideoCaptureFile)(&strFile); 2468 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2469 ULONG ulMaxTime; 2470 hrc = pMachine->COMGETTER(VideoCaptureMaxTime)(&ulMaxTime); 2471 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2472 ULONG ulMaxSize; 2473 hrc = pMachine->COMGETTER(VideoCaptureMaxFileSize)(&ulMaxSize); 2474 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2475 BSTR strOptions; 2476 hrc = pMachine->COMGETTER(VideoCaptureOptions)(&strOptions); 2477 AssertComRCReturn(hrc, VERR_COM_UNEXPECTED); 2478 2479 RTTIMESPEC ts; 2480 RTTimeNow(&ts); 2481 RTTIME time; 2482 RTTimeExplode(&time, &ts); 2578 2483 2579 for (unsigned uScreen = 0; uScreen < mcMonitors; uScreen++) 2484 2580 { 2485 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(strFile).c_str()); 2486 char *pszSuff = RTPathSuffix(pszAbsPath); 2487 if (pszSuff) 2488 pszSuff = RTStrDup(pszSuff); 2489 RTPathStripSuffix(pszAbsPath); 2490 if (!pszAbsPath) 2491 rc = VERR_INVALID_PARAMETER; 2492 if (!pszSuff) 2493 pszSuff = RTStrDup(".webm"); 2494 char *pszName = NULL; 2581 int rc2 = VideoRecStreamInit(mpVideoRecCtx, uScreen); 2582 if (RT_SUCCESS(rc2)) 2583 { 2584 i_videoCaptureScreenChanged(uScreen); 2585 } 2586 else 2587 LogRel(("Display::VideoCaptureStart: Failed to initialize video recording context #%u (%Rrc)\n", uScreen, rc2)); 2588 2495 2589 if (RT_SUCCESS(rc)) 2496 { 2497 if (mcMonitors > 1) 2498 rc = RTStrAPrintf(&pszName, "%s-%u%s", pszAbsPath, uScreen+1, pszSuff); 2499 else 2500 rc = RTStrAPrintf(&pszName, "%s%s", pszAbsPath, pszSuff); 2501 } 2502 if (RT_SUCCESS(rc)) 2503 { 2504 rc = VideoRecStreamInit(mpVideoRecCtx, uScreen, 2505 pszName, ulWidth, ulHeight, 2506 ulRate, ulFPS, ulMaxTime, 2507 ulMaxSize, com::Utf8Str(strOptions).c_str()); 2508 if (rc == VERR_ALREADY_EXISTS) 2509 { 2510 RTStrFree(pszName); 2511 pszName = NULL; 2512 2513 if (mcMonitors > 1) 2514 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s", 2515 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 2516 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 2517 uScreen+1, pszSuff); 2518 else 2519 rc = RTStrAPrintf(&pszName, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s", 2520 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 2521 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 2522 pszSuff); 2523 if (RT_SUCCESS(rc)) 2524 rc = VideoRecStreamInit(mpVideoRecCtx, uScreen, 2525 pszName, ulWidth, ulHeight, ulRate, 2526 ulFPS, ulMaxTime, 2527 ulMaxSize, com::Utf8Str(strOptions).c_str()); 2528 } 2529 } 2530 2531 if (RT_SUCCESS(rc)) 2532 { 2533 videoCaptureScreenChanged(uScreen); 2534 } 2535 else 2536 LogRel(("Display::VideoCaptureStart: Failed to initialize video recording context #%u, (%Rrc)\n", uScreen, rc)); 2537 2538 RTStrFree(pszName); 2539 RTStrFree(pszSuff); 2540 RTStrFree(pszAbsPath); 2590 rc = rc2; 2541 2591 } 2542 2592 return rc; 2543 #else /* VBOX_WITH_VIDEOREC */2544 return VERR_NOT_SUPPORTED;2545 #endif2546 2593 } 2547 2594 … … 2549 2596 * Stop video capturing. Does nothing if video capturing is not active. 2550 2597 */ 2551 void Display::i_videoCaptureStop() 2552 { 2553 #ifdef VBOX_WITH_VIDEOREC 2554 if (!VideoRecIsEnabled(mpVideoRecCtx)) 2598 void Display::i_videoCaptureStop(void) 2599 { 2600 if (!VideoRecIsActive(mpVideoRecCtx)) 2555 2601 return; 2556 2602 … … 2560 2606 unsigned uScreenId; 2561 2607 for (uScreenId = 0; uScreenId < mcMonitors; ++uScreenId) 2562 videoCaptureScreenChanged(uScreenId); 2563 #endif 2564 } 2565 2566 #ifdef VBOX_WITH_VIDEOREC 2567 void Display::videoCaptureScreenChanged(unsigned uScreenId) 2568 { 2569 if ( !VideoRecIsEnabled(mpVideoRecCtx) 2608 i_videoCaptureScreenChanged(uScreenId); 2609 } 2610 2611 void Display::i_videoCaptureScreenChanged(unsigned uScreenId) 2612 { 2613 if ( !VideoRecIsActive(mpVideoRecCtx) 2570 2614 || !maVideoRecEnabled[uScreenId]) 2571 2615 { … … 3289 3333 3290 3334 #ifdef VBOX_WITH_VIDEOREC 3291 if (VideoRecIsEnabled(pDisplay->mpVideoRecCtx)) 3335 if ( VideoRecIsActive(pDisplay->mpVideoRecCtx) 3336 && VideoRecGetEnabled(&pDisplay->mVideoRecCfg) & VIDEORECFEATURE_VIDEO) 3292 3337 { 3293 3338 do { … … 4485 4530 if (fEnabled) 4486 4531 { 4487 rc = pDisplay->i_videoCaptureStart(); 4488 fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource()); 4532 rc = pDisplay->i_videoCaptureInvalidate(); 4533 if (RT_SUCCESS(rc)) 4534 { 4535 rc = pDisplay->i_videoCaptureStart(); 4536 fireVideoCaptureChangedEvent(pDisplay->mParent->i_getEventSource()); 4537 } 4489 4538 } 4490 4539 #endif -
trunk/src/VBox/Main/src-client/VideoRec.cpp
r68776 r68798 22 22 #include <vector> 23 23 24 #include <VBox/log.h>25 24 #include <iprt/asm.h> 26 25 #include <iprt/assert.h> 27 26 #include <iprt/critsect.h> 27 #include <iprt/path.h> 28 28 #include <iprt/semaphore.h> 29 29 #include <iprt/thread.h> 30 30 #include <iprt/time.h> 31 31 32 #include <VBox/log.h> 32 33 #include <VBox/com/VirtualBox.h> 33 34 #include <VBox/com/com.h> … … 54 55 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight); 55 56 56 int videoRecStreamLock(PVIDEORECSTREAM pStream); 57 int videoRecStreamUnlock(PVIDEORECSTREAM pStream); 57 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream); 58 static void videoRecStreamLock(PVIDEORECSTREAM pStream); 59 static void videoRecStreamUnlock(PVIDEORECSTREAM pStream); 58 60 59 61 using namespace com; … … 114 116 /** VPX image context. */ 115 117 vpx_image_t RawImage; 116 /** Encoder deadline. */117 unsigned int uEncoderDeadline;118 118 } VPX; 119 119 #endif /* VBOX_WITH_LIBVPX */ … … 158 158 typedef struct VIDEORECSTREAM 159 159 { 160 /** Container context. */ 161 WebMWriter *pEBML; 160 /** Video recording context this stream is associated to. */ 161 PVIDEORECCONTEXT pCtx; 162 /** Destination where to write the stream to. */ 163 VIDEORECDEST enmDst; 164 union 165 { 166 struct 167 { 168 /** File handle to use for writing. */ 169 RTFILE hFile; 170 /** File name being used for this stream. */ 171 char *pszFile; 172 /** Pointer to WebM writer instance being used. */ 173 WebMWriter *pWEBM; 174 } File; 175 }; 162 176 #ifdef VBOX_WITH_AUDIO_VIDEOREC 163 177 /** Track number of audio stream. */ … … 204 218 typedef struct VIDEORECCONTEXT 205 219 { 220 /** Used recording configuration. */ 221 VIDEORECCFG Cfg; 206 222 /** The current state. */ 207 223 uint32_t enmState; … … 210 226 /** Semaphore to signal the encoding worker thread. */ 211 227 RTSEMEVENT WaitEvent; 212 /** Whether recordingis enabled or not. */228 /** Whether this conext is enabled or not. */ 213 229 bool fEnabled; 214 230 /** Shutdown indicator. */ … … 216 232 /** Worker thread. */ 217 233 RTTHREAD Thread; 218 /** Maximal time (in ms) to record. */219 uint64_t uMaxTimeMs;220 /** Maximal file size (in MB) to record. */221 uint32_t uMaxSizeMB;222 234 /** Vector of current recording stream contexts. */ 223 235 VideoRecStreams vecStreams; 236 /** Timestamp (in ms) of when recording has been started. */ 237 uint64_t tsStartMs; 224 238 #ifdef VBOX_WITH_AUDIO_VIDEOREC 225 239 struct … … 628 642 629 643 WebMWriter::BlockData_Opus blockData = { audioFrame.abBuf, audioFrame.cbBuf, audioFrame.uTimeStampMs }; 630 rc = pStream-> pEBML->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));644 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData)); 631 645 if (RT_FAILURE(rc)) 632 646 { … … 653 667 * 654 668 * @returns IPRT status code. 655 * @param cScreens Number of screens to create context for. 656 * @param ppCtx Pointer to created video recording context on success. 657 */ 658 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCONTEXT *ppCtx) 659 { 660 AssertReturn(cScreens, VERR_INVALID_PARAMETER); 661 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER); 669 * @param cScreens Number of screens to create context for. 670 * @param pVideoRecCfg Pointer to video recording configuration to use. 671 * @param ppCtx Pointer to created video recording context on success. 672 */ 673 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx) 674 { 675 AssertReturn(cScreens, VERR_INVALID_PARAMETER); 676 AssertPtrReturn(pVideoRecCfg, VERR_INVALID_POINTER); 677 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER); 662 678 663 679 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(sizeof(VIDEORECCONTEXT)); … … 667 683 int rc = RTCritSectInit(&pCtx->CritSect); 668 684 if (RT_FAILURE(rc)) 685 { 686 RTMemFree(pCtx); 669 687 return rc; 688 } 670 689 671 690 for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++) … … 688 707 pCtx->vecStreams.push_back(pStream); 689 708 690 pStream-> pEBML= new WebMWriter();709 pStream->File.pWEBM = new WebMWriter(); 691 710 } 692 711 catch (std::bad_alloc) … … 699 718 if (RT_SUCCESS(rc)) 700 719 { 720 pCtx->tsStartMs = RTTimeMilliTS(); 701 721 pCtx->enmState = VIDEORECSTS_UNINITIALIZED; 702 722 pCtx->fShutdown = false; 703 723 724 /* Copy the configuration to our context. */ 725 pCtx->Cfg = *pVideoRecCfg; 726 704 727 rc = RTSemEventCreate(&pCtx->WaitEvent); 705 728 AssertRCReturn(rc, rc); … … 723 746 if (RT_FAILURE(rc)) 724 747 { 725 /* Roll back allocations on error. */ 748 int rc2 = VideoRecContextDestroy(pCtx); 749 AssertRC(rc2); 750 } 751 752 return rc; 753 } 754 755 /** 756 * Destroys a video recording context. 757 * 758 * @param pCtx Video recording context to destroy. 759 */ 760 int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx) 761 { 762 if (!pCtx) 763 return VINF_SUCCESS; 764 765 /* First, disable the context. */ 766 ASMAtomicWriteBool(&pCtx->fEnabled, false); 767 768 if (pCtx->enmState == VIDEORECSTS_INITIALIZED) 769 { 770 /* Set shutdown indicator. */ 771 ASMAtomicWriteBool(&pCtx->fShutdown, true); 772 773 /* Signal the thread. */ 774 RTSemEventSignal(pCtx->WaitEvent); 775 776 int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL); 777 if (RT_FAILURE(rc)) 778 return rc; 779 780 rc = RTSemEventDestroy(pCtx->WaitEvent); 781 AssertRC(rc); 782 783 pCtx->WaitEvent = NIL_RTSEMEVENT; 784 } 785 786 int rc = RTCritSectEnter(&pCtx->CritSect); 787 if (RT_SUCCESS(rc)) 788 { 726 789 VideoRecStreams::iterator it = pCtx->vecStreams.begin(); 727 790 while (it != pCtx->vecStreams.end()) … … 729 792 PVIDEORECSTREAM pStream = (*it); 730 793 731 if (pStream->pEBML) 732 delete pStream->pEBML; 733 734 it = pCtx->vecStreams.erase(it); 735 736 RTMemFree(pStream); 737 pStream = NULL; 738 } 739 740 Assert(pCtx->vecStreams.empty()); 741 } 742 743 return rc; 744 } 745 746 /** 747 * Destroys a video recording context. 748 * 749 * @param pCtx Video recording context to destroy. 750 */ 751 int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx) 752 { 753 if (!pCtx) 754 return VINF_SUCCESS; 755 756 /* Set shutdown indicator. */ 757 ASMAtomicWriteBool(&pCtx->fShutdown, true); 758 759 /* Signal the thread. */ 760 RTSemEventSignal(pCtx->WaitEvent); 761 762 int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL); 763 if (RT_FAILURE(rc)) 764 return rc; 765 766 rc = RTSemEventDestroy(pCtx->WaitEvent); 767 AssertRC(rc); 768 769 pCtx->WaitEvent = NIL_RTSEMEVENT; 770 771 VideoRecStreams::iterator it = pCtx->vecStreams.begin(); 772 while (it != pCtx->vecStreams.end()) 773 { 774 PVIDEORECSTREAM pStream = (*it); 775 776 if (pStream->fEnabled) 777 { 778 AssertPtr(pStream->pEBML); 779 pStream->pEBML->Close(); 780 781 vpx_img_free(&pStream->Video.Codec.VPX.RawImage); 782 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx); 783 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 794 videoRecStreamLock(pStream); 795 796 if (pStream->fEnabled) 797 { 798 switch (pStream->enmDst) 799 { 800 case VIDEORECDEST_FILE: 801 { 802 if (pStream->File.pWEBM) 803 pStream->File.pWEBM->Close(); 804 break; 805 } 806 807 default: 808 AssertFailed(); /* Should never happen. */ 809 break; 810 } 811 812 vpx_img_free(&pStream->Video.Codec.VPX.RawImage); 813 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx); 814 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 784 815 785 816 #ifdef VBOX_VIDEOREC_WITH_QUEUE 786 817 # error "Implement me!" 787 818 #else 788 PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame; 789 #endif 790 if (pFrame->pu8RGBBuf) 819 PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame; 820 #endif 821 if (pFrame->pu8RGBBuf) 822 { 823 Assert(pFrame->cbRGBBuf); 824 825 RTMemFree(pFrame->pu8RGBBuf); 826 pFrame->pu8RGBBuf = NULL; 827 } 828 829 pFrame->cbRGBBuf = 0; 830 831 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID)); 832 } 833 834 switch (pStream->enmDst) 791 835 { 792 Assert(pFrame->cbRGBBuf); 793 794 RTMemFree(pFrame->pu8RGBBuf); 795 pFrame->pu8RGBBuf = NULL; 836 case VIDEORECDEST_FILE: 837 { 838 int rc2 = videoRecStreamCloseFile(pStream); 839 AssertRC(rc2); 840 841 if (pStream->File.pWEBM) 842 { 843 delete pStream->File.pWEBM; 844 pStream->File.pWEBM = NULL; 845 } 846 break; 847 } 848 849 default: 850 AssertFailed(); /* Should never happen. */ 851 break; 796 852 } 797 853 798 pFrame->cbRGBBuf = 0; 799 800 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID)); 801 } 802 803 if (pStream->pEBML) 804 { 805 delete pStream->pEBML; 806 pStream->pEBML = NULL; 807 } 808 809 it = pCtx->vecStreams.erase(it); 810 811 RTCritSectDelete(&pStream->CritSect); 812 813 RTMemFree(pStream); 814 pStream = NULL; 815 } 816 817 Assert(pCtx->vecStreams.empty()); 818 819 RTCritSectDelete(&pCtx->CritSect); 820 821 RTMemFree(pCtx); 822 pCtx = NULL; 823 824 return VINF_SUCCESS; 854 it = pCtx->vecStreams.erase(it); 855 856 videoRecStreamUnlock(pStream); 857 858 RTCritSectDelete(&pStream->CritSect); 859 860 RTMemFree(pStream); 861 pStream = NULL; 862 } 863 864 Assert(pCtx->vecStreams.empty()); 865 866 int rc2 = RTCritSectLeave(&pCtx->CritSect); 867 AssertRC(rc2); 868 869 RTCritSectDelete(&pCtx->CritSect); 870 871 RTMemFree(pCtx); 872 pCtx = NULL; 873 } 874 875 return rc; 825 876 } 826 877 … … 853 904 * Locks a recording stream. 854 905 * 855 * @returns IPRT status code.856 906 * @param pStream Recording stream to lock. 857 907 */ 858 intvideoRecStreamLock(PVIDEORECSTREAM pStream)908 static void videoRecStreamLock(PVIDEORECSTREAM pStream) 859 909 { 860 910 int rc = RTCritSectEnter(&pStream->CritSect); 861 911 AssertRC(rc); 862 863 return rc;864 912 } 865 913 … … 867 915 * Unlocks a locked recording stream. 868 916 * 869 * @returns IPRT status code.870 917 * @param pStream Recording stream to unlock. 871 918 */ 872 intvideoRecStreamUnlock(PVIDEORECSTREAM pStream)919 static void videoRecStreamUnlock(PVIDEORECSTREAM pStream) 873 920 { 874 921 int rc = RTCritSectLeave(&pStream->CritSect); 875 922 AssertRC(rc); 923 } 924 925 /** 926 * Opens a file for a given recording stream to capture to. 927 * 928 * @returns IPRT status code. 929 * @param pStream Recording stream to open file for. 930 * @param pCfg Recording configuration to use. 931 */ 932 int videoRecStreamOpenFile(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 933 { 934 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 935 AssertPtrReturn(pCfg, VERR_INVALID_POINTER); 936 937 Assert(pStream->enmDst == VIDEORECDEST_INVALID); 938 Assert(pCfg->enmDst == VIDEORECDEST_FILE); 939 940 RTTIMESPEC ts; 941 RTTimeNow(&ts); 942 943 RTTIME time; 944 RTTimeExplode(&time, &ts); 945 946 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(pCfg->File.strFile).c_str()); 947 AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY); 948 949 char *pszSuff = RTPathSuffix(pszAbsPath); 950 if (pszSuff) 951 pszSuff = RTStrDup(pszSuff); 952 953 RTPathStripSuffix(pszAbsPath); 954 AssertPtrReturn(pszAbsPath, VERR_INVALID_PARAMETER); 955 956 if (!pszSuff) 957 pszSuff = RTStrDup(".webm"); 958 959 char *pszFile = NULL; 960 961 int rc; 962 if (pStream->uScreenID > 1) 963 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff); 964 else 965 rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff); 966 967 if (RT_SUCCESS(rc)) 968 { 969 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; 970 #ifdef DEBUG 971 fOpen |= RTFILE_O_CREATE_REPLACE; 972 #else 973 /* Play safe: the file must not exist, overwriting is potentially 974 * hazardous as nothing prevents the user from picking a file name of some 975 * other important file, causing unintentional data loss. */ 976 fOpen |= RTFILE_O_CREATE; 977 #endif 978 RTFILE hFile; 979 rc = RTFileOpen(&hFile, pszFile, fOpen); 980 if (RT_SUCCESS(rc)) 981 { 982 pStream->enmDst = VIDEORECDEST_FILE; 983 pStream->File.hFile = hFile; 984 pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */ 985 } 986 else 987 RTStrFree(pszFile); 988 } 989 990 RTStrFree(pszSuff); 991 RTStrFree(pszAbsPath); 992 993 if (RT_FAILURE(rc)) 994 LogRel(("VideoRec: Failed to open file for screen %RU32, rc=%Rrc\n", pStream->uScreenID, rc)); 876 995 877 996 return rc; 997 } 998 999 /** 1000 * Closes a recording stream's file again. 1001 * 1002 * @returns IPRT status code. 1003 * @param pStream Recording stream to close file for. 1004 */ 1005 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream) 1006 { 1007 Assert(pStream->enmDst == VIDEORECDEST_FILE); 1008 1009 pStream->enmDst = VIDEORECDEST_INVALID; 1010 1011 AssertPtr(pStream->File.pszFile); 1012 1013 if (RTFileIsValid(pStream->File.hFile)) 1014 { 1015 RTFileClose(pStream->File.hFile); 1016 LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile)); 1017 } 1018 1019 RTStrFree(pStream->File.pszFile); 1020 pStream->File.pszFile = NULL; 1021 1022 return VINF_SUCCESS; 878 1023 } 879 1024 … … 884 1029 * @param pCtx Pointer to video recording context. 885 1030 * @param uScreen Screen number to record. 886 * @param pszFile File to save recording to. 887 * @param uWidth Target video resolution (width). 888 * @param uHeight Target video resolution (height). 889 * @param uRate Target encoding bit rate. 890 * @param uFPS Target FPS (Frame Per Second). 891 * @param uMaxTimeS Maximum time (in s) to record, or 0 for no time limit. 892 * @param uMaxSizeMB Maximum file size (in MB) to record, or 0 for no limit. 893 * @param pszOptions Additional options in "key=value" array format. Optional. 894 */ 895 int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile, 896 uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFPS, 897 uint32_t uMaxTimeS, uint32_t uMaxSizeMB, const char *pszOptions) 898 { 899 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 900 AssertPtrReturn(pszFile, VERR_INVALID_POINTER); 901 AssertReturn(uWidth, VERR_INVALID_PARAMETER); 902 AssertReturn(uHeight, VERR_INVALID_PARAMETER); 903 AssertReturn(uRate, VERR_INVALID_PARAMETER); 904 AssertReturn(uFPS, VERR_INVALID_PARAMETER); 905 /* pszOptions is optional. */ 1031 */ 1032 int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen) 1033 { 1034 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 906 1035 907 1036 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen); … … 909 1038 return VERR_NOT_FOUND; 910 1039 911 pCtx->uMaxTimeMs = (uMaxTimeS > 0 ? RTTimeProgramMilliTS() + uMaxTimeS * 1000 : 0); 912 pCtx->uMaxSizeMB = uMaxSizeMB; 913 914 pStream->Video.uWidth = uWidth; 915 pStream->Video.uHeight = uHeight; 1040 int rc = videoRecStreamOpenFile(pStream, &pCtx->Cfg); 1041 if (RT_FAILURE(rc)) 1042 return rc; 1043 1044 PVIDEORECCFG pCfg = &pCtx->Cfg; 1045 1046 pStream->pCtx = pCtx; 1047 /** @todo Make the following parameters configurable on a per-stream basis? */ 1048 pStream->Video.uWidth = pCfg->Video.uWidth; 1049 pStream->Video.uHeight = pCfg->Video.uHeight; 916 1050 917 1051 #ifndef VBOX_VIDEOREC_WITH_QUEUE … … 933 1067 934 1068 #ifdef VBOX_WITH_LIBVPX 935 pVC->VPX.uEncoderDeadline = VPX_DL_REALTIME;936 937 1069 vpx_codec_err_t rcv = vpx_codec_enc_config_default(DEFAULTCODEC, &pVC->VPX.Cfg, 0); 938 1070 if (rcv != VPX_CODEC_OK) … … 943 1075 #endif 944 1076 945 com::Utf8Str options(pszOptions); 946 size_t pos = 0; 947 948 /* By default we only enable video recording for now. 949 * Audio support is considered as being experimental. There be dragons! */ 950 bool fHasVideoTrack = true; 1077 pStream->Video.uDelayMs = 1000 / pCfg->Video.uFPS; 1078 1079 switch (pStream->enmDst) 1080 { 1081 case VIDEORECDEST_FILE: 1082 { 1083 rc = pStream->File.pWEBM->CreateEx(pStream->File.pszFile, &pStream->File.hFile, 951 1084 #ifdef VBOX_WITH_AUDIO_VIDEOREC 952 bool fHasAudioTrack = false; 953 /* By default we use 48kHz, 16-bit, stereo for the audio track. */ 954 uint16_t uAudioHz = 48000; 955 uint8_t uAudioBits = 16; 956 uint8_t uAudioChannels = 2; 957 #endif 958 959 com::Utf8Str key, value; 960 while ((pos = options.parseKeyValue(key, value, pos)) != com::Utf8Str::npos) 961 { 962 if (key.compare("vc_quality", Utf8Str::CaseInsensitive) == 0) 963 { 964 if (value.compare("realtime", Utf8Str::CaseInsensitive) == 0) 1085 pCfg->Audio.fEnabled ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None, 1086 #else 1087 WebMWriter::AudioCodec_None, 1088 #endif 1089 pCfg->Video.fEnabled ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None); 1090 if (RT_FAILURE(rc)) 965 1091 { 966 #ifdef VBOX_WITH_LIBVPX 967 pVC->VPX.uEncoderDeadline = VPX_DL_REALTIME; 968 #endif 1092 LogRel(("VideoRec: Failed to create the capture output file '%s' (%Rrc)\n", pStream->File.pszFile, rc)); 1093 break; 969 1094 } 970 else if (value.compare("good", Utf8Str::CaseInsensitive) == 0) 1095 1096 const char *pszFile = pStream->File.pszFile; 1097 1098 if (pCfg->Video.fEnabled) 971 1099 { 972 pVC->VPX.uEncoderDeadline = 1000000 / uFPS; 1100 rc = pStream->File.pWEBM->AddVideoTrack(pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uFPS, 1101 &pStream->uTrackVideo); 1102 if (RT_FAILURE(rc)) 1103 { 1104 LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc)); 1105 break; 1106 } 1107 1108 LogRel(("VideoRec: Recording screen #%u with %RU32x%RU32 @ %RU32 kbps, %u FPS\n", 1109 uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS)); 973 1110 } 974 else if (value.compare("best", Utf8Str::CaseInsensitive) == 0) 1111 1112 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1113 if (pCfg->Audio.fEnabled) 975 1114 { 976 #ifdef VBOX_WITH_LIBVPX 977 pVC->VPX.uEncoderDeadline = VPX_DL_BEST_QUALITY; 978 #endif 1115 rc = pStream->File.pWEBM->AddAudioTrack(pCfg->Audio.uHz, pCfg->Audio.cChannels, pCfg->Audio.cBits, 1116 &pStream->uTrackAudio); 1117 if (RT_FAILURE(rc)) 1118 { 1119 LogRel(("VideoRec: Failed to add audio track to output file '%s' (%Rrc)\n", pszFile, rc)); 1120 break; 1121 } 1122 1123 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s\n", 1124 pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channel" : "channels")); 979 1125 } 980 else 1126 #endif 1127 1128 if ( pCfg->Video.fEnabled 1129 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1130 || pCfg->Audio.fEnabled 1131 #endif 1132 ) 981 1133 { 982 LogRel(("VideoRec: Setting quality deadline to '%s'\n", value.c_str())); 983 pVC->VPX.uEncoderDeadline = value.toUInt32(); 1134 char szWhat[32] = { 0 }; 1135 if (pCfg->Video.fEnabled) 1136 RTStrCat(szWhat, sizeof(szWhat), "video"); 1137 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1138 if (pCfg->Audio.fEnabled) 1139 { 1140 if (pCfg->Video.fEnabled) 1141 RTStrCat(szWhat, sizeof(szWhat), " + "); 1142 RTStrCat(szWhat, sizeof(szWhat), "audio"); 1143 } 1144 #endif 1145 LogRel(("VideoRec: Recording %s to '%s'\n", szWhat, pszFile)); 984 1146 } 985 } 986 else if (key.compare("vc_enabled", Utf8Str::CaseInsensitive) == 0) 987 { 988 #ifdef VBOX_WITH_AUDIO_VIDEOREC 989 if (value.compare("false", Utf8Str::CaseInsensitive) == 0) 990 { 991 fHasVideoTrack = false; 992 LogRel(("VideoRec: Only audio will be recorded\n")); 993 } 994 #endif 995 } 996 else if (key.compare("ac_enabled", Utf8Str::CaseInsensitive) == 0) 997 { 998 #ifdef VBOX_WITH_AUDIO_VIDEOREC 999 if (value.compare("false", Utf8Str::CaseInsensitive)) 1000 { 1001 fHasAudioTrack = false; 1002 LogRel(("VideoRec: Only video will be recorded\n")); 1003 } 1004 #endif 1005 } 1006 else if (key.compare("ac_profile", Utf8Str::CaseInsensitive) == 0) 1007 { 1008 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1009 if (value.compare("low", Utf8Str::CaseInsensitive) == 0) 1010 { 1011 uAudioHz = 8000; 1012 uAudioBits = 16; 1013 uAudioChannels = 1; 1014 } 1015 else if (value.startsWith("med" /* "med[ium]" */, Utf8Str::CaseInsensitive) == 0) 1016 { 1017 uAudioHz = 22050; 1018 uAudioBits = 16; 1019 uAudioChannels = 2; 1020 } 1021 else if (value.compare("high", Utf8Str::CaseInsensitive) == 0) 1022 { 1023 /* Stay with the default set above. */ 1024 } 1025 #endif 1026 } 1027 else 1028 LogRel(("VideoRec: Unknown option '%s' (value '%s'), skipping\n", key.c_str(), value.c_str())); 1029 1030 } /* while */ 1031 1032 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; 1033 #ifdef DEBUG 1034 fOpen |= RTFILE_O_CREATE_REPLACE; 1035 #else 1036 /* Play safe: the file must not exist, overwriting is potentially 1037 * hazardous as nothing prevents the user from picking a file name of some 1038 * other important file, causing unintentional data loss. */ 1039 fOpen |= RTFILE_O_CREATE; 1040 #endif 1041 1042 int rc = pStream->pEBML->Create(pszFile, fOpen, 1043 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1044 fHasAudioTrack ? WebMWriter::AudioCodec_Opus : WebMWriter::AudioCodec_None, 1045 #else 1046 WebMWriter::AudioCodec_None, 1047 #endif 1048 fHasVideoTrack ? WebMWriter::VideoCodec_VP8 : WebMWriter::VideoCodec_None); 1147 1148 break; 1149 } 1150 1151 default: 1152 AssertFailed(); /* Should never happen. */ 1153 rc = VERR_NOT_IMPLEMENTED; 1154 break; 1155 } 1156 1049 1157 if (RT_FAILURE(rc)) 1050 {1051 LogRel(("VideoRec: Failed to create the video capture output file '%s' (%Rrc)\n", pszFile, rc));1052 1158 return rc; 1053 }1054 1055 pStream->Video.uDelayMs = 1000 / uFPS;1056 1057 if (fHasVideoTrack)1058 {1059 rc = pStream->pEBML->AddVideoTrack(uWidth, uHeight, uFPS, &pStream->uTrackVideo);1060 if (RT_FAILURE(rc))1061 {1062 LogRel(("VideoRec: Failed to add video track to output file '%s' (%Rrc)\n", pszFile, rc));1063 return rc;1064 }1065 1066 LogRel(("VideoRec: Recording screen #%u with %ux%u @ %u kbps, %u FPS\n",1067 uScreen, uWidth, uHeight, uRate, uFPS));1068 }1069 1070 #ifdef VBOX_WITH_AUDIO_VIDEOREC1071 if (fHasAudioTrack)1072 {1073 rc = pStream->pEBML->AddAudioTrack(uAudioHz, uAudioChannels, uAudioBits, &pStream->uTrackAudio);1074 if (RT_FAILURE(rc))1075 {1076 LogRel(("VideoRec: Failed to add audio track to output file '%s' (%Rrc)\n", pszFile, rc));1077 return rc;1078 }1079 1080 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s\n",1081 uAudioHz, uAudioBits, uAudioChannels, uAudioChannels == 1 ? "channel" : "channels"));1082 }1083 #endif1084 1085 if ( fHasVideoTrack1086 #ifdef VBOX_WITH_AUDIO_VIDEOREC1087 || fHasAudioTrack1088 #endif1089 )1090 {1091 char szWhat[32] = { 0 };1092 if (fHasVideoTrack)1093 RTStrCat(szWhat, sizeof(szWhat), "video");1094 #ifdef VBOX_WITH_AUDIO_VIDEOREC1095 if (fHasAudioTrack)1096 {1097 if (fHasVideoTrack)1098 RTStrCat(szWhat, sizeof(szWhat), " + ");1099 RTStrCat(szWhat, sizeof(szWhat), "audio");1100 }1101 #endif1102 LogRel(("Recording %s to '%s'\n", szWhat, pszFile));1103 }1104 1159 1105 1160 #ifdef VBOX_WITH_LIBVPX 1106 1161 /* Target bitrate in kilobits per second. */ 1107 pVC->VPX.Cfg.rc_target_bitrate = uRate;1162 pVC->VPX.Cfg.rc_target_bitrate = pCfg->Video.uRate; 1108 1163 /* Frame width. */ 1109 pVC->VPX.Cfg.g_w = uWidth;1164 pVC->VPX.Cfg.g_w = pCfg->Video.uWidth; 1110 1165 /* Frame height. */ 1111 pVC->VPX.Cfg.g_h = uHeight;1166 pVC->VPX.Cfg.g_h = pCfg->Video.uHeight; 1112 1167 /* 1ms per frame. */ 1113 1168 pVC->VPX.Cfg.g_timebase.num = 1; … … 1120 1175 if (rcv != VPX_CODEC_OK) 1121 1176 { 1122 LogFlow(("Failed to initialize VP8 encoder %s", vpx_codec_err_to_string(rcv)));1177 LogFlow(("Failed to initialize VP8 encoder: %s\n", vpx_codec_err_to_string(rcv))); 1123 1178 return VERR_INVALID_PARAMETER; 1124 1179 } 1125 1180 1126 if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, uWidth,uHeight, 1))1127 { 1128 LogFlow(("Failed to allocate image % dx%d", uWidth,uHeight));1181 if (!vpx_img_alloc(&pVC->VPX.RawImage, VPX_IMG_FMT_I420, pCfg->Video.uWidth, pCfg->Video.uHeight, 1)) 1182 { 1183 LogFlow(("Failed to allocate image %RU32x%RU32\n", pCfg->Video.uWidth, pCfg->Video.uHeight)); 1129 1184 return VERR_NO_MEMORY; 1130 1185 } … … 1139 1194 1140 1195 /** 1141 * VideoRec utility function to check if recording is enabled. 1142 * 1143 * @returns true if recording is enabled. 1144 * @param pCtx Pointer to video recording context. 1145 */ 1146 bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx) 1147 { 1148 if (!pCtx) 1149 return false; 1150 1151 return ASMAtomicReadBool(&pCtx->fEnabled); 1152 } 1153 1154 /** 1155 * VideoRec utility function to check if recording engine is ready to accept a new frame 1156 * for the given screen. 1196 * Returns which recording features currently are enabled for a given configuration. 1197 * 1198 * @returns Enabled video recording features. 1199 * @param pCtx Pointer to recording configuration. 1200 */ 1201 VIDEORECFEATURES VideoRecGetEnabled(PVIDEORECCFG pCfg) 1202 { 1203 if ( !pCfg 1204 || !pCfg->fEnabled) 1205 { 1206 return VIDEORECFEATURE_NONE; 1207 } 1208 1209 VIDEORECFEATURES fFeatures = VIDEORECFEATURE_NONE; 1210 1211 if (pCfg->Video.fEnabled) 1212 fFeatures |= VIDEORECFEATURE_VIDEO; 1213 1214 if (pCfg->Audio.fEnabled) 1215 fFeatures |= VIDEORECFEATURE_AUDIO; 1216 1217 return fFeatures; 1218 } 1219 1220 /** 1221 * Checks if recording engine is ready to accept a new frame for the given screen. 1157 1222 * 1158 1223 * @returns true if recording engine is ready. … … 1184 1249 1185 1250 /** 1186 * VideoRec utility function to check if a specified limit for recording 1187 * has been reached. 1251 * Returns whether video recording for a given recording context is active or not. 1252 * 1253 * @returns true if active, false if not. 1254 * @param pCtx Pointer to video recording context. 1255 */ 1256 bool VideoRecIsActive(PVIDEORECCONTEXT pCtx) 1257 { 1258 if (!pCtx) 1259 return false; 1260 1261 return ASMAtomicReadBool(&pCtx->fEnabled); 1262 } 1263 1264 /** 1265 * Checks if a specified limit for recording has been reached. 1188 1266 * 1189 1267 * @returns true if any limit has been reached. … … 1192 1270 * @param tsNowMs Current time stamp (in ms). 1193 1271 */ 1194 1195 1272 bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs) 1196 1273 { … … 1202 1279 } 1203 1280 1204 if ( pCtx->uMaxTimeMs 1205 && tsNowMs >= pCtx->uMaxTimeMs) 1281 const PVIDEORECCFG pCfg = &pCtx->Cfg; 1282 1283 if ( pCfg->uMaxTimeS 1284 && tsNowMs >= pCtx->tsStartMs + (pCfg->uMaxTimeS * 1000)) 1206 1285 { 1207 1286 return true; 1208 1287 } 1209 1288 1210 if (pCtx->uMaxSizeMB) 1211 { 1212 uint64_t sizeInMB = pStream->pEBML->GetFileSize() / (1024 * 1024); 1213 if(sizeInMB >= pCtx->uMaxSizeMB) 1289 if (pCfg->enmDst == VIDEORECDEST_FILE) 1290 { 1291 1292 if (pCfg->File.uMaxSizeMB) 1293 { 1294 uint64_t sizeInMB = pStream->File.pWEBM->GetFileSize() / (1024 * 1024); 1295 if(sizeInMB >= pCfg->File.uMaxSizeMB) 1296 return true; 1297 } 1298 1299 /* Check for available free disk space */ 1300 if ( pStream->File.pWEBM 1301 && pStream->File.pWEBM->GetAvailableSpace() < 0x100000) /**@todo r=andy WTF? Fix this. */ 1302 { 1303 LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n")); 1214 1304 return true; 1215 } 1216 1217 /* Check for available free disk space */ 1218 if ( pStream->pEBML 1219 && pStream->pEBML->GetAvailableSpace() < 0x100000) /**@todo r=andy WTF? Fix this. */ 1220 { 1221 LogRel(("VideoRec: Not enough free storage space available, stopping video capture\n")); 1222 return true; 1305 } 1223 1306 } 1224 1307 … … 1227 1310 1228 1311 /** 1229 * VideoRec utility function to encode the source image and write the encoded 1230 * image to target file. 1312 * Encodes the source image and write the encoded image to the stream's destination. 1231 1313 * 1232 1314 * @returns IPRT status code. … … 1236 1318 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, PVIDEORECVIDEOFRAME pFrame) 1237 1319 { 1320 AssertPtrReturn(pStream, VERR_INVALID_POINTER); 1321 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); 1322 1238 1323 int rc; 1239 1324 1240 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 1241 1325 AssertPtr(pStream->pCtx); 1326 PVIDEORECCFG pCfg = &pStream->pCtx->Cfg; 1327 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 1242 1328 #ifdef VBOX_WITH_LIBVPX 1243 1329 /* Presentation Time Stamp (PTS). */ … … 1245 1331 vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx, 1246 1332 &pVC->VPX.RawImage, 1247 pts /* Time stamp */,1248 pStream->Video.uDelayMs /* How long to show this frame */,1249 0 /* Flags */,1250 p VC->VPX.uEncoderDeadline /* Quality setting */);1333 pts /* Time stamp */, 1334 pStream->Video.uDelayMs /* How long to show this frame */, 1335 0 /* Flags */, 1336 pCfg->Video.Codec.VPX.uEncoderDeadline /* Quality setting */); 1251 1337 if (rcv != VPX_CODEC_OK) 1252 1338 { … … 1268 1354 { 1269 1355 WebMWriter::BlockData_VP8 blockData = { &pVC->VPX.Cfg, pPacket }; 1270 rc = pStream-> pEBML->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData));1356 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackVideo, &blockData, sizeof(blockData)); 1271 1357 break; 1272 1358 } … … 1286 1372 1287 1373 /** 1288 * VideoRec utility function to convert RGB to YUV.1374 * Converts a RGB to YUV buffer. 1289 1375 * 1290 1376 * @returns IPRT status code. … … 1365 1451 1366 1452 /** 1367 * VideoRec utility function to copy a source video frame to the intermediate1368 * RGB buffer.This function is executed only once per time.1453 * Copies a source video frame to the intermediate RGB buffer. 1454 * This function is executed only once per time. 1369 1455 * 1370 1456 * @thread EMT … … 1397 1483 return VERR_NOT_FOUND; 1398 1484 1399 int rc = RTCritSectEnter(&pStream->CritSect);1400 if (RT_FAILURE(rc)) 1401 return rc;1485 videoRecStreamLock(pStream); 1486 1487 int rc = VINF_SUCCESS; 1402 1488 1403 1489 do … … 1570 1656 } while (0); 1571 1657 1572 int rc2 = RTCritSectLeave(&pStream->CritSect); 1573 if (RT_SUCCESS(rc2)) 1574 { 1575 if ( RT_SUCCESS(rc) 1576 && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */ 1577 { 1578 rc2 = RTSemEventSignal(pCtx->WaitEvent); 1579 } 1580 } 1581 1582 if (RT_SUCCESS(rc)) 1583 rc = rc2; 1658 videoRecStreamUnlock(pStream); 1659 1660 if ( RT_SUCCESS(rc) 1661 && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */ 1662 { 1663 int rc2 = RTSemEventSignal(pCtx->WaitEvent); 1664 AssertRC(rc2); 1665 } 1584 1666 1585 1667 return rc; -
trunk/src/VBox/Main/src-client/VideoRec.h
r65435 r68798 19 19 #define ____H_VIDEOREC 20 20 21 #include <VBox/com/array.h> 22 21 23 struct VIDEORECCONTEXT; 22 24 typedef struct VIDEORECCONTEXT *PVIDEORECCONTEXT; … … 25 27 typedef struct VIDEORECSTREAM *PVIDEORECSTREAM; 26 28 27 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCONTEXT *ppCtx); 29 /** 30 * Enumeration for video recording destinations. 31 */ 32 typedef enum VIDEORECDEST 33 { 34 /** Invalid destination, do not use. */ 35 VIDEORECDEST_INVALID = 0, 36 /** Write to a file. */ 37 VIDEORECDEST_FILE = 1 38 } VIDEORECDEST; 39 40 /** 41 * Enumeration for definining a video / audio 42 * profile setting. 43 */ 44 typedef enum VIDEORECPROFILE 45 { 46 VIDEORECPROFILE_NONE = 0, 47 VIDEORECPROFILE_LOW, 48 VIDEORECPROFILE_MEDIUM, 49 VIDEORECPROFILE_HIGH, 50 VIDEORECPROFILE_BEST, 51 VIDEORECPROFILE_REALTIME 52 } VIDEORECPROFILE; 53 54 /** 55 * Structure for keeping a video recording configuration. 56 */ 57 typedef struct VIDEORECCFG 58 { 59 /** Whether recording is enabled or not (as a whole). */ 60 bool fEnabled; 61 /** */ 62 com::SafeArray<BOOL> aScreens; 63 /** Destination where to write the stream to. */ 64 VIDEORECDEST enmDst; 65 union 66 { 67 /** 68 * Structure for keeping recording parameters if 69 * destination is a file. 70 */ 71 struct 72 { 73 BSTR strFile; 74 /** Maximum file size (in MB) to record. */ 75 uint32_t uMaxSizeMB; 76 } File; 77 }; 78 79 #ifdef VBOX_WITH_AUDIO_VIDEOREC 80 /** 81 * Structure for keeping the audio recording parameters. 82 */ 83 struct 84 { 85 bool fEnabled; 86 uint16_t uHz; 87 uint8_t cBits; 88 uint8_t cChannels; 89 VIDEORECPROFILE enmProfile; 90 } Audio; 91 #endif 92 93 /** 94 * Structure for keeping the video recording parameters. 95 */ 96 struct 97 { 98 bool fEnabled; 99 uint32_t uWidth; 100 uint32_t uHeight; 101 uint32_t uRate; 102 uint32_t uFPS; 103 104 #ifdef VBOX_WITH_LIBVPX 105 union 106 { 107 struct 108 { 109 /** Encoder deadline. */ 110 unsigned int uEncoderDeadline; 111 } VPX; 112 } Codec; 113 #endif 114 115 } Video; 116 /** Maximum time (in s) to record. 117 * Specify 0 to disable this check. */ 118 uint32_t uMaxTimeS; 119 120 VIDEORECCFG& operator=(const VIDEORECCFG &that) 121 { 122 fEnabled = that.fEnabled; 123 that.aScreens.cloneTo(aScreens); 124 enmDst = that.enmDst; 125 126 File.strFile = that.File.strFile; 127 File.uMaxSizeMB = that.File.uMaxSizeMB; 128 #ifdef VBOX_WITH_AUDIO_VIDEOREC 129 Audio = that.Audio; 130 #endif 131 Video = that.Video; 132 uMaxTimeS = that.uMaxTimeS; 133 return *this; 134 } 135 136 } VIDEORECCFG, *PVIDEORECCFG; 137 138 /** Stores video recording features. */ 139 typedef uint32_t VIDEORECFEATURES; 140 141 /** Video recording is disabled completely. */ 142 #define VIDEORECFEATURE_NONE 0 143 /** Capturing video is enabled. */ 144 #define VIDEORECFEATURE_VIDEO RT_BIT(0) 145 /** Capturing audio is enabled. */ 146 #define VIDEORECFEATURE_AUDIO RT_BIT(1) 147 148 int VideoRecContextCreate(uint32_t cScreens, PVIDEORECCFG pVideoRecCfg, PVIDEORECCONTEXT *ppCtx); 28 149 int VideoRecContextDestroy(PVIDEORECCONTEXT pCtx); 29 150 30 int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen, const char *pszFile, 31 uint32_t uWidth, uint32_t uHeight, uint32_t uRate, uint32_t uFPS, 32 uint32_t uMaxTimeS, uint32_t uMaxFileSizeMB, const char *pszOptions); 151 int VideoRecStreamInit(PVIDEORECCONTEXT pCtx, uint32_t uScreen); 152 int VideoRecStreamUninit(PVIDEORECCONTEXT pCtx, uint32_t uScreen); 33 153 34 bool VideoRecIsEnabled(PVIDEORECCONTEXT pCtx); 35 int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs); 36 int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen, 154 VIDEORECFEATURES VideoRecGetEnabled(PVIDEORECCFG pCfg); 155 156 int VideoRecSendAudioFrame(PVIDEORECCONTEXT pCtx, const void *pvData, size_t cbData, uint64_t uTimestampMs); 157 int VideoRecSendVideoFrame(PVIDEORECCONTEXT pCtx, uint32_t uScreen, 37 158 uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, 38 159 uint32_t uBytesPerLine, uint32_t uSrcWidth, uint32_t uSrcHeight, 39 160 uint8_t *puSrcData, uint64_t uTimeStampMs); 40 161 bool VideoRecIsReady(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t uTimeStampMs); 162 bool VideoRecIsActive(PVIDEORECCONTEXT pCtx); 41 163 bool VideoRecIsLimitReached(PVIDEORECCONTEXT pCtx, uint32_t uScreen, uint64_t tsNowMs); 42 164
Note:
See TracChangeset
for help on using the changeset viewer.