XIL Device Porting and Extensibility Guide
  Suchtext Nur in diesem Buch
Dieses Buch im PDF-Format herunterladen

Compression/Decompression

6

This chapter explains how to add a new compression method and compression hardware.
In the XIL library, compression and decompression are implemented using loadable handlers. The library defines a generic compression/decompression interface; different compressors implement this interface and store the implementation in dynamically loadable libraries. The handlers are loaded at runtime when they are requested, much like the I/O handlers. From the viewpoint of the application, this allows a variety of compression techniques to be used in a similar fashion.
The public semantics of compression and the description of the compressors in the XIL library are explained in the XIL Programmer's Guide. The information in this chapter assumes you are familiar with those concepts.

Implementation of Compression

The implementation of compression is somewhat different from other handlers in that it is divided into two handlers: a compression handler and a compute handler. Compression handlers contain most of the utility functions for implementing a method of compression and decompression, even though the actual compress and decompress functions are provided in an associated compute handler. The compression handler performs buffer management and implements the semantics of the XilCis object.
Figure 6-1 shows the relationship of the classes that must be used to implement compression.

Grafik

Figure 6-1

The class XilDeviceCompression contains functions that manipulate compressed data buffers to enable compression. The actual compress() and decompress() functions belong to an associated compute handler. This is similar to the situation with I/O devices and the associated compute handlers containing molecules for that I/O device.
The API interface object that holds compression information is called the Compressed Image Sequence, or CIS. The CIS object is created by naming a specific compression via the API call:
XilCis xil_cis_create(XilSystemState system_state,
    char* compressor_name)

Calling this function causes the named compression handler to be loaded. For example, if the compressor_name parameter is MyCompressor, the core code looks for a loadable library named xilMyCompressor.so, which should contain the device compression classes.

XilDeviceCompressionType Class

When the compression handler is loaded, the XIL core looks for a specific function to call to create a class derived from XilDeviceCompressionType for the specific compression. Each compression handler must contain the following function:
XilDeviceCompressionType* XilCreateCompressionType()

This function creates a class derived from XilDeviceCompressionType. A single instance of this class holds all the information common to a specific compression type. For example, any non-varying tables used in compression could be placed here. The compression handler example on page 214 creates a derived class XilDeviceCompressionTypeIdentity; the call to XilCreateCompressionType() instantiates this derived class. Compression attribute names are shared between instances of the XilDeviceCompression class, so they are defined here. The primary purpose of this class is to create instances of XilDeviceCompression through the createDeviceCompression() member function.
The XilDeviceCompressionType class public data is shown below:.
Code Example 6-1 Definition of XilDeviceCompressionType Class

  class XilDeviceCompressionType : public XilDeviceType {  
   public:  
   virtual XilDeviceCompression* createDeviceCompression(  
         XilCis* xcis )=0;  
  // constructor for base class  
  // expects compression name and compression type  
  XilDeviceCompressionType::XilDeviceCompressionType(char *cname, char *ctype);  

Code Example 6-1 Definition of XilDeviceCompressionType Class (Continued)

  // register attribute is set up to remember only the last registered version  
  // of an attribute. Since the compression-specific attributes are registered  
  // last, this allows a compression to override the default implementation  
  // of an attribute.  
  protected:  
       void registerAttr(char* name, setAttrFunc set, getAttrFunc get);  
  };  

The constructor for the class derived from XilDeviceCompressionType should call the XilDeviceCompressionType constructor with the compressor name and compression type attributes of the CIS. This initializes the compress and decompress function names for this handler, appending cname to the compress_ or decompress_ basename. Also, the registerAttr() calls are made at this time. These calls register the codec functions that are called when the application calls xil_cis_set_attribute() and xil_cis_get_attribute() for a particular attribute string. Attributes are shared among actual instances of the device compression class (but attribute values are specific to an instance). Following is an example of the constructor for the Identity codec with an attribute COMPRESSION_QUALITY that can be set:

  typedef void (XilDeviceCompression::*setAttrFunc) (void *value);  
  typedef void* (XilDeviceCompression::*getAttrFunc) ();  
  
  XilDeviceCompressionTypeIdentity::XilDeviceCompressionTypeIdentity()  
  : XilDeviceCompressionType("Identity","IDENTITY") {  
       registerAttr("COMPRESSION_QUALITY",  
       (setAttrFunc)XilDeviceCompressionIdentity::setCompressionQuality);  
       (getAttrFunc)XilDeviceCompressionIdentity::getCompressionQuality);  
  }  

The initialization of compressor name and compression type, and the registration of attributes that the base class provides is sufficient for most compression types.
The member function createDeviceCompression() is used to create each instance of the compression device. It is a pure virtual function in XilDeviceCompressionType; that is, no default implementation exists. A reasonable implementation of createDeviceCompression() must be made part of the compression-specific class derived from XilDeviceCompressionType.
The ok() function of the XilDeviceCompression class must be called to ensure that the device compression instantiation was successful. The status of the constructor can be tracked by a local flag. This function returns a pointer to the device compression object if the constructor was successful. If the constructor failed, then it returns NULL. If destroy is TRUE, then this function will call the destructor before returning. This function should also call the ok() function of the base class to ensure it was successful. Shown next is an example of an ok() function:

  XilDeviceCompressionIdentity*  
  XilDeviceCompressionIdentity::ok(Xil_boolean destroy) {  
       if(this == NULL) {  
           return NULL;  
       } else {  
           // check base class constructor  
           if(XilDeviceCompression::ok(FALSE) == this && isok ==  
  TRUE) {  
                return this;  
           } else {  
                // either failure in base class or in this constructor  
                if(destroy == TRUE) delete this;  
                return NULL;  
           }  
       }  
  }  

XilDeviceCompression Class

More than one CIS may exist for a particular compression type. For each CIS, there is an instance of the class derived from XilDeviceCompression. The instance of the derived class is created using the
createDeviceCompression() function described above. This class implements the various utility functions needed to control the compressed data. The definition of the XilDeviceCompression class is shown next.
Code Example 6-2 Definition of XilDeviceCompression Class (1 of 3)

  class XilDeviceCompression : public XilDevice {  
  public:  
  // Sufficient default implementation.  
  // Functions in this grouping should have an adequate default  
  // implementation in the base class for a variety of compression types.  
  // They support calls from the XilCis class.  
  
   virtual int setInputType(XilImageType* type);// called if inputType is  
                                                         // unknown  
   char* getCompressor();  
   char* getCompressionType();  
   Xil_boolean getRandomAccess();  
   XilImageType* getInputType();  
   XilImageType* getOutputType();                      // may have to parse to get this  
   XilImageType* getOutputTypeHoldTheDerivation(); // no parsing  
   XilCisBufferManager* getCisBufferManager();  
   XilCis* getCis();  
   int getFramesToCompress();  
   void setFramesToCompress(int number_of_frames);  
   int getAttribute(char *name, void** value)  
   { return comp_type->getAttr(this, name, value); }  
   int setAttribute(char *name, void* value)  
   { return comp_type->setAttr(this, name, value); }  
   void setInMolecule(Xil_boolean on_off)  
   { in_molecule = on_off; }  
   Xil_boolean inMolecule()  
       { return in_molecule; }  
   void destroy() {delete this; };  
  
   // Dependent on XilCisBufferManager  
   // These functions reflect the actual state of the cis, as opposed to the state  
   // the user sees (if operations are deferred).  
   int getStartFrame();  
   int getReadFrame();  
   int getWriteFrame();  
  
   // No-action default implementation  
   // Functions in this grouping take no action for the default implementation,  

Code Example 6-2 Definition of XilDeviceCompression Class (2 of 3)

   // which will be sufficient for simple compression types  
   virtual int decompressHeader(void);  
   virtual void flush(void);  
  
   // Dependent on XilCisBufferManager, the default is no-typed frames.  
   // Functions in this grouping call functions within XilCisBufferManager  
   // to perform the action for compression types with no notion of history.  
   // If codec has history (typed frames that are known as key frames), these  
   // functions must be implemented in the derived class.  
   virtual void seek(int framenumber,                  // Seek to the given frame number  
           Xil_boolean history_update = TRUE);         // Maintain valid history if  
                                                        // history_update flag is TRUE  
   virtual int adjustStart(int new_start_frame);// Call to adjust the beg.  
                                                         // of the CIS  
  
   // Dependent on XilCisBufferManager ordinal numbering default.  
   // Functions in this grouping call functions within the XilCisBufferManager to  
   // perform the action for compression, where XilCisBufferManager considers  
   // frames in the order they appear in the CIS.  
   // If this does not apply for the compression type, as in MPEG1, these  
   // function must be implemented in the derived class.  
   virtual void* getBitsPtr(int* nbytes, int* nframes);  
   virtual int hasData();  
   virtual int numberOfFrames();  
   virtual Xil_boolean hasFrame();  
   virtual void putBitsPtr(int nbytes, int nframes, void* data,  
           XIL_FUNCPTR_DONE_WITH_DATA done_with_data = NULL);  
   virtual void putBits(int nbytes, int nframes, void* data);  
  
   // Error reporting function  
   // Defaults to notifyError provided for each systemState  
   // which is sufficient for most compression types  
   void generateError(XilErrorCategory category, char* id,  
                         int primary, Xil_boolean read_invalid,  
                         Xil_boolean write_invalid,  
                         int line, char* file);  
   void generateError(XilErrorCategory category, char* id,  
                         int primary, int line, char* file);  
  
   // Error recovery function  
   // Activated by xil_cis_attempt_recovery  
   // Defaults to no action  

Code Example 6-2 Definition of XilDeviceCompression Class (3 of 3)

   virtual void attemptRecovery(unsigned int nframes, unsigned int nbytes,  
                             Xil_boolean &read_invalid, Xil_boolean &write_invalid);  
  
   // These functions MUST be implemented in the derived class  
   // Functions that are specific to the compression type  
   virtual void reset(void);  
   virtual int deriveOutputType(void);  
   virtual int findNextFrameBoundary();  
  
  // Function called to ensure the device compression instantiation was  
  // successful  
   XilDeviceCompression* ok(Xil_boolean destroy = TRUE);  
  
   protected:  
       virtual int getMaxFrameSize() = 0;              // called to figure maximum  
                                                        // frame size  
       virtual void burnFrames(int nframes) = 0;// update history going forward  
  
  };  

This class has several virtual functions with default implementations. These implementations will work for most compression types that do not have interframe encoding. For compression techniques that require interframe encoding, the implementor must replace these functions with appropriate ones that obey the semantics.
There is a library of buffer management classes that is used to implement the default versions of these members. The CIS buffer manager is described in the section "The CIS Buffer Manager" on page 189.

Base Class Implementations

This section discusses the functions that use the base class implementations. These functions support calls from the XilCis class.
The following functions return class variables that have been initialized during the creation of the codec:
  • getCompressor()
  • getCompressionType()
  • getRandomAccess()
  • getInputType()
  • getOutputType()
  • getOutputTypeHoldTheDerivation()
  • getCisBufferManager()
  • getCis()
  • getFramesToCompress()
  • setFramesToCompress()
  • getAttribute()
  • setAttribute()
Note that getAttribute() and setAttribute() essentially invoke the codec function that was registered (in the type constructor) with char *name.
setInMolecule() sets a flag that indicates if the currently active codec function is a molecule. At the entry point of the molecule, this flag should be set to TRUE. At the entry point of an atomic compress or decompress, this flag should be set to FALSE. This function is used by the error reporting.
destroy() calls the destructor of the derived class when the CIS is destroyed via xil_cis_destroy().
The following three functions query the XilCisBufferManager object for the actual state of the CIS (opposed to the state the user sees):
  • getStartFrame()
  • getReadFrame()
  • getWriteFrame()
