package org.jcodec.containers.mp4.demuxer;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.DemuxerTrackMeta;
import org.jcodec.common.TrackType;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Packet;
import org.jcodec.common.model.Packet.FrameType;
import org.jcodec.containers.mp4.MP4Packet;
import org.jcodec.containers.mp4.QTTimeUtil;
import org.jcodec.containers.mp4.boxes.AudioSampleEntry;
import org.jcodec.containers.mp4.boxes.Box;
import org.jcodec.containers.mp4.boxes.MovieBox;
import org.jcodec.containers.mp4.boxes.NodeBox;
import org.jcodec.containers.mp4.boxes.SampleEntry;
import org.jcodec.containers.mp4.boxes.SampleSizesBox;
import org.jcodec.containers.mp4.boxes.TrakBox;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Specialized demuxer track for PCM audio samples
*
* Always reads one chunk of frames at a time, except for after seek. After seek
* the beginning of chunk before the seek point is not read effectivaly reading
* PCM frame from exactly the frame seek was performed to.
*
* Packet size depends on underlying container PCM chunk sizes.
*
* @author The JCodec project
*
*/
public class PCMMP4DemuxerTrack extends AbstractMP4DemuxerTrack {
private int defaultSampleSize;
private int posShift;
protected int totalFrames;
private SeekableByteChannel input;
private MovieBox movie;
public PCMMP4DemuxerTrack(MovieBox movie, TrakBox trak, SeekableByteChannel input) {
super(trak);
this.movie = movie;
this.input = input;
SampleSizesBox stsz = NodeBox.findFirstPath(trak, SampleSizesBox.class, Box.path("mdia.minf.stbl.stsz"));
defaultSampleSize = stsz.getDefaultSize();
int chunks = 0;
for (int i = 1; i < sampleToChunks.length; i++) {
int ch = (int) (sampleToChunks[i].getFirst() - sampleToChunks[i - 1].getFirst());
totalFrames += ch * sampleToChunks[i - 1].getCount();
chunks += ch;
}
totalFrames += sampleToChunks[sampleToChunks.length - 1].getCount() * (chunkOffsets.length - chunks);
}
public Packet nextFrame() throws IOException {
int frameSize = getFrameSize();
int chSize = sampleToChunks[stscInd].getCount() * frameSize - posShift;
return getNextFrame(ByteBuffer.allocate(chSize));
}
@Override
public synchronized MP4Packet getNextFrame(ByteBuffer buffer) throws IOException {
if (stcoInd >= chunkOffsets.length)
return null;
int frameSize = getFrameSize();
int se = sampleToChunks[stscInd].getEntry();
int chSize = sampleToChunks[stscInd].getCount() * frameSize;
long pktOff = chunkOffsets[stcoInd] + posShift;
int pktSize = chSize - posShift;
ByteBuffer result = readPacketData(input, buffer, pktOff, pktSize);
long ptsRem = pts;
int doneFrames = pktSize / frameSize;
shiftPts(doneFrames);
MP4Packet pkt = new MP4Packet(result, QTTimeUtil.mediaToEdited(box, ptsRem, movie.getTimescale()), timescale,
(int) (pts - ptsRem), curFrame, FrameType.KEY, null, 0, ptsRem, se - 1, pktOff, pktSize, true);
curFrame += doneFrames;
posShift = 0;
++stcoInd;
if (stscInd < sampleToChunks.length - 1 && (stcoInd + 1) == sampleToChunks[stscInd + 1].getFirst())
stscInd++;
return pkt;
}
@Override
public boolean gotoSyncFrame(long frameNo) {
return gotoFrame(frameNo);
}
public int getFrameSize() {
SampleEntry entry = sampleEntries[sampleToChunks[stscInd].getEntry() - 1];
if (entry instanceof AudioSampleEntry) {
return ((AudioSampleEntry) entry).calcFrameSize();
} else {
return defaultSampleSize;
}
}
protected void seekPointer(long frameNo) {
for (stcoInd = 0, stscInd = 0, curFrame = 0;;) {
long nextFrame = curFrame + sampleToChunks[stscInd].getCount();
if (nextFrame > frameNo)
break;
curFrame = nextFrame;
nextChunk();
}
posShift = (int) ((frameNo - curFrame) * getFrameSize());
curFrame = frameNo;
}
public long getFrameCount() {
return totalFrames;
}
@Override
public DemuxerTrackMeta getMeta() {
AudioSampleEntry ase = (AudioSampleEntry) getSampleEntries()[0];
AudioCodecMeta audioCodecMeta = new AudioCodecMeta(ase.getFormat());
return new DemuxerTrackMeta(TrackType.AUDIO, getCodec(), (double) duration / timescale, null, totalFrames,
null, null, audioCodecMeta);
}
}