C#-接入声网SDK实现网页版1对1视频通话以及边录制边上传
来源:互联网 发布:python pil和opencv 编辑:程序博客网 时间:2024/05/20 20:57
准备工作:
1、进入声网官网:https://www.agora.io 登录进入开发者后台创建一个项目,并获取项目的App ID,在Recording Server IP or Domain填写录制服务器的ip以及端口号,录制服务器我用的是Centos。
2、准备两台电脑,必须支持摄像头与麦克风,必须在谷歌浏览器上运行。
直接官网下载web-sdk无插件版demo,在谷歌浏览器上打开,输入App ID,channelId随意、check两端必须都勾上(先勾上的为房主),点击join,实现通话。
注意:如果出现getUserMedia failed的提示,可以尝试降低画面质量,如找到并替换下面这行代码:
localStream.setVideoProfile('720p');-->localStream.setVideoProfile('360p_3');3、录制启用时,如果声网上录制的log日志提示Permission deny 则开放整个录制sdk文件夹的所有权限
直接贴代码
后台:
[HttpGet] public ActionResult Meeting(int dId) { string userData = ((FormsIdentity)User.Identity).Ticket.UserData; LoginUser user = Common.GetUser(userData); try { MeetingRecord model = meetingRecordRepository.Get(m => m.dId == dId && m.UserId == user.UserId && m.Statu == MeetingStatus.Meeting); if (model != null) { ViewBag.CannelName = model.dId.ToString() + Convert.ToDateTime(model.BeginDate).ToString("HHmmss");//自定义唯一频道名称,会用于在录制服务器上查找录制文件 ViewBag.MeetingId = model.MeetingId; return View("Meeting"); } else return Content("<script>alert('通话已结束或不存在!');window.location.href='videocenter';</script>"); } catch { return Content("<script>alert('操作失败,请重试!');window.location.href='videocenter';</script>"); } }
前端:
@{ ViewBag.Title = "视频通话"; int meetingId = (int)ViewBag.MeetingId; string cannelName = ViewBag.CannelName; string appId = System.Configuration.ConfigurationManager.AppSettings["AgoraAppId"].ToString();}@section Head{ <script type="text/javascript" src="@Url.Content("~/content/meeting/AgoraRTCSDK-1.13.0.js")"></script> <script type="text/javascript" src="@Url.Content("~/scripts/jquery.loadmask.min.js")"></script>}<div id="tabs"> <div class="section-header"> <div class="title"> @ViewBag.Title - <a href="@Url.Action("videocenter")">返回</a> </div> <div class="options"> <input type="button" id="startRecord" value="开启录制" onclick="StartRecord()" /> <input type="button" id="stopAndQuit" onclick="stopAndQuit()" style="display: none" value="保存录制并结束会话" /> <input type="button" id="leave" onclick="leave()" value="结束会话" /> </div> </div> <div class="ui-tabs ui-widget ui-widget-content ui-corner-all"> <div id="div_device" class="panel panel-default" style="display: none"> <div class="select"> <label for="audioSource"> Audio source: </label> <select id="audioSource"> </select> </div> <div class="select"> <label for="videoSource"> Video source: </label> <select id="videoSource"> </select> </div> </div> <div id="div_join" style="display:none"> <div class="panel-body"> 访问密钥: <input id="key" type="text" value="@appId" size="36" class="text ui-widget-content ui-corner-all" /> 通讯频道: <input id="channel" type="text" value="@cannelName" size="4" class="text ui-widget-content ui-corner-all" /> Host: <input id="video" type="checkbox" checked /> <input type="button" id="join" class="btn btn-primary" onclick="join()" value="呼叫" /> @*<button id="publish" class="btn btn-primary" onclick="publish()">Publish</button><button id="unpublish" class="btn btn-primary" onclick="unpublish()">Unpublish</button>*@ </div> </div> <!--style> .video__box{width:910px; margin:0 auto; overflow:hidden;} .video__main{ width:810px; height:607px;float:left } .video__list{ width:200px; height:607px; float:left;} .video__item{ width:200px; height:174px; hei background:url(/img/icon_live.png) center center no-repeat; } </style> <div class="video__main"> </div> <div class="video__list"> <div class="video__item"></div> <div id="video" class="video__item"> <div id="agora_local"></div> </div> </div--> <div id="video" style="margin: 0 auto;"> <div id="agora_local" style="width: 450px; height: 350px; display: inline-block;"> </div> </div> </div></div><script type="text/javascript" language="javascript"> $(function () { join(); }) function StartRecord() { var posturl = '@Url.Action("StartRecord")'; $.ajax({ type: "Post", dataType: "json", url: posturl, data: { MeetingId: '@meetingId' }, success: function (data) { if (data == "success") { $("#startRecord").hide(); $("#leave").hide(); $("#stopAndQuit").show(); } else { alert('无法连接录制服务器,请联系管理员。'); } }, error: function (data) { alert('系统出错,请重试。'); } }) } var client, localStream, camera, microphone; var audioSelect = document.querySelector('select#audioSource'); var videoSelect = document.querySelector('select#videoSource'); function join() { document.getElementById("join").disabled = true; document.getElementById("video").disabled = true; var channel_key = null; console.log("Init AgoraRTC client with vendor key: " + key.value); client = AgoraRTC.createClient({ mode: 'interop' }); client.init(key.value, function () { console.log("AgoraRTC client initialized"); client.join(channel_key, channel.value, null, function (uid) { console.log("User " + uid + " join channel successfully"); if (document.getElementById("video").checked) { camera = videoSource.value; microphone = audioSource.value; localStream = AgoraRTC.createStream({ streamID: uid, audio: true, cameraId: camera, microphoneId: microphone, video: document.getElementById("video").checked, screen: false }); if (document.getElementById("video").checked) { localStream.setVideoProfile('360p_3'); } localStream.init(function () { console.log("getUserMedia successfully"); localStream.play('agora_local'); client.publish(localStream, function (err) { console.log("Publish local stream error: " + err); }); client.on('stream-published', function (evt) { console.log("Publish local stream successfully"); }); }, function (err) { console.log("getUserMedia failed", err); alert("未检查到摄像头或麦克风"); }); } }, function (err) { alert("加入视频失败:" + err); }); }, function (err) { console.log("client init failed", err); }); channelKey = ""; client.on('error', function (err) { console.log("Got error msg:", err.reason); if (err.reason === 'DYNAMIC_KEY_TIMEOUT') { client.renewChannelKey(channelKey, function () { console.log("Renew channel key successfully"); }, function (err) { console.log("Renew channel key failed: ", err); }); } }); client.on('stream-added', function (evt) { var stream = evt.stream; console.log("New stream added: " + stream.getId()); console.log("Subscribe ", stream); client.subscribe(stream, function (err) { console.log("Subscribe stream failed", err); }); }); client.on('stream-subscribed', function (evt) { var stream = evt.stream; console.log("Subscribe remote stream successfully: " + stream.getId()); if ($('div#video #agora_remote' + stream.getId()).length === 0) { $('div#video').append('<div id="agora_remote' + stream.getId() + '" style="float:right; width:450px;height:350px;display:inline-block;"></div>'); } stream.play('agora_remote' + stream.getId()); }); client.on('stream-removed', function (evt) { var stream = evt.stream; stream.stop(); $('#agora_remote' + stream.getId()).remove(); console.log("Remote stream is removed " + stream.getId()); }); client.on('peer-leave', function (evt) { var stream = evt.stream; if (stream) { stream.stop(); $('#agora_remote' + stream.getId()).remove(); console.log(evt.uid + " leaved from this channel"); alert("对方结束了通话,即将为您退出"); window.location.href = '@Url.Action("LeaveMeeting", new { meetingId = meetingId })'; } }); } function leave() { document.getElementById("leave").disabled = true; client.leave(function () { //console.log("Leavel channel successfully"); window.location.href = '@Url.Action("LeaveMeeting", new { meetingId = meetingId })'; }, function (err) { console.log("Leave channel failed"); }); } function stopAndQuit() { client.leave(function () { $("#tabs").mask("录制文件传输中,该过程约3-5分钟,请您耐心等待..."); }, function (err) { alert(""); }); $.ajax({ type: "Post", dataType: "json", url: '@Url.Action("stopandquit")', data: { MeetingId: '@meetingId' }, success: function (data) { $("#tabs").unmask(); if (data == "empty") { alert("传输失败,请重试"); } else if (data == "error") { alert('无效目录'); } else { alert("保存成功。"); } window.location.href = '/loan/videocenter'; }, error: function (data) { alert('操作失败'); window.location.href = '/loan/videocenter'; } }) } function publish() { document.getElementById("publish").disabled = true; document.getElementById("unpublish").disabled = false; client.publish(localStream, function (err) { console.log("Publish local stream error: " + err); }); } function unpublish() { document.getElementById("publish").disabled = false; document.getElementById("unpublish").disabled = true; client.unpublish(localStream, function (err) { console.log("Unpublish local stream failed" + err); }); } function getDevices() { AgoraRTC.getDevices(function (devices) { for (var i = 0; i !== devices.length; ++i) { var device = devices[i]; var option = document.createElement('option'); option.value = device.deviceId; if (device.kind === 'audioinput') { option.text = device.label || 'microphone ' + (audioSelect.length + 1); audioSelect.appendChild(option); } else if (device.kind === 'videoinput') { option.text = device.label || 'camera ' + (videoSelect.length + 1); videoSelect.appendChild(option); } else { console.log('Some other kind of source/device: ', device); } } }); }</script>
服务端代码:#region 发送开始录制命令 /// <summary> /// 发送开始录制命令 /// </summary> /// <param name="dealerId"></param> /// <param name="dateString"></param> /// <returns></returns> [HttpPost] public ActionResult StartRecord(int MeetingId) { MeetingRecord model = meetingRecordRepository.GetById(MeetingId); string yyyyMMdd = Convert.ToDateTime(model.BeginDate).ToString("yyyyMMdd"); string channelName = model.dId.ToString() + Convert.ToDateTime(model.BeginDate).ToString("HHmmss"); //调用发送接口 string result = SSHHelper.StartRecord(yyyyMMdd, channelName); if (result != "error") { model.IsOpenRecord = 1;//开启录制成功 meetingRecordRepository.Update(model); } return Json(result, JsonRequestBehavior.AllowGet); } #endregion#region 转码并生成下载文件 /// <summary> /// 转码 /// </summary> /// <param name="meetingId"></param> /// <returns></returns> [HttpPost] public ActionResult StopAndQuit(int MeetingId) { MeetingRecord model = meetingRecordRepository.GetById(MeetingId); string yyyyMMdd = Convert.ToDateTime(model.BeginDate).ToString("yyyyMMdd"); string channelName = model.dId.ToString() + Convert.ToDateTime(model.BeginDate).ToString("HHmmss"); string result = SSHHelper.TranferRecord(yyyyMMdd, channelName); model.Statu = MeetingStatus.Finish; model.EndDate = DateTime.Now; if (result != "empty" && result != "error") { string[] filenames = SSHHelper.GetDownLoadUrl(result); if (filenames.Length > 0) { string url = ""; foreach (var item in filenames) { url += item + ","; } model.Remarkurl = url; } meetingRecordRepository.Update(model); unitOfWork.Commit(); return Json("success", JsonRequestBehavior.AllowGet); } meetingRecordRepository.Update(model); unitOfWork.Commit(); return Json(result, JsonRequestBehavior.AllowGet); } #endregion
SSHHelper.csusing System;using System.Collections.Generic;using System.Net.Mail;using System.Configuration;using System.Net;using Tamir.SharpSsh;using System.Text.RegularExpressions;namespace aab.Core{ public class SSHHelper { /// <summary> /// 声网提供的项目appid /// </summary> public static readonly string _AgoraAppId = System.Configuration.ConfigurationManager.AppSettings["AgoraAppId"].ToString(); #region 录制服务器信息 /// <summary> /// IP /// </summary> public static readonly string _SendServer = System.Configuration.ConfigurationManager.AppSettings["LinuxSendServer"].ToString(); /// <summary> /// 端口 /// </summary> public static readonly int _SendPort = int.Parse(System.Configuration.ConfigurationManager.AppSettings["LinuxSendPort"].ToString()); /// <summary> /// 登录名 /// </summary> public static readonly string _LoginName = System.Configuration.ConfigurationManager.AppSettings["LinuxSendName"].ToString(); /// <summary> /// 密码 /// </summary> public static readonly string _LoginPwd = System.Configuration.ConfigurationManager.AppSettings["LinuxSendPassword"].ToString(); /// <summary> /// 录制文件保存根目录->>/www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/samples /// </summary> public static readonly string RecordSaveRoot = System.Configuration.ConfigurationManager.AppSettings["LinuxSaveRoot"].ToString(); /// <summary> ///下载文件路径 /// </summary> public static readonly string downLoadRoot = System.Configuration.ConfigurationManager.AppSettings["LinuxDownLoadRoot"].ToString(); #endregion #region 发送录制命令 /// <summary> /// 开启录制 /// </summary> /// idle 20 =>频道没有人20秒后自动结束录制 /// 声网SDK文档:https://document.agora.io/cn/1.13/quickstart/recording.html#recording-commandline /// <param name="cancleName">频道名称</param> /// <returns></returns> public static string StartRecord(string yyyyMMdd, string channelName) { //发送录制命令 string command = "/www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/samples/Recorder_local --appId " + _AgoraAppId + " --uid 0 --channel " + channelName + " --appliteDir /www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/bin --idle 20 --recordFileRootDir " + RecordSaveRoot + " --lowUdpPort 40000 --highUdpPort 41000 --getAudioFrame 0 --getVideoFrame 0"; string result = SendSshMessage(command); //检查是否存在相应文件夹 //if (result == "success")//发送录制命令后,检查有没有webm文件 //{ // string filePath = RecordSaveRoot + "/" + yyyyMMdd + "/"; // string workPath = GetFilePath(filePath, channelName); // SshStream ssh = new SshStream(_SendServer, _LoginName, _LoginPwd); // ssh.Prompt = "#"; // ssh.RemoveTerminalEmulationCharacters = true; // string response = ssh.ReadResponse(); // ssh.Write("ls -a " + workPath); // response = ssh.ReadResponse(); // ssh.Flush(); // if (!response.Contains(".webm")) // return "error"; //} return result; } #endregion #region 检查录制文件并转码 /// <summary> /// 录制文件转码,生成UID_HHMMSSMS_av.mp4->3945464681_20171018092758159_av.mp4 /// </summary> /// sdk文档:https://document.agora.io/cn/1.13/tutorial/recording.html#id12 /// <param name="dealerId"></param> /// <returns></returns> public static string TranferRecord(string yyyyMMdd, string channelname) { string filePath = RecordSaveRoot + "/" + yyyyMMdd + "/"; string workPath = GetFilePath(filePath, channelname); if (!string.IsNullOrEmpty(workPath))//等待录制完成 { SshStream ssh = new SshStream(_SendServer, _LoginName, _LoginPwd); ssh.Prompt = "#"; ssh.RemoveTerminalEmulationCharacters = true; string response = ssh.ReadResponse(); int i = 1; do { System.Threading.Thread.Sleep(30 * 1000);//睡眠30秒 i++; if (i >= 3) return "empty";//未找到录制文件 ssh.Write("ls -a " + workPath); response = ssh.ReadResponse(); ssh.Flush(); } while (!response.Contains("recording2-done.txt")); if (response.Contains("recording2-done.txt") && response.Contains(".webm"))//必须在出现recording2-done.txt后才进行转码(表示录制完成) { string command = "python /www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/tools/video_convert.py " + workPath;//转码命令 ssh.Write(command);//发送转码命令 System.Threading.Thread.Sleep(60 * 1000);//等待转码完成 ssh.Flush(); response = FindDocPath(workPath, ".mp4");//查找是否有转码后的MP4文件, ssh.Flush(); return response; } return "empty"; } return "error"; } #endregion #region 根据文件名称的部分关键字,获得该文件的完整名称以及在该文件服务器上的绝对路径 /// <summary> /// Linux:根据文件名称的部分关键字,获得该文件的完整名称以及在该文件服务器上的绝对路径 /// </summary> /// <param name="targetPath">查找目录:/www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/samples/20171030/</param> /// <param name="search">该文件夹内的唯一关键字:228113237_033242</param> /// <returns>/www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/samples/20171030/228113237_033242</returns> public static string GetFilePath(string targetPath, string targetName) { string command = "ls -a " + targetPath;//linux命令->列出目录中的所有文件名称:ls -a 目标目录 string returnStr = SendSshMessageReturn(command); int objIndex = returnStr.IndexOf(targetName); if (objIndex == -1) return ""; string filename = returnStr.Substring(objIndex, targetName.Length + 7);//获得频道标识的文件夹名称:228113237_033242 return targetPath + filename; } #endregion #region Linux:查找目标目录下,文件名称还有某关键字的文件,并返回这些文件的完全名称以及绝对路径 /// <summary> /// Linux:查找目标目录下,文件名称含有某关键字的文件,并返回这些文件的完全名称以及完整路径 /// </summary> /// <param name="targetPath">目标目录</param> /// <param name="searchTxt">关键字</param> /// <returns>一连串linux服务器返回字符串,需要使用正则表达式处理</returns> public static string FindDocPath(string targetPath, string searchTxt) { string command = "find " + targetPath + " -name '*" + searchTxt + "*'";//linux命令:find 目标目录 -name '*关键字*' string result = SendSshMessageReturn(command); return result; } #endregion #region 向录制服务器发送shell命令 /// <summary> /// 向录制服务器发送shell命令 /// </summary> /// <param name="command">命令行</param> /// <returns></returns> public static string SendSshMessage(string command) { try { SshStream ssh = new SshStream(_SendServer, _LoginName, _LoginPwd); ssh.Prompt = "#"; ssh.RemoveTerminalEmulationCharacters = true; string response = ssh.ReadResponse(); ssh.Write(command); ssh.Flush(); return "success"; } catch { return "error"; } } #endregion #region 向录制服务器发送shell命令并获得返回值 /// <summary> /// 向录制服务器发送shell命令并获得返回值 /// </summary> /// <param name="command">命令行</param> /// <returns></returns> public static string SendSshMessageReturn(string command) { try { SshStream ssh = new SshStream(_SendServer, _LoginName, _LoginPwd); ssh.Prompt = "#"; ssh.RemoveTerminalEmulationCharacters = true; string response = ssh.ReadResponse(); ssh.Write(command); ssh.Flush(); ssh.Write("/n"); response = ssh.ReadResponse(); return response; } catch { return "error"; } } #endregion #region 获取生成MP4文件的下载链接 /// <summary> /// 获取生成MP4文件的下载链接 /// </summary> /// <param name="response"></param> /// <returns></returns> public static string[] GetDownLoadUrl(string response) { MatchCollection mc = Regex.Matches(response, @"/sdk/Agora_Recording_SDK_for_Linux_FULL/samples/[0-9a-zA-Z_/-]+[.]mp4"); string[] name = new string[mc.Count]; if (mc.Count == 0) return name; for (int i = 0; i < mc.Count; i++) { name[i] = downLoadRoot + mc[i].Value; } return name; } #endregion }}
webconfig配置<configuration>
<appSettings>下添加<!--视频录制服务设置--> <add key="AgoraAppId" value="32ffteaea8f4e79a5332d8e128af06b"/> <add key="LinuxSendServer" value="xxx.xx.xx.xxx"/>-->自己的录制服务器ip <add key="LinuxSendPort" value="22"/> <add key="LinuxSendName" value="xxx"/>->访问账号 <add key="LinuxSendPassword" value="xxxxxxxxxxxx"/>->访问密码 <add key="LinuxSaveRoot" value="/www/web/default/sdk/Agora_Recording_SDK_for_Linux_FULL/samples"/>-->sdk保存位置,自定义 <add key="LinuxDownLoadRoot" value="http://xxx.xx.xx.xxx"/> <!--视频录制服务设置-->C#用于发送linux命令的组件
百度:Tamir.SharpSSH.dll
阅读全文
0 0
- C#-接入声网SDK实现网页版1对1视频通话以及边录制边上传
- Android利用环信SDK 3.x实现1对1视频通话
- WebRtc 视频通话(网页对网页)
- 实时语音视频通话SDK如何实现立体声(一)
- 实时语音视频通话SDK如何实现立体声(二)
- Android如何实现边采集边上传
- Android如何实现边采集边上传
- Android如何实现边采集边上传
- 主流视频通话SDK比较
- 主流视频通话SDK比较
- unity接入ShareREC(iOS)SDK --获取录制的包含外部声音的视频路径
- jmf实现视频通话
- AnyChatSDK 实现视频通话
- 小结1--视频接入
- Android视频录制以及声音视频特效实现研究
- 边采集边上传
- WebRtc技术实现网页摄像头录制视频并上传服务器
- 提供手机视频通话SDK技术服务
- 常见浏览器兼容性问题与解决方案
- 非211计算机保研的那些事
- 算法:单调递增最长子序列
- java 集合总结
- 使用布局(layout)资源
- C#-接入声网SDK实现网页版1对1视频通话以及边录制边上传
- 数据结构—位图
- 什么是分布式系统
- 将指定工程文件脱离svn版本控制
- quartz集群(二)
- BUG
- 三种数据不一致性
- a标签传输汉字和form传输汉字的区别,以及转utf-8
- Java Web后端--入职技能任务单(SSM框架搭建)三