J2440 mjpg-streamer学习笔记8------输出通道--相关函数

1、_readline函数

对于_readline函数,有一个容器iobuf->buffer,iobuf还有一个level,第一次运行while语句的时候,在没有执行read函数前,我们的iobuf->level等于0.然后执行read函数从客户端读取一串数据,假设客户端发送的是abcd\n一共5个字节,读到的数据是存到iobuf->buffer,容器里依次存放a、b、c、d、\n这五个字节,read函数返回值是指读取到的字节数,iobuf->level为5


/* read just a single line or timeout */
int _readline(int fd, iobuffer *iobuf, void *buffer, size_t len, int timeout)
{
char c='\0', *out=buffer;
int i;


/*
_readline:
从iobuf.buf[]中一个字节一个字节的将数据取出存放到buffer中,直到遇见
换行符号'\n'或者长度达到了.
*/
iobuf.buf[]里面的数据是从哪里来的呢?

看_read函数

memset(buffer, 0, len);


for ( i=0; i<len && c != '\n'; i++ )
{
if ( _read(fd, iobuf, &c, 1, timeout) <= 0 )
{
/* timeout or error occured */
return -1;
}
*out++ = c;
}


return i;
}

2、_read函数

关键调用socket编程的read函数

int _read(int fd, iobuffer *iobuf, void *buffer, size_t len, int timeout)
{
int copied=0, rc, i;  
fd_set fds;
struct timeval tv;


memset(buffer, 0, len);

size_t是标准C库中定义的,应为unsigned int,在64位系统中为 long unsigned int。

len是1
while ( (copied < len) )  //copied变量表示已经读了多少字节的数据
{
i = MIN(iobuf->level, len-copied);// 第一次,i=0(iobuf->level初始化为0,len-copied,其中len为1,copied为0),以后,i=1  

                 i=0相当于下面的不拷贝
memcpy(buffer+copied, iobuf->buffer+IO_BUFFER-iobuf->level, i);


iobuf->level -= i;  自减操作
copied += i;          自加操作
if ( copied >= len )   不返回
return copied;


/* select will return in case of timeout or new data arrived */
/*
当客服端发有数据或者超时的时候,select函数就返回,目的防止while循环永不退出
*/
tv.tv_sec = timeout;
tv.tv_usec = 0;
FD_ZERO(&fds);
FD_SET(fd, &fds);
if ( (rc = select(fd+1, &fds, NULL, NULL, &tv)) <= 0 )
{
if ( rc < 0)
exit(EXIT_FAILURE);


/* this must be a timeout */
return copied;
}


init_iobuffer(iobuf);// 将 iobuf 清0


/*
* there should be at least one byte, because select signalled it.
* But: It may happen (very seldomly), that the socket gets closed remotly between
* the select() and the following read. That is the reason for not relying
* on reading at least one byte.
*/
/*
调用read函数,从客服端读取最多 256 字节的数据,存放到iobuf->buffer,
*/
if ( (iobuf->level = read(fd, &iobuf->buffer, IO_BUFFER)) <= 0 )
{
/* an error occured */
return -1;
}

IO_BUFFER是256
/* align data to the end of the buffer if less than IO_BUFFER bytes were read */
/* 拷贝iobuf->buffer中的数据到地址iobuf->buffer+(IO_BUFFER-iobuf->level) */
memmove(iobuf->buffer+(IO_BUFFER-iobuf->level), iobuf->buffer, iobuf->level);
}


return 0;
}

3、send_snapshot函数

