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