// AddTimeCode.C // Copyright (c) 2009 The Foundry Visionmongers Ltd. All Rights Reserved. static const char* const CLASS = "AddTimeCode"; static const char* const HELP = "AddTimeCode:\n" "Adds a timecode to the metadata passed through."; static const double MAX_FPS_VAL = 1000; #include #include "DDImage/Iop.h" #include "DDImage/Knobs.h" #include "DDImage/Row.h" #include "DDImage/MetaData.h" #include "DDImage/DDMath.h" using namespace DD::Image; class AddTimeCode : public Iop { MetaData::Bundle _meta; int _fps; bool _fpsFromMeta; const char* _startCode; int _startFrame; bool _startSpecify; public: void _validate(bool); void _request(int, int, int, int, ChannelMask, int); void engine(int y, int x, int r, ChannelMask, Row &); int split_input(int) const { return 1; } int maximum_inputs() const { return 1; } int minimum_inputs() const { return 1; } const char* Class() const { return CLASS; } static const Iop::Description d; const char* node_help() const { return HELP; } void knobs(Knob_Callback); AddTimeCode(Node* node) : Iop(node) { _startCode = "01:00:00:00"; _fps = 24; _fpsFromMeta = true; _startSpecify = false; _startFrame = 1; } Iop* in() const { return input(0); } void append(Hash& hash) { hash.append(outputContext().frame()); } int knob_changed(DD::Image::Knob* k) { if (k->is("useFrame")) { knob("frame")->enable(k->get_value() != 0.0); return true; } if (k->is("metafps")) { knob("fps")->enable(!k->get_value()); return true; } return Iop::knob_changed(k); } const MetaData::Bundle& _fetchMetaData(const char* key) { _meta = Iop::_fetchMetaData(key); int ihh, imm, iss, iff; if (sscanf(_startCode, "%2d:%2d:%2d:%2d", &ihh, &imm, &iss, &iff) != 4) { ihh = 1; imm = 0; iss = 0; iff = 0; } int startFrame = info_.first_frame(); if (_startSpecify) { startFrame = _startFrame; } int fps; if (_fpsFromMeta) { double fpsAsFl = _meta.getDouble(MetaData::FRAME_RATE); if ((!isfinite(fpsAsFl)) || (fpsAsFl > MAX_FPS_VAL)) { fps = 24; } else { fps = int(fpsAsFl + .5); } } else fps = _fps; if (fps == 0) fps = 24; _meta.setData(MetaData::FRAME_RATE, fps); int frame = int(outputContext().frame()) - startFrame; int divisor = 60 * 60 * 100 * fps; int signBit = ((unsigned int)(frame)) >> ((sizeof(int) * CHAR_BIT) - 1); int quot = (frame + signBit) / divisor - signBit; int theRest = frame - (quot * divisor); frame = theRest; int offset = ihh; offset *= 60; offset += imm; offset *= 60; offset += iss; offset *= fps; offset += iff; int ff = frame + offset; int ss = ff / fps; ff %= fps; int mm = ss / 60; ss %= 60; int hh = mm / 60; mm %= 60; hh %= 100; char timecode[100]; sprintf(timecode, "%02i:%02i:%02i:%02i", hh, mm, ss, ff); _meta.setData(MetaData::TIMECODE, timecode); return _meta; } }; void AddTimeCode::_validate(bool) { copy_info(); } void AddTimeCode::_request(int x, int y, int r, int t, ChannelMask channels, int count) { in()->request(x, y, r, t, channels, count); } void AddTimeCode::engine(int y, int x, int r, ChannelMask channels, Row& out) { out.get(*in(), y, x, r, channels); } void AddTimeCode::knobs(Knob_Callback f) { Eval_String_knob(f, &_startCode, "startcode", "start code"); Tooltip(f, "The start of the time code. Must be in the format of HH:MM:SS:FF. Supports expressions, e.g. \"[frame]:00:00:00\" will set a different hour for each frame."); Int_knob(f, &_fps, "fps"); SetFlags(f, DD::Image::Knob::DISABLED); Bool_knob(f, &_fpsFromMeta, "metafps", "get FPS from metadata"); Int_knob(f, &_startFrame, "frame", "start frame"); SetFlags(f, DD::Image::Knob::DISABLED); Bool_knob(f, &_startSpecify, "useFrame", "use start frame?"); } static Iop* build(Node* node) { return new AddTimeCode(node); } const Iop::Description AddTimeCode::d(CLASS, "MetaData/Modify", build);