/*
** AACDecoder - Freeware Advanced Audio (AAC) Decoder for Android
** Copyright (C) 2011 Spolecne s.r.o., http://www.spoledge.com
**
** This file is a part of AACDecoder.
**
** AACDecoder 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 program 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 program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.spoledge.aacdecoder;
import java.io.InputStream;
import java.io.IOException;
import com.rubika.aotalk.util.Logging;
/**
* This is a separate thread for reading data from a stream.
* The buffer creates 3 buffer instances - one is being filled by the execution thread,
* one is prepared with data, one can be processed by a consumer.
* <pre>
* InputStream is = ...;
*
* // create a new reader with capacity of 4096 bytes per buffer:
* BufferReader reader = new BufferReader( 4096, is );
*
* // start the execution thread which reads the stream and fills the buffers:
* new Thread(reader).start();
*
* // get the data
* while (...) {
* BufferReader.Buffer buf = reader.next();
*
* if (!buf.getSize() == 0) break;
*
* // process data
* ...
* }
* </pre>
*/
public class BufferReader implements Runnable {
private static final String APP_TAG = "--> The Leet :: BufferReader";
public static class Buffer {
private byte[] data;
private int size;
Buffer( int capacity ) {
data = new byte[ capacity ];
}
public final byte[] getData() {
return data;
}
public final int getSize() {
return size;
}
}
int capacity;
private Buffer[] buffers;
/**
* The buffer to be write into.
*/
private int indexMine;
/**
* The index of the buffer last returned in the next() method.
*/
private int indexBlocked;
private boolean stopped;
private InputStream is;
////////////////////////////////////////////////////////////////////////////
// Constructors
////////////////////////////////////////////////////////////////////////////
/**
* Creates a new buffer.
*
* @param capacity the capacity of one buffer in bytes
* = total allocated memory
*
* @param is the input stream
*/
public BufferReader( int capacity, InputStream is ) {
this.capacity = capacity;
this.is = is;
Logging.log(APP_TAG, "init(): capacity=" + capacity);
buffers = new Buffer[3];
for (int i=0; i < buffers.length; i++) {
buffers[i] = new Buffer( capacity );
}
indexMine = 0;
indexBlocked = buffers.length-1;
}
////////////////////////////////////////////////////////////////////////////
// Public
////////////////////////////////////////////////////////////////////////////
/**
* Changes the capacity of the buffer.
*/
public synchronized void setCapacity( int capacity ) {
Logging.log(APP_TAG, "setCapacity(): " + capacity);
this.capacity = capacity;
}
/**
* The main loop.
*/
public void run() {
Logging.log(APP_TAG, "run() started....");
int cap = capacity;
int total = 0;
while (!stopped) {
Buffer buffer = buffers[indexMine];
total = 0;
if (cap != buffer.data.length) {
Logging.log(APP_TAG, "run() capacity changed: " + buffer.data.length + " -> " + cap);
buffers[indexMine] = buffer = null;
buffers[indexMine] = buffer = new Buffer( cap );
}
while (!stopped && total < cap) {
try {
int n = is.read( buffer.data, total, cap - total );
if (n == -1) stopped = true;
else total += n;
}
catch (IOException e) {
Logging.log(APP_TAG, "Exception when reading: " + e);
stopped = true;
}
}
buffer.size = total;
synchronized (this) {
notify();
int indexNew = (indexMine + 1) % buffers.length;
while (!stopped && indexNew == indexBlocked) {
//Logging.log(APP_TAG, "run() waiting....");
try {
wait();
} catch (InterruptedException e) {
Logging.log(APP_TAG, e.getMessage());
}
//Logging.log(APP_TAG, "run() awaken");
}
indexMine = indexNew;
cap = capacity;
}
}
Logging.log(APP_TAG, "run() stopped.");
}
/**
* Stops the thread - the object cannot be longer used.
*/
public synchronized void stop() {
stopped = true;
notify();
}
/**
* Returns true if this thread was stopped.
*/
public boolean isStopped() {
return stopped;
}
/**
* Returns next available buffer instance.
* The returned instance can be freely used by another thread.
* Blocks the caller until a buffer is ready.
*/
public synchronized Buffer next() {
int indexNew = (indexBlocked + 1) % buffers.length;
while (!stopped && indexNew == indexMine) {
//Logging.log(APP_TAG, "next() waiting....");
try {
wait();
} catch (InterruptedException e) {
Logging.log(APP_TAG, e.getMessage());
}
//Logging.log(APP_TAG, "next() awaken");
}
if (indexNew == indexMine) return null;
indexBlocked = indexNew;
notify();
return buffers[indexBlocked];
}
}