/*****************************************************************************
 *
 *  interfacemplayer.cpp -- Interface to MPlayer CLI. 
 *
 *  Copyright (C) 2010 Starbuck <starbuck@labdv.com>
 *  All rights reserved.
 *
 *  This file is part of the Player 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.
 *
 *****************************************************************************/

#define	RX_CATCH_MPLAYER_OUTPUT_STATUS		"(mplayer\\s+[^\\s]+.*MPlayer Team)"
#define	RX_CATCH_MPLAYER_OUTPUT_HELP		"Available video output drivers:(.*)Available audio output drivers:(.*)"

#define	RX_PARSE_MPLAYER_SIGNATURE			"(mplayer)\\s+([^\\s]+).*(MPlayer Team)"

#define	RX_PARSE_MPLAYER_ID_DVD_DISCID		"ID_DVD_VOLUME_ID=(.*)"
#define RX_PARSE_MPLAYER_ID_DVD_TITLES		"ID_DVD_TITLES=(\\d+)"
#define RX_PARSE_MPLAYER_ID_DVD_CHAPTERS	"ID_DVD_TITLE_(\\d+)_CHAPTERS=(\\d+)"
#define RX_PARSE_MPLAYER_ID_DVD_ANGLES		"ID_DVD_TITLE_(\\d+)_ANGLES=(\\d+)"
#define RX_PARSE_MPLAYER_ID_DVD_LENGTH		"ID_DVD_TITLE_(\\d+)_LENGTH=(\\d+\\.\\d+)"

#define	RX_PARSE_MPLAYER_CHAPTERS			"CHAPTERS:\\s*([\\d:,]+)"
#define	RX_PARSE_MPLAYER_VIDEO_RAW			"VIDEO:\\s*\\[\\]\\s+"
#define	RX_PARSE_MPLAYER_VIDEO_CODEC		"VIDEO:\\s*\\[([^\\s]+)\\]\\s+"
#define	RX_PARSE_MPLAYER_VIDEO_RESOLUTION	"(\\d+)x(\\d+)\\s"
#define	RX_PARSE_MPLAYER_VIDEO_COLORDEPTH	"(\\d+)bpp\\s"
#define	RX_PARSE_MPLAYER_VIDEO_ASPECT		"\\(aspect\\s*(\\d)\\)\\s"
#define	RX_PARSE_MPLAYER_VIDEO_FPS			"\\s*([^\\s]+)\\s*fps\\s"
#define	RX_PARSE_MPLAYER_VIDEO_KBPS			"\\s*([^\\s]+)\\s*kbps\\s"	
#define	RX_PARSE_MPLAYER_MOVIE_ASPECT		"Movie-Aspect is\\s+([^\\s]+):1\\s"
#define	RX_PARSE_MPLAYER_OUTPUT_RESOLUTION	"VO:.*\\d+x\\d+\\s*=>\\s*(\\d+)x(\\d+)\\s"
#define RX_PARSE_MPLAYER_AUDIO_SAMPLING		"(\\d+)\\s*Hz"
#define RX_PARSE_MPLAYER_AUDIO_NCH			"(\\d+)\\s*ch"
#define RX_PARSE_MPLAYER_AUDIO_KBPS			"(\\d+.\\d+)\\s*kbit"
#define	RX_PARSE_MPLAYER_MPEG_CODEC			"(MPEG\\d)\\s"
#define	RX_PARSE_MPLAYER_DVDAUDIOSTREAM		"audio stream:\\s*(\\d+)\\s+format:\\s*([^\\s]+)\\s+\\(([^\\s]+)\\)\\s+language:\\s*([^\\s]+)\\s+aid:\\s*(\\d+)"
#define	RX_PARSE_MPLAYER_DVDSUB				"subtitle[^:]*:\\s*(\\d+)\\s+language:\\s+([^\\s]+)"
#define	RX_PARSE_MPLAYER_LAVFSTREAM			"stream\\s+(\\d+):\\s+(audio|video|subtitle)\\s+"
#define	RX_PARSE_MPLAYER_LAVFAUDIO			"stream\\s+(\\d+):\\s+audio\\s+\\(([^\\s]+)\\),\\s+-aid\\s+(\\d+),\\s+-alang\\s+([^\\s^,]+)(,\\s+(.*))?"
#define	RX_PARSE_MPLAYER_LAVFSUB			"stream\\s+(\\d+):\\s+subtitle\\s+\\(([^\\s]+)\\),\\s+-sid\\s+(\\d+),\\s+-slang\\s+([^\\s^,]+)(,\\s+(.*))?"
#define	RX_PARSE_MPLAYER_MKVTRACK			"Track ID\\s+(\\d+):\\s+(audio|video|subtitles)\\s+"
#define	RX_PARSE_MPLAYER_MKVAUDIOTRACK		"Track ID\\s+(\\d+):\\s+audio\\s+\\(([^\\s]+)\\)(\\s+\"([^\"]+)\")?,\\s+-aid\\s+(\\d+),\\s+-alang\\s+([^\\s]+)"
#define	RX_PARSE_MPLAYER_MKVSUB				"Track ID\\s+(\\d+):\\s+subtitles\\s+\\(([^\\s]+)\\)(\\s+\"([^\"]+)\")?,\\s+-sid\\s+(\\d+),\\s+-slang\\s+([^\\s]+)"
#define	RX_PARSE_MPLAYER_AVISTREAM			"\\[aviheader\\]\\s+(audio|video|subtitles).+\\s+-(a|s)id\\s+(\\d+)"
#ifdef WIN32
#define	RX_PARSE_MPLAYER_CODECS			"([^\\s]+)\\s+([^\\n]*)\\r\\n"
#else
#define	RX_PARSE_MPLAYER_CODECS			"([^\\s]+)\\s+([^\\n]*)\\n"
#endif
	
#define LSTR_CATCH_MPLAYER_ID						"ID_"
#define LSTR_CATCH_MPLAYER_ID_LENGTH				"ID_LENGTH="
#define LSTR_CATCH_MPLAYER_ID_VIDEO_CODEC			"ID_VIDEO_CODEC="
#define LSTR_CATCH_MPLAYER_ID_AUDIO_ID				"ID_AUDIO_ID="
#define LSTR_CATCH_MPLAYER_ID_AUDIO_FORMAT			"ID_AUDIO_FORMAT="
#define LSTR_CATCH_MPLAYER_ID_AUDIO_BITRATE			"ID_AUDIO_BITRATE="
#define LSTR_CATCH_MPLAYER_ID_AUDIO_RATE			"ID_AUDIO_RATE="
#define LSTR_CATCH_MPLAYER_ID_AUDIO_NCH				"ID_AUDIO_NCH="
#define LSTR_CATCH_MPLAYER_ID_AUDIO_CODEC			"ID_AUDIO_CODEC="

#define LSTR_CATCH_MPLAYER_ID_DVD					"ID_DVD_"
#define	LSTR_CATCH_MPLAYER_OUTPUT_PLAYING			"Playing"
#define	LSTR_CATCH_MPLAYER_OUTPUT_STARTINGPLAYBACK	"Starting playback..."
#define	LSTR_CATCH_MPLAYER_OUTPUT_ENDPLAYBACK		"Exiting"
#define LSTR_CATCH_MPLAYER_OUTPUT_AUDIOONLYFILE		"Audio only file format detected."
#define LSTR_CATCH_MPLAYER_OUTPUT_NOVIDEO			"Video: no video"
#define	LSTR_CATCH_MPLAYER_OUTPUT_LENGTH			"ANS_LENGTH"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DEINTERLACE		"ANS_deinterlace"
#define	LSTR_CATCH_MPLAYER_OUTPUT_CHAPTER			"ANS_chapter"
#define	LSTR_CATCH_MPLAYER_OUTPUT_ASPECT			"Movie-Aspect is "
#define	LSTR_CATCH_MPLAYER_OUTPUT_DRIVER			"VO:"
#define	LSTR_CATCH_MPLAYER_OUTPUT_AUDIOSTREAM		"AUDIO:"
#define	LSTR_CATCH_MPLAYER_OUTPUT_VIDEOSTREAM		"VIDEO:"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DVDCALLBACK		"There are"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DVDAUDIONUMBER	"number of audio channels on disk:"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DVDAUDIOSTREAM	"audio stream:"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DVDSUBNUMBER		"number of subtitles on disk:"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DVDSUB			"subtitle"
#define	LSTR_CATCH_MPLAYER_OUTPUT_LAVFSTREAM		"[lavf] stream"
#define	LSTR_CATCH_MPLAYER_OUTPUT_MKVTRACK			"[mkv] Track ID"
#define	LSTR_CATCH_MPLAYER_OUTPUT_AVISTREAM			"[aviheader]"
#define	LSTR_CATCH_MPLAYER_OUTPUT_POSITION			"ANS_TIME_POSITION"
#define	LSTR_CATCH_MPLAYER_OUTPUT_RESOLUTION		"ANS_VIDEO_RESOLUTION"
#define	LSTR_CATCH_MPLAYER_OUTPUT_SPEED				"ANS_speed"
#define	LSTR_CATCH_MPLAYER_OUTPUT_DVDVOLUMEID		"ID_DVD_VOLUME_ID="
#define	LSTR_CATCH_MPLAYER_OUTPUT_CHAPTERS			"CHAPTERS:"

#define	RSTR_CATCH_MPLAYER_OUTPUT_FILEFORMAT		"file format detected."
#define	RSTR_CATCH_MPLAYER_OUTPUT_DVDCHAPTERS		"chapters in this DVD title."
#define	RSTR_CATCH_MPLAYER_OUTPUT_DVDANGLES			"angles in this DVD title."
#define	RSTR_CATCH_MPLAYER_OUTPUT_DVDTITLES			"titles on this DVD."

#define	LSTR_CATCH_MPLAYER_ERROR_FILENOTFOUND		"File not found"
#define	LSTR_CATCH_MPLAYER_ERROR_OPENVMG			"Can't open VMG info"

#include "interfacemplayer.h"
#include "../video/video_constants.h"
#include "../video/video_functions.h"
#include "macros.h"
#include <QProcess>
#include <QRegExp>
#include <QDebug>
#include <QTime>
#include <QFileInfo>

InterfaceMplayer::InterfaceMplayer()
{
	videoOff = false;
	soundOff = false;
	subtitleOff = false;
	audioLang = "";
	audioId = -1;
	subLang = "";
	subId = -1;
	title = 0;
	angle = 0;
	chapterList.clear();
	targetWid = "";
	preferredVideoDriver.clear();
	externalRenderer = QRect(0, 0, 0, 0);
}

void InterfaceMplayer::initialize(const QString &p, player_engine_capability_t *c, player_engine_status_t *s)
{
	exec = p;
	capability = c;
	status = s;
}

void InterfaceMplayer::setBinPath(const QString &binPath)
{
	exec = binPath;
}

void InterfaceMplayer::setDvdSource(const QString &dev, const int titleNumber)
{
	source = dev;
	media = Video::Media::DVD;
	title = titleNumber;
	this->novideo = false;

	angle = 0;
	chapterList.clear();

	audioLang = "";
	audioId = -1;
	subLang = "";
	subId = -1;
}

void InterfaceMplayer::setFileSource(const QString &path, const bool novideo)
{
	source = path;
	media = Video::Media::File;
	this->novideo = novideo;

	title = 0;
	angle = 0;
	chapterList.clear();

	audioLang = "";
	audioId = -1;
	subLang = "";
	subId = -1;
}

void InterfaceMplayer::setExternalRenderer(const int x, const int y, const int width)
{
	externalRenderer.setLeft( x );
	externalRenderer.setTop( y );
	externalRenderer.setWidth( width );
}

void InterfaceMplayer::setSize(const QSize size)
{
	pictureSize = size;
}

void InterfaceMplayer::setWid(const QString &wid)
{
	targetWid = wid;
}

bool InterfaceMplayer::setPreferredVideoDriver(const QString &driver)
{
	bool ok = supportsVideoDriver(driver);

	if (ok) preferredVideoDriver = driver;

	return ok;
}

bool InterfaceMplayer::supportsVideoDriver(const QString &driver)
{
	return capability->video->contains(driver);
}

void InterfaceMplayer::setSoundOn()
{
	soundOff = false;
}

void InterfaceMplayer::setSoundOff()
{
	soundOff = true;
}

void InterfaceMplayer::setVideoOn()
{
	videoOff = false;
}

void InterfaceMplayer::setVideoOff()
{
	videoOff = true;
}

void InterfaceMplayer::setSubtitleOn()
{
	subtitleOff = false;
}

void InterfaceMplayer::setSubtitleOff()
{
	subtitleOff = true;
}

void InterfaceMplayer::setTitle(const int t)
{
	title = t;
}

void InterfaceMplayer::setAudioLanguage(const QString &lang)
{
	audioLang = lang;
	audioId = -1;
}

void InterfaceMplayer::setAudioLanguage(const int &lang)
{
	audioLang = "";
	audioId = lang;
}

void InterfaceMplayer::setSubtitleLanguage(const QString &lang)
{
	subLang = lang;
	subId = -1;
}

void InterfaceMplayer::setSubtitleLanguage(const int &lang)
{
	subLang = "";
	subId = lang;
}

void InterfaceMplayer::setAngle(const int a)
{
	angle = a;
}

void InterfaceMplayer::setChapterList(const QList<int> chapters)
{
	chapterList = chapters;
}

bool InterfaceMplayer::soundOn()
{
	return !soundOff;
}

bool InterfaceMplayer::videoOn()
{
	return !videoOff;
}

bool InterfaceMplayer::subtitleOn()
{
	return !subtitleOff;
}

void InterfaceMplayer::cliCmdUpdateFontCache(QString *cmd, QStringList *args)
{
	cmd->clear();
	args->clear();

	// Command
	*cmd = exec.toLatin1();

	// Backend
	*args << "-fontconfig";
	*args << "-noquiet";
	*args << "-slave";  // use mplayer as a backend

	*args << "-vo" << "null";  // no video
	*args << "-ao" << "null";  // no audio

	*args << "-frames" << "0";
	*args << "nul";	// an input is required

	qDebug() << "InterfaceMplayer::cliCmdUpdateFontCache() returns command:" << *cmd << "with options" << *args;
}

void InterfaceMplayer::cliCmdIdentify(QString *cmd, QStringList *args)
{
	cmd->clear();
	args->clear();

	// Command
#ifdef WIN32
	*cmd = exec;
	//if (subtitleOff || subLang.isEmpty()) cmd->append("-vp8");  // a more recent MPlayer
	//*cmd = "C:/Projects/Dev/DVDx/dvdx4/build/Win32/Debug/bin/mencoder/mplayer-vp8.exe";
	*cmd = cmd->toLatin1();
#else
	*cmd = exec.toLatin1();
#endif

	// Backend
	*args << "-noconfig" << "all";
	*args << "-noquiet";
	*args << "-nocache";
	*args << "-nofs";
	*args << "-nomouseinput";
	*args << "-slave";  // use mplayer as a backend

#ifdef WIN32
	//*args << "-font" << QFileInfo(*cmd).absolutePath() + "/subfont.ttf";
	*args << "-nofontconfig";
#endif

	*args << "-vo" << "null";  // no video
	*args << "-ao" << "null";  // no audio

	*args << "-frames" << "1"; // must be >0 to get video decoder info: VO: [gl] 352x288 => 512x288 Planar YV12

	// Target Windows Id
	if (!targetWid.isEmpty())
		*args << "-wid" << targetWid;

	// Video source to play
	if ( media == Video::Media::DVD )
	{
		*args << "-msglevel" << "identify=6"; 
		*args << "-dvd-device" << source << QString("dvd://%1").arg(QString::number(title));
		*args << "-dvdangle" << "1";
	}
	else
	{
		*args << "-msglevel" << "identify=4";  // 6 is too high (DVDx seems to hang wen parsing 100s of lines)
		*args << source; 
	}

	qDebug() << "InterfaceMplayer::cliCmdIdentify() returns command:" << *cmd << "with options" << *args;
}

