Ehome:智能家居之基于USB摄像头的实时视频监控功能


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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姜源Jerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值