diff --git a/Ganimede/Ganimede/Ganimede.csproj b/Ganimede/Ganimede/Ganimede.csproj index 8e8115e..138a426 100644 --- a/Ganimede/Ganimede/Ganimede.csproj +++ b/Ganimede/Ganimede/Ganimede.csproj @@ -31,8 +31,5 @@ - - - diff --git a/Ganimede/Ganimede/Models/.keep b/Ganimede/Ganimede/Models/.keep deleted file mode 100644 index 24a1d50..0000000 --- a/Ganimede/Ganimede/Models/.keep +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ganimede.Models -{ - // Modelli futuri (es. VideoInfo, FrameInfo) -} diff --git a/Ganimede/Ganimede/Scripts/download-ffmpeg.ps1 b/Ganimede/Ganimede/Scripts/download-ffmpeg.ps1 deleted file mode 100644 index 8531c8a..0000000 --- a/Ganimede/Ganimede/Scripts/download-ffmpeg.ps1 +++ /dev/null @@ -1,75 +0,0 @@ -# Download FFmpeg binaries for Ganimede -# This script downloads FFmpeg shared libraries from official BtbN builds - -$ErrorActionPreference = "Stop" - -$ffmpegDir = Join-Path $PSScriptRoot "..\ffmpeg" -$tempZip = Join-Path $env:TEMP "ffmpeg-download.zip" -$tempExtract = Join-Path $env:TEMP "ffmpeg-extract" - -# FFmpeg download URL (latest 7.x shared build) -$ffmpegUrl = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl-shared.zip" - -Write-Host "Downloading FFmpeg libraries for Ganimede..." -ForegroundColor Cyan - -try { - # Check if already downloaded - if (Test-Path $ffmpegDir) { - $dllCount = (Get-ChildItem -Path $ffmpegDir -Filter "*.dll" -ErrorAction SilentlyContinue).Count - if ($dllCount -ge 5) { - Write-Host "FFmpeg libraries already exist ($dllCount DLLs found). Skipping download." -ForegroundColor Green - exit 0 - } - } - - Write-Host "Downloading from: $ffmpegUrl" -ForegroundColor Yellow - - # Download - $ProgressPreference = 'SilentlyContinue' - Invoke-WebRequest -Uri $ffmpegUrl -OutFile $tempZip -UseBasicParsing - $ProgressPreference = 'Continue' - - Write-Host "Download completed. Extracting..." -ForegroundColor Yellow - - # Clean temp folder - if (Test-Path $tempExtract) { - Remove-Item $tempExtract -Recurse -Force - } - New-Item -ItemType Directory -Path $tempExtract | Out-Null - - # Extract - Expand-Archive -Path $tempZip -DestinationPath $tempExtract -Force - - Write-Host "Extraction completed. Copying DLL files..." -ForegroundColor Yellow - - # Find bin folder - $binFolder = Get-ChildItem -Path $tempExtract -Filter "bin" -Recurse -Directory | Select-Object -First 1 - - if (-not $binFolder) { - throw "Could not find 'bin' folder in FFmpeg archive" - } - - # Create ffmpeg directory - if (-not (Test-Path $ffmpegDir)) { - New-Item -ItemType Directory -Path $ffmpegDir | Out-Null - } - - # Copy all DLL files - $dllFiles = Get-ChildItem -Path $binFolder.FullName -Filter "*.dll" - foreach ($dll in $dllFiles) { - Copy-Item -Path $dll.FullName -Destination $ffmpegDir -Force - Write-Host " Copied: $($dll.Name)" -ForegroundColor Green - } - - # Cleanup - Remove-Item $tempZip -Force -ErrorAction SilentlyContinue - Remove-Item $tempExtract -Recurse -Force -ErrorAction SilentlyContinue - - Write-Host "FFmpeg libraries installed successfully!" -ForegroundColor Green - Write-Host "Location: $ffmpegDir" -ForegroundColor Cyan - -} catch { - Write-Host "Error downloading FFmpeg: $_" -ForegroundColor Red - Write-Host "Please download manually from: https://github.com/BtbN/FFmpeg-Builds/releases" -ForegroundColor Yellow - exit 1 -} diff --git a/Ganimede/Ganimede/VideoProcessing/FrameExtractor.cs b/Ganimede/Ganimede/VideoProcessing/FrameExtractor.cs index 00682b5..4125b55 100644 --- a/Ganimede/Ganimede/VideoProcessing/FrameExtractor.cs +++ b/Ganimede/Ganimede/VideoProcessing/FrameExtractor.cs @@ -2,7 +2,7 @@ using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; -using System.Runtime.InteropServices; +using Ganimede.VideoProcessing.MediaFoundation; namespace Ganimede.VideoProcessing { @@ -25,14 +25,36 @@ namespace Ganimede.VideoProcessing if (!File.Exists(videoPath)) throw new FileNotFoundException($"Video file not found: {videoPath}"); - // For now, this is a placeholder implementation - // Full Windows Media Foundation frame extraction is very complex - // and requires several thousand lines of P/Invoke code - - throw new NotImplementedException( - "Frame extraction with Windows Media Foundation requires extensive implementation. " + - "This feature will be added in a future update. " + - "For now, please use alternative methods or third-party tools."); + try + { + using (var reader = new MFVideoReader(videoPath)) + { + // Read frame at specified time + using (var bitmap = reader.ReadFrameAtTime(timePosition)) + { + if (bitmap == null) + throw new InvalidOperationException($"Could not extract frame at position {timePosition}"); + + // Resize if needed + if (targetWidth > 0 && targetHeight > 0 && + (targetWidth != bitmap.Width || targetHeight != bitmap.Height)) + { + using (var resized = new Bitmap(bitmap, targetWidth, targetHeight)) + { + SaveBitmapAsPng(resized, outputPath); + } + } + else + { + SaveBitmapAsPng(bitmap, outputPath); + } + } + } + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to extract frame: {ex.Message}", ex); + } } /// @@ -53,10 +75,61 @@ namespace Ganimede.VideoProcessing if (!Directory.Exists(outputFolder)) Directory.CreateDirectory(outputFolder); - // For now, this is a placeholder implementation - throw new NotImplementedException( - "Full frame extraction with Windows Media Foundation requires extensive implementation. " + - "This feature will be added in a future update."); + try + { + using (var reader = new MFVideoReader(videoPath)) + { + // Calculate total frames + int totalFrames = (int)(reader.Duration.TotalSeconds * reader.FrameRate); + int frameIndex = 0; + TimeSpan currentTime = TimeSpan.Zero; + + while (true) + { + // Read next frame + using (var bitmap = reader.ReadFrame()) + { + if (bitmap == null) + break; // End of stream + + // Generate filename + var fileName = fileNameGenerator(frameIndex, currentTime); + var fullPath = Path.Combine(outputFolder, fileName); + + // Check if frame should be skipped + if (shouldSkipFrame != null && shouldSkipFrame(fullPath)) + { + frameIndex++; + currentTime = TimeSpan.FromSeconds(frameIndex / reader.FrameRate); + onProgress?.Invoke(frameIndex, totalFrames); + continue; + } + + // Resize if needed + if (targetWidth > 0 && targetHeight > 0 && + (targetWidth != bitmap.Width || targetHeight != bitmap.Height)) + { + using (var resized = new Bitmap(bitmap, targetWidth, targetHeight)) + { + SaveBitmapAsPng(resized, fullPath); + } + } + else + { + SaveBitmapAsPng(bitmap, fullPath); + } + + frameIndex++; + currentTime = TimeSpan.FromSeconds(frameIndex / reader.FrameRate); + onProgress?.Invoke(frameIndex, totalFrames); + } + } + } + } + catch (Exception ex) + { + throw new InvalidOperationException($"Failed to extract frames: {ex.Message}", ex); + } } /// diff --git a/Ganimede/Ganimede/VideoProcessing/MediaFoundation/MFInterfaces.cs b/Ganimede/Ganimede/VideoProcessing/MediaFoundation/MFInterfaces.cs new file mode 100644 index 0000000..c3551b5 --- /dev/null +++ b/Ganimede/Ganimede/VideoProcessing/MediaFoundation/MFInterfaces.cs @@ -0,0 +1,315 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ganimede.VideoProcessing.MediaFoundation +{ + /// + /// Additional Media Foundation COM interfaces for frame extraction + /// These interfaces extend the basic WMF functionality for video decoding + /// + + #region Source Reader Interfaces + + [ComImport, Guid("70ae66f2-c809-4e4f-8915-bdcb406b7993")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMFSourceReader + { + [PreserveSig] + int GetStreamSelection( + int dwStreamIndex, + out bool pfSelected); + + [PreserveSig] + int SetStreamSelection( + int dwStreamIndex, + bool fSelected); + + [PreserveSig] + int GetNativeMediaType( + int dwStreamIndex, + int dwMediaTypeIndex, + out IMFMediaType ppMediaType); + + [PreserveSig] + int GetCurrentMediaType( + int dwStreamIndex, + out IMFMediaType ppMediaType); + + [PreserveSig] + int SetCurrentMediaType( + int dwStreamIndex, + IntPtr pdwReserved, + IMFMediaType pMediaType); + + [PreserveSig] + int SetCurrentPosition( + Guid guidTimeFormat, + IntPtr varPosition); + + [PreserveSig] + int ReadSample( + int dwStreamIndex, + int dwControlFlags, + out int pdwActualStreamIndex, + out MF_SOURCE_READER_FLAG pdwStreamFlags, + out long pllTimestamp, + out IMFSample ppSample); + + [PreserveSig] + int Flush(int dwStreamIndex); + + [PreserveSig] + int GetServiceForStream( + int dwStreamIndex, + Guid guidService, + Guid riid, + out IntPtr ppvObject); + + [PreserveSig] + int GetPresentationAttribute( + int dwStreamIndex, + Guid guidAttribute, + IntPtr pvarAttribute); + } + + [Flags] + internal enum MF_SOURCE_READER_FLAG + { + None = 0, + Error = 0x00000001, + EndOfStream = 0x00000002, + NewStream = 0x00000004, + NativeMediaTypeChanged = 0x00000010, + CurrentMediaTypeChanged = 0x00000020, + StreamTick = 0x00000100, + AllEffectsRemoved = 0x00000200 + } + + internal static class MFSourceReaderIndex + { + public const int FirstVideoStream = unchecked((int)0xFFFFFFFC); + public const int FirstAudioStream = unchecked((int)0xFFFFFFFD); + public const int MediaSource = unchecked((int)0xFFFFFFFE); + public const int AnyStream = unchecked((int)0xFFFFFFFE); + } + + #endregion + + #region Sample and Buffer Interfaces + + [ComImport, Guid("c40a00f2-b93a-4d80-ae8c-5a1c634f58e4")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMFSample : IMFAttributes + { + #region IMFAttributes methods + [PreserveSig] new int GetItem(Guid guidKey, IntPtr pValue); + [PreserveSig] new int GetItemType(Guid guidKey, out ushort pType); + [PreserveSig] new int CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); + [PreserveSig] new int Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); + [PreserveSig] new int GetUINT32(Guid guidKey, out int punValue); + [PreserveSig] new int GetUINT64(Guid guidKey, out long punValue); + [PreserveSig] new int GetDouble(Guid guidKey, out double pfValue); + [PreserveSig] new int GetGUID(Guid guidKey, out Guid pguidValue); + [PreserveSig] new int GetStringLength(Guid guidKey, out int pcchLength); + [PreserveSig] new int GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); + [PreserveSig] new int GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); + [PreserveSig] new int GetBlobSize(Guid guidKey, out int pcbBlobSize); + [PreserveSig] new int GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); + [PreserveSig] new int GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); + [PreserveSig] new int GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); + [PreserveSig] new int SetItem(Guid guidKey, IntPtr Value); + [PreserveSig] new int DeleteItem(Guid guidKey); + [PreserveSig] new int DeleteAllItems(); + [PreserveSig] new int SetUINT32(Guid guidKey, int unValue); + [PreserveSig] new int SetUINT64(Guid guidKey, long unValue); + [PreserveSig] new int SetDouble(Guid guidKey, double fValue); + [PreserveSig] new int SetGUID(Guid guidKey, Guid guidValue); + [PreserveSig] new int SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); + [PreserveSig] new int SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); + [PreserveSig] new int SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); + [PreserveSig] new int LockStore(); + [PreserveSig] new int UnlockStore(); + [PreserveSig] new int GetCount(out int pcItems); + [PreserveSig] new int GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); + [PreserveSig] new int CopyAllItems(IMFAttributes pDest); + #endregion + + [PreserveSig] int GetSampleFlags(out int pdwSampleFlags); + [PreserveSig] int SetSampleFlags(int dwSampleFlags); + [PreserveSig] int GetSampleTime(out long phnsSampleTime); + [PreserveSig] int SetSampleTime(long hnsSampleTime); + [PreserveSig] int GetSampleDuration(out long phnsSampleDuration); + [PreserveSig] int SetSampleDuration(long hnsSampleDuration); + [PreserveSig] int GetBufferCount(out int pdwBufferCount); + [PreserveSig] int GetBufferByIndex(int dwIndex, out IMFMediaBuffer ppBuffer); + [PreserveSig] int ConvertToContiguousBuffer(out IMFMediaBuffer ppBuffer); + [PreserveSig] int AddBuffer(IMFMediaBuffer pBuffer); + [PreserveSig] int RemoveBufferByIndex(int dwIndex); + [PreserveSig] int RemoveAllBuffers(); + [PreserveSig] int GetTotalLength(out int pcbTotalLength); + [PreserveSig] int CopyToBuffer(IMFMediaBuffer pBuffer); + } + + [ComImport, Guid("045FA593-8799-42b8-BC8D-8968C6453507")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMFMediaBuffer + { + [PreserveSig] int Lock(out IntPtr ppbBuffer, out int pcbMaxLength, out int pcbCurrentLength); + [PreserveSig] int Unlock(); + [PreserveSig] int GetCurrentLength(out int pcbCurrentLength); + [PreserveSig] int SetCurrentLength(int cbCurrentLength); + [PreserveSig] int GetMaxLength(out int pcbMaxLength); + } + + [ComImport, Guid("7DC9D5F9-9ED9-44ec-9BBF-0600BB589FBB")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMF2DBuffer + { + [PreserveSig] int Lock2D(out IntPtr pbScanline0, out int plPitch); + [PreserveSig] int Unlock2D(); + [PreserveSig] int GetScanline0AndPitch(out IntPtr pbScanline0, out int plPitch); + [PreserveSig] int IsContiguousFormat(out bool pfIsContiguous); + [PreserveSig] int GetContiguousLength(out int pcbLength); + [PreserveSig] int ContiguousCopyTo(IntPtr pbDestBuffer, int cbDestBuffer); + [PreserveSig] int ContiguousCopyFrom(IntPtr pbSrcBuffer, int cbSrcBuffer); + } + + #endregion + + #region Helper Functions and Constants + + internal static class MFExternExtended + { + [DllImport("mfreadwrite.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFCreateSourceReaderFromURL( + [MarshalAs(UnmanagedType.LPWStr)] string pwszURL, + IMFAttributes pAttributes, + out IMFSourceReader ppSourceReader); + + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFCreateMediaType(out IMFMediaType ppMFType); + + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFCreateAttributes(out IMFAttributes ppMFAttributes, int cInitialSize); + + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFCreateMemoryBuffer(int cbMaxLength, out IMFMediaBuffer ppBuffer); + } + + internal static class MFVideoFormat + { + // Uncompressed RGB formats + public static readonly Guid RGB32 = new Guid("00000016-0000-0010-8000-00aa00389b71"); + public static readonly Guid RGB24 = new Guid("00000014-0000-0010-8000-00aa00389b71"); + public static readonly Guid RGB555 = new Guid("00000018-0000-0010-8000-00aa00389b71"); + public static readonly Guid RGB565 = new Guid("00000017-0000-0010-8000-00aa00389b71"); + + // YUV formats + public static readonly Guid NV12 = new Guid("3231564E-0000-0010-8000-00AA00389B71"); + public static readonly Guid YUY2 = new Guid("32595559-0000-0010-8000-00AA00389B71"); + public static readonly Guid UYVY = new Guid("59565955-0000-0010-8000-00AA00389B71"); + public static readonly Guid YV12 = new Guid("32315659-0000-0010-8000-00AA00389B71"); + public static readonly Guid I420 = new Guid("30323449-0000-0010-8000-00AA00389B71"); + } + + internal static class MFAttributesClsidExtended + { + // Source Reader attributes + public static readonly Guid MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING = + new Guid("fb394f3d-ccf1-42ee-bbb3-f9b845d5681d"); + public static readonly Guid MF_SOURCE_READER_DISABLE_DXVA = + new Guid("aa456cfd-3943-4a1e-a77d-1838c0ea2e35"); + public static readonly Guid MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING = + new Guid("0f81da2c-b537-4672-a8b2-a681b17307a3"); + + // Low latency mode + public static readonly Guid MF_LOW_LATENCY = + new Guid("9c27891a-ed7a-40e1-88e8-b22727a024ee"); + } + + #endregion + + #region Base Interfaces (referenced from VideoAnalyzer) + + [ComImport, Guid("2CD2D921-C447-44A7-A13C-4ADABFC247E3")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMFAttributes + { + [PreserveSig] int GetItem(Guid guidKey, IntPtr pValue); + [PreserveSig] int GetItemType(Guid guidKey, out ushort pType); + [PreserveSig] int CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); + [PreserveSig] int Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); + [PreserveSig] int GetUINT32(Guid guidKey, out int punValue); + [PreserveSig] int GetUINT64(Guid guidKey, out long punValue); + [PreserveSig] int GetDouble(Guid guidKey, out double pfValue); + [PreserveSig] int GetGUID(Guid guidKey, out Guid pguidValue); + [PreserveSig] int GetStringLength(Guid guidKey, out int pcchLength); + [PreserveSig] int GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); + [PreserveSig] int GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); + [PreserveSig] int GetBlobSize(Guid guidKey, out int pcbBlobSize); + [PreserveSig] int GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); + [PreserveSig] int GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); + [PreserveSig] int GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); + [PreserveSig] int SetItem(Guid guidKey, IntPtr Value); + [PreserveSig] int DeleteItem(Guid guidKey); + [PreserveSig] int DeleteAllItems(); + [PreserveSig] int SetUINT32(Guid guidKey, int unValue); + [PreserveSig] int SetUINT64(Guid guidKey, long unValue); + [PreserveSig] int SetDouble(Guid guidKey, double fValue); + [PreserveSig] int SetGUID(Guid guidKey, Guid guidValue); + [PreserveSig] int SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); + [PreserveSig] int SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); + [PreserveSig] int SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); + [PreserveSig] int LockStore(); + [PreserveSig] int UnlockStore(); + [PreserveSig] int GetCount(out int pcItems); + [PreserveSig] int GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); + [PreserveSig] int CopyAllItems(IMFAttributes pDest); + } + + [ComImport, Guid("44AE0FA8-EA31-4109-8D2E-4CAE4997C555")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IMFMediaType : IMFAttributes + { + #region IMFAttributes methods + [PreserveSig] new int GetItem(Guid guidKey, IntPtr pValue); + [PreserveSig] new int GetItemType(Guid guidKey, out ushort pType); + [PreserveSig] new int CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); + [PreserveSig] new int Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); + [PreserveSig] new int GetUINT32(Guid guidKey, out int punValue); + [PreserveSig] new int GetUINT64(Guid guidKey, out long punValue); + [PreserveSig] new int GetDouble(Guid guidKey, out double pfValue); + [PreserveSig] new int GetGUID(Guid guidKey, out Guid pguidValue); + [PreserveSig] new int GetStringLength(Guid guidKey, out int pcchLength); + [PreserveSig] new int GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); + [PreserveSig] new int GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); + [PreserveSig] new int GetBlobSize(Guid guidKey, out int pcbBlobSize); + [PreserveSig] new int GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); + [PreserveSig] new int GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); + [PreserveSig] new int GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); + [PreserveSig] new int SetItem(Guid guidKey, IntPtr Value); + [PreserveSig] new int DeleteItem(Guid guidKey); + [PreserveSig] new int DeleteAllItems(); + [PreserveSig] new int SetUINT32(Guid guidKey, int unValue); + [PreserveSig] new int SetUINT64(Guid guidKey, long unValue); + [PreserveSig] new int SetDouble(Guid guidKey, double fValue); + [PreserveSig] new int SetGUID(Guid guidKey, Guid guidValue); + [PreserveSig] new int SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); + [PreserveSig] new int SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); + [PreserveSig] new int SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); + [PreserveSig] new int LockStore(); + [PreserveSig] new int UnlockStore(); + [PreserveSig] new int GetCount(out int pcItems); + [PreserveSig] new int GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); + [PreserveSig] new int CopyAllItems(IMFAttributes pDest); + #endregion + + [PreserveSig] int GetMajorType(out Guid pguidMajorType); + [PreserveSig] int IsCompressedFormat(out bool pfCompressed); + [PreserveSig] int IsEqual(IMFMediaType pIMediaType, out uint pdwFlags); + [PreserveSig] int GetRepresentation(Guid guidRepresentation, out IntPtr ppvRepresentation); + [PreserveSig] int FreeRepresentation(Guid guidRepresentation, IntPtr pvRepresentation); + } + + #endregion +} diff --git a/Ganimede/Ganimede/VideoProcessing/MediaFoundation/MFVideoReader.cs b/Ganimede/Ganimede/VideoProcessing/MediaFoundation/MFVideoReader.cs new file mode 100644 index 0000000..86e0e43 --- /dev/null +++ b/Ganimede/Ganimede/VideoProcessing/MediaFoundation/MFVideoReader.cs @@ -0,0 +1,339 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; + +namespace Ganimede.VideoProcessing.MediaFoundation +{ + /// + /// Video reader class using Windows Media Foundation Source Reader + /// Handles frame extraction from video files + /// + public class MFVideoReader : IDisposable + { + private IMFSourceReader? _sourceReader; + private bool _disposed = false; + private int _videoStreamIndex = 0; + private int _width; + private int _height; + private double _frameRate; + private TimeSpan _duration; + + public int Width => _width; + public int Height => _height; + public double FrameRate => _frameRate; + public TimeSpan Duration => _duration; + + public MFVideoReader(string videoPath) + { + if (!File.Exists(videoPath)) + throw new FileNotFoundException($"Video file not found: {videoPath}"); + + // Initialize Media Foundation + int hr = MFExtern.MFStartup(MFExtern.MF_VERSION, 0); + Marshal.ThrowExceptionForHR(hr); + + try + { + // Create attributes for source reader + IMFAttributes? attributes = null; + hr = MFExternExtended.MFCreateAttributes(out attributes, 2); + Marshal.ThrowExceptionForHR(hr); + + // Enable video processing + hr = attributes!.SetUINT32(MFAttributesClsidExtended.MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, 1); + Marshal.ThrowExceptionForHR(hr); + + // Low latency mode + hr = attributes.SetUINT32(MFAttributesClsidExtended.MF_LOW_LATENCY, 1); + Marshal.ThrowExceptionForHR(hr); + + // Create source reader + hr = MFExternExtended.MFCreateSourceReaderFromURL(videoPath, attributes, out _sourceReader); + Marshal.ThrowExceptionForHR(hr); + + // Find first video stream + _videoStreamIndex = MFSourceReaderIndex.FirstVideoStream; + + // Configure output media type (RGB32) + IMFMediaType? mediaType = null; + hr = MFExternExtended.MFCreateMediaType(out mediaType); + Marshal.ThrowExceptionForHR(hr); + + try + { + hr = mediaType!.SetGUID(MFAttributesClsid.MF_MT_MAJOR_TYPE, MFMediaType.Video); + Marshal.ThrowExceptionForHR(hr); + + hr = mediaType.SetGUID(MFAttributesClsid.MF_MT_SUBTYPE, MFVideoFormat.RGB32); + Marshal.ThrowExceptionForHR(hr); + + hr = _sourceReader!.SetCurrentMediaType(_videoStreamIndex, IntPtr.Zero, mediaType); + Marshal.ThrowExceptionForHR(hr); + } + finally + { + if (mediaType != null) Marshal.ReleaseComObject(mediaType); + } + + // Get actual output media type + IMFMediaType? currentMediaType = null; + hr = _sourceReader.GetCurrentMediaType(_videoStreamIndex, out currentMediaType); + Marshal.ThrowExceptionForHR(hr); + + try + { + // Get frame size + long frameSize; + hr = currentMediaType!.GetUINT64(MFAttributesClsid.MF_MT_FRAME_SIZE, out frameSize); + Marshal.ThrowExceptionForHR(hr); + _width = (int)(frameSize >> 32); + _height = (int)(frameSize & 0xFFFFFFFF); + + // Get frame rate + long frameRateRatio; + hr = currentMediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_RATE, out frameRateRatio); + if (hr >= 0) + { + int numerator = (int)(frameRateRatio >> 32); + int denominator = (int)(frameRateRatio & 0xFFFFFFFF); + _frameRate = denominator > 0 ? (double)numerator / denominator : 30.0; + } + else + { + _frameRate = 30.0; // Default + } + } + finally + { + if (currentMediaType != null) Marshal.ReleaseComObject(currentMediaType); + } + + // Get duration + IntPtr durationPtr = IntPtr.Zero; + hr = _sourceReader.GetPresentationAttribute( + MFSourceReaderIndex.MediaSource, + MFAttributesClsid.MF_PD_DURATION, + durationPtr); + + if (hr >= 0 && durationPtr != IntPtr.Zero) + { + long durationTicks = Marshal.ReadInt64(durationPtr); + _duration = TimeSpan.FromTicks(durationTicks / 10); + } + else + { + _duration = TimeSpan.Zero; + } + + // Cleanup attributes + if (attributes != null) Marshal.ReleaseComObject(attributes); + } + catch + { + Dispose(); + throw; + } + } + + /// + /// Seeks to a specific time position in the video + /// + public void SeekTo(TimeSpan position) + { + if (_disposed || _sourceReader == null) + throw new ObjectDisposedException(nameof(MFVideoReader)); + + long timeInHundredNanoseconds = position.Ticks * 10; + IntPtr varPosition = Marshal.AllocHGlobal(16); // PROPVARIANT size + + try + { + // Write PROPVARIANT structure + // VT_I8 = 20 (64-bit signed integer) + Marshal.WriteInt16(varPosition, 0, 20); // vt + Marshal.WriteInt64(varPosition, 8, timeInHundredNanoseconds); // hVal + + int hr = _sourceReader.SetCurrentPosition(Guid.Empty, varPosition); + Marshal.ThrowExceptionForHR(hr); + } + finally + { + Marshal.FreeHGlobal(varPosition); + } + } + + /// + /// Reads the next video frame + /// + public Bitmap? ReadFrame() + { + if (_disposed || _sourceReader == null) + throw new ObjectDisposedException(nameof(MFVideoReader)); + + IMFSample? sample = null; + int streamIndex; + MF_SOURCE_READER_FLAG flags; + long timestamp; + + int hr = _sourceReader.ReadSample( + _videoStreamIndex, + 0, + out streamIndex, + out flags, + out timestamp, + out sample); + + Marshal.ThrowExceptionForHR(hr); + + // Check for end of stream + if ((flags & MF_SOURCE_READER_FLAG.EndOfStream) != 0) + { + return null; + } + + if (sample == null) + { + return null; + } + + try + { + return ConvertSampleToBitmap(sample); + } + finally + { + Marshal.ReleaseComObject(sample); + } + } + + /// + /// Reads a frame at a specific time position + /// + public Bitmap? ReadFrameAtTime(TimeSpan position) + { + SeekTo(position); + return ReadFrame(); + } + + /// + /// Converts an IMFSample to a Bitmap + /// + private Bitmap ConvertSampleToBitmap(IMFSample sample) + { + IMFMediaBuffer? buffer = null; + int hr = sample.ConvertToContiguousBuffer(out buffer); + Marshal.ThrowExceptionForHR(hr); + + try + { + IntPtr pData; + int cbMaxLength, cbCurrentLength; + + hr = buffer!.Lock(out pData, out cbMaxLength, out cbCurrentLength); + Marshal.ThrowExceptionForHR(hr); + + try + { + // Create bitmap + Bitmap bitmap = new Bitmap(_width, _height, PixelFormat.Format32bppRgb); + + // Lock bitmap data + Rectangle rect = new Rectangle(0, 0, _width, _height); + BitmapData bmpData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb); + + try + { + int stride = _width * 4; // 4 bytes per pixel (RGB32) + + // Copy data line by line (bottom-up for RGB32) + for (int y = 0; y < _height; y++) + { + IntPtr srcLine = IntPtr.Add(pData, y * stride); + IntPtr dstLine = IntPtr.Add(bmpData.Scan0, (_height - 1 - y) * bmpData.Stride); + + // Copy scanline + unsafe + { + Buffer.MemoryCopy( + srcLine.ToPointer(), + dstLine.ToPointer(), + bmpData.Stride, + stride); + } + } + } + finally + { + bitmap.UnlockBits(bmpData); + } + + return bitmap; + } + finally + { + buffer.Unlock(); + } + } + finally + { + if (buffer != null) Marshal.ReleaseComObject(buffer); + } + } + + public void Dispose() + { + if (!_disposed) + { + if (_sourceReader != null) + { + Marshal.ReleaseComObject(_sourceReader); + _sourceReader = null; + } + + MFExtern.MFShutdown(); + _disposed = true; + } + + GC.SuppressFinalize(this); + } + + ~MFVideoReader() + { + Dispose(); + } + } + + #region Helper Classes from VideoAnalyzer + + internal static class MFExtern + { + private const uint MF_SDK_VERSION = 0x0002; + private const uint MF_API_VERSION = 0x0070; + internal const uint MF_VERSION = (MF_SDK_VERSION << 16) | MF_API_VERSION; + + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFStartup(uint Version, uint dwFlags = 0); + + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFShutdown(); + } + + internal static class MFAttributesClsid + { + public static readonly Guid MF_PD_DURATION = new Guid("6c990d33-bb8e-477a-8598-0d5d96fcd88a"); + public static readonly Guid MF_MT_MAJOR_TYPE = new Guid("48eba18e-f8c9-4687-bf11-0a74c9f96a8f"); + public static readonly Guid MF_MT_SUBTYPE = new Guid("f7e34c9a-42e8-4714-b74b-cb29d72c35e5"); + public static readonly Guid MF_MT_FRAME_SIZE = new Guid("1652c33d-d6b2-4012-b834-72030849a37d"); + public static readonly Guid MF_MT_FRAME_RATE = new Guid("c459a2e8-3d2c-4e44-b132-fee5156c7bb0"); + } + + internal static class MFMediaType + { + public static readonly Guid Video = new Guid("73646976-0000-0010-8000-00AA00389B71"); + public static readonly Guid Audio = new Guid("73647561-0000-0010-8000-00AA00389B71"); + } + + #endregion +} diff --git a/Ganimede/Ganimede/VideoProcessing/VideoAnalyzer.cs b/Ganimede/Ganimede/VideoProcessing/VideoAnalyzer.cs index 42ff528..9c763d9 100644 --- a/Ganimede/Ganimede/VideoProcessing/VideoAnalyzer.cs +++ b/Ganimede/Ganimede/VideoProcessing/VideoAnalyzer.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; namespace Ganimede.VideoProcessing { @@ -12,7 +11,7 @@ namespace Ganimede.VideoProcessing public class VideoAnalyzer { /// - /// Analyzes a video file and returns its metadata using Windows Media Foundation + /// Analyzes a video file and returns its metadata using Windows Media Foundation Source Reader /// public static VideoMetadata Analyze(string videoPath) { @@ -25,140 +24,103 @@ namespace Ganimede.VideoProcessing try { - IMFSourceResolver? sourceResolver = null; - IMFMediaSource? mediaSource = null; - IMFPresentationDescriptor? presentationDescriptor = null; + IntPtr pSourceReader = IntPtr.Zero; + IMFSourceReader? sourceReader = null; try { - // Create source resolver - hr = MFExtern.MFCreateSourceResolver(out sourceResolver); + // Create source reader from URL + hr = MFExtern.MFCreateSourceReaderFromURL(videoPath, IntPtr.Zero, out pSourceReader); + Marshal.ThrowExceptionForHR(hr); + + // Convert to interface + sourceReader = (IMFSourceReader)Marshal.GetObjectForIUnknown(pSourceReader); + + // Get native media type for first video stream + IntPtr pMediaType = IntPtr.Zero; + hr = sourceReader!.GetNativeMediaType( + MFSourceReaderIndex.FirstVideoStream, + 0, + out pMediaType); Marshal.ThrowExceptionForHR(hr); - // Create media source from file - MFObjectType objectType; - object source; - hr = sourceResolver!.CreateObjectFromURL( - videoPath, - MFResolution.MediaSource, - null, - out objectType, - out source); - Marshal.ThrowExceptionForHR(hr); - - mediaSource = (IMFMediaSource)source; - - // Get presentation descriptor - hr = mediaSource!.CreatePresentationDescriptor(out presentationDescriptor); - Marshal.ThrowExceptionForHR(hr); - - // Get duration - long durationTicks; - hr = presentationDescriptor!.GetUINT64(MFAttributesClsid.MF_PD_DURATION, out durationTicks); - Marshal.ThrowExceptionForHR(hr); - TimeSpan duration = TimeSpan.FromTicks(durationTicks / 10); // Convert from 100-nanosecond units - - // Find video stream - int streamCount; - hr = presentationDescriptor.GetStreamDescriptorCount(out streamCount); - Marshal.ThrowExceptionForHR(hr); - - for (int i = 0; i < streamCount; i++) + IMFMediaType? mediaType = null; + try { - IMFStreamDescriptor? streamDescriptor = null; - bool selected; + mediaType = (IMFMediaType)Marshal.GetObjectForIUnknown(pMediaType); - hr = presentationDescriptor.GetStreamDescriptorByIndex(i, out selected, out streamDescriptor); + // Get frame size + long frameSize; + hr = mediaType!.GetUINT64(MFAttributesClsid.MF_MT_FRAME_SIZE, out frameSize); Marshal.ThrowExceptionForHR(hr); + int width = (int)(frameSize >> 32); + int height = (int)(frameSize & 0xFFFFFFFF); + + // Get frame rate + long frameRateRatio; + hr = mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_RATE, out frameRateRatio); + Marshal.ThrowExceptionForHR(hr); + + int frameRateNumerator = (int)(frameRateRatio >> 32); + int frameRateDenominator = (int)(frameRateRatio & 0xFFFFFFFF); + double fps = frameRateDenominator > 0 ? (double)frameRateNumerator / frameRateDenominator : 30.0; + + // Get codec subtype + Guid subType; + hr = mediaType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subType); + string codecName = GetCodecName(subType); + + // Get duration from presentation attribute + IntPtr varDuration = Marshal.AllocHGlobal(16); // PROPVARIANT size try { - // Get media type handler - IMFMediaTypeHandler? handler = null; - hr = streamDescriptor!.GetMediaTypeHandler(out handler); - Marshal.ThrowExceptionForHR(hr); + hr = sourceReader.GetPresentationAttribute( + MFSourceReaderIndex.MediaSource, + MFAttributesClsid.MF_PD_DURATION, + varDuration); - try + TimeSpan duration = TimeSpan.Zero; + if (hr >= 0) { - // Get major type - Guid majorType; - hr = handler!.GetMajorType(out majorType); - Marshal.ThrowExceptionForHR(hr); - - // Check if this is a video stream - if (majorType == MFMediaType.Video) - { - // Get current media type - IMFMediaType? mediaType = null; - hr = handler.GetCurrentMediaType(out mediaType); - Marshal.ThrowExceptionForHR(hr); - - try - { - // Get frame size - long frameSize; - hr = mediaType!.GetUINT64(MFAttributesClsid.MF_MT_FRAME_SIZE, out frameSize); - Marshal.ThrowExceptionForHR(hr); - - int width = (int)(frameSize >> 32); - int height = (int)(frameSize & 0xFFFFFFFF); - - // Get frame rate - long frameRate; - hr = mediaType.GetUINT64(MFAttributesClsid.MF_MT_FRAME_RATE, out frameRate); - Marshal.ThrowExceptionForHR(hr); - - int frameRateNumerator = (int)(frameRate >> 32); - int frameRateDenominator = (int)(frameRate & 0xFFFFFFFF); - double fps = frameRateDenominator > 0 ? (double)frameRateNumerator / frameRateDenominator : 30.0; - - // Calculate total frames - int totalFrames = (int)(duration.TotalSeconds * fps); - - // Get bitrate (approximate from file size) - long fileSize = new FileInfo(videoPath).Length; - long bitrate = duration.TotalSeconds > 0 ? (long)((fileSize * 8) / duration.TotalSeconds) : 0; - - // Get codec - Guid subType; - hr = mediaType.GetGUID(MFAttributesClsid.MF_MT_SUBTYPE, out subType); - string codecName = GetCodecName(subType); - - return new VideoMetadata - { - Duration = duration, - FrameRate = fps, - TotalFrames = totalFrames, - Width = width, - Height = height, - BitRate = bitrate, - CodecName = codecName - }; - } - finally - { - if (mediaType != null) Marshal.ReleaseComObject(mediaType); - } - } + // Read PROPVARIANT (VT_I8) + long durationTicks = Marshal.ReadInt64(varDuration, 8); + duration = TimeSpan.FromTicks(durationTicks / 10); // Convert from 100-nanosecond units } - finally + + // Calculate total frames + int totalFrames = (int)(duration.TotalSeconds * fps); + + // Get bitrate (approximate from file size) + long fileSize = new FileInfo(videoPath).Length; + long bitrate = duration.TotalSeconds > 0 ? (long)((fileSize * 8) / duration.TotalSeconds) : 0; + + return new VideoMetadata { - if (handler != null) Marshal.ReleaseComObject(handler); - } + Duration = duration, + FrameRate = fps, + TotalFrames = totalFrames, + Width = width, + Height = height, + BitRate = bitrate, + CodecName = codecName + }; } finally { - if (streamDescriptor != null) Marshal.ReleaseComObject(streamDescriptor); + Marshal.FreeHGlobal(varDuration); } } - - throw new InvalidOperationException("No video stream found in the file"); + finally + { + if (mediaType != null) Marshal.ReleaseComObject(mediaType); + if (pMediaType != IntPtr.Zero) Marshal.Release(pMediaType); + } } finally { - if (presentationDescriptor != null) Marshal.ReleaseComObject(presentationDescriptor); - if (mediaSource != null) Marshal.ReleaseComObject(mediaSource); - if (sourceResolver != null) Marshal.ReleaseComObject(sourceResolver); + if (sourceReader != null) Marshal.ReleaseComObject(sourceReader); + if (pSourceReader != IntPtr.Zero) Marshal.Release(pSourceReader); } } finally @@ -204,300 +166,77 @@ namespace Ganimede.VideoProcessing private const uint MF_API_VERSION = 0x0070; internal const uint MF_VERSION = (MF_SDK_VERSION << 16) | MF_API_VERSION; - [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)] - internal static extern void MFStartup(uint Version, uint dwFlags = 0); + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFStartup(uint Version, uint dwFlags = 0); - [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)] - internal static extern void MFShutdown(); + [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFShutdown(); - [DllImport("mfplat.dll", ExactSpelling = true, PreserveSig = false)] - internal static extern void MFCreateSourceResolver( - [MarshalAs(UnmanagedType.Interface)] out IMFSourceResolver ppISourceResolver); + [DllImport("mfreadwrite.dll", ExactSpelling = true, PreserveSig = true)] + internal static extern int MFCreateSourceReaderFromURL( + [MarshalAs(UnmanagedType.LPWStr)] string pwszURL, + IntPtr pAttributes, + out IntPtr ppSourceReader); } - [ComImport, Guid("FBE5A32D-A497-4b57-BB57-B1EF73A689E4")] + [ComImport, Guid("70ae66f2-c809-4e4f-8915-bdcb406b7993")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFSourceResolver + internal interface IMFSourceReader { - [PreserveSig] - int CreateObjectFromURL( - [In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, - [In] MFResolution dwFlags, - [In, MarshalAs(UnmanagedType.Interface)] IPropertyStore? pProps, - [Out] out MFObjectType pObjectType, - [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppObject); - - [PreserveSig] - int CreateObjectFromByteStream( - [In, MarshalAs(UnmanagedType.Interface)] IMFByteStream pByteStream, - [In, MarshalAs(UnmanagedType.LPWStr)] string? pwszURL, - [In] MFResolution dwFlags, - [In, MarshalAs(UnmanagedType.Interface)] IPropertyStore? pProps, - [Out] out MFObjectType pObjectType, - [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppObject); - - [PreserveSig] - int BeginCreateObjectFromURL( - [In, MarshalAs(UnmanagedType.LPWStr)] string pwszURL, - [In] MFResolution dwFlags, - [In, MarshalAs(UnmanagedType.Interface)] IPropertyStore? pProps, - [Out, MarshalAs(UnmanagedType.IUnknown)] out object? ppIUnknownCancelCookie, - [In, MarshalAs(UnmanagedType.Interface)] IMFAsyncCallback pCallback, - [In, MarshalAs(UnmanagedType.IUnknown)] object? punkState); - - [PreserveSig] - int EndCreateObjectFromURL( - [In, MarshalAs(UnmanagedType.Interface)] IMFAsyncResult pResult, - [Out] out MFObjectType pObjectType, - [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppObject); - - [PreserveSig] - int BeginCreateObjectFromByteStream( - [In, MarshalAs(UnmanagedType.Interface)] IMFByteStream pByteStream, - [In, MarshalAs(UnmanagedType.LPWStr)] string? pwszURL, - [In] MFResolution dwFlags, - [In, MarshalAs(UnmanagedType.Interface)] IPropertyStore? pProps, - [Out, MarshalAs(UnmanagedType.IUnknown)] out object? ppIUnknownCancelCookie, - [In, MarshalAs(UnmanagedType.Interface)] IMFAsyncCallback pCallback, - [In, MarshalAs(UnmanagedType.IUnknown)] object? punkState); - - [PreserveSig] - int EndCreateObjectFromByteStream( - [In, MarshalAs(UnmanagedType.Interface)] IMFAsyncResult pResult, - [Out] out MFObjectType pObjectType, - [Out, MarshalAs(UnmanagedType.IUnknown)] out object ppObject); - - [PreserveSig] - int CancelObjectCreation( - [In, MarshalAs(UnmanagedType.IUnknown)] object pIUnknownCancelCookie); + [PreserveSig] int GetStreamSelection(int dwStreamIndex, out bool pfSelected); + [PreserveSig] int SetStreamSelection(int dwStreamIndex, bool fSelected); + [PreserveSig] int GetNativeMediaType(int dwStreamIndex, int dwMediaTypeIndex, out IntPtr ppMediaType); + [PreserveSig] int GetCurrentMediaType(int dwStreamIndex, out IntPtr ppMediaType); + [PreserveSig] int SetCurrentMediaType(int dwStreamIndex, IntPtr pdwReserved, IntPtr pMediaType); + [PreserveSig] int SetCurrentPosition(Guid guidTimeFormat, IntPtr varPosition); + [PreserveSig] int ReadSample(int dwStreamIndex, int dwControlFlags, out int pdwActualStreamIndex, + out int pdwStreamFlags, out long pllTimestamp, out IntPtr ppSample); + [PreserveSig] int Flush(int dwStreamIndex); + [PreserveSig] int GetServiceForStream(int dwStreamIndex, Guid guidService, Guid riid, out IntPtr ppvObject); + [PreserveSig] int GetPresentationAttribute(int dwStreamIndex, Guid guidAttribute, IntPtr pvarAttribute); } - [ComImport, Guid("279A808D-AEC7-40C8-9C6B-A6B492C78A66")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFMediaSource : IMFMediaEventGenerator + internal static class MFSourceReaderIndex { - #region IMFMediaEventGenerator methods - new void GetEvent(uint dwFlags, out IMFMediaEvent ppEvent); - new void BeginGetEvent(IMFAsyncCallback pCallback, object punkState); - new void EndGetEvent(IMFAsyncResult pResult, out IMFMediaEvent ppEvent); - new void QueueEvent(uint met, Guid guidExtendedType, int hrStatus, IntPtr pvValue); - #endregion - - void GetCharacteristics(out uint pdwCharacteristics); - - [PreserveSig] - int CreatePresentationDescriptor( - [MarshalAs(UnmanagedType.Interface)] out IMFPresentationDescriptor ppPresentationDescriptor); - - void Start( - IMFPresentationDescriptor pPresentationDescriptor, - IntPtr pguidTimeFormat, - IntPtr pvarStartPosition); - - void Stop(); - void Pause(); - void Shutdown(); - } - - [ComImport, Guid("7FEE9E9A-4A89-47a6-899C-B6A53A70FB67")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFMediaEventGenerator - { - void GetEvent(uint dwFlags, out IMFMediaEvent ppEvent); - void BeginGetEvent(IMFAsyncCallback pCallback, object punkState); - void EndGetEvent(IMFAsyncResult pResult, out IMFMediaEvent ppEvent); - void QueueEvent(uint met, Guid guidExtendedType, int hrStatus, IntPtr pvValue); - } - - [ComImport, Guid("DF598932-F10C-4E39-BBA2-C308F101DAA3")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFMediaEvent : IMFAttributes - { - #region IMFAttributes methods - new void GetItem(Guid guidKey, IntPtr pValue); - new void GetItemType(Guid guidKey, out ushort pType); - new void CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); - new void Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); - new void GetUINT32(Guid guidKey, out int punValue); - new void GetUINT64(Guid guidKey, out long punValue); - new void GetDouble(Guid guidKey, out double pfValue); - new void GetGUID(Guid guidKey, out Guid pguidValue); - new void GetStringLength(Guid guidKey, out int pcchLength); - new void GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); - new void GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); - new void GetBlobSize(Guid guidKey, out int pcbBlobSize); - new void GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); - new void GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); - new void GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); - new void SetItem(Guid guidKey, IntPtr Value); - new void DeleteItem(Guid guidKey); - new void DeleteAllItems(); - new void SetUINT32(Guid guidKey, int unValue); - new void SetUINT64(Guid guidKey, long unValue); - new void SetDouble(Guid guidKey, double fValue); - new void SetGUID(Guid guidKey, Guid guidValue); - new void SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); - new void SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); - new void SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); - new void LockStore(); - new void UnlockStore(); - new void GetCount(out int pcItems); - new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); - new void CopyAllItems(IMFAttributes pDest); - #endregion - - void GetType(out uint pmet); - void GetExtendedType(out Guid pguidExtendedType); - void GetStatus(out int phrStatus); - void GetValue(out object pvValue); + public const int FirstVideoStream = unchecked((int)0xFFFFFFFC); + public const int FirstAudioStream = unchecked((int)0xFFFFFFFD); + public const int MediaSource = unchecked((int)0xFFFFFFFF); } [ComImport, Guid("2CD2D921-C447-44A7-A13C-4ADABFC247E3")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IMFAttributes { - void GetItem(Guid guidKey, IntPtr pValue); - void GetItemType(Guid guidKey, out ushort pType); - void CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); - void Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); - void GetUINT32(Guid guidKey, out int punValue); - void GetUINT64(Guid guidKey, out long punValue); - void GetDouble(Guid guidKey, out double pfValue); - void GetGUID(Guid guidKey, out Guid pguidValue); - void GetStringLength(Guid guidKey, out int pcchLength); - void GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); - void GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); - void GetBlobSize(Guid guidKey, out int pcbBlobSize); - void GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); - void GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); - void GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); - void SetItem(Guid guidKey, IntPtr Value); - void DeleteItem(Guid guidKey); - void DeleteAllItems(); - void SetUINT32(Guid guidKey, int unValue); - void SetUINT64(Guid guidKey, long unValue); - void SetDouble(Guid guidKey, double fValue); - void SetGUID(Guid guidKey, Guid guidValue); - void SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); - void SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); - void SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); - void LockStore(); - void UnlockStore(); - void GetCount(out int pcItems); - void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); - void CopyAllItems(IMFAttributes pDest); - } - - [ComImport, Guid("03CB2711-24D7-4DB6-A17F-F3A7A479A536")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFPresentationDescriptor : IMFAttributes - { - #region IMFAttributes methods - new void GetItem(Guid guidKey, IntPtr pValue); - new void GetItemType(Guid guidKey, out ushort pType); - new void CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); - new void Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); - new void GetUINT32(Guid guidKey, out int punValue); - new void GetUINT64(Guid guidKey, out long punValue); - new void GetDouble(Guid guidKey, out double pfValue); - new void GetGUID(Guid guidKey, out Guid pguidValue); - new void GetStringLength(Guid guidKey, out int pcchLength); - new void GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); - new void GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); - new void GetBlobSize(Guid guidKey, out int pcbBlobSize); - new void GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); - new void GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); - new void GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); - new void SetItem(Guid guidKey, IntPtr Value); - new void DeleteItem(Guid guidKey); - new void DeleteAllItems(); - new void SetUINT32(Guid guidKey, int unValue); - new void SetUINT64(Guid guidKey, long unValue); - new void SetDouble(Guid guidKey, double fValue); - new void SetGUID(Guid guidKey, Guid guidValue); - new void SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); - new void SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); - new void SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); - new void LockStore(); - new void UnlockStore(); - new void GetCount(out int pcItems); - new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); - new void CopyAllItems(IMFAttributes pDest); - #endregion - - void GetStreamDescriptorCount(out int pdwDescriptorCount); - - [PreserveSig] - int GetStreamDescriptorByIndex( - int dwIndex, - out bool pfSelected, - [MarshalAs(UnmanagedType.Interface)] out IMFStreamDescriptor ppDescriptor); - - void SelectStream(int dwDescriptorIndex); - void DeselectStream(int dwDescriptorIndex); - void Clone(out IMFPresentationDescriptor ppPresentationDescriptor); - } - - [ComImport, Guid("56C03D9C-9DBB-45F5-AB4B-D80F47C05938")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFStreamDescriptor : IMFAttributes - { - #region IMFAttributes methods - new void GetItem(Guid guidKey, IntPtr pValue); - new void GetItemType(Guid guidKey, out ushort pType); - new void CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); - new void Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); - new void GetUINT32(Guid guidKey, out int punValue); - new void GetUINT64(Guid guidKey, out long punValue); - new void GetDouble(Guid guidKey, out double pfValue); - new void GetGUID(Guid guidKey, out Guid pguidValue); - new void GetStringLength(Guid guidKey, out int pcchLength); - new void GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); - new void GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); - new void GetBlobSize(Guid guidKey, out int pcbBlobSize); - new void GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); - new void GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); - new void GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); - new void SetItem(Guid guidKey, IntPtr Value); - new void DeleteItem(Guid guidKey); - new void DeleteAllItems(); - new void SetUINT32(Guid guidKey, int unValue); - new void SetUINT64(Guid guidKey, long unValue); - new void SetDouble(Guid guidKey, double fValue); - new void SetGUID(Guid guidKey, Guid guidValue); - new void SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); - new void SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); - new void SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); - new void LockStore(); - new void UnlockStore(); - new void GetCount(out int pcItems); - new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); - new void CopyAllItems(IMFAttributes pDest); - #endregion - - void GetStreamIdentifier(out int pdwStreamIdentifier); - - [PreserveSig] - int GetMediaTypeHandler( - [MarshalAs(UnmanagedType.Interface)] out IMFMediaTypeHandler ppMediaTypeHandler); - } - - [ComImport, Guid("E93DCF6C-4B07-4E1E-8123-AA16ED6EADF5")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFMediaTypeHandler - { - void IsMediaTypeSupported(IMFMediaType pMediaType, out IMFMediaType ppMediaType); - void GetMediaTypeCount(out int pdwTypeCount); - void GetMediaTypeByIndex(int dwIndex, out IMFMediaType ppType); - - [PreserveSig] - int SetCurrentMediaType(IMFMediaType pMediaType); - - [PreserveSig] - int GetCurrentMediaType([MarshalAs(UnmanagedType.Interface)] out IMFMediaType ppMediaType); - - [PreserveSig] - int GetMajorType(out Guid pguidMajorType); + [PreserveSig] int GetItem(Guid guidKey, IntPtr pValue); + [PreserveSig] int GetItemType(Guid guidKey, out ushort pType); + [PreserveSig] int CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); + [PreserveSig] int Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); + [PreserveSig] int GetUINT32(Guid guidKey, out int punValue); + [PreserveSig] int GetUINT64(Guid guidKey, out long punValue); + [PreserveSig] int GetDouble(Guid guidKey, out double pfValue); + [PreserveSig] int GetGUID(Guid guidKey, out Guid pguidValue); + [PreserveSig] int GetStringLength(Guid guidKey, out int pcchLength); + [PreserveSig] int GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); + [PreserveSig] int GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); + [PreserveSig] int GetBlobSize(Guid guidKey, out int pcbBlobSize); + [PreserveSig] int GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); + [PreserveSig] int GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); + [PreserveSig] int GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); + [PreserveSig] int SetItem(Guid guidKey, IntPtr Value); + [PreserveSig] int DeleteItem(Guid guidKey); + [PreserveSig] int DeleteAllItems(); + [PreserveSig] int SetUINT32(Guid guidKey, int unValue); + [PreserveSig] int SetUINT64(Guid guidKey, long unValue); + [PreserveSig] int SetDouble(Guid guidKey, double fValue); + [PreserveSig] int SetGUID(Guid guidKey, Guid guidValue); + [PreserveSig] int SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); + [PreserveSig] int SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); + [PreserveSig] int SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); + [PreserveSig] int LockStore(); + [PreserveSig] int UnlockStore(); + [PreserveSig] int GetCount(out int pcItems); + [PreserveSig] int GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); + [PreserveSig] int CopyAllItems(IMFAttributes pDest); } [ComImport, Guid("44AE0FA8-EA31-4109-8D2E-4CAE4997C555")] @@ -505,96 +244,43 @@ namespace Ganimede.VideoProcessing internal interface IMFMediaType : IMFAttributes { #region IMFAttributes methods - new void GetItem(Guid guidKey, IntPtr pValue); - new void GetItemType(Guid guidKey, out ushort pType); - new void CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); - new void Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); - new void GetUINT32(Guid guidKey, out int punValue); - new void GetUINT64(Guid guidKey, out long punValue); - new void GetDouble(Guid guidKey, out double pfValue); - new void GetGUID(Guid guidKey, out Guid pguidValue); - new void GetStringLength(Guid guidKey, out int pcchLength); - new void GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); - new void GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); - new void GetBlobSize(Guid guidKey, out int pcbBlobSize); - new void GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); - new void GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); - new void GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); - new void SetItem(Guid guidKey, IntPtr Value); - new void DeleteItem(Guid guidKey); - new void DeleteAllItems(); - new void SetUINT32(Guid guidKey, int unValue); - new void SetUINT64(Guid guidKey, long unValue); - new void SetDouble(Guid guidKey, double fValue); - new void SetGUID(Guid guidKey, Guid guidValue); - new void SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); - new void SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); - new void SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); - new void LockStore(); - new void UnlockStore(); - new void GetCount(out int pcItems); - new void GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); - new void CopyAllItems(IMFAttributes pDest); + [PreserveSig] new int GetItem(Guid guidKey, IntPtr pValue); + [PreserveSig] new int GetItemType(Guid guidKey, out ushort pType); + [PreserveSig] new int CompareItem(Guid guidKey, IntPtr Value, out bool pbResult); + [PreserveSig] new int Compare(IMFAttributes pTheirs, int MatchType, out bool pbResult); + [PreserveSig] new int GetUINT32(Guid guidKey, out int punValue); + [PreserveSig] new int GetUINT64(Guid guidKey, out long punValue); + [PreserveSig] new int GetDouble(Guid guidKey, out double pfValue); + [PreserveSig] new int GetGUID(Guid guidKey, out Guid pguidValue); + [PreserveSig] new int GetStringLength(Guid guidKey, out int pcchLength); + [PreserveSig] new int GetString(Guid guidKey, IntPtr pwszValue, int cchBufSize, IntPtr pcchLength); + [PreserveSig] new int GetAllocatedString(Guid guidKey, out IntPtr ppwszValue, out int pcchLength); + [PreserveSig] new int GetBlobSize(Guid guidKey, out int pcbBlobSize); + [PreserveSig] new int GetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize, IntPtr pcbBlobSize); + [PreserveSig] new int GetAllocatedBlob(Guid guidKey, out IntPtr ppBuf, out int pcbSize); + [PreserveSig] new int GetUnknown(Guid guidKey, Guid riid, out IntPtr ppv); + [PreserveSig] new int SetItem(Guid guidKey, IntPtr Value); + [PreserveSig] new int DeleteItem(Guid guidKey); + [PreserveSig] new int DeleteAllItems(); + [PreserveSig] new int SetUINT32(Guid guidKey, int unValue); + [PreserveSig] new int SetUINT64(Guid guidKey, long unValue); + [PreserveSig] new int SetDouble(Guid guidKey, double fValue); + [PreserveSig] new int SetGUID(Guid guidKey, Guid guidValue); + [PreserveSig] new int SetString(Guid guidKey, [MarshalAs(UnmanagedType.LPWStr)] string wszValue); + [PreserveSig] new int SetBlob(Guid guidKey, IntPtr pBuf, int cbBufSize); + [PreserveSig] new int SetUnknown(Guid guidKey, [MarshalAs(UnmanagedType.IUnknown)] object pUnknown); + [PreserveSig] new int LockStore(); + [PreserveSig] new int UnlockStore(); + [PreserveSig] new int GetCount(out int pcItems); + [PreserveSig] new int GetItemByIndex(int unIndex, out Guid pguidKey, IntPtr pValue); + [PreserveSig] new int CopyAllItems(IMFAttributes pDest); #endregion - void GetMajorType(out Guid pguidMajorType); - void IsCompressedFormat(out bool pfCompressed); - void IsEqual(IMFMediaType pIMediaType, out uint pdwFlags); - void GetRepresentation(Guid guidRepresentation, out IntPtr ppvRepresentation); - void FreeRepresentation(Guid guidRepresentation, IntPtr pvRepresentation); - } - - [ComImport, Guid("AC6B7889-0740-4D51-8619-905994A55CC6")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFAsyncResult - { - void GetState([MarshalAs(UnmanagedType.IUnknown)] out object ppunkState); - void GetStatus(); - void SetStatus(int hrStatus); - void GetObject([MarshalAs(UnmanagedType.IUnknown)] out object ppObject); - [return: MarshalAs(UnmanagedType.IUnknown)] - object GetStateNoAddRef(); - } - - [ComImport, Guid("A27003CF-2354-4F2A-8D6A-AB7CFF15437E")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFAsyncCallback - { - void GetParameters(out uint pdwFlags, out uint pdwQueue); - void Invoke(IMFAsyncResult pAsyncResult); - } - - [ComImport, Guid("AD4C1B00-4BF7-422F-9175-756693D9130D")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IMFByteStream - { - // Methods not needed for this implementation - } - - [ComImport, Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IPropertyStore - { - // Methods not needed for this implementation - } - - internal enum MFResolution - { - MediaSource = 0x00000001, - ByteStream = 0x00000002, - ContentDoesNotHaveToMatchExtensionOrMimeType = 0x00000010, - KeepByteStreamAliveOnFail = 0x00000020, - DisableLocalPlugins = 0x00000040, - PluginControlPolicy = 0x00000080, - Read = 0x00010000, - Write = 0x00020000 - } - - internal enum MFObjectType - { - MediaSource = 0, - ByteStream = 1, - Unknown = 2 + [PreserveSig] int GetMajorType(out Guid pguidMajorType); + [PreserveSig] int IsCompressedFormat(out bool pfCompressed); + [PreserveSig] int IsEqual(IMFMediaType pIMediaType, out uint pdwFlags); + [PreserveSig] int GetRepresentation(Guid guidRepresentation, out IntPtr ppvRepresentation); + [PreserveSig] int FreeRepresentation(Guid guidRepresentation, IntPtr pvRepresentation); } internal static class MFAttributesClsid diff --git a/Ganimede/Ganimede/ViewModels/.keep b/Ganimede/Ganimede/ViewModels/.keep deleted file mode 100644 index 5a7e5de..0000000 --- a/Ganimede/Ganimede/ViewModels/.keep +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ganimede.ViewModels -{ - // ViewModel principale e futuri ViewModel -} diff --git a/Ganimede/Ganimede/Views/.keep b/Ganimede/Ganimede/Views/.keep deleted file mode 100644 index b7fc37d..0000000 --- a/Ganimede/Ganimede/Views/.keep +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ganimede.Views -{ - // Views aggiuntive se necessario -}