Live555에 보면 RTSP스트림을 받을 수 있는 소스코드를 이미 제공하고 있다.

하나는 openRTSP.c이고 다른하나는 testRTSPClinet.c라는 소스코드이다.

 

openRTSP는 RTSP규약을 전부다 구현해 놓은 Client 프로그램으로 재생 정지 되감기 등의 RTSP기능을 가지고 있다. 즉, 좀 보기 복잡하다.. ㅎㅎ 반면에 testRTSPClient는 RTSP스트림을 받는 것만 구현이 되어 있어서 기능은 그닥 뭐 볼건 없지만, Live555가 제공하고 있는 파이프라인 구조라든지 RTSP client와 server간의 동작을 간단히 살펴 보는데는 더할 나위 없이 좋다. 

 

무엇보다도 내가 만들고자 했던 프로그램은 항상 Live영상만 H.264로 전달하는 IP Camera의 RTSP서비스를 활용하기 때문에 다양한 RTSP규약의 기능을 별로 알필요가 없어 구지 어려운 소스코드를 볼 필요가 없었다. =_=

 

어쨋든 이번 포스트에서는 testRTSPClient를 대상으로 해서 어떻게 RTSP서비스로부터 데이터를 수신할 것인지, 그리고 어떻게 파이프라인을 구성해서 활용하고 있는지를 살펴볼 것이다.

 

먼저 main부터 살펴보자

 

이미지로 찍어 올렸더니 잘 안보인다.

소스코드는 testRTSPClient.c에 들어가 있기 때문에 그 소스코드를 열어 보시면 좋겠다.

어찌됐든 위 부분이 console타입의 testRTSPClient프로그램의 도입부인 main함수이다.

 

생각외로 매우 조촐하다.

보이는 함수도 별로 없고 심지어는 지역변수도  별로 없다.

 

Main함수가 이토록 조촐한 이유는 Live555가 MFC의 메시지 드리븐 방식과 비슷하게 동작하기 때문이다.

(메시지 드리븐이라는 말은 큐에 메시지가 들어가고, 각 메시지가 발생한 순서대로 처리되는 방식을 의미한다.)

 

Live555에서는 이런 메시지 드리븐 방식의 동작구조를 TaskScheduler가 제공하고 있으며, main초반부에 보면 TaskScheduler를 생성하여 사용하고 잇는 것을 볼 수 있다. 아울러 그 다음에 생성된 UsageEnvironment는 이전 포스트에서 설명 했듯이, OS와 Live555의 기능 사이에서 소켓등 OS에 종속된 기능들을 추상화 하여 Live555에 제공하는 역할을 한다. 이 클래스 때문에 Live555함수의 기능들은 OS 따라 함수코드를 바꿀 필요 없이, UsageEnvironment만 바라보고 소스코드의 일관성을 유지 할 수 있다. 실제로 OS에 따라 바뀌는 것은 UsageEnvironment이다.

(OS에 따라 UsageEnvironment를 상속하여 적합한 UsageEnvironment를 제공해야 하며 Windows, Linux에서는 BasicUsageEnvironment를 사용하면 된다.) 

 

어쨋든 main에서는 이제 프로그램의 아규먼트로 입력받은 RTSP URL를 인자로 하여 openURL함수를 호출한다.

그리고서는 메시지 큐가 정상적으로 동작을 시작하도록 taskScheduler의 doEventLoop를 실행한다.

이 doEventLoop는 글로벌 변수로 어떤 변수를 받는데, 이 글로벌 변수가 0이 아닌 값이 들어가면, 이벤트 루프를 종료하고 return 0; 문으로 넘어가 프로그램이 종료되게 한다.

 

그럼 이제 RTSP URL로 뭘하는지 실제 OpenURL 함수의 구조를 살펴보자.

openURL함수는 아래와 같다.

 

역시 이미지로 올렸더니 흐릿하니 잘 안보인다. testRTSPClient.c를 확인하시라.. =_=

 

openURL에서는 ourRTSPClient를 하나 생성해서 해당 URL로 대표되는 RTSP서비스를 처리하도록 하고 있다.

그 다음에 sendDescribeCommand를 이용해 RTSP서버로 DESCRIBE명령을 전송한다. sendDescribeCommand의 인자로 주어진 continueAfterDESCRIBE는 콜백함수 포인터로, 서버로 보낸 DESCRIBE명령에 대한 응답이 돌아왔을 때 호출될 콜백함수의 포인터이다.

 

RTSP서비스를 제공하는 RTSP서버에 DESCRIBE명령을 전송하면 RTSP서버는 클라이언트로 SDP ( Simple Description Protocol ) 문서를 전달한다. 문서라고 하니까 무슨 파일로 되어 있는 그런걸 떠올릴 수 있는데 파일은 아니고 단순히 어떤 문자열에 불과하다. 이 SDP문자열에는 해당 RTSP서비스가 제공하고 있는 미디어스트림의 종류 등 정보가 들어가 있다.

 