void InterfaceMplayer::cliCmdPlay(QString *cmd, QStringList *args, 
	int frames, int chapter, int seekPosition, int volume)
{
	cmd->clear();
	args->clear();

	// Command
	*cmd = exec.toLatin1();
#ifdef WIN32
	if ( media == Video::Media::DVD )
	{
		cmd->replace(QRegExp("mplayer(-\\w)?\.exe"), QString("mplayer-dvd.exe"));
	}
#endif

	// Backend
	*args /* not working as extected << "-nogui"	// mut be the first argument */
		<< "-slave"		// use mplayer as a backend
		<< "-nofs" 
		<< "-nomouseinput" 
		//<< "-sub-fuzziness" << "1"
		<< "-nokeepaspect" 
		<< "-nocache"
		<< "-noconfig" << "all"
#ifdef WIN32
		<< "-priority" << "abovenormal" 
		//<< "-font" << QFileInfo(*cmd).absolutePath() + "/subfont.ttf"
		//<< "-font" << QFileInfo(*cmd).absolutePath() + "/mplayer/subfont.ttf"
		<< "-nofontconfig"
#endif
		<< "-framedrop" 
		<< "-nodr"			// not sure but may avoid some OpenGL rendering issue
		<< "-double"		// not sure but may be useful only with svga driver
		<< "-monitorpixelaspect" << "1"
#ifdef WIN32
/*experimental
		<< "-ass" << "-embeddedfonts" << "-ass-line-spacing" << "0"
		//<< "-ass-color" << "00FF0080"
		//<< "-ass-border-color" << "00FF0080"
		<< "-ass-font-scale" << "1" 
		<< "-ass-styles" << "styles.ass" 
		<< "-subfont-autoscale" << "0" 
		<< "-subfont-osd-scale" << "20" 
		<< "-subfont-text-scale" << "20" 
		//<< "-subcp" << "ISO-8859-1" 
		//<< "-subpos" << "100"
*/
#endif
#ifdef Q_OS_MAC
		<< "-ontop"
		<< "-title" << QString("DVDx 4 - Playing %1").arg(source)
		//<< "-noborder"
		//<< "-use-filename-title"
#endif
		<< "-osdlevel" << "0"
		//<< "-vf-add" << "screenshot"	// allow screenshot in slave mode 
		//<< "-slices"
		//<< "-af" << "scaletempo,equalizer=0:0:0:0:0:0:0:0:0:0
		<< "-softvol" << "-softvol-max" << "110";	// maybe safe for OSD

#ifdef _DEBUG
	//*args << "-noquiet";
	*args << "-quiet";
	//*args << "-really-quiet";  // not working for us cause not enough verbose 
#else
	*args << "-quiet";  // not willing to parse too much :)
#endif

	//*args << "-monitorpixelaspect" << "1";

	// Video decoder
	if (videoOff)
	{
		*args << "-vo" << "null";  // no video
	}
	else if (!novideo)
	{
		if (!preferredVideoDriver.isEmpty()) *args << "-vo" << preferredVideoDriver;

		if ( pictureSize.width() > 0 && pictureSize.height() > 0 )
		{

#ifdef Q_OS_MAC
			*args << "-vf" << QString("scale=%1:%2")
				.arg(pictureSize.width()).arg(pictureSize.height());

			*args << "-geometry" << QString("%1:%2")
				.arg(externalRenderer.left()).arg(externalRenderer.top());
#else
			*args << "-vf" << QString("scale=%1:%2")
				.arg(pictureSize.width()).arg(pictureSize.height());

			*args << "-geometry" << QString("%1x%2")
				.arg(pictureSize.width()).arg(pictureSize.height());
#endif
		}
	}

	// Audio
	if (soundOff)
	{
		*args << "-ao" << "null";  // no audio
	}
	else
	{
		if (volume != 0) *args << "-af" << QString( "volume=%1" ).arg( volume ); 
	}

	// DVD stuff
	if ( media == Video::Media::DVD )
	{
		if (!soundOff && (audioId > -1))
			*args << "-aid" << QString::number(audioId);
		if (!soundOff && !audioLang.isEmpty())
			*args << "-alang" << audioLang;

		if (!subtitleOff && (subId > -1))
			*args << "-sid" << QString::number(subId);
		if (!subtitleOff && !subLang.isEmpty())
			*args << "-slang" << subLang;

		if (subId < 0)
			*args << "-nosub";

		if (angle > 1)
			*args << "-dvdangle" << QString::number(angle);
		else
			*args << "-dvdangle" << "1";

		if (chapter > 0)
		{
			// Test mode
			*args << "-chapter" << QString::number(chapter);
		}
		else if (!chapterList.isEmpty())
		{
			for (int i = 0; i < chapterList.size(); ++i) 
			{
				*args << "-chapter" << QString::number(chapterList.at(i));
			}
		}
	}
	// File stuff
	else if ( media == Video::Media::File )
	{
		if (!soundOff && !audioLang.isEmpty())
			*args << "-alang" << audioLang;

		if (!soundOff && (audioId > -1))
			*args << "-aid" << QString::number(audioId);

		if (!subtitleOff && !subLang.isEmpty())
			*args << "-slang" << subLang;

		if (!soundOff && (subId > -1))
			*args << "-sid" << QString::number(subId);

		else if (seekPosition > 0)
		{
			// Test mode
			*args << "-ss" << QString::number(seekPosition);
		}
	}

	if (frames > 0)
	{
		// Test mode
		*args << "-frames" << QString::number(frames);
	}

#ifndef Q_OS_MAC
	// Target Windows Id
	if (!targetWid.isEmpty())
		*args << "-wid" << targetWid;
	else
		*args << "-title" << "DVDx 4 - WebM video player";
#endif

	// Video source to play
	if ( media == Video::Media::DVD )
	{
		*args << "-dvd-device" << source;
		*args << QString("dvd://%1").arg(QString::number(title));
	}
	else
	{
		*args << source; 
	}

	qDebug() << "InterfaceMplayer::cliPlay() returns command:" << *cmd << "with options" << *args;
}

void InterfaceMplayer::cliCmdGetStatus(QString *cmd, QStringList *args)
{
	cmd->clear();
	args->clear();

	// Command
	*cmd = exec.toLatin1();

	*args << "-noconfig" << "all";
	*args << "-noquiet";
	*args << "-nocache";
	*args << "-nofs";
	*args << "-nomouseinput";

	// Query video and audio supported
	*args << "-vo" << "help" << "-ao" << "help";

	qDebug() << "InterfaceMplayer::cliCmdGetStatus() returns command:" << *cmd << "with options" << *args;
}

QString InterfaceMplayer::cliStop()
{
	return QString("quit\n");
}

QString InterfaceMplayer::cliSelectTitle(const int title)
{
	qDebug() << "InterfaceMplayer::cliSelectTitle() return:" << "switch_title" << title;

	return QString("switch_title " + QString::number(title) + "\n");  // not working :-(
}

QString InterfaceMplayer::cliSelectAudio(const int audioId)
{
	qDebug() << "InterfaceMplayer::cliSelectAudio() return:" << "switch_audio" << audioId;

	QString command;

	if (audioId < 0)
	{
		command = "mute 1\n";
	}
	else
	{
		command = "mute 0\n"
				  "switch_audio " + QString::number(audioId) + "\n";
	}

	qDebug() << "InterfaceMplayer::cliSelectAudio() return:" << command;

	return command;
}

QString InterfaceMplayer::cliSelectSubtitle(const int subId)
{
	qDebug() << "InterfaceMplayer::cliSelectSubtitle() return:" << "sub_demux" << subId;

	return QString("sub_demux " + QString::number(subId) + "\n");
}

QString InterfaceMplayer::cliAvert(const QString &message, const int duration)
{
	return QString("osd_show_text \"%1\" %2\n").arg(message).arg(duration);
}

QString InterfaceMplayer::cliShowProgress()
{
	return QString("osd_show_progression\n");
}

QString InterfaceMplayer::cliVideoResolution()
{
	return QString("get_video_resolution\n");
}

QString InterfaceMplayer::cliTimeLength()
{
	return QString("get_time_length\n");
}

QString InterfaceMplayer::cliTimePosition()
{
	return QString("get_time_pos\n");
}

QString InterfaceMplayer::cliChapter()
{
	return QString("get_property chapter\n");
}

QString InterfaceMplayer::cliDeinterlace()
{
	return QString("get_property deinterlace\n");
}

QString InterfaceMplayer::cliSpeed()
{
	return QString("get_property speed\n");
}

QString InterfaceMplayer::cliSeekChapter(const int chapt)
{
	if (chapt < 0)
	{
		qDebug() << "InterfaceMplayer::cliSeekChapter() called with chapter below 0, considered as 0";
		return QString("seek_chapter 0 1\n");
	}

	return QString("seek_chapter " + QString::number(chapt) + " 1\n");
}

QString InterfaceMplayer::cliSeekPrev(const int seconds)
{
	QString command;

	if (seconds == 0)
	{
		command = "seek_chapter -1 0\n";
	}
	else
	{
		command = "seek -" + QString::number(qAbs(seconds)) + " 0\n"; // for movies 10% length step is correct
	}

	return command;
}

QString InterfaceMplayer::cliSeekNext(const int seconds)
{
	QString command;

	if (seconds == 0)
	{
		command = "seek_chapter +1 0\n";
	}
	else
	{
		command = "seek +" + QString::number(qAbs(seconds)) + " 0\n"; // for movies 10% length step is correct
	}

	return command;
}

QString InterfaceMplayer::cliSeekRelative(const int seconds)
{
	return QString("seek " + QString::number(seconds) + " 0\n");
}

QString InterfaceMplayer::cliSeekPercent(const int percent)
{
	if (percent < 0)
	{
		qDebug() << "InterfaceMplayer::cliSeekPercent() called with percent below 0, considered as 0";
		return QString("seek 0 1\n");
	}

	if (percent > 100)
	{
		qDebug() << "InterfaceMplayer::cliSeekPercent() called with percent exceeding 100, considered as 100";
		return QString("seek 100 1\n");
	}

	return QString("seek " + QString::number(percent) + " 1\n");
}

QString InterfaceMplayer::cliSeek(const int seconds)
{
	if (seconds < 0)
	{
		qDebug() << "InterfaceMplayer::cliSeek() called with seconds below 0, considered as 0";
		return QString("seek 0 2\n");
	}

	return QString("seek " + QString::number(seconds) + " 2\n");
}

QString InterfaceMplayer::cliSetSpeedFaster()
{
	return QString("speed_mult 2\n");
}

QString InterfaceMplayer::cliSetSpeedSlower()
{
	return QString("speed_mult 0.5\n");
}

QString InterfaceMplayer::cliSetSpeedNormal()
{
	return QString("speed_set 1\n");
}

QString InterfaceMplayer::cliSwitchAngle(const int angle)
{
	if (angle < 0)
	{
		qDebug() << "InterfaceMplayer::cliSwitchAngle() called with angle below 0, considered as 0";
		return QString("switch_angle 0\n");
	}

	return QString("switch_angle " + QString::number(angle) + "\n");
}

