File indexing completed on 2024-04-28 04:49:43
0001 /* 0002 SPDX-FileCopyrightText: 2016-2017 Leslie Zhai <lesliezhai@llvm.org.cn> 0003 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include <config-k3b.h> 0009 0010 #include "k3bcdrskinwriter.h" 0011 0012 #include "k3bcore.h" 0013 #include "k3bexternalbinmanager.h" 0014 #include "k3bprocess.h" 0015 #include "k3bdevice.h" 0016 #include "k3bdeviceglobals.h" 0017 #include "k3bdevicemanager.h" 0018 #include "k3bdevicehandler.h" 0019 #include "k3bglobals.h" 0020 #include "k3bthroughputestimator.h" 0021 #include "k3bglobalsettings.h" 0022 0023 #include <QDebug> 0024 #include <QString> 0025 #include <QStringList> 0026 #include <QRegExp> 0027 #include <QRegularExpression> 0028 #include <QFile> 0029 #include "k3b_i18n.h" 0030 0031 #include <QTemporaryFile> 0032 0033 0034 Q_DECLARE_METATYPE( QProcess::ExitStatus ) 0035 0036 0037 class K3b::CdrskinWriter::Private 0038 { 0039 public: 0040 Private() 0041 : cdTextFile(0) { 0042 } 0043 0044 const ExternalBin* cdrskinBinObject; 0045 Process process; 0046 0047 WritingMode writingMode; 0048 FormattingMode formattingMode; 0049 bool totalTracksParsed; 0050 bool clone; 0051 bool cue; 0052 bool multi; 0053 bool force; 0054 bool formatting; 0055 0056 QString cueFile; 0057 QStringList arguments; 0058 0059 int currentTrack; 0060 int totalTracks; 0061 int totalSize; 0062 int alreadyWritten; 0063 0064 int lastFifoValue; 0065 0066 int cdrskinError; 0067 bool writingStarted; 0068 0069 QByteArray rawCdText; 0070 0071 K3b::ThroughputEstimator* speedEst; 0072 bool canceled; 0073 bool usingBurnfree; 0074 int usedSpeed; 0075 0076 struct Track { 0077 int size; 0078 bool audio; 0079 }; 0080 0081 QList<Track> tracks; 0082 0083 QTemporaryFile* cdTextFile; 0084 0085 Device::MediaType burnedMediaType; 0086 K3b::Device::SpeedMultiplicator usedSpeedFactor; 0087 }; 0088 0089 0090 K3b::CdrskinWriter::CdrskinWriter( K3b::Device::Device* dev, K3b::JobHandler* hdl, 0091 QObject* parent ) 0092 : K3b::AbstractWriter( dev, hdl, parent ) 0093 { 0094 d = new Private(); 0095 d->speedEst = new K3b::ThroughputEstimator( this ); 0096 connect( d->speedEst, SIGNAL(throughput(int)), 0097 this, SLOT(slotThroughput(int)) ); 0098 0099 d->writingMode = K3b::WritingModeTao; 0100 d->formattingMode = K3b::FormattingQuick; 0101 d->clone = false; 0102 d->cue = false; 0103 d->multi = false; 0104 d->force = false; 0105 d->formatting = false; 0106 0107 d->process.setSplitStdout(true); 0108 d->process.setSuppressEmptyLines(true); 0109 d->process.setFlags( K3bQProcess::RawStdin ); 0110 connect( &d->process, SIGNAL(stdoutLine(QString)), this, SLOT(slotStdLine(QString)) ); 0111 0112 // we use a queued connection to give the process object time to wrap up and return to a correct state 0113 qRegisterMetaType<QProcess::ExitStatus>(); 0114 connect( &d->process, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotProcessExited(int,QProcess::ExitStatus)), Qt::QueuedConnection ); 0115 } 0116 0117 0118 K3b::CdrskinWriter::~CdrskinWriter() 0119 { 0120 delete d->cdTextFile; 0121 delete d; 0122 } 0123 0124 0125 bool K3b::CdrskinWriter::active() const 0126 { 0127 return d->process.isRunning(); 0128 } 0129 0130 0131 QIODevice* K3b::CdrskinWriter::ioDevice() const 0132 { 0133 return &d->process; 0134 } 0135 0136 0137 void K3b::CdrskinWriter::setDao( bool b ) 0138 { 0139 d->writingMode = ( b ? K3b::WritingModeSao : K3b::WritingModeTao ); 0140 } 0141 0142 0143 void K3b::CdrskinWriter::setCueFile( const QString& s) 0144 { 0145 d->cue = true; 0146 d->cueFile = s; 0147 0148 // cuefile only works in DAO mode 0149 setWritingMode( K3b::WritingModeSao ); 0150 } 0151 0152 0153 void K3b::CdrskinWriter::setClone( bool b ) 0154 { 0155 d->clone = b; 0156 } 0157 0158 0159 void K3b::CdrskinWriter::setRawCdText( const QByteArray& a ) 0160 { 0161 d->rawCdText = a; 0162 } 0163 0164 0165 void K3b::CdrskinWriter::setWritingMode( K3b::WritingMode mode ) 0166 { 0167 d->writingMode = mode; 0168 } 0169 0170 0171 void K3b::CdrskinWriter::setFormattingMode( FormattingMode mode ) 0172 { 0173 d->formattingMode = mode; 0174 d->formatting = true; 0175 } 0176 0177 0178 void K3b::CdrskinWriter::setMulti( bool b ) 0179 { 0180 d->multi = b; 0181 } 0182 0183 0184 void K3b::CdrskinWriter::setForce( bool b ) 0185 { 0186 d->force = b; 0187 } 0188 0189 0190 bool K3b::CdrskinWriter::prepareProcess() 0191 { 0192 d->cdrskinBinObject = k3bcore->externalBinManager()->binObject("cdrskin"); 0193 0194 if( !d->cdrskinBinObject ) { 0195 emit infoMessage( i18n("Could not find %1 executable.", QLatin1String("cdrskin")), MessageError ); 0196 return false; 0197 } 0198 0199 d->process.clearProgram(); 0200 0201 d->burnedMediaType = burnDevice()->mediaType(); 0202 0203 d->process << d->cdrskinBinObject; 0204 0205 // display progress 0206 d->process << "-v"; 0207 #ifdef K3B_DEBUG 0208 d->process << "-V"; 0209 #endif 0210 if (d->cdrskinBinObject->hasFeature("gracetime")) 0211 d->process << "gracetime=0"; // quote: I am Thomas and i allow gracetime=0. :) 0212 // Further cdrskin's gracetime default is 0. 0213 // Only with undersized tracks (< 600 KB) the gracetime is at least 15 seconds 0214 // in order to let a command line user abort the run. 0215 0216 // Again we assume the device to be set! 0217 d->process << QString("dev=%1").arg(K3b::externalBinDeviceParameter(burnDevice(), d->cdrskinBinObject)); 0218 0219 d->usedSpeedFactor = K3b::speedMultiplicatorForMediaType( d->burnedMediaType ); 0220 d->usedSpeed = burnSpeed(); 0221 if( d->usedSpeed == 0 ) { 0222 // try to determine the writeSpeed 0223 // if it fails determineMaximalWriteSpeed() will return 0 and 0224 // the choice is left to cdrskin 0225 d->usedSpeed = burnDevice()->determineMaximalWriteSpeed(); 0226 } 0227 0228 // cdrskin accepts special speed values "0" = slowest, "-1" or "any" = fastest. 0229 // Default is fastest speed. 0230 if (d->usedSpeed != 0) { 0231 // TODO: so does need to check formatWritingSpeedFactor? 0232 // Obviously the emulated drive (CDEmu) does not honor the speed wishes issued 0233 // by libburn. (It is so fast that the watcher thread of cdrskin misses 0234 // several MB between its inquiries.) 0235 d->process << QString("speed=%1").arg(formatWritingSpeedFactor(d->usedSpeed, d->burnedMediaType, SpeedFormatInteger)); 0236 } 0237 0238 0239 if (K3b::Device::isBdMedia(d->burnedMediaType)) { 0240 if (!d->cdrskinBinObject->hasFeature("blu-ray")) { 0241 emit infoMessage(i18n("Cdrskin version %1 does not support Blu-ray writing.", d->cdrskinBinObject->version()), MessageError); 0242 // Never mind! bin.addFeature("blu-ray"); when cdrskin' version >= 0.6.2 0243 } 0244 d->process << "-sao"; 0245 } 0246 else if ( K3b::Device::isDvdMedia( d->burnedMediaType ) ) { 0247 // It supports -tao on all media except fast-blanked DVD-RW and DVD-R DL, 0248 // which both support only write type DAO. 0249 // On DVD+RW and alike, there is no difference between -sao and -tao. 0250 // On DVD-R, -tao chooses Incremental mode which is capable of -multi and 0251 // of appending new sessions to existing Incremental sessions. 0252 // On DVD+R, -sao emits a command RESERVE TRACK, whereas -tao does not. 0253 // 0254 // One may omit both -tao and -sao in order to let cdrskin decide on base 0255 // of -multi, input source and Medium state which write type to use. 0256 if (burnDevice()->vendor().contains("CDEmu") || burnDevice()->description().contains("Virt")) 0257 d->process << "-tao"; 0258 #ifdef K3B_DEBUG 0259 qDebug() << "DEBUG:" << __PRETTY_FUNCTION__ << "K3B user get *NO* " 0260 "opportunity via UI to explicitly choose SAO/DAO" << 0261 "let libburn choose the write type according to other parameters " 0262 "and the medium state."; 0263 #endif 0264 } 0265 else if( K3b::Device::isCdMedia( d->burnedMediaType ) ) { 0266 if( d->writingMode == K3b::WritingModeSao || d->cue ) { 0267 if( burnDevice()->dao() ) 0268 d->process << "-sao"; 0269 else { 0270 if( d->cdrskinBinObject->hasFeature( "tao" ) ) 0271 d->process << "-tao"; 0272 emit infoMessage( i18n("Writer does not support disk at once (DAO) recording"), MessageWarning ); 0273 } 0274 } 0275 else if( d->writingMode == K3b::WritingModeRaw ) { 0276 // These write modes are not supported by cdrskin. They need to be made 0277 // conditional. 0278 // cdrskin supports only -audio and -data. 0279 // Options -xa1, -xa, -xa2, -mode2 do not lead to error but the payload is 0280 // nevertheless written as -data. 0281 emit infoMessage(i18n("Writer does not support raw writing."), MessageError); 0282 } 0283 else if( d->cdrskinBinObject->hasFeature( "tao" ) ) 0284 d->process << "-tao"; 0285 } 0286 else { 0287 emit infoMessage( i18n( "Cdrskin does not support writing %1 media." , K3b::Device::mediaTypeString( d->burnedMediaType ) ), MessageError ); 0288 // FIXME: add a way to fail the whole thing here 0289 } 0290 0291 if (simulate()) 0292 d->process << "-dummy"; 0293 0294 d->usingBurnfree = false; 0295 if (k3bcore->globalSettings()->burnfree()) { 0296 if (burnDevice()->burnproof()) { 0297 d->usingBurnfree = true; 0298 // "burnproof" is accepted as alias of "burnfree". 0299 d->process << "driveropts=burnfree"; 0300 } 0301 else 0302 emit infoMessage( i18n("Writer does not support buffer underrun free recording (Burnfree)"), MessageWarning ); 0303 } 0304 0305 if( k3bcore->globalSettings()->force() || d->force ) { 0306 d->process << "-force"; 0307 emit infoMessage( i18n("'Force unsafe operations' enabled."), MessageWarning ); 0308 } 0309 0310 if( d->cue ) { 0311 d->process.setWorkingDirectory( d->cueFile ); 0312 d->process << QString("cuefile=%1").arg( d->cueFile ); 0313 } 0314 0315 if( d->clone ) 0316 d->process << "-clone"; 0317 0318 if( d->multi ) 0319 d->process << "-multi"; 0320 0321 if (d->formatting) { 0322 // On DVD-RW, cdrskin "blank=fast" works like "blank=all". 0323 // Only "blank=deformat_sequential_quickest" will perform fast blanking 0324 // and thus restrict the medium to DAO for the next write run. 0325 switch (d->formattingMode) { 0326 case FormattingComplete: 0327 d->process << "blank=all"; 0328 break; 0329 case FormattingQuick: 0330 if (d->burnedMediaType & Device::MEDIA_DVD_RW_SEQ) 0331 d->process << "blank=deformat_sequential_quickest"; 0332 break; 0333 } 0334 } 0335 0336 if( d->rawCdText.size() > 0 ) { 0337 delete d->cdTextFile; 0338 d->cdTextFile = new QTemporaryFile(); 0339 if ( !d->cdTextFile->open() || 0340 d->cdTextFile->write( d->rawCdText ) != d->rawCdText.size() || 0341 !d->cdTextFile->flush() ) { 0342 emit infoMessage( i18n( "Failed to write temporary file '%1'", d->cdTextFile->fileName() ), MessageError ); 0343 return false; 0344 } 0345 d->process << "textfile=" + d->cdTextFile->fileName(); 0346 } 0347 0348 bool manualBufferSize = k3bcore->globalSettings()->useManualBufferSize(); 0349 if( manualBufferSize ) { 0350 d->process << QString("fs=%1m").arg( k3bcore->globalSettings()->bufferSize() ); 0351 } 0352 0353 bool overburn = k3bcore->globalSettings()->overburn(); 0354 if( overburn ) { 0355 if( d->cdrskinBinObject->hasFeature("overburn") ) { 0356 if ( k3bcore->globalSettings()->force() ) 0357 d->process << "-ignsize"; 0358 else 0359 d->process << "-overburn"; 0360 } 0361 else { 0362 emit infoMessage( i18n("Cdrskin %1 does not support overburning.",d->cdrskinBinObject->version()), MessageWarning ); 0363 } 0364 } 0365 0366 // additional user parameters from config 0367 const QStringList& params = d->cdrskinBinObject->userParameters(); 0368 for( QStringList::const_iterator it = params.constBegin(); it != params.constEnd(); ++it ) 0369 d->process << *it; 0370 0371 // add the user parameters 0372 for( QStringList::const_iterator it = d->arguments.constBegin(); it != d->arguments.constEnd(); ++it ) 0373 d->process << *it; 0374 0375 return true; 0376 } 0377 0378 0379 K3b::CdrskinWriter* K3b::CdrskinWriter::addArgument( const QString& arg ) 0380 { 0381 d->arguments.append( arg ); 0382 return this; 0383 } 0384 0385 0386 void K3b::CdrskinWriter::clearArguments() 0387 { 0388 d->arguments.clear(); 0389 } 0390 0391 0392 void K3b::CdrskinWriter::start() 0393 { 0394 jobStarted(); 0395 0396 d->canceled = false; 0397 d->speedEst->reset(); 0398 d->writingStarted = false; 0399 0400 if ( !prepareProcess() ) { 0401 jobFinished(false); 0402 return; 0403 } 0404 0405 emit debuggingOutput( QLatin1String( "Used versions" ), QString::fromLatin1( "cdrskin: %1" ).arg(d->cdrskinBinObject->version()) ); 0406 0407 if( !d->cdrskinBinObject->copyright().isEmpty() ) 0408 emit infoMessage( i18n("Using %1 %2 – Copyright © %3" 0409 ,(d->cdrskinBinObject->hasFeature( "wodim" ) ? "Wodim" : "Cdrskin" ) 0410 ,d->cdrskinBinObject->version() 0411 ,d->cdrskinBinObject->copyright()), MessageInfo ); 0412 0413 0414 qDebug() << "***** " << d->cdrskinBinObject->name() << " parameters:\n"; 0415 QString s = d->process.joinedArgs(); 0416 qDebug() << s << Qt::flush; 0417 emit debuggingOutput( d->cdrskinBinObject->name() + " command:", s); 0418 0419 d->currentTrack = 0; 0420 d->cdrskinError = UNKNOWN; 0421 d->totalTracksParsed = false; 0422 d->alreadyWritten = 0; 0423 d->tracks.clear(); 0424 d->totalSize = 0; 0425 0426 emit newSubTask( i18n("Preparing write process...") ); 0427 0428 // FIXME: check the return value 0429 if( K3b::isMounted( burnDevice() ) ) { 0430 emit infoMessage( i18n("Unmounting medium"), MessageInfo ); 0431 K3b::unmount( burnDevice() ); 0432 } 0433 0434 // block the device (including certain checks) 0435 k3bcore->blockDevice( burnDevice() ); 0436 0437 // lock the device for good in this process since it will 0438 // be opened in the cdrskin process 0439 burnDevice()->close(); 0440 burnDevice()->usageLock(); 0441 0442 if( !d->process.start( KProcess::MergedChannels ) ) { 0443 // something went wrong when starting the program 0444 // it "should" be the executable 0445 qDebug() << "(K3b::CdrskinWriter) could not start " << d->cdrskinBinObject->name(); 0446 emit infoMessage( i18n("Could not start %1.",d->cdrskinBinObject->name()), K3b::Job::MessageError ); 0447 jobFinished(false); 0448 } 0449 else { 0450 const QString formattedSpeed = formatWritingSpeedFactor( d->usedSpeed, d->burnedMediaType, SpeedFormatInteger ); 0451 const QString formattedMode = writingModeString( d->writingMode ); 0452 // FIXME: these messages should also take DVD into account. 0453 if( simulate() ) { 0454 emit newTask( i18n("Simulating") ); 0455 if ( d->burnedMediaType & Device::MEDIA_DVD_PLUS_ALL ) 0456 // xgettext: no-c-format 0457 emit infoMessage( i18n("Starting simulation at %1x speed...", formattedSpeed ), Job::MessageInfo ); 0458 else 0459 emit infoMessage( i18n("Starting %1 simulation at %2x speed...", formattedMode, formattedSpeed ), Job::MessageInfo ); 0460 } 0461 else { 0462 emit newTask( i18n("Writing") ); 0463 if ( d->burnedMediaType & Device::MEDIA_DVD_PLUS_ALL ) 0464 // xgettext: no-c-format 0465 emit infoMessage( i18n("Starting writing at %1x speed...", formattedSpeed ), Job::MessageInfo ); 0466 else 0467 emit infoMessage( i18n("Starting %1 writing at %2x speed...", formattedMode, formattedSpeed ), Job::MessageInfo ); 0468 } 0469 } 0470 } 0471 0472 0473 void K3b::CdrskinWriter::cancel() 0474 { 0475 if( active() ) { 0476 d->canceled = true; 0477 if( d->process.isRunning() ) 0478 d->process.terminate(); 0479 } 0480 } 0481 0482 0483 void K3b::CdrskinWriter::slotStdLine( const QString& line ) 0484 { 0485 static QRegExp s_burnfreeCounterRx( "^BURN\\-Free\\swas\\s(\\d+)\\stimes\\sused" ); 0486 static QRegExp s_burnfreeCounterRxPredict( "^Total\\sof\\s(\\d+)\\s\\spossible\\sbuffer\\sunderruns\\spredicted" ); 0487 0488 // tracknumber: cap(1) 0489 // done: cap(2) 0490 // complete: cap(3) 0491 // fifo: cap(4) (it seems as if some patched cdrskin versions do not emit the fifo info but only the buf... :( 0492 // buffer: cap(5) 0493 static QRegExp s_progressRx( "Track\\s(\\d\\d)\\:\\s*(\\d*)\\sof\\s*(\\d*)\\sMB\\swritten\\s(?:\\(fifo\\s*(\\d*)\\%\\)\\s*)?(?:\\[buf\\s*(\\d*)\\%\\])?.*" ); 0494 0495 emit debuggingOutput( d->cdrskinBinObject->name(), line ); 0496 0497 // 0498 // Progress and toc parsing 0499 // 0500 0501 if( line.startsWith( "Track " ) ) { 0502 if( !d->totalTracksParsed ) { 0503 // this is not the progress display but the list of tracks that will get written 0504 // we always extract the tracknumber to get the highest at last 0505 bool ok; 0506 int tt = line.mid( 6, 2 ).toInt(&ok); 0507 0508 if( ok ) { 0509 struct Private::Track track; 0510 track.audio = ( line.mid( 10, 5 ) == "audio" ); 0511 0512 d->totalTracks = tt; 0513 0514 static const QRegularExpression rx("\\d"); 0515 int sizeStart = line.indexOf( rx, 10 ); 0516 int sizeEnd = line.indexOf( "MB", sizeStart ); 0517 track.size = line.mid( sizeStart, sizeEnd-sizeStart ).toInt(&ok); 0518 0519 if( ok ) { 0520 d->tracks.append(track); 0521 d->totalSize += track.size; 0522 } 0523 else 0524 qDebug() << "(K3b::CdrskinWriter) track number parse error: " 0525 << line.mid( sizeStart, sizeEnd-sizeStart ); 0526 } 0527 else 0528 qDebug() << "(K3b::CdrskinWriter) track number parse error: " 0529 << line.mid( 6, 2 ); 0530 } 0531 0532 else if( s_progressRx.exactMatch( line ) ) { 0533 // int num = s_progressRx.cap(1).toInt(); 0534 int made = s_progressRx.cap(2).toInt(); 0535 int size = s_progressRx.cap(3).toInt(); 0536 int fifo = s_progressRx.cap(4).toInt(); 0537 0538 emit buffer( fifo ); 0539 d->lastFifoValue = fifo; 0540 0541 if( s_progressRx.captureCount() > 4 ) 0542 emit deviceBuffer( s_progressRx.cap(5).toInt() ); 0543 0544 // 0545 // cdrskin's output sucks a bit. 0546 // we get track sizes that differ from the sizes in the progress 0547 // info since these are dependent on the writing mode. 0548 // so we just use the track sizes and do a bit of math... 0549 // 0550 0551 if( d->tracks.count() > d->currentTrack-1 && size > 0 ) { 0552 double convV = (double)d->tracks[d->currentTrack-1].size/(double)size; 0553 made = (int)((double)made * convV); 0554 size = d->tracks[d->currentTrack-1].size; 0555 } 0556 else { 0557 qCritical() << "(K3b::CdrskinWriter) Did not parse all tracks sizes!" << Qt::endl; 0558 } 0559 0560 if( !d->writingStarted ) { 0561 d->writingStarted = true; 0562 emit newSubTask( i18n("Writing data") ); 0563 } 0564 0565 if( size > 0 ) { 0566 emit processedSubSize( made, size ); 0567 emit subPercent( 100*made/size ); 0568 } 0569 0570 if( d->totalSize > 0 ) { 0571 emit processedSize( d->alreadyWritten+made, d->totalSize ); 0572 emit percent( 100*(d->alreadyWritten+made)/d->totalSize ); 0573 } 0574 0575 d->speedEst->dataWritten( (d->alreadyWritten+made)*1024 ); 0576 } 0577 } 0578 0579 // 0580 // Cdrecord starts all error and warning messages with it's path 0581 // With Debian's script it starts with cdrecord (or /usr/bin/cdrskin or whatever! I hate this script!) 0582 // In general one will have to cross check the messages listed here with 0583 // the source of cdrskin, whether they appear there. 0584 0585 else if( line.startsWith( "cdrskin" ) || 0586 line.startsWith( d->cdrskinBinObject->path() ) || 0587 line.startsWith( d->cdrskinBinObject->path().left(d->cdrskinBinObject->path().length()-5) ) ) { 0588 // get rid of the path and the following colon and space 0589 QString errStr = line.mid( line.indexOf(':') + 2 ); 0590 0591 if( errStr.startsWith( "Drive does not support SAO" ) ) { 0592 emit infoMessage( i18n("DAO (Disk At Once) recording not supported with this writer"), K3b::Job::MessageError ); 0593 emit infoMessage( i18n("Please choose TAO (Track At Once) and try again"), K3b::Job::MessageError ); 0594 } 0595 else if( errStr.startsWith( "Drive does not support RAW" ) ) { 0596 emit infoMessage( i18n("RAW recording not supported with this writer"), K3b::Job::MessageError ); 0597 } 0598 else if( errStr.startsWith("Input/output error.") ) { 0599 emit infoMessage( i18n("Input/output error. Not necessarily serious."), MessageWarning ); 0600 } 0601 else if( errStr.startsWith("shmget failed") ) { 0602 d->cdrskinError = SHMGET_FAILED; 0603 } 0604 else if( errStr.startsWith("OPC failed") ) { 0605 d->cdrskinError = OPC_FAILED; 0606 } 0607 else if( errStr.startsWith( "Drive needs to reload the media" ) ) { 0608 emit infoMessage( i18n("Reloading of medium required"), K3b::Job::MessageInfo ); 0609 } 0610 else if( errStr.startsWith( "The current problem looks like a buffer underrun" ) ) { 0611 if( d->cdrskinError == UNKNOWN ) // it is almost never a buffer underrun these days. 0612 d->cdrskinError = BUFFER_UNDERRUN; 0613 } 0614 else if( errStr.startsWith("MessageWarning: Data may not fit") ) { 0615 bool overburn = k3bcore->globalSettings()->overburn(); 0616 if( overburn && d->cdrskinBinObject->hasFeature("overburn") ) 0617 emit infoMessage( i18n("Trying to write more than the official disk capacity"), K3b::Job::MessageWarning ); 0618 d->cdrskinError = OVERSIZE; 0619 } 0620 else if( errStr.startsWith("Bad Option") ) { 0621 d->cdrskinError = BAD_OPTION; 0622 // parse option 0623 int pos = line.indexOf( "Bad Option" ) + 12; 0624 int len = line.length() - pos - 1; 0625 emit infoMessage( i18n("No valid %1 option: %2",d->cdrskinBinObject->name(),line.mid(pos, len)), 0626 MessageError ); 0627 } 0628 else if( errStr.startsWith("Cannot set speed/dummy") ) { 0629 d->cdrskinError = CANNOT_SET_SPEED; 0630 } 0631 else if( errStr.startsWith("Cannot open new session") ) { 0632 d->cdrskinError = CANNOT_OPEN_NEW_SESSION; 0633 } 0634 else if( errStr.startsWith("Cannot send CUE sheet") ) { 0635 d->cdrskinError = CANNOT_SEND_CUE_SHEET; 0636 } 0637 else if( errStr.startsWith( "Trying to use ultra high speed" ) || 0638 errStr.startsWith( "Trying to use high speed" ) || 0639 errStr.startsWith( "Probably trying to use ultra high speed" ) || 0640 errStr.startsWith( "You did use a high speed medium on an improper writer" ) || 0641 errStr.startsWith( "You did use a ultra high speed medium on an improper writer" ) ) { 0642 d->cdrskinError = HIGH_SPEED_MEDIUM; 0643 } 0644 else if( errStr.startsWith( "You may have used an ultra low speed medium" ) ) { 0645 d->cdrskinError = LOW_SPEED_MEDIUM; 0646 } 0647 else if( errStr.startsWith( "Permission denied. Cannot open" ) || 0648 errStr.startsWith( "Operation not permitted." ) ) { 0649 d->cdrskinError = PERMISSION_DENIED; 0650 } 0651 else if( errStr.startsWith( "Can only copy session # 1") ) { 0652 emit infoMessage( i18n("Only session 1 will be cloned."), MessageWarning ); 0653 } 0654 else if( errStr == "Cannot fixate disk." ) { 0655 emit infoMessage( i18n("Unable to fixate the disk."), MessageError ); 0656 if( d->cdrskinError == UNKNOWN ) 0657 d->cdrskinError = CANNOT_FIXATE_DISK; 0658 } 0659 else if( errStr == "A write error occurred." ) { 0660 d->cdrskinError = WRITE_ERROR; 0661 } 0662 else if( errStr.startsWith( "Try again with cdrskin blank=all." ) ) { 0663 d->cdrskinError = BLANK_FAILED; 0664 } 0665 else if( errStr.startsWith( "faio_wait_on_buffer for reader timed out" ) ) { 0666 d->cdrskinError = SHORT_READ; 0667 } 0668 } 0669 0670 // 0671 // All other messages 0672 // 0673 0674 else if( line.contains( "at speed" ) ) { 0675 // parse the speed and inform the user if cdrdao switched it down 0676 const int pos = line.indexOf( "at speed" ); 0677 const int pos2 = line.indexOf( "in", pos+9 ); 0678 const int speed( double( K3b::speedMultiplicatorForMediaType( d->burnedMediaType ) ) * line.mid( pos+9, pos2-pos-10 ).toDouble() ); // cdrskin-dvd >= 2.01a25 uses 8.0 and stuff 0679 if( speed > 0 && double( qAbs( speed - d->usedSpeed ) ) > 0.5*double( K3b::speedMultiplicatorForMediaType( d->burnedMediaType ) ) ) { 0680 // xgettext: no-c-format 0681 emit infoMessage( i18n("Medium or burner does not support writing at %1x speed", formatWritingSpeedFactor( d->usedSpeed, d->burnedMediaType ) ), 0682 K3b::Job::MessageWarning ); 0683 if( speed > d->usedSpeed ) 0684 // xgettext: no-c-format 0685 emit infoMessage( i18n("Switching burn speed up to %1x", formatWritingSpeedFactor( speed, d->burnedMediaType ) ), K3b::Job::MessageWarning ); 0686 else 0687 // xgettext: no-c-format 0688 emit infoMessage( i18n("Switching burn speed down to %1x", formatWritingSpeedFactor( speed, d->burnedMediaType ) ), K3b::Job::MessageWarning ); 0689 } 0690 } 0691 else if( line.startsWith( "Starting new" ) ) { 0692 d->totalTracksParsed = true; 0693 if( d->currentTrack > 0 ) {// nothing has been written at the start of track 1 0694 if( d->tracks.count() > d->currentTrack-1 ) 0695 d->alreadyWritten += d->tracks[d->currentTrack-1].size; 0696 else 0697 qCritical() << "(K3b::CdrskinWriter) Did not parse all tracks sizes!"; 0698 } 0699 else 0700 emit infoMessage( i18n("Starting disc write"), MessageInfo ); 0701 0702 d->currentTrack++; 0703 0704 if( d->currentTrack > d->tracks.count() ) { 0705 qDebug() << "(K3b::CdrskinWriter) need to add dummy track struct."; 0706 struct Private::Track t; 0707 t.size = 1; 0708 t.audio = false; 0709 d->tracks.append(t); 0710 } 0711 0712 qDebug() << "(K3b::CdrskinWriter) writing track " << d->currentTrack << " of " << d->totalTracks << " tracks."; 0713 emit nextTrack( d->currentTrack, d->totalTracks ); 0714 } 0715 else if( line.startsWith( "Fixating" ) ) { 0716 emit newSubTask( i18n("Closing Session") ); 0717 } 0718 else if( line.startsWith( "Writing lead-in" ) ) { 0719 d->totalTracksParsed = true; 0720 emit newSubTask( i18n("Writing Leadin") ); 0721 } 0722 else if( line.startsWith( "Writing Leadout") ) { 0723 emit newSubTask( i18n("Writing Leadout") ); 0724 } 0725 else if( line.startsWith( "Writing pregap" ) ) { 0726 emit newSubTask( i18n("Writing pregap") ); 0727 } 0728 else if( line.startsWith( "Performing OPC" ) ) { 0729 emit infoMessage( i18n("Performing Optimum Power Calibration"), K3b::Job::MessageInfo ); 0730 } 0731 else if( line.startsWith( "Sending" ) ) { 0732 emit infoMessage( i18n("Sending CUE sheet"), K3b::Job::MessageInfo ); 0733 } 0734 else if( line.startsWith( "Turning BURN-Free on" ) || line.startsWith( "BURN-Free is ON") ) { 0735 emit infoMessage( i18n("Enabled Burnfree"), K3b::Job::MessageInfo ); 0736 } 0737 else if( line.startsWith( "Turning BURN-Free off" ) ) { 0738 emit infoMessage( i18n("Disabled Burnfree"), K3b::Job::MessageWarning ); 0739 } 0740 else if( line.startsWith( "Re-load disk and hit" ) ) { 0741 // this happens on some notebooks where cdrskin is not able to close the 0742 // tray itself, so we need to ask the user to do so 0743 blockingInformation( i18n("Please reload the medium and press 'OK'"), 0744 i18n("Unable to close the tray") ); 0745 0746 // now send a <CR> to cdrskin 0747 // hopefully this will do it since I have no possibility to test it! 0748 d->process.write( "\n", 1 ); 0749 } 0750 else if( s_burnfreeCounterRx.indexIn( line ) ) { 0751 bool ok; 0752 int num = s_burnfreeCounterRx.cap(1).toInt(&ok); 0753 if( ok ) 0754 emit infoMessage( i18np("Burnfree was used once.", "Burnfree was used %1 times.", num), MessageInfo ); 0755 } 0756 else if( s_burnfreeCounterRxPredict.indexIn( line ) ) { 0757 bool ok; 0758 int num = s_burnfreeCounterRxPredict.cap(1).toInt(&ok); 0759 if( ok ) 0760 emit infoMessage( i18np("Buffer was low once.", "Buffer was low %1 times.", num), MessageInfo ); 0761 } 0762 else if( line.contains("Medium Error") ) { 0763 d->cdrskinError = MEDIUM_ERROR; 0764 } 0765 else if( line.startsWith( "Error trying to open" ) && line.contains( "(Device or resource busy)" ) ) { 0766 d->cdrskinError = DEVICE_BUSY; 0767 } 0768 else { 0769 // debugging 0770 qDebug() << "(" << d->cdrskinBinObject->name() << ") " << line; 0771 } 0772 } 0773 0774 0775 void K3b::CdrskinWriter::slotProcessExited( int exitCode, QProcess::ExitStatus exitStatus ) 0776 { 0777 // remove temporary cdtext file 0778 delete d->cdTextFile; 0779 d->cdTextFile = 0; 0780 0781 // release the device within this process 0782 burnDevice()->usageUnlock(); 0783 0784 // unblock the device 0785 k3bcore->unblockDevice( burnDevice() ); 0786 0787 if( d->canceled ) { 0788 // this will unblock and eject the drive and emit the finished/canceled signals 0789 K3b::AbstractWriter::cancel(); 0790 return; 0791 } 0792 0793 0794 if( exitStatus == QProcess::NormalExit ) { 0795 switch( exitCode ) { 0796 case 0: 0797 { 0798 if( d->formatting ) 0799 emit infoMessage( i18n("Erasing successfully completed"), K3b::Job::MessageSuccess ); 0800 else if( simulate() ) 0801 emit infoMessage( i18n("Simulation successfully completed"), K3b::Job::MessageSuccess ); 0802 else 0803 emit infoMessage( i18n("Writing successfully completed"), K3b::Job::MessageSuccess ); 0804 0805 if( !d->formatting ) { 0806 int s = d->speedEst->average(); 0807 emit infoMessage( ki18n("Average overall write speed: %1 KB/s (%2x)" ) 0808 .subs( s ).subs( ( double )s/( double )d->usedSpeedFactor, 0, 'g', 2 ).toString(), 0809 MessageInfo ); 0810 } 0811 0812 jobFinished( true ); 0813 } 0814 break; 0815 0816 default: 0817 qDebug() << "(K3b::CdrskinWriter) error: " << exitCode; 0818 0819 if( d->cdrskinError == UNKNOWN && d->lastFifoValue <= 3 ) 0820 d->cdrskinError = BUFFER_UNDERRUN; 0821 0822 switch( d->cdrskinError ) { 0823 case OVERSIZE: 0824 if( k3bcore->globalSettings()->overburn() && 0825 d->cdrskinBinObject->hasFeature("overburn") ) 0826 emit infoMessage( i18n("Data did not fit on disk."), MessageError ); 0827 else { 0828 emit infoMessage( i18n("Data does not fit on disk."), MessageError ); 0829 if( d->cdrskinBinObject->hasFeature("overburn") ) 0830 emit infoMessage( i18n("Enable overburning in the advanced K3b settings to burn anyway."), MessageInfo ); 0831 } 0832 break; 0833 case BAD_OPTION: 0834 // error message has already been emitted earlier since we needed the actual line 0835 break; 0836 case SHMGET_FAILED: 0837 emit infoMessage( i18n("%1 could not reserve shared memory segment of requested size.",d->cdrskinBinObject->name()), MessageError ); 0838 emit infoMessage( i18n("Probably you chose a too large buffer size."), MessageError ); 0839 break; 0840 case OPC_FAILED: 0841 emit infoMessage( i18n("OPC failed. Probably the writer does not like the medium."), MessageError ); 0842 break; 0843 case CANNOT_SET_SPEED: 0844 emit infoMessage( i18n("Unable to set write speed to %1.",formatWritingSpeedFactor( d->usedSpeed, d->burnedMediaType, SpeedFormatInteger ) ), MessageError ); 0845 emit infoMessage( i18n("Probably this is lower than your writer's lowest writing speed."), MessageError ); 0846 break; 0847 case CANNOT_SEND_CUE_SHEET: 0848 emit infoMessage( i18n("Unable to send CUE sheet."), MessageError ); 0849 if( d->writingMode == K3b::WritingModeSao ) 0850 emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), MessageError ); 0851 break; 0852 case CANNOT_OPEN_NEW_SESSION: 0853 emit infoMessage( i18n("Unable to open new session."), MessageError ); 0854 emit infoMessage( i18n("Probably a problem with the medium."), MessageError ); 0855 break; 0856 case CANNOT_FIXATE_DISK: 0857 emit infoMessage( i18n("The disk might still be readable."), MessageError ); 0858 if( d->writingMode == K3b::WritingModeTao && burnDevice()->dao() ) 0859 emit infoMessage( i18n("Try DAO writing mode."), MessageError ); 0860 break; 0861 case PERMISSION_DENIED: 0862 emit infoMessage( i18n("%1 has no permission to open the device.",QString("cdrskin")), MessageError ); 0863 emit infoMessage( i18n("Modify device settings in K3b to solve this problem."), MessageError ); 0864 break; 0865 case BUFFER_UNDERRUN: 0866 emit infoMessage( i18n("Probably a buffer underrun occurred."), MessageError ); 0867 if( !d->usingBurnfree && burnDevice()->burnproof() ) 0868 emit infoMessage( i18n("Please enable Burnfree or choose a lower burning speed."), MessageError ); 0869 else 0870 emit infoMessage( i18n("Please choose a lower burning speed."), MessageError ); 0871 break; 0872 case HIGH_SPEED_MEDIUM: 0873 emit infoMessage( i18n("Found a high-speed medium not suitable for the writer being used."), MessageError ); 0874 emit infoMessage( i18n("Use the 'force unsafe operations' option to ignore this."), MessageError ); 0875 break; 0876 case LOW_SPEED_MEDIUM: 0877 emit infoMessage( i18n("Found a low-speed medium not suitable for the writer being used."), MessageError ); 0878 emit infoMessage( i18n("Use the 'force unsafe operations' option to ignore this."), MessageError ); 0879 break; 0880 case MEDIUM_ERROR: 0881 emit infoMessage( i18n("Most likely the burning failed due to low-quality media."), MessageError ); 0882 break; 0883 case DEVICE_BUSY: 0884 emit infoMessage( i18n("Another application is blocking the device (most likely automounting)."), MessageError ); 0885 break; 0886 case WRITE_ERROR: 0887 emit infoMessage( i18n("A write error occurred."), MessageError ); 0888 if( d->writingMode == K3b::WritingModeSao ) 0889 emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), MessageError ); 0890 break; 0891 case BLANK_FAILED: 0892 emit infoMessage( i18n("Some drives do not support all erase types."), MessageError ); 0893 emit infoMessage( i18n("Try again using 'Complete' erasing."), MessageError ); 0894 break; 0895 case SHORT_READ: 0896 emit infoMessage( QLatin1String("Internal error: short read. Please report!"), MessageError ); 0897 break; 0898 case UNKNOWN: 0899 if( (exitCode == 12) && K3b::kernelVersion() >= K3b::Version( 2, 6, 8 ) && d->cdrskinBinObject->hasFeature( "suidroot" ) ) { 0900 emit infoMessage( i18n("Since kernel version 2.6.8 cdrskin cannot use SCSI transport when running suid root anymore."), MessageError ); 0901 emit infoMessage( i18n("You may use K3b::Setup to solve this problem or remove the suid bit manually."), MessageError ); 0902 } 0903 else if( !wasSourceUnreadable() ) { 0904 emit infoMessage( i18n("%1 returned an unknown error (code %2).", 0905 d->cdrskinBinObject->name(), exitCode), 0906 K3b::Job::MessageError ); 0907 0908 if( (exitCode >= 254) && d->writingMode == K3b::WritingModeSao ) { 0909 emit infoMessage( i18n("Sometimes using TAO writing mode solves this issue."), MessageError ); 0910 } 0911 else { 0912 emit infoMessage( i18n("If you are running an unpatched cdrskin version..."), MessageError ); 0913 emit infoMessage( i18n("...and this error also occurs with high quality media..."), MessageError ); 0914 emit infoMessage( i18n("...and the K3b FAQ does not help you..."), MessageError ); 0915 emit infoMessage( i18n("...please include the debugging output in your problem report."), MessageError ); 0916 } 0917 } 0918 break; 0919 } 0920 jobFinished( false ); 0921 } 0922 } 0923 else { 0924 emit infoMessage( i18n("%1 crashed.", d->cdrskinBinObject->name()), 0925 MessageError ); 0926 jobFinished( false ); 0927 } 0928 } 0929 0930 0931 void K3b::CdrskinWriter::slotThroughput( int t ) 0932 { 0933 emit writeSpeed( t, d->tracks.count() > d->currentTrack && !d->tracks[d->currentTrack-1].audio 0934 ? K3b::Device::SPEED_FACTOR_CD_MODE1 0935 : d->usedSpeedFactor ); 0936 } 0937 0938 0939 qint64 K3b::CdrskinWriter::write( const char* data, qint64 maxSize ) 0940 { 0941 return d->process.write( data, maxSize ); 0942 } 0943 0944 #include "moc_k3bcdrskinwriter.cpp"