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