Видео
37WPF --- Элементы управления WPF --- Видео
Все, что было сказано о применении класса MediaElement, в равной степени касается и воспроизведения видеофайлов. Как и можно было ожидать, класс MediaElement поддерживает все видеоформаты, которые поддерживает проигрыватель Windows Media. Хотя поддержка и зависит от установленных кодеков, вполне можно рассчитывать на базовую поддержку форматов WMV, MPEG и AVI.
Ключевое отличие при воспроизведении видеофайлов заключается в том, что здесь становятся важными визуальные свойства MediaPlayer, а также свойства, касающиеся его компоновки. Важнее всего свойства Stretch и StretchDirection, определяющие, как масштабируется видео-окно для заполнения контейнера (эти свойства работают так же, как свойства Stretch и StretchDirection классов-наследников Shape). При установке значения Stretch можно использовать None для сохранения исходного размера, Uniform — чтобы растянуть его для заполнения контейнера без изменения пропорций, Fill — для растяжения по обоим измерениям до размеров контейнера (даже если это значит искажение пропорций) и UniformToFill — для растяжения изображения, чтобы оно уместилось в максимальное измерение контейнера, а пропорции сохранились (при этом, если пропорции видео-окна не будут совпадать с пропорциями контейнера, то часть видео-окна будет усечена).
Предпочтительный размер MediaElement основан на исходных пропорциях видео. Например, если создать MediaElement со значением Stretch, равным Uniform (по умолчанию так и есть) и поместить его внутрь строки Grid со свойством Height, установленным в Auto, то строка будет подогнана по размеру так, чтобы вместить видео в его стандартном размере, без масштабирования.
Видео-эффекты
Поскольку MediaElements работает как любой другой элемент WPF, есть возможность манипулировать им несколько неожиданным образом. Ниже описаны примеры:
MediaElement можно использовать в качестве содержимого элемента управления, такого как кнопка.
Можно установить содержимое для тысяч элементов сразу с помощью множества объектов MediaElement, хотя это существенная вычислительная нагрузка на процессор.
Можно комбинировать видео с трансформациями через свойства LayoutTransformorm или RenderTransform. Это позволит перемещать видео-окно, растягивать, наклонять или вращать его.
Обычно для MediaElement трансформация RenderTransform предпочтительнее, чем LayoutTransformor, поскольку она более легковесная. Кроме того, она принимает во внимание значение удобного свойства RenderTransformOrigin, позволяя использовать относительные координаты для определенных трансформаций.
Можно устанавливать свойство Clipping объекта MediaElement так, чтобы обрезать видео-окно по определенной фигуре или пути и показывать только часть полного окна.
Можно устанавливать свойство Opacity, позволяя отображать другое содержимое сквозь видео-окно. Фактически, есть даже возможность сложить вместе в стопку несколько полупрозрачных видео-окон (с тяжелыми последствиями для производительности).
Можно использовать анимацию, чтобы динамически изменять свойство MediaElement (или одного из его трансформаций).
Можно копировать текущее содержимое видео-окна в другое место пользовательского интерфейса с использованием VisualBrush, что позволяет создавать специфические эффекты типа отражений.
Можно поместить видео-окно на трехмерную поверхность и использовать анимацию для перемещения его во время воспроизведения видео.
Например, следующий код разметки создает эффект отражения. Он делает это посредством создания объекта Grid, состоящего из двух строк. Верхняя строка содержит MediaElement, воспроизводящий видеофайл. Нижняя строка содержит Rectangle, который рисуется с помощью VisualBrush. Трюк в том, что VisualBrush принимает свое содержимое от видео-окна, расположенного над ним, используя выражение привязки. Видео-содержимое затем опрокидывается с использованием RelativeTransform, а затем плавно затухает сверху вниз с помощью градиента OpacityMask:
<Grid Margin="15" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border BorderBrush="DarkGray" BorderThickness="1" CornerRadius="2">
<MediaElement x:Name="video" Source="test.mpg" LoadedBehavior="Manual" Stretch="Fill"></MediaElement>
</Border>
<Border Grid.Row="1" BorderBrush="DarkGray" BorderThickness="1" CornerRadius="2">
<Rectangle VerticalAlignment="Stretch" Stretch="Uniform">
<Rectangle.Fill>
<VisualBrush Visual="{Binding ElementName=video}">
<VisualBrush.RelativeTransform>
<ScaleTransform ScaleY="-1" CenterY="0.5"></ScaleTransform>
</VisualBrush.RelativeTransform>
</VisualBrush>
</Rectangle.Fill>
<Rectangle.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="Black" Offset="0"></GradientStop>
<GradientStop Color="Transparent" Offset="0.6"></GradientStop>
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
</Border>
<Button Grid.Row="2" Padding="3" Click="cmdPlay_Click">Play</Button>
</Grid>
Этот пример работает очень хорошо. Эффект отражения влечет за собой те же накладные расходы, что и два видео-окна, поскольку каждый кадр должен копироваться в нижний прямоугольник. Вдобавок каждый кадр понадобится опрокинуть и добавить к нему затухание, чтобы создать эффект отражения. (Для выполнения таких трансформаций WPF использует промежуточную поверхность визуализации.) Но в современных компьютерах упомянутыми накладными расходами можно пренебречь.
Это не так в случае других видеоэффектов. Фактически видео — одна из немногих областей WPF, где очень легко перегрузить процессор и получить неважно работающий пользовательский интерфейс. Средние компьютеры не могут обрабатывать более чем несколько видео-окон одновременно (конечно, в зависимости от размеров видеофайла — более высокое разрешение и большая частота кадров, естественно, означает больший объем данных, на обработку которых требуется больше времени).
В WPF определен класс VideoDrawing, унаследованный от класса Drawing. VideoDrawing может применяться для создания кисти DrawingBrush, которая, в свою очередь, служит для заполнения поверхности элемента, создавая тот же эффект, что был продемонстрирован выше в примере с VisualBrush.
Однако есть одно отличие, которое может сделать подход с VideoDrawing более эффективным. Это связано с тем, что VideoDrawing использует класс MediaPlayer, в то время как подход на основе VisualBrush требует применения класса MediaElement. Класс MediaPlayer не нуждается в управлении компоновкой, фокусом или другими деталями элемента, поэтому он более легковесный, чем MediaElement. В некоторых ситуациях использование VideoDrawing и DrawingBrush вместо MediaElement и VisualBrush помогает избежать необходимости в промежуточной поверхности визуализации и за счет этого повышает производительность (хотя проведенное тестирование не позволило заметить существенную разницу между этими двумя подходами).