Nuke error writing frame to file

Recently I was working on a short film called "June", directed by actor/director Dwayne Cameron . Unfortunately, the render farm software in the facility is under maintenance. Fortunately, it opened up an opportunity for me to look into Frame Server in Nuke 11 to learn how to utilize th

Recently I was working on a short film called «June», directed by actor/director Dwayne Cameron. Unfortunately, the render farm software in the facility is under maintenance. Fortunately, it opened up an opportunity for me to look into Frame Server in Nuke 11 to learn how to utilize the whole room of computers around me! Basically, frame server is now a standard feature in Nuke (Not only in NukeStudio), it’s kind of like a render farm software that comes with Nuke itself, that allows you to distribute your nuke script for other computers to render.

Surprisingly there’s not a lot of documentation out there and referencing The Foundry’s guide and took me a while, so here’s what I figured out.

Steps

To clarify, a frame server is like any render farm system. There’s your main workstation which you use, and the slave machines you want to use for render only. Below are the things you requires to get frame server up and running.

  • Main workstation’s IP address/System name (You don’t need the slaves’ IP or system name, just the main)

    • you can get the former by executing ipconfig in cmd and look for the IPv4 address. As for system name just look at the computer properties.

  • Make sure each slave machines have the same nuke version installed and running on the same network.

  • Write the batch file which you can open from each slaves (Which I will explain below)

  • Run the batch file on each slave machines.

    • if it’s running correctly, the command prompt window should stay opens.

    • Any error you might encounter will be logged on this window, until you close it.

  • Run this command on your main workstation in nuke to see if your slaves are connected:

  • If they shows up, you have your frame server running!

  • Once you are done and wish to terminate frame server, simply close the command prompt window.

Writing batch file for your slave machines

  1. For the batch file, you need to create it with a text editor. Window’s notepad works just fine since it’s short but I would recommend Notepad++ or some more advance editors. First, change directory to wherever your nuke directory is. This is because you need to start frame server with python, and not only nuke directory already has python version. It also makes it easier for later step.

  2. Next runs runframeserver.py with Python, which should be in your nuke directory.

  3. Since we’re already in Nuke directory, it should be as simple as

In 2. step above you need to include arguments required to run frameserver, which is explained in The Foundry’s guide. Here’s what my final batch file looks like after putting in the required arguments.

Pay attention to arguments

—workerconnecturl

  • What you really needs to pay attention in step 3 is the —workerconnecturl. Which I spent most of my time figuring it out. In my case it is tcl://2017C018:5560

  • 2017C018 is the name of my main workstation, where I distribute jobs to the slave machines. You can also use IP address for this which you can get from running ipconfig. Personally, I prefer system name as its easier to remember. 5560 is nuke default communication port, which shouldn’t change. Nuke path is where your nuke exe resides, if you changed your directory in the beginning of this script it should be just —nukepath=nuke(version).exe

—useInteractiveLicense

By default frameserver will launch with a nuke_r license, but the facility I was in only has nuke_i available, hence the last flag —useInteractiveLicense

Saving the file

Finally, save this file as a batch file, *.bat I named it as frameserver2017C018.bat so I know which main workstation is the bat file written for. As mentioned you can run the same bat file on any machines on the same network, and they’ll all be slaves for your main machine.

And that’s it! Apologies for my messy code, but it’s only a bat file with 2 lines! I hope this short guide here is elaborate enough for anyone who’s reading this to get their own frame server running!

Gday.

We are trying submit nuke scripts with centos7 and w10 Through render selected panel inside Nuke but we got this output error message :

Starting CGRU Nuke add-ons from: /opt/cgru/plugins/nuke Unknown switch: -r C:cgru.2.2.3pluginsnukerender.py : No such file or directory Exception IOError: (2, ‘No such file or directory’, ‘C:cgru.2.2.3pluginsnukerender.py’) in <module ‘threading’ from ‘/usr/local/Nuke11.2v4/lib/python2.7/threading.py’> ignored

we use this great dispatcher daily with maya scenes smoothly whit different OS by Afanasy Starter windows .
Here our config json config file

