멤버
private : 자식놈은 여기 있는 함수, 변수 사용못함

protected: 자식놈은 여기 있는 함수, 변수를 private처럼 사용할수 있음.

public: 개나 소나 다 사용할수 있음.

상속
private : 부모의 모든 속성이 private로 변함. 자식 사용 못함.

protected: 부모의 모든 속성이 protected로 변함. 함수, 변수를 protected처럼 사용할수 있음.

public: protected 멤버 이상 사용할수 있음.

'프로그래밍 > C | C++' 카테고리의 다른 글

UPNP 프로토콜  (0) 2013.08.21
FFMPEG 디코딩 API 사용법  (1) 2013.08.16
클래스 가상함수, 순수가상함수  (0) 2013.08.14
윈도우 malloc  (0) 2013.08.14
RTPLIB 1.0b 라이브러리 예제  (0) 2013.08.14
쉽게 설명해서.

가상함수 : 자식놈이 생성안해도 되는 함수.
virtual 부모::printf()
{
 printf("hi\n");
}

virtual 자식::printf()
{
 printf("hi 자식\n");
}

가상테이블에 printf에 대해서 등록하고
자식이 이걸 오버라이딩 하면 자식으로 등록됨.

1. 부모클래스 *a = new(자식클래스);
a->printf(); // 자식놈이 printf()

2. 부모클래스 *a = new(부모클래스);
a->printf(); // 부모 printf();

3. 자식클래스 *a = new(자식클래스);
a->printf(); // 자식 printf();

순수가상함수 : 자식놈이 반듯이 생성해야 하는 함수.
virtual printf()=0;

가상테이블에 함수를 만들고 자식이 생성해야지만 사용되는 함수.
자식이 호출 안하려면 생성 안해도 됨.
 

'프로그래밍 > C | C++' 카테고리의 다른 글

FFMPEG 디코딩 API 사용법  (1) 2013.08.16
클래스 private, protected, public 멤버 및 상속  (0) 2013.08.14
윈도우 malloc  (0) 2013.08.14
RTPLIB 1.0b 라이브러리 예제  (0) 2013.08.14
RTP 문서 한글 번역본  (0) 2013.08.14
struct _CrtMemBlockHeader
{
       struct _CrtMemBlockHeader * pBlockHeaderNext;
       struct _CrtMemBlockHeader * pBlockHeaderPrev;
       char *                      szFileName;
       int                         nLine;
       size_t                      nDataSize;
       int                         nBlockUse;
       long                        lRequest;
       unsigned char               gap[nNoMansLandSize];
} _CrtMemBlockHeader;

메모리 할당 하믄
할당된 메모리 이전에 요 구조체 데이터가 있다.
메모리를 링크 리스트 형태로 관리하는듯.
HeaderNext, HeaderPrev, FileName ??어디에 쓰지??
nLine 요것두??, nDataSize (사용자가 요청한 크기), nBlockUse (할당 되었는지
free되었는지 여부), lRequest pass --;;  nNoMansLandsize = 4byte
이 헤더 다음에 다시 4바이트가 붙고 그후 사용자가 할당한 메모리 주소가
나온다.
앞 4바이트는 뭔지 모르겠음. 그냥 chunk 인가.
 
요 예제..example1.c
example2.c 요거

전송 과 받는 측 예제인데...

RTPExecute()요함수 부분에서 리턴값을 검사를 해서
RTP_OK가 아니면 다음 처리로 넘어가야 하는데

 while (evt_queue != NULL && evt_queue->event_time <= now) {
   /* There is a pending RTP event (currently this means there's
    * an RTCP packet to send), so run it. */
   RTPExecute(evt_queue->cid, evt_queue->event_opaque);
   /* Advance the queue */
   next = evt_queue->next;
   free(evt_queue);
   evt_queue = next;
 }
봐라 소스... 리턴값 검사 안한다.
RTPExecute시, DONT_SEND_NOW가 발생할수 있다.
즉, 지금 현재 보낼수 없고 다음에 보낸다는 것인데.
리턴값 검사안하고 함수 호출후 현 이벤트큐를 지워 버린다.
즉, 상대방에게 SR 또는 RR를 보내지 못하게 된다.

못 보내면 어떻게 되냐고??
SR과 RR정보는 패킷이 몇개 수신 되었고 몇개 짤렸고
언제쯤 도착했다라는 정보가 있고
그 정보로 네트워크 전송 속도를 제어 한다든지 인코딩시
저화질 , 고화질로 선택 할수 있다는거지.

ADSL 라이트로 XVID 인코딩된 데이터 다중채널(2영상) 보내다가
컴퓨터가 디져븠다.

왜 죽을까 원인은 모른다^^
송신 버퍼가 가득 차서 일까^^

RTP 요거 물건이긴 한데
다중채널일시 네트워크 대역폭은 계산해야 한다는거!
 

'프로그래밍 > C | C++' 카테고리의 다른 글

클래스 가상함수, 순수가상함수  (0) 2013.08.14
윈도우 malloc  (0) 2013.08.14
RTP 문서 한글 번역본  (0) 2013.08.14
문자열 처리 함수 정리  (0) 2013.08.14
FFMPEG 압축 기본적인 사용법  (0) 2013.08.14

 

RTP: A Transport Protocol for Real-Time Applications(RFC1889)

 

1. 개요

 

   RTP 오디오, 비디오 시뮬레이션 데이터와 같은 실시간 데이터 멀티캐스트 또는 유니캐스트 네트웍을 이용해서 전송하는 응용 서비스에 알맞은 단말--단말 네트웍 전송 기능을 제공한다.  RTP 자원 예약을 수행하지 않으며, 따라서 적시 전달, 순차 전달과 같은 서비스 품질도 보장하지 않는다. RTP 데이터 전송 기능은 제어 프로토콜에 의해 확장되는데, RTCP 불리우는 제어 프로토콜은 데이터의 전달 상황을 감시하며, 최소한의 제어 기능 매체 식별 기능 제공한다. RTP RTCP 하위의 전송 네트웍 계층에 무관하게 설계되었다.

 

   RTP 별개의 독립 계층으로 구현되기 보다는 특정 응용에서 요구되는 정보를 제공하여 프로토콜의 처리가 응용의 처리 과정으로 통합될 있도록 설계되었다. 따라서 기존의 프로토콜들과는 달리 RTP 응용의 필요에 따라 헤더를 변경하거나 추가하여 응용에 맞는 프로토콜이 있도록 하는 일종의 맞춤형 프로토콜이다. 문서에서는 RTP 이용할 있을 것으로 추정되는 모든 응용들이 공통적으로 필요로 기능들 만을 명시하고 있다. 따라서, 특정 응용 서비스에 필요한 RTP 구현하기 위해서는 문서 이외에 RTP 페이로드의 종류와 형식을 정의하는 프로파일 문서(Profile Specification) 페이로드의 전송 방법을 정의한 페이로드 형식 문서(Payload Format Specification) 필요하다.

 

 

2. RTP 데이터 전송 프로토콜

 

    0                      1                      2                   3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |V=2|P|X|  CC  |M|       PT      |           sequence number         |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                                    timestamp                          |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |               synchronization source (SSRC) identifier            |

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                 contributing source (CSRC) identifiers             |

   |                                      ....                              |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

   모든 RTP 패킷의 상위 12바이트는 다음과 같이 고정되어 있으며, 이후의 CSRC 필드는 혼합기(Mixer) 삽입했을 경우에만 존재한다. 고정 헤더 필드의 부분은 다음의 의미를 가진다.

 

n  Version(V) : 2비트 필드로 현재는 항상 2 값을 가진다.

n  Padding(P) : 1비트 필드로 1 값을 가지면 패킷에 하나 이상의 채워넣기 바이트가 포함되어 있음을 나타낸다. 마지막 채워넣기 바이트는 패킷에서 무시되어야 하는 채워넣기 바이트의 수를 나타낸다.

n  Extension(X) : 1비트 필드로 1 값을 가지면 고정 헤더 이후에 정확히 하나의 확장 헤더가 등장함을 의미한다.

n  CSRC Count(CC) : 4비트 필드로 고정 헤더 이후에 나열되는 CSRC 식별자의 수를 나타낸다.

n  Marker(M) : 1비트 필드로 필드의 해석은 프로파일에 의해서 결정된다. 필드는 패킷 스트림 내에서 프레임 경계와 같은 중요한 이벤트들을 표시하는데 이용된다. 프로파일은 추가 표시 비트들을 정의하거나 PT 필드를 확장하여 표시 비트를 없앨 수도 있다.

n  Payload Type(PT) : 7비트 필드로 RTP 페이로드의 타입을 나타낸다. 프로파일에서 페이로드 타입의 값과 실제 페이로드 형식을 연결한다.

n  Sequence Number(SN) : 16비트 필드로 송신되는 RTP 패킷에 대해 1 증가하는 값을 가진다. 수신 측에서는 패킷 분실을 검출하거나 패킷의 순서를 맞추는데 이용된다. 초기값은 보안을 위해서 무작위로 설정된다.

n  Timestamp : 32비트 필드로 RTP 데이터 패킷의 첫번째 바이트의 샘플링 순간을 나타낸다. 시계의 주파수는 페이로드의 데이터 형식에 종속되고 형식을 정의하는 프로파일이나 페이로드 형식 문서에 정적으로 명시된다. 초기값은 순번과 마찬가지로 무작위 수로 설정된다.

n  Synchronization Source(SSRC) Identifier : 32비트 필드로 동기화 소스를 나타낸다. 값은 같은 RTP 세션 내에서 같은 SSRC 가진 동기화 소스가 두개 이상 나타나지 않도록 무작위로 선택된다.

n  Contributing Source(CSRC) Identifiers : 필드에는 0에서 15목록까지 포함될 있으며 목록은 32비트를 차지한다. CSRC 패킷에 포함된 페이로드에 기여한 제공 소스들을 나타낸다. 제공 소스가 15 이상일 경우에도 15개의 제공 소스만 기록된다. 필드는 혼합기에 의해 삽입되고 목록은 혼합되는 모든 소스들의 SSRC 식별자이다.

 

3. RTP 세션 다중화

 

   효과적인 프로토콜 처리를 위해서 다중화 점의 수는 최소화되어져야 한다. RTP 경우에 다중화는 목적지 전송 주소에 의해 수행된다.. 예를 들어 오디오와 비디오로 이루어지는 회의에서 매체는 자신만의 목적지 전송 주소를 가지는 독자의 RTP 세션으로 전송되어야 한다.

 

4. 프로파일에 따른 RTP 헤더 변경

 

   Marker 비트와 PT 필드는 프로파일에 종속된 정보를 수송하지만 고정 헤더에 할당되어 있다. 이유는 많은 응용들이 그것들을 필요로 것이고 이렇게 제공하지 않으면 값들을 위해 다른 32비트를 확장해야 하기 때문이다. PT 필드는 프로파일에서 재정의할 있으며 Marker 비트가 있는 경우는 필드의 최상위 비트에 기록되어야 한다. 외의 특정 페이로드 형식을 위해 필요한 정보들은 패킷의 페이로드 섹션에 기록된다.

 

   특정 응용에서 페이로드 형식과 무관한 부가적인 기능이 필요하면 응용이 따르는 프로파일에서 고정 헤더의 SSRC필드 이후에 바로 추가 헤더 필드를 정의한다. 그러한 응용들은 부가 필드를 바로 참조할 있으며, 프로파일과 무관한 감시기나 기록기들은 아직도 RTP 패킷의 12바이트 만을 해석하여 처리할 있다. 이후 부가 필드가 모든 응용들에서 필요할 것으로 판단되면 새로운 RTP 버전에서 필드를 고정 헤더 필드에 포함시켜 영구적으로 이용될 있도록 한다.

 

5. RTP 헤더 확장

 

   확장 기법은 개개의 구현에서 페이로드 형식에 무관하게 부가적인 정보를 필요로 하는 새로운 기능들을 실험할 있도록 하는데 이용된다. 기법은 확장과 무관한 다른 응용들에서는 확장부가 무시될 있도록 설계되었다. 특정 페이로드 형식에 필요한 부가 정보는 헤더 확장을 이용해서는 안되고, 패킷의 페이로드 섹션으로 전송되어야 한다.

 

    0                      1                      2                      3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |       defined by profile        |             length                |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                             header extension                          |

   |                                    ....                                 |

 

   고정 헤더 필드의 X 비트가 1이면 가변 길이 확장 헤더가 RTP 고정 헤더에 추가된다. 추가되는 위치는 SSRC 이후, 또는 CSRC 존재하면 CSRC 이후 이다. 확장 헤더는 16비트의 길이 필드를 가지고 있는데, 필드는 4바이트의 확장 헤더를 제외하고 확장에 포함된 32비트 워드의 수를 나타낸다. 하나의 확장 헤더만이 RTP 고정 헤더에 추가될 있다. 그러나 확장 헤더의 상위 16비트가 프로파일에서 정의할 있도록 남겨져 있기 때문에 필드를 이용해서 여러 가지의 확장 헤더들을 실험할 수도 있고 다수의 연동 응용들이 각자의 확장 헤더를 실험해 수도 있다.

 

6. RTP 제어 프로토콜 - RTCP

 

   RTCP 기본적으로 세션의 모든 참가자들에게 주기적인 제어 패킷을 보내어 다음의 기능들을 수행한다.

 

n  데이터 분배의 품질에 대한 피드백을 제공한다. 기능은 RTCP Sender Report(SR) Receiver Report(RR) 의해 수행된다.

n  RTP 소스의 식별을 위해 지속적인 식별자를 수송한다. 식별자는 CNAME(Canonical Name)이라 부른다. SSRC 충돌이 발생하거나 프로그램이 다시 시작될 경우에 변경될 있기 때문에 수신자는 참가자들을 일관성 있게 유지하기 위해서 CNAME 필요로 한다.

 

   기능은 모든 참가자들이 RTCP 패킷을 송신할 것을 요구한다. 따라서 RTP 많은 수의 참가자를 수용할 있기 위해서는 송신 비율이 제어되어야 한다. 참가자가 모든 다른 참가자들에게 제어 패킷을 전송하기 때문에 참가자는 전체 참가자의 수를 파악할 있게 된다. 참가자 수를 이용해서 제어 패킷 전송 간격을 조정한다.

 