void send_snapshot(int fd)
{
unsigned char *frame=NULL;
int frame_size=0;
char buffer[BUFFER_SIZE] = {0};


/* wait for a fresh frame */
/* 等待输入通道input_uvc.c里发送数据更新请求 */

输入通道:摄像头源源不断采集数据,每采集完一帧数据就会往仓库里存放数据,存放好后,发出一个数据更新信号(通过phread_cond_broadcast函数)
pthread_cond_wait(&pglobal->db_update, &pglobal->db);


/* read buffer */
frame_size = pglobal->size; // 得到一帧数据的大小


/* allocate a buffer for this single frame */
/* 根据一帧数据的大小,分配一个 frame 缓冲区 */
if ( (frame = malloc(frame_size+1)) == NULL )
{
free(frame);
pthread_mutex_unlock( &pglobal->db );
send_error(fd, 500, "not enough memory");
return;
}


/* 从仓库(pglobal->buf)中取出数据 */
memcpy(frame, pglobal->buf, frame_size);
DBG("got frame (size: %d kB)\n", frame_size/1024);


pthread_mutex_unlock( &pglobal->db );

buffer的字符串为HTTP/1.0 200 OK\r\n" STD_HEADER \"Content-type: image/jpeg 
HTTP/1.0 表明http协议所用版本1.0
/* write the response */
/* 让buffer = "" */
sprintf(buffer, "HTTP/1.0 200 OK\r\n" \
 STD_HEADER \
 "Content-type: image/jpeg\r\n" \
 "\r\n");


/* send header and image now */
/* 将buffer中的字符串发送给客服端 */

对于mjpeg-streamer,输出通道是通过socket编程来模拟http协议,而对于我们的http协议来说,它需要先让客户端发送一个请求,当服务器收到这个请求以后,接下来会发送应答,首先会发送一个头部信息,http应答中的头部信息buffer(报文),会报告http协议所用的版本,
if( write(fd, buffer, strlen(buffer)) < 0 )
{
free(frame);
return;
}
write(fd, frame, frame_size); // 将一帧图片给发送出去


free(frame); // 释放缓冲区
}

4、 send_stream函数

void send_stream(int fd)
{
unsigned char *frame=NULL, *tmp=NULL;
int frame_size=0, max_frame_size=0;
char buffer[BUFFER_SIZE] = {0};


DBG("preparing header\n");


/* 让buffer = "" */
sprintf(buffer, "HTTP/1.0 200 OK\r\n" \
STD_HEADER \
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" \
"\r\n" \
"--" BOUNDARY "\r\n");


/* 将 buffer 中的字符串发送出去(报文) */
if ( write(fd, buffer, strlen(buffer)) < 0 )
{
free(frame);
return;
}


DBG("Headers send, sending stream now\n");
循环发送图片形成视频流
进入循环,pglobal->stop为1时终止,按ctrl+c时pglobal->stop为1
while ( !pglobal->stop )
{


/* wait for fresh frames */
/* 等待输入通道发出数据更新的信号 */
pthread_cond_wait(&pglobal->db_update, &pglobal->db);


/* read buffer */
frame_size = pglobal->size; // 得到一帧图片的大小


/* check if framebuffer is large enough, increase it if necessary */
/* 检查我们之前分配的缓存是否够大,如果不够,则重新分配 */
if ( frame_size > max_frame_size )
{
DBG("increasing buffer size to %d\n", frame_size);


max_frame_size = frame_size+TEN_K;
if ( (tmp = realloc(frame, max_frame_size)) == NULL ) // 重新分配缓存
{
free(frame);
pthread_mutex_unlock( &pglobal->db );
send_error(fd, 500, "not enough memory");
return;
}


frame = tmp;
}


/* 从仓库中取出一帧数据 */
memcpy(frame, pglobal->buf, frame_size);
DBG("got frame (size: %d kB)\n", frame_size/1024);


pthread_mutex_unlock( &pglobal->db );


/*
* print the individual mimetype and the length
* sending the content-length fixes random stream disruption observed
* with firefox
*/
/* 让 buffer = ""报文,告诉客服端即将发送的图片的大小 */
sprintf(buffer, "Content-Type: image/jpeg\r\n" \
"Content-Length: %d\r\n" \
"\r\n", frame_size);
DBG("sending intemdiate header\n");
if ( write(fd, buffer, strlen(buffer)) < 0 ) break;


/* 将一帧图片发送出去 */
DBG("sending frame\n");
if( write(fd, frame, frame_size) < 0 ) break;


/* 让 buffer = "boundarydonotcross" */

发送这个字符串是因为对于客户端来说,接收一帧数据怎么知道接收一帧数据接收完。一种是根据frame_size来判断一帧数据是否接收完,另一种是接收的数据是字符串boundarydonotcross(每两帧图片的边界值)的时候,就表示一帧图片接收完了,即将接收的是第二帧图片
DBG("sending boundary\n");
sprintf(buffer, "\r\n--" BOUNDARY "\r\n");
if ( write(fd, buffer, strlen(buffer)) < 0 ) break;
}


free(frame); // 释放缓存
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值