package org.jcodec.movtool.streaming.tracks; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channels; import org.jcodec.codecs.wav.WavHeader; import org.jcodec.common.NIOUtils; import org.jcodec.common.SeekableByteChannel; import org.jcodec.containers.mp4.boxes.AudioSampleEntry; import org.jcodec.containers.mp4.boxes.ChannelBox; import org.jcodec.containers.mp4.boxes.EndianBox.Endian; import org.jcodec.containers.mp4.boxes.SampleEntry; import org.jcodec.containers.mp4.boxes.channel.ChannelUtils; import org.jcodec.containers.mp4.boxes.channel.Label; import org.jcodec.containers.mp4.muxer.MP4Muxer; import org.jcodec.movtool.streaming.VirtualPacket; import org.jcodec.movtool.streaming.VirtualTrack; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * Reads data from a wave file * * @author The JCodec project * */ public class WavTrack implements VirtualTrack { public static final int FRAMES_PER_PKT = 1024; private ByteChannelPool pool; private WavHeader header; private AudioSampleEntry se; private int pktDataLen; private double pktDuration; private long offset; private double pts; private int frameNo; private long size; public WavTrack(ByteChannelPool pool, Label... labels) throws IOException { this.pool = pool; SeekableByteChannel ch = null; try { ch = pool.getChannel(); header = WavHeader.read(Channels.newInputStream(ch)); size = header.dataSize <= 0 ? ch.size() : header.dataSize; } finally { ch.close(); } se = MP4Muxer.audioSampleEntry("sowt", 1, header.fmt.bitsPerSample >> 3, header.fmt.numChannels, header.fmt.sampleRate, Endian.LITTLE_ENDIAN); ChannelBox chan = new ChannelBox(); if (labels != null && labels.length > 0) { ChannelUtils.setLabels(labels, chan); } else { labels = new Label[header.getFormat().getChannels()]; for (int i = 0; i < labels.length; i++) labels[i] = Label.Mono; ChannelUtils.setLabels(labels, chan); } se.add(chan); pktDataLen = FRAMES_PER_PKT * header.fmt.numChannels * (header.fmt.bitsPerSample >> 3); pktDuration = (double) FRAMES_PER_PKT / header.fmt.sampleRate; offset = header.dataOffset; pts = 0; frameNo = 0; } @Override public VirtualPacket nextPacket() throws IOException { if (offset >= size) return null; WavPacket pkt = new WavPacket(frameNo, pts, offset, (int) Math.min(size - offset, pktDataLen)); offset += pktDataLen; frameNo += FRAMES_PER_PKT; pts = (double) frameNo / header.fmt.sampleRate; return pkt; } @Override public SampleEntry getSampleEntry() { return se; } @Override public VirtualEdit[] getEdits() { return null; } @Override public int getPreferredTimescale() { return header.fmt.sampleRate; } @Override public void close() throws IOException { pool.close(); } public class WavPacket implements VirtualPacket { private int frameNo; private double pts; private long offset; private int dataLen; public WavPacket(int frameNo, double pts, long offset, int dataLen) { this.frameNo = frameNo; this.pts = pts; this.offset = offset; this.dataLen = dataLen; } @Override public ByteBuffer getData() throws IOException { SeekableByteChannel ch = null; try { ch = pool.getChannel(); ch.position(offset); ByteBuffer buffer = ByteBuffer.allocate(dataLen); NIOUtils.read(ch, buffer); buffer.flip(); return buffer; } finally { ch.close(); } } @Override public int getDataLen() throws IOException { return dataLen; } @Override public double getPts() { return pts; } @Override public double getDuration() { return pktDuration; } @Override public boolean isKeyframe() { return true; } @Override public int getFrameNo() { return frameNo; } } }