Java使缓冲区读取同步线程

时间:2016-03-30 12:47:05

标签: java android multithreading concurrency buffer

我正在GLSurfaceView上通过Android中的手机摄像头渲染预览,并希望在可用时立即保存帧。由于将帧保存到SD卡所花费的时间比两次onFrameAvailable之间的时间要多得多,所以在我从屏幕上读取像素后,我在另一个线程中执行此操作。

public void onDrawFrame(GL10 gl) {
//Process the frame, display it on the screen and other stuff

//Read pixels from the screen into my buffer
       glReadPixels(0, 0, offscreenSize_.x, offscreenSize_.y, GL_RGBA, GL_UNSIGNED_BYTE,
                intBuffer.clear());
     // At the moment this has a counter and happens every 150ms
     if(saveImage)
     {
     Thread thread = new Thread() {
            @Override
            public void run() {
                // Convert to an array for Bitmap.createBitmap().
                final int[] pixels = new int[intBuffer.capacity()];
                intBuffer.rewind();
                intBuffer.get(pixels);

                try {
                    // Create/access a pictures subdirectory.
                    File directory = new File(
                            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                            "TangoFrames");
                    if (!directory.mkdirs() && !directory.isDirectory()) {
                        Log.d("WRITE ERR", "Couldn't create directory");
                        return;
                    }

                    // Get the current capture index to construct a unique filename.
                    SharedPreferences prefs = activity_.getPreferences(Context.MODE_PRIVATE);
                    int index = prefs.getInt("index", 0);
                    SharedPreferences.Editor prefsEditor = prefs.edit();
                    prefsEditor.putInt("index", index + 1);
                    prefsEditor.apply();

                    // Create the capture file.
                    File file = new File(directory, String.format("tango%05d.png", index));
                    FileOutputStream fileOutputStream = new FileOutputStream(file);

                    // Bitmap conveniently provides file output.
                    Bitmap bitmap = Bitmap.createBitmap(pixels, offscreenSize_.x, offscreenSize_.y, Bitmap.Config.ARGB_8888);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 90, fileOutputStream);
                    fileOutputStream.close();
                    numberOfFrames++;
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
    }
}

我的问题是,当线程开始落后时,有时会发生读取开始的帧:

final int[] pixels = new int[intBuffer.capacity()];
intBuffer.rewind();
intBuffer.get(pixels);

触发下一个回调并运行intBuffer.clear(),因此其余回调被破坏。 如何以这样的方式同步线程:只有在缓冲区已满时才运行,并注意在线程运行时缓冲区没有被清除?

2 个答案:

答案 0 :(得分:0)

听起来像是您遇到了经典的共享数据并发问题。

解决此类问题的最简单方法是停止共享数据。您可以向工作线程发送int的支持IntBuffer数组的副本:

...
glReadPixels(0, 0, offscreenSize_.x, offscreenSize_.y, GL_RGBA, GL_UNSIGNED_BYTE,
      intBuffer.clear());
final int[] bufferContents = intBuffer.array();
final int[] pixels = Arrays.copyOf(bufferContents , bufferContents .length);
// reset buffer here
if (saveImage) {
   ...
   // use pixels here

IntBuffer的读写操作的简单同步将"顺序化"程序的关键部分可能会导致抓取框架的保真度降低(如果您的工作线程仍在读取框架更改事件侦听器,则您无法立即调用glReadPixels IntBuffer内容)。它也会更容易出错,所以我不推荐它。

答案 1 :(得分:-1)

我最终只使用了Queue IntBufferQueue个对象,并在退出应用之前等待onPause为空。在调用#include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #include <GL/glut.h> void init(void) { glClearColor(0.8, 0.8, 0.8, 0.0); /* window color white */ glEnable(GL_DEPTH_TEST); } void drawScene(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(3.0,3.0,3.0, 0.0,0.0,0.0, 0.0,1.0,0.0); //used some random parameters... gluLookAt(0.0, 0.0, 0.0, 5.0, 5.0, 5.0, 0, 1, 0); //axis //x glColor3f(1.0,0.0,0.0); glLineWidth(1.0); glBegin(GL_LINES); glVertex3d(0.0,0.0,0.0); glVertex3d(1.0,0.0,0.0); glEnd(); //y glColor3f(0.0,1.0,0.0); glLineWidth(1.0); glBegin(GL_LINES); glVertex3d(0.0,0.0,0.0); glVertex3d(1.0,0.0,0.0); glEnd(); //z glColor3f(0.0,0.0,1.0); glLineWidth(1.0); glBegin(GL_LINES); glVertex3d(0.0,0.0,0.0); glVertex3d(1.0,0.0,0.0); glEnd(); glFlush(); } void herschaal(){ glViewport(0,0,500,500); glMatrixMode(GL_PROJECTION); glOrtho(-10, 10, 10, -10, 10, -10); glLoadIdentity(); } int main( int argc, char * argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH ); glutInitWindowPosition(50, 100); glutInitWindowSize(500, 500); glutCreateWindow("mijn test"); glutReshapeFunc(herschaal); init(); glutDisplayFunc(drawScene); glutMainLoop(); return 0; } 时基本上运行该函数。

因此,对于读取此内容的人来说,可能只是将数据添加到队列中并等待它在退出之前进行处理。