Changeset 74987 in vbox
- Timestamp:
- Oct 23, 2018 8:46:05 AM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 126011
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/VideoRec.cpp
r74984 r74987 538 538 static void videoRecAudioFrameFree(PVIDEORECAUDIOFRAME pFrame); 539 539 #endif 540 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame);541 540 static int videoRecRGBToYUV(uint32_t uPixelFormat, 542 541 uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight, 543 542 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight); 544 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream); 543 static int videoRecStreamClose(PVIDEORECSTREAM pStream); 544 static int videoRecStreamOpen(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg); 545 static int videoRecStreamUninit(PVIDEORECSTREAM pStream); 546 static int videoRecStreamUnitVideo(PVIDEORECSTREAM pStream); 547 static int videoRecStreamInitVideo(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg); 548 #ifdef VBOX_WITH_LIBVPX 549 static int videoRecStreamInitVideoVPX(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg); 550 static int videoRecStreamUninitVideoVPX(PVIDEORECSTREAM pStream); 551 static int videoRecStreamWriteVideoVPX(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame); 552 #endif 545 553 static void videoRecStreamLock(PVIDEORECSTREAM pStream); 546 554 static void videoRecStreamUnlock(PVIDEORECSTREAM pStream); … … 764 772 pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight); 765 773 if (RT_SUCCESS(rc)) 766 rc = videoRec EncodeAndWrite(pStream, uTimeStampMs, pVideoFrame);774 rc = videoRecStreamWriteVideoVPX(pStream, uTimeStampMs, pVideoFrame); 767 775 } 768 776 #endif … … 1012 1020 videoRecStreamLock(pStream); 1013 1021 1014 if (pStream->fEnabled) 1015 { 1016 switch (pStream->enmDst) 1017 { 1018 case VIDEORECDEST_FILE: 1019 { 1020 if (pStream->File.pWEBM) 1021 pStream->File.pWEBM->Close(); 1022 break; 1023 } 1024 1025 default: 1026 AssertFailed(); /* Should never happen. */ 1027 break; 1028 } 1029 1030 vpx_img_free(&pStream->Video.Codec.VPX.RawImage); 1031 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx); 1032 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 1033 1034 pStream->Blocks.Clear(); 1035 1036 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID)); 1037 } 1038 1039 switch (pStream->enmDst) 1040 { 1041 case VIDEORECDEST_FILE: 1042 { 1043 int rc2 = videoRecStreamCloseFile(pStream); 1044 AssertRC(rc2); 1045 1046 if (pStream->File.pWEBM) 1047 { 1048 delete pStream->File.pWEBM; 1049 pStream->File.pWEBM = NULL; 1050 } 1051 break; 1052 } 1053 1054 default: 1055 AssertFailed(); /* Should never happen. */ 1056 break; 1057 } 1022 int rc2 = videoRecStreamClose(pStream); 1023 if (RT_SUCCESS(rc)) 1024 rc = rc2; 1025 1026 rc2 = videoRecStreamUninit(pStream); 1027 if (RT_SUCCESS(rc)) 1028 rc = rc2; 1058 1029 1059 1030 pCtx->vecStreams.erase(it); … … 1132 1103 1133 1104 /** 1134 * Opens a file for a given recording stream to capture to.1105 * Opens a recording stream. 1135 1106 * 1136 1107 * @returns IPRT status code. 1137 * @param pStream Recording stream to open file for.1108 * @param pStream Recording stream to open. 1138 1109 * @param pCfg Recording configuration to use. 1139 1110 */ 1140 static int videoRecStreamOpen File(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg)1111 static int videoRecStreamOpen(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 1141 1112 { 1142 1113 AssertPtrReturn(pStream, VERR_INVALID_POINTER); … … 1144 1115 1145 1116 Assert(pStream->enmDst == VIDEORECDEST_INVALID); 1146 Assert(pCfg->enmDst == VIDEORECDEST_FILE);1147 1148 Assert(pCfg->File.strName.isNotEmpty());1149 1150 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(pCfg->File.strName).c_str());1151 AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY);1152 1153 RTPathStripSuffix(pszAbsPath);1154 1155 char *pszSuff = RTStrDup(".webm");1156 if (!pszSuff)1157 {1158 RTStrFree(pszAbsPath);1159 return VERR_NO_MEMORY;1160 }1161 1162 char *pszFile = NULL;1163 1117 1164 1118 int rc; 1165 if (pCfg->aScreens.size() > 1) 1166 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff); 1167 else 1168 rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff); 1169 1170 if (RT_SUCCESS(rc)) 1171 { 1172 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; 1173 1174 /* Play safe: the file must not exist, overwriting is potentially 1175 * hazardous as nothing prevents the user from picking a file name of some 1176 * other important file, causing unintentional data loss. */ 1177 fOpen |= RTFILE_O_CREATE; 1178 1179 RTFILE hFile; 1180 rc = RTFileOpen(&hFile, pszFile, fOpen); 1181 if (rc == VERR_ALREADY_EXISTS) 1182 { 1183 RTStrFree(pszFile); 1184 pszFile = NULL; 1185 1186 RTTIMESPEC ts; 1187 RTTimeNow(&ts); 1188 RTTIME time; 1189 RTTimeExplode(&time, &ts); 1119 1120 switch (pCfg->enmDst) 1121 { 1122 case VIDEORECDEST_FILE: 1123 { 1124 Assert(pCfg->File.strName.isNotEmpty()); 1125 1126 char *pszAbsPath = RTPathAbsDup(com::Utf8Str(pCfg->File.strName).c_str()); 1127 AssertPtrReturn(pszAbsPath, VERR_NO_MEMORY); 1128 1129 RTPathStripSuffix(pszAbsPath); 1130 1131 char *pszSuff = RTStrDup(".webm"); 1132 if (!pszSuff) 1133 { 1134 RTStrFree(pszAbsPath); 1135 rc = VERR_NO_MEMORY; 1136 break; 1137 } 1138 1139 char *pszFile = NULL; 1190 1140 1191 1141 if (pCfg->aScreens.size() > 1) 1192 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s", 1193 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 1194 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 1195 pStream->uScreenID + 1, pszSuff); 1142 rc = RTStrAPrintf(&pszFile, "%s-%u%s", pszAbsPath, pStream->uScreenID + 1, pszSuff); 1196 1143 else 1197 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s", 1198 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 1199 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 1200 pszSuff); 1144 rc = RTStrAPrintf(&pszFile, "%s%s", pszAbsPath, pszSuff); 1201 1145 1202 1146 if (RT_SUCCESS(rc)) 1147 { 1148 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; 1149 1150 /* Play safe: the file must not exist, overwriting is potentially 1151 * hazardous as nothing prevents the user from picking a file name of some 1152 * other important file, causing unintentional data loss. */ 1153 fOpen |= RTFILE_O_CREATE; 1154 1155 RTFILE hFile; 1203 1156 rc = RTFileOpen(&hFile, pszFile, fOpen); 1204 } 1205 1206 if (RT_SUCCESS(rc)) 1207 { 1208 pStream->enmDst = VIDEORECDEST_FILE; 1209 pStream->File.hFile = hFile; 1210 pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */ 1211 } 1212 } 1213 1214 RTStrFree(pszSuff); 1215 RTStrFree(pszAbsPath); 1216 1217 if (RT_FAILURE(rc)) 1218 { 1219 LogRel(("VideoRec: Failed to open file '%s' for screen %RU32, rc=%Rrc\n", 1220 pszFile ? pszFile : "<Unnamed>", pStream->uScreenID, rc)); 1221 RTStrFree(pszFile); 1222 } 1223 1157 if (rc == VERR_ALREADY_EXISTS) 1158 { 1159 RTStrFree(pszFile); 1160 pszFile = NULL; 1161 1162 RTTIMESPEC ts; 1163 RTTimeNow(&ts); 1164 RTTIME time; 1165 RTTimeExplode(&time, &ts); 1166 1167 if (pCfg->aScreens.size() > 1) 1168 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ-%u%s", 1169 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 1170 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 1171 pStream->uScreenID + 1, pszSuff); 1172 else 1173 rc = RTStrAPrintf(&pszFile, "%s-%04d-%02u-%02uT%02u-%02u-%02u-%09uZ%s", 1174 pszAbsPath, time.i32Year, time.u8Month, time.u8MonthDay, 1175 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond, 1176 pszSuff); 1177 1178 if (RT_SUCCESS(rc)) 1179 rc = RTFileOpen(&hFile, pszFile, fOpen); 1180 } 1181 1182 if (RT_SUCCESS(rc)) 1183 { 1184 pStream->enmDst = VIDEORECDEST_FILE; 1185 pStream->File.hFile = hFile; 1186 pStream->File.pszFile = pszFile; /* Assign allocated string to our stream's config. */ 1187 } 1188 } 1189 1190 RTStrFree(pszSuff); 1191 RTStrFree(pszAbsPath); 1192 1193 if (RT_FAILURE(rc)) 1194 { 1195 LogRel(("VideoRec: Failed to open file '%s' for screen %RU32, rc=%Rrc\n", 1196 pszFile ? pszFile : "<Unnamed>", pStream->uScreenID, rc)); 1197 RTStrFree(pszFile); 1198 } 1199 1200 break; 1201 } 1202 1203 default: 1204 rc = VERR_NOT_IMPLEMENTED; 1205 break; 1206 } 1207 1208 LogFlowFuncLeaveRC(rc); 1224 1209 return rc; 1225 }1226 1227 /**1228 * Closes a recording stream's file again.1229 *1230 * @returns IPRT status code.1231 * @param pStream Recording stream to close file for.1232 */1233 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream)1234 {1235 Assert(pStream->enmDst == VIDEORECDEST_FILE);1236 1237 pStream->enmDst = VIDEORECDEST_INVALID;1238 1239 AssertPtr(pStream->File.pszFile);1240 1241 if (RTFileIsValid(pStream->File.hFile))1242 {1243 RTFileClose(pStream->File.hFile);1244 LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile));1245 }1246 1247 RTStrFree(pStream->File.pszFile);1248 pStream->File.pszFile = NULL;1249 1250 return VINF_SUCCESS;1251 1210 } 1252 1211 … … 1278 1237 return VERR_NOT_FOUND; 1279 1238 1280 int rc = videoRecStreamOpen File(pStream, &pCtx->Cfg);1239 int rc = videoRecStreamOpen(pStream, pCfg); 1281 1240 if (RT_FAILURE(rc)) 1282 1241 return rc; … … 1284 1243 pStream->pCtx = pCtx; 1285 1244 1286 /** @todo Make the following parameters configurable on a per-stream basis? */ 1287 pStream->Video.uWidth = pCfg->Video.uWidth; 1288 pStream->Video.uHeight = pCfg->Video.uHeight; 1289 pStream->Video.cFailedEncodingFrames = 0; 1290 1291 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 1292 1293 pStream->Video.uDelayMs = RT_MS_1SEC / pCfg->Video.uFPS; 1245 if (pCfg->Video.fEnabled) 1246 rc = videoRecStreamInitVideo(pStream, pCfg); 1294 1247 1295 1248 switch (pStream->enmDst) … … 1322 1275 } 1323 1276 1324 LogRel(("VideoRec: Recording screen #%u with %RU32x%RU32 @ %RU32 kbps, %RU32 FPS\n", 1325 uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS)); 1277 LogRel(("VideoRec: Recording video of screen #%u with %RU32x%RU32 @ %RU32 kbps, %RU32 FPS (track #%RU8)\n", 1278 uScreen, pCfg->Video.uWidth, pCfg->Video.uHeight, pCfg->Video.uRate, pCfg->Video.uFPS, 1279 pStream->uTrackVideo)); 1326 1280 } 1327 1281 … … 1337 1291 } 1338 1292 1339 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s\n", 1340 pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channels" : "channel")); 1293 LogRel(("VideoRec: Recording audio in %RU16Hz, %RU8 bit, %RU8 %s (track #%RU8)\n", 1294 pCfg->Audio.uHz, pCfg->Audio.cBits, pCfg->Audio.cChannels, pCfg->Audio.cChannels ? "channels" : "channel", 1295 pStream->uTrackAudio)); 1341 1296 } 1342 1297 #endif … … 1372 1327 1373 1328 if (RT_FAILURE(rc)) 1329 { 1330 int rc2 = videoRecStreamClose(pStream); 1331 AssertRC(rc2); 1374 1332 return rc; 1375 1333 } 1334 1335 pStream->fEnabled = true; 1336 1337 return VINF_SUCCESS; 1338 } 1339 1340 /** 1341 * Closes a recording stream. 1342 * Depending on the stream's recording destination, this function closes all associated handles 1343 * and finalizes recording. 1344 * 1345 * @returns IPRT status code. 1346 * @param pStream Recording stream to close. 1347 * 1348 */ 1349 static int videoRecStreamClose(PVIDEORECSTREAM pStream) 1350 { 1351 int rc = VINF_SUCCESS; 1352 1353 if (pStream->fEnabled) 1354 { 1355 switch (pStream->enmDst) 1356 { 1357 case VIDEORECDEST_FILE: 1358 { 1359 if (pStream->File.pWEBM) 1360 rc = pStream->File.pWEBM->Close(); 1361 break; 1362 } 1363 1364 default: 1365 AssertFailed(); /* Should never happen. */ 1366 break; 1367 } 1368 1369 pStream->Blocks.Clear(); 1370 1371 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID)); 1372 } 1373 1374 if (RT_FAILURE(rc)) 1375 { 1376 LogRel(("VideoRec: Error stopping recording screen #%u, rc=%Rrc\n", pStream->uScreenID, rc)); 1377 return rc; 1378 } 1379 1380 switch (pStream->enmDst) 1381 { 1382 case VIDEORECDEST_FILE: 1383 { 1384 AssertPtr(pStream->File.pszFile); 1385 if (RTFileIsValid(pStream->File.hFile)) 1386 { 1387 rc = RTFileClose(pStream->File.hFile); 1388 if (RT_SUCCESS(rc)) 1389 { 1390 LogRel(("VideoRec: Closed file '%s'\n", pStream->File.pszFile)); 1391 } 1392 else 1393 { 1394 LogRel(("VideoRec: Error closing file '%s', rc=%Rrc\n", pStream->File.pszFile, rc)); 1395 break; 1396 } 1397 } 1398 1399 RTStrFree(pStream->File.pszFile); 1400 pStream->File.pszFile = NULL; 1401 1402 if (pStream->File.pWEBM) 1403 { 1404 delete pStream->File.pWEBM; 1405 pStream->File.pWEBM = NULL; 1406 } 1407 break; 1408 } 1409 1410 default: 1411 rc = VERR_NOT_IMPLEMENTED; 1412 break; 1413 } 1414 1415 if (RT_SUCCESS(rc)) 1416 { 1417 pStream->enmDst = VIDEORECDEST_INVALID; 1418 } 1419 1420 LogFlowFuncLeaveRC(rc); 1421 return rc; 1422 } 1423 1424 /** 1425 * Uninitializes a recording stream. 1426 * 1427 * @returns IPRT status code. 1428 * @param pStream Recording stream to uninitialize. 1429 */ 1430 static int videoRecStreamUninit(PVIDEORECSTREAM pStream) 1431 { 1432 int rc = VINF_SUCCESS; 1433 1434 if (pStream->pCtx->Cfg.Video.fEnabled) 1435 { 1436 int rc2 = videoRecStreamUnitVideo(pStream); 1437 if (RT_SUCCESS(rc)) 1438 rc = rc2; 1439 } 1440 1441 return rc; 1442 } 1443 1444 /** 1445 * Uninitializes video recording for a certain recording stream. 1446 * 1447 * @returns IPRT status code. 1448 * @param pStream Recording stream to uninitialize video recording for. 1449 */ 1450 static int videoRecStreamUnitVideo(PVIDEORECSTREAM pStream) 1451 { 1376 1452 #ifdef VBOX_WITH_LIBVPX 1453 /* At the moment we only have VPX. */ 1454 return videoRecStreamUninitVideoVPX(pStream); 1455 #else 1456 return VERR_NOT_SUPPORTED; 1457 #endif 1458 } 1459 1460 #ifdef VBOX_WITH_LIBVPX 1461 /** 1462 * Uninitializes the VPX codec for a certain recording stream. 1463 * 1464 * @returns IPRT status code. 1465 * @param pStream Recording stream to uninitialize VPX codec for. 1466 */ 1467 static int videoRecStreamUninitVideoVPX(PVIDEORECSTREAM pStream) 1468 { 1469 vpx_img_free(&pStream->Video.Codec.VPX.RawImage); 1470 vpx_codec_err_t rcv = vpx_codec_destroy(&pStream->Video.Codec.VPX.Ctx); 1471 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 1472 1473 return VINF_SUCCESS; 1474 } 1475 #endif 1476 1477 /** 1478 * Initializes the video recording for a certain recording stream. 1479 * 1480 * @returns IPRT status code. 1481 * @param pStream Recording stream to initialize video recording for. 1482 * @param pCfg Video recording configuration to use for initialization. 1483 */ 1484 static int videoRecStreamInitVideo(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 1485 { 1486 #ifdef VBOX_WITH_LIBVPX 1487 /* At the moment we only have VPX. */ 1488 return videoRecStreamInitVideoVPX(pStream, pCfg); 1489 #else 1490 return VERR_NOT_SUPPORTED; 1491 #endif 1492 } 1493 1494 #ifdef VBOX_WITH_LIBVPX 1495 /** 1496 * Initializes the VPX codec for a certain recording stream. 1497 * 1498 * @returns IPRT status code. 1499 * @param pStream Recording stream to initialize VPX codec for. 1500 * @param pCfg Video recording configuration to use for initialization. 1501 */ 1502 static int videoRecStreamInitVideoVPX(PVIDEORECSTREAM pStream, PVIDEORECCFG pCfg) 1503 { 1504 pStream->Video.uWidth = pCfg->Video.uWidth; 1505 pStream->Video.uHeight = pCfg->Video.uHeight; 1506 pStream->Video.cFailedEncodingFrames = 0; 1507 1508 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 1509 1510 pStream->Video.uDelayMs = RT_MS_1SEC / pCfg->Video.uFPS; 1511 1377 1512 # ifdef VBOX_WITH_LIBVPX_VP9 1378 1513 vpx_codec_iface_t *pCodecIface = vpx_codec_vp9_cx(); … … 1416 1551 /* Save a pointer to the first raw YUV plane. */ 1417 1552 pStream->Video.Codec.VPX.pu8YuvBuf = pVC->VPX.RawImage.planes[0]; 1418 #endif1419 pStream->fEnabled = true;1420 1553 1421 1554 return VINF_SUCCESS; 1422 1555 } 1556 #endif 1423 1557 1424 1558 /** … … 1540 1674 } 1541 1675 1676 #ifdef VBOX_WITH_LIBVPX 1542 1677 /** 1543 1678 * Encodes the source image and write the encoded image to the stream's destination. … … 1548 1683 * @param pFrame Frame to encode and submit. 1549 1684 */ 1550 static int videoRec EncodeAndWrite(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame)1685 static int videoRecStreamWriteVideoVPX(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame) 1551 1686 { 1552 1687 AssertPtrReturn(pStream, VERR_INVALID_POINTER); … … 1558 1693 PVIDEORECCFG pCfg = &pStream->pCtx->Cfg; 1559 1694 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; 1560 #ifdef VBOX_WITH_LIBVPX 1695 1561 1696 /* Presentation Time Stamp (PTS). */ 1562 1697 vpx_codec_pts_t pts = uTimeStampMs; … … 1601 1736 } 1602 1737 } 1603 #else 1604 RT_NOREF(pStream);1605 rc = VERR_NOT_SUPPORTED; 1738 1739 return rc; 1740 } 1606 1741 #endif /* VBOX_WITH_LIBVPX */ 1607 return rc;1608 }1609 1742 1610 1743 /**
Note:
See TracChangeset
for help on using the changeset viewer.