n  최소한의 세션 제어 정보를 수송한다. 기능은 참가자가 마음대로 가입, 탈퇴할 있는 허술하게 제어되는 세션에서 유용하게 이용될 있는 기능으로 예를 들어 새로운 참가자의 신분 또는 이름을 제공함으로써 다른 모든 참가자의 사용자 인터페이스에 새로운 참가자의 이름을 알릴 있게 된다.

 

6.1 RTCP 패킷 형식

 

if encrypted: random 32-bit integer

    |

    |[------- packet -------][----------- packet -----------][-packet-]

    |

    |             receiver reports               chunk               chunk

    V                                            item  item        item  item

  --------------------------------------------------------------------

   |R[SR|# sender #site#site][SDES|# CNAME PHONE |#CNAME LOC][BYE##why]

   |R[  |# report #  1 #  2 ][     |#               |#          ][  ##    ]

   |R[  |#        #      #    ][    |#               |#          ][  ##    ]

   |R[  |#        #      #    ][    |#               |#          ][  ##    ]

  --------------------------------------------------------------------

   |<------------------  UDP packet (compound packet) --------------->|

 

   #: SSRC/CSRC

 

   RTCP 패킷은 RTP 데이터 패킷과 비슷하게 고정부로 시작을 해서 패킷의 종류에 따라 달라지는 가변 길이의 구조화된 요소들이 뒤따르고 항상 32비트 경계로 끝난다. 이러한 정렬 방식과 고정부의 길이 필드는 RTCP 패킷을 “Stackable”하게 만든다. , 다수의 RTCP 패킷들로 복합 RTCP 패킷을 만들어 하나의 하위 프로토콜 패킷으로 전송할 있게 한다. 복합 패킷을 이루는 RTCP 패킷의 수는 하위 프로토콜의 최대 길이에 의해 한정된다. 복합 패킷의 개별 RTCP 패킷들은 독립적으로 처리되지만 프로토콜의 기능을 수행하기 위해서 다음의 제약이 주어진다.

 

n  송수신 보고(SR, RR) 대역폭 제약이 허용하는 자주 보내서 송수신 통계 치의 정밀도를 최대화시켜야 한다. 따라서 주기적으로 전송되는 복합 RTCP 패킷에는 반드시 보고가 포함되어 있어야 한다.

n  새로운 수신자는 가능한 빨리 소스의 CNAME 수신해서 소스를 식별하고 Lip-Sync 같은 목적을 위해 그것을 매체와 연결해야 한다. 따라서 복합 RTCP 패킷은 SDES CNAME 포함해야만 한다.

n  모든 RTCP 패킷들은 적어도 두개의 개별 패킷으로 구성된 복합 패킷의 형태로 전송되어야 한다. 이때 다음의 형식이 권고된다.

 

1)      Encryption Prefix : 복합 패킷이 암화화 경우에만 무작위 32비트의 값이 전치된다. 값은 매번 전송되는 복합 패킷에 대해 새로운 값으로 선택된다.

2)      SR 또는 RR : 복합 패킷의 RTCP 패킷은 항상 보고 패킷이어야 한다. 이것은 수신되거나 송신된 데이터가 없을 경우에도 마찬가지로, 경우에는 RR 전송된다. 복합 패킷의 나머지 하나가 BYE 경우에도 마찬가지로 보고 패킷이 패킷으로 등장해야 한다.

3)      추가 RR : 수신 보고 소스의 수가 31 넘어서면 보고 패킷에 이어 첨가 RR 패킷이 나와야 한다.

4)      SDES : CNAME 포함하는 하나의 SDES 패킷은 복합 패킷에 반드시 포함되어야 한다. 다른 소스 설명 항목들도 응용에서 필요로 하면 대역폭 제한에 따라 포함될 수도 있다.

5)      BYE 또는 APP : 주어진 SSRC/CSRC 대해 마지막 패킷으로 전송되어야 하는BYE 제외한 나머지 패킷들은 어떤 순서로도 이어질 있다.

 

   변환기와 혼합기들은 다수의 소스로부터 전달되는 개별 RTCP 패킷들을 가능하면 항상 복합 패킷으로 합쳐서 보내는 것이 바람직하다. 그렇게 함으로써 패킷 오버헤드를 줄일 있기 때문이다. 복합 패킷의 전체 길이가 하위 프로토콜의 MTU 초과할 경우는 복합 패킷을 잘라서 여러 개의 하위 프로토콜 패킷에 포함시켜 보낼 있다.

 

6.2 RTCP 전송 간격

 

   RTP 수명에서 천명의 참가자를 하나의 세션에 참가시킬 있도록 설계되었다. 오디오 회의의 경우에 데이터 패킷은 참가자의 수에 상관없이 비교적 일정한 비트율을 가지지만(언제나 발언하는 사람은 사람 이므로) 제어 패킷의 경우에는 참가자의 수에 비례하여 비트율이 증가( 참가자가 나머지 모두에게 일정한 간격으로 제어 패킷을 전송하기 때문에)하게 된다. 따라서 제어 패킷의 전송 간격은 제어되어야 한다. RTCP 할당되는 대역폭은 세션 대역폭의 5% 고정되는 것이 바람직 하다.

 

6.2.1 세션 멤버 관리

 

   새로운 참가자에 대한 새로운 SSRC 가지는 다수의 패킷을 수신할 까지 참가자는 유효하지 않은 것으로 간주한다. 참가자는 어떤 사이트가 5개의 RTCP 보고 간격 동안 RTP 혹은 RTCP 패킷을 보내지 않으면 사이트를 비활성화 상태로 표시하거나 삭제한다. 일단 사이트가 유효화되면 나중에 비활성 상태로 표시되어도 30 까지는 사이트의 상태는 유지되고 RTCP 대역폭을 공유하는 전체 사이트의 수에 계속 계산된다.

 

6.2.2 SDES 대역폭 할당

 

   SDES 필수 항목인 CNAME 이외에 NAME, EMAIL 등과 같은 부가적인 정보에 제어 대역폭을 할당하는 것을 신중하게 고려해야 한다. 이유는 부가 정보들이 수신 보고와 CNAME 전송 간격을 늘려서 전체 프로토콜의 성능 저하를 유발할 있기 때문이다. 일반적으로 참가자에 할당된 RTCP 대역폭의 20% 미만이 이러한 정보들을 수송하는데 이용되도록 권유 된다. 더욱이 모든 SDES 항목이 모든 응용에 포함될 필요는 없다. 포함된 것들은 사용 정도에 따라 적정 비율의 대역폭을 할당받으면 된다.

 

6.3 송신자/수신자 보고

 

   RTP 수신자는 자신이 동시에 송신자일 경우와 아닌 경우에 각각 SR RR 보내서 수신 품질에 대한 피드백을 제공한다. 패킷 종류 코드 이외에 패킷의 다른점은 SR 활성화된 송신자들에 의해서 이용되는 20바이트의 송신자 정보 섹션을 가지고 있다는 것이다. 사이트가 최종 보고 이후 RTCP 전송 간격 동안 아무 데이터 패킷이라도 보냈으면 SR 보내고 그렇지 않았을 경우에는 RR 보낸다.

 

   SR이나 RR 모두 0 이상의 수신 보고 블럭을 가진다. 보고 블럭은 수신자의 최종 보고 이후에 수신자에게 RTP 데이터 패킷을 보낸 SSRC 각각에 대해 하나씩 존재한다. CSRC 리스트에 나열된 CSRC들에 대해서는 보고를 보내지 않는다. SR 이나 RR 패킷에는 최대 31개의 수신 보고 블럭이 포함될 있기 때문에 그것을 초과하는 경우에는 추가적인 RR 패킷들이 초기 SR 또는 RR 패킷에 연이어 쌓일 있다.

 

6.3.1 SR : 송신자 보고 RTCP 패킷

 

    0                      1                      2                      3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |V=2|P|    RC  |    PT=SR=200    |               length              |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                             SSRC of sender                            |

   +=+=+=+=+=+=+=+=+=+=+=+=end of header=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                  NTP timestamp, most significant word              |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                NTP timestamp, least significant word               |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                             RTP timestamp                              |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                        sender's packet count                          |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                          sender's octet count                         |

   +=+=+=+=+=+=+=+=end of sender information=+=+=+=+=+=+=+=+=+=+=+=+

   |                    SSRC_1 (SSRC of first source)                     |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   | fraction lost |         cumulative number of packets lost         | 

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |             extended highest sequence number received              |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                          interarrival jitter                          |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                             last SR (LSR)                              |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                      delay since last SR (DLSR)                      |

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                    SSRC_2 (SSRC of second source)                   |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   :                               ...                                       :  

   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                     profile-specific extensions                      |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

   송신자 보고 패킷은 개의 섹션으로 구성되며, 프로파일에 따른 번째 확장 섹션이 추가될 수도 있다.

  

   헤더

n  Report Count(RC) : 5비트 필드로 패킷에 포함된 수신 보고 블럭의 수를 나타낸다.

n  Packet Type(PT) : 8비트 필드로 RTCP SR 패킷의 경우에는 200 값을 가진다.

n  Length : 16비트 필드로 32비트 워드로 계산된 전체 패킷의 길이에서 1 값으로 헤더와 채워넣기를 모두 포함한다. 0 값도 유효하다.

 

   송신자 정보

n  NTP Timestamp : 64비트 필드로 보고가 보내진 시간을 나타내고 Round-Trip 지연 계산에 이용된다.

n  RTP Timestamp : 32비트 필드로 NTP Timestamp 시간에 상응한다. 그러나 단위와 무작위 오프셋은 데이터 패킷의 RTP Timestamp들과 같다. 이것은 RTP Timestamp 계수와 실제 시간 간의 관계를 이용해서 해당 NTP Timestamp로부터 계산된다.

n  송신자 패킷 계수 : 32비트 필드로 전송 시작에서부터 SR 패킷이 생성될 까지 송신자에 의해 전송된 RTP 데이터 패킷의 수를 나타낸다. 값은 송신자가 SSRC 식별자를 변경하면 초기화 된다.

n  송신자 바이트 계수 : 32비트 필드로 전송 시작에서부터 SR 패킷이 생성될 까지 송신자에 의해 전송된 RTP 데이터 패킷에서 헤더와 채워넣기를 제외한 페이로드 바이트의 수를 나타낸다. 송신자가 SSRC 값을 변경하면 필드는 초기화 된다.

 

   수신 보고 블럭

n  SSRC_n : 32 비트 필드로, 수신 보고 블럭의 정보가 해당되는 소스의 SSRC 식별자를 나타낸다.

n  Fraction lost : 8비트 필드로, 이전 SR 또는 RR 패킷이 송신된 이후 분실된 RTP 데이터 패킷의 비율이 고정 소수로 표현된다. 이것은 분실 비율을 256으로 곱한 정수부를 취하는 것과 같다. 값은 분실된 패킷의 수를 기대된 패킷의 수로 나누어 얻어진다. 중복 수신에 의해 분실이 음의 값을 가질 경우 분실 비율은 0 된다.

n  Cumulative number of packets lost : 24비트 필드로, 소스 SSRC_n으로부터 수신 시작 이래로 분실한 RTP 데이터 패킷 수로 예측된 패킷 수에서 실제 수신된 패킷 수를 값으로 정의된다. 실제 수신된 패킷 수에는 지연 도착된 것과 중복 도착된 것도 포함된다.

n  Extended highest sequence number received : 32비트 필드로, 하위 16비트는 소스 SSRC_n으로부터 수신된 RTP 데이터 패킷의 최고 순번을 포함하고 상위 16비트는 순번을 부록A.1 알고리즘에 따라 관리되는 순번 사이클의 해당 계수로 확장한다.

n  Interarrival jitter : 32비트 필드로, Timestamp 단위로 측정되고 비부호 정수로 표현되는 RTP 데이터 패킷 도착 시간 간의 통계적 가변성의 측정값을 나타낸다. Si 패킷 i로부터의 RTP Timestamp이고 Ri 패킷 i RTP Timestamp 단위로 측정한 도착 시간일 , 패킷 i j 지터 D D(i,j) = (Rj-Ri) - (Sj-Si) = (Rj-Sj) - (Ri - Si) 같이 표현되고, 도착간 지터 J J = J + (|D(i-1, i)| - J)/16 정의된다. 여기서 1/16 Gain Parameter 적당한 수렴 속도를 보장하면서 좋은 소음 감쇄 율을 보장한다.

n  Last SR Timestamp(LSR) : 32비트 필드로, 소스 SSRC_n으로부터 최근에 받은 RTCP SR 일부인 64비트 NTP 타임스탬프의 중간 32비트를 나타낸다. SR 아직 받지 않았으면 필드는 0 값을 가진다.

n  Delay since last SR(DLSR) : 32비트 필드로, 소스 SSRC_n으로부터의 최종 SR 패킷 수신과 수신 보고 블럭 송신간의 지연을 나타내며, 1/65536 단위로 표시된다. SSRC_n으로부터 아직 SR 패킷을 받지 못했을 경우 필드의 값은 0으로 설정된다.

 

[10 Nov 1995 11:33:25.125]           [10 Nov 1995 11:33:36.5]

   n                 SR(n)              A=b710:8000 (46864.500 s)

  ---------------------------------------------------------------->

                         v                 ^

   ntp_sec =0xb44db705 v               ^ dlsr=0x0005.4000 (    5.250s)

   ntp_frac=0x20000000  v            ^  lsr =0xb705:2000 (46853.125s)

      (3024992016.125 s)  v          ^

   r                          v         ^ RR(n)

  ---------------------------------------------------------------->

                              |<-DLSR->|

                              (5.250 s)

 

   A     0xb710:8000 (46864.500 s)

   DLSR -0x0005:4000 (    5.250 s)

   LSR  -0xb705:2000 (46853.125 s)

  -------------------------------

   delay 0x   6:2000 (    6.125 s)

 

   SSRC_r 현재의 RR 보내는 수신자를 의미할 , 소스 SSRC_n SSRC_r 대한 왕복 전송 지연을 다음과 같이 구할 있다. RR 수신된 시간(A) 기록하고, 왕복 전송 시간 A-LSR 최종 SR Timestamp 필드를 이용해서 계산한다. 그리고 여기서 DLSR 빼서 왕복 전송 지연을 구한다.(A - LSR - DLSR)

 

