#include "evideo.h" #include "cfg.h" #include "tools.h" #include #include #include #include #include #include EVideo *EVideo::create(const QString &file, PageListItem *pageItem, EBase *multiWin) { int64_t dur; AVCodecID codecId; QImage img; QString err = Tools::videoInfo(file.toUtf8(), img, &dur, &codecId); if(! err.isEmpty()) { QMessageBox::critical(gMainWin, "Video Error", err+"\n"+file); return 0; } QFileInfo rawInfo(file); QString rawName = rawInfo.fileName(); QString outFile = transcoding(file, rawName, pageItem->mPageDir, img.width(), img.height(), codecId); if(outFile.isEmpty()) return 0; QFileInfo outInfo(outFile); if(! outInfo.isFile() || outInfo.size()==0) return 0; return new EVideo(outInfo.absolutePath(), outInfo.fileName(), rawInfo.absolutePath(), rawName, img, dur/1000000, pageItem, multiWin); } EVideo *EVideo::create(const QJsonObject &json, PageListItem *pageItem, EBase *multiWin) { auto widget = json["widget"]; auto dir = widget["path"].toString(); auto name = widget["file"].toString(); if(! QFileInfo::exists(dir)) dir = pageItem->mPageDir; QString file = dir + "/" + name; if(QFileInfo::exists(file)) ; else if(QFileInfo::exists(file = pageItem->mPageDir + "/" + name)) dir = pageItem->mPageDir; else return nullptr; int64_t dur; QImage img; QString err = Tools::videoInfo(file.toUtf8(), img, &dur, 0); if(! err.isEmpty()) return nullptr; auto ins = new EVideo(dir, name, widget["pathRaw"].toString(), widget["fileRaw"].toString(), img, dur/1000000, pageItem, multiWin); ins->setBaseAttr(json); auto play = json["play"]; ins->playDuration = play["playDuration"].toInt(); ins->playTimes = play["playTimes"].toInt(); if(ins->playDuration<10) ins->playDuration = dur/1000000; return ins; } QJsonObject EVideo::genProg(const QJsonObject &ele, const QString &dstDir) { auto widget = ele["widget"]; auto path = widget["path"].toString(); auto name = widget["file"].toString(); QString srcFile = path + "/" + name; QFileInfo srcInfo(srcFile); if(! srcInfo.isFile()) return QJsonObject(); QString id = Tools::fileMd5(srcFile); if(id.isEmpty()) return QJsonObject(); QFile::copy(srcFile, dstDir+"/"+id); QJsonObject oRes; oRes["_type"] = "Video"; oRes["id"] = id; oRes["md5"] = id; oRes["name"] = name; oRes["size"] = srcInfo.size(); QString suffix = srcInfo.suffix().toLower(); oRes["fileExt"] = suffix; oRes["mime"] = "video/"+suffix; auto play = ele["play"]; oRes["timeSpan"] = play["playDuration"].toInt() * play["playTimes"].toInt(); oRes["enabled"] = true; oRes["entryEffect"] = "None"; oRes["exitEffect"] = "None"; oRes["entryEffectTimeSpan"] = 0; oRes["exitEffectTimeSpan"] = 0; return oRes; } EVideo::EVideo(const QString &dir, const QString &name, const QString &rawDir, const QString &rawName, QImage &coverImg, int dur, PageListItem *pageItem, EBase *multiWin) : EBase(multiWin), mDir(dir), mName(name), mRawDir(rawDir), mRawName(rawName), mCoverImg(coverImg), playDuration(dur), mPageItem(pageItem) { mType = EBase::Video; } void EVideo::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { auto inner = innerRect(); painter->save(); painter->setRenderHints(QPainter::SmoothPixmapTransform); painter->drawImage(inner, mCoverImg); double maskW = mask().width()/2, maskH = mask().height()/2; if(maskW>inner.width() || maskH>inner.height()) { double rate = qMin(inner.width() / maskW, inner.height() / maskH); maskW *= rate; maskH *= rate; } painter->drawImage(QRectF(inner.left()+(inner.width() - maskW)/2, inner.top()+(inner.height() - maskH)/2, maskW, maskH), mask()); painter->restore(); EBase::paint(painter, option, widget); } QWidget* EVideo::attrWgt() { auto wgtAttr = new QWidget(); auto vBox = new QVBoxLayout(wgtAttr); vBox->setContentsMargins(6, 0, 6, 0); if(mMultiWin!=nullptr) vBox->setSpacing(3); addBaseAttrWgt(vBox); auto hBox = new QHBoxLayout(); hBox->addWidget(new QLabel(tr("Basic Properties"))); auto line = new QFrame(); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); hBox->addWidget(line, 1); vBox->addLayout(hBox); hBox = new QHBoxLayout(); hBox->addSpacing(6); hBox->addWidget(new QLabel(tr("File"))); auto fdFileName = new QLineEdit(mRawName); fdFileName->setReadOnly(true); hBox->addWidget(fdFileName); auto bnSelectFile = new QPushButton("..."); bnSelectFile->setFixedWidth(30); bnSelectFile->setObjectName("bnSelectFile"); hBox->addWidget(bnSelectFile); vBox->addLayout(hBox); // hBox = new QHBoxLayout(); // hBox->addSpacing(6); // hBox->addWidget(new QLabel(tr("AspectRatioMode"))); // auto wAspectRatioMode = new QComboBox(); // wAspectRatioMode->addItem(tr("IgnoreAspectRatio")); // wAspectRatioMode->addItem(tr("KeepAspectRatio")); // wAspectRatioMode->addItem(tr("KeepAspectRatioByExpanding")); // wAspectRatioMode->setCurrentIndex(aspectRatioMode); // connect(wAspectRatioMode, (void(QComboBox::*)(int))&QComboBox::currentIndexChanged, this, [this](int value) { // aspectRatioMode = value; // }); // hBox->addWidget(wAspectRatioMode); // hBox->addStretch(); // vBox->addLayout(hBox); hBox = new QHBoxLayout(); hBox->addWidget(new QLabel(tr("Play Properties"))); line = new QFrame(); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); hBox->addWidget(line, 1); vBox->addLayout(hBox); hBox = new QHBoxLayout(); hBox->addSpacing(6); hBox->addWidget(new QLabel(tr("Play Duration"))); auto fdDuration = new QSpinBox(); fdDuration->setRange(1, 99999); fdDuration->setValue(playDuration); connect(fdDuration, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this](int value) { playDuration = value; }); connect(bnSelectFile, &QPushButton::clicked, this, [this, fdFileName, fdDuration] { auto rawFile = QFileDialog::getOpenFileName(gMainWin, tr("Select File"), gFileHome, EVideo::filters()); if(rawFile.isEmpty()) return; QFileInfo rawInfo(rawFile); int64_t dur; AVCodecID codecId; QString err = Tools::videoInfo(rawFile.toUtf8(), mCoverImg, &dur, &codecId); if(! err.isEmpty()) { QMessageBox::critical(gMainWin, "Video Error", err+"\n"+rawFile); return; }; mRawDir = rawInfo.absolutePath(); mRawName = rawInfo.fileName(); gFileHome = mRawDir; fdFileName->setText(mRawName); playDuration = dur/1000000; fdDuration->setValue(playDuration); QString outFile = transcoding(rawFile, mRawName, mPageItem->mPageDir, mCoverImg.width(), mCoverImg.height(), codecId); if(outFile.isEmpty()) return; QFile oldfile(mDir+"/"+mName); if(oldfile.exists()) oldfile.remove(); QFileInfo outInfo(outFile); mDir = outInfo.absolutePath(); mName = outInfo.fileName(); }); hBox->addWidget(fdDuration); hBox->addWidget(new QLabel(tr("s"))); hBox->addStretch(); vBox->addLayout(hBox); hBox = new QHBoxLayout(); hBox->addSpacing(6); hBox->addWidget(new QLabel(tr("Play Times"))); auto wPlayTimes = new QSpinBox(); wPlayTimes->setRange(1, 99999); wPlayTimes->setValue(playTimes); connect(wPlayTimes, (void(QSpinBox::*)(int))&QSpinBox::valueChanged, this, [this](int value) { playTimes = value; }); hBox->addWidget(wPlayTimes); hBox->addStretch(); vBox->addLayout(hBox); vBox->addStretch(); return wgtAttr; } bool EVideo::save(const QString &pageDir) { QString saveFile = pageDir + "/" + mName; if(QFile::exists(saveFile)) return true; QString oldFile = mDir + PAGEDEL_SUFFIX "/" + mName; if(QFile::exists(oldFile)) ; else if(QFile::exists(oldFile = mDir + "/" + mName)) ; else return false; QFile(oldFile).copy(saveFile); mDir = pageDir; return true; } QJsonObject EVideo::attrJson() const { QJsonObject oRoot; addBaseAttr(oRoot); oRoot["elementType"] = "Movie"; oRoot["widget"] = QJsonObject{ {"path", mDir}, {"file", mName}, {"pathRaw", mRawDir}, {"fileRaw", mRawName} }; oRoot["play"] = QJsonObject{ {"playDuration", playDuration}, {"playTimes", playTimes} }; return oRoot; } QString EVideo::transcoding(QString rawFile, QString rawName, QString dir, int w, int h, AVCodecID codec_id) { QSettings settings; int rawMin = qMin(w, h); if(settings.value("VideoCompress", true).toBool() && rawMin > 768 && w > gProgWidth && h > gProgHeight) { double rate = 720.0 / rawMin; w *= rate; h *= rate; } else if(! settings.value("VideoTranscoding", true).toBool() || codec_id == AV_CODEC_ID_H264) { QString outFile = dir+"/"+rawName; QFile::copy(rawFile, outFile); return outFile; } QString outFile = dir+"/"+Tools::fileMd5(rawFile)+".mp4"; if(QFileInfo::exists(outFile)) return outFile; QProcess process; process.setWorkingDirectory(QApplication::applicationDirPath()); QMessageBox msgBox(gMainWin); msgBox.setWindowFlag(Qt::WindowCloseButtonHint, false); msgBox.setStandardButtons(QMessageBox::NoButton); msgBox.setWindowTitle(tr("Video Transcoding")); msgBox.setText(tr("Video Transcoding Progress")+": 0% "); connect(&process, (void(QProcess::*)(int, QProcess::ExitStatus))&QProcess::finished, &msgBox, [&msgBox] { msgBox.accept(); }); connect(&process, &QProcess::errorOccurred, &msgBox, [&msgBox](QProcess::ProcessError error) { msgBox.accept(); QMessageBox::critical(gMainWin, tr("Error"), QString(QMetaEnum::fromType().valueToKey(error))+" ("+QString::number(error)+")"); }); connect(&process, &QProcess::readyReadStandardOutput, &msgBox, [&process] { auto data = process.readAllStandardOutput(); qInfo()<<"EVideo::transcoding StandardOutput"; qInfo()< -1) dur = QTime::fromString(txt.mid(idx+10, 11)+"0", "HH:mm:ss.zzz").msecsSinceStartOfDay(); } else { int idx = txt.indexOf("time="); if(idx > -1) { auto cur = QTime::fromString(txt.mid(idx+5, 11)+"0", "HH:mm:ss.zzz").msecsSinceStartOfDay(); if(dur > 0) msgBox.setText(tr("Video Transcoding Progress")+": "+QString::number(cur*100/dur)+"% "); } else err.append(txt); } }); process.start("ffmpeg", {"-y", "-i", rawFile, "-vcodec", "h264", "-s", QString::number(w)+"x"+QString::number(h), "-profile:v", "main", "-b:v", QString::number(w*h/150)+"k", outFile}); msgBox.exec(); if(err.right(32).contains("Conversion failed!")) { QMessageBox::critical(gMainWin, tr("Error"), err); return ""; } return outFile; }