1. 程式人生 > >MJPG-streamer原始碼分析-輸出部分

MJPG-streamer原始碼分析-輸出部分

MJPG-streamer可以建立多個輸出,簡單的說,根據主函式中輸入的引數解析的結果,確定輸出通道的個數,至少保證有一個輸出通道在程式執行時存在。從引數解析結果確定每個輸出通道的引數,並以這些引數為每個通道建立傳送執行緒。在每個傳送執行緒上,不斷偵聽是否有連線請求。每當有連線請求,在未達到最高連線數目時,為每個連線請求建立連線執行緒。在連線執行緒中,根據引數,確實傳送方式是stream?snapshot?或者是其他方式。在連線請求不關閉時,連線執行緒一直存在,連線請求退出時,執行緒隨之退出,並有系統自動釋放其所分配的資源。當程式執行終止訊號stop不為1時,輸出通道執行緒不斷偵聽連線和傳送資料,當終止訊號被置1時,退出輸出執行緒,同時釋放其所分配的資源。

輸出通道的初始化部分僅僅是將輸出通道的引數分別解析和分配到各自通道指定的變數中,為後續每個通道的run函式提供引數。其餘的,都是run函式所執行部分,包括其執行的子函式和操作等。

需要說明的是,由於有多個輸出通道,故而需要對每個通道都進行初始化和執行輸出,這個過程通過一個迴圈體完成,迴圈體的控制變數就是輸出通道的個數。

本文根據上述思路,把關鍵部分原始碼進行解釋,具體的還得參考原始碼,如有錯誤,歡迎指出以便改正。

===============================================初始化部分====================================================

1、定義和初始化部分引數,這些引數用於在對輸出通道引數解析過程中的臨時變數;

  1. char *argv[MAX_ARGUMENTS]={NULL};  
  2. int  argc=1, i;  
  3. int  port;  
  4. char *credentials, *www_folder;  
  5. char nocommands;  
  6. port = htons(8080);  
  7. credentials = NULL;  
  8. www_folder = NULL;  
  9. nocommands = 0;  
  10. argv[0] = OUTPUT_PLUGIN_NAME;  

2、【解析引數】
將傳入的單串引數param->parameter_string轉換為字串陣列,存放在argv陣列中,呼叫c = getopt_long_only(argc, argv, "", long_options, &option_index),當c==-1時,說明解析完畢,退出解析過程,否則根據option_index引數設定port,credentials,www_folder,nocommands等引數

這部分的程式碼與前面主程式和輸入通道程式的解析過程類似,不再詳述,都是把對應引數填寫到指定的變數上。

3、根據param->id配置每個伺服器執行緒引數(servers全域性)

程式為每個輸出通道都定義了一個對應的引數,用於配置和儲存每個輸出通道的資訊。其結構體如下所示:

  1. /* context of each server thread */
  2. typedefstruct {  
  3.   int sd;  
  4.   int id;  
  5.   globals *pglobal;  
  6.   pthread_t threadID;  
  7.   config conf;  
  8. } context;  

經過上述的引數解析過程之後,使用者輸入的引數都被識別,之後再被存放到每個輸出通道伺服器執行緒引數變數上。即servers[param->id]。
  1. servers[param->id].id = param->id;  
  2. servers[param->id].pglobal = param->global;  
  3. servers[param->id].conf.port = port;  
  4. servers[param->id].conf.credentials = credentials;  
  5. servers[param->id].conf.www_folder = www_folder;  
  6. servers[param->id].conf.nocommands = nocommands;  

至此,輸出通道的初始化完畢,等待run函式的執行。

===============================================輸出通道執行部分====================================================

 在每個輸出通道上,先為每個通道建立服務執行緒,呼叫執行緒處理函式進行連線請求的偵聽和處理

  1. int output_run(int id) {  
  2.   DBG("launching server thread #%02d\n", id);  
  3.   //server_thread位於httpd.c標頭檔案中
  4.   pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));  
  5.   //將狀態改為unjoinable狀態,確保資源的釋放,無須呼叫join函式釋放資源
  6.   pthread_detach(servers[id].threadID);  
  7.   return 0;  
  8. }  

執行緒處理函式建立開啟一個socket並等待連線請求,在未達到最大連線請求時,為每個請求建立相對應的客戶端服務執行緒client_thread(),並使用通道執行緒相同的處理方式,將該子執行緒detach處理。子執行緒根據不同的請求方式,進行資料的分發。
  1. void *server_thread( void *arg ) {  
  2.   //定義執行緒引數:服務端和客戶端地址變數,客戶端服務子執行緒
  3.   struct sockaddr_in addr, client_addr;  
  4.   int on;  
  5.   pthread_t client;  
  6.   socklen_t addr_len = sizeof(struct sockaddr_in);  
  7.   //每個伺服器執行緒的引數
  8.   context *pcontext = arg;  
  9.   pglobal = pcontext->pglobal;  
  10.   /* set cleanup handler to cleanup ressources */
  11.   pthread_cleanup_push(server_cleanup, pcontext);  
  12.   /* 開啟通道服務端執行緒 */
  13.   pcontext->sd = socket(PF_INET, SOCK_STREAM, 0);  
  14.   if ( pcontext->sd < 0 ) {  
  15.     fprintf(stderr, "socket failed\n");  
  16.     exit(EXIT_FAILURE);  
  17.   }  
  18.   //預設情況下,server重啟,呼叫socket,bind,然後listen,會失敗.因為該埠正在被使用.
  19.   //一個埠釋放後會等待兩分鐘之後才能再被使用,SO_REUSEADDR是讓埠釋放後立即就可以被再次使用。
  20.   on = 1;  
  21.   if (setsockopt(pcontext->sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {  
  22.     perror("setsockopt(SO_REUSEADDR) failed");  
  23.     exit(EXIT_FAILURE);  
  24.   }  
  25.   /* 配置伺服器引數,用於監聽 */
  26.   memset(&addr, 0, sizeof(addr));  
  27.   addr.sin_family = AF_INET;  
  28.   addr.sin_port = pcontext->conf.port; /* is already in right byteorder */
  29.   //巨集INADDR_ANY代替本機的IP,無需手動選擇,自動替換,當存在多個網絡卡情況下,自動選擇
  30.   addr.sin_addr.s_addr = htonl(INADDR_ANY);  
  31.   if ( bind(pcontext->sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) {  
  32.     perror("bind");  
  33.     OPRINT("%s(): bind(%d) failed", __FUNCTION__, htons(pcontext->conf.port));  
  34.     closelog();  
  35.     exit(EXIT_FAILURE);  
  36.   }  
  37.   //socket()函式建立的socket預設是一個主動型別的,listen函式將socket變為被動型別的,等待客戶的連線請求。
  38.   if ( listen(pcontext->sd, 10) != 0 ) {  
  39.     fprintf(stderr, "listen failed\n");  
  40.     exit(EXIT_FAILURE);  
  41.   }  
  42.   //等待客戶端連線,有連線則建立新的執行緒進行處理
  43.   while ( !pglobal->stop ) {  
  44.     //cfd結構體定義在httpd.d
  45.     cfd *pcfd = malloc(sizeof(cfd));  
  46.     if (pcfd == NULL) {  
  47.       fprintf(stderr, "failed to allocate (a very small amount of) memory\n");  
  48.       exit(EXIT_FAILURE);  
  49.     }  
  50.     DBG("waiting for clients to connect\n");  
  51.     pcfd->fd = accept(pcontext->sd, (struct sockaddr *)&client_addr, &addr_len);  
  52.     pcfd->pc = pcontext;  
  53.     /* 建立客戶端子執行緒處理連線請求 */
  54.     DBG("create thread to handle client that just established a connection\n");  
  55.     syslog(LOG_INFO, "serving client: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));  
  56.     if( pthread_create(&client, NULL, &client_thread, pcfd) != 0 ) {  
  57.       DBG("could not launch another client thread\n");  
  58.       close(pcfd->fd);  
  59.       free(pcfd);  
  60.       continue;  
  61.     }  
  62.     pthread_detach(client);  
  63.   }  
  64.   DBG("leaving server thread, calling cleanup function now\n");  
  65.   pthread_cleanup_pop(1);  
  66.   return NULL;  
  67. }  

其中,用於儲存客戶端子執行緒引數的結構體定義如下:
  1. typedefstruct {  
  2.   context *pc;  //保留父執行緒的變數
  3.   int fd;       //儲存監聽accept返回的套接字描述符
  4. } cfd;  

每個連線請求的處理都是在void *client_thread( void *arg )中進行。函式首先對連線請求的引數進行解析配置,根據req.type確定傳送資料的方式:
  1. /* thread for clients that connected to this server */
  2. void *client_thread( void *arg ) {  
  3.   int cnt;  
  4.   char buffer[BUFFER_SIZE]={0}, *pb=buffer;  
  5.   iobuffer iobuf;  
  6.   request req;  
  7.   //本地連線請求檔案描述變數
  8.   cfd lcfd;            
  9.   //如果傳入引數不為空,則將引數的內容拷貝到 lcfd 中(引數為 pcfd ,不為空)
  10. 相關推薦

    MJPG-streamer原始碼分析-輸出部分

    MJPG-streamer可以建立多個輸出,簡單的說,根據主函式中輸入的引數解析的結果,確定輸出通道的個數,至少保證有一個輸出通道在程式執行時存在。從引數解析結果確定每個輸出通道的引數,並以這些引數為每個通道建立傳送執行緒。在每個傳送執行緒上,不斷偵聽是否有連線請求。每當有連

    MJPG-streamer原始碼分析-輸入部分

    MJPG-streamer僅可以建立一個輸入通道,對一個相機進行採集(如需多個,需另行修改)。在進行相機採集開始前,需要對其進行初始化,包括採集過程的全域性變數定義和記憶體開闢。執行過程則是將相機採集得到的資料,根據從相機對映到記憶體的地址上,按照不同的格式要求(YUV或M

    MJPG-Streamer原始碼分析(一)

    -------------------------------------------------------------------------------------------------- 另一片篇推薦的博文:http://www.armbbs.net/foru

    hadoop(2.7.3) 原始碼分析--RPC部分

    hadoop(2.7.3) 原始碼分析–RPC部分 序列化 hadoop 自帶了Writable序列化方法,可序列化的物件需實現 Writable 介面。 Hadoop common下org.apache.hadoop.io 大量的可序列化物件,他們都

    MJPG-streamer原始碼簡析

      MJPG-streamer主體上是由main函式和輸入外掛、輸出外掛組成。   軟體執行的流程是先對攝像頭進行初始化然後設定基本的輸入輸出引數,接著從攝像頭中獲取資料放到全域性記憶體中,然後通知輸出函式來取出,接著輸出。   攝像頭的初始化由結構體vdIn來進行

    mjpg-streamer專案原始碼分析 2

    mjpg-streamer專案原始碼分析   2013-09-09 10:41:05|  分類: GT2440 |舉報|字號 訂閱  mjpg-streamer專案原始碼分析 2012-11-06 10:04 397人閱讀 評論(2) 收藏 舉報         前一段時間

    無線視訊監控Mjpg-streamer輸出分析

    /******************************************************************************* #                                                                       

    mjpg-streamer專案原始碼分析[轉載]

    前一段時間自己買了個開發板(GT2440的),可是我沒有夠相應的買cmos攝像頭,可是又想做下國嵌的usb視訊採集和傳輸的那個專案。沒辦法,只好網上找找相關的專案,最終發現了mjpg-streamer這個開源專案。看了blog們的文章,有種激動,於是自己問同學借了個usb攝

    輸出文件】 Android 加密 模組原始碼分析

                                       Android6.0 加密模組解析

    輸出文件】 Android MountService 原始碼分析

    Android 儲存裝置管理框架 在android之VOLD程序啟動原始碼分析一文中介紹了儲存裝置的管控中心Vold程序,Vold屬於native後臺程序,通過netlink方式接收kernel的uevent訊息,並通過socket方式將uevent訊息傳送給MountService,同時實時接

    SNMP原始碼分析之(一)配置檔案部分

    snmpd.conf想必不陌生。在程序啟動過程中會去讀取配置檔案中各個配置。其中幾個引數需要先知道是幹什麼的:   token:配置檔案的每行的開頭,例如 group MyROGroup v1 readSec 這行token的引數是group。  

    String 部分原始碼分析

    String 無引數建構函式 /** * 底層儲存字串的目標位元組陣列, * Jdk 8 之前都是字元陣列 private final char[] value; */ @Stable private final byte[] value;

    《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》

    《2.uboot和系統移植-第6部分-2.6.uboot原始碼分析2-啟動第二階段》 第一部分、章節目錄 2.6.1.start_armboot函式簡介 2.6.2.start_armboot解析1 2.6.3.記憶體使用排布 2.6.4.start_armboot解析2 2.6.5.s

    《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》

    《2.uboot和系統移植-第5部分-2.5.uboot原始碼分析1-啟動第一階段》 第一部分、章節目錄 2.5.1.start.S引入 2.5.2.start.S解析1 2.5.3.start.S解析2 2.5.4.start.S解析3 2.5.5.start.S解析4 2.5.6.s

    一步步實現windows版ijkplayer系列文章之三——Ijkplayer播放器原始碼分析之音視訊輸出——音訊篇

    一步步實現windows版ijkplayer系列文章之三——Ijkplayer播放器原始碼分析之音視訊輸出——音訊篇 這篇文章的ijkplayer音訊原始碼研究我們還是選擇Android平臺,它的音訊解碼是不支援硬解的,音訊播放使用的API是OpenSL ES或AudioTrack。 OpenSL ES

    php中parse_url函式的原始碼分析(scheme部分)

    前言 看師傅們的文章時發現,parse_url出現的次數較多,單純parse_url解析漏洞的考題也有很多,在此研究一下原始碼(太菜了看不懂,待日後再補充Orz) 原始碼 在ext/standard/url.c檔案中 PHPAPI php_url *php_url_parse_ex(char const

    MapReduce的原始碼分析中map端輸出原始碼分析

    分割槽: 只有一個reduce的情況下,partition號為0 分割槽大有1的情況下,採用hash的方法: 在輸入階段最核心的類是linerecorderReader() 在輸出階段最核心的類是mapoutputbuffer()   達到80%的

    JSK1.8 String類部分原始碼分析

    本文基於JDK1.8中的String類,看原始碼時無意發現String類中幾個比較有意思的地方,特此記錄下。 String類的兩個重要屬性,final的字元陣列和int的hash值,還有序列化相關的兩個欄位,這裡不寫; final char value[]初始化

    Vue provide/inject 部分原始碼分析 實現響應式資料更新

    下面是我自己曾經遇到 一個問題,直接以自己QA的形式來寫吧 官網給出例項,說本身是不支援資料響應式的, 但是可以傳入響應式資料,那麼provide,inject就可以實現響應式。我這裡理解應該沒錯哈,有不對的地方請指出。 我自己寫的demo,做了如下更改 parent 頁面: export def

    subsampling-scale-image-view部分載入bitmap原始碼分析(一)

    subsampling-scale-image-view原始碼分析背景介紹使用原始碼分析總結參考 背景 對於安卓開發人員,最頭疼的問題就是記憶體問題了,而記憶體問題又當屬bitmap最頭疼,雖然說現在市面上已經有越來越多成熟的圖片載入框架,像Fresco,Gli