6.3.2 RR : 수신자 보고 RTCP 패킷

 

    0                      1                      2                      3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |V=2|P|    RC  |    PT=RR=201    |               length               |

   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                     SSRC of packet sender                             |

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                 SSRC_1 (SSRC of first source)                        |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   | fraction lost |       cumulative number of packets lost           | 

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |           extended highest sequence number received                |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                      interarrival jitter                              |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                         last SR (LSR)                                  |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                   delay since last SR (DLSR)                         |

   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                 SSRC_2 (SSRC of second source)                       |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   :                               ...                                        :  

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                      profile-specific extensions                     |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

   RR 패킷의 형식은 패킷 종류 필드가 201 값을 가지고 송신자 정보 5워드(NTP/RTP Timestamp, Sender Packet/Byte Counter) 생략되어 있다는 것을 제외하고는 SR 같다. 보고할 데이터의 전송이나 수신이 없을 경우에는 복합 RTCP 패킷의 전단에 RC 값이 0 RR 패킷을 삽입한다.

 

6.3.3 송신자 수신자 보고 확장

 

   송신자 또는 수신자에 대해서 주기적으로 보고되어야 추가 정보가 있을 경우 프로파일은 프로파일에 따른(profile-specific) 또는 응용에 따른(application-specific) 송신자와 수신자 보고에 대한 확장을 정의해야 한다. 추가적인 송신자 정보가 필요할 경우, 우선은 송신자 보고의 확장부에 포함되어야 한다. 수신자에 관계되는 정보가 포함될 경우 데이터는 기존의 수신 보고 블럭의 배열에 병치하는 블럭의 배열로 구조화될 것이다. , 블럭의 수가 RC 필드에 반영될 것이다.

 

6.3.4 송신자 수신자 보고 분석

 

   수신 품질 피드백은 송신자 뿐만 아니라 다른 수신자들이나 제삼자 모니터들에도 유용할 것이다. 송신자는 피드백에 따라서 전송 율을 변경할 있고, 수신자는 문제가 지역적인 것인지, 전역적인 것인지를 결정할 있으며, 관리자는 멀티캐스트 분배를 위한 그들의 망의 성능을 평가하기 위해서 RTCP 패킷 만을 수신하는 프로파일에 무관한 모니터를 이용할 있다.

 

   송신자 정보와 수신자 보고 블럭에 모두 누적 계수가 이용되기 때문에 보고 사이의 차이점을 계산하여 단기 그리고 장기에 걸친 측정을 하고 보고의 분실에 대한 회복력을 제공한다. 수신된 최근 보고의 차이가 분배의 최근 품질을 측정하는데 이용될 있다. 보고 사이의 기간 동안 이러한 차이점으로부터 여러 가지 Rate들이 계산될 있도록 NTP 타임스탬프가 포함되었다. 타임스탬프는 데이터 인코딩을 위한 클럭 속도와 무관하기 때문에 인코딩과 프로파일에 무관한 품질 감시기를 구현할 있다.

 

6.4 SDES : 소스 설명 RTCP 패킷

 

    0                      1                      2                      3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |V=2|P|    SC  |    PT=SDES=202  |                 length            |

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                          SSRC/CSRC_1                                  |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |                           SDES items                                  |

   |                              ...                                       |

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

   |                          SSRC/CSRC_2                                  |

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+  

   |                           SDES items                                  |

   |                              ...                                       |

  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

 

   SDES 헤더와 0 이상의 Chunk 구성된 3-계층 구조로 Chunk Chunk 식별된 소스를 설명하는 Item들로 구성된다.

 

n  Packet Type(PT) : 8비트 필드로, RTCP SDES 패킷의 경우에는 202 값을 갖는다.

n  Source Count(SC) : 5비트 필드로, SDES 패킷에 포함된 SSRC/CSRC Chunk 수를 나타낸다. 0 값도 유효하지만 아무런 의미도 없다.

 

   Chunk SSRC/CSRC 식별자와 SSRC/CSRC 관계되는 정보를 수송하는 0 이상의 Item들의 리스트로 구성된다. Chunk 32비트 경계에서 시작한다. Item 8비트의 종류 필드와 텍스트의 길이를 나타내는 8비트의 바이트 계수, 그리고 텍스트 자체로 구성된다. 텍스트는 255 바이트까지 이용할 있지만 RTCP 대역폭 소비에 관계된다는 것을 인지해야 한다.

 

   텍스트는 ISO 10646 Annex F 명시된 UTF-2(UTF-8 또는 UTF-FSS로도 알려짐) 인코딩 방법에 따라 인코딩 된다. US-ASCII 인코딩 기법의 부분집합이기 때문에 부가적인 인코딩을 필요로 하지 않고, 캐릭터의 최상위 비트를 1 설정함으로써 멀티-바이트 인코딩 기법으로 인코딩 사실을 알린다.

 

   종단 시스템은 고정 RTP 헤더의 SSRC 같은 값의 소스 식별자를 포함하는 하나의 SDES 패킷을 보낸다. Item 가운데 CNAME 만이 필수이다.

 

      CNAME(Canonical End-point Identifier, 1) : CNAME 무작위로 할당된 SSRC 식별자에 충돌이 발생할 있고, 프로그램이 재시작 되었을 SSRC 식별자가 변경될 있기 때문에 새로운 SSRC 식별자와 상시적으로 남아있는 소스를 위한 식별자와의 바인딩을 제공한다. SSRC 식별자처럼 CNAME RTP 세션 내에서 유일해야 한다.

 

    0                      1                      2                      3

    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   |    CNAME=1    |      length    |  user and domain name       ...|

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

   CNAME 가능하면 자동적으로 만들어져야 한다. 이러한 요구 사항을 만족시키기 위해서 프로파일이 별도로 명시하지 않은 “user@host” 또는 “host” 형식이 이용되어야 한다. 이러한 형식의 장점은 NAME 아이템을 별도로 전달할 필요가 없다는 점이고, 단점은 사용자가 호스트에서 다수의 소스를 생성할 경우 소스에 대해 유일한 식별자가 제공되지 못한다는 점이다.

 

   NAME(User name, 2) : 소스를 설명하는데 이용되는 실명으로, 사용자가 원하는 어떤 형태로도 기록될 있다. NAME 적어도 세션 기간 동안에는 변함이 없어야 하며, 세션 내에서 유일할 필요는 없다. 프로파일에서 Item들의 우선순위를 정하여 전송되는 횟수를 결정하는데 도움을 있다.

 

      EMAIL(Electronic mail address, 3) : RFC822 명시된 형식의 전자 메일 주소로, 세션 기간 동안에는 변함이 없어야 한다.

 

      PHONE(Phone number, 4) : 국제 접속 코드를 + 기호로 변경한 형태의 전화번호이다. , +1 908 555 1212

 

      LOC(Geographic user location, 5) : Item 응용에 따라서 정밀도가 달라진다. “Murray Hill, New Jersey”, “Room 2A244, AT&T BL MH” 같이 표현될 있다. 정밀도의 결정은 구현자나 사용자에 달려있지만 각각의 형식과 내용은 프로파일에 서술되어 있을 있다. 이동 호스트를 제외하고는 세션 기간 동안에는 변함이 없어야 한다.

 

      TOOL(Application or tool name, 6) : 실시간 스트림을 생성하는 응용의 이름과 버전 등을 나타낸다. 정보는 디버깅 목적에 유용하다. 정보 또한 세션 기간 동안에는 변함이 없어야 한다.

 

      NOTE(Notice/status, 7) : 소스의 상태를 설명하는 임시 메시지(, “on the phone, can’t talk”) 전달하거나 세미나 중일 경우 발표의 제목을 전달하는 등의 목적으로 이용되는 부분 이지만 프로파일에 의해 다른 용도로 정의될 수도 있다. NOTE 예외적인 정보를 수송하는 데만 이용되어야 하고 모든 참가자들이 일상적인 용도로 이용해서는 안된다. 그렇게 되면 제어 패킷 대역폭이 낭비되어 CNAME 같은 중요한 정보가 전달되는 간격이 늘어나기 때문이다. NOTE Item 활성화된 동안에는 디스플레이하는 것이 중요하기 때문에 NOTE Item RTCP 대역폭을 차지할 있도록 CNAME 아닌 다른 Item들의 전송을 자제하는 것이 좋다. 수신 측에서 NOTE Item 반복 기간의 기간 동안 또는 20~30 RTCP 기간 동안 NOTE 받지 못하면 NOTE Item 비활성화 것으로 간주해야 한다.

 

      PRIV(Private extensions, 8) : Item 실험적인 또는 응용에 따른 SDES 확장을 정의하는데 이용된다. Item 길이-스트링 쌍으로 구성된 전치부(prefix) Item 나머지를 채우고 원하는 정보를 수송하는 스트링(value string) 포함한다. 전치부 길이 필드는 8비트 필드이고 전치 스트링은 PRIV Item 응용이 수신할 다른 PRIV Item들에 대해 유일하게끔 정의하는 이름으로 사람에 의해서 선택된다. SDES PRIV 전치부는 IANA 등록되지 않는다. 대신 어떤 종류의 PRIV Item 일반적인 유용성을 가지고 있는 것으로 판명되면 IANA 등록된 정규 SDES Item 타입을 할당되어 전치부를 필요하지 않게 해야 한다. 이렇게 하는 것이 사용을 단순화 시키고 전송 효율을 증가시킨다.

 

6.5 BYE : Goodbye RTCP 패킷

 

   BYE 패킷은 소스가 이상 활성화 상태가 아님을 알린다.

 

n  Packet Type(PT) : 8비트 필드로, RTCP BYE 패킷은 203 값을 갖는다.

n  Source Count(SC) : 5비트 필드로 BYE 패킷에 포함된 SSRC/CSRC 식별자의 수를 나타낸다. 0 값도 유효하지만 의미는 없다.

 

   BYE 패킷이 혼합기에 도착하면 혼합기는 SSRC/CSRC 식별자 변경 없이 그대로 전달한다. 혼합기가 수행이 중지될 자신의 SSRC 식별자와 자신이 처리하는 모든 제공 소스들을 나열한 BYE 패킷을 보내야 한다. 부가적으로 BYE 패킷은 8비트 바이트 계수와 계수 바이트 크기의 탈퇴 이유(, “camera malfunction”, “RTP loop detected”) 나타내는 텍스트를 포함할 있다.

 

6.6 APP : Application-defined RTCP 패킷

 

   새로운 응용이나 새로운 기능이 개발되었을 패킷 종류 값을 등록할 필요 없이 실험을 목적으로 사용되기 위한 패킷이다. 인식할 없는 이름을 가진 APP 패킷은 무시되어야 한다. 실험 후에 광범위한 이용성이 판명되면 APP 패킷은 타입과 이름 필드 없이 재정의되어 RTCP 패킷 타입을 이용해서 IANA(Internet Assigned Number Authority) 등록하도록 권고된다.

 

n  Subtype : 5비트 필드로 일련의 APP 패킷들이 하나의 유일한 이름으로 정의될 있도록 하는 타입으로 사용되거나 응용에 종속되는 데이터를 위해 사용될 있다.

n  Packet Type(PT) : 8비트 필드로 RTCP APP 패킷은 204 값을 가진다.

n  Name : 4바이트 필드로 응용이 수신할 다른 APP 패킷들에 대해 유일하도록 APP 패킷을 정의하는 이름으로 사용자에 의해 선택된다.

n  Application-specific Data : 가변 길이의 필드로 APP 패킷에 나타날 수도, 나타나지 않을 수도 있다. 필드는 RTP 의해 해석되지 않고 응용에 의해 해석된다. 필드의 길이는 32비트의 배수여야 한다.

 

7. RTP 변환기와 혼합기

 

   RTP 변환기와 혼합기는 두개 이상의 전송 계층 “cloud” 연결한다. 일반적으로, cloud 공통 네트웍 전송 프로토콜(, IP/UDP) 멀티캐스트 주소 또는 쌍의 유니캐스트 주소, 그리고 전송 계층 목적지 포트로 정의된다.

 

   변환기 : RTP 패킷들을 SSRC 식별자 변경 없이 전달한다. 이렇게 하여 같은 변환기를 통과하여 변환기의 네트웍 소스 주소를 부여 받는 다양한 소스로부터의 스트림들이 수신기에서 구분될 있게 된다.

 

   혼합기 : 하나 이상의 소스로부터 RTP 데이터 패킷 스트림을 수신해서 데이터 형식을 변경하여 스트림들을 같은 방법으로 결합하고 결합된 스트림을 전달한다. 일반적으로 다수의 입력 소스들 간에 타이밍 동기가 맞지 않기 때문에 혼합기는 이들간에 타이밍 정렬을 수행하여 결합된 스트림을 위한 타이밍을 만들어낸다. 경우 혼합기가 결합된 스트림의 타이밍 소스가 되고, 혼합기에 의해 전달되는 모든 데이터 패킷들은 혼합기의 SSRC 식별자를 부여 받게 된다. 혼합된 패킷에 스트림을 제공한 원래 소스들의 식별자를 유지하기 위해서 혼합기는 그들의 SSRC 혼합된 패킷의 RTP 고정 헤더 바로 다음에 CSRC 식별자 리스트 필드에 기록한다. 혼합기 자신이 제공 소스인 경우에도 자신의 SSRC 식별자를 CSRC 리스트에 포함시킨다.

 

        [E1]                                          [E6]

          |                                             |

   E1:17 |                                      E6:15 |

          |                                            |   E6:15

          V  M1:48 (1,17)           M1:48 (1,17)    V   M1:48 (1,17)

        (M1)-------------><T1>-----------------><T2>-------------->[E7]

          ^                    ^     E4:47               ^   E4:47

    E2:1 |             E4:47 |                          |   M3:89 (64,45)

          |                    |                          |

         [E2]                [E4]        M3:89 (64,45) |

                                                           |         legend:

   [E3] --------->(M2)----------->(M3)------------|         [End system]

          E3:64           M2:12 (64)  ^                           (Mixer)

                                         | E5:45                   <Translator>

                                         |

                                      [E5]               source: SSRC (CSRCs)

                                                        ------------------->

 

