1 java media framework rtp 를 이용한 데이터 송 / 수신. 2.2 java media framework live media...

34
1 Java Media Framework RTP 를 를를를 를를를 를 / 를를

Upload: jane-hamilton

Post on 05-Jan-2016

237 views

Category:

Documents


9 download

TRANSCRIPT

1

Java Media Framework

RTP 를 이용한 데이터 송 / 수신

2.2

Java Media Framework

Live media broadcast 또는 video conference를 인터넷이나 인트라넷에서 수행하기위해서 Real-time 으로 미디어 데이터를 전송해야 한다 .

이 러 한 조 건 을 만 족 하 기 위 해 서 Real-time Transport Protocol 이 소 개 되 는 것 이 고 , 네트워크를 통한 미디어 데이터의 송 / 수신을 위해 JMF 에서는 RTP 를 지원한다 .

2.3

Protocol for Streaming Media

실시간 전송에서는 높은 대역폭 (bandwidth) 를 요구한다 . HTTP 나 FTP 는 TCP 기반의 프로토콜이다 . TCP 프로토콜은 안 정 적 인 데 이 터 전 송 과 low bandwidth, high network error 환경에 적합한 프로토콜이다 .

그 래 서 스 트 리 밍 데 이 터 의 전 송 에 서 는 TCP 와 는 다 른 프로토콜을 이용하게 되었고 , 일반적으로 UDP 프로토콜을 사용한다 .

UDP 는 Unreliable 프로토콜로서 수신단측에서의 데이터 수신에 대한 보장을 하지 않는다 . TCP 와 마찬가지로 UDP역시 일반적인 Transport Layer 의 통신 프로토콜이다 .

오디오나 비디오와 같은 스트리밍 데이터전송에 대한 인터넷 표준으로 RTP 를 이용하게 되었고 , RTP 는 IEEE RFC 1889로 규정되어 있다 . RTP 는 UDP 에 기반을 두고 있다 .

2.4

용어 정리

RTP Session RTP 를 이용해서 통신을 수행하는 애플리케이션들의

집합을 말한다 . Session 은 네트워크 어드레스와 전송포트의 쌍으로 이루어져 있다 . 포트하나는 미디어 데이터를 위해 이용되고 , 다른 포트하나는 RTCP 데이터의 제어를 위해 이용된다 .

Participants 세션에 참여하고 있는 단일 컴퓨터 , 호스트 , 또는 user 를

말한다 . 즉 , 통신상에 참여하는 모든 참여자라고 볼 수 있다 . 각각의 Participants 는 데이터를 전송하는 Sender의 역할을 하기도 하고 , 데이터를 수신하는 Receiver 의 역할을 하기도 한다 .

2.5

용어정리

Session Manager RTP Session 들간의 통제 역할을 하고 session 에 참가한

participants 에 대한 추적과 관리 , 전송되어지는 스트림에 대한 관리 기능을 수행한다 .

세션의 상태정보를 관리하고 RTCP control channel 을 취급하며 Sender, Receiver 를 위한 RTCP 를 지원한다 .

작성한 애플리케이션의 초기화 , 세션에 참여 , 연결을 해제 할 때는 애플리케이션에 의해서 생성된 각각의 스트림을 제거 , 전체 세션을 닫는 기능을 수행한다 .

참고 : 각각의 미디어 데이터들은 서로 다른 세션을 통해서 전송된다 .

다시 말하면 화상회의 시스템이라고 하면 영상과 음성이 각각 별도의 세션을 통해 전송된다는 것이다 . 세션이 분리되면 , Participants 들이 어떤 세션의 미디어를 선택 할지를 가능하게 한다 . 네트워크의 대역폭이 넓다면 모든 영상과 음성을 수신받고 , 대역폭이 좁다면 음성만 받는 방법도 가능하다 .

2.6

네트워크를 통한 RTP 데이터의 수신

2.7

네트워크를 통한 RTP 데이터의 수신

네트워크를 통해 전달되는 RTP 데이터는 일단 Session Manager 에 의해 세션별로 분리가 되어서 별도의 데이터 소스들로 분리가 된다 . 만약 수신단에서 저장을 하고자 한다면 데이터 소스를 프로세서로 넘기고 프로세서의 출력을 다시 데이터 소스로 만들고 , 그것을 데이터 싱크로 넘긴후 데이터 싱크에서 저장하면된다

