DSPushSourceDesktop.cxx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. #if !defined(_WIN32_WCE)
  2. //------------------------------------------------------------------------------
  3. // File: PushSourceDesktop.cpp
  4. //
  5. // Desc: DirectShow sample code - In-memory push mode source filter
  6. // Provides an image of the user's desktop as a continuously updating stream.
  7. //
  8. // Copyright (c) Microsoft Corporation. All rights reserved.
  9. //------------------------------------------------------------------------------
  10. #include <streams.h>
  11. #include "DSPushSource.h"
  12. #include "DSDibHelper.h"
  13. #include "DSUtils.h"
  14. /**********************************************
  15. *
  16. * CPushPinDesktop Class
  17. *
  18. *
  19. **********************************************/
  20. CPushPinDesktop::CPushPinDesktop(HRESULT *phr, CSource *pFilter)
  21. : CSourceStream(NAME("Push Source Desktop"), phr, pFilter, L"Out"),
  22. m_FramesWritten(0),
  23. m_bZeroMemory(0),
  24. m_iFrameNumber(0),
  25. m_rtFrameLength(FPS_5), // Capture and display desktop 5 times per second
  26. m_nCurrentBitDepth(24),
  27. m_hSrcHwnd(NULL)
  28. {
  29. // The main point of this sample is to demonstrate how to take a DIB
  30. // in host memory and insert it into a video stream.
  31. // To keep this sample as simple as possible, we just read the desktop image
  32. // from a file and copy it into every frame that we send downstream.
  33. //
  34. // In the filter graph, we connect this filter to the AVI Mux, which creates
  35. // the AVI file with the video frames we pass to it. In this case,
  36. // the end result is a screen capture video (GDI images only, with no
  37. // support for overlay surfaces).
  38. // Get the device context of the main display
  39. HDC hDC;
  40. hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);
  41. // Get the dimensions of the main desktop window
  42. m_rScreen.left = m_rScreen.top = 0;
  43. m_rScreen.right = GetDeviceCaps(hDC, HORZRES);
  44. m_rScreen.bottom = GetDeviceCaps(hDC, VERTRES);
  45. // Save dimensions for later use in FillBuffer()
  46. m_iImageWidth = m_rScreen.right - m_rScreen.left;
  47. m_iImageHeight = m_rScreen.bottom - m_rScreen.top;
  48. // Release the device context
  49. DeleteDC(hDC);
  50. }
  51. CPushPinDesktop::~CPushPinDesktop()
  52. {
  53. DbgLog((LOG_TRACE, 3, TEXT("Frames written %d"), m_iFrameNumber));
  54. }
  55. //
  56. // GetMediaType
  57. //
  58. // Prefer 5 formats - 8, 16 (*2), 24 or 32 bits per pixel
  59. //
  60. // Prefered types should be ordered by quality, with zero as highest quality.
  61. // Therefore, iPosition =
  62. // 0 Return a 32bit mediatype
  63. // 1 Return a 24bit mediatype
  64. // 2 Return 16bit RGB565
  65. // 3 Return a 16bit mediatype (rgb555)
  66. // 4 Return 8 bit palettised format
  67. // >4 Invalid
  68. //
  69. HRESULT CPushPinDesktop::GetMediaType(int iPosition, CMediaType *pmt)
  70. {
  71. CheckPointer(pmt,E_POINTER);
  72. CAutoLock cAutoLock(m_pFilter->pStateLock());
  73. if(iPosition < 0) {
  74. return E_INVALIDARG;
  75. }
  76. // Have we run off the end of types?
  77. if(iPosition > 4) {
  78. return VFW_S_NO_MORE_ITEMS;
  79. }
  80. VIDEOINFO *pvi = (VIDEOINFO *) pmt->AllocFormatBuffer(sizeof(VIDEOINFO));
  81. if(NULL == pvi) {
  82. return(E_OUTOFMEMORY);
  83. }
  84. // Initialize the VideoInfo structure before configuring its members
  85. ZeroMemory(pvi, sizeof(VIDEOINFO));
  86. switch(iPosition) {
  87. case 0: {
  88. // Return our highest quality 32bit format
  89. // Since we use RGB888 (the default for 32 bit), there is
  90. // no reason to use BI_BITFIELDS to specify the RGB
  91. // masks. Also, not everything supports BI_BITFIELDS
  92. pvi->bmiHeader.biCompression = BI_RGB;
  93. pvi->bmiHeader.biBitCount = 32;
  94. break;
  95. }
  96. case 1: {
  97. // Return our 24bit format
  98. pvi->bmiHeader.biCompression = BI_RGB;
  99. pvi->bmiHeader.biBitCount = 24;
  100. break;
  101. }
  102. case 2: {
  103. // 16 bit per pixel RGB565
  104. // Place the RGB masks as the first 3 doublewords in the palette area
  105. for(int i = 0; i < 3; i++) {
  106. pvi->TrueColorInfo.dwBitMasks[i] = bits565[i];
  107. }
  108. pvi->bmiHeader.biCompression = BI_BITFIELDS;
  109. pvi->bmiHeader.biBitCount = 16;
  110. break;
  111. }
  112. case 3: {
  113. // 16 bits per pixel RGB555
  114. // Place the RGB masks as the first 3 doublewords in the palette area
  115. for(int i = 0; i < 3; i++) {
  116. pvi->TrueColorInfo.dwBitMasks[i] = bits555[i];
  117. }
  118. pvi->bmiHeader.biCompression = BI_BITFIELDS;
  119. pvi->bmiHeader.biBitCount = 16;
  120. break;
  121. }
  122. case 4: {
  123. // 8 bit palettised
  124. pvi->bmiHeader.biCompression = BI_RGB;
  125. pvi->bmiHeader.biBitCount = 8;
  126. pvi->bmiHeader.biClrUsed = iPALETTE_COLORS;
  127. break;
  128. }
  129. }
  130. // Adjust the parameters common to all formats
  131. pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  132. pvi->bmiHeader.biWidth = m_iImageWidth;
  133. pvi->bmiHeader.biHeight = m_iImageHeight;
  134. pvi->bmiHeader.biPlanes = 1;
  135. pvi->bmiHeader.biSizeImage = GetBitmapSize(&pvi->bmiHeader);
  136. pvi->bmiHeader.biClrImportant = 0;
  137. SetRectEmpty(&(pvi->rcSource)); // we want the whole image area rendered.
  138. SetRectEmpty(&(pvi->rcTarget)); // no particular destination rectangle
  139. pmt->SetType(&MEDIATYPE_Video);
  140. pmt->SetFormatType(&FORMAT_VideoInfo);
  141. pmt->SetTemporalCompression(FALSE);
  142. // Work out the GUID for the subtype from the header info.
  143. const GUID SubTypeGUID = GetBitmapSubtype(&pvi->bmiHeader);
  144. pmt->SetSubtype(&SubTypeGUID);
  145. pmt->SetSampleSize(pvi->bmiHeader.biSizeImage);
  146. return NOERROR;
  147. } // GetMediaType
  148. //
  149. // CheckMediaType
  150. //
  151. // We will accept 8, 16, 24 or 32 bit video formats, in any
  152. // image size that gives room to bounce.
  153. // Returns E_INVALIDARG if the mediatype is not acceptable
  154. //
  155. HRESULT CPushPinDesktop::CheckMediaType(const CMediaType *pMediaType)
  156. {
  157. CheckPointer(pMediaType,E_POINTER);
  158. if((*(pMediaType->Type()) != MEDIATYPE_Video) || // we only output video
  159. !(pMediaType->IsFixedSize())) { // in fixed size samples
  160. return E_INVALIDARG;
  161. }
  162. // Check for the subtypes we support
  163. const GUID *SubType = pMediaType->Subtype();
  164. if (SubType == NULL) {
  165. return E_INVALIDARG;
  166. }
  167. if( (*SubType != MEDIASUBTYPE_RGB24)
  168. #if 0
  169. && (*SubType != MEDIASUBTYPE_RGB565)
  170. && (*SubType != MEDIASUBTYPE_RGB555)
  171. && (*SubType != MEDIASUBTYPE_RGB32)
  172. && (*SubType != MEDIASUBTYPE_RGB8)
  173. #endif
  174. ) {
  175. return E_INVALIDARG;
  176. }
  177. // Get the format area of the media type
  178. VIDEOINFO *pvi = (VIDEOINFO *) pMediaType->Format();
  179. if(pvi == NULL) {
  180. return E_INVALIDARG;
  181. }
  182. // Check if the image width & height have changed
  183. if( pvi->bmiHeader.biWidth != m_iImageWidth ||
  184. abs(pvi->bmiHeader.biHeight) != m_iImageHeight) {
  185. // If the image width/height is changed, fail CheckMediaType() to force
  186. // the renderer to resize the image.
  187. return E_INVALIDARG;
  188. }
  189. // Don't accept formats with negative height, which would cause the desktop
  190. // image to be displayed upside down.
  191. if (pvi->bmiHeader.biHeight < 0) {
  192. return E_INVALIDARG;
  193. }
  194. return S_OK; // This format is acceptable.
  195. } // CheckMediaType
  196. //
  197. // DecideBufferSize
  198. //
  199. // This will always be called after the format has been sucessfully
  200. // negotiated. So we have a look at m_mt to see what size image we agreed.
  201. // Then we can ask for buffers of the correct size to contain them.
  202. //
  203. HRESULT CPushPinDesktop::DecideBufferSize(IMemAllocator *pAlloc,
  204. ALLOCATOR_PROPERTIES *pProperties)
  205. {
  206. CheckPointer(pAlloc,E_POINTER);
  207. CheckPointer(pProperties,E_POINTER);
  208. CAutoLock cAutoLock(m_pFilter->pStateLock());
  209. HRESULT hr = NOERROR;
  210. VIDEOINFO *pvi = (VIDEOINFO *) m_mt.Format();
  211. pProperties->cBuffers = 1;
  212. pProperties->cbBuffer = pvi->bmiHeader.biSizeImage;
  213. ASSERT(pProperties->cbBuffer);
  214. // Ask the allocator to reserve us some sample memory. NOTE: the function
  215. // can succeed (return NOERROR) but still not have allocated the
  216. // memory that we requested, so we must check we got whatever we wanted.
  217. ALLOCATOR_PROPERTIES Actual;
  218. hr = pAlloc->SetProperties(pProperties,&Actual);
  219. if(FAILED(hr)) {
  220. return hr;
  221. }
  222. // Is this allocator unsuitable?
  223. if(Actual.cbBuffer < pProperties->cbBuffer) {
  224. return E_FAIL;
  225. }
  226. // Make sure that we have only 1 buffer (we erase the ball in the
  227. // old buffer to save having to zero a 200k+ buffer every time
  228. // we draw a frame)
  229. ASSERT(Actual.cBuffers == 1);
  230. return NOERROR;
  231. } // DecideBufferSize
  232. //
  233. // SetMediaType
  234. //
  235. // Called when a media type is agreed between filters
  236. //
  237. HRESULT CPushPinDesktop::SetMediaType(const CMediaType *pMediaType)
  238. {
  239. CAutoLock cAutoLock(m_pFilter->pStateLock());
  240. // Pass the call up to my base class
  241. HRESULT hr = CSourceStream::SetMediaType(pMediaType);
  242. if(SUCCEEDED(hr)) {
  243. VIDEOINFO * pvi = (VIDEOINFO *) m_mt.Format();
  244. if (pvi == NULL) {
  245. return E_UNEXPECTED;
  246. }
  247. switch(pvi->bmiHeader.biBitCount) {
  248. case 8: // 8-bit palettized
  249. case 16: // RGB565, RGB555
  250. case 24: // RGB24
  251. case 32: // RGB32
  252. // Save the current media type and bit depth
  253. m_MediaType = *pMediaType;
  254. m_nCurrentBitDepth = pvi->bmiHeader.biBitCount;
  255. hr = S_OK;
  256. break;
  257. default:
  258. // We should never agree any other media types
  259. ASSERT(FALSE);
  260. hr = E_INVALIDARG;
  261. break;
  262. }
  263. }
  264. return hr;
  265. } // SetMediaType
  266. // This is where we insert the DIB bits into the video stream.
  267. // FillBuffer is called once for every sample in the stream.
  268. HRESULT CPushPinDesktop::FillBuffer(IMediaSample *pSample)
  269. {
  270. BYTE *pData;
  271. long cbData;
  272. CheckPointer(pSample, E_POINTER);
  273. CAutoLock cAutoLockShared(&m_cSharedState);
  274. // Access the sample's data buffer
  275. pSample->GetPointer(&pData);
  276. cbData = pSample->GetSize();
  277. // Check that we're still using video
  278. ASSERT(m_mt.formattype == FORMAT_VideoInfo);
  279. VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)m_mt.pbFormat;
  280. // Copy the DIB bits over into our filter's output buffer.
  281. // Since sample size may be larger than the image size, bound the copy size.
  282. int nSize = min(pVih->bmiHeader.biSizeImage, (DWORD) cbData);
  283. HDIB hDib = CopyScreenToBitmap(&m_rScreen, pData, (BITMAPINFO *) &(pVih->bmiHeader));
  284. if (hDib) {
  285. DeleteObject(hDib);
  286. }
  287. // Set the timestamps that will govern playback frame rate.
  288. // If this file is getting written out as an AVI,
  289. // then you'll also need to configure the AVI Mux filter to
  290. // set the Average Time Per Frame for the AVI Header.
  291. // The current time is the sample's start.
  292. REFERENCE_TIME rtStart = m_iFrameNumber * m_rtFrameLength;
  293. REFERENCE_TIME rtStop = rtStart + m_rtFrameLength;
  294. pSample->SetTime(&rtStart, &rtStop);
  295. m_iFrameNumber++;
  296. // Set TRUE on every sample for uncompressed frames
  297. pSample->SetSyncPoint(TRUE);
  298. return S_OK;
  299. }
  300. /**********************************************
  301. *
  302. * CPushSourceDesktop Class
  303. *
  304. **********************************************/
  305. CPushSourceDesktop::CPushSourceDesktop(IUnknown *pUnk, HRESULT *phr)
  306. : CSource(NAME("PushSourceDesktop"), pUnk, CLSID_PushSourceDesktop)
  307. {
  308. // The pin magically adds itself to our pin array.
  309. m_pPin = new CPushPinDesktop(phr, this);
  310. if (phr) {
  311. if (m_pPin == NULL) {
  312. *phr = E_OUTOFMEMORY;
  313. }
  314. else {
  315. *phr = S_OK;
  316. }
  317. }
  318. }
  319. CPushSourceDesktop::~CPushSourceDesktop()
  320. {
  321. if (m_pPin) {
  322. delete m_pPin;
  323. m_pPin = NULL;
  324. }
  325. }
  326. CUnknown * WINAPI CPushSourceDesktop::CreateInstance(IUnknown *pUnk, HRESULT *phr)
  327. {
  328. CPushSourceDesktop *pNewFilter = new CPushSourceDesktop(pUnk, phr );
  329. if (phr) {
  330. if (pNewFilter == NULL) {
  331. *phr = E_OUTOFMEMORY;
  332. }
  333. else {
  334. *phr = S_OK;
  335. }
  336. }
  337. return pNewFilter;
  338. }
  339. HRESULT CPushSourceDesktop::SetSrcHwnd(HWND hWnd)
  340. {
  341. if (m_pPin) {
  342. return m_pPin->SetSrcHwnd(hWnd);
  343. }
  344. return E_FAIL;
  345. }
  346. #endif /* _WIN32_WCE */