|
|
@@ -0,0 +1,586 @@
|
|
|
+<template>
|
|
|
+ <div class="video-container">
|
|
|
+ <div class="video-wrapper">
|
|
|
+ <video
|
|
|
+ ref="videoElement"
|
|
|
+ class="video-player video-js vjs-default-skin"
|
|
|
+ controls
|
|
|
+ autoplay
|
|
|
+ ></video>
|
|
|
+ </div>
|
|
|
+ <div class="control-panel">
|
|
|
+ <div class="format-decoder-options">
|
|
|
+ <div class="format-selector">
|
|
|
+ <label class="format-label">
|
|
|
+ <input type="radio" v-model="videoFormat" value="flv">
|
|
|
+ <span>FLV</span>
|
|
|
+ </label>
|
|
|
+ <label class="format-label">
|
|
|
+ <input type="radio" v-model="videoFormat" value="hls">
|
|
|
+ <span>HLS (H.265)</span>
|
|
|
+ </label>
|
|
|
+ <label class="format-label">
|
|
|
+ <input type="radio" v-model="videoFormat" value="mp4">
|
|
|
+ <span>MP4</span>
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ <div class="decoder-options">
|
|
|
+ <label class="decoder-label">
|
|
|
+ <input type="radio" v-model="decoderType" value="hardware">
|
|
|
+ <span>硬件加速</span>
|
|
|
+ </label>
|
|
|
+ <label class="decoder-label">
|
|
|
+ <input type="radio" v-model="decoderType" value="software">
|
|
|
+ <span>软件解码</span>
|
|
|
+ </label>
|
|
|
+ <label class="decoder-label">
|
|
|
+ <input type="radio" v-model="decoderType" value="auto">
|
|
|
+ <span>自动选择</span>
|
|
|
+ </label>
|
|
|
+ <label class="decoder-label">
|
|
|
+ <input type="radio" v-model="decoderType" value="none">
|
|
|
+ <span>关闭解码</span>
|
|
|
+ </label>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="video-controls">
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ v-model="videoUrl"
|
|
|
+ placeholder="请输入视频URL"
|
|
|
+ class="video-url-input"
|
|
|
+ />
|
|
|
+ <button @click="loadVideo" class="load-button">
|
|
|
+ <span class="button-text">播放</span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import flvjs from 'flv.js'
|
|
|
+import Hls from 'hls.js'
|
|
|
+import 'video.js/dist/video-js.css'
|
|
|
+import videojs from 'video.js'
|
|
|
+import 'videojs-contrib-hls'
|
|
|
+import 'videojs-contrib-quality-levels'
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'VideoPlayer',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ videoUrl: '',
|
|
|
+ flvPlayer: null,
|
|
|
+ hlsPlayer: null,
|
|
|
+ vjsPlayer: null,
|
|
|
+ videoFormat: 'flv',
|
|
|
+ decoderType: 'auto',
|
|
|
+ showPlayButton: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ loadVideo() {
|
|
|
+ if (!this.videoUrl) {
|
|
|
+ alert('请输入视频URL');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 销毁之前的播放器实例
|
|
|
+ this.destroyPlayer();
|
|
|
+
|
|
|
+ const videoElement = this.$refs.videoElement;
|
|
|
+
|
|
|
+ // 根据选择的格式和解码选项加载视频
|
|
|
+ if (this.decoderType === 'hardware') {
|
|
|
+ // 使用 video.js 进行硬件加速解码
|
|
|
+ this.loadWithVideoJS();
|
|
|
+ } else if (this.decoderType === 'software') {
|
|
|
+ // 使用软件解码
|
|
|
+ this.loadWithSoftwareDecoder();
|
|
|
+ } else if (this.decoderType === 'none') {
|
|
|
+ // 关闭解码,直接使用浏览器原生播放
|
|
|
+ this.loadWithoutDecoder();
|
|
|
+ } else {
|
|
|
+ // 自动选择解码方式
|
|
|
+ if (this.videoFormat === 'flv') {
|
|
|
+ this.loadFlvVideo(videoElement);
|
|
|
+ } else if (this.videoFormat === 'hls') {
|
|
|
+ this.loadHlsVideo(videoElement);
|
|
|
+ } else if (this.videoFormat === 'mp4') {
|
|
|
+ this.loadMp4Video(videoElement);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ loadWithoutDecoder() {
|
|
|
+ const videoElement = this.$refs.videoElement;
|
|
|
+
|
|
|
+ // 移除 video.js 相关类
|
|
|
+ videoElement.classList.remove('video-js', 'vjs-default-skin');
|
|
|
+
|
|
|
+ // 根据不同格式尝试直接播放
|
|
|
+ if (this.videoFormat === 'mp4') {
|
|
|
+ // MP4 格式大多数浏览器原生支持
|
|
|
+ videoElement.src = this.videoUrl;
|
|
|
+ videoElement.load();
|
|
|
+ videoElement.play().catch(e => {
|
|
|
+ console.error('原生播放失败:', e);
|
|
|
+ alert('您的浏览器可能不支持直接播放此格式,请尝试启用解码器');
|
|
|
+ });
|
|
|
+ } else if (this.videoFormat === 'hls' && videoElement.canPlayType('application/vnd.apple.mpegurl')) {
|
|
|
+ // 某些浏览器(Safari)原生支持HLS
|
|
|
+ videoElement.src = this.videoUrl;
|
|
|
+ videoElement.addEventListener('loadedmetadata', () => {
|
|
|
+ videoElement.play().catch(e => {
|
|
|
+ console.error('原生HLS播放失败:', e);
|
|
|
+ });
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ // 其他格式尝试直接播放,但可能不支持
|
|
|
+ videoElement.src = this.videoUrl;
|
|
|
+ videoElement.load();
|
|
|
+ videoElement.play().catch(e => {
|
|
|
+ console.error('原生播放失败:', e);
|
|
|
+ alert(`您的浏览器不支持直接播放${this.videoFormat.toUpperCase()}格式,请启用解码器`);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ loadWithSoftwareDecoder() {
|
|
|
+ const videoElement = this.$refs.videoElement;
|
|
|
+
|
|
|
+ if (this.videoFormat === 'flv') {
|
|
|
+ // FLV 软件解码
|
|
|
+ if (flvjs.isSupported()) {
|
|
|
+ this.flvPlayer = flvjs.createPlayer({
|
|
|
+ type: 'flv',
|
|
|
+ url: this.videoUrl,
|
|
|
+ hasAudio: true,
|
|
|
+ hasVideo: true,
|
|
|
+ isLive: true,
|
|
|
+ cors: true,
|
|
|
+ withCredentials: false,
|
|
|
+ enableStashBuffer: true,
|
|
|
+ stashInitialSize: 1024 * 256, // 更大的缓冲区
|
|
|
+ enableWorker: true, // 启用 Web Worker
|
|
|
+ softwareDecoder: true, // 启用软件解码
|
|
|
+ softwareDecodeErrorRecover: true, // 软件解码错误恢复
|
|
|
+ lazyLoad: false,
|
|
|
+ seekType: 'range',
|
|
|
+ rangeLoadZeroStart: true,
|
|
|
+ fixAudioTimestampGap: true,
|
|
|
+ });
|
|
|
+
|
|
|
+ this.flvPlayer.attachMediaElement(videoElement);
|
|
|
+ this.flvPlayer.load();
|
|
|
+
|
|
|
+ // 添加错误处理
|
|
|
+ this.flvPlayer.on(flvjs.Events.ERROR, (errorType, errorDetail) => {
|
|
|
+ console.error('FLV软件解码错误:', errorType, errorDetail);
|
|
|
+
|
|
|
+ // 尝试恢复播放
|
|
|
+ if (errorType === flvjs.ErrorTypes.NETWORK_ERROR) {
|
|
|
+ setTimeout(() => {
|
|
|
+ console.log('尝试重新加载视频...');
|
|
|
+ this.flvPlayer.unload();
|
|
|
+ this.flvPlayer.load();
|
|
|
+ this.flvPlayer.play();
|
|
|
+ }, 3000);
|
|
|
+ } else if (errorType === flvjs.ErrorTypes.MEDIA_ERROR) {
|
|
|
+ // 媒体错误,尝试重新创建播放器
|
|
|
+ setTimeout(() => {
|
|
|
+ console.log('尝试重新创建播放器...');
|
|
|
+ this.destroyPlayer();
|
|
|
+ this.loadWithSoftwareDecoder();
|
|
|
+ }, 2000);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.flvPlayer.play().catch(e => {
|
|
|
+ console.error('软件解码播放失败:', e);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ alert('您的浏览器不支持FLV视频播放');
|
|
|
+ }
|
|
|
+ } else if (this.videoFormat === 'hls') {
|
|
|
+ // HLS 软件解码
|
|
|
+ if (Hls.isSupported()) {
|
|
|
+ this.hlsPlayer = new Hls({
|
|
|
+ enableWorker: true,
|
|
|
+ lowLatencyMode: false, // 关闭低延迟模式以提高稳定性
|
|
|
+ maxBufferLength: 60,
|
|
|
+ maxMaxBufferLength: 120,
|
|
|
+ backBufferLength: 90,
|
|
|
+ enableSoftwareAES: true, // 启用软件 AES 解密
|
|
|
+ fragLoadingTimeOut: 20000, // 增加片段加载超时
|
|
|
+ manifestLoadingTimeOut: 20000, // 增加清单加载超时
|
|
|
+ levelLoadingTimeOut: 20000, // 增加级别加载超时
|
|
|
+ startFragPrefetch: true, // 预取片段
|
|
|
+ });
|
|
|
+
|
|
|
+ this.hlsPlayer.loadSource(this.videoUrl);
|
|
|
+ this.hlsPlayer.attachMedia(videoElement);
|
|
|
+
|
|
|
+ this.hlsPlayer.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
|
+ videoElement.play();
|
|
|
+ });
|
|
|
+
|
|
|
+ this.hlsPlayer.on(Hls.Events.ERROR, (event, data) => {
|
|
|
+ console.error('HLS软件解码错误:', data);
|
|
|
+ if (data.fatal) {
|
|
|
+ switch(data.type) {
|
|
|
+ case Hls.ErrorTypes.NETWORK_ERROR:
|
|
|
+ console.error('网络错误,尝试恢复...');
|
|
|
+ this.hlsPlayer.startLoad();
|
|
|
+ break;
|
|
|
+ case Hls.ErrorTypes.MEDIA_ERROR:
|
|
|
+ console.error('媒体错误,尝试恢复...');
|
|
|
+ this.hlsPlayer.recoverMediaError();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ console.error('无法恢复的错误,重新创建播放器...');
|
|
|
+ setTimeout(() => {
|
|
|
+ this.destroyPlayer();
|
|
|
+ this.loadWithSoftwareDecoder();
|
|
|
+ }, 2000);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ alert('您的浏览器不支持HLS视频播放');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // MP4 软件解码 (使用原生播放器)
|
|
|
+ this.loadMp4Video(videoElement);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ loadWithVideoJS() {
|
|
|
+ // 初始化 video.js 播放器
|
|
|
+ this.vjsPlayer = videojs(this.$refs.videoElement, {
|
|
|
+ techOrder: ['html5'],
|
|
|
+ sources: [{
|
|
|
+ src: this.videoUrl,
|
|
|
+ type: this.getVideoJSType()
|
|
|
+ }],
|
|
|
+ controls: true,
|
|
|
+ autoplay: true,
|
|
|
+ preload: 'auto',
|
|
|
+ fluid: true,
|
|
|
+ html5: {
|
|
|
+ hls: {
|
|
|
+ overrideNative: true,
|
|
|
+ enableLowInitialPlaylist: true,
|
|
|
+ smoothQualityChange: true,
|
|
|
+ handleManifestRedirects: true
|
|
|
+ },
|
|
|
+ nativeVideoTracks: false,
|
|
|
+ nativeAudioTracks: false,
|
|
|
+ nativeTextTracks: false
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 添加质量选择功能
|
|
|
+ this.vjsPlayer.qualityLevels();
|
|
|
+
|
|
|
+ // 错误处理
|
|
|
+ this.vjsPlayer.on('error', () => {
|
|
|
+ console.error('VideoJS 播放错误:', this.vjsPlayer.error());
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ getVideoJSType() {
|
|
|
+ switch(this.videoFormat) {
|
|
|
+ case 'flv':
|
|
|
+ return 'video/x-flv';
|
|
|
+ case 'hls':
|
|
|
+ return 'application/x-mpegURL';
|
|
|
+ case 'mp4':
|
|
|
+ return 'video/mp4';
|
|
|
+ default:
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ loadFlvVideo(videoElement) {
|
|
|
+ if (flvjs.isSupported()) {
|
|
|
+ this.flvPlayer = flvjs.createPlayer({
|
|
|
+ type: 'flv',
|
|
|
+ url: this.videoUrl,
|
|
|
+ hasAudio: true,
|
|
|
+ hasVideo: true,
|
|
|
+ isLive: true,
|
|
|
+ cors: true,
|
|
|
+ withCredentials: false,
|
|
|
+ enableStashBuffer: true,
|
|
|
+ stashInitialSize: 1024 * 128, // 增加缓冲区大小
|
|
|
+ lazyLoad: false, // 禁用懒加载
|
|
|
+ lazyLoadMaxDuration: 0,
|
|
|
+ seekType: 'range',
|
|
|
+ rangeLoadZeroStart: true, // 从0开始加载
|
|
|
+ fixAudioTimestampGap: true, // 修复音频时间戳间隙
|
|
|
+ });
|
|
|
+
|
|
|
+ this.flvPlayer.attachMediaElement(videoElement);
|
|
|
+ this.flvPlayer.load();
|
|
|
+
|
|
|
+ // 添加错误处理
|
|
|
+ this.flvPlayer.on(flvjs.Events.ERROR, (errorType, errorDetail) => {
|
|
|
+ console.error('FLV播放错误:', errorType, errorDetail);
|
|
|
+
|
|
|
+ // 尝试恢复播放
|
|
|
+ if (errorType === flvjs.ErrorTypes.NETWORK_ERROR) {
|
|
|
+ // 网络错误,尝试重新加载
|
|
|
+ setTimeout(() => {
|
|
|
+ console.log('尝试重新加载视频...');
|
|
|
+ this.flvPlayer.unload();
|
|
|
+ this.flvPlayer.load();
|
|
|
+ this.flvPlayer.play();
|
|
|
+ }, 3000);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.flvPlayer.play().catch(e => {
|
|
|
+ console.error('播放失败:', e);
|
|
|
+ // 可能是自动播放策略阻止,添加手动播放按钮
|
|
|
+ this.showPlayButton = true;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ alert('您的浏览器不支持FLV视频播放');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ loadHlsVideo(videoElement) {
|
|
|
+ if (videoElement.canPlayType('application/vnd.apple.mpegurl')) {
|
|
|
+ videoElement.src = this.videoUrl;
|
|
|
+ videoElement.addEventListener('loadedmetadata', () => {
|
|
|
+ videoElement.play();
|
|
|
+ });
|
|
|
+ } else if (Hls.isSupported()) {
|
|
|
+ this.hlsPlayer = new Hls({
|
|
|
+ enableWorker: true,
|
|
|
+ lowLatencyMode: true,
|
|
|
+ maxBufferLength: 30,
|
|
|
+ maxMaxBufferLength: 60
|
|
|
+ });
|
|
|
+ this.hlsPlayer.loadSource(this.videoUrl);
|
|
|
+ this.hlsPlayer.attachMedia(videoElement);
|
|
|
+ this.hlsPlayer.on(Hls.Events.MANIFEST_PARSED, () => {
|
|
|
+ videoElement.play();
|
|
|
+ });
|
|
|
+
|
|
|
+ this.hlsPlayer.on(Hls.Events.ERROR, (event, data) => {
|
|
|
+ if (data.fatal) {
|
|
|
+ switch(data.type) {
|
|
|
+ case Hls.ErrorTypes.NETWORK_ERROR:
|
|
|
+ console.error('网络错误');
|
|
|
+ this.hlsPlayer.startLoad();
|
|
|
+ break;
|
|
|
+ case Hls.ErrorTypes.MEDIA_ERROR:
|
|
|
+ console.error('媒体错误');
|
|
|
+ this.hlsPlayer.recoverMediaError();
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ console.error('无法恢复的错误', data);
|
|
|
+ this.destroyPlayer();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ alert('您的浏览器不支持HLS视频播放');
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ loadMp4Video(videoElement) {
|
|
|
+ videoElement.src = this.videoUrl;
|
|
|
+ videoElement.load();
|
|
|
+ videoElement.play();
|
|
|
+ },
|
|
|
+
|
|
|
+ destroyPlayer() {
|
|
|
+ if (this.flvPlayer) {
|
|
|
+ this.flvPlayer.unload();
|
|
|
+ this.flvPlayer.detachMediaElement();
|
|
|
+ this.flvPlayer.destroy();
|
|
|
+ this.flvPlayer = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.hlsPlayer) {
|
|
|
+ this.hlsPlayer.destroy();
|
|
|
+ this.hlsPlayer = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.vjsPlayer) {
|
|
|
+ this.vjsPlayer.dispose();
|
|
|
+ this.vjsPlayer = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const videoElement = this.$refs.videoElement;
|
|
|
+ if (videoElement) {
|
|
|
+ videoElement.src = '';
|
|
|
+ videoElement.removeAttribute('src');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ this.destroyPlayer();
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.video-container {
|
|
|
+ width: 100%;
|
|
|
+ max-width: 1200px;
|
|
|
+ margin: 0 auto;
|
|
|
+ font-family: 'Roboto', sans-serif;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.video-wrapper {
|
|
|
+ width: 100%;
|
|
|
+ position: relative;
|
|
|
+ background-color: #000;
|
|
|
+ border-radius: 4px 4px 0 0;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.video-player {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 500px;
|
|
|
+ display: block;
|
|
|
+ background-color: #000;
|
|
|
+}
|
|
|
+
|
|
|
+.control-panel {
|
|
|
+ width: 100%;
|
|
|
+ padding: 16px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 0 0 4px 4px;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.format-decoder-options {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ justify-content: space-between;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+
|
|
|
+.format-selector {
|
|
|
+ display: flex;
|
|
|
+ gap: 16px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.format-label {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #424242;
|
|
|
+}
|
|
|
+
|
|
|
+.format-label input {
|
|
|
+ margin-right: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.decoder-options {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.decoder-label {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ cursor: pointer;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #424242;
|
|
|
+ margin-right: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.decoder-label input {
|
|
|
+ margin-right: 6px;
|
|
|
+}
|
|
|
+
|
|
|
+.video-controls {
|
|
|
+ display: flex;
|
|
|
+ gap: 12px;
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.video-url-input {
|
|
|
+ flex: 1;
|
|
|
+ padding: 12px;
|
|
|
+ border-radius: 4px;
|
|
|
+ border: 1px solid #e0e0e0;
|
|
|
+ font-size: 14px;
|
|
|
+ background-color: #fff;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ outline: none;
|
|
|
+ box-sizing: border-box;
|
|
|
+}
|
|
|
+
|
|
|
+.video-url-input:focus {
|
|
|
+ border-color: #6200ee;
|
|
|
+ box-shadow: 0 0 0 2px rgba(98, 0, 238, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.load-button {
|
|
|
+ padding: 10px 24px;
|
|
|
+ background-color: #6200ee;
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.2s ease;
|
|
|
+ text-transform: uppercase;
|
|
|
+ letter-spacing: 0.5px;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.button-text {
|
|
|
+ margin-left: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.load-button:hover {
|
|
|
+ background-color: #7c4dff;
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.load-button:active {
|
|
|
+ background-color: #5600e8;
|
|
|
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+@media (max-width: 768px) {
|
|
|
+ .format-decoder-options {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .format-selector, .decoder-options {
|
|
|
+ flex-wrap: wrap;
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .video-controls {
|
|
|
+ flex-direction: column;
|
|
|
+ }
|
|
|
+
|
|
|
+ .load-button {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|