UDP로 영상 스트리밍을 하려는데 문제가 있어 질문드립니다.

kachukun의 이미지

안녕하세요.

처음 가입해서 남기는 글이 질문드리는 글이라 죄송합니다(...)

현재 서버에서 얻은 이미지를 UDP 통신을 이용해 클라이언트로 스트리밍하는 프로그램을 만들고 있습니다.

서버는 Raspberry Pi 3, 클라이언트는 iPhone 5s 입니다.

보내는 이미지 한 장의 크기는 330x330의 raw data로, unsigned char에 108,900 byte 입니다.

서버는 AP 역할을 하도록 만들어두었고 클라이언트는 Wifi로 해당 서버에 연결됩니다.

UDP 에서 한번에 전송할 수 있는 packet 크기가 65535라고 알고 있어서, 이미지를 54,450 byte로 분할하여 클라이언트로 보내고 있습니다.

원하는 속도는 최소 10 fps, 최대 30 fps라 클라이언트에서 요청을 약 0.05초~0.016초마다 sendto를 통해 서버의 데이터를 요청하도록 하였습니다.

문제점은, 이런 식으로 프로그램을 실행했더니 자주 패킷 교환이 정상적으로 이루어지지 않더군요.

현상은 다음과 같습니다.

원래의 전송 메커니즘은 아래와 같습니다.

Client : sendto(sendMsg) //sendMsg는 char[2] 입니다.
↓ 전송
Server : recvfrom(recvMsg) //recvMsg는 char[2] 입니다. 크기 2의 char가 담겨오면 클라이언트로 데이터를 sendto 하게 되어있습니다.
Server : sendto(sendBuff) // sendBuff는 char[54450] 입니다.
↓ 전송
Client : recvfrom(recvBuff) // recvBuff는 char[54450] 입니다.

전송이 잘 될 때는 위와 같이 전송이 이루어집니다만, 어느정도 시간이 지나고 나면(짧으면 2분, 길면 1시간 정도 후입니다)

Client : recvfrom(recvBuff)

단계에서 recv가 fail이 됩니다.

recv가 fail이 되어도 클라이언트가 sendto하는 일은 recv의 성공여부에 관계없이 주기적으로 반복하고 있는데, 항상 다른 단계는 모두 잘 되지만 recv만 하지 못합니다.

나름대로 여러가지 원인을 분석해보았습니다만, 패킷은 모두 정상적으로 주고받고 있습니다만 모종의 이유로 recv fail이 발생하는듯 합니다.

1500인 MTU에 맞추어 데이터를 더 잘게 쪼개어 전송해보기도 했습니다만, frame rate가 너무 느려 사용하기가 어렵더군요 ㅠㅠ

현재 해결을 위해 고려중인 방법으로는 (가능한지는 모르겠습니다만) fail이 일정 이상 발생하면 서버의 ap를 내렸다가 다시 올린다거나, 네트워크의 트래픽을 reset 한다거나... 하는 방향으로 생각하고 있습니다. (소프트웨어적으로는 해결할만한 방법을 찾을 수 없을 것 같아서요 ㅠㅠ)

도저히 안되면 영상 스트리밍을 rtp로 한다거나 하는 방법도 고려하고 있습니다만.. 가능하면 udp만으로 해결할 수 있으면 좋겠어서...

혹시 이를 해결할만한 방법이 있을까요? 또는 제가 잘못 생각하고 있는 부분이 있다면 조언 주시면 감사드리겠습니다.

만일 답변하시는데 제가 구현한 내용에 대해 구체적인 코드가 필요하시다면 말씀해주시는대로 추가작성할 수 있도록 하겠습니다.

읽어주셔서 감사합니다!

sangho의 이미지

학부시절에 연습삼아 했을때는... 320 x 240 크기의 영상을 압축한후에 실시간 전송하는 걸로 했었는데..
1frame이 평균 30k byte가 됬었는데 전송부분에서 엄청 느리게 되더라구요... 그래서 실패했어요 저는..
전송 및 수신이 되긴했지만 frame drop이 좀 심했죠...
당시에는 send, recv버퍼 사이즈 늘리는거만 해줬던 기억이 나긴 하는데.. 여튼 그당시 지식으로는 못했었습니다.
혹시 서버에서 sendto로 보낼때 (약 50000의 바이트의 data를 보낼 때) 원하는 속도로 전송이 되시나요??

