找回密码
 立即注册
收起左侧

Qt音视频开发41-文件推流(支持网页和播放器播放并切换进...

0
回复
1079
查看
[复制链接]
累计签到:7 天
连续签到:1 天
来源: 2023-4-27 09:20:51 显示全部楼层 |阅读模式


## 一、前言
本功能最初也是有一些人提过类似的需求,就是能不能将本地的音视频文件,通过纯Qt程序推流出去,然后用户可以直接在网页上播放,也可以用各种播放器播放,然后还可以任意切换播放进度,其实说白了就是个文件服务器,用户通过网络地址访问以后,告诉对方当前是媒体文件就会自动播放,是其他文件则可以开启下载,很多视频网站最初也是按照这个思路来设计,当然缺点很明显,那就是无法防止用户下载,毕竟这个本来就是当做文件发给用户的,无所谓保密的需求,话说现在的无论哪一种视频网站,只要能播放,用户就能通过各种手段录制下来的,也是无法规避这个问题。


无论网络协议如何发展,都离不开最底层的两种协议,tcp/udp通信,http也是建立在这两种协议基础上,然后又在http基础上衍生了众多的协议,总之,最基础的tcp/udp几十年都没变过,现在音视频发展这么迅速,衍生的各种rtmp/rtsp/hsl/webrtc啥的,最终底层还是基于tcp/udp通信,明白了这个道理,文件推流理论上基于tcp就可以实现。音视频文件在普通文件服务的基础上还多了个范围的参数Accept-Ranges: bytes/Content-Range,就是用户单击了进度后告诉服务这边当前要切换到哪个字节位置,这样就可以任意跳转播放进度。


## 二、效果图



## 三、体验地址
1. 国内站点:[https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun)
2. 国际站点:[https://github.com/feiyangqingyun](https://github.com/feiyangqingyun)
3. 个人作品:[https://blog.csdn.net/feiyangqingyun/article/details/97565652](https://blog.csdn.net/feiyangqingyun/article/details/97565652)
4. 体验地址:[https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g](https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g) 提取码:01jf 文件名:bin_video_push。


## 四、相关代码
```cpp
void FilePushClient::readData()
{
    //GET /后缀 HTTP/1.1
    //Host: 192.168.0.110:6908
    //Connection: keep-alive (一般网页请求是keep-alive/其他都是close)
    //Upgrade-Insecure-Requests: 1
    //User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
    //Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    //Accept-Encoding: gzip, deflate
    //Accept-Language: zh-CN,zh;q=0.9,en;q=0.8


    QByteArray data = tcpSocket->readAll();
    buffer.append(data);


    //超过了长度说明数据是垃圾数据
    if (buffer.size() > 1000) {
        buffer.clear();
        return;
    }


    //末尾必须是两个回车换行
    if (!buffer.endsWith("\r\n\r\n")) {
        return;
    }


    //解析请求(解析失败则不用继续)
    QHttpParser parser;
    bool ok = parser.parserRequest(buffer);
    emit receiveData(buffer);
    buffer.clear();
    if (!ok) {
        quit();
        return;
    }


    //不是对应的方法
    if (parser.method() != "GET") {
        quit();
        return;
    }


    //取出后缀地址(如果是请求图标则不用继续)
    QString url = parser.url();
    if (!url.startsWith("/") || url.startsWith("/favicon.ico")) {
        return;
    }


    //根据请求中的hash值查找文件
    QString hash = url.mid(1, url.length());
    FilePushServer *server = (FilePushServer *)this->parent();
    QString fileName = server->findFile(hash);
    if (!this->setFile(fileName)) {
        quit();
        return;
    }


    QString range = parser.headerValue("Range");
    if (range.isEmpty()) {
        this->writeData200(0);
        return;
    }


    //Range: bytes=0- / bytes=-1024 / bytes=0-1024
    QStringList list = range.split("=");
    if (list.count() != 2) {
        quit();
        return;
    }


    //取出进度范围
    range = list[1];
    range.replace(" ", "");
    list.clear();
    list = range.split("-");


    //测试下来发现基本上都是 x- 的情况
    qint64 startPos, endPos;
    if (range.startsWith("-")) {
        endPos = fileSize - 1;
        startPos = endPos - list.at(0).toInt();
    } else if (range.endsWith("-")) {
        startPos = list.at(0).toInt();
        endPos = fileSize - 1;
    } else {
        startPos = list.at(0).toInt();
        endPos = list.at(1).toInt();
    }


    this->writeData206(startPos, endPos);
}


void FilePushClient::writeData(qint64 bytes)
{
    writeByteCount -= bytes;
    if (tcpSocket && writeByteCount > 0) {
        qint64 size = 512 * 1024;
        size = tcpSocket->write(file->read(size));
        //qDebug() << TIMEMS << "writeData" << size;
    }
}


QByteArray FilePushClient::getHeadData(const QString &flag, qint64 startPos, qint64 endPos, qint64 bufferSize)
{
    QStringList list;
    list << flag;
    list << "Server: QQ_517216493 WX_feiyangqingyun";
    list << "Cache-control: no-cache";
    list << "Pragma: no-cache";
    list << "Connection: close";
    list << "Accept-Ranges: bytes";
    list << "Access-Control-Allow-Origin: *";
    list << QString("Content-Type: %1").arg(fileType);
    list << QString("Content-Length: %1").arg(bufferSize);
    if (playMode == 1) {
        list << QString("Content-Disposition: attachment;filename=%1").arg(fileName);
    }
    list << QString("Content-Range: bytes %1-%2/%3").arg(startPos).arg(endPos).arg(fileSize);
    //末尾必须加个回车换行
    list << "\r\n";


    QString data = list.join("\r\n");
    return data.toUtf8();
}


void FilePushClient::writeData200(qint64 startPos)
{
    if (!file->isOpen()) {
        return;
    }


    if (startPos >= fileSize) {
        return;
    }


    //文件切换到对应位置
    file->seek(startPos);
    qint64 endPos = fileSize - 1;
    qint64 bufferSize = fileSize - startPos;
    QByteArray data = getHeadData("HTTP/1.1 200 OK", startPos, endPos, bufferSize);


    //计算并发送数据
    writeByteCount = data.size() + (fileSize - startPos);
    tcpSocket->write(data);
    emit sendData(data);
    //qDebug() << TIMEMS << "writeData200";
}


void FilePushClient::writeData206(qint64 startPos, qint64 endPos)
{
    if (!file->isOpen()) {
        return;
    }


    if (startPos >= fileSize || startPos >= endPos) {
        return;
    }


    //文件切换到对应位置
    file->seek(startPos);
    qint64 bufferSize = endPos - startPos + 1;
    QByteArray data = getHeadData("HTTP/1.1 206 Partial Content", startPos, endPos, bufferSize);


    //计算并发送数据
    writeByteCount = data.size() + (fileSize - startPos);
    tcpSocket->write(data);
    emit sendData(data);
    //qDebug() << TIMEMS << "writeData206";
}
```


## 五、功能特点
### 5.1 文件推流
1. 指定网卡和监听端口,接收网络请求推送音视频等各种文件。
2. 实时统计显示每个文件对应的访问数量、总访问数量、不同IP地址访问数量。
3. 可指定多种模式,0-直接播放、1-下载播放。
4. 实时打印显示各种收发请求和应答数据。
5. 每个文件对应MD5加密的唯一标识符,用于请求地址后缀区分访问哪个文件。
6. 支持各种浏览器(谷歌chromium/微软edge/火狐firefox等)、各种播放器(vlc/mpv/ffplay/potplayer/mpchc等)打开请求。
7. 播放过程中可以任意切换播放进度,支持倍速播放。
8. 需要推流的文件名称历史记录自动存储和打开加载应用。
9. 切换文件获取访问地址,自动拷贝地址到剪切板方便直接粘贴测试使用。
10. 极低CPU占用,128路1080P同时推流不到1%CPU占用,异步发送数据机制。
11. 纯QTcpSocket通信,不依赖流媒体服务程序,核心源码不到500行,注释详细,功能完整。
12. 支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。


### 5.2 网络推流
1. 支持各种本地视频文件和网络视频文件。
2. 支持各种网络视频流,网络摄像头,协议包括rtsp、rtmp、http。
3. 支持将本地摄像头设备推流,可指定分辨率和帧率等。
4. 支持将本地桌面推流,可指定屏幕区域和帧率等。
5. 自动启动流媒体服务程序,默认mediamtx(原rtsp-simple-server),可选用srs、EasyDarwin、LiveQing、ZLMediaKit等。
6. 可实时切换预览视频文件。
7. 推流的清晰度和质量可调。
8. 可动态添加文件、目录、地址。
9. 视频文件自动循环推流,如果视频源是视频流,在掉线后会自动重连。
10. 网络视频流自动重连,重连成功自动继续推流。
11. 网络视频流实时性极高,延迟极低,延迟时间大概在100ms左右。
12. 推流后除了用rtmp地址访问以外,还支持直接hls/webrtc访问,可以直接浏览器打开看实时画面。
13. 支持Qt4/Qt5/Qt6任意版本,支持任意系统(windows/linux/macos/android/嵌入式linux等)。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了