chrome插件-给音视频添加倍速播放控制功能

背景:

使用了某在线教育的课程,发现没有倍速功能很难受。

content.js

javascript 复制代码
(function() {
  'use strict';
  
  let speedControlPanel = null;
  let isPanelVisible = false;
  let currentSpeed = 1.0;
  let hasInitialized = false;
  
  // 延迟初始化,等待页面完全加载
  function delayedInit() {
    if (hasInitialized) return;
    hasInitialized = true;
    
    console.log(' Video Speed Controller 开始初始化...');
    
    // 创建控制面板
    createSpeedControlPanel();
    
    // 绑定键盘事件
    document.addEventListener('keydown', handleKeyboardShortcuts);
    
    // 持续监听视频元素
    observeVideoElements();
    
    // 尝试恢复速度
    setTimeout(() => {
      restoreLastSpeed();
    }, 2000);
    
    console.log('✅ Video Speed Controller 初始化完成');
    console.log('💡 按 Ctrl+Shift+V 或 Cmd+Shift+V 打开控制面板');
  }
  
  // 立即执行 + DOMContentLoaded 双重保障
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => {
      setTimeout(delayedInit, 1000);
    });
  } else {
    setTimeout(delayedInit, 1000);
  }
  
  // 额外的 MutationObserver 确保在动态内容加载后也能工作
  const mainObserver = new MutationObserver(() => {
    if (!hasInitialized && document.body) {
      delayedInit();
    }
  });
  
  if (document.documentElement) {
    mainObserver.observe(document.documentElement, {
      childList: true,
      subtree: true
    });
  }
  
  function createSpeedControlPanel() {
    if (speedControlPanel) return;
    
    console.log(' 创建速度控制面板...');
    
    const panel = document.createElement('div');
    panel.id = 'video-speed-control-panel';
    panel.className = 'video-speed-panel';
    
    panel.innerHTML = `
      <div class="panel-header">
        <span class="panel-title">⚡ 倍速控制</span>
        <button class="panel-close">×</button>
      </div>
      <div class="panel-content">
        <div class="current-speed-display">
          <span>当前速度:</span>
          <strong id="panel-current-speed">1.0x</strong>
        </div>
        <div class="speed-buttons">
          <button class="speed-btn" data-speed="0.5">0.5x</button>
          <button class="speed-btn" data-speed="0.75">0.75x</button>
          <button class="speed-btn active" data-speed="1">1x</button>
          <button class="speed-btn" data-speed="1.25">1.25x</button>
          <button class="speed-btn" data-speed="1.5">1.5x</button>
          <button class="speed-btn" data-speed="2">2x</button>
          <button class="speed-btn" data-speed="2.5">2.5x</button>
          <button class="speed-btn" data-speed="3">3x</button>
        </div>
        <div class="custom-speed">
          <input type="number" id="custom-speed-input" placeholder="自定义速度" min="0.1" max="16" step="0.1">
          <button id="custom-speed-btn">应用</button>
        </div>
        <div class="keyboard-hint">
          💡 快捷键: ←减速 | →加速 | R重置 | Ctrl+Shift+V开关
        </div>
      </div>
    `;
    
    // 使用最高优先级插入到 body
    if (document.body) {
      document.body.appendChild(panel);
      console.log('✅ 控制面板已添加到页面');
    } else {
      console.error('❌ document.body 不存在,无法添加控制面板');
      return;
    }
    
    speedControlPanel = panel;
    bindPanelEvents();
  }
  
  function bindPanelEvents() {
    if (!speedControlPanel) return;
    
    const closeBtn = speedControlPanel.querySelector('.panel-close');
    if (closeBtn) {
      closeBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        hidePanel();
      });
    }
    
    const speedButtons = speedControlPanel.querySelectorAll('.speed-btn');
    speedButtons.forEach(btn => {
      btn.addEventListener('click', (e) => {
        e.stopPropagation();
        const speed = parseFloat(btn.dataset.speed);
        setAllVideosSpeed(speed);
        updateActiveButton(btn);
      });
    });
    
    const customInput = speedControlPanel.querySelector('#custom-speed-input');
    const customBtn = speedControlPanel.querySelector('#custom-speed-btn');
    
    if (customBtn) {
      customBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        const speed = parseFloat(customInput.value);
        if (speed && speed >= 0.1 && speed <= 16) {
          setAllVideosSpeed(speed);
          customInput.value = '';
        } else {
          alert('请输入 0.1 到 16 之间的有效数字');
        }
      });
    }
    
    if (customInput) {
      customInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
          customBtn.click();
        }
      });
    }
  }
  
  function showPanel() {
    if (!speedControlPanel) {
      createSpeedControlPanel();
    }
    if (speedControlPanel) {
      speedControlPanel.classList.add('visible');
      isPanelVisible = true;
      console.log(' 控制面板已显示');
    }
  }
  
  function hidePanel() {
    if (speedControlPanel) {
      speedControlPanel.classList.remove('visible');
      isPanelVisible = false;
      console.log('📱 控制面板已隐藏');
    }
  }
  
  function togglePanel() {
    console.log('🔄 切换控制面板状态');
    if (isPanelVisible) {
      hidePanel();
    } else {
      showPanel();
    }
  }
  
  function updateActiveButton(activeBtn) {
    if (!speedControlPanel) return;
    const buttons = speedControlPanel.querySelectorAll('.speed-btn');
    buttons.forEach(btn => btn.classList.remove('active'));
    if (activeBtn) {
      activeBtn.classList.add('active');
    }
  }
  
  function updatePanelSpeedDisplay(speed) {
    if (!speedControlPanel) return;
    const display = speedControlPanel.querySelector('#panel-current-speed');
    if (display) {
      display.textContent = speed.toFixed(2) + 'x';
    }
  }
  
  function setAllVideosSpeed(speed) {
    currentSpeed = speed;
    let videoCount = 0;
    
    console.log(`🎯 尝试设置视频速度为: ${speed}x`);
    
    // 方法1: 标准 video 标签
    const videos = document.querySelectorAll('video');
    console.log(`📹 找到 ${videos.length} 个 video 元素`);
    
    videos.forEach((video, index) => {
      try {
        console.log(`  [${index}] video 元素:`, video);
        video.playbackRate = speed;
        videoCount++;
        console.log(`  ✅ 已设置 video[${index}] 速度为 ${speed}x`);
      } catch (e) {
        console.warn(`  ❌ 设置 video[${index}] 失败:`, e);
      }
    });
    
    // 方法2: 查找 shadow DOM 中的视频
    const allElements = document.querySelectorAll('*');
    allElements.forEach(el => {
      if (el.shadowRoot) {
        const shadowVideos = el.shadowRoot.querySelectorAll('video');
        shadowVideos.forEach(video => {
          try {
            video.playbackRate = speed;
            videoCount++;
            console.log('✅ 已设置 shadow DOM 中的 video 速度');
          } catch (e) {}
        });
      }
    });
    
    // 保存到本地存储
    try {
      localStorage.setItem('preferredPlaybackSpeed', speed);
    } catch (e) {}
    
    updatePanelSpeedDisplay(speed);
    
    if (videoCount === 0) {
      console.warn('⚠️ 未找到可控制的视频元素');
      showNoVideoMessage();
    } else {
      console.log(`✅ 成功设置 ${videoCount} 个视频的速度为 ${speed}x`);
    }
    
    return videoCount;
  }
  
  function showNoVideoMessage() {
    const existingMsg = document.querySelector('.no-video-message');
    if (existingMsg) return;
    
    const message = document.createElement('div');
    message.className = 'no-video-message';
    message.textContent = '⚠️ 未检测到视频元素';
    message.style.cssText = `
      position: fixed;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background: rgba(255, 87, 34, 0.9);
      color: white;
      padding: 20px 30px;
      border-radius: 8px;
      z-index: 9999999;
      font-size: 16px;
      font-weight: bold;
      box-shadow: 0 4px 20px rgba(0,0,0,0.3);
    `;
    
    document.body.appendChild(message);
    
    setTimeout(() => {
      if (message.parentNode) {
        message.remove();
      }
    }, 3000);
  }
  
  function restoreLastSpeed() {
    try {
      const savedSpeed = localStorage.getItem('preferredPlaybackSpeed');
      if (savedSpeed) {
        const speed = parseFloat(savedSpeed);
        if (!isNaN(speed)) {
          console.log(`💾 恢复上次保存的速度: ${speed}x`);
          setAllVideosSpeed(speed);
          
          if (speedControlPanel) {
            const buttons = speedControlPanel.querySelectorAll('.speed-btn');
            buttons.forEach(btn => {
              if (Math.abs(parseFloat(btn.dataset.speed) - speed) < 0.01) {
                updateActiveButton(btn);
              }
            });
          }
        }
      }
    } catch (e) {
      console.warn('恢复速度失败:', e);
    }
  }
  
  function handleKeyboardShortcuts(e) {
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
      return;
    }
    
    // Ctrl/Cmd + Shift + V
    if ((e.ctrlKey || e.metaKey) && e.shiftKey && (e.key === 'V' || e.key === 'v')) {
      e.preventDefault();
      e.stopPropagation();
      console.log('⌨️ 检测到快捷键: Ctrl+Shift+V');
      togglePanel();
      return;
    }
    
    if (isPanelVisible) {
      return;
    }
    
    // 左箭头
    if (e.key === 'ArrowLeft') {
      e.preventDefault();
      adjustSpeed(-0.25);
    }
    // 右箭头
    else if (e.key === 'ArrowRight') {
      e.preventDefault();
      adjustSpeed(0.25);
    }
    // R键
    else if (e.key === 'r' || e.key === 'R') {
      e.preventDefault();
      setAllVideosSpeed(1.0);
    }
  }
  
  function adjustSpeed(delta) {
    const newSpeed = Math.max(0.25, Math.min(16, currentSpeed + delta));
    console.log(`⚡ 调整速度: ${currentSpeed}x → ${newSpeed}x`);
    setAllVideosSpeed(newSpeed);
  }
  
  function observeVideoElements() {
    const observer = new MutationObserver((mutations) => {
      mutations.forEach(mutation => {
        if (mutation.addedNodes.length > 0) {
          mutation.addedNodes.forEach(node => {
            if (node.nodeName === 'VIDEO' || 
                (node.querySelectorAll && node.querySelectorAll('video').length > 0)) {
              console.log('🎥 检测到新视频元素添加');
              setTimeout(() => {
                restoreLastSpeed();
              }, 500);
            }
          });
        }
      });
    });
    
    if (document.body) {
      observer.observe(document.body, {
        childList: true,
        subtree: true
      });
      console.log(' 已开始监听视频元素变化');
    }
  }
})();

manifest.json

html 复制代码
{
  "manifest_version": 3,
  "name": "Video Speed Controller",
  "version": "1.0.0",
  "description": "为网页视频添加倍速播放控制功能",
  "permissions": [
    "activeTab",
    "scripting"
  ],
  "host_permissions": [
    "<all_urls>"
  ],
  "action": {
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"],
      "css": ["styles.css"],
      "run_at": "document_idle"
    }
  ]
}
html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Video Speed Controller</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    
    body {
      width: 300px;
      padding: 20px;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      background: #f5f5f5;
    }
    
    h1 {
      font-size: 18px;
      margin-bottom: 15px;
      color: #333;
      text-align: center;
    }
    
    .control-group {
      margin-bottom: 15px;
    }
    
    label {
      display: block;
      margin-bottom: 5px;
      font-size: 14px;
      color: #666;
    }
    
    input[type="range"] {
      width: 100%;
      margin-bottom: 5px;
      cursor: pointer;
    }
    
    .speed-display {
      text-align: center;
      font-size: 28px;
      font-weight: bold;
      color: #667eea;
      margin: 15px 0;
      padding: 10px;
      background: white;
      border-radius: 8px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    
    button {
      width: 100%;
      padding: 12px;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      color: white;
      border: none;
      border-radius: 8px;
      cursor: pointer;
      font-size: 14px;
      font-weight: 600;
      margin-bottom: 8px;
      transition: all 0.2s;
    }
    
    button:hover {
      transform: translateY(-2px);
      box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
    }
    
    button.secondary {
      background: #e0e0e0;
      color: #333;
    }
    
    button.secondary:hover {
      background: #d0d0d0;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
    }
    
    .preset-speeds {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      gap: 8px;
      margin-top: 10px;
    }
    
    .preset-btn {
      padding: 10px;
      font-size: 13px;
    }
    
    .info {
      margin-top: 15px;
      padding: 10px;
      background: #fff3cd;
      border-left: 3px solid #ffc107;
      border-radius: 4px;
      font-size: 12px;
      color: #856404;
    }
  </style>
</head>
<body>
  <h1>🎬 视频倍速控制器</h1>
  
  <div class="control-group">
    <div class="speed-display" id="currentSpeed">1.0x</div>
  </div>
  
  <div class="control-group">
    <label>调整速度:</label>
    <input type="range" id="speedSlider" min="0.25" max="4" step="0.25" value="1">
  </div>
  
  <button id="applyBtn">应用速度</button>
  
  <div class="preset-speeds">
    <button class="preset-btn secondary" data-speed="0.5">0.5x</button>
    <button class="preset-btn secondary" data-speed="0.75">0.75x</button>
    <button class="preset-btn secondary" data-speed="1">1x</button>
    <button class="preset-btn secondary" data-speed="1.25">1.25x</button>
    <button class="preset-btn secondary" data-speed="1.5">1.5x</button>
    <button class="preset-btn secondary" data-speed="2">2x</button>
    <button class="preset-btn secondary" data-speed="2.5">2.5x</button>
    <button class="preset-btn secondary" data-speed="3">3x</button>
    <button class="preset-btn secondary" data-speed="4">4x</button>
  </div>
  
  <button id="resetBtn" class="secondary" style="margin-top: 10px;">重置为默认</button>
  
  <div class="info">
    💡 提示:也可以在页面按 Ctrl+Shift+V 打开控制面板
  </div>
  
  <script src="popup.js"></script>
</body>
</html>

popup.js

javascript 复制代码
document.addEventListener('DOMContentLoaded', function() {
  const speedSlider = document.getElementById('speedSlider');
  const currentSpeedDisplay = document.getElementById('currentSpeed');
  const applyBtn = document.getElementById('applyBtn');
  const resetBtn = document.getElementById('resetBtn');
  const presetButtons = document.querySelectorAll('.preset-btn');
  
  // 获取当前标签页的视频元素并获取其播放速度
  chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
    if (tabs[0]) {
      chrome.scripting.executeScript({
        target: { tabId: tabs[0].id },
        func: () => {
          const videos = document.querySelectorAll('video');
          return videos.length > 0 ? videos[0].playbackRate : 1.0;
        }
      }, function(results) {
        if (results && results[0] && results[0].result !== undefined) {
          const currentSpeed = results[0].result;
          speedSlider.value = currentSpeed;
          currentSpeedDisplay.textContent = currentSpeed.toFixed(2) + 'x';
        }
      });
    }
  });
  
  // 滑块变化时更新显示
  speedSlider.addEventListener('input', function() {
    currentSpeedDisplay.textContent = parseFloat(this.value).toFixed(2) + 'x';
  });
  
  // 应用速度按钮
  applyBtn.addEventListener('click', function() {
    const speed = parseFloat(speedSlider.value);
    setVideoSpeed(speed);
  });
  
  // 预设速度按钮
  presetButtons.forEach(btn => {
    btn.addEventListener('click', function() {
      const speed = parseFloat(this.dataset.speed);
      speedSlider.value = speed;
      currentSpeedDisplay.textContent = speed.toFixed(2) + 'x';
      setVideoSpeed(speed);
    });
  });
  
  // 重置按钮
  resetBtn.addEventListener('click', function() {
    speedSlider.value = 1.0;
    currentSpeedDisplay.textContent = '1.00x';
    setVideoSpeed(1.0);
  });
  
  // 设置视频播放速度
  function setVideoSpeed(speed) {
    chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
      if (tabs[0]) {
        chrome.scripting.executeScript({
          target: { tabId: tabs[0].id },
          func: (speed) => {
            const videos = document.querySelectorAll('video');
            videos.forEach(video => {
              video.playbackRate = speed;
            });
            
            try {
              localStorage.setItem('preferredPlaybackSpeed', speed);
            } catch (e) {}
            
            return videos.length;
          },
          args: [speed]
        }, function(results) {
          if (results && results[0] && results[0].result > 0) {
            console.log(`已将 ${results[0].result} 个视频的速度设置为 ${speed}x`);
          } else {
            alert('未找到视频元素');
          }
        });
      }
    });
  }
});

