# JsView媒体播放的解决方法

因为Android盒子的碎片化,导致很多播放器异常发生。

为了让用户能够根据自己的场景自定义播放器,JsView有三个播放器集成的建议:

# 方案1. apk直接打包播放器

# 1. apk直接集成播放器,

通过MiniApp或者MainProxy的AddJavaScriptInterface接口将play/pause等方法传给js调用。

通过MiniApp获得的JsView句柄,通过其emitEvent接口,将timeupdate的信息发给js。

# 2. js通过组建做一个透过的区域

透过区域的制作方法有两种: 1. JsvHole组件, 2. JsvNativeSharedView组件(会讲其top,left,width,height信息同步给Java)

# 方案2. 撰写播放器插件

相对于将播放器集成到apk,插件的方式方便后续对播放器接口进行更新。但缺点也是,插件机制不支持resource导入,只能纯java逻辑。

# 1. 撰写插件处理

点击进入 插件的制作方法

# 2. js加载插件,并做一个透过的区域

透过区域的制作方法有两种: 1. JsvHole组件, 2. JsvNativeSharedView组件(会讲其top,left,width,height信息同步给Java)

# 方案3. 直接使用JsView关联的商用版的播放器(需要授权)

此为vue的组件,组件名为 JsvPlayer,使用方法和 video 标签一致,这是一个平台适配好的播放器插件,适配android 4.4以上的设备,提供包括rtsp等IPTV直播播放协议,并支持播放变速,PIP等播放行为。此为增值服务,需要时请直接联系我们。

# 附录1. JsvNativeSharedView组件, js java对接方法

# 1. js端

范例代码如下,流程为,创建JsvNativeSharedView,并赋予宽高和visibility,并注册一个回调函数接受view的id值。 当收到view的id后,将此id同步通知给java端,java需要此id来进行对view的位置变化事件进行监听。

<script setup>

const getId = (viewId)=>{
  // TODO: 将viewId通过java穿透的接口通知给java端, 例如: 当java穿透对象为 jNativeMedia 接口为 syncViewId 时
  window.jNativeMedia?.syncViewId(viewId);
}

</script>

<template>
    <jsv-native-shared-div :getId="getId" :style="{
        left: 0,
        top: 0,
        width: 500,
        height: 500,
        visibility: 'visible'
      }"/>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 2. java端

范例代码如下,流程为,收到从js收到的view id开始,通过jsview的句柄(注1)的ListenerToAckEvent接口(注2),注册监听回调(注意若有上一次注册的话,应该此时进行回收,规避内存泄漏),回调函数要处理的event(注3)的处理上需要有以下几个注意点:

A. stashedEvent: 酌情处理此标识为true的内容,stash内容是发生在 listenerToAckEvent 调用之前的已经发生过的Event内容。对于简单的跟进播放窗口位置的话,可以直接使用,没有顾虑。

B. 拿到的 x, y, width, height 以js的坐标系为标准的数值,需要进行转换,才能成java端屏幕坐标,以x举例,转化公式为: x / dw * JavaFrameLayout宽度,其中x和dw的值来源于Event结构,另外,此JavaFrameLayout指的是传给 MainPageProxy.onCreate 函数中的rootView。PS: 计算结果精度的取舍可根据应用现场表现进行调整。

import com.qcode.jsview.AckEventListener;
import com.qcode.jsview.JsView;

import com.qcode.jsview.shared_defined.CATEGORY_VIEW;
import com.qcode.jsview.shared_defined.TYPE_SHARED_VIEW_LAYOUT;


...
class NativeMediaInterface {
  AckEventListener mEventListener;
  JsView mJsView;

  ...

  // 声明供js设置view id的接口
  @JavascriptInterface
  void syncViewId(String viewId) {
    // 清理原引用
    if (mEventListener != null) {
      mEventListener.recycle();
      mEventListener = null;
    }

    // 注册新监听
    mEventListener = mJsView.listenerToAckEvent(CATEGORY_VIEW, TYPE_SHARED_VIEW_LAYOUT, viewId, (Bundle eventData)->{
      // 这是来自绘制线程的直接调用,需要发送到主线程才能对UI进行调整
      Looper.getMainLooper().post(()->{
        if (eventData.getBoolean("stashedEvent")) {
          // TODO: 此为已经发生过的事件
        }

        // 获取坐标和调整坐标
        // 以 mMediaView 为目标调整的视频窗口举例
        int containerWidth = constFrameLayoutWidth; // constFrameLayoutWidth 为外部设置
        int designedMapWidth = eventData.getInt("dw");
        int viewLeft = Math.floor((double)(eventData.getInt("x")) * containerWidth / designedMapWidth );
        int viewTop = Math.floor((double)(eventData.getInt("y")) * containerWidth / designedMapWidth );
        int viewWidth = Math.floor((double)(eventData.getInt("width")) * containerWidth / designedMapWidth );
        int viewHeight = Math.floor((double)(eventData.getInt("height")) * containerWidth / designedMapWidth);
        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)mMediaView.getLayoutParams(); 
        params.setMargins(viewLeft, viewTop, 0, 0);
        params.width = viewWidth;
        params.height = viewHeight;
        mMediaView.setLayoutParams(params);

        // 获取可视变化
        if (eventData.getInt("visible") == 0) {
          // hidden, 不可见
          mMediaView.setVisibility(View.GONE);
        } else {
          mMediaView.setVisibility(View.VISIBLE);
        }

        // TODO: order调整,若有多个view有层级关系
        int order = eventData.getInt("order");
      });
    });
  }

  ...
}

{
  ...
  // 以 mMiniApp 为 MiniApp 对象的句柄举例
  // 向js注入此播放控制接口, js端可见名为 window.jNativeMedia
  mMiniApp.addJavascriptInterface("jNativeMedia", new NativeMediaInterface());
  ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

其中注解:

注1: JsView句柄获取方法: [点击进入],

注2: JsView.listenerToAckEvent接口说明: [点击进入]

注3: Event格式说明: [点击进入]

Last Updated: 1/24/2024, 2:04:19 AM