use-play.ts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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 useFullScreen(video) {
  25. if (video.requestFullscreen) {
  26. console.log('requestFullscreen-1');
  27. video.requestFullscreen();
  28. } else if (video.mozRequestFullScreen) {
  29. console.log('mozRequestFullScreen-2');
  30. // Firefox
  31. video.mozRequestFullScreen();
  32. } else if (video.webkitRequestFullscreen) {
  33. console.log('webkitRequestFullscreen-3');
  34. // Chrome, Safari和Opera
  35. video.webkitRequestFullscreen();
  36. } else if (video.msRequestFullscreen) {
  37. console.log('msRequestFullscreen-4');
  38. // IE/Edge
  39. video.msRequestFullscreen();
  40. } else if (video.webkitEnterFullscreen) {
  41. console.log('webkitEnterFullscreen-4');
  42. // IOS
  43. video.webkitEnterFullscreen();
  44. } else {
  45. console.log('不支持全屏');
  46. }
  47. }
  48. export function useFlvPlay() {
  49. // const flvPlayer = ref<flvJs.Player>();
  50. const flvPlayer = ref<mpegts.Player>();
  51. const flvVideoEl = ref<HTMLVideoElement>();
  52. const cacheStore = usePiniaCacheStore();
  53. const appStore = useAppStore();
  54. const initRetryMax = 120;
  55. const retryMax = ref(initRetryMax);
  56. const retry = ref(0);
  57. const retryTimer = ref();
  58. const retrying = ref(false);
  59. const flvIsPlaying = ref(false);
  60. onMounted(() => {});
  61. onUnmounted(() => {
  62. destroyFlv();
  63. });
  64. function destroyFlv() {
  65. if (flvPlayer.value) {
  66. flvPlayer.value.destroy();
  67. flvPlayer.value = undefined;
  68. }
  69. flvVideoEl.value?.remove();
  70. clearInterval(retryTimer.value);
  71. retryMax.value = initRetryMax;
  72. }
  73. function setMuted(val) {
  74. if (flvVideoEl.value) {
  75. flvVideoEl.value.muted = val;
  76. }
  77. if (flvPlayer.value) {
  78. flvPlayer.value.muted = val;
  79. }
  80. }
  81. function setVolume(val: number) {
  82. if (flvVideoEl.value) {
  83. flvVideoEl.value.volume = val / 100;
  84. }
  85. if (flvPlayer.value) {
  86. flvPlayer.value.volume = val / 100;
  87. }
  88. }
  89. function setPlay(val: boolean) {
  90. if (val) {
  91. flvVideoEl.value?.play();
  92. flvPlayer.value?.play();
  93. } else {
  94. flvVideoEl.value?.pause();
  95. flvPlayer.value?.pause();
  96. }
  97. }
  98. watch(
  99. () => cacheStore.muted,
  100. (newVal) => {
  101. setMuted(newVal);
  102. }
  103. );
  104. watch(
  105. () => cacheStore.volume,
  106. (newVal) => {
  107. setVolume(newVal);
  108. }
  109. );
  110. watch(
  111. () => appStore.playing,
  112. (newVal) => {
  113. setPlay(newVal);
  114. }
  115. );
  116. function startFlvPlay(data: { flvurl: string }) {
  117. console.log('startFlvPlay', data.flvurl);
  118. return new Promise((resolve) => {
  119. function main() {
  120. destroyFlv();
  121. // mseLivePlayback,指示 HTTP MPEG2-TS/FLV 直播流是否可以在您的浏览器上运行。
  122. // msePlayback,与 相同mpegts.isSupported(),表示基本播放是否可以在您的浏览器上运行。
  123. if (
  124. mpegts.getFeatureList().mseLivePlayback &&
  125. mpegts.getFeatureList().msePlayback
  126. ) {
  127. flvPlayer.value = mpegts.createPlayer({
  128. type: 'flv', // could also be mpegts, m2ts, flv
  129. isLive: true,
  130. url: handlePlayUrl(data.flvurl),
  131. });
  132. const videoEl = createVideo({});
  133. videoEl.addEventListener('play', () => {
  134. console.log('flv-play');
  135. });
  136. videoEl.addEventListener('playing', () => {
  137. console.log('flv-playing');
  138. flvIsPlaying.value = true;
  139. retry.value = 0;
  140. setMuted(cacheStore.muted);
  141. setVolume(cacheStore.volume);
  142. flvVideoEl.value = videoEl;
  143. resolve('');
  144. });
  145. videoEl.addEventListener('loadedmetadata', () => {
  146. console.log('flv-loadedmetadata');
  147. });
  148. flvPlayer.value.attachMediaElement(videoEl);
  149. flvPlayer.value.load();
  150. flvPlayer.value.on(mpegts.Events.ERROR, () => {
  151. console.error('mpegts消息:mpegts.Events.ERROR');
  152. if (retry.value < retryMax.value && !retrying.value) {
  153. retrying.value = true;
  154. destroyFlv();
  155. retryTimer.value = setTimeout(() => {
  156. console.error(
  157. '播放flv错误,重新加载,剩余次数:',
  158. retryMax.value - retry.value
  159. );
  160. retry.value += 1;
  161. retrying.value = false;
  162. main();
  163. }, 1000);
  164. }
  165. });
  166. flvPlayer.value.on(mpegts.Events.MEDIA_INFO, (data) => {
  167. console.log('mpegts.Events.MEDIA_INFO', data);
  168. // appStore.videoFps = data?.fps?.toFixed(2);
  169. });
  170. flvPlayer.value.on(mpegts.Events.STATISTICS_INFO, (data) => {
  171. appStore.videoKBs = data?.speed?.toFixed(2);
  172. });
  173. try {
  174. console.log(`开始播放flv,muted:${cacheStore.muted}`);
  175. flvPlayer.value.play();
  176. } catch (error) {
  177. console.error('flv播放失败');
  178. console.log(error);
  179. }
  180. } else {
  181. console.error('不支持flv');
  182. }
  183. }
  184. main();
  185. });
  186. }
  187. return { flvPlayer, flvVideoEl, flvIsPlaying, startFlvPlay, destroyFlv };
  188. }
  189. export function useHlsPlay() {
  190. const hlsPlayer = ref<Player>();
  191. const hlsVideoEl = ref<HTMLVideoElement>();
  192. const cacheStore = usePiniaCacheStore();
  193. const appStore = useAppStore();
  194. const initRetryMax = 120;
  195. const retryMax = ref(initRetryMax);
  196. const retry = ref(0);
  197. const retryTimer = ref();
  198. const retrying = ref(false);
  199. const hlsIsPlaying = ref(false);
  200. onMounted(() => {});
  201. onUnmounted(() => {
  202. destroyHls();
  203. });
  204. function destroyHls() {
  205. if (hlsPlayer.value) {
  206. hlsPlayer.value.dispose();
  207. hlsPlayer.value = undefined;
  208. }
  209. hlsVideoEl.value?.remove();
  210. clearInterval(retryTimer.value);
  211. retryMax.value = initRetryMax;
  212. }
  213. function setMuted(val: boolean) {
  214. if (hlsVideoEl.value) {
  215. hlsVideoEl.value.muted = val;
  216. }
  217. if (hlsPlayer.value) {
  218. hlsPlayer.value.muted(val);
  219. }
  220. }
  221. function setVolume(val: number) {
  222. if (hlsVideoEl.value) {
  223. hlsVideoEl.value.volume = val / 100;
  224. }
  225. if (hlsPlayer.value) {
  226. hlsPlayer.value.volume(val / 100);
  227. }
  228. }
  229. function setPlay(val: boolean) {
  230. if (val) {
  231. hlsVideoEl.value?.play();
  232. hlsPlayer.value?.play();
  233. } else {
  234. hlsVideoEl.value?.pause();
  235. hlsPlayer.value?.pause();
  236. }
  237. }
  238. watch(
  239. () => cacheStore.muted,
  240. (newVal) => {
  241. setMuted(newVal);
  242. }
  243. );
  244. watch(
  245. () => cacheStore.volume,
  246. (newVal) => {
  247. setVolume(newVal);
  248. }
  249. );
  250. watch(
  251. () => appStore.playing,
  252. (newVal) => {
  253. setPlay(newVal);
  254. }
  255. );
  256. function startHlsPlay(data: { hlsurl: string }) {
  257. return new Promise((resolve) => {
  258. function main() {
  259. console.log('startHlsPlay', data.hlsurl);
  260. destroyHls();
  261. const videoEl = createVideo({
  262. muted: cacheStore.muted,
  263. autoplay: true,
  264. });
  265. hlsPlayer.value = videoJs(
  266. videoEl,
  267. {
  268. sources: [
  269. {
  270. src: handlePlayUrl(data.hlsurl),
  271. type: 'application/x-mpegURL',
  272. },
  273. ],
  274. },
  275. function () {
  276. try {
  277. // console.log(`开始播放hls,muted:${cacheStore.muted}`);
  278. hlsPlayer.value?.play();
  279. } catch (error) {
  280. console.error('hls播放失败');
  281. console.log(error);
  282. }
  283. }
  284. );
  285. hlsPlayer.value?.on('error', () => {
  286. console.log('hls-error');
  287. if (retry.value < retryMax.value && !retrying.value) {
  288. retrying.value = true;
  289. retryTimer.value = setTimeout(() => {
  290. console.error(
  291. '播放hls错误,重新加载,剩余次数:',
  292. retryMax.value - retry.value
  293. );
  294. retry.value += 1;
  295. retrying.value = false;
  296. main();
  297. }, 1000);
  298. }
  299. });
  300. hlsPlayer.value?.on('play', () => {
  301. console.log('hls-play');
  302. // console.log(hlsPlayer.value?.videoHeight()); // 获取到的是0!
  303. });
  304. hlsPlayer.value?.on('playing', () => {
  305. console.log('hls-playing');
  306. hlsIsPlaying.value = true;
  307. setMuted(cacheStore.muted);
  308. setVolume(cacheStore.volume);
  309. retry.value = 0;
  310. // console.log(hlsPlayer.value?.videoHeight()); // 获取到的是正确的!
  311. const childNodes = hlsPlayer.value?.el().childNodes;
  312. if (childNodes) {
  313. childNodes.forEach((item) => {
  314. if (item.nodeName.toLowerCase() === 'video') {
  315. // @ts-ignore
  316. hlsVideoEl.value = item;
  317. }
  318. });
  319. }
  320. resolve('');
  321. });
  322. hlsPlayer.value?.on('loadedmetadata', () => {
  323. console.log('hls-loadedmetadata');
  324. });
  325. }
  326. main();
  327. });
  328. }
  329. return { hlsPlayer, hlsVideoEl, hlsIsPlaying, startHlsPlay, destroyHls };
  330. }