视频采集方案
Posted Session__csdn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了视频采集方案相关的知识,希望对你有一定的参考价值。
android视频采集,传输,编码解码的方案总结
-
ipcamera-for-android
服务器 : Android手机充当服务器,使用NanoHTTPD充当服务器
客户端 : 手机或者pc输入http://server ip:8080观看。
这种方案可以参考 ipcamera-for-android开源项目,网址 http://code.google.com/p/ipcamera-for-android/source/checkout
可以运行的源代码可以在这下载 http://download.csdn.net/detail/xiaoliouc/4933558
缺点:android手机必须支持MP4+ARM_BN格式,有些手机不兼容,延时有点厉害,而且用到NDK编程,现在看来方案不可取
现在似乎有个更新的版本,项目名字:android-eye-master
需要的可以百度或者留言, 不过还是用camera类采集视频流。
2 . ivideochat
服务器 : 通过rtmp协议发布服务到red5服务器,可用red5自带的的OFLA Demo测试.
客户端 : Android手机采用juv-rtmp-client.jar包,网上有破解的收费包。
播放端 : 使用支持rtmp协议的播放器播放,如ffplay,vlc,ffmpeg等.
服务器当然是red5了,可以用red5自带的的OFLA Demo做测试.
客户端 ivideochat 下载地址 : http://download.csdn.net/detail/xiaoliouc/4933594
缺点:demo延迟很厉害,仅供参考。rtmp协议半公开,难度比较大
3. Camera and Socket
通过Camera拍摄预览中设置setPreviewCallback实现onPreviewFrame接口,实时截取每一帧视频流数据 . 把一帧一帧的图像压缩通过socket发送到服务器,服务器可以直接观看。而要想让另一台手机也能观看,可以让服务器转发来实现。
服务器 : Android手机通过camera类拍摄视频,把一帧一帧的图像压缩通过socket发 送到服务器,服务器可以直接观看
客户端 : 要想让另一台手机也能观看,可以让服务器转发来实现。
例子网上有很多, http://download.csdn.net/detail/xiaoliouc/4933610
缺点: 通过一帧一帧的发送数据,传输过程耗费大量流量。玩玩可以,但实际项目 中不可取。
4. 流媒体服务器方式:ffmpeg或Getstreamer等获取Camera视频
android手机通过camera类拍摄视频,把拍摄的视频通过h264编码,可以采用软编码(使用x264库或者opencore软件库),java类通过jni调用编译后的so文件来实现。然后通过基于udp的rtp协议传输到服务器。为什么不使用tcp协议呢,因为tcp的重传机制会产生延时和抖动,而单独使用udp传输协议本身是面向无连接的,不能提供质量保证,需要在udp协议只上采用rtp或者rtcp提供流量控制和拥塞控制服务。服务器通过ffmpeg对接收的h264解码并播放。播放可以使用VLC media player。如果对c++比较熟悉,可以看看live555这个开源项目。
缺点:需要懂得的知识很多,jni啊,h264编码解码 ,rtp协议等。使用软编码,效率比较低,且视频质量较差。
5. MediaRecorder+h264
通过Andoriod的MediaRecorder,在SetoutputFile函数中绑定LocalSocket实现 .
android手机通过mediaRecorder类拍摄视频,其中当然包括音频了。把拍摄的视频通过h264编码,可以采用硬编码(面向手机的硬件直接操作),只能针对3gp,mp4视频格式。
方法参考 http://blog.csdn.net/zblue78/article/details/6078040
这篇博客,里面讲的很详细,提取h264的sps,pps,可以参考
http://blog.csdn.net/peijiangping1989/article/details/6934317
winHex是一款好用的16进制查看工具,下载地址 http://download.csdn.net/detail/xiaoliouc/4928773
代码网上有很多,个人理解是:mediaRecorder录制视频(3gp,MP4),可以通过mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());预览视频,通过localsocket发送到本地的localserversocket的h264实时视频流。
这个过程涉及到硬编码,硬编码个人理解是,在预览过程或者提前确定视频的sps,pps,head(一般为0x00000001),不同的手机硬件不一样。把得到的这些参数写入h264,得到正确的h264视频流,然后把流推送到流媒体服务器,使用支持rtsp协议的播放器播放,比如vlc.
6、采用HLS协议,服务器采用nginx,ffmpeg解码。nginx服务器搭建过程,ffmpeg安装过程 见我前几篇文章。
然后用ffmpeg对解码后的mp4文件进行ts切片,生成带有索引的m3u8文件,然后客户端就可以通过浏览器http://ip :port/ *.m3u8访问。
过程貌似是这样的,但自己由于刚接触不到一个周,还不太理解。
7. smartcam的一个开源项目,看了下源代码,发现其实现原理是利用Android 的camera图像的预采集,通过实现PreviewCallback类的回调函数onPreviewFrame,获得camera采集的原始图像数据之后,压成jpeg格式传到pc端。pc端对接收到的jpeg图像序列进行实时解压和显示,就达到了预想的效果。
虽然这种方式稍微显得比较笨拙,这个方式还可以接受。但是不可接受的是jpeg只是帧内压缩,320x280的图片序列,FPS大概是10上下,网络流量就到达了100kb/s以上。这个几乎是无法实际应用的。
于是必须直接传视频流,MPEG4或者H.264格式。貌似我的开发机上(HTC G8)只支持到MPEG4,所以还是选取MPEG4。但是如何实时采集视频流是一个大问题,毕竟在video方面,Android并没有提供一个类似于OnPreviewFrame的回调函数。
想到用opencore或者更为新一点的stagefright,大概看看了其sdk的框架后,马上泄气了,这个太庞大了。在http://www.linuxidc.com/Linux/2011-04/34468.htm的帖子中提到一个很好的解决方案,就是利用MediaRecorder:MediaRecorder的输出路径(其实叫file descriptor)除了是本地文件路径之外,还可以绑定socket端口。也就是说,通过一个socket端口,就可以实时获得MediaRecorder的视频流数据。
6. spydroid -MediaRecorder
相对容易、且效果不错的方法,android手机上搭建rtsp服务器,另一台手机使用 VLC播放器输入rtsp://ip:port/播放视频。具体原理是,通过android手机对mediaRecorder录制视频,把localsocket传输到本地的流经过硬编码,添加rtp头,分离NALU包,根据rtsp协议交互过程把数据发送到对方。
代码参考spydroid了,源代码可以通过svncheckout,能够正常运行,且效果不错。
http://code.google.com/p/spydroid-ipcamera/source/checkout 。
网上还没有分析spydroid源码的文章,等自己空了有机会分析下源代码。
7. 前面讲的都是单向视频,如果是双向视频,其实就是视频会议了,可以参考sipdroid开源代码了
网址 : http://code.google.com/p/sipdroid/source/checkout
Libstreaming是一个开源的流媒体框架,它可以让手机变成一台流媒体服务器,直接在PC端查看手机摄像头的实时画面。值得一提的是它的作者也是spydroid的作者。按照作者的说法,spydroid是利用该库完成流媒体传输的,但据笔者的分析观察,此说法并不十分确切。Libstreaming是spydroid的抽象与升华,RTSP服务器的实现方式也有很大的不同。
巧妇难为无米之炊,我们先把Libstreaming的源代码下载下来。地址:https://github.com/fyhertz/libstreaming 下载完毕后导入eclipse,并新建工程引用该库。这里要颇为注意,新建的工程必须和Libstreaming在同一个盘符下,否则可能出现引用失败的问题。
接下来看看官方文档中给出的创建RTSP服务器的步骤:
1、Add this to your manifest:
<service android:name="net.majorkernelpanic.streaming.rtsp.RtspServer"/>
把RtspServer这个服务在androidManifest文件中进行注册。在libstreaming库中,rtsp服务器是作为service组建实现的,这与spydroid的实现方式完全不一样。
2、You can change the port used by the RtspServer:
Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); editor.putString(RtspServer.KEY_PORT, String.valueOf(1234)); editor.commit();
The port is indeed stored as a String in the preferences, there is a good reason to that. The EditTextPreference object saves its input as a String and cannot easily (one would need to override it) be configured to store it as an Integer.
可以改变rtsp服务器的端口,当然,这是非必需的。默认端口是8086。若想改变端口,必须通过sharedPreference完成。先获取一个指向本activity的sharedPreference的editor对象,再将指定的端口号put进去。至于为什么用String类型而不是用整形存储端口号,主要是考虑到EditTextPreference对象的保存类型是string。
3、Configure its behavior with the SessionBuilder:
SessionBuilder.getInstance() .setSurfaceHolder(mSurfaceView.getHolder()) .setContext(getApplicationContext()) .setAudioEncoder(SessionBuilder.AUDIO_AAC) .setVideoEncoder(SessionBuilder.VIDEO_H264);
sessionBuilder是session的建造者。而session又是服务器与客户端间通信的载体。此部分主要是设置sessionBuilder的一些选项,如音频编码器、视频编码器等等。注意,sessionBuilder用到了单例设计模式,整个程序共享这一个sessionBuilder对象。此外,值得一提的是sessionBuilder的setSurfaceHolder方法,此方法其实没有太大的用处,不过因为android 某些API的限制,使得如果你要录制视频,必须要有有效的surface。
5、Start and stop the server like this:
// Starts the RTSP server context.startService(new Intent(this,RtspServer.class)); // Stops the RTSP server context.stopService(new Intent(this,RtspServer.class));
最后,启动或停止service。
完整的示例:
1、布局文件——activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<SurfaceView
android:id="@+id/surface"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
2、代码——MainActivity.java
package com.dyc.spydroidrtspserver;
import net.majorkernelpanic.streaming.SessionBuilder;
import net.majorkernelpanic.streaming.rtsp.RtspServer;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.view.Menu;
import android.view.SurfaceView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity
SurfaceView surfaceView;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//加载布局
surfaceView=(SurfaceView)findViewById(R.id.surface);
Editor editor=PreferenceManager.getDefaultSharedPreferences(this).edit();//获取sharedPreference的editor对象
editor.putString(RtspServer.KEY_PORT, "1234");//将新的端口号put进去
editor.commit();//提交更改
SessionBuilder.getInstance()
.setAudioEncoder(SessionBuilder.AUDIO_AMRNB)
.setVideoEncoder(SessionBuilder.VIDEO_H264).
setContext(getApplicationContext()).
setSurfaceHolder(surfaceView.getHolder());//配置sessionBuilder对象
this.startService(new Intent(this, RtspServer.class));//启动服务
displayIpAddress();//显示地址
private void displayIpAddress()
WifiManager wifiManager=(WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo=wifiManager.getConnectionInfo();
int address=wifiInfo.getIpAddress();//获取IP地址,注意获取的结果是整数
Toast.makeText(this, "rtsp://"+intToIp(address)+":1234", Toast.LENGTH_LONG).show();//用toast打印地址
private String intToIp(int i) //整形转IP
return (i & 0xFF)+ "." + ((i >> 8 ) & 0xFF)+ "." + ((i >> 16 ) & 0xFF) +"."+((i >> 24 ) & 0xFF );
@Override
public boolean onCreateOptionsMenu(Menu menu)
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
3、androidManifest文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.dyc.spydroidrtspserver"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.dyc.spydroidrtspserver.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="net.majorkernelpanic.streaming.rtsp.RtspServer" />
</application>
</manifest>
我这里只是一个简单的演示 ,所以没有判断到底有没有用wifi。它官方有给示例,详见:https://github.com/fyhertz/libstreaming-examples 可以去看一下
spydroid源码分析(一):介绍spydroid每个包的大体功能
看了接近一周的spydroid源代码,对spydroid这个开源项目有了一定的认识。也许有些理解不一定正确,给后来者一点启示。也是自己对rtsp协议,rtp协议的总结。
在windows下,如果安装了svn,可以通过 svn checkout http://spydroid-ipcamera.googlecode.com/svn/ 下载源码,最新源码是spydroid6.7.1
下载后,导入eclipse工程,直接就可以编译运行。
在这里只关心其src源码部分,其他地方都比较简单,就不介绍了。src源码有以下几个package。
net.majorkernelpanic.http主要是介绍http server,spydroid自身内置http服务器,客户端可以通过在VLC等播放器中输入http://ip:8080/播放
net.majorkernelpanic.mp4主要是介绍提取mp4文件的profile,sps,pps等信息
net.majorkernelpanic.networking 主要是介绍rtsp服务器部分,spydroid自身内置rtsp服务器,客户端可以通过在VLC等播放器中输入rtsp://ip:8086/播放
net.majorkernelpanic.rtp主要是介绍rtp协议通信
net.majorkernelpanic.spydroid主要是activity的界面部分。
net.majorkernelpanic.streaming主要是stream接口和抽象类
net.majorkernelpanic.streaming.audio介绍音频部分
net.majorkernelpanic.streaming.video介绍视频部分
以后针对mp4,networking ,rtp,stream这4个包的内容重点分析
未完待续
这几天空闲的时候在看《struts2技术内幕》这本书,作者downpour说的这句话我很赞同,忘了原文了, 学习开源项目,不是一个包一个包的阅读代码,而是通过动态运行项目,通过断点调试,来获取相关信息。 我也打算用这种方式来看spydroid源代码,但了解每个package大体的功能是必须的。
如果spydroid已经安装到了android手机上,开启这个软件,VLC就可以通过rtsp://手机的ip:8086/访问。在这里以H264来说明spydroid的运行流程,其他类似。在这里需要吧option的encode编码设为h264.调试android源代码,可以通过logcat打印相关信息
程序运行时,进入net.majorkernelpanic.spydroid.SpydroidActivity,该activity 运行时候,开启http server ,rtsp server。这里重点关注rtsp 服务。
进入 net.majorkernelpanic.networking.RtspServer
rtspserver开启后,启动一个线程RequestListenerThread,负责监听客户端(这里用VLC)的请求,public void start() throws IOException
if (running) return;
running = true;
listenerThread = new RequestListenerThread(port,handler);
listenerThread.start();
当有客户端请求的时候,开启一个workerTread线程。一个线程session代表一个请求
new WorkerThread(server.accept(), handler).start();
VLC向rtsp服务器进行交互时,这里就需要用到rtsp协议的内容了,主要分为Options,Describe,Setup,play,teardown这5步骤。
下面是我进行rtsp连接,服务器与客户端请求与响应的详细信息,
//当来自192.168.1.26的VLC客户端向手机服务器发送rtsp://192.168.1.60:8086请求时
Connection from 192.168.1.26 //来自192.168.1.26的请求
//下面的C表示客户端client,S表示服务器server
C-S:OPTIONS rtsp://192.168.1.60:8086/ RTSP/1.0 //可用选项
S-C: Public: DESCRIBE,SETUP,TEARDOWN,PLAY,PAUSE //描述信息、建立连接、关闭、播放、暂停
C-S:DESCRIBE rtsp://192.168.1.60:8086/ RTSP/1.0
S-C:
v=0
o=- 1357627796453 1357627796453 IN IP4 192.168.1.60 //1357627796453是当前的timestamp信息,timestamp =System.currentTimeMillis();
s=Unnamed
i=N/A
c=IN IP4 192.168.1.26
t=0 0 //t=0 0意味着会话是永久的
a=recvonly
m=video 5006 RTP/AVP 96 //video指明是视频信息, 5006是指客户端VLC接收视频信息的udp端口号
b=RR:0
a=rtpmap:96 H264/90000 //这里的96的信息很关键,因为这个是rtp的负载类型,Payload type (PT): 7 bits。后面介绍rtp协议的时候会介绍,H264编码,90000是H264视频传输的默认视频采样频率,必须是这个值
a=fmtp:96 packetization-mode=1;profile-level-id=42c016;sprop-parameter-sets=ZOLAFukBQHsg,aM4G4g==;</ //packetization-mode=1指定rtp打包模式,有3种模式,数值只能为0,1,2,。0是单NAL单元模式 1是非交互模式 2是交互模式
profile,sps,pps这3个数值是从mp4中提取出来的base64编码,在net.majorkernelpanic.mp4这个包 有详细介绍。a=control:trackID=0 //trackID为0
Content-Base: 192.168.1.60:8086/
Content-Type: application/sdp //规定文件格式类型为sdp
C-S:SETUP 192.168.1.60:8086/trackID=0 RTSP/1.0
S-C:
Transport: RTP/AVP/UDP;unicast;destination=192.168.1.26;client_port=5006-5007;server_port=49749-49750;ssrc=431567f7;mode=play
Session: 1185d20035702ca //制定基于udp协议的rtp传输,目标地址,客户端端口、服务器端口,以及ssrc的数值,这里ssrc的数值很重要,它是同步源标识,synchronization source (SSRC) identifier,在rtp传输中,会包含这个内容
Cache-Control: no-cache
C-S: PLAY 192.168.1.60:8086/ RTSP/1.0
S-C:
以上是关于视频采集方案的主要内容,如果未能解决你的问题,请参考以下文章