| /**************************************************************************** |
| ** |
| ** 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); |
| } |