diff --git a/ImageManager/ExtractOneVideoFrame.cpp b/ImageManager/ExtractOneVideoFrame.cpp index d6adb5db..7e7dab50 100644 --- a/ImageManager/ExtractOneVideoFrame.cpp +++ b/ImageManager/ExtractOneVideoFrame.cpp @@ -1,176 +1,177 @@ /* Copyright 2012 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ExtractOneVideoFrame.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG_IMAGEMANAGER # define Debug qDebug #else # define Debug if(0) qDebug #endif namespace ImageManager { QString ExtractOneVideoFrame::s_tokenForShortVideos; #define STR(x) QString::fromUtf8(x) void ExtractOneVideoFrame::extract(const DB::FileName &fileName, double offset, QObject* receiver, const char* slot) { if ( MainWindow::FeatureDialog::hasVideoThumbnailer()) new ExtractOneVideoFrame(fileName, offset, receiver, slot); } ExtractOneVideoFrame::ExtractOneVideoFrame(const DB::FileName &fileName, double offset, QObject *receiver, const char *slot) { m_fileName = fileName; m_process = new Utilities::Process(this); setupWorkingDirectory(); m_process->setWorkingDirectory(m_workingDirectory); connect( m_process, SIGNAL(finished(int)), this, SLOT(frameFetched())); connect( m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(handleError(QProcess::ProcessError))); connect( this, SIGNAL(result(QImage)), receiver, slot); if (MainWindow::FeatureDialog::ffmpegBinary().isEmpty()) { QStringList arguments; arguments << STR("-nosound") << STR("-ss") << QString::number(offset,'f',4) << STR("-vf") << STR("screenshot") << STR("-frames") << STR("20") << STR("-vo") << STR("png:z=9") << fileName.absolute(); Debug( "%s %s", qPrintable(MainWindow::FeatureDialog::mplayerBinary()), qPrintable(arguments.join(QString::fromLatin1(" ")))); m_process->start(MainWindow::FeatureDialog::mplayerBinary(), arguments); } else { QStringList arguments; - arguments << STR("-ss") << QString::number(offset, 'f', 4) << STR("-i") << fileName.absolute() - << STR("-sn") << STR("-an") - << STR("-vframes") << STR("20") - << m_workingDirectory + STR("/000000%02d.png"); + // analyzeduration is for videos where the videostream starts later than the sound + arguments << STR("-ss") << QString::number(offset, 'f', 4) << STR("-analyzeduration") + << STR("200M") << STR("-i") << fileName.absolute() << STR("-vf") << STR("thumbnail") + << STR("-vframes") << STR("20") << m_workingDirectory + STR("/000000%02d.png"); + Debug( "%s %s", qPrintable(MainWindow::FeatureDialog::ffmpegBinary()), qPrintable(arguments.join(QString::fromLatin1(" ")))); m_process->start(MainWindow::FeatureDialog::ffmpegBinary(), arguments); } } void ExtractOneVideoFrame::frameFetched() { if (!QFile::exists(m_workingDirectory + STR("/00000020.png"))) markShortVideo(m_fileName); QString name; for (int i = 20; i>0; --i) { name = m_workingDirectory +STR("/000000%1.png").arg(i,2, 10, QChar::fromLatin1('0')); if (QFile::exists(name)) { Debug() << "Using video frame " << i; break; } } QImage image(name); emit result(image); deleteWorkingDirectory(); deleteLater(); } void ExtractOneVideoFrame::handleError(QProcess::ProcessError error) { QString message; switch (error) { case QProcess::FailedToStart: message = i18n("Failed to start"); break; case QProcess::Crashed: message = i18n("Crashed"); break; case QProcess::Timedout: message = i18n("Timedout"); break; case QProcess::ReadError: message = i18n("Read error"); break; case QProcess::WriteError: message = i18n("Write error"); break; case QProcess::UnknownError: message = i18n("Unknown error"); break; } KMessageBox::information( MainWindow::Window::theMainWindow(), i18n("

Error when extracting video thumbnails.
Error was: %1

" , message ), QString(), QLatin1String("errorWhenRunningQProcessFromExtractOneVideoFrame")); emit result(QImage()); deleteLater(); } void ExtractOneVideoFrame::setupWorkingDirectory() { const QString tmpPath = STR("%1/KPA-XXXXXX").arg(QDir::tempPath()); m_workingDirectory = QString::fromUtf8(mkdtemp(tmpPath.toUtf8().data())); } void ExtractOneVideoFrame::deleteWorkingDirectory() { QDir dir(m_workingDirectory); QStringList files = dir.entryList(QDir::Files); for ( const QString& file : files ) dir.remove(file); dir.rmdir(m_workingDirectory); } void ExtractOneVideoFrame::markShortVideo(const DB::FileName &fileName) { if (s_tokenForShortVideos.isNull()) { Utilities::StringSet usedTokens = MainWindow::TokenEditor::tokensInUse().toSet(); for ( int ch = 'A'; ch <= 'Z'; ++ch ) { QString token = QChar::fromLatin1( (char) ch ); if (!usedTokens.contains(token)) { s_tokenForShortVideos = token; break; } } if (s_tokenForShortVideos.isNull()) { // Hmmm, no free token. OK lets just skip setting tokens. return; } KMessageBox::information(MainWindow::Window::theMainWindow(), i18n("Unable to extract video thumbnails from some files. " "Either the file is damaged in some way, or the video is ultra short. " "For your convenience, the token '%1' " "has been set on those videos.\n\n" "(You might need to wait till the video extraction led in your status bar has stopped blinking, " "to see all affected videos.)", s_tokenForShortVideos)); } DB::ImageInfoPtr info = DB::ImageDB::instance()->info(fileName); DB::CategoryPtr tokensCategory = DB::ImageDB::instance()->categoryCollection()->categoryForSpecial(DB::Category::TokensCategory); info->addCategoryInfo(tokensCategory->name(), s_tokenForShortVideos); MainWindow::DirtyIndicator::markDirty(); } } // namespace ImageManager // vi:expandtab:tabstop=4 shiftwidth=4: diff --git a/ImageManager/VideoLengthExtractor.cpp b/ImageManager/VideoLengthExtractor.cpp index 31f475e8..01cd7908 100644 --- a/ImageManager/VideoLengthExtractor.cpp +++ b/ImageManager/VideoLengthExtractor.cpp @@ -1,138 +1,136 @@ /* Copyright 2012 Jesper K. Pedersen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "VideoLengthExtractor.h" #include #include #include #include #define STR(x) QString::fromUtf8(x) #ifdef DEBUG_IMAGEMANAGER # define Debug qDebug #else # define Debug if(0) qDebug #endif ImageManager::VideoLengthExtractor::VideoLengthExtractor(QObject *parent) : QObject(parent), m_process(nullptr) { } void ImageManager::VideoLengthExtractor::extract(const DB::FileName &fileName) { m_fileName = fileName; if ( m_process ) { disconnect( m_process, SIGNAL(finished(int)), this, SLOT(processEnded())); m_process->kill(); delete m_process; m_process = nullptr; } if (!MainWindow::FeatureDialog::hasVideoThumbnailer()) { emit unableToDetermineLength(); return; } m_process = new Utilities::Process(this); m_process->setWorkingDirectory(QDir::tempPath()); connect( m_process, SIGNAL(finished(int)), this, SLOT(processEnded())); if (MainWindow::FeatureDialog::ffmpegBinary().isEmpty()) { QStringList arguments; arguments << STR("-identify") << STR("-frames") << STR("0") << STR("-vc") << STR("null") << STR("-vo") << STR("null") << STR("-ao") << STR("null") << fileName.absolute(); m_process->start(MainWindow::FeatureDialog::mplayerBinary(), arguments); } else { QStringList arguments; - arguments << STR("-v") << STR("error") << STR("-select_streams") << STR("v:0") - << STR("-show_entries") << STR("stream=duration") + // Just look at the length of the container. Some videos have streams without duration entry + arguments << STR("-v") << STR("0") << STR("-show_entries") << STR("format=duration") << STR("-of") << STR("default=noprint_wrappers=1:nokey=1") << fileName.absolute(); Debug( "%s %s", qPrintable(MainWindow::FeatureDialog::ffprobeBinary()), qPrintable(arguments.join(QString::fromLatin1(" ")))); m_process->start(MainWindow::FeatureDialog::ffprobeBinary(), arguments); } } void ImageManager::VideoLengthExtractor::processEnded() { if ( !m_process->stderr().isEmpty() ) Debug() << m_process->stderr(); QString lenStr; if (MainWindow::FeatureDialog::ffmpegBinary().isEmpty()) { QStringList list = m_process->stdout().split(QChar::fromLatin1('\n')); list = list.filter(STR("ID_LENGTH=")); if ( list.count() == 0 ) { qWarning() << "Unable to find ID_LENGTH in output from MPlayer for file " << m_fileName.absolute() << "\n" << "Output was:\n" << m_process->stdout(); emit unableToDetermineLength(); return; } const QString match = list[0]; const QRegExp regexp(STR("ID_LENGTH=([0-9.]+)")); if (!regexp.exactMatch(match)) { qWarning() << STR("Unable to match regexp for string: %1 (for file %2)").arg(match).arg(m_fileName.absolute()); emit unableToDetermineLength(); return; } lenStr = regexp.cap(1); } else { QStringList list = m_process->stdout().split(QChar::fromLatin1('\n')); - // one line-break -> 2 parts - // some videos with subtitles or other additional streams might have more than one line - // in these cases, we just take the first one as both lengths should be the same anyways - if ( list.count() < 2 ) { + // ffprobe -v 0 just prints one line, except if panicking + if ( list.count() < 1 ) { qWarning() << "Unable to parse video length from ffprobe output!" << "Output was:\n" << m_process->stdout(); emit unableToDetermineLength(); return; } lenStr = list[0].trimmed(); } bool ok = false; const double length = lenStr.toDouble(&ok); if ( !ok ) { qWarning() << STR("Unable to convert string \"%1\"to double (for file %2)").arg(lenStr).arg(m_fileName.absolute()); emit unableToDetermineLength(); return; } if ( length == 0 ) { qWarning() << "video length returned was 0 for file " << m_fileName.absolute(); emit unableToDetermineLength(); return; } emit lengthFound(length); m_process->deleteLater(); m_process = nullptr; } // vi:expandtab:tabstop=4 shiftwidth=4: