Changeset 105006 in vbox for trunk/src/VBox/Main/src-client
- Timestamp:
- Jun 24, 2024 5:43:00 PM (7 months ago)
- Location:
- trunk/src/VBox/Main/src-client
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/ConsoleImpl.cpp
r104799 r105006 7666 7666 } 7667 7667 7668 /** 7669 * Sends a cursor shape change to the recording context. 7670 * 7671 * @returns VBox status code. 7672 * @param fVisible Whether the mouse cursor actually is visible or not. 7673 * @param fAlpha Whether the pixel data contains alpha channel information or not. 7674 * @param xHot X hot position (in pixel) of the new cursor. 7675 * @param yHot Y hot position (in pixel) of the new cursor. 7676 * @param uWidth Width (in pixel) of the new cursor. 7677 * @param uHeight Height (in pixel) of the new cursor. 7678 * @param pu8Shape Pixel data of the new cursor. 7679 * @param cbShape Size of \a pu8Shape (in bytes). 7680 */ 7681 int Console::i_recordingCursorShapeChange(bool fVisible, bool fAlpha, uint32_t xHot, uint32_t yHot, uint32_t uWidth, uint32_t uHeight, const uint8_t *pu8Shape, uint32_t cbShape) 7682 { 7683 if (!mRecording.mCtx.IsStarted()) 7684 return VINF_SUCCESS; 7685 7686 return mRecording.mCtx.SendCursorShapeChange(fVisible, fAlpha, xHot, yHot, uWidth, uHeight, pu8Shape, cbShape, 7687 mRecording.mCtx.GetCurrentPTS()); 7688 } 7668 7689 #endif /* VBOX_WITH_RECORDING */ 7669 7690 … … 7795 7816 7796 7817 if (!mMouse.isNull()) 7797 mMouse-> updateMousePointerShape(fVisible, fAlpha, xHot, yHot, width, height, pu8Shape, cbShape);7818 mMouse->i_updatePointerShape(fVisible, fAlpha, xHot, yHot, width, height, pu8Shape, cbShape); 7798 7819 7799 7820 com::SafeArray<BYTE> shape(cbShape); … … 7801 7822 memcpy(shape.raw(), pu8Shape, cbShape); 7802 7823 ::FireMousePointerShapeChangedEvent(mEventSource, fVisible, fAlpha, xHot, yHot, width, height, ComSafeArrayAsInParam(shape)); 7824 7825 #ifdef VBOX_WITH_RECORDING 7826 i_recordingCursorShapeChange(fVisible, fAlpha, xHot, yHot, width, height, pu8Shape, cbShape); 7827 #endif 7803 7828 7804 7829 #if 0 -
trunk/src/VBox/Main/src-client/DisplayImpl.cpp
r104801 r105006 34 34 #include "ConsoleVRDPServer.h" 35 35 #include "GuestImpl.h" 36 #include "MouseImpl.h" 36 37 #include "VMMDev.h" 37 38 … … 61 62 # include <iprt/path.h> 62 63 # include "Recording.h" 64 # include "RecordingUtils.h" 63 65 64 66 # include <VBox/vmm/pdmapi.h> … … 590 592 maFramebuffers[uScreenId].updateImage.cbLine = 0; 591 593 maFramebuffers[uScreenId].pFramebuffer.setNull(); 592 #ifdef VBOX_WITH_RECORDING593 maFramebuffers[uScreenId].Recording.pSourceBitmap.setNull();594 #endif595 594 } 596 595 … … 797 796 const bool fDisabled = pFBInfo->fDisabled; 798 797 798 #ifdef VBOX_WITH_RECORDING 799 /* Recording needs to be called before releasing the display's lock below. */ 800 i_recordingScreenChanged(uScreenId, pFBInfo); 801 #endif 802 799 803 alock.release(); 800 804 801 805 if (!pFramebuffer.isNull()) 802 806 { 803 HRESULT hr = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /** @todo origin */804 LogFunc(("NotifyChange hr %08X\n", hr ));805 NOREF(hr);807 HRESULT hrc = pFramebuffer->NotifyChange(uScreenId, 0, 0, w, h); /** @todo origin */ 808 LogFunc(("NotifyChange hr %08X\n", hrc)); 809 RT_NOREF(hrc); 806 810 } 807 811 … … 828 832 i_handleSetVisibleRegion(mcRectVisibleRegion, mpRectVisibleRegion); 829 833 830 #ifdef VBOX_WITH_RECORDING831 i_recordingScreenChanged(uScreenId);832 #endif833 834 834 LogRelFlowFunc(("[%d]: default format %d\n", uScreenId, pFBInfo->fDefaultFormat)); 835 835 … … 909 909 { 910 910 pFramebuffer->NotifyUpdate(x, y, w, h); 911 912 #ifdef VBOX_WITH_RECORDING 913 RECORDINGVIDEOFRAME Frame = 914 { 915 (uint16_t)w, (uint16_t)h, 916 (uint8_t )pFBInfo->u16BitsPerPixel, RECORDINGPIXELFMT_BRGA32, pFBInfo->u32LineSize, 917 pFBInfo->pu8FramebufferVRAM + (y * pFBInfo->u32LineSize + x * (pFBInfo->u16BitsPerPixel / 8)), 918 pFBInfo->w * pFBInfo->u32LineSize, 919 (uint16_t)x, (uint16_t)y 920 }; 921 i_recordingScreenUpdate(uScreenId, &Frame); 922 #endif 911 923 } 912 924 } … … 943 955 pFBInfo->updateImage.pu8Address = pAddress; 944 956 pFBInfo->updateImage.cbLine = ulBytesPerLine; 957 #ifdef VBOX_WITH_RECORDING 958 RECORDINGVIDEOFRAME Frame = 959 { 960 (uint16_t)ulWidth, (uint16_t)ulHeight, 961 (uint8_t )ulBitsPerPixel, RECORDINGPIXELFMT_BRGA32, ulBytesPerLine, 962 pAddress, ulHeight * ulBytesPerLine, 963 0, 0 964 }; 965 966 i_recordingScreenUpdate(uScreenId, &Frame); 967 #endif 945 968 } 946 969 … … 972 995 i_checkCoordBounds(&x, &y, &w, &h, ulWidth, ulHeight); 973 996 974 if (w != 0 && h != 0) 997 if ( w != 0 998 && h != 0) 975 999 { 976 const size_t cbData = w * h * 4; 1000 unsigned const uBytesPerPixel = ulBitsPerPixel / 8; 1001 1002 const size_t cbData = w * h * uBytesPerPixel; 977 1003 com::SafeArray<BYTE> image(cbData); 978 1004 979 1005 uint8_t *pu8Dst = image.raw(); 980 const uint8_t *pu8Src = pbAddress + ulBytesPerLine * y + x * 4; 981 982 int i; 983 for (i = y; i < y + h; ++i) 1006 const uint8_t *pu8Src = pbAddress + ulBytesPerLine * y + x * uBytesPerPixel; 1007 1008 for (int i = y; i < y + h; ++i) 984 1009 { 985 memcpy(pu8Dst, pu8Src, w * 4);986 pu8Dst += w * 4;1010 memcpy(pu8Dst, pu8Src, w * uBytesPerPixel); 1011 pu8Dst += w * uBytesPerPixel; 987 1012 pu8Src += ulBytesPerLine; 988 1013 } 989 1014 990 1015 pFramebuffer->NotifyUpdateImage(x, y, w, h, ComSafeArrayAsInParam(image)); 1016 1017 #ifdef VBOX_WITH_RECORDING 1018 RECORDINGVIDEOFRAME Frame = 1019 { 1020 (uint16_t)w, (uint16_t)h, 1021 (uint8_t)ulBitsPerPixel, RECORDINGPIXELFMT_BRGA32, ulBytesPerLine, 1022 pu8Dst, h * ulBytesPerLine, 1023 (uint16_t)x, (uint16_t)y 1024 }; 1025 1026 i_recordingScreenUpdate(uScreenId, &Frame); 1027 #endif 991 1028 } 992 1029 } … … 1128 1165 /** Updates the device's view of the host cursor handling capabilities. 1129 1166 * Calls into mpDrv->pUpPort. */ 1130 void Display::i_ UpdateDeviceCursorCapabilities(void)1167 void Display::i_updateDeviceCursorCapabilities(void) 1131 1168 { 1132 1169 bool fRenderCursor = true; … … 1172 1209 alock.release(); /* Release before calling up for lock order reasons. */ 1173 1210 mfHostCursorCapabilities = fHostCursorCapabilities; 1174 i_ UpdateDeviceCursorCapabilities();1211 i_updateDeviceCursorCapabilities(); 1175 1212 return S_OK; 1176 1213 } … … 1419 1456 1420 1457 i_VideoAccelVRDP(fConnect, c); 1421 i_ UpdateDeviceCursorCapabilities();1458 i_updateDeviceCursorCapabilities(); 1422 1459 } 1423 1460 … … 2150 2187 * @returns VBox status code. 2151 2188 * @param fForce Whether to force invalidation or not. Default is @c false. 2189 * 2190 * @note Takes the display's read lock. 2152 2191 */ 2153 2192 int Display::i_recordingInvalidate(bool fForce /* = false */) … … 2156 2195 if (!pCtx) 2157 2196 return VINF_SUCCESS; 2197 2198 LogFlowFuncEnter(); 2199 2200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2158 2201 2159 2202 /* … … 2169 2212 maRecordingEnabled[uScreen] = fStreamEnabled; 2170 2213 2171 if (fChanged && uScreen < mcMonitors) 2172 i_recordingScreenChanged(uScreen); 2214 if ( fStreamEnabled 2215 && fChanged) 2216 { 2217 DISPLAYFBINFO const *pFBInfo = &maFramebuffers[uScreen]; 2218 /* ignore rc */ i_recordingScreenChanged(uScreen, pFBInfo); 2219 } 2173 2220 } 2174 2221 … … 2179 2226 * Called when the recording state of a screen got changed. 2180 2227 * 2228 * @returns VBox status code. 2181 2229 * @param uScreenId ID of screen for which the recording state got changed. 2230 * @param pFBInfo Frame buffer information to use. 2182 2231 */ 2183 void Display::i_recordingScreenChanged(unsigned uScreenId) 2184 { 2232 int Display::i_recordingScreenChanged(unsigned uScreenId, const DISPLAYFBINFO *pFBInfo) 2233 { 2234 AssertReturn(uScreenId < mcMonitors, VERR_INVALID_PARAMETER); 2235 2185 2236 RecordingContext *pCtx = mParent->i_recordingGetContext(); 2186 2237 2187 i_ UpdateDeviceCursorCapabilities();2238 i_updateDeviceCursorCapabilities(); 2188 2239 if ( RT_LIKELY(!maRecordingEnabled[uScreenId]) 2189 2240 || !pCtx || !pCtx->IsStarted()) 2190 2241 { 2191 2242 /* Skip recording this screen. */ 2192 return; 2193 } 2194 2195 /* Get a new source bitmap which will be used by video recording code. */ 2196 ComPtr<IDisplaySourceBitmap> pSourceBitmap; 2197 QuerySourceBitmap(uScreenId, pSourceBitmap.asOutParam()); 2198 2199 int vrc2 = RTCritSectEnter(&mVideoRecLock); 2200 if (RT_SUCCESS(vrc2)) 2201 { 2202 maFramebuffers[uScreenId].Recording.pSourceBitmap = pSourceBitmap; 2203 2204 vrc2 = RTCritSectLeave(&mVideoRecLock); 2205 AssertRC(vrc2); 2206 } 2243 return VINF_SUCCESS; 2244 } 2245 2246 LogFlowFuncEnter(); 2247 2248 RECORDINGSURFACEINFO ScreenInfo; 2249 ScreenInfo.uWidth = pFBInfo->w; 2250 ScreenInfo.uHeight = pFBInfo->h; 2251 ScreenInfo.uBPP = (uint8_t)pFBInfo->u16BitsPerPixel; 2252 ScreenInfo.enmPixelFmt = RECORDINGPIXELFMT_BRGA32; /** @todo Does this apply everywhere? */ 2253 2254 uint64_t const tsNowMs = pCtx->GetCurrentPTS(); 2255 2256 int vrc = pCtx->SendScreenChange(uScreenId, &ScreenInfo, tsNowMs); 2257 if (RT_SUCCESS(vrc)) 2258 { 2259 /** @todo BUGBUG For whatever reason pFBInfo contains a wrong pFBInfo->u32LineSize 2260 * when shutting down a VM. So (re-)calculate the line size based on the framebuffer's 2261 * BPP + width parameters. 2262 * 2263 * So fend off any requests here which look fishy before sending a full screen update. */ 2264 if ( !pFBInfo->u16BitsPerPixel 2265 || pFBInfo->u16BitsPerPixel % 2 != 0 2266 || !pFBInfo->w 2267 || !pFBInfo->h 2268 || pFBInfo->u32LineSize != pFBInfo->w * (pFBInfo->u16BitsPerPixel / 8)) 2269 { 2270 vrc = VERR_INVALID_PARAMETER; 2271 } 2272 else 2273 { 2274 /* Make sure that we get the latest mouse pointer shape required for recording. */ 2275 MousePointerData pointerData; 2276 mParent->i_getMouse()->i_getPointerShape(pointerData); 2277 mParent->i_recordingCursorShapeChange(pointerData.fVisible, pointerData.fAlpha, 2278 pointerData.hotX, pointerData.hotY, 2279 pointerData.width, pointerData.height, 2280 pointerData.pu8Shape, pointerData.cbShape); 2281 /* Send the full screen update. */ 2282 RECORDINGVIDEOFRAME Frame = 2283 { 2284 (uint16_t)pFBInfo->w, (uint16_t)pFBInfo->h, 2285 (uint8_t)pFBInfo->u16BitsPerPixel, RECORDINGPIXELFMT_BRGA32, pFBInfo->u32LineSize, //pFBInfo->w * (pFBInfo->u16BitsPerPixel / 8), 2286 pFBInfo->pu8FramebufferVRAM, pFBInfo->h * pFBInfo->u32LineSize, 0, 0 2287 }; 2288 2289 vrc = i_recordingScreenUpdate(uScreenId, &Frame); 2290 } 2291 } 2292 2293 LogFlowFuncLeaveRC(vrc); 2294 return vrc; 2295 } 2296 2297 /** 2298 * Called when a part of a screen got updated. 2299 * 2300 * @returns VBox status code. 2301 * @param uScreenId ID of screen which got updated. 2302 * @param pFrame Video frama to send. 2303 */ 2304 int Display::i_recordingScreenUpdate(unsigned uScreenId, PRECORDINGVIDEOFRAME pFrame) 2305 { 2306 if (!maRecordingEnabled[uScreenId]) 2307 return VINF_NO_CHANGE; 2308 2309 AssertPtr(mParent); 2310 RecordingContext *pCtx = mParent->i_recordingGetContext(); 2311 if (!pCtx) 2312 return VINF_SUCCESS; 2313 2314 /* If the recording context has reached the configured recording 2315 * limit, disable recording. */ 2316 if (pCtx->IsLimitReached()) 2317 { 2318 mParent->i_onRecordingChange(FALSE /* Disable */); 2319 return VINF_SUCCESS; 2320 } 2321 2322 uint64_t const tsNowMs = pCtx->GetCurrentPTS(); 2323 2324 int vrc = VINF_NO_CHANGE; 2325 2326 if ( pCtx->IsStarted() 2327 && pCtx->IsFeatureEnabled(RecordingFeature_Video)) 2328 { 2329 STAM_PROFILE_START(&Stats.Monitor[uScreenId].Recording.profileRecording, a); 2330 2331 vrc = pCtx->SendVideoFrame(uScreenId, pFrame, tsNowMs); 2332 2333 STAM_PROFILE_STOP(&Stats.Monitor[uScreenId].Recording.profileRecording, a); 2334 } 2335 2336 return vrc; 2337 } 2338 2339 /** 2340 * Called when the mouse cursor position has changed within the guest. 2341 * 2342 * @returns VBox status code. 2343 * @param uScreenId ID of screen. 2344 * @param fFlags Position flags. Not used yet. 2345 * @param x X position of the mouse cursor (within guest). 2346 * @param y Y position of the mouse cursor (within guest). 2347 * 2348 * @note Requires Guest Additions installed + mouse integration enabled. 2349 */ 2350 int Display::i_recordingCursorPositionChange(unsigned uScreenId, uint32_t fFlags, int32_t x, int32_t y) 2351 { 2352 RT_NOREF(fFlags); 2353 2354 #if 0 /** @todo For whatever reason we always report SVGA_ID_INVALID here. Needs investigation. */ 2355 if ( uScreenId > mcMonitors /* Might be SVGA_ID_INVALID. */ 2356 || !maRecordingEnabled[uScreenId]) 2357 return VINF_SUCCESS; 2358 #endif 2359 2360 AssertPtr(mParent); 2361 RecordingContext *pCtx = mParent->i_recordingGetContext(); 2362 if (!pCtx) 2363 return VINF_SUCCESS; 2364 2365 /* If the recording context has reached the configured recording 2366 * limit, disable recording. */ 2367 if (pCtx->IsLimitReached()) 2368 { 2369 mParent->i_onRecordingChange(FALSE /* Disable */); 2370 return VINF_SUCCESS; 2371 } 2372 2373 if ( pCtx->IsStarted() 2374 && pCtx->IsFeatureEnabled(RecordingFeature_Video)) 2375 { 2376 return pCtx->SendCursorPositionChange(uScreenId, x, y, pCtx->GetCurrentPTS()); 2377 } 2378 2379 return VINF_SUCCESS; 2207 2380 } 2208 2381 #endif /* VBOX_WITH_RECORDING */ … … 3024 3197 } 3025 3198 } 3026 3027 #ifdef VBOX_WITH_RECORDING3028 AssertPtr(pDisplay->mParent);3029 RecordingContext *pCtx = pDisplay->mParent->i_recordingGetContext();3030 3031 if ( pCtx3032 && pCtx->IsStarted()3033 && pCtx->IsFeatureEnabled(RecordingFeature_Video))3034 {3035 do3036 {3037 /* If the recording context has reached the configured recording3038 * limit, disable recording. */3039 if (pCtx->IsLimitReached())3040 {3041 pDisplay->mParent->i_onRecordingChange(FALSE /* Disable */);3042 break;3043 }3044 3045 STAM_PROFILE_START(&pDisplay->Stats.Recording.profileRecording, b);3046 3047 uint64_t tsNowMs = RTTimeProgramMilliTS();3048 for (uScreenId = 0; uScreenId < pDisplay->mcMonitors; uScreenId++)3049 {3050 if (!pDisplay->maRecordingEnabled[uScreenId])3051 continue;3052 3053 if (!pCtx->NeedsUpdate(uScreenId, tsNowMs))3054 continue;3055 3056 STAM_PROFILE_START(&pDisplay->Stats.Monitor[uScreenId].Recording.profileRecording, c);3057 3058 DISPLAYFBINFO *pFBInfo = &pDisplay->maFramebuffers[uScreenId];3059 if (!pFBInfo->fDisabled)3060 {3061 ComPtr<IDisplaySourceBitmap> pSourceBitmap;3062 int vrc2 = RTCritSectEnter(&pDisplay->mVideoRecLock);3063 if (RT_SUCCESS(vrc2))3064 {3065 pSourceBitmap = pFBInfo->Recording.pSourceBitmap;3066 RTCritSectLeave(&pDisplay->mVideoRecLock);3067 }3068 3069 if (!pSourceBitmap.isNull())3070 {3071 BYTE *pbAddress = NULL;3072 ULONG ulWidth = 0;3073 ULONG ulHeight = 0;3074 ULONG ulBitsPerPixel = 0;3075 ULONG ulBytesPerLine = 0;3076 BitmapFormat_T bitmapFormat = BitmapFormat_Opaque;3077 HRESULT hrc = pSourceBitmap->QueryBitmapInfo(&pbAddress,3078 &ulWidth,3079 &ulHeight,3080 &ulBitsPerPixel,3081 &ulBytesPerLine,3082 &bitmapFormat);3083 if (SUCCEEDED(hrc) && pbAddress)3084 vrc = pCtx->SendVideoFrame(uScreenId, 0, 0, BitmapFormat_BGR,3085 ulBitsPerPixel, ulBytesPerLine, ulWidth, ulHeight,3086 pbAddress, tsNowMs);3087 else3088 vrc = VERR_NOT_SUPPORTED;3089 3090 pSourceBitmap.setNull();3091 }3092 else3093 vrc = VERR_NOT_SUPPORTED;3094 3095 if (vrc == VINF_TRY_AGAIN)3096 break;3097 }3098 3099 STAM_PROFILE_STOP(&pDisplay->Stats.Monitor[uScreenId].Recording.profileRecording, c);3100 }3101 3102 STAM_PROFILE_STOP(&pDisplay->Stats.Recording.profileRecording, b);3103 3104 } while (0);3105 }3106 #endif /* VBOX_WITH_RECORDING */3107 3199 3108 3200 STAM_PROFILE_STOP(&pDisplay->Stats.profileDisplayRefreshCallback, a); … … 3693 3785 } 3694 3786 ::FireCursorPositionChangedEvent(pThis->mParent->i_getEventSource(), RT_BOOL(fFlags & VBVA_CURSOR_VALID_DATA), x, y); 3787 3788 # ifdef VBOX_WITH_RECORDING 3789 pThis->i_recordingCursorPositionChange(aScreenId, fFlags, x, y); 3790 # endif 3695 3791 } 3696 3792 -
trunk/src/VBox/Main/src-client/DrvAudioRec.cpp
r104635 r105006 318 318 /* Make sure to let the driver backend know that we need the audio data in 319 319 * a specific sampling rate the codec is optimized for. */ 320 pCfgAcq->Props = pCodec->Parms. Audio.PCMProps;320 pCfgAcq->Props = pCodec->Parms.u.Audio.PCMProps; 321 321 322 322 /* Every codec frame marks a period for now. Optimize this later. */ … … 491 491 const void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten) 492 492 { 493 RT_NOREF(pInterface);494 PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream;493 PDRVAUDIORECORDING pThis = RT_FROM_CPP_MEMBER(pInterface, DRVAUDIORECORDING, IHostAudio); 494 PAVRECSTREAM pStreamAV = (PAVRECSTREAM)pStream; 495 495 AssertPtrReturn(pStreamAV, VERR_INVALID_POINTER); 496 496 if (cbBuf) … … 561 561 if (cbSrc == cbFrame) /* Only send full codec frames. */ 562 562 { 563 vrc = pRecStream->SendAudioFrame(pStreamAV->pvSrcBuf, cbSrc, RTTimeProgramMilliTS()); 563 AssertPtr(pThis->pRecCtx); 564 vrc = pRecStream->SendAudioFrame(pStreamAV->pvSrcBuf, cbSrc, pThis->pRecCtx->GetCurrentPTS()); 564 565 if (RT_FAILURE(vrc)) 565 566 break; -
trunk/src/VBox/Main/src-client/MouseImpl.cpp
r99739 r105006 323 323 } 324 324 325 void Mouse::updateMousePointerShape(bool fVisible, bool fAlpha, 326 uint32_t hotX, uint32_t hotY, 327 uint32_t width, uint32_t height, 328 const uint8_t *pu8Shape, uint32_t cbShape) 325 /** 326 * Updates the pointer shape data. 327 * 328 * @returns VBox status code. 329 * @param fVisible Whether the mouse cursor actually is visible or not. 330 * @param fAlpha Whether the pixel data contains an alpha mask or not. 331 * @param uHotX X hot position (in pixel) of the new cursor. 332 * @param uHotY Y hot position (in pixel) of the new cursor. 333 * @param uWidth Width (in pixel) of the new cursor. 334 * @param uHeight Height (in pixel) of the new cursor. 335 * @param pu8Shape Pixel data of the new cursor. 336 * @param cbShape Size of \a pu8Shape (in bytes). 337 * 338 * @note Takes the write lock. 339 */ 340 int Mouse::i_updatePointerShape(bool fVisible, bool fAlpha, 341 uint32_t uHotX, uint32_t uHotY, 342 uint32_t uWidth, uint32_t uHeight, 343 const uint8_t *pu8Shape, uint32_t cbShape) 329 344 { 330 345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 331 346 332 RTMemFree(mPointerData.pu8Shape); 333 mPointerData.pu8Shape = NULL; 334 mPointerData.cbShape = 0; 335 336 mPointerData.fVisible = fVisible; 337 mPointerData.fAlpha = fAlpha; 338 mPointerData.hotX = hotX; 339 mPointerData.hotY = hotY; 340 mPointerData.width = width; 341 mPointerData.height = height; 342 if (cbShape) 343 { 344 mPointerData.pu8Shape = (uint8_t *)RTMemDup(pu8Shape, cbShape); 345 if (mPointerData.pu8Shape) 346 { 347 mPointerData.cbShape = cbShape; 348 } 349 } 350 351 mPointerShape.setNull(); 347 mPointerData.Destroy(); /* Destroy old data first. */ 348 349 int vrc = mPointerData.Init(fVisible, fAlpha, uHotX, uHotY, uWidth, uHeight, pu8Shape, cbShape); 350 if (RT_SUCCESS(vrc)) 351 { 352 mPointerShape.setNull(); 353 } 354 355 return vrc; 352 356 } 353 357 … … 1124 1128 { 1125 1129 return RT_BOOL(mfVMMDevGuestCaps & VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR); 1130 } 1131 1132 1133 /** 1134 * Returns the current mouse pointer data. 1135 * 1136 * @returns VBox status code. 1137 * @param aData Where to return the current mouse pointer data. 1138 */ 1139 int Mouse::i_getPointerShape(MousePointerData &aData) 1140 { 1141 AutoReadLock aLock(this COMMA_LOCKVAL_SRC_POS); 1142 1143 return aData.Init(mPointerData); 1126 1144 } 1127 1145 -
trunk/src/VBox/Main/src-client/Recording.cpp
r98278 r105006 68 68 69 69 70 71 RecordingCursorState::RecordingCursorState() 72 : m_fFlags(VBOX_RECORDING_CURSOR_F_NONE) 73 { 74 m_Shape.Pos.x = UINT16_MAX; 75 m_Shape.Pos.y = UINT16_MAX; 76 77 RT_ZERO(m_Shape); 78 } 79 80 RecordingCursorState::~RecordingCursorState() 81 { 82 Destroy(); 83 } 84 85 /** 86 * Destroys a cursor state. 87 */ 88 void RecordingCursorState::Destroy(void) 89 { 90 RecordingVideoFrameDestroy(&m_Shape); 91 } 92 93 /** 94 * Creates or updates the cursor shape. 95 * 96 * @returns VBox status code. 97 * @param fAlpha Whether the pixel data contains alpha channel information or not. 98 * @param uWidth Width (in pixel) of new cursor shape. 99 * @param uHeight Height (in pixel) of new cursor shape. 100 * @param pu8Shape Pixel data of new cursor shape. 101 * @param cbShape Bytes of \a pu8Shape. 102 */ 103 int RecordingCursorState::CreateOrUpdate(bool fAlpha, uint32_t uWidth, uint32_t uHeight, const uint8_t *pu8Shape, size_t cbShape) 104 { 105 int vrc; 106 107 uint32_t fFlags = RECORDINGVIDEOFRAME_F_VISIBLE; 108 109 const uint8_t uBPP = 32; /* Seems to be fixed. */ 110 111 uint32_t offShape; 112 if (fAlpha) 113 { 114 /* Calculate the offset to the actual pixel data. */ 115 offShape = (uWidth + 7) / 8 * uHeight; /* size of the AND mask */ 116 offShape = (offShape + 3) & ~3; 117 AssertReturn(offShape <= cbShape, VERR_INVALID_PARAMETER); 118 fFlags |= RECORDINGVIDEOFRAME_F_BLIT_ALPHA; 119 } 120 else 121 offShape = 0; 122 123 /* Cursor shape size has become bigger? Reallocate. */ 124 if (cbShape > m_Shape.cbBuf) 125 { 126 RecordingVideoFrameDestroy(&m_Shape); 127 vrc = RecordingVideoFrameInit(&m_Shape, fFlags, uWidth, uHeight, 0 /* posX */, 0 /* posY */, 128 uBPP, RECORDINGPIXELFMT_BRGA32); 129 } 130 else /* Otherwise just zero out first. */ 131 { 132 RecordingVideoFrameClear(&m_Shape); 133 vrc = VINF_SUCCESS; 134 } 135 136 if (RT_SUCCESS(vrc)) 137 vrc = RecordingVideoFrameBlitRaw(&m_Shape, 0, 0, &pu8Shape[offShape], cbShape - offShape, 0, 0, uWidth, uHeight, uWidth * 4 /* BPP */, uBPP, 138 m_Shape.Info.enmPixelFmt); 139 #ifdef DEBUG_andy_disabled 140 RecordingUtilsDbgDumpVideoFrameEx(&m_Shape, "/tmp/recording", "cursor-update"); 141 #endif 142 143 return vrc; 144 } 145 146 /** 147 * Moves (sets) the cursor to a new position. 148 * 149 * @returns VBox status code. 150 * @retval VERR_NO_CHANGE if the cursor wasn't moved (set). 151 * @param iX New X position to set. 152 * @param iY New Y position to set. 153 */ 154 int RecordingCursorState::Move(int32_t iX, int32_t iY) 155 { 156 /* No relative coordinates here. */ 157 if ( iX < 0 158 || iY < 0) 159 return VERR_NO_CHANGE; 160 161 if ( m_Shape.Pos.x == (uint32_t)iX 162 && m_Shape.Pos.y == (uint32_t)iY) 163 return VERR_NO_CHANGE; 164 165 m_Shape.Pos.x = (uint16_t)iX; 166 m_Shape.Pos.y = (uint16_t)iY; 167 168 return VINF_SUCCESS; 169 } 170 171 172 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 173 174 70 175 /** 71 176 * Recording context constructor. … … 129 234 for (;;) 130 235 { 131 int vrc = RTSemEventWait(pThis->m_WaitEvent, RT_INDEFINITE_WAIT); 132 AssertRCBreak(vrc); 133 134 Log2Func(("Processing %zu streams\n", pThis->m_vecStreams.size())); 236 int vrcWait = RTSemEventWait(pThis->m_WaitEvent, RT_MS_1SEC); 237 238 Log2Func(("Processing %zu streams (wait = %Rrc)\n", pThis->m_vecStreams.size(), vrcWait)); 135 239 136 240 /* Process common raw blocks (data which not has been encoded yet). */ 137 vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */);241 int vrc = pThis->processCommonData(pThis->m_mapBlocksRaw, 100 /* ms timeout */); 138 242 139 243 /** @todo r=andy This is inefficient -- as we already wake up this thread … … 146 250 147 251 /* Hand-in common encoded blocks. */ 148 vrc = pStream-> Process(pThis->m_mapBlocksEncoded);252 vrc = pStream->ThreadMain(vrcWait, pThis->m_mapBlocksEncoded); 149 253 if (RT_FAILURE(vrc)) 150 254 { … … 207 311 { 208 312 RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock); 209 switch (pBlockCommon->enmType) 313 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)pBlockCommon->pvData; 314 AssertPtr(pFrame); 315 switch (pFrame->enmType) 210 316 { 211 317 #ifdef VBOX_WITH_AUDIO_RECORDING 212 case RECORDING BLOCKTYPE_AUDIO:318 case RECORDINGFRAME_TYPE_AUDIO: 213 319 { 214 PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData; 215 216 RECORDINGFRAME Frame; 217 Frame.msTimestamp = pBlockCommon->msTimestamp; 218 Frame.Audio.pvBuf = pAudioFrame->pvBuf; 219 Frame.Audio.cbBuf = pAudioFrame->cbBuf; 220 221 vrc = recordingCodecEncode(&m_CodecAudio, &Frame, NULL, NULL); 320 vrc = recordingCodecEncodeFrame(&m_CodecAudio, pFrame, pFrame->msTimestamp, NULL /* pvUser */); 222 321 break; 223 322 } 224 323 #endif /* VBOX_WITH_AUDIO_RECORDING */ 225 default: 226 /* Skip unknown stuff. */ 227 break; 324 325 default: 326 /* Skip unknown stuff. */ 327 break; 228 328 } 229 329 … … 278 378 pCodec, cbData, msTimestamp, uFlags)); 279 379 280 /** @todo Optimize this! Three allocations in here! */ 281 282 RECORDINGBLOCKTYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO 283 ? RECORDINGBLOCKTYPE_AUDIO : RECORDINGBLOCKTYPE_UNKNOWN; 284 285 AssertReturn(enmType != RECORDINGBLOCKTYPE_UNKNOWN, VERR_NOT_SUPPORTED); 286 287 RecordingBlock *pBlock = new RecordingBlock(); 380 RECORDINGFRAME_TYPE const enmType = pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO 381 ? RECORDINGFRAME_TYPE_AUDIO : RECORDINGFRAME_TYPE_INVALID; 382 383 AssertReturn(enmType != RECORDINGFRAME_TYPE_INVALID, VERR_NOT_SUPPORTED); 384 385 PRECORDINGFRAME pFrame = NULL; 288 386 289 387 switch (enmType) 290 388 { 291 case RECORDING BLOCKTYPE_AUDIO:292 { 293 PRECORDINGAUDIOFRAME pFrame = (PRECORDINGAUDIOFRAME)RTMemAlloc(sizeof(RECORDINGAUDIOFRAME));389 case RECORDINGFRAME_TYPE_AUDIO: 390 { 391 pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME)); 294 392 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 295 296 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData); 297 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY); 298 pFrame->cbBuf = cbData; 299 300 memcpy(pFrame->pvBuf, pvData, cbData); 301 302 pBlock->enmType = enmType; 303 pBlock->pvData = pFrame; 304 pBlock->cbData = sizeof(RECORDINGAUDIOFRAME) + cbData; 305 pBlock->cRefs = m_cStreamsEnabled; 306 pBlock->msTimestamp = msTimestamp; 307 pBlock->uFlags = uFlags; 308 393 pFrame->enmType = RECORDINGFRAME_TYPE_AUDIO; 394 pFrame->msTimestamp = msTimestamp; 395 396 PRECORDINGAUDIOFRAME pAudioFrame = &pFrame->u.Audio; 397 pAudioFrame->pvBuf = (uint8_t *)RTMemDup(pvData, cbData); 398 AssertPtrReturn(pAudioFrame->pvBuf, VERR_NO_MEMORY); 399 pAudioFrame->cbBuf = cbData; 309 400 break; 310 401 } … … 319 410 int vrc; 320 411 412 RecordingBlock *pBlock = NULL; 321 413 try 322 414 { 415 pBlock = new RecordingBlock(); 416 417 pBlock->pvData = pFrame; 418 pBlock->cbData = sizeof(RECORDINGFRAME); 419 pBlock->cRefs = m_cStreamsEnabled; 420 pBlock->msTimestamp = msTimestamp; 421 pBlock->uFlags = uFlags; 422 323 423 RecordingBlockMap::iterator itBlocks = mapCommon.find(msTimestamp); 324 424 if (itBlocks == mapCommon.end()) … … 334 434 vrc = VINF_SUCCESS; 335 435 } 336 catch (const std::exception &ex) 337 { 338 RT_NOREF(ex); 436 catch (const std::exception &) 437 { 339 438 vrc = VERR_NO_MEMORY; 340 439 } … … 343 442 344 443 if (RT_SUCCESS(vrc)) 444 { 345 445 vrc = threadNotify(); 446 } 447 else 448 { 449 if (pBlock) 450 delete pBlock; 451 RecordingFrameFree(pFrame); 452 } 346 453 347 454 return vrc; … … 448 555 if (RT_SUCCESS(vrc)) 449 556 { 450 m_tsStartMs = RTTimeMilliTS();557 m_tsStartMs = 0; 451 558 m_enmState = RECORDINGSTS_CREATED; 452 559 m_fShutdown = false; … … 474 581 Assert(m_enmState == RECORDINGSTS_CREATED); 475 582 583 m_tsStartMs = RTTimeMilliTS(); 584 476 585 int vrc = RTThreadCreate(&m_Thread, RecordingContext::threadMain, (void *)this, 0, 477 586 RTTHREADTYPE_MAIN_WORKER, RTTHREADFLAGS_WAITABLE, "Record"); … … 483 592 { 484 593 LogRel(("Recording: Started\n")); 485 m_enmState = RECORDINGSTS_STARTED;594 m_enmState = RECORDINGSTS_STARTED; 486 595 } 487 596 else … … 516 625 { 517 626 LogRel(("Recording: Stopped\n")); 518 m_enmState = RECORDINGSTS_CREATED; 627 m_tsStartMs = 0; 628 m_enmState = RECORDINGSTS_CREATED; 519 629 } 520 630 else … … 689 799 690 800 /** 801 * Returns the current PTS (presentation time stamp) for a recording context. 802 * 803 * @returns Current PTS. 804 */ 805 uint64_t RecordingContext::GetCurrentPTS(void) const 806 { 807 return RTTimeMilliTS() - m_tsStartMs; 808 } 809 810 /** 691 811 * Returns if a specific recoding feature is enabled for at least one of the attached 692 812 * recording streams or not. … … 829 949 * @param msTimestamp Timestamp (PTS, in ms). 830 950 */ 831 bool RecordingContext::NeedsUpdate( 951 bool RecordingContext::NeedsUpdate(uint32_t uScreen, uint64_t msTimestamp) 832 952 { 833 953 lock(); … … 900 1020 * 901 1021 * @returns VBox status code. 902 * @param uScreen Screen number to send video frame to. 903 * @param x Starting x coordinate of the video frame. 904 * @param y Starting y coordinate of the video frame. 905 * @param uPixelFormat Pixel format. 906 * @param uBPP Bits Per Pixel (BPP). 907 * @param uBytesPerLine Bytes per scanline. 908 * @param uSrcWidth Width of the video frame. 909 * @param uSrcHeight Height of the video frame. 910 * @param puSrcData Pointer to video frame data. 911 * @param msTimestamp Timestamp (PTS, in ms). 912 */ 913 int RecordingContext::SendVideoFrame(uint32_t uScreen, uint32_t x, uint32_t y, 914 uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine, 915 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, 916 uint64_t msTimestamp) 917 { 918 AssertReturn(uSrcWidth, VERR_INVALID_PARAMETER); 919 AssertReturn(uSrcHeight, VERR_INVALID_PARAMETER); 920 AssertReturn(puSrcData, VERR_INVALID_POINTER); 1022 * @param uScreen Screen number to send video frame to. 1023 * @param pFrame Video frame to send. 1024 * @param msTimestamp Timestamp (PTS, in ms). 1025 */ 1026 int RecordingContext::SendVideoFrame(uint32_t uScreen, PRECORDINGVIDEOFRAME pFrame, uint64_t msTimestamp) 1027 { 1028 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); 1029 1030 LogFlowFunc(("uScreen=%RU32, offX=%RU32, offY=%RU32, w=%RU32, h=%RU32, msTimestamp=%RU64\n", 1031 uScreen, pFrame->Pos.x, pFrame->Pos.y, pFrame->Info.uWidth, pFrame->Info.uHeight, msTimestamp)); 1032 1033 if (!pFrame->pau8Buf) /* Empty / invalid frame, skip. */ 1034 return VINF_SUCCESS; 1035 1036 /* Sanity. */ 1037 AssertReturn(pFrame->Info.uWidth * pFrame->Info.uHeight * (pFrame->Info.uBPP / 8) <= pFrame->cbBuf, VERR_INVALID_PARAMETER); 1038 AssertReturn(pFrame->Info.uHeight * pFrame->Info.uBytesPerLine <= pFrame->cbBuf, VERR_INVALID_PARAMETER); 921 1039 922 1040 lock(); … … 931 1049 } 932 1050 933 int vrc = pStream->SendVideoFrame( x, y, uPixelFormat, uBPP, uBytesPerLine, uSrcWidth, uSrcHeight, puSrcData, msTimestamp);1051 int vrc = pStream->SendVideoFrame(pFrame, msTimestamp); 934 1052 935 1053 unlock(); … … 944 1062 } 945 1063 1064 /** 1065 * Sends a cursor position change to the recording context. 1066 * 1067 * @returns VBox status code. 1068 * @param uScreen Screen number. 1069 * @param x X location within the guest. 1070 * @param y Y location within the guest. 1071 * @param msTimestamp Timestamp (PTS, in ms). 1072 */ 1073 int RecordingContext::SendCursorPositionChange(uint32_t uScreen, int32_t x, int32_t y, uint64_t msTimestamp) 1074 { 1075 LogFlowFunc(("uScreen=%RU32, x=%RU32, y=%RU32\n", uScreen, x, y)); 1076 1077 /* If no cursor shape is set yet, skip any cursor position changes. */ 1078 if (!m_Cursor.m_Shape.pau8Buf) 1079 return VINF_SUCCESS; 1080 1081 if (uScreen == 0xFFFFFFFF /* SVGA_ID_INVALID */) 1082 uScreen = 0; 1083 1084 int vrc = m_Cursor.Move(x, y); 1085 if (RT_SUCCESS(vrc)) 1086 { 1087 lock(); 1088 1089 RecordingStream *pStream = getStreamInternal(uScreen); 1090 if (!pStream) 1091 { 1092 unlock(); 1093 1094 AssertFailed(); 1095 return VERR_NOT_FOUND; 1096 } 1097 1098 vrc = pStream->SendCursorPos(0 /* idCursor */, &m_Cursor.m_Shape.Pos, msTimestamp); 1099 1100 unlock(); 1101 1102 if ( RT_SUCCESS(vrc) 1103 && vrc != VINF_RECORDING_THROTTLED) /* Only signal the thread if operation was successful. */ 1104 { 1105 threadNotify(); 1106 } 1107 } 1108 1109 return vrc; 1110 } 1111 1112 /** 1113 * Sends a cursor shape change to the recording context. 1114 * 1115 * @returns VBox status code. 1116 * @param fVisible Whether the mouse cursor actually is visible or not. 1117 * @param fAlpha Whether the pixel data contains alpha channel information or not. 1118 * @param xHot X hot position (in pixel) of the new cursor. 1119 * @param yHot Y hot position (in pixel) of the new cursor. 1120 * @param uWidth Width (in pixel) of the new cursor. 1121 * @param uHeight Height (in pixel) of the new cursor. 1122 * @param pu8Shape Pixel data of the new cursor. Must be 32 BPP RGBA for now. 1123 * @param cbShape Size of \a pu8Shape (in bytes). 1124 * @param msTimestamp Timestamp (PTS, in ms). 1125 */ 1126 int RecordingContext::SendCursorShapeChange(bool fVisible, bool fAlpha, uint32_t xHot, uint32_t yHot, 1127 uint32_t uWidth, uint32_t uHeight, const uint8_t *pu8Shape, size_t cbShape, 1128 uint64_t msTimestamp) 1129 { 1130 RT_NOREF(fAlpha, xHot, yHot); 1131 1132 LogFlowFunc(("fVisible=%RTbool, fAlpha=%RTbool, uWidth=%RU32, uHeight=%RU32\n", fVisible, fAlpha, uWidth, uHeight)); 1133 1134 if ( !pu8Shape /* Might be NULL on saved state load. */ 1135 || !fVisible) 1136 return VINF_SUCCESS; 1137 1138 AssertReturn(cbShape, VERR_INVALID_PARAMETER); 1139 1140 lock(); 1141 1142 int vrc = m_Cursor.CreateOrUpdate(fAlpha, uWidth, uHeight, pu8Shape, cbShape); 1143 1144 RecordingStreams::iterator it = m_vecStreams.begin(); 1145 while (it != m_vecStreams.end()) 1146 { 1147 RecordingStream *pStream = (*it); 1148 1149 int vrc2 = pStream->SendCursorShape(0 /* idCursor */, &m_Cursor.m_Shape, msTimestamp); 1150 if (RT_SUCCESS(vrc)) 1151 vrc = vrc2; 1152 1153 ++it; 1154 } 1155 1156 unlock(); 1157 1158 if (RT_SUCCESS(vrc)) 1159 threadNotify(); 1160 1161 return vrc; 1162 } 1163 1164 /** 1165 * Sends a screen change to a recording stream. 1166 * 1167 * @returns VBox status code. 1168 * @param uScreen Screen number. 1169 * @param pInfo Recording screen info to use. 1170 * @param msTimestamp Timestamp (PTS, in ms). 1171 */ 1172 int RecordingContext::SendScreenChange(uint32_t uScreen, PRECORDINGSURFACEINFO pInfo, uint64_t msTimestamp) 1173 { 1174 lock(); 1175 1176 RecordingStream *pStream = getStreamInternal(uScreen); 1177 if (!pStream) 1178 { 1179 unlock(); 1180 1181 AssertFailed(); 1182 return VERR_NOT_FOUND; 1183 } 1184 1185 int const vrc = pStream->SendScreenChange(pInfo, msTimestamp); 1186 1187 unlock(); 1188 1189 return vrc; 1190 } 1191 -
trunk/src/VBox/Main/src-client/RecordingCodec.cpp
r104635 r105006 66 66 #include <VBox/vmm/pdmaudioinline.h> 67 67 68 #include "Recording.h" 68 69 #include "RecordingInternals.h" 69 70 #include "RecordingUtils.h" … … 72 73 #include <math.h> 73 74 75 #include <iprt/formats/bmp.h> 76 77 78 /********************************************************************************************************************************* 79 * Prototypes * 80 *********************************************************************************************************************************/ 81 static int recordingCodecVPXEncodeWorker(PRECORDINGCODEC pCodec, vpx_image_t *pImage, uint64_t msTimestamp); 82 83 84 /********************************************************************************************************************************* 85 * Generic inline functions * 86 *********************************************************************************************************************************/ 87 88 DECLINLINE(void) recordingCodecLock(PRECORDINGCODEC pCodec) 89 { 90 int vrc2 = RTCritSectEnter(&pCodec->CritSect); 91 AssertRC(vrc2); 92 } 93 94 DECLINLINE(void) recordingCodecUnlock(PRECORDINGCODEC pCodec) 95 { 96 int vrc2 = RTCritSectLeave(&pCodec->CritSect); 97 AssertRC(vrc2); 98 } 99 74 100 75 101 /********************************************************************************************************************************* … … 78 104 79 105 #ifdef VBOX_WITH_LIBVPX 106 /** Prototypes. */ 107 static DECLCALLBACK(int) recordingCodecVPXScreenChange(PRECORDINGCODEC pCodec, PRECORDINGSURFACEINFO pInfo); 108 109 /** 110 * Clears (zeros) the VPX planes. 111 */ 112 DECLINLINE(void) recordingCodecVPXClearPlanes(PRECORDINGCODEC pCodec) 113 { 114 size_t const cbYPlane = pCodec->Parms.u.Video.uWidth * pCodec->Parms.u.Video.uHeight; 115 memset(pCodec->Video.VPX.RawImage.planes[VPX_PLANE_Y], 0, cbYPlane); 116 size_t const cbUVPlane = (pCodec->Parms.u.Video.uWidth / 2) * (pCodec->Parms.u.Video.uHeight / 2); 117 memset(pCodec->Video.VPX.RawImage.planes[VPX_PLANE_U], 128, cbUVPlane); 118 memset(pCodec->Video.VPX.RawImage.planes[VPX_PLANE_V], 128, cbUVPlane); 119 } 120 80 121 /** @copydoc RECORDINGCODECOPS::pfnInit */ 81 122 static DECLCALLBACK(int) recordingCodecVPXInit(PRECORDINGCODEC pCodec) 82 123 { 83 pCodec->cbScratch = _4K; 84 pCodec->pvScratch = RTMemAlloc(pCodec->cbScratch); 85 AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY); 86 87 pCodec->Parms.csFrame = 0; 88 pCodec->Parms.cbFrame = pCodec->Parms.Video.uWidth * pCodec->Parms.Video.uHeight * 4 /* 32-bit */; 89 pCodec->Parms.msFrame = 1; /* 1ms per frame. */ 124 const unsigned uBPP = 32; 125 126 pCodec->Parms.csFrame = 0; 127 pCodec->Parms.cbFrame = pCodec->Parms.u.Video.uWidth * pCodec->Parms.u.Video.uHeight * (uBPP / 8); 128 pCodec->Parms.msFrame = 1; /* 1ms per frame. */ 90 129 91 130 # ifdef VBOX_WITH_LIBVPX_VP9 … … 106 145 pVPX->Cfg.rc_target_bitrate = pCodec->Parms.uBitrate; 107 146 /* Frame width. */ 108 pVPX->Cfg.g_w = pCodec->Parms. Video.uWidth;147 pVPX->Cfg.g_w = pCodec->Parms.u.Video.uWidth; 109 148 /* Frame height. */ 110 pVPX->Cfg.g_h = pCodec->Parms. Video.uHeight;149 pVPX->Cfg.g_h = pCodec->Parms.u.Video.uHeight; 111 150 /* ms per frame. */ 112 151 pVPX->Cfg.g_timebase.num = pCodec->Parms.msFrame; … … 124 163 125 164 if (!vpx_img_alloc(&pVPX->RawImage, VPX_IMG_FMT_I420, 126 pCodec->Parms. Video.uWidth, pCodec->Parms.Video.uHeight, 1))127 { 128 LogRel(("Recording: Failed to allocate image %RU32x%RU32\n", pCodec->Parms. Video.uWidth, pCodec->Parms.Video.uHeight));165 pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight, 1)) 166 { 167 LogRel(("Recording: Failed to allocate image %RU32x%RU32\n", pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight)); 129 168 return VERR_RECORDING_CODEC_INIT_FAILED; 130 169 } 131 170 132 /* Save a pointer to the first raw YUV plane. */ 133 pVPX->pu8YuvBuf = pVPX->RawImage.planes[0]; 134 135 return VINF_SUCCESS; 171 /* Save a pointer to the Y (Luminance) plane. */ 172 pVPX->pu8YuvBuf = pVPX->RawImage.planes[VPX_PLANE_Y]; 173 174 /* Initialize front + back buffers. */ 175 RT_ZERO(pCodec->Video.VPX.Front); 176 RT_ZERO(pCodec->Video.VPX.Back); 177 178 pCodec->Video.VPX.pCursorShape = NULL; 179 180 RECORDINGSURFACEINFO ScreenInfo; 181 ScreenInfo.uWidth = pCodec->Parms.u.Video.uWidth; 182 ScreenInfo.uHeight = pCodec->Parms.u.Video.uHeight; 183 ScreenInfo.uBPP = uBPP; 184 ScreenInfo.enmPixelFmt = RECORDINGPIXELFMT_BRGA32; 185 186 RT_ZERO(pCodec->Video.VPX.PosCursorOld); 187 188 int vrc = recordingCodecVPXScreenChange(pCodec, &ScreenInfo); 189 if (RT_FAILURE(vrc)) 190 LogRel(("Recording: Failed to initialize codec: %Rrc\n", vrc)); 191 192 return vrc; 136 193 } 137 194 … … 147 204 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 148 205 206 RecordingVideoFrameDestroy(&pCodec->Video.VPX.Front); 207 RecordingVideoFrameDestroy(&pCodec->Video.VPX.Back); 208 209 RecordingVideoFrameFree(pCodec->Video.VPX.pCursorShape); 210 pCodec->Video.VPX.pCursorShape = NULL; 211 149 212 return VINF_SUCCESS; 213 } 214 215 /** @copydoc RECORDINGCODECOPS::pfnFinalize */ 216 static DECLCALLBACK(int) recordingCodecVPXFinalize(PRECORDINGCODEC pCodec) 217 { 218 recordingCodecLock(pCodec); 219 220 int vrc = recordingCodecVPXEncodeWorker(pCodec, NULL /* pImage */, pCodec->State.tsLastWrittenMs + 1); 221 222 recordingCodecUnlock(pCodec); 223 224 return vrc; 150 225 } 151 226 … … 165 240 else if (value.compare("good", com::Utf8Str::CaseInsensitive) == 0) 166 241 { 167 AssertStmt(pCodec->Parms. Video.uFPS, pCodec->Parms.Video.uFPS = 25);168 pVPX->uEncoderDeadline = 1000000 / pCodec->Parms. Video.uFPS;242 AssertStmt(pCodec->Parms.u.Video.uFPS, pCodec->Parms.u.Video.uFPS = 25); 243 pVPX->uEncoderDeadline = 1000000 / pCodec->Parms.u.Video.uFPS; 169 244 } 170 245 else if (value.compare("best", com::Utf8Str::CaseInsensitive) == 0) … … 180 255 } 181 256 182 /** @copydoc RECORDINGCODECOPS::pfnEncode */ 183 static DECLCALLBACK(int) recordingCodecVPXEncode(PRECORDINGCODEC pCodec, PRECORDINGFRAME pFrame, 184 size_t *pcEncoded, size_t *pcbEncoded) 185 { 186 RT_NOREF(pcEncoded, pcbEncoded); 187 188 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); 189 190 PRECORDINGVIDEOFRAME pVideoFrame = pFrame->VideoPtr; 191 192 int vrc = RecordingUtilsRGBToYUV(pVideoFrame->enmPixelFmt, 193 /* Destination */ 194 pCodec->Video.VPX.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight, 195 /* Source */ 196 pVideoFrame->pu8RGBBuf, pCodec->Parms.Video.uWidth, pCodec->Parms.Video.uHeight); 197 AssertRCReturn(vrc, vrc); 257 /** 258 * Worker for encoding the last composed image. 259 * 260 * @returns VBox status code. 261 * @param pCodec Pointer to codec instance. 262 * @param pImage VPX image to encode. 263 * Set to NULL to signal the encoder that it has to finish up stuff when ending encoding. 264 * @param msTimestamp Timestamp (PTS) to use for encoding. 265 * 266 * @note Caller must take encoder lock. 267 */ 268 static int recordingCodecVPXEncodeWorker(PRECORDINGCODEC pCodec, vpx_image_t *pImage, uint64_t msTimestamp) 269 { 270 int vrc; 198 271 199 272 PRECORDINGCODECVPX pVPX = &pCodec->Video.VPX; 200 273 201 274 /* Presentation TimeStamp (PTS). */ 202 vpx_codec_pts_t pts = pFrame->msTimestamp;203 vpx_codec_err_t rcv = vpx_codec_encode(&pVPX->Ctx,204 &pVPX->RawImage,205 pts/* Timestamp */,206 pCodec->Parms.Video.uDelayMs /* How long to show this frame */,207 0/* Flags */,208 pVPX->uEncoderDeadline/* Quality setting */);275 vpx_codec_pts_t const pts = msTimestamp; 276 vpx_codec_err_t const rcv = vpx_codec_encode(&pVPX->Ctx, 277 pImage, 278 pts /* Timestamp */, 279 pCodec->Parms.u.Video.uDelayMs /* How long to show this frame */, 280 0 /* Flags */, 281 pVPX->uEncoderDeadline /* Quality setting */); 209 282 if (rcv != VPX_CODEC_OK) 210 283 { … … 221 294 { 222 295 const vpx_codec_cx_pkt_t *pPkt = vpx_codec_get_cx_data(&pVPX->Ctx, &iter); 223 if (!pPkt) 296 if (!pPkt) /* End of list */ 224 297 break; 225 298 … … 240 313 fFlags |= RECORDINGCODEC_ENC_F_BLOCK_IS_INVISIBLE; 241 314 315 Log3Func(("msTimestamp=%RU64, fFlags=%#x\n", msTimestamp, fFlags)); 316 242 317 vrc = pCodec->Callbacks.pfnWriteData(pCodec, pPkt->data.frame.buf, pPkt->data.frame.sz, 243 318 tsAbsPTSMs, fFlags, pCodec->Callbacks.pvUser); … … 254 329 return vrc; 255 330 } 331 332 /** @copydoc RECORDINGCODECOPS::pfnEncode */ 333 static DECLCALLBACK(int) recordingCodecVPXEncode(PRECORDINGCODEC pCodec, PRECORDINGFRAME pFrame, 334 uint64_t msTimestamp, void *pvUser) 335 { 336 recordingCodecLock(pCodec); 337 338 int vrc; 339 340 /* If no frame is given, encode the last composed frame again with the given timestamp. */ 341 if (pFrame == NULL) 342 { 343 vrc = recordingCodecVPXEncodeWorker(pCodec, &pCodec->Video.VPX.RawImage, msTimestamp); 344 recordingCodecUnlock(pCodec); 345 return vrc; 346 } 347 348 RecordingContext *pCtx = (RecordingContext *)pvUser; 349 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 350 351 Assert(pFrame->msTimestamp == msTimestamp); 352 353 /* Note: We get BGRA 32 input here. */ 354 PRECORDINGVIDEOFRAME pFront = &pCodec->Video.VPX.Front; 355 PRECORDINGVIDEOFRAME pBack = &pCodec->Video.VPX.Back; 356 357 int32_t sx = 0; /* X origin within the source frame. */ 358 int32_t sy = 0; /* Y origin within the source frame. */ 359 int32_t sw = 0; /* Width of the source frame (starting at X origin). */ 360 int32_t sh = 0; /* Height of the source frame (starting at Y origin). */ 361 int32_t dx = 0; /* X destination of the source frame within the destination frame. */ 362 int32_t dy = 0; /* Y destination of the source frame within the destination frame. */ 363 364 /* 365 * Note! 366 * 367 * We don't implement any rendering graph or some such here, as we only have two things to render here, namely: 368 * 369 * - the actual framebuffer updates 370 * - if available (through mouse integration via Guest Additions): the guest's mouse cursor via a (software) overlay 371 * 372 * So composing is done the folowing way: 373 * 374 * - always store the plain framebuffer updates in our back buffer first 375 * - copy the framebuffer updates to our front buffer 376 * - restore the area of the old mouse cursor position by copying frame buffer area data from back -> front buffer 377 * - apply the mouse cursor updates to our front buffer 378 */ 379 380 switch (pFrame->enmType) 381 { 382 case RECORDINGFRAME_TYPE_VIDEO: 383 { 384 PRECORDINGVIDEOFRAME pSrc = &pFrame->u.Video; 385 386 vrc = RecordingVideoFrameBlitFrame(pFront, pSrc->Pos.x, pSrc->Pos.y, 387 pSrc, 0 /* uSrcX */, 0 /* uSrcY */, pSrc->Info.uWidth, pSrc->Info.uHeight); 388 #if 0 389 RecordingUtilsDbgDumpVideoFrameEx(pFront, "/tmp/recording", "encode-front"); 390 RecordingUtilsDbgDumpVideoFrameEx(pSrc, "/tmp/recording", "encode-src"); 391 #endif 392 vrc = RecordingVideoFrameBlitFrame(pBack, pSrc->Pos.x, pSrc->Pos.y, 393 pSrc, 0 /* uSrcX */, 0 /* uSrcY */, pSrc->Info.uWidth, pSrc->Info.uHeight); 394 pFront = pBack; 395 396 sw = pSrc->Info.uWidth; 397 sh = pSrc->Info.uHeight; 398 sx = pSrc->Pos.x; 399 sy = pSrc->Pos.y; 400 401 Log3Func(("RECORDINGFRAME_TYPE_VIDEO: sx=%d, sy=%d, sw=%d, sh=%d\n", sx, sy, sw, sh)); 402 403 dx = pSrc->Pos.x; 404 dy = pSrc->Pos.y; 405 break; 406 } 407 408 case RECORDINGFRAME_TYPE_CURSOR_SHAPE: 409 { 410 pCodec->Video.VPX.pCursorShape = RecordingVideoFrameDup(&pFrame->u.CursorShape); 411 AssertPtr(pCodec->Video.VPX.pCursorShape); 412 413 RT_FALL_THROUGH(); /* Re-render cursor with new shape below. */ 414 } 415 416 case RECORDINGFRAME_TYPE_CURSOR_POS: 417 { 418 const PRECORDINGVIDEOFRAME pCursor = pCodec->Video.VPX.pCursorShape; 419 if (!pCursor) /* No cursor shape set yet. */ 420 break; 421 422 PRECORDINGPOS pPosOld = &pCodec->Video.VPX.PosCursorOld; 423 PRECORDINGPOS pPosNew = pFrame->enmType == RECORDINGFRAME_TYPE_CURSOR_POS 424 ? &pFrame->u.Cursor.Pos 425 : pPosOld; 426 427 Log3Func(("RECORDINGFRAME_TYPE_CURSOR_POS: x=%d, y=%d, oldx=%d, oldy=%d, w=%d, h=%d\n", 428 pPosNew->x, pPosNew->y, 429 pPosOld->x, pPosOld->y, pCursor->Info.uWidth, pCursor->Info.uHeight)); 430 431 /* Calculate the merged area between the old and the new (current) cursor position 432 * so that we update everything to not create any ghosting. */ 433 sx = RT_MIN(pPosNew->x, pPosOld->x); 434 sy = RT_MIN(pPosNew->y, pPosOld->y); 435 sw = ( pPosNew->x > pPosOld->x 436 ? pPosNew->x - pPosOld->x 437 : pPosOld->x - pPosNew->x) + pCursor->Info.uWidth; 438 sh = ( pPosNew->y > pPosOld->y 439 ? pPosNew->y - pPosOld->y 440 : pPosOld->y - pPosNew->y) + pCursor->Info.uHeight; 441 442 /* Limit the width / height to blit to the front buffer's size. */ 443 if (sx + sw >= (int32_t)pFront->Info.uWidth) 444 sw = pFront->Info.uWidth - sx; 445 if (sy + sh >= (int32_t)pFront->Info.uHeight) 446 sh = pFront->Info.uHeight - sy; 447 448 /* Save current cursor position for next iteration. */ 449 *pPosOld = *pPosNew; 450 451 dx = sx; 452 dy = sy; 453 454 Log3Func(("RECORDINGFRAME_TYPE_CURSOR_POS: sx=%d, sy=%d, sw=%d, sh=%d\n", sx, sy, sw, sh)); 455 456 /* Nothing to encode? Bail out. */ 457 if ( sw <= 0 458 || sh <= 0 459 || (uint32_t)sx > pBack->Info.uWidth 460 || (uint32_t)sy > pBack->Info.uHeight) 461 break; 462 463 /* Restore background of front buffer first. */ 464 vrc = RecordingVideoFrameBlitFrame(pFront, dx, dy, 465 pBack, sx, sy, sw, sh); 466 467 /* Blit mouse cursor to front buffer. */ 468 if (RT_SUCCESS(vrc)) 469 vrc = RecordingVideoFrameBlitRawAlpha(pFront, pPosNew->x, pPosNew->y, 470 pCursor->pau8Buf, pCursor->cbBuf, 471 0 /* uSrcX */, 0 /* uSrcY */, pCursor->Info.uWidth, pCursor->Info.uHeight, 472 pCursor->Info.uBytesPerLine, pCursor->Info.uBPP, pCursor->Info.enmPixelFmt); 473 #if 0 474 RecordingUtilsDbgDumpVideoFrameEx(pFront, "/tmp/recording", "cursor-alpha-front"); 475 #endif 476 break; 477 } 478 479 default: 480 AssertFailed(); 481 break; 482 483 } 484 485 /* Nothing to encode? Bail out. */ 486 if ( sw == 0 487 || sh == 0) 488 { 489 recordingCodecUnlock(pCodec); 490 return VINF_SUCCESS; 491 } 492 493 Log3Func(("Encoding video parameters: %RU16x%RU16 (%RU8 FPS), originX=%RI32, originY=%RI32\n", 494 pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight, pCodec->Parms.u.Video.uFPS, 495 pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginX, pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginY)); 496 497 int32_t sx_b = sx; /* X origin within the source frame. */ 498 int32_t sy_b = sy; /* Y origin within the source frame. */ 499 int32_t sw_b = sw; /* Width of the source frame (starting at X origin). */ 500 int32_t sh_b = sh; /* Height of the source frame (starting at Y origin). */ 501 int32_t dx_b = dx; /* X destination of the source frame within the destination frame. */ 502 int32_t dy_b = dy; /* Y destination of the source frame within the destination frame. */ 503 504 RT_NOREF(sx_b); 505 RT_NOREF(sy_b); 506 RT_NOREF(sw_b); 507 RT_NOREF(sh_b); 508 RT_NOREF(dx_b); 509 RT_NOREF(dy_b); 510 511 vrc = RecordingUtilsCoordsCropCenter(&pCodec->Parms, &sx, &sy, &sw, &sh, &dx, &dy); 512 if (vrc == VINF_SUCCESS) /* vrc might be VWRN_RECORDING_ENCODING_SKIPPED to skip encoding. */ 513 { 514 Log3Func(("Encoding source %RI32,%RI32 (%RI32x%RI32) to %RI32,%RI32 (%zu bytes)\n", 515 sx, sy, sw, sh, dx, dy, sw * sh * (pFront->Info.uBPP / 8))); 516 #ifdef DEBUG 517 AssertReturn(sw <= (int32_t)pFront->Info.uWidth, VERR_INVALID_PARAMETER); 518 AssertReturn(sh <= (int32_t)pFront->Info.uHeight, VERR_INVALID_PARAMETER); 519 AssertReturn(sx + sw <= (int32_t)pFront->Info.uWidth , VERR_INVALID_PARAMETER); 520 AssertReturn(sy + sh <= (int32_t)pFront->Info.uHeight, VERR_INVALID_PARAMETER); 521 #endif 522 523 #if 0 524 RecordingUtilsDbgDumpImageData(&pFront->pau8Buf[(sy * pFront->Info.uBytesPerLine) + (sx * (pFront->Info.uBPP / 8))], pFront->cbBuf, 525 "/tmp/recording", "cropped", sw, sh, pFront->Info.uBytesPerLine, pFront->Info.uBPP); 526 #endif 527 /* Blit (and convert from BGRA 32) the changed parts of the front buffer to the YUV 420 surface of the codec. */ 528 RecordingUtilsConvBGRA32ToYUVI420Ex(/* Destination */ 529 pCodec->Video.VPX.pu8YuvBuf, dx, dy, pCodec->Parms.u.Video.uWidth, pCodec->Parms.u.Video.uHeight, 530 /* Source */ 531 pFront->pau8Buf, sx, sy, sw, sh, pFront->Info.uBytesPerLine, pFront->Info.uBPP); 532 533 vrc = recordingCodecVPXEncodeWorker(pCodec, &pCodec->Video.VPX.RawImage, msTimestamp); 534 } 535 536 recordingCodecUnlock(pCodec); 537 538 return vrc; 539 } 540 541 /** @copydoc RECORDINGCODECOPS::pfnScreenChange */ 542 static DECLCALLBACK(int) recordingCodecVPXScreenChange(PRECORDINGCODEC pCodec, PRECORDINGSURFACEINFO pInfo) 543 { 544 LogRel2(("Recording: Codec got screen change notification (%RU16x%RU16, %RU8 BPP)\n", 545 pInfo->uWidth, pInfo->uHeight, pInfo->uBPP)); 546 547 /* Fend-off bogus reports. */ 548 if ( !pInfo->uWidth 549 || !pInfo->uHeight) 550 return VERR_INVALID_PARAMETER; 551 552 /** @todo BUGBUG Not sure why we sometimes get 0 BPP for a display change from Main. 553 * For now we ASSUME 32 BPP. */ 554 if (!pInfo->uBPP) 555 pInfo->uBPP = 32; 556 557 /* The VPX encoder only understands even frame sizes. */ 558 if ( (pInfo->uWidth % 2) != 0 559 || (pInfo->uHeight % 2) != 0) 560 return VERR_INVALID_PARAMETER; 561 562 PRECORDINGCODECVPX pVPX = &pCodec->Video.VPX; 563 564 recordingCodecLock(pCodec); 565 566 /* Tear down old stuff. */ 567 RecordingVideoFrameDestroy(&pVPX->Front); 568 RecordingVideoFrameDestroy(&pVPX->Back); 569 570 /* Initialize front + back buffers. */ 571 int vrc = RecordingVideoFrameInit(&pVPX->Front, RECORDINGVIDEOFRAME_F_VISIBLE, 572 pInfo->uWidth, pInfo->uHeight, 0, 0, 573 pInfo->uBPP, pInfo->enmPixelFmt); 574 if (RT_SUCCESS(vrc)) 575 vrc = RecordingVideoFrameInit(&pVPX->Back, RECORDINGVIDEOFRAME_F_VISIBLE, 576 pInfo->uWidth, pInfo->uHeight, 0, 0, 577 pInfo->uBPP, pInfo->enmPixelFmt); 578 if (RT_SUCCESS(vrc)) 579 { 580 RecordingVideoFrameClear(&pVPX->Front); 581 RecordingVideoFrameClear(&pVPX->Back); 582 583 recordingCodecVPXClearPlanes(pCodec); 584 585 /* Calculate the X/Y origins for cropping / centering. 586 * This is needed as the codec's video output size not necessarily matches the VM's frame buffer size. */ 587 pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginX = int32_t(pCodec->Parms.u.Video.uWidth - pInfo->uWidth) / 2; 588 pCodec->Parms.u.Video.Scaling.u.Crop.m_iOriginY = int32_t(pCodec->Parms.u.Video.uHeight - pInfo->uHeight) / 2; 589 } 590 591 recordingCodecUnlock(pCodec); 592 593 if (RT_FAILURE(vrc)) 594 LogRel(("Recording: Codec error handling screen change notification: %Rrc\n", vrc)); 595 596 return vrc; 597 598 } 256 599 #endif /* VBOX_WITH_LIBVPX */ 257 600 … … 269 612 AssertPtrReturn(pCodec->pvScratch, VERR_NO_MEMORY); 270 613 271 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms. Audio.PCMProps;614 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.u.Audio.PCMProps; 272 615 273 616 /** @todo BUGBUG When left out this call, vorbis_block_init() does not find oggpack_writeinit and all goes belly up ... */ … … 336 679 /** @copydoc RECORDINGCODECOPS::pfnEncode */ 337 680 static DECLCALLBACK(int) recordingCodecVorbisEncode(PRECORDINGCODEC pCodec, 338 const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded) 339 { 340 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.Audio.PCMProps; 681 const PRECORDINGFRAME pFrame, uint64_t msTimestamp, void *pvUser) 682 { 683 RT_NOREF(msTimestamp, pvUser); 684 685 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.u.Audio.PCMProps; 341 686 342 687 Assert (pCodec->Parms.cbFrame); 343 AssertReturn(pFrame-> Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER);344 Assert (pFrame-> Audio.cbBuf);345 AssertReturn(pFrame-> Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER);346 AssertReturn(pCodec->cbScratch >= pFrame-> Audio.cbBuf, VERR_INVALID_PARAMETER);688 AssertReturn(pFrame->u.Audio.cbBuf % pCodec->Parms.cbFrame == 0, VERR_INVALID_PARAMETER); 689 Assert (pFrame->u.Audio.cbBuf); 690 AssertReturn(pFrame->u.Audio.cbBuf % PDMAudioPropsFrameSize(pPCMProps) == 0, VERR_INVALID_PARAMETER); 691 AssertReturn(pCodec->cbScratch >= pFrame->u.Audio.cbBuf, VERR_INVALID_PARAMETER); 347 692 348 693 int vrc = VINF_SUCCESS; 349 694 350 695 int const cbFrame = PDMAudioPropsFrameSize(pPCMProps); 351 int const cFrames = (int)(pFrame-> Audio.cbBuf / cbFrame);696 int const cFrames = (int)(pFrame->u.Audio.cbBuf / cbFrame); 352 697 353 698 /* Write non-interleaved frames. */ 354 699 float **buffer = vorbis_analysis_buffer(&pCodec->Audio.Vorbis.dsp_state, cFrames); 355 int16_t *puSrc = (int16_t *)pFrame-> Audio.pvBuf; RT_NOREF(puSrc);700 int16_t *puSrc = (int16_t *)pFrame->u.Audio.pvBuf; RT_NOREF(puSrc); 356 701 357 702 /* Convert samples into floating point. */ … … 375 720 return VERR_RECORDING_ENCODING_FAILED; 376 721 } 377 378 if (pcEncoded)379 *pcEncoded = 0;380 if (pcbEncoded)381 *pcbEncoded = 0;382 722 383 723 size_t cBlocksEncoded = 0; … … 439 779 } 440 780 441 if (pcbEncoded)442 *pcbEncoded = 0;443 if (pcEncoded)444 *pcEncoded = 0;445 446 781 if (RT_FAILURE(vrc)) 447 782 LogRel(("Recording: Encoding Vorbis audio data failed, vrc=%Rrc\n", vrc)); 448 783 449 784 Log3Func(("cbSrc=%zu, cbDst=%zu, cEncoded=%zu, cbEncoded=%zu, vrc=%Rrc\n", 450 pFrame->Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc)); 451 452 return vrc; 453 } 454 785 pFrame->u.Audio.cbBuf, pCodec->cbScratch, cBlocksEncoded, cBytesEncoded, vrc)); 786 787 return vrc; 788 } 789 790 /** @copydoc RECORDINGCODECOPS::pfnFinalize */ 455 791 static DECLCALLBACK(int) recordingCodecVorbisFinalize(PRECORDINGCODEC pCodec) 456 792 { … … 488 824 LogRel(("Recording: Initializing audio codec '%s'\n", strCodec.c_str())); 489 825 490 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms. Audio.PCMProps;826 const PPDMAUDIOPCMPROPS pPCMProps = &pCodec->Parms.u.Audio.PCMProps; 491 827 492 828 PDMAudioPropsInit(pPCMProps, … … 546 882 LogRel(("Recording: Initializing video codec '%s'\n", strTemp.c_str())); 547 883 548 pCodec->Parms.uBitrate = Settings.Video.ulRate;549 pCodec->Parms. Video.uFPS = Settings.Video.ulFPS;550 pCodec->Parms. Video.uWidth = Settings.Video.ulWidth;551 pCodec->Parms. Video.uHeight = Settings.Video.ulHeight;552 pCodec->Parms. Video.uDelayMs = RT_MS_1SEC / pCodec->Parms.Video.uFPS;884 pCodec->Parms.uBitrate = Settings.Video.ulRate; 885 pCodec->Parms.u.Video.uFPS = Settings.Video.ulFPS; 886 pCodec->Parms.u.Video.uWidth = Settings.Video.ulWidth; 887 pCodec->Parms.u.Video.uHeight = Settings.Video.ulHeight; 888 pCodec->Parms.u.Video.uDelayMs = RT_MS_1SEC / pCodec->Parms.u.Video.uFPS; 553 889 554 890 if (pCallbacks) … … 556 892 557 893 AssertReturn(pCodec->Parms.uBitrate, VERR_INVALID_PARAMETER); /* Bitrate must be set. */ 558 AssertStmt(pCodec->Parms. Video.uFPS, pCodec->Parms.Video.uFPS = 25); /* Prevent division by zero. */559 560 AssertReturn(pCodec->Parms. Video.uHeight, VERR_INVALID_PARAMETER);561 AssertReturn(pCodec->Parms. Video.uWidth, VERR_INVALID_PARAMETER);562 AssertReturn(pCodec->Parms. Video.uDelayMs, VERR_INVALID_PARAMETER);894 AssertStmt(pCodec->Parms.u.Video.uFPS, pCodec->Parms.u.Video.uFPS = 25); /* Prevent division by zero. */ 895 896 AssertReturn(pCodec->Parms.u.Video.uHeight, VERR_INVALID_PARAMETER); 897 AssertReturn(pCodec->Parms.u.Video.uWidth, VERR_INVALID_PARAMETER); 898 AssertReturn(pCodec->Parms.u.Video.uDelayMs, VERR_INVALID_PARAMETER); 563 899 564 900 int vrc = VINF_SUCCESS; … … 602 938 if (value.compare("low", com::Utf8Str::CaseInsensitive) == 0) 603 939 { 604 PDMAudioPropsInit(&pCodec->Parms. Audio.PCMProps, 16, true /* fSigned */, 1 /* Channels */, 8000 /* Hz */);940 PDMAudioPropsInit(&pCodec->Parms.u.Audio.PCMProps, 16, true /* fSigned */, 1 /* Channels */, 8000 /* Hz */); 605 941 } 606 942 else if (value.startsWith("med" /* "med[ium]" */, com::Utf8Str::CaseInsensitive) == 0) … … 610 946 else if (value.compare("high", com::Utf8Str::CaseInsensitive) == 0) 611 947 { 612 PDMAudioPropsInit(&pCodec->Parms. Audio.PCMProps, 16, true /* fSigned */, 2 /* Channels */, 48000 /* Hz */);948 PDMAudioPropsInit(&pCodec->Parms.u.Audio.PCMProps, 16, true /* fSigned */, 2 /* Channels */, 48000 /* Hz */); 613 949 } 614 950 } … … 625 961 { 626 962 pCodec->State.tsLastWrittenMs = 0; 627 628 963 pCodec->State.cEncErrors = 0; 629 #ifdef VBOX_WITH_STATISTICS630 pCodec->STAM.cEncBlocks = 0;631 pCodec->STAM.msEncTotal = 0;632 #endif633 964 } 634 965 … … 708 1039 pCodec->Ops.pfnInit = recordingCodecVPXInit; 709 1040 pCodec->Ops.pfnDestroy = recordingCodecVPXDestroy; 1041 pCodec->Ops.pfnFinalize = recordingCodecVPXFinalize; 710 1042 pCodec->Ops.pfnParseOptions = recordingCodecVPXParseOptions; 711 1043 pCodec->Ops.pfnEncode = recordingCodecVPXEncode; 1044 pCodec->Ops.pfnScreenChange = recordingCodecVPXScreenChange; 712 1045 713 1046 vrc = VINF_SUCCESS; … … 740 1073 int recordingCodecInit(const PRECORDINGCODEC pCodec, const PRECORDINGCODECCALLBACKS pCallbacks, const settings::RecordingScreenSettings &Settings) 741 1074 { 1075 int vrc = RTCritSectInit(&pCodec->CritSect); 1076 AssertRCReturn(vrc, vrc); 1077 1078 pCodec->cbScratch = 0; 1079 pCodec->pvScratch = NULL; 1080 742 1081 recordingCodecReset(pCodec); 743 1082 744 int vrc;745 1083 if (pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO) 746 1084 vrc = recordingCodecInitAudio(pCodec, pCallbacks, Settings); … … 811 1149 pCodec->Parms.enmType = RECORDINGCODECTYPE_INVALID; 812 1150 pCodec->Parms.enmVideoCodec = RecordingVideoCodec_None; 813 } 814 815 return vrc; 816 } 817 818 /** 819 * Feeds the codec encoder with data to encode. 1151 1152 int vrc2 = RTCritSectDelete(&pCodec->CritSect); 1153 AssertRC(vrc2); 1154 } 1155 1156 return vrc; 1157 } 1158 1159 /** 1160 * Feeds the codec encoder with frame data to encode. 820 1161 * 821 1162 * @returns VBox status code. 822 1163 * @param pCodec Codec to use. 823 1164 * @param pFrame Pointer to frame data to encode. 824 * @param pcEncoded Where to return the number of encoded blocks in \a pvDst on success. Optional. 825 * @param pcbEncoded Where to return the number of encoded bytes in \a pvDst on success. Optional. 826 */ 827 int recordingCodecEncode(PRECORDINGCODEC pCodec, 828 const PRECORDINGFRAME pFrame, size_t *pcEncoded, size_t *pcbEncoded) 1165 * @param msTimestamp Timestamp (PTS) to use for encoding. 1166 * @param pvUser User data pointer. Optional and can be NULL. 1167 */ 1168 int recordingCodecEncodeFrame(PRECORDINGCODEC pCodec, const PRECORDINGFRAME pFrame, uint64_t msTimestamp, void *pvUser) 829 1169 { 830 1170 AssertPtrReturn(pCodec->Ops.pfnEncode, VERR_NOT_SUPPORTED); 831 1171 832 size_t cEncoded, cbEncoded; 833 int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, &cEncoded, &cbEncoded); 1172 int vrc = pCodec->Ops.pfnEncode(pCodec, pFrame, msTimestamp, pvUser); 834 1173 if (RT_SUCCESS(vrc)) 835 {836 1174 pCodec->State.tsLastWrittenMs = pFrame->msTimestamp; 837 1175 838 #ifdef VBOX_WITH_STATISTICS 839 pCodec->STAM.cEncBlocks += cEncoded; 840 pCodec->STAM.msEncTotal += pCodec->Parms.msFrame * cEncoded; 841 #endif 842 if (pcEncoded) 843 *pcEncoded = cEncoded; 844 if (pcbEncoded) 845 *pcbEncoded = cbEncoded; 846 } 847 848 return vrc; 1176 return vrc; 1177 } 1178 1179 /** 1180 * Feeds the codec encoder with the current composed image. 1181 * 1182 * @returns VBox status code. 1183 * @param pCodec Codec to use. 1184 * @param msTimestamp Timestamp (PTS) to use for encoding. 1185 */ 1186 int recordingCodecEncodeCurrent(PRECORDINGCODEC pCodec, uint64_t msTimestamp) 1187 { 1188 int vrc = pCodec->Ops.pfnEncode(pCodec, NULL /* pFrame */, msTimestamp, NULL /* pvUser */); 1189 if (RT_SUCCESS(vrc)) 1190 pCodec->State.tsLastWrittenMs = msTimestamp; 1191 1192 return vrc; 1193 } 1194 1195 /** 1196 * Lets the codec know that a screen change has happened. 1197 * 1198 * @returns VBox status code. 1199 * @param pCodec Codec to use. 1200 * @param pInfo Screen info to send. 1201 */ 1202 int recordingCodecScreenChange(PRECORDINGCODEC pCodec, PRECORDINGSURFACEINFO pInfo) 1203 { 1204 if (!pCodec->Ops.pfnScreenChange) 1205 return VINF_SUCCESS; 1206 1207 return pCodec->Ops.pfnScreenChange(pCodec, pInfo); 849 1208 } 850 1209 … … 885 1244 { 886 1245 Log3Func(("%RU64 -- tsLastWrittenMs=%RU64 + uDelayMs=%RU32\n", 887 msTimestamp, pCodec->State.tsLastWrittenMs,pCodec->Parms. Video.uDelayMs));888 889 if (msTimestamp < pCodec->State.tsLastWrittenMs + pCodec->Parms. Video.uDelayMs)1246 msTimestamp, pCodec->State.tsLastWrittenMs,pCodec->Parms.u.Video.uDelayMs)); 1247 1248 if (msTimestamp < pCodec->State.tsLastWrittenMs + pCodec->Parms.u.Video.uDelayMs) 890 1249 return 0; /* Too early for writing (respect set FPS). */ 891 1250 … … 894 1253 return pCodec->Parms.cbFrame; 895 1254 } 1255 -
trunk/src/VBox/Main/src-client/RecordingInternals.cpp
r99739 r105006 27 27 28 28 #include "RecordingInternals.h" 29 #include "RecordingUtils.h" 29 30 30 31 #include <iprt/assert.h> 31 32 #include <iprt/mem.h> 32 33 33 #ifdef VBOX_WITH_AUDIO_RECORDING 34 35 /** 36 * Initializes a recording frame. 37 * 34 #ifdef DEBUG 35 # include <math.h> 36 # include <iprt/file.h> 37 # include <iprt/formats/bmp.h> 38 #endif 39 40 #include "../src-client/Remotery.h" 41 42 43 /********************************************************************************************************************************* 44 * Prototypes * 45 *********************************************************************************************************************************/ 46 DECLINLINE(int) recordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, uint32_t fFlags, uint32_t uWidth, uint32_t uHeight, uint32_t uPosX, uint32_t uPosY, 47 uint8_t uBPP, RECORDINGPIXELFMT enmFmt); 48 49 50 /** 51 * Allocates an empty video frame, inline version. 52 * 53 * @returns Allocated video frame on success, or NULL on failure. 54 */ 55 DECLINLINE(PRECORDINGVIDEOFRAME) recordingVideoFrameAlloc(void) 56 { 57 return (PRECORDINGVIDEOFRAME)RTMemAlloc(sizeof(RECORDINGVIDEOFRAME)); 58 } 59 60 /** 61 * Allocates an empty video frame. 62 * 63 * @returns Allocated video frame on success, or NULL on failure. 64 */ 65 PRECORDINGVIDEOFRAME RecordingVideoFrameAlloc(void) 66 { 67 PRECORDINGVIDEOFRAME pFrame = recordingVideoFrameAlloc(); 68 AssertPtrReturn(pFrame, NULL); 69 RT_BZERO(pFrame, sizeof(RECORDINGVIDEOFRAME)); 70 return pFrame; 71 } 72 73 /** 74 * Returns an allocated video frame from given image data. 75 * 76 * @returns Allocated video frame on success, or NULL on failure. 77 * @param pvData Pointer to image data to use. 78 * @param x X location hint (in pixel) to use for allocated frame. 79 * This is *not* the offset within \a pvData! 80 * @param y X location hint (in pixel) to use for allocated frame. 81 * This is *not* the offset within \a pvData! 82 * @param w Width (in pixel) of \a pvData image data. 83 * @param h Height (in pixel) of \a pvData image data. 84 * @param uBPP Bits per pixel) of \a pvData image data. 85 * @param enmFmt Pixel format of \a pvData image data. 86 */ 87 PRECORDINGVIDEOFRAME RecordingVideoFrameAllocEx(const void *pvData, uint32_t x, uint32_t y, uint32_t w, uint32_t h, 88 uint8_t uBPP, RECORDINGPIXELFMT enmFmt) 89 { 90 PRECORDINGVIDEOFRAME pFrame = recordingVideoFrameAlloc(); 91 AssertPtrReturn(pFrame, NULL); 92 int rc = recordingVideoFrameInit(pFrame, RECORDINGVIDEOFRAME_F_VISIBLE, w, h, x, y, uBPP, enmFmt); 93 AssertRCReturn(rc, NULL); 94 memcpy(pFrame->pau8Buf, pvData, pFrame->cbBuf); 95 96 return VINF_SUCCESS; 97 } 98 99 /** 100 * Frees a recording video frame. 101 * 102 * @param pFrame Pointer to video frame to free. The pointer will be invalid after return. 103 */ 104 void RecordingVideoFrameFree(PRECORDINGVIDEOFRAME pFrame) 105 { 106 if (!pFrame) 107 return; 108 109 RecordingVideoFrameDestroy(pFrame); 110 111 RTMemFree(pFrame); 112 } 113 114 /** 115 * Initializes a recording frame, inline version. 116 * 117 * @returns VBox status code. 38 118 * @param pFrame Pointer to video frame to initialize. 39 * @param w Width (in pixel) of video frame. 40 * @param h Height (in pixel) of video frame. 119 * @param fFlags Flags of type RECORDINGVIDEOFRAME_F_XXX. 120 * @param uWidth Width (in pixel) of video frame. 121 * @param uHeight Height (in pixel) of video frame. 122 * @param uPosX X positioning hint. 123 * @param uPosY Y positioning hint. 41 124 * @param uBPP Bits per pixel (BPP). 42 * @param enmPixelFmt Pixel format to use. 43 */ 44 int RecordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, int w, int h, uint8_t uBPP, RECORDINGPIXELFMT enmPixelFmt) 45 { 125 * @param enmFmt Pixel format to use. 126 */ 127 DECLINLINE(int) recordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, uint32_t fFlags, uint32_t uWidth, uint32_t uHeight, 128 uint32_t uPosX, uint32_t uPosY, uint8_t uBPP, RECORDINGPIXELFMT enmFmt) 129 { 130 AssertPtrReturn(pFrame, VERR_INVALID_POINTER); 131 AssertReturn(uWidth, VERR_INVALID_PARAMETER); 132 AssertReturn(uHeight, VERR_INVALID_PARAMETER); 133 AssertReturn(uBPP && uBPP % 8 == 0, VERR_INVALID_PARAMETER); 134 46 135 /* Calculate bytes per pixel and set pixel format. */ 47 136 const unsigned uBytesPerPixel = uBPP / 8; 48 const size_t cbRGBBuf = w * h * uBytesPerPixel; 137 138 /* Calculate bytes per pixel and set pixel format. */ 139 const size_t cbRGBBuf = uWidth * uHeight * uBytesPerPixel; 49 140 AssertReturn(cbRGBBuf, VERR_INVALID_PARAMETER); 50 141 51 pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf); 52 AssertPtrReturn(pFrame->pu8RGBBuf, VERR_NO_MEMORY); 53 pFrame->cbRGBBuf = cbRGBBuf; 54 55 pFrame->uX = 0; 56 pFrame->uY = 0; 57 pFrame->uWidth = w; 58 pFrame->uHeight = h; 59 pFrame->enmPixelFmt = enmPixelFmt; 60 pFrame->uBPP = uBPP; 61 pFrame->uBytesPerLine = w * uBytesPerPixel; 142 pFrame->pau8Buf = (uint8_t *)RTMemAlloc(cbRGBBuf); 143 AssertPtrReturn(pFrame->pau8Buf, VERR_NO_MEMORY); 144 pFrame->cbBuf = cbRGBBuf; 145 146 pFrame->fFlags = fFlags; 147 pFrame->Info.uWidth = uWidth; 148 pFrame->Info.uHeight = uHeight; 149 pFrame->Info.uBPP = uBPP; 150 pFrame->Info.enmPixelFmt = enmFmt; 151 pFrame->Info.uBytesPerLine = uWidth * uBytesPerPixel; 152 pFrame->Pos.x = uPosX; 153 pFrame->Pos.y = uPosY; 62 154 63 155 return VINF_SUCCESS; … … 65 157 66 158 /** 159 * Initializes a recording frame. 160 * 161 * @param pFrame Pointer to video frame to initialize. 162 * @param fFlags Flags of type RECORDINGVIDEOFRAME_F_XXX. 163 * @param uWidth Width (in pixel) of video frame. 164 * @param uHeight Height (in pixel) of video frame. 165 * @param uPosX X positioning hint. 166 * @param uPosY Y positioning hint. 167 * @param uBPP Bits per pixel (BPP). 168 * @param enmFmt Pixel format to use. 169 */ 170 int RecordingVideoFrameInit(PRECORDINGVIDEOFRAME pFrame, uint32_t fFlags, uint32_t uWidth, uint32_t uHeight, uint32_t uPosX, uint32_t uPosY, 171 uint8_t uBPP, RECORDINGPIXELFMT enmFmt) 172 { 173 return recordingVideoFrameInit(pFrame, fFlags, uWidth, uHeight, uPosX, uPosY, uBPP, enmFmt); 174 } 175 176 /** 177 * Destroys a recording video frame. 178 * 179 * @param pFrame Pointer to video frame to destroy. 180 */ 181 void RecordingVideoFrameDestroy(PRECORDINGVIDEOFRAME pFrame) 182 { 183 if (!pFrame) 184 return; 185 186 if (pFrame->pau8Buf) 187 { 188 Assert(pFrame->cbBuf); 189 RTMemFree(pFrame->pau8Buf); 190 pFrame->pau8Buf = NULL; 191 pFrame->cbBuf = 0; 192 } 193 } 194 195 /** 196 * Duplicates a video frame. 197 * 198 * @returns Pointer to duplicated frame on success, or NULL on failure. 199 * @param pFrame Video frame to duplicate. 200 */ 201 PRECORDINGVIDEOFRAME RecordingVideoFrameDup(PRECORDINGVIDEOFRAME pFrame) 202 { 203 PRECORDINGVIDEOFRAME pFrameDup = (PRECORDINGVIDEOFRAME)RTMemDup(pFrame, sizeof(RECORDINGVIDEOFRAME)); 204 AssertPtrReturn(pFrameDup, NULL); 205 pFrameDup->pau8Buf = (uint8_t *)RTMemDup(pFrame->pau8Buf, pFrame->cbBuf); 206 AssertPtrReturnStmt(pFrameDup, RTMemFree(pFrameDup), NULL); 207 208 return pFrameDup; 209 } 210 211 /** 212 * Clears the content of a video recording frame, inlined version. 213 * 214 * @param pFrame Video recording frame to clear content for. 215 */ 216 DECLINLINE(void) recordingVideoFrameClear(PRECORDINGVIDEOFRAME pFrame) 217 { 218 RT_BZERO(pFrame->pau8Buf, pFrame->cbBuf); 219 } 220 221 /** 222 * Clears the content of a video recording frame. 223 * 224 * @param pFrame Video recording frame to clear content for. 225 */ 226 void RecordingVideoFrameClear(PRECORDINGVIDEOFRAME pFrame) 227 { 228 recordingVideoFrameClear(pFrame); 229 } 230 231 /** 232 * Simple blitting function for raw image data, inlined version. 233 * 234 * @returns VBox status code. 235 * @param pFrame Destination frame. 236 * @param uDstX X destination (in pixel) within destination frame. 237 * @param uDstY Y destination (in pixel) within destination frame. 238 * @param uDstBytesPerLine Bytes per line in destination buffer. 239 * @param uDstBPP BPP of destination buffer. 240 * @param enmDstFmt Pixel format of source data. Must match \a pFrame. 241 * @param pu8Src Source data to blit. Must be in the same pixel format as \a pFrame. 242 * @param cbSrc Size (in bytes) of \a pu8Src. 243 * @param uSrcX X start (in pixel) within source data. 244 * @param uSrcY Y start (in pixel) within source data. 245 * @param uSrcWidth Width (in pixel) to blit from source data. 246 * @param uSrcHeight Height (in pixel) to blit from data. 247 * @param uSrcBytesPerLine Bytes per line in source data. 248 * @param uSrcBPP BPP of source data. Must match \a pFrame. 249 * @param enmSrcFmt Pixel format of source data. Must match \a pFrame. 250 */ 251 DECLINLINE(int) recordingVideoFrameBlitRaw(uint8_t *pu8Dst, size_t cbDst, uint32_t uDstX, uint32_t uDstY, 252 uint32_t uDstBytesPerLine, uint8_t uDstBPP, RECORDINGPIXELFMT enmDstFmt, 253 const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight, 254 uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmSrcFmt) 255 { 256 RT_NOREF(enmDstFmt, enmSrcFmt); 257 258 uint8_t const uDstBytesPerPixel = uDstBPP / 8; 259 uint8_t const uSrcBytesPerPixel = uSrcBPP / 8; 260 261 size_t offSrc = RT_MIN(uSrcY * uSrcBytesPerLine + uSrcX * uSrcBytesPerPixel, cbSrc); 262 size_t offDst = RT_MIN(uDstY * uDstBytesPerLine + uDstX * uDstBytesPerPixel, cbDst); 263 264 for (uint32_t y = 0; y < uSrcHeight; y++) 265 { 266 size_t const cbToCopy = RT_MIN(cbDst - offDst, 267 RT_MIN(uSrcWidth * uSrcBytesPerPixel, cbSrc - offSrc)); 268 if (!cbToCopy) 269 break; 270 memcpy(pu8Dst + offDst, (const uint8_t *)pu8Src + offSrc, cbToCopy); 271 offDst = RT_MIN(offDst + uDstBytesPerLine, cbDst); 272 Assert(offDst <= cbDst); 273 offSrc = RT_MIN(offSrc + uSrcBytesPerLine, cbSrc); 274 Assert(offSrc <= cbSrc); 275 } 276 277 return VINF_SUCCESS; 278 } 279 280 /** 281 * Simple blitting function for raw image data with alpha channel, inlined version. 282 * 283 * @returns VBox status code. 284 * @param pFrame Destination frame. 285 * @param uDstX X destination (in pixel) within destination frame. 286 * @param uDstY Y destination (in pixel) within destination frame. 287 * @param pu8Src Source data to blit. Must be in the same pixel format as \a pFrame. 288 * @param cbSrc Size (in bytes) of \a pu8Src. 289 * @param uSrcX X start (in pixel) within source data. 290 * @param uSrcY Y start (in pixel) within source data. 291 * @param uSrcWidth Width (in pixel) to blit from source data. 292 * @param uSrcHeight Height (in pixel) to blit from data. 293 * @param uSrcBytesPerLine Bytes per line in source data. 294 * @param uSrcBPP BPP of source data. Must match \a pFrame. 295 * @param enmFmt Pixel format of source data. Must match \a pFrame. 296 */ 297 DECLINLINE(int) recordingVideoFrameBlitRawAlpha(PRECORDINGVIDEOFRAME pFrame, uint32_t uDstX, uint32_t uDstY, 298 const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight, 299 uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmFmt) 300 { 301 AssertReturn(pFrame->Info.enmPixelFmt == enmFmt, VERR_NOT_SUPPORTED); 302 AssertReturn(pFrame->Info.uBPP == uSrcBPP, VERR_NOT_SUPPORTED); 303 304 RT_NOREF(uDstX, uDstY, cbSrc, uSrcX, uSrcY, uSrcBytesPerLine); 305 uint8_t const uDstBytesPerPixel = pFrame->Info.uBPP / 8; 306 uint8_t const uSrcBytesPerPixel = uSrcBPP / 8; 307 308 for (uint32_t y = 0; y < uSrcHeight; y++) 309 { 310 size_t offSrc = RT_MIN((uSrcY + y) * uSrcBytesPerLine + uSrcX * uSrcBytesPerPixel, cbSrc); 311 size_t offDst = RT_MIN((uDstY + y) * pFrame->Info.uBytesPerLine + uDstX * uDstBytesPerPixel, pFrame->cbBuf); 312 313 for (uint32_t x = 0; x < uSrcWidth; x++) 314 { 315 /* BGRA */ 316 int const idx_b = 0; 317 int const idx_g = 1; 318 int const idx_r = 2; 319 int const idx_a = 3; 320 321 unsigned int const alpha = pu8Src[offSrc + idx_a] + 1; 322 unsigned int const inv_alpha = 256 - pu8Src[offSrc + idx_a]; 323 if (pu8Src[offSrc + idx_a]) 324 { 325 pFrame->pau8Buf[offDst + idx_r] = (unsigned char)((alpha * pu8Src[offSrc + idx_r] + inv_alpha * pFrame->pau8Buf[offDst + idx_r]) >> 8); 326 pFrame->pau8Buf[offDst + idx_g] = (unsigned char)((alpha * pu8Src[offSrc + idx_g] + inv_alpha * pFrame->pau8Buf[offDst + idx_g]) >> 8); 327 pFrame->pau8Buf[offDst + idx_b] = (unsigned char)((alpha * pu8Src[offSrc + idx_b] + inv_alpha * pFrame->pau8Buf[offDst + idx_b]) >> 8); 328 pFrame->pau8Buf[offDst + idx_a] = 0xff; 329 } 330 331 offSrc = RT_MIN(offSrc + uSrcBytesPerPixel, cbSrc); 332 if (offSrc >= cbSrc) 333 break; 334 offDst = RT_MIN(offDst + uDstBytesPerPixel, pFrame->cbBuf); 335 if (offDst >= pFrame->cbBuf) 336 break; 337 } 338 } 339 340 #if 0 341 RecordingUtilsDbgDumpImageData(pu8Src, cbSrc, "/tmp", "cursor-src", uSrcWidth, uSrcHeight, uSrcBytesPerLine, 32); 342 RecordingUtilsDbgDumpVideoFrameEx(pFrame, "/tmp", "cursor-dst"); 343 #endif 344 345 return VINF_SUCCESS; 346 } 347 348 /** 349 * Simple blitting function for raw image data. 350 * 351 * @returns VBox status code. 352 * @param pDstFrame Destination frame. 353 * @param uDstX X destination (in pixel) within destination frame. 354 * @param uDstY Y destination (in pixel) within destination frame. 355 * @param pu8Src Source data to blit. Must be in the same pixel format as \a pFrame. 356 * @param cbSrc Size (in bytes) of \a pu8Src. 357 * @param uSrcX X start (in pixel) within source data. 358 * @param uSrcY Y start (in pixel) within source data. 359 * @param uSrcWidth Width (in pixel) to blit from source data. 360 * @param uSrcHeight Height (in pixel) to blit from data. 361 * @param uSrcBytesPerLine Bytes per line in source data. 362 * @param uSrcBPP BPP of source data. Must match \a pFrame. 363 * @param enmFmt Pixel format of source data. Must match \a pFrame. 364 */ 365 int RecordingVideoFrameBlitRaw(PRECORDINGVIDEOFRAME pDstFrame, uint32_t uDstX, uint32_t uDstY, 366 const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight, 367 uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmFmt) 368 { 369 return recordingVideoFrameBlitRaw(/* Destination */ 370 pDstFrame->pau8Buf, pDstFrame->cbBuf, uDstX, uDstY, 371 pDstFrame->Info.uBytesPerLine, pDstFrame->Info.uBPP, pDstFrame->Info.enmPixelFmt, 372 /* Source */ 373 pu8Src, cbSrc, uSrcX, uSrcY, uSrcWidth, uSrcHeight, uSrcBytesPerLine, uSrcBPP, enmFmt); 374 } 375 376 /** 377 * Simple blitting function for raw image data with alpha channel. 378 * 379 * @returns VBox status code. 380 * @param pFrame Destination frame. 381 * @param uDstX X destination (in pixel) within destination frame. 382 * @param uDstY Y destination (in pixel) within destination frame. 383 * @param pu8Src Source data to blit. Must be in the same pixel format as \a pFrame. 384 * @param cbSrc Size (in bytes) of \a pu8Src. 385 * @param uSrcX X start (in pixel) within source data. 386 * @param uSrcY Y start (in pixel) within source data. 387 * @param uSrcWidth Width (in pixel) to blit from source data. 388 * @param uSrcHeight Height (in pixel) to blit from data. 389 * @param uSrcBytesPerLine Bytes per line in source data. 390 * @param uSrcBPP BPP of source data. Must match \a pFrame. 391 * @param enmFmt Pixel format of source data. Must match \a pFrame. 392 */ 393 int RecordingVideoFrameBlitRawAlpha(PRECORDINGVIDEOFRAME pFrame, uint32_t uDstX, uint32_t uDstY, 394 const uint8_t *pu8Src, size_t cbSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight, 395 uint32_t uSrcBytesPerLine, uint8_t uSrcBPP, RECORDINGPIXELFMT enmFmt) 396 { 397 return recordingVideoFrameBlitRawAlpha(pFrame, uDstX, uDstY, 398 pu8Src, cbSrc, uSrcX, uSrcY, uSrcWidth, uSrcHeight, uSrcBytesPerLine, uSrcBPP, enmFmt); 399 } 400 401 /** 402 * Simple blitting function for video frames. 403 * 404 * @returns VBox status code. 405 * @param pDstFrame Destination frame. 406 * @param uDstX X destination (in pixel) within destination frame. 407 * @param uDstY Y destination (in pixel) within destination frame. 408 * @param pSrcFrame Source frame. 409 * @param uSrcX X start (in pixel) within source frame. 410 * @param uSrcY Y start (in pixel) within source frame. 411 * @param uSrcWidth Width (in pixel) to blit from source frame. 412 * @param uSrcHeight Height (in pixel) to blit from frame. 413 * 414 * @note Does NOT check for limits, so use with care! 415 */ 416 int RecordingVideoFrameBlitFrame(PRECORDINGVIDEOFRAME pDstFrame, uint32_t uDstX, uint32_t uDstY, 417 PRECORDINGVIDEOFRAME pSrcFrame, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight) 418 { 419 return recordingVideoFrameBlitRaw(/* Dest */ 420 pDstFrame->pau8Buf, pDstFrame->cbBuf, uDstX, uDstY, 421 pDstFrame->Info.uBytesPerLine, pDstFrame->Info.uBPP, pDstFrame->Info.enmPixelFmt, 422 /* Source */ 423 pSrcFrame->pau8Buf, pSrcFrame->cbBuf, uSrcX, uSrcY, uSrcWidth, uSrcHeight, 424 pSrcFrame->Info.uBytesPerLine, pSrcFrame->Info.uBPP, pSrcFrame->Info.enmPixelFmt); 425 } 426 427 #ifdef VBOX_WITH_AUDIO_RECORDING 428 /** 67 429 * Destroys a recording audio frame. 68 430 * 69 431 * @param pFrame Pointer to audio frame to destroy. 70 432 */ 71 static void recordingAudioFrameDestroy(PRECORDINGAUDIOFRAME pFrame) 72 { 433 DECLINLINE(void) recordingAudioFrameDestroy(PRECORDINGAUDIOFRAME pFrame) 434 { 435 if (!pFrame) 436 return; 437 73 438 if (pFrame->pvBuf) 74 439 { 75 440 Assert(pFrame->cbBuf); 76 441 RTMemFree(pFrame->pvBuf); 442 pFrame->pvBuf = NULL; 77 443 pFrame->cbBuf = 0; 78 444 } … … 94 460 pFrame = NULL; 95 461 } 96 97 462 #endif /* VBOX_WITH_AUDIO_RECORDING */ 98 463 99 464 /** 100 * Destroys a recording video frame.101 *102 * @param pFrame Pointer to video frame to destroy.103 */104 void RecordingVideoFrameDestroy(PRECORDINGVIDEOFRAME pFrame)105 {106 if (pFrame->pu8RGBBuf)107 {108 Assert(pFrame->cbRGBBuf);109 RTMemFree(pFrame->pu8RGBBuf);110 pFrame->cbRGBBuf = 0;111 }112 }113 114 /**115 * Frees a recording video frame.116 *117 * @param pFrame Pointer to video frame to free. The pointer will be invalid after return.118 */119 void RecordingVideoFrameFree(PRECORDINGVIDEOFRAME pFrame)120 {121 if (!pFrame)122 return;123 124 RecordingVideoFrameDestroy(pFrame);125 126 RTMemFree(pFrame);127 }128 129 /**130 465 * Frees a recording frame. 131 466 * 132 * @param pFrame Pointer to recording frame to free. The pointer will be invalid after return. 467 * @param pFrame Pointer to recording frame to free. 468 * The pointer will be invalid after return. 133 469 */ 134 470 void RecordingFrameFree(PRECORDINGFRAME pFrame) … … 141 477 #ifdef VBOX_WITH_AUDIO_RECORDING 142 478 case RECORDINGFRAME_TYPE_AUDIO: 143 recordingAudioFrameDestroy(&pFrame-> Audio);479 recordingAudioFrameDestroy(&pFrame->u.Audio); 144 480 break; 145 481 #endif 146 482 case RECORDINGFRAME_TYPE_VIDEO: 147 RecordingVideoFrameDestroy(&pFrame-> Video);483 RecordingVideoFrameDestroy(&pFrame->u.Video); 148 484 break; 149 485 486 case RECORDINGFRAME_TYPE_CURSOR_SHAPE: 487 RecordingVideoFrameDestroy(&pFrame->u.CursorShape); 488 break; 489 150 490 default: 151 AssertFailed();491 /* Nothing to do here. */ 152 492 break; 153 493 } -
trunk/src/VBox/Main/src-client/RecordingStream.cpp
r104752 r105006 259 259 /** 260 260 * Processes a recording stream. 261 * 261 262 * This function takes care of the actual encoding and writing of a certain stream. 262 263 * As this can be very CPU intensive, this function usually is called from a separate thread. 263 264 * 264 265 * @returns VBox status code. 265 * @param mapBlocksCommon Map of common block to process for this stream. 266 * @param streamBlocks Block set of stream to process. 267 * @param commonBlocks Block set of common blocks to process for this stream. 266 268 * 267 269 * @note Runs in recording thread. 268 270 */ 269 int RecordingStream:: Process(RecordingBlockMap &mapBlocksCommon)271 int RecordingStream::process(const RecordingBlockSet &streamBlocks, RecordingBlockMap &commonBlocks) 270 272 { 271 273 LogFlowFuncEnter(); … … 281 283 int vrc = VINF_SUCCESS; 282 284 283 RecordingBlockMap:: iterator itStreamBlocks = m_Blocks.Map.begin();284 while (itStreamBlock s != m_Blocks.Map.end())285 { 286 uint64_t const msTimestamp = itStreamBlock s->first;287 RecordingBlocks *pBlocks = itStreamBlock s->second;285 RecordingBlockMap::const_iterator itStreamBlock = streamBlocks.Map.begin(); 286 while (itStreamBlock != streamBlocks.Map.end()) 287 { 288 uint64_t const msTimestamp = itStreamBlock->first; RT_NOREF(msTimestamp); 289 RecordingBlocks *pBlocks = itStreamBlock->second; 288 290 289 291 AssertPtr(pBlocks); 290 292 291 while (!pBlocks->List.empty()) 293 RecordingBlockList::const_iterator itBlockInList = pBlocks->List.cbegin(); 294 while (itBlockInList != pBlocks->List.cend()) 292 295 { 293 RecordingBlock *pBlock = pBlocks->List.front(); 294 AssertPtr(pBlock); 295 296 switch (pBlock->enmType) 297 { 298 case RECORDINGBLOCKTYPE_VIDEO: 299 { 300 RECORDINGFRAME Frame; 301 Frame.VideoPtr = (PRECORDINGVIDEOFRAME)pBlock->pvData; 302 Frame.msTimestamp = msTimestamp; 303 304 int vrc2 = recordingCodecEncode(&m_CodecVideo, &Frame, NULL, NULL); 296 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)(*itBlockInList)->pvData; 297 AssertPtr(pFrame); 298 Assert(pFrame->msTimestamp == msTimestamp); 299 300 switch (pFrame->enmType) 301 { 302 case RECORDINGFRAME_TYPE_VIDEO: 303 RT_FALL_THROUGH(); 304 case RECORDINGFRAME_TYPE_CURSOR_SHAPE: 305 RT_FALL_THROUGH(); 306 case RECORDINGFRAME_TYPE_CURSOR_POS: 307 { 308 int vrc2 = recordingCodecEncodeFrame(&m_CodecVideo, pFrame, pFrame->msTimestamp, m_pCtx /* pvUser */); 305 309 AssertRC(vrc2); 306 310 if (RT_SUCCESS(vrc)) 307 311 vrc = vrc2; 308 309 312 break; 310 313 } 311 314 315 case RECORDINGFRAME_TYPE_SCREEN_CHANGE: 316 { 317 /* ignore rc */ recordingCodecScreenChange(&m_CodecVideo, &pFrame->u.ScreenInfo); 318 break; 319 } 320 312 321 default: 313 /* Note: Audio data already is encoded. */314 322 break; 315 323 } 316 324 317 pBlocks->List.pop_front(); 318 delete pBlock; 325 ++itBlockInList; 319 326 } 320 327 321 Assert(pBlocks->List.empty()); 322 delete pBlocks; 323 324 m_Blocks.Map.erase(itStreamBlocks); 325 itStreamBlocks = m_Blocks.Map.begin(); 328 ++itStreamBlock; 326 329 } 327 330 … … 332 335 /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be 333 336 * written to the screen's assigned recording stream. */ 334 RecordingBlockMap:: iterator itCommonBlocks = mapBlocksCommon.begin();335 while (it CommonBlocks != mapBlocksCommon.end())337 RecordingBlockMap::const_iterator itBlockMap = commonBlocks.begin(); 338 while (itBlockMap != commonBlocks.end()) 336 339 { 337 RecordingBlockList::iterator itBlock = itCommonBlocks->second->List.begin(); 338 while (itBlock != itCommonBlocks->second->List.end()) 339 { 340 RecordingBlock *pBlockCommon = (RecordingBlock *)(*itBlock); 341 switch (pBlockCommon->enmType) 342 { 343 case RECORDINGBLOCKTYPE_AUDIO: 344 { 345 PRECORDINGAUDIOFRAME pAudioFrame = (PRECORDINGAUDIOFRAME)pBlockCommon->pvData; 346 AssertPtr(pAudioFrame); 347 AssertPtr(pAudioFrame->pvBuf); 348 Assert(pAudioFrame->cbBuf); 349 350 AssertPtr(this->File.m_pWEBM); 351 int vrc2 = this->File.m_pWEBM->WriteBlock(m_uTrackAudio, pAudioFrame->pvBuf, pAudioFrame->cbBuf, pBlockCommon->msTimestamp, pBlockCommon->uFlags); 352 AssertRC(vrc2); 353 if (RT_SUCCESS(vrc)) 354 vrc = vrc2; 355 break; 356 } 357 358 default: 359 AssertFailed(); 360 break; 361 } 362 363 Assert(pBlockCommon->cRefs); 364 pBlockCommon->cRefs--; 365 if (pBlockCommon->cRefs == 0) 366 { 367 itCommonBlocks->second->List.erase(itBlock); 368 delete pBlockCommon; 369 itBlock = itCommonBlocks->second->List.begin(); 340 RecordingBlockList &blockList = itBlockMap->second->List; 341 342 RecordingBlockList::iterator itBlockList = blockList.begin(); 343 while (itBlockList != blockList.end()) 344 { 345 RecordingBlock *pBlock = (RecordingBlock *)(*itBlockList); 346 347 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)pBlock->pvData; 348 Assert(pFrame->enmType == RECORDINGFRAME_TYPE_AUDIO); 349 PRECORDINGAUDIOFRAME pAudioFrame = &pFrame->u.Audio; 350 351 int vrc2 = this->File.m_pWEBM->WriteBlock(m_uTrackAudio, pAudioFrame->pvBuf, pAudioFrame->cbBuf, pBlock->msTimestamp, pBlock->uFlags); 352 if (RT_SUCCESS(vrc)) 353 vrc = vrc2; 354 355 Log3Func(("RECORDINGFRAME_TYPE_AUDIO: %zu bytes -> %Rrc\n", pAudioFrame->cbBuf, vrc2)); 356 357 Assert(pBlock->cRefs); 358 pBlock->cRefs--; 359 if (pBlock->cRefs == 0) 360 { 361 blockList.erase(itBlockList); 362 delete pBlock; 363 itBlockList = blockList.begin(); 370 364 } 371 365 else 372 ++itBlock ;373 } 374 375 /* If no entries are left over in the block map, remove it altogether. */376 if ( itCommonBlocks->second->List.empty())377 { 378 delete it CommonBlocks->second;379 mapBlocksCommon.erase(itCommonBlocks);380 it CommonBlocks = mapBlocksCommon.begin();366 ++itBlockList; 367 } 368 369 /* If no entries are left over in the block list, remove it altogether. */ 370 if (blockList.empty()) 371 { 372 delete itBlockMap->second; 373 commonBlocks.erase(itBlockMap); 374 itBlockMap = commonBlocks.begin(); 381 375 } 382 376 else 383 ++itCommonBlocks; 384 385 LogFunc(("Common blocks: %zu\n", mapBlocksCommon.size())); 377 ++itBlockMap; 386 378 } 387 379 } 388 380 #else 389 RT_NOREF( mapBlocksCommon);381 RT_NOREF(commonBlocks); 390 382 #endif /* VBOX_WITH_AUDIO_RECORDING */ 391 383 … … 393 385 394 386 LogFlowFuncLeaveRC(vrc); 387 return vrc; 388 } 389 390 /** 391 * The stream's main routine called from the encoding thread. 392 * 393 * @returns VBox status code. 394 * @param rcWait Result of the encoding thread's wait operation. 395 * Can be used for figuring out if the encoder has to perform some 396 * worked based on that result. 397 * @param commonBlocks Common blocks multiplexed to all recording streams. 398 * 399 * @note Runs in encoding thread. 400 */ 401 int RecordingStream::ThreadMain(int rcWait, RecordingBlockMap &commonBlocks) 402 { 403 Log3Func(("rcWait=%Rrc\n", rcWait)); 404 405 /* No new data arrived within time? Feed the encoder with the last frame we built. 406 * 407 * This is necessary in order to render a video which has a consistent time line, 408 * as we only encode data when something has changed ("dirty areas"). */ 409 if ( rcWait == VERR_TIMEOUT 410 && m_ScreenSettings.isFeatureEnabled(RecordingFeature_Video)) 411 { 412 return recordingCodecEncodeCurrent(&m_CodecVideo, m_pCtx->GetCurrentPTS()); 413 } 414 415 int vrc = process(m_Blocks, commonBlocks); 416 417 /* 418 * Housekeeping. 419 * 420 * Here we delete all processed stream blocks of this stream. 421 * The common blocks will be deleted by the recording context (which owns those). 422 */ 423 lock(); 424 425 RecordingBlockMap::iterator itStreamBlocks = m_Blocks.Map.begin(); 426 while (itStreamBlocks != m_Blocks.Map.end()) 427 { 428 RecordingBlocks *pBlocks = itStreamBlocks->second; 429 AssertPtr(pBlocks); 430 pBlocks->Clear(); 431 Assert(pBlocks->List.empty()); 432 delete pBlocks; 433 434 m_Blocks.Map.erase(itStreamBlocks); 435 itStreamBlocks = m_Blocks.Map.begin(); 436 } 437 Assert(m_Blocks.Map.empty()); 438 439 unlock(); 440 441 return vrc; 442 } 443 444 /** 445 * Adds a recording frame to be fed to the encoder. 446 * 447 * @returns VBox status code. 448 * @param pFrame Recording frame to add. 449 * Ownership of the frame will be transferred to the encoder on success then. 450 * Must be free'd by the caller on failure. 451 * @param msTimestamp Timestamp (PTS, in ms). 452 * 453 * @note Caller needs to take the stream's lock. 454 */ 455 int RecordingStream::addFrame(PRECORDINGFRAME pFrame, uint64_t msTimestamp) 456 { 457 int vrc; 458 459 Assert(pFrame->msTimestamp == msTimestamp); /* Sanity. */ 460 461 try 462 { 463 RecordingBlock *pBlock = new RecordingBlock(); 464 465 pBlock->pvData = pFrame; 466 pBlock->cbData = sizeof(RECORDINGFRAME); 467 468 try 469 { 470 RecordingBlocks *pRecordingBlocks; 471 RecordingBlockMap::const_iterator it = m_Blocks.Map.find(msTimestamp); 472 if (it == m_Blocks.Map.end()) 473 { 474 pRecordingBlocks = new RecordingBlocks(); 475 pRecordingBlocks->List.push_back(pBlock); 476 m_Blocks.Map.insert(std::make_pair(msTimestamp, pRecordingBlocks)); 477 } 478 else 479 { 480 pRecordingBlocks = it->second; 481 pRecordingBlocks->List.push_back(pBlock); 482 } 483 484 vrc = VINF_SUCCESS; 485 } 486 catch (const std::exception &) 487 { 488 delete pBlock; 489 vrc = VERR_NO_MEMORY; 490 } 491 } 492 catch (const std::exception &) 493 { 494 vrc = VERR_NO_MEMORY; 495 } 496 395 497 return vrc; 396 498 } … … 407 509 { 408 510 AssertPtrReturn(m_pCtx, VERR_WRONG_ORDER); 409 AssertReturn(NeedsUpdate(msTimestamp), VINF_RECORDING_THROTTLED); /* We ASSUME that the caller checked that first. */410 411 Log3Func(("cbData=%zu, msTimestamp=%RU64\n", cbData, msTimestamp));412 511 413 512 /* As audio data is common across all streams, re-route this to the recording context, where … … 417 516 418 517 /** 518 * Sends a cursor position change to the recording stream. 519 * 520 * @returns VBox status code. 521 * @param idCursor Cursor ID. Currently unused and always set to 0. 522 * @param pPos Cursor information to send. 523 * @param msTimestamp Timestamp (PTS, in ms). 524 */ 525 int RecordingStream::SendCursorPos(uint8_t idCursor, PRECORDINGPOS pPos, uint64_t msTimestamp) 526 { 527 RT_NOREF(idCursor); 528 AssertPtrReturn(pPos, VERR_INVALID_POINTER); 529 530 lock(); 531 532 int vrc = iterateInternal(msTimestamp); 533 if (vrc != VINF_SUCCESS) /* Can return VINF_RECORDING_LIMIT_REACHED. */ 534 { 535 unlock(); 536 return vrc; 537 } 538 539 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME)); 540 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 541 pFrame->enmType = RECORDINGFRAME_TYPE_CURSOR_POS; 542 pFrame->msTimestamp = msTimestamp; 543 544 pFrame->u.Cursor.Pos = *pPos; 545 546 vrc = addFrame(pFrame, msTimestamp); 547 548 unlock(); 549 550 return vrc; 551 } 552 553 /** 554 * Sends a cursor shape change to the recording stream. 555 * 556 * @returns VBox status code. 557 * @param idCursor Cursor ID. Currently unused and always set to 0. 558 * @param pShape Cursor shape to send. 559 * @param msTimestamp Timestamp (PTS, in ms). 560 * 561 * @note Keep it as simple as possible, as this function might run on EMT. 562 * @thread EMT 563 */ 564 int RecordingStream::SendCursorShape(uint8_t idCursor, PRECORDINGVIDEOFRAME pShape, uint64_t msTimestamp) 565 { 566 RT_NOREF(idCursor); 567 AssertPtrReturn(pShape, VERR_INVALID_POINTER); 568 AssertPtrReturn(m_pCtx, VERR_WRONG_ORDER); 569 570 lock(); 571 572 int vrc = iterateInternal(msTimestamp); 573 if (vrc != VINF_SUCCESS) /* Can return VINF_RECORDING_LIMIT_REACHED. */ 574 { 575 unlock(); 576 return vrc; 577 } 578 579 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME)); 580 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 581 582 pFrame->u.Video = *pShape; 583 /* Make a deep copy of the pixel data. */ 584 pFrame->u.Video.pau8Buf = (uint8_t *)RTMemDup(pShape->pau8Buf, pShape->cbBuf); 585 AssertPtrReturnStmt(pFrame->u.Video.pau8Buf, RTMemFree(pFrame), VERR_NO_MEMORY); 586 pFrame->u.Video.cbBuf = pShape->cbBuf; 587 588 pFrame->enmType = RECORDINGFRAME_TYPE_CURSOR_SHAPE; 589 pFrame->msTimestamp = msTimestamp; 590 591 vrc = addFrame(pFrame, msTimestamp); 592 593 if (RT_FAILURE(vrc)) 594 { 595 RecordingVideoFrameDestroy(&pFrame->u.Video); 596 RecordingFrameFree(pFrame); 597 } 598 599 unlock(); 600 601 LogFlowFuncLeaveRC(vrc); 602 return vrc; 603 } 604 605 /** 419 606 * Sends a raw (e.g. not yet encoded) video frame to the recording stream. 420 607 * … … 422 609 * @retval VINF_RECORDING_LIMIT_REACHED if the stream's recording limit has been reached. 423 610 * @retval VINF_RECORDING_THROTTLED if the frame is too early for the current FPS setting. 424 * @param x Upper left (X) coordinate where the video frame starts. 425 * @param y Upper left (Y) coordinate where the video frame starts. 426 * @param uPixelFormat Pixel format of the video frame. 427 * @param uBPP Bits per pixel (BPP) of the video frame. 428 * @param uBytesPerLine Bytes per line of the video frame. 429 * @param uSrcWidth Width (in pixels) of the video frame. 430 * @param uSrcHeight Height (in pixels) of the video frame. 431 * @param puSrcData Actual pixel data of the video frame. 611 * @param pVideoFrame Video frame to send. 432 612 * @param msTimestamp Timestamp (PTS, in ms). 433 */ 434 int RecordingStream::SendVideoFrame(uint32_t x, uint32_t y, uint32_t uPixelFormat, uint32_t uBPP, uint32_t uBytesPerLine, 435 uint32_t uSrcWidth, uint32_t uSrcHeight, uint8_t *puSrcData, uint64_t msTimestamp) 436 { 613 * 614 * @note Keep it as simple as possible, as this function might run on EMT. 615 * @thread EMT 616 */ 617 int RecordingStream::SendVideoFrame(PRECORDINGVIDEOFRAME pVideoFrame, uint64_t msTimestamp) 618 { 619 AssertPtrReturn(pVideoFrame, VERR_INVALID_POINTER); 437 620 AssertPtrReturn(m_pCtx, VERR_WRONG_ORDER); 438 621 439 if (RT_UNLIKELY(!NeedsUpdate(msTimestamp)))440 return VINF_RECORDING_THROTTLED;441 442 622 lock(); 443 444 Log3Func(("[%RU32 %RU32 %RU32 %RU32] msTimestamp=%RU64\n", x , y, uSrcWidth, uSrcHeight, msTimestamp));445 446 PRECORDINGVIDEOFRAME pFrame = NULL;447 623 448 624 int vrc = iterateInternal(msTimestamp); … … 453 629 } 454 630 455 do 456 { 457 int xDiff = ((int)m_ScreenSettings.Video.ulWidth - (int)uSrcWidth) / 2; 458 uint32_t w = uSrcWidth; 459 if ((int)w + xDiff + (int)x <= 0) /* Nothing visible. */ 460 { 461 vrc = VERR_INVALID_PARAMETER; 462 break; 463 } 464 465 uint32_t destX; 466 if ((int)x < -xDiff) 467 { 468 w += xDiff + x; 469 x = -xDiff; 470 destX = 0; 471 } 472 else 473 destX = x + xDiff; 474 475 uint32_t h = uSrcHeight; 476 int yDiff = ((int)m_ScreenSettings.Video.ulHeight - (int)uSrcHeight) / 2; 477 if ((int)h + yDiff + (int)y <= 0) /* Nothing visible. */ 478 { 479 vrc = VERR_INVALID_PARAMETER; 480 break; 481 } 482 483 uint32_t destY; 484 if ((int)y < -yDiff) 485 { 486 h += yDiff + (int)y; 487 y = -yDiff; 488 destY = 0; 489 } 490 else 491 destY = y + yDiff; 492 493 if ( destX > m_ScreenSettings.Video.ulWidth 494 || destY > m_ScreenSettings.Video.ulHeight) 495 { 496 vrc = VERR_INVALID_PARAMETER; /* Nothing visible. */ 497 break; 498 } 499 500 if (destX + w > m_ScreenSettings.Video.ulWidth) 501 w = m_ScreenSettings.Video.ulWidth - destX; 502 503 if (destY + h > m_ScreenSettings.Video.ulHeight) 504 h = m_ScreenSettings.Video.ulHeight - destY; 505 506 pFrame = (PRECORDINGVIDEOFRAME)RTMemAllocZ(sizeof(RECORDINGVIDEOFRAME)); 507 AssertBreakStmt(pFrame, vrc = VERR_NO_MEMORY); 508 509 /* Calculate bytes per pixel and set pixel format. */ 510 const unsigned uBytesPerPixel = uBPP / 8; 511 if (uPixelFormat == BitmapFormat_BGR) 512 { 513 switch (uBPP) 514 { 515 case 32: 516 pFrame->enmPixelFmt = RECORDINGPIXELFMT_RGB32; 517 break; 518 case 24: 519 pFrame->enmPixelFmt = RECORDINGPIXELFMT_RGB24; 520 break; 521 case 16: 522 pFrame->enmPixelFmt = RECORDINGPIXELFMT_RGB565; 523 break; 524 default: 525 AssertMsgFailedBreakStmt(("Unknown color depth (%RU32)\n", uBPP), vrc = VERR_NOT_SUPPORTED); 526 break; 527 } 528 } 529 else 530 AssertMsgFailedBreakStmt(("Unknown pixel format (%RU32)\n", uPixelFormat), vrc = VERR_NOT_SUPPORTED); 531 532 const size_t cbRGBBuf = m_ScreenSettings.Video.ulWidth 533 * m_ScreenSettings.Video.ulHeight 534 * uBytesPerPixel; 535 AssertBreakStmt(cbRGBBuf, vrc = VERR_INVALID_PARAMETER); 536 537 pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf); 538 AssertBreakStmt(pFrame->pu8RGBBuf, vrc = VERR_NO_MEMORY); 539 pFrame->cbRGBBuf = cbRGBBuf; 540 pFrame->uWidth = uSrcWidth; 541 pFrame->uHeight = uSrcHeight; 542 543 /* If the current video frame is smaller than video resolution we're going to encode, 544 * clear the frame beforehand to prevent artifacts. */ 545 if ( uSrcWidth < m_ScreenSettings.Video.ulWidth 546 || uSrcHeight < m_ScreenSettings.Video.ulHeight) 547 { 548 RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf); 549 } 550 551 /* Calculate start offset in source and destination buffers. */ 552 uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel; 553 uint32_t offDst = (destY * m_ScreenSettings.Video.ulWidth + destX) * uBytesPerPixel; 554 555 #ifdef VBOX_RECORDING_DUMP 556 BMPFILEHDR fileHdr; 557 RT_ZERO(fileHdr); 558 559 BMPWIN3XINFOHDR coreHdr; 560 RT_ZERO(coreHdr); 561 562 fileHdr.uType = BMP_HDR_MAGIC; 563 fileHdr.cbFileSize = (uint32_t)(sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR) + (w * h * uBytesPerPixel)); 564 fileHdr.offBits = (uint32_t)(sizeof(BMPFILEHDR) + sizeof(BMPWIN3XINFOHDR)); 565 566 coreHdr.cbSize = sizeof(BMPWIN3XINFOHDR); 567 coreHdr.uWidth = w; 568 coreHdr.uHeight = h; 569 coreHdr.cPlanes = 1; 570 coreHdr.cBits = uBPP; 571 coreHdr.uXPelsPerMeter = 5000; 572 coreHdr.uYPelsPerMeter = 5000; 573 574 char szFileName[RTPATH_MAX]; 575 RTStrPrintf2(szFileName, sizeof(szFileName), "/tmp/VideoRecFrame-%RU32.bmp", m_uScreenID); 576 577 RTFILE fh; 578 int vrc2 = RTFileOpen(&fh, szFileName, 579 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE); 580 if (RT_SUCCESS(vrc2)) 581 { 582 RTFileWrite(fh, &fileHdr, sizeof(fileHdr), NULL); 583 RTFileWrite(fh, &coreHdr, sizeof(coreHdr), NULL); 584 } 585 #endif 586 Assert(pFrame->cbRGBBuf >= w * h * uBytesPerPixel); 587 588 /* Do the copy. */ 589 for (unsigned int i = 0; i < h; i++) 590 { 591 /* Overflow check. */ 592 Assert(offSrc + w * uBytesPerPixel <= uSrcHeight * uBytesPerLine); 593 Assert(offDst + w * uBytesPerPixel <= m_ScreenSettings.Video.ulHeight * m_ScreenSettings.Video.ulWidth * uBytesPerPixel); 594 595 memcpy(pFrame->pu8RGBBuf + offDst, puSrcData + offSrc, w * uBytesPerPixel); 596 597 #ifdef VBOX_RECORDING_DUMP 598 if (RT_SUCCESS(rc2)) 599 RTFileWrite(fh, pFrame->pu8RGBBuf + offDst, w * uBytesPerPixel, NULL); 600 #endif 601 offSrc += uBytesPerLine; 602 offDst += m_ScreenSettings.Video.ulWidth * uBytesPerPixel; 603 } 604 605 #ifdef VBOX_RECORDING_DUMP 606 if (RT_SUCCESS(vrc2)) 607 RTFileClose(fh); 608 #endif 609 610 } while (0); 611 612 if (vrc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */ 613 { 614 RecordingBlock *pBlock = new RecordingBlock(); 615 if (pBlock) 616 { 617 AssertPtr(pFrame); 618 619 pBlock->enmType = RECORDINGBLOCKTYPE_VIDEO; 620 pBlock->pvData = pFrame; 621 pBlock->cbData = sizeof(RECORDINGVIDEOFRAME) + pFrame->cbRGBBuf; 622 623 try 624 { 625 RecordingBlocks *pRecordingBlocks = new RecordingBlocks(); 626 pRecordingBlocks->List.push_back(pBlock); 627 628 Assert(m_Blocks.Map.find(msTimestamp) == m_Blocks.Map.end()); 629 m_Blocks.Map.insert(std::make_pair(msTimestamp, pRecordingBlocks)); 630 } 631 catch (const std::exception &ex) 632 { 633 RT_NOREF(ex); 634 635 delete pBlock; 636 vrc = VERR_NO_MEMORY; 637 } 638 } 639 else 640 vrc = VERR_NO_MEMORY; 641 } 631 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME)); 632 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 633 634 pFrame->u.Video = *pVideoFrame; 635 /* Make a deep copy of the pixel data. */ 636 pFrame->u.Video.pau8Buf = (uint8_t *)RTMemDup(pVideoFrame->pau8Buf, pVideoFrame->cbBuf); 637 AssertPtrReturnStmt(pFrame->u.Video.pau8Buf, RTMemFree(pFrame), VERR_NO_MEMORY); 638 pFrame->u.Video.cbBuf = pVideoFrame->cbBuf; 639 640 pFrame->enmType = RECORDINGFRAME_TYPE_VIDEO; 641 pFrame->msTimestamp = msTimestamp; 642 643 vrc = addFrame(pFrame, msTimestamp); 642 644 643 645 if (RT_FAILURE(vrc)) 644 RecordingVideoFrameFree(pFrame); 646 { 647 RecordingVideoFrameDestroy(&pFrame->u.Video); 648 RecordingFrameFree(pFrame); 649 } 650 651 unlock(); 652 653 LogFlowFuncLeaveRC(vrc); 654 return vrc; 655 } 656 657 /** 658 * Sends a screen size change to a recording stream. 659 * 660 * @returns VBox status code. 661 * @param pInfo Recording screen info to use. 662 * @param msTimestamp Timestamp (PTS, in ms). 663 * @param fForce Set to \c true to force a change, otherwise to \c false. 664 */ 665 int RecordingStream::SendScreenChange(PRECORDINGSURFACEINFO pInfo, uint64_t msTimestamp, bool fForce /* = false */) 666 { 667 AssertPtrReturn(pInfo, VERR_INVALID_POINTER); 668 669 if ( !pInfo->uWidth 670 || !pInfo->uHeight) 671 return VINF_SUCCESS; 672 673 RT_NOREF(fForce); 674 675 LogRel(("Recording: Size of screen #%RU32 changed to %RU32x%RU32 (%RU8 BPP)\n", 676 m_uScreenID, pInfo->uWidth, pInfo->uHeight, pInfo->uBPP)); 677 678 lock(); 679 680 PRECORDINGFRAME pFrame = (PRECORDINGFRAME)RTMemAlloc(sizeof(RECORDINGFRAME)); 681 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 682 pFrame->enmType = RECORDINGFRAME_TYPE_SCREEN_CHANGE; 683 pFrame->msTimestamp = msTimestamp; 684 685 pFrame->u.ScreenInfo = *pInfo; 686 687 int vrc = addFrame(pFrame, msTimestamp); 645 688 646 689 unlock(); … … 795 838 m_enmState = RECORDINGSTREAMSTATE_INITIALIZED; 796 839 m_fEnabled = true; 797 m_tsStartMs = RTTime ProgramMilliTS();840 m_tsStartMs = RTTimeMilliTS(); 798 841 799 842 return VINF_SUCCESS; … … 817 860 { 818 861 int vrc = VINF_SUCCESS; 862 863 /* ignore rc */ recordingCodecFinalize(&m_CodecVideo); 819 864 820 865 switch (m_ScreenSettings.enmDest) … … 918 963 919 964 if (m_ScreenSettings.isFeatureEnabled(RecordingFeature_Video)) 920 { 921 vrc = recordingCodecFinalize(&m_CodecVideo); 922 if (RT_SUCCESS(vrc)) 923 vrc = recordingCodecDestroy(&m_CodecVideo); 924 } 965 vrc = recordingCodecDestroy(&m_CodecVideo); 925 966 926 967 if (RT_SUCCESS(vrc)) … … 953 994 954 995 WebMWriter::WebMBlockFlags blockFlags = VBOX_WEBM_BLOCK_FLAG_NONE; 955 if (RT_LIKELY(uFlags != RECORDINGCODEC_ENC_F_NONE))996 if (RT_LIKELY(uFlags == RECORDINGCODEC_ENC_F_NONE)) 956 997 { 957 998 /* All set. */ … … 966 1007 967 1008 return this->File.m_pWEBM->WriteBlock( pCodec->Parms.enmType == RECORDINGCODECTYPE_AUDIO 968 ? m_uTrackAudio : m_uTrackVideo,969 pvData, cbData, msAbsPTS, blockFlags);1009 ? m_uTrackAudio : m_uTrackVideo, 1010 pvData, cbData, msAbsPTS, blockFlags); 970 1011 } 971 1012 … … 1012 1053 Callbacks.pfnWriteData = RecordingStream::codecWriteDataCallback; 1013 1054 1014 int vrc = recordingCodecCreateVideo(pCodec, screenSettings.Video.enmCodec); 1055 RECORDINGSURFACEINFO ScreenInfo; 1056 ScreenInfo.uWidth = screenSettings.Video.ulWidth; 1057 ScreenInfo.uHeight = screenSettings.Video.ulHeight; 1058 ScreenInfo.uBPP = 32; /* We always start with 32 bit. */ 1059 1060 int vrc = SendScreenChange(&ScreenInfo, true /* fForce */); 1015 1061 if (RT_SUCCESS(vrc)) 1016 vrc = recordingCodecInit(pCodec, &Callbacks, screenSettings); 1062 { 1063 vrc = recordingCodecCreateVideo(pCodec, screenSettings.Video.enmCodec); 1064 if (RT_SUCCESS(vrc)) 1065 vrc = recordingCodecInit(pCodec, &Callbacks, screenSettings); 1066 } 1017 1067 1018 1068 if (RT_FAILURE(vrc)) -
trunk/src/VBox/Main/src-client/RecordingUtils.cpp
r98103 r105006 42 42 #endif 43 43 44 #include <VBox/log.h> 45 44 46 45 47 /** … … 60 62 RT_NOREF(aDstWidth, aDstHeight); 61 63 62 AssertReturn(!(aSrcWidth & 1), false); 63 AssertReturn(!(aSrcHeight & 1), false); 64 65 bool fRc = true; 66 T iter1(aSrcWidth, aSrcHeight, aSrcBuf); 67 T iter2 = iter1; 68 iter2.skip(aSrcWidth); 69 unsigned cPixels = aSrcWidth * aSrcHeight; 70 unsigned offY = 0; 71 unsigned offU = cPixels; 72 unsigned offV = cPixels + cPixels / 4; 73 unsigned const cyHalf = aSrcHeight / 2; 74 unsigned const cxHalf = aSrcWidth / 2; 75 for (unsigned i = 0; i < cyHalf && fRc; ++i) 76 { 77 for (unsigned j = 0; j < cxHalf; ++j) 78 { 79 unsigned red, green, blue; 80 fRc = iter1.getRGB(&red, &green, &blue); 81 AssertReturn(fRc, false); 82 aDstBuf[offY] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; 83 unsigned u = (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; 84 unsigned v = (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; 85 86 fRc = iter1.getRGB(&red, &green, &blue); 87 AssertReturn(fRc, false); 88 aDstBuf[offY + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; 89 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; 90 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; 91 92 fRc = iter2.getRGB(&red, &green, &blue); 93 AssertReturn(fRc, false); 94 aDstBuf[offY + aSrcWidth] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; 95 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; 96 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; 97 98 fRc = iter2.getRGB(&red, &green, &blue); 99 AssertReturn(fRc, false); 100 aDstBuf[offY + aSrcWidth + 1] = ((66 * red + 129 * green + 25 * blue + 128) >> 8) + 16; 101 u += (((-38 * red - 74 * green + 112 * blue + 128) >> 8) + 128) / 4; 102 v += (((112 * red - 94 * green - 18 * blue + 128) >> 8) + 128) / 4; 103 104 aDstBuf[offU] = u; 105 aDstBuf[offV] = v; 106 offY += 2; 107 ++offU; 108 ++offV; 109 } 110 111 iter1.skip(aSrcWidth); 112 iter2.skip(aSrcWidth); 113 offY += aSrcWidth; 64 size_t image_size = aSrcWidth * aSrcHeight; 65 size_t upos = image_size; 66 size_t vpos = upos + upos / 4; 67 size_t i = 0; 68 69 #define CALC_Y(r, g, b) \ 70 ((66 * r + 129 * g + 25 * b) >> 8) + 16 71 #define CALC_U(r, g, b) \ 72 ((-38 * r + -74 * g + 112 * b) >> 8) + 128 73 #define CALC_V(r, g, b) \ 74 ((112 * r + -94 * g + -18 * b) >> 8) + 128 75 76 for (size_t line = 0; line < aSrcHeight; ++line) 77 { 78 if ((line % 2) == 0) 79 { 80 for (size_t x = 0; x < aSrcWidth; x += 2) 81 { 82 uint8_t b = aSrcBuf[4 * i]; 83 uint8_t g = aSrcBuf[4 * i + 1]; 84 uint8_t r = aSrcBuf[4 * i + 2]; 85 86 aDstBuf[i++] = CALC_Y(r, g, b); 87 88 aDstBuf[upos++] = CALC_U(r, g, b); 89 aDstBuf[vpos++] = CALC_V(r, g, b); 90 91 b = aSrcBuf[4 * i]; 92 g = aSrcBuf[4 * i + 1]; 93 r = aSrcBuf[4 * i + 2]; 94 95 aDstBuf[i++] = CALC_Y(r, g, b); 96 } 97 } 98 else 99 { 100 for (size_t x = 0; x < aSrcWidth; x++) 101 { 102 uint8_t b = aSrcBuf[4 * i]; 103 uint8_t g = aSrcBuf[4 * i + 1]; 104 uint8_t r = aSrcBuf[4 * i + 2]; 105 106 aDstBuf[i++] = CALC_Y(r, g, b); 107 } 108 } 114 109 } 115 110 … … 152 147 153 148 /** 154 * Converts a RGB to YUV buffer. 155 * 156 * @returns IPRT status code. 157 * @param enmPixelFormat Pixel format to use for conversion. 149 * Converts an entire RGB BGRA32 buffer to a YUV I420 buffer. 150 * 158 151 * @param paDst Pointer to destination buffer. 159 * @param uDstWidth Width (X, in pixel s) of destination buffer.160 * @param uDstHeight Height (Y, in pixel s) of destination buffer.152 * @param uDstWidth Width (X, in pixel) of destination buffer. 153 * @param uDstHeight Height (Y, in pixel) of destination buffer. 161 154 * @param paSrc Pointer to source buffer. 162 * @param uSrcWidth Width (X, in pixels) of source buffer. 163 * @param uSrcHeight Height (Y, in pixels) of source buffer. 164 */ 165 int RecordingUtilsRGBToYUV(RECORDINGPIXELFMT enmPixelFormat, 166 uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight, 167 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight) 168 { 169 switch (enmPixelFormat) 170 { 171 case RECORDINGPIXELFMT_RGB32: 172 if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGRA32Iter>(paDst, uDstWidth, uDstHeight, 173 paSrc, uSrcWidth, uSrcHeight)) 174 return VERR_INVALID_PARAMETER; 175 break; 176 case RECORDINGPIXELFMT_RGB24: 177 if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGR24Iter>(paDst, uDstWidth, uDstHeight, 178 paSrc, uSrcWidth, uSrcHeight)) 179 return VERR_INVALID_PARAMETER; 180 break; 181 case RECORDINGPIXELFMT_RGB565: 182 if (!recordingUtilsColorConvWriteYUV420p<ColorConvBGR565Iter>(paDst, uDstWidth, uDstHeight, 183 paSrc, uSrcWidth, uSrcHeight)) 184 return VERR_INVALID_PARAMETER; 185 break; 186 default: 187 AssertFailed(); 188 return VERR_NOT_SUPPORTED; 189 } 190 return VINF_SUCCESS; 155 * @param uSrcWidth Width (X, in pixel) of source buffer. 156 * @param uSrcHeight Height (Y, in pixel) of source buffer. 157 */ 158 void RecordingUtilsConvBGRA32ToYUVI420(uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight, 159 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight) 160 { 161 RT_NOREF(uDstWidth, uDstHeight); 162 163 size_t const image_size = uSrcWidth * uSrcHeight; 164 size_t upos = image_size; 165 size_t vpos = upos + upos / 4; 166 size_t i = 0; 167 168 #define CALC_Y(r, g, b) \ 169 ((66 * r + 129 * g + 25 * b) >> 8) + 16 170 #define CALC_U(r, g, b) \ 171 ((-38 * r + -74 * g + 112 * b) >> 8) + 128 172 #define CALC_V(r, g, b) \ 173 ((112 * r + -94 * g + -18 * b) >> 8) + 128 174 175 for (size_t y = 0; y < uSrcHeight; y++) 176 { 177 if ((y % 2) == 0) 178 { 179 for (size_t x = 0; x < uSrcWidth; x += 2) 180 { 181 uint8_t b = paSrc[4 * i]; 182 uint8_t g = paSrc[4 * i + 1]; 183 uint8_t r = paSrc[4 * i + 2]; 184 185 paDst[i++] = CALC_Y(r, g, b); 186 187 paDst[upos++] = CALC_U(r, g, b); 188 paDst[vpos++] = CALC_V(r, g, b); 189 190 b = paSrc[4 * i]; 191 g = paSrc[4 * i + 1]; 192 r = paSrc[4 * i + 2]; 193 194 paDst[i++] = CALC_Y(r, g, b); 195 } 196 } 197 else 198 { 199 for (size_t x = 0; x < uSrcWidth; x++) 200 { 201 uint8_t const b = paSrc[4 * i]; 202 uint8_t const g = paSrc[4 * i + 1]; 203 uint8_t const r = paSrc[4 * i + 2]; 204 205 paDst[i++] = CALC_Y(r, g, b); 206 } 207 } 208 } 209 210 #undef CALC_Y 211 #undef CALC_U 212 #undef CALC_V 213 } 214 215 /** 216 * Converts a part of a RGB BGRA32 buffer to a YUV I420 buffer. 217 * 218 * @param paDst Pointer to destination buffer. 219 * @param uDstX X destination position (in pixel). 220 * @param uDstY Y destination position (in pixel). 221 * @param uDstWidth Width (X, in pixel) of destination buffer. 222 * @param uDstHeight Height (Y, in pixel) of destination buffer. 223 * @param paSrc Pointer to source buffer. 224 * @param uSrcX X source position (in pixel). 225 * @param uSrcY Y source position (in pixel). 226 * @param uSrcWidth Width (X, in pixel) of source buffer. 227 * @param uSrcHeight Height (Y, in pixel) of source buffer. 228 * @param uSrcStride Stride (in bytes) of source buffer. 229 * @param uSrcBPP Bits per pixel of source buffer. 230 */ 231 void RecordingUtilsConvBGRA32ToYUVI420Ex(uint8_t *paDst, uint32_t uDstX, uint32_t uDstY, uint32_t uDstWidth, uint32_t uDstHeight, 232 uint8_t *paSrc, uint32_t uSrcX, uint32_t uSrcY, uint32_t uSrcWidth, uint32_t uSrcHeight, 233 uint32_t uSrcStride, uint8_t uSrcBPP) 234 { 235 Assert(uDstX < uDstWidth); 236 Assert(uDstX + uSrcWidth <= uDstWidth); 237 Assert(uDstY < uDstHeight); 238 Assert(uDstY + uSrcHeight <= uDstHeight); 239 Assert(uSrcBPP % 8 == 0); 240 241 RT_NOREF(uSrcHeight, uDstHeight); 242 243 #define CALC_Y(r, g, b) \ 244 (66 * r + 129 * g + 25 * b) >> 8 245 #define CALC_U(r, g, b) \ 246 ((-38 * r + -74 * g + 112 * b) >> 8) + 128 247 #define CALC_V(r, g, b) \ 248 ((112 * r + -94 * g + -18 * b) >> 8) + 128 249 250 uint32_t uDstXCur = uDstX; 251 252 const unsigned uSrcBytesPerPixel = uSrcBPP / 8; 253 254 size_t offSrc = (uSrcY * uSrcStride) + (uSrcX * uSrcBytesPerPixel); 255 256 for (size_t y = 0; y < uSrcHeight; y++) 257 { 258 for (size_t x = 0; x < uSrcWidth; x++) 259 { 260 size_t const offBGR = offSrc + (x * uSrcBytesPerPixel); 261 262 uint8_t const b = paSrc[offBGR]; 263 uint8_t const g = paSrc[offBGR + 1]; 264 uint8_t const r = paSrc[offBGR + 2]; 265 266 size_t const offY = uDstY * uDstWidth + uDstXCur; 267 size_t const offUV = (uDstY / 2) * (uDstWidth / 2) + (uDstXCur / 2) + uDstWidth * uDstHeight; 268 269 paDst[offY] = CALC_Y(r, g, b); 270 paDst[offUV] = CALC_U(r, g, b); 271 paDst[offUV + uDstWidth * uDstHeight / 4] = CALC_V(r, g, b); 272 273 uDstXCur++; 274 } 275 276 offSrc += uSrcStride; 277 278 uDstXCur = uDstX; 279 uDstY++; 280 } 281 282 #undef CALC_Y 283 #undef CALC_U 284 #undef CALC_V 285 } 286 287 /** 288 * Crops (centers) or centers coordinates of a given source. 289 * 290 * @returns VBox status code. 291 * @retval VWRN_RECORDING_ENCODING_SKIPPED if the source is not visible. 292 * @param pCodecParms Video codec parameters to use for cropping / centering. 293 * @param sx Input / output: X location (in pixel) of the source. 294 * @param sy Input / output: Y location (in pixel) of the source. 295 * @param sw Input / output: Width (in pixel) of the source. 296 * @param sh Input / output: Height (in pixel) of the source. 297 * @param dx Input / output: X location (in pixel) of the destination. 298 * @param dy Input / output: Y location (in pixel) of the destination. 299 * 300 * @note Used when no other scaling algorithm is being selected / available. See testcase. 301 */ 302 int RecordingUtilsCoordsCropCenter(PRECORDINGCODECPARMS pCodecParms, 303 int32_t *sx, int32_t *sy, int32_t *sw, int32_t *sh, int32_t *dx, int32_t *dy) 304 { 305 int vrc = VINF_SUCCESS; 306 307 Log3Func(("Original: sx=%RI32 sy=%RI32 sw=%RI32 sh=%RI32 dx=%RI32 dy=%RI32\n", 308 *sx, *sy, *sw, *sh, *dx, *dy)); 309 310 /* 311 * Do centered cropping or centering. 312 */ 313 314 int32_t const uOriginX = (int32_t)pCodecParms->u.Video.Scaling.u.Crop.m_iOriginX; 315 int32_t const uOriginY = (int32_t)pCodecParms->u.Video.Scaling.u.Crop.m_iOriginY; 316 317 /* Apply cropping / centering values. */ 318 *dx += uOriginX; 319 *dy += uOriginY; 320 321 if (*dx < 0) 322 { 323 *dx = 0; 324 *sx += RT_ABS(uOriginX); 325 *sw -= RT_ABS(uOriginX); 326 } 327 328 if (*dy < 0) 329 { 330 *dy = 0; 331 *sy += RT_ABS(uOriginY); 332 *sh -= RT_ABS(uOriginY); 333 } 334 335 Log3Func(("Crop0: sx=%RI32 sy=%RI32 sw=%RI32 sh=%RI32 dx=%RI32 dy=%RI32\n", 336 *sx, *sy, *sw, *sh, *dx, *dy)); 337 338 if (*sw > (int32_t)pCodecParms->u.Video.uWidth) 339 *sw = (int32_t)pCodecParms->u.Video.uWidth; 340 341 if (*sh > (int32_t)pCodecParms->u.Video.uHeight) 342 *sh = pCodecParms->u.Video.uHeight; 343 344 if (*dx + *sw >= (int32_t)pCodecParms->u.Video.uWidth) 345 *sw = pCodecParms->u.Video.uWidth - *dx; 346 347 if (*dy + *sh >= (int32_t)pCodecParms->u.Video.uHeight) 348 *sh = pCodecParms->u.Video.uHeight - *dy; 349 350 if ( *dx + *sw < 1 351 || *dy + *sh < 1 352 || *dx > (int32_t)pCodecParms->u.Video.uWidth 353 || *dy > (int32_t)pCodecParms->u.Video.uHeight 354 || *sw < 1 355 || *sh < 1) 356 { 357 vrc = VWRN_RECORDING_ENCODING_SKIPPED; /* Not visible, skip encoding. */ 358 } 359 360 Log3Func(("Crop1: sx=%RI32 sy=%RI32 sw=%RI32 sh=%RI32 dx=%RI32 dy=%RI32 -> vrc=%Rrc\n", 361 *sx, *sy, *sw, *sh, *dx, *dy, vrc)); 362 363 return vrc; 191 364 } 192 365 193 366 #ifdef DEBUG 194 367 /** 195 * Dumps a video recording frame to a bitmap (BMP) file, extended version.368 * Dumps image data to a bitmap (BMP) file. 196 369 * 197 370 * @returns VBox status code. 198 * @param pu8RGBBuf Pointer to actual RGB frame data.371 * @param pu8RGBBuf Pointer to actual RGB image data. 199 372 * @param cbRGBBuf Size (in bytes) of \a pu8RGBBuf. 200 373 * @param pszPath Absolute path to dump file to. Must exist. 201 * Specify NULL to use the system's temp directory .202 * Existing f rame files will be overwritten.203 * @param psz Prefx Naming prefix to use. Optional and can be NULL.374 * Specify NULL to use the system's temp directory as output directory. 375 * Existing files will be overwritten. 376 * @param pszWhat Hint of what to dump. Optional and can be NULL. 204 377 * @param uWidth Width (in pixel) to write. 205 378 * @param uHeight Height (in pixel) to write. 379 * @param uBytsPerLine Bytes per line. 206 380 * @param uBPP Bits in pixel. 207 381 */ 208 int RecordingUtilsDbgDump FrameEx(const uint8_t *pu8RGBBuf, size_t cbRGBBuf, const char *pszPath, const char *pszPrefx,209 uint16_t uWidth, uint32_t uHeight, uint8_t uBPP)382 int RecordingUtilsDbgDumpImageData(const uint8_t *pu8RGBBuf, size_t cbRGBBuf, const char *pszPath, const char *pszWhat, 383 uint32_t uWidth, uint32_t uHeight, uint32_t uBytesPerLine, uint8_t uBPP) 210 384 { 211 385 RT_NOREF(cbRGBBuf); … … 228 402 229 403 coreHdr.cbSize = sizeof(BMPWIN3XINFOHDR); 230 coreHdr.uWidth = uWidth 404 coreHdr.uWidth = uWidth; 231 405 coreHdr.uHeight = uHeight; 232 406 coreHdr.cPlanes = 1; … … 237 411 static uint64_t s_iCount = 0; 238 412 413 /* Hardcoded path for now. */ 239 414 char szPath[RTPATH_MAX]; 240 415 if (!pszPath) 241 RTPathTemp(szPath, sizeof(szPath)); 416 { 417 int rc2 = RTPathTemp(szPath, sizeof(szPath)); 418 if (RT_FAILURE(rc2)) 419 return rc2; 420 } 242 421 243 422 char szFileName[RTPATH_MAX]; 244 423 if (RTStrPrintf2(szFileName, sizeof(szFileName), "%s/RecDump-%04RU64-%s-w%RU16h%RU16.bmp", 245 pszPath ? pszPath : szPath, s_iCount, pszPrefx ? pszPrefx : "Frame", uWidth, uHeight) <= 0) 246 { 424 pszPath ? pszPath : szPath, s_iCount, pszWhat ? pszWhat : "Frame", uWidth, uHeight) <= 0) 247 425 return VERR_BUFFER_OVERFLOW; 248 }249 426 250 427 s_iCount++; … … 259 436 260 437 /* Bitmaps (DIBs) are stored upside-down (thanks, OS/2), so work from the bottom up. */ 261 uint32_t offSrc = (uHeight * uWidth * uBytesPerPixel) - uWidth * uBytesPerPixel;438 uint32_t offSrc = cbRGBBuf - uBytesPerLine; 262 439 263 440 /* Do the copy. */ 264 441 for (unsigned int i = 0; i < uHeight; i++) 265 442 { 266 RTFileWrite(fh, pu8RGBBuf + offSrc, u Width * uBytesPerPixel, NULL);267 offSrc -= u Width * uBytesPerPixel;443 RTFileWrite(fh, pu8RGBBuf + offSrc, uBytesPerLine, NULL); 444 offSrc -= uBytesPerLine; 268 445 } 269 446 … … 275 452 276 453 /** 277 * Dumps a video recording frame to a bitmap (BMP) file .454 * Dumps a video recording frame to a bitmap (BMP) file, extended version. 278 455 * 279 456 * @returns VBox status code. 280 457 * @param pFrame Video frame to dump. 281 */ 282 int RecordingUtilsDbgDumpFrame(const PRECORDINGFRAME pFrame) 283 { 284 AssertReturn(pFrame->enmType == RECORDINGFRAME_TYPE_VIDEO, VERR_INVALID_PARAMETER); 285 return RecordingUtilsDbgDumpFrameEx(pFrame->Video.pu8RGBBuf, pFrame->Video.cbRGBBuf, 286 NULL /* Use temp directory */, NULL /* pszPrefix */, 287 pFrame->Video.uWidth, pFrame->Video.uHeight, pFrame->Video.uBPP); 458 * @param pszPath Output directory. 459 * @param pszWhat Hint of what to dump. Optional and can be NULL. 460 */ 461 int RecordingUtilsDbgDumpVideoFrameEx(const PRECORDINGVIDEOFRAME pFrame, const char *pszPath, const char *pszWhat) 462 { 463 return RecordingUtilsDbgDumpImageData(pFrame->pau8Buf, pFrame->cbBuf, 464 pszPath, pszWhat, 465 pFrame->Info.uWidth, pFrame->Info.uHeight, pFrame->Info.uBytesPerLine, pFrame->Info.uBPP); 466 } 467 468 /** 469 * Dumps a video recording frame to a bitmap (BMP) file. 470 * 471 * @returns VBox status code. 472 * @param pFrame Video frame to dump. 473 * @param pszWhat Hint of what to dump. Optional and can be NULL. 474 */ 475 int RecordingUtilsDbgDumpVideoFrame(const PRECORDINGVIDEOFRAME pFrame, const char *pszWhat) 476 { 477 return RecordingUtilsDbgDumpVideoFrameEx(pFrame, NULL /* Use temp directory */, pszWhat); 288 478 } 289 479 #endif /* DEBUG */
Note:
See TracChangeset
for help on using the changeset viewer.