From 2539c9272a2fe32cd50a3513ef000ac5b4f80498 Mon Sep 17 00:00:00 2001 From: bsobhani Date: Wed, 13 Dec 2023 16:19:33 +0000 Subject: [PATCH] adding video compression (h264) --- ADApp/ADSrc/Codec.h | 6 ++- ADApp/Db/NDCodec.template | 20 ++++++++ ADApp/commonDriverMakefile | 14 ++++++ ADApp/commonLibraryMakefile | 14 ++++++ ADApp/pluginSrc/Makefile | 5 ++ ADApp/pluginSrc/NDPluginCodec.cpp | 84 ++++++++++++++++++++++++++++++- ADApp/pluginSrc/NDPluginCodec.h | 4 ++ 7 files changed, 144 insertions(+), 3 deletions(-) diff --git a/ADApp/ADSrc/Codec.h b/ADApp/ADSrc/Codec.h index 9012783df..c5abc9d9e 100644 --- a/ADApp/ADSrc/Codec.h +++ b/ADApp/ADSrc/Codec.h @@ -6,7 +6,8 @@ static std::string codecName[] = { "jpeg", "blosc", "lz4", - "bslz4" + "bslz4", + "h264" }; typedef enum { @@ -14,7 +15,8 @@ typedef enum { NDCODEC_JPEG, NDCODEC_BLOSC, NDCODEC_LZ4, - NDCODEC_BSLZ4 + NDCODEC_BSLZ4, + NDCODEC_H264 } NDCodecCompressor_t; typedef struct Codec_t { diff --git a/ADApp/Db/NDCodec.template b/ADApp/Db/NDCodec.template index 1ecfeae4a..394fae94b 100644 --- a/ADApp/Db/NDCodec.template +++ b/ADApp/Db/NDCodec.template @@ -44,6 +44,8 @@ record(mbbo, "$(P)$(R)Compressor") field(THVL, "3") field(FRST, "BSLZ4") field(FRVL, "4") + field(FVST, "H264") + field(FVVL, "5") info(autosaveFields, "VAL") } @@ -61,6 +63,8 @@ record(mbbi, "$(P)$(R)Compressor_RBV") field(THVL, "3") field(FRST, "BSLZ4") field(FRVL, "4") + field(FVST, "H264") + field(FVVL, "5") field(SCAN, "I/O Intr") } @@ -217,3 +221,19 @@ record(waveform, "$(P)$(R)CodecError") field(FTVL, "CHAR") field(NELM, "256") } + +# Sets group of picture size. A value of 1 is effectively frame-level compression. Set to higher values for true video compression. +record(ao, "$(P)$(R)GOPSize") +{ + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))GOP_SIZE") +} + +# Sets the quantizer (qmin and qmax) to value written to PV. +# One can effectively adjust quality this way, with 1 being the least lossy, and higher numbers lossier +record(ao, "$(P)$(R)QMinMax") +{ + field(DTYP, "asynInt32") + field(OUT, "@asyn($(PORT),$(ADDR=0),$(TIMEOUT=1))QMINMAX") +} + diff --git a/ADApp/commonDriverMakefile b/ADApp/commonDriverMakefile index 1347f01e5..d6a5c7b4b 100644 --- a/ADApp/commonDriverMakefile +++ b/ADApp/commonDriverMakefile @@ -160,6 +160,20 @@ ifeq ($(WITH_BLOSC),YES) endif endif +ifeq ($(WITH_VC),YES) + ifeq ($(VC_EXTERNAL),NO) + PROD_LIBS += videoCompression + else + ifdef VC_LIB + VC_DIR = $(VC_LIB) + PROD_LIBS += videoCompression + else + PROD_SYS_LIBS += videoCompression + endif + endif +endif + + ifeq ($(WITH_SZIP),YES) ifeq ($(SZIP_EXTERNAL),NO) PROD_LIBS += szip diff --git a/ADApp/commonLibraryMakefile b/ADApp/commonLibraryMakefile index a159db671..70c3f769f 100644 --- a/ADApp/commonLibraryMakefile +++ b/ADApp/commonLibraryMakefile @@ -129,6 +129,20 @@ ifeq ($(WITH_BLOSC),YES) endif endif +ifeq ($(WITH_VC),YES) + ifeq ($(VC_EXTERNAL),NO) + PROD_LIBS += videoCompression + else + ifdef VC_LIB + VC_DIR = $(VC_LIB) + PROD_LIBS += videoCompression + else + PROD_SYS_LIBS += videoCompression + endif + endif +endif + + ifeq ($(WITH_SZIP),YES) ifeq ($(SZIP_EXTERNAL),NO) LIB_LIBS += szip diff --git a/ADApp/pluginSrc/Makefile b/ADApp/pluginSrc/Makefile index f3e2c74f4..f2c562e02 100644 --- a/ADApp/pluginSrc/Makefile +++ b/ADApp/pluginSrc/Makefile @@ -204,6 +204,11 @@ ifeq ($(WITH_BITSHUFFLE), YES) USR_CXXFLAGS += -DHAVE_BITSHUFFLE endif +ifeq ($(WITH_VC), YES) + #INC += video_compression.h + USR_CXXFLAGS += -DHAVE_VC +endif + ifdef BLOSC_INCLUDE USR_INCLUDES += $(addprefix -I, $(BLOSC_INCLUDE)) endif diff --git a/ADApp/pluginSrc/NDPluginCodec.cpp b/ADApp/pluginSrc/NDPluginCodec.cpp index 30e02358f..529e6fb29 100644 --- a/ADApp/pluginSrc/NDPluginCodec.cpp +++ b/ADApp/pluginSrc/NDPluginCodec.cpp @@ -491,7 +491,6 @@ NDArray *compressLZ4(NDArray *input, NDCodecStatus_t *status, char *errorMessage return output; } - NDArray *decompressLZ4(NDArray *input, NDCodecStatus_t *status, char *errorMessage) { // Sanity check @@ -635,6 +634,70 @@ NDArray *decompressBSLZ4(NDArray *input, NDCodecStatus_t *status, char *errorMes #endif // ifdef HAVE_BITSHUFFLE +#ifdef HAVE_VC +#include +//extern int H264_compress_default(const char*, char*, int, int, int); +NDArray *compressH264(NDArray *input, NDCodecStatus_t *status, char *errorMessage) +{ + //printf("inside compressH264\n"); + if (!input->codec.empty()) { + sprintf(errorMessage, "Array is already compressed"); + *status = NDCODEC_WARNING; + return NULL; + } + + switch (input->dataType) { + case NDInt8: + case NDUInt8: + //case NDInt16: + //case NDUInt16: + break; + default: + sprintf(errorMessage, "H264 only supports 8-bit data"); + *status = NDCODEC_ERROR; + //goto failure; + return NULL; + } + + + + NDArrayInfo_t info; + input->getInfo(&info); + int outputSize = LZ4_compressBound((int)info.totalBytes); + NDArray *output = allocArray(input, -1, outputSize); + + if (!output) { + sprintf(errorMessage, "Failed to allocate H264 output array"); + *status = NDCODEC_ERROR; + return NULL; + } + + //int compSize = LZ4_compress_default((const char*)input->pData, (char*)output->pData, (int)info.totalBytes, outputSize); + int x_size = input->dims[0].size; + int y_size = input->dims[1].size; + + //printf("x_size %d y_size %d\n", x_size, y_size); + //printf("before H264 compress default\n"); + //int compSize = H264_compress_default((const char*)input->pData, (char*)output->pData, (int)x_size, (int)y_size, outputSize); + int compSize = H264_compress((const char*)input->pData, (char*)output->pData, (int)x_size, (int)y_size); + + if (compSize <= 0) { + output->release(); + sprintf(errorMessage, "Internal H264 error"); + *status = NDCODEC_ERROR; + return NULL; + } + + output->codec.name = codecName[NDCODEC_H264]; + output->compressedSize = compSize; + //printf("before return output\n"); + + return output; +} +#endif + + + /** Callback function that is called by the NDArray driver with new NDArray data. * Does JPEG or Blosc compression on the array. * If compression is None or fails the input array is passed on without @@ -717,6 +780,14 @@ void NDPluginCodec::processCallbacks(NDArray *pArray) break; } + case NDCODEC_H264: { + //commenting out unlock for now - crashes when changing AVCodecContext parameters while acquiring + //unlock(); + result = compressH264(pArray, &codecStatus, errorMessage); + //lock(); + break; + } + } if (result && result != pArray) { @@ -807,8 +878,17 @@ asynStatus NDPluginCodec::writeInt32(asynUser *pasynUser, epicsInt32 value) value = 1; } else if (function < FIRST_NDCODEC_PARAM) { status = NDPluginDriver::writeInt32(pasynUser, value); + } else if (function == NDCodecGOPSize) { + printf("setting GOP Size...\n"); + set_gop_size(value); + } else if (function == NDCodecQMinMax) { + //lock(); + printf("setting qmin and qmax...\n"); + set_q_min_max(value); + //unlock(); } + /* Set the parameter in the parameter library. */ status = (asynStatus) setIntegerParam(function, value); @@ -877,6 +957,8 @@ NDPluginCodec::NDPluginCodec(const char *portName, int queueSize, int blockingCa createParam(NDCodecBloscCLevelString, asynParamInt32, &NDCodecBloscCLevel); createParam(NDCodecBloscShuffleString, asynParamInt32, &NDCodecBloscShuffle); createParam(NDCodecBloscNumThreadsString, asynParamInt32, &NDCodecBloscNumThreads); + createParam(NDCodecGOPSizeString, asynParamInt32, &NDCodecGOPSize); + createParam(NDCodecQMinMaxString, asynParamInt32, &NDCodecQMinMax); /* Set the plugin type string */ setStringParam(NDPluginDriverPluginType, "NDPluginCodec"); diff --git a/ADApp/pluginSrc/NDPluginCodec.h b/ADApp/pluginSrc/NDPluginCodec.h index b86ce599b..c00484661 100644 --- a/ADApp/pluginSrc/NDPluginCodec.h +++ b/ADApp/pluginSrc/NDPluginCodec.h @@ -13,6 +13,8 @@ #define NDCodecBloscCLevelString "BLOSC_CLEVEL" /* (int r/w) Blosc compression level */ #define NDCodecBloscShuffleString "BLOSC_SHUFFLE" /* (bool r/w) Should Blosc apply shuffling? */ #define NDCodecBloscNumThreadsString "BLOSC_NUMTHREADS" /* (int r/w) Number of threads to be used by Blosc */ +#define NDCodecGOPSizeString "GOP_SIZE" /* (int r/w) Group of pictures size for H264 */ +#define NDCodecQMinMaxString "QMINMAX" /* (int r/w) Sets min and max values for quantizer for H264 */ /** Compress/decompress NDArrays according to available codecs. * This plugin is a source of NDArray callbacks, passing the (possibly @@ -87,6 +89,8 @@ class NDPLUGIN_API NDPluginCodec : public NDPluginDriver { int NDCodecBloscCLevel; int NDCodecBloscShuffle; int NDCodecBloscNumThreads; + int NDCodecGOPSize; + int NDCodecQMinMax; };