Added JpegCompressor abstract class and two implementations -- one cross-platform and another IRIX-specific.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2320 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/IrixDMIC_RawToJpeg.cxx b/common/rfb/IrixDMIC_RawToJpeg.cxx
new file mode 100644
index 0000000..6b76304
--- /dev/null
+++ b/common/rfb/IrixDMIC_RawToJpeg.cxx
@@ -0,0 +1,423 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+
+#include <rfb/IrixDMIC_RawToJpeg.h>
+
+using namespace rfb;
+
+//
+// Constructor. Find a converter and create a context. Also, create
+// DMparams structures for using with dmICSetSrcParams,
+// dmICSetDstParams and dmICSetConvParams.
+//
+
+IrixDMIC_RawToJpeg::IrixDMIC_RawToJpeg(bool needRealtime)
+  : m_valid_ic(false),
+    m_srcParams(0),
+    m_dstParams(0),
+    m_convParams(0)
+{
+  if ( dmParamsCreate(&m_srcParams) != DM_SUCCESS ||
+       dmParamsCreate(&m_dstParams) != DM_SUCCESS ||
+       dmParamsCreate(&m_convParams) != DM_SUCCESS ) {
+    reportError("dmParamsCreate");
+    return;
+  }
+
+  DMparams *p;
+  const int JPEG_ID = 0x6A706567; // same as 'jpeg'
+  int id, direction, speed;
+
+  int n = dmICGetNum();
+  while (n--) {
+    if (dmParamsCreate(&p) != DM_SUCCESS) {
+      reportError("dmParamsCreate");
+      return;
+    }
+    if (dmICGetDescription(n, p) != DM_SUCCESS)
+      continue;
+    id = dmParamsGetInt(p, DM_IC_ID);
+    direction = dmParamsGetEnum(p, DM_IC_CODE_DIRECTION);
+    speed = dmParamsGetEnum(p, DM_IC_SPEED);
+    if ( id == JPEG_ID && direction == DM_IC_CODE_DIRECTION_ENCODE &&
+	 (!needRealtime || speed == DM_IC_SPEED_REALTIME) ) {
+
+      // FIXME: Just save the engine name and make x0vncserver print it.
+      const char *engine = dmParamsGetString(p, DM_IC_ENGINE);
+      printf("Found JPEG encoder: \"%s\" (%d)\n", engine, n);
+
+      dmParamsDestroy(p);
+      break;
+    }
+    dmParamsDestroy(p);
+  }
+  if (n < 0) {
+    // FIXME: Unify error handling.
+    fprintf (stderr, "Error: No matching JPEG encoder found\n");
+    return;
+  }
+  if (dmICCreate(n, &m_ic) != DM_SUCCESS) {
+    reportError("dmICCreate");
+    return;
+  }
+
+  m_valid_ic = true;
+}
+
+//
+// Destructor.
+//
+
+IrixDMIC_RawToJpeg::~IrixDMIC_RawToJpeg()
+{
+  if (m_valid_ic)
+    dmICDestroy(m_ic);
+
+  if (m_convParams)
+    dmParamsDestroy(m_convParams);
+  if (m_dstParams)
+    dmParamsDestroy(m_dstParams);
+  if (m_srcParams)
+    dmParamsDestroy(m_srcParams);
+}
+
+//
+// Configure the source and destination formats.
+//
+// FIXME: Remember image size that was previously set, do not set the
+//        same size again.
+//
+
+bool
+IrixDMIC_RawToJpeg::setImageParams(int w, int h)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  // Set source image parameters.
+  DMpacking packing = DM_IMAGE_PACKING_XBGR;
+  int orient = DM_IMAGE_TOP_TO_BOTTOM;
+  if (dmSetImageDefaults(m_srcParams, w, h, packing) != DM_SUCCESS) {
+    reportError("dmSetImageDefaults");
+    return false;
+  }
+  DMstatus err = dmParamsSetEnum(m_srcParams, DM_IMAGE_ORIENTATION, orient);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetEnum");
+    return false;
+  }
+  if (dmICSetSrcParams(m_ic, m_srcParams) != DM_SUCCESS) {
+    reportError("dmICSetSrcParams");
+    return false;
+  }
+
+  // Set destination image parameters.
+  packing = DM_IMAGE_PACKING_CbYCrY;
+  const char *compression = DM_IMAGE_JPEG;
+  if (dmSetImageDefaults(m_dstParams, w, h, packing) != DM_SUCCESS) {
+    reportError("dmSetImageDefaults");
+    return false;
+  }
+  err = dmParamsSetEnum(m_dstParams, DM_IMAGE_ORIENTATION, orient);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetEnum");
+    return false;
+  }
+  err = dmParamsSetString(m_dstParams, DM_IMAGE_COMPRESSION, compression);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetString");
+    return false;
+  }
+  if (dmICSetDstParams(m_ic, m_dstParams) != DM_SUCCESS) {
+    reportError("dmICSetDstParams");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Set JPEG image quality level (an integer in the range 0..99).
+//
+// FIXME: Remember image quality that was previously set, do not set
+//        the same quality again.
+//
+
+bool
+IrixDMIC_RawToJpeg::setImageQuality(int quality)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  double qf = (double)quality / 100.0;
+
+  DMstatus err = dmParamsSetFloat(m_convParams, DM_IMAGE_QUALITY_SPATIAL, qf);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetFloat");
+    return false;
+  }
+  if (dmICSetConvParams(m_ic, m_convParams) != DM_SUCCESS) {
+    reportError("dmICSetConvParams");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Set up source buffer pool.
+//
+// NOTE: Both setImageParams() and setImageQuality() functions should
+//       be called prior to creating and registering buffer pools.
+//
+
+bool
+IrixDMIC_RawToJpeg::createSrcBufferPool(DMbufferpool *pool,
+                                        int bufCount, int bufSize)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  DMparams *p;
+  if (dmParamsCreate(&p) != DM_SUCCESS) {
+    reportError("dmParamsCreate");
+    return false;
+  }
+
+  if (dmBufferSetPoolDefaults(p, bufCount, bufSize, DM_FALSE, DM_TRUE) !=
+      DM_SUCCESS) {
+    reportError("dmBufferSetPoolDefaults");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmICGetSrcPoolParams(m_ic, p) != DM_SUCCESS) {
+    reportError("dmICGetSrcPoolParams");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmBufferCreatePool(p, pool) != DM_SUCCESS) {
+    reportError("dmBufferCreatePool");
+    dmParamsDestroy(p);
+    return false;
+  }
+
+  dmParamsDestroy(p);
+  return true;
+}
+
+//
+// Set up and register destination buffer pool.
+//
+// NOTE: Both setImageParams() and setImageQuality() functions should
+//       be called prior to creating and registering buffer pools.
+//
+
+bool
+IrixDMIC_RawToJpeg::registerDstBufferPool(DMbufferpool *pool,
+                                          int bufCount, int bufSize)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  DMparams *p;
+  if (dmParamsCreate(&p) != DM_SUCCESS) {
+    reportError("dmParamsCreate");
+    return false;
+  }
+
+  if (dmBufferSetPoolDefaults(p, bufCount, bufSize, DM_FALSE, DM_TRUE) !=
+      DM_SUCCESS) {
+    reportError("dmBufferSetPoolDefaults");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmICGetDstPoolParams(m_ic, p) != DM_SUCCESS) {
+    reportError("dmICGetDstPoolParams");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmBufferCreatePool(p, pool) != DM_SUCCESS) {
+    reportError("dmBufferCreatePool");
+    dmParamsDestroy(p);
+    return false;
+  }
+
+  dmParamsDestroy(p);
+
+  if (dmICSetDstPool(m_ic, *pool) != DM_SUCCESS) {
+    reportError("dmICSetDstPool");
+    destroyBufferPool(*pool);
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Destroy buffer pool created with either createSrcBufferPool() or
+// registerDstBufferPool().
+//
+
+void
+IrixDMIC_RawToJpeg::destroyBufferPool(DMbufferpool pool)
+{
+  if (dmBufferDestroyPool(pool) != DM_SUCCESS)
+    reportError("dmBufferDestroyPool");
+}
+
+//
+// Allocate a buffer from the specified pool.
+//
+
+bool
+IrixDMIC_RawToJpeg::allocBuffer(DMbuffer *pbuf, DMbufferpool pool)
+{
+  if (dmBufferAllocate(pool, pbuf) != DM_SUCCESS) {
+    reportError("dmBufferAllocate");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Fill in a DMbuffer with data.
+//
+// NOTE: The caller must make sure that the buffer size is no less
+//       than dataSize.
+//
+
+bool
+IrixDMIC_RawToJpeg::copyToBuffer(DMbuffer buf, const void *data, int dataSize)
+{
+  void *bufPtr = dmBufferMapData(buf);
+  memcpy(bufPtr, data, dataSize);
+
+  if (dmBufferSetSize(buf, dataSize) != DM_SUCCESS) {
+    reportError("dmBufferSetSize");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Map DMbuffer to physical memory.
+//
+
+void *
+IrixDMIC_RawToJpeg::mapBufferData(DMbuffer buf)
+{
+  return dmBufferMapData(buf);
+}
+
+//
+// Get the number of valid bytes in DMbuffer.
+//
+
+int
+IrixDMIC_RawToJpeg::getBufferSize(DMbuffer buf)
+{
+  return dmBufferGetSize(buf);
+}
+
+//
+// Free DMbuffer.
+//
+
+void
+IrixDMIC_RawToJpeg::freeBuffer(DMbuffer buf)
+{
+  if (dmBufferFree(buf) != DM_SUCCESS)
+    reportError("dmBufferFree");
+}
+
+//
+// Send input data (raw pixels) to the converter.
+//
+
+bool
+IrixDMIC_RawToJpeg::sendData(DMbuffer buf)
+{
+  if (dmICSend(m_ic, buf, 0, NULL) != DM_SUCCESS) {
+    reportError("dmICSend");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Wait until compression is finished (infinite timeout!).
+// This function should be called after sendData() and before receiveData().
+//
+// FIXME: Report errors.
+//
+
+bool
+IrixDMIC_RawToJpeg::waitConversion()
+{
+  struct pollfd ps;
+  ps.fd = dmICGetDstQueueFD(m_ic);
+  ps.events = POLLIN;
+
+  int result = poll(&ps, 1, -1);
+  if (result != 1)
+    return false;
+
+  if ((ps.revents & POLLIN) != 0) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+//
+// Receive output (JPEG data) from the converter.
+// Call waitConversion() function first.
+//
+
+bool
+IrixDMIC_RawToJpeg::receiveData(DMbuffer *pbuf)
+{
+  if (dmICReceive(m_ic, pbuf) != DM_SUCCESS) {
+    reportError("dmICReceive");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Report an error when a function returns DM_FAILURE.
+//
+
+void
+IrixDMIC_RawToJpeg::reportError(const char *funcName)
+{
+  char errorDetail[DM_MAX_ERROR_DETAIL];
+  const char *errorCategory = dmGetError(NULL, errorDetail);
+  fprintf(stderr, "%s() failed: %s: %s\n",
+	  funcName, errorCategory, errorDetail);
+}
+
+//
+// Report an error when (m_valid_ic == false).
+//
+
+void
+IrixDMIC_RawToJpeg::reportErrorNotInited()
+{
+  fprintf(stderr, "Internal error: Image converter not initialized\n");
+}
+