您的位置:首页 > 移动开发 > Android开发

vlc-android 中调用用libvlcjni.so实现流媒体播放

2014-04-06 18:56 489 查看
最近公司搞的项目中涉及到流媒体播放,并且需要硬解码,所以想到了VLC这个开源项目。去官网下载了vlc-android源码进行编译,生成的apk安装在公司的设备上可以运行,不错不错,有现成的东西当然不会再去“造轮胎”,把编译后的android 工程导入eclipse 看了所有的代码,觉得对于我们只需要实现流媒体播放的来说显得有些累赘,这篇文章只需要实现流媒体播放的部分

关于源码下载和编译的部分可以查看:http://wiki.videolan.org/AndroidCompile

下面的代码有多部分是vlc-android工程源码,它们已经为我们封装好了要调用的jni函数和一些配置信息,这部分源码可以拿来就用。

1.创建一个android工程,界面很简单,就一个SurfaceView

MainActivity 的代码如下:

[java] view
plaincopy

public class MainActivity extends Activity implements SurfaceHolder.Callback{

private SurfaceView mSurface;

private SurfaceHolder mSurfaceHolder;

private LibVLC mLibVLC;

private EventManager mEventManger;

private boolean mIsPlaying;

private int mVideoHeight;

private int mVideoWidth;

private int mSarNum;

private int mSarDen;

private int mSurfaceAlign;

private static final int SURFACE_SIZE = 3;

private static final int SURFACE_BEST_FIT = 0;

private static final int SURFACE_FIT_HORIZONTAL = 1;

private static final int SURFACE_FIT_VERTICAL = 2;

private static final int SURFACE_FILL = 3;

private static final int SURFACE_16_9 = 4;

private static final int SURFACE_4_3 = 5;

private static final int SURFACE_ORIGINAL = 6;

private int mCurrentSize = SURFACE_BEST_FIT;

private static final String uri = "rtsp://217.146.95.166:554/live/ch6bqvga.3gp";

private static final String TAG = "DTV";

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mSurface = (SurfaceView) findViewById(R.id.surface);

mSurfaceHolder = mSurface.getHolder();

mSurfaceHolder.addCallback(this);

mSurface.setKeepScreenOn(true);

SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);

int pitch;

String chroma = pref.getString("chroma_format", "");

if(Util.isGingerbreadOrLater() && chroma.equals("YV12")) {

mSurfaceHolder.setFormat(ImageFormat.YV12);

pitch = ImageFormat.getBitsPerPixel(ImageFormat.YV12) / 8;

} else if (chroma.equals("RV16")) {

mSurfaceHolder.setFormat(PixelFormat.RGB_565);

PixelFormat info = new PixelFormat();

PixelFormat.getPixelFormatInfo(PixelFormat.RGB_565, info);

pitch = info.bytesPerPixel;

} else {

mSurfaceHolder.setFormat(PixelFormat.RGBX_8888);

PixelFormat info = new PixelFormat();

PixelFormat.getPixelFormatInfo(PixelFormat.RGBX_8888, info);

pitch = info.bytesPerPixel;

}

mSurfaceAlign = 16 / pitch - 1;

enableIOMX(true);

try {

mLibVLC = LibVLC.getInstance();

} catch (LibVlcException e) {

Log.i(TAG, "LibVLC.getInstance() error:"+e.toString());

e.printStackTrace();

return ;

}

mEventManger = EventManager.getInstance();

mEventManger.addHandler(mEventHandler);

}

private void enableIOMX(boolean enableIomx){

SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(VLCApplication.getAppContext());

Editor e = p.edit();

e.putBoolean("enable_iomx", enableIomx);

LibVLC.restart();

}

