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
21DEFINE_GUID(CLSID_SINKFILTER, 0x88cdbbdc, 0xa73b, 0x4afa, 0xac, 0xbf, 0x15, 0xd5,
22            0xe2, 0xce, 0x12, 0xc3);
23
24namespace webrtc
25{
26namespace videocapturemodule
27{
28
29typedef 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
37CaptureInputPin::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
51CaptureInputPin::~CaptureInputPin()
52{
53}
54
55HRESULT
56CaptureInputPin::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
163HRESULT
164CaptureInputPin::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
317HRESULT
318CaptureInputPin::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
378HRESULT CaptureInputPin::SetMatchingMediaType(
379                                    const VideoCaptureCapability& capability)
380{
381
382    _requestedCapability = capability;
383    _resultingCapability = VideoCaptureCapability();
384    return S_OK;
385}
386//  ----------------------------------------------------------------------------
387CaptureSinkFilter::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
411CaptureSinkFilter::~CaptureSinkFilter()
412{
413    delete m_pInput;
414}
415
416int CaptureSinkFilter::GetPinCount()
417{
418    return 1;
419}
420
421CBasePin *
422CaptureSinkFilter::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
438STDMETHODIMP 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
464STDMETHODIMP 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
481void CaptureSinkFilter::SetFilterGraph(IGraphBuilder* graph)
482{
483    LockFilter();
484    m_pGraph = graph;
485    UnlockFilter();
486}
487
488void 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
506STDMETHODIMP 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
525STDMETHODIMP CaptureSinkFilter::GetClassID( OUT CLSID * pCLSID )
526{
527    (* pCLSID) = CLSID_SINKFILTER;
528    return S_OK;
529}
530
531}  // namespace videocapturemodule
532}  // namespace webrtc
533