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