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/sink_input_pin_win.h"
6
7#include <cstring>
8
9// Avoid including strsafe.h via dshow as it will cause build warnings.
10#define NO_DSHOW_STRSAFE
11#include <dshow.h>
12
13#include "base/logging.h"
14
15namespace media {
16
17const REFERENCE_TIME kSecondsToReferenceTime = 10000000;
18
19SinkInputPin::SinkInputPin(IBaseFilter* filter,
20                           SinkFilterObserver* observer)
21    : observer_(observer),
22      PinBase(filter) {
23}
24
25SinkInputPin::~SinkInputPin() {}
26
27bool SinkInputPin::GetValidMediaType(int index, AM_MEDIA_TYPE* media_type) {
28  if (media_type->cbFormat < sizeof(VIDEOINFOHEADER))
29    return false;
30
31  VIDEOINFOHEADER* pvi =
32      reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
33
34  ZeroMemory(pvi, sizeof(VIDEOINFOHEADER));
35  pvi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
36  pvi->bmiHeader.biPlanes = 1;
37  pvi->bmiHeader.biClrImportant = 0;
38  pvi->bmiHeader.biClrUsed = 0;
39  if (requested_format_.frame_rate > 0) {
40    pvi->AvgTimePerFrame =
41        kSecondsToReferenceTime / requested_format_.frame_rate;
42  }
43
44  media_type->majortype = MEDIATYPE_Video;
45  media_type->formattype = FORMAT_VideoInfo;
46  media_type->bTemporalCompression = FALSE;
47
48  switch (index) {
49    case 0: {
50      pvi->bmiHeader.biCompression = MAKEFOURCC('I', '4', '2', '0');
51      pvi->bmiHeader.biBitCount = 12;  // bit per pixel
52      pvi->bmiHeader.biWidth = requested_format_.frame_size.width();
53      pvi->bmiHeader.biHeight = requested_format_.frame_size.height();
54      pvi->bmiHeader.biSizeImage =
55          requested_format_.frame_size.GetArea() * 3 / 2;
56      media_type->subtype = kMediaSubTypeI420;
57      break;
58    }
59    case 1: {
60      pvi->bmiHeader.biCompression = MAKEFOURCC('Y', 'U', 'Y', '2');
61      pvi->bmiHeader.biBitCount = 16;
62      pvi->bmiHeader.biWidth = requested_format_.frame_size.width();
63      pvi->bmiHeader.biHeight = requested_format_.frame_size.height();
64      pvi->bmiHeader.biSizeImage = requested_format_.frame_size.GetArea() * 2;
65      media_type->subtype = MEDIASUBTYPE_YUY2;
66      break;
67    }
68    case 2: {
69      pvi->bmiHeader.biCompression = BI_RGB;
70      pvi->bmiHeader.biBitCount = 24;
71      pvi->bmiHeader.biWidth = requested_format_.frame_size.width();
72      pvi->bmiHeader.biHeight = requested_format_.frame_size.height();
73      pvi->bmiHeader.biSizeImage = requested_format_.frame_size.GetArea() * 3;
74      media_type->subtype = MEDIASUBTYPE_RGB24;
75      break;
76    }
77    default:
78      return false;
79  }
80
81  media_type->bFixedSizeSamples = TRUE;
82  media_type->lSampleSize = pvi->bmiHeader.biSizeImage;
83  return true;
84}
85
86bool SinkInputPin::IsMediaTypeValid(const AM_MEDIA_TYPE* media_type) {
87  GUID type = media_type->majortype;
88  if (type != MEDIATYPE_Video)
89    return false;
90
91  GUID format_type = media_type->formattype;
92  if (format_type != FORMAT_VideoInfo)
93    return false;
94
95  // Check for the sub types we support.
96  GUID sub_type = media_type->subtype;
97  VIDEOINFOHEADER* pvi =
98      reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
99  if (pvi == NULL)
100    return false;
101
102  // Store the incoming width and height.
103  resulting_format_.frame_size.SetSize(pvi->bmiHeader.biWidth,
104                                       abs(pvi->bmiHeader.biHeight));
105  if (pvi->AvgTimePerFrame > 0) {
106    resulting_format_.frame_rate =
107        static_cast<int>(kSecondsToReferenceTime / pvi->AvgTimePerFrame);
108  } else {
109    resulting_format_.frame_rate = requested_format_.frame_rate;
110  }
111  if (sub_type == kMediaSubTypeI420 &&
112      pvi->bmiHeader.biCompression == MAKEFOURCC('I', '4', '2', '0')) {
113    resulting_format_.pixel_format = PIXEL_FORMAT_I420;
114    return true;  // This format is acceptable.
115  }
116  if (sub_type == MEDIASUBTYPE_YUY2 &&
117      pvi->bmiHeader.biCompression == MAKEFOURCC('Y', 'U', 'Y', '2')) {
118    resulting_format_.pixel_format = PIXEL_FORMAT_YUY2;
119    return true;  // This format is acceptable.
120  }
121  if (sub_type == MEDIASUBTYPE_RGB24 &&
122      pvi->bmiHeader.biCompression == BI_RGB) {
123    resulting_format_.pixel_format = PIXEL_FORMAT_RGB24;
124    return true;  // This format is acceptable.
125  }
126  return false;
127}
128
129HRESULT SinkInputPin::Receive(IMediaSample* sample) {
130  const int length = sample->GetActualDataLength();
131  uint8* buffer = NULL;
132  if (FAILED(sample->GetPointer(&buffer)))
133    return S_FALSE;
134
135  observer_->FrameReceived(buffer, length);
136  return S_OK;
137}
138
139void SinkInputPin::SetRequestedMediaFormat(const VideoCaptureFormat& format) {
140  requested_format_ = format;
141  resulting_format_.frame_size.SetSize(0, 0);
142  resulting_format_.frame_rate = 0;
143  resulting_format_.pixel_format = PIXEL_FORMAT_UNKNOWN;
144}
145
146const VideoCaptureFormat& SinkInputPin::ResultingFormat() {
147  return resulting_format_;
148}
149
150}  // namespace media
151