수신단에서 수신받은 데이터를 플레이 하고자 하는 경우에는 Session Manager 에서 나오는 데이터 소스를 Player 에게 넘겨주면 화면으로 볼 수가 있다 .

또 다른 가능성은 Session Manager 를 통해서 나온 데이터 소스를 아무런 가공도 하지 않고 단순히 저장하기 위해 바로 DataSink 로 넘겨서 저장하는 방법이다 .

2.8

네트워크를 통한 RTP 데이터의 송신

데이터 송신을 위해 데이터 획득을 해야한다 . 데이터는 하드디스크에 저장된 파일이나 캡쳐를 통한 오디오 / 비디오 미디어 데이터도 가능하다 . 이러한 데이터는 DataSource 로 보내어 지고 데이터 가공을 위해서 Processor 를 거친후 나온 DataSource 를 Session Manager 를 통해 네트워크로 전송하거나 DataSink 를 통해 파일로 저장할 수 있다 .

2.9

연구방법 (1/5)

RTP(Real-time Protocol) 실시간 데이터 전송을 위한 표준 패킷 형식 (RFC 1889) 하위 프로토콜 (UDP, TCP) 의 전송능력에 의존 , 패킷전송의

보증이나 실시간 전송을 보증하지 않는다 데이터 전송시에 발생하는 패킷 손실 , 패킷 지연 , 비순차 패킷등은

RTP 의 패킷순차번호 (Sequence Number) 와 타임스탬프로 확인하여 제어할수 있음

RTP 패킷의 구조

Version(V), Padding(P), Extension(X), CSRC Count(CC), Marker(M), Payload Type(PT), Sequence Number(SN), Timestamp, Synchronization Source(SSRC) Identifier, Contributing Source(CSRC) Identifiers

V P X CC M Payload Sequence Number

TimeStamp

RTP Data, etc.

0 3 4 5 9 10 17 32

2.10

RTP(Real-time Protocol)

RTCP(Real-time Control Protocol) RTP 제어 프로토콜 (RFC 1890) SR(Sender Report), RR(Receiver Report), APP(Application

specific function), BYE 패킷으로 구성 송수신 패킷의 정보를 피드백시켜 트래픽을 모니터링

NAME, EMAIL 등과 같은 부가적인 정보 송신자 패킷 계수 송신자 바이트 계수 SR 또는 RR 패킷이 송신된 이후 분실된 RTP 데이터 패킷 Jitter 수신 보고 블럭 송신간의 지연

2.11

RTP(Real-time Protocol)

RTSP(Real-time Streaming Protocol) RTP 상위에 존재하는 사용자 접속 프로토콜 (RFC 2326) RTSP 는 멀티미디어 서버를 위한 네트워크 원거리 제어로 이용 RTSP 와 HTTP 의 중요한 차이점은 HTTP 는 상태를 유지하지

않는 프로토콜인 반면에 , RTSP 는 대부분의 경우 상태를 유지한다

Describe 미디어의 자료를 반환

Setup

Play

Record

Redirect

RTP 로 전송하기 전 필요한 자원을 수집

미디어를 RTP 로 전송

RTP 로 전송된 미디어를 녹화

다른 RTSP 서버로 권한변경

Teardown RTSP Session 종료

Options RTSP 서버에서 사용 가능한 명령을 반환

2.12

RTP Events

RTP 와 관련된 이벤트는 javax.media.rtp.event 패키지에 정의되어 있다 . 이벤트의 등록과 처리는 우선 RTP 리스너를 구현해야 하고 , Session Manager 를 이용해서 RTP 리스너를 등록하면 된다 .

SessionListener( 세션의 상태 변화 감지 ) NewParticipantsEvent : 세션에 새로운 participants 가

참여하는 것을 알려준다 . LocalCollisionEvent : participants 의 동기화 소스가 이미

사용중임을 알려줌 SendStreamListener( 전송될 RTP stream 의 상태 변화 감지 )

NewSendStreamEvent : local participants 에 의해 새로운 send stream 이 생성될 때

