C# WebApi+Webrtc局域网音视频通话实例

C# WebApi+Webrtc 局域网音视频通话示例,供大家参考,具体内容如下

本示例通过IIS部署webapi,利用websocket进行webrtc消息交换,通过Chrome浏览器访问,可实现局域网内webrtc 音视频通话。

通过Chrome浏览器打开localhost/live.html本地网址,打开两个本地网,点击任意页面连接按钮即联通。

本示例未实现NAT穿透处理,互联网无法联通,如需NAT穿透请自行查阅相关资料。

关于webrtc、webapi相关技术说明请自行查阅相关资料,本文不做赘述说明。

运行效果如下图:

webapi端Handler1.ashx代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.WebSockets;

namespace webrtclan
{
    /// <summary>
    /// 离线消息
    /// </summary>
    public class MessageInfo
    {
        public MessageInfo(DateTime _MsgTime, ArraySegment<byte> _MsgContent)
        {
            MsgTime = _MsgTime;
            MsgContent = _MsgContent;
        }
        public DateTime MsgTime { get; set; }
        public ArraySegment<byte> MsgContent { get; set; }

    }

    /// <summary>
    /// Handler1 的摘要说明
    /// </summary>
    public class Handler1 : IHttpHandler
    {
        private static Dictionary<string, WebSocket> CONNECT_POOL = new Dictionary<string, WebSocket>();//用户连接池
        private static Dictionary<string, List<MessageInfo>> MESSAGE_POOL = new Dictionary<string, List<MessageInfo>>();//离线消息池

        public void ProcessRequest(HttpContext context)
        {

            if (context.IsWebSocketRequest)
            {
                context.Response.ContentType = "application/json";
                context.Response.Charset = "utf-8";
                context.AcceptWebSocketRequest(ProcessMsg);
            }
        }

        private async Task ProcessMsg(AspNetWebSocketContext context)
        {
            WebSocket socket = context.WebSocket;
            string user = context.QueryString["user"].ToString();

            try
            {
                #region 用户添加连接池
                //第一次open时,添加到连接池中
                if (!CONNECT_POOL.ContainsKey(user))
                {
                    CONNECT_POOL.Add(user, socket);//不存在,添加
                }
                else
                {
                    if (socket != CONNECT_POOL[user])//当前对象不一致,更新
                    {
                        CONNECT_POOL[user] = socket;
                    }
                }
                #endregion

                //#region 连线成功
                //for (int cp = 0; cp < CONNECT_POOL.Count; cp++)
                //{
                //    if (CONNECT_POOL.ElementAt(cp).Key != user)
                //    {
                //        string joinedmsg = "{\"FROM\":\"" + user + "\",\"event\":\"joined\"}";
                //        ArraySegment<byte> joinedmsgbuffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(joinedmsg));
                //        WebSocket destSocket = CONNECT_POOL.ElementAt(cp).Value;//目的客户端
                //        await destSocket.SendAsync(joinedmsgbuffer, WebSocketMessageType.Text, true, CancellationToken.None);
                //    }
                //}
                //#endregion

                #region 离线消息处理
                if (MESSAGE_POOL.ContainsKey(user))
                {
                    List<MessageInfo> msgs = MESSAGE_POOL[user];
                    foreach (MessageInfo item in msgs)
                    {
                        await socket.SendAsync(item.MsgContent, WebSocketMessageType.Text, true, CancellationToken.None);
                    }
                    MESSAGE_POOL.Remove(user);//移除离线消息
                }
                #endregion

                while (true)
                {
                    if (socket.State == WebSocketState.Open)
                    {
                        ArraySegment<byte> wholemessage= new ArraySegment<byte>(new byte[10240]);

                        int i = 0;

                        WebSocketReceiveResult dresult;
                        do
                        {
                            //因为websocket每一次发送的数据会被tcp分包
                            //所以必须判断接收到的消息是否完整
                            //不完整就要继续接收并拼接数据包
                            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[2048]);
                            dresult = await socket.ReceiveAsync(buffer, CancellationToken.None);
                            string message1 = Encoding.UTF8.GetString(buffer.Array);
                            buffer.Array.CopyTo(wholemessage.Array,i);
                            i += 2048;
                        } while (false == dresult.EndOfMessage);                       

                        //string message = Encoding.UTF8.GetString(wholemessage.Array);
                        //message = message.Replace("\0", "").Trim();
                        //JavaScriptSerializer serializer = new JavaScriptSerializer();
                        //Dictionary<string, object> json = (Dictionary<string, object>)serializer.DeserializeObject(message);
                        //string target = (string)json.ElementAt(1).Value;

                        #region 消息处理(字符截取、消息转发)
                        try
                        {
                            #region 关闭Socket处理,删除连接池
                            if (socket.State != WebSocketState.Open)//连接关闭
                            {
                                if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//删除连接池
                                break;
                            }
                            #endregion

                            for (int cp = 0; cp < CONNECT_POOL.Count; cp++)
                            {
                                 //if (CONNECT_POOL.ElementAt(cp).Key!=target)
                                 //   {
                                        WebSocket destSocket = CONNECT_POOL.ElementAt(cp).Value;//目的客户端
                                        await destSocket.SendAsync(wholemessage, WebSocketMessageType.Text, true, CancellationToken.None);
                                 //  }
                             }

                            //if (CONNECT_POOL.ContainsKey(descUser))//判断客户端是否在线
                            //{
                            //    WebSocket destSocket = CONNECT_POOL[descUser];//目的客户端
                            //    if (destSocket != null && destSocket.State == WebSocketState.Open)
                            //        await destSocket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
                            //}
                            //else
                            //{
                            //    _ = Task.Run(() =>
                            //      {
                            //          if (!MESSAGE_POOL.ContainsKey(descUser))//将用户添加至离线消息池中
                            //            MESSAGE_POOL.Add(descUser, new List<MessageInfo>());
                            //          MESSAGE_POOL[descUser].Add(new MessageInfo(DateTime.Now, buffer));//添加离线消息
                            //    });
                            //}
                        }
                        catch (Exception exs)
                        {
                            //消息转发异常处理,本次消息忽略 继续监听接下来的消息
                        }
                        #endregion
                    }
                    else
                    {
                        if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);//删除连接池
                        break;
                    }
                }//while end
            }
            catch (Exception ex)
            {
                //整体异常处理
                if (CONNECT_POOL.ContainsKey(user)) CONNECT_POOL.Remove(user);

            }
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }

    }
}

