社区
C#
帖子详情
如何捕获摄像头视频流?
雪夜之狼
2010-11-17 10:29:08
hi
我准备写一个C#的远程视频会议系统。目前可以捕获视频和音频到文件,但是如何才能把视频和音频以流(IOStream)的方式捕获,以便进行传输?我是用DirectX.Capture实现捕获到文件的,它似乎没有办法捕获到流...
...全文
1050
26
打赏
收藏
如何捕获摄像头视频流?
hi 我准备写一个C#的远程视频会议系统。目前可以捕获视频和音频到文件,但是如何才能把视频和音频以流(IOStream)的方式捕获,以便进行传输?我是用DirectX.Capture实现捕获到文件的,它似乎没有办法捕获到流...
复制链接
扫一扫
分享
转发到动态
举报
AI
作业
写回复
配置赞助广告
用AI写文章
26 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
szzzp110
2010-11-30
打赏
举报
回复
C# directShow中有 callback事件的
雨师88
2010-11-30
打赏
举报
回复
private int intPosWrite = 0;//内存流中写指针位移
private int intPosPlay = 0;//内存流中播放指针位移
private int intNotifySize = 5000;//设置通知大小
/// <summary>
/// 从字节数组中获取音频数据,并进行播放
/// </summary>
/// <param name="intRecv">字节数组长度</param>
/// <param name="bytRecv">包含音频数据的字节数组</param>
public void GetVoiceData(int intRecv, byte[] bytRecv)
{
//intPosWrite指示最新的数据写好后的末尾。intPosPlay指示本次播放开始的位置。
if (intPosWrite + intRecv <= memstream.Capacity)
{//如果当前写指针所在的位移+将要写入到缓冲区的长度小于缓冲区总大小
if ((intPosWrite - intPosPlay >= 0 && intPosWrite - intPosPlay < intNotifySize) || (intPosWrite - intPosPlay < 0 && intPosWrite - intPosPlay + memstream.Capacity < intNotifySize))
{
memstream.Write(bytRecv, 0, intRecv);
intPosWrite += intRecv;
}
else if (intPosWrite - intPosPlay >= 0)
{//先存储一定量的数据,当达到一定数据量时就播放声音。
buffDiscript.BufferBytes = intPosWrite - intPosPlay;//缓冲区大小为播放指针到写指针之间的距离。
SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。
memstream.Position = intPosPlay;//先将memstream的指针定位到这一次播放开始的位置
sec.Write(0, memstream, intPosWrite - intPosPlay, LockFlag.FromWriteCursor);
sec.Play(0, BufferPlayFlags.Default);
memstream.Position = intPosWrite;//写完后重新将memstream的指针定位到将要写下去的位置。
intPosPlay = intPosWrite;
}
else if (intPosWrite - intPosPlay < 0)
{
buffDiscript.BufferBytes = intPosWrite - intPosPlay + memstream.Capacity;//缓冲区大小为播放指针到写指针之间的距离。
SecondaryBuffer sec = new SecondaryBuffer(buffDiscript, PlayDev);//建立一个合适的缓冲区用于播放这段数据。
memstream.Position = intPosPlay;
sec.Write(0, memstream, memstream.Capacity - intPosPlay, LockFlag.FromWriteCursor);
memstream.Position = 0;
sec.Write(memstream.Capacity - intPosPlay, memstream, intPosWrite, LockFlag.FromWriteCursor);
sec.Play(0, BufferPlayFlags.Default);
memstream.Position = intPosWrite;
intPosPlay = intPosWrite;
}
}
else
{//当数据将要大于memstream可容纳的大小时
int irest = memstream.Capacity - intPosWrite;//memstream中剩下的可容纳的字节数。
memstream.Write(bytRecv, 0, irest);//先写完这个内存流。
memstream.Position = 0;//然后让新的数据从memstream的0位置开始记录
memstream.Write(bytRecv, irest, intRecv - irest);//覆盖旧的数据
intPosWrite = intRecv - irest;//更新写指针位置。写指针指示下一个开始写入的位置而不是上一次结束的位置,因此不用减一
}
}
雨师88
2010-11-30
打赏
举报
回复
public const int cNotifyNum = 16; // 缓冲队列的数目
private int mNextCaptureOffset = 0; // 该次录音缓冲区的起始值
private int mSampleCount = 0; // 录制的样本数目
private int mNotifySize = 0; // 每次通知大小
private int mBufferSize = 0; // 缓冲队列大小
private string mFileName = string.Empty; // 文件名
private FileStream mWaveFile = null; // 文件流
private BinaryWriter mWriter = null; // 写文件
private MemoryStream _memStream = null;
private Capture mCapDev = null; // 音频捕捉设备
private CaptureBuffer mRecBuffer = null; // 缓冲区对象
private Notify mNotify = null; // 消息通知对象
private WaveFormat mWavFormat; // 录音的格式
private Thread mNotifyThread = null; // 处理缓冲区消息的线程
private AutoResetEvent mNotificationEvent = null; // 通知事件
public Form3()
{
InitializeComponent();
//初始化音频捕捉设备
InitCaptureDevice();
//设定录音格式
mWavFormat = CreateWaveFormat();
}
/// <summary>
/// 设定录音结束后保存的文件,包括路径
/// </summary>
/// <param name="filename"></param>
public void SetFileName(string filename)
{
mFileName = filename;
}
/// <summary>
/// 开始录音
/// </summary>
public void RecStart()
{
// 创建录音文件
CreateSoundFile();
byte[] bytMemory = new byte[100000];
_memStream = new MemoryStream(bytMemory, 0, 100000, true, true);
InitCaptureDevice();
// 创建一个录音缓冲区,并开始录音
CreateCaptureBuffe();
// 建立通知消息,当缓冲区满的时候处理方法
InitNotifications();
mRecBuffer.Start(true);
}
/// <summary>
/// 停止录音
/// </summary>
public void RecStop()
{
if (mNotificationEvent != null)
mNotificationEvent.Set();
//停止录音
mRecBuffer.Stop();
// 写入缓冲区最后的数据
RecordCapturedData();
// 回写长度信息
mWriter.Seek(4, SeekOrigin.Begin);
mWriter.Write((int)(mSampleCount + 36)); // 写文件长度
mWriter.Seek(40, SeekOrigin.Begin);
mWriter.Write(mSampleCount); // 写数据长度
mWriter.Close();
mWaveFile.Close();
mWriter = null;
mWaveFile = null;
}
/// <summary>
/// 初始化录音设备,此处使用主录音设备
/// </summary>
/// <returns></returns>
private bool InitCaptureDevice()
{
// 获取默认音频捕捉设备
CaptureDevicesCollection devices = new CaptureDevicesCollection(); // 枚举音频捕捉设备
Guid deviceGuid = Guid.Empty;
if (devices.Count > 0)
deviceGuid = devices[0].DriverGuid;
else
{
MessageBox.Show("系统中没有音频捕捉设备!");
return false;
}
// 用指定的捕捉设备创建Capture对象
try
{
mCapDev = new Capture(deviceGuid);
}
catch (DirectXException e)
{
MessageBox.Show(e.ToString());
return false;
}
return true;
}
/// <summary>
/// 创建录音格式,此处使用16Bit,16KHz,Mono的录音格式
/// </summary>
/// <returns></returns>
private WaveFormat CreateWaveFormat()
{
WaveFormat format = new WaveFormat();
format.FormatTag = WaveFormatTag.Pcm; // PCM
format.SamplesPerSecond = 44100; // 16KHz
format.BitsPerSample = 16; // 16Bit
format.Channels = 2; // Mono
format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));
format.AverageBytesPerSecond = ((format.BitsPerSample) / 8) * format.Channels * format.SamplesPerSecond;
return format;
}
/// <summary>
/// 创建录音使用的缓冲区
/// </summary>
private void CreateCaptureBuffe()
{
// 缓冲区的描述对象
CaptureBufferDescription bufferdescription = new CaptureBufferDescription();
if (mNotify != null)
{
mNotify.Dispose();
mNotify = null;
}
if (mRecBuffer != null)
{
mRecBuffer.Dispose();
mRecBuffer = null;
}
// 设定通知的大小,默认为1s种
mNotifySize = (1024 > mWavFormat.AverageBytesPerSecond / 8) ? 1024 : (mWavFormat.AverageBytesPerSecond / 8);
mNotifySize -= mNotifySize % mWavFormat.BlockAlign;
// 设定缓冲区大小
mBufferSize = mNotifySize * cNotifyNum;
// 创建缓冲区描述
bufferdescription.BufferBytes = mBufferSize;
bufferdescription.Format = mWavFormat; // 录音格式
// 创建缓冲区
mRecBuffer = new CaptureBuffer(bufferdescription, mCapDev);
mNextCaptureOffset = 0;
}
/// <summary>
/// 初始化通知事件,将原缓冲区分成16个缓冲队列,在每个缓冲队列的结束点设定通知点。
/// </summary>
/// <returns>是否成功</returns>
private bool InitNotifications()
{
if (mRecBuffer == null)
{
MessageBox.Show("未创建录音缓冲区!");
return false;
}
// 创建一个通知事件,当缓冲队列满了就激发该事件
mNotificationEvent = new AutoResetEvent(false);
// 创建一个线程管理缓冲区事件
if (mNotifyThread == null)
{
mNotifyThread = new Thread(new ThreadStart(WaitThread));
mNotifyThread.Start();
}
// 设定通知的位置
BufferPositionNotify[] PositionNotify = new BufferPositionNotify[cNotifyNum + 1];
for (int i = 0; i < cNotifyNum; i++)
{
PositionNotify[i].Offset = (mNotifySize * i) + mNotifySize - 1;
PositionNotify[i].EventNotifyHandle = mNotificationEvent.Handle;
}
mNotify = new Notify(mRecBuffer);
mNotify.SetNotificationPositions(PositionNotify, cNotifyNum);
return true;
}
/// <summary>
/// 将录制的数据写入WAV文件
/// </summary>
private void RecordCapturedData()
{
byte[] CaptureData = null;
int ReadPos, CapturePos, LockSize;
mRecBuffer.GetCurrentPosition(out CapturePos, out ReadPos);
LockSize = ReadPos - mNextCaptureOffset;
if (LockSize < 0)
LockSize += mBufferSize;
LockSize -= (LockSize % mNotifySize);
if (LockSize == 0)
return;
// 读取缓冲区内地数据
CaptureData = (byte[])mRecBuffer.Read(mNextCaptureOffset, typeof(byte), LockFlag.None, LockSize);
// 写入WAV文件
mWriter.Write(CaptureData, 0, CaptureData.Length);
_memStream.Write(CaptureData, 0, CaptureData.Length);
// 更新已经录制的数据长度.
mSampleCount += CaptureData.Length;
// 移动录制数据的起始点,通知消息只负责产生消息的位置,并不记录上次录制的位置.
mNextCaptureOffset += CaptureData.Length;
mNextCaptureOffset %= mBufferSize; // Circular buffer
}
/// <summary>
/// 接收缓冲区满消息的处理线程
/// </summary>
private void WaitThread()
{
while (true)
{
// 等待缓冲区的通知消息
mNotificationEvent.WaitOne(Timeout.Infinite, true);
// 录制数据
RecordCapturedData();
}
}
/// <summary>
/// 创建保存的波形文件,并写入必要的文件头
/// </summary>
private void CreateSoundFile()
{
mWaveFile = new FileStream(mFileName, FileMode.Create);
mWriter = new BinaryWriter(mWaveFile);
// Set up file with RIFF chunk info.
char[] ChunkRiff = { 'R','I','F','F' };
char[] ChunkType = { 'W', 'A', 'V', 'E' };
char[] ChunkFmt = { 'f', 'm', 't', ' ' };
char[] ChunkData = { 'd', 'a', 't', 'a' };
short shPad = 1; // File padding
int nFormatChunkLength = 0x10; // Formt chunk length
int nLength = 0; // File Length,minus first 8 bytes of RIFF description. This will be filld in later.
short shBytesPerSample = 0; // Bytes per sample.
// 一个样本点的字节数目
if (mWavFormat.BitsPerSample == 8 && mWavFormat.Channels == 1)
shBytesPerSample = 1;
else if ((mWavFormat.BitsPerSample == 8 && mWavFormat.Channels == 2) || (mWavFormat.BitsPerSample == 16 && mWavFormat.Channels == 1))
shBytesPerSample = 2;
else if (mWavFormat.BitsPerSample == 16 && mWavFormat.Channels == 2)
shBytesPerSample = 4;
// FIFF 块
mWriter.Write(ChunkRiff);
mWriter.Write(nLength);
mWriter.Write(ChunkType);
// WAVE 块
mWriter.Write(ChunkFmt);
mWriter.Write(nFormatChunkLength);
mWriter.Write(shPad);
mWriter.Write(mWavFormat.Channels);
mWriter.Write(mWavFormat.SamplesPerSecond);
mWriter.Write(mWavFormat.AverageBytesPerSecond);
mWriter.Write(shBytesPerSample);
mWriter.Write(mWavFormat.BitsPerSample);
// 数据块
mWriter.Write(ChunkData);
mWriter.Write((int)0); // The sample length will be written in later.
}
vpoint2011
2010-11-30
打赏
举报
回复
用directshow.net 或者openCV+emgucv之类的开源工具可以获取真正视频流。
昵称就这么定了
2010-11-30
打赏
举报
回复
用flex + FMS 或者flex+red5
li321
2010-11-30
打赏
举报
回复
你在局域网还是广域网做的?
dxh2122
2010-11-27
打赏
举报
回复
试试这个:http://user.qzone.qq.com/154122890/infocenter
我试过可以,C/S下做的
haoxuea
2010-11-27
打赏
举报
回复
高科技啊。
tjava_net
2010-11-27
打赏
举报
回复
关注中,我也想知道,呵呵
雪夜之狼
2010-11-27
打赏
举报
回复
ok我搞定了....
笨办法,一秒钟抓10张图,每10s完整传输一次,其余的比较和上一张图之间的差异,用文本格式发出去....
Qcontriver
2010-11-20
打赏
举报
回复
帮顶一下,我现在做的项目和你这个太相似了,等待高手指点。
我现在还没有多少思路呢。。。。
crackdung
2010-11-20
打赏
举报
回复
它帮到你
http://www.datastead.com/products/tvideograbber/download.html
my blog
http://ufo-crackerx.blog.163.com/
wbxiaozhong
2010-11-20
打赏
举报
回复
学习了
nannan4
2010-11-20
打赏
举报
回复
关注中
yangquanlaohou
2010-11-19
打赏
举报
回复
关注中关注中关注中
csdn_风中雪狼
2010-11-19
打赏
举报
回复
高科技哟,没有搞过呢,
关注中
fangxqian
2010-11-19
打赏
举报
回复
帮你顶起来!
雪夜之狼
2010-11-18
打赏
举报
回复
而且貌似Silverlight也不能用DirectX.Capture这个库的=。=
雪夜之狼
2010-11-18
打赏
举报
回复
我是准备用Silverlight做的。Silverlight的网络通讯我会,关键是如何把摄像头捕获到的画面用Socket进行传输?DirectX.Capture只能捕获到文件……
happyday1799
2010-11-17
打赏
举报
回复
下面是Silverlight Socket通信简单的例子,你先看看
http://blog.csdn.net/banmuhuangci/archive/2009/05/16/4192031.aspx
加载更多回复(5)
Windows下通过DirectShow控制USB
摄像头
,
捕获
视频流
Windows下通过DirectShow控制USB
摄像头
,
捕获
视频流
,并且支持实时抓拍图片,格式为BMP,编译前请自行安装Microsoft DirectX 9.0 SDK
IOS获取本地视频库和获取
摄像头
视频流
获取本地视频库和获取
摄像头
视频流
VideoUpload.zip
Android
摄像头
RTMP推流
使用FFMPEG的RTMP推流修改而来,可以读取
摄像头
和音频推流至流媒体服务器,适合做视频直播的新手参考.
VFW(delphi
摄像头
视频控件)
VFW(delphi
摄像头
视频控件),VFW(delphi
摄像头
视频控件),VFW(delphi
摄像头
视频控件)
DirectShow实现视频播放以及
摄像头
视频采集
MFC+DirectShow实现视频播放器,同事还可以
摄像头
采集视频,并播放出来
C#
111,112
社区成员
642,554
社区内容
发帖
与我相关
我的任务
C#
.NET技术 C#
复制链接
扫一扫
分享
社区描述
.NET技术 C#
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
让您成为最强悍的C#开发者
试试用AI创作助手写篇文章吧
+ 用AI写文章