App Programming/JAVA2008. 5. 8. 10:50
출처:http://kineo2k.tistory.com/2

Open Study and Java 모임에서 세미나 했던 내용이다.
IO와 NIO를 테스트하는 간단한 예제 소스이다.
앞선 COPY 예제에서는 IO와 NIO의 차이가 극명히 나타나지는 않지만, MP3를 잘라내는 프로그램에서는 성능 차이가 크게는 5~6배까지 나기도 한다.

Watch.java

public class Watch
{
 private long start = 0L;
 private long end = 0L;
 
 public Watch()
 {}
 
 public void click()
 {
  if(start != 0L)
   end = System.nanoTime();
  else
   start = System.nanoTime();
 }
 
 public double doubleSec()
 {
  return (double)(end - start)/1000000000;
 }
 
 public void showTime()
 {
  System.out.println("구동시간 : "+doubleSec()+"sec");
 }
 
 public static void main(String[] args) throws Exception
 {
  Watch w = new Watch();
  w.click();
  Thread.sleep(1000);
  w.click();
  w.showTime();
 }
}


 

File Copy



COPY_IO.java

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class COPY_IO
{
 private FileInputStream in  = null;
 private FileOutputStream out = null;
 private String sourcePath  = null;
 private String destPath   = null;
 private Watch watch    = null;
 
 public COPY_IO(String _sourcePath, String _destPath) throws IOException
 {
  sourcePath = _sourcePath;
  destPath = _destPath;
  in   = new FileInputStream(sourcePath);
  out   = new FileOutputStream(destPath);
  watch  = new Watch();
 }
 
 public void copy() throws IOException
 {
  byte[] buf  = new byte[4096];
  int readBytes = 0x00;
 
  watch.click(); // 복사 시작
  while((readBytes = in.read(buf)) > -1)
   out.write(buf, 0, readBytes);
  watch.click(); // 복사 종료
 
  if(in  != null) { in.close();  in  = null; }
  if(out != null) { out.close(); out = null; }
 }
 
 public void showResult()
 {
  File source = new File(sourcePath);
  File dest = new File(destPath);
 
  System.out.println("원   본 : 파일명 - "+source.getName()+"\t파일크기 - "+dest.length());
  System.out.println("복사본 : 파일명 - "+dest.getName()+"\t파일크기 - "+dest.length());
 
  System.out.println("\n복사시간 : "+watch.doubleSec()+"sec");
 }
 
 public static void main(String[] args) throws Exception
 {
  COPY_IO test = new COPY_IO("E:\\edit\\source", "E:\\edit\\dest_io");
  test.copy();
  test.showResult();
 }
}


COPY_NIO.java

import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.channels.FileChannel;

public class COPY_NIO
{
 private FileChannel in  = null;
 private FileChannel out  = null;
 private String sourcePath = null;
 private String destPath  = null;
 private Watch watch   = null;
 
 public COPY_NIO(String _sourcePath, String _destPath) throws IOException
 {
  sourcePath = _sourcePath;
  destPath = _destPath;
  in   = new RandomAccessFile(sourcePath, "r").getChannel();
  out   = new RandomAccessFile(destPath, "rw").getChannel();
  watch  = new Watch();
 }
 
 public void copy() throws IOException
 {
  watch.click(); // 복사 시작
  in.transferTo(0, in.size(), out);
//  out.transferFrom(in, 0, in.size());
  watch.click(); // 복사 종료
 
  if(in  != null) { in.close();  in  = null; }
  if(out != null) { out.close(); out = null; }
 }
 
 public void showResult()
 {
  File source = new File(sourcePath);
  File dest = new File(destPath);
 
  System.out.println("원   본 : 파일명 - "+source.getName()+"\t파일크기 - "+dest.length());
  System.out.println("복사본 : 파일명 - "+dest.getName()+"\t파일크기 - "+dest.length());
 
  System.out.println("\n복사시간 : "+watch.doubleSec()+"sec");
 }
 
 public static void main(String[] args) throws Exception
 {
  COPY_NIO test = new COPY_NIO("E:\\edit\\source", "E:\\edit\\dest_nio");
  test.copy();
  test.showResult();
 }
}



File Handler



MP3Cutter_IO.java


import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;

public class MP3Cutter_IO
{
 private final int MPEG_VERSION1  = 3;
 private final int MPEG_VERSION2  = 2;
 private final int MPEG_VERSION2_5 = 0;
 
 private final int[][] BITRATES =
 {
  {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1},
  {0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, -1},
  {0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, -1},
  {0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, -1},
  {0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, -1},
 };
 
