Project Name: web based remote video transmission
Software platform: Ubuntu
Hardware platform: NanoPi Duo2
Note: there are many hardware platforms, such as mini2440, NanoPi series, raspberry pie, etc.
mjpg-streamerMJPG-streamer
It is an open source video streaming server based on IP address. Its input plug-in reads video data from the camera. This input plug-in generates video data and copies the video data into memory. It has several output plug-ins to process these video data. The most important output plug-in is the website server plug-in, which transmits the video data to the user's browser. Mjpg streamer's job is to bind one input plug-in and multiple output plug-ins together, and all the work is completed through its plug-ins.
Step 1:
Download source code address: https://sourceforge.net/p/mjpg-streamer/code/HEAD/tree/
You can directly download the compressed file of the source package: mjpg-streamer-code-r182 zip
Or download through SVN: svn checkout https://svn.code.sf.net/p/mjpg-streamer/code/ mjpg-streamer-code
Note: svn needs to be installed in the environment first
If you don't want trouble, baidu online disk: link: https://pan.baidu.com/s/1B29YOHRYDkzGpkD0h8HsmA Extraction code: qjpd, get it directly!
To install mjpg streamer, you need to install libjpeg first. The source address of libjpeg https://sourceforge.net/projects/libjpeg/ You can use wget to download directly.
Step 2:
Cross compilation
1. Install cross compilation environment (take NanoPi Duo2 platform as an example)
*Download cross compiler
arm-cortexa9-linux-gnueabihf-4.9.3-20160512.tar.xz
Note: the NanoPi official website provides a download link
*Put it in the opt directory of Ubuntu and unzip it
tar -xf arm-cortexa9-linux-gnueabihf-4.9.3-20160512.tar.xz
*Modify file / root / bashrc,
*Add export PATH=$PATH:/opt/4.9.3/bin at the end
*Make the modified file effective source / root / bashrc
2. Compile libjpeg
*Unzip and enter the directory;
*Configuring the compilation environment
./configure --host=arm-linux- --prefix=/home/jpeg-install
*Compile make
*Install make install
*After installation, you will see the JPEG install directory in the / home directory.
3. Compile mjpg streamer
-
Makefile modify top level
CC=arm-linux-gcc -
Modify the Makefile of the plug-in (plugins/input_uvc/Makefile)
CC = arm linux GCC and dynamic library path $(CC) $(cflags) - L / home / jpeg install - ljpeg - o $@ input_ uvc. so -
Modify the plugin Makefile (plugins/output_http/Makefile)
CC=arm-linux-gcc- Compile make
The final generated files are packaged and copied to the development board. libjpeg needs to be installed, and mjpg streamer does not need to be installed.
Step 3:
function
1. Configure the network to ensure that NanoPi and computer can form a LAN;
ifconfig
wlan0Link encap:Ethernet HWaddr c0:84:7d:a0:27:14 inetaddr:192.168.0.105 Bcast:192.168.0.255 Mask:255.255.255.0inet6addr: fe80::9e9a:8bbc:69f1:24f0/64 Scope:LinkUPBROADCAST RUNNING MULTICAST MTU:1500 Metric:1RXpackets:8731 errors:0 dropped:0 overruns:0 frame:0TXpackets:3727 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RXbytes:6593454 (6.5 MB) TX bytes:502413 (502.4 KB)
The address of the development board is 192.168.0.105, which is very important.
2. Open the browser of the notebook and enter: http://192.168.0.105:8080/ Select Stream
Source code analysis
1. Several important documents
- input_testpicture.so: This is an image test plug-in. It compiles the preset image into a header file, which can transmit the image without a camera, so as to facilitate the debugging of the program.
- input_uvc.so: this file calls the USB camera driver V4L2 to read video data from the camera.
- output_http.so: This is a fully functional web server. It can not only process files from a single folder, but also execute certain commands. It can process an image from the input plug-in, or output the video files of the input plug-in in in the form of HTTP video data service stream according to the existing M-JPEG standard.
- input_control.so: this file realizes the control of camera parameters.
- output_file.so: the function of this plug-in is to store the JPEG image of the input plug-in in a specific folder, which can be used to capture the image.
2. Command parsing
intmain(int argc, char *argv[]){char *input = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0"; char *output[MAX_OUTPUT_PLUGINS];//10int daemon=0, i; size_t tmp=0;output[0] = "output_http.so --port 8080"; global.outcnt = 0;global.control = control;/* parameter parsing */while(1) {int option_index = 0, c=0; staticstruct option long_options[] = \ { {"h", no_argument, 0, 0}, {"help", no_argument, 0, 0}, {"i", required_argument, 0, 0}, {"input", required_argument, 0, 0}, {"o", required_argument, 0, 0}, {"output", required_argument, 0, 0}, {"v", no_argument, 0, 0}, {"version", no_argument, 0, 0}, {"b", no_argument, 0, 0}, {"background", no_argument, 0, 0}, {0, 0, 0, 0} };c = getopt_long_only(argc, argv, "", long_options, &option_index);
Here we mainly encounter a function function getopt_long_only(), which is mainly used to parse the command line options, that is, to parse the parameters of - h -i -o -v -b in input. Please refer to getopt for how to use this function_ long_ only.
3. Call input plug-in
/* open input plugin */ tmp = (size_t)(strchr(input, ' ')-input);global.in.plugin = (tmp > 0)?strndup(input, tmp):strdup(input);global.in.handle = dlopen(global.in.plugin, RTLD_LAZY);if ( !global.in.handle ) { LOG("ERROR: could not find input plugin\n"); LOG(" Perhaps you want to adjust the search path with:\n"); LOG(" # export LD_ LIBRARY_ PATH=/path/to/plugin/folder\n"); LOG(" dlopen: %s\n", dlerror() ); closelog(); exit(EXIT_FAILURE); }global.in.init = dlsym(global.in.handle, "input_init");if ( global.in.init == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global. in. stop = dlsym(global.in.handle, "input_stop"); if ( global.in.stop == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } global. in. run = dlsym(global.in.handle, "input_run"); if ( global.in.run == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); } The dlopen() function is responsible for opening * So plug-in, dlsym() is responsible for calling related functions in the plug-in. When entering/ mjpg_streamer -i "./input_uvc.so" the system will call input_ uvc. Input in so_ Init function to initialize the input device.
4. Call output plug-in
/* open output plugin */for (i=0; i<global.outcnt; i++) { tmp = (size_t)(strchr(output[i], ' ')-output[i]);global.out[i].plugin = (tmp > 0)?strndup(output[i], tmp):strdup(output[i]);global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);if ( !global.out[i].handle ) { LOG("ERROR: could not find output plugin %s\n", global.out[i].plugin); LOG(" Perhaps you want to adjust the search path with:\n"); LOG(" # export LD_LIBRARY_PATH=/path/to/plugin/folder\n"); LOG(" dlopen: %s\n", dlerror() ); closelog(); exit(EXIT_FAILURE); }global.out[i].init = dlsym(global.out[i].handle, "output_init");if ( global.out[i].init == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); }global.out[i].stop = dlsym(global.out[i].handle, "output_stop");if ( global.out[i].stop == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); }if ( global.out[i].stop == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); }global.out[i].run = dlsym(global.out[i].handle, "output_run");if ( global.out[i].run == NULL ) { LOG("%s\n", dlerror()); exit(EXIT_FAILURE); }
5. Input function analysis
intinput_init(input_parameter *param)
This function code implements some settings for the format, frame, request buf, queue buf, etc. of the usb camera,
The main implementation is also in the function
init_videoIn(videoIn, dev, width, height, fps, format, 1) int input_stop(void) { DBG("will cancel input thread\n"); pthread_cancel(cam); return 0; }
Input stop, mainly to cancel the thread.
6. Output function
intoutput_init(output_parameter *param) int output_stop(intid) int output_run(intid)