/// <summary> /// Called from RenderWindow to add the renderer to the graph, create a sample grabber, add it /// to the graph and connect it all up /// </summary> /// <param name="icgb">ICaptureGraphBuilder2 to use</param> /// <param name="pCallback">ICaptureGraphBuilder2 to use</param> /// <param name="sType">String to use in creating filter graph object descriptions</param> /// <param name="pPin">Pin to connect from</param> /// <param name="ibfRenderer">Renderer to add</param> protected void RenderWindowHelper(ICaptureGraphBuilder2 icgb, CallbackHandler pCallback, string sType, IPin pPin, IBaseFilter ibfRenderer) { int hr; // Add the renderer to the graph hr = _graph.AddFilter(ibfRenderer, sType + " Renderer"); _dc.Add(ibfRenderer); DESError.ThrowExceptionForHR(hr); // Do everything else RenderHelper(icgb, pCallback, sType, pPin, null, ibfRenderer); }
/// <summary> /// Common routine used by RenderTo* /// </summary> /// <param name="icgb">ICaptureGraphBuilder2 to use</param> /// <param name="pCallback">Callback to use (or null)</param> /// <param name="sType">string to use in creating filter graph object descriptions</param> /// <param name="pPin">Pin to connect from</param> /// <param name="ibfCompressor">Compressor to use, or null for none</param> /// <param name="pOutput">Endpoint (renderer or file writer) to connect to</param> protected void RenderHelper(ICaptureGraphBuilder2 icgb, CallbackHandler pCallback, string sType, IPin pPin, IBaseFilter ibfCompressor, IBaseFilter pOutput) { int hr; IBaseFilter ibfSampleGrabber = null; try { // If no callback was provided, don't create a samplegrabber if (pCallback != null) { ISampleGrabber isg = (ISampleGrabber) new SampleGrabber(); ibfSampleGrabber = (IBaseFilter)isg; _dc.Add(ibfSampleGrabber); hr = isg.SetCallback(pCallback, 1); DESError.ThrowExceptionForHR(hr); hr = _graph.AddFilter(ibfSampleGrabber, sType + " sample grabber"); DESError.ThrowExceptionForHR(hr); } // If a compressor was provided, add it to the graph and connect it up if (ibfCompressor != null) { // Connect the pin. hr = _graph.AddFilter(ibfCompressor, sType + " Compressor"); DESError.ThrowExceptionForHR(hr); FilterGraphTools.ConnectFilters(_graph, pPin, ibfSampleGrabber, true); FilterGraphTools.ConnectFilters(_graph, ibfSampleGrabber, ibfCompressor, true); FilterGraphTools.ConnectFilters(_graph, ibfCompressor, pOutput, true); } else { // Just connect the SampleGrabber (if any) hr = icgb.RenderStream(null, null, pPin, ibfSampleGrabber, pOutput); DESError.ThrowExceptionForHR(hr); } } finally { if (ibfSampleGrabber != null) { Marshal.ReleaseComObject(ibfSampleGrabber); } } }
/// <summary> /// Configure the graph to output the results to a video window. /// </summary> /// <remarks> /// The callback routines are invoked once for each sample. This allows for additional processing to /// be performed on the video or audio buffers. /// </remarks> /// <param name="windowHandle">Window handle to render to, or IntPtr.Zero to render to its own window</param> /// <param name="videoParticipants">Callback routine to be called for each video frame or null for no callback</param> /// <param name="audioParticipants">Callback routine to be called for each audio frame or null for no callback</param> private void RenderToWindow(IntPtr windowHandle, ICallbackParticipant[] videoParticipants, ICallbackParticipant[] audioParticipants) { int hr; IPin pin; IVideoWindow videoWindow; IAMTimelineObj group; IAMTimeline desTimeline = Timeline.DesTimeline; // Contains useful routines for creating the graph var graphBuilder = (ICaptureGraphBuilder2) new CaptureGraphBuilder2(); try { hr = graphBuilder.SetFiltergraph(Graph); DESError.ThrowExceptionForHR(hr); int NumGroups; hr = desTimeline.GetGroupCount(out NumGroups); DESError.ThrowExceptionForHR(hr); // Walk the groups. For DESCombine, there is one group that // contains all the video, and a second group for the audio. for (int i = 0; i < NumGroups; i++) { hr = desTimeline.GetGroup(out group, i); DESError.ThrowExceptionForHR(hr); try { // Inform the graph we will be previewing (rather than writing to disk) var pTLGroup = (IAMTimelineGroup) group; hr = pTLGroup.SetPreviewMode(true); DESError.ThrowExceptionForHR(hr); } finally { // Release the group Marshal.ReleaseComObject(group); } // Get the IPin for the current group hr = RenderEngine.GetGroupOutputPin(i, out pin); DESError.ThrowExceptionForHR(hr); try { // If this is the video pin if (FilterGraphTools.IsVideo(pin)) { // Get a video renderer var ibfVideoRenderer = (IBaseFilter) new VideoRenderer(); try { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(videoParticipants); RenderWindowHelper(graphBuilder, mcb, "Video", pin, ibfVideoRenderer); } finally { Marshal.ReleaseComObject(ibfVideoRenderer); } } else { // Get an audio renderer var ibfAudioRenderer = (IBaseFilter) new AudioRender(); try { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(audioParticipants); RenderWindowHelper(graphBuilder, mcb, "Audio", pin, ibfAudioRenderer); } finally { Marshal.ReleaseComObject(ibfAudioRenderer); } } } finally { Marshal.ReleaseComObject(pin); } } // Configure the video window videoWindow = (IVideoWindow) Graph; // If a window handle was supplied, use it if (windowHandle != IntPtr.Zero) { hr = videoWindow.put_Owner(windowHandle); DESError.ThrowExceptionForHR(hr); } else { // Use our own window hr = videoWindow.put_Caption(Resources.DefaultVideoRenderingWindowCaption); DESError.ThrowExceptionForHR(hr); // since no user interaction is allowed, remove // system menu and maximize/minimize buttons WindowStyle lStyle = 0; hr = videoWindow.get_WindowStyle(out lStyle); DESError.ThrowExceptionForHR(hr); lStyle &= ~(WindowStyle.MinimizeBox | WindowStyle.MaximizeBox | WindowStyle.SysMenu); hr = videoWindow.put_WindowStyle(lStyle); DESError.ThrowExceptionForHR(hr); } } finally { Marshal.ReleaseComObject(graphBuilder); } }
protected void RenderGroups(ICaptureGraphBuilder2 graphBuilder, IBaseFilter audioCompressor, IBaseFilter videoCompressor, IBaseFilter audioDestination, IBaseFilter videoDestination, ICallbackParticipant[] audioParticipants, ICallbackParticipant[] videoParticipants) { int hr = 0; if (audioCompressor != null) _cleanup.Add(audioCompressor); if (videoCompressor != null) _cleanup.Add(videoCompressor); if (audioDestination != null) _cleanup.Add(audioDestination); if ((videoDestination != null) && (audioDestination != videoDestination)) _cleanup.Add(videoDestination); IAMTimeline desTimeline = _timeline.DesTimeline; int groupCount; hr = desTimeline.GetGroupCount(out groupCount); DESError.ThrowExceptionForHR(hr); // Walk the groups. For this class, there is one group that // contains all the video, and a second group for the audio. for (int i = (groupCount - 1); i >= 0; i--) { IAMTimelineObj group; hr = desTimeline.GetGroup(out group, i); DESError.ThrowExceptionForHR(hr); try { // Inform the graph we will be writing to disk (rather than previewing) var timelineGroup = (IAMTimelineGroup) group; hr = timelineGroup.SetPreviewMode(false); DESError.ThrowExceptionForHR(hr); } finally { Marshal.ReleaseComObject(group); } IPin pPin; // Get the IPin for the current group hr = _renderEngine.GetGroupOutputPin(i, out pPin); _cleanup.Add(pPin); DESError.ThrowExceptionForHR(hr); try { if (FilterGraphTools.IsVideo(pPin)) { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(videoParticipants); RenderHelper(graphBuilder, mcb, "Video", pPin, videoCompressor, videoDestination); } else { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(audioParticipants); RenderHelper(graphBuilder, mcb, "Audio", pPin, audioCompressor, audioDestination); } } finally { Marshal.ReleaseComObject(pPin); } } }
protected void RenderGroups(ICaptureGraphBuilder2 graphBuilder, IBaseFilter audioCompressor, IBaseFilter videoCompressor, IBaseFilter audioDestination, IBaseFilter videoDestination, ICallbackParticipant[] audioParticipants, ICallbackParticipant[] videoParticipants) { int hr = 0; if (audioCompressor != null) { _cleanup.Add(audioCompressor); } if (videoCompressor != null) { _cleanup.Add(videoCompressor); } if (audioDestination != null) { _cleanup.Add(audioDestination); } if ((videoDestination != null) && (audioDestination != videoDestination)) { _cleanup.Add(videoDestination); } IAMTimeline desTimeline = _timeline.DesTimeline; int groupCount; hr = desTimeline.GetGroupCount(out groupCount); DESError.ThrowExceptionForHR(hr); // Walk the groups. For this class, there is one group that // contains all the video, and a second group for the audio. for (int i = (groupCount - 1); i >= 0; i--) { IAMTimelineObj group; hr = desTimeline.GetGroup(out group, i); DESError.ThrowExceptionForHR(hr); try { // Inform the graph we will be writing to disk (rather than previewing) var timelineGroup = (IAMTimelineGroup)group; hr = timelineGroup.SetPreviewMode(false); DESError.ThrowExceptionForHR(hr); } finally { Marshal.ReleaseComObject(group); } IPin pPin; // Get the IPin for the current group hr = _renderEngine.GetGroupOutputPin(i, out pPin); _cleanup.Add(pPin); DESError.ThrowExceptionForHR(hr); try { if (FilterGraphTools.IsVideo(pPin)) { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(videoParticipants); RenderHelper(graphBuilder, mcb, "Video", pPin, videoCompressor, videoDestination); } else { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(audioParticipants); RenderHelper(graphBuilder, mcb, "Audio", pPin, audioCompressor, audioDestination); } } finally { Marshal.ReleaseComObject(pPin); } } }
/// <summary> /// Configure the graph to output the results to a video window. /// </summary> /// <remarks> /// The callback routines are invoked once for each sample. This allows for additional processing to /// be performed on the video or audio buffers. /// </remarks> /// <param name="hWnd">Window handle to render to, or IntPtr.Zero to render to its own window</param> /// <param name="pVideoCallback">Callback routine to be called for each video frame or null for no callback</param> /// <param name="pAudioCallback">Callback routine to be called for each audio frame or null for no callback</param> private void RenderToWindow(IntPtr hWnd, IDESCombineCB pVideoCallback, IDESCombineCB pAudioCallback) { int hr; IPin pPin; IVideoWindow pVidWindow; IAMTimelineObj pGroup; IAMTimeline desTimeline = _timeline.DesTimeline; // Contains useful routines for creating the graph ICaptureGraphBuilder2 icgb = (ICaptureGraphBuilder2) new CaptureGraphBuilder2(); try { hr = icgb.SetFiltergraph(_graph); DESError.ThrowExceptionForHR(hr); int NumGroups; hr = desTimeline.GetGroupCount(out NumGroups); DESError.ThrowExceptionForHR(hr); // Walk the groups. For DESCombine, there is one group that // contains all the video, and a second group for the audio. for (int i = 0; i < NumGroups; i++) { hr = desTimeline.GetGroup(out pGroup, i); DESError.ThrowExceptionForHR(hr); try { // Inform the graph we will be previewing (rather than writing to disk) IAMTimelineGroup pTLGroup = (IAMTimelineGroup)pGroup; hr = pTLGroup.SetPreviewMode(true); DESError.ThrowExceptionForHR(hr); } finally { // Release the group Marshal.ReleaseComObject(pGroup); } // Get the IPin for the current group hr = _renderEngine.GetGroupOutputPin(i, out pPin); DESError.ThrowExceptionForHR(hr); try { // If this is the video pin if (PinUtils.IsVideo(pPin)) { // Get a video renderer IBaseFilter ibfVideoRenderer = (IBaseFilter) new VideoRenderer(); try { // Create a sample grabber, add it to the graph and connect it all up CallbackHandler mcb = new CallbackHandler(_firstVideoGroup, pVideoCallback, (IMediaEventSink)_graph, EC_VideoFileComplete); RenderWindowHelper(icgb, mcb, "Video", pPin, ibfVideoRenderer); } finally { Marshal.ReleaseComObject(ibfVideoRenderer); } } else { // Get an audio renderer IBaseFilter ibfAudioRenderer = (IBaseFilter) new AudioRender(); try { // Create a sample grabber, add it to the graph and connect it all up CallbackHandler mcb = new CallbackHandler(_firstAudioGroup, pAudioCallback, (IMediaEventSink)_graph, EC_AudioFileComplete); RenderWindowHelper(icgb, mcb, "Audio", pPin, ibfAudioRenderer); } finally { Marshal.ReleaseComObject(ibfAudioRenderer); } } } finally { Marshal.ReleaseComObject(pPin); } } // Configure the video window pVidWindow = (IVideoWindow)_graph; // If a window handle was supplied, use it if (hWnd != IntPtr.Zero) { hr = pVidWindow.put_Owner(hWnd); DESError.ThrowExceptionForHR(hr); } else { // Use our own window hr = pVidWindow.put_Caption("Video Rendering Window"); DESError.ThrowExceptionForHR(hr); // since no user interaction is allowed, remove // system menu and maximize/minimize buttons WindowStyle lStyle = 0; hr = pVidWindow.get_WindowStyle(out lStyle); DESError.ThrowExceptionForHR(hr); lStyle &= ~(WindowStyle.MinimizeBox | WindowStyle.MaximizeBox | WindowStyle.SysMenu); hr = pVidWindow.put_WindowStyle(lStyle); DESError.ThrowExceptionForHR(hr); } } finally { Marshal.ReleaseComObject(icgb); } }
protected void RenderGroups(ICaptureGraphBuilder2 icgb, IBaseFilter audioCompressor, IBaseFilter videoCompressor, IBaseFilter audioDest, IBaseFilter videoDest, IDESCombineCB pAudioCallback, IDESCombineCB pVideoCallback) { int hr = 0; if (audioCompressor != null) { _dc.Add(audioCompressor); } if (videoCompressor != null) { _dc.Add(videoCompressor); } if (audioDest != null) { _dc.Add(audioDest); } if ((videoDest != null) && (audioDest != videoDest)) { _dc.Add(videoDest); } IAMTimeline desTimeline = _timeline.DesTimeline; int NumGroups; hr = desTimeline.GetGroupCount(out NumGroups); DESError.ThrowExceptionForHR(hr); // Walk the groups. For this class, there is one group that // contains all the video, and a second group for the audio. for (int i = (NumGroups - 1); i >= 0; i--) { IAMTimelineObj pGroup; hr = desTimeline.GetGroup(out pGroup, i); DESError.ThrowExceptionForHR(hr); try { // Inform the graph we will be writing to disk (rather than previewing) IAMTimelineGroup pTLGroup = (IAMTimelineGroup)pGroup; hr = pTLGroup.SetPreviewMode(false); DESError.ThrowExceptionForHR(hr); } finally { Marshal.ReleaseComObject(pGroup); } IPin pPin; // Get the IPin for the current group hr = _renderEngine.GetGroupOutputPin(i, out pPin); _dc.Add(pPin); DESError.ThrowExceptionForHR(hr); try { if (PinUtils.IsVideo(pPin)) { // Create a sample grabber, add it to the graph and connect it all up CallbackHandler mcb = new CallbackHandler(_firstVideoGroup, pVideoCallback, (IMediaEventSink)_graph, EC_VideoFileComplete); RenderHelper(icgb, mcb, "Video", pPin, videoCompressor, videoDest); } else { // Create a sample grabber, add it to the graph and connect it all up CallbackHandler mcb = new CallbackHandler(_firstAudioGroup, pAudioCallback, (IMediaEventSink)_graph, EC_AudioFileComplete); RenderHelper(icgb, mcb, "Audio", pPin, audioCompressor, audioDest); } } finally { Marshal.ReleaseComObject(pPin); } } }
/// <summary> /// Configure the graph to output the results to a video window. /// </summary> /// <remarks> /// The callback routines are invoked once for each sample. This allows for additional processing to /// be performed on the video or audio buffers. /// </remarks> /// <param name="windowHandle">Window handle to render to, or IntPtr.Zero to render to its own window</param> /// <param name="videoParticipants">Callback routine to be called for each video frame or null for no callback</param> /// <param name="audioParticipants">Callback routine to be called for each audio frame or null for no callback</param> private void RenderToWindow(IntPtr windowHandle, ICallbackParticipant[] videoParticipants, ICallbackParticipant[] audioParticipants) { int hr; IPin pin; IVideoWindow videoWindow; IAMTimelineObj group; IAMTimeline desTimeline = Timeline.DesTimeline; // Contains useful routines for creating the graph var graphBuilder = (ICaptureGraphBuilder2) new CaptureGraphBuilder2(); try { hr = graphBuilder.SetFiltergraph(Graph); DESError.ThrowExceptionForHR(hr); int NumGroups; hr = desTimeline.GetGroupCount(out NumGroups); DESError.ThrowExceptionForHR(hr); // Walk the groups. For DESCombine, there is one group that // contains all the video, and a second group for the audio. for (int i = 0; i < NumGroups; i++) { hr = desTimeline.GetGroup(out group, i); DESError.ThrowExceptionForHR(hr); try { // Inform the graph we will be previewing (rather than writing to disk) var pTLGroup = (IAMTimelineGroup)group; hr = pTLGroup.SetPreviewMode(true); DESError.ThrowExceptionForHR(hr); } finally { // Release the group Marshal.ReleaseComObject(group); } // Get the IPin for the current group hr = RenderEngine.GetGroupOutputPin(i, out pin); DESError.ThrowExceptionForHR(hr); try { // If this is the video pin if (FilterGraphTools.IsVideo(pin)) { // Get a video renderer var ibfVideoRenderer = (IBaseFilter) new VideoRenderer(); try { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(videoParticipants); RenderWindowHelper(graphBuilder, mcb, "Video", pin, ibfVideoRenderer); } finally { Marshal.ReleaseComObject(ibfVideoRenderer); } } else { // Get an audio renderer var ibfAudioRenderer = (IBaseFilter) new AudioRender(); try { // Create a sample grabber, add it to the graph and connect it all up var mcb = new CallbackHandler(audioParticipants); RenderWindowHelper(graphBuilder, mcb, "Audio", pin, ibfAudioRenderer); } finally { Marshal.ReleaseComObject(ibfAudioRenderer); } } } finally { Marshal.ReleaseComObject(pin); } } // Configure the video window videoWindow = (IVideoWindow)Graph; // If a window handle was supplied, use it if (windowHandle != IntPtr.Zero) { hr = videoWindow.put_Owner(windowHandle); DESError.ThrowExceptionForHR(hr); } else { // Use our own window hr = videoWindow.put_Caption(Resources.DefaultVideoRenderingWindowCaption); DESError.ThrowExceptionForHR(hr); // since no user interaction is allowed, remove // system menu and maximize/minimize buttons WindowStyle lStyle = 0; hr = videoWindow.get_WindowStyle(out lStyle); DESError.ThrowExceptionForHR(hr); lStyle &= ~(WindowStyle.MinimizeBox | WindowStyle.MaximizeBox | WindowStyle.SysMenu); hr = videoWindow.put_WindowStyle(lStyle); DESError.ThrowExceptionForHR(hr); } } finally { Marshal.ReleaseComObject(graphBuilder); } }