From 089f01e07cea648bc56a6b825d70a54abfe15fd3 Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Tue, 5 May 2009 17:44:43 +1000 Subject: ffmpeg support still image based for now, streaming next --- tools/qmlviewer/main.cpp | 18 +++++++--- tools/qmlviewer/qmlviewer.cpp | 81 ++++++++++++++++++++++++++++++++++--------- tools/qmlviewer/qmlviewer.h | 2 ++ 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/tools/qmlviewer/main.cpp b/tools/qmlviewer/main.cpp index c5676ab..26ff213 100644 --- a/tools/qmlviewer/main.cpp +++ b/tools/qmlviewer/main.cpp @@ -27,11 +27,15 @@ void usage() qWarning(" -v, -version ............................. display version"); qWarning(" -frameless ............................... run with no window frame"); qWarning(" -skin ...................... run with a skin window frame"); - qWarning(" -recorddither ordered|threshold|floyd .... set dither mode used for recording"); + qWarning(" -recordfile ..................... set output file"); + qWarning(" - ImageMagick 'convert' for GIF)"); + qWarning(" - png file for raw frames"); + qWarning(" - 'ffmpeg' for other formats"); + qWarning(" -recorddither ordered|threshold|floyd .... set GIF dither recording mode"); qWarning(" -recordperiod ............. set time between recording frames"); - qWarning(" -autorecord [from-] ...... set recording to start and stop automatically"); + qWarning(" -autorecord [from-] ...... set recording to start and stop"); qWarning(" -devicekeys .............................. use numeric keys (see F1)"); - qWarning(" -cache ................................... enable a disk cache of remote content"); + qWarning(" -cache ................................... disk cache remote content"); qWarning(" -recordtest .................. record an autotest"); qWarning(" -runtest ..................... run a previously recorded test"); qWarning(" "); @@ -66,7 +70,8 @@ int main(int argc, char ** argv) int period = 0; int autorecord_from = 0; int autorecord_to = 0; - QString dither = "threshold"; + QString dither = "none"; + QString recordfile = "animation.gif"; QString skin; bool devkeys = false; bool cache = false; @@ -83,6 +88,10 @@ int main(int argc, char ** argv) cache = true; } else if (arg == "-recordperiod") { period = QString(argv[++i]).toInt(); + } else if (arg == "-recordfile") { + recordfile = QString(argv[++i]); + } else if (arg == "-recorddither") { + dither = QString(argv[++i]); } else if (arg == "-autorecord") { QString range = QString(argv[++i]); int dash = range.indexOf('-'); @@ -119,6 +128,7 @@ int main(int argc, char ** argv) QmlViewer viewer(testMode, testDir, 0, frameless ? Qt::FramelessWindowHint : Qt::Widget); viewer.setCacheEnabled(cache); viewer.openQml(fileName); + viewer.setRecordFile(recordfile); if (period>0) viewer.setRecordPeriod(period); if (autorecord_to) diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp index 3c52cfe..950baf8 100644 --- a/tools/qmlviewer/qmlviewer.cpp +++ b/tools/qmlviewer/qmlviewer.cpp @@ -210,6 +210,11 @@ void QmlViewer::setAutoRecord(int from, int to) } } +void QmlViewer::setRecordFile(const QString& f) +{ + record_file = f; +} + void QmlViewer::setRecordPeriod(int ms) { record_period = ms; @@ -238,7 +243,7 @@ void QmlViewer::keyPressEvent(QKeyEvent *event) exit(0); else if (event->key() == Qt::Key_F1 || (event->key() == Qt::Key_1 && devicemode)) { qDebug() << "F1 - help\n" - << "F2 - toggle GIF recording\n" + << "F2 - toggle video recording\n" << "F3 - take PNG snapshot\n" << "F4 - show items and state\n" << "F5 - reload QML\n" @@ -286,9 +291,21 @@ void QmlViewer::setRecording(bool on) QStringList inputs; qDebug() << "Saving frames..."; + QString framename; + bool png_output = false; + if (record_file.right(4).toLower()==".png") { + if (record_file.contains('%')) + framename = record_file; + else + framename = record_file.left(record_file.length()-4)+"%04d"+record_file.right(4); + png_output = true; + } else { + framename = "tmp-frame%04d.png"; + png_output = false; + } foreach (QImage* img, frames) { QString name; - name.sprintf("tmp-frame%04d.png",frame++); + name.sprintf(framename.toLocal8Bit(),frame++); if (record_dither=="ordered") img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name); else if (record_dither=="threshold") @@ -296,27 +313,57 @@ void QmlViewer::setRecording(bool on) else if (record_dither=="floyd") img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name); else - img->convertToFormat(QImage::Format_Indexed8).save(name); + img->save(name); inputs << name; delete img; } - QString output="animation.gif"; - QStringList args; - - args << "-delay" << QString::number(record_period/10); - args << inputs; - args << output; - qDebug() << "Converting..." << output; - if (0!=QProcess::execute("convert", args)) { - qWarning() << "Cannot run ImageMagick 'convert' - not converted to gif"; + if (png_output) { + framename.replace(QRegExp("%\\d*."),"*"); + qDebug() << "Wrote frames" << framename; inputs.clear(); // don't remove them - qDebug() << "Wrote frames tmp-frame*.png"; } else { - qDebug() << "Compressing..." << output; - if (0!=QProcess::execute("gifsicle", QStringList() << "-O2" << "-o" << output << output)) - qWarning() << "Cannot run 'gifsicle' - not compressed"; - qDebug() << "Wrote" << output; + if (record_file.right(4).toLower()==".gif") { + // ImageMagick and gifsicle for GIF encoding + QStringList args; + args << "-delay" << QString::number(record_period/10); + args << inputs; + args << record_file; + qDebug() << "Converting..." << record_file; + if (0!=QProcess::execute("convert", args)) { + qWarning() << "Cannot run ImageMagick 'convert' - recorded frames not converted"; + inputs.clear(); // don't remove them + qDebug() << "Wrote frames tmp-frame*.png"; + } else { + if (record_file.right(4).toLower() == ".gif") { + qDebug() << "Compressing..." << record_file; + if (0!=QProcess::execute("gifsicle", QStringList() << "-O2" << "-o" << record_file << record_file)) + qWarning() << "Cannot run 'gifsicle' - not compressed"; + } + qDebug() << "Wrote" << record_file; + } + } else { + // ffmpeg for other formats (eg. MPEG) + + // Ensure no old file after end + QString name; + name.sprintf(framename.toLocal8Bit(),frame++); + QFile::remove(name); + + QStringList args; + args << "-sameq"; // ie. high + args << "-y"; + args << "-r" << QString::number(1000/record_period); + args << "-i" << framename; + args << "-s" << QString("%1x%2").arg(canvas->width()).arg(canvas->height()); + args << record_file; + qDebug() << "Converting..." << record_file; + if (0!=QProcess::execute("ffmpeg", args)) { + qWarning() << "Cannot run ffmpeg - recorded frames not converted"; + inputs.clear(); // don't remove them + qDebug() << "Wrote frames tmp-frame*.png"; + } + } } foreach (QString name, inputs) diff --git a/tools/qmlviewer/qmlviewer.h b/tools/qmlviewer/qmlviewer.h index 0fa879d..ee0c3d7 100644 --- a/tools/qmlviewer/qmlviewer.h +++ b/tools/qmlviewer/qmlviewer.h @@ -33,6 +33,7 @@ public: void setRecordDither(const QString& s) { record_dither = s; } void setRecordPeriod(int ms); + void setRecordFile(const QString&); int recordPeriod() const { return record_period; } void setRecording(bool on); bool isRecording() const { return recordTimer.isActive(); } @@ -62,6 +63,7 @@ private: QBasicTimer autoStartTimer; QTime autoTimer; QString record_dither; + QString record_file; int record_period; int record_autotime; bool devicemode; -- cgit v0.12 From a454798fff3e0093a180a9aa890d94375280e09c Mon Sep 17 00:00:00 2001 From: Warwick Allison Date: Tue, 5 May 2009 18:00:30 +1000 Subject: Stream video recording. --- tools/qmlviewer/qmlviewer.cpp | 131 ++++++++++++++++++++++-------------------- tools/qmlviewer/qmlviewer.h | 1 + 2 files changed, 71 insertions(+), 61 deletions(-) diff --git a/tools/qmlviewer/qmlviewer.cpp b/tools/qmlviewer/qmlviewer.cpp index 950baf8..93370d9 100644 --- a/tools/qmlviewer/qmlviewer.cpp +++ b/tools/qmlviewer/qmlviewer.cpp @@ -35,7 +35,7 @@ #include QmlViewer::QmlViewer(QFxTestEngine::TestMode testMode, const QString &testDir, QWidget *parent, Qt::WindowFlags flags) - : QWidget(parent, flags) + : QWidget(parent, flags), frame_stream(0) { testEngine = 0; devicemode = false; @@ -285,45 +285,70 @@ void QmlViewer::setRecording(bool on) if (on) { recordTimer.start(record_period,this); + QString fmt = record_file.right(4).toLower(); + if (fmt != ".png" && fmt != ".gif") { + // Stream video to ffmpeg + + QProcess *proc = new QProcess(this); + frame_stream = proc; + + QStringList args; + args << "-sameq"; // ie. high + args << "-y"; + args << "-r" << QString::number(1000/record_period); + args << "-f" << "rawvideo"; + args << "-pix_fmt" << "rgb32"; + args << "-s" << QString("%1x%2").arg(canvas->width()).arg(canvas->height()); + args << "-i" << "-"; + args << record_file; + proc->start("ffmpeg",args,QIODevice::WriteOnly); + } else { + // Store frames, save to GIF/PNG + frame_stream = 0; + } } else { recordTimer.stop(); - int frame=0; - QStringList inputs; - qDebug() << "Saving frames..."; - - QString framename; - bool png_output = false; - if (record_file.right(4).toLower()==".png") { - if (record_file.contains('%')) - framename = record_file; - else - framename = record_file.left(record_file.length()-4)+"%04d"+record_file.right(4); - png_output = true; + if (frame_stream) { + qDebug() << "Saving video..."; + frame_stream->close(); + qDebug() << "Wrote" << record_file; } else { - framename = "tmp-frame%04d.png"; - png_output = false; - } - foreach (QImage* img, frames) { - QString name; - name.sprintf(framename.toLocal8Bit(),frame++); - if (record_dither=="ordered") - img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name); - else if (record_dither=="threshold") - img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::ThresholdDither).save(name); - else if (record_dither=="floyd") - img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name); - else - img->save(name); - inputs << name; - delete img; - } + int frame=0; + QStringList inputs; + qDebug() << "Saving frames..."; + + QString framename; + bool png_output = false; + if (record_file.right(4).toLower()==".png") { + if (record_file.contains('%')) + framename = record_file; + else + framename = record_file.left(record_file.length()-4)+"%04d"+record_file.right(4); + png_output = true; + } else { + framename = "tmp-frame%04d.png"; + png_output = false; + } + foreach (QImage* img, frames) { + QString name; + name.sprintf(framename.toLocal8Bit(),frame++); + if (record_dither=="ordered") + img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::OrderedDither).save(name); + else if (record_dither=="threshold") + img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither|Qt::ThresholdDither).save(name); + else if (record_dither=="floyd") + img->convertToFormat(QImage::Format_Indexed8,Qt::PreferDither).save(name); + else + img->save(name); + inputs << name; + delete img; + } - if (png_output) { - framename.replace(QRegExp("%\\d*."),"*"); - qDebug() << "Wrote frames" << framename; - inputs.clear(); // don't remove them - } else { - if (record_file.right(4).toLower()==".gif") { + if (png_output) { + framename.replace(QRegExp("%\\d*."),"*"); + qDebug() << "Wrote frames" << framename; + inputs.clear(); // don't remove them + } else { // ImageMagick and gifsicle for GIF encoding QStringList args; args << "-delay" << QString::number(record_period/10); @@ -342,34 +367,13 @@ void QmlViewer::setRecording(bool on) } qDebug() << "Wrote" << record_file; } - } else { - // ffmpeg for other formats (eg. MPEG) + } - // Ensure no old file after end - QString name; - name.sprintf(framename.toLocal8Bit(),frame++); + foreach (QString name, inputs) QFile::remove(name); - QStringList args; - args << "-sameq"; // ie. high - args << "-y"; - args << "-r" << QString::number(1000/record_period); - args << "-i" << framename; - args << "-s" << QString("%1x%2").arg(canvas->width()).arg(canvas->height()); - args << record_file; - qDebug() << "Converting..." << record_file; - if (0!=QProcess::execute("ffmpeg", args)) { - qWarning() << "Cannot run ffmpeg - recorded frames not converted"; - inputs.clear(); // don't remove them - qDebug() << "Wrote frames tmp-frame*.png"; - } - } + frames.clear(); } - - foreach (QString name, inputs) - QFile::remove(name); - - frames.clear(); } qDebug() << "Recording: " << (recordTimer.isActive()?"ON":"OFF"); } @@ -377,7 +381,12 @@ void QmlViewer::setRecording(bool on) void QmlViewer::timerEvent(QTimerEvent *event) { if (event->timerId() == recordTimer.timerId()) { - frames.append(new QImage(canvas->asImage())); + if (frame_stream) { + QImage frame(canvas->asImage()); + frame_stream->write((char*)frame.bits(),frame.numBytes()); + } else { + frames.append(new QImage(canvas->asImage())); + } if (record_autotime && autoTimer.elapsed() >= record_autotime) setRecording(false); } else if (event->timerId() == autoStartTimer.timerId()) { diff --git a/tools/qmlviewer/qmlviewer.h b/tools/qmlviewer/qmlviewer.h index ee0c3d7..fc65ebf 100644 --- a/tools/qmlviewer/qmlviewer.h +++ b/tools/qmlviewer/qmlviewer.h @@ -60,6 +60,7 @@ private: void init(QFxTestEngine::TestMode, const QString &, const QString& fileName); QBasicTimer recordTimer; QList frames; + QIODevice* frame_stream; QBasicTimer autoStartTimer; QTime autoTimer; QString record_dither; -- cgit v0.12