1 /*  smplayer, GUI front-end for mplayer.
2     Copyright (C) 2006-2021 Ricardo Villalba <ricardo@smplayer.info>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include "videopreview.h"
20 #include "videopreviewconfigdialog.h"
21 #include "playerid.h"
22 #include <QAction>
23 #include <QProcess>
24 #include <QRegExp>
25 #include <QDir>
26 #include <QTime>
27 #include <QProgressDialog>
28 #include <QWidget>
29 #include <QGridLayout>
30 #include <QLabel>
31 #include <QScrollArea>
32 #include <QDialogButtonBox>
33 #include <QPushButton>
34 #include <QPainter>
35 #include <QFileDialog>
36 #include <QMessageBox>
37 #include <QSettings>
38 #include <QApplication>
39 #include <QPixmapCache>
40 #include <QImageWriter>
41 #include <QImageReader>
42 #include <QDebug>
43 
44 #include <cmath>
45 
46 #ifndef NO_SMPLAYER_SUPPORT
47 #include "inforeader.h"
48 #include "images.h"
49 #endif
50 
51 #define RENAME_PICTURES 0
52 
53 #define N_OUTPUT_FRAMES 1
54 
55 // MPlayer2 doesn't support png outdir
56 /* #define VP_USE_PNG_OUTDIR */
57 
58 #define FONT_STYLE "font-family:ubuntu,verdana,arial;"
59 #define HEADER_STYLE "style=\"" FONT_STYLE " font-size:16px\""
60 #define FOOTER_STYLE HEADER_STYLE
61 
VideoPreview(QString mplayer_path,QWidget * parent)62 VideoPreview::VideoPreview(QString mplayer_path, QWidget * parent) : QWidget(parent, Qt::Window)
63 {
64 	setMplayerPath(mplayer_path);
65 
66 	set = 0; // settings
67 	save_last_directory = true;
68 
69 	prop.input_video.clear();
70 	prop.dvd_device.clear();
71 	prop.n_cols = 4;
72 	prop.n_rows = 4;
73 	prop.initial_step = 20;
74 	prop.max_width = 800;
75 	prop.aspect_ratio = 0;
76 	prop.display_osd = true;
77 	prop.extract_format = JPEG;
78 
79 	output_dir = "smplayer_preview";
80 	full_output_dir = QDir::tempPath() +"/"+ output_dir;
81 
82 	progress = new QProgressDialog(parent != 0 ? parent : this);
83 	progress->setMinimumDuration(0);
84 	progress->reset(); // Prevent the dialog to be shown on initialization (Qt 5.5)
85 	connect( progress, SIGNAL(canceled()), this, SLOT(cancelPressed()) );
86 
87 	w_contents = new QWidget(this);
88 	w_contents->setContentsMargins(0, 0, 0, 0);
89 	QPalette p = w_contents->palette();
90 	p.setColor(w_contents->backgroundRole(), Qt::white);
91 	p.setColor(w_contents->foregroundRole(), Qt::black);
92 	w_contents->setPalette(p);
93 
94 	title = new QLabel(this);
95 	title->setWordWrap(true);
96 
97 	info = new QLabel(this);
98 	info->setWordWrap(false);
99 
100 	foot = new QLabel(this);
101 	foot->setAlignment(Qt::AlignRight);
102 
103 	grid_layout = new QGridLayout;
104 	grid_layout->setSpacing(2);
105 	grid_layout->setContentsMargins(0, 0, 0, 0);
106 
107 	QVBoxLayout * l = new QVBoxLayout;
108 	l->setContentsMargins(4, 4, 4, 4);
109 	l->setSpacing(0);
110 	l->setSizeConstraint(QLayout::SetFixedSize);
111 	l->addWidget(title);
112 	l->addWidget(info);
113 	l->addLayout(grid_layout);
114 	l->addWidget(foot);
115 
116 	w_contents->setLayout(l);
117 
118 	scroll_area = new QScrollArea(this);
119 	scroll_area->setWidgetResizable(true);
120 	scroll_area->setAlignment(Qt::AlignCenter);
121 	scroll_area->setWidget( w_contents );
122 
123 	button_box = new QDialogButtonBox(QDialogButtonBox::Close | QDialogButtonBox::Save, Qt::Horizontal, this);
124 	connect( button_box, SIGNAL(rejected()), this, SLOT(close()) );
125 	connect( button_box->button(QDialogButtonBox::Save), SIGNAL(clicked()), this, SLOT(saveImage()) );
126 
127 	button_box->button(QDialogButtonBox::Close)->setDefault(true);
128 	button_box->button(QDialogButtonBox::Close)->setText(tr("&Close"));
129 	button_box->button(QDialogButtonBox::Save)->setText(tr("&Save"));
130 #ifndef NO_SMPLAYER_SUPPORT
131 	button_box->button(QDialogButtonBox::Save)->setIcon(Images::icon("save"));
132 #endif
133 
134 	QVBoxLayout * my_layout = new QVBoxLayout;
135 	my_layout->addWidget(scroll_area);
136 	my_layout->addWidget(button_box);
137 	setLayout(my_layout);
138 
139 	retranslateStrings();
140 
141 	QList<QByteArray> r_formats = QImageReader::supportedImageFormats();
142 	QString read_formats;
143 	for (int n=0; n < r_formats.count(); n++) {
144 		read_formats.append(r_formats[n]+" ");
145 	}
146 	qDebug("VideoPreview::VideoPreview: supported formats for reading: %s", read_formats.toUtf8().constData());
147 
148 	QList<QByteArray> w_formats = QImageWriter::supportedImageFormats();
149 	QString write_formats;
150 	for (int n=0; n < w_formats.count(); n++) {
151 		write_formats.append(w_formats[n]+" ");
152 	}
153 	qDebug("VideoPreview::VideoPreview: supported formats for writing: %s", write_formats.toUtf8().constData());
154 
155 	toggleInfoAct = new QAction(this);
156 	toggleInfoAct->setCheckable(true);
157 	toggleInfoAct->setChecked(true);
158 	toggleInfoAct->setShortcut( QKeySequence("Ctrl+H") );
159 	connect( toggleInfoAct, SIGNAL(toggled(bool)), this, SLOT(showInfo(bool)) );
160 	addAction(toggleInfoAct);
161 }
162 
~VideoPreview()163 VideoPreview::~VideoPreview() {
164 	if (set) saveSettings();
165 }
166 
retranslateStrings()167 void VideoPreview::retranslateStrings() {
168 	progress->setWindowTitle(tr("Thumbnail Generator"));
169 	progress->setCancelButtonText( tr("Cancel") );
170 
171 	foot->setText("<i " FOOTER_STYLE "><b>http://smplayer.info</b></i>");
172 }
173 
setMplayerPath(QString mplayer_path)174 void VideoPreview::setMplayerPath(QString mplayer_path) {
175 	mplayer_bin = mplayer_path;
176 	QFileInfo fi(mplayer_bin);
177 	if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
178 		mplayer_bin = fi.absoluteFilePath();
179 	}
180 
181 	qDebug("VideoPreview::setMplayerPath: mplayer_bin: '%s'", mplayer_bin.toUtf8().constData());
182 }
183 
setSettings(QSettings * settings)184 void VideoPreview::setSettings(QSettings * settings) {
185 	set = settings;
186 	loadSettings();
187 }
188 
clearThumbnails()189 void VideoPreview::clearThumbnails() {
190 	for (int n=0; n < label_list.count(); n++) {
191 		grid_layout->removeWidget( label_list[n] );
192 		delete label_list[n];
193 	}
194 	label_list.clear();
195 	info->clear();
196 }
197 
framePicture()198 QString VideoPreview::framePicture() {
199 	return QString("0000000%1.%2").arg(N_OUTPUT_FRAMES == 1 ? 1 : N_OUTPUT_FRAMES-1).arg(prop.extract_format == PNG ? "png" : "jpg");
200 }
201 
createThumbnails()202 bool VideoPreview::createThumbnails() {
203 	clearThumbnails();
204 	error_message.clear();
205 
206 	button_box->setEnabled(false);
207 
208 	bool result = extractImages();
209 
210 	progress->close();
211 
212 	if ((result == false) && (!error_message.isEmpty())) {
213 		QMessageBox::critical(this, tr("Error"),
214                               tr("The following error has occurred while creating the thumbnails:")+"\n"+ error_message );
215 	}
216 
217 	button_box->setEnabled(true);
218 
219 	// Adjust size
220 	//resize( w_contents->sizeHint() );
221 
222 	cleanDir(full_output_dir);
223 	return result;
224 }
225 
extractImages()226 bool VideoPreview::extractImages() {
227 	VideoInfo i = getInfo(mplayer_bin, prop.input_video);
228 	int length = i.length;
229 
230 	if (length == 0) {
231 		if (error_message.isEmpty()) error_message = tr("The length of the video is 0");
232 		return false;
233 	}
234 
235 	// Create a temporary directory
236 	QDir d(QDir::tempPath());
237 	if (!d.exists(output_dir)) {
238 		if (!d.mkpath(output_dir)) {
239 			qDebug("VideoPreview::extractImages: error: can't create '%s'", full_output_dir.toUtf8().constData());
240 			error_message = tr("The temporary directory (%1) can't be created").arg(full_output_dir);
241 			return false;
242 		}
243 	}
244 
245 	displayVideoInfo(i);
246 
247 	// Let's begin
248 	run.thumbnail_width = 0;
249 
250 	int num_pictures = prop.n_cols * prop.n_rows;
251 	length -= prop.initial_step;
252 	double s_step = (double) length / num_pictures;
253 
254 	double current_time = prop.initial_step;
255 
256 	canceled = false;
257 	progress->setLabelText(tr("Creating thumbnails..."));
258 	progress->setRange(0, num_pictures-1);
259 	progress->show();
260 
261 	double aspect_ratio = i.aspect;
262 	if (prop.aspect_ratio != 0) aspect_ratio = prop.aspect_ratio;
263 
264 	for (int n = 0; n < num_pictures; n++) {
265 		qDebug("VideoPreview::extractImages: getting frame %d of %d...", n+1, num_pictures);
266 		progress->setValue(n);
267 		qApp->processEvents();
268 
269 		if (canceled) return false;
270 
271 		if (!runPlayer(current_time, aspect_ratio)) return false;
272 
273 		QString frame_picture = full_output_dir + "/" + framePicture();
274 		if (!QFile::exists(frame_picture)) {
275 			error_message = tr("The file %1 doesn't exist").arg(frame_picture);
276 			return false;
277 		}
278 
279 #if RENAME_PICTURES
280 		QString extension = (extractFormat()==PNG) ? "png" : "jpg";
281 		QString output_file = output_dir + QString("/picture_%1.%2").arg(current_time, 8, 10, QLatin1Char('0')).arg(extension);
282 		d.rename(output_dir + "/" + framePicture(), output_file);
283 #else
284 		QString output_file = output_dir + "/" + framePicture();
285 #endif
286 
287 		if (!addPicture(QDir::tempPath() +"/"+ output_file, n, current_time)) {
288 			return false;
289 		}
290 
291 		current_time += s_step;
292 	}
293 
294 	return true;
295 }
296 
297 #if defined(Q_OS_UNIX) && !defined(NO_SMPLAYER_SUPPORT)
isOptionAvailableinMPV(const QString & option)298 bool VideoPreview::isOptionAvailableinMPV(const QString & option) {
299 	static QStringList option_list;
300 	static QString executable;
301 
302 	if (option_list.isEmpty() || executable != mplayer_bin) {
303 		InfoReader * ir = InfoReader::obj(mplayer_bin);
304 		ir->getInfo();
305 		option_list = ir->optionList();
306 		executable = mplayer_bin;
307 	}
308 	return option_list.contains(option);
309 }
310 #endif
311 
runPlayer(double seek,double aspect_ratio)312 bool VideoPreview::runPlayer(double seek, double aspect_ratio) {
313 	QStringList args;
314 
315 	if (PlayerID::player(mplayer_bin) == PlayerID::MPV) {
316 		#ifdef MPV_SUPPORT
317 		bool use_new_options = true;
318 		#if defined(Q_OS_UNIX) && !defined(NO_SMPLAYER_SUPPORT)
319 		if (!isOptionAvailableinMPV("--vo-image-format")) use_new_options = false;
320 		#endif
321 
322 		// MPV
323 		args << "--no-config" << "--no-audio" << "--no-cache" << "--hr-seek=yes" << "--sid=no";
324 		args << "--frames=" + QString::number(N_OUTPUT_FRAMES);
325 		args << "--framedrop=no" << "--start=" + QString::number(seek);
326 		if (aspect_ratio != 0) {
327 			args << "--video-aspect=" + QString::number(aspect_ratio);
328 		}
329 		if (!prop.dvd_device.isEmpty()) args << "--dvd-device=" + prop.dvd_device;
330 		if (!use_new_options) {
331 			QString format = (prop.extract_format == PNG) ? "png:png-compression=0" : "jpg";
332 			args << QString("--vo=image=format=%1:outdir=\"%2\"").arg(format).arg(full_output_dir);
333 		} else {
334 			QString format = (prop.extract_format == PNG) ? "png" : "jpg";
335 			args << "--vo-image-format=" + format << "--vo-image-outdir=" + full_output_dir;
336 			args << "--vo=image";
337 			if (prop.extract_format == PNG) args << "--vo-image-png-compression=0";
338 		}
339 
340 		/*
341 		#ifdef Q_OS_WIN
342 		args << "--use-text-osd=no"; // option removed in mpv 0.12
343 		#endif
344 		*/
345 		#endif // MPV_SUPPORT
346 	}
347 	else {
348 		#ifdef MPLAYER_SUPPORT
349 		// MPlayer
350 		args << "-nosound" << "-nocache" << "-noframedrop" << "-noconfig" << "all";
351 
352 		if (prop.extract_format == PNG) {
353 			args << "-vo"
354 			#ifdef VP_USE_PNG_OUTDIR
355 			<< "png:outdir=\""+full_output_dir+"\"";
356 			#else
357 			<< "png";
358 			#endif
359 		} else {
360 			args << "-vo"
361 			<< "jpeg:outdir=\""+full_output_dir+"\"";
362 		}
363 
364 		args << "-frames" << QString::number(N_OUTPUT_FRAMES) << "-ss" << QString::number(seek);
365 
366 		if (aspect_ratio != 0) {
367 			args << "-aspect" << QString::number(aspect_ratio) << "-zoom";
368 		}
369 
370 		if (!prop.dvd_device.isEmpty()) {
371 			args << "-dvd-device" << prop.dvd_device;
372 		}
373 
374 		#ifdef Q_OS_WIN
375 		args << "-nofontconfig";
376 		#endif
377 
378 		/*
379 		if (display_osd) {
380 			args << "-vf" << "expand=osd=1" << "-osdlevel" << "2";
381 		}
382 		*/
383 		#endif // MPLAYER_SUPPORT
384 	}
385 
386 	args << prop.input_video;
387 
388 	QString command = mplayer_bin + " " + args.join(" ");
389 	qDebug() << "VideoPreview::runMplayer: command:" << command;
390 
391 	QProcess p;
392 	#ifndef VP_USE_PNG_OUTDIR
393 	p.setWorkingDirectory(full_output_dir);
394 	#endif
395 	p.start(mplayer_bin, args);
396 
397 	if (!p.waitForStarted()) {
398 		qDebug("VideoPreview::runMplayer: error: the process didn't start");
399 	}
400 
401 	if (!p.waitForFinished()) {
402 		qDebug("VideoPreview::runMplayer: error running process");
403 		error_message = tr("The mplayer process didn't run");
404 		return false;
405 	}
406 
407 	//qDebug() << "VideoPreview::runMplayer: process output:" << p.readAll();
408 
409 	return true;
410 }
411 
412 
addPicture(const QString & filename,int num,int time)413 bool VideoPreview::addPicture(const QString & filename, int num, int time) {
414 	int row = num / prop.n_cols;
415 	int col = num % prop.n_cols;
416 
417 	qDebug("VideoPreview::addPicture: %d (row: %d col: %d) file: '%s'", num, row, col, filename.toUtf8().constData());
418 
419 	QPixmapCache::clear();
420 	QPixmap picture;
421 	if (!picture.load(filename)) {
422 		qDebug("VideoPreview::addPicture: can't load file");
423 		error_message = tr("The file %1 can't be loaded").arg(filename);
424 		return false;
425 	}
426 
427 	if (run.thumbnail_width == 0) {
428 		qDebug("VideoPreview::addPicture: horizontalSpacing: %d", grid_layout->horizontalSpacing());
429 		int spacing = grid_layout->horizontalSpacing() * (prop.n_cols-1);
430 		QMargins m = w_contents->layout()->contentsMargins();
431 		qDebug("VideoPreview::addPicture: contentsMargins: %d, %d", m.left(), m.right());
432 		spacing += (m.left() + m.right());
433 		if (spacing < 0) spacing = 0;
434 		qDebug("VideoPreview::addPicture: spacing: %d", spacing);
435 		run.thumbnail_width = (prop.max_width - spacing) / prop.n_cols;
436 		if (run.thumbnail_width > picture.width()) run.thumbnail_width = picture.width();
437 		qDebug("VideoPreview::addPicture: thumbnail_width set to %d", run.thumbnail_width);
438 	}
439 
440 	QPixmap scaled_picture = picture.scaledToWidth(run.thumbnail_width, Qt::SmoothTransformation);
441 
442 	// Add current time text
443 	if (prop.display_osd) {
444 		QTime t(0,0);
445 		QString stime = t.addSecs(time).toString("hh:mm:ss");
446 		//qDebug() << "VideoPreview::addPicture: stime:" << stime << "time:" << time;
447 		QFont font;
448 		font.setPointSize(12);
449 		font.setBold(true);
450 		QPainter painter(&scaled_picture);
451 		painter.setPen(Qt::white);
452 		painter.setFont(font);
453 		//painter.drawText(scaled_picture.rect(), Qt::AlignRight | Qt::AlignBottom, stime);
454 
455 		// Set background
456 		QFontMetrics fm(font);
457 		#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0))
458 		int px_wide = fm.horizontalAdvance(stime);
459 		#else
460 		int px_wide = fm.width(stime);
461 		#endif
462 		int px_high = fm.height();
463 		QRect rect(scaled_picture.rect());
464 		rect.setX(rect.width() - px_wide);
465 		rect.setY(rect.height() - px_high);
466 		rect.setWidth(px_wide);
467 		rect.setHeight(px_high);
468 		painter.fillRect(rect, QBrush(QColor(0, 0, 0, 128)));
469 
470 		// Draw text
471 		painter.drawText(rect, Qt::AlignCenter, stime);
472 	}
473 
474 	QLabel * l = new QLabel(this);
475 	label_list.append(l);
476 	l->setPixmap(scaled_picture);
477 	//l->setPixmap(picture);
478 	grid_layout->addWidget(l, row, col);
479 
480 	return true;
481 }
482 
483 #define ADD_INFO( data) \
484 	{ \
485 	text += data; \
486 	if (count++ % 3 == 0) text += "</td><td>"; else text += "<br>"; \
487 	}
488 
displayVideoInfo(const VideoInfo & i)489 void VideoPreview::displayVideoInfo(const VideoInfo & i) {
490 	// Display info about the video
491 	QTime t(0,0);
492 	t = t.addSecs(i.length);
493 
494 	QString aspect = QString::number(i.aspect);
495 	if (fabs(1.77 - i.aspect) < 0.1) aspect = "16:9";
496 	else
497 	if (fabs(1.33 - i.aspect) < 0.1) aspect = "4:3";
498 	else
499 	if (fabs(2.35 - i.aspect) < 0.1) aspect = "2.35:1";
500 
501 	QString no_info = tr("No info");
502 
503 	QString fps = (i.fps==0 || i.fps==1000) ? no_info : QString("%1").arg(i.fps);
504 	QString video_bitrate = (i.video_bitrate==0) ? no_info : tr("%1 kbps").arg(i.video_bitrate/1000);
505 	QString audio_bitrate = (i.audio_bitrate==0) ? no_info : tr("%1 kbps").arg(i.audio_bitrate/1000);
506 	QString audio_rate = (i.audio_rate==0) ? no_info : tr("%1 Hz").arg(i.audio_rate);
507 
508 	title->setText("<h2 " FONT_STYLE ">" + i.filename + "</h2>");
509 
510 	int count = 1;
511 
512 	QString text =
513 		"<table cellspacing=4 cellpadding=4 " HEADER_STYLE ">"
514 		"<tr><td>";
515 
516 	ADD_INFO(tr("Size: %1 MB").arg(i.size / (1024*1024)));
517 	ADD_INFO(tr("Resolution: %1x%2").arg(i.width).arg(i.height));
518 	ADD_INFO(tr("Length: %1").arg(t.toString("hh:mm:ss")));
519 
520 	if (i.fps && i.fps != 1000) ADD_INFO(tr("FPS: %1").arg(fps));
521 	if (!i.video_format.isEmpty()) ADD_INFO(tr("Video format: %1").arg(i.video_format));
522 	if (!i.audio_format.isEmpty()) ADD_INFO(tr("Audio format: %1").arg(i.audio_format));
523 
524 	ADD_INFO(tr("Aspect ratio: %1").arg(aspect));
525 
526 	if (i.video_bitrate) ADD_INFO(tr("Video bitrate: %1").arg(video_bitrate));
527 	if (i.audio_bitrate) ADD_INFO(tr("Audio bitrate: %1").arg(audio_bitrate));
528 	if (i.audio_rate) ADD_INFO(tr("Audio rate: %1").arg(audio_rate));
529 
530 	text += "</td></tr></table>";
531 
532 	//qDebug() << "VideoPreview::displayVideoInfo: text:" << text;
533 
534 	info->setText(text);
535 	setWindowTitle( tr("Video preview") + " - " + i.filename );
536 }
537 
cleanDir(QString directory)538 void VideoPreview::cleanDir(QString directory) {
539 	QStringList filter;
540 	if (prop.extract_format == PNG) {
541 		filter.append("*.png");
542 	} else {
543 		filter.append("*.jpg");
544 	}
545 
546 	QDir d(directory);
547 	QStringList l = d.entryList( filter, QDir::Files, QDir::Unsorted);
548 
549 	for (int n = 0; n < l.count(); n++) {
550 		qDebug("VideoPreview::cleanDir: deleting '%s'", l[n].toUtf8().constData());
551 		d.remove(l[n]);
552 	}
553 	qDebug("VideoPreview::cleanDir: removing directory '%s'", directory.toUtf8().constData());
554 	d.rmpath(directory);
555 }
556 
getInfo(const QString & mplayer_path,const QString & filename)557 VideoInfo VideoPreview::getInfo(const QString & mplayer_path, const QString & filename) {
558 	VideoInfo i;
559 
560 	if (filename.isEmpty()) {
561 		error_message = tr("No filename");
562 		return i;
563 	}
564 
565 	QFileInfo fi(filename);
566 	if (fi.exists()) {
567 		i.filename = fi.fileName();
568 		i.size = fi.size();
569 	}
570 
571 	QRegExp rx("^ID_(.*)=(.*)");
572 
573 	QProcess p;
574 	p.setProcessChannelMode( QProcess::MergedChannels );
575 
576 	QStringList args;
577 
578 	if (PlayerID::player(mplayer_path) == PlayerID::MPV) {
579 		#ifdef MPV_SUPPORT
580 		// MPV
581 		args << "--term-playing-msg="
582                 "ID_LENGTH=${=duration:${=length}}\n"
583                 "ID_VIDEO_WIDTH=${=width}\n"
584                 "ID_VIDEO_HEIGHT=${=height}\n"
585                 "ID_VIDEO_FPS=${=container-fps:${=fps}}\n"
586                 "ID_VIDEO_ASPECT=${=video-aspect}\n"
587                 "ID_VIDEO_BITRATE=${=video-bitrate}\n"
588                 "ID_AUDIO_BITRATE=${=audio-bitrate}\n"
589                 "ID_AUDIO_RATE=${audio-params/samplerate:${=audio-samplerate}}\n"
590                 "ID_VIDEO_FORMAT=${video-format}\n"
591                 "ID_AUDIO_FORMAT=${audio-codec-name}\n";
592 
593 		args << "--vo=null" << "-ao=null" << "--frames=1" << "--no-quiet" << "--no-cache" << "--no-config";
594 		if (!prop.dvd_device.isEmpty()) args << "--dvd-device=" + prop.dvd_device;
595 		args << filename;
596 		#endif // MPV_SUPPORT
597 	}
598 	else {
599 		#ifdef MPLAYER_SUPPORT
600 		// MPlayer
601 		args << "-vo" << "null" << "-ao" << "null" << "-frames" << "1" << "-identify" << "-nocache" << "-noquiet" << "-noconfig" << "all";
602 		if (!prop.dvd_device.isEmpty()) args << "-dvd-device" << prop.dvd_device;
603 
604 		#ifdef Q_OS_WIN
605 		args << "-nofontconfig";
606 		#endif
607 
608 		args << filename;
609 		#endif // MPLAYER_SUPPORT
610 	}
611 
612 	QString command = mplayer_path + " " + args.join(" ").replace("\n", "\\n");//.replace("$", "\\$");
613 	qDebug() << "VideoPreview::getInfo: command:" << command;
614 
615 	p.start(mplayer_path, args);
616 
617 	if (p.waitForFinished()) {
618 		QByteArray line;
619 		while (p.canReadLine()) {
620 			line = p.readLine().trimmed();
621 			qDebug("VideoPreview::getInfo: '%s'", line.constData());
622 			if (rx.indexIn(line) > -1) {
623 				QString tag = rx.cap(1);
624 				QString value = rx.cap(2);
625 				qDebug("VideoPreview::getInfo: tag: '%s', value: '%s'", tag.toUtf8().constData(), value.toUtf8().constData());
626 
627 				if (tag == "LENGTH") i.length = (int) value.toDouble();
628 				else
629 				if (tag == "VIDEO_WIDTH") i.width = value.toInt();
630 				else
631 				if (tag == "VIDEO_HEIGHT") i.height = value.toInt();
632 				else
633 				if (tag == "VIDEO_FPS") i.fps = value.toDouble();
634 				else
635 				if (tag == "VIDEO_ASPECT") {
636 					i.aspect = value.toDouble();
637 					if ((i.aspect == 0) && (i.width != 0) && (i.height != 0)) {
638 						i.aspect = (double) i.width / i.height;
639 					}
640 				}
641 				else
642 				if (tag == "VIDEO_BITRATE") i.video_bitrate = value.toInt();
643 				else
644 				if (tag == "AUDIO_BITRATE") i.audio_bitrate = value.toInt();
645 				else
646 				if (tag == "AUDIO_RATE") i.audio_rate = value.toInt();
647 				else
648 				if (tag == "VIDEO_FORMAT") i.video_format = value;
649 				else
650 				if (tag == "AUDIO_FORMAT") i.audio_format = value;
651 			}
652 		}
653 	} else {
654 		qDebug("VideoPreview::getInfo: error: process didn't start");
655 		error_message = tr("The mplayer process didn't start while trying to get info about the video");
656 	}
657 
658 	qDebug("VideoPreview::getInfo: filename: '%s'", i.filename.toUtf8().constData());
659 	qDebug("VideoPreview::getInfo: resolution: '%d x %d'", i.width, i.height);
660 	qDebug("VideoPreview::getInfo: length: '%d'", i.length);
661 	qDebug("VideoPreview::getInfo: size: '%d'", (int) i.size);
662 
663 	return i;
664 }
665 
showInfo(bool visible)666 void VideoPreview::showInfo(bool visible) {
667 	qDebug("VideoPreview::showInfo: %d", visible);
668 	info->setVisible(visible);
669 	foot->setVisible(visible);
670 }
671 
saveImage()672 void VideoPreview::saveImage() {
673 	qDebug("VideoPreview::saveImage");
674 
675 	// Proposed name
676 	QString proposed_name = "";
677 	if (save_last_directory) proposed_name = last_directory;
678 
679 	QFileInfo fi(prop.input_video);
680 	if (fi.exists()) {
681 		if (!save_last_directory) proposed_name = fi.absolutePath();
682 		QString extension = (extractFormat()==PNG) ? "png" : "jpg";
683 		proposed_name += "/"+ fi.completeBaseName() +"_preview."+ extension;
684 	}
685 
686 	// Formats
687 	QList<QByteArray> w_formats = QImageWriter::supportedImageFormats();
688 	QString write_formats;
689 	for (int n=0; n < w_formats.count(); n++) {
690 		write_formats.append("*."+w_formats[n]+" ");
691 	}
692 	if (write_formats.isEmpty()) {
693 		// Shouldn't happen!
694 		write_formats = "*.png *.jpg";
695 	}
696 
697 	QString filename = QFileDialog::getSaveFileName(this, tr("Save file"),
698                             proposed_name, tr("Images") +" ("+ write_formats +")");
699 
700 	if (!filename.isEmpty()) {
701 		#if QT_VERSION >= 0x050000
702 		QPixmap image = w_contents->grab();
703 		#else
704 		QPixmap image = QPixmap::grabWidget(w_contents);
705 		#endif
706 		qDebug("VideoPreview::saveImage: size: %d %d", image.size().width(), image.size().height());
707 		if (image.size().width() > prop.max_width) {
708 			image = image.scaledToWidth(prop.max_width, Qt::SmoothTransformation);
709 			qDebug("VideoPreview::saveImage: image scaled to : %d %d", image.size().width(), image.size().height());
710 		}
711 		if (!image.save(filename)) {
712 			// Failed!!!
713 			qDebug("VideoPreview::saveImage: error saving '%s'", filename.toUtf8().constData());
714 			QMessageBox::warning(this, tr("Error saving file"),
715                                  tr("The file couldn't be saved") );
716 		} else {
717 			last_directory = QFileInfo(filename).absolutePath();
718 		}
719 	}
720 }
721 
showConfigDialog(QWidget * parent)722 bool VideoPreview::showConfigDialog(QWidget * parent) {
723 	VideoPreviewConfigDialog d(parent);
724 
725 	d.setVideoFile( videoFile() );
726 	d.setDVDDevice( DVDDevice() );
727 	d.setCols( cols() );
728 	d.setRows( rows() );
729 	d.setInitialStep( initialStep() );
730 	d.setMaxWidth( maxWidth() );
731 	d.setDisplayOSD( displayOSD() );
732 	d.setAspectRatio( aspectRatio() );
733 	d.setFormat( extractFormat() );
734 	d.setSaveLastDirectory( save_last_directory );
735 
736 	if (d.exec() == QDialog::Accepted) {
737 		setVideoFile( d.videoFile() );
738 		setDVDDevice( d.DVDDevice() );
739 		setCols( d.cols() );
740 		setRows( d.rows() );
741 		setInitialStep( d.initialStep() );
742 		setMaxWidth( d.maxWidth() );
743 		setDisplayOSD( d.displayOSD() );
744 		setAspectRatio( d.aspectRatio() );
745 		setExtractFormat(d.format() );
746 		save_last_directory = d.saveLastDirectory();
747 
748 		return true;
749 	}
750 
751 	return false;
752 }
753 
saveSettings()754 void VideoPreview::saveSettings() {
755 	qDebug("VideoPreview::saveSettings");
756 
757 	set->beginGroup("videopreview");
758 
759 	set->setValue("columns", cols());
760 	set->setValue("rows", rows());
761 	set->setValue("initial_step", initialStep());
762 	set->setValue("max_width", maxWidth());
763 	set->setValue("osd", displayOSD());
764 	set->setValue("format", extractFormat());
765 	set->setValue("save_last_directory", save_last_directory);
766 
767 	if (save_last_directory) {
768 		set->setValue("last_directory", last_directory);
769 	}
770 
771 	set->setValue("filename", videoFile());
772 	set->setValue("dvd_device", DVDDevice());
773 
774 	set->setValue("show_info", toggleInfoAct->isChecked());
775 
776 	set->endGroup();
777 }
778 
loadSettings()779 void VideoPreview::loadSettings() {
780 	qDebug("VideoPreview::loadSettings");
781 
782 	set->beginGroup("videopreview");
783 
784 	setCols( set->value("columns", cols()).toInt() );
785 	setRows( set->value("rows", rows()).toInt() );
786 	setInitialStep( set->value("initial_step", initialStep()).toInt() );
787 	setMaxWidth( set->value("max_width", maxWidth()).toInt() );
788 	setDisplayOSD( set->value("osd", displayOSD()).toBool() );
789 	setExtractFormat( (ExtractFormat) set->value("format", extractFormat()).toInt() );
790 	save_last_directory = set->value("save_last_directory", save_last_directory).toBool();
791 	last_directory = set->value("last_directory", last_directory).toString();
792 
793 	setVideoFile( set->value("filename", videoFile()).toString() );
794 	setDVDDevice( set->value("dvd_device", DVDDevice()).toString() );
795 
796 	toggleInfoAct->setChecked(set->value("show_info", true).toBool());
797 
798 	set->endGroup();
799 }
800 
adjustWindowSize()801 void VideoPreview::adjustWindowSize() {
802 	qDebug("VideoPreview::adjustWindowSize: window size: %d %d", width(), height());
803 	qDebug("VideoPreview::adjustWindowSize: scroll_area size: %d %d", scroll_area->width(), scroll_area->height());
804 
805 	int diff_width = width() - scroll_area->maximumViewportSize().width();
806 	int diff_height = height() - scroll_area->maximumViewportSize().height();
807 
808 	qDebug("VideoPreview::adjustWindowSize: diff_width: %d diff_height: %d", diff_width, diff_height);
809 
810 	QSize new_size = w_contents->size() + QSize( diff_width, diff_height);
811 
812 	qDebug("VideoPreview::adjustWindowSize: new_size: %d %d", new_size.width(), new_size.height());
813 
814 	resize(new_size);
815 }
816 
cancelPressed()817 void VideoPreview::cancelPressed() {
818 	canceled = true;
819 }
820 
821 // Language change stuff
changeEvent(QEvent * e)822 void VideoPreview::changeEvent(QEvent *e) {
823 	if (e->type() == QEvent::LanguageChange) {
824 		retranslateStrings();
825 	} else {
826 		QWidget::changeEvent(e);
827 	}
828 }
829 
830 #include "moc_videopreview.cpp"
831 
832