Skip to content

Commit

Permalink
Merge pull request #234 from determaer/main
Browse files Browse the repository at this point in the history
fix: VMR v2 - screen recorder, AMR style
  • Loading branch information
antirek authored Feb 12, 2025
2 parents 6b38baa + 8b070dc commit 4163216
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 37 deletions.
60 changes: 35 additions & 25 deletions src/library/components/AudioRecorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@
class="audio-recorder__recording-container"
>
<span class="audio-recorder__recording-icon pi pi-circle-fill"/>
<span class="audio-recorder__recording-time">
{{elapsedTime}}
</span>

<button
class="audio-recorder__button audio-recorder__button-record"
@click="cancelAudioRecording"
Expand All @@ -25,13 +21,16 @@
class="audio-recorder__button audio-recorder__button-record"
@click="stopAudioRecording"
>
<span class="pi pi-stop" />
<div class="audio-recorder__stop"></div>
</button>
<span class="audio-recorder__recording-time">
{{elapsedTime}}
</span>
</div>
<button
v-if="!audioRecording && uploadStatus != 'uploading' && !getMessage().isRecording"
class="audio-recorder__button"
:class="{'audio-recorder__button-disabled' : state == 'disabled'}"
:class="{'audio-recorder__button-disabled' : state == 'disabled' || getMessage().file}"
@click="startAudioRecording"
>
<span class="pi pi-microphone" />
Expand Down Expand Up @@ -93,28 +92,30 @@ const props = defineProps({
})
const startAudioRecording = async () => {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
timer.value = setInterval(() => {
ms.value += 10;
if(ms.value == 1000){
ms.value = 0;
s.value++;
if(s.value == 60){
s.value = 0;
m.value++;
if(m.value == 60){
m.value = 0;
h.value++;
if (!getMessage().file && props.state == 'active'){
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
timer.value = setInterval(() => {
ms.value += 10;
if(ms.value == 1000){
ms.value = 0;
s.value++;
if(s.value == 60){
s.value = 0;
m.value++;
if(m.value == 60){
m.value = 0;
h.value++;
}
}
}
}, 10)
setRecordingMessage(true)
audioRecording.value = true
mediaRecorder.value = new MediaRecorder(stream)
mediaRecorder.value.start();
mediaRecorder.value.ondataavailable = (event: any) => {
chunks.value.push(event.data);
}
}, 10)
setRecordingMessage(true)
audioRecording.value = true
mediaRecorder.value = new MediaRecorder(stream)
mediaRecorder.value.start();
mediaRecorder.value.ondataavailable = (event: any) => {
chunks.value.push(event.data);
}
}
Expand Down Expand Up @@ -207,6 +208,15 @@ watch(
-webkit-animation: blink 3s linear infinite;
animation: blink 3s linear infinite;
}
&__stop {
width: 20px;
height: 20px;
background-color: black;
cursor: pointer;
border-radius: 2px;
}
@-webkit-keyframes blink {
0% { color: red; }
50% { color: transparent; }
Expand Down
6 changes: 4 additions & 2 deletions src/library/components/VideoRecorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<button
v-if="uploadStatus != 'uploading'"
class="video-recorder__button"
:class="{'video-recorder__button-disabled' : state == 'disabled'}"
:class="{'video-recorder__button-disabled' : state == 'disabled' || getMessage().file}"
@click="openVideoRecorder"
>
<span class="pi pi-video" />
Expand Down Expand Up @@ -57,7 +57,8 @@ const props = defineProps({
})
const openVideoRecorder = async () => {
await useModalVideoRecorder()
if (!getMessage().file && props.state == 'active'){
await useModalVideoRecorder()
.then(async (data) => {
if (data.videoFile){
uploadStatus.value = 'uploading'
Expand Down Expand Up @@ -87,6 +88,7 @@ const openVideoRecorder = async () => {
})
}
})
}
}
const resetRecordedAudio = () => {
Expand Down
80 changes: 70 additions & 10 deletions src/library/modals/ModalVideoRecorder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@

<div>
<video
class='video-recorder__view-area'
v-show="!videoURL"
ref="refVideo"
/>
<video
class='video-recorder__view-area'
v-show="videoURL"
ref="refRecord"
controls
Expand All @@ -22,6 +24,18 @@
<div
class="video-recorder__recording-container"
>
<button
v-if="!videoRecording && !videoURL"
class="video-recorder__button"
@click="changeRecordSource"
>
<span
:class="{
'pi pi-desktop': recordSource == 'camera',
'pi pi-camera' : recordSource == 'screen'
}"
/>
</button>
<button
v-if="!videoURL"
class="video-recorder__button"
Expand Down Expand Up @@ -102,8 +116,29 @@ const chunks = ref<any[]>([])
const videoURL = ref<string>()
const videoFile = ref<File>()
const recordSource = ref('camera') //camera | screen
const changeRecordSource = () => {
if (recordSource.value == 'camera'){
recordSource.value = 'screen'
runIdleScreenVideo()
}
else if (recordSource.value == 'screen'){
stopCapture()
recordSource.value = 'camera'
runIdleVideo()
}
}
const startVideoRecording = async () => {
if (!videoRecording.value){
if (!videoRecording.value && stream.value instanceof MediaStream){
mediaRecorder.value = new MediaRecorder(stream.value)
mediaRecorder.value.start();
mediaRecorder.value.ondataavailable = (event: any) => {
chunks.value.push(event.data);
}
videoRecording.value = true
timer.value = setInterval(() => {
ms.value += 10;
if(ms.value == 1000){
Expand All @@ -119,20 +154,12 @@ const startVideoRecording = async () => {
}
}
}, 10)
videoRecording.value = true
if (stream.value instanceof MediaStream){
mediaRecorder.value = new MediaRecorder(stream.value)
mediaRecorder.value.start();
mediaRecorder.value.ondataavailable = (event: any) => {
chunks.value.push(event.data);
}
}
}
}
const cancelVideoRecording = async () => {
clearTemp()
stopCapture()
if (videoURL.value){
videoURL.value = undefined
await runIdleVideo()
Expand All @@ -152,8 +179,15 @@ const stopVideoRecording = () => {
}
}
clearTemp()
stopCapture()
}
function stopCapture() {
let tracks = stream.value?.getTracks()
tracks?.forEach((track) => track.stop());
}
const clearTemp = () => {
clearInterval(timer.value)
ms.value = 0
Expand All @@ -171,6 +205,7 @@ const saveRecordedVideo = () => {
emit('change', {videoFile: videoFile.value});
emit('submit')
}
const runIdleVideo = async () => {
await navigator.mediaDevices
.getUserMedia({ audio: true, video: true })
Expand All @@ -185,6 +220,26 @@ const runIdleVideo = async () => {
})
}
const runIdleScreenVideo = async () => {
const displayMediaOptions = {
video: {
cursor: 'always',
},
audio: false,
} as DisplayMediaStreamOptions;
await navigator.mediaDevices.getDisplayMedia(displayMediaOptions)
.then((s) => {
const v = unref(refVideo.value) as HTMLVideoElement
stream.value = s;
v.srcObject = s;
v.addEventListener("loadedmetadata", () => {
unref(refVideo.value)?.play()
});
v.muted = true;
})
}
onMounted(async () => {
await runIdleVideo()
})
Expand All @@ -201,6 +256,11 @@ onMounted(async () => {
background-color: var(--chat-input-container-bg);
}
&__view-area{
width: 640px;
height: 480px;
}
&__controls{
display: flex;
justify-content: space-between;
Expand Down

0 comments on commit 4163216

Please sign in to comment.