예를 들자면.. 아래와 같은 SDP는 오디오, 비디오 2종류의 미디어를 제공하고 있다는 의미이다..

 

m=audio 49170 RTP/AVP 0 8 97

a=rtpmap:0 PCMU/8000

a=rtpmap:8 PCMA/8000

a=rtpmap:97 iLBC/8000

m=video 51372 RTP/AVP 31

a=rtpmap:31 H261/90000

 

특히 "m=audio", audio미디어를 49170포트로 Realtime Transport Protocol / Audio Video Payloadtype

 에 정의된 0,8,97번 즉, PCMU/8000, PCMA/8000, iLBC/8000으로 제공하고

"m=video", video미디어는 51372포트로 Realtime Transport Protocol / Audio Video Payloadtype 에 정의된 31번

즉, H261/90000으로 제공한다는 의미를 가지고 있다는 의미이다.

 

물론 이런 정보를 알아내기 위해서 SDP를 공부할 필요는 없다.

Live555에서 SDP를 파싱해서 우리가 이해 가능한 정보로 바꿔주는 기능을 제공해 주니 두려워 않아도 좋다.

 

그럼 이제 continueAfterDESCRIBE함수에서 어떻게 이 SDP를 처리하고 있는지 살펴보자. 

허허... 그림이 커질수록 점점 더 안보이기 시작하고 있다..=_=

 

어쨋든 눈을 부릅뜨고 한번 보자. 참고로 이 continueAfterDESCRIBE는 static 함수로 어떤 rtspClient의 DESCRIBE명령로부터 콜백 된 함수인지 모르기 때문에 Live555에서는 함수의 인자로 rtspClient를 전달해준다. 그리고 3번째 인자로 resultString을 건네주는데 이게 SDP 스트링이다.

 

이걸 SDP를 파싱해서 쓰기 위해서 205줄과 같이 MediaSession을 만든다. 인자로 sdp문자열을 전달하는데 이렇게 되면 자동으로 SDP에 들어있는 미디어 스트림 정보마다 MediaSubsession이 생성되어 MediaSession에 추가된다.

 

그리고는 221 줄과 같이 각 MediaSubsession에 대해 setup을 시도한다. setup은 각 MediaSubsession이 제공하는 미디어 스트림데이터를 받을 수 있는 서버-클라이언트 간 환경을 조성하는 과정이다. setupNextSubsession( ) 함수는 rtspClient를 인수로 받아 이과정을 수행한다.

 

참고로 ourRTSPClinet를 살펴보면 아래와 같이 RTSP연결을 의미하는 MediaSession, RTSP연결에서 제공하는 미디어 스트림연결을 의미하는 MediaSubsession, 그리고 각 MediaSubsession을 순회할 수 있는 MediaSubSessionIterator정보가.. 정확히는 이런 정보를 가지고 있는 StreamClientState 인스턴스가 들어가 있다.

 

 

자세한 부분은 이 포스트를 전체적으로 한번 읽어보고 중단점을 걸어 한단계씩 넘어가다보면 어떻게 ourRTSPClient가 각 MediaSubSession등의 정보를 저장하고 있는지 알 수 있을 것이다.

 

자, 그럼 이제 setupNextSubsession에서 어떻게 서버 - 클라이언트간 미디어 스트림 데이터 수신 환경을 구축하고 있는지 살펴보자. 아래는 setupNextSubsession이다.

 

 

허허.. 쪼매 길다. 코드가

 

 

어쨋든 처음부터 차근차근 살펴보자..

다소 복잡해 보이지만, 전체 적인 코드 구조는 아래와 같다.

 

    1. 인자로 넘겨받은 rtspClient포인터를 전달받아 ourRTSPClient객체를 찾는다.

    2. ourRTSPClient객체의 StreamClietState멤버로부터 MediaSession이 가진 MediaSubsession을 순회

    3. 각 MediaSubSession에 대해 Initiate를 호출해 초기화한다.

    4. 각 MediaSubSession에 대해 sendSetupCommand명령을 전송한다.

    5. 모든 MediaSubSession에 대해 sendSetupCommand를 전송하고 순회가 완료 (NULL)

    6. sendPlayCommand로 Play Command를 RTSP서버로 전송한다.

 

이 setupNextSubsession함수는 모든 MediaSubsession을 순회하도록 재귀함수 구조로 되어 있는데, MeidaSubsession이 실패한 경우는 이 함수내에서 직접적으로 setupNextSubsession을 247줄과 같이 호출해서, 성공한 경우는 sendSetupCommand의 인자로 전달한 continueAfterSetup 콜백에서 재귀적으로 다시 호출된다.

Posted by 굿쟌
,