The return values will not include operations that have been deferred. The return values are the current values the XilCisBufferManager object has for the start, read, and write frame of the CIS.

Sufficient Default Implementation

setInputType() provides the mechanism to track the input and output types of a CIS. The type includes the width, height, number of bands, and the data type of the CIS. This function is called automatically by the XilCis class when the first xil_compress() is scheduled. Once the input type of the CIS has been defined with nonzero values, it cannot be changed. Therefore, any subsequent xil_compress() calls with different values for the width, height, number of bands, or data type than the original values generates an error
condition. When no calls to xil_compress() are made, the deriveOutputType() function activates the type tracking after it determines the type from the bitstream. See the section"Functions That Must Be Implemented" on page 186 for a description of deriveOutputType().

No Action for the Default Implementation

The following two functions take no action for the default implementation:
  • decompressHeader()
  • flush()
int decompressHeader(void);

decompressHeader() is called when the function xil_decompress() is scheduled. This function takes no action for the default implementation. However, decompressHeader() can be implemented to parse the bitstream and make attributes for the current frame available to the application. The implementation of decompressHeader() is useful when there are attributes of the CIS that are easily located by parsing the frame. For example, each frame in an H261 bitstream has an attribute bit that flags the source of the image as a document Camera. In an application, you may want to direct document Camera images to a different destination than non-document Camera images from the CIS. Therefore, the handler could have a decompressHeader() function that stores the attribute value from the current frame. Then, the application can use the xil_cis_get_attribute() function to get the value of the attribute and take appropriate action before the scheduled decompress gets executed.
void flush(void);

flush() is called when the function xil_flush() is scheduled. This function takes no action for the default implementation. The CIS will be synchronized so that any pending compress operations are scheduled. This function should be implemented for a codec that buffers frames internally (such as MPEG). The buffered frames need to be made available in some form for the output CIS.

Determine the CIS Read Position


Note - seek() calls a function within XilCisBufferManager to perform the action for compression types with no typed frames. If a codec has history (typed frames that are known as key frames), this function must be implemented in the derived class.

void seek(int framenumber, Xil_boolean history_update);

seek() determines the CIS read position (this is always to a frame number). The framenumber parameter is determined by the XilCis class, which tracks the read frame. The history_update parameter of the seek() function is determined by the function that is coupled with seek(), which is handled by the XilCis class, as shown next:

  xil_cis_seek(cis,0,0);         // Set up XilCis read_frame  
  xil_cis_has_frame(cis);        // Call deviceCompression->seek();  
                             // then call deviceCompression->hasFrame()  

The value history_update indicates whether the seek must maintain valid history. TRUE indicates that the history is necessary; FALSE indicates it is not necessary. Note that the history_update parameter is useful only for compression types that have key frames (history). The default value for history_update is FALSE, which assumes the compression type has no key frames. Therefore, seeks are based only on position.
Shown next is the default implementation of the seek() function in the XilDeviceCompression class, which shows that the actual seek is performed by the XilCisBufferManager::seek() function:

  frames_to_burn = cbm.seek(framenumber,XIL_CIS_ANY_FRAME_TYPE);  
  if (frames_to_burn > 0)  
       burnFrames(frames_to_burn);  

The value returned, frames_to_burn, is the number of frames that must be processed to reach the requested position. For more information on the XilCisBufferManager::seek() function, see the section "Seek a Specific
Frame within the CIS" on page 203. burnFrames() must be implemented for each class derived from XilDeviceCompression. The parsing burnFrames() performs is compression specific. See the section "Functions That Must Be Implemented" on page 186 for a description of burnFrames().
The following table explains the meanings for the possible values of frames_to_burn.
ValueMeaning
NegativeAn error occurred
Zero (0)The read position of the CIS is at the desired frame
PositiveThe number of frames which must be processed to reach the requested position

Adjust the Start of a CIS

int adjustStart(int new_start_frame);

adjustStart() is called by the XilCis class when the start of a CIS must be adjusted. An adjustment is activated by the CIS attributes defined by xil_cis_set_keep_frames() and xil_cis_set_max_frames(). These two functions are described in XIL Programmer's Guide. The default implementation of the adjustStart() function adjusts the start frame based only on the input parameter for the frame number.

Note - This default implementation is not sufficient for a compression type with key frames (history), which may be kept in the CIS prior to the start frame. See the section "Adjust Start Frame within Buffer Lists" on page 206 that discusses the XilCisBufferManager::adjustStart() function.

Compression Types with Ordinal Numbering

The following functions depend on the XilCisBufferManager class to perform the requested action:
  • getBitsPtr()
  • hasData()
  • numberOfFrames()
  • hasFrame()
  • putBitsPtr()
  • putBits()
The default implementations of these functions as defined in the XilCisBufferManager class work correctly for compression types with ordinal numbering. In other words, when there are five frames in the CIS, the frames are numbered 0-4, in order. However, for a codec that has out-of-order frames (such as MPEG), the codec must determine if the five frames in the CIS are really frames 0-4 by tracking the temporal reference of each frame.
The tracking of the temporal reference of each frame requires extra parsing, which is not implemented in the default functions listed above. Therefore, if a codec has out-of-order frames, these functions must be implemented in the derived class.
See the section "XilCisBufferManager Class" on page 192 for a description of each of these functions.

Error Reporting

void generateError (XilErrorCategory category,
       char* id, int primary, Xil_boolean read_invalid,
       Xil_boolean write_invalid, int line, char* file);

generateError() is called by the derived XilDeviceCompression class to register the state of its error. If the error occurs during the reading (decompress) of the bitstream, the read_invalid parameter should be set to TRUE. If the error occurs during the writing (compress) to the bitstream, the write_invalid parameter should be set to TRUE. This error function calls a corresponding function in the XilCis class to store the information for the read/write invalid flags and current CIS state.

Error Recovery

attemptRecovery(), by default, takes no action. It is a hook that is provided to enable you to manage a response to an illegal bitstream and to go beyond error reporting.

Functions That Must Be Implemented

The following functions must be implemented in the derived class:
  • reset()
  • getMaxFrameSize()
  • deriveOutputType()
  • burnFrames()
  • findNextFrameBoundary()
void reset(void);

reset() is called when the codec is reset via xil_cis_reset(). This function must clear the state of the CIS so that it is the same as a newly created CIS. Clearing the state of the CIS involves many of the same actions that are performed by the class constructor. In the XIL codec implementations, each class has a private function, initValues(), that performs the common actions for start-up and reset.
One of the common actions is to set up the default input type of the CIS. The value of the variable fields within an input type must be 0 (zero). The value of the non-variable fields within an input type must be their restricted value. For example, "MyCodec" operates only on byte images, but the byte images can be of varying size and number of bands. Therefore, the input type has only one non-variable field, and its value must be XIL_BYTE. The other fields are variable fields, and their values are 0 (zero), as shown in the following code:

  int XilDeviceCompressionMyCodec::initValues() {  
       // set up input/output type  
       XilImageType* t = cis->getSystemState()->  
           createImageType(0,0,0,XIL_BYTE);  
       outputType = inputType = t;  
  
       // initialize any state  
       width = 0;  
       height = 0;  
  
       return XIL_SUCCESS;  
  }  

reset() must destroy the current input and output type, call the initValues() function, and then call the base class reset() function. The base class reset() function performs the reset for the XilCisBufferManager class. For example,

  void XilDeviceCompressionMycodec::reset() {  
       if (inputType != outputType)  
           outputType->destroy();  
       inputType->destroy();  
  
       initValues();  
       XilDeviceCompression::reset();  
  }  

int deriveOutputType(void);

deriveOutputType() is called when the input/output type of the CIS is unknown (for example when the data has been loaded into the CIS from an external file, rather than compressed). In this case, the bitstream must be parsed to determine the fields for the type: xsize, ysize, number_bands, and datatype. The parsed type must be passed to the setInputType() function of the base class. setInputType() will compare the parsed type against the variable fields for this CIS and report any errors. If no errors exist, setInputType() stores the parsed type as the input/output type of the CIS. For example,

  // get pointer to current frame  
  bp32 = (Xil_unsigned32*)cbm.nextFrame();  
  
  image_width = *bp32++;  
  image_height = *bp32++;  
  image_bands = *bp32++;  
  
  if(image_width && image_height && image_bands) {  
     newtype = cis->getSystemState()->createImageType(image_width,  
                image_height, image_bands, XIL_BYTE);  
  
       // set up input/output type and check variable fields  
       setInputType(newtype);  
  }  

int findNextFrameBoundary();

findNextFrameBoundary() is activated by the XilCisBufferManager class when frame boundaries have not been determined for a CIS. This function parses the CIS bitstream, using special interface functions within the XilCisBufferManager class, until the end of the current frame is found, or until no more available data in the CIS exists. The function then returns a status to indicate its success or failure at finding the end of the current frame. See the section "Determine if a Complete Frame Exists" on page 201" for a detailed discussion of findNextFrameBoundary().
void burnFrames(int nframes);

