xref: /aosp_15_r20/external/webrtc/modules/video_capture/windows/sink_filter_ds.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 "modules/video_capture/windows/sink_filter_ds.h"
12 
13 #include <dvdmedia.h>  // VIDEOINFOHEADER2
14 #include <initguid.h>
15 
16 #include <algorithm>
17 #include <list>
18 
19 #include "rtc_base/arraysize.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/logging.h"
22 #include "rtc_base/platform_thread.h"
23 #include "rtc_base/string_utils.h"
24 
25 DEFINE_GUID(CLSID_SINKFILTER,
26             0x88cdbbdc,
27             0xa73b,
28             0x4afa,
29             0xac,
30             0xbf,
31             0x15,
32             0xd5,
33             0xe2,
34             0xce,
35             0x12,
36             0xc3);
37 
38 namespace webrtc {
39 namespace videocapturemodule {
40 namespace {
41 
42 // Simple enumeration implementation that enumerates over a single pin :-/
43 class EnumPins : public IEnumPins {
44  public:
EnumPins(IPin * pin)45   EnumPins(IPin* pin) : pin_(pin) {}
46 
47  protected:
~EnumPins()48   virtual ~EnumPins() {}
49 
50  private:
STDMETHOD(QueryInterface)51   STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override {
52     if (riid == IID_IUnknown || riid == IID_IEnumPins) {
53       *ppv = static_cast<IEnumPins*>(this);
54       AddRef();
55       return S_OK;
56     }
57     return E_NOINTERFACE;
58   }
59 
STDMETHOD(Clone)60   STDMETHOD(Clone)(IEnumPins** pins) {
61     RTC_DCHECK_NOTREACHED();
62     return E_NOTIMPL;
63   }
64 
STDMETHOD(Next)65   STDMETHOD(Next)(ULONG count, IPin** pins, ULONG* fetched) {
66     RTC_DCHECK(count > 0);
67     RTC_DCHECK(pins);
68     // fetched may be NULL.
69 
70     if (pos_ > 0) {
71       if (fetched)
72         *fetched = 0;
73       return S_FALSE;
74     }
75 
76     ++pos_;
77     pins[0] = pin_.get();
78     pins[0]->AddRef();
79     if (fetched)
80       *fetched = 1;
81 
82     return count == 1 ? S_OK : S_FALSE;
83   }
84 
STDMETHOD(Skip)85   STDMETHOD(Skip)(ULONG count) {
86     RTC_DCHECK_NOTREACHED();
87     return E_NOTIMPL;
88   }
89 
STDMETHOD(Reset)90   STDMETHOD(Reset)() {
91     pos_ = 0;
92     return S_OK;
93   }
94 
95   rtc::scoped_refptr<IPin> pin_;
96   int pos_ = 0;
97 };
98 
IsMediaTypePartialMatch(const AM_MEDIA_TYPE & a,const AM_MEDIA_TYPE & b)99 bool IsMediaTypePartialMatch(const AM_MEDIA_TYPE& a, const AM_MEDIA_TYPE& b) {
100   if (b.majortype != GUID_NULL && a.majortype != b.majortype)
101     return false;
102 
103   if (b.subtype != GUID_NULL && a.subtype != b.subtype)
104     return false;
105 
106   if (b.formattype != GUID_NULL) {
107     // if the format block is specified then it must match exactly
108     if (a.formattype != b.formattype)
109       return false;
110 
111     if (a.cbFormat != b.cbFormat)
112       return false;
113 
114     if (a.cbFormat != 0 && memcmp(a.pbFormat, b.pbFormat, a.cbFormat) != 0)
115       return false;
116   }
117 
118   return true;
119 }
120 
IsMediaTypeFullySpecified(const AM_MEDIA_TYPE & type)121 bool IsMediaTypeFullySpecified(const AM_MEDIA_TYPE& type) {
122   return type.majortype != GUID_NULL && type.formattype != GUID_NULL;
123 }
124 
AllocMediaTypeFormatBuffer(AM_MEDIA_TYPE * media_type,ULONG length)125 BYTE* AllocMediaTypeFormatBuffer(AM_MEDIA_TYPE* media_type, ULONG length) {
126   RTC_DCHECK(length);
127   if (media_type->cbFormat == length)
128     return media_type->pbFormat;
129 
130   BYTE* buffer = static_cast<BYTE*>(CoTaskMemAlloc(length));
131   if (!buffer)
132     return nullptr;
133 
134   if (media_type->pbFormat) {
135     RTC_DCHECK(media_type->cbFormat);
136     CoTaskMemFree(media_type->pbFormat);
137     media_type->pbFormat = nullptr;
138   }
139 
140   media_type->cbFormat = length;
141   media_type->pbFormat = buffer;
142   return buffer;
143 }
144 
GetSampleProperties(IMediaSample * sample,AM_SAMPLE2_PROPERTIES * props)145 void GetSampleProperties(IMediaSample* sample, AM_SAMPLE2_PROPERTIES* props) {
146   rtc::scoped_refptr<IMediaSample2> sample2;
147   if (SUCCEEDED(GetComInterface(sample, &sample2))) {
148     sample2->GetProperties(sizeof(*props), reinterpret_cast<BYTE*>(props));
149     return;
150   }
151 
152   //  Get the properties the hard way.
153   props->cbData = sizeof(*props);
154   props->dwTypeSpecificFlags = 0;
155   props->dwStreamId = AM_STREAM_MEDIA;
156   props->dwSampleFlags = 0;
157 
158   if (sample->IsDiscontinuity() == S_OK)
159     props->dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
160 
161   if (sample->IsPreroll() == S_OK)
162     props->dwSampleFlags |= AM_SAMPLE_PREROLL;
163 
164   if (sample->IsSyncPoint() == S_OK)
165     props->dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
166 
167   if (SUCCEEDED(sample->GetTime(&props->tStart, &props->tStop)))
168     props->dwSampleFlags |= AM_SAMPLE_TIMEVALID | AM_SAMPLE_STOPVALID;
169 
170   if (sample->GetMediaType(&props->pMediaType) == S_OK)
171     props->dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
172 
173   sample->GetPointer(&props->pbBuffer);
174   props->lActual = sample->GetActualDataLength();
175   props->cbBuffer = sample->GetSize();
176 }
177 
178 // Returns true if the media type is supported, false otherwise.
179 // For supported types, the `capability` will be populated accordingly.
TranslateMediaTypeToVideoCaptureCapability(const AM_MEDIA_TYPE * media_type,VideoCaptureCapability * capability)180 bool TranslateMediaTypeToVideoCaptureCapability(
181     const AM_MEDIA_TYPE* media_type,
182     VideoCaptureCapability* capability) {
183   RTC_DCHECK(capability);
184   if (!media_type || media_type->majortype != MEDIATYPE_Video ||
185       !media_type->pbFormat) {
186     return false;
187   }
188 
189   const BITMAPINFOHEADER* bih = nullptr;
190   if (media_type->formattype == FORMAT_VideoInfo) {
191     bih = &reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat)->bmiHeader;
192   } else if (media_type->formattype != FORMAT_VideoInfo2) {
193     bih = &reinterpret_cast<VIDEOINFOHEADER2*>(media_type->pbFormat)->bmiHeader;
194   } else {
195     return false;
196   }
197 
198   RTC_LOG(LS_INFO) << "TranslateMediaTypeToVideoCaptureCapability width:"
199                    << bih->biWidth << " height:" << bih->biHeight
200                    << " Compression:0x" << rtc::ToHex(bih->biCompression);
201 
202   const GUID& sub_type = media_type->subtype;
203   if (sub_type == MEDIASUBTYPE_MJPG &&
204       bih->biCompression == MAKEFOURCC('M', 'J', 'P', 'G')) {
205     capability->videoType = VideoType::kMJPEG;
206   } else if (sub_type == MEDIASUBTYPE_I420 &&
207              bih->biCompression == MAKEFOURCC('I', '4', '2', '0')) {
208     capability->videoType = VideoType::kI420;
209   } else if (sub_type == MEDIASUBTYPE_YUY2 &&
210              bih->biCompression == MAKEFOURCC('Y', 'U', 'Y', '2')) {
211     capability->videoType = VideoType::kYUY2;
212   } else if (sub_type == MEDIASUBTYPE_UYVY &&
213              bih->biCompression == MAKEFOURCC('U', 'Y', 'V', 'Y')) {
214     capability->videoType = VideoType::kUYVY;
215   } else if (sub_type == MEDIASUBTYPE_HDYC) {
216     capability->videoType = VideoType::kUYVY;
217   } else if (sub_type == MEDIASUBTYPE_RGB24 && bih->biCompression == BI_RGB) {
218     capability->videoType = VideoType::kRGB24;
219   } else {
220     return false;
221   }
222 
223   // Store the incoming width and height
224   capability->width = bih->biWidth;
225 
226   // Store the incoming height,
227   // for RGB24 we assume the frame to be upside down
228   if (sub_type == MEDIASUBTYPE_RGB24 && bih->biHeight > 0) {
229     capability->height = -(bih->biHeight);
230   } else {
231     capability->height = abs(bih->biHeight);
232   }
233 
234   return true;
235 }
236 
237 class MediaTypesEnum : public IEnumMediaTypes {
238  public:
MediaTypesEnum(const VideoCaptureCapability & capability)239   MediaTypesEnum(const VideoCaptureCapability& capability)
240       : capability_(capability),
241         format_preference_order_(
242             {// Default preferences, sorted by cost-to-convert-to-i420.
243              VideoType::kI420, VideoType::kYUY2, VideoType::kRGB24,
244              VideoType::kUYVY, VideoType::kMJPEG}) {
245     // Use the preferred video type, if supported.
246     auto it = std::find(format_preference_order_.begin(),
247                         format_preference_order_.end(), capability_.videoType);
248     if (it != format_preference_order_.end()) {
249       RTC_LOG(LS_INFO) << "Selected video type: " << *it;
250       // Move it to the front of the list, if it isn't already there.
251       if (it != format_preference_order_.begin()) {
252         format_preference_order_.splice(format_preference_order_.begin(),
253                                         format_preference_order_, it,
254                                         std::next(it));
255       }
256     } else {
257       RTC_LOG(LS_WARNING) << "Unsupported video type: " << *it
258                           << ", using default preference list.";
259     }
260   }
261 
262  protected:
~MediaTypesEnum()263   virtual ~MediaTypesEnum() {}
264 
265  private:
STDMETHOD(QueryInterface)266   STDMETHOD(QueryInterface)(REFIID riid, void** ppv) override {
267     if (riid == IID_IUnknown || riid == IID_IEnumMediaTypes) {
268       *ppv = static_cast<IEnumMediaTypes*>(this);
269       AddRef();
270       return S_OK;
271     }
272     return E_NOINTERFACE;
273   }
274 
275   // IEnumMediaTypes
STDMETHOD(Clone)276   STDMETHOD(Clone)(IEnumMediaTypes** pins) {
277     RTC_DCHECK_NOTREACHED();
278     return E_NOTIMPL;
279   }
280 
STDMETHOD(Next)281   STDMETHOD(Next)(ULONG count, AM_MEDIA_TYPE** types, ULONG* fetched) {
282     RTC_DCHECK(count > 0);
283     RTC_DCHECK(types);
284     // fetched may be NULL.
285     if (fetched)
286       *fetched = 0;
287 
288     for (ULONG i = 0;
289          i < count && pos_ < static_cast<int>(format_preference_order_.size());
290          ++i) {
291       AM_MEDIA_TYPE* media_type = reinterpret_cast<AM_MEDIA_TYPE*>(
292           CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE)));
293       ZeroMemory(media_type, sizeof(*media_type));
294       types[i] = media_type;
295       VIDEOINFOHEADER* vih = reinterpret_cast<VIDEOINFOHEADER*>(
296           AllocMediaTypeFormatBuffer(media_type, sizeof(VIDEOINFOHEADER)));
297       ZeroMemory(vih, sizeof(*vih));
298       vih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
299       vih->bmiHeader.biPlanes = 1;
300       vih->bmiHeader.biClrImportant = 0;
301       vih->bmiHeader.biClrUsed = 0;
302       if (capability_.maxFPS != 0)
303         vih->AvgTimePerFrame = 10000000 / capability_.maxFPS;
304 
305       SetRectEmpty(&vih->rcSource);  // we want the whole image area rendered.
306       SetRectEmpty(&vih->rcTarget);  // no particular destination rectangle
307 
308       media_type->majortype = MEDIATYPE_Video;
309       media_type->formattype = FORMAT_VideoInfo;
310       media_type->bTemporalCompression = FALSE;
311 
312       // Set format information.
313       auto format_it = std::next(format_preference_order_.begin(), pos_++);
314       SetMediaInfoFromVideoType(*format_it, &vih->bmiHeader, media_type);
315 
316       vih->bmiHeader.biWidth = capability_.width;
317       vih->bmiHeader.biHeight = capability_.height;
318       vih->bmiHeader.biSizeImage = ((vih->bmiHeader.biBitCount / 4) *
319                                     capability_.height * capability_.width) /
320                                    2;
321 
322       RTC_DCHECK(vih->bmiHeader.biSizeImage);
323       media_type->lSampleSize = vih->bmiHeader.biSizeImage;
324       media_type->bFixedSizeSamples = true;
325       if (fetched)
326         ++(*fetched);
327     }
328     return pos_ == static_cast<int>(format_preference_order_.size()) ? S_FALSE
329                                                                      : S_OK;
330   }
331 
SetMediaInfoFromVideoType(VideoType video_type,BITMAPINFOHEADER * bitmap_header,AM_MEDIA_TYPE * media_type)332   static void SetMediaInfoFromVideoType(VideoType video_type,
333                                         BITMAPINFOHEADER* bitmap_header,
334                                         AM_MEDIA_TYPE* media_type) {
335     switch (video_type) {
336       case VideoType::kI420:
337         bitmap_header->biCompression = MAKEFOURCC('I', '4', '2', '0');
338         bitmap_header->biBitCount = 12;  // bit per pixel
339         media_type->subtype = MEDIASUBTYPE_I420;
340         break;
341       case VideoType::kYUY2:
342         bitmap_header->biCompression = MAKEFOURCC('Y', 'U', 'Y', '2');
343         bitmap_header->biBitCount = 16;  // bit per pixel
344         media_type->subtype = MEDIASUBTYPE_YUY2;
345         break;
346       case VideoType::kRGB24:
347         bitmap_header->biCompression = BI_RGB;
348         bitmap_header->biBitCount = 24;  // bit per pixel
349         media_type->subtype = MEDIASUBTYPE_RGB24;
350         break;
351       case VideoType::kUYVY:
352         bitmap_header->biCompression = MAKEFOURCC('U', 'Y', 'V', 'Y');
353         bitmap_header->biBitCount = 16;  // bit per pixel
354         media_type->subtype = MEDIASUBTYPE_UYVY;
355         break;
356       case VideoType::kMJPEG:
357         bitmap_header->biCompression = MAKEFOURCC('M', 'J', 'P', 'G');
358         bitmap_header->biBitCount = 12;  // bit per pixel
359         media_type->subtype = MEDIASUBTYPE_MJPG;
360         break;
361       default:
362         RTC_DCHECK_NOTREACHED();
363     }
364   }
365 
STDMETHOD(Skip)366   STDMETHOD(Skip)(ULONG count) {
367     RTC_DCHECK_NOTREACHED();
368     return E_NOTIMPL;
369   }
370 
STDMETHOD(Reset)371   STDMETHOD(Reset)() {
372     pos_ = 0;
373     return S_OK;
374   }
375 
376   int pos_ = 0;
377   const VideoCaptureCapability capability_;
378   std::list<VideoType> format_preference_order_;
379 };
380 
381 }  // namespace
382 
CaptureInputPin(CaptureSinkFilter * filter)383 CaptureInputPin::CaptureInputPin(CaptureSinkFilter* filter) {
384   capture_checker_.Detach();
385   // No reference held to avoid circular references.
386   info_.pFilter = filter;
387   info_.dir = PINDIR_INPUT;
388 }
389 
~CaptureInputPin()390 CaptureInputPin::~CaptureInputPin() {
391   RTC_DCHECK_RUN_ON(&main_checker_);
392   ResetMediaType(&media_type_);
393 }
394 
SetRequestedCapability(const VideoCaptureCapability & capability)395 HRESULT CaptureInputPin::SetRequestedCapability(
396     const VideoCaptureCapability& capability) {
397   RTC_DCHECK_RUN_ON(&main_checker_);
398   RTC_DCHECK(Filter()->IsStopped());
399   requested_capability_ = capability;
400   resulting_capability_ = VideoCaptureCapability();
401   return S_OK;
402 }
403 
OnFilterActivated()404 void CaptureInputPin::OnFilterActivated() {
405   RTC_DCHECK_RUN_ON(&main_checker_);
406   runtime_error_ = false;
407   flushing_ = false;
408   capture_checker_.Detach();
409   capture_thread_id_ = 0;
410 }
411 
OnFilterDeactivated()412 void CaptureInputPin::OnFilterDeactivated() {
413   RTC_DCHECK_RUN_ON(&main_checker_);
414   // Expedite shutdown by raising the flushing flag so no further processing
415   // on the capture thread occurs. When the graph is stopped and all filters
416   // have been told to stop, the media controller (graph) will wait for the
417   // capture thread to stop.
418   flushing_ = true;
419   if (allocator_)
420     allocator_->Decommit();
421 }
422 
Filter() const423 CaptureSinkFilter* CaptureInputPin::Filter() const {
424   return static_cast<CaptureSinkFilter*>(info_.pFilter);
425 }
426 
AttemptConnection(IPin * receive_pin,const AM_MEDIA_TYPE * media_type)427 HRESULT CaptureInputPin::AttemptConnection(IPin* receive_pin,
428                                            const AM_MEDIA_TYPE* media_type) {
429   RTC_DCHECK_RUN_ON(&main_checker_);
430   RTC_DCHECK(Filter()->IsStopped());
431 
432   // Check that the connection is valid  -- need to do this for every
433   // connect attempt since BreakConnect will undo it.
434   HRESULT hr = CheckDirection(receive_pin);
435   if (FAILED(hr))
436     return hr;
437 
438   if (!TranslateMediaTypeToVideoCaptureCapability(media_type,
439                                                   &resulting_capability_)) {
440     ClearAllocator(true);
441     return VFW_E_TYPE_NOT_ACCEPTED;
442   }
443 
444   // See if the other pin will accept this type.
445   hr = receive_pin->ReceiveConnection(static_cast<IPin*>(this), media_type);
446   if (FAILED(hr)) {
447     receive_pin_ = nullptr;  // Should already be null, but just in case.
448     return hr;
449   }
450 
451   // Should have been set as part of the connect process.
452   RTC_DCHECK_EQ(receive_pin_, receive_pin);
453 
454   ResetMediaType(&media_type_);
455   CopyMediaType(&media_type_, media_type);
456 
457   return S_OK;
458 }
459 
DetermineCandidateFormats(IPin * receive_pin,const AM_MEDIA_TYPE * media_type)460 std::vector<AM_MEDIA_TYPE*> CaptureInputPin::DetermineCandidateFormats(
461     IPin* receive_pin,
462     const AM_MEDIA_TYPE* media_type) {
463   RTC_DCHECK_RUN_ON(&main_checker_);
464   RTC_DCHECK(receive_pin);
465   RTC_DCHECK(media_type);
466 
467   std::vector<AM_MEDIA_TYPE*> ret;
468 
469   for (int i = 0; i < 2; i++) {
470     IEnumMediaTypes* types = nullptr;
471     if (i == 0) {
472       // First time around, try types from receive_pin.
473       receive_pin->EnumMediaTypes(&types);
474     } else {
475       // Then try ours.
476       EnumMediaTypes(&types);
477     }
478 
479     if (types) {
480       while (true) {
481         ULONG fetched = 0;
482         AM_MEDIA_TYPE* this_type = nullptr;
483         if (types->Next(1, &this_type, &fetched) != S_OK)
484           break;
485 
486         if (IsMediaTypePartialMatch(*this_type, *media_type)) {
487           ret.push_back(this_type);
488         } else {
489           FreeMediaType(this_type);
490         }
491       }
492       types->Release();
493     }
494   }
495 
496   return ret;
497 }
498 
ClearAllocator(bool decommit)499 void CaptureInputPin::ClearAllocator(bool decommit) {
500   RTC_DCHECK_RUN_ON(&main_checker_);
501   if (!allocator_)
502     return;
503   if (decommit)
504     allocator_->Decommit();
505   allocator_ = nullptr;
506 }
507 
CheckDirection(IPin * pin) const508 HRESULT CaptureInputPin::CheckDirection(IPin* pin) const {
509   RTC_DCHECK_RUN_ON(&main_checker_);
510   PIN_DIRECTION pd;
511   pin->QueryDirection(&pd);
512   // Fairly basic check, make sure we don't pair input with input etc.
513   return pd == info_.dir ? VFW_E_INVALID_DIRECTION : S_OK;
514 }
515 
QueryInterface(REFIID riid,void ** ppv)516 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::QueryInterface(REFIID riid,
517                                                                   void** ppv) {
518   (*ppv) = nullptr;
519   if (riid == IID_IUnknown || riid == IID_IMemInputPin) {
520     *ppv = static_cast<IMemInputPin*>(this);
521   } else if (riid == IID_IPin) {
522     *ppv = static_cast<IPin*>(this);
523   }
524 
525   if (!(*ppv))
526     return E_NOINTERFACE;
527 
528   static_cast<IMemInputPin*>(this)->AddRef();
529   return S_OK;
530 }
531 
532 COM_DECLSPEC_NOTHROW STDMETHODIMP
Connect(IPin * receive_pin,const AM_MEDIA_TYPE * media_type)533 CaptureInputPin::Connect(IPin* receive_pin, const AM_MEDIA_TYPE* media_type) {
534   RTC_DCHECK_RUN_ON(&main_checker_);
535   if (!media_type || !receive_pin)
536     return E_POINTER;
537 
538   if (!Filter()->IsStopped())
539     return VFW_E_NOT_STOPPED;
540 
541   if (receive_pin_) {
542     RTC_DCHECK_NOTREACHED();
543     return VFW_E_ALREADY_CONNECTED;
544   }
545 
546   if (IsMediaTypeFullySpecified(*media_type))
547     return AttemptConnection(receive_pin, media_type);
548 
549   auto types = DetermineCandidateFormats(receive_pin, media_type);
550   bool connected = false;
551   for (auto* type : types) {
552     if (!connected && AttemptConnection(receive_pin, media_type) == S_OK)
553       connected = true;
554 
555     FreeMediaType(type);
556   }
557 
558   return connected ? S_OK : VFW_E_NO_ACCEPTABLE_TYPES;
559 }
560 
561 COM_DECLSPEC_NOTHROW STDMETHODIMP
ReceiveConnection(IPin * connector,const AM_MEDIA_TYPE * media_type)562 CaptureInputPin::ReceiveConnection(IPin* connector,
563                                    const AM_MEDIA_TYPE* media_type) {
564   RTC_DCHECK_RUN_ON(&main_checker_);
565   RTC_DCHECK(Filter()->IsStopped());
566 
567   if (receive_pin_) {
568     RTC_DCHECK_NOTREACHED();
569     return VFW_E_ALREADY_CONNECTED;
570   }
571 
572   HRESULT hr = CheckDirection(connector);
573   if (FAILED(hr))
574     return hr;
575 
576   if (!TranslateMediaTypeToVideoCaptureCapability(media_type,
577                                                   &resulting_capability_))
578     return VFW_E_TYPE_NOT_ACCEPTED;
579 
580   // Complete the connection
581 
582   receive_pin_ = connector;
583   ResetMediaType(&media_type_);
584   CopyMediaType(&media_type_, media_type);
585 
586   return S_OK;
587 }
588 
Disconnect()589 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::Disconnect() {
590   RTC_DCHECK_RUN_ON(&main_checker_);
591   if (!Filter()->IsStopped())
592     return VFW_E_NOT_STOPPED;
593 
594   if (!receive_pin_)
595     return S_FALSE;
596 
597   ClearAllocator(true);
598   receive_pin_ = nullptr;
599 
600   return S_OK;
601 }
602 
ConnectedTo(IPin ** pin)603 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::ConnectedTo(IPin** pin) {
604   RTC_DCHECK_RUN_ON(&main_checker_);
605 
606   if (!receive_pin_)
607     return VFW_E_NOT_CONNECTED;
608 
609   *pin = receive_pin_.get();
610   receive_pin_->AddRef();
611 
612   return S_OK;
613 }
614 
615 COM_DECLSPEC_NOTHROW STDMETHODIMP
ConnectionMediaType(AM_MEDIA_TYPE * media_type)616 CaptureInputPin::ConnectionMediaType(AM_MEDIA_TYPE* media_type) {
617   RTC_DCHECK_RUN_ON(&main_checker_);
618 
619   if (!receive_pin_)
620     return VFW_E_NOT_CONNECTED;
621 
622   CopyMediaType(media_type, &media_type_);
623 
624   return S_OK;
625 }
626 
627 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryPinInfo(PIN_INFO * info)628 CaptureInputPin::QueryPinInfo(PIN_INFO* info) {
629   RTC_DCHECK_RUN_ON(&main_checker_);
630   *info = info_;
631   if (info_.pFilter)
632     info_.pFilter->AddRef();
633   return S_OK;
634 }
635 
636 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryDirection(PIN_DIRECTION * pin_dir)637 CaptureInputPin::QueryDirection(PIN_DIRECTION* pin_dir) {
638   RTC_DCHECK_RUN_ON(&main_checker_);
639   *pin_dir = info_.dir;
640   return S_OK;
641 }
642 
QueryId(LPWSTR * id)643 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::QueryId(LPWSTR* id) {
644   RTC_DCHECK_RUN_ON(&main_checker_);
645   size_t len = lstrlenW(info_.achName);
646   *id = reinterpret_cast<LPWSTR>(CoTaskMemAlloc((len + 1) * sizeof(wchar_t)));
647   lstrcpyW(*id, info_.achName);
648   return S_OK;
649 }
650 
651 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryAccept(const AM_MEDIA_TYPE * media_type)652 CaptureInputPin::QueryAccept(const AM_MEDIA_TYPE* media_type) {
653   RTC_DCHECK_RUN_ON(&main_checker_);
654   RTC_DCHECK(Filter()->IsStopped());
655   VideoCaptureCapability capability(resulting_capability_);
656   return TranslateMediaTypeToVideoCaptureCapability(media_type, &capability)
657              ? S_FALSE
658              : S_OK;
659 }
660 
661 COM_DECLSPEC_NOTHROW STDMETHODIMP
EnumMediaTypes(IEnumMediaTypes ** types)662 CaptureInputPin::EnumMediaTypes(IEnumMediaTypes** types) {
663   RTC_DCHECK_RUN_ON(&main_checker_);
664   *types = new ComRefCount<MediaTypesEnum>(requested_capability_);
665   (*types)->AddRef();
666   return S_OK;
667 }
668 
669 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryInternalConnections(IPin ** pins,ULONG * count)670 CaptureInputPin::QueryInternalConnections(IPin** pins, ULONG* count) {
671   return E_NOTIMPL;
672 }
673 
EndOfStream()674 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::EndOfStream() {
675   return S_OK;
676 }
677 
BeginFlush()678 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::BeginFlush() {
679   RTC_DCHECK_RUN_ON(&main_checker_);
680   flushing_ = true;
681   return S_OK;
682 }
683 
EndFlush()684 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::EndFlush() {
685   RTC_DCHECK_RUN_ON(&main_checker_);
686   flushing_ = false;
687   runtime_error_ = false;
688   return S_OK;
689 }
690 
691 COM_DECLSPEC_NOTHROW STDMETHODIMP
NewSegment(REFERENCE_TIME start,REFERENCE_TIME stop,double rate)692 CaptureInputPin::NewSegment(REFERENCE_TIME start,
693                             REFERENCE_TIME stop,
694                             double rate) {
695   RTC_DCHECK_RUN_ON(&main_checker_);
696   return S_OK;
697 }
698 
699 COM_DECLSPEC_NOTHROW STDMETHODIMP
GetAllocator(IMemAllocator ** allocator)700 CaptureInputPin::GetAllocator(IMemAllocator** allocator) {
701   RTC_DCHECK_RUN_ON(&main_checker_);
702   if (allocator_ == nullptr) {
703     HRESULT hr = CoCreateInstance(CLSID_MemoryAllocator, 0,
704                                   CLSCTX_INPROC_SERVER, IID_IMemAllocator,
705                                   reinterpret_cast<void**>(allocator));
706     if (FAILED(hr))
707       return hr;
708     allocator_.swap(allocator);
709   }
710   *allocator = allocator_.get();
711   allocator_->AddRef();
712   return S_OK;
713 }
714 
715 COM_DECLSPEC_NOTHROW STDMETHODIMP
NotifyAllocator(IMemAllocator * allocator,BOOL read_only)716 CaptureInputPin::NotifyAllocator(IMemAllocator* allocator, BOOL read_only) {
717   RTC_DCHECK_RUN_ON(&main_checker_);
718   allocator_.swap(&allocator);
719   if (allocator_)
720     allocator_->AddRef();
721   if (allocator)
722     allocator->Release();
723   return S_OK;
724 }
725 
726 COM_DECLSPEC_NOTHROW STDMETHODIMP
GetAllocatorRequirements(ALLOCATOR_PROPERTIES * props)727 CaptureInputPin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* props) {
728   return E_NOTIMPL;
729 }
730 
731 COM_DECLSPEC_NOTHROW STDMETHODIMP
Receive(IMediaSample * media_sample)732 CaptureInputPin::Receive(IMediaSample* media_sample) {
733   RTC_DCHECK_RUN_ON(&capture_checker_);
734 
735   CaptureSinkFilter* const filter = static_cast<CaptureSinkFilter*>(Filter());
736 
737   if (flushing_.load(std::memory_order_relaxed))
738     return S_FALSE;
739 
740   if (runtime_error_.load(std::memory_order_relaxed))
741     return VFW_E_RUNTIME_ERROR;
742 
743   if (!capture_thread_id_) {
744     // Make sure we set the thread name only once.
745     capture_thread_id_ = GetCurrentThreadId();
746     rtc::SetCurrentThreadName("webrtc_video_capture");
747   }
748 
749   AM_SAMPLE2_PROPERTIES sample_props = {};
750   GetSampleProperties(media_sample, &sample_props);
751   // Has the format changed in this sample?
752   if (sample_props.dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
753     // Check the derived class accepts the new format.
754     // This shouldn't fail as the source must call QueryAccept first.
755 
756     // Note: This will modify resulting_capability_.
757     // That should be OK as long as resulting_capability_ is only modified
758     // on this thread while it is running (filter is not stopped), and only
759     // modified on the main thread when the filter is stopped (i.e. this thread
760     // is not running).
761     if (!TranslateMediaTypeToVideoCaptureCapability(sample_props.pMediaType,
762                                                     &resulting_capability_)) {
763       // Raise a runtime error if we fail the media type
764       runtime_error_ = true;
765       EndOfStream();
766       Filter()->NotifyEvent(EC_ERRORABORT, VFW_E_TYPE_NOT_ACCEPTED, 0);
767       return VFW_E_INVALIDMEDIATYPE;
768     }
769   }
770 
771   filter->ProcessCapturedFrame(sample_props.pbBuffer, sample_props.lActual,
772                                resulting_capability_);
773 
774   return S_OK;
775 }
776 
777 COM_DECLSPEC_NOTHROW STDMETHODIMP
ReceiveMultiple(IMediaSample ** samples,long count,long * processed)778 CaptureInputPin::ReceiveMultiple(IMediaSample** samples,
779                                  long count,
780                                  long* processed) {
781   HRESULT hr = S_OK;
782   *processed = 0;
783   while (count-- > 0) {
784     hr = Receive(samples[*processed]);
785     if (hr != S_OK)
786       break;
787     ++(*processed);
788   }
789   return hr;
790 }
791 
ReceiveCanBlock()792 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureInputPin::ReceiveCanBlock() {
793   return S_FALSE;
794 }
795 
796 //  ----------------------------------------------------------------------------
797 
CaptureSinkFilter(VideoCaptureImpl * capture_observer)798 CaptureSinkFilter::CaptureSinkFilter(VideoCaptureImpl* capture_observer)
799     : input_pin_(new ComRefCount<CaptureInputPin>(this)),
800       capture_observer_(capture_observer) {}
801 
~CaptureSinkFilter()802 CaptureSinkFilter::~CaptureSinkFilter() {
803   RTC_DCHECK_RUN_ON(&main_checker_);
804 }
805 
SetRequestedCapability(const VideoCaptureCapability & capability)806 HRESULT CaptureSinkFilter::SetRequestedCapability(
807     const VideoCaptureCapability& capability) {
808   RTC_DCHECK_RUN_ON(&main_checker_);
809   // Called on the same thread as capture is started on.
810   return input_pin_->SetRequestedCapability(capability);
811 }
812 
813 COM_DECLSPEC_NOTHROW STDMETHODIMP
GetState(DWORD msecs,FILTER_STATE * state)814 CaptureSinkFilter::GetState(DWORD msecs, FILTER_STATE* state) {
815   RTC_DCHECK_RUN_ON(&main_checker_);
816   *state = state_;
817   return S_OK;
818 }
819 
820 COM_DECLSPEC_NOTHROW STDMETHODIMP
SetSyncSource(IReferenceClock * clock)821 CaptureSinkFilter::SetSyncSource(IReferenceClock* clock) {
822   RTC_DCHECK_RUN_ON(&main_checker_);
823   return S_OK;
824 }
825 
826 COM_DECLSPEC_NOTHROW STDMETHODIMP
GetSyncSource(IReferenceClock ** clock)827 CaptureSinkFilter::GetSyncSource(IReferenceClock** clock) {
828   RTC_DCHECK_RUN_ON(&main_checker_);
829   return E_NOTIMPL;
830 }
831 
Pause()832 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::Pause() {
833   RTC_DCHECK_RUN_ON(&main_checker_);
834   state_ = State_Paused;
835   return S_OK;
836 }
837 
838 COM_DECLSPEC_NOTHROW STDMETHODIMP
Run(REFERENCE_TIME tStart)839 CaptureSinkFilter::Run(REFERENCE_TIME tStart) {
840   RTC_DCHECK_RUN_ON(&main_checker_);
841   if (state_ == State_Stopped)
842     Pause();
843 
844   state_ = State_Running;
845   input_pin_->OnFilterActivated();
846 
847   return S_OK;
848 }
849 
Stop()850 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::Stop() {
851   RTC_DCHECK_RUN_ON(&main_checker_);
852   if (state_ == State_Stopped)
853     return S_OK;
854 
855   state_ = State_Stopped;
856   input_pin_->OnFilterDeactivated();
857 
858   return S_OK;
859 }
860 
861 COM_DECLSPEC_NOTHROW STDMETHODIMP
EnumPins(IEnumPins ** pins)862 CaptureSinkFilter::EnumPins(IEnumPins** pins) {
863   RTC_DCHECK_RUN_ON(&main_checker_);
864   *pins = new ComRefCount<class EnumPins>(input_pin_.get());
865   (*pins)->AddRef();
866   return S_OK;
867 }
868 
FindPin(LPCWSTR id,IPin ** pin)869 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::FindPin(LPCWSTR id,
870                                                              IPin** pin) {
871   RTC_DCHECK_RUN_ON(&main_checker_);
872   // There's no ID assigned to our input pin, so looking it up based on one
873   // is pointless (and in practice, this method isn't being used).
874   return VFW_E_NOT_FOUND;
875 }
876 
877 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryFilterInfo(FILTER_INFO * info)878 CaptureSinkFilter::QueryFilterInfo(FILTER_INFO* info) {
879   RTC_DCHECK_RUN_ON(&main_checker_);
880   *info = info_;
881   if (info->pGraph)
882     info->pGraph->AddRef();
883   return S_OK;
884 }
885 
886 COM_DECLSPEC_NOTHROW STDMETHODIMP
JoinFilterGraph(IFilterGraph * graph,LPCWSTR name)887 CaptureSinkFilter::JoinFilterGraph(IFilterGraph* graph, LPCWSTR name) {
888   RTC_DCHECK_RUN_ON(&main_checker_);
889   RTC_DCHECK(IsStopped());
890 
891   // Note, since a reference to the filter is held by the graph manager,
892   // filters must not hold a reference to the graph. If they would, we'd have
893   // a circular reference. Instead, a pointer to the graph can be held without
894   // reference. See documentation for IBaseFilter::JoinFilterGraph for more.
895   info_.pGraph = graph;  // No AddRef().
896   sink_ = nullptr;
897 
898   if (info_.pGraph) {
899     // make sure we don't hold on to the reference we may receive.
900     // Note that this assumes the same object identity, but so be it.
901     rtc::scoped_refptr<IMediaEventSink> sink;
902     GetComInterface(info_.pGraph, &sink);
903     sink_ = sink.get();
904   }
905 
906   info_.achName[0] = L'\0';
907   if (name)
908     lstrcpynW(info_.achName, name, arraysize(info_.achName));
909 
910   return S_OK;
911 }
912 
913 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryVendorInfo(LPWSTR * vendor_info)914 CaptureSinkFilter::QueryVendorInfo(LPWSTR* vendor_info) {
915   return E_NOTIMPL;
916 }
917 
ProcessCapturedFrame(unsigned char * buffer,size_t length,const VideoCaptureCapability & frame_info)918 void CaptureSinkFilter::ProcessCapturedFrame(
919     unsigned char* buffer,
920     size_t length,
921     const VideoCaptureCapability& frame_info) {
922   // Called on the capture thread.
923   capture_observer_->IncomingFrame(buffer, length, frame_info);
924 }
925 
NotifyEvent(long code,LONG_PTR param1,LONG_PTR param2)926 void CaptureSinkFilter::NotifyEvent(long code,
927                                     LONG_PTR param1,
928                                     LONG_PTR param2) {
929   // Called on the capture thread.
930   if (!sink_)
931     return;
932 
933   if (EC_COMPLETE == code)
934     param2 = reinterpret_cast<LONG_PTR>(static_cast<IBaseFilter*>(this));
935   sink_->Notify(code, param1, param2);
936 }
937 
IsStopped() const938 bool CaptureSinkFilter::IsStopped() const {
939   RTC_DCHECK_RUN_ON(&main_checker_);
940   return state_ == State_Stopped;
941 }
942 
943 COM_DECLSPEC_NOTHROW STDMETHODIMP
QueryInterface(REFIID riid,void ** ppv)944 CaptureSinkFilter::QueryInterface(REFIID riid, void** ppv) {
945   if (riid == IID_IUnknown || riid == IID_IPersist || riid == IID_IBaseFilter) {
946     *ppv = static_cast<IBaseFilter*>(this);
947     AddRef();
948     return S_OK;
949   }
950   return E_NOINTERFACE;
951 }
952 
GetClassID(CLSID * clsid)953 COM_DECLSPEC_NOTHROW STDMETHODIMP CaptureSinkFilter::GetClassID(CLSID* clsid) {
954   *clsid = CLSID_SINKFILTER;
955   return S_OK;
956 }
957 
958 }  // namespace videocapturemodule
959 }  // namespace webrtc
960