live.html客户端代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>webrtc</title>
    <style>
        #yours {
            width: 200px;
            position: absolute;
            top: 50px;
            left: 100px;
        }
        #theirs {
            width: 600px;
            position: absolute;
            top: 50px;
            left: 400px;
        }
    </style>
</head>
<body>
    <button onclick="createOffer()">建立连接</button>
    <video id="yours" autoplay controls="controls" ></video>
    <video id="theirs" autoplay controls="controls"></video>

</body>

<script src="webrtc.js"></script>

</html>

webrtc.js脚本代码如下:

var websocket;

function randomNum(minNum, maxNum) {
    switch (arguments.length) {
        case 1:
            return parseInt(Math.random() * minNum + 1, 10);
            break;
        case 2:
            return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
            break;
        default:
            return 0;
            break;
    }
}
const userid = 'user' + randomNum(0, 100000);

function hasUserMedia() {
    navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
    return !!navigator.getUserMedia;
}
function hasRTCPeerConnection() {
    window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection;
    return !!window.RTCPeerConnection;
}

var yourVideo = document.getElementById("yours");
var theirVideo = document.getElementById("theirs");
var Connection;

function startPeerConnection() {
    //return;
    var config = {
        'iceServers': [
            //{ 'urls': 'stun:stun.xten.com:3478' },
            //{ 'urls': 'stun:stun.voxgratia.org:3478' },

            //{ 'url': 'stun:stun.l.google.com:19302' }
        ]
    };
    config = {
        iceServers: [
            //{ urls: 'stun:stun.l.google.com:19302' },
            //{ urls: 'stun:global.stun.twilio.com:3478?transport=udp' }
        ]
        //sdpSemantics: 'unified-plan'
    };
    // {
    //     "iceServers": [{
    //         "url": "stun:stun.1.google.com:19302"
    //     }]
    // };
    Connection = new RTCPeerConnection(config);
    Connection.onicecandidate = function (e) {
        console.log('onicecandidate');
        if (e.candidate) {
            websocket.send(JSON.stringify({
                "userid": userid,
                "event": "_ice_candidate",
                "data": {
                    "candidate": e.candidate
                }
            }));
        }
    };
    Connection.onaddstream = function (e) {
        console.log('onaddstream');
        //theirVideo.src = window.URL.createObjectURL(e.stream);
        theirVideo.srcObject = e.stream;
    };
    Connection.onclose = function (e) {
        console.log('RTCPeerConnection close'+e);
    };
}

createSocket();
startPeerConnection();

if (hasUserMedia()) {
    navigator.getUserMedia({ video: true, audio: true },
        stream => {
            yourVideo.srcObject = stream;
            window.stream = stream;
            yourVideo.muted = true;
            Connection.addStream(stream)
        },
        err => {
            console.log(err);
        })
}

