video-player.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <template>
  2. <view class="video-player-container">
  3. <!-- H5环境使用Jessibuca -->
  4. <view v-if="isH5" class="h5-player">
  5. <view ref="jessibucaContainer" class="jessibuca-container"></view>
  6. <view v-if="loadError" class="error-message">
  7. <text>视频加载失败,请刷新重试</text>
  8. </view>
  9. </view>
  10. <!-- 小程序环境使用live-player -->
  11. <view v-else class="mp-player">
  12. <live-player
  13. id="videoPlayer"
  14. :src="videoUrl"
  15. mode="live"
  16. :autoplay="autoplay"
  17. :muted="isMuted"
  18. object-fit="contain"
  19. @statechange="onStateChange"
  20. @error="onError"
  21. @fullscreenchange="onFullscreenChange"
  22. class="live-player"
  23. ></live-player>
  24. </view>
  25. </view>
  26. </template>
  27. <script>
  28. export default {
  29. name: 'VideoPlayer',
  30. props: {
  31. videoUrl: {
  32. type: String,
  33. required: true
  34. },
  35. hasAudio: {
  36. type: Boolean,
  37. default: true
  38. },
  39. autoplay: {
  40. type: Boolean,
  41. default: true
  42. }
  43. },
  44. data() {
  45. return {
  46. isH5: false,
  47. isMuted: false,
  48. isPlaying: false,
  49. isFullscreen: false,
  50. jessibucaPlayer: null,
  51. livePlayerContext: null,
  52. loadError: false
  53. }
  54. },
  55. mounted() {
  56. // 判断当前环境
  57. // #ifdef H5
  58. this.isH5 = true;
  59. this.$nextTick(() => {
  60. this.initJessibucaPlayer();
  61. });
  62. // #endif
  63. // #ifdef MP-WEIXIN
  64. this.isH5 = false;
  65. this.initLivePlayer();
  66. // #endif
  67. },
  68. beforeDestroy() {
  69. this.destroyPlayer();
  70. },
  71. watch: {
  72. videoUrl: {
  73. handler(newUrl) {
  74. if (this.isH5 && this.jessibucaPlayer) {
  75. this.destroyPlayer();
  76. this.$nextTick(() => {
  77. this.initJessibucaPlayer();
  78. });
  79. }
  80. }
  81. }
  82. },
  83. methods: {
  84. // 初始化Jessibuca播放器(H5环境)
  85. initJessibucaPlayer() {
  86. if (!this.isH5 || !this.$jessibuca) {
  87. console.error('H5环境或Jessibuca插件未初始化');
  88. this.loadError = true;
  89. return;
  90. }
  91. console.log('正在初始化Jessibuca播放器,URL:', this.videoUrl);
  92. // 使用Jessibuca插件
  93. this.$jessibuca.createPlayer({
  94. container: this.$refs.jessibucaContainer,
  95. url: this.videoUrl,
  96. hasAudio: this.hasAudio,
  97. autoplay: this.autoplay,
  98. decoder: './static/js/jessibuca/decoder.js',
  99. wasmUrl: './static/js/jessibuca/decoder.wasm'
  100. }).then(player => {
  101. if (!player) {
  102. console.error('Jessibuca播放器创建失败');
  103. this.loadError = true;
  104. return;
  105. }
  106. this.jessibucaPlayer = player;
  107. console.log('Jessibuca播放器创建成功');
  108. // 监听事件
  109. player.on('play', () => {
  110. this.isPlaying = true;
  111. this.loadError = false;
  112. this.$emit('play');
  113. });
  114. player.on('pause', () => {
  115. this.isPlaying = false;
  116. this.$emit('pause');
  117. });
  118. player.on('error', (err) => {
  119. console.error('Jessibuca播放错误:', err);
  120. this.loadError = true;
  121. this.$emit('error', err);
  122. });
  123. }).catch(err => {
  124. console.error('Jessibuca播放器初始化失败:', err);
  125. this.loadError = true;
  126. });
  127. },
  128. // 初始化小程序live-player
  129. initLivePlayer() {
  130. if (this.isH5) return;
  131. // #ifdef MP-WEIXIN
  132. this.livePlayerContext = uni.createLivePlayerContext('videoPlayer', this);
  133. if (this.autoplay) {
  134. this.play();
  135. }
  136. // #endif
  137. },
  138. // 播放
  139. play() {
  140. if (this.isH5 && this.jessibucaPlayer) {
  141. this.jessibucaPlayer.play();
  142. } else if (this.livePlayerContext) {
  143. this.livePlayerContext.play({
  144. success: () => {
  145. this.isPlaying = true;
  146. this.$emit('play');
  147. },
  148. fail: (err) => {
  149. console.error('播放失败:', err);
  150. this.$emit('error', err);
  151. }
  152. });
  153. }
  154. },
  155. // 暂停
  156. pause() {
  157. if (this.isH5 && this.jessibucaPlayer) {
  158. this.jessibucaPlayer.pause();
  159. } else if (this.livePlayerContext) {
  160. this.livePlayerContext.pause({
  161. success: () => {
  162. this.isPlaying = false;
  163. this.$emit('pause');
  164. }
  165. });
  166. }
  167. },
  168. // 截图
  169. screenshot() {
  170. if (this.isH5 && this.jessibucaPlayer) {
  171. return this.jessibucaPlayer.screenshot();
  172. } else if (this.livePlayerContext) {
  173. return new Promise((resolve, reject) => {
  174. this.livePlayerContext.snapshot({
  175. success: (res) => {
  176. resolve(res.tempImagePath);
  177. },
  178. fail: (err) => {
  179. reject(err);
  180. }
  181. });
  182. });
  183. }
  184. },
  185. // 静音
  186. mute() {
  187. this.isMuted = true;
  188. if (this.isH5 && this.jessibucaPlayer) {
  189. this.jessibucaPlayer.mute();
  190. }
  191. },
  192. // 取消静音
  193. cancelMute() {
  194. this.isMuted = false;
  195. if (this.isH5 && this.jessibucaPlayer) {
  196. this.jessibucaPlayer.cancelMute();
  197. }
  198. },
  199. // 全屏
  200. fullscreenSwich() {
  201. if (this.isH5 && this.jessibucaPlayer) {
  202. this.jessibucaPlayer.fullscreen();
  203. } else if (this.livePlayerContext) {
  204. if (this.isFullscreen) {
  205. this.livePlayerContext.exitFullScreen();
  206. } else {
  207. this.livePlayerContext.requestFullScreen({
  208. direction: 90
  209. });
  210. }
  211. }
  212. },
  213. // 重置大小
  214. resize() {
  215. if (this.isH5 && this.jessibucaPlayer) {
  216. this.jessibucaPlayer.resize();
  217. }
  218. },
  219. // 检查是否全屏
  220. isFullscreen() {
  221. return this.isFullscreen;
  222. },
  223. // live-player状态变化
  224. onStateChange(e) {
  225. console.log('播放器状态变化:', e.detail);
  226. const state = e.detail.code;
  227. if (state === 2003) { // 播放中
  228. this.isPlaying = true;
  229. this.$emit('play');
  230. } else if (state === 2004) { // 暂停
  231. this.isPlaying = false;
  232. this.$emit('pause');
  233. }
  234. },
  235. // live-player错误
  236. onError(e) {
  237. console.error('播放器错误:', e.detail);
  238. this.$emit('error', e.detail);
  239. },
  240. // 全屏状态变化
  241. onFullscreenChange(e) {
  242. this.isFullscreen = e.detail.fullScreen;
  243. this.$emit('fullscreenchange', this.isFullscreen);
  244. },
  245. // 销毁播放器
  246. destroyPlayer() {
  247. if (this.isH5 && this.jessibucaPlayer) {
  248. this.jessibucaPlayer.destroy();
  249. this.jessibucaPlayer = null;
  250. }
  251. }
  252. }
  253. }
  254. </script>
  255. <style>
  256. .video-player-container {
  257. width: 100%;
  258. height: 100%;
  259. background-color: #000;
  260. position: relative;
  261. }
  262. .h5-player, .mp-player, .jessibuca-container, .live-player {
  263. width: 100%;
  264. height: 100%;
  265. }
  266. .error-message {
  267. position: absolute;
  268. top: 50%;
  269. left: 50%;
  270. transform: translate(-50%, -50%);
  271. color: #fff;
  272. background-color: rgba(0, 0, 0, 0.5);
  273. padding: 10px 20px;
  274. border-radius: 4px;
  275. }
  276. </style>