Active/InactiveSendStreamEvent : send stream 의 생성에 이용된 DataSource 의 전송이 시작 / 중지 되었을 때

LocalPayloadChangeEvent : send stream 의 포맷이 변화되었을 때

2.13

RTP Events

ReceiveStreamListener( 수신될 RTP stream 의 상태변화 감지 ) NewReceiveStreamEvent : 새로운 receive stream 이

생성되었을때 Active/InactiveReceiveStreamEvent : 데이터 전송의 시작 /

중단시에 TimeoutEvent : 데이터 전송이 Time out 되는 경우 RemotePayloadChangedEvent : receive 스트림의 포맷

변경시 ApplicationEvent : RTCP APP 패킷이 수신되었을 때 StreamMappedEvent : 이전에 제대로 처리되지 못한

Orphaned ReceiveStream 이 participants 와 연결되었을 때

RemoteListener( 원격 participants 로부터 수신된 이벤트 혹은 RTP control Message 에 대한 감지 )

ReceiveReportEvent SenderReportEvent RemoteCollisionEvent

2.14

RTP Session Manager 를 이용한 수신

수신과정 Session Manager 를 통해 스트림 수신 ReceiveStreamListener 에서 스트림 수신시 이벤트 발생 수신된 스트림에서 DataSource 를 획득 획득한 DataSource 를 이용해서 Player 생성

Session Manager 생성과 초기화 SessionManager RTPMgr =

new com.sun.media.rtp.RTPSessionMgr(); RTPMgr.initSession(localaddr, userDesc, 0.05, 0.25); RTPMgr.startSession(destAddress, 1, null);

ReceiveStreamListener 를 등록 RTPMgr.addReceiveStreamListener(this);

2.15

RTP Session Manager 를 이용한 수신

새로운 스트림이 수신 되었을때 스트림에서 DataSource 를 획득

ReceiveStream stream =

((NewReceiveStreamEvent)ev).getReceiveStream(); DataSource dsource = stream.getDataSource();

획득한 DataSource 에서 Player 생성 player = Manager.createPlayer(dsource); player.addControllerListener(this); player.start();

2.16

RTP Session Manager 를 이용한 송신

송신 과정 캡쳐 디바이스나 파일을 이용해서 DataSource 생성 DataSource 를 이용해서 Processor 생성 Processor 에서 조작 ( 포맷변환 , 인코딩 ) 된 DataSource 생성 Session Manager 생성 , 초기화 SendStream 생성 , 송신

캡쳐 디바이스를 이용해서 DataSource 생성 Vector captureDevices =

CaptureDeviceManager.getDeviceList(new VideoFormat(VideoFormat.YUV));

CaptureDeviceInfo cdi = (CaptureDeviceInfo)captureDevices.firstElement();

DataSource dSource = Manager.createDataSource(cdi.getLocator());

2.17

RTP Session Manager 를 이용한 송신

DataSource 를 이용해서 Processor 생성 Processor processor =

Manager.createProcessor(dSource);

Processor 에서의 인코딩과 DataSource 획득 비디오 트랙을 획득

TrackControl tracks[] = processor.getTrackControls();

JPEG 로 인코딩 (H.261, H.263, MPEG 등도 가능 ) VideoFormat jpegFormat = new

VideoFormat(VideoFormat.JPEG_RTP); tracks[i].setFormat( jpegFormat);

인코딩 된 DataSource 를 획득 DataSource ds = processor.getDataOutput();

2.18

RTP Session Manager 를 이용한 송신

Session Manager 생성 , 초기화 SessionManager rtpMgrs = new

com.sun.media.rtp.RTPSessionMgr(); rtpMgrs.initSession(localAddr, userDesc, 0.05, 0.25); rtpMgrs.startSession(destAddr, 1, null);

SendStream 생성 , 전송 sendStream = rtpMgrs.createSendStream(ds, 0); sendStream.start();

2.19

Player 의 Start 와 Close

Player 의 생성단계 Unrealized → Realzing → Realized → Prefetching →

Prefetched → Started

Realized, Prefetched 된 후에 Start 가능 RealizeCompleteEvent PrefetchCompleteEvent 위의 두 메시지는 Realized, Prefetched 돼 됐을 때 발생

