将巨大的ByteBuffer的部分存储到文件中

时间:2014-09-15 09:01:32

标签: android memory-management io

我已经实现了一个循环缓冲区(或循环缓冲区),总共存储250帧原始视频数据(帧分辨率1280x720)。作为缓冲区,我正在使用ByteBuffer类。缓冲区使用Looper在单独的线程中运行,每个新帧都通过消息传递给线程Handler对象。达到限制时,位置设置为0,整个缓冲区从头开始被覆盖。像那样,缓冲区总是包含最后250个视频帧。

由于所需的堆空间量很大(大约320 MB),我在清单中使用了标记android:largeHeap="true"

现在我们来解决这个问题。循环运行良好,它的消耗量略小于允许的堆空间大小(这对我来说是可接受的)。但是在某些时候,我想将整个缓冲区存储到原始二进制文件中,同时尊重缓冲区的当前位置。

让我用一个小图解释:

循环缓冲区如下所示:

| ========== HEAD ========== | =============== TAIL ======== ==== |

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();
}}

非常感谢你!

2 个答案:

答案 0 :(得分:0)

只需创建一个子部分并将其写入文件。

或者调用set Length然后将其写入然后重新设置。

答案 1 :(得分:0)

好的,与此同时我进一步调查并找到了解决方案。

我使用简单的ByteBuffer数组而不是byte[]个对象。在开始时,我正在分配帧所需的所有堆空间。在存储它时,我可以使用当前位置写入缓冲区的头部和尾部。这工作并且比预期容易。 :)