• C# 实现腾讯云多路直播流的云端混合录制


    目录

     应用场景

    腾讯云直播和云点播

    产品架构

    混流显示示例 

    关键代码

    API实现

    小结


     应用场景

    在云考试或视频面试中,除了对考生、考官的实时音视频监控以防止作弊行为的发生以外,对直播流的音视频录制也尤为重要,可做为后期证据材料进行追溯、举证。

    在实际的应用场景中,会有多路直播流的产生,因此根据业务需要可以将多路直播流混合录制成一个视频文件,腾讯云称其为云端混录。混录后的视频可以更加直观的进行回放,可以同时查看多路直播流的视频情况。

    混录场景举例:

    场景1:在线考试回放,三路混流。主图像显示考生面部及背后方视频、副图1显示考生正前方视频、副图2显示屏幕共享视频。

    场景2:一对一视频面试,两路混流。主图显示考生答题情况视频、副图1显示考官提问情况视频。

    腾讯云直播和云点播

    云端混流涉及腾讯云直播和云点播服务。

    腾讯云直播(Cloud Streaming Services,CSS)提供极速、稳定、专业的云端直播处理服务,根据业务的不同直播场景需求,提供标准直播、快直播、云导播台三种服务,分别针对大规模实时观看、超低延时直播、便捷云端导播的场景,配合腾讯云视立方·直播 SDK,提供一站式的音视频直播解决方案。具体可访问该网址进行了解:https://cloud.tencent.com/product/css

    腾讯云点播(VOD)面向音视频、图片等媒体,提供制作上传、存储、转码、媒体处理、媒体 AI、加速分发播放、版权保护等一体化的高品质媒体服务。具体可访问该网址进行了解:https://cloud.tencent.com/product/vod

    产品架构

    下图是我们基于腾讯云产品架构图的部分采用和实现方案:

    ​ 

    混流显示示例 

    我们的混流设计输出如下图演示:

    副图1显示在右上方,如果有副图2则依次向下排列。

    ​关于更多布局设计和产品文档请参考腾讯云产品网址进行了解:https://www.tencentcloud.com/zh/document/product/267/37665

    关键代码

    API实现

    1. //混录方法,参数 mixtype,默认为混录,填写cancel为申请取消,roomid为直播房间号,userid1 为主图直播流名称,userid2 为副图直播流名称,videosize为视频分辨率,如“720p"           
    2.  public string MixRecord(string mixtype,string roomid,string userid1,string userid2,string videosize)
    3.             {
    4.                 roomid = int.Parse(roomid).ToString();  //对roomid进行一次转int的特殊处理
    5. //计算主图尺寸
    6.                 int vw = 640;
    7.                 int vh = 480;
    8.                 switch (videosize)
    9.                 {
    10.                     case "240p":
    11.                         vw=320;vh=240;break;
    12.                     case "360p":
    13.                         vw=640;vh=360;break;
    14.                     case "480p":
    15.                         vw=640;vh=480;break;
    16.                     case "720p":
    17.                         vw=1280;vh=720;break;
    18.                     case "1080p":
    19.                         vw=1920;vh=1080;break;
    20.                     case "1440p":
    21.                         vw=2560;vh=1440;break;
    22.                     case "4K":
    23.                         vw=3840;vh=2160;break;
    24.  
    25.                 }
    26. //计算副图尺寸
    27.                 int svw = vw / 4;
    28.                 int svh = vh / 4;
    29.                 switch (videosize)
    30.                 {
    31.                     case "240p":
    32.                         svh=60; break;
    33.                     case "360p":
    34.                         svh=90; break;
    35.                     case "480p":
    36.                         svh=120; break;
    37.                     case "720p":
    38.                         svh=180; break;
    39.                     case "1080p":
    40.                         svh=270; break;
    41.                     case "1440p":
    42.                         svh=360; break;
    43.                     case "4K":
    44.                         svh = 540; break;
    45.                 }
    46. //提供在腾讯云申请的开发帐号及开发KEY等
    47.                 string _appid = "";   
    48.                 string _sdkappid = "";
    49.                 string _key = "";
    50. //调用腾讯云混流API
    51.                 string _interface = "Mix_StreamV2";
    52.                 MD5 md5 = new MD5();
    53.                 string _t=getTimestamp(60); //加偏移量60秒
    54.                 string _sign = md5.GetMD5Hash(_key+_t).ToLower(); //计算MD5
    55.                 
    56.                 var url = string.Format("http://fcgi.video.qcloud.com/common_access?appid={0}&interface={1}&t={2}&sign={3}",_appid,_interface,_t,_sign);
    57.                 string _timestamp = getTimestamp(0);
    58.                 string _eventid = getTimestamp(0);
    59.                 string _app_id = _appid;
    60.                 string mix_stream_session_id = roomid;
    61.                 string output_stream_id = _sdkappid + "_" + roomid + "_" + userid1 + "_main";
    62.                 string input_stream_id1 = _sdkappid + "_" + roomid + "_" + userid1 + "_main";
    63.                 string input_stream_id2 = _sdkappid + "_" + roomid + "_" + userid2 + "_main";
    64.                 var postData = "{\"timestamp\":" + _timestamp + ",\"eventId\":" + _eventid + ",\"interface\":{\"interfaceName\":\"Mix_StreamV2\",\"para\":{\"app_id\":\"" + _app_id + "\",\"interface\": \"mix_streamv2.start_mix_stream_advanced\",\"mix_stream_session_id\" : \"" + mix_stream_session_id + "\",\"output_stream_id\": \"" + output_stream_id + "\",\"input_stream_list\":[{\"input_stream_id\":\"" + input_stream_id1 + "\",\"layout_params\":{\"image_layer\":1}},{\"input_stream_id\":\"" + input_stream_id2 + "\",\"layout_params\":{\"image_layer\": 2,\"image_width\": "+svw+",\"image_height\": "+svh+",\"location_x\": "+(vw-svw-20).ToString()+",\"location_y\": 20}}]}}}";
    65.                 if (mixtype == "cancel")
    66.                 {
    67.                     postData = "{\"timestamp\":"+_timestamp+",\"eventId\":"+_eventid+",\"interface\":{\"interfaceName\":\"Mix_StreamV2\",\"para\":{\"app_id\":\""+_app_id+"\",\"interface\": \"mix_streamv2.cancel_mix_stream\",\"mix_stream_session_id\" : \""+mix_stream_session_id+"\",\"output_stream_id\": \""+output_stream_id+"\"}}}";
    68.                 }
    69.                 System.Net.HttpWebRequest request;
    70.                 request = (System.Net.HttpWebRequest)WebRequest.Create(url);
    71.                 request.Method = "POST";
    72.                 request.ContentType = "application/json;charset=UTF-8";
    73.                 byte[] payload;
    74.                 payload = System.Text.Encoding.UTF8.GetBytes(postData);
    75.                 request.ContentLength = payload.Length;
    76.                 try
    77.                 {
    78.                     Stream writer = request.GetRequestStream();
    79.                     writer.Write(payload, 0, payload.Length);
    80.                     writer.Close();
    81.                     System.Net.HttpWebResponse response;
    82.                     response = (System.Net.HttpWebResponse)request.GetResponse();
    83.                     System.IO.Stream stream;
    84.                     stream = response.GetResponseStream();
    85.                     List<byte> bytes = new List<byte>();
    86.                     int temp = stream.ReadByte();
    87.                     while (temp != -1)
    88.                     {
    89.                         bytes.Add((byte)temp);
    90.                         temp = stream.ReadByte();
    91.                     }
    92.                     byte[] result = bytes.ToArray();
    93.                     
    94.                     return System.Text.Encoding.Default.GetString(result);
    95.                 }
    96.                 catch (Exception ee)
    97.                 {
    98.                     return  "{\"errcode\":2,\"errmsg\":\"" + ee.Message + "\"}";
    99.                 }
    100.             }//request mix
    1.             public class MD5
    2.             {
    3.                 public  string GetMD5Hash(string str)
    4.                 {
    5.                     //就是比string往后一直加要好的优化容器
    6.                     StringBuilder sb = new StringBuilder();
    7.                     using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
    8.                     {
    9.                         //将输入字符串转换为字节数组并计算哈希。
    10.                         byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
    11.                         //X为     十六进制 X都是大写 x都为小写
    12.                         //2为 每次都是两位数
    13.                         //假设有两个数10和26,正常情况十六进制显示0xA、0x1A,这样看起来不整齐,为了好看,可以指定"X2",这样显示出来就是:0x0A、0x1A。 
    14.                         //遍历哈希数据的每个字节
    15.                         //并将每个字符串格式化为十六进制字符串。
    16.                         int length = data.Length;
    17.                         for (int i = 0; i < length; i++)
    18.                             sb.Append(data[i].ToString("X2"));
    19.                     }
    20.                     return sb.ToString();
    21.                 }
    22.             }
    1. public string getTimestamp(int seconds)
    2. {
    3. TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
    4. return Convert.ToInt64(ts.TotalSeconds+seconds).ToString();
    5. }

    小结

    以上提供的代码仅供参考,在实际的应用中,我们要编写符合自己业务的逻辑,比如多路混流,还要考虑实际的运营成本,比如录制费用、存储费用等。有关腾讯云点播产品的价格情况,可以访问:https://cloud.tencent.com/act/pro/vod

    云端混录在直播时进行合成,腾讯的建议是延迟一段时间再进行API申请,我在这里设置为5秒以后再申请。

    为防止混录失败,我们可以在腾讯云直播管理后台,设置自动生成各路直播流的录制,以做为素材备用(会产生存储费用和录制费用),后期可以下载视频进行再合成。

    以上就是自己的一些分享,时间仓促,不妥之处还请大家批评指正!

  • 相关阅读:
    2022年Java面试题
    【教程】搭建Docker版kali linux(内含MSF)
    1125:矩阵乘法《详解》
    Spring Boot 中自动装配机制的原理
    Xilinx Vivado set *.svh as SystemVerilog Header
    低代码开发如何助力数字化企业管理系统平台构建
    TinyOs操作系统---第3章 任务中断间的共享资源保护
    YOLOv6:又快又准的目标检测框架开源啦
    华为OD机试 - 压缩报文还原 - 正则表达式(Java 2023 B卷 100分)
    使用OpenVINO实现人体动作识别
  • 原文地址:https://blog.csdn.net/michaelline/article/details/134458337