유돌이

calendar

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

Notice

2011. 7. 9. 10:55 핫이슈

COM은?
-.컴포넌트 객체 모델 (Component Object Model)의 약자이다. COM은 컴파일과 링크가 되어 있는 바이너리코드로 객체지향 프로그래밍이 가능하게 해주는 윈도우의 기능 중에 하나이다.
-."인터페이스라는 미리 정의된 루틴들을 통해 객체들 사이의 상호작용을 가능하게 해주는 객체기반의 바이너리 표준"라고 MS에서 정의 해 놓았다. 이 말은 객체(Application도 포함)들 사이에 인터페이스가 있는데, 이 인터페이스를 통해 객체간의 공유가 가능하다. 분산환경(Corba, COM) 프로그래밍에는 각각 인터페이스를 정의 하는 표준적인 룰과 그를 응용한 룰(타입 라이브러리 등)이 있다.
-.인터페이스가 상속과 다형성 등의 할 수 있으므로 바이너리 코드기반으로 객체지향 프로그래밍이 가능하다.
-.COM 객체는 위에서 말했듯이 바이너리코드 이다. 이것을 프로세스 단위로 실행 시켜 주어야 한다. 그 프로세스 단위 안에 COM 객체는 1개 이상 있을 수 있다. 또 그 프로세스는 DLL (in-process), EXE (out-of-process) 형태로 만들어질 수 있다.
-.이상 위의 내용을 정리하면 "COM은 인터페이스라는 약속을 정해놓고 특정 프로세스단위 내에 존재하는 클래스를 지금 개발자가 만들고 있는 Application의 인스턴스로 갖다 쓰자." 라고 이해 하자. 추가로 "그 인터페이스를 상속 받아서 다른 COM Class도 만들 수 있다." 라고 이해 하자.
-. 위의 내용을 그림으로 그린 것이 [그림1] 이다.

[그림1] COM의 기본구조.

[그림1] 같이 COM객체는 COM 서버 내에 존재해야 하고, 각각의 COM 객체는 1개 이상의 인터페이스를 통해 Client Application 에게 COM객체 자신의 기능을 노출시킨다. Client Application은 노출된 인터페이스의 GUID (= IID)를 가지고 해당 COM 객체에 접근할 수 있는 것이다. COM 객체 자체가 어떤 일을 하고 그에 따른 함수를 노출하기만 하면(인터페이스를 통해서 ) Client Application 에서는 COM 객체가 어떻게 구현되어있는지 알지 않아도 COM 객체의 기능을 사용할 수 있다. 그렇다면 인터페이스를 이해할 필요가 있을 것이다. 아래에 인터페이스의 기본원리와 어떤 규칙을 따르는지 알아보자.

Interface는?
-.인터페이스는 COM 객체를 외부에서 사용할 수 있도록 하는 매개체 역활을 한다.
-.인터페이스는 함수들의 선언으로만 구성되어있고 COM 객체에서 인터페이스의 함수를 구현해 준다.
-.인터페이스를 서로를 식별하기 위해 GUID 타입의 128bit의 유일한 값을 가진다.
-.인터페이스는 언어 독립적으로 제작가능하고 인터페이스 내의 함수들은 포인터를 통해서 접근할 수 있다.

COM 서버의 형태는?
-. 위에서 COM객체는 COM 서버 내에 존재해야 한다고 설명했는데, COM 서버의 형태에 따란 Client Application에 바인딩이 달리 된다. [그림2]는 이것을 설명하는 그림이다.


[그림2-1 ] In-Process 의 형태


-. [그림2-1]의 In-Process는 COM 서버가 *.DLL로 제작되어있을 경우 이다. DLL로 되어있는 COM 객체에 Client Application이 접근하면 Client Application의 프로세스내에 COM 서버의 인스턴스가 바인딩 된다. 주로 ActiveX 컨트롤이 이런 형태이다. 익스플로어에서 COM서버내의 COM 객체를 호출할 때 익스플로어 프로세스내에 바인딩 되는 것이다. 참고로 ActiveX 컨트롤의 OCX 파일은 구조적으로 DLL과 동일하다.



[그림2-2] Out-Of-Process 의 형태


-. [그림2-2]의 Out-of-process는 COM 서버가 *.EXE로 제작되어있을 경우 이다. EXE로 되어있는 COM 객체에 Client Application이 접근하면 COM 서버는 자체적으로 프로세스를 생성된다. 내부적으로 Client Application에서는 Proxy로 COM서버의 Stub으로 연결된 구조로 In-process 보다 복잡하지만 개발자는 별다른 신경을 쓰지않아도 In-Process와 동일하게 작성하면 된다. 단지 Client Application내에 COM 서버가 인스턴스화 되는지 아니면 다른 프로세스로 생성 되는지 가 중요하다. 그래서 여러 Client Application에서 하나의 COM 객체를 호출할 때 Out-of-Process의 경우에는 호출한 Client Application의 수 만큼 COM 서버 프로세스가 생성되고, In-Process의 경우에는 호출한 Client Application의 수 만큼 COM 서버 인스턴스가 생성되는 것이다.

-. 그 외로 다른 컴퓨터의 COM서버의 COM객체를 호출하는 구조의 Remote COM 이 있다. 하지만 이 문서에는 따로 언급을 하지 않겠다.

COM의 확장이란?
-.MS나 그 외 여러 문서를 살펴보면 OLE, ActiveX, COM 등의 정의를 분명하게 하지않아서 혼동의 여지가 있다. 어떤 곳에서는 COM에서 OLE한 면이 없으면 ActiveX 이라고도 하고 OLE3.0에서 부터 COM를 ActiveX라고 부르기도 하지만 모두 틀린 말은 아니다.
-.그래도 계속 혼동스럽 다면 기본 COM에 추가된 기능, 확장된 기능 이라고 생각하자. (단어에 연연하지 말자는 뜻 이다.)
•.Automation Server (독립실행 가능한 Application(EXE)이나 DLL이 프로그래밍을 할수 있도록 객체(COM객체)를 다른 애플리케이션에 제공하는것)
•.Automation Control (위에서 설명한 Automation Sever에서 제공된 COM 객체를 이용하는 Client Application)
•.ActiveX Control(프로그래밍의 언어에 상관 없이 일반 컴포넌트처럼 사용할 수 있고 웹에서도 사용 가능한 객체)
•.Type Library (COM 서버내의 COM객체에 대한 정보를 Client Application에서 사용 가능하도록 하는 바이너리 코드. 표준적인 방식으로 저장되므로, 프로그래밍의 언어와 상관없이 읽을 수 있다. 또 COM 서버파일(DLL)등에 함께 저장될 수 있고 따로 저장해서 링크해서 사용할 수 있다. )
-.그 외 ActiveX Documemt , DCOM, MTS 등은 COM를 기초로 확장된 모습이다.

실습 (간단한 COM서버(DLL=In-Process)와 Client Application 만들기. Delphi5 Ent 기준)

-. COM 서버와 COM 객체 만들기.
1. 모든 프로젝트를 종료하고 File|New 해서 New Items 대화상자에서 --> ActiveX 탭 --> ActiveX Library 를 선택하고 [ OK ]버튼을 클릭한다. 여기서 추가한 ActiveX Library는 일반 DLL과 같은 파일을 생성하지만 COM객체를 레지스트리에 등록하고 해제하고 COM 서버 내의 특정 COM객체를 얻을 수 있는 함수를 Export 한다.

2.File|Save 해서 프로젝트명을 COMSrv.dpr로 저장한다. 다시 File|New --> ActiveX 탭 --> COM Object 를 선택하고 [ OK ]버튼을 클릭한다. CoClassName 을 COMObject로 하고 나머지 옵션은 기본사항으로 하고 [ OK ]버튼을 클릭한다.

옵션 설명
•.Instancing
-. Internal = COM객체를 COM 서버 내부적으로만 사용 가능하게 생성한다.
-. Single Instance = Application 마다 하나의 COM 인터페이스 만을 허용. 다른 인스턴스를 요구하면 COM 서버자체를 또 생성한다.
-. Multiple Instance = 여러 Application에서 서버객체를 연결 하면 인스턴스를 계속 새로 성성 한다. In-Process는 이것만 적용 가능하다.
•. Thread Model
-. Single = COM 서버전체가 하나의 쓰레드에서 생성한다. 쓰레드 사용이 불가능 하다.
-. Apartment = COM 객체는 각각의 쓰레드에서 생성된다. STA 라고 부르고  [그림 3-1]의 모습이다.
-. Free = Client Application은 어떤 시점이나, 어떤 쓰레드 상에서도 COM 객체의 메소드를 호출할 수 있다. MTA 라고 부르고 [그림 3-2]의 모습이다.
-. Both = STA와 MTA 둘 다 사용 가능하다.

[그림 3-1] Single Thread Apartment 모델 [그림 3-2] Mutipul Thread Apartment 모델

그림3-1처럼 STA의 경우에는 COM 객체를 생성한 Thread 만이 해당 COM 객체를 접근할 수 있다. 다른 Thread에서 생성한 COM객체는 접근할 수 없다. 그림3-2 처럼 MTA의 경우에는 COM 객체를 생성한 Thread 말고도 다른 쓰레드 에서도 COM객체에 접근할 수 있다. 개발자가 프로젝트가 쓰레드를 사용하지 않는 다면, STA로 만들고 별로 신경 쓰지 않아도 된다.

3. 타입 라이브러리에서 ICOMObject (COM 객체의 인터페이스)에 팝업메뉴을 띄워서 method를 새로 추가한다.

4. Method 이름을 "callDialog" 로 변경하고 오른쪽 파라메타 탭에서 파라메타를 1개 추가 한다. Name = uName , Type = BSTR

