00001
00002 #include <cmath>
00003
00004
00005 #include <sys/types.h>
00006 #include <sys/time.h>
00007 #include <fcntl.h>
00008 #include <utime.h>
00009
00010
00011 #include <QCoreApplication>
00012 #include <QTemporaryFile>
00013 #include <QFileInfo>
00014 #include <QMetaType>
00015 #include <QImage>
00016 #include <QDir>
00017 #include <QUrl>
00018
00019
00020 #include "mythconfig.h"
00021
00022 #include "ringbuffer.h"
00023 #include "mythplayer.h"
00024 #include "previewgenerator.h"
00025 #include "tv_rec.h"
00026 #include "mythsocket.h"
00027 #include "remotefile.h"
00028 #include "storagegroup.h"
00029 #include "mythmiscutil.h"
00030 #include "playercontext.h"
00031 #include "mythdirs.h"
00032 #include "remoteutil.h"
00033 #include "mythsystem.h"
00034 #include "exitcodes.h"
00035 #include "mythlogging.h"
00036
00037 #define LOC QString("Preview: ")
00038
00067 PreviewGenerator::PreviewGenerator(const ProgramInfo *pginfo,
00068 const QString &_token,
00069 PreviewGenerator::Mode _mode)
00070 : MThread("PreviewGenerator"),
00071 programInfo(*pginfo), mode(_mode), listener(NULL),
00072 pathname(pginfo->GetPathname()),
00073 timeInSeconds(true), captureTime(-1), outFileName(QString::null),
00074 outSize(0,0), token(_token), gotReply(false), pixmapOk(false)
00075 {
00076 }
00077
00078 PreviewGenerator::~PreviewGenerator()
00079 {
00080 TeardownAll();
00081 wait();
00082 }
00083
00084 void PreviewGenerator::SetOutputFilename(const QString &fileName)
00085 {
00086 outFileName = fileName;
00087 }
00088
00089 void PreviewGenerator::TeardownAll(void)
00090 {
00091 QMutexLocker locker(&previewLock);
00092 previewWaitCondition.wakeAll();
00093 listener = NULL;
00094 }
00095
00096 void PreviewGenerator::deleteLater()
00097 {
00098 TeardownAll();
00099 QObject::deleteLater();
00100 }
00101
00102 void PreviewGenerator::AttachSignals(QObject *obj)
00103 {
00104 QMutexLocker locker(&previewLock);
00105 listener = obj;
00106 }
00107
00111 bool PreviewGenerator::RunReal(void)
00112 {
00113 QString msg;
00114 QTime tm = QTime::currentTime();
00115 bool ok = false;
00116 bool is_local = IsLocal();
00117
00118 if (!is_local && !!(mode & kRemote))
00119 {
00120 LOG(VB_GENERAL, LOG_ERR, LOC +
00121 QString("RunReal() file not local: '%1'")
00122 .arg(pathname));
00123 }
00124 else if (!(mode & kLocal) && !(mode & kRemote))
00125 {
00126 LOG(VB_GENERAL, LOG_ERR, LOC +
00127 QString("RunReal() Preview of '%1' failed "
00128 "because mode was invalid 0x%2")
00129 .arg(pathname).arg((int)mode,0,16));
00130 }
00131 else if (is_local && !!(mode & kLocal) && LocalPreviewRun())
00132 {
00133 ok = true;
00134 msg = QString("Generated on %1 in %2 seconds, starting at %3")
00135 .arg(gCoreContext->GetHostName())
00136 .arg(tm.elapsed()*0.001)
00137 .arg(tm.toString(Qt::ISODate));
00138 }
00139 else if (!!(mode & kRemote))
00140 {
00141 if (is_local && (mode & kLocal))
00142 {
00143 LOG(VB_GENERAL, LOG_WARNING, LOC + "Failed to save preview."
00144 "\n\t\t\tYou may need to check user and group ownership on"
00145 "\n\t\t\tyour frontend and backend for quicker previews.\n"
00146 "\n\t\t\tAttempting to regenerate preview on backend.\n");
00147 }
00148 ok = RemotePreviewRun();
00149 if (ok)
00150 {
00151 msg = QString("Generated remotely in %1 seconds, starting at %2")
00152 .arg(tm.elapsed()*0.001)
00153 .arg(tm.toString(Qt::ISODate));
00154 }
00155 else
00156 {
00157 msg = "Remote preview failed";
00158 }
00159 }
00160 else
00161 {
00162 msg = "Could not access recording";
00163 }
00164
00165 QMutexLocker locker(&previewLock);
00166 if (listener)
00167 {
00168 QString output_fn = outFileName.isEmpty() ?
00169 (programInfo.GetPathname()+".png") : outFileName;
00170
00171 QDateTime dt;
00172 if (ok)
00173 {
00174 QFileInfo fi(output_fn);
00175 if (fi.exists())
00176 dt = fi.lastModified();
00177 }
00178
00179 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED";
00180 QStringList list;
00181 list.push_back(programInfo.MakeUniqueKey());
00182 list.push_back(output_fn);
00183 list.push_back(msg);
00184 list.push_back(dt.isValid()?dt.toString(Qt::ISODate):"");
00185 list.push_back(token);
00186 QCoreApplication::postEvent(listener, new MythEvent(message, list));
00187 }
00188
00189 return ok;
00190 }
00191
00192 bool PreviewGenerator::Run(void)
00193 {
00194 QString msg;
00195 QDateTime dtm = QDateTime::currentDateTime();
00196 QTime tm = QTime::currentTime();
00197 bool ok = false;
00198 QString command = GetInstallPrefix() + "/bin/mythpreviewgen";
00199 bool local_ok = ((IsLocal() || !!(mode & kForceLocal)) &&
00200 (!!(mode & kLocal)) &&
00201 QFileInfo(command).isExecutable());
00202 if (!local_ok)
00203 {
00204 if (!!(mode & kRemote))
00205 {
00206 ok = RemotePreviewRun();
00207 if (ok)
00208 {
00209 msg =
00210 QString("Generated remotely in %1 seconds, starting at %2")
00211 .arg(tm.elapsed()*0.001)
00212 .arg(tm.toString(Qt::ISODate));
00213 }
00214 }
00215 else
00216 {
00217 LOG(VB_GENERAL, LOG_ERR, LOC +
00218 QString("Run() cannot generate preview locally for: '%1'")
00219 .arg(pathname));
00220 msg = "Failed, local preview requested for remote file.";
00221 }
00222 }
00223 else
00224 {
00225
00226 command += QString(" --size %1x%2")
00227 .arg(outSize.width()).arg(outSize.height());
00228 if (captureTime >= 0)
00229 {
00230 if (timeInSeconds)
00231 command += QString(" --seconds %1").arg(captureTime);
00232 else
00233 command += QString(" --frame %1").arg(captureTime);
00234 }
00235 command += QString(" --chanid %1").arg(programInfo.GetChanID());
00236 command += QString(" --starttime %1")
00237 .arg(programInfo.GetRecordingStartTime(MythDate));
00238
00239 if (!outFileName.isEmpty())
00240 command += QString(" --outfile \"%1\"").arg(outFileName);
00241
00242 command += logPropagateArgs;
00243 if (!logPropagateQuiet())
00244 command += " --quiet";
00245
00246
00247 uint ret = myth_system(command, kMSDontBlockInputDevs |
00248 kMSDontDisableDrawing |
00249 kMSProcessEvents, 30);
00250 if (ret != GENERIC_EXIT_OK)
00251 {
00252 LOG(VB_GENERAL, LOG_ERR, LOC +
00253 QString("Encountered problems running '%1' (%2)")
00254 .arg(command) .arg(ret));
00255 }
00256 else
00257 {
00258 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process returned 0.");
00259 QString outname = (!outFileName.isEmpty()) ?
00260 outFileName : (pathname + ".png");
00261
00262 QString lpath = QFileInfo(outname).fileName();
00263 if (lpath == outname)
00264 {
00265 StorageGroup sgroup;
00266 QString tmpFile = sgroup.FindFile(lpath);
00267 outname = (tmpFile.isEmpty()) ? outname : tmpFile;
00268 }
00269
00270 QFileInfo fi(outname);
00271 ok = (fi.exists() && fi.isReadable() && fi.size());
00272 if (ok)
00273 {
00274 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Preview process ran ok.");
00275 msg = QString("Generated on %1 in %2 seconds, starting at %3")
00276 .arg(gCoreContext->GetHostName())
00277 .arg(tm.elapsed()*0.001)
00278 .arg(tm.toString(Qt::ISODate));
00279 }
00280 else
00281 {
00282 LOG(VB_GENERAL, LOG_ERR, LOC + "Preview process not ok." +
00283 QString("\n\t\t\tfileinfo(%1)").arg(outname) +
00284 QString(" exists: %1").arg(fi.exists()) +
00285 QString(" readable: %1").arg(fi.isReadable()) +
00286 QString(" size: %1").arg(fi.size()));
00287 LOG(VB_GENERAL, LOG_ERR, LOC +
00288 QString("Despite command '%1' returning success")
00289 .arg(command));
00290 msg = QString("Failed to read preview image despite "
00291 "preview process returning success.");
00292 }
00293 }
00294 }
00295
00296 QMutexLocker locker(&previewLock);
00297
00298
00299
00300 QString output_fn = outFileName.isEmpty() ?
00301 (programInfo.GetPathname()+".png") : outFileName;
00302
00303 QDateTime dt;
00304 if (ok)
00305 {
00306 QFileInfo fi(output_fn);
00307 if (fi.exists())
00308 dt = fi.lastModified();
00309 }
00310
00311 QString message = (ok) ? "PREVIEW_SUCCESS" : "PREVIEW_FAILED";
00312 if (listener)
00313 {
00314 QStringList list;
00315 list.push_back(programInfo.MakeUniqueKey());
00316 list.push_back(outFileName.isEmpty() ?
00317 (programInfo.GetPathname()+".png") : outFileName);
00318 list.push_back(msg);
00319 list.push_back(dt.isValid()?dt.toString(Qt::ISODate):"");
00320 list.push_back(token);
00321 QCoreApplication::postEvent(listener, new MythEvent(message, list));
00322 }
00323
00324 return ok;
00325 }
00326
00327 void PreviewGenerator::run(void)
00328 {
00329 RunProlog();
00330 Run();
00331 RunEpilog();
00332 }
00333
00334 bool PreviewGenerator::RemotePreviewRun(void)
00335 {
00336 QStringList strlist( "QUERY_GENPIXMAP2" );
00337 if (token.isEmpty())
00338 {
00339 token = QString("%1:%2")
00340 .arg(programInfo.MakeUniqueKey()).arg(random());
00341 }
00342 strlist.push_back(token);
00343 programInfo.ToStringList(strlist);
00344 strlist.push_back(timeInSeconds ? "s" : "f");
00345 strlist.push_back(QString::number(captureTime));
00346 if (outFileName.isEmpty())
00347 {
00348 strlist.push_back("<EMPTY>");
00349 }
00350 else
00351 {
00352 QFileInfo fi(outFileName);
00353 strlist.push_back(fi.fileName());
00354 }
00355 strlist.push_back(QString::number(outSize.width()));
00356 strlist.push_back(QString::number(outSize.height()));
00357
00358 gCoreContext->addListener(this);
00359 pixmapOk = false;
00360
00361 bool ok = gCoreContext->SendReceiveStringList(strlist);
00362 if (!ok || strlist.empty() || (strlist[0] != "OK"))
00363 {
00364 if (!ok)
00365 {
00366 LOG(VB_GENERAL, LOG_ERR, LOC +
00367 "Remote Preview failed due to communications error.");
00368 }
00369 else if (strlist.size() > 1)
00370 {
00371 LOG(VB_GENERAL, LOG_ERR, LOC +
00372 "Remote Preview failed, reason given: " + strlist[1]);
00373 }
00374
00375 gCoreContext->removeListener(this);
00376
00377 return false;
00378 }
00379
00380 QMutexLocker locker(&previewLock);
00381
00382
00383
00384 if (!gotReply)
00385 previewWaitCondition.wait(&previewLock, 35 * 1000);
00386
00387 if (!gotReply)
00388 LOG(VB_GENERAL, LOG_NOTICE, LOC + "RemotePreviewRun() -- no reply..");
00389
00390 gCoreContext->removeListener(this);
00391
00392 return pixmapOk;
00393 }
00394
00395 bool PreviewGenerator::event(QEvent *e)
00396 {
00397 if (e->type() != (QEvent::Type) MythEvent::MythEventMessage)
00398 return QObject::event(e);
00399
00400 MythEvent *me = (MythEvent*)e;
00401 if (me->Message() != "GENERATED_PIXMAP" || me->ExtraDataCount() < 3)
00402 return QObject::event(e);
00403
00404 bool ok = me->ExtraData(0) == "OK";
00405 bool ours = false;
00406 uint i = ok ? 4 : 3;
00407 for (; i < (uint) me->ExtraDataCount() && !ours; i++)
00408 ours |= me->ExtraData(i) == token;
00409 if (!ours)
00410 return false;
00411
00412 QString pginfokey = me->ExtraData(1);
00413
00414 QMutexLocker locker(&previewLock);
00415 gotReply = true;
00416 pixmapOk = ok;
00417 if (!ok)
00418 {
00419 LOG(VB_GENERAL, LOG_ERR, LOC + pginfokey + ": " + me->ExtraData(2));
00420 previewWaitCondition.wakeAll();
00421 return true;
00422 }
00423
00424 if (me->ExtraDataCount() < 5)
00425 {
00426 pixmapOk = false;
00427 previewWaitCondition.wakeAll();
00428 return true;
00429 }
00430
00431 QDateTime datetime = QDateTime::fromString(me->ExtraData(3), Qt::ISODate);
00432 if (!datetime.isValid())
00433 {
00434 pixmapOk = false;
00435 LOG(VB_GENERAL, LOG_ERR, LOC + pginfokey + "Got invalid date");
00436 previewWaitCondition.wakeAll();
00437 return false;
00438 }
00439
00440 size_t length = me->ExtraData(4).toULongLong();
00441 quint16 checksum16 = me->ExtraData(5).toUInt();
00442 QByteArray data = QByteArray::fromBase64(me->ExtraData(6).toAscii());
00443 if ((size_t) data.size() < length)
00444 {
00445
00446 LOG(VB_GENERAL, LOG_ERR, LOC +
00447 QString("Preview size check failed %1 < %2")
00448 .arg(data.size()).arg(length));
00449 data.clear();
00450 }
00451 data.resize(length);
00452
00453 if (checksum16 != qChecksum(data.constData(), data.size()))
00454 {
00455 LOG(VB_GENERAL, LOG_ERR, LOC + "Preview checksum failed");
00456 data.clear();
00457 }
00458
00459 pixmapOk = (data.isEmpty()) ? false : SaveOutFile(data, datetime);
00460
00461 previewWaitCondition.wakeAll();
00462
00463 return true;
00464 }
00465
00466 bool PreviewGenerator::SaveOutFile(const QByteArray &data, const QDateTime &dt)
00467 {
00468 if (outFileName.isEmpty())
00469 {
00470 QString remotecachedirname =
00471 QString("%1/remotecache").arg(GetConfDir());
00472 QDir remotecachedir(remotecachedirname);
00473
00474 if (!remotecachedir.exists())
00475 {
00476 if (!remotecachedir.mkdir(remotecachedirname))
00477 {
00478 LOG(VB_GENERAL, LOG_ERR, LOC +
00479 "Remote Preview failed because we could not create a "
00480 "remote cache directory");
00481 return false;
00482 }
00483 }
00484
00485 QString filename = programInfo.GetBasename() + ".png";
00486 outFileName = QString("%1/%2").arg(remotecachedirname).arg(filename);
00487 }
00488
00489 QFile file(outFileName);
00490 bool ok = file.open(QIODevice::Unbuffered|QIODevice::WriteOnly);
00491 if (!ok)
00492 {
00493 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open: '%1'")
00494 .arg(outFileName));
00495 }
00496
00497 off_t offset = 0;
00498 size_t remaining = data.size();
00499 uint failure_cnt = 0;
00500 while ((remaining > 0) && (failure_cnt < 5))
00501 {
00502 ssize_t written = file.write(data.data() + offset, remaining);
00503 if (written < 0)
00504 {
00505 failure_cnt++;
00506 usleep(50000);
00507 continue;
00508 }
00509
00510 failure_cnt = 0;
00511 offset += written;
00512 remaining -= written;
00513 }
00514
00515 if (ok && !remaining)
00516 {
00517 file.close();
00518 struct utimbuf times;
00519 times.actime = times.modtime = dt.toTime_t();
00520 utime(outFileName.toLocal8Bit().constData(), ×);
00521 LOG(VB_FILE, LOG_INFO, LOC + QString("Saved: '%1'").arg(outFileName));
00522 }
00523 else
00524 {
00525 file.remove();
00526 }
00527
00528 return ok;
00529 }
00530
00531 bool PreviewGenerator::SavePreview(QString filename,
00532 const unsigned char *data,
00533 uint width, uint height, float aspect,
00534 int desired_width, int desired_height)
00535 {
00536 if (!data || !width || !height)
00537 return false;
00538
00539 if( height == 1088 )
00540 {
00541
00542
00543
00544
00545
00546 height = 1080;
00547 }
00548
00549 const QImage img((unsigned char*) data,
00550 width, height, QImage::Format_RGB32);
00551
00552 float ppw = max(desired_width, 0);
00553 float pph = max(desired_height, 0);
00554 bool desired_size_exactly_specified = true;
00555 if ((ppw < 1.0f) && (pph < 1.0f))
00556 {
00557 ppw = gCoreContext->GetNumSetting("PreviewPixmapWidth", 320);
00558 pph = gCoreContext->GetNumSetting("PreviewPixmapHeight", 240);
00559 desired_size_exactly_specified = false;
00560 }
00561
00562 aspect = (aspect <= 0.0f) ? ((float) width) / height : aspect;
00563 pph = (pph < 1.0f) ? (ppw / aspect) : pph;
00564 ppw = (ppw < 1.0f) ? (pph * aspect) : ppw;
00565
00566 if (!desired_size_exactly_specified)
00567 {
00568 if (aspect > ppw / pph)
00569 pph = (ppw / aspect);
00570 else
00571 ppw = (pph * aspect);
00572 }
00573
00574 ppw = max(1.0f, ppw);
00575 pph = max(1.0f, pph);;
00576
00577 QImage small_img = img.scaled((int) ppw, (int) pph,
00578 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
00579
00580 QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX");
00581 f.setAutoRemove(false);
00582 if (f.open() && small_img.save(&f, "PNG"))
00583 {
00584
00585 makeFileAccessible(f.fileName().toLocal8Bit().constData());
00586 QFile of(filename);
00587 of.remove();
00588 if (f.rename(filename))
00589 {
00590 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Saved preview '%0' %1x%2")
00591 .arg(filename).arg((int) ppw).arg((int) pph));
00592 return true;
00593 }
00594 f.remove();
00595 }
00596
00597 return false;
00598 }
00599
00600 bool PreviewGenerator::LocalPreviewRun(void)
00601 {
00602 programInfo.MarkAsInUse(true, kPreviewGeneratorInUseID);
00603
00604 float aspect = 0;
00605 int width, height, sz;
00606 long long captime = captureTime;
00607
00608 QDateTime dt = QDateTime::currentDateTime();
00609
00610 if (captime > 0)
00611 LOG(VB_GENERAL, LOG_INFO, "Preview from time spec");
00612
00613 if (captime < 0)
00614 {
00615 captime = programInfo.QueryBookmark();
00616 if (captime > 0)
00617 {
00618 timeInSeconds = false;
00619 LOG(VB_GENERAL, LOG_INFO,
00620 QString("Preview from bookmark (frame %1)").arg(captime));
00621 }
00622 else
00623 captime = -1;
00624 }
00625
00626 if (captime <= 0)
00627 {
00628 timeInSeconds = true;
00629 int startEarly = 0;
00630 int programDuration = 0;
00631 int preroll = gCoreContext->GetNumSetting("RecordPreRoll", 0);
00632 if (programInfo.GetScheduledStartTime().isValid() &&
00633 programInfo.GetScheduledEndTime().isValid() &&
00634 (programInfo.GetScheduledStartTime() !=
00635 programInfo.GetScheduledEndTime()))
00636 {
00637 programDuration = programInfo.GetScheduledStartTime()
00638 .secsTo(programInfo.GetScheduledEndTime());
00639 }
00640 if (programInfo.GetRecordingStartTime().isValid() &&
00641 programInfo.GetScheduledStartTime().isValid() &&
00642 (programInfo.GetRecordingStartTime() !=
00643 programInfo.GetScheduledStartTime()))
00644 {
00645 startEarly = programInfo.GetRecordingStartTime()
00646 .secsTo(programInfo.GetScheduledStartTime());
00647 }
00648 if (programDuration > 0)
00649 {
00650 captime = startEarly + (programDuration / 3);
00651 }
00652 if (captime < 0)
00653 captime = 600;
00654 captime += preroll;
00655 LOG(VB_GENERAL, LOG_INFO,
00656 QString("Preview at calculated offset (%1 seconds)").arg(captime));
00657 }
00658
00659 width = height = sz = 0;
00660 unsigned char *data = (unsigned char*)
00661 GetScreenGrab(programInfo, pathname,
00662 captime, timeInSeconds,
00663 sz, width, height, aspect);
00664
00665 QString outname = CreateAccessibleFilename(pathname, outFileName);
00666
00667 int dw = (outSize.width() < 0) ? width : outSize.width();
00668 int dh = (outSize.height() < 0) ? height : outSize.height();
00669
00670 bool ok = SavePreview(outname, data, width, height, aspect, dw, dh);
00671
00672 if (ok)
00673 {
00674
00675
00676 struct utimbuf times;
00677 times.actime = times.modtime = dt.toTime_t();
00678 utime(outname.toLocal8Bit().constData(), ×);
00679 }
00680
00681 delete[] data;
00682
00683 programInfo.MarkAsInUse(false, kPreviewGeneratorInUseID);
00684
00685 return ok;
00686 }
00687
00688 QString PreviewGenerator::CreateAccessibleFilename(
00689 const QString &pathname, const QString &outFileName)
00690 {
00691 QString outname = pathname + ".png";
00692
00693 if (outFileName.isEmpty())
00694 return outname;
00695
00696 outname = outFileName;
00697 QFileInfo fi(outname);
00698 if (outname == fi.fileName())
00699 {
00700 QString dir = QString::null;
00701 if (pathname.contains(':'))
00702 {
00703 QUrl uinfo(pathname);
00704 uinfo.setPath("");
00705 dir = uinfo.toString();
00706 }
00707 else
00708 {
00709 dir = QFileInfo(pathname).path();
00710 }
00711 outname = dir + "/" + fi.fileName();
00712 LOG(VB_FILE, LOG_INFO, LOC + QString("outfile '%1' -> '%2'")
00713 .arg(outFileName).arg(outname));
00714 }
00715
00716 return outname;
00717 }
00718
00719 bool PreviewGenerator::IsLocal(void) const
00720 {
00721 QString tmppathname = pathname;
00722
00723 if (tmppathname.left(4) == "dvd:")
00724 tmppathname = tmppathname.section(":", 1, 1);
00725
00726 if (!QFileInfo(tmppathname).isReadable())
00727 return false;
00728
00729 tmppathname = outFileName.isEmpty() ? tmppathname : outFileName;
00730 QString pathdir = QFileInfo(tmppathname).path();
00731
00732 if (!QFileInfo(pathdir).isWritable())
00733 {
00734 LOG(VB_GENERAL, LOG_WARNING, LOC +
00735 QString("Output path '%1' is not writeable") .arg(pathdir));
00736 return false;
00737 }
00738
00739 return true;
00740 }
00741
00757 char *PreviewGenerator::GetScreenGrab(
00758 const ProgramInfo &pginfo, const QString &filename,
00759 long long seektime, bool time_in_secs,
00760 int &bufferlen,
00761 int &video_width, int &video_height, float &video_aspect)
00762 {
00763 (void) pginfo;
00764 (void) filename;
00765 (void) seektime;
00766 (void) time_in_secs;
00767 (void) bufferlen;
00768 (void) video_width;
00769 (void) video_height;
00770 char *retbuf = NULL;
00771 bufferlen = 0;
00772
00773 if (!MSqlQuery::testDBConnection())
00774 {
00775 LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer could not connect to DB.");
00776 return NULL;
00777 }
00778
00779
00780 if (filename.left(1)=="/")
00781 {
00782 QFileInfo info(filename);
00783 bool invalid = (!info.exists() || !info.isReadable() ||
00784 (info.isFile() && (info.size() < 8*1024)));
00785 if (invalid)
00786 {
00787 LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer file " +
00788 QString("'%1'").arg(filename) + " is not valid.");
00789 return NULL;
00790 }
00791 }
00792
00793 RingBuffer *rbuf = RingBuffer::Create(filename, false, false, 0);
00794 if (!rbuf->IsOpen())
00795 {
00796 LOG(VB_GENERAL, LOG_ERR, LOC + "Previewer could not open file: " +
00797 QString("'%1'").arg(filename));
00798 delete rbuf;
00799 return NULL;
00800 }
00801
00802 PlayerContext *ctx = new PlayerContext(kPreviewGeneratorInUseID);
00803 ctx->SetRingBuffer(rbuf);
00804 ctx->SetPlayingInfo(&pginfo);
00805 ctx->SetPlayer(new MythPlayer((PlayerFlags)(kAudioMuted | kVideoIsNull)));
00806 ctx->player->SetPlayerInfo(NULL, NULL, ctx);
00807
00808 if (time_in_secs)
00809 retbuf = ctx->player->GetScreenGrab(seektime, bufferlen,
00810 video_width, video_height, video_aspect);
00811 else
00812 retbuf = ctx->player->GetScreenGrabAtFrame(
00813 seektime, true, bufferlen,
00814 video_width, video_height, video_aspect);
00815
00816 delete ctx;
00817
00818 if (retbuf)
00819 {
00820 LOG(VB_GENERAL, LOG_INFO, LOC +
00821 QString("Grabbed preview '%0' %1x%2@%3%4")
00822 .arg(filename).arg(video_width).arg(video_height)
00823 .arg(seektime).arg((time_in_secs) ? "s" : "f"));
00824 }
00825
00826 return retbuf;
00827 }
00828
00829