{«cgru_config»:{
«»:»This is a CGRU default config file»,
«»:»Describe your own configuration in ‘config.json’, it will be included last»,
«»:»To disable property you can just prefix it with ‘-‘»,

«include»:[«afanasy/config_default.json»,»config.json»],

«»:»‘OS_unix’: Any UNIX (Linux, MacOSX) specific settings»,
«»:»‘OS_windows’: MS Windows specific settings»,
«»:»‘OS_linux’: Linix specific settings»,
«»:»‘OS_macosx’: MacOSX specific settings»,

«»:»Digest authentication realm»,
«realm»:»CGRU»,

«»:»To open location/shot in RULES:»,
«-rules_url»:»http://localhost/cgru/#/»,
«»:»By default it is disabled»,

«projects_root»:[«/mnt/prj/»,»/projects/»,»/cgru/rules_root/»],

«nuke_filenamefilter»:true,

«open_folder_cmd»:»open «@PATH@»»,

«previewcmds»:[
«Nuke|nuke -v «@ARG@»»,
«Mplay|mplay «@ARG@»»,
«Mplay *|mplay `dirname «@ARG@»`/*»,
«DJV|djv_view «@ARG@»»,
«IMDisplay|display «@ARG@»»,
«XTerm Folder|cd `dirname @ARG@` && xterm»
],
«annotations»:[
«APPROVED»,
«FAILED»
],

«docs_url»:»http://cgru.info»,
«forum_url»:»http://forum.cgru.info»,

«»:»Configure SMTP server to send emails from»,
«email_smtp_server»:»localhost»,
«»:»You can keep ‘localhost’, but configure machines with RULES server»,
«»:» and Afanasy ‘events’ service to have such service running»,

«»:»This command provided with CGRU and uses utilities/emailsend.py Python script»,
«email_send_cmd»:»emailsend»,

«email_sender_address_host»:»cgru.info»,

«keeper_port»:50999,
«keeper_port_https»:50443,

«timeformat»:»%a %d %b %H:%M.%S»,
«»:»‘strftime’ function format http://www.cplusplus.com/reference/clib … e/strftime»,

«filenamesizemax»:250,

«platforms»:[«linux»,»mac»,»windows»],

«OS_unix»:{
«cmd_shell»:»/bin/sh -c»
},
«OS_windows»:{
«open_folder_cmd»:»explorer «@PATH@»»,
«cmd_shell»:»cmd.exe /c»,

«»:»This is a paths map example (disabled)»,
«»:»Client:Server»,
«pathsmap»:[
[«X:/»,»/mnt/nas/»],
],
«»:»Client slashes direction plays no role, as it tries both slashes when searchs a path to replace.»,
«»:»But will replace with specified slashes, if unix slashes not forced (for Nuke, for example).»
},
«OS_linux»:{
«open_folder_cmd»:»nautilus «@PATH@»»,
«pathsmap»:[
[«/mnt/nas/»,»X:/»],
]
},

«pswd_visor»:»1832116180fdc61b64fd978401e462e9″,
«pswd_god»:»73bcaaa458bff0d27989ed331b68b64d»,

«»:»»
}}

Do you have any advice for fixing the Nuke issue.
Thank u in advance.
Band tm

On 26/06/12 03:18, mgriffith wrote:

You mentioned you wrote a new ffmpegReader that works with current
ffmpeg libraries; would you be willing to share it? I'm having a doozy
of a time dealing with reading in ProRes files in linux Sad

Hi guys,

These are *unsupported* in 6.3, so you’re on your own with these, but
the attached files build against ffmpeg from February and *should* work
with the current ffmpeg API.


You’ll obviously have to use it against a newer version of the ffmpeg
libs (our bundled ones won’t work for 6.x), but the end result appears
to be several in-demand codecs work surprisingly well through ffmpeg :)

Peter
--
Peter Pearson, Software Engineer
The Foundry, 6th Floor, The Communications Building,
48 Leicester Square, London, UK, WC2H 7LT
Tel: +44 (0)20 7434 0449   Web: www.thefoundry.co.uk

The Foundry Visionmongers Ltd.
Registered in England and Wales No: 4642027
// Copyright (c) 2009 The Foundry Visionmongers Ltd.  All Rights Reserved.

#include "Build/fnBuild.h"
#include "DDImage/DDString.h"
#include "DDImage/Reader.h"
#include "DDImage/Row.h"

#ifdef _WIN32
  #include <io.h>
#endif

#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)

extern "C" {
#include <errno.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <libavutil/avutil.h>
#include <libavutil/error.h>
}

using namespace DD::Image;

namespace
{
  void ffmpegError(int error, char* errorBuf, size_t bufSize)
  {
    av_strerror(error, errorBuf, bufSize);
  }
}

class ffmpegReader : public Reader
{
public:
  explicit ffmpegReader(Read* iop);
  ~ffmpegReader();

  virtual bool videosequence() const { return true; }

  void engine(int y, int x, int r, ChannelMask channels, Row& row);
  void open();
  
private:
  MetaData::Bundle meta;
  static const Reader::Description d;

public:
  const MetaData::Bundle& fetchMetaData(const char* key) { return meta; }

private:
  bool findStreamInfo();
  bool hasVideo() const { return !videoIdx_.empty(); }
  AVStream* getVideoStream() { return currVideoIdx_ >= 0 ? context_->streams[videoIdx_[currVideoIdx_]] : NULL; }
  
  void setMetaDataItem(MetaData::Bundle& metadata, const std::string& DDIkey, AVDictionary* dict, const char* ffmpegKey);
  void setMetaDataItem(MetaData::Bundle& metadata, const std::string& DDIkey, const char* stringValue);
  
  double fps() const;
  void openVideoCodec();
  void closeVideoCodec();
  int64_t getTimeStamp(int pos) const;
  bool seek(int pos);
  bool decodeImage();
  int width() const { return width_; }
  int height() const { return height_; }
  
  // use internal frame query function, as ffmpeg uses 0-based frames for most codecs
  // so they might not be the same as Nuke's frames
  int ffmpegFrame() const;
  
  bool isCodecBlacklisted(const char* name);

private:
  AVFormatContext* context_;
  AVInputFormat* format_;
  AVFrame* avFrame_;
  AVCodec* videoCodec_;
  AVPacket pkt_;
  SwsContext* convertCtx_;
  std::vector<int> videoIdx_;
  int fpsNum_;
  int fpsDen_;
  int currVideoIdx_;
  uint64_t frames_;
  int width_;
  int height_;
  double aspect_;
  std::vector<unsigned char> data_;
  bool offsetTime_;
  int lastSearchPos_;
  int lastDecodedPos_;
  bool codecZeroBasedStart_;
  
  bool  _invalidState;
};

static const char* codecBlacklist[] =
{
  "r10k",
  "ogg",
  "mpjpeg",
  "asf",
  "asf_stream",
  "h261",
  "h263",
  "rcv",
  "yuv4mpegpipe",
  "prores",
  "dnxhd",
  "swf",
  "dv",
  "ipod",
  "psp",
  "image2",
  "3g2",
  "3gp",
  "RoQ",
  
  #ifdef FN_OS_WINDOWS
  
  "dirac",
  "ffm",
  
  #elif FN_OS_MAC
  
  "mjpeg",
  "vc1",
  
  #elif FN_OS_LINUX
  
  "mjpeg",
  "vc1",  
  
  #endif
  
  NULL
  
};

ffmpegReader::ffmpegReader(Read* iop)
  : Reader(iop)
  , context_(NULL)
  , format_(NULL)
  , avFrame_(NULL)
  , videoCodec_(NULL)
  , convertCtx_(NULL)
  , fpsNum_(0)
  , fpsDen_(0)
  , currVideoIdx_(-1)
  , frames_(0)
  , width_(720)
  , height_(576)
  , aspect_(1.0f)
  , offsetTime_(true)
  , lastSearchPos_(-1)
  , lastDecodedPos_(-1)
  , codecZeroBasedStart_(true)
  , _invalidState(false)
{
  av_log_set_level(AV_LOG_WARNING);
  av_register_all();

  avFrame_ = avcodec_alloc_frame();

  // FIXME_GC: shouldn't the plugin be passed the filename without the prefix?
  int offset = 0;
  std::string filename(iop->filename());
  if (filename.find("ffmpeg:") != std::string::npos)
    offset = 7;
  int error = avformat_open_input(&context_, iop->filename() + offset, format_, NULL);
  if (error < 0) {
    char szError[1024];
    ffmpegError(error, szError, 1024);
    iop->error(szError);
  }
  else {
    // FIXME_GC: needs to know if it's streamable.
    error = avformat_find_stream_info(context_, NULL);
    if (error < 0) {
      char szError[1024];
      ffmpegError(error, szError, 1024);
      iop->error(szError);
    }
    else {
      if (findStreamInfo() && getVideoStream()) {
        AVCodecContext* codecContext = getVideoStream()->codec;
        
        if (isCodecBlacklisted(codecContext->codec->name)) {
          iop->error("unsupported codec...");
          _invalidState = true;
          return;
        }
        
        _invalidState = false;
        
        if (getVideoStream()->sample_aspect_ratio.num)
          aspect_ = av_q2d(getVideoStream()->sample_aspect_ratio);
        else if (codecContext->sample_aspect_ratio.num)
          aspect_ = av_q2d(codecContext->sample_aspect_ratio);

        info_.channels(Mask_RGBA);
        set_info(width_, height_, 3, aspect_);
        info_.first_frame(1);
        info_.last_frame(frames_);

        data_.resize(width() * height() * 3);

        // hack so seeking works from our intended position.
        if (!strcmp(codecContext->codec->name, "mjpeg") || !strcmp(codecContext->codec->name, "dvvideo"))
          offsetTime_ = false;
        
        // H264 codec uses a different initial frame than the other codecs
        if (!strcmp(codecContext->codec->name, "h264"))
          codecZeroBasedStart_ = false;
        
        AVDictionary* metadata = context_->metadata;
        
        setMetaDataItem(meta, MetaData::CREATOR, metadata, "author");
        setMetaDataItem(meta, MetaData::COMMENT, metadata, "comment");
        setMetaDataItem(meta, MetaData::PROJECT, metadata, "album");
        setMetaDataItem(meta, MetaData::COPYRIGHT, metadata, "copyright");
        
        meta.setData("ffmpeg/num_streams", context_->nb_streams);
        setMetaDataItem(meta, "ffmpeg/codec/codecName", codecContext->codec->name);
        
        meta.setData(MetaData::FRAME_RATE, fps());
        meta.setData("ffmpeg/codec/timecodeFrameStart", (unsigned int)codecContext->timecode_frame_start);
        meta.setData("ffmpeg/codec/startTime", (unsigned int)context_->start_time);
      }
      else {
        iop->error("unable to find codec");
        _invalidState = true;
      }
    }
  }
}

ffmpegReader::~ffmpegReader()
{
  closeVideoCodec();
  if (context_)
    avformat_close_input(&context_);
  av_free(avFrame_);
  av_free(convertCtx_);
}

void ffmpegReader::engine(int y, int x, int rx, ChannelMask channels, Row& out)
{
  foreach ( z, channels ) {
    float* TO = out.writable(z) + x;
    unsigned char* FROM = &data_[0];
    FROM += (height() - y - 1) * width() * 3;
    FROM += x * 3;
    from_byte(z, TO, FROM + z - 1, NULL, rx - x, 3);
  }
}

void ffmpegReader::open()
{
  int internalFrame = ffmpegFrame();
  
  if (lastDecodedPos_ + 1 != internalFrame) {
    seek(0);
    seek(internalFrame);
  }

  av_init_packet(&pkt_);

  bool hasPicture = false;
  int error = 0;
  while (error >= 0 && !hasPicture) {
    error = av_read_frame(context_, &pkt_);
    if (error < 0)
      break;

    if (error >= 0 && videoIdx_.size() && currVideoIdx_ != -1 && pkt_.stream_index == videoIdx_[currVideoIdx_])
      hasPicture = decodeImage();
    av_free_packet(&pkt_);
  }
}

bool ffmpegReader::findStreamInfo()
{
  for (int i = 0; i < static_cast<int>(context_->nb_streams); ++i) {
    AVCodecContext* codecContext = context_->streams[i]->codec;
    if (avcodec_find_decoder(codecContext->codec_id) == NULL)
      continue;

    switch (codecContext->codec_type) {
      case AVMEDIA_TYPE_VIDEO:
        videoIdx_.push_back(i);
        if (currVideoIdx_ < 0)
          currVideoIdx_ = 0;
        width_ = codecContext->width;
        height_ = codecContext->height;
        break;

      // ignore all audio streams
      case AVMEDIA_TYPE_AUDIO:
      default:
        break;
    }
  }

  if (!hasVideo())
    return false;

  AVStream* stream = getVideoStream();
  if (stream->r_frame_rate.num != 0 && stream->r_frame_rate.den != 0) {
    fpsNum_ = stream->r_frame_rate.num;
    fpsDen_ = stream->r_frame_rate.den;
  }

  openVideoCodec();

  // Set the duration
  if ((uint64_t)context_->duration != AV_NOPTS_VALUE)
    frames_ = uint64_t((fps() * (double)context_->duration / (double)AV_TIME_BASE));
  else
    frames_ = 1 << 29;

  // try to calculate the number of frames
  if (!frames_) {
    seek(0);
    av_init_packet(&pkt_);
    av_read_frame(context_, &pkt_);
    uint64_t firstPts = pkt_.pts;
    uint64_t maxPts = firstPts;
    seek(1 << 29);
    av_init_packet(&pkt_);
    while (stream && av_read_frame(context_, &pkt_) >= 0) {
      uint64_t currPts = (uint64_t)(av_q2d(getVideoStream()->time_base) * (pkt_.pts - firstPts) * fps());
      if (currPts > maxPts)
        maxPts = currPts;
    }

    frames_ = maxPts;
  }

  return true;
}

void ffmpegReader::setMetaDataItem(MetaData::Bundle& metadata, const std::string& DDIkey, AVDictionary* dict, const char* ffmpegKey)
{
  if (!dict)
    return;
  
   AVDictionaryEntry* dictEntry = av_dict_get(dict, ffmpegKey, NULL, 0);
   if (!dictEntry)
     return;
   
   metadata.setData(DDIkey, dictEntry->value);
}

void ffmpegReader::setMetaDataItem(MetaData::Bundle& metadata, const std::string& DDIkey, const char* stringValue)
{
  if (!stringValue)
    return;
  
  metadata.setData(DDIkey, stringValue);
}

double ffmpegReader::fps() const
{
  if (fpsDen_)
    return fpsNum_ / (double)fpsDen_;
  return 1.0f;
}

void ffmpegReader::openVideoCodec()
{
  AVStream* stream = getVideoStream();
  AVCodecContext* codecContext = stream->codec;
  videoCodec_ = avcodec_find_decoder(codecContext->codec_id);
  if (videoCodec_ == NULL || avcodec_open2(codecContext, videoCodec_, NULL) < 0)
    currVideoIdx_ = -1;
}

void ffmpegReader::closeVideoCodec()
{
  AVStream* stream = getVideoStream();
  if (stream && stream->codec)
    avcodec_close(stream->codec);
}

int64_t ffmpegReader::getTimeStamp(int pos) const
{
  int64_t timestamp = (int64_t)(((double) pos / fps()) * AV_TIME_BASE);
  if ((uint64_t) context_->start_time != AV_NOPTS_VALUE)
    timestamp += context_->start_time;
  return timestamp;
}

bool ffmpegReader::seek(int pos)
{
  int64_t offset = getTimeStamp(pos);
  if (offsetTime_) {
    offset -= AV_TIME_BASE;
    if (offset < context_->start_time)
      offset = 0;
  }

  avcodec_flush_buffers(getVideoStream()->codec);
  if (av_seek_frame(context_, -1, offset, AVSEEK_FLAG_BACKWARD) < 0)
    return false;

  return true;
}

bool ffmpegReader::decodeImage()
{
  if (_invalidState)
    return false;
  
  // search for our picture.
  double pts = 0;
  if ((uint64_t) pkt_.dts != AV_NOPTS_VALUE)
    pts = av_q2d(getVideoStream()->time_base) * pkt_.dts;

  int curPos = int(pts * fps() + 0.5f);
  if (curPos == lastSearchPos_)
    curPos = lastSearchPos_ + 1;
  lastSearchPos_ = curPos;

  if ((uint64_t)context_->start_time != AV_NOPTS_VALUE)
    curPos -= int(context_->start_time * fps() / AV_TIME_BASE);

  int hasPicture = 0;
  int curSearch = 0;
  AVCodecContext* codecContext = getVideoStream()->codec;
  if (curPos >= ffmpegFrame())
    avcodec_decode_video2(codecContext, avFrame_, &hasPicture, &pkt_);
  else if (offsetTime_)
    avcodec_decode_video2(codecContext, avFrame_, &curSearch, &pkt_);

  if (!hasPicture)
    return false;

  lastDecodedPos_ = lastSearchPos_;
  
  AVPicture output;
  avpicture_fill(&output, &data_[0], PIX_FMT_RGB24, width_, height_);
  
  if (!convertCtx_) {
    convertCtx_ = sws_getContext(width_, height_, codecContext->pix_fmt, width_, height_,
                                 PIX_FMT_RGB24, SWS_BICUBIC, NULL, NULL, NULL);
  }

  sws_scale(convertCtx_, avFrame_->data, avFrame_->linesize, 0, height_, output.data, output.linesize);
  return true;
}

int ffmpegReader::ffmpegFrame() const
{
  int internalFrame = frame();
  // ffmpeg uses 0-based frames for most codecs, so we need to convert Nuke's 1-based frames
  // H264 seems to still use 1-based, however
  if (codecZeroBasedStart_)
    internalFrame --;
  
  return internalFrame;
}

bool ffmpegReader::isCodecBlacklisted(const char* name)
{
  const char** iterator = codecBlacklist;
  
  while (*iterator != NULL) {
    if (strncmp(name, *iterator, strlen(*iterator)) == 0) {
      return true;
    }
    
    ++iterator;
  }
  return false;
}

static Reader* build(Read* iop, int fd, const unsigned char* b, int n)
{
  ::close(fd);
  return new ffmpegReader(iop);
}

static bool test(int fd, const unsigned char* block, int length)
{
  return true;
}

const Reader::Description ffmpegReader::d("ffmpeg", build, test);
// Copyright (c) 2009 The Foundry Visionmongers Ltd.  All Rights Reserved.

#include "Build/fnBuild.h"
#include "DDImage/DDString.h"
#include "DDImage/Writer.h"
#include "DDImage/Row.h"
#include "DDImage/Knobs.h"

#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)

extern "C" {
#include <errno.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include "libavutil/imgutils.h"
#include "libavformat/avio.h"
#include <libavutil/opt.h>
#include <libswscale/swscale.h>
#include <libavutil/avutil.h>
#include <libavutil/error.h>
}

using namespace DD::Image;

class ffmpegWriter : public Writer
{
private:
  enum WriterError { SUCCESS = 0, IGNORE_FINISH, CLEANUP };
  
public:
  explicit ffmpegWriter(Write* iop);
  ~ffmpegWriter();
  
  virtual bool movie() const { return true; }
  
  void execute();
  void finish();
  void knobs(Knob_Callback f);
  static const Writer::Description d;
  
private:
  void freeFormat();
  bool isCodecBlacklisted(const char* name);
  
private:
  AVCodecContext*   codecContext_;
  AVFormatContext*  formatContext_;
  AVStream* stream_;
  std::vector<std::string> formatsLongNames_;
  std::vector<const char*> formatsShortNames_;
  std::vector<const char*> codecsLongNames_;
  std::vector<const char*> codecsShortNames_;
  
  WriterError error_;
  // knobs variables
  float fps_;
  int format_;
  int codec_;
  int bitrate_;
  int bitrateTolerance_;
  int gopSize_;
  int bFrames_;
  int mbDecision_;
};

static const char* codecBlacklist[] =
{
  "r10k",
  "ogg",
  "mpjpeg",
  "asf",
  "asf_stream",
  "h261",
  "h263",
  "rcv",
  "yuv4mpegpipe",
  "prores",
  "dnxhd",
  "swf",
  "dv",
  "ipod",
  "psp",
  "image2",
  "3g2",
  "3gp",
  "RoQ",
  
  #ifdef FN_OS_WINDOWS
  
  "dirac",
  "ffm",
  
  #elif FN_OS_MAC
  
  "mjpeg",
  "vc1",
  
  #elif FN_OS_LINUX
  
  "mjpeg",
  "vc1",  
  
  #endif
  
  NULL
  
};

ffmpegWriter::ffmpegWriter(Write* iop)
  : Writer(iop)
  , codecContext_(NULL)
  , formatContext_(NULL)
  , stream_(0)
  , error_(IGNORE_FINISH)
  , fps_(DD::Image::root_real_fps())
  , format_(0)
  , codec_(0)
  , bitrate_(400000)
  , bitrateTolerance_(4000 * 10000)
  , gopSize_(12)
  , bFrames_(0)
  , mbDecision_(FF_MB_DECISION_SIMPLE)
{
  av_log_set_level(AV_LOG_WARNING);
  av_register_all();
  
  formatsLongNames_.push_back("default");
  formatsShortNames_.push_back("default");
  AVOutputFormat* fmt = av_oformat_next(NULL);
  while (fmt) {
    
    if (fmt->video_codec != CODEC_ID_NONE && !isCodecBlacklisted( fmt->name ) ) {
      if (fmt->long_name) {
        formatsLongNames_.push_back(std::string(fmt->long_name) + std::string(" (") + std::string(fmt->name) + std::string(")"));
        formatsShortNames_.push_back(fmt->name);
      }
    }
    fmt = av_oformat_next(fmt);
  }
  formatsShortNames_.push_back(0);
  
  codecsLongNames_.push_back("default");
  codecsShortNames_.push_back("default");
  AVCodec* c = av_codec_next(NULL);
  while (c) {
    if (c->type == AVMEDIA_TYPE_VIDEO && c->encode && !isCodecBlacklisted( c->name ) ) {
      if (c->long_name) {
        codecsLongNames_.push_back(c->long_name);
        codecsShortNames_.push_back(c->name);
      }
    }
    c = av_codec_next(c);
  }
  codecsLongNames_.push_back(0);
  codecsShortNames_.push_back(0);
}

bool ffmpegWriter::isCodecBlacklisted(const char* name)
{
  const char** iterator = codecBlacklist;
  
  while( *iterator != NULL ) {
    if (strncmp(name, *iterator, strlen(*iterator)) == 0)  {
      return true;
    }
    
    ++iterator;
  }
  return false;
}

ffmpegWriter::~ffmpegWriter()
{
  av_free(codecContext_);
}

void ffmpegWriter::execute()
{
  error_ = IGNORE_FINISH;
  
  AVOutputFormat* fmt = 0;
  if (!format_) {
    fmt = av_guess_format(NULL, filename(), NULL);
    if (!fmt) {
      iop->critical("could not deduce output format from file extension");
      return;
    }
  }
  else {
    fmt = av_guess_format(formatsShortNames_[format_], NULL, NULL);
    if (!fmt) {
      iop->critical("could not deduce output format");
      return;
    }
  }
  
  if (!formatContext_)
    avformat_alloc_output_context2(&formatContext_, fmt, NULL, filename());

  snprintf(formatContext_->filename, sizeof(formatContext_->filename), "%s", filename());

  CodecID codecId = fmt->video_codec;
  if (codec_) {
    AVCodec* userCodec = avcodec_find_encoder_by_name(codecsShortNames_[codec_]);
    if (userCodec) {
      codecId = userCodec->id;
    }
  }
  
  AVCodec* videoCodec = avcodec_find_encoder(codecId);
  if (!videoCodec) {
    iop->critical("unable to find codec");
    freeFormat();
    return;
  }
  
  PixelFormat pixFMT = PIX_FMT_YUV420P;
  
  if (videoCodec->pix_fmts != NULL) {
    pixFMT = *videoCodec->pix_fmts;
  }
  else {
    if (strcmp(fmt->name, "gif") == 0) {
      pixFMT = PIX_FMT_RGB24;
    }
  }
  
  if (isCodecBlacklisted(videoCodec->name)) {
    iop->critical("unsupported codec");
    freeFormat();
    return;
  }
    
  if (!stream_) {
    stream_ = avformat_new_stream(formatContext_, NULL);
    if (!stream_) {
      iop->critical("out of memory");
      return;
    }

    codecContext_ = stream_->codec;
    
    // this seems to be needed for certain codecs, as otherwise they don't have relevant options set
    avcodec_get_context_defaults3(codecContext_, videoCodec);
    
    codecContext_->pix_fmt = pixFMT;   // this is set to the first element of FMT a choice could be added
    
    codecContext_->bit_rate = bitrate_;
    codecContext_->bit_rate_tolerance = bitrateTolerance_;
    codecContext_->width = width();
    codecContext_->height = height();
    
    // Bug 23953
    // ffmpeg does a horrible job of converting floats to AVRationals
    // It adds 0.5 randomly and does some other stuff
    // To work around that, we just multiply the fps by what I think is a reasonable number to make it an int
    // and use the reasonable number as the numerator for the timebase.
    // Timebase is not the frame rate; it's the inverse of the framerate
    // So instead of doing 1/fps, we just set the numerator and denominator of the timebase directly.
    // The upshot is that this allows ffmpeg to properly do framerates of 23.78 (or 23.796, which is what the user really wants when they put that in).
    //
    // The code was this:
    //stream_->codec->time_base = av_d2q(1.0 / fps_, 100);
    const float CONVERSION_FACTOR = 1000.0f;
    codecContext_->time_base.num = (int) CONVERSION_FACTOR;
    codecContext_->time_base.den = (int) (fps_ * CONVERSION_FACTOR);
    //
    
    codecContext_->gop_size = gopSize_;
    // NOTE: in new ffmpeg, bframes don't seem to work correctly - ffmpeg crashes...
    if (bFrames_) {
      codecContext_->max_b_frames = bFrames_;
      codecContext_->b_frame_strategy = 0;
      codecContext_->b_quant_factor = 2.0f;
    }
      
    codecContext_->mb_decision = mbDecision_;
    
    if (!strcmp(formatContext_->oformat->name, "mp4") || !strcmp(formatContext_->oformat->name, "mov") || !strcmp(formatContext_->oformat->name, "3gp"))
      codecContext_->flags |= CODEC_FLAG_GLOBAL_HEADER;
    
    if (formatContext_->oformat->flags & AVFMT_GLOBALHEADER)
      codecContext_->flags |= CODEC_FLAG_GLOBAL_HEADER;
      
    if (avcodec_open2(codecContext_, videoCodec, NULL) < 0) {
      iop->critical("unable to open codec");
      freeFormat();
      return;
    }
                  
    if (!(fmt->flags & AVFMT_NOFILE)) {
      if (avio_open(&formatContext_->pb, filename(), AVIO_FLAG_WRITE) < 0) {
        iop->critical("unable to open file");
        freeFormat();
        return;
      }
    }
    
    avformat_write_header(formatContext_, NULL);
  }
  
  error_ = CLEANUP;
  
  AVPicture picture;
  int picSize = avpicture_get_size(PIX_FMT_RGB24, width(), height());
  // allocate a buffer for the picture's image...
  uint8_t* buffer = (uint8_t*)av_malloc(picSize);
  // blank the values - this initialises stuff and seems to be needed
  avpicture_fill(&picture, buffer, PIX_FMT_RGB24, width(), height());
    
  Row row(0, width());
  input0().validate();
  input0().request(0, 0, width(), height(), Mask_RGB, 1);
 
  for (int y = 0; y < height(); ++y) {
    get(y, 0, width(), Mask_RGB, row);
    if (iop->aborted()) {
      av_free(buffer);
      return;
    }
    
    for (Channel z = Chan_Red; z <= Chan_Blue; incr(z)) {
      const float* from = row[z];
      to_byte(z - 1, picture.data[0] + (height() - y - 1) * picture.linesize[0] + z - 1, from, NULL, width(), 3);
    }
  }
  
  // now allocate an image frame for the image in the output codec's format...
  AVFrame* output = avcodec_alloc_frame();
  picSize = avpicture_get_size(pixFMT, width(), height());
  uint8_t* outBuffer = (uint8_t*)av_malloc(picSize);
  
  av_image_alloc(output->data, output->linesize, width(), height(), pixFMT, 1);
  
  SwsContext* convertCtx = sws_getContext(width(), height(), PIX_FMT_RGB24, width(), height(),
                                                pixFMT, SWS_BICUBIC, NULL, NULL, NULL);
  
  int sliceHeight = sws_scale(convertCtx, picture.data, picture.linesize, 0, height(), output->data, output->linesize);
  assert(sliceHeight > 0);
  
  int ret = 0;
  if ((formatContext_->oformat->flags & AVFMT_RAWPICTURE) != 0) {
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.flags |= AV_PKT_FLAG_KEY;
    pkt.stream_index = stream_->index;
    pkt.data = (uint8_t*)output;
    pkt.size = sizeof(AVPicture);
    ret = av_interleaved_write_frame(formatContext_, &pkt);
  }
  else {
    uint8_t* outbuf = (uint8_t*)av_malloc(picSize);
    assert(outbuf != NULL);
    ret = avcodec_encode_video(codecContext_, outbuf, picSize, output);
    if (ret > 0) {
      AVPacket pkt;
      av_init_packet(&pkt);
      if (codecContext_->coded_frame && static_cast<unsigned long>(codecContext_->coded_frame->pts) != AV_NOPTS_VALUE)
        pkt.pts = av_rescale_q(codecContext_->coded_frame->pts, codecContext_->time_base, stream_->time_base);
      if (codecContext_->coded_frame && codecContext_->coded_frame->key_frame)
        pkt.flags |= AV_PKT_FLAG_KEY;
      
      pkt.stream_index = stream_->index;
      pkt.data = outbuf;
      pkt.size = ret;
      
      ret = av_interleaved_write_frame(formatContext_, &pkt);
    }
    else {
      // we've got an error
      char szError[1024];
      av_strerror(ret, szError, 1024);
      iop->error(szError);
    }
    
    av_free(outbuf);
  }
  
  av_free(outBuffer);
  av_free(buffer);
  av_free(output);
  
  if (ret) {
    iop->critical("error writing frame to file");
    return;
  }
  
  error_ = SUCCESS;
}

void ffmpegWriter::finish()
{
  if (error_ == IGNORE_FINISH)
    return;
  av_write_trailer(formatContext_);
  avcodec_close(codecContext_);
  if (!(formatContext_->oformat->flags & AVFMT_NOFILE))
    avio_close(formatContext_->pb);
  freeFormat();
}

void ffmpegWriter::knobs(Knob_Callback f)
{
  static std::vector<const char*> formatsAliases;
  
  formatsAliases.resize(formatsLongNames_.size());
  for (int i = 0; i < static_cast<int>(formatsLongNames_.size()); ++i)
    formatsAliases[i] = formatsLongNames_[i].c_str();
  formatsAliases.push_back(0);
  
  Enumeration_knob(f, &format_, &formatsAliases[0], "format");
  Float_knob(f, &fps_, IRange(0.0, 100.0f), "fps");
  
  BeginClosedGroup(f, "Advanced");
  
  Enumeration_knob(f, &codec_, &codecsLongNames_[0], "codec");
  Int_knob(f, &bitrate_, IRange(0.0, 400000), "bitrate");
  SetFlags(f, Knob::SLIDER | Knob::LOG_SLIDER);
  Int_knob(f, &bitrateTolerance_, IRange(0, 4000 * 10000), "bitrateTol", "bitrate tolerance");
  SetFlags(f, Knob::SLIDER | Knob::LOG_SLIDER);
  Int_knob(f, &gopSize_, IRange(0, 30), "gopSize", "GOP size");
  SetFlags(f, Knob::SLIDER | Knob::LOG_SLIDER);
  Int_knob(f, &bFrames_, IRange(0, 30), "bFrames", "B Frames");
  SetFlags(f, Knob::SLIDER | Knob::LOG_SLIDER);
  
  static const char* mbDecisionTypes[] = {
    "FF_MB_DECISION_SIMPLE", "FF_MB_DECISION_BITS", "FF_MB_DECISION_RD", 0
  };
  Enumeration_knob(f, &mbDecision_, mbDecisionTypes, "mbDecision", "macro block decision mode");
  
  EndGroup(f);
}

void ffmpegWriter::freeFormat()
{
  for (int i = 0; i < static_cast<int>(formatContext_->nb_streams); ++i)
    av_freep(&formatContext_->streams[i]);
  av_free(formatContext_);
  formatContext_ = NULL;
  stream_ = NULL;
}

static Writer* build(Write* iop)
{
  return new ffmpegWriter(iop);
}

const Writer::Description ffmpegWriter::d("ffmpegmovavi", build);
_______________________________________________
Nuke-python mailing list
Nuke-python@support.thefoundry.co.uk, http://forums.thefoundry.co.uk/
http://support.thefoundry.co.uk/cgi-bin/mailman/listinfo/nuke-python
from __future__ import with_statement from __future__ import print_function from __future__ import division import os, sys import subprocess, shlex import argparse import tempfile import numpy as np from libtiff import TIFF import nuke from sys import platform as __platform if __platform == «linux» or __platform == «linux2»: _platform = ‘linux’ elif __platform == «darwin»: _platform = ‘osx’ elif __platform == «win32»: _platform = ‘win’ def frames_to_tc(total_frames, frame_rate): fps_int = int(round(frame_rate)) smpte_token = «:» hours = int(total_frames / (3600 * fps_int)) minutes = int(total_frames / (60 * fps_int) % 60) seconds = int(total_frames / fps_int % 60) frames = int(total_frames % fps_int) return «%02d:%02d:%02d%s%02d» % (hours, minutes, seconds, smpte_token, frames) def terminal_render(): parser = argparse.ArgumentParser(description=‘Render from Nuke to ffmpeg.’) parser.add_argument(«nuke_script», help=«Nuke script to render.») parser.add_argument(«-X», «—write», help=«Name of the WriteFFMPEG node to render.») parser.add_argument(«-F», «—framerange», help=«framerange to render. Please specify <start>-<end>.», required=False) parser.add_argument(«-o», «—output», help=«Output qt to render to. Will use the value of the file knob on the WriteFFMPEG node if not specified.», required=False) args = parser.parse_args() nuke_script = args.nuke_script nuke.scriptOpen(nuke_script) node = nuke.toNode(args.write) node.begin() write = nuke.toNode(‘write_tmp’) if args.framerange and «-« in args.framerange: fr = nuke.FrameRange() fr.setLast(int(args.framerange.split(‘-‘)[1])) fr.setFirst(int(args.framerange.split(‘-‘)[0])) else: node_framerange = node[‘framerange’].getValue() if node_framerange and «-« in node_framerange: fr = nuke.FrameRange() fr.setLast(int(node_framerange.split(‘-‘)[1])) fr.setFirst(int(node_framerange.split(‘-‘)[0])) else: fr = node.frameRange() tmpimg = tempfile.mkstemp(‘.tiff’, «ffmpeg_temp_»)[1] write[‘file’].setValue(tmpimg) framerate = node[‘framerate’].getValue() output = node[‘file’].getValue() tc = frames_to_tc(fr.first(), framerate) ffmpeg_args = «ffmpeg -hide_banner -loglevel info -y -f rawvideo -pixel_format rgb48le -video_size {0}x{1} -framerate {2} -i pipe:0 -timecode {3} {4} {5}».format( node.width(), node.height(), framerate, tc, node[‘ffmpeg_args’].getValue(), output) print(ffmpeg_args) ffproc = subprocess.Popen( shlex.split(ffmpeg_args), stdin=subprocess.PIPE, stdout=subprocess.PIPE ) for i, f in enumerate(fr): nuke.execute(write, f, f) print(«Rendering frame t{0} of {1}».format(i, fr.frames())) img = TIFF.open(tmpimg, mode=‘r’) img = img.read_image() img.tofile(ffproc.stdin) os.remove(tmpimg) result, error = ffproc.communicate() if __name__==«__main__»: terminal_render() def prep(): nuke.scriptSave() node = nuke.thisNode() ffpy = __file__ ffpy = ffpy.replace(‘pyc’, ‘py’) node_framerange = node[‘framerange’].getValue() nk_cmd = «{0} -t {1} {2} —write {3} —output {4}».format( nuke.EXE_PATH, ffpy, nuke.root().knob(«name»).value(), node.fullName(), node[‘file’].getValue()) print(«RENDER COMMAND:nt{0}».format(nk_cmd)) if _platform == «win»: nuke.message(«Windows not supported.») return if _platform == «osx»: cmd = »’osascript 2>/dev/null <<EOF tell application «Terminal» if not (exists window 1) then reopen activate do script «{0}» end tell EOF»’.format(nk_cmd) elif _platform == «linux»: # cmd = ‘xterm -e «bash {0}»‘.format(nk_cmd) cmd = ‘gnome-terminal -e «bash -c \«{0}; exec bash\«»‘.format(nk_cmd) #cmd = ‘gnome-terminal -e «bash -c \»{0}\»»‘.format(nk_cmd) subprocess.Popen(cmd, shell=True)

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Not an srep compressed file как исправить
  • Not all control paths return a value как исправить
  • Not all arguments converted during string formatting питон ошибка
  • Not all arguments converted during string formatting как исправить
  • Not all arguments converted during string formatting python ошибка

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии