UnrealEngineでWindowsのファイルのプロパティから動画の再生時間を取得するのをやってみたのでメモしておきます。
試した環境
- UE5.2.1
- Windows11
参照サイト
- Windows-classic-samples/Samples/Win7Samples/winui/shell/appplatform/PropertyEdit/PropertyEdit.cpp at main · microsoft/Windows-classic-samples · GitHub
- c# - Get video duration without playing? - Stack Overflow
- c# - How to get video duration from mp4, wmv, flv, mov videos - Stack Overflow
試したコード
[プロジェクト名].build.cs
if (Target.Platform == UnrealTargetPlatform.Win64)
{
PublicSystemLibraries.AddRange(new string[] { "Propsys.lib" });
}
コード本体
#include "Windows/AllowWindowsPlatformTypes.h"
#include "Windows/AllowWindowsPlatformAtomics.h"
#include "Windows/PreWindowsApi.h"
#include <shobjidl.h>
#include <propsys.h>
#include <propvarutil.h>
#include <propkey.h>
#include <strsafe.h>
#include "Windows/PostWindowsApi.h"
#include "Windows/HideWindowsPlatformAtomics.h"
#include "Windows/HideWindowsPlatformTypes.h"
int UPlatformUtils::GetMediaDurationFromFile(FString Filename)
{
int Duration = -1;
#if PLATFORM_WINDOWS
// Convert the Canonical name of the property to PROPERTYKEY
PROPERTYKEY key;
HRESULT hr = PSGetPropertyKeyFromName(TEXT("System.Media.Duration"), &key);
if (!SUCCEEDED(hr))
{
UE_LOG(LogTemp, Error, TEXT("Invalid property specified: System.Media.Duration"));
return Duration;
}
IPropertyStore* pps = NULL;
WCHAR szExpanded[MAX_PATH];
hr = ExpandEnvironmentStrings(*Filename, szExpanded, ARRAYSIZE(szExpanded)) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
if (!SUCCEEDED(hr))
{
UE_LOG(LogTemp, Error, TEXT("Error %x: failed to ExpandEnvironmentStrings"), hr);
return Duration;
}
WCHAR szAbsPath[MAX_PATH];
hr = _wfullpath(szAbsPath, szExpanded, ARRAYSIZE(szAbsPath)) ? S_OK : E_FAIL;
if (!SUCCEEDED(hr))
{
UE_LOG(LogTemp, Error, TEXT("Error %x: failed to _wfullpath"), hr);
return Duration;
}
hr = SHGetPropertyStoreFromParsingName(szAbsPath, NULL, GPS_DEFAULT, IID_PPV_ARGS(&pps));
if (!SUCCEEDED(hr))
{
UE_LOG(LogTemp, Error, TEXT("Error %x: getting the propertystore for the item."), hr);
return Duration;
}
TSharedPtr<IPropertyStore> Pps(pps, [](IPropertyStore* ptr) {
if (ptr) ptr->Release();
});
PROPVARIANT propvarValue = { 0 };
hr = Pps->GetValue(key, &propvarValue);
if (!SUCCEEDED(hr))
{
UE_LOG(LogTemp, Error, TEXT("Error %x: cannot get value"), hr);
return Duration;
}
TSharedPtr<PROPVARIANT> PropvarValue(&propvarValue, [](PROPVARIANT* ptr) {
if (ptr) PropVariantClear(ptr);
});
PWSTR pszDisplayValue = NULL;
hr = PSFormatForDisplayAlloc(key, propvarValue, PDFF_DEFAULT, &pszDisplayValue);
if (!SUCCEEDED(hr))
{
UE_LOG(LogTemp, Error, TEXT("Error %x: failed to PSFormatForDisplayAlloc."), hr);
return Duration;
}
TSharedPtr<WCHAR> PszDisplayValue(pszDisplayValue, [](PWSTR ptr) {
if (ptr) CoTaskMemFree(ptr);
});
FString DurationStr = PszDisplayValue.Get();
const FRegexPattern Pattern = FRegexPattern(TEXT("^([0-9]*):([0-9]*):([0-9]*)$"));
FRegexMatcher Matcher(Pattern, DurationStr);
if (Matcher.FindNext())
{
Duration = FCString::Atoi(*Matcher.GetCaptureGroup(3));
Duration += FCString::Atoi(*Matcher.GetCaptureGroup(2)) * 60;
Duration += FCString::Atoi(*Matcher.GetCaptureGroup(1)) * 60 * 60;
}
else
{
UE_LOG(LogTemp, Error, TEXT("Error: duration format is invalid %s"), *DurationStr);
}
#endif
return Duration;
}