Mjpeg-streamer源码学习笔记-Main-get_long(一)

来源:互联网 发布:java项目源代码下载 编辑:程序博客网 时间:2024/05/29 16:51

目标文件:mjpg-stream/mjpg-stream.c

这一篇的主要难点是get_long(),get_long_only().

新手写,有不对的请大神指正,鼓励。

本人参考文章:

http://www.360doc.com/content/13/0913/13/13876325_314174121.shtml

http://blog.sina.com.cn/s/blog_a31ff26901013l22.html


---------------------------------------------------------------------------------------------------------------------------

一:直接对主函数main分析。遇到具体情况,具体往下延伸。

//char *input  = "input_uvc.so --resolution 640x480 --fps 5 --device /dev/video0";
    char *input[MAX_INPUT_PLUGINS];/* 这里MAX_INPUT_PLUGINS在.h文件中定为10 */
    char *output[MAX_OUTPUT_PLUGINS]; /* 一个输入最大可以对应10个输出 */
    int daemon = 0, i;/* 守护进程变量,守护进程作为重点,下一篇详述 */
    size_t tmp = 0;
    output[0] = "output_http.so --port 8080";/* 默认情况下将video0作为输入,http的8080端口作为输出 */
    global.outcnt = 0;/* 代码中 参数行操作的计数 */

---------------------------------------------------------------------------------------------------------------------------

二:下面是一个while()循环,来解析main()函数后面所带的参数

/* parameter parsing */
 while(1) {
 int option_index = 0, c=0;
 static struct option long_options[] = \ /* 长选项表,进行长选项的比对 */
 {
  {"h", no_argument, 0, 0},  /* 第一个参数为选项名,前面没有短横线。譬如"help"、"verbose"之类 */
  {"help", no_argument, 0, 0},  /* 第二个参数描述了选项是否有选项参数 |no_argument 0 选项没有参数|required_argument 1 选项需要参数|optional_argument 2 选项参数可选|*/
  {"i", required_argument, 0, 0},  /* 第三个参数指明长选项如何返回,如果flag为NULL,则getopt_long返回val。
  {"input", required_argument, 0, 0},  * 否则返回0,flag指向一个值为val的变量。如果该长选项没有发现,flag保持不变.
  {"o", required_argument, 0, 0},   */
  {"output", required_argument, 0, 0}, /* 第四个参数是发现了长选项时的返回值,或者flag不是NULL时载入*flag中的值 */
  {"v", no_argument, 0, 0},
  {"version", no_argument, 0, 0},
  {"b", no_argument, 0, 0},  /* 每个长选项在长选项表中都有一个单独条目,该条目里需要填入正确的数值。数组中最后的元素的值应该全是0。
  {"background", no_argument, 0, 0},  *数组不需要排序,getopt_long()会进行线性搜索。但是,根据长名字来排序会使程序员读起来更容易. 
  {0, 0, 0, 0}     */     
 };

---------------------------------------------------------------------------------------------------------------------------

三:接下来是getopt_long_only函数,重点分析

---------------------------------------------------------------------------------------------------------------------------

c = getopt_long_only(argc, argv, "", long_options, &option_index);


由于和后面的代码有关,故一起分析。

---------------------------------------------------------------------------------------------------------------------------

/* no more options to parse */
 if (c == -1) break;
 /* unrecognized option */
 if(c=='?'){ help(argv[0]); return 0; }
 switch (option_index) {
 /* h, help */
 case 0:
 case 1:
 help(argv[0]);
 return 0;
 break;
 /* i, input */
 case 2:
 case 3:
 input = strdup(optarg);
 break;
 /* o, output */
 case 4:
 case 5:
 output[global.outcnt++] = strdup(optarg);
 break;
 /* v, version */
 case 6:
 case 7:
 printf("MJPG Streamer Version: %s\n" \
 "Compilation Date.....: %s\n" \
 "Compilation Time.....: %s\n", SOURCE_VERSION, __DATE__, __TIME__);
 return 0;
 break;
 /* b, background */
 case 8:
 case 9:
 daemon=1;
 break;
 default:
 help(argv[0]);
 return 0;
 }
 }

---------------------------------------------------------------------------------------------------------------------------

代码里出现了help自定义函数,一个switch选择结构,c为flag作为判断标准,同时c也是getopt_long_only的返回值。

strdup函数,变量optarg,变量daemon


3.1 get_long_only函数:


#include <getopt.h>

int getopt_long_only(int argc, char * const argv[],

                     const char *optstring, const struct option *longopts, int *longindex);


说明:函数中的参数说明

~1+2 argc和argv通常直接从main()到两个参数传递而来

~3 optsting是选项参数组成的字符串,如果该字符串里任一字母后有冒号,那么这个选项就要求有参数

~4 下一个参数是指向数组的指针,这个数组是option结构数组,option结构称为长选项表,

其声明如下:

struct option 

{

     const char *name;

     int has_arg;

     int *flag;

     int val;

};

_1 const char *name:选项名,前面没有短横线。譬如"help"、"verbose"之类。

_2 int has_arg:描述长选项是否有选项参数,如果有,是哪种类型的参数,其值见下表:

----------------------------------------------------

符号常量             数值            含义

no_argument            0            选项没有参数

required_argument      1            选项需要参数

optional_argument      2            选项参数是可选的

----------------------------------------------------

_3 int *flag:

如果该指针为NULL,那么getopt_long返回val字段的值;

如果该指针不为NULL,那么会使得它所指向的结构填入val字段的值,同时getopt_long返回0

_4 int val:

如果flag是NULL,那么val通常是个字符常量,如果短选项和长选项一致,那么该字符就应该与optstring中出现的这个选项的参数相同;


~5 :longindex参数一般赋为NULL即可;如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。


对于options类型参数可以有两种方式:

1)短选项(short options):顾名思义,就是短小参数。它们通常包含一个连字号和一个字母(大写或小写字母)。例如:-s,-h等。

2)长选项(long options):长选项,包含了两个连字号和一些大小写字母组成的单词。例如,--size,--help等。


记住几种常见返回值:

(a)每次调用该函数,它都会分析一个选项,并且返回它的短选项,如果分析完毕,即已经没有选项了,则会返回-1

(b)如果getopt_long_only或者getopt_long在分析选项时,遇到一个没有定义过的选项,则返回值为‘?’,此时,程序员可以打印出所定义命令行的使用信息给用户。

(c)当处理一个带参数的选项时,全局变量optarg会指向它的参数

(d)当函数分析完所有参数时,全局变量optind(into argv)会指向第一个‘非选项’的位置


讲了这么多。还是抽象。上实例解析getopt:

(getopt_long和getopt_long_only类似,以getlong讲解,参数是一样的)

实例1:

#include <stdio.h>

#include <getopt.h>

char *l_opt_arg;

char* const short_options = "nbl:";

struct option long_options[] = {

{ "name",     0,   NULL,    'n'     },

{ "bf_name",  0,   NULL,    'b'     },

{ "love",     1,   NULL,    'l'     },

     0,     0,     0,     0},

};

int main(int argc, char *argv[])

