当前位置: 首页 > wzjs >正文

厚街网站建设费用谁可以教我做网站

厚街网站建设费用,谁可以教我做网站,网站域名做注册,湖南响应式网站建设价位在浏览器中实现屏幕录制通常使用 MediaStream API 和 MediaRecorder API。下面是一个完整的示例,展示如何在Web页面中录制屏幕,并提供下载功能。 功能 选择屏幕或窗口进行录制开始/暂停/停止录制录制完成后可下载视频 核心技术 navigator.mediaDevice…

在浏览器中实现屏幕录制通常使用 MediaStream API 和 MediaRecorder API。下面是一个完整的示例,展示如何在Web页面中录制屏幕,并提供下载功能。

功能

  • 选择屏幕或窗口进行录制
  • 开始/暂停/停止录制
  • 录制完成后可下载视频

核心技术

  1. navigator.mediaDevices.getDisplayMedia():获取屏幕媒体流

  2. MediaRecorder:录制流并保存为视频文件

界面截图:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

案例源码:

<template><div class="rdp-recorder" :class="{ 'minimal-ui': minimalUI }"><!-- 悬浮的最小化控制面板 - 适合无感录制 --><div v-if="minimalUI" class="floating-controls"><div class="recording-status"><span class="record-indicator" :class="{ paused: isPaused }"></span><span class="timer">{{ formatTime(recordingTime) }}</span></div><div class="button-group"><buttonv-if="isPaused"class="icon-btn resume-btn"title="继续录制"@click="resumeRecording"></button><buttonv-else-if="isRecording"class="icon-btn pause-btn"title="暂停录制"@click="pauseRecording"></button><buttonv-if="isRecording"class="icon-btn stop-btn"title="停止录制"@click="stopRecording"></button></div><button class="icon-btn expand-btn" title="展开面板" @click="minimalUI = false"></button></div><!-- 完整控制面板 --><div v-else class="full-controls"><div class="panel-header"><h3>RDP会话录制</h3><button class="icon-btn minimize-btn" title="最小化" @click="minimalUI = true"></button></div><div class="controls"><buttonv-if="!isRecording":disabled="!hasSupport"class="btn start-btn"@click="startRDPRecording">开始录制RDP会话</button><template v-else><buttonv-if="!isPaused"class="btn pause-btn"@click="pauseRecording">暂停</button><buttonv-elseclass="btn resume-btn"@click="resumeRecording">继续</button><buttonclass="btn stop-btn"@click="stopRecording">停止录制</button></template></div><div v-if="isRecording" class="recording-info"><div class="recording-status"><span class="record-indicator" :class="{ paused: isPaused }"></span><span>{{ isPaused ? '已暂停' : '正在录制RDP会话...' }}</span></div><div class="timer">{{ formatTime(recordingTime) }}</div></div><div v-if="recordingURL" class="preview"><h3>录制预览</h3><video :src="recordingURL" controls width="100%"></video><button class="btn download-btn" @click="downloadRecording('rdp-recording.webm')">下载录制文件</button></div><div v-if="error" class="error">录制错误: {{ error }}</div><div v-if="!hasSupport" class="no-support">您的浏览器不支持屏幕录制功能</div><div class="settings"><h4>录制设置</h4><div class="checkbox-group"><label><input v-model="autoMinimize" type="checkbox" />开始录制后自动最小化</label></div><div class="checkbox-group"><label><input v-model="recordAudio" type="checkbox" />录制音频</label></div><button class="btn auto-start-btn" @click="autoStartRecording">自动选择RDP窗口录制</button><p class="tip">提示: 自动选择功能需要您在弹出框中选择RDP窗口</p></div></div></div>
</template><script lang="ts" setup>import { ref, onMounted, watch } from 'vue';import { useRDPScreenRecorder } from './useRDPScreenRecorder';// 组件名称定义defineOptions({name: 'RDPRecorderComponent',});// 状态const hasSupport = ref(false);const minimalUI = ref(false);const autoMinimize = ref(true);const recordAudio = ref(true);// 引入录屏功能const {isRecording,isPaused,recordingTime,recordingURL,error,startRecording,autoStartRDPRecording,pauseRecording,resumeRecording,stopRecording,downloadRecording,} = useRDPScreenRecorder();// 检查浏览器支持onMounted(() => {hasSupport.value = !!(navigator.mediaDevices &&navigator.mediaDevices.getDisplayMedia);});// 在录制开始后自动最小化UIwatch(isRecording, (newValue) => {if (newValue && autoMinimize.value) {minimalUI.value = true;}});// 格式化时间显示const formatTime = (seconds: number): string => {const mins = Math.floor(seconds / 60);const secs = seconds % 60;return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;};// 开始RDP会话录制const startRDPRecording = () => {startRecording({videoConstraints: {video: {frameRate: { ideal: 15 },cursor: 'always',displaySurface: 'window', // 窗口模式,便于选择RDP窗口logicalSurface: true,width: { ideal: 1280 },height: { ideal: 720 },},},includeAudio: recordAudio.value,captureApplicationAudio: recordAudio.value,mimeType: 'video/webm;codecs=vp9',});};// 自动开始RDP会话录制const autoStartRecording = () => {autoStartRDPRecording();};
</script><style scoped>.rdp-recorder {position: relative;max-width: 800px;margin: 0 auto;padding: 20px;font-family: Arial, sans-serif;border-radius: 8px;background-color: #fff;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);}.floating-controls {position: fixed;bottom: 20px;right: 20px;display: flex;align-items: center;padding: 8px 12px;background-color: rgba(33, 33, 33, 0.8);border-radius: 50px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);z-index: 9999;color: white;}.recording-status {display: flex;align-items: center;gap: 8px;}.record-indicator {display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color: #F44336;animation: blink 1s infinite;}.record-indicator.paused {background-color: #FFC107;animation: none;}.button-group {display: flex;margin: 0 8px;}.icon-btn {background-color: transparent;border: none;color: white;width: 28px;height: 28px;border-radius: 50%;display: flex;align-items: center;justify-content: center;cursor: pointer;margin: 0 2px;font-size: 14px;}.icon-btn:hover {background-color: rgba(255, 255, 255, 0.2);}.minimize-btn, .expand-btn {font-size: 12px;}.panel-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 15px;}.panel-header h3 {margin: 0;}.controls {margin: 20px 0;display: flex;gap: 10px;}.btn {padding: 10px 15px;border: none;border-radius: 4px;cursor: pointer;font-weight: bold;transition: background-color 0.3s;}.start-btn {background-color: #4CAF50;color: white;}.pause-btn {background-color: #FFC107;color: #333;}.resume-btn {background-color: #2196F3;color: white;}.stop-btn {background-color: #F44336;color: white;}.download-btn {background-color: #673AB7;color: white;margin-top: 10px;}.auto-start-btn {background-color: #2196F3;color: white;}.btn:hover {opacity: 0.9;}.btn:disabled {background-color: #cccccc;cursor: not-allowed;}.recording-info {display: flex;align-items: center;justify-content: space-between;margin: 20px 0;padding: 10px;background-color: #f5f5f5;border-radius: 4px;}.timer {font-family: monospace;font-size: 1.2rem;}.floating-controls .timer {font-size: 1rem;}.preview {margin-top: 20px;}.error {color: #F44336;margin: 10px 0;padding: 10px;background-color: #FFEBEE;border-radius: 4px;}.no-support {color: #F44336;margin: 10px 0;padding: 10px;background-color: #FFEBEE;border-radius: 4px;}.settings {margin-top: 20px;padding: 15px;background-color: #f5f5f5;border-radius: 4px;}.settings h4 {margin-top: 0;margin-bottom: 15px;}.checkbox-group {margin-bottom: 10px;}.tip {font-size: 12px;color: #666;margin-top: 10px;}@keyframes blink {0% { opacity: 1; }50% { opacity: 0.4; }100% { opacity: 1; }}
</style>
// useRDPScreenRecorder.ts
import { ref, onUnmounted } from 'vue';export function useRDPScreenRecorder() {const isRecording = ref<boolean>(false);const isPaused = ref<boolean>(false);const recordingTime = ref<number>(0);const recordingURL = ref<string>('');const error = ref<string | null>(null);let mediaRecorder: MediaRecorder | null = null;let recordedChunks: Blob[] = [];let startTime = 0;let timerInterval: number | null = null;let combinedStream: MediaStream | null = null;interface RecordingOptions {videoConstraints?: MediaStreamConstraints;audioConstraints?: MediaStreamConstraints;mimeType?: string;timeslice?: number;includeAudio?: boolean;captureApplicationAudio?: boolean;}// 开始录制屏幕和音频const startRecording = async (options: RecordingOptions = {}) => {const defaultOptions: RecordingOptions = {videoConstraints: {video: {// 针对RDP窗口的优化设置frameRate: { ideal: 15 }, // 降低帧率以减少RDP连接的负担cursor: 'always', // 始终捕获光标displaySurface: 'window', // 优先尝试捕获窗口而非整个屏幕logicalSurface: true, // 捕获逻辑窗口表面而非物理显示width: { ideal: 1920 }, // 设置理想分辨率height: { ideal: 1080 },},},audioConstraints: {audio: {// 音频设置优化echoCancellation: true, // 回声消除noiseSuppression: true, // 噪声抑制autoGainControl: true, // 自动增益控制},},mimeType: 'video/webm;codecs=vp9', // VP9编码器在低带宽环境下表现更好timeslice: 1000, // 每秒获取一次数据includeAudio: true, // 是否包含麦克风音频captureApplicationAudio: true, // 是否尝试捕获应用程序音频};const config = { ...defaultOptions, ...options };recordedChunks = [];error.value = null;try {// 获取屏幕流,用户在选择对话框中应该选择RDP窗口const screenStream = await navigator.mediaDevices.getDisplayMedia(config.videoConstraints);// 处理录制结束screenStream.getVideoTracks()[0].onended = () => {stopRecording();};let streams = [screenStream];// 尝试捕获系统音频(RDP会话中的声音)// 注意:这依赖于浏览器和系统支持,可能不适用于所有环境if (config.captureApplicationAudio) {try {// 在某些浏览器中,系统音频可以通过getDisplayMedia的audio选项获取// 检查是否已经有音频轨道const hasAudioTrack = screenStream.getAudioTracks().length > 0;if (!hasAudioTrack) {console.warn('无法直接捕获RDP会话的系统音频,这在某些浏览器中是正常现象');}} catch (err) {console.warn('捕获系统音频失败:', err);}}// 如果需要麦克风音频,获取麦克风流if (config.includeAudio) {try {const audioStream = await navigator.mediaDevices.getUserMedia(config.audioConstraints);streams.push(audioStream);} catch (err) {console.warn('无法获取麦克风权限,继续仅录制视频', err);}}// 合并流combinedStream = new MediaStream();// 添加所有视频轨道streams.forEach(stream => {stream.getVideoTracks().forEach(track => {combinedStream.addTrack(track);});});// 添加所有音频轨道streams.forEach(stream => {stream.getAudioTracks().forEach(track => {combinedStream.addTrack(track);});});// 创建MediaRecorder,优化RDP场景的录制设置const recorderOptions: MediaRecorderOptions = {};if (config.mimeType) {// 检查MIME类型支持if (MediaRecorder.isTypeSupported(config.mimeType)) {recorderOptions.mimeType = config.mimeType;} else if (MediaRecorder.isTypeSupported('video/webm')) {recorderOptions.mimeType = 'video/webm'; // 降级}}// 可以添加录制质量设置// 对于RDP场景,可以考虑降低视频比特率以减少资源占用// 初始化录制器mediaRecorder = new MediaRecorder(combinedStream, recorderOptions);// 处理数据可用事件mediaRecorder.ondataavailable = (event) => {if (event.data.size > 0) {recordedChunks.push(event.data);}};// 处理录制结束事件mediaRecorder.onstop = () => {const mimeType = mediaRecorder?.mimeType || 'video/webm';const blob = new Blob(recordedChunks, { type: mimeType });recordingURL.value = URL.createObjectURL(blob);isRecording.value = false;if (timerInterval) {clearInterval(timerInterval);timerInterval = null;}};// 开始录制mediaRecorder.start(config.timeslice);isRecording.value = true;isPaused.value = false;startTime = Date.now();// 启动计时器timerInterval = window.setInterval(() => {recordingTime.value = Math.floor((Date.now() - startTime) / 1000);}, 1000);} catch (err: any) {error.value = err.message || '录制失败';console.error('录制失败:', err);}};// 自动开始录制RDP窗口(无感知方式)const autoStartRDPRecording = async () => {try {// 尝试自动开始录制,但需要用户交互触发// 建议在用户点击页面其他地方或进行其他交互后调用await startRecording({videoConstraints: {video: {// 针对RDP会话的优化设置frameRate: { ideal: 10 }, // 降低帧率可减轻网络负担cursor: 'always',displaySurface: 'window', // 设置为窗口模式,方便用户选择RDP窗口logicalSurface: true,// 减少分辨率可以降低CPU使用率width: { ideal: 1280 },height: { ideal: 720 },},},// 降低录制频率,减少资源占用timeslice: 2000,captureApplicationAudio: true,});console.log('RDP录制已自动开始,请在弹出的对话框中选择RDP窗口');} catch (err) {console.error('自动开始RDP录制失败:', err);}};// 暂停录制const pauseRecording = () => {if (mediaRecorder && isRecording.value && !isPaused.value && mediaRecorder.state === 'recording') {mediaRecorder.pause();isPaused.value = true;if (timerInterval) {clearInterval(timerInterval);timerInterval = null;}}};// 继续录制const resumeRecording = () => {if (mediaRecorder && isRecording.value && isPaused.value && mediaRecorder.state === 'paused') {mediaRecorder.resume();isPaused.value = false;// 重启计时器timerInterval = window.setInterval(() => {recordingTime.value = Math.floor((Date.now() - startTime) / 1000);}, 1000);}};// 停止录制const stopRecording = () => {if (mediaRecorder && isRecording.value &&(mediaRecorder.state === 'recording' || mediaRecorder.state === 'paused')) {mediaRecorder.stop();// 停止所有轨道if (combinedStream) {combinedStream.getTracks().forEach(track => track.stop());}if (timerInterval) {clearInterval(timerInterval);timerInterval = null;}}};// 下载录制内容const downloadRecording = (filename = 'rdp-recording.webm') => {if (recordingURL.value) {const a = document.createElement('a');document.body.appendChild(a);a.style.display = 'none';a.href = recordingURL.value;a.download = filename;a.click();document.body.removeChild(a);}};// 清理函数onUnmounted(() => {stopRecording();if (recordingURL.value) {URL.revokeObjectURL(recordingURL.value);}});return {isRecording,isPaused,recordingTime,recordingURL,error,startRecording,autoStartRDPRecording,pauseRecording,resumeRecording,stopRecording,downloadRecording,};
}

文章转载自:

http://tAbqpWEG.fxqjz.cn
http://Ab1Tt4vm.fxqjz.cn
http://4Hi4Kefe.fxqjz.cn
http://yLGc446y.fxqjz.cn
http://8ajL9VWf.fxqjz.cn
http://yGrMWF39.fxqjz.cn
http://iZsPPVBd.fxqjz.cn
http://1PfJ0fX2.fxqjz.cn
http://JC4VXric.fxqjz.cn
http://qheLpFBD.fxqjz.cn
http://hwX9jsvK.fxqjz.cn
http://mRiHg2ED.fxqjz.cn
http://ZlNw99I8.fxqjz.cn
http://fkCDNqQp.fxqjz.cn
http://5vMfYhJn.fxqjz.cn
http://42CX0DEs.fxqjz.cn
http://HqrPMbB7.fxqjz.cn
http://RWxgKy71.fxqjz.cn
http://BzFstwZc.fxqjz.cn
http://RwXpnCWT.fxqjz.cn
http://xMlf8ihf.fxqjz.cn
http://G4iqG7E8.fxqjz.cn
http://kCn7z3Rs.fxqjz.cn
http://HUlWnyy0.fxqjz.cn
http://JAfy3uZN.fxqjz.cn
http://Syv2HyAv.fxqjz.cn
http://kfJWwoRW.fxqjz.cn
http://B24u2ZGU.fxqjz.cn
http://q0otmPYo.fxqjz.cn
http://KdL7KZrK.fxqjz.cn
http://www.dtcms.com/wzjs/739341.html

相关文章:

  • 婚庆行业网站建设电子商务网站设计
  • 微信网站建设报价单wordpress加备案号
  • 代码网站怎么做的扬州个人做网站
  • iis两个网站做ssl旅游电子商务网站建设与研究
  • 网站集约化建设情况给wordpress替换主题
  • 湛江手机建站模板江西省城乡建设培训网官方网站
  • 建设公众号官方网站域名数和网站数
  • 电子政务建设网站图片优秀原创设计网站
  • 百度营消 营销推广吴江seo
  • 养老网站建设 中企动力青岛网站设计定制
  • 想给孩子找点题做 都有什么网站知乎 淘宝网站建设
  • 做展示型网站便宜吗齐鲁人才网泰安最新招聘信息
  • 珠海做网站的直播间网站建设
  • 网站怎么建设及推广备案不关闭网站的方法
  • 做环保要知道的几个网站学术网站建设
  • 做shopify网站建网站要租服务器吗
  • 网站建设网络推广方案ppt网站备案审核
  • 做淘宝代销哪个网站好广州番禺职业技术学院
  • 哪个网站的织梦源码好口碑营销的概念
  • 做网站的上海市哪家技术好淘宝上网站开发
  • 沛县专业做网站全国各大知名网站
  • 可信赖的邢台做网站互联网创业项目代理
  • 网站栏目规划叫什么wordpress免登录付费阅读
  • 品牌手机网站开发注册域名com和cn
  • dede网站模板网站开发文档word
  • vs手机网站开发app推广拉新平台
  • 自动网站建设系统cms广告创意与设计
  • ssh小型购物网站开发外国人注册公司需要什么条件
  • 如何打死网站平面设计app软件有哪些
  • 二道江网站建设华亮建设集团公司主页