搞了一段时间的嵌入式网络视频采集与传输,基本上在服务器端采集视频用的是servfox程序包,它可以用arm-linux-gcc编译后移植到开发板上,我现在一直在做这方面的工作,用的是S3C2410的板子,这个开源程序十分不错,觉得有必要深入理解和学习一下它的源程序,网上一般没有对它的详细说明,只是简单介绍了它怎么用的,就如我当初刚开始学习嵌入式时一样看不懂这个程序,现在对其各个代码作个详细的解释,以备后忘,也希望对新手有所帮助。
servfox主要有server.c,spcav4l.c ,spcav4l.h,utils.c ,utils.h,tcputils.c ,tcputils.h,spcaframe.h,Makefile,Makefile.fox这几个文件, arm-linux-gcc编译后可以生成servfox可执行文件,可以移植到ARM板上作为服务器端视频采集程序用。我们从server.c代码开始。
1. server.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <errno.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/fcntl.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <arpa/inet.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <netinet/ether.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
//#include <signal.h>
#include "spcaframe.h"
#include "spcav4l.h"
#include "utils.h"
#include "tcputils.h"
#include "version.h"
static int debug = 0;
void grab (void);
void service (void *ir);
void sigchld_handler(int s);
struct vdIn videoIn;
int main (int argc, char *argv[])
{
char *videodevice = NULL;
int grabmethod = 1;
int format = VIDEO_PALETTE_JPEG;
int width = 352;
int height = 288;
char *separateur;
char *sizestring = NULL;
int i;
int serv_sock,new_sock;
pthread_t w1;
pthread_t server_th;
int sin_size;
unsigned short serverport = 7070;
struct sockaddr_in their_addr;
struct sigaction sa;
int ret;
/*********************************************命令行输入参数设置************************************************/
/********************************************************************************************
命令行参数应该这个样子的:./servfox -g -d /dev/video0 -s 640x480 -w 7070
*********************************************************************************************/
for (i = 1; i < argc; i++)
{
/* skip bad arguments */
if (argv[i] == NULL || *argv[i] == 0 || *argv[i] != '-')
{
continue;
}
if (strcmp (argv[i], "-d") == 0)
/********************************************************************************************
-d 参数用于设置输入视频采集设备。
*********************************************************************************************/
{
if (i + 1 >= argc)
{
if(debug) printf ("No parameter specified with -d, aborting./n");
exit (1);
}
videodevice = strdup (argv[i + 1]);
/********************************************************************************************
函数原型:extern char *strdup(char *str)
参数说明:str待复制的字符串。
所在库名:#include <string.h>
函数功能:实现复制字符串str。
返回说明:返回复制的新字符串的指针,复制失败返回NULL, 所需空间由malloc()分配且可以由free()释放。
strdup()主要是拷贝字符串 str 的一个副本,由函数返回值返回, 函数返回值是一个字符型指针,它指向 字符串 str 副本的地址空间, 这个副本有自己的内存空间, 是由malloc()自动分配的, 和 str 不相干。
执行完这句后, char *videodevice就指向 argv[i + 1]所表示的字符串,即 /dev/video0
与 strcpy 区别:
strdup不是标准的c函数, strcpy是标准的c函数
strdup可以直接把要复制的内容复制给没有初始化的指针,因为它会自动分配空间给目的指针
strcpy的目的指针一定是已经分配内存的指针
strcpy
原型: extern char *strcpy(char *dest,char *src);
用法:#include <string.h>
功能:把src所指由NULL结束的字符串复制到dest所指的数组中。
说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
返回指向dest的指针。
*********************************************************************************************/
}
if (strcmp (argv[i], "-g") == 0)
{
/* Ask for read instead default mmap */
grabmethod = 0;
}
/********************************************************************************************
-g 参数用于读取采集到的视频的方式,用read方式而非mmap方式。
*********************************************************************************************/
if (strcmp (argv[i], "-s") == 0) {
if (i + 1 >= argc) {
if(debug) printf ("No parameter specified with -s, aborting./n");
exit (1);
}
/********************************************************************************************
-s 参数用于设置视频图像帧的大小,如 640x480
*********************************************************************************************/
sizestring = strdup (argv[i + 1]);
width = strtoul (sizestring, &separateur, 10);
/********************************************************************************************
strtoul(将字符串转换成无符号长整型数)
头文件:#include<stdlib.h>
unsigned long strtoul( const char *nptr, char **endptr, int base )
把输入的字符串转换成数字。
参数一 字符串的起始地址。
参数二 返回字符串有效数字的结尾地址。如 123456ff789 则返回数字6的地址。实际上返回的应该是第一个f的地址
参数三 转化基数.
函数说明: strtol ()会将参数nptr字符串根据参数base来转换成长整型数。参数base范围从2至36,或0。参数base代表采用的进制方式 ,如base值为10则采用10进制(字符串以10进制表示) ,若base值为16则采用16进制(字符串以16进制表示)。当base值为0时则是采用10进制做转换,但遇到如''0x''前置字符则会使用16进制做转换。一开始strtol ()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,再遇到非数字或字符串时(''/0'')结束转换,并将结果返回 。若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
返回值: 返回转换后的长整型数,否则返回ERANGE并将错误代码存入errno中。
若参数endptr不为NULL,则会将遇到不合条件而终止的nptr中的字符指针由endptr返回。
参数二 返回字符串有效数字的结尾地址。如 123456ff789 则返回数字6的地址。实际上返回的应该是第一个f的地址
char *sizestring 指向 argv[i + 1]所表示的字符串,即 640x480
strtoul (sizestring, &separateur, 10)中遇到x结束转换,并返回结果 640给width
*********************************************************************************************/
if (*separateur != 'x') {
if(debug) printf ("Error in size use -s widthxheight /n");
exit (1);
} else {
++separateur;
height =strtoul (separateur, &separateur, 10);
if (*separateur != 0)
if(debug) printf ("hmm.. dont like that!! trying this height /n");
if(debug) printf (" size width: %d height: %d /n",
width, height);
}
}
if (strcmp (argv[i], "-w") == 0) {
if (i + 1 >= argc) {
if(debug) printf ("No parameter specified with -w, aborting./n");
exit (1);
}
/********************************************************************************************
-w参数用于设置端口。
*********************************************************************************************/
serverport = (unsigned short) atoi (argv[i + 1]);
if (serverport < 1024 ){
if(debug) printf ("Port should be between 1024 to 65536 set default 7070 !./n");
serverport = 7070;
}
}
if (strcmp (argv[i], "-h") == 0)
{
printf ("usage: cdse [-h -d -g ] /n");
printf ("-h print this message /n");
printf ("-d /dev/videoX use videoX device/n");
printf ("-g use read method for grab instead mmap /n");
printf ("-s widthxheight use specified input size /n");
printf ("-w port server port /n");
exit (0);
}
}
/********************************* main code主程序开始位置*************************************************/
printf(" %s /n",version);
if (videodevice == NULL || *videodevice == 0)
{
videodevice = "/dev/video0";
}
memset (&videoIn, 0, sizeof (struct vdIn));
if (init_videoIn(&videoIn, videodevice, width, height, format,grabmethod) != 0) 跟踪进入 init_videoIn()
if(debug) printf (" damned encore rate !!/n");
/*****************************************************************************************
spcav4l.c中:
int init_videoIn (struct vdIn *vd, char *device, int width, int height,
int format, int grabmethod)
struct vdIn videoIn; //在spcav4l.h中定义
videodevice = "/dev/video0";
int width = 352;
int height = 288;
int format = VIDEO_PALETTE_JPEG;
int grabmethod = 1;
请进入下面的 跟踪进入 init_videoIn() ,对其进 行详细说明,我们来看看它里面做了些什么事情。
***********************************************************************************************/
main()函数还没有完,请看嵌入式网络视频采集源程序servfox解析02