QString InterfaceMplayer::cliSwitchAspectRatio(const double ratio)
{
	if (ratio <= 0)
	{
		qDebug() << "InterfaceMplayer::cliSwitchAspectRatio() called with angle below or equal 0, considered as 16/9";
		return QString("switch_angle 1.77778\n");
	}

	return QString("switch_ratio " + QString::number(ratio, 'g', 5) + "\n");
}

QString InterfaceMplayer::cliSwitchPause()
{
	return QString("pause\n");
}

bool InterfaceMplayer::parseStdoutClip(const QByteArray &cout, int *state, video_clip_t *clip)
{
	bool ok;
	QRegExp rx;
	QByteArray buffer(cout.simplified());

	// Only DVD and File implemented yet
	if (clip->media != Video::Media::DVD && clip->media != Video::Media::File)
	{
		qDebug() << "InterfaceMplayer: parseStdoutClip() method not implemented for media" 
			<< clip->media << "ignoring" << QString(cout.simplified());
		return false;
	}

	// End of playback: Exiting... (Quit) or (End of file) or etc.
	if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_ENDPLAYBACK))
	{
		qDebug() << "InterfaceMplayer: parseStdoutClip() detected end of playback" 
			<< QString(cout.simplified());
	
		*state = Video::Parsing::ExitPlayback;
		return true;
	}
	// File format: Matroska file format detected.
	else if (buffer.endsWith(RSTR_CATCH_MPLAYER_OUTPUT_FILEFORMAT))
	{
		buffer.chop(22); // remove " file format detected."
		clip->format = QString( buffer );
		qDebug() << "InterfaceMplayer: parseStdoutClip() file format detected" << buffer 
			<< "in" << QString(cout.simplified());
		*state = Video::Parsing::Format;
		return true;
	}
	// Audio only file format detected.
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_AUDIOONLYFILE))
	{
		clip->novideo = true;
		qDebug() << "InterfaceMplayer: parseStdoutClip() audio file detected in" 
			<< QString(cout.simplified());
		*state = Video::Parsing::NoVideo;
		return true;
	}
	// Video : novideo.
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_NOVIDEO))
	{
		clip->novideo = true;
		qDebug() << "InterfaceMplayer: parseStdoutClip() audio file detected in" 
			<< QString(cout.simplified());
		*state = Video::Parsing::NoVideo;
		return true;
	}
	// Identify callback: ID_ ...
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID))
	{
		buffer = buffer.trimmed();

		// Stream length: ID_LENGTH=832.920
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_LENGTH))
		{
			buffer.remove(0, 10); // remove ID_LENGTH=
			double maxTime = buffer.toDouble(&ok);
			if ( ok )
			{
				clip->length = maxTime;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected movie 'length'"
					<< maxTime << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse movie 'length' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Length;
			return true;
		}

		// Audio ID: ID_AUDIO_ID=0
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_AUDIO_ID))
		{
			buffer.remove(0, 12); // remove ID_AUDIO_ID=
			int aid = buffer.toInt(&ok);
			if ( ok )
			{
				clip->audio.aid = aid;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio 'aid'"
					<< aid << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'aid' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::AudioId;
			return true;
		}

		// Audio ID: ID_AUDIO_FORMAT=85 for MP3
		// Audio ID: ID_AUDIO_FORMAT=80 for MP2
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_AUDIO_FORMAT))
		{
			buffer.remove(0, 16); // remove ID_AUDIO_FORMAT=
			QString format = buffer.trimmed();
			if ( !format.isEmpty() )
			{
				if ( format == "85" )
				{
					clip->audio.format = "MP3";
				}
				else if ( format == "80" )
				{
					clip->audio.format = "MP2";
				}
				else if (clip->audio.format.startsWith("ff"))
				{
					clip->audio.format.remove(0, 2);  // Mplayer's libavcodec (FFmpeg) codec
				}
				else
				{
					rx.setPattern("^[a-z0-9_-]+$");
					rx.setCaseSensitivity(Qt::CaseInsensitive);
					if (rx.exactMatch(format)) clip->audio.format = format;
				}
				
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio 'format'"
					<< format << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'format' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::AudioFormat;
			return true;
		}

		// Audio bitrate: ID_AUDIO_BITRATE=128000
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_AUDIO_BITRATE))
		{
			buffer.remove(0, 17); // remove ID_AUDIO_BITRATE=
			if (buffer.length() > 3) buffer.truncate(buffer.length() - 3); // remove 000 at the end
			int bitrate = buffer.toInt(&ok);
			if ( ok )
			{
				clip->audio.kbps = bitrate;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio 'bitrate'"
					<< bitrate << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'bitrate' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::AudioBitrate;
			return true;
		}

		// Audio sampling: ID_AUDIO_RATE=48000
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_AUDIO_RATE))
		{
			buffer.remove(0, 14); // remove ID_AUDIO_RATE=
			int sampling = buffer.toInt(&ok);
			if ( ok )
			{
				clip->audio.sampling = sampling;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio 'sampling'"
					<< sampling << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'sampling' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::AudioSampling;
			return true;
		}

		// Audio channels: ID_AUDIO_NCH=2
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_AUDIO_NCH))
		{
			buffer.remove(0, 13); // remove ID_AUDIO_NCH=
			int channel_number = buffer.toInt(&ok);
			if ( ok )
			{
				clip->audio.channel_number = channel_number;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio 'channel number'"
					<< channel_number << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'channel number' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::AudioChannelNumber;
			return true;
		}

		// Audio codec: ID_AUDIO_CODEC=mp3
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_AUDIO_CODEC))
		{
			buffer.remove(0, 15); // remove ID_AUDIO_CODEC=
			QString codec = buffer.trimmed();
			if ( !codec.isEmpty() )
			{
				clip->audio.codec = codec;

				if (clip->audio.codec.startsWith("ff"))
					clip->audio.codec.remove(0, 2);  // Mplayer's libavcodec (FFmpeg) codec

				qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio 'codec'"
					<< codec << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'codec' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::AudioCodec;
			return true;
		}

		// Audio codec: ID_VIDEO_CODEC=ffh264
		if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_VIDEO_CODEC))
		{
			buffer.remove(0, 15); // remove ID_VIDEO_CODEC=
			QString codec = buffer.trimmed();
			if ( !codec.isEmpty() )
			{
				if (clip->codec.isEmpty())
				{
					clip->codec = codec;

					// Mplayer's libavcodec (FFmpeg) codec and ffodivx
					if (clip->codec.startsWith("ffo")) clip->codec.remove(0, 3);
					else if (clip->codec.startsWith("ff")) clip->codec.remove(0, 2);

					qDebug() << "InterfaceMplayer: parseStdoutClip() detected video 'codec'"
						<< codec << "in" << QString(cout.simplified());
				}
				else
				{
					qDebug() << "InterfaceMplayer: parseStdoutClip() detected (and ignored) video 'codec'"
						<< codec << "in" << QString(cout.simplified());
				}
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse audio 'codec' in "
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::VideoCodec;
			return true;
		}
	}
	// Answer to get_time_length : ANS_LENGTH=xx.yy
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_LENGTH))
	{
		buffer.remove(0, 11); // remove ANS_LENGTH=
		double maxTime = buffer.toDouble(&ok);
		if ( ok )
		{
			clip->length = maxTime;
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected movie 'length'"
				<< maxTime << "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse movie 'length' in "
				<< QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Length;
		return true;
	}
	// Answer to get_property deinterlace : ANS_deinterlace=no
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DEINTERLACE))
	{
		buffer.remove(0, 16); // remove ANS_deinterlace=
		if ( buffer == "yes" )
		{
			clip->interlaced = 1;

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected deinterlace ON"
				<< "in" << QString(cout.simplified());
		}
		else if ( buffer == "no" )
		{
			clip->interlaced = 0;

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected deinterlace OFF"
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse movie 'deinterlace' in" << QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Deinterlace;
		return true;
	}
	// Video decoder info: Movie-Aspect is 1.77:1
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_ASPECT))
	{
		QRegExp rx(RX_PARSE_MPLAYER_MOVIE_ASPECT);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 1)
		{
			bool ok;
			double f = rx.cap(1).toDouble( &ok );
			if (ok)
			{
				clip->display_aspect = f;
	
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected movie 'aspect ratio'"
					<< rx.cap(1) << clip->display_aspect << "in" << QString(cout.simplified());
			
				*state = Video::Parsing::Aspect;
				return true;
			}
			
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse movie 'aspect ratio'"
				<< "in" << buffer;
		}
	}
	// Video decoder info: VO: [gl2] 352x288 => 512x288 Planar YV12
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DRIVER))
	{
		QRegExp rx(RX_PARSE_MPLAYER_OUTPUT_RESOLUTION);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 2)
		{
			clip->scaledRez.setWidth( rx.cap(1).toInt() );
			clip->scaledRez.setHeight( rx.cap(2).toInt() );

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video scaled resolution" 
				<< clip->scaledRez.width() << "x" << clip->scaledRez.height() << "in" << QString(cout.simplified());

			*state = Video::Parsing::ScaledResolution;
			return true;
		}
	}
	// DVD callback: VIDEO:  MPEG2  720x480  (aspect 3)  29.970 fps  7500.0 kbps (937.5 kbyte/s)
	// File callback: VIDEO:  [XVID]  640x256  12bpp  25.000 fps  1409.3 kbps (172.0 kbyte/s)
	//                VIDEO:  []  624x352  24bpp  23.976 fps    0.0 kbps ( 0.0 kbyte/s)
	//                VIDEO:  [WMV3]  1280x720  24bpp  1000.000 fps  2342.1 kbps (285.9 kbyte/s)
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_VIDEOSTREAM))
	{
		buffer = buffer.trimmed();
		
		qDebug() << "InterfaceMplayer: parseStdoutClip() captured video stream data" << buffer;

		// Some defaults
		clip->mpeg_aspect = 0;	// undefined
		clip->display_aspect = 0.0;	// undefined
		clip->bpp = 0;			// undefined

		// Get MPEG codec: "VIDEO: MPEG2"
		QRegExp rx(RX_PARSE_MPLAYER_MPEG_CODEC);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->codec = rx.cap(1);
	
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video stream" 
				<< clip->codec << "in" << QString(cout.simplified());
		}
		else 
		{
			// Raw video: "VIDEO: []"
			rx.setPattern(RX_PARSE_MPLAYER_VIDEO_RAW);
			if (rx.indexIn(buffer) == 0)
			{
				clip->codec = "Raw";
		
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected video stream" 
					<< clip->codec << "in" << QString(cout.simplified());
			}
			else
			{
				// Other codecs: "VIDEO: [XVID]"
				rx.setPattern(RX_PARSE_MPLAYER_VIDEO_CODEC);
				if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 1)
				{
					clip->codec = rx.cap(1);
	
					// Mplayer's libavcodec (FFmpeg) codec and ffodivx
					if (clip->codec.startsWith("ffo")) clip->codec.remove(0, 3);
					else if (clip->codec.startsWith("ff")) clip->codec.remove(0, 2);

					qDebug() << "InterfaceMplayer: parseStdoutClip() detected video stream" 
						<< clip->codec << "in" << QString(cout.simplified());
				}
				else
				{
					clip->codec = "Unknown";

					qDebug() << "InterfaceMplayer: parseStdoutClip() detected video stream" 
						<< clip->codec << "in" << QString(cout.simplified());
				}
			}
		}

		// Get resolution: "624 x 352"
		rx.setPattern(RX_PARSE_MPLAYER_VIDEO_RESOLUTION);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 2)
		{
			clip->rez.setWidth( rx.cap(1).toInt() );
			clip->rez.setHeight( rx.cap(2).toInt() );

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video resolution" 
				<< clip->rez.width() << "x" << clip->rez.height() << "in" << QString(cout.simplified());
		}

		// Get color depth: "24bpp"
		rx.setPattern(RX_PARSE_MPLAYER_VIDEO_COLORDEPTH);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->bpp = rx.cap(1).toInt();

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video color depth" 
				<< clip->bpp << "in" << QString(cout.simplified());
		}

		// Get aspect ratio "(aspect 3)"
		rx.setPattern(RX_PARSE_MPLAYER_VIDEO_ASPECT);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->mpeg_aspect = rx.cap(1).toInt();

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video MPEG aspect ratio" 
				<< clip->mpeg_aspect << "coding" << Video::strDvdAspect(clip->mpeg_aspect) 
				<< "in" << QString(cout.simplified());
		}

		// Get FPS: "23.976 fps"
		rx.setPattern(RX_PARSE_MPLAYER_VIDEO_FPS);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->fps = rx.cap(1).toDouble();
			clip->mpeg_fps = parseFpsToMpegFps(rx.cap(1));

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video frame rate" 
				<< clip->fps << "[MPEG code:" << clip->mpeg_fps << "coding" << Video::strDvdFps(clip->mpeg_fps) 
				<< "fps] in" << QString(cout.simplified());
		}

		// Get KBPS: "890.0 kbps"
		rx.setPattern(RX_PARSE_MPLAYER_VIDEO_KBPS);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->kbps = rx.cap(1).toDouble();
	
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected video bit rate" 
				<< Video::strDvdKbps(clip->kbps) 
				<< "kbps in" << QString(cout.simplified());
		}

		*state = Video::Parsing::Stream;
		return true;
	}
	// DVD callbacks: There are ...
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDCALLBACK))
	{
	
		buffer = buffer.trimmed();

		// DVD chapters: There are 38 chapters in this DVD title.
		if (buffer.endsWith(RSTR_CATCH_MPLAYER_OUTPUT_DVDCHAPTERS))
		{
			buffer.remove(0, 9); // remove There are
			buffer.chop(28); // remove " chapters in this DVD title."
			int c = buffer.toInt(&ok);
			if ( ok )
			{
				clip->chapterN = c;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected" << c << "chapters"
					<< "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD chapter number in" << QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Chapters;
			return true;
		}
		// DVD angles: There are 1 angles in this DVD title.
		else if (buffer.endsWith(RSTR_CATCH_MPLAYER_OUTPUT_DVDANGLES))
		{
			buffer.remove(0, 9); // remove There are
			buffer.chop(26); // remove " angles in this DVD title."
			int a = buffer.toInt(&ok);
			if ( ok )
			{
				clip->angleN = a;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected" << a << "angles"
					<< "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD angle number in" << QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Angles;
			return true;
		}
	}
	// DVD callback: number of audio channels on disk: 3.
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDAUDIONUMBER))
	{
		buffer = buffer.trimmed();
		buffer.remove(0, 34); // remove number of audio channels on disk:
		buffer.chop(1); // remove "."
		int a = buffer.toInt(&ok);
		if ( ok )
		{
			clip->audioN = a;
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected" << a << "audio channels"
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD number of audio channels in" << QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::AudioNum;
		return true;
	}
	// DVD callback: audio stream: 0 format: ac3 (5.1) language: en aid: 128.
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDAUDIOSTREAM))
	{	
		buffer = buffer.trimmed();

		QRegExp rx(RX_PARSE_MPLAYER_DVDAUDIOSTREAM);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		rx.indexIn(buffer);
		if (rx.numCaptures() != 5)
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD audio stream info in" << QString(cout.simplified());
			return false;
		}
		else
		{
			// Fill in new audio channel info
			clip->audio.n = rx.cap(1).toInt();
			clip->audio.format = rx.cap(2);
			clip->audio.channels = rx.cap(3);
			clip->audio.language = rx.cap(4);
			clip->audio.aid = rx.cap(5).toInt();
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio stream" << clip->audio.n
				<< "[ id #" << clip->audio.aid 
				<< "language" << clip->audio.language 
				<< "format" << clip->audio.format << "channels" << clip->audio.channels
				<< "] in" << QString(cout.simplified());
		}
		*state = Video::Parsing::AudioStream;
		return true;
	}
	// DVD callback: number of subtitles on disk: 1
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDSUBNUMBER))
	{	
		buffer = buffer.trimmed();
		buffer.remove(0, 29); // remove number of audio channels on disk:
		int s = buffer.toInt(&ok);
		if ( ok )
		{
			clip->subtitleN = s;
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected" << s << "subtitles"
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD number of subtitles in" << QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::SubtitleNum;
		return true;
	}
	// DVD callback: subtitle ( sid ): 1 language: en
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDSUB))
	{	
		buffer = buffer.trimmed();

		QRegExp rx(RX_PARSE_MPLAYER_DVDSUB);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		rx.indexIn(buffer);
		if (rx.numCaptures() != 2)
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD subtitle in" << QString(cout.simplified());
			return false;
		}
		else
		{
			// Fill in new subtitle info
			clip->subtitle.sid = rx.cap(1).toInt();
			clip->subtitle.language = rx.cap(2);
			clip->subtitle.n = clip->subtitle.sid; // undefined on DVD, can't be -1 so copy sid
			qDebug() << "InterfaceMplayer: parseStdoutClip() detected subtitle" << clip->subtitle.sid 
				<< "[ language" << clip->subtitle.language
				<< "] in" << QString(cout.simplified());
		}
		*state = Video::Parsing::Subtitle;
		return true;
	}
	// File callback: AUDIO: 32000 Hz, 2 ch, s16le, 59.5 kbit/5.81% (ratio: 7441->128000)
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_AUDIOSTREAM))
	{
		buffer = buffer.trimmed();
		
		qDebug() << "InterfaceMplayer: parseStdoutClip() captured video stream data" << buffer;
	
		// Get audio sampling: "32000 Hz"
		QRegExp rx(RX_PARSE_MPLAYER_AUDIO_SAMPLING);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->audio.sampling = rx.cap(1).toInt();

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio sampling" 
				<< clip->audio.sampling << "Hz in" << QString(cout.simplified());
		}

		// Get audio sampling: "2 ch"
		rx.setPattern(RX_PARSE_MPLAYER_AUDIO_NCH);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->audio.channel_number = rx.cap(1).toInt();

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio channel number" 
				<< clip->audio.channel_number << "Hz in" << QString(cout.simplified());
		}
	
		// Get audio bitrate: "59.5 kbit ch"
		rx.setPattern(RX_PARSE_MPLAYER_AUDIO_KBPS);
		if (rx.indexIn(buffer) > 5 && rx.numCaptures() == 1)
		{
			clip->audio.kbps = rx.cap(1).toDouble();

			qDebug() << "InterfaceMplayer: parseStdoutClip() detected audio bitrate" 
				<< clip->audio.kbps << "Hz in" << QString(cout.simplified());
		}

		clip->audio.n = 1;
		clip->audio.aid = 0;

		*state = Video::Parsing::AudioMainStream;
		return true;
	}
	// lavf (MKV) callback: [lavf] stream 1: audio (dca), -aid 0, -alang fre, French DTS
	//                      [lavf] stream 2: audio (mp3), -aid 1, -alang eng
	//                      [lavf] stream 4: subtitle (unknown), -sid 0, -slang mis
	//                      [lavf] stream 5: subtitle (dvdsub), -sid 1, -slang fre
	// ignoring:            [lavf] stream 0: video (h264), -vid 0, Video
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_LAVFSTREAM))
	{	
		buffer = buffer.trimmed();

		// [lavf] stream 0: video
		// [lavf] stream 1: audio
		// [lavf] stream 4: subtitle
		QRegExp rx(RX_PARSE_MPLAYER_LAVFSTREAM);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		rx.indexIn(buffer);
		if (rx.numCaptures() != 2)
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() unidentified LAVF stream in" << QString(cout.simplified());
			return false;
		}
		else
		{
			if (rx.cap(2).compare("audio",Qt::CaseInsensitive) == 0)
			{
				// [lavf] stream 1: audio (dca), -aid 0, -alang fre, French DTS
				// [lavf] stream 2: audio (mp3), -aid 1, -alang eng
				QRegExp rx(RX_PARSE_MPLAYER_LAVFAUDIO);
				rx.setCaseSensitivity(Qt::CaseInsensitive);
				rx.setMinimal(false);
				rx.indexIn(buffer);
				if (rx.numCaptures() != 6)
				{
					qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse LAVF audio stream in" << QString(cout.simplified());
					return false;
				}
				else
				{
					// Fill in new audio info
					clip->audio.n = rx.cap(1).toInt();
					clip->audio.format = rx.cap(2);
					clip->audio.description = rx.cap(6);
					clip->audio.aid = rx.cap(3).toInt();
					clip->audio.language = rx.cap(4);
					qDebug() << "InterfaceMplayer: parseStdoutClip() detected LAVF audio stream" 
						<< clip->audio.aid 
						<< "[ stream" << clip->audio.n
						<< "language" << clip->audio.language
						<< "format" << clip->audio.format
						<< "description" << clip->audio.description
						<< "] in" << QString(cout.simplified());
					*state = Video::Parsing::AudioStream;
					return true;
				}
			}
			else if (rx.cap(2).compare("subtitle",Qt::CaseInsensitive) == 0)
			{
				// [lavf] stream 4: subtitle (unknown), -sid 0, -slang mis
				// [lavf] stream 5: subtitle (dvdsub), -sid 1, -slang fre
				// [lavf] stream 6: subtitle (unknown), -sid 1, -slang spa, Ranma 030 - Subs audio ESP
				QRegExp rx(RX_PARSE_MPLAYER_LAVFSUB);
				rx.setCaseSensitivity(Qt::CaseInsensitive);
				rx.setMinimal(false);
				rx.indexIn(buffer);
				if (rx.numCaptures() != 6)
				{
					qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse LAVF subtitle stream in" << QString(cout.simplified());
					return false;
				}
				else
				{
					// Fill in new subtitle info
					clip->subtitle.n = rx.cap(1).toInt();
					clip->subtitle.format = rx.cap(2);
					clip->subtitle.description = rx.cap(6);
					clip->subtitle.sid = rx.cap(3).toInt();
					clip->subtitle.language = rx.cap(4);
					qDebug() << "InterfaceMplayer: parseStdoutClip() detected LAVF subtitle stream" << clip->subtitle.sid 
						<< "[ stream" << clip->subtitle.n
						<< "language" << clip->subtitle.language
						<< "format" << clip->subtitle.format
						<< "description" << clip->subtitle.description
						<< "] in" << QString(cout.simplified());

					*state = Video::Parsing::Subtitle;
					return true;
				}
			}
		}

		qDebug() << "InterfaceMplayer: parseStdoutClip() ignored LAVF stream in" << QString(cout.simplified());
		
		*state = Video::Parsing::None;
		return true;
	}
	// MKV callback: [mkv] Track ID 2: audio (A_MS/ACM), -aid 0, -alang eng
	//               [mkv] Track ID 4: audio (A_AAC/MPEG2/LC/SBR) "commentaire", -aid 2, -alang eng
	//               [mkv] Track ID 5: subtitles (S_TEXT/UTF8), -sid 0, -slang mis
	//               [mkv] Track ID 6: subtitles (S_VOBSUB), -sid 1, -slang fre
	//               [mkv] Track ID 7: subtitles (S_TEXT/ASS) "Subs audio ESP", -sid 2, -slang spa
	// ignoring:     [mkv] Track ID 1: video (V_REAL/RV40), -vid 0
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_MKVTRACK))
	{	
		buffer = buffer.trimmed();

		// [mkv] Track ID 1: video
		// [mkv] Track ID 2: audio
		// [mkv] Track ID 5: subtitles
		QRegExp rx(RX_PARSE_MPLAYER_MKVTRACK);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		rx.indexIn(buffer);
		if (rx.numCaptures() != 2)
		{
			qDebug() << "InterfaceMplayer: parseStdoutClip() unidentified MKV stream in" << QString(cout.simplified());
			return false;
		}
		else
		{
			if (rx.cap(2).compare("audio",Qt::CaseInsensitive) == 0)
			{
				// [mkv] Track ID 2: audio (A_MS/ACM), -aid 0, -alang eng
				// [mkv] Track ID 4: audio (A_AAC/MPEG2/LC/SBR) "commentaire", -aid 2, -alang eng
				QRegExp rx(RX_PARSE_MPLAYER_MKVAUDIOTRACK);
				rx.setCaseSensitivity(Qt::CaseInsensitive);
				rx.setMinimal(false);
				rx.indexIn(buffer);
				if (rx.numCaptures() != 6)
				{
					qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse MKV audio stream in" << QString(cout.simplified());
					return false;
				}
				else
				{
					// Fill in new audio info
					clip->audio.n = rx.cap(1).toInt();
					clip->audio.format = rx.cap(2);
					clip->audio.description = rx.cap(4);
					clip->audio.aid = rx.cap(5).toInt();
					clip->audio.language = rx.cap(6);

					if (clip->audio.format.startsWith("ff"))
						clip->audio.format.remove(0, 2);  // Mplayer's libavcodec (FFmpeg) codec
					
					qDebug() << "InterfaceMplayer: parseStdoutClip() detected MKV audio stream" 
						<< clip->audio.aid 
						<< "[ track" << clip->audio.n
						<< "language" << clip->audio.language
						<< "format" << clip->audio.format
						<< "description" << clip->audio.description
						<< "] in" << QString(cout.simplified());
					*state = Video::Parsing::AudioStream;
					return true;
				}
			}
			else if (rx.cap(2).compare("subtitles",Qt::CaseInsensitive) == 0)
			{
				// [mkv] Track ID 6: subtitles (S_VOBSUB), -sid 1, -slang fre
				// [mkv] Track ID 7: subtitles (S_TEXT/ASS) "Subs audio ESP", -sid 2, -slang spa
				QRegExp rx(RX_PARSE_MPLAYER_MKVSUB);
				rx.setCaseSensitivity(Qt::CaseInsensitive);
				rx.setMinimal(false);
				rx.indexIn(buffer);
				if (rx.numCaptures() != 6)
				{
					qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse MKV subtitle stream in" << QString(cout.simplified());
					return false;
				}
				else
				{
					// Fill in new subtitle info
					clip->subtitle.n = rx.cap(1).toInt();
					clip->subtitle.format = rx.cap(2);
					clip->subtitle.description = rx.cap(4);
					clip->subtitle.sid = rx.cap(5).toInt();
					clip->subtitle.language = rx.cap(6);
					qDebug() << "InterfaceMplayer: parseStdoutClip() detected MKV subtitle stream" << clip->subtitle.sid 
						<< "[ track" << clip->subtitle.n
						<< "language" << clip->subtitle.language
						<< "format" << clip->subtitle.format
						<< "description" << clip->subtitle.description
						<< "] in" << QString(cout.simplified());

					*state = Video::Parsing::Subtitle;
					return true;
				}
			}
		}

		qDebug() << "InterfaceMplayer: parseStdoutClip() ignored MKV stream in" << QString(cout.simplified());
		
		*state = Video::Parsing::None;
		return true;
	}
	// AVI callback: [aviheader] Video stream found, -vid 0
	//               [aviheader] Audio stream found, -aid 1
	//               [aviheader] Audio stream found, -aid 2
	// and supposed: [aviheader] Subtitles stream found, -sid 1
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_AVISTREAM))
	{	
		buffer = buffer.trimmed();

		// [aviheader] Audio stream found, -aid 1
		QRegExp rx(RX_PARSE_MPLAYER_AVISTREAM);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		rx.indexIn(buffer);

		if (rx.numCaptures() == 3)
		{
			if (rx.cap(1).compare("audio",Qt::CaseInsensitive) == 0)
			{
				// Fill in new audio info
				clip->audio.n = clip->audio.aid = rx.cap(3).toInt();
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected AVI audio stream" 
					<< clip->audio.aid 
					<< "in" << QString(cout.simplified());
				*state = Video::Parsing::AudioStream;
				return true;
			}
			if (rx.cap(1).compare("subtitles",Qt::CaseInsensitive) == 0)
			{
				// Fill in new subtitle info
				clip->subtitle.n = clip->subtitle.sid = rx.cap(3).toInt();
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected AVI subtitle stream"
					<< clip->subtitle.sid 
					<< "in" << QString(cout.simplified());
				*state = Video::Parsing::Subtitle;
				return true;
			}
		}

		qDebug() << "InterfaceMplayer: parseStdoutClip() ignored AVI stream in" << QString(cout.simplified());
		
		*state = Video::Parsing::None;
		return true;
	}
	// CHAPTERS: 00:00:00,00:05:40,00:10:11,00:17:17,00:22:46,00:33:20,00:43:45,00:53:48,01:02:25,
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_CHAPTERS))
	{
		buffer.remove(0, 10); // remove "CHAPTERS: "
		QString chapterString = QString::fromLatin1(buffer);
		QStringList chapterList = chapterString.split(",", QString::SkipEmptyParts);

		for (int i = 0; i < chapterList.count(); ++i)
		{
			QTime time = QTime::fromString(chapterList[i], "hh:mm:ss");

			int seconds = 3600 * time.hour() + 60 * time.minute() + time.second();

			clip->chapter.append( (double)seconds );
		}
			
		clip->chapterN = clip->chapter.count();
		qDebug() << "InterfaceMplayer: parseStdoutClip() detected" <<  clip->chapterN
			<< "in" << QString(cout.simplified());

		*state = Video::Parsing::ChapterList;
		return true;
	}

	// Others are ignored
	qDebug() << "InterfaceMplayer: parseStdoutClip() ignored ouput" << QString(cout.simplified());
	
	*state = Video::Parsing::None;
	return true;
}

