iOS播放IPCamera之MJ流媒体解析

a3369c54d0b9de18.jpg

最近开始将陆续写写iOS 流媒体相关的文章,其中包括简单的实现方式和FFmpeg,主要写写这么做了这么多次监控方面的心得吧,也都是自己的理解,如果有误的地方还望见谅并指出。

Jasper(ZR)原创文章,转载请注明出处。

先大概说一下,iOS播放IPCamera,也是就是网络摄像头,多用于做监控功能,实现的方式有大致有几种1.解码MJ流;2.FFmpeg解码流媒体;3.使用Kxmovie。这里面1最简单,但是只能看见图像没有声音,下面会解释为什么没有声音。2是使用开源FFmpeg,难度不小,不适合新手接触,因为想实现功能简单,但是想做的好很难,博主做过多次FFmpeg,每次都觉得仍有不足的地方,而且加上声音的时候想做音视频同步解码也是个不小的难点,总之还是那句话,做出来容易,想做好不容易。3是使用现成的Kxmovie,也是一个使用FFmpeg的开源播放器项目,完成度很高,可以直接引用进项目里面~~~大体就先说这些,方法还有其他的,比如VLC等等,Kxmovie和VLC都是把FFmpeg用到如火纯情的地步(个人感觉)。

本文先写MJ流,一起看一下百度百科摘抄:

M-JPEG是一种基于静态图像压缩技术JPEG发展起来的动态图像压缩技术,可以生成序列化的运动图像。其主要特点是基本不考虑视频流中不同帧之间的变化,只单独对某一帧进行压缩,其压缩倍数为20~80倍,适合静态画面的压缩,分辨率可从352×288到704×576。

这里可以得知MJ是一种静态图片压缩成的视频流,所以一般不包含声音(除非复合的音视频流),每一帧都可以当成一张JPEG图片去解,JPEG的标志位是0xFF 0xD8开始,0xFF 0xD9结束,由此分析此种简单的解码办法其原理就是按照标志去解析每帧JPEG图像再连续播放出来。

这样分析一来解码就很容易了,用网络请求获取到连续的MJ流之后进行JPEG解码之后输出,显示可以有多种方式,简单点的就是imageView贴图,虽然方法很low,但是真的很简单,但是效果和效率一般,需求不高还可以~~再者就是SDL,这块需要了解的知识也很多,常见的是FFmpeg + SDL,这里先不讲这些~~~因为SDL博主掌握的也不好(*^__^*) 。。。

我的做法是自定义一个CameraView,继承自imageView,内部封装了网络请求,和一些常用的方法(暂停、播放、截图等),解码一帧图片后就可以直接给自身贴图。

这里面其他的方法就不讲了,只说说怎么解每一帧图片,首先知道每帧JPEG的开始和结束是D8和D9,那么我们先define END_MARKER_BYTES {0xFF, 0xD9},然后网络接收到的是持续的data,用NSRange判断此次接收到的NSData里面是否包含结束标志,这里默认每个Data都是每帧的开始,如果检测到结束标志,再把起始到结束这段Data用Range获取出来转成img,并贴图。

这里面有两套解析方式,一套是刚才上面描述的,还注释了一段,注释了一段是因为担心上面提取出来的Data作为一帧,剩下的Data可以看见已经释放了,然后又是从头开始的解下一帧,担心这样如果每次截取到标识符之后后面还有下一帧的部分Data,这样就会造成丢帧。所以当时写了一个交换,就是每次获取完一帧data,把剩余的data留下来,之后跟下一次的先拼接再解析,这样理论上可以防止丢帧,后来看log的时候,发现我身边的摄像头每次都是完整的一个Data正好一帧,我就把这套方案给注释掉了。

总结,对于流媒体,我可以说熟悉,因为早在2011年的时候就在大学做流媒体闭路电视转播,很早就接触流媒体的概念,也可以说不熟悉,因为至今我对它了解的深度依然不够,很多概念也是不全明白,现在了解的这些是2013年做智能家居的项目,做到FFmpeg流媒体监控的时候,翻看国内外资料了解到的,给我的体会是,很多东西,想一次就做好基本上是不可能的,比如FFmpeg,我先后做过它相关3次,每次体会到的都比以前要深,所以,多思考,多做,都理解把~~~我觉得多思考,也是程序猿进阶的方式之一。

我尽量写博文,想把自己的思考方式分享给大家,我如果要是做一个以前没接触过的东西,那么我第一件事会是去百度google他的功课,基本概念,等等,只有知道了你要做的是什么,你才能磨刀不误砍柴工~~~以上都是我个人的见解啦~如有不妥,希望大家指出,也怕误导到别人。

对于流媒体,我现在接触到使用范围是这些,1.项目中使用,比如监控项目。2.diy玩家使用,比如我现在也做做FPV机器人开发,iPad遥控的那种,所以也会涉及到摄像头。3.一些视频播放的需求项目。总之熟悉了流媒体,还是很好玩的~~

下面上一些我做的APP截图把(智能家居的两张是用FFmpeg开发的)~~~

288ff4.jpg
1.jpg
1.png
2.jpg

6 条评论

  1. //网络视频
    -(void)netWorkVideo
    {
    //NSString* urlStr=[NSString stringWithFormat:@”http://192.168.1.1:8080?action=naspshot”];
    NSString* urlS=@”http://192.168.1.1:8080?action=naspshot”;
    NSURL* url= [NSURL URLWithString:urlS];
    NSLog(@”url==%@”,url);
    NSURLRequest*request=[[NSURLRequest alloc]initWithURL:url];
    //通过url,获得requet;//根据request得到Connection;
    NSURLConnection* myConnection=[[NSURLConnection alloc]initWithRequest:request delegate:self];
    [myConnection start];
    }
    //每接收一段数据就会调用此函数
    -(void)connection:(NSURLConnection*)connection didReceiveData:(NSData *)data
    {
    // NSLog(@”==didReceiveData:data==%@==”,data);
    [self.allData appendData:data];
    // NSLog(@”==didReceiveData==self.allData==%@==”,self.allData);
    // http://zasper.net/archives/4891
    NSLog(@”connectDidReceiveData-endMarkerData-%@”,endMarkerData);
    NSRange endRange=[self.allData rangeOfData:endMarkerData
    options:0
    range:NSMakeRange(0, self.allData.length)];
    long endLocation=endRange.location + endRange.length;
    if(self.allData.length >= endLocation){
    NSRange imageRange=NSMakeRange(0, endLocation);
    NSData* imageData=[self.allData subdataWithRange:imageRange];
    UIImage* receivedImage=[UIImage imageWithData:imageData];
    if(receivedImage){
    self.imageView.image = receivedImage;
    NSLog(@”解码图片”);
    }
    }
    }
    但是---endMarkerData是空---请明示啊
    2016-03-04 17:03:50.582 JIYI[2249:168200] connectDidReceiveData-endMarkerData-
    2016-03-04 17:03:50.583 JIYI[2249:168200] connectDidReceiveData-endMarkerData-

  2. 请问,我使用avplayer播放一个m3u8的流媒体视频怎么截取当前帧,如果知道请告诉我,谢谢

发表评论

电子邮件地址不会被公开。 必填项已用*标注