Changeset 74904 in vbox
- Timestamp:
- Oct 18, 2018 8:03:38 AM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 125898
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/src-client/VideoRec.cpp
r73505 r74904 1 1 /* $Id$ */ 2 2 /** @file 3 * Video capturing utility routines. 3 * Video recording (with optional audio recording) code. 4 * 5 * This code employs a separate encoding thread per recording context 6 * to keep time spent in EMT as short as possible. Each configured VM display 7 * is represented by an own recording stream, which in turn has its own rendering 8 * queue. Common recording data across all recording streams is kept in a 9 * separate queue in the recording context to minimize data duplication and 10 * multiplexing overhead in EMT. 4 11 */ 5 12 6 13 /* 7 * Copyright (C) 2012-201 7Oracle Corporation14 * Copyright (C) 2012-2018 Oracle Corporation 8 15 * 9 16 * This file is part of VirtualBox Open Source Edition (OSE), as … … 46 53 #endif /* VBOX_WITH_LIBVPX */ 47 54 48 struct VIDEORECVIDEOFRAME;49 typedef struct VIDEORECVIDEOFRAME *PVIDEORECVIDEOFRAME;50 51 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, PVIDEORECVIDEOFRAME pFrame);52 static int videoRecRGBToYUV(uint32_t uPixelFormat,53 uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight,54 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight);55 56 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream);57 static void videoRecStreamLock(PVIDEORECSTREAM pStream);58 static void videoRecStreamUnlock(PVIDEORECSTREAM pStream);59 60 55 using namespace com; 61 56 62 #if 063 /** Enables support for encoding multiple audio / video data frames at once. */64 #define VBOX_VIDEOREC_WITH_QUEUE65 #endif66 57 #ifdef DEBUG_andy 67 58 /** Enables dumping audio / video data for debugging reasons. */ … … 131 122 /** Pixel format of this frame. */ 132 123 uint32_t uPixelFormat; 133 /** Time stamp (in ms). */134 uint64_t uTimeStampMs;135 124 /** RGB buffer containing the unmodified frame buffer data from Main's display. */ 136 125 uint8_t *pu8RGBBuf; … … 145 134 typedef struct VIDEORECAUDIOFRAME 146 135 { 147 uint8_t abBuf[_64K]; /** @todo Fix! */ 136 /** Pointer to audio data. */ 137 uint8_t *pvBuf; 138 /** Size (in bytes) of audio data. */ 148 139 size_t cbBuf; 149 /** Absolute time stamp (in ms). */150 uint64_t uTimeStampMs;151 140 } VIDEORECAUDIOFRAME, *PVIDEORECAUDIOFRAME; 152 141 #endif 153 142 154 143 /** 155 * Strucutre for maintaining a video recording stream. 156 */ 157 typedef struct VIDEORECSTREAM 144 * Enumeration for specifying a video recording block type. 145 */ 146 typedef enum VIDEORECBLOCKTYPE 147 { 148 /** Uknown block type, do not use. */ 149 VIDEORECBLOCKTYPE_UNKNOWN = 0, 150 /** The block is a video frame. */ 151 VIDEORECBLOCKTYPE_VIDEO, 152 #ifdef VBOX_WITH_AUDIO_VIDEOREC 153 /** The block is an audio frame. */ 154 VIDEORECBLOCKTYPE_AUDIO 155 #endif 156 } VIDEORECBLOCKTYPE; 157 158 /** 159 * Generic structure for keeping a single video recording (data) block. 160 */ 161 typedef struct VIDEORECBLOCK 162 { 163 /** The block's type. */ 164 VIDEORECBLOCKTYPE enmType; 165 /** Number of references held of this block. */ 166 uint16_t cRefs; 167 /** The (absolute) time stamp (in ms, PTS) of this block. */ 168 uint64_t uTimeStampMs; 169 /** Opaque data block to the actual block data, depending on the block's type. */ 170 void *pvData; 171 /** Size (in bytes) of the (opaque) data block. */ 172 size_t cbData; 173 } VIDEORECBLOCK, *PVIDEORECBLOCK; 174 175 /** List for keeping video recording (data) blocks. */ 176 typedef std::list<PVIDEORECBLOCK> VideoRecBlockList; 177 178 static void videoRecBlockFree(PVIDEORECBLOCK pBlock); 179 180 /** Structure for queuing all blocks bound to a single timecode. 181 * This can happen if multiple tracks are being involved. */ 182 struct VideoRecBlocks 183 { 184 virtual ~VideoRecBlocks() 185 { 186 Clear(); 187 } 188 189 /** 190 * Resets a video recording block list by removing (destroying) 191 * all current elements. 192 */ 193 void Clear() 194 { 195 while (!List.empty()) 196 { 197 PVIDEORECBLOCK pBlock = List.front(); 198 videoRecBlockFree(pBlock); 199 List.pop_front(); 200 } 201 202 Assert(List.size() == 0); 203 } 204 205 /** The actual block list for this timecode. */ 206 VideoRecBlockList List; 207 }; 208 209 /** A block map containing all currently queued blocks. 210 * The key specifies a unique timecode, whereas the value 211 * is a list of blocks which all correlate to the same key (timecode). */ 212 typedef std::map<uint64_t, VideoRecBlocks *> VideoRecBlockMap; 213 214 /** 215 * Structure for holding a set of video recording (data) blocks. 216 */ 217 struct VideoRecBlockSet 218 { 219 virtual ~VideoRecBlockSet() 220 { 221 Clear(); 222 } 223 224 /** 225 * Resets a video recording block set by removing (destroying) 226 * all current elements. 227 */ 228 void Clear() 229 { 230 VideoRecBlockMap::iterator it = Map.begin(); 231 while (it != Map.end()) 232 { 233 it->second->Clear(); 234 delete it->second; 235 it = Map.erase(it); 236 } 237 238 Assert(Map.size() == 0); 239 } 240 241 /** Timestamp (in ms) when this set was last processed. */ 242 uint64_t tsLastProcessedMs; 243 /** All blocks related to this block set. */ 244 VideoRecBlockMap Map; 245 }; 246 247 /** 248 * Structure for maintaining a video recording stream. 249 */ 250 struct VIDEORECSTREAM 158 251 { 159 252 /** Video recording context this stream is associated to. */ … … 200 293 /** Pointer to the codec's internal YUV buffer. */ 201 294 uint8_t *pu8YuvBuf; 202 #ifdef VBOX_VIDEOREC_WITH_QUEUE203 # error "Implement me!"204 #else205 VIDEORECVIDEOFRAME Frame;206 bool fHasVideoData;207 #endif208 295 /** Number of failed attempts to encode the current video frame in a row. */ 209 296 uint16_t cFailedEncodingFrames; 210 297 } Video; 211 } VIDEORECSTREAM, *PVIDEORECSTREAM; 298 299 /** Common set of video recording (data) blocks, needed for 300 * multiplexing to all recording streams. */ 301 VideoRecBlockSet Blocks; 302 }; 212 303 213 304 /** Vector of video recording streams. */ … … 217 308 * Structure for keeping a video recording context. 218 309 */ 219 typedefstruct VIDEORECCONTEXT310 struct VIDEORECCONTEXT 220 311 { 221 312 /** Used recording configuration. */ … … 233 324 /** Worker thread. */ 234 325 RTTHREAD Thread; 235 /** Vector of current recording stream contexts. */ 326 /** Vector of current recording streams. 327 * Per VM screen (display) one recording stream is being used. */ 236 328 VideoRecStreams vecStreams; 237 329 /** Timestamp (in ms) of when recording has been started. */ 238 330 uint64_t tsStartMs; 239 #ifdef VBOX_WITH_AUDIO_VIDEOREC 240 struct 241 { 242 bool fHasAudioData; 243 VIDEORECAUDIOFRAME Frame; 244 } Audio; 245 #endif 246 } VIDEORECCONTEXT, *PVIDEORECCONTEXT; 331 /** Block map of common blocks which need to get multiplexed 332 * to all recording streams. This common block maps should help 333 * reducing the time spent in EMT and avoid doing the (expensive) 334 * multiplexing work in there. 335 * 336 * For now this only affects audio, e.g. all recording streams 337 * need to have the same audio data at a specific point in time. */ 338 VideoRecBlockMap mapBlocksCommon; 339 }; 247 340 248 341 #ifdef VBOX_VIDEOREC_DUMP … … 440 533 }; 441 534 535 #ifdef VBOX_WITH_AUDIO_VIDEOREC 536 static void videoRecAudioFrameFree(PVIDEORECAUDIOFRAME pFrame); 537 #endif 538 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame); 539 static int videoRecRGBToYUV(uint32_t uPixelFormat, 540 uint8_t *paDst, uint32_t uDstWidth, uint32_t uDstHeight, 541 uint8_t *paSrc, uint32_t uSrcWidth, uint32_t uSrcHeight); 542 static int videoRecStreamCloseFile(PVIDEORECSTREAM pStream); 543 static void videoRecStreamLock(PVIDEORECSTREAM pStream); 544 static void videoRecStreamUnlock(PVIDEORECSTREAM pStream); 545 static void videoRecVideoFrameFree(PVIDEORECVIDEOFRAME pFrame); 546 442 547 /** 443 548 * Convert an image to YUV420p format. … … 547 652 } 548 653 654 #ifdef VBOX_WITH_AUDIO_VIDEOREC 655 /** 656 * Frees a previously allocated video recording audio frame. 657 * 658 * @param pFrame Audio frame to free. The pointer will be invalid after return. 659 */ 660 static void videoRecAudioFrameFree(PVIDEORECAUDIOFRAME pFrame) 661 { 662 if (!pFrame) 663 return; 664 665 if (pFrame->pvBuf) 666 { 667 Assert(pFrame->cbBuf); 668 RTMemFree(pFrame->pvBuf); 669 } 670 RTMemFree(pFrame); 671 pFrame = NULL; 672 } 673 #endif 674 675 /** 676 * Frees a video recording (data) block. 677 * 678 * @returns IPRT status code. 679 * @param pBlock Video recording (data) block to free. The pointer will be invalid after return. 680 */ 681 static void videoRecBlockFree(PVIDEORECBLOCK pBlock) 682 { 683 if (!pBlock) 684 return; 685 686 switch (pBlock->enmType) 687 { 688 case VIDEORECBLOCKTYPE_VIDEO: 689 videoRecVideoFrameFree((PVIDEORECVIDEOFRAME)pBlock->pvData); 690 break; 691 692 #ifdef VBOX_WITH_AUDIO_VIDEOREC 693 case VIDEORECBLOCKTYPE_AUDIO: 694 videoRecAudioFrameFree((PVIDEORECAUDIOFRAME)pBlock->pvData); 695 break; 696 #endif 697 default: 698 AssertFailed(); 699 break; 700 } 701 702 RTMemFree(pBlock); 703 pBlock = NULL; 704 } 705 549 706 /** 550 707 * Worker thread for all streams of a video recording context. 551 708 * 552 * DoesRGB/YUV conversion and encoding.709 * For video frames, this also does the RGB/YUV conversion and encoding. 553 710 */ 554 711 static DECLCALLBACK(int) videoRecThread(RTTHREAD hThreadSelf, void *pvUser) … … 559 716 RTThreadUserSignal(hThreadSelf); 560 717 718 LogFunc(("Thread started\n")); 719 561 720 for (;;) 562 721 { 563 722 int rc = RTSemEventWait(pCtx->WaitEvent, RT_INDEFINITE_WAIT); 564 723 AssertRCBreak(rc); 565 566 if (ASMAtomicReadBool(&pCtx->fShutdown))567 break;568 569 #ifdef VBOX_WITH_AUDIO_VIDEOREC570 VIDEORECAUDIOFRAME audioFrame;571 RT_ZERO(audioFrame);572 573 int rc2 = RTCritSectEnter(&pCtx->CritSect);574 AssertRC(rc2);575 576 const bool fEncodeAudio = pCtx->Audio.fHasAudioData;577 if (fEncodeAudio)578 {579 /*580 * Every recording stream needs to get the same audio data at a certain point in time.581 * Do the multiplexing here to not block EMT for too long.582 *583 * For now just doing a simple copy of the current audio frame should be good enough.584 */585 memcpy(&audioFrame, &pCtx->Audio.Frame, sizeof(VIDEORECAUDIOFRAME));586 587 pCtx->Audio.fHasAudioData = false;588 }589 590 rc2 = RTCritSectLeave(&pCtx->CritSect);591 AssertRC(rc2);592 #endif593 724 594 725 /** @todo r=andy This is inefficient -- as we already wake up this thread 595 726 * for every screen from Main, we here go again (on every wake up) through 596 727 * all screens. */ 597 for (VideoRecStreams::iterator it = pCtx->vecStreams.begin(); it != pCtx->vecStreams.end(); it++)598 { 599 PVIDEORECSTREAM pStream = (*it );728 for (VideoRecStreams::iterator itStream = pCtx->vecStreams.begin(); itStream != pCtx->vecStreams.end(); itStream++) 729 { 730 PVIDEORECSTREAM pStream = (*itStream); 600 731 601 732 videoRecStreamLock(pStream); … … 607 738 } 608 739 609 PVIDEORECVIDEOFRAME pVideoFrame = &pStream->Video.Frame; 610 const bool fEncodeVideo = pStream->Video.fHasVideoData; 611 612 if (fEncodeVideo) 740 VideoRecBlockMap::iterator itBlockStream = pStream->Blocks.Map.begin(); 741 while (itBlockStream != pStream->Blocks.Map.end()) 613 742 { 614 rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat, 615 /* Destination */ 616 pStream->Video.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight, 617 /* Source */ 618 pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight); 619 if (RT_SUCCESS(rc)) 620 rc = videoRecEncodeAndWrite(pStream, pVideoFrame); 621 622 pStream->Video.fHasVideoData = false; 743 const uint64_t uTimeStampMs = itBlockStream->first; 744 VideoRecBlocks *pBlocks = itBlockStream->second; 745 746 AssertPtr(pBlocks); 747 748 while (!pBlocks->List.empty()) 749 { 750 PVIDEORECBLOCK pBlock = pBlocks->List.front(); 751 AssertPtr(pBlock); 752 753 if (pBlock->enmType == VIDEORECBLOCKTYPE_VIDEO) 754 { 755 PVIDEORECVIDEOFRAME pVideoFrame = (PVIDEORECVIDEOFRAME)pBlock->pvData; 756 757 rc = videoRecRGBToYUV(pVideoFrame->uPixelFormat, 758 /* Destination */ 759 pStream->Video.pu8YuvBuf, pVideoFrame->uWidth, pVideoFrame->uHeight, 760 /* Source */ 761 pVideoFrame->pu8RGBBuf, pStream->Video.uWidth, pStream->Video.uHeight); 762 if (RT_SUCCESS(rc)) 763 rc = videoRecEncodeAndWrite(pStream, uTimeStampMs, pVideoFrame); 764 } 765 766 videoRecBlockFree(pBlock); 767 pBlock = NULL; 768 769 pBlocks->List.pop_front(); 770 } 771 772 #ifdef VBOX_WITH_AUDIO_VIDEOREC 773 /* As each (enabled) screen has to get the same audio data, look for common (audio) data which needs to be 774 * written to the screen's assigned recording stream. */ 775 VideoRecBlockMap::iterator itCommon = pCtx->mapBlocksCommon.begin(); 776 while (itCommon != pCtx->mapBlocksCommon.end()) 777 { 778 VideoRecBlockList::iterator itBlockCommon = itCommon->second->List.begin(); 779 while (itBlockCommon != itCommon->second->List.end()) 780 { 781 PVIDEORECBLOCK pBlockCommon = (PVIDEORECBLOCK)(*itBlockCommon); 782 switch (pBlockCommon->enmType) 783 { 784 case VIDEORECBLOCKTYPE_AUDIO: 785 { 786 PVIDEORECAUDIOFRAME pAudioFrame = (PVIDEORECAUDIOFRAME)pBlockCommon->pvData; 787 AssertPtr(pAudioFrame); 788 AssertPtr(pAudioFrame->pvBuf); 789 Assert(pAudioFrame->cbBuf); 790 791 WebMWriter::BlockData_Opus blockData = { pAudioFrame->pvBuf, pAudioFrame->cbBuf, 792 pBlockCommon->uTimeStampMs }; 793 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData)); 794 break; 795 } 796 797 default: 798 AssertFailed(); 799 break; 800 } 801 802 Assert(pBlockCommon->cRefs); 803 if (--pBlockCommon->cRefs == 0) 804 { 805 videoRecBlockFree(pBlockCommon); 806 itBlockCommon = itCommon->second->List.erase(itBlockCommon); 807 } 808 else 809 ++itBlockCommon; 810 } 811 812 /* If no entries are left over in the block map, remove it altogether. */ 813 if (itCommon->second->List.empty()) 814 { 815 delete itCommon->second; 816 pCtx->mapBlocksCommon.erase(itCommon); 817 } 818 819 itCommon = pCtx->mapBlocksCommon.begin(); 820 821 LogFunc(("Common blocks: %zu\n", pCtx->mapBlocksCommon.size())); 822 } 823 #endif 824 ++itBlockStream; 623 825 } 624 826 625 827 videoRecStreamUnlock(pStream); 626 627 if (RT_FAILURE(rc))628 {629 static unsigned s_cErrEncVideo = 0;630 if (s_cErrEncVideo < 32)631 {632 LogRel(("VideoRec: Error %Rrc encoding / writing video frame\n", rc));633 s_cErrEncVideo++;634 }635 }636 637 #ifdef VBOX_WITH_AUDIO_VIDEOREC638 /* Each (enabled) screen has to get the same audio data. */639 if (fEncodeAudio)640 {641 Assert(audioFrame.cbBuf);642 Assert(audioFrame.cbBuf <= _64K); /** @todo Fix. */643 644 WebMWriter::BlockData_Opus blockData = { audioFrame.abBuf, audioFrame.cbBuf, audioFrame.uTimeStampMs };645 rc = pStream->File.pWEBM->WriteBlock(pStream->uTrackAudio, &blockData, sizeof(blockData));646 if (RT_FAILURE(rc))647 {648 static unsigned s_cErrEncAudio = 0;649 if (s_cErrEncAudio < 32)650 {651 LogRel(("VideoRec: Error %Rrc encoding audio frame\n", rc));652 s_cErrEncAudio++;653 }654 }655 }656 #endif657 828 } 658 829 659 830 /* Keep going in case of errors. */ 660 831 832 if (ASMAtomicReadBool(&pCtx->fShutdown)) 833 { 834 LogFunc(("Thread is shutting down ...\n")); 835 break; 836 } 837 661 838 } /* for */ 662 839 840 LogFunc(("Thread ended\n")); 663 841 return VINF_SUCCESS; 842 } 843 844 /** 845 * Notifies a recording context's encoding thread. 846 * 847 * @returns IPRT status code. 848 * @param pCtx Video recording context to notify thread for. 849 */ 850 static int videoRecThreadNotify(PVIDEORECCONTEXT pCtx) 851 { 852 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 853 854 return RTSemEventSignal(pCtx->WaitEvent); 664 855 } 665 856 … … 678 869 AssertPtrReturn(ppCtx, VERR_INVALID_POINTER); 679 870 680 PVIDEORECCONTEXT pCtx = (PVIDEORECCONTEXT)RTMemAllocZ(sizeof(VIDEORECCONTEXT)); 681 if (!pCtx) 871 VIDEORECCONTEXT *pCtx = NULL; 872 try 873 { 874 pCtx = new VIDEORECCONTEXT(); 875 } 876 catch (std::bad_alloc &) 877 { 682 878 return VERR_NO_MEMORY; 879 } 683 880 684 881 int rc = RTCritSectInit(&pCtx->CritSect); 685 882 if (RT_FAILURE(rc)) 686 883 { 687 RTMemFree(pCtx);884 delete pCtx; 688 885 return rc; 689 886 } … … 691 888 for (uint32_t uScreen = 0; uScreen < cScreens; uScreen++) 692 889 { 693 PVIDEORECSTREAM pStream = (PVIDEORECSTREAM)RTMemAllocZ(sizeof(VIDEORECSTREAM)); 694 if (!pStream) 890 VIDEORECSTREAM *pStream = NULL; 891 try 892 { 893 pStream = new VIDEORECSTREAM(); 894 } 895 catch (std::bad_alloc &) 695 896 { 696 897 rc = VERR_NO_MEMORY; … … 765 966 return VINF_SUCCESS; 766 967 968 int rc = VINF_SUCCESS; 969 767 970 if (pCtx->enmState == VIDEORECSTS_INITIALIZED) 768 971 { 972 LogFunc(("Shutting down thread ...\n")); 973 769 974 /* Set shutdown indicator. */ 770 975 ASMAtomicWriteBool(&pCtx->fShutdown, true); 771 976 772 /* Signal the thread. */ 773 RTSemEventSignal(pCtx->WaitEvent); 774 775 int rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL); 776 if (RT_FAILURE(rc)) 777 return rc; 778 779 /* Disable the context. */ 780 ASMAtomicWriteBool(&pCtx->fStarted, false); 781 782 rc = RTSemEventDestroy(pCtx->WaitEvent); 977 /* Signal the thread and wait for it to shut down. */ 978 rc = videoRecThreadNotify(pCtx); 979 if (RT_SUCCESS(rc)) 980 rc = RTThreadWait(pCtx->Thread, 10 * 1000 /* 10s timeout */, NULL); 981 982 if (RT_SUCCESS(rc)) 983 { 984 /* Disable the context. */ 985 ASMAtomicWriteBool(&pCtx->fStarted, false); 986 987 int rc2 = RTSemEventDestroy(pCtx->WaitEvent); 988 AssertRC(rc2); 989 990 pCtx->WaitEvent = NIL_RTSEMEVENT; 991 } 992 } 993 994 if (RT_FAILURE(rc)) 995 { 783 996 AssertRC(rc); 784 785 pCtx->WaitEvent = NIL_RTSEMEVENT; 786 } 787 788 int rc = RTCritSectEnter(&pCtx->CritSect); 997 return rc; 998 } 999 1000 rc = RTCritSectEnter(&pCtx->CritSect); 789 1001 if (RT_SUCCESS(rc)) 790 1002 { … … 816 1028 Assert(rcv == VPX_CODEC_OK); RT_NOREF(rcv); 817 1029 818 #ifdef VBOX_VIDEOREC_WITH_QUEUE 819 # error "Implement me!" 820 #else 821 PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame; 822 #endif 823 if (pFrame->pu8RGBBuf) 824 { 825 Assert(pFrame->cbRGBBuf); 826 827 RTMemFree(pFrame->pu8RGBBuf); 828 pFrame->pu8RGBBuf = NULL; 829 } 830 831 pFrame->cbRGBBuf = 0; 1030 pStream->Blocks.Clear(); 832 1031 833 1032 LogRel(("VideoRec: Recording screen #%u stopped\n", pStream->uScreenID)); … … 860 1059 RTCritSectDelete(&pStream->CritSect); 861 1060 862 RTMemFree(pStream);1061 delete pStream; 863 1062 pStream = NULL; 864 1063 } 865 1064 1065 /* Sanity. */ 866 1066 Assert(pCtx->vecStreams.empty()); 1067 Assert(pCtx->mapBlocksCommon.size() == 0); 867 1068 868 1069 int rc2 = RTCritSectLeave(&pCtx->CritSect); … … 871 1072 RTCritSectDelete(&pCtx->CritSect); 872 1073 873 RTMemFree(pCtx);1074 delete pCtx; 874 1075 pCtx = NULL; 875 1076 } … … 1056 1257 AssertPtrReturn(pCtx, VERR_INVALID_POINTER); 1057 1258 1259 PVIDEORECCFG pCfg = &pCtx->Cfg; 1260 1261 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1262 if (pCfg->Audio.fEnabled) 1263 { 1264 /* Sanity. */ 1265 AssertReturn(pCfg->Audio.uHz, VERR_INVALID_PARAMETER); 1266 AssertReturn(pCfg->Audio.cBits, VERR_INVALID_PARAMETER); 1267 AssertReturn(pCfg->Audio.cChannels, VERR_INVALID_PARAMETER); 1268 } 1269 #endif 1270 1058 1271 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen); 1059 1272 if (!pStream) … … 1063 1276 if (RT_FAILURE(rc)) 1064 1277 return rc; 1065 1066 PVIDEORECCFG pCfg = &pCtx->Cfg;1067 1278 1068 1279 pStream->pCtx = pCtx; … … 1072 1283 pStream->Video.uHeight = pCfg->Video.uHeight; 1073 1284 pStream->Video.cFailedEncodingFrames = 0; 1074 1075 #ifndef VBOX_VIDEOREC_WITH_QUEUE1076 /* When not using a queue, we only use one frame per stream at once.1077 * So do the initialization here. */1078 PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame;1079 1080 const size_t cbRGBBuf = pStream->Video.uWidth1081 * pStream->Video.uHeight1082 * 4 /* 32 BPP maximum */;1083 AssertReturn(cbRGBBuf, VERR_INVALID_PARAMETER);1084 1085 pFrame->pu8RGBBuf = (uint8_t *)RTMemAllocZ(cbRGBBuf);1086 AssertReturn(pFrame->pu8RGBBuf, VERR_NO_MEMORY);1087 pFrame->cbRGBBuf = cbRGBBuf;1088 #endif1089 1285 1090 1286 PVIDEORECVIDEOCODEC pVC = &pStream->Video.Codec; … … 1267 1463 } 1268 1464 1269 PVIDEORECVIDEOFRAME pLastFrame = &pStream->Video.Frame; 1270 1271 if (uTimeStampMs < pLastFrame->uTimeStampMs + pStream->Video.uDelayMs) 1465 if (uTimeStampMs < pStream->Video.uLastTimeStampMs + pStream->Video.uDelayMs) 1272 1466 return false; 1273 1467 … … 1341 1535 * @returns IPRT status code. 1342 1536 * @param pStream Stream to encode and submit to. 1537 * @param uTimeStampMs Absolute timestamp (PTS) of frame (in ms) to encode. 1343 1538 * @param pFrame Frame to encode and submit. 1344 1539 */ 1345 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, PVIDEORECVIDEOFRAME pFrame)1540 static int videoRecEncodeAndWrite(PVIDEORECSTREAM pStream, uint64_t uTimeStampMs, PVIDEORECVIDEOFRAME pFrame) 1346 1541 { 1347 1542 AssertPtrReturn(pStream, VERR_INVALID_POINTER); … … 1355 1550 #ifdef VBOX_WITH_LIBVPX 1356 1551 /* Presentation Time Stamp (PTS). */ 1357 vpx_codec_pts_t pts = pFrame->uTimeStampMs;1552 vpx_codec_pts_t pts = uTimeStampMs; 1358 1553 vpx_codec_err_t rcv = vpx_codec_encode(&pVC->VPX.Ctx, 1359 1554 &pVC->VPX.RawImage, … … 1445 1640 * @param pCtx Pointer to the video recording context. 1446 1641 * @param pvData Audio frame data to send. 1447 * @param cbData Size (in bytes) of audio frame data.1642 * @param cbData Size (in bytes) of (encoded) audio frame data. 1448 1643 * @param uTimeStampMs Time stamp (in ms) of audio playback. 1449 1644 */ … … 1451 1646 { 1452 1647 #ifdef VBOX_WITH_AUDIO_VIDEOREC 1453 AssertReturn(cbData <= _64K, VERR_INVALID_PARAMETER); 1454 1455 int rc = RTCritSectEnter(&pCtx->CritSect); 1456 if (RT_FAILURE(rc)) 1457 return rc; 1648 AssertPtrReturn(pvData, VERR_INVALID_POINTER); 1649 AssertReturn(cbData, VERR_INVALID_PARAMETER); 1458 1650 1459 1651 /* To save time spent in EMT, do the required audio multiplexing in the encoding thread. … … 1462 1654 * audio data at the same given point in time. 1463 1655 */ 1464 PVIDEORECAUDIOFRAME pFrame = &pCtx->Audio.Frame; 1465 1466 memcpy(pFrame->abBuf, pvData, RT_MIN(_64K /** @todo Fix! */, cbData)); 1467 1468 pFrame->cbBuf = cbData; 1469 pFrame->uTimeStampMs = uTimeStampMs; 1470 1471 pCtx->Audio.fHasAudioData = true; 1472 1473 rc = RTCritSectLeave(&pCtx->CritSect); 1656 PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK)); 1657 AssertPtrReturn(pBlock, VERR_NO_MEMORY); 1658 pBlock->enmType = VIDEORECBLOCKTYPE_AUDIO; 1659 1660 PVIDEORECAUDIOFRAME pFrame = (PVIDEORECAUDIOFRAME)RTMemAlloc(sizeof(VIDEORECAUDIOFRAME)); 1661 AssertPtrReturn(pFrame, VERR_NO_MEMORY); 1662 1663 pFrame->pvBuf = (uint8_t *)RTMemAlloc(cbData); 1664 AssertPtrReturn(pFrame->pvBuf, VERR_NO_MEMORY); 1665 pFrame->cbBuf = cbData; 1666 1667 memcpy(pFrame->pvBuf, pvData, cbData); 1668 1669 pBlock->pvData = pFrame; 1670 pBlock->cbData = sizeof(VIDEORECAUDIOFRAME) + cbData; 1671 pBlock->cRefs = (uint16_t)pCtx->vecStreams.size(); /* All streams need the same audio data. */ 1672 pBlock->uTimeStampMs = uTimeStampMs; 1673 1674 int rc = RTCritSectEnter(&pCtx->CritSect); 1675 if (RT_FAILURE(rc)) 1676 return rc; 1677 1678 try 1679 { 1680 VideoRecBlockMap::iterator itBlocks = pCtx->mapBlocksCommon.find(uTimeStampMs); 1681 if (itBlocks == pCtx->mapBlocksCommon.end()) 1682 { 1683 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks(); 1684 pVideoRecBlocks->List.push_back(pBlock); 1685 1686 pCtx->mapBlocksCommon.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks)); 1687 } 1688 else 1689 itBlocks->second->List.push_back(pBlock); 1690 } 1691 catch (const std::exception &ex) 1692 { 1693 RT_NOREF(ex); 1694 rc = VERR_NO_MEMORY; 1695 } 1696 1697 int rc2 = RTCritSectLeave(&pCtx->CritSect); 1698 AssertRC(rc2); 1699 1474 1700 if (RT_SUCCESS(rc)) 1475 rc = RTSemEventSignal(pCtx->WaitEvent);1701 rc = videoRecThreadNotify(pCtx); 1476 1702 1477 1703 return rc; … … 1511 1737 AssertReturn(puSrcData, VERR_INVALID_POINTER); 1512 1738 1739 int rc = RTCritSectEnter(&pCtx->CritSect); 1740 AssertRC(rc); 1741 1513 1742 PVIDEORECSTREAM pStream = videoRecStreamGet(pCtx, uScreen); 1514 1743 if (!pStream) 1744 { 1745 rc = RTCritSectLeave(&pCtx->CritSect); 1746 AssertRC(rc); 1747 1515 1748 return VERR_NOT_FOUND; 1749 } 1516 1750 1517 1751 videoRecStreamLock(pStream); 1518 1752 1519 int rc = VINF_SUCCESS;1753 PVIDEORECVIDEOFRAME pFrame = NULL; 1520 1754 1521 1755 do … … 1584 1818 h = pStream->Video.uHeight - destY; 1585 1819 1586 #ifdef VBOX_VIDEOREC_WITH_QUEUE 1587 # error "Implement me!" 1588 #else 1589 PVIDEORECVIDEOFRAME pFrame = &pStream->Video.Frame; 1590 #endif 1820 pFrame = (PVIDEORECVIDEOFRAME)RTMemAllocZ(sizeof(VIDEORECVIDEOFRAME)); 1821 AssertBreakStmt(pFrame, rc = VERR_NO_MEMORY); 1822 1591 1823 /* Calculate bytes per pixel and set pixel format. */ 1592 1824 const unsigned uBytesPerPixel = uBPP / 8; … … 1612 1844 AssertMsgFailed(("Unknown pixel format (%RU32)\n", uPixelFormat)); 1613 1845 1614 #ifndef VBOX_VIDEOREC_WITH_QUEUE 1615 /* If we don't use a queue then we have to compare the dimensions 1616 * of the current frame with the previous frame: 1617 * 1618 * If it's smaller than before then clear the entire buffer to prevent artifacts 1619 * from the previous frame. */ 1620 if ( uSrcWidth < pFrame->uWidth 1621 || uSrcHeight < pFrame->uHeight) 1622 { 1623 /** @todo r=andy Only clear dirty areas. */ 1846 const size_t cbRGBBuf = pStream->Video.uWidth 1847 * pStream->Video.uHeight 1848 * uBytesPerPixel; 1849 AssertBreakStmt(cbRGBBuf, rc = VERR_INVALID_PARAMETER); 1850 1851 pFrame->pu8RGBBuf = (uint8_t *)RTMemAlloc(cbRGBBuf); 1852 AssertBreakStmt(pFrame->pu8RGBBuf, rc = VERR_NO_MEMORY); 1853 pFrame->cbRGBBuf = cbRGBBuf; 1854 pFrame->uWidth = uSrcWidth; 1855 pFrame->uHeight = uSrcHeight; 1856 1857 /* If the current video frame is smaller than video resolution we're going to encode, 1858 * clear the frame beforehand to prevent artifacts. */ 1859 if ( uSrcWidth < pStream->Video.uWidth 1860 || uSrcHeight < pStream->Video.uHeight) 1861 { 1624 1862 RT_BZERO(pFrame->pu8RGBBuf, pFrame->cbRGBBuf); 1625 1863 } 1626 #endif 1864 1627 1865 /* Calculate start offset in source and destination buffers. */ 1628 1866 uint32_t offSrc = y * uBytesPerLine + x * uBytesPerPixel; … … 1680 1918 RTFileClose(fh); 1681 1919 #endif 1682 pFrame->uTimeStampMs = uTimeStampMs;1683 pFrame->uWidth = uSrcWidth;1684 pFrame->uHeight = uSrcHeight;1685 1686 pStream->Video.fHasVideoData = true;1687 1920 1688 1921 } while (0); 1689 1922 1923 if (rc == VINF_SUCCESS) /* Note: Also could be VINF_TRY_AGAIN. */ 1924 { 1925 PVIDEORECBLOCK pBlock = (PVIDEORECBLOCK)RTMemAlloc(sizeof(VIDEORECBLOCK)); 1926 if (pBlock) 1927 { 1928 AssertPtr(pFrame); 1929 1930 pBlock->enmType = VIDEORECBLOCKTYPE_VIDEO; 1931 pBlock->pvData = pFrame; 1932 pBlock->cbData = sizeof(VIDEORECVIDEOFRAME) + pFrame->cbRGBBuf; 1933 1934 try 1935 { 1936 VideoRecBlocks *pVideoRecBlocks = new VideoRecBlocks(); 1937 pVideoRecBlocks->List.push_back(pBlock); 1938 1939 Assert(pStream->Blocks.Map.find(uTimeStampMs) == pStream->Blocks.Map.end()); 1940 pStream->Blocks.Map.insert(std::make_pair(uTimeStampMs, pVideoRecBlocks)); 1941 } 1942 catch (const std::exception &ex) 1943 { 1944 RT_NOREF(ex); 1945 1946 RTMemFree(pBlock); 1947 rc = VERR_NO_MEMORY; 1948 } 1949 } 1950 else 1951 rc = VERR_NO_MEMORY; 1952 } 1953 1954 if (RT_FAILURE(rc)) 1955 videoRecVideoFrameFree(pFrame); 1956 1690 1957 videoRecStreamUnlock(pStream); 1958 1959 int rc2 = RTCritSectLeave(&pCtx->CritSect); 1960 AssertRC(rc2); 1691 1961 1692 1962 if ( RT_SUCCESS(rc) 1693 1963 && rc != VINF_TRY_AGAIN) /* Only signal the thread if operation was successful. */ 1694 1964 { 1695 int rc2 = RTSemEventSignal(pCtx->WaitEvent); 1696 AssertRC(rc2); 1965 videoRecThreadNotify(pCtx); 1697 1966 } 1698 1967 1699 1968 return rc; 1700 1969 } 1970 1971 /** 1972 * Frees a video recording video frame. 1973 * 1974 * @returns IPRT status code. 1975 * @param pFrame Pointer to video frame to free. The pointer will be invalid after return. 1976 */ 1977 static void videoRecVideoFrameFree(PVIDEORECVIDEOFRAME pFrame) 1978 { 1979 if (!pFrame) 1980 return; 1981 1982 if (pFrame->pu8RGBBuf) 1983 { 1984 Assert(pFrame->cbRGBBuf); 1985 RTMemFree(pFrame->pu8RGBBuf); 1986 } 1987 RTMemFree(pFrame); 1988 } 1989
Note:
See TracChangeset
for help on using the changeset viewer.