7.1 변환기에서의 RTCP 처리

 

   변경된 또는 원래의 데이터 패킷을 전달하는 이외에 변환기와 혼합기는 RTCP 처리도 담당해야 한다. 많은 경우 이것들은 종단 시스템으로부터 전송된 복합 RTCP 패킷들을 분리하여 SDES 정보를 합치고 SR 또는 RR 패킷을 변경한다. 정보의 재전송은 패킷의 도착이나 변환기나 혼합기의 RTCP 간격 타이머에 의해 활성화된다.

 

7.2 혼합기에서의 RTCP 처리

 

   혼합기는 새로운 데이터 스트림을 생성하기 때문에 SR 또는 RR 패킷들을 전혀 통과시키지 않고 대신 혼합기 양단의 종단 시스템을 위한 새로운 정보를 생성한다.

 

7.3 직렬 혼합기(Cascaded Mixers)

 

   직렬로 연결된 혼합기들 가운데 번째 혼합기는 혼합기에서 보내오는 결합된 패킷의 CSRC 리스트와 새로운 입력 패킷들의 SSRC 식별자들을 이용해서 CSRC 리스트를 새롭게 구성한다.

 

8. SSRC 식별자 할당과 이용

 

   RTP 헤더와 RTCP 패킷의 다양한 필드들에 기록되는 SSRC 식별자는 RTP 세션 내에서 유일해야 하는 32비트 무작위 수이다. 특히 같은 네트웍의 참가자들이나 같은 시간에 시작하는 참가자들이 같은 수를 선택하지 않도록 하는 것은 아주 중요하다.

 

8.1 충돌 확률

 

   식별자들이 무작위로 선택되기 때문에, 두개 이상의 소스들이 같은 번호를 선택할 가능성이 있다. 충돌은 모든 소스들이 동시에 시작할 경우 가능성이 가장 높다. 소수의 수가 N이고 식별자의 길이가 L(여기서는 32비트) , 소스가 독립적으로 같은 값을 선택할 확률은 대략 1 - exp(-N**2 / 2**(L+1)) large N 정도 이다. 예를 들어, N 1000이면 확률은 10**-4 이다.

 

   일반적인 충돌 확률은 위의 최악의 경우에 비해 훨씬 낮다. 새로운 소스가 모든 소스들이 유일한 식별자를 가지고 있는 RTP 세션에 참가할 , 충돌 확률은 공간 밖에서 이용되는 숫자들의 일부(the fraction of numbers used out of the space) N/2**L 이다. 다시 N 1000 확률은 2*10**-7 이다.

 

   충돌 확률은 소스가 자신의 패킷을 송신하기 이전에 다른 참가자들로부터 패킷을 수신할 기회가 높다는 점에서 더욱 줄어들게 된다. , 소스가 다른 참가자들을 미리 추적해 있다면 자신의 식별자가 다른 소스들의 식별자와 충돌하는지를 살펴볼 있고, 충돌의 경우 다른 식별자를 선택할 있게 되는 것이다.

 

8.2 충돌 해소와 루프 검출

 

   SSRC 식별자 충돌 확률이 낮기는 하지만 모든 RTP 시스템들은 충돌 검출 기능과 충돌을 해결할 적절한 수단이 준비되어 있어야 한다. SSRC 식별자 충돌을 발견한 소스는 RTCP BYE 패킷을 보내고 다른 식별자를 선택해야 한다. 다른 소스의 식별자가 충돌하는 것을 발견(SSRC 같고 소스 전송 주소나 CNAME 다른) 소스는 하나로부터의 패킷은 포기하고 나머지는 받아들일 있다. 경우 이러한 상황이 오래 지속되지 않도록 충돌하는 소스가 충돌을 해결해야 한다.

 

9. 보안

 

   RTP 응용들에서 요구하는 인증(Authentication), 무결성(Integrity), 그리고 신뢰성(Confidentiality) 등과 같은 보안 서비스는 궁극적으로 하위 계층 프로토콜들이 모두 제공할 것이다. 이러한 서비스들이 최근에 IP 명시되었다. RTP 이용할 것으로 보이는 초기의 오디오/비디오 응용들이 신뢰성 서비스의 필요성을 확립 시켜 놓았기 때문에 다음 섹션에 하위 계층 서비스가 유용할 까지 RTP, RTCP 함께 이용될 신뢰성 서비스를 정의한다. 서비스를 위한 프로토콜상의 부담(Overhead) 그리 많지 않기 때문에 가까운 장래에 하위 계층 서비스에 의해 쓸모 없어진다 하더라도 피해는 크지 않을 것이다.

 

9.1 신뢰성

 

   신뢰성은 의도된 수신자만이 수신된 패킷들을 디코딩할 있어야 함을 의미한다. 대부분의 경우 내용의 신뢰성은 암호화(Encryption) 의해 성취된다. 기본 암호화 알고리즘은 RFC1423 설명된 cipher block chaining(CBC) 모드의 DES(Data Encryption Standard) 알고리즘을 이용한다.

 

9.2 인증과 메시지 무결성

 

   서비스들은 관리 하부구조 없이는 구현할 없기 때문에 RTP 버전에서는 명시하지 않는다. 장래에는 하위 계층 프로토콜에 의해서 서비스들이 지원될 것이다.

 

10. 네트웍 전송 프로토콜상의 RTP

 

   RTP RTP 데이터와 RTCP 제어 스트림의 분리를 하위 프로토콜에 의존한다. UDP 비슷한 프로토콜들의 경우, RTP 짝수 포트 번호를 이용하고 RTP 해당하는 RTCP 스트림은 바로 상위의 홀수 포트 번호를 이용한다. RTP 데이터 패킷들은 길이 필드나 경계를 포함하지 않기 때문에 길이 확인은 하위 프로토콜에 의존한다. RTP 패킷의 최대 길이는 오직 하위 프로토콜에 의해서만 제한된다.

 

   RTP 패킷들이 메시지 단위가 아닌 연속된 바이트 스트림으로 추상화하는 하위 프로토콜에 수송되어야 경우, RTP 패킷의 캡슐화는 프레이밍 기법을 지원하도록 정의되어야 한다. 프레이밍 기법은 여기서 정의하지 않는다.

 

 

'프로그래밍 > C | C++' 카테고리의 다른 글

윈도우 malloc  (0) 2013.08.14
RTPLIB 1.0b 라이브러리 예제  (0) 2013.08.14
문자열 처리 함수 정리  (0) 2013.08.14
FFMPEG 압축 기본적인 사용법  (0) 2013.08.14
[펌] C 프로그래머가 알아야할 것들 #4  (0) 2013.08.14
가끔 문자열 처리나오면 새로 만드는 귀찮니즘 때문에



"C기초에서 그래픽까지" 김정철님께서 쓰신 책의 내용을 정리해서



올려봅니다..



조금이나마 도움이 됐으면 합니다...



** 문자열 처리 함수 **

#include <string.h>



1) 문자열의 길이 측정(strlen)

   strlen(문자열)



2) 문자열 결합(strcat)

   char *ptr;

   ptr = strcat(문자열, 문자열2)



3) 문자열 비교(strcmp)

   int i;

   i = strcmp(문자열, 문자열2)

   i == 0 <--- 같다.

   i != 1 같지 않다.



4) 문자열 복사(strcpy)

   char *ptr;

   strcpy(문자열, 문자열2)

   문자열2를 문자열로 복사



5) 문자열 변환(atoi, atol, atof)

   atoi -> 정수

   atol -> long

   atof -> 부동 소수점.

   

   long b; float c;

   int a;

   a = atoi("1234");

   b = atol("-544334");

   c = atof("43.5456);



6) 문자열 교환 함수 (str_swap)

   void str_cpy(char *ptr1, char *ptr2)

   {

      while(*ptr2)

       *ptr1++ = *ptr2++;

      *ptr1 = NULL;

   }



   void str_swap(char *a, char *b)

   {

      char temp[255];



      str_cpy(temp, a)

      str_cpy(a, b);

      str_cpy(b, temp);

   }

7) 문자열 내에서 특정 문자의 개수를 카운트 하는 함수(char_cnt)

   int char_cnt(char *ptr, char ch)

   {

       int i = 0;

       wile(*ptr)

       if(*ptr++ == ch)

       i++;

       return(i);

   }  



8) 문자열 내의 특정 문자를 다른 문자로 바꾸는 함수(str_chg)

   int str_chg(char *ptr, char ch1, char ch2)

   {

      while(*ptr)

      {

       if(*ptr == ch1)

          *ptr = ch2;

       ptr++;

      }

   }



9) 문자열 내의 특정 문자를 탐색(str_fine)

   char *str_find(char *ptr, char ch)

   {

      while(*ptr)

      {

       if(*ptr == ch)

          return(ptr);

       ptr++;

      }

      return((char *)(-1));

   }



10) 문자열 내의 소문자를 대문자로 바꾸는 함수(str_upp)

   void str_upp(char *ptr)

   {

      while(*ptr)

      {

       if(islower(*ptr))

          *ptr = toupper(*ptr);

       ptr++;

       }

   }



11) 문자열 내의 대문자를 소문자로 바꾸는 함수(str_low)

   void str_low(char *ptr)

   {

      while(*ptr)

      {

       if(isupper(*ptr))

          *ptr = tolower(*ptr);

       ptr++;

      }

   }



12) 대문자와 소문자 상호 교환 함수(str_case)

   void str_case(char *ptr)

   {

      while(*ptr)

      {

       if(isupper(*ptr))

          *ptr = tolower(*ptr);

       else if(islower(*ptr)

          *ptr = toupper(*ptr);

       ptr++;

      }

   }



13) 각 문장의 첫 문자만 대문자로 교환하는 함수(str_fst)

   void str_fst(char *ptr)

   {

       if(islower(*ptr))

       *ptr = toupper(*ptr);

       while(*ptr)

       {

       if(*ptr == '.')

       {

          ptr++;

          while(*ptr == ' ')

           ptr++;

          if(islower(*ptr))

           *ptr = toupper(*ptr);

       }

       }

       ptr++;

   }



14) 문자열 내의 지정한 위치에 다른 문자열을 삽입하는 함수(str_ins)

   char *str_ins(char ptr1[], char ptr2[],int t)

   {

      char temp[255];

      int i, j, k;

      if(t>=str_len(ptr1) return (-1);

     

      for(k=0; i=0; i<t; i++; k++)

       temp[k] = ptr1[i];

   

      for(j=0; ptr2[j] !=NULL; j++, k++)  

       temp[k] = ptr2[j];



      while(ptrl1[i])

       temp[k++] = ptr1[i++];

      temp[k] = NULL;

   }



15) 문자열 앞에 지정한 개수 만큼의 공백을 추가하는 함수(str_blk)

   void str_blk(char ptr[], int t)

   {

      static char temp[255];

      int i;

      for(i = 0; i<t; i++)

       temp[i] = BLANK;

      temp[i] = NULL;

      str_cat(temp ptr);

      str_cpy(ptr, temp);

   }



16) 문자열 내의 모든 공백을 삭제하는 함수(rmv_blk)

   void rmv_blk(char ptrp[])

   {

      char temp[255];

      int i, k;



      for(i=0, k=0; ptr[i] != NULL; i++)

       if(ptr[i] != BLANK)

          temp[k++] = ptr[i];

      temp[k] = NULL;

      str_cpy(ptr, temp);

   }



17) 문자열 내에서 원하는 개수 만큼 문자를 삭제하는 함수(str_rmv)

   void str_rmv(char *ptr, int loc, int count)

   {

      int len, i, j;

      len = str_len(ptr);

      if(loc >= len)

       return (-1);

      if(loc + count <= len)

      {

       j = loc + count;

       for(i=loc; ptr[j] != 0; i++)

       {

          ptr[i] = ptr[j];

          j++;

       }

           ptr[i] = NULL

      }

      else

       ptr[loc] = NULL;

   }



18) 특정한 문자열이 기억된 위치를 계산하는 함수(str_loc)

   int str_loc(char ptr1[], char ptr2[])

   {

      int i, j, k;

      for(i=0; ptr1[i] != NULL; i++)

       for(j=i, k=0; ptr2[k] == ptr1[j]; k++, j++)

          return (i);

      return (-1);

   }



19) 특정한 문자열이 나온 개수를 카운트 하는 함수(str_cnt)

   int str_cnt(char ptr1[], char ptr2[])

   {

      int i, j, k;

      int count = 0;

      for(i=0; ptr1[i] != NULL; i++)

       for(j=i, k=0; ptr2[k] == ptr1[j]; k++, j++)

          if(ptr2[k+1] == NULL)

          {

           count++;

           break;

          }

       return ((count == 0) ? -1 : count);

   }
제목: ffmpeg의 apiexample을 사용한 mpeg encoding 방법
날짜: 2004년 4월 18일
작성: 정성욱 (director@smu.ac.kr)

-Content
---------

1. ffmpeg library 사용법
2. ffmpeg encoding 방법의 종류
 2.1 YUV
 2.2 RGB->YUV
 2.3 YUV library함수 사용하기
 2.4 RGB->YUV library함수 사용하기
3. V4L에서 영상 받기
 3.1 TV Card
 3.2 USB
4. ffmpeg를 사용하여 encoding하기
 4.1 saveToFile
 4.2 포맷에 따는 각각의 MPEG압축 방법
   4.2.1 YUV420P
   4.2.2 RGB24->YUV420P
   4.2.3 YUV library함수 사용하기
   4.2.4 RGB24->YUV420P library함수 사용하기
5. 요약

-서문
------

본 문서는 ffmpeg를 사용하여 video 신호를 mpeg codec로 encoding하는 방법을 소개한다. 여기서 사용하는 source는 libavcodec의 예제 source인 apiexample.c를 가지고 변형하여 작성한 문서임을 밝힌다. Reference문서가 많지 않아서 정확한 의미를 알지 못하는 부분이 있다. 만약 그러한 부분에 대한 정화한 의미를 아는 사람이 있다면, director@smu.ac.kr로 연락주기 바란다. 그럼 이제부터 WebCAM과 TV Card를 사용한 ffmpeg encoding 방법에 대해서 알아보자

1. ffmpeg library 사용법
-------------------------

우리가 ffmpeg library를 사용하기 위해서는 동적 라이브러리 파일을 컴파일시 연결시켜 주어야 한다. 동적 라이브러리 파일은 "*.a"파일로서, 우리가 ffmpeg를 통해 인코디하기 위해 사용하는 파일은 "/libavcodec/libavcodec.a"파일이다. 이렇게 ffmpeg library를 사용하는 파일을 컴파일하는 방법은 다음과 같다.

--------------------------------------------------------------------------------
$ gcc -g -O3 -Wall -o [filename] [filename.c] libavcodec.a -lm -lz -ldl
--------------------------------------------------------------------------------


2. ffmpeg encoding 방법의 종류
-------------------------------

장치가 가지는 신호의 종류는 여러가지이다. 그러나 여기서는 RGB24와 YUV420P두가지의 방식을 사용함을 먼저 밝힌다. RGB24와 YUV420P 두가지의 방식을 사용하여 기본적으로 apiexample에서 제공하는 방식과 함수를 사용하는 방식 2가지로 구분하면 총 4가지의 방식이 나오게 된다. 이제부터 이 4가지의 방식에 대해 소개 하려 한다.

2.1 encoding_YUV.c
-------------------

apiexample.c에서 사용한 기본적인 방식을 사용한 방식이다. AVFrame이라는 구조체를 선헌하고 구조체의 data배열에 실제 영상을 넣어주어 encoding하는 방식이다.

2.2 encoding_YUV_f.c
---------------------

AVPicture 구조체에 avpicture_fill()함수를 사용하여 영상데이터를 library함수로 넣어주는 방식이다.

2.3 encoding_RGB.c
-------------------

encoding_YUV.c와 방식은 동일하다. context의 세팅값은 PIX_FMT_YUV420P방식으로 세팅되어 있다. 단지 영상을 V4L을 통해서 받아 들일때 우리는 RGB24로 받아오게 되는것이다. RGB24로 받아진 영상은 AVPicture라는 구조체에 저장되고 저장된 AVPicture를 img_convert()를 통해서 YUV420P로 변환한다. 그런후에 AVFrame 구조체의 배열인 data에 넣어주고 인코딩하게 되는것이다.

2.4 encoding_RGB_f.c
---------------------

모든 것은 위의 encoding_RGB.c와 동일하나 단지 apiexample.c에서 영상을 받아오는 방법과 틀릴뿐이다. apiexample.c에서는 영상을 받아와 AVFrmae *picture의 data에 넣어 주었다 하지만 여기서는 avpicture_fill()을 사용하여 캡쳐된 영상을 AVPicture 타입의 구조체에 넣어준다. RGB타입인 이 영상을 YUV방식으로 변환하기 위하여 img_convert()함수를 사용하여 RGB24형태의 영상을 YUV420P포맷으로 변환한 후 avcodec_encode_video()를 사용하여 인코드 시키는 것이다.


3. V4L에서 영상받기
--------------------

자세한 내용은 http://pl.smu.ac.kr/researches/projects/linux-dvr/ 의2004-02-02-ELF-Video4linux-Capture.txt 문서를 참조 하기 바란다. 여기서는 TV Card와 USB Camera를 세팅하기 위한 부분만 보도록 하겠다.

--------------------------------------------------------------------------------
int setChannel(struct video_channel chan, int channel_no, int device_type)
{
 switch(device_type)
 {
   /* Case 1 : TV Card setting */
   case 0 :
     chan.channel = channel_no;
     chan.flags = VIDEO_VC_TUNER;
     chan.type = VIDEO_TYPE_TV;
     chan.norm = VIDEO_MODE_NTSC;
     if((rv = ioctl(video, VIDIOCSCHAN, &chan)) < 0)
     {
return -1;
     }
     break;
   case 1 :
     chan.channel = channel_no;
     chan.type = VIDEO_TYPE_CAMERA;
     chan.norm = VIDEO_MODE_NTSC;
     if((rv = ioctl(video, VIDIOCSCHAN, &chan)) < 0)
     {
return -1;
     }
     break;
   default :
     break;
 }
 return 0;
}
--------------------------------------------------------------------------------

3.1 TV Card
------------

위의 소스 코드를 보면 TV Card의 경우 device_type의 값이 0일경우 대응됨을 알 수 있다. 이경우 우리는 총 4가지를 설정해준다. channel값과 flags, type, norm값등이다. 이런 세팅값들은 다른 TV Card들에서도 동일하다.
여기서 channel값은 일반적으로 가정용 비디오에서의 Input의 종류를 의미한다. flag값은 비디오 장치가 tuner를 가지고 있다는 것을 알려준다. tuner가 없을 경우 설정하지 않아도 된다. type값은 비디오 장치의 종류를 의미하고 여기서는 TV Card이기 때문에 VIDEO_TYPE_TVnorm값은 NTSC와 PAL이라는 TV신호의 종류를 의미한다.

3.2 USB
--------

위의 소스 코드를 보면 USB의 경우 device_type의 값이 1일경우 대응됨을 알 수 있다. 이경우 우리는 3가지를 설정해주는데 channel값과 type, norm값을 설정해 주면된다.


4. ffmpeg를 사용하여 encoding하기
----------------------------------

ffmpeg를 사용하여 encoding을 하기 위해서 우리는 크게 5가지의 단계를 거칠 것이다.
첫번째, avcodec_init();
두번째, avcodec_register_all();
세번째, InitCaptureDevice(DEVICE_NAME, VIDEO_INPUT);
네번째, CaptureImage();
다섯번재, SaveToFile(nMaxCount, map, IMG_W, IMG_H);
첫번째 단계는 avcodec_init()함수를 사용하여 avcodec을 초기화 하는 단계이다.
다음으로 우리는 초기화된 avcodec의 모든 codec을 등록해야 하는데 이일을 avcodec_register_all()함수가 해주게 된다.
그러면 일단 avcodec과 관련된 기본적인 설정은 끝난것이다.
다음으로 우리는 우리가 만든 initCaptureDevice()라는 함수를 부르게 된다. 여기에는 DEVICE_NAME와, VIDEO_INPUT이 인수로 들어가게 되는데 둘다 상수 값으로서 DEVICE_NAME는 TV 또는 USB Camera를 의미하고 VIDEO_INPUT은 말 그대로 input의 종류를 의미한다.
그 다음으로 우리는 설정된 디바이스에서 메모리로 사진을 가져오게 된다. 영상은 여러장의 사진이 모아진 것이므로 우리는 먼저 사진을 찍어야하고 이사진들을 여러장 찍어서 동영상을 만드는 작업을 하게 되는 것이다. 따라서, captureImage()라는 함수를 호출하게 된다. 이 함수는 장치 디바이스로 부터 사진을 받아와 v_map이라는 곳에다 저장하게 된다. 그것을 우리는 in_addr이라는 포인터로 접근 할 수 있다.
이렇게 가져온 사진들을 동영상으로 만들기위해 saveToFile(nMaxCount, map, IMG_W, IMG_H);와 같이 saveToFile()함수를 부르게 된다. 이때 같이 넘겨주는 인수는 총 프레임의 수와 v_map영상이 저장된 곳의 주소(여기서는 in_addr) 포인터, 이미지의 가로와 세로 길이를 넘겨준다. saveToFile()함수는 이러한 인수를 받아서 nMaxCount만큼 돌면서 map주소에서 IMG_W*IMG_H크기 만큼의 사진을 얻어와 우리가 설정해준 값에 따라서 파일로 저장해 준다.
그렇다면 이렇게 저장되기 위해서 우리는 어떠한 설정을 해주어야 하는 것이라는 것을 알수 있다. 우리는 encoding을 하기 전에 AVContext라는 구조체의 값을 설정해주어야 한다. 이것은 우리가 encoding할 파일의 정보를 설정하는 것으로서 bitrate, framerate등 영상파일에 대한 정보를 가지고 있다. 자세한 내용은 별첨 문서에서 알아보기로 한다.

4.1 saveToFile
---------------

-----------------------------------------------------------------------------
 /*
  *
  * Function    : saveToFile
  *
  * Description : After encoding a frame, save to file
  *
  * Global      : n_frame_count
  *
  * Arguments   : int n_max_count, char *int_addr, int n_width, int n_height
  *
  */
 void saveToFile( int n_max_count, char *in_addr, int n_width, int n_height )
 {
   unsigned char *y;
   unsigned char *u;
   unsigned char *v;
   int yy;
   int xx;
   int i = 0;

   AVCodec *codec = NULL;
   AVCodecContext *c = NULL;
   int out_size = 0, size = 0, outbuf_size = 0;
   AVFrame *picture = NULL;
   uint8_t *outbuf = 0, *picture_buf = 0;

   if ( n_frame_count == 0 )
   {
     f = fopen ( "test.mpg", "w" );
     if ( !f )
     {
       fprintf( stderr, "could not open test.mpg\n" );
       exit( 1 );
     }
   }

   if( n_frame_count < n_max_count )
   {
     n_frame_count++;
     printf( "Video encoding\n" );

     /* find the mpeg1 video encoder */
     codec = avcodec_find_encoder( CODEC_ID_MPEG1VIDEO );
     if ( !codec )
     {
       fprintf( stderr, "codec not found\n" );
       exit( 1 );
     }
   
     c = avcodec_alloc_context();
     picture = avcodec_alloc_frame();

     /*format*/
     c->get_format = PIX_FMT_YUV420P;
     /* put sample parameters */
     c->bit_rate = 400000;
     /* resolution must be a multiple of two */
     printf("%d, %d\n", n_width, n_height);
     c->width = n_width;
     c->height = n_height;
     /* frames per second */
     c->frame_rate = 30;  
     c->frame_rate_base= 1;
     c->gop_size = 10; /* emit one intra frame every ten frames */
     
     /*maximum number of b frames between non b frames.
       note: the output will be delayed by max_b_frames+! relative to th input
       - encodeing : set by user.
       - decoding: unused*/
     c->max_b_frames=1;

     /* open it */
     if ( avcodec_open( c, codec ) < 0 )
     {
       fprintf( stderr, "could not open codec\n" );
       exit( 1 );
     }
   
     /* the codec gives us the frame size, in samples */
     /* alloc image and output buffer */
     outbuf_size = 100000;
     outbuf = malloc( outbuf_size );
     size = c->width * c->height;
     
     picture_buf = malloc( ( size * 3 ) / 2 ); /* size for YUV 420 */
     picture->data[0] = picture_buf;
     picture->data[1] = picture->data[0] + size;
     picture->data[2] = picture->data[1] + size / 4;
     picture->linesize[0] = c->width;
     picture->linesize[1] = c->width / 2;
     picture->linesize[2] = c->width / 2;
   
     //dp = (unsigned char *)out_addr;
     y = ( unsigned char * )in_addr;
     u = y + n_width * n_height;
     v = u + n_width * n_height / 4;

     fflush( stdout );
     /* prepare a dummy image */
     /* Y */
     for( yy = 0; yy < c->height; yy++ )
     {
       for( xx = 0; xx < c->width; xx++ )
       {
   picture->data[0][yy * picture->linesize[0] + xx] = *y;
   y++;
       }
     }
     /* Cb and Cr */
     for( yy = 0; yy < c->height / 2; yy++ )
     {
       for( xx = 0; xx < c->width / 2; xx++ )
       {
 picture->data[1][yy * picture->linesize[1] + xx] = *u;
     picture->data[2][yy * picture->linesize[2] + xx] = *v;
     u++; v++;
       }
     }

     /* encode the image */
     for( i = 0; i < 2; i++ )
       out_size = avcodec_encode_video( c, outbuf, outbuf_size, picture );
     
     printf( "encoding frame %3d (size=%5d)\n", i, out_size );
     fwrite(outbuf, 1, out_size, f);
   }
 
   if ( n_frame_count == n_max_count )
   {
     n_frame_count++;
     
     /* get the delayed frames */    
     for( ; out_size; i++ )
     {
       fflush( stdout);        
       out_size = avcodec_encode_video( c, outbuf, outbuf_size, NULL );
       printf( "write frame %3d (size=%5d)\n", i, out_size );
       fwrite( outbuf, 1, out_size, f );
     }

     /* add sequence end code to have a real mpeg file */
     outbuf[0] = 0x00;
     outbuf[1] = 0x00;
     outbuf[2] = 0x01;
     outbuf[3] = 0xb7;
     fwrite( outbuf, 1, 4, f );
     fclose( f );
     free( picture_buf );
     free( outbuf );

     avcodec_close( c );
     free( c );
     free( picture );
     printf( "\n" );
   }
 }
-----------------------------------------------------------------------------
위의 소스를 보고 순서 대로 알아보자
여기서는 MPEG1을 사용한 인코딩의 방법만을 알아본다.
우리는 먼저 avcodec_find_encoder() 함수를 사용하여 우리가 사용할 코덱을 찾는다 이 작업이 끝나면 AVContext 형의 c에 avcodec_alloc_context();를 사용하여 메모리 공간을 할당해 준다. 그 다음에 AVFrame 형의 picture에 avcodec_alloc_frame();함수를 사용하여 메모리 공간을 할당하여 준다. 그런후 구조체 c에 값들을 세팅해준다. 여기서 세팅하는 값은 나중에 encoding을 할경우 인자로 같이 넘겨줄 값이다.
그런 다음 우리는 실제 영상이 들어갈 곳을 실제 영상이 있는 곳과 연결 시켜 준다. 이부분은 RGB이인지 YUV인지에 따라서 틀려지는 부분이 될 것이다. 여기서는 YUV420P포멧이기 때문에 Y, U, V 3개로 나눈다. 이것은 picture의 의 멤버 변수인 data[]배열에 연결 시켜주는데, 우리가 v_map에서 얻어온 in_addr의 주소를 data[0]에 넣어주고 data[1]와 data[2]에는 Y값이 차지하는 공간을 계산하고 in_addr주소에 Y가 차지하는 주소 공간을 더한 값을 넣어주게 된다. 이렇게 세팅을 한후 이미지를 인코딩 하는데 이미지가 처음에는 지연에 의해서 인코딩 되지 않기 때문에 2번의 인코딩을 함으로써 우리는 실제 데이터를 얻을 수 있다. 따라서, for문 안에 avcodec_encode_video( c, outbuf, outbuf_size, picture );를 2번 돌려서 우리가 원하는 압축된 사진을 얻을수 있는 것이다. 이렇게 나온 사진을 우리는 바로 파일에 저장한다. 이렇게 끝난후에 우리는 지연된 이미지가 있는지 알아보기위해 처음부터 out_size만큼 지연된 프레임을 확인하고 다시 파일에 저장 하게 된다. 그런후 파일의 마지막에 mpeg헤더를 추가한후에 모든 자원을 해제하고 끝내게 된다.

4.2 포맷에 따는 각각의 MPEG압축 방법
-------------------------------------

포맷과 함수를 사용하는지 마는지에 따라서 우리는 전부 4가지 경우의 사례를 볼 수 있다. 이러한 부분은 전부가 picture의 data배열에 자료를 넣어주는 방법에서 차이를 보이게 된다. 이러한 차이에 대해서 이제부터 알아볼것이다.

4.2.1 YUV420P
--------------

-----------------------------------------------------------------------------
     picture_buf = malloc( ( size * 3 ) / 2 ); /* size for YUV 420 */
     picture->data[0] = picture_buf;
     picture->data[1] = picture->data[0] + size;
     picture->data[2] = picture->data[1] + size / 4;
     picture->linesize[0] = c->width;
     picture->linesize[1] = c->width / 2;
     picture->linesize[2] = c->width / 2;
   
     //dp = (unsigned char *)out_addr;
     y = ( unsigned char * )in_addr;
     u = y + n_width * n_height;
     v = u + n_width * n_height / 4;

     fflush( stdout );
     /* prepare a dummy image */
     /* Y */
     for( yy = 0; yy < c->height; yy++ )
     {
       for( xx = 0; xx < c->width; xx++ )
       {
   picture->data[0][yy * picture->linesize[0] + xx] = *y;
   y++;
       }
     }
     /* Cb and Cr */
     for( yy = 0; yy < c->height / 2; yy++ )
     {
       for( xx = 0; xx < c->width / 2; xx++ )
       {
 picture->data[1][yy * picture->linesize[1] + xx] = *u;
     picture->data[2][yy * picture->linesize[2] + xx] = *v;
     u++; v++;
       }
     }
-----------------------------------------------------------------------------
AVFrame형의 data[0]에는 우리는 원래 YUV420P포맷의 영상을 가지고 있는 주소를 넣어준다. 그런후에 포인터를 Y의 크기 만큼 증가 시킨후에 U의 주소를 data[1]에 넣어주고 V의 주소를 data[2]에 넣어준다. 그런후에 linesize를 정해주는데, Y는 원래 width이고 U,V는 크기가 압축되어 원래 width크기의 반절밖에 안되기 때문에 라인사이즈는 width/2가 된다. 그리고 for문을 돌리면서 각각의  data의 개개별 값에 y, u, v값을 넣어주게 된다.

4.2.2 RGB24->YUV420P
---------------------

-----------------------------------------------------------------------------
   picture_buf = malloc((size * 3) / 2); /* size for YUV 420 */
   picture->data[0] = picture_buf;
   picture->data[1] = picture->data[0] + size;
   picture->data[2] = picture->data[1] + size / 4;
   picture->linesize[0] = c->width;
   picture->linesize[1] = c->width / 2;
   picture->linesize[2] = c->width / 2;

   newPicture.data[0] = picture->data[0];
   newPicture.data[1] = picture->data[1];
   newPicture.data[2] = picture->data[2];
   newPicture.linesize[0] = picture->linesize[0];
   newPicture.linesize[1] = picture->linesize[1];
   newPicture.linesize[2] = picture->linesize[2];

   avpicture_fill(&myPicture, in_addr, PIX_FMT_BGR24, c->width, c->height);
   img_convert(&newPicture, PIX_FMT_YUV420P, &myPicture, PIX_FMT_BGR24, c->width, c->height);
   
   fflush(stdout);
   /* prepare a dummy image */
   /* Y */
   for(y=0;y<c->height;y++)
   {
     for(x=0;x<c->width;x++)
     {
picture->data[0][y * picture->linesize[0] + x] = newPicture.data[0][y * newPicture.linesize[0] + x];
     }
   }
   
   /* Cb and Cr */
   for(y=0;y<c->height/2;y++)
   {
     for(x=0;x<c->width/2;x++)
     {
picture->data[1][y * picture->linesize[1] + x] = newPicture.data[1][y * newPicture.linesize[1] + x];;
picture->data[2][y * picture->linesize[2] + x] = newPicture.data[2][y * newPicture.linesize[2] + x];;
     }
   }
-----------------------------------------------------------------------------
RGB24->YUV420P와 YUV420P와 다른 점은 encoding은 YUV420P로 하지만 처음에 영상을 받은 후에 이미지를 컨버트 하는 점이다. 우리는 avpicture_fill()이란 함수를 사용해 AVPicture형의 구조체에 영상을 받아오게 된다. 받아온 영상은 RGB24포맷이고 이것을 우리는 img_convert()함수를 사용해 YUV420P포맷으로 변화하게 된다. 변화된 영상은 새로운 AVPicture형의 구조체에 할당 되게 되고 이것을 AVFrame의 구조체의 data[0]부터 data[1], data[2]에 넣어주므로서 그냥 YUV420P포맷을 사용했을때와 별 다를바없이 MPEG encoding을 할수 있게 된다.

4.2.3 YUV library함수 사용하기
-------------------------------

-----------------------------------------------------------------------------
   avpicture_fill(picture, in_addr, PIX_FMT_YUV420P, c->width, c->height);
-----------------------------------------------------------------------------
위에서 보았던 것과 같이 AVFrame의 data배열에 직접 넣어주어야 했던 실제 데이터 영상을 우리는 avpicture_fill()이라는 함수를 통해서 자동으로 할 수 있다. 하지만, 여기서는 약간의 문제가 생기게 된다. 무엇이냐면, avpicture_fill()함수의 첫번째인자는 즉 데이터가 들어가게 되는 곳의 형이 AVPicture타입이라는 점이다. AVPicture와 AVFrame의 차이점은 별첨 문서에서 확인하게 될것이다. 이런 것은 우리가 원하는 영상을 얻는데 영향을 미치지는 않는다. 하지만, 컴파일시 warning메세지를 출력하게 된다.
이렇게 간단하게 해주고 우리는 encoding함수를 호출하면 된다.


4.2.4 RGB24->YUV420P library함수 사용하기
------------------------------------------

-----------------------------------------------------------------------------
   avpicture_fill(&temp_picture, buf1, PIX_FMT_BGR24, c->width, c->height);
   avpicture_fill(&temp_picture, in_addr, PIX_FMT_BGR24, c->width, c->height);
   avpicture_fill(&new_picture, buf, PIX_FMT_YUV420P, c->width, c->height);
   img_convert(&new_picture, PIX_FMT_YUV420P, &temp_picture, PIX_FMT_BGR24, c->width, c->height);
   picture = (AVFrame *)&new_picture;
-----------------------------------------------------------------------------
RGB24에서 YUV420P로 변화시키는 과정은 위에서 보았던 함수를 사용하지 않는 것과 차이가 없다. 단지 data배열에 넣는 과정을 함수화 했다는 것이다. 실제 ffmpeg library에 있는 함수를 사용하게 되는것이다. 그리고 형의 충돌을 막기위해 AVFrame형의 picture에 AVPicture형의 new_picture를 넣을때 (AVFrame *)으로 형변환을 해주게 된다.

5. 요약
--------

ffmpeg library에 대한 문서가 정확히 나와있지 않은 상황에서 작성된 문서라 많은 부분이 부족 할 것으로 생각된다. 이 문서를 작성하는 동안에도 새로운 것을 알게 되었고 그부분에 대해서 추가 하지는 못했다. 새로운 방향으로 소스 코드를 고쳐야 하는 문제가 생겼기 때문이다. 그렇지만 기본적으로 영상을 저장하는데는 문제가 없었고 약 1개월동안 Mailing List를 보면서 알게된 점에대해서 부족하지만 적어 보았다. 많은 도움이 되기를 바라고 문제가 있을시에는 director@smu.ac.kr으로 연락 바란다.


끝.
C 프로그래머가 알아야할 것들 - Chapter 4. 프로그램 언어

김성훈 (sunghun84@nate.com)

Chapter 4. 프로그램 언어

(1) 왜 문법을 배워야하는가?
(2) 내가 이걸 배워서 과연 실전에 사용할 수 있을까?
(3) 무엇을 위한 프로그램인가?

(1) 왜 문법을 배워야 하는가?

사실 이건 당연한 이야기 일 수 있는데요, 한국어를 할줄 모르는 독일인과, 독일어를 할줄 모르는 한국인과 대화가 가능할까요? 바디 랭귀지로 하면 되지 않느냐는 분도 계시겠지만 그것도 어느정도 한계가 있기에, 제대로된 의사소통은 불가능할겁니다.

컴퓨터는 0과 1 (2진수)밖에 인식하지 못한다고 앞에서 배웠습니다.
그렇다고 컴퓨터에게 00001110 01010101 이런식으로 모든 명령을 내려야한다면 프로그래머는 정말 수학에 능통한 사람이 아니면 힘들껍니다. (수학에 능통하더라도, 구현할 수 있는 수준은 한계가 있겠죠)

그래서, 기계어에서는 16진수 (2진수를 4개씩 묶어서) 표현하고 있습니다. 2진수일 때보다 조금 나아졌지만, 과연 이걸로 프로그램을 만들 수 있을까요? 이걸로도 뭔가 많이 부족해보입니다.

좀 더 사용자(프로그래머)가 이해하기 쉬운 언어가 필요했습니다.

그래서 나온 어셈블리어는 좀 더 사용자가 알기 편하게 하기 위해, 오퍼랜드, 오퍼레이터, 명령 니모닉등으로 구성되어 있는데, 그렇기에 프로그래머가 기계어보다는 이해하기 쉬웠습니다만, 규모가 큰 프로그램에는 적합하지 못했죠.

결국 프로그래머가 사용하기도 편하고, 기능도(비교적) 우수한 구조형 언어 (파스칼, 코볼,포트란, C언어)등이 나오게되었습니다.

프로그램 언어를 통해 컴퓨터에게 일을 시키기 위해선 어떻게 해야 할까요?

"야 지금부터 시간재라". "야 지금부터 음악 재생좀 해봐."

이렇게 컴퓨터에게 우리가 사용하는 말로 말하면 알까요? (음성인식 프로그램은 예외입니다) 컴퓨터는 컴퓨터가 알 수 있는 말로 해야하기 때문에, 기계어로 말하는건 사실상 불가능에 가까우니, 프로그램 언어의 힘을 빌려야하는데, 그 프로그램 언어가 가진 규칙이 문법입니다. 프로그래머가 컴퓨터와 의사소통을 하기 위해선, 사용하는 프로그램 언어의 문법을 지켜야만 원하는 일을 시키고, 컴퓨터에게 상황을 보고 받을 수 있기 때문에 문법에 대해 알아야 하는건 당연하겠죠?

(2) 내가 이걸 배워서 과연 실전에 사용할 수 있을까?

많은 분들이 하시는 고민. 지금 이 글을 쓰고 있는 저도 마찬가지였던 고민.
바로 그것이 과연 지금 배운 문법들로 프로그램을 만들 수 있겠는가 하는것입니다.
당연한 얘기지만, 정답은 "그렇다"는 것입니다.

그런데, 왜 문법까진 이해했는데, 왜 난 프로그램을 만들 수 없을까하는 생각을 하시는 분들이 많은데요, 그 이유는 프로그래밍에 대한 막연함과, 프로그램 개발 과정에 대한 부족한 이해도등의 이유도 있고, 또 프로그램을 구성하고 있는 기본 법칙이나 내부 원리에 대한 이해가 부족한 이유도 있습니다.

프로그래밍에 대한 막연함이란, 입문서 혹은 문법서에 나온 에제정도만 작성해보았지, 실제 사용할 만한 프로그램 개발 경험이 전무하기 때문에 가지는 부담감이라 할 수 있을겁니다.

프로그램 개발과정에 대한 부족한 이해도란, 프로그래밍이란 단순히 프로그램 언어만 안다고 되는 그런 간단한 것이 아닙니다. 프로그래머는 속도와 메모리의 최적화를 해야할 사명이 있는 것이고, 그것은 하드웨어의 발달과는 무관하게 당연한 것입니다. 프로그램의 개발 과정에는 분석,설계등의 선행 작업이 있고, 분석 과정은 개발할 프로그램이 무엇을 필요로 하고, 무엇이 필요한지, 설계과정에서는 데이터 구조, 사용할 알고리즘등 정해야하죠. 그리고 그것을 문서화하는데, 이러한 과정들이 없이 막연히 어떠한 프로그램을 만든다는 것은 한계가 있고, 그러한 프로그램들은 유지보수, 또는 재사용은 불가능에 가깝다고 봐야합니다. (이 부분은 XP=eXtream Programming과 의견을 달리하는데, 그 방법을 무시하는 것이 아니라, 저는 이 방식이 옳은 방식이라고 보기 때문입니다) 차근 차근 하나 하나 설계해나가고, 작성해나가면 생각보다 쉽게 풀립니다.

