ドロップフレーム(NULL frame, Drop frame)について     2004. 8.05 (01版)
ところで、以前データアクセスの基礎を説明しましたが、もし入力ファイルにドロップフレームが
存在したら果たしてどういうことになるでしょうか?

例えば、データアクセスの基礎(2) [YV12編] で例示したモノクロ化するコードを
ドロップフレームが含まれるソースファイルで実行してみました。

VirtualDubで直接AVIファイル(XviD)を開いたところ
NULL frameXviD NULL frame
Delta frameXviD Delta frame

同じファイルをAvisynth(AVSスクリプト)経由で開いたところ
NULL frame

Key frame
AVS NULL->Key frame
Delta frame

Key frame
AVS Delta->Key frame


ごらんのように正常に実行できました。

下の2つを見て分かるように、Avisynthを通すとドロップフレームはキーフレームに変換されています。
つまりドロップフレームの直前の有効なフレームのコピーとなるわけです。
他にも、デルタフレームもキーフレームに、またXviD圧縮が無圧縮YV12となっています。
つまり、Avisynthではクリップは無圧縮のキーフレーム(完全なフレームデータ)のみを取り扱うことになります。

逆に言えば、フィルタでNULL frameを取り扱う(返す)ことは出来ないということです。
目次へジャンプ。
DLLのデータ領域の使用についての注意点     2004. 8.05 (01版)
DLLにはその構造から独特のいろいろな制限があります。
私もほとんど知らないで使用していますが、以下に明らかなものを明記しておきます。
(実は私はことごとく嵌った経験があります。)

なお、DLLはタスクとして動作することはできず、呼び出したアプリケーションの一部として動作します。
メモリ割り当ては呼び出した実行可能ファイルのコンテキストで行われ、
コードセグメントは複数のアプリ間で共通となります。

(1) ローカルデータ
DLL(ダイナミックリンクライブラリ)は、スタック領域を持ちません。
ローカル作業領域はすべて呼び出したアプリケーションのスタックを使用します。
従って、DLLからこのスタックスペース量をコントロールすることは出来ないし、
よって自由に無尽蔵に使えるものではありません。(それほど神経質になる必要はありませんが。)

画像データバッファのような大きなサイズを獲得すると、ビルド時には正常でも実行時に
異常終了することになります。
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
    BYTE        buf[720*480*2*2];
    PVideoFrame src = child->GetFrame(n, env);
    PVideoFrame dst = env->NewVideoFrame(vi);
    env->BitBlt(buf, 720*2, src->GetReadPtr(), src->GetPitch(), src->GetRowSize(), src->GetHeight());
    env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), buf, 720*2, dst->GetRowSize(), dst->GetHeight());
    return dst;
}
VDub - Stack OverFlow

(2) 静的データ
では、静的なデータとして取ればどうだろう。ということになりますがここでは別の問題が出てきます。
その前に、静的なデータはDLLにプライベートなものと共有されるものとが存在します。
通常はプライベートであり、共有されるデータセグメントを使用する場合は明示的に要求します。
静的データは、マルチスレッドを意識する必要があります。
あるスレッドから呼び出された関数が実行途中で、別のスレッドに制御が切り替わり、
同じ関数が呼ばれるということが起き得るということです。
例えば、
  {
1)    static bool foo;     //--- thread unsafe ---
2)    foo = false
      if(xxxx) {
          foo = true;
          //----- proc 1 -----
      }
3)    if(foo == true) {
          //----- proc 2 -----
      }
  }
というコードがあると、proc2のコードは実行されることが期待されますが、
2)を実行後、3)が実行される前にスレッドが切り替わって別のスレッドから同じコードが
呼び出され1)が実行され、元のスレッドに実行権が戻るとfooはtrueからfalseに変化してる
場合があり、3)は正しく実行されないことになります。
※ こういうのをスレッドセーフではないと言います。(Thread unsafe)
   こういうバグはタチが悪いです。タイミングにより起きたり起きなかったりし、さらに
   プログラムロジックを追っても単独では誤りが無く、原因を突き止めるのが大変です。
   上の例では単なる値でしたが、これがポインタだったら指してるアドレスが
   とんでもないアドレスを指したりすることにもなりアクセスバイオレーションを
   起こすでしょう。まあ、Avisynthのプラグインのような使われ方ではこういったことは
   おそらく起きることは無いとは思いますが。
   (実は私のプラグインも未だにスレッドセーフでないものがあります。)


さらに共有データセグメントを使用する場合は、プリエンプティブマルチタスキングや同期を
考慮する必要が出てきます。

またマルチスレッドでなくても、Avisynthでは同じフィルタを2重に掛けることができるわけで、
例えばパラメタを静的変数に格納してたりすると、2度目のコールで1度目のパラメタを上書きする
ことになってしまいます。
AVISource("foo.avi")
hogehoge(arg1=10)    --- (1)
hogehoge(arg1=20)    --- (2)
return last
もし上のスクリプトで、hogehoge()がarg1を静的変数に保存していたりすると、
(1)のインスタンスが生成された時点では、arg1=10ですが、
(2)のインスタンスが生成された時点で、arg1=20に変化し、
実際の処理では、hogehoge(arg1=20)が(1)でも(2)でも実行されることになります。

解決策は、状況により変化するものには静的変数を使用しないことです。
最初のような場合には、スレッドローカルストレージを使用することでも良い場合があります。

(3) ヒープ(フリーストア)
malloc関数で獲得したりnew演算子で生成するオブジェクトが置かれるデータストレージです。
画像データの格納領域のような大きな領域はここにとるようにします。
また、クラス内で取る領域もインスタンスはヒープに取られるようですが、
こういう大きな作業領域はなるべく自分でアロケートした方が良いと思われます。
class Sample : public GenericVideoFilter {
    AVSValue    args;
    BYTE        buf[720*480*2*2];
public:
    Sample(PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child) {}
    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
    PVideoFrame src = child->GetFrame(n, env);
    PVideoFrame dst = env->NewVideoFrame(vi);
    env->BitBlt(buf, 720*2, src->GetReadPtr(), src->GetPitch(), src->GetRowSize(), src->GetHeight());
    env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), buf, 720*2, dst->GetRowSize(), dst->GetHeight());
    return dst;
}

class Sample : public GenericVideoFilter {
    AVSValue    args;
//-------
//    BYTE       *buf;
//-------
public:
    Sample(PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child) {}
//-------
//    Sample(PClip _child, IScriptEnvironment* env)
//       : GenericVideoFilter(_child) { buf = new BYTE [720*480*2*2]; }
//    ~Sample() { delete [] buf; }
//-------
    PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
};
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
    BYTE        *buf = new BYTE [720*480*2*2];
    PVideoFrame  src = child->GetFrame(n, env);
    PVideoFrame  dst = env->NewVideoFrame(vi);
    env->BitBlt(buf, 720*2, src->GetReadPtr(), src->GetPitch(), src->GetRowSize(), src->GetHeight());
    env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), buf, 720*2, dst->GetRowSize(), dst->GetHeight());
    delete [] buf;
    return dst;
}


※ DLLでのCランタイムライブラリの使用
以前はDLLでCランタイムライブラリを使用する場合、DllEntryPoint関数(DLLの初期化関数)で
_CRT_INIT関数を呼び出して初期化する必要があるということでしたが、
現在はOBSOLETEになったようです。

スレッドセーフでリエントラントな関数を作成するように心がけることが大切です。
目次へジャンプ。
Clipのデータアライメントの注意点     2004. 8.19 (01版)
データアクセスの基礎(1) [YUY2編]でクリップデータは8の倍数のアドレス境界に
アラインメントされていると言いましたが、単純にどういう場合でもこれを前提にしてはいけません。
env->MakeWritable()関数を使うような(In-Place Filter)場合には問題ありませんが、
env->NewVideoFrame()を使って新たなVideoFrameを構築するような場合には、
無加工のClipソースデータを取り扱うことになろうと思います。

