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