turtletooth 04d8fd
#include "tiio_ffmpeg.h"
turtletooth 04d8fd
#include "../toonz/tapp.h"
turtletooth 04d8fd
#include "tsystem.h"
turtletooth 04d8fd
#include "tsound.h"
justburner 64e039
#include "timageinfo.h"
justburner 64e039
#include "toonz/stage.h"
turtletooth 04d8fd
turtletooth 04d8fd
#include <qprocess></qprocess>
justburner 69bbaf
#include <qeventloop></qeventloop>
justburner 69bbaf
#include <qtimer></qtimer>
turtletooth 04d8fd
#include <qdir></qdir>
turtletooth 04d8fd
#include <qtgui qimage=""></qtgui>
Jeremy Bullock 3b0af2
#include <qregexp></qregexp>
turtletooth 04d8fd
#include "toonz/preferences.h"
turtletooth 04d8fd
#include "toonz/toonzfolders.h"
Jeremy Bullock 55b5db
#include "tmsgcore.h"
justburner 64e039
#include "thirdparty.h"
turtletooth 04d8fd
turtletooth 04d8fd
Ffmpeg::Ffmpeg() {
justburner 64e039
  m_ffmpegTimeout      = ThirdParty::getFFmpegTimeout() * 1000;
turtletooth 04d8fd
  m_intermediateFormat = "png";
justburner 188d84
  m_startNumber        = 2147483647;  // Lowest frame determines starting frame
turtletooth 04d8fd
}
turtletooth 04d8fd
Ffmpeg::~Ffmpeg() {}
turtletooth 04d8fd
turtletooth 04d8fd
bool Ffmpeg::checkFormat(std::string format) {
shun-iwasawa 27b0cf
  QStringList args;
shun-iwasawa 27b0cf
  args << "-formats";
shun-iwasawa 27b0cf
  QProcess ffmpeg;
justburner 64e039
  ThirdParty::runFFmpeg(ffmpeg, args);
shun-iwasawa 27b0cf
  ffmpeg.waitForFinished();
shun-iwasawa 27b0cf
  QString results = ffmpeg.readAllStandardError();
shun-iwasawa 27b0cf
  results += ffmpeg.readAllStandardOutput();
shun-iwasawa 27b0cf
  ffmpeg.close();
shun-iwasawa 27b0cf
  std::string strResults = results.toStdString();
shun-iwasawa 27b0cf
  std::string::size_type n;
shun-iwasawa 27b0cf
  n = strResults.find(format);
shun-iwasawa 27b0cf
  if (n != std::string::npos)
shun-iwasawa 27b0cf
    return true;
shun-iwasawa 27b0cf
  else
shun-iwasawa 27b0cf
    return false;
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
TFilePath Ffmpeg::getFfmpegCache() {
turtletooth 04d8fd
  QString cacheRoot = ToonzFolder::getCacheRootFolder().getQString();
turtletooth 04d8fd
  if (!TSystem::doesExistFileOrLevel(TFilePath(cacheRoot + "/ffmpeg"))) {
turtletooth 04d8fd
    TSystem::mkDir(TFilePath(cacheRoot + "/ffmpeg"));
turtletooth 04d8fd
  }
turtletooth 04d8fd
  std::string ffmpegPath =
turtletooth 04d8fd
      TFilePath(cacheRoot + "/ffmpeg").getQString().toStdString();
turtletooth 04d8fd
  return TFilePath(cacheRoot + "/ffmpeg");
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::setFrameRate(double fps) { m_frameRate = fps; }
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::setPath(TFilePath path) { m_path = path; }
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::createIntermediateImage(const TImageP &img, int frameIndex) {
Jeremy Bullock 3837a1
  m_frameCount++;
justburner 188d84
  frameIndex--;  // ffmpeg start at 0 by default
justburner 188d84
  if (frameIndex < m_startNumber) m_startNumber = frameIndex;
Jeremy Bullock edaab7
  QString tempPath = getFfmpegCache().getQString() + "//" +
Jeremy Bullock edaab7
                     QString::fromStdString(m_path.getName()) + "tempOut" +
justburner 64e039
                     QString::number(frameIndex) + "." + m_intermediateFormat;
turtletooth 04d8fd
  std::string saveStatus = "";
turtletooth 04d8fd
  TRasterImageP tempImage(img);
turtletooth 04d8fd
  TRasterImage *image = (TRasterImage *)tempImage->cloneImage();
turtletooth 04d8fd
turtletooth 04d8fd
  m_lx           = image->getRaster()->getLx();
turtletooth 04d8fd
  m_ly           = image->getRaster()->getLy();
turtletooth 04d8fd
  m_bpp          = image->getRaster()->getPixelSize();
turtletooth 04d8fd
  int totalBytes = m_lx * m_ly * m_bpp;
turtletooth 04d8fd
  image->getRaster()->yMirror();
turtletooth 04d8fd
turtletooth 04d8fd
  // lock raster to get data
turtletooth 04d8fd
  image->getRaster()->lock();
turtletooth 04d8fd
  void *buffin = image->getRaster()->getRawData();
turtletooth 04d8fd
  assert(buffin);
turtletooth 04d8fd
  void *buffer = malloc(totalBytes);
turtletooth 04d8fd
  memcpy(buffer, buffin, totalBytes);
turtletooth 04d8fd
turtletooth 04d8fd
  image->getRaster()->unlock();
turtletooth 04d8fd
turtletooth 04d8fd
  // create QImage save format
turtletooth 04d8fd
  QByteArray ba      = m_intermediateFormat.toUpper().toLatin1();
turtletooth 04d8fd
  const char *format = ba.data();
turtletooth 04d8fd
turtletooth 04d8fd
  QImage *qi = new QImage((uint8_t *)buffer, m_lx, m_ly, QImage::Format_ARGB32);
turtletooth 04d8fd
  qi->save(tempPath, format, -1);
turtletooth 04d8fd
  free(buffer);
turtletooth 04d8fd
  m_cleanUpList.push_back(tempPath);
Jeremy Bullock 3837a1
turtletooth 04d8fd
  delete qi;
turtletooth 04d8fd
  delete image;
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::runFfmpeg(QStringList preIArgs, QStringList postIArgs,
turtletooth 04d8fd
                       bool includesInPath, bool includesOutPath,
justburner 64e039
                       bool overWriteFiles, bool asyncProcess) {
Jeremy Bullock edaab7
  QString tempName = "//" + QString::fromStdString(m_path.getName()) +
Jeremy Bullock edaab7
                     "tempOut%d." + m_intermediateFormat;
Jeremy Bullock edaab7
  tempName = getFfmpegCache().getQString() + tempName;
turtletooth 04d8fd
turtletooth 04d8fd
  QStringList args;
turtletooth 04d8fd
  args = args + preIArgs;
turtletooth 04d8fd
  if (!includesInPath) {  // NOTE:  if including the in path, it needs to be in
turtletooth 04d8fd
                          // the preIArgs argument.
justburner 188d84
    if (m_startNumber != 0) {
justburner 188d84
      args << "-start_number";
justburner 188d84
      args << QString::number(m_startNumber);
justburner 188d84
    }
turtletooth 04d8fd
    args << "-i";
turtletooth 04d8fd
    args << tempName;
turtletooth 04d8fd
  }
turtletooth 04d8fd
  if (m_hasSoundTrack) args = args + m_audioArgs;
shun-iwasawa 8fb291
  args = args + postIArgs;
turtletooth 04d8fd
  if (overWriteFiles && !includesOutPath) {  // if includesOutPath is true, you
turtletooth 04d8fd
                                             // need to include the overwrite in
turtletooth 04d8fd
                                             // your postIArgs.
turtletooth 04d8fd
    args << "-y";
turtletooth 04d8fd
  }
turtletooth 04d8fd
  if (!includesOutPath) {
turtletooth 04d8fd
    args << m_path.getQString();
turtletooth 04d8fd
  }
turtletooth 04d8fd
turtletooth 04d8fd
  // write the file
turtletooth 04d8fd
  QProcess ffmpeg;
justburner 64e039
  ThirdParty::runFFmpeg(ffmpeg, args);
justburner 64e039
  if (waitFfmpeg(ffmpeg, asyncProcess)) {
Jeremy Bullock 55b5db
    QString results = ffmpeg.readAllStandardError();
Jeremy Bullock 55b5db
    results += ffmpeg.readAllStandardOutput();
justburner 64e039
    int exitCode           = ffmpeg.exitCode();
Jeremy Bullock 55b5db
    std::string strResults = results.toStdString();
Jeremy Bullock 55b5db
  }
justburner 69bbaf
  ffmpeg.close();
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
QString Ffmpeg::runFfprobe(QStringList args) {
turtletooth 04d8fd
  QProcess ffmpeg;
justburner 64e039
  ThirdParty::runFFprobe(ffmpeg, args);
justburner 64e039
  if (!waitFfmpeg(ffmpeg, false)) {
justburner 69bbaf
    throw TImageException(m_path, "error accessing ffprobe.");
justburner 69bbaf
  }
turtletooth 04d8fd
  QString results = ffmpeg.readAllStandardError();
turtletooth 04d8fd
  results += ffmpeg.readAllStandardOutput();
turtletooth 04d8fd
  int exitCode = ffmpeg.exitCode();
turtletooth 04d8fd
  ffmpeg.close();
shun-iwasawa a1dbd7
  // If the url cannot be opened or recognized as a multimedia file, ffprobe
shun-iwasawa a1dbd7
  // returns a positive exit code.
shun-iwasawa a1dbd7
  if (exitCode > 0) throw TImageException(m_path, "error reading info.");
turtletooth 04d8fd
  std::string strResults = results.toStdString();
turtletooth 04d8fd
  return results;
turtletooth 04d8fd
}
turtletooth 04d8fd
justburner 64e039
bool Ffmpeg::waitFfmpeg(QProcess &ffmpeg, bool asyncProcess) {
justburner 64e039
  if (!asyncProcess) {
justburner 64e039
    bool status = ffmpeg.waitForFinished(m_ffmpegTimeout);
justburner 64e039
    if (!status) {
justburner 64e039
      DVGui::warning(
justburner 64e039
          QObject::tr("FFmpeg timed out.\n"
justburner 64e039
                      "Please check the file for errors.\n"
justburner 64e039
                      "If the file doesn't play or is incomplete, \n"
justburner 64e039
                      "Please try raising the FFmpeg timeout in Preferences."));
justburner 64e039
    }
justburner 64e039
    return status;
justburner 64e039
  }
justburner 64e039
justburner 64e039
  int exitCode = ThirdParty::waitAsyncProcess(ffmpeg, m_ffmpegTimeout);
justburner 69bbaf
  if (exitCode == 0) return true;
justburner 69bbaf
  if (exitCode == -1) {
justburner 69bbaf
    DVGui::warning(
justburner 69bbaf
        QObject::tr("FFmpeg returned error-code: %1").arg(ffmpeg.exitCode()));
justburner 69bbaf
  }
justburner 69bbaf
  if (exitCode == -2) {
justburner 69bbaf
    DVGui::warning(
justburner 69bbaf
        QObject::tr("FFmpeg timed out.\n"
justburner 69bbaf
                    "Please check the file for errors.\n"
justburner 69bbaf
                    "If the file doesn't play or is incomplete, \n"
justburner 69bbaf
                    "Please try raising the FFmpeg timeout in Preferences."));
justburner 69bbaf
  }
justburner 69bbaf
  return false;
justburner 69bbaf
}
justburner 69bbaf
turtletooth 04d8fd
void Ffmpeg::saveSoundTrack(TSoundTrack *st) {
turtletooth 04d8fd
  m_sampleRate    = st->getSampleRate();
turtletooth 04d8fd
  m_channelCount  = st->getChannelCount();
turtletooth 04d8fd
  m_bitsPerSample = st->getBitPerSample();
turtletooth 04d8fd
  // LONG count = st->getSampleCount();
turtletooth 04d8fd
  int bufSize         = st->getSampleCount() * st->getSampleSize();
turtletooth 04d8fd
  const UCHAR *buffer = st->getRawData();
turtletooth 04d8fd
Jeremy Bullock edaab7
  m_audioPath = getFfmpegCache().getQString() + "//" +
Jeremy Bullock edaab7
                QString::fromStdString(m_path.getName()) + "tempOut.raw";
turtletooth 04d8fd
  m_audioFormat = "s" + QString::number(m_bitsPerSample);
turtletooth 04d8fd
  if (m_bitsPerSample > 8) m_audioFormat = m_audioFormat + "le";
shun-iwasawa 8fb291
  std::string strPath = m_audioPath.toStdString();
turtletooth 04d8fd
turtletooth 04d8fd
  QByteArray data;
turtletooth 04d8fd
  data.insert(0, (char *)buffer, bufSize);
turtletooth 04d8fd
turtletooth 04d8fd
  QFile file(m_audioPath);
turtletooth 04d8fd
  file.open(QIODevice::WriteOnly);
turtletooth 04d8fd
  file.write(data);
turtletooth 04d8fd
  file.close();
turtletooth 04d8fd
  m_hasSoundTrack = true;
turtletooth 04d8fd
turtletooth 04d8fd
  m_audioArgs << "-f";
turtletooth 04d8fd
  m_audioArgs << m_audioFormat;
turtletooth 04d8fd
  m_audioArgs << "-ar";
turtletooth 04d8fd
  m_audioArgs << QString::number(m_sampleRate);
turtletooth 04d8fd
  m_audioArgs << "-ac";
turtletooth 04d8fd
  m_audioArgs << QString::number(m_channelCount);
turtletooth 04d8fd
  m_audioArgs << "-i";
turtletooth 04d8fd
  m_audioArgs << m_audioPath;
turtletooth 04d8fd
turtletooth 04d8fd
  // add file to framesWritten for cleanup
turtletooth 04d8fd
  m_cleanUpList.push_back(m_audioPath);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
bool Ffmpeg::checkFilesExist() {
turtletooth 04d8fd
  QString ffmpegCachePath = getFfmpegCache().getQString();
Jeremy Bullock 686e82
  QString tempPath = ffmpegCachePath + "//" + cleanPathSymbols() + "In0001." +
Jeremy Bullock 686e82
                     m_intermediateFormat;
turtletooth 04d8fd
  if (TSystem::doesExistFileOrLevel(TFilePath(tempPath))) {
turtletooth 04d8fd
    return true;
turtletooth 04d8fd
  } else
turtletooth 04d8fd
    return false;
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
ffmpegFileInfo Ffmpeg::getInfo() {
turtletooth 04d8fd
  QString ffmpegCachePath = getFfmpegCache().getQString();
Jeremy Bullock 686e82
  QString tempPath = ffmpegCachePath + "//" + cleanPathSymbols() + ".txt";
turtletooth 04d8fd
  if (QFile::exists(tempPath)) {
turtletooth 04d8fd
    QFile infoText(tempPath);
turtletooth 04d8fd
    infoText.open(QIODevice::ReadOnly);
turtletooth 04d8fd
    QByteArray ba = infoText.readAll();
turtletooth 04d8fd
    infoText.close();
justburner 64e039
    QString text         = QString::fromStdString(ba.toStdString());
justburner 188d84
    QStringList textlist = text.split(" ");
justburner 188d84
    if (textlist.count() >= 4) {
justburner 188d84
      m_lx         = textlist[0].toInt();
justburner 188d84
      m_ly         = textlist[1].toInt();
justburner 188d84
      m_frameRate  = textlist[2].toDouble();
justburner 188d84
      m_frameCount = textlist[3].toInt();
justburner 188d84
    }
turtletooth 04d8fd
  } else {
turtletooth 04d8fd
    QFile infoText(tempPath);
turtletooth 04d8fd
    getSize();
turtletooth 04d8fd
    getFrameCount();
manongjohn f2da01
    getFrameRate();
turtletooth 04d8fd
    infoText.open(QIODevice::WriteOnly);
turtletooth 04d8fd
    std::string infoToWrite =
turtletooth 04d8fd
        std::to_string(m_lx) + " " + std::to_string(m_ly) + " " +
turtletooth 04d8fd
        std::to_string(m_frameRate) + " " + std::to_string(m_frameCount);
turtletooth 04d8fd
    int infoLength = infoToWrite.length();
turtletooth 04d8fd
    infoText.write(infoToWrite.c_str(), infoLength);
turtletooth 04d8fd
    infoText.close();
turtletooth 04d8fd
  }
turtletooth 04d8fd
  ffmpegFileInfo info;
turtletooth 04d8fd
  info.m_lx         = m_lx;
turtletooth 04d8fd
  info.m_ly         = m_ly;
turtletooth 04d8fd
  info.m_frameRate  = m_frameRate;
turtletooth 04d8fd
  info.m_frameCount = m_frameCount;
turtletooth 04d8fd
  return info;
turtletooth 04d8fd
}
turtletooth 04d8fd
TRasterImageP Ffmpeg::getImage(int frameIndex) {
turtletooth 04d8fd
  QString ffmpegCachePath = getFfmpegCache().getQString();
Jeremy Bullock 686e82
  QString tempPath        = ffmpegCachePath + "//" + cleanPathSymbols();
Jeremy Bullock 686e82
  std::string tmpPath     = tempPath.toStdString();
turtletooth 04d8fd
  // QString tempPath= m_path.getQString();
turtletooth 04d8fd
  QString number   = QString("%1").arg(frameIndex, 4, 10, QChar('0'));
turtletooth 04d8fd
  QString tempName = "In" + number + ".png";
turtletooth 04d8fd
  tempName         = tempPath + tempName;
turtletooth 04d8fd
turtletooth 04d8fd
  // for debugging
turtletooth 04d8fd
  std::string strPath = tempName.toStdString();
turtletooth 04d8fd
  if (TSystem::doesExistFileOrLevel(TFilePath(tempName))) {
turtletooth 04d8fd
    QImage *temp = new QImage(tempName, "PNG");
turtletooth 04d8fd
    if (temp) {
turtletooth 04d8fd
      QImage tempToo = temp->convertToFormat(QImage::Format_ARGB32);
turtletooth 04d8fd
      delete temp;
turtletooth 04d8fd
      const UCHAR *bits = tempToo.bits();
turtletooth 04d8fd
turtletooth 04d8fd
      TRasterPT<tpixelrgbm32> ret;</tpixelrgbm32>
turtletooth 04d8fd
      ret.create(m_lx, m_ly);
turtletooth 04d8fd
      ret->lock();
turtletooth 04d8fd
      memcpy(ret->getRawData(), bits, m_lx * m_ly * 4);
turtletooth 04d8fd
      ret->unlock();
turtletooth 04d8fd
      ret->yMirror();
turtletooth 04d8fd
      return TRasterImageP(ret);
turtletooth 04d8fd
    }
Rozhuk Ivan 823a31
  }
Rozhuk Ivan 823a31
  return TRasterImageP();
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
double Ffmpeg::getFrameRate() {
manongjohn f2da01
  QStringList fpsArgs;
manongjohn d182c8
  int fpsNum = 0, fpsDen = 0;
manongjohn f2da01
  fpsArgs << "-v";
manongjohn f2da01
  fpsArgs << "error";
manongjohn f2da01
  fpsArgs << "-select_streams";
manongjohn f2da01
  fpsArgs << "v:0";
manongjohn f2da01
  fpsArgs << "-show_entries";
manongjohn f2da01
  fpsArgs << "stream=r_frame_rate";
manongjohn f2da01
  fpsArgs << "-of";
manongjohn f2da01
  fpsArgs << "default=noprint_wrappers=1:nokey=1";
manongjohn f2da01
  fpsArgs << m_path.getQString();
manongjohn f2da01
  QString fpsResults = runFfprobe(fpsArgs);
manongjohn f2da01
manongjohn d182c8
  QStringList fpsResultsList = fpsResults.split("/");
manongjohn d182c8
  if (fpsResultsList.size() > 1) {
manongjohn d182c8
    fpsNum = fpsResultsList[0].toInt();
manongjohn d182c8
    fpsDen = fpsResultsList[1].toInt();
manongjohn d182c8
  }
manongjohn d182c8
manongjohn d182c8
  // if for some reason we don't have enough info to calculate it. Use the
manongjohn d182c8
  // avg_frame_rate
manongjohn d182c8
  if (!fpsDen) {
manongjohn d182c8
    fpsArgs.clear();
manongjohn d182c8
    fpsArgs << "-v";
manongjohn d182c8
    fpsArgs << "error";
manongjohn d182c8
    fpsArgs << "-select_streams";
manongjohn d182c8
    fpsArgs << "v:0";
manongjohn d182c8
    fpsArgs << "-show_entries";
manongjohn d182c8
    fpsArgs << "stream=avg_frame_rate";
manongjohn d182c8
    fpsArgs << "-of";
manongjohn d182c8
    fpsArgs << "default=noprint_wrappers=1:nokey=1";
manongjohn d182c8
    fpsArgs << m_path.getQString();
manongjohn d182c8
    QString fpsResults = runFfprobe(fpsArgs);
manongjohn d182c8
manongjohn d182c8
    fpsResultsList = fpsResults.split("/");
manongjohn d182c8
    if (fpsResultsList.size() > 1) {
manongjohn d182c8
      fpsNum = fpsResultsList[0].toInt();
manongjohn d182c8
      fpsDen = fpsResultsList[1].toInt();
manongjohn d182c8
    }
manongjohn d182c8
  }
manongjohn d182c8
manongjohn f2da01
  if (fpsDen > 0) {
manongjohn f2da01
    m_frameRate = (double)fpsNum / (double)fpsDen;
turtletooth 04d8fd
  }
turtletooth 04d8fd
  return m_frameRate;
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
TDimension Ffmpeg::getSize() {
turtletooth 04d8fd
  QStringList sizeArgs;
turtletooth 04d8fd
  sizeArgs << "-v";
turtletooth 04d8fd
  sizeArgs << "error";
turtletooth 04d8fd
  sizeArgs << "-of";
turtletooth 04d8fd
  sizeArgs << "flat=s=_";
turtletooth 04d8fd
  sizeArgs << "-select_streams";
turtletooth 04d8fd
  sizeArgs << "v:0";
turtletooth 04d8fd
  sizeArgs << "-show_entries";
turtletooth 04d8fd
  sizeArgs << "stream=height,width";
turtletooth 04d8fd
  sizeArgs << m_path.getQString();
turtletooth 04d8fd
turtletooth 04d8fd
  QString sizeResults = runFfprobe(sizeArgs);
turtletooth 04d8fd
  QStringList split   = sizeResults.split("\n");
turtletooth 04d8fd
  m_lx                = split[0].split("=")[1].toInt();
turtletooth 04d8fd
  m_ly                = split[1].split("=")[1].toInt();
turtletooth 04d8fd
  return TDimension(m_lx, m_ly);
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
int Ffmpeg::getFrameCount() {
manongjohn d182c8
  // nb_read_frames from files may not be accurate. Let's calculate it based on
manongjohn d182c8
  // r_frame_rate * duration
turtletooth 04d8fd
  QStringList frameCountArgs;
turtletooth 04d8fd
  frameCountArgs << "-v";
turtletooth 04d8fd
  frameCountArgs << "error";
turtletooth 04d8fd
  frameCountArgs << "-count_frames";
turtletooth 04d8fd
  frameCountArgs << "-select_streams";
turtletooth 04d8fd
  frameCountArgs << "v:0";
turtletooth 04d8fd
  frameCountArgs << "-show_entries";
manongjohn f2da01
  frameCountArgs << "stream=duration";
turtletooth 04d8fd
  frameCountArgs << "-of";
turtletooth 04d8fd
  frameCountArgs << "default=nokey=1:noprint_wrappers=1";
turtletooth 04d8fd
  frameCountArgs << m_path.getQString();
turtletooth 04d8fd
turtletooth 04d8fd
  QString frameResults = runFfprobe(frameCountArgs);
manongjohn f2da01
  m_frameCount         = frameResults.toDouble() * getFrameRate();
manongjohn d182c8
manongjohn d182c8
  // if for some reason we don't have enough info to calculate it. Use the
manongjohn d182c8
  // nb_read_frames
manongjohn d182c8
  if (!m_frameCount) {
manongjohn d182c8
    frameCountArgs.clear();
manongjohn d182c8
    frameCountArgs << "-v";
manongjohn d182c8
    frameCountArgs << "error";
manongjohn d182c8
    frameCountArgs << "-count_frames";
manongjohn d182c8
    frameCountArgs << "-select_streams";
manongjohn d182c8
    frameCountArgs << "v:0";
manongjohn d182c8
    frameCountArgs << "-show_entries";
manongjohn d182c8
    frameCountArgs << "stream=nb_read_frames";
manongjohn d182c8
    frameCountArgs << "-of";
manongjohn d182c8
    frameCountArgs << "default=nokey=1:noprint_wrappers=1";
manongjohn d182c8
    frameCountArgs << m_path.getQString();
manongjohn d182c8
manongjohn d182c8
    frameResults = runFfprobe(frameCountArgs);
manongjohn d182c8
    m_frameCount = frameResults.toInt();
manongjohn d182c8
  }
manongjohn d182c8
turtletooth 04d8fd
  return m_frameCount;
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::getFramesFromMovie(int frame) {
turtletooth 04d8fd
  QString ffmpegCachePath = getFfmpegCache().getQString();
Jeremy Bullock 686e82
  QString tempPath        = ffmpegCachePath + "//" + cleanPathSymbols();
Jeremy Bullock 686e82
  std::string tmpPath     = tempPath.toStdString();
Jeremy Bullock 686e82
  QString tempName        = "In%04d." + m_intermediateFormat;
Jeremy Bullock 686e82
  tempName                = tempPath + tempName;
turtletooth 04d8fd
  QString tempStart;
turtletooth 04d8fd
  if (frame == -1) {
turtletooth 04d8fd
    tempStart = "In0001." + m_intermediateFormat;
turtletooth 04d8fd
    tempStart = tempPath + tempStart;
turtletooth 04d8fd
  } else {
turtletooth 04d8fd
    QString number = QString("%1").arg(frame, 4, 10, QChar('0'));
turtletooth 04d8fd
    tempStart      = tempPath + "In" + number + "." + m_intermediateFormat;
turtletooth 04d8fd
  }
turtletooth 04d8fd
  QString tempBase = tempPath + "In";
turtletooth 04d8fd
  QString addToDelete;
turtletooth 04d8fd
  if (!TSystem::doesExistFileOrLevel(TFilePath(tempStart))) {
turtletooth 04d8fd
    // for debugging
turtletooth 04d8fd
    std::string strPath = tempName.toStdString();
turtletooth 04d8fd
turtletooth 04d8fd
    QStringList preIFrameArgs;
turtletooth 04d8fd
    QStringList postIFrameArgs;
turtletooth 04d8fd
    // frameArgs << "-accurate_seek";
turtletooth 04d8fd
    // frameArgs << "-ss";
turtletooth 04d8fd
    // frameArgs << "0" + QString::number(frameIndex / m_info->m_frameRate);
justburner 188d84
    if (m_path.getType() == "webm") {
justburner 188d84
      // To load in webm transparency
justburner 188d84
      preIFrameArgs << "-vcodec";
justburner 188d84
      preIFrameArgs << "libvpx";
justburner 188d84
    }
turtletooth 04d8fd
    preIFrameArgs << "-i";
turtletooth 04d8fd
    preIFrameArgs << m_path.getQString();
turtletooth 04d8fd
    postIFrameArgs << "-y";
turtletooth 04d8fd
    postIFrameArgs << "-f";
turtletooth 04d8fd
    postIFrameArgs << "image2";
turtletooth 04d8fd
turtletooth 04d8fd
    postIFrameArgs << tempName;
turtletooth 04d8fd
justburner 64e039
    runFfmpeg(preIFrameArgs, postIFrameArgs, true, true, true, false);
turtletooth 04d8fd
turtletooth 04d8fd
    for (int i = 1; i <= m_frameCount; i++) {
turtletooth 04d8fd
      QString number      = QString("%1").arg(i, 4, 10, QChar('0'));
turtletooth 04d8fd
      addToDelete         = tempBase + number + "." + m_intermediateFormat;
turtletooth 04d8fd
      std::string delPath = addToDelete.toStdString();
turtletooth 04d8fd
      // addToCleanUp(addToDelete);
turtletooth 04d8fd
    }
turtletooth 04d8fd
  }
turtletooth 04d8fd
}
turtletooth 04d8fd
Jeremy Bullock 686e82
QString Ffmpeg::cleanPathSymbols() {
Jeremy Bullock 686e82
  return m_path.getQString().remove(QRegExp(
shun-iwasawa a1dbd7
      QString::fromUtf8("[-`~!@#$%^&*()_—+=|:;<>«»,.?/{}\'\"\\[\\]\\\\]")));
Jeremy Bullock 686e82
}
Jeremy Bullock 686e82
Jeremy Bullock 779cdf
int Ffmpeg::getGifFrameCount() {
Jeremy Bullock 779cdf
  int frame               = 1;
Jeremy Bullock 779cdf
  QString ffmpegCachePath = getFfmpegCache().getQString();
Jeremy Bullock 686e82
  QString tempPath        = ffmpegCachePath + "//" + cleanPathSymbols();
Jeremy Bullock 686e82
  std::string tmpPath     = tempPath.toStdString();
Jeremy Bullock 686e82
  QString tempName        = "In%04d." + m_intermediateFormat;
Jeremy Bullock 686e82
  tempName                = tempPath + tempName;
Jeremy Bullock 779cdf
  QString tempStart;
Jeremy Bullock 779cdf
  tempStart = "In0001." + m_intermediateFormat;
Jeremy Bullock 779cdf
  tempStart = tempPath + tempStart;
Jeremy Bullock 779cdf
  while (TSystem::doesExistFileOrLevel(TFilePath(tempStart))) {
Jeremy Bullock 779cdf
    frame++;
Jeremy Bullock 779cdf
    QString number = QString("%1").arg(frame, 4, 10, QChar('0'));
Jeremy Bullock 779cdf
    tempStart      = tempPath + "In" + number + "." + m_intermediateFormat;
Jeremy Bullock 779cdf
  }
Jeremy Bullock 779cdf
  return frame - 1;
Jeremy Bullock 779cdf
}
Jeremy Bullock 779cdf
turtletooth 04d8fd
void Ffmpeg::addToCleanUp(QString path) {
turtletooth 04d8fd
  if (TSystem::doesExistFileOrLevel(TFilePath(path))) {
turtletooth 04d8fd
    m_cleanUpList.push_back(path);
turtletooth 04d8fd
  }
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::cleanUpFiles() {
turtletooth 04d8fd
  for (QString path : m_cleanUpList) {
turtletooth 04d8fd
    if (TSystem::doesExistFileOrLevel(TFilePath(path))) {
turtletooth 04d8fd
      TSystem::deleteFile(TFilePath(path));
turtletooth 04d8fd
    }
turtletooth 04d8fd
  }
turtletooth 04d8fd
}
turtletooth 04d8fd
turtletooth 04d8fd
void Ffmpeg::disablePrecompute() {
turtletooth 04d8fd
  Preferences::instance()->setPrecompute(false);
justburner 64e039
}
justburner 64e039
justburner 64e039
//===========================================================
justburner 64e039
//
justburner 64e039
//  TImageReaderFFmpeg
justburner 64e039
//
justburner 64e039
//===========================================================
justburner 64e039
justburner 64e039
class TImageReaderFFmpeg final : public TImageReader {
justburner 64e039
public:
justburner 64e039
  int m_frameIndex;
justburner 64e039
justburner 64e039
  TImageReaderFFmpeg(const TFilePath &path, int index, TLevelReaderFFmpeg *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
  ~TImageReaderFFmpeg() { 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
  TLevelReaderFFmpeg *m_lra;
justburner 64e039
  TImageInfo *m_info;
justburner 64e039
justburner 64e039
  // not implemented
justburner 64e039
  TImageReaderFFmpeg(const TImageReaderFFmpeg &);
justburner 64e039
  TImageReaderFFmpeg &operator=(const TImageReaderFFmpeg &src);
justburner 64e039
};
justburner 64e039
justburner 64e039
//===========================================================
justburner 64e039
//
justburner 64e039
//  TLevelReaderFFmpeg
justburner 64e039
//
justburner 64e039
//===========================================================
justburner 64e039
justburner 64e039
TLevelReaderFFmpeg::TLevelReaderFFmpeg(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
TLevelReaderFFmpeg::~TLevelReaderFFmpeg() {}
justburner 64e039
justburner 64e039
//-----------------------------------------------------------
justburner 64e039
justburner 64e039
TLevelP TLevelReaderFFmpeg::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 TLevelReaderFFmpeg::getFrameReader(TFrameId fid) {
justburner 64e039
  if (!fid.getLetter().isEmpty()) return TImageReaderP(0);
justburner 64e039
  int index = fid.getNumber();
justburner 64e039
justburner 64e039
  TImageReaderFFmpeg *irm = new TImageReaderFFmpeg(m_path, index, this, m_info);
justburner 64e039
  return TImageReaderP(irm);
justburner 64e039
}
justburner 64e039
justburner 64e039
//------------------------------------------------------------------------------
justburner 64e039
justburner 64e039
TDimension TLevelReaderFFmpeg::getSize() { return m_size; }
justburner 64e039
justburner 64e039
//------------------------------------------------
justburner 64e039
justburner 64e039
TImageP TLevelReaderFFmpeg::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
}