separated PackageUID from Timed Text Asset ID
authorjhurst <jhurst@cinecert.com>
Thu, 14 Jun 2007 06:12:13 +0000 (06:12 +0000)
committerjhurst <>
Thu, 14 Jun 2007 06:12:13 +0000 (06:12 +0000)
AS_DCP_TimedText.h
README
src/AS_DCP_PCM.cpp
src/AS_DCP_TimedText.cpp
src/AS_DCP_internal.h
src/MDD.cpp
src/MDD.h
src/Metadata.cpp
src/Metadata.h
src/h__Writer.cpp
src/path-test.cpp

index 9c0262ca7be5956016878803c934f0fe42fe7cab..fb956121e89708f166ec8d2a2b4b3460f4745be4 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2003-2006, John Hurst
+Copyright (c) 2007, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -24,9 +24,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
-/*! \file    AS_DCP_Subtitle.h
+/*! \file    AS_DCP_TimedText.h
     \version $Id$       
-    \brief   experimental AS-DCP subtitle
+    \brief   experimental AS-DCP timed-text container
 
     Implements Draft S429-5
 */
@@ -129,8 +129,8 @@ namespace ASDCP {
          // argument can be provided which will be used to retrieve the resource
          // having a particulat UUID. If a Resolver is not supplied, the default
          // internal resolver will return the contents of the file having the UUID
-         // (with optional extension) as the filename. The filename must exist in
-         // the same directory as the XML file opened with OpenRead().
+         // as the filename. The filename must exist in the same directory as the
+         // XML file opened with OpenRead().
          Result_t ReadAncillaryResource(const byte_t* uuid, FrameBuffer&,
                                         const IResourceResolver* Resolver = 0) const;
        };
diff --git a/README b/README
index cedad0bc35ad0bafc70d50cadde003e5d11e033b..acad68fea28b81fb9f0a33eb329f75e269271a60 100755 (executable)
--- a/README
+++ b/README
@@ -113,6 +113,37 @@ utilities all respond to -h.
 
 
 Change History
+2007.06.xx - Timed Text and Bug fixes v.1.1.15
+ o Added support for draft SMPTE 429-5 Timed Text Track File. See
+   AS_DCP_TimedText.h for the API. This is *very* experimental, so
+   do not use for shipping products. Timed Text support is NOT part
+   of the default build, run `make WITH_TIMED_TEXT=1` to enable
+   this part of the library. AS_DCP_TimedText.h will be combined
+   with AS_DCP.h when it becomes stable. Because we need an XML
+   parser to create the Track File, expat is now part of the Timed
+   Text build. Follow the XML_PARSER macro in the makefile and KM_xml
+   to see how you can support your favorite parser. See also
+   S429-5-cgi.cpp for an example that shows how to serve plaintext
+   MXF file elements directly via HTTP.
+ o Refactored the following files as a side-effect of the above
+   work: AS_DCP_JP2K.cpp AS_DCP_MPEG2.cpp AS_DCP_PCM.cpp
+   AS_DCP_MXF.cpp AS_DCP_internal.h MXF.[h|cpp] MXFTypes.[h|cpp]
+   Metadata.[h|cpp] h__Reader.cpp h__Writer.cpp klvwalk.cpp.
+   WARNING: While significant effort has been extended to make sure
+   that this work does not affect existing stable file support,
+   users are cautioned to test this release thouroughly.
+ o Added a large set of path manglers to KM_fileio.h. See
+   path-test.cpp for example usage. The path manglers have not yet
+   been tested on win32 platforms (they are currently used only by
+   the Timed Text module. The PathIsFile(), PathIsDirectory() and
+   FileSize() subroutines have been modified to accept const
+   std::string& instead of const char*.
+ o Added namespace and parsing support (Expat) to Kumu::XMLElement
+   (currently used only for Timed Text support). Also added some
+   new accessors.
+ o ...
+
+
 2007.03.31 - Bug fixes v.1.1.14
  o Fixed KeyFrameOffset value in MPEG wrapping to have negative
    value. This is probably not yet complete for handling all
@@ -133,7 +164,7 @@ Change History
  o Fixed a bug in ByteString::Unarchive() that caused the operation
    to fail when the buffer was smaller than the read (i.e., when
    Capacity() was called).
- o Added IdentifierList class to KM_util.
+ o Added IdentifierList class to KM_util.h.
  o Changed some Error() messages to Debug() in Wav.cpp
  o Revived jp2k-test.cpp and asdcp-mem-test.cpp (they both had
    stale #includes).
index 815de31bd4ee5de26c853ff9fec7fc0a5efd414f..19ea3bb26f7c399d85ea6beaf6084bd15b96727d 100755 (executable)
@@ -160,7 +160,8 @@ ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
     }
 
   // check for sample/frame rate sanity
-  if ( m_ADesc.SampleRate != EditRate_24
+  if ( ASDCP_SUCCESS(result)
+       && m_ADesc.SampleRate != EditRate_24
        && m_ADesc.SampleRate != EditRate_48
        && m_ADesc.SampleRate != EditRate_23_98 )
     {
index 5299dd1b61fa3d6af2b4dd748c1ce5ed742a2255..84dd3acc970abda1f0c61931040092f016049678 100644 (file)
@@ -127,6 +127,7 @@ ASDCP::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedText::TimedTe
 
   TDesc.EditRate = TDescObj->SampleRate;
   TDesc.ContainerDuration = TDescObj->ContainerDuration;
+  memcpy(TDesc.AssetID, TDescObj->AssetID.Value(), UUIDlen);
   TDesc.NamespaceName = TDescObj->RootNamespaceName;
   TDesc.EncodingName = TDescObj->UTFEncoding;
 
@@ -141,7 +142,7 @@ ASDCP::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedText::TimedTe
       if ( KM_SUCCESS(result) )
        {
          TimedTextResourceDescriptor TmpResource;
-         memcpy(TmpResource.ResourceID, DescObject->ResourcePackageID.Value(), UUIDlen);
+         memcpy(TmpResource.ResourceID, DescObject->ResourceID.Value(), UUIDlen);
 
          if ( DescObject->ResourceMIMEType.find("font/") != std::string::npos )
            TmpResource.Type = MT_OPENTYPE;
@@ -153,7 +154,7 @@ ASDCP::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedText::TimedTe
            TmpResource.Type = MT_BIN;
 
          TDesc.ResourceList.push_back(TmpResource);
-         m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->ResourcePackageID, *sdi));
+         m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->ResourceID, *sdi));
        }
       else
        {
@@ -185,9 +186,6 @@ ASDCP::TimedText::MXFReader::h__Reader::OpenRead(char const* filename)
   if( ASDCP_SUCCESS(result) )
     result = InitInfo();
 
-  if( ASDCP_SUCCESS(result) )
-    memcpy(m_TDesc.AssetID, m_Info.AssetUUID, UUIDlen);
-
   return result;
 }
 
@@ -429,6 +427,7 @@ ASDCP::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTe
 
   TDescObj->SampleRate = TDesc.EditRate;
   TDescObj->ContainerDuration = TDesc.ContainerDuration;
+  TDescObj->AssetID.Set(TDesc.AssetID);
   TDescObj->RootNamespaceName = TDesc.NamespaceName;
   TDescObj->UTFEncoding = TDesc.EncodingName;
 
