2015年4月7日 星期二

Kconfig

來源: http://www.wretch.cc/blog/welkinchen/9859140
Kconfig 文件的用途 (Make 選單的建立)

Kconfig文件的用途

配置選項是以樹的形式組織的,每個選項都有其自己的依賴關係。
這些依賴關係決定了選項是否是可見的。父選項可見,子選項才能可見。

核心原始碼的目錄下都有兩個文件 Kconfig(2.4版本是Config.in)和 Makefile。
分佈到各目錄的Kconfig構成了一個分散式的核心配置資料庫,
每個 Kconfig分別描述了與所屬目錄文件相關的核心配置功能表。
在核心配置 make menuconfig(或 xconfig 等)時,
從 Kconfig 中讀出功能表,用戶選擇後保存到 .config的核心配置文件中。
在核心編譯時,主Makefile 調用這個 .config,就知道了用戶的選擇。

*上面的內容說明了, Kconfig 就是對應著核心的配置功能表。
假如想添加新的驅動到核心的原始碼中,修改 Kconfig,這樣就能夠選擇這個驅動,
假如想編譯這個驅動,要修改 Makefile,所以添加新的驅動時需要修改的文件有兩種(注意不只是兩個)
Kconfig
Makefile

要想知道怎麼修改這兩種文件,就要知道兩種文件的語法結構

Kconfig
每個功能表都有一個關鍵字標識,最常見的就是config

config MODVERSIONS
    bool "Set version information on all module symbols"
    depends MODULES
    help
      Usually, modules have to be recompiled whenever you switch to a new
      kernel.  ...

每行都是以關鍵字開始,並可以接多個參數。
"config" 為定義了一新的配置選項。
而緊接著的下面幾行定義了該配置選項的屬性。
屬性可以是該配置選項的類型,輸入提示(input prompt),依賴關係,幫助資訊和預設值。
配置選項可以用相同的名字定義多次,但每個定義只能有一個輸入提示並且類型不能衝突。

語法:
config
symbol是個新的標記的功能表項,options是在這個新的功能表項下的屬性和選項
其中options部分有:

- 類型定義:"bool"/"tristate"/"string"/"hex"/"int"
  每個config功能表項都要有類型定義,每個配置選項都必須指定類型。
  有兩個基本類型:tristate 和 string,其他類型都是基於這兩個基本類型。
 
  bool布林類型、
  tristate三態:內建、模組、移除、
  string字串、
  hex十六進位、
  integer整型
 
  例如config HELLO_MODULE
  bool "hello test module"
  bool 類型的只能選或不選,tristate 類型的選項多了編譯成核心模組的選項,
       假如選擇編譯成核心模組,則會在 .config 中生成一個 CONFIG_HELLO_MODULE=m 的配置,
       假如選擇內建,就是直接編譯進核心,就會在 .config 中生成一個 CONFIG_HELLO_MODULE=y 的配置.

  類型定義可以用輸入提示,所以下面的兩個例子是等價的:
     bool "Networking support"
  and
     bool
          prompt "Networking support"

- 依賴型定義 depends on或 requires
  指此功能表的出現與否是依賴另一個定義
  config HELLO_MODULE
         bool "hello test module"
         depends on ARCH_PXA

  這個例子表明HELLO_MODULE這個功能表項只對XScale處理器有效。

- 幫助資訊: "help" or "---help---"
  定義一幫助資訊。幫助資訊的結束就由縮進的水準決定的,
  這也就意味著資訊是在第一個比幫助資訊開始行的縮進小的行結束。
  "---help---" 和 "help" 在實現的作用上沒有區別,
  "---help---" 有助於將檔中的配置邏輯與給開發人員的提示分開。

- 輸入提示: "prompt" ["if" ]
  每個功能表選項最多只能有一個顯示給用戶的輸入提示。
  可以用 "if" 來表示該提示的依賴關係,當然這是可選的。

- 預設值:"default" ["if" ]
  一個配置選項可以有任意多個預設值。
  如果有多個預設值,那麼只有第一個被定義的值是可用的。
  預設值並不是只限于應用在定義他們的功能表選項。
  這就意味著預設值可以定義在任何地方或被更早的定義覆蓋。
  如果用戶沒有設置(通過上面的輸入提示),配置選項的值就是預設值。
  如果可以顯示輸入提示的話,就會把預設值顯示給用戶,並可以讓用戶進行修改。
  預設值的依賴關係可以用 "if" 添加。(可選項)

