blob: d9864870a5763df91b1174e69f28c7e580d0097f [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <dshow.h>
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#include <QtMultimedia/qmediametadata.h>
#include <QtCore/qcoreapplication.h>
#include <QSize>
#include <qdatetime.h>
#include <qimage.h>
#include <initguid.h>
#include <qnetwork.h>
#include "directshowmetadatacontrol.h"
#include "directshowplayerservice.h"
#include <QtMultimedia/private/qtmultimedia-config_p.h>
#if QT_CONFIG(wmsdk)
#include <wmsdk.h>
#endif
#if QT_CONFIG(wshellitem)
#include <shlobj.h>
#include <propkeydef.h>
#include <private/qsystemlibrary_p.h>
DEFINE_PROPERTYKEY(PKEY_Author, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 4);
DEFINE_PROPERTYKEY(PKEY_Title, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 2);
DEFINE_PROPERTYKEY(PKEY_Media_SubTitle, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 38);
DEFINE_PROPERTYKEY(PKEY_ParentalRating, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 21);
DEFINE_PROPERTYKEY(PKEY_Comment, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 6);
DEFINE_PROPERTYKEY(PKEY_Copyright, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 11);
DEFINE_PROPERTYKEY(PKEY_Media_ProviderStyle, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 40);
DEFINE_PROPERTYKEY(PKEY_Media_Year, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 5);
DEFINE_PROPERTYKEY(PKEY_Media_DateEncoded, 0x2E4B640D, 0x5019, 0x46D8, 0x88, 0x81, 0x55, 0x41, 0x4C, 0xC5, 0xCA, 0xA0, 100);
DEFINE_PROPERTYKEY(PKEY_Rating, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 9);
DEFINE_PROPERTYKEY(PKEY_Keywords, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 5);
DEFINE_PROPERTYKEY(PKEY_Language, 0xD5CDD502, 0x2E9C, 0x101B, 0x93, 0x97, 0x08, 0x00, 0x2B, 0x2C, 0xF9, 0xAE, 28);
DEFINE_PROPERTYKEY(PKEY_Media_Publisher, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 30);
DEFINE_PROPERTYKEY(PKEY_Media_Duration, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 3);
DEFINE_PROPERTYKEY(PKEY_Audio_EncodingBitrate, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 4);
DEFINE_PROPERTYKEY(PKEY_Media_AverageLevel, 0x09EDD5B6, 0xB301, 0x43C5, 0x99, 0x90, 0xD0, 0x03, 0x02, 0xEF, 0xFD, 0x46, 100);
DEFINE_PROPERTYKEY(PKEY_Audio_ChannelCount, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 7);
DEFINE_PROPERTYKEY(PKEY_Audio_PeakValue, 0x2579E5D0, 0x1116, 0x4084, 0xBD, 0x9A, 0x9B, 0x4F, 0x7C, 0xB4, 0xDF, 0x5E, 100);
DEFINE_PROPERTYKEY(PKEY_Audio_SampleRate, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 5);
DEFINE_PROPERTYKEY(PKEY_Audio_Format, 0x64440490, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 2);
DEFINE_PROPERTYKEY(PKEY_Music_AlbumTitle, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 4);
DEFINE_PROPERTYKEY(PKEY_Music_AlbumArtist, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 13);
DEFINE_PROPERTYKEY(PKEY_Music_Artist, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 2);
DEFINE_PROPERTYKEY(PKEY_Music_Composer, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 19);
DEFINE_PROPERTYKEY(PKEY_Music_Conductor, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 36);
DEFINE_PROPERTYKEY(PKEY_Music_Lyrics, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 12);
DEFINE_PROPERTYKEY(PKEY_Music_Mood, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 39);
DEFINE_PROPERTYKEY(PKEY_Music_TrackNumber, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 7);
DEFINE_PROPERTYKEY(PKEY_Music_Genre, 0x56A3372E, 0xCE9C, 0x11D2, 0x9F, 0x0E, 0x00, 0x60, 0x97, 0xC6, 0x86, 0xF6, 11);
DEFINE_PROPERTYKEY(PKEY_ThumbnailStream, 0xF29F85E0, 0x4FF9, 0x1068, 0xAB, 0x91, 0x08, 0x00, 0x2B, 0x27, 0xB3, 0xD9, 27);
DEFINE_PROPERTYKEY(PKEY_Video_FrameHeight, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 4);
DEFINE_PROPERTYKEY(PKEY_Video_FrameWidth, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 3);
DEFINE_PROPERTYKEY(PKEY_Video_HorizontalAspectRatio, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 42);
DEFINE_PROPERTYKEY(PKEY_Video_VerticalAspectRatio, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 45);
DEFINE_PROPERTYKEY(PKEY_Video_FrameRate, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 6);
DEFINE_PROPERTYKEY(PKEY_Video_EncodingBitrate, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 8);
DEFINE_PROPERTYKEY(PKEY_Video_Director, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 20);
DEFINE_PROPERTYKEY(PKEY_Video_Compression, 0x64440491, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 10);
DEFINE_PROPERTYKEY(PKEY_Media_Writer, 0x64440492, 0x4C8B, 0x11D1, 0x8B, 0x70, 0x08, 0x00, 0x36, 0xB1, 0x1A, 0x03, 23);
static QString nameForGUIDString(const QString &guid)
{
// Audio formats
if (guid == "{00001610-0000-0010-8000-00AA00389B71}" || guid == "{000000FF-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG AAC Audio");
if (guid == "{00001600-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG ADTS AAC Audio");
if (guid == "{00000092-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Dolby AC-3 SPDIF");
if (guid == "{E06D802C-DB46-11CF-B4D1-00805F6CBBEA}" || guid == "{00002000-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Dolby AC-3");
if (guid == "{A7FB87AF-2D02-42FB-A4D4-05CD93843BDD}")
return QStringLiteral("Dolby Digital Plus");
if (guid == "{00000009-0000-0010-8000-00AA00389B71}")
return QStringLiteral("DRM");
if (guid == "{00000008-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Digital Theater Systems Audio (DTS)");
if (guid == "{00000003-0000-0010-8000-00AA00389B71}")
return QStringLiteral("IEEE Float Audio");
if (guid == "{00000055-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG Audio Layer-3 (MP3)");
if (guid == "{00000050-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG-1 Audio");
if (guid == "{2E6D7033-767A-494D-B478-F29D25DC9037}")
return QStringLiteral("MPEG Audio Layer 1/2");
if (guid == "{0000000A-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Audio Voice");
if (guid == "{00000001-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Uncompressed PCM Audio");
if (guid == "{00000164-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Audio 9 SPDIF");
if (guid == "{00000161-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Audio 8 (WMA2)");
if (guid == "{00000162-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Audio 9 (WMA3");
if (guid == "{00000163-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Audio 9 Lossless");
if (guid == "{8D2FD10B-5841-4a6b-8905-588FEC1ADED9}")
return QStringLiteral("Vorbis");
if (guid == "{0000F1AC-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Free Lossless Audio Codec (FLAC)");
if (guid == "{00006C61-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Apple Lossless Audio Codec (ALAC)");
// Video formats
if (guid == "{35327664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("DVCPRO 25 (DV25)");
if (guid == "{30357664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("DVCPRO 50 (DV50)");
if (guid == "{20637664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("DVC/DV Video");
if (guid == "{31687664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("DVCPRO 100 (DVH1)");
if (guid == "{64687664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("HD-DVCR (DVHD)");
if (guid == "{64737664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("SDL-DVCR (DVSD)");
if (guid == "{6C737664-0000-0010-8000-00AA00389B71}")
return QStringLiteral("SD-DVCR (DVSL)");
if (guid == "{33363248-0000-0010-8000-00AA00389B71}")
return QStringLiteral("H.263 Video");
if (guid == "{34363248-0000-0010-8000-00AA00389B71}")
return QStringLiteral("H.264 Video");
if (guid == "{35363248-0000-0010-8000-00AA00389B71}")
return QStringLiteral("H.265 Video");
if (guid == "{43564548-0000-0010-8000-00AA00389B71}")
return QStringLiteral("High Efficiency Video Coding (HEVC)");
if (guid == "{3253344D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG-4 part 2 Video (M4S2)");
if (guid == "{47504A4D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Motion JPEG (MJPG)");
if (guid == "{3334504D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Microsoft MPEG 4 version 3 (MP43)");
if (guid == "{5334504D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("ISO MPEG 4 version 1 (MP4S)");
if (guid == "{5634504D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG-4 part 2 Video (MP4V)");
if (guid == "{E06D8026-DB46-11CF-B4D1-00805F6CBBEA}")
return QStringLiteral("MPEG-2 Video");
if (guid == "{3147504D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("MPEG-1 Video");
if (guid == "{3153534D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Screen 1 (MSS1)");
if (guid == "{3253534D-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Video 9 Screen (MSS2)");
if (guid == "{31564D57-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Video 7 (WMV1)");
if (guid == "{32564D57-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Video 8 (WMV2)");
if (guid == "{33564D57-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Video 9 (WMV3)");
if (guid == "{31435657-0000-0010-8000-00AA00389B71}")
return QStringLiteral("Windows Media Video VC1 (WVC1)");
if (guid == "{30385056-0000-0010-8000-00AA00389B71}")
return QStringLiteral("VP8 Video");
if (guid == "{30395056-0000-0010-8000-00AA00389B71}")
return QStringLiteral("VP9 Video");
return QStringLiteral("Unknown codec");
}
typedef HRESULT (WINAPI *q_SHCreateItemFromParsingName)(PCWSTR, IBindCtx *, const GUID&, void **);
static q_SHCreateItemFromParsingName sHCreateItemFromParsingName = nullptr;
#endif
#if QT_CONFIG(wmsdk)
namespace
{
struct QWMMetaDataKey
{
QString qtName;
const wchar_t *wmName;
QWMMetaDataKey(const QString &qtn, const wchar_t *wmn) : qtName(qtn), wmName(wmn) { }
};
}
using QWMMetaDataKeys = QList<QWMMetaDataKey>;
Q_GLOBAL_STATIC(QWMMetaDataKeys, metadataKeys)
static const QWMMetaDataKeys *qt_wmMetaDataKeys()
{
if (metadataKeys->isEmpty()) {
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Title, L"Title"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::SubTitle, L"WM/SubTitle"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Author, L"Author"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Comment, L"Comment"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Description, L"Description"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Category, L"WM/Category"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Genre, L"WM/Genre"));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Date, 0));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Year, L"WM/Year"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::UserRating, L"Rating"));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::MetaDatawords, 0));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Language, L"WM/Language"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Publisher, L"WM/Publisher"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Copyright, L"Copyright"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ParentalRating, L"WM/ParentalRating"));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::RatingOrganisation, L"RatingOrganisation"));
// Media
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Size, L"FileSize"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::MediaType, L"MediaType"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Duration, L"Duration"));
// Audio
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AudioBitRate, L"AudioBitRate"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AudioCodec, L"AudioCodec"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ChannelCount, L"ChannelCount"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::SampleRate, L"Frequency"));
// Music
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AlbumTitle, L"WM/AlbumTitle"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::AlbumArtist, L"WM/AlbumArtist"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ContributingArtist, L"Author"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Composer, L"WM/Composer"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Conductor, L"WM/Conductor"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Lyrics, L"WM/Lyrics"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Mood, L"WM/Mood"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::TrackNumber, L"WM/TrackNumber"));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::TrackCount, 0));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::CoverArtUriSmall, 0));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::CoverArtUriLarge, 0));
// Image/Video
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Resolution, L"WM/VideoHeight"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::PixelAspectRatio, L"AspectRatioX"));
// Video
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::VideoFrameRate, L"WM/VideoFrameRate"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::VideoBitRate, L"VideoBitRate"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::VideoCodec, L"VideoCodec"));
//metadataKeys->append(QWMMetaDataKey(QMediaMetaData::PosterUri, 0));
// Movie
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::ChapterNumber, L"ChapterNumber"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Director, L"WM/Director"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::LeadPerformer, L"LeadPerformer"));
metadataKeys->append(QWMMetaDataKey(QMediaMetaData::Writer, L"WM/Writer"));
}
return metadataKeys;
}
static QVariant getValue(IWMHeaderInfo *header, const wchar_t *key)
{
WORD streamNumber = 0;
WMT_ATTR_DATATYPE type = WMT_TYPE_DWORD;
WORD size = 0;
if (header->GetAttributeByName(&streamNumber, key, &type, nullptr, &size) == S_OK) {
switch (type) {
case WMT_TYPE_DWORD:
if (size == sizeof(DWORD)) {
DWORD word;
if (header->GetAttributeByName(
&streamNumber,
key,
&type,
reinterpret_cast<BYTE *>(&word),
&size) == S_OK) {
return int(word);
}
}
break;
case WMT_TYPE_STRING:
{
QString string;
string.resize(size / 2); // size is in bytes, string is in UTF16
if (header->GetAttributeByName(
&streamNumber,
key,
&type,
reinterpret_cast<BYTE *>(const_cast<ushort *>(string.utf16())),
&size) == S_OK) {
return string;
}
}
break;
case WMT_TYPE_BINARY:
{
QByteArray bytes;
bytes.resize(size);
if (header->GetAttributeByName(
&streamNumber,
key,
&type,
reinterpret_cast<BYTE *>(bytes.data()),
&size) == S_OK) {
return bytes;
}
}
break;
case WMT_TYPE_BOOL:
if (size == sizeof(DWORD)) {
DWORD word;
if (header->GetAttributeByName(
&streamNumber,
key,
&type,
reinterpret_cast<BYTE *>(&word),
&size) == S_OK) {
return bool(word);
}
}
break;
case WMT_TYPE_QWORD:
if (size == sizeof(QWORD)) {
QWORD word;
if (header->GetAttributeByName(
&streamNumber,
key,
&type,
reinterpret_cast<BYTE *>(&word),
&size) == S_OK) {
return qint64(word);
}
}
break;
case WMT_TYPE_WORD:
if (size == sizeof(WORD)){
WORD word;
if (header->GetAttributeByName(
&streamNumber,
key,
&type,
reinterpret_cast<BYTE *>(&word),
&size) == S_OK) {
return short(word);
}
}
break;
case WMT_TYPE_GUID:
if (size == 16) {
}
break;
default:
break;
}
}
return QVariant();
}
#endif
#if QT_CONFIG(wshellitem)
static QVariant convertValue(const PROPVARIANT& var)
{
QVariant value;
switch (var.vt) {
case VT_LPWSTR:
value = QString::fromUtf16(reinterpret_cast<const ushort*>(var.pwszVal));
break;
case VT_UI4:
value = uint(var.ulVal);
break;
case VT_UI8:
value = qulonglong(var.uhVal.QuadPart);
break;
case VT_BOOL:
value = bool(var.boolVal);
break;
case VT_FILETIME:
SYSTEMTIME sysDate;
if (!FileTimeToSystemTime(&var.filetime, &sysDate))
break;
value = QDate(sysDate.wYear, sysDate.wMonth, sysDate.wDay);
break;
case VT_STREAM:
{
STATSTG stat;
if (FAILED(var.pStream->Stat(&stat, STATFLAG_NONAME)))
break;
void *data = malloc(stat.cbSize.QuadPart);
ULONG read = 0;
if (FAILED(var.pStream->Read(data, stat.cbSize.QuadPart, &read))) {
free(data);
break;
}
value = QImage::fromData(reinterpret_cast<const uchar*>(data), read);
free(data);
}
break;
case VT_VECTOR | VT_LPWSTR:
QStringList vList;
for (ULONG i = 0; i < var.calpwstr.cElems; ++i)
vList.append(QString::fromUtf16(reinterpret_cast<const ushort*>(var.calpwstr.pElems[i])));
value = vList;
break;
}
return value;
}
#endif
DirectShowMetaDataControl::DirectShowMetaDataControl(QObject *parent)
: QMetaDataReaderControl(parent)
{
}
DirectShowMetaDataControl::~DirectShowMetaDataControl() = default;
bool DirectShowMetaDataControl::isMetaDataAvailable() const
{
return m_available;
}
QVariant DirectShowMetaDataControl::metaData(const QString &key) const
{
return m_metadata.value(key);
}
QStringList DirectShowMetaDataControl::availableMetaData() const
{
return m_metadata.keys();
}
static QString convertBSTR(BSTR *string)
{
QString value = QString::fromUtf16(reinterpret_cast<ushort *>(*string),
::SysStringLen(*string));
::SysFreeString(*string);
string = nullptr;
return value;
}
void DirectShowMetaDataControl::setMetadata(const QVariantMap &metadata)
{
m_metadata = metadata;
setMetadataAvailable(!m_metadata.isEmpty());
}
void DirectShowMetaDataControl::updateMetadata(const QString &fileSrc, QVariantMap &metadata)
{
#if QT_CONFIG(wshellitem)
if (!sHCreateItemFromParsingName) {
QSystemLibrary lib(QStringLiteral("shell32"));
sHCreateItemFromParsingName = (q_SHCreateItemFromParsingName)(lib.resolve("SHCreateItemFromParsingName"));
}
if (!fileSrc.isEmpty() && sHCreateItemFromParsingName) {
IShellItem2* shellItem = nullptr;
if (sHCreateItemFromParsingName(reinterpret_cast<const WCHAR*>(fileSrc.utf16()),
nullptr, IID_PPV_ARGS(&shellItem)) == S_OK) {
IPropertyStore *pStore = nullptr;
if (shellItem->GetPropertyStore(GPS_DEFAULT, IID_PPV_ARGS(&pStore)) == S_OK) {
DWORD cProps;
if (SUCCEEDED(pStore->GetCount(&cProps))) {
for (DWORD i = 0; i < cProps; ++i)
{
PROPERTYKEY key;
PROPVARIANT var;
PropVariantInit(&var);
if (FAILED(pStore->GetAt(i, &key)))
continue;
if (FAILED(pStore->GetValue(key, &var)))
continue;
if (IsEqualPropertyKey(key, PKEY_Author)) {
metadata.insert(QMediaMetaData::Author, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Title)) {
metadata.insert(QMediaMetaData::Title, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_SubTitle)) {
metadata.insert(QMediaMetaData::SubTitle, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_ParentalRating)) {
metadata.insert(QMediaMetaData::ParentalRating, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Comment)) {
metadata.insert(QMediaMetaData::Description, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Copyright)) {
metadata.insert(QMediaMetaData::Copyright, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_ProviderStyle)) {
metadata.insert(QMediaMetaData::Genre, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_Year)) {
metadata.insert(QMediaMetaData::Year, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_DateEncoded)) {
metadata.insert(QMediaMetaData::Date, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Rating)) {
metadata.insert(QMediaMetaData::UserRating,
int((convertValue(var).toUInt() - 1) / qreal(98) * 100));
} else if (IsEqualPropertyKey(key, PKEY_Keywords)) {
metadata.insert(QMediaMetaData::Keywords, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Language)) {
metadata.insert(QMediaMetaData::Language, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_Publisher)) {
metadata.insert(QMediaMetaData::Publisher, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_Duration)) {
metadata.insert(QMediaMetaData::Duration,
(convertValue(var).toLongLong() + 10000) / 10000);
} else if (IsEqualPropertyKey(key, PKEY_Audio_EncodingBitrate)) {
metadata.insert(QMediaMetaData::AudioBitRate, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_AverageLevel)) {
metadata.insert(QMediaMetaData::AverageLevel, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Audio_ChannelCount)) {
metadata.insert(QMediaMetaData::ChannelCount, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Audio_PeakValue)) {
metadata.insert(QMediaMetaData::PeakValue, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Audio_SampleRate)) {
metadata.insert(QMediaMetaData::SampleRate, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_AlbumTitle)) {
metadata.insert(QMediaMetaData::AlbumTitle, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_AlbumArtist)) {
metadata.insert(QMediaMetaData::AlbumArtist, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_Artist)) {
metadata.insert(QMediaMetaData::ContributingArtist, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_Composer)) {
metadata.insert(QMediaMetaData::Composer, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_Conductor)) {
metadata.insert(QMediaMetaData::Conductor, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_Lyrics)) {
metadata.insert(QMediaMetaData::Lyrics, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_Mood)) {
metadata.insert(QMediaMetaData::Mood, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_TrackNumber)) {
metadata.insert(QMediaMetaData::TrackNumber, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Music_Genre)) {
metadata.insert(QMediaMetaData::Genre, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_ThumbnailStream)) {
metadata.insert(QMediaMetaData::ThumbnailImage, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Video_FrameHeight)) {
QSize res;
res.setHeight(convertValue(var).toUInt());
if (SUCCEEDED(pStore->GetValue(PKEY_Video_FrameWidth, &var)))
res.setWidth(convertValue(var).toUInt());
metadata.insert(QMediaMetaData::Resolution, res);
} else if (IsEqualPropertyKey(key, PKEY_Video_HorizontalAspectRatio)) {
QSize aspectRatio;
aspectRatio.setWidth(convertValue(var).toUInt());
if (SUCCEEDED(pStore->GetValue(PKEY_Video_VerticalAspectRatio, &var)))
aspectRatio.setHeight(convertValue(var).toUInt());
metadata.insert(QMediaMetaData::PixelAspectRatio, aspectRatio);
} else if (IsEqualPropertyKey(key, PKEY_Video_FrameRate)) {
metadata.insert(QMediaMetaData::VideoFrameRate,
convertValue(var).toReal() / 1000);
} else if (IsEqualPropertyKey(key, PKEY_Video_EncodingBitrate)) {
metadata.insert(QMediaMetaData::VideoBitRate, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Video_Director)) {
metadata.insert(QMediaMetaData::Director, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Media_Writer)) {
metadata.insert(QMediaMetaData::Writer, convertValue(var));
} else if (IsEqualPropertyKey(key, PKEY_Video_Compression)) {
metadata.insert(QMediaMetaData::VideoCodec, nameForGUIDString(convertValue(var).toString()));
} else if (IsEqualPropertyKey(key, PKEY_Audio_Format)) {
metadata.insert(QMediaMetaData::AudioCodec, nameForGUIDString(convertValue(var).toString()));
}
PropVariantClear(&var);
}
}
pStore->Release();
}
shellItem->Release();
}
}
#else
Q_UNUSED(fileSrc);
Q_UNUSED(metadata);
#endif
}
void DirectShowMetaDataControl::updateMetadata(IFilterGraph2 *graph, IBaseFilter *source, QVariantMap &metadata)
{
#if QT_CONFIG(wmsdk)
if (IWMHeaderInfo *info = com_cast<IWMHeaderInfo>(source, IID_IWMHeaderInfo)) {
const auto keys = *qt_wmMetaDataKeys();
for (const QWMMetaDataKey &key : keys) {
QVariant var = getValue(info, key.wmName);
if (var.isValid()) {
if (key.qtName == QMediaMetaData::Duration) {
// duration is provided in 100-nanosecond units, convert to milliseconds
var = (var.toLongLong() + 10000) / 10000;
} else if (key.qtName == QMediaMetaData::Resolution) {
QSize res;
res.setHeight(var.toUInt());
res.setWidth(getValue(info, L"WM/VideoWidth").toUInt());
var = res;
} else if (key.qtName == QMediaMetaData::VideoFrameRate) {
var = var.toReal() / 1000.f;
} else if (key.qtName == QMediaMetaData::PixelAspectRatio) {
QSize aspectRatio;
aspectRatio.setWidth(var.toUInt());
aspectRatio.setHeight(getValue(info, L"AspectRatioY").toUInt());
var = aspectRatio;
} else if (key.qtName == QMediaMetaData::UserRating) {
var = (var.toUInt() - 1) / qreal(98) * 100;
}
metadata.insert(key.qtName, var);
}
}
info->Release();
}
if (!metadata.isEmpty())
return;
#endif
{
IAMMediaContent *content = nullptr;
if ((!graph || graph->QueryInterface(
IID_IAMMediaContent, reinterpret_cast<void **>(&content)) != S_OK)
&& (!source || source->QueryInterface(
IID_IAMMediaContent, reinterpret_cast<void **>(&content)) != S_OK)) {
content = nullptr;
}
if (content) {
BSTR string = nullptr;
if (content->get_AuthorName(&string) == S_OK)
metadata.insert(QMediaMetaData::Author, convertBSTR(&string));
if (content->get_Title(&string) == S_OK)
metadata.insert(QMediaMetaData::Title, convertBSTR(&string));
if (content->get_Description(&string) == S_OK)
metadata.insert(QMediaMetaData::Description, convertBSTR(&string));
if (content->get_Rating(&string) == S_OK)
metadata.insert(QMediaMetaData::UserRating, convertBSTR(&string));
if (content->get_Copyright(&string) == S_OK)
metadata.insert(QMediaMetaData::Copyright, convertBSTR(&string));
content->Release();
}
}
}
void DirectShowMetaDataControl::setMetadataAvailable(bool available)
{
if (m_available == available)
return;
m_available = available;
// If the metadata is not available, notify about it immediately.
Qt::ConnectionType type = m_available ? Qt::QueuedConnection : Qt::AutoConnection;
QMetaObject::invokeMethod(this, "metaDataAvailableChanged", type, Q_ARG(bool, m_available));
// Currently the metadata is changed only with its availability.
QMetaObject::invokeMethod(this, "metaDataChanged", type);
}