changeset 12371:656b5b442f0b draft

CEA 608/708 Support Parse the SEI messages from text file and insert it into the userSEI with cli option.
author indumathi@multicorewareinc.com
date Mon, 11 Jun 2018 19:44:51 +0530
parents 8f1d8a875bee
children 289b8a3730ae
files doc/reST/cli.rst source/CMakeLists.txt source/common/param.cpp source/encoder/encoder.cpp source/encoder/encoder.h source/encoder/sei.cpp source/encoder/sei.h source/x265.h source/x265cli.h
diffstat 9 files changed, 153 insertions(+-), 2 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Fri Jun 15 13:03:20 2018 +0530
+++ b/doc/reST/cli.rst	Mon Jun 11 19:44:51 2018 +0530
@@ -2135,6 +2135,12 @@ VUI fields must be manually specified.
 
 	Maximum luma value allowed for input pictures. Any values above max-luma
 	are clipped.  No default.
+    
+.. option:: --usersei-file <filename>
+
+    Text file containing userSEI in POC order : <POC><space><PREFIX><space><NAL UNIT TYPE>/<SEI TYPE><space><SEI Payload>
+    Parse the input file specified and inserts SEI messages into the bitstream. 
+    Currently, we support only PREFIX SEI messages. This is an "application-only" feature.
 
 .. option:: --atc-sei <integer>
 
