Android原生的MediaPlayer虽然也可以播放rtsp流媒体,可是却有几秒的延时,不符合实时的要求。于是需要使用第三方库live555来解决这个问题。
1、搭建live555框架
我们先下载源码,然后在eclipse里创建一个新的library工程,在工程jni下创建一个live目录,然后将源码拷贝过来。之后在jni下新建一个Android.mk和Application.mk。
图Android.mk
图Application.mk
2、跑通rtsp client流程
分析源码,我们先将testRTSPClient.cpp从jni/live/testProgs里拷贝出来,放到jni下, 然后新建一个jni_interface.h。
图jni_interface.h
Java_com_live555_ctl_liveRtsp_start、Java_com_live555_ctl_liveRtsp_dosometing这两个接口我们将在testRTSPClient.cpp里实现。
在Java_com_live555_ctl_liveRtsp_start里我们根据传过来的url和progName调用openURL,然后调起主循环doEventLoop。 这个时候,我们可以跑通rtsp client的流程,可以连接server了,可是,接收到的视频数据还没有做处理。
3、接收处理视频数据
在live555的架构里,要说明一下Sink source,这两者的概念及关系:
liveMedia库中Sink就是消费数据的对象,比如把接收到的数据存储到文件,这个文件就是一个Sink。 Source就是生产数据的对象,比如通过 RTP读取数据。数据流经过多个source和sinks。
图MediaSink
图MediaSource
根据rtsp流程,我们在continueAfterSETUP里找到了
{
scs.subsession->sink =DummySink::createNew(env, *scs.subsession, rtspClient->url());
}
将其替换为
{
bool oneFilePerFrame = false;
char const* fmtp_spropparametersets =(*scs.subsession).fmtp_spropparametersets();
scs.subsession->sink =H264VideoFileSink::createNew(env, (charconst*)"/storage/sdcard0/testRtsp.mp4",fmtp_spropparametersets,DUMMY_SINK_RECEIVE_BUFFER_SIZE,oneFilePerFrame);
}
替换DummySink为H264VideoFileSink后,我们就可以发现,rtsp传过来的视频数据被保存在了/storage/sdcard0/testRtsp.mp4。
我们将H264VideoStreamSink.cpp、H264VideoStreamSink.hh、 H264or5VideoStreamSink.cpp、H264or5VideoStreamSink.hh、 StreamSink.cpp、StreamSink.hh拷贝到jni下,改名为WrH264VideoStreamSink.cpp、WrH264VideoStreamSink.hh、WrH264or5VideoStreamSink.cpp、WrH264or5VideoStreamSink.hh、 WrStreamSink.cpp、 WrStreamSink.hh。
{
scs.subsession->sink =H264VideoFileSink::createNew(env,(charconst*)"/storage/sdcard0/testRtsp.mp4",fmtp_spropparametersets,DUMMY_SINK_RECEIVE_BUFFER_SIZE,oneFilePerFrame);
}
改为
{
scs.subsession->sink =WrH264VideoStreamSink::createNew(env,
&function_callback,fmtp_spropparametersets,
DUMMY_SINK_RECEIVE_BUFFER_SIZE);
}
传递一个回调进去typedefvoidfunctioncallback(unsignedchar* buffer,int datasize,int videowidth,int videoheight, int pts),然后修改相应的代码,将保存为mp4的功能去掉,改为将h264的视频frame通过回调传出来。
{
void function_callback(unsigned char* buffer,int datasize, int videowidth, int videoheight, int pts)
{
if(0!= liveRtsp_env && 0 != liveRtsp_obj && 0 != liveRtsp_array) {
liveRtsp_env->SetByteArrayRegion(liveRtsp_array,0, datasize, (const jbyte*)buffer);
int iframeSize = datasize;
liveRtsp_env->CallVoidMethod(*liveRtsp_obj,mid_onNativeCallback, liveRtsp_array, videowidth, videoheight, pts, datasize);
}
}
}
然后通过mid_onNativeCallback =env->GetMethodID(clsstring, "onNativeCallback","([BIIII)V")将h264的frame传到Java层。
4、Java层接口
图 live555接口
至此,live555 rtsp client的library就搭建好了。
5、应用live555rtsp client的library
{
String prog = "dlna_connect";
byte[] progName =new byte[prog.length() +1];
System.arraycopy(prog.getBytes(), 0, progName, 0, prog.length());
progName[prog.length()] = 0;
Stringuri = playURI;//"rtsp://192.168.43.1:8086";
byte[] nameUri =new byte[uri.length() +1];
System.arraycopy(uri.getBytes(), 0, nameUri, 0, uri.length());
nameUri[uri.length()] = 0;
if(null == mliveRtsp)
mliveRtsp =new liveRtsp();
mliveRtsp.mliveRtspInterface = liveRtspIf;
mliveRtsp.start(progName, nameUri);
}
这里开始启动liveRtsp
{
public liveRtsp.liveRtspInterface liveRtspIf = new liveRtsp.liveRtspInterface() {
@Override
public voidliveRtspCallback(byte[] data, int width, int height,
intpts, int len) {
...在这里可以收到rtsp的视频帧
}
}
}
接收到视频帧后,需要解码显示到Surface上,需要注意的是,live555里只有一个主线程,所以回调也是在liveRtsp的主线程里,即创建liveRtsp的线程。