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.cs

using 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

 
原创粉丝点击