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

org.libav.audio.SampleInputStream Maven / Gradle / Ivy

/*
 * Copyright (C) 2012 Ondrej Perutka
 *
 * This program is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either 
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this library. If not, see 
 * .
 */
package org.libav.audio;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Semaphore;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.libav.LibavException;

/**
 * Sample input stream. It is an adapter between an audio frame consumer and
 * audio input stream.
 * 
 * @author Ondrej Perutka
 */
public class SampleInputStream extends InputStream implements IAudioFrameConsumer {

    private final byte[] buffer;
    private int front;
    private int end;
    private int size;
    private boolean blockingFrameProcessing;
    private Semaphore emptySpace;
    
    private boolean eof;
    
    /**
     * Create a new sample input stream and set its buffer size. The buffer size
     * must be greather than the biggest frame size of all frames passed to
     * this object via the processFrame method.
     * 
     * @param bufferSize a buffer size
     */
    public SampleInputStream(int bufferSize) {
        this(bufferSize, true);
    }
    
    /**
     * Create a new sample input stream, set its buffer size and the blocking 
     * frame processing flag. The buffer size must be greather than the biggest 
     * frame size of all frames passed to this object via the processFrame 
     * method.
     * 
     * If the blocking frame processing is true, the processFrame method waits
     * until there is a space to store the whole frame inside the buffer. If the
     * blocking frame processing is false, the processFrame method may drop
     * data from the begining of the buffer to get enough space to store the
     * audio frame.
     * 
     * @param bufferSize a buffer size
     * @param blockingFrameProcessing a blocking frame processing flag
     */
    public SampleInputStream(int bufferSize, boolean blockingFrameProcessing) {
        this.buffer = new byte[bufferSize];
        this.front = 0;
        this.end = 0;
        this.size = 0;
        this.blockingFrameProcessing = blockingFrameProcessing;
        this.emptySpace = new Semaphore(bufferSize, true);
        
        this.eof = false;
    }

    /**
     * Get blocking frame processing flag.
     * 
     * If the blocking frame processing is true, the processFrame method waits
     * until there is a space to store the whole frame inside the buffer. If the
     * blocking frame processing is false, the processFrame method may drop
     * data from the begining of the buffer to get enough space to store the
     * audio frame.
     * 
     * @return blocking frame processing flag
     */
    public boolean isFrameProcessingBlocking() {
        return blockingFrameProcessing;
    }

    /**
     * Set blocking frame processing flag.
     * 
     * If the blocking frame processing is true, the processFrame method waits
     * until there is a space to store the whole frame inside the buffer. If the
     * blocking frame processing is false, the processFrame method may drop
     * data from the begining of the buffer to get enough space to store the
     * audio frame.
     * 
     * @param blockingFrameProcessing a blocking frame processing flag
     */
    public void setBlockingFrameProcessing(boolean blockingFrameProcessing) {
        this.blockingFrameProcessing = blockingFrameProcessing;
    }
    
    private int dropBufferData(int len) {
        synchronized (buffer) {
            if (len <= 0)
                return 0;
            len = len < size ? len : size;
            front = (front + len) % buffer.length;
            size -= len;
            if (blockingFrameProcessing)
                emptySpace.release(len);
            return len;
        }
    }
    
    /**
     * Drop all data stored inside the buffer.
     */
    public void flushBuffer() {
        synchronized (buffer) {
            dropBufferData(size);
        }
    }

    @Override
    public void processFrame(Object producer, AudioFrame frame) throws LibavException {
        int len = frame.getFrameSize();
        byte[] data = frame.getSamples();
        
        if (len > buffer.length)
            throw new LibavException("sample stream buffer is smaller than frame");
        if (blockingFrameProcessing) {
            try {
                emptySpace.acquire(len);
            } catch (InterruptedException ex) {
                Logger.getLogger(getClass().getName()).log(Level.WARNING, "interrupted while waiting for empty space");
                return;
            }
        }
        
        synchronized (buffer) {
            if (len > (buffer.length - size) && !blockingFrameProcessing)
                dropBufferData(len - buffer.length + size);
            
            for (int i = 0; i < len; i++)
                buffer[(end + i) % buffer.length] = data[i];
            end = (end + len) % buffer.length;
            size += len;
            
            buffer.notifyAll();
        }
    }
    
    @Override
    public int available() throws IOException {
        return size;
    }

    @Override
    public void close() throws IOException {
        synchronized (buffer) {
            eof = true;
            flushBuffer();
            buffer.notifyAll();
        }
    }

    @Override
    public synchronized void mark(int i) {
    }

    @Override
    public synchronized void reset() throws IOException {
    }

    @Override
    public boolean markSupported() {
        return false;
    }

    @Override
    public int read() throws IOException {
        synchronized (buffer) {
            while (true) {
                if (eof && size < 1)
                    return -1;
                else if (size < 1) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException ex) {
                        throw new IOException("interrupted while waiting for data", ex);
                    }
                } else {
                    byte result = buffer[front];
                    front = (front + 1) % buffer.length;
                    size--;
                    if (blockingFrameProcessing)
                        emptySpace.release();
                    return result;
                }
            }
        }
    }

    @Override
    public int read(byte[] bytes) throws IOException {
        return read(bytes, 0, bytes.length);
    }

    @Override
    public int read(byte[] bytes, int off, int len) throws IOException {
        if (bytes == null)
            throw new NullPointerException();
        if (off < 0 || len < 0 || (off + len) > bytes.length)
            throw new IndexOutOfBoundsException();
        if (len == 0)
            return 0;
        
        synchronized (buffer) {
            int rest = len;
            while (rest > 0) {
                if (eof && size < 1 && rest == len)
                    return -1;
                else if (eof && size < 1)
                    return len - rest;
                else if (size < 1) {
                    try {
                        buffer.wait();
                    } catch (InterruptedException ex) {
                        throw new IOException("interrupted while waiting for data", ex);
                    }
                } else {
                    int readLen = rest < size ? rest : size;
                    for (int i = 0; i < readLen; i++)
                        bytes[off + i] = buffer[(front + i) % buffer.length];
                    off += readLen;
                    rest -= readLen;
                    front = (front + readLen) % buffer.length;
                    size -= readLen;
                    if (blockingFrameProcessing)
                        emptySpace.release(readLen);
                }
            }
            
            return len;
        }
    }

    @Override
    public long skip(long l) throws IOException {
        return dropBufferData((int)l);
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy