FLV视频流格式
FLV (Flash Video) 是一种轻量化的视频封装格式,适合实时流媒体传输,主要特点包括:
- 轻量级封装:封装开销低,适合在网络上传输。
- 流式播放:支持边下载边播放,特别适合直播场景。
- 适配性强 :虽然 Flash 逐渐被淘汰,但 FLV 数据仍可以通过现代播放器(如
flv.js
)播放。
http-flv:https://blog.csdn.net/www_dong/article/details/144571432
flv协议:https://blog.csdn.net/www_dong/article/details/128166528
WebSocket 协议
特点
-
全双工通信:客户端和服务器可以随时发送消息,无需等待请求。
-
基于 TCP 的协议:通过 TCP 连接提供高效的数据传输。
-
长连接:建立连接后,保持持久连接,无需多次握手。
-
低延迟:相比于 HTTP 请求/响应,WebSocket 消除额外的 HTTP 报文开销。
-
轻量级协议头:消息头非常小,减少了带宽消耗。
-
事件驱动:通过事件监听机制处理消息和连接状态。
连接过程
WebSocket 连接建立需要经过一个 HTTP 升级握手(Upgrade Handshake),步骤如下:
客户端请求升级
客户端通过 HTTP 发送升级请求:
http
GET /chat HTTP/1.1
Host: example.com:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
关键头部:
Upgrade: websocket
:请求将 HTTP 升级为 WebSocket。Connection: Upgrade
:指定连接为升级类型。Sec-WebSocket-Key
:一个 Base64 编码的随机字符串,用于验证连接有效性。Sec-WebSocket-Version
:指定 WebSocket 协议的版本(常见为13
)。
服务器响应
服务器收到请求后验证头部信息,返回以下响应:
http
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
关键头部:
Sec-WebSocket-Accept
:基于客户端的Sec-WebSocket-Key
和特定算法生成的值,用于确认握手。
握手完成
连接建立后,双方切换到 WebSocket 协议,开始通过 TCP 通道进行数据交换。
特点
优点
- 实时性强:支持低延迟双向通信。
- 节省带宽:帧头较小,减少了网络开销。
- 轻量化:在 TCP 层之上直接操作,避免了 HTTP 的冗余数据。
缺点
- 复杂性:实现握手和协议需要更多开发和调试。
- 网络环境要求:依赖于 TCP 长连接,在某些防火墙或代理下可能不支持。
- 安全性:需要特别注意对传输数据的加密和校验(如 WSS)。
数据帧格式
WebSocket 数据通过帧(Frame)进行传输。每个帧包含以下字段:
字段名称 | 长度 | 描述 |
---|---|---|
FIN | 1 位 | 表示消息是否结束(1 表示结束)。 |
RSV1, RSV2, RSV3 | 各 1 位 | 保留位,通常为 0。 |
Opcode | 4 位 | 指定帧类型(如文本、二进制)。 |
Mask | 1 位 | 指示是否对数据进行掩码。 |
Payload Length | 7 位或更多 | 数据负载的长度。 |
Masking-Key | 4 字节(可选) | 掩码密钥,用于客户端到服务器的数据。 |
Payload Data | 可变长度 | 实际传输的数据。 |
Opcode 的取值
0x1
:文本帧0x2
:二进制帧0x8
:连接关闭帧0x9
:Ping 帧0xA
:Pong 帧
示例
- 客户端
javascript
// 创建 WebSocket 连接
const socket = new WebSocket("ws://example.com/socket");
// 连接打开事件
socket.onopen = () => {
console.log("WebSocket connection established.");
socket.send("Hello, Server!");
};
// 接收消息事件
socket.onmessage = (event) => {
console.log("Message from server:", event.data);
};
// 错误事件
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
// 关闭事件
socket.onclose = () => {
console.log("WebSocket connection closed.");
};
- 服务端
javascript
const WebSocket = require('ws');
// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
console.log("Client connected.");
// 接收客户端消息
ws.on('message', (message) => {
console.log("Received:", message);
ws.send("Hello, Client!");
});
// 处理关闭事件
ws.on('close', () => {
console.log("Client disconnected.");
});
});
技术对比
特性 | WebSocket | HTTP/2 | SSE (Server-Sent Events) |
---|---|---|---|
双向通信 | 是 | 否 | 否 |
连接类型 | 长连接 | 多路复用的长连接 | 长连接 |
协议复杂性 | 中等 | 高 | 低 |
消息方向 | 双向 | 单向(服务器推送为主) | 单向(服务器推送) |
适用场景 | 实时聊天、游戏、股票推送 | HTTP 优化(如 REST API) | 实时通知、新闻推送 |
ws-flv
WebSocket-FLV 是指使用 WebSocket 协议传输 FLV 格式视频流的技术组合。它主要用于实时直播场景,结合了 WebSocket 的实时性和 FLV 的轻量特点。
工作流程
服务器端:
- 将音视频流编码为 FLV 格式(可使用 FFmpeg 或其他编码工具)。
- 通过 WebSocket 推送 FLV 数据到客户端。
客户端:
- 使用 WebSocket 接收 FLV 数据流。
- 利用播放工具(如
flv.js
)解析并渲染 FLV 数据。
优点
- 延迟极低,通常低于 1 秒。
- 数据传输高效,FLV 格式进一步减小了数据量。
- 实现较为简单,适合实时直播。
示例(c++)
- 服务端
c++
#include <uwebsockets/App.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <thread>
class WebSocketFLVServer {
public:
WebSocketFLVServer(const std::string &flvFilePath, int port)
: flvFilePath(flvFilePath), port(port) {}
void start() {
// 加载 FLV 文件
if (!loadFLVFile()) {
std::cerr << "Failed to load FLV file: " << flvFilePath << std::endl;
return;
}
// 创建 WebSocket 服务器
uWS::App()
.ws<ConnectionData>("/*", {
.open = [this](auto *ws) {
std::cout << "Client connected!" << std::endl;
ws->send(reinterpret_cast<char *>(flvData.data()), flvData.size(), uWS::BINARY);
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
// 处理客户端消息(可选)
std::cout << "Received message: " << message << std::endl;
},
.close = [](auto *ws, int code, std::string_view message) {
std::cout << "Client disconnected!" << std::endl;
}
})
.listen(port, [this](auto *token) {
if (token) {
std::cout << "WebSocket server listening on port " << port << std::endl;
} else {
std::cerr << "Failed to listen on port " << port << std::endl;
}
})
.run();
}
private:
struct ConnectionData {};
std::string flvFilePath;
int port;
std::vector<uint8_t> flvData;
bool loadFLVFile() {
std::ifstream file(flvFilePath, std::ios::binary);
if (!file.is_open()) {
return false;
}
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
flvData.resize(fileSize);
file.read(reinterpret_cast<char *>(flvData.data()), fileSize);
file.close();
return true;
}
};
int main() {
const std::string flvFilePath = "live.flv"; // 替换为实际的 FLV 文件路径
const int port = 9001;
WebSocketFLVServer server(flvFilePath, port);
server.start();
return 0;
}
wss-flv
WSS-FLV 是指通过 WebSocket Secure(WSS) 协议传输 FLV(Flash Video) 视频流的技术组合。WSS 是 WebSocket 协议的安全版本,类似于 HTTPS,相较于普通 WebSocket(WS),WSS 在传输层使用了 TLS/SSL 加密,保障了数据的安全性。
特点
-
加密通信:通过 TLS/SSL 加密,防止数据被篡改或窃听。
-
与 HTTPS 兼容:可以在 HTTPS 环境中无缝工作,特别适合前端页面通过 WebSocket 通信的场景。
-
适用高安全场景:如支付视频流、医疗直播、敏感监控视频传输等。
应用
直播平台:
- 提供安全的低延迟直播传输,避免流媒体数据被拦截或劫持。
实时监控:
- 安全传输监控视频流,保障用户隐私。
教育和会议系统:
- 在线教育或实时会议中,确保音视频数据加密传输。
远程医疗:
- 保障医疗数据和视频流的传输安全性。
示例(c++)
c++
#include <uwebsockets/App.h>
#include <fstream>
#include <iostream>
#include <vector>
#include <thread>
class WSSFLVServer {
public:
WSSFLVServer(const std::string &flvFilePath, int port, const std::string &certPath, const std::string &keyPath)
: flvFilePath(flvFilePath), port(port), certPath(certPath), keyPath(keyPath) {}
void start() {
// 加载 FLV 文件
if (!loadFLVFile()) {
std::cerr << "Failed to load FLV file: " << flvFilePath << std::endl;
return;
}
// 启动 WSS 服务
uWS::SSLApp({
.key_file_name = keyPath.c_str(),
.cert_file_name = certPath.c_str(),
.passphrase = ""
})
.ws<ConnectionData>("/*", {
.open = [this](auto *ws) {
std::cout << "Client connected (secure)!" << std::endl;
ws->send(reinterpret_cast<char *>(flvData.data()), flvData.size(), uWS::BINARY);
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
// 可选的客户端消息处理
std::cout << "Received message: " << message << std::endl;
},
.close = [](auto *ws, int code, std::string_view message) {
std::cout << "Client disconnected!" << std::endl;
}
})
.listen(port, [this](auto *token) {
if (token) {
std::cout << "WSS server listening on port " << port << std::endl;
} else {
std::cerr << "Failed to listen on port " << port << std::endl;
}
})
.run();
}
private:
struct ConnectionData {};
std::string flvFilePath;
int port;
std::string certPath;
std::string keyPath;
std::vector<uint8_t> flvData;
bool loadFLVFile() {
std::ifstream file(flvFilePath, std::ios::binary);
if (!file.is_open()) {
return false;
}
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
flvData.resize(fileSize);
file.read(reinterpret_cast<char *>(flvData.data()), fileSize);
file.close();
return true;
}
};
int main() {
const std::string flvFilePath = "live.flv"; // 替换为实际 FLV 文件路径
const int port = 9001;
const std::string certPath = "certificate.crt"; // SSL 证书文件路径
const std::string keyPath = "private.key"; // SSL 私钥文件路径
WSSFLVServer server(flvFilePath, port, certPath, keyPath);
server.start();
return 0;
}