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实现

//混录方法,参数 mixtype,默认为混录,填写cancel为申请取消,roomid为直播房间号,userid1 为主图直播流名称,userid2 为副图直播流名称,videosize为视频分辨率,如“720p"           
 public string MixRecord(string mixtype,string roomid,string userid1,string userid2,string videosize)
            {
                roomid = int.Parse(roomid).ToString();  //对roomid进行一次转int的特殊处理
//计算主图尺寸
                int vw = 640;
                int vh = 480;
                switch (videosize)
                {
                    case "240p":
                        vw=320;vh=240;break;
                    case "360p":
                        vw=640;vh=360;break;
                    case "480p":
                        vw=640;vh=480;break;
                    case "720p":
                        vw=1280;vh=720;break;
                    case "1080p":
                        vw=1920;vh=1080;break;
                    case "1440p":
                        vw=2560;vh=1440;break;
                    case "4K":
                        vw=3840;vh=2160;break;
 
                }
//计算副图尺寸
                int svw = vw / 4;
                int svh = vh / 4;
                switch (videosize)
                {
                    case "240p":
                        svh=60; break;
                    case "360p":
                        svh=90; break;
                    case "480p":
                        svh=120; break;
                    case "720p":
                        svh=180; break;
                    case "1080p":
                        svh=270; break;
                    case "1440p":
                        svh=360; break;
                    case "4K":
                        svh = 540; break;

                }
//提供在腾讯云申请的开发帐号及开发KEY等
                string _appid = "";   
                string _sdkappid = "";
                string _key = "";
//调用腾讯云混流API
                string _interface = "Mix_StreamV2";
                MD5 md5 = new MD5();
                string _t=getTimestamp(60); //加偏移量60秒
                string _sign = md5.GetMD5Hash(_key+_t).ToLower(); //计算MD5
                
                var url = string.Format("http://fcgi.video.qcloud.com/common_access?appid={0}&interface={1}&t={2}&sign={3}",_appid,_interface,_t,_sign);

                string _timestamp = getTimestamp(0);
                string _eventid = getTimestamp(0);
                string _app_id = _appid;
                string mix_stream_session_id = roomid;
                string output_stream_id = _sdkappid + "_" + roomid + "_" + userid1 + "_main";
                string input_stream_id1 = _sdkappid + "_" + roomid + "_" + userid1 + "_main";
                string input_stream_id2 = _sdkappid + "_" + roomid + "_" + userid2 + "_main";

                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}}]}}}";
 
                if (mixtype == "cancel")
                {
                    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+"\"}}}";
                }
                System.Net.HttpWebRequest request;
                request = (System.Net.HttpWebRequest)WebRequest.Create(url);
                request.Method = "POST";
                request.ContentType = "application/json;charset=UTF-8";
                byte[] payload;
                payload = System.Text.Encoding.UTF8.GetBytes(postData);
                request.ContentLength = payload.Length;
                try
                {
                    Stream writer = request.GetRequestStream();
                    writer.Write(payload, 0, payload.Length);
                    writer.Close();
                    System.Net.HttpWebResponse response;
                    response = (System.Net.HttpWebResponse)request.GetResponse();
                    System.IO.Stream stream;
                    stream = response.GetResponseStream();
                    List<byte> bytes = new List<byte>();
                    int temp = stream.ReadByte();
                    while (temp != -1)
                    {
                        bytes.Add((byte)temp);
                        temp = stream.ReadByte();
                    }
                    byte[] result = bytes.ToArray();
                    
                    return System.Text.Encoding.Default.GetString(result);
                }
                catch (Exception ee)
                {
                    return  "{\"errcode\":2,\"errmsg\":\"" + ee.Message + "\"}";

                }
            }//request mix

            public class MD5
            {
                public  string GetMD5Hash(string str)
                {
                    //就是比string往后一直加要好的优化容器
                    StringBuilder sb = new StringBuilder();
                    using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
                    {
                        //将输入字符串转换为字节数组并计算哈希。
                        byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));

                        //X为     十六进制 X都是大写 x都为小写
                        //2为 每次都是两位数
                        //假设有两个数10和26,正常情况十六进制显示0xA、0x1A,这样看起来不整齐,为了好看,可以指定"X2",这样显示出来就是:0x0A、0x1A。 
                        //遍历哈希数据的每个字节
                        //并将每个字符串格式化为十六进制字符串。
                        int length = data.Length;
                        for (int i = 0; i < length; i++)
                            sb.Append(data[i].ToString("X2"));

                    }
                    return sb.ToString();
                }
            }

            public string getTimestamp(int seconds)
            {
                TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                return Convert.ToInt64(ts.TotalSeconds+seconds).ToString();
            }

小结

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/159917.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring对事务的实现

Spring对事务的支持 事务概述事务的四个处理过程事务的四个特性 引入事务场景Spring实现事务的两种方式声明式事务之注解实现方式 事务概述 在一个业务流程当中&#xff0c;通常需要多条DML&#xff08;insert delete update&#xff09;语句共同联合才能完成&#xff0c;这多…

RT-Thread STM32F407 PWM

为了展示PWM效果&#xff0c;这里用ADC来采集PWM输出通道的电平变化 第一步&#xff0c;进入RT-Thread Settings配置PWM驱动 第二步&#xff0c;进入board.h&#xff0c;打开PWM宏 第三步&#xff0c;进入STM32CubeMX&#xff0c;配置时钟及PWM 第四步&#xff0c;回到R…

鸿蒙4.0开发笔记之DevEco Studio如何使用Previewer窗口预览器(一)

