[x265] [PATCH Alpha 07/10] Add support for reconstructed file corresponding to Alpha layer

Anusuya Kumarasamy anusuya.kumarasamy at multicorewareinc.com
Mon Aug 5 11:08:21 UTC 2024


>From 9a1baf8cbcfa95be89fd72b00c6b531ede4fb66f Mon Sep 17 00:00:00 2001
From: Kirithika <kirithika at multicorewareinc.com>
Date: Fri, 5 Jul 2024 15:28:10 +0530
Subject: [PATCH] Add support for reconstructed file corresponding to Alpha
 layer.

This commit also does :
1.Receive and proccess frames of all layers corresponding to same POC from
the Frame encoder thread.
---
 source/abrEncApp.cpp            |  34 ++-
 source/encoder/api.cpp          |   4 +-
 source/encoder/encoder.cpp      | 456 ++++++++++++++++----------------
 source/encoder/encoder.h        |   4 +-
 source/encoder/frameencoder.cpp |  24 +-
 source/encoder/frameencoder.h   |   3 +-
 source/x265.h                   |   4 +-
 source/x265cli.cpp              |  54 ++--
 source/x265cli.h                |   5 +-
 9 files changed, 320 insertions(+), 268 deletions(-)

diff --git a/source/abrEncApp.cpp b/source/abrEncApp.cpp
index 96c854de6..eabfb7d2b 100644
--- a/source/abrEncApp.cpp
+++ b/source/abrEncApp.cpp
@@ -530,11 +530,13 @@ ret:
                 x265_log(m_param, X265_LOG_ERROR, "Unable to register
CTRL+C handler: %s in %s\n",
                     strerror(errno), profileName);

-            x265_picture pic_orig, pic_out;
+            x265_picture pic_orig, pic_out[MAX_SCALABLE_LAYERS];
             x265_picture *pic_in = &pic_orig;
             /* Allocate recon picture if analysis save/load is enabled */
             std::priority_queue<int64_t>* pts_queue =
m_cliopt.output->needPTS() ? new std::priority_queue<int64_t>() : NULL;
-            x265_picture *pic_recon = (m_cliopt.recon ||
m_param->analysisSave || m_param->analysisLoad || pts_queue || reconPlay ||
m_param->csvLogLevel) ? &pic_out : NULL;
+            x265_picture *pic_recon[MAX_SCALABLE_LAYERS];
+            for(int i = 0; i < m_param->numScalableLayers; i++)
+                pic_recon[i] = (m_cliopt.recon[i] || m_param->analysisSave
|| m_param->analysisLoad || pts_queue || reconPlay || m_param->csvLogLevel)
? &pic_out[i] : NULL;
             uint32_t inFrameCount = 0;
             uint32_t outFrameCount = 0;
             x265_nal *p_nal;
@@ -545,7 +547,7 @@ ret:
             uint8_t *rpuPayload = NULL;
             int inputPicNum = 1;
             x265_picture picField1, picField2;
-            x265_analysis_data* analysisInfo =
(x265_analysis_data*)(&pic_out.analysisData);
+            x265_analysis_data* analysisInfo =
(x265_analysis_data*)(&pic_out[0].analysisData);
             bool isAbrSave = m_cliopt.saveLevel && (m_parent->m_numEncodes
> 1);

             if (!m_param->bRepeatHeaders && !m_param->bEnableSvtHevc)
@@ -735,7 +737,7 @@ ret:
                     }

                     if (reconPlay && numEncoded)
-                        reconPlay->writePicture(*pic_recon);
+                        reconPlay->writePicture(*pic_recon[0]);

                     outFrameCount += numEncoded;

@@ -744,14 +746,17 @@ ret:
                         copyInfo(analysisInfo);
                     }

-                    if (numEncoded && pic_recon && m_cliopt.recon)
-                        m_cliopt.recon->writePicture(pic_out);
+                    for (int i = 0; i < m_param->numScalableLayers; i++)
+                    {
+                        if (numEncoded && pic_recon[i] &&
m_cliopt.recon[i])
+                            m_cliopt.recon[i]->writePicture(pic_out[i]);
+                    }
                     if (nal)
                     {
-                        m_cliopt.totalbytes +=
m_cliopt.output->writeFrame(p_nal, nal, pic_out);
+                        m_cliopt.totalbytes +=
m_cliopt.output->writeFrame(p_nal, nal, pic_out[0]);
                         if (pts_queue)
                         {
-                            pts_queue->push(-pic_out.pts);
+                            pts_queue->push(-pic_out[0].pts);
                             if (pts_queue->size() > 2)
                                 pts_queue->pop();
                         }
@@ -771,7 +776,7 @@ ret:
                 }

                 if (reconPlay && numEncoded)
-                    reconPlay->writePicture(*pic_recon);
+                    reconPlay->writePicture(*pic_recon[0]);

                 outFrameCount += numEncoded;
                 if (isAbrSave && numEncoded)
@@ -779,14 +784,17 @@ ret:
                     copyInfo(analysisInfo);
                 }

-                if (numEncoded && pic_recon && m_cliopt.recon)
-                    m_cliopt.recon->writePicture(pic_out);
+                for (int i = 0; i < m_param->numScalableLayers; i++)
+                {
+                    if (numEncoded && pic_recon[i] && m_cliopt.recon[i])
+                        m_cliopt.recon[i]->writePicture(pic_out[i]);
+                }
                 if (nal)
                 {
-                    m_cliopt.totalbytes +=
m_cliopt.output->writeFrame(p_nal, nal, pic_out);
+                    m_cliopt.totalbytes +=
m_cliopt.output->writeFrame(p_nal, nal, pic_out[0]);
                     if (pts_queue)
                     {
-                        pts_queue->push(-pic_out.pts);
+                        pts_queue->push(-pic_out[0].pts);
                         if (pts_queue->size() > 2)
                             pts_queue->pop();
                     }
diff --git a/source/encoder/api.cpp b/source/encoder/api.cpp
index 33e98a066..60893f4d1 100644
--- a/source/encoder/api.cpp
+++ b/source/encoder/api.cpp
@@ -406,7 +406,7 @@ int x265_encoder_reconfig_zone(x265_encoder* enc,
x265_zone* zone_in)
     return 0;
 }

