概要
日本の放送局等で使われているドロップフレームでの加算、減算について記述します。
計算を行うにはタイムコードからフレーム数に変換し、
再度タイムコード形式に変換する手法を使います。
タイムコードからフレーム数に変換するときにはドロップフレーム分間引く(減算する)処理が必要で、
フレーム数からタイムコードに変換するときにはドロップフレーム分だけフレーム数を増やして上げる処理が必要になります。
前提知識
表記について
NDF(ノンドロップフレーム形式)では01:02:03:12
のように時:分:秒:フレーム
のようにすべて :
コロン区切りで示し、
DF(ドロップフレーム)形式では01:02:03;12
のように時:分:秒;フレーム
のように時分秒は:
コロン、フレームの区切りは;
セミコロンで区切ることが多いので、この記事内ではそのように示します。
(これは編集ソフトにもよるようです)
計算方法
タイムコードからフレームに変換する
tM = 60 * hours + mins
まず、タイムコードの時・分を分単位に換算します。
(1時間20分なら80[分])
これをtM(totalMinutes)と置きます
\begin{equation}
\begin{split}
fN &= 60 * 60 * 30 * hours\\
&+ 60 * 30 * mins\\
&+ 30 * secs\\
&+ frames\\
&- 2 * (tM - tM \quad div \quad 10)
\end{split}
\end{equation}
NDF(ノンドロップフレーム)形式のときは30fpsなので、
素直に30×秒数+フレーム数
でフレーム単位に変換することができますが、
DF(ドロップフレーム)形式のときは、10の倍数の正分を除く毎正分1分につき2フレームを間引く必要があるので、
最後に2×(tM - tM div 10)
を引いてあげます。これをfN(frameNumber)と置きます。
(divは割り算をした整数部分(余りを除いた数))
タイムコードからフレームに変換することができたら、
あとは加算減算をして、計算結果をフレームからタイムコード形式に変換します
フレーム数からタイムコード形式に変換する
フレーム数からタイムコード形式に変換するときには
10分あたり18フレーム増やしてあげるひつようが有るので、10分あたりのフレーム数は
\begin{equation}
\begin{split}
29.97 * 60 * 10= 17982
\end{split}
\end{equation}
フレーム総数に10分の塊がいくつあるかをカウントすると
\begin{equation}
\begin{split}
D &= fN \quad div \quad 17982
\end{split}
\end{equation}
10分に満たないフレーム数は
\begin{equation}
\begin{split}
M &= fN \quad mod \quad 17982
\end{split}
\end{equation}
※modは剰余
1分あたりのフレーム数は(29.97 * 60 = )1798.2であり、
10分に満たないフレームについて毎分2フレームずつ間引いているので、
フレーム数は
\begin{equation}
\begin{split}
L &= (M - 2)\quad div \quad 1798 \quad[M \geqq 2 のとき]\\
L &= 0\quad [M < 2のとき]
\end{split}
\end{equation}
※1798.2を1798として計算しているのは10分毎に2フレーム増やす処理を省略することで帳尻を合わせている
※(M - 2) < 0となるとき、つまりM < 2 のとき、0扱いとする。
10分おきに18フレーム増やす(0.2足りなかった分の帳尻合わせとして10分毎に2フレーム増やす処理を省略する分10×2-2=18)
\begin{equation}
\begin{split}
fN &+= 18 * D + 2 * L
\end{split}
\end{equation}
JavaScriptでのコード
/**
* タイムコードからドロップフレームのフレーム数へ変換する
* (とりあえず時間・分・秒・フレームの数値を入力とする)
*/
function convTC2Frame(h, m, s, f) {
const tM = 60 * h + m;
return (60 * 60 * 30 * h) + (60 * 30 * m) + (30 * s) + f - 2 * (tM - Math.floor(tM / 10));
}
/**
* ドロップフレームのフレーム数からタイムコードへ変換する
*/
function convFrameDF2TC(frame) {
// 17982[frame] = 29.97[frame/sec] * 10[min] * 60[sec/min]・・・10分ごとのフレーム数
const D = Math.floor(frame / 17982);
const M = frame % 17982;
let L;
if (M < 2) {
L = 0;
} else {
L = Math.floor((M - 2) / 1798);
}
let fN;
fN = frame + (D * 18) + 2 * L;
// ↓あとから加工するのであれば連想配列で返す。とりあえずはTC形式の文字列を返す
// const rtn = {
// h: '00',
// m: '00',
// s: '00',
// f: '00'
// };
// rtn.f = ('00' + fN % 30).slice(-2);
// rtn.s = ('00' + (Math.floor(fN / 30)) % 60).slice(-2);
// rtn.m = ('00' + Math.floor(Math.floor(fN / 30) / 60) % 60).slice(-2);
// rtn.h = ('00' + Math.floor(Math.floor(Math.floor(fN / 30) / 60) / 60) % 24).slice(-2);
return ('00' + Math.floor(Math.floor(Math.floor(fN / 30) / 60) / 60) % 24).slice(-2) + ':' +
('00' + Math.floor(Math.floor(fN / 30) / 60) % 60).slice(-2) + ':' +
('00' + (Math.floor(fN / 30)) % 60).slice(-2) + ";" +
('00' + fN % 30).slice(-2);
}
// 以下動作確認
let frame1 = convTC2Frame(0, 0, 59, 29); // 00:00:59;29
console.log(convFrameDF2TC(frame1 + 1)); // 00:01:00;02
// 1フレーム後はNDFの場合00:01:00:00であるが、DFの場合00、01フレームが間引かれている
let frame2 = convTC2Frame(0, 09, 59, 29); // 00:09:59;29
console.log(convFrameDF2TC(frame2 + 1)); // 00:10:00;00
// 10の倍数分なのでフレームの間引きは無し