1

找出发生了什么——同时运行的加速度计和 VideoBrush 将冻结 VideoBrush。要解决此问题,请在启动 VideoBrush 之前停止加速度计

使用最少的代码(如下所述),它会在 VideoBrush 在应用程序上启动后大约 2 分 15 秒后可靠地崩溃。这是在 Windows Phone 7.5 应用程序的 Windows 7 模拟器上,使用 c# 和 Xaml。如果 VideoBrush 拉伸也设置为填充等,就会发生这种情况(这有一个 Silverlight 1.0 错误)

有关我如何得出此结论的故事,请查看下面的文字答案墙:)

为什么会这样?我不知道。也许有内存泄漏或什么?值得注意的是——这实际上不会使应用程序本身崩溃。您不会弹出任何异常。

重现问题的代码(创建一个名为 PhoneApp1 的新应用程序并自己尝试!):

Xaml 代码:

<phone:PhoneApplicationPage 
    x:Class="PhoneApp1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="False">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">

            <Rectangle x:Name="viewfinderCanvas" Width="480" Height="800" >
                <Rectangle.Fill>
                    <VideoBrush x:Name="viewfinderBrush"/>
                </Rectangle.Fill>
            </Rectangle>

    </Grid>
</phone:PhoneApplicationPage>

C#代码:

using System;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Devices;
using Microsoft.Devices.Sensors;

namespace PhoneApp1
{
    public partial class MainPage : PhoneApplicationPage
    {
        private Accelerometer AccelerometerSensor;

        // Constructor
        public MainPage()
        {
            InitializeComponent();
            AccelerometerSensor = new Accelerometer();
            AccelerometerStartup();

            if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true))
            {
                viewfinderCanvas.Visibility = Visibility.Visible;
                var cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);

                viewfinderBrush.SetSource(cam);
            }
        }

        #region Accelerometer Startup Function
        private void AccelerometerStartup()
        {
            try
            {
                if (AccelerometerSensor != null)
                {
                    AccelerometerSensor.Start();

                }
            }
            catch (AccelerometerFailedException)
            {
            }
            catch (UnauthorizedAccessException)
            {
            }
        }
        #endregion
    }
}

下面的原始问题:( 保留下面提到的代码,但由于没有连接加速度计,因此不会重现问题)

这是一个有趣的,我保证。

我决定在我的 wp7.5 应用程序中放置一个矩形,其填充属性是 VideoBrush。我使用了从几个网站上找到的代码,并认为一切都很好。直到我注意到相机中的 VideoBrush 会冻结...运行 6 到 40 秒。

困惑,我以为我的调度计时器干扰了相机。将它们注释掉以使其无法运行并不能解决问题。然后我尝试在应用程序中禁用我的广告。也没有修好。我禁用了可能在整个应用程序中呈现更新的所有内容(计时器、调度计时器、广告轮换、任何循环),但它仍然冻结。

我做了同样的事情,甚至从计算机上拔下我的设备,因为我在连接到计算机的情况下使用设备进行调试时打开 zune 可能会破坏事情。还是没有骰子。

我创建了一个新项目并复制粘贴了 VideoBrush 代码,它可以正常工作而不会冻结。

XAML 代码:

<Rectangle x:Name="viewfinderCanvas" Width="480" Height="800" Visibility="Collapsed" DoubleTap="viewfinderCanvas_DoubleTap">
            <Rectangle.Fill>
                <VideoBrush x:Name="videoBrush">
                <VideoBrush.RelativeTransform>
                    <CompositeTransform x:Name="previewTransform"
                            CenterX=".5"
                            CenterY=".5" />
                </VideoBrush.RelativeTransform>
                </VideoBrush>
            </Rectangle.Fill>
        </Rectangle>

C#代码:

if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true))
    {
           viewfinderCanvas.Visibility = Visibility.Visible;
           var cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);

           if (Orientation == PageOrientation.PortraitUp || Orientation ==  
           PageOrientation.PortraitDown || Orientation == PageOrientation.Portrait)
             {
                videoBrush.RelativeTransform =
                new CompositeTransform() { CenterX = 0.5, CenterY = 0.5, Rotation = 90 };
             }

           videoBrush.SetSource(cam);
    }

真的,我不知道是什么导致了 VideoBrush 显示冻结。我在每个函数上都设置了断点,但什么也没得到。不存在错误消息...视频只是冻结。

有没有人遇到过这个?它发生在我的设备和我编写代码的计算机上——模拟器的白盒刚刚停止。

为清楚起见——这样做的目的只是显示相机在应用程序中看到的内容——我不是在拍照或录制视频。我只是在做这个节目。要关闭窗口,用户将双击矩形以关闭原始视图。

4

3 回答 3

1

我发现了发生了什么,这确实与我的代码有关。

TL:DR:带有活动 VideoBrush 的活动加速度计会导致 VideoBrush 最终冻结。

我做了什么来测试这个:

拿起我的 2000 多行代码,我坐在那里开始注释 API、引用以及我通常认为会导致 VideoBrush 冻结的东西。每次我拿出相当大的代码块时,我都会测试该应用程序,看看它是否有所作为。我的测试依赖于 VideoBrush 从启动到冻结所需的秒数。

