Repository navigation
Expand file tree
/
Copy pathindex.html
More file actions
144 lines (129 loc) · 7.44 KB
/
Copy pathindex.html
File metadata and controls
144 lines (129 loc) · 7.44 KB
1
2
3
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>pplayer</title>
<style>
body { background: #1a1a1a; color: #fff; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; display: flex; flex-direction: column; height: 100vh; margin: 0; }
#controls { background: #333; padding: 15px; display: flex; flex-direction: column; gap: 10px; border-bottom: 1px solid #444; }
.controls-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
input[type="text"] { flex: 1; padding: 8px; border-radius: 4px; border: 1px solid #555; background: #222; color: #fff; font-size: 14px; }
select { padding: 8px; border-radius: 4px; border: 1px solid #555; background: #222; color: #fff; font-size: 14px; min-width: 150px; }
select:disabled { opacity: 0.5; cursor: not-allowed; }
#bufferRange { width: 110px; accent-color: #00bcd4; }
#bufferValue { font-family: monospace; min-width: 52px; display: inline-block; text-align: right; }
/* Set by main.js when the browser exposes neither jitterBufferTarget
nor playoutDelayHint, so the control visibly reads as inert
instead of silently doing nothing. */
#bufferControl.unsupported { opacity: 0.45; }
button { padding: 8px 14px; min-width: 40px; border: none; border-radius: 4px; font-weight: bold; font-size: 16px; line-height: 1; cursor: pointer; transition: 0.2s; }
#startBtn { background: #28a745; color: white; }
#startBtn:hover { background: #218838; }
#exitBtn { background: #dc3545; color: white; }
#exitBtn:hover { background: #c82333; }
#videoPauseBtn, #audioPauseBtn { background: #5253aa; color: white; }
#videoPauseBtn:hover, #audioPauseBtn:hover { background: #6465bd; }
#muteBtn { background: #00838f; color: white; }
#muteBtn:hover { background: #00a5b5; }
#snapshotBtn { background: #6c5ce7; color: white; }
#snapshotBtn:hover { background: #7d6ff2; }
#recordBtn { background: #b71c1c; color: white; }
#recordBtn:hover { background: #d32f2f; }
#recordBtn.recording { background: #ff1744; animation: recordPulse 1s infinite; }
@keyframes recordPulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
#fullscreenBtn { background: #555; color: white; }
#fullscreenBtn:hover { background: #777; }
#statsToggleBtn { background: #0f766e; color: white; }
#statsToggleBtn:hover { background: #0d9488; }
button:disabled { opacity: 0.4; cursor: not-allowed; pointer-events: none; animation: none; }
#main-area { flex: 1; display: flex; position: relative; overflow: hidden; }
#video-container { flex: 1; display: flex; justify-content: center; align-items: center; background: #000; }
video { width: 100%; height: 100%; object-fit: contain; }
#stats {
position: absolute; top: 10px; right: 10px; width: 320px; max-height: calc(100% - 20px);
background: rgba(0,0,0,0.85); border: 1px solid #444; padding: 10px;
border-radius: 6px; overflow-y: auto; font-size: 13px; font-family: monospace;
box-shadow: 0 4px 6px rgba(0,0,0,0.3); pointer-events: none;
}
/* Toggled by the stats button in the controls row. */
#stats.collapsed { display: none; }
.stat-group { margin-bottom: 15px; }
.stat-title { color: #00bcd4; font-weight: bold; border-bottom: 1px dashed #555; margin-bottom: 5px; padding-bottom: 3px; }
.stat-row { display: flex; justify-content: space-between; margin-bottom: 3px; }
.stat-key { color: #aaa; }
.stat-val { color: #fff; font-weight: bold; }
.stat-val.good { color: #28a745; }
.stat-val.warn { color: #ffc107; }
.stat-val.bad { color: #dc3545; }
.ws-status { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 5px; background: #555; }
.ws-connected { background: #28a745; box-shadow: 0 0 5px #28a745; }
.ws-disconnected { background: #dc3545; }
</style>
</head>
<body>
<div id="controls">
<!-- The player is normally driven by the signed link's query parameters
(ppcenter/appId/streamName/txTime/txSecret), or by ?url=<signed WHEP>.
The Stream box is the manual entry point: type your own
appId/streamName (an optional trailing /h264 or /hevc forces the codec)
and press Start. It is prefilled from the query string when one is
present, and keeps the demo default below otherwise. -->
<div class="controls-row">
<label for="streamInput">Stream:</label>
<input type="text" id="streamInput" value="keyc0c021b8ef993bcc/B01-frontView/hevc"
placeholder="appId/streamName (optionally /h264 or /hevc)"
title="Type appId/streamName to play your own stream. Append /h264 or /hevc to force the codec.">
</div>
<div class="controls-row">
<div style="display: flex; align-items: center; gap: 5px;">
<span id="wsStatus" class="ws-status" title="Signaling Status"></span>
<label for="layerSelect">Quality:</label>
<select id="layerSelect" disabled>
<option value="-1">Auto (Waiting WS...)</option>
</select>
</div>
<div id="bufferControl" style="display: flex; align-items: center; gap: 5px; margin-left: 10px;"
title="Playout buffer: how much media the jitter buffer holds before rendering. Higher = smoother under jitter, more latency. Defaults to auto - the browser's own adaptive buffer.">
<label for="bufferRange">Buffer:</label>
<input type="range" id="bufferRange" min="100" max="1000" step="50" value="100">
<span id="bufferValue">auto</span>
</div>
<button id="startBtn" title="Start">▶</button>
<button id="exitBtn" title="Exit">⏹</button>
<button id="videoPauseBtn" title="Pause Video">⏸</button>
<button id="audioPauseBtn" title="Pause Audio">⏸</button>
<button id="muteBtn" title="Unmute">🔇</button>
<button id="snapshotBtn" title="Snapshot">📷</button>
<button id="recordBtn" title="Record 60s (WebM)">⏺</button>
<button id="fullscreenBtn" title="Fullscreen">⛶</button>
<button id="statsToggleBtn" title="Show / hide statistics">📊</button>
</div>
</div>
<div id="main-area">
<div id="video-container">
<video id="video" autoplay playsinline muted controls></video>
</div>
<div id="stats">
<div style="color: #888; text-align: center;">Waiting for stream...</div>
</div>
</div>
<script>
(function() {
// Collapse/expand the playback statistics overlay.
var statsToggle = document.getElementById('statsToggleBtn');
var statsPanel = document.getElementById('stats');
if (statsToggle && statsPanel) {
statsToggle.addEventListener('click', function () {
statsPanel.classList.toggle('collapsed');
});
}
})();
</script>
<!-- ES modules: main.js imports everything else, so only the entry point is
listed here. Module scripts are deferred and run after parsing, so the
DOM lookups at the top of main.js are safe. -->
<script type="module" src="main.js?v=20261011-4"></script>
</body>
</html>