[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