한마디로, 소프트웨어 개발 방법론에 대한 이해가 필요하다는 것이죠.
프로그램은 기본적으로 순차적이고, 그 분기는 순환문 또는 조건문을 통해 이루어지고 있고, 처리 과정이 계산을 통해 이루어져있다는 것만 이해한다면, 그다지 어려운 것이 아닙니다.

컴퓨터의 내부 원리에 대한 이해도 기본적으로 되어있어야 어떻게 프로그램을 구성해야 될지 감이 오게됩니다. (추상화=캡슐화를 통해 이런 부분까지 알지 않아도 가능한 시대가 왔다는 사람들도 있지만, 여전히 이 것에 대한 이해는 중요합니다) 우리는 API를 사용함으로써 키보드 장치에 대한 프로그래밍을 하지 않아도, 키보드 입력에 관한 정보를 얻을 수 있고, 어떤식으로 화면이 점을 찍는지 내부원리를 알지 못해도, SetPixel()함수를 통해서 점을 찍을 수 있습니다. 이 것은 매우 유용하고 저를 비롯하여 많은 프로그래머들이 이런 기능을 사용하고 있습니다. 그렇지만, 점을 어떤식으로 화면상이 표시하는지에 대한 원리, 키보드 입력이 어떻게 하여 프로그램으로 메시지로 전달되는지에 대한 이해가 되어있는 사람과, 그렇지 않은 사람과의 실력차이는 분명합니다.

근본 원리를 알고 있는 사람은 좀 더 멀리 내다볼 수 있고, 신 기술에 대한 적응도 빠릅니다. 음악파일의 근본 원리를 아는 사람은, 새로운 포맷을 만들어 낼 수도 있고, 압축과 신장 방법을 알아낼 수 있는등, 그 것의 효율적인 사용법을 알아 낼 수도 있지만, 원리는 모르고 MFC혹은 각종 라이브러리에서 제공하는 함수의 사용법만으로 프로그램을 개발하는 사람은 그 함수가 지원하는 기능으로 프로그램을 구성하는 그 이상은 불가능합니다.

문법을 배워서 실전(프로그래밍)에 사용할 수 있는 것은 가능한 일이고 당연한 일이지만, 더 좋은 프로그램, 더 나은 프로그래밍을 위해서는, 프로그램 언어 이외에도 배워야할 것들이 많고, 그것들을 놓치면 안되는것입니다.

(3) 무엇을 위한 프로그램인가?

프로그램이란 사용자를 위한 것입니다. 우리나라에서는 각종 시스템이 사용자 혹은 고객을 위한 경우가 매우 드뭅니다. (요새는 많이 나아진 편이지만, 그렇지 않은 곳이 여전히 존재하죠.) 기능의 업그레이드나, 버그 수정은 말할거 없이 '당연한'것이고, 부가적으로 사용자 입장을 고려한 프로그램이 개발 되어야하는데, 이 것도 부족한 경우가 상당히 많습니다.

저같은 경우는 자주 사용하는 기기가, 게임기, 컴퓨터, MP3정도인데, 가장 불만인 것이 이 MP3입니다. 해당 개발업체가 아니면 수정할수 없는 펌웨어의 경우 좀 더 신중한 테스트가 필요하고, 사용과정에서 쉽게 눈에 띄는 버그는 존재해선 안됩니다. 자주 발생하지 않는 특정상황이나, 특정 환경에서만 발생하는 버그가 아니라면 말이죠.

어떠한 프로그램이던지, 버그로부터 자유로울 수는 없습니다. 우리가 입문서등에서 가장 처음 접하는 "Hello World!"라는 문장을 출력하는 간단한 프로그램마저도여타 프로그램과 충돌또는 예기치 못할 문제에 이해 오작동할 가능성도 배재할 수 없습니다. 하지만, 눈에 보이는 버그. 테스트 과정에서 쉽게 발견할 수 있는 버그의 경우는 발견즉시 수정을 위한 노력을 기울여야하고, 기업은 자신들이 발매한 프로그램에 대한 문제수정을 위한 인력(프로그래머든, AS직원이든)을 배치해둘 필요가 있습니다. 이런 기본적인 사용자를 위한 배려도 되지 않는다는 것은 상식적으로 이해할 수 없는 일이지만, 이런 일이 빈번하게 벌어지고 있다는 것이 문제입니다.

프로그램 개발 기간에 대한 이야기도 해보자면, 투자자 혹은 기업주의 압박과 독촉에 의하여, 충분한 테스트 기간과, 완성도를 갖추지 못하고 제품이 출시되는 경우가 종종 있습니다. 이런 사례는 게임업계에서 유독 심한걸로 알려져있는데, 요새야 온라인 게임이 대세이다보니 클로즈베타, 오픈베타과정을 거쳐 정식 서비스를 하기에 이런 문제가 적어지고 있는 추세지만, 이전 PC패키지 게임이 주를 이루던 시기에 버그로 인한 사용자의 피해는 말도 못할정도였습니다. (마그나 카르타와, 포가튼 사가는 두고 두고 화자되고 있지요. 창세기전 시리즈, 어스토니시아 스토리,화이트 데이등 국내에서 손꼽히는 명작들도 버그로 인해 그 작품성에 흠을 내곤했으니 말이죠.) 개발자들의 노고와 힘든 상황을 이해 못하는 것은 아니지만, 그것은 자신들의 상황을 핑계로 사용자에게 피해를 입히는 행위로, 이런일이 다신 벌어지지 않아야할 것이고, 그러기 위해서는 개발자들을 위한 환경이 지금보다 많이 개선되어야하겠고, 개발자들도 사용자를 위해 완성도 높은 프로그램을 목표로 꾸준히 노력하는 자세를 가지도록해야한다고 생각합니다.
C 프로그래머가 알아야할 것들 - Chapter 3. 운영체제와 컴퓨터 원리

김성훈 (sunghun84@nate.com)

Chapter 3. 운영체제와 컴퓨터 원리

(1) 운영체제란?
(2) 이벤트
(3) 프로세스와 쓰레드
(4) 컴퓨터는 계산기다
(5) 디지털과 아날로그
(6) 2D게임이 3D게임보다 빠르다?

(1) 운영체제란?

초기에 컴퓨터는 컴퓨터를 키자마자 애플리케이션 디스크를 삽입해야만 했습니다.
그리고 특별한 경우를 제외하고는 다른 프로그램 사용시에는 재부팅 시켜야만 했습니다.
이 방법은 매우 불편했습니다. (비디오 게임기들은 아직 이 방식을 채용하고 있는 경우도 있습니다.)

그래서 유닉스, MS-DOS등의 운영체제가 나오게 됐습니다. (많은 분들이 오해하시는 것이 있으신데, MDIR은 운영체제가 아닙니다. 인터페이스를 제공해주는 프로그램이죠)
각 운영체제하에 프로그램을 구동시킨후, 프로그램 종료시에는 그 운영체제로 돌아오게끔 하는 방식을 취한것이죠.

또한 프로그래머를 위한 장치 지원 인터페이스를 제공해 주기도 했습니다. (MS-DOS에서는 매우 미약했지만, 윈도우로 넘어와서는 극찬 받는 기능이죠)
이전에는 그래픽 카드나 프린터, 사운드 카드마다 출력을 지원해주는 방식이 달랐습니다. 점 하나 찍거나 소리를 내는방법이 하드웨어에 따라 달랐던것이지요.

그래서 각 하드웨어 장치(여기선 일반적으로 그래픽 카드와 사운드 카드를 의미합니다)를 컨트롤 하기 위한 작업들은 프로그램 개발 업체마다 따로 이루어져야했고, 그렇기에 발매된지 얼마 되지 않은 하드웨어나, 판매량이 적은 비 인기 하드웨어의 경우는 지원되지 않는 경우가 대부분이었습니다.

상황이 이렇다보니 프로그래머들은 프로그래머 나름대로 다수의 하드웨어 장치를 지원하려다 보니 힘들었고, 사용자들은 사용자 나름대로 내 하드웨어가 내가 사려는 소프트웨어의 지원이 되는지 곰곰히 따져봐야하는 불편한 상황이었죠.

물론 DOS시절에도 VESA (Video Electronics Standard Association)등에서 그래픽 카드의 표준화를 시키긴했지만, 호환성 문제는 고질적인 문제였습니다.

그래서 나온 것이 바로 윈도우입니다.

대게 MS-DOS와 비교되는 윈도우의 장점으로 GUI (Graphic User Interface)를 꼽지만, 플러그 앤 플레이나, API (Application Programming Interface)도 빠지면 안될정도로 중요한 요소입니다.

플러그 인 플레이는 자동 하드웨어 장치 인식 기능으로, MS-DOS의 단점을 보완해주기에 충분했습니다.

API는 프로그램 개발용 함수 모음으로, 점찍기, 타이머, 텍스트 출력, 마우스 입력, 키보드 입력 등등 프로그램 개발에 필요한 기본적인 기능을 지원해준 것입니다. 각 프로그램마다 자체적으로 지원하기 위해 엄청난 시간투자를 해왔던 작업들을 운영체제가 지원해줌으로써, 프로그램 개발이 한결 편해진것입니다.

여기서는 주로 윈도우의 예를 들겠지만, 다른 운영체제도 용어만 틀리지 원리는 비슷하니 이해하시는데 무리는 없을겁니다.

(2) 이벤트

MS 윈도우(이하 윈도우)에서 이벤트란 윈도우에서 발생하는 정보들을 말합니다.
즉, 마우스 이동, 마우스왼쪽 버튼 클릭, 마우스오른쪽 버튼 클릭, 키보드 누름, 키보드 뗌, 문자 키 누름,프로그램 시작, 프로그램 종료등 다양한 상황마다 이벤트가 발생하는데, 그렇게 발생되는 이벤트를 메시지로 프로그램에 보내주는 것입니다.

그 메시지를 받아서 필요한 메시지를 이용하여 처리해주는 것이 윈도우 프로그래밍에서의 이벤트 프로그래밍 방식이라고 합니다.

하드웨어 장치를 어느 회사 제품이냐, 어떠한 식으로 제어해야 하는가를 프로그래머가 신경써야한다면, 운영체제가 이벤트를 보낼수 없겠죠? 하드웨어 제어를 프로그래머가 만든 응용 프로그램이 한다면, 운영체제가 그 하드웨어에서 발생한 이벤트를 보낼 수 없을겁니다. (응용 프로그램 내부에서 그 하드웨어를 제어하며 이벤트 발생시 메시지를 보낼 수는 있겠지만요.)

하드웨어의 접근을 운영체제가 직접 관리하기 때문에, 프로그래머들은 그 부분에 신경않아도 되고, 이벤트 발생시 그 이벤트를 메시지로 받아서 처리하기만 하면 되는것이죠

(3) 프로세스와 스레드

간단히 말해서 프로그램이 실행되고 있는 것을 프로세스라 합니다.
이런 프로세스를 (어느정도는 의미가 다르지만) 태스크라 부르기도 하죠.
우리가 흔히 들어온 멀티 태스크란, 다중 프로그램 구동이라고 생각해도 될겁니다.
이 멀티 태스크를 통해 우리는 동시에 프로그램이 실행되고 있구나라고 생각하시는 분도 많을겁니다.
그러나 실상은 눈깜짝할사이에 여러개의 프로그램이 번갈아가면서 실행되고 있기에 우리는 동시에 작동하는걸로 생각되는것이죠.

스레드는 프로세스 내부의 실행 단위를 말합니다. 프로세스내에서 스레드가 여러개 존재하여 처리되는 것을 멀티 스레드라 하는것이죠.
예를 들면 메신져에서 음악 재생하면서 채팅(메시지 입력)을 할 수 있는 것은, 음악 재생과 채팅기능이 스레드 단위로 구동되기 떄문입니다.
만약 이 프로그램이 태스크 단위로 구동되었다면, 그 프로그램을 이용하여 음악을 재생하는중에는 그 프로그램을 이용한 채팅이 불가능하게 됩니다. 음악이 자동으로 멈추거나, 중지 시키지 않는다면 말이죠.

(4) 컴퓨터는 계산기다

컴퓨터는 계산기라고 한다면, 아니? 계산기에서 동영상도 볼 수 있고, 3D게임도 할 수 있고, 그림도 볼 수 있고, 음악도 나온다는 게 말이되냐고 하시는분도있으실겁니다.
하지만 사실입니다. 입력장치(키보드,마우스)나 출력장치(모니터,프린터,스피커)는 주변기기입니다. 컴퓨터는 입력장치로부터 신호를 받아 그 것을 비트로 변환하여 프로그램(혹은 운영체제)에 전달한후 그에 맞는 처리를 하다가, 출력 장치로 출력해줄 필요가 있을 때 출력장치에 신호를 보내서 출력해주게 됩니다. 이 과정들도 비트로 이루어져있습니다. 컴퓨터는 이진수(on, off)밖에 모르기 때문입니다.

초기 컴퓨터(최초의 컴퓨터로는 애니악으로 잘못 알려져있는데 실은 앨런 튜닝이 세계 2차대전에서 독일군 암호 해독을 위해 만들어진 콜로서스입니다)는 연산속도도 느리고, 연산을 위한 저장 장소(덧셈을 해주려면, 최소 두 개의 저장 장소가 필요합니다. 첫 번째값, 그리고 더해줄 두 번째값. 세 번째 값은 결과값이 필요하다고 생각하실지도 모르겠습니다만, 첫 번째 값과 두 번째 값은 연산후에는 필요가 없어지기에 둘중 아무곳에나 결과값을 저장하면 됩니다.)가 작았기 때문에 간단한 처리밖에 못했습니다. 그런 계산들을 하기 위해 컴퓨터가 만들어졌고, 그것이 당연했죠.