5. Method 를 한 개 더 추가해 이름을 "getNowTime" 로 변경하고 오른쪽 파라메타를 1개 추가한다. Name = nowtime, Type = BSTR* , Modifier = [out, retval] nowTime 파라메타는 호출한 Application으로 리턴할 수 있다.

6. Refresh 버튼을 눌러서 현재 상태를 적용한다.
-. 참고로 기본 리턴값인 HResult 는 COM서버의 내장된 객체의 메소드를 호출이 성공, 실패의 리턴값을 돌려준다.

7. File|SaveAll 해서 Unit1.pas를 ComSrvImpl.pas 로 저장한다. 그리고 다음과 같이 소스코드를 작성한다.

 unit ComSrvImpl;
 interface
 uses
   Windows, ActiveX, Classes, ComObj, COMServer_inpro_TLB, StdVcl;
 type
   TMyCOMObject = class(TTypedComObject, IMyCOMObject)
   protected
     function callDialog(const uName: WideString): HResult; stdcall;
     function getNowTime(out nowTime: WideString): HResult; stdcall;
   end;
 implementation
 uses ComServ, Dialogs, SysUtils;
 function TMyCOMObject.callDialog(const uName: WideString): HResult;
 begin
    ShowMessage('이름: ' + uName  +#13#10 + 'COM 서버에서실행된 대화상자');
 end;
 function TMyCOMObject.getNowTime(out nowTime: WideString): HResult;
 begin
    nowTime := TimeToStr(Now);
 end;
 initialization
   TTypedComObjectFactory.Create(ComServer, TMyCOMObject, Class_MyCOMObject,
     ciMultiInstance, tmApartment); //COM Object Factory 에 인스턴스 등록 
 end.

-. Client Application 만들기


[그림 4] Client Application 폼

1. 그림4 처럼 폼을 디자인 한다. 그리고 아래와 같이 소스코드를 작성한다.

  uses COMSrv_TLB, COMObj;
  {$R *.DFM}
  procedure TForm1.Button1Click(Sender: TObject);
  var
    COMObj: ICOMObject;
    strTime: wideString;
  begin
   COMObj := CoCOMObject.Create;
   oleCheck(COMObj.callDialog(Edit1.Text));
   oleCheck(COMObj.getNowTime(strTime));
   Label2.Caption := '호출된 시간: ' + strTime
  end;
 

2. 테스트.
-. 일반적인 DLL 파일은 실행파일과 같은 디렉토리나 Windows\System\ 디렉토리에 위치하면 되지만 COM 서버 DLL은 위치에 상관없도록 레지스트리에 등록해서 사용한다.
-. 레지스트리에 등록되는 내용은 ClassID (CLASS_), Type Libray ID (LibID_), InterfaceID(IID_) 등에 DLL 파일의 위치와 ProID 등이 저장된다.
-. 등록하는 방법은 COMSrv.dpr프로젝트를 Open 하고 RUN 메뉴에 Register ActiveX Server를 선택하면 된다. Out-Of-Process 경우 RUN만 한다.
-. Client Application을 실행하고 [COM 객체의 함수 호출] 버튼을 클릭한다. 그러면 메시지 창과 함께 현재시간이 Label1에 출력되면 COM 서버를 레지스트리 에서 위치를 얻고 난 후에 해당 인터펭이스를 통해 COM서버를 Client Application 내에 바인딩 한다.

Automation 서버 / Client 만들기

- Automation 서버 만들기 ( Out-Of-Process )

-.델파이에서는 자동화 컨트롤러가 자동화 서버를 호출하는 3가지방법을 제공한다.
1.Virtual Method Table (Vtable) 을 이용하는 방식 - Interface를 이용( 초기 바인딩 )
2.Dispatch Interface 를 이용하는 방식 - Idispatch.invoke()를 이용 ( 후기 바인딩 )
3.Variant를 이용하는 방식( 후기 바인딩 )
참고로 성능 Vtable이 가장 좋고, Dispath Interface가 그 다음이고, Variant는 가장 느리다. 델파이에서는 Vtable을 생성하는 Interface방식으로 Client Application을 만들면 되고, COM 객체는 Dual Interface(Vtable, Dispateh Interface) 형식으로 호환성을 생각해서 작성한다.


[그림 5 ] Automation 서버의 폼

1. 그림5 처럼 File|New Application 해서 폼안에 TShape 컴포넌트를 한 개 추가해서 폼을 디자인한다. File|SaveAll해서 프로젝트이름 AutoSrv.dpr 로 Unit1.pas --> SrvFrm.pas 로 한다.

2. File|New 해서 ActiveX 탭 --> Automation Object 를 선택하고 [ OK ]버튼을 클릭한다. CoClassName을 AutoTest 로 하고 나머지는 기본값으로하고 [ OK ] 버튼을 클릭한다.

3. Type Library 에서 IAutoTest 인터페이스를 클릭하고, setShapeType 메소드를 추가한다. 파라메타 탭에서 paramater name = sType , Type = int 로 파라메타 한개를 추가한다.

5. File Save All 해서 COM 객체 유닛을 AutoImpl.pas로 저장한다.

6. AutoImpl.pas 파일에 선언되어있는 setShapeType 메소드를 구현한다.

  uses ComServ,SrvFrm, extCtrls ;
  procedure TAutoTest.setShapeType(sType: SYSINT);
  begin
    Form1.Shape1.Shape := TShapeType(sType);
  end; 

7. 전체를 다시 저장하고, COM 서버를 레지스트리에 등록한다. Out-Of-Process(.Exe)는 실행만 해도 레지스트리에 자동 등록 된다.

- Automation Control 만들기


[그림6 ] Automation Control의 폼

1. 그림6 처럼 File | New Application 해서 폼안에 TEdit 한개와 TUpDown 1개 Associcate 속성을 Edit1 으로 한다. 그리고 TButton 컴포넌트 4개를 추가하고 위에서부터 vTable호출 (Button1) ~ 연결해제 (Button4) 로 캡션을 입력한다.

2. 소스코드를 아래와 같이 작성한다.

  uses AutoServer_TLB, COMObj;
  {$R *.DFM}
  var
    AutoTest: IAutoTest;  // interface 이용
    AutoTestDisp: IAutoTestDisp; //dispinterface 이용
    vAutoTest: Variant; // variant 이용

  procedure TForm1.Button1Click(Sender: TObject); // Interface(Vtable)을 이용해서 COM 객체 생성
  begin  
     AutoTest := CoAutoTest.Create;
    AutoTest.setShapeType(UpDown1.Position);
  end;

  procedure TForm1.Button2Click(Sender: TObject); // Dispatch Interface를 이용해서 COM 객체 생성
  begin
    AutoTestDisp := CreateComObject(Class_AutoTest) as IAutoTestDisp;
    AutoTestDisp.setShapeType(UpDown1.Position);
  end;
 
  procedure TForm1.Button3Click(Sender: TObject); // Variant 변수에 Program ID 값을 넣고 COM 객체 생성
  begin
    vAutoTest := CreateOleObject('AutoServer.AutoTest');
    vAutoTest.setShapeType(UpDown1.Position);
  end;

  procedure TForm1.Button4Click(Sender: TObject);
  begin
    AutoTest := nil; // Pointer 변수 해제
    AutoTestDisp := nil; 
    vAutoTest := Unassigned; //Variant 변수 해제 
  end;

3. 모두 저장하고 Automation Control (Client Application ) 을 실행한다. UpDown 컴포넌트의 값을 0 ~5 사이에서 변경하면서 테스트 해보자.

Excel 97, 2000 을 Control 하는 Automation Controler 만들기

-. MS사에서 만든 많은 제품들이 COM 객체로써 외부에서 Control 할 수 있도록 만들어져 있다. 이번 실습은 Excel 97, 2000 을 간단히 Control 할 수 있도록 COM Controler 를 만들어 본다.


[그림7 ] Excel Controler 폼

- . 델파이 5 에서 Server 탭에 MS의 Office 제품군을 Control 할 수 있도록 컴포넌트를 만들어 놓았는데, 기본적으로 Interface (VTable) 방식으로 접근할수 있다. 물론 다른 방식도 가능하지만 위에서 설명했듯이 Automation 서버를 Control하는 Client Application 을 델파이에서 만들때에는 Interface 방식의 성능이 가장 우수하다.

1. File|New Application 해서 그림7과 같이 TMemo 컴포넌트 1개와 TButton 3개, Server탭에서 TExcelApplication, TExcelWorkSheet, TExcelWorkBook 1개를 폼안에 추가한다.

2. 아래와 같이 소스코드를 작성한다.

  var
    vExcel: Variant;
 implementation
  uses COMObj;

  procedure TForm1.Button1Click(Sender: TObject);
  Var 
    i: Integer;
  begin
    varExcel := CreateOleObject('Excel.Application');
    varExcel.workbooks.Add;
    for i := 0 to Memo1.Lines.Count-1 do
      varExcel.workbooks[1].worksheets[1].Cells[i+1, 1] := Memo1.Lines[i];
    varExcel.Visible := true;
  end;

  procedure TForm1.Button2Click(Sender: TObject);
  var
    i: Integer;
  begin
    ExcelApplication1.Connect;
    ExcelWorkbook1.ConnectTo(ExcelApplication1.Workbooks.Item[1]);
    ExcelWorkSheet1.ConnectTo(ExcelWorkBook1.Sheets[1] as _WorkSheet);
    with ExcelWorkSheet1 do
      for i := 0 to Memo1.Lines.Count-1 do
        Cells.Item[i+1, 1] := Memo1.Lines[i];
    ExcelApplication1.Visible[0] := True;
  end;

  procedure TForm1.Button4Click(Sender: TObject);
  begin
    if not varIsEmpty(varExcel) then
      varExcel.quit;
    if not varIsEmpty(ExcelApplication1.Application) then
    begin
      ExcelApplication1.Disconnect;
      ExcelApplication1.Quit;
    end;
  end;
 

3. 실행 하고 버튼을 눌러본다. 위의 예제는 PC에 Excel 프로그램이 설치되어 있는 경우에만 제대로 동작 한다.

-이외에도 Delphi에서는Excel의 차트등 거의 모든 객체를 Control 할수 있다. Excel .vbs 파일을 생성해서 실행할수도 있다
아래 소스코드는 Excel 내에 특정 매크로를 생성해서 실행시키는 예제 이다.

  procedure TForm1.Button1Click(Sender: TObject);
  var
    str: String;
    vModule: OleVariant;
  begin
    str := 'Sub Macro()' + #13#10 +
         'Dim X, Y' + #13#10 +
         'Dim X_END, Y_END' + #13#10 +
         'Dim Min, Max' + #13#10 +
         'If Cells(1, 2) = "N" Then'+ #13#10 +
         'X_END = 21'+ #13#10 +
         'Else'+ #13#10 +
         'X_END = 25'+ #13#10 +
         'End If'+ #13#10#13#10+
         'Y_END = Cells(1, 3) + 4'+ #13#10#13#10+
         'For Y = 5 To Y_END'+ #13#10 +
         ' For X = 2 To X_END'+ #13#10 +
         '   Min = Cells(3, X)'+ #13#10 +
         '   Max = Cells(4, X)'+ #13#10 +
         '   Cur = Cells(Y, X)'+ #13#10#13#10+
         '   Cells(Y, X).Font.ColorIndex = 2'+ #13#10 +
         '   Cells(Y, X).Font.FontStyle = "굵게"'+ #13#10#13#10+
         '   If (Min <= Cur) And (Cur <= Max) Then'+ #13#10 +
         '     Cells(Y, X).Interior.ColorIndex = 5'+ #13#10 +
         '   Else'+ #13#10 +
         '     Cells(Y, X).Interior.ColorIndex = 3'+ #13#10 +
         '   End If'+ #13#10 +
         '  Next X' + #13#10 +
         '  Next Y' + #13#10 +
         'End Sub';

    vModule :=  ExcelWorkBook1.VBProject.VBComponents.Add(1);
    vModule.CodeModule.AddFromString(str);
    ExcelApplication1.Run('Macro');
  end; 

'핫이슈' 카테고리의 다른 글

★냉동인간★ 부활시킬 단초 발견!!!  (0) 2011.07.09
고지전 예고편!!  (0) 2011.07.09
나도가수다!! 도플겡어들..ㅋ  (0) 2011.07.08
연금복권 인터넷구매 방법!!!!  (0) 2011.07.08
옵티머스3d 상세스펙  (0) 2011.07.08
posted by 유돌이
2011. 7. 5. 13:11 델파이
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, OleCtrls, SHDocVw, ActiveX;

type
  TForm1 = class(TForm)
    WebBrowser1: TWebBrowser;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure WBLoadHTML(WebBrowser: TWebBrowser; HTMLCode: string) ;
var
   sl: TStringList;
   ms: TMemoryStream;
begin
   WebBrowser.Navigate('about:blank') ;
   while WebBrowser.ReadyState < READYSTATE_INTERACTIVE do
    Application.ProcessMessages;

   if Assigned(WebBrowser.Document) then
   begin
     sl := TStringList.Create;
     try
       ms := TMemoryStream.Create;
       try
         sl.Text := HTMLCode;
         sl.SaveToStream(ms);
         ms.Seek(0, 0);
         (WebBrowser.Document as IPersistStreamInit).Load(TStreamAdapter.Create(ms)) ;
       finally
         ms.Free;
       end;
     finally
       sl.Free;
     end;
   end;
end;

procedure TForm1.FormCreate(Sender: TObject) ;
var
  sHTML : string;
begin
  sHTML := '<a href="http://www.howto.pe.kr">GOTO</a>' +
           '<b>howto.pe.kr</b>';
  WBLoadHTML(WebBrowser1,sHTML) ;
end;

end.

posted by 유돌이
2011. 7. 5. 13:10 델파이

Creating a Dynamic Link Library

The following few lines will demonstrate how to create a simple DLL using Delphi.

For the beginning start Delphi and select File | New ... DLL. This will create a new DLL template in the editor window. Select the default text and replace it with the next piece of code.

 library TestLibrary;
 
 uses SysUtils, Classes, Dialogs;
 
 procedure DllMessage; export;
 begin
   ShowMessage('Hello world from a Delphi DLL') ;
 end;
 
 exports DllMessage;
 
 begin
 end. 
If you look at the project file of any Delphi application, you’ll see that it starts with the reserved word Program. By contrast, DLLs always begin with the reserved word Library. This is then followed by a uses clause for any needed units. In this simple example, there then follows a procedure called DllMessage which does nothing except showing a simple message.

At the end of the source code we find an exports statement. This lists the routines that are actually exported from the DLL in a way that they can be called by another application. What this means is that we can have, let's say, 5 procedures in a DLL and only 2 of them (listed in the exports section) can be called from an external program (the remaining 3 are "sub procedures" in a DLL).

In order to use this simple DLL, we have to compile it by pressing Ctrl+F9. This should create a DLL called SimpleMessageDLL.DLL in your projects folder.

Finally, let's see how to call the DllMessage procedure from a (statically loaded) DLL.

To import a procedure contained in a DLL, we use the keyword external in the procedure declaration. For example, given the DllMessage procedure shown earlier, the declaration in the calling application would look like :

 procedure DllMessage; external 'SimpleMessageDLL.dll' 
the actual call to a procedure will be nothing more than
 DllMessage; 
The entire code for a Delphi form (name: Form1) with a TButton on it (name: Button1) that's calls the DLLMessage function could be:
 unit Unit1;
 
 interface
 
 uses
    Windows, Messages, SysUtils, Variants, Classes,
    Graphics, Controls, Forms, Dialogs, StdCtrls;
 
 type
    TForm1 = class(TForm)
      Button1: TButton;
      procedure Button1Click(Sender: TObject) ;
    private
      { Private declarations }
    public
      { Public declarations }
    end;
 
 var
    Form1: TForm1;
 
   procedure DllMessage; external 'SimpleMessageDLL.dll'
 
 implementation
 
 {$R *.dfm}
 
 procedure TForm1.Button1Click(Sender: TObject) ;
 begin
    DllMessage;
 end;
 
 end. 

posted by 유돌이
2011. 7. 5. 13:08 델파이

function ReadUnicode(fName: String): WideString;
var
  F: File;
  FileSize, ReadSize: Cardinal;
  SearchRec: TSearchRec;
  Buffer: WideString;


begin
  FileSize:=0;
  if FindFirst(fName, faAnyFile, SearchRec)=0 then begin
    FileSize:=SearchRec.Size;
    FindClose(SearchRec);
  end;

 

  AssignFile(F, fName);
  SetLength(Buffer, FileSize);
  Reset(F, 1);
  BlockRead(F, PAnsiChar(Buffer)^, FileSize, ReadSize);

 

  Result:=Buffer;
  CloseFile(F);
end;

 

 

사용예

procedure TForm1.Button1Click(Sender: TObject);

const 파일경로 = 'C:\유니코드파일.dat';

var sList: TStringList;

begin

  sList:=TStringList.Create;

  try

    sList.Text:=ReadUnicode(파일경로);

    Memo1.Text:=sList.Text;

  finally

    FreeAndNil(sList);

  end;

end

posted by 유돌이
2010. 1. 11. 14:36 델파이

간단한 예제 입니다.

[uses : ShellApi 추가!]

 

function DeleteDirectory(Const DirPath: String): Boolean;
var
  SHFileOpStruct: TSHFileOpStruct;
  DirBuf: array [0..255] of char;
  Directory: string;

begin
  try
    Directory := ExcludeTrailingPathDelimiter(DirPath);
    Fillchar(SHFileOpStruct, sizeof(SHFileOpStruct), 0);
    FillChar(DirBuf, sizeof(DirBuf), 0);
    StrPCopy(DirBuf, Directory);

    with SHFileOpStruct do
    begin
      Wnd := 0;
      pFrom := @DirBuf;
      wFunc := FO_DELETE;
      //fFlags := fFlags or FOF_ALLOWUNDO; // 휴지통에 담기
      fFlags := fFlags or FOF_NOCONFIRMATION;
      fFlags := fFlags or FOF_SILENT;
    end;
    Result := (SHFileOperation(SHFileOpStruct) = 0);
  except
    Result := False;
  end;
end;


posted by 유돌이
2010. 1. 11. 14:35 델파이

function ResponseCodeCheck(URL: String): Boolean;
var
  IdHTTP1: TIdHTTP;

begin
  Result := False;
  IdHTTP1 := TIdHTTP.Create;
  try
    IdHTTP1.Get(URL);

    case IdHTTP1.ResponseCode of
      200:
      begin
        Result := True;
        OutputDebugString('OK');
      end;

     403:OutputDebugString('Forbidden');
     404:OutputDebugString('Not Found');
     500:OutputDebugString('Server Error');

      else begin

        OutputDebugString('The Others ');

      end;
    end;
 finally
    FreeAndNil(IdHTTP1);
  end;


posted by 유돌이
2009. 6. 26. 14:02 델파이
now는 현재의 년 달 날짜 시간 까지 리턴해주는 함수고

formatDatetime함수를 이용해서 string으로 변환 할 수 있습니다.
 
 
(예제) 
   NowDate := formatDateTime('yyyy-mm-dd',now);

'델파이' 카테고리의 다른 글

중복 실행 방지 (Mutex)  (0) 2009.06.26
Caps Lock 체크 하는 방법  (0) 2009.06.26
델파이 단축키  (0) 2009.06.26
델파이에서 case문 사용방법  (0) 2009.06.26
한글문자를 초성, 중성, 종성으로 나누기  (0) 2009.06.26
posted by 유돌이
2009. 6. 26. 13:59 델파이

function fnStringGridToExcel(StringGrid1: TStringGrid) : Boolean;
var
XL, XArr: Variant;
i, j: Integer;
XLastPosion: String;
Sp : Integer;
begin

//데이타 처리변수
XArr := VarArrayCreate([1, StringGrid1.ColCount], VarVariant);

try
//엑셀을 실행
XL := CreateOLEObject('Excel.Application');
except
MessageDlg('Excel이 설치되어 있지 않습니다.', MtWarning, [mbok], 0);
Exit;
end;

XL.WorkBooks.Add; //새로운 페이지 생성
// XL.Visible := True;

for i := 0 to StringGrid1.RowCount - 1 do begin
for j := 0 to StringGrid1.ColCount - 1 do begin
XArr[j + 1] := StringGrid1.Cells[j, i];
end;


Sp := StringGrid1.ColCount Div 26;
if Sp <> 0 then
XLastPosion := CHR(64 + Sp) + CHR(64 + StringGrid1.ColCount Mod 26)
else
XLastPosion := CHR(64 + StringGrid1.ColCount);

//엑셀에 값을 넣는다.
XL.Range['A' + IntToStr(i + 1), XLastPosion + IntToStr(i + 1)].Value :=
XArr;

end;
//셀 크기 조정
XL.Range['A1', XLastPosion + IntToStr(i + 1)].Select;
XL.Selection.Columns.AutoFit;
XL.Range['A1', 'A1'].Select;
XL.Visible := True;
Result := True;
end;


posted by 유돌이
2009. 1. 5. 18:13 델파이

지난회에서는 소스필터와 변환필터 각각의 종류에 대하여 설명하였습니다. 그렇다면 이제 DShow에서 제공하는 중요 기본 필터들을 살펴보고, 그 특성과 사용방법에 대하여 강의할 것입니다. 일단 여러분이 밥먹듯이 익히고 사용해야 할 주요 필터들의 항목은 다음과 같습니다. 다음의 필터는 GraphEdit의 '필터삽입윈도우'에서 DirectShow Filters 카테고리에 있는 것들입니다.  

 => Avi Mux, Avi Splitter, Color Space Converter, File Source(Async.), File Writer, Infinite Pin Tee Filter, Overlay Mixer, Sample Grabber, Smart Tee, Video Renderer

위에서 Overlay Mixer 나 Infinite Pin Tee Filte 를 제외하고는 DShow를 대변하는 주요 필터라고 할 수가 있겠습니다.
DShow용 어플을 개발하는데 있어서 거의 필수적이라고 생각하셔도 무방할 것입니다. 그럼 하나씩 알아보도록 하겠습니다. 

 (1) Avi Mux

통상 '먹스'라고 부릅니다. 여러분은 '믹싱'과 '먹싱'에 대하여 구분하셔야 합니다.
믹싱은 두개이상의 데이터를 하나에 완전히 혼합시키는 것을 의미합니다. 예를 들어서 강남콩이 반쯤 담겨있는 통에 좁쌀을 붓고서 마구 흔들어 섞어지는 형태를 의미한다고 할 수 있겠습니다. 강남콩과 좁쌀이 완전히 뒤섞여 다시 원래대로 분류하기가 힘들게 되어 버리는 것을 의미합니다. 몇달전에 비디오 스트림 안에 자막을 '믹싱'처리 해달라는 의뢰를 받은 적이 있었습니다. 이것은 즉 비디오 영상의 각 프레임마다 자막을 이미지 위에 그려넣어달라는 것을 의미합니다.

자, 그렇다면 '먹싱'이란 무엇을 의미하는 것일까요. 먹싱은 하나의 통에 강남콩과 좁쌀을 별도로 비닝봉지에 넣어 보관하는 상태를 의미합니다. '먹싱' 을 예로 들때에 대표적으로 영상과 음성의 스트림입니다. 영상과 음성은 서로 독립된 형태의 데이타로서 보존해야 하기때문에 대부분 '먹싱'처리를 합니다. 여러분의 하드디스크에 어떤 영화파일이 있다면 한번 울트라 에디트로 열어보시길 바랍니다. 영상과 음성의 데이터 사이에 일정한 간격을 두고 마치 공백처럼 느껴지는 FF값으로 가득차 있는 곳을 간혹 발견하실 수가 있을 것입니다. 이것이 바로 영상과 음성이 나뉘어지는 칸막이 비슷한 것이라고 생각하시면 되겠습니다.

우리가 동영상을 Avi로 저장하기 위해서는 이렇게 일단 '먹싱'를 해야 합니다.  위의 Avi Mux 필터를  GraphEidt 상에서 '필터삽입윈도우'를 사용하여 생성시키면 메인화면에는 Input핀 하나와 OutPut핀 하나가 있는 네모박스 형태의 비주얼한 필터가 보여질 것입니다. 자, 이상태에서 USB 카메라의 입력장치를 하나 필터로 생성시켜 봅니다. 이것은 제가 Video Capture Source 카테고리에 있다고 하였지요. 두개의 필터를 연결시켜 보시면 알겠지만, 연결하자 마자  Avi Mux 필터에는 또다른 하나의 입력핀이 생겨지는게 보이실 것입니다. 

-------------------------------------------------------------------------------------------------
참고 -> Avi Mux 필터의 입력핀은 이렇게 계속해서 생겨집니다. 현재 연결되어 있는 핀보다 항상 갯수가 하나더 만들어지게 되어 있는 것이죠. 만일 여러분이 필터개발자라면 이런식으로 동적 핀을 생성하게 만드는 것도 사실 간단한 일은 아닙니다. 동적핀을 만드는 것이 내부적으로 어려운 것은, 그 핀들을 단순히 생성시키는 것의 문제가 아니라 계속해서 또다른 입력이 들어올 수 있다는 가정하에 내부로직을 준비해야 하기 때문입니다. 
-------------------------------------------------------------------------------------------------

Avi Mux 필터의 입력핀에 비디오 스트림을 연결시키면 하나의 입력핀이 더 생겨집니다. 이때 두번째 입력핀에 오디오 스트림을 연결하고 보면 출력핀은 그대로 하나인 것을 보실 수가 있습니다. 이처럼 영상과 음성 두개의 입력 스트림이 Mux 되어 하나의 스트림으로 출력되는 것입니다. 
 
그럼 여기서 GraphEdit를 사용하여 간단하게 파일을 만드는 법을 설명하겠습니다.  


  Cam Filter ----> Avi Mux -> File Writer    
                            ▲  
  Audio Filter ------┘


위와같이 연결하시고 Play를 하시면 되겠습니다. 물론 Cam Filter는 제가 앞서말한 Video Capture Source 카테고리에 있는 USB 카메라 입력장치를 말합니다. 또한 AudioFilter는 Audio Capture Source 카테고리에 있습니다. 마지막으로 Filter Writer는 Avi Mux와 마찬가지로 DirectShow Filtes 카테고리에 있을 것입니다. Play를 하면 화면에는 어떠한 랜더링 창도 뜨질 않지만, 대신에 하드디스크에 여러분이 지정하신 파일 이름으로 영상과 음성이 먹싱되어 저장되는 중일 것입니다. 마이크가 있으시다면 목소리를 가다듬고 소리를 지르셔도 좋으실 것입니다.

자, 지금까지 '믹싱'과 '먹싱'의 차이에 대하여 알아보았으며 실제로 영상과 음성을 '먹싱'처리하여 저장하기까지 하였습니다. 

(2) Avi Splitter 

Avi Splitter는 '파서 필터'의 일종이라고 전회에 설명하였습니다. 따라서 추가 설명은 하지 않겠습니다. 

(3) Color Space Converter

이 Color Space Converter라는 필터가 아주 고마운 녀석입니다. 이 필터는 입력핀에 YUV형태의 미디어타입이 들어온것을 RGB형태로 변환합니다. 일반적으로 YUV를 RGB로 변환하는 로직을 MMX나 SSE로 최적화된 로직이 있습니다. 하지만 이런 로직을 DShow에서 사용하기 위해서는 별도의 변환필터를 만들거나 해야합니다. 그런데 DShow에서 아예 이런 필터를 준비해 뒀다는 것은 참으로 고마운 것입니다. 이것을 다른 시각에서 본다면 DShow를 사용하면 이런 형태의 서비스를 받을 수가 있다는 의미도 될 것입니다. 

사실 제가 DShow 초보시절에 바로 이 필터의 존재조차도 몰라서 직접 변환로직을 만들기위해 얼마간 끙끙거렸던 적이 있었습니다. 왜냐하면 연결하려는 앞의 필터에서 나오는 출력핀의 미디어 타입에 RGB형이 없고 죄다 YUV형 타입이었기 때문입니다. 머리가 나쁘면 손발이 고생한다고, 눈앞에 뻔히 있는 것을 별도로 만들기 위해 몇일간을 헤메었던 기억이 눈앞에 선합니다. 굳이 있는 것을 별도로 만들 필요는 없을 터이죠. 게다가 테스트한 결과 이 Color Space Converter 필터의 변환 성능은 상당히 최고급 수준입니다. 

(4) Overlay Mixer

이 Overlay Mixer 필터는 예전에 영상이나 자막을 합성할때 사용하던 필터입니다. 그래픽 카드의 오버레이 평면을 이용한 것인데요, 이것은 하나의 디스플레이 평면 위에 또다른 평면이 오버레이 되면서 합성되어지는 방식을 의미합니다. 하도 오래전에 사용해 보아서 기억이 가물가물합니다. 이 필터는 요즘 별로 사용하지 않을 거라는 생각이 듭니다.

자막을 아직도 이 필터를 사용해서 하는지 모르겠습니다. 예전에 지금의 곰플레이어가 아닌 아드레날린이라는 어플이 마악 태동할 초창기에는 자막 처리가 동영상 어플 개발자들의 관심사항 첫순위였습니다. 신화선님의 책에도 자막처리가 독립된 장으로 상당히 할당되어 나와있을 정도였으니까요.
아무튼 언듯 기억나는 것은 메인화면 위에 오버레이 평면이 겹쳐지게 되는데요, 이때 오버레이 평면에 특정한 색을 투명으로 지정을 하면 그 부분만 뻥 뚤려서 아래의 메인화면과 합성되어 보여지는 식입니다. 이것은 CPU에 전혀 부담이 없이 그래픽카드에서 처리해 주는 것입니다.
만일 자막 처리를 CPU에서 '믹싱'의 방식으로 버퍼링 처리를 하였다면 상당한 부담이 되었을 터입니다. 그당시 컴퓨터 성능이 상당히 낮았고, 웬만한 동영상 파일하나 재생 하는데에도 CPU 점유율이 20, 30%까지 치솟았던 점을 감안한다면 왜 그렇게 자막처리에 안달을 볶았는지 지금에서야 이해되는 측면도 있습니다.  아무튼 그냥 이정도만 알고 계셔도 무방하다고 생각합니다.
OverLay에 대해서 더 알고 싶으시다면 차라리 DirectX 게임관련 서적을 참고하시는 게 더 자세하다고 생각합니다. 왜냐하면 OverLay라는 것 자체가 그래픽카드에서 지원되는 자원중 하나이기 때문입니다. 

(5) Sample Grabber

이 Grabber를 사전에서 찾아보면 1. 부여잡는 사람, 강탈자, 욕심꾸러기, 2. 흥미진진한 것, 깜짝 놀라게 하는 것. 이라고 나와 있습니다. 하지만 프로그래밍에서 혹은 전산일반에서 Grabber(그래버)라고 한다면 이 말의 의미는 조금 독특한 성격의 어떤 기능을 대변하고 있습니다. 즉, 어떤 것들 중에서 하나를 추려내는 기능이라는 의미인 것입니다. 아마도 '부여잡는 사람'이라는 첫번째 의미와 비슷할 것입니다만, 그와는 또다른 차원의 전산적 의미를 내포하는 것이라 하겠습니다. 

FA라는 공장자동화 계열에는 비젼시스템이라고 있습니다. 이 시스템은 생산라인에서의 불량을 자동 체크하는 기능을 갖도록 이미지 판독을 위한 카메라와 보드등을 갖추고 있는데요, 여기서도 '그래버 기능'이라는 표현이 사용되어 집니다. 실제로 '그래버 기능'이라는 것은 연속된 스트림에서 어느 한 프레임을 '찰칵' 찍어내는 기능을 의미합니다.
아마도 이게 사실 어떤 것인지 이해하기 어려울 지도 모르겠습니다. 동영상이라는게 연속된 일련의 이미지들인데, 여기서 기껏 한장을 뽑아내는 기능을 별도로 '그래버 기능'이라고 말할 정도까지 까다로운 것인가 하고요...  결론적으로 말씀드린다면 까다롭고요, 까다로울 수 밖에 없는 이유는 각각의 R, G, B  채널과 주파수와의 상관 관계에 있습니다. 아무튼 중요한 것은 이렇듯 FA라는 산업분야의 비젼시스템에서도 '그래버'라는 용어를 사용하고 있다는 것입니다. 

그래버라는 것은 전산일반에서 사전적인 뜻 이외의 독특한 기능을 대변하고 있다는 것을 알고 계시면 좋을 것 같아서 이렇게 설명이 삼천포로 빠졌습니다. 나중에 FA의 비전시스템 SDK 프로그래밍을 하실때 이 '그래버'라는 의미를  지금 기회에 알아두시면 좋을 것 같아서 설명드린 것입니다. 

아마도 다음회부터 우리는 만들고자 하는 테스트 프로그램에서 바로 이 Grabber 필터를 사용할 것입니다. 이것을 사용하여 카메라로부터 들어온 영상 중에서 '착칵'하고 한 프레임의 사진을 뽑아내어 이미지로 저장해 보일 것입니다. 

(6) File Source(Async.)

이 필터는 글자 그대로 소스필터입니다. 보다 정확히 표현한다면 소스필터 중에서 풀모드 형식의 소스필터입니다. 우리는 전회에서 '소스필터'의 종류와 내부기능에 대하여 공부하였습니다. 이 필터 뒤에는 반드시 파서필터가 붙어야 하며, 일반적인 Avi 표준형식이라면 DShow에서 이미 제공하고 있는 위의  Avi Splitter 필터가 달라 붙게 될 것입니다. 

대부분의 풀모드 소스필터가 그러하듯이 이 필터가 하는 일이란 달랑 원하는 동영상 파일을 로딩하는 것입니다. 이런 서비스를 하기 위해서 IFileSourceFilter 라는 인터페이스를 내부적으로 가지고 있는데요, 우리는 이것을 사용하여 원하는 파일을 로딩하게끔 코딩상에 설정하실 수가 있습니다. 

만일 여러분이 GraphEdit에서 본 필터를 생성하였다면, 생성과 동시에 '파일선택 다이알로그 창'이 뜨게될 것입니다.
여기서 원하는 동영상 파일을 선택하면, GraphEdit의 메인화면에 네모난 박스형태의  File Source 필터안에 여러분이 선택한 파일이름이 표시되어 나타날 것입니다. 

DShow SDK를 설치하셨다면 여러분은 이 소스필터의 원형을 직접 살펴보실 수가 있습니다.  

  ===>  C:\DXSDK\Samples\C++\DirectShow\Filters\Async

위 디렉토리에 가시면 File Source 소스필터의 C++ 소스를 만나보실 수가 있습니다. 풀모드 소스필터임에도 불구하고 상당히 복잡하게 되어있습니다. 

-------------------------------------------------------------------------------------------------
참고 => DirectShow SDK에 있는 샘플 필터의 소스를 살펴보시면 우선 무엇인지도 모를 엄청난 양의 소스에 질겁을  하게 될 것입니다. 이것은 어쩌면 당연한 일입니다. 왜냐하면 이곳에 놓인 필터들의 성격은 굉장히 범용적인 사용을  위한 준비로서 다양한 로직을 갖춰놓고 있기 때문입니다. 따라서 여러분이 필터의 기본적인 구조에 대한 이해도 없이 마구잡이로 이곳에 있는 필터들의 소스를 분석하고자 한다면, 그것은 참으로 어리석은 일이 될 것입니다.
참고로 제가 예전에 그랬었습니다. 그냥 죽기 아니면 까무러치기로 파고 들어가면 어차피 클래스고 함수일터인데 해석하지 못할까 싶었기 때문입니다. 그러나 감히 말씀드리자면 이것은 지도없이 낯선 도시를 헤메는 것과 같은 어리석음입니다. 만일 누군가 DShow 필터개발자가 옆에 있어서, 막힐때마다 친절하게 가르침을 배울 수 있는 환경이라면 모를까, 무턱대고 필터의 소스부터 프린트해서 해독하기 위해 밤새고 그러는 것은 전혀 바람직하지 않습니다.
------------------------------------------------------------------------------------------------

(7) File Writer

이 필터의 기능은 글자 그대로 입력된 스트림을 파일로 기록하는 기능을 하고 있습니다. 여러분이 GraphEdit로 필터를 생성시키면 위의 File Source 필터와 비슷하게 '파일저장 다이알로그 창'이 뜨게 됩니다. 물론 이렇게 윈도우가 뜨는것은 얼마든지 DShow 어플의 코딩상에서 내부적으로 처리할 수가 있습니다. 

자, 그런데 이 필터를 설명드리면서 앞에서 한가지 부족했던 부분에 대하여 보충설명을 드려야 하겠습니다. 앞의 서두에서 저는 Avi Mux 필터에 대하여 단순히 영상과 음성의 스트림을 '먹싱'처리하여 하나의 스트림으로 내보내는 역활을 한다고 하였습니다. 그러나 이때 단순히 두개의 데이타를 하나로 합쳐지게 만드는 것이 아니라, Avi 표준구조에 맞게 합쳐지게 한다는 것입니다. 즉, 가장 첫머리에 Avi 헤더가 붙고, 각각의 영상 프레임이나 음성 데이터 마다 식별코드와 파일 사이즈등의 정보들이 붙어 있게 된다는 것입니다.

제가 위와같은 보충설명을 드린 것은 다음과 같은 원인을 설명하고자 하기 때문입니다. 여러분이 만일 GraphEdit상에서 카메라입력장치를 하나 생성시키고, 그 다음에 File Writer를 생성시키고서, 이 두개의 필터를 직접 연결시킨다면, 아마도 '연결을 가능하게 하는 중간 필터를 찾을 수가 없습니다'라는 메시지가 뜨는 것을 보시게 될 것입니다. 즉 카메라 입력장치든지(광의의 필터) 혹은 위에서 언급한 파일소스필터와 파서필터가 붙은 상태에서의 파서필터이든지, 곧바로 File Writer필터를 붙일 수가 없다는 것입니다. 왜냐하면 지금까지 계속해서 강조해 왔듯이 양쪽의 미디어 타입이 일치가 되지 않기 때문이며, File Writer 필터의 입력핀쪽 미디어타입에서 서브타입으로 Avi형식을 요구하고 있기 때문입니다. 즉, 연결하려는 앞쪽의 필터에서 Avi의 완성된 미디어형 타입이 있어야 한다는 것입니다.

위와같은 상황이 이해가 잘 안되신다면, 그냥 일반적으로 File Writer 필터는 앞에 꼭 Avi Mux 필터가 붙어야 한다라고 생각해주시면 되겠습니다. 이것은 마치 위의 File Source(Async.) 필터의 뒤에는 반드시 Avi Splitter필터가 붙어야 한다는 것과 함께 쌍으로 염두에 두시면 좋을 것입니다. 

(8) Smart Tee  

Smart Tee 필터(이하 스마트티 필터)는 Video Renderer 필터 다음으로 DShow에서는 밥먹듯이 사용해야하는 중요한 필터입니다. 이 필터가 필요한 이유를 들어 보겠습니다. 우리가 카메라로부터 들어온 영상을 저장과 동시에 랜더링하고자 한다고 생각해 보십시오. 그렇다면 하나의 입력 스트림을 두개로 쪼개어 흘려 보내야 합니다. 즉, 필터에는 한 개의 입력핀과 두개의 출력핀을 갖되, 출력핀에서 나오는 각각의 동영상은 모두 동일해야 한다는 것입니다. 이것을 다음과 같이 Smart Tee 필터로서 그려보겠습니다. 


            Cam Fitler -> Smart Tee  -->  Video Renderer 
                                            └---->  Avi Mux -> File Writer 


위와같은 연결이 만들어질 것입니다. 위에서 스마트 티 필터는 입력으로 들어오는 한개의 스트림을 가지고 두개로 쪼개어 아랫쪽으로 흘려보내는 역활을 하고 있습니다. 이런 역활을 하는 필터가 없다면 여러분은 직접 이런 역활의 필터를 개발해야 했을 것입니다. DShow에서 스마트 티라는 필터를 미리 마련해 두었으니 우리는 사용만 하는 되는 것이곘죠. 

(9)  Infinite Pin Tee Filter

이 Infinite Pin Tee 필터는 위에서 언급한 Smart Tee 필터와 비슷한 역활을 하는 것입니다. 즉 하나의 입력스트림을  여러개의 출력으로 분배하는 것이죠. 그러나 여러분이 일단 이 필터를 GraphEdit 상에서 생성시키면 달랑 입력과 출력이 각각 1개뿐인 필터로 보여지실 것입니다. 이때 당황하지 마시고 출력핀을 어딘가로 연결시켜 보시기 바랍니다.

그러면 연결되자마자 곧 새로운 출력핀이 한개가 더 만들어 지는게 보이실 것입니다. 그렇습니다. 이러한 방식은 Avi Mux에서 입력핀이 계속 증가하는 것과 동일한 형태인 것입니다. 다시말해서 출력핀은 언제나 연결된 핏의 갯수보다 하나가 많은 상태로 유지가 된다는 것이죠. 이렇듯 Infinite Pin Tee 필터는 Smart Tee 필터와는 다르게 입력스트림을 무한하게 여러개의 출력으로 나누어 보낼 수가 있는 것입니다.  

자, 여러분은 이제 이 Infinite Pin Tee 필터 (일명 무한필터)를 사용하여 Smart Tee 필터처럼 사용할 수도 있습니다. 
즉 아래와 같이 연결할 수 있다는 말이 되겠습니다. 


            Cam Fitler -> Infinite Pin Tee  -->  Video Renderer 
                                             └---->  Avi Mux -> File Writer 
                                             └---->   //항상 연결된 핀갯수보다 하나가 더 많게 된다. 

 

Smart Tee 필터와   Infinite Pin Tee 필터의 역활은 비슷합니다. 하지만 우리는 어떨때 Smart Tee 필터를 사용하고 어떨때 Infinite Pin Tee 필터를 사용해야 하는지에 대해서도 알아야 합니다. 그러기 위해는 필터의 내부 특성을 알아야 하는데요,  여기서는 Help에 나온 사항을 참고하겠습니다.

스마트티는 하나의 입력핀을 단지 두개의 출력핀으로 나눌 뿐입니다. 하지만 각각의 핀에는 독특한 이름이 부여되는데요, 바로 Capture 핀과  Preview 핀이라는 이름입니다. 이 각각의 이름은 중요한 차이를 가지고 있습니다. 물론 사용법도 다르게 되겠지요. 일반적으로 Captuer핀에서 흘러나오는 스트림은 Avi Mux로 연결되어서 동영상을 저장하기 위한 용도로 사용됩니다. 그리고 Preview 핀에서 흘러나오는 스트림은 Renderer 로 향해서 랜더링되어 집니다. 

그런데 만일 이 두가지를 반대로 사용한다면 어떻게 될까요. 만일 여러분이 Capture 핀으로 랜더링을 하고 Preview 핀으로 저장을 한다면 GraphEidt는 에러메시지를 보내게 될 것입니다. 그 메시지에는 '타팀스탬프가 없습니다'라는 문구가 될 것인데요, 이것이 중요한 차이입니다. 즉 에러메시지가 발생한 이유는 Preview 핀에서 스트림을 저장하기 위해 연결한 Avi Mux 필터에서는 반드시 '타임스탬프'가 존재해야 하는데요,  Preview 핀에서 흘러나오는 스트림은 '타임스탬프'가 제거된 상태이기 때문입니다. 이거 왜 이럴까요?

동영상을 랜더링 하면서 동시에 실시간 Write 한다면 필터그래프에는 지연시간이라는게 발생하게 됩니다. 따라서 만일 Preview핀에서 흘러나오는 스트림에 타임스탬프가 존재한다면 지정된 시간에 프레임이 랜더링되지 못하고 드롭되고 마는데요, 이것은 상당히 부자연스러운 차이를 만들게 됩니다. 이처럼 랜더링 되어야할 프레임이 드롭되는 것을 막고자 Preview 핀에서 강제로 타임스탬프를 제거하여 내보내는 것입니다.

일반적으로 동영상을 저장과 동시에 랜더링을 하고자 할 때에는 위와같은 이유로 무한필터보다는 스마트 티 필터를 이용하기를 권장합니다. 장난삼아 무한필터로도 테스트해 보곤 하는데요, 별차이는 없어 보였습니다. 하지만 헬프에 그렇게 나와 있으므로 굳이 무한필터를 사용하지는 마시기 바랍니다. 

++ 타임스탬프 ++
-------------------------------------------------------------------------------------------------

타임스탬프라는 것에 대해서 잠시 설명 드리겠습니다. 타임스탬프는 하나의 미디어물이 최종적으로 랜더링 되어야하는 StartTime과 EndTime을 의미합니다. 예를 들어 우리가 DShow 어플을 Play하면 그 순간부터 절대시간이 생성되어집니다. 0초, 1초, 2초, 3초... 이렇게 말이죠. 이런 상태에서 연속된 프레임에서 각각의 프레임마다 언제 랜더링되어져야 하는지를 가지고 있습니다. 만일 초당 30프레임이라면 가장 첫번째 프레임의 타임스탬프 시간은  StartTime 이 0 이 될 터이고, EndTime이 30/1000 초가 될 것입니다. 두번째 프레임은 어떻게 될까요.이것을 다음과 같이 나타내 보겠습니다.

                1번째 프레임    StartTime     0초             EndTime  0.03초
                2번째 프레임    StartTime     0.03초         EndTime  0.06초
                3번째 프레임    StartTime     0.06초         EndTime  0.09초
                4번째 프레임    StartTime     0.09초         EndTime  0.12초
                        .                    .                                     .
                        .                    .                                     .  

위와 같이 각 프레임마다 StartTime 과 EndTime을 가지고 있다는 것이죠. 그런데 간혹 이것들이 여러개의 필터들을  거치면서 제시간에 도착하지 못하는 경우가 발생될 때가 있습니다. Smart Tee 경우와 함께 일반적으로 네트워크 필터에서도 이런 경우가 발생하여, 저같은 경우도 마지막에는 결국 타임스탬프를 제거해야만 했습니다.

사실 네트워크 소스필터의 경우에는 Smart Tee와 같은 이유 때문에 타임스탬프를 제거한 것이 아니라, 전송지와 수신지와의 동시성을 위해서 어쩔 수없는 선택이었습니다. 예를 들어, 전송지에서 TCP/IP를 통해 서버로 하나의 프레임을 전송했다면 수신지에서는 다시 그 서버에서부터 하나의 프레임을 받아올 것입니다. 그런데 어떤 경우는 통신이 느려졌다가 갑자기 빨라질 경우가 생기는데, 이 경우 전송지의 소켓버퍼와 서버의 버퍼에 그동안 밀려서 쌓여있던 프레임이 한꺼번에 전달되기 때문에 문제가 발생 합니다. 만일 타임스탬프가 존재하고 순식간적으로 한꺼번에 밀려들어온 각각의 프레임에 일정시간을 동일하게 배분 한다면 전송지와 수신지와의 랜더링시간은 계속해서 차이가 벌어지게 될 것입니다. 저는 약 1시간 가까이 딜레이 되는 것을 지켜봐야 했습니다. 참으로 놀라운 딜레이 현상이었습니다.


출처 : 델마당  dong님의 글(dongsoft)

posted by 유돌이
2009. 1. 5. 18:11 델파이

지금까지 우리는 간단한 USB 카메라용 DShow 어플을 제작하였습니다. 너무도 간단한 것이었지만 이해하기가 쉽지는 않았을 터인데요, COM에 대해서는 좀더 체계적인 공부를 하라고 권해 드리고 싶습니다. 자, 이번장 부터는 DShow 의 필터에 대하여 공부해 보겠습니다. 우리는 필터들의 내부구조를 조금씩 들여다보고, 그 각각의 역할과 의미를 짚어본 다음에 DShow에서 제공하는 몇몇 기본 필터들에 대하여 공부할 것입니다. 이 지식들을 활용하여 전장에서 실습하였던 USB 카메라의 랜더링 프로그램을 수정하여 간단하게 AVI 파일로 저장하는 것과 중간의 프레임에서 원하는 이미지 사진을 뽑아내는 기능을 추가할 것입니다. 

[1] DShow 필터의 역할과 구조. -- (소스필터)   

우리는 지난회에서 DShow의 버퍼공유에 대하여 언급하였습니다. DShow에 있어서 버퍼공유가 얼마나 중요한 가에 대해서는 그당시 적절하게 설명을 하였다고 생각합니다. 그렇다면 이제 버퍼공유의 내부적인 원리를 약간 살펴보고  넘어가야 할듯 싶습니다. 이 원리는 중요성에 비하여 그리 거창하게 복잡한 것은 아닙니다. 아주 간단한 예를 들어 보겠습니다. 우리가 압축안된 AVI라는 파일을 TFileStream이라는 객체를 통하여 로딩하였다고 생각해 봅니다. 그리고 로딩한 파일에서 한 프레임에 해당하는 버퍼를 일정시간마다 읽어봅니다.
 
procedure TForm1.Run_ButtonClick(Sender: TObject);
var
  FileStm : TFileStream;
  Buff : PChar;
begin
  FileStm := TFileStream.Create('C:\무압축다이하드.avi', fmOpenRead );
  GetMem(Buff, 320*240*3);  //320 * 240의 크기에 RGB24라면...

  while FileStm.Position < FileStm.Size do
  begin
    FileStm.Read(Buff^,320*240*3);    //파일에서 하나의 프레임을 읽어낸다.
    Transfer(Buff);                             //Transfer 함수로 버퍼의 포인터만 보낸다. 
    Sleep(33);                                   // 1초당 30개의 프레임이라고 가정한다.
  end;

  FreeMem(Buff);
  FileStm.Free;
end;

 
procedure TForm1.Transfer(var Buff: PChar);
begin
  //비디오 프레임을 변환시킬 일이 있다면 변환시킨다.
  Renderer(Buff);
end;

 
procedure TForm1.Renderer(var Buff: Pchar);
begin
   //Buff로 랜더링을 한다.
end;

 

만일 DShow의 COM Object구조가 아닌, 일반 어플에서 시도를 했다면 위와 같은 구조를 생각해 볼수가 있을 것입니다. (실제로 적용한다면 AVI파일의 헤더를 읽는 부분과 위의 Renderer 함수에서 비디오 카드의 DirectDraw평면을 가져와서 그곳에 데이터를 옮기는 작업을 하게될 것입니다.) 자, 위의 프로그램은 아주 간단합니다. 압축안된 AVI 파일을 읽어서 그것을 일정시간 간격으로 계속해서 두개의 함수, Transfer과 Renderer 함수를 실행시킨다는 것입니다. 

상식적으로 위의 모든 과정은 하나의 함수안에 포함시킬 수가 있습니다. 필터도 마찬가지입니다. 우리가 소스필터나  변환필터나 랜더러필터도 각각 그 역할을 나눠놓는 것이 사용하기 편하기 때문에 한것이지, 굳이 구조적으로 반드시  그렇게 해야할 이유는 없다는 것입니다. 그렇다면 우리는 하나의 의문이 생깁니다. DShow는 버퍼공유가 중요하다고 했는데, 아니 하나의 필터에 몽땅 집어 넣을수가 있다면 굳이 버퍼공유라는 이유 때문에 복잡한 여러종류의 필터를 연결해서 사용해야만 하는 이유는 무엇인가.  

우리가 Visual C++을 한다는 것은  MFC를 사용한다는 것과 마찬가지이고, 델파이를 한다는 것은 VCL을 사용한다는 것과 마찬가지이듯이, DShow를 한다는 것은 결국 기본적으로 제공되는 수많은 DShow의 필터를 이용할 수 있다는 것과 동일한 의미일 것입니다. 즉, 'DShow는 버퍼공유를 위해서 필터형식으로 되어있다'는 것이 아니라, '동영상 어플 개발에 있어서 다양한 필터형식의 서비스를 제공함에도 불구하고 버퍼공유를 한다'는 의미로 받아들여야 할 것입니다. 

DShow에는 다양한 종류의 기본필터가 준비되어 있습니다. 이들 필터를 사용한다는 것은 어플개발을 손쉽게 할 수 있다는 의미 이외에도 윈도우즈라는 운영체제에 있어서 범용인 동영상 어플을 개발할 수 있다는 의미도 있을 것입니다.

다양한 필터형식의 범용적인 서비스를 제공하면서 동시에 하부구조 깊숙히 자리잡은 버퍼공유를 함으로서 동영상 개발을 획기적으로 진보시켰다고 볼수가 있을 것입니다. (그러나 배우기는 어렵다는 거... 쩝.)

Anyway... 이제 다시 본론으로 들어가겠습니다. 위의 예제샘플 프로그램에서 우리는 동영상 어플개발의 기본적인 구조를 살펴볼 수가 있습니다. 파일을 로딩하고, 그것을 루프를 돌려서 한 프레임씩 읽어내고, 읽어낸 것을 변형하고 랜더링한다는 것입니다. 눈치 채셨겠지만, 각각의 함수들은 모두 DShow에서 각각의 필터들의 역할을 대변하고 있습니다. 즉, 파일을 로딩하여 루프를 돌리는 첫 프로시저는 '소스필터'를 의미하고 두번째 Transfer는 말 그대로 변환필터를 의미하며 마지막으로 Renderer함수도 랜더러필터를 의미할 것입니다.  

자, 그런데 소스필터에 있어서는 약간 다르게 구조화되는 경우를 생각해 볼수가 있습니다. 즉, 로딩하는 부분과 루프를 돌려서 한 프레임씩 읽어내는 경우를 별도의 필터로 만드는 것인데요, 이렇게 별도의 필터로 나누어 만드는 방식을 풀모드라고 하고, 하나의 필터에 로딩과 루프를 모두 갖춘것을 푸쉬방식이라고 합니다.
여기서 루프는 사실 스레드를 의미한다고 생각하시면 될 것입니다. 또한 두개의 부분으로 나뉘었을때 앞의 필터를 풀모드의 소스필터라고 하며 뒤의  필터를 '파서 필터'라고 합니다. 푸쉬 소스필터 하나를 굳이 풀모드 소스필터와 파서필터로 나누어 놓는 것은 DShow 가 각부분의 필터 서비스를 좀더 세밀하게 제공하기 위함이라고 생각하시면 될 것입니다.  

소스필터를 너무 간단히 설명드렸지만, 사실 소스필터는 무지하게 복잡한 구조를 가지고 있습니다. 우선 이것은 수 없이 다양한 Avi파일의 표준구조를 읽어내야하고, 음성과 영상의 프레임을 각각 뽑아내어 동기화작업도 해야합니다. 또한 네트워크를 통해서 들어올 경우에는 소켓을 포함하여 그에 따른 로직이 준비되어 있어야 할 것입니다.
DShow 의 필터 개발자들이 우선 첫번째로 경험삼아 개발하는 것이 변환필터이고, 그 다음으로는 바로 이 네트워크 소스필터인데요 제가 처음에 말씀드렸던 신화선님의 사이트에 가시면 '네트워크 소스필터'로 인하여 울부짓는 질문들이 상당수 있을 것입니다. 네트워크 소스필터가 어려운것은 소켓 프로그래밍 때문이 아니라, 영상과 음성의 싱크문제, 각각의 스레드의 동기화 문제, 버퍼링 문제와 같은 것들입니다. 이들 모두가 소스필터에 해당한다고 보시면 될 것입니다.  


이번에는 파서필터를 직접 만나시겠습니다. 여러분이 만약 GraphEidt로 인터넷에서 다운받은 영화를 Render Media File...이라는 메뉴로 불러오셨다면 메인화면에는 수없이 많은 필터들이 연결되어 있는 것을 보실수가 있을 것입니다.
그 여러종류의 필터들 중에 유독 하나의 필터에서 두개의 Out핀이 나온것을 보실 수가 있는데요, 요놈이 바로 파서필터입니다. 이름은 아마도 'Avi Splitter'라고 적혀 있을 것입니다. 이 Avi Splitter이라는 놈은 결코 우수운 놈이 아닙니다.
 
위에서 설명드린 것처럼 수없이 다양한 Avi파일의 표준구조를 읽어내고 영상과 음성의 프레임을 각각 동기화시켜서 스레드로 푸쉬(다른 필터로 밀어내기)하고 있는  것입니다. 

지금까지 정리하자면 다음과 같습니다. 

  1) 소스필터는 두가지 종류가 있는데, 하나는 푸쉬모드 소스필터이고 다른 하나는 풀모드 소스필터이다. 
  2) 푸쉬모드 소스필터는 영상파일을 로딩하는 것과 스레드 안에서 각 프레임을 뽑어내는 기능 모두를 포함한다. 
  3) 풀모드 소스필터의 경우는 영상파일을 로딩하는 기능만을 가지고 있으며 뒤에는 스레드로 각 프레임을 뽑아내는 기능을 하는 '파서필터'가 별도로 붙는다.  