 private final int[][] SAMPLERATES =
 {
  {11025, 12000, 8000, -1},  // Version 2.5
  {-1, -1, -1, -1},    // Unknown version
  {22050, 24000, 16000, -1},  // Version 2
  {44100, 44800, 32000, -1},  // Version 1
 };
 
 private RandomAccessFile in  = null;
 private RandomAccessFile out = null;
 private String sourcePath  = null;
 private String destPath   = null;
 private int startPoint   = 0x00;
 private int endPoint   = 0x00;
 private Watch watch    = null;
 
 public MP3Cutter_IO(String _sourcePath, String _destPath, int _startPoint, int _endPoint) throws IOException
 {
  sourcePath = _sourcePath;
  destPath = _destPath;
  startPoint = _startPoint;
  endPoint = _endPoint;
  in   = new RandomAccessFile(sourcePath, "r");
  out   = new RandomAccessFile(destPath, "rw");
  watch  = new Watch();
 }
 
 // startPoint와 endPoint사이의 mp3데이타를 새로운 파일에 저장
 public void cut() throws IOException
 {
  watch.click();
  byte[] buf    = null;
  int readBytes   = 0x00;
  long mp3Frame    = 0x00;
  int currentTimestamp = 0;
  long start    = 0L;
  long size    = 0L;
 
  // 잘라내기 시작할 지점보다 이전 부분이면 스킵한다.
  while(currentTimestamp < startPoint)
  {
   mp3Frame = readUnsignedInt();
   in.skipBytes(rawDataSize(mp3Frame));
   currentTimestamp += 26;
  }
 
  // 여기부터 endPoint까지 잘라낼 구간이다.
  start = in.getFilePointer();
  while(currentTimestamp < endPoint)
  {
   mp3Frame = readUnsignedInt();
   in.skipBytes(rawDataSize(mp3Frame));  
   currentTimestamp += 26;
  }
  size = in.getFilePointer() - start;
  buf  = new byte[(int)size];
  in.readFully(buf);
  out.write(buf);
 
  // endPoint까지 데이타 작성완료. 스트림을 닫는다.
  if(in  != null) { in.close();  in = null; }
  if(out != null) { out.close(); out = null; }
  watch.click();
 }
 
 /* MP3 프레임 헤더를 가져오기 위해서 작성해준 메소드
  * 싱크 비트가 '1111 1111 111' 으로 시작하는데 이는 첫 자리가 1인 음수 정수 값이 된다.
  * 음수는 문제가 될 가능성이 있으므로 long타입의 하위 4바이트만 사용하는 방식으로
  * 문제가 발생할 가능성을 방지했다.
  */
 private long readUnsignedInt() throws IOException
 {
  long rv = 0L;
  rv = rv | in.readInt();
  /*
  rv = in.readUnsignedByte();
  rv = (rv << 8) & 0xFFFFFFFFL | in.readUnsignedByte();
  rv = (rv << 8) & 0xFFFFFFFFL | in.readUnsignedByte();
  rv = (rv << 8) & 0xFFFFFFFFL | in.readUnsignedByte();
  */
  return rv;
 }
 
 // MP3의 프레임에 로우데이타의 크기를 계산하는 메소드
 private int rawDataSize(long mp3Frame)
 {
  int rawDataSize  = 0;
  int mpegVersion  = 0;  // 2
  int layer   = 0;  // 2
  int bitrate   = 0;  // 4
  int samplingRate = 0;  // 2
  int paddingBit  = 0;  // 1
  int col1=0, col2=0;
  int row1=0, row2=0;
 
  // 여기부터는 MP3 Frame의 각 정보를 추출
  mpegVersion  = (int)(((mp3Frame << 11) & 0x00000000FFFFFFFFL) >> 30);
  layer   = (int)(((mp3Frame << 13) & 0x00000000FFFFFFFFL) >> 30);
  bitrate   = (int)(((mp3Frame << 16) & 0x00000000FFFFFFFFL) >> 28);
  samplingRate  = (int)(((mp3Frame << 20) & 0x00000000FFFFFFFFL) >> 30);
  paddingBit  = (int)(((mp3Frame << 22) & 0x00000000FFFFFFFFL) >> 31);
 
  // 이 부분은 Bitrate 인덱스 세팅 부분
  if(mpegVersion == 3 && layer == 3)
   row1 = 0;
  else if(mpegVersion == 3 && layer == 2)
   row1 = 1;
  else if(mpegVersion == 3 && layer == 1)
   row1 = 2;
  else if(mpegVersion == 2 && layer == 3)
   row1 = 3;
  else if(mpegVersion == 2 && (layer == 2 || layer == 1))
   row1 = 4;
  col1 = (int)bitrate;
 
  // 이 부분은 Sampling rate 세팅 부분
  if(mpegVersion == MPEG_VERSION1)
   row2 = 3;
  else if(mpegVersion == MPEG_VERSION2)
   row2 = 2;
  else if(mpegVersion == MPEG_VERSION2_5)
   row2 = 0;
 
  if(samplingRate == 0)
   col2 = 0;
  if(samplingRate == 1)
   col2 = 1;
  if(samplingRate == 2)
   col2 = 2;
 
  /** Raw Data 크기 계산 공식
   * The size of the sample data is calculated like this (using integer arithmetic):
   * Size = (((MpegVersion == MPEG1 ? 144 : 72) * Bitrate) / SamplingRate) + PaddingBit - 4
   * For example: The size of the sample data for an MPEG1 frame with a Bitrate of 128000,
   * a SamplingRate of 44100, and PaddingBit of 1 is:
   * Size = (144 * 128000) / 44100 + 1 ? 4 = 414 bytes
   */
  rawDataSize = (int)((((mpegVersion == MPEG_VERSION1 ? 144 : 72)
    * BITRATES[row1][col1] * 1000)
    / SAMPLERATES[row2][col2])
    + paddingBit-4);
 
  return rawDataSize;
 }
 
 // 결과 출력
 public void showResult()
 {
  File source = new File(sourcePath);
  File dest = new File(destPath);
 
  System.out.println("원본 파일명    - "+source.getName()+"\t파일크기 - "+source.length());
  System.out.println("잘라낸 파일명 - "+dest.getName()+"\t파일크기 - "+dest.length());
  System.out.println("\n복사시간 : "+watch.doubleSec()+"sec");
 }
 
 public static void main(String[] args) throws Exception
 {
  MP3Cutter_IO test = new MP3Cutter_IO("E:\\edit\\111.mp3", "E:\\edit\\111_cut_io.mp3", 10000, 30000);
  test.cut();
  test.showResult();
 }
}



MP3Cutter_NIO.java

import java.io.File;
import java.io.RandomAccessFile;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.MappedByteBuffer;

public class MP3Cutter_NIO
{
 private final int MPEG_VERSION1  = 3;
 private final int MPEG_VERSION2  = 2;
 private final int MPEG_VERSION2_5 = 0;
 
 private final int[][] BITRATES =
 {
  {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1},
  {0, 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, -1},
  {0, 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, -1},
  {0, 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, -1},
  {0,  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, -1},
 };
 
 private final int[][] SAMPLERATES =
 {
  {11025, 12000, 8000, -1},  // Version 2.5
  {-1, -1, -1, -1},    // Unknown version
  {22050, 24000, 16000, -1},  // Version 2
  {44100, 44800, 32000, -1},  // Version 1
 };
 
 private FileChannel in  = null;
 private FileChannel out  = null;
 private MappedByteBuffer m = null;
 private String sourcePath = null;
 private String destPath  = null;
 private int startPoint  = 0x00;
 private int endPoint  = 0x00;
 private Watch watch   = null;
 
 public MP3Cutter_NIO(String _sourcePath, String _destPath, int _startPoint, int _endPoint) throws IOException
 {
  sourcePath = _sourcePath;
  destPath = _destPath;
  startPoint = _startPoint;
  endPoint = _endPoint;
  in   = new RandomAccessFile(sourcePath, "r").getChannel();
  out   = new RandomAccessFile(destPath, "rw").getChannel();
  m   = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());;
  watch  = new Watch();
 }
 
 // startPoint와 endPoint사이의 mp3데이타를 새로운 파일에 저장
 public void cut() throws IOException
 {
  watch.click();
  long mp3Frame    = 0x00;
  int currentTimestamp = 0;
  long start    = 0L;
  long size    = 0L;
 
  // 잘라내기 시작할 지점보다 이전 부분이면 스킵한다.
  while(currentTimestamp < startPoint)
  {
   mp3Frame = readUnsignedInt();
   skip(rawDataSize(mp3Frame));
   currentTimestamp += 26;
  }

  // 여기부터 endPoint가 잘라낸 구간이다.
  start = m.position();
  while(currentTimestamp < endPoint)
  {
   mp3Frame = readUnsignedInt();
   skip(rawDataSize(mp3Frame));
   currentTimestamp += 26;
  }
  size = m.position() - start;
  in.transferTo(start, size, out);

  // endPoint까지 데이타 작성완료. 스트림을 닫는다.
  if(in  != null) { in.close();  in = null; }
  if(out != null) { out.close(); out = null; }
  watch.click();
 }
 
 /* MP3 프레임 헤더를 가져오기 위해서 작성해준 메소드
  * 싱크 비트가 '1111 1111 111' 으로 시작하는데 이는 첫 자리가 1인 음수 정수 값이 된다.
  * 음수는 문제가 될 가능성이 있으므로 long타입의 하위 4바이트만 사용하는 방식으로
  * 문제가 발생할 가능성을 방지했다.
  */
 private long readUnsignedInt() throws IOException
 {
  long rv = 0L;
  rv = rv | m.getInt();
  return rv;
 }
 
 // 지정된  size만큼 skip
 private void skip(int size) throws IOException
 {
  m.position(m.position()+size);
 }
 
 // MP3의 프레임에 로우데이타의 크기를 계산하는 메소드
 private int rawDataSize(long mp3Frame)
 {
  int rawDataSize  = 0;
  int mpegVersion  = 0;  // 2
  int layer   = 0;  // 2
  int bitrate   = 0;  // 4
  int samplingRate = 0;  // 2
  int paddingBit  = 0;  // 1
  int col1=0, col2=0;
  int row1=0, row2=0;
 
  // 여기부터는 MP3 Frame의 각 정보를 추출
  mpegVersion  = (int)(((mp3Frame << 11) & 0x00000000FFFFFFFFL) >> 30);
  layer   = (int)(((mp3Frame << 13) & 0x00000000FFFFFFFFL) >> 30);
  bitrate   = (int)(((mp3Frame << 16) & 0x00000000FFFFFFFFL) >> 28);
  samplingRate  = (int)(((mp3Frame << 20) & 0x00000000FFFFFFFFL) >> 30);
  paddingBit  = (int)(((mp3Frame << 22) & 0x00000000FFFFFFFFL) >> 31);
 
  // 이 부분은 Bitrate 인덱스 세팅 부분
  if(mpegVersion == 3 && layer == 3)
   row1 = 0;
  else if(mpegVersion == 3 && layer == 2)
   row1 = 1;
  else if(mpegVersion == 3 && layer == 1)
   row1 = 2;
  else if(mpegVersion == 2 && layer == 3)
   row1 = 3;
  else if(mpegVersion == 2 && (layer == 2 || layer == 1))
   row1 = 4;
  col1 = (int)bitrate;
 
  // 이 부분은 Sampling rate 세팅 부분
  if(mpegVersion == MPEG_VERSION1)
   row2 = 3;
  else if(mpegVersion == MPEG_VERSION2)
   row2 = 2;
  else if(mpegVersion == MPEG_VERSION2_5)
   row2 = 0;
 
  if(samplingRate == 0)
   col2 = 0;
  if(samplingRate == 1)
   col2 = 1;
  if(samplingRate == 2)
   col2 = 2;
 
  /** Raw Data 크기 계산 공식
   * The size of the sample data is calculated like this (using integer arithmetic):
   * Size = (((MpegVersion == MPEG1 ? 144 : 72) * Bitrate) / SamplingRate) + PaddingBit - 4
   * For example: The size of the sample data for an MPEG1 frame with a Bitrate of 128000,
   * a SamplingRate of 44100, and PaddingBit of 1 is:
   * Size = (144 * 128000) / 44100 + 1 ? 4 = 414 bytes
   */
  rawDataSize = (int)((((mpegVersion == MPEG_VERSION1 ? 144 : 72)
    * BITRATES[row1][col1] * 1000)
    / SAMPLERATES[row2][col2])
    + paddingBit-4);
 
  return rawDataSize;
 }
 
 // 결과 출력
 public void showResult()
 {
  File source = new File(sourcePath);
  File dest = new File(destPath);
 
  System.out.println("원본 파일명    - "+source.getName()+"\t파일크기 - "+source.length());
  System.out.println("잘라낸 파일명 - "+dest.getName()+"\t파일크기 - "+dest.length());
  System.out.println("\n복사시간 : "+watch.doubleSec()+"sec");
 }
 
 public static void main(String[] args) throws Exception
 {
  MP3Cutter_NIO test = new MP3Cutter_NIO("E:\\edit\\111.mp3", "E:\\edit\\111_cut_nio.mp3", 10000, 30000);
  test.cut();
  test.showResult();
 }
}



Posted by BAGE