このとき、例えば、自分のフィルタに制御が渡される前にCropがチェーンされていた場合などでは、
有効データの先頭はきちんとアライメントされていない可能性があります。
MMX,SSE,SSE2命令を使うような場合、この点に注意していないとCPU命令の実行時にペナルティが発生して
余分なコストがかかったり、アクセス例外が発生したりすることになります。
(別途、MMX,SSE,SSE2命令の解説も行いたいと思っています。)

LoadPlugin("F:\Src\sample\release\sample.dll")
OpenDMLSource("foo.avi")
AssumeFrameBased().ComplementParity()
Crop(2,0,-2,0)
sample()
return last

(1) データアラインメントが8の倍数になっている
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
    PVideoFrame   src  = child->GetFrame(n, env);
    env->MakeWritable(&src);
    char msg[256];
    sprintf(msg, "frame=%d  GetReadPtr=%08x Offset=%08x", n, src->GetReadPtr(), src->GetOffset());
    OutputDebugString(msg);
    return src;
}
実行結果
    frame=0  GetReadPtr=039d0020 Offset=00000000
    frame=1  GetReadPtr=0bd20020 Offset=00000000
    frame=2  GetReadPtr=0be60020 Offset=00000000
(2) データアラインメントが8の倍数になっていない
PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
    PVideoFrame   src  = child->GetFrame(n, env);
//    env->MakeWritable(&src);
    char msg[256];
    sprintf(msg, "frame=%d  GetReadPtr=%08x Offset=%08x", n, src->GetReadPtr(), src->GetOffset());
    OutputDebugString(msg);
    return src;
}
実行結果
    frame=0  GetReadPtr=03750024 Offset=00000004
    frame=1  GetReadPtr=0bbe0024 Offset=00000004
    frame=2  GetReadPtr=0bc80024 Offset=00000004
※ なおここでは (PVideoFrame)->GetOffset() を使用していますが、これは実際には使用は避けるべきです。

では、ここでCropを使うとどうしてこういうことになるのかちょっと動作内容を見てみましょう。
最初に結論を言うと、Cropパラメタで制限された範囲データを新たなVideoFrameにコピーしているわけではなく、
単にSubFrame化してデータ先頭ポインタ(offset)を移動して、つじつまを合わせているだけなのです。
(Cropのパラメタにalign=trueが指定されていて、かつ、データが8(SSE2の場合16)の倍数の境界でない場合、
 新たにVideoFrameが構築されデータがコピーされます。)

----- transform.h -----------------------------------------------------------------
class Crop : public GenericVideoFilter 
/**
  * Class to crop a video
 **/
{  
public:
  Crop(int _left, int _top, int _width, int _height, int _align, PClip _child, IScriptEnvironment* env);
  PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);

  static AVSValue __cdecl Create(AVSValue args, void*, IScriptEnvironment* env);

private:
  /*const*/ int left_bytes, top, align;
};

このようにエントリ関数をクラスメソッドにする場合、static宣言してインスタンスと無関係にして、
自分自身のクラスインスタンスへのポインタである暗黙の第一引数(Crop *)thisを取り除きます。
----- transform.cpp ----------------------------------------------------------------- AVSFunction Transform_filters[] = { { "FlipVertical", "c", FlipVertical::Create }, { "FlipHorizontal", "c", FlipHorizontal::Create },
{ "Crop", "ciiii[align]b", Crop::Create }, // left, top, width, height *OR* // left, top, -right, -bottom (VDub style) { "CropBottom", "ci", Create_CropBottom }, // bottom amount { "AddBorders", "ciiii[color]i", AddBorders::Create }, // left, top, right, bottom [,color] { "Letterbox", "cii[x1]i[x2]i[color]i", Create_Letterbox }, // top, bottom, [left], [right] [,color] { 0 } }; /****************************** ******* Crop Filter ****** *****************************/ Crop::Crop(int _left, int _top, int _width, int _height, int _align, PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child), align(_align) { /* Negative values -> VDub-style syntax Namely, Crop(a, b, -c, -d) will crop c pixels from the right and d pixels from the bottom. Flags on 0 values too since AFAICT it's much more useful to this syntax than the standard one. */ if ( (_left<0) || (_top<0) ) env->ThrowError("Crop: Top and Left must be more than 0"); if (_width <= 0) _width = vi.width - _left + _width; if (_height <= 0) _height = vi.height - _top + _height; if (_width <=0) env->ThrowError("Crop: Destination width is 0 or less."); if (_height<=0) env->ThrowError("Crop: Destination height is 0 or less."); if (vi.IsYUV()) { // YUY2 can only crop to even pixel boundaries horizontally if (_left&1) env->ThrowError("Crop: YUV images can only be cropped by even numbers (left side)."); if (_width&1) env->ThrowError("Crop: YUV images can only be cropped by even numbers (right side)."); if (vi.IsYV12()) { if (_top&1) env->ThrowError("Crop: YV12 images can only be cropped by even numbers (top)."); if (_height&1) env->ThrowError("Crop: YV12 images can only be cropped by even numbers (bottom)."); } } else { // RGB is upside-down _top = vi.height - _height - _top; } if (_left + _width > vi.width || _top + _height > vi.height) env->ThrowError("Crop: you cannot use crop to enlarge or 'shift' a clip"); left_bytes = vi.BytesFromPixels(_left); top = _top; vi.width = _width; vi.height = _height; if (align) { align = 8; if (env->GetCPUFlags() & CPUF_SSE2) align = 16; if (!(left_bytes & (align-1))) // We already have alignment. align=0; } } PVideoFrame Crop::GetFrame(int n, IScriptEnvironment* env) { PVideoFrame frame = child->GetFrame(n, env); if (align) { PVideoFrame dst = env->NewVideoFrame(vi,align); env->BitBlt(dst->GetWritePtr(PLANAR_Y), dst->GetPitch(PLANAR_Y), frame->GetReadPtr(PLANAR_Y) + top * frame->GetPitch(PLANAR_Y) + left_bytes, frame->GetPitch(PLANAR_Y), dst->GetRowSize(PLANAR_Y), dst->GetHeight(PLANAR_Y)); env->BitBlt(dst->GetWritePtr(PLANAR_U), dst->GetPitch(PLANAR_U), frame->GetReadPtr(PLANAR_U) + (top>>1) * frame->GetPitch(PLANAR_U) + (left_bytes>>1), frame->GetPitch(PLANAR_U), dst->GetRowSize(PLANAR_U), dst->GetHeight(PLANAR_U)); env->BitBlt(dst->GetWritePtr(PLANAR_V), dst->GetPitch(PLANAR_V), frame->GetReadPtr(PLANAR_V) + (top>>1) * frame->GetPitch(PLANAR_V) + (left_bytes>>1), frame->GetPitch(PLANAR_V), dst->GetRowSize(PLANAR_V), dst->GetHeight(PLANAR_V)); return dst; } if (!vi.IsPlanar()) return frame->Subframe(top * frame->GetPitch() + left_bytes, frame->GetPitch(), vi.RowSize(), vi.height); else return frame->Subframe(top * frame->GetPitch() + left_bytes, frame->GetPitch(), vi.RowSize(), vi.height, (top/2) * frame->GetPitch(PLANAR_U) + (left_bytes/2), (top/2) * frame->GetPitch(PLANAR_V) + (left_bytes/2), frame->GetPitch(PLANAR_U)); } AVSValue __cdecl Crop::Create(AVSValue args, void*, IScriptEnvironment* env) { return new Crop( args[1].AsInt(), args[2].AsInt(), args[3].AsInt(), args[4].AsInt(), args[5].AsBool(false) ? 1 : 0, args[0].AsClip(), env ); } ----- avisynth.cpp ----------------------------------------------------------------- class LinkedVideoFrame { public: LinkedVideoFrame* next; VideoFrame vf; }; static LinkedVideoFrame* g_VideoFrame_recycle_bin = 0; void* VideoFrame::operator new(unsigned) { // CriticalSection for (LinkedVideoFrame* i = g_VideoFrame_recycle_bin; i; i = i->next) if (i->vf.refcount == 0) return &i->vf; LinkedVideoFrame* result = (LinkedVideoFrame*)::operator new(sizeof(LinkedVideoFrame)); result->next = g_VideoFrame_recycle_bin; g_VideoFrame_recycle_bin = result; return &result->vf; } VideoFrame::VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height) : refcount(0), vfb(_vfb), offset(_offset), pitch(_pitch), row_size(_row_size), height(_height),offsetU(_offset),offsetV(_offset),pitchUV(0) // PitchUV=0 so this doesn't take up additional space { InterlockedIncrement(&vfb->refcount); } VideoFrame::VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV) : refcount(0), vfb(_vfb), offset(_offset), pitch(_pitch), row_size(_row_size), height(_height),offsetU(_offsetU),offsetV(_offsetV),pitchUV(_pitchUV) { InterlockedIncrement(&vfb->refcount); } VideoFrame* VideoFrame::Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const { return new VideoFrame(vfb, offset+rel_offset, new_pitch, new_row_size, new_height); } VideoFrame* VideoFrame::Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) const { return new VideoFrame(vfb, offset+rel_offset, new_pitch, new_row_size, new_height, rel_offsetU+offsetU, rel_offsetV+offsetV, new_pitchUV); } ---------------------------------------------------------------------- PVideoFrame __stdcall ScriptEnvironment::Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) { return src->Subframe(rel_offset, new_pitch, new_row_size, new_height); } ----------------------------------------------------------------------
目次へジャンプ。
クリップの属性変更(1) - VideoInfoとenv->NewVideoFrame()     2004. 8.20 (02版)
2004. 8.19 (01版)
今回はクリップの属性を変更するにはどうすれば良いのかを理解しましょう。
まず、VideoInfo構造体で定義されている属性には以下のようなものがあります。
属性意味
width 画像の横幅(pixel)
height 画像の高さ(pixel)
fps_numerator
fps_denominator
画像のフレームレ−ト
(fps)
SetFPS(numerator, denominator)
※ fps = fps_numerator/fps_denominator
num_frames 画像の総フレーム数
pixel_type 画像のピクセルタイプ CS_UNKNOWN = 0
CS_BGR = 1<<28
CS_YUV = 1<<29
CS_INTERLEAVED = 1<<30
CS_PLANAR = 1<<31
CS_BGR24 = 1<<0 | CS_BGR | CS_INTERLEAVED
CS_BGR32 = 1<<1 | CS_BGR | CS_INTERLEAVED
CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED
CS_YV12 = 1<<3 | CS_YUV | CS_PLANAR
CS_I420 = 1<<4 | CS_YUV | CS_PLANAR
CS_IYUV = 1<<4 | CS_YUV | CS_PLANAR
pixel_typeとThrowError()について参照
audio_samples_per_second 音声の1秒間あたりのサンプリング数
(周波数 Hz)
sample_type 音声のサンプルタイプ SAMPLE_INT8 = 1<<0
SAMPLE_INT16 = 1<<1
SAMPLE_INT24 = 1<<2
SAMPLE_INT32 = 1<<3
SAMPLE_FLOAT = 1<<4
num_audio_samples 音声の総サンプル数
nchannel 音声のチャンネル数
image_type 画像のイメージタイプ IT_BFF = 1<<0
IT_TFF = 1<<1
IT_FIELDBASED = 1<<2

これらの値を設定(変更)するには、コンストラクタの中で行います。
GenericVideoFilterクラスには、VideoInfo構造体viをメンバとして持っていましたので、
この値を変更することになります。

【例】
Sample::Sample(PClip _child, IScriptEnvironment* env) : GenericVideoFilter(_child) { vi.width = 320; vi.height = 240; vi.SetFPS(30000 * 4, 1001 * 5); // set 23.976fps vi.num_frames = vi.fps_numerator * 10 / vi.fps_denominator; // set 10 sec vi.pixel_type = VideoInfo::CS_YUY2; vi.audio_samples_per_second = 44100; // 44.1KHz vi.sample_type = SAMPLE_INT16; vi.num_audio_samples = vi.AudioSamplesFromFrames(vi.num_frames); // (__int64)vi.SamplesPerSecond() * 10; vi.nchannels = 2; // stereo vi.SetFieldBased(false); // clear IT_FIELDBASED vi.Clear(VideoInfo::IT_FIELDBASED); // same as above vi.Set(VideoInfo::IT_TFF); } PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) { PVideoFrame src = child->GetFrame(n, env); PVideoFrame dst = env->NewVideoFrame(vi); // ----- needs writing src->dst data conversion----- return dst; }
※ ここで、env->NewVideoFrame(vi)のかわりに、env->MakeWritable(&src)にして
   return src; にすると当然ですがClip属性の変更だけ行われデータは元のまま
   の属性での値となってしまいます。
   (フレームレートの変更や総フレーム数の変更程度ならこれでも良いですが。)
void __stdcall Sample::GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) { // ----- needs writing src->dst data conversion----- }

さて、ここでClip属性の変更をコンストラクタではなく各フレームデータの処理の中で行ったら
いったいどうなるでしょう?
ちょっと以下のコードで試してみましょう。
Sample::Sample(PClip _child, IScriptEnvironment* env)
 : GenericVideoFilter(_child) {
}

PVideoFrame __stdcall Sample::GetFrame(int n, IScriptEnvironment* env) {
    vi.width      = 320;
    vi.height     = 240;
    vi.SetFPS(30000 * 4, 1001 * 5);
    vi.num_frames = vi.fps_numerator * 10 / vi.fps_denominator;
    vi.pixel_type = VideoInfo::CS_YUY2;
    vi.audio_samples_per_second = 44100;
    vi.sample_type              = SAMPLE_INT16;
    vi.num_audio_samples        = vi.AudioSamplesFromFrames(vi.num_frames);
    vi.nchannels                = 2;
    vi.SetFieldBased(false);
    vi.Clear(VideoInfo::IT_FIELDBASED);
    vi.Set(VideoInfo::IT_TFF);

    PVideoFrame   src  = child->GetFrame(n, env);
    PVideoFrame   dst  = env->NewVideoFrame(vi);
    env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr(), src->GetPitch()
                , dst->GetRowSize(), dst->GetHeight());
    return dst;
}

LoadPlugin("F:\Src\sample\release\sample.dll")
ColorBars(640,480)
ConvertToYUY2()
sample()
return last

Change_vi Test Result

env->BitBlt(dst->GetWritePtr(), dst->GetPitch(), src->GetReadPtr(), src->GetPitch()
                , dst->GetRowSize(), dst->GetHeight());
       ここでは、dst->GetRowSize() = 320*2, dst->GetHeight() = 240 になっています。
       でもこのフィルタの外に出た場合には、640x480のまま(その他の属性もソースのまま)
       になっています。

つまり、viの変更結果は自分の中で閉じていて、外部には影響しないということです。
このことは、逆に言うと、
他フィルタによって、各フレーム処理時に属性を変更されることは無い。
属性は各フレーム処理の中でいちいち判定しなくとも、コンストラクタの中で一度だけ行えばよい。
とも言えます。(ただし、将来はどうなるか判りませんが)


さて、ここで出てきた、 env->NewVideoFrame(vi) についてですが、
引数に、const VideoInfo& vi, int align の2つを取ります。
alignを省略した場合、デフォルトのデータ境界(YUY2では8の倍数)となりますが、
それ以上の値のデータ境界にしたい場合は指定することになります。
max(align, 16) : align>0を指定した場合
align * -1     : align<0を指定した場合(17バイト以上、32バイト境界とかにしたい場合、-32を指定)
さらに獲得したVideoFrameは初期化されていないことに注意してください。
獲得後のデータの値は不定ですので、必ずデータを書き込む必要があります。

----- avisynth.cpp -----------------------------------------------------------------
PVideoFrame ScriptEnvironment::NewPlanarVideoFrame(int width, int height, int align, bool U_first) {
  int pitch = (width+align-1) / align * align;  // Y plane, width = 1 byte per pixel
//  int UVpitch = ((width>>1)+align-1) / align * align;  // UV plane, width = 1/2 byte per pixel - can't align UV planes seperately.
  int UVpitch = pitch>>1;  // UV plane, width = 1/2 byte per pixel
  int size = pitch * height + UVpitch * height;
  VideoFrameBuffer* vfb = GetFrameBuffer(size+(FRAME_ALIGN*4));
  if (!vfb)
    ThrowError("NewPlanarVideoFrame: Returned 0 size image!"); 
#ifdef _DEBUG
  {
    static const BYTE filler[] = { 0x0A, 0x11, 0x0C, 0xA7, 0xED };
    BYTE* p = vfb->GetWritePtr();
    BYTE* q = p + vfb->GetDataSize()/5*5;
    for (; p<q; p+=5) {
      p[0]=filler[0]; p[1]=filler[1]; p[2]=filler[2]; p[3]=filler[3]; p[4]=filler[4];
    }
  }
#endif
//  int offset = (-int(vfb->GetWritePtr())) & (align-1);  // align first line offset
  int offset = int(vfb->GetWritePtr()) & (FRAME_ALIGN-1);  // align first line offset
  offset = (FRAME_ALIGN - offset)%FRAME_ALIGN;
  int Uoffset, Voffset;
  if (U_first) {
    Uoffset = offset + pitch * height;
    Voffset = offset + pitch * height + UVpitch * (height>>1);
  } else {
    Voffset = offset + pitch * height;
    Uoffset = offset + pitch * height + UVpitch * (height>>1);
  }
  return new VideoFrame(vfb, offset, pitch, width, height, Uoffset, Voffset, UVpitch);
}


PVideoFrame ScriptEnvironment::NewVideoFrame(int row_size, int height, int align) {
  int pitch = (row_size+align-1) / align * align;
  int size = pitch * height;
  VideoFrameBuffer* vfb = GetFrameBuffer(size+(align*4));
#ifdef _DEBUG
  {
    static const BYTE filler[] = { 0x0A, 0x11, 0x0C, 0xA7, 0xED };
    BYTE* p = vfb->GetWritePtr();
    BYTE* q = p + vfb->GetDataSize()/5*5;
    for (; p<q; p+=5) {
      p[0]=filler[0]; p[1]=filler[1]; p[2]=filler[2]; p[3]=filler[3]; p[4]=filler[4];
    }
  }
#endif
  int offset = int(vfb->GetWritePtr()) & (FRAME_ALIGN-1);  // align first line offset
  offset = (FRAME_ALIGN - offset)%FRAME_ALIGN;
//  int offset = (-int(vfb->GetWritePtr())) & (align-1);  // align first line offset  (alignment is free here!)
  return new VideoFrame(vfb, offset, pitch, row_size, height);
}


PVideoFrame __stdcall ScriptEnvironment::NewVideoFrame(const VideoInfo& vi, int align) { 
  // Check requested pixel_type:
  switch (vi.pixel_type) {
    case VideoInfo::CS_BGR24:
    case VideoInfo::CS_BGR32:
    case VideoInfo::CS_YUY2:
    case VideoInfo::CS_YV12:
    case VideoInfo::CS_I420:
      break;
    default:
      ThrowError("Filter Error: Filter attempted to create VideoFrame with invalid pixel_type.");
  }
  // If align is negative, it will be forced, if not it may be made bigger
  if (vi.IsPlanar()) { // Planar requires different math ;)
    if (align<0) {
      align *= -1;
    } else {
      align = max(align,FRAME_ALIGN);
    }
    if ((vi.height&1)||(vi.width&1))
      ThrowError("Filter Error: Attempted to request an YV12 frame that wasn't mod2 in width and height!");
    return ScriptEnvironment::NewPlanarVideoFrame(vi.width, vi.height, align, !vi.IsVPlaneFirst());  // If planar, maybe swap U&V
  } else {
    if ((vi.width&1)&&(vi.IsYUY2()))
      ThrowError("Filter Error: Attempted to request an YUY2 frame that wasn't mod2 in width.");
    if (align<0) {
      align *= -1;
    } else {
      align = max(align,FRAME_ALIGN);
    }
    return ScriptEnvironment::NewVideoFrame(vi.RowSize(), vi.height, align);
  }
}
------------------------------------------------------------------------------------
目次へジャンプ。

[PR]中古車探しはガリバー:在庫多数、全車保証つき!