mpegts-vue3 0.1.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/dist/index.cjs +237 -0
- package/dist/index.d.cts +44 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +213 -0
- package/package.json +55 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let vue = require("vue");
|
|
25
|
+
let mpegts_js = require("mpegts.js");
|
|
26
|
+
mpegts_js = __toESM(mpegts_js);
|
|
27
|
+
//#region src/components/MpegtsPlayer.vue
|
|
28
|
+
const _hoisted_1 = { class: "relative w-full h-full bg-black rounded-lg overflow-hidden" };
|
|
29
|
+
const _hoisted_2 = {
|
|
30
|
+
key: 0,
|
|
31
|
+
class: "absolute inset-0 flex flex-col items-center justify-center bg-gray-900/90"
|
|
32
|
+
};
|
|
33
|
+
const _hoisted_3 = {
|
|
34
|
+
key: 1,
|
|
35
|
+
class: "absolute inset-0 flex items-center justify-center bg-black/60"
|
|
36
|
+
};
|
|
37
|
+
const _hoisted_4 = {
|
|
38
|
+
key: 2,
|
|
39
|
+
class: "absolute inset-0 flex items-center justify-center bg-black/60"
|
|
40
|
+
};
|
|
41
|
+
const _sfc_main = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
42
|
+
__name: "MpegtsPlayer",
|
|
43
|
+
props: {
|
|
44
|
+
url: {},
|
|
45
|
+
autoplay: {
|
|
46
|
+
type: Boolean,
|
|
47
|
+
default: true
|
|
48
|
+
},
|
|
49
|
+
isLive: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: true
|
|
52
|
+
},
|
|
53
|
+
muted: {
|
|
54
|
+
type: Boolean,
|
|
55
|
+
default: true
|
|
56
|
+
},
|
|
57
|
+
type: { default: "mse" },
|
|
58
|
+
cors: { type: Boolean },
|
|
59
|
+
withCredentials: { type: Boolean },
|
|
60
|
+
hasAudio: { type: Boolean },
|
|
61
|
+
hasVideo: { type: Boolean },
|
|
62
|
+
duration: {},
|
|
63
|
+
filesize: {},
|
|
64
|
+
config: { default: () => ({}) }
|
|
65
|
+
},
|
|
66
|
+
emits: ["error", "status"],
|
|
67
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
68
|
+
const DEFAULT_CONFIG = {
|
|
69
|
+
enableStashBuffer: false,
|
|
70
|
+
liveBufferLatencyChasing: true,
|
|
71
|
+
liveBufferLatencyChasingOnPaused: true,
|
|
72
|
+
liveBufferLatencyMaxLatency: 1.5,
|
|
73
|
+
liveBufferLatencyMinRemain: .3,
|
|
74
|
+
liveSync: true,
|
|
75
|
+
liveSyncMaxLatency: 1.2,
|
|
76
|
+
liveSyncTargetLatency: .5,
|
|
77
|
+
autoCleanupSourceBuffer: true,
|
|
78
|
+
autoCleanupMaxBackwardDuration: 30,
|
|
79
|
+
autoCleanupMinBackwardDuration: 10,
|
|
80
|
+
fixAudioTimestampGap: true
|
|
81
|
+
};
|
|
82
|
+
const props = __props;
|
|
83
|
+
const emit = __emit;
|
|
84
|
+
const videoRef = (0, vue.ref)();
|
|
85
|
+
const status = (0, vue.ref)("nosignal");
|
|
86
|
+
let player = null;
|
|
87
|
+
function destroyPlayer() {
|
|
88
|
+
if (!player) return;
|
|
89
|
+
status.value = "destroying";
|
|
90
|
+
try {
|
|
91
|
+
player.pause();
|
|
92
|
+
player.unload();
|
|
93
|
+
player.detachMediaElement();
|
|
94
|
+
player.destroy();
|
|
95
|
+
} catch {}
|
|
96
|
+
player = null;
|
|
97
|
+
status.value = "nosignal";
|
|
98
|
+
}
|
|
99
|
+
function play() {
|
|
100
|
+
if (!player) return;
|
|
101
|
+
videoRef.value.muted = props.muted;
|
|
102
|
+
const result = player.play();
|
|
103
|
+
if (result instanceof Promise) result.then(() => {
|
|
104
|
+
status.value = "playing";
|
|
105
|
+
emit("status", "playing");
|
|
106
|
+
}).catch(() => {
|
|
107
|
+
status.value = "stopped";
|
|
108
|
+
emit("status", "stopped");
|
|
109
|
+
});
|
|
110
|
+
else {
|
|
111
|
+
status.value = "playing";
|
|
112
|
+
emit("status", "playing");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function pause() {
|
|
116
|
+
if (!player) return;
|
|
117
|
+
player.pause();
|
|
118
|
+
status.value = "stopped";
|
|
119
|
+
emit("status", "stopped");
|
|
120
|
+
}
|
|
121
|
+
__expose({
|
|
122
|
+
play,
|
|
123
|
+
pause
|
|
124
|
+
});
|
|
125
|
+
function buildMediaDataSource() {
|
|
126
|
+
const source = {
|
|
127
|
+
type: props.type ?? "mse",
|
|
128
|
+
isLive: props.isLive,
|
|
129
|
+
url: props.url
|
|
130
|
+
};
|
|
131
|
+
if (props.cors !== void 0) source.cors = props.cors;
|
|
132
|
+
if (props.withCredentials !== void 0) source.withCredentials = props.withCredentials;
|
|
133
|
+
if (props.hasAudio !== void 0) source.hasAudio = props.hasAudio;
|
|
134
|
+
if (props.hasVideo !== void 0) source.hasVideo = props.hasVideo;
|
|
135
|
+
if (props.duration !== void 0) source.duration = props.duration;
|
|
136
|
+
if (props.filesize !== void 0) source.filesize = props.filesize;
|
|
137
|
+
return source;
|
|
138
|
+
}
|
|
139
|
+
function createPlayer() {
|
|
140
|
+
destroyPlayer();
|
|
141
|
+
if (!props.url || !videoRef.value) return;
|
|
142
|
+
if (!mpegts_js.default.isSupported()) {
|
|
143
|
+
status.value = "error";
|
|
144
|
+
emit("status", "error");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
status.value = "connecting";
|
|
148
|
+
emit("status", "connecting");
|
|
149
|
+
const mergedConfig = {
|
|
150
|
+
...DEFAULT_CONFIG,
|
|
151
|
+
...props.config
|
|
152
|
+
};
|
|
153
|
+
const mediaSource = buildMediaDataSource();
|
|
154
|
+
player = mpegts_js.default.createPlayer(mediaSource, mergedConfig);
|
|
155
|
+
player.attachMediaElement(videoRef.value);
|
|
156
|
+
player.on(mpegts_js.default.Events.ERROR, (errorType, errorDetail, errorInfo) => {
|
|
157
|
+
status.value = "error";
|
|
158
|
+
emit("status", "error");
|
|
159
|
+
emit("error", errorType, errorDetail, errorInfo);
|
|
160
|
+
});
|
|
161
|
+
player.load();
|
|
162
|
+
if (props.autoplay) {
|
|
163
|
+
videoRef.value.muted = props.muted;
|
|
164
|
+
const result = player.play();
|
|
165
|
+
if (result instanceof Promise) result.then(() => {
|
|
166
|
+
status.value = "playing";
|
|
167
|
+
emit("status", "playing");
|
|
168
|
+
}).catch(() => {
|
|
169
|
+
status.value = "stopped";
|
|
170
|
+
emit("status", "stopped");
|
|
171
|
+
});
|
|
172
|
+
else {
|
|
173
|
+
status.value = "playing";
|
|
174
|
+
emit("status", "playing");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
(0, vue.watch)(() => props.url, (newUrl) => {
|
|
179
|
+
if (newUrl) createPlayer();
|
|
180
|
+
else {
|
|
181
|
+
destroyPlayer();
|
|
182
|
+
status.value = "nosignal";
|
|
183
|
+
emit("status", "nosignal");
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
(0, vue.watch)(() => props.config, () => {
|
|
187
|
+
if (props.url) createPlayer();
|
|
188
|
+
}, { deep: true });
|
|
189
|
+
(0, vue.watch)(() => [
|
|
190
|
+
props.type,
|
|
191
|
+
props.isLive,
|
|
192
|
+
props.cors,
|
|
193
|
+
props.withCredentials,
|
|
194
|
+
props.hasAudio,
|
|
195
|
+
props.hasVideo,
|
|
196
|
+
props.duration,
|
|
197
|
+
props.filesize
|
|
198
|
+
], () => {
|
|
199
|
+
if (props.url) createPlayer();
|
|
200
|
+
});
|
|
201
|
+
(0, vue.onMounted)(() => {
|
|
202
|
+
if (props.url) createPlayer();
|
|
203
|
+
});
|
|
204
|
+
(0, vue.watch)(() => props.muted, (val) => {
|
|
205
|
+
if (videoRef.value) videoRef.value.muted = val;
|
|
206
|
+
});
|
|
207
|
+
(0, vue.onUnmounted)(() => {
|
|
208
|
+
destroyPlayer();
|
|
209
|
+
});
|
|
210
|
+
return (_ctx, _cache) => {
|
|
211
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1, [
|
|
212
|
+
(0, vue.createElementVNode)("video", {
|
|
213
|
+
ref_key: "videoRef",
|
|
214
|
+
ref: videoRef,
|
|
215
|
+
class: "absolute inset-0 w-full h-full object-contain",
|
|
216
|
+
onClick: _cache[0] || (_cache[0] = (0, vue.withModifiers)(() => {}, ["prevent"])),
|
|
217
|
+
onContextmenu: _cache[1] || (_cache[1] = (0, vue.withModifiers)(() => {}, ["prevent"]))
|
|
218
|
+
}, null, 544),
|
|
219
|
+
status.value === "nosignal" || !__props.url ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_2, [..._cache[2] || (_cache[2] = [(0, vue.createStaticVNode)("<div class=\"mb-3 flex items-center gap-2 text-gray-400\"><svg class=\"size-10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" viewBox=\"0 0 24 24\"><path d=\"m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path><path d=\"M18 12 6 12M18 8 6 8M18 16l-12 0\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></svg></div><span class=\"text-sm font-medium text-gray-400 tracking-wider uppercase\"> No Signal </span>", 2)])])) : (0, vue.createCommentVNode)("v-if", true),
|
|
220
|
+
status.value === "connecting" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_3, [..._cache[3] || (_cache[3] = [(0, vue.createElementVNode)("div", { class: "flex flex-col items-center gap-3" }, [(0, vue.createElementVNode)("div", { class: "size-8 rounded-full border-2 border-blue-500 border-t-transparent animate-spin" }), (0, vue.createElementVNode)("span", { class: "text-sm text-gray-300" }, "Connecting...")], -1)])])) : (0, vue.createCommentVNode)("v-if", true),
|
|
221
|
+
status.value === "error" && __props.url ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4, [..._cache[4] || (_cache[4] = [(0, vue.createElementVNode)("div", { class: "flex flex-col items-center gap-2" }, [(0, vue.createElementVNode)("svg", {
|
|
222
|
+
class: "size-8 text-red-400",
|
|
223
|
+
fill: "none",
|
|
224
|
+
stroke: "currentColor",
|
|
225
|
+
"stroke-width": "1.5",
|
|
226
|
+
viewBox: "0 0 24 24"
|
|
227
|
+
}, [(0, vue.createElementVNode)("path", {
|
|
228
|
+
d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z",
|
|
229
|
+
"stroke-linecap": "round",
|
|
230
|
+
"stroke-linejoin": "round"
|
|
231
|
+
})]), (0, vue.createElementVNode)("span", { class: "text-sm text-red-400" }, "Connection Failed")], -1)])])) : (0, vue.createCommentVNode)("v-if", true)
|
|
232
|
+
]);
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
//#endregion
|
|
237
|
+
exports.MpegtsPlayer = _sfc_main;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as vue0 from "vue";
|
|
2
|
+
import Mpegts from "mpegts.js";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
type MediaDataSource = Mpegts.MediaDataSource;
|
|
6
|
+
type MediaSegment = Mpegts.MediaSegment;
|
|
7
|
+
type MpegtsConfig = Mpegts.Config;
|
|
8
|
+
type PlayerStatus = 'connecting' | 'destroying' | 'error' | 'nosignal' | 'playing' | 'stopped';
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/components/MpegtsPlayer.vue.d.ts
|
|
11
|
+
interface Props {
|
|
12
|
+
url: string;
|
|
13
|
+
autoplay?: boolean;
|
|
14
|
+
isLive?: boolean;
|
|
15
|
+
muted?: boolean;
|
|
16
|
+
type?: string;
|
|
17
|
+
cors?: boolean;
|
|
18
|
+
withCredentials?: boolean;
|
|
19
|
+
hasAudio?: boolean;
|
|
20
|
+
hasVideo?: boolean;
|
|
21
|
+
duration?: number;
|
|
22
|
+
filesize?: number;
|
|
23
|
+
config?: Partial<MpegtsConfig>;
|
|
24
|
+
}
|
|
25
|
+
declare function play(): void;
|
|
26
|
+
declare function pause(): void;
|
|
27
|
+
declare const _default: vue0.DefineComponent<Props, {
|
|
28
|
+
play: typeof play;
|
|
29
|
+
pause: typeof pause;
|
|
30
|
+
}, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {
|
|
31
|
+
error: (errorType: string, errorDetail: string, errorInfo: any) => any;
|
|
32
|
+
status: (status: PlayerStatus) => any;
|
|
33
|
+
}, string, vue0.PublicProps, Readonly<Props> & Readonly<{
|
|
34
|
+
onError?: ((errorType: string, errorDetail: string, errorInfo: any) => any) | undefined;
|
|
35
|
+
onStatus?: ((status: PlayerStatus) => any) | undefined;
|
|
36
|
+
}>, {
|
|
37
|
+
autoplay: boolean;
|
|
38
|
+
isLive: boolean;
|
|
39
|
+
muted: boolean;
|
|
40
|
+
type: string;
|
|
41
|
+
config: Partial<MpegtsConfig>;
|
|
42
|
+
}, {}, {}, {}, string, vue0.ComponentProvideOptions, false, {}, any>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { type MediaDataSource, type MediaSegment, type MpegtsConfig, _default as MpegtsPlayer, type PlayerStatus };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as vue0 from "vue";
|
|
2
|
+
import Mpegts from "mpegts.js";
|
|
3
|
+
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
type MediaDataSource = Mpegts.MediaDataSource;
|
|
6
|
+
type MediaSegment = Mpegts.MediaSegment;
|
|
7
|
+
type MpegtsConfig = Mpegts.Config;
|
|
8
|
+
type PlayerStatus = 'connecting' | 'destroying' | 'error' | 'nosignal' | 'playing' | 'stopped';
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/components/MpegtsPlayer.vue.d.ts
|
|
11
|
+
interface Props {
|
|
12
|
+
url: string;
|
|
13
|
+
autoplay?: boolean;
|
|
14
|
+
isLive?: boolean;
|
|
15
|
+
muted?: boolean;
|
|
16
|
+
type?: string;
|
|
17
|
+
cors?: boolean;
|
|
18
|
+
withCredentials?: boolean;
|
|
19
|
+
hasAudio?: boolean;
|
|
20
|
+
hasVideo?: boolean;
|
|
21
|
+
duration?: number;
|
|
22
|
+
filesize?: number;
|
|
23
|
+
config?: Partial<MpegtsConfig>;
|
|
24
|
+
}
|
|
25
|
+
declare function play(): void;
|
|
26
|
+
declare function pause(): void;
|
|
27
|
+
declare const _default: vue0.DefineComponent<Props, {
|
|
28
|
+
play: typeof play;
|
|
29
|
+
pause: typeof pause;
|
|
30
|
+
}, {}, {}, {}, vue0.ComponentOptionsMixin, vue0.ComponentOptionsMixin, {
|
|
31
|
+
error: (errorType: string, errorDetail: string, errorInfo: any) => any;
|
|
32
|
+
status: (status: PlayerStatus) => any;
|
|
33
|
+
}, string, vue0.PublicProps, Readonly<Props> & Readonly<{
|
|
34
|
+
onError?: ((errorType: string, errorDetail: string, errorInfo: any) => any) | undefined;
|
|
35
|
+
onStatus?: ((status: PlayerStatus) => any) | undefined;
|
|
36
|
+
}>, {
|
|
37
|
+
autoplay: boolean;
|
|
38
|
+
isLive: boolean;
|
|
39
|
+
muted: boolean;
|
|
40
|
+
type: string;
|
|
41
|
+
config: Partial<MpegtsConfig>;
|
|
42
|
+
}, {}, {}, {}, string, vue0.ComponentProvideOptions, false, {}, any>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { type MediaDataSource, type MediaSegment, type MpegtsConfig, _default as MpegtsPlayer, type PlayerStatus };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { createCommentVNode, createElementBlock, createElementVNode, createStaticVNode, defineComponent, onMounted, onUnmounted, openBlock, ref, watch, withModifiers } from "vue";
|
|
2
|
+
import Mpegts from "mpegts.js";
|
|
3
|
+
//#region src/components/MpegtsPlayer.vue
|
|
4
|
+
const _hoisted_1 = { class: "relative w-full h-full bg-black rounded-lg overflow-hidden" };
|
|
5
|
+
const _hoisted_2 = {
|
|
6
|
+
key: 0,
|
|
7
|
+
class: "absolute inset-0 flex flex-col items-center justify-center bg-gray-900/90"
|
|
8
|
+
};
|
|
9
|
+
const _hoisted_3 = {
|
|
10
|
+
key: 1,
|
|
11
|
+
class: "absolute inset-0 flex items-center justify-center bg-black/60"
|
|
12
|
+
};
|
|
13
|
+
const _hoisted_4 = {
|
|
14
|
+
key: 2,
|
|
15
|
+
class: "absolute inset-0 flex items-center justify-center bg-black/60"
|
|
16
|
+
};
|
|
17
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
18
|
+
__name: "MpegtsPlayer",
|
|
19
|
+
props: {
|
|
20
|
+
url: {},
|
|
21
|
+
autoplay: {
|
|
22
|
+
type: Boolean,
|
|
23
|
+
default: true
|
|
24
|
+
},
|
|
25
|
+
isLive: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: true
|
|
28
|
+
},
|
|
29
|
+
muted: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: true
|
|
32
|
+
},
|
|
33
|
+
type: { default: "mse" },
|
|
34
|
+
cors: { type: Boolean },
|
|
35
|
+
withCredentials: { type: Boolean },
|
|
36
|
+
hasAudio: { type: Boolean },
|
|
37
|
+
hasVideo: { type: Boolean },
|
|
38
|
+
duration: {},
|
|
39
|
+
filesize: {},
|
|
40
|
+
config: { default: () => ({}) }
|
|
41
|
+
},
|
|
42
|
+
emits: ["error", "status"],
|
|
43
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
44
|
+
const DEFAULT_CONFIG = {
|
|
45
|
+
enableStashBuffer: false,
|
|
46
|
+
liveBufferLatencyChasing: true,
|
|
47
|
+
liveBufferLatencyChasingOnPaused: true,
|
|
48
|
+
liveBufferLatencyMaxLatency: 1.5,
|
|
49
|
+
liveBufferLatencyMinRemain: .3,
|
|
50
|
+
liveSync: true,
|
|
51
|
+
liveSyncMaxLatency: 1.2,
|
|
52
|
+
liveSyncTargetLatency: .5,
|
|
53
|
+
autoCleanupSourceBuffer: true,
|
|
54
|
+
autoCleanupMaxBackwardDuration: 30,
|
|
55
|
+
autoCleanupMinBackwardDuration: 10,
|
|
56
|
+
fixAudioTimestampGap: true
|
|
57
|
+
};
|
|
58
|
+
const props = __props;
|
|
59
|
+
const emit = __emit;
|
|
60
|
+
const videoRef = ref();
|
|
61
|
+
const status = ref("nosignal");
|
|
62
|
+
let player = null;
|
|
63
|
+
function destroyPlayer() {
|
|
64
|
+
if (!player) return;
|
|
65
|
+
status.value = "destroying";
|
|
66
|
+
try {
|
|
67
|
+
player.pause();
|
|
68
|
+
player.unload();
|
|
69
|
+
player.detachMediaElement();
|
|
70
|
+
player.destroy();
|
|
71
|
+
} catch {}
|
|
72
|
+
player = null;
|
|
73
|
+
status.value = "nosignal";
|
|
74
|
+
}
|
|
75
|
+
function play() {
|
|
76
|
+
if (!player) return;
|
|
77
|
+
videoRef.value.muted = props.muted;
|
|
78
|
+
const result = player.play();
|
|
79
|
+
if (result instanceof Promise) result.then(() => {
|
|
80
|
+
status.value = "playing";
|
|
81
|
+
emit("status", "playing");
|
|
82
|
+
}).catch(() => {
|
|
83
|
+
status.value = "stopped";
|
|
84
|
+
emit("status", "stopped");
|
|
85
|
+
});
|
|
86
|
+
else {
|
|
87
|
+
status.value = "playing";
|
|
88
|
+
emit("status", "playing");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function pause() {
|
|
92
|
+
if (!player) return;
|
|
93
|
+
player.pause();
|
|
94
|
+
status.value = "stopped";
|
|
95
|
+
emit("status", "stopped");
|
|
96
|
+
}
|
|
97
|
+
__expose({
|
|
98
|
+
play,
|
|
99
|
+
pause
|
|
100
|
+
});
|
|
101
|
+
function buildMediaDataSource() {
|
|
102
|
+
const source = {
|
|
103
|
+
type: props.type ?? "mse",
|
|
104
|
+
isLive: props.isLive,
|
|
105
|
+
url: props.url
|
|
106
|
+
};
|
|
107
|
+
if (props.cors !== void 0) source.cors = props.cors;
|
|
108
|
+
if (props.withCredentials !== void 0) source.withCredentials = props.withCredentials;
|
|
109
|
+
if (props.hasAudio !== void 0) source.hasAudio = props.hasAudio;
|
|
110
|
+
if (props.hasVideo !== void 0) source.hasVideo = props.hasVideo;
|
|
111
|
+
if (props.duration !== void 0) source.duration = props.duration;
|
|
112
|
+
if (props.filesize !== void 0) source.filesize = props.filesize;
|
|
113
|
+
return source;
|
|
114
|
+
}
|
|
115
|
+
function createPlayer() {
|
|
116
|
+
destroyPlayer();
|
|
117
|
+
if (!props.url || !videoRef.value) return;
|
|
118
|
+
if (!Mpegts.isSupported()) {
|
|
119
|
+
status.value = "error";
|
|
120
|
+
emit("status", "error");
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
status.value = "connecting";
|
|
124
|
+
emit("status", "connecting");
|
|
125
|
+
const mergedConfig = {
|
|
126
|
+
...DEFAULT_CONFIG,
|
|
127
|
+
...props.config
|
|
128
|
+
};
|
|
129
|
+
const mediaSource = buildMediaDataSource();
|
|
130
|
+
player = Mpegts.createPlayer(mediaSource, mergedConfig);
|
|
131
|
+
player.attachMediaElement(videoRef.value);
|
|
132
|
+
player.on(Mpegts.Events.ERROR, (errorType, errorDetail, errorInfo) => {
|
|
133
|
+
status.value = "error";
|
|
134
|
+
emit("status", "error");
|
|
135
|
+
emit("error", errorType, errorDetail, errorInfo);
|
|
136
|
+
});
|
|
137
|
+
player.load();
|
|
138
|
+
if (props.autoplay) {
|
|
139
|
+
videoRef.value.muted = props.muted;
|
|
140
|
+
const result = player.play();
|
|
141
|
+
if (result instanceof Promise) result.then(() => {
|
|
142
|
+
status.value = "playing";
|
|
143
|
+
emit("status", "playing");
|
|
144
|
+
}).catch(() => {
|
|
145
|
+
status.value = "stopped";
|
|
146
|
+
emit("status", "stopped");
|
|
147
|
+
});
|
|
148
|
+
else {
|
|
149
|
+
status.value = "playing";
|
|
150
|
+
emit("status", "playing");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
watch(() => props.url, (newUrl) => {
|
|
155
|
+
if (newUrl) createPlayer();
|
|
156
|
+
else {
|
|
157
|
+
destroyPlayer();
|
|
158
|
+
status.value = "nosignal";
|
|
159
|
+
emit("status", "nosignal");
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
watch(() => props.config, () => {
|
|
163
|
+
if (props.url) createPlayer();
|
|
164
|
+
}, { deep: true });
|
|
165
|
+
watch(() => [
|
|
166
|
+
props.type,
|
|
167
|
+
props.isLive,
|
|
168
|
+
props.cors,
|
|
169
|
+
props.withCredentials,
|
|
170
|
+
props.hasAudio,
|
|
171
|
+
props.hasVideo,
|
|
172
|
+
props.duration,
|
|
173
|
+
props.filesize
|
|
174
|
+
], () => {
|
|
175
|
+
if (props.url) createPlayer();
|
|
176
|
+
});
|
|
177
|
+
onMounted(() => {
|
|
178
|
+
if (props.url) createPlayer();
|
|
179
|
+
});
|
|
180
|
+
watch(() => props.muted, (val) => {
|
|
181
|
+
if (videoRef.value) videoRef.value.muted = val;
|
|
182
|
+
});
|
|
183
|
+
onUnmounted(() => {
|
|
184
|
+
destroyPlayer();
|
|
185
|
+
});
|
|
186
|
+
return (_ctx, _cache) => {
|
|
187
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
188
|
+
createElementVNode("video", {
|
|
189
|
+
ref_key: "videoRef",
|
|
190
|
+
ref: videoRef,
|
|
191
|
+
class: "absolute inset-0 w-full h-full object-contain",
|
|
192
|
+
onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"])),
|
|
193
|
+
onContextmenu: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"]))
|
|
194
|
+
}, null, 544),
|
|
195
|
+
status.value === "nosignal" || !__props.url ? (openBlock(), createElementBlock("div", _hoisted_2, [..._cache[2] || (_cache[2] = [createStaticVNode("<div class=\"mb-3 flex items-center gap-2 text-gray-400\"><svg class=\"size-10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" viewBox=\"0 0 24 24\"><path d=\"m15.75 10.5 4.72-4.72a.75.75 0 0 1 1.28.53v11.38a.75.75 0 0 1-1.28.53l-4.72-4.72M4.5 18.75h9a2.25 2.25 0 0 0 2.25-2.25v-9a2.25 2.25 0 0 0-2.25-2.25h-9A2.25 2.25 0 0 0 2.25 7.5v9a2.25 2.25 0 0 0 2.25 2.25Z\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path><path d=\"M18 12 6 12M18 8 6 8M18 16l-12 0\" stroke-linecap=\"round\" stroke-linejoin=\"round\"></path></svg></div><span class=\"text-sm font-medium text-gray-400 tracking-wider uppercase\"> No Signal </span>", 2)])])) : createCommentVNode("v-if", true),
|
|
196
|
+
status.value === "connecting" ? (openBlock(), createElementBlock("div", _hoisted_3, [..._cache[3] || (_cache[3] = [createElementVNode("div", { class: "flex flex-col items-center gap-3" }, [createElementVNode("div", { class: "size-8 rounded-full border-2 border-blue-500 border-t-transparent animate-spin" }), createElementVNode("span", { class: "text-sm text-gray-300" }, "Connecting...")], -1)])])) : createCommentVNode("v-if", true),
|
|
197
|
+
status.value === "error" && __props.url ? (openBlock(), createElementBlock("div", _hoisted_4, [..._cache[4] || (_cache[4] = [createElementVNode("div", { class: "flex flex-col items-center gap-2" }, [createElementVNode("svg", {
|
|
198
|
+
class: "size-8 text-red-400",
|
|
199
|
+
fill: "none",
|
|
200
|
+
stroke: "currentColor",
|
|
201
|
+
"stroke-width": "1.5",
|
|
202
|
+
viewBox: "0 0 24 24"
|
|
203
|
+
}, [createElementVNode("path", {
|
|
204
|
+
d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z",
|
|
205
|
+
"stroke-linecap": "round",
|
|
206
|
+
"stroke-linejoin": "round"
|
|
207
|
+
})]), createElementVNode("span", { class: "text-sm text-red-400" }, "Connection Failed")], -1)])])) : createCommentVNode("v-if", true)
|
|
208
|
+
]);
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
//#endregion
|
|
213
|
+
export { _sfc_main as MpegtsPlayer };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mpegts-vue3",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Vue 3 component for mpegts.js video streaming player",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"vue",
|
|
8
|
+
"vue3",
|
|
9
|
+
"mpegts",
|
|
10
|
+
"flv",
|
|
11
|
+
"live-streaming",
|
|
12
|
+
"player"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/huangzida/mpegts-vue3"
|
|
19
|
+
},
|
|
20
|
+
"main": "./dist/index.cjs",
|
|
21
|
+
"module": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"require": {
|
|
30
|
+
"types": "./dist/index.d.cts",
|
|
31
|
+
"default": "./dist/index.cjs"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsdown && node -e \"const fs=require('fs'),d='dist';fs.readdirSync(d).filter(f=>f.startsWith('index-')&&f.endsWith('.d.ts')).forEach(f=>fs.renameSync(d+'/'+f,d+'/index.d.ts'));fs.readdirSync(d).filter(f=>f.startsWith('index-')&&f.endsWith('.d.cts')).forEach(f=>fs.renameSync(d+'/'+f,d+'/index.d.cts'))\"",
|
|
41
|
+
"dev": "tsdown --watch"
|
|
42
|
+
},
|
|
43
|
+
"peerDependencies": {
|
|
44
|
+
"mpegts.js": "^1.8.0",
|
|
45
|
+
"vue": "^3.4.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"mpegts.js": "^1.8.0",
|
|
49
|
+
"tsdown": "^0.12.0",
|
|
50
|
+
"typescript": "^5.8.0",
|
|
51
|
+
"unplugin-vue": "^7.1.1",
|
|
52
|
+
"vue": "^3.5.0",
|
|
53
|
+
"vue-tsc": "^2.2.0"
|
|
54
|
+
}
|
|
55
|
+
}
|