/* smplayer, GUI front-end for mplayer. Copyright (C) 2007 Ricardo Villalba This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mplayerprocess.h" #include #include #include MplayerProcess::MplayerProcess(QObject * parent) : QProcess(parent) { connect( this, SIGNAL(readyReadStandardOutput()), this, SLOT(read()) ); connect( this, SIGNAL(readyReadStandardError()), this, SLOT(readFromStderr()) ); connect( this, SIGNAL(lineAvailable(QString)), this, SLOT(parseLine(QString)) ); incomplete_line = ""; notified_mplayer_is_running = FALSE; last_sub_id = -1; program=""; arg.clear(); setProcessChannelMode( QProcess::MergedChannels ); } MplayerProcess::~MplayerProcess() { } void MplayerProcess::clearArguments() { program=""; arg.clear(); } bool MplayerProcess::isRunning() { return (state() == QProcess::Running); } void MplayerProcess::writeToStdin ( const QString & buf ) { write(buf.latin1()); } void MplayerProcess::addArgument(const QString & a) { if (program.isEmpty()) { program = a; } else { arg.append(a); } } QStringList MplayerProcess::arguments() { QStringList l = arg; l.prepend(program); return l; } bool MplayerProcess::start( QStringList * env ) { md.reset(); notified_mplayer_is_running = FALSE; incomplete_line = ""; last_sub_id = -1; //setEnvironment(env); QProcess::start(program, arg); } void MplayerProcess::readFromStderr() { /* QString line; while (canReadLineStderr()) { line = "stderr: " + readLineStderr(); emit lineAvailable(line); } */ } void MplayerProcess::read() { //qDebug("MplayerProcess::read"); QString line; QString l = incomplete_line; l += readAllStandardOutput(); l = l.replace(0x0D, '\n'); QStringList buffer; buffer.clear(); int pos = l.find('\n'); while (pos > -1) { buffer.append( l.left(pos) ); l = l.mid(pos+1); #ifdef Q_OS_WIN // If line starts with \n, remove it if (l.startsWith("\n")) l = l.mid(1); #endif pos = l.find('\n'); } incomplete_line = l; //qDebug("incomplete_line: '%s'", incomplete_line.utf8().data()); for ( QStringList::Iterator it = buffer.begin(); it != buffer.end(); ++it ) { line = QTextCodec::codecForLocale()->toUnicode( (*it) ); //qDebug( "line: %s", line.utf8().data());; emit lineAvailable(line); } } QRegExp rx_av("^[AV]: *([0-9,:.-]+)"); QRegExp rx_frame("^[AV]:.* (\\d+)\\/.\\d+");// [0-9,.]+"); QRegExp rx("^(.*)=(.*)"); QRegExp rx_subs("^ID_SID_(\\d+)_(LANG|NAME)=(.*)"); QRegExp rx_audio_mat("^ID_AID_(\\d+)_(LANG|NAME)=(.*)"); QRegExp rx_title("^ID_DVD_TITLE_(\\d+)_(LENGTH|CHAPTERS|ANGLES)=(.*)"); QRegExp rx_winresolution("^VO: \\[(.*)\\] (\\d+)x(\\d+) => (\\d+)x(\\d+)"); QRegExp rx_ao("^AO: \\[(.*)\\]"); QRegExp rx_paused("^ID_PAUSED"); QRegExp rx_novideo("^Video: no video"); QRegExp rx_cache("^Cache fill:.*"); QRegExp rx_create_index("^Generating Index:.*"); QRegExp rx_play("^Starting playback..."); QRegExp rx_connecting("^Connecting to .*"); QRegExp rx_resolving("^Resolving .*"); QRegExp rx_screenshot("^\\*\\*\\* screenshot '(.*)'"); QRegExp rx_endoffile("^Exiting... \\(End of file\\)"); QRegExp rx_mkvchapters("\\[mkv\\] Chapter (\\d+) from"); //Clip info QRegExp rx_clip_name("^ (name|title): (.*)"); QRegExp rx_clip_artist("^ artist: (.*)"); QRegExp rx_clip_author("^ author: (.*)"); QRegExp rx_clip_album("^ album: (.*)"); QRegExp rx_clip_genre("^ genre: (.*)"); QRegExp rx_clip_date("^ (creation date|year): (.*)"); QRegExp rx_clip_track("^ track: (.*)"); QRegExp rx_clip_copyright("^ copyright: (.*)"); QRegExp rx_clip_comment("^ comment: (.*)"); QRegExp rx_clip_software("^ software: (.*)"); void MplayerProcess::parseLine(QString line) { //qDebug("MplayerProcess::parseLine: '%s'", line.utf8().data() ); rx_clip_name.setCaseSensitive(FALSE); rx_clip_artist.setCaseSensitive(FALSE); rx_clip_author.setCaseSensitive(FALSE); rx_clip_album.setCaseSensitive(FALSE); rx_clip_genre.setCaseSensitive(FALSE); rx_clip_date.setCaseSensitive(FALSE); rx_clip_track.setCaseSensitive(FALSE); rx_clip_copyright.setCaseSensitive(FALSE); rx_clip_comment.setCaseSensitive(FALSE); rx_clip_software.setCaseSensitive(FALSE); int pos; QString tag; QString value; // Parse A: V: line //qDebug("%s", line.utf8().data()); pos = rx_av.search(line); if (pos > -1) { double sec = rx_av.cap(1).toDouble(); //qDebug("cap(1): '%s'", rx_av.cap(1).utf8().data() ); //qDebug("sec: %f", sec); if (!notified_mplayer_is_running) { emit mplayerFullyLoaded(); notified_mplayer_is_running = TRUE; } emit receivedCurrentSec( sec ); // Check for frame if (rx_frame.search(line) > -1) { int frame = rx_frame.cap(1).toInt(); //qDebug(" frame: %d", frame); emit receivedCurrentFrame(frame); } } else { // Parse other things qDebug("MplayerProcess::parseLine: '%s'", line.utf8().data() ); // Window resolution pos = rx_winresolution.search(line); if (pos > -1) { /* md.win_width = rx_winresolution.cap(4).toInt(); md.win_height = rx_winresolution.cap(5).toInt(); md.video_aspect = (double) md.win_width / md.win_height; */ int w = rx_winresolution.cap(4).toInt(); int h = rx_winresolution.cap(5).toInt(); emit receivedVO( rx_winresolution.cap(1) ); emit receivedWindowResolution( w, h ); //emit mplayerFullyLoaded(); } // AO pos = rx_ao.search(line); if (pos > -1) { emit receivedAO( rx_ao.cap(1) ); } // No video pos = rx_novideo.search(line); if (pos > -1) { md.novideo = TRUE; emit receivedNoVideo(); //emit mplayerFullyLoaded(); } // Pause pos = rx_paused.search(line); if (pos > -1) { emit receivedPause(); } // Matroska subtitles pos = rx_subs.search(line); if (pos > -1) { int ID = rx_subs.cap(1).toInt(); QString lang = rx_subs.cap(3); QString t = rx_subs.cap(2); qDebug("MplayerProcess::parseLine: Subs: ID: %d, Lang: '%s' Type: '%s'", ID, lang.utf8().data(), t.utf8().data()); if ( t == "NAME" ) md.subtitles.addName(ID, lang); else md.subtitles.addLang(ID, lang); } // Matroska audio pos = rx_audio_mat.search(line); if (pos > -1) { int ID = rx_audio_mat.cap(1).toInt(); QString lang = rx_audio_mat.cap(3); QString t = rx_audio_mat.cap(2); qDebug("MplayerProcess::parseLine: Audio: ID: %d, Lang: '%s' Type: '%s'", ID, lang.utf8().data(), t.utf8().data()); if ( t == "NAME" ) md.audios.addName(ID, lang); else md.audios.addLang(ID, lang); } // Matroshka chapters if (rx_mkvchapters.search(line)!=-1) { int c = rx_mkvchapters.cap(1).toInt(); qDebug("MplayerProcess::parseLine: mkv chapters: %d", c); if (c > md.mkv_chapters) { md.mkv_chapters = c; qDebug("MplayerProcess::parseLine: mkv_chapters set to: %d", c); } } // DVD titles pos = rx_title.search(line); if (pos > -1) { int ID = rx_title.cap(1).toInt(); QString t = rx_title.cap(2); if (t=="LENGTH") { double length = rx_title.cap(3).toDouble(); qDebug("MplayerProcess::parseLine: Title: ID: %d, Length: '%f'", ID, length); md.titles.addDuration(ID, length); } else if (t=="CHAPTERS") { int chapters = rx_title.cap(3).toInt(); qDebug("MplayerProcess::parseLine: Title: ID: %d, Chapters: '%d'", ID, chapters); md.titles.addChapters(ID, chapters); } else if (t=="ANGLES") { int angles = rx_title.cap(3).toInt(); qDebug("MplayerProcess::parseLine: Title: ID: %d, Angles: '%d'", ID, angles); md.titles.addAngles(ID, angles); } } // Catch cache messages pos = rx_cache.search(line); if (pos > -1) { emit receivedCacheMessage(line); } pos = rx_create_index.search(line); if (pos > -1) { emit receivedCreatingIndex(line); } // Catch connecting message pos = rx_connecting.search(line); if (pos > -1) { emit receivedConnectingToMessage(line); } // Catch resolving message pos = rx_resolving.search(line); if (pos > -1) { emit receivedResolvingMessage(line); } pos = rx_screenshot.search(line); if (pos > -1) { QString shot = rx_screenshot.cap(1); qDebug("MplayerProcess::parseLine: screenshot: '%s'", shot.utf8().data()); emit receivedScreenshot( shot ); } if (rx_endoffile.search(line) > -1) { qDebug("MplayerProcess::parseLine: detected end of file"); emit receivedEndOfFile(); } // Clip info // Name if (rx_clip_name.search(line) > -1) { QString s = rx_clip_name.cap(2); qDebug("MplayerProcess::parseLine: clip_name: '%s'", s.utf8().data()); md.clip_name = s; } // Artist if (rx_clip_artist.search(line) > -1) { QString s = rx_clip_artist.cap(1); qDebug("MplayerProcess::parseLine: clip_artist: '%s'", s.utf8().data()); md.clip_artist = s; } // Author if (rx_clip_author.search(line) > -1) { QString s = rx_clip_author.cap(1); qDebug("MplayerProcess::parseLine: clip_author: '%s'", s.utf8().data()); md.clip_author = s; } // Album if (rx_clip_album.search(line) > -1) { QString s = rx_clip_album.cap(1); qDebug("MplayerProcess::parseLine: clip_album: '%s'", s.utf8().data()); md.clip_album = s; } // Genre if (rx_clip_genre.search(line) > -1) { QString s = rx_clip_genre.cap(1); qDebug("MplayerProcess::parseLine: clip_genre: '%s'", s.utf8().data()); md.clip_genre = s; } // Date if (rx_clip_date.search(line) > -1) { QString s = rx_clip_date.cap(2); qDebug("MplayerProcess::parseLine: clip_date: '%s'", s.utf8().data()); md.clip_date = s; } // Track if (rx_clip_track.search(line) > -1) { QString s = rx_clip_track.cap(1); qDebug("MplayerProcess::parseLine: clip_track: '%s'", s.utf8().data()); md.clip_track = s; } // Copyright if (rx_clip_copyright.search(line) > -1) { QString s = rx_clip_copyright.cap(1); qDebug("MplayerProcess::parseLine: clip_copyright: '%s'", s.utf8().data()); md.clip_copyright = s; } // Comment if (rx_clip_comment.search(line) > -1) { QString s = rx_clip_comment.cap(1); qDebug("MplayerProcess::parseLine: clip_comment: '%s'", s.utf8().data()); md.clip_comment = s; } // Software if (rx_clip_software.search(line) > -1) { QString s = rx_clip_software.cap(1); qDebug("MplayerProcess::parseLine: clip_software: '%s'", s.utf8().data()); md.clip_software = s; } // Catch starting message /* pos = rx_play.search(line); if (pos > -1) { emit mplayerFullyLoaded(); } */ //Generic things pos = rx.search(line); if (pos > -1) { tag = rx.cap(1); value = rx.cap(2); //qDebug("MplayerProcess::parseLine: tag: %s, value: %s", tag.utf8().data(), value.utf8().data()); // Generic audio if (tag == "ID_AUDIO_ID") { int ID = value.toInt(); qDebug("MplayerProcess::parseLine: ID_AUDIO_ID: %d", ID); md.audios.addID( ID ); } else // Generic subtitle if (tag == "ID_SUBTITLE_ID") { int ID = value.toInt(); qDebug("MplayerProcess::parseLine: ID_SUBTITLE_ID: %d", ID); md.subtitles.addID( ID ); } else // Avi subs (srt, sub...) if (tag == "ID_FILE_SUB_ID") { int ID = value.toInt(); qDebug("MplayerProcess::parseLine: SUB_ID: %d", ID); md.subtitles.addID( ID ); last_sub_id = ID; } else if (tag == "ID_FILE_SUB_FILENAME") { QString name = value; qDebug("MplayerProcess::parseLine: SUB_FILENAME: %s", name.utf8().data() ); if (last_sub_id != -1) md.subtitles.addFilename( last_sub_id, name ); /* if (!md.subtitles.existsFilename(name)) { int new_id = md.subtitles.lastID() + 1; qDebug("MplayerProcess::parseLine: creating new id for file sub: %d", new_id); md.subtitles.addFilename( new_id, value ); } */ } if (tag == "ID_LENGTH") { md.duration = value.toDouble(); qDebug("MplayerProcess::parseLine: md.duration set to %f", md.duration); } else if (tag == "ID_VIDEO_WIDTH") { md.video_width = value.toInt(); qDebug("MplayerProcess::parseLine: md.video_width set to %d", md.video_width); } else if (tag == "ID_VIDEO_HEIGHT") { md.video_height = value.toInt(); qDebug("MplayerProcess::parseLine: md.video_height set to %d", md.video_height); } else if (tag == "ID_VIDEO_ASPECT") { md.video_aspect = value.toDouble(); if ( md.video_aspect == 0.0 ) { // I hope width & height are already set. md.video_aspect = (double) md.video_width / md.video_height; } qDebug("MplayerProcess::parseLine: md.video_aspect set to %f", md.video_aspect); } else if (tag == "ID_DVD_DISC_ID") { md.dvd_id = value; qDebug("MplayerProcess::parseLine: md.dvd_id set to '%s'", md.dvd_id.utf8().data()); } else if (tag == "ID_DEMUXER") { md.demuxer = value; } else if (tag == "ID_VIDEO_FORMAT") { md.video_format = value; } else if (tag == "ID_AUDIO_FORMAT") { md.audio_format = value; } else if (tag == "ID_VIDEO_BITRATE") { md.video_bitrate = value.toInt(); } else if (tag == "ID_VIDEO_FPS") { md.video_fps = value; } else if (tag == "ID_AUDIO_BITRATE") { md.audio_bitrate = value.toInt(); } else if (tag == "ID_AUDIO_RATE") { md.audio_rate = value.toInt(); } else if (tag == "ID_AUDIO_NCH") { md.audio_nch = value.toInt(); } else if (tag == "ID_VIDEO_CODEC") { md.video_codec = value; } else if (tag == "ID_AUDIO_CODEC") { md.audio_codec = value; } } } }