--- a/source/CMakeLists.txt	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/CMakeLists.txt	Mon Jun 11 19:44:51 2018 +0530
@@ -29,7 +29,7 @@ option(NATIVE_BUILD "Target the build CP
 option(STATIC_LINK_CRT "Statically link C runtime for release builds" OFF)
 mark_as_advanced(FPROFILE_USE FPROFILE_GENERATE NATIVE_BUILD)
 # X265_BUILD must be incremented each time the public API is changed
-set(X265_BUILD 161)
+set(X265_BUILD 162)
 configure_file("${PROJECT_SOURCE_DIR}/x265.def.in"
                "${PROJECT_BINARY_DIR}/x265.def")
 configure_file("${PROJECT_SOURCE_DIR}/x265_config.h.in"
--- a/source/common/param.cpp	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/common/param.cpp	Mon Jun 11 19:44:51 2018 +0530
@@ -302,6 +302,7 @@ void x265_param_default(x265_param* para
     param->bDisableLookahead = 0;
     param->bCopyPicToFrame = 1;
     param->maxAUSizeFactor = 1;
+    param->userSeiFile = NULL;
 
     /* DCT Approximations */
     param->bLowPassDct = 0;
@@ -1048,6 +1049,7 @@ int x265_param_parse(x265_param* p, cons
 		OPT("pic-struct") p->pictureStructure = atoi(value);
         OPT("chunk-start") p->chunkStart = atoi(value);
         OPT("chunk-end") p->chunkEnd = atoi(value);
+        OPT("usersei-file") p->userSeiFile = strdup(value);
         else
             return X265_PARAM_BAD_NAME;
     }
--- a/source/encoder/encoder.cpp	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/encoder/encoder.cpp	Mon Jun 11 19:44:51 2018 +0530
@@ -79,6 +79,7 @@ Encoder::Encoder()
     m_threadPool = NULL;
     m_analysisFileIn = NULL;
     m_analysisFileOut = NULL;
+    m_seiFile = NULL;
     m_offsetEmergency = NULL;
     m_iFrameNum = 0;
     m_iPPSQpMinus26 = 0;
@@ -412,6 +413,20 @@ void Encoder::create()
 
     m_emitCLLSEI = p->maxCLL || p->maxFALL;
 
+    if (m_param->userSeiFile)
+    {
+        m_seiFile = x265_fopen(m_param->userSeiFile, "r");
+        if (!m_seiFile)
+        {
+            x265_log_file(NULL, X265_LOG_ERROR, "%s file not found or Failed to open\n", m_param->userSeiFile);
+            m_aborted = true;
+        }
+        else
+             m_enableUserSei = 1;
+    }
+    else
+         m_enableUserSei = 0;
+
 #if ENABLE_HDR10_PLUS
     if (m_bToneMap)
         m_numCimInfo = m_hdr10plus_api->hdr10plus_json_to_movie_cim(m_param->toneMapFile, m_cim);
@@ -782,6 +797,8 @@ void Encoder::destroy()
         }
         X265_FREE(temp);
      }
+    if (m_seiFile)
+        fclose(m_seiFile);
     if (m_param)
     {
         if (m_param->csvfpt)
@@ -922,7 +939,12 @@ int Encoder::encode(const x265_picture* 
             }
         }
 #endif
-
+/* seiMsg will contain SEI messages specified in a fixed file format in POC order.
+* Format of the file : <POC><space><PREFIX><space><NAL UNIT TYPE>/<SEI TYPE><space><SEI Payload> */
+        x265_sei_payload seiMsg;
+        seiMsg.payload = NULL;
+        if (m_enableUserSei)
+            readUserSeiFile(seiMsg, m_pocLast);
         if (pic_in->bitDepth < 8 || pic_in->bitDepth > 16)
         {
             x265_log(m_param, X265_LOG_ERROR, "Input bit depth (%d) must be between 8 and 16\n",
@@ -1008,6 +1030,8 @@ int Encoder::encode(const x265_picture* 
         if (m_bToneMap && toneMap.payload)
             toneMapEnable = 1;
         int numPayloads = pic_in->userSEI.numPayloads + toneMapEnable;
+        if (m_enableUserSei && seiMsg.payload)
+            numPayloads += m_enableUserSei;
         inFrame->m_userSEI.numPayloads = numPayloads;
 
         if (inFrame->m_userSEI.numPayloads)
@@ -1023,6 +1047,8 @@ int Encoder::encode(const x265_picture* 
                 x265_sei_payload input;
                 if ((i == (numPayloads - 1)) && toneMapEnable)
                     input = toneMap;
+                else if (m_enableUserSei)
+                    input = seiMsg;
                 else
                     input = pic_in->userSEI.payloads[i];
                 int size = inFrame->m_userSEI.payloads[i].payloadSize = input.payloadSize;
@@ -1033,6 +1059,8 @@ int Encoder::encode(const x265_picture* 
             }
             if (toneMap.payload)
                 x265_free(toneMap.payload);
+            if (seiMsg.payload)
+                x265_free(seiMsg.payload);
         }
 
         if (pic_in->quantOffsets != NULL)
@@ -4708,6 +4736,52 @@ void Encoder::printReconfigureParams()
     TOOLCMP(oldParam->rc.rfConstant, newParam->rc.rfConstant, "crf=%f to %f\n");
 }
 
+void Encoder::readUserSeiFile(x265_sei_payload& seiMsg, int curPoc)
+{
+    char line[1024];
+    while (!feof(m_seiFile))
+    {
+        fgets(line, sizeof(line), m_seiFile);
+        int poc = atoi(strtok(line, " "));
+        char *prefix = strtok(NULL, " ");
+        int nalType = atoi(strtok(NULL, "/"));
+        int payloadType = atoi(strtok(NULL, " "));
+        char *base64Encode = strtok(NULL, "\n");
+        int base64EncodeLength = (int)strlen(base64Encode);
+        char *base64Decode = SEI::base64Decode(base64Encode, base64EncodeLength);
+        if (nalType == NAL_UNIT_PREFIX_SEI && (!strcmp(prefix, "PREFIX")))
+        {
+            int currentPOC = curPoc + 1;
+            if (currentPOC == poc)
+            {
+                seiMsg.payloadSize = (base64EncodeLength / 4) * 3;
+                seiMsg.payload = (uint8_t*)x265_malloc(sizeof(uint8_t) * seiMsg.payloadSize);
+                if (!seiMsg.payload)
+                {
+                    x265_log(m_param, X265_LOG_ERROR, "Unable to allocate memory for SEI payload\n");
+                    break;
+                }
+                if (payloadType == 4)
+                    seiMsg.payloadType = USER_DATA_REGISTERED_ITU_T_T35;
+                else if (payloadType == 5)
+                    seiMsg.payloadType = USER_DATA_UNREGISTERED;
+                else
+                {
+                    x265_log(m_param, X265_LOG_WARNING, "Unsupported SEI payload Type for frame %d\n", poc);
+                    break;
+                }
+                memcpy(seiMsg.payload, base64Decode, seiMsg.payloadSize);
+                break;
+            }
+        }
+        else
+        {
+            x265_log(m_param, X265_LOG_WARNING, "SEI message for frame %d is not inserted. Will support only PREFIX SEI messages.\n", poc);
+            break;
+        }
+    }
+}
+
 bool Encoder::computeSPSRPSIndex()
 {
     RPS* rpsInSPS = m_sps.spsrps;
--- a/source/encoder/encoder.h	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/encoder/encoder.h	Mon Jun 11 19:44:51 2018 +0530
@@ -169,6 +169,7 @@ public:
     Frame*             m_exportedPic;
     FILE*              m_analysisFileIn;
     FILE*              m_analysisFileOut;
+    FILE*              m_seiFile;
     x265_param*        m_param;
     x265_param*        m_latestParam;     // Holds latest param during a reconfigure
     RateControl*       m_rateControl;
@@ -212,6 +213,7 @@ public:
     double                m_cR;
 
     int                     m_bToneMap; // Enables tone-mapping
+    int                     m_enableUserSei;
 
 #ifdef ENABLE_HDR10_PLUS
     const hdr10plus_api     *m_hdr10plus_api;
@@ -299,6 +301,8 @@ public:
 
     int validateAnalysisData(x265_analysis_data* analysis, int readWriteFlag);
 
+    void readUserSeiFile(x265_sei_payload& seiMsg, int poc);
+
     void calcRefreshInterval(Frame* frameEnc);
 
     void initRefIdx();
--- a/source/encoder/sei.cpp	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/encoder/sei.cpp	Mon Jun 11 19:44:51 2018 +0530
@@ -90,3 +90,63 @@ void SEI::setSize(uint32_t size)
 {
     m_payloadSize = size;
 }
+
+/* charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" */
+
+char* SEI::base64Decode(char encodedString[], int base64EncodeLength)
+{
+    char* decodedString;
+    decodedString = (char*)malloc(sizeof(char) * ((base64EncodeLength / 4) * 3));
+    int i, j, k = 0;
+    // stores the bitstream
+    int bitstream = 0;
+    // countBits stores current number of bits in bitstream
+    int countBits = 0;
+    // selects 4 characters from encodedString at a time. Find the position of each encoded character in charSet and stores in bitstream
+    for (i = 0; i < base64EncodeLength; i += 4)
+    {
+        bitstream = 0, countBits = 0;
+        for (j = 0; j < 4; j++)
+        {
+            // make space for 6 bits
+            if (encodedString[i + j] != '=')
+            {
+                bitstream = bitstream << 6;
+                countBits += 6;
+            }
+            // Finding the position of each encoded character in charSet and storing in bitstream, use OR '|' operator to store bits
+
+            if (encodedString[i + j] >= 'A' && encodedString[i + j] <= 'Z')
+                bitstream = bitstream | (encodedString[i + j] - 'A');
+
+            else if (encodedString[i + j] >= 'a' && encodedString[i + j] <= 'z')
+                bitstream = bitstream | (encodedString[i + j] - 'a' + 26);
+            
+            else if (encodedString[i + j] >= '0' && encodedString[i + j] <= '9')
+                bitstream = bitstream | (encodedString[i + j] - '0' + 52);
+            
+            // '+' occurs in 62nd position in charSet
+            else if (encodedString[i + j] == '+')
+                bitstream = bitstream | 62;
+            
+            // '/' occurs in 63rd position in charSet
+            else if (encodedString[i + j] == '/')
+                bitstream = bitstream | 63;
+            
+            // to delete appended bits during encoding
+            else
+            {
+                bitstream = bitstream >> 2;
+                countBits -= 2;
+            }
+        }
+    
+        while (countBits != 0)
+        {
+            countBits -= 8;
+            decodedString[k++] = (bitstream >> countBits) & 255;
+        }
+    }
+    return decodedString;
+}
+
--- a/source/encoder/sei.h	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/encoder/sei.h	Mon Jun 11 19:44:51 2018 +0530
@@ -41,6 +41,7 @@ public:
     void alignAndSerialize(Bitstream& bs, int lastSei, int isSingleSei, NalUnitType nalUnitType, NALList& list);
     int countPayloadSize(const SPS& sps);
     void setSize(uint32_t size);
+    static char* base64Decode(char encodedString[], int base64EncodeLength);
     virtual ~SEI() {}
 protected:
     SEIPayloadType  m_payloadType;
--- a/source/x265.h	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/x265.h	Mon Jun 11 19:44:51 2018 +0530
@@ -1641,6 +1641,8 @@ typedef struct x265_param
 	* used in taking lookahead decisions, but, they will not be encoded.
     * Default 0 (disabled). */
     int       chunkEnd;
+    /* File containing base64 encoded SEI messages in POC order */
+    const char*    userSeiFile;
 
 } x265_param;
 
--- a/source/x265cli.h	Fri Jun 15 13:03:20 2018 +0530
+++ b/source/x265cli.h	Mon Jun 11 19:44:51 2018 +0530
@@ -304,6 +304,7 @@ static const struct option long_options[
     { "no-single-sei", no_argument, NULL, 0 },
 	{ "atc-sei", required_argument, NULL, 0 },
 	{ "pic-struct", required_argument, NULL, 0 },
+    { "usersei-file", required_argument, NULL, 0 },
     { 0, 0, 0, 0 },
     { 0, 0, 0, 0 },
     { 0, 0, 0, 0 },
@@ -354,6 +355,7 @@ static void showHelp(x265_param *param)
     H0("   --dhdr10-info <filename>      JSON file containing the Creative Intent Metadata to be encoded as Dynamic Tone Mapping\n");
     H0("   --[no-]dhdr10-opt             Insert tone mapping SEI only for IDR frames and when the tone mapping information changes. Default disabled\n");
 #endif
+    H0("   --usersei-file <filename>     Text file containing SEI messages in the following format : <POC><space><PREFIX><space><NAL UNIT TYPE>/<SEI TYPE><space><SEI Payload>\n");
     H0("-f/--frames <integer>            Maximum number of frames to encode. Default all\n");
     H0("   --seek <integer>              First frame to encode\n");
     H1("   --[no-]interlace <bff|tff>    Indicate input pictures are interlace fields in temporal order. Default progressive\n");