using System;
using System.Collections.Generic;
using NAudio.Utils;
using System.Runtime.InteropServices;
using NAudio.Wave;
using System.Diagnostics;
namespace NAudio.Dmo
{
///
/// Media Object
///
public class MediaObject : IDisposable
{
private IMediaObject mediaObject;
private readonly int inputStreams;
private readonly int outputStreams;
#region Construction
///
/// Creates a new Media Object
///
/// Media Object COM interface
internal MediaObject(IMediaObject mediaObject)
{
this.mediaObject = mediaObject;
mediaObject.GetStreamCount(out inputStreams, out outputStreams);
}
#endregion
#region Public Properties
///
/// Number of input streams
///
public int InputStreamCount
{
get { return inputStreams; }
}
///
/// Number of output streams
///
public int OutputStreamCount
{
get { return outputStreams; }
}
#endregion
#region Get Input and Output Types
///
/// Gets the input media type for the specified input stream
///
/// Input stream index
/// Input type index
/// DMO Media Type or null if there are no more input types
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;
}
///
/// Gets the DMO Media Output type
///
/// The output stream
/// Output type index
/// DMO Media Type or null if no more available
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;
}
///
/// retrieves the media type that was set for an output stream, if any
///
/// Output stream index
/// DMO Media Type or null if no more available
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);
}
}
}
///
/// Enumerates the supported input types
///
/// Input stream index
/// Enumeration of input types
public IEnumerable GetInputTypes(int inputStreamIndex)
{
int typeIndex = 0;
DmoMediaType? mediaType;
while ((mediaType = GetInputType(inputStreamIndex,typeIndex)) != null)
{
yield return mediaType.Value;
typeIndex++;
}
}
///
/// Enumerates the output types
///
/// Output stream index
/// Enumeration of supported output types
public IEnumerable GetOutputTypes(int outputStreamIndex)
{
int typeIndex = 0;
DmoMediaType? mediaType;
while ((mediaType = GetOutputType(outputStreamIndex, typeIndex)) != null)
{
yield return mediaType.Value;
typeIndex++;
}
}
#endregion
#region Set Input Type
///
/// Querys whether a specified input type is supported
///
/// Input stream index
/// Media type to check
/// true if supports
public bool SupportsInputType(int inputStreamIndex, DmoMediaType mediaType)
{
return SetInputType(inputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
}
///
/// Sets the input type helper method
///
/// Input stream index
/// Media type
/// Flags (can be used to test rather than set)
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;
}
///
/// Sets the input type
///
/// Input stream index
/// Media Type
public void SetInputType(int inputStreamIndex, DmoMediaType mediaType)
{
if(!SetInputType(inputStreamIndex,mediaType,DmoSetTypeFlags.None))
{
throw new ArgumentException("Media Type not supported");
}
}
///
/// Sets the input type to the specified Wave format
///
/// Input stream index
/// Wave format
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");
}
}
///
/// Requests whether the specified Wave format is supported as an input
///
/// Input stream index
/// Wave format
/// true if supported
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;
}
///
/// Helper function to make a DMO Media Type to represent a particular WaveFormat
///
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
///
/// Checks if a specified output type is supported
/// n.b. you may need to set the input type first
///
/// Output stream index
/// Media type
/// True if supported
public bool SupportsOutputType(int outputStreamIndex, DmoMediaType mediaType)
{
return SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.DMO_SET_TYPEF_TEST_ONLY);
}
///
/// Tests if the specified Wave Format is supported for output
/// n.b. may need to set the input type first
///
/// Output stream index
/// Wave format
/// True if supported
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;
}
///
/// Helper method to call SetOutputType
///
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);
}
}
///
/// Sets the output type
/// n.b. may need to set the input type first
///
/// Output stream index
/// Media type to set
public void SetOutputType(int outputStreamIndex, DmoMediaType mediaType)
{
if (!SetOutputType(outputStreamIndex, mediaType, DmoSetTypeFlags.None))
{
throw new ArgumentException("Media Type not supported");
}
}
///
/// Set output type to the specified wave format
/// n.b. may need to set input type first
///
/// Output stream index
/// Wave format
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
///
/// Get Input Size Info
///
/// Input Stream Index
/// Input Size Info
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);
}
///
/// Get Output Size Info
///
/// Output Stream Index
/// Output Size Info
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
///
/// Process Input
///
/// Input Stream index
/// Media Buffer
/// Flags
/// Timestamp
/// Duration
public void ProcessInput(int inputStreamIndex, IMediaBuffer mediaBuffer, DmoInputDataBufferFlags flags,
long timestamp, long duration)
{
Marshal.ThrowExceptionForHR(mediaObject.ProcessInput(inputStreamIndex, mediaBuffer, flags, timestamp, duration));
}
///
/// Process Output
///
/// Flags
/// Output buffer count
/// Output buffers
public void ProcessOutput(DmoProcessOutputFlags flags, int outputBufferCount, DmoOutputDataBuffer[] outputBuffers)
{
int reserved;
Marshal.ThrowExceptionForHR(mediaObject.ProcessOutput(flags, outputBufferCount, outputBuffers, out reserved));
}
#endregion
///
/// Gives the DMO a chance to allocate any resources needed for streaming
///
public void AllocateStreamingResources()
{
Marshal.ThrowExceptionForHR(mediaObject.AllocateStreamingResources());
}
///
/// Tells the DMO to free any resources needed for streaming
///
public void FreeStreamingResources()
{
Marshal.ThrowExceptionForHR(mediaObject.FreeStreamingResources());
}
///
/// Gets maximum input latency
///
/// input stream index
/// Maximum input latency as a ref-time
public long GetInputMaxLatency(int inputStreamIndex)
{
long maxLatency;
Marshal.ThrowExceptionForHR(mediaObject.GetInputMaxLatency(inputStreamIndex, out maxLatency));
return maxLatency;
}
///
/// Flushes all buffered data
///
public void Flush()
{
Marshal.ThrowExceptionForHR(mediaObject.Flush());
}
///
/// Report a discontinuity on the specified input stream
///
/// Input Stream index
public void Discontinuity(int inputStreamIndex)
{
Marshal.ThrowExceptionForHR(mediaObject.Discontinuity(inputStreamIndex));
}
///
/// Is this input stream accepting data?
///
/// Input Stream index
/// true if accepting data
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
///
/// Experimental code, not currently being called
/// Not sure if it is necessary anyway
///
public void Dispose()
{
if (mediaObject != null)
{
Marshal.ReleaseComObject(mediaObject);
mediaObject = null;
}
}
#endregion
}
}