这段时间一直都在研究推流的技术,经过断断续续将近两个月的摸索实践,终于能稳定地推流了。

这个demo的主要功能就是将采集到的摄像头或桌面的视频、以及麦克风或声卡的音频数据推到Nginx-RTMP服务器上,再由Web浏览器去拉流并播放。

  接下来介绍Demo整个功能的实现原理和代码逻辑,大家可以从文末下载源码后,对照源码再来看下面的介绍就会更清晰些。

一.客户端实现

客户端的界面效果图如下所示:

  

  客户端的具体功能:可以采集摄像头或者桌面图像,也可以采集麦克风与声卡的声音 并将它们推送到Nginx流服务器上。

  从上面功能就可以看出这里需要有多个采集器来采集相关的数据:摄像头采集器、麦克风采集器、桌面采集器、以及声卡采集器。如果需要将麦克风或声卡的声音混音(比如,主播一边用电脑播放背景音乐一边播讲),则还需要使用混音器。

在点击启动设备按钮时,我们就需要来启动各自对应的采集器,并开始采集:

        #region 设置采集器
if (this.radioButton_desktop.Checked)
{
//桌面采集器
//如果需要录制鼠标的操作,第二个参数请设置为true
this.desktopCapturer = CapturerFactory.CreateDesktopCapturer(frameRate, false,new Rectangle(0,0,1920,1080));
this.desktopCapturer.ImageCaptured += this.Form1_ImageCaptured; }
else if (this.radioButton_camera.Checked)
{
//摄像头采集器
this.cameraCapturer = CapturerFactory.CreateCameraCapturer(0, this.defaultVideoSize, frameRate);
this.cameraCapturer.ImageCaptured += new CbGeneric<Bitmap>(this.Form1_ImageCaptured);
} if (this.checkBox_micro.Checked)
{
//麦克风采集器
this.microphoneCapturer = CapturerFactory.CreateMicrophoneCapturer(0);
this.microphoneCapturer.CaptureError += new CbGeneric<Exception>(this.CaptureError);
} if (this.checkBox_soundCard.Checked)
{
//声卡采集器 【目前声卡采集仅支持vista以及以上系统】扬声器 属性 高级设置 16位 48000HZ(DVD音质)
this.soundcardCapturer = CapturerFactory.CreateSoundcardCapturer();
this.soundcardCapturer.CaptureError += this.CaptureError;
if (this.soundcardCapturer.SampleRate != 48000)
{
throw new Exception("声卡采样率必须为48000HZ");
}
audioSampleRate = this.soundcardCapturer.SampleRate;
this.channelCount = this.soundcardCapturer.ChannelCount;
} if (this.checkBox_micro.Checked && this.checkBox_soundCard.Checked)
{
//混音器
this.audioMixter = CapturerFactory.CreateAudioMixter(this.microphoneCapturer, this.soundcardCapturer,
SoundcardMode4Mix.DoubleChannel, true);
this.audioMixter.AudioMixed += audioMixter_AudioMixed;
audioSampleRate = this.audioMixter.SampleRate;
this.channelCount = this.audioMixter.ChannelCount;
} else if (this.checkBox_micro.Checked)
{
this.microphoneCapturer.AudioCaptured += audioMixter_AudioMixed;
}
else if (this.checkBox_soundCard.Checked)
{
this.soundcardCapturer.AudioCaptured += audioMixter_AudioMixed;
}
#endregion #region //开始采集
if (this.checkBox_micro.Checked)
{
this.microphoneCapturer.Start();
}
if (this.checkBox_soundCard.Checked)
{
this.soundcardCapturer.Start();
} if (this.radioButton_camera.Checked)
{
this.cameraCapturer.Start();
}
else if (this.radioButton_desktop.Checked)
{
this.desktopCapturer.Start();
}
#endregion

  开始采集后,我们就可以点击开始推流按钮,初始化推流器,将采集的数据推到流服务器上:

        //TODO 开始录制桌面,依据 声音复选框 来选择使用 声卡 麦克风 还是混合录制, 图像复选框来选择 图像的采集器
