BLOG ARTICLE 인터넷방송 궁금하니? | 2 ARTICLE FOUND

  1. 2008.04.18 인터넷 라디오 (Shoutcast Metadata Protocol)
  2. 2008.04.16 객체를 이용한 winamp방송 얻기

출처: http://blog.naver.com/tjkim9393/70019516638

 

인터넷 라디오 (Shoutcast Metadata Protocol) 나름 강좌

2007/07/10 01:06

복사 http://blog.naver.com/tjkim9393/70019516638

Shoutcast Server에 접속해서 MP3를 듣기위해서는 접속 규격대로 데이터를 주고 받아야 한다.

Shoutcast Stream은 MP3 Data에 Metadata가 붙어있는 형태로 Metadata를 적절히 추출해 내고 MP3 Stream을 MP3 Decoder로 전달해 주면 MP3 음악을 Server에 접속해서 들을 수 있다.


참고로 직접 주고 받는 Data를 보고 싶으면 Ethereal이라는 공개 소프트웨어를 PC에 설치한 후 Ethernet을 통해 주고 받는 모든 Packet을 볼 수 있다. 실제 구현시에도 이 툴을 사용해서 주고 받는 Packet을 보면서 그대로 옮겼다.


1. Shoutcast Stream Format

 우선 Shoutcast Data를 보면 다음 그림과 같다.



MP3 Data에 Metadata가 붙어있는 형태이다.



2. Connect to Server

보통 Shoutcast Server는 URL로 되어있다. 예를 들면 미인 방송국의 URL은 "miin.saycast.com"이다.

이 URL에 접속해서 streaming을 하고 있는 server의 ip와 socket 번호를 알아내야 한다.

URL에 대해 IP를 얻어서(DNS Request : GetHostByName) 해당 IP의 80 Port (HTTP Protocol)에 Request를 보낸다.

Request는 HTTP String으로 다음과 같이 보낸다.

2번째 라인의 Host: %s의 Parameter는 해당 URL String이다.

이 Request는 추후 실제 Streming server에 Metadata를 받기 위해서 다시 사용한다.


    static prog_char szReqStr[] =
    {
        "GET / HTTP/1.0\r\n"
        "Host: %s\r\n"
        "User-Agent: WinampMPEG/5.09\r\n"
        "Accept: */*\r\n"
        "Icy-MetaData:1\r\n"
        "Connection: close\r\n"
        "\r\n"
    };


Data caputre는 PC에서 Winamp로 miin방송국 Streaming Server Request를 하는 부분이다.




3. Get Streaming Server

앞의 Request에 대해 Server에서는 다음과 같은 응답 패킷을 보낸다. (HTTP Protocol만 보면 된다.)

HTTP/1.1 302 Found\r\n 라는 응답으로 시작되며 Data, Server, Location 정보등이 날라오게 된다.

이중 Location 정보가 실제 Streaming Server의 주소가 되며 :8102는 포트 번호가 된다.

URL에 접속할 때마다 Streaming Server의 주소와 포트번호는 바뀔 수 있다. (접속량에 따라 Server가 Connection을 분배하기 때문이다.)

이 그림에서는 sc07.saycast.com이 Streming Server의 URL이고 포트 번호는 8102가 된다.



4. Request Metadata

Streaming Server의 주소와 Port 번호를 알아냈으니 이제 접속해서 Steam의 Metadata를 얻어와야 한다.

Metadata를 얻기 위해 위에서 사용한 Request를 그대로 사용하되 Host의 파라미터를 위에서 얻은 Streaming Server의 URL을 사용한다.


    static prog_char szReqStr[] =
    {
        "GET / HTTP/1.0\r\n"
        "Host: %s\r\n"
        "User-Agent: WinampMPEG/5.09\r\n"
        "Accept: */*\r\n"
        "Icy-MetaData:1\r\n"
        "Connection: close\r\n"
        "\r\n"
    };


80 Port가 아니어서 Protocol이 HTTP로 인식되지 않으니 Ethereal에서 오른쪽 버튼을 눌러 강제로 HTTP로 디코딩을 하면 다음과 같은 데이터를 보내는 것을 알 수 있다.



5. Interpret Response

위의 Request에 대해 Streaming Server는 다음과 같은 응답을 보낸다.