-int x265_encoder_encode(x265_encoder *enc, x265_nal **pp_nal, uint32_t
*pi_nal, x265_picture *pic_in, x265_picture *pic_out)
+int x265_encoder_encode(x265_encoder *enc, x265_nal **pp_nal, uint32_t
*pi_nal, x265_picture *pic_in, x265_picture **pic_out)
 {
     if (!enc)
         return -1;
@@ -602,7 +602,7 @@ fail:
         *pi_nal = 0;

     if (numEncoded && encoder->m_param->csvLogLevel &&
encoder->m_outputCount >= encoder->m_latestParam->chunkStart)
-        x265_csvlog_frame(encoder->m_param, pic_out);
+        x265_csvlog_frame(encoder->m_param, pic_out[0]);

     if (numEncoded < 0)
         encoder->m_aborted = true;
diff --git a/source/encoder/encoder.cpp b/source/encoder/encoder.cpp
index e61db1ebc..4ac71f654 100644
--- a/source/encoder/encoder.cpp
+++ b/source/encoder/encoder.cpp
@@ -134,7 +134,6 @@ Encoder::Encoder()
     m_lookahead = NULL;
     m_rateControl = NULL;
     m_dpb = NULL;
-    m_exportedPic = NULL;
     m_numDelayedPic = 0;
     m_outputCount = 0;
     m_param = NULL;
@@ -150,6 +149,8 @@ Encoder::Encoder()
     m_rpsInSpsCount = 0;
     m_cB = 1.0;
     m_cR = 1.0;
+    for (int i = 0; i < MAX_SCALABLE_LAYERS; i++)
+        m_exportedPic[i] = NULL;
     for (int i = 0; i < X265_MAX_FRAME_THREADS; i++)
         m_frameEncoder[i] = NULL;
     for (uint32_t i = 0; i < DUP_BUFFER; i++)
@@ -861,10 +862,13 @@ void Encoder::destroy()
         X265_FREE(m_rdCost);
         X265_FREE(m_trainingCount);
     }
-    if (m_exportedPic)
+    for (int layer = 0; layer < m_param->numScalableLayers; layer++)
     {
-        ATOMIC_DEC(&m_exportedPic->m_countRefEncoders);
-        m_exportedPic = NULL;
+        if (m_exportedPic[layer])
+        {
+            ATOMIC_DEC(&m_exportedPic[layer]->m_countRefEncoders);
+            m_exportedPic[layer] = NULL;
+        }
     }

     if (m_param->bEnableFrameDuplication)
@@ -1462,7 +1466,7 @@ bool Encoder::generateMcstfRef(Frame* frameEnc,
FrameEncoder* currEncoder)
  * returns 0 if no frames are currently available for output
  *         1 if frame was output, m_nalList contains access unit
  *         negative on malloc error or abort */