bool InterfaceMplayer::parseStdoutDisk(const QByteArray &cout, int *state, mplayer_video_disk_t *disk)
{
	bool ok;
	QRegExp rx;
	QByteArray buffer(cout.simplified());

	// Only DVD implemented yet
	if (disk->media != Video::Media::DVD)
	{
		qDebug() << "InterfaceMplayer: parseStdoutDisk() method not implemented for media"
			<< disk->media  << Video::strMedia(disk->media) 
			<< "ignoring" << QString(cout.simplified());
		return false;
	}

	// End of playback: Exiting... (Quit) or (End of file) or etc.
	if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_ENDPLAYBACK))
	{
		qDebug() << "InterfaceMplayer: parseStdoutDisk() detected end of playback" 
			<< QString(cout.simplified());
	
		*state = Video::Parsing::ExitPlayback;
		return true;
	}
	// DVD callback: Playing
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_PLAYING))
	{
		qDebug() << "InterfaceMplayer: parseStdoutDisk() detected end playing" 
			<< QString(cout.simplified());
	
		*state = Video::Parsing::Playing;
		return true;
	}
	// DVD callback: Starting playback...
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_STARTINGPLAYBACK))
	{
		qDebug() << "InterfaceMplayer: parseStdoutDisk() detected starting playback" 
			<< QString(cout.simplified());
	
		*state = Video::Parsing::StartingPlayback;
		return true;
	}
	// DVD callback: There are ...
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDCALLBACK))
	{
	
		buffer = buffer.trimmed();

		// DVD titles: There are 35 titles on this DVD.
		if (buffer.endsWith(RSTR_CATCH_MPLAYER_OUTPUT_DVDTITLES))
		{
			buffer.remove(0, 9); // remove There are
			buffer.chop(20); // remove " titles on this DVD."
			int t = buffer.toInt(&ok);
			if ( ok )
			{
				disk->titles = t;
				qDebug() << "InterfaceMplayer: parseStdoutDisk() detected" << t 
					<< "titles in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutDisk() failed to parse DVD title number in " 
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Titles;
			return true;
		}
	}
	// DVD callback: ID_DVD_VOLUME_ID= ...
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DVDVOLUMEID))
	{
		buffer.remove(0, 17); // remove ID_DVD_VOLUME_ID=
		QString volumeId = QString::fromLatin1(buffer);

		disk->volumeIdentifier = volumeId;
		qDebug() << "InterfaceMplayer: parseStdoutDisk() detected DVD volume identifier" << volumeId
			<< "in" << QString(cout.simplified());

		*state = Video::Parsing::VolumeId;
		return true;
	}
	// DVD identify callback: ID_DVD_ ...
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_ID_DVD))
	{
		buffer = buffer.trimmed();

		// DVD titles: ID_DVD_VOLUME_ID=MI2PALFR
		rx.setPattern(RX_PARSE_MPLAYER_ID_DVD_DISCID);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 1 && rx.cap(1).length() > 0)
		{
			disk->volumeIdentifier = rx.cap(1);
			qDebug() << "InterfaceMplayer: parseStdoutDisk() detected DVD volume identifier" 
				<< disk->volumeIdentifier << "in" << QString(cout.simplified());

			*state = Video::Parsing::VolumeId;
			return true;
		}

		// DVD titles: ID_DVD_TITLES=18
		rx.setPattern(RX_PARSE_MPLAYER_ID_DVD_TITLES);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 1)
		{
			bool ok;
			int n = rx.cap(1).toInt( &ok );
			if ( ok )
			{
				disk->titles = n;
				qDebug() << "InterfaceMplayer: parseStdoutDisk() detected" << n 
					<< "titles in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutDisk() failed to parse DVD title number in " 
					<< QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Titles;
			return true;
		}

		// DVD title chapters: ID_DVD_TITLE_1_CHAPTERS=17
		rx.setPattern(RX_PARSE_MPLAYER_ID_DVD_CHAPTERS);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 2)
		{
			bool ok1, ok2;
			int t = rx.cap(1).toInt( &ok1 );
			int c = rx.cap(2).toInt( &ok2 );
			if ( ok1 && ok2 )
			{
				//mplayer_video_disk_title_t *data = disk->title.data();
				//disk->title[t - 1].chapterN = c;

				disk->current = t;
				//disk->title[t - 1].chapterN = c;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected" << c << "chapters"
					<< "for title" << t << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD chapter number in" << QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Chapters;
			return true;
		}

		// DVD title chapters: ID_DVD_TITLE_1_ANGLES=1
		rx.setPattern(RX_PARSE_MPLAYER_ID_DVD_ANGLES);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 2)
		{
			bool ok1, ok2;
			int t = rx.cap(1).toInt( &ok1 );
			int a = rx.cap(2).toInt( &ok2 );
			if ( ok1 && ok2 )
			{
				//mplayer_video_disk_title_t *data = disk->title.data();
				//data[t - 1].angleN = a;

				disk->current = t;
				disk->title[t - 1].angleN = a;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected" << a << "angle(s)"
					<< "for title" << t << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD angle number in" << QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Angles;
			return true;
		}

		// DVD title length: ID_DVD_TITLE_6_LENGTH=832.920
		rx.setPattern(RX_PARSE_MPLAYER_ID_DVD_LENGTH);
		rx.setCaseSensitivity(Qt::CaseInsensitive);
		rx.setMinimal(false);
		if (rx.indexIn(buffer) == 0 && rx.numCaptures() == 2)
		{
			bool ok1, ok2;
			int t = rx.cap(1).toInt( &ok1 );
			int l = rx.cap(2).toDouble( &ok2 );
			if ( ok1 && ok2 )
			{
				//mplayer_video_disk_title_t *data = disk->title.data();
				//data[t - 1].length = l;

				disk->current = t;
				disk->title[t - 1].length = l;
				qDebug() << "InterfaceMplayer: parseStdoutClip() detected title" << t 
					<< "length" << l << "in" << QString(cout.simplified());
			}
			else
			{
				qDebug() << "InterfaceMplayer: parseStdoutClip() failed to parse DVD title length in" << QString(cout.simplified());
				return false;
			}
			*state = Video::Parsing::Length;
			return true;
		}
	}
	
	// Others are ignored
	qDebug() << "InterfaceMplayer: parseStdoutDisk() ignored output" << QString(cout.simplified());
	
	*state = Video::Parsing::None;
	return true;
}