function createOffer() {
    //发送offer和answer的函数,发送本地session描述
    Connection.createOffer().then(offer => {
        Connection.setLocalDescription(offer);
        websocket.send(JSON.stringify({
            "userid": userid,
            "event": "offer",
            "data": {
                "sdp": offer
            }
        }));
    });
}

function createSocket() {
    //websocket = null;
    websocket = new WebSocket('ws://localhost:80/Handler1.ashx?user='+userid);//('wss://www.ecoblog.online/wss');
    eventBind();
};

function eventBind() {
    //连接成功
    websocket.onopen = function (e) {
        console.log('open:' + e);
    };
    //server端请求关闭
    websocket.onclose = function (e) {
        console.log('close:' + e);
    };
    //error
    websocket.onerror = function (e) {
        console.log('error:' + e.data);
    };
    //收到消息
    websocket.onmessage = (event) => {
        if (event.data == "new user") {
            location.reload();
        } else {
            var js = event.data.replace(/[\u0000-\u0019]+/g, "");
            var json = JSON.parse(js);

            if (json.userid != userid) {
                //如果是一个ICE的候选,则将其加入到PeerConnection中,否则设定对方的session描述为传递过来的描述
                if (json.event === "_ice_candidate" && json.data.candidate) {
                    Connection.addIceCandidate(new RTCIceCandidate(json.data.candidate));
                }
                else if (json.event === 'offer') {
                    Connection.setRemoteDescription(json.data.sdp);
                    Connection.createAnswer().then(answer => {
                        Connection.setLocalDescription(answer);
                        //console.log(window.stream)
                        websocket.send(JSON.stringify({
                            "userid": userid,
                            "event": "answer",
                            "data": {
                                "sdp": answer
                            }
                        }));
                    })
                }
                else if (json.event === 'answer') {
                    Connection.setRemoteDescription(json.data.sdp);
                    //console.log(window.stream)

                }
            }
        }
    };
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C# 调用FFmpeg处理音视频的示例

    FFmpeg 开源.跨平台.体积小.功能强大,提供了录制.转换以及流化音视频的完整解决方案. 官网:https://www.ffmpeg.org/ 百科:https://baike.baidu.com/item/ffmpeg/2665727?fr=aladdin FFmpeg 应用非常广泛,可以用来播放本地视频甚至网络视频,查看音视频信息,还可以用于从视频中提取音频,转换音视频文件格式等等,本文主要介绍如何调用 FFmpeg 来查看音视频信息.从视频中提取音频.转换音视频格式等. 1. 调用FF

  • C#实现语音视频录制-附demo源码

    在很多语音视频软件系统中,经常有将实时的音频或视频录制为文件保存到磁盘的需求,比如,视频监控系统中录制监控到的视频.视频会议系统中录制整个会议的过程.语音通话系统中录制完整的对话内容.等等. MFile组件(Oraycn.MFile.dll)是傲瑞实用组件之一,它可以将原始的语音数据和视频数据按照指定的格式进行编码,并将它们写入到视频文件(如.mp4)中. 一.缘起 最近正在做的一个网络招聘平台的项目,其中有一个模块是这样的,应聘者可以通过该系统的客户端录制自己的视频(自我介绍)上传到服务器,而

  • C# 使用SDL2实现Mp4文件播放音视频操作

    播放音视频的关键:视频的格式是H264,音频的格式是AAC.使用ffmpeg探测流的方式来实现音视频流的解码播放. 数据处理逻辑:H264->YUV AAC->PCM. SDL2工具类 using SDL2; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threadi

  • C# WebApi+Webrtc局域网音视频通话实例

    C# WebApi+Webrtc 局域网音视频通话示例,供大家参考,具体内容如下 本示例通过IIS部署webapi,利用websocket进行webrtc消息交换,通过Chrome浏览器访问,可实现局域网内webrtc 音视频通话. 通过Chrome浏览器打开localhost/live.html本地网址,打开两个本地网,点击任意页面连接按钮即联通. 本示例未实现NAT穿透处理,互联网无法联通,如需NAT穿透请自行查阅相关资料. 关于webrtc.webapi相关技术说明请自行查阅相关资料,本文

  • ASP.NET Core 2.0 WebApi全局配置及日志实例

    最新在将原来写的一些webSerivce转换为WebApi,直接就用了ASP.Net Core 2.0的框架,在使用中,发现的与原有的asp.net不同的地方,通过搜索已经慢慢解决,记录下来备用. 一.全局配置 在asp.net中,全局变更配置写在web.config中,如下所示 <?xml version="1.0"?> <configuration> <connectionStrings> <add name="conn"

  • Android仿微信多人音视频通话界面

    工作中需要实现一个类似微信多人视频通话功能的界面,分别使用自定义viewgroup和自定义layoutManager的方式进行了实现.最终工作中采用了layoutManager,因为可以使用payload更新单个布局控件,效率更好.下面放出两种具体的实现效果代码. 1.使用自定义ViewGroup方式实现 下面是三个人通话时候的效果,其他的可以参考微信多人音视频通话界面. package com.dnaer.android.telephone.widgets; import android.co

  • Python实现自动化刷抖音的实例

    前言 都说抖音有毒,一刷就停不下来了.看来抖音这款产品紧紧抓住了人们内心深处的某些需求.当然今天不是来探讨抖音这款产品的啊.今天我们来学习如何用Python实现自动刷抖音,并为颜值高的的小哥哥小姐姐点赞并评论. 项目环境 语言:Python3 编辑器:Pycharm 其他工具:手机一个,数据线一条,android studio 实现思路 1.获取手机抖音短视频的截图 2.调用百度API识别人脸 3.对符合条件的视频点赞和评论 获取抖音视频的截图 获取视频截屏,在这里用的是adb工具.adb工具即

  • Java创建多线程局域网聊天室实例

    局域网聊天室 在学习了一个学期的java以后,觉得java真是博大精深,彻底放弃了因为c++而轻视java的心态,搞了一个多线程的聊天室,熟悉了一下服务器和客户机的操作. 1.TCP 要实现局域网连接,就必须知道信息传输的原理. 在局域网里面传输的信息都是以包的形式,我使用的TCP包传输数据,TCP包里面封装了IP报文. 下面这句话就是通过一个静态IPV4协议的类得到一个服务器的IP地址. address = InetAddress.getByName("192.168.43.86")

  • c# 获得局域网主机列表实例

    using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Net; using System.Threading; namespace WindowLanSearch { /// <summary> /// Form1 的摘要说明. /// </s

  • 在Ubuntu上搭建一个基于webrtc的多人视频聊天服务实例代码详解

    WebRTC,即Web Real-Time Communication,web实时通信技术.简单地说就是在web浏览器里面引入实时通信,包括音视频通话等. 在疫情期间哪里也去不了,在家没事就研究webrtc视频直播技术,网上找了些教程最终都不太能顺利跑起来的,可能是文章写的比较老,使用的一些开源组件已经更新了,有些配置已经不太一样了,所以按照以前的步骤会有问题.折腾了一阵终于跑起来了,记录一下. 一个简单的聊天室html页面 这个页面使用simple-webrtc来实现webrtc的通讯,sim

  • Android基于腾讯云实时音视频仿微信视频通话最小化悬浮

    最近项目中有需要语音.视频通话需求,看到这个像环信.融云等SDK都有具体Demo实现,但咋的领导对腾讯情有独钟啊,IM要用腾讯云IM,不妙的是腾讯云IM并不包含有音视频通话都要自己实现,没办法深入了解腾讯云产品后,决定自己基于腾讯云实时音视频做去语音.视频通话功能.在这里把实现过程记录下为以后用到便于查阅,另一方面也给有需要的人提供一个思路,让大家少走弯路,有可能我的实现的方法不是最好,但是这或许是一个可行的方案,大家不喜勿喷.基于腾讯云实时音视频SDK 6.5.7272版本,腾讯DEMO下载地

  • 如何基于 Blueprint 在游戏中创建实时音视频功能

    近日,Epic 公布了他们为次时代游戏准备的 Unreal 5 游戏引擎,以及一份效果惊艳的 Demo.据称,游戏中的每个资源有一百万个绘制三角面,每一帧有超过十亿个绘制三角面.也就是说,开发者构建的游戏场景,能获得电影特效场景般的视觉体验. 事实上,声网也有许多采用 Unreal 的开发者.为了能让开发者可以在游戏中快速实现实时音视频对话,声网 Agora Unreal SDK Beta 版也已上线. 由于 Unreal 为开发者提供了两种开发方式,一种是 Blueprint,即可视化编程,另

  • 详解IOS WebRTC的实现原理

    概述 它在2011年5月开放了工程的源代码,在行业内得到了广泛的支持和应用,成为下一代视频通话的标准. WebRTC的音视频通信是基于P2P,那么什么是P2P呢? 它是点对点连接的英文缩写. P2P连接模式 一般我们传统的连接方式,都是以服务器为中介的模式: 类似http协议:客户端?服务端(当然这里服务端返回的箭头仅仅代表返回请求数据). 我们在进行即时通讯时,进行文字.图片.录音等传输的时候:客户端A?服务器?客户端B. 而点对点的连接恰恰数据通道一旦形成,中间是不经过服务端的,数据直接从一

随机推荐