From 047ddd57f67e68f6c1ccfcde7fe2a1bd430d4675 Mon Sep 17 00:00:00 2001
From: "DESKTOP-RQ919RC\\Pc" <1300399510@qq.com>
Date: Fri, 25 Jul 2025 18:49:11 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0HEVC=E8=BD=ACH.264?=
=?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8=E5=92=8C=E6=89=93=E5=AD=97=E6=9C=BA?=
=?UTF-8?q?=E6=95=88=E6=9E=9C=E9=A1=B5=E9=9D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
添加HEVC转H.264播放器功能,包含文件选择、转码进度显示和视频播放
新增打字机效果展示页面,使用Vue实现逐字显示效果
---
1.html | 55 ++--
2.html | 88 ++++++
hevc-to-h264-player.js | 199 +++++++++++++
index.html | 617 ++++++++++++++++++++++++++++++-----------
4 files changed, 778 insertions(+), 181 deletions(-)
create mode 100644 2.html
create mode 100644 hevc-to-h264-player.js
diff --git a/1.html b/1.html
index a3996d1..8269a84 100644
--- a/1.html
+++ b/1.html
@@ -1,26 +1,49 @@
-
+
-
-
+
+
HEVC转H.264播放器
+
+
请选择HEVC编码的视频文件
+
+
-
+
+
+
diff --git a/2.html b/2.html
new file mode 100644
index 0000000..f70d8ea
--- /dev/null
+++ b/2.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
打字机效果
+
+
+
+
+
+
+
+
+
+
+ {{ item.type }}
+
:
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/hevc-to-h264-player.js b/hevc-to-h264-player.js
new file mode 100644
index 0000000..04f725c
--- /dev/null
+++ b/hevc-to-h264-player.js
@@ -0,0 +1,199 @@
+class HevcToH264Player {
+ constructor() {
+ // 关键修正:使用正确的核心库路径(注意文件后缀和目录)
+ this.config = {
+ ffmpegScriptUrl: "https://cdnjs.cloudflare.com/ajax/libs/ffmpeg/0.12.15/umd/ffmpeg.min.js",
+ // 核心库必须使用完整路径,包含wasm文件的正确位置
+ ffmpegCorePath: "https://cdnjs.cloudflare.com/ajax/libs/ffmpeg-core/0.12.10/esm/ffmpeg-core.js",
+ };
+
+ // DOM元素
+ this.fileInput = document.getElementById("hevcFileInput");
+ this.statusEl = document.getElementById("conversionStatus");
+ this.progressBar = document.getElementById("progressBar");
+ this.videoContainer = document.getElementById("videoContainer");
+
+ // 状态变量
+ this.ffmpeg = null;
+ this.isLoaded = false;
+ this.coreLoaded = false;
+
+ // 初始化
+ this.bindEvents();
+ this.createVideoElement();
+ }
+
+ // 创建视频元素
+ createVideoElement() {
+ this.video = document.createElement("video");
+ this.video.controls = true;
+ this.video.style.width = "100%";
+ this.video.style.display = "none";
+ this.videoContainer.appendChild(this.video);
+
+ this.loadingDiv = document.createElement("div");
+ this.loadingDiv.style.cssText = `
+ position: absolute; top:0; left:0; width:100%; height:100%;
+ background:rgba(0,0,0,0.7); color:white; display:flex;
+ flex-direction:column; justify-content:center; align-items:center;
+ `;
+ this.loadingDiv.innerHTML = '
准备中...
';
+ this.videoContainer.appendChild(this.loadingDiv);
+ this.loadingText = this.loadingDiv.querySelector("#loadingText");
+
+ // 添加动画样式
+ const style = document.createElement("style");
+ style.textContent = `
+ .spinner { border:4px solid rgba(255,255,255,0.3); border-top:4px solid white;
+ border-radius:50%; width:40px; height:40px; animation:spin 1s linear infinite; }
+ @keyframes spin { 0% { transform:rotate(0deg); } 100% { transform:rotate(360deg); } }
+ `;
+ document.head.appendChild(style);
+ }
+
+ // 绑定事件
+ bindEvents() {
+ this.fileInput.addEventListener("change", (e) => {
+ if (e.target.files.length) {
+ this.convert(e.target.files[0]);
+ }
+ });
+ }
+
+ // 加载主库
+ async loadMainLibrary() {
+ return new Promise((resolve, reject) => {
+ // if (window.FFmpeg && window.FFmpeg.createFFmpeg) {
+ // resolve(window.FFmpeg);
+ // return;
+ // }
+
+ const script = document.createElement("script");
+ script.src = this.config.ffmpegScriptUrl;
+ script.type = "text/javascript";
+
+ script.onload = () => {
+ if (window.FFmpeg && window.FFmpeg.createFFmpeg) {
+ resolve(window.FFmpeg);
+ } else {
+ reject(new Error("主库加载但未找到createFFmpeg方法(版本不兼容)"));
+ }
+ };
+
+ script.onerror = () => reject(new Error("主库加载失败(网络或URL错误)"));
+ document.head.appendChild(script);
+ });
+ }
+
+ // 验证核心库是否加载(关键修正)
+ checkCoreLibraryLoaded() {
+ return new Promise((resolve) => {
+ const checkInterval = setInterval(() => {
+ // 检查核心库的WASM文件是否已加载
+ const wasmLoaded = document.querySelectorAll(`script[src*="ffmpeg-core.wasm"]`).length > 0;
+ if (wasmLoaded) {
+ clearInterval(checkInterval);
+ this.coreLoaded = true;
+ resolve();
+ }
+ }, 200);
+
+ // 核心库加载超时(30秒)
+ setTimeout(() => {
+ clearInterval(checkInterval);
+ resolve(); // 超时也继续,让FFmpeg自己处理错误
+ }, 30000);
+ });
+ }
+
+ // 初始化FFmpeg
+ async initFFmpeg() {
+ if (this.ffmpeg && this.isLoaded) return;
+
+ this.updateStatus("加载主库...");
+ const FFmpeg = await this.loadMainLibrary();
+ console.log("FFmpeg", FFmpeg);
+
+ this.updateStatus("初始化FFmpeg实例...");
+ this.ffmpeg = FFmpeg.createFFmpeg({
+ log: true,
+ corePath: this.config.ffmpegCorePath,
+ });
+
+ this.updateStatus("加载核心库(约20MB)...");
+
+ // 开始加载核心库
+ const loadPromise = this.ffmpeg.load();
+
+ // 等待核心库加载迹象
+ // await this.checkCoreLibraryLoaded();
+
+ // 等待加载完成
+ await loadPromise;
+
+ this.isLoaded = true;
+ this.updateStatus("FFmpeg完全加载成功");
+ }
+
+ // 更新状态
+ updateStatus(text) {
+ if (this.statusEl) this.statusEl.textContent = text;
+ if (this.loadingText) this.loadingText.textContent = text;
+ }
+
+ // 转码主逻辑
+ async convert(file) {
+ this.updateStatus("准备转码...");
+ this.progressBar.style.width = "0%";
+ this.loadingDiv.style.display = "flex";
+ this.video.style.display = "none";
+
+ try {
+ await this.initFFmpeg();
+
+ // 写入文件
+ const inputName = "input.hevc";
+ const outputName = "output.mp4";
+ this.ffmpeg.FS("writeFile", inputName, new Uint8Array(await file.arrayBuffer()));
+
+ // 转码进度
+ this.ffmpeg.setProgress(({ ratio }) => {
+ const progress = Math.round(ratio * 100);
+ this.progressBar.style.width = `${progress}%`;
+ this.updateStatus(`转码中:${progress}%`);
+ });
+
+ // 执行转码
+ this.updateStatus("开始转码...");
+ await this.ffmpeg.run("-i", inputName, "-c:v", "libx264", "-crf", "23", "-preset", "medium", "-c:a", "aac", "-b:a", "128k", outputName);
+
+ // 播放结果
+ const data = this.ffmpeg.FS("readFile", outputName);
+ const url = URL.createObjectURL(new Blob([data.buffer], { type: "video/mp4" }));
+ this.video.src = url;
+ this.video.onloadedmetadata = () => {
+ this.video.style.display = "block";
+ this.loadingDiv.style.display = "none";
+ this.updateStatus("转码完成,正在播放");
+ this.video.play().catch(() => {});
+ };
+ } catch (error) {
+ // 核心库未加载的特殊处理
+ if (!this.coreLoaded) {
+ error.message = "核心库加载失败,请检查网络或尝试更换CDN";
+ }
+ this.updateStatus(`转码失败:${error.message}`);
+ // console.error("错误详情:", error);
+ }
+ }
+}
+
+// 页面加载后初始化
+document.addEventListener("DOMContentLoaded", () => {
+ // 确保DOM元素存在
+ if (["hevcFileInput", "conversionStatus", "progressBar", "videoContainer"].every((id) => document.getElementById(id))) {
+ window.hevcPlayer = new HevcToH264Player();
+ } else {
+ console.error("缺少必要的DOM元素");
+ }
+});
diff --git a/index.html b/index.html
index 0b8263f..f8074d9 100644
--- a/index.html
+++ b/index.html
@@ -2,32 +2,32 @@
-
-
-
5222
-
+
你好,招生官专题 - 寄托天下
+
+
+
+
+
+
+
-