- 依賴關係:"depends on"/"requires"
  為一功能表選項定義依賴關係。
  如果定義了多個依賴關係,它們之間用 '&&' 間隔。
  依賴關係也可以應用到該功能表中所有的其他選項(同樣接受一if運算式),
  所以下面的兩個例子是等價的:

    bool "foo" if BAR
         default y if BAR
  and
    depends on BAR
    bool "foo"
         default y

- 反向依賴關係:"select" ["if" ]
  儘管普通的依賴關係可以降低一選項的上限,反向依賴能將這一限制降的更低。
  當前功能表選項的值是symbol的最小值。
  如果symbol被選擇了多次,上限就是其中的最大值。
  反向依賴只能用在 boolean 或 tristate 選項上。

- 資料範圍:"range" ["if" ]
  為int和hex類型的選項設置可以接受輸入值範圍。
  用戶只能輸入大於等於第一個symbol,小於等於第二個symbol的值。


選單依賴關係
------------

依賴關係決定了功能表選項是否可見,也可以減少tristate的輸入範圍。
tristate邏輯比boolean邏輯在表達式中用更多的狀態(state)來表示模組的狀態。
依賴關係運算式的語法如下:

::=                              (1)
           '='                 (2)
           '!='                (3)
           '(' ')'                       (4)
           '!'                            (5)
           '&&'                    (6)
           '||'                    (7)

運算式是以優先順序的降冪列出的。

(1) 將symbol賦給運算式。boolean和tristate類型的symbol直接賦給運算式。
    所有其他類型的symbol都賦 'n'。
(2) 如果兩個symbol相等,返回'y',否則為'n'。
(3) 如果兩個symbol相等,返回'n',否則為'y'。
(4) 返回運算式的值。用於改變優先順序。
(5) 返回 (2-/expr/) 的結果。
(6) 返回 min(/expr/,/expr/) 的結果。
(7) 返回 max(/expr/,/expr/) 的結果。

一個運算式的值可以是'n','m'或'y'(或者是計算的結果 0,1,2)。
當運算式的值為'm'或'y'的時候,選單選項才是可見的。

symbol有兩種類型:不可變的和可變的。不可變的symbol是最普通的,
由'config'語句定義,完全由數字、字母和下劃線組成(alphanumeric characters or underscores)。
不可變的symbol只是運算式的一部分。經常用單引號或雙引號括起來。
在引號中,可以使用任何字元,使用引號要用轉義字元'\'。

功能表結構
--------

功能表在樹中的位置可由兩種方法決定。第一種可以是這樣:

menu "Network device support"
    depends NET

config NETDEVICES
    ...

endmenu

所有的在"menu" ... "endmenu" 之間都是"Network device support"的子功能表。
所有的子功能表選項都繼承了父功能表的依賴關係,
比如,"NET"的依賴關係就被加到了配置選項NETDEVICES的依賴列表中。

還有就是通過分析依賴關係生成功能表的結構。
如果功能表選項在一定程度上依賴於前面的選項,它就能成為該選項的子功能表。
首先,前面的(父)選項必須是依賴列表中的一部分並且它們中必須有滿足下面兩個條件的選項:
- 如果父選項為'n',子選項必須不可見。
- 如果父選項可見,子選項才能可見。

config MODULES
    bool "Enable loadable module support"

config MODVERSIONS
    bool "Set version information on all module symbols"
    depends MODULES

comment "module support disabled"
    depends !MODULES

MODVERSIONS 直接依賴 MODULES,這就意味著如果MODULES不為'n',該選項才可見。
換句話說,當MODULES可見時,選項才可見(MODULES的(空)依賴關係也是選項依賴關係的一部分)。

Kconfig 語法
------------

配置檔描述了功能表選項,每行都是以一關鍵字開頭(除了幫助資訊)。
下面的關鍵字結束一功能表選項:
- config
- menuconfig
- choice/endchoice
- comment
- menu/endmenu
- if/endif
- source
前5個同樣可以用在功能表選項定義的開始。

config:

    "config"
   

定義了一配置選項 並且可以接受任何前面介紹的屬性。

menuconfig:
    "menuconfig"
   

此關鍵字和前面的關鍵字很相似,但它在前面的基礎上要求所有的子選項作為獨立的行顯示。
( This is similar to the simple config entry above,
  but it also gives a hint to front ends,
  that all suboptions should be displayed as a separate list of options.)

choices:

    "choice"
   
   
    "endchoice"

該關鍵字定義了一組選擇項,並且選項可以是前面描述的任何屬性。
儘管boolean只允許選擇一個配置選項,tristate可以於多個配置選項設為'm',
但選項只能是boolean或tristate類型。
這可以在一個硬體有多個驅動的情況下使用,最終只有一個驅動被編譯進/載入到核心,
但所有的驅動都可以編譯成模組。
選項可以接受的另一個選項是"optional",這樣選項就被設置為'n',沒有被選中的。

comment:

    "comment"
   

這裏定義了在配置過程中顯示給用戶的注釋,該注釋還將寫進輸出檔中。
唯一可用的可選項是依賴關係。

menu:

    "menu"
   
   
    "endmenu"

這裏定義了一個功能表,詳細資訊請看前面的"功能表結構"。
唯一可用的可選項是依賴關係。

if:

    "if"
   
    "endif"

這裏定義了if結構。依賴關係被加到所有在if ... endif 中的功能表選項中。
(lb:請看linux-source/drivers/ata/Kconfig:20)

 

Makefile
在 linux2.6.x/Documentation/kbuild 目錄下有詳細關於 kernel makefile 的介紹。
核心的Makefile分為5個組成部分:
Makefile  最頂層的Makefile
.config   核心的當前配置文件,編譯時成為定層Makefile的一部分
arch/$(ARCH)/Makefile 和體系結構相關的 Makefile
s/ Makefile.*   一些Makefile的通用規則
kbuild Makefile 各級目錄下的大約有500個文件,編譯時根據上層 Makefile 傳下來的
                巨集定義和其他編譯規則,將源代碼編譯成模組或編入核心

頂層的Makefile文件讀取 .config 文件的內容,並且在總體上負責建立核心和模組。
Arch Makefile 則提供補充系統結構相關的資訊。
s 目錄下的 Makefile 文件包含了任何根據 kbuild Makefile 構建核心所需的定義和規則。
(其中 .config 的內容是在 make menuconfig 的時候,通過 Kconfig 文件配置的結果。

舉個例子:
假設想把自己寫的一個flash的驅動程式載入到工程中,而且能夠通過 menuconfig 配置
核心時選擇該驅動該怎麼辦呢?能夠分三步:

第一:將您寫的 flashtest.c 文件添加到 /driver/mtd/maps/ 目錄下。
第二:修改 /driver/mtd/maps 目錄下的 kconfig 文件:
      config MTD_flashtest
      tristate “ap71 flash"
      這樣當 make menuconfig 時 ,將會出現 ap71 flash選項。
第三:修改該目錄下makefile文件。
      添加如下內容:obj-$(CONFIG_MTD_flashtest)+= flashtest.o

這樣,當您運行 make menucofnig 時,您將發現ap71 flash選項。
假如您選擇了此項,該選擇就會保存在 .config 文件中。
當您編譯核心時,將會讀取 .config 文件,當發現 ap71 flash 選項為 yes 時,
系統在呼叫 /driver/mtd/maps/ 下的 makefile 時,將會把 flashtest.o 加入到核心中。

2015年3月24日 星期二

EtherCAT


節錄自 http://zh.wikipedia.org/wiki/EtherCAT

EtherCAT乙太網控制自動化技術[1])是一個開放架構,以乙太網為基礎的現場總線系統,其名稱的CAT為控制自動化技術(ControlAutomation Technology)字首的縮寫。EtherCAT是確定性的工業以太網,最早是由德國的Beckhoff公司所研發[2]
自動化對通訊一般會要求較短的資料更新時間(或稱為週期時間)、資料同步時的通訊抖動量低,而且硬體的成本要低,EtherCAT開發的目的就是讓乙太網可以運用在自動化應用中。

OpenMP


官網 http://openmp.org/

以下節錄自 http://zh.wikipedia.org/wiki/OpenMP


OpenMP(Open Multi-Processing)是一套支持跨平台共享內存方式的多執行緒並發的編程API,...,可以在大多數的處理器體系和作業系統中運行,包括Solaris, AIX, HP-UX, GNU/Linux, Mac OS X, 和Microsoft Windows。包括一套編譯器指令、庫和一些能夠影響運行行為的環境變量。

OpenMP是由OpenMP Architecture Review Board牽頭提

出的,並已被廣泛接受的,用於共享內存並行系統的多執行緒程序設計的一套指導性注釋(Compiler Directive)。...。OpenMP提供了對並行算法的高層的抽象描述,程式設計師通過在原始碼中加入專用的pragma來指明自己的意圖,由此編譯器可以自動將程序進行並行化,並在必要之處加入同步互斥以及通信。當選擇忽略這些pragma,或者編譯器不支持OpenMP時,程序又可退化為通常的程序(一般為串行),代碼仍然可以正常運作,只是不能利用多執行緒來加速程序執行。

OpenMP支持的程式語言包括C語言C++Fortran

介紹

OpenMP是一個跨平台的多執行緒實現,主執行緒(順序的執行指令)生成一系列的子執行緒,並將任務劃分給這些子執行緒進行執行。這些子執行緒並行的運行,由運行時環境將執行緒分配給不同的處理器。

要進行並行執行的代碼片段需要進行相應的標記,用預編譯指令使得在代碼片段被執行前生成執行緒,每個執行緒會分配一個id,可以通過函數(called omp_get_thread_num())來獲得該值,該值是一個整數,主執行緒的id為0。在並行化的代碼運行結束後,子執行緒join到主執行緒中,並繼續執行程序。

默認情況下,各個執行緒獨立的執行並行區域的代碼。可以使用Work-sharing constructs來劃分任務,使每個執行緒執行其分配部分的代碼。通過這種方式,使用OpenMP可以實現任務並行數據並行

運行時環境分配給每個處理器的執行緒數取決於使用方法、機器負載和其他因素。執行緒的數目可以通過環境變量或者代碼中的函數來指定。在C/C++中,OpenMP的函數都聲明在頭文件omp.h中。

語法

#pragma omp  [clause[[,] clause] ...]

directive

其中,directive共11個:
  • atomic 內存位置將會原子更新(Specifies that a memory location that will be updated atomically.)
  • barrier 執行緒在此等待,直到所有的執行緒都執行到此barrier。用來同步所有執行緒。
  • critical 其後的代碼塊為臨界區,任意時刻只能被一個執行緒執行。
  • flush 所有執行緒對所有共享對象具有相同的內存視圖(view of memory)
  • for 用在for循環之前,把for循環並行化由多個執行緒執行。循環變量只能是整型
  • master 指定由主執行緒來執行接下來的程式。
  • ordered 指定在接下來的代碼塊中,被並行化的 for循環將依序執行(sequential loop)
  • parallel 代表接下來的代碼塊將被多個執行緒並行各執行一遍。
  • sections 將接下來的代碼塊包含將被並行執行的section塊。
  • single 之後的程式將只會在一個執行緒(未必是主執行緒)中被執行,不會被並行執行。
  • threadprivate 指定一個變量是執行緒局部存儲(thread local storage)

clause

共計13個clause:
  • copyin 讓threadprivate的變量的值和主執行緒的值相同。
  • copyprivate 不同執行緒中的變量在所有執行緒中共享。
  • default Specifies the behavior of unscoped variables in a parallel region.
  • firstprivate 對於執行緒局部存儲的變量,其初值是進入並行區之前的值。
  • if 判斷條件,可用來決定是否要並行化。
  • lastprivate 在一個循環並行執行結束後,指定變量的值為循環體在順序最後一次執行時取得的值,或者#pragma sections在中,按文本順序最後一個section中執行取得的值。
  • nowait 忽略barrier的同步等待。
  • num_threads 設定執行緒數量的數量。默認值為當前計算機硬體支持的最大並發數。一般就是CPU的內核數目。超執行緒被作業系統視為獨立的CPU內核。
  • ordered 使用於 for,可以在將循環並行化的時候,將程式中有標記 directive ordered 的部份依序執行。
  • private 指定變量為執行緒局部存儲。
  • reduction Specifies that one or more variables that are private to each thread are the subject of a reduction operation at the end of the parallel region.
  • schedule 設定for循環的並行化方法;有 dynamic、guided、runtime、static 四種方法。
    • schedule(static, chunk_size) 把chunk_size數目的循環體的執行,靜態依序指定給各執行緒。
    • schedule(dynamic, chunk_size) 把循環體的執行按照chunk_size(預設值為1)分為若干組(即chunk),每個等待的執行緒獲得當前一組去執行,執行完後重新等待分配新的組。
    • schedule(guided, chunk_size) 把循環體的執行分組,分配給等待執行的執行緒。最初的組中的循環體執行數目較大,然後逐漸按指數方式下降到chunk_size。
    • schedule(runtime) 循環的並行化方式不在編譯時靜態確定,而是推遲到程序執行時動態地根據環境變量OMP_SCHEDULE 來決定要使用的方法。
  • shared 指定變量為所有執行緒共享。


OpenMP的庫函數

OpenMP定義了20多個庫函數:
1.void omp_set_num_threads(int _Num_threads);
在後續並行區域設置執行緒數,此調用只影響調用執行緒所遇到的同一級或內部嵌套級別的後續並行區域.說明:此函數只能在串行代碼部分調用.
2.int omp_get_num_threads(void);
返回當前執行緒數目.說明:如果在串行代碼中調用此函數,返回值為1.
3.int omp_get_max_threads(void);
如果在程序中此處遇到未使用 num_threads() 子句指定的活動並行區域,則返回程序的最大可用執行緒數量.說明:可以在串行或並行區域調用,通常這個最大數量由omp_set_num_threads()或OMP_NUM_THREADS環境變量決定.
4.int omp_get_thread_num(void);
返回當前執行緒id.id從1開始順序編號,主執行緒id是0.
5.int omp_get_num_procs(void);
返回程序可用的處理器數.
6.void omp_set_dynamic(int _Dynamic_threads);
啟用或禁用可用執行緒數的動態調整.(預設情況下啟用動態調整.)此調用只影響調用執行緒所遇到的同一級或內部嵌套級別的後續並行區域.如果 _Dynamic_threads 的值為非零值,啟用動態調整;否則,禁用動態調整.
7.int omp_get_dynamic(void);
確定在程序中此處是否啟用了動態執行緒調整.啟用了動態執行緒調整時返回非零值;否則,返回零值.
8.int omp_in_parallel(void);
確定執行緒是否在並行區域的動態範圍內執行.如果在活動並行區域的動態範圍內調用,則返回非零值;否則,返回零值.活動並行區域是指 IF 子句求值為 TRUE 的並行區域.
9.void omp_set_nested(int _Nested);
啟用或禁用嵌套並行操作.此調用只影響調用執行緒所遇到的同一級或內部嵌套級別的後續並行區域._Nested 的值為非零值時啟用嵌套並行操作;否則,禁用嵌套並行操作.預設情況下,禁用嵌套並行操作.
10.int omp_get_nested(void);
確定在程序中此處是否啟用了嵌套並行操作.啟用嵌套並行操作時返回非零值;否則,返回零值.
互斥鎖操作 嵌套鎖操作 功能
11.void omp_init_lock(omp_lock_t * _Lock); 12. void omp_init_nest_lock(omp_nest_lock_t * _Lock);
初始化一個(嵌套)互斥鎖.
13.void omp_destroy_lock(omp_lock_t * _Lock); 14.void omp_destroy_nest_lock(omp_nest_lock_t * _Lock);
結束一個(嵌套)互斥鎖的使用並釋放內存.
15.void omp_set_lock(omp_lock_t * _Lock); 16.void omp_set_nest_lock(omp_nest_lock_t * _Lock);
獲得一個(嵌套)互斥鎖.
17.void omp_unset_lock(omp_lock_t * _Lock); 18.void omp_unset_nest_lock(omp_nest_lock_t * _Lock);
釋放一個(嵌套)互斥鎖.
19.int omp_test_lock(omp_lock_t * _Lock); 20.int omp_test_nest_lock(omp_nest_lock_t * _Lock);
試圖獲得一個(嵌套)互斥鎖,並在成功時放回真(true),失敗是返回假(false).
21.double omp_get_wtime(void);
獲取wall clock time,返回一個double的數,表示從過去的某一時刻經歷的時間,一般用於成對出現,進行時間比較. 此函數得到的時間是相對於執行緒的,也就是每一個執行緒都有自己的時間.
22.double omp_get_wtick(void);
得到clock ticks的秒數.


例子

在 omp parallel 段內的程序代碼由多線程來執行:
 int main(int argc, char* argv[])
 {
  #pragma omp parallel  
   printf("Hello, world.\n");
 
   return 1;
 }
執行結果
% gcc omp.c (由單線程來執行)
% ./a.out
Hello, world.

% gcc -fopenmp omp.c (由多線程來執行)
% ./a.out
Hello, world.
Hello, world.
Hello, world.
Hello, world.

環境變量

OpenMP可以使用環境變量 OMP_NUM_THREADS以控制執行線程的數量。

例子

% gcc -fopenmp omp.c 

% setenv OMP_NUM_THREADS 2(由2線程來執行)
setenv是CSH的指令

在bash shell 環境中 要用export 
% export OMP_NUM_THREADS=2 (由2線程來執行)

% ./a.out
Hello, world.
Hello, world.

優點和缺點

優點
  • 可移植的多執行緒代碼(在C/C++和其他語言中,人們通常為了獲得多執行緒而調用特定於平台的原語)
  • 簡單,沒必要向MPI中那樣處理消息傳遞
  • 數據分布和分解由指令自動完成
  • 增量並行,一次可以只在代碼的一部分執行,對代碼不需要顯著的改變
  • 統一的順序執行和並行執行的代碼,在順序執行編譯器上,OpenMP的執行按照注釋進行對待;
  • 在一般情況下,使用OpenMP並行時原始的(串行)代碼語句不需要進行修改,這減少不經意間引入錯誤的機會。
  • 同時支持粗粒度和細粒度的並行
缺點
  • 存在引入難以調試的同步錯誤和競爭條件的風險
  • 目前,只能在共享內存的多處理器平台高效運行
  • 需要一個支持OpenMP的編譯器
  • 可擴展性是受到內存架構的限制
  • 不支持比較和交換
  • 缺乏可靠的錯誤處理
  • 缺乏對執行緒與處理器映射的細粒度控制
  • GPU上不能使用
  • 很容易出現一些不能共享的代碼
  • 多執行緒的可執行文件的啟動需要更多的時間,可能比單執行緒的運行的慢,因此,使用多執行緒一定要有其他有優勢的地方
  • 很多情況下使用多執行緒不僅沒有好處,還會帶來一些額外消耗

OpenCL


節錄自 http://zh.wikipedia.org/wiki/OpenCL

OpenCL (Open Computing Language,開放計算語言) 是一個為異構平台編寫程式的框架,此異構平台可由 CPU GPU 或其他型別的處理器組成。 OpenCL 由一門用於編寫 kernels (在 OpenCL 裝置上執行的函式)的語言(基於 C99 )和一組用於定義並控制平台的 API 組成。 OpenCL 提供了基於任務分割和資料分割的並列計算機制。

2015年3月23日 星期一

Mommy Needs a Raise !






H.264



原文 http://blog.sina.com.cn/s/blog_8fb8cd4801018yyo.html


----------------------
前言
-----------------------
        H264是新一代的編碼標準,以高壓縮高質量和支持多種網絡的流媒體傳輸著稱,在編碼方面,我理解的他的理論依據是:參照一段時間內圖像的統計結果表明,在相鄰幾幅圖像畫面中,一般有差別的像素只有10%以內的點,亮度差值變化不超過2%,而色度差值的變化只有1%以內。所以對於一段變化不大圖像畫面,我們可以先編碼出一個完整的圖像幀A,隨後的B幀就不編碼全部圖像,只寫入與A幀的差別,這樣B幀的大小就只有完整幀的1/10或更小!B幀之後的C幀如果變化不大,我們可以繼續以參考B的方式編碼C幀,這樣循環下去。這段圖像我們稱為一個序列(序列就是有相同特點的一段數據),當某個圖像與之前的圖像變化很大,無法參考前面的幀來生成,那我們就結束上一個序列,開始下一段序列,也就是對這個圖像生成一個完整幀A1,隨後的圖像就參考A1生成,只寫入與A1的差別內容。
        在H264協議裡定義了三種幀,完整編碼的幀叫I幀,參考之前的I幀生成的只包含差異部分編碼的幀叫P幀,還有一種參考前後的幀編碼的幀叫B幀。
        H264採用的核心算法是幀內壓縮和幀間壓縮,幀內壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。



----------------------
序列的說明
----------------------
        在H264中圖像以序列為單位進行組織,一個序列是一段圖像編碼後的數據流,以I幀開始,到下一個I幀結束。
        一個序列的第一個圖像叫做IDR圖像(立即刷新圖像),IDR圖像都是I幀圖像。H.264引入IDR圖像是為了解碼的重同步,當解碼器解碼到IDR圖像時,立即將參考幀隊列清空,將已解碼的數據全部輸出或拋棄,重新查找參數集,開始一個新的序列。這樣,如果前一個序列出現重大錯誤,在這裡可以獲得重新同步的機會。IDR圖像之後的圖像永遠不會使用IDR之前的圖像的數據來解碼。
        一個序列就是一段內容差異不太大的圖像編碼後生成的一串數據流。當運動變化比較少時,一個序列可以很長,因為運動變化少就代表圖像畫面的內容變動很小,所以就可以編一個I幀,然後一直P幀、B幀了。當運動變化多時,可能一個序列就比較短了,比如就包含一個I幀和3、4個P幀。


-----------------------
三種幀的說明
-----------------------
I幀:幀內編碼幀,I幀表示關鍵幀,你可以理解為這一幀畫面的完整保留;解碼時只需要本幀數據就可以完成(因為包含完整畫面)

I幀特點: 
1.它是一個全幀壓縮編碼幀。它將全幀圖像信息進行JPEG壓縮編碼及傳輸; 
2.解碼時僅用I幀的數據就可重構完整圖像; 
3.I幀描述了圖像背景和運動主體的詳情; 
4.I幀不需要參考其他畫面而生成; 
5.I幀是P幀和B幀的參考幀(其質量直接影響到同組中以後各幀的質量); 
6.I幀是幀組GOP的基礎幀(第一幀),在一組中只有一個I幀; 
7.I幀不需要考慮運動矢量; 
8.I幀所佔數據的信息量比較大。 

P幀:前向預測編碼幀。P幀表示的是這一幀跟之前的一個關鍵幀(或P幀)的差別,解碼時需要用之前緩存的畫面疊加上本幀定義的差別,生成最終畫面。(也就是差別幀,P幀沒有完整畫面數據,只有與前一幀的畫面差別的數據)

P幀的預測與重構:P幀是以I幀為參考幀,在I幀中找出P幀“某點”的預測值和運動矢量,取預測差值和運動矢量一起傳送。在接收端根據運動矢量從I幀中找出P幀“某點”的預測值並與差值相加以得到P幀“某點”樣值,從而可得到完整的P幀。 

P幀特點: 
1.P幀是I幀後面相隔1~2幀的編碼幀; 
2.P幀採用運動補償的方法傳送它與前面的I或P幀的差值及運動矢量(預測誤差); 
3.解碼時必須將I幀中的預測值與預測誤差求和後才能重構完整的P幀圖像; 
4.P幀屬於前向預測的幀間編碼。它只參考前面最靠近它的I幀或P幀; 
5.P幀可以是其後面P幀的參考幀,也可以是其前後的B幀的參考幀; 
6.由於P幀是參考幀,它可能造成解碼錯誤的擴散; 
7.由於是差值傳送,P幀的壓縮比較高。 

B幀:雙向預測內插編碼幀。B幀是雙向差別幀,也就是B幀記錄的是本幀與前後幀的差別(具體比較複雜,有4種情況,但我這樣說簡單些),換言之,要解碼B幀,不僅要取得之前的緩存畫面,還要解碼之後的畫面,通過前後畫面的與本幀數據的疊加取得最終的畫面。B幀壓縮率高,但是解碼時CPU會比較累。 

