in Sources/Media/Microsoft.Psi.Media.Linux/MediaCapture.cs [109:228]
public unsafe void Start(Action<DateTime> notifyCompletionTime)
{
// notify that this is an infinite source component
notifyCompletionTime(DateTime.MaxValue);
this.camera = new MediaCaptureInternal(this.configuration.DeviceId);
this.camera.Open();
var isFormatSupported = false;
foreach (var format in this.camera.SupportedPixelFormats())
{
if (format.Pixels == this.configuration.PixelFormat)
{
this.camera.SetVideoFormat(this.configuration.Width, this.configuration.Height, format);
isFormatSupported = true;
}
}
if (!isFormatSupported)
{
throw new ArgumentException($"Pixel format {this.configuration.PixelFormat} is not supported by the camera");
}
var current = this.camera.GetVideoFormat();
if (current.Width != this.configuration.Width || current.Height != this.configuration.Height)
{
throw new ArgumentException($"Width/height {this.configuration.Width}x{this.configuration.Height} is not supported by the camera");
}
this.camera.OnFrame += (_, frame) =>
{
var originatingTime = this.pipeline.GetCurrentTime();
if (this.Raw.HasSubscribers)
{
var len = frame.Length;
using (Shared<byte[]> shared = SharedArrayPool<byte>.GetOrCreate(len))
{
Marshal.Copy(frame.Start, shared.Resource, 0, len);
this.Raw.Post(shared, originatingTime);
}
}
if (this.Out.HasSubscribers)
{
if (this.configuration.PixelFormat == PixelFormatId.BGR24)
{
using (var sharedImage = ImagePool.GetOrCreate(this.configuration.Width, this.configuration.Height, PixelFormat.BGR_24bpp))
{
sharedImage.Resource.CopyFrom((IntPtr)frame.Start);
this.Out.Post(sharedImage, this.pipeline.GetCurrentTime());
}
}
else if (this.configuration.PixelFormat == PixelFormatId.YUYV)
{
// convert YUYV -> BGR24 (see https://msdn.microsoft.com/en-us/library/ms893078.aspx)
using (var sharedImage = ImagePool.GetOrCreate(this.configuration.Width, this.configuration.Height, PixelFormat.BGR_24bpp))
{
var len = (int)(frame.Length * 1.5);
using (Shared<byte[]> shared = SharedArrayPool<byte>.GetOrCreate(len))
{
var bytes = shared.Resource;
var pY = (byte*)frame.Start.ToPointer();
var pU = pY + 1;
var pV = pY + 3;
for (var i = 0; i < len;)
{
int y = (*pY - 16) * 298;
int u = *pU - 128;
int v = *pV - 128;
int r = (y + (409 * v) + 128) >> 8;
int g = (y - (100 * u) - (208 * v) + 128) >> 8;
int b = (y + (516 * u) + 128) >> 8;
bytes[i++] = (byte)((r < 0) ? 0 : ((r > 255) ? 255 : r));
bytes[i++] = (byte)((g < 0) ? 0 : ((g > 255) ? 255 : g));
bytes[i++] = (byte)((b < 0) ? 0 : ((b > 255) ? 255 : b));
pY += 2;
y = (*pY - 16) * 298;
r = (y + (409 * v) + 128) >> 8;
g = (y - (100 * u) - (208 * v) + 128) >> 8;
b = (y + (516 * u) + 128) >> 8;
bytes[i++] = (byte)((r < 0) ? 0 : ((r > 255) ? 255 : r));
bytes[i++] = (byte)((g < 0) ? 0 : ((g > 255) ? 255 : g));
bytes[i++] = (byte)((b < 0) ? 0 : ((b > 255) ? 255 : b));
pY += 2;
pU += 4;
pV += 4;
}
sharedImage.Resource.CopyFrom(bytes);
this.Out.Post(sharedImage, originatingTime);
}
}
}
else if (this.configuration.PixelFormat == PixelFormatId.MJPEG)
{
var decoded = SKBitmap.Decode(new UnmanagedMemoryStream((byte*)frame.Start, frame.Length));
if (decoded != null)
{
using (var sharedImage = ImagePool.GetOrCreate(this.configuration.Width, this.configuration.Height, PixelFormat.BGRA_32bpp))
{
sharedImage.Resource.CopyFrom(decoded.Bytes);
this.Out.Post(sharedImage, originatingTime);
}
}
}
}
#if TEST_DROPPED_FRAMES
System.Threading.Thread.Sleep(1000); // for testing dropped frames
#endif // TEST_DROPPED_FRAMES
frame.Dispose(); // release back to driver!
};
this.camera.StreamBuffers();
}