[2] DShow 필터의 역할과 구조. -- (변환필터)   

이제 변환필터에 대해서 말씀드리겠습니다. 아마도 여러분이 가장 원하시는 종목이 바로 변환필터 만들기 일 것입니다. 일단 이 변환필터를 만드는 데에는 크게 두가지의 경우가 있습니다. 첫째로, 소스필터에서 시작된 영상의 형식을 그대로 두고 버퍼의 내용만 바꾸는 것과, 둘째로 영상의 형식과 내용 모두를 바꿔치기하는 방식입니다. 전자의 것을 InPlace 변환필터라고 하고 후자의 것을 Copy 변환필터라고 합니다. 자, 이 두개가 무슨 의미가 있는지를 설명하겠습니다. 

우리가 앞의 필터에서 흘러나온 스트림을 변환시키기 위해서는 뒤로 흘려보낼 경우까지 모두 고려해야 할 것입니다.
만일 앞의 영상이 YUV의 형태였고, 뒤쪽으로 흘려보내야하는 영상이 RGB24라면 어떻게 될까요. 이경우 어쩔 수 없이 한번의 버퍼링을 반드시 해야할 것입니다. 왜냐하면 뒤쪽의 타입에 맞게 변경해줘야 하기 때문이지요. 그러나 앞과 뒤의 영상타입이 정확히 일치한다면 우리는 굳이 버퍼링을 할 필요가 없습니다. 이 경우 필터는 앞의 필터에서 사용되어진 버퍼의 포인터를 그대로 가져와 사용할 수가 있는 것입니다. 
 
