mjpg-streame

来源:互联网 发布:安卓手机怎么清除数据 编辑:程序博客网 时间:2024/05/16 01:30
//--- 摄像头 mjpg-streame, 路由器实验32M内存占用了40%,
如果摄像头支持mjpg格式输出,mjpg-streamer 只需要将得到jpeg格式的图片转发到HTTP服务器就可以了。
但如果摄像头不支持mjpg格式输出,仅支持YUV格式,需要将原始图片压缩成jpeg格式,那就需要消耗大量CPU。
电脑上用何种摄像头的区别不大, 但小路由受到硬件的限制只能应用支持mjpg的摄像头

  添加了常见的中星微zc3xx驱动和UVC支持。您可以获取设备的"Device ID",参考Linux UVC driver查询支援。
http://sourceforge.net/projects/mjpg-streamer
To view the stream use VLC or Firefox and open the URL:
http://192.168.1.1:8080/?action=stream

To view a single JPEG just call:
http://192.168.1.1:8080/?action=snapshot

To compile and start the tool:
# tar xzvf mjpg-streamer.tgz
# cd mjpg-streamer
# make clean all
# export LD_LIBRARY_PATH=.
# ./mjpg_streamer -o "output_http.so -w ./www"
百脑通-D881HD720P,92包邮送一个小包,效果还可以,注意D881有一个是旧版的,不是720P,不要贪便宜买那个。
测试 640x480@25pfs,CPU 用25%左右。1280x720@15pfs,CPU用30%左右。
HD-6000: 640x480@25pfs,CPU 占用36% 左右, 内存占用 69%. 1024x768@20pfs,
微软 LifeCam HD-5000(只有工作时灯才亮), LifeCam HD-3000, 罗技C110可用65~69元

/etc/init.d/mjpg-streame, 一定注意修改文件权限: chmod 777 /etc/init.d/wificar
mjpg_streamer -b -i "input_uvc.so -r 800x600 -f 30"  -o "output_http.so -p 8080 -w /www/camwww"
mjpg-streamer -i "input_uvc.so -d /dev/video0 -r 320x240 -f 30" -o "output_file.so -f /tmp/"
mjpg_streamer -b -i "input_uvc.so -r 640x480 -f 25"  -o "output_http.so -p 8080 -w /www/camwww" -o "output_file.so  -d 3600000 -f /tmp"
可以 同时 网页输出和保存到U盘. 只能保存图片, 要录像用 ffmpeg
-d 参数 是 时间 单位MS, -f  文件夹

mjpg-streamer 是一个很好的开源项目,用来做视频服务器.
这个代码里有三个部分是我们需要掌握的内容,第一是v4l2接口,第二个是socket编程,第三个是多线程编程。
一、 v4l2接口说明
从摄像头中把数据取出来,首先是封装一个结构体用来描述摄像头的一些信息,比如采集图片的宽高,图片的格式,等等。
plugins\input_uvc\v412uvc.h,
接着是把这个结构体写入驱动中,用来初始化摄像头。这个操作通过 ioctl 完成,
涉及到的命令包括VIDIOC_QUERYCAP、VIDIOC_S_FMT、VIDIOC_S_PARM、VIDIOC_REQBUFS,VIDIOC_QUERYBUF,并通过mmap完成内存的映射。
最后我们通过ioct命令完成图片的读取,涉及到的命令包括VIDIOC_QBUF和VIDIOC_DQBUF。
然后把获得的数据写入到文件里就是图片,通过网络传输出去连续的图片就是视频。

二、 socket编程
在这个程序里使用的是tcp套接字,每有一个连接请求就创建一个线程单独和这个请求通信,
函数包括 socket, bind, listen, accept, write,

三、 多线程编程
为了能同时响应多个客户端的请求,这里使用了多线程编程,为每一个请求建立一个连接,每个连接就是一个线程。函数如下:
pthread_create, pthread_detach, pthread_cond_init, pthread_cond_destroy, pthread_mutex_init, pthread_mutex_destroy,
pthread_mutex_lock, pthread_mutex_unlock,

typedef struct _globals globals;
struct _globals {
  int stop;
  /* signal fresh frames */
  pthread_mutex_t db; //pthread_mutex_lock( &pglobal->db );
  pthread_cond_t  db_update; //pthread_cond_broadcast(&pglobal->db_update);

