001package net.bramp.ffmpeg.nut; 002 003import static com.google.common.base.Preconditions.checkArgument; 004import static com.google.common.base.Preconditions.checkNotNull; 005import static net.bramp.ffmpeg.nut.StreamHeaderPacket.fourccToString; 006 007import java.awt.image.BufferedImage; 008import java.io.ByteArrayInputStream; 009import java.io.InputStream; 010import java.nio.ByteBuffer; 011import java.nio.ByteOrder; 012import java.nio.IntBuffer; 013import java.util.Arrays; 014import javax.sound.sampled.AudioFormat; 015import javax.sound.sampled.AudioInputStream; 016 017public class RawHandler { 018 019 private static int[] bytesToInts(byte[] bytes) { 020 IntBuffer buf = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).asIntBuffer(); 021 022 int[] data = new int[buf.capacity()]; 023 buf.get(data); 024 return data; 025 } 026 027 public static BufferedImage toBufferedImage(Frame frame) { 028 checkNotNull(frame); 029 030 final StreamHeaderPacket header = frame.stream.header; 031 032 checkArgument(header.type == StreamHeaderPacket.VIDEO); 033 034 // DataBufferByte buffer = new DataBufferByte(frame.data, frame.data.length); 035 // SampleModel sample = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, 036 // streamHeader.width, streamHeader.height, 1); 037 // Raster raster = new Raster(sample, buffer, new Point(0,0)); 038 039 int type = BufferedImage.TYPE_INT_ARGB; // TODO Use the type defined in the stream header. 040 BufferedImage img = new BufferedImage(header.width, header.height, type); 041 042 // TODO Avoid this conversion. 043 int[] data = bytesToInts(frame.data); 044 int stride = header.width; // TODO Check this is true 045 img.setRGB(0, 0, header.width, header.height, data, 0, stride); 046 047 return img; 048 } 049 050 /** 051 * Parses a FourCC into a AudioEncoding based on the following rules:<br> 052 * "ALAW" = A-LAW<br> 053 * "ULAW" = MU-LAW<br> 054 * P[type][interleaving][bits] = little-endian PCM<br> 055 * [bits][interleaving][type]P = big-endian PCM<br> 056 * Where:<br> 057 * [type] is S for signed integer, U for unsigned integer, F for IEEE float<br> 058 * [interleaving] is D for default, P is for planar.<br> 059 * [bits] is 8/16/24/32<br> 060 * 061 * @param header The stream's header. 062 * @return The AudioFormat matching this header. 063 */ 064 public static AudioFormat streamToAudioFormat(final StreamHeaderPacket header) { 065 checkNotNull(header); 066 checkArgument(header.type == StreamHeaderPacket.AUDIO); 067 068 // Vars that go into the AudioFormat 069 AudioFormat.Encoding encoding; 070 float sampleRate = header.sampleRate.floatValue(); 071 int bits = 8; 072 boolean bigEndian = false; 073 074 final byte[] fourcc = header.fourcc; 075 076 if (Arrays.equals(fourcc, new byte[] {'A', 'L', 'A', 'W'})) { 077 encoding = AudioFormat.Encoding.ALAW; 078 079 } else if (Arrays.equals(fourcc, new byte[] {'U', 'L', 'A', 'W'})) { 080 encoding = AudioFormat.Encoding.ULAW; 081 082 } else if (fourcc.length == 4) { 083 byte type; 084 byte interleaving; 085 086 if (fourcc[0] == 'P') { 087 bigEndian = false; 088 type = fourcc[1]; 089 interleaving = fourcc[2]; 090 bits = fourcc[3]; 091 } else if (fourcc[3] == 'P') { 092 bigEndian = true; 093 type = fourcc[2]; 094 interleaving = fourcc[1]; 095 bits = fourcc[0]; 096 } else { 097 throw new IllegalArgumentException( 098 "unknown fourcc value: '" + fourccToString(fourcc) + "'"); 099 } 100 101 if (interleaving != 'D') { 102 throw new IllegalArgumentException( 103 "unsupported interleaving '" 104 + interleaving 105 + "' in fourcc value '" 106 + fourccToString(fourcc) 107 + "'"); 108 } 109 110 switch (type) { 111 case 'S': 112 encoding = AudioFormat.Encoding.PCM_SIGNED; 113 break; 114 case 'U': 115 encoding = AudioFormat.Encoding.PCM_UNSIGNED; 116 break; 117 case 'F': 118 encoding = AudioFormat.Encoding.PCM_FLOAT; 119 break; 120 default: 121 throw new IllegalArgumentException( 122 "unknown fourcc '" + fourccToString(fourcc) + "' type: " + type); 123 } 124 } else { 125 throw new IllegalArgumentException("unknown fourcc value: '" + fourccToString(fourcc) + "'"); 126 } 127 128 int frameSize = (bits * header.channels) / 8; 129 float frameRate = sampleRate; // This may not be true for the compressed formats 130 131 return new AudioFormat( 132 encoding, sampleRate, bits, header.channels, frameSize, frameRate, bigEndian); 133 } 134 135 public static AudioInputStream toAudioInputStream(Frame frame) { 136 checkNotNull(frame); 137 final StreamHeaderPacket header = checkNotNull(frame.stream.header); 138 checkArgument(header.type == StreamHeaderPacket.AUDIO); 139 140 AudioFormat format = streamToAudioFormat(header); 141 InputStream stream = new ByteArrayInputStream(frame.data); 142 143 return new AudioInputStream(stream, format, frame.data.length / format.getFrameSize()); 144 } 145}