<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>疑似配信サイト - PC画面&システム音声</title>
<style>
/* ベースリセット */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: sans-serif;
background: #f5f5f5;
color: #333;
}
header, footer {
background: #333;
color: #fff;
padding: 15px;
text-align: center;
}
header h1 {
margin: 0;
font-size: 1.5rem;
letter-spacing: 0.05em;
}
footer p {
margin: 0;
font-size: 0.9rem;
}
main {
max-width: 1200px;
margin: 20px auto;
padding: 20px;
background: #fff;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.page-title {
text-align: center;
margin-bottom: 20px;
font-size: 1.4rem;
}
/* 2カラムレイアウト */
.container {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.video-section {
flex: 1 1 600px;
min-width: 300px;
}
.aside-section {
flex: 1 1 350px;
min-width: 300px;
background: #fafafa;
border-left: 1px solid #ccc;
padding: 10px;
}
/* 動画表示領域 */
.video-wrapper {
background: #000;
overflow: hidden;
}
.video-wrapper video {
display: block;
width: 100%;
height: auto;
background: #000;
}
.video-title {
margin: 10px 0;
font-weight: bold;
}
.video-controls {
text-align: center;
margin-top: 10px;
}
.video-controls button {
margin: 0 5px;
padding: 8px 16px;
font-size: 1rem;
cursor: pointer;
}
/* チャットUI */
.chat-section {
display: flex;
flex-direction: column;
height: 400px;
}
.chat-log {
flex: 1;
border: 1px solid #ccc;
padding: 10px;
overflow-y: auto;
margin-bottom: 10px;
background: #fff;
}
.chat-log p {
margin: 0 0 5px 0;
}
.chat-log p span.username {
font-weight: bold;
color: #3366cc;
margin-right: 5px;
}
.chat-input-area {
display: flex;
gap: 5px;
}
.chat-input-area input[type="text"] {
flex: 1;
padding: 8px;
font-size: 1rem;
}
.chat-input-area button {
padding: 8px 16px;
font-size: 1rem;
cursor: pointer;
}
/* スケジュール */
.schedule {
margin-top: 20px;
}
.schedule h2 {
font-size: 1.2rem;
margin-bottom: 10px;
}
.schedule table {
width: 100%;
border-collapse: collapse;
}
.schedule th, .schedule td {
border: 1px solid #ccc;
padding: 10px;
text-align: center;
}
.schedule th {
background: #f0f0f0;
}
.schedule tr:nth-child(even) {
background: #fafafa;
}
</style>
</head>
<body>
<!-- ヘッダー -->
<header>
<h1>My Advanced Live Streaming (Local Demo)</h1>
</header>
<!-- メインコンテンツ -->
<main>
<div class="page-title">PC画面&システム音声をローカル再生するデモ</div>
<div class="container">
<!-- ▼ 動画エリア ▼ -->
<section class="video-section">
<div class="video-title">疑似ライブ映像</div>
<div class="video-wrapper">
<video id="liveVideo" controls autoplay></video>
</div>
<!-- ボタン類 -->
<div style="text-align: center; margin-top: 10px;">
<button id="screenShareBtn">画面共有(システム音声)</button>
<button id="cameraShareBtn">カメラ&マイク</button>
<button id="stopBtn">停止</button>
</div>
<!-- 再生/一時停止/ミュート/全画面 -->
<div class="video-controls">
<button id="playBtn">再生</button>
<button id="pauseBtn">一時停止</button>
<button id="muteBtn">ミュート/解除</button>
<button id="fullscreenBtn">全画面</button>
</div>
<div class="schedule">
<h2>配信スケジュール(仮)</h2>
<table>
<thead>
<tr>
<th>日付</th>
<th>配信タイトル</th>
<th>開始時間</th>
<th>予定</th>
</tr>
</thead>
<tbody>
<tr>
<td>2/15</td>
<td>PC画面共有テスト</td>
<td>18:00</td>
<td>30分</td>
</tr>
<tr>
<td>2/18</td>
<td>音声ミキシング練習</td>
<td>20:00</td>
<td>1時間</td>
</tr>
<tr>
<td>2/20</td>
<td>週末雑談</td>
<td>21:30</td>
<td>2時間</td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- ▼ チャットエリア ▼ -->
<aside class="aside-section">
<h2>チャット</h2>
<div class="chat-section">
<!-- チャットログ -->
<div id="chatLog" class="chat-log"></div>
<!-- 入力フォーム -->
<div class="chat-input-area">
<input type="text" id="usernameInput" placeholder="ユーザー名" />
<input type="text" id="messageInput" placeholder="メッセージを入力…" />
<button id="sendBtn">送信</button>
</div>
</div>
</aside>
</div><!-- /container -->
</main>
<!-- フッター -->
<footer>
<p>© 2025 My Advanced Live Streaming (Local Demo)</p>
</footer>
<script>
/*****************************************************************
* 1. PC画面&システム音声 or カメラ&マイクを取得
*****************************************************************/
const video = document.getElementById('liveVideo');
const screenShareBtn = document.getElementById('screenShareBtn');
const cameraShareBtn = document.getElementById('cameraShareBtn');
const stopBtn = document.getElementById('stopBtn');
let localStream = null;
// 画面共有 (PC画面 + システム音声)
async function startScreenShare() {
stopMedia(); // 既存ストリームを止めてから開始
try {
// getDisplayMediaで画面を共有
// audio:true -> Chromeの場合「システム音声を共有」オプションが表示される
localStream = await navigator.mediaDevices.getDisplayMedia({
video: true,
audio: true // ただしブラウザやOSによっては無視される可能性あり
});
video.srcObject = localStream;
// システム音声をモニタリングする場合、ミュートOFFにする
video.muted = false;
console.log('画面共有開始 (システム音声含む可能性)');
} catch (err) {
alert('画面共有を開始できませんでした: ' + err.message);
}
}
// カメラ&マイク
async function startCameraShare() {
stopMedia();
try {
localStream = await navigator.mediaDevices.getUserMedia({video: true, audio: true});
video.srcObject = localStream;
// 自分のマイク音声がループしないようにしたい場合は video.muted = true;
video.muted = true;
console.log('カメラ&マイク開始');
} catch (err) {
alert('カメラ/マイクを取得できませんでした: ' + err.message);
}
}
// 停止
function stopMedia() {
if (!localStream) return;
localStream.getTracks().forEach(track => track.stop());
localStream = null;
video.srcObject = null;
console.log('メディア停止');
}
screenShareBtn.addEventListener('click', startScreenShare);
cameraShareBtn.addEventListener('click', startCameraShare);
stopBtn.addEventListener('click', stopMedia);
/*****************************************************************
* 2. 再生/一時停止/ミュート/全画面など動画コントロール
*****************************************************************/
const playBtn = document.getElementById('playBtn');
const pauseBtn = document.getElementById('pauseBtn');
const muteBtn = document.getElementById('muteBtn');
const fullscreenBtn = document.getElementById('fullscreenBtn');
playBtn.addEventListener('click', () => {
video.play();
});
pauseBtn.addEventListener('click', () => {
video.pause();
});
muteBtn.addEventListener('click', () => {
video.muted = !video.muted;
});
fullscreenBtn.addEventListener('click', () => {
if (!document.fullscreenElement) {
video.requestFullscreen().catch(err => {
console.warn('フルスクリーンモードにできません:', err);
});
} else {
document.exitFullscreen();
}
});
/*****************************************************************
* 3. 簡易チャット機能(ローカルのみ)
*****************************************************************/
const chatLog = document.getElementById('chatLog');
const usernameInput = document.getElementById('usernameInput');
const messageInput = document.getElementById('messageInput');
const sendBtn = document.getElementById('sendBtn');
let messages = [];
function renderChatLog() {
chatLog.innerHTML = '';
messages.forEach(msg => {
const p = document.createElement('p');
const userSpan = document.createElement('span');
userSpan.className = 'username';
userSpan.textContent = msg.username + ': ';
const textNode = document.createTextNode(msg.text);
p.appendChild(userSpan);
p.appendChild(textNode);
chatLog.appendChild(p);
});
chatLog.scrollTop = chatLog.scrollHeight;
}
function sendMessage() {
const username = usernameInput.value.trim();
const text = messageInput.value.trim();
if (!username || !text) return;
messages.push({ username, text });
renderChatLog();
messageInput.value = '';
}
sendBtn.addEventListener('click', sendMessage);
messageInput.addEventListener('keydown', e => {
if (e.key === 'Enter') sendMessage();
});
// デモ用メッセージ
(function initDemoChat() {
messages.push(
{ username: 'Alice', text: 'こんにちは!' },
{ username: 'Bob', text: 'システム音声聞こえる?' },
{ username: 'Carol', text: 'タブの音は共有できてます〜' }
);
renderChatLog();
})();
</script>
</body>
</html>