Я знаю, что это старый пост, но сегодня я столкнулся с той же проблемой на Windows Phone 8.1 Silverlight, и я нашел хорошее решение, поэтому я решил оставить его для людей с подобной проблемой.Он был опубликован Чарльзом Петцольдом на MSDN как Видеопотоки на Windows Phone 7 (это показано на примере класса VideoSink
, но не должно быть проблемой воспроизвести его в другом случае).Он создал простой класс, производный от VideoSink
:
SimpleVideoSink C # code:
public class SimpleVideoSink : VideoSink
{
VideoFormat videoFormat;
WriteableBitmap writeableBitmap;
Action<WriteableBitmap> action;
public SimpleVideoSink(Action<WriteableBitmap> action)
{
this.action = action;
}
protected override void OnCaptureStarted() { }
protected override void OnCaptureStopped() { }
protected override void OnFormatChange(VideoFormat videoFormat)
{
this.videoFormat = videoFormat;
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
writeableBitmap = new WriteableBitmap(videoFormat.PixelWidth,
videoFormat.PixelHeight);
action(writeableBitmap);
});
}
protected override void OnSample(long sampleTimeInHundredNanoseconds,
long frameDurationInHundredNanoseconds, byte[] sampleData)
{
if (writeableBitmap == null)
return;
int baseIndex = 0;
for (int row = 0; row < writeableBitmap.PixelHeight; row++)
{
for (int col = 0; col < writeableBitmap.PixelWidth; col++)
{
int pixel = 0;
if (videoFormat.PixelFormat == PixelFormatType.Format8bppGrayscale)
{
byte grayShade = sampleData[baseIndex + col];
pixel = (int)grayShade | (grayShade << 8) |
(grayShade << 16) | (0xFF << 24);
}
else
{
int index = baseIndex + 4 * col;
pixel = (int)sampleData[index + 0] | (sampleData[index + 1] << 8) |
(sampleData[index + 2] << 16) | (sampleData[index + 3] << 24);
}
writeableBitmap.Pixels[row * writeableBitmap.PixelWidth + col] = pixel;
}
baseIndex += videoFormat.Stride;
}
writeableBitmap.Dispatcher.BeginInvoke(() =>
{
writeableBitmap.Invalidate();
});
}
}
Однако этот код нуждается в некоторой модификации - VideoSink.CaptureSource
должен быть предоставлен с CaptureSource
(Я только что передал его в конструктор):
public SimpleVideoSink(CaptureSource captureSource, Action<WriteableBitmap> action)
{
base.CaptureSource = captureSource;
this.action = action;
}
Когда мы инициализируем класс SimpleVideoSink
в ViewModel, мы должны предоставить ему параметр Action
.В моем случае было достаточно предоставить ViewModel инициализированное поле writeableBitmap
:
ViewModel C # code:
private WriteableBitmap videoWriteableBitmap;
public WriteableBitmap VideoWriteableBitmap
{
get
{
return videoWriteableBitmap;
}
set
{
videoWriteableBitmap = value;
RaisePropertyChanged("VideoWriteableBitmap");
}
}
private void OnWriteableBitmapChange(WriteableBitmap writeableBitmap)
{
VideoWriteableBitmap = writeableBitmap;
}
//this part goes to constructor/method
SimpleVideoSink videoFrameHandler = new SimpleVideoSink(captureSource, OnWriteableBitmapChange);
Тогда все, что нам нужно сделать, это привязать его к View:
Просмотр кода XAML:
<Image Source="{Binding VideoWriteableBitmap}" />
В этом примере Image
источник обновляется при каждом вызове метода OnSample
и отправляется в основной поток через WriteableBitmap.Dispatcher
.
Это решение генерирует правильное изображение без пустого пикселя (альфа-канал также заполнен), а метод Invalidate()
работает как следует.