Added support for CAP, PRF and CPF markers
authorPierre-Anthony Lemieux <pal@palemieux.com>
Thu, 28 Nov 2019 18:58:15 +0000 (10:58 -0800)
committerPierre-Anthony Lemieux <pal@palemieux.com>
Thu, 28 Nov 2019 18:58:15 +0000 (10:58 -0800)
Improved Marker vs Marker Segment discrimination

.gitignore
src/AS_DCP.h
src/AS_DCP_JP2K.cpp
src/JP2K.cpp
src/JP2K.h
src/JP2K_Codestream_Parser.cpp
src/JP2K_Sequence_Parser.cpp
src/j2c-test.cpp

index 8aed0ccb51678fec8aa6718d20e2fee8408751c8..ba1f60fac3d28b1cd897e4cbc5fd09778567f457 100644 (file)
 #
 src/.deps
 src/.libs
+/build
+
+###
+# some tool dirs
+#
+
+/.vscode
 
 ###
 # autotools output
index 96d066cac98acbb8485ba92b443d510a7010d2a6..b28f1143f7588567025e3fb1b4c1004d7177d110 100755 (executable)
@@ -1048,6 +1048,9 @@ namespace ASDCP {
       const ui32_t MaxComponents = 3;
       const ui32_t MaxPrecincts = 32; // ISO 15444-1 Annex A.6.1
       const ui32_t MaxDefaults = 256; // made up
+      const ui8_t  MaxCapabilities = 32;
+                       const ui16_t MaxPRFN = 4;
+                       const ui16_t MaxCPFN = 4;
 
 #pragma pack(1)
       struct ImageComponent_t  // ISO 15444-1 Annex A.5.1
@@ -1085,6 +1088,25 @@ namespace ASDCP {
        ui8_t  SPqcd[MaxDefaults];
        ui8_t  SPqcdLength;
       };
+
+      struct ExtendedCapabilities_t // ISO 15444-1 Annex A.5.2
+      {
+       ui32_t  Pcap; // Pcap = 0 means that no extended capabilities are required
+       ui16_t  Ccap[MaxCapabilities]; // Ccap^i in ISO/IEC 15444-1 corresponds to Ccap[i -1]
+      };
+
+                       struct Profile_t // ISO 15444-1
+      {
+       ui16_t  N; // N = 0 means that the profile is signaled through Rsiz exclusively
+       ui16_t  Pprf[MaxPRFN]; // Pprf^i in ISO/IEC 15444-1 corresponds to Pprf[i -1]
+      };
+
+                       struct CorrespondingProfile_t // ISO 15444-1
+      {
+       ui16_t  N; // N = 0 means that no corresponding profile is signaled
+       ui16_t  Pcpf[MaxCPFN]; // Pcpf^i in ISO/IEC 15444-1 corresponds to Pcpf[i -1]
+      };
+
 #pragma pack()
 
       struct PictureDescriptor
@@ -1108,6 +1130,9 @@ namespace ASDCP {
        ImageComponent_t      ImageComponents[MaxComponents];
        CodingStyleDefault_t  CodingStyleDefault;
        QuantizationDefault_t QuantizationDefault;
+  ExtendedCapabilities_t ExtendedCapabilities;
+  Profile_t   Profile;
+       CorrespondingProfile_t   CorrespondingProfile;    
       };
 
       // Print debugging information to std::ostream
index a4bffc67eeefc4c1af7e0e9e3a2b52bf2e9bfcdf..ccb6b09bb62945037ef039efff7beccce40a8880 100755 (executable)
@@ -106,6 +106,46 @@ ASDCP::JP2K::operator << (std::ostream& strm, const PictureDescriptor& PDesc)
   strm << "              SPqcd: " << Kumu::bin2hex(PDesc.QuantizationDefault.SPqcd, PDesc.QuantizationDefault.SPqcdLength, tmp_buf, MaxDefaults*2)
        << std::endl;
 
+  if (PDesc.Profile.N != 0) {
+         strm << "Profile:" << std::endl;
+
+         for (ui16_t i = 0; i < PDesc.Profile.N; i++) {
+                 strm << "              Pprf(" << (i + 1) << "): "
+                         << std::hex << std::showbase << PDesc.Profile.Pprf[i] << std::dec << std::noshowbase
+                         << std::endl;
+         }
+  }
+
+  if (PDesc.CorrespondingProfile.N != 0) {
+         strm << "Corresponding Profile:" << std::endl;
+
+         for (ui16_t i = 0; i < PDesc.CorrespondingProfile.N; i++) {
+
+                 strm << "              Pcpf(" << (i + 1) << "): "
+                         << std::hex << std::showbase <<  PDesc.CorrespondingProfile.Pcpf[i] << std::dec << std::noshowbase
+                         << std::endl;
+         }
+  }
+
+  if (PDesc.ExtendedCapabilities.Pcap != 0) {
+
+         strm << "Extended Capabilities:" << std::endl;
+
+
+         strm << "                     Pcap:" << PDesc.ExtendedCapabilities.Pcap << std::endl;
+
+         for (i32_t b = 0; b < JP2K::MaxCapabilities; b++) {
+
+                 if ((PDesc.ExtendedCapabilities.Pcap >> b) & 0x1) {
+
+                         strm << "              Ccap(" << (JP2K::MaxCapabilities - b) << "): " <<
+                                 std::hex << std::showbase << PDesc.ExtendedCapabilities.Ccap[JP2K::MaxCapabilities - b - 1] << std::dec << std::noshowbase
+                                 << std::endl;
+
+                 }
+         }
+  }
+
   return strm;
 }
 