ICY 200 OK\r\n을 보낸 후 해당 Stream에 대한 정보를 보내게 된다.

각 field는 icy로 시작되며 각 field는 다음과 같은 의미이다.


Field

Meaning

icy-notice{n}

서버에서 공지사항을 보낼 때 사용

icy-name

방송국 이름

icy-genre

음악 장르

icy-url

방송국 Homepage URL

content-type

Metadata 뒤에 오는 Content Type

icy-br

Stream Bitrate

icy-pub

a switch to either allow the server to publish itself in the directory or not (1 meaning yes)

icy-metaint

Metadata의 Interval


icy-metaint가 바로 data가 반복되는 주기를 나타낸다. 이 만큼의 MP3 Data가 오고나서 metadata가 오게 된다.

아래 그림에서는 32768 byte만큼 MP3 데이터가 온 후에 metadata가 나오게 된다.

(icy-name을 이용하여 방송국 이름을 화면에 나타냈다.)



6. Interpret Metadata

icy-metaint만큼의 MP3 데이터가 온 후에 비로소 Metadata가 온다.

Metadata의 제일 첫 바이트는 실제 Meatadata의 크기로 이 값에 x16을 해서 Metadata Size로 사용한다.

따라서 Metadata < 4080 보다 작다. 이 Metadata를 Buffering해서 저장한 다음에 전부 다 받으면 Parsing을 해야 한다.

Metadata에는 현재 재생중인 곡의 Title이나 Stream URL과 같은 부가 정보가 오게 된다.

만약 Metadata Size byte가 0이면 Metadata가 없음을 나타낸다. 대부분 Metadata size가 0인 데이터가 온다.

Metadata는 접속을 했을 때 또는 재생중인 곡의 제목이 바뀔 때 날라오는 경우가 대부분이다.

재생중일때는 대부분 0인 Metadata가 온다. 아래 그림에서는 한글로 나온 제목이 "."으로 처리되어 보인다.

StreamTitle='xxxxxx' Field가 현재 재생중인 제목이다.


 
 
MP3 Data는 Buffering을 적절히(?) 시킨 후에 MP3 Decoder에 전달하면 된다.
 
VS1003 과 같은 Decoder를 사용시에는 DRQ Pin을 봐가면서 데이터를 전달해야 한다.
한꺼번에 받은 데이터를 모두 Writing을 시키면 Overflow가 나게 되서 소리가 끊기게 된다.
실제 Streaming 되는 Data는 Bitrate에 맞춰 전송이 되긴 하지만, 한 번에 받는 데이터의 크기 만큼 VS1003에 Write하지 못하기 때문에 일부 데이터를 Buffering해야 한다.
 

AND

출처: http://blog.naver.com/soora24?Redirect=Log&logNo=20001680368

객체를 이용한 winamp 방송정보 알아내는 소스  



흑심품은연필   2003-04-07 18:12:43, 조회 : 1,054, 추천 : 1

몇몇 분들이 저에게 다중서버정보를 어떻게 알아내느냐는 메일을 보내셔서
이렇게 정보를 공유하겠습니다

방송정보 알아내기 구조체 화일 (winamp_cast_information.php) 내용

<?



class winamp_cast_information {

          var $now_listen;
                  var $max_listen_persons;
          var $cast_title;
                  var $cast_song;

     # 단일서버 정보 얻어 오기 메소드 시작
        function winamp_cast($cast_ip){

          $listen = $this->winamp_shoutcast_listen($cast_ip);            

               # 현재 청취자수 와 최대청취할수 있는 인원
               $this->now_listen += $listen[0];
                           $this->max_listen_persons +=  $listen[1];  
              
                           $cast_info = $this->winamp_shoutcast_info($cast_ip);

               $this->cast_title = $this->str_re("title",$cast_info[title]);
                           $this->cast_song =  $this->str_re("song",$cast_info[song]);

              

        }
      # 단일서버 정보 얻어 오기 메소드 시작  