styles.css

css 复制代码
.video-speed-panel {
  position: fixed;
  top: 20px;
  right: 20px;
  width: 300px;
  background: rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(10px);
  border-radius: 12px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
  z-index: 999999;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-20px);
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  border: 1px solid rgba(0, 0, 0, 0.1);
}

.video-speed-panel.visible {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.panel-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 16px 20px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border-radius: 12px 12px 0 0;
  color: white;
}

.panel-title {
  font-size: 16px;
  font-weight: 600;
  letter-spacing: 0.5px;
}

.panel-close {
  background: none;
  border: none;
  color: white;
  font-size: 28px;
  cursor: pointer;
  padding: 0;
  width: 32px;
  height: 32px;
  line-height: 1;
  border-radius: 50%;
  transition: all 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}

.panel-close:hover {
  background: rgba(255, 255, 255, 0.2);
  transform: rotate(90deg);
}

.panel-content {
  padding: 20px;
}

.current-speed-display {
  text-align: center;
  margin-bottom: 20px;
  padding: 12px;
  background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
  border-radius: 8px;
  font-size: 14px;
  color: #333;
}

.current-speed-display strong {
  font-size: 24px;
  color: #667eea;
  margin-left: 8px;
}

.speed-buttons {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 8px;
  margin-bottom: 16px;
}

.speed-btn {
  padding: 12px 8px;
  border: 2px solid #e0e0e0;
  background: white;
  border-radius: 8px;
  cursor: pointer;
  font-size: 13px;
  font-weight: 600;
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
  color: #333;
  position: relative;
  overflow: hidden;
}

.speed-btn::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0;
  height: 0;
  border-radius: 50%;
  background: rgba(102, 126, 234, 0.1);
  transform: translate(-50%, -50%);
  transition: width 0.3s, height 0.3s;
}

.speed-btn:hover::before {
  width: 200px;
  height: 200px;
}

.speed-btn:hover {
  border-color: #667eea;
  background: #f8f9ff;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}

.speed-btn.active {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border-color: transparent;
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4);
  transform: scale(1.05);
}

.custom-speed {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
}

.custom-speed input {
  flex: 1;
  padding: 12px;
  border: 2px solid #e0e0e0;
  border-radius: 8px;
  font-size: 14px;
  outline: none;
  transition: all 0.2s;
}

.custom-speed input:focus {
  border-color: #667eea;
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

.custom-speed button {
  padding: 12px 24px;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: 600;
  transition: all 0.2s;
  white-space: nowrap;
}

.custom-speed button:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}

.keyboard-hint {
  padding: 12px;
  background: #f8f9fa;
  border-radius: 8px;
  font-size: 12px;
  color: #666;
  text-align: center;
  line-height: 1.6;
  border-left: 3px solid #667eea;
}

