关于如何编译在官方有介绍,网上也有很多教程,不做赘述。
目前博主水平有限,目前主要是分析java部分代码和如何使用解码库。
先看下目录结构:
LibVLC是load解码库和so库的,提供调用的方法。
/* Load library before object instantiation */
static {
try {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
System.loadLibrary("iomx-gingerbread");
else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2)
System.loadLibrary("iomx-hc");
else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2)
System.loadLibrary("iomx-ics");
} catch (Throwable t) {
Log.w(TAG, "Unable to load the iomx library: " + t);
}
try {
System.loadLibrary("vlcjni");
} catch (UnsatisfiedLinkError ule) {
Log.e(TAG, "Can't load vlcjni library: " + ule);
/// FIXME Alert user
System.exit(1);
} catch (SecurityException se) {
Log.e(TAG, "Encountered a security issue when loading vlcjni library: " + se);
/// FIXME Alert user
System.exit(1);
}
}
//调用所有libvlc的方法之前必须进行init操作
/**
* Initialize the libVLC class.
*
* This function must be called before using any libVLC functions.
*
* @throws LibVlcException
*/
public void init(Context context) throws LibVlcException {
Log.v(TAG, "Initializing LibVLC");
mDebugLogBuffer = new StringBuffer();
if (!mIsInitialized) {
if(!LibVlcUtil.hasCompatibleCPU(context)) {
Log.e(TAG, LibVlcUtil.getErrorMsg());
throw new LibVlcException();
}
nativeInit();
mMediaList = mPrimaryList = new MediaList(this);
setEventHandler(EventHandler.getInstance());
mIsInitialized = true;
}
}
播放实时视频,只需调用如下方法,传入一个rtps的url
/**
* Play an MRL directly.
*
* @param mrl MRL of the media to play.
*/
public void playMyMRL(String mrl) {
// index=-1 will return options from libvlc instance without relying on MediaList
String[] options = new String[0];
mInternalMediaPlayerIndex = 0;
playMRL(mLibVlcInstance, mrl, options);
}
LibVlcUtil 主要是对设备CPU的检查。ABI,CPU_ABI是我们经常都会碰见的,例如我们在Android工程里面的libs库下面就有armeabi-v7a类型的库。这里的ABI是指Application Binary Interface。
ABI描述了应用程序和操作系统之间,一个应用和它的库之间,或者应用的组成部分之间的低层接口。ABI不同于应用程序接口(API),API定义了源代码和库之间的接口,因此同样的代码可以在支持这个API的任何系统中编译,然而ABI允许编译好的目标代码在使用兼容ABI的系统中无需改动就能运行。
ABI掩盖了各种细节,例如:调用约定(控制着函数的参数如何传送以及如何接受返回值);系统调用的编码和一个应用如何向操作系统进行系统调用;以及在一个完整的操作系统ABI中,对象文件的二进制格式、程序库等等。一个完整的ABI,像Intel二进制兼容标准 (iBCS) ,允许支持它的操作系统上的程序不经修改在其他支持此ABI的操作体统上运行。其他的 ABI 标准化细节包括 C++ name decoration 和同一个平台上的编译器之间的调用约定,但是不包括跨平台的兼容性。
简单的说,ABI规范了应用程序对寄存器的使用方法,Call procedure,以及如何进入trap。符合某一平台ABI规范的应用程序就可以在这一平台上运行。这一规范是针对binary,而不是source的。所以同样的高级语言代码,使用不同的toolchain,可以得到符合不同ABI规范的binary。关于这部分,还不明白的,自行Google(我也不是很清楚,免得误人子弟)。
VLCApplication做的事情不多,简单来说,做了两件事。第一个是一个本地配置的初始化,主要是Locale的配置,而且都是针对中文做特别处理,另外就是当系统内存不多的时候,回收一些bitmap的内存。剩下的就是保存一个全局的静态对象供其他类使用。下面给出系统内存低的时候,回收bitmap缓存。代码如下:
// Are we using advanced debugging - locale?
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
String p = pref.getString("set_locale", "");
if (p != null && !p.equals("")) {
Locale locale;
// workaround due to region code
if(p.equals("zh-TW")) {
locale = Locale.TRADITIONAL_CHINESE;
} else if(p.startsWith("zh")) {
locale = Locale.CHINA;
} else if(p.equals("pt-BR")) {
locale = new Locale("pt", "BR");
} else if(p.equals("bn-IN") || p.startsWith("bn")) {
locale = new Locale("bn", "IN");
} else {
/**
* Avoid a crash of
* java.lang.AssertionError: couldn't initialize LocaleData for locale
* if the user enters nonsensical region codes.
*/
if(p.contains("-"))
p = p.substring(0, p.indexOf('-'));
locale = new Locale(p);
}
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
}
低内存的时候
/**
* Called when the overall system is running low on memory
*/
@Override
public void onLowMemory() {
super.onLowMemory();
Log.w(TAG, "System is running low on memory");
BitmapCache.getInstance().clear();
}
具体的调用:
try {
EventHandler em = EventHandler.getInstance();
em.addHandler(handler);
mLoadingDialog = DialogUtil.createLoadingDialog(MainActivity.this,"正在获取信息,请稍后...");
mLoadingDialog.show();
mLibVLC = Util.getLibVlcInstance();
mLibVLC.init(getApplicationContext());
if (mLibVLC != null) {
String pathUri = "rtsp://192.168.103.39:10554/654321.sdp";
// String pathUri = "http://vod.cntv.lxdns.com/flash/mp4video50/TMS/2016/04/19/f1db9354900946a19a7fefc3bf040593_h264200000nero_aac16.mp4";
// String pathUri = "/storage/emulated/0/DCIM/Camera/VID_20170511_103045.mp4";
mLibVLC.playMyMRL(pathUri);
}
} catch (LibVlcException e) {
e.printStackTrace();
}
初始化handler,libvlc初始化的时候,往下一层层调用,最后handler将信息返回,调用play播放方法的时候,我也没有搞清楚为什么那么慢,可能和网络有关,还是和服务器那边推流的速度,关于这一点仍然在研究,目前给的措施就是loading加载。
当接收到handler传来的信息,对此进行相应的操作:
Handler handler = new Handler() {
public void handleMessage(Message msg) {
Log.d(TAG, "Event = " + msg.getData().getInt("event"));
switch (msg.getData().getInt("event")) {
case EventHandler.MediaPlayerPlaying:
break;
case EventHandler.MediaPlayerPaused:
break;
case EventHandler.MediaPlayerStopped:
break;
case EventHandler.MediaPlayerEndReached:
break;
case EventHandler.MediaPlayerVout:
if (mLoadingDialog != null) {
mLoadingDialog.dismiss();
mLoadingDialog = null;
}
if (msg.getData().getInt("data") > 0) {
Intent intent = new Intent();
intent.setClass(getApplicationContext(),VideoPlayerActivity.class);
startActivity(intent);
MainActivity.this.finish();
}
break;
case EventHandler.MediaPlayerPositionChanged:
break;
case EventHandler.MediaPlayerEncounteredError:
AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
.setTitle("提示信息")
.setMessage("无法连接到摄像头,请检查设备是否已经连接到摄像头所在的wifi热点")
.setNegativeButton("知道了",
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
MainActivity.this.finish();
}
}).create();
dialog.setCanceledOnTouchOutside(false);
dialog.show();
break;
default:
Log.d(TAG, "Event not handled ");
break;
}
}
};
EventHandler类里面定义很多常量,根据项目的需求选择:
/*
* Be sure to subscribe to events you need in the JNI too.
*/
//public static final int MediaMetaChanged = 0;
//public static final int MediaSubItemAdded = 1;
//public static final int MediaDurationChanged = 2;
//public static final int MediaParsedChanged = 3;
//public static final int MediaFreed = 4;
//public static final int MediaStateChanged = 5;
//public static final int MediaPlayerMediaChanged = 0x100;
//public static final int MediaPlayerNothingSpecial = 0x101;
//public static final int MediaPlayerOpening = 0x102;
//public static final int MediaPlayerBuffering = 0x103;
public static final int MediaPlayerPlaying = 0x104;
public static final int MediaPlayerPaused = 0x105;
public static final int MediaPlayerStopped = 0x106;
//public static final int MediaPlayerForward = 0x107;
//public static final int MediaPlayerBackward = 0x108;
public static final int MediaPlayerEndReached = 0x109;
public static final int MediaPlayerEncounteredError = 0x10a;
//public static final int MediaPlayerTimeChanged = 0x10b;
public static final int MediaPlayerPositionChanged = 0x10c;
//public static final int MediaPlayerSeekableChanged = 0x10d;
//public static final int MediaPlayerPausableChanged = 0x10e;
//public static final int MediaPlayerTitleChanged = 0x10f;
//public static final int MediaPlayerSnapshotTaken = 0x110;
//public static final int MediaPlayerLengthChanged = 0x111;
public static final int MediaPlayerVout = 0x112;
//public static final int MediaListItemAdded = 0x200;
//public static final int MediaListWillAddItem = 0x201;
//public static final int MediaListItemDeleted = 0x202;
//public static final int MediaListWillDeleteItem = 0x203;
//public static final int MediaListViewItemAdded = 0x300;
//public static final int MediaListViewWillAddItem = 0x301;
//public static final int MediaListViewItemDeleted = 0x302;
//public static final int MediaListViewWillDeleteItem = 0x303;
//public static final int MediaListPlayerPlayed = 0x400;
//public static final int MediaListPlayerNextItemSet = 0x401;
//public static final int MediaListPlayerStopped = 0x402;
//public static final int MediaDiscovererStarted = 0x500;
//public static final int MediaDiscovererEnded = 0x501;
//public static final int VlmMediaAdded = 0x600;
//public static final int VlmMediaRemoved = 0x601;
//public static final int VlmMediaChanged = 0x602;
//public static final int VlmMediaInstanceStarted = 0x603;
//public static final int VlmMediaInstanceStopped = 0x604;
//public static final int VlmMediaInstanceStatusInit = 0x605;
//public static final int VlmMediaInstanceStatusOpening = 0x606;
//public static final int VlmMediaInstanceStatusPlaying = 0x607;
//public static final int VlmMediaInstanceStatusPause = 0x608;
//public static final int VlmMediaInstanceStatusEnd = 0x609;
//public static final int VlmMediaInstanceStatusError = 0x60a;
public static final int CustomMediaListExpanding = 0x2000;
public static final int CustomMediaListExpandingEnd = 0x2001;
public static final int CustomMediaListItemAdded = 0x2002;
public static final int CustomMediaListItemDeleted = 0x2003;
但是必须确保jni中存在。其余就是添加移除handler的方法:
public void addHandler(Handler handler) {
if (!mEventHandler.contains(handler))
mEventHandler.add(handler);
}
public void removeHandler(Handler handler) {
mEventHandler.remove(handler);
}
/** This method is called by a native thread **/
public void callback(int event, Bundle b) {
b.putInt("event", event);
for (int i = 0; i < mEventHandler.size(); i++) {
Message msg = Message.obtain();
msg.setData(b);
mEventHandler.get(i).sendMessage(msg);
}
}
最后就是VideoPlayerActivity,视频播放的页面。其实我也不太会写博客,总想记录一些,学习一些,也知道自己写的不好,基本都是复制粘贴,只是遇到的一些坑能够写出来,让大家少走弯路,还是希望大神可以指点指点。不喜勿喷。