use-play.ts 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import '@/assets/css/videojs.scss';
  2. import { getRandomString } from 'billd-utils';
  3. import md5 from 'crypto-js/md5';
  4. import mpegts from 'mpegts.js';
  5. import videoJs from 'video.js';
  6. import Player from 'video.js/dist/types/player';
  7. import { onMounted, onUnmounted, ref, watch } from 'vue';
  8. import { SRS_CB_URL_PARAMS } from '@/constant';
  9. import { useAppStore } from '@/store/app';
  10. import { usePiniaCacheStore } from '@/store/cache';
  11. import { useUserStore } from '@/store/user';
  12. import { createVideo } from '@/utils';
  13. export * as flvJs from 'flv.js';
  14. function handlePlayUrl(url: string) {
  15. const userStore = useUserStore();
  16. const userInfo = userStore.userInfo;
  17. const userToken = md5(userStore.token) as string;
  18. return !userInfo
  19. ? `${url}?${SRS_CB_URL_PARAMS.randomId}=${getRandomString(8)}`
  20. : `${url}?${SRS_CB_URL_PARAMS.userToken}=${userToken}&${
  21. SRS_CB_URL_PARAMS.userId
  22. }=${userInfo.id!}&${SRS_CB_URL_PARAMS.randomId}=${getRandomString(8)}`;
  23. }
  24. export function useFlvPlay() {
  25. // const flvPlayer = ref<flvJs.Player>();
  26. const flvPlayer = ref<mpegts.Player>();
  27. const flvVideoEl = ref<HTMLVideoElement>();
  28. const cacheStore = usePiniaCacheStore();
  29. const appStore = useAppStore();
  30. const initRetryMax = 120;
  31. const retryMax = ref(initRetryMax);
  32. const retry = ref(0);
  33. const retryTimer = ref();
  34. const retrying = ref(false);
  35. onMounted(() => {});
  36. onUnmounted(() => {
  37. destroyFlv();
  38. });
  39. function destroyFlv() {
  40. if (flvPlayer.value) {
  41. flvPlayer.value.destroy();
  42. flvPlayer.value = undefined;
  43. }
  44. flvVideoEl.value?.remove();
  45. clearInterval(retryTimer.value);
  46. retryMax.value = initRetryMax;
  47. }
  48. function setMuted(val) {
  49. if (flvVideoEl.value) {
  50. flvVideoEl.value.muted = val;
  51. }
  52. if (flvPlayer.value) {
  53. flvPlayer.value.muted = val;
  54. }
  55. }
  56. function setVolume(val: number) {
  57. if (flvVideoEl.value) {
  58. flvVideoEl.value.volume = val / 100;
  59. }
  60. if (flvPlayer.value) {
  61. flvPlayer.value.volume = val / 100;
  62. }
  63. }
  64. function setPlay(val: boolean) {
  65. if (val) {
  66. flvVideoEl.value?.play();
  67. flvPlayer.value?.play();
  68. } else {
  69. flvVideoEl.value?.pause();
  70. flvPlayer.value?.pause();
  71. }
  72. }
  73. watch(
  74. () => cacheStore.muted,
  75. (newVal) => {
  76. setMuted(newVal);
  77. }
  78. );
  79. watch(
  80. () => cacheStore.volume,
  81. (newVal) => {
  82. setVolume(newVal);
  83. }
  84. );
  85. watch(
  86. () => appStore.play,
  87. (newVal) => {
  88. setPlay(newVal);
  89. }
  90. );
  91. function startFlvPlay(data: { flvurl: string }) {
  92. console.log('startFlvPlay', data.flvurl);
  93. return new Promise((resolve) => {
  94. function main() {
  95. destroyFlv();
  96. if (mpegts.getFeatureList().mseLivePlayback && mpegts.isSupported()) {
  97. flvPlayer.value = mpegts.createPlayer({
  98. type: 'flv', // could also be mpegts, m2ts, flv
  99. isLive: true,
  100. url: handlePlayUrl(data.flvurl),
  101. });
  102. const videoEl = createVideo({});
  103. videoEl.addEventListener('play', () => {
  104. console.log('flv-play');
  105. });
  106. videoEl.addEventListener('playing', () => {
  107. console.log('flv-playing');
  108. retry.value = 0;
  109. setMuted(cacheStore.muted);
  110. setVolume(cacheStore.volume);
  111. flvVideoEl.value = videoEl;
  112. resolve('');
  113. });
  114. videoEl.addEventListener('loadedmetadata', () => {
  115. console.log('flv-loadedmetadata');
  116. });
  117. flvPlayer.value.attachMediaElement(videoEl);
  118. flvPlayer.value.load();
  119. flvPlayer.value.on(mpegts.Events.ERROR, () => {
  120. console.error('mpegts消息:mpegts.Events.ERROR');
  121. if (retry.value < retryMax.value && !retrying.value) {
  122. retrying.value = true;
  123. destroyFlv();
  124. retryTimer.value = setTimeout(() => {
  125. console.error(
  126. '播放flv错误,重新加载,剩余次数:',
  127. retryMax.value - retry.value
  128. );
  129. retry.value += 1;
  130. retrying.value = false;
  131. main();
  132. }, 1000);
  133. }
  134. });
  135. flvPlayer.value.on(mpegts.Events.MEDIA_INFO, () => {
  136. console.log('mpegts消息:mpegts.Events.MEDIA_INFO');
  137. });
  138. try {
  139. console.log(`开始播放flv,muted:${cacheStore.muted}`);
  140. flvPlayer.value.play();
  141. } catch (err) {
  142. console.error('flv播放失败');
  143. console.log(err);
  144. }
  145. } else {
  146. console.error('不支持flv');
  147. }
  148. }
  149. main();
  150. });
  151. }
  152. return { flvPlayer, flvVideoEl, startFlvPlay, destroyFlv };
  153. }
  154. export function useHlsPlay() {
  155. const hlsPlayer = ref<Player>();
  156. const hlsVideoEl = ref<HTMLVideoElement>();
  157. const cacheStore = usePiniaCacheStore();
  158. const appStore = useAppStore();
  159. const initRetryMax = 120;
  160. const retryMax = ref(initRetryMax);
  161. const retry = ref(0);
  162. const retryTimer = ref();
  163. const retrying = ref(false);
  164. onMounted(() => {});
  165. onUnmounted(() => {
  166. destroyHls();
  167. });
  168. function destroyHls() {
  169. if (hlsPlayer.value) {
  170. hlsPlayer.value.dispose();
  171. hlsPlayer.value = undefined;
  172. }
  173. hlsVideoEl.value?.remove();
  174. clearInterval(retryTimer.value);
  175. retryMax.value = initRetryMax;
  176. }
  177. function setMuted(val: boolean) {
  178. if (hlsVideoEl.value) {
  179. hlsVideoEl.value.muted = val;
  180. }
  181. if (hlsPlayer.value) {
  182. hlsPlayer.value.muted(val);
  183. }
  184. }
  185. function setVolume(val: number) {
  186. if (hlsVideoEl.value) {
  187. hlsVideoEl.value.volume = val / 100;
  188. }
  189. if (hlsPlayer.value) {
  190. hlsPlayer.value.volume(val / 100);
  191. }
  192. }
  193. function setPlay(val: boolean) {
  194. if (val) {
  195. hlsVideoEl.value?.play();
  196. hlsPlayer.value?.play();
  197. } else {
  198. hlsVideoEl.value?.pause();
  199. hlsPlayer.value?.pause();
  200. }
  201. }
  202. watch(
  203. () => cacheStore.muted,
  204. (newVal) => {
  205. setMuted(newVal);
  206. }
  207. );
  208. watch(
  209. () => cacheStore.volume,
  210. (newVal) => {
  211. setVolume(newVal);
  212. }
  213. );
  214. watch(
  215. () => appStore.play,
  216. (newVal) => {
  217. setPlay(newVal);
  218. }
  219. );
  220. function startHlsPlay(data: { hlsurl: string }) {
  221. return new Promise((resolve) => {
  222. function main() {
  223. console.log('startHlsPlay', data.hlsurl);
  224. destroyHls();
  225. const videoEl = createVideo({
  226. muted: cacheStore.muted,
  227. autoplay: true,
  228. });
  229. hlsPlayer.value = videoJs(
  230. videoEl,
  231. {
  232. sources: [
  233. {
  234. src: handlePlayUrl(data.hlsurl),
  235. type: 'application/x-mpegURL',
  236. },
  237. ],
  238. },
  239. function () {
  240. try {
  241. // console.log(`开始播放hls,muted:${cacheStore.muted}`);
  242. hlsPlayer.value?.play();
  243. } catch (err) {
  244. console.error('hls播放失败');
  245. console.log(err);
  246. }
  247. }
  248. );
  249. hlsPlayer.value?.on('error', () => {
  250. console.log('hls-error');
  251. if (retry.value < retryMax.value && !retrying.value) {
  252. retrying.value = true;
  253. retryTimer.value = setTimeout(() => {
  254. console.error(
  255. '播放hls错误,重新加载,剩余次数:',
  256. retryMax.value - retry.value
  257. );
  258. retry.value += 1;
  259. retrying.value = false;
  260. main();
  261. }, 1000);
  262. }
  263. });
  264. hlsPlayer.value?.on('play', () => {
  265. console.log('hls-play');
  266. // console.log(hlsPlayer.value?.videoHeight()); // 获取到的是0!
  267. });
  268. hlsPlayer.value?.on('playing', () => {
  269. console.log('hls-playing');
  270. setMuted(cacheStore.muted);
  271. setVolume(cacheStore.volume);
  272. retry.value = 0;
  273. // console.log(hlsPlayer.value?.videoHeight()); // 获取到的是正确的!
  274. const childNodes = hlsPlayer.value?.el().childNodes;
  275. if (childNodes) {
  276. childNodes.forEach((item) => {
  277. if (item.nodeName.toLowerCase() === 'video') {
  278. // @ts-ignore
  279. hlsVideoEl.value = item;
  280. }
  281. });
  282. }
  283. resolve('');
  284. });
  285. hlsPlayer.value?.on('loadedmetadata', () => {
  286. console.log('hls-loadedmetadata');
  287. });
  288. }
  289. main();
  290. });
  291. }
  292. return { hlsPlayer, hlsVideoEl, startHlsPlay, destroyHls };
  293. }