手把手教你ubuntu下移植MJPG-streamer

語言: CN / TW / HK

彭老師在錄製的《物聯網綜合專案實戰》課程中,在web頁面中加入了實時監控攝像頭的功能,特地整理了一篇如何移植視訊流伺服器的文章,供大家學習。

一、嵌入式視訊影象開源庫

在嵌入式系統中,常用的視訊影象處理開源系統有:luvcview、cheese、motion、mjpg-streamer或者ffmpeg,其中:

• luvcview: 基於V4L2、SDL的程式,支援拍照錄像,引數調節,程式碼精簡實用,適合學習V4L2程式設計

• cheese:基於V4L2、GTK的程式,支援拍照錄像,特殊視訊效果

• motion:移動偵測拍照程式

• mjpg-streamer:網路攝像機程式

二、mjpg-streamer簡介

MJPG-streamer是一個優秀的開源project,它可以通過HTTP的方式訪問linux上面的相容攝像頭,從而做到遠端視訊傳輸的效果。

MJPG-streamer從webcam攝像頭採集影象,把他們以流的形式通過基於ip的網路傳輸到瀏覽器如Firehox,Cambozola,VLC播放器,Windows的移動裝置或者其他擁有瀏覽器的移動裝置。

它可以利用某些webcams的硬體壓縮功能來降低伺服器CPU的開銷。

它為嵌入式裝置和一些常規伺服器提供了一個輕量且更少CPU消耗的方案,因為它無需為視訊幀壓縮浪費大量的計算效率。

三、測試攝像頭

1)按上圖的方式將羅技攝像頭連線入虛擬機器

2)下載安裝cheese 檢測攝像頭是否能夠正常工作

$ sudo apt-get update
$ sudo apt-get install cheese

ubuntu 16.04已經自帶該程式

3)測試

攝像頭連線後會生成以下裝置檔案

root@ubuntu:/home/peng/work# ls /dev/video0 -l
crw-rw----+ 1 root video 81, 0 Mar 25 07:18 /dev/video0

執行

root@ubuntu:/home/peng/work# cheese 

四、移植

安裝準備:

1. 安裝前準備

sudo apt-get install libsdl1.2-dev subversion
sudo apt-get install libjpeg62-dev
sudo apt-get install imagemagick

2. 下載mjpeg-streamer

git clone https://github.com/shrkey/mjpg-streamer

如果沒有安裝git,執行以下命令

sudo apt-get install git

3. 編譯安裝

cd mjpg-streamer/mjpg-streamer
root@ubuntu:/home/peng/work/camera/mjpg-streamer# tree -L 1 ./
./
├── doc
├── mjpeg-client #分別有 linux和windows 的客戶端
├── mjpg-streamer #目錄下提供了 的執行程式和各個輸入輸出裝置元件
├── mjpg-streamer-experimental
├── mjpg-streamer.tar.gz
├── README.md
├── udp_client
└── uvc-streamer #目錄下提供了 uvc-streamer的可執行目錄

6 directories, 2 files

ps:重新編譯前,需要執行