try
{
int videoWidth = 0, videoHeight = 0;
if (this.radioButton_desktop.Checked)
{
videoWidth = this.desktopCapturer.VideoSize.Width;
videoHeight = this.desktopCapturer.VideoSize.Height;
}
else {
videoWidth = this.defaultVideoSize.Width;
videoHeight = this.defaultVideoSize.Height;
}

this.streamPusher.UpsideDown4RGB24 = true;
this.streamPusher.Initialize("192.168.1.56", 9000, true, this.streamID, videoWidth, videoHeight, NPusher.InputAudioDataType.PCM, NPusher.InputVideoDataType.RGB24,this.channelCount);
this.isPushing = true;
this.button_start.Enabled = false;
this.button_stop.Enabled = true;
this.button3.Enabled = false;
this.ShowStateMsg("推流中...");
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}

   上述代码中红色标记部分,即是初始化推流器:由于我们采集到的视频是H264数据,声音是PCM数据,所以,在初始化时,选择InputAudioDataType.PCM和InputVideoDataType.RGB24。

在采集时我们预定了对应的采集事件,采集到数据后我们就加到推流器中,它会自动将数据推到我们的Nginx服务器上:

        //采集到的视频或桌面图像
void Form1_ImageCaptured(Bitmap img)
{
if (this.radioButton_camera.Checked)//显示摄像头的图像到窗体
{
Image copy = ESBasic.Helpers.ImageHelper.CopyImageDeeply(img);
this.DisplayVideo(copy);
}
if (this.isPushing)
{
img.RotateFlip(RotateFlipType.Rotate180FlipY);
byte[] data = ESBasic.Helpers.ImageHelper.GetRGB24CoreData(img);
this.streamPusher.PushVideoFrame(data);
}
} //采集到的声卡、麦克风、声卡麦克风的混音数据
void audioMixter_AudioMixed(byte[] audioData)
{
if (this.isPushing)
{
if (this.checkBox_soundCard.Checked && !this.checkBox_micro.Checked)
{
audioData = AudioHelper.ConvertTo16kFrom48k(audioData ,this.channelCount);
}
this.streamPusher.PushAudioFrame(audioData);
}
}

  代码中标记为红色的部分PushVideoFrame和PushAudioFrame方法,即是将采集到的视频帧和音频帧推流到流服务器。

二.Nginx服务端部署

  这里可以在文末网盘下载服务端来部署到服务器上,其中有3个地方需要根据服务器的配置自行做修改

  1. conf目录下nginx.conf 文件中 rtmp 端口 9000、http 端口8000 。
  2. html目录下index.html 文件中 设置流服务器的IP
    src: "rtmp://192.168.1.56:9000/hls/"+pqs._parameters.id[0],    //将192.168.1.56改成流服务器的IP
  3. html目录下mobile.html 文件中 也同样设置流服务器的IP

    var hls_url = "http://192.168.1.56:8000/hls/" + pqs._parameters.id[0] + ".m3u8"; //将192.168.1.56改成流服务器的IP

三.浏览器访问

  PC的浏览器访问 http://192.168.1.56:8000/?id=aa01,其中aa01为流的ID。效果如下图

  

  手机浏览器访问 http://192.168.1.56:8000/mobile.html?id=aa01,其中aa01为流的ID。效果如下图

  

四.源码下载

(1)C#推流RTMP(摄像头、麦克风、桌面、声卡)-源码

(2)Nginx部署版下载 网盘下载 (提取码: 1234)  

注:查看Nginx运行状态可访问: http://192.168.1.56:8000/stat 。

C#推流RTMP,摄像头、麦克风、桌面、声卡(附源码)的更多相关文章

  1. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码]

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码] 开始,我们有了一系列的解决方案,我们将动手搭建新系统吧. 用 ...

  2. 为SRS流媒体服务器添加HLS加密功能(附源码)

    为SRS流媒体服务器添加HLS加密功能(附源码) 之前测试使用过nginx的HLS加密功能,会使用到一个叫做nginx-rtmp-module的插件,但此插件很久不更新了,网上搜索到一个中国制造的叫做 ...

  3. 13行代码实现:Python实时视频采集(附源码)

    一.前言 本文是<人脸识别完整项目实战>系列博文第3部分:程序设计篇(Python版),第1节<Python实时视频采集程序设计>,本章内容系统介绍:基于Python+open ...

  4. 在网站开发中很有用的8个 jQuery 效果【附源码】

    jQuery 作为最优秀 JavaScript 库之一,改变了很多人编写 JavaScript 的方式.它简化了 HTML 文档遍历,事件处理,动画和 Ajax 交互,而且有成千上万的成熟 jQuer ...

  5. Web 开发中很实用的10个效果【附源码下载】

    在工作中,我们可能会用到各种交互效果.而这些效果在平常翻看文章的时候碰到很多,但是一时半会又想不起来在哪,所以养成知识整理的习惯是很有必要的.这篇文章给大家推荐10个在 Web 开发中很有用的效果,记 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  7. C#进阶系列——一步一步封装自己的HtmlHelper组件:BootstrapHelper(三:附源码)

    前言:之前的两篇封装了一些基础的表单组件,这篇继续来封装几个基于bootstrap的其他组件.和上篇不同的是,这篇的有几个组件需要某些js文件的支持. 本文原创地址:http://www.cnblog ...

  8. 轻量级通信引擎StriveEngine —— C/S通信demo(2) —— 使用二进制协议 (附源码)

    在网络上,交互的双方基于TCP或UDP进行通信,通信协议的格式通常分为两类:文本消息.二进制消息. 文本协议相对简单,通常使用一个特殊的标记符作为一个消息的结束. 二进制协议,通常是由消息头(Head ...

  9. jquery自定义插件结合baiduTemplate.js实现异步刷新(附源码)

    上一篇记录了BaiduTemplate模板引擎使用示例附源码,在此基础上对使用方法进行了封装 自定义插件jajaxrefresh.js 代码如下: //闭包限定命名空间 (function ($) { ...

随机推荐

  1. 【机器学习 Azure Machine Learning】使用VS Code登录到Linux VM上 (Remote-SSH), 及可直接通过VS Code编辑VM中的文件

    问题描述 在平常的工作习惯中,如果使用VS Code做脚本的开发,是一个非常好用的工具,现在也可以通过VS Code的不同方式来连接到Linux VM中(ssh), 第一种是VS Code的Termi ...

  2. Java语言对对象采用的是引用传递还是按值传递?

    按值调用表示方法接收的是调用者提供的值:而按引用调用表示方法接收的是调用者提供的变量地址:一个方法可以修改传递引用所对应的变量值, 而不能修改传递值调用所对应的变量值: Java语言对对象采用的是引用 ...

  3. StringUtils工具类(Apache lang3 )

    引入依赖 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons- ...

  4. Spark如何删除无效rdd checkpoint

    spark可以使用checkpoint来作为检查点,将rdd的数据写入hdfs文件,也可以利用本地缓存子系统. 当我们使用checkpoint将rdd保存到hdfs文件时,如果任务的临时文件长时间不删 ...

  5. JavaScript实现基于数组的栈

    class StackArray {   constructor() {     this.items = [];   }   push(element) {     this.items.push( ...

  6. 数据结构(C++)——顺序表

    顺序表和链表的比较 1.存取方式 顺序表可以随机访问,而链表只能从表头顺序查找.(因此经常查找顺序表某一个元素时,顺序表更适合) 2.逻辑结构与物理结构 顺序表中,逻辑上相邻的元素,其物理存储位置也相 ...

  7. MySQL全面瓦解3:数据类型介绍与分析

    概述 MySQL支持很多数据类型,以便我们能在复杂的业务场景中支持各种各样的数据格式,存储适当的数据内容.我们在设计数据库时,正确的使用数据库类型对整个数据库的整洁和高效,会有很大的帮助. 目前常用的 ...

  8. Spark性能调优的方法

    原则一:避免创建重复的RDD 通常来说,我们在开发一个Spark作业时,首先是基于某个数据源(比如Hive表或HDFS文件)创建一个初始的RDD:接着对这个RDD执行某个算子操作,然后得到下一个RDD ...

  9. Redis 数据结构与编码技术 (Object Encoding)

    数据结构实现 相信大家对 redis 的数据结构都比较熟悉: string:字符串(可以表示字符串.整数.位图) list:列表(可以表示线性表.栈.双端队列.阻塞队列) hash:哈希表 set:集 ...

  10. spring mvc 中获取HttpServletRequest ,HttpServletResponse

    spring中的bean最常用的 singleton 模式 如果要在springmvc Controller 中获取  HttpServletRequest ,HttpServletResponse ...