@@ -37,8 +37,6 @@
-
-
@@ -83,18 +81,18 @@
-
-
+
+
-
{{ item.title }}
-
{{ item.lecture_time || '长期答疑' }}
+
{{ item.title }}
+
{{ item.lecture_time || "长期答疑" }}
@@ -104,9 +102,32 @@
-
-
-
+
+
+

+
![]()
+
+
{{ item.title }}
+
{{ item.subtitle }}
+
+
+

+
+

+ 访谈人物
+
+
+ {{ item.admission_officer_name }}
+
{{ item.admission_officer_rank }}
+
+
{{ item.name }}
+
{{ item.admission_officer_position }}
+
+
+
+
+
+
@@ -148,15 +169,13 @@
@@ -171,7 +190,7 @@
{{ it }}季
@@ -205,7 +224,7 @@
- {{ it.date || '长期答疑'}}
+ {{ it.date || "长期答疑" }}
了解详情
@@ -229,18 +248,31 @@
{{ item.name }}
{{ item.enname }}
-
+
{{ it }}季
-
+
- 更多
-

+ 更多
@@ -250,34 +282,31 @@
{{ item }}
-

-

+

+
-

+
-
![]()
+
-
-
@@ -294,20 +323,21 @@
-

+
- {{ item.title }}
+
+
-

+
- {{ item.date || '长期答疑'}}
+ {{ item.date || "长期答疑" }}
了解详情
-
+
@@ -318,20 +348,27 @@
-

-
+

+
+
+
+
+
-
-
+
+
+
+
+
+