qt/LedOK/program/evideo.cpp
2024-06-20 10:44:08 +08:00

303 lines
12 KiB
C++

#include "evideo.h"
#include "cfg.h"
#include "tools.h"
#include "globaldefine.h"
#include "base/ffutil.h"
#include <QLineEdit>
#include <QMessageBox>
#include <QMetaEnum>
#include <QProcess>
#include <QSettings>
#include <QSpinBox>
#include <QPainter>
EVideo *EVideo::create(const QString &file, PageListItem *pageItem, EBase *multiWin) {
int64_t dur;
AVCodecID codecId;
QImage img;
auto err = videoInfo(file.toUtf8(), img, &dur, &codecId);
if(! err.isEmpty()) {
QMessageBox::critical(pageItem->listWidget(), "Video Error", err+"\n"+file);
return 0;
}
QFileInfo rawInfo(file);
QString rawName = rawInfo.fileName();
QString outFile = transcoding(pageItem->listWidget(), 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;
auto ins = new EVideo(outInfo.absolutePath(), outInfo.fileName(), rawInfo.absolutePath(), rawName, img, pageItem, multiWin);
ins->mDuration = round(dur*0.000001);
return ins;
}
EVideo *EVideo::create(const JObj &ele, PageListItem *pageItem, EBase *multiWin) {
auto widget = ele["widget"];
if(widget.isNull()) widget = ele;
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 0;
int64_t dur;
QImage img;
auto err = videoInfo(file.toUtf8(), img, &dur, 0);
if(! err.isEmpty()) return 0;
dur = round(dur*0.000001);
auto ins = new EVideo(dir, name, widget["pathRaw"].toString(), widget["fileRaw"].toString(), img, pageItem, multiWin);
ins->setBaseAttr(ele);
if(ins->mDuration < 4) ins->mDuration = dur;
auto play = ele["play"];
ins->playTimes = (play.isNull() ? ele : play)["playTimes"].toInt(1);
return ins;
}
JObj EVideo::genProg(const JValue &ele, const QString &dstDir, ProgItem *progItem) {
auto widget = ele["widget"];
if(widget.isNull()) widget = ele;
auto path = widget["path"].toString();
auto name = widget["file"].toString();
//if(progItem->maxLen) name += "-square.mp4";
QString srcFile = path + "/" + name;
QFileInfo srcInfo(srcFile);
if(! srcInfo.isFile()) return JObj();
QString id = Tools::fileMd5(srcFile);
if(id.isEmpty()) return JObj();
QFile::copy(srcFile, dstDir+"/"+id);
JObj oRes;
oRes["_type"] = "Video";
oRes["id"] = id;
oRes["md5"] = id;
oRes["name"] = name;
auto play = ele["play"];
oRes["timeSpan"] = play.isNull() ? ele["duration"].toInt() * ele["playTimes"].toInt() : play["playDuration"].toInt() * play["playTimes"].toInt();
return oRes;
}
EVideo::EVideo(const QString &dir, const QString &name, const QString &rawDir, const QString &rawName, QImage &coverImg, PageListItem *pageItem, EBase *multiWin)
: EBase(multiWin), mDir(dir), mName(name), mRawDir(rawDir), mRawName(rawName), mCoverImg(coverImg), 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) 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");
connect(bnSelectFile, &QPushButton::clicked, this, [=] {
auto rawFile = QFileDialog::getOpenFileName(wgtAttr, tr("Select File"), gFileHome, EVideo::filters());
if(rawFile.isEmpty()) return;
QFileInfo rawInfo(rawFile);
int64_t dur;
AVCodecID codecId;
auto err = videoInfo(rawFile.toUtf8(), mCoverImg, &dur, &codecId);
if(! err.isEmpty()) {
QMessageBox::critical(wgtAttr, "Video Error", err+"\n"+rawFile);
return;
};
mRawDir = rawInfo.absolutePath();
mRawName = rawInfo.fileName();
gFileHome = mRawDir;
fdFileName->setText(mRawName);
mDuration = round(dur*0.000001);
fdDuration->setValue(mDuration);
auto outFile = transcoding(wgtAttr, rawFile, mRawName, mPageItem->mPageDir, mCoverImg.width(), mCoverImg.height(), codecId);
if(outFile.isEmpty()) return;
QFileInfo outInfo(outFile);
mDir = outInfo.absolutePath();
mName = outInfo.fileName();
});
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 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;
// if(gProgItem->maxLen) {
// auto waitingDlg = new WaitingDlg(mPageItem->listWidget()->window(), "正在转码视频 ...");
// auto thread = new VideoSplitThread(mWidth, mHeight, gProgItem->maxLen, gProgItem->isVer ? gProgItem->mWidth : gProgItem->mHeight, gProgItem->partLens, gProgItem->isVer, pos(), saveFile.toUtf8());
// connect(thread, &VideoSplitThread::emErr, this, [=](QString err) {
// waitingDlg->close();
// if(! err.isEmpty()) QMessageBox::critical(mPageItem->listWidget()->window(), "Video trans error", err+"\n"+saveFile);
// });
// connect(thread, &VideoSplitThread::emProgress, this, [saveFile, waitingDlg](int progress) {
// waitingDlg->fdText->setText(QString("正在转码视频 %1%").arg(progress));
// });
// thread->start();
// waitingDlg->exec();
// }
return true;
}
JObj EVideo::attrJson() const {
JObj obj{
{"elementType", "Video"},
{"path", mDir},
{"file", mName},
{"pathRaw", mRawDir},
{"fileRaw", mRawName},
{"playTimes", playTimes}
};
addBaseAttr(obj);
return obj;
}
QString EVideo::transcoding(QWidget *parent, QString rawFile, QString rawName, QString dir, int w, int h, AVCodecID codec_id) {
// if(gProgItem->maxLen) {
// auto outFile = dir+"/"+rawName;
// QFile::copy(rawFile, outFile);
// return outFile;
// }
QSettings settings;
int rawMax = qMax(w, h);
if(settings.value("VideoCompress").toBool() && rawMax > 1360 && (w > gProgItem->mWidth || h > gProgItem->mHeight)) {
auto rate = 1280.0 / rawMax;
w *= rate;
h *= rate;
qDebug()<<"Compressed Size"<<w<<h;
} else if(! settings.value("VideoTranscoding").toBool() || codec_id == AV_CODEC_ID_H264) {
auto 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(parent);
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(parent, tr("Error"), QString(QMetaEnum::fromType<QProcess::ProcessError>().valueToKey(error))+" ("+QString::number(error)+")");
});
connect(&process, &QProcess::readyReadStandardOutput, &msgBox, [&process] {
auto data = process.readAllStandardOutput();
qInfo()<<"EVideo::transcoding StandardOutput";
qInfo()<<data.data();
});
int dur = 0;
QString err;
connect(&process, &QProcess::readyReadStandardError, &msgBox, [&process, &msgBox, &dur, &err] {
auto data = process.readAllStandardError();
qInfo()<<"EVideo::transcoding ffmpeg Output";
qInfo()<<data.data();
auto txt = QString::fromUtf8(data);
if(dur==0) {
int idx = txt.indexOf("Duration: ");
if(idx > -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(parent, tr("Error"), err);
return "";
}
return outFile;
}