Player 를 Close 시킬때 stop() → deallocate() → close() 단계를 거쳐 close

2.20

Processor 의 Start 와 Close

Processor 는 Player 를 상속 받음

Processor 의 생성단계 Unrealized → Configuring → Configured → Realzing

→ Realized → Prefetching → Prefetched → Started Player 생성 단계에서 Configuring, Configured 추가 ConfigureCompleteEvent 추가

Processor 도 Player 와 같은 방법으로 Close

2.21

DataSink 를 이용한 저장

스트림에서 획득한 DataSource 를 이용하여 Processor 를 생성 Processor 에서 파일타입을 설정 Processor 에서 getDataOutput() 을 이용 , 변환된 DataSource 를

획득 획득한 DataSourcce 를 이용해서 DataSink 를 생성 예 )

p.setContentDescriptor(new FileTypeDescriptor(FileTypeDescriptor.WAVE));

DataSink sink; MediaLocator dest = new MediaLocator("file://newfile.wav"); try { sink = Manager.createDataSink(p.getDataOutput(), dest);

sink.open(); sink.start();

} catch (Exception e) {}

2.22

송신측 프로그램 ( 파일 재생 , 화상 캡쳐 전송 )

import java.awt.*;import java.io.*;import java.awt.event.*;import java.applet.Applet;

import java.util.*;import java.net.*;

import javax.media.*;import javax.media.rtp.*;import javax.media.rtp.rtcp.*;import com.sun.media.rtp.*;import javax.media.format.*;import javax.media.protocol.*;import javax.media.control.TrackControl;import javax.media.rtp.rtcp.SourceDescription;

public class DataTransmitApp2 extends Frame implements ActionListener{

Player player;Processor processor;

ControllerHandler controllerHandler = new ControllerHandler();

CaptureDeviceInfo cdi;DataSource dSource;DataSource dSource2;DataSink dSink;

private SessionManager rtpMgrs;

TextField status = new TextField();

MenuBar mb = new MenuBar();Menu fileMenu = new Menu("File");

MenuItem openFile = new MenuItem("Open File");

MenuItem openCap = new MenuItem("Open Capture");

MenuItem exit_ = new MenuItem("Exit");public DataTransmitApp2(){

setLayout(new BorderLayout());

add("North", status);

fileMenu.add(openFile);fileMenu.add(openCap);fileMenu.addSeparator();fileMenu.add(exit_);mb.add(fileMenu);setMenuBar(mb);

openFile.addActionListener(this);

openCap.addActionListener(this);exit_.addActionListener(this);status.setEnabled(false);

}

2.23

송신측 프로그램 ( 파일 재생 , 화상 캡쳐 전송 )

private void openFile(String mediaFile)

{

File file = new File(mediaFile);

String strFile = "file:///"+file.getPath();

try

{

URL mediaURL = new URL(strFile);

layer = Manager.createPlayer(mediaURL);

player.addControllerListener(controllerHandler);

player.realize();

}catch(Exception e)

{

System.out.println("Get Exception "+e);

}

}

private void openCapture()

{

// 비디오 캡쳐장치의 정보를 검색해서 벡터에 저장Vector captureDevices = CaptureDeviceManager.getDeviceList(new VideoFormat(VideoFormat.YUV));

if(captureDevices.size() > 0)

{

// 검색된 캡쳐 장치를 선택cdi = (CaptureDeviceInfo)captureDevices.firstElement();

}

try{// 캡쳐 장치의 위치 정보를 이용해서 DataSource 생성 dSource = Manager.createDataSource(cdi.getLocator()); // 캡쳐한 영상을 플레이 하기 위해 복제 dSource = Manager.createCloneableDataSource(dSource); dSource2 = ((SourceCloneable)dSource).createClone(); }catch(NoDataSourceException e) { System.err.println("NoDataSource"); }catch(IOException e) { System.err.println("IOException"); } try{ //DataSource 를 이용해서 Processor 생성 processor = Manager.createProcessor(dSource); //processor 의 상태 전환을 도와 주는 클래스 인스턴스 생성 sh = new StateHelper(processor); }catch(NoProcessorException e) { System.err.println("NoProcessor"); }catch(IOException e) { System.err.println("IOException "+e); }

2.24

송신측 프로그램 ( 파일 재생 , 화상 캡쳐 전송 )

// 프로세서를 configure 단계로 전환시키고 configure 완료 확인 위해 10 초까지 대기if(!sh.configure(10000))

{ System.err.println("Not configured");}

processor.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW_RTP)); //ContentDescriptor 설정

// 프로세서에서 트랙을 획득 : 오디오 및 비디오TrackControl tracks[] = processor.getTrackControls();if(tracks == null || tracks.length < 1){ System.err.println("Couldn't find tracks in processor");}// 인코딩을 확이하기 위한 플래그boolean programmed = false;for(int i = 0 ; i < tracks.length ; i++){// 획득한 트랙들의 포맷을 확인 Format format = tracks[i].getFormat();// 비디오 포맷일 경우if (tracks[i].isEnabled() && format instanceof VideoFormat && !programmed){

// 인코딩 할 포맷을 정한다

VideoFormat jpegFormat = new VideoFormat(VideoFormat.JPEG_RTP);// 포맷 변환 .... if(tracks[i].setFormat( jpegFormat) == null) { System.out.println("SetFormat Fail"); System.exit(0); } programmed = true;}else{ tracks[i].setEnabled(false);}}if(!programmed){ System.err.println( "Couldn't find video track");}

if(!sh.realize(10000)) // 프로세서를 realize 단계로 전환시키고 realize 완료 확인 위해 10 초까지 대기{ System.err.println("Not Realized");}// 포맷이 변환된 DataSource 를 획득DataSource ds = processor.getDataOutput();if(ds == null){ System.err.println("Data Source is null");}

2.25

송신측 프로그램 ( 파일 재생 , 화상 캡쳐 전송 )

try

{

// 전송과 동시에 자신도 볼 수 있게 플레이어 생성 player = Manager.createPlayer(dSource2);

player.addControllerListener(controllerHandler);

player.realize();

}catch(NoPlayerException e)

{

System.err.println("NoPlayerException");

}catch(IOException e)

{

System.err.println("IOExcption "+e);

}

//SessionManager 를 생성rtpMgrs = new RTPSessionMgr();

SessionAddress localAddr, destAddr;

InetAddress ipAddr;

SendStream sendStream;

int port;

SourceDescription srcDesList[];

SourceDescription userDesc[] = new SourceDescription[4];

for(int i = 0 ; i < userDesc.length ; i++)

{

if(i == 0)

{

userDesc[i] = new SourceDescription(SourceDescription.SOURCE_DESC_EMAIL, "[email protected]", 1, false);

continue;

}

if(i == 1)

{

userDesc[i] = new SourceDescription(SourceDescription.SOURCE_DESC_NAME, "jaxal", 1, false);

continue;

}

if(i == 2)

{

userDesc[i] = new SourceDescription(SourceDescription.SOURCE_DESC_CNAME, rtpMgrs.generateCNAME(), 1, false);

continue;

}

2.26

송신측 프로그램 ( 파일 재생 , 화상 캡쳐 전송 )

if(i == 3)

{

userDesc[i] = new SourceDescription(SourceDescription.SOURCE_DESC_TOOL, "JMF 2.1", 1, false); continue;}}try{ localAddr = new SessionAddress();

port = 30000;

ipAddr = InetAddress.getByName("224.119.252.80");

destAddr = new SessionAddress(ipAddr, port, ipAddr, port+1);// 세션을 초기화 한다rtpMgrs.initSession(localAddr, userDesc, 0.05, 0.25);rtpMgrs.startSession(destAddr, 1, null);//SendStream 을 생성하고 전송시작sendStream = rtpMgrs.createSendStream(ds, 0);sendStream.start();}catch(Exception e){ System.err.println("Transmit "+e);}}

class ControllerHandler implements ControllerListener

{

public synchronized void controllerUpdate(ControllerEvent e)

{ if(e instanceof RealizeCompleteEvent) { player.prefetch(); } else if(e instanceof PrefetchCompleteEvent) { Component comp; if((comp = player.getVisualComponent()) != null) { add("Center", comp); } if((comp = player.getControlPanelComponent()) != null) { add("South", comp); } player.start(); validate(); repaint(); } }}

2.27

송신측 프로그램 ( 파일 재생 , 화상 캡쳐 전송 )

public void actionPerformed(ActionEvent e){ if(e.getSource() == openFile) { if(processor != null) { sh.close(); } ClosePlayer(); FileDialog fileDlg = new FileDialog(this, "Select File"); fileDlg.setVisible(true); openFile(fileDlg.getDirectory()+fileDlg.getFile());}else if(e.getSource() == openCap){ ClosePlayer(); openCapture(); processor.start();}else if(e.getSource() == exit_){ System.exit(0);}}

private void ClosePlayer( ){ if ( player != null ) { player.stop(); player.deallocate(); player.close(); player = null; removeAll(); } }

public static void main(String args[])

{

DataTransmitApp2 dta = new DataTransmitApp2();

dta.setSize(200, 250);

dta.setVisible(true);

dta.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){System.exit(0);}});

}

}

