Last active
August 28, 2019 13:05
-
-
Save parsd/364f78d3b1b20069658fc0158e0c812c to your computer and use it in GitHub Desktop.
CVB.Net GigE Vision Chunk Parsing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Linq; | |
namespace Stemmer.Cvb.Driver | |
{ | |
/// <summary> | |
/// Available GigE Vision transport payload types. | |
/// </summary> | |
/// <remarks> | |
/// As of GigE Vision spec 2.0. | |
/// </remarks> | |
public enum GevPayloadType | |
{ | |
Image = 0x0001, | |
RawData = 0x0002, | |
File = 0x0003, | |
ChunkData = 0x0004, | |
ExtendedChunkData = 0x0005, | |
JPEG = 0x0006, | |
JPEG2000 = 0x0007, | |
H264 = 0x0008, | |
MultiZoneImage = 0x0009, | |
ExtendedChunkWithImage = 0x4001, | |
ExtendedChunkWithRawData = 0x4002, | |
ExtendedChunkWithFile = 0x4003, | |
ExtendedChunkWithJPEG = 0x4006, | |
ExtendedChunkWithJPEG2000 = 0x4007, | |
ExtendedChunkWithH264 = 0x4008, | |
ExtendedChunkWithMultiZoneImage = 0x4009 | |
} | |
/// <summary> | |
/// Extended functionality for GenICam <see cref="Device"/> | |
/// <see cref="DeviceImage"/>s . | |
/// </summary> | |
public static class GenICamDeviceImageExtensions | |
{ | |
private static readonly DeviceControlCommand GetDeliveredPaylodType = new DeviceControlCommand(DeviceControlOperation.Get, 0x1000); | |
private static readonly DeviceControlCommand GetPaylodSize = new DeviceControlCommand(DeviceControlOperation.Get, 0x1900); | |
private static readonly DeviceControlCommand GetDeliveredPaylodSize = new DeviceControlCommand(DeviceControlOperation.Get, 0x1B00); | |
/// <summary> | |
/// Gets whether the device image contains chunk data. | |
/// </summary> | |
/// <param name="image">This GenICam image.</param> | |
/// <returns><b>true</b> this <paramref name="image"/> has chunk data; | |
/// <b>false</b> otherwise.</returns> | |
/// <exception cref="InvalidOperationException">If <paramref name="image"/> | |
/// is not a GenICam device image.</exception> | |
public static bool HasChunks(this DeviceImage image) | |
{ | |
switch(image.GetPayloadType()) | |
{ | |
default: | |
return false; | |
case GevPayloadType.ChunkData: | |
case GevPayloadType.ExtendedChunkData: | |
case GevPayloadType.ExtendedChunkWithFile: | |
case GevPayloadType.ExtendedChunkWithH264: | |
case GevPayloadType.ExtendedChunkWithImage: | |
case GevPayloadType.ExtendedChunkWithJPEG: | |
case GevPayloadType.ExtendedChunkWithJPEG2000: | |
case GevPayloadType.ExtendedChunkWithMultiZoneImage: | |
case GevPayloadType.ExtendedChunkWithRawData: | |
return true; | |
} | |
} | |
/// <summary> | |
/// Gets the buffer base pointer of this <paramref name="image"/>. | |
/// </summary> | |
/// <param name="image">This contiguous image.</param> | |
/// <returns>Pointer to the first element of the buffer.</returns> | |
public static IntPtr GetBufferBasePtr(this DeviceImage image) | |
{ | |
if (image == null) | |
throw new ArgumentNullException(nameof(image)); | |
return image.Planes | |
.Min(plane => plane.GetLinearAccess().BasePtr); | |
} | |
/// <summary> | |
/// Gets the <see cref="GevPayloadType"/> of this <paramref name="image"/>. | |
/// </summary> | |
/// <param name="image">This GenICam image.</param> | |
/// <returns>The payload type that delivered this image.</returns> | |
/// <exception cref="InvalidOperationException">If <paramref name="image"/> | |
/// is not a GenICam device image.</exception> | |
public static GevPayloadType GetPayloadType(this DeviceImage image) | |
{ | |
if (image == null) | |
throw new ArgumentNullException(nameof(image)); | |
long payloadType; | |
DeviceControl(image).SendCommand(GetDeliveredPaylodType, IntPtr.Zero, out payloadType); | |
return (GevPayloadType)payloadType; | |
} | |
/// <summary> | |
/// Gets the valid size of the delivered buffer storing the image and | |
/// additional data. | |
/// </summary> | |
/// <param name="image">This GenICam image.</param> | |
/// <returns>Number of valid bytes in the <paramref name="image"/>'s buffer. | |
/// </returns> | |
public static long GetPayloadSize(this DeviceImage image) | |
{ | |
if (image == null) | |
throw new ArgumentNullException(nameof(image)); | |
long payloadSize = 0; | |
try | |
{ | |
DeviceControl(image).SendCommand(GetDeliveredPaylodSize, IntPtr.Zero, out payloadSize); | |
if (payloadSize > 0) | |
return payloadSize; | |
} | |
catch | |
{ | |
// swallow | |
} | |
DeviceControl(image).SendCommand(GetPaylodSize, IntPtr.Zero, out payloadSize); | |
return payloadSize; | |
} | |
private static IDeviceControl DeviceControl(DeviceImage image) | |
{ | |
Debug.Assert(image != null); | |
if (!IsGenICamDevice(image.Parent)) | |
throw new InvalidOperationException("DeviceImage is not of GenICam Device"); | |
return image.Parent.DeviceControl; | |
} | |
private static bool IsGenICamDevice(Device device) | |
{ | |
return device?.DriverGuid == DeviceFactory.GenICamGuid; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Stemmer.Cvb.Driver | |
{ | |
/// <summary> | |
/// Single data chunk from inside a <see cref="DeviceImage"/>. | |
/// </summary> | |
/// <remarks> | |
/// Attention: the chunk is only a view onto the image's buffer. It is only | |
/// valid as long as the referenced <see cref="DeviceImage"/> is valid. | |
/// </remarks> | |
public struct GevChunk | |
{ | |
/// <summary> | |
/// The device dependent chunk ID. | |
/// </summary> | |
/// <remarks> | |
/// Check either the <see cref="NodeMapNames.Device"/> | |
/// <see cref="NodeMap"/> of the <see cref="Device.NodeMaps"/> or the | |
/// device's manual. | |
/// </remarks> | |
public uint ID { get; private set; } | |
/// <summary> | |
/// The offset from the image base pointer to this chunk. | |
/// </summary> | |
public long Offset { get; private set; } | |
/// <summary> | |
/// The size of this chunk in bytes. | |
/// </summary> | |
public long Length { get; private set; } | |
/// <summary> | |
/// Creates a new chunk. | |
/// </summary> | |
/// <param name="id">Device dependent chunk identifier.</param> | |
/// <param name="basePtr">Base address of the chunk.</param> | |
/// <param name="length">Length in bytes of the chunk.</param> | |
public GevChunk(uint id, long offset, long length) | |
{ | |
if (offset < 0) | |
throw new ArgumentOutOfRangeException(nameof(offset), "must be positive or 0"); | |
if (length < 1) | |
throw new ArgumentOutOfRangeException(nameof(length), "must be positive non-0"); | |
ID = id; | |
Offset = offset; | |
Length = length; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Runtime.InteropServices; | |
namespace Stemmer.Cvb.Driver | |
{ | |
/// <summary> | |
/// Parser for GigE Vision chunk data. | |
/// </summary> | |
public static class GevChunkParser | |
{ | |
/// <summary> | |
/// Parses the given <paramref name="image"/> for chunks. | |
/// </summary> | |
/// <param name="image">Image to parse for chunks.</param> | |
public static GevChunk[] Parse(DeviceImage image) | |
{ | |
if (image == null) | |
throw new ArgumentNullException(nameof(image)); | |
if (!image.HasChunks()) | |
return new GevChunk[0]; | |
return Iterate(image.GetBufferBasePtr(), image.GetPayloadSize()); | |
} | |
private static GevChunk[] Iterate(IntPtr bufferPtr, long bufferSize) | |
{ | |
Debug.Assert(bufferPtr != IntPtr.Zero); | |
Debug.Assert(bufferSize > 0); | |
long bufferBase = bufferPtr.ToInt64(); | |
int tagSize = ChunkTag.Size; | |
var chunks = new List<GevChunk>(); | |
var currentTag = bufferBase + bufferSize - tagSize; | |
while (currentTag > bufferBase) | |
{ | |
var tag = ChunkTag.Dereference((IntPtr)currentTag); | |
var dataLength = tag.Length; | |
var data = currentTag - dataLength; | |
if (data < bufferBase) | |
throw new InvalidDataException("chunk length larger than available buffer size"); | |
chunks.Add(new GevChunk | |
( | |
tag.ID, | |
data - bufferBase, | |
dataLength | |
)); | |
currentTag = data - tagSize; | |
} | |
return chunks.ToArray(); | |
} | |
[StructLayout(LayoutKind.Sequential, Pack = 4)] | |
private struct ChunkTag | |
{ | |
private uint _id; | |
private uint _length; | |
public static unsafe ChunkTag Dereference(IntPtr addr) | |
{ | |
return *(ChunkTag*)addr; | |
} | |
public static unsafe int Size => sizeof(ChunkTag); | |
public uint ID => SwapEndianess(_id); | |
public uint Length => SwapEndianess(_length); | |
private static uint SwapEndianess(uint value) => | |
(value & 0x000000FF) << 24 | |
| (value & 0x0000FF00) << 8 | |
| (value & 0x00FF0000) >> 8 | |
| (value & 0xFF000000) >> 24; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment