JMF의 Control중에서 Spec상에서는 FrameRateControl을 통해서 framerate를 변경할수 있도록 되어 있다. Framerate를 변경하는 코드는 다음과 같은데..
-------------------------------------------------------------------------------
Control control = recordProcessor.getControl("javax.media.control.FrameRateControl");
if ( control != null &&
control instanceof javax.media.control.FrameRateControl )
((javax.media.control.FrameRateControl)control).setFrameRate(15.0f);
-------------------------------------------------------------------------------
실제로 setFrameRate를 하게 되면 -1이 리턴하여 변경이 되지 않는다.
이전에 보았던 Customized Push BufferStream의 구현 과 Customized DataSource를 만들어 보자 를 참고하여 framerate를 감소시켜서 RTP로 전송하는 프로그램을 만들어 보도록 하겠다.
참고를 해야 하는 소스 파일은
http://java.sun.com/products/java-media/jmf/2.1.1/solutions/LiveData.html
의 DataSource.java 및 LiveStream.java 그리고
http://www.exactfutures.com/vid2jpg.zip
의 vid2jpg 클래스들
http://java.sun.com/products/java-media/jmf/2.1.1/solutions/VideoTransmit.html
의 VideoTransmit.java
가 참고 되어야 한다.
1. vid2jpg.java의 분석
[DataSourceHandler 내부 클래스 분석]
- vid2jpg 클래스에서는 DataSourceHandler라는 내부 클래스가 존재한다. 이 클래스는 BufferTransferHandler를 구현한 클래스로 BufferTransferHandler는 PushBufferStream의 callback에 해당한다. PushBufferStream은 push될 data가 가능할 경우 notify를 할 필요가 있다.
PushBufferStream의 setTransferHandler 메소드를 이용하여 Handler클래스를 등록하고 Handler에 transferData(PushBufferStream stream) 함수를 구현(Override?)하여 데이터를 가져와서 처리 가능하다
DataSourceHandler의 주요한 코드는 다음과 같다.
private void setSource(DataSource source) throws IncompatibleSourceException 의
pushStrms = ((PushBufferDataSource) source).getStreams();
pushStrms[0].setTransferHandler(this);
public void transferData(PushBufferStream stream) 의
stream.read(readBuffer);
// Just in case contents of data object changed by some other thread
Buffer inBuffer = (Buffer)(readBuffer.clone());
useFrameData(inBuffer); (이 메소드는 vid2jpg의 메소드 이다)
DataSourceHandler의 주요한 기능은 DataSourceHandler.setSource 메소드로 설정된 데이터 소스에 대해서 데이터가 준비가 되면 해당 데이터를 읽어서 Buffer에 세팅하고 이 Buffer를 가지고 vid2jpg의 userFrameData에서 처리하도록 하게 하는 것이다.
[vid2jpg]분석
1. Program Start
public static void main(String[] args)
new vid2jpg("미디어 소스 위치");
2. vid2jpg 생성자
public vid2jpg(String path)
if((ml = new MediaLocator(args)) == null) [미디어 로케이터 설정]
if(!open(ml)) [vid2jpg::open 호출]
3. vid2jpg::open 메소드
private boolean open(MediaLocator ml)
p = Manager.createProcessor(ml); [Processor 생성]
p.addControllerListener(this); [Listener 등록]
p.configure(); [Processor의 Configure상태로 전이]
p.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW));
[컨텐츠를 RAW TYPE으로 설정]
TrackControl tc[] = p.getTrackControls(); [Track을 가져옴]
tc[i].setFormat(new RGBFormat(null, -1, Format.byteArray, -1.0F, 24, 3, 2, 1));
[ Video Format에 대해서 RAW/RGB Format으로 설정함]
p.realize(); [ Processor를 Realize 상태로 전이 ]
DataSource ods = p.getDataOutput();
handler = new DataSourceHandler(); [DataSourceHandler를 생성]
handler.setSource(ods); [ DataSourceHandler에 DataSource 등록]
handler.start(); [ DataSourceHandler를 Start ]
p.prefetch(); [ Processor를 Prefetch 상태로 전이]
p.start(); [ Processor를 Start]
- 3번까지 하게 되면 지정한 미디어 소스에서 데이터를 가져 오게 되고(DataSource) 이 데이터가 읽혀질때마나 handler(DataSourceHandler의 transferData가 호출되게 된다. 이때 transferData는 vid2jpg의 useFrameData를 호출하여 읽혀진 Buffer를 전달한다.
4. useFrameData
private void useFrameData(Buffer inBuffer)
countFr++;
printDataInfo(inBuffer) [inBuffer의 info 출력]
if(inBuffer.getData()!=null) // vfw://0 can deliver nulls [데이터를 가져와서]
if(outvid==null)outvid = new int[imgWidth*imgHeight];
outdataBuffer(outvid,(byte[])inBuffer.getData()); [출력할 형태로 변환하고]
setImage(outvid); [panel에 출력
jpeg포멧으로 파일 저장하는 코드 [jpeg파일로 저장]
- 4번에서 주목해야될 내용은 전달되는 각 프레임을 하나하나 제어가 가능하다는 것이다. 따라서 프레임 레이트를 감소 시키려면 useFrameData에 전달된 프레임중 일부를 누락시켜서 다른 DataSource로 전달하면 FrameRate를 줄일수 있다.
다음 포스트에 계속
[JMF] Media의 Framerate 를 실시간 감소 시키기
2007. 4. 23. 14:45
반응형
반응형