동영상 스트림의 형태는 상당히 까다롭습니다. 이 형태를 '미디어형'이라고 하는데요, DShow를 하기 위해서는 반드시 이 '미디어형'의 전체 구조가 머릿속에 들어가 있어야 합니다. 이것에 대한 의미를 정확히 안다면 필터개발에 있어서 반이상을 정복하셨다고 해도 과언이 아닐 것입니다. 그런데 이 미디어형을 알기 위해서는 각각의 미디어타입의 의미를 또한 알고 있어야 합니다. 예를 들어 대체 YUV는 무엇인가에 대한 해답을 가지고 있어야 한다는 것입니다. 

간단하게 YUV에 대해서 설명해보겠습니다. 우리가 일반적으로 색을 표현할 때에는 Red, Green, Blue 이렇게 세가지의 색을 조합해서 표현하는 RGB 방식을 흔히 사용합니다. 그런데 이 방식은 Image로 표현하는데 있어서는 상당히 정확한 방식이지만, 반면에 인간이 느끼지 못하는 부분까지 구분하고 있기 때문에 정보의 취급 효율면에서는 떨어집니다. 그 효율이 아주 작은 차이라고 하더라도 동영상에서는 무시하지 못할 엄청난 차이가 됩니다. 따라서 동영상에서는 주로 이 RGB 계열의 형식을 사용하지 않습니다. 대신 색의 밝기인 Y성분과 색상인 U와 V성분으로 조절되어지는 YUV 형태를 주로 사용합니다. 이 방식을 사용하는 이유는 효율이 상당히 크기 때문입니다.
일반적으로 인간의 시각은 명도에 민감하고 색상에는 별로 민감하지 않습니다. 예를 들어 320*240 크기의 프레임 이미지라면 명도에 해당하는 Y부분을 320*240 크기로 1바이트씩 배정해 놓고 나머지 색상에 해당하는 U와 V는 각각 네 개마다 하나씩 공유 하게 되어도 큰 문제가 없다는 것이죠.    


      y           y             y            .             .              .

           uv          uv

      y           y             y

 
 
      .

 

