5. 视频客户端
5.1 HTTP协议: // http.zip超文本传输协议,基于TCP的传输通信模型。
通信的双方:
客户端要给服务器端发送request(请求)
服务器端根据客户端的请求回送response(响应)
HTTP是一个'应用层'协议,由请求和响应构成。
HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。
HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
http默认采用 80 端口。
5.2 对mjpg-streamer的分析得到以下内容
如果客户端发送的请求中包含" GET /?action=stream",服务器就会将视频数据封装HTTP格式数据帧,不断发送给客户端,按照http协议request的数据格式,就是给服务器发送" GET /?action=stream HTTP/1.1\r\n\r\n"
HTTP格式请求格式:
request:
【请求行】例如GET /images/logo.gif HTTP/1.1,表示从/images目录下请求logo.gif这个文件
【请求头】例如Accept-Language: en
【空行】\r\n
【可选的消息体】
请求行和标题必须以<CR><LF>作为结尾(也就是,回车然后换行)。空行内必须只有<CR><LF>而无其他空格。在HTTP/1.1协议中,几乎所有的请求头都是可选的
response:
【响应行】
【响应头】
【空行】
【可选的消息体】
5.3 编写一个tcp客户端程序
code_for_mjpgstreamer.rar
1)保证客户端和服务器能够联通
serverip : 开发板IP
端口号: 8080
给服务器发送请求 " GET /?action=stream HTTP/1.1\r\n\r\n"
读取一次服务器返回的数据,并将数据打印
2)在1)的基础上,不断地读取数据
把读到的数据保存 /tmp/test.jpg文件
保存1M数据后程序退出
3)能不能把http头信息过滤掉 把http尾信息过滤掉
只将图像信息内容保存到/tmp/test.jpg文件去
$:' hexdump -C /tmp/test.jpeg | less
服务器返回的图像是jpeg格式的
jpeg图像帧在存储时是有固定的格式
JPEG是一种有损的图像压缩算法
JPEG文件由两部分组成: 标记码 压缩数据
' FF D8 .... ... .... FF D9'
code_for_mjpgstreamer.rar/03代码
/* 代码演示 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int flag;
int find_start_pos (unsigned char *buf, int len)
{
int i = 0;
for (; i < len-1; i++) {
if (buf[i] == 0xff && buf[i+1] == 0xd8)
return i;
}
return -1;
}
int find_end_pos (unsigned char *buf, int len)
{
int i = 0;
for (; i < len-1; i++) {
if (buf[i] == 0xff && buf[i+1] == 0xd9) {
flag = 1;
return i+2;
}
}
return -1;
}
void http_request (char *ip, int port)
{
unsigned char buffer[1024] = {0};
unsigned char sendbuf[] = "GET /?action=stream HTTP/1.1\r\n\r\n";
struct sockaddr_in addr;
int sd = socket (AF_INET, SOCK_STREAM, 0);
if (sd < 0) {
perror ("socket");
return;
}
addr.sin_family = AF_INET;
addr.sin_port = htons (port);
addr.sin_addr.s_addr = inet_addr (ip);
int ret = connect (sd, (struct sockaddr *)&addr, sizeof (addr));
if (ret < 0) {
perror ("connect");
close (sd);
return ;
}
ret = write (sd, sendbuf, strlen (sendbuf));
ret = read (sd, buffer, 1024);
printf ("%s\n", buffer);
int count = 0;
int fd = open ("/tmp/test.jpg", O_WRONLY|O_CREAT, 0777);
if (fd < 0) {
perror ("open");
close (sd);
return;
}
unsigned char *mbuf = (unsigned char *)malloc (1024*1024);
int size = 0;
unsigned char soi[2] = {0xff, 0xd8};
unsigned char eoi[2] = {0xff, 0xd9};
while (count < 1024*1024) {
int ret = read (sd, buffer, 1024);
memcpy (mbuf + size, buffer, ret);
count += ret;
size += ret;
int psoi = find_start_pos (mbuf, size);
if (psoi == -1)
continue;
int peoi = find_end_pos (mbuf, size);
if (peoi == -1)
continue;
if (flag == 1) {
lseek (fd, 0, SEEK_SET);
write (fd, mbuf + psoi, peoi - psoi);
flag = 0;
}
printf ("write bytes = %d\n", peoi-psoi);
size = 0;
}
close (fd);
close (sd);
}
int main (void)
{
http_request ("192.168.1.6", 8080);
return 0;
}
$:' gcc http_request.c
$:' a.out
$:' nautilus /tmp/
// 查看对应的图片写入变化情况 1..2..1..2..1..
4) 编写GUI客户端显示图像数据
// project/env/ehome/ehome_day04/video/05/
camer: 该类负责和HTTP server进行通信
连接服务器
connectToHost
给服务器发送请求"GET /?action=stream HTTP/1.1\r\n\r\n"
requestImage
接收server返回的数据
并从中过滤出"ff d8 .... ff d9"
void CamClient::readImage()
{
接收数据
过滤出图像
发送信号 newImageReady(image)给mainwindow
}
void MainWindow::showNewImage(QImage img)
{
/*显示图片*/
ui->imageLabel->setPixmap(QPixmap::fromImage(img));
}
通信过程中使用了QTcpSocket
注意1:SIGNAL(readyRead())
当收到readyRead()信号时,去调用对应的槽函数接收数据,过滤数据
camer::camer(QObject *parent) :
QObject(parent)
{
connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(readImage()));
}
注意2: 自定义的信号
CamClient 当过滤到一个完整的图像帧发出
MainWindow负责处理该信号
视频数据的压缩 H264
// 命令行自启动shell脚本
$:' vi home/bin/start.sh
source /home/etc/profile
#有摄像头的情况下启动www服务。
insmod home/drivers/leds_drv.ko
/home/bin/server & #led灯的server
/home/bin/mjpg_streamer -i "/home/lib/input_uvc.so -d /dev/video9 -y -r 320x240 -f 30" -o "/home/lib/output_http.so -w /home/www" &
#没有摄像头的情况下启动www服务。
#insmod home/drivers/leds_drv.ko
#/home/bin/server & #led灯的server
#/home/bin/mjpg_streamer -i "/home/lib/input_testpicture.so -r 320x240 -d 500" -o "/home/lib/output_http.so -w /home/www -p 8080" &
source /home/etc/profile