@@ -197,6 +237,38 @@ ASDCP::JP2K::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream)
          Kumu::bin2hex(PDesc.QuantizationDefault.SPqcd, PDesc.QuantizationDefault.SPqcdLength,
                        tmp_buf, MaxDefaults*2)
          );
+
+
+  if (PDesc.Profile.N != 0) {
+         fprintf(stream, "               Profile:\n");
+
+         for (ui16_t i = 0; i < PDesc.Profile.N; i++) {
+
+                 fprintf(stream, "              Pprf(%d): %hx\n", i + 1, PDesc.Profile.Pprf[i]);
+
+         }
+  }
+
+  if (PDesc.CorrespondingProfile.N != 0) {
+         fprintf(stream, "Corresponding Profile:\n");
+
+         for (ui16_t i = 0; i < PDesc.CorrespondingProfile.N; i++) {
+                 fprintf(stream, "              Pcpf(%d): %hx\n", i + 1, PDesc.CorrespondingProfile.Pcpf[i]);
+
+         }
+  }
+
+  fprintf(stream, "Extended Capabilities: %x\n", PDesc.ExtendedCapabilities.Pcap);
+
+       for (i32_t b = 0; b < JP2K::MaxCapabilities; b++) {
+
+               if ((PDesc.ExtendedCapabilities.Pcap >> b) & 0x1) {
+
+                       fprintf(stream, "           Ccap(%d): %hx\n", JP2K::MaxCapabilities - b, PDesc.ExtendedCapabilities.Ccap[JP2K::MaxCapabilities - b - 1]);
+
+               }
+       }
+  
 }
 
 
index a0f2a8a124757cf59bcd2b7c6b59aa3343b558d7..4d2c130787acbf289430296076b1279363967fa6 100755 (executable)
@@ -37,45 +37,17 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using Kumu::DefaultLogSink;
 
 
-// when indexed with the second byte of a marker code, this table will procuce one of
-// two values:
-//   0 - the marker is a standalone marker
-//   1 - the marker designates a marker segment
-//
-const byte_t MarkerSegmentMap[] =
-  {
-    /*      0   1   2   3   4   5   6   7      8   9   a   b   c   d   e   f */
-    /* 0 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 0
-    /* 1 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 1
-    /* 2 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 2
-    /* 3 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 3
-    /* 4 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 4
-    /* 5 */ 0,  1,  1,  1,  1,  1,  0,  1,     1,  0,  0,  0,  1,  1,  1,  1, // 5
-    /* 6 */ 1,  1,  0,  1,  1,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 6
-    /* 7 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 7
-    /* 8 */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 8
-    /* 9 */ 1,  1,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // 9
-    /* a */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // a
-    /* b */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // b
-    /* c */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // c
-    /* d */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // d
-    /* e */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // e
-    /* f */ 0,  0,  0,  0,  0,  0,  0,  0,     0,  0,  0,  0,  0,  0,  0,  0, // f
-    /*      0   1   2   3   4   5   6   7      8   9   a   b   c   d   e   f */
-  };
-
-
 //
 ASDCP::Result_t
 ASDCP::JP2K::GetNextMarker(const byte_t** buf, JP2K::Marker& Marker)
 {
   assert((buf != 0) && (*buf != 0 ));
   
-  if ( **buf != 0xff )
+  if (*(*buf)++ != 0xff )
     return ASDCP::RESULT_FAIL;
 
-  Marker.m_IsSegment = (MarkerSegmentMap[*(++(*buf))] == 1);
   Marker.m_Type = (Marker_t)(0xff00 | *(*buf)++);
+  Marker.m_IsSegment = Marker.m_Type != MRK_SOC && Marker.m_Type != MRK_SOD && Marker.m_Type != MRK_EOC;
 
   if ( Marker.m_IsSegment )
     {
@@ -86,13 +58,13 @@ ASDCP::JP2K::GetNextMarker(const byte_t** buf, JP2K::Marker& Marker)
       *buf += Marker.m_DataSize;
     }
 
-
+  /* TODO: why is this here?
   if ( Marker.m_DataSize != 0 && Marker.m_DataSize < 3 )
     {
       DefaultLogSink().Error("Illegal data size: %u\n", Marker.m_DataSize);
       return ASDCP::RESULT_FAIL;
     }
-
+       */
   return ASDCP::RESULT_OK;
 }
 
