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
int _readline(int fd, iobuffer *iobuf, void *buffer, size_t len, int timeout)
{
char c='\0', *out=buffer;
int i;
iobuf.buf[]裡面的資料是從哪裡來的呢?
看_read函數
memset(buffer, 0, len);
for ( i=0; i<len && c != '\n'; i++ )
{
if ( _read(fd, iobuf, &c, 1, timeout) <= 0 )
{
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;
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);
return copied;
}
init_iobuffer(iobuf);// 将 iobuf 清0
if ( (iobuf->level = read(fd, &iobuf->buffer, IO_BUFFER)) <= 0 )
{
return -1;
}
IO_BUFFER是256
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};
輸入通道:攝像頭源源不斷采集資料,每采集完一幀資料就會往倉庫裡存放資料,存放好後,發出一個資料更新信号(通過phread_cond_broadcast函數)
pthread_cond_wait(&pglobal->db_update, &pglobal->db);
frame_size = pglobal->size; // 得到一幀資料的大小
if ( (frame = malloc(frame_size+1)) == NULL )
{
free(frame);
pthread_mutex_unlock( &pglobal->db );
send_error(fd, 500, "not enough memory");
return;
}
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
sprintf(buffer, "HTTP/1.0 200 OK\r\n" \
STD_HEADER \
"Content-type: image/jpeg\r\n" \
"\r\n");
對于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");
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");
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 )
{
pthread_cond_wait(&pglobal->db_update, &pglobal->db);
frame_size = pglobal->size; // 得到一幀圖檔的大小
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 );
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;
發送這個字元串是因為對于用戶端來說,接收一幀資料怎麼知道接收一幀資料接收完。一種是根據frame_size來判斷一幀資料是否接收完,另一種是接收的資料是字元串boundarydonotcross(每兩幀圖檔的邊界值)的時候,就表示一幀圖檔接收完了,即将接收的是第二幀圖檔
DBG("sending boundary\n");
sprintf(buffer, "\r\n--" BOUNDARY "\r\n");
if ( write(fd, buffer, strlen(buffer)) < 0 ) break;
}
free(frame); // 釋放緩存
}