diff --git a/source/Lib/CommonLib/SEI.h b/source/Lib/CommonLib/SEI.h
index 42f63bd5914171c2f5ec75fc359fe125bf3c0b4e..33308f11b1bddeac166ead75441d1052697dbca5 100644
--- a/source/Lib/CommonLib/SEI.h
+++ b/source/Lib/CommonLib/SEI.h
@@ -60,6 +60,9 @@ public:
     USER_DATA_REGISTERED_ITU_T_T35       = 4,
     USER_DATA_UNREGISTERED               = 5,
     FILM_GRAIN_CHARACTERISTICS           = 19,
+#if JVET_AB0070_POST_FILTER_HINT
+    POST_FILTER_HINT = 22,
+#endif
     FRAME_PACKING                        = 45,
     DISPLAY_ORIENTATION                  = 47,
     GREEN_METADATA                       = 56,
@@ -1303,6 +1306,25 @@ public:
   uint32_t       m_id;
 };
 
+#if JVET_AB0070_POST_FILTER_HINT
+class SEIPostFilterHint : public SEI
+{
+public:
+  PayloadType payloadType() const { return PayloadType::POST_FILTER_HINT; }
+
+  SEIPostFilterHint() {}
+  virtual ~SEIPostFilterHint() {}
+
+  bool             m_filterHintCancelFlag;
+  bool             m_filterHintPersistenceFlag;
+  uint32_t         m_filterHintSizeY;
+  uint32_t         m_filterHintSizeX;
+  uint32_t         m_filterHintType;
+  bool             m_filterHintChromaCoeffPresentFlag;
+  std::vector<int> m_filterHintValues;   // values stored in linear array, [ ( ( component * sizeY + y ) * SizeX ) + x ]
+};
+#endif
+
 //! \}
 
 
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index d6fe02c331d63f5112723257bc7f72b614bc6639..3b3f678c06afa3f02b6f40b1246e897845694b9e 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -71,6 +71,8 @@
 #define JVET_AB0072                                      1 //Green-MPEG SEI Messaging (JVET-AB0072)
 #define M60678_BALLOT_COMMENTS_OF_FI_03                  1 // m60678: Ballot comments of FI_03
 
+#define JVET_AB0070_POST_FILTER_HINT                      1 // JVET-AB0070: post-filter hint SEI
+
 //########### place macros to be be kept below this line ###############
 
 #define GDR_ENABLED   1
