Home | History | Annotate | Download | only in windows
      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