자, 위와 같은 경우 이미지의 크기는 반으로 줄어들게 됩니다. RGB의 경우, 픽셀당 각각 1바이트 씩을 차지하므로 모두 3바이트였다면, 위와같은 형식의 YUV인 경우에는 각 픽셀당 Y가 1바이트, UV가 0.5바이트를 차지하게 되므로 전체 메모리는 반으로 줄어들게 되는 것입니다. 그런데 아이러니한 것은, 이렇게 정보의 크기가 반으로 줄어들었음에도 불구하고 RGB의 경우보다 오히려 더 선명하게 느껴진다는 것입니다. 이것은 일종의 착시현상으로 픽셀과 픽셀간의 색차 정보가 흐려지는 결과로 빚어지는 것입니다. 

위의 YUV의 형태를 인식하는 것은 중요한 첫 걸음입니다. YUV의 형태는 실제 다양한데요, 24비트로 된 것도 있고, 16 비트나 12비트, 심지어 8비트로 된 것도 있습니다. 그러나 가장 중요한 것은 이것을 사용했을때의 효능입니다. RGB에 비하여 엄청난 결과를 가져옵니다. 즉, 영상의 화질은 더 부드럽고(비록 착시현상이지만...)  CPU의 점유율은 거의 절반으로 떨어지기 때문입니다. 모든 영화 파일의 기본압축 미디어형이 바로 이 YUV형식 인것도 바로 이 때문인 것입니다. ( 일부를 제외하고 거의 모든 Mpeg의 압축을 위해서 들어가는 기본 형태는 RGB가 아닌 YUV형식이다. 반대로 그 압축된 데이터가 DeCoder 필터를 통해 압축 해제되어 나오는 미디어의 기본 형태도 바로 YUV형식중 하나이다.)