{

int c;

while((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1)

{

switch (c)

{

case 'n':

printf("My name is XL.\n");

break;

case 'b':

printf("His name is ST.\n");

break;

case 'l':

l_opt_arg = optarg;

printf("Our love is %s!\n", l_opt_arg);

break;

}

}

return 0;

}

编译并运行:

[root@localhost liuxltest]# gcc -o getopt getopt.c

[root@localhost liuxltest]# ./getopt -n -b -l forever

My name is XL.

His name is ST.

Our love is forever!

[root@localhost liuxltest]#

[root@localhost liuxltest]# ./getopt -nb -l forever

My name is XL.

His name is ST.

Our love is forever!

[root@localhost liuxltest]# ./getopt -nbl forever

My name is XL.

His name is ST.

Our love is forever!


编译结果分析:

char* const short_options = "nbl:";这一句,表示nb不需要参数,l需要参数  

无冒号不要参数,一个冒号需要参数,两个冒号参数可选.

所以forever是-l命令的参数.故需要char *l_opt_arg;

如我有一个程序,有两个选项,-a, -b, 我输入的时候是  ./a.out  -a -b, 那么中间会处理成这种 ab这种字符串的形式

这就解释了-nbl的输入

现在对于处理命令行参数的操作是不是明朗一点了。


再看个例子

实例2:

#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
char *l_opt_arg;
const char* const short_options = "myl:";
struct option long_options[] = {
        { "name",      0,   NULL,    'm'}, //长选项对应的短选项参数, 第二个0表示选项后面无参数, 1为有参数,2为可选
        { "yourname",  0,   NULL,    'y'},
        { "love",      1,   NULL,    'l'},
        {      0,      0,      0,     0},
};
int main(int argc, char *argv[])
{
        int c, i;

        printf("before:\n");
        for (i = 1; i < argc; i++)
                printf("arg:%d\r\targv:%s\n", i, argv[i]);
        printf("\n");

        while((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1)
        {
               
                switch (c)
                {
                        case 'm':
                                printf("My name is A.\n");
                                break;
                        case 'y':
                                printf("His name is B.\n");
                                break;
                        case 'l':
                                l_opt_arg = optarg;
                                printf("Our love is %s!\n", l_opt_arg);
                                break;
                }
        }
        printf("optind:%d\n", optind);

        printf("\nafter:\n");
        for (i=1; i<argc; i++)
                printf("arg:%d\r\targv:%s\n", i, argv[i]);
        printf("................................\n");
        for (i = optind; i < argc; i++)
                printf("arg:%d\rargv:%s\n",i,argv[i]);
        return 0;
}

注意,此程序可接收的的选项有三个, 一个是m ,不带参数, y 不带参数, l  要求有参数。

那如果-m 不带参数,如果我写了参数,会怎么样呢?下面看测试

第一种:

$ ./a.out -m -y
before:
arg:1 : -m
arg:2 : -y
My name is A.
His name is B.
optind:3
after:
arg:1 : -m
arg:2 : -y

第二种:

$ ./a.out -m -y -l banana
before:
arg:1 : -m
arg:2 : -y
arg:3 : -l
arg:4 : banana
My name is A.
His name is B.
Our love is banana!
optind:5
after:
arg:1 : -m
arg:2 : -y
arg:3 : -l
arg:4 : banana

第三种

$./a.out -m lisi -y zhangsan  -l banana  aaa
before:
arg:1 : -m
arg:2 : lisi
arg:3 : -y
arg:4 : zhangsan
arg:5 : -l
arg:6 : banana
arg:7 : aaa
My name is A.
His name is B.
Our love is banana!
optind:5
after:
arg:1 : -m
arg:2 : -y
arg:3 : -l
arg:4 : banana
arg:5 : lisi
arg:6 : zhangsan
arg:7 : aaa

第三种里:注意 argv 里面值的顺序已经和原来不一样了,对命令行的参数重新组织了一下顺序,也就是不认识的命令行参数,都放在了argv的最后,其中 optind 指向了这些没有被解释的参数的第一个。

optind有作用吧!因为5,6,7不通过解析。


附:如果是长参数,则使用 --, 如 --help, 因为 -help时,(选项不需要参数的情况) 会把它当成 四个选项, -h -e -l -p. 所以使用长参数时,要用两个 横线 --。


小结末尾。提一下optind,optarg,opterr三个参数.

optind:每当解析完一个argv,optind就会递增

optarg:如果选项带参数,参数保存在optarg中。如果选项带可选参数,而实际无参数时,optarg为NULL

opterr:当解析错误时,如果opterr为1则自动打印一条错误消息(默认),否则不打印。

这里是getopt.h中的注释。有兴趣的自己翻译翻译哦.

/* For communication from `getopt' to the caller.   When `getopt' finds an option that takes an argument,   the argument value is returned here.   Also, when `ordering' is RETURN_IN_ORDER,   each non-option ARGV-element is returned here.  */extern char *optarg;/* Index in ARGV of the next element to be scanned.   This is used for communication to and from the caller   and for communication between successive calls to `getopt'.   On entry to `getopt', zero means this is the first call; initialize.   When `getopt' returns EOF, this is the index of the first of the   non-option elements that the caller should itself scan.   Otherwise, `optind' communicates from one call to the next   how much of ARGV has been scanned so far.  */extern int optind;/* Callers store zero here to inhibit the error message `getopt' prints   for unrecognized options.  */extern int opterr;


3.2 Help函数

就在这个c文件里.代码就不全贴了。就只贴一部分

---------------------------------------------------------------------------------------------------------------------------

fprintf(stderr, "Usage: %s\n" \
            "  -i | --input \"<input-plugin.so> [parameters]\"\n" \
            "  -o | --output \"<output-plugin.so> [parameters]\"\n" \
            " [-h | --help ]........: display this help\n" \
            " [-v | --version ].....: display version information\n" \
            " [-b | --background]...: fork to the background, daemon mode\n", progname);

---------------------------------------------------------------------------------------------------------------------------

大家应该很熟悉的格式,就是LINUX的help结构  一个命令的延伸,如ls -l ,ls -c,ls -u等等。

总之,他就是一个Switch帮助函数。


3.3 strdup函数

 /* i, input */
 case 2:
 case 3:
 input = strdup(optarg);
 break;

没见过strdup这个函数。故度娘之~

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

extern char *strdup(char *s);

头文件:string.h

功  能: 将串拷贝到新建的位置处

strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。

返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------


既然提到malloc,那就比较一下,而且都是和free()搭配使用的。


---------------------------------------------------------------------------------------------------------------------------------------------------------------------

extern void *malloc(unsigned int num_bytes);

如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------

可以看到区别,前者返回字符串,后者返回的是一个空指针,故malloc范围比strdup大。同时malloc中运用类型强制转换

也是家常便饭。

OK,继续下一段代码解析。


四:系统记录相关

--------------------------------------------------------------------------------------------------------------------

    openlog("MJPG-streamer ", LOG_PID | LOG_CONS, LOG_USER);
//openlog("MJPG-streamer ", LOG_PID|LOG_CONS|LOG_PERROR, LOG_USER);
    syslog(LOG_INFO, "starting application");

--------------------------------------------------------------------------------------------------------------------

2次度娘后。

--------------------------------------------------------------------------------------------------------------------

功能:记录至系统记录。
头文件:#include <syslog.h>
语法: int syslog(int priority, string message);
返回值: 整数

LOG_INFO:情报信息,正常的系统消息,比如骚扰报告,带宽数据等,不需要处理。
--------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------

功能:用于打开系统记录。
头文件:#include <syslog.h>
语法: void openlog(const char *ident, int option, int facility);

LOG_CONS
直接写入系统控制台,如果有一个错误,同时发送到系统日志记录。

LOG_PID
包括每个消息的PID。

LOG_USER (default)
generic user-level messages

--------------------------------------------------------------------------------------------------------------------

更多可能参数值,度娘~

由于接下来的代码和守护进程有关,而本人又不熟悉。故在下一篇中详细学习以及总结。