-int Encoder::encode(const x265_picture* pic_in, x265_picture* pic_out)
+int Encoder::encode(const x265_picture* pic_in, x265_picture** pic_out)
 {
 #if CHECKED_BUILD || _DEBUG
     if (g_checkFailures)
@@ -1479,14 +1483,16 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
     bool dontRead = false;
     bool dropflag = false;

-    if (m_exportedPic)
+    if (*m_exportedPic)
     {
         if (!m_param->bUseAnalysisFile && m_param->analysisSave)
-            x265_free_analysis_data(m_param,
&m_exportedPic->m_analysisData);
-
-        ATOMIC_DEC(&m_exportedPic->m_countRefEncoders);
+            x265_free_analysis_data(m_param,
&m_exportedPic[0]->m_analysisData);

-        m_exportedPic = NULL;
+        for (int i = 0; i < m_param->numScalableLayers; i++)
+        {
+            ATOMIC_DEC(&m_exportedPic[i]->m_countRefEncoders);
+            m_exportedPic[i] = NULL;
+        }
         m_dpb->recycleUnreferenced();

         if (m_param->bEnableTemporalFilter)
@@ -1579,7 +1585,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)

         Frame* inFrame[MAX_SCALABLE_LAYERS];
         x265_param *p = (m_reconfigure || m_reconfigureRc) ? m_latestParam
: m_param;
-        for (int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)
+        for (int layer = 0; layer < m_param->numScalableLayers; layer++)
         {
             if (m_dpb->m_freeList.empty())
             {
@@ -1648,6 +1654,8 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
                 inFrame[layer]->m_tempLayer = 0;
                 inFrame[layer]->m_sameLayerRefPic = 0;
                 inFrame[layer]->m_sLayerId = layer;
+                inFrame[layer]->m_valid = false;
+                inFrame[layer]->m_lowres.bKeyframe = false;
             }

             /* Copy input picture into a Frame and PicYuv, send to
lookahead */
@@ -1887,7 +1895,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
      * and then to give it a new frame to work on.  In zero-latency mode,
we must encode this
      * input picture before returning so the order must be reversed. This
do/while() loop allows
      * us to alternate the order of the calls without ugly code
replication */
-    Frame* outFrame = NULL;
+    Frame** outFrames = { NULL };
     Frame* frameEnc[MAX_SCALABLE_LAYERS] = { NULL };
     int pass = 0;
     do
@@ -1896,243 +1904,246 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
          * encoding the frame.  This is how back-pressure through the API
is
          * accomplished when the encoder is full */
         if (!m_bZeroLatency || pass)
-            outFrame = curEncoder->getEncodedPicture(m_nalList);
-        if (outFrame)
+            outFrames = curEncoder->getEncodedPicture(m_nalList);
+        if (outFrames)
         {
-            Slice *slice = outFrame->m_encData->m_slice;
-            x265_frame_stats* frameData = NULL;
-
-            /* Free up inputPic->analysisData since it has already been
used */
-            if ((m_param->analysisLoad && !m_param->analysisSave) ||
((m_param->bAnalysisType == AVC_INFO) && slice->m_sliceType != I_SLICE))
-                x265_free_analysis_data(m_param,
&outFrame->m_analysisData);
-
-            if (pic_out)
+            for (int sLayer = 0; sLayer < m_param->numScalableLayers;
sLayer++)
             {
-                PicYuv *recpic = outFrame->m_reconPic;
-                pic_out->poc = slice->m_poc;
-                pic_out->bitDepth = X265_DEPTH;
-                pic_out->userData = outFrame->m_userData;
-                pic_out->colorSpace = m_param->internalCsp;
-                pic_out->frameData.tLayer = outFrame->m_tempLayer;
-                frameData = &(pic_out->frameData);
-
-                pic_out->pts = outFrame->m_pts;
-                pic_out->dts = outFrame->m_dts;
-                pic_out->reorderedPts = outFrame->m_reorderedPts;
-                pic_out->sliceType = outFrame->m_lowres.sliceType;
-                pic_out->planes[0] = recpic->m_picOrg[0];
-                pic_out->stride[0] = (int)(recpic->m_stride *
sizeof(pixel));
-                if (m_param->internalCsp != X265_CSP_I400)
+                Frame* outFrame = *(outFrames + sLayer);
+                Slice* slice = outFrame->m_encData->m_slice;
+                x265_frame_stats* frameData = NULL;
+
+                /* Free up inputPic->analysisData since it has already
been used */
+                if ((m_param->analysisLoad && !m_param->analysisSave) ||
((m_param->bAnalysisType == AVC_INFO) && slice->m_sliceType != I_SLICE))
+                    x265_free_analysis_data(m_param,
&outFrame->m_analysisData);
+                if (pic_out[sLayer])
                 {
-                    pic_out->planes[1] = recpic->m_picOrg[1];
-                    pic_out->stride[1] = (int)(recpic->m_strideC *
sizeof(pixel));
-                    pic_out->planes[2] = recpic->m_picOrg[2];
-                    pic_out->stride[2] = (int)(recpic->m_strideC *
sizeof(pixel));
-                }
+                    PicYuv* recpic = outFrame->m_reconPic;
+                    pic_out[sLayer]->poc = slice->m_poc;
+                    pic_out[sLayer]->bitDepth = X265_DEPTH;
+                    pic_out[sLayer]->userData = outFrame->m_userData;
+                    pic_out[sLayer]->colorSpace = m_param->internalCsp;
+                    pic_out[sLayer]->frameData.tLayer =
outFrame->m_tempLayer;
+                    frameData = &(pic_out[sLayer]->frameData);
+
+                    pic_out[sLayer]->pts = outFrame->m_pts;
+                    pic_out[sLayer]->dts = outFrame->m_dts;
+                    pic_out[sLayer]->reorderedPts =
outFrame->m_reorderedPts;
+                    pic_out[sLayer]->sliceType =
outFrame->m_lowres.sliceType;
+                    pic_out[sLayer]->planes[0] = recpic->m_picOrg[0];
+                    pic_out[sLayer]->stride[0] = (int)(recpic->m_stride *
sizeof(pixel));
+                    if (m_param->internalCsp != X265_CSP_I400)
+                    {
+                        pic_out[sLayer]->planes[1] = recpic->m_picOrg[1];
+                        pic_out[sLayer]->stride[1] =
(int)(recpic->m_strideC * sizeof(pixel));
+                        pic_out[sLayer]->planes[2] = recpic->m_picOrg[2];
+                        pic_out[sLayer]->stride[2] =
(int)(recpic->m_strideC * sizeof(pixel));
+                    }

-                /* Dump analysis data from pic_out to file in save mode
and free */
-                if (m_param->analysisSave)
-                {
-                    pic_out->analysisData.poc = pic_out->poc;
-                    pic_out->analysisData.sliceType = pic_out->sliceType;
-                    pic_out->analysisData.bScenecut =
outFrame->m_lowres.bScenecut;
-                    pic_out->analysisData.satdCost  =
outFrame->m_lowres.satdCost;
-                    pic_out->analysisData.numCUsInFrame =
outFrame->m_analysisData.numCUsInFrame;
-                    pic_out->analysisData.numPartitions =
outFrame->m_analysisData.numPartitions;
-                    pic_out->analysisData.wt = outFrame->m_analysisData.wt;
-                    pic_out->analysisData.interData =
outFrame->m_analysisData.interData;
-                    pic_out->analysisData.intraData =
outFrame->m_analysisData.intraData;
-                    pic_out->analysisData.distortionData =
outFrame->m_analysisData.distortionData;
-                    pic_out->analysisData.modeFlag[0] =
outFrame->m_analysisData.modeFlag[0];
-                    pic_out->analysisData.modeFlag[1] =
outFrame->m_analysisData.modeFlag[1];
-                    if (m_param->bDisableLookahead)
+                    /* Dump analysis data from pic_out[sLayer] to file in
save mode and free */
+                    if (m_param->analysisSave)
                     {
-                        int factor = 1;
-                        if (m_param->scaleFactor)
-                            factor = m_param->scaleFactor * 2;
-                        pic_out->analysisData.numCuInHeight =
outFrame->m_analysisData.numCuInHeight;
-                        pic_out->analysisData.lookahead.dts =
outFrame->m_dts;
-                        pic_out->analysisData.lookahead.reorderedPts =
outFrame->m_reorderedPts;
-                        pic_out->analysisData.satdCost *= factor;
-                        pic_out->analysisData.lookahead.keyframe =
outFrame->m_lowres.bKeyframe;
-                        pic_out->analysisData.lookahead.lastMiniGopBFrame
= outFrame->m_lowres.bLastMiniGopBFrame;
-                        if (m_rateControl->m_isVbv)
+                        pic_out[sLayer]->analysisData.poc =
pic_out[sLayer]->poc;
+                        pic_out[sLayer]->analysisData.sliceType =
pic_out[sLayer]->sliceType;
+                        pic_out[sLayer]->analysisData.bScenecut =
outFrame->m_lowres.bScenecut;
+                        pic_out[sLayer]->analysisData.satdCost =
outFrame->m_lowres.satdCost;
+                        pic_out[sLayer]->analysisData.numCUsInFrame =
outFrame->m_analysisData.numCUsInFrame;
+                        pic_out[sLayer]->analysisData.numPartitions =
outFrame->m_analysisData.numPartitions;
+                        pic_out[sLayer]->analysisData.wt =
outFrame->m_analysisData.wt;
+                        pic_out[sLayer]->analysisData.interData =
outFrame->m_analysisData.interData;
+                        pic_out[sLayer]->analysisData.intraData =
outFrame->m_analysisData.intraData;
+                        pic_out[sLayer]->analysisData.distortionData =
outFrame->m_analysisData.distortionData;
+                        pic_out[sLayer]->analysisData.modeFlag[0] =
outFrame->m_analysisData.modeFlag[0];
+                        pic_out[sLayer]->analysisData.modeFlag[1] =
outFrame->m_analysisData.modeFlag[1];
+                        if (m_param->bDisableLookahead)
                         {
-                            int vbvCount = m_param->lookaheadDepth +
m_param->bframes + 2;
-                            for (int index = 0; index < vbvCount; index++)
-                            {
-
 pic_out->analysisData.lookahead.plannedSatd[index] =
outFrame->m_lowres.plannedSatd[index];
-
 pic_out->analysisData.lookahead.plannedType[index] =
outFrame->m_lowres.plannedType[index];
-                            }
-                            for (uint32_t index = 0; index <
pic_out->analysisData.numCuInHeight; index++)
-                            {
-
 outFrame->m_analysisData.lookahead.intraSatdForVbv[index] =
outFrame->m_encData->m_rowStat[index].intraSatdForVbv;
-
 outFrame->m_analysisData.lookahead.satdForVbv[index] =
outFrame->m_encData->m_rowStat[index].satdForVbv;
-                            }
-
 pic_out->analysisData.lookahead.intraSatdForVbv =
outFrame->m_analysisData.lookahead.intraSatdForVbv;
-                            pic_out->analysisData.lookahead.satdForVbv =
outFrame->m_analysisData.lookahead.satdForVbv;
-                            for (uint32_t index = 0; index <
pic_out->analysisData.numCUsInFrame; index++)
+                            int factor = 1;
+                            if (m_param->scaleFactor)
+                                factor = m_param->scaleFactor * 2;
+                            pic_out[sLayer]->analysisData.numCuInHeight =
outFrame->m_analysisData.numCuInHeight;
+                            pic_out[sLayer]->analysisData.lookahead.dts =
outFrame->m_dts;
+
 pic_out[sLayer]->analysisData.lookahead.reorderedPts =
outFrame->m_reorderedPts;
+                            pic_out[sLayer]->analysisData.satdCost *=
factor;
+
 pic_out[sLayer]->analysisData.lookahead.keyframe =
outFrame->m_lowres.bKeyframe;
+
 pic_out[sLayer]->analysisData.lookahead.lastMiniGopBFrame =
outFrame->m_lowres.bLastMiniGopBFrame;
+                            if (m_rateControl->m_isVbv)
                             {
-
 outFrame->m_analysisData.lookahead.intraVbvCost[index] =
outFrame->m_encData->m_cuStat[index].intraVbvCost;
-
 outFrame->m_analysisData.lookahead.vbvCost[index] =
outFrame->m_encData->m_cuStat[index].vbvCost;
+                                int vbvCount = m_param->lookaheadDepth +
m_param->bframes + 2;
+                                for (int index = 0; index < vbvCount;
index++)
+                                {
+
 pic_out[sLayer]->analysisData.lookahead.plannedSatd[index] =
outFrame->m_lowres.plannedSatd[index];
+
 pic_out[sLayer]->analysisData.lookahead.plannedType[index] =
outFrame->m_lowres.plannedType[index];
+                                }
+                                for (uint32_t index = 0; index <
pic_out[sLayer]->analysisData.numCuInHeight; index++)
+                                {
+
 outFrame->m_analysisData.lookahead.intraSatdForVbv[index] =
outFrame->m_encData->m_rowStat[index].intraSatdForVbv;
+
 outFrame->m_analysisData.lookahead.satdForVbv[index] =
outFrame->m_encData->m_rowStat[index].satdForVbv;
+                                }
+
 pic_out[sLayer]->analysisData.lookahead.intraSatdForVbv =
outFrame->m_analysisData.lookahead.intraSatdForVbv;
+
 pic_out[sLayer]->analysisData.lookahead.satdForVbv =
outFrame->m_analysisData.lookahead.satdForVbv;
+                                for (uint32_t index = 0; index <
pic_out[sLayer]->analysisData.numCUsInFrame; index++)
+                                {
+
 outFrame->m_analysisData.lookahead.intraVbvCost[index] =
outFrame->m_encData->m_cuStat[index].intraVbvCost;
+
 outFrame->m_analysisData.lookahead.vbvCost[index] =
outFrame->m_encData->m_cuStat[index].vbvCost;
+                                }
+
 pic_out[sLayer]->analysisData.lookahead.intraVbvCost =
outFrame->m_analysisData.lookahead.intraVbvCost;
+
 pic_out[sLayer]->analysisData.lookahead.vbvCost =
outFrame->m_analysisData.lookahead.vbvCost;
                             }
-                            pic_out->analysisData.lookahead.intraVbvCost =
outFrame->m_analysisData.lookahead.intraVbvCost;
-                            pic_out->analysisData.lookahead.vbvCost =
outFrame->m_analysisData.lookahead.vbvCost;
                         }
+                        writeAnalysisFile(&pic_out[sLayer]->analysisData,
*outFrame->m_encData);
+                        pic_out[sLayer]->analysisData.saveParam =
pic_out[sLayer]->analysisData.saveParam;
+                        if (m_param->bUseAnalysisFile)
+                            x265_free_analysis_data(m_param,
&pic_out[sLayer]->analysisData);
                     }
-                    writeAnalysisFile(&pic_out->analysisData,
*outFrame->m_encData);
-                    pic_out->analysisData.saveParam =
pic_out->analysisData.saveParam;
-                    if (m_param->bUseAnalysisFile)
-                        x265_free_analysis_data(m_param,
&pic_out->analysisData);
-                }
-            }
-            if (m_param->rc.bStatWrite &&
(m_param->analysisMultiPassRefine || m_param->analysisMultiPassDistortion))
-            {
-                if (pic_out)
-                {
-                    pic_out->analysisData.poc = pic_out->poc;
-                    pic_out->analysisData.interData =
outFrame->m_analysisData.interData;
-                    pic_out->analysisData.intraData =
outFrame->m_analysisData.intraData;
-                    pic_out->analysisData.distortionData =
outFrame->m_analysisData.distortionData;
-                }
-                writeAnalysisFileRefine(&outFrame->m_analysisData,
*outFrame->m_encData);
-            }
-            if (m_param->analysisMultiPassRefine ||
m_param->analysisMultiPassDistortion)
-                x265_free_analysis_data(m_param,
&outFrame->m_analysisData);
-            if (m_param->internalCsp == X265_CSP_I400)
-            {
-                if (slice->m_sliceType == P_SLICE)
-                {
-                    if (slice->m_weightPredTable[0][0][0].wtPresent)
-                        m_numLumaWPFrames++;
                 }
-                else if (slice->m_sliceType == B_SLICE)
+                if (m_param->rc.bStatWrite &&
(m_param->analysisMultiPassRefine || m_param->analysisMultiPassDistortion))
                 {
-                    bool bLuma = false;
-                    for (int l = 0; l < 2; l++)
+                    if (pic_out[sLayer])
                     {
-                        if (slice->m_weightPredTable[l][0][0].wtPresent)
-                            bLuma = true;
+                        pic_out[sLayer]->analysisData.poc =
pic_out[sLayer]->poc;
+                        pic_out[sLayer]->analysisData.interData =
outFrame->m_analysisData.interData;
+                        pic_out[sLayer]->analysisData.intraData =
outFrame->m_analysisData.intraData;
+                        pic_out[sLayer]->analysisData.distortionData =
outFrame->m_analysisData.distortionData;
                     }
-                    if (bLuma)
-                        m_numLumaWPBiFrames++;
+                    writeAnalysisFileRefine(&outFrame->m_analysisData,
*outFrame->m_encData);
                 }
-            }
-            else
-            {
-                if (slice->m_sliceType == P_SLICE)
+                if (m_param->analysisMultiPassRefine ||
m_param->analysisMultiPassDistortion)
+                    x265_free_analysis_data(m_param,
&outFrame->m_analysisData);
+                if (m_param->internalCsp == X265_CSP_I400)
                 {
-                    if (slice->m_weightPredTable[0][0][0].wtPresent)
-                        m_numLumaWPFrames++;
-                    if (slice->m_weightPredTable[0][0][1].wtPresent ||
-                        slice->m_weightPredTable[0][0][2].wtPresent)
-                        m_numChromaWPFrames++;
+                    if (slice->m_sliceType == P_SLICE)
+                    {
+                        if (slice->m_weightPredTable[0][0][0].wtPresent)
+                            m_numLumaWPFrames++;
+                    }
+                    else if (slice->m_sliceType == B_SLICE)
+                    {
+                        bool bLuma = false;
+                        for (int l = 0; l < 2; l++)
+                        {
+                            if
(slice->m_weightPredTable[l][0][0].wtPresent)
+                                bLuma = true;
+                        }
+                        if (bLuma)
+                            m_numLumaWPBiFrames++;
+                    }
                 }
-                else if (slice->m_sliceType == B_SLICE)
+                else
                 {
-                    bool bLuma = false, bChroma = false;
-                    for (int l = 0; l < 2; l++)
+                    if (slice->m_sliceType == P_SLICE)
                     {
-                        if (slice->m_weightPredTable[l][0][0].wtPresent)
-                            bLuma = true;
-                        if (slice->m_weightPredTable[l][0][1].wtPresent ||
-                            slice->m_weightPredTable[l][0][2].wtPresent)
-                            bChroma = true;
+                        if (slice->m_weightPredTable[0][0][0].wtPresent)
+                            m_numLumaWPFrames++;
+                        if (slice->m_weightPredTable[0][0][1].wtPresent ||
+                            slice->m_weightPredTable[0][0][2].wtPresent)
+                            m_numChromaWPFrames++;
                     }
+                    else if (slice->m_sliceType == B_SLICE)
+                    {
+                        bool bLuma = false, bChroma = false;
+                        for (int l = 0; l < 2; l++)
+                        {
+                            if
(slice->m_weightPredTable[l][0][0].wtPresent)
+                                bLuma = true;
+                            if
(slice->m_weightPredTable[l][0][1].wtPresent ||
+
 slice->m_weightPredTable[l][0][2].wtPresent)
+                                bChroma = true;
+                        }

-                    if (bLuma)
-                        m_numLumaWPBiFrames++;
-                    if (bChroma)
-                        m_numChromaWPBiFrames++;
+                        if (bLuma)
+                            m_numLumaWPBiFrames++;
+                        if (bChroma)
+                            m_numChromaWPBiFrames++;
+                    }
                 }
-            }
-            if (m_aborted)
-                return -1;
+                if (m_aborted)
+                    return -1;

-            if ((m_outputCount + 1)  >= m_param->chunkStart)
-                finishFrameStats(outFrame, curEncoder, frameData,
m_pocLast);
-            if (m_param->analysisSave)
-            {
-                pic_out->analysisData.frameBits = frameData->bits;
-                if (!slice->isIntra())
+                if ((m_outputCount + 1) >= m_param->chunkStart && !sLayer)
+                    finishFrameStats(outFrame, curEncoder, frameData,
m_pocLast);
+                if (m_param->analysisSave)
                 {
-                    for (int ref = 0; ref < MAX_NUM_REF; ref++)
-                        pic_out->analysisData.list0POC[ref] =
frameData->list0POC[ref];
+                    pic_out[sLayer]->analysisData.frameBits =
frameData->bits;
+                    if (!slice->isIntra())
+                    {
+                        for (int ref = 0; ref < MAX_NUM_REF; ref++)
+                            pic_out[sLayer]->analysisData.list0POC[ref] =
frameData->list0POC[ref];

-                    double totalIntraPercent = 0;
+                        double totalIntraPercent = 0;

-                    for (uint32_t depth = 0; depth < m_param->maxCUDepth;
depth++)
-                        for (uint32_t intramode = 0; intramode < 3;
intramode++)
-                            totalIntraPercent +=
frameData->cuStats.percentIntraDistribution[depth][intramode];
-                    totalIntraPercent +=
frameData->cuStats.percentIntraNxN;
+                        for (uint32_t depth = 0; depth <
m_param->maxCUDepth; depth++)
+                            for (uint32_t intramode = 0; intramode < 3;
intramode++)
+                                totalIntraPercent +=
frameData->cuStats.percentIntraDistribution[depth][intramode];
+                        totalIntraPercent +=
frameData->cuStats.percentIntraNxN;

-                    for (uint32_t depth = 0; depth < m_param->maxCUDepth;
depth++)
-                        totalIntraPercent +=
frameData->puStats.percentIntraPu[depth];
-                    pic_out->analysisData.totalIntraPercent =
totalIntraPercent;
+                        for (uint32_t depth = 0; depth <
m_param->maxCUDepth; depth++)
+                            totalIntraPercent +=
frameData->puStats.percentIntraPu[depth];
+                        pic_out[sLayer]->analysisData.totalIntraPercent =
totalIntraPercent;

-                    if (!slice->isInterP())
-                    {
-                        for (int ref = 0; ref < MAX_NUM_REF; ref++)
-                            pic_out->analysisData.list1POC[ref] =
frameData->list1POC[ref];
+                        if (!slice->isInterP())
+                        {
+                            for (int ref = 0; ref < MAX_NUM_REF; ref++)
+
 pic_out[sLayer]->analysisData.list1POC[ref] = frameData->list1POC[ref];
+                        }
                     }
                 }
-            }
-
-            /* Write RateControl Frame level stats in multipass encodes */
-            if (m_param->rc.bStatWrite)
-                if (m_rateControl->writeRateControlFrameStats(outFrame,
&curEncoder->m_rce))
-                    m_aborted = true;
-            if (pic_out)
-            {
-                /* m_rcData is allocated for every frame */
-                pic_out->rcData = outFrame->m_rcData;
-                outFrame->m_rcData->qpaRc = outFrame->m_encData->m_avgQpRc;
-                outFrame->m_rcData->qRceq = curEncoder->m_rce.qRceq;
-                outFrame->m_rcData->qpNoVbv = curEncoder->m_rce.qpNoVbv;
-                outFrame->m_rcData->coeffBits =
outFrame->m_encData->m_frameStats.coeffBits;
-                outFrame->m_rcData->miscBits =
outFrame->m_encData->m_frameStats.miscBits;
-                outFrame->m_rcData->mvBits =
outFrame->m_encData->m_frameStats.mvBits;
-                outFrame->m_rcData->qScale = outFrame->m_rcData->newQScale
= x265_qp2qScale(outFrame->m_encData->m_avgQpRc);
-                outFrame->m_rcData->poc = curEncoder->m_rce.poc;
-                outFrame->m_rcData->encodeOrder =
curEncoder->m_rce.encodeOrder;
-                outFrame->m_rcData->sliceType =
curEncoder->m_rce.sliceType;
-                outFrame->m_rcData->keptAsRef =
curEncoder->m_rce.sliceType == B_SLICE && !IS_REFERENCED(outFrame) ? 0 : 1;
-                outFrame->m_rcData->qpAq = outFrame->m_encData->m_avgQpAq;
-                outFrame->m_rcData->iCuCount =
outFrame->m_encData->m_frameStats.percent8x8Intra * m_rateControl->m_ncu;
-                outFrame->m_rcData->pCuCount =
outFrame->m_encData->m_frameStats.percent8x8Inter * m_rateControl->m_ncu;
-                outFrame->m_rcData->skipCuCount =
outFrame->m_encData->m_frameStats.percent8x8Skip  * m_rateControl->m_ncu;
-                outFrame->m_rcData->currentSatd =
curEncoder->m_rce.coeffBits;
-            }

-            if (m_param->bEnableTemporalFilter)
-            {
-                Frame *curFrame =
m_origPicBuffer->m_mcstfPicList.getPOCMCSTF(outFrame->m_poc);
-                X265_CHECK(curFrame, "Outframe not found in DPB's
mcstfPicList");
-                curFrame->m_refPicCnt[0]--;
-                curFrame->m_refPicCnt[1]--;
-                curFrame =
m_origPicBuffer->m_mcstfOrigPicList.getPOCMCSTF(outFrame->m_poc);
-                X265_CHECK(curFrame, "Outframe not found in OPB's
mcstfOrigPicList");
-                curFrame->m_refPicCnt[1]--;
-            }
+                /* Write RateControl Frame level stats in multipass
encodes */
+                if (m_param->rc.bStatWrite)
+                    if
(m_rateControl->writeRateControlFrameStats(outFrame, &curEncoder->m_rce))
+                        m_aborted = true;
+                if (pic_out[sLayer])
+                {
+                    /* m_rcData is allocated for every frame */
+                    pic_out[sLayer]->rcData = outFrame->m_rcData;
+                    outFrame->m_rcData->qpaRc =
outFrame->m_encData->m_avgQpRc;
+                    outFrame->m_rcData->qRceq = curEncoder->m_rce.qRceq;
+                    outFrame->m_rcData->qpNoVbv =
curEncoder->m_rce.qpNoVbv;
+                    outFrame->m_rcData->coeffBits =
outFrame->m_encData->m_frameStats.coeffBits;
+                    outFrame->m_rcData->miscBits =
outFrame->m_encData->m_frameStats.miscBits;
+                    outFrame->m_rcData->mvBits =
outFrame->m_encData->m_frameStats.mvBits;
+                    outFrame->m_rcData->qScale =
outFrame->m_rcData->newQScale =
x265_qp2qScale(outFrame->m_encData->m_avgQpRc);
+                    outFrame->m_rcData->poc = curEncoder->m_rce.poc;
+                    outFrame->m_rcData->encodeOrder =
curEncoder->m_rce.encodeOrder;
+                    outFrame->m_rcData->sliceType =
curEncoder->m_rce.sliceType;
+                    outFrame->m_rcData->keptAsRef =
curEncoder->m_rce.sliceType == B_SLICE && !IS_REFERENCED(outFrame) ? 0 : 1;
+                    outFrame->m_rcData->qpAq =
outFrame->m_encData->m_avgQpAq;
+                    outFrame->m_rcData->iCuCount =
outFrame->m_encData->m_frameStats.percent8x8Intra * m_rateControl->m_ncu;
+                    outFrame->m_rcData->pCuCount =
outFrame->m_encData->m_frameStats.percent8x8Inter * m_rateControl->m_ncu;
+                    outFrame->m_rcData->skipCuCount =
outFrame->m_encData->m_frameStats.percent8x8Skip * m_rateControl->m_ncu;
+                    outFrame->m_rcData->currentSatd =
curEncoder->m_rce.coeffBits;
+                }

-            /* Allow this frame to be recycled if no frame encoders are
using it for reference */
-            if (!pic_out)
-            {
-                ATOMIC_DEC(&outFrame->m_countRefEncoders);
-                m_dpb->recycleUnreferenced();
                 if (m_param->bEnableTemporalFilter)
-                    m_origPicBuffer->recycleOrigPicList();
-            }
-            else
-                m_exportedPic = outFrame;
-
-            m_outputCount++;
-            if (m_param->chunkEnd == m_outputCount)
-                m_numDelayedPic = 0;
-            else
-                m_numDelayedPic--;
+                {
+                    Frame* curFrame =
m_origPicBuffer->m_mcstfPicList.getPOCMCSTF(outFrame->m_poc);
+                    X265_CHECK(curFrame, "Outframe not found in DPB's
mcstfPicList");
+                    curFrame->m_refPicCnt[0]--;
+                    curFrame->m_refPicCnt[1]--;
+                    curFrame =
m_origPicBuffer->m_mcstfOrigPicList.getPOCMCSTF(outFrame->m_poc);
+                    X265_CHECK(curFrame, "Outframe not found in OPB's
mcstfOrigPicList");
+                    curFrame->m_refPicCnt[1]--;
+                }
+
+                /* Allow this frame to be recycled if no frame encoders
are using it for reference */
+                if (!pic_out[sLayer])
+                {
+                    ATOMIC_DEC(&outFrame->m_countRefEncoders);
+                    m_dpb->recycleUnreferenced();
+                    if (m_param->bEnableTemporalFilter)
+                        m_origPicBuffer->recycleOrigPicList();
+                }
+                else
+                    m_exportedPic[sLayer] = outFrame;
+
+                m_outputCount++;
+                if (m_param->chunkEnd == m_outputCount)
+                    m_numDelayedPic = 0;
+                else if (outFrame->m_sLayerId == m_param->bEnableAlpha)
+                    m_numDelayedPic--;

-            ret = 1;
+                ret = 1;
+            }
         }

         /* pop a single frame from decided list, then provide to frame
encoder
@@ -2142,9 +2153,12 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
         if (frameEnc[0] && !pass && (!m_param->chunkEnd ||
(m_encodedFrameNum < m_param->chunkEnd)))
         {
             //Pop non base view pictures from DPB piclist
-            frameEnc[1] = m_dpb->m_picList.getPOC(frameEnc[0]->m_poc, 1);
-            m_dpb->m_picList.remove(*frameEnc[1]);
-            frameEnc[1]->m_lowres.sliceType =
frameEnc[0]->m_lowres.sliceType;
+            for (int layer = 1; layer < m_param->numScalableLayers;
layer++)
+            {
+                Frame* currentFrame =
m_dpb->m_picList.getPOC(frameEnc[0]->m_poc, layer);
+                frameEnc[layer] =
m_dpb->m_picList.removeFrame(*currentFrame);
+                frameEnc[layer]->m_lowres.sliceType =
frameEnc[0]->m_lowres.sliceType;
+            }

             if ((m_param->bEnableSceneCutAwareQp & FORWARD) &&
m_param->rc.bStatRead)
             {
@@ -2208,7 +2222,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
             curEncoder->m_reconfigure = m_reconfigure;

             /* give this frame a FrameData instance before encoding */
-            for (int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)
+            for (int layer = 0; layer < m_param->numScalableLayers;
layer++)
             {
                 if (m_dpb->m_frameDataFreeList)
                 {
@@ -2339,7 +2353,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
                 }
             }
             /* determine references, setup RPS, etc */
-            for (int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)
+            for (int layer = 0; layer < m_param->numScalableLayers;
layer++)
                 m_dpb->prepareEncode(frameEnc[layer]);

             if (m_param->bEnableTemporalFilter)
@@ -2361,7 +2375,7 @@ int Encoder::encode(const x265_picture* pic_in,
x265_picture* pic_out)
                 m_origPicBuffer->setOrigPicList(frameEnc[0], m_pocLast);
             }

-            for (int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)
+            for (int layer = 0; layer < m_param->numScalableLayers;
layer++)
             {
                 if (!!m_param->selectiveSAO)
                 {
diff --git a/source/encoder/encoder.h b/source/encoder/encoder.h
index 77e32fe42..22976b180 100644
--- a/source/encoder/encoder.h
+++ b/source/encoder/encoder.h
@@ -202,7 +202,7 @@ public:
     ThreadPool*        m_threadPool;
     FrameEncoder*      m_frameEncoder[X265_MAX_FRAME_THREADS];
     DPB*               m_dpb;
-    Frame*             m_exportedPic;
+    Frame*             m_exportedPic[MAX_SCALABLE_LAYERS];
     FILE*              m_analysisFileIn;
     FILE*              m_analysisFileOut;
     FILE*              m_naluFile;
@@ -300,7 +300,7 @@ public:
     void stopJobs();
     void destroy();

-    int encode(const x265_picture* pic, x265_picture *pic_out);
+    int encode(const x265_picture* pic, x265_picture **pic_out);

     int reconfigureParam(x265_param* encParam, x265_param* param);

diff --git a/source/encoder/frameencoder.cpp
b/source/encoder/frameencoder.cpp
index ebaf3e840..b36094f33 100644
--- a/source/encoder/frameencoder.cpp
+++ b/source/encoder/frameencoder.cpp
@@ -62,6 +62,7 @@ FrameEncoder::FrameEncoder()
     memset(&m_rce, 0, sizeof(RateControlEntry));
     for(int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)
         m_frame[layer] = NULL;
+    m_retFrameBuffer = { NULL };
 }

 void FrameEncoder::destroy()
@@ -95,6 +96,7 @@ void FrameEncoder::destroy()
     X265_FREE(m_ctuGeomMap);
     X265_FREE(m_substreamSizes);
     X265_FREE(m_nr);
+    X265_FREE(m_retFrameBuffer);

     m_frameFilter.destroy();

@@ -217,6 +219,9 @@ bool FrameEncoder::init(Encoder *top, int numRows, int
numCols)
             ok &= !!m_frameEncTF->createRefPicInfo(&m_mcstfRefList[i],
m_param);
     }

+    m_retFrameBuffer = X265_MALLOC(Frame*, m_param->numScalableLayers);
+    for (int layer = 0; layer < m_param->numScalableLayers; layer++)
+        m_retFrameBuffer[layer] = NULL;
     return ok;
 }

@@ -286,7 +291,7 @@ bool FrameEncoder::initializeGeoms()
 bool FrameEncoder::startCompressFrame(Frame* curFrame[MAX_SCALABLE_LAYERS])
 {
     m_slicetypeWaitTime = x265_mdate() - m_prevOutputTime;
-    for (int layer = 0; layer < MAX_SCALABLE_LAYERS; layer++)
+    for (int layer = 0; layer < m_param->numScalableLayers; layer++)
     {
         m_frame[layer] = curFrame[layer];
         curFrame[layer]->m_encData->m_frameEncoderID = m_jpId;
@@ -368,7 +373,7 @@ void FrameEncoder::threadMain()
                 m_frame[0]->m_copyMVType.wait();
         }

-        for(int layer = 0; layer < MAX_SCALABLE_LAYERS; layer ++)
+        for(int layer = 0; layer < m_param->numScalableLayers; layer ++)
             compressFrame(layer);
         m_done.trigger(); /* FrameEncoder::getEncodedPicture() blocks for
this event */
         m_enable.wait();
@@ -604,7 +609,7 @@ void FrameEncoder::compressFrame(int layer)

     /* Get the QP for this frame from rate control. This call may block
until
      * frames ahead of it in encode order have called rateControlEnd() */
-    int qp = (layer == 0) ?
m_top->m_rateControl->rateControlStart(m_frame[layer], &m_rce, m_top) :
m_rce.newQp;
+    int qp = (layer == 0) ?
m_top->m_rateControl->rateControlStart(m_frame[layer], &m_rce, m_top) :
(int)m_rce.newQp;

     m_rce.newQp = qp;

@@ -2267,18 +2272,21 @@ void FrameEncoder::vmafFrameLevelScore()
 }
 #endif

-Frame *FrameEncoder::getEncodedPicture(NALList& output)
+Frame** FrameEncoder::getEncodedPicture(NALList& output)
 {
-    if (m_frame[0])
+    if (m_frame && m_frame[0])
     {
         /* block here until worker thread completes */
         m_done.wait();

-        Frame *ret = m_frame[0];
-        m_frame[0] = NULL;
+        for (int i = 0; i < m_param->numScalableLayers; i++)
+        {
+            m_retFrameBuffer[i] = m_frame[i];
+            m_frame[i] = NULL;
+        }
         output.takeContents(m_nalList);
         m_prevOutputTime = x265_mdate();
-        return ret;
+        return m_retFrameBuffer;
     }

     return NULL;
diff --git a/source/encoder/frameencoder.h b/source/encoder/frameencoder.h
index fa4bad60f..9fcd2dcf5 100644
--- a/source/encoder/frameencoder.h
+++ b/source/encoder/frameencoder.h
@@ -159,7 +159,7 @@ public:
     bool startCompressFrame(Frame* curFrame[MAX_SCALABLE_LAYERS]);

     /* blocks until worker thread is done, returns access unit */
-    Frame *getEncodedPicture(NALList& list);
+    Frame **getEncodedPicture(NALList& list);

     void initDecodedPictureHashSEI(int row, int cuAddr, int height, int
layer);

@@ -218,6 +218,7 @@ public:
     Encoder*                 m_top;
     x265_param*              m_param;
     Frame*                   m_frame[MAX_SCALABLE_LAYERS];
+    Frame**                  m_retFrameBuffer;
     NoiseReduction*          m_nr;
     ThreadLocalData*         m_tld; /* for --no-wpp */
     Bitstream*               m_outStreams;
diff --git a/source/x265.h b/source/x265.h
index e994ea3e2..7c1acfea3 100644
--- a/source/x265.h
+++ b/source/x265.h
@@ -2441,7 +2441,7 @@ int x265_encoder_headers(x265_encoder *, x265_nal
**pp_nal, uint32_t *pi_nal);
  *      the payloads of all output NALs are guaranteed to be sequential in
memory.
  *      To flush the encoder and retrieve delayed output pictures, pass
pic_in as NULL.
  *      Once flushing has begun, all subsequent calls must pass pic_in as
NULL. */
-int x265_encoder_encode(x265_encoder *encoder, x265_nal **pp_nal, uint32_t
*pi_nal, x265_picture *pic_in, x265_picture *pic_out);
+int x265_encoder_encode(x265_encoder *encoder, x265_nal **pp_nal, uint32_t
*pi_nal, x265_picture *pic_in, x265_picture **pic_out);

 /* x265_encoder_reconfig:
  *      various parameters from x265_param are copied.
@@ -2595,7 +2595,7 @@ typedef struct x265_api
     int           (*encoder_reconfig)(x265_encoder*, x265_param*);
     int           (*encoder_reconfig_zone)(x265_encoder*, x265_zone*);
     int           (*encoder_headers)(x265_encoder*, x265_nal**, uint32_t*);
-    int           (*encoder_encode)(x265_encoder*, x265_nal**, uint32_t*,
x265_picture*, x265_picture*);
+    int           (*encoder_encode)(x265_encoder*, x265_nal**, uint32_t*,
x265_picture*, x265_picture**);
     void          (*encoder_get_stats)(x265_encoder*, x265_stats*,
uint32_t);
     void          (*encoder_log)(x265_encoder*, int, char**);
     void          (*encoder_close)(x265_encoder*);
diff --git a/source/x265cli.cpp b/source/x265cli.cpp
index 070011cf0..6bb81f6d4 100755
--- a/source/x265cli.cpp
+++ b/source/x265cli.cpp
@@ -422,9 +422,12 @@ namespace X265_NS {
         if (input)
             input->release();
         input = NULL;
-        if (recon)
-            recon->release();
-        recon = NULL;
+        for (int i = 0; i < param->numScalableLayers; i++)
+        {
+            if (recon[i])
+                recon[i]->release();
+            recon[i] = NULL;
+        }
         if (qpfile)
             fclose(qpfile);
         qpfile = NULL;
@@ -581,7 +584,7 @@ namespace X265_NS {
         int outputBitDepth = 0;
         int reconFileBitDepth = 0;
         const char *inputfn = NULL;
-        const char *reconfn = NULL;
+        const char* reconfn[MAX_SCALABLE_LAYERS] = { NULL };
         const char *outputfn = NULL;
         const char *preset = NULL;
         const char *tune = NULL;
@@ -721,7 +724,7 @@ namespace X265_NS {
                 OPT("no-progress") this->bProgress = false;
                 OPT("output") outputfn = optarg;
                 OPT("input") inputfn = optarg;
-                OPT("recon") reconfn = optarg;
+                OPT("recon") reconfn[0] = optarg;
                 OPT("input-depth") inputBitDepth =
(uint32_t)x265_atoi(optarg, bError);
                 OPT("dither") this->bDither = true;
                 OPT("recon-depth") reconFileBitDepth =
(uint32_t)x265_atoi(optarg, bError);
@@ -915,23 +918,40 @@ namespace X265_NS {

         this->input->startReader();

-        if (reconfn)
+        if (reconfn[0])
         {
             if (reconFileBitDepth == 0)
                 reconFileBitDepth = param->internalBitDepth;
-            this->recon = ReconFile::open(reconfn, param->sourceWidth,
param->sourceHeight, reconFileBitDepth,
-                param->fpsNum, param->fpsDenom, param->internalCsp,
param->sourceBitDepth);
-            if (this->recon->isFail())
+#if ENABLE_ALPHA
+            if (param->bEnableAlpha)
             {
-                x265_log(param, X265_LOG_WARNING, "unable to write
reconstructed outputs file\n");
-                this->recon->release();
-                this->recon = 0;
+                char* temp = new char[strlen(reconfn[0])];
+                strcpy(temp, reconfn[0]);
+                const char* token = strtok(temp, ".");
+                for (int view = 0; view < param->numScalableLayers; view++)
+                {
+                    char* buf = new char[strlen(temp) + 7];
+                    sprintf(buf, "%s-%d.yuv", token, view);
+                    reconfn[view] = buf;
+                }
+            }
+#endif
+            for (int i = 0; i < param->numScalableLayers; i++)
+            {
+                this->recon[i] = ReconFile::open(reconfn[i],
param->sourceWidth, param->sourceHeight, reconFileBitDepth,
+                    param->fpsNum, param->fpsDenom, param->internalCsp,
param->sourceBitDepth);
+                if (this->recon[i]->isFail())
+                {
+                    x265_log(param, X265_LOG_WARNING, "unable to write
reconstructed outputs file\n");
+                    this->recon[i]->release();
+                    this->recon[i] = 0;
+                }
+                else
+                    general_log(param, this->recon[i]->getName(),
X265_LOG_INFO,
+                        "reconstructed images %dx%d fps %d/%d %s\n",
+                        param->sourceWidth, param->sourceHeight,
param->fpsNum, param->fpsDenom,
+                        x265_source_csp_names[param->internalCsp]);
             }
-            else
-                general_log(param, this->recon->getName(), X265_LOG_INFO,
-                "reconstructed images %dx%d fps %d/%d %s\n",
-                param->sourceWidth, param->sourceHeight, param->fpsNum,
param->fpsDenom,
-                x265_source_csp_names[param->internalCsp]);
         }
 #if ENABLE_LIBVMAF
         if (!reconfn)
diff --git a/source/x265cli.h b/source/x265cli.h
index 9bdaba0f3..7abf91eb8 100644
--- a/source/x265cli.h
+++ b/source/x265cli.h
@@ -397,7 +397,7 @@ static const struct option long_options[] =
     struct CLIOptions
     {
         InputFile* input;
-        ReconFile* recon;
+        ReconFile* recon[MAX_SCALABLE_LAYERS];
         OutputFile* output;
         FILE*       qpfile;
         FILE*       zoneFile;
@@ -435,7 +435,8 @@ static const struct option long_options[] =
         CLIOptions()
         {
             input = NULL;
-            recon = NULL;
+            for (int i = 0; i < MAX_SCALABLE_LAYERS; i++)
+                recon[i] = NULL;
             output = NULL;
             qpfile = NULL;
             zoneFile = NULL;
-- 
2.36.0.windows.1
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20240805/0e4be793/attachment-0001.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0007-Add-support-for-reconstructed-file-corresponding-to-.patch
Type: application/octet-stream
Size: 51526 bytes
Desc: not available
URL: <http://mailman.videolan.org/pipermail/x265-devel/attachments/20240805/0e4be793/attachment-0001.obj>


More information about the x265-devel mailing list