流程图
- 因为erizon::VideoFrameConstructor(继承于VideoFrameSource)作为视频源,从mediaStream把数据接收过来,VideoFrameSource中,然后分别存到owt_base.InternalServer的m_sourceMap(以publicTrackId = transportId + ‘-’ + track.id 为key,addon.VideoFrameSource 为value)(这里什么时候使用???)中和Connections中(这里是子在视频订阅的时候用,linkup的时候使用)
VideoFrameSource存放在connections中
/*{ConnectionID: {type: 'webrtc' | 'avstream' | 'recording' | 'internal',
direction: 'in' | 'out',
audioFrom: ConnectionID | undefined,
videoFrom: ConnectionID | undefined,
connnection: WebRtcConnection | InternalOut | RTSPConnectionOut
}
}
*/
connection 存放的就是VideoFrameSource
-
mediaUpdate, 通过RpcClient.remoteCast, 消息广播给controller, ConferenceAgent
-
trackUpdate, 通过RpcClient.remoteCast, 消息广播给controller,ConferenceAgent
1. track-added
dist/webrtc_agent/webrtc/wrtcConnection.js
const setupTransport = function (mid) {
...
if (rids) {
...
} else {
// 走这里的代码
// No simulcast
if (!trackMap.has(mid)) {
// 1. Connection wrtc
trackMap.set(mid, new WrtcStream(mid, wrtc, direction, trackSettings));
...
// 3. Notify new track
on_track({
type: 'track-added',
track: trackMap.get(mid),
operationId: opSettings.operationId,
mid: mid
});
} else {
log.warn(`Conflict trackId ${mid} for ${wrtcId}`);
}
}
return opSettings.operationId;
};
trackMap 属性——存放了WrtcStream
// composedId => WrtcStream
var trackMap = new Map();
composeId
const composeId = function (mid, rid) {
return mid + ':' + rid;
};
1.1 on_track
dist/webrtc_agent/webrtc/wrtcConnection.js
module.exports = function (spec, on_status, on_track) {
...
}
1.2 on_track= function onTrack(trackInfo)
dist/webrtc_agent/webrtc/index.js
var connection = new WrtcConnection({
connectionId: transportId,
threadPool: threadPool,
ioThreadPool: ioThreadPool,
network_interfaces: global.config.webrtc.network_interfaces,
owner,
}, function onTransportStatus(status) {
notifyTransportStatus(controller, transportId, status);
}, function onTrack(trackInfo) {
handleTrackInfo(transportId, trackInfo, controller);
});
2. ======WebrtcNode.handleTrackInfo
dist/webrtc_agent/webrtc/index.js
// trackInfo 就是on__track 回调回来
var handleTrackInfo = function (transportId, trackInfo, controller) {
var publicTrackId;
var updateInfo;
if (trackInfo.type === 'track-added') {
// Generate public track ID
const track = trackInfo.track; // track是WrtcStream
// track.id 就是mid
publicTrackId = transportId + '-' + track.id;
if (mediaTracks.has(publicTrackId)) {
log.error('Conflict public track id:', publicTrackId, transportId, track.id);
return;
}
mediaTracks.set(publicTrackId, track);
mappingPublicId.get(transportId).set(track.id, publicTrackId);
if (track.direction === 'in') {
// 1. source, 返回的是addon.Source
// WrtcStream.sender()
// WrtcStream 就是在小结4中创建
const trackSource = track.sender();
// 2. add source
// InternalConnectionRouter router
router.addLocalSource(publicTrackId, 'webrtc', trackSource)
.catch(e => log.warn('Unexpected error during track add:', e));
} else {
router.addLocalDestination(publicTrackId, 'webrtc', track)
.catch(e => log.warn('Unexpected error during track add:', e));
}
// 3. Bind media-update handler
track.on('media-update', (jsonUpdate) => {
log.debug('notifyMediaUpdate:', publicTrackId, jsonUpdate);
notifyMediaUpdate(controller, publicTrackId, track.direction, JSON.parse(jsonUpdate));
});
// 4. Notify controller
const mediaType = track.format('audio') ? 'audio' : 'video';
updateInfo = {
type: 'track-added',
trackId: publicTrackId,
mediaType: track.format('audio') ? 'audio' : 'video',
mediaFormat: track.format(mediaType),
direction: track.direction,
operationId: trackInfo.operationId,
mid: trackInfo.mid,
rid: trackInfo.rid,
active: true,
};
log.debug('notifyTrackUpdate', controller, publicTrackId, updateInfo);
notifyTrackUpdate(controller, transportId, updateInfo);
} else if (trackInfo.type === 'track-removed') {
...
} else if (trackInfo.type === 'tracks-complete') {
updateInfo = {
type: 'tracks-complete',
operationId: trackInfo.operationId
};
notifyTrackUpdate(controller, transportId, updateInfo);
}
};
track==WrtcStream
publicTrackId = transportId + ‘-’ + track.id;
track.id, (rids不空,composedId=mid:rids)
track.id, (rids为空,composedId=mid)
mediaTracks——{ publicTrackId => WrtcTrack }
// Map { publicTrackId => WrtcTrack }
var mediaTracks = new Map();
mediaTracks.set(publicTrackId, track);
mappingPublicId——{ transportId => Map { trackId => publicTrackId } }
// Map { transportId => Map { trackId => publicTrackId } }
var mappingPublicId = new Map();
mappingPublicId.get(transportId).set(track.id, publicTrackId);
??? var router = new InternalConnectionRouter(global.config.internal);
2.1 ==========WrtcStream.sender——获取source,VideoFrameSource
dist-debug/webrtc_agent/webrtc/wrtcConnection.js
sender(track) {
let sender = null;
if (track === 'audio' && this.audioFrameConstructor) {
...
} else if (track === 'video' && this.videoFrameConstructor) {
...
// track = null
} else if (!track) {
// 走这里的流程
let parent = (this.audioFrameConstructor || this.videoFrameConstructor);
// parent.source() 返回addon.VideoFrameSource, 调用native
// addon.VideoFrameSource sender,子类FrameSource
sender = parent.source();
//
// 这里赋值了parent
sender.parent = parent;
} else {
log.error('sender error');
}
if (sender) {
// 定义两个函数
sender.addDestination = (track, dest) => {
// parent 就是VideoFrameConstructor
sender.parent.addDestination(dest);
};
sender.removeDestination = (track, dest) => {
sender.parent.removeDestination(dest);
};
///
}
return sender;
}
2.1.1 NAN_METHOD(VideoFrameConstructor::source)
source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc
NAN_METHOD(VideoFrameConstructor::source) {
const int argc = 1;
v8::Local<v8::Value> argv[argc] = {info.Holder()};
v8::Local<v8::Function> cons = Nan::New(VideoFrameSource::constructor);
// 创建 VideoFrameSource对象
info.GetReturnValue().Set(Nan::NewInstance(cons, 1, argv).ToLocalChecked());
}
2.1.2 =====NAN_METHOD(VideoFrameSource::New)
source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc
NAN_METHOD(VideoFrameSource::New) {
if (info.Length() == 1) {
VideoFrameConstructor* parent = Nan::ObjectWrap::Unwrap<VideoFrameConstructor>(
Nan::To<v8::Object>(info[0]).ToLocalChecked());
VideoFrameSource* obj = new VideoFrameSource();
// VideoFrameConstructor* parent 的 me 就是owt_base::VideoFrameConstructor
// VideoFrameSource* obj me 就是owt_base::VideoFrameConstructor
obj->me = parent->me;
// obj->src 就是owt_base::FrameSource
// owt_base::VideoFrameConstructor 继承了owt_base::FrameSource
obj->src = obj->me;
obj->Wrap(info.This());
info.GetReturnValue().Set(info.This());
}
}
2.1.3 addon.VideoFrameSource
source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.h
class VideoFrameSource : public FrameSource {
public:
static NAN_MODULE_INIT(Init);
owt_base::VideoFrameConstructor* me;
private:
VideoFrameSource() {};
~VideoFrameSource() {};
static NAN_METHOD(New);
static Nan::Persistent<v8::Function> constructor;
friend class VideoFrameConstructor;
};
2.1.4 addon.FrameSource
source/agent/addons/common/MediaFramePipelineWrapper.h
/*
* Wrapper class of owt_base::FrameSource
*/
class FrameSource : public node::ObjectWrap{
public:
owt_base::FrameSource* src;
};
2.1.5 owt_base::FrameSource
======sender.parent = parent;
sender 就是VideoFrameSource
parent 就是VideoFrameConstructor
2.2 InternalConnectionRouter.addLocalSource
dist-debug/webrtc_agent/webrtc/internalConnectionRouter.js
/*
* @param {string} id ID for source connection (stream),publicTrackId
* @param {string} type Type description for connection
* @param {FrameSource} source Wrapper class for FrameSource
*/
// id = publicTrackId = transportId + '-' + track.id;
// type = 'webrtc',
// trackSource 就是2.1 创建的VideoFrameSource
addLocalSource(id, type, source) {
const isNativeSource = (type === 'quic' || type === 'mediabridge');
// addon.InternalServer internalServer
// 1.
this.internalServer.addSource(id, source, isNativeSource);
// 2. Connections connnections
return this.connections.addConnection(id, type, '', source, 'in');
}
2.2.1 internalServer 赋值const {InternalServer, InternalClient} = internalIO;
dist-debug/webrtc_agent/webrtc/internalConnectionRouter.js
source/agent/addons/internalIO/InternalServerWrapper.h
source/agent/addons/internalIO/InternalClientWrapper.h
2.2.2 NAN_METHOD(addSource)
source/agent/addons/internalIO/InternalServerWrapper.cc
NAN_METHOD(InternalServer::addSource) {
InternalServer* obj = ObjectWrap::Unwrap<InternalServer>(info.Holder());
owt_base::InternalServer* me = obj->me;
Nan::Utf8String param0(Nan::To<v8::String>(info[0]).ToLocalChecked());
// streamId = publicTrackId
std::string streamId = std::string(*param0);
bool isNanSource(false);
if (info.Length() >= 3) {
isNanSource = Nan::To<bool>(info[2]).FromJust();
}
owt_base::FrameSource* src(nullptr);
if (isNanSource) {
NanFrameNode* param = Nan::ObjectWrap::Unwrap<NanFrameNode>(
Nan::To<v8::Object>(info[1]).ToLocalChecked());
src = param->FrameSource();
} else {
// 走了这里流程,FrameSource 就是 2.1 创建的VideoFrameSource
FrameSource* param = ObjectWrap::Unwrap<FrameSource>(
Nan::To<v8::Object>(info[1]).ToLocalChecked());
// 从FrameSource中获取owt_base::FrameSource src
src = param->src;
}
// owt_base::InternalServer::addSource
me->addSource(streamId, src);
}
2.2.3 owt_base::InternalServer::addSource
source/core/owt_base/internal/InternalServer.cpp
// streamdId = publicTrackId
// source owt_base::FrameSource, 就是owt_base::VideoFrameConstructor
bool InternalServer::addSource(const std::string& streamId, FrameSource* src)
{
ELOG_DEBUG("addSource %s, %p", streamId.c_str(), src);
if (m_sourceMap.count(streamId)) {
ELOG_WARN("Source for stream:%s already added", streamId.c_str());
return false;
}
m_sourceMap[streamId] = src;
return true;
}
owt.InternalServer - addSource 99649f7149c44673ad7c8338e1494c4f-0, 0x61c3d68
????? InternalServer::onSessionData
source/core/owt_base/internal/InternalServer.cpp
2.3.4 Connections.addConnection
dist-debug/webrtc_agent/webrtc/connections.js
// addConnection(id, type, '', source, 'in');
// connectionId = id = publicTrackId
// connectionType = type = 'webrtc'
// connectionController = ''
// conn = VideoFrameSource
// direction = 'in'
that.addConnection = function (connectionId, connectionType, connectionController, conn, direction) {
log.debug('Add connection:', connectionId, connectionType, connectionController);
if (connections[connectionId]) {
log.error('Connection already exists:'+connectionId);
return Promise.reject({type: 'failed', reason: 'Connection already exists:'+connectionId});
}
connections[connectionId] = {
type: connectionType,
direction: direction,
audioFrom: undefined,
videoFrom: undefined,
connection: conn,
controller: connectionController
};
return Promise.resolve('ok');
};
这里存放在connections的数据包括VideoFrameSource,在linkupConnection 的时候调用
connections
/*{ConnectionID: {type: 'webrtc' | 'avstream' | 'recording' | 'internal',
direction: 'in' | 'out',
audioFrom: ConnectionID | undefined,
videoFrom: ConnectionID | undefined,
connnection: WebRtcConnection | InternalOut | RTSPConnectionOut
}
}
*/
connections = {};
log
2023-04-05T17:31:30.615 - DEBUG: Connections - Add connection: 99649f7149c44673ad7c8338e1494c4f-0 webrtc
log——conn
2023-04-26T21:23:41.396 - DEBUG: Connections - AudioFrameSource {
parent: AudioFrameConstructor {},
addDestination: [Function (anonymous)],
removeDestination: [Function (anonymous)]
}
2023-04-26T21:23:41.399 - DEBUG: Connections - VideoFrameSource {
parent: VideoFrameConstructor {},
addDestination: [Function (anonymous)],
removeDestination: [Function (anonymous)]
}
2.3 WebrtcNode.notifyMediaUpdate
从WrtcStream 注册回调到C++,然后C++回调到WrtcStream, 通过EventEmitter.emit 转发到WebrtcNode。
var notifyMediaUpdate = function (controller, publicTrackId, direction, mediaUpdate) {
rpcClient.remoteCast(controller, 'onMediaUpdate', [publicTrackId, direction, mediaUpdate]);
...
};
log
2023-04-26 21:54:07,908 - DEBUG: owt.VideoFrameConstructor - onVideoInfo {"video": {"parameters": {"resolution": {"width":0, "height":0}}}} 0x57708f0
2023-04-26T21:54:07.908 - INFO: WrtcConnection - _onMediaUpdate:{"video": {"parameters": {"resolution": {"width":0, "height":0}}}}
WrtcStream._onMediaUpdate
dist/webrtc_agent/webrtc/index.js
_onMediaUpdate(jsonUpdate) {
log.info('_onMediaUpdate:' + jsonUpdate);
this.emit('media-update', jsonUpdate);
}
dist/webrtc_agent/webrtc/wrtcConnection.js
WrtcStream
{
this.videoFrameConstructor = new VideoFrameConstructor(
this._onMediaUpdate.bind(this), video.transportcc, wrtc.callBase);
...
}
source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc
NAN_METHOD(VideoFrameConstructor::New) {
.......
// 第一个参数就是 this._onMediaUpdate.bind(this),
obj->Callback_ = new Nan::Callback(info[0].As<Function>());
...
}
NAUV_WORK_CB(VideoFrameConstructor::Callback)
source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc
NAUV_WORK_CB(VideoFrameConstructor::Callback) {
Nan::HandleScope scope;
VideoFrameConstructor* obj = reinterpret_cast<VideoFrameConstructor*>(async->data);
if (!obj || obj->me == NULL)
return;
boost::mutex::scoped_lock lock(obj->mutex);
// for循环,从queue中读取消息,调用Callback_,回调到js的_onMediaUpdate
while (!obj->videoInfoMsgs.empty()) {
Local<Value> args[] = {Nan::New(obj->videoInfoMsgs.front().c_str()).ToLocalChecked()};
obj->asyncResource_->runInAsyncScope(Nan::GetCurrentContext()->Global(), obj->Callback_->GetFunction(), 1, args);
obj->videoInfoMsgs.pop();
}
}
VideoFrameConstructor::onVideoInfo
source/agent/webrtc/rtcFrame/VideoFrameConstructorWrapper.cc
void VideoFrameConstructor::onVideoInfo(const std::string& message) {
boost::mutex::scoped_lock lock(mutex);
// std::queue<std::string> videoInfoMsgs;
// 把回调的消息放到queue中,
this->videoInfoMsgs.push(message);
async_.data = this;
uv_async_send(&async_);
}
2.4 WebrtcNode.notifyTrackUpdate
dist/webrtc_agent/webrtc/index.js
/* updateInfo = {
* event: ('track-added' | 'track-removed' | 'track-updated'),
* trackId, mediaType, mediaFormat, active }
*/
// controller 就是WebrtcNode.publish的时候options传递的controller
var notifyTrackUpdate = function (controller, transportId, updateInfo) {
// 广播消息
rpcClient.remoteCast(controller, 'onTrackUpdate', [transportId, updateInfo]);
...
};
log
WebrtcNode - notifyTrackUpdate conference-9534c64ba3c0a7727a84@192.168.221.62_0 99649f7149c44673ad7c8338e1494c4f-0 {
type: 'track-added',
trackId: '99649f7149c44673ad7c8338e1494c4f-0',
mediaType: 'audio',
mediaFormat: { codec: 'opus', sampleRate: 48000, channelNum: 2 },
direction: 'in',
operationId: '99649f7149c44673ad7c8338e1494c4f',
mid: '0',
rid: undefined,
active: true
WebrtcNode - notifyTrackUpdate conference-9534c64ba3c0a7727a84@192.168.221.62_0 99649f7149c44673ad7c8338e1494c4f-1 {
type: 'track-added',
trackId: '99649f7149c44673ad7c8338e1494c4f-1',
mediaType: 'video',
mediaFormat: { codec: 'av1' },
direction: 'in',
operationId: '99649f7149c44673ad7c8338e1494c4f',
mid: '1',
rid: undefined,
active: true
}
RpcClient.remoteCast
dist/webrtc_agent/amqpClient.js
remoteCast(to, method, args) {
const channel = this.bus.channel;
if (this.ready && channel) {
const content = JSON.stringify({
method,
args,
});
try {
channel.publish(RPC_EXC.name, to, Buffer.from(content));
} catch (e) {
log.warn('Failed to publish:', e);
}
} else {
this.ready = false;
}
}