有趣的是,在移除任何东西之前,VideoBrush 冻结之前通常大约需要 45 秒左右。在剪掉我的代码后,当它突然跳到 2 分 45 秒时,我得到了我的第一个主要结果。这是在我删除了一些网格时实现的,这些网格包含几个在 Blend 中动画的堆栈面板、文本块和按钮。

在这一点上,我幽默地认为我的混合内容不会从 Visibility.Visible 变为 Visibility.Collapsed,因此随着时间的推移有些东西正在积累。情况并非如此,因为在调用 VideoBrush 之前折叠所有内容的功能在 2:45 的冻结时间没有任何变化。

我取出了所有资产,取出了所有计时器、调度计时器、秒表、日期时间实例,但它仍然没有做任何事情来改变 2:45。

然后我遇到了它:我的加速度计。

对于全貌,我在我的 wp7 应用程序中的两个不同页面上使用了加速度计。当用户第一次打开应用程序时,他将被带到一个教程页面,该页面有一个加速度计来模仿主页。完成该教程后,用户将被带到主页,该主页还有一个具有相同名称的加速度计和事件更改通知。

作为旁注,在查看我的代码时,我注意到我的一个编码错误,我实际上并没有在任一页面上停止/处置我的加速度计。这从未导致应用程序崩溃,因此它从未成为现实问题。

修复后,我注意到 VideoBrush 冻结之前的时间会上升到大约 5:00。进步了,但仍然是什么导致了这种冻结?

然后,我决定在启动 VideoBrush 之前停止加速度计,并且你瞧,VideoBrush 可以正常工作而不会冻结其图片。

很抱歉,因为这是一个不可能回答的问题,但不知道什么可以阻止 VideoBrush 在应用程序崩溃的情况下工作。至少现在有一种已知的方法!

于 2012-11-20T06:29:49.437 回答
0

正如 KooKiz 的评论中所指出的,我能够用绝对最少的代码重现 VideoBrush 冻结。时间可能会有所不同,但使用以下代码,VideoBrush 会在 2 分 14 秒到 2 分 15 秒之间可靠地冻结。(我通过运行模拟器并打开我的系统时钟多次计时,注意 VideoBrush 何时启动以及何时冻结。)

Xaml 代码:

<phone:PhoneApplicationPage 
    x:Class="PhoneApp1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
    xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="False">

    <!--LayoutRoot is the root grid where all page content is placed-->
    <Grid x:Name="LayoutRoot" Background="Transparent">

            <Rectangle x:Name="viewfinderCanvas" Width="480" Height="800" >
                <Rectangle.Fill>
                    <VideoBrush x:Name="viewfinderBrush"/>
                </Rectangle.Fill>
            </Rectangle>

    </Grid>
</phone:PhoneApplicationPage>

C#代码:

using System;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Devices;
using Microsoft.Devices.Sensors;

namespace PhoneApp1
{
    public partial class MainPage : PhoneApplicationPage
    {
        private Accelerometer AccelerometerSensor;

        // Constructor
        public MainPage()
        {
            InitializeComponent();
            AccelerometerSensor = new Accelerometer();
            AccelerometerStartup();

            if ((PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true))
            {
                viewfinderCanvas.Visibility = Visibility.Visible;
                var cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);

                viewfinderBrush.SetSource(cam);
            }
        }


        #region Accelerometer Startup Function
        private void AccelerometerStartup()
        {
            try
            {
                if (AccelerometerSensor != null)
                {
                    AccelerometerSensor.Start();

                }
            }
            catch (AccelerometerFailedException)
            {
            }
            catch (UnauthorizedAccessException)
            {
            }
        }
        #endregion
    }
}
于 2012-11-20T17:40:32.917 回答
0

感谢您的代码,我能够重现该问题,并且找到了根本原因。

准备好,这将是一个“……就这样?” 片刻!

基本上,整个问题都在这条线上:

var cam = new Microsoft.Devices.PhotoCamera(CameraType.Primary);

您创建了一个新的PhotoCamera,但您没有在任何地方保留参考。因此,当垃圾收集器启动时,它会释放您的孤立PhotoCamera实例,从而冻结视频。

很容易重现:只需在页面上添加一个按钮,然后放入点击处理程序:

GC.Collect();

只要按下按钮,视频就会冻结。

现在显而易见的问题是:为什么只有加速度计才会冻结?其实很简单。当分配的对象总数达到阈值时,会自动触发垃圾回收。当您只有视频时,分配的对象很少(可能没有),因此垃圾收集器不会在很长一段时间内被触发。打开加速度计时,它会快速更新其位置,在此过程中分配新对象,因此会在几分钟后触发垃圾收集。

如何解决问题?只需将您的PhotoCamera参考存储在一个属性中:

private PhotoCamera cam;

public MainPage()
{
    InitializeComponent();

    AccelerometerSensor = new Accelerometer();
    AccelerometerSensor.CurrentValueChanged += new EventHandler<SensorReadingEventArgs<AccelerometerReading>>(AccelerometerSensor_CurrentValueChanged);
    AccelerometerStartup();

    if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary))
    {
        viewfinderCanvas.Visibility = Visibility.Visible;
        cam = new PhotoCamera(CameraType.Primary);

        viewfinderBrush.SetSource(cam);
    }
}
于 2012-11-20T20:22:45.460 回答