Ticket #3361: 3361-v3.patch

File 3361-v3.patch, 58.3 KB (added by danielk, 16 years ago)

Updated patch, starts extending the protocol to allow for different sized previews

  • libs/libmythtv/previewgenerator.cpp

     
    6363PreviewGenerator::PreviewGenerator(const ProgramInfo *pginfo,
    6464                                   bool local_only)
    6565    : programInfo(*pginfo), localOnly(local_only), isConnected(false),
    66       createSockets(false), serverSock(NULL),      pathname(pginfo->pathname)
     66      createSockets(false), serverSock(NULL),      pathname(pginfo->pathname),
     67      timeInSeconds(true),  captureTime(-1),       outFileName(QString::null),
     68      outSize(0,0)
    6769{
    6870    if (IsLocal())
    6971        return;
     
    8183        "'%1' is not local, "
    8284        "\n\t\t\treplacing with '%2', which is local.")
    8385        .arg(pathname).arg(localFN);
    84     VERBOSE(VB_RECORD, LOC + msg);
     86    VERBOSE(VB_IMPORTANT, LOC + msg);
    8587    pathname = localFN;
    8688}
    8789
     
    9092    TeardownAll();
    9193}
    9294
     95void PreviewGenerator::SetOutputFilename(const QString &fileName)
     96{
     97    outFileName = QDeepCopy<QString>(fileName);
     98}
     99
    93100void PreviewGenerator::TeardownAll(void)
    94101{
    95102    if (!isConnected)
     
    109116        previewLock.unlock();
    110117        usleep(5000);
    111118    }
    112     VERBOSE(VB_PLAYBACK, LOC + "previewThreadDone took "<<t.elapsed()<<"ms");
     119    VERBOSE(VB_IMPORTANT, LOC + "previewThreadDone took "<<t.elapsed()<<"ms");
    113120    disconnectSafe();
    114121}
    115122
     
    150157    pthread_detach(previewThread);
    151158}
    152159
    153 /** \fn PreviewGenerator::Run(void)
     160/** \fn PreviewGenerator::RunReal(void)
    154161 *  \brief This call creates a preview without starting a new thread.
    155162 */
    156 void PreviewGenerator::Run(void)
     163bool PreviewGenerator::RunReal(void)
    157164{
    158     if (IsLocal())
     165    bool ok = false;
     166    bool is_local = IsLocal();
     167    if (is_local && LocalPreviewRun())
    159168    {
    160         LocalPreviewRun();
     169        ok = true;
    161170    }
    162171    else if (!localOnly)
    163172    {
    164         RemotePreviewRun();
     173        if (is_local)
     174        {
     175            VERBOSE(VB_IMPORTANT, LOC_WARN + "Failed to save preview."
     176                    "\n\t\t\tYou may need to check user and group ownership on"
     177                    "\n\t\t\tyour frontend and backend for quicker previews.\n"
     178                    "\n\t\t\tAttempting to regenerate preview on backend.\n");
     179        }
     180        ok = RemotePreviewRun();
    165181    }
    166182    else
    167183    {
    168184        VERBOSE(VB_IMPORTANT, LOC_ERR + QString("Run() file not local: '%1'")
    169185                .arg(pathname));
    170186    }
     187
     188    return ok;
    171189}
    172190
     191bool PreviewGenerator::Run(void)
     192{
     193    bool ok = false;
     194    if (!IsLocal())
     195    {
     196        if (!localOnly)
     197        {
     198            ok = RemotePreviewRun();
     199        }
     200        else
     201        {
     202            VERBOSE(VB_IMPORTANT, LOC_ERR +
     203                    QString("Run() file not local: '%1'")
     204                    .arg(pathname));
     205        }
     206    }
     207    else
     208    {
     209        // This is where we fork and run mythbackend to actually make preview
     210        QString command = "mythbackend --generate-preview ";
     211        command += QString("%1x%2")
     212            .arg(outSize.width()).arg(outSize.height());
     213        if (captureTime >= 0)
     214            command += QString("@%1%2")
     215                .arg(captureTime).arg(timeInSeconds ? "s" : "f");
     216        command += " ";
     217        command += QString("--chanid %1 ").arg(programInfo.chanid);
     218        command += QString("--starttime %1 ")
     219            .arg(programInfo.recstartts.toString("yyyyMMddhhmmss"));
     220        if (!outFileName.isEmpty())
     221            command += QString("--outfile %1 ").arg(outFileName);
     222
     223        int ret = myth_system(command);
     224        if (ret)
     225        {
     226            VERBOSE(VB_IMPORTANT, LOC_ERR + "Encountered problems running " +
     227                    QString("'%1'").arg(command));
     228        }
     229        else
     230        {
     231            VERBOSE(VB_IMPORTANT, LOC + "Preview process returned 0.");
     232            QString outname = (!outFileName.isEmpty()) ?
     233                outFileName : (pathname + ".png");
     234
     235            QFileInfo fi(outname);
     236            ok = (fi.exists() && fi.isReadable() && fi.size());
     237            if (ok)
     238                VERBOSE(VB_IMPORTANT, LOC + "Preview process ran ok.");
     239            else
     240            {
     241                VERBOSE(VB_IMPORTANT, LOC_ERR + "Preview process not ok." +
     242                        QString("\n\t\t\tfileinfo(%1)").arg(outname)
     243                        <<" exits: "<<fi.exists()
     244                        <<" readable: "<<fi.isReadable()
     245                        <<" size: "<<fi.size());
     246            }
     247        }
     248    }
     249
     250    if (ok)
     251    {
     252        QMutexLocker locker(&previewLock);
     253        emit previewReady(&programInfo);
     254    }
     255
     256    return ok;
     257}
     258
    173259void *PreviewGenerator::PreviewRun(void *param)
    174260{
    175261    // Lower scheduling priority, to avoid problems with recordings.
     
    191277    return serverSock;
    192278}
    193279
    194 void PreviewGenerator::RemotePreviewRun(void)
     280bool PreviewGenerator::RemotePreviewRun(void)
    195281{
    196282    QStringList strlist = "QUERY_GENPIXMAP";
    197283    programInfo.ToStringList(strlist);
     284    strlist.push_back(timeInSeconds ? "s" : "f");
     285    encodeLongLong(strlist, captureTime);
     286    strlist.push_back(outFileName.isEmpty() ? "<EMPTY>" : outFileName);
     287    strlist.push_back(QString::number(outSize.width()));
     288    strlist.push_back(QString::number(outSize.height()));
     289
    198290    bool ok = false;
    199291
    200292    if (createSockets)
     
    202294        if (!RemotePreviewSetup())
    203295        {
    204296            VERBOSE(VB_IMPORTANT, LOC_ERR + "Failed to open sockets.");
    205             return;
     297            return false;
    206298        }
    207299
    208300        if (serverSock)
     
    218310        ok = gContext->SendReceiveStringList(strlist);
    219311    }
    220312
    221     if (ok && (strlist[0] == "OK"))
    222     {
    223         QMutexLocker locker(&previewLock);
    224         emit previewReady(&programInfo);
    225     }
     313    return (ok && strlist.size() && (strlist[0] == "OK"));
    226314}
    227315
    228316void PreviewGenerator::RemotePreviewTeardown(void)
     
    236324
    237325bool PreviewGenerator::SavePreview(QString filename,
    238326                                   const unsigned char *data,
    239                                    uint width, uint height, float aspect)
     327                                   uint width, uint height, float aspect,
     328                                   int desired_width, int desired_height)
    240329{
    241330    if (!data || !width || !height)
    242331        return false;
     
    245334                     width, height, 32, NULL, 65536 * 65536,
    246335                     QImage::LittleEndian);
    247336
    248     float ppw = gContext->GetNumSetting("PreviewPixmapWidth", 320);
    249     float pph = gContext->GetNumSetting("PreviewPixmapHeight", 240);
     337    float ppw = max(desired_width, 0);
     338    float pph = max(desired_height, 0);
     339    bool desired_size_exactly_specified = true;
     340    if ((ppw < 1.0f) && (pph < 1.0f))
     341    {
     342        ppw = gContext->GetNumSetting("PreviewPixmapWidth",  320);
     343        pph = gContext->GetNumSetting("PreviewPixmapHeight", 240);
     344        desired_size_exactly_specified = false;
     345    }
    250346
    251     aspect = (aspect <= 0) ? ((float) width) / height : aspect;
     347    aspect = (aspect <= 0.0f) ? ((float) width) / height : aspect;
     348    pph = (pph < 1.0f) ? (ppw / aspect) : pph;
     349    ppw = (ppw < 1.0f) ? (pph * aspect) : ppw;
    252350
    253     if (aspect > ppw / pph)
    254         pph = rint(ppw / aspect);
    255     else
    256         ppw = rint(pph * aspect);
     351    if (!desired_size_exactly_specified)
     352    {
     353        if (aspect > ppw / pph)
     354            pph = (ppw / aspect);
     355        else
     356            ppw = (pph * aspect);
     357    }
    257358
     359    ppw = max(1.0f, ppw);
     360    pph = max(1.0f, pph);;
     361
    258362    QImage small_img = img.smoothScale((int) ppw, (int) pph);
    259363
    260364    if (small_img.save(filename.ascii(), "PNG"))
    261365    {
    262366        chmod(filename.ascii(), 0666); // Let anybody update it
     367
     368        VERBOSE(VB_GENERAL, LOC +
     369                QString("Saved preview '%0' %1x%2")
     370                .arg(filename).arg((int) ppw).arg((int) pph));
     371
    263372        return true;
    264373    }
    265374
     
    270379    {
    271380        chmod(newfile.ascii(), 0666);
    272381        rename(newfile.ascii(), filename.ascii());
     382
     383        VERBOSE(VB_GENERAL, LOC +
     384                QString("Saved preview '%0' %1x%2")
     385                .arg(filename).arg((int) ppw).arg((int) pph));
     386
    273387        return true;
    274388    }
    275389
     
    277391    return false;
    278392}
    279393
    280 void PreviewGenerator::LocalPreviewRun(void)
     394bool PreviewGenerator::LocalPreviewRun(void)
    281395{
     396#if 0
     397    char *memaddr = (char*)0x1111;
     398    memset(memaddr, 0, 0xFFFFFFFF); // try to cause segfault
     399#endif
     400
    282401    programInfo.MarkAsInUse(true, kInUseID);
    283402
    284403    float aspect = 0;
    285     int   secsin = (gContext->GetNumSetting("PreviewPixmapOffset", 64) +
    286                     gContext->GetNumSetting("RecordPreRoll",       0));
    287404    int   len, width, height, sz;
     405    long long captime = captureTime;
     406    if (captime < 0)
     407    {
     408        timeInSeconds = true;
     409        captime = (gContext->GetNumSetting("PreviewPixmapOffset", 64) +
     410                   gContext->GetNumSetting("RecordPreRoll",       0));
     411    }
    288412
    289413    len = width = height = sz = 0;
    290414    unsigned char *data = (unsigned char*)
    291         GetScreenGrab(&programInfo, pathname, secsin,
     415        GetScreenGrab(&programInfo, pathname,
     416                      captime, timeInSeconds,
    292417                      sz, width, height, aspect);
    293    
    294     bool ok = SavePreview(pathname + ".png",
    295                           data, width, height, aspect);
    296     if (ok)
    297     {
    298         QMutexLocker locker(&previewLock);
    299         emit previewReady(&programInfo);
    300     }
    301418
     419    QString outname = pathname + ".png";
     420    outname = (outFileName.isEmpty()) ? outname : outFileName;
     421
     422    int dw = (outSize.width()  < 0) ? width  : outSize.width();
     423    int dh = (outSize.height() < 0) ? height : outSize.height();
     424
     425    bool ok = SavePreview(outname, data, width, height, aspect, dw, dh);
     426
    302427    if (data)
    303428        delete[] data;
    304429
    305430    programInfo.MarkAsInUse(false);
    306431
    307     // local update failed, try remote...
    308     if (!ok && !localOnly)
    309     {
    310         VERBOSE(VB_IMPORTANT, LOC_WARN + "Failed to save preview."
    311                 "\n\t\t\tYou may need to check user and group ownership"
    312                 "\n\t\t\ton your frontend and backend for quicker previews."
    313                 "\n\n\t\t\tAttempting to regenerate preview on backend.\n");
    314 
    315         RemotePreviewRun();
    316     }
     432    return ok;
    317433}
    318434
    319435bool PreviewGenerator::IsLocal(void) const
     
    322438    return (QFileInfo(pathname).exists() && QFileInfo(pathdir).isWritable());
    323439}
    324440
    325 /**
    326  *  \brief Saves a screenshot to a file.
    327  *
    328  *  \param pginfo       Recording to grab from.
    329  *  \param outFile      Filename to save the image to
    330  *  \param frameNumber  Frame Number to capture
    331  *  \param thumbWidth   Width of desired image
    332  *  \param thumbHeight  Height of desired image
    333  *  \return True if successful, false otherwise.
    334  */
    335 bool PreviewGenerator::SaveScreenshot(ProgramInfo *pginfo, QString &outFile,
    336         long long frameNumber, int &thumbWidth, int &thumbHeight)
    337 {
    338     float desWidth = (float)thumbWidth;
    339     float desHeight = (float)thumbHeight;
    340     float frameAspect = 0.0;
    341     char *retbuf = NULL;
    342     int bufferLen = 0;
    343     int frameWidth = 0;
    344     int frameHeight = 0;
    345 
    346     if (!pginfo)
    347         return false;
    348 
    349     QString inFile = pginfo->GetPlaybackURL();
    350     RingBuffer *rbuf = new RingBuffer(inFile, false, false, 0);
    351 
    352     if (!rbuf->IsOpen())
    353     {
    354         VERBOSE(VB_IMPORTANT, LOC_ERR +
    355                 "SaveScreenshot: Could not open file: " +
    356                 QString("'%1'").arg(inFile));
    357         delete rbuf;
    358         return false;
    359     }
    360 
    361     NuppelVideoPlayer *nvp = new NuppelVideoPlayer(kInUseID, pginfo);
    362     nvp->SetRingBuffer(rbuf);
    363 
    364     retbuf = nvp->GetScreenGrabAtFrame(frameNumber, true, bufferLen,
    365                                        frameWidth, frameHeight, frameAspect);
    366 
    367     if (thumbWidth == -1)
    368         desWidth = frameWidth;
    369     if (thumbHeight == -1)
    370         desHeight = frameHeight;
    371 
    372     delete nvp;
    373     delete rbuf;
    374 
    375     if (!retbuf || bufferLen == 0)
    376         return false;
    377 
    378     if (frameAspect <= 0)
    379         frameAspect = frameWidth / frameHeight;
    380 
    381     const QImage img((unsigned char*) retbuf,
    382                      frameWidth, frameHeight, 32, NULL, 65536 * 65536,
    383                      QImage::LittleEndian);
    384 
    385     if (frameAspect > desWidth / desHeight)
    386         desHeight = rint(desWidth / frameAspect);
    387     else
    388         desWidth = rint(desHeight * frameAspect);
    389 
    390     QImage small_img = img.smoothScale((int) desWidth, (int) desHeight);
    391     QString type = "PNG";
    392 
    393     if (outFile.right(4).lower() == ".png")
    394         type = outFile.right(3).upper();
    395     else
    396         VERBOSE(VB_IMPORTANT, LOC_WARN + QString("SaveScreenshot: The filename "
    397                 "(%1) does not end in .png, but we only support png format. "
    398                 "The file will contain a PNG formatted image.")
    399                 .arg(outFile));
    400 
    401     if (small_img.save(outFile.ascii(), type))
    402         chmod(outFile.ascii(), 0666);
    403     else
    404         VERBOSE(VB_IMPORTANT, LOC_ERR + QString("SaveScreenshot: unable to "
    405                 "open save image").arg(outFile));
    406 
    407     delete retbuf;
    408 
    409     thumbWidth = (int)desWidth;
    410     thumbHeight = (int)desHeight;
    411 
    412     return true;
    413 }
    414 
    415 /** \fn PreviewGenerator::GetScreenGrab(const ProgramInfo*,const QString&,int,int&,int&,int&,float&)
     441/** \fn PreviewGenerator::GetScreenGrab(const ProgramInfo*, const QString&,
     442    long long, bool, int&, int&, int&, float&)
    416443 *  \brief Returns a PIX_FMT_RGBA32 buffer containg a frame from the video.
    417444 *
    418445 *  \param pginfo       Recording to grab from.
    419446 *  \param filename     File containing recording.
    420  *  \param secondsin    Seconds into the video to seek before
     447 *  \param seektime     Seconds or frames into the video to seek before
    421448 *                      capturing a frame.
     449 *  \param time_in_secs if true time is in seconds, otherwise it is in frames.
    422450 *  \param bufferlen    Returns size of buffer returned (in bytes).
    423451 *  \param video_width  Returns width of frame grabbed.
    424452 *  \param video_height Returns height of frame grabbed.
     
    427455 *          successful, NULL otherwise.
    428456 */
    429457char *PreviewGenerator::GetScreenGrab(
    430     const ProgramInfo *pginfo, const QString &filename, int secondsin,
     458    const ProgramInfo *pginfo, const QString &filename,
     459    long long seektime, bool time_in_secs,
    431460    int &bufferlen,
    432461    int &video_width, int &video_height, float &video_aspect)
    433462{
    434463    (void) pginfo;
    435464    (void) filename;
    436     (void) secondsin;
     465    (void) seektime;
     466    (void) time_in_secs;
    437467    (void) bufferlen;
    438468    (void) video_width;
    439469    (void) video_height;
     
    483513    NuppelVideoPlayer *nvp = new NuppelVideoPlayer(kInUseID, pginfo);
    484514    nvp->SetRingBuffer(rbuf);
    485515
    486     retbuf = nvp->GetScreenGrab(secondsin, bufferlen,
    487                                 video_width, video_height, video_aspect);
     516    if (time_in_secs)
     517        retbuf = nvp->GetScreenGrab(seektime, bufferlen,
     518                                    video_width, video_height, video_aspect);
     519    else
     520        retbuf = nvp->GetScreenGrabAtFrame(
     521            seektime, true, bufferlen,
     522            video_width, video_height, video_aspect);
    488523
    489524    delete nvp;
    490525    delete rbuf;
     
    493528    QString msg = "Backend compiled without USING_FRONTEND !!!!";
    494529    VERBOSE(VB_IMPORTANT, LOC_ERR + msg);
    495530#endif // USING_FRONTEND
     531
     532    if (retbuf)
     533    {
     534        VERBOSE(VB_GENERAL, LOC +
     535                QString("Grabbed preview '%0' %1x%2@%3%4")
     536                .arg(filename).arg(video_width).arg(video_height)
     537                .arg((Q_LLONG)seektime).arg((time_in_secs) ? "s" : "f"));
     538    }
     539
    496540    return retbuf;
    497541}
    498542
  • libs/libmythtv/tv_play.cpp

     
    28302830        else if ((action == "SCREENSHOT") && (!activerbuffer->isDVD()))
    28312831        {
    28322832            long long frameNumber = nvp->GetFramesPlayed();
    2833             int frameWidth = -1;
    2834             int frameHeight = -1;
    28352833            QString outFile =
    28362834               QString("%1/.mythtv/%2_%3_%4.png")
    2837                        .arg(QDir::homeDirPath()).arg(playbackinfo->chanid)
    2838                        .arg(playbackinfo->recstartts.toString("yyyyMMddhhmmss"))
    2839                        .arg((long)frameNumber);
     2835                .arg(QDir::homeDirPath()).arg(playbackinfo->chanid)
     2836                .arg(playbackinfo->recstartts.toString("yyyyMMddhhmmss"))
     2837                .arg((long)frameNumber);
    28402838
    2841             PreviewGenerator::SaveScreenshot(playbackinfo, outFile, frameNumber,
    2842                                              frameWidth, frameHeight);
     2839            PreviewGenerator *previewgen = new PreviewGenerator(playbackinfo);
     2840            previewgen->SetPreviewTimeAsFrameNumber(frameNumber);
     2841            previewgen->SetOutputSize(QSize(-1,-1));
     2842            previewgen->SetOutputFilename(outFile);
     2843            QString okStr = (previewgen->Run()) ? "" : tr("ERR", "Error");
     2844            previewgen->deleteLater();
     2845            QString msg = tr("Screen Shot") + " " + okStr;
     2846
    28432847            if (activenvp == nvp && GetOSD())
    2844                 GetOSD()->SetSettingsText(tr("Screen Shot"), 3);
     2848                GetOSD()->SetSettingsText(msg, 3);
    28452849        }
    28462850        else if (action == "EXITSHOWNOPROMPTS")
    28472851        {
  • libs/libmythtv/previewgenerator.h

     
    1414
    1515class MPUBLIC PreviewGenerator : public QObject
    1616{
     17    friend int preview_helper(const QString &chanid, const QString &starttime,
     18                              long long previewFrameNumber,
     19                              long long previewSeconds,
     20                              const QSize &previewSize,
     21                              const QString &infile, const QString &outfile);
     22
    1723    Q_OBJECT
    1824  public:
    1925    PreviewGenerator(const ProgramInfo *pginfo, bool local_only = true);
    20     virtual ~PreviewGenerator();
    2126
     27    void SetPreviewTime(long long time, bool in_seconds)
     28        { captureTime = time; timeInSeconds = in_seconds; }
     29    void SetPreviewTimeAsSeconds(long long seconds_in)
     30        { SetPreviewTime(seconds_in, true); }
     31    void SetPreviewTimeAsFrameNumber(long long frame_number)
     32        { SetPreviewTime(frame_number, false); }
     33    void SetOutputFilename(const QString&);
     34    void SetOutputSize(const QSize &size) { outSize = size; }
     35
    2236    void Start(void);
    23     void Run(void);
     37    bool Run(void);
    2438
    25     static bool SavePreview(QString filename,
    26                             const unsigned char *data,
    27                             uint width, uint height, float aspect);
    28 
    29     static char *GetScreenGrab(const ProgramInfo *pginfo,
    30                                const QString &filename, int secondsin,
    31                                int &bufferlen,
    32                                int &video_width, int &video_height,
    33                                float &video_aspect);
    34 
    35     static bool SaveScreenshot(ProgramInfo *pginfo, QString &outFile,
    36                               long long frameNumber,
    37                               int &thumbWidth, int &thumbHeight);
    38 
    3939    void AttachSignals(QObject*);
    4040    void disconnectSafe(void);
    4141
     
    4848    void deleteLater();
    4949
    5050  private:
     51    virtual ~PreviewGenerator();   
     52    bool RunReal(void);
     53
     54    static char *GetScreenGrab(const ProgramInfo *pginfo,
     55                               const QString &filename,
     56                               long long seektime, bool time_in_secs,
     57                               int &bufferlen,
     58                               int &video_width, int &video_height,
     59                               float &video_aspect);
     60
     61    static bool SavePreview(QString filename,
     62                            const unsigned char *data,
     63                            uint width, uint height, float aspect,
     64                            int desired_width, int desired_height);
     65
    5166    void TeardownAll(void);
    5267    bool RemotePreviewSetup(void);
    53     void RemotePreviewRun(void);
     68    bool RemotePreviewRun(void);
    5469    void RemotePreviewTeardown(void);
    55     void LocalPreviewRun(void);
     70    bool LocalPreviewRun(void);
    5671    bool IsLocal(void) const;
    5772    static void *PreviewRun(void*);
    5873
     
    6580    bool               createSockets;
    6681    MythSocket        *serverSock;
    6782    QString            pathname;
     83
     84    /// tells us whether to use time as seconds or frame number
     85    bool               timeInSeconds;
     86    /// snapshot time in seconds or frame number, depending on timeInSeconds
     87    long long          captureTime;
     88    QString            outFileName;
     89    QSize              outSize;
    6890};
    6991
    7092#endif // PREVIEW_GENERATOR_H_
  • programs/mythfrontend/playbackbox.cpp

     
    2222#include <unistd.h>
    2323#include <stdlib.h>
    2424
     25#include <algorithm>
    2526#include <iostream>
    2627using namespace std;
    2728
     
    5455
    5556QWaitCondition pbbIsVisibleCond;
    5657
     58const uint PreviewGenState::maxAttempts     = 5;
     59const uint PreviewGenState::minBlockSeconds = 60;
     60
     61const uint PlaybackBox::previewGeneratorMaxRunning = 2;
     62
    5763static int comp_programid(ProgramInfo *a, ProgramInfo *b)
    5864{
    5965    if (a->programid == b->programid)
     
    273279      // Preview Image Variables
    274280      previewPixmapEnabled(false),      previewPixmap(NULL),
    275281      previewSuspend(false),            previewChanid(""),
     282      previewGeneratorRunning(0),
    276283      // Network Control Variables
    277284      underNetworkControl(false),
    278285      m_player(NULL)
     
    470477
    471478    // disconnect preview generators
    472479    QMutexLocker locker(&previewGeneratorLock);
    473     QMap<QString, PreviewGenerator*>::iterator it = previewGenerator.begin();
     480    PreviewMap::iterator it = previewGenerator.begin();
    474481    for (;it != previewGenerator.end(); ++it)
    475482    {
    476         if (*it)
    477             (*it)->disconnectSafe();
     483        if ((*it).gen)
     484            (*it).gen->disconnectSafe();
    478485    }
    479486
    480487    // free preview pixmap after preview generators are
     
    42384245    return datetime;
    42394246}
    42404247
     4248void PlaybackBox::IncPreviewGeneratorPriority(const QString &xfn)
     4249{
     4250    QString fn = QDeepCopy<QString>(xfn.mid(max(xfn.findRev('/') + 1,0)));
     4251
     4252    QMutexLocker locker(&previewGeneratorLock);
     4253    vector<QString> &q = previewGeneratorQueue;
     4254    vector<QString>::iterator it = std::find(q.begin(), q.end(), fn);
     4255    if (it != q.end())
     4256        q.erase(it);
     4257
     4258    PreviewMap::iterator pit = previewGenerator.find(fn);
     4259    if (pit != previewGenerator.end() && (*pit).gen && !(*pit).genStarted)
     4260        q.push_back(fn);
     4261}
     4262
     4263void PlaybackBox::UpdatePreviewGeneratorThreads(void)
     4264{
     4265    QMutexLocker locker(&previewGeneratorLock);
     4266    vector<QString> &q = previewGeneratorQueue;
     4267    if ((previewGeneratorRunning < previewGeneratorMaxRunning) && q.size())
     4268    {
     4269        QString fn = q.back();
     4270        q.pop_back();
     4271        PreviewMap::iterator it = previewGenerator.find(fn);
     4272        if (it != previewGenerator.end() && (*it).gen && !(*it).genStarted)
     4273        {
     4274            previewGeneratorRunning++;
     4275            (*it).gen->Start();
     4276            (*it).genStarted = true;
     4277        }
     4278    }
     4279}
     4280
    42414281/** \fn PlaybackBox::SetPreviewGenerator(const QString&, PreviewGenerator*)
    42424282 *  \brief Sets the PreviewGenerator for a specific file.
    42434283 *  \return true iff call succeeded.
    42444284 */
    42454285bool PlaybackBox::SetPreviewGenerator(const QString &xfn, PreviewGenerator *g)
    42464286{
    4247     QString fn = xfn.mid(max(xfn.findRev('/') + 1,0));
     4287    QString fn = QDeepCopy<QString>(xfn.mid(max(xfn.findRev('/') + 1,0)));
     4288    QMutexLocker locker(&previewGeneratorLock);
     4289
    42484290    if (!g)
    42494291    {
    4250         if (previewGeneratorLock.tryLock())
    4251         {
    4252             previewGenerator.erase(fn);
    4253             previewGeneratorLock.unlock();
    4254             return true;
    4255         }
    4256         return false;
     4292        previewGeneratorRunning = max(0, (int)previewGeneratorRunning - 1);
     4293        PreviewMap::iterator it = previewGenerator.find(fn);
     4294        if (it == previewGenerator.end())
     4295            return false;
     4296
     4297        (*it).gen        = NULL;
     4298        (*it).genStarted = false;
     4299        (*it).ready      = false;
     4300        (*it).lastBlockTime =
     4301            max(PreviewGenState::minBlockSeconds, (*it).lastBlockTime * 2);
     4302        (*it).blockRetryUntil =
     4303            QDateTime::currentDateTime().addSecs((*it).lastBlockTime);
     4304
     4305        return true;
    42574306    }
    42584307
    4259     QMutexLocker locker(&previewGeneratorLock);
    42604308    g->AttachSignals(this);
    4261     previewGenerator[fn] = g;
    4262     g->Start();
     4309    previewGenerator[fn].gen = g;
     4310    previewGenerator[fn].genStarted = false;
     4311    previewGenerator[fn].ready = false;
    42634312
     4313    previewGeneratorLock.unlock();
     4314    IncPreviewGeneratorPriority(xfn);
     4315    previewGeneratorLock.lock();
     4316
    42644317    return true;
    42654318}
    42664319
    4267 /** \fn PlaybackBox::IsGeneratingPreview(const QString&) const
     4320/** \fn PlaybackBox::IsGeneratingPreview(const QString&, bool) const
    42684321 *  \brief Returns true if we have already started a
    42694322 *         PreviewGenerator to create this file.
    42704323 */
    4271 bool PlaybackBox::IsGeneratingPreview(const QString &xfn) const
     4324bool PlaybackBox::IsGeneratingPreview(const QString &xfn, bool really) const
    42724325{
    4273     QMap<QString, PreviewGenerator*>::const_iterator it;
     4326    PreviewMap::const_iterator it;
    42744327    QMutexLocker locker(&previewGeneratorLock);
    42754328
    42764329    QString fn = xfn.mid(max(xfn.findRev('/') + 1,0));
    42774330    if ((it = previewGenerator.find(fn)) == previewGenerator.end())
    42784331        return false;
    4279     return *it;
     4332
     4333    if (really)
     4334        return ((*it).gen && !(*it).ready);
     4335
     4336    if ((*it).blockRetryUntil.isValid())
     4337        return QDateTime::currentDateTime() < (*it).blockRetryUntil;
     4338
     4339    return (*it).gen;
    42804340}
    42814341
    42824342/** \fn PlaybackBox::IncPreviewGeneratorAttempts(const QString&)
     
    42874347{
    42884348    QMutexLocker locker(&previewGeneratorLock);
    42894349    QString fn = xfn.mid(max(xfn.findRev('/') + 1,0));
    4290     return previewGeneratorAttempts[fn]++;
     4350    return previewGenerator[fn].attempts++;
    42914351}
    42924352
    42934353void PlaybackBox::previewThreadDone(const QString &fn, bool &success)
    42944354{
    42954355    success = SetPreviewGenerator(fn, NULL);
     4356    UpdatePreviewGeneratorThreads();
    42964357}
    42974358
    42984359/** \fn PlaybackBox::previewReady(const ProgramInfo*)
     
    43024363 */
    43034364void PlaybackBox::previewReady(const ProgramInfo *pginfo)
    43044365{
     4366    QString xfn = pginfo->pathname + ".png";
     4367    QString fn = xfn.mid(max(xfn.findRev('/') + 1,0));
     4368
     4369    previewGeneratorLock.lock();
     4370    PreviewMap::iterator it = previewGenerator.find(fn);
     4371    if (it != previewGenerator.end())
     4372    {
     4373        (*it).ready         = true;
     4374        (*it).attempts      = 0;
     4375        (*it).lastBlockTime = 0;
     4376    }
     4377    previewGeneratorLock.unlock();
     4378
    43054379    // lock QApplication so that we don't remove pixmap
    43064380    // from under a running paint event.
    43074381    qApp->lock();
     
    43564430    if (check_date)
    43574431        previewLastModified = getPreviewLastModified(pginfo);
    43584432
     4433    IncPreviewGeneratorPriority(filename);
     4434
    43594435    if (previewFromBookmark &&
    43604436        check_date &&
    43614437        (!previewLastModified.isValid() ||
     
    43814457                .arg(!IsGeneratingPreview(filename)));
    43824458
    43834459        uint attempts = IncPreviewGeneratorAttempts(filename);
    4384         if (attempts < 5)
     4460        if (attempts < PreviewGenState::maxAttempts)
    43854461        {
    4386 #ifdef USE_PREV_GEN_THREAD
    43874462            SetPreviewGenerator(filename, new PreviewGenerator(pginfo, false));
    4388 #else
    4389             PreviewGenerator pg(pginfo, false);
    4390             pg.Run();
    4391 #endif
    43924463        }
    4393         else if (attempts == 5)
     4464        else if (attempts == PreviewGenState::maxAttempts)
    43944465        {
    43954466            VERBOSE(VB_IMPORTANT, LOC_ERR +
    4396                     QString("Attempted to generate preview for '%1'")
    4397                     .arg(filename) + " 5 times, giving up.");
     4467                    QString("Attempted to generate preview for '%1' "
     4468                            "%2 times, giving up.")
     4469                    .arg(filename).arg(PreviewGenState::maxAttempts));
    43984470        }
    43994471
    4400         if (attempts >= 5)
     4472        if (attempts >= PreviewGenState::maxAttempts)
    44014473            return retpixmap;
    44024474    }
    44034475
     4476    UpdatePreviewGeneratorThreads();
     4477
    44044478    // Check and see if we've already tried this one.
    44054479    if (previewPixmap &&
    44064480        pginfo->recstartts == previewStartts &&
     
    44364510    //if this is a remote frontend, then we need to refresh the pixmap
    44374511    //if another frontend has already regenerated the stale pixmap on the disk
    44384512    bool refreshPixmap      = (previewLastModified >= previewFilets);
    4439     bool generating_preview = IsGeneratingPreview(filename);
    44404513
    44414514    QImage *image = NULL;
    4442     if (!generating_preview)
     4515    if (!IsGeneratingPreview(filename, true))
    44434516        image = gContext->CacheRemotePixmap(filename, refreshPixmap);
    44444517
    44454518    // If the image is not available remotely either, we need to generate it.
    4446     if (!image && !generating_preview)
     4519    if (!image && !IsGeneratingPreview(filename))
    44474520    {
    4448 #ifdef USE_PREV_GEN_THREAD
    44494521        uint attempts = IncPreviewGeneratorAttempts(filename);
    4450         if (attempts < 5)
     4522        if (attempts < PreviewGenState::maxAttempts)
    44514523        {
    44524524            VERBOSE(VB_PLAYBACK, "Starting preview generator");
    44534525            SetPreviewGenerator(filename, new PreviewGenerator(pginfo, false));
    44544526        }
    4455         else if (attempts == 5)
     4527        else if (attempts == PreviewGenState::maxAttempts)
    44564528        {
    44574529            VERBOSE(VB_IMPORTANT, LOC_ERR +
    4458                     QString("Attempted to generate preview for '%1'")
    4459                     .arg(filename) + " 5 times, giving up.");
     4530                    QString("Attempted to generate preview for '%1' "
     4531                            "%2 times, giving up.")
     4532                    .arg(filename).arg(PreviewGenState::maxAttempts));
     4533
    44604534            return retpixmap;
    44614535        }
    4462 #else
    4463         PreviewGenerator pg(pginfo, false);
    4464         pg.Run();
    4465         image = gContext->CacheRemotePixmap(filename, true);
    4466 #endif
    44674536    }
    44684537
    44694538    if (image)
  • programs/mythfrontend/playbackbox.h

     
    33#ifndef PLAYBACKBOX_H_
    44#define PLAYBACKBOX_H_
    55
     6// C++ headers
     7#include <vector>
     8using namespace std;
     9
    610#include <qdatetime.h>
    711#include <qdom.h>
    812#include <qmutex.h>
     
    2933
    3034class LayerSet;
    3135
     36class PreviewGenState
     37{
     38  public:
     39    PreviewGenState() :
     40        gen(NULL), genStarted(false), ready(false),
     41        attempts(0), lastBlockTime(0) {}
     42    PreviewGenerator *gen;
     43    bool              genStarted;
     44    bool              ready;
     45    uint              attempts;
     46    uint              lastBlockTime;
     47    QDateTime         blockRetryUntil;
     48
     49    static const uint maxAttempts;
     50    static const uint minBlockSeconds;
     51};
     52
    3253typedef QMap<QString,ProgramList>       ProgramMap;
    3354typedef QMap<QString,QString>           Str2StrMap;
    34 typedef QMap<QString,PreviewGenerator*> PreviewMap;
     55typedef QMap<QString,PreviewGenState>  PreviewMap;
    3556typedef QMap<QString,MythTimer>         LastCheckedMap;
    3657
    3758class PlaybackBox : public MythDialog
     
    238259    void keyPressEvent(QKeyEvent *e);
    239260
    240261    bool SetPreviewGenerator(const QString &fn, PreviewGenerator *g);
    241     bool IsGeneratingPreview(const QString &fn) const;
     262    void IncPreviewGeneratorPriority(const QString &fn);
     263    void UpdatePreviewGeneratorThreads(void);
     264    bool IsGeneratingPreview(const QString &fn, bool really = false) const;
    242265    uint IncPreviewGeneratorAttempts(const QString &fn);
    243266
    244267    void SetRecGroupPassword(const QString &oldPasswd,
     
    462485    QString             previewChanid;
    463486    mutable QMutex      previewGeneratorLock;
    464487    PreviewMap          previewGenerator;
    465     QMap<QString,uint>  previewGeneratorAttempts;
     488    vector<QString>     previewGeneratorQueue;
     489    uint                previewGeneratorRunning;
     490    static const uint   previewGeneratorMaxRunning;
    466491
    467492    // Network Control Variables //////////////////////////////////////////////
    468493    mutable QMutex      ncLock;
  • programs/mythfrontend/networkcontrol.cpp

     
    979979
    980980        frameNumber = results[7].toInt();
    981981
    982         bool res = PreviewGenerator::SaveScreenshot(pginfo, outFile,
    983                                      frameNumber, width, height);
     982        PreviewGenerator *previewgen = new PreviewGenerator(pginfo);
     983        previewgen->SetPreviewTimeAsFrameNumber(frameNumber);
     984        previewgen->SetOutputFilename(outFile);
     985        previewgen->SetOutputSize(QSize(width, height));
     986        previewgen->Run();
     987        previewgen->deleteLater();
     988
    984989        delete pginfo;
    985990
    986         if (res)
    987             return QString("OK %1x%2").arg(width).arg(height);
    988         else
    989             return "ERROR: Unable to generate screenshot, check logs";
     991//        if (res)
     992            return QString("OK %1x%2").arg(width>0?width:64).arg(height>0?height:64);
     993//        else
     994//            return "ERROR: Unable to generate screenshot, check logs";
    990995    }
    991996    else
    992997        return "ERROR: Timed out waiting for reply from player";
  • programs/mythbackend/mythxml.cpp

     
    44// Purpose - Html & XML status HttpServerExtension
    55//                                                                           
    66// Created By  : David Blain                    Created On : Oct. 24, 2005
    7 // Modified By :                                Modified On:                 
     7// Modified By : Daniel Kristjansson            Modified On: Oct. 31, 2007
    88//                                                                           
    99//////////////////////////////////////////////////////////////////////////////
    1010
     
    1515#include "libmyth/util.h"
    1616#include "libmyth/mythdbcon.h"
    1717
     18#include "previewgenerator.h"
    1819#include "backendutil.h"
    1920
    2021#include <qtextstream.h>
     
    973974
    974975    // ----------------------------------------------------------------------
    975976
    976     if ((nWidth == 0) && (nHeight == 0))
    977     {
    978         bDefaultPixmap = true;
    979         nWidth         = gContext->GetNumSetting("PreviewPixmapWidth", 160);
    980         nHeight        = gContext->GetNumSetting("PreviewPixmapHeight", 120);
    981     }
     977    bDefaultPixmap = (nWidth == 0) && (nHeight == 0) && (nSecsIn >= 0);
    982978
    983979    // ----------------------------------------------------------------------
    984     // Determine Time the image should be extracted from
    985     // ----------------------------------------------------------------------
    986 
    987     if (nSecsIn == 0)
    988     {
    989         nSecsIn = gContext->GetNumSetting("PreviewPixmapOffset", 64) +
    990                   gContext->GetNumSetting("RecordPreRoll",0);
    991     }
    992 
    993     // ----------------------------------------------------------------------
    994980    // If a specific size/time is requested, don't use cached image.
    995981    // ----------------------------------------------------------------------
    996982    // -=>TODO: should cache custom sized images...
     
    10231009    // Must generate Preview Image, Generate Image and save.
    10241010    // ------------------------------------------------------------------
    10251011
    1026     float fAspect = 0.0;
    1027 
    1028     QImage *pImage = GeneratePreviewImage( pInfo,
    1029                                            sFileName,
    1030                                            nSecsIn,
    1031                                            fAspect );
    1032 
    1033     if (pImage == NULL)
     1012    PreviewGenerator *previewgen = new PreviewGenerator(pInfo, true);
     1013    previewgen->SetPreviewTimeAsSeconds(nSecsIn);
     1014    previewgen->SetOutputFilename(pRequest->m_sFileName);
     1015    previewgen->SetOutputSize(QSize(nWidth, nHeight));
     1016    bool ok = previewgen->Run();
     1017    if (ok)
    10341018    {
    1035         delete pInfo;
    1036         return;
     1019        pRequest->m_eResponseType   = ResponseTypeFile;
     1020        pRequest->m_nResponseStatus = 200;
    10371021    }
    1038 
    1039     // ------------------------------------------------------------------
    1040 
    1041     if (bDefaultPixmap)
    1042     {
    1043 
    1044        if (fAspect <= 0)
    1045            fAspect = (float)(nWidth) / nHeight;
    1046 
    1047        if (fAspect > nWidth / nHeight)
    1048            nHeight = (int)rint(nWidth / fAspect);
    1049        else
    1050            nWidth = (int)rint(nHeight * fAspect);
    1051     }
    1052     else
    1053     {
    1054         if ( nWidth == 0 )
    1055             nWidth = (int)rint(nHeight * fAspect);
    1056 
    1057         if ( nHeight == 0 )
    1058             nHeight = (int)rint(nWidth / fAspect);
    1059 
    1060         /*
    1061         QByteArray aBytes;
    1062         QBuffer    buffer( aBytes );
    1063 
    1064         buffer.open( IO_WriteOnly );
    1065         img.save( &buffer, "PNG" );
    1066 
    1067         pRequest->m_eResponseType     = ResponseTypeOther;
    1068         pRequest->m_sResponseTypeText = pRequest->GetMimeType( "png" );
    1069         pRequest->m_nResponseStatus   = 200;
    1070 
    1071         pRequest->m_response.writeRawBytes( aBytes.data(), aBytes.size() );
    1072         */
    1073     }
    1074 
    1075     QImage img = pImage->smoothScale( nWidth, nHeight);
    1076 
    1077     img.save( pRequest->m_sFileName.ascii(), "PNG" );
    1078 
    1079     delete pImage;
    1080 
    1081     if (pInfo)
    1082         delete pInfo;
    1083 
    1084     pRequest->m_eResponseType   = ResponseTypeFile;
    1085     pRequest->m_nResponseStatus = 200;
     1022    previewgen->deleteLater();
    10861023}
    10871024
    10881025/////////////////////////////////////////////////////////////////////////////
    10891026//                 
    10901027/////////////////////////////////////////////////////////////////////////////
    10911028
    1092 QImage *MythXML::GeneratePreviewImage( ProgramInfo   *pInfo,
    1093                                        const QString &sFileName,
    1094                                        int            nSecsIn,
    1095                                        float         &fAspect )
    1096 {
    1097 
    1098     // Find first Local encoder
    1099 
    1100     EncoderLink *pEncoder = NULL;
    1101 
    1102     for ( QMap<int, EncoderLink *>::Iterator it = m_pEncoders->begin();
    1103           it != m_pEncoders->end();
    1104           ++it )
    1105     {
    1106         if (it.data()->IsLocal())
    1107         {
    1108             pEncoder = it.data();
    1109             break;
    1110         }
    1111     }
    1112 
    1113     if ( pEncoder == NULL)
    1114         return NULL;
    1115 
    1116     // ------------------------------------------------------------------
    1117     // Generate Preview Image and save.
    1118     // ------------------------------------------------------------------
    1119 
    1120     int   nLen    = 0, nWidth = 0, nHeight = 0;
    1121 
    1122     unsigned char *pData = (unsigned char *)pEncoder->GetScreenGrab( pInfo,
    1123                                                                      sFileName,
    1124                                                                      nSecsIn,
    1125                                                                      nLen,
    1126                                                                      nWidth,
    1127                                                                      nHeight,
    1128                                                                      fAspect);
    1129     if (!pData)
    1130         return NULL;
    1131 
    1132     return new QImage( pData, nWidth, nHeight, 32, NULL, 65536 * 65536, QImage::LittleEndian );
    1133 }
    1134 
    1135 /////////////////////////////////////////////////////////////////////////////
    1136 //                 
    1137 /////////////////////////////////////////////////////////////////////////////
    1138 
    11391029void MythXML::GetRecording( HttpWorkerThread *pThread,
    11401030                            HTTPRequest      *pRequest )
    11411031{
  • programs/mythbackend/playbacksock.h

     
    4646    int CheckRecordingActive(const ProgramInfo *pginfo);
    4747    int DeleteRecording(const ProgramInfo *pginfo, bool forceMetadataDelete = false);
    4848    void FillProgramInfo(ProgramInfo *pginfo, QString &playbackhost);
    49     void GenPreviewPixmap(const ProgramInfo *pginfo);
     49    QStringList GenPreviewPixmap(const ProgramInfo *pginfo);
     50    QStringList GenPreviewPixmap(const ProgramInfo *pginfo,
     51                                 bool               time_fmt_sec,
     52                                 long long          time,
     53                                 const QString     &outputFile,
     54                                 const QSize       &outputSize);
    5055    QString PixmapLastModified(const ProgramInfo *pginfo);
    5156    bool CheckFile(ProgramInfo *pginfo);
    5257
  • programs/mythbackend/main.cpp

     
     1#include <sys/time.h>     // for setpriority
     2#include <sys/resource.h> // for setpriority
     3
    14#include <qapplication.h>
    25#include <qsqldatabase.h>
    36#include <qfile.h>
     
    3639#include "libmythtv/dbcheck.h"
    3740#include "libmythtv/jobqueue.h"
    3841#include "libmythtv/storagegroup.h"
     42#include "libmythtv/previewgenerator.h"
    3943
    4044#include "mediaserver.h"
    4145#include "httpstatus.h"
     
    245249    log_rotate(0);
    246250}
    247251
     252int preview_helper(const QString &chanid, const QString &starttime,
     253                   long long previewFrameNumber, long long previewSeconds,
     254                   const QSize &previewSize,
     255                   const QString &infile, const QString &outfile)
     256{
     257    // Lower scheduling priority, to avoid problems with recordings.
     258    if (setpriority(PRIO_PROCESS, 0, 9))
     259        VERBOSE(VB_GENERAL, "Setting priority failed." + ENO);
    248260
     261    ProgramInfo *pginfo = NULL;
     262    if (!chanid.isEmpty() && !starttime.isEmpty())
     263    {
     264        pginfo = ProgramInfo::GetProgramFromRecorded(chanid, starttime);
     265        if (!pginfo)
     266        {
     267            VERBOSE(VB_IMPORTANT, QString(
     268                        "Can not locate recording made on '%1' at '%2'")
     269                    .arg(chanid).arg(starttime));
     270            return GENERIC_EXIT_NOT_OK;
     271        }
     272    }
     273    else if (!infile.isEmpty())
     274    {
     275        pginfo = ProgramInfo::GetProgramFromBasename(infile);
     276        if (!pginfo)
     277        {
     278            VERBOSE(VB_IMPORTANT, QString(
     279                        "Can not locate recording '%1'").arg(infile));
     280            return GENERIC_EXIT_NOT_OK;
     281        }
     282    }
     283    else
     284    {
     285        VERBOSE(VB_IMPORTANT, "Can not locate recording for preview");
     286        return GENERIC_EXIT_NOT_OK;
     287    }
     288
     289    PreviewGenerator *previewgen = new PreviewGenerator(pginfo, true);
     290
     291    if (previewFrameNumber >= 0)
     292        previewgen->SetPreviewTimeAsFrameNumber(previewFrameNumber);
     293
     294    if (previewSeconds >= 0)
     295        previewgen->SetPreviewTimeAsSeconds(previewSeconds);
     296
     297    previewgen->SetOutputSize(previewSize);
     298    previewgen->SetOutputFilename(outfile);
     299    previewgen->RunReal();
     300    previewgen->deleteLater();
     301
     302    delete pginfo;
     303
     304    return GENERIC_EXIT_OK;
     305}
     306
     307// [WxH] | [WxH@]seconds[S] | [WxH@]frame_numF
     308bool parse_preview_info(const QString &param,
     309                        long long     &previewFrameNumber,
     310                        long long     &previewSeconds,
     311                        QSize         &previewSize)
     312{
     313    previewFrameNumber = -1;
     314    previewSeconds = -1;
     315    previewSize = QSize(0,0);
     316    if (param.isEmpty())
     317        return true;
     318
     319    int xat = param.find("x", 0, false);
     320    int aat = param.find("@", 0, false);
     321    if (xat > 0)
     322    {
     323        QString widthStr  = param.left(xat);
     324        QString heightStr = QString::null;
     325        if (aat > xat)
     326            heightStr = param.mid(xat + 1, aat - xat - 1);
     327        else
     328            heightStr = param.mid(xat + 1);
     329
     330        bool ok1, ok2;
     331        previewSize = QSize(widthStr.toInt(&ok1), heightStr.toInt(&ok2));
     332        if (!ok1 || !ok2)
     333        {
     334            VERBOSE(VB_IMPORTANT, QString(
     335                        "Error: Failed to parse --generate-preview "
     336                        "param '%1'").arg(param));
     337        }
     338    }
     339    if ((xat > 0) && (aat < xat))
     340        return true;
     341
     342    QString lastChar = param.at(param.length() - 1).lower();
     343    QString frameNumStr = QString::null;
     344    QString secsStr = QString::null;
     345    if (lastChar == "f")
     346        frameNumStr = param.mid(aat + 1, param.length() - aat - 2);
     347    else if (lastChar == "s")
     348        secsStr = param.mid(aat + 1, param.length() - aat - 2);
     349    else
     350        secsStr = param.mid(aat + 1, param.length() - aat - 1);
     351
     352    bool ok = false;
     353    if (!frameNumStr.isEmpty())
     354        previewFrameNumber = frameNumStr.toUInt(&ok);
     355    else if (!secsStr.isEmpty())
     356        previewSeconds = secsStr.toUInt(&ok);
     357
     358    if (!ok)
     359    {
     360        VERBOSE(VB_IMPORTANT, QString(
     361                    "Error: Failed to parse --generate-preview "
     362                    "param '%1'").arg(param));
     363    }
     364
     365    return ok;
     366}
     367
    249368int main(int argc, char **argv)
    250369{
    251370    for(int i = 3; i < sysconf(_SC_OPEN_MAX) - 1; ++i)
     
    256375    QMap<QString, QString> settingsOverride;
    257376    QString binname = basename(a.argv()[0]);
    258377
     378    long long previewFrameNumber = -2;
     379    long long previewSeconds = -2;
     380    QSize previewSize(0,0);
     381    QString chanid = QString::null;
     382    QString starttime = QString::null;
     383    QString infile = QString::null;
     384    QString outfile = QString::null;
     385
    259386    bool daemonize = false;
    260387    bool printsched = false;
    261388    bool testsched = false;
     
    417544#endif
    418545            return BACKEND_EXIT_OK;
    419546        }
     547        else if (!strcmp(a.argv()[argpos],"--generate-preview"))
     548        {
     549            QString tmp = QString::null;
     550            if ((argpos + 1) < a.argc())
     551            {
     552                tmp = a.argv()[argpos+1];
     553                bool ok = true;
     554                if (tmp.left(1) == "-")
     555                    tmp.left(2).toInt(&ok);
     556                if (ok)
     557                    argpos++;
     558                else
     559                    tmp = QString::null;
     560            }
     561
     562            if (!parse_preview_info(tmp, previewFrameNumber, previewSeconds,
     563                                    previewSize))
     564            {
     565                VERBOSE(VB_IMPORTANT,
     566                        QString("Unable to parse --generate-preview option '%1'")
     567                        .arg(tmp));
     568                return BACKEND_EXIT_INVALID_CMDLINE;
     569            }
     570        }
     571        else if (!strcmp(a.argv()[argpos],"-c") ||
     572                 !strcmp(a.argv()[argpos],"--chanid"))
     573        {
     574            if (((argpos + 1) >= a.argc()) ||
     575                !strncmp(a.argv()[argpos + 1], "-", 1))
     576            {
     577                VERBOSE(VB_IMPORTANT,
     578                        "Missing or invalid parameters for --chanid option");
     579                return BACKEND_EXIT_INVALID_CMDLINE;
     580            }
     581
     582            chanid = a.argv()[++argpos];
     583        }
     584        else if (!strcmp(a.argv()[argpos],"-s") ||
     585                 !strcmp(a.argv()[argpos],"--starttime"))
     586        {
     587            if (((argpos + 1) >= a.argc()) ||
     588                !strncmp(a.argv()[argpos + 1], "-", 1))
     589            {
     590                VERBOSE(VB_IMPORTANT,
     591                        "Missing or invalid parameters for --starttime option");
     592                return BACKEND_EXIT_INVALID_CMDLINE;
     593            }
     594
     595            starttime = a.argv()[++argpos];
     596        }
     597        else if (!strcmp(a.argv()[argpos],"--infile"))
     598        {
     599            if (((argpos + 1) >= a.argc()) ||
     600                !strncmp(a.argv()[argpos + 1], "-", 1))
     601            {
     602                VERBOSE(VB_IMPORTANT,
     603                        "Missing or invalid parameters for --infile option");
     604                return BACKEND_EXIT_INVALID_CMDLINE;
     605            }
     606
     607            infile = a.argv()[++argpos];
     608        }
     609        else if (!strcmp(a.argv()[argpos],"--outfile"))
     610        {
     611            if (((argpos + 1) >= a.argc()) ||
     612                !strncmp(a.argv()[argpos + 1], "-", 1))
     613            {
     614                VERBOSE(VB_IMPORTANT,
     615                        "Missing or invalid parameters for --outfile option");
     616                return BACKEND_EXIT_INVALID_CMDLINE;
     617            }
     618
     619            outfile = a.argv()[++argpos];
     620        }
    420621        else
    421622        {
    422623            if (!(!strcmp(a.argv()[argpos],"-h") ||
     
    439640                    "--nohousekeeper                Do not start the Housekeeper" << endl <<
    440641                    "--noautoexpire                 Do not start the AutoExpire thread" << endl <<
    441642                    "--clearcache                   Clear the settings cache on all myth servers" << endl <<
    442                     "--version                      Version information" << endl;
     643                    "--version                      Version information" << endl <<
     644                    "--generate-preview             Generate a preview image" << endl <<
     645                    "--infile                       Input file for preview generation" << endl <<
     646                    "--outfile                      Optional output file for preview generation" << endl <<
     647                    "--chanid                       Channel ID for preview generation" << endl <<
     648                    "--starttime                    Recording start time for preview generation" << endl;
    443649            return BACKEND_EXIT_INVALID_CMDLINE;
    444650        }
    445651    }
    446652
     653    if (((previewFrameNumber >= -1) || previewSeconds >= -1) &&
     654        (chanid.isEmpty() || starttime.isEmpty()) && infile.isEmpty())
     655    {
     656        cerr << "--generate-preview must be accompanied by either " <<endl
     657             << "\nboth --chanid and --starttime paramaters, " << endl
     658             << "\nor the --infile paramater." << endl;
     659        return BACKEND_EXIT_INVALID_CMDLINE;
     660    }
     661
    447662    if (logfile != "" )
    448663    {
    449664        if (log_rotate(1) < 0)
     
    576791        return BACKEND_EXIT_OK;
    577792    }
    578793
     794    if ((previewFrameNumber >= -1) || (previewSeconds >= -1))
     795    {
     796        return preview_helper(
     797            chanid, starttime,
     798            previewFrameNumber, previewSeconds, previewSize,
     799            infile, outfile);
     800    }
     801
    579802    int port = gContext->GetNumSetting("BackendServerPort", 6543);
    580803
    581804    QString myip = gContext->GetSetting("BackendServerIP");
  • programs/mythbackend/encoderlink.h

     
    103103                        QString callsign, QString channum,
    104104                        QString channame, QString xmltv);
    105105
    106     char *GetScreenGrab(const ProgramInfo *pginfo, const QString &filename,
    107                         int secondsin, int &bufferlen,
    108                         int &video_width, int &video_height,
    109                         float &video_aspect);
    110 
    111106  private:
    112107    int m_capturecardnum;
    113108
  • programs/mythbackend/playbacksock.cpp

     
    152152    pginfo->FromStringList(strlist, 0);
    153153}
    154154
    155 void PlaybackSock::GenPreviewPixmap(const ProgramInfo *pginfo)
     155QStringList PlaybackSock::GenPreviewPixmap(const ProgramInfo *pginfo)
    156156{
    157157    QStringList strlist = QString("QUERY_GENPIXMAP");
    158158    pginfo->ToStringList(strlist);
    159159
    160160    SendReceiveStringList(strlist);
     161
     162    return strlist;
    161163}
    162164
     165QStringList PlaybackSock::GenPreviewPixmap(const ProgramInfo *pginfo,
     166                                           bool               time_fmt_sec,
     167                                           long long          time,
     168                                           const QString     &outputFile,
     169                                           const QSize       &outputSize)
     170{
     171    QStringList strlist = "QUERY_GENPIXMAP";
     172    pginfo->ToStringList(strlist);
     173    strlist.push_back(time_fmt_sec ? "s" : "f");
     174    encodeLongLong(strlist, time);
     175    strlist.push_back(outputFile.isEmpty() ? "<EMPTY>" : outputFile);
     176    strlist.push_back(QString::number(outputSize.width()));
     177    strlist.push_back(QString::number(outputSize.height()));
     178
     179    SendReceiveStringList(strlist);
     180
     181    return strlist;
     182}
     183
    163184QString PlaybackSock::PixmapLastModified(const ProgramInfo *pginfo)
    164185{
    165186    QStringList strlist = QString("QUERY_PIXMAP_LASTMODIFIED");
  • programs/mythbackend/mythxml.h

     
    104104        void    GetConnectionInfo( HTTPRequest *pRequest );
    105105        void    GetAlbumArt    ( HTTPRequest *pRequest );
    106106
    107         QImage *GeneratePreviewImage( ProgramInfo   *pInfo,
    108                                       const QString &sFileName,
    109                                       int            nSecsIn,
    110                                       float         &fAspect );
    111 
    112107        void    GetExpiring    ( HTTPRequest *pRequest );
    113108
    114109        void    GetRecording   ( HttpWorkerThread *pThread,
  • programs/mythbackend/mainserver.cpp

     
    36583658{
    36593659    MythSocket *pbssock = pbs->getSocket();
    36603660
     3661    bool      time_fmt_sec   = true;
     3662    long long time           = -1;
     3663    QString   outputfile     = QString::null;
     3664    int       width          = -1;
     3665    int       height         = -1;
     3666    bool      has_extra_data = false;
     3667
     3668    QStringList::iterator it = slist.at(1);
    36613669    ProgramInfo *pginfo = new ProgramInfo();
    3662     pginfo->FromStringList(slist, 1);
     3670    bool ok = pginfo->FromStringList(slist, it);
     3671    if (!ok)
     3672    {
     3673        VERBOSE(VB_IMPORTANT, "MainServer: Failed to parse pixmap request.");
     3674        QStringList outputlist = "BAD";
     3675        outputlist += "ERROR_INVALID_REQUEST";
     3676        SendResponse(pbssock, outputlist);
     3677    }
     3678    if (it != slist.end())
     3679        (time_fmt_sec = ((*it).lower() == "s")), it++;
     3680    if (it != slist.end())
     3681        time = decodeLongLong(slist, it);
     3682    if (it != slist.end())
     3683        (outputfile = *it), it++;
     3684    outputfile = (outputfile == "<EMPTY>") ? QString::null : outputfile;
     3685    if (it != slist.end())
     3686    {
     3687        width = (*it).toInt(&ok); it++;
     3688        width = (ok) ? width : -1;
     3689    }
     3690    if (it != slist.end())
     3691    {
     3692        height = (*it).toInt(&ok); it++;
     3693        height = (ok) ? height : -1;
     3694        has_extra_data = true;
     3695    }
     3696    QSize outputsize = QSize(width, height);
     3697
     3698    if (has_extra_data)
     3699    {
     3700        VERBOSE(VB_IMPORTANT, "HandleGenPreviewPixmap got extra data\n\t\t\t"
     3701                << QString("%1%2 %3x%4 '%5'")
     3702                .arg(time).arg(time_fmt_sec?"s":"f")
     3703                .arg(width).arg(height).arg(outputfile));
     3704    }
     3705
    36633706    pginfo->pathname = GetPlaybackURL(pginfo);
    36643707
    36653708    if ((ismaster) &&
     
    36713714
    36723715        if (slave)
    36733716        {
    3674             slave->GenPreviewPixmap(pginfo);
     3717            QStringList outputlist = "OK";
     3718            if (has_extra_data)
     3719            {
     3720                outputlist = slave->GenPreviewPixmap(
     3721                    pginfo, time_fmt_sec, time, outputfile, outputsize);
     3722            }
     3723            else
     3724            {
     3725                outputlist = slave->GenPreviewPixmap(pginfo);
     3726            }
     3727
    36753728            slave->DownRef();
    3676             QStringList outputlist = "OK";
    36773729            SendResponse(pbssock, outputlist);
    36783730            delete pginfo;
    36793731            return;
     
    36893741        VERBOSE(VB_IMPORTANT, "MainServer: HandleGenPreviewPixmap: Unable to "
    36903742                "find file locally, unable to make preview image.");
    36913743        QStringList outputlist = "BAD";
     3744        outputlist += "ERROR_NOFILE";
    36923745        SendResponse(pbssock, outputlist);
    36933746        delete pginfo;
    36943747        return;
    36953748    }
    36963749
    3697     int len = 0;
    3698     int width = 0, height = 0;
    3699     float aspect = 0;
    3700     int secondsin = gContext->GetNumSetting("PreviewPixmapOffset", 64) +
    3701                     gContext->GetNumSetting("RecordPreRoll",0);
     3750    PreviewGenerator *previewgen = new PreviewGenerator(pginfo);
     3751    ok = previewgen->Run();
     3752    previewgen->deleteLater();
    37023753
    3703     unsigned char *data = (unsigned char *)
    3704         PreviewGenerator::GetScreenGrab(pginfo, pginfo->pathname, secondsin,
    3705                                         len, width, height, aspect);
    3706 
    3707     if (data && PreviewGenerator::SavePreview(pginfo->pathname + ".png", data,
    3708                                               width, height, aspect))
     3754    if (ok)
    37093755    {
    3710         QStringList retlist = "OK";
    3711         SendResponse(pbssock, retlist);   
     3756        QStringList outputlist = "OK";
     3757        if (!outputfile.isEmpty())
     3758            outputlist += outputfile;
     3759        SendResponse(pbssock, outputlist);
    37123760    }
    37133761    else
    37143762    {
    37153763        VERBOSE(VB_IMPORTANT, "MainServer: Failed to make preview image.");
    37163764        QStringList outputlist = "BAD";
     3765        outputlist += "ERROR_UNKNOWN";
    37173766        SendResponse(pbssock, outputlist);
    37183767    }
    37193768
    3720     if (data)
    3721         delete [] data;
    37223769    delete pginfo;
    37233770}
    37243771
  • programs/mythbackend/encoderlink.cpp

     
    836836                              callsign, channum, channame, xmltv);
    837837}
    838838
    839 /** \fn EncoderLink::GetScreenGrab(const ProgramInfo*,const QString&,int,int&,int&,int&,float&)
    840  *  \brief Returns a PIX_FMT_RGBA32 buffer containg a frame from the video.
    841  *         <b>This only works on local recorders.</b>
    842  *  \param pginfo       Recording to grab from.
    843  *  \param filename     File containing recording.
    844  *  \param secondsin    Seconds into the video to seek before capturing a frame.
    845  *  \param bufferlen    Returns size of buffer returned (in bytes).
    846  *  \param video_width  Returns width of frame grabbed.
    847  *  \param video_height Returns height of frame grabbed.
    848  *  \param video_aspect Returns the aspect ratio of frame grabbed.
    849  *  \return Buffer allocated with new containing frame in RGBA32 format if
    850  *          successful, NULL otherwise.
    851  */
    852 char *EncoderLink::GetScreenGrab(const ProgramInfo *pginfo,
    853                                  const QString &filename,
    854                                  int secondsin, int &bufferlen,
    855                                  int &video_width, int &video_height,
    856                                  float &video_aspect)
    857 {
    858     if (local)
    859     {
    860         return PreviewGenerator::GetScreenGrab(
    861             pginfo, filename, secondsin, bufferlen,
    862             video_width, video_height, video_aspect);
    863     }
    864     VERBOSE(VB_IMPORTANT, "Should be local only query: GetScreenGrab");
    865     return NULL;
    866 }
    867 
    868839/* vim: set expandtab tabstop=4 shiftwidth=4: */