@@ -469,7 +468,7 @@ ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT
     {
       DCTimedTextResourceDescriptor* resourceSubdescriptor = new DCTimedTextResourceDescriptor;
       GenRandomValue(resourceSubdescriptor->InstanceUID);
-      resourceSubdescriptor->ResourcePackageID.Set((*ri).ResourceID);
+      resourceSubdescriptor->ResourceID.Set((*ri).ResourceID);
       resourceSubdescriptor->ResourceMIMEType = MIME2str((*ri).Type);
       resourceSubdescriptor->ResourceSID = m_ResourceSID++;
       m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
@@ -480,12 +479,9 @@ ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT
 
   if ( ASDCP_SUCCESS(result) )
     {
-      UMID SourcePackageUMID;
-      SourcePackageUMID.MakeUMID(0x0f, m_TDesc.AssetID);
-
       InitHeader();
       AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL,
-                  UL(Dict::ul(MDD_PictureDataDef)), TIMED_TEXT_PACKAGE_LABEL, SourcePackageUMID);
+                  UL(Dict::ul(MDD_PictureDataDef)), TIMED_TEXT_PACKAGE_LABEL);
 
       AddEssenceDescriptor(UL(Dict::ul(MDD_DCTimedTextWrapping)));
 
index 7242ef8ae178e1f86b5f4ef9aebe08574b474297..86ccc20cf4af1dcb6c81a6d0e86cf27e0f3fc952 100755 (executable)
@@ -195,7 +195,7 @@ namespace ASDCP
                         const std::string& PackageLabel);
       void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
                         const std::string& TrackName, const UL& DataDefinition,
-                       const std::string& PackageLabel, const UMID& PackageUID);
+                       const std::string& PackageLabel);
       void AddEssenceDescriptor(const UL& WrappingUL);
       Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
 
index db43df0080599ecd45c2de42d66ff5fe42e8d063..0b507964a00be9ee26db66494e1d1716768c1079 100644 (file)
@@ -809,38 +809,41 @@ static const ASDCP::MDDEntry s_MDD_Table[] = {
       {0}, false, "DCTimedTextDescriptor" },
   { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 258
       0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x03 },
-      {0}, false, "DCTimedTextDescriptor_UTFEncoding" },
+      {0}, false, "DCTimedTextDescriptor_AssetID" },
   { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 259
       0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x04 },
-      {0}, false, "DCTimedTextDescriptor_RootNamespaceName" },
+      {0}, false, "DCTimedTextDescriptor_UTFEncoding" },
   { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 260
       0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x05 },
-      {0}, false, "DCTimedTextResourceDescriptor" },
+      {0}, false, "DCTimedTextDescriptor_RootNamespaceName" },
   { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 261
       0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x06 },
-      {0}, false, "DCTimedTextResourceDescriptor_ResourcePackageID" },
+      {0}, false, "DCTimedTextResourceDescriptor" },
   { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 262
       0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x07 },
-      {0}, false, "DCTimedTextResourceDescriptor_ResourceMIMEType" },
+      {0}, false, "DCTimedTextResourceDescriptor_ResourceID" },
   { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 263
       0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x08 },
+      {0}, false, "DCTimedTextResourceDescriptor_ResourceMIMEType" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x00, 0x01, 0x01, 0x01, // 264
+      0x0d, 0x01, 0x03, 0x01, 0xfa, 0xce, 0x00, 0x09 },
     {0}, false, "DCTimedTextResourceDescriptor_ResourceSID" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, // 264
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, // 265
       0x06, 0x10, 0x10, 0x05, 0x01, 0x02, 0x11, 0x00 },
     {0}, false, "GenericStreamPartition" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, // 265
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, // 266
       0x04, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 },
     {0x02, 0x01}, false, "DMSegment_DataDefinition" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, // 266
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, // 267
       0x07, 0x02, 0x02, 0x01, 0x01, 0x03, 0x00, 0x00 },
     {0x02, 0x02}, true, "DMSegment_Duration" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, // 267
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x02, // 268
       0x01, 0x07, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00 },
     {0x61, 0x02}, false, "DMSegment_TrackIDList" },
   { {0}, {0}, false, 0 }
 };
 
-const ui32_t s_MDD_Table_size = 268;
+const ui32_t s_MDD_Table_size = 269;
 
 //
 // end MDD.cpp