@@ -227,6 +199,78 @@ ASDCP::JP2K::Accessor::COM::Dump(FILE* stream) const
     }
 }
 
+//
+void
+ASDCP::JP2K::Accessor::PRF::Dump(FILE* stream) const
+{
+  if ( stream == 0 )
+    stream = stderr;
+
+  fprintf(stream, "PRF: \n");
+
+  if (N() == 0) {
+
+         fprintf(stream, "     N/A");
+
+  } else {
+
+         for (ui16_t i = 1; i <= N(); i++) {
+                 fprintf(stream, "pprf(%d): %d\n", i, pprf(i));
+         }
+
+  }
+}
+
+//
+void
+ASDCP::JP2K::Accessor::CPF::Dump(FILE* stream) const
+{
+  if ( stream == 0 )
+    stream = stderr;
+
+  fprintf(stream, "CPF: \n");
+
+  if (N() == 0) {
+
+         fprintf(stream, "     N/A");
+
+  } else {
+
+         for (ui16_t i = 1; i <= N(); i++) {
+                 fprintf(stream, "pcpf(%d): %d\n", i, pcpf(i));
+         }
+
+  }
+}
+
+
+//
+void
+ASDCP::JP2K::Accessor::CAP::Dump(FILE* stream) const
+{
+  if ( stream == 0 )
+    stream = stderr;
+
+  fprintf(stream, "CAP: \n");
+
+  ui32_t pcap = this->pcap();
+
+  if (pcap == 0) {
+
+         fprintf(stream, "     None");
+
+  } else {
+
+         for (i32_t b = 32, i = 1; b > 0; b--) {
+
+                 if ((pcap >> (32 - b)) & 0x1) {
+
+                         fprintf(stream, "     ccap(%d): %d\n", b, this->ccap(i++));
+
+                 }
+         }
+  }
+}
 
 //-------------------------------------------------------------------------------------------------------
 //
@@ -274,6 +318,9 @@ ASDCP::JP2K::GetMarkerString(Marker_t m)
     case MRK_EPH: return "EPH: End of packet header"; break;
     case MRK_CRG: return "CRG: Component registration"; break;
     case MRK_COM: return "COM: Comment"; break;
+    case MRK_CPF: return "CPF: Corresponding profile"; break;
+    case MRK_CAP: return "CAP: Capabilities"; break;
+    case MRK_PRF: return "PRF: Profile"; break;
     }
 
   return "Unknown marker code";
index b043dfc8de36eca354bb80941cb162a84b042b66..5599b2bb97298a8656cbfd4209161d5a8149fa0e 100755 (executable)
@@ -54,9 +54,12 @@ namespace JP2K
       MRK_SOT = 0xff90, // Start of tile-part
       MRK_SOD = 0xff93, // Start of data
       MRK_EOC = 0xffd9, // End of codestream
+      MRK_CAP = 0xff50,  // Extended capabilities
       MRK_SIZ = 0xff51, // Image and tile size
       MRK_COD = 0xff52, // Coding style default
       MRK_COC = 0xff53, // Coding style component