burnFrames() must process through the number of frames specified by the input parameter, nframes. The XilDeviceCompression object must process the bitstream in accordance with the device's dependence on history or interframe data. Burning a frame can be as simple as parsing through until the end-of-frame marker is found, or it may invoke many of the same functions as a decompress. The burnFrames() function should use the nextFrame() and decompressedFrame() functions in the XilCisBufferManager class (see the section "Guarantee a Complete Frame for the Codec to Decompress" on page 198" and the section "After a Frame is Decompressed" on page 198. Below is an example that has a fixed size for each frame; in this case burning is quite simple:

  void  
  XilDeviceCompressionIdentity::burnFrames(int nframes)  
  {  
      Xil_unsigned8* bp = (Xil_unsigned8*)cbm.nextFrame();  
                    .  
                    .  
      for(int i=0; i<nframes; i++) {  
          bp += frame_size;  
          cbm.decompressedFrame(bp);  
       }  

int getMaxFrameSize();

getMaxFrameSize() must determine the worst case compression for the given input type of a CIS. This function should return the maximum number of bytes for any frame in the CIS, which must include anything that appears
between the start of a frame and the start of the next frame (for example, headers and markers). This function is used by the XilCisBufferManager class to determine the space needed for compression and to test for at least one frame in the current buffer.
For the Identity example at the end of this chapter, the getMaxFrameSize() function is the (xsize*ysize*nbands) + "12", for the three header words:

  int XilDeviceCompressionIdentity::getMaxFrameSize(void) {  
       return ((int)inputType->getWidth()*  
                (int)inputType->getHeight()*  
                     inputType->getBands() +12);  
  }  

The CIS Buffer Manager

The CIS buffer manager maintains a list of buffers in which compressed data is stored. There is a XilCisBufferManager object associated with each XilDeviceCompression object (see Figure 6-1).
What follows is a description of the CIS buffer manager interface. The default implementation of the virtual functions in the XilDeviceCompression class make use of the CIS buffer manager. If the CIS buffer manager works for your compression technique, you should make use of it; overriding the default virtual functions in the XilDeviceCompression class is possible without it, but re-implementing the functionality of the CIS buffer manager will certainly increase the effort needed to implement a new compression technique.

XilCisBuffer Class

An XilCisBuffer object acts as a buffer for compressed data. It can be used to create a buffer of a particular size or to make use of an already-existing buffer. The object contains the number of complete frames and number of bytes currently contained in the buffer, the size of the buffer, and an index to the first frame in the buffer. It also contains a flag that indicates whether the buffer may contain a partial frame at its end.

Note - An XilCIsBuffer object contains only frames. It does not support a bitstream that mixes frame data with non-frame data (for instance, audio). If frame data mixed with non-frame data is supported by your codec, then the non-frame data must be grouped along with its associated frame, which can be either the previous or following frame. The codec is responsible for handling and processing the non-frame data. If you are mixing frame and non-frame data, the maximum frame size for the compression type must include the maximum size of the attached non-frame data.

The XilCisBuffer object maintains pointers to the start of the current frame being written to, the current frame being read from, and the next available byte in the buffer. XilCisBuffer may be allowed control over its buffer; in this case it may destroy the buffer if needed. Otherwise, the buffer is expected to be allocated elsewhere, and a callback function may be provided to free the external storage.
XilCisBuffer also keeps a list of objects that contain information about each frame within the buffer. These XilFrameInfo objects contain information such as the starting byte of a frame within the buffer and the number of bytes in the frame. This list is built up by the compressor each time it compresses a frame, and by the decompressor each time a frame is decompressed. A pointer to the current position in the list is also held by the XilCisBuffer object.
The public part of the XilCisBuffer class is shown below:
Code Example 6-3 The XilCisBuffer Class

  class XilCisBuffer {  
  public:  
   XilCisBuffer(unsigned buf_size, int approx_nframes);  
   XilCisBuffer(unsigned nbytes, int nframes, Xil_unsigned8* buf,  
      int frame_id, XIL_FUNCPTR_DONE_WITH_DATA done_data,  
      int approx_nframes = 0);  
   ~XilCisBuffer();  
   XilCisBuffer * ok(); // constructor creation OK function  
   //--------------------- Byte Addition Functions -------------------------  

Code Example 6-3 The XilCisBuffer Class

   void addByte( int b );  
   void addBytes( Xil_unsigned8* b, unsigned nbytes );  
  
   void addShort(int s) { addByte((s) >> 8); addByte(s); }  
   void addShorts(int* s, unsigned m_shorts );  
  
   //------------------------ Public Attributes Access --------------------  
  
   int getNumFrames() const { return num_frames; }  
   int getStartFrameId() const { return start_frame_id; }  
   int getNumBytes() const { return wptr - buffer; }  
   unsigned getBufferSize() const { return buffer_size; }  
   int getNumBytesInWFrame() const { return wptr - wfptr; }  
   int getNumBytesInRFrame();  
   Xil_unsigned8* getNumBytesToFrame(int end_id, int* nbytes);  
  
   //--------------------- Other Member Functions ------------------------  
  
   int frameAtRfptr() const;  
   int frameAfterRfptr(int max_frame_size, Xil_boolean need_EOF = FALSE) const;  
   int numAvailBytes() const;  
  
   //----------- Merging PB --------------------  
   int removeStartFrame();  
  friend class XilCisBufferManager;  
  
  };  

For device compressions that use the implementation of the XilCisBuffer, the only functions that are important are the byte addition functions:
  • addByte() inserts the given byte
  • addBytes() inserts n bytes starting from the given pointer
  • addShort() inserts the given short
  • addShorts() inserts n shorts starting from the given pointer
These functions allow a compressor that uses the XilCisBufferManager::nextBuffer() function to add bytes into the current buffer.
The other functions in the XilCisBuffer class are used by the XilCisBufferManager class (discussed next). These functions are shown in Code Example 6-3 in the case that you want to reimplement them.

XilCisBufferManager Class

The XilCisBufferManager class manages the multiple XilCisBuffer objects that make up a CIS. This class maintains a list of buffers in which the compressed data is kept. Three important positions exist within this list of buffers: the start of the list, the buffer which certain operations will read from, and the current write buffer. The XilCisBufferManager class is shown next:
Code Example 6-4 The XilCisBufferManager Class (1 of 3)

  class XilCisBufferManager {  
  public:  
  
   XilCisBufferManager(int mfs, int nfpb); // constructor  
  
   void   reset();  
   XilCisBufferManager* ok();                     // constructor creation OK function  
   void setXilDeviceCompression(XilDeviceCompression* dc);  
   XilDeviceCompression* getXilDeviceCompression();  
  
  // functions to set or get the maximum frame size or number of frames per buffer  
   int setFrameSize(int fs);                  // set maximum frame size (mfs)  
   void setNumFramesPerBuffer(int nfpb)// set number of frames per buffer (nfpb)  
   int getFrameSize();                        // get mfs  
   int getNumFramesPerBuffer();               // get nfpb  
  
  // functions to get attributes of a frame  
   int getSFrameId();                         // get start frame ID  
   int getRFrameId();                         // get read frame ID  
   int getWFrameId();                         // get write frame ID  
   int getRFrameType();                       // get read frame type  
   void* getRFrameUserPtr();                  // get read frame user pointer  
   int setRFrameUserPtr(void* uptr);          // set read frame user pointer  
  
  // compress frame into CIS, method 1  
   XilCisBuffer* nextBuffer();  
   int compressedFrame(int type = XIL_CIS_DEFAULT_FRAME_TYPE);  

Code Example 6-4 The XilCisBufferManager Class (2 of 3)

  // compress frame into CIS, method 2  
   Xil_unsigned8* nextBufferSpace();  
   int doneBufferSpace(int nbytes, int type = XIL_CIS_DEFAULT_FRAME_TYPE);  
  
  // function to guarantee a complete frame is available for codec to decompress  
   Xil_unsigned8* nextFrame(Xil_unsigned8** r_buffer_end = NULL,  
           Xil_boolean need_EOF = FALSE);  
  
  // function that is called by the decompressor when it is done with a frame  
   void decompressedFrame(Xil_unsigned8* bfptr,  
           int type = XIL_CIS_DEFAULT_FRAME_TYPE, void* user_ptr = NULL);  
  
  // functions to put data into a buffer of the CIS buffer manager  
   void putBits(int nbytes, int nframes, void* data);  
   void putBitsPtr(int nbytes, int nframes, void* data,  
           XIL_FUNCPTR_DONE_WITH_DATA = NULL);  
  
  // function to return a pointer to data that has been compressed or loaded into  
  // the current read buffer of the CIS buffer manager  
   void* getBitsPtr(int* nbytes, int* nframes);  
  
  // functions to return data and frame information about the CIS  
   int hasData();  
   int numberOfFrames();  
   Xil_boolean hasFrame();  
  
  // functions to determine if a complete frame exists in the current read buffer  
   Xil_unsigned8* getNextByte();  
   Xil_unsigned8* getNextBytes(int* nbytes);  
   int foundNextFrameBoundary(Xil_unsigned8* frame_ptr);  
  
  // function to return any bytes that may have been over-read  
   Xil_unsigned8* ungetBytes(Xil_unsigned8* curr_ptr, int nbytes);  
  
  // functions to seek a specific frame within a CIS  
   int seek(int framenumber, int type = XIL_CIS_ANY_FRAME_TYPE);  
   void setSeekToStartFrameFlag(Xil_boolean value)  
       {seek_to_start_frame_flag = value;}  
  // function to adjust the start frame within buffer lists  
   int adjustStart(int framenumber, int type = XIL_CIS_ANY_FRAME_TYPE);  

Code Example 6-4 The XilCisBufferManager Class (3 of 3)

  // this function is a special case of seek()  
   int seekBackToFrameType(int type);  
  
  // function to add end-of-sequence marker for MPEG  
   int addToLastFrame(Xil_unsigned8* data, int nbytes);  
  
  // functions to allow MPEG to implement its own getBits function  
   Xil_unsigned8* getRBuffer() { return ((Xil_unsigned8 *) r_buffer); }  
   Xil_unsigned8* getNumBytesToFrame(int end_id, int* nbytes);  
   int moveEndStartOneBuffer();  
  
  // functions to handle errors and recovery  
   void byteError(Xil_unsigned8* bptr); // called instead of decompressedFrame  
   int nextSeek(int framenumber, int type = XIL_CIS_ANY_FRAME_TYPE);  
   int prevSeek(int framenumber, int type = XIL_CIS_ANY_FRAME_TYPE);  
   void nextKnownFrameBoundary(Xil_unsigned8* cptr, Xil_unsigned8** fptr,  
        int* num_frames);  
   void errorRecoveryDone(Xil_unsigned8* fptr, int num_frames,  
        Xil_boolean fixed);  
   void foundFrameDuringRecovery(Xil_unsigned8* fptr);  
  
  };  

An XilCisBufferManager object has a frame size and a number of frames per buffer value associated with it. Whenever the manager deems it necessary to create a new XilCisBuffer object with its own data storage, it will create the new object such that it has a buffer of size (max_frame_size * num_frames_per_buf) bytes. For XilCisBuffer objects with external storage, the memory buffer is allocated by the application and is whatever size the application provides.

Attributes of a Frame

The elemental unit of a CIS is a frame. The codec can access three attributes of a frame through the XilCisBufferManager class, as follows:
  • The frame's ID (frame_id), which is an ordinal number that refers to the position in the CIS, starting at 0 and increasing monotonically.
  • The frame's type (frame_type), which is a positive integer assigned to the frame when the frame was compressed or decompressed. This attribute is useful for codecs that understand the concept of key frames in the bitstream. This attribute defaults to XIL_CIS_DEFAULT_FRAME_TYPE.
  • The frame's user data pointer (user_ptr), which is a pointer to data allocated by the decompressor that is associated with the frame using malloc(). For instance, an MPEG codec might use this attribute to store the frame's display ID, since this is different from the ordinal frame_id.
The XilCisBufferManager class keeps track of the following special frames:
  • Write frame, which is the frame_id of the write position in a CIS. It may be equal to -1 if an unknown number of frames has been loaded into the CIS.
  • Start frame, which is the frame_id of the first position in the CIS. It is initialized as 0, but the start of the CIS may be adjusted to minimize data storage requirements. See the discussion of adjustStart() in the section "Adjust the Start of a CIS" on page 184.
  • Read frame, which is the frame_id of the read position in a CIS. It is advanced when data is read from the CIS, either for a decompress or for an output (getBits) operation. The read frame can be positioned by an explicit seek from the application. Refer to the discussion of seek() in the section "Determine the CIS Read Position" on page 183.

The Constructor and Associated Functions

The constructor for the XilCisBufferManager class requires two parameters:
  • mfs, the maximum frame size for the compression type
  • nfpb, the number of frames per buffer
The value of mfs must be the worst case scenario (for example, the largest number of bytes that a compressed frame might require, including header, markers, etc.). The value of nfpb is used with the value of mfs to determine the number of bytes allocated for each XilCisBuffer object (mfs * nfpb).
ok() checks the success of the constructor in a similar fashion to the XilDeviceCompression::ok function. ok() returns a NULL pointer if a failure occurs; otherwise, it returns a pointer to the XilCisBufferManager object.
setXilDeviceCompression() is called by the base class XilDeviceCompression during the instantiation. This function registers the derived XilDeviceCompression class, which is necessary for access to that class's findNextFrameBoundary() function. getXilDeviceCompression() returns the registered pointer.

Reset the Codec

reset() is called by the base class XilDeviceCompression when the codec must be reset. reset() handles the freeing of currently buffered data in a CIS. The pointers for the start, read, and write positions are reset to 0, and any local variables are initialized to their default values. The CIS is empty and ready for a new bitstream.

Set/Get Maximum Frame Size and Number of Frames per Buffer

The following functions are used to set/get the maximum frame size and the number of frames per buffer:
  • setFrameSize() sets the maximum frame size per buffer
  • getFrameSize() gets the maximum frame size per buffer
  • setNumFramesPerBuffer() sets the number of frames per buffer
  • getNumFramesPerBuffer() gets the number of frames per buffer
The codec is allowed only to increase the maximum frame size, not decrease it. A request to decrease its value generates an error.

Method One of Adding Data to a CIS Bitstream

One method of adding data to a CIS bitstream is to use the nextBuffer() and compressedFrame() functions.
XilCisBuffer* nextBuffer(); The nextBuffer() function is called by the compressor to request space for a compressed frame. The XilCisBufferManager object checks the current XilCisBuffer to see if max_frame_size bytes are available. If they are not, XilCisBufferManager allocates a new XilCisBuffer object. The pointer to the appropriate buffer is returned to the compressor. The derived
XilDeviceCompression class must use the XilCisBuffer functions shown in the following code example to add data to the CIS. The buffer tracks its own pointer to the added bytes/shorts. Next, the example compressor adds the header bytes to the CIS for width, height, and nbands.

  // write the image parameters into the byte-stream  
  cisbuf->addBytes((Xil_unsigned8*)&cis_width,  
  sizeof(cis_width));  
  cisbuf->addBytes((Xil_unsigned8*)&cis_height,  
       sizeof(cis_height));  
  cisbuf_addBytes((Xil_unsigned8*)&cis_bands, sizeof(cis_bands));  

int compressedFrame(int type =
                           XIL_CIS_DEFAULT_FRAME_TYPE);

When the compressor has finished adding data to the CIS bitstream, it must call compressedFrame(). If the frame that was compressed needs to have a type associated with it, you should pass the frame's type as a parameter to the compressedFrame() function. Otherwise, the default value for a frame type is assigned to the frame.

Note - You must use nextBuffer() with compressedFrame().

Method Two of Adding Data to a CIS Bitstream

Method two of adding data to a CIS bitstream is to use the nextBufferSpace() and doneBufferSpace() functions.
Xil_unsigned8* nextBufferSpace();

for the compressor to call the nextBufferSpace() function is called by the compressor to return a pointer to an available buffer. The pointer is of type Xil_unsigned8*, which means the compressor is responsible for adding data one frame at a time and tracking its own pointer. There are no calls to the XilCisBuffer class.
int doneBufferSpace(int nbytes, int type =
                            XIL_CIS_DEFAULT_FRAME_TYPE);

When the compressor has finished adding data to the CIS bitstream, it must call the doneBufferSpace() function. The first parameter you must pass to this function is the number of bytes (nbytes) added to the buffer. Also, there is an optional frame type parameter for doneBufferSpace().
doneBufferSpace() may cancel a call to nextBufferSpace() if the value of nbytes for doneBufferSpace() is -1.

Note - You must use nextBufferSpace() with doneBufferSpace().

Guarantee a Complete Frame for the Codec to Decompress

nextFrame() is the only function that guarantees a complete frame is available for the codec to decompress. The prototype of nextFrame() is:
Xil_unsigned8* nextFrame(Xil_unsigned8** r_buffer_end,
                     Xil_boolean need_EOF);

This function returns a pointer to the start of the frame. If a non-NULL value for the optional parameter r_buffer_end is supplied, the function will be loaded with a pointer to the last byte in the current buffer. This pointer can be used by the decompressor to protect against bad bitstreams. If you supply a non-NULL value for the optional parameter r_buffer_end and set the parameter need_EOF to TRUE, r_buffer_end will be loaded with a pointer to the end of the frame. Requesting a pointer to the end of the frame may be very expensive with regard to time because the frame must be pre-parsed. Normally just a pointer to the end of the buffer is sufficient to protect against reading past valid memory.

After a Frame is Decompressed

decompressedFrame() is called by the decompressor when it is done with a frame. The prototype of decompressedFrame() is:
void decompressedFrame(Xil_unsigned8** bfptr, int type,
                     void* user_ptr);

The XilCisBufferManager object expects the first parameter, bfptr, to be set to one byte past the end of the frame. Several optional parameters exist. The type parameter may specify a positive integer to store as the frame type. The user_ptr parameter is assumed to be a pointer to data that the codec has allocated and wishes to associate with the frame. If the value of the user_ptr parameter is NULL, no change to the current value is made. The update_next parameter (flag) is TRUE if the function was called after the frame data was processed, not just parsed. The setting of this flag is necessary since there are functions that call decompressedFrame() that only establish frame boundaries.

An Alternative to Compressing into a CIS

An alternative to compressing into a CIS is to load the CIS with already compressed data from another bitstream or a file. You can use putBits() and putBitsPtr() to put data into a buffer of the XilCisBufferManager object.
void putBits(int nbytes, int nframes, void* data);
putBits() copies nbytes from specified data into a newly allocated
XilCisBuffer object.

void putBitsPtr(int nbytes, int nframes, void* data,
                       XIL_FUNCPTR_DONE_WITH_DATA);

putBitsPtr() creates a new XilCisBuffer object whose buffer space references the external storage at a given data pointer. This function has an optional parameter XIL_FUNCPTR_DONE_WITH_DATA that is a pointer to a function that can be called when the XilCisBuffer object with an external storage pointer is destroyed. This XilCisBuffer object can be destroyed because the CIS was reset or destroyed, or the frames in the buffer are no longer necessary to the CIS (see the section "Adjust Start Frame within Buffer Lists" on page 206). The callback can be used to reclaim data space. The default value for this parameter is NULL, which means no callback is made.
Both functions expect the parameter nframes to be specified with one of the following values:
ValueMeaning
-1Unknown number of frames
0May contain partial frames
An integer (n) greater than 0n frames

Note - It is very important that a buffer be loaded with a correct value for nframes.

Return a Pointer to Data

getBitsPtr() returns a pointer to data that has been compressed or loaded into the current read buffer of the XilCisBufferManager object, starting at the current read frame. The prototype of the function is:
void* getBitsPtr(int* nbytes, int* nframes); The pointer returned is to the start of the read frame; the parameters nbytes and nframes are loaded with the number of bytes and the number of complete frames in the buffer. If there is not a complete frame of data to return, the pointer is NULL, and nbytes and nframes are loaded with 0.

Return Data and Frame Information about the CIS

The following three functions return either data or frame information about the CIS:
  • hasData()
  • numberOfFrames()
  • hasFrame()
int hasData(); This function returns the amount of data in the CIS from the current read position to the end of the CIS.
int numberOfFrames();
This function returns the number of complete frames in the CIS from the
current read position to the end of the CIS.

Xil_boolean hasFrame(); This function returns TRUE if a complete frame exists at the read position of the CIS.

Note - When a CIS is loaded and the frame boundaries are not known, hasFrame() and numberOfFrames() invoke the
findNextFrameBoundary() function of the XilDeviceCompression class, if necessary, to determine the frame boundaries.

Determine if a Complete Frame Exists

The XilCisBufferManager object is responsible for determining if a complete frame exists in the current read buffer for certain functions, such as nextFrame(). Frame boundaries are easy to locate in a CIS that was just compressed; the compressor established each frame's start and end. However, if the CIS was loaded with data that contained a partial frame, then the CIS must be parsed to establish the next frame boundary. This parsing is done by each XilDeviceCompression object.
When the XilCisBufferManager object cannot determine if a complete frame exists, the object saves data about the current read buffer and position, and creates temporary pointers for use by the XilDeviceCompression object. Then, the XilCisBufferManager object calls the
findNextFrameBoundary() function of the specific XilDeviceCompression object.
findNextFrameBoundary() must use the getNextByte() and getNextBytes() functions of the XilCisBufferManager object to get data from the CIS. These functions update the temporary pointers that the XilCisBufferManager object created for use by the XilDeviceCompression object when parsing.
Xil_unsigned8* getNextByte();
getNextByte() returns a pointer to the next available byte in the CIS. This
function handles the transition to the next frame buffer if it reaches the end of
the current buffer. If no next buffer exists (the end of the CIS is reached), the
function returns NULL.

Xil_unsigned8* getNextBytes(int* nbytes);

getNextBytes() returns a pointer to the next available byte in the CIS and a count of the number of bytes to the end of that buffer. When XilDeviceCompression has parsed through all of the bytes in the buffer and needs to parse the next buffer, it must call getNextBytes() again. If there is no next buffer, getNextBytes() returns NULL.
int foundNextFrameBoundary(Xil_unsigned8* frame_ptr);

If findNextFrameBoundary() is successful in finding the end of the frame, it must call the XilCisBufferManager::foundNextFrameBoundary() function and return this function's status. foundNextFrameBoundary() expects a pointer to one byte beyond the end of the frame, which is the same as the first byte of the next frame. This function resolves the previous state saved by XilCisBufferManager (the state saved before
findNextFrameBoundary() was called) and the current state (the state after the getNextByte() and getNextBytes() functions were called). For example, the frame end found by findNextFrameBoundary() may be within the next buffer instead of the current read buffer. This would require the XilCisBufferManager to allocate a new buffer that can hold the entire frame and copy the frame pieces into this new buffer.
The foundNextFrameBoundary() function returns the status of either XIL_SUCCESS or XIL_FAILURE to findNextFrameBoundary(), which then should return the status to its calling function in the XilCisBufferManager object, as shown below:

  // success, within findNextFrameBoundary  
  return(cbm->foundNextFrameBoundary(frame_end);  

If findNextFrameBoundary() did not find the end of the frame and has exhausted all the bytes available in the CIS, it should return XIL_FAILURE. The exception is for a compression type that does not use an end-of-frame
marker; the end of this frame is determined by the start of the next frame. In this case, findNextFrameBoundary() should return XIL_UNRESOLVED. The XilCisBufferManager object interprets this status according to whether or not partial frames are present in the buffer.

Over-read Bytes

ungetBytes() allows the XilDeviceCompression object to return any bytes that it may have over-read when determining the frame end (using findNextFrameBoundary()). This function is needed because some compression types do not contain an end-of-frame marker. The next start-of-frame marker must be read and identified before the end-of-frame is known. "Reading ahead" may move the frame tracking pointers to a different XilCisBuffer object. Since only data within an XilCisBuffer is contiguous, the compression device cannot just subtract n bytes from the current pointer to find the end-of-frame pointer. Instead, the compression device must request to return the over-read bytes by using the ungetBytes() function. This function will detect and handle backing up the pointer by n bytes of valid buffer data.

Seek a Specific Frame within the CIS

The following functions are used for seeking a specific frame within a CIS:
  • seek()
  • setSeekToStartFrameFlag()
int seek(int framenumber, int type); The XilCompressionDevice object uses the seek() function to seek to a specific frame within a CIS. The parameter framenumber corresponds to the frame ID of a frame in the CIS. The optional type parameter specifies the frame type, which corresponds to the type of a frame in the CIS. This parameter defaults to XIL_CIS_ANY_FRAME_TYPE (any frame type), which allows a seek based on position only.
The XilCisBufferManager object performs a seek in two stages: the first stage is for position and the second stage is for type. First, XilCisBufferManager positions the read frame as close as possible to the desired frame. If the desired frame does not have a frame boundary, the read
frame is the closest preceding frame number (this could be the first frame in the CIS, the start frame). The delta between the current read frame and the desired frame number is stored, and the second stage begins.
The second stage positions the read frame based on the requested type. If the frame type is XIL_CIS_ANY_FRAME_TYPE or matches the current read frame type, then the second stage delta is zero. Otherwise, XilCisBufferManager searches backward until it finds the requested frame type. Then, it leaves the read frame at this frame and stores the additional number of frames from the position of the read frame in the second stage delta. The return value is the total of the first stage delta (position) and the second stage delta (frame type).
Note that the XilCisBufferManager object treats the next decompressed frame in a special way. During the seek, the next decompress frame is assigned the type XIL_CIS_ANY_FRAME_TYPE. This assignment ensures that a burn forward into a CIS starts from the last decompressed frame. The next decompress frame is tracked via the update_next parameter of the decompressedFrame() function.
The history_update parameter of the XilDeviceCompression::seek() function can be used to determine the frame type specified to the XilCisBufferManager::seek() function. If history_update is TRUE, then the seek must preserve history. The type specified in the XilCisBufferManager::seek() function must be the appropriate key frame type. If history_update is FALSE, then the seek is for position only, and the type specified in the XilCisBufferManager::seek() function should be the XIL_CIS_NO_BURN_TYPE, which is a special flag to the XilCisBufferManager object to skip the second stage delta. Following is a typical code fragment:

  if (history_update == TRUE)  
      frames_to_burn = cbm.seek(framenumber, seekFrameType);  
  else  
      frames_to_burn = cbm.seek(framenumber, XIL_CIS_NO_BURN_TYPE);  

Figure 6-2 is a diagram that helps to illustrate the actions taken by the XilCisBufferManager object for a seek.

Grafik

Figure 6-2 XilCisBufferManager::seek()

Figure 6-2 shows key frames at 0 and 5 and a block of four frames starting at frame 7, where boundaries are not known. The current read frame is frame 3 for all examples below.
cbm->seek(3, xxxx)
                             position 3, returns 0, regardless of frame type
cbm->seek(4, Key)
                             position 3, returns 1 (burn 1 forward)
cbm->seek(2, Key)
                             position 0, returns 2 (burn 2 forward)
cbm->seek(6, Key)
                             position 5, returns 1 (burn 1 forward)
cbm->seek(6, Any)
                             position 6, returns 0
cbm->seek(8, Key)

position 5, returns 3 (burn 3 forward)
cbm->seek(8, Any)
position 7, returns 1 (burn 1 forward)
Note that frames are typed by the function decompressedFrame() or compressedFrame().
void setSeekToStartFrameFlag(Xil_boolean value); This function determines the behavior of the XilCisBufferManager::seek() function during the second stage, seek to frame type. When the XilCisBufferManager object has moved back through the CIS to the start frame and still has not matched with the requested type, it checks the value of the seek_to_start_frame_flag parameter. If the value of this parameter is TRUE, then the start frame of the CIS is granted the type XIL_CIS_ANY_FRAME_TYPE. In this case, the worst case burn starts from the
first available frame, which for most compressions is a reasonable option. The default value of the seek_to_start_frame_flag parameter is TRUE for an initialized (reset) XilCisBufferManager object.

Adjust Start Frame within Buffer Lists

adjustStart() is used to adjust the start frame within the buffer lists. The prototype of this function is:
int adjustStart(int framenumber, int type); Since the new start frame may not be able to be processed without information from prior frames, adjustStart() can take as a parameter a frame type. Any frames from the desired frame back to a frame of the given type are kept within the CIS, although the frames may not be accessed directly via seek(). These additional frames are used only to process the new start frame. Once the adjustment is made, any buffers prior to the new start buffer are destroyed.
This function returns an int that represents status, either XIL_SUCCESS or XIL_FAILURE.

Device Compression with Out-of-Order Frames

The following functions discussed in this section provide necessary interfaces for device compression with out-of-order frames to take advantage of the XilCisBufferManager class:
  • seekBackToFrameType()
  • addToLastFrame()
Out-of-order frames means the frame ID and display ID do not match (MPEG is an example of this type of device compression). For more information about MPEG, see the XIL Programmer's Guide.
int seekBackToFrameType(int type);

seekBackToFrameType() is a special case of seek. It begins looking for the specified frame type at the frame previous to the current read frame. This function does not have framenumber as a parameter. It takes identical action to the second stage of the seek() function. It positions the current read frame at a frame of the specified type, which must be positioned before the original
read frame. The number returned is the number of frames from the current read frame to the original read frame (the read frame upon entry to the function).
int addToLastFrame(Xil_unsigned8* data, int nbytes); Device compression with out-of-order frames uses an end-of-sequence marker, which from the library's standpoint is part of the last frame in the CIS. This function allows the bytes for the end-of-sequence marker to be added after the last frame has already been registered via the compressedFrame() function. addToLastFrame() automatically increases the frame size by nbytes and adjusts the write pointers of the CIS.
The following functions allow device compression with out-of-order frames to implement its own getBits function, which can handle out-of-order frames:
  • getRBuffer()
  • getNumBytesToFrame()
  • moveEndStartOneBuffer()
Xil_unsigned8* getRBuffer();
getRBuffer() returns a pointer to the current read buffer. This pointer allows
the XilDeviceCompression object to determine when it has "crossed" the
buffer boundary to get to the next frame.

Xil_unsigned8* getNumBytesToFrame(int end_id,
                    int* nbytes);

getNumBytesToFrame() returns a pointer to a data block starting at the current read frame in the current buffer. The number of bytes between the current read frame and the specified end frame (end_id) is loaded into nbytes. This allows the XilDeviceCompression object to get less than all of the frames in the current read buffer by allowing you to specify on which frame to stop.
int moveEndStartOneBuffer();

moveEndStartOneBuffer() moves the last frame of the current buffer and the start frame of the next buffer into a new buffer and inserts it into the buffer list. This function is used to move a predictive frame at the buffer end and bidirectional frame at the next buffer start into the same buffer.

Error Handling and Recovery

The following functions discussed in the section are provided as hooks for error handling and recovery:
  • byteError()
  • nextSeek()
  • prevSeek()
  • nextKnownFrameBoundary()
  • errorRecoveryDone()
  • foundFrameDuringRecovery()
Currently, these functions are not used by the XIL Imaging Library.
void byteError(Xil_unsigned8* bptr);
byteError() is called when a decompressor finds a bitstream error during
decompress() or findNextFrameBoundary(). The byte pointer input
parameter should be set to the location of the bitstream error. Doing this sets
up the next byte that getNextByte() returns.

int nextSeek(int framenumber, int type);
nextSeek() determines the closest frame that is greater than or equal to the
given framenumber. It returns -1 when no such "seekable" frame exists.

int prevSeek(int framenumber, int type);
prevSeek() determines the closest frame that is less than or equal to the
given framenumber. It returns -1 if no such "seekable" frame exists.

void nextKnownFrameBoundary(Xil_unsigned8* cptr,
                    Xil_unsigned8** fptr int* num_frames);

nextKnownFrameBoundary() returns a pointer to the next established frame boundary in relation to a pointer in the current buffer (cptr). It returns the pointer in fptr and a number of frames (num_frames) between the current position and the known boundary, including the current frame.
void errorRecoveryDone(Xil_unsigned8* fptr,
                    int num_frames, Xil_boolean fixed);

errorRecoveryDone() is called by xil_cis_attempt_recovery() just before it completes. It expects the current pointer, the number of frames parsed, and the state of the recovery.
void foundFrameDuringRecovery(Xil_unsigned8* fptr);

foundFrameDuringRecovery() expects a pointer to the start of the next frame, which established previous bytes as part of the previous frame.

Adding a New Compression Method

The complexity of adding a new compression type to the XIL library varies widely, depending on the compression technology. In order to install a compressor like JPEG or CCITT G3, very little work has to be done other than to actually write the compression and decompression functions and the few pure virtual functions. The default implementation for the XilDeviceCompression will likely work. For a more complicated compression technique, like MPEG 1, with its multiple frame types and out-of-order transmission, relatively little of the default implementation may be used. In either case, however, the general steps to add a compression technique are the same.
Table 6-1 lists the classes that you must create, the functions that you must implement, and the functions that you optionally can implement to add a new compression method.
Table 6-1
Class/FunctionRequiredOptional
Derived class from XilDeviceCompressionType classX
createDeviceCompression()X
Derived class from XilDeviceCompression classX
findNextFrameBoundary()X
burnFrames()X
getMaxFrameSize()X
reset()X
Table 6-1
Class/FunctionRequiredOptional
deriveOutputType()X
decompressHeader()
X
seek()
X
flush()
X
adjustStart()
X
getBitsPtr()
X
putBits()
X
putBitsPtr()
X
hasData()
X
numberOfFrames()
X
hasFrame()
X
setInputType()
X
attemptRecovery()
X
  1. Create the derived class from XilDeviceCompressionType. For example, the code at the end of this chapter defines XilDeviceCompressionTypeIdentity. The global function XilCreateCompressionType() must be written to create a single instance of this derived class. If the compression technique contains exposed attributes, these should be registered here by calling registerAttr() from within the constructor for XilDeviceCompressionType. Finally, the pure virtual member function createDeviceCompression() must be written. Usually, this involves calling the constructor for the XilDeviceCompression derived class.

  2. Next, a class derived from XilDeviceCompression must be created. In the example, the class XilDeviceCompressionIdentity is created. Table 6-1 lists the pure virtual functions that must be implemented in the derived class and the ones that are optional. Optional functions need only be implemented if the default implementation inherited from XilDeviceCompression is inadequate.

  1. The actual compression and decompression functions are added to a compute handler, as described in Chapter 4, "Compute Devices."

    The names of these functions should be compress_compression_name and decompress_compression_name, where compression_name is the name of the new compression type. In the section "Sample Compressor" on page 214 the example derives a compute class called XilDeviceComputeTypeIdentity. It contains three members:

  describeMembers(), compress_Identity() and
  decompress_Identity(). describeMembers() is generated
  automatically as described in Chapter 4, "Compute Devices."

The name of the created compute module should look like this:
xilcomputeCompressorname_COMPANYNAMEmemory.so.major_ver_no
In the case of the example, the compute module is named
  xilcomputeIdentity_SUNWmemory.so.1

  1. Finally, the /opt/SUNWits/Graphics-sw/xil/lib/xil.compute configuration file must be updated to show the new compression type. The appropriate line to add looks like this:

    computeCompressorname_COMPANYNAMEmemory Compressorname

    This indicates the dependence on the compression handler by the compute handler, which implements the compression and decompression. More information on installing handlers can be found in Chapter 2, "More on Writing Device Handlers."

Adding Compression Hardware

Hardware to support compression usually falls into two categories. In the first, the device operates on memory, doing compression using a fast special purpose processor, and then putting the results back into memory. Adding support into the XIL library for this type of hardware can be a simple as rewriting a single function (or pair of functions, if the device is capable of compression and decompression). The second type of device is usually tied to input or output: a frame grabber with built-in JPEG compression, for example, or a JPEG decompress board with the ability to map windows onto the screen. In this case, it is necessary to write the appropriate I/O handler for the device, and write a molecule to perform the capture/compress or the
decompress/display function. If the desired compression format is not one that is currently available, the entire compression handler must be created in the manner described in the previous section.
The simpler case is for devices that are not associated with input or output, and for a compression type that currently exists in the XIL library (say, JPEG). In the simplest fashion, porting this type of device requires subclassing the XilDeviceComputeType, just like what is done to add accelerator support for any XIL operator. The new functions would be named
compress_Compressorname() and decompress_Compressorname(), where Compressorname is the name of the compression type that is being supported. The function describeMembers() must be generated using the method described in Chapter 4, "Compute Devices." The new compute handler containing the compression functions should be called
xilcomputeCompressorname_COMPANYNAMEdevicename.so
where devicename is the name for the accelerator device. A line in the xil.compute configuration file should be added as follows:
computeCompressorname_COMPANYNAMEdevicename Compressorname
As described in Chapter 4, "Compute Devices," adding this compute device will replace the default function called for compress(). The implementation of compress() and decompress() is required to put their compressed data and decompressed images back in the CPU memory when the operation is done. If a device also has other capabilities, such as doing RGB to YCC color conversion, then it would also be advantageous to provide a molecule compress(color_convert()), for example. Molecules are added in this case exactly like the noncompression case described in Chapter 4, "Compute Devices."
If the device contains integrated input or output, the situation is slightly different. First, in order for the XIL library to expose the device as a device image to the application, an I/O handler must be written. This procedure is described in Chapter 3, "I/O Devices." In most cases, it is advantageous to provide such a handler even for the cases where compression or decompression are not performed (raw frame grab or displaying uncompressed data), if the hardware supports such capabilities. In order to provide the decompression capabilities of the device, a molecule must be written that supports display(decompress()) or compress(capture()). This is also discussed in Chapter 3, "I/O Devices,"
where the interaction of compute and I/O handlers is discussed. Again, it may be advantageous to provide other molecules to support whatever functionality the hardware has: color conversion or zoom, for example.
After the I/O handler is written, the compute handler must be written. The situation is the same as described above. The compute module should be called
xilcomputeCompressorname_COMPANYNAMEdevicename.so
just as before. However, this time, there is an added notation in the configuration file that indicates the dependence on the I/O handler for the device:
computeCompressorname_COMPANYNAMEdevicename\ Compressorname ioCOMPANYNAMEdevicename
These two dependency entries in xil.compute would reference the following modules:
xilCompressorname.so
and
xiliodevicename.so

The first module contains the generic compression information for the compression type, and the second contains the generic I/O handler for the accelerator device.
Finally, I/O compression devices with associated image storage may also be defined. Chapter 5, "Storage Devices," describes storage devices in detail.

Sample Compressor

The code in this sample implements an example identity compression. In this lossless compression, raw image data is simply put into the CIS in a predefined manner.
The example contains four files:
  • XilDeviceCompressionTypeIdentity.h and XilDeviceCompressionTypeIdentity.cc, which defines the device compression type
  • XilDeviceCompressionIdentity.h and XilDeviceCompressionIdentity.cc, which defines the identity device compression itself
  • compress_Identity.cc, which encodes the images into the CIS
  • decompress_Identity.cc, which decodes the images from the CIS

XilDeviceCompressionTypeIdentity.h

Code Example 6-5 XilDeviceCompressionTypeIdentity.h

  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:XilDeviceCompressionTypeIdentity.h  
  //  Project:XIL  
  //  Created:93/04/14  
  //  Revision:1.1  
  //  Last Mod:12:05:08, 07 Mar 1994  
  //  
  //  Description:  
  //  This is the class that maintains the Identity compression  
  //  type information.  It is derived from the more generic  
  //  XilDeviceCompressionType class and is responsible for  
  //  registering the attribute setting/getting functions for  
  //      Identity compression and decompression.  
  //  
  //      The class is also used to maintain information which is not  
  //      specific to any single instantiation of the Identity  
  //      compressor.  There will be only one instantiation of this  
  //      class for the Identity compression irregardless of how many  
  //      XilCis objects are created.  
  //  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)XilDeviceCompressionTypeIdentity.h1.1\t94/03/07  "  
  
  #ifndef XilDeviceCompressionTypeIdentity_H  
  #define XilDeviceCompressionTypeIdentity_H  
  
  #include <xil/XilError.h>  
  #include <xil/XilCis.h>  
  #include <xil/XilDeviceCompressionType.h>  
  
  classXilDeviceCompressionTypeIdentity : public XilDeviceCompressionType  
  {  
  public:  
      virtual XilDeviceCompression*  createDeviceCompression(XilCis*  xcis);  
  
      //  
      //  The constructor is moved into the public space here because  
      //  this derived class can be created, but the parent class is not  
      //  permitted to be created without being derived upon.  

Code Example 6-5 XilDeviceCompressionTypeIdentity.h (Continued)

      //  
      XilDeviceCompressionTypeIdentity(void);  
      ~XilDeviceCompressionTypeIdentity(void);  
  };  
  
  #endif  XilDeviceCompressionTypeIdentity_H  

XilDeviceCompressionTypeIdentity.cc

Code Example 6-6 XilDeviceCompressionTypeIdentity.cc (1 of 3)

  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        XilDeviceCompressionTypeIdentity.cc  
  //  Project:     XIL  
  //  Created:     93/04/14  
  //  Revision:    1.2  
  //  Last Mod:    09:31:22, 22 Mar 1994  
  //  
  //  Description:  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)XilDeviceCompressionTypeIdentity.cc1.2\t94/03/22  "  
  
  #include "XilDeviceCompressionTypeIdentity.h"  
  #include "XilDeviceCompressionIdentity.h"  
  
  //------------------------------------------------------------------------  
  //  
  //  Function:    XilCreateCompressionType  
  //  Created:     93/04/14  
  //  
  //  Description:  
  //  The XilCreateCompressionType() is called when the XIL core  
  //  opens the xilIdentity.so library.  XilCreateCompressiontype()  
  //  is responsible for creating the Identity compression type class.  
  //  
  //------------------------------------------------------------------------  
  
  XilDeviceCompressionType*  
  XilCreateCompressionType()  
  {  
      XilDeviceCompressionTypeIdentity* device;  
  
      device = new XilDeviceCompressionTypeIdentity();  
      if(device==NULL) {  
       // out of memory  
       XIL_ERROR(NULL, XIL_ERROR_RESOURCE,"di-1",TRUE);  
      }  

Code Example 6-6 XilDeviceCompressionTypeIdentity.cc (2 of 3)

      return device;  
  }  
  
  //------------------------------------------------------------------------  
  //  
  //  Function:    XilDeviceCompressionTypeIdentity::createDeviceCompression()  
  //  Created:     93/04/14  
  //  
  //  Description:  
  //  createDeviceCompression() is used to create new instances of  
  //  the Identity device compression when new CISs are created by the  
  //  user.  
  //  
  //------------------------------------------------------------------------  
  XilDeviceCompression*  
  XilDeviceCompressionTypeIdentity::createDeviceCompression(XilCis* xcis)  
  {  
      XilDeviceCompressionIdentity* device;  
  
      //  
      //  Create a new XilDeviceCompressionIdentity  
      //  
      device = new XilDeviceCompressionIdentity(this, xcis);  
      if(device == NULL) {  
          // out of memory  
          XIL_ERROR(xcis->getSystemState(), XIL_ERROR_RESOURCE,"di-1",TRUE);  
      }  
  
      //  
      //  Check to see if the device construction was completed  
      //  successfully.  
      //  
      device = device->ok();  
      if(device == NULL) {  
          //  Couldn't create internal base XilDeviceCompression object  
          XIL_ERROR(xcis->getSystemState(), XIL_ERROR_SYSTEM,"di-278", FALSE);  
      }  
  
      return device;  
  }  

Code Example 6-6 XilDeviceCompressionTypeIdentity.cc (3 of 3)

  //------------------------------------------------------------------------  
  //  
  //  Function:    XilDeviceCompressionTypeIdentity()  
  //  Created:     93/04/14  
  //  
  //  Description:  
  //  The device compression type constructor initializes any  
  //  Identity compression type specific data and registers all of the  
  //  Identity attributes with the XIL core.  
  //  
  //------------------------------------------------------------------------  
  
  XilDeviceCompressionTypeIdentity::XilDeviceCompressionTypeIdentity()  
  : XilDeviceCompressionType("Identity","IDENTITY")  
  {  
  
     // any attributes which the codec would like to provide access  
     // to via the xil_cis_set_attribute() and/or xil_cis_get_attribute()  
     // bindings must be registered here.  
     // NOTE: These attribute functions are registered here as an  
     // example ONLY...the Identity codec does not make use of  
     // "quality" ...it is just an example of the registerAttr mechanism.  
  
          registerAttr("COMPRESSION_QUALITY",  
  
  (setAttrFunc)XilDeviceCompressionIdentity::setCompressionQuality,  
  
  (getAttrFunc)XilDeviceCompressionIdentity::getCompressionQuality);  
  
          registerAttr("DECOMPRESSION_QUALITY",  
  
  (setAttrFunc)XilDeviceCompressionIdentity::setDecompressionQuality,  
  
  (getAttrFunc)XilDeviceCompressionIdentity::getDecompressionQuality);  
  }  
  
  XilDeviceCompressionTypeIdentity::~XilDeviceCompressionTypeIdentity(void) { }  

XilDeviceCompressionIdentity.h

Code Example 6-7 XilDeviceCompressionIdentity.h (1 of 3)

  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        XilDeviceCompressionIdentity.h  
  //  Project:     XIL  
  //  Created:     93/04/14  
  //  Revision:    1.2  
  //  Last Mod:    09:29:16, 22 Mar 1994  
  //  
  //  Description:  
  //  The file contains the definitions for Identity compression and  
  //  decompression.  Each Identity cis has its own instantiation of  
  //  this class.  
  //  
  //  The Identity bit stream has the following format:  
  //  
  //         [ 32-bit INTEGER ]    width  
  //         [ 32-bit INTEGER ]    height  
  //         [ 32-bit INTEGER ]    nbands  
  //         [ IMAGE DATA ]  
  //  
  //  NOTE:  The code included here to implement this bitstream  
  //  creates a compressed stream which is not portable between  
  //  different endian machines (i.e. x86 <--> SPARC).  
  //  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)XilDeviceCompressionIdentity.h1.2\t94/03/22  "  
  
  #ifndef XilDeviceCompressionIdentity_H  
  #define XilDeviceCompressionIdentity_H  
  
  #include <xil/XilError.h>  
  #include <xil/XilCis.h>  
  #include <xil/XilImage.h>  
  #include <xil/XilDeviceCompression.h>  
  
  #define FRAMES_PER_BUFFER  3  
  #define IDENTITY_FRAME_TYPE 1  

Code Example 6-7 XilDeviceCompressionIdentity.h (2 of 3)

  class XilDeviceCompressionIdentity : public XilDeviceCompression  
  {  
  public:  
      XilDeviceCompressionIdentity(XilDeviceCompressionType* xdct,  
                                   XilCis*                   cis);  
      ~XilDeviceCompressionIdentity(void);  
  
      int comp_quality;          //compression quality attribute  
      int decomp_quality;        // decompression quality attribute  
      Xil_boolean derivedType;   // flag for derived type from bitstream  
  
      //  
      //  Pure virtual member functions of XilDeviceCompression which I  
      //  must implement.  
      //  
      int            getMaxFrameSize(void);  
      void           burnFrames(int nframes);  
      int            findNextFrameBoundary(void);  
  
      //  Allocation/Creation verification member function  
      //  
      XilDeviceCompressionIdentity* ok(Xil_boolean destroy = TRUE);  
  
      //  
      //  Function to read header and fill in the header information --  
      //  specifically width and height  
      //  
      int   deriveOutputType(void);  
  
      //  
      //  Function to reset the codec state, destroy old inputType  
      //  
      voidreset();  
  
      //  
      //  Virtual member functions of XilDeviceCompression which I've  
      //  chosen to implement because the default functions do not work  
      //  for the Identity codec.  
      //  the Identity codec marks even frames with its own  
      //  frame type; this is done in order to illustrate how a codec  
      //  with typed frames would interface with the cbm  

Code Example 6-7 XilDeviceCompressionIdentity.h (3 of 3)

      void        seek(int framenumber, Xil_boolean history_update=TRUE);  
      int         adjustStart(int framenumber);  
  
      // functions for registered attribute set/get  
      void       setCompressionQuality(int value);  
      int        getCompressionQuality();  
      void       setDecompressionQuality(int value);  
      int        getDecompressionQuality();  
  
  private:  
      Xil_boolean     isok;  
  
      //  
      //  Function used by reset and the constructor to set values  
      //  
      int    initValues();  
  };  
  
  #endif  

XilDeviceCompressionIdentity.cc

Code Example 6-8 XilDeviceCompressionIdentity.cc (1 of 9)

  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        XilDeviceCompressionIdentity.cc  
  //  Project:     XIL  
  //  Created:     93/04/14  
  //  Revision:    1.3  
  //  Last Mod:    08:37:54, 28 Mar 1994  
  //  
  //  Description:  
  //  Contains the member functions of XilDeviceCompressionIdentity.  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //------------------------------------------------------------------------  
  #pragma ident    "@(#)XilDeviceCompressionIdentity.cc1.3\t94/03/28  "  
  
  #include "XilDeviceCompressionIdentity.h"  
  
  XilDeviceCompressionIdentity*  
  XilDeviceCompressionIdentity::ok(Xil_boolean destroy) {  
      if(this == NULL) {  
          return NULL;  
      } else {  
          if(XilDeviceCompression::ok(FALSE) == this && isok == TRUE) {  
              return this;  
          } else {  
              if(destroy == TRUE) delete this;  
              return NULL;  
          }  
      }  
  }  
  
  int            XilDeviceCompressionIdentity::getMaxFrameSize(void) {  
      return ((int)inputType->getWidth()*(int)inputType->getHeight()  
                *inputType->getBands() + 12);  

Code Example 6-8 XilDeviceCompressionIdentity.cc (2 of 9)

  }  
  
  int  
  XilDeviceCompressionIdentity::initValues()  
  {  
      XilImageType* t =  
          getCis()->getSystemState()->createImageType(0,0,0,XIL_BYTE);  
  
      XIL_SIMULATE_FAILURE(992, t=NULL);  
      if(t == NULL) {  
          // out of memory  
          XIL_ERROR(getCis()->getSystemState(), XIL_ERROR_RESOURCE,"di-1",TRUE);  
          return XIL_FAILURE;  
      }  
  
      inputType = outputType = t;  
  
      // output type has not yet been derived from bitstream  
      derivedType = FALSE;  
  
      // reset any attributes to default state  
      comp_quality = 0;  
      decomp_quality = 0;  
  
      return XIL_SUCCESS;  
  }  
  
  void  
  XilDeviceCompressionIdentity::reset()  
  {  
      if (inputType != outputType)  
       outputType->destroy();  
      inputType->destroy();  
  
      initValues();  
      XilDeviceCompression::reset();  
  }  
  
  //  
  //  FRAMES_PER_BUFFER is a recommendation on the size of each buffer inside the  
  //  CBM.  
  //  

Code Example 6-8 XilDeviceCompressionIdentity.cc (3 of 9)

  XilDeviceCompressionIdentity::XilDeviceCompressionIdentity  
        (XilDeviceCompressionType* xdct,XilCis* cis)  
  : XilDeviceCompression(xdct, cis, 0, FRAMES_PER_BUFFER)  
  {  
      isok = FALSE;  
  
      if(XilDeviceCompression::ok(FALSE) == NULL) {  
          //  Couldn't create internal base XilDeviceCompression object  
          XIL_ERROR(getCis()->getSystemState(), XIL_ERROR_SYSTEM,"di-278",  
  FALSE);  
          return;  
      }  
  
      if(initValues() == XIL_FAILURE) {  
          //  Couldn't create internal Identity compressor object  
          XIL_ERROR(getCis()->getSystemState(), XIL_ERROR_SYSTEM,"di-275",  
  FALSE);  
          return;  
      }  
  
      isok = TRUE;  
  }  
  
  XilDeviceCompressionIdentity::~XilDeviceCompressionIdentity(void) { }  
  
  //  
  //  Function to read header and fill in the ImageType information  
  //  
  int  
  XilDeviceCompressionIdentity::deriveOutputType(void)  
  {  
     // derivedType flags if the type has been derived from  
     // the bitstream--prevents an infinite loop when neither the  
     // boundary nor type of the first frame in the CIS have been  
     // established  
      if (derivedType == FALSE) {  
         //  
         //  This call will ensure that there is an entire frame for me to  
         //  look through.  If necessary, the cbm will call this class's  
         //  findNextFrameBoundary to parse the bitstream and  
         //  locate the end of the frame.  

Code Example 6-8 XilDeviceCompressionIdentity.cc (4 of 9)

         //  
         Xil_unsigned32* bp32 =  
           (Xil_unsigned32*)cbm.nextFrame();  
         if(bp32 == NULL) {  
            return XIL_FAILURE;  
         }  
  
         //  
         //  NOTE:  This doesn't produce an endian-portable bitstream.  
         //  
         unsigned int image_width  = *bp32++;  
         unsigned int image_height = *bp32++;  
         unsigned int image_bands  = *bp32++;  
  
         if(image_width && image_height && image_bands) {  
            XilImageType*  newtype =  
           cis->getSystemState()->createImageType(image_width, image_height,  
                                                     image_bands, XIL_BYTE);  
  
            XIL_SIMULATE_FAILURE(993, newtype=NULL);  
            if(newtype == NULL) {  
               // out of memory  
               XIL_ERROR(getCis()->getSystemState(), XIL_ERROR_RESOURCE,"di-  
  1",TRUE);  
               return XIL_FAILURE;  
            }  
  
            //  
            //  This will also set the outputType as a side-effect  
            //  
            setInputType(newtype);  
            newtype->destroy();// destroy copy  
            derivedType=TRUE;  
         }  
      }  
      return XIL_SUCCESS;  
  }  
  
  void  
  XilDeviceCompressionIdentity::burnFrames(int nframes)  
  {  

Code Example 6-8 XilDeviceCompressionIdentity.cc (5 of 9)

      int frame_type;  
  
      // In order to illustrate "key" frames,  
      // this codec marks even frames with its own frame type  
      // This illustrates the use of frame type with the  
      // compressedFrame/decompressFrame/seek/adjustStart functions  
      // (Of course, codecs generally have a much better reason  
      // to mark a frame as a "key" frame!)  
  
      //  
      //  Get the information about the CIS image type.  
      //  
      XilImageType*  cis_outtype    = getOutputType();  
      unsigned int   cis_width      = cis_outtype->getWidth();  
      unsigned int   cis_height     = cis_outtype->getHeight();  
      unsigned int   cis_bands      = cis_outtype->getBands();  
  
      //  
      //  Compute how far the next frame should be...  
      //  
      unsigned long  frame_size =  
          cis_width*cis_height*cis_bands + 3*sizeof(Xil_unsigned32);  
  
      for(int i=0; i<nframes; i++) {  
  
         Xil_unsigned8* bp = (Xil_unsigned8*)cbm.nextFrame();  
  
         // Get the frame number of the burn frame  
         if (cbm.getRFrameId() & 0x1)  
           // odd frame, no special frame type  
           frame_type = XIL_CIS_DEFAULT_FRAME_TYPE;  
         else  
           // even frame, mark it as our key frame  
           frame_type = IDENTITY_FRAME_TYPE;  
  
         bp += frame_size;  
         cbm.decompressedFrame(bp,frame_type);  
      }  
  }  

Code Example 6-8 XilDeviceCompressionIdentity.cc (6 of 9)

  //  
  //  Function to find the next frame boundary  
  //  
  int  
  XilDeviceCompressionIdentity::findNextFrameBoundary(void)  
  {  
      Xil_unsigned8* bp;  
      unsigned long  frame_size;  
  
      if (derivedType==FALSE) {  
         unsigned int   image_dimensions[3] = {0,0,0};  
         unsigned int   i,j;  
  
         // not yet derived input/output type  
         // cannot call getOutputType because we will recurse on this function!  
         // parse bitstream bytes to get width/height/bands  
         for (i=0;i<3;i++) {  
            for (j=0;j<sizeof(Xil_unsigned32);j++) {  
               if ((bp=cbm.getNextByte())==NULL)  
                 // here if no more bytes in buffer--failed!  
                 return XIL_FAILURE;  
               // accumulate bytes into current dimension  
               image_dimensions[i] = (image_dimensions[i]*256) + *bp;  
            }  
         }  
         //  
         //  Compute how far we have to advance the pointer.  
         //  
         frame_size =  
           image_dimensions[0]*image_dimensions[1]*image_dimensions[2];  
      }  
      else {  
         unsigned int   image_width;  
         unsigned int   image_height;  
         unsigned int   image_bands;  
         //  
         //  Get the information about the CIS image type.  
         //  will cause deriveOutputType() to be called if  
         //  outputType not yet established.  
         //  
         XilImageType*  cis_outtype    = getOutputType();  
         image_width      = cis_outtype->getWidth();  

Code Example 6-8 XilDeviceCompressionIdentity.cc (7 of 9)

         image_height     = cis_outtype->getHeight();  
         image_bands      = cis_outtype->getBands();  
  
         //  
         //  Compute how far we have to advance the pointer.  
         //  
         frame_size =  
           image_width*image_height*image_bands + 3*sizeof(Xil_unsigned32);  
      }  
  
      //  
      //  Run through the frame one byte at a time up to the second to  
      //  last byte in the frame.  The final byte will be set to the  
      //  return value of getNextByte() -- as opposed to updating it  
      //  on every cycle of the loop.  
      //  
  
      for(int i=0; i<frame_size - 1; i++) {  
          if(cbm.getNextByte() == NULL)     return XIL_FAILURE;  
      }  
      if((bp = cbm.getNextByte()) == NULL)  return XIL_FAILURE;  
  
      //  
      //  Tell the CisBufferManager where the frame boundary is...  
      //  
      return cbm.foundNextFrameBoundary(bp + 1);  
  }  
  
  void  
  XilDeviceCompressionIdentity::seek(int framenumber, Xil_boolean  
  history_update)  
  {  
      int frames_to_burn;  
  
      if (history_update == TRUE)  
        // when history_update is true, if we have key frames  
        // then we must seek with respect to the key frame.  
        // The "frames_to_burn" returned by the cbm  
        // will start from a key frame, which means our history remains  
        // correct  
        frames_to_burn = cbm.seek(framenumber, IDENTITY_FRAME_TYPE);  
      else  

Code Example 6-8 XilDeviceCompressionIdentity.cc (8 of 9)

        // when history_update is false, then we are interested  
        // in position only for this seek.  Flag the cbm that  
        // there should be no burn frames for frame type.  
        frames_to_burn = cbm.seek(framenumber, XIL_CIS_NO_BURN_TYPE);  
  
      if(frames_to_burn > 0) {  
       burnFrames(frames_to_burn);  
      }  
  }  
  
  int  
  XilDeviceCompressionIdentity::adjustStart(int new_start_frame)  
  {  
     //Called by the compression core to indicate that existing  
     //frames prior to the frame number given are not to be retained  
     //any longer due to KEEPFRAMES or MAXFRAMES requirements.  
     //We'll just simply call the XilCisBufferManager and tell it  
     //which type of frame MUST be kept and let it do any actual  
     //deleting of data.  
  
      return cbm.adjustStart(new_start_frame, IDENTITY_FRAME_TYPE);  
  }  
  
  // NOTE: the Identity codec does not make use of  
  // "quality" ...these functions are here to illustrate  
  // the XilDeviceCompressionIdentityType registerAttr mechanism.  
  
  void  
  XilDeviceCompressionIdentity::setCompressionQuality(int value)  
  {  
     comp_quality = value;  
  }  
  
  int  
  XilDeviceCompressionIdentity::getCompressionQuality()  
  {  
     return comp_quality;  
  }  
  
  void  
  XilDeviceCompressionIdentity::setDecompressionQuality(int value)  

Code Example 6-8 XilDeviceCompressionIdentity.cc (9 of 9)

  {  
     decomp_quality = value;  
  }  
  
  int  
  XilDeviceCompressionIdentity::getDecompressionQuality()  
  {  
     return decomp_quality;  
  }  

XilDeviceComputeTypeIdentityMemory.h

Code Example 6-9 XilDeviceComputeTypeIdentityMemory.h

  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        XilDeviceComputeTypeIdentityMemory.h  
  //  Project:     XIL  
  //  Created:     93/04/15  
  //  Revision:    1.2  
  //  Last Mod:    09:32:01, 22 Mar 1994  
  //  
  //  Description:  
  //  Contains the definitions of the derived XilDeviceComputeType  
  //  for Identity compression and decompression.  
  //  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)XilDeviceComputeTypeIdentityMemory.h1.2\t94/03/22  "  
  
  #include <xil/XilDeviceComputeType.h>  
  #include "XilDeviceCompressionIdentity.h"  
  
  class XilDeviceComputeTypeIdentityMemory : public XilDeviceComputeType {  
  public:  
      XilDeviceComputeTypeIdentityMemory()  
      : XilDeviceComputeType("XilDeviceCompIdentityMemory") {};  
  
      int describeMembers();  
  
      //  
      //  Compress  
      //  
      int  compress_Identity(XilOp* op, int op_count);  
  
      //  
      //  Decompress  
      //  
      int  decompress_Identity(XilOp* op, int op_count);  
  
      ~XilDeviceComputeTypeIdentityMemory();  
  };  

XilDeviceComputeTypeIdentityMemory.cc

Code Example 6-10 XilDeviceComputeTypeIdentityMemory.cc


  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        XilDeviceComputeTypeIdentityMemory.cc  
  //  Project:     XIL  
  //  Created:     93/04/15  
  //  Revision:    1.2  
  //  Last Mod:    09:32:03, 22 Mar 1994  
  //  
  //  Description:  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)XilDeviceComputeTypeIdentityMemory.cc1.2\t94/03/22  "  
  
  #include <xil/xili.h>  
  #include "XilDeviceComputeTypeIdentityMemory.h"  
  
  XilDeviceComputeType* XilCreateComputeType()  
  {  
      XilDeviceComputeTypeIdentityMemory* device;  
  
      device= new XilDeviceComputeTypeIdentityMemory();  
  
      XIL_SIMULATE_FAILURE(942, delete device;device=NULL);  
      if(device==NULL) {  
       // out of memory error  
       XIL_ERROR( NULL, XIL_ERROR_RESOURCE,"di-1",TRUE);  
       return NULL;  
      }  
  
      device->describeMembers();  
  
      return(device);  

Code Example 6-10 XilDeviceComputeTypeIdentityMemory.cc (Continued)


  }  
  
  XilDeviceComputeTypeIdentityMemory::~XilDeviceComputeTypeIdentityMemory()  
  {  
  }  

compress_Identity.cc

Code Example 6-11 compress_Identity.cc  (1 of 4)


  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        compress_Identity.cc  
  //  Project:     XIL  
  //  Created:     93/04/15  
  //  Revision:    1.2  
  //  Last Mod:    09:32:32, 22 Mar 1994  
  //  
  //  Description:  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)compress_Identity.cc1.2\t94/03/22  "  
  
  #include <xil/XilRoi.h>  
  #include <xil/XilRoiList.h>  
  #include <xil/XilOp.h>  
  #include "XilDeviceComputeTypeIdentityMemory.h"  
  #include "XilDeviceCompressionIdentity.h"  
  
  /* XILCONFIG: compress_Identity= compress_Identity()  */  
  int  
  XilDeviceComputeTypeIdentityMemory::compress_Identity(XilOp* op, int)  
  {  
     int frame_type;  
  
      //  
      //  Get the source image off of the DAG.  
      //  
      XilImage* src = op->getSrc1();  

Code Example 6-11 compress_Identity.cc  (2 of 4)


      //  
      //  Get the XilDeviceCompression associated with this CIS  
      //  
      XilDeviceCompressionIdentity* dc = (XilDeviceCompressionIdentity*)  
       (op->getDstCis())->getDeviceCompression();  
  
      //  
      //  Get the system state.  
      //  
      XilSystemState* systemState = src->getSystemState();  
  
      // In order to illustrate "key" frames,  
      // this codec marks even frames with its own frame type  
      // This illustrates the use of frame type with the  
      // compressedFrame/decompressFrame/seek/adjustStart functions  
      // (Of course, codecs generally have a much better reason  
      // to mark a frame as a "key" frame!)  
  
      // Get the frame number of the compress  
      if (op->getLongParam(1) & 0x1)  
        // odd frame, no special frame type  
        frame_type = XIL_CIS_DEFAULT_FRAME_TYPE;  
      else  
        // even frame, mark it as our key frame  
        frame_type = IDENTITY_FRAME_TYPE;  
  
      //  
      //  Local copies of image type information.  
      //  
      XilImageType*  cis_intype = dc->getOutputType();  
      unsigned int   cis_width  = cis_intype->getWidth();  
      unsigned int   cis_height = cis_intype->getHeight();  
      unsigned int   cis_bands  = cis_intype->getBands();  
  
      //  
      //  No ROI clipping or non-zero origins are allowed for compressions.  
      //  Also, the image width and  image height must match the size of the CIS.  
      //  Both of these conditions are checked in XilCis::compress().  So, no  
      //  check is required here.  
      //  

Code Example 6-11 compress_Identity.cc  (3 of 4)


      //  
      //  Get the next buffer to compress into.  
      //  
      XilCisBuffer* cisbuf = dc->getCisBufferManager()->nextBuffer();  
  
      //  
      //  Write the image parameters into the byte-stream  
      //  
      cisbuf->addBytes((Xil_unsigned8*)&cis_width, sizeof(cis_width));  
      cisbuf->addBytes((Xil_unsigned8*)&cis_height, sizeof(cis_height));  
      cisbuf->addBytes((Xil_unsigned8*)&cis_bands, sizeof(cis_bands));  
  
      //  
      //  Get the source image's memory storage.  
      //  
      long x_origin, y_origin;  
      src->getOrigin(&x_origin,&y_origin);  
  
      //  
      //  Actually perform the compression into the CisBuffer  
      //  
      XilMemoryStorageByte* src_mem =  
          (XilMemoryStorageByte*)src->getMemoryStorage();  
  
      Xil_unsigned8* src_data        = src_mem->data;  
  
      Xil_unsigned8* src_scanline =  
          src_mem->data +  
          (y_origin * src_mem->scanline_stride) +  
          (x_origin * src_mem->pixel_stride);  
  
      Xil_unsigned8* src_pixel;  
      Xil_unsigned8* src_band;  
  
      for(int i=0; i<cis_height; i++) {  
          src_pixel = src_scanline;  
          for(int j=0; j<cis_width; j++) {  
              src_band = src_pixel;  
              for(int k=0; k<cis_bands; k++) {  
                  cisbuf->addByte(*src_band);  
                  src_band++;  
              }  

Code Example 6-11 compress_Identity.cc  (4 of 4)


              src_pixel += src_mem->pixel_stride;  
          }  
          src_scanline += src_mem->scanline_stride;  
      }  
  
      dc->getCisBufferManager()->compressedFrame(frame_type);  
  
      return XIL_SUCCESS;  
  }  

decompress_Identity.cc

Code Example 6-12 decompress_Identity.cc  (1 of 4)


  //This line lets emacs recognize this as -*- C++ -*- Code  
  //------------------------------------------------------------------------  
  //  
  //  File:        decompress_Identity.cc  
  //  Project:     XIL  
  //  Created:     93/04/15  
  //  Revision:    1.2  
  //  Last Mod:    09:33:15, 22 Mar 1994  
  //  
  //  Description:  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //  
  //------------------------------------------------------------------------  
  #pragma ident"@(#)decompress_Identity.cc1.2\t94/03/22  "  
  
  #include <xil/XilOp.h>  
  #include <xil/XilDefines.h>  
  #include "XilDeviceComputeTypeIdentityMemory.h"  
  
  /* XILCONFIG: decompress_Identity= decompress_Identity()  */  
  
  #define IDENTITY_BYTESTREAM_ERROR(bp,ftype) \  
  { \  
     dc->getCisBufferManager()->decompressedFrame((Xil_unsigned8*)bp,ftype); \  
     XIL_CIS_ERROR(XIL_ERROR_SYSTEM, "di-285", TRUE, dc, FALSE, FALSE); \  
     return XIL_FAILURE; \  
  }  
  
  int  
  XilDeviceComputeTypeIdentityMemory::decompress_Identity(XilOp* op, int)  
  {  
      int frame_type;  

Code Example 6-12 decompress_Identity.cc  (2 of 4)


      //  
      //  Get the destination image off of the DAG  
      //  
      XilImage*                 dst = op->getDst();  
  
      //  
      //  Get the XilDeviceCompression associated with this CIS  
      //  
      XilDeviceCompressionIdentity* dc  = (XilDeviceCompressionIdentity*)  
       (op->getSrcCis())->getDeviceCompression();  
  
      //  
      //  The frame number which we're supposed to decompress is  
      //  specified by the first parameter on the Op.  So, we'll seek to  
      //  that frame.  
      //  
      dc->seek((int)op->getLongParam(1));  
  
      // In order to illustrate "key" frames,  
      // this codec marks even frames with its own frame type  
      // This illustrates the use of frame type with the  
      // compressedFrame/decompressFrame/seek/adjustStart functions  
      // (Of course, codecs generally have a much better reason  
      // to mark a frame as a "key" frame!)  
  
      // Test odd/even frame for decompress  
      if (op->getLongParam(1) & 0x1)  
        // odd frame, no special frame type  
        frame_type = XIL_CIS_DEFAULT_FRAME_TYPE;  
      else  
        // even frame, mark it as our key frame  
        frame_type = IDENTITY_FRAME_TYPE;  
  
      //  
      //  Get the information about the CIS image type.  
      //  
      XilImageType*  cis_outtype = dc->getOutputType();  
      unsigned int   cis_width      = cis_outtype->getWidth();  
      unsigned int   cis_height     = cis_outtype->getHeight();  
      unsigned int   cis_bands      = cis_outtype->getBands();  

Code Example 6-12 decompress_Identity.cc  (3 of 4)


      //  
      //  Get the pointer to the data to decompress...  
      //  
      Xil_unsigned32* bp32 =  
          (Xil_unsigned32*) dc->getCisBufferManager()->nextFrame();  
  
      if(bp32 == NULL) {  
       // XilCis: No data to decompress  
          XIL_CIS_ERROR(XIL_ERROR_SYSTEM, "di-100", TRUE, dc, FALSE, FALSE);  
       return XIL_FAILURE;  
      }  
  
      //  
      //  Just in case we've had an error before, we don't want to  
      //  SEGV trying to access a word when non-word aligned  
      //  
      if((int)bp32 % sizeof(unsigned int)) {  
          IDENTITY_BYTESTREAM_ERROR(bp32,frame_type);  
      }  
      if(*bp32++ != cis_width) {  
          IDENTITY_BYTESTREAM_ERROR(bp32,frame_type);  
      }  
      if(*bp32++ != cis_height) {  
          IDENTITY_BYTESTREAM_ERROR(bp32,frame_type);  
      }  
      if(*bp32++ != cis_bands) {  
          IDENTITY_BYTESTREAM_ERROR(bp32,frame_type);  
      }  
  
      Xil_unsigned8* bp = (Xil_unsigned8*) bp32;  
  
      //  
      //  Get the destination image's origin  
      //  
      long x_origin, y_origin;  
      dst->getOrigin(&x_origin,&y_origin);  
  
      //  
      //  Get the destination image's memory storage.  
      //  
      XilMemoryStorageByte* dst_mem =  
          (XilMemoryStorageByte*)dst->getMemoryStorage();  

Code Example 6-12 decompress_Identity.cc  (4 of 4)


      Xil_unsigned8* dst_data        = dst_mem->data;  
  
      Xil_unsigned8* dst_scanline =  
          dst_mem->data +  
          (y_origin * dst_mem->scanline_stride) +  
          (x_origin * dst_mem->pixel_stride);  
  
      Xil_unsigned8* dst_pixel;  
      Xil_unsigned8* dst_band;  
  
      for(int i=0; i<cis_height; i++) {  
          dst_pixel = dst_scanline;  
          for(int j=0; j<cis_width; j++) {  
              dst_band = dst_pixel;  
              for(int k=0; k<cis_bands; k++) {  
                  *dst_band++ = *bp++;  
              }  
              dst_pixel += dst_mem->pixel_stride;  
          }  
          dst_scanline += dst_mem->scanline_stride;  
      }  
  
      dc->getCisBufferManager()->decompressedFrame(bp,frame_type);  
  
      return XIL_SUCCESS;  
  }