3rd party interface adjustments + more

This commit is contained in:
justburner 2022-10-28 05:47:28 +01:00 committed by manongjohn
parent cea78e587a
commit 7b474d9950
40 changed files with 1155 additions and 358 deletions

View file

@ -16,9 +16,10 @@ set(HEADERS
../include/tnzimage.h
ffmpeg/tiio_gif.h
ffmpeg/tiio_webm.h
ffmpeg/tiio_apng.h
ffmpeg/tiio_mp4.h
ffmpeg/tiio_mov.h
ffmpeg/tiio_ffmpeg.h
ffmpeg/tiio_ff_mov.h
sprite/tiio_sprite.h
mesh/tiio_mesh.h
exr/tinyexr_otmod.h
@ -44,9 +45,10 @@ set(SOURCES
tzl/tiio_tzl.cpp
ffmpeg/tiio_gif.cpp
ffmpeg/tiio_webm.cpp
ffmpeg/tiio_apng.cpp
ffmpeg/tiio_mp4.cpp
ffmpeg/tiio_mov.cpp
ffmpeg/tiio_ffmpeg.cpp
ffmpeg/tiio_ff_mov.cpp
sprite/tiio_sprite.cpp
mesh/tiio_mesh.cpp
exr/tiio_exr.cpp

View file

@ -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 <QStringList>
//===========================================================
//
// 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"));
}

View file

@ -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 <QCoreApplication>
//===========================================================
//
// 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

View file

@ -1,6 +1,6 @@
#include "tsystem.h"
#include "tiio_mov.h"
#include "tiio_ff_mov.h"
#include "trasterimage.h"
#include "timageinfo.h"
#include "tsound.h"
@ -9,36 +9,38 @@
//===========================================================
//
// TImageWriterMov
// TImageWriterFFMov
//
//===========================================================
class TImageWriterMov : public TImageWriter {
class TImageWriterFFMov : public TImageWriter {
public:
int m_frameIndex;
TImageWriterMov(const TFilePath &path, int frameIndex, TLevelWriterMov *lwg)
TImageWriterFFMov(const TFilePath &path, int frameIndex,
TLevelWriterFFMov *lwg)
: TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) {
m_lwg->addRef();
}
~TImageWriterMov() { m_lwg->release(); }
~TImageWriterFFMov() { m_lwg->release(); }
bool is64bitOutputSupported() override { return false; }
void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); }
private:
TLevelWriterMov *m_lwg;
TLevelWriterFFMov *m_lwg;
};
//===========================================================
//
// TLevelWriterMov;
// TLevelWriterFFMov;
//
//===========================================================
TLevelWriterMov::TLevelWriterMov(const TFilePath &path, TPropertyGroup *winfo)
TLevelWriterFFMov::TLevelWriterFFMov(const TFilePath &path,
TPropertyGroup *winfo)
: TLevelWriter(path, winfo) {
if (!m_properties) m_properties = new Tiio::MovWriterProperties();
if (!m_properties) m_properties = new Tiio::FFMovWriterProperties();
if (m_properties->getPropertyCount() == 0) {
m_scale = 100;
m_vidQuality = 100;
@ -56,8 +58,7 @@ TLevelWriterMov::TLevelWriterMov(const TFilePath &path, TPropertyGroup *winfo)
//-----------------------------------------------------------
TLevelWriterMov::~TLevelWriterMov() {
// QProcess createMov;
TLevelWriterFFMov::~TLevelWriterFFMov() {
QStringList preIArgs;
QStringList postIArgs;
@ -84,12 +85,12 @@ TLevelWriterMov::~TLevelWriterMov() {
preIArgs << "-framerate";
preIArgs << QString::number(m_frameRate);
postIArgs << "-pix_fmt";
postIArgs << "yuva444p10le";
postIArgs << "-c:v";
postIArgs << "prores_ks";
postIArgs << "-profile";
postIArgs << "4444";
postIArgs << "-pix_fmt";
postIArgs << "yuva444p10le";
postIArgs << "-profile:v";
postIArgs << "4";
postIArgs << "-s";
postIArgs << QString::number(outLx) + "x" + QString::number(outLy);
postIArgs << "-b";
@ -101,28 +102,26 @@ TLevelWriterMov::~TLevelWriterMov() {
//-----------------------------------------------------------
TImageWriterP TLevelWriterMov::getFrameWriter(TFrameId fid) {
// if (IOError != 0)
// throw TImageException(m_path, buildMovExceptionString(IOError));
TImageWriterP TLevelWriterFFMov::getFrameWriter(TFrameId fid) {
if (!fid.getLetter().isEmpty()) return TImageWriterP(0);
int index = fid.getNumber();
TImageWriterMov *iwg = new TImageWriterMov(m_path, index, this);
int index = fid.getNumber();
TImageWriterFFMov *iwg = new TImageWriterFFMov(m_path, index, this);
return TImageWriterP(iwg);
}
//-----------------------------------------------------------
void TLevelWriterMov::setFrameRate(double fps) {
void TLevelWriterFFMov::setFrameRate(double fps) {
m_frameRate = fps;
ffmpegWriter->setFrameRate(fps);
}
void TLevelWriterMov::saveSoundTrack(TSoundTrack *st) {
void TLevelWriterFFMov::saveSoundTrack(TSoundTrack *st) {
ffmpegWriter->saveSoundTrack(st);
}
//-----------------------------------------------------------
void TLevelWriterMov::save(const TImageP &img, int frameIndex) {
void TLevelWriterFFMov::save(const TImageP &img, int frameIndex) {
TRasterImageP image(img);
m_lx = image->getRaster()->getLx();
m_ly = image->getRaster()->getLy();
@ -131,20 +130,20 @@ void TLevelWriterMov::save(const TImageP &img, int frameIndex) {
//===========================================================
//
// TImageReaderMov
// TImageReaderFFMov
//
//===========================================================
class TImageReaderMov final : public TImageReader {
class TImageReaderFFMov final : public TImageReader {
public:
int m_frameIndex;
TImageReaderMov(const TFilePath &path, int index, TLevelReaderMov *lra,
TImageInfo *info)
TImageReaderFFMov(const TFilePath &path, int index, TLevelReaderFFMov *lra,
TImageInfo *info)
: TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) {
m_lra->addRef();
}
~TImageReaderMov() { m_lra->release(); }
~TImageReaderFFMov() { m_lra->release(); }
TImageP load() override { return m_lra->load(m_frameIndex); }
TDimension getSize() const { return m_lra->getSize(); }
@ -152,21 +151,22 @@ public:
const TImageInfo *getImageInfo() const override { return m_info; }
private:
TLevelReaderMov *m_lra;
TLevelReaderFFMov *m_lra;
TImageInfo *m_info;
// not implemented
TImageReaderMov(const TImageReaderMov &);
TImageReaderMov &operator=(const TImageReaderMov &src);
TImageReaderFFMov(const TImageReaderFFMov &);
TImageReaderFFMov &operator=(const TImageReaderFFMov &src);
};
//===========================================================
//
// TLevelReaderMov
// TLevelReaderFFMov
//
//===========================================================
TLevelReaderMov::TLevelReaderMov(const TFilePath &path) : TLevelReader(path) {
TLevelReaderFFMov::TLevelReaderFFMov(const TFilePath &path)
: TLevelReader(path) {
ffmpegReader = new Ffmpeg();
ffmpegReader->setPath(m_path);
ffmpegReader->disablePrecompute();
@ -189,13 +189,11 @@ TLevelReaderMov::TLevelReaderMov(const TFilePath &path) : TLevelReader(path) {
}
//-----------------------------------------------------------
TLevelReaderMov::~TLevelReaderMov() {
// ffmpegReader->cleanUpFiles();
}
TLevelReaderFFMov::~TLevelReaderFFMov() {}
//-----------------------------------------------------------
TLevelP TLevelReaderMov::loadInfo() {
TLevelP TLevelReaderFFMov::loadInfo() {
if (m_frameCount == -1) return TLevelP();
TLevelP level;
for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP());
@ -204,23 +202,21 @@ TLevelP TLevelReaderMov::loadInfo() {
//-----------------------------------------------------------
TImageReaderP TLevelReaderMov::getFrameReader(TFrameId fid) {
// if (IOError != 0)
// throw TImageException(m_path, buildAVIExceptionString(IOError));
TImageReaderP TLevelReaderFFMov::getFrameReader(TFrameId fid) {
if (!fid.getLetter().isEmpty()) return TImageReaderP(0);
int index = fid.getNumber();
TImageReaderMov *irm = new TImageReaderMov(m_path, index, this, m_info);
TImageReaderFFMov *irm = new TImageReaderFFMov(m_path, index, this, m_info);
return TImageReaderP(irm);
}
//------------------------------------------------------------------------------
TDimension TLevelReaderMov::getSize() { return m_size; }
TDimension TLevelReaderFFMov::getSize() { return m_size; }
//------------------------------------------------
TImageP TLevelReaderMov::load(int frameIndex) {
TImageP TLevelReaderFFMov::load(int frameIndex) {
if (!ffmpegFramesCreated) {
ffmpegReader->getFramesFromMovie();
ffmpegFramesCreated = true;
@ -228,16 +224,13 @@ TImageP TLevelReaderMov::load(int frameIndex) {
return ffmpegReader->getImage(frameIndex);
}
Tiio::MovWriterProperties::MovWriterProperties()
Tiio::FFMovWriterProperties::FFMovWriterProperties()
: m_vidQuality("Quality", 1, 100, 90), m_scale("Scale", 1, 100, 100) {
bind(m_vidQuality);
bind(m_scale);
}
void Tiio::MovWriterProperties::updateTranslation() {
void Tiio::FFMovWriterProperties::updateTranslation() {
m_vidQuality.setQStringName(tr("Quality"));
m_scale.setQStringName(tr("Scale"));
}
// Tiio::Reader* Tiio::makeMovReader(){ return nullptr; }
// Tiio::Writer* Tiio::makeMovWriter(){ return nullptr; }
}

View file

@ -1,7 +1,7 @@
#pragma once
#ifndef TTIO_MOV_INCLUDED
#define TTIO_MOV_INCLUDED
#ifndef TTIO_FFMPEG_MOV_INCLUDED
#define TTIO_FFMPEG_MOV_INCLUDED
#include "tproperty.h"
#include "tlevel_io.h"
@ -11,14 +11,14 @@
//===========================================================
//
// TLevelWriterMov
// TLevelWriterFFMov
//
//===========================================================
class TLevelWriterMov : public TLevelWriter {
class TLevelWriterFFMov : public TLevelWriter {
public:
TLevelWriterMov(const TFilePath &path, TPropertyGroup *winfo);
~TLevelWriterMov();
TLevelWriterFFMov(const TFilePath &path, TPropertyGroup *winfo);
~TLevelWriterFFMov();
void setFrameRate(double fps) override;
TImageWriterP getFrameWriter(TFrameId fid) override;
@ -27,7 +27,7 @@ public:
void saveSoundTrack(TSoundTrack *st) override;
static TLevelWriter *create(const TFilePath &path, TPropertyGroup *winfo) {
return new TLevelWriterMov(path, winfo);
return new TLevelWriterFFMov(path, winfo);
}
private:
@ -35,30 +35,27 @@ private:
int m_lx, m_ly;
int m_scale;
int m_vidQuality;
// void *m_buffer;
};
//===========================================================
//
// TLevelReaderMov
// TLevelReaderFFMov
//
//===========================================================
class TLevelReaderMov final : public TLevelReader {
class TLevelReaderFFMov final : public TLevelReader {
public:
TLevelReaderMov(const TFilePath &path);
~TLevelReaderMov();
TLevelReaderFFMov(const TFilePath &path);
~TLevelReaderFFMov();
TImageReaderP getFrameReader(TFrameId fid) override;
static TLevelReader *create(const TFilePath &f) {
return new TLevelReaderMov(f);
return new TLevelReaderFFMov(f);
}
TLevelP loadInfo() override;
TImageP load(int frameIndex);
TDimension getSize();
// TThread::Mutex m_mutex;
// void *m_decompressedBuffer;
private:
Ffmpeg *ffmpegReader;
bool ffmpegFramesCreated = false;
@ -72,22 +69,17 @@ namespace Tiio {
//===========================================================================
class MovWriterProperties : public TPropertyGroup {
Q_DECLARE_TR_FUNCTIONS(MovWriterProperties)
class FFMovWriterProperties : public TPropertyGroup {
Q_DECLARE_TR_FUNCTIONS(FFMovWriterProperties)
public:
// TEnumProperty m_pixelSize;
// TBoolProperty m_matte;
TIntProperty m_vidQuality;
TIntProperty m_scale;
MovWriterProperties();
FFMovWriterProperties();
void updateTranslation() override;
};
//===========================================================================
// Tiio::Reader *makeMovReader();
// Tiio::Writer *makeMovWriter();
} // namespace Tiio
} // namespace
#endif
#endif

View file

@ -3,6 +3,8 @@
#include "tsystem.h"
#include "tsound.h"
#include "tenv.h"
#include "timageinfo.h"
#include "toonz/stage.h"
#include <QProcess>
#include <QEventLoop>
@ -13,137 +15,50 @@
#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();
if (m_ffmpegTimeout > 0)
m_ffmpegTimeout *= 1000;
else
m_ffmpegTimeout = ThirdParty::getFFmpegTimeout() * 1000;
if (m_ffmpegTimeout <= 0)
m_ffmpegTimeout = -1;
std::string strPath = m_ffmpegPath.toStdString();
m_intermediateFormat = "png";
m_startNumber = 2147483647; // Lowest frame determines starting frame
}
Ffmpeg::~Ffmpeg() {}
bool Ffmpeg::checkFfmpeg() {
QString exe = "ffmpeg";
#if defined(_WIN32)
exe = exe + ".exe";
#endif
// check the user defined path in preferences first
QString path = Preferences::instance()->getFfmpegPath() + "/" + exe;
if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true;
// Let's try and autodetect the exe included with release
QStringList folderList;
folderList.append(".");
folderList.append("./ffmpeg"); // ffmpeg folder
#ifdef MACOSX
// Look inside app
folderList.append("./" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/ffmpeg"); // ffmpeg folder
#elif defined(LINUX) || defined(FREEBSD)
// Need to account for symbolic links
folderList.append(TEnv::getWorkingDirectory().getQString() +
"/ffmpeg"); // ffmpeg folder
#endif
QString exePath = TSystem::findFileLocation(folderList, exe);
if (!exePath.isEmpty()) {
Preferences::instance()->setValue(ffmpegPath, exePath);
return true;
}
// give up
return false;
}
bool Ffmpeg::checkFfprobe() {
QString exe = "ffprobe";
#if defined(_WIN32)
exe = exe + ".exe";
#endif
// check the user defined path in preferences first
QString path = Preferences::instance()->getFfmpegPath() + "/" + exe;
if (TSystem::doesExistFileOrLevel(TFilePath(path))) return true;
// Let's try and autodetect the exe included with release
QStringList folderList;
folderList.append(".");
folderList.append("./ffmpeg"); // ffmpeg folder
#ifdef MACOSX
// Look inside app
folderList.append("./" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/ffmpeg"); // ffmpeg folder
#elif defined(LINUX) || defined(FREEBSD)
// Need to account for symbolic links
folderList.append(TEnv::getWorkingDirectory().getQString() +
"/ffmpeg"); // ffmpeg folder
#endif
QString exePath = TSystem::findFileLocation(folderList, exe);
if (!exePath.isEmpty()) {
Preferences::instance()->setValue(ffmpegPath, exePath);
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);
if (waitFfmpeg(ffmpeg, 60000)) { // 1 minute timeout
QString results = ffmpeg.readAllStandardError();
results += ffmpeg.readAllStandardOutput();
ffmpeg.close();
std::string strResults = results.toStdString();
std::string::size_type n;
n = strResults.find(format);
if (n != std::string::npos)
return true;
}
ThirdParty::runFFmpeg(ffmpeg, args);
ffmpeg.waitForFinished();
QString results = ffmpeg.readAllStandardError();
results += ffmpeg.readAllStandardOutput();
ffmpeg.close();
std::string strResults = results.toStdString();
std::string::size_type n;
n = strResults.find(format);
if (n != std::string::npos)
return true;
return false;
}
bool Ffmpeg::checkCodecs(std::string codec) {
QString path = Preferences::instance()->getFfmpegPath() + "/ffmpeg";
#if defined(_WIN32)
path = path + ".exe";
#endif
QStringList args;
args << "-codecs";
QProcess ffmpeg;
ffmpeg.start(path, args);
if (waitFfmpeg(ffmpeg, 60000)) { // 1 minute timeout
QString results = ffmpeg.readAllStandardError();
results += ffmpeg.readAllStandardOutput();
ffmpeg.close();
std::string strResults = results.toStdString();
std::string::size_type n;
n = strResults.find(codec);
if (n != std::string::npos)
return true;
}
ThirdParty::runFFmpeg(ffmpeg, args);
ffmpeg.waitForFinished();
QString results = ffmpeg.readAllStandardError();
results += ffmpeg.readAllStandardOutput();
ffmpeg.close();
std::string strResults = results.toStdString();
std::string::size_type n;
n = strResults.find(codec);
if (n != std::string::npos)
return true;
return false;
}
@ -167,8 +82,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();
@ -203,7 +117,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;
@ -232,8 +146,8 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs,
// write the file
QProcess ffmpeg;
ffmpeg.start(m_ffmpegPath + "/ffmpeg", args);
if (waitFfmpeg(ffmpeg, m_ffmpegTimeout)) {
ThirdParty::runFFmpeg(ffmpeg, args);
if (waitFfmpeg(ffmpeg, asyncProcess)) {
QString results = ffmpeg.readAllStandardError();
results += ffmpeg.readAllStandardOutput();
int exitCode = ffmpeg.exitCode();
@ -244,8 +158,8 @@ void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs,
QString Ffmpeg::runFfprobe(QStringList args) {
QProcess ffmpeg;
ffmpeg.start(m_ffmpegPath + "/ffprobe", args);
if (!waitFfmpeg(ffmpeg, m_ffmpegTimeout)) {
ThirdParty::runFFprobe(ffmpeg, args);
if (!waitFfmpeg(ffmpeg, false)) {
throw TImageException(m_path, "error accessing ffprobe.");
}
QString results = ffmpeg.readAllStandardError();
@ -259,19 +173,20 @@ QString Ffmpeg::runFfprobe(QStringList args) {
return results;
}
bool Ffmpeg::waitFfmpeg(const QProcess &ffmpeg, int timeout) {
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<void (QProcess::*)(int, QProcess::ExitStatus)>(
&QProcess::finished),
&eloop, &QEventLoop::quit);
timer.start(timeout);
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 = eloop.exec();
int exitCode = ThirdParty::waitAsyncProcess(ffmpeg, m_ffmpegTimeout);
if (exitCode == 0) return true;
if (exitCode == -1) {
DVGui::warning(
@ -544,7 +459,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'));
@ -595,3 +510,99 @@ void Ffmpeg::cleanUpFiles() {
void Ffmpeg::disablePrecompute() {
Preferences::instance()->setPrecompute(false);
}
//===========================================================
//
// 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);
}

View file

@ -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);
static bool checkCodecs(std::string format);
double getFrameRate();
@ -46,17 +44,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 = -1, 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<QString> m_cleanUpList;
QStringList m_audioArgs;
TUINT32 m_sampleRate;
QString cleanPathSymbols();
static bool waitFfmpeg(const QProcess &ffmpeg, int timeout);
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

View file

@ -2,6 +2,7 @@
#include "tnzimage.h"
#include "tiio.h"
#include "tfiletype.h"
#include "thirdparty.h"
//-------------------------------------------------------------------
@ -57,7 +58,8 @@
#include "./ffmpeg/tiio_gif.h"
#include "./ffmpeg/tiio_webm.h"
#include "./ffmpeg/tiio_mp4.h"
#include "./ffmpeg/tiio_mov.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"
@ -160,32 +162,45 @@ 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("mov")) {
TLevelWriter::define("mov", TLevelWriterMov::create, true);
if (ffprobe) TLevelReader::define("mov", TLevelReaderMov::create);
TFileType::declare("mov", TFileType::RASTER_LEVEL);
Tiio::defineWriterProperties("mov", new Tiio::MovWriterProperties());
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 (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

View file

@ -0,0 +1,71 @@
#pragma once
#ifndef THIRDPARTY_INCLUDED
#define THIRDPARTY_INCLUDED
#include "tcommon.h"
#include <QProcess>
#include <QString>
#include <QStringList>
#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

View file

@ -237,8 +237,8 @@ public:
// Some useful utility inlines
inline bool isMovieType(std::string type) {
return (type == "avi" || type == "webm" ||
type == "mp4" || type == "mov");
return (type == "mov" || type == "avi" || type == "3gp" || type == "webm" ||
type == "mp4" || type == "apng");
}
//-----------------------------------------------------------
@ -264,7 +264,7 @@ inline bool isMovieTypeOpaque(const TFilePath &fp) {
//-----------------------------------------------------------
inline bool isSequencialRequired(std::string type) {
return (type == "avi" || type == "3gp");
return (type == "mov" || type == "avi" || type == "3gp");
}
//-----------------------------------------------------------
@ -279,7 +279,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");
}
//-----------------------------------------------------------

View file

@ -145,6 +145,7 @@ public:
void saveTemplate(ToonzScene *scene);
// void clearProjectsRoot();
// void addProjectsRoot(const TFilePath &fp);
void addSVNProjectsRoot(const TFilePath &fp);

View file

@ -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);

View file

@ -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})

View file

@ -0,0 +1,35 @@
#include <memory>
#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 <QDir>
#include <QProcess>
//==============================================================================
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;
}

View file

@ -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

View file

@ -8,6 +8,8 @@
#include "tenv.h"
#include "toonz/preferences.h"
#include "toonz/toonzfolders.h"
#include "thirdparty.h"
#include <QDir>
#include <QProcess>
@ -37,50 +39,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 Tahoma2D 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;
}
#ifdef MACOSX
path = QDir::currentPath() + "/" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/ffmpeg/ffmpeg";
if (TSystem::doesExistFileOrLevel(TFilePath(path))) {
Preferences::instance()->setValue(
ffmpegPath, QDir::currentPath() + "/" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/ffmpeg/");
return true;
}
#endif
#if defined(LINUX) || defined(FREEBSD)
QString currentPath = TEnv::getWorkingDirectory().getQString();
path = currentPath + "/ffmpeg/ffmpeg";
if (TSystem::doesExistFileOrLevel(TFilePath(path))) {
Preferences::instance()->setValue(ffmpegPath, currentPath + "/ffmpeg/");
return true;
}
#endif
// give up
return false;
}
TFilePath FfmpegAudio::getFfmpegCache() {
QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString();
if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/ffmpeg"))) {
@ -93,10 +51,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();

View file

@ -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();

View file

@ -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,10 +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);
TSoundTrackReader::define("ogg", TSoundTrackReaderFFmpeg::create);
TFileType::declare("ogg", TFileType::AUDIO_LEVEL);
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;
}

View file

@ -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

View file

@ -449,7 +449,7 @@ int main(int argc, char *argv[]) {
exit(1);
}
}
if (ext != "pli") {
if (ext != "3gp" && ext != "pli") {
convert(srcFilePath, dstFilePath, range, width, prop, resQuality);
} else {
msg = "Cannot convert to ." + ext + " format.";

View file

@ -288,7 +288,10 @@ void BatchesController::setTasksTree(TaskTreeModel *tree) {
//------------------------------------------------------------------------------
inline bool isMovieType(std::string type) { return (type == "avi" || type == "mp4" || type == "webm" || type == "mov"); }
inline bool isMovieType(std::string type) {
return (type == "mov" || type == "avi" || type == "3gp" || type == "mp4" ||
type == "webm");
}
//------------------------------------------------------------------------------

View file

@ -884,7 +884,8 @@ void CastBrowser::viewFile() {
if (!TFileType::isViewable(TFileType::getInfo(filePath))) return;
if (Preferences::instance()->isDefaultViewerEnabled() &&
(filePath.getType() == "avi"))
(filePath.getType() == "mov" || filePath.getType() == "avi" ||
filePath.getType() == "3gp"))
QDesktopServices::openUrl(QUrl("file:///" + toQString(filePath)));
else
::viewFile(filePath);

View file

@ -371,7 +371,8 @@ you want to do?";*/
if (m_frame >= totalFrameCount &&
Preferences::instance()->isGeneratedMovieViewEnabled()) {
if (Preferences::instance()->isDefaultViewerEnabled() &&
(outPath.getType() == "avi")) {
(outPath.getType() == "mov" || outPath.getType() == "avi" ||
outPath.getType() == "3gp")) {
QString name = QString::fromStdString(outPath.getName());
if (!TSystem::showDocument(outPath)) {

View file

@ -393,7 +393,8 @@ void FileSelection::viewFile() {
continue;
if (Preferences::instance()->isDefaultViewerEnabled() &&
(files[i].getType() == "avi"))
(files[i].getType() == "mov" || files[i].getType() == "avi" ||
files[i].getType() == "3gp"))
QDesktopServices::openUrl(QUrl("file:///" + toQString(files[i])));
else if (files[i].getType() == "tpl") {
viewedPalette = StudioPalette::instance()->getPalette(files[i], false);

View file

@ -370,7 +370,9 @@ LoadImagesPopup::LoadImagesPopup(FlipBook *flip)
m_shrinkField = new DVGui::LineEdit("1", this);
// Define the append/load filter types
m_appendFilterTypes << "jpg"
m_appendFilterTypes << "3gp"
<< "mov"
<< "jpg"
<< "png"
<< "tga"
<< "tif"
@ -2269,7 +2271,8 @@ FlipBook *viewFile(const TFilePath &path, int from, int to, int step,
}
// Movie files must not have the ".." extension
if ((path.getType() == "avi") &&
if ((path.getType() == "mov" || path.getType() == "avi" ||
path.getType() == "3gp") &&
path.isLevelName()) {
DVGui::warning(QObject::tr("%1 has an invalid extension format.")
.arg(QString::fromStdString(path.getLevelName())));

View file

@ -25,6 +25,7 @@
#include "tsound_io.h"
#include "toutputproperties.h"
#include "toonz/tproject.h"
#include "thirdparty.h"
// TnzCore includes
#include "filebrowsermodel.h"
@ -530,7 +531,7 @@ void LipSyncPopup::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();
@ -702,46 +703,6 @@ void LipSyncPopup::saveAudio() {
//-----------------------------------------------------------------------------
bool LipSyncPopup::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;
// Let's try and autodetect the exe included with release
QStringList folderList;
folderList.append(".");
folderList.append("./rhubarb"); // rhubarb folder
#ifdef MACOSX
// Look inside app
folderList.append("./" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/rhubarb"); // rhubarb folder
#elif defined(LINUX) || defined(FREEBSD)
// Need to account for symbolic links
folderList.append(TEnv::getWorkingDirectory().getQString() +
"/rhubarb"); // rhubarb folder
#endif
QString exePath = TSystem::findFileLocation(folderList, exe);
if (!exePath.isEmpty()) {
Preferences::instance()->setValue(rhubarbPath, exePath);
return true;
}
// give up
return false;
}
//-----------------------------------------------------------------------------
void LipSyncPopup::runRhubarb() {
QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString();
if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/rhubarb"))) {

View file

@ -94,7 +94,6 @@ protected:
void refreshSoundLevels();
void saveAudio();
void runRhubarb();
bool checkRhubarb();
public slots:
void onApplyButton();

View file

@ -12,6 +12,7 @@
#include "cleanupsettingspopup.h"
#include "filebrowsermodel.h"
#include "expressionreferencemanager.h"
#include "thirdparty.h"
#include "startuppopup.h"
// TnzTools includes
@ -507,6 +508,9 @@ int main(int argc, char *argv[]) {
TBigMemoryManager::instance()->setRunOutOfContiguousMemoryHandler(
&toonzRunOutOfContMemHandler);
// Setup third party
ThirdParty::initialize();
// Toonz environment
initToonzEnv(argumentPathValues);

View file

@ -159,7 +159,7 @@ public:
TPointD center(0.5 * cameraSize.lx, 0.5 * cameraSize.ly);
m_viewAff = TTranslation(center);
std::string ext = fp.getType();
m_isFrames = ext != "avi";
m_isFrames = ext != "avi" && ext != "mov" && ext != "3gp";
m_fileOptions = properties.getFileFormatProperties(ext);
}

View file

@ -87,10 +87,9 @@ public:
if (Preferences::instance()->isGeneratedMovieViewEnabled()) {
if (!isPreview && (Preferences::instance()->isDefaultViewerEnabled()) &&
(m_fp.getType() == "avi" ||
m_fp.getType() == "mp4" ||
m_fp.getType() == "gif" || m_fp.getType() == "webm" ||
m_fp.getType() == "mov")) {
(m_fp.getType() == "mov" || m_fp.getType() == "avi" ||
m_fp.getType() == "3gp" || m_fp.getType() == "mp4" ||
m_fp.getType() == "gif" || m_fp.getType() == "webm")) {
QString name = QString::fromStdString(m_fp.getName());
int index;
if ((index = name.indexOf("#RENDERID")) != -1) //! quite ugly I

View file

@ -482,9 +482,7 @@ void ChangeObjectParent::refresh() {
: viewer->getOtherCameraColor();
} else if (id.isColumn() && (!xsh->isColumnEmpty(index))) {
TXshColumn *colx = xsh->getColumn(index);
if (colx->getColumnType() == TXshColumn::eSoundTextType ||
colx->getColumnType() == TXshColumn::eSoundType)
continue;
if (!colx->canBeParent()) continue;
QColor unused;
viewer->getColumnColor(newTextBG, unused, id.getIndex(), xsh);

View file

@ -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
@ -247,6 +248,7 @@ set(SOURCES
tcolumnfxset.cpp
tdistort.cpp
texturemanager.cpp
thirdparty.cpp
tlog.cpp
tnewoutlinevectorize.cpp
toonzfolders.cpp

View file

@ -241,7 +241,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

View file

@ -295,6 +295,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");
@ -306,11 +307,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

View file

@ -0,0 +1,349 @@
#include "thirdparty.h"
// TnzLib includes
#include "toonz/preferences.h"
// TnzCore includes
#include "tsystem.h"
#include "tmsgcore.h"
// QT includes
#include <QCoreApplication>
#include <QEventLoop>
#include <QTimer>
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;
// Let's try and autodetect the exe included with release
QStringList folderList;
folderList.append(".");
folderList.append("./ffmpeg"); // ffmpeg folder
#ifdef MACOSX
// Look inside app
folderList.append("./" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/ffmpeg"); // ffmpeg folder
#elif defined(LINUX) || defined(FREEBSD)
// Need to account for symbolic links
folderList.append(TEnv::getWorkingDirectory().getQString() +
"/ffmpeg"); // ffmpeg folder
#endif
#ifndef _WIN32
folderList.append("/usr/local/bin");
folderList.append("/usr/bin");
folderList.append("/bin");
#endif
#ifdef _WIN32
QString exePath = TSystem::findFileLocation(folderList, "ffmpeg.exe");
#else
QString exePath = TSystem::findFileLocation(folderList, "ffmpeg");
#endif
if (!exePath.isEmpty()) return exePath;
// give up
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;
// Let's try and autodetect the exe included with release
QStringList folderList;
folderList.append(".");
folderList.append("./rhubarb"); // rhubarb folder
#ifdef MACOSX
// Look inside app
folderList.append("./" +
QString::fromStdString(TEnv::getApplicationFileName()) +
".app/rhubarb"); // rhubarb folder
#elif defined(LINUX) || defined(FREEBSD)
// Need to account for symbolic links
folderList.append(TEnv::getWorkingDirectory().getQString() +
"/rhubarb"); // rhubarb folder
#endif
#ifndef _WIN32
folderList.append("/usr/local/bin");
folderList.append(return "/usr/bin");
folderList.append(return "/bin");
#endif
#ifdef _WIN32
QString exePath = TSystem::findFileLocation(folderList, "rhubarb.exe");
#else
QString exePath = TSystem::findFileLocation(folderList, "rhubarb");
#endif
if (!exePath.isEmpty()) {
Preferences::instance()->setValue(rhubarbPath, exePath);
return exePath;
}
// give up
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<void (QProcess::*)(int, QProcess::ExitStatus)>(
&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

View file

@ -801,7 +801,7 @@ else
p->setValue(L"64(RGBM)");
}
bool isMovie = (format=="avi");
bool isMovie = (format=="mov" || format=="avi" || format=="3gp");
TLevelWriterP lw;

View file

@ -836,6 +836,10 @@ TProjectManager *TProjectManager::instance() {
return &_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.

View file

@ -593,6 +593,23 @@ TXshColumn::ColumnType TXshColumn::toColumnType(int levelType) {
return colType;
}
//-----------------------------------------------------------------------------
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;

View file

@ -321,7 +321,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()) +

View file

@ -267,7 +267,7 @@ QString InfoViewerImp::getTypeString() {
return "Smart Raster Level";
else if (ext == "pli" || ext == "svg")
return "Vector Level";
else if (ext == "avi")
else if (ext == "mov" || ext == "avi" || ext == "3gp")
return "Movie File";
else if (ext == "tnz")
return "Tahoma2D Scene";