justburner 64e039
justburner 64e039
#include "tsystem.h"
justburner 64e039
#include "tiio_ff_mov.h"
justburner 64e039
#include "trasterimage.h"
justburner 64e039
#include "timageinfo.h"
justburner 64e039
#include "tsound.h"
justburner 64e039
#include "toonz/stage.h"
justburner 64e039
#include <qstringlist></qstringlist>
justburner 64e039
justburner 64e039
//===========================================================
justburner 64e039
//
justburner 64e039
//  TImageWriterFFMov
justburner 64e039
//
justburner 64e039
//===========================================================
justburner 64e039
justburner 64e039
class TImageWriterFFMov : public TImageWriter {
justburner 64e039
public:
justburner 64e039
  int m_frameIndex;
justburner 64e039
justburner 64e039
  TImageWriterFFMov(const TFilePath &path, int frameIndex,
justburner 64e039
                    TLevelWriterFFMov *lwg)
justburner 64e039
      : TImageWriter(path), m_frameIndex(frameIndex), m_lwg(lwg) {
justburner 64e039
    m_lwg->addRef();
justburner 64e039
  }
justburner 64e039
  ~TImageWriterFFMov() { m_lwg->release(); }
justburner 64e039
justburner 64e039
  bool is64bitOutputSupported() override { return false; }
justburner 64e039
  void save(const TImageP &img) override { m_lwg->save(img, m_frameIndex); }
justburner 64e039
justburner 64e039
private:
justburner 64e039
  TLevelWriterFFMov *m_lwg;
justburner 64e039
};
justburner 64e039
justburner 64e039
//===========================================================
justburner 64e039
//
justburner 64e039
//  TLevelWriterFFMov;
justburner 64e039
//
justburner 64e039
//===========================================================
justburner 64e039
justburner 64e039
TLevelWriterFFMov::TLevelWriterFFMov(const TFilePath &path,
justburner 64e039
                                     TPropertyGroup *winfo)
