1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "directshowmediatype.h"
41 #include "directshowglobal.h"
42
43 #include <initguid.h>
44
45 DEFINE_GUID(MEDIASUBTYPE_Y800, 0x30303859, 0x0000, 0x0010, 0x80, 0x00,
46 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
47
48 namespace
49 {
50 struct TypeLookup
51 {
52 QVideoFrame::PixelFormat pixelFormat;
53 GUID mediaType;
54 };
55
56 static const TypeLookup qt_typeLookup[] =
57 {
58 { QVideoFrame::Format_ARGB32, MEDIASUBTYPE_ARGB32 },
59 { QVideoFrame::Format_RGB32, MEDIASUBTYPE_RGB32 },
60 { QVideoFrame::Format_BGR24, MEDIASUBTYPE_RGB24 },
61 { QVideoFrame::Format_RGB565, MEDIASUBTYPE_RGB565 },
62 { QVideoFrame::Format_RGB555, MEDIASUBTYPE_RGB555 },
63 { QVideoFrame::Format_AYUV444, MEDIASUBTYPE_AYUV },
64 { QVideoFrame::Format_YUYV, MEDIASUBTYPE_YUY2 },
65 { QVideoFrame::Format_UYVY, MEDIASUBTYPE_UYVY },
66 { QVideoFrame::Format_IMC1, MEDIASUBTYPE_IMC1 },
67 { QVideoFrame::Format_IMC2, MEDIASUBTYPE_IMC2 },
68 { QVideoFrame::Format_IMC3, MEDIASUBTYPE_IMC3 },
69 { QVideoFrame::Format_IMC4, MEDIASUBTYPE_IMC4 },
70 { QVideoFrame::Format_YV12, MEDIASUBTYPE_YV12 },
71 { QVideoFrame::Format_NV12, MEDIASUBTYPE_NV12 },
72 { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_IYUV },
73 { QVideoFrame::Format_YUV420P, MEDIASUBTYPE_I420 },
74 { QVideoFrame::Format_Jpeg, MEDIASUBTYPE_MJPG },
75 { QVideoFrame::Format_Y8, MEDIASUBTYPE_Y800 },
76 };
77 }
78
isPartiallySpecified(const AM_MEDIA_TYPE * mediaType)79 bool DirectShowMediaType::isPartiallySpecified(const AM_MEDIA_TYPE *mediaType)
80 {
81 return mediaType->majortype == GUID_NULL || mediaType->formattype == GUID_NULL;
82 }
83
DirectShowMediaType()84 DirectShowMediaType::DirectShowMediaType()
85 : mediaType({ GUID_NULL, GUID_NULL, TRUE, FALSE, 1, GUID_NULL, nullptr, 0, nullptr})
86 {
87 }
88
DirectShowMediaType(const AM_MEDIA_TYPE & type)89 DirectShowMediaType::DirectShowMediaType(const AM_MEDIA_TYPE &type)
90 : DirectShowMediaType()
91 {
92 copy(&mediaType, &type);
93 }
94
DirectShowMediaType(AM_MEDIA_TYPE && type)95 DirectShowMediaType::DirectShowMediaType(AM_MEDIA_TYPE &&type)
96 : DirectShowMediaType()
97 {
98 move(&mediaType, type);
99 }
100
DirectShowMediaType(const DirectShowMediaType & other)101 DirectShowMediaType::DirectShowMediaType(const DirectShowMediaType &other)
102 : DirectShowMediaType()
103 {
104 copy(&mediaType, &other.mediaType);
105 }
106
DirectShowMediaType(DirectShowMediaType && other)107 DirectShowMediaType::DirectShowMediaType(DirectShowMediaType &&other) noexcept
108 : DirectShowMediaType()
109 {
110 move(&mediaType, other.mediaType);
111 }
112
operator =(const DirectShowMediaType & other)113 DirectShowMediaType &DirectShowMediaType::operator=(const DirectShowMediaType &other)
114 {
115 copy(&mediaType, &other.mediaType);
116 return *this;
117 }
118
operator =(DirectShowMediaType && other)119 DirectShowMediaType &DirectShowMediaType::operator=(DirectShowMediaType &&other) noexcept
120 {
121 move(&mediaType, other.mediaType);
122 return *this;
123 }
124
init(AM_MEDIA_TYPE * type)125 void DirectShowMediaType::init(AM_MEDIA_TYPE *type)
126 {
127 Q_ASSERT(type);
128 SecureZeroMemory(reinterpret_cast<void *>(type), sizeof(AM_MEDIA_TYPE));
129 type->lSampleSize = 1;
130 type->bFixedSizeSamples = TRUE;
131 }
132
copy(AM_MEDIA_TYPE * target,const AM_MEDIA_TYPE * source)133 void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source)
134 {
135 if (!(target && source))
136 return;
137
138 if (target == source)
139 return;
140
141 clear(*target);
142
143 copyToUninitialized(target, source);
144 }
145
copyToUninitialized(AM_MEDIA_TYPE * target,const AM_MEDIA_TYPE * source)146 void DirectShowMediaType::copyToUninitialized(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE *source)
147 {
148 *target = *source;
149
150 if (source->cbFormat > 0) {
151 target->pbFormat = reinterpret_cast<PBYTE>(CoTaskMemAlloc(source->cbFormat));
152 memcpy(target->pbFormat, source->pbFormat, source->cbFormat);
153 }
154 if (target->pUnk)
155 target->pUnk->AddRef();
156 }
157
move(AM_MEDIA_TYPE * target,AM_MEDIA_TYPE ** source)158 void DirectShowMediaType::move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE **source)
159 {
160 if (!target || !source || !(*source))
161 return;
162
163 if (target == *source)
164 return;
165
166 clear(*target);
167 *target = *(*source);
168 SecureZeroMemory(reinterpret_cast<void *>(*source), sizeof(AM_MEDIA_TYPE));
169 *source = nullptr;
170 }
171
move(AM_MEDIA_TYPE * target,AM_MEDIA_TYPE & source)172 void DirectShowMediaType::move(AM_MEDIA_TYPE *target, AM_MEDIA_TYPE &source)
173 {
174 AM_MEDIA_TYPE *srcPtr = &source;
175 move(target, &srcPtr);
176 }
177
178 /**
179 * @brief DirectShowMediaType::deleteType - Used for AM_MEDIA_TYPE structures that have
180 * been allocated by CoTaskMemAlloc or CreateMediaType.
181 * @param type
182 */
deleteType(AM_MEDIA_TYPE * type)183 void DirectShowMediaType::deleteType(AM_MEDIA_TYPE *type)
184 {
185 if (!type)
186 return;
187
188 clear(*type);
189 CoTaskMemFree(type);
190 }
191
isCompatible(const AM_MEDIA_TYPE * a,const AM_MEDIA_TYPE * b)192 bool DirectShowMediaType::isCompatible(const AM_MEDIA_TYPE *a, const AM_MEDIA_TYPE *b)
193 {
194 if (b->majortype != GUID_NULL && a->majortype != b->majortype)
195 return false;
196
197 if (b->subtype != GUID_NULL && a->subtype != b->subtype)
198 return false;
199
200 if (b->formattype != GUID_NULL) {
201 if (a->formattype != b->formattype)
202 return false;
203 if (a->cbFormat != b->cbFormat)
204 return false;
205 if (a->cbFormat != 0 && memcmp(a->pbFormat, b->pbFormat, a->cbFormat) != 0)
206 return false;
207 }
208
209 return true;
210 }
211
212 /**
213 * @brief DirectShowMediaType::clear - Clears all member data, and releases allocated buffers.
214 * Use this to release automatic AM_MEDIA_TYPE structures.
215 * @param type
216 */
clear(AM_MEDIA_TYPE & type)217 void DirectShowMediaType::clear(AM_MEDIA_TYPE &type)
218 {
219 if (type.cbFormat > 0)
220 CoTaskMemFree(type.pbFormat);
221
222 if (type.pUnk)
223 type.pUnk->Release();
224
225 SecureZeroMemory(&type, sizeof(type));
226 }
227
228
convertPixelFormat(QVideoFrame::PixelFormat format)229 GUID DirectShowMediaType::convertPixelFormat(QVideoFrame::PixelFormat format)
230 {
231 for (const auto &lookupType : qt_typeLookup) {
232 if (lookupType.pixelFormat == format)
233 return lookupType.mediaType;
234 }
235
236 return MEDIASUBTYPE_None;
237 }
238
videoFormatFromType(const AM_MEDIA_TYPE * type)239 QVideoSurfaceFormat DirectShowMediaType::videoFormatFromType(const AM_MEDIA_TYPE *type)
240 {
241 if (!type)
242 return QVideoSurfaceFormat();
243
244 for (const auto &lookupType : qt_typeLookup) {
245 if (IsEqualGUID(lookupType.mediaType, type->subtype) && type->cbFormat > 0) {
246 if (IsEqualGUID(type->formattype, FORMAT_VideoInfo)) {
247 VIDEOINFOHEADER *header = reinterpret_cast<VIDEOINFOHEADER *>(type->pbFormat);
248
249 QVideoSurfaceFormat format(
250 QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)),
251 lookupType.pixelFormat);
252
253 if (header->AvgTimePerFrame > 0)
254 format.setFrameRate(10000 /header->AvgTimePerFrame);
255
256 format.setScanLineDirection(scanLineDirection(format.pixelFormat(), header->bmiHeader));
257
258 return format;
259 }
260 if (IsEqualGUID(type->formattype, FORMAT_VideoInfo2)) {
261 VIDEOINFOHEADER2 *header = reinterpret_cast<VIDEOINFOHEADER2 *>(type->pbFormat);
262
263 QVideoSurfaceFormat format(
264 QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)),
265 lookupType.pixelFormat);
266
267 if (header->AvgTimePerFrame > 0)
268 format.setFrameRate(10000 / header->AvgTimePerFrame);
269
270 format.setScanLineDirection(scanLineDirection(format.pixelFormat(), header->bmiHeader));
271
272 return format;
273 }
274 }
275 }
276 return QVideoSurfaceFormat();
277 }
278
pixelFormatFromType(const AM_MEDIA_TYPE * type)279 QVideoFrame::PixelFormat DirectShowMediaType::pixelFormatFromType(const AM_MEDIA_TYPE *type)
280 {
281 if (!type)
282 return QVideoFrame::Format_Invalid;
283
284 for (const auto &lookupType : qt_typeLookup) {
285 if (IsEqualGUID(lookupType.mediaType, type->subtype))
286 return lookupType.pixelFormat;
287 }
288
289 return QVideoFrame::Format_Invalid;
290 }
291
292 #define PAD_TO_DWORD(x) (((x) + 3) & ~3)
bytesPerLine(const QVideoSurfaceFormat & format)293 int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format)
294 {
295 switch (format.pixelFormat()) {
296 // 32 bpp packed formats.
297 case QVideoFrame::Format_ARGB32:
298 case QVideoFrame::Format_RGB32:
299 case QVideoFrame::Format_AYUV444:
300 return format.frameWidth() * 4;
301 // 24 bpp packed formats.
302 case QVideoFrame::Format_RGB24:
303 case QVideoFrame::Format_BGR24:
304 return PAD_TO_DWORD(format.frameWidth() * 3);
305 // 16 bpp packed formats.
306 case QVideoFrame::Format_RGB565:
307 case QVideoFrame::Format_RGB555:
308 case QVideoFrame::Format_YUYV:
309 case QVideoFrame::Format_UYVY:
310 return PAD_TO_DWORD(format.frameWidth() * 2);
311 // Planar formats.
312 case QVideoFrame::Format_YV12:
313 case QVideoFrame::Format_YUV420P:
314 case QVideoFrame::Format_IMC1:
315 case QVideoFrame::Format_IMC2:
316 case QVideoFrame::Format_IMC3:
317 case QVideoFrame::Format_IMC4:
318 case QVideoFrame::Format_NV12:
319 return format.frameWidth();
320 default:
321 return 0;
322 }
323 }
324
scanLineDirection(QVideoFrame::PixelFormat pixelFormat,const BITMAPINFOHEADER & bmiHeader)325 QVideoSurfaceFormat::Direction DirectShowMediaType::scanLineDirection(QVideoFrame::PixelFormat pixelFormat, const BITMAPINFOHEADER &bmiHeader)
326 {
327 /* MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/dd318229(v=vs.85).aspx */
328 /* For uncompressed RGB bitmaps:
329 * if biHeight is positive, the bitmap is a bottom-up DIB with the origin at the lower left corner.
330 * If biHeight is negative, the bitmap is a top-down DIB with the origin at the upper left corner.
331 *
332 * For YUV bitmaps:
333 * the bitmap is always top-down, regardless of the sign of biHeight.
334 * Decoders should offer YUV formats with postive biHeight, but for backward compatibility they should accept YUV formats with either positive or negative biHeight.
335 *
336 * For compressed formats:
337 * biHeight must be positive, regardless of image orientation.
338 */
339 switch (pixelFormat)
340 {
341 case QVideoFrame::Format_ARGB32:
342 case QVideoFrame::Format_RGB32:
343 case QVideoFrame::Format_RGB24:
344 case QVideoFrame::Format_RGB565:
345 case QVideoFrame::Format_RGB555:
346 return bmiHeader.biHeight < 0
347 ? QVideoSurfaceFormat::TopToBottom
348 : QVideoSurfaceFormat::BottomToTop;
349 default:
350 return QVideoSurfaceFormat::TopToBottom;
351 }
352 }
353