Index: CMakeLists.txt =================================================================== --- CMakeLists.txt (Revision 1123743) +++ CMakeLists.txt (Arbeitskopie) @@ -17,6 +17,10 @@ alsa_configure_file(${CMAKE_BINARY_DIR}/config-alsa.h) +macro_optional_find_package(FFmpeg) +macro_log_feature(FFMPEG_FOUND "FFmpeg" "FFmpeg libraries" "FFmpeg" FALSE "" "FFmpeg required for FFMpegVideoPreview backend") +macro_log_feature(SWSCALE_FOUND "swscale" "FFmpeg video scaling libraries" "swscale" FALSE "" "swscale required for FFMpegVideoPreview backend") + macro_optional_find_package(Xine) macro_log_feature(XINE_XCB_FOUND "Xine XCB" "Xine libraries" "http://www.xine-project.org/" FALSE "" "Xine and phonon-xine must be compiled with XCB support.") @@ -39,6 +43,12 @@ add_subdirectory(libkcompactdisc) add_subdirectory(mplayerthumbs) +if (FFMPEG_FOUND) + if (SWSCALE_FOUND) + macro_optional_add_subdirectory(ffmpegthumbs) + endif(SWSCALE_FOUND) +endif (FFMPEG_FOUND) + if (TAGLIB_FOUND) macro_optional_add_subdirectory(juk) endif(TAGLIB_FOUND) Index: ffmpegthumbs/CMakeLists.txt =================================================================== --- ffmpegthumbs/CMakeLists.txt (Revision 0) +++ ffmpegthumbs/CMakeLists.txt (Revision 0) @@ -0,0 +1,55 @@ + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${AVUTIL_INCLUDES} ${AVFORMAT_INCLUDES} ${AVCODEC_INCLUDES} ${SWSCALE_INCLUDES} ) + +set(FFMPEG_API FALSE) + +CHECK_INCLUDE_FILE(ffmpeg/avcodec.h HAVE_FFMPEG_AVCODEC) +CHECK_INCLUDE_FILE(ffmpeg/avformat.h HAVE_FFMPEG_AVFORMAT) +CHECK_INCLUDE_FILE(ffmpeg/avutil.h HAVE_FFMPEG_AVUTIL) +CHECK_INCLUDE_FILE(libswscale/swscale.h HAVE_FFMPEG_SWSCALE) + +if(FFMPEG_FOUND) + set (FFMPEG_API 1) +endif(FFMPEG_FOUND) + +if (HAVE_FFMPEG_AVFORMAT) + SET( CMAKE_CXX_FLAGS "-D FFMPEG_AVFORMAT_API=1" ) +endif(HAVE_FFMPEG_AVFORMAT) + +if (HAVE_FFMPEG_AVCODEC) + SET( CMAKE_CXX_FLAGS "-D FFMPEG_AVCODEC_API=1" ) +endif(HAVE_FFMPEG_AVCODEC) + +if (HAVE_FFMPEG_SWSCALE) + SET( CMAKE_CXX_FLAGS "-D FFMPEG_SWSCALE_API=1" ) +endif(HAVE_FFMPEG_SWSCALE) + + +if( FFMPEG_API ) + set( ffmpegthumbs_PART_SRCS + + ffmpegthumbnailer.cpp + ffmpegthumbnailer/filmstripfilter.cpp + ffmpegthumbnailer/moviedecoder.cpp + ffmpegthumbnailer/imagewriter.cpp + ffmpegthumbnailer/videothumbnailer.cpp +) +endif( FFMPEG_API ) + + +kde4_add_plugin(ffmpegthumbs ${ffmpegthumbs_PART_SRCS}) + + +if ( FFMPEG_API ) + target_link_libraries(ffmpegthumbs ${KDE4_KIO_LIBS} ${AVUTIL_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES} ) +endif ( FFMPEG_API ) + +install(TARGETS ffmpegthumbs DESTINATION ${PLUGIN_INSTALL_DIR}) + + +########### install files ############### + +install(FILES ffmpegthumbs.desktop DESTINATION ${SERVICES_INSTALL_DIR}) +add_subdirectory(tests) + + Index: ffmpegthumbs/ffmpegthumbnailer.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer.h (Revision 0) @@ -0,0 +1,40 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef KFFMPEG_THUMBNAILER_H +#define KFFMPEG_THUMBNAILER_H + +#include <QObject> +#include <kio/thumbcreator.h> + +#include <ffmpegthumbnailer/videothumbnailer.h> +#include <ffmpegthumbnailer/filmstripfilter.h> + +class FFMpegThumbnailer : public QObject, public ThumbCreator +{ +Q_OBJECT +public: + FFMpegThumbnailer(); + virtual ~FFMpegThumbnailer(); + virtual bool create(const QString& path, int width, int height, QImage& img); + virtual Flags flags() const; + +private: + ffmpegthumbnailer::VideoThumbnailer m_Thumbnailer; + ffmpegthumbnailer::FilmStripFilter m_FilmStrip; +}; + +#endif Index: ffmpegthumbs/ffmpegthumbnailer.cpp =================================================================== --- ffmpegthumbs/ffmpegthumbnailer.cpp (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer.cpp (Revision 0) @@ -0,0 +1,55 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "ffmpegthumbnailer.h" +#include <kdebug.h> +#include <QImage> + +extern "C" +{ + KDE_EXPORT ThumbCreator* new_creator() + { + return new FFMpegThumbnailer(); + } +} + + +FFMpegThumbnailer::FFMpegThumbnailer() +{ + m_Thumbnailer.addFilter(&m_FilmStrip); +} + +FFMpegThumbnailer::~FFMpegThumbnailer() +{ +} + +bool FFMpegThumbnailer::create(const QString& path, int width, int /*heigth*/, QImage& img) +{ + m_Thumbnailer.setThumbnailSize(width); + // 20% seek inside the video to generate the preview + m_Thumbnailer.setSeekPercentage(25); + //Smart frame selection is very slow compared to the fixed detection + //m_Thumbnailer.setSmartFrameSelection(true); + m_Thumbnailer.generateThumbnail(path, img); + + return !img.isNull(); +} + +ThumbCreator::Flags FFMpegThumbnailer::flags() const +{ + return (Flags)(DrawFrame); +} + Index: ffmpegthumbs/ffmpegthumbnailer/.directory =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/.directory (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/.directory (Revision 0) @@ -0,0 +1,5 @@ +[Dolphin] +Timestamp=2010,4,27,14,23,9 + +[Settings] +ShowDotFiles=true Index: ffmpegthumbs/ffmpegthumbnailer/AUTHORS =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/AUTHORS (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/AUTHORS (Revision 0) @@ -0,0 +1,2 @@ +Dirk Vanden Boer <dirk.vdb@gmail.com> +Andreas Scherf <ascherfy@googlemail.com> (KDE changes) Eigenschaftsänderungen: ffmpegthumbs/ffmpegthumbnailer/AUTHORS ___________________________________________________________________ Hinzugefügt: svn:executable + * Index: ffmpegthumbs/ffmpegthumbnailer/ChangeLog =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/ChangeLog (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/ChangeLog (Revision 0) @@ -0,0 +1,101 @@ +FFmpegThumbnailer + +Version 2.0.1 +- Setting the thumbnail size to 0 will use the original video size (thanks to John Fremlin) +- Fix for video files containing lots of audio packets before a video packet +- Fixed libs in pkgconfig file (thanks to magnus.tuominen) + +version 2.0.0 +- Fixed some issues in package-config file (Thanks to ambrop7) +- C++ library has been put in a namespace +- C library functions have been renamed + +version 1.5.6 +- Fixed segmentation fault when seek in video file fails +- Command line option added to specify output format of the image regardless of the filename +- Fixed memory leak in MovieDecoder (thanks to Ulrich Völkel) + +version 1.5.5 +- Fixed build issue with certain versions of autoconf +- Fixed build issue with recent versions of gcc +- Fixed thumbnail generation for ogm files +- Fixed ffmpegthumbnailer hanging on certain filetypes caused by AVFMT_FLAG_GENPTS flag of video decoder + +version 1.5.4 +- Fixed support for large files + +version 1.5.3 +- Memory alignment fix causing segmentation in ffmpeg with altivec enabled + +version 1.5.2 +- Fixed soname version + +version 1.5.1 +- Optional compilation of jpeg and png support +- Fixed ffmpeg CFLAGS issue (causing compilation errors on Fedora) +- Fixed build error against latest ffmpeg (verified against ffmpeg revision 18769) +- Fixed missing include files for library usage (thanks to gustavo.boiko) + +version 1.5.0 +- Aspect ratio bug fix (Thanks to S. Eguchi) +- Support input files from stdin (will ignore seek time) +- Fixed build error against latest ffmpeg (verified against ffmpeg release 0.5) + +version 1.4.0 +- Filter support added that allows library users to implement filters that will be applied to the frame data +- Fixed compilation error with gcc 4.4 + +version 1.3.0 +- Option added to ignore aspect ratio and generate square thumbnails +- Quality option added for jpeg thumbnails +- Man page added (thanks to Lionel Le Folgoc) +- Added option to seek to absolute time in stead of percentage + +version 1.2.6 +- Fixed seeking in large files due to integer overflow (Thanks to AWaters1) +- Fixed possible linker error (Thanks to pressh) + +version 1.2.5 +- Fixed crash when thumbnailing raw avi files + +version 1.2.4 +- Fixed missing includes when compiling with gcc 4.3 (thanks to Samuli Suominen) + +version 1.2.3 +- ffmegthumbnailer now compiles against latest ffmpeg revisions (thanks to Alexis Ballier) + +version 1.2.2 +- Fixed linker errors when using certain ldflags + +version 1.2.1 +- Fixed compilation error in c interface when compiled with c compiler +- Error handling added to c interface + +version 1.2.0 +- Option to output files in jpeg +- ffmpegthumbnailer can now be accessed as a library (libffmpegthumbnailer) by other applications + +version 1.1.5 +- Support for large files (files larger than 2Gb) +- Seeking in h264 files is enabled again, use -w option from commandline to disable this if you have 100% cpu usage (using older versions of ffmpeg) +- Give up reading packets from a file after a number of attempts to avoid a hanging ffmpegthumbnailer for certain file types (noticed on h264 and ac3 in vob container) + +version 1.1.4 +- License information added to source files + +version 1.1.3 +- Use PkgConfig for ffmpeg to avoid build problems + +version 1.1.2 +- Fixed compilation error when using latest ffmpeg (thanks to Samuli Suominen) + +version 1.1.1 +- Fixed compilation error when using latest ffmpeg +- Disabled seeking in h264 files, because this causes 100% CPU usage in ffmpeglib (workaround) + +version 1.1 +- Updated command line argument parser +- Possibility to add a movie strip overlay + +version 1.0 +- Initial release of the FFmpegThumbnailer Index: ffmpegthumbs/ffmpegthumbnailer/README =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/README (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/README (Revision 0) @@ -0,0 +1,7 @@ +FFmpegthumbnailer is a lightweight video thumbnailer that can be used by file +managers to create thumbnails for your video files. The thumbnailer uses ffmpeg +o decode frames from the video files, so supported videoformats depend on the +configuration flags of ffmpeg. + +This thumbnailer was designed to be as fast and lightweight as possible. The +only dependencies are ffmpeglibraies and libswscale . Eigenschaftsänderungen: ffmpegthumbs/ffmpegthumbnailer/README ___________________________________________________________________ Hinzugefügt: svn:executable + * Index: ffmpegthumbs/ffmpegthumbnailer/filmstripfilter.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/filmstripfilter.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/filmstripfilter.h (Revision 0) @@ -0,0 +1,33 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef FILMSTRIPFILTER_H +#define FILMSTRIPFILTER_H + +#include "ifilter.h" + +namespace ffmpegthumbnailer +{ + +class FilmStripFilter : public IFilter +{ +public: + virtual void process(VideoFrame& videoFrame); +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/filmstripfilter.cpp =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/filmstripfilter.cpp (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/filmstripfilter.cpp (Revision 0) @@ -0,0 +1,69 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "filmstripfilter.h" + +namespace ffmpegthumbnailer +{ + +static const int FILMHOLE_WIDTH = 12; +static const int FILMHOLE_HEIGHT = 10; + +static const uint8_t filmHole[FILMHOLE_WIDTH * FILMHOLE_HEIGHT * 3] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x23, 0x23, 0x7a, 0x7a, 0x7a, 0x83, 0x83, 0x83, 0x8c, 0x8c, 0x8c, 0x90, 0x90, 0x90, 0x8e, 0x8e, 0x8e, 0x52, 0x52, 0x52, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x6e, 0x6e, 0x83, 0x83, 0x83, 0x93, 0x93, 0x93, 0xa3, 0xa3, 0xa3, 0xab, 0xab, 0xab, 0xa8, 0xa8, 0xa8, 0x9b, 0x9b, 0x9b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x72, 0x72, 0x8e, 0x8e, 0x8e, 0xa4, 0xa4, 0xa4, 0xbb, 0xbb, 0xbb, 0xc4, 0xc4, 0xc4, 0xc1, 0xc1, 0xc1, 0xaf, 0xaf, 0xaf, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x90, 0x90, 0x90, 0xa6, 0xa6, 0xa6, 0xbe, 0xbe, 0xbe, 0xc8, 0xc8, 0xc8, 0xc4, 0xc4, 0xc4, 0x8e, 0x8e, 0x8e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x07, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +void FilmStripFilter::process(VideoFrame& videoFrame) +{ + if (videoFrame.width < FILMHOLE_WIDTH * 2) + { + return; + } + + int frameIndex = 0; + int filmHoleIndex = 0; + int offset = (videoFrame.width * 3) - 3; + + for (int i = 0; i < videoFrame.height; ++i) + { + for (int j = 0; j < FILMHOLE_WIDTH * 3; j+=3) + { + int currentFilmHoleIndex = filmHoleIndex + j; + + videoFrame.frameData[frameIndex + j] = filmHole[currentFilmHoleIndex]; + videoFrame.frameData[frameIndex + j + 1] = filmHole[currentFilmHoleIndex + 1]; + videoFrame.frameData[frameIndex + j + 2] = filmHole[currentFilmHoleIndex + 2]; + + videoFrame.frameData[frameIndex + offset - j] = filmHole[currentFilmHoleIndex]; + videoFrame.frameData[frameIndex + offset - j + 1] = filmHole[currentFilmHoleIndex + 1]; + videoFrame.frameData[frameIndex + offset - j + 2] = filmHole[currentFilmHoleIndex + 2]; + } + frameIndex += videoFrame.lineSize; + filmHoleIndex = (i % FILMHOLE_HEIGHT) * FILMHOLE_WIDTH * 3; + } +} + +} Index: ffmpegthumbs/ffmpegthumbnailer/histogram.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/histogram.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/histogram.h (Revision 0) @@ -0,0 +1,42 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef HISTOGRAM_H +#define HISTOGRAM_H + +#include <string.h> + +namespace ffmpegthumbnailer +{ + +template <typename T> +struct Histogram +{ + T r[256]; + T g[256]; + T b[256]; + + Histogram() + { + memset(r, 0, 255 * sizeof(T)); + memset(g, 0, 255 * sizeof(T)); + memset(b, 0, 255 * sizeof(T)); + } +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/ifilter.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/ifilter.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/ifilter.h (Revision 0) @@ -0,0 +1,33 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef IFILTER_H +#define IFILTER_H + +#include "videoframe.h" + +namespace ffmpegthumbnailer +{ + +class IFilter +{ +public: + virtual void process(VideoFrame& frameData) = 0; +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/imagewriter.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/imagewriter.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/imagewriter.h (Revision 0) @@ -0,0 +1,38 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef IMAGE_WRITER_H +#define IMAGE_WRITER_H +#include "videoframe.h" +#include <string> +#include <inttypes.h> +#include <QString> +#include <QImage> +namespace ffmpegthumbnailer +{ + +class ImageWriter +{ +public: + ImageWriter(); + virtual ~ImageWriter() {} + + virtual void writeFrame(VideoFrame& frame, QImage& image); +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/imagewriter.cpp =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/imagewriter.cpp (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/imagewriter.cpp (Revision 0) @@ -0,0 +1,42 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "imagewriter.h" +#include <kdebug.h> +#include <iostream> + + +using namespace std; + +namespace ffmpegthumbnailer +{ + + ImageWriter::ImageWriter() + { + } + + void ImageWriter::writeFrame(VideoFrame& frame, QImage& image) + { + QImage previewImage( frame.width,frame.height,QImage::Format_RGB888); + for(unsigned int y=0;y<frame.height;y++) + { + // Copy each line .. + memcpy(previewImage.scanLine(y),&frame.frameData[0+y*frame.lineSize],frame.width*3); + } + + image=previewImage; + } +} Index: ffmpegthumbs/ffmpegthumbnailer/moviedecoder.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/moviedecoder.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/moviedecoder.h (Revision 0) @@ -0,0 +1,93 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef MOVIEDECODER_H +#define MOVIEDECODER_H + +#define __STDC_CONSTANT_MACROS +#define INT64_C +#include <inttypes.h> + +#include <string> +#include <vector> + +#include "videoframe.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +extern "C" { +#if FFMPEG_AVCODEC_API +#include <ffmpeg/avcodec.h> +#else +#include <libavcodec/avcodec.h> +#endif +#if FFMPEG_AVFORMAT_API +#include <ffmpeg/avformat.h> +#else +#include <libavformat/avformat.h> +#endif +} + +#include <QString> + +namespace ffmpegthumbnailer +{ + +class MovieDecoder +{ +public: + MovieDecoder(const QString& filename, AVFormatContext* pavContext = NULL); + ~MovieDecoder(); + + std::string getCodec(); + void seek(int timeInSeconds); + void decodeVideoFrame(); + void getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame); + + int getWidth(); + int getHeight(); + int getDuration(); + + void initialize(const QString& filename); + void destroy(); + +private: + void initializeVideo(); + + bool decodeVideoPacket(); + bool getVideoPacket(); + void convertAndScaleFrame(PixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight); + void createAVFrame(AVFrame** avFrame, uint8_t** frameBuffer, int width, int height, PixelFormat format); + void calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight); + +private: + int m_VideoStream; + AVFormatContext* m_pFormatContext; + AVCodecContext* m_pVideoCodecContext; + AVCodec* m_pVideoCodec; + AVStream* m_pVideoStream; + AVFrame* m_pFrame; + uint8_t* m_pFrameBuffer; + AVPacket* m_pPacket; + bool m_FormatContextWasGiven; + bool m_AllowSeek; +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/moviedecoder.cpp =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/moviedecoder.cpp (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/moviedecoder.cpp (Revision 0) @@ -0,0 +1,406 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "moviedecoder.h" +#include <stdexcept> + + +#include <cstring> +#include <kdebug.h> + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +extern "C" { +#if FFMPEG_SWSCALE_API +#include <ffmpeg/swscale.h> +#else +#include <libswscale/swscale.h> +#endif +} + +using namespace std; + +namespace ffmpegthumbnailer +{ + +MovieDecoder::MovieDecoder(const QString& filename, AVFormatContext* pavContext) +: m_VideoStream(-1) +, m_pFormatContext(pavContext) +, m_pVideoCodecContext(NULL) +, m_pVideoCodec(NULL) +, m_pVideoStream(NULL) +, m_pFrame(NULL) +, m_pFrameBuffer(NULL) +, m_pPacket(NULL) +, m_FormatContextWasGiven(pavContext != NULL) +, m_AllowSeek(filename != "-") +{ + initialize(filename); +} + +MovieDecoder::~MovieDecoder() +{ + destroy(); +} + +void MovieDecoder::initialize(const QString& filename) +{ + av_register_all(); + avcodec_init(); + avcodec_register_all(); + + QString inputFile = filename == "-" ? "pipe:" : filename; + + if ((!m_FormatContextWasGiven) && av_open_input_file(&m_pFormatContext, inputFile.toAscii().data(), NULL, 0, NULL) != 0) + { + kDebug() << "Could not open input file: " << filename; + } + + if (av_find_stream_info(m_pFormatContext) < 0) + { + kDebug() << "Could not find stream information"; + } + + initializeVideo(); + m_pFrame = avcodec_alloc_frame(); +} + +void MovieDecoder::destroy() +{ + if (m_pVideoCodecContext) + { + avcodec_close(m_pVideoCodecContext); + m_pVideoCodecContext = NULL; + } + + if ((!m_FormatContextWasGiven) && m_pFormatContext) + { + av_close_input_file(m_pFormatContext); + m_pFormatContext = NULL; + } + + if (m_pPacket) + { + av_free_packet(m_pPacket); + delete m_pPacket; + m_pPacket = NULL; + } + + if (m_pFrame) + { + av_free(m_pFrame); + m_pFrame = NULL; + } + + if (m_pFrameBuffer) + { + av_free(m_pFrameBuffer); + m_pFrameBuffer = NULL; + } +} + +string MovieDecoder::getCodec() +{ + if (m_pVideoCodec) + { + return m_pVideoCodec->name; + } + + return ""; +} + +void MovieDecoder::initializeVideo() +{ + for(unsigned int i = 0; i < m_pFormatContext->nb_streams; i++) + { + if (m_pFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) + { + m_pVideoStream = m_pFormatContext->streams[i]; + m_VideoStream = i; + break; + } + } + + if (m_VideoStream == -1) + { + kDebug() << "Could not find video stream"; + return; + } + + m_pVideoCodecContext = m_pFormatContext->streams[m_VideoStream]->codec; + m_pVideoCodec = avcodec_find_decoder(m_pVideoCodecContext->codec_id); + + if (m_pVideoCodec == NULL) + { + // set to NULL, otherwise avcodec_close(m_pVideoCodecContext) crashes + m_pVideoCodecContext = NULL; + kDebug() << "Video Codec not found"; + return; + } + + m_pVideoCodecContext->workaround_bugs = 1; + + if (avcodec_open(m_pVideoCodecContext, m_pVideoCodec) < 0) + { + kDebug() << "Could not open video codec"; + } +} + +int MovieDecoder::getWidth() +{ + if (m_pVideoCodecContext) + { + return m_pVideoCodecContext->width; + } + + return -1; +} + +int MovieDecoder::getHeight() +{ + if (m_pVideoCodecContext) + { + return m_pVideoCodecContext->height; + } + + return -1; +} + +int MovieDecoder::getDuration() +{ + if (m_pFormatContext) + { + return static_cast<int>(m_pFormatContext->duration / AV_TIME_BASE); + } + + return 0; +} + +void MovieDecoder::seek(int timeInSeconds) +{ + if (!m_AllowSeek) + { + return; + } + + int64_t timestamp = AV_TIME_BASE * static_cast<int64_t>(timeInSeconds); + + if (timestamp < 0) + { + timestamp = 0; + } + + int ret = av_seek_frame(m_pFormatContext, -1, timestamp, 0); + if (ret >= 0) + { + avcodec_flush_buffers(m_pFormatContext->streams[m_VideoStream]->codec); + } + else + { + kDebug() << "Seeking in video failed"; + return; + } + + int keyFrameAttempts = 0; + bool gotFrame = 0; + + do + { + int count = 0; + gotFrame = 0; + + while (!gotFrame && count < 20) + { + getVideoPacket(); + gotFrame = decodeVideoPacket(); + ++count; + } + + ++keyFrameAttempts; + } while ((!gotFrame || !m_pFrame->key_frame) && keyFrameAttempts < 200); + + if (gotFrame == 0) + { + kDebug() << "Seeking in video failed"; + } +} + + +void MovieDecoder::decodeVideoFrame() +{ + bool frameFinished = false; + + while(!frameFinished && getVideoPacket()) + { + frameFinished = decodeVideoPacket(); + } + + if (!frameFinished) + { + kDebug() << "decodeVideoFrame() failed: frame not finished"; + return; + } +} + +bool MovieDecoder::decodeVideoPacket() +{ + if (m_pPacket->stream_index != m_VideoStream) + { + return false; + } + + avcodec_get_frame_defaults(m_pFrame); + + int frameFinished; + +#if LIBAVCODEC_VERSION_MAJOR < 53 + int bytesDecoded = avcodec_decode_video(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket->data, m_pPacket->size); +#else + int bytesDecoded = avcodec_decode_video2(m_pVideoCodecContext, m_pFrame, &frameFinished, m_pPacket); +#endif + + if (bytesDecoded < 0) + { + kDebug() << "Failed to decode video frame: bytesDecoded < 0"; + } + + return (frameFinished > 0); +} + +bool MovieDecoder::getVideoPacket() +{ + bool framesAvailable = true; + bool frameDecoded = false; + + int attempts = 0; + + if (m_pPacket) + { + av_free_packet(m_pPacket); + delete m_pPacket; + } + + m_pPacket = new AVPacket(); + + while (framesAvailable && !frameDecoded && (attempts++ < 1000)) + { + framesAvailable = av_read_frame(m_pFormatContext, m_pPacket) >= 0; + if (framesAvailable) + { + frameDecoded = m_pPacket->stream_index == m_VideoStream; + if (!frameDecoded) + { + av_free_packet(m_pPacket); + } + } + } + + return frameDecoded; +} + +void MovieDecoder::getScaledVideoFrame(int scaledSize, bool maintainAspectRatio, VideoFrame& videoFrame) +{ + if (m_pFrame->interlaced_frame) + { + avpicture_deinterlace((AVPicture*) m_pFrame, (AVPicture*) m_pFrame, m_pVideoCodecContext->pix_fmt, + m_pVideoCodecContext->width, m_pVideoCodecContext->height); + } + + int scaledWidth, scaledHeight; + convertAndScaleFrame(PIX_FMT_RGB24, scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); + + videoFrame.width = scaledWidth; + videoFrame.height = scaledHeight; + videoFrame.lineSize = m_pFrame->linesize[0]; + + videoFrame.frameData.clear(); + videoFrame.frameData.resize(videoFrame.lineSize * videoFrame.height); + memcpy((&(videoFrame.frameData.front())), m_pFrame->data[0], videoFrame.lineSize * videoFrame.height); +} + +void MovieDecoder::convertAndScaleFrame(PixelFormat format, int scaledSize, bool maintainAspectRatio, int& scaledWidth, int& scaledHeight) +{ + calculateDimensions(scaledSize, maintainAspectRatio, scaledWidth, scaledHeight); + SwsContext* scaleContext = sws_getContext(m_pVideoCodecContext->width, m_pVideoCodecContext->height, + m_pVideoCodecContext->pix_fmt, scaledWidth, scaledHeight, + format, SWS_BICUBIC, NULL, NULL, NULL); + + if (NULL == scaleContext) + { + kDebug() << "Failed to create resize context"; + return; + } + + AVFrame* convertedFrame = NULL; + uint8_t* convertedFrameBuffer = NULL; + + createAVFrame(&convertedFrame, &convertedFrameBuffer, scaledWidth, scaledHeight, format); + + sws_scale(scaleContext, m_pFrame->data, m_pFrame->linesize, 0, m_pVideoCodecContext->height, + convertedFrame->data, convertedFrame->linesize); + sws_freeContext(scaleContext); + + av_free(m_pFrame); + av_free(m_pFrameBuffer); + + m_pFrame = convertedFrame; + m_pFrameBuffer = convertedFrameBuffer; +} + +void MovieDecoder::calculateDimensions(int squareSize, bool maintainAspectRatio, int& destWidth, int& destHeight) +{ + if (!maintainAspectRatio) + { + destWidth = squareSize; + destHeight = squareSize; + } + else + { + int srcWidth = m_pVideoCodecContext->width; + int srcHeight = m_pVideoCodecContext->height; + int ascpectNominator = m_pVideoCodecContext->sample_aspect_ratio.num; + int ascpectDenominator = m_pVideoCodecContext->sample_aspect_ratio.den; + + if (ascpectNominator != 0 && ascpectDenominator != 0) + { + srcWidth = srcWidth * ascpectNominator / ascpectDenominator; + } + + if (srcWidth > srcHeight) + { + destWidth = squareSize; + destHeight = static_cast<int>(static_cast<float>(squareSize) / srcWidth * srcHeight); + } + else + { + destWidth = static_cast<int>(static_cast<float>(squareSize) / srcHeight * srcWidth); + destHeight = squareSize; + } + } +} + +void MovieDecoder::createAVFrame(AVFrame** avFrame, uint8_t** frameBuffer, int width, int height, PixelFormat format) +{ + *avFrame = avcodec_alloc_frame(); + + int numBytes = avpicture_get_size(format, width, height); + *frameBuffer = reinterpret_cast<uint8_t*>(av_malloc(numBytes)); + avpicture_fill((AVPicture*) *avFrame, *frameBuffer, format, width, height); +} + +} Index: ffmpegthumbs/ffmpegthumbnailer/videoframe.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/videoframe.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/videoframe.h (Revision 0) @@ -0,0 +1,43 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef VIDEOFRAME_H +#define VIDEOFRAME_H + +#include <inttypes.h> +#include <vector> + +namespace ffmpegthumbnailer +{ + +struct VideoFrame +{ + VideoFrame() + : width(0), height(0), lineSize(0) {} + + VideoFrame(int width, int height, int lineSize) + : width(width), height(height), lineSize(lineSize) {} + + int width; + int height; + int lineSize; + + std::vector<uint8_t> frameData; +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.h =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.h (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.h (Revision 0) @@ -0,0 +1,83 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef VIDEO_THUMBNAILER_H +#define VIDEO_THUMBNAILER_H + +#include <string> +#include <vector> +#include <map> +#include <inttypes.h> + +#include "ifilter.h" +#include "histogram.h" +#include <QString> +#include <QImage> + + +namespace ffmpegthumbnailer +{ + +class VideoFrame; +class ImageWriter; +class MovieDecoder; + +class VideoThumbnailer +{ +public: + VideoThumbnailer(); + VideoThumbnailer(int thumbnailSize, bool workaroundIssues, bool maintainAspectRatio, int imageQuality, bool smartFrameSelection); + ~VideoThumbnailer(); + + void generateThumbnail(const QString& videoFile, QImage &image); + + void setThumbnailSize(int size); + void setSeekPercentage(int percentage); + void setSeekTime(const QString& seekTime); + void setWorkAroundIssues(bool workAround); + void setImageQuality(int imageQuality); + void setMaintainAspectRatio(bool enabled); + void setSmartFrameSelection(bool enabled); + void addFilter(IFilter* filter); + void removeFilter(IFilter* filter); + void clearFilters(); + +private: + void generateThumbnail(const QString& videoFile, ImageWriter& imageWriter,QImage& image); + void generateSmartThumbnail(MovieDecoder& movieDecoder, VideoFrame& videoFrame); + + QString getMimeType(const QString& videoFile); + QString getExtension(const QString& videoFilename); + + void generateHistogram(const VideoFrame& videoFrame, Histogram<int>& histogram); + int getBestThumbnailIndex(std::vector<VideoFrame>& videoFrames, const std::vector<Histogram<int> >& histograms); + void applyFilters(VideoFrame& frameData); + +private: + int m_ThumbnailSize; + uint16_t m_SeekPercentage; + bool m_OverlayFilmStrip; + bool m_WorkAroundIssues; + int m_ImageQuality; + bool m_MaintainAspectRatio; + bool m_SmartFrameSelection; + QString m_SeekTime; + std::vector<IFilter*> m_Filters; +}; + +} + +#endif Index: ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.cpp =================================================================== --- ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.cpp (Revision 0) +++ ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.cpp (Revision 0) @@ -0,0 +1,247 @@ +// Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com> +// +// 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) any later version. +// +// 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, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "videothumbnailer.h" + +#include "moviedecoder.h" +#include "filmstripfilter.h" +#include "imagewriter.h" + +#include <iostream> +#include <cfloat> +#include <cmath> +#include <qglobal.h> +#include <sys/stat.h> +#include <QFileInfo> +#include <QTime> + + +using namespace std; + +namespace ffmpegthumbnailer +{ + +static const int SMART_FRAME_ATTEMPTS = 25; + +VideoThumbnailer::VideoThumbnailer() +: m_ThumbnailSize(128) +, m_SeekPercentage(10) +, m_OverlayFilmStrip(false) +, m_WorkAroundIssues(false) +, m_ImageQuality(8) +, m_MaintainAspectRatio(true) +, m_SmartFrameSelection(false) +{ +} + +VideoThumbnailer::VideoThumbnailer(int thumbnailSize, bool workaroundIssues, bool maintainAspectRatio, int imageQuality, bool smartFrameSelection) +: m_ThumbnailSize(thumbnailSize) +, m_SeekPercentage(10) +, m_WorkAroundIssues(workaroundIssues) +, m_ImageQuality(imageQuality) +, m_MaintainAspectRatio(maintainAspectRatio) +, m_SmartFrameSelection(smartFrameSelection) +{ +} + +VideoThumbnailer::~VideoThumbnailer() +{ +} + +void VideoThumbnailer::setSeekPercentage(int percentage) +{ + m_SeekTime.clear(); + m_SeekPercentage = percentage > 95 ? 95 : percentage; +} + +void VideoThumbnailer::setSeekTime(const QString& seekTime) +{ + m_SeekTime = seekTime; +} + +void VideoThumbnailer::setThumbnailSize(int size) +{ + m_ThumbnailSize = size; +} + +void VideoThumbnailer::setWorkAroundIssues(bool workAround) +{ + m_WorkAroundIssues = workAround; +} + +void VideoThumbnailer::setImageQuality(int imageQuality) +{ + m_ImageQuality = imageQuality; +} + +void VideoThumbnailer::setMaintainAspectRatio(bool enabled) +{ + m_MaintainAspectRatio = enabled; +} + +void VideoThumbnailer::setSmartFrameSelection(bool enabled) +{ + m_SmartFrameSelection = enabled; +} + +int timeToSeconds(const QString& time) +{ + return QTime::fromString(time,"hh:mm:ss").secsTo(QTime(0,0,0)); +} + +void VideoThumbnailer::generateThumbnail(const QString& videoFile, ImageWriter& imageWriter,QImage &image) +{ + MovieDecoder movieDecoder(videoFile, NULL); + movieDecoder.decodeVideoFrame(); //before seeking, a frame has to be decoded + + if ((!m_WorkAroundIssues) || (movieDecoder.getCodec() != "h264")) //workaround for bug in older ffmpeg (100% cpu usage when seeking in h264 files) + { + int secondToSeekTo = m_SeekTime.isEmpty() ? movieDecoder.getDuration() * m_SeekPercentage / 100 : timeToSeconds(m_SeekTime); + movieDecoder.seek(secondToSeekTo); + } + + VideoFrame videoFrame; + + if (m_SmartFrameSelection) + { + generateSmartThumbnail(movieDecoder, videoFrame); + } + else + { + movieDecoder.getScaledVideoFrame(m_ThumbnailSize, m_MaintainAspectRatio, videoFrame); + } + + applyFilters(videoFrame); + imageWriter.writeFrame(videoFrame, image); +} + +void VideoThumbnailer::generateSmartThumbnail(MovieDecoder& movieDecoder, VideoFrame& videoFrame) +{ + vector<VideoFrame> videoFrames(SMART_FRAME_ATTEMPTS); + vector<Histogram<int> > histograms(SMART_FRAME_ATTEMPTS); + + for (int i = 0; i < SMART_FRAME_ATTEMPTS; ++i) + { + movieDecoder.decodeVideoFrame(); + movieDecoder.getScaledVideoFrame(m_ThumbnailSize, m_MaintainAspectRatio, videoFrames[i]); + generateHistogram(videoFrames[i], histograms[i]); + } + + int bestFrame = getBestThumbnailIndex(videoFrames, histograms); + + Q_ASSERT(bestFrame != -1); + videoFrame = videoFrames[bestFrame]; +} + + +void VideoThumbnailer::generateThumbnail(const QString& videoFile, QImage &image) +{ + ImageWriter* imageWriter = new ImageWriter(); + generateThumbnail(videoFile, *imageWriter,image); + delete imageWriter; +} + + + +void VideoThumbnailer::addFilter(IFilter* filter) +{ + m_Filters.push_back(filter); +} + +void VideoThumbnailer::removeFilter(IFilter* filter) +{ + for (vector<IFilter*>::iterator iter = m_Filters.begin(); + iter != m_Filters.end(); + ++iter) + { + if (*iter == filter) + { + m_Filters.erase(iter); + break; + } + } +} + +void VideoThumbnailer::clearFilters() +{ + m_Filters.clear(); +} + +void VideoThumbnailer::applyFilters(VideoFrame& videoFrame) +{ + for (vector<IFilter*>::iterator iter = m_Filters.begin(); + iter != m_Filters.end(); + ++iter) + { + (*iter)->process(videoFrame); + } +} + +void VideoThumbnailer::generateHistogram(const VideoFrame& videoFrame, Histogram<int>& histogram) +{ + for (int i = 0; i < videoFrame.height; ++i) + { + int pixelIndex = i * videoFrame.lineSize; + for (int j = 0; j < videoFrame.width * 3; j += 3) + { + ++histogram.r[videoFrame.frameData[pixelIndex + j]]; + ++histogram.g[videoFrame.frameData[pixelIndex + j + 1]]; + ++histogram.b[videoFrame.frameData[pixelIndex + j + 2]]; + } + } +} + +int VideoThumbnailer::getBestThumbnailIndex(vector<VideoFrame>& videoFrames, const vector<Histogram<int> >& histograms) +{ + Histogram<float> avgHistogram; + for (size_t i = 0; i < histograms.size(); ++i) + { + for (int j = 0; j < 255; ++j) + { + avgHistogram.r[j] += static_cast<float>(histograms[i].r[j]) / histograms.size(); + avgHistogram.g[j] += static_cast<float>(histograms[i].g[j]) / histograms.size(); + avgHistogram.b[j] += static_cast<float>(histograms[i].b[j]) / histograms.size(); + } + } + + int bestFrame = -1; + float minRMSE = FLT_MAX; + for (size_t i = 0; i < histograms.size(); ++i) + { + //calculate root mean squared error + float rmse = 0.0; + for (int j = 0; j < 255; ++j) + { + float error = fabsf(avgHistogram.r[j] - histograms[i].r[j]) + + fabsf(avgHistogram.g[j] - histograms[i].g[j]) + + fabsf(avgHistogram.b[j] - histograms[i].b[j]); + rmse += (error * error) / 255; + } + + rmse = sqrtf(rmse); + if (rmse < minRMSE) + { + minRMSE = rmse; + bestFrame = i; + } + } +#ifdef DEBUG_MODE + cout << "Best frame was: " << bestFrame << "(RMSE: " << minRMSE << ")" << endl; +#endif + return bestFrame; +} + +} Index: ffmpegthumbs/ffmpegthumbs.desktop =================================================================== --- ffmpegthumbs/ffmpegthumbs.desktop (Revision 0) +++ ffmpegthumbs/ffmpegthumbs.desktop (Revision 0) @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +Name=Video Files (ffmpegthumbs) +X-KDE-ServiceTypes=ThumbCreator +MimeType=video/*;application/x-flash-video;application/vnd.ms-asf;application/vnd.rn-realmedia; +X-KDE-Library=ffmpegthumbs +ServiceTypes=ThumbCreator +CacheThumbnail=true +ThumbnailerVersion=2 +IgnoreMaximumSize=true Index: ffmpegthumbs/tests/CMakeLists.txt =================================================================== --- ffmpegthumbs/tests/CMakeLists.txt (Revision 0) +++ ffmpegthumbs/tests/CMakeLists.txt (Revision 0) @@ -0,0 +1,26 @@ +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +########### next target ############### + + +include_directories( .. ) + + +########### next target ############### +set(ffmpegthumbtest_SRCS ffmpegthumbtest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpegthumbnailer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpegthumbnailer/filmstripfilter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpegthumbnailer/moviedecoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpegthumbnailer/imagewriter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../ffmpegthumbnailer/videothumbnailer.cpp +) + + +kde4_add_executable(ffmpegthumbtest ${ffmpegthumbtest_SRCS} ) + +target_link_libraries(ffmpegthumbtest ${KDE4_KDECORE_LIBS} ${KDE4_KIO_LIBS} ${AVUTIL_LIBRARIES} ${AVFORMAT_LIBRARIES} ${AVCODEC_LIBRARIES} ${SWSCALE_LIBRARIES}) + + + + Index: ffmpegthumbs/tests/ffmpegthumbtest.cpp =================================================================== --- ffmpegthumbs/tests/ffmpegthumbtest.cpp (Revision 0) +++ ffmpegthumbs/tests/ffmpegthumbtest.cpp (Revision 0) @@ -0,0 +1,25 @@ + +#include "ffmpegthumbnailer.h" + +#include <QCoreApplication> +#include <QDir> +#include <iostream> + +#include <stdlib.h> + +using std::cout; +using std::endl; + + +int main( int argc, char **argv ) +{ + QCoreApplication app( argc, argv ); + QImage image; + FFMpegThumbnailer *thumbnailer = new FFMpegThumbnailer(); + thumbnailer->create("/home/scherfa/test.mpg",128,128,image); + delete thumbnailer; + cout << "All OK" << endl; + return 0; +} + +// vim: set et sw=4 tw=0 sta: