/*****************************************************************************
 *
 *  playerwidget.cpp -- Container for player controls.
 *
 *  Copyright (C) 2010 Starbuck <starbuck@labdv.com>
 *  All rights reserved.
 *
 *  This file is part of the Gui4 module of the DVDx 4 application.
 *
 *  GNU Lesser General Public License Usage
 *  This file may be used under the terms of the GNU Lesser General
 *  Public License version 2.1 as published by the Free Software
 *  Foundation and appearing in the file LICENSE.LGPL included in the
 *  packaging of this file.  Please review the following information to
 *  ensure the GNU Lesser General Public License version 2.1 requirements
 *  will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 *
 *  GNU General Public License Usage
 *  Alternatively, this file may be used under the terms of the GNU
 *  General Public License version 3.0 as published by the Free Software
 *  Foundation and appearing in the file LICENSE.GPL included in the
 *  packaging of this file.  Please review the following information to
 *  ensure the GNU General Public License version 3.0 requirements will be
 *  met: http://www.gnu.org/copyleft/gpl.html.
 *
 *****************************************************************************/

#include "../video/video_struct.h"
#include "../video/video_constants.h"
#include "../video/video_functions.h"
#include "../player/player.h"
#include "playerwidget.h"
#include "infoboxdialog.h"
#include "macros.h"
#include "widgetbridge.h"
#include <QApplication>
#include <QFileInfo>
#include <QVBoxLayout>
#include <QLayoutItem>
#include <QPaintEvent>
#include <QPainter>
#include <QColor>
#include <QRect>
#include <QLinearGradient>
#include <QSizePolicy>
#include <QCloseEvent>
#include <QString>
#include <QTime>
#include <QFont>
#include <QDebug>
#include <QCoreApplication>
#include <QStackedLayout>
#include <QDesktopWidget>

PlayerWidget::PlayerWidget(QWidget *refConsole, const QString &binaryPath, 
			const QString &mediaInfoPath, const QString &ffprobePath,
		QTextEdit *stdIn, QTextEdit *stdOut, QTextEdit *stdErr, 
		QTextEdit *defLog, const int verbosityLevel, Preferences *prefs, QWidget *parent)
	: QWidget(parent), 
	autoZoom(false), zoom(1.0), media(Video::Media::File),
	title(0), chapter(0),audio(-1), subtitle(-1), angle(-1),
	titles(0), chapters(0),audios(0), subtitles(0), angles(0),
	runPlaying(false), runNormalSpeed(true),
	noChoiceInList(true), playMute(false), playPause(false), progressBar(0)
{
	mainConsole = refConsole;  // reference for centering/sizing/moving child windows

	binPath = binaryPath;
	mediainfoBinPath = mediaInfoPath;
	ffprobeBinPath = ffprobePath;

	if ( prefs != 0 ) preferences = *prefs;

	// Set IO channels from parent channels
	standardInput = stdIn;
	standardOutput = stdOut;
	standardError = stdErr;
	log = defLog;
	verbosity = verbosityLevel;

	// Count playback errors
	playbackErrors = new QList<QString>;

	// Default render size
	scaledResolutionReference = QSize(preferences.auto_zoom_max_width, preferences.auto_zoom_max_height);
	scaledResolution = scaledResolutionReference * zoom;

	// Control widgets
	playIcon = new QIcon(":/gui4/images/play.png");
	playExcerptIcon = new QIcon(":/gui4/images/play_excerpt.png");
	playFastIcon = new QIcon(":/gui4/images/play_fast.png");
	playExcerptFastIcon = new QIcon(":/gui4/images/play_excerpt_fast.png");
	stopIcon = new QIcon(":/gui4/images/stop.png");
	stopExcerptIcon = new QIcon(":/gui4/images/stop_excerpt.png");
	slowIcon = new QIcon(":/gui4/images/slow.png");
	slowExcerptIcon = new QIcon(":/gui4/images/slow_excerpt.png");
	fastIcon = new QIcon(":/gui4/images/fast.png");
	fastExcerptIcon = new QIcon(":/gui4/images/fast_excerpt.png");

	controller = new QPushButton;
	controller->setToolTip(tr("Play original source movie"));
	controller->setIcon(*playIcon);
	controller->setFlat(true);

	pauseButton = new QPushButton(tr("Pause"));
	pauseButton->hide();
#ifdef WIN32
#pragma message (ToBeDone "hide pause button while not working - " __FUNCTION__)
#endif

	slowerButton = new QPushButton;
	slowerButton->setToolTip(tr("Slower"));
	slowerButton->setIcon(*slowIcon);
	slowerButton->setFlat(true);

	fasterButton = new QPushButton;
	fasterButton->setToolTip(tr("Faster"));
	fasterButton->setIcon(*fastIcon);
	fasterButton->setFlat(true);

	prevChapterButton = new QPushButton;
	prevChapterButton->setToolTip(tr("Previous chapter"));
	prevChapterButton->setIcon(QIcon(":/gui4/images/prev_chapt.png"));
	prevChapterButton->setFlat(true);

	nextChapterButton = new QPushButton;
	nextChapterButton->setToolTip(tr("Next chapter"));
	nextChapterButton->setIcon(QIcon(":/gui4/images/next_chapt.png"));
	nextChapterButton->setFlat(true);

	prevButton = new QPushButton;
	prevButton->setToolTip(tr("Backward"));
	prevButton->setIcon(QIcon(":/gui4/images/prev.png"));
	prevButton->setFlat(true);

	nextButton = new QPushButton;
	nextButton->setToolTip(tr("Forward"));
	nextButton->setIcon(QIcon(":/gui4/images/next.png"));
	nextButton->setFlat(true);

	// Video renderer
	renderTV = new QFrame(this);	
	renderTV->setFrameShape(QFrame::StyledPanel);
	renderTV->setMinimumSize( scaledResolution );

	renderTarget = new QFrame(renderTV);
#ifdef Q_OS_MAC
	// On Mac, mplayer open its own window
	renderTV->setVisible(false);
#else
	renderTarget->setAutoFillBackground(true);
	renderTarget->setFrameShape(QFrame::Box);
	renderTarget->setFrameShadow(QFrame::Plain);
	renderTarget->setLineWidth(0);
	// The renderer is fixed size, scaling size is not managed by MPlayer, so we are
	renderTarget->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
	renderTarget->setAttribute(Qt::WA_PaintOnScreen);
#endif
	QPalette palette;
	palette.setColor(QPalette::Background, Qt::black);
	renderTarget->setPalette(palette);
	renderBackground = new QLabel(tr("Video bug"), renderTarget);
	renderTarget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
	renderBackground->setAlignment(Qt::AlignHCenter|Qt::AlignVCenter);
	
	audioPixmap = new QPixmap(":/gui4/images/audio.png");
	renderBackground->setAutoFillBackground(true);
	renderBackground->setPalette(palette);
	renderBackground->setFixedSize(renderTarget->size());
	if ( renderBackground->width() < audioPixmap->width() 
		|| renderBackground->height() < audioPixmap->height() )
	{
		renderBackground->setPixmap( audioPixmap->scaled(renderBackground->size(), Qt::KeepAspectRatio) );
	}
	else
	{
		renderBackground->setPixmap( *audioPixmap );
	}
	renderBackground->setVisible(false);

	// Video timeline
	timeLine = new QSlider(Qt::Horizontal);
	timeLine->setTracking(false);
	timeLine->setEnabled(false);
	timeLine->hide();
#ifdef WIN32
#pragma message (ToBeDone "hide timeLine while bugging on user action - " __FUNCTION__)
#endif

	// Chapter display
	chapterDisplay = new QLabel(this);
	chapterDisplay->setEnabled(false);
	chapterDisplay->setToolTip(tr("Chapter"));
	chapterDisplay->setText("_");

	QFont font(chapterDisplay->font());
	font.setPointSize(8);
	font.setWeight(QFont::Black);
	chapterDisplay->setFont(font);

	QVBoxLayout *chapterDisplayLayout = new QVBoxLayout;
	chapterDisplayLayout->addStretch();
	chapterDisplayLayout->addWidget(chapterDisplay);
	chapterDisplayLayout->addSpacing(2);

	// Timecode display
	timecodeDisplay = new TimecodeWidget(this);
	timecodeDisplay->setEnabled(false);

	QVBoxLayout *timecodeDisplayLayout = new QVBoxLayout;
	timecodeDisplayLayout->addStretch();
	timecodeDisplayLayout->addWidget(timecodeDisplay);

	// DVD selection controls
	trackGroup = new QGroupBox(tr("Track selection"), this);
	trackGroup->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
	titleList = new QComboBox();
	audioList = new QComboBox();
	subtitleList = new QComboBox();
	angleList = new QComboBox();

	// Control layouts
#ifdef Q_OS_MAC
	int buttonWidth = QImage(":/gui4/images/play.png").width();
	//pauseButton->setFixedWidth(buttonWidth);
	prevChapterButton->setFixedWidth(buttonWidth);
	prevButton->setFixedWidth(buttonWidth);
	slowerButton->setFixedWidth(buttonWidth);
	controller->setFixedWidth(buttonWidth);
	fasterButton->setFixedWidth(buttonWidth);
	nextButton->setFixedWidth(buttonWidth);
	nextChapterButton->setFixedWidth(buttonWidth);
#endif
	QHBoxLayout *buttons = new QHBoxLayout;
	buttons->setAlignment( Qt::AlignLeft | Qt::AlignTop );
	//buttons->addWidget(pauseButton);
	buttons->addWidget(prevChapterButton);
	buttons->addWidget(prevButton);
	buttons->addWidget(slowerButton);
	buttons->addWidget(controller);
	buttons->addWidget(fasterButton);
	buttons->addWidget(nextButton);
	buttons->addWidget(nextChapterButton);
#ifdef Q_OS_MAC
	buttons->addSpacing(buttonWidth * 4);
#endif
	buttons->addStretch();
	buttons->addItem(chapterDisplayLayout);
	buttons->addItem(timecodeDisplayLayout);

	QHBoxLayout *selection = new QHBoxLayout;
	selection->addWidget(titleList);
	selection->addWidget(audioList);
	selection->addWidget(subtitleList);
	/*experimental
	selection->addWidget(angleList);
	*/
	trackGroup->setLayout(selection);

	QVBoxLayout *layout = new QVBoxLayout;
	layout->addWidget(renderTV);
	layout->addWidget(timeLine);
	layout->addLayout(buttons);
	layout->addWidget(trackGroup);

	setLayout(layout);

	// Processing stuff
	playerProcess = new QProcess(this);
	poller = new QTimer(this);

	player = new Player(Video::Player::MPlayer, binaryPath, mediainfoBinPath, ffprobeBinPath,
		stdIn, stdOut, stdErr, defLog, verbosity,
		preferences.latency_start, preferences.latency_finished, preferences.latency_kill);
#if defined( WIN32 )
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	// Reinterpret_cast<qlonglong> is mandatory, winId() not kind enough when being converted ;)
	player->setWid(QString::number(reinterpret_cast<qlonglong>(renderTarget->winId())));
	// OpenGL video driver doesn't require Windows to switch color space
	if (!player->setPreferredVideoDriver("gl2")) player->setPreferredVideoDriver("gl");
#elif defined( Q_OS_MAC )
	// MPlayer displays in its on window on Mac
	// On Mac OS X, the type returned depends on which framework Qt was linked against. 
	// If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, 
	// {WId} is a pointer to an NSView.
	//*experimental
	player->setPreferredVideoDriver("corevideo");
	// May work on Mac if renderTarget is a QWidget (see Phonon WidgetNoPaintEvent)
	// player.setWid(QString::number(renderTarget->winId()));
#else // UNIX and !MAC
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	player->setWid(QString::number(renderTarget->winId()));
	if (!player->setPreferredVideoDriver("xv"))
		if (!player->setPreferredVideoDriver("x11"))
			if (!player->setPreferredVideoDriver("gl2")) player->setPreferredVideoDriver("gl");
	// xv has better performance but have issues on Ubuntu 11.10
	//if (!player->setPreferredVideoDriver("x11"))
	//	if (!player->setPreferredVideoDriver("gl2"))
	//		if (!player->setPreferredVideoDriver("gl")) player->setPreferredVideoDriver("xv");
#endif
	playerCapability = player->getCapability();		
	playerStatus = player->getStatus();	
	mediaInfoStatus = player->getMediaInfoStatus();	
	mediaInfoData = player->getMediaInfoData();	
	ffprobeStatus = player->getFFprobeStatus();	
	ffprobeData = player->getFFprobeData();	

	// Invisible state controls (keep other controls up-to-date thanks to signals/events)
	isMovieLoaded = new QCheckBox();
	isMovieLoaded->setHidden(true);
	isMovieLoaded->setTristate(true);
#ifdef WIN32
#pragma message (Reminder "state widget sounds to work event not in a layout - " __FUNCTION__)
#endif

	// Slots/signals
	connect(controller, SIGNAL(clicked()), this, SLOT(switchState()));
	connect(pauseButton, SIGNAL(clicked()), this, SLOT(switchPause()));
	connect(fasterButton, SIGNAL(clicked()), this, SLOT(switchFaster()));
	connect(slowerButton, SIGNAL(clicked()), this, SLOT(switchSlower()));
	connect(prevChapterButton, SIGNAL(clicked()), this, SLOT(prevChapter()));
	connect(nextChapterButton, SIGNAL(clicked()), this, SLOT(nextChapter()));
	connect(prevButton, SIGNAL(clicked()), this, SLOT(prev()));
	connect(nextButton, SIGNAL(clicked()), this, SLOT(next()));

	connect(titleList, SIGNAL(activated(int)), this, SLOT(selectTitleByUser(int)));
	connect(titleList, SIGNAL(currentIndexChanged(int)), this, SLOT(selectTitle(int)));
	connect(audioList, SIGNAL(currentIndexChanged(int)), this, SLOT(selectAudio(int)));
	connect(subtitleList, SIGNAL(currentIndexChanged(int)), this, SLOT(selectSubtitle(int)));
	connect(angleList, SIGNAL(currentIndexChanged(int)), this, SLOT(selectAngle(int)));

	connect(playerProcess, SIGNAL(readyReadStandardOutput()), 
		this, SLOT(readStandardOutput()));
	connect(playerProcess, SIGNAL(readyReadStandardError()), 
		this, SLOT(readStandardError()));
	connect(playerProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
		this, SLOT(playerEnded(int, QProcess::ExitStatus)));
	connect(poller, SIGNAL(timeout()), this, SLOT(pollCurrentTime()));
	connect(poller, SIGNAL(timeout()), this, SLOT(monitorPlaybackErrors()));
	connect(timeLine, SIGNAL(sliderMoved(int)), this, SLOT(timeLineChanged(int)));

	connect(isMovieLoaded, SIGNAL(stateChanged(int)), this, SLOT(updateControlsOnLoading(int)));
	connect(isMovieLoaded, SIGNAL(stateChanged(int)), this, SLOT(centerRenderer()));
	
	connect(this, SIGNAL(selectionChanged()), this, SLOT(setListToolTips()));

	// Widget brige connections
	connect(WidgetBridge::getIt(), SIGNAL(sendMessage(QString, bool)), this, SLOT(processBridgeMessage(QString, bool)));
	connect(WidgetBridge::getIt(), SIGNAL(sendMessage(QString, int)), this, SLOT(processBridgeMessage(QString, int)));

	// Set controls state
	decorateController();
	isMovieLoaded->setChecked(false);  // NotLoaded
	updateControlsOnLoading(isMovieLoaded->checkState());  // Force event to update controls
}

PlayerWidget::~PlayerWidget()
{
	stop();  // ends QProcess properly

	Q_CHECK_PTR(playbackErrors);
	delete(playbackErrors);
}

void PlayerWidget::getStatus(bool *mplayerReady, bool *mediainfoReady, bool *ffprobeReady)
{
	*mplayerReady = playerStatus->state == Video::Status::EngineOk;
	*mediainfoReady = mediaInfoStatus->state == Video::Status::EngineOk;
	*ffprobeReady = ffprobeStatus->state == Video::Status::EngineOk;
}

void PlayerWidget::processBridgeMessage(const QString &message, const bool data)
{
	if ( message == MSG_CHANGETESTMODE )
	{
		preferences.test_mode = data;
		decorateController();
	}
}

void PlayerWidget::processBridgeMessage(const QString &message, const int data)
{
	if ( message == MSG_CHANGEVOLUME )
	{
		preferences.volume_correction = data;
	}
}

void PlayerWidget::decorateController()
{
	if ( preferences.test_mode )
	{
		controller->setIcon(*playExcerptIcon);
		controller->setToolTip(tr("Play source movie excerpt for encoding preview"));

		slowerButton->setIcon(*slowExcerptIcon);
		fasterButton->setIcon(*fastExcerptIcon);
	}
	else
	{
		controller->setIcon(*playIcon);
		controller->setToolTip(tr("Play original source movie"));

		slowerButton->setIcon(*slowIcon);
		fasterButton->setIcon(*fastIcon);
	}
}

void PlayerWidget::setPreferences(Preferences *prefs)
{
	preferences = *prefs;

	player->setLatency( preferences.latency_start, preferences.latency_finished, preferences.latency_kill );
}

void PlayerWidget::resetPreferences()
{
	preferences.reset();

	player->setLatency( preferences.latency_start, preferences.latency_finished, preferences.latency_kill );
}

bool PlayerWidget::isReady(const bool refresh) 
{
	if (!refresh)
	{
		return playerStatus->state == Video::Status::EngineOk;
	}

	if (playerStatus->state != Video::Status::EngineOk)
	{
		return false;
	}

	Player player(this->player->interfaceType(), binPath, mediainfoBinPath, ffprobeBinPath,
		standardInput, standardOutput, standardError, log, verbosity,
		preferences.latency_start, preferences.latency_finished, preferences.latency_kill);

	player.setBinPath(binPath);

	// Mute local player
	player.setSoundOff();
	player.setVideoOff();
	player.setSubtitleOff();

	// We don't use the player process member for this, we use a local QProcess
	// IT MUST BE DECLARED HERE, 
	// so is reset and next title playack doesn't overlap current playback channels
	QProcess process; // on the stack so no need to delete

	// Get player command line
	QString *cmd = new QString;
	QStringList *args = new QStringList;;

	player.setSoundOff();
	player.cliCmdUpdateFontCache(cmd, args);
	player.setSoundOn();

	// Start!
	QString strIn(*cmd);
	strIn.append(" ").append(args->join(" "));  // monitor process input
	standardInput->append(strIn);

	qDebug() << "Start update MPlayer font cache" << *cmd << *args;

	process.start(*cmd, *args);

	if (!process.waitForStarted( preferences.latency_start ))
	{
		qDebug() << "Failed to update MPlayer font cache [process start timed-out]";
		printLog( tr("Failed to update MPlayer font cache (process start timed-out)."), VERBOSITY_QUIET );
		return false;
	}

	// Let MPlayer's font cache if necessary
	userStopUpdateFontCache = false;
	//bool updateFontCache(false);
	bool updatingFontCache(false);

	while(1)
	{
		QCoreApplication::processEvents();  // required for Qprocess::canReadLine()

		if (updatingFontCache && userStopUpdateFontCache) break;
		if (process.state() != QProcess::Running) break;

		process.setReadChannel(QProcess::StandardOutput);

		while(process.canReadLine())
		{
			QByteArray buffer(process.readLine());
			QString stdOut(buffer.simplified().trimmed());
			qDebug() << "PlayerWidget::isReady updating font cache" << stdOut;

			// Parsing standard output

			if (!updatingFontCache && stdOut.startsWith("Scanning file"))
			{
				updatingFontCache = true;

				// Show progress bar
				progressBar = new ProgressWidget(tr("%1 - Processing").arg(APPTITLE),
					tr("Please wait while updating subtitle font cache (can take 1 minute...)"),
					1, 100, mainConsole);

				connect( progressBar, SIGNAL(closedByUser()), this, SLOT(stopUpdateFontCache()) );

				progressBar->setWindowModality(Qt::ApplicationModal);
				progressBar->stepUpdate();
				progressBar->show();
				QCoreApplication::processEvents();  // we want to show progress bar immediately
			}

			if (updatingFontCache)
			{
				if (progressBar)
				{
					if (progressBar->value() >= 80)
						progressBar->reset(1, 100, 20);
					else
						progressBar->stepUpdate();
				}

				QCoreApplication::processEvents();
			}
		}

		process.setReadChannel(QProcess::StandardError);

		while(process.canReadLine())
		{
			QByteArray buffer(process.readLine());
			QString stdErr(buffer.simplified().trimmed());

			qDebug() << "PlayerWidget::isReady updating font cache" << stdErr;
		}

	} // while(1)

	// Delete progress bar
	if (progressBar)
	{
		if (progressBar) delete(progressBar);
		progressBar = 0;
	}

	process.close();

	return true; 
}

void PlayerWidget::setVerbosity(const int verbose)
{
	verbosity = verbose;

	player->setVerbosity(verbose);
}

void PlayerWidget::setBinPath(const QString &path)
{
	binPath = path;
	player->setBinPath( path );

	enablePlaybackControls( isMovieLoaded->checkState() == Qt::Checked && isReady() );
	enableTrackControls( isMovieLoaded->checkState() == Qt::Checked && isReady() );
	
#if defined( WIN32 )
	if (!player->setPreferredVideoDriver("gl2")) player->setPreferredVideoDriver("gl");
#elif defined( Q_OS_MAC )
	// MPlayer displays in its on window on Mac
	// On Mac OS X, the type returned depends on which framework Qt was linked against. 
	// If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, 
	// {WId} is a pointer to an NSView.
	//*experimental
	player->setPreferredVideoDriver("corevideo");
#else // UNIX and !MAC
	if (!player->setPreferredVideoDriver("xv"))
		if (!player->setPreferredVideoDriver("x11"))
			if (!player->setPreferredVideoDriver("gl2")) player->setPreferredVideoDriver("gl");
	// xv has better performance but have issues on Ubuntu 11.10
	//if (!player->setPreferredVideoDriver("x11"))
	//	if (!player->setPreferredVideoDriver("gl2"))
	//		if (!player->setPreferredVideoDriver("gl")) player->setPreferredVideoDriver("xv");
#endif
}

void PlayerWidget::setMediaInfoBinPath(const QString &path)
{
	mediainfoBinPath = path;
	player->setMediaInfoBinPath( path );
}

void PlayerWidget::setFFprobeBinPath(const QString &path)
{
	ffprobeBinPath = path;
	player->setFFprobeBinPath( path );
}

bool PlayerWidget::setInput(const QString &path, const int media)
{
	isMovieLoaded->setCheckState(Qt::PartiallyChecked);  // Loading

	if ( media == Video::Media::File )
	{
		// Show progress
		progressBar = new ProgressWidget(tr("%1 - Processing").arg(APPTITLE), 
			tr("Please wait while computing media informations..."), 1, 50, mainConsole);

		// Try to load new input file
		if ( !loadFile(path) )
		{
			if (progressBar) delete(progressBar);
			progressBar = 0;
			
			isMovieLoaded->setChecked(false);  // Not loaded
			return false;
		}

		if (progressBar) delete(progressBar);
		progressBar = 0;
	}
	else if ( media == Video::Media::DVD )
	{
		// Show progress
		progressBar = new ProgressWidget(tr("%1 - Processing").arg(APPTITLE), 
			tr("Please wait while loading DVD informations..."), 1, 50, mainConsole);

		userStopLoadDvd = false;
		connect( progressBar, SIGNAL(closedByUser()), this, SLOT(stopLoadDvd()) );

		progressBar->setWindowModality(Qt::ApplicationModal);
		progressBar->stepUpdate();
		progressBar->show();
		QCoreApplication::processEvents();  // we want to show message immediately

		// Try to access and authenticate DVD
		if ( !openDvd(path) )
		{
			if (progressBar) delete(progressBar);
			progressBar = 0;

			isMovieLoaded->setChecked(false);  // Not loaded
			return false;
		}

		if ( !loadDvd(path) )
		{
			if (progressBar) delete(progressBar);
			progressBar = 0;

			isMovieLoaded->setChecked(false);  // Not loaded
			return false;
		}

		if (progressBar) delete(progressBar);
		progressBar = 0;
	}
	else
	{
		return false;
	}


	// Now we can set the new valid input
	this->movie = path;
	this->media = media;
	if ( media == Video::Media::DVD )
	{
		player->setSource(movie, media, this->title);  // title was set while loading
	}
	else
	{
		player->setSource(movie, media, movieInfo.novideo);
	}

	// Can play now !
	isMovieLoaded->setChecked(true);  // will refresh GUI with player controls (signal/slot)

	setZoom(zoom);
			
	printLog( tr("Set player input (%1%2): %3")
		.arg(Video::strMedia(media))
		.arg(media==Video::Media::DVD?tr(" title %1").arg(title):"")
		.arg(movie), VERBOSITY_VERBOSE );

	// And enable appropriate controls
	enablePlaybackControls( true );

	return true;
}

QSize PlayerWidget::renderSize()
{
	return renderTarget->size();
}

QString PlayerWidget::currentAspectRatio()
{
	QString aspect("16:9");  // arbitrary default is screen 16:9

	if (isMovieLoaded->isChecked())
	{
		if (media == Video::Media::DVD)
		{
			aspect = Video::strDvdAspect( titleInfo[title -1].mpeg_aspect );
		}
		else if (media == Video::Media::File)
		{
			if (movieInfo.aspect_ratio.contains(":"))
			{
				aspect = movieInfo.aspect_ratio;
			}
			else
			{
				aspect =  QString(tr("%1:1").arg(movieInfo.display_aspect, 0, 'f', 2));
			}
		}
	}

	return aspect;
}

void PlayerWidget::enableTrackControls(const bool yes)
{
	trackGroup->setEnabled(yes);
}

void PlayerWidget::enablePlaybackControls(const bool yes)
{
	// Playback buttons
	controller->setEnabled(yes);
	pauseButton->setEnabled(yes && runPlaying);
	slowerButton->setEnabled(yes && runPlaying);
	fasterButton->setEnabled(yes && runPlaying);
	prevChapterButton->setEnabled(yes && !preferences.test_mode && runPlaying);
	nextChapterButton->setEnabled(yes && !preferences.test_mode && runPlaying);
	prevButton->setEnabled(yes && !preferences.test_mode && runPlaying);
	nextButton->setEnabled(yes && !preferences.test_mode && runPlaying);
}

void PlayerWidget::clearTrackControls()
{
	qDebug() << "PlayerWidget: clear track controls";

#ifdef WIN32
	// Windows style sets combobox width on first string, and limits drop-down width
	// so ensure largest to title, smallest to angle
	titleList->clear();
	//titleList->addItem(tr("Video______________________________"));
	titleList->addItem(tr("Video                                                     "));
	titleList->setDisabled(false);
	audioList->clear();
	//audioList->addItem(tr("Audio______________"));
	audioList->addItem(tr("Audio                             "));
	audioList->setDisabled(false);
	subtitleList->clear();
	subtitleList->addItem(tr("Subtitle"));
	subtitleList->setDisabled(false);
	angleList->clear();
	angleList->addItem(tr("Angle"));
	angleList->setDisabled(false);
#else
	titleList->clear();
	titleList->addItem(tr("Video"));
	titleList->setDisabled(true);
	audioList->clear();
	audioList->addItem(tr("Audio"));
	audioList->setDisabled(true);
	subtitleList->clear();
	subtitleList->addItem(tr("Subtitle"));
	subtitleList->setDisabled(true);
	angleList->clear();
	angleList->addItem(tr("Angle"));
	angleList->setDisabled(true);
#endif
}

void PlayerWidget::populateTrackControls()
{
	QString trackDescription;

	if (this->media == Video::Media::DVD && titles > 0)
	{
		printLog( tr("Populate %1 DVD titles in user selection.").arg(titles), VERBOSITY_VERBOSE );

		titleList->clear();

		int t;
		for (t = 1; t <= titles; t++)
		{
			if (titleInfo[t - 1].ignored)
			{
				printLog( tr("Title %1 ignored [%2].")
								.arg(t).arg(Video::strDvdLength(titleInfo[t - 1].length)), VERBOSITY_NORMAL );
				qDebug() << "Title" << t << "ignored" << Video::strDvdLength(titleInfo[t - 1].length);
				continue;
			}

			trackDescription.clear();
	
			trackDescription.append(tr("Title %1 - %2 %3x%4 %5 %6fps [%7 %8kbps]")
				.arg(titleInfo[t - 1].n, 2)
				.arg(Video::strDvdLength(titleInfo[t - 1].length))
				.arg(titleInfo[t - 1].rez.width())
				.arg(titleInfo[t - 1].rez.height())
				.arg(Video::strDvdAspect(titleInfo[t - 1].mpeg_aspect))
				.arg(Video::strDvdFps(titleInfo[t - 1].mpeg_fps))
				.arg(titleInfo[t - 1].codec)
				.arg(Video::strDvdKbps(titleInfo[t - 1].kbps)));

			titleList->addItem(trackDescription.simplified(), t);

			printLog( tr("Enable title %1 for user selection [%2].")
							.arg(t).arg(Video::strDvdLength(titleInfo[t - 1].length)), VERBOSITY_VERBOSE );
			qDebug() << "Enable title" << t << Video::strDvdLength(titleInfo[t - 1].length);
		}

		// Ensure there's at least one title in the list (then is title 1)
		if (titleList->count() == 0)
		{
			// Then don't ignore title 1
			trackDescription.clear();
			t = 1;
			trackDescription.append(tr("Title %1 - %2 %3x%4 %5 %6fps [%7 %8kbps]")
				.arg(titleInfo[t - 1].n, 2)
				.arg(Video::strDvdLength(titleInfo[t - 1].length))
				.arg(titleInfo[t - 1].rez.width())
				.arg(titleInfo[t - 1].rez.height())
				.arg(Video::strDvdAspect(titleInfo[t - 1].mpeg_aspect))
				.arg(Video::strDvdFps(titleInfo[t - 1].mpeg_fps))
				.arg(titleInfo[t - 1].codec)
				.arg(Video::strDvdKbps(titleInfo[t - 1].kbps)));
			
			titleList->addItem(trackDescription.simplified(), t);

			printLog( tr("Enable title %1 [%2] for user selection (enable at least one  title).")
							.arg(t).arg(Video::strDvdLength(titleInfo[t - 1].length)) , VERBOSITY_VERBOSE);
			qDebug() << "Enable title" << t << Video::strDvdLength(titleInfo[t - 1].length);
		}
	
		// Set current title to main title
		titleList->setCurrentIndex( titleList->findData(longest) );
		titleList->setDisabled(false);
		this->title = titleList->itemData(titleList->currentIndex()).toInt();

		// Update audio, subs and angles in this current title
		populateTrackControlsAud(this->title);
		populateTrackControlsSub(this->title);
		populateTrackControlsAng(this->title);

		// Show all lists
		titleList->setVisible(true);
		audioList->setVisible(true);
		subtitleList->setVisible(true);
		//if (angle != -1) angleList->setVisible(true);
	}
	else if (this->media == Video::Media::File && !movieInfo.codec.isEmpty())
	{
		titleList->clear();
		if (noChoiceInList) titleList->addItem(QString(tr("No video")), -1);

		trackDescription.clear();

		QString strCodec =  movieInfo.codec.toUpper();

		if (movieInfo.kbps == 0.0)
		{
			trackDescription.append(tr("[%4]")
			.arg(strCodec));
		}
		else
		{
			trackDescription.append(tr("[%4 %5kbps]")
			.arg(strCodec)
			.arg(Video::strDvdKbps(movieInfo.kbps)));
		}
	
		if (movieInfo.aspect_ratio == "4:3" || movieInfo.aspect_ratio == "16:9")
		{
			trackDescription.prepend(tr("Video - %1 %2x%3 %4 %5fps ")
				.arg(Video::strDvdLength(movieInfo.length))
				.arg(movieInfo.rez.width())
				.arg(movieInfo.rez.height())
				.arg(movieInfo.aspect_ratio)
				.arg(movieInfo.fps));
		}
		else
		{
			trackDescription.prepend(tr("Video - %1 %2x%3 %4fps ")
				.arg(Video::strDvdLength(movieInfo.length))
				.arg(movieInfo.rez.width())
				.arg(movieInfo.rez.height())
				.arg(movieInfo.fps));
		}

		printLog( tr("Enable video track for user selection [%1].")
							.arg(Video::strDvdLength(movieInfo.length)), VERBOSITY_VERBOSE );
		qDebug() << "Enable video track" << Video::strDvdLength(movieInfo.length);

		titleList->addItem(trackDescription.simplified(), 1);	
		
		titleList->setDisabled(false);
		if (noChoiceInList)
			titleList->setCurrentIndex( 1 );  // the video track of the movie file (0 is no video)
		else
			titleList->setCurrentIndex( 0 );  // the video track of the movie file

		// Update audio, subs and angles in this current title
		populateTrackControlsAud(movieInfo);
		populateTrackControlsSub(movieInfo);
		populateTrackControlsAng(movieInfo);

		// Show all lists
		titleList->setVisible(true);
		audioList->setVisible(true);
		if (movieInfo.subtitle.isEmpty()) subtitleList->setVisible(false);
		//if (angle != -1) angleList->setVisible(true);
	}
	else if (this->media == Video::Media::File && movieInfo.novideo)
	{
		populateTrackControlsAud(movieInfo);
		populateTrackControlsSub(movieInfo);
		populateTrackControlsAng(movieInfo);

		// Show audio and hide video and subtitle
		audioList->setVisible(true);
		if (movieInfo.novideo) titleList->setVisible(false);
		if (movieInfo.subtitle.isEmpty()) subtitleList->setVisible(false);
		//if (angle != -1) angleList->setVisible(true);
	}
	else
	{
		clearTrackControls();

		titleList->setVisible(true);
		audioList->setVisible(true);
		subtitleList->setVisible(true);
		//if (angle != -1) angleList->setVisible(true);
	}
}

void PlayerWidget::populateTrackControlsAud(const int title)
{
	int a, t(title -1);

	if (t < 0)
		return;  // sure, but should be considered befaore calling

	qDebug() << "PlayerWidget: populate" << titleInfo[t].audioN << "DVD audio streams for title" << title;

	audioList->clear();
	audioList->addItem(QString(tr("No audio")), -1);

	if (titleInfo[t].audioN <= 0)
	{
		audioList->setCurrentIndex( 0 );  // sounds reasonable as a reset (no audio)
		audioList->setDisabled(true);
		audio = -1;
		return;
	}

	// Append audio streams detected
	for (a = 0; a < titleInfo[t].audioN; a++)
	{
		QString str = tr("%1 %2 %3")
						.arg(Video::strDvdLang(titleInfo[t].audio[a].language))
						.arg(titleInfo[t].audio[a].format)
						.arg(titleInfo[t].audio[a].channels);

		if (str.trimmed().isEmpty())
		{
			str = tr("Audio %1 - %2 kbps %3 kHz")
					.arg(titleInfo[t].audio[a].n, 2)
					.arg(Video::strMovieKbps(titleInfo[t].audio[a].kbps))
					.arg(Video::strMovieSampling(titleInfo[t].audio[a].sampling));
		}
		else
		{
			str = tr("Audio %1 - %2 %3 kbps %4 kHz [%5 %6]")
					.arg(titleInfo[t].audio[a].n, 2)
					.arg(Video::strDvdLang(titleInfo[t].audio[a].language))
					.arg(Video::strMovieKbps(titleInfo[t].audio[a].kbps))
					.arg(Video::strMovieSampling(titleInfo[t].audio[a].sampling))
					.arg(titleInfo[t].audio[a].format.toUpper())
					.arg(titleInfo[t].audio[a].channels);
		}
		
		audioList->addItem(str.simplified(), a);
	}

	audioList->setCurrentIndex( 1 );  // default first track, 0 is noaudio
	audioList->setDisabled(false);
	audio = audioList->itemData( audioList->currentIndex() ).toInt();
}

void PlayerWidget::populateTrackControlsAud(const movie_t &)
{
	// We're using plymorphisme here, but cause ther's only 1 movie_t member,
	// we don't check it or use indirection, we directly use movieInfo member
	
	int a;

	qDebug() << "PlayerWidget: populate" << movieInfo.audioN << "audio streams for current movie";

	audioList->clear();

	if (movieInfo.audioN <= 0)
	{
		audioList->addItem(QString(tr("No audio")), -1);
		audioList->setCurrentIndex( 0 );  // sounds reasonable as a reset (no audio)
		audioList->setDisabled(true);
		audio = -1;
		return;
	}

	// Start with "no" choice if not audio only file
	if ( noChoiceInList && !movieInfo.novideo ) audioList->addItem(QString(tr("No audio")), -1);

	// Append audio streams detected
	for (a = 0; a < movieInfo.audioN; a++)
	{
		QString aidStr, strLang, strChannels, strSampling, strBitrate, strFormat, strCodec;

		aidStr = movieInfo.audio[a].aid == 0 ? "0" : aidStr.sprintf( "%#2.2X", movieInfo.audio[a].aid );
		 
		strLang = (!movieInfo.audio[a].language.isEmpty() && movieInfo.audio[a].language != "und") ?
			Video::strDvdLang(movieInfo.audio[a].language) + " ": "";

		if (movieInfo.audio[a].sampling > 0)
			strSampling = Video::strMovieSampling(movieInfo.audio[a].sampling) + " kHz ";

		if (movieInfo.audio[a].kbps > 0.0)
			strBitrate = Video::strMovieKbps(movieInfo.audio[a].kbps) + " kbps ";
		else
			strBitrate = "VBR ";

		if (!movieInfo.audio[a].channels.isEmpty())
			strChannels = " " + movieInfo.audio[a].channels; 
		else if (movieInfo.audio[a].channel_number == 1)
			strChannels = " " + tr("mono");
		else if (movieInfo.audio[a].channel_number == 2)
			strChannels = " " + tr("stereo");
		else if (movieInfo.audio[a].channel_number == 3)
			strChannels = " " + tr("2.1");
		else if (movieInfo.audio[a].channel_number == 6)
			strChannels = " " + tr("5.1");
		else if (movieInfo.audio[a].channel_number == 8)
			strChannels = " " + tr("7.1");
		else if (movieInfo.audio[a].channel_number > 0)
			strChannels = " " + QString::number(movieInfo.audio[a].channel_number) + tr(" ch.");
	
		strFormat = movieInfo.audio[a].format.toUpper();

		strCodec =  movieInfo.audio[a].codec.toUpper();

		/*experimental we'd use MediaInfo (Codec Hint)
		if (!strCodec.isEmpty() && !strFormat.isEmpty() && strCodec != strFormat)
			strCodec = strCodec + "/" + strFormat;
		else*/
		if (!strFormat.isEmpty())
			strCodec = strFormat;
		
		QString str = movieInfo.audio[a].aid >= 128 ? 
					tr("Audio ") + aidStr : 
					tr("Audio %1").arg(movieInfo.audio[a].n, 2);

		str += " - " + strLang + strBitrate + strSampling + " [" + strCodec + strChannels + "]";

		if (!movieInfo.audio[a].description.isEmpty()) str += " " + movieInfo.audio[a].description;
	
		audioList->addItem(str.simplified(), a);
	}

	// Default first track
	if ( noChoiceInList && !movieInfo.novideo )
		audioList->setCurrentIndex( 1 );  // 0 is noaudio
	else
		audioList->setCurrentIndex( 0 );

	audioList->setDisabled(false);
	audio = audioList->itemData( audioList->currentIndex() ).toInt();
}

void PlayerWidget::populateTrackControlsSub(const int title)
{
	int s, t(title -1);

	if (t < 0)
		return;  // sure, but should be considered befaore calling

	qDebug() << "PlayerWidget: populate" << titleInfo[t].subtitleN << "DVD subtitles for title" << title;

	subtitleList->clear();

	if (titleInfo[t].subtitleN <= 0)
	{
		subtitleList->addItem(QString(tr("No subtitle")), -1);
		subtitleList->setCurrentIndex( 0 );  // sounds reasonable as a reset
		subtitleList->setDisabled(true);
		subtitle = -1;
		return;
	}

	// Start with "no" choice
	subtitleList->addItem(QString(tr("No subtitle")), -1);

	//  Append detected subtitles
	for (s = 0; s < titleInfo[t].subtitleN; s++)
	{
		QString str;

		str = tr("Subtitle %1 - %2")
				.arg(titleInfo[t].subtitle[s].sid)
				.arg(Video::strDvdLang(titleInfo[t].subtitle[s].language));

		subtitleList->addItem(str.simplified(), s);
	}

	subtitleList->setCurrentIndex( 0 );  // on top of list
	subtitleList->setDisabled(false);
	subtitle = subtitleList->itemData( subtitleList->currentIndex() ).toInt();
}

void PlayerWidget::populateTrackControlsSub(const movie_t &)
{
	// We're using polymorphism here, but cause there's only 1 movie_t member,
	// we don't check it or use indirection, we directly use movieInfo member
	
	int s;

	qDebug() << "PlayerWidget: populate" << movieInfo.subtitleN << "subtitle streams for current movie";

	subtitleList->clear();

	subtitle = -1;  // no sub by default

	if (movieInfo.subtitleN <= 0)
	{
		subtitleList->addItem(QString(tr("Subtitle")), -1);
		subtitleList->setCurrentIndex( 0 );  // sounds reasonable as a reset
		subtitleList->setDisabled(true);
		return;
	}

	// Start with "no" choice
	subtitleList->addItem(QString(tr("No subtitle")), -1);

	//  Append detected subtitles
	for (s = 0; s < movieInfo.subtitleN; s++)
	{
		QString str("");

		if (movieInfo.subtitle[s].description.isEmpty())
		{
			str = tr("Subtitle %1 - %2")
					.arg(movieInfo.subtitle[s].sid)
					.arg(Video::strDvdLang(movieInfo.subtitle[s].language));  // subtitle format has no value for user
		}
		else
		{
			str = tr("Subtitle %1 - %2, %3")
					.arg(movieInfo.subtitle[s].sid)
					.arg(Video::strDvdLang(movieInfo.subtitle[s].language))
					.arg(movieInfo.subtitle[s].description);
		}

		subtitleList->addItem(str.simplified(), s);
	}

	subtitleList->setCurrentIndex( 0 );  // on top of list
	subtitleList->setDisabled(false);
	subtitle = subtitleList->itemData( subtitleList->currentIndex() ).toInt();
}

void PlayerWidget::populateTrackControlsAng(const int title)
{
	int a, t(title -1);

	if (t < 0)
		return;  // sure, but should be considered befaore calling

	qDebug() << "PlayerWidget: populate" << titleInfo[t].angleN << "DVD angles for title" << title;

	angleList->clear();

	angle = -1;  // no particular angle by default

	if (titleInfo[t].angleN <= 1)
	{
		angleList->addItem(tr("Angle"), -1);
		angleList->setCurrentIndex( 0 ); 
		angleList->setDisabled(true); 
		return;
	}

	// Append multi-angleN detected	
	for (a = 1; a <= titleInfo[t].angleN; a++)
	{
		QString str = tr("Angle %1").arg(a);
		angleList->addItem(str, a);
	}
	
	angleList->setCurrentIndex( 0 );  // on top of list
	angleList->setDisabled(false);
	angle = angleList->itemData( angleList->currentIndex() ).toInt();
}

void PlayerWidget::populateTrackControlsAng(const movie_t &)
{
	qDebug() << "PlayerWidget: populate [No multi-angles] for current movie";

	angleList->clear();

	angle = -1;  // no particular angle by default

	angleList->addItem(tr("Angle"), -1);
	angleList->setDisabled(true); 
	angle = angleList->itemData( angleList->currentIndex() ).toInt();
}

void PlayerWidget::stopLoadDvd()
{
	userStopLoadDvd = true;
}

void PlayerWidget::stopUpdateFontCache()
{
	userStopUpdateFontCache = true;
}

bool PlayerWidget::openDvd(const QString &path)
{
	if (progressBar) progressBar->stepUpdate();

	// Prerequisite: check source path
	QFileInfo info(path);

	if (!info.exists())
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load DVD from:\n%1").arg(path));
		infoBox.setInformativeText(tr("This path doesn't exist."));
		infoBox.exec();

		return false;
	}

	if (progressBar) progressBar->stepUpdate();

	if (!info.isDir())
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to access DVD from:\n%1").arg(movie));
		infoBox.setInformativeText(tr("This isn't a valid directory."));
		infoBox.exec();

		return false;
	}

	if (progressBar) progressBar->stepUpdate();

	// Authenticate DVD
	QStringList log;
	QString stdOut, stdErr;

	player->authenticateDvd(path);

	if (progressBar) progressBar->stepUpdate();

	// We do twice because sometimes once isn't enough
	player->authenticateDvd(path);

	printOut( stdOut );	
	printErr( stdErr );	

	while (!log.isEmpty()) printLog( log.takeFirst(), VERBOSITY_VERBOSE );

	if (progressBar) progressBar->stepUpdate();

	return true;
}

bool PlayerWidget::loadDvd(const QString &path)
{
	// Proceed
	printLog(tr("Start loading DVD."), VERBOSITY_NORMAL);

	// Create a local player interface
	Player player(this->player->interfaceType(), binPath, mediainfoBinPath, ffprobeBinPath,
		standardInput, standardOutput, standardError, log, verbosity,
		preferences.latency_start, preferences.latency_finished, preferences.latency_kill);

	player.setBinPath(binPath);
	player.setSource(path, Video::Media::DVD, 1);

	// Mute local player
	player.setSoundOff();
	player.setVideoOff();
	player.setSubtitleOff();

	qDebug() << "PlayerWidget::loadDvd() access DVD and get title number";

	// We don't use the player process member for this, we use a local QProcess
	// IT MUST BE DECLARED HERE, 
	// so is reset and next title playack doesn't overlap current playback channels
	QProcess process; // on the stack so no need to delete

	// Reset disk info
	mplayer_video_disk_t disk;
	disk.media = Video::Media::DVD;
	disk.titles = 0;
		
	qDebug() << "PlayerWidget::loadDvd() reset disk info data";

	// Refresh WId as it's better to set it the later
#if defined(WIN32)
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	// Reinterpret_cast<qlonglong> is mandatory, winId() not kind enough when being converted ;)
	player.setWid(QString::number(reinterpret_cast<qlonglong>(renderTarget->winId())));
#elif defined(Q_OS_MAC)
	// MPlayer displays in its on window on Mac
	// On Mac OS X, the type returned depends on which framework Qt was linked against. 
	// If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, 
	// {WId} is a pointer to an NSView.
	// May work on Mac if renderTarget is a QWidget (see Phonon WidgetNoPaintEvent)
	// player.setWid(QString::number(renderTarget->winId()));
#else	// UNIX and !MAC
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	player.setWid(QString::number(renderTarget->winId()));
#endif

	// Get player command line
	QString *cmd = new QString;
	QStringList *args = new QStringList;;

	player.cliCmdIdentify(cmd, args);

	// Start!
	QString strIn(*cmd);
	strIn.append(" ").append(args->join(" "));  // monitor process input
	standardInput->append(strIn);

	qDebug() << "Start DVD loading process" << *cmd << *args;

	process.start(*cmd, *args);  // will stop by itself after 1 second

	if (!process.waitForStarted( preferences.latency_start ))
	{
		qDebug() << "Failed to open DVD [process start timed-out]";
		printLog( tr("Failed to open DVD (process start timed-out)."), VERBOSITY_QUIET );
		printLog( tr("Try to increase player latency (start) in timing preferences."), VERBOSITY_QUIET );

		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to access DVD from:\n%1").arg(movie));
		infoBox.setInformativeText(tr("Try to increase player latency (start) in timing preferences."));
		infoBox.exec();

		return false;
	}

	// Process will stop by itself because -frames 0, but it has a lot to do, so increase latency
	int latency_finished = preferences.latency_finished + 2000;

	if (!process.waitForFinished( latency_finished ))
	{
		qDebug() << "Failed to stop open DVD process softly, waitForFinished() has timed out at (msec):"
			<< preferences.latency_finished;

		printLog( tr("Failed to stop open DVD process softly in %1 milliseconds.")
			.arg(preferences.latency_finished), VERBOSITY_QUIET );
		printLog( tr("Try to increase player latency (finished) in timing preferences."), VERBOSITY_QUIET );

		printLog( tr("Kill open DVD process."), VERBOSITY_VERBOSE );
	
		printLog( tr("Send a kill command to open DVD process."), VERBOSITY_VERBOSE );
		process.kill();
	}	

	process.setReadChannel(QProcess::StandardError);

	while(process.canReadLine())
	{
		// This loop can be long and doesn't run in its own thread
		QCoreApplication::processEvents();  // we want to show message immediately

		QByteArray buffer(process.readLine());

		QString strError = QString(buffer.simplified()).trimmed();	
					
		// Filter out some messages if not verbose
		if ( verbosity != VERBOSITY_VERBOSE )
		{
			QStringList lines = strError.split( QRegExp("\\n"), QString::SkipEmptyParts );

			QString str;
			QStringListIterator lineIterator(lines);
			while (lineIterator.hasNext())
			{
				QString buffStr = lineIterator.next();

				if ( !buffStr.startsWith("libdvdread:") 
					&& !buffStr.startsWith("libdvdnav:") 
					&& !buffStr.startsWith("libdvdcss:")  )
				{
					str.append( buffStr );
				}
			}

			strError = str;
		}
		
		printErr( strError );	
	}

	process.setReadChannel(QProcess::StandardOutput);

	while(process.canReadLine())
	{
		// This loop can be long and doesn't run in its own thread
		QCoreApplication::processEvents();  // we want to show message immediately

		QByteArray buffer(process.readLine());

		// log everything so we can easily find bugs,
		// discover player interface changes and
		// improve callback computation
		printOut( QString(buffer.simplified()).trimmed() );	
					
		// Parsing standard output

		int res;
			
		if (!player.parseStdoutDisk( buffer, &res, &disk ))
		{
			printLog( tr("Failed to parse player output [%1]").arg(QString(buffer)), VERBOSITY_NORMAL );		
			qDebug() << "PlayerWidget: failed to parse player output" << buffer;

			continue; // we can't parse anything then
		}
					
		if ( res == Video::Parsing::NoDvdVmg )
		{
			printLog(tr("Failed to open DVD."), VERBOSITY_QUIET); 
		
			InfoBoxDialog infoBox(this);
			infoBox.setText(tr("Failed to open DVD from:\n%1").arg(path));
			infoBox.setInformativeText(tr("This isn't a valid DVD structure."));
			infoBox.exec();

			return false;
		}

		if ( res == Video::Parsing::Titles)
		{
			printLog( tr("Info: detected %1 titles in the DVD.").arg(disk.titles), VERBOSITY_QUIET );
			qDebug() << "PlayerWidget: detected DVD title number" << disk.titles;
		}

		if ( res == Video::Parsing::VolumeId)
		{
			printLog( tr("Info: detected DVD volume identifier (%1).").arg(disk.volumeIdentifier), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected DVD volume identifier" << disk.volumeIdentifier;
		}

		if ( res == Video::Parsing::Chapters)
		{
			printLog( tr("Info: detected %1 chapters in title %2.")
				.arg(disk.title[disk.current - 1].chapterN).arg(disk.current), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected" << disk.title[disk.current - 1].chapterN 
				<< "chapters in DVD title" << disk.current;
		}

		if ( res == Video::Parsing::Angles)
		{
			printLog( tr("Info: detected %1 angle(s) in title %2.")
				.arg(disk.title[disk.current - 1].angleN).arg(disk.current), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected" << disk.title[disk.current - 1].angleN
				<< "chapters in DVD title" << disk.current;
		}

		if ( res == Video::Parsing::Length)
		{
			if (progressBar) progressBar->stepUpdate();

			if ( preferences.dvd_ignore_short_titles)
			{
				disk.title[disk.current - 1].ignored = (disk.title[disk.current - 1].length < preferences.dvd_title_min_length);
			}
			else
			{
				disk.title[disk.current - 1].ignored = false;
			}					
			
			if ( disk.longest == 0 )
			{
				 disk.longest = disk.current;
			}
			else if ( disk.title[disk.longest - 1].length < disk.title[disk.current - 1].length )
			{
				 disk.longest = disk.current;
			}

			if ( disk.title[disk.current - 1].ignored )
			{
				printLog( tr("Info: title %1 will be ignored because too short [%2].")
					.arg(disk.current).arg(Video::strDvdLength(disk.title[disk.current - 1].length)), VERBOSITY_QUIET );
				qDebug() << "PlayerWidget: title" << disk.current << "will be ignored" 
					<< Video::strDvdLength(disk.title[disk.current - 1].length);
			}
			else
			{
				printLog( tr("Info: detected DVD title %1 [%2].")
					.arg(disk.current).arg(Video::strDvdLength(disk.title[disk.current - 1].length)), VERBOSITY_QUIET );
				qDebug() << "PlayerWidget: detected title" << disk.current 
					<< "length" << Video::strDvdLength(disk.title[disk.current - 1].length);
			}
		}
	}

	process.close();

	// A few comments to user
	if (disk.titles < 1)
	{
		printLog(tr("Can't get DVD title number."), VERBOSITY_QUIET);
		qDebug() << "Can't get DVD title number.";

		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to access DVD from:\n%1").arg(path));
		infoBox.setInformativeText(tr("Player engine couldn't get DVD title number."));
		infoBox.exec();

		return false;
	}

	// We got title number so we can alter DVD data members (media, titles, titleInfo[])
	this->media = disk.media;
	this->titles = disk.titles;
	this->longest = disk.longest;
	this->name = disk.volumeIdentifier;  
	this->titleInfo.clear();
	this->movieInfo.clear();  // clear eventual movie file data
	this->novideo = false;

	if (this->name.isEmpty()) this->name = path;

	printLog(tr("Start loading all DVD titles."), VERBOSITY_VERBOSE);

	progressBar->setRemainingStep(titles + 2);  // 3 more steps after reading titles

	// Proceed for  every title
	int t;
	//int longestTitle(1);  // for MediaInfo completion
	//double longestLength(0.0);
	int titleNotLoaded(0);

	for (t = 1; t <= titles; ++t)
	{
		if (userStopLoadDvd)
		{
			printLog(tr("Loading DVD aborted by user."), VERBOSITY_QUIET);
			qDebug() << "Loading DVD aborted by user.";

			if (progressBar) delete(progressBar);
			progressBar = 0;

			return false;
		}

		if (progressBar) progressBar->stepUpdate();

		title_t info;
		info.n = t;
		//info.mpeg_aspect = 3;  // most likely
		//info.mpeg_fps = 2;  // most supported world wide
		info.ignored = disk.title[t - 1].ignored;
		info.chapterN = disk.title[t - 1].chapterN;
		info.angleN = disk.title[t - 1].angleN;
		info.length = disk.title[t - 1].length;

		this->audio = 0;

		qDebug() << "Create title info data structure for title" << t;
		titleInfo.append(info);
		
		// Skip ignored title
		if (info.ignored) continue;

		// We don't use the player process member for this, we use a local QProcess
		// IT MUST BE DECLARED HERE, 
		// so is reset and next title playack doesn't overlap current playback channels
		QProcess process; // on the stack so no need to delete

		player.setSource(path, Video::Media::DVD, t);

		printLog( tr("Load DVD title %1.").arg(t), VERBOSITY_VERBOSE );

		// Refresh WId as it's better to set it the later
#if defined(WIN32)
		qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
		// Reinterpret_cast<qlonglong> is mandatory, winId() not kind enough when being converted ;)
		player.setWid(QString::number(reinterpret_cast<qlonglong>(renderTarget->winId())));
#elif defined(Q_OS_MAC)
		// MPlayer displays in its on window on Mac
		// On Mac OS X, the type returned depends on which framework Qt was linked against. 
		// If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, 
		// {WId} is a pointer to an NSView.
		// May work on Mac if renderTarget is a QWidget (see Phonon WidgetNoPaintEvent)
		// player.setWid(QString::number(renderTarget->winId()));
#else	// UNIX and !MAC
		qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
		player.setWid(QString::number(renderTarget->winId()));
#endif

		// Get player command line
		QString *cmd = new QString;
		QStringList *args = new QStringList;;

		player.cliCmdIdentify(cmd, args);

		// Start!
		QString strIn(*cmd);
		strIn.append(" ").append(args->join(" "));  // monitor process input
		standardInput->append(strIn);

		qDebug() << "Start DVD title" << t << "loading process" << *cmd << *args;

		process.start(*cmd, *args);

		if (!process.waitForStarted( preferences.latency_start ))
		{
			++titleNotLoaded;

			qDebug() << "Failed to load DVD title" << t << "[process start timed-out]";
			printLog( tr("Failed to load DVD title %1 (process start timed-out).").arg(t), VERBOSITY_QUIET );

			titleInfo[t - 1].ignored = true;
			continue;
		}

		// Ask for title length 
		//process.write( player.cliTimeLength().toUtf8() );  // required for DVD title length

		// Ask for deinterlacing
		process.write( player.cliDeinterlace().toUtf8() ); // useful to fill in source interlaced property 

		// Stop playback
		//process.write( player.cliStop().toUtf8() );
		
		// Identifying title can be very long
		int latency = 10000;

		if (!process.waitForFinished( latency ))
		{
			qDebug() << "Failed to stop DVD title" << t << "loading process softly, waitForFinished() has timed out at (msec):"
				<< latency;
			
			printLog( tr("Failed to stop DVD title %1 loading process softly in %2 milliseconds.")
				.arg(t).arg(latency), VERBOSITY_QUIET );
			printLog( tr("Try to increase player latency (finished) in timing preferences."), VERBOSITY_QUIET );

			printLog( tr("Kill DVD title %1 loading process.").arg(t), VERBOSITY_VERBOSE );
		
			printLog( tr("Send a kill command to DVD checking process.") );
			process.kill();
		}	

		// DVD check data (what we need to complete a checking)
		bool gotLength(false), gotFormat(false), gotVideo(false), gotScaledRez(false);
		bool gotChapters(false), gotAngles(false);
		bool gotAudioNum(false), gotSubtitleNum(false);
		bool titleCompleted(false);

		process.setReadChannel(QProcess::StandardError);
		
		int countErrors(0);
		while(process.canReadLine())
		{
			// This loop can be long and doesn't run in its own thread
			QCoreApplication::processEvents();  // we want to show message immediately
		
			QByteArray buffer(process.readLine());

			QString strError = QString(buffer.simplified()).trimmed();	
					
			// Filter out some messages if not verbose
			if ( verbosity != VERBOSITY_VERBOSE )
			{
				QStringList lines = strError.split( QRegExp("\\n"), QString::SkipEmptyParts );

				QString str;
				QStringListIterator lineIterator(lines);
				while (lineIterator.hasNext())
				{
					QString buffStr = lineIterator.next();

					if ( !buffStr.startsWith("libdvdread:") 
						&& !buffStr.startsWith("libdvdnav:") 
						&& !buffStr.startsWith("libdvdcss:")  )
					{
						str.append( buffStr );
					}
				}

				strError = str;
			}
		
			printErr( strError );	
					
			++countErrors;

			if (countErrors > preferences.maximum_loading_errors) break;
		}

		if (countErrors > preferences.maximum_loading_errors) 
		{
			++titleNotLoaded;

			qDebug() << "PlayerWidget: too many errors while loading DVD title" << t;
							// We're done for this title :-)
			printLog(tr("DVD title %1 not loaded, too many player backend errors.").arg(t), VERBOSITY_QUIET);

			continue;
		}

		if (titleNotLoaded >= titles)
		{
			qDebug() << "PlayerWidget: too many errors while loading every DVD title";

			// We're done with this DVD :-(
			printLog(tr("No title loaded for this DVD."), VERBOSITY_QUIET);
		
			InfoBoxDialog infoBox(this);
			infoBox.setText(tr("Failed to load DVD from:\n%1").arg(path));
			infoBox.setInformativeText(tr("Too many player backend errors."));
			infoBox.exec();

			return false;
		}

		process.setReadChannel(QProcess::StandardOutput);

		while(process.canReadLine())
		{
			// This loop can be long and doesn't run in its own thread
			QCoreApplication::processEvents();  // we want to show message immediately

			QByteArray buffer(process.readLine());

			// log everything so we can easily find bugs,
			// discover player interface changes and
			// improve callback computation
			printOut( QString(buffer.simplified()).trimmed() );	
					
			// Parsing standard output

			int res;
			video_clip_t clip;
			clip.media = Video::Media::DVD;
			
			if (!player.parseStdoutClip( buffer, &res, &clip ))
			{
				printLog( tr("Failed to parse player output [%1]").arg(QString(buffer).trimmed()), VERBOSITY_NORMAL );		
				qDebug() << "PlayerWidget: failed to parse player output" << buffer;

				continue; // we can't parse anything then
			}
					
			gotLength = true;
			gotAngles = true;

			if ( res == Video::Parsing::ChapterList )
			{
				titleInfo[t - 1].chapterN = clip.chapterN;
				titleInfo[t - 1].chapter = clip.chapter;
				gotChapters = true;

				printLog( tr("Info: detected %1 chapters in title %2.")
					.arg(clip.chapterN).arg(t), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected" << clip.chapterN 
					<< "chapters in DVD title" << t;
			}

			/* Got it from identify with -msglevel identify=6
			if ( res == Video::Parsing::Length )
			{
				titleInfo[t - 1].length = clip.length;
				if ( preferences.dvd_ignore_short_titles)
				{
					titleInfo[t - 1].ignored = (clip.length < preferences.dvd_title_min_length) ? true : false;
				}
				else
				{
					titleInfo[t - 1].ignored = false;
				}					
				gotLength = true;

				if ( clip.length > longestLength )
				{
					longestLength = clip.length;
					longestTitle = t;
				}
				
				if (titleInfo[t - 1].ignored)
				{
					printLog( tr("Info: title %1 will be ignored because too short [%2].")
						.arg(t).arg(Video::strDvdLength(clip.length)), VERBOSITY_QUIET );
					qDebug() << "PlayerWidget: title" << t << "will be ignored" 
						<< Video::strDvdLength(clip.length);
				}
				else
				{
					printLog( tr("Info: detected DVD title %1 [%2].")
						.arg(t).arg(Video::strDvdLength(clip.length)), VERBOSITY_QUIET );
					qDebug() << "PlayerWidget: detected title" << t 
						<< "length" << Video::strDvdLength(clip.length)
						<< "chapters" << clip.chapterN;
				}
			}
			
			if ( res == Video::Parsing::Chapters )
			{
				titleInfo[t - 1].chapters = clip.chapterN;
				gotChapters = true;

				printLog( tr("Info: detected %1 chapters in title %2.")
					.arg(clip.chapters).arg(t), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected" << clip.chapterN 
					<< "chapters in DVD title" << t;
			}

			if ( res == Video::Parsing::Angles )
			{
				titleInfo[t - 1].angles = clip.angles;				
				gotAngles = true;
			}
			*/

			if ( res == Video::Parsing::ScaledResolution )
			{
				titleInfo[t - 1].scaledRez = clip.scaledRez;
				gotScaledRez = !clip.scaledRez.isNull();

				printLog( tr("Info: detected %1x%2 scaled resolution in title %3.")
					.arg(clip.scaledRez.width()).arg(clip.scaledRez.height()).arg(t), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected" << clip.scaledRez 
					<< "scaled resolution in DVD title" << t;
			}

			if ( res == Video::Parsing::AudioNum )
			{
				titleInfo[t - 1].audioN = clip.audioN;				
				gotAudioNum = true;
			}

			if ( res == Video::Parsing::AudioStream )
			{
				// Fill in new audio channel info
				audio_t audio;
				audio.n = clip.audio.n;
				audio.aid = clip.audio.aid;
				audio.format = clip.audio.format;
				audio.channels = clip.audio.channels;
				audio.language = clip.audio.language;
				// And append to the title list
				titleInfo[t - 1].audio.append(audio);

				printLog( tr("Info: detected audio stream %1 in this DVD title - id %2 language %3 format %4 channels %5.")
					.arg(clip.audio.n).arg(clip.audio.aid).arg(clip.audio.language)
					.arg(clip.audio.format).arg(clip.audio.channels), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected audio stream" << clip.audio.n 
					<< "in this DVD title - id" << clip.audio.aid 
					<< "language" << clip.audio.language 
					<< "format" << clip.audio.format << "channels" << clip.audio.channels;
			}

			if ( res == Video::Parsing::SubtitleNum )
			{
				titleInfo[t - 1].subtitleN = clip.subtitleN;				
				gotSubtitleNum = true;
			}

			if ( res == Video::Parsing::Subtitle )
			{
				// Fill in new subtitle info
				subtitle_t subtitle;
				subtitle.n = clip.subtitle.n;
				subtitle.sid = clip.subtitle.sid;
				subtitle.language = clip.subtitle.language;
				// And append to the title list
				titleInfo[t - 1].subtitle.append(subtitle);

				printLog( tr("Info: detected subtitle %1 in this DVD title - language %2.")
					.arg(clip.subtitle.sid).arg(clip.subtitle.language), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected subtitle" << clip.subtitle.sid 
					<< "in this DVD title - language" << clip.subtitle.language;
			}

			if ( res == Video::Parsing::Format )
			{
				titleInfo[t - 1].format = clip.format;
				gotFormat = true;

				printLog( tr("Info: detected this DVD title file format [%1].")
					.arg(clip.format), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected file format" << clip.format;
			}

			if ( res == Video::Parsing::Stream )
			{
				titleInfo[t - 1].codec = clip.codec;
				titleInfo[t - 1].rez = clip.rez;
				titleInfo[t - 1].mpeg_aspect = clip.mpeg_aspect;
				titleInfo[t - 1].mpeg_fps = clip.mpeg_fps;
				titleInfo[t - 1].kbps = clip.kbps;
				gotVideo = !clip.rez.isNull();

				printLog( tr("Info: detected video stream %1 in this DVD title - resolution %2x%3 aspect %4 (%5) %6 fps %7 kbps.")
					.arg(clip.codec).arg(clip.rez.width()).arg(clip.rez.height())
					.arg(clip.mpeg_aspect).arg(Video::strDvdAspect(clip.mpeg_aspect))
					.arg(Video::strDvdFps(clip.mpeg_fps)).arg(Video::strDvdKbps(clip.kbps)), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected video stream" << clip.codec
					<< "in this DVD title - resolution" << clip.rez.width() << "x" << clip.rez.height() 
					<< "aspect" << clip.mpeg_aspect << Video::strDvdAspect(clip.mpeg_aspect) 
					<< "fps" << clip.mpeg_fps << Video::strDvdFps(clip.mpeg_fps) 
					<< "kbps" << Video::strDvdKbps(clip.kbps);
			}

			// Test we got all info for current title
			if ( gotLength && gotVideo && gotChapters && gotAngles
					&& gotAudioNum && gotSubtitleNum & gotScaledRez
					&& titleInfo[t - 1].audioN == titleInfo[t - 1].audio.count()
					&& titleInfo[t - 1].subtitleN == titleInfo[t - 1].subtitle.count() )
			{
				titleCompleted = true;

				printLog( tr("Info: DVD title %1 info is complete.").arg(t), VERBOSITY_NORMAL );
				qDebug() << "PlayerWidget: DVD title" << t << "info is complete"; 
				break;  // we're done with tis title
			}
		}
		
		// Ensure consistent title data
		if (titleInfo[t - 1].audioN != titleInfo[t - 1].audio.count())
		{
			printLog( tr("Can't parse data for all %1 audio streams")
				.arg(titleInfo[t - 1].audioN), VERBOSITY_NORMAL );
			printLog( tr("Info: successfully loaded data for %1 audio streams")
				.arg(titleInfo[t - 1].audio.count()), VERBOSITY_NORMAL );
			qDebug() << "PlayerWidget: loaded" << titleInfo[t - 1].audio.count() << "streams on"
				<< titleInfo[t - 1].audioN << "audio streams detected in title" << t; 
			titleInfo[t - 1].audioN = titleInfo[t - 1].audio.count();
		}
		if (titleInfo[t - 1].subtitleN != titleInfo[t - 1].subtitle.count())
		{
			printLog( tr("Can't parse data for all %1 subtitle streams")
				.arg(titleInfo[t - 1].subtitleN), VERBOSITY_NORMAL );
			printLog( tr("Info: successfully loaded data for %1 subtitle streams")
				.arg(titleInfo[t - 1].subtitle.count()), VERBOSITY_NORMAL );
			qDebug() << "PlayerWidget: loaded" << titleInfo[t - 1].subtitle.count() << "streams on"
				<< titleInfo[t - 1].subtitleN << "subtitle streams detected in title" << t; 
			titleInfo[t - 1].subtitleN = titleInfo[t - 1].subtitle.count();
		}

		if (!titleCompleted)
		{
			titleInfo[t - 1].ignored = true;  // safe cause rez 0x0 and length 0 will be an issue
		}

		// Run very audio stream in current title to get audio sampling and bitrate
		for (int a = 0; a < titleInfo[t - 1].audioN; ++a)
		{
			this->audio = a;

			player.cliCmdPlay(cmd, args);

			process.start(*cmd, *args);

			if (!process.waitForStarted( preferences.latency_start )) continue;

			process.write( player.cliStop().toUtf8() );
		
			if (!process.waitForFinished( preferences.latency_finished )) process.kill();

			process.setReadChannel(QProcess::StandardOutput);

			while(process.canReadLine())
			{
				QByteArray buffer(process.readLine());

				int res;
				video_clip_t clip;
				clip.media = Video::Media::DVD;
			
				if (!player.parseStdoutClip( buffer, &res, &clip ))
				{
					printLog( tr("Failed to parse DVD title audio output [title %1 audio #%2]")
						.arg(t).arg(a), VERBOSITY_NORMAL );		
					qDebug() << "PlayerWidget: failed to parse DVD title audio output" << buffer;

					continue;
				}

				if ( res == Video::Parsing::AudioMainStream )
				{
					titleInfo[t - 1].audio[a].kbps = clip.audio.kbps;
					titleInfo[t - 1].audio[a].sampling = clip.audio.sampling;

					printLog( tr("Info: detected audio main stream details - %1 Hz %2 kbps")
						.arg(clip.audio.sampling).arg(clip.audio.kbps), VERBOSITY_VERBOSE );
					qDebug() << "PlayerWidget: detected audio stream details" << clip.audio.sampling << "Hz"
						<< clip.audio.kbps << "kbps";
				}
			}
		}
		process.close();
	}

	if (progressBar) progressBar->stepUpdate();

	// Set scaled rezolution
	scaledResolutionReference = titleInfo[longest - 1].scaledRez;

	// Also read mediaInfo
	this->player->readMediaInfo(path); 

	if (progressBar) progressBar->stepUpdate();

	// Check we got consistent DVD data
	int countNotIgnored(0);
	for (t = 1; t <= titles; ++t)
	{
		if (!titleInfo[t - 1].ignored) ++countNotIgnored;
	}

	if (countNotIgnored == 0)
	{
		printLog( tr("Failed to load DVD (try slower timing preferences or rip on hard-drive first)"),
					VERBOSITY_QUIET );

		return false;
	}

	// We're done :-)
	printLog(tr("DVD loaded, ready to play [%1].").arg(this->name), VERBOSITY_QUIET);

	if (progressBar) progressBar->stepUpdate();

	return true;
}

bool PlayerWidget::loadFile(const QString &path)
{
	// Prerequisite: check source path
	QFileInfo info(path);

	if (!info.exists())
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
		infoBox.setInformativeText(tr("This path doesn't exist."));
		infoBox.exec();

		return false;
	}

	if (!info.isFile())
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
		infoBox.setInformativeText(tr("This isn't a valid file."));
		infoBox.exec();

		return false;
	}

	if (progressBar) 
	{
		progressBar->show();
		progressBar->stepUpdate();
		QCoreApplication::processEvents();  // we want to show message immediately

		progressBar->setRemainingStep(9);
	}

	// Proceed
	printLog(tr("Start loading movie."), VERBOSITY_NORMAL);

	// Start with MediaInfo
	this->player->readMediaInfo(path);

	if (progressBar) progressBar->stepUpdate();

	// File must contain video or audio
	if ( mediaInfoData->video.isEmpty() && mediaInfoData->audio.isEmpty() )
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
		infoBox.setInformativeText(tr("This isn't a valid media file (MediaInfo)."));
		infoBox.exec();

		return false;
	}

	// Probe file with FFprobe
	this->player->readFFprobe(path);

	if (progressBar) progressBar->stepUpdate();

	// File must contain video or audio
	if ( ffprobeData->muxer.isEmpty() )
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
		infoBox.setInformativeText(tr("This isn't a valid media file (FFprobe)."));
		infoBox.exec();

		return false;
	}

	if (progressBar) progressBar->stepUpdate();

	// Create a local player interface
	Player player(this->player->interfaceType(), binPath, mediainfoBinPath, ffprobeBinPath,
		standardInput, standardOutput, standardError, log, verbosity,
		preferences.latency_start, preferences.latency_finished, preferences.latency_kill);

	player.setBinPath(binPath);
	player.setSource(path);

	// Mute local player
	player.setSoundOff();
	player.setVideoOff();
	player.setSubtitleOff();

	// We don't use the player process member for this, we use a local QProcess
	// IT MUST BE DECLARED HERE, 
	// so is reset and next title playack doesn't overlap current playback channels
	QProcess process; // on the stack so no need to delete

	// Refresh WId as it's better to set it the later
#if defined(WIN32)
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	// Reinterpret_cast<qlonglong> is mandatory, winId() not kind enough when being converted ;)
	player.setWid(QString::number(reinterpret_cast<qlonglong>(renderTarget->winId())));
#elif defined(Q_OS_MAC)
	// MPlayer displays in its on window on Mac
	// On Mac OS X, the type returned depends on which framework Qt was linked against. 
	// If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, 
	// {WId} is a pointer to an NSView.
	// May work on Mac if renderTarget is a QWidget (see Phonon WidgetNoPaintEvent)
	// player.setWid(QString::number(renderTarget->winId()));
#else	// UNIX and !MAC
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	player.setWid(QString::number(renderTarget->winId()));
#endif

	// Get player command line
	QString *cmd = new QString;
	QStringList *args = new QStringList;;

	player.setSoundOff();
	player.cliCmdIdentify(cmd, args);
	player.setSoundOn();

	// Start!
	QString strIn(*cmd);
	strIn.append(" ").append(args->join(" "));  // monitor process input
	standardInput->append(strIn);

	qDebug() << "Start movie file loading process" << *cmd << *args;

	if (progressBar) progressBar->stepUpdate();

	process.start(*cmd, *args);

	if (!process.waitForStarted( preferences.latency_start ))
	{
		qDebug() << "Failed to load movie [process start timed-out]";
		printLog( tr("Failed to load movie (process start timed-out)."), VERBOSITY_QUIET );
		printLog( tr("Try to increase player latency (start) in timing preferences."), VERBOSITY_QUIET );

		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
		infoBox.setInformativeText(tr("Try to increase player latency (start) in timing preferences."));
		infoBox.exec();

		return false;
	}

	// Ask for deinterlacing
	process.write( player.cliDeinterlace().toUtf8() ); // useful to fill in source interlaced property 
		
	if (progressBar) progressBar->stepUpdate();

	// Process will stop by itself because -frames 0
	if (!process.waitForFinished( preferences.latency_finished ))
	{
		qDebug() << "Failed to stop movie file loading process softly, waitForFinished() has timed out at (msec):"
			<< preferences.latency_finished;

		printLog( tr("Failed to stop movie file loading process softly in %1 milliseconds.")
			.arg(preferences.latency_finished), VERBOSITY_QUIET );
		printLog( tr("Try to increase player latency (finished) in timing preferences."), VERBOSITY_QUIET );

		printLog( tr("Kill movie file loading process."), VERBOSITY_VERBOSE );
	
		printLog( tr("Send a kill command to movie file loading process."), VERBOSITY_VERBOSE );
		process.kill();
	}	

	if (progressBar) progressBar->stepUpdate();

	process.setReadChannel(QProcess::StandardError);

	int countErrors(0);
	while(process.canReadLine())
	{
		QByteArray buffer(process.readLine());

		// log everything so we can easily find bugs,
		// discover player interface changes and
		// improve callback computation
		printErr( QString(buffer.simplified()).trimmed() );	
					
		// Parsing standard error

		int res;
		QString errmsg;
			
		player.parseStderr( buffer, &res, &errmsg );
					
		if ( res == Video::Parsing::FileNotFound )
		{
			qDebug() << "PlayerWidget: detected movie file not found";

			// We're done :-)
			printLog(tr("Movie not loaded, file not found."), VERBOSITY_QUIET);

			InfoBoxDialog infoBox(this);
			infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
			infoBox.setInformativeText(tr("File not found by player engine."));
			infoBox.exec();
			
			return false;
		}

		if (countErrors > preferences.maximum_loading_errors) 
		{
			qDebug() << "PlayerWidget: too many errors while loading file";

			// We're done :-)
			printLog(tr("Movie not loaded, too many player backend errors."), VERBOSITY_QUIET);

			InfoBoxDialog infoBox(this);
			infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
			infoBox.setInformativeText(tr("Too many player backend errors."));
			infoBox.exec();

			return false;
		}

		++countErrors;
	}

	if (progressBar) progressBar->stepUpdate();

	qDebug() << "PlayerWidget::loadFile() error count:" << countErrors;

	// Movie info is ready to be parsed
	qDebug() << "PlayerWidget::loadFile() reset movie and DVD info data";
	this->media = Video::Media::File;
	this->movieInfo.clear();
	this->novideo = false;

	this->name = QFileInfo(path).completeBaseName();

	this->titles = 0;  // clear eventual DVD data
	this->titleInfo.clear(); 

	audio_t audioOnly;
	audioOnly.n = 1;

	bool audioPartial = false;  // for incomplete detection

	process.setReadChannel(QProcess::StandardOutput);

	while(process.canReadLine())
	{
		QByteArray buffer(process.readLine());

		// log everything so we can easily find bugs,
		// discover player interface changes and
		// improve callback computation
		printOut( QString(buffer.simplified()).trimmed() );	
					
		// Parsing standard output

		int res;
		video_clip_t clip;
		clip.media = Video::Media::File;
			
		if (!player.parseStdoutClip( buffer, &res, &clip ))
		{
			printLog( tr("Failed to parse player output [%1]").arg(QString(buffer)), VERBOSITY_NORMAL );		
			qDebug() << "PlayerWidget: failed to parse player output" << buffer;

			continue; // we can't parse anything then
		}
					
		if ( res == Video::Parsing::VideoCodec )
		{
			movieInfo.codec = clip.codec;

			printLog( tr("Info: detected video codec [%1].").arg(clip.codec), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected video codec" << clip.codec;
		}

		if ( res == Video::Parsing::Length )
		{
			movieInfo.length = clip.length;

			printLog( tr("Info: detected movie length [%1].")
				.arg(Video::strMovieLength(clip.length)), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected movie length" << Video::strMovieLength(clip.length);
		}

		if ( res == Video::Parsing::Aspect )
		{
			movieInfo.display_aspect = clip.display_aspect;

			printLog( tr("Info: detected movie aspect ratio [%1].")
				.arg(Video::strMovieAspect(clip.display_aspect)), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected movie aspect ratio" << clip.display_aspect;
		}

		if ( res == Video::Parsing::ScaledResolution )
		{
			movieInfo.scaledRez = clip.scaledRez;

			printLog( tr("Info: detected movie scaled resolution [%1x%2].")
				.arg(clip.scaledRez.width()).arg(clip.scaledRez.height()), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected movie scaled resolution" << clip.scaledRez;
		}

		if ( res == Video::Parsing::AudioStream )
		{
			// Fill in new audio channel info
			audio_t audio;
			audio.n = clip.audio.n;
			audio.aid = clip.audio.aid;
			audio.format = clip.audio.format;
			audio.channels = clip.audio.channels;
			audio.language = clip.audio.language;
			audio.description = clip.audio.description;
			// And append to the audio track list
			movieInfo.audio.append(audio);

			printLog( tr("Info: detected audio stream %1 in this movie - id #%2 language %3 \"%4\" format %5 channels \"%6\".")
				.arg(clip.audio.n).arg(clip.audio.aid).arg(clip.audio.language)
				.arg(clip.audio.description).arg(clip.audio.format).arg(clip.audio.channels), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio stream" << clip.audio.n 
				<< "in this movie - id #" << clip.audio.aid 
				<< "language" << clip.audio.language << "description" << clip.audio.description 
				<< "format" << clip.audio.format << "channels" << clip.audio.channels;
		}

		if ( res == Video::Parsing::AudioMainStream )
		{
			// Complete audio main stream info
			if (movieInfo.audio.count() > 0)
			{
				movieInfo.audio[0].sampling = clip.audio.sampling;
				movieInfo.audio[0].kbps = clip.audio.kbps;
				movieInfo.audio[0].channel_number = clip.audio.channel_number;

				printLog( tr("Info: detected audio main stream details - %1 Hz %2 kbps %3 ch.")
					.arg(clip.audio.sampling).arg(clip.audio.kbps).arg(clip.audio.channel_number), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected audio stream details" << clip.audio.sampling << "Hz"
					<< clip.audio.kbps << "kbps" << clip.audio.channel_number << "ch.";
			}
		}

		if ( res == Video::Parsing::Subtitle )
		{
			// Fill in new subtitle info
			subtitle_t subtitle;
			subtitle.n = clip.subtitle.n;
			subtitle.sid = clip.subtitle.sid;
			subtitle.language = clip.subtitle.language;
			subtitle.format = clip.subtitle.format;
			subtitle.description = clip.subtitle.description;
			// And append to the title list
			movieInfo.subtitle.append(subtitle);

			printLog( tr("Info: detected subtitle %1 in this movie - language %2.")
				.arg(clip.subtitle.sid).arg(clip.subtitle.language), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected subtitle" << clip.subtitle.sid 
				<< "in this movie - language" << clip.subtitle.language;
		}
		
		if ( res == Video::Parsing::NoVideo )
		{
			novideo = movieInfo.novideo = clip.novideo;

			printLog( tr("Info: detected audio file."), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio file";
		}

		if ( res == Video::Parsing::AudioBitrate )
		{
			audioOnly.kbps = clip.audio.kbps;
			audioPartial = true;
					
			printLog( tr("Info: detected audio bitrate [%1].")
				.arg(Video::strMovieKbps(clip.audio.kbps)), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio bitrate" << clip.audio.kbps;
		}

		if ( res == Video::Parsing::AudioId )
		{
			audioOnly.aid = clip.audio.aid;
			audioPartial = true;

			printLog( tr("Info: detected audio ID [%1].")
					.arg(clip.audio.aid), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio ID" << clip.audio.aid;
		}

		if ( res == Video::Parsing::AudioSampling )
		{
			audioOnly.sampling = clip.audio.sampling;
			audioPartial = true;

			printLog( tr("Info: detected audio sampling [%1 Hz].")
				.arg(clip.audio.sampling), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio sampling" << clip.audio.sampling;
		}

		if ( res == Video::Parsing::AudioChannelNumber )
		{
			audioOnly.channel_number = clip.audio.channel_number;
			audioPartial = true;
					
			printLog( tr("Info: detected audio channel number [%1].")
				.arg(clip.audio.channel_number), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio channel number" << clip.audio.channel_number;
		}

		if ( res == Video::Parsing::AudioFormat )
		{
			// We don't want numerical format (85 for MP3 is meaningless for us)
			QRegExp rx("^\\d+$");
			if ( !rx.exactMatch(clip.audio.format) )
			{
				audioOnly.format = clip.audio.format;
				audioPartial = true;

				if (movieInfo.audio.count() > 0)
				{
					movieInfo.audio[0].format = clip.audio.format;
				}

				printLog( tr("Info: detected audio format [%1].")
					.arg(clip.audio.format), VERBOSITY_VERBOSE );
				qDebug() << "PlayerWidget: detected audio format" << clip.audio.format;
			}
		}

		if ( res == Video::Parsing::AudioCodec )
		{
			audioOnly.codec = clip.audio.codec;
			audioPartial = true;

			if (movieInfo.audio.count() > 0)
			{
				movieInfo.audio[0].codec = clip.audio.codec;
			}

			printLog( tr("Info: detected audio codec [%1].")
				.arg(clip.audio.codec), VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: detected audio format" << clip.audio.codec;
		}

		if ( res == Video::Parsing::Stream )
		{
			movieInfo.codec = clip.codec;
			movieInfo.bpp = clip.bpp;
			movieInfo.rez = clip.rez;
			movieInfo.display_aspect = clip.display_aspect;
			movieInfo.fps = clip.fps;
			movieInfo.kbps = clip.kbps;

			QString strCodec;

			if (clip.bpp > 0)
				strCodec = clip.codec.append(tr(" %1bpp").arg(clip.bpp));
			else
				strCodec = clip.codec;

			if (clip.display_aspect > 0)
			{	
				printLog( tr("Info: detected video format %1 [%2x%3 (%4) %5 fps %6 kbps].")
					.arg(strCodec).arg(clip.rez.width()).arg(clip.rez.height())
					.arg(Video::strMovieAspect(clip.display_aspect))
					.arg(Video::strMovieFps(clip.fps))
					.arg(Video::strMovieKbps(clip.kbps)), VERBOSITY_VERBOSE );
			}
			else
			{
				printLog( tr("Info: detected video format %1 [%2x%3 %4 fps %5 kbps].")
					.arg(strCodec).arg(clip.rez.width()).arg(clip.rez.height())
					.arg(Video::strMovieFps(clip.fps))
					.arg(Video::strMovieKbps(clip.kbps)), VERBOSITY_VERBOSE );
			}

			qDebug() << "PlayerWidget: detected video format" << strCodec
				<< "in this file - resolution" << clip.rez.width() << "x" << clip.rez.height() 
				<< "aspect" << clip.display_aspect
				<< "fps" << clip.fps 
				<< "kbps" << Video::strDvdKbps(clip.kbps);
		}
	}
	
	process.close();

	if (progressBar) progressBar->stepUpdate();

	// Audio file now supported 
	if (movieInfo.novideo && movieInfo.audio.isEmpty())
	{
		// Create the audio channel info
		movieInfo.audio.append(audioOnly);
	}
	// And some partially detected audio stream
	else if (movieInfo.audio.isEmpty())
	{

		// Add the audio channel
		movieInfo.audio.append(audioOnly);
	}

	// Ensure consistent title data (multitrack video files -like Matroska- only have 1 title)
	movieInfo.audioN = movieInfo.audio.count();
	movieInfo.subtitleN = movieInfo.subtitle.count();

	// Set scaled rezolution
	// ONLY FFPROBE IS COMPLETELY RELIABLE (for mpeg1video doing sar/dar is 1.36, expecting 1.33)
	// MPlayer bad with MPEG-1 and some MPEG-2
	// MediaInfo is wrong with H.264
	if ( !ffprobeData->video.isEmpty() )
	{
		if (ffprobeData->video[0]->codec == "mpeg1video")
		{
			movieInfo.scaledRez = QSize( int((double)movieInfo.rez.height() * 1.33),
										 movieInfo.rez.height() );
		}
		else if (ffprobeData->video[0]->display_aspect_ratio > 0.0)
		{
			movieInfo.scaledRez = QSize( int((double)movieInfo.rez.height() * ffprobeData->video[0]->display_aspect_ratio),
										 movieInfo.rez.height() );
		}

		// We adopt FFmpeg codecs convention names for File, and FOURCC for AVI
		if (ffprobeData->muxer == "avi" && !ffprobeData->video[0]->codec_tag_string.isEmpty())
		{
			movieInfo.codec = ffprobeData->video[0]->codec_tag_string;
		}
		else
		{
			movieInfo.codec = ffprobeData->video[0]->codec;
		}
	}

	scaledResolutionReference = movieInfo.scaledRez;

	// File format (muxer)
	if ( mediaInfoData->general.contains("Format") )
	{
		movieInfo.format = mediaInfoData->general.value("Format");
	}

	if (!movieInfo.novideo && movieInfo.kbps == 0.0)
	{
		// Remove " Kbps" from string
		if (mediaInfoData->video.contains("Nominal bit rate") 
			&& mediaInfoData->video.value("Nominal bit rate").endsWith(" Kbps"))
		{
			movieInfo.kbps = mediaInfoData->video.value("Nominal bit rate")
				.left(mediaInfoData->video.value("Nominal bit rate")
				.length() - 5).remove(' ').toDouble();
		}
		else if (mediaInfoData->video.contains("Bit rate"))
		{
			movieInfo.kbps = mediaInfoData->video.value("Bit rate")
				.left(mediaInfoData->video.value("Bit rate")
				.length() - 5).remove(' ').toDouble();
		}
	}

	if (!movieInfo.novideo && movieInfo.length == 0.0 && mediaInfoData->video.contains("Duration"))
	{
		QString str = mediaInfoData->video.value("Duration"); // 1h 16 mn 40s 542ms

		int h(0), mn(0), s(0), ms(0), pos;
		QRegExp rx("(\\d+)h");
		if ((pos = rx.indexIn(str)) != -1) h = rx.cap(1).toInt();

		rx.setPattern("(\\d+)mn");
		if ((pos = rx.indexIn(str)) != -1) mn = rx.cap(1).toInt();

		rx.setPattern("(\\d+)s");
		if ((pos = rx.indexIn(str)) != -1) s = rx.cap(1).toInt();

		rx.setPattern("(\\d+)ms");
		if ((pos = rx.indexIn(str)) != -1) ms = rx.cap(1).toInt();

		movieInfo.length = 3600 * h + 60 * mn + s + (double)ms / 1000.0;
	}

	if (!movieInfo.novideo && movieInfo.scaledRez.isNull()
		&& mediaInfoData->video.contains("Width") 
		&& mediaInfoData->video.contains("Height") )
	{
		if (mediaInfoData->video.value("Width").endsWith(" pixels") 
			&& mediaInfoData->video.value("Height").endsWith(" pixels"))
		{
			// Remove " pixels" from strings
			movieInfo.rez = QSize( 
				mediaInfoData->video.value("Width")
					.left(mediaInfoData->video.value("Width")
					.length() - 7).remove(' ').toInt(),
				mediaInfoData->video.value("Height")
					.left(mediaInfoData->video.value("Height")
					.length() - 7).remove(' ').toInt() );
		}
		else
		{
			printLog( tr("Info: can't parse video rez (width and height not given in pixels)."),
				VERBOSITY_VERBOSE );
			qDebug() << "PlayerWidget: can't parse video rez (width and height not given in pixels)" 
				<< mediaInfoData->video.value("Width") << mediaInfoData->video.value("Height");
		}
	}

	// Frame rate reliable in MediaInfo (avoid 24.005 fps not supported as encoders output)
	if ( mediaInfoData->video.contains("Frame rate") )
	{
		QString str = mediaInfoData->video.value("Frame rate");
		bool changeScaledRez(false);
		
		QRegExp rx("(\\d+.?\\d*)");
		rx.setMinimal(false);
		rx.indexIn(str);
		if (rx.numCaptures() == 1 && rx.cap(1).toDouble() > 0)
		{
			movieInfo.fps = rx.cap(1).toDouble();
		}
	}

	// Aspect ratio
	if ( mediaInfoData->video.contains("Original display aspect ratio") )
	{
		movieInfo.aspect_ratio = mediaInfoData->video.value("Original display aspect ratio");
	}
	else if ( mediaInfoData->video.contains("Display aspect ratio") )
	{
		movieInfo.aspect_ratio = mediaInfoData->video.value("Display aspect ratio");
	}

	// Audio streams
	if (movieInfo.audio.count() == mediaInfoData->audio.count())
	{
		for (int i=0; i < movieInfo.audio.count(); ++i)
		{
			// Audio channels more reliable in MediaInfo (mplayer shows its output, usually 2 ch.)
			if ( mediaInfoData->audio[i].contains("Channel(s)") )
			{
				QString str = mediaInfoData->audio[i].value("Channel(s)");
			
				str.truncate(str.length() - 9); // remove " channels"
				movieInfo.audio[i].channel_number = str.toInt();
			}

			// Audio channel positions only in MediaInfo
			if ( mediaInfoData->audio[i].contains("Channel positions") )
			{
				movieInfo.audio[i].channel_positions = mediaInfoData->audio[i].value("Channel positions");
			}

			// We adopt FFmpeg codecs convention names for File
			movieInfo.audio[i].codec = ffprobeData->audio[i]->codec;
		}
	}

	// Complete video info if necessary
	if (!movieInfo.novideo)
	{
		if (movieInfo.scaledRez.isNull())
		{
			movieInfo.scaledRez = (movieInfo.display_aspect > 0.0) ?
						QSize( movieInfo.rez.height() * movieInfo.display_aspect, movieInfo.rez.height() ):
						movieInfo.rez;

			scaledResolutionReference = movieInfo.scaledRez;
		}
	}

	/*experimental
	if (scaledResolutionReference.isNull())
	{
		qDebug() << "PlayerWidget: rezolution not detected (may not be playable)";

		// We're done :-)
		printLog(tr("Movie not loaded, invalid video stream (failed to detect picture width x height)."), VERBOSITY_QUIET);

		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to load movie from:\n%1").arg(path));
		infoBox.setInformativeText(tr("Invalid video stream (failed to detect picture width x height)."));
		infoBox.exec();
		return false;
	}
	*/

	// We're done :-)
	if (progressBar) progressBar->stepUpdate();
	renderBackground->setVisible(movieInfo.novideo);
	printLog(tr("Movie loaded, ready to play [%1].").arg(this->name), VERBOSITY_QUIET);

/*
	// And complete movie info
	if ( mediaInfoData->video.contains("Standard") )
	{
		movieInfo.standard =  mediaInfoData->video.value("Standard");
	}

	if ( mediaInfoData->video.contains("Scan type") )
	{
		if ( mediaInfoData->video.value("Scan type") == "Interlaced" )
		{
			movieInfo.interlaced = 1;
		}
		else if ( mediaInfoData->video.value("Scan type") == "Progressive" )
		{
			movieInfo.interlaced = 0;
		}
	}

	if ( mediaInfoData->video.contains("Scan order") )
	{
		if ( mediaInfoData->video.value("Scan order") == "Bottom Field First" )
		{
			movieInfo.scan_order = 0;
		}
		else if ( mediaInfoData->video.value("Scan order") == "Top Field First" )
		{
			movieInfo.scan_order = 1;
		}
		else if ( mediaInfoData->video.value("Scan order") == "2:3 Pulldown" )
		{
			movieInfo.scan_order = 2;
		}
		else if ( mediaInfoData->video.value("Scan order") == "2:2:2:2:2:2:2:2:2:2:2:3 Pulldown" )
		{
			movieInfo.scan_order = 3;
		}
	}

	if ( movieInfo.pixel_aspect == 0.0 && movieInfo.display_aspect != 0.0
		&& mediaInfoData->video.contains("Width") && mediaInfoData->video.contains("Height") )
	{
		int width = mediaInfoData->video.value("Width").section(" ", 0, 0).toInt();
		int height = mediaInfoData->video.value("Height").section(" ", 0, 0).toInt();
		
		movieInfo.pixel_aspect = movieInfo.display_aspect * height / width;
	}

	// Audio streams are read in same order by MPlayer and MediaInfo
	for (int i = 0; i < movieInfo.audioN; i++)
	{
		if ( mediaInfoData->audio[i].contains("Channel(s)") )
		{
			movieInfo.audio[i].channel_number = mediaInfoData->audio[i].value("Channel(s)").section(" ", 0, 0).toInt();
		}

		if ( mediaInfoData->audio[i].contains("Channel positions") )
		{
			movieInfo.audio[i].channel_positions = mediaInfoData->audio[i].value("Channel positions");
		}

		if ( mediaInfoData->audio[i].contains("Sampling rate") )
		{
			movieInfo.audio[i].sampling = mediaInfoData->audio[i].value("Sampling rate").section(" ", 0, 0).toDouble();
		}

		if ( mediaInfoData->audio[i].contains("Stream size") )
		{
			movieInfo.audio[i].size = mediaInfoData->audio[i].value("Stream size").section(" ", 0, 0).toDouble();
		}
	}

	// Subtitle streams are read in same order by MPlayer and MediaInfo
	for (int i = 0; i < movieInfo.subtitleN; i++)
	{
		if ( mediaInfoData->subtitle[i].contains("ID") )
		{
			movieInfo.subtitle[i].id = mediaInfoData->subtitle[i].value("ID").section(" ", 0, 0).toInt();
		}
	}
*/
	return true;
}

void PlayerWidget::setRunPlaying(const bool play)
{
	runPlaying = play;
}

bool PlayerWidget::start()
{
	if (runPlaying)
		return true;

	if ( playerProcess->state() != QProcess::NotRunning )
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to start playback."));
		infoBox.setInformativeText(tr("Player is not ready."));
 		infoBox.exec();

		return false;
	}

	printLog( tr("Start playback."), VERBOSITY_VERBOSE );

	qDebug("Start playback.");

	// Reset playback errors
	playbackErrors->clear();

	// Check source
	QFileInfo info(movie);
	if (!info.exists())
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to start playback from:\n%1").arg(movie));
		infoBox.setInformativeText(tr("This movie path doesn't exist."));
		infoBox.exec();
		return false;
	}

	timecodeDisplay->clear();
	
	bool vp8WorkAround(false), muteAudioWarning(false);

	// Prepare player with DVD playback data
	if ( media == Video::Media::DVD )
	{
		refChapter = 0;
		refPosition = 0.0;

		chapterDisplay->setText("_");
		chapterDisplay->setVisible(true);
	
		if (title < 1)
		{
			printLog( tr("Invalid title number %1 forced to 1 for playback.")
						.arg(title), VERBOSITY_VERBOSE );
			title = 1;
		}

		player->setTitle(title);

		if (titleInfo[title -1].length > 0.0) timeLine->setMaximum( (int)titleInfo[title -1].length + 1);

		if (audio > -1 && titleInfo[title -1].audio.count() > 0)
		{
			player->setAudioId(titleInfo[title -1].audio[audio].aid);  // alang is amibuous
			player->setSoundOn();

#ifdef Q_OS_LINUX
			if (titleInfo[title -1].audio[audio].codec.contains("amr")
				||  titleInfo[title -1].audio[audio].codec.contains("flac"))
			{
				muteAudioWarning = true;
			}
#endif
		}
		else
		{
			player->setSoundOff();
		}

		if (subtitle > -1 && titleInfo[title -1].subtitle.count() > 0)
		{
			player->setSubtitleId(titleInfo[title -1].subtitle[subtitle].sid); // slang is amibuous
			player->setSubtitleOn();
		}
		else
		{
			player->setSubtitleOff();
		}

		if (angle > 1)
			player->setAngle(angle);

		if (!sliceCut.isEmpty())
		{
			player->setSliceCut(&sliceCut);
		}

		if (titleInfo[title -1].codec == "vp8" || titleInfo[title -1].codec == "VP8")
		{
			vp8WorkAround = true;
		}
	}
	// Prepare player with File playback data
	else if ( media == Video::Media::File )
	{
		chapterDisplay->setVisible(false);
#ifdef WIN32
#pragma message (ToBeDone "support MKV chapters - " __FUNCTION__)
#endif
		
		if (title > -1)
		{
			player->setVideoOn();

			// Rescale the timeline
			//if (movieInfo.length > 0.0) timeLine->setMaximum(static_cast<int>(movieInfo.length + 1));
			if (movieInfo.length > 0.0) timeLine->setMaximum( (int)movieInfo.length + 1);
		}
		else
		{
			player->setVideoOff();
		}

		if (audio > -1 && movieInfo.audio.count() > 0)
		{
			player->setAudioId(movieInfo.audio[audio].aid);
			player->setSoundOn();

			// Rescale the timeline
			//if (movieInfo.length > 0.0) timeLine->setMaximum(static_cast<int>(movieInfo.length + 1));
			if (movieInfo.length > 0.0) timeLine->setMaximum( (int)movieInfo.length + 1);

#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
			if (movieInfo.audio[audio].codec.contains("amr")
				||  movieInfo.audio[audio].codec.contains("flac"))
			{
				muteAudioWarning = true;
			}
#endif

			/* experimental FLAV plyed by MPlayer Sherpya-SVN-r33216-4.2.5
#if defined(WIN32)
			if (movieInfo.audio[audio].codec.contains("flac"))
			{
				muteAudioWarning = true;
			}
#endif
			*/
		}
		else
		{
			player->setSoundOff();
		}

		if (subtitle > -1 && movieInfo.subtitle.count() > 0)
		{
			player->setSubtitleId(movieInfo.subtitle[subtitle].sid);
			player->setSubtitleOn();
		}
		else
		{
			player->setSubtitleOff();
		}

		if (!sliceCut.isEmpty())
		{
			player->setSliceCut(&sliceCut);
		}

		if (movieInfo.codec == "vp8" || movieInfo.codec == "VP8")
		{
			vp8WorkAround = true;
		}
	}
	else
	{
		printLog( tr("Media %1 (%2) not supported, forced to 0 (Video::Media::File) for basic playback.")
				.arg(media).arg(Video::strMedia(media)), VERBOSITY_VERBOSE );
		media = Video::Media::File;
	}

	if (muteAudioWarning)
	{
		InfoBoxDialog alertBox(this);
		alertBox.setTextFormat(Qt::RichText);
		alertBox.setText(tr("<u>Player limitation warning.</u>"));
		alertBox.setInformativeText(tr(	"Currently DVDx 4 player isn't playing AMR-NB nor FLAC audio.<br><br>" 
										"Even if the audio stream is correct you aren't going to ear it. "
										"You may try to play the file in VLC media player.") );
		alertBox.exec();
	}

	// Get player command line
	QString *cmd = new QString;
	QStringList *args = new QStringList;;

	// Refresh WId as it's better to set it the later
#if defined(WIN32)
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	// Reinterpret_cast<qlonglong> is mandatory, winId() not kind enough when being converted ;)
	player->setWid(QString::number(reinterpret_cast<qlonglong>(renderTarget->winId())));
#elif defined(Q_OS_MAC)
	// MPlayer displays in its on window on Mac
	// On Mac OS X, the type returned depends on which framework Qt was linked against. 
	// If Qt is using Carbon, the {WId} is actually an HIViewRef. If Qt is using Cocoa, 
	// {WId} is a pointer to an NSView.
	// May work on Mac if renderTarget is a QWidget (see Phonon WidgetNoPaintEvent)
	// player.setWid(QString::number(renderTarget->winId()));
#else	// UNIX and !MAC
	qDebug() << "PlayerWidget: set Player Wid" << renderTarget->winId();
	player->setWid(QString::number(renderTarget->winId()));
#endif

#ifndef WIN32
	if (vp8WorkAround) player->setWid(0);
#endif

	int frames = preferences.test_mode ? preferences.test_frames : 0;    // 0: no limit
	int chapter = preferences.test_mode ? preferences.test_chapter : 0;  // chapter to start from (0: very beginning)
	int seek = preferences.test_mode ? preferences.test_position : 0;  // chapter to start from (0: very beginning)

	// External renderer on Mac
#ifdef Q_OS_MAC
	int externalRendererTop(0), externalRendererLeft(0), externalRendererWidth(0);

	if (mainConsole)
	{
		externalRendererLeft += mainConsole->x();
		externalRendererLeft += (mainConsole->width() - scaledResolution.width()) / 2;

		externalRendererTop = mainConsole->y() + mainConsole->y()
							- mainConsole->geometry().y()
							- scaledResolution.height();

		externalRendererWidth = mainConsole->width();
	}
	else
	{
		externalRendererLeft += parentWidget()->frameGeometry().x();
		externalRendererLeft += (parentWidget()->frameGeometry().width() - scaledResolution.width()) / 2;

		externalRendererTop = parentWidget()->y() + parentWidget()->y()
							- parentWidget()->geometry().y()
							- scaledResolution.height();

		externalRendererWidth = parentWidget()->frameGeometry().width();
	}

	QDesktopWidget desktop;
	QRect dkrect = desktop.availableGeometry(desktop.screenNumber(this));

	if ( externalRendererTop < 0 )
	{
		int extremeRight = mainConsole ? 
				mainConsole->x() + mainConsole->width() + externalRendererWidth:
				parentWidget()->x() + parentWidget()->frameGeometry().width() + externalRendererWidth;

		int extremeLeft = mainConsole ? 
				mainConsole->x() - scaledResolution.width():
				parentWidget()->x() - scaledResolution.width();

		if (extremeRight < (dkrect.right() - 1))
		{
			// Display external renderer at right
			externalRendererTop = mainConsole ? 
				mainConsole->y() + mainConsole->y() - mainConsole->geometry().y(): 
				parentWidget()->y() + parentWidget()->y() - parentWidget()->geometry().y();

			externalRendererLeft = mainConsole ? mainConsole->x() + mainConsole->width() + 1:
				parentWidget()->x() + parentWidget()->frameGeometry().width() + 1;
		}
		else if (extremeLeft >= 2)
		{
			// Display external renderer at left
			externalRendererTop = mainConsole ? 
				mainConsole->y() + mainConsole->y() - mainConsole->geometry().y(): 
				parentWidget()->y() + parentWidget()->y() - parentWidget()->geometry().y();

			externalRendererLeft = extremeLeft - 2;
		}
	}

	player->setExternalRenderer( externalRendererLeft, externalRendererTop, externalRendererWidth);
#endif

	player->cliCmdPlay(cmd, args, frames, chapter, seek, preferences.volume_correction);

	// Start!
	QString strIn(*cmd);
	strIn.append(" ").append(args->join(" "));
	standardInput->append(strIn);

	qDebug() << "Start player process" << *cmd << *args << "in new process...";

	poller->blockSignals(false);

	playerProcess->start(*cmd, *args, QProcess::Unbuffered | QProcess::ReadWrite);
	playerProcess->blockSignals(false);

	if (!playerProcess->waitForStarted( preferences.latency_start ))
	{
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Failed to start playback."));
		infoBox.setInformativeText(tr("Please check %1 installation and player settings.")
			.arg(QApplication::applicationName()));
		infoBox.setDetailedText(strIn);
		infoBox.exec();

		printLog(tr("Failed to start playback."), VERBOSITY_QUIET);
		return false;
	}

	printLog( tr("Playback started."), VERBOSITY_VERBOSE );
	qDebug("Playback started.");

	// Get basic infos (required for timeline scaling, etc.)
	//*experimental: commented cause useless (all in titleInfo or movieInfo
	//playerProcess->write( player->cliTimeLength().toUtf8() );  // for timeline scaling
	//playerProcess->write( player->cliVideoResolution().toUtf8() );
	playerProcess->write( player->cliDeinterlace().toUtf8() );
	playerProcess->write( player->cliAvert(tr("Preview mode"), 3).toUtf8() );
	playerProcess->write( player->cliShowProgress().toUtf8() );

	poller->start( preferences.poll_ticks );

	setTransparentBackground(true);
	playSpeed = 1;
	playPause = false;
	playPos = 0;

	pauseButton->setEnabled(true);
	slowerButton->setEnabled(true);
	fasterButton->setEnabled(true);
	prevChapterButton->setEnabled(!preferences.test_mode);
	nextChapterButton->setEnabled(!preferences.test_mode);
	prevButton->setEnabled(!preferences.test_mode);
	nextButton->setEnabled(!preferences.test_mode);

	return true;
}

bool PlayerWidget::stop()
{
	if (!runPlaying)
		return true;

	printLog( tr("Stop playback.") );
	qDebug("Stop playback.");

	playerProcess->write( player->cliStop().toUtf8() );

	if (!playerProcess->waitForFinished( preferences.latency_finished ))
	{
		qDebug("Failed to stop player backend softly, waitForFinished() has timed out.");
		printLog( tr("Failed to stop playback softly, kill player backend process."), VERBOSITY_VERBOSE );

		poller->blockSignals(true);
		poller->stop();

		QCoreApplication::processEvents();
		playerProcess->blockSignals(true);
		playerProcess->close();
	}
	else
	{
		poller->blockSignals(true);
		poller->stop();

		QCoreApplication::processEvents();
		playerProcess->blockSignals(true);
	}

	timeLine->setValue(0);
	pauseButton->setEnabled(false);
	slowerButton->setEnabled(false);
	fasterButton->setEnabled(false);
	prevChapterButton->setEnabled(false);
	nextChapterButton->setEnabled(false);

	return true;
}

void PlayerWidget::pollCurrentTime()
{
	if ( media == Video::Media::DVD ) playerProcess->write( player->cliChapter().toUtf8() );

	playerProcess->write( player->cliTimePosition().toUtf8() );
}

void PlayerWidget::timeLineChanged(int pos)
{
	playerProcess->write( player->cliSeek(pos).toUtf8() );
}

void PlayerWidget::monitorPlaybackErrors()
{
	bool exceedLimit(false);
	
	if ( playbackErrors->count() > preferences.maximum_errors ) exceedLimit = true;

	QStringListIterator errorIterator(*playbackErrors);
	while (errorIterator.hasNext())
	{
		if ( playbackErrors->count(errorIterator.next()) > preferences.maximum_repetead_errors )
		{
			exceedLimit = true;
			break;
		}
	}

	if (exceedLimit)
	{
		printLog( tr("Player error: too many playback errors."), VERBOSITY_QUIET );
		stopRequest();
		
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Playback aborted"));
		infoBox.setInformativeText(tr("Player received too many palyback errors from."));
		infoBox.exec();
	}
}

void PlayerWidget::centerRenderer()
{
#ifdef Q_OS_MAC
	if ( zoom == 0 )
	{
		setZoom(0);
	}
	else
	{
		setZoom(zoom);
	}

	return;
#endif

	// Adjust render to movie
	renderTarget->setFixedSize( scaledResolution );

	// Center render in TV
	int moveX(0), moveY(0);

	if ( renderTarget->width() < renderTV->width() )
	{
		moveX = ( renderTV->width() - renderTarget->width() ) / 2;
	}

	if ( renderTarget->height() < renderTV->height() )
	{
		moveY = ( renderTV->height() - renderTarget->height() ) / 2;
	}

	renderTarget->move( moveX, moveY );

	renderBackground->setFixedSize( scaledResolution );
	renderBackground->move( 0, 0 );

	if ( renderBackground->width() < audioPixmap->width() 
		|| renderBackground->height() < audioPixmap->height() )
	{
		renderBackground->setPixmap( audioPixmap->scaled(renderBackground->size(), Qt::KeepAspectRatio) );
	}
	else
	{
		renderBackground->setPixmap( *audioPixmap );
	}

	emit guiSizeAdjustRequest();
}

void PlayerWidget::updateControlsOnLoading(int loaded)
{
	// loaded = 0:NotLoaded, 1:Loading, 2:Loaded

	switch (loaded )
	{
	case Qt::Unchecked:			// Not loaded
	case Qt::PartiallyChecked:	// Loading
		clearTrackControls();
		enablePlaybackControls(false);
		enableTrackControls(false);
		trackGroup->setTitle(tr("Stream selection"));
		break;
	case Qt::Checked:			// Loaded
		clearTrackControls();
		enablePlaybackControls(true);
		if (media == Video::Media::DVD)
		{
			populateTrackControls();
		 	trackGroup->setTitle(tr("DVD title selection"));
			enableTrackControls(true);
		}
		else if (media == Video::Media::File )
		{
			populateTrackControls();
		 	trackGroup->setTitle(tr("Stream selection"));
			enableTrackControls(true);
		}
		else
		{
		 	trackGroup->setTitle(tr("Stream selection"));
			enableTrackControls(false);
		}
		break;
	default:
		qDebug() << "PlayerWidget::updateOnLoad() unexpected state" << loaded << "(ignored)";
	}
}

void PlayerWidget::switchState()
{
	if (!runNormalSpeed)
	{
		playerProcess->write( player->cliSetSpeedNormal().toUtf8() );
		enablePlaybackControls(true); // refresh state of all playback controls
		if ( preferences.test_mode )
		{
			controller->setIcon(*stopExcerptIcon);
		}
		else
		{
			controller->setIcon(*stopIcon);
		}
		controller->setToolTip(tr("Stop playback"));
		runNormalSpeed = true;
	}
	else if (!runPlaying)
	{
		if (!start())
		{
			printLog(tr("Can't start playback [failed to start player]."), VERBOSITY_QUIET);
			return;
		}

		timeLine->setEnabled(true);
		timecodeDisplay->setEnabled(true);
		chapterDisplay->setEnabled(true);

		setTransparentBackground(true);
		setRunPlaying(true);
		runNormalSpeed = true;

		if ( preferences.test_mode )
		{
			controller->setIcon(*stopExcerptIcon);
		}
		else
		{
			controller->setIcon(*stopIcon);
		}
		controller->setToolTip(tr("Stop playback"));
		enablePlaybackControls(true); // refresh state of all playback controls
	}
	else
	{
		if (!stop())
		{
			printLog(tr("Can't stop playback [player process may be still running]."), VERBOSITY_NORMAL);
			return;
		}

		timeLine->setEnabled(false);
		timecodeDisplay->setEnabled(false);
		chapterDisplay->setEnabled(false);

		setTransparentBackground(false);
		runNormalSpeed = true;

		decorateController();
		enablePlaybackControls(true); // refresh state of all playback controls
	}

	emit stateChanged( runPlaying );
}

void PlayerWidget::switchPause()
{
	if (!playPause)
	{
		playerProcess->write( player->cliSwitchPause().toUtf8() );
		qDebug("PlayerWidget: pause");
		playPause = true;
		pauseButton->setText(tr("Resume"));
	}
	else
	{
		playerProcess->write( player->cliSwitchPause().toUtf8() );
		qDebug("PlayerWidget: resume");
		playPause = false;
		pauseButton->setText(tr("Pause"));
	}
}

void PlayerWidget::switchFaster()
{
	if (!runPlaying || playPause)
		return;

	playerProcess->write( player->cliSetSpeedFaster().toUtf8() );

	playerProcess->write( player->cliSpeed().toUtf8() );  // to catch max speed and avoid annoying increments
}

void PlayerWidget::switchSlower()
{
	if (!runPlaying || playPause)
		return;

	playerProcess->write( player->cliSetSpeedSlower().toUtf8() );
		
	playerProcess->write( player->cliSpeed().toUtf8() );  // to catch max speed and avoid annoying increments
}

void PlayerWidget::prevChapter()
{
	if (!runPlaying || playPause)
		return;

	QString command;

	if (media == Video::Media::DVD)
	{
		command = player->cliSeekPrev();
	}
	else
	{
		int jump = (int)(movieInfo.length / 10.0);
		if (jump < 30) jump = 30;

		if (((int)refPosition - jump) > 0) command = player->cliSeekPrev( jump );
	}

	playerProcess->write( command.toUtf8() );
}

void PlayerWidget::nextChapter()
{
	if (!runPlaying || playPause)
		return;

	QString command;

	if (media == Video::Media::DVD)
	{
		command = player->cliSeekNext();
	}
	else
	{
		int jump = (int)(movieInfo.length / 10.0);
		if (jump < 30) jump = 30;

		if (((int)refPosition + jump) < movieInfo.length) command = player->cliSeekNext( jump );
	}

	playerProcess->write( command.toUtf8() );
}

void PlayerWidget::prev()
{
	if (!runPlaying || playPause)
		return;

	QString command;
	double length = media == Video::Media::DVD ? titleInfo[title - 1].length : movieInfo.length;

	int jump = (int)(length / 100.0);
	if (jump < 10) jump = 10;

	if (((int)refPosition - jump) > 0) command = player->cliSeekPrev( jump );

	playerProcess->write( command.toUtf8() );
}

void PlayerWidget::next()
{
	if (!runPlaying || playPause)
		return;

	QString command;
	double length = media == Video::Media::DVD ? titleInfo[title - 1].length : movieInfo.length;

	int jump = (int)(length / 100.0);
	if (jump < 10) jump = 10;

	if (((int)refPosition + jump) < length) command = player->cliSeekNext( jump );

	playerProcess->write( command.toUtf8() );
}

void PlayerWidget::playRequest()
{
	if (!runPlaying)
	{
		if (!start())
		{
			printLog(tr("Can't start playback [failed to start player]."), VERBOSITY_QUIET);
			return;
		}

		timeLine->setEnabled(true);
		timecodeDisplay->setEnabled(true);
		chapterDisplay->setEnabled(true);

		setTransparentBackground(true);
		setRunPlaying(true);
		runNormalSpeed = true;

		if ( preferences.test_mode )
		{
			controller->setIcon(*stopExcerptIcon);
		}
		else
		{
			controller->setIcon(*stopIcon);
		}
		controller->setToolTip(tr("Stop playback"));
		enablePlaybackControls(true); // refresh state of all playback controls
	}

	emit stateChanged( runPlaying );
}

void PlayerWidget::stopRequest()
{
	if (runPlaying)
	{
		if (!stop())
		{
			printLog(tr("Can't stop playback [player process may be still running]."), VERBOSITY_NORMAL);
			return;
		}

		decorateController();
		enablePlaybackControls(true); // refresh state of all playback controls

		timeLine->setEnabled(false);
		timecodeDisplay->setEnabled(false);
		chapterDisplay->setEnabled(false);

		setTransparentBackground(false);
		runNormalSpeed = true;
	}

	emit stateChanged( runPlaying );
}

void PlayerWidget::selectTitleByUser(int item)
{
	selectTitle(item);

	novideo = (title == -1); 
	
	emit selectionChanged();
}

void PlayerWidget::selectTitle(int item)
{
	if (item < 0 || isMovieLoaded->checkState() != Qt::Checked)
		return;

	title = titleList->itemData(item).toInt();

	if (runPlaying)
	{
		stop();
		emit selectionChanged();
	}

	if (media == Video::Media::DVD)
	{
		novideo =  (title == -1); 

		if (title > 0) scaledResolutionReference = titleInfo[title - 1].scaledRez;

		// Update audio, subs and angles in this current title
		populateTrackControlsAud(title);
		populateTrackControlsSub(title);
		populateTrackControlsAng(title);
	}
	else
	{
		bool b_isLoaded = isMovieLoaded->checkState() == Qt::Checked;
		bool b_isReady = isReady();
	
		int i_count = titleList->count();

		if (title > 0) scaledResolutionReference = movieInfo.scaledRez;

		//novideo = (title == -1); 
	}

	renderBackground->setVisible(novideo);

	if (title > 0)
	{
		setZoom(zoom);
	}

	//*experimental not working
	//if (runPlaying && media == Video::Media::DVD)
	//{
	//	player->setTitle(title);
	//	playerProcess->write( player->cliSelectTitle(title).toUtf8() );
	//}

	emit selectionChanged();
}

void PlayerWidget::selectAudio(int item)
{
	if (item < 0)
		return;

	audio = audioList->itemData(item).toInt();

	// Changing audio while playin isn not sure, except no audio (which mute)
	//if (runPlaying && audio != -1)
	//{
	//	stop();
	//	emit selectionChanged();
	//}

	if (runPlaying && media == Video::Media::DVD)
	{
		if (audio < 0)
		{
			player->setAudioId(-1);
			playerProcess->write( player->cliSelectAudio(-1).toUtf8() );
		}
		else
		{
			player->setAudioId(titleInfo[title - 1].audio[audio].aid);
			playerProcess->write( player->cliSelectAudio(audio).toUtf8() );
		}
	}

	emit selectionChanged();
}

void PlayerWidget::selectSubtitle(int item)
{
	if (item < 0)
		return;

	subtitle = subtitleList->itemData(item).toInt();

	if (runPlaying && media == Video::Media::DVD)
	{
		if (subtitle < 0)
		{
			player->setSubtitleId(-1);
			playerProcess->write( player->cliSelectSubtitle(-1).toUtf8() );
		}
		else
		{
			player->setSubtitleId(titleInfo[title - 1].subtitle[subtitle].sid);
			playerProcess->write( player->cliSelectSubtitle(subtitle).toUtf8() );
		}
	}

	emit selectionChanged();
}

void PlayerWidget::selectAngle(int item)
{
	if (item < 0)
		return;

	//if (runPlaying)
	//	 stop();

	angle = angleList->itemData(item).toInt();

	emit selectionChanged();
}

void PlayerWidget::setListToolTips()
{
	titleList->setToolTip( titleList->itemText( titleList->currentIndex() ) );
	audioList->setToolTip( audioList->itemText( audioList->currentIndex() ) );
	subtitleList->setToolTip( subtitleList->itemText( subtitleList->currentIndex() ) );
	angleList->setToolTip( angleList->itemText( angleList->currentIndex() ) );
}

void PlayerWidget::readStandardOutput()
{
	playerProcess->setReadChannel(QProcess::StandardOutput);

	while(playerProcess->canReadLine())
	{
		// This loop can be long and doesn't run in its own thread
		QCoreApplication::processEvents();  // we want to show message immediately

		// Parsing standard output
		QByteArray buffer(playerProcess->readLine());

		int res;
		mplayer_video_running_t run;
			
		if (!player->parseStdoutRun( buffer, &res, &run ))
		{
			printLog( tr("Failed to parse player output [%1]").arg(QString(buffer)), VERBOSITY_NORMAL );		
			qDebug() << "PlayerWidget: failed to parse player output" << buffer;

			continue; // we can't parse anything then
		}

		// Log all but 'Position' and 'Chapter' which are too many
		if ( res != Video::Parsing::Position && res != Video::Parsing::Chapter )
		{
			printOut( QString(buffer.simplified()).trimmed() );
		}

		// Position: change timeline
		if ( res == Video::Parsing::Position )
		{
			// Update the timeline
			timeLine->setValue(static_cast<int>(run.position + 1));
			//timeLine->setMaximum( (int)run.length + 1);
#ifdef WIN32
#pragma message (ToBeDone "timeline update avoid some user timeline moves, should be improved - " __FUNCTION__)
#endif

			QTime time(0, 0, 0);
			double correction = 0.0;

			if ( media == Video::Media::DVD )
			{
				// Change reference chapter ?
				if (run.position <= refPosition)
				{
					refChapter = chapter;

					while ( refChapter > 0 && chapter < titleInfo[title - 1].chapter.count()
							&& (run.position + titleInfo[title - 1].chapter[refChapter])
								> titleInfo[title - 1].chapter[chapter + 1] ) --refChapter;
				}
				correction = titleInfo[title - 1].chapter[refChapter];

				qDebug() << "PlayerWidget: DVD time code correction" << time.toString("hh:mm:ss")
					<< "position, chapter, correction:" << run.position << chapter << correction;
			}
			refPosition = run.position;
			time = time.addSecs( correction + run.position );
			timecodeDisplay->display( time.toString("hh:mm:ss") );

			qDebug() << "PlayerWidget: set time code" << time.toString("hh:mm:ss");
		}
		else if ( res == Video::Parsing::Chapter )
		{
			// Update the timeline
			chapter = static_cast<int>(run.chapter);

			if (chapter)
				chapterDisplay->setNum( chapter );
			else
				chapterDisplay->setText( "_" );

			qDebug() << "PlayerWidget: current chapter" << run.chapter;
		}
		// Speed: adjust real speed give by player
		else if ( res == Video::Parsing::Speed )
		{
			// Udate controller
			runNormalSpeed = (run.speed == 1.0) ? true : false;

			if (runNormalSpeed)
			{
				if ( preferences.test_mode )
					controller->setIcon(*stopExcerptIcon);
				else
					controller->setIcon(*stopIcon);

					controller->setToolTip(tr("Stop playback"));
			}
			else
			{
				if ( preferences.test_mode )
				{
					controller->setIcon(*playExcerptFastIcon);
				}
				else
				{
					controller->setIcon(*playFastIcon);
				}

				controller->setToolTip(tr("Normal speed"));
			}

			qDebug() << "PlayerWidget: get real playback speed" << run.speed;
		}
		// Length: change timeline (less reliable than MediaInfo, so only if not already set)
		else if ( res == Video::Parsing::Length && timeLine->maximum() == 0)
		{
			// Rescale the timeline
			//timeLine->setMaximum(static_cast<int>(run.length + 1));
			timeLine->setMaximum( (int)run.length + 1);

			qDebug() << "PlayerWidget: get movie length info while playback" << run.length;
		}
		// ExitPlayback: redundant with Ended event
		else if ( res == Video::Parsing::ExitPlayback )
		{
			printLog( tr("Warning: playback ended."), VERBOSITY_VERBOSE );	

			qDebug() << "PlayerWidget: playback ended (process exited).";
		}
	}
}

void PlayerWidget::readStandardError()
{
	QString strError;

	playerProcess->setReadChannel(QProcess::StandardError);

	while(playerProcess->canReadLine())
	{
		// Ths loop can be long and doesn't run in its own thread
		QCoreApplication::processEvents();  // we want to show message immediately

		QByteArray buffer(playerProcess->readLine());

		// Store all playback errors to all parse at once
		strError.append(buffer);

		QString cleanBufferStr( buffer.simplified().trimmed() );
		printErr(cleanBufferStr);	// log everything so we can easily find bugs,
															// discover player interface changes and
															// improve callback computation
		// Store errors to count them
		playbackErrors->append(cleanBufferStr);

		qDebug() << "Playback StdErr:" << cleanBufferStr;
	}

	// Parse all playback errors at once
	if ( strError.contains("MPlayer interrupted")
		|| strError.contains("MPlayer crashed") )
	{
		// Rezolution too high:
		// MPlayer interrupted by signal 11 in module: decode_video
		// - MPlayer crashed by bad usage of CPU/FPU/RAM.
		printLog( tr("Player error: playback failed."), VERBOSITY_QUIET );
		stopRequest();
		
		InfoBoxDialog infoBox(this);
		infoBox.setText(tr("Playback aborted"));
		infoBox.setInformativeText(tr("Player process crashed, try to change display zoom to lower value."));
		infoBox.exec();
	}
}

void PlayerWidget::playerEnded(int exitCode, QProcess::ExitStatus exitStatus)
{
	poller->blockSignals(true);
	playerProcess->blockSignals(true);

	poller->stop();

	setTransparentBackground(false);
	setRunPlaying(false);
	decorateController();
	enablePlaybackControls(true); // refresh state of all playback controls

	printLog(tr("Playback ended."), VERBOSITY_QUIET);

	qDebug() << "Playback ended.";

	emit stateChanged( runPlaying );
}

player_engine_status_t PlayerWidget::readStatus()
{
	return (*playerStatus);
}

utility_status_t PlayerWidget::readMediaInfoStatus()
{
	return (*mediaInfoStatus);
}

interfacemediainfo_data_t PlayerWidget::readMediaInfoData()
{
	return (*mediaInfoData);
}

utility_status_t PlayerWidget::readFFprobeStatus()
{
	return (*ffprobeStatus);
}

interfaceffprobe_t PlayerWidget::readFFprobeData()
{
	return (*ffprobeData);
}

player_engine_capability_t PlayerWidget::readCapability()
{
	return (*playerCapability);
}

void PlayerWidget::printLog(const QString &message, const int verbosityLevel)
{
	if ( log == 0 )
		qDebug() << "PlayerWidget Log:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		log->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void PlayerWidget::printIn(const QString &message, const int verbosityLevel)
{
	if ( standardInput == 0)
		qDebug() << "PlayerWidget stdIn:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		standardInput->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void PlayerWidget::printOut(const QString &message, const int verbosityLevel)
{
	if ( standardOutput == 0)
		qDebug() << "PlayerWidget stdOut:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		standardOutput->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void PlayerWidget::printErr(const QString &message, const int verbosityLevel)
{
	if ( standardError == 0)
		qDebug() << "PlayerWidget stdErr:" << message;
	else if ( verbosityLevel <= verbosity )
	{
		standardError->append( message );
		QCoreApplication::processEvents();  // we want to show message immediately
	}
}

void PlayerWidget::clearLog()
{
	if ( log != 0)
		log->clear();
}

void PlayerWidget::clearIn()
{
	if ( standardInput != 0)
		standardInput->clear();
}

void PlayerWidget::clearOut()
{
	if ( standardOutput != 0)
		standardOutput->clear();
}

void PlayerWidget::clearErr()
{
	if ( standardError != 0)
		standardError->clear();
}

void PlayerWidget::setTransparentBackground(const bool yes)
{
	QPalette palette;

	if (yes)
		palette.setColor(QPalette::Background, Qt::transparent);
	else
		palette.setColor(QPalette::Background, Qt::black);

	renderTarget->setPalette(palette);
}

void PlayerWidget::streamCount(int *Nvideo, int *Naudio, int *Nsubtitle)
{
	if (media == Video::Media::DVD && title > 0)
	{
		*Nvideo = 1;
		*Naudio = titleInfo[title -1].audioN;
		*Nsubtitle = titleInfo[title -1].subtitleN;;
	}
	else if (media == Video::Media::File)
	{
		*Nvideo = movieInfo.novideo ? 0 : 1;
		*Naudio = movieInfo.audioN;
		*Nsubtitle = movieInfo.subtitleN;
	}
	else
	{
		*Nvideo = 0;
		*Naudio = 0;
		*Nsubtitle = 0;
	}
}

video_t PlayerWidget::currentVideo()
{
	video_t video;

	if (media == Video::Media::DVD && title > 0)
	{
		video.media = Video::Media::DVD;
		video.title = title;
		video.angle = angle;
		video.format = titleInfo[title - 1].format;
		video.codec = titleInfo[title - 1].codec;
		video.rez = titleInfo[title - 1].rez;
		video.scaledRez = titleInfo[title - 1].scaledRez;
		video.standard = titleInfo[title - 1].standard;
		video.interlaced = titleInfo[title - 1].interlaced;
		video.scan_order = titleInfo[title - 1].scan_order;
		video.display_aspect = Video::valDvdAspect( titleInfo[title - 1].mpeg_aspect );
		video.pixel_aspect = titleInfo[title - 1].pixel_aspect;
		video.fps = Video::valDvdFps( titleInfo[title - 1].mpeg_fps );
		video.kbps = titleInfo[title - 1].kbps;
		video.length = titleInfo[title - 1].length;
	}
	else if (media == Video::Media::File)
	{
		video.media = movieInfo.novideo ? 0 : Video::Media::File;
		video.format = movieInfo.format;
		video.codec = movieInfo.codec;
		video.bpp = movieInfo.bpp;
		video.rez = movieInfo.rez;
		video.scaledRez = movieInfo.scaledRez;
		video.standard = movieInfo.standard;
		video.interlaced = movieInfo.interlaced;
		video.scan_order = movieInfo.scan_order;
		video.display_aspect = movieInfo.display_aspect;
		video.pixel_aspect = movieInfo.pixel_aspect;
		video.fps = movieInfo.fps;
		video.kbps = movieInfo.kbps;
		video.length = movieInfo.length;
	}

	return video;
}

audio_t PlayerWidget::currentAudio()
{
	audio_t audioTrack;

	if (media == Video::Media::DVD && title > 0
		&& titleInfo[title - 1].audioN > 0 
		&& this->audio > -1 )
	{
		audioTrack = titleInfo[title - 1].audio[audio];
	}
	else if (media == Video::Media::File
		&& movieInfo.audioN > 0 
		&& this->audio > -1 )
	{
		audioTrack = movieInfo.audio[audio];
	}

	return audioTrack;
}

subtitle_t PlayerWidget::currentSubtitle()
{
	subtitle_t subtitleTrack;

	if (media == Video::Media::DVD && title > 0
		&& titleInfo[title - 1].subtitleN > 0 
		&& this->subtitle > -1 )
	{
		subtitleTrack = titleInfo[title - 1].subtitle[subtitle];
	}
	else if (media == Video::Media::File
		&& movieInfo.subtitleN > 0
		&& this->subtitle > -1 )
	{
		subtitleTrack = movieInfo.subtitle[subtitle];
	}

	return subtitleTrack;
}

void PlayerWidget::resizeEvent(QResizeEvent *)
{
	centerRenderer();
}

double PlayerWidget::calculateAutoZoom()
{
	double autoZoom(1.0);  // default no zoom

#ifdef Q_OS_MAC
	int rendererWidth = mainConsole ? mainConsole->width() : parentWidget()->frameGeometry().width();

	autoZoom = (double) rendererWidth / (double) scaledResolutionReference.width();
#else
	if (scaledResolutionReference.width() != preferences.auto_zoom_max_width)
	{
		autoZoom = (double)preferences.auto_zoom_max_width / (double)scaledResolutionReference.width();
	}

	if (scaledResolutionReference.height() != preferences.auto_zoom_max_height)
	{
		autoZoom = qMin( autoZoom,
			(double)preferences.auto_zoom_max_height / (double)scaledResolutionReference.height() );
	}
#endif

	return autoZoom;
}

void PlayerWidget::setZoom(const double z)
{
	if (novideo || (media == Video::Media::File && movieInfo.novideo) )
	{
		//zoom = 1;
		scaledResolutionReference = QSize(preferences.auto_zoom_max_width, preferences.auto_zoom_max_height);
		scaledResolution = scaledResolutionReference;
	}

	if ( z == 0 ) autoZoom = true;

	if (autoZoom)
	{
		zoom = calculateAutoZoom();
	}
	else
	{
		zoom = z;
	}
	
	if (zoom > 2.0)
	{
		// Maximum zoom for max rezolution supported: 1366x768)
		scaledResolution.scale( 1366, 768, Qt::KeepAspectRatio );

		printLog( tr("Set player maximum zoom (display resolution: %1x%2)")
			.arg(scaledResolution.width()).arg(scaledResolution.height()), VERBOSITY_VERBOSE );
	}
	else
	{
		scaledResolution = scaledResolutionReference * zoom;

		printLog( tr("Set player zoom %1 (display resolution: %2x%3)")
			.arg(zoom).arg(scaledResolution.width()).arg(scaledResolution.height()), VERBOSITY_VERBOSE );
	}

	renderTV->setMinimumSize( scaledResolution );

	renderBackground->setFixedSize( renderTarget->size() );

	player->setSize( scaledResolution );

#ifndef Q_OS_MAC
	if ( renderBackground->width() < audioPixmap->width() 
		|| renderBackground->height() < audioPixmap->height() )
	{
		renderBackground->setPixmap( audioPixmap->scaled(renderBackground->size(), Qt::KeepAspectRatio) );
	}
	else
	{
		renderBackground->setPixmap( *audioPixmap );
	}

	centerRenderer();
#endif
}