2.28

수신 측 프로그램 (ReceiveStream2 클래스를 이용 )

import java.awt.*;import java.io.*;import javax.swing.*;import java.awt.event.*;class ReceiveApp2 extends JFrame {

JPanel panelVideo = new JPanel(new BorderLayout());

JPanel panelCenter = new JPanel(new GridLayout(1,2)); ReceiveStream2 rau = new ReceiveStream2(); JFileChooser m_chooser; public ReceiveApp2() { super("Video Receiver"); setSize(200, 250); JMenuBar menuBar = createMenuBar(); setJMenuBar(menuBar); m_chooser = new JFileChooser(); m_chooser.setCurrentDirectory(new File(".")); panelVideo.add(rau); panelCenter.add(panelVideo); getContentPane().setLayout(new BorderLayout()); getContentPane().add("Center", panelCenter); getContentPane().add("North", new JLabel(" ")); }

protected JMenuBar createMenuBar(){ final JMenuBar menuBar = new JMenuBar(); JMenu mFile = new JMenu("File"); mFile.setMnemonic('f'); JMenuItem item = new JMenuItem("Video Save"); item.setMnemonic('v'); ActionListener itemEvent = new ActionListener() { public void actionPerformed(ActionEvent e) { VideoReceive.this.repaint();

if(m_chooser.showSaveDialog(VideoReceive.this) != JFileChooser.APPROVE_OPTION)

{

return;

}

rau.sinkStart(m_chooser.getSelectedFile());

}

};

item.addActionListener(itemEvent);

mFile.add(item);

2.29

수신 측 프로그램 (ReceiveStream2 클래스를 이용 )

item = new JMenuItem("Exit");

item.setMnemonic('x');

itemEvent = new ActionListener()

{

public void actionPerformed(ActionEvent e)

{

rau.sink.close();

System.exit(0);

}

};

item.addActionListener(itemEvent);

mFile.add(item);

menuBar.add(mFile);

return menuBar;

}

public static void main(String args[])

{

ReceiveApp2 mr = new ReceiveApp2();

mr.setVisible(true);

mr.rau.StartSessionManager("224.119.252.80", 30000);

mr.setVisible(true);

mr.addWindowListener(new WindowAdapter()

{ public void windowClosing(WindowEvent e){ System.exit(0);}});

}

}

2.30

수신 측 프로그램 ( 화상수신 , 재생 , 저장 )

import java.awt.*;import java.awt.event.*;import java.io.*;import java.net.*;import java.util.*;

import javax.swing.*;import javax.media.*;import javax.media.control.*;import javax.media.rtp.*;import javax.media.rtp.rtcp.*;import javax.media.rtp.event.*;import javax.media.protocol.*;import javax.media.format.*;import javax.media.bean.playerbean.*;import com.sun.media.ui.*;

