mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-22 09:34:51 +00:00
748 lines
23 KiB
C#
748 lines
23 KiB
C#
using DirectShowLib;
|
|
using System;
|
|
using System.Runtime.ExceptionServices;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace WPFMediaKit.DirectShow.MediaPlayers;
|
|
|
|
[ComImport, Guid("04FE9017-F873-410E-871E-AB91661A4EF7")]
|
|
internal class FFDShow
|
|
{
|
|
}
|
|
|
|
[ComImport, Guid("B98D13E7-55DB-4385-A33D-09FD1BA26338")]
|
|
public class LAVSplitterSource { }
|
|
|
|
[ComImport, Guid("171252A0-8820-4AFE-9DF8-5C92B2D66B04")]
|
|
public class LAVSplitter { }
|
|
|
|
[ComImport]
|
|
[Guid("e436ebb5-524f-11ce-9f53-0020af0ba770")]
|
|
public class AsyncReader { }
|
|
|
|
/// <summary>
|
|
/// The MediaUriPlayer plays media files from a given Uri.
|
|
/// </summary>
|
|
public class MediaUriPlayer : MediaSeekingPlayer
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(MediaUriPlayer));
|
|
|
|
/// <summary>
|
|
/// The name of the default audio render. This is the
|
|
/// same on all versions of windows
|
|
/// </summary>
|
|
public const string DEFAULT_AUDIO_RENDERER_NAME = "Default DirectSound Device";
|
|
|
|
/// <summary>
|
|
/// Set the default audio renderer property backing
|
|
/// </summary>
|
|
private string m_audioRenderer = DEFAULT_AUDIO_RENDERER_NAME;
|
|
|
|
#if DEBUG
|
|
|
|
/// <summary>
|
|
/// Used to view the graph in graphedit
|
|
/// </summary>
|
|
private DsROTEntry m_dsRotEntry;
|
|
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// The DirectShow graph interface. In this example
|
|
/// We keep reference to this so we can dispose
|
|
/// of it later.
|
|
/// </summary>
|
|
private IGraphBuilder m_graph;
|
|
|
|
public IGraphBuilder Graph
|
|
{
|
|
get { return m_graph; }
|
|
}
|
|
|
|
public MediaUriPlayer()
|
|
{
|
|
LAVFilterDirectory = "./";
|
|
|
|
//we are going to use this source for playback because it does not lock the file
|
|
AsyncFileSource = new FilterName("System AsyncFileSource", ClassId.FilesyncSource, "Not applicable");
|
|
|
|
//we are only using this source for determine if stream has video(bool HasVideo) NOT for playback
|
|
// because this source locks the file
|
|
SplitterSource = new FilterName("LAV Splitter Source", ClassId.LAVFilterSource, "LAVSplitter.ax");
|
|
|
|
Splitter = new FilterName("LAV Splitter", ClassId.LAVFilter, "LAVSplitter.ax");
|
|
VideoDecoder = new FilterName("LAV Video Decoder", ClassId.LAVFilterVideo, "LAVVideo.ax");
|
|
AudioDecoder = new FilterName("LAV Audio Decoder", ClassId.LAVFilterAudio, "LAVAudio.ax");
|
|
}
|
|
|
|
/// <summary>
|
|
/// The media Uri
|
|
/// </summary>
|
|
private Uri m_sourceUri;
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Uri source of the media
|
|
/// </summary>
|
|
public Uri Source
|
|
{
|
|
get
|
|
{
|
|
VerifyAccess();
|
|
return m_sourceUri;
|
|
}
|
|
set
|
|
{
|
|
VerifyAccess();
|
|
m_sourceUri = value;
|
|
|
|
OpenSource();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return Source as a string path or uri.
|
|
/// </summary>
|
|
private string FileSource
|
|
{
|
|
get
|
|
{
|
|
if (m_sourceUri == null)
|
|
return null;
|
|
if (m_sourceUri.IsFile)
|
|
return m_sourceUri.LocalPath;
|
|
return m_sourceUri.ToString();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The renderer type to use when
|
|
/// rendering video
|
|
/// </summary>
|
|
public VideoRendererType VideoRenderer
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implementation of framestepinterface to step video forward by minimum of one frame e.g. by mousewheel
|
|
/// </summary>
|
|
private IVideoFrameStep frameStep;
|
|
|
|
/// <summary>
|
|
/// step the frames
|
|
/// </summary>
|
|
public void StepFrame(int framecount)
|
|
{
|
|
int i = frameStep.CanStep(0, null);
|
|
if (i == 0)
|
|
{
|
|
this.Play();
|
|
frameStep.Step(framecount, null);
|
|
this.PlayerState = PlayerState.SteppingFrames;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Some video renderers support stepping media frame by frame with the
|
|
// IVideoFrameStep interface. See the interface documentation for more
|
|
// details on frame stepping.
|
|
//
|
|
private bool GetFrameStepInterface()
|
|
{
|
|
int hr = 0;
|
|
|
|
IVideoFrameStep frameStepTest = null;
|
|
|
|
// Get the frame step interface, if supported
|
|
frameStepTest = (IVideoFrameStep)this.m_graph;
|
|
|
|
// Check if this decoder can step
|
|
hr = frameStepTest.CanStep(0, null);
|
|
if (hr == 0)
|
|
{
|
|
this.frameStep = frameStepTest;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
this.frameStep = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The name of the audio renderer device
|
|
/// </summary>
|
|
public string AudioRenderer
|
|
{
|
|
get
|
|
{
|
|
VerifyAccess();
|
|
return m_audioRenderer;
|
|
}
|
|
set
|
|
{
|
|
VerifyAccess();
|
|
m_audioRenderer = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets if the media should play in loop
|
|
/// or if it should just stop when the media is complete
|
|
/// </summary>
|
|
public bool Loop { get; set; }
|
|
|
|
/// <summary>
|
|
/// Is ran everytime a new media event occurs on the graph
|
|
/// </summary>
|
|
/// <param name="code">The Event code that occured</param>
|
|
/// <param name="lparam1">The first event parameter sent by the graph</param>
|
|
/// <param name="lparam2">The second event parameter sent by the graph</param>
|
|
protected override void OnMediaEvent(EventCode code, IntPtr lparam1, IntPtr lparam2)
|
|
{
|
|
if (Loop)
|
|
{
|
|
switch (code)
|
|
{
|
|
case EventCode.Complete:
|
|
MediaPosition = 0;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
/* Only run the base when we don't loop
|
|
* otherwise the default behavior is to
|
|
* fire a media ended event */
|
|
base.OnMediaEvent(code, lparam1, lparam2);
|
|
}
|
|
|
|
public void DumpGraphInfo(string fileName)
|
|
{
|
|
DumpGraphInfo(fileName, m_graph);
|
|
}
|
|
|
|
public void DumpGraphInfo(string fileName, IGraphBuilder graph)
|
|
{
|
|
if (string.IsNullOrEmpty(fileName) || graph == null) return;
|
|
|
|
string filterOutputString = string.Empty;
|
|
|
|
IEnumFilters enumFilters;
|
|
int hr = graph.EnumFilters(out enumFilters);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
IntPtr fetched = IntPtr.Zero;
|
|
|
|
IBaseFilter[] filters = { null };
|
|
|
|
int r = 0;
|
|
while (r == 0)
|
|
{
|
|
try
|
|
{
|
|
r = enumFilters.Next(filters.Length, filters, fetched);
|
|
DsError.ThrowExceptionForHR(r);
|
|
|
|
if (filters == null || filters.Length == 0 || filters[0] == null) continue;
|
|
|
|
FilterInfo filterInfo;
|
|
filters[0].QueryFilterInfo(out filterInfo);
|
|
|
|
filterOutputString += string.Format("{0:X8}", Marshal.GetIUnknownForObjectInContext(filters[0]).ToInt32()) + " ";
|
|
|
|
filterOutputString += filterInfo.achName + Environment.NewLine;
|
|
|
|
/* We will want to enum all the pins on the source filter */
|
|
IEnumPins pinEnum;
|
|
|
|
hr = filters[0].EnumPins(out pinEnum);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
IntPtr fetched2 = IntPtr.Zero;
|
|
IPin[] pins = { null };
|
|
|
|
while (pinEnum.Next(pins.Length, pins, fetched2) == 0)
|
|
{
|
|
PinInfo pinInfo;
|
|
pins[0].QueryPinInfo(out pinInfo);
|
|
|
|
var prefix = "[In ] ";
|
|
if (pinInfo.dir == PinDirection.Output)
|
|
prefix = "[Out] ";
|
|
|
|
filterOutputString += string.Format("{0:X8}", Marshal.GetIUnknownForObjectInContext(pins[0]).ToInt32()) + " ";
|
|
filterOutputString += prefix + pinInfo.name + Environment.NewLine;
|
|
|
|
Marshal.ReleaseComObject(pins[0]);
|
|
}
|
|
|
|
Marshal.ReleaseComObject(pinEnum);
|
|
}
|
|
catch
|
|
{
|
|
r = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
Marshal.ReleaseComObject(enumFilters);
|
|
|
|
var file2 = System.IO.File.CreateText(fileName);
|
|
file2.AutoFlush = true;
|
|
file2.Write(filterOutputString);
|
|
file2.Close();
|
|
}
|
|
|
|
public string LAVFilterDirectory { get; set; }
|
|
public FilterName Splitter { get; set; }
|
|
public FilterName SplitterSource { get; set; }
|
|
public FilterName VideoDecoder { get; set; }
|
|
public FilterName AudioDecoder { get; set; }
|
|
public FilterName AsyncFileSource { get; set; }
|
|
|
|
/// <summary>
|
|
/// Opens the media by initializing the DirectShow graph
|
|
/// </summary>
|
|
[HandleProcessCorruptedStateExceptions]
|
|
protected virtual void OpenSource()
|
|
{
|
|
/* Make sure we clean up any remaining mess */
|
|
FreeResources();
|
|
|
|
string fileSource = FileSource;
|
|
|
|
if (string.IsNullOrEmpty(fileSource))
|
|
return;
|
|
|
|
try
|
|
{
|
|
//lets get over with it right here
|
|
HasVideo = DoesItHaveVideo(FileSource);
|
|
GC.Collect();
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
/* Creates the GraphBuilder COM object */
|
|
m_graph = new FilterGraphNoThread() as IGraphBuilder;
|
|
|
|
if (m_graph == null)
|
|
throw new WPFMediaKitException("Could not create a graph");
|
|
|
|
var filterGraph = m_graph as IFilterGraph2;
|
|
|
|
if (filterGraph == null)
|
|
throw new WPFMediaKitException("Could not QueryInterface for the IFilterGraph2");
|
|
|
|
IBaseFilter sourceFilter;
|
|
int hr;
|
|
|
|
// Set LAV Splitter
|
|
/* LAVSplitterSource reader = new LAVSplitterSource();
|
|
sourceFilter = reader as IBaseFilter;
|
|
var objectWithSite = reader as IObjectWithSite;
|
|
if (objectWithSite != null)
|
|
{
|
|
objectWithSite.SetSite(this);
|
|
}
|
|
|
|
hr = m_graph.AddFilter(sourceFilter, SplitterSource);
|
|
DsError.ThrowExceptionForHR(hr);*/
|
|
|
|
//we are using AsyncFileSource here, so that file will not be locked during preview
|
|
sourceFilter = DirectShowUtil.AddFilterToGraph(m_graph, AsyncFileSource, LAVFilterDirectory, Guid.Empty);
|
|
if (sourceFilter == null)
|
|
throw new WPFMediaKitException("Could not add SplitterSource to graph.");
|
|
|
|
IFileSourceFilter interfaceFile = (IFileSourceFilter)sourceFilter;
|
|
hr = interfaceFile.Load(fileSource, null);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
// we are going to need LavSpltter here, to connect AsyncFileSource with VideoDecoder
|
|
DirectShowUtil.AddFilterToGraph(m_graph, Splitter, LAVFilterDirectory, Guid.Empty);
|
|
|
|
DirectShowUtil.AddFilterToGraph(m_graph, VideoDecoder, LAVFilterDirectory, Guid.Empty);
|
|
|
|
try
|
|
{
|
|
// use preffered audio filter
|
|
InsertAudioFilter(sourceFilter, AudioDecoder);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// codecs misconfigured
|
|
log.Error(ex, "Cannot add audio decoder: {0}", AudioDecoder);
|
|
}
|
|
|
|
// use prefered audio renderer
|
|
try
|
|
{
|
|
InsertAudioRenderer(AudioRenderer);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error(ex, "Cannot add audio render: {0}", AudioRenderer);
|
|
}
|
|
|
|
IBaseFilter renderer = CreateVideoRenderer(VideoRenderer, m_graph, 2);
|
|
|
|
/* We will want to enum all the pins on the source filter */
|
|
IEnumPins pinEnum;
|
|
|
|
hr = sourceFilter.EnumPins(out pinEnum);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
IntPtr fetched = IntPtr.Zero;
|
|
IPin[] pins = { null };
|
|
|
|
/* Counter for how many pins successfully rendered */
|
|
int pinsRendered = 0;
|
|
|
|
if (VideoRenderer == VideoRendererType.VideoMixingRenderer9)
|
|
{
|
|
var mixer = renderer as IVMRMixerControl9;
|
|
|
|
if (mixer != null)
|
|
{
|
|
VMR9MixerPrefs dwPrefs;
|
|
mixer.GetMixingPrefs(out dwPrefs);
|
|
dwPrefs &= ~VMR9MixerPrefs.RenderTargetMask;
|
|
dwPrefs |= VMR9MixerPrefs.RenderTargetRGB;
|
|
//mixer.SetMixingPrefs(dwPrefs);
|
|
}
|
|
}
|
|
|
|
/* Loop over each pin of the source filter */
|
|
while (pinEnum.Next(pins.Length, pins, fetched) == 0)
|
|
{
|
|
if (filterGraph.RenderEx(pins[0],
|
|
AMRenderExFlags.RenderToExistingRenderers,
|
|
IntPtr.Zero) >= 0)
|
|
pinsRendered++;
|
|
|
|
Marshal.ReleaseComObject(pins[0]);
|
|
}
|
|
|
|
Marshal.ReleaseComObject(pinEnum);
|
|
Marshal.ReleaseComObject(sourceFilter);
|
|
|
|
if (pinsRendered == 0)
|
|
throw new WPFMediaKitException("Could not render any streams from the source Uri");
|
|
|
|
#if DEBUG
|
|
/* Adds the GB to the ROT so we can view
|
|
* it in graphedit */
|
|
m_dsRotEntry = new DsROTEntry(m_graph);
|
|
#endif
|
|
/* Configure the graph in the base class */
|
|
SetupFilterGraph(m_graph);
|
|
|
|
GetFrameStepInterface();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
/* This exection will happen usually if the media does
|
|
* not exist or could not open due to not having the
|
|
* proper filters installed */
|
|
|
|
// Fallback try auto graph:
|
|
var result = oldOpenSource();
|
|
|
|
if (!result)
|
|
{
|
|
FreeResources();
|
|
|
|
HasVideo = false;
|
|
|
|
/* Fire our failed event */
|
|
InvokeMediaFailed(new MediaFailedEventArgs(ex.Message, ex));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
InvokeMediaOpened();
|
|
}
|
|
|
|
private bool DoesItHaveVideo(string filename)
|
|
{
|
|
if (string.IsNullOrEmpty(filename))
|
|
return false;
|
|
bool result;
|
|
IGraphBuilder temp_graph = new FilterGraphNoThread() as IGraphBuilder;
|
|
try
|
|
{
|
|
if (temp_graph == null)
|
|
throw new WPFMediaKitException("Could not create a graph");
|
|
|
|
var filterGraph = temp_graph as IFilterGraph2;
|
|
|
|
if (filterGraph == null)
|
|
throw new WPFMediaKitException("Could not QueryInterface for the IFilterGraph2");
|
|
|
|
IBaseFilter sourceFilter;
|
|
int hr;
|
|
|
|
sourceFilter = DirectShowUtil.AddFilterToGraph(temp_graph, SplitterSource, LAVFilterDirectory, Guid.Empty);
|
|
if (sourceFilter == null)
|
|
throw new WPFMediaKitException("Could not add SplitterSource to graph.");
|
|
|
|
IFileSourceFilter interfaceFile = (IFileSourceFilter)sourceFilter;
|
|
hr = interfaceFile.Load(filename, null);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
// Set Video Codec
|
|
// Remove Pin
|
|
var videoPinFrom = DirectShowLib.DsFindPin.ByName(sourceFilter, "Video");
|
|
IPin videoPinTo;
|
|
if (videoPinFrom != null)
|
|
{
|
|
hr = videoPinFrom.ConnectedTo(out videoPinTo);
|
|
if (hr >= 0 && videoPinTo != null)
|
|
{
|
|
PinInfo pInfo;
|
|
videoPinTo.QueryPinInfo(out pInfo);
|
|
FilterInfo fInfo;
|
|
pInfo.filter.QueryFilterInfo(out fInfo);
|
|
|
|
DirectShowUtil.DisconnectAllPins(temp_graph, pInfo.filter);
|
|
temp_graph.RemoveFilter(pInfo.filter);
|
|
|
|
DsUtils.FreePinInfo(pInfo);
|
|
Marshal.ReleaseComObject(fInfo.pGraph);
|
|
Marshal.ReleaseComObject(videoPinTo);
|
|
videoPinTo = null;
|
|
}
|
|
Marshal.ReleaseComObject(videoPinFrom);
|
|
videoPinFrom = null;
|
|
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
|
|
DirectShowUtil.RemoveFilters(temp_graph, SplitterSource.Name);
|
|
Marshal.FinalReleaseComObject(sourceFilter);
|
|
}
|
|
catch
|
|
{ result = false; }
|
|
finally
|
|
{
|
|
Marshal.ReleaseComObject(temp_graph);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
[HandleProcessCorruptedStateExceptions]
|
|
private bool oldOpenSource()
|
|
{
|
|
/* Make sure we clean up any remaining mess */
|
|
FreeResources();
|
|
|
|
string fileSource = FileSource;
|
|
|
|
if (string.IsNullOrEmpty(fileSource))
|
|
return false;
|
|
|
|
try
|
|
{
|
|
/* Creates the GraphBuilder COM object */
|
|
m_graph = new FilterGraphNoThread() as IGraphBuilder;
|
|
|
|
if (m_graph == null)
|
|
throw new WPFMediaKitException("Could not create a graph");
|
|
|
|
// use prefered audio renderer
|
|
try
|
|
{
|
|
InsertAudioRenderer(AudioRenderer);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.Error(ex, "Cannot add audio render: {0}", AudioRenderer);
|
|
}
|
|
|
|
IBaseFilter renderer = CreateVideoRenderer(VideoRenderer, m_graph, 2);
|
|
|
|
var filterGraph = m_graph as IFilterGraph2;
|
|
|
|
if (filterGraph == null)
|
|
throw new WPFMediaKitException("Could not QueryInterface for the IFilterGraph2");
|
|
|
|
IBaseFilter sourceFilter;
|
|
|
|
/* Have DirectShow find the correct source filter for the Uri */
|
|
int hr = filterGraph.AddSourceFilter(fileSource, fileSource, out sourceFilter);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
/* Check for video stream*/
|
|
var videoPinFrom = DsFindPin.ByName(sourceFilter, "Video");
|
|
if (videoPinFrom != null)
|
|
{
|
|
Marshal.ReleaseComObject(videoPinFrom);
|
|
HasVideo = true;
|
|
}
|
|
else
|
|
{
|
|
HasVideo = false;
|
|
}
|
|
|
|
/* We will want to enum all the pins on the source filter */
|
|
IEnumPins pinEnum;
|
|
|
|
hr = sourceFilter.EnumPins(out pinEnum);
|
|
DsError.ThrowExceptionForHR(hr);
|
|
|
|
IntPtr fetched = IntPtr.Zero;
|
|
IPin[] pins = { null };
|
|
|
|
/* Counter for how many pins successfully rendered */
|
|
int pinsRendered = 0;
|
|
|
|
if (VideoRenderer == VideoRendererType.VideoMixingRenderer9)
|
|
{
|
|
var mixer = renderer as IVMRMixerControl9;
|
|
|
|
if (mixer != null)
|
|
{
|
|
VMR9MixerPrefs dwPrefs;
|
|
mixer.GetMixingPrefs(out dwPrefs);
|
|
dwPrefs &= ~VMR9MixerPrefs.RenderTargetMask;
|
|
dwPrefs |= VMR9MixerPrefs.RenderTargetRGB;
|
|
//mixer.SetMixingPrefs(dwPrefs);
|
|
}
|
|
}
|
|
|
|
/* Loop over each pin of the source filter */
|
|
while (pinEnum.Next(pins.Length, pins, fetched) == 0)
|
|
{
|
|
if (filterGraph.RenderEx(pins[0],
|
|
AMRenderExFlags.RenderToExistingRenderers,
|
|
IntPtr.Zero) >= 0)
|
|
pinsRendered++;
|
|
|
|
Marshal.ReleaseComObject(pins[0]);
|
|
}
|
|
|
|
Marshal.ReleaseComObject(pinEnum);
|
|
Marshal.ReleaseComObject(sourceFilter);
|
|
|
|
if (pinsRendered == 0)
|
|
throw new WPFMediaKitException("Could not render any streams from the source Uri");
|
|
|
|
#if DEBUG
|
|
/* Adds the GB to the ROT so we can view
|
|
* it in graphedit */
|
|
m_dsRotEntry = new DsROTEntry(m_graph);
|
|
#endif
|
|
/* Configure the graph in the base class */
|
|
SetupFilterGraph(m_graph);
|
|
|
|
/* Sets the NaturalVideoWidth/Height */
|
|
//SetNativePixelSizes(renderer);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
/* This exection will happen usually if the media does
|
|
* not exist or could not open due to not having the
|
|
* proper filters installed */
|
|
FreeResources();
|
|
|
|
/* Fire our failed event */
|
|
InvokeMediaFailed(new MediaFailedEventArgs(ex.Message, ex));
|
|
|
|
return false;
|
|
}
|
|
|
|
InvokeMediaOpened();
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts the audio renderer by the name.
|
|
/// </summary>
|
|
protected virtual void InsertAudioRenderer(string audioRenderer)
|
|
{
|
|
if (string.IsNullOrEmpty(audioRenderer))
|
|
return;
|
|
|
|
/* Add our prefered audio renderer */
|
|
AddFilterByName(m_graph, DirectShowLib.FilterCategory.AudioRendererCategory, audioRenderer);
|
|
}
|
|
|
|
protected virtual void InsertAudioFilter(IBaseFilter sourceFilter, FilterName audioDecoder)
|
|
{
|
|
if (audioDecoder.CLSID == Guid.Empty)
|
|
return;
|
|
|
|
// Set Audio Codec
|
|
// Remove Pin
|
|
var audioPinFrom = DirectShowLib.DsFindPin.ByName(sourceFilter, "Audio");
|
|
IPin audioPinTo;
|
|
if (audioPinFrom != null)
|
|
{
|
|
int hr = audioPinFrom.ConnectedTo(out audioPinTo);
|
|
if (hr >= 0 && audioPinTo != null)
|
|
{
|
|
PinInfo pInfo;
|
|
audioPinTo.QueryPinInfo(out pInfo);
|
|
FilterInfo fInfo;
|
|
pInfo.filter.QueryFilterInfo(out fInfo);
|
|
|
|
DirectShowUtil.DisconnectAllPins(m_graph, pInfo.filter);
|
|
m_graph.RemoveFilter(pInfo.filter);
|
|
|
|
DsUtils.FreePinInfo(pInfo);
|
|
Marshal.ReleaseComObject(fInfo.pGraph);
|
|
Marshal.ReleaseComObject(audioPinTo);
|
|
audioPinTo = null;
|
|
}
|
|
Marshal.ReleaseComObject(audioPinFrom);
|
|
audioPinFrom = null;
|
|
}
|
|
|
|
DirectShowUtil.AddFilterToGraph(m_graph, audioDecoder, LAVFilterDirectory, Guid.Empty);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Frees all unmanaged memory and resets the object back
|
|
/// to its initial state
|
|
/// </summary>
|
|
protected override void FreeResources()
|
|
{
|
|
#if DEBUG
|
|
/* Remove us from the ROT */
|
|
if (m_dsRotEntry != null)
|
|
{
|
|
m_dsRotEntry.Dispose();
|
|
m_dsRotEntry = null;
|
|
}
|
|
#endif
|
|
|
|
/* We run the StopInternal() to avoid any
|
|
* Dispatcher VeryifyAccess() issues because
|
|
* this may be called from the GC */
|
|
StopInternal();
|
|
|
|
if (m_graph != null)
|
|
{
|
|
DirectShowUtil.RemoveFilters(m_graph);
|
|
Marshal.ReleaseComObject(m_graph);
|
|
m_graph = null;
|
|
|
|
base.FreeResources();
|
|
|
|
/* Only run the media closed if we have an
|
|
* initialized filter graph */
|
|
InvokeMediaClosed(EventArgs.Empty);
|
|
}
|
|
else
|
|
{
|
|
base.FreeResources();
|
|
}
|
|
}
|
|
}
|