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

这个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. CentOS7使用firewalld管理防火墙

    firewalld的基本使用 #启动 systemctl start firewalld #关闭 systemctl stop firewalld #查看状态 systemctl status fir ...

  2. drf Serializer使用

    drf序列化 在前后端不分离的项目中,可以使用Django自带的forms组件进行数据验证,也可以使用Django自带的序列化组件对模型表数据进行序列化. 那么在前后端分离的项目中,drf也提供了数据 ...

  3. 12 个设计 API 的安全建议,不要等出事儿了“捶胸顿足”

    原文地址:API Security Best Practices 原文作者:Mark Michon 译者 & 校正:HelloGitHub-小鱼干 & HelloGitHub-鸭鸭 虽 ...

  4. java-try catch中return在finally之前 还是之后执行

    finally语句在return语句执行之后return返回之前执行的. finally块中的return语句会覆盖try块中的return返回. 如果finally语句中没有return语句覆盖返回 ...

  5. C2. Balanced Removals (Harder) (幾何、思維)

    Codeforce 1237C2 Balanced Removals (Harder) (幾何.思維) 今天我們來看看CF1237C2 題目連結 題目 給你偶數個三維座標點,每次選其中兩點,如果兩點為 ...

  6. CodeForces 1408G Clusterization Counting

    题意 给定 \(n\) 个点的无向带权完全图,边权为 \(1\sim\frac{n(n-1)}{2}\).对于满足 \(1\leq k\leq n\) 的每个 \(k\) 求出将原图划分成 \(k\) ...

  7. CodeForces 1426F Number of Subsequences

    题意 给定一个长度为 \(n\) 的串,只包含 abc 和通配符.通配符可以替换 abc 的一个.求所有得到的字符串中子序列 abc 出现的次数,对 \(10^9+7\) 取模. \(\texttt{ ...

  8. Luogu P5072 [Ynoi2015]盼君勿忘

    题意 给定一个长度为 \(n\) 的序列 \(a\) 和 \(m\) 次询问,第 \(i\) 次询问需要求出 \([l_i,r_i]\) 内所有子序列去重之后的和,对 \(p_i\) 取模. \(\t ...

  9. Java学习的第二十四天

    1. 目录管理 2.文件方法太多记不清 3.明天学习流和流的分类

  10. Linux 网络编程的5种IO模型:异步IO模型

    Linux 网络编程的5种IO模型:异步IO模型 资料已经整理好,但是还有未竟之业:复习多路复用epoll 阅读例程, 异步IO 函数实现 背景 上一讲< Linux 网络编程的5种IO模型:信 ...