@shijiu/jsview-vue 2.0.1002 → 2.0.1045-next-vue.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/utils/JsViewEngineWidget/MetroWidget/MetroWidget.vue +11 -1
- package/utils/JsViewPlugin/JsvAudio/AudioProxy.js +35 -18
- package/utils/JsViewPlugin/JsvAudio/JsvAudio.vue +120 -133
- package/utils/JsViewPlugin/JsvAudio/JsvAudioBridgeProxy.js +6 -3
- package/utils/JsViewPlugin/JsvAudio/version.js +3 -3
- package/utils/JsViewPlugin/JsvAudio/version.mjs +3 -3
- package/utils/JsViewPlugin/JsvPlayer/GetVersion.js +1 -1
- package/utils/JsViewPlugin/JsvPlayer/JsvPlayerBrowser.vue +370 -41
- package/utils/JsViewPlugin/JsvPlayer/version.mjs +6 -6
- package/utils/JsViewVueTools/JsvDemoTester.js +80 -0
- package/utils/JsViewVueTools/JsvHashHistory.js +2 -1
- package/utils/JsViewVueTools/index.js +1 -0
- package/utils/JsViewVueWidget/JsvApic/index.js +1 -1
- package/utils/JsViewVueWidget/JsvNinePatch.vue +3 -0
- package/utils/JsViewVueWidget/JsvSystemAudio.vue +219 -0
- package/utils/JsViewVueWidget/index.js +1 -0
- package/utils/JsViewPlugin/JsvPlayer/version.js +0 -19
- package/utils/JsViewVueWidget/JsvApic/BrowserApic/index.js +0 -1
package/package.json
CHANGED
|
@@ -135,6 +135,10 @@
|
|
|
135
135
|
unlock
|
|
136
136
|
@description 解锁
|
|
137
137
|
@params {int} type 参考lock的type参数
|
|
138
|
+
slots:
|
|
139
|
+
renderItem: 该slot用于描画每个单元格
|
|
140
|
+
background: 该slot描画在item后, 一般用于描画需要跟随MetroWidget滚动的内容
|
|
141
|
+
foreground: 该slot描画在item前, 一般用于描画需要跟随MetroWidget滚动的内容
|
|
138
142
|
* 【技巧说明】
|
|
139
143
|
* Q: 插槽props如何使用?
|
|
140
144
|
* A: data: 当前item的数据
|
|
@@ -1490,7 +1494,7 @@ const _calculateVisibleStart = (target_item, direction) => {
|
|
|
1490
1494
|
new_visible_start =
|
|
1491
1495
|
new_visible_start > boundary ? last_visible_start : new_visible_start;
|
|
1492
1496
|
}
|
|
1493
|
-
return new_visible_start;
|
|
1497
|
+
return Math.round(new_visible_start);
|
|
1494
1498
|
};
|
|
1495
1499
|
|
|
1496
1500
|
const _onFocusableItemEdge = (edge_info) => {
|
|
@@ -1956,6 +1960,9 @@ defineExpose(exportObject);
|
|
|
1956
1960
|
height: touchContainerH,
|
|
1957
1961
|
}"
|
|
1958
1962
|
>
|
|
1963
|
+
<div>
|
|
1964
|
+
<slot name="background"></slot>
|
|
1965
|
+
</div>
|
|
1959
1966
|
<div ref="locateDiv">
|
|
1960
1967
|
<jsv-focus-block
|
|
1961
1968
|
ref="focusNode"
|
|
@@ -2016,6 +2023,9 @@ defineExpose(exportObject);
|
|
|
2016
2023
|
</div>
|
|
2017
2024
|
</jsv-focus-block>
|
|
2018
2025
|
</div>
|
|
2026
|
+
<div>
|
|
2027
|
+
<slot name="foreground"></slot>
|
|
2028
|
+
</div>
|
|
2019
2029
|
</div>
|
|
2020
2030
|
</div>
|
|
2021
2031
|
</div>
|
|
@@ -10,6 +10,11 @@ import Events from "./Events.js"
|
|
|
10
10
|
|
|
11
11
|
const TAG = "JsvAudio";
|
|
12
12
|
|
|
13
|
+
let keyToken = 0;
|
|
14
|
+
const getKeyToken = () => {
|
|
15
|
+
return ++keyToken;
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
class AudioManager {
|
|
14
19
|
constructor() {
|
|
15
20
|
this.audioMap = {};
|
|
@@ -40,20 +45,9 @@ class AudioManager {
|
|
|
40
45
|
this.audioMap[id].buildPlatformInstance();
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
|
-
|
|
44
|
-
onCreatePlayer(result) {
|
|
45
|
-
console.log("CreatePlayerResult: " + result);
|
|
46
|
-
let resultObj = JSON.parse(result);
|
|
47
|
-
if (resultObj.code == 0) {
|
|
48
|
-
this.audioMap[resultObj.key]?.initPlayer();
|
|
49
|
-
} else {
|
|
50
|
-
console.error("Create player failed, key=" + resultObj.key);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
48
|
}
|
|
54
49
|
const sAudioManager = new AudioManager();
|
|
55
50
|
|
|
56
|
-
window.top.CreatePlayerResult = sAudioManager.onCreatePlayer.bind(sAudioManager);
|
|
57
51
|
|
|
58
52
|
const AUDIO_PROPS = {
|
|
59
53
|
"src": { type: Object },
|
|
@@ -74,6 +68,8 @@ class AudioProxy {
|
|
|
74
68
|
this._created = false;
|
|
75
69
|
this._eventListener = {};
|
|
76
70
|
this._propCache = {};
|
|
71
|
+
this._onVisibilityChange = this.onVisibilityChange.bind(this);
|
|
72
|
+
window.JsView?.onVisibilityChange(this._onVisibilityChange);
|
|
77
73
|
}
|
|
78
74
|
|
|
79
75
|
/** 初始化函数, 外部不应该调用 */
|
|
@@ -83,11 +79,14 @@ class AudioProxy {
|
|
|
83
79
|
console.error("no jsvAudioBridge");
|
|
84
80
|
return;
|
|
85
81
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
JsvAudioBridgeProxy.CreateAudio(this._playerId).then((data) => {
|
|
83
|
+
const info = JSON.parse(data);
|
|
84
|
+
if (info.state == 0) {
|
|
85
|
+
this.initPlayer();
|
|
86
|
+
} else {
|
|
87
|
+
console.error(TAG, "create audio failed", info.errorInfo);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
91
90
|
}
|
|
92
91
|
|
|
93
92
|
initPlayer() {
|
|
@@ -143,7 +142,7 @@ class AudioProxy {
|
|
|
143
142
|
switch (event) {
|
|
144
143
|
case Events.EVENT_END:
|
|
145
144
|
this.setProperty("paused", true);
|
|
146
|
-
this.callEventListener("
|
|
145
|
+
this.callEventListener("onended");
|
|
147
146
|
break;
|
|
148
147
|
case Events.EVENT_LOAD_START:
|
|
149
148
|
this.callEventListener("onloadstart");
|
|
@@ -306,8 +305,26 @@ class AudioProxy {
|
|
|
306
305
|
window.JMD.unsubscribe(this._playerId, this._onEvent);
|
|
307
306
|
JsvAudioBridgeProxy.ReleasePlayer(this._playerId);
|
|
308
307
|
this._audioManager.releaseAudio(this._playerId);
|
|
308
|
+
window.JsView?.removeEventCallback(this._onVisibilityChange);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
onVisibilityChange(state) {
|
|
313
|
+
console.log(TAG, "audio visibility change", JSON.stringify(state));
|
|
314
|
+
if (state.status == "show") {
|
|
315
|
+
if (typeof this._propCache["customerPause"] != "undefined") {
|
|
316
|
+
const customerPause = this._propCache["customerPause"];
|
|
317
|
+
delete this._propCache["customerPause"];
|
|
318
|
+
if (!customerPause) {
|
|
319
|
+
this.play();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
} else {
|
|
323
|
+
// pause when invisible
|
|
324
|
+
this._propCache["customerPause"] = this._propCache["paused"];
|
|
325
|
+
this.pause();
|
|
309
326
|
}
|
|
310
327
|
}
|
|
311
328
|
}
|
|
312
329
|
|
|
313
|
-
export { sAudioManager };
|
|
330
|
+
export { sAudioManager, getKeyToken };
|
|
@@ -1,151 +1,138 @@
|
|
|
1
|
-
<script>
|
|
2
|
-
import { sAudioManager } from "./AudioProxy.js";
|
|
1
|
+
<script setup>
|
|
2
|
+
import { sAudioManager, getKeyToken } from "./AudioProxy.js";
|
|
3
|
+
import { onBeforeUnmount } from "vue";
|
|
3
4
|
|
|
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
|
-
return {};
|
|
38
|
-
},
|
|
5
|
+
const props = defineProps({
|
|
6
|
+
/**
|
|
7
|
+
* 回调函数,播放器对象通知接口
|
|
8
|
+
* @param {Object} video对象,可以通过此video对象调用video相关属性和方法,具体属性和方法定义见JsvMedia.js文件里相关说明。
|
|
9
|
+
*/
|
|
10
|
+
onRef: { type: Function, default: () => {} },
|
|
11
|
+
/**
|
|
12
|
+
* 属性,Boolean类型,true表示自动播放,默认false。
|
|
13
|
+
*/
|
|
14
|
+
autoplay: { type: Boolean, default: false },
|
|
15
|
+
/**
|
|
16
|
+
* 属性,String类型,播放器实例索引,同样的key使用同一个播放器实例。
|
|
17
|
+
*/
|
|
18
|
+
playerKey: { type: String, default: null },
|
|
19
|
+
/**
|
|
20
|
+
* 属性,Boolean类型,true表示静音,默认false。
|
|
21
|
+
*/
|
|
22
|
+
muted: { type: Boolean, default: false },
|
|
23
|
+
/**
|
|
24
|
+
* 属性,Boolean类型,true表示循环播放,默认false。
|
|
25
|
+
*/
|
|
26
|
+
loop: { type: Boolean, default: false },
|
|
27
|
+
/**
|
|
28
|
+
* 属性,String类型,播放地址。
|
|
29
|
+
*/
|
|
30
|
+
src: { type: String, default: "" },
|
|
31
|
+
/**
|
|
32
|
+
* 回调函数,播放结束时通过此回调接口通知。
|
|
33
|
+
*/
|
|
34
|
+
onEnded: {
|
|
35
|
+
type: Function,
|
|
36
|
+
default: () => {
|
|
37
|
+
return {};
|
|
39
38
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
/**
|
|
51
|
-
* 回调函数,开始加载,设置完播放地址后,上报此事件。
|
|
52
|
-
*/
|
|
53
|
-
onLoadStart: {
|
|
54
|
-
type: Function,
|
|
55
|
-
default: () => {
|
|
56
|
-
return {};
|
|
57
|
-
},
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* 回调函数,播放错误时通过此接口通知。
|
|
42
|
+
* @param {int} 错误类型,当前定义了四种错误。1是异常中断;2是网络错误;3是解码错误;4是格式不支持。
|
|
43
|
+
*/
|
|
44
|
+
onError: {
|
|
45
|
+
type: Function,
|
|
46
|
+
default: () => {
|
|
47
|
+
return {};
|
|
58
48
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
49
|
+
},
|
|
50
|
+
/**
|
|
51
|
+
* 回调函数,开始加载,设置完播放地址后,上报此事件。
|
|
52
|
+
*/
|
|
53
|
+
onLoadStart: {
|
|
54
|
+
type: Function,
|
|
55
|
+
default: () => {
|
|
56
|
+
return {};
|
|
67
57
|
},
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
58
|
+
},
|
|
59
|
+
/**
|
|
60
|
+
* 回调函数,视频准备好后触发。
|
|
61
|
+
*/
|
|
62
|
+
onLoadedMetaData: {
|
|
63
|
+
type: Function,
|
|
64
|
+
default: () => {
|
|
65
|
+
return {};
|
|
76
66
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* 回调函数,视频准备好后触发,这个时候可以正常seek。
|
|
70
|
+
*/
|
|
71
|
+
onLoad: {
|
|
72
|
+
type: Function,
|
|
73
|
+
default: () => {
|
|
74
|
+
return {};
|
|
85
75
|
},
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
76
|
+
},
|
|
77
|
+
/**
|
|
78
|
+
* 回调函数,音频失去焦点后触发此事件,可能会导致pause(点播)或者离开频道(直播)。
|
|
79
|
+
*/
|
|
80
|
+
onAudioFocusLoss: {
|
|
81
|
+
type: Function,
|
|
82
|
+
default: () => {
|
|
83
|
+
return {};
|
|
94
84
|
},
|
|
95
85
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
86
|
+
/**
|
|
87
|
+
* 回调函数,音频获取焦点后触发此事件,可能会触发resume(点播)或者加入频道(直播)。
|
|
88
|
+
*/
|
|
89
|
+
onAudioFocusGain: {
|
|
90
|
+
type: Function,
|
|
91
|
+
default: () => {
|
|
92
|
+
return {};
|
|
93
|
+
},
|
|
100
94
|
},
|
|
101
|
-
|
|
102
|
-
let key = "JsvAudio_" + Math.floor(Math.random() * 10000);
|
|
103
|
-
if (this.playerKey) {
|
|
104
|
-
key = this.playerKey;
|
|
105
|
-
}
|
|
106
|
-
console.log("player key:" + key);
|
|
95
|
+
});
|
|
107
96
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
let key = "JsvAudio_" + getKeyToken();
|
|
98
|
+
if (props.playerKey) {
|
|
99
|
+
key = props.playerKey;
|
|
100
|
+
}
|
|
101
|
+
console.log("player key:" + key);
|
|
102
|
+
const audio = sAudioManager.createAudio(key);
|
|
103
|
+
//register listener
|
|
104
|
+
audio.addEventListener("ended", props.onEnded);
|
|
105
|
+
audio.addEventListener("error", props.onError);
|
|
106
|
+
audio.addEventListener("loadstart", props.onLoadStart);
|
|
107
|
+
audio.addEventListener("loadedmetadata", props.onLoadedMetaData);
|
|
108
|
+
audio.addEventListener("load", props.onLoad);
|
|
109
|
+
audio.addEventListener("audiofocusloss", props.onAudioFocusLoss);
|
|
110
|
+
audio.addEventListener("audiofocusgain", props.onAudioFocusGain);
|
|
111
|
+
// ste props
|
|
112
|
+
if (props.src && props.src !== "") {
|
|
113
|
+
audio.src = props.src;
|
|
114
|
+
}
|
|
113
115
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
if (props.autoplay) {
|
|
117
|
+
audio.autoplay = props.autoplay;
|
|
118
|
+
}
|
|
117
119
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
if (props.muted) {
|
|
121
|
+
audio.muted = props.muted;
|
|
122
|
+
}
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.onRef?.(this.audio);
|
|
126
|
-
},
|
|
124
|
+
if (props.loop) {
|
|
125
|
+
audio.loop = props.loop;
|
|
126
|
+
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
if (this.audio != null) {
|
|
130
|
-
this.audio.release();
|
|
131
|
-
this.onRef?.(null);
|
|
132
|
-
}
|
|
133
|
-
},
|
|
128
|
+
props.onRef?.(audio);
|
|
134
129
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
this.audio.addEventListener("loadedmetadata", this.onLoadedMetaData);
|
|
142
|
-
this.audio.addEventListener("load", this.onLoad);
|
|
143
|
-
this.audio.addEventListener("audiofocusloss", this.onAudioFocusLoss);
|
|
144
|
-
this.audio.addEventListener("audiofocusgain", this.onAudioFocusGain);
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
};
|
|
130
|
+
onBeforeUnmount(() => {
|
|
131
|
+
if (audio != null) {
|
|
132
|
+
audio.release();
|
|
133
|
+
props.onRef?.(null);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
149
136
|
</script>
|
|
150
137
|
|
|
151
138
|
<template>
|
|
@@ -7,12 +7,15 @@ export default class {
|
|
|
7
7
|
static isReady() {
|
|
8
8
|
return typeof window.jsvAudioBridge != "undefined" && !!window.jsvAudioBridge;
|
|
9
9
|
}
|
|
10
|
-
static CreateAudio(key
|
|
10
|
+
static CreateAudio(key) {
|
|
11
11
|
if (typeof window.jsvAudioBridge != "undefined"
|
|
12
12
|
&& typeof window.jsvAudioBridge.CreateAudio != "undefined") {
|
|
13
|
-
return window.jsvAudioBridge.CreateAudio(key
|
|
13
|
+
return window.jsvAudioBridge.CreateAudio(key);
|
|
14
14
|
}
|
|
15
|
-
return
|
|
15
|
+
return Promise.reject(JSON.stringify({
|
|
16
|
+
state: -2,
|
|
17
|
+
errorInfo: "jsvAudioBridge is null."
|
|
18
|
+
}));
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
static ReleasePlayer(key) {
|
|
@@ -7,9 +7,9 @@ let PluginInfo = {
|
|
|
7
7
|
// downloadUrl:"http://192.168.0.63:8080/plugin/JsvAudio-164.zip", //插件下载地址
|
|
8
8
|
packageName: "com.qcode.jsvaudio",
|
|
9
9
|
name: "音频插件",
|
|
10
|
-
version: "1.0.
|
|
11
|
-
versionCodeMin:
|
|
12
|
-
versionCodeMax:
|
|
10
|
+
version: "1.0.21", //插件需要的版本号
|
|
11
|
+
versionCodeMin: 21,
|
|
12
|
+
versionCodeMax: 21,
|
|
13
13
|
bridgeName: "jsvAudioBridge", //插件bridge注册到jsview的名称
|
|
14
14
|
className: "com.qcode.jsvaudio.JsvAudio", //插件初始化类名称
|
|
15
15
|
initMethod: "createInstance", //插件初始化方法
|
|
@@ -7,9 +7,9 @@ let PluginInfo = {
|
|
|
7
7
|
// downloadUrl:"http://192.168.0.63:8080/plugin/JsvAudio-164.zip", //插件下载地址
|
|
8
8
|
packageName: "com.qcode.jsvaudio",
|
|
9
9
|
name: "音频插件",
|
|
10
|
-
version: "1.0.
|
|
11
|
-
versionCodeMin:
|
|
12
|
-
versionCodeMax:
|
|
10
|
+
version: "1.0.21", //插件需要的版本号
|
|
11
|
+
versionCodeMin: 21,
|
|
12
|
+
versionCodeMax: 21,
|
|
13
13
|
bridgeName: "jsvAudioBridge", //插件bridge注册到jsview的名称
|
|
14
14
|
className: "com.qcode.jsvaudio.JsvAudio", //插件初始化类名称
|
|
15
15
|
initMethod: "createInstance", //插件初始化方法
|
|
@@ -1,53 +1,382 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
2
|
+
import { Forge } from "@shijiu/jsview/dom/jsv-forge-define";
|
|
3
|
+
import { shallowRef } from "vue"
|
|
4
|
+
|
|
5
|
+
let logDebug = console.log;
|
|
3
6
|
|
|
4
7
|
export default {
|
|
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
|
-
|
|
8
|
+
props: {
|
|
9
|
+
/**
|
|
10
|
+
* 回调函数,播放器对象通知接口
|
|
11
|
+
* @param {Object} video对象,可以通过此video对象调用video相关属性和方法,具体属性和方法定义见JsvMedia.js文件里相关说明。
|
|
12
|
+
*/
|
|
13
|
+
onRef: { type: Function, default: () => {} },
|
|
14
|
+
/**
|
|
15
|
+
* 属性,Boolean类型,true表示自动播放,默认false。
|
|
16
|
+
*/
|
|
17
|
+
autoplay: { type: Boolean, default: false },
|
|
18
|
+
/**
|
|
19
|
+
* 属性,String类型,播放器实例索引,同样的key使用同一个播放器实例。
|
|
20
|
+
*/
|
|
21
|
+
playerKey: { type: String, default: null},
|
|
22
|
+
/**
|
|
23
|
+
* 属性,String类型,播放器窗口模式,默认resizable模式。
|
|
24
|
+
* full:观影模式,全屏方式,不建议修改w/h/l/t,视频清晰度/流畅度最佳;
|
|
25
|
+
* resizable:可变窗口模式,可以随意修改w/h/l/t,支持动画效果,视频清晰度/流畅度表现可能不如full模式;
|
|
26
|
+
* pip:画中画模式,只在支持多路硬解的情况下使用,用户画中画的小窗播放,对清晰度/流畅度要求较差,可能会有反交错问题。
|
|
27
|
+
*/
|
|
28
|
+
windowMode: {type: String, default: "resizable"},
|
|
29
|
+
/**
|
|
30
|
+
* 属性,int类型,底层使用的播放器类型。1:系统播放器;2:jsv播放器。默认2。
|
|
31
|
+
*/
|
|
32
|
+
playerType: {type: Number, default: 2},
|
|
33
|
+
/**
|
|
34
|
+
* 属性,int类型,播放器解码方式。0:根据硬件能力自动匹配;1:硬解码;2:软解码。默认0(根据芯片能力自动匹配)。
|
|
35
|
+
*/
|
|
36
|
+
decodeType: {type: Number, default: 0},
|
|
37
|
+
/**
|
|
38
|
+
* 属性,Boolean类型,层级关系,true表示在界面的下面,false表示在界面上面,默认true。
|
|
39
|
+
*/
|
|
40
|
+
background: {type: Boolean, default: true},
|
|
41
|
+
/**
|
|
42
|
+
* 属性,Boolean类型,true表示静音,默认false。
|
|
43
|
+
*/
|
|
44
|
+
muted: {type: Boolean, default: false},
|
|
45
|
+
/**
|
|
46
|
+
* 属性,Boolean类型,true表示循环播放,默认false。
|
|
47
|
+
*/
|
|
48
|
+
loop:{type: Boolean, default: false},
|
|
49
|
+
/**
|
|
50
|
+
* 属性,String类型,播放地址。
|
|
51
|
+
*/
|
|
52
|
+
src: {type: String, default: ""},
|
|
53
|
+
/**
|
|
54
|
+
* 属性,Double类型,起播时间,0-duration。
|
|
55
|
+
*/
|
|
56
|
+
currentTime: { type: Number, default: 0},
|
|
57
|
+
/**
|
|
58
|
+
* 属性,Boolean类型,是否保留最后一帧,true保留,false不保留。
|
|
59
|
+
*/
|
|
60
|
+
keepLastFrame: {type: Boolean, default: true},
|
|
61
|
+
/**
|
|
62
|
+
* 属性,String类型,视频显示比例,origin原始比例显示,full全屏显示,x:y按照指定比例显示。
|
|
63
|
+
*/
|
|
64
|
+
videoAspectRatio: {type: String, default: "origin"},
|
|
65
|
+
/**
|
|
66
|
+
* 属性,JSON Object类型,色度抠像参数,包含color,colorDistance,edgeDistance,greenOffset四个参数,
|
|
67
|
+
* 参考格式:{color: 0x30FF30, colorDistance: 0.15, edgeDistance: 0.4, greenOffset: 0.05}
|
|
68
|
+
* color,色值,int类型,为抠图色值范围的中心点,格式为0xRRGGBB。
|
|
69
|
+
* colorDistance,float类型,色域范围,范围0-1.0。
|
|
70
|
+
* edgeDistance,float类型,alpha透明度距离,范围colorDistance-1.0,从colorDistance到edgeDistance的alpha值从1.0-0线性递减。
|
|
71
|
+
* greenOffset,float类型,红蓝差值,范围0-1.0。
|
|
72
|
+
* 目前只支持扣绿。
|
|
73
|
+
*/
|
|
74
|
+
chromaKey: {type: Object, default: null},
|
|
75
|
+
/**
|
|
76
|
+
* 回调函数,播放结束时通过此回调接口通知。
|
|
77
|
+
*/
|
|
78
|
+
onEnded: {type: Function, default: ()=>{return{}}},
|
|
79
|
+
/**
|
|
80
|
+
* 回调函数,播放错误时通过此接口通知。
|
|
81
|
+
* @param {int} 错误类型,当前定义了四种错误。1是异常中断;2是网络错误;3是解码错误;4是格式不支持。
|
|
82
|
+
*/
|
|
83
|
+
onError: {type: Function, default: ()=>{return{}}},
|
|
84
|
+
// onAbort: {type: Function, default: ()=>{return{}}},
|
|
85
|
+
/**
|
|
86
|
+
* 回调函数,当正常播放时,每0.5秒上报一次,回调接口里会带上当前时间,也可以使用video对象的currentTime属性去获取当前时间。
|
|
87
|
+
* @param {Long} 当前播放时间,单位秒。
|
|
88
|
+
*/
|
|
89
|
+
onTimeUpdate: {type: Function, default: ()=>{return{}}},
|
|
90
|
+
/**
|
|
91
|
+
* 回调函数,开始加载,设置完播放地址后,上报此事件。
|
|
92
|
+
*/
|
|
93
|
+
onLoadStart: {type: Function, default: ()=>{return{}}},
|
|
94
|
+
/**
|
|
95
|
+
* 回调函数,开始正常播放,同一个播放地址仅触发一次。
|
|
96
|
+
*/
|
|
97
|
+
onCanPlayThrough: {type: Function, default: ()=>{return{}}},
|
|
98
|
+
/**
|
|
99
|
+
* 回调函数,正常下载,duration不变的情况下只触发一次。
|
|
100
|
+
*/
|
|
101
|
+
onProgress: {type: Function, default: ()=>{return{}}},
|
|
102
|
+
/**
|
|
103
|
+
* 回调函数,视频准备好后触发。
|
|
104
|
+
*/
|
|
105
|
+
onLoadedMetaData: {type: Function, default: ()=>{return{}}},
|
|
106
|
+
/**
|
|
107
|
+
* 回调函数,视频准备好后触发,这个时候可以正常seek。
|
|
108
|
+
*/
|
|
109
|
+
onLoad: {type: Function, default: ()=>{return{}}},
|
|
110
|
+
/**
|
|
111
|
+
* 回调函数,视频准备好后触发,这个时候可以获取duration。
|
|
112
|
+
*/
|
|
113
|
+
onDurationChange: {type: Function, default: ()=>{return{}}},
|
|
114
|
+
/**
|
|
115
|
+
* 回调函数,视频准备好后,seek(修改currentTime)会出发此事件。
|
|
116
|
+
*/
|
|
117
|
+
onSeeking: {type: Function, default: ()=>{return{}}},
|
|
118
|
+
/**
|
|
119
|
+
* 回调函数,seek完成后触发此事件。
|
|
120
|
+
*/
|
|
121
|
+
onSeeked: {type: Function, default: ()=>{return{}}},
|
|
122
|
+
/**
|
|
123
|
+
* 回调函数,缓冲时触发。
|
|
124
|
+
*/
|
|
125
|
+
onStalled: {type: Function, default: ()=>{return{}}},
|
|
126
|
+
/**
|
|
127
|
+
* 回调函数,正常播放后触发。
|
|
128
|
+
*/
|
|
129
|
+
onPlaying: {type: Function, default: ()=>{return{}}},
|
|
130
|
+
/**
|
|
131
|
+
* 回调函数,正常显示后触发此事件。
|
|
132
|
+
*/
|
|
133
|
+
onCanPlay: {type: Function, default: ()=>{return{}}},
|
|
134
|
+
/**
|
|
135
|
+
* 回调函数,音频失去焦点后触发此事件,可能会导致pause(点播)或者离开频道(直播)。
|
|
136
|
+
*/
|
|
137
|
+
onAudioFocusLoss: {type: Function, default: ()=>{return{}}},
|
|
138
|
+
/**
|
|
139
|
+
* 回调函数,音频获取焦点后触发此事件,可能会触发resume(点播)或者加入频道(直播)。
|
|
140
|
+
*/
|
|
141
|
+
onAudioFocusGain: {type: Function, default: ()=>{return{}}},
|
|
142
|
+
/**
|
|
143
|
+
* 回调函数,直播进入时移状态时,触发此事件。
|
|
144
|
+
*/
|
|
145
|
+
onTimeShift: {type: Function, default: ()=>{return{}}},
|
|
146
|
+
/**
|
|
147
|
+
* 回调函数,直播进入时移状态时,成功播放触发此事件。
|
|
148
|
+
*/
|
|
149
|
+
onTimeShifted: {type: Function, default: ()=>{return{}}},
|
|
150
|
+
/**
|
|
151
|
+
* 回调函数,时移回到直播状态,触发此事件。
|
|
152
|
+
*/
|
|
153
|
+
onBackLive: {type: Function, default: ()=>{return{}}},
|
|
154
|
+
/**
|
|
155
|
+
* 回调函数,时移回到直播状态时,成功播放触发此事件。
|
|
156
|
+
*/
|
|
157
|
+
onBackLived: {type: Function, default: ()=>{return{}}},
|
|
158
|
+
/**
|
|
159
|
+
* 属性,Object类型,设置窗口属性,同其他组件的style设置。
|
|
160
|
+
*/
|
|
161
|
+
style: {type: Object, default: ()=>{return{}}},
|
|
162
|
+
/**
|
|
163
|
+
* 属性,Boolean类型,活跃状态,当多个JsvPlayer组件指向一个video对象时,只能有一个处于活跃状态。
|
|
164
|
+
* 视频显示在活跃JsvPlayer设置的显示区域内,事件通知也只会送给活跃的JsvPlayer组件注册的回调函数。
|
|
165
|
+
*/
|
|
166
|
+
active:{type: Boolean, default: true},
|
|
167
|
+
},
|
|
168
|
+
components: {
|
|
169
|
+
|
|
170
|
+
},
|
|
171
|
+
watch: {
|
|
172
|
+
active(newValue) {
|
|
173
|
+
console.log("LudlDebug active newValue: "+newValue);
|
|
174
|
+
if(this.video){
|
|
175
|
+
if(newValue){
|
|
176
|
+
this.registerEvent();
|
|
177
|
+
this.video.setHoleID(this.holeId);
|
|
178
|
+
this.holeStyle = {
|
|
179
|
+
left: 0, top: 0,
|
|
180
|
+
width: this.style.width, height: this.style.height
|
|
181
|
+
};
|
|
182
|
+
}else{
|
|
183
|
+
this.holeStyle = {
|
|
184
|
+
left: 0, top: 0,
|
|
185
|
+
width: 0, height: 0
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
style(newValue){
|
|
191
|
+
logDebug("holeStyle newValue: left="+newValue.left+", top="+newValue.top+", width="+newValue.width+", height="+newValue.height);
|
|
192
|
+
if(this.active){
|
|
193
|
+
this.holeStyle = {
|
|
194
|
+
left: 0, top: 0,
|
|
195
|
+
width:newValue.width, height:newValue.height
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
keepLastFrame(newValue){
|
|
200
|
+
logDebug("keepLastFrame newValue: "+newValue);
|
|
201
|
+
if(this.video && this.active){
|
|
202
|
+
this.video.keepLastFrame = this.keepLastFrame;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
setup(){
|
|
208
|
+
return {
|
|
209
|
+
video: null,
|
|
210
|
+
holeId: "",
|
|
211
|
+
jsvMainView: shallowRef(null),
|
|
212
|
+
innerViewId: shallowRef(-1),
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
data() {
|
|
217
|
+
return {
|
|
218
|
+
holeStyle: {},
|
|
219
|
+
};
|
|
220
|
+
},
|
|
221
|
+
created() {
|
|
222
|
+
},
|
|
223
|
+
mounted() {
|
|
224
|
+
logDebug("JsvPlayer:",this.style)
|
|
225
|
+
|
|
226
|
+
let key = "Jsv_" + Math.floor(Math.random() * 10000);
|
|
227
|
+
if(this.playerKey){
|
|
228
|
+
key = this.playerKey;
|
|
229
|
+
}
|
|
230
|
+
logDebug("player key:"+key)
|
|
231
|
+
|
|
232
|
+
let player_type = 1;
|
|
233
|
+
if(this.playerType)
|
|
234
|
+
player_type = this.playerType;
|
|
235
|
+
|
|
236
|
+
let background = true;
|
|
237
|
+
if(!this.background)
|
|
238
|
+
background = this.background;
|
|
239
|
+
|
|
240
|
+
logDebug("JsvPlayer background:"+background)
|
|
241
|
+
|
|
242
|
+
const designMap = window.Forge.DesignMap();
|
|
243
|
+
logDebug("JsvPlayer:", this.holeId)
|
|
244
|
+
|
|
245
|
+
// this.video = findMediaObjectByKey(key);
|
|
246
|
+
// TODO: 后续支持共享MediaId ?
|
|
247
|
+
let first_create = true;
|
|
248
|
+
|
|
249
|
+
if(!this.video){
|
|
250
|
+
// 创建PC版本的video标签
|
|
251
|
+
this.video = window.originDocument.createElement("video");
|
|
252
|
+
}else{
|
|
253
|
+
this.video.setRef();
|
|
254
|
+
first_create = false;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if(this.video != null){
|
|
258
|
+
if(this.active){
|
|
259
|
+
this.registerEvent();
|
|
260
|
+
|
|
261
|
+
//if(first_create){
|
|
262
|
+
if(this.src && this.src !== "")
|
|
263
|
+
this.video.src = this.src;
|
|
264
|
+
|
|
265
|
+
if(this.currentTime !== 0)
|
|
266
|
+
this.video.currentTime = this.currentTime;
|
|
267
|
+
|
|
268
|
+
if(this.autoplay){
|
|
269
|
+
this.video.autoplay = this.autoplay;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if(this.muted){
|
|
273
|
+
this.video.muted = this.muted;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if(this.loop){
|
|
277
|
+
this.video.loop = this.loop;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if(!this.keepLastFrame){
|
|
281
|
+
this.video.keepLastFrame = this.keepLastFrame;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if(this.videoAspectRatio !== "origin"){
|
|
285
|
+
this.video.videoAspectRatio = this.videoAspectRatio;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if(this.chromaKey && this.chromaKey !== ""){
|
|
289
|
+
this.video.setChromaKey(this.chromaKey)
|
|
290
|
+
}
|
|
291
|
+
//}else{
|
|
292
|
+
if(!first_create){
|
|
293
|
+
this.video.setHoleID(this.holeId);
|
|
294
|
+
this.holeStyle = {
|
|
295
|
+
left: 0, top: 0,
|
|
296
|
+
width: this.style.width, height: this.style.height
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// 创建挂载的VideoView
|
|
302
|
+
this.jsvMainView = new Forge.VideoView(this.video, null);
|
|
303
|
+
this.innerViewId = Forge.sViewStore.add(
|
|
304
|
+
new Forge.ViewInfo(this.jsvMainView)
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
this.onRef?.(this.video)
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
unmounted(){
|
|
312
|
+
if(this.video != null) {
|
|
313
|
+
// 清理View管理缓存
|
|
314
|
+
if (this.innerViewId !== -1) {
|
|
315
|
+
Forge.sViewStore.remove(this.innerViewId);
|
|
316
|
+
this.innerViewId = -1;
|
|
317
|
+
this.jsvMainView = null;
|
|
32
318
|
}
|
|
319
|
+
this.onRef?.(null)
|
|
320
|
+
}
|
|
321
|
+
},
|
|
33
322
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
323
|
+
methods: {
|
|
324
|
+
getHoleId(id){
|
|
325
|
+
logDebug("getHoleId:", id)
|
|
326
|
+
this.holeId = id;
|
|
327
|
+
},
|
|
328
|
+
registerEvent(){
|
|
329
|
+
if(this.video && this.active){
|
|
330
|
+
this.video.addEventListener("end", this.onEnded);
|
|
331
|
+
this.video.addEventListener("error", this.onError);
|
|
332
|
+
//this.video.addEventListener("abort", this.onAbort);
|
|
333
|
+
this.video.addEventListener("timeupdate", this.onTimeUpdate);
|
|
334
|
+
this.video.addEventListener("loadstart", () => {
|
|
335
|
+
logDebug("JsvPlayer received loadstart event.");
|
|
336
|
+
this.holeStyle = {
|
|
337
|
+
left: 0, top: 0,
|
|
338
|
+
width:this.style.width, height:this.style.height
|
|
339
|
+
};
|
|
340
|
+
this.onLoadStart();
|
|
341
|
+
});
|
|
342
|
+
this.video.addEventListener("canplaythrough", this.onCanPlayThrough);
|
|
343
|
+
this.video.addEventListener("progress", this.onProgress);
|
|
344
|
+
this.video.addEventListener("loadedmetadata", this.onLoadedMetaData);
|
|
345
|
+
this.video.addEventListener("load", this.onLoad);
|
|
346
|
+
this.video.addEventListener("durationchange", this.onDurationChange);
|
|
347
|
+
this.video.addEventListener("seeking", this.onSeeking);
|
|
348
|
+
this.video.addEventListener("seeked", this.onSeeked);
|
|
349
|
+
this.video.addEventListener("stalled", this.onStalled);
|
|
350
|
+
this.video.addEventListener("playing", this.onPlaying);
|
|
351
|
+
this.video.addEventListener("canplay", this.onCanPlay);
|
|
352
|
+
this.video.addEventListener("audiofocusloss", this.onAudioFocusLoss);
|
|
353
|
+
this.video.addEventListener("audiofocusgain", this.onAudioFocusGain);
|
|
354
|
+
this.video.addEventListener("timeshift", this.onTimeShift);
|
|
355
|
+
this.video.addEventListener("timeshifted", this.onTimeShifted);
|
|
356
|
+
this.video.addEventListener("backlive", this.onBackLive);
|
|
357
|
+
this.video.addEventListener("backlived", this.onBackLived);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
}
|
|
45
362
|
|
|
363
|
+
//export default JsvPlayer;
|
|
46
364
|
</script>
|
|
47
365
|
|
|
48
366
|
<template>
|
|
49
|
-
<
|
|
367
|
+
<div :style="holeStyle" backgroundColor="rgba(0,0,0,1)">
|
|
368
|
+
<div
|
|
369
|
+
:style="{
|
|
370
|
+
left: holeStyle.left,
|
|
371
|
+
top: holeStyle.top,
|
|
372
|
+
width: holeStyle.width,
|
|
373
|
+
height: holeStyle.height,
|
|
374
|
+
}"
|
|
375
|
+
:data-jsv-vw-innerview="innerViewId"
|
|
376
|
+
/>
|
|
377
|
+
</div>
|
|
50
378
|
</template>
|
|
51
379
|
|
|
52
380
|
<style scoped>
|
|
53
|
-
|
|
381
|
+
|
|
382
|
+
</style>
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
let PluginInfo={
|
|
2
|
-
// downloadUrl:"http://192.168.0.
|
|
2
|
+
// downloadUrl:"http://192.168.0.85:8080/plugin/JsvPlayer-199.zip", //插件下载地址
|
|
3
3
|
packageName:"com.qcode.jsvplayer",
|
|
4
4
|
name:"播放器插件",
|
|
5
|
-
version:"
|
|
6
|
-
versionCodeMin:
|
|
7
|
-
versionCodeMax:
|
|
5
|
+
version:"2.0.2", //插件需要的版本号
|
|
6
|
+
versionCodeMin:202,
|
|
7
|
+
versionCodeMax:202,
|
|
8
8
|
bridgeName:"jsvPlayerBridge", //插件bridge注册到jsview的名称
|
|
9
9
|
className:"com.qcode.jsvplayer.JsvPlayer", //插件初始化类名称
|
|
10
10
|
initMethod:"createInstance", //插件初始化方法
|
|
11
11
|
listener:"top.JsvPlayerPluginLoadResult", //插件加载结果回调
|
|
12
12
|
listener2: "top.JsvPlayerPluginStatus",
|
|
13
13
|
// debug:true,
|
|
14
|
-
md5:"
|
|
14
|
+
md5:"6de8d620492bf0143bbccc62636a6271"
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// 导出字段要含有 pluginVersionInfo 信息,作为npm时版本参考
|
|
18
|
-
export default PluginInfo;
|
|
18
|
+
export default PluginInfo;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
let interfaceLoaded = false;
|
|
2
|
+
let idGeneratoer = 1;
|
|
3
|
+
let idObjectMap = {};
|
|
4
|
+
|
|
5
|
+
const ensureInterface = ()=>{
|
|
6
|
+
return new Promise((resolve, reject)=>{
|
|
7
|
+
if (interfaceLoaded) {
|
|
8
|
+
// 已经加载完成
|
|
9
|
+
resolve();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (window.JsView) {
|
|
14
|
+
let cb = ()=>{
|
|
15
|
+
// dynamic加载完成
|
|
16
|
+
if (window.jJsvDemoTester) {
|
|
17
|
+
console.log("window.jJsvDemoTester ready")
|
|
18
|
+
} else {
|
|
19
|
+
console.log("window.jJsvDemoTester failed")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
console.log("remove cb=" + cb);
|
|
23
|
+
|
|
24
|
+
// 移除回调
|
|
25
|
+
window.JsView.removeEventListener(cb);
|
|
26
|
+
|
|
27
|
+
// 无论成功失败,暂且都论为加载完成
|
|
28
|
+
interfaceLoaded = true;
|
|
29
|
+
resolve();
|
|
30
|
+
};
|
|
31
|
+
window.JsView.addEventListener("__DemoTesterReady", cb);
|
|
32
|
+
window.JsView.ensureTesterUtils();
|
|
33
|
+
} else {
|
|
34
|
+
interfaceLoaded = true;
|
|
35
|
+
resolve(); // 非JsView场景,默认为完成状态
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const enableNativeViewListener = (nativeViewId)=>{
|
|
41
|
+
let listenerId = idGeneratoer;
|
|
42
|
+
idGeneratoer++;
|
|
43
|
+
idObjectMap[listenerId] = {
|
|
44
|
+
eventCallback: (ret)=>{
|
|
45
|
+
console.log("JsvDemoTester nativeView info=" + ret.info);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
(async ()=>{
|
|
50
|
+
await ensureInterface();
|
|
51
|
+
|
|
52
|
+
if (idObjectMap.hasOwnProperty(listenerId)) {
|
|
53
|
+
let eventName = "__enableNativeViewListener_" + listenerId;
|
|
54
|
+
|
|
55
|
+
window.JsView?.addEventListener(eventName, idObjectMap[listenerId].eventCallback);
|
|
56
|
+
window.jJsvDemoTester?.enableNativeViewListener(nativeViewId, listenerId, eventName);
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
|
|
60
|
+
return listenerId; // 用于 disableNativeViewListener() 调用
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const disableNativeViewListener = (listenerId)=>{
|
|
64
|
+
(async ()=>{
|
|
65
|
+
await ensureInterface();
|
|
66
|
+
|
|
67
|
+
if (idObjectMap.hasOwnProperty(listenerId)) {
|
|
68
|
+
window.JsView?.removeEventListener(idObjectMap[listenerId].eventCallback);
|
|
69
|
+
window.jJsvDemoTester?.disableNativeViewListener(listenerId);
|
|
70
|
+
delete idObjectMap[listenerId];
|
|
71
|
+
} else {
|
|
72
|
+
console.warn("disableNativeViewListener id gone");
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
enableNativeViewListener,
|
|
79
|
+
disableNativeViewListener
|
|
80
|
+
}
|
|
@@ -50,7 +50,8 @@ class JsvHashHistory {
|
|
|
50
50
|
|
|
51
51
|
#createMockHashHistory(base) {
|
|
52
52
|
// 抄 createWebHashHistory 作业
|
|
53
|
-
base = location.host ? base || location.pathname + location.search : '';
|
|
53
|
+
// base = location.host ? base || location.pathname + location.search : '';
|
|
54
|
+
base = (location.host || location.protocol === 'file:') ? base || location.pathname + location.search : '';
|
|
54
55
|
if (!base.includes('#'))
|
|
55
56
|
base += '#';
|
|
56
57
|
if (!base.endsWith('#/') && !base.endsWith('#')) {
|
|
@@ -11,7 +11,7 @@ let _JsvApic;
|
|
|
11
11
|
if (window.JsView) {
|
|
12
12
|
_JsvApic = JsvApic.default;
|
|
13
13
|
} else {
|
|
14
|
-
const BrowserApic = await import("./BrowserApic/
|
|
14
|
+
const BrowserApic = await import("./BrowserApic/BrowserApic.vue");
|
|
15
15
|
_JsvApic = BrowserApic.default;
|
|
16
16
|
}
|
|
17
17
|
let LoopType = JsvApic.LoopType;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* borderOutset {int} (必填)原图信息: 图片边缘到中心内容区域边缘的距离, 快速计算: (原图宽度 - 原图中放内容宽度) / 2
|
|
15
15
|
* animTime {int} (必填)缩放动画的时长(单位秒)
|
|
16
16
|
* waitForInit {boolean} (选填)尺寸为0时是否进行描画(例如: 首次显示不展示动画的场合,设置为true),默认值为true
|
|
17
|
+
* onTransitionEnd { Function } (选填)transition动画结束的回调
|
|
17
18
|
-->
|
|
18
19
|
|
|
19
20
|
<script setup>
|
|
@@ -37,6 +38,7 @@ const props = defineProps({
|
|
|
37
38
|
centerWidth: { type: Number, default: -1, required: true },
|
|
38
39
|
borderOutset: { type: Number, required: true },
|
|
39
40
|
animTime: { type: Number, default: 0 },
|
|
41
|
+
onTransitionEnd: { type: Function },
|
|
40
42
|
});
|
|
41
43
|
|
|
42
44
|
const isReady = () => {
|
|
@@ -99,5 +101,6 @@ watchEffect(() => {
|
|
|
99
101
|
borderImageWidth: `${borderDspWidth}px`,
|
|
100
102
|
borderImageOutset: `${convertedBorderOutset}px`,
|
|
101
103
|
}"
|
|
104
|
+
@transitionend="props.onTransitionEnd"
|
|
102
105
|
/>
|
|
103
106
|
</template>
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div :style="{ width: 1, height: 1 }" backgroundColor="rgba(0,0,0,1)"></div>
|
|
3
|
+
</template>
|
|
4
|
+
<script >
|
|
5
|
+
export default {
|
|
6
|
+
props: {
|
|
7
|
+
/**
|
|
8
|
+
* 回调函数,播放器对象通知接口
|
|
9
|
+
* @param {Object} video对象,可以通过此video对象调用video相关属性和方法,具体属性和方法定义见JsvMedia.js文件里相关说明。
|
|
10
|
+
*/
|
|
11
|
+
onRef: { type: Function, default: () => { } },
|
|
12
|
+
/**
|
|
13
|
+
* 属性,Boolean类型,true表示自动播放,默认false。
|
|
14
|
+
*/
|
|
15
|
+
autoplay: { type: Boolean, default: false },
|
|
16
|
+
/**
|
|
17
|
+
* 属性,String类型,播放器实例索引,同样的key使用同一个播放器实例。
|
|
18
|
+
*/
|
|
19
|
+
playerKey: { type: String, default: null },
|
|
20
|
+
/**
|
|
21
|
+
* 属性,Boolean类型,true表示静音,默认false。
|
|
22
|
+
*/
|
|
23
|
+
muted: { type: Boolean, default: false },
|
|
24
|
+
/**
|
|
25
|
+
* 属性,Boolean类型,true表示循环播放,默认false。
|
|
26
|
+
*/
|
|
27
|
+
loop: { type: Boolean, default: false },
|
|
28
|
+
/**
|
|
29
|
+
* 属性,String类型,播放地址。
|
|
30
|
+
*/
|
|
31
|
+
src: { type: String, default: "" },
|
|
32
|
+
/**
|
|
33
|
+
* 回调函数,播放结束时通过此回调接口通知。
|
|
34
|
+
*/
|
|
35
|
+
onEnded: {
|
|
36
|
+
type: Function,
|
|
37
|
+
default: () => {
|
|
38
|
+
return {};
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* 回调函数,播放错误时通过此接口通知。
|
|
43
|
+
* @param {int} 错误类型,当前定义了四种错误。1是异常中断;2是网络错误;3是解码错误;4是格式不支持。
|
|
44
|
+
*/
|
|
45
|
+
onError: {
|
|
46
|
+
type: Function,
|
|
47
|
+
default: () => {
|
|
48
|
+
return {};
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* 回调函数,开始加载,设置完播放地址后,上报此事件。
|
|
53
|
+
*/
|
|
54
|
+
onLoadStart: {
|
|
55
|
+
type: Function,
|
|
56
|
+
default: () => {
|
|
57
|
+
return {};
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
/**
|
|
61
|
+
* 回调函数,视频准备好后触发。
|
|
62
|
+
*/
|
|
63
|
+
onLoadedMetaData: {
|
|
64
|
+
type: Function,
|
|
65
|
+
default: () => {
|
|
66
|
+
return {};
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* 回调函数,视频准备好后触发,这个时候可以正常seek。
|
|
71
|
+
*/
|
|
72
|
+
onLoad: {
|
|
73
|
+
type: Function,
|
|
74
|
+
default: () => {
|
|
75
|
+
return {};
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* 回调函数,音频失去焦点后触发此事件,可能会导致pause(点播)或者离开频道(直播)。
|
|
80
|
+
*/
|
|
81
|
+
onAudioFocusLoss: {
|
|
82
|
+
type: Function,
|
|
83
|
+
default: () => {
|
|
84
|
+
return {};
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
/**
|
|
88
|
+
* 回调函数,音频获取焦点后触发此事件,可能会触发resume(点播)或者加入频道(直播)。
|
|
89
|
+
*/
|
|
90
|
+
onAudioFocusGain: {
|
|
91
|
+
type: Function,
|
|
92
|
+
default: () => {
|
|
93
|
+
return {};
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
/*
|
|
97
|
+
*预下载音频
|
|
98
|
+
*/
|
|
99
|
+
preDownload: {
|
|
100
|
+
type: Boolean,
|
|
101
|
+
default: false
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
setup() {
|
|
105
|
+
return {
|
|
106
|
+
audio: null,
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
mounted() {
|
|
110
|
+
this.Jsvinit()
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
beforeUnmount() {
|
|
114
|
+
if (this.audio != null) {
|
|
115
|
+
this.audio.unload?.();
|
|
116
|
+
this.audio.releaseResource?.();
|
|
117
|
+
this.audio.onPlatformDestroy?.();
|
|
118
|
+
this.onRef?.(null);
|
|
119
|
+
this.unregisterOnVisibilityChange()
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
methods: {
|
|
124
|
+
registerEvent() {
|
|
125
|
+
if (this.audio) {
|
|
126
|
+
this.audio.addEventListener("ended", this.onEnded);
|
|
127
|
+
this.audio.addEventListener("error", this.onError);
|
|
128
|
+
this.audio.addEventListener("loadstart", this.onLoadStart);
|
|
129
|
+
this.audio.addEventListener("loadedmetadata", this.onLoadedMetaData);
|
|
130
|
+
this.audio.addEventListener("load", this.onLoad);
|
|
131
|
+
this.audio.addEventListener("audiofocusloss", this.onAudioFocusLoss);
|
|
132
|
+
this.audio.addEventListener("audiofocusgain", this.onAudioFocusGain);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
onVisibilityChange(status) {
|
|
136
|
+
if (status.status === "show") {
|
|
137
|
+
this.onJsViewShow();
|
|
138
|
+
} else if (status.status === "hide") {
|
|
139
|
+
this.onJsViewHide();
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
registerOnVisibilityChange() {
|
|
143
|
+
if (window.JsView) {
|
|
144
|
+
window.JsView.onVisibilityChange(this.onVisibilityChange);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
unregisterOnVisibilityChange() {
|
|
148
|
+
if (window.JsView) {
|
|
149
|
+
window.JsView.removeEventCallback(this.onVisibilityChange);
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
onJsViewHide() {
|
|
153
|
+
if (this.audio) {
|
|
154
|
+
//释放
|
|
155
|
+
if (!this.audio.paused) {
|
|
156
|
+
this.stopByOnHide = true;
|
|
157
|
+
}
|
|
158
|
+
this.audio.unload();
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
onJsViewShow() {
|
|
162
|
+
//重新加载
|
|
163
|
+
if (!this.audio) {
|
|
164
|
+
this.Jsvinit()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (this.stopByOnHide) {
|
|
168
|
+
this.stopByOnHide = false;
|
|
169
|
+
this.Jsvinit()
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
Jsvinit() {
|
|
173
|
+
let key = "JsvAudio_" + Math.floor(Math.random() * 10000);
|
|
174
|
+
if (this.playerKey) {
|
|
175
|
+
key = this.playerKey;
|
|
176
|
+
}
|
|
177
|
+
console.log("player key:" + key);
|
|
178
|
+
|
|
179
|
+
this.audio = new Audio();
|
|
180
|
+
this.registerEvent();
|
|
181
|
+
if (this.src && this.src !== "") {
|
|
182
|
+
let realUrl
|
|
183
|
+
if (window.JsView) {
|
|
184
|
+
// jsview上
|
|
185
|
+
realUrl = new window.JsView.Dom.UrlRef(this.src).href;
|
|
186
|
+
} else {
|
|
187
|
+
realUrl = this.src;
|
|
188
|
+
}
|
|
189
|
+
this.audio.src = realUrl;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (this.autoplay) {
|
|
193
|
+
this.audio.autoplay = this.autoplay;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (this.muted) {
|
|
197
|
+
this.audio.muted = this.muted;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (this.loop) {
|
|
201
|
+
this.audio.loop = this.loop;
|
|
202
|
+
}
|
|
203
|
+
if (this.preDownload) {
|
|
204
|
+
this.audio.predownload = this.preDownload;
|
|
205
|
+
}
|
|
206
|
+
this.onRef?.(this.audio);
|
|
207
|
+
//初始化
|
|
208
|
+
Promise.resolve().then(() => {
|
|
209
|
+
if (this.audio) {
|
|
210
|
+
this.audio.confirmInitSetup?.();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
this.registerOnVisibilityChange()
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
<style lang="scss" scoped></style>
|
|
@@ -40,3 +40,4 @@ export { default as JsvTextBox } from "./JsvTextBox.vue";
|
|
|
40
40
|
export { default as JsvTouchContainer } from "./JsvTouchContainer.vue";
|
|
41
41
|
export { default as JsvTransparentDiv } from "./JsvTransparentDiv.vue";
|
|
42
42
|
export { default as JsvVideo } from "./JsvVideo.vue";
|
|
43
|
+
export { default as JsvSystemAudio } from "./JsvSystemAudio.vue";
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
let PluginInfo={
|
|
2
|
-
// downloadUrl:"http://192.168.0.63:8080/plugin/JsvPlayer-164.zip", //插件下载地址
|
|
3
|
-
packageName:"com.qcode.jsvplayer",
|
|
4
|
-
name:"播放器插件",
|
|
5
|
-
version:"1.8.4", //插件需要的版本号
|
|
6
|
-
versionCodeMin:184,
|
|
7
|
-
versionCodeMax:184,
|
|
8
|
-
bridgeName:"jsvPlayerBridge", //插件bridge注册到jsview的名称
|
|
9
|
-
className:"com.qcode.jsvplayer.JsvPlayer", //插件初始化类名称
|
|
10
|
-
initMethod:"createInstance", //插件初始化方法
|
|
11
|
-
listener:"top.JsvPlayerPluginLoadResult", //插件加载结果回调
|
|
12
|
-
listener2: "top.JsvPlayerPluginStatus",
|
|
13
|
-
// debug:true,
|
|
14
|
-
md5:"7d6d28f8eda9c4b2725eecb578b553ab"
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// 不要用export default,update-env脚本不能解析
|
|
18
|
-
// 导出字段要含有 pluginVersionInfo 信息,作为npm时版本参考
|
|
19
|
-
module.exports = PluginInfo;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./BrowserApic.vue"
|