혹 어느 책에서는 YUV가 일종의 압축형태라고 표현하는 곳도 있는데요, 이것은 엄밀히 말하자면 틀린 말입니다. 하지만 그럼에도 불구하고 'YUV로 압축된 형태로 들어옵니다.'라고 표현하는 것은 절반의 데이타 양으로 거의 동일하게 표현하는 효율적인 측명을 지나치게 강조한 것이라고 할 수 있을 것입니다. 아무튼 YUV 이것을 아는게 중요합니다. 나중에 여러분이 필터를 만들게 되면 이 YUV를 직접 눈으로 보실수가 있게 됩니다. 저도 한번 샘플로 YUV를 RGB인 척 하고 랜더링한 적이 있는데요. 다음과 같은 모양이 나왔습니다. 


                  ************************************************
                  ************************************************
                  ************************************************
                  ***********************■************************
                  ***********************■************************
                  ***********************■************************
                  *********************■***■*********************
                  ********************■*****■********************
                  *******************■*******■*******************
                  ******************■*********■******************
                  ************************************************
                  ************************************************
                  ************************************************
                  ************************************************
                  ************************************************


                  ************************  ***********************
                  ***********■***********  **********■************
                  ***********■***********  **********■************
                  **********■*■**********  ********■**■**********
                  *********■***■*********  *******■****■*********
                  ************************  ***********************
                  ************************  ***********************


