Logo Search packages:      
Sourcecode: camstream version File versions  Download package


  \class CCamStreamApp
  \brief The CamStream main application class.

  This class is the main object for the CamStream application. It does
  the following:
   - creates the canvas, loads widgets;
   - determines available image file formats;
   - stores and retrieves user settings;

  The configuration is stored as an XML document. The structure still
  has to be defined fully, and is partially defined by sub-elements.


#include <config.h>

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <qdir.h>
#include <qfile.h>
#include <qimage.h>
#include <qmessagebox.h>
#include <qstring.h>
#include <qstrlist.h>
#include <qtextstream.h>

#include "CamStreamApp.h"

static const char *conf_file = ".camstream";
static const char *upload_dir = ".camstream-upload";

/* This must follow FileTypeEnum! */
static struct {
  const char *extension;
  const char *format;
} FileTypes[] = {
  { "jpg", "JPEG" },
  { "png", "PNG" },
  { "ppm", "PPM" },
  { "bmp", "BMP" },

00051 CCamStreamApp *CamApp = NULL; /* should be the only one */

 \fn CCamStreamApp::CCamStreamApp(int argc, char *argv[])
 \brief Constructor
 Takes the \b argc and \b argv arguments from main().

00060 CCamStreamApp::CCamStreamApp(int argc, char *argv[])
      : QApplication(argc, argv)
   QString fullname;
   QDomDocument DD("Configuration");
   QDir Home;
   assert(CamApp == NULL);
   CamApp = this;

   Configuration.Root = DD; // Weird but true; if we just use an empty initialized DomDocument, it doesn't work.
   VideoDevices = CVideoCollector::Instance();


   /* Set program wide defaults */

   fullname = QDir::homeDirPath();
   UploadDir = fullname;

   Home = QDir::home();

   if (!Home.exists(UploadDir)) {   
     if (!Home.mkdir(UploadDir)) {
       switch(QMessageBox::warning(NULL, "upload directory", 
              "Failed to create temporary upload directory " + UploadDir,
              QMessageBox::Ok    | QMessageBox::Default, 
              QMessageBox::Abort | QMessageBox::Escape)) {
          case QMessageBox::Ok:
            UploadDir = "";
          case QMessageBox::Abort: // Escape

   /* Read user settings */

 \fn CCamStreamApp::~CCamStreamApp()
 \brief Destructor
 End of program; saves user settings (image formats, etc)

00115 CCamStreamApp::~CCamStreamApp()

// private

/** Determine available formats for saving files, set basic filename and text color */
00123 void CCamStreamApp::InitFileTypes()
   /* A friend of mine once told me this story:
      At work they used to have a mainframe or other heavy Unix machine
      that had a switch, labeled "Magic" and "More magic" (a simple,
      dual flip switch like an on-off button). Whenever they put the
      switch in the "Magic" position, the machine would crash instantly.
      In the "More magic" position it worked fine. Now for the weird part:
      there was only a single wire leading from the switch to the mainboard,
      and there it was connected to Ground. In other words: this wire was
      electrically speaking just floating in the air, with a switch on the
      end that wasn't connected to anything. Yet, if it was not in the
      right position, it would crash the machine.
      (Needless to say, this reeks of an Urban Legend)
   QStrList lit;
   QStrListIterator *it;
   char *s;
   int c;

   snap_bitmask = 0;
   lit = QImage::outputFormats();
   it = new QStrListIterator(lit);
   s = it->toFirst();
   while (s != NULL) {
      for (c = 0; c < file_MAX; c++) {
         if (!strcmp(s, FileTypes[c].format))
           snap_bitmask |= (1 << c);
      s = it->current();

void CCamStreamApp::ReadConfigFile()
   QDomNode n;
   QDomElement e;
   QDomDocument oldconfig;

   if (!ConfigFile.open(IO_ReadOnly))
     qWarning("Failed to open configuration file for reading.\n");
   else {
     if (!oldconfig.setContent(&ConfigFile))
       qWarning("Failed to parse configuration file.\n");

   // Create skeleton, and topnodes of configuration
   QDomElement config = Configuration.Root.documentElement();
   if (config.isNull())
     config = Configuration.Root.createElement("config");

   Configuration.Defaults = Configuration.Root.createElement("defaults");
   Configuration.VideoDevices = Configuration.Root.createElement("videodevices");

   // Walk elements, interpreting nodes
   n = oldconfig.documentElement().firstChild();
   while (!n.isNull()) {
      e = n.toElement(); // try to make it an Element
      if (!e.isNull()) { // Success... but what is it?
        if (e.tagName() == "defaults") { // Program defaults
//qDebug("Setting Configuration.Defaults");
          Configuration.Defaults = e; // for now

        if (e.tagName() == "videodevices") {
          /* Configuration of video devices; basicly, we walk the existing tree,
             duplication nodes and appending them to Configuration.VideoDevices.
             This way new variables get in, and old ones disappear.
          QDomNode vn;
          QDomElement ve;
          SVideoOptions *vopt;
          vn = e.firstChild();
          while (!vn.isNull()) {
             ve = vn.toElement();
             if (!ve.isNull()) {
//qDebug("Appending video options " + ve.tagName() + " name=" + ve.attribute("name"));
               vopt = new SVideoOptions(Configuration.Root);
               // Append, so new options get in, and old ones disappear
             vn = vn.nextSibling();

      n = n.nextSibling();
   Configuration.Root.appendChild(config); // This will become the root element; only one object is allowed!

void CCamStreamApp::SaveConfigFile()
   QTextStream *ts;
   QDomElement config;
   mode_t oldmask;

   /* Make sure our configuration cannot be read by others (FTP passwords!) */
   oldmask = umask(077);   
   if (!ConfigFile.open(IO_WriteOnly)) {
     qWarning("Failed to open configuration file for writing.\n");
   else {
     /* Save config. We assume a consistent tree has been built in ReadConfigFile. */
     config = Configuration.Root.documentElement();
     ts = new QTextStream(&ConfigFile);
     Configuration.Root.save(*ts, 0);
     delete ts;

// public

QString CCamStreamApp::GetUploadTmpDir() const
   return UploadDir;

  \brief Return number of file formats
  This function returns the number of programmed file formats, including 
  the formats that are currently not supported by Qt. The supported formats
  are determined at run time, see \ref GetFileTypeMask.
00267 int CCamStreamApp::GetNumberOfFileTypes() const
   return file_MAX;

  \brief Return bitmak of available file formats.
  This function returns a bitmask of available formats. Every enumerated
  fileformat is represented by its corresponding bit (use 1 << \e n to
  mask out a bit). The number of fileformats is thus limited to 32.

00280 int CCamStreamApp::GetFileTypeMask() const
   return snap_bitmask;

  \brief Get string with the proper filename extension.
  \param n One of \ref FileTypeEnum
  Return the proper filename extension for the given file format. For example,
  for JPEG files, it will return "jpg".
00292 QString CCamStreamApp::GetFileTypeExtension(int n) const
   if (n >= 0 && n < file_MAX) {
     return QString(FileTypes[n].extension);
   return QString("xxx"); /* something we don't understand */

  \brief Get string with format for saving files.

  Return proper handler string for current file format (which is different
  from the file extension). See \b QImage::save(...)
00306 QString CCamStreamApp::GetFileTypeFormatStr(int n) const
   if (n >= 0 && n < file_MAX) {
     return QString(FileTypes[n].format);
   return QString("");

  \brief Find enumeration belonging to file format
  \return A positive integer on a match, -1 when nothing was found
  This function returns a number from \ref FileTypeEnum that matches 
  the string given in \e format. See \ref GetFileTypeFormatStr
00321 int CCamStreamApp::FormatStrToEnum(const QString &format) const
   int i;
   for (i = 0; i < file_MAX; i++) 
      if (format == FileTypes[i].format)
        return i;
   return -1; // No match

QString CCamStreamApp::FormatStrToExtension(const QString &format) const
   int i;
   for (i = 0; i < file_MAX; i++) 
      if (format == FileTypes[i].format)
        return QString(FileTypes[i].extension);
   return QString(""); // No match

  \brief Find the options of a videodevice, matching by name or device node
  \param name The name from the device, e.g. "CPiA webcam"
  \param node The device nodename, e.g. /dev/video1
  \param create If TRUE, will create a new structure which is added to the internal tree
  \return a \ref SVideoOptions structure, or NULL if nothing was found and create was FALSE

  This function tries to find a matching \ref SVideoOptions struct for 
  a device. It first searches the list looking for the 'name' (which is 
  a symbolic name returned by the device), if that fails it uses the
  device nodename.
  By setting \b create to TRUE, a SVideoOptions structure will be created when
  it wasn't found in the current list, and will be appended to the 
  known configurations.

00359 struct SVideoOptions *CCamStreamApp::FindVideoOptions(const QString &name, const QString &node, bool create)
   struct SVideoOptions *opt;

   qDebug("Trying to find video options for %s:%s", (const char *)name, (const char *)node);
   opt = VOpts.first();
   while (opt != NULL) {
qDebug("searching %s", (const char *)opt->GetDeviceName());
      if (opt->GetDeviceName() == name)
      opt = VOpts.next();
   // if opt == NULL, search again

   if (opt == NULL && create) {
     QDomElement ne = Configuration.Root.createElement("me");
qDebug("Creating new video options");
     opt = new SVideoOptions(Configuration.Root);
   return opt;

Generated by  Doxygen 1.6.0   Back to index