All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jcodec.containers.mp4.demuxer.PCMMP4DemuxerTrack Maven / Gradle / Ivy

There is a newer version: 0.2.5
Show newest version
package org.jcodec.containers.mp4.demuxer;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.jcodec.common.AudioCodecMeta;
import org.jcodec.common.Codec;
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);
    }

    @Override
    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 && defaultSampleSize == 0) {
            return ((AudioSampleEntry) entry).calcFrameSize();
        } else {
            return defaultSampleSize;
        }
    }

    @Override
    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;
    }

    @Override
    public long getFrameCount() {
        return totalFrames;
    }

    @Override
    public DemuxerTrackMeta getMeta() {
        AudioSampleEntry ase = (AudioSampleEntry) getSampleEntries()[0];
        AudioCodecMeta audioCodecMeta = org.jcodec.common.AudioCodecMeta.fromAudioFormat(ase.getFormat());
        return new DemuxerTrackMeta(TrackType.AUDIO, Codec.codecByFourcc(getFourcc()), (double) duration / timescale, null, totalFrames,
                null, null, audioCodecMeta);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy