mediasoup源码分析--channel创建及信令交互

mediasoup源码分析--channel创建及信令交互

概述

golang实现mediasoup的tcp服务及channel通道一文中,已经介绍过信令服务中tcp和channel的创建,本文主要讲解c++中mediasoup的channel创建,以及信令服务和mediasoup服务如何交互

跨职能图

业务流程图

数据发送有两种方式:

应用层发送的request最后被封装在Requst对象中,其中包含着"id",因为Request对象中包含着Channel::UnixStreamSocket对象,所以可以直接调用Request::Accept()将处理后的结果告诉应用层进程。

Worker进程也可以主动给应用层进程发送消息,通过Notifier::Emit()即可以给应用进程发送消息,Notifier类中有Channel::UnixStreamSocket,所以直接调用Channel::UnixStreamSocket::Send()就可以发送消息。Notifier类内部的数据成员和函数成员都是静态的,所以在任意位置可以直接通过Channel::Notifier::Emit()函数发送消息。

代码剖析

1.channel创建

cpp 复制代码
int main(int argc, char* argv[])
{
    // Ensure we are called by our Node library.
    if (argc == 1)
    {
        std::cerr << "ERROR: you don't seem to be my real father" << std::endl;

        std::_Exit(EXIT_FAILURE);
    }

    std::string id = std::string(argv[1]);
    std::string ip = std::string(argv[2]);
    int port = atoi(argv[3]);
    int iperfPort = atoi(argv[4]);

    // Initialize libuv stuff (we need it for the Channel).
    DepLibUV::ClassInit();
    //..........省略部分代码..............
    // Set the Channel socket (this will be handled and deleted by the Worker).
    printf("new Channel to %s:%d\n",ip.c_str(),port);
    auto* channel = new Channel::UnixStreamSocket(ip,port);
    //..........省略部分代码..............
    try
        {
            // Run the Worker.
            Worker worker(id,channel);

            // Worker ended.
            destroy();

            exitSuccess();
        }
    catch (const MediaSoupError& error)
        {
            MS_ERROR_STD("failure exit: %s", error.what());

            destroy();
            exitWithError();
        }
}

UnixStreamSocket构造函数

cpp 复制代码
UnixStreamSocket::UnixStreamSocket(const std::string& ip,int port) : ::UnixStreamSocket::UnixStreamSocket(ip,port, MaxSize)
{
    MS_TRACE_STD();

    // Create the JSON reader.
    {
        Json::CharReaderBuilder builder;
        Json::Value settings = Json::nullValue;
        Json::Value invalidSettings;

        builder.strictMode(&settings);

        MS_ASSERT(builder.validate(&invalidSettings), "invalid Json::CharReaderBuilder");

        this->jsonReader = builder.newCharReader();
    }

    // Create the JSON writer.
    {
        Json::StreamWriterBuilder builder;
        Json::Value invalidSettings;

        builder["commentStyle"]            = "None";
        builder["indentation"]             = "";
        builder["enableYAMLCompatibility"] = false;
        builder["dropNullPlaceholders"]    = false;

        MS_ASSERT(builder.validate(&invalidSettings), "invalid Json::StreamWriterBuilder");

        this->jsonWriter = builder.newStreamWriter();
    }
}

跳转到handles\UnixStreamSocket.cpp下

cpp 复制代码
UnixStreamSocket::UnixStreamSocket( const std::string& ip,int port,size_t bufferSize) : bufferSize(bufferSize)
{
    printf("::UnixStreamSocket::UnixStreamSocket\n");
    MS_TRACE_STD();

    int err;

    this->uvHandle       = new uv_tcp_t;
    this->uvHandle->data = (void*)this;
    err = uv_tcp_init(DepLibUV::GetLoop(), this->uvHandle);
    if (err != 0)
    {
        delete this->uvHandle;
        this->uvHandle = nullptr;
        printf("uv_tcp_init() failed: %s\n", uv_strerror(err));
        MS_THROW_ERROR_STD("uv_tcp_init() failed: %s", uv_strerror(err));
    }
    struct sockaddr_in dest;
    uv_ip4_addr(ip.c_str(), port, &dest);
    this->connect = new uv_connect_t;
    printf("will connect to %s:%d\n",ip.c_str(),port);
    err = uv_tcp_connect(this->connect, this->uvHandle, (const struct sockaddr*)&dest, onConnect);
    if (err != 0)
    {
        delete this->uvHandle;
        this->uvHandle = nullptr;
        printf("uv_tcp_connect() failed: %s\n", uv_strerror(err));
        MS_THROW_ERROR_STD("uv_tcp_connect() failed: %s", uv_strerror(err));
    }
    // Start reading.
    err = uv_read_start(
        reinterpret_cast<uv_stream_t*>(this->uvHandle),
        static_cast<uv_alloc_cb>(onAlloc),
        static_cast<uv_read_cb>(onRead));
    if (err != 0)
    {
        uv_close(reinterpret_cast<uv_handle_t*>(this->uvHandle), static_cast<uv_close_cb>(onClose));

        MS_THROW_ERROR_STD("uv_read_start() failed: %s", uv_strerror(err));
    }
    // NOTE: Don't allocate the buffer here. Instead wait for the first uv_alloc_cb().
}

代码中的uv_read_start接口中onRead回调

cpp 复制代码
    err = uv_read_start(
        reinterpret_cast<uv_stream_t*>(this->uvHandle),
        static_cast<uv_alloc_cb>(onAlloc),
        static_cast<uv_read_cb>(onRead));

跳转到onRead中

cpp 复制代码
inline static void onRead(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf)
{
	auto* socket = static_cast<UnixStreamSocket*>(handle->data);

	if (socket == nullptr)
		return;

	socket->OnUvRead(nread, buf);
}

OnUvRead中调用UserOnUnixStreamRead

cpp 复制代码
	void UnixStreamSocket::UserOnUnixStreamRead()
	{
		MS_TRACE_STD();

		// Be ready to parse more than a single message in a single TCP chunk.
		while (true)
		{
			if (IsClosed())
				return;

			size_t readLen  = this->bufferDataLen - this->msgStart;
			char* jsonStart = nullptr;
			size_t jsonLen;
			int nsRet = netstring_read(
			  reinterpret_cast<char*>(this->buffer + this->msgStart), readLen, &jsonStart, &jsonLen);
            //.............省略部分代码..............
			// If here it means that jsonStart points to the beginning of a JSON string
			// with jsonLen bytes length, so recalculate readLen.
			readLen =
			  reinterpret_cast<const uint8_t*>(jsonStart) - (this->buffer + this->msgStart) + jsonLen + 1;

			Json::Value json;
			std::string jsonParseError;

			if (this->jsonReader->parse(
			      (const char*)jsonStart, (const char*)jsonStart + jsonLen, &json, &jsonParseError))
			{
				Channel::Request* request = nullptr;

				try
				{
					request = new Channel::Request(this, json);
				}
				catch (const MediaSoupError& error)
				{
					MS_ERROR_STD("discarding wrong Channel request");
				}

				if (request != nullptr)
				{
					// Notify the listener.
					this->listener->OnChannelRequest(this, request);

					// Delete the Request.
					delete request;
				}
                //.............省略部分代码..............
                ...
		}
	}

channel创建完成,至此,跳转到worker.cpp中的OnChannelRequest接口。mediasoup监听channel信令并根据request->methodId分类处理

根据request->methodId,分别执行不同的业务

request->methodId有如下分类

cpp 复制代码
	std::unordered_map<std::string, Request::MethodId> Request::string2MethodId =
	{
		{ "worker.dump",                       Request::MethodId::WORKER_DUMP                          },
		{ "worker.updateSettings",             Request::MethodId::WORKER_UPDATE_SETTINGS               },
		{ "worker.createRouter",               Request::MethodId::WORKER_CREATE_ROUTER                 },
		{ "router.close",                      Request::MethodId::ROUTER_CLOSE                         },
		{ "router.dump",                       Request::MethodId::ROUTER_DUMP                          },
		{ "router.createWebRtcTransport",      Request::MethodId::ROUTER_CREATE_WEBRTC_TRANSPORT       },
		{ "router.createPlainRtpTransport",    Request::MethodId::ROUTER_CREATE_PLAIN_RTP_TRANSPORT    },
		{ "router.createProducer",             Request::MethodId::ROUTER_CREATE_PRODUCER               },
		{ "router.createConsumer",             Request::MethodId::ROUTER_CREATE_CONSUMER               },
		{ "router.setAudioLevelsEvent",        Request::MethodId::ROUTER_SET_AUDIO_LEVELS_EVENT        },
		{ "transport.close",                   Request::MethodId::TRANSPORT_CLOSE                      },
		{ "transport.dump",                    Request::MethodId::TRANSPORT_DUMP                       },
		{ "transport.getStats",                Request::MethodId::TRANSPORT_GET_STATS                  },
		{ "transport.setRemoteDtlsParameters", Request::MethodId::TRANSPORT_SET_REMOTE_DTLS_PARAMETERS },
		{ "transport.setRemoteParameters",     Request::MethodId::TRANSPORT_SET_REMOTE_PARAMETERS      },
		{ "transport.setMaxBitrate",           Request::MethodId::TRANSPORT_SET_MAX_BITRATE            },
		{ "transport.changeUfragPwd",          Request::MethodId::TRANSPORT_CHANGE_UFRAG_PWD           },
		{ "transport.startMirroring",          Request::MethodId::TRANSPORT_START_MIRRORING            },
		{ "transport.stopMirroring",           Request::MethodId::TRANSPORT_STOP_MIRRORING             },
		{ "producer.close",                    Request::MethodId::PRODUCER_CLOSE                       },
		{ "producer.dump",                     Request::MethodId::PRODUCER_DUMP                        },
		{ "producer.getStats",                 Request::MethodId::PRODUCER_GET_STATS                   },
		{ "producer.pause",                    Request::MethodId::PRODUCER_PAUSE                       },
		{ "producer.resume" ,                  Request::MethodId::PRODUCER_RESUME                      },
		{ "producer.setPreferredProfile",      Request::MethodId::PRODUCER_SET_PREFERRED_PROFILE       },
		{ "consumer.close",                    Request::MethodId::CONSUMER_CLOSE                       },
		{ "consumer.dump",                     Request::MethodId::CONSUMER_DUMP                        },
		{ "consumer.getStats",                 Request::MethodId::CONSUMER_GET_STATS                   },
		{ "consumer.enable",                   Request::MethodId::CONSUMER_ENABLE                      },
		{ "consumer.pause",                    Request::MethodId::CONSUMER_PAUSE                       },
		{ "consumer.resume",                   Request::MethodId::CONSUMER_RESUME                      },
		{ "consumer.setPreferredProfile",      Request::MethodId::CONSUMER_SET_PREFERRED_PROFILE       },
		{ "consumer.setEncodingPreferences",   Request::MethodId::CONSUMER_SET_ENCODING_PREFERENCES    },
		{ "consumer.requestKeyFrame",          Request::MethodId::CONSUMER_REQUEST_KEY_FRAME           }
	};

下一章节介绍mediasoup如何将信令返回值及其他通知信息推送到信令服务,敬请期待!

相关推荐
番茄灭世神3 小时前
PN学堂GD32教程第8篇——RTC
实时音视频
runner365.git4 小时前
RTC实现VoiceAgent(二)
大模型·webrtc·实时音视频·voiceagent
xuxie9913 小时前
N18 RTC
单片机·嵌入式硬件·实时音视频
runner365.git1 天前
RTC会议实时翻译系统
实时音视频
runner365.git1 天前
如何使用RTCPilot配置一个集群RTC服务
webrtc·实时音视频·音视频开发
深念Y2 天前
从WebSocket到WebRTC,豆包级实时语音交互背后的技术演进
websocket·网络协议·实时互动·webrtc·语音识别·实时音视频
海水冷却5 天前
2026 主流 RTC SDK 选型参考,7 大维度横向对比
实时音视频·rtc
TEL189246224776 天前
IT6636/IT66362(3进1出 / 2进1出 HDMI 2.1 48Gbps Retiming Switch,内置 MCU)
音视频·实时音视频·视频编解码
天上路人11 天前
A-59F 多功能语音处理模组在本地会议系统扩音啸叫处理中的技术应用与性能分析
人工智能·神经网络·算法·硬件架构·音视频·语音识别·实时音视频
爱学习的程序媛11 天前
Windows系统下安装与配置FreeSWITCH完整指南
windows·实时互动·webrtc·实时音视频·信息与通信·媒体