pleora: add support for sending/receiving KLV metadata as chunk data

This attemps to partially implement MISB ST1608.1, "Transport of Motion
Imagery and Metadata over GigE Vision". This relies on GstKLVMeta, which
is currently a merge request 124 for gst-plugins-base. For now we
include it here. Currently all KLVMeta is packed into one chunk, no
special handling of timestamps is done. Testing has only been done
between pleorasink and pleorasrc, no other MISB-compliant stream.
This commit is contained in:
Joshua M. Doe
2019-11-21 11:47:42 -05:00
parent f6509a1b4d
commit a92281c965
9 changed files with 674 additions and 10 deletions

View File

@@ -18,13 +18,19 @@
*/
#include "streamingchannelsource.h"
#include "klv.h"
/* setup debug */
GST_DEBUG_CATEGORY_EXTERN (pleorasink_debug);
#define GST_CAT_DEFAULT pleorasink_debug
/* these seem to be arbitrary */
#define CHUNKLAYOUTID 0xABCD
#define KLV_CHUNKID 0xFEDC
GstStreamingChannelSource::GstStreamingChannelSource ()
: mAcquisitionBuffer (NULL), mBufferCount (0), mBufferValid (FALSE)
: mAcquisitionBuffer (NULL), mBufferCount (0), mBufferValid (FALSE),
mChunkModeActive(TRUE), mChunkKlvEnabled(TRUE), mKlvChunkSize(0)
{
}
@@ -58,6 +64,67 @@ PvResult GstStreamingChannelSource::GetSupportedPixelType (int aIndex,
return PvResult::Code::OK;
}
PvResult GstStreamingChannelSource::GetSupportedChunk (int aIndex, uint32_t &aID, PvString &aName) const
{
switch (aIndex) {
case 0:
aID = KLV_CHUNKID;
aName = "KLV";
return PvResult::Code::OK;
default:
break;
}
return PvResult::Code::INVALID_PARAMETER;
}
bool GstStreamingChannelSource::GetChunkEnable (uint32_t aChunkID) const
{
switch (aChunkID) {
case KLV_CHUNKID:
return mChunkKlvEnabled;
default:
break;
}
return false;
}
PvResult GstStreamingChannelSource::SetChunkEnable (uint32_t aChunkID, bool aEnabled)
{
switch (aChunkID) {
case KLV_CHUNKID:
mChunkKlvEnabled = aEnabled;
mSink->output_klv = mChunkKlvEnabled;
return PvResult::Code::OK;
default:
break;
}
return PvResult::Code::INVALID_PARAMETER;
}
uint32_t GstStreamingChannelSource::GetRequiredChunkSize() const
{
if (mChunkModeActive && mChunkKlvEnabled) {
/* chunk data must be multiple of 4 bytes, and 16 bytes extra seem
to be needed for chunk ID and length */
return GST_ROUND_UP_4 (mKlvChunkSize) + 16;
} else {
return 0;
}
}
void GstStreamingChannelSource::SetKlvEnabled (gboolean enable)
{
SetChunkEnable (KLV_CHUNKID, enable);
}
gboolean GstStreamingChannelSource::GetKlvEnabled()
{
return GetChunkEnable (KLV_CHUNKID);
}
PvBuffer * GstStreamingChannelSource::AllocBuffer ()
{
if (mBufferCount < mSink->num_internal_buffers) {
@@ -154,12 +221,13 @@ GstStreamingChannelSource::SetCaps (GstCaps * caps)
void
GstStreamingChannelSource::ResizeBufferIfNeeded (PvBuffer * aBuffer)
{
uint32_t lRequiredChunkSize = 0;
uint32_t lRequiredChunkSize = GetRequiredChunkSize();
PvImage *lImage = aBuffer->GetImage ();
if ((lImage->GetWidth () != mWidth) ||
(lImage->GetHeight () != mHeight) ||
(lImage->GetPixelType () != mPixelType) ||
(lImage->GetMaximumChunkLength () != lRequiredChunkSize)) {
GST_LOG_OBJECT (mSink, "Width=%d, Height=%d, PixelType=%d, and/or ChunkLength=%d changed, reallocating buffer", mWidth, mHeight, mPixelType, lRequiredChunkSize);
lImage->Alloc (mWidth, mHeight, mPixelType, 0, 0, lRequiredChunkSize);
}
}
@@ -167,7 +235,9 @@ GstStreamingChannelSource::ResizeBufferIfNeeded (PvBuffer * aBuffer)
void
GstStreamingChannelSource::SetBuffer (GstBuffer * buf)
{
GST_LOG ("SetBuffer");
GByteArray * klv_byte_array = NULL;
GST_LOG_OBJECT (mSink, "SetBuffer");
g_mutex_lock (&mSink->mutex);
@@ -184,15 +254,25 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf)
return;
}
if (mChunkKlvEnabled) {
klv_byte_array = GetKlvByteArray (buf);
if (klv_byte_array) {
mKlvChunkSize = klv_byte_array->len;
} else {
mKlvChunkSize = 0;
}
}
ResizeBufferIfNeeded (mAcquisitionBuffer);
/* TODO: avoid memcpy (when strides align) by attaching to PvBuffer */
GstMapInfo minfo;
gst_buffer_map (buf, &minfo, GST_MAP_READ);
ResizeBufferIfNeeded (mAcquisitionBuffer);
guint8 *dst = mAcquisitionBuffer->GetDataPointer ();
if (!dst) {
GST_ERROR ("Have buffer to fill, but data pointer is invalid");
GST_ERROR_OBJECT (mSink, "Have buffer to fill, but data pointer is invalid");
g_mutex_unlock (&mSink->mutex);
return;
}
@@ -202,8 +282,68 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf)
gst_buffer_unmap (buf, &minfo);
mAcquisitionBuffer->ResetChunks();
mAcquisitionBuffer->SetChunkLayoutID(CHUNKLAYOUTID);
if (mChunkKlvEnabled && klv_byte_array && klv_byte_array->len > 0) {
PvResult pvRes;
pvRes = mAcquisitionBuffer->AddChunk (KLV_CHUNKID, (uint8_t*)klv_byte_array->data, klv_byte_array->len);
if (pvRes.IsOK ()) {
GST_LOG_OBJECT (mSink, "Added KLV as chunk data (len=%d)", klv_byte_array->len);
} else {
GST_WARNING_OBJECT (mSink, "Failed to add KLV as chunk data (len=%d): %s",
klv_byte_array->len,
pvRes.GetDescription ().GetAscii ());
}
}
if (klv_byte_array) {
g_byte_array_unref (klv_byte_array);
}
mBufferValid = TRUE;
g_cond_signal (&mSink->cond);
g_mutex_unlock (&mSink->mutex);
}
GByteArray * GstStreamingChannelSource::GetKlvByteArray (GstBuffer * buf)
{
GstKLVMeta *klv_meta;
gpointer iter = NULL;
GByteArray *byte_array;
{
//FIXME put fake data for testing
const guint8 klv_header[18] =
{ 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x0b, 0x01, 0x01, 0x0e, 0x01, 0x03,
0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 0xFF };
buf = gst_buffer_make_writable (buf);
gst_buffer_add_klv_meta_from_data (buf, klv_header, sizeof (klv_header));
gst_buffer_add_klv_meta_from_data (buf, klv_header, sizeof (klv_header));
}
/* spec says KLV can all be in one chunk, or multiple chunks, we do one chunk */
byte_array = g_byte_array_new ();
while ((klv_meta = (GstKLVMeta *) gst_buffer_iterate_meta_filtered (buf,
&iter, GST_KLV_META_API_TYPE))) {
gsize klv_size;
const guint8 *klv_data;
klv_data = gst_klv_meta_get_data (klv_meta, &klv_size);
if (!klv_data) {
GST_WARNING_OBJECT (mSink, "Failed to get KLV data from meta");
break;
}
g_byte_array_append (byte_array, klv_data, klv_size);
}
/* chunk length must be multiple of 4 bytes */
if (byte_array->len % 4 != 0) {
const guint8 padding[4] = {0};
const guint padding_len = GST_ROUND_UP_4 (byte_array->len) - byte_array->len;
g_byte_array_append (byte_array, padding, padding_len);
}
return byte_array;
}