diff --git a/source/Lib/DecoderLib/SEIread.cpp b/source/Lib/DecoderLib/SEIread.cpp
index eba12a176408113803bf15430f993dcef366041f..0d3bbbb2db49b2b0352f23a08d88bd2c798377d4 100644
--- a/source/Lib/DecoderLib/SEIread.cpp
+++ b/source/Lib/DecoderLib/SEIread.cpp
@@ -460,6 +460,12 @@ void SEIReader::xReadSEImessage(SEIMessages& seis, const NalUnitType nalUnitType
       sei = new SEIProcessingOrderInfo;
       xParseSEIProcessingOrder((SEIProcessingOrderInfo &) *sei, payloadSize, pDecodedMessageOutputStream);
       break;
+#if JVET_AB0070_POST_FILTER_HINT
+    case SEI::PayloadType::POST_FILTER_HINT:
+      sei = new SEIPostFilterHint;
+      xParseSEIPostFilterHint((SEIPostFilterHint &) *sei, payloadSize, pDecodedMessageOutputStream);
+      break;
+#endif
     default:
       for (uint32_t i = 0; i < payloadSize; i++)
       {
@@ -2923,6 +2929,38 @@ void SEIReader::xParseSEIPhaseIndication(SEIPhaseIndication& sei, uint32_t paylo
   CHECK(sei.m_verPhaseNum > sei.m_verPhaseDenMinus1 + 1, "The value of ver_phase_num shall be in the range of 0 to ver_phase_den_minus1 + 1, inclusive");
 }
 
+#if JVET_AB0070_POST_FILTER_HINT
+void SEIReader::xParseSEIPostFilterHint(SEIPostFilterHint &sei, uint32_t payloadSize,
+                                        std::ostream *pDecodedMessageOutputStream)
+{
+  uint32_t val;
+  output_sei_message_header(sei, pDecodedMessageOutputStream, payloadSize);
+
+  sei_read_flag(pDecodedMessageOutputStream, val, "filter_hint_cancel_flag");
+  sei.m_filterHintCancelFlag = val;
+  if (sei.m_filterHintCancelFlag == false)
+  {
+    sei_read_flag(pDecodedMessageOutputStream, val, "filter_hint_persistence_flag");
+    sei.m_filterHintPersistenceFlag = val;
+    sei_read_uvlc(pDecodedMessageOutputStream, val, "filter_hint_size_y");
+    sei.m_filterHintSizeY = val;
+    sei_read_uvlc(pDecodedMessageOutputStream, val, "filter_hint_size_x");
+    sei.m_filterHintSizeX = val;
+    sei_read_code(pDecodedMessageOutputStream, 2, val, "filter_hint_type");
+    sei.m_filterHintType = val;
+    sei_read_flag(pDecodedMessageOutputStream, val, "filter_hint_chroma_coeff_present_flag");
+    sei.m_filterHintChromaCoeffPresentFlag = val;
+
+    sei.m_filterHintValues.resize((sei.m_filterHintChromaCoeffPresentFlag ? 3 : 1) * sei.m_filterHintSizeX
+                                  * sei.m_filterHintSizeY);
+    for (uint32_t i = 0; i < sei.m_filterHintValues.size(); i++)
+    {
+      sei_read_svlc(pDecodedMessageOutputStream, sei.m_filterHintValues[i], "filter_hint_value[][][]");
+    }
+  }
+}
+#endif
+
 #if JVET_S0257_DUMP_360SEI_MESSAGE
 void SeiCfgFileDump::write360SeiDump (std::string decoded360MessageFileName, SEIMessages& seis, const SPS* sps)
 {
diff --git a/source/Lib/DecoderLib/SEIread.h b/source/Lib/DecoderLib/SEIread.h
index d2b28787751b84adfb9c9734e0d1b3e211b0a10a..c0f884636afcd9a5410e743bdbbb38aaed2a1891 100644
--- a/source/Lib/DecoderLib/SEIread.h
+++ b/source/Lib/DecoderLib/SEIread.h
@@ -116,6 +116,9 @@ protected:
   void xParseSEINNPostFilterActivation(SEINeuralNetworkPostFilterActivation& sei, uint32_t payloadSize, std::ostream *pDecodedMessageOutputStream);
   void xParseSEIPhaseIndication(SEIPhaseIndication& sei, uint32_t payloadSize, std::ostream* pDecodedMessageOutputStream);
   void xParseSEIProcessingOrder               (SEIProcessingOrderInfo& sei, uint32_t payloadSize, std::ostream *decodedMessageOutputStream);
+#if JVET_AB0070_POST_FILTER_HINT
+  void xParseSEIPostFilterHint(SEIPostFilterHint &sei, uint32_t payloadSize, std::ostream *pDecodedMessageOutputStream);
+#endif
 
   void sei_read_scode(std::ostream *pOS, uint32_t length, int& code, const char *pSymbolName);
   void sei_read_code(std::ostream *pOS, uint32_t length, uint32_t &ruiCode, const char *pSymbolName);
diff --git a/source/Lib/EncoderLib/SEIwrite.cpp b/source/Lib/EncoderLib/SEIwrite.cpp
index 2c8ccd60cc60182b0855ec3cb141aeb20ce7aca1..7b558a46ac891915f84a5f7ec614df985d40958d 100644
--- a/source/Lib/EncoderLib/SEIwrite.cpp
+++ b/source/Lib/EncoderLib/SEIwrite.cpp
@@ -213,6 +213,11 @@ void SEIWriter::xWriteSEIpayloadData(OutputBitstream &bs, const SEI &sei, HRD &h
   case SEI::PayloadType::SEI_PROCESSING_ORDER:
     xWriteSEIProcessingOrder(*static_cast<const SEIProcessingOrderInfo *>(&sei));
     break;
+#if JVET_AB0070_POST_FILTER_HINT
+  case SEI::PayloadType::POST_FILTER_HINT:
+    xWriteSEIPostFilterHint(*static_cast<const SEIPostFilterHint *>(&sei));
+    break;
+#endif
   default:
     THROW("Trying to write unhandled SEI message");
     break;
@@ -1905,4 +1910,26 @@ void SEIWriter::xWriteSEINeuralNetworkPostFilterActivation(const SEINeuralNetwor
 {
   WRITE_UVLC(sei.m_id, "nnpfa_id");
 }
+
+#if JVET_AB0070_POST_FILTER_HINT
+void SEIWriter::xWriteSEIPostFilterHint(const SEIPostFilterHint &sei)
+{
+  WRITE_FLAG(sei.m_filterHintCancelFlag, "filter_hint_cancel_flag");
+  if (sei.m_filterHintCancelFlag == false)
+  {
+    WRITE_FLAG(sei.m_filterHintPersistenceFlag, "filter_hint_persistence_flag");
+    WRITE_UVLC(sei.m_filterHintSizeY, "filter_hint_size_y");
+    WRITE_UVLC(sei.m_filterHintSizeX, "filter_hint_size_x");
+    WRITE_CODE(sei.m_filterHintType, 2, "filter_hint_type");
+    WRITE_FLAG(sei.m_filterHintChromaCoeffPresentFlag, "filter_hint_chroma_coeff_present_flag");
+
+    assert(sei.m_filterHintValues.size()
+           == (sei.m_filterHintChromaCoeffPresentFlag ? 3 : 1) * sei.m_filterHintSizeX * sei.m_filterHintSizeY);
+    for (uint32_t i = 0; i < sei.m_filterHintValues.size(); i++)
+    {
+      WRITE_SVLC(sei.m_filterHintValues[i], "filter_hint_value[][][]");
+    }
+  }
+}
+#endif
 //! \}
diff --git a/source/Lib/EncoderLib/SEIwrite.h b/source/Lib/EncoderLib/SEIwrite.h
index 7eade74dc0a944baf7218b8c292c9981ccedd620..9d65505b414809608802ce4ac521f3a4eb1051eb 100644
--- a/source/Lib/EncoderLib/SEIwrite.h
+++ b/source/Lib/EncoderLib/SEIwrite.h
@@ -142,6 +142,9 @@ protected:
 #if GREEN_METADATA_SEI_ENABLED
   void xWriteSEIGreenMetadataInfo                 (const SEIGreenMetadataInfo &sei);
 #endif
+#if JVET_AB0070_POST_FILTER_HINT
+  void xWriteSEIPostFilterHint(const SEIPostFilterHint &sei);
+#endif
 protected:
   HRD m_nestingHrd;
 };