音视频项目-异地情侣影院-可写简历

视频讲解及源码领取:https://www.bilibili.com/video/BV1PSareYE9L/

1 编译器环境

  • 可以正常编译运行的环境:QT6.5.3和QT6.6.3 mingw64编译器
  • 有异常的环境:QT6.7有bug,不能正常初始化MediaPlayer

2 项目框架图

这里我们重点讲解播放链接推送、播放控制推送和文字聊天推送,语音聊天大家可以自行研究。

server有QList<QTcpSocket*> clients; 保存客户端列表。

以play播放命令为例,播放器1⻆色为server和client1,由播放器1发起play播放命令,则流程如下所示(播放器2也一样可以发起play播放命令)

3 信令分析

信令主要类型:

src:播放源推送

  • 网络流链接推送:src+net+url
  • 本地文件路径推送:src+lcl+ur

snc:播放控制命令

  • 播放命令 snc+play+position,范例: sncplay12693
  • 暂停命令 snc+paus+position,范例: sncpaus12693
  • 跳转命令 snc+seek+position,范例: sncseek12693
  • 同步命令 snc+sync+position,范例: sncsync12693,每秒server发送一次给所有client
  • 停止命令 snc+stop,范例: sncstop

msg:文字聊天

  • 文字聊天推送:msg+username: + content

后续的讲解,只讲解client的推送和接收数据,server直接参考前面项目框架图的讲解。

3.1 播放链接推送

src:播放链接

1.推送网络流链接命令src+net+url,范例:

srcnethttps://stream7.iqilu.com/10339/upload_transcode/202002/09/2020020910490

2N3v5Vpxuvb.mp4

2.推送本地文件链接命令src+lcl+url,此时需要各个端本地有对应的文件路径,范例:

srclcl/E:/rec_video/00-音视频高级教程-课程简介.mp

这里我们只讲解网络流链接。

推送

操作:文件->打开链接->触发

void MainWindow::on_actionOpen_URL_triggered()
{
   openURL* urlWindow = new openURL(this); //退出对话框
   urlWindow->setWindowTitle("Open URL");
   urlWindow->show();
   // 绑定信号槽,如果对话框点击ok将触发setVideoSource函数的调用
   connect(urlWindow, SIGNAL(urlSet(QString)), this, SLOT(setVideoSource(QString)));
}

这个是弹出的对话框

在对话框输入可以播放的url,然后点击ok,点击ok后触发openURL::on_openURLbuttonBox_accepted(),在该函数里获取url并发送信号urlSet(url)。

void openURL::on_openURLbuttonBox_accepted()
{
   QString url = ui->urlInput->text();
   emit urlSet(url);
}

MainWindow::setVideoSource()响应,这里把url播放链接推送给所有的client。

void MainWindow::setVideoSource(QString url)
{
   qDebug() << "MainWindow::setVideoSource url: " << url;
   if (client) client->writeToServer(url, "srcnet");
   else player->setSource(QUrl(url));
}

接收

接收端

connect(client, &Client::remoteSetVideoSource, this,

&MainWindow::remoteSetVideoSource);

