~ubuntu-branches/ubuntu/natty/kdemultimedia/natty-proposed

« back to all changes in this revision

Viewing changes to ffmpegthumbs/ffmpegthumbnailer/videothumbnailer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Debian Qt/KDE Maintainers
  • Date: 2011-05-26 02:41:36 UTC
  • mfrom: (0.2.3 upstream)
  • mto: This revision was merged to the branch mainline in revision 108.
  • Revision ID: james.westby@ubuntu.com-20110526024136-jjwsigfy402jhupm
Tags: upstream-4.6.3
ImportĀ upstreamĀ versionĀ 4.6.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//    Copyright (C) 2010 Dirk Vanden Boer <dirk.vdb@gmail.com>
 
2
//
 
3
//    This program is free software; you can redistribute it and/or modify
 
4
//    it under the terms of the GNU General Public License as published by
 
5
//    the Free Software Foundation; either version 2 of the License, or
 
6
//    (at your option) any later version.
 
7
//
 
8
//    This program is distributed in the hope that it will be useful,
 
9
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
//    GNU General Public License for more details.
 
12
//
 
13
//    You should have received a copy of the GNU General Public License
 
14
//    along with this program; if not, write to the Free Software
 
15
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
#include "videothumbnailer.h"
 
18
 
 
19
#include "moviedecoder.h"
 
20
#include "filmstripfilter.h"
 
21
#include "imagewriter.h"
 
22
 
 
23
#include <iostream>
 
24
#include <cfloat>
 
25
#include <cmath>
 
26
#include <qglobal.h>
 
27
#include <sys/stat.h>
 
28
#include <QTime>
 
29
 
 
30
 
 
31
using namespace std;
 
32
 
 
33
namespace ffmpegthumbnailer
 
34
{
 
35
 
 
36
static const int SMART_FRAME_ATTEMPTS = 25;
 
37
 
 
38
VideoThumbnailer::VideoThumbnailer()
 
39
        : m_ThumbnailSize(128)
 
40
        , m_SeekPercentage(10)
 
41
        , m_OverlayFilmStrip(false)
 
42
        , m_WorkAroundIssues(false)
 
43
        , m_MaintainAspectRatio(true)
 
44
        , m_SmartFrameSelection(false)
 
45
{
 
46
}
 
47
 
 
48
VideoThumbnailer::VideoThumbnailer(int thumbnailSize, bool workaroundIssues, bool maintainAspectRatio, bool smartFrameSelection)
 
49
        : m_ThumbnailSize(thumbnailSize)
 
50
        , m_SeekPercentage(10)
 
51
        , m_WorkAroundIssues(workaroundIssues)
 
52
        , m_MaintainAspectRatio(maintainAspectRatio)
 
53
        , m_SmartFrameSelection(smartFrameSelection)
 
54
{
 
55
}
 
56
 
 
57
VideoThumbnailer::~VideoThumbnailer()
 
58
{
 
59
}
 
60
 
 
61
void VideoThumbnailer::setSeekPercentage(int percentage)
 
62
{
 
63
    m_SeekTime.clear();
 
64
    m_SeekPercentage = percentage > 95 ? 95 : percentage;
 
65
}
 
66
 
 
67
void VideoThumbnailer::setSeekTime(const QString& seekTime)
 
68
{
 
69
    m_SeekTime = seekTime;
 
70
}
 
71
 
 
72
void VideoThumbnailer::setThumbnailSize(int size)
 
73
{
 
74
    m_ThumbnailSize = size;
 
75
}
 
76
 
 
77
void VideoThumbnailer::setWorkAroundIssues(bool workAround)
 
78
{
 
79
    m_WorkAroundIssues = workAround;
 
80
}
 
81
 
 
82
void VideoThumbnailer::setMaintainAspectRatio(bool enabled)
 
83
{
 
84
    m_MaintainAspectRatio = enabled;
 
85
}
 
86
 
 
87
void VideoThumbnailer::setSmartFrameSelection(bool enabled)
 
88
{
 
89
    m_SmartFrameSelection = enabled;
 
90
}
 
91
 
 
92
int timeToSeconds(const QString& time)
 
93
{
 
94
    return QTime::fromString(time, QLatin1String("hh:mm:ss")).secsTo(QTime(0, 0, 0));
 
95
}
 
96
 
 
97
void VideoThumbnailer::generateThumbnail(const QString& videoFile, ImageWriter& imageWriter, QImage &image)
 
98
{
 
99
    MovieDecoder movieDecoder(videoFile, NULL);
 
100
    if (movieDecoder.getInitialized()) {
 
101
        movieDecoder.decodeVideoFrame(); //before seeking, a frame has to be decoded
 
102
        
 
103
        if ((!m_WorkAroundIssues) || (movieDecoder.getCodec() != QLatin1String("h264"))) { //workaround for bug in older ffmpeg (100% cpu usage when seeking in h264 files)
 
104
            int secondToSeekTo = m_SeekTime.isEmpty() ? movieDecoder.getDuration() * m_SeekPercentage / 100 : timeToSeconds(m_SeekTime);
 
105
            movieDecoder.seek(secondToSeekTo);
 
106
        }
 
107
    
 
108
        VideoFrame videoFrame;
 
109
        
 
110
        if (m_SmartFrameSelection) {
 
111
            generateSmartThumbnail(movieDecoder, videoFrame);
 
112
        } else {
 
113
            movieDecoder.getScaledVideoFrame(m_ThumbnailSize, m_MaintainAspectRatio, videoFrame);
 
114
        }
 
115
        
 
116
        applyFilters(videoFrame);
 
117
        imageWriter.writeFrame(videoFrame, image);
 
118
    }
 
119
}
 
120
 
 
121
void VideoThumbnailer::generateSmartThumbnail(MovieDecoder& movieDecoder, VideoFrame& videoFrame)
 
122
{
 
123
    vector<VideoFrame> videoFrames(SMART_FRAME_ATTEMPTS);
 
124
    vector<Histogram<int> > histograms(SMART_FRAME_ATTEMPTS);
 
125
 
 
126
    for (int i = 0; i < SMART_FRAME_ATTEMPTS; ++i) {
 
127
        movieDecoder.decodeVideoFrame();
 
128
        movieDecoder.getScaledVideoFrame(m_ThumbnailSize, m_MaintainAspectRatio, videoFrames[i]);
 
129
        generateHistogram(videoFrames[i], histograms[i]);
 
130
    }
 
131
 
 
132
    int bestFrame = getBestThumbnailIndex(videoFrames, histograms);
 
133
 
 
134
    Q_ASSERT(bestFrame != -1);
 
135
    videoFrame = videoFrames[bestFrame];
 
136
}
 
137
 
 
138
void VideoThumbnailer::generateThumbnail(const QString& videoFile, QImage &image)
 
139
{
 
140
    ImageWriter* imageWriter = new  ImageWriter();
 
141
    generateThumbnail(videoFile, *imageWriter, image);
 
142
    delete imageWriter;
 
143
}
 
144
 
 
145
void VideoThumbnailer::addFilter(IFilter* filter)
 
146
{
 
147
    m_Filters.push_back(filter);
 
148
}
 
149
 
 
150
void VideoThumbnailer::removeFilter(IFilter* filter)
 
151
{
 
152
    for (vector<IFilter*>::iterator iter = m_Filters.begin();
 
153
            iter != m_Filters.end();
 
154
            ++iter) {
 
155
        if (*iter == filter) {
 
156
            m_Filters.erase(iter);
 
157
            break;
 
158
        }
 
159
    }
 
160
}
 
161
 
 
162
void VideoThumbnailer::clearFilters()
 
163
{
 
164
    m_Filters.clear();
 
165
}
 
166
 
 
167
void VideoThumbnailer::applyFilters(VideoFrame& videoFrame)
 
168
{
 
169
    for (vector<IFilter*>::iterator iter = m_Filters.begin();
 
170
            iter != m_Filters.end();
 
171
            ++iter) {
 
172
        (*iter)->process(videoFrame);
 
173
    }
 
174
}
 
175
 
 
176
void VideoThumbnailer::generateHistogram(const VideoFrame& videoFrame, Histogram<int>& histogram)
 
177
{
 
178
    for (int i = 0; i < videoFrame.height; ++i) {
 
179
        int pixelIndex = i * videoFrame.lineSize;
 
180
        for (int j = 0; j < videoFrame.width * 3; j += 3) {
 
181
            ++histogram.r[videoFrame.frameData[pixelIndex + j]];
 
182
            ++histogram.g[videoFrame.frameData[pixelIndex + j + 1]];
 
183
            ++histogram.b[videoFrame.frameData[pixelIndex + j + 2]];
 
184
        }
 
185
    }
 
186
}
 
187
 
 
188
int VideoThumbnailer::getBestThumbnailIndex(vector<VideoFrame>& videoFrames, const vector<Histogram<int> >& histograms)
 
189
{
 
190
    Q_UNUSED(videoFrames);
 
191
    Histogram<float> avgHistogram;
 
192
    for (size_t i = 0; i < histograms.size(); ++i) {
 
193
        for (int j = 0; j < 255; ++j) {
 
194
            avgHistogram.r[j] += static_cast<float>(histograms[i].r[j]) / histograms.size();
 
195
            avgHistogram.g[j] += static_cast<float>(histograms[i].g[j]) / histograms.size();
 
196
            avgHistogram.b[j] += static_cast<float>(histograms[i].b[j]) / histograms.size();
 
197
        }
 
198
    }
 
199
 
 
200
    int bestFrame = -1;
 
201
    float minRMSE = FLT_MAX;
 
202
    for (size_t i = 0; i < histograms.size(); ++i) {
 
203
        //calculate root mean squared error
 
204
        float rmse = 0.0;
 
205
        for (int j = 0; j < 255; ++j) {
 
206
            float error = fabsf(avgHistogram.r[j] - histograms[i].r[j])
 
207
                          + fabsf(avgHistogram.g[j] - histograms[i].g[j])
 
208
                          + fabsf(avgHistogram.b[j] - histograms[i].b[j]);
 
209
            rmse += (error * error) / 255;
 
210
        }
 
211
 
 
212
        rmse = sqrtf(rmse);
 
213
        if (rmse < minRMSE) {
 
214
            minRMSE = rmse;
 
215
            bestFrame = i;
 
216
        }
 
217
    }
 
218
#ifdef DEBUG_MODE
 
219
    cout << "Best frame was: " << bestFrame << "(RMSE: " << minRMSE << ")" << endl;
 
220
#endif
 
221
    return bestFrame;
 
222
}
 
223
 
 
224
}