위에서 보시면 알겠지만, 가장 첫번째 큰 이미지가 Y값을 가진 전체 화면이 되겠고요, 나머지 두개의 작은 이미지가  각각 4개의 Y값에 대응하는 U와 V의 값들이 모여있는 화면입니다. 실제로 버퍼에 이런 식으로 저장이 되어 있는 것을 보고 정말 재미있어 했습니다.  

이야기하다보니 또 샛길로 빠졌습니다. 강의의 깊이를 조절하기가 정말 힘이 드네요. 이번 장에는 무려 5번의 새로쓰기를 하였습니다. 제 나름대로 전체적인 구조를 잡은 상태에서 진행하고 싶었는데요, 지나친 욕심이었나 봅니다. 아무튼 이번장이 중요한 것은, COM 다음으로 DShow의 배경지식이 되기 때문입니다.

변환필터에 대해서 이야기하였는데요, 두가지 형식이 있다고 하였습니다. 하나는 InPlace 변환필터, 일명 제자리 변환 필터라고 불리기도 하고요,  Copy 변환필터, 일명 복사 변환필터라고 합니다. 이 두개의 형식의 가장큰 차이는 전자의 것은 버퍼링이 없다는 것이고요, 후자의 것은 반드시 한번 이상의 버퍼링이 존재한다는 것입니다. 사실 Copy 변환필터가 내부적으로 버퍼링을 해야한다는 것은 당연한 일입니다. 앞에서 들어온 스트림의 미디어형이 뒤쪽으로 나가는 미디어형과 일치하지 않기 때문에, 그 형변환을 위해서는 버퍼링이 필요하고, 버퍼링을 하기 위해서 Copy 변환필터라는 구조가(COM Class가) 만들어진 것이기 때문이지요. 

