Skip to content

Instantly share code, notes, and snippets.

@parsd
Last active August 28, 2019 13:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save parsd/364f78d3b1b20069658fc0158e0c812c to your computer and use it in GitHub Desktop.
Save parsd/364f78d3b1b20069658fc0158e0c812c to your computer and use it in GitHub Desktop.
CVB.Net GigE Vision Chunk Parsing
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;
}
}
}
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;
}
}
}
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