Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-dml
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-dml
Commits
53e29aa7
提交
53e29aa7
authored
7月 13, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 优化直播功能,支持上传会话管理和流控制
上级
28f4122c
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
83 行增加
和
24 行删除
+83
-24
Live.vue
src/modules/live/test/components/Live.vue
+78
-21
useLive.ts
src/modules/live/test/composables/useLive.ts
+5
-3
没有找到文件。
src/modules/live/test/components/Live.vue
浏览文件 @
53e29aa7
...
...
@@ -70,7 +70,10 @@ const { start: startSpeechTranscriber, stop: stopSpeechTranscriber } = useSpeech
})
const
isOSSUpload
=
true
let
ossResult
=
null
let
uploadSession
=
{
result
:
null
,
queue
:
Promise
.
resolve
(),
}
let
totalSize
=
0
const
{
...
...
@@ -78,6 +81,7 @@ const {
start
:
startLive
,
stop
:
stopLive
,
restartStream
,
stopStream
,
startTime
,
endTime
,
duration
,
...
...
@@ -85,9 +89,15 @@ const {
switchCamera
,
isMobile
,
}
=
useLive
({
enabledUserMedia
:
false
,
keepChunks
:
props
.
isLocalUpload
,
onStart
:
()
=>
{
console
.
log
(
'开始直播'
)
uploadSession
=
{
result
:
null
,
queue
:
Promise
.
resolve
(),
}
totalSize
=
0
props
.
onStart
&&
props
.
onStart
()
startChat
()
if
(
!
props
.
isLocalUpload
)
{
...
...
@@ -98,11 +108,20 @@ const {
totalSize
+=
blob
.
size
if
(
props
.
isLocalUpload
)
return
if
(
isOSSUpload
)
{
ossResult
=
await
appendUpload
(
blob
,
{
name
:
`live/experiment_
${
experimentId
.
value
}
/user_
${
ssoId
}
/
${
fileName
.
value
}
.mp4`
,
position
:
ossResult
?.
nextAppendPosition
,
})
fileUrl
.
value
=
ossResult
.
url
const
session
=
uploadSession
const
name
=
`live/experiment_
${
experimentId
.
value
}
/user_
${
ssoId
}
/
${
fileName
.
value
}
.mp4`
session
.
queue
=
session
.
queue
.
then
(
async
()
=>
{
session
.
result
=
await
appendUpload
(
blob
,
{
name
,
position
:
session
.
result
?.
nextAppendPosition
,
})
if
(
uploadSession
===
session
)
fileUrl
.
value
=
session
.
result
.
url
})
.
catch
((
error
)
=>
{
console
.
error
(
'OSS append upload failed:'
,
error
)
})
await
session
.
queue
}
else
{
const
base64Data
=
await
readBlobAsBase64
(
blob
)
const
jsonData
=
JSON
.
stringify
({
...
...
@@ -200,31 +219,67 @@ watchEffect(() => {
})
const
enabled
=
ref
(
false
)
const
toggleLive
=
()
=>
{
enabled
.
value
?
startLive
()
:
stopLive
()
}
watch
(
enabled
,
toggleLive
)
const
starting
=
ref
(
false
)
let
operationId
=
0
let
disposed
=
false
async
function
start
()
{
if
(
!
video
.
value
)
return
if
(
window
.
parent
!==
window
)
{
window
.
parent
.
postMessage
({
action
:
'monitor-pause'
},
'*'
)
// 等父页面释放摄像头后再重新获取
await
new
Promise
((
r
)
=>
setTimeout
(
r
,
500
))
if
(
!
video
.
value
||
enabled
.
value
||
starting
.
value
)
return
const
currentOperationId
=
++
operationId
starting
.
value
=
true
try
{
if
(
window
.
parent
!==
window
)
{
await
new
Promise
((
resolve
)
=>
{
const
timeout
=
window
.
setTimeout
(
done
,
1000
)
function
done
()
{
window
.
clearTimeout
(
timeout
)
window
.
removeEventListener
(
'message'
,
handleMonitorPaused
)
resolve
()
}
function
handleMonitorPaused
(
event
)
{
if
(
event
.
source
===
window
.
parent
&&
event
.
data
?.
action
===
'monitor-paused'
)
done
()
}
window
.
addEventListener
(
'message'
,
handleMonitorPaused
)
window
.
parent
.
postMessage
({
action
:
'monitor-pause'
},
'*'
)
})
}
if
(
disposed
||
currentOperationId
!==
operationId
)
return
await
restartStream
()
if
(
disposed
||
currentOperationId
!==
operationId
)
{
stopStream
()
return
}
startLive
()
enabled
.
value
=
true
}
catch
(
error
)
{
stopStream
()
console
.
error
(
'Failed to start live stream:'
,
error
)
ElMessage
.
error
(
'摄像头启动失败,请检查摄像头权限后重试'
)
if
(
window
.
parent
!==
window
)
window
.
parent
.
postMessage
({
action
:
'monitor-resume'
},
'*'
)
}
finally
{
if
(
currentOperationId
===
operationId
)
starting
.
value
=
false
}
enabled
.
value
=
true
}
function
stop
()
{
if
(
!
video
.
value
)
return
operationId
+=
1
starting
.
value
=
false
if
(
enabled
.
value
)
stopLive
()
enabled
.
value
=
false
stopStream
()
if
(
window
.
parent
!==
window
)
{
// 等录制完全停止后通知父页面恢复监控
setTimeout
(()
=>
{
window
.
parent
.
postMessage
({
action
:
'monitor-resume'
},
'*'
)
},
500
)
window
.
parent
.
postMessage
({
action
:
'monitor-resume'
},
'*'
)
}
}
onUnmounted
(()
=>
{
disposed
=
true
stop
()
})
defineExpose
({
enabled
,
start
,
stop
})
</
script
>
...
...
@@ -266,6 +321,8 @@ defineExpose({ enabled, start, stop })
size=
"large"
round
class=
"live-ft-btn"
:loading=
"starting"
:disabled=
"starting"
@
click=
"enabled ? stop() : start()"
v-if=
"!recordId"
>
{{
enabled
?
'结束直播'
:
'开始直播'
}}
...
...
src/modules/live/test/composables/useLive.ts
浏览文件 @
53e29aa7
...
...
@@ -33,7 +33,7 @@ export function useLive({ enabledUserMedia = true, keepChunks = true, onStart, o
const
currentTime
=
ref
(
0
)
// 获取设备列表并设置默认设备
const
{
videoInputs
:
cameras
,
audioInputs
:
microphones
}
=
useDevicesList
({
requestPermissions
:
true
})
const
{
videoInputs
:
cameras
,
audioInputs
:
microphones
}
=
useDevicesList
({
requestPermissions
:
enabledUserMedia
})
const
currentCamera
=
computed
(()
=>
cameras
.
value
[
0
]?.
deviceId
)
const
currentMicrophone
=
computed
(()
=>
microphones
.
value
[
0
]?.
deviceId
)
...
...
@@ -53,7 +53,7 @@ export function useLive({ enabledUserMedia = true, keepChunks = true, onStart, o
})
// 创建媒体流
const
{
stream
,
restart
:
restartStream
}
=
useUserMedia
({
const
{
stream
,
restart
:
restartStream
,
stop
:
stopStream
}
=
useUserMedia
({
enabled
:
enabledUserMedia
,
constraints
:
constraints
as
any
,
})
...
...
@@ -110,12 +110,13 @@ export function useLive({ enabledUserMedia = true, keepChunks = true, onStart, o
const
start
=
()
=>
{
if
(
!
mediaRecorder
)
initializeMediaRecorder
()
recordedChunks
.
value
=
[]
mediaRecorder
?.
start
(
2000
)
// 每100ms触发一次dataavailable事件
mediaRecorder
?.
start
(
3000
)
}
// 停止录制
const
stop
=
()
=>
{
if
(
mediaRecorder
)
mediaRecorder
.
stop
()
mediaRecorder
=
null
}
// 切换摄像头(移动端前置/后置)
...
...
@@ -131,6 +132,7 @@ export function useLive({ enabledUserMedia = true, keepChunks = true, onStart, o
start
,
stop
,
restartStream
,
stopStream
,
startTime
,
endTime
,
duration
,
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论