1 /* 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/modules/video_capture/windows/sink_filter_ds.h" 12 13 #include "webrtc/modules/video_capture/windows/help_functions_ds.h" 14 #include "webrtc/system_wrappers/interface/trace.h" 15 16 #include <Dvdmedia.h> // VIDEOINFOHEADER2 17 #include <initguid.h> 18 19 #define DELETE_RESET(p) { delete (p) ; (p) = NULL ;} 20 21 DEFINE_GUID(CLSID_SINKFILTER, 0x88cdbbdc, 0xa73b, 0x4afa, 0xac, 0xbf, 0x15, 0xd5, 22 0xe2, 0xce, 0x12, 0xc3); 23 24 namespace webrtc 25 { 26 namespace videocapturemodule 27 { 28 29 typedef struct tagTHREADNAME_INFO 30 { 31 DWORD dwType; // must be 0x1000 32 LPCSTR szName; // pointer to name (in user addr space) 33 DWORD dwThreadID; // thread ID (-1=caller thread) 34 DWORD dwFlags; // reserved for future use, must be zero 35 } THREADNAME_INFO; 36 37 CaptureInputPin::CaptureInputPin (int32_t moduleId, 38 IN TCHAR * szName, 39 IN CaptureSinkFilter* pFilter, 40 IN CCritSec * pLock, 41 OUT HRESULT * pHr, 42 IN LPCWSTR pszName) 43 : CBaseInputPin (szName, pFilter, pLock, pHr, pszName), 44 _requestedCapability(), 45 _resultingCapability() 46 { 47 _moduleId=moduleId; 48 _threadHandle = NULL; 49 } 50 51 CaptureInputPin::~CaptureInputPin() 52 { 53 } 54 55 HRESULT 56 CaptureInputPin::GetMediaType (IN int iPosition, OUT CMediaType * pmt) 57 { 58 // reset the thread handle 59 _threadHandle = NULL; 60 61 if(iPosition < 0) 62 return E_INVALIDARG; 63 64 VIDEOINFOHEADER* pvi = (VIDEOINFOHEADER*) pmt->AllocFormatBuffer( 65 sizeof(VIDEOINFOHEADER)); 66 if(NULL == pvi) 67 { 68 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, 69 "CheckMediaType VIDEOINFOHEADER is NULL. Returning...Line:%d\n", __LINE__); 70 return(E_OUTOFMEMORY); 71 } 72 73 ZeroMemory(pvi, sizeof(VIDEOINFOHEADER)); 74 pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 75 pvi->bmiHeader.biPlanes = 1; 76 pvi->bmiHeader.biClrImportant = 0; 77 pvi->bmiHeader.biClrUsed = 0; 78 if (_requestedCapability.maxFPS != 0) { 79 pvi->AvgTimePerFrame = 10000000/_requestedCapability.maxFPS; 80 } 81 82 SetRectEmpty(&(pvi->rcSource)); // we want the whole image area rendered. 83 SetRectEmpty(&(pvi->rcTarget)); // no particular destination rectangle 84 85 pmt->SetType(&MEDIATYPE_Video); 86 pmt->SetFormatType(&FORMAT_VideoInfo); 87 pmt->SetTemporalCompression(FALSE); 88 89 int32_t positionOffset=1; 90 if(_requestedCapability.codecType!=kVideoCodecUnknown) 91 { 92 positionOffset=0; 93 } 94 95 switch (iPosition+positionOffset) 96 { 97 case 0: 98 { 99 pvi->bmiHeader.biCompression = MAKEFOURCC('I','4','2','0'); 100 pvi->bmiHeader.biBitCount = 12; //bit per pixel 101 pvi->bmiHeader.biWidth = _requestedCapability.width; 102 pvi->bmiHeader.biHeight = _requestedCapability.height; 103 pvi->bmiHeader.biSizeImage = 3*_requestedCapability.height 104 *_requestedCapability.width/2; 105 pmt->SetSubtype(&MEDIASUBTYPE_I420); 106 } 107 break; 108 case 1: 109 { 110 pvi->bmiHeader.biCompression = MAKEFOURCC('Y','U','Y','2');; 111 pvi->bmiHeader.biBitCount = 16; //bit per pixel 112 pvi->bmiHeader.biWidth = _requestedCapability.width; 113 pvi->bmiHeader.biHeight = _requestedCapability.height; 114 pvi->bmiHeader.biSizeImage = 2*_requestedCapability.width 115 *_requestedCapability.height; 116 pmt->SetSubtype(&MEDIASUBTYPE_YUY2); 117 } 118 break; 119 case 2: 120 { 121 pvi->bmiHeader.biCompression = BI_RGB; 122 pvi->bmiHeader.biBitCount = 24; //bit per pixel 123 pvi->bmiHeader.biWidth = _requestedCapability.width; 124 pvi->bmiHeader.biHeight = _requestedCapability.height; 125 pvi->bmiHeader.biSizeImage = 3*_requestedCapability.height 126 *_requestedCapability.width; 127 pmt->SetSubtype(&MEDIASUBTYPE_RGB24); 128 } 129 break; 130 case 3: 131 { 132 pvi->bmiHeader.biCompression = MAKEFOURCC('U','Y','V','Y'); 133 pvi->bmiHeader.biBitCount = 16; //bit per pixel 134 pvi->bmiHeader.biWidth = _requestedCapability.width; 135 pvi->bmiHeader.biHeight = _requestedCapability.height; 136 pvi->bmiHeader.biSizeImage = 2*_requestedCapability.height 137 *_requestedCapability.width; 138 pmt->SetSubtype(&MEDIASUBTYPE_UYVY); 139 } 140 break; 141 case 4: 142 { 143 pvi->bmiHeader.biCompression = MAKEFOURCC('M','J','P','G'); 144 pvi->bmiHeader.biBitCount = 12; //bit per pixel 145 pvi->bmiHeader.biWidth = _requestedCapability.width; 146 pvi->bmiHeader.biHeight = _requestedCapability.height; 147 pvi->bmiHeader.biSizeImage = 3*_requestedCapability.height 148 *_requestedCapability.width/2; 149 pmt->SetSubtype(&MEDIASUBTYPE_MJPG); 150 } 151 break; 152 default : 153 return VFW_S_NO_MORE_ITEMS; 154 } 155 pmt->SetSampleSize(pvi->bmiHeader.biSizeImage); 156 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, 157 "GetMediaType position %d, width %d, height %d, biCompression 0x%x", 158 iPosition, _requestedCapability.width, 159 _requestedCapability.height,pvi->bmiHeader.biCompression); 160 return NOERROR; 161 } 162 163 HRESULT 164 CaptureInputPin::CheckMediaType ( IN const CMediaType * pMediaType) 165 { 166 // reset the thread handle 167 _threadHandle = NULL; 168 169 const GUID *type = pMediaType->Type(); 170 if (*type != MEDIATYPE_Video) 171 return E_INVALIDARG; 172 173 const GUID *formatType = pMediaType->FormatType(); 174 175 // Check for the subtypes we support 176 const GUID *SubType = pMediaType->Subtype(); 177 if (SubType == NULL) 178 { 179 return E_INVALIDARG; 180 } 181 182 if(*formatType == FORMAT_VideoInfo) 183 { 184 VIDEOINFOHEADER *pvi = (VIDEOINFOHEADER *) pMediaType->Format(); 185 if(pvi == NULL) 186 { 187 return E_INVALIDARG; 188 } 189 190 // Store the incoming width and height 191 _resultingCapability.width = pvi->bmiHeader.biWidth; 192 193 // Store the incoming height, 194 // for RGB24 we assume the frame to be upside down 195 if(*SubType == MEDIASUBTYPE_RGB24 196 && pvi->bmiHeader.biHeight > 0) 197 { 198 _resultingCapability.height = -(pvi->bmiHeader.biHeight); 199 } 200 else 201 { 202 _resultingCapability.height = abs(pvi->bmiHeader.biHeight); 203 } 204 205 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, 206 "CheckMediaType width:%d height:%d Compression:0x%x\n", 207 pvi->bmiHeader.biWidth,pvi->bmiHeader.biHeight, 208 pvi->bmiHeader.biCompression); 209 210 if(*SubType == MEDIASUBTYPE_MJPG 211 && pvi->bmiHeader.biCompression == MAKEFOURCC('M','J','P','G')) 212 { 213 _resultingCapability.rawType = kVideoMJPEG; 214 return S_OK; // This format is acceptable. 215 } 216 if(*SubType == MEDIASUBTYPE_I420 217 && pvi->bmiHeader.biCompression == MAKEFOURCC('I','4','2','0')) 218 { 219 _resultingCapability.rawType = kVideoI420; 220 return S_OK; // This format is acceptable. 221 } 222 if(*SubType == MEDIASUBTYPE_YUY2 223 && pvi->bmiHeader.biCompression == MAKEFOURCC('Y','U','Y','2')) 224 { 225 _resultingCapability.rawType = kVideoYUY2; 226 ::Sleep(60); // workaround for bad driver 227 return S_OK; // This format is acceptable. 228 } 229 if(*SubType == MEDIASUBTYPE_UYVY 230 && pvi->bmiHeader.biCompression == MAKEFOURCC('U','Y','V','Y')) 231 { 232 _resultingCapability.rawType = kVideoUYVY; 233 return S_OK; // This format is acceptable. 234 } 235 236 if(*SubType == MEDIASUBTYPE_HDYC) 237 { 238 _resultingCapability.rawType = kVideoUYVY; 239 return S_OK; // This format is acceptable. 240 } 241 if(*SubType == MEDIASUBTYPE_RGB24 242 && pvi->bmiHeader.biCompression == BI_RGB) 243 { 244 _resultingCapability.rawType = kVideoRGB24; 245 return S_OK; // This format is acceptable. 246 } 247 } 248 if(*formatType == FORMAT_VideoInfo2) 249 { 250 // VIDEOINFOHEADER2 that has dwInterlaceFlags 251 VIDEOINFOHEADER2 *pvi = (VIDEOINFOHEADER2 *) pMediaType->Format(); 252 253 if(pvi == NULL) 254 { 255 return E_INVALIDARG; 256 } 257 258 WEBRTC_TRACE(webrtc::kTraceInfo, webrtc::kTraceVideoCapture, _moduleId, 259 "CheckMediaType width:%d height:%d Compression:0x%x\n", 260 pvi->bmiHeader.biWidth,pvi->bmiHeader.biHeight, 261 pvi->bmiHeader.biCompression); 262 263 _resultingCapability.width = pvi->bmiHeader.biWidth; 264 265 // Store the incoming height, 266 // for RGB24 we assume the frame to be upside down 267 if(*SubType == MEDIASUBTYPE_RGB24 268 && pvi->bmiHeader.biHeight > 0) 269 { 270 _resultingCapability.height = -(pvi->bmiHeader.biHeight); 271 } 272 else 273 { 274 _resultingCapability.height = abs(pvi->bmiHeader.biHeight); 275 } 276 277 if(*SubType == MEDIASUBTYPE_MJPG 278 && pvi->bmiHeader.biCompression == MAKEFOURCC('M','J','P','G')) 279 { 280 _resultingCapability.rawType = kVideoMJPEG; 281 return S_OK; // This format is acceptable. 282 } 283 if(*SubType == MEDIASUBTYPE_I420 284 && pvi->bmiHeader.biCompression == MAKEFOURCC('I','4','2','0')) 285 { 286 _resultingCapability.rawType = kVideoI420; 287 return S_OK; // This format is acceptable. 288 } 289 if(*SubType == MEDIASUBTYPE_YUY2 290 && pvi->bmiHeader.biCompression == MAKEFOURCC('Y','U','Y','2')) 291 { 292 _resultingCapability.rawType = kVideoYUY2; 293 return S_OK; // This format is acceptable. 294 } 295 if(*SubType == MEDIASUBTYPE_UYVY 296 && pvi->bmiHeader.biCompression == MAKEFOURCC('U','Y','V','Y')) 297 { 298 _resultingCapability.rawType = kVideoUYVY; 299 return S_OK; // This format is acceptable. 300 } 301 302 if(*SubType == MEDIASUBTYPE_HDYC) 303 { 304 _resultingCapability.rawType = kVideoUYVY; 305 return S_OK; // This format is acceptable. 306 } 307 if(*SubType == MEDIASUBTYPE_RGB24 308 && pvi->bmiHeader.biCompression == BI_RGB) 309 { 310 _resultingCapability.rawType = kVideoRGB24; 311 return S_OK; // This format is acceptable. 312 } 313 } 314 return E_INVALIDARG; 315 } 316 317 HRESULT 318 CaptureInputPin::Receive ( IN IMediaSample * pIMediaSample ) 319 { 320 HRESULT hr = S_OK; 321 322 ASSERT (m_pFilter); 323 ASSERT (pIMediaSample); 324 325 // get the thread handle of the delivering thread inc its priority 326 if( _threadHandle == NULL) 327 { 328 HANDLE handle= GetCurrentThread(); 329 SetThreadPriority(handle, THREAD_PRIORITY_HIGHEST); 330 _threadHandle = handle; 331 // See http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.71).aspx for details on the code 332 // in this function. Name od article is "Setting a Thread Name (Unmanaged)". 333 334 THREADNAME_INFO info; 335 info.dwType = 0x1000; 336 info.szName = "capture_thread"; 337 info.dwThreadID = (DWORD)-1; 338 info.dwFlags = 0; 339 340 __try 341 { 342 RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), 343 (DWORD_PTR*)&info ); 344 } 345 __except (EXCEPTION_CONTINUE_EXECUTION) 346 { 347 } 348 349 } 350 351 reinterpret_cast <CaptureSinkFilter *>(m_pFilter)->LockReceive(); 352 hr = CBaseInputPin::Receive (pIMediaSample); 353 354 if (SUCCEEDED (hr)) 355 { 356 const int32_t length = pIMediaSample->GetActualDataLength(); 357 358 unsigned char* pBuffer = NULL; 359 if(S_OK != pIMediaSample->GetPointer(&pBuffer)) 360 { 361 reinterpret_cast <CaptureSinkFilter *>(m_pFilter)->UnlockReceive(); 362 return S_FALSE; 363 } 364 365 // NOTE: filter unlocked within Send call 366 reinterpret_cast <CaptureSinkFilter *> (m_pFilter)->ProcessCapturedFrame( 367 pBuffer,length,_resultingCapability); 368 } 369 else 370 { 371 reinterpret_cast <CaptureSinkFilter *>(m_pFilter)->UnlockReceive(); 372 } 373 374 return hr; 375 } 376 377 // called under LockReceive 378 HRESULT CaptureInputPin::SetMatchingMediaType( 379 const VideoCaptureCapability& capability) 380 { 381 382 _requestedCapability = capability; 383 _resultingCapability = VideoCaptureCapability(); 384 return S_OK; 385 } 386 // ---------------------------------------------------------------------------- 387 CaptureSinkFilter::CaptureSinkFilter (IN TCHAR * tszName, 388 IN LPUNKNOWN punk, 389 OUT HRESULT * phr, 390 VideoCaptureExternal& captureObserver, 391 int32_t moduleId) 392 : CBaseFilter(tszName,punk,& m_crtFilter,CLSID_SINKFILTER), 393 m_pInput(NULL), 394 _captureObserver(captureObserver), 395 _moduleId(moduleId) 396 { 397 (* phr) = S_OK; 398 m_pInput = new CaptureInputPin(moduleId,NAME ("VideoCaptureInputPin"), 399 this, 400 & m_crtFilter, 401 phr, L"VideoCapture"); 402 if (m_pInput == NULL || FAILED (* phr)) 403 { 404 (* phr) = FAILED (* phr) ? (* phr) : E_OUTOFMEMORY; 405 goto cleanup; 406 } 407 cleanup : 408 return; 409 } 410 411 CaptureSinkFilter::~CaptureSinkFilter() 412 { 413 delete m_pInput; 414 } 415 416 int CaptureSinkFilter::GetPinCount() 417 { 418 return 1; 419 } 420 421 CBasePin * 422 CaptureSinkFilter::GetPin(IN int Index) 423 { 424 CBasePin * pPin; 425 LockFilter (); 426 if (Index == 0) 427 { 428 pPin = m_pInput; 429 } 430 else 431 { 432 pPin = NULL; 433 } 434 UnlockFilter (); 435 return pPin; 436 } 437 438 STDMETHODIMP CaptureSinkFilter::Pause() 439 { 440 LockReceive(); 441 LockFilter(); 442 if (m_State == State_Stopped) 443 { 444 // change the state, THEN activate the input pin 445 m_State = State_Paused; 446 if (m_pInput && m_pInput->IsConnected()) 447 { 448 m_pInput->Active(); 449 } 450 if (m_pInput && !m_pInput->IsConnected()) 451 { 452 m_State = State_Running; 453 } 454 } 455 else if (m_State == State_Running) 456 { 457 m_State = State_Paused; 458 } 459 UnlockFilter(); 460 UnlockReceive(); 461 return S_OK; 462 } 463 464 STDMETHODIMP CaptureSinkFilter::Stop() 465 { 466 LockReceive(); 467 LockFilter(); 468 469 // set the state 470 m_State = State_Stopped; 471 472 // inactivate the pins 473 if (m_pInput) 474 m_pInput->Inactive(); 475 476 UnlockFilter(); 477 UnlockReceive(); 478 return S_OK; 479 } 480 481 void CaptureSinkFilter::SetFilterGraph(IGraphBuilder* graph) 482 { 483 LockFilter(); 484 m_pGraph = graph; 485 UnlockFilter(); 486 } 487 488 void CaptureSinkFilter::ProcessCapturedFrame(unsigned char* pBuffer, 489 int32_t length, 490 const VideoCaptureCapability& frameInfo) 491 { 492 // we have the receiver lock 493 if (m_State == State_Running) 494 { 495 _captureObserver.IncomingFrame(pBuffer, length, frameInfo); 496 497 // trying to hold it since it's only a memcpy 498 // IMPROVEMENT if this work move critsect 499 UnlockReceive(); 500 return; 501 } 502 UnlockReceive(); 503 return; 504 } 505 506 STDMETHODIMP CaptureSinkFilter::SetMatchingMediaType( 507 const VideoCaptureCapability& capability) 508 { 509 LockReceive(); 510 LockFilter(); 511 HRESULT hr; 512 if (m_pInput) 513 { 514 hr = m_pInput->SetMatchingMediaType(capability); 515 } 516 else 517 { 518 hr = E_UNEXPECTED; 519 } 520 UnlockFilter(); 521 UnlockReceive(); 522 return hr; 523 } 524 525 STDMETHODIMP CaptureSinkFilter::GetClassID( OUT CLSID * pCLSID ) 526 { 527 (* pCLSID) = CLSID_SINKFILTER; 528 return S_OK; 529 } 530 531 } // namespace videocapturemodule 532 } // namespace webrtc 533