+      MRK_PRF = 0xff56, // Profile
+      MRK_CPF = 0xff59, // Corresponding profile
       MRK_RGN = 0xff5e, // Region of interest
       MRK_QCD = 0xff5c, // Quantization default
       MRK_QCC = 0xff5d, // Quantization component
@@ -219,6 +222,85 @@ namespace JP2K
          inline ui32_t CommentSize() const { return m_DataSize; }
          void Dump(FILE* stream = 0) const;
        };
+
+      // Corresponding Profile
+      class CPF {
+
+          const ui16_t* m_Data;
+                 ui16_t m_N;
+
+          KM_NO_COPY_CONSTRUCT(CPF);
+          CPF();
+
+      public:
+          CPF(const Marker& M) {
+              assert(M.m_Type == MRK_CPF);
+
+              m_Data = (ui16_t*) M.m_Data;
+                         m_N = M.m_DataSize >> 1;
+          }
+
+          ~CPF() {}
+
+          inline ui16_t N() const { return m_N; }
+
+          inline ui16_t pcpf(ui16_t i) const { return KM_i16_BE(m_Data[2 * (i - 1)]); }
+
+          void Dump(FILE* stream = 0) const;
+      };
+
+      // Profile
+      class PRF {
+
+          const ui16_t* m_Data;
+                 ui16_t m_N;
+
+          KM_NO_COPY_CONSTRUCT(PRF);
+          PRF();
+
+      public:
+          PRF(const Marker& M) {
+              assert(M.m_Type == MRK_CPF);
+
+                         m_Data = (ui16_t*) M.m_Data;
+                         m_N = M.m_DataSize >> 1;
+                 }
+
+          ~PRF() {}
+
+          inline ui16_t N() const { return m_N; }
+
+          inline ui16_t pprf(ui16_t i) const { return KM_i16_BE(m_Data[2 * (i - 1)]); }
+
+          void Dump(FILE* stream = 0) const;
+      };
+
+      // Extended capabilities
+      class CAP {
+
+          const ui16_t* m_Data;
+
+                 ui32_t m_Pcap;
+
+          KM_NO_COPY_CONSTRUCT(CAP);
+          CAP();
+
+      public:
+          CAP(const Marker& M) {
+              assert(M.m_Type == MRK_CAP);
+
+              m_Data = (ui16_t *) (M.m_Data + 4);
+                         m_Pcap = KM_i32_BE(*(ui32_t*)(M.m_Data));
+          }
+
+          ~CAP() {}
+
+          inline ui32_t pcap() const { return m_Pcap; }
+
+          inline ui16_t ccap(ui16_t i) const { return KM_i16_BE(m_Data[2 * (i - 1)]); }
+
+          void Dump(FILE* stream = 0) const;
+      };
     } // namespace Accessor
 } // namespace JP2K
 } // namespace ASDCP
index 0ed26492e82e4f8ccce9295d92c3247a219108e0..43665e690f427dced353c8cc971ff4230f254cf3 100755 (executable)
@@ -178,6 +178,71 @@ ASDCP::JP2K::ParseMetadataIntoDesc(const FrameBuffer& FB, PictureDescriptor& PDe
          memcpy(&PDesc.QuantizationDefault, NextMarker.m_Data, NextMarker.m_DataSize);
          PDesc.QuantizationDefault.SPqcdLength = NextMarker.m_DataSize - 1;
          break;
+
+       case MRK_CAP:
+         {
+           Accessor::CAP CAP_(NextMarker);
+           
+                       PDesc.ExtendedCapabilities.Pcap = CAP_.pcap();
+
+                       for(i32_t b = 32, i = 1; b > 0; b--) {
+
+                               if ( (PDesc.ExtendedCapabilities.Pcap >> (b - 1)) & 0x1 ) {
+
+                                       PDesc.ExtendedCapabilities.Ccap[32 - b] = CAP_.ccap(i++);
+
+                               } else {
+
+                                       PDesc.ExtendedCapabilities.Ccap[32 - b] = 0;
+
+                               }
+
+                       }
+         }
+         break;
+
+               case MRK_PRF:
+         {
+           Accessor::PRF PRF_(NextMarker);
+
+               ui16_t n = PRF_.N();
+
+               if ( n > MaxPRFN )
+           {
+             DefaultLogSink().Error("Number (%d) of Pprf^i exceeds maximum supported\n", n);
+             return RESULT_RAW_FORMAT;
+           }
+
+                       PDesc.Profile.N = n;
+
+                       for(i32_t i = 0; i < n ; i++) {
+
+                               PDesc.Profile.Pprf[i] = PRF_.pprf(i+1);
+                       }
+         }
+         break;
+
+               case MRK_CPF:
+         {
+           Accessor::CPF CPF_(NextMarker);
+
+               ui16_t n = CPF_.N();
+
+               if ( n > MaxCPFN )
+           {
+             DefaultLogSink().Error("Number (%d) of Pcpf^i exceeds maximum supported\n", n);
+             return RESULT_RAW_FORMAT;
+           }
+
+                       PDesc.CorrespondingProfile.N = n;
+
+                       for(i32_t i = 0; i < n; i++) {
+
+                               PDesc.CorrespondingProfile.Pcpf[i] = CPF_.pcpf(i+1);
+                       }
+         }
+         break;
+
        }
     }
 