지금까지의 설명을 종합하겠습니다. 

   1) 변환필터에는 InPlace 형과 Copy 형 두가지가 있다.
   2) InPlace형은 내부 버퍼링이 필요없고, 앞의 필터의 버퍼 포인터를 그대로 사용한다.
   3) Copy 형은 반드시 버퍼링이 필요하고, 앞의 InPut 미디어형과 뒤쪽의 OutPut 미디어형이 일치하지 않을때 사용한다. 
   4) DeCoder 압축해제필터는 일종의  Copy형 변환필터이다.
------------------------------------------------------------------------------------------------
추가해설 --> 엄밀히 이야기하자면 InPlace형 변환필터도 내부 버퍼링을 합니다. 그러나 위에서 내부 버퍼링이 필요없다고 한것은 이해의 편리를 위한 것이라고 생각하시면 될 것입니다. 좀더 정확히 표현하자면 'InPlace필터는 버퍼링을 최대한 하지 않아도 되게끔 지원한다.'는 표현이 맞을 것입니다. 자, 이것에 대해서는 후에 '할당자'를 설명하면서 논하게 될지도 모르겠습니다. 하지만 워낙 Inplace 필터의 구조가 복잡해서, 충분히 설명할 수 있을지 모르겠습니다. 

시간이 있으면 나중에 필터제작하는 시간에 '할당자'에 대한 부연설명을 하면서 보충할 수도 있을 것이지만, 아무튼 현재로는 'InPlace 필터는 내부 버퍼링을 가능한한 줄여주기 위해 지원한다' 는 정도로 이해하시면 좋을 것입니다.


출처 : 델마당  dong님의 글(dongsoft)

posted by 유돌이
prev 1 2 3 next