Vitamio的踩坑+填坑

博客介绍了在使用Vitamio视频框架时遇到的兼容性问题,特别是在Android 6.0设备上,当targetSdkVersion设置为23时应用崩溃。作者通过降低targetSdkVersion到22暂时解决问题,然后发现缺少libffmpeg.so库,通过添加res/raw文件夹和libarm.so文件解决黑屏问题。最后,作者更新到Vitamio 5.2.3版本,成功支持Android 6.0且优化了初始化流程。

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()

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值