     # 멀티방송 서버 정보 얻어 오기 메소드 시작
      function winamp_cast_array_list($array_cast_ip_list){

            for($i=0;$i < sizeof($array_cast_ip_list);$i++){  
                        
                          $server_ip =  $array_cast_ip_list[$i];
                                

               $listen = $this->winamp_shoutcast_listen($server_ip);            

               # 현재 청취자수 와 최대청취할수 있는 인원
               $this->now_listen += $listen[0];
                           $this->max_listen_persons +=  $listen[1];  
              
                           $cast_info = $this->winamp_shoutcast_info($server_ip);

               $this->cast_title = $this->str_re("title",$cast_info[title]);
                           $this->cast_song =  $this->str_re("song",$cast_info[song]);

                         }
          
           }
      # 멀티방송 서버 정보 얻어 오기 메소드 끝



     ### 방송정보를 알아내는 메소드 시작 - 방송제목(title), 현재곡(song) ###
     function winamp_shoutcast_info($server_ip){

                  $server_ip = str_replace("http://", "", $server_ip);
                  $server_ip = str_replace("/", "", $server_ip);  

                  $info = array();

                  # 방송주소을 (:)으로 주소(ip)와 포트(port)로 나눈다
                  $addre = explode(":","$server_ip");

                  $address = $addre[0];
                  $port = $addre[1];

                  $fp1 = fsockopen("$address", $port, $errno, $errstr);

                   #문단 1 시작
                  if(!$fp1){
                                          
                                          $info[ok] = 0;

                     #문단 1 끝
                                         #문단 2 시작
                     }else{

                      $info[ok] = 1;

                         # 소켓을 이용해서 서버에 html 형태로 테이타를 보내라고 명령한다
                     fputs ($fp1, "GET /index.html HTTP/1.0\nUser-Agent: Mozilla/4.0\n\n");

                    # 소켓의 마지막(eof) 까지 데이타를 읽는다(1024바이트씩)
                    while(!feof($fp1)){
        
                            $end .= fgets($fp1,1024);

                              }                                

                   # 소켓을 닫는다
                   fclose($fp1);

                  $end = strip_tags ("$end");

                  if(eregi("(([1-9]){2,3}) kbps",$end,$arr)){
                  $info[kbps] =  $arr[1];
                   }
      

         # $end 부분에서 방송제목과 장르, 홈주소 그리고 지금나오는 음악제목를 얻는다  
            #array("URL:","Stream")
       #array("Genre:","Stream")

      $info_spl = array(array("Title:","Stream Genre:"),array("Song:","Copyright"));

      $val = array("title","song");


     for($i=0;$i<2;$i++){

          $one = explode ($info_spl[$i][0], $end);
          $two = explode($info_spl[$i][1],$one[1]);

          $info[$val[$i]] = $two[0];

          $end = $one[1];

      }

    
      #문단 2 끝
         }

# 결과 리턴
return $info;


}  
### 방송정보를 알아내는 메소드 끝 - 방송제목(title), 현재곡(song) ###

     # 청취자수 알아내는 메소드 시작
     function winamp_shoutcast_listen($server_ip){

          $listen = array();

          $server_ip = str_replace("http://", "", $server_ip);
          $server_ip = str_replace("/", "", $server_ip);  

          # 방송주소을 (:)으로 주소(ip)와 포트(port)로 나눈다
          $addre = explode(":","$server_ip");
  
          $address = $addre[0];
          $port = $addre[1];

          $fp1 = fsockopen("$address", $port, $errno, $errstr);

          #문단 1 시작
          if(!$fp1){
                          
                          # 청취자수
                  $listen[0] =  0;
                  
                                  # 최대 청취자수
                                  $listen[1] =  0;

          #문단 1 끝
                  #문단 2 시작
          }else{

                  # 소켓을 이용해서 서버에 html 형태로 테이타를 보내라고 명령한다
              fputs ($fp1, "GET /index.html HTTP/1.0\nUser-Agent: Mozilla/4.0\n\n");

             # 소켓의 마지막(eof) 까지 데이타를 읽는다(1024바이트씩)
             while(!feof($fp1)){
        
                      $end .= fgets($fp1,1024);

                 }                                

            # 소켓을 닫는다
            fclose($fp1);

            $end = strip_tags ("$end");

           if(eregi("with (([0-9]){1,}) of (([0-9]){1,}) listeners",$end,$arr)){
                  
                                  # 청취자수
                  $listen[0] =  $arr[1];
                  
                                  # 최대 청취자수
                                  $listen[1] =  $arr[3];
                          
                           }  
  
         #문단 2 끝
         }

       # 결과 리턴
       return $listen;

   # 청취자수 알아내는 메소드 끝
   }
  
     function str_re($what,$value){
      
         if($what == "title"){

            $end = str_replace("Content Type: audio/mpeg","", $value);

         }else if($what == "song"){

                     $end = str_replace("Written by Stephen 'Tag Loomis, Tom Pepper and Justin Frankel","", $value);
                
                 }

    
       return $end;
    
}




}


?>

방송정보 알아내기 객체를 생성 정보를 알아내는 소스 (winamp_info.php) 내용

<?

# mysql_db 구조체를 만들어놓은 화일 읽어
include("winamp_cast_information.php");

# 새로운 객체($mysqldb)를 생성
$winamp = new winamp_cast_information;

# Mycast(My portrait of Youthful day) 방송국 방송 주소
$cast_ip = "http://218.145.30.154:11100";

$winamp->winamp_cast($cast_ip);

echo "청취자수 : ";
echo $winamp->now_listen;
echo "<br>";
echo "최대가능청취자수 : ";
echo $winamp->max_listen_persons;
echo "<br>";
echo "방송제목 : ";
echo $winamp->cast_title;
echo "<br>";
echo "현재나가는곡 : ";
echo $winamp->cast_song;
echo "<br><br>";


# 뮤클케스트 방송국 방송 주소들
$cast_ip_array = array();

$cast_ip_array[0] = "http://cast1.mukulcast.com:11000";
$cast_ip_array[1] = "http://cast2.mukulcast.com:11000";
$cast_ip_array[2] = "http://cast3.mukulcast.com:11000";

$winamp->winamp_cast_array_list($cast_ip_array);

echo "청취자수 : ";
echo $winamp->now_listen;
echo "<br>";
echo "최대가능청취자수 : ";
echo $winamp->max_listen_persons;
echo "<br>";
echo "방송제목 : ";
echo $winamp->cast_title;
echo "<br>";
echo "현재나가는곡 : ";
echo $winamp->cast_song;


?>

이상 끝!!

젊은날의 초상 My portrait of Youthful day  MyCast  웹디팀 MyWD연필(흑심품은연필)......
방송주소 - http://218.145.30.154:11100/


61.43.149.205




ZipShin
  $cast_ip_array[0] = "http://cast1.mukulcast.com:11000";
$cast_ip_array[1] = "http://cast2.mukulcast.com:11000";
$cast_ip_array[2] = "http://cast3.mukulcast.com:11000";
이라는 배열을 사용함으로써, 여러개의 방송 주소를 더 많이 첨가 할수있어요? 2003-04-08
13:17:20
  



흑심품은연필
  네 가능해요 정답을 알고 있으시면서 이히 2003-04-26
09:03:46
  



남상규
  우와!!! 대단하다! 안 그래도 질문 남기려다 검색했더니 나오네! 고맙습니다! 꼭 원하던 소스... 2003-05-07
11:21:11
  



남상규
  이 소스를 약간 수정해 보았습니다.(winamp_info.php)
<?

# mysql_db 구조체를 만들어놓은 화일 읽어
include("winamp_cast_information.php");

# 새로운 객체($mysqldb)를 생성
$winamp = new winamp_cast_information;

# Mycast(My portrait of Youthful day) 방송국 방송 주소
$cast_ip = "http://pmonzzang.w3ip.com:8000";

$winamp->winamp_cast($cast_ip);

echo "청취자수 : ";
echo $winamp->now_listen;
echo "<br>";
echo "최대가능청취자수 : ";
echo $winamp->max_listen_persons;
echo "<br>";
echo "방송제목 : ";
if($winamp -> cast_title) echo $winamp->cast_title;
else echo "지금은 방송중이 아닙니다.";
echo "<br>";
echo "현재나가는곡 : ";
if($winamp -> cast_song) echo $winamp->cast_song;
else echo "지금은 방송중이 아닙니다.";
echo "<br><br>";
?>
뮤클캐스트 전 안 하니까 없앴구요, 방송중이 아니면 '지금은 방송중이 아닙니다.'라고 나오게 했습니다. 2003-05-07
11:27:07
AND