diff --git a/toonz/sources/image/CMakeLists.txt b/toonz/sources/image/CMakeLists.txt index de8a302..87953ce 100644 --- a/toonz/sources/image/CMakeLists.txt +++ b/toonz/sources/image/CMakeLists.txt @@ -18,8 +18,10 @@ set(HEADERS 3gp/tiio_3gp_proxy.h ffmpeg/tiio_gif.h ffmpeg/tiio_webm.h + ffmpeg/tiio_apng.h ffmpeg/tiio_mp4.h ffmpeg/tiio_ffmpeg.h + ffmpeg/tiio_ff_mov.h sprite/tiio_sprite.h mesh/tiio_mesh.h exr/tinyexr_otmod.h @@ -47,8 +49,10 @@ set(SOURCES 3gp/tiio_3gp_proxy.cpp ffmpeg/tiio_gif.cpp ffmpeg/tiio_webm.cpp + ffmpeg/tiio_apng.cpp ffmpeg/tiio_mp4.cpp ffmpeg/tiio_ffmpeg.cpp + ffmpeg/tiio_ff_mov.cpp sprite/tiio_sprite.cpp mesh/tiio_mesh.cpp exr/tiio_exr.cpp diff --git a/toonz/sources/image/ffmpeg/tiio_apng.cpp b/toonz/sources/image/ffmpeg/tiio_apng.cpp new file mode 100644 index 0000000..b6858f2 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_apng.cpp @@ -0,0 +1,228 @@ + +#include "tiio_apng.h" +#include "tsystem.h" +#include "trasterimage.h" +#include "tsound.h" +#include "timageinfo.h" +#include "toonz/stage.h" +#include + +//=========================================================== +// +// TImageWriterAPng +// +//=========================================================== + +class TImageWriterAPng : public TImageWriter { +public: + int m_frameIndex; + + TImageWriterAPng(const TFilePath &path, int frameIndex, TLevelWriterAPng *lwg) + : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { + m_lwg->addRef(); + } + ~TImageWriterAPng() { m_lwg->release(); } + + bool is64bitOutputSupported() override { return false; } + void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } + +private: + TLevelWriterAPng *m_lwg; +}; + +//=========================================================== +// +// TLevelWriterAPng; +// +//=========================================================== + +TLevelWriterAPng::TLevelWriterAPng(const TFilePath &path, TPropertyGroup *winfo) + : TLevelWriter(path, winfo) { + if (!m_properties) m_properties = new Tiio::APngWriterProperties(); + + std::string scale = m_properties->getProperty("Scale")->getValueAsString(); + m_scale = QString::fromStdString(scale).toInt(); + + TBoolProperty *extPng = (TBoolProperty *)m_properties->getProperty("ExtPng"); + m_extPng = extPng->getValue(); + + TBoolProperty *loop = (TBoolProperty *)m_properties->getProperty("Looping"); + m_looping = loop->getValue(); + + if (m_extPng) { + m_path = m_path.getParentDir() + TFilePath(m_path.getWideName() + L".png"); + } + + ffmpegWriter = new Ffmpeg(); + ffmpegWriter->setPath(m_path); + if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path); +} + +//----------------------------------------------------------- + +TLevelWriterAPng::~TLevelWriterAPng() { + QStringList preIArgs; + QStringList postIArgs; + + int outLx = m_lx; + int outLy = m_ly; + + // set scaling + if (m_scale != 0) { + outLx = m_lx * m_scale / 100; + outLy = m_ly * m_scale / 100; + } + // ffmpeg doesn't like resolutions that aren't divisible by 2. + if (outLx % 2 != 0) outLx++; + if (outLy % 2 != 0) outLy++; + + preIArgs << "-framerate"; + preIArgs << QString::number(m_frameRate); + postIArgs << "-plays"; + postIArgs << (m_looping ? "0" : "1"); + postIArgs << "-f"; + postIArgs << "apng"; + postIArgs << "-s"; + postIArgs << QString::number(outLx) + "x" + QString::number(outLy); + + ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true); + ffmpegWriter->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriterAPng::getFrameWriter(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageWriterP(0); + int index = fid.getNumber(); + TImageWriterAPng *iwg = new TImageWriterAPng(m_path, index, this); + return TImageWriterP(iwg); +} + +//----------------------------------------------------------- +void TLevelWriterAPng::setFrameRate(double fps) { + m_frameRate = fps; + ffmpegWriter->setFrameRate(fps); +} + +void TLevelWriterAPng::saveSoundTrack(TSoundTrack *st) { + ffmpegWriter->saveSoundTrack(st); +} + +//----------------------------------------------------------- + +void TLevelWriterAPng::save(const TImageP &img, int frameIndex) { + TRasterImageP image(img); + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + ffmpegWriter->createIntermediateImage(img, frameIndex); +} + +//=========================================================== +// +// TImageReaderAPng +// +//=========================================================== + +class TImageReaderAPng final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderAPng(const TFilePath &path, int index, TLevelReaderAPng *lra, + TImageInfo *info) + : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) { + m_lra->addRef(); + } + ~TImageReaderAPng() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + const TImageInfo *getImageInfo() const override { return m_info; } + +private: + TLevelReaderAPng *m_lra; + TImageInfo *m_info; + + // not implemented + TImageReaderAPng(const TImageReaderAPng &); + TImageReaderAPng &operator=(const TImageReaderAPng &src); +}; + +//=========================================================== +// +// TLevelReaderAPng +// +//=========================================================== + +TLevelReaderAPng::TLevelReaderAPng(const TFilePath &path) : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; + m_info->m_dpix = Stage::standardDpi; + m_info->m_dpiy = Stage::standardDpi; +} +//----------------------------------------------------------- + +TLevelReaderAPng::~TLevelReaderAPng() {} + +//----------------------------------------------------------- + +TLevelP TLevelReaderAPng::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderAPng::getFrameReader(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderAPng *irm = new TImageReaderAPng(m_path, index, this, m_info); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderAPng::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderAPng::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } + return ffmpegReader->getImage(frameIndex); +} + +Tiio::APngWriterProperties::APngWriterProperties() + : m_scale("Scale", 1, 100, 100) + , m_looping("Looping", true) + , m_extPng("ExtPng", false) { + bind(m_scale); + bind(m_looping); + bind(m_extPng); +} + +void Tiio::APngWriterProperties::updateTranslation() { + m_scale.setQStringName(tr("Scale")); + m_looping.setQStringName(tr("Looping")); + m_extPng.setQStringName(tr("Write as .png")); +} diff --git a/toonz/sources/image/ffmpeg/tiio_apng.h b/toonz/sources/image/ffmpeg/tiio_apng.h new file mode 100644 index 0000000..5bc82b1 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_apng.h @@ -0,0 +1,86 @@ +#pragma once + +#ifndef TTIO_APNG_INCLUDED +#define TTIO_APNG_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "tiio_ffmpeg.h" +#include + +//=========================================================== +// +// TLevelWriterAPng +// +//=========================================================== + +class TLevelWriterAPng : public TLevelWriter { +public: + TLevelWriterAPng(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterAPng(); + void setFrameRate(double fps) override; + + TImageWriterP getFrameWriter(TFrameId fid) override; + void save(const TImageP &image, int frameIndex); + + void saveSoundTrack(TSoundTrack *st) override; + + static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { + return new TLevelWriterAPng(path, winfo); + } + +private: + Ffmpeg *ffmpegWriter; + int m_lx, m_ly; + int m_scale; + bool m_looping; + bool m_extPng; +}; + +//=========================================================== +// +// TLevelReaderAPng +// +//=========================================================== + +class TLevelReaderAPng final : public TLevelReader { +public: + TLevelReaderAPng(const TFilePath &path); + ~TLevelReaderAPng(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderAPng(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); +private: + Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + +namespace Tiio { + +//=========================================================================== + +class APngWriterProperties : public TPropertyGroup { + Q_DECLARE_TR_FUNCTIONS(APngWriterProperties) +public: + TIntProperty m_scale; + TBoolProperty m_looping; + TBoolProperty m_extPng; + APngWriterProperties(); + void updateTranslation() override; +}; + +//=========================================================================== + +} // namespace Tiio + +#endif diff --git a/toonz/sources/image/ffmpeg/tiio_ff_mov.cpp b/toonz/sources/image/ffmpeg/tiio_ff_mov.cpp new file mode 100644 index 0000000..d097bd7 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_ff_mov.cpp @@ -0,0 +1,236 @@ + +#include "tsystem.h" +#include "tiio_ff_mov.h" +#include "trasterimage.h" +#include "timageinfo.h" +#include "tsound.h" +#include "toonz/stage.h" +#include + +//=========================================================== +// +// TImageWriterFFMov +// +//=========================================================== + +class TImageWriterFFMov : public TImageWriter { +public: + int m_frameIndex; + + TImageWriterFFMov(const TFilePath &path, int frameIndex, + TLevelWriterFFMov *lwg) + : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) { + m_lwg->addRef(); + } + ~TImageWriterFFMov() { m_lwg->release(); } + + bool is64bitOutputSupported() override { return false; } + void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); } + +private: + TLevelWriterFFMov *m_lwg; +}; + +//=========================================================== +// +// TLevelWriterFFMov; +// +//=========================================================== + +TLevelWriterFFMov::TLevelWriterFFMov(const TFilePath &path, + TPropertyGroup *winfo) + : TLevelWriter(path, winfo) { + if (!m_properties) m_properties = new Tiio::FFMovWriterProperties(); + if (m_properties->getPropertyCount() == 0) { + m_scale = 100; + m_vidQuality = 100; + } else { + std::string scale = m_properties->getProperty("Scale")->getValueAsString(); + m_scale = QString::fromStdString(scale).toInt(); + std::string quality = + m_properties->getProperty("Quality")->getValueAsString(); + m_vidQuality = QString::fromStdString(quality).toInt(); + } + ffmpegWriter = new Ffmpeg(); + ffmpegWriter->setPath(m_path); + if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path); +} + +//----------------------------------------------------------- + +TLevelWriterFFMov::~TLevelWriterFFMov() { + QStringList preIArgs; + QStringList postIArgs; + + int outLx = m_lx; + int outLy = m_ly; + + // set scaling + if (m_scale != 0) { + outLx = m_lx * m_scale / 100; + outLy = m_ly * m_scale / 100; + } + // ffmpeg doesn't like resolutions that aren't divisible by 2. + if (outLx % 2 != 0) outLx++; + if (outLy % 2 != 0) outLy++; + + // calculate quality (bitrate) + int pixelCount = m_lx * m_ly; + int bitRate = pixelCount / 150; // crude but gets decent values + double quality = m_vidQuality / 100.0; + double tempRate = (double)bitRate * quality; + int finalBitrate = (int)tempRate; + int crf = 51 - (m_vidQuality * 51 / 100); + + preIArgs << "-framerate"; + preIArgs << QString::number(m_frameRate); + + postIArgs << "-c:v"; + postIArgs << "prores_ks"; + postIArgs << "-pix_fmt"; + postIArgs << "yuva444p10le"; + postIArgs << "-profile:v"; + postIArgs << "4"; + postIArgs << "-s"; + postIArgs << QString::number(outLx) + "x" + QString::number(outLy); + postIArgs << "-b"; + postIArgs << QString::number(finalBitrate) + "k"; + + ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true); + ffmpegWriter->cleanUpFiles(); +} + +//----------------------------------------------------------- + +TImageWriterP TLevelWriterFFMov::getFrameWriter(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageWriterP(0); + int index = fid.getNumber(); + TImageWriterFFMov *iwg = new TImageWriterFFMov(m_path, index, this); + return TImageWriterP(iwg); +} + +//----------------------------------------------------------- +void TLevelWriterFFMov::setFrameRate(double fps) { + m_frameRate = fps; + ffmpegWriter->setFrameRate(fps); +} + +void TLevelWriterFFMov::saveSoundTrack(TSoundTrack *st) { + ffmpegWriter->saveSoundTrack(st); +} + +//----------------------------------------------------------- + +void TLevelWriterFFMov::save(const TImageP &img, int frameIndex) { + TRasterImageP image(img); + m_lx = image->getRaster()->getLx(); + m_ly = image->getRaster()->getLy(); + ffmpegWriter->createIntermediateImage(img, frameIndex); +} + +//=========================================================== +// +// TImageReaderFFMov +// +//=========================================================== + +class TImageReaderFFMov final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderFFMov(const TFilePath &path, int index, TLevelReaderFFMov *lra, + TImageInfo *info) + : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) { + m_lra->addRef(); + } + ~TImageReaderFFMov() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + const TImageInfo *getImageInfo() const override { return m_info; } + +private: + TLevelReaderFFMov *m_lra; + TImageInfo *m_info; + + // not implemented + TImageReaderFFMov(const TImageReaderFFMov &); + TImageReaderFFMov &operator=(const TImageReaderFFMov &src); +}; + +//=========================================================== +// +// TLevelReaderFFMov +// +//=========================================================== + +TLevelReaderFFMov::TLevelReaderFFMov(const TFilePath &path) + : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; + m_info->m_dpix = Stage::standardDpi; + m_info->m_dpiy = Stage::standardDpi; +} +//----------------------------------------------------------- + +TLevelReaderFFMov::~TLevelReaderFFMov() {} + +//----------------------------------------------------------- + +TLevelP TLevelReaderFFMov::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderFFMov::getFrameReader(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderFFMov *irm = new TImageReaderFFMov(m_path, index, this, m_info); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderFFMov::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderFFMov::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } + return ffmpegReader->getImage(frameIndex); +} + +Tiio::FFMovWriterProperties::FFMovWriterProperties() + : m_vidQuality("Quality", 1, 100, 90), m_scale("Scale", 1, 100, 100) { + bind(m_vidQuality); + bind(m_scale); +} + +void Tiio::FFMovWriterProperties::updateTranslation() { + m_vidQuality.setQStringName(tr("Quality")); + m_scale.setQStringName(tr("Scale")); +} diff --git a/toonz/sources/image/ffmpeg/tiio_ff_mov.h b/toonz/sources/image/ffmpeg/tiio_ff_mov.h new file mode 100644 index 0000000..5604473 --- /dev/null +++ b/toonz/sources/image/ffmpeg/tiio_ff_mov.h @@ -0,0 +1,85 @@ +#pragma once + +#ifndef TTIO_FFMPEG_MOV_INCLUDED +#define TTIO_FFMPEG_MOV_INCLUDED + +#include "tproperty.h" +#include "tlevel_io.h" +#include "tiio_ffmpeg.h" + +#include + +//=========================================================== +// +// TLevelWriterFFMov +// +//=========================================================== + +class TLevelWriterFFMov : public TLevelWriter { +public: + TLevelWriterFFMov(const TFilePath &path, TPropertyGroup *winfo); + ~TLevelWriterFFMov(); + void setFrameRate(double fps) override; + + TImageWriterP getFrameWriter(TFrameId fid) override; + void save(const TImageP &image, int frameIndex); + + void saveSoundTrack(TSoundTrack *st) override; + + static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) { + return new TLevelWriterFFMov(path, winfo); + } + +private: + Ffmpeg *ffmpegWriter; + int m_lx, m_ly; + int m_scale; + int m_vidQuality; +}; + +//=========================================================== +// +// TLevelReaderFFMov +// +//=========================================================== + +class TLevelReaderFFMov final : public TLevelReader { +public: + TLevelReaderFFMov(const TFilePath &path); + ~TLevelReaderFFMov(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderFFMov(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); +private: + Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + +namespace Tiio { + +//=========================================================================== + +class FFMovWriterProperties : public TPropertyGroup { + Q_DECLARE_TR_FUNCTIONS(FFMovWriterProperties) +public: + TIntProperty m_vidQuality; + TIntProperty m_scale; + FFMovWriterProperties(); + void updateTranslation() override; +}; + +//=========================================================================== + +} // namespace Tiio + +#endif diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp index 9a58f3f..34beb18 100644 --- a/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.cpp @@ -2,6 +2,8 @@ #include "../toonz/tapp.h" #include "tsystem.h" #include "tsound.h" +#include "timageinfo.h" +#include "toonz/stage.h" #include #include @@ -12,70 +14,20 @@ #include "toonz/preferences.h" #include "toonz/toonzfolders.h" #include "tmsgcore.h" +#include "thirdparty.h" Ffmpeg::Ffmpeg() { - m_ffmpegPath = Preferences::instance()->getFfmpegPath(); - m_ffmpegTimeout = Preferences::instance()->getFfmpegTimeout() * 1000; - std::string strPath = m_ffmpegPath.toStdString(); + m_ffmpegTimeout = ThirdParty::getFFmpegTimeout() * 1000; m_intermediateFormat = "png"; m_startNumber = 2147483647; // Lowest frame determines starting frame } Ffmpeg::~Ffmpeg() {} -bool Ffmpeg::checkFfmpeg() { - // ffmpeg executable -#if defined(_WIN32) - QString exe = "/ffmpeg.exe"; -#else - QString exe = "/ffmpeg"; -#endif - - // check the user defined path in preferences first - QString path = Preferences::instance()->getFfmpegPath() + exe; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // check the OpenToonz root directory next - path = QDir::currentPath() + exe; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue(ffmpegPath, QDir::currentPath()); - return true; - } - - // give up - return false; -} - -bool Ffmpeg::checkFfprobe() { - // check the user defined path in preferences first - QString path = Preferences::instance()->getFfmpegPath() + "/ffprobe"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // check the OpenToonz root directory next - path = QDir::currentPath() + "/ffprobe"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue(ffmpegPath, QDir::currentPath()); - return true; - } - - // give up - return false; -} - bool Ffmpeg::checkFormat(std::string format) { - QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif QStringList args; args << "-formats"; QProcess ffmpeg; - ffmpeg.start(path, args); + ThirdParty::runFFmpeg(ffmpeg, args); ffmpeg.waitForFinished(); QString results = ffmpeg.readAllStandardError(); results += ffmpeg.readAllStandardOutput(); @@ -109,8 +61,7 @@ void Ffmpeg::createIntermediateImage(const TImageP &img, int frameIndex) { if (frameIndex < m_startNumber) m_startNumber = frameIndex; QString tempPath = getFfmpegCache().getQString() + "//" + QString::fromStdString(m_path.getName()) + "tempOut" + - QString::number(frameIndex) + "." + - m_intermediateFormat; + QString::number(frameIndex) + "." + m_intermediateFormat; std::string saveStatus = ""; TRasterImageP tempImage(img); TRasterImage *image = (TRasterImage *)tempImage->cloneImage(); @@ -145,7 +96,7 @@ void Ffmpeg::createIntermediateImage(const TImageP &img, int frameIndex) { void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, bool includesInPath, bool includesOutPath, - bool overWriteFiles) { + bool overWriteFiles, bool asyncProcess) { QString tempName = "//" + QString::fromStdString(m_path.getName()) + "tempOut%d." + m_intermediateFormat; tempName = getFfmpegCache().getQString() + tempName; @@ -174,11 +125,11 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, // write the file QProcess ffmpeg; - ffmpeg.start(m_ffmpegPath + "/ffmpeg", args); - if (waitFfmpeg(ffmpeg)) { + ThirdParty::runFFmpeg(ffmpeg, args); + if (waitFfmpeg(ffmpeg, asyncProcess)) { QString results = ffmpeg.readAllStandardError(); results += ffmpeg.readAllStandardOutput(); - int exitCode = ffmpeg.exitCode(); + int exitCode = ffmpeg.exitCode(); std::string strResults = results.toStdString(); } ffmpeg.close(); @@ -186,8 +137,8 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs, QString Ffmpeg::runFfprobe(QStringList args) { QProcess ffmpeg; - ffmpeg.start(m_ffmpegPath + "/ffprobe", args); - if (!waitFfmpeg(ffmpeg)) { + ThirdParty::runFFprobe(ffmpeg, args); + if (!waitFfmpeg(ffmpeg, false)) { throw TImageException(m_path, "error accessing ffprobe."); } QString results = ffmpeg.readAllStandardError(); @@ -201,19 +152,20 @@ QString Ffmpeg::runFfprobe(QStringList args) { return results; } -bool Ffmpeg::waitFfmpeg(const QProcess &ffmpeg) { - QEventLoop eloop; - QTimer timer; - timer.connect(&timer, &QTimer::timeout, &eloop, [&eloop] { eloop.exit(-2); }); - ffmpeg.connect(&ffmpeg, &QProcess::errorOccurred, &eloop, - [&eloop] { eloop.exit(-1); }); - ffmpeg.connect(&ffmpeg, - static_cast( - &QProcess::finished), - &eloop, &QEventLoop::quit); - timer.start(m_ffmpegTimeout); - - int exitCode = eloop.exec(); +bool Ffmpeg::waitFfmpeg(QProcess &ffmpeg, bool asyncProcess) { + if (!asyncProcess) { + bool status = ffmpeg.waitForFinished(m_ffmpegTimeout); + if (!status) { + DVGui::warning( + QObject::tr("FFmpeg timed out.\n" + "Please check the file for errors.\n" + "If the file doesn't play or is incomplete, \n" + "Please try raising the FFmpeg timeout in Preferences.")); + } + return status; + } + + int exitCode = ThirdParty::waitAsyncProcess(ffmpeg, m_ffmpegTimeout); if (exitCode == 0) return true; if (exitCode == -1) { DVGui::warning( @@ -283,7 +235,7 @@ ffmpegFileInfo Ffmpeg::getInfo() { infoText.open(QIODevice::ReadOnly); QByteArray ba = infoText.readAll(); infoText.close(); - QString text = QString::fromStdString(ba.toStdString()); + QString text = QString::fromStdString(ba.toStdString()); QStringList textlist = text.split(" "); if (textlist.count() >= 4) { m_lx = textlist[0].toInt(); @@ -486,7 +438,7 @@ void Ffmpeg::getFramesFromMovie(int frame) { postIFrameArgs << tempName; - runFfmpeg(preIFrameArgs, postIFrameArgs, true, true, true); + runFfmpeg(preIFrameArgs, postIFrameArgs, true, true, true, false); for (int i = 1; i <= m_frameCount; i++) { QString number = QString("%1").arg(i, 4, 10, QChar('0')); @@ -536,4 +488,100 @@ void Ffmpeg::cleanUpFiles() { void Ffmpeg::disablePrecompute() { Preferences::instance()->setPrecompute(false); -} \ No newline at end of file +} + +//=========================================================== +// +// TImageReaderFFmpeg +// +//=========================================================== + +class TImageReaderFFmpeg final : public TImageReader { +public: + int m_frameIndex; + + TImageReaderFFmpeg(const TFilePath &path, int index, TLevelReaderFFmpeg *lra, + TImageInfo *info) + : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) { + m_lra->addRef(); + } + ~TImageReaderFFmpeg() { m_lra->release(); } + + TImageP load() override { return m_lra->load(m_frameIndex); } + TDimension getSize() const { return m_lra->getSize(); } + TRect getBBox() const { return TRect(); } + const TImageInfo *getImageInfo() const override { return m_info; } + +private: + TLevelReaderFFmpeg *m_lra; + TImageInfo *m_info; + + // not implemented + TImageReaderFFmpeg(const TImageReaderFFmpeg &); + TImageReaderFFmpeg &operator=(const TImageReaderFFmpeg &src); +}; + +//=========================================================== +// +// TLevelReaderFFmpeg +// +//=========================================================== + +TLevelReaderFFmpeg::TLevelReaderFFmpeg(const TFilePath &path) + : TLevelReader(path) { + ffmpegReader = new Ffmpeg(); + ffmpegReader->setPath(m_path); + ffmpegReader->disablePrecompute(); + ffmpegFileInfo tempInfo = ffmpegReader->getInfo(); + double fps = tempInfo.m_frameRate; + m_frameCount = tempInfo.m_frameCount; + m_size = TDimension(tempInfo.m_lx, tempInfo.m_ly); + m_lx = m_size.lx; + m_ly = m_size.ly; + + // set values + m_info = new TImageInfo(); + m_info->m_frameRate = fps; + m_info->m_lx = m_lx; + m_info->m_ly = m_ly; + m_info->m_bitsPerSample = 8; + m_info->m_samplePerPixel = 4; + m_info->m_dpix = Stage::standardDpi; + m_info->m_dpiy = Stage::standardDpi; +} +//----------------------------------------------------------- + +TLevelReaderFFmpeg::~TLevelReaderFFmpeg() {} + +//----------------------------------------------------------- + +TLevelP TLevelReaderFFmpeg::loadInfo() { + if (m_frameCount == -1) return TLevelP(); + TLevelP level; + for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP()); + return level; +} + +//----------------------------------------------------------- + +TImageReaderP TLevelReaderFFmpeg::getFrameReader(TFrameId fid) { + if (!fid.getLetter().isEmpty()) return TImageReaderP(0); + int index = fid.getNumber(); + + TImageReaderFFmpeg *irm = new TImageReaderFFmpeg(m_path, index, this, m_info); + return TImageReaderP(irm); +} + +//------------------------------------------------------------------------------ + +TDimension TLevelReaderFFmpeg::getSize() { return m_size; } + +//------------------------------------------------ + +TImageP TLevelReaderFFmpeg::load(int frameIndex) { + if (!ffmpegFramesCreated) { + ffmpegReader->getFramesFromMovie(); + ffmpegFramesCreated = true; + } + return ffmpegReader->getImage(frameIndex); +} diff --git a/toonz/sources/image/ffmpeg/tiio_ffmpeg.h b/toonz/sources/image/ffmpeg/tiio_ffmpeg.h index 359b84b..878c364 100644 --- a/toonz/sources/image/ffmpeg/tiio_ffmpeg.h +++ b/toonz/sources/image/ffmpeg/tiio_ffmpeg.h @@ -21,8 +21,8 @@ public: ~Ffmpeg(); void createIntermediateImage(const TImageP &image, int frameIndex); void runFfmpeg(QStringList preIArgs, QStringList postIArgs, - bool includesInPath, bool includesOutPath, - bool overWriteFiles); + bool includesInPath, bool includesOutPath, bool overWriteFiles, + bool asyncProcess = true); void runFfmpeg(QStringList preIArgs, QStringList postIArgs, TFilePath path); QString runFfprobe(QStringList args); void cleanUpFiles(); @@ -31,8 +31,6 @@ public: void setPath(TFilePath path); void saveSoundTrack(TSoundTrack *st); bool checkFilesExist(); - static bool checkFfmpeg(); - static bool checkFfprobe(); static bool checkFormat(std::string format); double getFrameRate(); TDimension getSize(); @@ -45,17 +43,46 @@ public: int getGifFrameCount(); private: - QString m_intermediateFormat, m_ffmpegPath, m_audioPath, m_audioFormat; + QString m_intermediateFormat, m_audioPath, m_audioFormat; int m_frameCount = 0, m_lx, m_ly, m_bpp, m_bitsPerSample, m_channelCount, m_ffmpegTimeout = 30000, m_startNumber = 2147483647; - double m_frameRate = 24.0; - bool m_ffmpegExists = false, m_ffprobeExists = false, m_hasSoundTrack = false; + double m_frameRate = 24.0; + bool m_hasSoundTrack = false; TFilePath m_path; QVector m_cleanUpList; QStringList m_audioArgs; TUINT32 m_sampleRate; QString cleanPathSymbols(); - bool waitFfmpeg(const QProcess &ffmpeg); + bool waitFfmpeg(QProcess &ffmpeg, bool asyncProcess); }; +//=========================================================== +// +// TLevelReaderFFmpeg +// +//=========================================================== + +class TLevelReaderFFmpeg final : public TLevelReader { +public: + TLevelReaderFFmpeg(const TFilePath &path); + ~TLevelReaderFFmpeg(); + TImageReaderP getFrameReader(TFrameId fid) override; + + static TLevelReader *create(const TFilePath &f) { + return new TLevelReaderFFmpeg(f); + } + + TLevelP loadInfo() override; + TImageP load(int frameIndex); + TDimension getSize(); + +private: + Ffmpeg *ffmpegReader; + bool ffmpegFramesCreated = false; + TDimension m_size; + int m_frameCount, m_lx, m_ly; +}; + +//=========================================================================== + #endif diff --git a/toonz/sources/image/tiio.cpp b/toonz/sources/image/tiio.cpp index 1bbf33a..1bde568 100644 --- a/toonz/sources/image/tiio.cpp +++ b/toonz/sources/image/tiio.cpp @@ -2,6 +2,7 @@ #include "tnzimage.h" #include "tiio.h" #include "tfiletype.h" +#include "thirdparty.h" //------------------------------------------------------------------- @@ -79,6 +80,8 @@ #include "./ffmpeg/tiio_gif.h" #include "./ffmpeg/tiio_webm.h" #include "./ffmpeg/tiio_mp4.h" +#include "./ffmpeg/tiio_apng.h" +#include "./ffmpeg/tiio_ff_mov.h" #include "./mesh/tiio_mesh.h" #include "./sprite/tiio_sprite.h" #include "./exr/tiio_exr.h" @@ -188,26 +191,47 @@ void initImageIo(bool lightVersion) { // ffmpeg #if !defined(_WIN32) || defined(x64) || (defined(_WIN32) && defined(__GNUC__)) - if (Ffmpeg::checkFfmpeg()) { - bool ffprobe = Ffmpeg::checkFfprobe(); + if (ThirdParty::checkFFmpeg()) { if (Ffmpeg::checkFormat("webm")) { TLevelWriter::define("webm", TLevelWriterWebm::create, true); - if (ffprobe) TLevelReader::define("webm", TLevelReaderWebm::create); + TLevelReader::define("webm", TLevelReaderWebm::create); TFileType::declare("webm", TFileType::RASTER_LEVEL); Tiio::defineWriterProperties("webm", new Tiio::WebmWriterProperties()); } if (Ffmpeg::checkFormat("gif")) { TLevelWriter::define("gif", TLevelWriterGif::create, true); - if (ffprobe) TLevelReader::define("gif", TLevelReaderGif::create); + TLevelReader::define("gif", TLevelReaderGif::create); TFileType::declare("gif", TFileType::RASTER_LEVEL); Tiio::defineWriterProperties("gif", new Tiio::GifWriterProperties()); } if (Ffmpeg::checkFormat("mp4")) { TLevelWriter::define("mp4", TLevelWriterMp4::create, true); - if (ffprobe) TLevelReader::define("mp4", TLevelReaderMp4::create); + TLevelReader::define("mp4", TLevelReaderMp4::create); TFileType::declare("mp4", TFileType::RASTER_LEVEL); Tiio::defineWriterProperties("mp4", new Tiio::Mp4WriterProperties()); } + if (Ffmpeg::checkFormat("apng")) { + TLevelWriter::define("apng", TLevelWriterAPng::create, true); + TLevelReader::define("apng", TLevelReaderAPng::create); + TFileType::declare("apng", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("apng", new Tiio::APngWriterProperties()); + } + if (!IsQuickTimeInstalled()) { + if (Ffmpeg::checkFormat("mov")) { + TLevelWriter::define("mov", TLevelWriterFFMov::create, true); + TLevelReader::define("mov", TLevelReaderFFMov::create); + TFileType::declare("mov", TFileType::RASTER_LEVEL); + Tiio::defineWriterProperties("mov", new Tiio::FFMovWriterProperties()); + } + if (Ffmpeg::checkFormat("3gp")) { + TLevelReader::define("3gp", TLevelReaderFFmpeg::create); + TFileType::declare("3gp", TFileType::RASTER_LEVEL); + } + } + TLevelReader::define("webp", TLevelReaderFFmpeg::create); + TFileType::declare("webp", TFileType::RASTER_LEVEL); + TLevelReader::define("ffvideo", TLevelReaderFFmpeg::create); + TFileType::declare("ffvideo", TFileType::RASTER_LEVEL); } #endif // end ffmpeg diff --git a/toonz/sources/include/thirdparty.h b/toonz/sources/include/thirdparty.h new file mode 100644 index 0000000..eac7382 --- /dev/null +++ b/toonz/sources/include/thirdparty.h @@ -0,0 +1,71 @@ +#pragma once + +#ifndef THIRDPARTY_INCLUDED +#define THIRDPARTY_INCLUDED + +#include "tcommon.h" + +#include +#include +#include + +#undef DVAPI +#ifdef TOONZLIB_EXPORTS +#define DVAPI DV_EXPORT_API +#else +#define DVAPI DV_IMPORT_API +#endif + +namespace ThirdParty { + +//----------------------------------------------------------------------------- + +DVAPI void initialize(); + +//----------------------------------------------------------------------------- + +DVAPI void getFFmpegVideoSupported(QStringList &exts); +DVAPI void getFFmpegAudioSupported(QStringList &exts); + +DVAPI bool findFFmpeg(QString dir); +DVAPI bool checkFFmpeg(); +DVAPI QString autodetectFFmpeg(); + +DVAPI QString getFFmpegDir(); +DVAPI void setFFmpegDir(const QString &dir); +DVAPI int getFFmpegTimeout(); +DVAPI void setFFmpegTimeout(int secs); + +DVAPI void runFFmpeg(QProcess &process, const QStringList &arguments); +DVAPI void runFFprobe(QProcess &process, const QStringList &arguments); + +DVAPI void runFFmpegAudio(QProcess &process, QString srcPath, QString dstPath, + int samplerate = 44100, int bpp = 16, + int channels = 2); +DVAPI bool readFFmpegAudio(QProcess &process, QByteArray &rawData); + +//----------------------------------------------------------------------------- + +DVAPI bool findRhubarb(QString dir); +DVAPI bool checkRhubarb(); +DVAPI QString autodetectRhubarb(); + +DVAPI QString getRhubarbDir(); +DVAPI void setRhubarbDir(const QString &dir); +DVAPI int getRhubarbTimeout(); +DVAPI void setRhubarbTimeout(int secs); + +DVAPI void runRhubarb(QProcess &process, const QStringList &arguments); + +//----------------------------------------------------------------------------- + +// return 0 = No error +// return -1 = error code +// return -2 = timed out +DVAPI int waitAsyncProcess(const QProcess &process, int timeout); + +//----------------------------------------------------------------------------- + +} // namespace ThirdParty + +#endif diff --git a/toonz/sources/include/tlevel_io.h b/toonz/sources/include/tlevel_io.h index ce9c3b6..230188e 100644 --- a/toonz/sources/include/tlevel_io.h +++ b/toonz/sources/include/tlevel_io.h @@ -233,7 +233,7 @@ public: inline bool isMovieType(std::string type) { return (type == "mov" || type == "avi" || type == "3gp" || type == "webm" || - type == "mp4"); + type == "mp4" || type == "apng"); } //----------------------------------------------------------- @@ -274,7 +274,7 @@ inline bool isSequencialRequired(const TFilePath &fp) { inline bool isMultipleFrameType(std::string type) { return (type == "tlv" || type == "tzl" || type == "pli" || type == "mov" || type == "avi" || type == "3gp" || type == "gif" || type == "mp4" || - type == "webm"); + type == "webm" || type == "apng"); } //----------------------------------------------------------- diff --git a/toonz/sources/include/toonz/tproject.h b/toonz/sources/include/toonz/tproject.h index 7683018..109111d 100644 --- a/toonz/sources/include/toonz/tproject.h +++ b/toonz/sources/include/toonz/tproject.h @@ -140,6 +140,7 @@ public: void saveTemplate(ToonzScene *scene); + void clearProjectsRoot(); void addProjectsRoot(const TFilePath &fp); void addSVNProjectsRoot(const TFilePath &fp); diff --git a/toonz/sources/include/toonz/txshcolumn.h b/toonz/sources/include/toonz/txshcolumn.h index 480ed33..3f75975 100644 --- a/toonz/sources/include/toonz/txshcolumn.h +++ b/toonz/sources/include/toonz/txshcolumn.h @@ -130,6 +130,9 @@ Constructs a TXshColumn with default value. //! Returns the column type used to store levels of the specified type. static ColumnType toColumnType(int levelType); + //! Returns true if the column can be parent of another. + bool canBeParent() const; + //! Creates an empty TXshColumn of the specified column type. static TXshColumn *createEmpty(int colType); diff --git a/toonz/sources/sound/CMakeLists.txt b/toonz/sources/sound/CMakeLists.txt index f2a5cec..09a4a1f 100644 --- a/toonz/sources/sound/CMakeLists.txt +++ b/toonz/sources/sound/CMakeLists.txt @@ -2,9 +2,8 @@ set(HEADERS wav/tsio_wav.h aiff/tsio_aiff.h raw/tsio_raw.h - mp3/tsio_mp3.h + ffmpeg/tsio_ffmpeg.h ../include/tnzsound.h - tsio.h ) set(SOURCES @@ -13,7 +12,7 @@ set(SOURCES wav/tsio_wav.cpp aiff/tsio_aiff.cpp raw/tsio_raw.cpp - mp3/tsio_mp3.cpp + ffmpeg/tsio_ffmpeg.cpp ) add_library(sound SHARED ${HEADERS} ${SOURCES}) diff --git a/toonz/sources/sound/ffmpeg/tsio_ffmpeg.cpp b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.cpp new file mode 100644 index 0000000..98c0257 --- /dev/null +++ b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.cpp @@ -0,0 +1,35 @@ +#include + +#include "tmachine.h" +#include "tsio_ffmpeg.h" +#include "tsystem.h" +#include "tfilepath_io.h" +#include "tsound_t.h" +#include "toonz/preferences.h" +#include "toonz/toonzfolders.h" +#include "thirdparty.h" + +#include +#include + +//============================================================================== + +TSoundTrackReaderFFmpeg::TSoundTrackReaderFFmpeg(const TFilePath &fp) + : TSoundTrackReader(fp) {} + +//------------------------------------------------------------------------------ + +TSoundTrackP TSoundTrackReaderFFmpeg::load() { + QProcess ffmpeg; + QByteArray rawAudio; + + // Pipe the audio through ffmpeg + ThirdParty::runFFmpegAudio(ffmpeg, m_path.getQString(), "-"); + if (!ThirdParty::readFFmpegAudio(ffmpeg, rawAudio)) return nullptr; + + long sampleCount = rawAudio.size() / 4; + + TSoundTrack *track = new TSoundTrackStereo16(44100, 2, sampleCount); + memcpy((char *)track->getRawData(), rawAudio.constData(), sampleCount * 4); + return track; +} diff --git a/toonz/sources/sound/ffmpeg/tsio_ffmpeg.h b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.h new file mode 100644 index 0000000..21bc139 --- /dev/null +++ b/toonz/sources/sound/ffmpeg/tsio_ffmpeg.h @@ -0,0 +1,31 @@ +#pragma once + +#ifndef TSIO_FFMPEG_INCLUDED +#define TSIO_FFMPEG_INCLUDED + +#include "tsound_io.h" + +//========================================================== +/*! +The class TSoundTrackReaderFFmpeg reads audio files +*/ +class TSoundTrackReaderFFmpeg final : public TSoundTrackReader { +public: + TSoundTrackReaderFFmpeg(const TFilePath &fp); + ~TSoundTrackReaderFFmpeg() {} + + /*! +Loads audio file whose path has been specified in the constructor. +It returns a TSoundTrackP created from the audio file +*/ + TSoundTrackP load() override; + + /*! +Returns a soundtrack reader able to read audio files +*/ + static TSoundTrackReader *create(const TFilePath &fp) { + return new TSoundTrackReaderFFmpeg(fp); + } +}; + +#endif diff --git a/toonz/sources/sound/mp3/tsio_mp3.cpp b/toonz/sources/sound/mp3/tsio_mp3.cpp index cba3cef..4c78274 100644 --- a/toonz/sources/sound/mp3/tsio_mp3.cpp +++ b/toonz/sources/sound/mp3/tsio_mp3.cpp @@ -7,6 +7,8 @@ #include "tsound_t.h" #include "toonz/preferences.h" #include "toonz/toonzfolders.h" +#include "thirdparty.h" + #include #include @@ -36,28 +38,6 @@ TSoundTrackP TSoundTrackReaderMp3::load() { return track; } -bool FfmpegAudio::checkFfmpeg() { - // check the user defined path in preferences first - QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // check the OpenToonz root directory next - path = QDir::currentPath() + "/ffmpeg"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue(ffmpegPath, QDir::currentPath()); - return true; - } - - // give up - return false; -} - TFilePath FfmpegAudio::getFfmpegCache() { QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString(); if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/ffmpeg"))) { @@ -70,10 +50,8 @@ TFilePath FfmpegAudio::getFfmpegCache() { void FfmpegAudio::runFfmpeg(QStringList args) { // write the file - QString m_ffmpegPath = Preferences::instance()->getFfmpegPath(); - std::string strFfmpegPath = m_ffmpegPath.toStdString(); QProcess ffmpeg; - ffmpeg.start(m_ffmpegPath + "/ffmpeg", args); + ThirdParty::runFFmpeg(ffmpeg, args); ffmpeg.waitForFinished(30000); QString results = ffmpeg.readAllStandardError(); results += ffmpeg.readAllStandardOutput(); @@ -100,4 +78,4 @@ TFilePath FfmpegAudio::getRawAudio(TFilePath path) { args << outPath.getQString(); runFfmpeg(args); return outPath; -} \ No newline at end of file +} diff --git a/toonz/sources/sound/mp3/tsio_mp3.h b/toonz/sources/sound/mp3/tsio_mp3.h index 60797c6..a5d0a47 100644 --- a/toonz/sources/sound/mp3/tsio_mp3.h +++ b/toonz/sources/sound/mp3/tsio_mp3.h @@ -32,7 +32,6 @@ Returns a soundtrack reader able to read .mp3 audio files class FfmpegAudio { public: TFilePath getRawAudio(TFilePath path); - static bool checkFfmpeg(); private: TFilePath getFfmpegCache(); diff --git a/toonz/sources/sound/tsio.cpp b/toonz/sources/sound/tsio.cpp index 0625e6d..a6b8093 100644 --- a/toonz/sources/sound/tsio.cpp +++ b/toonz/sources/sound/tsio.cpp @@ -1,10 +1,15 @@ #include "tnzsound.h" -#include "tsio.h" // #include "tpluginmanager.h" #include "tsound_io.h" #include "tfiletype.h" +#include "thirdparty.h" + +#include "wav/tsio_wav.h" +#include "aiff/tsio_aiff.h" +#include "raw/tsio_raw.h" +#include "ffmpeg/tsio_ffmpeg.h" // static TPluginInfo info("soundIOPlugin"); @@ -25,17 +30,19 @@ void initSoundIo() { TSoundTrackWriter::define("raw", TSoundTrackWriterRaw::create); TFileType::declare("raw", TFileType::AUDIO_LEVEL); - if (FfmpegAudio::checkFfmpeg()) { - TSoundTrackReader::define("mp3", TSoundTrackReaderMp3::create); - // TSoundTrackWriter::define("mp3", TSoundTrackWriterMp3::create); + if (ThirdParty::checkFFmpeg()) { + TSoundTrackReader::define("mp3", TSoundTrackReaderFFmpeg::create); TFileType::declare("mp3", TFileType::AUDIO_LEVEL); - - // TSoundTrackReaderMp3 is so generic that will work for other ffmpeg - // audio formats, nice :D - TSoundTrackReader::define("ogg", TSoundTrackReaderMp3::create); + TSoundTrackReader::define("ogg", TSoundTrackReaderFFmpeg::create); TFileType::declare("ogg", TFileType::AUDIO_LEVEL); - TSoundTrackReader::define("flac", TSoundTrackReaderMp3::create); + TSoundTrackReader::define("flac", TSoundTrackReaderFFmpeg::create); TFileType::declare("flac", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("m4a", TSoundTrackReaderFFmpeg::create); + TFileType::declare("m4a", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("aac", TSoundTrackReaderFFmpeg::create); + TFileType::declare("aac", TFileType::AUDIO_LEVEL); + TSoundTrackReader::define("ffaudio", TSoundTrackReaderFFmpeg::create); + TFileType::declare("ffaudio", TFileType::AUDIO_LEVEL); } // return &info; } diff --git a/toonz/sources/sound/tsio.h b/toonz/sources/sound/tsio.h deleted file mode 100644 index 4c35aa4..0000000 --- a/toonz/sources/sound/tsio.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#ifndef TSIO_INCLUDED -#define TSIO_INCLUDED - -#include "wav/tsio_wav.h" -#include "aiff/tsio_aiff.h" -#include "raw/tsio_raw.h" -#include "mp3/tsio_mp3.h" - -#endif diff --git a/toonz/sources/toonz/autolipsyncpopup.cpp b/toonz/sources/toonz/autolipsyncpopup.cpp index f9f0c90..de96639 100644 --- a/toonz/sources/toonz/autolipsyncpopup.cpp +++ b/toonz/sources/toonz/autolipsyncpopup.cpp @@ -23,6 +23,7 @@ #include "tsound_io.h" #include "toutputproperties.h" #include "toonz/tproject.h" +#include "thirdparty.h" // TnzCore includes #include "filebrowsermodel.h" @@ -457,7 +458,7 @@ void AutoLipSyncPopup::showEvent(QShowEvent *) { m_startAt->setValue(row + 1); m_startAt->clearFocus(); - if (checkRhubarb()) m_rhubarbPath = Preferences::instance()->getRhubarbPath(); + if (ThirdParty::checkRhubarb()) m_rhubarbPath = ThirdParty::getRhubarbDir(); TXshLevelHandle *level = app->getCurrentLevel(); m_sl = level->getSimpleLevel(); @@ -672,32 +673,6 @@ void AutoLipSyncPopup::saveAudio() { //----------------------------------------------------------------------------- -bool AutoLipSyncPopup::checkRhubarb() { - QString exe = "rhubarb"; -#if defined(_WIN32) - exe = exe + ".exe"; -#endif - - // check the user defined path in preferences first - QString path = Preferences::instance()->getRhubarbPath() + "/" + exe; - if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true; - - // check the OpenToonz root directory next - path = QDir::currentPath() + "/rhubarb"; -#if defined(_WIN32) - path = path + ".exe"; -#endif - if (TSystem::doesExistFileOrLevel(TFilePath(path))) { - Preferences::instance()->setValue(rhubarbPath, QDir::currentPath()); - return true; - } - - // give up - return false; -} - -//----------------------------------------------------------------------------- - void AutoLipSyncPopup::runRhubarb() { QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString(); if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/rhubarb"))) { @@ -739,11 +714,7 @@ void AutoLipSyncPopup::runRhubarb() { connect(m_rhubarb, &QProcess::readyReadStandardError, this, &AutoLipSyncPopup::onOutputReady); - QString rhubarbExe = m_rhubarbPath + "/rhubarb"; -#ifdef _WIN32 - rhubarbExe = rhubarbExe + ".exe"; -#endif - m_rhubarb->start(rhubarbExe, args); + ThirdParty::runRhubarb(*m_rhubarb, args); } //----------------------------------------------------------------------------- @@ -812,7 +783,7 @@ void AutoLipSyncPopup::onApplyButton() { } runRhubarb(); - int rhubarbTimeout = Preferences::instance()->getRhubarbTimeout(); + int rhubarbTimeout = ThirdParty::getRhubarbTimeout(); if (rhubarbTimeout > 0) rhubarbTimeout *= 1000; else diff --git a/toonz/sources/toonz/autolipsyncpopup.h b/toonz/sources/toonz/autolipsyncpopup.h index bee983f..f3ed0bb 100644 --- a/toonz/sources/toonz/autolipsyncpopup.h +++ b/toonz/sources/toonz/autolipsyncpopup.h @@ -93,7 +93,6 @@ protected: void refreshSoundLevels(); void saveAudio(); void runRhubarb(); - bool checkRhubarb(); public slots: void onApplyButton(); diff --git a/toonz/sources/toonz/formatsettingspopups.cpp b/toonz/sources/toonz/formatsettingspopups.cpp index 4838380..fa735fd 100644 --- a/toonz/sources/toonz/formatsettingspopups.cpp +++ b/toonz/sources/toonz/formatsettingspopups.cpp @@ -23,6 +23,7 @@ #include "movsettings.h" #include "timageinfo.h" #include "tfiletype.h" +#include "tiio.h" // Qt includes #include @@ -403,9 +404,11 @@ void FormatSettingsPopup::onPaddingCBChanged() { bool openFormatSettingsPopup(QWidget *parent, const std::string &format, TPropertyGroup *props, TFrameId *tmplFId, bool forInput, const TFilePath &levelPath) { - if (format == "mov" || format == "3gp") // trattato diversamente; il format - // popup dei mov e' quello di - // quicktime + bool quicktime = + (format == "mov" || format == "3gp") && Tiio::isQuicktimeInstalled(); + if (quicktime) // trattato diversamente; il format + // popup dei mov e' quello di + // quicktime { // gmt 26/9/07. con la penna capita spesso di premere ripetutamente il // bottone. diff --git a/toonz/sources/toonz/icons/dark/actions/16/save_and_render.svg b/toonz/sources/toonz/icons/dark/actions/16/save_and_render.svg new file mode 100644 index 0000000..9993dd2 --- /dev/null +++ b/toonz/sources/toonz/icons/dark/actions/16/save_and_render.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/toonz/sources/toonz/main.cpp b/toonz/sources/toonz/main.cpp index 1e941d3..cad4d65 100644 --- a/toonz/sources/toonz/main.cpp +++ b/toonz/sources/toonz/main.cpp @@ -10,6 +10,7 @@ #include "cleanupsettingspopup.h" #include "filebrowsermodel.h" #include "expressionreferencemanager.h" +#include "thirdparty.h" // TnzTools includes #include "tools/tool.h" @@ -495,6 +496,9 @@ int main(int argc, char *argv[]) { // Toonz environment initToonzEnv(argumentPathValues); + // Setup third party + ThirdParty::initialize(); + // prepare for 30bit display if (Preferences::instance()->is30bitDisplayEnabled()) { QSurfaceFormat sFmt = QSurfaceFormat::defaultFormat(); diff --git a/toonz/sources/toonz/mainwindow.cpp b/toonz/sources/toonz/mainwindow.cpp index ba2bda2..146b819 100644 --- a/toonz/sources/toonz/mainwindow.cpp +++ b/toonz/sources/toonz/mainwindow.cpp @@ -1035,6 +1035,17 @@ void MainWindow::autofillToggle() { void MainWindow::resetRoomsLayout() { if (!m_saveSettingsOnQuit) return; + QString message(tr("Reset rooms to their default?")); + message += "\n" + tr("All user rooms will be lost!"); + + QMessageBox::StandardButton ret = QMessageBox::question( + this, tr("Reset Rooms"), message, + QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No)); + + if (ret != QMessageBox::Yes) return; + + // Reflect changes to initwizard.cpp: void UIPage::resetRoom() + m_saveSettingsOnQuit = false; TFilePath layoutDir = ToonzFolder::getMyRoomsDir(); @@ -1058,7 +1069,13 @@ void MainWindow::resetRoomsLayout() { }*/ DVGui::info( - QObject::tr("The rooms will be reset the next time you run Toonz.")); + QObject::tr("The rooms will be reset the next time you run OpenToonz.")); + + ret = QMessageBox::question( + this, tr("Reset Rooms"), tr("You must restart OpenToonz, close it now?"), + QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No)); + + if (ret == QMessageBox::Yes) close(); } void MainWindow::maximizePanel() { diff --git a/toonz/sources/toonz/toonz.qrc b/toonz/sources/toonz/toonz.qrc index 57e7d66..60f901a 100644 --- a/toonz/sources/toonz/toonz.qrc +++ b/toonz/sources/toonz/toonz.qrc @@ -166,11 +166,12 @@ icons/dark/mimetypes/60/svg_icon.svg icons/dark/mimetypes/60/audio_icon.svg icons/dark/mimetypes/60/script_icon.svg - icons/dark/mimetypes/60/broken_icon.svg - icons/dark/mimetypes/60/json_icon.svg + icons/dark/mimetypes/60/broken_icon.svg + icons/dark/mimetypes/60/json_icon.svg icons/dark/actions/16/render.svg + icons/dark/actions/16/save_and_render.svg icons/dark/actions/16/render_clapboard.svg icons/dark/actions/16/render_add.svg icons/dark/actions/16/output_settings.svg @@ -215,8 +216,8 @@ icons/dark/actions/16/nextstep.svg icons/dark/actions/16/prevstep.svg icons/dark/actions/16/nextkey.svg - icons/dark/actions/16/prevkey.svg - icons/dark/actions/16/blankframes.svg + icons/dark/actions/16/prevkey.svg + icons/dark/actions/16/blankframes.svg icons/dark/actions/16/snapshot.svg icons/dark/actions/16/compare.svg @@ -520,7 +521,7 @@ icons/dark/actions/48/colorchiporder_lowleft.svg icons/dark/actions/48/colorchiporder_upleft.svg icons/dark/misc/startup.png - icons/dark/actions/20/touch.svg + icons/dark/actions/20/touch.svg icons/dark/actions/16/quit.svg icons/dark/actions/16/clear_cache.svg icons/dark/actions/16/dialogue.svg @@ -534,7 +535,7 @@ icons/dark/actions/16/tool_options.svg icons/dark/actions/16/view_file.svg icons/dark/actions/16/level_strip.svg - icons/dark/actions/17/syncscale.svg + icons/dark/actions/17/syncscale.svg icons/dark/actions/20/key_off.svg diff --git a/toonz/sources/toonz/xshcellviewer.cpp b/toonz/sources/toonz/xshcellviewer.cpp index 2eb4a5a..b2d39f9 100644 --- a/toonz/sources/toonz/xshcellviewer.cpp +++ b/toonz/sources/toonz/xshcellviewer.cpp @@ -2743,9 +2743,9 @@ void CellArea::drawPaletteCell(QPainter &p, int row, int col, : m_viewer->getReferenceColumnColor(); sideColor = m_viewer->getReferenceColumnBorderColor(); } else { - cellColor = (isSelected) ? m_viewer->getSelectedPaletteColumnColor() - : m_viewer->getPaletteColumnColor(); - sideColor = m_viewer->getPaletteColumnBorderColor(); + int levelType; + m_viewer->getCellTypeAndColors(levelType, cellColor, sideColor, cell, + isSelected); } // paint cell diff --git a/toonz/sources/toonz/xshcolumnviewer.cpp b/toonz/sources/toonz/xshcolumnviewer.cpp index c1216d1..fe804bf 100644 --- a/toonz/sources/toonz/xshcolumnviewer.cpp +++ b/toonz/sources/toonz/xshcolumnviewer.cpp @@ -475,14 +475,13 @@ void ChangeObjectParent::refresh() { else if (id.isCamera()) { bool isActive = (id == xsh->getStageObjectTree()->getCurrentCameraId()); newTextBG = isActive ? viewer->getActiveCameraColor() - : viewer->getOtherCameraColor(); + : viewer->getOtherCameraColor(); } else if (id.isColumn() && (!xsh->isColumnEmpty(index))) { TXshColumn *colx = xsh->getColumn(index); - if (colx->getColumnType() != TXshColumn::eSoundTextType && - colx->getColumnType() != TXshColumn::eSoundType) { - QColor unused; - viewer->getColumnColor(newTextBG, unused, id.getIndex(), xsh); - } + if (!colx->canBeParent()) continue; + + QColor unused; + viewer->getColumnColor(newTextBG, unused, id.getIndex(), xsh); } else continue; @@ -797,8 +796,8 @@ void ColumnArea::DrawHeader::levelColors(QColor &columnColor, cameraId.getIndex() == m_viewer->getXsheet()->getCameraColumnIndex(); columnColor = isActive ? m_viewer->getActiveCameraColor() : m_viewer->getOtherCameraColor(); - dragColor = isActive ? m_viewer->getActiveCameraColor() - : m_viewer->getOtherCameraColor(); + dragColor = isActive ? m_viewer->getActiveCameraColor() + : m_viewer->getOtherCameraColor(); return; } enum { Normal, Reference, Control } usage = Reference; @@ -934,10 +933,9 @@ void ColumnArea::DrawHeader::drawPreviewToggle(int opacity) const { // camstand visible toggle QColor bgColor; QImage icon; - int buttonType = - !column->isCamstandVisible() - ? CAMSTAND_OFF_XSHBUTTON - : opacity < 255 ? CAMSTAND_TRANSP_XSHBUTTON : CAMSTAND_ON_XSHBUTTON; + int buttonType = !column->isCamstandVisible() ? CAMSTAND_OFF_XSHBUTTON + : opacity < 255 ? CAMSTAND_TRANSP_XSHBUTTON + : CAMSTAND_ON_XSHBUTTON; m_viewer->getButton(buttonType, bgColor, icon, !o->isVerticalTimeline()); QRect tableViewRect = @@ -1119,8 +1117,9 @@ void ColumnArea::DrawHeader::drawColumnName() const { leftadj = 24; } - p.setPen((isCurrent) ? m_viewer->getSelectedColumnTextColor() - : nameBacklit ? Qt::black : m_viewer->getTextColor()); + p.setPen((isCurrent) ? m_viewer->getSelectedColumnTextColor() + : nameBacklit ? Qt::black + : m_viewer->getTextColor()); } else p.setPen((isCurrent) ? m_viewer->getSelectedColumnTextColor() : m_viewer->getTextColor()); @@ -1258,8 +1257,7 @@ void ColumnArea::DrawHeader::drawPegbarName() const { p.setPen(m_viewer->getVerticalLineColor()); if (o->flag(PredefinedFlag::PEGBAR_NAME_BORDER)) p.drawRect(pegbarnamerect); - if (column->getSoundColumn() || column->getSoundTextColumn() || - column->getPaletteColumn()) + if (column->getSoundColumn() || column->getSoundTextColumn()) return; if (Preferences::instance()->isParentColorsInXsheetColumnEnabled() && @@ -1295,8 +1293,7 @@ void ColumnArea::DrawHeader::drawPegbarName() const { void ColumnArea::DrawHeader::drawParentHandleName() const { if (col < 0 || isEmpty || !o->flag(PredefinedFlag::PARENT_HANDLE_NAME_VISIBILE) || - column->getSoundColumn() || column->getSoundTextColumn() || - column->getPaletteColumn()) + column->getSoundColumn() || column->getSoundTextColumn()) return; QRect parenthandleRect = diff --git a/toonz/sources/toonz/xsheetviewer.cpp b/toonz/sources/toonz/xsheetviewer.cpp index aa4a0a8..f4ee0d4 100644 --- a/toonz/sources/toonz/xsheetviewer.cpp +++ b/toonz/sources/toonz/xsheetviewer.cpp @@ -86,6 +86,11 @@ void XsheetViewer::getCellTypeAndColors(int <ype, QColor &cellColor, (isSelected) ? getSelectedLevelColumnColor() : getLevelColumnColor(); sideColor = getLevelColumnBorderColor(); break; + case PLT_XSHLEVEL: + cellColor = (isSelected) ? getSelectedPaletteColumnColor() + : getPaletteColumnColor(); + sideColor = getPaletteColumnBorderColor(); + break; case ZERARYFX_XSHLEVEL: cellColor = (isSelected) ? getSelectedFxColumnColor() : getFxColumnColor(); @@ -148,50 +153,50 @@ void XsheetViewer::getButton(const int &btype, QColor &bgColor, QImage &iconImage, bool isTimeline) { switch (btype) { case PREVIEW_ON_XSHBUTTON: - bgColor = (isTimeline) ? getTimelinePreviewButtonBgOnColor() - : getXsheetPreviewButtonBgOnColor(); + bgColor = (isTimeline) ? getTimelinePreviewButtonBgOnColor() + : getXsheetPreviewButtonBgOnColor(); iconImage = (isTimeline) ? getTimelinePreviewButtonOnImage() : getXsheetPreviewButtonOnImage(); break; case PREVIEW_OFF_XSHBUTTON: - bgColor = (isTimeline) ? getTimelinePreviewButtonBgOffColor() - : getXsheetPreviewButtonBgOffColor(); + bgColor = (isTimeline) ? getTimelinePreviewButtonBgOffColor() + : getXsheetPreviewButtonBgOffColor(); iconImage = (isTimeline) ? getTimelinePreviewButtonOffImage() : getXsheetPreviewButtonOffImage(); break; case CAMSTAND_ON_XSHBUTTON: - bgColor = (isTimeline) ? getTimelineCamstandButtonBgOnColor() - : getXsheetCamstandButtonBgOnColor(); + bgColor = (isTimeline) ? getTimelineCamstandButtonBgOnColor() + : getXsheetCamstandButtonBgOnColor(); iconImage = (isTimeline) ? getTimelineCamstandButtonOnImage() : getXsheetCamstandButtonOnImage(); break; case CAMSTAND_TRANSP_XSHBUTTON: - bgColor = (isTimeline) ? getTimelineCamstandButtonBgOnColor() - : getXsheetCamstandButtonBgOnColor(); + bgColor = (isTimeline) ? getTimelineCamstandButtonBgOnColor() + : getXsheetCamstandButtonBgOnColor(); iconImage = (isTimeline) ? getTimelineCamstandButtonTranspImage() : getXsheetCamstandButtonTranspImage(); break; case CAMSTAND_OFF_XSHBUTTON: - bgColor = (isTimeline) ? getTimelineCamstandButtonBgOffColor() - : getXsheetCamstandButtonBgOffColor(); + bgColor = (isTimeline) ? getTimelineCamstandButtonBgOffColor() + : getXsheetCamstandButtonBgOffColor(); iconImage = (isTimeline) ? getTimelineCamstandButtonOffImage() : getXsheetCamstandButtonOffImage(); break; case LOCK_ON_XSHBUTTON: - bgColor = (isTimeline) ? getTimelineLockButtonBgOnColor() - : getXsheetLockButtonBgOnColor(); + bgColor = (isTimeline) ? getTimelineLockButtonBgOnColor() + : getXsheetLockButtonBgOnColor(); iconImage = (isTimeline) ? getTimelineLockButtonOnImage() : getXsheetLockButtonOnImage(); break; case LOCK_OFF_XSHBUTTON: - bgColor = (isTimeline) ? getTimelineLockButtonBgOffColor() - : getXsheetLockButtonBgOffColor(); + bgColor = (isTimeline) ? getTimelineLockButtonBgOffColor() + : getXsheetLockButtonBgOffColor(); iconImage = (isTimeline) ? getTimelineLockButtonOffImage() : getXsheetLockButtonOffImage(); break; case CONFIG_XSHBUTTON: - bgColor = (isTimeline) ? getTimelineConfigButtonBgColor() - : getXsheetConfigButtonBgColor(); + bgColor = (isTimeline) ? getTimelineConfigButtonBgColor() + : getXsheetConfigButtonBgColor(); iconImage = (isTimeline) ? getTimelineConfigButtonImage() : getXsheetConfigButtonImage(); break; @@ -1823,9 +1828,9 @@ QList XsheetViewer::availableFramesPerPage() { ret << frameRate * 6; // visible area size - int size = (orientation()->isVerticalTimeline()) - ? m_cellScrollArea->viewport()->height() - : m_cellScrollArea->viewport()->width(); + int size = (orientation()->isVerticalTimeline()) + ? m_cellScrollArea->viewport()->height() + : m_cellScrollArea->viewport()->width(); int scaleMin = (orientation()->isVerticalTimeline()) ? 50 : 20; int scaleMax = 100; @@ -1852,9 +1857,9 @@ QList XsheetViewer::availableFramesPerPage() { //---------------------------------------------------------------- void XsheetViewer::zoomToFramesPerPage(int frames) { - int size = (orientation()->isVerticalTimeline()) - ? m_cellScrollArea->viewport()->height() - : m_cellScrollArea->viewport()->width(); + int size = (orientation()->isVerticalTimeline()) + ? m_cellScrollArea->viewport()->height() + : m_cellScrollArea->viewport()->width(); int frameDim = orientation()->dimension(PredefinedDimension::FRAME); double scale = (double)size / ((double)frameDim * (double)frames); diff --git a/toonz/sources/toonzlib/CMakeLists.txt b/toonz/sources/toonzlib/CMakeLists.txt index 692f8d0..d5b8b24 100644 --- a/toonz/sources/toonzlib/CMakeLists.txt +++ b/toonz/sources/toonzlib/CMakeLists.txt @@ -65,6 +65,7 @@ set(HEADERS sandor_fxs/patternmap.h sandor_fxs/toonz4_6staff.h ../include/convert2tlv.h + ../include/thirdparty.h ../include/orientation.h ../include/toonz/Naa2TlvConverter.h ../include/toonz/autoclose.h @@ -246,6 +247,7 @@ set(SOURCES tcolumnfxset.cpp tdistort.cpp texturemanager.cpp + thirdparty.cpp tlog.cpp tnewoutlinevectorize.cpp toonzfolders.cpp diff --git a/toonz/sources/toonzlib/levelupdater.cpp b/toonz/sources/toonzlib/levelupdater.cpp index 31df108..f18485f 100644 --- a/toonz/sources/toonzlib/levelupdater.cpp +++ b/toonz/sources/toonzlib/levelupdater.cpp @@ -244,7 +244,7 @@ void LevelUpdater::open(const TFilePath &fp, TPropertyGroup *pg, TLevelReaderP(); // Release the reader. This is necessary since the m_lw = TLevelWriterP( fp, m_pg->clone()); // original file itself will be MODIFIED. - m_lwPath = fp; + m_lwPath = m_lw->getFilePath(); } } catch (...) { // In this case, TLevelWriterP(..) failed, that object was never diff --git a/toonz/sources/toonzlib/movierenderer.cpp b/toonz/sources/toonzlib/movierenderer.cpp index 1ea8651..73308da 100644 --- a/toonz/sources/toonzlib/movierenderer.cpp +++ b/toonz/sources/toonzlib/movierenderer.cpp @@ -283,6 +283,7 @@ void MovieRenderer::Imp::prepareForStart() { m_fp, oprop->getFileFormatProperties(m_fp.getType()), oprop->formatTemplateFId())); m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate); + m_fp = m_levelUpdaterA->getLevelWriter()->getFilePath(); } else { TFilePath leftFp = m_fp.withName(m_fp.getName() + "_l"); TFilePath rightFp = m_fp.withName(m_fp.getName() + "_r"); @@ -294,11 +295,13 @@ void MovieRenderer::Imp::prepareForStart() { leftFp, oprop->getFileFormatProperties(leftFp.getType()), oprop->formatTemplateFId())); m_levelUpdaterA->getLevelWriter()->setFrameRate(frameRate); + leftFp = m_levelUpdaterA->getLevelWriter()->getFilePath(); m_levelUpdaterB.reset(new LevelUpdater( rightFp, oprop->getFileFormatProperties(rightFp.getType()), oprop->formatTemplateFId())); m_levelUpdaterB->getLevelWriter()->setFrameRate(frameRate); + rightFp = m_levelUpdaterB->getLevelWriter()->getFilePath(); } } catch (...) { // If we get here, it's because one of the LevelUpdaters could not be diff --git a/toonz/sources/toonzlib/thirdparty.cpp b/toonz/sources/toonzlib/thirdparty.cpp new file mode 100644 index 0000000..e5b5cae --- /dev/null +++ b/toonz/sources/toonzlib/thirdparty.cpp @@ -0,0 +1,306 @@ +#include "thirdparty.h" + +// TnzLib includes +#include "toonz/preferences.h" + +// TnzCore includes +#include "tsystem.h" +#include "tmsgcore.h" + +// QT includes +#include +#include +#include + +namespace ThirdParty { + +//----------------------------------------------------------------------------- + +void initialize() { + // Auto detect FFmpeg + if (!ThirdParty::checkFFmpeg()) { + QString path = ThirdParty::autodetectFFmpeg(); + if (!path.isEmpty()) ThirdParty::setFFmpegDir(path); + } + + // Auto detect Rhubarb + if (!ThirdParty::checkRhubarb()) { + QString path = ThirdParty::autodetectRhubarb(); + if (!path.isEmpty()) ThirdParty::setRhubarbDir(path); + } +} + +//============================================================================= +// FFmpeg interface +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define FFMPEG_EXE "/ffmpeg.exe" +#define FFPROBE_EXE "/ffprobe.exe" +#else +#define FFMPEG_EXE "/ffmpeg" +#define FFPROBE_EXE "/ffprobe" +#endif + +//----------------------------------------------------------------------------- + +void getFFmpegVideoSupported(QStringList &exts) { + exts.append("gif"); + exts.append("mp4"); + exts.append("webm"); +} + +//----------------------------------------------------------------------------- + +void getFFmpegAudioSupported(QStringList &exts) { + exts.append("mp3"); + exts.append("ogg"); + exts.append("flac"); +} + +//----------------------------------------------------------------------------- + +bool findFFmpeg(QString dir) { + // Relative path + if (dir.isEmpty() || dir.at(0) == ".") { + dir = QCoreApplication::applicationDirPath() + "/" + dir; + } + + // Check if both executables exist + return TSystem::doesExistFileOrLevel(TFilePath(dir + FFMPEG_EXE)) && + TSystem::doesExistFileOrLevel(TFilePath(dir + FFPROBE_EXE)); +} + +//----------------------------------------------------------------------------- + +bool checkFFmpeg() { + // Path in preferences + return findFFmpeg(Preferences::instance()->getFfmpegPath()); +} + +//----------------------------------------------------------------------------- + +QString autodetectFFmpeg() { + QString dir = Preferences::instance()->getFfmpegPath(); + if (findFFmpeg(dir)) return dir; + + if (findFFmpeg(".")) return "."; + if (findFFmpeg("./ffmpeg")) return "./ffmpeg"; + if (findFFmpeg("./ffmpeg/bin")) return "./ffmpeg/bin"; + if (findFFmpeg("./FFmpeg")) return "./FFmpeg"; + if (findFFmpeg("./FFmpeg/bin")) return "./FFmpeg/bin"; + +#ifndef _WIN32 + if (findFFmpeg("/usr/local/bin")) return "/usr/local/bin"; + if (findFFmpeg("/usr/bin")) return "/usr/bin"; + if (findFFmpeg("/bin")) return "/bin"; +#endif + + return ""; +} + +//----------------------------------------------------------------------------- + +QString getFFmpegDir() { + return Preferences::instance()->getStringValue(ffmpegPath); +} + +//----------------------------------------------------------------------------- + +void setFFmpegDir(const QString &dir) { + QString path = Preferences::instance()->getFfmpegPath(); + if (path != dir) Preferences::instance()->setValue(ffmpegPath, dir); +} + +//----------------------------------------------------------------------------- + +int getFFmpegTimeout() { + return Preferences::instance()->getIntValue(ffmpegTimeout); +} + +//----------------------------------------------------------------------------- + +void setFFmpegTimeout(int secs) { + int timeout = Preferences::instance()->getIntValue(ffmpegTimeout); + if (secs != timeout) Preferences::instance()->setValue(ffmpegTimeout, secs); +} + +//----------------------------------------------------------------------------- + +void runFFmpeg(QProcess &process, const QStringList &arguments) { + QString dir = Preferences::instance()->getFfmpegPath(); + if (dir.at(0) == '.') // Relative path + dir = QCoreApplication::applicationDirPath() + "/" + dir; + + process.start(dir + FFMPEG_EXE, arguments); +} + +//----------------------------------------------------------------------------- + +void runFFprobe(QProcess &process, const QStringList &arguments) { + QString dir = Preferences::instance()->getFfmpegPath(); + if (dir.at(0) == '.') // Relative path + dir = QCoreApplication::applicationDirPath() + "/" + dir; + + process.start(dir + FFPROBE_EXE, arguments); +} + +//----------------------------------------------------------------------------- + +void runFFmpegAudio(QProcess &process, QString srcPath, QString dstPath, + int samplerate, int bpp, int channels) { + QStringList args; + args << "-y"; + args << "-i"; + args << srcPath; + args << "-f"; + switch (bpp) { + case 8: // unsigned + args << "u8"; + break; + case 16: + args << "s16le"; + break; + case 24: + args << "s24le"; + break; + case 32: // floating-point + args << "f32le"; + break; + default: + return; + } + args << "-ac"; + args << QString::number(channels); + args << "-ar"; + args << QString::number(samplerate); + args << dstPath; + + ThirdParty::runFFmpeg(process, args); +} + +//----------------------------------------------------------------------------- + +bool readFFmpegAudio(QProcess &process, QByteArray &rawData) { + if (!process.waitForStarted()) return false; + if (!process.waitForFinished(30000)) return false; + bool success = (process.exitCode() == 0); + + if (success) rawData = process.readAllStandardOutput(); + process.close(); + + return success; +} + +//============================================================================= +// Rhubarb interface +//----------------------------------------------------------------------------- + +#ifdef _WIN32 +#define RHUBARB_EXE "/rhubarb.exe" +#else +#define RHUBARB_EXE "/rhubarb" +#endif + +//----------------------------------------------------------------------------- + +bool findRhubarb(QString dir) { + // Rhubarb executable + + // Relative path + if (dir.isEmpty() || dir.at(0) == ".") { + dir = QCoreApplication::applicationDirPath() + "/" + dir; + } + + // Check if executable exist + return TSystem::doesExistFileOrLevel(TFilePath(dir + RHUBARB_EXE)); +} + +//----------------------------------------------------------------------------- + +bool checkRhubarb() { + // Path in preferences + return findRhubarb(Preferences::instance()->getRhubarbPath()); +} + +//----------------------------------------------------------------------------- + +QString autodetectRhubarb() { + QString dir = Preferences::instance()->getRhubarbPath(); + if (findRhubarb(dir)) return dir; + + if (findRhubarb(".")) return "."; + if (findRhubarb("./rhubarb")) return "./rhubarb"; + if (findRhubarb("./rhubarb/bin")) return "./rhubarb/bin"; + if (findRhubarb("./Rhubarb-Lip-Sync")) return "./Rhubarb-Lip-Sync"; + +#ifndef _WIN32 + if (findRhubarb("/usr/local/bin")) return "/usr/local/bin"; + if (findRhubarb("/usr/bin")) return "/usr/bin"; + if (findRhubarb("/bin")) return "/bin"; +#endif + + return ""; +} + +//----------------------------------------------------------------------------- + +void runRhubarb(QProcess &process, const QStringList &arguments) { + QString dir = Preferences::instance()->getRhubarbPath(); + if (dir.at(0) == '.') // Relative path + dir = QCoreApplication::applicationDirPath() + "/" + dir; + + process.start(dir + RHUBARB_EXE, arguments); +} + +//----------------------------------------------------------------------------- + +QString getRhubarbDir() { + return Preferences::instance()->getStringValue(rhubarbPath); +} + +//----------------------------------------------------------------------------- + +void setRhubarbDir(const QString &dir) { + QString path = Preferences::instance()->getRhubarbPath(); + if (path != dir) Preferences::instance()->setValue(rhubarbPath, dir); +} + +//----------------------------------------------------------------------------- + +int getRhubarbTimeout() { + return Preferences::instance()->getIntValue(rhubarbTimeout); +} + +//----------------------------------------------------------------------------- + +void setRhubarbTimeout(int secs) { + int timeout = Preferences ::instance()->getIntValue(rhubarbTimeout); + if (secs != timeout) Preferences::instance()->setValue(rhubarbTimeout, secs); +} + +//----------------------------------------------------------------------------- + +int waitAsyncProcess(const QProcess &process, int timeout) { + QEventLoop eloop; + QTimer timer; + timer.connect(&timer, &QTimer::timeout, &eloop, [&eloop] { eloop.exit(-2); }); + QMetaObject::Connection con1 = process.connect( + &process, &QProcess::errorOccurred, &eloop, [&eloop] { eloop.exit(-1); }); + QMetaObject::Connection con2 = process.connect( + &process, + static_cast( + &QProcess::finished), + &eloop, &QEventLoop::quit); + if (timeout >= 0) timer.start(timeout); + + int result = eloop.exec(); + process.disconnect(con1); + process.disconnect(con2); + + return result; +} + +//----------------------------------------------------------------------------- + +} // namespace ThirdParty diff --git a/toonz/sources/toonzlib/tproject.cpp b/toonz/sources/toonzlib/tproject.cpp index 9629974..95ea400 100644 --- a/toonz/sources/toonzlib/tproject.cpp +++ b/toonz/sources/toonzlib/tproject.cpp @@ -780,6 +780,10 @@ TProjectManager *TProjectManager::instance() { } //------------------------------------------------------------------- +// Clear all projecs roots container. +void TProjectManager::clearProjectsRoot() { m_projectsRoots.clear(); } + +//------------------------------------------------------------------- /*! Adds the specified folder \b fp in the projecs roots container.\n If \b fp is already contained in the container, the method does nothing. \note \b fp must be a folder and not a file path.*/ diff --git a/toonz/sources/toonzlib/txshcolumn.cpp b/toonz/sources/toonzlib/txshcolumn.cpp index df5dcad..fd4594e 100644 --- a/toonz/sources/toonzlib/txshcolumn.cpp +++ b/toonz/sources/toonzlib/txshcolumn.cpp @@ -541,6 +541,23 @@ TXshColumn::ColumnType TXshColumn::toColumnType(int levelType) { } //----------------------------------------------------------------------------- + +bool TXshColumn::canBeParent() const { + switch (getColumnType()) { + case eLevelType: + case eZeraryFxType: + case ePaletteType: + case eMeshType: + return true; + case eSoundType: + case eSoundTextType: + return false; + default: + assert(!"Unknown level type!"); + } +} + +//----------------------------------------------------------------------------- bool TXshColumn::isRendered() const { // if (!getXsheet() || !getFx()) return false; // if (!isPreviewVisible()) return false; diff --git a/toonz/sources/toonzqt/dvdialog.cpp b/toonz/sources/toonzqt/dvdialog.cpp index 3a33dcb..ee892a1 100644 --- a/toonz/sources/toonzqt/dvdialog.cpp +++ b/toonz/sources/toonzqt/dvdialog.cpp @@ -320,7 +320,7 @@ Dialog::Dialog(QWidget *parent, bool hasButton, bool hasFixedSize, if (x > screen.right() - 50) x = screen.right() - 50; if (x < screen.left()) x = screen.left(); if (y > screen.bottom() - 90) y = screen.bottom() - 90; - if (y < screen.top()) y = screen.top(); + if (y <= screen.top()) y = screen.top() + 50; // pad for window title setGeometry(x, y, values.at(2).toInt(), values.at(3).toInt()); settings.setValue(m_name, QString::number(x) + " " + QString::number(y) + " " + QString::number(values.at(2).toInt()) +