1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "media/video/capture/win/pin_base_win.h"
6
7#include "base/logging.h"
8
9namespace media {
10
11// Implement IEnumPins.
12class TypeEnumerator FINAL
13    : public IEnumMediaTypes,
14      public base::RefCounted<TypeEnumerator> {
15 public:
16  explicit TypeEnumerator(PinBase* pin)
17      : pin_(pin),
18        index_(0) {
19  }
20
21  ~TypeEnumerator() {
22  }
23
24  // Implement from IUnknown.
25  STDMETHOD(QueryInterface)(REFIID iid, void** object_ptr) {
26    if (iid == IID_IEnumMediaTypes || iid == IID_IUnknown) {
27      AddRef();
28      *object_ptr = static_cast<IEnumMediaTypes*>(this);
29      return S_OK;
30    }
31    return E_NOINTERFACE;
32  }
33
34  STDMETHOD_(ULONG, AddRef)() {
35    base::RefCounted<TypeEnumerator>::AddRef();
36    return 1;
37  }
38
39  STDMETHOD_(ULONG, Release)() {
40    base::RefCounted<TypeEnumerator>::Release();
41    return 1;
42  }
43
44  // Implement IEnumMediaTypes.
45  STDMETHOD(Next)(ULONG count, AM_MEDIA_TYPE** types, ULONG* fetched) {
46    ULONG types_fetched = 0;
47
48    while (types_fetched < count) {
49      // Allocate AM_MEDIA_TYPE that we will store the media type in.
50      AM_MEDIA_TYPE* type = reinterpret_cast<AM_MEDIA_TYPE*>(CoTaskMemAlloc(
51          sizeof(AM_MEDIA_TYPE)));
52      if (!type) {
53        FreeAllocatedMediaTypes(types_fetched, types);
54        return E_OUTOFMEMORY;
55      }
56      ZeroMemory(type, sizeof(AM_MEDIA_TYPE));
57
58      // Allocate a VIDEOINFOHEADER and connect it to the AM_MEDIA_TYPE.
59      type->cbFormat = sizeof(VIDEOINFOHEADER);
60      BYTE *format = reinterpret_cast<BYTE*>(CoTaskMemAlloc(
61          sizeof(VIDEOINFOHEADER)));
62      if (!format) {
63        CoTaskMemFree(type);
64        FreeAllocatedMediaTypes(types_fetched, types);
65        return E_OUTOFMEMORY;
66      }
67      type->pbFormat = format;
68      // Get the media type from the pin.
69      if (pin_->GetValidMediaType(index_++, type)) {
70        types[types_fetched++] = type;
71      } else {
72        CoTaskMemFree(format);
73        CoTaskMemFree(type);
74        break;
75      }
76    }
77
78    if (fetched)
79      *fetched = types_fetched;
80
81    return types_fetched == count ? S_OK : S_FALSE;
82  }
83
84  STDMETHOD(Skip)(ULONG count) {
85    index_ += count;
86    return S_OK;
87  }
88
89  STDMETHOD(Reset)() {
90    index_ = 0;
91    return S_OK;
92  }
93
94  STDMETHOD(Clone)(IEnumMediaTypes** clone) {
95    TypeEnumerator* type_enum = new TypeEnumerator(pin_);
96    type_enum->AddRef();
97    type_enum->index_ = index_;
98    *clone = type_enum;
99    return S_OK;
100  }
101
102 private:
103  void FreeAllocatedMediaTypes(ULONG allocated, AM_MEDIA_TYPE** types) {
104    for (ULONG i = 0; i < allocated; ++i) {
105      CoTaskMemFree(types[i]->pbFormat);
106      CoTaskMemFree(types[i]);
107    }
108  }
109
110  scoped_refptr<PinBase> pin_;
111  int index_;
112};
113
114PinBase::PinBase(IBaseFilter* owner)
115    : owner_(owner) {
116  memset(&current_media_type_, 0, sizeof(current_media_type_));
117}
118
119PinBase::~PinBase() {
120}
121
122void PinBase::SetOwner(IBaseFilter* owner) {
123  owner_ = owner;
124}
125
126// Called on an output pin to and establish a
127//   connection.
128STDMETHODIMP PinBase::Connect(IPin* receive_pin,
129                              const AM_MEDIA_TYPE* media_type) {
130  if (!receive_pin || !media_type)
131    return E_POINTER;
132
133  current_media_type_ = *media_type;
134  receive_pin->AddRef();
135  connected_pin_.Attach(receive_pin);
136  HRESULT hr = receive_pin->ReceiveConnection(this, media_type);
137
138  return hr;
139}
140
141// Called from an output pin on an input pin to and establish a
142// connection.
143STDMETHODIMP PinBase::ReceiveConnection(IPin* connector,
144                                        const AM_MEDIA_TYPE* media_type) {
145  if (!IsMediaTypeValid(media_type))
146    return VFW_E_TYPE_NOT_ACCEPTED;
147
148  current_media_type_ = *media_type;
149  connector->AddRef();
150  connected_pin_.Attach(connector);
151  return S_OK;
152}
153
154STDMETHODIMP PinBase::Disconnect() {
155  if (!connected_pin_)
156    return S_FALSE;
157
158  connected_pin_.Release();
159  return S_OK;
160}
161
162STDMETHODIMP PinBase::ConnectedTo(IPin** pin) {
163  *pin = connected_pin_;
164  if (!connected_pin_)
165    return VFW_E_NOT_CONNECTED;
166
167  connected_pin_.get()->AddRef();
168  return S_OK;
169}
170
171STDMETHODIMP PinBase::ConnectionMediaType(AM_MEDIA_TYPE* media_type) {
172  if (!connected_pin_)
173    return VFW_E_NOT_CONNECTED;
174  *media_type = current_media_type_;
175  return S_OK;
176}
177
178STDMETHODIMP PinBase::QueryPinInfo(PIN_INFO* info) {
179  info->dir = PINDIR_INPUT;
180  info->pFilter = owner_;
181  if (owner_)
182    owner_->AddRef();
183  info->achName[0] = L'\0';
184
185  return S_OK;
186}
187
188STDMETHODIMP PinBase::QueryDirection(PIN_DIRECTION* pin_dir) {
189  *pin_dir = PINDIR_INPUT;
190  return S_OK;
191}
192
193STDMETHODIMP PinBase::QueryId(LPWSTR* id) {
194  NOTREACHED();
195  return E_OUTOFMEMORY;
196}
197
198STDMETHODIMP PinBase::QueryAccept(const AM_MEDIA_TYPE* media_type) {
199  return S_FALSE;
200}
201
202STDMETHODIMP PinBase::EnumMediaTypes(IEnumMediaTypes** types) {
203  *types = new TypeEnumerator(this);
204  (*types)->AddRef();
205  return S_OK;
206}
207
208STDMETHODIMP PinBase::QueryInternalConnections(IPin** pins, ULONG* no_pins) {
209  return E_NOTIMPL;
210}
211
212STDMETHODIMP PinBase::EndOfStream() {
213  return S_OK;
214}
215
216STDMETHODIMP PinBase::BeginFlush() {
217  return S_OK;
218}
219
220STDMETHODIMP PinBase::EndFlush() {
221  return S_OK;
222}
223
224STDMETHODIMP PinBase::NewSegment(REFERENCE_TIME start,
225                                 REFERENCE_TIME stop,
226                                 double rate) {
227  NOTREACHED();
228  return E_NOTIMPL;
229}
230
231// Inherited from IMemInputPin.
232STDMETHODIMP PinBase::GetAllocator(IMemAllocator** allocator) {
233  return VFW_E_NO_ALLOCATOR;
234}
235
236STDMETHODIMP PinBase::NotifyAllocator(IMemAllocator* allocator,
237                                      BOOL read_only) {
238  return S_OK;
239}
240
241STDMETHODIMP PinBase::GetAllocatorRequirements(
242    ALLOCATOR_PROPERTIES* properties) {
243  return E_NOTIMPL;
244}
245
246STDMETHODIMP PinBase::ReceiveMultiple(IMediaSample** samples,
247                                      long sample_count,
248                                      long* processed) {
249  DCHECK(samples);
250
251  HRESULT hr = S_OK;
252  *processed = 0;
253  while (sample_count--) {
254    hr = Receive(samples[*processed]);
255    // S_FALSE means don't send any more.
256    if (hr != S_OK)
257      break;
258    ++(*processed);
259  }
260  return hr;
261}
262
263STDMETHODIMP PinBase::ReceiveCanBlock() {
264  return S_FALSE;
265}
266
267// Inherited from IUnknown.
268STDMETHODIMP PinBase::QueryInterface(REFIID id, void** object_ptr) {
269  if (id == IID_IPin || id == IID_IUnknown) {
270    *object_ptr = static_cast<IPin*>(this);
271  } else if (id == IID_IMemInputPin) {
272    *object_ptr = static_cast<IMemInputPin*>(this);
273  } else {
274    return E_NOINTERFACE;
275  }
276  AddRef();
277  return S_OK;
278}
279
280STDMETHODIMP_(ULONG) PinBase::AddRef() {
281  base::RefCounted<PinBase>::AddRef();
282  return 1;
283}
284
285STDMETHODIMP_(ULONG) PinBase::Release() {
286  base::RefCounted<PinBase>::Release();
287  return 1;
288}
289
290}  // namespace media
291