private DtvCallbackTask mDtvCallbackTask = new DtvCallbackTask(this) {

@Override

public void run() {

// TODO Auto-generated method stub

int n = 25;

while((n-- != 0)&& !mIsPlaying){

try {

Thread.sleep(200);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

if(!mIsPlaying){

Log.i(TAG, "could not open media or internet not access");

}

}

};

private final VideoEventHandler mEventHandler = new VideoEventHandler(this);

private class VideoEventHandler extends WeakHandler<MainActivity>{

public VideoEventHandler(MainActivity owner) {

super(owner);

}

@Override

public void handleMessage(Message msg) {

MainActivity activity = getOwner();

if(activity == null) return;

switch (msg.getData().getInt("event")) {

case EventManager.MediaPlayerPlaying:

Log.i(TAG, "MediaPlayerPlaying");

mIsPlaying = true;

break;

case EventManager.MediaPlayerPaused:

Log.i(TAG, "MediaPlayerPaused");

mIsPlaying = false;

break;

case EventManager.MediaPlayerStopped:

Log.i(TAG, "MediaPlayerStopped");

mIsPlaying = false;

break;

case EventManager.MediaPlayerEndReached:

Log.i(TAG, "MediaPlayerEndReached");

break;

case EventManager.MediaPlayerVout:

break;

case EventManager.MediaPlayerPositionChanged:

//don't spam the logs

break;

default:

Log.e(TAG, String.format("Event not handled (0x%x)", msg.getData().getInt("event")));

break;

}

super.handleMessage(msg);

}

}

@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;

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {

mLibVLC.attachSurface(mSurfaceHolder.getSurface(), MainActivity.this,width,height);

Log.i(TAG, " width="+ width+" height="+height);

}

@Override

public void surfaceCreated(SurfaceHolder holder) {

// TODO Auto-generated method stu

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

// TODO Auto-generated method stub

mLibVLC.detachSurface();

}

public void setSurfaceSize(int width, int height, int sar_num, int sar_den) {

if (width * height == 0)

return;

// store video size

mVideoHeight = height;

mVideoWidth = width;

mSarNum = sar_num;

mSarDen = sar_den;

Message msg = mHandler.obtainMessage(SURFACE_SIZE);

mHandler.sendMessage(msg);

}

private final Handler mHandler = new VideoPlayerHandler(this);

private static class VideoPlayerHandler extends WeakHandler<MainActivity> {

public VideoPlayerHandler(MainActivity owner) {

super(owner);

}

@Override

public void handleMessage(Message msg) {

MainActivity activity = getOwner();

if(activity == null) // WeakReference could be GC'ed early

return;

switch (msg.what) {

case SURFACE_SIZE:

activity.changeSurfaceSize();

break;

}

}

};

@Override

protected void onResume() {

super.onResume();

if(mLibVLC != null){

try{

mLibVLC.readMedia(uri, false);

}catch(Exception e){

Log.i(TAG,e.toString());

return;

}

mDtvCallbackTask.execute();

}else {

return;

}

}

private void changeSurfaceSize() {

// get screen size

int dw = getWindow().getDecorView().getWidth();

int dh = getWindow().getDecorView().getHeight();

// getWindow().getDecorView() doesn't always take orientation into account, we have to correct the values

boolean isPortrait = getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT;

if (dw > dh && isPortrait || dw < dh && !isPortrait) {

int d = dw;

dw = dh;

dh = d;

}

// sanity check

if (dw * dh == 0 || mVideoWidth * mVideoHeight == 0) {

Log.e(TAG, "Invalid surface size");

return;

}

// compute the aspect ratio

double ar, vw;

double density = (double)mSarNum / (double)mSarDen;

if (density == 1.0) {

/* No indication about the density, assuming 1:1 */

vw = mVideoWidth;

ar = (double)mVideoWidth / (double)mVideoHeight;

} else {

/* Use the specified aspect ratio */

vw = mVideoWidth * density;

ar = vw / mVideoHeight;

}

// compute the display aspect ratio

double dar = (double) dw / (double) dh;

switch (mCurrentSize) {

case SURFACE_BEST_FIT:

if (dar < ar)

dh = (int) (dw / ar);

else

dw = (int) (dh * ar);

break;

case SURFACE_FIT_HORIZONTAL:

dh = (int) (dw / ar);

break;

case SURFACE_FIT_VERTICAL:

dw = (int) (dh * ar);

break;

case SURFACE_FILL:

break;

case SURFACE_16_9:

ar = 16.0 / 9.0;

if (dar < ar)

dh = (int) (dw / ar);

else

dw = (int) (dh * ar);

break;

case SURFACE_4_3:

ar = 4.0 / 3.0;

if (dar < ar)

dh = (int) (dw / ar);

else

dw = (int) (dh * ar);

break;

case SURFACE_ORIGINAL:

dh = mVideoHeight;

dw = (int) vw;

break;

}

// align width on 16bytes

int alignedWidth = (mVideoWidth + mSurfaceAlign) & ~mSurfaceAlign;

// force surface buffer size

mSurfaceHolder.setFixedSize(alignedWidth, mVideoHeight);

// set display size

LayoutParams lp = mSurface.getLayoutParams();

lp.width = dw * alignedWidth / mVideoWidth;

lp.height = dh;

mSurface.setLayoutParams(lp);

mSurface.invalidate();

}

@Override

protected void onDestroy() {

if(mLibVLC.isPlaying()){

mLibVLC.stop();

}

mLibVLC = null;

super.onDestroy();

}

}

2.将vlc-android 中org.videolan.vlc包下面的这几个class 添加:

Aout.java

BitmapCache.java

EventManager.java

LibVLC.java

LibVlcException.java

TrackInfo.java

Util.java

VLCApplication.java

WeakHandler.java

3.将源码编译出的libs下的armeabi-v7a(如果设设备是arm6 或者以下,是armeabi)文件夹添加在android工程的libs下面

uri = "rtsp://217.146.95.166:554/live/ch6bqvga.3gp"是我在网上随便找的一个rtsp 流媒体地址

主要的部分是:

a. mLibVLC = LibVLC.getInstance(); 用来获取mLIbVLC的实例,其中会初始化LibVLC,在AndroidManifest.xml中要添加android:name="org.videolan.vlc.VLCApplication"这样程序启动时会调用VLCApplication使其生成实例,不会导致LibVLC.getInstance()出错。

b.mLibVLC.readMedia(uri, false);调用这一句后如果uri地址可用,流媒体就开始在载入,并且播放,并不需要mLibVLC.play()。

c.mLibVLC.attachSurface(mSurfaceHolder.getSurface(), MainActivity.this,width,height);调用这句的时候如果视频不显示,界面突然退出,是因为没有添加:public void setSurfaceSize(int width, int height, int sar_num, int sar_den)这个函数(我编译源码的时候ANDROID_ABI=armeabi-v7a,ANDROID_ABI设置不同这个函数的参数不同),它在libvlcjni.c
的jni_SetAndroidSurfaceSize函数中调用,用来设置surfaceview大小的。

如果需要硬件解码,就需要添加以下方法:

[java] view
plaincopy

private void enableIOMX(boolean enableIomx){

SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(VLCApplication.getAppContext());

Editor e = p.edit();

e.putBoolean("enable_iomx", enableIomx);

LibVLC.restart();

}

将sharedpreferences 的key "enable_iomx'设置为true,因为libvlcjni.c 中通过函数libvlc_media_t *new_media(jlong instance, JNIEnv *env, jobject thiz, jstring fileLocation, bool noOmx, bool noVideo)调用java 代码LibVLC.java
中的useIOMX()获取“enable_iomx”的值,然后判断是否用硬件解码。

在调试的过程中还会出现的错误是因为:Util.java 中String ANDROID_ABI = properties.getProperty("ANDROID_ABI");调用属性“ANDROID_ABI”的值时返回的是null导致,这主要发生在LibVLC.getInstance();时,自己判断一下,如果为ANDROID_ABI==null,就根据自己的设备选择赋值“armeabi-v7a”或者“armeabi”.

[html] view
plaincopy

mEventManger = EventManager.getInstance();

mEventManger.addHandler(mEventHandler);

是用来添加播放事件的,当播放视频出现play,stop,pause等状态时,会接收到。

项目中碰到的问题就这些让我困惑了一阵,其余的可以通过谷歌或着度娘找到相应的方法。
http://blog.csdn.net/zhongyili_sohu/article/details/9225541
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: