vitamio这个视频框架已经有快两年没有没有使用过了,今天想写个demo再复习下。
- 首先肯定是下载官方的demo跑一下了,他们的demo放在github上,所以我就直接上github搜索并下载VitamioBundle,我个人比较喜欢用新的api,所以手动将targetSdkVersion改为了27,重新编译运行,app打开了,没问题,点击VideoView条目跳转播放页面播放,居然崩溃了。
07-19 07:44:09.764 5133-5133/io.vov.vitamio.demo E/linker: "/data/data/io.vov.vitamio.demo/libs/libffmpeg.so" has text relocations (https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#Text-Relocations-Enforced-for-API-level-23)
07-19 07:44:09.765 5133-5133/io.vov.vitamio.demo E/Vitamio[4.2.1][Player]: LOAD FFMPEG ERROR: dlopen failed: "/data/data/io.vov.vitamio.demo/libs/libffmpeg.so" has text relocations (https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#Text-Relocations-Enforced-for-API-level-23)
07-19 07:44:09.766 5133-5133/io.vov.vitamio.demo E/Vitamio[4.2.1][Player]: FIND_NAME_SYM vvo, render_yuv
07-19 07:44:09.768 5133-5133/io.vov.vitamio.demo A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x0 in tid 5133 (ov.vitamio.demo), pid 5133 (ov.vitamio.demo)
07-19 07:44:09.792 5170-5170/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/sdk_gphone_x86/generic_x86:8.1.0/OSM1.180201.021/4741582:userdebug/dev-keys'
Revision: '0'
ABI: 'x86'
pid: 5133, tid: 5133, name: ov.vitamio.demo >>> io.vov.vitamio.demo <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
eax ca5b5ac0 ebx ca5aded8 ecx e49a91e4 edx e3b10860
07-19 07:44:09.792 5170-5170/? A/DEBUG: esi e3b10860 edi 00000075
xcs 00000023 xds 0000002b xes 0000002b xfs 0000006b xss 0000002b
eip 00000000 ebp ca598f98 esp ff832c2c flags 00010246
backtrace:
#00 pc 00000000 <unknown>
#01 pc 5b6f696c <unknown>
说实话这日志看的我一脸懵逼,但还是得找解决方案google一下。
Vitamio Crash In Android 6.0(Marshmallow) Devices when android targetSdkVersion set to 23
在这里我找到了暂时解决得方案,把targetSdkVersion改到22,再次运行,视频正常播放。
- demo跑起来了,接下来当然是自己写个demo玩玩了。(具体的导入过程我就不多说了,百度一下有很多)编译运行,what?黑屏~。查看日志:
07-19 08:03:08.076 5822-5822/io.vov.vitamio.demo E/Vitamio[Player]: Native libs libffmpeg.so not exists!
打开Vitamio.java,找到输出错误日志的地方:
/**
* Check if Vitamio is initialized at this device
*
* @param ctx Android Context
* @return true if the Vitamio has been initialized.
*/
public static boolean isInitialized(Context ctx) {
vitamioPackage = ctx.getPackageName();
vitamioLibraryPath = ContextUtils.getDataDir(ctx) + "libs/";
File dir = new File(getLibraryPath());
if (dir.exists() && dir.isDirectory()) {
String[] libs = dir.list();
if (libs != null) {
Arrays.sort(libs);
for (String L : getRequiredLibs()) {
if (Arrays.binarySearch(libs, L) < 0) {
Log.e("Native libs %s not exists!", L);
return false;
}
}
File lock = new File(getLibraryPath() + LIBS_LOCK);
BufferedReader buffer = null;
try {
buffer = new BufferedReader(new FileReader(lock));
int appVersion = ContextUtils.getVersionCode(ctx);
int libVersion = Integer.valueOf(buffer.readLine());
Log.i("isNativeLibsInited, APP VERSION: %d, Vitamio Library version: %d", appVersion, libVersion);
if (libVersion == appVersion)
return true;
} catch (IOException e) {
Log.e("isNativeLibsInited", e);
} catch (NumberFormatException e) {
Log.e("isNativeLibsInited", e);
} finally {
IOUtils.closeSilently(buffer);
}
}
}
return false;
} 原来是没有初始化成功,先看看我们加载布局之前做了什么。
//检查初始化
if (!LibsChecker.checkVitamioLibs(this))
return; 那我们再看一下LibsChecker这个类做了什么
public final class LibsChecker {
public static final String FROM_ME = "fromVitamioInitActivity";
public static final boolean checkVitamioLibs(Activity ctx) {
if (!Vitamio.isInitialized(ctx) && !ctx.getIntent().getBooleanExtra(FROM_ME, false)) {
Intent i = new Intent();
i.setClassName(Vitamio.getVitamioPackage(), "io.vov.vitamio.activity.InitActivity");
i.putExtras(ctx.getIntent());
i.setData(ctx.getIntent().getData());
i.putExtra("package", ctx.getPackageName());
i.putExtra("className", ctx.getClass().getName());
ctx.startActivity(i);
ctx.finish();
return false;
}
return true;
}
} 由此我们发现,它的作用主要是将当前Activity的信息传递给InitActivity,并把当前页面finish掉。再看一下InitActivity
public class InitActivity extends Activity {
public static final String FROM_ME = "fromVitamioInitActivity";
private ProgressDialog mPD;
private UIHandler uiHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
uiHandler = new UIHandler(this);
new AsyncTask<Object, Object, Boolean>() {
@Override
protected void onPreExecute() {
mPD = new ProgressDialog(InitActivity.this);
mPD.setCancelable(false);
mPD.setMessage(InitActivity.this.getString(getResources().getIdentifier("vitamio_init_decoders", "string", getPackageName())));
mPD.show();
}
@Override
protected Boolean doInBackground(Object... params) {
//进行初始化操作
return Vitamio.initialize(InitActivity.this, getResources().getIdentifier("libarm", "raw", getPackageName()));
}
@Override
protected void onPostExecute(Boolean inited) {
if (inited) {
uiHandler.sendEmptyMessage(0);
}
}
}.execute();
}
private static class UIHandler extends Handler {
private WeakReference<Context> mContext;
public UIHandler(Context c) {
mContext = new WeakReference<Context>(c);
}
public void handleMessage(Message msg) {
InitActivity ctx = (InitActivity) mContext.get();
switch (msg.what) {
case 0:
ctx.mPD.dismiss();
Intent src = ctx.getIntent();
Intent i = new Intent();
i.setClassName(src.getStringExtra("package"), src.getStringExtra("className"));
i.setData(src.getData());
i.putExtras(src);
i.putExtra(FROM_ME, true);
ctx.startActivity(i);
ctx.finish();
break;
}
}
}
}
原来初始化的操作放在了这里,开启了一个异步任务进行初始化操作,初始化成功后通过handler发送一个空消息,然后执行操作,跳转到之前的那个页面(信息都传过来了),并关闭InitActivity。这里有两个地方比较疑惑
- 为什么要加一个handler呢,onPostExecute里面的就是执行在UI线程的吧
- Vitamio.initialize(Context ctx, int rawId)方法里面,两个条件问什么是或的关系,明明其中一个失败就能导致初始化失败
/**
* Same as {@link #initialize(Context)}
*
* @param ctx Android Context
* @param rawId R.raw.libarm
* @return true if the Vitamio initialized successfully.
*/
public static boolean initialize(Context ctx, int rawId) {
return isInitialized(ctx) || extractLibs(ctx, rawId);
}
补充:如果不是通过定义静态内部类的方法使用handler,是有可能造成内存泄漏的。
回归正题,通过以上我们发现了一个参数rawId,默认值是R.raw.libarm。但是我创建项目的时候res下并没有创建raw文件夹。对比了一下demo,发现别人的确实有这么个文件夹,里面放了libarm.so文件。(vitamio库里面也有这个,不太清楚为什么自己module里面也要加上) 重新编译运行,可以正常播放视频。
重新回到最初的问题,我总不能为了使用vitamio而放弃使用高版本的api吧。搜寻一番无果后,我打开了vitamio的官网,看到了新版本5.2.3,并且已经说明支持Android6.0。(这时我想拍死自己),既然有新的了,那么重新集成,这次我把targetSdkVersion设为了27,编译运行,成功。
补充:我将两个版本的demo做了下对比。新的demo中不在需要res/raw文件夹了,并且删除了LibsChecker,同时InitActivity里面不再执行初始化操作了。(感觉这个类也失去了存在的意义,不过没删除而已)
我们做初始化操作,由
if (!LibsChecker.checkVitamioLibs(this))
return;
变成了
Vitamio.isInitialized(this);
activity由以前的启动2次变成了1次。
4.2.1版本:VideoViewActivity(start)->LibsChecker.checkVitamioLibs(this)->InitActivity(start),VideoViewActivity(finish)->Vitamio.initialize(this)->VideoViewActivity(start),InitActivity(finish)->setContentView()
5.2.3版本:VideoViewActivity(start)->Vitamio.isInitialized(this)->setContentView()
博客介绍了在使用Vitamio视频框架时遇到的兼容性问题,特别是在Android 6.0设备上,当targetSdkVersion设置为23时应用崩溃。作者通过降低targetSdkVersion到22暂时解决问题,然后发现缺少libffmpeg.so库,通过添加res/raw文件夹和libarm.so文件解决黑屏问题。最后,作者更新到Vitamio 5.2.3版本,成功支持Android 6.0且优化了初始化流程。

664

被折叠的 条评论
为什么被折叠?



