File indexing completed on 2024-05-12 04:52:07
0001 /* 0002 * mplayermediawidget.cpp 0003 * 0004 * Copyright (C) 2010-2011 Christoph Pfister <christophpfister@gmail.com> 0005 * 0006 * This program is free software; you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as published by 0008 * the Free Software Foundation; either version 2 of the License, or 0009 * (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License along 0017 * with this program; if not, write to the Free Software Foundation, Inc., 0018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0019 */ 0020 0021 #include <KLocalizedString> 0022 #include <KMessageBox> 0023 0024 #include "mplayermediawidget.h" 0025 #include "mplayervideowidget.h" 0026 0027 MPlayerMediaWidget::MPlayerMediaWidget(QWidget *parent) : AbstractMediaWidget(parent), 0028 muted(false), volume(0), aspectRatio(MediaWidget::AspectRatioAuto), deinterlacing(false), 0029 timerId(0), videoWidth(0), videoHeight(0), videoAspectRatio(1) 0030 { 0031 videoWidget = new MPlayerVideoWidget(this); 0032 standardError.open(stderr, QIODevice::WriteOnly); 0033 connect(&process, SIGNAL(error(QProcess::ProcessError)), 0034 this, SLOT(error(QProcess::ProcessError))); 0035 connect(&process, SIGNAL(readyReadStandardOutput()), this, SLOT(readStandardOutput())); 0036 connect(&process, SIGNAL(readyReadStandardError()), this, SLOT(readStandardError())); 0037 process.start(QString("mplayer -idle -osdlevel 0 -quiet -slave -softvol -vf yadif " 0038 "-volume 0 -wid %1").arg(videoWidget->winId())); 0039 } 0040 0041 MPlayerMediaWidget::~MPlayerMediaWidget() 0042 { 0043 sendCommand(Quit); 0044 process.waitForFinished(10000); 0045 } 0046 0047 AbstractMediaWidget *MPlayerMediaWidget::createMPlayerMediaWidget(QWidget *parent) 0048 { 0049 return new MPlayerMediaWidget(parent); 0050 } 0051 0052 void MPlayerMediaWidget::setMuted(bool muted_) 0053 { 0054 muted = muted_; 0055 sendCommand(SetVolume); 0056 } 0057 0058 void MPlayerMediaWidget::setVolume(int volume_) 0059 { 0060 volume = volume_; 0061 sendCommand(SetVolume); 0062 } 0063 0064 void MPlayerMediaWidget::setAspectRatio(MediaWidget::AspectRatio aspectRatio_) 0065 { 0066 aspectRatio = aspectRatio_; 0067 updateVideoWidgetGeometry(); 0068 } 0069 0070 void MPlayerMediaWidget::setDeinterlacing(MediaWidget::DeinterlaceMode deinterlacing); 0071 0072 { 0073 deinterlacing = deinterlacing_; 0074 sendCommand(SetDeinterlacing); 0075 } 0076 0077 void MPlayerMediaWidget::play(const MediaSource &source) 0078 { 0079 resetState(); 0080 QByteArray url = source.getUrl().toEncoded(); 0081 0082 switch (source.getType()) { 0083 case MediaSource::Url: 0084 if (url.endsWith(".iso")) { 0085 // FIXME use dvd://, dvdnav:// ? 0086 updateDvdMenu(true); 0087 } 0088 0089 if (source.getUrl().isLocalFile()) { 0090 // mplayer can't deal with urls like "file:///tmp/te%20st.m2t" 0091 url = QFile::encodeName(source.getUrl().toLocalFile()); 0092 url.replace(' ', "\\ "); 0093 } 0094 0095 break; 0096 case MediaSource::AudioCd: 0097 if (url.size() >= 7) { 0098 // e.g. cdda:////dev/sr0 0099 url.replace(0, 5, "cdda:/"); 0100 } else { 0101 url = "cdda://"; 0102 } 0103 0104 break; 0105 case MediaSource::VideoCd: 0106 if (url.size() >= 7) { 0107 // e.g. vcd:////dev/sr0 0108 url.replace(0, 5, "vcd:/"); 0109 } else { 0110 url = "vcd://"; 0111 } 0112 0113 break; 0114 case MediaSource::Dvd: 0115 if (url.size() >= 7) { 0116 // e.g. dvdnav:////dev/sr0 0117 url.replace(0, 5, "dvdnav:/"); 0118 } else { 0119 url = "dvdnav://"; 0120 } 0121 0122 updateDvdMenu(true); 0123 break; 0124 case MediaSource::Dvb: 0125 if (source.getUrl().isLocalFile()) { 0126 // mplayer can't deal with urls like "file:///tmp/te%20st.m2t" 0127 url = QFile::encodeName(source.getUrl().toLocalFile()); 0128 url.replace(' ', "\\ "); 0129 } 0130 0131 break; 0132 } 0133 0134 updatePlaybackStatus(MediaWidget::Playing); 0135 updateSeekable(true); 0136 process.write("loadfile " + url + '\n'); 0137 process.write("pausing_keep_force get_property path\n"); 0138 sendCommand(SetDeinterlacing); 0139 sendCommand(SetVolume); 0140 timerId = startTimer(500); 0141 } 0142 0143 void MPlayerMediaWidget::stop() 0144 { 0145 resetState(); 0146 sendCommand(Stop); 0147 } 0148 0149 void MPlayerMediaWidget::setPaused(bool paused) 0150 { 0151 switch (getPlaybackStatus()) { 0152 case MediaWidget::Idle: 0153 break; 0154 case MediaWidget::Playing: 0155 if (paused) { 0156 updatePlaybackStatus(MediaWidget::Paused); 0157 sendCommand(TogglePause); 0158 } 0159 0160 break; 0161 case MediaWidget::Paused: 0162 if (!paused) { 0163 updatePlaybackStatus(MediaWidget::Playing); 0164 sendCommand(TogglePause); 0165 } 0166 0167 break; 0168 } 0169 } 0170 0171 void MPlayerMediaWidget::seek(int time) 0172 { 0173 process.write("pausing_keep_force set_property time_pos " + 0174 QByteArray::number(float(time) / 1000) + '\n'); 0175 } 0176 0177 void MPlayerMediaWidget::setCurrentAudioStream(int currentAudioStream) 0178 { 0179 if ((currentAudioStream >= 0) && (currentAudioStream < audioIds.size())) { 0180 process.write("pausing_keep_force set_property switch_audio " + 0181 QByteArray::number(audioIds.at(currentAudioStream)) + 0182 "\npausing_keep_force get_property switch_audio\n"); 0183 } 0184 } 0185 0186 void MPlayerMediaWidget::setCurrentSubtitle(int currentSubtitle) 0187 { 0188 process.write("pausing_keep_force set_property sub " + 0189 QByteArray::number(currentSubtitle) + 0190 "\npausing_keep_force get_property sub\n"); 0191 } 0192 0193 void MPlayerMediaWidget::setExternalSubtitle(const QUrl &subtitleUrl) 0194 { 0195 // FIXME 0196 Q_UNUSED(subtitleUrl) 0197 } 0198 0199 void MPlayerMediaWidget::setCurrentTitle(int currentTitle) 0200 { 0201 process.write("pausing_keep_force set_property switch_title " + 0202 QByteArray::number(currentTitle) + 0203 "\npausing_keep_force get_property switch_title\n" 0204 "pausing_keep_force get_property chapter\n"); 0205 } 0206 0207 void MPlayerMediaWidget::setCurrentChapter(int currentChapter) 0208 { 0209 process.write("pausing_keep_force set_property chapter " + 0210 QByteArray::number(currentChapter) + 0211 "\npausing_keep_force get_property switch_title\n" 0212 "pausing_keep_force get_property chapter\n"); 0213 } 0214 0215 void MPlayerMediaWidget::setCurrentAngle(int currentAngle) 0216 { 0217 process.write("pausing_keep_force set_property switch_angle " + 0218 QByteArray::number(currentAngle) + '\n'); 0219 } 0220 0221 bool MPlayerMediaWidget::jumpToPreviousChapter() 0222 { 0223 if ((getCurrentChapter() - 1) >= 0) { 0224 setCurrentChapter(getCurrentChapter() - 1); 0225 return true; 0226 } 0227 0228 if ((getCurrentTitle() - 1) >= 0) { 0229 setCurrentTitle(getCurrentTitle() - 1); 0230 return true; 0231 } 0232 0233 return false; 0234 } 0235 0236 bool MPlayerMediaWidget::jumpToNextChapter() 0237 { 0238 if ((getCurrentChapter() + 1) < getChapterCount()) { 0239 setCurrentChapter(getCurrentChapter() + 1); 0240 return true; 0241 } 0242 0243 if ((getCurrentTitle() + 1) < getTitleCount()) { 0244 setCurrentTitle(getCurrentTitle() + 1); 0245 return true; 0246 } 0247 0248 return false; 0249 } 0250 0251 void MPlayerMediaWidget::showDvdMenu() 0252 { 0253 sendCommand(ShowDvdMenu); 0254 } 0255 0256 void MPlayerMediaWidget::error(QProcess::ProcessError error) 0257 { 0258 Q_UNUSED(error) 0259 KMessageBox::queuedMessageBox(this, KMessageBox::Error, 0260 i18n("Cannot start mplayer process.")); 0261 } 0262 0263 void MPlayerMediaWidget::readStandardOutput() 0264 { 0265 QByteArray data = process.readAllStandardOutput(); 0266 standardError.write(data); // forward 0267 standardError.flush(); 0268 0269 if ((data == "\n") || (data.indexOf("\n\n") >= 0)) { 0270 process.write("pausing_keep_force get_property path\n"); 0271 } 0272 0273 bool videoPropertiesChanged = false; 0274 QStringList audioStreams = getAudioStreams(); 0275 bool audioStreamsChanged = false; 0276 QStringList subtitles = getSubtitles(); 0277 bool subtitlesChanged = false; 0278 0279 foreach (const QByteArray &line, data.split('\n')) { 0280 if (line.startsWith("VO: ")) { 0281 videoPropertiesChanged = true; 0282 continue; 0283 } 0284 0285 if (line.startsWith("audio stream: ")) { 0286 int begin = 14; 0287 int end = line.indexOf(' ', begin); 0288 0289 if (end < 0) { 0290 end = line.size(); 0291 } 0292 0293 int audioStreamIndex = line.mid(begin, end - begin).toInt(); 0294 0295 while (audioStreams.size() < audioStreamIndex) { 0296 audioStreams.append(QString::number(audioStreams.size() + 1)); 0297 } 0298 0299 while (audioIds.size() < audioStreamIndex) { 0300 audioIds.append(-1); 0301 } 0302 0303 audioStreams.erase(audioStreams.begin() + audioStreamIndex, 0304 audioStreams.end()); 0305 audioIds.erase(audioIds.begin() + audioStreamIndex, audioIds.end()); 0306 QString audioStream; 0307 begin = line.indexOf("language: "); 0308 0309 if (begin >= 0) { 0310 begin += 10; 0311 end = line.indexOf(' ', begin); 0312 0313 if (end < 0) { 0314 end = line.size(); 0315 } 0316 0317 audioStream = line.mid(begin, end - begin); 0318 } 0319 0320 if (audioStream.isEmpty()) { 0321 audioStream = QString::number(audioStreams.size() + 1); 0322 } 0323 0324 int audioId = -1; 0325 begin = line.indexOf("aid: "); 0326 0327 if (begin >= 0) { 0328 begin += 5; 0329 end = line.indexOf('.', begin); 0330 0331 if (end < 0) { 0332 end = line.size(); 0333 } 0334 0335 audioId = line.mid(begin, end - begin).toInt(); 0336 } 0337 0338 audioStreams.append(audioStream); 0339 audioIds.append(audioId); 0340 audioStreamsChanged = true; 0341 continue; 0342 } 0343 0344 if (line.startsWith("subtitle ")) { 0345 int begin = line.indexOf("( sid ): "); 0346 0347 if (begin < 0) { 0348 continue; 0349 } 0350 0351 begin += 9; 0352 int end = line.indexOf(' ', begin); 0353 0354 if (end < 0) { 0355 end = line.size(); 0356 } 0357 0358 int subtitleIndex = line.mid(begin, end - begin).toInt(); 0359 0360 while (subtitles.size() < subtitleIndex) { 0361 subtitles.append(QString::number(subtitles.size() + 1)); 0362 } 0363 0364 subtitles.erase(subtitles.begin() + subtitleIndex, subtitles.end()); 0365 QString subtitle; 0366 begin = line.indexOf("language: "); 0367 0368 if (begin >= 0) { 0369 begin += 10; 0370 end = line.indexOf(' ', begin); 0371 0372 if (end < 0) { 0373 end = line.size(); 0374 } 0375 0376 subtitle = line.mid(begin, end - begin); 0377 } 0378 0379 if (subtitle.isEmpty()) { 0380 subtitle = QString::number(subtitles.size() + 1); 0381 } 0382 0383 subtitles.append(subtitle); 0384 subtitlesChanged = true; 0385 continue; 0386 } 0387 0388 if (line == "ANS_path=(null)") { 0389 switch (getPlaybackStatus()) { 0390 case MediaWidget::Idle: 0391 break; 0392 case MediaWidget::Playing: 0393 case MediaWidget::Paused: 0394 playbackFinished(); 0395 break; 0396 } 0397 0398 resetState(); 0399 continue; 0400 } 0401 0402 if (line.startsWith("ANS_length=")) { 0403 int totalTime = (line.mid(11).toFloat() * 1000 + 0.5); 0404 updateCurrentTotalTime(getCurrentTime(), totalTime); 0405 continue; 0406 } 0407 0408 if (line.startsWith("ANS_time_pos=")) { 0409 int currentTime = (line.mid(13).toFloat() * 1000 + 0.5); 0410 updateCurrentTotalTime(currentTime, getTotalTime()); 0411 continue; 0412 } 0413 0414 if (line.startsWith("ANS_width=")) { 0415 videoWidth = line.mid(10).toInt(); 0416 0417 if (videoWidth < 0) { 0418 videoWidth = 0; 0419 } 0420 0421 continue; 0422 } 0423 0424 if (line.startsWith("ANS_height=")) { 0425 videoHeight = line.mid(11).toInt(); 0426 0427 if (videoHeight < 0) { 0428 videoHeight = 0; 0429 } 0430 0431 continue; 0432 } 0433 0434 if (line.startsWith("ANS_aspect=")) { 0435 videoAspectRatio = line.mid(11).toFloat(); 0436 0437 if ((videoAspectRatio > 0.01) && (videoAspectRatio < 100)) { 0438 // ok 0439 } else { 0440 videoAspectRatio = (videoWidth / float(videoHeight)); 0441 0442 if ((videoAspectRatio > 0.01) && (videoAspectRatio < 100)) { 0443 // ok 0444 } else { 0445 videoAspectRatio = 1; 0446 } 0447 } 0448 0449 updateVideoWidgetGeometry(); 0450 continue; 0451 } 0452 0453 if (line.startsWith("ANS_switch_audio=")) { 0454 int audioId = line.mid(17).toInt(); 0455 updateCurrentAudioStream(audioIds.indexOf(audioId)); 0456 continue; 0457 } 0458 0459 if (line.startsWith("ANS_sub=")) { 0460 int currentSubtitle = line.mid(8).toInt(); 0461 updateCurrentSubtitle(currentSubtitle); 0462 continue; 0463 } 0464 } 0465 0466 if (videoPropertiesChanged) { 0467 process.write("pausing_keep_force get_property width\n" 0468 "pausing_keep_force get_property height\n" 0469 "pausing_keep_force get_property aspect\n"); 0470 } 0471 0472 if (audioStreamsChanged) { 0473 updateAudioStreams(audioStreams); 0474 process.write("pausing_keep_force get_property switch_audio\n"); 0475 } 0476 0477 if (subtitlesChanged) { 0478 updateSubtitles(subtitles); 0479 process.write("pausing_keep_force get_property sub\n"); 0480 } 0481 } 0482 0483 void MPlayerMediaWidget::readStandardError() 0484 { 0485 QByteArray data = process.readAllStandardError(); 0486 standardError.write(data); // forward 0487 standardError.flush(); 0488 } 0489 0490 void MPlayerMediaWidget::mouseMoved(int x, int y) 0491 { 0492 process.write("set_mouse_pos " + QByteArray::number(x) + ' ' + QByteArray::number(y) + 0493 '\n'); 0494 } 0495 0496 void MPlayerMediaWidget::mouseClicked() 0497 { 0498 process.write("dvdnav mouse\n"); 0499 } 0500 0501 void MPlayerMediaWidget::resetState() 0502 { 0503 resetBaseState(); 0504 0505 if (timerId != 0) { 0506 killTimer(timerId); 0507 timerId = 0; 0508 } 0509 0510 audioIds.clear(); 0511 videoWidth = 0; 0512 videoHeight = 0; 0513 videoAspectRatio = 1; 0514 updateVideoWidgetGeometry(); 0515 } 0516 0517 void MPlayerMediaWidget::resizeEvent(QResizeEvent *event) 0518 { 0519 updateVideoWidgetGeometry(); 0520 AbstractMediaWidget::resizeEvent(event); 0521 } 0522 0523 void MPlayerMediaWidget::sendCommand(Command command) 0524 { 0525 switch (command) { 0526 case SetDeinterlacing: 0527 if (getPlaybackStatus() == MediaWidget::Idle) { 0528 // only works if media is loaded 0529 break; 0530 } 0531 0532 process.write("pausing_keep_force set_property deinterlace %1\n", deinterlacing); 0533 0534 break; 0535 case SetVolume: { 0536 if (getPlaybackStatus() == MediaWidget::Idle) { 0537 // only works if media is loaded 0538 break; 0539 } 0540 0541 int realVolume = volume; 0542 0543 if (muted) { 0544 realVolume = 0; 0545 } 0546 0547 process.write("pausing_keep_force set_property volume " + 0548 QByteArray::number(realVolume) + '\n'); 0549 break; 0550 } 0551 case ShowDvdMenu: 0552 process.write("dvdnav menu\n"); 0553 break; 0554 case Stop: 0555 process.write("stop\n"); 0556 break; 0557 case TogglePause: 0558 process.write("pause\n"); 0559 break; 0560 case Quit: 0561 process.write("quit\n"); 0562 break; 0563 } 0564 } 0565 0566 void MPlayerMediaWidget::timerEvent(QTimerEvent *event) 0567 { 0568 Q_UNUSED(event) 0569 process.write("pausing_keep_force get_property length\n" 0570 "pausing_keep_force get_property time_pos\n"); 0571 } 0572 0573 void MPlayerMediaWidget::updateVideoWidgetGeometry() 0574 { 0575 float effectiveAspectRatio = videoAspectRatio; 0576 0577 switch (aspectRatio) { 0578 case MediaWidget::AspectRatioAuto: 0579 break; 0580 case MediaWidget::AspectRatio1_1: 0581 effectiveAspectRatio = 1; 0582 break; 0583 case MediaWidget::AspectRatio4_3: 0584 effectiveAspectRatio = (4.0 / 3.0); 0585 break; 0586 case MediaWidget::AspectRatio5_4: 0587 effectiveAspectRatio = (5.0 / 4.0); 0588 break; 0589 case MediaWidget::AspectRatio16_9: 0590 effectiveAspectRatio = (16.0 / 9.0); 0591 break; 0592 case MediaWidget::AspectRatio16_10: 0593 effectiveAspectRatio = (16.0 / 10.0); 0594 break; 0595 case MediaWidget::AspectRatio221_100: 0596 effectiveAspectRatio = (221.0 / 100.0); 0597 break; 0598 case MediaWidget::AspectRatio235_100: 0599 effectiveAspectRatio = (235.0 / 100.0); 0600 break; 0601 case MediaWidget::AspectRatio239_100: 0602 effectiveAspectRatio = (239.0 / 100.0); 0603 break; 0604 } 0605 0606 QRect geometry(QPoint(0, 0), size()); 0607 0608 if (getPlaybackStatus() == MediaWidget::Idle) { 0609 geometry.setSize(QSize(0, 0)); 0610 } else if (effectiveAspectRatio > 0) { 0611 int newWidth = (geometry.height() * effectiveAspectRatio + 0.5); 0612 0613 if (newWidth <= geometry.width()) { 0614 geometry.setX((geometry.width() - newWidth) / 2); 0615 geometry.setWidth(newWidth); 0616 } else { 0617 int newHeight = (geometry.width() / effectiveAspectRatio + 0.5); 0618 geometry.setY((geometry.height() - newHeight) / 2); 0619 geometry.setHeight(newHeight); 0620 } 0621 } 0622 0623 if (videoWidget->geometry() != geometry) { 0624 videoWidget->setGeometry(geometry); 0625 } 0626 } 0627 0628 #include "moc_mplayermediawidget.cpp"