天天看點

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

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); // 釋放緩存

}