/// <inheritdoc/> public void DecodeFromStream(Stream stream, Image image) { if (!this.HasNV12Header(stream)) { throw new ArgumentException("Stream does not appear to be NV12-encoded (missing header)."); } stream.Position = 4; // skip header // decode NV12 var width = image.Width; var height = image.Height; int startUV = width * height; int strideUV = width; var size = (int)(width * height * 1.5 + 0.5); // 12-bit/pixel using var sharedData = SharedArrayPool <byte> .GetOrCreate(size); var data = sharedData.Resource; stream.Read(data, 0, size); unsafe { var buffer = (byte *)image.UnmanagedBuffer.Data; for (int i = 0; i < height; i++) { var p = buffer + (i * 4 * width); int row = i * width; var startUVrow = startUV + ((i / 2) * strideUV); for (int j = 0; j < width; j++) { var y = data[row + j] - 16; var uindex = startUVrow + (2 * (j / 2)); var u = data[uindex] - 128; var v = data[uindex + 1] - 128; var yy = 1.164383 * y; var b = yy + (2.017232 * u); var g = yy - (0.812968 * v) - (0.391762 * u); var r = yy + (1.596027 * v); *p++ = (byte)Math.Max(0, Math.Min(255, b + 0.5)); *p++ = (byte)Math.Max(0, Math.Min(255, g + 0.5)); *p++ = (byte)Math.Max(0, Math.Min(255, r + 0.5)); *p++ = 0xff; // alpha } } } }
public void SharedArrayPoolTest() { // allocate and release an array of size 10 var int10 = SharedArrayPool <int> .GetOrCreate(10); Assert.AreEqual(10, int10.Resource.Length); object ref10 = int10.Resource; // capture the underlying array reference int10.Dispose(); // allocate and release an array of size 20 var int20 = SharedArrayPool <int> .GetOrCreate(20); Assert.AreEqual(20, int20.Resource.Length); object ref20 = int20.Resource; // capture the underlying array reference int20.Dispose(); // request another array of size 10 without releasing int10 = SharedArrayPool <int> .GetOrCreate(10); Assert.AreEqual(10, int10.Resource.Length); Assert.AreSame(ref10, int10.Resource); // verify recycled resource // request another array of size 20 without releasing int20 = SharedArrayPool <int> .GetOrCreate(20); Assert.AreEqual(20, int20.Resource.Length); Assert.AreSame(ref20, int20.Resource); // verify recycled resource // allocate another array of size 20 without releasing var int20_2 = SharedArrayPool <int> .GetOrCreate(20); Assert.AreEqual(20, int20_2.Resource.Length); Assert.AreNotSame(ref20, int20_2.Resource); // not recycled // allocate another array of size 10 without releasing var int10_2 = SharedArrayPool <int> .GetOrCreate(10); Assert.AreEqual(10, int10_2.Resource.Length); Assert.AreNotSame(ref10, int10_2.Resource); // not recycled // release all instances int10.Dispose(); int20.Dispose(); int10_2.Dispose(); int20_2.Dispose(); }
/// <inheritdoc/> 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) { using (var sharedImage = ImagePool.GetOrCreate(this.configuration.Width, this.configuration.Height, PixelFormat.BGR_24bpp)) { if (this.configuration.PixelFormat == PixelFormatId.BGR24) { 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) 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); } } } } #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(); }