Dive video: paint duration-bar above thumbnail in profile plot

Paint a rectangle on top of thumbnails indicating the run-time
of the video.

Use the z=100.0-101.0 range for painting the thumbnails, whereby
the z-value increases uniformly from first to last thumbnail
(sorted by timestamp). The duration-bars are placed at z-values
midway between those of the thumbnails.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2018-07-15 17:56:18 +02:00 committed by Dirk Hohndel
parent c742885984
commit b3feaa80e2
9 changed files with 163 additions and 76 deletions

View file

@ -93,40 +93,36 @@ Thumbnailer::Thumbnail Thumbnailer::fetchImage(const QString &filename, const QS
mediatype_t type = get_metadata(qPrintable(filename), &md);
// For io error or video, return early with the appropriate dummy-icon.
if (type == MEDIATYPE_IO_ERROR) {
return { failImage, MEDIATYPE_IO_ERROR };
} else if (type == MEDIATYPE_VIDEO) {
addVideoThumbnailToCache(originalFilename, md.duration);
return { videoImage, MEDIATYPE_VIDEO };
}
if (type == MEDIATYPE_IO_ERROR)
return { failImage, MEDIATYPE_IO_ERROR, 0 };
else if (type == MEDIATYPE_VIDEO)
return addVideoThumbnailToCache(originalFilename, md.duration);
// Try if Qt can parse this image. If it does, use this as a thumbnail.
QImage thumb(filename);
if (!thumb.isNull()) {
addPictureThumbnailToCache(originalFilename, thumb);
return { thumb, MEDIATYPE_PICTURE };
int size = maxThumbnailSize();
thumb = thumb.scaled(size, size, Qt::KeepAspectRatio);
return addPictureThumbnailToCache(originalFilename, thumb);
}
// Neither our code, nor Qt could determine the type of this object from looking at the data.
// Try to check for a video-file extension. Since we couldn't parse the video file,
// we pass 0 as the duration.
if (hasVideoFileExtension(filename)) {
addVideoThumbnailToCache(originalFilename, {0} );
return { videoImage, MEDIATYPE_VIDEO };
}
if (hasVideoFileExtension(filename))
return addVideoThumbnailToCache(originalFilename, {0} );
// Give up: we simply couldn't determine what this thing is.
// But since we managed to read this file, mark this file in the cache as unknown.
addUnknownThumbnailToCache(originalFilename);
return { unknownImage, MEDIATYPE_UNKNOWN };
return addUnknownThumbnailToCache(originalFilename);
} else if (tryDownload) {
// This has to be done in UI main thread, because QNetworkManager refuses
// to treat requests from other threads. invokeMethod() is Qt's way of calling a
// function in a different thread, namely the thread the called object is associated to.
QMetaObject::invokeMethod(ImageDownloader::instance(), "load", Qt::AutoConnection, Q_ARG(QUrl, url), Q_ARG(QString, originalFilename));
return { QImage(), MEDIATYPE_STILL_LOADING };
return { QImage(), MEDIATYPE_STILL_LOADING, 0 };
}
return { QImage(), MEDIATYPE_IO_ERROR };
return { QImage(), MEDIATYPE_IO_ERROR, 0 };
}
// Fetch a picture based on its original filename. If there is a translated filename (obtained either
@ -142,7 +138,7 @@ Thumbnailer::Thumbnail Thumbnailer::getHashedImage(const QString &filename, bool
// If there is a translated filename, try that first.
// Note that we set the default type to io-error, so that if we didn't try
// the local filename first, we will load the file from the canonical filename.
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR };
Thumbnail thumbnail { QImage(), MEDIATYPE_IO_ERROR, 0 };
if (localFilename != filename)
thumbnail = fetchImage(localFilename, filename, tryDownload);
@ -199,7 +195,9 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
// If reading did not succeed, schedule for recalculation - this thumbnail might
// have been written by an older version, which couldn't extract the duration.
if (stream.status() != QDataStream::Ok)
// Likewise test the duration and number of pictures for sanity (no videos longer than 10 h,
// no more than 10000 pictures).
if (stream.status() != QDataStream::Ok || duration > 36000 || numPics > 10000)
return { QImage(), MEDIATYPE_VIDEO, 0 };
// Currently, we support only one picture
@ -211,7 +209,7 @@ Thumbnailer::Thumbnail Thumbnailer::getVideoThumbnailFromStream(QDataStream &str
}
// No picture -> show dummy-icon
return { res.isNull() ? videoImage : res, MEDIATYPE_VIDEO, {(int32_t)duration} };
return { res.isNull() ? videoImage : res, MEDIATYPE_VIDEO, (int32_t)duration };
}
// Fetch a thumbnail from cache.
@ -220,7 +218,7 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
{
QString filename = thumbnailFileName(picture_filename);
if (filename.isEmpty())
return { QImage(), MEDIATYPE_UNKNOWN };
return { QImage(), MEDIATYPE_UNKNOWN, 0 };
QFile file(filename);
if (prefs.auto_recalculate_thumbnails) {
@ -256,7 +254,7 @@ Thumbnailer::Thumbnail Thumbnailer::getThumbnailFromCache(const QString &picture
}
}
void Thumbnailer::addVideoThumbnailToCache(const QString &picture_filename, duration_t duration)
Thumbnailer::Thumbnail Thumbnailer::addVideoThumbnailToCache(const QString &picture_filename, duration_t duration)
{
// The format of video thumbnails:
// uint32 MEDIATYPE_VIDEO
@ -267,43 +265,43 @@ void Thumbnailer::addVideoThumbnailToCache(const QString &picture_filename, dura
// QImage frame
QString filename = thumbnailFileName(picture_filename);
QSaveFile file(filename);
if (!file.open(QIODevice::WriteOnly))
return;
QDataStream stream(&file);
if (file.open(QIODevice::WriteOnly)) {
QDataStream stream(&file);
stream << (quint32)MEDIATYPE_VIDEO;
stream << (quint32)duration.seconds;
stream << (quint32)0; // Currently, we don't support extraction of images
file.commit();
stream << (quint32)MEDIATYPE_VIDEO;
stream << (quint32)duration.seconds;
stream << (quint32)0; // Currently, we don't support extraction of images
file.commit();
}
return { videoImage, MEDIATYPE_VIDEO, duration };
}
void Thumbnailer::addPictureThumbnailToCache(const QString &picture_filename, const QImage &thumbnail)
Thumbnailer::Thumbnail Thumbnailer::addPictureThumbnailToCache(const QString &picture_filename, const QImage &thumbnail)
{
if (thumbnail.isNull())
return;
// The format of a picture-thumbnail is very simple:
// uint32 MEDIATYPE_PICTURE
// QImage thumbnail
QString filename = thumbnailFileName(picture_filename);
QSaveFile file(filename);
if (!file.open(QIODevice::WriteOnly))
return;
QDataStream stream(&file);
if (file.open(QIODevice::WriteOnly)) {
QDataStream stream(&file);
stream << (quint32)MEDIATYPE_PICTURE;
stream << thumbnail;
file.commit();
stream << (quint32)MEDIATYPE_PICTURE;
stream << thumbnail;
file.commit();
}
return { thumbnail, MEDIATYPE_PICTURE, 0 };
}
void Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
Thumbnailer::Thumbnail Thumbnailer::addUnknownThumbnailToCache(const QString &picture_filename)
{
QString filename = thumbnailFileName(picture_filename);
QSaveFile file(filename);
if (!file.open(QIODevice::WriteOnly))
return;
QDataStream stream(&file);
stream << (quint32)MEDIATYPE_UNKNOWN;
if (file.open(QIODevice::WriteOnly)) {
QDataStream stream(&file);
stream << (quint32)MEDIATYPE_UNKNOWN;
}
return { unknownImage, MEDIATYPE_UNKNOWN, 0 };
}
void Thumbnailer::recalculate(QString filename)
@ -317,7 +315,7 @@ void Thumbnailer::recalculate(QString filename)
return;
QMutexLocker l(&lock);
emit thumbnailChanged(filename, thumbnail.img);
emit thumbnailChanged(filename, thumbnail.img, thumbnail.duration);
workingOn.remove(filename);
}
@ -339,7 +337,7 @@ void Thumbnailer::processItem(QString filename, bool tryDownload)
}
QMutexLocker l(&lock);
emit thumbnailChanged(filename, thumbnail.img);
emit thumbnailChanged(filename, thumbnail.img, thumbnail.duration);
workingOn.remove(filename);
}
@ -352,7 +350,7 @@ void Thumbnailer::imageDownloaded(QString filename)
void Thumbnailer::imageDownloadFailed(QString filename)
{
emit thumbnailChanged(filename, failImage);
emit thumbnailChanged(filename, failImage, duration_t{ 0 });
QMutexLocker l(&lock);
workingOn.remove(filename);
}