  /* global JPG frame, this is more or less the "database" */
  unsigned char *buf;
  int size; //memcpy(frame, pglobal->buf, frame_size);

  /* input plugin */
  input in;

  /* output plugin */
  output out[MAX_OUTPUT_PLUGINS];
  int outcnt;
};
static globals *pglobal;

global.out[i].run = dlsym(global.out[i].handle, "output_run");
-->output_http.c
int output_run(int id) {
  /* create thread and pass context to thread function */
  pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));
  pthread_detach(servers[id].threadID);

  return 0;
}
-->httpd.c
void *server_thread( void *arg ) {
if( pthread_create(&client, NULL, &client_thread, pcfd) != 0 ) {
    DBG("could not launch another client thread\n");
    close(pcfd->fd);
    free(pcfd);
    continue;
  }
  pthread_detach(client);

void *client_thread( void *arg ) {
if ( strstr(buffer, "GET /?action=snapshot") != NULL ) {
    req.type = A_SNAPSHOT;
  }
  else if ( strstr(buffer, "GET /?action=stream") != NULL ) {
    req.type = A_STREAM;
  }
  else if ( strstr(buffer, "GET /?action=command") != NULL ) {

---> Send a complete HTTP response and a stream of JPG-frames. 参考此文件, FF下有点差异
sending the content-length fixes random stream disruption observed with firefox
void send_stream(int fd) {
/* wait for fresh frames */
    pthread_cond_wait(&pglobal->db_update, &pglobal->db);
    /* read buffer */
    frame_size = pglobal->size;
    memcpy(frame, pglobal->buf, frame_size);
    pthread_mutex_unlock( &pglobal->db );

Client A private IP 192.168.1.192 port 4005 public IP 123.45.67.1 port 45667
Client B private IP 192.168.1.100 port 7005 public IP 143.45.67.1 port 35667
now the question is how to start connection between these two client

1. shall I create a server socket in ClientA which will listen to port 4005
and in Client B at port 7005 so that each can recive message at 4005 and 7005 respectively.
so when ClientB send some data to ClientB at public IP 123.45.67.1 port 45667
it will arrive at 4005 port at clientA and vice varsa.

please confirm my understanding.if it is wrong way to do this then pease
suggest correct way to implement UDP hole punching.

---> 相关 file 操作: if( ...)
--- input_file.c:
void *worker_thread( void *arg ) {
  /* set cleanup handler to cleanup allocated ressources */
  pthread_cleanup_push(worker_cleanup, NULL);

  while( !pglobal->stop ) {
   /* grab a frame, ???? */
   if( ... ) {
      fprintf(stderr, "Error grabbing frames\n");
      exit(EXIT_FAILURE);
    }

    /* copy JPG picture to global buffer */
    pthread_mutex_lock( &pglobal->db );
    pglobal->size = memcpy_picture(pglobal->buf, videoIn->tmpbuffer, videoIn->buf.bytesused);

    /* signal fresh_frame */
    pthread_cond_broadcast(&pglobal->db_update);
    pthread_mutex_unlock( &pglobal->db );

    usleep(1000*delay);
  }
  pthread_cleanup_pop(1);
}

--- output_file.c: 每个文件线程, 分配了512K
int output_run(int id) {
  pthread_create(&worker, 0, worker_thread, NULL);
  pthread_detach(worker);
  return 0;
}

void *worker_thread( void *arg ) {
  if ( (frame = malloc(512*1024)) == NULL ) {
    OPRINT("not enough memory for worker thread\n");
    exit(EXIT_FAILURE);
  }

---> main.c:
/* this mutex and the conditional variable are used to synchronize access to the global picture buffer */
  if( pthread_mutex_init(&global.db, NULL) != 0 ) {
    LOG("could not initialize mutex variable\n");
    closelog();
    exit(EXIT_FAILURE);
  }
  if( pthread_cond_init(&global.db_update, NULL) != 0 ) {
    LOG("could not initialize condition variable\n");
    closelog();
    exit(EXIT_FAILURE);
  }

  /* ignore SIGPIPE (send by OS if transmitting to closed TCP sockets) */
  signal(SIGPIPE, SIG_IGN);

  /* register signal handler for <CTRL>+C in order to clean up */
  if (signal(SIGINT, signal_handler) == SIG_ERR) {
    LOG("could not register signal handler\n");
    closelog();
    exit(EXIT_FAILURE);
  }
原创粉丝点击