WebRTC学习之十:最简单的视频聊天(使用WebRtcVideoEngine2)
来源:互联网 发布:手机网络创业 编辑:程序博客网 时间:2024/06/06 17:38
这篇在上篇WebRTC学习之九:摄像头的捕捉和显示 的基础上修改而来。上篇中主要使用了WebRtcVideoEngine2中的WebRtcVideoCapturer类,而本篇中主要使用了WebRtcVideoEngine2中的WebRtcVideoChannel2类。
一.环境
参考:WebRTC学习之三:录音和播放
二.实现
在WebRTC学习之四:最简单的语音聊天 中我们是通过实现Transport类的两个纯虚函数SendRtp和SendRtcp,并在它们的实现中调用通信协议的发送函数将数据发送出去。然而WebRtcVideoChannel2类继承自Transport,实现了SendRtp和SendRtcp,并在实现中分别调用了MediaChannel类的SendPacket和SendRtcp函数。MediaChannel类也是WebRtcVideoChannel2的基类。SendPacket和SendRtcp函数最终调用的是MediaChannel类中NetworkInterface接口的同名函数SendPacket和SendRtcp。因此我们只需要去继承NetworkInterface接口,实现其中的函数SendPacket和SendRtcp,并在它们的实现中调用通信协议的发送函数将数据发送出去。
mynetworkinterface.h
#ifndef MYNETWORKINTERFACE_H#define MYNETWORKINTERFACE_H#include <QUdpSocket>#include "webrtc/media/base/mediachannel.h"#include "webrtc/modules/rtp_rtcp/source/byte_io.h"class MyNetworkInterface:public QObject,public cricket::MediaChannel::NetworkInterface{ Q_OBJECTpublic: MyNetworkInterface(); ~MyNetworkInterface(); void setLocalReceiver(int port); void stopRecieve(); void setSendDestination(QString ip, int port); void stopSend(); // NetworkInterface functions override bool SendPacket(rtc::CopyOnWriteBuffer* packet,const rtc::PacketOptions& options) override; bool SendRtcp(rtc::CopyOnWriteBuffer* packet,const rtc::PacketOptions& options) override; int SetOption(SocketType type, rtc::Socket::Option opt,int option) override { return 0; }private: QUdpSocket * udpsocketSendRTP; QUdpSocket * udpsocketSendRTCP; QUdpSocket * udpSocketRecvRTP; QUdpSocket * udpSocketRecvRTCP; QString destIP; int destPort; bool sendFlag; bool recvFlag;signals: void signalRecvRTPData(char *data,int length); void signalRecvRTCPData(char *data,int length); void signalSendRTPData(char *data,int length); void signalSendRTCPData(char *data,int length);private slots: void slotRTPReadPendingDatagrams(); void slotRTCPReadPendingDatagrams(); void slotSendRTPData(char *data,int length); void slotSendRTCPData(char *data,int length);};#endif // MYNETWORKINTERFACE_H
mynetworkinterface.cpp
#include "mynetworkinterface.h"#include "QDebug"#include <QTime>MyNetworkInterface::MyNetworkInterface() :destIP(""), destPort(0), sendFlag(true), recvFlag(true){ udpsocketSendRTP=new QUdpSocket(); udpSocketRecvRTP = new QUdpSocket(); udpsocketSendRTCP=new QUdpSocket(); udpSocketRecvRTCP = new QUdpSocket(); connect(udpSocketRecvRTP, SIGNAL(readyRead()), this, SLOT(slotRTPReadPendingDatagrams())); connect(udpSocketRecvRTCP, SIGNAL(readyRead()), this, SLOT(slotRTCPReadPendingDatagrams())); connect(this,SIGNAL(signalSendRTPData(char *,int)),this,SLOT(slotSendRTPData(char *,int))); connect(this,SIGNAL(signalSendRTCPData(char *,int)),this,SLOT(slotSendRTCPData(char *,int)));}MyNetworkInterface::~MyNetworkInterface(){ udpsocketSendRTP->deleteLater(); udpSocketRecvRTP->deleteLater(); udpsocketSendRTCP->deleteLater(); udpSocketRecvRTCP->deleteLater();}void MyNetworkInterface::setLocalReceiver(int port){ udpSocketRecvRTP->bind(port, QUdpSocket::ShareAddress); udpSocketRecvRTCP->bind(port+1, QUdpSocket::ShareAddress); recvFlag=true;}void MyNetworkInterface::stopRecieve(){ udpSocketRecvRTP->abort(); udpSocketRecvRTCP->abort(); recvFlag=false;}void MyNetworkInterface::setSendDestination(QString ip, int port){ destIP=ip; destPort=port; sendFlag=true;}void MyNetworkInterface::stopSend(){ sendFlag=false;}//为何不直接调用udpsocketSendRTP->writeDatagram,而用信号,是因为SendPacket在另一个线程里bool MyNetworkInterface::SendPacket(rtc::CopyOnWriteBuffer* packet,const rtc::PacketOptions& options){ Q_UNUSED(options); //测试发送的ssrc// uint8_t* packet1=(uint8_t*)data;// uint32_t ssrc = webrtc::ByteReader<uint32_t>::ReadBigEndian(&packet1[8]);// qDebug()<<"Send SSRC:"<<ssrc; if(sendFlag) emit signalSendRTPData((char*)packet->data<uint8_t>(),packet->size()); return true;}//为何不直接调用udpsocketSendRTCP->writeDatagram,而用信号,是因为SendRtcp在另一个线程里bool MyNetworkInterface::SendRtcp(rtc::CopyOnWriteBuffer* packet,const rtc::PacketOptions& options){ Q_UNUSED(options); if(sendFlag) emit signalSendRTCPData(packet->data<char>(),packet->size()); return true;}void MyNetworkInterface::slotSendRTPData(char *data,int length){ //测试发送的ssrc// uint8_t* packet1=(uint8_t*)data;// uint32_t ssrc = webrtc::ByteReader<uint32_t>::ReadBigEndian(&packet1[8]);// qDebug()<<"Send SSRC:"<<ssrc; udpsocketSendRTP->writeDatagram(data, length,QHostAddress(destIP), destPort);}//RTCP端口为RTP端口+1void MyNetworkInterface::slotSendRTCPData(char *data,int length){ udpsocketSendRTCP->writeDatagram(data, length,QHostAddress(destIP), destPort+1);}void MyNetworkInterface::slotRTPReadPendingDatagrams(){ QByteArray datagram; while (udpSocketRecvRTP->hasPendingDatagrams()&&recvFlag) { datagram.resize(udpSocketRecvRTP->pendingDatagramSize()); QHostAddress sender; quint16 senderPort; int size=udpSocketRecvRTP->readDatagram( datagram.data(), datagram.size(), &sender, &senderPort); if(size>0) { //测试接收的ssrc // uint8_t* packet1=(uint8_t*)data; // uint32_t ssrc = webrtc::ByteReader<uint32_t>::ReadBigEndian(&packet1[8]); // qDebug()<<"Receive SSRC:"<<ssrc; emit signalRecvRTPData(datagram.data(),datagram.size()); } }}void MyNetworkInterface::slotRTCPReadPendingDatagrams(){ QByteArray datagram; while (udpSocketRecvRTCP->hasPendingDatagrams()&&recvFlag) { datagram.resize(udpSocketRecvRTCP->pendingDatagramSize()); QHostAddress sender; quint16 senderPort; int size=udpSocketRecvRTCP->readDatagram( datagram.data(), datagram.size(), &sender, &senderPort); if(size>0) { emit signalRecvRTCPData(datagram.data(),datagram.size()); } }}
WebRtcVideoChannel2中有个专门设置网络接口的函数SetInterface,其参数就是NetworkInterface,这样视频数据发送就与WebRtcVideoChannel2关联起来了。
至于频数据接收,需要调用通信协议的接收函数,将接收到的数据传递到WebRtcVideoChannel2的OnPacketReceived和OnRtcpReceived函数,这两个函数是其基类对应纯虚函数的实现。那么解码后的frame我们如何获取呢?在WebRtcVideoChannel2中有个函数SetSink,它的第二个参数是rtc::VideoSinkInterface<VideoFrame>*,因此我们只需要继承rtc::VideoSinkInterface<VideoFrame>,并实现其中的void OnFrame(const VideoFrameT& frame) =0;纯虚函数,就能得到解码后的frame,然后转换成RGB格式,就可以显示到Qt界面了。
void MainWindow::OnFrame(const cricket::VideoFrame& frame){ //复制一份 const cricket::VideoFrame* frameTemp = frame.GetCopyWithRotationApplied(); frameTemp->ConvertToRgbBuffer(cricket::FOURCC_ARGB, receivedVideoImageData.get(), frameTemp->width()*frameTemp->height()*32/8, frameTemp->width()*32/8); QImage image(receivedVideoImageData.get(), frameTemp->width(), frameTemp->height(), QImage::Format_RGB32); //因为OnFrame在另一个线程,所以不直接显示,用信号中转一下,将显示放在GUI线程(主线程) if(!image.isNull()) emit signalReceivedFrame(image);}void MainWindow::slotReceivedFrame(const QImage &image){ ui->labelReceivedVideo->setPixmap(QPixmap::fromImage(image));}三.效果
本人光荣上镜,长相一般,用个口罩挡一下。大窗口是摄像头捕捉和显示的图像,小窗口是通过网络接收到并显示的图像。
- WebRTC学习之十:最简单的视频聊天(使用WebRtcVideoEngine2)
- WebRTC学习之四:最简单的语音聊天
- WebRTC之WebRtcVideoEngine2模块(六)
- WebRTC手记之WebRtcVideoEngine2模块
- WebRTC手记之WebRtcVideoEngine2模块
- WebRTC手记之WebRtcVideoEngine2模块
- webrtc/apprtc视频聊天
- 最简单的WebRTC示例
- 最简单的WebRTC示例
- WebRTC学习之六:本地视频采集
- FMS学习(六):视频聊天,其实很简单
- 最简单的聊天程序
- 使用Socket实现最简单的聊天功能
- Android IOS WebRTC 音视频开发总结(十)
- Chrome引入WebRTC支持视频聊天
- 在网站中内置WebRTC视频聊天
- WebRTC + JsSIP + freeSWITCH一对一视频聊天
- WebRTC之本地视频采集(三)
- tableview个人中心-布局思路
- oracle 查看用户所在的表空间
- 使用xpath提取极客学院python课程内容名称
- DTD 文档类型定义
- Cocos2d-JS中CollectionView实现不同大小Cell同时显示的技巧(ListView中添加显示标签)
- WebRTC学习之十:最简单的视频聊天(使用WebRtcVideoEngine2)
- request.getRequestDispatcher().forward(request,response)和response.sendRedirect()的区别
- 高斯牛顿迭代法
- LBS初体验----百度地图SDK
- js中转换json对象 js自带转换
- LTE建模仿真交流
- 《Android源码设计模式与实战》稳健性开闭原则学习笔记二
- AMP或SMP: Zynq SoC操作系统在无线应用中应考虑的因素
- int string 转换