mirror of
https://invent.kde.org/graphics/okular
synced 2024-10-02 14:14:10 +00:00
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
This commit is contained in:
parent
aa042bd0f4
commit
8dbd83ab2a
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
30
core/movie.h
30
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
31
ui/snapshottaker.cpp
Normal file
31
ui/snapshottaker.cpp
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
27
ui/snapshottaker.h
Normal file
27
ui/snapshottaker.h
Normal file
|
@ -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
|
|
@ -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: ;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue