最近完成了新的專案,就是處理 MP4 檔案的 Apache module。
 
大致內容是,當 user request mp4 檔案格式時,需 parse mp4 header 並取得 max_bps,avg_bps 等資訊。將資料送出時,則根據 mp4 檔案的所需頻寬送出,做 Rate control。
這部分的程式因為是幫公司寫的,所以不能分享 souce code,只能在這邊分享一些實作上需注意的事情。
事實上,這是我第一次寫 Apache module,只是看範例+code tracing 把它作出來,沒有深入研究。若有錯誤還希望有高手指正喔!
 
  • Module Initization
    a. register_hooks(apr_pool_t *p)
    在開始寫 module 時,必須以 hook 的方式向 Apache 註冊你的 module,跟 windows 滿像的。
    Hook 的方式是使用 ap_hook_handler,而 hook 的位置可以是:
    APR_HOOK_REALLY_FIRST、APR_HOOK_FIRST、APR_HOOK_MIDDLE、APR_HOOK_LAST、APR_HOOK_REALLY_LAST。
    原則上可參考 撰寫 Apache module (二) - 模組架構及流程 裡面的流程圖,這幾項主要是在多個 modules 中處理的流程。
    b. ap_register_output_filter
    如果你的專案需要針對檔案輸出做控制的話,則你需要註冊一個 output_filter。透過這個 filter 來處理。好比說我需要做的 rate control 就需要註冊一個 filter。
    c. ap_hook_post_config
    如果你需要讀取 httpd.conf 或是 conf.d/ 下的設定檔的話,你就要使用 ap_hook_post_config 來處理設定檔的部分。
    d. AP_MODULE_DECLARE_DATA
    除了 hook 之外,也要描述一下你的 module 的定義:
    module AP_MODULE_DECLARE_DATA your_module = 
    {
        STANDARD20_MODULE_STUFF,    // stuff that needs to be declared in every 2.0 mod 
        NULL,                       // create per-directory config structure            
        NULL,                       // merge per-directory config structures            
        NULL,                       // create per-server config structure               
        NULL,                       // merge per-server config structures               
        NULL,                       // command apr_table_t                              
        register_hooks              // register hooks                                   
    };

    e. command_rec
    這裡需要定義你在 httpd.conf 中所需要的參數,以及參數的個數。(以下是我的定義)。
    static const command_rec mp4_d_cmds[] =
    {
        AP_INIT_FLAG("Mp4RateControl", mp4ratecontrol, NULL,
                     RSRC_CONF | ACCESS_CONF, 
                     "On or Off to enable or disable (default) the Mp4RateControl"),
        AP_INIT_TAKE1("Mp4Ratio", mp4ratio, NULL, RSRC_CONF | ACCESS_CONF,
                       "transfer ratio"),
        AP_INIT_TAKE1("Mp4FastStart", mp4faststart, NULL, RSRC_CONF | ACCESS_CONF,
                       "transfer N seconds data of the first request (in second) "),              
        {NULL}
    };
  • 取得設定資料
    前面的 module init 完成之後,先需要取得的就是設定檔資訊,這樣你的 module 運作時才可以抓到 user 的設定。
     
  • 進入程式 handle
    當以上都設定,並取得設定資料後。當 Apache 服務啟動,並有 user request 時,程式就會被導入你的模組,可以用以下的 function 取得你的設定資料:
    your_server_config *sconf =
        (your_server_config *) ap_get_module_config(r->server->
                                                         module_config,
                                                         &your_module);
    your_config *conf =
        (your_config *) ap_get_module_config(r->per_dir_config,
                                                  &your_module);
    如果讀取完設定,並確定你要繼續處理的話,記得使用 ap_add_output_filter 註冊 output filter。
  • 模組間傳遞資料
    在我的專案中,遇到較大的問題是:我同時註冊了一個 mp4 parsing 的 module 以及一個 rate control 的 filter 。比較麻煩的是要把 parsing 完的資料送到 rate control filter 中。由於看文件不如看程式,所以就追了一下程式碼,發現在 request_rec 中有一個 notes 的結構可以使用。文件中也明確地說到是用來在不同的 module 間傳遞資料使用。所以就利用 apr_table_set(), apr_table_get() 以下幾個 function 來把資料寫入 notes 並在 module 與 filter 之間傳遞。
  • 結語
    這篇只分享一些實作的經驗,礙於工作的關係,不能將 code 分享出來。不過有問題可以一起討論。在製作的過程中,最大的麻煩是沒有好的 debug 工具。而且許多資料都被 apache 包起來了。只能自己寫一些 debug function,然後印出一大堆資訊來看程式到底怎麼跑,資料到底怎麼傳遞。
    這算個比較急的案子,所以就只能用土方法,邊寫 code 邊看文件的方式把它趕緊做出來。或許以後有機會再好好認真看文件,找工具囉!
Facebook 討論區載入中...