bool PreviewGenerator::SavePreview(QString filename, const unsigned char *data, uint width, uint height, float aspect, int desired_width, int desired_height) { if (!data || !width || !height) return false; if( height == 1088 ) { // Remove the extra 8 pixels at the bottom of 1080i recordings that // decode to 1088 rows as these are bogus pixels and make the previews // look a bit wrong. The worst offender seems to be H.264 captures // from HDPVR. Apparently, BBC HD also may exhibit the same behavior // in the UK, although with a different width. height = 1080; } const QImage img((unsigned char*) data, width, height, QImage::Format_RGB32); float ppw = max(desired_width, 0); float pph = max(desired_height, 0); bool desired_size_exactly_specified = true; if ((ppw < 1.0f) && (pph < 1.0f)) { ppw = gCoreContext->GetNumSetting("PreviewPixmapWidth", 320); pph = gCoreContext->GetNumSetting("PreviewPixmapHeight", 240); desired_size_exactly_specified = false; } aspect = (aspect <= 0.0f) ? ((float) width) / height : aspect; pph = (pph < 1.0f) ? (ppw / aspect) : pph; ppw = (ppw < 1.0f) ? (pph * aspect) : ppw; if (!desired_size_exactly_specified) { if (aspect > ppw / pph) pph = (ppw / aspect); else ppw = (pph * aspect); } ppw = max(1.0f, ppw); pph = max(1.0f, pph);; QImage small_img = img.scaled((int) ppw, (int) pph, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX"); f.setAutoRemove(false); if (f.open() && small_img.save(&f, "PNG")) { // Let anybody update it makeFileAccessible(f.fileName().toLocal8Bit().constData()); QFile of(filename); of.remove(); if (f.rename(filename)) { LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Saved preview '%0' %1x%2") .arg(filename).arg((int) ppw).arg((int) pph)); return true; } f.remove(); } return false; }
QFileInfo Content::GetPreviewImage( int nRecordedId, int nChanId, const QDateTime &recstarttsRaw, int nWidth, int nHeight, int nSecsIn, const QString &sFormat ) { if ((nRecordedId <= 0) && (nChanId <= 0 || !recstarttsRaw.isValid())) throw QString("Recorded ID or Channel ID and StartTime appears invalid."); if (!sFormat.isEmpty() && !QImageWriter::supportedImageFormats().contains(sFormat.toLower().toLocal8Bit())) { throw "GetPreviewImage: Specified 'Format' is not supported."; } // ---------------------------------------------------------------------- // Read Recording From Database // ---------------------------------------------------------------------- // TODO Should use RecordingInfo ProgramInfo pginfo; if (nRecordedId > 0) pginfo = ProgramInfo(nRecordedId); else pginfo = ProgramInfo(nChanId, recstarttsRaw.toUTC()); if (!pginfo.GetChanID()) { LOG(VB_GENERAL, LOG_ERR, QString("GetPreviewImage: No recording for '%1'") .arg(nRecordedId)); return QFileInfo(); } if (pginfo.GetHostname().toLower() != gCoreContext->GetHostName().toLower()) { QString sMsg = QString("GetPreviewImage: Wrong Host '%1' request from '%2'") .arg( gCoreContext->GetHostName()) .arg( pginfo.GetHostname() ); LOG(VB_UPNP, LOG_ERR, sMsg); throw HttpRedirectException( pginfo.GetHostname() ); } QString sImageFormat = sFormat; if (sImageFormat.isEmpty()) sImageFormat = "PNG"; QString sFileName = GetPlaybackURL(&pginfo); // ---------------------------------------------------------------------- // check to see if default preview image is already created. // ---------------------------------------------------------------------- QString sPreviewFileName; if (nSecsIn <= 0) { nSecsIn = -1; sPreviewFileName = QString("%1.png").arg(sFileName); } else { sPreviewFileName = QString("%1.%2.png").arg(sFileName).arg(nSecsIn); } if (!QFile::exists( sPreviewFileName )) { // ------------------------------------------------------------------ // Must generate Preview Image, Generate Image and save. // ------------------------------------------------------------------ if (!pginfo.IsLocal() && sFileName.startsWith("/")) pginfo.SetPathname(sFileName); if (!pginfo.IsLocal()) return QFileInfo(); PreviewGenerator *previewgen = new PreviewGenerator( &pginfo, QString(), PreviewGenerator::kLocal); previewgen->SetPreviewTimeAsSeconds( nSecsIn ); previewgen->SetOutputFilename ( sPreviewFileName ); bool ok = previewgen->Run(); previewgen->deleteLater(); if (!ok) return QFileInfo(); } bool bDefaultPixmap = (nWidth == 0) && (nHeight == 0); QString sNewFileName; if (bDefaultPixmap) sNewFileName = sPreviewFileName; else { sNewFileName = QString( "%1.%2.%3x%4.%5" ) .arg( sFileName ) .arg( nSecsIn ) .arg( nWidth == 0 ? -1 : nWidth ) .arg( nHeight == 0 ? -1 : nHeight ) .arg( sImageFormat.toLower() ); // ---------------------------------------------------------------------- // check to see if scaled preview image is already created and isn't // out of date // ---------------------------------------------------------------------- if (QFile::exists( sNewFileName )) { if (QFileInfo(sPreviewFileName).lastModified() <= QFileInfo(sNewFileName).lastModified()) return QFileInfo( sNewFileName ); } QImage image = QImage(sPreviewFileName); if (image.isNull()) return QFileInfo(); // We can just re-scale the default (full-size version) to avoid // a preview generator run if ( nWidth <= 0 ) image = image.scaledToHeight(nHeight, Qt::SmoothTransformation); else if ( nHeight <= 0 ) image = image.scaledToWidth(nWidth, Qt::SmoothTransformation); else image = image.scaled(nWidth, nHeight, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); image.save(sNewFileName, sImageFormat.toUpper().toLocal8Bit()); // Let anybody update it bool ret = makeFileAccessible(sNewFileName.toLocal8Bit().constData()); if (!ret) { LOG(VB_GENERAL, LOG_ERR, "Unable to change permissions on " "preview image. Backends and frontends " "running under different users will be " "unable to access it"); } } if (QFile::exists( sNewFileName )) return QFileInfo( sNewFileName ); PreviewGenerator *previewgen = new PreviewGenerator( &pginfo, QString(), PreviewGenerator::kLocal); previewgen->SetPreviewTimeAsSeconds( nSecsIn ); previewgen->SetOutputFilename ( sNewFileName ); previewgen->SetOutputSize (QSize(nWidth,nHeight)); bool ok = previewgen->Run(); previewgen->deleteLater(); if (!ok) return QFileInfo(); return QFileInfo( sNewFileName ); }
bool PreviewGenerator::SavePreview(const QString &filename, const unsigned char *data, uint width, uint height, float aspect, int desired_width, int desired_height, const QString &format) { if (!data || !width || !height) return false; const QImage img((unsigned char*) data, width, height, QImage::Format_RGB32); float ppw = max(desired_width, 0); float pph = max(desired_height, 0); bool desired_size_exactly_specified = true; if ((ppw < 1.0f) && (pph < 1.0f)) { ppw = img.width(); pph = img.height(); desired_size_exactly_specified = false; } aspect = (aspect <= 0.0f) ? ((float) width) / height : aspect; pph = (pph < 1.0f) ? (ppw / aspect) : pph; ppw = (ppw < 1.0f) ? (pph * aspect) : ppw; if (!desired_size_exactly_specified) { if (aspect > ppw / pph) pph = (ppw / aspect); else ppw = (pph * aspect); } ppw = max(1.0f, ppw); pph = max(1.0f, pph);; QImage small_img = img.scaled((int) ppw, (int) pph, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QTemporaryFile f(QFileInfo(filename).absoluteFilePath()+".XXXXXX"); f.setAutoRemove(false); if (f.open() && small_img.save(&f, format.toLocal8Bit().constData())) { // Let anybody update it bool ret = makeFileAccessible(f.fileName().toLocal8Bit().constData()); if (!ret) { LOG(VB_GENERAL, LOG_ERR, "Unable to change permissions on " "preview image. Backends and frontends " "running under different users will be " "unable to access it"); } QFile of(filename); of.remove(); if (f.rename(filename)) { LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Saved preview '%0' %1x%2") .arg(filename).arg((int) ppw).arg((int) pph)); return true; } f.remove(); } return false; }