Qt audio and video development 41-file streaming (supports webpage and player playback and switching progress)

I. Introduction

At the beginning, some people raised similar requirements for this function, that is, whether it is possible to push the local audio and video files through a pure Qt program, and then users can play them directly on the web page or with various players, and then You can also switch the playback progress arbitrarily. In fact, it is a file server. After the user accesses through the network address, tell the other party that the current media file will be played automatically, and other files can be downloaded. Many video sites initially followed this idea. Design, of course, has obvious disadvantages, that is, it cannot prevent users from downloading. After all, this is originally sent to users as a file, and there is no need for confidentiality. It is said that no matter what kind of video website is available, as long as it can play, users can use various It is impossible to avoid this problem even if it is recorded by means.

No matter how the network protocol develops, it is inseparable from the bottom two protocols, tcp/udp communication, http is also based on these two protocols, and then many protocols are derived on the basis of http. In short, the most basic tcp/udp has not changed for decades. Now that audio and video are developing so rapidly, all kinds of rtmp/rtsp/hsl/webrtc are derived. In the end, the bottom layer is still based on tcp/udp communication. I understand this truth, file push theory It can be realized based on tcp. On the basis of ordinary file services, audio and video files also have a range of parameters Accept-Ranges: bytes/Content-Range, which is to tell the service which byte position to switch to after the user clicks the progress, so that you can Arbitrary jump playback progress.

2. Rendering

insert image description here
insert image description here
insert image description here

3. Experience address

  1. Domestic site: https://gitee.com/feiyangqingyun
  2. International site: https://github.com/feiyangqingyun
  3. Personal works: https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. Experience address: https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g Extraction code: 01jf File name: bin_video_push.

4. Related codes

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. Features

5.1 File streaming

  1. Specify the network card and listening port, and receive network requests to push various files such as audio and video.
  2. Real-time statistics display the number of visits corresponding to each file, the total number of visits, and the number of visits from different IP addresses.
  3. Multiple modes can be specified, 0-direct play, 1-download play.
  4. Real-time printing displays various sending and receiving request and response data.
  5. Each file corresponds to an MD5-encrypted unique identifier, which is used to request the address suffix to distinguish which file to access.
  6. Support various browsers (Google chromium/Microsoft edge/Firefox firefox, etc.), various players (vlc/mpv/ffplay/potplayer/mpchc, etc.) to open requests.
  7. During the playback process, the playback progress can be switched arbitrarily, and double-speed playback is supported.
  8. The name history of the file that needs to be streamed is automatically stored and opened to load the application.
  9. Switch files to get the access address, and automatically copy the address to the clipboard for direct pasting and testing.
  10. Extremely low CPU usage, 128-channel 1080P simultaneous streaming is less than 1% CPU usage, asynchronous data sending mechanism.
  11. Pure QTcpSocket communication, does not depend on the streaming media service program, the core source code is less than 500 lines, with detailed annotations and complete functions.
  12. Support any version of Qt4/Qt5/Qt6, support any system (windows/linux/macos/android/embedded linux, etc.).

5.2 Network streaming

  1. Support various local video files and network video files.
  2. Support various network video streams, webcams, protocols including rtsp, rtmp, http.
  3. It supports streaming of local camera devices, and the resolution and frame rate can be specified.
  4. Supports streaming of local desktops, specifying screen area and frame rate, etc.
  5. Automatically start the streaming media service program, the default mediamtx (formerly rtsp-simple-server), you can choose srs, EasyDarwin, LiveQing, ZLMediaKit, etc.
  6. You can switch and preview video files in real time.
  7. The clarity and quality of streaming can be adjusted.
  8. Files, directories and addresses can be added dynamically.
  9. Video files are automatically streamed in a loop. If the video source is a video stream, it will automatically reconnect after being disconnected.
  10. The network video stream is automatically reconnected, and the reconnection is successful and the streaming will continue automatically.
  11. The real-time performance of network video streaming is extremely high, and the delay is extremely low. The delay time is about 100ms.
  12. After pushing the stream, in addition to using the rtmp address to access, it also supports direct hls/webrtc access, and you can directly open the browser to view the real-time screen.
  13. Support any version of Qt4/Qt5/Qt6, support any system (windows/linux/macos/android/embedded linux, etc.).

Guess you like

Origin blog.csdn.net/feiyangqingyun/article/details/130398942