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);
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));
完整的示例:
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>