changeset 12513:220cdb4328a1 draft

Add codes to support field feature
author Shushuang <shushang@multicorewareinc.com>
date Thu, 23 May 2019 11:28:44 +0800
parents 3f4fb9a2ac68
children ccc7a3edd595
files doc/reST/cli.rst source/CMakeLists.txt source/common/frame.cpp source/common/frame.h source/common/param.cpp source/common/slice.h source/encoder/dpb.cpp source/encoder/encoder.cpp source/encoder/frameencoder.cpp source/x265.cpp source/x265.h source/x265cli.h
diffstat 12 files changed, 184 insertions(+-), 32 deletions(-) [+]
line wrap: on
line diff
--- a/doc/reST/cli.rst	Mon Apr 22 11:11:35 2019 +0530
+++ b/doc/reST/cli.rst	Thu May 23 11:28:44 2019 +0800
@@ -551,6 +551,10 @@ frame counts) are only applicable to the
 	This feature can be enabled only in closed GOP structures.
 	Default 0 (disabled).
 
+.. option:: --field, --no-field
+
+	Enable or disable field coding. Default disabled.
+	
 Profile, Level, Tier
 ====================
 
--- a/source/CMakeLists.txt	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/CMakeLists.txt	Thu May 23 11:28:44 2019 +0800
@@ -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 174)
+set(X265_BUILD 175)
 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/frame.cpp	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/common/frame.cpp	Thu May 23 11:28:44 2019 +0800
@@ -56,6 +56,7 @@ Frame::Frame()
     m_addOnCtuInfo = NULL;
     m_addOnPrevChange = NULL;
     m_classifyFrame = false;
+    m_fieldNum = 0;
 }
 
 bool Frame::create(x265_param *param, float* quantOffsets)
--- a/source/common/frame.h	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/common/frame.h	Thu May 23 11:28:44 2019 +0800
@@ -129,6 +129,7 @@ public:
     uint32_t*              m_classifyCount;
 
     bool                   m_classifyFrame;
+    int                    m_fieldNum;
 
     Frame();
 
--- a/source/common/param.cpp	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/common/param.cpp	Thu May 23 11:28:44 2019 +0800
@@ -140,6 +140,7 @@ void x265_param_default(x265_param* para
     param->uhdBluray = 0;
     param->bHighTier = 1; //Allow high tier by default
     param->interlaceMode = 0;
+    param->bField = 0;
     param->bAnnexB = 1;
     param->bRepeatHeaders = 0;
     param->bEnableAccessUnitDelimiters = 0;
@@ -1267,6 +1268,7 @@ int x265_param_parse(x265_param* p, cons
         OPT("svt-fps-in-vps") x265_log(p, X265_LOG_WARNING, "Option %s is SVT-HEVC Encoder specific; Disabling it here \n", name);
 #endif
         OPT("fades") p->bEnableFades = atobool(value);
+        OPT("field") p->bField = atobool( value );
         else
             return X265_PARAM_BAD_NAME;
     }
@@ -1639,6 +1641,12 @@ int x265_check_params(x265_param* param)
         if (param->dolbyProfile == 81)
             CHECK(!(param->masteringDisplayColorVolume), "Dolby Vision profile - 8.1 requires Mastering display color volume information\n");
     }
+
+    if (param->bField && param->interlaceMode)
+    {
+        CHECK( (param->bFrameAdaptive==0), "Adaptive B-frame decision method should be closed for field feature.\n" );
+        // to do
+    }
 #if !X86_64
     CHECK(param->searchMethod == X265_SEA && (param->sourceWidth > 840 || param->sourceHeight > 480),
         "SEA motion search does not support resolutions greater than 480p in 32 bit build");
