mirror of
https://gitee.com/xia-chu/ZLMediaKit.git
synced 2026-05-06 10:57:50 +08:00
初步支持语音双向对讲webrtc插件
Some checks are pending
Android / build (push) Waiting to run
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
Docker / build (push) Waiting to run
Linux / build (push) Waiting to run
macOS / build (push) Waiting to run
Windows / build (push) Waiting to run
Some checks are pending
Android / build (push) Waiting to run
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
Docker / build (push) Waiting to run
Linux / build (push) Waiting to run
macOS / build (push) Waiting to run
Windows / build (push) Waiting to run
This commit is contained in:
parent
5efe843595
commit
5165ac4f74
20
webrtc/Sdp.h
20
webrtc/Sdp.h
@ -55,36 +55,36 @@ namespace mediakit {
|
|||||||
// k=* (encryption key)
|
// k=* (encryption key)
|
||||||
// a=* (zero or more media attribute lines)
|
// a=* (zero or more media attribute lines)
|
||||||
|
|
||||||
enum class RtpDirection {
|
enum class RtpDirection : int8_t {
|
||||||
invalid = -1,
|
invalid = -1,
|
||||||
// 只发送 [AUTO-TRANSLATED:d7e7fdb7]
|
// 只发送 [AUTO-TRANSLATED:d7e7fdb7]
|
||||||
// Send only
|
// Send only
|
||||||
sendonly,
|
sendonly = 1 << 0,
|
||||||
// 只接收 [AUTO-TRANSLATED:f75ca789]
|
// 只接收 [AUTO-TRANSLATED:f75ca789]
|
||||||
// Receive only
|
// Receive only
|
||||||
recvonly,
|
recvonly = 1 << 1,
|
||||||
// 同时发送接收 [AUTO-TRANSLATED:7f900ba1]
|
// 同时发送接收 [AUTO-TRANSLATED:7f900ba1]
|
||||||
// Send and receive simultaneously
|
// Send and receive simultaneously
|
||||||
sendrecv,
|
sendrecv = sendonly | recvonly,
|
||||||
// 禁止发送数据 [AUTO-TRANSLATED:6045b47e]
|
// 禁止发送数据 [AUTO-TRANSLATED:6045b47e]
|
||||||
// Prohibit sending data
|
// Prohibit sending data
|
||||||
inactive
|
inactive = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DtlsRole {
|
enum class DtlsRole : int8_t {
|
||||||
invalid = -1,
|
invalid = -1,
|
||||||
// 客户端 [AUTO-TRANSLATED:915417a2]
|
// 客户端 [AUTO-TRANSLATED:915417a2]
|
||||||
// Client
|
// Client
|
||||||
active,
|
active = 1 << 0,
|
||||||
// 服务端 [AUTO-TRANSLATED:03a80b18]
|
// 服务端 [AUTO-TRANSLATED:03a80b18]
|
||||||
// Server
|
// Server
|
||||||
passive,
|
passive = 1 << 1,
|
||||||
// 既可作做客户端也可以做服务端 [AUTO-TRANSLATED:5ab1162e]
|
// 既可作做客户端也可以做服务端 [AUTO-TRANSLATED:5ab1162e]
|
||||||
// Can be used as both client and server
|
// Can be used as both client and server
|
||||||
actpass,
|
actpass = active | passive,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class SdpType { invalid = -1, offer, answer };
|
enum class SdpType : int8_t { invalid = -1, offer, answer };
|
||||||
|
|
||||||
DtlsRole getDtlsRole(const std::string &str);
|
DtlsRole getDtlsRole(const std::string &str);
|
||||||
const char *getDtlsRoleString(DtlsRole role);
|
const char *getDtlsRoleString(DtlsRole role);
|
||||||
|
|||||||
171
webrtc/WebRtcTalk.cpp
Normal file
171
webrtc/WebRtcTalk.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by MIT-like license that can be found in the
|
||||||
|
* LICENSE file in the root of the source tree. All contributing project authors
|
||||||
|
* may be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "WebRtcTalk.h"
|
||||||
|
|
||||||
|
#include "Util/base64.h"
|
||||||
|
#include "Common/config.h"
|
||||||
|
#include "Extension/Factory.h"
|
||||||
|
#include "Common/MultiMediaSourceMuxer.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace toolkit;
|
||||||
|
|
||||||
|
namespace mediakit {
|
||||||
|
|
||||||
|
WebRtcTalk::Ptr WebRtcTalk::create(
|
||||||
|
const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src, const MediaInfo &info, WebRtcTransport::Role role,
|
||||||
|
WebRtcTransport::SignalingProtocols signaling_protocols) {
|
||||||
|
WebRtcTalk::Ptr ret(new WebRtcTalk(poller, src, info), [](WebRtcTalk *ptr) {
|
||||||
|
ptr->onDestory();
|
||||||
|
delete ptr;
|
||||||
|
});
|
||||||
|
ret->setRole(role);
|
||||||
|
ret->setSignalingProtocols(signaling_protocols);
|
||||||
|
ret->onCreate();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRtcTalk::WebRtcTalk(const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src, const MediaInfo &info)
|
||||||
|
: WebRtcTransportImp(poller) {
|
||||||
|
_media_info = info;
|
||||||
|
_play_src = src;
|
||||||
|
CHECK(src);
|
||||||
|
_demuxer = std::make_shared<RtspDemuxer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcTalk::onStartWebRTC() {
|
||||||
|
auto playSrc = _play_src.lock();
|
||||||
|
if (!playSrc) {
|
||||||
|
onShutdown(SockException(Err_shutdown, "rtsp media source was shutdown"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WebRtcTransportImp::onStartWebRTC();
|
||||||
|
// 不支持simulcast
|
||||||
|
CHECK(!_answer_sdp->supportSimulcast());
|
||||||
|
auto sdp = _answer_sdp->toRtspSdp();
|
||||||
|
_demuxer->loadSdp(sdp);
|
||||||
|
auto audio_track = _demuxer->getTrack(TrackAudio, false);
|
||||||
|
// 必须包含音频track
|
||||||
|
CHECK(audio_track);
|
||||||
|
audio_track->addDelegate([this](const Frame::Ptr &frame) {
|
||||||
|
// 发送对讲语音rtp流
|
||||||
|
_sender->inputFrame(frame);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
MediaSourceEvent::SendRtpArgs args;
|
||||||
|
args.con_type = MediaSourceEvent::SendRtpArgs::kVoiceTalk;
|
||||||
|
args.recv_stream_vhost = playSrc->getMediaTuple().vhost;
|
||||||
|
args.recv_stream_app = playSrc->getMediaTuple().app;
|
||||||
|
args.recv_stream_id = playSrc->getMediaTuple().stream;
|
||||||
|
auto url_args = Parser::parseArgs(_media_info.params);
|
||||||
|
args.data_type = static_cast<MediaSourceEvent::SendRtpArgs::DataType>(atoi(url_args["data_type"].data()));
|
||||||
|
args.only_audio = true;
|
||||||
|
args.pt = static_cast<uint8_t>(atoi(url_args["pt"].data()));
|
||||||
|
args.ssrc = url_args["ssrc"];
|
||||||
|
|
||||||
|
std::weak_ptr<WebRtcTalk> weak_self = static_pointer_cast<WebRtcTalk>(shared_from_this());
|
||||||
|
_sender = std::make_shared<RtpSender>(getPoller());
|
||||||
|
_sender->startSend(*(playSrc->getMuxer()), args, [weak_self](uint16_t local_port, const SockException &ex) {
|
||||||
|
if (!ex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (auto strong_self = weak_self.lock()) {
|
||||||
|
strong_self->onShutdown(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_sender->addTrack(audio_track);
|
||||||
|
_sender->addTrackCompleted();
|
||||||
|
|
||||||
|
if (canSendRtp()) {
|
||||||
|
playSrc->pause(false);
|
||||||
|
_reader = playSrc->getRing()->attach(getPoller(), true);
|
||||||
|
weak_ptr<WebRtcTalk> weak_self = static_pointer_cast<WebRtcTalk>(shared_from_this());
|
||||||
|
weak_ptr<Session> weak_session = static_pointer_cast<Session>(getSession());
|
||||||
|
_reader->setGetInfoCB([weak_session]() {
|
||||||
|
Any ret;
|
||||||
|
ret.set(static_pointer_cast<Session>(weak_session.lock()));
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pkt) {
|
||||||
|
auto strong_self = weak_self.lock();
|
||||||
|
if (!strong_self) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
pkt->for_each([&](const RtpPacket::Ptr &rtp) { strong_self->onSendRtp(rtp, ++i == pkt->size()); });
|
||||||
|
});
|
||||||
|
_reader->setDetachCB([weak_self]() {
|
||||||
|
auto strong_self = weak_self.lock();
|
||||||
|
if (!strong_self) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strong_self->onShutdown(SockException(Err_shutdown, "rtsp ring buffer detached"));
|
||||||
|
});
|
||||||
|
|
||||||
|
_reader->setMessageCB([weak_self](const toolkit::Any &data) {
|
||||||
|
auto strong_self = weak_self.lock();
|
||||||
|
if (!strong_self) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.is<Buffer>()) {
|
||||||
|
auto &buffer = data.get<Buffer>();
|
||||||
|
// PPID 51: 文本string [AUTO-TRANSLATED:69a8cf81]
|
||||||
|
// PPID 51: Text string
|
||||||
|
// PPID 53: 二进制 [AUTO-TRANSLATED:faf00c3e]
|
||||||
|
// PPID 53: Binary
|
||||||
|
strong_self->sendDatachannel(0, 51, buffer.data(), buffer.size());
|
||||||
|
} else {
|
||||||
|
WarnL << "Send unknown message type to webrtc player: " << data.type_name();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void WebRtcTalk::onDestory() {
|
||||||
|
auto duration = getDuration();
|
||||||
|
auto bytes_usage = getBytesUsage();
|
||||||
|
// 流量统计事件广播 [AUTO-TRANSLATED:6b0b1234]
|
||||||
|
// Traffic statistics event broadcast
|
||||||
|
GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold);
|
||||||
|
auto session = getSession();
|
||||||
|
if (_reader && session) {
|
||||||
|
WarnL << "RTC对讲(" << _media_info.shortUrl() << ")结束播放,耗时(s):" << duration;
|
||||||
|
if (bytes_usage >= iFlowThreshold * 1024) {
|
||||||
|
NOTICE_EMIT(BroadcastFlowReportArgs, Broadcast::kBroadcastFlowReport, _media_info, bytes_usage, duration, true, *session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WebRtcTransportImp::onDestory();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcTalk::onRtcConfigure(RtcConfigure &configure) const {
|
||||||
|
WebRtcTransportImp::onRtcConfigure(configure);
|
||||||
|
auto playSrc = _play_src.lock();
|
||||||
|
if (playSrc) {
|
||||||
|
configure.setPlayRtspInfo(playSrc->getSdp());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不接收视频
|
||||||
|
configure.video.direction = static_cast<RtpDirection>(static_cast<int8_t>(configure.video.direction) & ~static_cast<int8_t>(RtpDirection::recvonly));
|
||||||
|
// 开启音频接收
|
||||||
|
configure.audio.direction = static_cast<RtpDirection>(static_cast<int8_t>(configure.audio.direction) | static_cast<int8_t>(RtpDirection::recvonly));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRtcTalk::onRecvRtp(MediaTrack &track, const std::string &rid, RtpPacket::Ptr rtp) {
|
||||||
|
// rtp解析为音频,视频丢弃
|
||||||
|
if (rtp->type == TrackAudio) {
|
||||||
|
_demuxer->inputRtp(rtp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace mediakit
|
||||||
56
webrtc/WebRtcTalk.h
Normal file
56
webrtc/WebRtcTalk.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by MIT-like license that can be found in the
|
||||||
|
* LICENSE file in the root of the source tree. All contributing project authors
|
||||||
|
* may be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZLMEDIAKIT_WEBRTC_TALK_H
|
||||||
|
#define ZLMEDIAKIT_WEBRTC_TALK_H
|
||||||
|
|
||||||
|
#include "WebRtcTransport.h"
|
||||||
|
#include "Rtsp/RtspMediaSource.h"
|
||||||
|
#include "Rtsp/RtspDemuxer.h"
|
||||||
|
#include "Rtp/RtpSender.h"
|
||||||
|
|
||||||
|
namespace mediakit {
|
||||||
|
|
||||||
|
class WebRtcTalk : public WebRtcTransportImp {
|
||||||
|
public:
|
||||||
|
using Ptr = std::shared_ptr<WebRtcTalk>;
|
||||||
|
static Ptr create(const toolkit::EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src, const MediaInfo &info,
|
||||||
|
WebRtcTransport::Role role, WebRtcTransport::SignalingProtocols signaling_protocols);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
///////WebRtcTransportImp override///////
|
||||||
|
void onStartWebRTC() override;
|
||||||
|
void onDestory() override;
|
||||||
|
void onRtcConfigure(RtcConfigure &configure) const override;
|
||||||
|
void onRecvRtp(MediaTrack &track, const std::string &rid, RtpPacket::Ptr rtp) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
WebRtcTalk(const toolkit::EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src, const MediaInfo &info);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// 媒体相关元数据 [AUTO-TRANSLATED:f4cf8045]
|
||||||
|
// Media related metadata
|
||||||
|
MediaInfo _media_info;
|
||||||
|
// 播放的rtsp源 [AUTO-TRANSLATED:9963eed1]
|
||||||
|
// Playing rtsp source
|
||||||
|
std::weak_ptr<RtspMediaSource> _play_src;
|
||||||
|
|
||||||
|
// 播放rtsp源的reader对象 [AUTO-TRANSLATED:7b305055]
|
||||||
|
// Reader object for playing rtsp source
|
||||||
|
RtspMediaSource::RingType::RingReader::Ptr _reader;
|
||||||
|
|
||||||
|
// 解析对讲语音rtp流为帧数据
|
||||||
|
RtspDemuxer::Ptr _demuxer;
|
||||||
|
// 打包语音帧数据为特定rtp并回复过去
|
||||||
|
RtpSender::Ptr _sender;
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace mediakit
|
||||||
|
#endif // ZLMEDIAKIT_WEBRTC_TALK_H
|
||||||
@ -28,6 +28,7 @@
|
|||||||
#include "WebRtcEchoTest.h"
|
#include "WebRtcEchoTest.h"
|
||||||
#include "WebRtcPlayer.h"
|
#include "WebRtcPlayer.h"
|
||||||
#include "WebRtcPusher.h"
|
#include "WebRtcPusher.h"
|
||||||
|
#include "WebRtcTalk.h"
|
||||||
#include "Rtsp/RtspMediaSourceImp.h"
|
#include "Rtsp/RtspMediaSourceImp.h"
|
||||||
|
|
||||||
#define RTP_SSRC_OFFSET 1
|
#define RTP_SSRC_OFFSET 1
|
||||||
@ -1726,6 +1727,7 @@ void push_plugin(SocketHelper& sender, const WebRtcArgs &args, const onCreateWeb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
void play_plugin(SocketHelper &sender, const WebRtcArgs &args, const onCreateWebRtc &cb) {
|
void play_plugin(SocketHelper &sender, const WebRtcArgs &args, const onCreateWebRtc &cb) {
|
||||||
|
|
||||||
MediaInfo info(args["url"]);
|
MediaInfo info(args["url"]);
|
||||||
@ -1748,7 +1750,7 @@ void play_plugin(SocketHelper &sender, const WebRtcArgs &args, const onCreateWeb
|
|||||||
// 还原成rtc,目的是为了hook时识别哪种播放协议 [AUTO-TRANSLATED:fe8dd2dc]
|
// 还原成rtc,目的是为了hook时识别哪种播放协议 [AUTO-TRANSLATED:fe8dd2dc]
|
||||||
// Restore to RTC, the purpose is to identify which playback protocol during hooking
|
// Restore to RTC, the purpose is to identify which playback protocol during hooking
|
||||||
info.schema = "rtc";
|
info.schema = "rtc";
|
||||||
auto rtc = WebRtcPlayer::create(EventPollerPool::Instance().getPoller(), src, info,
|
auto rtc = Type::create(EventPollerPool::Instance().getPoller(), src, info,
|
||||||
WebRtcTransport::Role::PEER, WebRtcTransport::SignalingProtocols::WHEP_WHIP);
|
WebRtcTransport::Role::PEER, WebRtcTransport::SignalingProtocols::WHEP_WHIP);
|
||||||
cb(*rtc);
|
cb(*rtc);
|
||||||
});
|
});
|
||||||
@ -1831,7 +1833,9 @@ static onceToken s_rtc_auto_register([]() {
|
|||||||
WebRtcPluginManager::Instance().registerPlugin("echo", echo_plugin);
|
WebRtcPluginManager::Instance().registerPlugin("echo", echo_plugin);
|
||||||
#endif
|
#endif
|
||||||
WebRtcPluginManager::Instance().registerPlugin("push", push_plugin);
|
WebRtcPluginManager::Instance().registerPlugin("push", push_plugin);
|
||||||
WebRtcPluginManager::Instance().registerPlugin("play", play_plugin);
|
WebRtcPluginManager::Instance().registerPlugin("play", play_plugin<WebRtcPlayer>);
|
||||||
|
WebRtcPluginManager::Instance().registerPlugin("talk", play_plugin<WebRtcTalk>);
|
||||||
|
|
||||||
WebRtcPluginManager::Instance().setListener([](SocketHelper& sender, const std::string &type, const WebRtcArgs &args, const WebRtcInterface &rtc) {
|
WebRtcPluginManager::Instance().setListener([](SocketHelper& sender, const std::string &type, const WebRtcArgs &args, const WebRtcInterface &rtc) {
|
||||||
setWebRtcArgs(args, const_cast<WebRtcInterface&>(rtc));
|
setWebRtcArgs(args, const_cast<WebRtcInterface&>(rtc));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -66,8 +66,9 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<label for="method">method(play or push or echo):</label>
|
<label for="method">method:</label>
|
||||||
<input type="radio" name="method" value="echo" >echo
|
<input type="radio" name="method" value="echo" >echo
|
||||||
|
<input type="radio" name="method" value="talk" >talk
|
||||||
<input type="radio" name="method" value="push" >push
|
<input type="radio" name="method" value="push" >push
|
||||||
<input type="radio" name="method" value="play" checked = true>play
|
<input type="radio" name="method" value="play" checked = true>play
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user