bool InterfaceMplayer::parseStdoutRun(const QByteArray &cout, int *state, mplayer_video_running_t *run)
{
	bool ok;
	QByteArray buffer(cout.simplified());

	// answer to get_time_pos : ANS_TIME_POSITION=xx.y
	if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_POSITION))
	{
		buffer.remove(0, 18); // remove ANS_TIME_POSITION=
		double pos = buffer.toDouble(&ok);
		if ( ok )
		{
			run->position = pos;
			qDebug() << "InterfaceMplayer: parseStdoutRun() detected position" << pos
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'time position' in" << QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Position;
		return true;
	}
	// answer to get_property deinterlace : ANS_chapter=3 (starting at 0)
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_CHAPTER))
	{
		buffer.remove(0, 12); // remove ANS_chapter=
		bool ok;
		int chap = buffer.toDouble( &ok );
		if ( ok )
		{
			run->chapter = chap;

			qDebug() << "InterfaceMplayer: parseStdoutRun() detected current chapter" << chap
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'current chapter' in" 
				<< QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Chapter;
		return true;
	}
	// answer to get_video_resolution : ANS_VIDEO_RESOLUTION='<width> x <height>'
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_RESOLUTION))
	{
		buffer.remove(0, 21); // remove ANS_VIDEO_RESOLUTION=
		buffer.replace(QByteArray("'"), QByteArray(""));
		buffer.replace(QByteArray(" "), QByteArray(""));
		int sepIndex = buffer.indexOf('x');
		int x = buffer.left(sepIndex).toInt(&ok);
		if ( ok )
		{
			run->rez.setWidth( x );
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'resX' in" << QString(cout.simplified());
			return false;
		}
		int y = buffer.mid(sepIndex+1).toInt(&ok);
		if ( ok )
		{
			run->rez.setHeight( y );
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'resY' in" << QString(cout.simplified());
			return false;
		}
		qDebug() << "InterfaceMplayer: parseStdoutRun() detected resolution" << x << "x" << y
			<< "in" << QString(cout.simplified());
		*state = Video::Parsing::Resolution;
		return true;
	}
	// answer to get_property deinterlace : ANS_deinterlace=no
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_DEINTERLACE))
	{
		buffer.remove(0, 16); // remove ANS_deinterlace=
		if ( buffer == "yes" )
		{
			run->interlaced = 1;

			qDebug() << "InterfaceMplayer: parseStdoutRun() detected deinterlace ON"
				<< "in" << QString(cout.simplified());
		}
		else if ( buffer == "no" )
		{
			run->interlaced = 0;

			qDebug() << "InterfaceMplayer: parseStdoutRun() detected deinterlace OFF"
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'deinterlace' in" << QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Deinterlace;
		return true;
	}
	// answer to get_property speed : ANS_speed=d.dddddd
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_SPEED))
	{
		buffer.remove(0, 10); // remove ANS_speed=
		double playSpeed = buffer.toDouble(&ok);
		if ( ok )
		{
			run->speed = playSpeed;
			qDebug() << "InterfaceMplayer: parseStdoutRun() detected speed" << playSpeed
				<< "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'speed' in" << QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Speed;
		return true;
	}
	// Answer to get_time_length : ANS_LENGTH=xx.yy
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_LENGTH))
	{
		buffer.remove(0, 11); // remove ANS_LENGTH=
		double maxTime = buffer.toDouble(&ok);
		if ( ok && maxTime > 0.0)
		{
			run->length = maxTime;
			qDebug() << "InterfaceMplayer: parseStdoutRun() detected movie 'length'"
				<< maxTime << "in" << QString(cout.simplified());
		}
		else
		{
			qDebug() << "InterfaceMplayer: parseStdoutRun() failed to parse movie 'length' in "
				<< QString(cout.simplified());
			return false;
		}
		*state = Video::Parsing::Length;
		return true;
	}
	// End of playback: Exiting... (Quit) or (End of file) or etc.
	else if (buffer.startsWith(LSTR_CATCH_MPLAYER_OUTPUT_ENDPLAYBACK))
	{
		qDebug() << "InterfaceMplayer: parseStdoutRun() detected end of playback" 
			<< QString(cout.simplified());
	
		*state = Video::Parsing::ExitPlayback;
		return true;
	}

	// Others are ignored
	qDebug() << "InterfaceMplayer: parseStdoutRun() ignored output" << QString(cout.simplified());
	
	*state = Video::Parsing::None;
	return true;
}

void InterfaceMplayer::parseStderr(const QByteArray &cerr, int *state, QString *message)
{
	QByteArray buffer(cerr.simplified());

	// DVD callback: File not found
	if (buffer.startsWith(LSTR_CATCH_MPLAYER_ERROR_FILENOTFOUND))
	{
		qDebug() << "InterfaceMplayer: parseStderr() detected File not found" 
			<< QString(cerr.simplified());
	
		*message = QString(cerr.simplified());
		*state = Video::Parsing::FileNotFound;
		return;
	}

	// DVD callback: Can't open VMG info!
	if (buffer.startsWith(LSTR_CATCH_MPLAYER_ERROR_OPENVMG))
	{
		qDebug() << "InterfaceMplayer: parseStderr() detected no DVD VMG" 
			<< QString(cerr.simplified());
	
		*message = QString(cerr.simplified());
		*state = Video::Parsing::NoDvdVmg;
		return;
	}

	if (buffer.length() > 0)
	{
		qDebug() << "InterfaceMplayer: parseStderr() reported unmanaged info" 
		<< QString(cerr.simplified());
	
		*message = QString(cerr.simplified());
		*state = Video::Parsing::Error;
	}

	// Ignore empty lines
	*message = QString("");
	*state = Video::Parsing::Ignore;
}
void InterfaceMplayer::parseStatus()
{
	qDebug() << "InterfaceMplayer::parseStatus() starts parsing status raw data";

	if (status->state != Video::Status::RawStatus) 
		return;

	// Parse standard output

	QRegExp rx(RX_CATCH_MPLAYER_OUTPUT_STATUS);
	rx.setCaseSensitivity(Qt::CaseInsensitive);
	rx.setMinimal(true);
	rx.indexIn(status->cout);
	if (rx.numCaptures() != 1 || rx.cap(1).length() == 0)
	{
		qDebug("InterfaceMplayer::parseStatus() - Failed to parse MPlayer log (signature)");
		status->state = Video::Status::ParseFail;
		return;
	}
	QString signature = rx.cap(1);
	qDebug() << "InterfaceMplayer::parseStatus() - signature:in" << signature;

	rx.setPattern(RX_PARSE_MPLAYER_SIGNATURE);
	rx.setMinimal(true);
	rx.indexIn(signature);
	if (rx.numCaptures() != 3 || rx.cap(1).length() == 0 || rx.cap(2).length() == 0)
	{
		qDebug("InterfaceMplayer::parseStatus() - Failed to parse MPlayer log (parsing signature)");
		status->state = Video::Status::ParseFail;
		return;
	}
	status->player = rx.cap(1);
	status->version = rx.cap(2);

	rx.setPattern(RX_CATCH_MPLAYER_OUTPUT_HELP);
	rx.setMinimal(false);
	rx.indexIn(status->cout);
	if (rx.numCaptures() != 2)
	{
		qDebug("InterfaceMplayer::parseStatus() - Failed to parse MPlayer standard output (-ovc help -oac help)");
		status->state = Video::Status::ParseFail;
		return;
	}
	QString videodrivers = rx.cap(1);
	QString audiodrivers = rx.cap(2);
	qDebug() << "InterfaceMplayer::parseStatus() - available video:" << videodrivers;
	qDebug() << "InterfaceMplayer::parseStatus() - available audio:" << audiodrivers;

	driver_t driver;
	int pos;
	rx.setPattern(RX_PARSE_MPLAYER_CODECS);
	rx.setMinimal(true);

	pos = 0;
	while ((pos = rx.indexIn(videodrivers, pos)) != -1)
	{
		capability->video->insert( rx.cap(1), rx.cap(2) ); 

		qDebug() << "InterfaceMplayer::parseStatus() - found video driver available:"  << rx.cap(1) << rx.cap(2);

		pos += rx.matchedLength();
	}

	pos = 0;
	while ((pos = rx.indexIn(audiodrivers, pos)) != -1) 
	{
		capability->audio->insert( rx.cap(1), rx.cap(2) ); 

		qDebug() << "InterfaceMplayer::parseStatus() - found audio driver available:"  << rx.cap(1) << rx.cap(2);

		pos += rx.matchedLength();
	}

#if defined( WIN32 )
	status->supportsOpenGL = supportsVideoDriver("gl") || supportsVideoDriver("gl2");
#endif

	status->state = Video::Status::EngineOk;
}

int InterfaceMplayer::parseFpsToMpegFps(const QString &str)
{
	if (str.contains("24000/1001") || str.contains("23.976"))
		return 1;
	else if (str.contains("24"))
		return 2;
	else if (str.contains("25"))
		return 3;
	else if (str.contains("30000/1001") || str.contains("29.97"))
		return 4;
	else if (str.contains("30"))
		return 5;
	else if (str.contains("50"))
		return 6;
	else if (str.contains("60000/1001") || str.contains("59.94"))
		return 7;
	else if (str.contains("60"))
		return 8;
	else
		return -1;
}
