Python/Opencv

[Python] 비디오 녹화 및 코덱 H.264 변환 (with opencv / flask)

Jerry_K 2024. 5. 24. 21:33

🔍 비디오 녹화 및 코덱 변환

지금 캡스톤 프로젝트를 진행하는데, 비디오 녹화 기능이 필요하다.

녹화 기능은 한번도 안해봐서 좀 걱정했는데, 다행히 어렵지는 않았다.

 

비디오 녹화는 opencv 라이브러리를 통해 구현하고, 
flask로 만든 서버로 확인 할 예정이다. 

 

또한 코덱 H.264로 바꾸는 방법도 포스팅하려고 한다. 

이것은 선택적 필요하면 참고하면 된다

(Firebase에 영상을 저장하고 웹에 영상을 나타내려는 경우 하면 좋음)

 

이 다음 포스트는 firebase에 영상을 저장하고, 웹에 영상들을 나타내는 것 까지 할 예정이다.


🧐 코드 참고

 

Python OpenCV 영상 웹스트리밍 서버 – WANDLAB

우리가 작성한 OpenCV 코드를 테스트하기 위해서, 다음과 같이 윈도우 창을 호출합니다. 이 윈도우창은, Qt 나 GTK를 기반으로한 Window GUI를 OpenCV가 실행합니다. import cv2 import platform src = 0 if platform.sy

wandlab.com

 

opencv의 코드는 여기 블로그 기반으로 구성했다. 

그냥 opencv에서 imsow하는 것보다, 이렇게 코드를 구성하는게 전반적으로 빠르고 효율적이였다.


📚 비디오 녹화

📑 Streamer 클래스

참고한 코드와 중복이 되는 부분은 설명하지 않을 예정이다. ( 자세한 설명은 해당 참고 블로그에 자세하게 있음)

 

def bytescode(self):
        if not self.capture.isOpened():
            frame = self.blank()
        else :
            frame = imutils.resize(self.read(), width=int(self.width) )
        
            if self.stat :  
                cv2.rectangle( frame, (0,0), (120,30), (0,0,0), -1)
                fps = 'FPS : ' + str(self.fps())
                cv2.putText  ( frame, fps, (10,20), cv2.FONT_HERSHEY_PLAIN, 1, (0,0,255), 1, cv2.LINE_AA)
        
        return cv2.imencode('.jpg', frame )[1].tobytes(), frame

 

우선 bytescode 함수 부분의 return 값에 frame을 추가했다.

(기존 코드에서는 encode한 값을 bytes로 변환하는 값만 return 함)

 

여기서 frame은 우리의 영상 정보가 담긴 행렬이라고 보면된다.

 

 

def get_filename(self):  # 비디오 파일이름 생성
        dt_now = datetime.datetime.now()
        dt_str = dt_now.strftime("%Y년%m월%d일_%H시%M분%S초")
        file_name = str(dt_str)
        
        fourcc = cv2.VideoWriter_fourcc(*'DIVX')
        out = cv2.VideoWriter(f"{file_name}.mp4", fourcc, 20.0, (640, 480))
        return out

 

그 다음 get_filename 함수를 추가시켰다. 

이 함수가 호출 될 때의 시간을 파일명으로 쓰기위해 str로 변환시켜 주었다. (파일명으로 불가능한 str들 조심) 

 

그리고 mp4 확장자로 동영상 파일을 얻기위해 ,  위와 같은 파라미터를 주었다. (20 fps, (640*480px))

(Firebase Strage에 업로드 하려면 DIVX 코덱 방식 말고 H264 해야 함 (아래 참고) )

 

 

📑 Flask sever

@app.route("/")
def index(): 
...

def stream_gen( src ):   
    try :
        
        # 비디오 녹화와 종류를 위한 변수
        start_time = time.time()  
        recording = True

        out = streamer.get_filename()
        streamer.run(src)
    
        while True :
           
            frame_byte,frame = streamer.bytescode()

            # if 응급상황 : recording = True / out = streamer.get_filename()

            if recording:  # 비디오 저장 (여기 recording에 조건 걸기)
                out.write(frame)
                if time.time() - start_time > 10:
                    print(f"녹화완료")
                    recording = False
          
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + frame_byte + b'\r\n')
                    
    except GeneratorExit :
        streamer.stop()


if __name__ == "__main__":
    app.run(port=1500,host="0.0.0.0",debug=True)

 

우선 out 값을 Streamer 클래스의 함수 get_filename로 미리 선언해준다.

 

streamer.bytescode() 함수를 호출하면 리턴값으로  bytes, frame이 오는 것을 streamer 클래스를 통해 알 수 있다.

 

아래 조건문은 비디오 녹화를 위한 코드이다.

리턴값으로 받은 frame을 out.write() 함수의 매개변수로 넣어준다.

녹화 시간은 시작 후 약 10초동안만 진행되고, 이후에는 recoding 의 값을 False로 두어 녹화를 종료한다.

(이 조건은 내기 원하는대로 설정하면 된다.)

 

 

📌실행 결과

 

해당 url로 접속하면 스트림이 되는 것을 확인 할 수 있다.   

(http://192.168.0.20:1500/stream)  

만일 port를 다르게 구성한 경우 port 번호를 맞춰줘야한다.

 

 

 

약 10초 정도 지나니 녹화도 잘 되는 것을 확인했다.

 


📚 비디오 코덱 H.264 변환

영상 재생 기능은 브라우저 또는 사용자의 장치에 달려있다.

일반적으로 웹 브라우저나 모바일 장치의 미디어 플레이어는 특정 코덱만 지원하는데,

H.264는 현재 대부분의 브라우저와 장치에 지원되는 국제 표준 비디오 코덱이다. 

 

앞서 쓴 코드는 "DIVX" 방식으로 mp4를 코덱한다.   

이 비디오 파일을 firebase storage에 업로드하면, 브라우저 미디어 플레이어 지원을 하지 않아 재생이 안된다.

이것을 해결하려면 비디오 녹화를  H.264 코덱으로 해야한다.

 

📑 FFmpeg 다운로드 

 

위에 작성했던 코드에 코덱 형식만 H.264로 바꿔서 작성하면 이런 에러가 발생한다. 

이것은 FFmpeg가 시스템 변수 path에 없기 때문에 발생하는 에러이다.

따라서 FFmpeg을 다운받고 path에 경로를 넣어줘야한다.

 

 

https://ffmpeg.org/download.html

 

Download FFmpeg

If you find FFmpeg useful, you are welcome to contribute by donating. More downloading options Git Repositories Since FFmpeg is developed with Git, multiple repositories from developers and groups of developers are available. Release Verification All FFmpe

ffmpeg.org

 

 

해당 URL에 들어가서 자신의 OS에 맞는 파일을 다운로드 받는다. 

나는 "windows builds from gyan.dev" -> "ffmpeg-2024-05-20-git-127ded5078-full_build.7z"를 다운로드 받았다.

(essentials을 다운 받으면 에러가 발생 할 수 있으니  full 버전을 다운 받는것을 권장한다.)

 


🔑 7Z, bz2 확장자 압축 해제 (선택사항)

내 컴퓨터가 7z / bz2 (아래에 나올 OpenH264 라이브러리 파일) 확장자들이 압축이 안되는 경우 진행하면 된다.

메인 컴퓨터로는 이 부분을 진행하지 않았지만, 서브에서는 설치해야 했다.

(해당 확장자의 압축은 일반 폴더의 압축 풀기처럼 되지 않는다.)

 

https://www.7-zip.org/

 

7-Zip

7-Zip 7-Zip is a file archiver with a high compression ratio. Download 7-Zip 24.05 (2024-05-14) for Windows x64 (64-bit): Link Type Windows Size Download .exe 64-bit x64 1.6 MB Download 7-Zip 24.05 for another Windows platforms (32-bit x86 or ARM64): Link

www.7-zip.org

 

위의 링크의 파일(문제의 확장자들 압축해제)을 다운로드 받는다.

 

 

 

그리고 아까 다운 받은 " ffmpeg-2024-05-20-git-127ded5078-full_build.7z" 파일을 우클릭하여

"여기에 압축 풀기"를 하면된다.

 

 

 

잘 압축이 해제되었으니, 다시 아래 과정으로 돌아가면 된다.

(아래 과정과 동일함을 위해 압축을 푼 폴더를 "C:\"  경로로 이동 시켰다. 이 부분은 자유롭게 하면 된다.)

 

 

또한  추후에 다운받을 "openh264-1.8.0-win64.dll.bz2"  파일도  이와 같은 방법으로 압축해제하여,

"openh264-1.8.0-win64.dll" 파일로 만들면 된다.


 

 

 

ffmpeg의 경로 설정 편의성을 위해, c드라이브 바로 아래 경로에 압출을 풀어주었다.

 

 

 

윈도우 검색창에 "환경 변수 편집"를 찾으면 위와 같은 창이 뜬다.

여기에서 "환경 변수(N)"을 눌러준다.

 

 

 

"시스템 변수(S)" 쪽에 "Path"를 더블 클릭해준다.

 

 

 

"새로 만들기(N)"을 하고,  아까 다운로드 받은 ffmpeg 폴더 안 bin 파일의 경로를 넣어준다.

 

 

ffmpeg -version

 

다운로드 받고 환경변수 설정이 잘 됐나 확인하기 위해,  터미널에 들어가서 ffmpeg -version을 확인해보았다.

위와 같이 뜨면 잘 설정 된것이다. 

 

 

ffmpeg -i 2.mp4  # firebase에 재생 안된 비디오

 

또한  ffmpeg -i "파일경로" 를 하면, 해당 파일에 대한 정보를 얻어 올 수있다.

2.mp4는 재생이 안되는 비디오로, 해당 명령어로 파일의 코덱을 확인해보았다.

 

( 현재 내 로컬 환경 firebase에서 동영상 재생이 되는 코덱은 H264이고 그 외의 코덱(mp4v,DIVX 등등)은 안된다.)

 

 

📑OpenH264 라이브러리 설치

 

이렇게만 하면 될 줄 알았는데, 이게 끝이 아니였다.

코덱형식을  H.264로하여 다시 비디오 녹화를 해보았는데, 위와 같은 에러가 발생했다.

( " openh264-1.8.0-wind64.dll " 파일을 업로드 실패 )

 

 

 

Releases · cisco/openh264

Open Source H.264 Codec . Contribute to cisco/openh264 development by creating an account on GitHub.

github.com

 

" openh264-1.8.0-wind64.dll " 파일은 바로 위 링크를 통해 다운로드 가능하다.

만일 "openh264-1.8.0-win64.dll.bz2"로 다운된 경우 압축해제를 해야한다.

(위에 "7Z, bz2 확장자 압축 해제" 참고)

 

나는 더 높은 버전을 넣었다가 안돼서"openh264-1.8.0-wind64.dll" 버전을 찾아서 다운받았다.

 

해당 링크 깃허브에서 " openh264-1.8.0-wind64.dll" 를 아무리 눌러도 다운로드 안 될 경우,

"openh264-1.8.0-wind64.dll"  주소 링크를 복사하고 위에 url 창에 넣어주면 다운로드 된다.

 

 

 

다운로드 받은 "openh264-1.8.0-wind64.dll"는 단일 파일로,

좀 전에 다운 받은 ffmpeg 폴더 안의 bin 파일에 위와 같이 넣어주면 된다.

 

 

fourcc = cv2.VideoWriter_fourcc(*'avc1') # 코덱 형식
out = cv2.VideoWriter(f"sdfsdf.mp4", fourcc, 20.0, (640, 480))

 

비디오 녹화에 썼던 코드 중 바꾼 코드만 가져왔다.

'avc1'가 비디오를  h264 코덱 형식으로 바꿔준다. (이 부분만 바뀜)

 

 

 

이제 작성한 비디오 녹화 코드를 실행하고, 녹화한 비디오 파일의 정보를 보면  h264로 변환 된 것을 확인 할 수 있다.

 

 

 

그리고 해당 비디오를 storage에 업로드해보면, 잘 재생이 되는 것을 확인 할 수 있다.