@keyframes fadeOut {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}

@media (max-width: 480px) {
  .video-speed-panel {
    width: calc(100% - 40px);
    right: 20px;
    left: 20px;
  }
  
  .speed-buttons {
    grid-template-columns: repeat(3, 1fr);
  }
}

Video Speed Controller - Chrome 倍速播放扩展

一个强大的 Chrome 浏览器扩展,为任意网页上的视频添加倍速播放控制功能。

✨ 功能特性

  • 🚀 **快速倍速调整**: 支持 0.5x - 3x 常用速度

  • 🎯 **精确控制**: 自定义速度范围 0.1x - 16x

  • ⌨️ **键盘快捷键**:

  • `Ctrl/Cmd + Shift + V`: 打开/关闭控制面板

  • `←`: 减速 0.25x

  • `→`: 加速 0.25x

  • `R`: 重置为 1x

  • 💾 **记忆功能**: 自动保存上次使用的速度

  • **美观界面**: 现代化设计,流畅动画

  • 📱 **响应式**: 适配各种屏幕尺寸

📦 安装方法

方法一:开发者模式安装

  1. 下载本项目到本地

  2. 打开 Chrome 浏览器,访问 `chrome://extensions/`

  3. 开启右上角的"开发者模式"

  4. 点击"加载已解压的扩展程序"

  5. 选择本项目的文件夹

  6. 安装完成!

方法二:从 Chrome Web Store 安装(待发布)

即将上线...

🎮 使用方法

方式一:使用弹出窗口

  1. 点击浏览器工具栏中的扩展图标

  2. 拖动滑块或点击预设速度按钮

  3. 点击"应用速度"按钮

方式二:使用页面控制面板(推荐)

  1. 在任何包含视频的页面

  2. 按下 `Ctrl + Shift + V`(Windows/Linux)或 `Cmd + Shift + V`(Mac)

  3. 在弹出的控制面板中选择速度

  4. 或直接使用键盘快捷键调整

方式三:键盘快捷键

  • 直接按 `←` / `→` 调整速度(±0.25x)

  • 按 `R` 重置为正常速度

️ 技术栈

  • **Manifest V3**: 最新的 Chrome 扩展规范

  • **Vanilla JavaScript**: 无依赖,轻量高效

  • **CSS3**: 现代样式和动画效果

  • **Chrome Extension API**: 完整的浏览器集成

📁 项目结构

video-speed-controller/

├── manifest.json

├── popup.html

├── popup.js

├── content.js

├── styles.css

├── README.md

相关推荐
潜创微科技38 分钟前
HDMI1.3 无线传输芯片方案 空旷 150 米量产级音视频方案
音视频
VidDown1 小时前
VidDown 工具站:免费、本地优先的开发者工具箱
javascript·编辑器·音视频·视频编解码·视频
换个昵称都难1 小时前
音频格式之WAV
音视频
AI创界者2 小时前
PilotTTS 一键整合包(Win/Mac):8G 显存畅跑,实测解锁情绪与副语言的精准控制
人工智能·macos·aigc·音视频
u152109648493 小时前
S.S.Audio PRO A2音频隔离器
嵌入式硬件·音视频·实时音视频·视频编解码·视频
VidDown4 小时前
显卡处理视频技术详解:从硬解码到 NVENC,GPU 如何让视频处理起飞?
javascript·编辑器·音视频·视频编解码·视频
EasyDSS5 小时前
全能音视频平台/私有化音视频系统EasyDSS!直播/点播/会议/集群对讲一站式落地
音视频
Damon_X6 小时前
车载音频复习
音视频
3DVisionary6 小时前
告别数据中断:XTDIC-VG视频引伸计在金属疲劳测试中3个真实案例
人工智能·音视频·应用案例·xtdic-vg·视频引伸计·疲劳测试·实战复盘
VidDown7 小时前
视频帧率技术详解:从 24fps 到 120fps,帧率如何影响你的观看体验?
网络·网络协议·编辑器·音视频·视频编解码·视频