kachukun의 이미지

안녕하세요! 답변 감사드립니다!
서버에서 sendto로 보낼 때 원하는 속도로 전송... 이라는 말씀을 제가 올바르게 이해했는지 잘 모르겠습니다만,
데이터 전송이 정상적으로 이루어지는 동안에는 결정해둔 주기에 어느정도 맞게 전송되는 것으로 보입니다.
30frames로 전송하면 연결이 너무 자주 끊어져서 정확하게 측정해볼 수는 없었지만... 10frames 정도에서는 제가 원하는대로 전송이 되었습니다.

라스코니의 이미지

UDP가 좀 까다로운 프로토콜인 것 같습니다. 그리고 UDP로 54k 바이트를 한꺼번에 보내는 것은 어지간한 네트웍 환경에서는 좀 어렵지 않나 싶습니다. 보내는 단이나 중간 단에서도 부담이 클 것으로 보이고요.

저는 UDP로 보낼때 1k 또는 4 k 바이트 단위로 보냈었는데 거의 네트워 허용폭까지 데이터를 보낼 수 있었습니다. 받는 쪽에서는 threading 처리가 필요한데 UDP 패킷을 받는 쓰레드는 원형 큐 같은 데이터 버퍼에 데이터를 put하고, 데이터 처리 쓰레드 (스트리밍)에서는 일정 부분 받으면 작업을 착수/재개하는 것으로 처리를 했었습니다.

핵심은 실시간 스트리밍이라는 것이 데이터를 받자마자 뿌리는 것이 아니라는 것입니다. 데이터를 받는 쓰레드는 따로 돌아야 하며 어느 정도 충분히 임시 버퍼에 영상 데이터가 쌓이면 뿌려주기 시작하며, 현재 영상을 뿌리는 쪽이 요구하든 안하든 받는 쪽 임시 버퍼가 허용하는 한 (끊김없이) 쭉 받아야 전체적인 흐름에 무리가 없습니다. 영상 뿌리는 쓰레드는 반드시 UDP 받는 쓰레드와 분리가 되어야 합니다.

kachukun의 이미지

말씀 감사합니다!

제 생각에는 현재 클라이언트에서 라스코니님께서 말씀해주신대로 쓰레드를 나누어서 하고 있지 않나... 하고 생각하고 있습니다.

iOS인 클라이언트에서는 디스패치 큐를 2개 생성하여, sendto & recvfrom을 수행하는 쓰레드와 이미지를 출력하는 쓰레드로 나누어두었습니다.

코드의 일부를 간략히 작성해보면 다음과 같습니다.

_netQueue = dispatch_queue_create("SendRecvMsg", 0);
netTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _netQueue);
dispatch_source_set_event_handler(netTimer, ^{
[self sendMsg]; // sendMsg는 sendto와 recvfrom을 수행하는 함수입니다.
});
dispatch_resume(netTimer);

...(중략)

_queue = dispatch_queue_create("ImageDraw", 0);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
dispatch_source_set_event_handler(netTimer, ^{
[self imageTick]; // imageTick은 그림을 그리는 함수입니다.
});
dispatch_resume(timer);

sendMsg에서는 나누어진 이미지를 받아서 임시 버퍼(m_pTempBuff 등)에 저장해두고, 마지막 이미지가 저장되면 임시 버퍼에 저장된 내용을 이미지 출력 버퍼(m_pImageBuff)에 저장합니다.

imageTick은 주기적으로 m_pImageBuff를 이미지를 뿌려주고 있습니다.

이렇게 해두면 말씀하신 쓰레드 분리는 되어있지 않는가... 싶습니다!

혹 sendMsg와 imageTick의 내용도 문제해결에 필요하다면 추가로 작성해보겠습니다.

감사합니다!

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.