티스토리 뷰

728x90

오늘은 그동안 스트리밍 관련 작업을 고도화 하면서 바뀌었던 구조들과

구조를 바꾸면서 겪었던 문제들을 정리해보려고 합니다.

 

이번 작업을 통해서 AVCaptureSession을 깊게 공부할 수 있던 계기가 됐던것 같고,

동료 개발자분과 같이 Objective-C로 된 WebRTC 라이브러리 내부를 까보고 파악해볼 수 있던 계기가 됐던것 같습니다.

 


 

 

기존 구조

화면 구성

스트리밍 RIB 

움직임 감지 녹화 RIB

 

처음 구성은 아래의 RIB들로 구성되어있었다.

스트리밍 요청이 오면 스트리밍 역할을 하는 RIB을 attach,

스트리밍이 종료되면 움직임 감지 녹화를 하는 RIB을 attach 하는 방식으로 구현을 했었다.

 

변경이 필요했던 이유

매번 스트리밍 연결 → 종료 할 때마다 시그널링채널을 생성하게 되는 문제가 있었고,

불필요 시그널링 채널이 많아짐에 따라 길면 반기별, 짧으면 분기별로 주기적으로 수동적으로 삭제 필요했다.

그리고 RIB 전환과 스트리밍을 새로 생성하고 연결하기 때문에 연결시간이 최소 5초이상 걸리는 문제가 있었다.

 

 


두번째 구조

 

화면 구성

스트리밍 + 움직임감지

 

 

기대효과

- 스트리밍과 동시에 움직임 감지 녹화를 할 수 있게 됨

- 스트리밍 연결시간 최적화 (최소 연결까지 걸리는 시간 2~3초)

 

어려웠던점

- WebRTC에서 사용하는 Video, Audio 데이터로 영상을 저장해야했다.

- 비디오는 RTCVideoFrame → CMSampleBuffer로 변환해서 영상저장이 가능했다.

  - 오디오도 비슷한기능이 있는지 찾아봤지만, 오디오는 아무리 찾아봐도 없었다.

      여러가지로 찾아봤던 내역들

        - 새로운 라이브러리를 써라 (불안정한 라이브러리였고, 용량만 300MB가 넘었다.) (링크) 

 

Capturing a CMSampleBuffer using an RTCAudioSource on iOS

I'm trying to stream a CMSampleBuffer video / audio combo using WebRTC on iOS, but I'm running into trouble trying to capture audio. Video works just fine: guard let pixelBuffer =

stackoverflow.com

- 애용하는 discuss-webrtc (링크)  해결방법을 아는분이 질문자랑 메일로 연락을 따로 주고 받음.. ㅠ

 

iOS WebRTC: Record remote audio stream

I am working on an audio streaming application with recording functionality for a receiver. I got stuck at the point where the user wants to record audio stream on the receiver side.

groups.google.com

- WebRTC iOS: Record Remote Audio stream using WebRTC (해결방안이 없음) 등등

 

여러가지로 검색은 해봤지만 해결하지 못해 아래 방법으로 진행을 했었다. 

 

    - 임시해결방안으로 WebRTC에서 받아오는 오디오 데이터가 아닌, 오디오만 AVCaptureSession을 따로만들어 해당 오디오 데이터로 음성을 녹화하도록 구현.

 

그렇게 임시로 오디오 데이터는 아래와 같이 가져왔다.

 

구조 

비디오(RTCVideoFrame → CMSampleBuffer) 

오디오(CMSampleBuffer)

 

 

문제점

움직임 감지 녹화를 오래 켜둘수록 영상과 음성의 싱크가 점점 벌어져 맞지 않는 문제가 생겼다. 

CCTV앱 특성상 짧으면 몇시간 길면 몇달동안 오랫동안 켤 수 밖에 없어 치명적인 오류였다. 

 

싱크가 안맞는 상황을 그림으로 표시해보자면 아래와 같다.

 

=: 데이터가 있는 경우

x: 데이터가 없는 경우

 

비디오 ==============================xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

오디오 xxxxxxxxxxxxx===========================================

 

비디오가 먼저나오고 뒤이어 오디오가 나오지만 영상과 소리가 맞지 않았고,

영상 마지막쯤 비디오가 먼저 멈추고 오디오만 나오는 문제였다. 

 

 

해결하면서 가장 어려웠던점은 하루 이상 켜놔야 싱크가 안맞는게 체감이 돼 디버깅하기가 어려웠다.

몇시간동안은 Log를 찍어봐도 비디오, 오디오 두 데이터의 presentationTimestamp 값은 미세하게 차이가 있을뿐 체감되지는 않았다.

여러가지 사이드이펙트를 의심해보다가 아래와 같이 몇가지 시도를 해봤다.

 

 

시도해본것

1. 오디오가 밀리고있음으로 startSession에 audio CMSampleBuffer의 presentationTimestamp을 넣어봄. (해결x)

2. 비디오, 오디오 presentationTimeStamp 값이 동일하지않으면 데이터를 추가하지 않음 (해결x)

3. 비디오 데이터 RTCVideoFrame → CMSampleBuffer 변환작업에서 문제가 있는게 아닌지 확인 (해결x)

4. AVCaptureSession의 synchronizationClock 프로퍼티 사용 (문제 해결 접근방법에 도움이 됨) 

  - (기준점) 비디오의 captureSession.synchronizationClock이 필요했고 WebRTC에서도 captureSession을 사용하고 있다는걸 알 수 있게 됨 (해결x)

 

 

4번의 시도로 WebRTC라이브러리 오픈소스를 파헤쳐 봐야겠다는 생각이 들었고

Objective-C로 되어있긴했지만 동료 개발자분과 gpt로 Swift번역을 돌려가면서 captureSession을 담당하고있는

RTCCameraVideoCapturer.m 부분을 분석해봤다.

 


최종 구조

 

화면 구성

 

스트리밍 + 움직임 감지 녹화 

 

 

Objective-C로 된 WebRTC 라이브러리 내부를 분석해봤다.

 

움직임 감지 녹화에서 사용했던 AVCaptureSession이 있었고, 

해당 구현부는 RTCCameraVideoCapturer.m 파일에서 하고 있었다.

 

captureSession의 captureOutput으로 방출되는 비디오, 오디오 데이터들을 스트리밍에 보여주고 있었고, 

이 방출되는 데이터들을 스트리밍, 영상녹화에 둘다 사용할 수 있을것 같았다.

 


스트리밍시 영상을 어떻게 보여주나?

라이브러리 내부 구조는 이렇게 되어있었다.

 

 

비디오

 

captureOutput에서 방출되는 데이터를 아래와 같이 변환

 

  1. CMSampleBuffer → CVPixelBuffer 
  2. CVPixelBuffer → RTCPixelBuffer 
  3. RTCPixelBuffer → RTCVideoFrame 

 

가공된 RTCVideoFrame 데이터를 아래 delegate를 통해 스트리밍시 영상이 나오게 할 수 있었다.

[self.delegate capturer:self didCaptureVideoFrame:videoFrame];

다시 돌아와서 이제 영상녹화, 음성녹음도 captureOutput으로 하면 해결이 될줄 알았으나.. 

아래의 문제가 있었다.

 

스트리밍시 오디오 데이터를 못받아오는 문제

스트리밍을 하는 경우에만 오디오 데이터가 출력되지 않는 문제가 있었다.

 

이상하게도 스트리밍시 실시간 소통할때 음성은 잘 나오는데, 왜 녹화하려고 할 때만 음성 데이터를 못 받아오는지 이해가 안됐다.

이때는 captureOutput에서 나오는 오디오 데이터말고 다른걸 사용하는건가? 라는 생각이 들었다.

 

다행히도 AVCaptureSession 공식문서를 훑어보다가 usesApplicationAudioSession 프로퍼티를 알게 됐고

default값이 true인데 false로 되어있어 스트리밍시에만 오디오 데이터가 오지 않았던건지 궁금해졌다.

false 일 경우 captureSession은 오디오 녹음을 위해 비공개 AVAudioSession 인스턴스를 사용하므로 자체 오디오 세션을 사용하는 경우 중단이 발생할 수 있습니다.

 

일단 프로젝트에는 해당 프로퍼티가 없으니 WebRTC라이브러리를 찾아봤다.

역시 있었다.

 

아래는 WebRTC 라이브러리의 setupCaptureSession 메서드

- (BOOL)setupCaptureSession:(AVCaptureSession *)captureSession {
  NSAssert(_captureSession == nil, @"Setup capture session called twice.");
  _captureSession = captureSession;
#if defined(WEBRTC_IOS)
  _captureSession.sessionPreset = AVCaptureSessionPresetInputPriority;
  _captureSession.usesApplicationAudioSession = NO;  // <== false를 사용하고 있었음.
#endif
  [self setupVideoDataOutput];
  // Add the output.
  if (![_captureSession canAddOutput:_videoDataOutput]) {
    RTCLogError(@"Video data output unsupported.");
    return NO;
  }
  [_captureSession addOutput:_videoDataOutput];
  return YES;
}

 

 

라이브러리에서 iOS의 경우에만 captureSession을 세팅해주면서 스트리밍시 오디오를 공유하지 않도록 막아버렸던게 원인이었고

usesApplicationAudioSession = true로 변경하고나니 스트리밍시에도 영상녹화를 할 때 오디오데이터를 받아오는것도 확인할 수 있었다.

 


 

입사 이후로 스트리밍, 움직임 감지 녹화 구조만 크게 3번은 바꾼듯하다.

 

그동안 기능은 아래와 같이 추가되고 고도화 되었다.

  1. 스트리밍 서비스만 제공
  2. 스트리밍 따로, 움직임 감지 녹화 따로 (이하 움감녹) 서비스 
  3. 스트리밍중 영상녹화, 움감녹
  4. 스트리밍과 동시에 움감녹

기능추가와 고도화 작업을 통해 앱도 많이 튕기고 불안정하기도 했지만 그래도 계속해서 문제를 해결해나가면서 지금은 어느정도 많이 안정화가 된것같다.

 

개선된 부분

  • 스트리밍 연결 소요시간 감소
    5~10초 → 2~5초
  • 스트리밍 성공률 증가
    60~80% → 90~95%

 

이번 작업을 통해 하나의 Class에 너무 많은 일을 하고 있는데 이를 역할에 따라 나눠보는 작업과

테스트를 좀더 원활하게 할 수 있는 방법이 없는지 고민을 좀 해봐야 할것 같다.

긴 시간 작업이었지만 그래도 유의미한 개선이 있어서 뿌듯했다.

728x90

'Story' 카테고리의 다른 글

2023 회고  (3) 2024.02.04
글또 9기 시작과 다짐  (8) 2023.11.30
[회고] 2023년의 절반을 돌아보며 (글또 8기)  (2) 2023.07.16
[회고] 23년 1분기  (2) 2023.05.07
호텔리어 개발자의 삶의 지도  (4) 2023.01.06