Я реализовал буфер цикла (или круговой буфер), содержащий в общей сложности 250 кадров необработанных видеоданных (разрешение кадра 1280x720). В качестве буфера я использую класс ByteBuffer
. Буфер запускается в отдельном потоке с использованием Looper, каждый новый кадр передается через сообщение в поток Handler
. Когда предел достигнут, позиция будет установлена в 0, а весь буфер будет перезаписан с самого начала. Таким образом, буфер всегда содержит последние 250 видеокадров.Хранить части огромного ByteBuffer в файл
Поскольку количество требуемого пространства кучи огромно (около 320 мегабайт), я использую тег android:largeHeap="true"
в манифесте.
Теперь мы приходим к проблеме. Петля работает хорошо, она потребляет немного меньше, чем допустимый размер пространства кучи (что приемлемо для меня). Но в некоторый момент времени я хочу сохранить весь буфер в необработанный двоичный файл, соблюдая текущую позицию буфера.
Позвольте мне объяснить, что с небольшим графиком:
Буфер цикла выглядит следующим образом:
| ========== ========== ГОЛОВЫ | =============== ХВОСТ ============ |
0 ------------------------- buffer.position()
------------------- ---- buffer.limit()
Во время сохранения я хочу сначала сохранить хвост файла (потому что он содержит начало видео), а затем голова до текущего buffer.position()
. Я не могу выделить больше байт-массивов для извлечения данных из ByteBuffer
(пустое место заполнено), поэтому я должен непосредственно написать файл ByteBuffer
в файл.
В настоящее время ByteBuffer
делает возможным только запись в файл полностью (write()
метод.) Кто-нибудь знает, что может быть решением? Или есть даже лучшее решение для моей задачи?
Я дам свой код ниже:
public class FrameRecorderThread extends Thread {
public int MAX_NUMBER_FRAMES_QUEUE = 25 * 10; // 25 fps * 10 seconds
public Handler frameHandler;
private ByteBuffer byteBuffer;
byte[] image = new byte[1382400]; // bytes for one image
@Override
public void run() {
Looper.prepare();
byteBuffer = ByteBuffer.allocateDirect(MAX_NUMBER_FRAMES_QUEUE * 1382400); // A lot of memory is allocated
frameHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Store message content (byte[]) to queue
if(msg.what == 0) { // STORE FRAME TO BUFFER
if(byteBuffer.position() < MAX_NUMBER_IMAGES_QUEUE * 1382400) {
byteBuffer.put((byte[])msg.obj);
}
else {
byteBuffer.position(0); // Start overwriting from the beginning
}
}
else if(msg.what == 1) { // SAVE IMAGES
String fileName = "VIDEO_BUF_1.raw";
File directory = new File(Environment.getExternalStorageDirectory()
+ "/FrameRecorder/");
directory.mkdirs();
try {
FileOutputStream outStream = new FileOutputStream(Environment.getExternalStorageDirectory()
+ "/FrameRecorder/" + fileName);
// This is the current position of the split between head and tail
int position = byteBuffer.position();
try {
// This stores the whole buffer in a file but does
// not respect the order (tail before head)
outStream.getChannel().write(byteBuffer);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
Log.e("FMT", "File not found. (" + e.getLocalizedMessage() + ")");
}
}
else if(msg.what == 2) { // STOP LOOPER
Looper looper = Looper.myLooper();
if(looper != null) {
looper.quit();
byteBuffer = null;
System.gc();
}
}
}
};
Looper.loop();
}}
Большое спасибо заранее!
Не могли бы вы привести мне пример, пожалуйста? Создание подсекции - вот в чем проблема, поэтому я не знаю, как это сделать. – Palund
Просто создайте новый со смещением и длиной, используя старый, как src. – Jay
Я не могу этого сделать, потому что для этого мне нужно выделить память. К сожалению, пустое место заполнено. Но я мог бы попытаться сделать это по-байт. Или есть лучшее решение? – Palund