그러나, CPU와 RAM의 속도와 용량이 증가함에 따라서, 컴퓨터의 연산속도가 급격히 상승했습니다. 그에 맞춰 소프트웨어와 입출력 장치들도 발전을 거듭하면서, 비트 단위로 이루어진 데이터를 이용하여 동영상을 재생하고, 그림 파일을 보여주고, 3D게임도 가능하게 된 것입니다. 동영상 파일이나, 그림 파일등이 비트를 기반으로 한 정보로 이루어졌었다고 배웠죠? 그런 데이터들을 출력하는 과정도, 출력 장치 혹은 주변 기기(그래픽 카드나 사운드 카드)의 도움을 받기는 하지만, 일반적으로 컴퓨터의 연산기능을 이용합니다.

그렇기에 컴퓨터는 매우 빠른(이런 말로 표현하기엔 터무니없이 부족하지만) 계산기라고도 할 수 있는것입니다.

(5) 디지털과 아날로그

컴퓨터를 이해하기 위해서 우리는 디지털과 아날로그에 대해 이해할 필요가 있습니다. 왜냐하면 컴퓨터는 수치를 기반으로 한 디지털에 기초하고 있기 때문입니다. (그렇기 떄문에 수학과도 큰 연관이 있습니다.)

아날로그란 연속된 정보를 말합니다. 물론 아날로그도 수치로 표현할 수 있지만, 좀 더 세분화된 정보를 다룬다는 것이 차이점입니다.

그에 반해 디지털은 단절된 정보를 다룹니다. 바로 중간값을 취하지 않는다는 얘기입니다. 예를 들면, 컴퓨터에서 작동하는 시계를 직접 만든다고 생각해봅시다. 이 시계는 큰 기능이 필요 없기 때문에 초까지만 다룬고 한다면, 0.1초나, 0.01초등의 정보를 다루지 않게 됩니다. 물론 0.01초까지 다룬기로 했다면, 이야기가 또 틀려지지만요. 결과적으로 중간값을 다루지 않죠.

컴퓨터는 디지털로 이루어져있지만, 아날로그 데이터도 다룰 수 있기 때문에 이 두 방식의 차이점을 기억해두도록 합시다.

(6) 2D게임이 3D게임보다 빠르다?

우리가 흔히 하는 착각은 2D게임이 3D게임보다 빠르다. 혹은 2D게임은 저 사양이다라는 생각입니다.

이것은 지금까지 배운 논리를 적용하면 쉽게 알 수 있는 것인데, 컴퓨터에서 구동되는 모든 과정은 계산에 의한것입니다.

그렇기 때문에, 2D던, 3D던 게임의 속도는 얼마만큼 많은 계산을 필요로 하는지에 달려있는 것이지, 같은 (혹은 비슷한) 기능을 가진 게임이라면 2D와 3D의 기본적인 연산 속도의 차이(3D는 일반적으로 폴리곤=다각형으로 이루어져 있기에 기본적으로 이루어져야할 연산이 많고, 실수 연산이 많이 필요하기 때문에 더욱 2D보다는 확실히 연산할 것이 많습니다.)는 있을 수 있어도, 게임의 규모가 커지다보면 오히려 2D게임이 느려지는 경우가 발생하게 됩니다.

3D게임의 경우는 모션에 따른 변화되는 정보를 가지고 원본 데이터에서 변화시키는 방식인데 반해, 2D의 경우는 캐릭터의 모든 행동 정보를 이미지 파일로 가지고 있어야하기 때문이다.

그리고, 시각적 효과가 거의 없는 게임인 FM시리즈(Football Manager의 약자로, 원래는 Championship Manager 시리즈였던 게임)의 경우 왠만한 3D게임보다도 속도가 느린데, 이 것은 이 게임이 처리해야 될 데이터가 많기 때문입니다. 모든 경기 결과는 랜덤이 아닌, FM시리즈의 규칙(데이터에 기반하되, 그 데이터가 전부가 아닌)에 따른 결과가 나와야 하기 때문에, 모든 경기 결과를 계산해야되는데, 그 계산해야 될 데이터가, 왠만한 3D게임보다 많기 때문에 느린 것입니다.

어때요? 컴퓨터의 속도에 대한 감이 오시나요?
C 프로그래머가 알아야할 것들 - Chapter 2. 비트의 법칙

김성훈 (sunghun84@nate.com)

Chapter 2. 비트의 법칙

(1) 비트가 뭐지?

(2) 프로그램 작성하는데 비트가 왜 필요한걸까?

(3) 데이터형

(4) 바이트로 구성된 파일

(5) 비트 연산자

(6) 간단히 구현해보는 압축


(1) 비트가 뭐지?

비트란 Binary Digit. 즉 이진수의 약자입니다. 한마디로 이진수를 의미한다고 할 수 있죠. 비트는 컴퓨터에서 제어 가능한 데이터의 최소단위입니다. 하지만, 컴퓨터에서 입출력할때 사용하는 최소 단위는 바이트죠. 둘다 최소단위인데...저게 뭔소린가...하실분도 있을겁니다.

간단히 설명하자면, 비트란 저번 강좌에서 배웠던 이진수 10을 2비트(2진수 2자리 수이기에)로 표현 가능하고 제어 가능하단 의미고, 바이트는 비트 8개가 모여서 구성된 것이 1바이트로, 파일이나 데이터형의 최소단위로 쓰입니다.

(2) 프로그램 작성하는데 비트가 왜 필요한걸까?

많은 분들이 프로그램을 만드는데 왜 비트가 필요한지 의문을 가지실거라고 생각합니다.
그런데, 위에서 설명했듯이 데이터의 최소단위는 비트입니다.

최적화 된 프로그램이란 메모리와 속도 모두 만족 시키는 프로그램을 말하는데, 그것을 만족하기 위해선 비트 단위 연산 또는 처리가 필수이기 때문입니다.

하드웨어의 발전에 따라 빠른 속도보다는 쉬운 사용법과, 유지 보수 또는 코드 재사용이 쉬운 프로그램이 인기를 끌고 있는건 사실입니다.

하드웨어가 발전함에 따라 그 하드웨어의 기능을 활용하기 위한 기술이 적용되다보니 여전히 빠른 프로그램을 작성할 필요성은 존재합니다.
메모리와 비트 단위 연산 권한이라는 강력한 기능을 부여받은 이상 비트에 대해 알아두는 것은 당연하다고 할 수 있습니다.

(3) 데이터형

C언어에서는 다양한 데이터형을 제공합니다.
여기서는 주로 사용되는 몇 개의 데이터형만 가지고, 비트와 관련해 알아보도록 하죠.

흔히 문자형으로 알려진 char (캐릭터형)는 1바이트로 이루어져있습니다.
일반적으로 1바이트=8비트이므로, 0000 0000 이렇게 8자리 2진수만큼 사용할 수 있는데, 2의 7승은 256 (첫 자리가 2의 0승이므로, 8비트의 경우 2의 7승만큼 사용 가능합니다.)
이므로, 부호가 없는 경우는 0~255, 부호가 있는 경우는 -128~127(C표준에서는 -127까지 보장해준다)까지 사용 가능하게 되는겁니다. 부호가 있는 경우는 최상위 비트를 부호 비트로 사용하게 되므로 사용 가능한 수의 범위가 반으로 줄 게 됩니다.

2바이트 데이터형인 short int 도, 2의 15승인 65536만큼의 수를 사용할 수 있는데, 부호 없는 경우 0~65535, 부호가 있는 경우 -32768~32767까지의 수를 사용할 수 있는거죠. 32비트 데이터형도 마찬가지로 2의 31승만큼 사용 가능하다고 알고 계시면 됩니다.

데이터형에 따른 사용범위는 곧 대중화될 예정으로 알려져있는 64비트 운영체제에서 주로 사용될 64비트 데이터형도 마찬가지겠구요.

(4) 바이트로 구성된 파일

비트 8개가 모여서 만든 바이트가 모여서 만들어진 것이 파일입니다.
우리가 흔히 사용하는 이미지 파일들도 알고보면 색상 정보를 담고 있는 바이트의 집합입니다. 여기서 조금 부연 설명을 하자면, BMP(비트맵)파일의 경우는 헤더나 컬러 테이블 정보도 담고 있긴하지만, 일반적으로는 RGB색상값만 가지고 있다고 보시면 됩니다. RGB 색상 값은 Red 1바이트, Green 1바이트, Blue 1바이트씩 저장됩니다.

색상값을 가지고 있다가 프로그램에서 BMP파일을 읽어들였을 때, 색상 정보를 읽어서 RGB색상값을 조합한후 점을 찍어서 (BitBlt함수로도 찍을 수 있는데라고 생각하실분도 있으시겠지만, 그 함수도 결국 내부적으론 점을 찍어서 표현해줍니다.) 그 색상을 모니터에 표현하도록 명령을 내려주기 때문에, 우리 눈에는 컬러 이미지를 볼 수 있는겁니다.

저장되어 있는 RGB색상값을 아무런 압축도 하지 않고 모두 가지고 있는 경우가 위에서 설명한 BMP파일 포맷이고, JPG의 경우는 고도의 압축 기법(압축률도 지정가능합니다)을 통해서 용량을 줄였지만 결과적으로는 색상값을 가지고 있다 신장(압축해제)후 화면에 뿌려주는 원리는 비슷합니다.

벡터 그래픽 파일의 경우에는 점,선,곡선등의 정보를 저장하고 있는 포맷이지요. 그래서 화면에 축소나 확대에서도 같은 이미지를 볼 수 있는것이지요.

동영상 파일도 많은 양의 그림 정보를 담고 있다가, 1초에 몇번 이상(1초에 몇 번 화면이 갱신되는지를 프레임이라고 하는데, 초당 24내지 30프레임은 되어야 깜빡임이 사람 눈에 보이지 않는다고 합니다. 일반적으로 모니터의 경우는 60번 이상 갱신되고 있죠)갱신되는지에 따라 그것을 재생시켜줍니다.

음악파일도 마찬가지로, 소리 정보를 디지털값(수치값)으로 가지고 있다가, 그 정보를 재생시간에 맞춰 재생하는 방식을 취하고 있죠.

스타크래프트 리플레이 파일의 경우에도 비슷하게 첫 위치값을 저장한후에 거기서 변화한 값과 내린 명령,시간값들을 저장했다가, 리플레이 메뉴에서 재생시 그 정보를 바탕으로 게임을 재생시키는 것이지요. 그래서 리플레이 파일의 용량이 그다지 크지 않은것입니다.

게임 세이브 파일의 경우에도, 그 캐릭터의 레벨, 무기 일람, 체력, 공격력, 방어력 등의 파라미터와, 그 캐릭터의 현재 위치, 플레이 시간 등의 정보를 저장하고 있습니다. 물론 에디트가 힘들 게 하기 위해 단순한 파일 구조(순차적)으로 구성하지 않는 경우가 대부분이긴하지만요.

모든 데이터는 바이트 단위로 저장된다는 것. 그 바이트를 구성하고 있는 것은 비트라는 것 이것이 핵심입니다.

(5) 비트 연산자

자 비트에 대해 배웠으니 비트 연산을 한번 해봐야겠죠?
비트연산은 부울대수를 배우셨던 분들은 매우 친숙하실겁니다.

비트연산에 사용되는 비트연산자란 논리 곱 (&) , 논리 합 (|), 논리 부정 (~), 베타적 논리합(^)이 있습니다.
여기서 시프트 연산자 좌측 시프트 (<<) 우측 시프트 (>>)도 있죠.

논리곱은 둘다 1일 때만 참(1)이 됩니다.

1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0

논리합은 둘중에 하나라도 1이면 참(1)이 됩니다.

1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0

베타적 논리합은 두수의 값이 달라야만 참(1)이 됩니다.

1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0

논리 부정은 0은 1로, 1은 0으로 만들어줍니다.

~1 = 0
~0 = 1

이 되는 것이죠.


좌측 시프트 연산자는 비트를 왼쪽으로 옮겨주고, 빈자리엔 0을 넣어줍니다.

0011 0010 << 2

이 연산후에 0011 0010 (10진수 50)에서 1100 1000 (10진수 200) 로 바뀝니다.
왼쪽으로 2번 이동했더니 수가 4배가 되었죠?
잘 보시면 시프트 연산자 한번당 수가 2배가 된다는 것을 아실수 있을겁니다.


우측 시프트 연산자는 비트를 오른쪽으로 옮겨주고, 빈자리엔 0을 넣어줍니다.

0011 0011 >> 2

이 연산후에는 0011 0010 (10진수 50)에서 0000 1100 (10진수 12) 가 되었습니다.
좌측 시프트 연산자와 마찬가지로, 우측으로 2번 이동했더니 수가 4분의 1이 되었죠? (12.5여야하지만, 소수점 이하는 버립니다)


(6) 간단히 구현해보는 압축

압축방법에는 런 렝스 코드, 허프만 코드등 다양한 방법이 있는데요, 여기서는 시프트 연산자와 비트 연산자의 조합으로 간단한 압축을 구현해보겠습니다.

아까 데이터를 압축하려면 현재 데이터에서 변화한 정보를 담는 것이 좋다고 했었죠?

그 원리를 이용하는 것입니다.

주로 다른 파일보다는 이미지, 음악 파일, 동영상 파일등에 사용되는 데이터 압축의 원리는 이 바이트를 기본으로 한 데이터들이 비트로 이루어져있다는 것이 핵심입니다.

어떤 데이터든 현재 상태와 다음상태의 값을 모두 가지고 있는 것보다 현재값 (초기값 또는 현재값은 모든 데이터를 기록해둬야 합니다)을 기록해둔후 차후에 변화되는 값을 저장하게 되면 적은 용량으로 데이터를 기록할 수 있을겁니다. 일반적으로 한 픽셀값은 1바이트로 저장된다고 얘기했었죠? 그런데 방금전 픽셀값은 10이고 다음 좌표값은 30이면 20만 저장하는 것입니다. 그렇게 되면 6비트 (2의 5승이 32이니, 0~31까지 사용가능)내에 데이터를 담을 수 있습니다. 그리고 나서 좌측 시프트 시키면 하위 2비트에 0이 들어오겠죠? 그 2비트에 다른 정보를 담게 되면 전체적으로 용량이 줄 게 됩니다. 이것이 바로 데이터 압축의 원리죠.

+ Recent posts