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 } }