
こんにちは、Habr! CMTimeの理解と応用に関する
Warren Mooreの記事の翻訳を読むことをお勧めします。
ビデオの操作に関連する以前の投稿で、これらのすばらしい非常に重要な構造について詳細に話すことを約束しました。 主な焦点は、時間を見てCMTimeの構造を説明することの重要性になりますが、最も単純な例がないと意味がありません。
時間推定の重要性と精度
ほとんどの人は時間の正確さについて考える必要はありません。 極端な場合を考えてみましょう。あなたは、100メートルのレースでのスピードと世界記録の差をミリ秒単位で気にするオリンピックランナーかもしれません。 これらの結果はほぼ同じです。 それでも、タイムリーな情報に関しては、非常に短い間隔(数十マイクロ秒)が重要ですが、これらのセグメントは数日または数週間である場合があります。
ムービーの正確なフレームを、たとえば
35:06に設定するとします。 時間を倍精度浮動小数点数として表すのは単純です。たとえば、次のようになります。
NSTimeInterval t = 2106.0;
多くの場合、このような表現は問題の状態に非常に適していますが、小さなコンポーネントに押しつぶされる非常に長い間隔になると適用されません。 浮動小数点数の詳細に入らない場合、doubleは8バイトで16桁(10進数)の有効数字を含むことができます。
sizeof(NSTimeInterval) == sizeof(Float64) == sizeof(double) == 8
浮動小数点数は1つの問題によって結合されます。繰り返し操作(加算、乗算など)は不正確さの増加につながり、長期間後に顕著な不一致につながる可能性があります。 そして、これにより、情報の複数のストリームの同期でエラーが発生する可能性があります。
最も簡単な例0.000001に等しい100万個の要素を合計すると、10000000000079181になります。 このエラーは、1e-6を浮動小数点形式(この例で使用)で正確に表すことができず、代わりに下位ビットが異なるバイナリ近似を使用しているために発生します。 このエラーはそれほど重大ではありませんが、HTTP Streaming Serverの使用に関しては、エラーは無期限に1秒あたり数千回増加します。
これが、浮動小数点数の使用とこの不正確さで終わる時間をより正確に表現する方法を見つけるきっかけとなりました(粗い丸め動作は言うまでもありません)。
有理数としての時間
MAC OSとiOSの両方の時間を表すためにAppleによって作成された多くのデータ構造があります。 MAC OS X 10.7とiOS 4の出現により、さらに2つが追加されました。
CMTime CMTimeRange
最初のものを理解すると、2番目のものを簡単に理解できるので、この記事では読者が
CMTimeに焦点を
当てます。
実際、CMTimeは4メンバー構造にすぎず、次のようになります。
typedef struct { CMTimeValue value; CMTimeScale timescale; CMTimeFlags flags; CMTimeEpoch epoch; } CMTime;
次に、
valueと
timescaleを詳しく調べますが、
フラグについては言及
する価値があります。 フラグのさまざまな値は、タイムスタンプ(
以降、時間間隔 )が無限のプラスまたはマイナスに等しくなる可能性があること、またはいくつかの計算の結果として丸められる可能性があることを示しています。 したがって、構造は
NSTimeIntervalよりも表現力があり、多くの利点があります。 実際に
CMTimeで表現されるものは何ですか?
値と
タイムスケールはそれぞれ64ビットと32ビットの整数として保存されることを理解することは非常に重要です。 これは、上記のテキストから明らかなはずです。なぜなら、
値は、例で与えられたものと同一のエラーを避けるような方法で提示されるからです。 さらに、合計64ビットを分母に割り当てることにより、可能な
タイムスケール値ごとに最大19桁の10進数を使用できます。
タイムスケールとは何ですか? これは、各秒が分割される「スライス」の数を表します。 オブジェクト全体のCMTimeの精度はこの値に制限されるため、これは重要です。
別の例タイムスケール値が1の場合、オブジェクトで表される時間間隔は1秒以上になり、ステップも1秒になります。 同様に、 timescale = 1000の場合、各秒は1000個に分割され、 valueには指定するミリ秒数が含まれます。
トリミングされたピースを取得しないように適切な
タイムスケールを選択する方法は? Appleはビデオに600を推奨しています(24、25、および30フレーム/秒のほとんどのビデオでは600が一般的であると説明しています)。 オーディオファイルの正確なインデックス作成が必要な場合は、値を60,000に設定できます。 この64ビット
値 の優れた点は、このパスに沿って
1/60000秒単位で580万年を想像できること
です 。
タイムスパンの秒数は
t.value / t.timescaleにすぎません。
CMTimeGetSecondsを使用して、最大精度を保証するような方法で
CMTimeを簡単に
float64に変換できます。
それほど便利ではない方法
CMTimeGetSecondsは友好的で便利な方法ですが、その邪悪な双子の兄弟である
CMTimeMakeWithSecondsは 、私たちの兄弟にとってそれほど友好的で
はあり
ません 。
CMTime CMTimeMakeWithSeconds ( Float64 seconds, int32_t preferredTimeScale );
この関数を使用する最初の数回は、私が望んでいたことを実行することができませんでした(
約Translator 、いまいましい)。
CMTimeの内部をすべて確認した
ので 、私の間違いを理解してください。 0.5秒でビューを実行して、
AVPlayerオブジェクトから定期的なコールバックを受信しようとしました(MP3ストリーミング)。
私は次のことをしようとしました:
CMTime interval = CMTimeMakeWithSeconds(0.5, 1);
この時点まで読んだ場合、間隔は実際には0.5ではなくゼロになることがわかります。 整数のシーケンスの半分のユニットを照会するのは意味がありませんが、これはまさに私がやろうとしたことです。 ここで
はタイムスケールが十分に正確で
はないため、
値は切り捨てられました。
Core Media APIには、
CMTimeに関連する算術演算を構築、比較、および実行するための関数の完全なセットが含まれてい
ます 。 これらの関数を使用して
CMTimeで算術演算を実行することは簡単ではありませんが、精度と整合性を維持する場合は、それらを使用することが重要です。 これらの各関数は、特別なオーバーフローチェックと丸めチェックを実行し、そのような必要が生じたときにCMTimeオブジェクトに対応するフラグを設定します。
2つの
CMTimesを追加するには、
CMTimeAdd関数を使用します。 比較するには、
CMTimeCompareを使用し
ます (この関数の戻り値は、標準Cライブラリで使用され、
qsortという名前でよく知られている関数コンパレーターの合意に
従います )。
CMTimeからさらに学習するには、
CMTimeMaximumを使用し
ます 。 残りの方法についてはドキュメントをご覧ください。通常、この機能セットで十分です。
不確実性と無限大:「奇妙な」フラグを設定する
CMTime関連の記事の最後の部分は、無限フラグ、未定義の値、および丸めの表示です。 不確実な時間とは、値が不明な時間です。 初期化される前に経過時間を要求すると、オブジェクトのこのような時間間隔を取得できます。 例外ごとにフラグ値を確認できます。
kCMTimeFlags_PositiveInfinity kCMTimeFlags_NegativeInfinity kCMTimeFlags_Indefinite
ビット単位のOR(オリジナルではOR'ing)は誰もが使用するわけではないため、これらのフラグの存在を確認する便利なマクロがあります。
CMTIME_IS_POSITIVE_INFINITY CMTIME_IS_NEGATIVE_INFINITY CMTIME_IS_INDEFINITE
ところで、それらは期間の有効性を検証するためにも使用できます。
タイムスタンプが有効
でない場合、このような操作の結果はNOになります。
さらに、さまざまな時間カテゴリの比較がどのように機能するかの長いリストを以下に示します。
- 無効な時間(無効な時間)は、別の無効な時間と等しく、他のどの時間よりも長くなります。
- さらに、無限大(正の無限大)は無効な時間よりも小さく、別の正の無限大に等しく、他の時間よりも長くなります。
- 不定時間は、無効な時間よりも小さく、無限大よりも小さく、同じ不定時間に等しく、他の時間よりも長くなります。
- 負の無限大(負の無限大)は、同じ値に等しく、他の値よりも小さくなります。
わかった。注:もちろん、検討中のトピックはかなり狭いですが、それでも一部の人にとっては興味深いように思えるかもしれません。 さらに、RuNetの情報はゼロよりわずかに小さくなっています。 著者の承認を得るために、またいくつかの点を明確にするために、私は彼に郵便で連絡しなければなりませんでした。 コメントやコメントにエラーを記述しないでください。内部メールに送信してください、ありがとう。