make
sudo make install
root@ubuntu:/home/peng/work/camera/mjpg-streamer/mjpg-streamer# make install
install --mode=755 mjpg_streamer /usr/local/bin
install --mode=644 input_uvc.so output_file.so output_udp.so output_http.so input_testpicture.so input_file.so /usr/local/lib/
install --mode=755 -d /usr/local/www
install --mode=644 -D www/* /usr/local/www

編譯生成的庫檔案功能

(1)input_testpicture.so。這是一個影象測試外掛,它將預設好的影象編譯成一個頭檔案,可以在沒有攝像頭的情況下傳輸影象,從而方便除錯程式。

(2)input_uvc.so。此檔案呼叫USB攝像頭驅動程式V4L2,從攝像頭讀取視訊資料。

(3)input_control.so。這個檔案實現對攝像頭轉動的控制介面。

(4)output_http.so。這是一個功能齊全的網站伺服器,它不僅可以從單一資料夾中處理檔案,還可以執行一定的命令,它可以從輸入外掛中處理一幅影象,也可以將輸入外掛的視訊檔案根據現有M-JPEG標準以HTTP視訊資料服務流形式輸出。

(5)output_file.so。這個外掛的功能是將輸入外掛的JPEG影象儲存到特定的資料夾下,它可以用來抓取影象。

4 修改指令碼

修改指令碼檔案

/home/peng/work/camera/mjpg-streamer/mjpg-streamer/start.sh
./mjpg_streamer -i "./input_uvc.so -y" -o "./output_http.so -w ./www" -o "./output_file.so -f /www/pice -d 15000"
"./input_uvc.so -y" :指定攝像頭是YUV,預設是JPEG,一口君使用的羅技攝像頭是YUV
"./output_http.so -w ./www" :指定web伺服器根目錄./www,我們可以通過瀏覽器測試攝像頭
"./output_file.so -f /www/pice -d 15000" : 指定拍照儲存照片目錄/www/pice,並且每15s儲存一次照片

也可以指定解析度:

./mjpg_streamer -i "input_uvc.so -d /dev/video0 -n -y -r 640x480 -f 30" -o "./output_http.so -w ./www" -o "./output_file.so -f /www/pice -d 15000"

市面上有的攝像頭支援格式有YUV,MJPEG,H264 ;mjpg-stream支援MJPEG和YUV兩種格式

5 測試

執行

./start.sh

(1)網頁測試

(2)網頁視訊流測試

(3) 拍照功能實現

瀏覽器上位址列輸入如下內容:

http://127.0.0.1:8080/?action=snapshot

或者

http://127.0.0.1:8080/?action=stream

snapshot 表示每次抓拍一張圖形顯示在網頁上,stream 表示視訊流也就是連續的圖 像

6. 補充

一口君還使用了一款z-star攝像頭,該款攝像頭不要新增 -y 選項

./mjpg_streamer -i "./input_uvc.so  -d /dev/video0" -o "./output_http.so -w ./www" -o "./output_file.so -f /www/pice -d 150000"

有時候攝像頭生成的裝置檔案不是 /dev/video0

我們需要對應引數:

-d /dev/video1

五、程式碼流程

六、支援單拍、連拍

由於 mjpg_stream 中 output-file.so 能實現連續拍照的功能,不能實現單拍或 連拍幾張的功能所以需要對 output_file 原碼進行修改。修改檔案目錄:

peng@ubuntu:~/work/camera/mjpg-streamer/mjpg-streamer/plugins/output_file/output_file.c
  1. 在 196 行 函式 void worker_thread(void arg) 體中加入以下程式碼:

char buf[10];   //用於存放從管道讀取的命令
int flags = 0; //拍照標誌,1:表示11張照片,2:表示1張照片
int fd_com = 0; //開啟管道的檔案描述符

int stop_num = 0; //拍照計數

if ( access("/tmp/webcom",F_OK) < 0 ) //建立有名管道用於接收拍照命令
{
if ( mkfifo("/tmp/webcom",0666 ) < 0)
{
printf("photo fifo create failed\n");
}
}

fd_com = open ("/tmp/webcom",O_RDONLY,0666);
if (fd_com < 0)
{
perror ("open the file webcom error");
}
  1. 在229行 while( ok >= 0 && !pglobal->stop){ 後加入
if (flags == 0)
{
while(1)
{
read(fd_com,buf,sizeof(buf));
if (strncmp(buf,"danger",6) == 0) //拍11張照片
{
flags = 1;
bzero(buf,sizeof(buf));
break;
}

if (strncmp(buf,"one",3) == 0) //拍1張照片
{
flags = 2;
bzero(buf,sizeof(buf));
break;
}
}
}
  1. 在355行

355         /* if specified, wait now */
356 if(delay > 0) {
357 usleep(1000 * delay);
358 }

後加入

  stop_num++;
if (flags == 1) //判斷拍照的數量
{
if ( stop_num > 9)
{
stop_num = 0;
flags = 0;
}
}
else if (flags == 2)
{
stop_num = 0;
flags = 0;
}