index a0fd5d0b9c2fde47ac8143fe13a5cd5b1cd228b4..a1ece3facc18fc9f98c4439a04adefd854647849 100755 (executable)
@@ -249,6 +249,51 @@ operator==(const ASDCP::JP2K::CodingStyleDefault_t& lhs, const ASDCP::JP2K::Codi
   return true;
 }
 
+//
+bool
+operator==(const ASDCP::JP2K::ExtendedCapabilities_t& lhs, const ASDCP::JP2K::ExtendedCapabilities_t& rhs)
+{
+       if (lhs.Pcap != rhs.Pcap) return false;
+
+       for (ui32_t i = 0; i < JP2K::MaxCapabilities; i++)
+       {
+               if (lhs.Ccap[i] != rhs.Ccap[i])
+                       return false;
+       }
+
+       return true;
+}
+
+//
+bool
+operator==(const ASDCP::JP2K::CorrespondingProfile_t& lhs, const ASDCP::JP2K::CorrespondingProfile_t& rhs)
+{
+       if (lhs.N != rhs.N) return false;
+
+       for (ui32_t i = 0; i < lhs.N; i++)
+       {
+               if (lhs.Pcpf[i] != rhs.Pcpf[i])
+                       return false;
+       }
+
+       return true;
+}
+
+//
+bool
+operator==(const ASDCP::JP2K::Profile_t& lhs, const ASDCP::JP2K::Profile_t& rhs)
+{
+       if (lhs.N != rhs.N) return false;
+
+       for (ui32_t i = 0; i < lhs.N; i++)
+       {
+               if (lhs.Pprf[i] != rhs.Pprf[i])
+                       return false;
+       }
+
+       return true;
+}
+
 //
 bool
 operator==(const ASDCP::JP2K::PictureDescriptor& lhs, const ASDCP::JP2K::PictureDescriptor& rhs)
@@ -271,6 +316,9 @@ operator==(const ASDCP::JP2K::PictureDescriptor& lhs, const ASDCP::JP2K::Picture
   if ( lhs.Csize != rhs.Csize ) return false;
   if ( ! ( lhs.CodingStyleDefault == rhs.CodingStyleDefault ) ) return false;
   if ( ! ( lhs.QuantizationDefault == rhs.QuantizationDefault ) ) return false;
+  if (!(lhs.Profile == rhs.Profile)) return false;
+  if (!(lhs.CorrespondingProfile == rhs.CorrespondingProfile)) return false;
+  if (!(lhs.ExtendedCapabilities == rhs.ExtendedCapabilities)) return false;
   
   for ( ui32_t i = 0; i < JP2K::MaxComponents; i++ )
     {
index 918498299bb001ef350013cd1cd5756e560cff75..bcf95d086485a94534f01f66f61aed62ba095708 100755 (executable)
@@ -246,6 +246,21 @@ main(int argc, const char** argv)
                {
                  has_tlm = true;
                }
+             else if ( current_marker.m_Type == MRK_CAP )
+               {
+                       Accessor::CAP CAP_(current_marker);
+                       CAP_.Dump(stdout);
+               }
+             else if ( current_marker.m_Type == MRK_PRF )
+               {
+                       Accessor::PRF PRF_(current_marker);
+                       PRF_.Dump(stdout);
+               }
+             else if ( current_marker.m_Type == MRK_CPF )
+               {
+                       Accessor::CPF CPF_(current_marker);
+                       CPF_.Dump(stdout);
+               }
              else
                {
                  fprintf(stderr, "Unprocessed marker - %s\n", GetMarkerString(current_marker.m_Type));