index dec65cfcef93eb15965dd17d3656e41b584b7d94..2f1bc34bdb54cfd93652938bcd7521a4f7bcd859 100755 (executable)
--- a/src/MDD.h
+++ b/src/MDD.h
@@ -293,16 +293,17 @@ namespace ASDCP {
        MDD_DCTimedTextWrapping, // 255
        MDD_DCTimedTextEssence, // 256
        MDD_DCTimedTextDescriptor, // 257
-       MDD_DCTimedTextDescriptor_UTFEncoding, // 258
-       MDD_DCTimedTextDescriptor_RootNamespaceName, // 259
-       MDD_DCTimedTextResourceDescriptor, // 260
-       MDD_DCTimedTextResourceDescriptor_ResourcePackageID, // 261
-       MDD_DCTimedTextResourceDescriptor_ResourceMIMEType, // 262
-       MDD_DCTimedTextResourceDescriptor_ResourceSID, //263
-       MDD_GenericStreamPartition, // 264
-       MDD_DMSegment_DataDefinition, // 265
-       MDD_DMSegment_Duration, // 266
-       MDD_DMSegment_TrackIDList, // 267
+       MDD_DCTimedTextDescriptor_AssetID, // 258
+       MDD_DCTimedTextDescriptor_UTFEncoding, // 259
+       MDD_DCTimedTextDescriptor_RootNamespaceName, // 260
+       MDD_DCTimedTextResourceDescriptor, // 261
+       MDD_DCTimedTextResourceDescriptor_ResourceID, // 262
+       MDD_DCTimedTextResourceDescriptor_ResourceMIMEType, // 263
+       MDD_DCTimedTextResourceDescriptor_ResourceSID, //264
+       MDD_GenericStreamPartition, // 265
+       MDD_DMSegment_DataDefinition, // 266
+       MDD_DMSegment_Duration, // 267
+       MDD_DMSegment_TrackIDList, // 268
     }; // enum MDD_t
 } // namespaceASDCP
 
index 9509926141da85780a3e138795460757f536d640..7e6a5e1f48f37b484366577fa9ca841ba341d330 100755 (executable)
@@ -1577,6 +1577,7 @@ ASDCP::Result_t
 DCTimedTextDescriptor::InitFromTLVSet(TLVReader& TLVSet)
 {
   Result_t result = GenericDataEssenceDescriptor::InitFromTLVSet(TLVSet);
+  if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DCTimedTextDescriptor, AssetID));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DCTimedTextDescriptor, UTFEncoding));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DCTimedTextDescriptor, RootNamespaceName));
   return result;
@@ -1587,6 +1588,7 @@ ASDCP::Result_t
 DCTimedTextDescriptor::WriteToTLVSet(TLVWriter& TLVSet)
 {
   Result_t result = GenericDataEssenceDescriptor::WriteToTLVSet(TLVSet);
+  if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DCTimedTextDescriptor, AssetID));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DCTimedTextDescriptor, UTFEncoding));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DCTimedTextDescriptor, RootNamespaceName));
   return result;
@@ -1603,6 +1605,7 @@ DCTimedTextDescriptor::Dump(FILE* stream)
     stream = stderr;
 
   GenericDataEssenceDescriptor::Dump(stream);
+  fprintf(stream, "  %22s = %s\n",  "AssetID", AssetID.EncodeString(identbuf, IdentBufferLen));
   fprintf(stream, "  %22s = %s\n",  "UTFEncoding", UTFEncoding.EncodeString(identbuf, IdentBufferLen));
   fprintf(stream, "  %22s = %s\n",  "RootNamespaceName", RootNamespaceName.EncodeString(identbuf, IdentBufferLen));
 }
@@ -1631,7 +1634,7 @@ ASDCP::Result_t
 DCTimedTextResourceDescriptor::InitFromTLVSet(TLVReader& TLVSet)
 {
   Result_t result = InterchangeObject::InitFromTLVSet(TLVSet);
-  if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DCTimedTextResourceDescriptor, ResourcePackageID));
+  if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DCTimedTextResourceDescriptor, ResourceID));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadObject(OBJ_READ_ARGS(DCTimedTextResourceDescriptor, ResourceMIMEType));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.ReadUi32(OBJ_READ_ARGS(DCTimedTextResourceDescriptor, ResourceSID));
   return result;
