Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
mfmetadata.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <qmediametadata.h>
5#include <qdatetime.h>
6#include <qtimezone.h>
7#include <qimage.h>
8#include <quuid.h>
9
10#include <guiddef.h>
11#include <cguid.h>
12#include <mfapi.h>
13#include <mfidl.h>
14#include <propvarutil.h>
15#include <propkey.h>
16
17#include "private/qwindowsmultimediautils_p.h"
18#include "mfmetadata_p.h"
19
20//#define DEBUG_MEDIAFOUNDATION
21
22static const PROPERTYKEY PROP_KEY_NULL = {GUID_NULL, 0};
23
24static QVariant convertValue(const PROPVARIANT& var)
25{
26 QVariant value;
27 switch (var.vt) {
28 case VT_LPWSTR:
29 value = QString::fromUtf16(reinterpret_cast<const char16_t *>(var.pwszVal));
30 break;
31 case VT_UI4:
32 value = uint(var.ulVal);
33 break;
34 case VT_UI8:
35 value = qulonglong(var.uhVal.QuadPart);
36 break;
37 case VT_BOOL:
38 value = bool(var.boolVal);
39 break;
40 case VT_FILETIME:
41 SYSTEMTIME t;
42 if (!FileTimeToSystemTime(&var.filetime, &t))
43 break;
44
45 value = QDateTime(QDate(t.wYear, t.wMonth, t.wDay),
46 QTime(t.wHour, t.wMinute, t.wSecond, t.wMilliseconds),
47 QTimeZone(QTimeZone::UTC));
48 break;
49 case VT_STREAM:
50 {
51 STATSTG stat;
52 if (FAILED(var.pStream->Stat(&stat, STATFLAG_NONAME)))
53 break;
54 void *data = malloc(stat.cbSize.QuadPart);
55 ULONG read = 0;
56 if (FAILED(var.pStream->Read(data, stat.cbSize.QuadPart, &read))) {
57 free(data);
58 break;
59 }
60 value = QImage::fromData((const uchar*)data, read);
61 free(data);
62 }
63 break;
64 case VT_VECTOR | VT_LPWSTR:
65 QStringList vList;
66 for (ULONG i = 0; i < var.calpwstr.cElems; ++i)
67 vList.append(QString::fromUtf16(reinterpret_cast<const char16_t *>(var.calpwstr.pElems[i])));
68 value = vList;
69 break;
70 }
71 return value;
72}
73
74static QVariant metaDataValue(IPropertyStore *content, const PROPERTYKEY &key)
75{
76 QVariant value;
77
78 PROPVARIANT var;
79 PropVariantInit(&var);
80 HRESULT hr = S_FALSE;
81 if (content)
82 hr = content->GetValue(key, &var);
83
84 if (SUCCEEDED(hr)) {
85 value = convertValue(var);
86
87 // some metadata needs to be reformatted
88 if (value.isValid() && content) {
89 if (key == PKEY_Media_ClassPrimaryID /*QMediaMetaData::MediaType*/) {
90 QString v = value.toString();
91 if (v == QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}"))
92 value = QStringLiteral("Music");
93 else if (v == QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}"))
94 value = QStringLiteral("Video");
95 else if (v == QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}"))
96 value = QStringLiteral("Audio");
97 else if (v == QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}"))
98 value = QStringLiteral("Other");
99 } else if (key == PKEY_Media_Duration) {
100 // duration is provided in 100-nanosecond units, convert to milliseconds
101 value = (value.toLongLong() + 10000) / 10000;
102 } else if (key == PKEY_Video_Compression) {
103 value = int(QWindowsMultimediaUtils::codecForVideoFormat(value.toUuid()));
104 } else if (key == PKEY_Audio_Format) {
105 value = int(QWindowsMultimediaUtils::codecForAudioFormat(value.toUuid()));
106 } else if (key == PKEY_Video_FrameHeight /*Resolution*/) {
107 QSize res;
108 res.setHeight(value.toUInt());
109 if (content && SUCCEEDED(content->GetValue(PKEY_Video_FrameWidth, &var)))
110 res.setWidth(convertValue(var).toUInt());
111 value = res;
112 } else if (key == PKEY_Video_Orientation) {
113 uint orientation = 0;
114 if (content && SUCCEEDED(content->GetValue(PKEY_Video_Orientation, &var)))
115 orientation = convertValue(var).toUInt();
116 value = orientation;
117 } else if (key == PKEY_Video_FrameRate) {
118 value = value.toReal() / 1000.f;
119 }
120 }
121 }
122
123 PropVariantClear(&var);
124 return value;
125}
126
127QMediaMetaData MFMetaData::fromNative(IMFMediaSource* mediaSource)
128{
129 QMediaMetaData metaData;
130
131 IPropertyStore *content = nullptr;
132 if (!SUCCEEDED(MFGetService(mediaSource, MF_PROPERTY_HANDLER_SERVICE, IID_PPV_ARGS(&content))))
133 return metaData;
134
135 Q_ASSERT(content);
136 DWORD cProps;
137 if (SUCCEEDED(content->GetCount(&cProps))) {
138 for (DWORD i = 0; i < cProps; i++)
139 {
140 PROPERTYKEY key;
141 if (FAILED(content->GetAt(i, &key)))
142 continue;
143 QMediaMetaData::Key mediaKey;
144 if (key == PKEY_Author) {
145 mediaKey = QMediaMetaData::Author;
146 } else if (key == PKEY_Title) {
147 mediaKey = QMediaMetaData::Title;
148// } else if (key == PKEY_Media_SubTitle) {
149// mediaKey = QMediaMetaData::SubTitle;
150// } else if (key == PKEY_ParentalRating) {
151// mediaKey = QMediaMetaData::ParentalRating;
152 } else if (key == PKEY_Media_EncodingSettings) {
153 mediaKey = QMediaMetaData::Description;
154 } else if (key == PKEY_Copyright) {
155 mediaKey = QMediaMetaData::Copyright;
156 } else if (key == PKEY_Comment) {
157 mediaKey = QMediaMetaData::Comment;
158 } else if (key == PKEY_Media_ProviderStyle) {
159 mediaKey = QMediaMetaData::Genre;
160 } else if (key == PKEY_Media_DateEncoded) {
161 mediaKey = QMediaMetaData::Date;
162// } else if (key == PKEY_Rating) {
163// mediaKey = QMediaMetaData::UserRating;
164// } else if (key == PKEY_Keywords) {
165// mediaKey = QMediaMetaData::Keywords;
166 } else if (key == PKEY_Language) {
167 mediaKey = QMediaMetaData::Language;
168 } else if (key == PKEY_Media_Publisher) {
169 mediaKey = QMediaMetaData::Publisher;
170 } else if (key == PKEY_Media_ClassPrimaryID) {
171 mediaKey = QMediaMetaData::MediaType;
172 } else if (key == PKEY_Media_Duration) {
173 mediaKey = QMediaMetaData::Duration;
174 } else if (key == PKEY_Audio_EncodingBitrate) {
175 mediaKey = QMediaMetaData::AudioBitRate;
176 } else if (key == PKEY_Audio_Format) {
177 mediaKey = QMediaMetaData::AudioCodec;
178// } else if (key == PKEY_Media_AverageLevel) {
179// mediaKey = QMediaMetaData::AverageLevel;
180// } else if (key == PKEY_Audio_ChannelCount) {
181// mediaKey = QMediaMetaData::ChannelCount;
182// } else if (key == PKEY_Audio_PeakValue) {
183// mediaKey = QMediaMetaData::PeakValue;
184// } else if (key == PKEY_Audio_SampleRate) {
185// mediaKey = QMediaMetaData::SampleRate;
186 } else if (key == PKEY_Music_AlbumTitle) {
187 mediaKey = QMediaMetaData::AlbumTitle;
188 } else if (key == PKEY_Music_AlbumArtist) {
189 mediaKey = QMediaMetaData::AlbumArtist;
190 } else if (key == PKEY_Music_Artist) {
191 mediaKey = QMediaMetaData::ContributingArtist;
192 } else if (key == PKEY_Music_Composer) {
193 mediaKey = QMediaMetaData::Composer;
194// } else if (key == PKEY_Music_Conductor) {
195// mediaKey = QMediaMetaData::Conductor;
196// } else if (key == PKEY_Music_Lyrics) {
197// mediaKey = QMediaMetaData::Lyrics;
198// } else if (key == PKEY_Music_Mood) {
199// mediaKey = QMediaMetaData::Mood;
200 } else if (key == PKEY_Music_TrackNumber) {
201 mediaKey = QMediaMetaData::TrackNumber;
202 } else if (key == PKEY_Music_Genre) {
203 mediaKey = QMediaMetaData::Genre;
204 } else if (key == PKEY_ThumbnailStream) {
205 mediaKey = QMediaMetaData::ThumbnailImage;
206 } else if (key == PKEY_Video_FrameHeight) {
207 mediaKey = QMediaMetaData::Resolution;
208 } else if (key == PKEY_Video_Orientation) {
209 mediaKey = QMediaMetaData::Orientation;
210 } else if (key == PKEY_Video_FrameRate) {
211 mediaKey = QMediaMetaData::VideoFrameRate;
212 } else if (key == PKEY_Video_EncodingBitrate) {
213 mediaKey = QMediaMetaData::VideoBitRate;
214 } else if (key == PKEY_Video_Compression) {
215 mediaKey = QMediaMetaData::VideoCodec;
216// } else if (key == PKEY_Video_Director) {
217// mediaKey = QMediaMetaData::Director;
218// } else if (key == PKEY_Media_Writer) {
219// mediaKey = QMediaMetaData::Writer;
220 } else {
221 continue;
222 }
223 metaData.insert(mediaKey, metaDataValue(content, key));
224 }
225 }
226
227 content->Release();
228
229 return metaData;
230}
231
232static REFPROPERTYKEY propertyKeyForMetaDataKey(QMediaMetaData::Key key)
233{
234 switch (key) {
235 case QMediaMetaData::Key::Title:
236 return PKEY_Title;
237 case QMediaMetaData::Key::Author:
238 return PKEY_Author;
239 case QMediaMetaData::Key::Comment:
240 return PKEY_Comment;
241 case QMediaMetaData::Key::Genre:
242 return PKEY_Music_Genre;
243 case QMediaMetaData::Key::Copyright:
244 return PKEY_Copyright;
245 case QMediaMetaData::Key::Publisher:
246 return PKEY_Media_Publisher;
247 case QMediaMetaData::Key::Url:
248 return PKEY_Media_AuthorUrl;
249 case QMediaMetaData::Key::AlbumTitle:
250 return PKEY_Music_AlbumTitle;
251 case QMediaMetaData::Key::AlbumArtist:
252 return PKEY_Music_AlbumArtist;
253 case QMediaMetaData::Key::TrackNumber:
254 return PKEY_Music_TrackNumber;
255 case QMediaMetaData::Key::Date:
256 return PKEY_Media_DateEncoded;
257 case QMediaMetaData::Key::Composer:
258 return PKEY_Music_Composer;
259 case QMediaMetaData::Key::Duration:
260 return PKEY_Media_Duration;
261 case QMediaMetaData::Key::Language:
262 return PKEY_Language;
263 case QMediaMetaData::Key::Description:
264 return PKEY_Media_EncodingSettings;
265 case QMediaMetaData::Key::AudioBitRate:
266 return PKEY_Audio_EncodingBitrate;
267 case QMediaMetaData::Key::ContributingArtist:
268 return PKEY_Music_Artist;
269 case QMediaMetaData::Key::ThumbnailImage:
270 return PKEY_ThumbnailStream;
271 case QMediaMetaData::Key::Orientation:
272 return PKEY_Video_Orientation;
273 case QMediaMetaData::Key::VideoFrameRate:
274 return PKEY_Video_FrameRate;
275 case QMediaMetaData::Key::VideoBitRate:
276 return PKEY_Video_EncodingBitrate;
277 case QMediaMetaData::MediaType:
278 return PKEY_Media_ClassPrimaryID;
279 default:
280 return PROP_KEY_NULL;
281 }
282}
283
284static void setStringProperty(IPropertyStore *content, REFPROPERTYKEY key, const QString &value)
285{
286 PROPVARIANT propValue = {};
287 if (SUCCEEDED(InitPropVariantFromString(reinterpret_cast<LPCWSTR>(value.utf16()), &propValue))) {
288 if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
289 content->SetValue(key, propValue);
290 PropVariantClear(&propValue);
291 }
292}
293
294static void setUInt32Property(IPropertyStore *content, REFPROPERTYKEY key, quint32 value)
295{
296 PROPVARIANT propValue = {};
297 if (SUCCEEDED(InitPropVariantFromUInt32(ULONG(value), &propValue))) {
298 if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
299 content->SetValue(key, propValue);
300 PropVariantClear(&propValue);
301 }
302}
303
304static void setUInt64Property(IPropertyStore *content, REFPROPERTYKEY key, quint64 value)
305{
306 PROPVARIANT propValue = {};
307 if (SUCCEEDED(InitPropVariantFromUInt64(ULONGLONG(value), &propValue))) {
308 if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
309 content->SetValue(key, propValue);
310 PropVariantClear(&propValue);
311 }
312}
313
314static void setFileTimeProperty(IPropertyStore *content, REFPROPERTYKEY key, const FILETIME *ft)
315{
316 PROPVARIANT propValue = {};
317 if (SUCCEEDED(InitPropVariantFromFileTime(ft, &propValue))) {
318 if (SUCCEEDED(PSCoerceToCanonicalValue(key, &propValue)))
319 content->SetValue(key, propValue);
320 PropVariantClear(&propValue);
321 }
322}
323
324void MFMetaData::toNative(const QMediaMetaData &metaData, IPropertyStore *content)
325{
326 if (content) {
327
328 for (const auto &key : metaData.keys()) {
329
330 QVariant value = metaData.value(key);
331
332 if (key == QMediaMetaData::Key::MediaType) {
333
334 QString strValue = metaData.stringValue(key);
335 QString v;
336
337 // Sets property to one of the MediaClassPrimaryID values defined by Microsoft:
338 // https://docs.microsoft.com/en-us/windows/win32/wmformat/wm-mediaprimaryid
339 if (strValue == QLatin1String("Music"))
340 v = QLatin1String("{D1607DBC-E323-4BE2-86A1-48A42A28441E}");
341 else if (strValue == QLatin1String("Video"))
342 v = QLatin1String("{DB9830BD-3AB3-4FAB-8A37-1A995F7FF74B}");
343 else if (strValue == QLatin1String("Audio"))
344 v = QLatin1String("{01CD0F29-DA4E-4157-897B-6275D50C4F11}");
345 else
346 v = QLatin1String("{FCF24A76-9A57-4036-990D-E35DD8B244E1}");
347
348 setStringProperty(content, PKEY_Media_ClassPrimaryID, v);
349
350 } else if (key == QMediaMetaData::Key::Duration) {
351
352 setUInt64Property(content, PKEY_Media_Duration, value.toULongLong() * 10000);
353
354 } else if (key == QMediaMetaData::Key::Resolution) {
355
356 QSize res = value.toSize();
357 setUInt32Property(content, PKEY_Video_FrameWidth, quint32(res.width()));
358 setUInt32Property(content, PKEY_Video_FrameHeight, quint32(res.height()));
359
360 } else if (key == QMediaMetaData::Key::Orientation) {
361
362 setUInt32Property(content, PKEY_Video_Orientation, value.toUInt());
363
364 } else if (key == QMediaMetaData::Key::VideoFrameRate) {
365
366 qreal fps = value.toReal();
367 setUInt32Property(content, PKEY_Video_FrameRate, quint32(fps * 1000));
368
369 } else if (key == QMediaMetaData::Key::TrackNumber) {
370
371 setUInt32Property(content, PKEY_Music_TrackNumber, value.toUInt());
372
373 } else if (key == QMediaMetaData::Key::AudioBitRate) {
374
375 setUInt32Property(content, PKEY_Audio_EncodingBitrate, value.toUInt());
376
377 } else if (key == QMediaMetaData::Key::VideoBitRate) {
378
379 setUInt32Property(content, PKEY_Video_EncodingBitrate, value.toUInt());
380
381 } else if (key == QMediaMetaData::Key::Date) {
382
383 // Convert QDateTime to FILETIME by converting to 100-nsecs since
384 // 01/01/1970 UTC and adding the difference from 1601 to 1970.
385 ULARGE_INTEGER t = {};
386 t.QuadPart = ULONGLONG(value.toDateTime().toUTC().toMSecsSinceEpoch() * 10000
387 + 116444736000000000LL);
388
389 FILETIME ft = {};
390 ft.dwHighDateTime = t.HighPart;
391 ft.dwLowDateTime = t.LowPart;
392
393 setFileTimeProperty(content, PKEY_Media_DateEncoded, &ft);
394
395 } else {
396
397 // By default use as string and let PSCoerceToCanonicalValue()
398 // do validation and type conversion.
399 REFPROPERTYKEY propKey = propertyKeyForMetaDataKey(key);
400
401 if (propKey != PROP_KEY_NULL) {
402 QString strValue = metaData.stringValue(key);
403 if (!strValue.isEmpty())
404 setStringProperty(content, propKey, strValue);
405 }
406 }
407 }
408 }
409}
\inmodule QtMultimedia
static QVariant metaDataValue(IPropertyStore *content, const PROPERTYKEY &key)
static const PROPERTYKEY PROP_KEY_NULL
static void setUInt32Property(IPropertyStore *content, REFPROPERTYKEY key, quint32 value)
static void setUInt64Property(IPropertyStore *content, REFPROPERTYKEY key, quint64 value)
static QVariant convertValue(const PROPVARIANT &var)
static void setFileTimeProperty(IPropertyStore *content, REFPROPERTYKEY key, const FILETIME *ft)
static REFPROPERTYKEY propertyKeyForMetaDataKey(QMediaMetaData::Key key)
static void setStringProperty(IPropertyStore *content, REFPROPERTYKEY key, const QString &value)