492 lines
18 KiB
C#
Raw Normal View History

2024-06-07 00:47:07 +02:00
using System;
using System.Collections.Generic;
using NAudio.Utils;
using System.Runtime.InteropServices;
using NAudio.Wave;
using System.Diagnostics;
namespace NAudio.Dmo
{
/// <summary>
/// Media Object
/// </summary>
public class MediaObject : IDisposable
{
private IMediaObject mediaObject;
private readonly int inputStreams;
private readonly int outputStreams;
#region Construction
/// <summary>
/// Creates a new Media Object
/// </summary>
/// <param name="mediaObject">Media Object COM interface</param>
internal MediaObject(IMediaObject mediaObject)
{
this.mediaObject = mediaObject;
mediaObject.GetStreamCount(out inputStreams, out outputStreams);
}
#endregion
#region Public Properties
/// <summary>
/// Number of input streams
/// </summary>
public int InputStreamCount
{
get { return inputStreams; }
}
/// <summary>
/// Number of output streams
/// </summary>
public int OutputStreamCount
{
get { return outputStreams; }
}
#endregion
#region Get Input and Output Types
/// <summary>
/// Gets the input media type for the specified input stream
/// </summary>
/// <param name="inputStream">Input stream index</param>
/// <param name="inputTypeIndex">Input type index</param>
/// <returns>DMO Media Type or null if there are no more input types</returns>
public DmoMediaType? GetInputType(int inputStream, int inputTypeIndex)
{
try
{
DmoMediaType mediaType;
int hresult = mediaObject.GetInputType(inputStream, inputTypeIndex, out mediaType);
if (hresult == HResult.S_OK)
{
// this frees the format (if present)
// we should therefore come up with a way of marshaling the format
// into a completely managed structure
DmoInterop.MoFreeMediaType(ref mediaType);
return mediaType;
}
}
catch (COMException e)
{
if (e.GetHResult() != (int)DmoHResults.DMO_E_NO_MORE_ITEMS)
{
throw;
}
}
return null;
}
/// <summary>
/// Gets the DMO Media Output type
/// </summary>
/// <param name="outputStream">The output stream</param>
/// <param name="outputTypeIndex">Output type index</param>
/// <returns>DMO Media Type or null if no more available</returns>
public DmoMediaType? GetOutputType(int outputStream, int outputTypeIndex)
{
try
{
DmoMediaType mediaType;
int hresult = mediaObject.GetOutputType(outputStream, outputTypeIndex, out mediaType);
if (hresult == HResult.S_OK)
{
// this frees the format (if present)
// we should therefore come up with a way of marshaling the format
// into a completely managed structure
DmoInterop.MoFreeMediaType(ref mediaType);
return mediaType;
}
}
catch (COMException e)
{
if (e.GetHResult() != (int)DmoHResults.DMO_E_NO_MORE_ITEMS)
{
throw;
}
}
return null;
}
/// <summary>
/// retrieves the media type that was set for an output stream, if any
/// </summary>
/// <param name="outputStreamIndex">Output stream index</param>
/// <returns>DMO Media Type or null if no more available</returns>
public DmoMediaType GetOutputCurrentType(int outputStreamIndex)
{
DmoMediaType mediaType;
int hresult = mediaObject.GetOutputCurrentType(outputStreamIndex, out mediaType);
if (hresult == HResult.S_OK)
{
// this frees the format (if present)
// we should therefore come up with a way of marshaling the format
// into a completely managed structure
DmoInterop.MoFreeMediaType(ref mediaType);
return mediaType;
}
else
{
if (hresult == (int)DmoHResults.DMO_E_TYPE_NOT_SET)
{
throw new InvalidOperationException("Media type was not set.");
}
else
{
throw Marshal.GetExceptionForHR(hresult);
}
}
}
/// <summary>
/// Enumerates the supported input types
/// </summary>
/// <param name="inputStreamIndex">Input stream index</param>
/// <returns>Enumeration of input types</returns>
public IEnumerable<DmoMediaType> GetInputTypes(int inputStreamIndex)
{
int typeIndex = 0;
DmoMediaType? mediaType;
while ((mediaType = GetInputType(inputStreamIndex,typeIndex)) != null)
{
yield return mediaType.Value;
typeIndex++;
}
}
/// <summary>
/// Enumerates the output types
/// </summary>
/// <param name="outputStreamIndex">Output stream index</param>
/// <returns>Enumeration of supported output types</returns>
public IEnumerable<DmoMediaType> GetOutputTypes(int outputStreamIndex)
{
int typeIndex = 0;
DmoMediaType? mediaType;
while ((mediaType = GetOutputType(outputStreamIndex, typeIndex)) != null)
{
yield return mediaType.Value;
typeIndex++;
}
}
#endregion
#region Set Input Type
/// <summary>
/// Querys whether a specified input type is supported
/// </summary>
/// <param name="inputStreamIndex">Input stream index</param>
/// <param name="mediaType">Media type to check</param>
/// <returns>true if supports</returns>
public bool SupportsInputType(int inputStreamIndex, DmoMediaType mediaType)
{
return SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
}
/// <summary>
/// Sets the input type helper method
/// </summary>
/// <param name="inputStreamIndex">Input stream index</param>
/// <param name="mediaType">Media type</param>
/// <param name="flags">Flags (can be used to test rather than set)</param>
private bool SetInputType(int inputStreamIndex, DmoMediaType mediaType, DmoSetTypeFlags flags)
{
int hResult = mediaObject.SetInputType(inputStreamIndex, ref mediaType, flags);
if (hResult != HResult.S_OK)
{
if (hResult == (int)DmoHResults.DMO_E_INVALIDSTREAMINDEX)
{
throw new ArgumentException("Invalid stream index");
}
if (hResult == (int)DmoHResults.DMO_E_TYPE_NOT_ACCEPTED)
{
Debug.WriteLine("Media type was not accepted");
}
return false;
}
return true;
}
/// <summary>
/// Sets the input type
/// </summary>
/// <param name="inputStreamIndex">Input stream index</param>
/// <param name="mediaType">Media Type</param>
public void SetInputType(int inputStreamIndex, DmoMediaType mediaType)
{
if(!SetInputType(inputStreamIndex,mediaType,DmoSetTypeFlags.None))
{
throw new ArgumentException("Media Type not supported");
}
}
/// <summary>
/// Sets the input type to the specified Wave format
/// </summary>
/// <param name="inputStreamIndex">Input stream index</param>
/// <param name="waveFormat">Wave format</param>
public void SetInputWaveFormat(int inputStreamIndex, WaveFormat waveFormat)
{
DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
bool set = SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.None);
DmoInterop.MoFreeMediaType(ref mediaType);
if (!set)
{
throw new ArgumentException("Media Type not supported");
}
}
/// <summary>
/// Requests whether the specified Wave format is supported as an input
/// </summary>
/// <param name="inputStreamIndex">Input stream index</param>
/// <param name="waveFormat">Wave format</param>
/// <returns>true if supported</returns>
public bool SupportsInputWaveFormat(int inputStreamIndex, WaveFormat waveFormat)
{
DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
bool supported = SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
DmoInterop.MoFreeMediaType(ref mediaType);
return supported;
}
/// <summary>
/// Helper function to make a DMO Media Type to represent a particular WaveFormat
/// </summary>
private DmoMediaType CreateDmoMediaTypeForWaveFormat(WaveFormat waveFormat)
{
DmoMediaType mediaType = new DmoMediaType();
int waveFormatExSize = Marshal.SizeOf(waveFormat); // 18 + waveFormat.ExtraSize;
DmoInterop.MoInitMediaType(ref mediaType, waveFormatExSize);
mediaType.SetWaveFormat(waveFormat);
return mediaType;
}
#endregion
#region Set Output Type
/// <summary>
/// Checks if a specified output type is supported
/// n.b. you may need to set the input type first
/// </summary>
/// <param name="outputStreamIndex">Output stream index</param>
/// <param name="mediaType">Media type</param>
/// <returns>True if supported</returns>
public bool SupportsOutputType(int outputStreamIndex, DmoMediaType mediaType)
{
return SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
}
/// <summary>
/// Tests if the specified Wave Format is supported for output
/// n.b. may need to set the input type first
/// </summary>
/// <param name="outputStreamIndex">Output stream index</param>
/// <param name="waveFormat">Wave format</param>
/// <returns>True if supported</returns>
public bool SupportsOutputWaveFormat(int outputStreamIndex, WaveFormat waveFormat)
{
DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
bool supported = SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
DmoInterop.MoFreeMediaType(ref mediaType);
return supported;
}
/// <summary>
/// Helper method to call SetOutputType
/// </summary>
private bool SetOutputType(int outputStreamIndex, DmoMediaType mediaType, DmoSetTypeFlags flags)
{
int hresult = mediaObject.SetOutputType(outputStreamIndex, ref mediaType, flags);
if (hresult == (int)DmoHResults.DMO_E_TYPE_NOT_ACCEPTED)
{
return false;
}
else if (hresult == HResult.S_OK)
{
return true;
}
else
{
throw Marshal.GetExceptionForHR(hresult);
}
}
/// <summary>
/// Sets the output type
/// n.b. may need to set the input type first
/// </summary>
/// <param name="outputStreamIndex">Output stream index</param>
/// <param name="mediaType">Media type to set</param>
public void SetOutputType(int outputStreamIndex, DmoMediaType mediaType)
{
if (!SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.None))
{
throw new ArgumentException("Media Type not supported");
}
}
/// <summary>
/// Set output type to the specified wave format
/// n.b. may need to set input type first
/// </summary>
/// <param name="outputStreamIndex">Output stream index</param>
/// <param name="waveFormat">Wave format</param>
public void SetOutputWaveFormat(int outputStreamIndex, WaveFormat waveFormat)
{
DmoMediaType mediaType = CreateDmoMediaTypeForWaveFormat(waveFormat);
bool succeeded = SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.None);
DmoInterop.MoFreeMediaType(ref mediaType);
if (!succeeded)
{
throw new ArgumentException("Media Type not supported");
}
}
#endregion
#region Get Input and Output Size Info
/// <summary>
/// Get Input Size Info
/// </summary>
/// <param name="inputStreamIndex">Input Stream Index</param>
/// <returns>Input Size Info</returns>
public MediaObjectSizeInfo GetInputSizeInfo(int inputStreamIndex)
{
int size;
int maxLookahead;
int alignment;
Marshal.ThrowExceptionForHR(mediaObject.GetInputSizeInfo(inputStreamIndex, out size, out maxLookahead, out alignment));
return new MediaObjectSizeInfo(size, maxLookahead, alignment);
}
/// <summary>
/// Get Output Size Info
/// </summary>
/// <param name="outputStreamIndex">Output Stream Index</param>
/// <returns>Output Size Info</returns>
public MediaObjectSizeInfo GetOutputSizeInfo(int outputStreamIndex)
{
int size;
int alignment;
Marshal.ThrowExceptionForHR(mediaObject.GetOutputSizeInfo(outputStreamIndex, out size, out alignment));
return new MediaObjectSizeInfo(size, 0, alignment);
}
#endregion
#region Buffer Processing
/// <summary>
/// Process Input
/// </summary>
/// <param name="inputStreamIndex">Input Stream index</param>
/// <param name="mediaBuffer">Media Buffer</param>
/// <param name="flags">Flags</param>
/// <param name="timestamp">Timestamp</param>
/// <param name="duration">Duration</param>
public void ProcessInput(int inputStreamIndex, IMediaBuffer mediaBuffer, DmoInputDataBufferFlags flags,
long timestamp, long duration)
{
Marshal.ThrowExceptionForHR(mediaObject.ProcessInput(inputStreamIndex, mediaBuffer, flags, timestamp, duration));
}
/// <summary>
/// Process Output
/// </summary>
/// <param name="flags">Flags</param>
/// <param name="outputBufferCount">Output buffer count</param>
/// <param name="outputBuffers">Output buffers</param>
public void ProcessOutput(DmoProcessOutputFlags flags, int outputBufferCount, DmoOutputDataBuffer[] outputBuffers)
{
int reserved;
Marshal.ThrowExceptionForHR(mediaObject.ProcessOutput(flags, outputBufferCount, outputBuffers, out reserved));
}
#endregion
/// <summary>
/// Gives the DMO a chance to allocate any resources needed for streaming
/// </summary>
public void AllocateStreamingResources()
{
Marshal.ThrowExceptionForHR(mediaObject.AllocateStreamingResources());
}
/// <summary>
/// Tells the DMO to free any resources needed for streaming
/// </summary>
public void FreeStreamingResources()
{
Marshal.ThrowExceptionForHR(mediaObject.FreeStreamingResources());
}
/// <summary>
/// Gets maximum input latency
/// </summary>
/// <param name="inputStreamIndex">input stream index</param>
/// <returns>Maximum input latency as a ref-time</returns>
public long GetInputMaxLatency(int inputStreamIndex)
{
long maxLatency;
Marshal.ThrowExceptionForHR(mediaObject.GetInputMaxLatency(inputStreamIndex, out maxLatency));
return maxLatency;
}
/// <summary>
/// Flushes all buffered data
/// </summary>
public void Flush()
{
Marshal.ThrowExceptionForHR(mediaObject.Flush());
}
/// <summary>
/// Report a discontinuity on the specified input stream
/// </summary>
/// <param name="inputStreamIndex">Input Stream index</param>
public void Discontinuity(int inputStreamIndex)
{
Marshal.ThrowExceptionForHR(mediaObject.Discontinuity(inputStreamIndex));
}
/// <summary>
/// Is this input stream accepting data?
/// </summary>
/// <param name="inputStreamIndex">Input Stream index</param>
/// <returns>true if accepting data</returns>
public bool IsAcceptingData(int inputStreamIndex)
{
DmoInputStatusFlags flags;
int hresult = mediaObject.GetInputStatus(inputStreamIndex, out flags);
Marshal.ThrowExceptionForHR(hresult);
return (flags & DmoInputStatusFlags.DMO_INPUT_STATUSF_ACCEPT_DATA) == DmoInputStatusFlags.DMO_INPUT_STATUSF_ACCEPT_DATA;
}
// TODO: there are still several IMediaObject functions to be wrapped
#region IDisposable Members
/// <summary>
/// Experimental code, not currently being called
/// Not sure if it is necessary anyway
/// </summary>
public void Dispose()
{
if (mediaObject != null)
{
Marshal.ReleaseComObject(mediaObject);
mediaObject = null;
}
}
#endregion
}
}