DemuxMPEG

대문 / 방송 / DemuxMPEG

DemuxMPEG

2. 시작하기전에

  • 이 문서에 사용된 용어는 일반적인 용어해석과 다를수 있습니다. 이것은 어디까지나 필자의 개별해석에 의한 불완전한 비표준수준의 문서임을 의미이며 실제 용어의 원뜻은 다른문서를 보시기 바랍니다.
  • MPEG의 이해는 지루한 bit 연산의 반복과 검색엔진의 사용에 가깝다는 생각이 듭니다. 하지만 패킹방식과 압축방법론, 그리고 그래픽처리등에서 많은 힌트를 얻을수 있어 한번쯤 재밌게 시도해볼수 있는 학문인것 같습니다.
  • 수반되어야 하는 기초지식: 연결리스트(버퍼, 스택, 트리), 할당전략, 신호처리, 네트웍(TCP,UDP), 수학기초(삼각함수,미/적분,통계,벡터,논리/산술연산), 어셈블리(CPU특성,역어셈블), 영상처리, ...

3. MPEG 이론의 일반화

3.1. 용어 정의

  • DMB : Digital Multimedia Broadcasting
  • CAT : Conditional access table
  • DTS : Decoding timestamp
  • ECM : Entitlement control message
  • EMM : Entitlement management message
  • ES : Elementary stream
  • NIT : Network information table
  • PAT : Program association table
  • PES : Packerized elementary stream
  • PCR : Program clock reference
  • PID : Packet indentifier
  • PMT : Program map table
  • PS : Program stream
  • PSI : Program specific information
  • PTS : Presentation timestamp
  • TS : Transport stream (ISO/IEC 13818-1)
  • TXT : Teletext
  • ME : Motion Estimation
  • MC : Motion Compensation
  • MV : Motion Vector
  • MB : Macro Block
  • DON : Decoding Order Number
  • FU : Fragmentation Unit
  • GDR : Gradual Decoder Refresh
  • SPS : Sequence Parameter Set
  • PPS : Picture Parameter Set
  • IDR : Instantaneous Decoding Refresh
  • MTAP : Multi-time Aggregation Packet
  • NRI : NAL Reference IDC
  • RBSP : Raw Byte Sequence Payload
  • SEI : Supplemental Enhancement Information
  • SI : Switching I
  • SNP : Single NAL unit Packet
  • SP : Switching P
  • STAP : Single-time Aggregation Packet
  • DPB : Decoded Picture Buffer
  • CAVLC : Context Adaptive Varable Length Coding
  • CABAC : Context Based Arithmetic Coding
  • VCL : Video Coding Layer
  • HRD : Hypothentical Reference Decoder
  • AU : Access Unit Delimiter
  • VOP : Video Object Plane
  • GOP : Group Of Picutres
  • GOV : Group Of Video Object Plane
  • POC : Picture Order Count
  • DHT : Discrete Hadamard Transform
  • FMO : Flexible Macroblock Ordering
  • ASO : Arbitary Slice Order
  • CPB : Coded Picture Buffer
  • VBV : Video Buffer Verifier
  • M1S : MPEG1 system
  • M2P : MPEG2 program
  • M2T : MPEG2 transport

3.2. 색상의 표현

  1. R/G/B : 빨강/초록/파랑 으로 이루어진 빛의 삼원색의 강도조절에 의한 색 표현 방식으로 각 R/G/B 간의 상호 연관이 커서 어느 한개의 색이 누락되면 시각적으로 금방 느껴집니다.
  2. YUV : Y는 밝기신호이고 Cb, Cr은 색차신호라고 하여 이 3가지 성분비율에 따른 표현방식으로 각 Y/Cb/Cr간의 상호 연관이 작아서 어느한개가 누락되더라도 시각적으로 큰 차이를 느끼기 어렵습니다.
    • MPEG에서는 4:4:4, 4:2:2, 4:2:0, 4:0:2 등의 비율을 사용합니다.
  3. YCbCr <-> RGB 근사치 비율공식 (보정하지 않은 공식)
    Y = krR + kgG + kbB
    Cr = R - Y
    Cg = G - Y
    Cb = B - Y
    
  4. YCbCr <-> RGB 비율상수공식
    1 = kb + kr + kg
    kr = 0.299
    kg = 0.587
    kb = 0.114
    
    Y = krR + (1 - kb - kr)G + kbB
    Cb = 0.5(B - Y)/(1 - kb)
    Cr = 0.5(R - Y)/(1 - kr)
    
    R = Y + (1 - kr)Cr/0.5
    G = Y - 2kb(1 - kb)Cb/(1 - kb - kr) - 2kr(1 - kr)Cr/(1 - kb - kr)
    B = Y + (1 - kb)Cb/0.5
    
  5. YCbCr <-> RGB 간의 kb,kr,kg 비율상수 적용후 정리된 공식
    Y = 0.299R + 0.587G + 0.114B
    Cb = 0.564(B - Y)
    Cr = 0.713(R - Y)
    
    R = Y + 1.402Cr
    G = Y - 0.344Cb - 0.714Cr
    B = Y + 1.772Cb
    
  6. YCbCr <-> RGB 변환 공식을 "8:8:8" 포맷에 대하여 최적화한 공식 (정수계산법)
    Y = 0.299R + 0.587G + 0.114B = 77R/256 + 150G/256 + 29B/256
    Cb = -0.16874R - 0.33126Green + 0.5B = -(44R/256) - 87G/256 + 131B/256 +128
    Cr = 0.5R - 0.41869G - 0.08131B = 131R/256 - 110G/256 + 21B/256 +128
    
    R = Y + 1.402Cr = s_Y + (359Cr - 128)/256
    G = Y - 0.34414Cb - 0.71414Cr = Y - (88Cb - 128)/256 - (183Cr - 128)/256
    B = Y + 1.772Cb = s_Y + (454Cb - 128)/256
    
  7. RGB( 8 : 8 : 8 ) to YUV( YCbCr, 8 : 8 : 8 ) 변환 예제소스
    void RGB_YCbCr(int s_Red, int s_Green, int s_Blue, int *s_Y, int *s_Cb, int *s_Cr)
    { /* 변환과정에서 색감 손실이 약 5% 까지 발생 */
     s_Red &= 0xff, s_Green &= 0xff, s_Blue  &= 0xff;
     *s_Y  = (int)(  ((19595 * s_Red) >> 16) + ((38470 * s_Green) >> 16) + ((7471  * s_Blue) >> 16)       );
     *s_Cb = (int)( -((11059 * s_Red) >> 16) - ((21709 * s_Green) >> 16) + ((32768 * s_Blue) >> 16) + 128 );
     *s_Cr = (int)(  ((32768 * s_Red) >> 16) - ((27439 * s_Green) >> 16) - ((5329  * s_Blue) >> 16) + 128 );
     *s_Y  = (int)(  (*s_Y  > 0) ? *s_Y  : 0);  *s_Y  = (int)((*s_Y  < 255) ? *s_Y  : 255        );
     *s_Cb = (int)(  (*s_Cb > 0) ? *s_Cb : 0);  *s_Cb = (int)((*s_Cb < 255) ? *s_Cb : 255        );
     *s_Cr = (int)(  (*s_Cr > 0) ? *s_Cr : 0);  *s_Cr = (int)((*s_Cr < 255) ? *s_Cr : 255        );
    } 
    
  8. YUV( YCbCr, 8 : 8 : 8 ) to RGB( 8 : 8 : 8 ) 변환 예제소스
    void YCbCr_RGB(int s_Y, int s_Cb, int s_Cr, int *s_Red, int *s_Green, int *s_Blue)
    { /* 변환과정에서 색감 손실이 약 5% 까지 발생 */
     s_Y &= 0xff, s_Cb &= 0xff, s_Cr &= 0xff;
     s_Cb -= 128;
     s_Cr -= 128;
     *(s_Red)   = (int)( s_Y + ((91881 *s_Cr)/65536) );
     *(s_Green) = (int)( s_Y - ((22554 *s_Cb)/65536) - ((46802*s_Cr)/65536) );
     *(s_Blue)  = (int)( s_Y + ((116130*s_Cb)/65536) );
    }
    
  9. 위 공식을 적용하여 각 color format간의 자유로운 변환이 가능한 library를 만들어 봤습니다. [http]상호변환library(http://blog.naver.com/minzkn/60020904427)
  10. Linux frame buffer 상에서의 YUV 표현
    • (이것을 예제로 보여줄까? 아니면 Frame map 구조에 대해서만 설명할까?)

3.3. MMX

  1. Intel 계열의 CPU에서 제공되는 MultiMedia eXtention 명령어
    MultiMedia eXtention 명령어
    기능 값의 순환이 고려될 때 부호가 고려될 때 부호가 고려되지 않을 때
    산술덧셈 PADDB, PADDW, PADDD PADDSB, PADDSW PADDUB, PADDUW
    산술뺄셈 PSUBB, PSUBW, PSUBD PSUBSB, PSUBSW PSUBUB, PSUBUW
    산술곱셈 PMULL, PMULH
    산술곱셈자리올림 PMADD
    비교 PCMPEQB, PCMPEQW, PCMPEQD
    PCMPGTPB, PCMPGTPW, PCMPGTPD
    변환 PUNPCKHBW PACKSSWB PACKUSWB
    PUNPCKHWD PACKSSDW
    PUNPCKHDQ
    PUNPCKLBW
    PUNPCKLWD
    PUNPCKLDQ
    논리연산 묶음의 단위 Quad단위
    PAND
    PANDN
    POR
    PXOR
    PSLLQ
    PSRLQ
    전송 Double word 전송 Quad word 전송
    MOVD MOVQ
    MMX 상태를 비움 EMMS
  2. Pack 명령어 : 바이트열에 대응하는 명령어 ('P'로 시작하는 명령어)
  3. Unpack 명령어 : 바이트열의 혼합에 대응하는 명령어
  4. 주의: MMX는 FPU명령과 혼합해서 사용할수 없습니다. 이것은 FPU의 레지스터와 MMX 의 레지스터는 같은 레지스터이기 때문입니다.

3.4. 주사방식

  • 같은 주사선이라도 격행주사로 구현할 경우 프레임수가 높은것같은 착각을 일으켜 보다 자연스러워 보이게 됩니다. 같은 100개의 주사선을 사용하였을때 각 프레임의 갯수가 격행주사의 경우 2배로 보이기 때문입니다.
    1. 순차주사 (Progressive scanning) : 주사선을 왼쪽에서 오른쪽으로 위에서 아래로 순차적으로 발생시키는 방식
    2. 격행주사 (Interlaced scanning) : 왼쪽에서 오른쪽으로 위에서 아래로 순차적으로 발생하지만 위에서 아래로의 주사선은 2배의 간격으로 1/2만큼의 주사선을 격자로 각각 발생시키는 방식

3.5. I/P/B picture

  • 이것은 시간과 공간 모두 관여되는 화면 압축의 범위이며 I의 경우는 공간에만 관여하는 화면이고 P, B의 경우는 공관가 더불어 시간이 관여되어야 하는 화면이며 B의 경우는 구현상 다소 어렵다는 것을 이해할수 있습니다.
    1. I (Intra picture) : 하나의 완전한 화면을 구성할수 있는 화면으로 바로 밑에 오는 P와 B화면을 구성하는데 참조적인 역할을 수행하는 기준입니다.
    2. P (Predictive picture) : 가장 최근의 I 또는 P 화면으로부터 움직임의 차이부분만을 압축한 화면입니다. 당연히 P화면만으로는 전체화면을 갱신할수 없으며 적어도 I 화면이 한개 이상 있어야 한다는 예상을 할수 있으며 움직임이 있는 영역만을 압축하므로 I 화면보다 작다는 것도 예상할수 있습니다. 여기서 중요한 점은 P 화면은 시간선상에서 과거의 화면으로부터 차이점만을 압축하는 순방향 예측만을 적용합니다.
    3. B (Bidirectionally predictive picture) : P와 비슷하게 인접한 I또는 P 또는 B로부터의 차이를 압축한 화면입니다. P와 다른점은 시간선상에서 과거 및 미래 두가지 모두 예측하는 것이 차이점이라 할수 있습니다.

3.6. 허프만부호 (Huffman)

  • 일반 허프만 : 이 압축방식은 비손실성 압축으로 발생확률이 높은 데이터에 대해서 보다 적은 비트를 할당하는 방식입니다. 아지만 일반 허프만 부호방식은 압축할 범위를 미리 검색하여 발생빈도를 축적시켜두어야 하는 단점이 있습니다.
  • 적응화 허프만 : 이 방식은 일반 허프만 부호와 같이 발생확률이 높은 데이터에 적은 비트를 할당하는 것은 같지만 미리 압축할 범위를 검색하지 않고 이진트리의 교환을 사용하여 지속적으로 발생확률에 적응시키는 것이 다릅니다.

3.7. DPCM (Differntial PCM)

  • 이것은 일련의 시간선상에서 변화되는 값을 그대로 시간선상에 배열하기 보다는 압축의 효율적인 방안을 고안하기 위해서 인접한 시간상에 변화된 값들간의 차이를 배열하는 방식입니다. 이것은 압축에 직접적인 영향을 주지는 않지만 값의 범위를 줄이는 효과가 발생하여 압축시에 적은 비트를 다루게 유도합니다. 즉, 다음과 같은 데이터가 주어진다면 이것을 DPCM으로 바꾸면 그 아래의 값처럼 변환되는것이고 보시면 아시겠지만 값의 크기가(양이 아닙니다!) 많이 줄었음을 확인할수 있습니다.
    • 주어진 시간적 데이터 : 값의 범위가 아래의 경우 1 부터 10까지 10단계였다고 하면
      시간의 흐름 ->
      1 2 4 7 7 10 9 7 4 3 1
    • DPCM으로 변환된 데이터 : 이렇게 변환된후에 -3 부터 3까지 7단계로 줄었네요.
      시간의 흐름 ->
      1 1 2 3 0 3 -1 -2 -3 -1 -2
  • 보시고서 데이터의 범위가 줄어든것이 어떤 이득일까 궁굼하신 분들이 계실겁니다. 뒤에 계속되는 글들을 읽으실때 이 궁굼함을 계속 염두하면서 읽어보세요.

3.8. 이산여현변환 (DCT: Discrete Cosine Transform)

  • 압축의 효율을 높이기위한 수단중에 한가지로 연속성을 보다 긴밀하게 유지하도록 하여 압축효율을 증대할수 있습니다. 주파수성분의 특성을 잘 활용하는 수학적인 변환방식의 하나입니다.
  • 일정크기의 블록으로 영역을 쪼개어 주파수 성분으로 분리해내는 방식으로 MPEG에서는 저주파 성분은 큰 차이를 보이지 않으므로 0에 수렴하는 값이 발생하도록 정렬하게 됩니다. 여기서 주파수 성분이라 함은 영상의 경우 인접한 픽셀간 색상값의 차이를 말하며 8x8블록내에서 전부 같은 색을 가졌을때 인접한 색상값의 차이는 0이 될것입니다. 이것을 우리는 저주파수라고 예기하게 됩니다.
  • 2차원에서 1차원 변환 : DCT에 주어지는 것은 영상으로 2차원에 해당합니다. 이것을 뭔가 해보려고 시도한다면 1차원으로 선형화 작성이 필요합니다. 하지만 그냥 왼쪽에서 오른쪽으로 위에서 아래로 값을 훝어 내려간다면 고주파수는 고주파수끼리 인잡하고 저주파수는 저주파수끼리 인접하는데는 수평적으로는 이루어지겠지만 수직적으로는 전혀 아닐겁니다. 때문에 지그재그 스캔방법을 사용하게 됩니다. 즉, 대각선으로 왼쪽 상단에서 오른쪽 하단으로 직선을 그었을때 그에 90도 방향으로 스캔하는 방법을 사용하게 됩니다. 그렇게 되면 2차원의 주파수성분을 보다 잘 밀집시킬수 있게 됩니다. 또한 MPEG-2에서는 좀더 복잡한 지그재그를 사용하게 되는데 이것은 말로 설명하기 어렵군요. 나중에 시간나면 그림그려서 보여드리겠습니다. MPEG-4, H.264 에 들어서면서 이것은 한층 강화되었습니다.

3.9. 양자화 (Quantization)

  • 아날로그 신호를 디지털 신호로 바꾸는데 있어서 어느정도의 레벨범위를 가져야 하는데 이러한 적정한 레벨로 범위를 결정하고 이를 디지털화 하는 작업이라고 설명하면 될까 싶습니다. 이때 설정한 레벨에 따라서 수치를 표현하는 비트수가 결정되는데 비트수가 적을수록 Data가 작아지고 뒤에 후행되는 부호화의 효율을 높일수있습니다. 단, 비트수가 적어질수록 손실률이 존재하게 되므로 적정한 레벨을 사용해야 합니다.
  • 모든 아날로그 전기신호를 적절한 디지털 범위를 갖는 레벨로 변환할수 있습니다.
  • 어떤 아날로그 신호의 대역폭에 대략적으로 2배수한 샘플링 주파수를 가지고 디지털화하게 되면 거의 완전하게 아날로그 신호로 다시 역변환이 가능한것으로 알려져 있습니다. (이것은 Shannon의 샘플링 원리에서 그 근거를 찾을수 있습니다.)

3.10. Hybrid coding

  • (공부중)

3.11. Aspect ratio (화면비율)

  • Resolution으로부터 화면비율을 계산하는 예제 소스
    #include <stdio.h> 
    
    typedef int t_gcm_value; 
    
    static t_gcm_value _gcm_to_abs(t_gcm_value s_value) 
    { 
     static t_gcm_value sg_msb = (((t_gcm_value)1) << ((sizeof(t_gcm_value) << 3) - 1)); 
     t_gcm_value s_temp; 
     s_temp = sg_msb >> ((sizeof(t_gcm_value) << 3) - 1); 
     if(s_temp != ((t_gcm_value)1)) 
     { /* t_gcm_value is signed type */ 
      if((s_value & sg_msb) == sg_msb) 
      { /* s_value < 0 */ 
       s_value = -s_value; 
      } 
     } 
     return(s_value); 
    } 
    
    static t_gcm_value _gcm(t_gcm_value s_value1, t_gcm_value s_value2) 
    { 
     t_gcm_value s_temp; 
     if(s_value1 < s_value2) 
     { /* swap */ 
      s_temp = s_value1; 
      s_value1 = s_value2; 
      s_value2 = s_temp; 
     } 
     do 
     { 
      s_temp = s_value1 % s_value2; 
      if(s_temp == ((t_gcm_value)0))break; 
      s_value1 = s_value2; 
      s_value2 = s_temp; 
     }while(1); 
     return(_gcm_to_abs(s_value2)); 
    } 
    
    void gcm(t_gcm_value s_value1, t_gcm_value s_value2) 
    { 
     t_gcm_value s_value; 
     s_value = _gcm(s_value1, s_value2); 
     (void)fprintf(stdout, "gcm(%4ld, %4ld) = %4ld (ASPECT RATIO=%ld:%ld)\n", 
      (long)s_value1, 
      (long)s_value2, 
      (long)s_value, 
      (long)(s_value1 / s_value), 
      (long)(s_value2 / s_value)); 
    } 
    
    int main(void) 
    { 
     /* test suite */ 
    
     gcm(640, 480); 
     gcm(720, 480); 
     gcm(960, 720); 
     gcm(1280, 720); 
     gcm(1440, 1080); 
     gcm(1920, 1080); 
    
     return(0); 
    } 
    
    /* End of source */ 
    
  • Resolution 과 Display aspect ratio 관계를 상호고려할때 Pixel aspect ratio 를 구하는 예제 (최대공약수 알고리즘)
    #include <stdio.h>
    
    static int _gcm(int s_value1, int s_value2)
    {
     int s_temp;
     if(s_value1 < s_value2)
     { /* swap */
      s_temp = s_value1;
      s_value1 = s_value2;
      s_value2 = s_temp;
     }
     do
     {
      s_temp = s_value1 % s_value2;
      if(s_temp == 0)break;
      s_value1 = s_value2;
      s_value2 = s_temp;
     }while(1);
     return(((s_value2 >> ((sizeof(s_value2) << 3) - 1)) ^ s_value2) - (s_value2 >> ((sizeof(s_value2) << 3) - 1)));
    }
    
    void pixel_aspect_ratio(int s_resx, int s_resy, int s_ax, int s_ay, int *s_sax, int *s_say)
    {
     int s_value, s_x = s_resy * s_ax, s_y = s_resx * s_ay;
     if((s_x != 0) && (s_y != 0))
     {
      s_value = _gcm(s_x, s_y);
      *(s_sax) = s_x / s_value, *(s_say) = s_y / s_value;
     }
     else *(s_sax) = *(s_say) = 1;
    }
    
    void test(int s_resx, int s_resy)
    {
     int s_sax, s_say;
     (void)fprintf(stdout, "%4d x %4d | ", s_resx, s_resy);
     pixel_aspect_ratio(s_resx, s_resy, 4, 3, &s_sax, &s_say);
     (void)fprintf(stdout, "%3d:%3d   ", s_sax, s_say);
     pixel_aspect_ratio(s_resx, s_resy, 16, 9, &s_sax, &s_say);
     (void)fprintf(stdout, "%3d:%3d\n", s_sax, s_say);
     (void)fflush(stdout);
    }
    
    int (main)(void)
    {
     (void)fprintf(stdout,
      "===============================\n"
      "resolution  | 4:3disp  16:9disp\n"
      "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
     test(   0,   0);
     test(-720,-576);
     test( 720, 576);
     test( 768, 576);
     test(1024, 576);
     test( 720, 576);
     test( 768, 576);
     test(1024, 576);
     (void)fprintf(stdout,
      "===============================\n");
     return(0);
    }
    
    /* End of source */
    

3.12. 시간의 표현

  • 시간의 표현범위
    • MPEG계열에서는 기본적으로 초당 90kHz의 샘플링을 갖는 클럭으로 표현하며 총 33비트로 표현합니다. 하지만 90kHz보다 좀더 세밀한 시간기준을 두기 위해서 27MHz의 샘플링을 갖는 클럭을 더불어 사용하기도 합니다.
    • ASF/WMV 계열에서는 1kHz의 샘플링을 갖는 클럭으로 표현하며 decoder의 기준시간을 별도로 제공받지 않고 PTS에서 Preroll time을 뺀 (단, 0이하일때 0으로 초기에 간주하는) 개념을 사용합니다. MS에서 어떤 미래를 위해서 그랬는지는 모르나 어떤곳은 32bit이고 어떤곳은 64bit로 예약을 해둔곳이 있습니다. 참으로 아리송한 부분입니다. 개인적인 여담이지만 preroll 시간만큼을 지연되어야 하는줄로 알고 있었으나 그것을 STC의 조작으로 가능하다는 것을 알게 되었는데 참 오묘합니다. 만들때는 명확한 논리가 머리속에 있었으나 만들어놓고 저도 이해가 안가므로 통과...
  • STC (System Clock Reference)
    • 시간의 절대적 기준시간을 말합니다. 밑에 설명하는 PCR, SCR, PTS 등은 이 STC를 기준으로 발생하고 움직이게 됩니다. Decoder 입장에서는 첫 PCR을 내부 클럭으로 가져오는 시점부터 내부 클럭을 STC로 간주할수 있습니다. Encoder 입장에서는 Encoder clock 자체가 STC가 되겠죠. 이것은 Mux과정에서 PCR로 실려 출력이 되는 것입니다.
  • PCR (Program Clock Reference)
    • 하나의 채널에 대한 기준이 되는 시간을 말합니다. 보통 Transport stream 에서 사용되는 시간기준을 말합니다. Transport에 여러개의 program이 실리는 경우에 별도의 PCR을 갖을수도 있지만 보통은 하나로 유지합니다. (TS에서는 PMT에서 PCR pid를 명시하게 되며 해당 pid로부터 PCR/EPCR을 참조받을수 있습니다.)
  • SCR (System Clock Reference)
    • 시스템의 기준이 되는 시간을 말합니다. 보통 Program stream 에서 사용되는 시간기준을 말합니다. 하지만 결국 decoding할때는 PCR와 SCR은 같은 역할의 기준시간이 된다고 예기할수 있습니다. (PS에서는 Pack header에서 이를 찾을수 있습니다.)
  • DTS (Decoding Time Stamp)
    • Decoder가 Decoding을 수행하는 시점의 시간을 말합니다.
  • PTS (Presentation Time Stamp)
    • 화면에 표출되어야 하는 시간을 말합니다. Decoder에 공급할때 일정량의 버퍼상태에 놓이게 되고 Decoding시에 소모되는 시간도 있으므로 DTS와 PTS는 약간의 시간차를 갖고 있을겁니다.
  • 시간의 유추
    • 때때로 위의 시간정보를 모두 취하는것이 오히려 불필요할때가 있을겁니다. 이때는 PTS만으로 PCR/SCR/STC, DTS 를 모두 적당한 간격으로 계산하여 사용하게 됩니다. 굳이 시간의 정확도보다는 어쨌건 재생되도록 할때 취하는 방법입니다. 하지만 이것은 100% 완벽하지는 않습니다. 특히 Live stream 처럼 일정 속도로 공급되는 경우 요게 또 많이~ 오묘합니다. 음... 오묘한것 뿐이군...

4. MPEG-1 (ISO/IEC 11172-2)

  • MPEG-1 은 1.5Mbps이하의 전송속도를 고려하여 제안된 영상 및 음향 압축에 대한 국제 표준입니다. 이것은 움직임의 상호 보상, DPCM(Differential Pulse Code Modulation: 신호간의 차이를 나타내는 방식), DCT(Discrete Cosine Transform: 주파수별로 분리해내는 기술), Quantization(양자화라고도 하여 비트의 발생량을 제어하는 하나의 방법), RLE(Run length encoding: 반복된 데이터의 줄임 표현 방식), 허프만부호(반복되는 데이터에 적게 비트를 할당하는 가변비트 할당방식)등의 알고리즘이 상호 협력하여 영상과 음향을 손실성 압축을 시도하여 전송량을 크게 줄이는 형식입니다.

5. MPEG-2 (ISO/IEC 13818-2, H.262)

  • MPEG-2 는 MPEG-1 과 호환성을 최대한 유지하면서 보다 제한적인 요소였던 전송속도와 고해상도문제, 순차주사방식 고려등이 연구된 형식입니다. MPEG-2는 복호화 부분만 표준으로 규정되어 있으며 부호기는 이에 동반하여 복호화와 맞도록 구현되도록 유도됩니다. (좀더 유연하다라고 해야 할지?)

6. MPEG-4 (ISO/IEC 14496-2, ASP)

  • Track
  • Hint track
  • Atom header
    Atom header
    moov Meta data 최상위
    mvhd Movie header
    iods Object 기술자
    trak Trak 또는 Stream 관여
  • MPEG4 probe
    1. 우선 "moov", "cmov"가 있는지 판별합니다.
    2. "mdat" 가 있는지 판별합니다.
    3. "moov" 또는 "cmov"중 한개가 먼저 검출되었고 "mdat"이 검출된다면 이것은 MPEG4 file 형식일 확률이 높습니다.
    4. 중요한것은 "mdat"이 검출되지 않는다면 이것은 meta 영역이 깨진것으로 판별할 필요가 있습니다.
  • ISMA
  • 스트리밍시에 Video 와 Audio가 별도의 채널을 사용
    1. TODO: 전반적으로 RTSP/RTP/ISMA에 대한 설명이 먼저 되어야 할것 같으므로 뒤에서 설명하는것을 고려

7. H.264 (ISO/IEC 14496-10, AVC)

  • NAL (Network abstraction layer)
    • 첫 2 byte는 00H로 시작하며 그 다음부터 비슷한 패턴의 일치를 막기 위해서 03H를 삽입할수 있으며 이때 이것은 그냥 무시하도록 처리하고 01H가 마지막으로 오고 다음 byte에 해당 NAL unit의 종류를 판가름할수 있는 값이 오게됨.
    • TODO: NAL unit type의 RFC에 명시된 부분에 대한 추가적인 언급이 필요
  • Over TS
    • TODO: TS 와 I/B/P slice 그리고 이에 따른 indexer 구현으로 trick mode 를 구현하는 기술까지를 공개할까? 말까?

8. WMV

  • ASF (Advanced Systems Format Specification)

8.1. WMV9

  • 구현상 Header 의 비디오 Compression ID 는 "WMV3"로 표현됨.
  • WMV9은 HD급 영상을 지원, MPEG4급의 압축률, 스트리밍의 용이성(단, 관련정보공개가 잘 안되어 있음)
  • Object 헤더 구조
    1. 4-2-2-8 구조 - (즉, GUID 는 16바이트)
    2. 크게 Header/Data/Index로 상위구조를 갖게 됨.
    3. HeaderObject는 필수 Object와 선택적 이용가능한 Object로 구분
    4. Header Object없이는 Data Object를 파악하기 힘듬.
    5. RTSP, ISMA, MMS등에서 Header Object 를 전달하는 방식을 모두 가지고 있음.
    6. ...
  • Header Object
    1. CompressionID, CodecID, ImageWidth/Height를 얻어 형식을 판단할수 있음.
    2. HD급 화질 굉장히 양호 - 디코딩에 비교적 많은 메모리 필요
    3. UNICODE LE16 기준으로 정의됨
    4. NSC에 Format1 에 기술되는것은 HeaderObject 전체와 DataObject header를 포함한 인코딩된 내용입니다.
  • MMST 의 경우 DataPacket에 8바이트 덧붙여서 TimeCode만 참고해서 그냥 쏴주면 되는 편리한 Multicast Streamming 구조
  • NSC 형식 사용 (필자주: base64도 아닌것이 무지 비스므레 해가지고 더 복잡한 Encode방식을 가졌음. 짜증나서 구현하다가 방황을 한적있음. SDP구조가 훨씬 좋은것 같음.
  • 굳이 NSC를 사용할 필요는 없으며 어쨌건 ASF Header 를 전송해줄수 있고 그것을 해석할수만 있으면 MMST 를 구현가능합니다.
  • Streamming(Multicast) 시에 Preroll time 에 의존하여 초기 재생에 필요한 대응시간 필요. 보통 3000~5000ms 소요. 파일 재생시에는 이 시간보다 빨리 스트림을 공급할수 있어서 잘만 구현하면 별 문제가 없음. PreRoll time 의 단위는 ms.
  • Sinle/Multiple payload : TODO
  • Key frame : 완전한 화면을 구성가능한 기점의 payload 이며 중간부터 또는 TrickMode 시에 이것을 고려하여 적절한 Video payload skip 을 구현하면 모자이크 현상을 방지할수 있습니다.
  • Media Object Number : TODO
  • Presentation Time : MPEG1/2 와는 다른 PTS단위를 사용합니다. (Milli second)
  • Compression ID (Video FourCC) - 해당 4바이트 문자열을 단순히 숫자로 보면 됨.
    1. WMV1 : 0x31564d57 (윈도 미디어 비디오 v7)
    2. WMV2 : 0x32564d57 (윈도 미디어 비디오 v8)
    3. WMV3 : 0x33564d57 (윈도 미디어 비디오 v9)
    4. WMV4 : 0x34564d57 (미정의??? 확실치 않음)
    5. MP43 : 0x3334504d (MPEG4 영상 코덱 v3 - 비표준 MPEG4 코덱으로 알고 있음. 이것을 Reverse Engineerin 혹은 Hacking 하여 Divx 3.XX를 만들었음 )
    6. MP4S : 0x5334504d (ISO MPEG-4 영상 코덱 v1)
    7. WMVA : 0x41564D57 (윈도 미디어 비디오 v9 고급 프로파일 - WVC1과 차이가 뭔지 확실치 않음)
    8. WVC1 : 0x31435657 (윈도 미디어 비디오 v9 고급 프로파일)
  • Codec ID (Audio Codec ID)
    1. WMA audio version 1 : 0x0160
    2. WMA audio version 2 : 0x0161 or 0x7a21 or 0x7a22
    3. WMA audio pro or WMALSL : 0x0162 or 0x0163
  • (필자주: ErrorCorrection 에 대해서 공부중 - 공개된 정보가 빈약하여 아직도 파악 못하는 중.)
  • (필자주: 비교적 MPEG4보다는 스트리밍 구조는 좀 쉽게 구현가능 - 다만 정보공개가 거의 없는 상태)
  • (필자주: 일부 MediaServer버젼에 Packet크기 관련 1~2바이트 miss버그가 있는것으로 보임)
  • VC-1 : H.264 에 대응할만한 Microsoft의 차세대 압축방식 (필자는 이것의 정체를 아직 파악하지 못함.)

9. MP3 audio (MPEG 1/2 layer I/II/III)

  • mzmp3
    #define Frame header preview  
      32bit big endian - AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM 
    
    #define Frame header bit index
      A[31..21] : Frame sync
      B[20..19] : MPEG audio version indentification
      C[18..17] : Layer description
      D[16]     : Protection bit
      E[15..12] : Bitrate index
      F[11..10] : Sampling rate frequency index
      G[9]      : Padding bit
      H[8]      : Private bit
      I[7..6]   : Channel mode
      J[5..4]   : Mode extension
      K[3]      : Copyright 
      L[2]      : Original
      M[1..0]   : Emphasis
    
    #define Frame header description
        A: 11bit - Frame sync (All bit '1')
                   '11111111 111'
        B:  2bit - MPEG audio version identification
                   '00': MPEG version 2.5
    	       '01': Reserved
    	       '10': MPEG version 2.0 (ISO/IEC 13818-3)
    	       '11': MPEG version 1.0 (ISO/IEC 11172-3)
        C:  2bit - Layer description
                   '00': Reserved
    	       '01': Layer III
    	       '10': Layer II
    	       '11': Layer I
        D:  1bit - Protection bit
                   '0': Protected by CRC (16bit crc follow header)
    	       '1': Not protected
        E:  4bit - Bitrate index
                   아래참조
        F:  2bit - Sampling rate frequency index
                   MPEG version 1.0
                     '00': 44100
    		 '01': 48000
    		 '10': 32000
    		 '11': Reserved
                   MPEG version 2.0
                     '00': 22050
    		 '01': 24000
    		 '10': 16000
    		 '11': Reserved
                   MPEG version 2.5
                     '00': 11025
    		 '01': 12000
    		 '10': 8000
    		 '11': Reserved
        G:  1bit - Padding bit
                   '0': Frame is not padded
    	       '1': Frame is padded with one extra slot 
    	       Layer I frame size
    	         ((12000 * Bitrate / Samplerate) + Padding) * 4
    	       Layer II/III frame size
    	         (144000 * Bitrate / Samplerate) + Padding
        H:  1bit - Private bit
        I:  2bit - Channel mode
                   '00': Stereo
    	       '01': Joint stereo (Stereo)
    	       '10': Dual channel (Stereo)
    	       '11': Single channel (Mono)
        J:  2bit - Mode extension (Only if joint stereo)
                   Layer I/II
                     '00': Bands 4 to 31
    		 '01': Bands 8 to 31
    		 '10': Bands 12 to 31
    		 '11': Bands 16 to 31
                   Layer III
    	         '00': Intensity stereo off, MS stereo off
    	         '01': Intensity stereo on , MS stereo off
    	         '10': Intensity stereo off, MS stereo on
    	         '11': Intensity stereo on , MS stereo on
        K:  1bit - Copyright
                   '0': Audio is not copyrighted
    	       '1': Audio is copyrighted
        L:  1bit - Original
                   '0': Copy of original media
    	       '1': Original media
        M:  2bit - Emphasis
                   '00': None
    	       '01': 50/15ms
    	       '10': Reserved
    	       '11': CCIT J.17
    
    #define hint
    
    A: MPEG version 1.0, Layer I 
    B: V1,Layer II 
    C: V1,Layer III 
    D: V2,Layer I 
    E: V2, L2 & L3 
    -: Free format
    x: Not use (Invalid bitrate !)
    
    #define table
    
         [A] [B] [C] [D] [E]
    0000   -   -   -   -   - 
    0001  32  32  32  32   8 
    0010  64  48  40  48  16 
    0011  96  56  48  56  24 
    0100 128  64  56  64  32 
    0101 160  80  64  80  40 
    0110 192  96  80  96  48 
    0111 224 112  96 112  56 
    1000 256 128 112 128  64 
    1001 288 160 128 144  80 
    1010 320 192 160 160  96 
    1011 352 224 192 176 112 
    1100 384 256 224 192 128 
    1101 416 320 256 224 144 
    1110 448 384 320 256 160 
    1111   x   x   x   x   x
    

9.1. MP3 streaming

  • MP3의 streaming 을 위한 bitrate 제어에 필요한 요소를 우선 고려해보아야 합니다. 일단 대충 생각나는 것은 다음과 같은것이 있겠군요.
    1. Sampling rate (SR)
    2. Bits per sample (BPS)
    3. Channels (CH)
    4. Compression level (CL)
    5. MP3 header size (HS)
      초당 보내야 하는 bit ((SR * BPS * CH) / CL) + (HS * 8)

10. AAC

  • MPEG2 AAC
  • MPEG4 AAC (HE AAC)
  • TODO: AAC 개선안이 몇가 되는데 다 열거해야 하나?
  • AAC Bitstream을 Containing 하기위해서는 ADTS-AAC, ADIF-AAC등이 있으며 이중 ADTS-AAC 형태가 주로 사용된다. ADTS-AAC의 경우 1st ADTS HEADER + 1st AAC Raw Stream + 1st ADTS HEADER + 2nd AAC Raw Stream +............. + nst ADTS HEADER + nst AAC Raw Stream 의 구조 이며 ADIF-AAC의 경우 ADIF-Header + AAC Raw AAC Stream 이다

11. AC3

  1. AC3 Frame Length 계산 함수
    static const unsigned int _ac3_FrameSizeTbl[38][3] = 
    {
    	{64, 69, 96}, {64, 70, 96}, {80, 87, 120}, {80, 88, 120}, {96, 104, 144},
    	{96, 105, 144}, {112, 121, 168}, {112, 122, 168}, {128, 139, 192}, {128, 140, 192},
    	{160, 174, 240}, {160, 175, 240}, {192, 208, 288}, {192, 209, 288}, {224, 243, 336},
    	{224, 244, 336}, {256, 278, 384}, {256, 279, 384}, {320, 348, 480}, {320, 349, 480},
    	{384, 417, 576}, {384, 418, 576}, {448, 487, 672}, {448, 488, 672}, {512, 557, 768}, 
    	{512, 558, 768}, {640, 696, 960}, {640, 697, 960}, {768, 835, 1152}, {768, 836, 1152},
    	{896, 975, 1344}, {896, 976, 1344}, {1024, 1114, 1536}, {1024, 1115, 1536}, {1152, 1253, 1728},
    	{1152, 1254, 1728}, {1280, 1393, 1920}, {1280, 1394, 1920}
    };
    
    static unsigned int _AC3GetFrameLen(char *pFrameBuf)
    {
    	unsigned int nFsCod, nFrmSizeCod;
    
    	if (pFrameBuf == NULL) {
    		return 0;
    	}
    
    	// Sync word check
    	if (pFrameBuf[0] != 0x0b || pFrameBuf[1] != 0x77) {
    		return 0;
    	}
    	
    	nFsCod = (pFrameBuf[4] & 0xc0) >> 6;
    	if (nFsCod > 3) {
    		// nFsCod 가 4 이면 Reserved....
    		return 0;
    	}
    	nFrmSizeCod = pFrameBuf[4] & 0x3f;
    	if (nFrmSizeCod > 37) {
    		// nFrmSizeCod 가 37 보다 크면 Error
    		return 0;
    	}
    	return _ac3_FrameSizeTbl[nFrmSizeCod][nFsCod] << 1;
    }
    
    

12. DTS

13. Demux

  • Demux 모듈의 제작을 위해서는 프로그램의 구성을 짜임새있게 해두어야 추가되는 형식을 지원하기 쉽다는 것은 당연한 것이 아닐까 생각합니다. 기본적으로 필자는 다음과 같이 모듈화를 주장합니다. 독자분들도 이에 더불어 좀더 체계적인 모듈구성도를 그려보시면 조금이나마 도움이 될것이라 믿습니다. 아래는 큰 개념으로 소개한것이지만 사실은 각 부분마다 좀더 세분화된 개념으로 분리되어야 합니다.
    1. PES (Program element stream) packet parser
    2. PS (Program stream) 로부터 PES 를 분리해내는 parser
    3. TS (Transport stream) parser (PAT, PMT, ... 등의 section 분리 및 관리의 용이성이 충분히 고려되어야 함)
    4. MPEG4를 고려하기 위해서 Track/Hint 에 대한 고찰 및 전체 design 고려 부분
  • [http]Demux 샘플 프로그램 다운로드[](http://minzkn.wowdns.com:2744/phpBB2/viewtopic.php?t=262) : 안타깝게도 소스는 공개하지 못하게 되어 있습니다. 언젠가 공개할수 있도록 노력중입니다.

13.1. Demux 제작을 위한 준비단계

  • 우선 무조건 Bit stream buffer 코드가 필요합니다. 이것은 Demux 자체에 광범위하게 사용되므로 정말로 노력하여 제대로 된 stream buffer 를 만들어 둡시다. (Context 개념이 적용될수 있도록 최대한 모듈화해야 하는 부분입니다.)
  • MPEG의 헤더 모양새는 외우다시피 하거나 일목요연하게 볼수 있도록 정리해서 책상유리 및에 끼워두고 살아야 할지도 모릅니다.
  • Byte order 에 대해서 헷갈리지 않도록 훈련합시다.
  • MMX 와 SSEx 를 알아두시면 좋으며 어셈블리는 그에따른 불가결한 필수조건이겠죠.
  • Streaming 은 일렬로 데이터를 읽어가면 되지만 모든 것이 그렇지 않다는 이유로 충분한 고민을 하셔야 될겁니다.
  • 다른 사람이 재사용 가능하도록 충분히 name space 를 고려하여야 합니다. 다른 사람이 재사용 할수 없다면 만든 자신도 재사용하기 어렵습니다. (예로들면 TRUE, FALSE, ERROR 같은 define 을 피하며 getbyte 같이 함수명을 짖는 일은 피하십시요. demux 는 재사용성이 높아야 가치가 있다고 생각됩니다.)
  • MPEG 의 종류를 검출하는 것은 Demux 의 축약된 집합체로 구성됩니다. 때문에 MPEG의 종류만 검출할수 있는 코드를 만들수 있다면 demux 자체의 이해는 그리 어렵지 않다고 생각됩니다. 하지만 다른방향의 의미로는 MPEG 종류를 검출하는 것 자체가 커다란 학문이 될수 있다는 예기이며 쉽지 않다는 겁니다. 서로 다른 MPEG종류를 놓았을때 서로 비슷한 부분이 있기도 하고 아니기도 하여 Score 라는 개념을 사용하여야 한다는 점만 반드시 알아둘 필요가 있습니다. 즉, 예를 들면 TS의 sync byte인 47H가 188의 배수로 온다면 TS로 인식할수 있지만 이것이 sync byte 몇개가 보이면 결정할지를 score(점수계산법)를 증가해가면서 봐야 하고 이로서 score가 가장 높은 probe 루틴을 기준으로 MPEG의 종류를 판별해야 한다는 예기입니다. 머리속에 항상 이점은 염두하면서 이해합시다.
  • MPEG의 총 재생시간을 알기 위해서는 PTS를 감지하면 쉽게 구할수 있게 됩니다. 나중에 다시 설명하겠지만 MPEG의 첫 PTS가 나타날때 그 값과 맨 끝에 나오는 마지막 PTS값의 차이를 구하고 이것에 90KHz 를 나누면 해당 MPEG의 총 재생시간을 얻을수 있습니다. 그 밖에도 총 재생시간을 검출하는 방법은 많은데 Audio가 MPEG1 Layer 1/2/3 인경우를 예로 든다면 해당 샘플링 주파수에 초를 곱하면 나오기도 합니다. 또한 초기 몇개의 패킷을 샘플로 구하고 총 파일의 크기에서 나누면 마찬가지로 시간을 검출할수 있기도 합니다. 참고로 PTS를 저장할 변수의 크기는 33(!)비트 이상이어야 합니다. 그리고 재밌는 사실은 디코더에 공급되는 클럭이 항상 27MHz라는 점도 관찰해보시면 좋을듯 합니다. 힌트로 27MHz / 90KHz = 300 이 되는군요.

13.1.1. Bit stream 헤더정의 (시작코드 정의)

  • MPEG 1/2 PS 의 경우 4바이트의 구조로 아래와 같은 헤더값에 의해서 순차적인 demux를 수행합니다. 이에 대하여 byte stream buffer 가 제작되어야 한다는 것을 예측할수 있을겁니다. 또한 4바이트 에 순서대로 1바이트씩 byte stream buffer 에서 꺼내와서 쉬프트 시키면서 ((header & ffffff00H) == 100H) 가 만족될때 헤더로 볼수 있습니다.
    • Picture start code : 100H
    • Slice min start : 101H
    • Slice max start : 1afH
    • User start code : 1b2H
    • Sequence start code : 1b3H
    • Extension start code : 1b5H
    • Sequence end code : 1b7H
    • Group of picture start code : 1b8H
    • ISO end code : 1b9H
    • Pack start code : 1baH
    • System start code : 1bbH
    • Program stream map : 1bcH
    • Private stream 1 : 1bdH
    • Padding stream : 1beH
    • Private stream 2 : 1bfH
    • Video code : 1e0H ~ 1efH
    • Audio code: 1c0H ~ 1dhH

13.1.2. Transport(TS) 패킷 헤더

  • TS packet size : 188 or 204(FEC) byte 단위 (Big endian을 기준으로 열거함)
    • 8 bit : Sync byte (47H)
    • 1 bit : Transport error indicator
    • 1 bit : Payload unit start indicator
    • 1 bit : Transport priority
    • 13 bit : PID
    • 2 bit : Transport scrambling control
    • 2 bit : Adaptation field control
    • 4 bit : Continuity counter
    • (184 - x) or (200 - x) byte : Adaptation field

13.2. Demux PS

  • Demux 절차: 특별한 언급이 없다면 Big endian 입니다.
    1. Sequence header를 찾습니다. 즉, Header = (Header << 8) | getbyte(stream buffer) 와 같이 순차적으로 바이트를 4바이트에 쉬프트하면서 넣어 4바이트 헤더로 만들고 이것이 진짜 헤더인지 검출하기 위해서 (Header & ffffff00H) == 100H 인지 다시 검사합니다.
    2. 만약 Padding stream 또는 Private stream 2 인경우는 일단 WORD를 읽어냅니다. 그리고 WORD값만큼 stream을 skip 합니다.
    3. Header값이 Pack start code 일때까지 Sequence header 를 다시 찾습니다. (이것은 화면의 일그러짐현상을 줄이고자 하는 단계입니다.)
    4. 이제 올수 있는 헤더는 System start code, Private stream 1, Video / Audio code만이 올수 있습니다. 나머지의 경우는 다음 Sequence header 를 찾습니다.
    5. 이제 Header는 1bcH ~ 1efH (Stream ID)의 범위값을 가져야 합니다. 만약 범위에 없는 값이라면 다시 다음 Sequence header를 찾아야 합니다.
    6. 이제 이 시점에서 비로소 어떤 패킷인지 몰라도 codec에 밀어넣어줄 데이터입니다. (일부는 아니지만 그래도 대부분은...)
    7. 이제 WORD를 읽어서 어디다가 임의 변수에 저장해둡니다. 이 값은 현재 이 패킷의 길이를 나타냅니다. (여기서는 PacketSize 라는 변수에 저장한다고 합시다.)
    8. 이제 Stuffing byte가 존재하는데 한개의 BYTE씩을 읽어서 그 값이 ffH 인동안 버퍼를 소진시킵니다. (당연히 PacketSize 변수값 이상을 읽어야 되는 상황까지 간다면 그냥 다음 Sequence header를 읽어야 합니다.)
    9. 이제 ffH가 아닌 값이 읽어져 있는 상태일겁니다. 이 값에서 bit 연산으로 몇가지 정보를 추출해야 합니다. 생각보다 많은 처리를 요하는 부분이며 여기서 설명하기에는 분량이 많을것 같아서 미래를 기약하며 생략하겠습니다. (PTS, DTS, STD scale, STD size 등...)
      1. STD scale & STD size : (BYTE & c0H) == 40H 인경우
      2. PTS : (BYTE & f0H) == 20H 인경우
      3. PTS & DTS : (BYTE & f0) == 30H 인경우
      4. PES : (BYTE & c0H) == 80H 인경우
      5. 나머지 즉, BYTE 값이 0x0f 가 아닌 경우는 잘못된 형식으로 처리해야 하며 다음 Sequence header 를 찾습니다.
    10. 아직 PacketSize 가 0이 되면 안되고 또한 (224 * 1024)보다 커서도 안됩니다. 만약 이 조건을 만족하지 못하면 다음 Sequence header 를 찾아야 합니다. (이런경우 에러메세지 꼭 찍어둡시다. 나중에 증빙자료로 요긴합니다.)
    11. 이제 Header 가 Video code 라면 PacketSize만큼을 video decoder 에 push 하면 됩니다. 마찬가지로 Audio code 도 audio decoder 에 push 하면 됩니다.
    12. Stream buffer 가 소진될때까지 Sequence header 절차를 다시 진행합니다.
  • TODO: MPEG1 system 과 MPEG2 program 간의 차이점을 기술하고 이에 따른 호환성있는 구현방안에 대해서 설명이 필요

13.3. Demux TS

  • 일반 짧게 정리하면 PAT(프로그램선택) -> PMT(Descriptor로 언어권 선택) -> Video/Audio PID 추출 -> MGT/VCT/... 등
  • PS 보다 demux 가 좀 복잡한듯 생각됩니다. 어쨌건 설명해보겠습니다. 역시 소스는 공개하지 않겠습니다. 우선 TS는 Packet 의 크기가 일정하다는데서 처음에 위안이 될지 모르겠습니다. 하지만 이것은 실제 PES를 조합하는데 추가적인 버퍼가 필요하다는 것을 의미하며 좀 귀찮은 코드가 있어야 한다는것이 의미로 받아들여질지도 모르겠습니다. 우선 TS는 188, 192, 204바이트(보통은 188) 단위마다 Sync byte(47H) 가 존재하고 이것을 기점으로 하나의 Packet을 얻을수 있습니다. 또한 TS인지의 여부를 판별하는것은 당연히 Sync byte(47H)가 열쇠라는 것도 의미합니다. TS여부를 판별하는 코드는 단순할수 있다는 뜻으로 예기한겁니다.
  • TS header 항목의 의미
    • Sync byte : TS의 기준 정렬의 열쇠가 되는 1바이트값으로 47H 로 주기적인 바이트 간격마다 나타납니다. 주로 188바이트의 배수위치에 나타나는 것이 보통이며 FEC라고 하여 204바이트마다 나타나기도 합니다.
    • Transport error indicator : 이것은 진짜 Error가 났다고 알리는 것은 아니며 중간에 패킷을 잃어버렸을때 이 비트를 보고서 함께 무시하여도 되는지를 알수 있도록 힌트 비트입니다.
    • Payload unit start indicator : 하나의 패킷의 시작을 알려주는 비트로 이 값이 1인경우인 패킷에 TS unit header 가 존재하며 이를 통하여 payload 크기를 얻어 패킷을 조합할수 있습니다.
    • Transport priority : TS 전송경로에 같은 성격의 전송매체가 있다면 그중에서 우선순위를 결정합니다. (별로 사용할일은 없어보입니다.)
    • PID : Packet ID 또는 Program ID 라고 하여 하나의 전송채널을 뜻하기도 합니다. 즉, 고유 채널 식별자라고 하면 적당할듯 합니다. PID는 0인경우 PAT라는 것이 존재하며 2부터 15까지와 8191은 해당패킷을 해석하지 않습니다. 또한 16인경우 PMT가 존재하기도 하며 그 밖에 일부 PID는 상황에 따라서 특정한 전송체로 예약됩니다.
    • Transport scrambling control : 해당 TS가 Scrambling 되었는지 여부를 나타내는 필드입니다. 이는 원치 않는 data의 유입을 회피하기 위한 수단중에 하나로서 data의 순서를 바꾸는 행위를 동반합니다. 이때 필요한것이 PID 1번인 CAT 정보를 참조해야 합니다. (0x00 : Not Scrambled / 0x01 Reserved for future use/ 0x10 Scrambled with Even Key / 0x11 Scrambled with Odd Key)
    • Adaptation field control : 이것은 실제 payload 의 위치가 어디에 놓였는지 예견해줍니다. 또한 이것은 2개의 비트로 구성되며 00B 또는 10B 인경우 해당 패킷에 payload 가 존재하지 않는것을 의미합니다. 그리고 11B 인경우 188 packet의 offset 5 위치에 얼마나 뒤에 payload가 존재하는지를 의미하는 상대 오프셋값이 존재합니다.
    • Continuity counter : 이것은 packet의 순차적인 전송에서 각 packet마다 1씩 증가하면서 4비트 회전 증가값입니다. 즉, 0 부터 15까지 증가했다면 다음에는 다시 0부터 시작합니다. 이 값이 반드시 1씩 증가해야만 정상적인 packet으로 볼수 있으며 만약 증가하지 않고 같은 값이 반복된다면 이것은 bitrate control 또는 보정을 위한 재전송에 이용되었을 가능성이 높은 packet입니다. 즉, 1씩 증가하지 않으면 그 패킷은 해석하지 않고 건너뛰면 된다는 예기를 복잡하게 했습니다. 각 PID별로 별도의 continuity counter 가 증가된다는 것을 염두하셔야 합니다.
  • PSI : PSI(Programme Specific Information)
    1. 디코더가 TS 스트림내에 있는 프로그램을 디코딩 할 수 있도록 사용자가 정해주는 프로그램 정보들을 지칭한다.
    2. 만약 다중 채널 전송이라면 프로그램의 달라진 정보를 디코더가 가능한 빠른 시간 내에 정보를 받을 수 있어야 하는데 이를 위해서는 PSI정보를 일정한 시간간격으로 전송해야 한다.
    3. MPEG에서는 최대한 0.7초 이내에 PSI정보를 전송해야 한다고 규정하고 있다.
    4. PSI는 총 4개의 테이블로 이루어 진다. PAT, PMT, NIT, CAT이다.
      1. PAT : 일단 설명 보류. (다중채널 전송이 아니라면 이 packet은 건어뛴다는 정도만 일단 예기하겠습니다.)
        1. H.264 에서는 이것을 그냥 간과할수 없을것입니다.
        2. PID 0번을 지칭합니다.
        3. 규약에서는 이것이 0.7초 이내에 한번씩은 반드시 명시되어야 되는것으로 되어 있습니다.
        4. PAT는 PMT_PID와 프로그램 번호(Program channel or Program Number)로 이루어져 있다. 한개의 프로그램을 전송한다면 PMT_PID는 동일 하겠지만 여기서 말하는 다중채널 전송이라면 여러개의 PMT_PID를 가질수 있다.
      2. PMT : 일단 설명 보류. (다중채널 전송이 아니라면 이 packet은 건어뛴다는 정도만 일단 예기하겠습니다.)
        1. H.264 에서는 이것을 그냥 간과할수 없을것입니다.
        2. PAT에서 PMT를 나타내는 PID정보와 Program channel 을 참조할수 있게 됩니다.
      3. NIT : NIT(Nwtwork Information Table)
        1. 전송에 관련된 파라메터를 나타내주는 값으로 주파수나 트랜스포트번호 등에 관련된 데이타를 예로 들수 있다. 이와 같은 데이터를 전송하는 패킷의 PID도 PAT에 포함되어 있다.
        2. PAT에서 프로그램 번호가 0인 PMT_PID는 NIT_PID를 나타낸다. 다시 말한다면 PAT에서 프로그램 번호가 0인 PMT_PID가 있다면 이 PID를 갖는 TS packet는 NIT의 정보를 가지고 있다.
      4. CAT : 조건부 수신이 필요한 경우 스크램블링 혹은 Private stream에 관한 것이다.
  • Demux 절차 : 특별한 언급이 없다면 big endian 입니다.
    1. Packet을 해석하기전에 8192개의 PID별 저장소를 만들어 두어야 합니다.
    2. 우선 Stream buffer 로부터 BYTE단위로 읽어서 첫 Sync byte(47H)가 나타날때까지 소진시킵니다.
    3. 이제 188바이트만큼이 하나의 packet 입니다. 그리고 여기서 첫 Sync byte를 포함하여 4바이트를 Header값으로 취합니다.
    4. 이제 PID의 정상적인 범위인 0, 1, 16...8190 만 남기고 나머지의 경우는 전부 해석하지 않고 버립니다.
    5. 이제 PID별 저장소에 마지막 continuity counter 값이 유지되어야 하며 그 밖에 깨진 패킷수 계산등도 저장소에 의해서 유지합니다.
    6. 간단히 PID x 에 대한 고유 저장소를 TSS(Transport store stack)라고 명칭합시다. 이제 이것은 변수 포인터로 간단히 현재 PID의 해석통로로 구현될수 있습니다.
    7. 이제 TSS의 이전 Continuity counter 와 현재의 counter 를 비교하여 이전보다 현재것이 1크다면 정상적인 packet으로 다음 절차를 밟지만 아닌경우는 다시한번 Transport error 비트를 확인하여 1이면 현재 패킷은 해석하지 않고 버립니다.
    8. Adaptation field control 값이 00B 또는 02B인경우 역시 packet 해석을 하지 않고 버립니다.
    9. 만약 Adaptation field control 값이 11B인경우는 Header뒤에 1바이트가 건너뛸 padding 바이트 크기를 나타냅니다. 그래서 01B인경우는 그냥 이 절차를 통과하지만 11B인경우는 padding 바이트만큼 건너뛰어서 다음 절차해석에 들어갑니다.
    10. 이제 2가지로 나뉘는데 Payload unit start 비트에 의해서 나뉘어 집니다. 1인경우 payload의 고유 header가 존재하며 이를 해석해야 하며 MPEG이 전송매체라면 MPEG PES header 해석절차를 밟아야 하겠습니다. 그리고 0인경우는 payload 크기만큼 을 그대로 복사하면 됩니다.
    11. 이러한 절차가 전송채메가 어떤것인가에 따라서 추가적인 해석을 필요로 할수도 있고 아닐수도 있겠으며 그것은 적절히 해석하실수 있을겁니다.
  • TODO: PSI 에 대한 개괄적인 설명이 필요
  • TODO: TS broadcast 를 어떻게 구현하는지에 대한 설명이 필요
  • 높은전송속도의 TS를 해석하려면 효율적인 filter 구현이 필요합니다. 그중에서 Section filter 는 매우 중요한 비중을 차지합니다. 이러한 filter 에 대한 예시를 [http]http://www.minzkn.com:2744/blog/232에서 구현해 보았습니다.

13.4. Demux DivX (3)

DIVX의 Continer Format은 RIFF AVI이며 이와 관련된 문서는 AVI File Format (http://www.alexander-noe.com/video/documentation/avi.pdf), OpenDML AVI File Format Extensions (http://www.morgan-multimedia.com/download/odmlff2.pdf) 등이 있다.
  • (필자주: 공부중. 누가 관련 문서좀 있으면 보내주세요.)

14. 버퍼탄력 설계

14.1. 할당전략

  • Implementation

14.2. 복사전략

  • boost mode

14.3. 비율전략

  • Scale facter

14.4. 비트전략

14.5. 조율전략

15. 전송속도 조절설계

15.1. 정밀도의 오차범위 한계설정

15.2. 다중채널 조합전송 관리


16. Streamming

  • RTSP(Real time streaming protocol) 는 통상 554번 포트를 사용하는 Text(XML고려) 기반의 상호 대화형식의 프로토콜입니다.
  • RTP (Realtime transport protocol)는 RTSP에 의해서 상호 대화에 의해서 수집된 정보 또는 다른방법으로 수집된 정보를 기반으로 실제 Data를 전송하는 채널로 TCP, UDP(Unicast, Multicast) 등의 전송형식을 갖는 전송로입니다. 포트는 RTSP상호 대화에서 결정되기도 하고 이미 약속한 예약포트를 사용하기도 합니다.
  • RTCP : RTSP/RTP 보조
  • RMCP(Relayed Multicast Control Protocol) : 기존의 Multicast망구성을 개선하여 효율적인 전송을위해서 계층적으로 분산하는 프로토콜로 상당히 필요성이 높아져 가고 있습니다.
  • ISMA : RTSP와 비스므레하면서도 좀더 확장된 개념으로 볼만한 잘 짜여진 구조. SDP형식 사용. (관련 문서는 돈주고 사야함)
  • MMS(t) : (필자주: 관련문서 검색해보면 실망 무지함. 하나도 없음.)
    1. MMST 에서 오프셋 4의 2바이트의 용도가 비밀에 가려져 있음. MediaPlayer 만이 이값에 따라서 재생 못하는 현상을 보임.
    2. PreRoll time 에 의한 초기 진입 시간 제약 존재 : 대책이 없음.
  • TODO: 전반적인 RTSP/RTP/ISMA 에 대한 내용은 따로 빼는게 좋을것 같음.
  • TODO: ISMA/MMS 에 대한 부분은 공개해도 될지 모르겠네...
  • TODO: ACAP/OCAP
  • TODO: PCR-STC clock 편차 보정 (5KHz? 100msec? 45kHz? 400msec?)
    • 일정한 정규속도를 갖는 상태인 스트리밍 공급상태에서는 공급되는 PCR과 Decoder의 STC간에는 일정한 간격이상의 시간차가 발생하지 않도록 유지되는데 이상적인 상황인 STD모델에서는 이것을 0msec로 규정할수 있습니다. 일반적으로 공급되는 PCR과 STC간의 시간차는 100msec를 넘지 않도록 Encoding이 되는것이 보통이며 (반드시는 아니고 권장치일뿐) Decoding시에는 이것이 400msec 를 넘지 않는 선에서는 정상적인 Sync가 됩니다만 실제 구현상에서는 Encoder/Decoder간에 조율이 필요할 경우가 많습니다. H/W적이던가 S/W적인 한계로 인해서 경우에 따라서는 STC delay 기법을 사용하기도 하며 반대로 PTS지연입력을 하기도 합니다. 완전 꽁수인거죠.

16.1. 구문파서/필터

  • get_sep_word

16.1.1. Section Filter

MPEG TS 의 SectionFilter 를 구현하는데 있어서 Positive&Negative mask filter 알고리즘으로 적절한 H/W 적인 Filter와 유사한 동작을 구현할수 있게 됩니다.

여기서 Positive mask 는 주어진 data 와 mask 값을 AND연산후 comp 와 비교하는 일반적인 mask 동작으로 특정 비트패턴을 일치조건으로 취하는 동작입니다. 반면에 Negative mask 는 주어진 data 와 mask 값을 AND연산후 주어진 비트패턴이 주어진 comp 값과 다른 경우 일치조건으로 취하는 동작입니다.

Positive mask 는 특정 비트열을 통과시키는 용도로 구현되며 Negative mask 는 현재조건이 변경되는 시점을 통과시키는 용도로 구현되는것이죠.

아래의 예제가 충분할지는 모르겠지만 대략 다음과 같이 구현된다고 보시면 됩니다. 당연히 H/W 설계적인 관점에서도 가능한 구조로 되어 있습니다.

거의 기능상 구현으로 봤을때는 Positive mask 만 있으면 충분합니다. 하지만 불필요한 반복적인 data를 무시하지 않기 때문에 CPU가 과도하게 피곤해지겠죠. Negative mask 는 이것을 방지하게 됩니다.
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <unistd.h>

static __inline int __mzmatch_filter(const unsigned char *s_data, const unsigned char *s_mask, const unsigned char *s_mode, const unsigned char *s_comp, size_t s_size);
int mzmatch_filter(const void *s_data, const void *s_mask, const void *s_mode, const void *s_comp, size_t s_size);

static __inline int __mzmatch_filter(const unsigned char *s_data, const unsigned char *s_mask, const unsigned char *s_mode, const unsigned char *s_comp, size_t s_size)
{
    size_t s_offset = (size_t)0;
    unsigned char s_positive_mask, s_negative_mask;
    while(s_offset < s_size) {
        s_positive_mask = s_mask[s_offset] & s_mode[s_offset];
        s_negative_mask = s_mask[s_offset] & (~s_mode[s_offset]);
        if(((s_positive_mask & s_data[s_offset]) != (s_positive_mask & s_comp[s_offset])) ||
           ((s_negative_mask != ((unsigned char)0)) && ((s_negative_mask & (s_data[s_offset] ^ s_comp[s_offset])) == ((unsigned char)0)))) {
            return(0);
        }
        s_offset++;
    }
    return(1);
}

int mzmatch_filter(const void *s_data, const void *s_mask, const void *s_mode, const void *s_comp, size_t s_size)
{
    return(__mzmatch_filter((const unsigned char *)s_data, (const unsigned char *)s_mask, (const unsigned char *)s_mode, (const unsigned char *)s_comp, s_size));
}

void test(unsigned char s_mask, unsigned char s_mode, unsigned char s_comp)
{
    int s_match_condition;
    unsigned int s_data_min = 0x00u, s_data_max = 0xffu;
    (void)fprintf(stdout, "TEST => data=%02X~%02X mask=%02X mode=%02X comp=%02X",
        (unsigned int)s_data_min,
        (unsigned int)s_data_max,
        (unsigned int)s_mask,
        (unsigned int)s_mode,
        (unsigned int)s_comp
    );
    do {
        if((s_data_min % 0x10u) == 0x00u)(void)fputs("\n", stdout);
        s_match_condition = mzmatch_filter(&s_data_min, &s_mask, &s_mode, &s_comp, (size_t)1);
#if defined(__linux__) /* can use ansi code(escape sequence code) */
        (void)fprintf(stdout,
            " %s%02X\x1b[0m",
            (s_match_condition == 0) ? "\x1b[1;31m" : "\x1b[1;33m",
            (unsigned int)s_data_min);
#else
        if(s_match_condition != 0) {
            (void)fprintf(stdout,
                " %02X",
                (unsigned int)s_data_min);
        }
#endif
    }while((s_data_min++) < s_data_max);
    (void)fputs("\n", stdout);
}

int main(void)
{
    test(0xffu, 0xffu, 0x80u);
    test(0xffu, 0x00u, 0x80u);
    test(0xffu, 0xf9u, 0xceu);
    test(0xffu, 0xf0u, 0x24u);
    test(0x3fu, ~((unsigned char)0x3fu), 0x04u);
    /* TS filter */
    test(0xffu, 0xffu, 0x00u); /* PAT */
    test(0xffu, 0xffu, 0x01u); /* CAT */
    test(0xffu, 0xffu, 0x02u); /* PMT */
    test(0xffu, 0xffu, 0x3eu); /* DSM CC */
    test(0xfcu, 0xffu, 0x80u); /* ECM 0x80, 0x81, 0x82 */
    return(EXIT_SUCCESS);
}

16.2. 전송 Thread

  • ...
Retrieved from https://www.minzkn.com:443/moniwiki/wiki.php/DemuxMPEG
last modified 2021-10-21 13:04:42