C# MJPEG 客户端简单实现方法
MJPEG协议在此不在过多描述,这里主要介绍一下使用C#中的PictureBox控件频繁刷新MJPEG传输过来的图片,高频率的图片刷新实现视频播放效果;
环境:
服务端
MJPEG服务器使用的是手机的DroidCam,很方便的一个MJPEG服务器,端口4747,打开软件就能使用,并且还附带了web端展示。
客户端
MJPEG客户端使用C#Http请求,并获取到响应MJPEG视频流,截取到图片数据部分,用PictureBox展示图片内容。
整体流程:
1.C#向MJPEG发送请求URL,请求URL是MJPEG服务器定的,例如DroidCam,可以通过访问:{手机所在IP}:4747
图片中红框内容就是视频流的地址,使用GET请求后,服务端就会一直往这个请求的响应内容中写照片信息,直到这个GET请求断开为止(客户端、服务端其中一个主动退出)
ps:如果使用DroidCam当服务器,建议使用手机热点、或者手机通过数据线共享链接方式链接,因为MJPEG实际是把视频的每一帧截成一张图片发送过来的,非常的占带宽,并且网速不好还有图片数据不完整情况,需要手动处理跳过.手机开WiFi热点电脑链接,手机端IP:192.168.43.1:4747,手机数据线连接usb网络共享,手机端IP:192.168.43.129:4747;
2.C#读响应头,找出视频流中每张图片的分隔符,读取每张图片前Content-Length长度,读图片;
3.每读到一张图片,刷新一次PictureBox控件;
具体实现
//创建一个HTTP请求,只要请求不结束,MJPEG服务端会一直给请求的响应体中发送实时图片内容
HttpWebRequesthwRequest=(System.Net.HttpWebRequest)WebRequest.Create("请求URL地址");
hwRequest.Method="GET";
HttpWebResponsehwResponse=(HttpWebResponse)hwRequest.GetResponse();
//读boundary指定的每张图片分隔符,DroidCam为:--dcmjpeg
stringcontentType=hwResponse.Headers["Content-Type"];
stringboundryKey="boundary=";
stringboundary=contentType.Substring(contentType.IndexOf(boundryKey)+boundryKey.Length);
 
//拿到响应体流
Streamstream=hwResponse.GetResponseStream();
stringheaderName="Content-Length:";
//临时存储字符串数据
StringBuildersb=newStringBuilder();
intlen=1024;
while(true)
{
    //读取一行数据
    while(true){
        charc=(char)stream.ReadByte();
        //Console.Write(c);
        if(c=='\n'){
            break;
        }
        sb.Append(c);
    }
    stringline=sb.ToString();
    sb.Remove(0,sb.Length);
    //当前行中是否包含Content-Length:
    inti=line.IndexOf(headerName);
    if(i!=-1){
        //每张图片前有一段图片简单介绍(图片类型、长度),这里只关心长度(Content-Length:)后边的值,用于后续读取图片
        intimageFileLength=Convert.ToInt32(line.Substring(i+headerName.Length).Trim());
        //Content-Length:xxx完后会有一个/r/n的换行符,换行符后才是真正的图片数据(不知道是DroidCam自己这样还是都这样...)
        //这里跳过/r/n
        stream.Read(newbyte[2],0,2);
        //开始读取图片数据,imageFileLength就是读到的Content-Length:后的长度
        byte[]imageFileBytes=newbyte[imageFileLength];
        stream.Read(imageFileBytes,0,imageFileBytes.Length);
        //JPEG的文件头是:FFD8FF,文件尾是:FFD9,非常重要,调试时最好打印一下,便于区分读入的数据是否正好时图片的所有内容
        //Console.WriteLine("文件头:"+imageFileBytes[0].ToString("X")+""+imageFileBytes[1].ToString("X")+""+imageFileBytes[2].ToString("X")+""+imageFileBytes[3].ToString("X")+""+imageFileBytes[4].ToString("X"));
        //Console.WriteLine("文件尾:"+imageFileBytes[imageFileLength-2].ToString("X")+""+imageFileBytes[imageFileLength-1].ToString("X"));
        //此处做了一个如果读入文件不全时处理,图片越大,程序循环读取速度越快,越有可能导致读取文件不全情况...,如果有好的办法解决希望前辈们指教,非常感谢!
        //文件尾是否是FFD9
        if(imageFileBytes[imageFileLength-2].ToString("X")!="FF"&&imageFileBytes[imageFileLength-1].ToString("X")!="D9")
        {
            //读入文件内容不全,跳过次文件,让流位置跳到下次图片开始位置
            //Console.WriteLine("开始矫正...");
            charl='0';
            while(true)
            {
                charc=(char)stream.ReadByte();
                //这里只判断了--dcmjpeg中的前两个字符--,当读到的流中连续两个字符是--时,表示流已读到下次图片开始位置
                if(l==boundary[0]&&c==boundary[1])
                {
                    break;
                }
                l=c;
            }
        }
        else
        {
            //读取图片成功!
            //accessImageHandler是一个Action,用于把图片实时写到PictureBox控件中
            accessImageHandler(imageFileBytes);
        }
        //这里适当睡几十毫秒,会降低点图片读入不全情况,还未找到图片随机读取不全情况原因...
        Thread.Sleep(sleep);
    }
}
stream.Close();
hwResponse.Close();
可以先试着读一张图片,通过FileStream写成文件,看看写成的文件是否能用Windows图片查看器查看,如果不能并且机器上有PS的话,可以试着用PS打开一下,PS对图片支持的比较好,如果文件头多写两个其他字符它是可以过滤掉的。但是最后的效果还是需要Windows图片查看器能看,只有查看器能看,PictureBox才能正常显示内容,否则在打开图片时会报内存不足异常!
多调试几遍,查看一下请求头、请求尾是否正确。
如果有兴趣,可以看下我调试例子:链接:https://pan.baidu.com/s/1oihxe8ficnCm4gcaE9SQBg提取码:atwh,例子内容有点乱,并且很不完善,希望对你多少有些帮助!
补充使用IP摄像头APP连接时有密码情况:
MJPEG协议中应该是没规定加密情况,这个加密(httpauth)应该是IP摄像头APP规定的。
在使用IP摄像头App读MJPEG流时发现需要密码,使用浏览器直接访问会弹出输入账号密码框,通过解析请求发现其实就是在请求头中添加了一个请求头Authorization:
YWRtaW46YWRtaW4=是我在APP中设置的用户名(admin):密码(admin)拼接起来后转成Base64的字符串, admin:admin 转成base64为: YWRtaW46YWRtaW4=
所以在修改一下请求头就可以了:
hwRequest.Headers.Add("Authorization","Basic"+Convert.ToBase64String(Encoding.UTF8.GetBytes(user+":"+pass)));
这里hwRequest就是HttpWebRequest
user是用户名,pass是密码
以上就是C#MJPEG客户端简单实现方法的详细内容,更多关于C#MJPEG客户端简单实现的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