一、预览器作用 DevEco Studio预览器概况在HarmonyOS应用开发过程中&#xff0c;通过使用预览器&#xff0c;可以查看应用的UI效果&#xff0c;方便开发者实时查看应用的运行效果&#xff0c;随时调整代码。 二、打开Previewer预览器 1、正常启动 打开预览器的位置在DevEco…

三十分钟学会zookeeper

zookeeper 一、前提知识 集群与分布式 ​ 集群&#xff1a;将一个任务部署在多个服务器&#xff0c;每个服务器都能独立完成该任务。 ​ 分布式&#xff1a;将一个任务拆分成若干个子任务&#xff0c;由若干个服务器分别完成这些子任务&#xff0c;每个服务器只能完成某个特…

有趣的按钮分享

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 广告打完&#xff0c;我们进入正题&#xff0c;先看效果&#xff1a; 废话不多&#xff0c;上源码&#xff1a; <button class&quo…

【STM32】RTC(实时时钟)

1.RTC简介 本质&#xff1a;计数器 RTC中断是外部中断&#xff08;EXTI&#xff09; 当VDD掉电的时候&#xff0c;Vbat可以通过电源--->实时计时 STM32的RTC外设&#xff08;Real Time Clock&#xff09;&#xff0c;实质是一个 掉电 后还继续运行的定时器。从定时器的角度…

C#,数值计算——插值和外推,双线性插值(Bilin_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 双线性插值 /// interpolation routines for two dimensions /// Object for bilinear interpolation on a matrix. /// Construct with a vector of x1. /// value…

How to import dgl-cu113 如何导入 dgl-cu113

参考这个 从How to import dgl-cu113 如何导入 dgl-cu113https://discuss.dgl.ai/t/how-to-import-dgl-cu113/3381https://discuss.dgl.ai/t/how-to-import-dgl-cu113/3381

Pycharm之配置python虚拟环境

最近给身边的人写了脚本&#xff0c;在自己电脑可以正常运行。分享给我身边的人&#xff0c;却运行不起来&#xff0c;然后把报错的截图给我看了&#xff0c;所以难道不会利用pycharm搭建虚拟的环境&#xff1f;记录一下配置的过程。 第一步&#xff1a;右键要打开的python的代…

Idea 创建 Spring 项目(保姆级)

描述信息 最近卷起来&#xff0c;系统学习Spring&#xff1b;俗话说&#xff1a;万事开头难&#xff1b;创建一个Spring项目在网上找了好久没有找到好的方式&#xff1b;摸索了半天产出如下文档。 在 Idea 中新建项目 填写信息如下 生成项目目录结构 pom添加依赖 <depende…

华夏ERP打包手册

Maven安装及环境配置 1.下载 浏览器搜索maven点击apache Maven 2.选择安装目录&#xff0c;注意不能有中文 3.环境变量配置 点击计算机右键属性>高级系统设置>环境变量 新建系统变量 MAVEN_HOME 变量值是安装目录 进入path点击新建点击编辑&#xff0c;写入% MAVEN_H…

Leetcode刷题详解——不同路径

1. 题目链接&#xff1a;62. 不同路径 2. 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”…

40 _ 初识动态规划:如何巧妙解决“双十一”购物时的凑单问题?

淘宝的“双十一”购物节有各种促销活动,比如“满200元减50元”。假设你女朋友的购物车中有n个(n>100)想买的商品,她希望从里面选几个,在凑够满减条件的前提下,让选出来的商品价格总和最大程度地接近满减条件(200元),这样就可以极大限度地“薅羊毛”。作为程序员的你…

零代码编程:用ChatGPT自动合并多个Word文件

一个文件夹中有多个docx格式的word文档&#xff1a; 想要把它们都合并成一个文件&#xff0c;然后打印&#xff0c;可以在ChatGPT中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个处理word内容的任务&#xff0c;具体步骤如下&#xff1a; 打开文件夹…

WPF 使用.ttf文件中的图标失败

本章讲述问题&#xff1a;WPF 使用.ttf文件中的图标失败&#xff0c;变成白框问题。 在WPF开发过程中&#xff0c;我们需要使用.ttf文件中的图标和文字&#xff0c;但是经常会遇到类似问题&#xff1a;WPF 在XMAL里增加图标字体时没办法实时显示出来只显示一个小方框&#xff0…

DAY57 739. 每日温度 + 496.下一个更大元素 I

739. 每日温度 题目要求&#xff1a; 请根据每日 气温 列表&#xff0c;重新生成一个列表。对应位置的输出为&#xff1a;要想观测到更高的气温&#xff0c;至少需要等待的天数。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 例如&#xff0c;给定一个列…

PVP2 ProVideoPlayer 2.1.6(PVP2多屏幕演示投放软件)

PVP2 ProVideoPlayer2是一款视频播放和管理软件&#xff0c;旨在帮助用户实现多屏幕视频播放和内容管理。它被广泛应用于演出、活动、会议等场合&#xff0c;可以同时播放多个视频、图像和音频文件&#xff0c;并提供强大的控制和管理功能。 PVP2 ProVideoPlayer2具有直观的用…

Leetcode刷题详解——斐波那契数

1. 题目链接&#xff1a;509. 斐波那契数 2. 题目描述&#xff1a; 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1…

【开源】基于Vue和SpringBoot的民宿预定管理系统

项目编号&#xff1a; S 058 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S058&#xff0c;文末获取源码。} 项目编号&#xff1a;S058&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用例设计2.2 功能设计2.2.1 租客角色…

23111707[含文档+PPT+源码等]计算机毕业设计基于javawebmysql的旅游网址前后台-全新项目

文章目录 **软件开发环境及开发工具&#xff1a;****功能介绍&#xff1a;****论文截图&#xff1a;****实现&#xff1a;****代码:** 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 软件开发环境及开发工具&#xff1a; 前端使用技术&a…