实现一个高性能的HTTP-FLV流媒体服务器


前言

在当今的互联网时代,流媒体技术已经成为传输音视频内容的核心技术之一。为了更好地理解和应用流媒体技术,开始学习并实现了一个高性能的HTTP-FLV流媒体服务器。HTTP-FLV是一种常用的实时流媒体传输协议,结合了HTTP协议的广泛兼容性和FLV(Flash Video)格式的高效性,非常适用于实时视频流的传输。在博客中,将详细介绍HTTP-FLV的基本概念,并分享在实现高性能HTTP-FLV流媒体服务器过程中积累的经验和技巧。


一、HTTP-FLV是什么?

FLV (Flash Video) 是 Adobe 公司推出的另一种视频格式,是一种在网络上传输的流媒体数据存储容器格式。其格式相对简单轻量,不需要很大的媒体头部信息。整个 FLV 由 The FLV Header, The FLV Body 以及其它 Tag 组成。因此加载速度极快。采用 FLV 格式封装的文件后缀为 .flv。
在这里插入图片描述
HTTP-FLV 依靠 MIME 的特性,根据协议中的 Content-Type 来选择相应的程序去处理相应的内容,使得流媒体可以通过 HTTP 传输。相较于 RTMP 协议,HTTP-FLV 能够好的穿透防火墙,它是基于 HTTP/80 传输,有效避免被防火墙拦截。除此之外,它可以通过 HTTP 302 跳转灵活调度/负载均衡,支持使用 HTTPS 加密传输,也能够兼容支持 Android,iOS 的移动端。

说了这么多优点,也来顺便说下 HTTP-FLV 的缺点,由于它的传输特性,会让流媒体资源缓存在本地客户端,在保密性方面不够好。因为网络流量较大,它也不适合做拉流协议。

地址是以http://开头的,是基于HTTP协议的,HTTP-FLV可以简单地理解为RTMP的HTTP协议版本。但HTTP-FLV协议一般只能用拉流观看,HTTP-FLV协议的延迟也是比较低的,大概在1-3秒左右。HTTP-FLV相对于RTMP适配更多的播放场景。
在这里插入图片描述
HTTP-FLV直播流一般需要加入插件才能播放,如网页需要引入flv.js后,浏览器才能播放HTTP-FLV直播流。B站开源的flv.js促进了HTTP-FLV在浏览器的普及。
在这里插入图片描述
一般HTTP-FLV的流媒体服务,推流是以RTMP协议,拉流是用HTTP-FLV协议
在这里插入图片描述
HTTP-FLV 使用类似 RTMP流式的 HTTP 长连接,需由特定流媒体服务器分发的,兼顾两者的优点。以及可以复用现有 HTTP 分发资源的流式协议。它的实时性和 RTMP 相等,与 RTMP 相比又省去了部分协议交互时间,首屏时间更短,可拓展的功能也更多。在这里插入图片描述

二、实现一个高性能的HTTP-FLV流媒体服务器

1.main.cpp

代码及详细注释如下:

#include "Server/BoostServer.h"
#include "Scheduler.h"
#include "Utils/Config.h"

/*

ffmpeg 播放http-flv视频流
    ffplay -i http://localhost:8080/test.flv

*/

int main(int argc, char* argv[])
{
   
    const char* file = NULL;
    file = "config.json";   // 配置文件,启动参数放置在config.json

    Config config(file);    // 利用第三方库jsoncpp解析config.json
    if (!config.state) {
       // 配置参数读取失败
        printf("failed to read config file: %s\n", file);
        return -1;
    }

/*
BoostServer server(&config);: 这行代码创建了一个名为 server 的 BoostServer 对象,
并使用名为 config 的参数进行初始化。

std::thread([&]() { ... }).detach();: 这是一个 std::thread 对象的构造,
它使用了一个 lambda 表达式作为线程的执行体。
lambda 表达式 [&]() 表示捕获外部作用域的所有变量(以引用方式),
{ ... } 中的代码是线程的主体部分。

在 lambda 表达式内部,server.start(); 调用了 BoostServer 对象的 start() 方法,这是启动服务器的操作。

detach() 方法将新创建的线程分离,意味着它会在后台运行,
与主线程独立执行,不会等待该线程的结束。这样做是为了使主线程继续执行而不被阻塞。
*/
    BoostServer server(&config);
    std::thread([&]() {
        
        server.start();
    }).detach();
    
    Scheduler sch(&server,&config);
    sch.loop();

    return 0;
}

2.配置相关类

代码及详细注释如下:

1. config.json

// config.json
{
   
  "ip": "0.0.0.0",
  "port": 8080,
  "threadNum": 1
}

2. config.h

#ifndef CONFIG_H
#define CONFIG_H
#include <string>

class Config
{
   
	public:
		Config(const char* file);
		Config() = delete;
		~Config();

	public:
		bool state = false;
		void show();

		const char* getIp() const {
   
			return ip.data();
		}
		int getPort() const {
   
			return port;
		}
		int getThreadNum() const {
   
			return threadNum;
		}
	private:
		std::string mFile;

		std::string ip;			
		int         port;		
		int         threadNum;
};
#endif //CONFIG_H

3. config.cpp

#include "Config.h"
#include <fstream>
#include <json/json.h>
#include "Log.h"


Config::Config(const char* file) : mFile(file)  // 传递配置文件的文件路径
{
   
    std::ifstream ifs(mFile, std::ios::binary);

    if (!ifs.is_open()) {
   
        LOGE("open %s error", file);
        return;
    }
    else {
   
        Json::CharReaderBuilder builder;
        builder["collectComments"] = true;
        JSONCPP_STRING errs;
        Json::Value root;

        if (parseFromStream(builder, ifs, &root, &errs)) {
   

            this->ip = root["ip"].asString();
            this->port = root["port"].asInt();
            this->threadNum = root["threadNum"].asInt();

            state = true;
        }
        else {
   
            LOGE("parse %s error", file);
        }
        ifs.close();
    }
}

Config::~Config()
{
   

}

void Config::show() {
   

    //printf("config.file=%s\n", mFile.data());

}

3.BoostServer

1. BoostServer.h

#ifndef BOOSTSERVER_H
#define BOOSTSERVER_H

#include "boost.h"
#include <map>

class Config;
class HttpServerConnection;

class BoostServer
{
   
public:
	BoostServer(Config* config);
	~BoostServer();
public:
	void start();

	bool addConn(HttpServerConnection* conn);		// 添加连接
	bool removeConn(std::string session);
	Ht
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值