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