B幀的預測與重構 B幀以前面的I或P幀和後面的P幀為參考幀,“找出”B幀“某點”的預測值和兩個運動矢量,並取預測差值和運動矢量傳送。接收端根據運動矢量在兩個參考幀中“找出(算出)”預測值並與差值求和,得到B幀“某點”樣值,從而可得到完整的B幀。 

B幀特點 
1.B幀是由前面的I或P幀和後面的P幀來進行預測的; 
2.B幀傳送的是它與前面的I或P幀和後面的P幀之間的預測誤差及運動矢量; 
3.B幀是雙向預測編碼幀; 
4.B幀壓縮比最高,因為它只反映丙參考幀間運動主體的變化情況,預測比較準確; 
5.B幀不是參考幀,不會造成解碼錯誤的擴散。 

注:I、B、P各幀是根據壓縮算法的需要,是人為定義的,它們都是實實在在的物理幀。一般來說,I幀的壓縮率是7(跟JPG差不多),P幀是20,B幀可以達到50。可見使用B幀能節省大量空間,節省 出來的空間可以用來保存多一些I幀,這樣在相同碼率下,可以提供更好的畫質。


--------------------------------
壓縮算法的說明
--------------------------------
h264的壓縮方法:
1.分組:把幾幀圖像分為一組(GOP,也就是一個序列),為防止運動變化,幀數不宜取多。
2.定義幀:將每組內各幀圖像定義為三種類型,即I幀、B幀和P幀; 
3.預測幀:以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀; 
4.數據傳輸:最後將I幀數據與預測的差值信息進行存儲和傳輸。
        幀內(Intraframe)壓縮也稱為空間壓縮(Spatial compression)。當壓縮一幀圖像時,僅考慮本幀的數據而不考慮相鄰幀之間的冗餘信息,這實際上與靜態圖像壓縮類似。幀內一般採用有損壓縮算法,由於幀內壓縮是編碼一個完整的圖像,所以可以獨立的解碼、顯示。幀內壓縮一般達不到很高的壓縮,跟編碼jpeg差不多。
  
        幀間(Interframe)壓縮的原理是:相鄰幾幀的數據有很大的相關性,或者說前後兩幀信息變化很小的特點。也即連續的視頻其相鄰幀之間具有冗餘信息,根據這一特性,壓縮相鄰幀之間的冗餘量就可以進一步提高壓縮量,減小壓縮比。幀間壓縮也稱為時間壓縮(Temporal compression),它通過比較時間軸上不同幀之間的數據進行壓縮。幀間壓縮一般是無損的。幀差值(Frame differencing)算法是一種典型的時間壓縮法,它通過比較本幀與相鄰幀之間的差異,僅記錄本幀與其相鄰幀的差值,這樣可以大大減少數據量。

        順便說下有損(Lossy )壓縮和無損(Lossy less)壓縮。無損壓縮也即壓縮前和解壓縮後的數據完全一致。多數的無損壓縮都採用RLE行程編碼算法。有損壓縮意味著解壓縮後的數據與壓縮前的數據不一致。在壓縮的過程中要丟失一些人眼和人耳所不敏感的圖像或音頻信息,而且丟失的信息不可恢復。幾乎所有高壓縮的算法都採用有損壓縮,這樣才能達到低數據率的目標。丟失的數據率與壓縮比有關,壓縮比越小,丟失的數據越多,解壓縮後的效果一般越差。此外,某些有損壓縮算法採用多次重複壓縮的方式,這樣還會引起額外的數據丟失。

2015年3月12日 星期四

不應該在建構函數中呼叫虛擬函數(Virtual function)


程式執行到一個衍生物件並開始建構時, 它的基底物件會先被建構起來並產生其VTABLE. 如果衍生類別有重新定義某個虛擬函數, 則衍生物件的建構函數會在該衍生物件的;VTABLE中更新被重新定義虛擬函數之位址. 這就是你不應該在建構函數中呼叫虛擬函數的原因: 衍生物件中被重定義的虛擬函數之位址可能還沒有被放入/更改到衍生物件之VTABLE中. 你可能執行到舊的虛擬函數.

完整原文