提交 98b49f36 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 移除水印功能,优化直播监控的文件命名逻辑

上级 464c32ed
...@@ -21,79 +21,14 @@ export const readBlobAsBase64 = (blob: Blob): Promise<string> => { ...@@ -21,79 +21,14 @@ export const readBlobAsBase64 = (blob: Blob): Promise<string> => {
interface UseLiveProps { interface UseLiveProps {
enabledUserMedia?: boolean enabledUserMedia?: boolean
keepChunks?: boolean keepChunks?: boolean
watermark?: boolean
onStart?: () => void onStart?: () => void
onRecord?: (data: Blob) => void onRecord?: (data: Blob) => void
onStop?: (blob: Blob) => void onStop?: (blob: Blob) => void
} }
function formatTimestamp() {
const now = new Date()
const pad = (n: number) => String(n).padStart(2, '0')
return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
}
/** 创建时间水印覆盖层:video → canvas → canvasStream */
function createWatermark(cameraStream: MediaStream) {
const video = document.createElement('video')
video.muted = true
video.playsInline = true
video.srcObject = cameraStream
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')!
let animId: number | null = null
let _captureStream: MediaStream | null = null
let _ready: Promise<void>
const start = () => {
_ready = video.play().then(() => {
canvas.width = video.videoWidth || 640
canvas.height = video.videoHeight || 480
_captureStream = canvas.captureStream(30)
cameraStream.getAudioTracks().forEach((track) => _captureStream?.addTrack(track))
const draw = () => {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
const fontSize = Math.max(14, Math.floor(canvas.height / 36))
ctx.font = `bold ${fontSize}px monospace`
ctx.textBaseline = 'bottom'
const text = formatTimestamp()
const textWidth = ctx.measureText(text).width
const x = canvas.width - textWidth - 12
const y = canvas.height - 12
ctx.fillStyle = 'rgba(0, 0, 0, 0.4)'
ctx.fillRect(x - 6, y - fontSize - 4, textWidth + 12, fontSize + 8)
ctx.fillStyle = 'rgba(255, 255, 255, 0.9)'
ctx.fillText(text, x, y)
animId = requestAnimationFrame(draw)
}
if (video.readyState >= 1) draw()
else video.addEventListener('loadedmetadata', draw, { once: true })
})
}
const ready = () => _ready
const getStream = () => _captureStream
const stop = () => {
if (animId !== null) cancelAnimationFrame(animId)
if (_captureStream) {
_captureStream.getTracks().forEach((t) => t.stop())
_captureStream = null
}
video.pause()
video.srcObject = null
}
return { start, stop, getStream, ready }
}
export function useLive({ export function useLive({
enabledUserMedia = true, enabledUserMedia = true,
keepChunks = true, keepChunks = true,
watermark = false,
onStart, onStart,
onRecord, onRecord,
onStop, onStop,
...@@ -134,22 +69,11 @@ export function useLive({ ...@@ -134,22 +69,11 @@ export function useLive({
let mediaRecorder: MediaRecorder | null = null let mediaRecorder: MediaRecorder | null = null
let timeUpdateInterval: number | null = null let timeUpdateInterval: number | null = null
// 水印
let watermarkCtx: ReturnType<typeof createWatermark> | null = null
function getRecordStream(): MediaStream | null {
if (watermark && watermarkCtx) return watermarkCtx.getStream()
return stream.value ?? null
}
// 初始化MediaRecorder // 初始化MediaRecorder
const initializeMediaRecorder = async () => { const initializeMediaRecorder = () => {
// 水印模式:等待 canvas 流就绪 if (!stream.value) return
if (watermark && watermarkCtx) await watermarkCtx.ready()
const recordStream = getRecordStream()
if (!recordStream) return
const mimeType = MediaRecorder.isTypeSupported('video/mp4') ? 'video/mp4' : 'video/webm' const mimeType = MediaRecorder.isTypeSupported('video/mp4') ? 'video/mp4' : 'video/webm'
mediaRecorder = new MediaRecorder(recordStream, { mimeType }) mediaRecorder = new MediaRecorder(stream.value, { mimeType })
mediaRecorder.ondataavailable = handleDataAvailable mediaRecorder.ondataavailable = handleDataAvailable
mediaRecorder.onstart = handleStart mediaRecorder.onstart = handleStart
mediaRecorder.onstop = handleStop mediaRecorder.onstop = handleStop
...@@ -216,20 +140,6 @@ export function useLive({ ...@@ -216,20 +140,6 @@ export function useLive({
nextTick(() => initializeMediaRecorder()) nextTick(() => initializeMediaRecorder())
} }
// 水印生命周期(sync 确保 restart 后 start 前水印已就绪)
watch(stream, (newStream) => {
if (!watermark) return
if (watermarkCtx) watermarkCtx.stop()
if (newStream) {
watermarkCtx = createWatermark(newStream)
watermarkCtx.start()
}
}, { flush: 'sync' })
onUnmounted(() => {
if (watermarkCtx) watermarkCtx.stop()
})
return { return {
stream, stream,
start, start,
......
...@@ -50,7 +50,6 @@ export function useLiveMonitor({ ...@@ -50,7 +50,6 @@ export function useLiveMonitor({
const { startTime, start, stop, restart, stream } = useLive({ const { startTime, start, stop, restart, stream } = useLive({
enabledUserMedia: autoStart, enabledUserMedia: autoStart,
keepChunks: false, keepChunks: false,
watermark: true,
onStart: () => { onStart: () => {
uploadSession = { uploadSession = {
result: null, result: null,
...@@ -60,7 +59,8 @@ export function useLiveMonitor({ ...@@ -60,7 +59,8 @@ export function useLiveMonitor({
onRecord: (blob) => { onRecord: (blob) => {
if (isOSSUpload) { if (isOSSUpload) {
const session = uploadSession const session = uploadSession
const name = `date_${dateStr}/user_${ssoId}/${fileName.value}.mp4` const extension = blob.type.includes('webm') ? 'webm' : 'mp4'
const name = `date_${dateStr}/user_${ssoId}/${fileName.value}.${extension}`
session.queue = session.queue session.queue = session.queue
.then(async () => { .then(async () => {
session.result = await appendUpload(blob, { session.result = await appendUpload(blob, {
......
Markdown 格式
0% 或
您添加了 0 人 到此讨论。请谨慎行事。
请先完成此评论的编辑!
请 注册 或者 后发表评论