MythTV  0.26-pre
zmliveplayer.cpp
Go to the documentation of this file.
00001 /* ============================================================
00002  * This program is free software; you can redistribute it
00003  * and/or modify it under the terms of the GNU General
00004  * Public License as published bythe Free Software Foundation;
00005  * either version 2, or (at your option)
00006  * any later version.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * ============================================================ */
00014 
00015 // qt
00016 #include <QDateTime>
00017 #include <QTimer>
00018 #include <QKeyEvent>
00019 
00020 // myth
00021 #include <mythcontext.h>
00022 #include <mythuihelper.h>
00023 #include <mythmainwindow.h>
00024 #include <mythdialogbox.h>
00025 
00026 // zoneminder
00027 #include "zmliveplayer.h"
00028 #include "zmclient.h"
00029 
00030 // the maximum image size we are ever likely to get from ZM
00031 #define MAX_IMAGE_SIZE  (2048*1536*3)
00032 
00033 const int FRAME_UPDATE_TIME = 1000 / 10;  // try to update the frame 10 times a second
00034 
00035 ZMLivePlayer::ZMLivePlayer(MythScreenStack *parent)
00036              :MythScreenType(parent, "zmliveview")
00037 {
00038     m_paused = false;
00039 
00040     m_players = NULL;
00041     m_monitors = NULL;
00042     m_monitorLayout = 1;
00043 
00044     GetMythUI()->DoDisableScreensaver();
00045 
00046     m_frameTimer = new QTimer(this);
00047     connect(m_frameTimer, SIGNAL(timeout()), this,
00048             SLOT(updateFrame()));
00049 
00050     getMonitorList();
00051 }
00052 
00053 bool ZMLivePlayer::Create(void)
00054 {
00055     bool foundtheme = false;
00056 
00057     // Load the theme for this screen
00058     foundtheme = LoadWindowFromXML("zoneminder-ui.xml", "zmliveplayer", this);
00059 
00060     if (!foundtheme)
00061         return false;
00062 
00063     if (!hideAll())
00064         return false;
00065 
00066     if (!initMonitorLayout())
00067         return false;
00068 
00069     return true;
00070 }
00071 
00072 MythUIType* ZMLivePlayer::GetMythUIType(const QString &name, bool optional)
00073 {
00074     MythUIType *type = GetChild(name);
00075 
00076     if (!optional && !type)
00077         throw name;
00078 
00079     return type;
00080 }
00081 
00082 bool ZMLivePlayer::hideAll(void)
00083 {
00084     try
00085     {
00086         // one player layout
00087         GetMythUIType("name1-1")->SetVisible(false);
00088         GetMythUIType("status1-1")->SetVisible(false);
00089         GetMythUIType("frame1-1")->SetVisible(false);
00090 
00091         // two player layout
00092         for (int x = 1; x < 3; x++)
00093         {
00094             GetMythUIType(QString("name2-%1").arg(x))->SetVisible(false);
00095             GetMythUIType(QString("status2-%1").arg(x))->SetVisible(false);
00096             GetMythUIType(QString("frame2-%1").arg(x))->SetVisible(false);
00097         }
00098 
00099         // four player layout
00100         for (int x = 1; x < 5; x++)
00101         {
00102             GetMythUIType(QString("name3-%1").arg(x))->SetVisible(false);
00103             GetMythUIType(QString("status3-%1").arg(x))->SetVisible(false);
00104             GetMythUIType(QString("frame3-%1").arg(x))->SetVisible(false);
00105         }
00106     }
00107     catch (const QString &name)
00108     {
00109         LOG(VB_GENERAL, LOG_ERR,
00110             QString("Theme is missing a critical theme element ('%1')")
00111                 .arg(name));
00112         return false;
00113     }
00114 
00115     return true;
00116 }
00117 
00118 bool ZMLivePlayer::initMonitorLayout()
00119 {
00120     // if we haven't got any monitors there's not much we can do so bail out!
00121     if (m_monitors->empty())
00122     {
00123         LOG(VB_GENERAL, LOG_ERR, "Cannot find any monitors. Bailing out!");
00124         ShowOkPopup(tr("Can't show live view.") + "\n" +
00125                     tr("You don't have any monitors defined!"));
00126         return false;
00127     }
00128 
00129     setMonitorLayout(gCoreContext->GetNumSetting("ZoneMinderLiveLayout", 1), true);
00130     m_frameTimer->start(FRAME_UPDATE_TIME);
00131 
00132     return true;
00133 }
00134 
00135 ZMLivePlayer::~ZMLivePlayer()
00136 {
00137     gCoreContext->SaveSetting("ZoneMinderLiveLayout", m_monitorLayout);
00138 
00139     GetMythUI()->DoRestoreScreensaver();
00140 
00141     if (m_players)
00142     {
00143         QString s = "";
00144         Player *p;
00145         vector<Player*>::iterator i = m_players->begin();
00146         for (; i != m_players->end(); ++i)
00147         {
00148             p = *i;
00149             if (s != "")
00150                 s += ",";
00151             s += QString("%1").arg(p->getMonitor()->id);
00152         }
00153 
00154         gCoreContext->SaveSetting("ZoneMinderLiveCameras", s);
00155 
00156         delete m_players;
00157     }
00158     else
00159         gCoreContext->SaveSetting("ZoneMinderLiveCameras", "");
00160 
00161     if (m_monitors)
00162         delete m_monitors;
00163 
00164     delete m_frameTimer;
00165 }
00166 
00167 bool ZMLivePlayer::keyPressEvent(QKeyEvent *event)
00168 {
00169     if (GetFocusWidget() && GetFocusWidget()->keyPressEvent(event))
00170         return true;
00171 
00172     bool handled = false;
00173     QStringList actions;
00174     handled = GetMythMainWindow()->TranslateKeyPress("TV Playback", event, actions);
00175 
00176     for (int i = 0; i < actions.size() && !handled; i++)
00177     {
00178         QString action = actions[i];
00179         handled = true;
00180 
00181         if (action == "PAUSE")
00182         {
00183             if (m_paused)
00184             {
00185                 m_frameTimer->start(FRAME_UPDATE_TIME);
00186                 m_paused = false;
00187             }
00188             else
00189             {
00190                 m_frameTimer->stop();
00191                 m_paused = true;
00192             }
00193         }
00194         else if (action == "INFO")
00195         {
00196             m_monitorLayout++;
00197             if (m_monitorLayout > 3)
00198                 m_monitorLayout = 1;
00199             setMonitorLayout(m_monitorLayout);
00200         }
00201         else if (action == "1" || action == "2" || action == "3" ||
00202                  action == "4" || action == "5" || action == "6" ||
00203                  action == "7" || action == "8" || action == "9")
00204             changePlayerMonitor(action.toInt());
00205         else
00206             handled = false;
00207     }
00208 
00209     if (!handled && MythScreenType::keyPressEvent(event))
00210         handled = true;
00211 
00212     return handled;
00213 }
00214 
00215 void ZMLivePlayer::changePlayerMonitor(int playerNo)
00216 {
00217     if (playerNo > (int)m_players->size())
00218         return;
00219 
00220     m_frameTimer->stop();
00221 
00222     int oldMonID = m_players->at(playerNo - 1)->getMonitor()->id;
00223     Monitor *mon;
00224 
00225     // find the old monitor ID in the list of available monitors
00226     vector<Monitor*>::iterator i = m_monitors->begin();
00227     for (; i != m_monitors->end(); ++i)
00228     {
00229         mon = *i;
00230         if (oldMonID == mon->id)
00231         {
00232             break;
00233         }
00234     }
00235 
00236     // get the next monitor in the list
00237     if (i != m_monitors->end())
00238         ++i;
00239 
00240     // wrap around to the start if we've reached the end
00241     if (i == m_monitors->end())
00242         i = m_monitors->begin();
00243 
00244     mon = *i;
00245 
00246     m_players->at(playerNo - 1)->setMonitor(mon);
00247     m_players->at(playerNo - 1)->updateCamera();
00248 
00249     m_frameTimer->start(FRAME_UPDATE_TIME);
00250 }
00251 
00252 void ZMLivePlayer::updateFrame()
00253 {
00254     class ZMClient *zm = ZMClient::get();
00255     if (!zm)
00256         return;
00257 
00258     static unsigned char buffer[MAX_IMAGE_SIZE];
00259     m_frameTimer->stop();
00260 
00261     // get a list of monitor id's that need updating
00262     QList<int> monList;
00263     Player *p;
00264     vector<Player*>::iterator i = m_players->begin();
00265     for (; i != m_players->end(); ++i)
00266     {
00267         p = *i;
00268         if (!monList.contains(p->getMonitor()->id))
00269             monList.append(p->getMonitor()->id);
00270     }
00271 
00272     for (int x = 0; x < monList.count(); x++)
00273     {
00274         QString status;
00275         int frameSize = zm->getLiveFrame(monList[x], status, buffer, sizeof(buffer));
00276 
00277         if (frameSize > 0 && !status.startsWith("ERROR"))
00278         {
00279             // update each player that is displaying this monitor
00280             Player *p;
00281             vector<Player*>::iterator i = m_players->begin();
00282             for (; i != m_players->end(); ++i)
00283             {
00284                 p = *i;
00285                 if (p->getMonitor()->id == monList[x])
00286                 {
00287                     if (p->getMonitor()->status != status)
00288                     {
00289                         p->getMonitor()->status = status;
00290                         p->updateStatus();
00291                     }
00292                     p->updateFrame(buffer);
00293                 }
00294             }
00295         }
00296     }
00297 
00298     m_frameTimer->start(FRAME_UPDATE_TIME);
00299 }
00300 
00301 void ZMLivePlayer::stopPlayers()
00302 {
00303     m_frameTimer->stop();
00304 }
00305 
00306 void ZMLivePlayer::setMonitorLayout(int layout, bool restore)
00307 {
00308     QStringList monList = gCoreContext->GetSetting("ZoneMinderLiveCameras", "").split(",");
00309     m_monitorLayout = layout;
00310 
00311     if (m_players)
00312     {
00313         stopPlayers();
00314         delete m_players;
00315     }
00316 
00317     m_players = new vector<Player *>;
00318     m_monitorCount = 1;
00319 
00320     if (layout == 1)
00321         m_monitorCount = 1;
00322     else if (layout == 2)
00323         m_monitorCount = 2;
00324     else if (layout == 3)
00325         m_monitorCount = 4;
00326     else if (layout == 4)
00327         m_monitorCount = 9;
00328 
00329     hideAll();
00330 
00331     uint monitorNo = 1;
00332 
00333     for (int x = 1; x <= m_monitorCount; x++)
00334     {
00335         Monitor *monitor = NULL;
00336 
00337         if (restore)
00338         {
00339             if (x <= (int) monList.size())
00340             {
00341                 QString s = monList.at(x - 1);
00342                 int monID = s.toInt(); 
00343 
00344                 // try to find a monitor that matches the monID
00345                 vector<Monitor*>::iterator i = m_monitors->begin();
00346                 for (; i != m_monitors->end(); ++i)
00347                 {
00348                     if ((*i)->id == monID)
00349                     {
00350                         monitor = *i;
00351                         break;
00352                     }
00353                 }
00354             }
00355         }
00356 
00357         if (!monitor)
00358             monitor = m_monitors->at(monitorNo - 1);
00359 
00360         MythUIImage *frameImage = dynamic_cast<MythUIImage *> (GetChild(QString("frame%1-%2").arg(layout).arg(x)));
00361         MythUIText  *cameraText = dynamic_cast<MythUIText *> (GetChild(QString("name%1-%2").arg(layout).arg(x)));
00362         MythUIText  *statusText = dynamic_cast<MythUIText *> (GetChild(QString("status%1-%2").arg(layout).arg(x)));
00363 
00364         Player *p = new Player();
00365         p->setMonitor(monitor);
00366         p->setWidgets(frameImage, statusText, cameraText);
00367         p->updateCamera();
00368         m_players->push_back(p);
00369 
00370         monitorNo++;
00371         if (monitorNo > m_monitors->size())
00372             monitorNo = 1;
00373     }
00374 
00375     updateFrame();
00376 }
00377 
00378 void ZMLivePlayer::getMonitorList(void)
00379 {
00380     if (!m_monitors)
00381         m_monitors = new vector<Monitor *>;
00382 
00383     m_monitors->clear();
00384 
00385     if (class ZMClient *zm = ZMClient::get())
00386         zm->getMonitorList(m_monitors);
00387 }
00388 
00390 
00391 Player::Player() :
00392     m_frameImage(NULL), m_statusText(NULL), m_cameraText(NULL),
00393     m_image(NULL), m_rgba(NULL)
00394 {
00395 }
00396 
00397 Player::~Player()
00398 {
00399     if (m_rgba)
00400         free(m_rgba);
00401 }
00402 
00403 void Player::setMonitor(Monitor *mon)
00404 {
00405     m_monitor = *mon;
00406 
00407     if (m_rgba)
00408         free(m_rgba);
00409 
00410     m_rgba = (uchar *) malloc(m_monitor.width * m_monitor.height * 4);
00411 }
00412 
00413 void Player::setWidgets(MythUIImage *image, MythUIText *status, MythUIText  *camera)
00414 {
00415     m_frameImage = image;
00416     m_statusText = status;
00417     m_cameraText = camera;
00418 
00419     if (m_frameImage)
00420         m_frameImage->SetVisible(true);
00421 
00422     if (m_statusText)
00423         m_statusText->SetVisible(true);
00424 
00425     if (m_cameraText)
00426         m_cameraText->SetVisible(true);
00427 }
00428 
00429 void Player::updateFrame(const unsigned char* buffer)
00430 {
00431     unsigned int pos_data;
00432     unsigned int pos_rgba = 0;
00433     unsigned int r,g,b;
00434 
00435     if (m_monitor.palette == MP_GREY)
00436     {
00437         // grey palette
00438         for (pos_data = 0; pos_data < (unsigned int) (m_monitor.width * m_monitor.height); )
00439         {
00440             m_rgba[pos_rgba++] = buffer[pos_data];   //b
00441             m_rgba[pos_rgba++] = buffer[pos_data];   //g
00442             m_rgba[pos_rgba++] = buffer[pos_data++]; //r
00443             m_rgba[pos_rgba++] = 0xff;               //a
00444         }
00445     }
00446     else
00447     {
00448         // all other color palettes
00449         for (pos_data = 0; pos_data < (unsigned int) (m_monitor.width * m_monitor.height * 3); )
00450         {
00451             r = buffer[pos_data++];
00452             g = buffer[pos_data++];
00453             b = buffer[pos_data++];
00454             if (m_monitor.isV4L2)
00455             {
00456                 m_rgba[pos_rgba++] = g;
00457                 m_rgba[pos_rgba++] = r;
00458                 m_rgba[pos_rgba++] = b;
00459                 m_rgba[pos_rgba++] = 0xff;
00460             }
00461             else
00462             {
00463                 m_rgba[pos_rgba++] = b;
00464                 m_rgba[pos_rgba++] = g;
00465                 m_rgba[pos_rgba++] = r;
00466                 m_rgba[pos_rgba++] = 0xff;
00467             }
00468         }
00469     }
00470 
00471     QImage image(m_rgba, m_monitor.width, m_monitor.height, QImage::Format_ARGB32);
00472 
00473     if (m_image)
00474     {
00475         m_image->DownRef();
00476         m_image = NULL;
00477     }
00478 
00479     m_image = GetMythMainWindow()->GetCurrentPainter()->GetFormatImage();
00480     m_image->Assign(image);
00481     m_image->UpRef();
00482 
00483     m_frameImage->SetImage(m_image);
00484 }
00485 
00486 void Player::updateStatus(void)
00487 {
00488     if (m_statusText)
00489     {
00490         if (m_monitor.status == "Alarm" || m_monitor.status == "Error")
00491             m_statusText->SetFontState("alarm");
00492         else if (m_monitor.status == "Alert")
00493             m_statusText->SetFontState("alert");
00494         else
00495             m_statusText->SetFontState("idle");
00496 
00497         m_statusText->SetText(m_monitor.status);
00498     }
00499 }
00500 
00501 void Player::updateCamera()
00502 {
00503     if (m_cameraText)
00504         m_cameraText->SetText(m_monitor.name);
00505 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends