void FfmpegExporter::bytesWritten(qint64 bytes) { const qint64 bufsize = _writebuffer.size(); _written += bytes; _chunk -= bytes; Q_ASSERT(_written <= bufsize); Q_ASSERT(_chunk >= 0); //qDebug() << "wrote" << bytes << "bytes:" << _written << "of" << bufsize << QString("(%1%)").arg(_written/qreal(bufsize)*100, 0, 'f', 1); if(_written == bufsize) { --_repeats; if(_repeats<=0) { _writebuffer.clear(); emit exporterReady(); } else { _written = 0; _chunk = 0; bytesWritten(0); } } else if(_written < bufsize && _chunk==0) { _chunk = qMin(bufsize - _written, qint64(1024 * 1024)); _encoder->write(_writebuffer.constData() + _written, _chunk); } }
void ImageSeriesExporter::writeFrame(const QImage &image, int repeat) { for(int f=1;f<=repeat;++f) { QString filename = _filepattern; filename.replace(QLatin1Literal("{F}"), QString("%1").arg(frame() + f, 5, 10, QLatin1Char('0'))); filename.replace(QLatin1Literal("{E}"), _format); QString fullpath = QFileInfo(QDir(_path), filename).absoluteFilePath(); QImageWriter writer(fullpath, _format); if(!writer.write(image)) { emit exporterError(writer.errorString()); return; } } emit exporterReady(); }
void ImageSeriesExporter::initExporter() { emit exporterReady(); }
void FfmpegExporter::initExporter() { Q_ASSERT(!_encoder); QStringList args; // Image input args << "-f" << "image2pipe" << "-c:v" << "bmp" << "-i" << "-"; // Soundtrack input if(!_soundtrack.isEmpty()) { args << "-i" << _soundtrack; } // Framerate args << "-r" << QString::number(fps()); // Output format is deduced automatically from the file name, // but we need to set the codec options // Possible quality values are: [0] low, [1] normal, [2] good, [3] very good args << "-c:v"; if(_videoCodec == "H.264") { args << "libx264"; switch(_quality) { case 0: args << "-crf" << "32" << "-preset" << "medium"; break; case 2: args << "-crf" << "18" << "-preset" << "slow"; break; case 3: args << "-crf" << "16" << "-preset" << "veryslow"; break; case 1: default: args << "-crf" << "23" << "-preset" << "slow"; break; } } else if(_videoCodec == "VP8") { args << "libvpx"; switch(_quality) { case 0: args << "-crf" << "32" << "-b:v" << "0.5M"; break; case 2: args << "-crf" << "10" << "-b:v" << "1M"; break; case 3: args << "-crf" << "5" << "-b:v" << "2M"; break; case 1: default: args << "-crf" << "15" << "-b:v" << "1M"; break; } } else if(_videoCodec == "Theora") { args << "libtheora"; switch(_quality) { case 0: args << "-qscale:v" << "3"; break; case 2: args << "-qscale:v" << "7"; break; case 3: args << "-qscale:v" << "10"; break; case 1: default: args << "-qscale:v" << "6"; break; } } else { qWarning() << "unhandled video codec:" << _videoCodec; // no quality adjustment for unknown codecs! args << _videoCodec.toLower(); } if(!_soundtrack.isEmpty()) { args << "-c:a"; if(_audioCodec.isEmpty()) args << "copy"; else if(_audioCodec == "MP3") args << "libmp3lame"; else if(_audioCodec == "Vorbis") args << "libvorbis"; else args << _audioCodec.toLower(); // TODO: this should cut the audio down to the length of the video // but using it will trigger a QProcess write error. //args << "-shortest"; } // Output file (overwrite) args << "-y" << _filename; _encoder = new QProcess(this); _encoder->setProcessChannelMode(QProcess::ForwardedChannels); connect(_encoder, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); connect(_encoder, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64))); connect(_encoder, SIGNAL(started()), this, SIGNAL(exporterReady())); connect(_encoder, SIGNAL(finished(int)), this, SIGNAL(exporterFinished())); qDebug() << "Encoding:" << getFfmpegPath() << args; _encoder->start(getFfmpegPath(), args); }