@@ -2045,6 +2053,7 @@ char *x265_param2string(x265_param* p, i
     BOOL(p->bSingleSeiNal, "single-sei");
     BOOL(p->rc.hevcAq, "hevc-aq");
     BOOL(p->bEnableSvtHevc, "svt");
+    BOOL(p->bField, "field");
     s += sprintf(s, " qp-adaptation-range=%.2f", p->rc.qpAdaptationRange);
 #undef BOOL
     return buf;
@@ -2370,6 +2379,7 @@ void x265_copy_params(x265_param* dst, x
     dst->dolbyProfile = src->dolbyProfile;
     dst->bEnableSvtHevc = src->bEnableSvtHevc;
     dst->bEnableFades = src->bEnableFades;
+    dst->bField = src->bField;
 
 #ifdef SVT_HEVC
     memcpy(dst->svtHevcParam, src->svtHevcParam, sizeof(EB_H265_ENC_CONFIGURATION));
--- a/source/common/slice.h	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/common/slice.h	Thu May 23 11:28:44 2019 +0800
@@ -361,6 +361,7 @@ public:
     int         numRefIdxDefault[2];
     int         m_iNumRPSInSPS;
     const x265_param *m_param;
+    int         m_fieldNum;
 
     Slice()
     {
@@ -376,6 +377,7 @@ public:
         numRefIdxDefault[1] = 1;
         m_rpsIdx = -1;
         m_chromaQpOffset[0] = m_chromaQpOffset[1] = 0;
+        m_fieldNum = 0;
     }
 
     void disableWeights();
--- a/source/encoder/dpb.cpp	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/encoder/dpb.cpp	Thu May 23 11:28:44 2019 +0800
@@ -127,6 +127,7 @@ void DPB::prepareEncode(Frame *newFrame)
 {
     Slice* slice = newFrame->m_encData->m_slice;
     slice->m_poc = newFrame->m_poc;
+    slice->m_fieldNum = newFrame->m_fieldNum;
 
     int pocCurr = slice->m_poc;
     int type = newFrame->m_lowres.sliceType;
--- a/source/encoder/encoder.cpp	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/encoder/encoder.cpp	Thu May 23 11:28:44 2019 +0800
@@ -1107,6 +1107,8 @@ int Encoder::encode(const x265_picture* 
         inFrame->m_pts       = pic_in->pts;
         inFrame->m_forceqp   = pic_in->forceqp;
         inFrame->m_param     = (m_reconfigure || m_reconfigureRc) ? m_latestParam : m_param;
+        if (m_param->bField && m_param->interlaceMode)
+            inFrame->m_fieldNum = pic_in->fieldNum;
 
         copyUserSEIMessages(inFrame, pic_in);
 
--- a/source/encoder/frameencoder.cpp	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/encoder/frameencoder.cpp	Thu May 23 11:28:44 2019 +0800
@@ -686,10 +686,25 @@ void FrameEncoder::compressFrame()
 
         if (vui->frameFieldInfoPresentFlag)
         {
-            if (m_param->interlaceMode == 2)
-                sei->m_picStruct = (poc & 1) ? 1 /* top */ : 2 /* bottom */;
-            else if (m_param->interlaceMode == 1)
-                sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1 /* top */;
+            if (m_param->interlaceMode > 0)
+            {
+                if( m_param->interlaceMode == 2 )
+                {   
+                    // m_picStruct should be set to 3 or 4 when field feature is enabled
+                    if (m_param->bField)
+                        // 3: Top field, bottom field, in that order; 4: Bottom field, top field, in that order
+                        sei->m_picStruct = (slice->m_fieldNum == 1) ? 4 : 3;
+                    else
+                        sei->m_picStruct = (poc & 1) ? 1 /* top */ : 2 /* bottom */;
+                }     
+                else if (m_param->interlaceMode == 1)
+                {
+                    if (m_param->bField)
+                        sei->m_picStruct = (slice->m_fieldNum == 1) ? 3: 4;
+                    else
+                        sei->m_picStruct = (poc & 1) ? 2 /* bottom */ : 1 /* top */;
+                }
+            }
             else
                 sei->m_picStruct = m_param->pictureStructure;
 
--- a/source/x265.cpp	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/x265.cpp	Thu May 23 11:28:44 2019 +0800
@@ -575,6 +575,15 @@ bool CLIOptions::parse(int argc, char **
     info.timebaseNum = param->fpsDenom;
     info.timebaseDenom = param->fpsNum;
 
+    if (param->bField && param->interlaceMode)
+    {   // Field FPS
+        param->fpsNum *= 2;
+        // Field height
+        param->sourceHeight = param->sourceHeight >> 1;
+        // Number of fields to encode
+        param->totalFrames *= 2;
+    }
+
     if (api->param_apply_profile(param, profile))
         return true;
 
@@ -916,7 +925,8 @@ int main(int argc, char **argv)
     bool bDolbyVisionRPU = false;
     uint8_t *rpuPayload = NULL;
     int ret = 0;
-
+    int inputPicNum = 1;
+    x265_picture picField1, picField2;
 
     if (!param->bRepeatHeaders && !param->bEnableSvtHevc)
     {
@@ -930,7 +940,16 @@ int main(int argc, char **argv)
             cliopt.totalbytes += cliopt.output->writeHeaders(p_nal, nal);
     }
 
-    api->picture_init(param, pic_in);
+    if (param->bField && param->interlaceMode)
+    {
+        api->picture_init(param, &picField1);
+        api->picture_init(param, &picField2);
+        // return back the original height of input
+        param->sourceHeight *= 2;
+        api->picture_init(param, pic_in);
+    }
+    else
+        api->picture_init(param, pic_in);
 
     if (param->dolbyProfile && cliopt.dolbyVisionRpu)
     {
@@ -952,7 +971,7 @@ int main(int argc, char **argv)
     // main encoder loop
     while (pic_in && !b_ctrl_c)
     {
-        pic_orig.poc = inFrameCount;
+        pic_orig.poc = (param->bField && param->interlaceMode) ? inFrameCount * 2 : inFrameCount;
         if (cliopt.qpfile)
         {
             if (!cliopt.parseQPFile(pic_orig))
@@ -980,39 +999,128 @@ int main(int argc, char **argv)
             /* Overwrite PTS */
             pic_in->pts = pic_in->poc;
 
+            // convert to field
+            if (param->bField && param->interlaceMode)
+            {
+                int height = pic_in->height >> 1;
+                
+                int static bCreated = 0;
+                if (bCreated == 0)
+                {
+                    bCreated = 1;
+                    inputPicNum = 2;
+                    picField1.fieldNum = 1;
+                    picField2.fieldNum = 2;
+
+                    picField1.bitDepth = picField2.bitDepth = pic_in->bitDepth;
+                    picField1.colorSpace = picField2.colorSpace = pic_in->colorSpace;
+                    picField1.height = picField2.height = pic_in->height >> 1;
+                    picField1.framesize = picField2.framesize = pic_in->framesize >> 1;
+
+                    char* field1Buf = X265_MALLOC( char, pic_in->framesize >> 1 );
+                    char* field2Buf = X265_MALLOC( char, pic_in->framesize >> 1 );
+  
+                    int stride = picField1.stride[0] = picField2.stride[0] = pic_in->stride[0];
+                    int framesize = stride * (height >> x265_cli_csps[pic_in->colorSpace].height[0]);
+                    picField1.planes[0] = field1Buf;
+                    picField2.planes[0] = field2Buf;
+                    for (int i = 1; i < x265_cli_csps[pic_in->colorSpace].planes; i++)
+                    {
+                        picField1.planes[i] = field1Buf + framesize;
+                        picField2.planes[i] = field2Buf + framesize;
+
+                        stride = picField1.stride[i] = picField2.stride[i] = pic_in->stride[i];
+                        framesize += (stride * (height >> x265_cli_csps[pic_in->colorSpace].height[i]));
+                    }
+                    assert(framesize  == picField1.framesize);
+                }
+
+                picField1.pts = picField1.poc = pic_in->poc;
+                picField2.pts = picField2.poc = pic_in->poc + 1;
+
+                picField1.userSEI = picField2.userSEI = pic_in->userSEI;
+
+                //if (pic_in->userData)
+                //{
+                //    // Have to handle userData here
+                //}
+
+                if (pic_in->framesize)
+                {
+                    for (int i = 0; i < x265_cli_csps[pic_in->colorSpace].planes; i++)
+                    {
+                        char* srcP1 = (char*)pic_in->planes[i];
+                        char* srcP2 = (char*)pic_in->planes[i] + pic_in->stride[i];
+                        char* p1 = (char*)picField1.planes[i];
+                        char* p2 = (char*)picField2.planes[i];
+
+                        int stride = picField1.stride[i];
+
+                        for (int y = 0; y < (height >> x265_cli_csps[pic_in->colorSpace].height[i]); y++)
+                        {
+                            memcpy(p1, srcP1, stride);
+                            memcpy(p2, srcP2, stride);
+                            srcP1 += 2*stride;
+                            srcP2 += 2*stride;
+                            p1 += stride;
+                            p2 += stride;
+                        }
+                    }
+                }
+            }
+
             if (bDolbyVisionRPU)
             {
-                if (rpuParser(pic_in, cliopt.dolbyVisionRpu) > 0)
-                    goto fail;
+                if (param->bField && param->interlaceMode)
+                {
+                    if (rpuParser(&picField1, cliopt.dolbyVisionRpu) > 0)
+                        goto fail;
+                    if (rpuParser(&picField2, cliopt.dolbyVisionRpu) > 0)
+                        goto fail;
+                }
+                else
+                {
+                    if (rpuParser(pic_in, cliopt.dolbyVisionRpu) > 0)
+                        goto fail;
+                }
             }
         }
-        int numEncoded = api->encoder_encode(encoder, &p_nal, &nal, pic_in, pic_recon);
-        if (numEncoded < 0)
-        {
-            b_ctrl_c = 1;
-            ret = 4;
-            break;
-        }
+                
+        for (int inputNum = 0; inputNum < inputPicNum; inputNum++)
+        {  
+            x265_picture *picInput = NULL;
+            if (inputPicNum == 2)
+                picInput = pic_in ? (inputNum ? &picField2 : &picField1) : NULL;
+            else
+                picInput = pic_in;
 
-        if (reconPlay && numEncoded)
-            reconPlay->writePicture(*pic_recon);
-
-        outFrameCount += numEncoded;
+            int numEncoded = api->encoder_encode( encoder, &p_nal, &nal, picInput, pic_recon );
+            if( numEncoded < 0 )
+            {
+                b_ctrl_c = 1;
+                ret = 4;
+                break;
+            }
 
-        if (numEncoded && pic_recon && cliopt.recon)
-            cliopt.recon->writePicture(pic_out);
-        if (nal)
-        {
-            cliopt.totalbytes += cliopt.output->writeFrame(p_nal, nal, pic_out);
-            if (pts_queue)
+            if (reconPlay && numEncoded)
+                reconPlay->writePicture(*pic_recon);
+
+            outFrameCount += numEncoded;
+
+            if (numEncoded && pic_recon && cliopt.recon)
+                cliopt.recon->writePicture(pic_out);
+            if (nal)
             {
-                pts_queue->push(-pic_out.pts);
-                if (pts_queue->size() > 2)
-                    pts_queue->pop();
+                cliopt.totalbytes += cliopt.output->writeFrame(p_nal, nal, pic_out);
+                if (pts_queue)
+                {
+                    pts_queue->push(-pic_out.pts);
+                    if (pts_queue->size() > 2)
+                        pts_queue->pop();
+                }
             }
+            cliopt.printStatus( outFrameCount );
         }
-
-        cliopt.printStatus(outFrameCount);
     }
 
     /* Flush the encoder */
--- a/source/x265.h	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/x265.h	Thu May 23 11:28:44 2019 +0800
@@ -464,6 +464,8 @@ typedef struct x265_picture
 
     //Dolby Vision RPU metadata
     x265_dolby_vision_rpu rpu;
+ 
+    int fieldNum;
 } x265_picture;
 
 typedef enum
@@ -1775,6 +1777,9 @@ typedef struct x265_param
     /* Detect fade-in regions. Enforces I-slice for the brightest point.
        Re-init RC history at that point in ABR mode. Default is disabled. */
     int       bEnableFades;
+
+    /* Enable field coding */
+    int bField;
 } x265_param;
 /* x265_param_alloc:
  *  Allocates an x265_param instance. The returned param structure is not
--- a/source/x265cli.h	Mon Apr 22 11:11:35 2019 +0530
+++ b/source/x265cli.h	Thu May 23 11:28:44 2019 +0800
@@ -73,6 +73,8 @@ static const struct option long_options[
     { "input-csp",      required_argument, NULL, 0 },
     { "interlace",      required_argument, NULL, 0 },
     { "no-interlace",         no_argument, NULL, 0 },
+    { "field",                no_argument, NULL, 0 },
+    { "no-field",             no_argument, NULL, 0 },
     { "fps",            required_argument, NULL, 0 },
     { "seek",           required_argument, NULL, 0 },
     { "frame-skip",     required_argument, NULL, 0 },
@@ -391,6 +393,7 @@ static void showHelp(x265_param *param)
     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");
+    H0("   --[no-]field                  Enable or disable field coding. Default %s\n", OPT( param->bField));
     H1("   --dither                      Enable dither if downscaling to 8 bit pixels. Default disabled\n");
     H0("   --[no-]copy-pic               Copy buffers of input picture in frame. Default %s\n", OPT(param->bCopyPicToFrame));
     H0("\nQuality reporting metrics:\n");