Index: trunk/source/App/TAppDecoder/TAppDecCfg.cpp
===================================================================
--- trunk/source/App/TAppDecoder/TAppDecCfg.cpp	(revision 1356)
+++ trunk/source/App/TAppDecoder/TAppDecCfg.cpp	(revision 1386)
@@ -62,9 +62,7 @@
     \param argv array of arguments
  */
-Bool TAppDecCfg::parseCfg( Int argc, Char* argv[] )
+Bool TAppDecCfg::parseCfg( Int argc, TChar* argv[] )
 {
   Bool do_help = false;
-  string cfg_BitstreamFile;
-  string cfg_ReconFile;
   string cfg_TargetDecLayerIdSetFile;
 #if NH_3D
@@ -79,6 +77,6 @@
 
   ("help",                      do_help,                               false,      "this help text")
-  ("BitstreamFile,b",           cfg_BitstreamFile,                     string(""), "bitstream input file name")
-  ("ReconFile,o",               cfg_ReconFile,                         string(""), "reconstructed YUV output file name\n"
+  ("BitstreamFile,b",           m_bitstreamFileName,                   string(""), "bitstream input file name")
+  ("ReconFile,o",               m_reconFileName,                       string(""), "reconstructed YUV output file name\n"
                                                                                    "YUV writing is skipped if omitted")
 #if NH_3D
@@ -92,5 +90,5 @@
   ("OutputColourSpaceConvert",  outputColourSpaceConvert,              string(""), "Colour space conversion to apply to input 444 video. Permitted values are (empty string=UNCHANGED) " + getListOfColourSpaceConverts(false))
 #if NH_MV
-  ("TargetOptLayerSetIdx,x",    m_targetOptLayerSetIdx,                -1,         "Target output layer set index. (default: -1, determine automatically to be equal to highest layer set index") // Should actually equal to 0 as default. However, this would cause only the base layer to be decoded.  
+  ("TargetOptLayerSetIdx,x", m_targetOptLayerSetInd, std::vector<Int>(1,-1), "Target output layer set index. (default: -1, determine automatically to be equal to highest layer set index") // Should actually equal to 0 as default. However, this would cause only the base layer to be decoded.  
 #endif
   ("MaxTemporalLayer,t",        m_iMaxTemporalLayer,                   -1,         "Maximum Temporal Layer to be decoded. -1 to decode all layers")
@@ -106,8 +104,10 @@
   ("PrintNalus,n",              m_printReceivedNalus,                 false,        "Print information on received NAL units")
 #endif
+  ("SEIColourRemappingInfoFilename",  m_colourRemapSEIFileName,        string(""), "Colour Remapping YUV output file name. If empty, no remapping is applied (ignore SEI message)\n")
+
 #if O0043_BEST_EFFORT_DECODING
   ("ForceDecodeBitDepth",       m_forceDecodeBitDepth,                 0U,         "Force the decoder to operate at a particular bit-depth (best effort decoding)")
 #endif
-#if NH_MV_SEI
+#if NH_MV
   ("OutputDecodedSEIMessagesFilename,m",  m_outputDecodedSEIMessagesFilename,    string(""), "When non empty, output decoded SEI messages to the indicated file. If file is '-', then output to stdout\n")
 #else
@@ -119,7 +119,7 @@
   po::setDefaults(opts);
   po::ErrorReporter err;
-  const list<const Char*>& argv_unhandled = po::scanArgv(opts, argc, (const Char**) argv, err);
-
-  for (list<const Char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++)
+  const list<const TChar*>& argv_unhandled = po::scanArgv(opts, argc, (const TChar**) argv, err);
+
+  for (list<const TChar*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++)
   {
     fprintf(stderr, "Unhandled argument ignored: `%s'\n", *it);
@@ -148,13 +148,9 @@
   }
 
-  /* convert std::string to c string for compatability */
-  m_pchBitstreamFile = cfg_BitstreamFile.empty() ? NULL : strdup(cfg_BitstreamFile.c_str());
-  m_pchReconFile = cfg_ReconFile.empty() ? NULL : strdup(cfg_ReconFile.c_str());
-
 #if NH_3D
   m_pchScaleOffsetFile = cfg_ScaleOffsetFile.empty() ? NULL : strdup(cfg_ScaleOffsetFile.c_str());
 #endif
 
-  if (!m_pchBitstreamFile)
+  if (m_bitstreamFileName.empty())
   {
     fprintf(stderr, "No input file specified, aborting\n");
@@ -217,11 +213,11 @@
 
 #if NH_MV
-Void TAppDecCfg::xAppendToFileNameEnd( Char* pchInputFileName, const Char* pchStringToAppend, Char*& rpchOutputFileName)
+Void TAppDecCfg::xAppendToFileNameEnd( const TChar* pchInputFileName, const TChar* pchStringToAppend, TChar*& rpchOutputFileName)
 {
   size_t iInLength     = strlen(pchInputFileName);
   size_t iAppendLength = strlen(pchStringToAppend); 
 
-  rpchOutputFileName = (Char*) malloc(iInLength+iAppendLength+1);                        
-  Char* pCDot = strrchr(pchInputFileName,'.');         
+  rpchOutputFileName = (TChar*) malloc(iInLength+iAppendLength+1);                        
+  const TChar* pCDot = strrchr(pchInputFileName,'.');         
   pCDot = pCDot ? pCDot : pchInputFileName + iInLength;        
   size_t iCharsToDot = pCDot - pchInputFileName ; 
Index: trunk/source/App/TAppDecoder/TAppDecCfg.h
===================================================================
--- trunk/source/App/TAppDecoder/TAppDecCfg.h	(revision 1356)
+++ trunk/source/App/TAppDecoder/TAppDecCfg.h	(revision 1386)
@@ -60,6 +60,6 @@
 {
 protected:
-  Char*         m_pchBitstreamFile;                     ///< input bitstream file name
-  Char*         m_pchReconFile;                         ///< output reconstruction file name
+  std::string   m_bitstreamFileName;                    ///< input bitstream file name
+  std::string   m_reconFileName;                        ///< output reconstruction file name
   Int           m_iSkipFrame;                           ///< counter for frames prior to the random access point to skip
   Int           m_outputBitDepth[MAX_NUM_CHANNEL_TYPE]; ///< bit depth used for writing output
@@ -69,4 +69,5 @@
   Int           m_decodedPictureHashSEIEnabled;       ///< Checksum(3)/CRC(2)/MD5(1)/disable(0) acting on decoded picture hash SEI message
   Bool          m_decodedNoDisplaySEIEnabled;         ///< Enable(true)/disable(false) writing only pictures that get displayed based on the no display SEI message
+  std::string   m_colourRemapSEIFileName;             ///< output Colour Remapping file name
   std::vector<Int> m_targetDecLayerIdSet;             ///< set of LayerIds to be included in the sub-bitstream extraction process.
 
@@ -78,40 +79,47 @@
   Bool          m_bClipOutputVideoToRec709Range;      ///< If true, clip the output video to the Rec 709 range on saving.
 #if NH_MV
-  std::vector<Char*> m_pchReconFiles;                   ///< array of output reconstruction file name create from output reconstruction file name
+  std::vector<TChar*> m_pchReconFiles;                ///< array of output reconstruction file name create from output reconstruction file name
 
-  Int           m_targetOptLayerSetIdx;                 ///< target output layer set index
+  std::vector<Int> m_targetOptLayerSetInd;            ///< target output layer set indices (multiple decoding when more than one element, e.g. for additional layer sets)
+  Int           m_targetOptLayerSetIdx;               ///< current target output layer set index
+
   Int           m_targetDecLayerSetIdx;
   Int           m_baseLayerOutputFlag;
   Int           m_baseLayerPicOutputFlag;
   Int           m_auOutputFlag;
-  Int           m_maxLayerId;                           ///< maximum nuh_layer_id decoded
+  Int           m_maxLayerId;                         ///< maximum nuh_layer_id decoded
   std::ifstream m_bitstreamFile;
   Int           m_highestTid;
   Bool          m_targetDecLayerIdSetFileEmpty;       ///< indication if target layers are given by file
 
-  Bool          m_printVpsInfo;                      ///< Output VPS information
+  Bool          m_printVpsInfo;                       ///< Output VPS information
   Bool          m_printPicOutput;                     ///< Print information on picture output
   Bool          m_printReceivedNalus;                 ///< Print information on received NAL units
 #if NH_3D
-  Char*         m_pchScaleOffsetFile;                   ///< output coded scale and offset parameters
+  TChar*        m_pchScaleOffsetFile;                   ///< output coded scale and offset parameters
   Bool          m_depth420OutputFlag;                   ///< output depth layers in 4:2:0
 #endif
 
-  Void xAppendToFileNameEnd( Char* pchInputFileName, const Char* pchStringToAppend, Char*& rpchOutputFileName); ///< create filenames
+  Void xAppendToFileNameEnd( const TChar* pchInputFileName, const TChar* pchStringToAppend, TChar*& rpchOutputFileName); ///< create filenames
 #endif
 
 public:
   TAppDecCfg()
-  : m_pchBitstreamFile(NULL)
-  , m_pchReconFile(NULL)
+  : m_bitstreamFileName()
+  , m_reconFileName()
   , m_iSkipFrame(0)
+  // m_outputBitDepth array initialised below
   , m_outputColourSpaceConvert(IPCOLOURSPACE_UNCHANGED)
   , m_iMaxTemporalLayer(-1)
   , m_decodedPictureHashSEIEnabled(0)
   , m_decodedNoDisplaySEIEnabled(false)
+  , m_colourRemapSEIFileName()
+  , m_targetDecLayerIdSet()
   , m_respectDefDispWindow(0)
 #if O0043_BEST_EFFORT_DECODING
   , m_forceDecodeBitDepth(0)
 #endif
+  , m_outputDecodedSEIMessagesFilename()
+  , m_bClipOutputVideoToRec709Range(false)
 #if NH_MV
   , m_highestTid(-1)
@@ -131,5 +139,8 @@
   virtual ~TAppDecCfg() {}
 
-  Bool  parseCfg        ( Int argc, Char* argv[] );   ///< initialize option class from configuration
+  Bool  parseCfg        ( Int argc, TChar* argv[] );   ///< initialize option class from configuration
+#if NH_MV
+  Int   getNumDecodings ( ) { return (Int) m_targetOptLayerSetInd.size();  }; 
+#endif
 };
 
Index: trunk/source/App/TAppDecoder/TAppDecTop.cpp
===================================================================
--- trunk/source/App/TAppDecoder/TAppDecTop.cpp	(revision 1356)
+++ trunk/source/App/TAppDecoder/TAppDecTop.cpp	(revision 1386)
@@ -62,4 +62,5 @@
 : m_numDecoders( 0 )
 #endif
+ ,m_pcSeiColourRemappingInfoPrevious(NULL)
 {
 #if NH_MV
@@ -118,10 +119,5 @@
   xDestroyDecLib();
 #endif
-
-  if (m_pchBitstreamFile)
-  {
-    free (m_pchBitstreamFile);
-    m_pchBitstreamFile = NULL;
-  }
+  m_bitstreamFileName.clear();
 #if NH_MV
   for (Int decIdx = 0; decIdx < m_numDecoders; decIdx++)
@@ -134,9 +130,5 @@
   }
 #endif
-  if (m_pchReconFile)
-  {
-    free (m_pchReconFile);
-    m_pchReconFile = NULL;
-  }
+  m_reconFileName.clear();
 #if NH_3D
   if (m_pchScaleOffsetFile)
@@ -160,8 +152,8 @@
  .
  */
-
 #if NH_MV
-Void TAppDecTop::decode()
-{
+Void TAppDecTop::decode( Int num )
+{
+  m_targetOptLayerSetIdx = m_targetOptLayerSetInd[ num ]; 
   // create & initialize internal classes
   xInitFileIO  ();
@@ -221,13 +213,13 @@
 
 #if !NH_MV
-Void TAppDecTop::decode()
+Void TAppDecTop::decode( )
 {
   Int                 poc;
   TComList<TComPic*>* pcListPic = NULL;
 
-  ifstream bitstreamFile(m_pchBitstreamFile, ifstream::in | ifstream::binary);
+  ifstream bitstreamFile(m_bitstreamFileName.c_str(), ifstream::in | ifstream::binary);
   if (!bitstreamFile)
   {
-    fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_pchBitstreamFile);
+    fprintf(stderr, "\nfailed to open bitstream file `%s' for reading\n", m_bitstreamFileName.c_str());
     exit(EXIT_FAILURE);
   }
@@ -250,4 +242,15 @@
 
   m_iPOCLastDisplay += m_iSkipFrame;      // set the last displayed POC correctly for skip forward.
+
+  // clear contents of colour-remap-information-SEI output file
+  if (!m_colourRemapSEIFileName.empty())
+  {
+    std::ofstream ofile(m_colourRemapSEIFileName.c_str());
+    if (!ofile.good() || !ofile.is_open())
+    {
+      fprintf(stderr, "\nUnable to open file '%s' for writing colour-remap-information-SEI video\n", m_colourRemapSEIFileName.c_str());
+      exit(EXIT_FAILURE);
+    }
+  }
 
   // main decoder loop
@@ -336,5 +339,5 @@
     if( pcListPic )
     {
-      if ( m_pchReconFile && !openedReconFile )
+      if ( (!m_reconFileName.empty()) && (!openedReconFile) )
       {
         const BitDepths &bitDepths=pcListPic->front()->getPicSym()->getSPS().getBitDepths(); // use bit depths of first reconstructed picture.
@@ -346,5 +349,6 @@
           }
         }
-        m_cTVideoIOYuvReconFile.open( m_pchReconFile, true, m_outputBitDepth, m_outputBitDepth, bitDepths.recon ); // write mode
+
+        m_cTVideoIOYuvReconFile.open( m_reconFileName, true, m_outputBitDepth, m_outputBitDepth, bitDepths.recon ); // write mode
         openedReconFile = true;
       }
@@ -436,5 +440,5 @@
   }
 #else
-  if ( m_pchReconFile )
+  if ( !m_reconFileName.empty() )
   {
     m_cTVideoIOYuvReconFile. close();
@@ -444,4 +448,9 @@
   m_cTDecTop.destroy();
 #endif
+  if (m_pcSeiColourRemappingInfoPrevious != NULL)
+  {
+    delete m_pcSeiColourRemappingInfoPrevious;
+    m_pcSeiColourRemappingInfoPrevious = NULL;
+  }
 #if NH_3D
   m_cCamParsCollector.uninit();
@@ -474,4 +483,9 @@
   }
 #endif
+  if (m_pcSeiColourRemappingInfoPrevious != NULL)
+  {
+    delete m_pcSeiColourRemappingInfoPrevious;
+    m_pcSeiColourRemappingInfoPrevious = NULL;
+  }
 }
 
@@ -549,5 +563,5 @@
         // write to file
         numPicsNotYetDisplayed = numPicsNotYetDisplayed-2;
-        if ( m_pchReconFile )
+        if ( !m_reconFileName.empty() )
         {
           const Window &conf = pcPicTop->getConformanceWindow();
@@ -617,5 +631,5 @@
           dpbFullness--;
         }
-        if ( m_pchReconFile )
+        if ( !m_reconFileName.empty() )
         {
           const Window &conf    = pcPic->getConformanceWindow();
@@ -631,4 +645,9 @@
         }
 
+        if (!m_colourRemapSEIFileName.empty())
+        {
+          xOutputColourRemapPic(pcPic);
+        }
+
         // update POC of display order
         m_iPOCLastDisplay = pcPic->getPOC();
@@ -678,5 +697,5 @@
         // write to file
 
-        if ( m_pchReconFile )
+        if ( !m_reconFileName.empty() )
         {
           const Window &conf = pcPicTop->getConformanceWindow();
@@ -736,5 +755,5 @@
       {
         // write to file
-        if ( m_pchReconFile )
+        if ( !m_reconFileName.empty() )
         {
           const Window &conf    = pcPic->getConformanceWindow();
@@ -749,4 +768,9 @@
         }
 
+        if (!m_colourRemapSEIFileName.empty())
+        {
+          xOutputColourRemapPic(pcPic);
+        }
+
         // update POC of display order
         m_iPOCLastDisplay = pcPic->getPOC();
@@ -1068,5 +1092,5 @@
     m_newVpsActivatedbyCurAu  = true; //TBD
     m_newVpsActivatedbyCurPic = true;
-#if NH_3D
+#if NH_3D_VSO
     m_dpb.setVPS( m_vps ); 
 #endif
@@ -1232,5 +1256,5 @@
   }
 
-  if ( m_printVpsInfo  && ( m_decProcCvsg == ANNEX_F ) )
+  if ( m_printVpsInfo  && ( m_decProcCvsg == ANNEX_F ) && ( m_targetOptLayerSetIdx == m_targetOptLayerSetInd[ 0 ] ) )
   {
     m_vps->printScalabilityId();
@@ -1979,5 +2003,5 @@
       // - After all slices of the current picture have been decoded, the decoding process for ending the decoding of a
       //   coded picture with nuh_layer_id greater than 0 specified in clause F.8.1.6 is invoked.
-      xF816decProcEndDecOfCodPicLIdGrtZero( );
+      xF816decProcEndDecOfCodPicLIdGrtZero( );      
     }
 
@@ -2671,11 +2695,11 @@
 
     // create recon file related stuff
-    Char* pchTempFilename = NULL;
-    if ( m_pchReconFile )
-    {
-      Char buffer[4];
+    TChar* pchTempFilename = NULL;
+    if ( !m_reconFileName.empty() )
+    {
+      TChar buffer[4];
       sprintf(buffer,"_%i", layerId );
-      assert ( m_pchReconFile );
-      xAppendToFileNameEnd( m_pchReconFile , buffer, pchTempFilename );
+      assert ( !m_reconFileName.empty() );
+      xAppendToFileNameEnd( m_reconFileName.c_str() , buffer, pchTempFilename );
       assert( m_pchReconFiles.size() == m_numDecoders );
     }
@@ -2870,9 +2894,9 @@
 Void TAppDecTop::xInitFileIO()
 {
-  m_bitstreamFile.open(m_pchBitstreamFile, ifstream::in | ifstream::binary);
+  m_bitstreamFile.open(m_bitstreamFileName.c_str(), ifstream::in | ifstream::binary);
 
   if ( !m_bitstreamFile)
   {
-    fprintf(stderr, "\nUnable to open bitstream file `%s' for reading\n", m_pchBitstreamFile);
+    fprintf(stderr, "\nUnable to open bitstream file `%s' for reading\n", m_bitstreamFileName.c_str());
     exit(EXIT_FAILURE);
   }
@@ -2905,5 +2929,5 @@
   Int decIdx = xGetDecoderIdx( curPic->getLayerId() );
 
-  if ( m_pchReconFile && !m_reconOpen[decIdx] )
+  if ( !m_reconFileName.empty() && !m_reconOpen[decIdx] )
   {
     const BitDepths &bitDepths= curPic->getPicSym()->getSPS().getBitDepths(); // use bit depths of first reconstructed picture.
@@ -3007,3 +3031,324 @@
 
 #endif
+
+Void TAppDecTop::xOutputColourRemapPic(TComPic* pcPic)
+{
+  const TComSPS &sps=pcPic->getPicSym()->getSPS();
+  SEIMessages colourRemappingInfo = getSeisByType(pcPic->getSEIs(), SEI::COLOUR_REMAPPING_INFO );
+  SEIColourRemappingInfo *seiColourRemappingInfo = ( colourRemappingInfo.size() > 0 ) ? (SEIColourRemappingInfo*) *(colourRemappingInfo.begin()) : NULL;
+
+  if (colourRemappingInfo.size() > 1)
+  {
+    printf ("Warning: Got multiple Colour Remapping Information SEI messages. Using first.");
+  }
+  if (seiColourRemappingInfo)
+  {
+    applyColourRemapping(*pcPic->getPicYuvRec(), *seiColourRemappingInfo, sps);
+
+    // save the last CRI SEI received
+    if (m_pcSeiColourRemappingInfoPrevious == NULL)
+    {
+      m_pcSeiColourRemappingInfoPrevious = new SEIColourRemappingInfo();
+    }
+    m_pcSeiColourRemappingInfoPrevious->copyFrom(*seiColourRemappingInfo);
+  }
+  else  // using the last CRI SEI received
+  {
+    // TODO: prevent persistence of CRI SEI across C(L)VS.
+    if (m_pcSeiColourRemappingInfoPrevious != NULL)
+    {
+      if (m_pcSeiColourRemappingInfoPrevious->m_colourRemapPersistenceFlag == false)
+      {
+        printf("Warning No SEI-CRI message is present for the current picture, persistence of the CRI is not managed\n");
+      }
+      applyColourRemapping(*pcPic->getPicYuvRec(), *m_pcSeiColourRemappingInfoPrevious, sps);
+    }
+  }
+}
+
+// compute lut from SEI
+// use at lutPoints points aligned on a power of 2 value
+// SEI Lut must be in ascending values of coded Values
+static std::vector<Int>
+initColourRemappingInfoLut(const Int                                          bitDepth_in,     // bit-depth of the input values of the LUT
+                           const Int                                          nbDecimalValues, // Position of the fixed point
+                           const std::vector<SEIColourRemappingInfo::CRIlut> &lut,
+                           const Int                                          maxValue, // maximum output value
+                           const Int                                          lutOffset)
+{
+  const Int lutPoints = (1 << bitDepth_in) + 1 ;
+  std::vector<Int> retLut(lutPoints);
+
+  // missing values: need to define default values before first definition (check codedValue[0] == 0)
+  Int iTargetPrev = (lut.size() && lut[0].codedValue == 0) ? lut[0].targetValue: 0;
+  Int startPivot = (lut.size())? ((lut[0].codedValue == 0)? 1: 0): 1;
+  Int iCodedPrev  = 0;
+  // set max value with the coded bit-depth
+  // + ((1 << nbDecimalValues) - 1) is for the added bits
+  const Int maxValueFixedPoint = (maxValue << nbDecimalValues) + ((1 << nbDecimalValues) - 1);
+
+  Int iValue = 0;
+
+  for ( Int iPivot=startPivot ; iPivot < (Int)lut.size(); iPivot++ )
+  {
+    Int iCodedNext  = lut[iPivot].codedValue;
+    Int iTargetNext = lut[iPivot].targetValue;
+
+    // ensure correct bit depth and avoid overflow in lut address
+    Int iCodedNext_bitDepth = std::min(iCodedNext, (1 << bitDepth_in));
+
+    const Int divValue =  (iCodedNext - iCodedPrev > 0)? (iCodedNext - iCodedPrev): 1;
+    const Int lutValInit = (lutOffset + iTargetPrev) << nbDecimalValues;
+    const Int roundValue = divValue / 2;
+    for ( ; iValue<iCodedNext_bitDepth; iValue++ )
+    {
+      Int value = iValue;
+      Int interpol = ((((value-iCodedPrev) * (iTargetNext - iTargetPrev)) << nbDecimalValues) + roundValue) / divValue;               
+      retLut[iValue]  = std::min(lutValInit + interpol , maxValueFixedPoint);
+    }
+    iCodedPrev  = iCodedNext;
+    iTargetPrev = iTargetNext;
+  }
+  // fill missing values if necessary
+  if(iCodedPrev < (1 << bitDepth_in)+1)
+  {
+    Int iCodedNext  = (1 << bitDepth_in);
+    Int iTargetNext = (1 << bitDepth_in) - 1;
+
+    const Int divValue =  (iCodedNext - iCodedPrev > 0)? (iCodedNext - iCodedPrev): 1;
+    const Int lutValInit = (lutOffset + iTargetPrev) << nbDecimalValues;
+    const Int roundValue = divValue / 2;
+
+    for ( ; iValue<=iCodedNext; iValue++ )
+    {
+      Int value = iValue;
+      Int interpol = ((((value-iCodedPrev) * (iTargetNext - iTargetPrev)) << nbDecimalValues) + roundValue) / divValue; 
+      retLut[iValue]  = std::min(lutValInit + interpol , maxValueFixedPoint);
+    }
+  }
+  return retLut;
+}
+
+static Void
+initColourRemappingInfoLuts(std::vector<Int>      (&preLut)[3],
+                            std::vector<Int>      (&postLut)[3],
+                            SEIColourRemappingInfo &pCriSEI,
+                            const Int               maxBitDepth)
+{
+  Int internalBitDepth = pCriSEI.m_colourRemapBitDepth;
+  for ( Int c=0 ; c<3 ; c++ )
+  {
+    std::sort(pCriSEI.m_preLut[c].begin(), pCriSEI.m_preLut[c].end()); // ensure preLut is ordered in ascending values of codedValues   
+    preLut[c] = initColourRemappingInfoLut(pCriSEI.m_colourRemapInputBitDepth, maxBitDepth - pCriSEI.m_colourRemapInputBitDepth, pCriSEI.m_preLut[c], ((1 << internalBitDepth) - 1), 0); //Fill preLut
+
+    std::sort(pCriSEI.m_postLut[c].begin(), pCriSEI.m_postLut[c].end()); // ensure postLut is ordered in ascending values of codedValues       
+    postLut[c] = initColourRemappingInfoLut(pCriSEI.m_colourRemapBitDepth, maxBitDepth - pCriSEI.m_colourRemapBitDepth, pCriSEI.m_postLut[c], (1 << internalBitDepth) - 1, 0); //Fill postLut
+  }
+}
+
+// apply lut.
+// Input lut values are aligned on power of 2 boundaries
+static Int
+applyColourRemappingInfoLut1D(Int inVal, const std::vector<Int> &lut, const Int inValPrecisionBits)
+{
+  const Int roundValue = (inValPrecisionBits)? 1 << (inValPrecisionBits - 1): 0;
+  inVal = std::min(std::max(0, inVal), (Int)(((lut.size()-1) << inValPrecisionBits)));
+  Int index  = (Int) std::min((inVal >> inValPrecisionBits), (Int)(lut.size()-2));
+  Int outVal = (( inVal - (index<<inValPrecisionBits) ) * (lut[index+1] - lut[index]) + roundValue) >> inValPrecisionBits;
+  outVal +=  lut[index] ;
+
+  return outVal;
+}  
+
+static Int
+applyColourRemappingInfoMatrix(const Int (&colourRemapCoeffs)[3], const Int postOffsetShift, const Int p0, const Int p1, const Int p2, const Int offset)
+{
+  Int YUVMat = (colourRemapCoeffs[0]* p0 + colourRemapCoeffs[1]* p1 + colourRemapCoeffs[2]* p2  + offset) >> postOffsetShift;
+  return YUVMat;
+}
+
+static Void
+setColourRemappingInfoMatrixOffset(Int (&matrixOffset)[3], Int offset0, Int offset1, Int offset2)
+{
+  matrixOffset[0] = offset0;
+  matrixOffset[1] = offset1;
+  matrixOffset[2] = offset2;
+}
+
+static Void
+setColourRemappingInfoMatrixOffsets(      Int  (&matrixInputOffset)[3],
+                                          Int  (&matrixOutputOffset)[3],
+                                    const Int  bitDepth,
+                                    const Bool crInputFullRangeFlag,
+                                    const Int  crInputMatrixCoefficients,
+                                    const Bool crFullRangeFlag,
+                                    const Int  crMatrixCoefficients)
+{
+  // set static matrix offsets
+  Int crInputOffsetLuma = (crInputFullRangeFlag)? 0:-(16 << (bitDepth-8));
+  Int crOffsetLuma = (crFullRangeFlag)? 0:(16 << (bitDepth-8));
+  Int crInputOffsetChroma = 0;
+  Int crOffsetChroma = 0;
+
+  switch(crInputMatrixCoefficients)
+  {
+    case MATRIX_COEFFICIENTS_RGB:
+      crInputOffsetChroma = 0;
+      if(!crInputFullRangeFlag)
+      {
+        fprintf(stderr, "WARNING: crInputMatrixCoefficients set to MATRIX_COEFFICIENTS_RGB and crInputFullRangeFlag not set\n");
+        crInputOffsetLuma = 0;
+      }
+      break;
+    case MATRIX_COEFFICIENTS_UNSPECIFIED:
+    case MATRIX_COEFFICIENTS_BT709:
+    case MATRIX_COEFFICIENTS_BT2020_NON_CONSTANT_LUMINANCE:
+      crInputOffsetChroma = -(1 << (bitDepth-1));
+      break;
+    default:
+      fprintf(stderr, "WARNING: crInputMatrixCoefficients set to undefined value: %d\n", crInputMatrixCoefficients);
+  }
+
+  switch(crMatrixCoefficients)
+  {
+    case MATRIX_COEFFICIENTS_RGB:
+      crOffsetChroma = 0;
+      if(!crFullRangeFlag)
+      {
+        fprintf(stderr, "WARNING: crMatrixCoefficients set to MATRIX_COEFFICIENTS_RGB and crInputFullRangeFlag not set\n");
+        crOffsetLuma = 0;
+      }
+      break;
+    case MATRIX_COEFFICIENTS_UNSPECIFIED:
+    case MATRIX_COEFFICIENTS_BT709:
+    case MATRIX_COEFFICIENTS_BT2020_NON_CONSTANT_LUMINANCE:
+      crOffsetChroma = (1 << (bitDepth-1));
+      break;
+    default:
+      fprintf(stderr, "WARNING: crMatrixCoefficients set to undefined value: %d\n", crMatrixCoefficients);
+  }
+
+  setColourRemappingInfoMatrixOffset(matrixInputOffset, crInputOffsetLuma, crInputOffsetChroma, crInputOffsetChroma);
+  setColourRemappingInfoMatrixOffset(matrixOutputOffset, crOffsetLuma, crOffsetChroma, crOffsetChroma);
+}
+
+Void TAppDecTop::applyColourRemapping(const TComPicYuv& pic, SEIColourRemappingInfo& criSEI, const TComSPS &activeSPS)
+{  
+  const Int maxBitDepth = 16;
+
+  // create colour remapped picture
+  if( !criSEI.m_colourRemapCancelFlag && pic.getChromaFormat()!=CHROMA_400) // 4:0:0 not supported.
+  {
+    const Int          iHeight         = pic.getHeight(COMPONENT_Y);
+    const Int          iWidth          = pic.getWidth(COMPONENT_Y);
+    const ChromaFormat chromaFormatIDC = pic.getChromaFormat();
+
+    TComPicYuv picYuvColourRemapped;
+    picYuvColourRemapped.createWithoutCUInfo( iWidth, iHeight, chromaFormatIDC );
+
+    const Int  iStrideIn   = pic.getStride(COMPONENT_Y);
+    const Int  iCStrideIn  = pic.getStride(COMPONENT_Cb);
+    const Int  iStrideOut  = picYuvColourRemapped.getStride(COMPONENT_Y);
+    const Int  iCStrideOut = picYuvColourRemapped.getStride(COMPONENT_Cb);
+    const Bool b444        = ( pic.getChromaFormat() == CHROMA_444 );
+    const Bool b422        = ( pic.getChromaFormat() == CHROMA_422 );
+    const Bool b420        = ( pic.getChromaFormat() == CHROMA_420 );
+
+    std::vector<Int> preLut[3];
+    std::vector<Int> postLut[3];
+    Int matrixInputOffset[3];
+    Int matrixOutputOffset[3];
+    const Pel *YUVIn[MAX_NUM_COMPONENT];
+    Pel *YUVOut[MAX_NUM_COMPONENT];
+    YUVIn[COMPONENT_Y]  = pic.getAddr(COMPONENT_Y);
+    YUVIn[COMPONENT_Cb] = pic.getAddr(COMPONENT_Cb);
+    YUVIn[COMPONENT_Cr] = pic.getAddr(COMPONENT_Cr);
+    YUVOut[COMPONENT_Y]  = picYuvColourRemapped.getAddr(COMPONENT_Y);
+    YUVOut[COMPONENT_Cb] = picYuvColourRemapped.getAddr(COMPONENT_Cb);
+    YUVOut[COMPONENT_Cr] = picYuvColourRemapped.getAddr(COMPONENT_Cr);
+
+    const Int bitDepth = criSEI.m_colourRemapBitDepth;
+    BitDepths        bitDepthsCriFile;
+    bitDepthsCriFile.recon[CHANNEL_TYPE_LUMA]   = bitDepth;
+    bitDepthsCriFile.recon[CHANNEL_TYPE_CHROMA] = bitDepth; // Different bitdepth is not implemented
+
+    const Int postOffsetShift = criSEI.m_log2MatrixDenom;
+    const Int matrixRound = 1 << (postOffsetShift - 1);
+    const Int postLutInputPrecision = (maxBitDepth - criSEI.m_colourRemapBitDepth);
+
+    if ( ! criSEI.m_colourRemapVideoSignalInfoPresentFlag ) // setting default
+    {
+      setColourRemappingInfoMatrixOffsets(matrixInputOffset, matrixOutputOffset, maxBitDepth,
+          activeSPS.getVuiParameters()->getVideoFullRangeFlag(), activeSPS.getVuiParameters()->getMatrixCoefficients(),
+          activeSPS.getVuiParameters()->getVideoFullRangeFlag(), activeSPS.getVuiParameters()->getMatrixCoefficients());
+    }
+    else
+    {
+      setColourRemappingInfoMatrixOffsets(matrixInputOffset, matrixOutputOffset, maxBitDepth,
+          activeSPS.getVuiParameters()->getVideoFullRangeFlag(), activeSPS.getVuiParameters()->getMatrixCoefficients(),
+          criSEI.m_colourRemapFullRangeFlag, criSEI.m_colourRemapMatrixCoefficients);
+    }
+
+    // add matrix rounding to output matrix offsets
+    matrixOutputOffset[0] = (matrixOutputOffset[0] << postOffsetShift) + matrixRound;
+    matrixOutputOffset[1] = (matrixOutputOffset[1] << postOffsetShift) + matrixRound;
+    matrixOutputOffset[2] = (matrixOutputOffset[2] << postOffsetShift) + matrixRound;
+
+    // Merge   matrixInputOffset and matrixOutputOffset to matrixOutputOffset
+    matrixOutputOffset[0] += applyColourRemappingInfoMatrix(criSEI.m_colourRemapCoeffs[0], 0, matrixInputOffset[0], matrixInputOffset[1], matrixInputOffset[2], 0);
+    matrixOutputOffset[1] += applyColourRemappingInfoMatrix(criSEI.m_colourRemapCoeffs[1], 0, matrixInputOffset[0], matrixInputOffset[1], matrixInputOffset[2], 0);
+    matrixOutputOffset[2] += applyColourRemappingInfoMatrix(criSEI.m_colourRemapCoeffs[2], 0, matrixInputOffset[0], matrixInputOffset[1], matrixInputOffset[2], 0);
+
+    // rescaling output: include CRI/output frame difference
+    const Int scaleShiftOut_neg = abs(bitDepth - maxBitDepth);
+    const Int scaleOut_round = 1 << (scaleShiftOut_neg-1);
+
+    initColourRemappingInfoLuts(preLut, postLut, criSEI, maxBitDepth);
+
+    assert(pic.getChromaFormat() != CHROMA_400);
+    const Int hs = pic.getComponentScaleX(ComponentID(COMPONENT_Cb));
+    const Int maxOutputValue = (1 << bitDepth) - 1;
+
+    for( Int y = 0; y < iHeight; y++ )
+    {
+      for( Int x = 0; x < iWidth; x++ )
+      {
+        const Int xc = (x>>hs);
+        Bool computeChroma = b444 || ((b422 || !(y&1)) && !(x&1));
+
+        Int YUVPre_0 = applyColourRemappingInfoLut1D(YUVIn[COMPONENT_Y][x], preLut[0], 0);
+        Int YUVPre_1 = applyColourRemappingInfoLut1D(YUVIn[COMPONENT_Cb][xc], preLut[1], 0);
+        Int YUVPre_2 = applyColourRemappingInfoLut1D(YUVIn[COMPONENT_Cr][xc], preLut[2], 0);
+
+        Int YUVMat_0 = applyColourRemappingInfoMatrix(criSEI.m_colourRemapCoeffs[0], postOffsetShift, YUVPre_0, YUVPre_1, YUVPre_2, matrixOutputOffset[0]);
+        Int YUVLutB_0 = applyColourRemappingInfoLut1D(YUVMat_0, postLut[0], postLutInputPrecision);
+        YUVOut[COMPONENT_Y][x] = std::min(maxOutputValue, (YUVLutB_0 + scaleOut_round) >> scaleShiftOut_neg);
+
+        if( computeChroma )
+        {
+          Int YUVMat_1 = applyColourRemappingInfoMatrix(criSEI.m_colourRemapCoeffs[1], postOffsetShift, YUVPre_0, YUVPre_1, YUVPre_2, matrixOutputOffset[1]);
+          Int YUVLutB_1 = applyColourRemappingInfoLut1D(YUVMat_1, postLut[1], postLutInputPrecision);
+          YUVOut[COMPONENT_Cb][xc] = std::min(maxOutputValue, (YUVLutB_1 + scaleOut_round) >> scaleShiftOut_neg);
+
+          Int YUVMat_2 = applyColourRemappingInfoMatrix(criSEI.m_colourRemapCoeffs[2], postOffsetShift, YUVPre_0, YUVPre_1, YUVPre_2, matrixOutputOffset[2]);
+          Int YUVLutB_2 = applyColourRemappingInfoLut1D(YUVMat_2, postLut[2], postLutInputPrecision);
+          YUVOut[COMPONENT_Cr][xc] = std::min(maxOutputValue, (YUVLutB_2 + scaleOut_round) >> scaleShiftOut_neg);
+        }
+      }
+
+      YUVIn[COMPONENT_Y]  += iStrideIn;
+      YUVOut[COMPONENT_Y] += iStrideOut;
+      if( !(b420 && !(y&1)) )
+      {
+         YUVIn[COMPONENT_Cb]  += iCStrideIn;
+         YUVIn[COMPONENT_Cr]  += iCStrideIn;
+         YUVOut[COMPONENT_Cb] += iCStrideOut;
+         YUVOut[COMPONENT_Cr] += iCStrideOut;
+      }
+    }
+    //Write remapped picture in display order
+    picYuvColourRemapped.dump( m_colourRemapSEIFileName, bitDepthsCriFile, true );
+    picYuvColourRemapped.destroy();
+  }
+}
 //! \}
Index: trunk/source/App/TAppDecoder/TAppDecTop.h
===================================================================
--- trunk/source/App/TAppDecoder/TAppDecTop.h	(revision 1356)
+++ trunk/source/App/TAppDecoder/TAppDecTop.h	(revision 1386)
@@ -128,4 +128,7 @@
 #endif
   std::ofstream                   m_seiMessageFileStream;         ///< Used for outputing SEI messages.  
+
+  SEIColourRemappingInfo*         m_pcSeiColourRemappingInfoPrevious;
+
 public:
   TAppDecTop();
@@ -134,9 +137,11 @@
   Void  create            (); ///< create internal members
   Void  destroy           (); ///< destroy internal members
-  Void  decode            (); ///< main decoding function
+  
 #if NH_MV
+  Void  decode            ( Int i ); ///< main decoding function
   UInt  getNumberOfChecksumErrorsDetected( ) const;
   UInt  getNumberOfChecksumErrorsDetected( Int decIdx ) const { return m_tDecTop[decIdx]->getNumberOfChecksumErrorsDetected(); }
 #else
+  Void  decode            ( ); ///< main decoding function
   UInt  getNumberOfChecksumErrorsDetected() const { return m_cTDecTop.getNumberOfChecksumErrorsDetected(); }
 #endif
@@ -206,4 +211,8 @@
   Void  xCropAndOutput                     ( TComPic* curPic );  
 #endif
+
+private:
+  Void applyColourRemapping(const TComPicYuv& pic, SEIColourRemappingInfo& pCriSEI, const TComSPS &activeSPS);
+  Void xOutputColourRemapPic(TComPic* pcPic);
 };
 
Index: trunk/source/App/TAppDecoder/decmain.cpp
===================================================================
--- trunk/source/App/TAppDecoder/decmain.cpp	(revision 1356)
+++ trunk/source/App/TAppDecoder/decmain.cpp	(revision 1386)
@@ -51,6 +51,7 @@
 {
   Int returnCode = EXIT_SUCCESS;
+#if !NH_MV
   TAppDecTop  cTAppDecTop;
-
+#endif
   // print information
   fprintf( stdout, "\n" );
@@ -65,35 +66,61 @@
   fprintf( stdout, "\n" );
 
-  // create application decoder class
-  cTAppDecTop.create();
+#if NH_MV
+  Int numDecodings = 1; 
+  Int curDecoding  = 0; 
+  Double dResult = 0; 
+  do {
+    TAppDecTop  cTAppDecTop;
+#endif
+    // create application decoder class
+    cTAppDecTop.create();
 
-  // parse configuration
-  if(!cTAppDecTop.parseCfg( argc, argv ))
-  {
-    cTAppDecTop.destroy();
-    returnCode = EXIT_FAILURE;
-    return returnCode;
-  }
+    // parse configuration
+    if(!cTAppDecTop.parseCfg( argc, argv ))
+    {
+      cTAppDecTop.destroy();
+      returnCode = EXIT_FAILURE;
+      return returnCode;
+    }
+#if NH_MV
+    numDecodings = cTAppDecTop.getNumDecodings(); 
+#endif
 
-  // starting time
+    // starting time    
+#if !NH_MV
   Double dResult;
-  clock_t lBefore = clock();
+#endif
+    clock_t lBefore = clock();
 
-  // call decoding function
-  cTAppDecTop.decode();
+    // call decoding function
+#if NH_MV
+    cTAppDecTop.decode( curDecoding );
+#else
+    cTAppDecTop.decode();
+#endif
 
-  if (cTAppDecTop.getNumberOfChecksumErrorsDetected() != 0)
-  {
-    printf("\n\n***ERROR*** A decoding mismatch occured: signalled md5sum does not match\n");
-    returnCode = EXIT_FAILURE;
-  }
+    if (cTAppDecTop.getNumberOfChecksumErrorsDetected() != 0)
+    {
+      printf("\n\n***ERROR*** A decoding mismatch occured: signalled md5sum does not match\n");
+      returnCode = EXIT_FAILURE;
+    }
 
-  // ending time
+    // ending time
+#if NH_MV
+    dResult += (double)(clock()-lBefore) / CLOCKS_PER_SEC;
+#else
   dResult = (Double)(clock()-lBefore) / CLOCKS_PER_SEC;
   printf("\n Total Time: %12.3f sec.\n", dResult);
+#endif
 
-  // destroy application decoder class
-  cTAppDecTop.destroy();
+    // destroy application decoder class
+    cTAppDecTop.destroy();
+#if NH_MV
+    curDecoding++; 
+  } 
+  while ( curDecoding < numDecodings );
 
+  printf("\n Total Time: %12.3f sec.\n", dResult);
+#endif
   return returnCode;
 }
