Fix ffmpeg loaded levels (#2876)
* Fix reading ffmpeg frame count and rate * Make ffmpeg loaded levels uneditable * Ffmpeg extract frames only when loading * Fix ffmpeg gif input frame rate
This commit is contained in:
parent
62d519fb72
commit
f2da01b0ab
15 changed files with 97 additions and 62 deletions
|
@ -258,8 +258,8 @@ ffmpegFileInfo Ffmpeg::getInfo() {
|
|||
} else {
|
||||
QFile infoText(tempPath);
|
||||
getSize();
|
||||
getFrameRate();
|
||||
getFrameCount();
|
||||
getFrameRate();
|
||||
infoText.open(QIODevice::WriteOnly);
|
||||
std::string infoToWrite =
|
||||
std::to_string(m_lx) + " " + std::to_string(m_ly) + " " +
|
||||
|
@ -306,24 +306,22 @@ TRasterImageP Ffmpeg::getImage(int frameIndex) {
|
|||
}
|
||||
|
||||
double Ffmpeg::getFrameRate() {
|
||||
if (m_frameCount > 0) {
|
||||
QStringList fpsArgs;
|
||||
fpsArgs << "-v";
|
||||
fpsArgs << "error";
|
||||
fpsArgs << "-select_streams";
|
||||
fpsArgs << "v:0";
|
||||
fpsArgs << "-show_entries";
|
||||
fpsArgs << "stream=avg_frame_rate";
|
||||
fpsArgs << "-of";
|
||||
fpsArgs << "default=noprint_wrappers=1:nokey=1";
|
||||
fpsArgs << m_path.getQString();
|
||||
QString fpsResults = runFfprobe(fpsArgs);
|
||||
QStringList fpsArgs;
|
||||
fpsArgs << "-v";
|
||||
fpsArgs << "error";
|
||||
fpsArgs << "-select_streams";
|
||||
fpsArgs << "v:0";
|
||||
fpsArgs << "-show_entries";
|
||||
fpsArgs << "stream=r_frame_rate";
|
||||
fpsArgs << "-of";
|
||||
fpsArgs << "default=noprint_wrappers=1:nokey=1";
|
||||
fpsArgs << m_path.getQString();
|
||||
QString fpsResults = runFfprobe(fpsArgs);
|
||||
|
||||
int fpsNum = fpsResults.split("/")[0].toInt();
|
||||
int fpsDen = fpsResults.split("/")[1].toInt();
|
||||
if (fpsDen > 0) {
|
||||
m_frameRate = fpsNum / fpsDen;
|
||||
}
|
||||
int fpsNum = fpsResults.split("/")[0].toInt();
|
||||
int fpsDen = fpsResults.split("/")[1].toInt();
|
||||
if (fpsDen > 0) {
|
||||
m_frameRate = (double)fpsNum / (double)fpsDen;
|
||||
}
|
||||
return m_frameRate;
|
||||
}
|
||||
|
@ -355,13 +353,13 @@ int Ffmpeg::getFrameCount() {
|
|||
frameCountArgs << "-select_streams";
|
||||
frameCountArgs << "v:0";
|
||||
frameCountArgs << "-show_entries";
|
||||
frameCountArgs << "stream=nb_read_frames";
|
||||
frameCountArgs << "stream=duration";
|
||||
frameCountArgs << "-of";
|
||||
frameCountArgs << "default=nokey=1:noprint_wrappers=1";
|
||||
frameCountArgs << m_path.getQString();
|
||||
|
||||
QString frameResults = runFfprobe(frameCountArgs);
|
||||
m_frameCount = frameResults.toInt();
|
||||
m_frameCount = frameResults.toDouble() * getFrameRate();
|
||||
return m_frameCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ TLevelWriterGif::~TLevelWriterGif() {
|
|||
preIArgs << "-v";
|
||||
preIArgs << "warning";
|
||||
preIArgs << "-r";
|
||||
preIArgs << QString::number(m_frameRate);
|
||||
preIArgs << QString::number((m_frameRate < 1 ? 12.0 : m_frameRate));
|
||||
if (m_palette) {
|
||||
postIArgs << "-i";
|
||||
postIArgs << palette;
|
||||
|
@ -194,8 +194,6 @@ TLevelReaderGif::TLevelReaderGif(const TFilePath &path)
|
|||
m_lx = m_size.lx;
|
||||
m_ly = m_size.ly;
|
||||
|
||||
ffmpegReader->getFramesFromMovie();
|
||||
m_frameCount = ffmpegReader->getGifFrameCount();
|
||||
// set values
|
||||
m_info = new TImageInfo();
|
||||
m_info->m_frameRate = fps;
|
||||
|
@ -239,6 +237,10 @@ TDimension TLevelReaderGif::getSize() { return m_size; }
|
|||
//------------------------------------------------
|
||||
|
||||
TImageP TLevelReaderGif::load(int frameIndex) {
|
||||
if (!ffmpegFramesCreated) {
|
||||
ffmpegReader->getFramesFromMovie();
|
||||
ffmpegFramesCreated = true;
|
||||
}
|
||||
return ffmpegReader->getImage(frameIndex);
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
// void *m_decompressedBuffer;
|
||||
private:
|
||||
Ffmpeg *ffmpegReader;
|
||||
bool ffmpegFramesCreated = false;
|
||||
TDimension m_size;
|
||||
int m_frameCount, m_lx, m_ly;
|
||||
};
|
||||
|
|
|
@ -173,8 +173,6 @@ TLevelReaderMp4::TLevelReaderMp4(const TFilePath &path) : TLevelReader(path) {
|
|||
m_lx = m_size.lx;
|
||||
m_ly = m_size.ly;
|
||||
|
||||
ffmpegReader->getFramesFromMovie();
|
||||
|
||||
// set values
|
||||
m_info = new TImageInfo();
|
||||
m_info->m_frameRate = fps;
|
||||
|
@ -219,6 +217,10 @@ TDimension TLevelReaderMp4::getSize() { return m_size; }
|
|||
//------------------------------------------------
|
||||
|
||||
TImageP TLevelReaderMp4::load(int frameIndex) {
|
||||
if (!ffmpegFramesCreated) {
|
||||
ffmpegReader->getFramesFromMovie();
|
||||
ffmpegFramesCreated = true;
|
||||
}
|
||||
return ffmpegReader->getImage(frameIndex);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
// void *m_decompressedBuffer;
|
||||
private:
|
||||
Ffmpeg *ffmpegReader;
|
||||
bool ffmpegFramesCreated = false;
|
||||
TDimension m_size;
|
||||
int m_frameCount, m_lx, m_ly;
|
||||
};
|
||||
|
|
|
@ -174,8 +174,6 @@ TLevelReaderWebm::TLevelReaderWebm(const TFilePath &path) : TLevelReader(path) {
|
|||
m_lx = m_size.lx;
|
||||
m_ly = m_size.ly;
|
||||
|
||||
ffmpegReader->getFramesFromMovie();
|
||||
|
||||
// set values
|
||||
m_info = new TImageInfo();
|
||||
m_info->m_frameRate = fps;
|
||||
|
@ -220,6 +218,10 @@ TDimension TLevelReaderWebm::getSize() { return m_size; }
|
|||
//------------------------------------------------
|
||||
|
||||
TImageP TLevelReaderWebm::load(int frameIndex) {
|
||||
if (!ffmpegFramesCreated) {
|
||||
ffmpegReader->getFramesFromMovie();
|
||||
ffmpegFramesCreated = true;
|
||||
}
|
||||
return ffmpegReader->getImage(frameIndex);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
// void *m_decompressedBuffer;
|
||||
private:
|
||||
Ffmpeg *ffmpegReader;
|
||||
bool ffmpegFramesCreated = false;
|
||||
TDimension m_size;
|
||||
int m_frameCount, m_lx, m_ly;
|
||||
};
|
||||
|
|
|
@ -990,6 +990,9 @@ QString TTool::updateEnabled(int rowIndex, int columnIndex) {
|
|||
// Check level type write support
|
||||
if (sl->getPath().getType() ==
|
||||
"psd" || // We don't have the API to write psd files
|
||||
sl->getPath().getType() == "gif" ||
|
||||
sl->getPath().getType() == "mp4" ||
|
||||
sl->getPath().getType() == "webm" ||
|
||||
sl->is16BitChannelLevel() || // Inherited by previous implementation.
|
||||
// Could be fixed?
|
||||
sl->getProperties()->getBpp() ==
|
||||
|
|
|
@ -495,7 +495,9 @@ bool pasteStrokesInCellWithoutUndo(
|
|||
} else {
|
||||
vi = cell.getImage(true);
|
||||
sl = cell.getSimpleLevel();
|
||||
if (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() == "psd")
|
||||
if (sl->getType() == OVL_XSHLEVEL &&
|
||||
(sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" ||
|
||||
sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm"))
|
||||
return false;
|
||||
fid = cell.getFrameId();
|
||||
if (!vi) {
|
||||
|
@ -1561,7 +1563,9 @@ static void pasteRasterImageInCell(int row, int col,
|
|||
if (sl) oldPalette = sl->getPalette();
|
||||
} else {
|
||||
TXshSimpleLevel *sl = cell.getSimpleLevel();
|
||||
if (sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() == "psd")
|
||||
if (sl->getType() == OVL_XSHLEVEL &&
|
||||
(sl->getPath().getType() == "psd" || sl->getPath().getType() == "gif" ||
|
||||
sl->getPath().getType() == "mp4" || sl->getPath().getType() == "webm"))
|
||||
return;
|
||||
oldPalette = sl->getPalette();
|
||||
}
|
||||
|
|
|
@ -599,7 +599,7 @@ ReframeUndo::ReframeUndo(int r0, int r1, std::vector<int> columnIndeces,
|
|||
assert(m_cells);
|
||||
int k = 0;
|
||||
for (int r = r0; r <= r1; r++)
|
||||
for (int c = 0; c < (int)m_columnIndeces.size(); c++)
|
||||
for (int c = 0; c < (int)m_columnIndeces.size(); c++)
|
||||
m_cells[k++] = TApp::instance()->getCurrentXsheet()->getXsheet()->getCell(
|
||||
r, m_columnIndeces[c]);
|
||||
|
||||
|
@ -719,7 +719,7 @@ void TCellSelection::reframeWithEmptyInbetweens() {
|
|||
|
||||
// destruction of m_reframePopup will be done along with the main window
|
||||
if (!m_reframePopup) m_reframePopup = new ReframePopup();
|
||||
int ret = m_reframePopup->exec();
|
||||
int ret = m_reframePopup->exec();
|
||||
if (ret == QDialog::Rejected) return;
|
||||
|
||||
int step, blank;
|
||||
|
@ -762,7 +762,7 @@ void TColumnSelection::reframeWithEmptyInbetweens() {
|
|||
colIndeces.push_back(*it);
|
||||
|
||||
if (!m_reframePopup) m_reframePopup = new ReframePopup();
|
||||
int ret = m_reframePopup->exec();
|
||||
int ret = m_reframePopup->exec();
|
||||
if (ret == QDialog::Rejected) return;
|
||||
|
||||
int step, blank;
|
||||
|
@ -1296,8 +1296,9 @@ public:
|
|||
void undo() const override;
|
||||
|
||||
int getSize() const override {
|
||||
return sizeof *this + (sizeof(TXshLevelP) + sizeof(TXshSimpleLevel *)) *
|
||||
m_insertedLevels.size();
|
||||
return sizeof *this +
|
||||
(sizeof(TXshLevelP) + sizeof(TXshSimpleLevel *)) *
|
||||
m_insertedLevels.size();
|
||||
}
|
||||
|
||||
QString getHistoryString() override {
|
||||
|
@ -1526,7 +1527,11 @@ void CloneLevelUndo::cloneLevels() const {
|
|||
assert(lt->first && !lt->second.empty());
|
||||
|
||||
TXshSimpleLevel *srcSl = lt->first;
|
||||
if (srcSl->getPath().getType() == "psd") continue;
|
||||
if (srcSl->getPath().getType() == "psd" ||
|
||||
srcSl->getPath().getType() == "gif" ||
|
||||
srcSl->getPath().getType() == "mp4" ||
|
||||
srcSl->getPath().getType() == "webm")
|
||||
continue;
|
||||
|
||||
const TFilePath &srcPath = srcSl->getPath();
|
||||
|
||||
|
|
|
@ -179,7 +179,10 @@ static bool canMergeColumns(int column, int mColumn, bool forMatchlines) {
|
|||
return false;
|
||||
// Check level type write support. Based on TTool::updateEnabled()
|
||||
if (level->getType() == OVL_XSHLEVEL &&
|
||||
(level->getPath().getType() == "psd" || // PSD files.
|
||||
(level->getPath().getType() == "psd" || // PSD files.
|
||||
level->getPath().getType() == "gif" ||
|
||||
level->getPath().getType() == "mp4" ||
|
||||
level->getPath().getType() == "webm" ||
|
||||
level->is16BitChannelLevel() || // 16bpc images.
|
||||
level->getProperties()->getBpp() == 1)) { // Black & White images.
|
||||
return false;
|
||||
|
|
|
@ -1065,7 +1065,10 @@ void FilmstripFrames::contextMenuEvent(QContextMenuEvent *event) {
|
|||
menu->addAction(cm->getAction(MI_RevertToCleanedUp));
|
||||
if (sl &&
|
||||
(sl->getType() == TZP_XSHLEVEL || sl->getType() == PLI_XSHLEVEL ||
|
||||
(sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "psd")))
|
||||
(sl->getType() == OVL_XSHLEVEL && sl->getPath().getType() != "psd" &&
|
||||
sl->getPath().getType() != "gif" &&
|
||||
sl->getPath().getType() != "mp4" &&
|
||||
sl->getPath().getType() != "webm")))
|
||||
menu->addAction(cm->getAction(MI_RevertToLastSaved));
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,9 @@ void TFilmstripSelection::enableCommands() {
|
|||
|
||||
bool doEnable =
|
||||
(type == PLI_XSHLEVEL || type == TZP_XSHLEVEL || type == MESH_XSHLEVEL ||
|
||||
(type == OVL_XSHLEVEL && path.getType() != "psd"));
|
||||
(type == OVL_XSHLEVEL && path.getType() != "psd" &&
|
||||
path.getType() != "gif" && path.getType() != "mp4" &&
|
||||
path.getType() != "webm"));
|
||||
|
||||
TRasterImageP ri = (TRasterImageP)sl->getSimpleLevel()->getFrame(
|
||||
sl->getSimpleLevel()->getFirstFid(), false);
|
||||
|
|
|
@ -309,7 +309,10 @@ void doCloneLevelNoSave(const TCellSelection::Range &range,
|
|||
fid = cell.getFrameId();
|
||||
|
||||
if (cell.getSimpleLevel() == 0 ||
|
||||
cell.getSimpleLevel()->getPath().getType() == "psd")
|
||||
cell.getSimpleLevel()->getPath().getType() == "psd" ||
|
||||
cell.getSimpleLevel()->getPath().getType() == "gif" ||
|
||||
cell.getSimpleLevel()->getPath().getType() == "mp4" ||
|
||||
cell.getSimpleLevel()->getPath().getType() == "webm")
|
||||
continue;
|
||||
|
||||
std::map<TXshSimpleLevel *, TXshLevelP>::iterator it =
|
||||
|
|
|
@ -104,8 +104,8 @@ bool checkCreatorString(const QString &creator) {
|
|||
if (pos >= 0 && len >= 4) {
|
||||
QString v;
|
||||
if (len > 4) v = creator.mid(pos + 3, len - 4);
|
||||
bool ok = true;
|
||||
mask = v.toInt(&ok, 16);
|
||||
bool ok = true;
|
||||
mask = v.toInt(&ok, 16);
|
||||
}
|
||||
}
|
||||
return (mask & compatibility.neededMask) == compatibility.neededMask &&
|
||||
|
@ -119,7 +119,9 @@ bool isAreadOnlyLevel(const TFilePath &path) {
|
|||
if (path.getDots() == "." ||
|
||||
(path.getDots() == ".." &&
|
||||
(path.getType() == "tlv" || path.getType() == "tpl"))) {
|
||||
if (path.getType() == "psd") return true;
|
||||
if (path.getType() == "psd" || path.getType() == "gif" ||
|
||||
path.getType() == "mp4" || path.getType() == "webm")
|
||||
return true;
|
||||
if (!TSystem::doesExistFileOrLevel(path)) return false;
|
||||
TFileStatus fs(path);
|
||||
return !fs.isWritable();
|
||||
|
@ -169,8 +171,8 @@ void getIndexesRangefromFids(TXshSimpleLevel *level,
|
|||
|
||||
std::set<TFrameId>::const_iterator it;
|
||||
for (it = fids.begin(); it != fids.end(); ++it) {
|
||||
int index = level->guessIndex(*it);
|
||||
if (index > toIndex) toIndex = index;
|
||||
int index = level->guessIndex(*it);
|
||||
if (index > toIndex) toIndex = index;
|
||||
if (index < fromIndex) fromIndex = index;
|
||||
}
|
||||
}
|
||||
|
@ -909,13 +911,13 @@ void TXshSimpleLevel::loadData(TIStream &is) {
|
|||
if (is.getTagParam("dpix", v)) xdpi = std::stod(v);
|
||||
if (is.getTagParam("dpiy", v)) ydpi = std::stod(v);
|
||||
if (xdpi != 0 && ydpi != 0) dpiPolicy = LevelProperties::DP_CustomDpi;
|
||||
std::string dpiType = is.getTagAttribute("dpiType");
|
||||
if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi;
|
||||
if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL;
|
||||
if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v);
|
||||
if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v);
|
||||
std::string dpiType = is.getTagAttribute("dpiType");
|
||||
if (dpiType == "image") dpiPolicy = LevelProperties::DP_ImageDpi;
|
||||
if (is.getTagParam("type", v) && v == "s") type = TZI_XSHLEVEL;
|
||||
if (is.getTagParam("subsampling", v)) subsampling = std::stoi(v);
|
||||
if (is.getTagParam("premultiply", v)) doPremultiply = std::stoi(v);
|
||||
if (is.getTagParam("antialias", v)) antialiasSoftness = std::stoi(v);
|
||||
if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v);
|
||||
if (is.getTagParam("whiteTransp", v)) whiteTransp = std::stoi(v);
|
||||
|
||||
m_properties->setDpiPolicy(dpiPolicy);
|
||||
m_properties->setDpi(TPointD(xdpi, ydpi));
|
||||
|
@ -1046,8 +1048,9 @@ static TFilePath getLevelPathAndSetNameWithPsdLevelName(
|
|||
if (removeFileName) wLevelName = list[1].toStdWString();
|
||||
|
||||
TLevelSet *levelSet = xshLevel->getScene()->getLevelSet();
|
||||
if (levelSet && levelSet->hasLevel(
|
||||
wLevelName)) // levelSet should be asserted instead
|
||||
if (levelSet &&
|
||||
levelSet->hasLevel(
|
||||
wLevelName)) // levelSet should be asserted instead
|
||||
levelSet->renameLevel(xshLevel, wLevelName);
|
||||
|
||||
xshLevel->setName(wLevelName);
|
||||
|
@ -1580,8 +1583,8 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
|
|||
std::vector<TFrameId> fids;
|
||||
getFids(fids);
|
||||
|
||||
bool isLevelModified = getProperties()->getDirtyFlag();
|
||||
bool isPaletteModified = false;
|
||||
bool isLevelModified = getProperties()->getDirtyFlag();
|
||||
bool isPaletteModified = false;
|
||||
if (getPalette()) isPaletteModified = getPalette()->getDirtyFlag();
|
||||
|
||||
if (isLevelModified || isPaletteModified) {
|
||||
|
@ -1712,6 +1715,9 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
|
|||
// strategia
|
||||
LevelUpdater updater(this);
|
||||
updater.getLevelWriter()->setCreator(getCreatorString());
|
||||
if (updater.getImageInfo())
|
||||
updater.getLevelWriter()->setFrameRate(
|
||||
updater.getImageInfo()->m_frameRate);
|
||||
|
||||
if (isLevelModified) {
|
||||
// Apply the level's renumber table, before saving other files.
|
||||
|
@ -1824,11 +1830,11 @@ void TXshSimpleLevel::saveSimpleLevel(const TFilePath &decodedFp,
|
|||
std::string TXshSimpleLevel::getImageId(const TFrameId &fid,
|
||||
int frameStatus) const {
|
||||
if (frameStatus < 0) frameStatus = getFrameStatus(fid);
|
||||
std::string prefix = "L";
|
||||
std::string prefix = "L";
|
||||
if (frameStatus & CleanupPreview)
|
||||
prefix = "P";
|
||||
else if ((frameStatus & (Scanned | Cleanupped)) == Scanned)
|
||||
prefix = "S";
|
||||
prefix = "S";
|
||||
std::string imageId = m_idBase + "_" + prefix + fid.expand();
|
||||
return imageId;
|
||||
}
|
||||
|
@ -2254,14 +2260,13 @@ TFilePath TXshSimpleLevel::getExistingHookFile(
|
|||
|
||||
int f, fCount = hookFiles.size();
|
||||
for (f = 0; f != fCount; ++f) {
|
||||
fPattern = locals::getPattern(hookFiles[f]);
|
||||
fPattern = locals::getPattern(hookFiles[f]);
|
||||
if (fPattern < p) p = fPattern, h = f;
|
||||
}
|
||||
|
||||
assert(h >= 0);
|
||||
return (h < 0) ? TFilePath()
|
||||
: decodedLevelPath.getParentDir() +
|
||||
TFilePath(hookFiles[h].toStdWString());
|
||||
return (h < 0) ? TFilePath() : decodedLevelPath.getParentDir() +
|
||||
TFilePath(hookFiles[h].toStdWString());
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -2294,8 +2299,8 @@ TRectD TXshSimpleLevel::getBBox(const TFrameId &fid) const {
|
|||
if (!info) return TRectD();
|
||||
|
||||
bbox = TRectD(TPointD(info->m_x0, info->m_y0),
|
||||
TPointD(info->m_x1, info->m_y1))
|
||||
- 0.5 * TPointD(info->m_lx, info->m_ly);
|
||||
TPointD(info->m_x1, info->m_y1)) -
|
||||
0.5 * TPointD(info->m_lx, info->m_ly);
|
||||
|
||||
if (info->m_dpix > 0.0 && info->m_dpiy > 0.0)
|
||||
dpiX = info->m_dpix, dpiY = info->m_dpiy;
|
||||
|
|
Loading…
Reference in a new issue