Copy link to clipboard
Copied
I was doing some extendscript work with dropping in markers and had to do some Time() math. I chose to do this with the .seconds method because tick math is a bit of a pain as it is stored as a string.
So as I was doing the math, I discovered that my results were randomly off by a frame. Dug a little deeper and found the issue was happening with performing float math with seconds.
var demo1 = new Time();
var demo2 = new Time();
// Ticks are a form of time measurement in Adobeland.
demo1.ticks = "13558104000000";
demo2.ticks = "12743136000000";
// Setting two variables to experiment with, as named.
var demo_seconds = new Time();
var demo_ticks = new Time();
// Seconds math is simple and done with float.
demo_seconds.seconds = demo1.seconds + demo2.seconds;
// Ticks, for some reason, are stored as strings and need to be converted to int, then converted back to a string to store.
var demo_ticks_string = parseInt(demo1.ticks) + parseInt(demo2.ticks);
demo_ticks.ticks = demo_ticks_string.toString();
// This is pulling some stuff Premiere to prepare to convert time to timecode HH:MM:SS:FF
var currentSeqSettings = app.project.activeSequence.getSettings();
// Formatting the results of the seconds
var timeAsText_secs = demo_seconds.getFormatted(currentSeqSettings.videoFrameRate, app.project.activeSequence.videoDisplayFormat);
// Formatting the results of the ticks
var timeAsText_ticks = demo_ticks.getFormatted(currentSeqSettings.videoFrameRate, app.project.activeSequence.videoDisplayFormat);
// Display results in seconds
$.writeln(demo1.seconds + " + " + demo2.seconds + " = " + demo_seconds.seconds);
$.writeln(timeAsText_secs);
// Display results in in ticks
$.writeln(demo1.seconds + " + " + demo2.seconds + " = " + demo_ticks.seconds);
$.writeln(timeAsText_ticks);
Seconds Math:
53.375 + 50.1666666666667 = 103.541666666663
00:01:43:12
Ticks Math (when converted to an integer)
53.375 + 50.1666666666667 = 103.541666666667
00:01:43:13
So what's going on here? Am I missing something obvious? Why is the float math seemingly wrong?
I had a similar issues with markers on the timeline for a panel I was working on. I reached out to Bruce and he mentioned the frame boundary. I finally figured out that when you're gettting a markers In or Out point programmically it uses the audio time units as the base unit. I called these "Audio Frames". An Audio frame and a video frame do not align so an audio frame could start in a video frame and then end in the following video frame. This accounts for the "rounding". Here is the com
...Copy link to clipboard
Copied
It seems like there is some rounding issue in Time.getFormatted(). What is a workaround to this to get frame accuracy?
Copy link to clipboard
Copied
It's likely forcing the value to a frame boundary.
Copy link to clipboard
Copied
I had a similar issues with markers on the timeline for a panel I was working on. I reached out to Bruce and he mentioned the frame boundary. I finally figured out that when you're gettting a markers In or Out point programmically it uses the audio time units as the base unit. I called these "Audio Frames". An Audio frame and a video frame do not align so an audio frame could start in a video frame and then end in the following video frame. This accounts for the "rounding". Here is the comment I put in my script.
/*Because Premiere rounds to "frame boundaries" when setting In and Out points programatically, we need to set a constant in ticks to add to the in and out point so the In and Out's are set on the correct video frame. The smallest "frame" unit is based on the audio sample rate of 48000 hz. Since we're working in Ticks and there are 254,016,000,000 ticks/second we divide 254,016,000,000 by 48000 to get 5292000 ticks per "audio frame". An "Audio Frame" can span 2 video frames. If our markers In/Out happens to be on one of these spanning audio frames the In/Out Point is set on the preceeding video frame. When we add 5292000 to the In and Out point we move our In/Out Point one "Audio Frame" forward making sure we aren't on a spanning audio frame.*/
I hope this helps.
Copy link to clipboard
Copied
Hi guys, I have a question that I believe is related to this thread. I'm developing ExtendScript code for a Premiere Pro panel that writes the start and end times of markers created in the active sequence to an XML file in seconds and milliseconds (ss.mmm) and SMPTE timecode (hh:mm:ss:ff). Additionally, I have a second script that imports files, and if it finds the created XML file, it uses those same ss.mmm values to create markers in the sequence. I've been working on this for weeks and I noticed two things: 1) every time I write the XML with the same markers without moving them, the milliseconds vary slightly, and 2) there is always a 1-frame difference compared to the timecode indicated by the markers panel. Is it possible to achieve what I need and make it frame accurate?
This is the fragment of the code that writes ss.mmm and timecode to the xml:
function formatMilliseconds(milliseconds) {
if (milliseconds < 10) {
return "00" + milliseconds;
} else if (milliseconds < 100) {
return "0" + milliseconds;
} else {
return milliseconds.toString();
}
}
var inElement = <in></in>;
var outElement = <out></out>;
var durElement = <dur></dur>;
function formatMilliseconds(milliseconds) {
var result = milliseconds.toString();
while (result.length < 3) {
result = "0" + result;
}
return result;
}
var startTimeSeconds = Math.floor(marker.start.seconds);
var startTimeMilliseconds = Math.floor((marker.start.seconds - startTimeSeconds) * 1000);
var endTimeSeconds = Math.floor(marker.end.seconds);
var endTimeMilliseconds = Math.floor((marker.end.seconds - endTimeSeconds) * 1000);
var durationSeconds = endTimeSeconds - startTimeSeconds;
var durationMilliseconds = endTimeMilliseconds - startTimeMilliseconds;
if (durationMilliseconds < 0) {
durationSeconds -= 1;
durationMilliseconds += 1000;
}
// Formatear los milisegundos de la duración
var formattedDurationMilliseconds = formatMilliseconds(durationMilliseconds);
var startTimeFormatted = startTimeSeconds + "." + formatMilliseconds(startTimeMilliseconds);
var endTimeFormatted = endTimeSeconds + "." + formatMilliseconds(endTimeMilliseconds);
function ticksToSMPTE(ticks, frameRate) {
// Calcula los frames exactos
var totalFramesExact = Math.floor(ticks * frameRate / 254016000000);
var hours = Math.floor(totalFramesExact / (3600 * frameRate));
var minutes = Math.floor((totalFramesExact % (3600 * frameRate)) / (60 * frameRate));
var seconds = Math.floor((totalFramesExact % (60 * frameRate)) / frameRate);
var frames = Math.floor(totalFramesExact % frameRate); // Redondea a frames enteros
// Formatea los valores en SMPTE
var formattedTime = zeroPad(hours, 2) + ":" + zeroPad(minutes, 2) + ":" + zeroPad(seconds, 2) + ";" + zeroPad(frames, 2);
return formattedTime;
}
function zeroPad(value, length) {
var paddedValue = value.toString();
while (paddedValue.length < length) {
paddedValue = "0" + paddedValue;
}
return paddedValue;
}
var inTCElement = <inTC></inTC>;
var outTCElement = <outTC></outTC>;
var durTCElement = <durTC></durTC>;
var startTimeInTicks = marker.start.ticks;
var endTimeInTicks = marker.end.ticks;
var durationInTicks = endTimeInTicks - startTimeInTicks;
var frameRate = 29.97;
var inTimecode = ticksToSMPTE(startTimeInTicks, frameRate);
var outTimecode = ticksToSMPTE(endTimeInTicks, frameRate);
var durTimecode = ticksToSMPTE(durationInTicks, frameRate);
And this is the fragment of the code that import the video file and create the markers from the xml:
function createMarkersFromXML(xmlContent, activeSeq) {
if (activeSeq) {
var markers = activeSeq.markers;
var xmlDocument = new XML(xmlContent);
var markerNodes = xmlDocument.Marker;
var typeToNameMap = {
"skip_intro_event": "Intro",
"recap_event": "Recap",
"nexton_event": "Next on",
"skip_credits_event": "End credits"
};
for (var i = 0; i < markerNodes.length(); i++) {
var markerNode = markerNodes[i];
var type = markerNode.type.toString();
var inTime = parseFloat(markerNode.in.toString());
var outTime = parseFloat(markerNode.out.toString());
var markerName = typeToNameMap[type] || type;
var segm_marker = markers.createMarker(inTime);
segm_marker.name = markerName;
segm_marker.setTypeAsSegmentation();
segm_marker.end = outTime;
if (type === "skip_intro_event") {
segm_marker.setColorByIndex(3);
} else if (type === "recap_event") {
segm_marker.setColorByIndex(1);
} else if (type === "nexton_event") {
segm_marker.setColorByIndex(0);
} else if (type === "skip_credits_event") {
segm_marker.setColorByIndex(6);
} else {
segm_marker.setColorByIndex(1);
}
}
}
}
Thanks a lot for the help in advance!
Laura