[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [okular] /: Implement support for poster image of videos in PDF documents
From: Tobias Koenig <tokoe () kde ! org>
Date: 2012-08-19 10:24:05
Message-ID: 20120819102405.A2C1FA6110 () git ! kde ! org
[Download RAW message or body]
Git commit 8dbd83ab2a57d691ad2dfdbfa3bd0d5204573950 by Tobias Koenig.
Committed on 29/06/2012 at 11:16.
Pushed by tokoe into branch 'master'.
Implement support for poster image of videos in PDF documents
With this commit Okular will show a so called poster image for PDF documents
containing movie annotations. The image will be a screenshot of the first frame
of the video.
BUGS: 301603
REVIEW: 105890
FIXED-IN: 4.10.0
M +1 -0 CMakeLists.txt
M +16 -3 cmake/modules/FindPoppler.cmake
M +24 -1 core/movie.cpp
M +30 -0 core/movie.h
M +3 -0 generators/poppler/config-okular-poppler.h.cmake
M +4 -0 generators/poppler/generator_pdf.cpp
M +2 -2 ui/pageview.cpp
M +2 -2 ui/presentationwidget.cpp
A +31 -0 ui/snapshottaker.cpp [License: UNKNOWN] *
A +27 -0 ui/snapshottaker.h [License: UNKNOWN] *
M +121 -7 ui/videowidget.cpp
M +6 -0 ui/videowidget.h
The files marked with a * at the end have a non valid license. Please read: \
http://techbase.kde.org/Policies/Licensing_Policy and use the headers which are \
listed at that page.
http://commits.kde.org/okular/8dbd83ab2a57d691ad2dfdbfa3bd0d5204573950
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e4cb88f..31d0da5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -169,6 +169,7 @@ set(okularpart_SRCS
ui/searchwidget.cpp
ui/sidebar.cpp
ui/side_reviews.cpp
+ ui/snapshottaker.cpp
ui/thumbnaillist.cpp
ui/toc.cpp
ui/tocmodel.cpp
diff --git a/cmake/modules/FindPoppler.cmake b/cmake/modules/FindPoppler.cmake
index ffa6950..06e7117 100644
--- a/cmake/modules/FindPoppler.cmake
+++ b/cmake/modules/FindPoppler.cmake
@@ -97,17 +97,30 @@ int main()
}
" HAVE_POPPLER_0_20)
+check_cxx_source_compiles("
+#include <poppler-qt4.h>
+
+int main()
+{
+ Poppler::MovieObject *movie = 0;
+ movie->showPosterImage();
+ return 0;
+}
+" HAVE_POPPLER_0_22)
+
set(CMAKE_REQUIRED_INCLUDES)
set(CMAKE_REQUIRED_LIBRARIES)
- if (HAVE_POPPLER_0_20)
+ if (HAVE_POPPLER_0_22)
+ set(popplerVersionMessage "0.22")
+ elseif (HAVE_POPPLER_0_20)
set(popplerVersionMessage "0.20")
elseif (HAVE_POPPLER_0_16)
set(popplerVersionMessage "0.16")
elseif (HAVE_POPPLER_0_12_1)
set(popplerVersionMessage "0.12.1")
- else (HAVE_POPPLER_0_20)
+ else (HAVE_POPPLER_0_22)
set(popplerVersionMessage "0.5.4")
- endif (HAVE_POPPLER_0_20)
+ endif (HAVE_POPPLER_0_22)
if (NOT Poppler_FIND_QUIETLY)
message(STATUS "Found Poppler-Qt4: ${POPPLER_LIBRARY}, (>= \
${popplerVersionMessage})") endif (NOT Poppler_FIND_QUIETLY)
diff --git a/core/movie.cpp b/core/movie.cpp
index 94e71a9..dd780eb 100644
--- a/core/movie.cpp
+++ b/core/movie.cpp
@@ -12,6 +12,7 @@
// qt/kde includes
#include <qdir.h>
+#include <qimage.h>
#include <qstring.h>
#include <qtemporaryfile.h>
@@ -30,7 +31,8 @@ class Movie::Private
m_playMode( PlayOnce ),
m_tmp( 0 ),
m_showControls( false ),
- m_autoPlay( false )
+ m_autoPlay( false ),
+ m_showPosterImage( false )
{
}
@@ -39,8 +41,10 @@ class Movie::Private
Rotation m_rotation;
PlayMode m_playMode;
QTemporaryFile *m_tmp;
+ QImage m_posterImage;
bool m_showControls : 1;
bool m_autoPlay : 1;
+ bool m_showPosterImage : 1;
};
Movie::Movie( const QString& fileName )
@@ -130,3 +134,22 @@ bool Movie::autoPlay() const
return d->m_autoPlay;
}
+void Movie::setShowPosterImage( bool show )
+{
+ d->m_showPosterImage = show;
+}
+
+bool Movie::showPosterImage() const
+{
+ return d->m_showPosterImage;
+}
+
+void Movie::setPosterImage( const QImage &image )
+{
+ d->m_posterImage = image;
+}
+
+QImage Movie::posterImage() const
+{
+ return d->m_posterImage;
+}
diff --git a/core/movie.h b/core/movie.h
index cbe382e..8d0e2fe 100644
--- a/core/movie.h
+++ b/core/movie.h
@@ -16,6 +16,8 @@
#include <QtCore/QSize>
+class QImage;
+
namespace Okular {
/**
@@ -107,6 +109,34 @@ class OKULAR_EXPORT Movie
*/
bool autoPlay() const;
+ /**
+ * Sets whether to show a poster image.
+ *
+ * @since 4.10
+ */
+ void setShowPosterImage( bool show );
+
+ /**
+ * Whether to show a poster image.
+ *
+ * @since 4.10
+ */
+ bool showPosterImage() const;
+
+ /**
+ * Sets the poster image.
+ *
+ * @since 4.10
+ */
+ void setPosterImage( const QImage &image );
+
+ /**
+ * Returns the poster image.
+ *
+ * @since 4.10
+ */
+ QImage posterImage() const;
+
private:
class Private;
Private* const d;
diff --git a/generators/poppler/config-okular-poppler.h.cmake \
b/generators/poppler/config-okular-poppler.h.cmake index df89d68..2ce6a4a 100644
--- a/generators/poppler/config-okular-poppler.h.cmake
+++ b/generators/poppler/config-okular-poppler.h.cmake
@@ -6,3 +6,6 @@
/* Defined if we have the 0.20 version of the Poppler library */
#cmakedefine HAVE_POPPLER_0_20 1
+
+/* Defined if we have the 0.22 version of the Poppler library */
+#cmakedefine HAVE_POPPLER_0_22 1
diff --git a/generators/poppler/generator_pdf.cpp \
b/generators/poppler/generator_pdf.cpp index 71c962c..4ae902b 100644
--- a/generators/poppler/generator_pdf.cpp
+++ b/generators/poppler/generator_pdf.cpp
@@ -180,6 +180,10 @@ Okular::Movie* createMovieFromPopplerMovie( const \
Poppler::MovieObject *popplerM movie->setShowControls( popplerMovie->showControls() \
);
movie->setPlayMode( (Okular::Movie::PlayMode)popplerMovie->playMode() );
movie->setAutoPlay( false ); // will be triggered by external MovieAnnotation
+#ifdef HAVE_POPPLER_0_22
+ movie->setShowPosterImage( popplerMovie->showPosterImage() );
+ movie->setPosterImage( popplerMovie->posterImage() );
+#endif
return movie;
}
diff --git a/ui/pageview.cpp b/ui/pageview.cpp
index 5e04412..25dc63b 100644
--- a/ui/pageview.cpp
+++ b/ui/pageview.cpp
@@ -860,7 +860,7 @@ void PageView::notifySetup( const QVector< Okular::Page * > & \
pageSet, int setup
Okular::MovieAnnotation * movieAnn = static_cast< \
Okular::MovieAnnotation * >( a );
VideoWidget * vw = new VideoWidget( movieAnn, d->document, \
viewport() ); item->videoWidgets().insert( movieAnn->movie(), vw );
- vw->hide();
+ vw->pageEntered();
}
}
}
@@ -3961,7 +3961,7 @@ void PageView::slotRequestVisiblePixmaps( int newValue )
if ( vw->isPlaying() && viewportRectAtZeroZero.intersect( vw->geometry() \
).isEmpty() ) { vw->stop();
- vw->hide();
+ vw->pageLeft();
}
}
diff --git a/ui/presentationwidget.cpp b/ui/presentationwidget.cpp
index f4da539..37523e8 100644
--- a/ui/presentationwidget.cpp
+++ b/ui/presentationwidget.cpp
@@ -316,7 +316,7 @@ void PresentationWidget::notifySetup( const QVector< Okular::Page \
* > & pageSet,
Okular::MovieAnnotation * movieAnn = static_cast< \
Okular::MovieAnnotation * >( a );
VideoWidget * vw = new VideoWidget( movieAnn, m_document, this );
frame->videoWidgets.insert( movieAnn->movie(), vw );
- vw->hide();
+ vw->pageEntered();
}
}
frame->recalcGeometry( m_width, m_height, screenRatio );
@@ -809,7 +809,7 @@ void PresentationWidget::changePage( int newPage )
Q_FOREACH ( VideoWidget *vw, m_frames[ m_frameIndex ]->videoWidgets )
{
vw->stop();
- vw->hide();
+ vw->pageLeft();
}
// stop audio playback, if any
diff --git a/ui/snapshottaker.cpp b/ui/snapshottaker.cpp
new file mode 100644
index 0000000..d48aafe
--- /dev/null
+++ b/ui/snapshottaker.cpp
@@ -0,0 +1,31 @@
+#include "snapshottaker.h"
+
+#include <phonon/mediaobject.h>
+#include <phonon/videowidget.h>
+
+#include <QtGui/QImage>
+
+SnapshotTaker::SnapshotTaker( const QString &url, QObject *parent )
+ : QObject( parent )
+ , m_player( new Phonon::VideoPlayer( Phonon::NoCategory, 0 ) )
+{
+ m_player->load( url );
+ m_player->hide();
+
+ connect(m_player->mediaObject(), SIGNAL(stateChanged(Phonon::State, \
Phonon::State)), + this, SLOT(stateChanged(Phonon::State, \
Phonon::State))); +
+ m_player->play();
+}
+
+void SnapshotTaker::stateChanged(Phonon::State newState, Phonon::State)
+{
+ if (newState == Phonon::PlayingState) {
+ const QImage image = m_player->videoWidget()->snapshot();
+ if (!image.isNull())
+ emit finished( image );
+
+ m_player->stop();
+ deleteLater();
+ }
+}
diff --git a/ui/snapshottaker.h b/ui/snapshottaker.h
new file mode 100644
index 0000000..66138d0
--- /dev/null
+++ b/ui/snapshottaker.h
@@ -0,0 +1,27 @@
+#ifndef SNAPSHOTTAKER_H
+#define SNAPSHOTTAKER_H
+
+#include <phonon/videoplayer.h>
+
+#include <QtCore/QObject>
+
+class QImage;
+
+class SnapshotTaker : public QObject
+{
+ Q_OBJECT
+
+ public:
+ SnapshotTaker( const QString &url, QObject *parent = 0 );
+
+ Q_SIGNALS:
+ void finished( const QImage &image );
+
+ private Q_SLOTS:
+ void stateChanged(Phonon::State, Phonon::State);
+
+ private:
+ Phonon::VideoPlayer *m_player;
+};
+
+#endif
diff --git a/ui/videowidget.cpp b/ui/videowidget.cpp
index 7d80b43..3b7782d 100644
--- a/ui/videowidget.cpp
+++ b/ui/videowidget.cpp
@@ -11,10 +11,13 @@
// qt/kde includes
#include <qaction.h>
+#include <qcoreapplication.h>
#include <qdir.h>
#include <qevent.h>
+#include <qlabel.h>
#include <qlayout.h>
#include <qmenu.h>
+#include <qstackedlayout.h>
#include <qtoolbar.h>
#include <qtoolbutton.h>
#include <qwidgetaction.h>
@@ -29,6 +32,7 @@
#include "core/annotations.h"
#include "core/document.h"
#include "core/movie.h"
+#include "snapshottaker.h"
static QAction* createToolBarButtonWithWidgetPopup( QToolBar* toolBar, QWidget \
*widget, const QIcon &icon ) {
@@ -62,6 +66,9 @@ public:
void load();
void setupPlayPauseAction( PlayPauseMode mode );
+ void setPosterImage( const QImage& );
+ void takeSnapshot();
+ void videoStopped();
// slots
void finished();
@@ -78,6 +85,8 @@ public:
QAction *stopAction;
QAction *seekSliderAction;
QAction *seekSliderMenuAction;
+ QStackedLayout *pageLayout;
+ QLabel *posterImagePage;
bool loaded : 1;
};
@@ -121,6 +130,37 @@ void VideoWidget::Private::setupPlayPauseAction( PlayPauseMode \
mode ) }
}
+void VideoWidget::Private::takeSnapshot()
+{
+ const QString url = anno->movie()->url();
+ KUrl newurl;
+ if ( QDir::isRelativePath( url ) )
+ {
+ newurl = document->currentDocument();
+ newurl.setFileName( url );
+ }
+ else
+ {
+ newurl = url;
+ }
+
+ SnapshotTaker *taker = 0;
+ if ( newurl.isLocalFile() )
+ taker = new SnapshotTaker( newurl.toLocalFile(), q );
+ else
+ taker = new SnapshotTaker( newurl.url(), q );
+
+ q->connect( taker, SIGNAL( finished( const QImage& ) ), q, SLOT( setPosterImage( \
const QImage& ) ) ); +}
+
+void VideoWidget::Private::videoStopped()
+{
+ if ( anno->movie()->showPosterImage() )
+ pageLayout->setCurrentIndex( 1 );
+ else
+ q->hide();
+}
+
void VideoWidget::Private::finished()
{
switch ( anno->movie()->playMode() )
@@ -132,7 +172,7 @@ void VideoWidget::Private::finished()
setupPlayPauseAction( PlayMode );
if ( anno->movie()->playMode() == Okular::Movie::PlayOnce )
controlBar->setVisible( false );
- q->setVisible(false);
+ videoStopped();
break;
case Okular::Movie::PlayRepeat:
// repeat the playback
@@ -158,6 +198,18 @@ void VideoWidget::Private::playOrPause()
}
}
+void VideoWidget::Private::setPosterImage( const QImage &image )
+{
+ if ( !image.isNull() )
+ {
+ // cache the snapshot image
+ anno->movie()->setPosterImage( image );
+ }
+
+ posterImagePage->setPixmap( QPixmap::fromImage( image ) );
+ q->show();
+}
+
VideoWidget::VideoWidget( Okular::MovieAnnotation *movieann, Okular::Document \
*document, QWidget *parent ) : QWidget( parent ), d( new Private( movieann, \
document, this ) ) {
@@ -165,15 +217,18 @@ VideoWidget::VideoWidget( Okular::MovieAnnotation *movieann, \
Okular::Document *d // they should be tied to this widget, not spread around...
setAttribute( Qt::WA_NoMousePropagation );
- QVBoxLayout *mainlay = new QVBoxLayout( this );
+ // Setup player page
+ QWidget *playerPage = new QWidget;
+
+ QVBoxLayout *mainlay = new QVBoxLayout( playerPage );
mainlay->setMargin( 0 );
mainlay->setSpacing( 0 );
- d->player = new Phonon::VideoPlayer( Phonon::NoCategory, this );
- d->player->installEventFilter( this );
+ d->player = new Phonon::VideoPlayer( Phonon::NoCategory, playerPage );
+ d->player->installEventFilter( playerPage );
mainlay->addWidget( d->player );
- d->controlBar = new QToolBar( this );
+ d->controlBar = new QToolBar( playerPage );
d->controlBar->setIconSize( QSize( 16, 16 ) );
d->controlBar->setAutoFillBackground( true );
mainlay->addWidget( d->controlBar );
@@ -203,6 +258,38 @@ VideoWidget::VideoWidget( Okular::MovieAnnotation *movieann, \
Okular::Document *d
connect( d->playPauseAction, SIGNAL(triggered()), this, SLOT(playOrPause()) );
d->geom = movieann->transformedBoundingRectangle();
+
+ // Setup poster image page
+ d->posterImagePage = new QLabel;
+ d->posterImagePage->setScaledContents( true );
+ d->posterImagePage->installEventFilter( this );
+ d->posterImagePage->setCursor( Qt::PointingHandCursor );
+
+ d->pageLayout = new QStackedLayout( this );
+ d->pageLayout->setMargin( 0 );
+ d->pageLayout->setSpacing( 0 );
+ d->pageLayout->addWidget( playerPage );
+ d->pageLayout->addWidget( d->posterImagePage );
+
+
+ if ( movieann->movie()->showPosterImage() )
+ {
+ d->pageLayout->setCurrentIndex( 1 );
+
+ const QImage posterImage = movieann->movie()->posterImage();
+ if ( posterImage.isNull() )
+ {
+ d->takeSnapshot();
+ }
+ else
+ {
+ d->setPosterImage( posterImage );
+ }
+ }
+ else
+ {
+ d->pageLayout->setCurrentIndex( 0 );
+ }
}
VideoWidget::~VideoWidget()
@@ -227,14 +314,29 @@ bool VideoWidget::isPlaying() const
void VideoWidget::pageEntered()
{
- if ( d->anno->movie()->autoPlay() ) {
+ if ( d->anno->movie()->showPosterImage() )
+ {
+ d->pageLayout->setCurrentIndex( 1 );
+ show();
+ }
+
+ if ( d->anno->movie()->autoPlay() )
+ {
show();
QMetaObject::invokeMethod(this, "play", Qt::QueuedConnection);
}
}
+void VideoWidget::pageLeft()
+{
+ d->videoStopped();
+
+ hide();
+}
+
void VideoWidget::play()
{
+ d->pageLayout->setCurrentIndex( 0 );
d->load();
d->player->play();
d->stopAction->setEnabled( true );
@@ -256,7 +358,7 @@ void VideoWidget::pause()
bool VideoWidget::eventFilter( QObject * object, QEvent * event )
{
- if ( object == d->player )
+ if ( object == d->player || object == d->posterImagePage )
{
switch ( event->type() )
{
@@ -272,6 +374,18 @@ bool VideoWidget::eventFilter( QObject * object, QEvent * event \
) event->accept();
}
}
+ case QEvent::Wheel:
+ {
+ if ( object == d->posterImagePage )
+ {
+ QWheelEvent * we = static_cast< QWheelEvent * >( event );
+
+ // forward wheel events to parent widget
+ QWheelEvent *copy = new QWheelEvent( we->pos(), we->globalPos(), \
we->delta(), we->buttons(), we->modifiers(), we->orientation() ); + \
QCoreApplication::postEvent( parentWidget(), copy ); + }
+ break;
+ }
default: ;
}
}
diff --git a/ui/videowidget.h b/ui/videowidget.h
index e82753c..71dda22 100644
--- a/ui/videowidget.h
+++ b/ui/videowidget.h
@@ -35,6 +35,11 @@ class VideoWidget : public QWidget
*/
void pageEntered();
+ /**
+ * This method is called when the page the video widget is located on has \
been left. + */
+ void pageLeft();
+
public slots:
void play();
void pause();
@@ -48,6 +53,7 @@ class VideoWidget : public QWidget
private:
Q_PRIVATE_SLOT( d, void finished() )
Q_PRIVATE_SLOT( d, void playOrPause() )
+ Q_PRIVATE_SLOT( d, void setPosterImage( const QImage& ) )
// private storage
class Private;
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic