1

我一直在使用 WPFMediaKit 来呈现 DirectShow 图。这是我的设置。

我在MediaPlayerBase多次使用多个 D3DRender 实例时遇到问题。我有一个返回用于预览IVideoEngine的单个图形(通过)。MediaPlayerBase内部IVideoEngine管理用于切换相机输入/等的 DirectShow 图形。这个想法是,这个单一的图(通过)可以通过基类MediaPlayerBase多次、同时或(更有可能)在不同的时间使用。D3DRenderer

我创建了一个新的RenderElementControl,它只是将 a 渲染MediaPlayerBase到表面上。它适用于用于特定实例的第一个实例MediaPlayerBase,但是当RenderElementControl再次使用时,不会渲染任何视频。

这是我专门渲染的源代码MediaPlayerBase

public class RenderElementControl : D3DRenderer
{
    private readonly MediaPlayerBase _mediaPlayerBase;

    public RenderElementControl(MediaPlayerBase mediaPlayerBase)
    {
        _mediaPlayerBase = mediaPlayerBase;
        Loaded += OnLoaded;
        Unloaded += OnUnloaded;
    }

    private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs)
    {
        _mediaPlayerBase.NewAllocatorFrame -= OnMediaPlayerNewAllocatorFramePrivate;
        _mediaPlayerBase.NewAllocatorSurface -= OnMediaPlayerNewAllocatorSurfacePrivate;
    }

    private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
    {
        _mediaPlayerBase.NewAllocatorFrame += OnMediaPlayerNewAllocatorFramePrivate;
        _mediaPlayerBase.NewAllocatorSurface += OnMediaPlayerNewAllocatorSurfacePrivate;
        _mediaPlayerBase.Dispatcher.DoEvents();
    }

    private void OnMediaPlayerNewAllocatorSurfacePrivate(object sender, IntPtr pSurface)
    {
        SetBackBuffer(pSurface);
    }

    private void OnMediaPlayerNewAllocatorFramePrivate()
    {
        InvalidateVideoImage();
    }
}

问题

为什么此控件不适用于 single 的第二个实例MediaPlayerBase?我如何做到这一点,以便我可以使用多个RenderElementControl,可能同时使用相同的MediaPlayerBase

注意:对于那些不熟悉 WPFMediaKit 的人,这里是处理 DirectShow 渲染(VMR 或 EMR)的D3DRendererMediaPlayerBase的源代码。

4

1 回答 1

2

我能够通过保留我自己的 RenderElementControl 内部副本并发布克隆的 D3Renderer 克隆来解决该问题。这是源代码。

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;

namespace WPF.Controls
{
    /// <summary>
    /// This element simply renders a, assumingly, continous stream of video from the given MediaPlayer
    /// </summary>
    public class RendererElement : ContentControl
    {
        static readonly Dictionary<int, RenderElementControl> GraphRenders = new Dictionary<int, RenderElementControl>(); 

        #region Dependency Properties

        public static readonly DependencyProperty PlayerProperty =
            DependencyProperty.Register("Player", typeof(MediaPlayerBase), typeof(RendererElement), new PropertyMetadata(default(MediaPlayerBase), PropertyChangedCallback));

        private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            ((RendererElement)dependencyObject).OnPlayerChanged();
        }

        public MediaPlayerBase Player
        {
            get { return (MediaPlayerBase)GetValue(PlayerProperty); }
            set { SetValue(PlayerProperty, value); }
        }

        #endregion

        protected void OnPlayerChanged()
        {
            Content = null;

            if (Player != null)
            {
                var existingRenderer = GraphRenders.ContainsKey(Player.GraphInstanceId) ? GraphRenders[Player.GraphInstanceId] : null;

                if(existingRenderer == null)
                {
                    // The first usage needs to add RenderElementControl so it can initialize property.
                    // After it is intially loaded, it is replaced by a clone (See RenderElementControlLoaded).
                    existingRenderer = new RenderElementControl(Player);
                    Content = existingRenderer;
                }else
                {
                    // A render has already been created, just grab a clone from it.
                    Content = existingRenderer.CloneD3DRenderer();
                }
            }
        }

        protected void RenderElementControlLoaded(RenderElementControl control)
        {
            GraphRenders.Add(control.GraphInstanceId, control);
            Content = control.CloneD3DRenderer();
        }

        #region Nested Classes

        /// <summary>
        /// The actual control that renders. This is stored internally, and clones are tooken from it so that we can re-render it.
        /// </summary>
        public class RenderElementControl : D3DRenderer
        {
            private readonly MediaPlayerBase _mediaPlayerBase;

            public RenderElementControl(MediaPlayerBase mediaPlayerBase)
            {
                _mediaPlayerBase = mediaPlayerBase;
                Loaded += OnLoaded;
            }

            private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
            {
                _mediaPlayerBase.NewAllocatorFrame += OnMediaPlayerNewAllocatorFramePrivate;
                _mediaPlayerBase.NewAllocatorSurface += OnMediaPlayerNewAllocatorSurfacePrivate;
                _mediaPlayerBase.Dispatcher.DoEvents();
                // let the RenderElement know we have loaded and initialized so that it can cache this instance,
                // as well as replace this instance with a clone.
                (Parent as RendererElement).RenderElementControlLoaded(this);
            }

            private void OnMediaPlayerNewAllocatorSurfacePrivate(object sender, IntPtr pSurface)
            {
                SetBackBuffer(pSurface);
            }

            private void OnMediaPlayerNewAllocatorFramePrivate()
            {
                InvalidateVideoImage();
            }

            public int GraphInstanceId
            {
                get { return _mediaPlayerBase.GraphInstanceId; }
            }
        }

        #endregion
    }
}
于 2013-07-21T11:04:11.047 回答