justburner 64e039
    : TLevelWriter(path, winfo) {
justburner 64e039
  if (!m_properties) m_properties = new Tiio::FFMovWriterProperties();
justburner 64e039
  if (m_properties->getPropertyCount() == 0) {
justburner 64e039
    m_scale      = 100;
justburner 64e039
    m_vidQuality = 100;
justburner 64e039
  } else {
justburner 64e039
    std::string scale = m_properties->getProperty("Scale")->getValueAsString();
justburner 64e039
    m_scale           = QString::fromStdString(scale).toInt();
justburner 64e039
    std::string quality =
justburner 64e039
        m_properties->getProperty("Quality")->getValueAsString();
justburner 64e039
    m_vidQuality = QString::fromStdString(quality).toInt();
justburner 64e039
  }
justburner 64e039
  ffmpegWriter = new Ffmpeg();
justburner 64e039
  ffmpegWriter->setPath(m_path);
justburner 64e039
  if (TSystem::doesExistFileOrLevel(m_path)) TSystem::deleteFile(m_path);
justburner 64e039
}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
TLevelWriterFFMov::~TLevelWriterFFMov() {
justburner 64e039
  QStringList preIArgs;
justburner 64e039
  QStringList postIArgs;
justburner 64e039
justburner 64e039
  int outLx = m_lx;
justburner 64e039
  int outLy = m_ly;
justburner 64e039
justburner 64e039
  // set scaling
justburner 64e039
  if (m_scale != 0) {
justburner 64e039
    outLx = m_lx * m_scale / 100;
justburner 64e039
    outLy = m_ly * m_scale / 100;
justburner 64e039
  }
justburner 64e039
  // ffmpeg doesn't like resolutions that aren't divisible by 2.
justburner 64e039
  if (outLx % 2 != 0) outLx++;
justburner 64e039
  if (outLy % 2 != 0) outLy++;
justburner 64e039
justburner 64e039
  // calculate quality (bitrate)
justburner 64e039
  int pixelCount   = m_lx * m_ly;
justburner 64e039
  int bitRate      = pixelCount / 150;  // crude but gets decent values
justburner 64e039
  double quality   = m_vidQuality / 100.0;
justburner 64e039
  double tempRate  = (double)bitRate * quality;
justburner 64e039
  int finalBitrate = (int)tempRate;
justburner 64e039
  int crf          = 51 - (m_vidQuality * 51 / 100);
justburner 64e039
justburner 64e039
  preIArgs << "-framerate";
justburner 64e039
  preIArgs << QString::number(m_frameRate);
justburner 64e039
justburner 64e039
  postIArgs << "-c:v";
justburner 64e039
  postIArgs << "prores_ks";
justburner 64e039
  postIArgs << "-pix_fmt";
justburner 64e039
  postIArgs << "yuva444p10le";
justburner 64e039
  postIArgs << "-profile:v";
justburner 64e039
  postIArgs << "4";
justburner 64e039
  postIArgs << "-s";
justburner 64e039
  postIArgs << QString::number(outLx) + "x" + QString::number(outLy);
justburner 64e039
  postIArgs << "-b";
justburner 64e039
  postIArgs << QString::number(finalBitrate) + "k";
justburner 64e039
justburner 64e039
  ffmpegWriter->runFfmpeg(preIArgs, postIArgs, false, false, true);
justburner 64e039
  ffmpegWriter->cleanUpFiles();
justburner 64e039
}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
TImageWriterP TLevelWriterFFMov::getFrameWriter(TFrameId fid) {
justburner 64e039
  if (!fid.getLetter().isEmpty()) return TImageWriterP(0);
justburner 64e039
  int index              = fid.getNumber();
justburner 64e039
  TImageWriterFFMov *iwg = new TImageWriterFFMov(m_path, index, this);
justburner 64e039
  return TImageWriterP(iwg);
justburner 64e039
}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
void TLevelWriterFFMov::setFrameRate(double fps) {
justburner 64e039
  m_frameRate = fps;
justburner 64e039
  ffmpegWriter->setFrameRate(fps);
justburner 64e039
}
justburner 64e039
justburner 64e039
void TLevelWriterFFMov::saveSoundTrack(TSoundTrack *st) {
justburner 64e039
  ffmpegWriter->saveSoundTrack(st);
justburner 64e039
}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
void TLevelWriterFFMov::save(const TImageP &img, int frameIndex) {
justburner 64e039
  TRasterImageP image(img);
justburner 64e039
  m_lx = image->getRaster()->getLx();
justburner 64e039
  m_ly = image->getRaster()->getLy();
justburner 64e039
  ffmpegWriter->createIntermediateImage(img, frameIndex);
justburner 64e039
}
justburner 64e039
justburner 64e039
//===========================================================
justburner 64e039
//
justburner 64e039
//  TImageReaderFFMov
justburner 64e039
//
justburner 64e039
//===========================================================
justburner 64e039
justburner 64e039
class TImageReaderFFMov final : public TImageReader {
justburner 64e039
public:
justburner 64e039
  int m_frameIndex;
justburner 64e039
justburner 64e039
  TImageReaderFFMov(const TFilePath &path, int index, TLevelReaderFFMov *lra,
justburner 64e039
                    TImageInfo *info)
justburner 64e039
      : TImageReader(path), m_lra(lra), m_frameIndex(index), m_info(info) {
justburner 64e039
    m_lra->addRef();
justburner 64e039
  }
justburner 64e039
  ~TImageReaderFFMov() { m_lra->release(); }
justburner 64e039
justburner 64e039
  TImageP load() override { return m_lra->load(m_frameIndex); }
justburner 64e039
  TDimension getSize() const { return m_lra->getSize(); }
justburner 64e039
  TRect getBBox() const { return TRect(); }
justburner 64e039
  const TImageInfo *getImageInfo() const override { return m_info; }
justburner 64e039
justburner 64e039
private:
justburner 64e039
  TLevelReaderFFMov *m_lra;
justburner 64e039
  TImageInfo *m_info;
justburner 64e039
justburner 64e039
  // not implemented
justburner 64e039
  TImageReaderFFMov(const TImageReaderFFMov &);
justburner 64e039
  TImageReaderFFMov &operator=(const TImageReaderFFMov &src);
justburner 64e039
};
justburner 64e039
justburner 64e039
//===========================================================
justburner 64e039
//
justburner 64e039
//  TLevelReaderFFMov
justburner 64e039
//
justburner 64e039
//===========================================================
justburner 64e039
justburner 64e039
TLevelReaderFFMov::TLevelReaderFFMov(const TFilePath &path)
justburner 64e039
    : TLevelReader(path) {
justburner 64e039
  ffmpegReader = new Ffmpeg();
justburner 64e039
  ffmpegReader->setPath(m_path);
justburner 64e039
  ffmpegReader->disablePrecompute();
justburner 64e039
  ffmpegFileInfo tempInfo = ffmpegReader->getInfo();
justburner 64e039
  double fps              = tempInfo.m_frameRate;
justburner 64e039
  m_frameCount            = tempInfo.m_frameCount;
justburner 64e039
  m_size                  = TDimension(tempInfo.m_lx, tempInfo.m_ly);
justburner 64e039
  m_lx                    = m_size.lx;
justburner 64e039
  m_ly                    = m_size.ly;
justburner 64e039
justburner 64e039
  // set values
justburner 64e039
  m_info                   = new TImageInfo();
justburner 64e039
  m_info->m_frameRate      = fps;
justburner 64e039
  m_info->m_lx             = m_lx;
justburner 64e039
  m_info->m_ly             = m_ly;
justburner 64e039
  m_info->m_bitsPerSample  = 8;
justburner 64e039
  m_info->m_samplePerPixel = 4;
justburner 64e039
  m_info->m_dpix           = Stage::standardDpi;
justburner 64e039
  m_info->m_dpiy           = Stage::standardDpi;
justburner 64e039
}
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
TLevelReaderFFMov::~TLevelReaderFFMov() {}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
TLevelP TLevelReaderFFMov::loadInfo() {
justburner 64e039
  if (m_frameCount == -1) return TLevelP();
justburner 64e039
  TLevelP level;
justburner 64e039
  for (int i = 1; i <= m_frameCount; i++) level->setFrame(i, TImageP());
justburner 64e039
  return level;
justburner 64e039
}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
TImageReaderP TLevelReaderFFMov::getFrameReader(TFrameId fid) {
justburner 64e039
  if (!fid.getLetter().isEmpty()) return TImageReaderP(0);
justburner 64e039
  int index = fid.getNumber();
justburner 64e039
justburner 64e039
  TImageReaderFFMov *irm = new TImageReaderFFMov(m_path, index, this, m_info);
justburner 64e039
  return TImageReaderP(irm);
justburner 64e039
}
justburner 64e039
justburner 64e039
//------------------------------------------------------------------------------
justburner 64e039
justburner 64e039
TDimension TLevelReaderFFMov::getSize() { return m_size; }
justburner 64e039
justburner 64e039
//------------------------------------------------
justburner 64e039
justburner 64e039
TImageP TLevelReaderFFMov::load(int frameIndex) {
justburner 64e039
  if (!ffmpegFramesCreated) {
justburner 64e039
    ffmpegReader->getFramesFromMovie();
justburner 64e039
    ffmpegFramesCreated = true;
justburner 64e039
  }
justburner 64e039
  return ffmpegReader->getImage(frameIndex);
justburner 64e039
}
justburner 64e039
justburner 64e039
Tiio::FFMovWriterProperties::FFMovWriterProperties()
justburner 64e039
    : m_vidQuality("Quality", 1, 100, 90), m_scale("Scale", 1, 100, 100) {
justburner 64e039
  bind(m_vidQuality);
justburner 64e039
  bind(m_scale);
justburner 64e039
}
justburner 64e039
justburner 64e039
void Tiio::FFMovWriterProperties::updateTranslation() {
justburner 64e039
  m_vidQuality.setQStringName(tr("Quality"));
justburner 64e039
  m_scale.setQStringName(tr("Scale"));
justburner 64e039
}