@@ -1642,7 +1645,7 @@ ASDCP::Result_t
 DCTimedTextResourceDescriptor::WriteToTLVSet(TLVWriter& TLVSet)
 {
   Result_t result = InterchangeObject::WriteToTLVSet(TLVSet);
-  if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DCTimedTextResourceDescriptor, ResourcePackageID));
+  if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DCTimedTextResourceDescriptor, ResourceID));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteObject(OBJ_WRITE_ARGS(DCTimedTextResourceDescriptor, ResourceMIMEType));
   if ( ASDCP_SUCCESS(result) ) result = TLVSet.WriteUi32(OBJ_WRITE_ARGS(DCTimedTextResourceDescriptor, ResourceSID));
   return result;
@@ -1659,7 +1662,7 @@ DCTimedTextResourceDescriptor::Dump(FILE* stream)
     stream = stderr;
 
   InterchangeObject::Dump(stream);
-  fprintf(stream, "  %22s = %s\n",  "ResourcePackageID", ResourcePackageID.EncodeString(identbuf, IdentBufferLen));
+  fprintf(stream, "  %22s = %s\n",  "ResourceID", ResourceID.EncodeString(identbuf, IdentBufferLen));
   fprintf(stream, "  %22s = %s\n",  "ResourceMIMEType", ResourceMIMEType.EncodeString(identbuf, IdentBufferLen));
   fprintf(stream, "  %22s = %d\n",  "ResourceSID", ResourceSID);
 }
index fbbe2165c074c9f84546d3208e74d4c61e7c3bd0..035dfd3ef793d7420f4a26c24523698cf7c1ff23 100755 (executable)
@@ -572,6 +572,7 @@ namespace ASDCP
          ASDCP_NO_COPY_CONSTRUCT(DCTimedTextDescriptor);
 
        public:
+         UUID AssetID;
           UTF16String UTFEncoding;
           UTF16String RootNamespaceName;
 
@@ -591,7 +592,7 @@ namespace ASDCP
          ASDCP_NO_COPY_CONSTRUCT(DCTimedTextResourceDescriptor);
 
        public:
-          UUID ResourcePackageID;
+          UUID ResourceID;
           UTF16String ResourceMIMEType;
           ui32_t ResourceSID;
 
index 76d657b4fc5ace8542460f96dc883bd8f3bb1e9e..9f1d3fe24093eb9c2cc11ca3f10b085a09649ab2 100755 (executable)
@@ -272,7 +272,7 @@ ASDCP::h__Writer::AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRat
 void
 ASDCP::h__Writer::AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
                                const std::string& TrackName, const UL& DataDefinition,
-                              const std::string& PackageLabel, const UMID& SourcePackageUMID)
+                              const std::string& PackageLabel)
 {
   //
   ContentStorage* Storage = new ContentStorage;
@@ -285,9 +285,9 @@ ASDCP::h__Writer::AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate
   ECD->IndexSID = 129;
   ECD->BodySID = 1;
 
-  //  UUID assetUUID(m_Info.AssetUUID);
-  UMID MaterialPackageUMID;
-  //  SourcePackageUMID.MakeUMID(0x0f, assetUUID);
+  UUID assetUUID(m_Info.AssetUUID);
+  UMID SourcePackageUMID, MaterialPackageUMID;
+  SourcePackageUMID.MakeUMID(0x0f, assetUUID);
   MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
 
   //
index bc0af330dd93bdf6ca9b6e9618e8b493983f99f5..0c5f9692a05311b02dfda5f030ec9b443ee81330 100644 (file)
@@ -98,7 +98,7 @@ main(int argc, const char** argv)
 
   cerr << "----------------------------------" << endl;
   OutList.clear();
-  FindInPaths(PathMatchRegex("^[A-J].*\.h$"), InList, OutList);
+  FindInPaths(PathMatchRegex("^[A-J].*\\.h$"), InList, OutList);
 
   for ( pi = OutList.begin(); pi != OutList.end(); pi++ )
     cerr << *pi << endl;