public class ReceiveStream2 extends Jpanel implements ControllerListener, ReceiveStreamListener{

static final int VIDEO_SAVE = 100;

Player player;

DataSource dSource;DataSource dSource2;

DataSink sink;String m_filePath;

public ReceiveStream2(){

setLayout(new BorderLayout());}

public synchronized void controllerUpdate(ControllerEvent ce){ Player pla = null; Controller control = (Controller)ce.getSource();

if(control instanceof Player) {

pla = (Player)ce.getSource(); }

2.31

수신 측 프로그램 ( 화상수신 , 재생 , 저장 )

if(ce instanceof RealizeCompleteEvent) { Component comp; if((comp = pla.getVisualComponent()) != null) { add("Center", comp); } if((comp=pla.getControlPanelComponent()) != null) { add("South", comp); } validate(); }}

public void update(ReceiveStreamEvent ev){ Player pl = null;

// 새로운 스트림이 수신 되었을때 if(ev instanceof NewReceiveStreamEvent) { //ReceiveStream 을 생성 ReceiveStream stream = ((NewReceiveStreamEvent)ev) .getReceiveStream();

//ReceiveStream 에서 DataSource 획득 try

{

// 획득한 DataSource 로 플레이어 생성 pl = Manager.createPlayer(dSource);

}catch(NoPlayerException e)

{

//status.setText("NoPlayerException");

System.err.println(e);

}catch(IOException e)

{

//status.setText("IOException");

System.err.println(e);

}

pl.addControllerListener(this);

2.32

수신 측 프로그램 ( 화상수신 , 재생 , 저장 )

// 플레이어 스타트 pl.start(); }} //Session Manager 를 생성public SessionManager StartSessionManager (String destAddr, int port){ InetAddress destaddr = null; SessionManager RTPMgr = new com.sun.media.rtp.RTPSessionMgr();

//ReceiveStreamListener 를 등록 RTPMgr.addReceiveStreamListener(this); SessionAddress localaddr = new SessionAddress(); try {destaddr=InetAddress.getByName(destAddr); }catch(UnknownHostException e) { //status.setText("UnknownHostException"); System.err.println(e); }

SessionAddress destAddress = new SessionAddress(destaddr, port, destaddr, port+1); SourceDescription userDesc[] = new SourceDescription[4];

for(int i = 0 ; i < userDesc.length ; i++) { if(i == 0) { userDesc[i] = new SourceDescription (SourceDescription.SOURCE_DESC_EMAIL, "[email protected]", 1, false); continue; } if(i == 1) { userDesc[i] = new SourceDescription (SourceDescription.SOURCE_DESC_NAME, "jaxal", 1, false); continue; } if(i == 2) { userDesc[i] = new SourceDescription (SourceDescription.SOURCE_DESC_CNAME, RTPMgr.generateCNAME(), 1, false); continue; }

2.33

수신 측 프로그램 ( 화상수신 , 재생 , 저장 )

if(i == 3) { userDesc[i] = new SourceDescription (SourceDescription.SOURCE_DESC_TOOL, "JMF 2.1", 1, false); continue; }}try{ //Session Manager 초기화 RTPMgr.initSession(localaddr, userDesc, 0.05, 0.25); RTPMgr.startSession(destAddress, 1, null); }catch(SessionManagerException e) { //status.setText("SessionManagerException “ // + e); System.err.println(e); }catch(IOException e) { //status.setText("IOException "+e); System.err.println(e); }return RTPMgr;}

// 파일로 저장하기 위한 초기화public void sinkStart(File selected){ Processor processor = null; StateHelper sh = null;

try { processor = Manager.createProcessor(dSource); sh = new StateHelper(processor); }catch(Exception e) { System.err.println(e); } if(!sh.configure(10000)) { System.err.println("configure fail"); } processor.setContentDescriptor (new FileTypeDescriptor (FileTypeDescriptor.MSVIDEO));

if(!sh.realize(10000)) { System.err.println("realize fail"); System.exit(0); }

2.34

수신 측 프로그램 ( 화상수신 , 재생 , 저장 )

DataSource ds = processor.getDataOutput(); try { MediaLocator media = new MediaLocator("file:///"+selected.getPath()); sink = Manager.createDataSink(ds, media); sink.open(); System.out.println("Saving........"); }catch(Exception e) { System.err.println("Sink Exception "+e); } StreamWriterControl swc = (StreamWriterControl)processor.getContRol ("javax.media.control.StreamWriterControl");

if(swc != null) { swc.setStreamSizeLimit(5000000); }

try { sink.start(); }catch(Exception e) { System.err.println(e); System.exit(0); }

sh.palyToEndOfMedia(30000);sh.close();sink.close();

}

}