webcam Diary Side Project - front(1)

김민섭·2023년 1월 3일
0

퇴사이후 pc를 mac으로 변경하고(필자는 mac을 처음 사용) 여러 준비를(어려절차 ...) 하면서 포스팅이 조금 늦어졌습니다.

첫 포스팅 글과 같이 첫프로젝트로 하루 공부기록을 할수있는 webcam project를 진행 하려 합니다.

사용할 기술스택으로는

navigator API

navigator 메소드중 getUserMedia는
미디어 유형을 포함하는 트랙 을 생성하는 미디어 입력을 사용할 수 있는 권한을 사용자 에게 제공합니다 -> webcam의 정보를 제공

참고 (첨부)
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia

navigator 메소드중 getDisplayMedia는
디스플레이 또는 그 일부 창(현재 내가 보는 컴퓨터의 화면)의 콘텐츠를 스트리밍으로 제공합니다.

참고 (첨부)
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia

MediaStream Recording API

MediaStream Recording API 의 MediaRecorder인터페이스는 미디어를 쉽게 녹음할 수 있는 기능을 제공합니다. 생성자 를 사용하여 생성됩니다.

MediaRecorder()
MediaStream Recording API 의 해당 생성자 메소드는 레코드 에 지정된 새 MediaRecorder개체를 만들어줍니다.(위에 webcam 과 local window의 녹화본을 생성)

MediaRecorder.pause()
미디어 기록이 일시 중지되면 시작됩니다.
일시정지 기능

MediaRecorder.resume()
일시 중지된 후 미디어 기록을 다시 시작합니다.
일시정지후 재시작 기능

MediaRecorder.start()
기록 매체를 시작합니다. 이 메서드는 선택적으로 timeslice밀리초 단위의 값이 있는 인수를 전달할 수 있습니다. 이것이 지정되면 미디어는 하나의 큰 청크에 미디어를 기록하는 기본 동작이 아니라 해당 기간의 별도 청크로 캡처됩니다.
비디오 기록 시작

MediaRecorder.stop()
저장된 데이터 dataavailable의 마지막을 포함 하는 이벤트가 발생하는 지점에서 기록을 중지합니다 . Blob더 이상 기록이 발생하지 않습니다
비디오 기록 종료

참고 (첨부)
https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder

해당 webcam과 localwidow 화면을 MediaRecorder 객체를 통해 녹화하고
blob 타입으로 변환하여 파일을 떨군후 프로젝트내에 저장을 어떻게 시킬지 고민하다
client상에서는 해당 파일들을 저장하는것이 비효율적이고 기술적으로 힘들것으로 판단 하여
간단한 백엔드 구축을 통해 해당 클라이언트 상의 변환된 파일을 저장하기로 생각했습니다.
백엔드는 간단하게 json-server 와 express 중 고민을 하다 express 가 적합하다 판단을 하여 express js 로 구현할 생각입니다

express 설명 ( 참고 : 위키백과 )
https://ko.wikipedia.org/wiki/Express.js

ui는 material ui css framework를 채택했습니다.
https://mui.com/

우선 웹캠 부터 구현을 해봅니다.

	// NOTE :: video 인자의 접속할 레퍼런스를 생성
    const videoElement = useRef< HTMLVideoElement | null >( null )

	const option = {
        video:{
            autoPlay : true,
        }
    }

JSX 구문에 해당 레퍼런스를 선언

  <video className="webcam_content" { ...option.video } ref={ videoElement } />

웹캠의 정보와 해당이벤트 object를 담을 state 선언

    const [test, setTest] = useState<any[]>([])
    const [videoEvent,setVideoEvent] = useState<any> ()

웹캠을 실행할 함수를 선언


 const onReadCam = async () => {
      
        try {     
        //NOTE :: 출력값 선언 (video true audio false -> 소리 출력시 에는 true)
            const stream = await navigator.mediaDevices.getUserMedia( { video : true , audio : false } );
   
            if(videoElement.current){
   		// NOTE :: 선언한 webcam 스트리밍 데이터를 태그 레퍼런스에 주입
                
                videoElement.current.srcObject = stream ;

		// NOTE :: 선언한 webcam 스트리밍 데이터를 MediaRecorder에 주입(녹화 데이터 object )
      
                let reader = new MediaRecorder(stream);
                
                reader.ondataavailable = (event)=>{
                    
                    // 녹화 데이터(Blob)가 들어올 때마다 배열에 담아두기
                    setTest( test.concat(event.data) );
                }             
            

        // NOTE :: reader object를 담아 녹화 시작 중지 함수들을 추출하여 사용 하기위한 데이터 주입 
                setVideoEvent( reader )

                videoElement.current.onloadedmetadata = () => {
                    videoElement.current?.play();
                }
                                
            }

        } 

        catch (err) { 
            console.error(`Error: ${err}`);
        }

    }

해당 함수를 Effect에 선언하여 랜더링시 동작