void Client::readFromServer()
{
   while (socket->canReadLine())
   {
     QByteArray buffer = socket->readLine();
     QString header = buffer.mid(0, 3);
     QString content = buffer.mid(3).trimmed();
     qInfo() << "Client::readFromServer -- header: " << header << ", content: " << content;
     if (header == "snc")
     {
       ..........
     }
     else if (header == "msg")
     {
       ...........
     }
     else if (header == "src")
     {
       QString srcType = content.mid(0, 3);
       content = content.mid(3);
       if (srcType == "net") //⽹络流
       {
         emit remoteSetVideoSource(content);
       }
       if (srcType == "lcl") //本地⽂件
       {
         emit remoteSetLocalVideoSource(content);
       }
       emit newChatMsg("Souce set:" + content);
     }

MainWindow::remoteSetVideoSource

void MainWindow::remoteSetVideoSource(QString src)
{
   player->setSource(QUrl(src));
}

3.2 播放控制推送

snc:播放控制

1. 播放命令 snc+play+position,范例: sncplay12693

2. 暂停命令 snc+paus+position,范例: sncpaus12693

3. 跳转命令 snc+seek+position,范例: sncseek12693

4. 同步命令 snc+sync+position,范例: sncsync12693,每秒server发送一次给所有client

5. 停止命令 snc+stop,范例: sncstop

播放、暂停、跳转、停止我们都很容易理解,重点是同步命令snc+sync+position,server怎么做的,client收到sync命令后又是怎么处理的

推送

难点在于同步推送,作为server的一端,每秒推送一次当前进度给其他client

server每秒获取一次当前播放进度并推送给所有的client

// 时间戳线程,server端定时将播放进度发送给所有client
void MainWindow::sendTimestampThreaded()
{
   if (server)
   {
     QThread *sendTimestampThread = QThread::create(&MainWindow::sendTimestamp, this);
     connect(this, &MainWindow::destroyed, sendTimestampThread, &QThread::quit);
     connect(sendTimestampThread, &QThread::finished, sendTimestampThread, &QThread::deleteLater);
     sendTimestampThread->start();
   }
}

// server每秒获取当前播放进度推送给其他client
void MainWindow::sendTimestamp()
{
   while(true)
   {
     // qInfo() << player->position();
     server->writeToClients(QString::number(player->position()), "sncsync");
     QObject().thread()->usleep(1000*1000*1); //every 1 second send sync info
   }
}

接收

client收到snssync 同步信息

//client接收server的信息
void Client::readFromServer()
{
   while (socket->canReadLine())
   {
     QByteArray buffer = socket->readLine();
     QString header = buffer.mid(0, 3);
     QString content = buffer.mid(3).trimmed();
     qInfo() << "Client::readFromServer -- header: " << header << ", content: " << content;
     if (header == "snc")
     {
       qInfo() << "Sync info from Server: " << content;
       QString syncType = content.mid(0, 4);
       qint64 position = content.mid(4).toLongLong();
       if (syncType == "play")
       {
         emit remotePlay(position);
       }
       else if (syncType == "paus")
       {
         emit remotePause(position);
       }
       else if (syncType == "stop")
       {
         emit remoteStop();
       }
       else if (syncType == "seek")
       {
         emit remoteSeek(position);
       }
       else if (syncType == "sync")
       {
         emit remoteSync(position);
       }
     }
     ............

remoteSync 找到这个绑定的响应函数 MainWindow::remoteSync,具体的同步算法也很简单,误差200ms内不做处理,超过200ms则seek到对应的位置。

void MainWindow::remoteSync(qint64 position)
{
   //误差200ms内不做处理
   if (abs(player->position() - position) > 200) player->setPosition(position);
}

3.3 文字聊天推送

聊天消息命令:msg+username: + content,⽐如username为server,范例为msgserver: 来了,显示的时候只需要显示server: 来了

client 推送 -> server ,server通过tcp发送给每个client -> client接收。

这里我们只讲client 推送 和 client接收。

推送

void MainWindow::on_chatInput_returnPressed()
{
     if (ui->chatInput->text() != "")
    {
       if (client) client->writeToServer(ui->chatInput->text(), "msg");
       ui->chatInput->clear();
    }
}

接收

client收到消息

void Client::readFromServer()
{
    while (socket->canReadLine())
    {
      QByteArray buffer = socket->readLine();
      QString header = buffer.mid(0, 3);
      QString content = buffer.mid(3).trimmed();
      qInfo() << "Client::readFromServer -- header: " << header << ", content: " << content;
      if (header == "snc")
      {
        ............
      }
      else if (header == "msg")
     {
     qInfo() << "Message from Server: " << content;
     emit newChatMsg(content); //收到消息
     }
     .......
 
  }
}

然后触发void MainWindow::newChatMsg(QString msg)函数的调用

void MainWindow::newChatMsg(QString msg)
 {
   QListWidgetItem* item = new QListWidgetItem(ui->chatWidget);
   item->setText(msg); 
   ui->chatWidget->addItem(item); //显示当条消息
   ui->chatWidget->scrollToBottom();
 }

4 扩展思路

1. 当前server其实也是在一个播放端上的,可以考虑把server抽取出来部署到公网,此时就需要选择其中一个client作为master,由这个master 发送同步信息。

2. 将mediaplayer改成使用ffmpeg

3. 增加变速机制等

4. 支持播放列表拉取

5 语音聊天框架

待续,比较降噪

#简历被挂麻了,求建议##秋招##校招##c++##简历中的项目经历要怎么写#
全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务