流媒体服务系列文章
文章目录
- 流媒体服务系列文章
- 前言
- 一、http flv?
- 二、使用步骤
- 服务器代码
- 总结
前言
HTTP FLV通过http传输,时延可控制在2秒以内,浏览器可基于bilibili开源的flv.js(采用h5 mse技术)开发,比起rtsp、rtmp等免插件播放,因此被广泛应用于直播场景。
一、http flv?
httpflv是什么
FLV (Flash Video) 是 Adobe 公司推出的另一种视频格式,是一种在网络上传输的流媒体数据存储容器格式。其格式相对简单轻量,不需要很大的媒体头部信息。整个FLV由 The FLV Header, The FLV Body 以及其它 Tag 组成。因此加载速度极快。采用 FLV 格式封装的文件后缀为.flv。而HTTP-FLV 即将流媒体数据封装成 FLV 格式,然后通过 HTTP 协议传输给客户端。
httpflv传输特点
分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由网页服务器发送给客户端应用( 通常是网页浏览器)的数据可以分成多个部分。分块传输编码只在HTTP协议1.1版本(HTTP/1.1)中提供。
HTTP协议中有个约定:content-length字段,http的body部分的长度服务器回复http请求的时候如果有这个字段,客户端就接收这个长度的数据然后就认为数据传输完成了,如果服务器回复http请求中没有这个字段,客户端就一直接收数据,直到服务器跟客户端的socket连接断开。
http-flv直播就是利用第二个原理,服务器回复客户端请求的时候不加content-length字段或者content-length为-1,在回复了http内容之后,紧接着发送flv数据,客户端就一直接收数据了。
httpflv传输格式
如果一个HTTP消息(请求消息或应答消息)的Transfer-Encoding消息头的值为chunked,那么,消息体由数量未定的块组成,并以最后一个大小为0的块为结束。
每一个非空的块都以该块包含数据的字节数(字节数以十六进制表示)开始,跟随一个CRLF (回车及换行),然后是数据本身,最后块CRLF结束。在一些实现中,块大小和CRLF之间填充有白空格(0x20)。
最后一块是单行,由块大小(0),一些可选的填充白空格,以及CRLF。最后一块不再包含任何数据,但是可以发送可选的尾部,包括消息头字段。
消息最后以CRLF结尾。
如
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
25
This is the data in the first chunk
1C
and this is the second one
3
con
8
sequence
0
httpflv优缺点
优点:
HTTP-FLV 依靠 MIME 的特性,根据协议中的 Content-Type 来选择相应的程序去处理相应的内容,使得流媒体可以通过 HTTP 传输。相较于 RTMP 协议,HTTP-FLV 能够较好的穿透防火墙,它是基于 HTTP/80 传输,有效避免被防火墙拦截。除此之外,它可以通过 HTTP 302 跳转灵活调度/负载均衡,支持使用 HTTPS 加密传输,也能够兼容支持 Android,iOS 的移动端。
缺点:
由于HTTP-FLV的传输特性,会让流媒体资源缓存在本地客户端,在保密性方面不够好。因为网络流量较大,它也不适合做拉流协议。
二、使用步骤
服务器代码
#include <stdint.h>
#include <iostream>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <cstring>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cassert>
#include <string>
#include <iostream>
#include <memory>
#include <functional>
#include <thread>
#define MAXCONN 1024
int main() {
int port = 8888;
const char* filename = "/home/Mycode/data/test.flv";
printf("httpflvServer http://10.20.39.168:%d/test.flv \n", port);
int serverFd = -1;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
serverFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverFd < 0) {
printf("socket create error\n");
return -1;
}
printf("socket create sucess %d",serverFd);
auto ret = bind(serverFd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (ret < 0) {
fprintf(stderr, "bind socket error %s errno: %d\n", strerror(errno), errno);
printf("socket bind error\n");
return -1;
}
if (listen(serverFd, MAXCONN) < 0) {
printf("socket listen error\n");
return -1;
}
constexpr char http_headers[] = \
"HTTP/1.1 200 OK\r\n" \
"Access-Control-Allow-Origin: * \r\n" \
"Content-Type: video/x-flv\r\n" \
"Content-Length: -1\r\n" \
"Connection: Keep-Alive\r\n" \
"Expires: -1\r\n" \
"Pragma: no-cache\r\n" \
"\r\n"
;
int http_headers_len = strlen(http_headers);
/*
constexpr char http_headers[] = \
"HTTP/1.1 200 OK\r\n" \
"Access-Control-Allow-Origin: * \r\n" \
"Cache-Control: no-cache\r\n" \
"Content-Type: video/x-flv\r\n" \
"Connection: close\r\n" \
"Expires: -1\r\n" \ //设置资源的有效期来控制http的缓存
"Pragma: no-cache\r\n" \ //用于客户端发送的请求中。客户端会要求所有的中间服务器不返回缓存的资源
"\r\n"
;
*/
while (true)
{
printf("等待新连接...\n");
int len = sizeof(struct sockaddr);
struct sockaddr_in accept_addr;
int clientFd = accept(serverFd, (struct sockaddr*)&accept_addr, (socklen_t*)&len);
if (clientFd < 0) {
printf("accept connection error \n");
break;
}
printf("发现新连接 clientFd=%d \n", clientFd);
unsigned char buf[5000];
char bufRecv[2000] = { 0 };
FILE* fp;
fp = fopen(filename, "rb");
if (!fp) {
printf("fopen %s fail!\n", filename);
}
else {
int times = 0;
while (true) {
times++;
if (times == 1) {
int bufRecvSize = recv(clientFd, bufRecv, 2000, 0);
printf("bufRecvSize=%d,bufRecv=%s \n", bufRecvSize, bufRecv);
send(clientFd, http_headers, http_headers_len, 0);
}
else {
int bufLen = fread(buf, 1, sizeof(buf), fp);
int ret = send(clientFd, (char*)buf, bufLen, 0);
if (ret <= 0) {
break;
}
else {
//printf("send bufLen=%d,ret=%d \n", bufLen, ret);
}
}
}
}
if (fp) {
fclose(fp);
}
close(clientFd);
printf("关闭连接 clientFd=%d \n", clientFd);
}
close(serverFd);
return 0;
}
/*
g++ main.cpp -o httpflv.server -std=c++11 -lpthread
*/
运行与用VLC播放
./httpflvServer
httpflvServer http://ip:8888/test.flv
socket create sucess 3
等待新连接...
播放地址为:http://ipaddr:8888/test.flv
VLC播放
1、申请httpflv视频流
2、收到http封装的flv数据消息
3、vlc进行解码播放
总结
通过本文的学习,你应该对http-flv视频流有了一定的认识,希望对你后面的学习有所帮助。
授之以鱼不如授之以渔