useEffect(() => {
        onReadCam();
    }, [])

녹화 시작 , 종료 버튼 생성 및 JSX 구문 추가

event

 const onStart = () => {
 
        videoEvent.start()
        
    }



    const onEnd = () => {
        
        videoEvent.stop()

    }    
    
 jsx
    
    <button onClick={onStart}>start</button>
    <button onClick={onEnd}>end</button>

위와같이 작업을 진행하면

녹화종료후 파일이 정상적으로 저장되어있는지 확인하기위한 Test download 기능

event

    const onDownload = ()=>{
        
        let downloadDom = document.createElement("a")

        const downloadfile = new Blob([test[0]],{type:"video/avi"})

        const downloadUrl = window.URL.createObjectURL(downloadfile); 
    
        downloadDom.href = downloadUrl

        downloadDom.click();
        
        downloadDom.remove();
        
    }
    
 jsx
    
    <button onClick={onDownload}>download</button>

위와 같이 코드를 작성한 후 웹상에서 화면을 보면

위와 같이 정상적으로 화면이 출력됩니다.

이제 영상을 start 후 end 버튼을 클릭하여 영상을 녹화하고 download 버튼을 누르면


파일이 다운로드 되게됩니다.

실행하면


정상적으로 녹화된것을 확인할 수 있습니다.

웹캠 녹화기능을 구현했으니 이제 localwindow 출력 기능을 구현해보겠습니다.

방식은 webcam과 거의 동일합니다.

	// NOTE :: video 인자의 접속할 레퍼런스를 생성
     const captureElement = useRef< HTMLVideoElement | null >( null )

// NOTE :: display option
const displayMediaOptions : any = {
        video: {
          displaySurface: "window"
        },
        audio: false
    };

JSX 구문에 해당 레퍼런스를 선언

<video className="capture_content" { ...option.video } ref={ captureElement } />

웹캠의 정보와 해당이벤트 object를 담을 state 선언

    const [test2, setTest2] = useState<any[]>([])
    const captureElement = useRef< HTMLVideoElement | null >( null )

웹캠을 실행할 함수를 선언

const onReadDisplayCapture = async () =>{

        try {

            if( captureElement.current){
            
                // NOTE :: local window 데이터 
                
                const captureStream = await navigator.mediaDevices.getDisplayMedia(displayMediaOptions);
                captureElement.current.srcObject = captureStream ;
                
				// NOTE :: 선언한 webcam 스트리밍 데이터를 MediaRecorder에 주입(녹화 데이터 object )
                
                let reader = new MediaRecorder(captureStream);
                                
                reader.ondataavailable = (event)=>{    
                    //NOTE :: 녹화 데이터(Blob)가 들어올 때마다 배열에 담아두기
                    setTest2( test2.concat(event.data) );
                }             
				   // NOTE :: reader object를 담아 녹화 시작 중지 함수들을 추출하여 사용 하기위한 데이터 주입 
                setCaptureEvent( reader )

                captureElement.current.onloadedmetadata = () => {
                    videoElement.current?.play();
                }

            }



        } 
        catch (err) {
          console.error(`Error: ${err}`);
        }


    }

해당 함수를 Effect에 선언하여 랜더링시 동작

useEffect(() => {
        onReadDisplayCapture();
    }, [])

녹화 시작 , 종료 버튼 생성 및 JSX 구문 추가

event

 const onStart = () => {
 
        captureEvent.start()
        
    }

    const onEnd = () => {
        
        captureEvent.stop()

    }    
    
 jsx
    
    <button onClick={onStart}>start</button>
    <button onClick={onEnd}>end</button>

위와같이 작업을 진행하면

녹화종료후 파일이 정상적으로 저장되어있는지 확인하기위한 Test download 기능

event

    const onDownload = ()=>{
        
        let downloadDom = document.createElement("a")

        const downloadfile = new Blob([test2[0]],{type:"video/avi"})

        const downloadUrl = window.URL.createObjectURL(downloadfile); 
    
        downloadDom.href = downloadUrl

        downloadDom.click();
        
        downloadDom.remove();
        
    }
    
 jsx
    
    <button onClick={onDownload}>download</button>

코드를 작성했으니 이제 테스트 download를 하여 정상적을 동작하는지 체크 합니다.

출력할 local 화면 선택후

정상적으로 화면이 출력되는것을 확인합니다

이후 녹화를 시작하고 종료해서 동영상파일을 실행하면 정상적으로 출력되는것을 확인할수 있습니다.

영상을 출력하고 파일로 받아오는 테스트 작업을 끝냈으니

이제 해당 파일을 백엔드쪽으로 넘겨 저장하고 출력하는 작업을 진행하겠습니다.

back end
https://velog.io/@front_ms/webcam-Diary-Side-Project-back-end

위와 같이 back end 작업을 진행했고 다음글에선 ui 수정 및 기능구현을 진행하겠습니다.

profile
Code Smell을 향기롭게 하자

0개의 댓글