SurfaceView hangs after rendering first few frames

Viewed 15

I am currently trying to output processed opencv mat images from openCV to SurfaceView at fps of 30 or more.

I am using asynctasks to process the mat images into bitmaps, with the results added to bitmap buffer in the MSurface class.

Then another repetitive thread will call outputDisplay of the same Msurface class which will pop the first bitmap from the bitmap buffer and output it SurfaceView through lockCanvas and unlockCanvasAndPost.

But only first or first few frames will be displayed before it freezes forever.

Code snippets below

In main activity

this.backGroundPool = new ScheduledThreadPoolExecutor(2);
        this.backGroundPool.setMaximumPoolSize(2);
        this.backgroundThread = new Runnable() {
            @Override
            public void run() {
                try {
                    if (cameraTasks != null && mSurface != null) {
                        //Log.w("Output Task", "Processing");
                        if (outputTasks != null) {
                            if (outputTasks.size() <= cameraTasks.size()) {
                                //Log.w("Output Task", "Adding");
                                outputTasks.addLast(new OutputTask(mSurface));
                                outputTasks.getLast().execute(imageBuffer.getFinalImg());
                            }
                            if (outputTasks.getFirst().getStatus() == AsyncTask.Status.FINISHED) {
                                outputTasks.removeFirst();
                            }
                        }
                    }
                    if (cameraTasks == null) {
                        Log.w("CameraTask", "Not initialized");
                    }
                    if (mSurface == null) {
                        Log.w("mSurface View", "Not initialized");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        this.backGroundPool.scheduleAtFixedRate(this.backgroundThread,
                START_DELAY,
                TIME_PERIOD,
                TIME_UNIT);
        this.outputThread = new Runnable() {
            @Override
            public void run() {
                try {
                    while (true) {
                        mSurface.outputDisplay();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        this.backGroundPool.scheduleAtFixedRate(this.outputThread,
                START_DELAY,
                TIME_PERIOD,
                TIME_UNIT);

In OutputTask class

public class OutputTask extends AsyncTask<Mat, Void, Bitmap> {
private MSurface mSurface;

public OutputTask(MSurface mSurface) {
    this.mSurface = mSurface;
}

@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected Bitmap doInBackground(Mat... images) {
    //Log.i("Camera Task", "Running");
    if (images.length != 0 && images[0] != null) {
        Bitmap bm = Bitmap.createBitmap(images[0].cols(), images[0].rows(), Bitmap.Config.ARGB_8888);;
        Utils.matToBitmap(images[0], bm);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.PNG, 100, out);
        Bitmap compressedBM = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
        return compressedBM;
    }
    else {
        Log.w("Mat Image", "Not Found");
        return null;
    }
}

@Override
protected void onPostExecute(Bitmap result) {
    try {
        if (result != null) {
            synchronized (this.mSurface.getHolder()) {
                //Log.i("Buffer", "Updating");
                this.mSurface.addBitmapBuffer(result);
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    super.onPostExecute(result);
}

In MSurface class

public class MSurface extends SurfaceView implements SurfaceHolder.Callback {
private static int MAXBUFFERSIZE = 30;
public LinkedList<Bitmap> bitmapBuffer;
private boolean surfaceAvailable;
private int surfaceWidth;
private int surfaceHeight;

public MSurface(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.getHolder().addCallback(this);
    this.getHolder().setFormat(PixelFormat.RGBA_8888);
    this.bitmapBuffer = new LinkedList<Bitmap>();
    this.surfaceAvailable = false;

}

public void addBitmapBuffer(Bitmap bitmap) {
    this.bitmapBuffer.addLast(bitmap);
    if (this.bitmapBuffer.size() > MAXBUFFERSIZE) {
        Bitmap bm = this.bitmapBuffer.pop();
        bm.recycle();
    }
}

public void outputDisplay() {
    if (this.bitmapBuffer.size() > 0 && this.surfaceAvailable) {
        Canvas canvas = getHolder().lockCanvas();
        //Log.i("Canvas", "Drawing new frame");
        //canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.MULTIPLY);
        Bitmap bm = this.bitmapBuffer.pop();
        Rect src = new Rect(0, 0, bm.getWidth(), bm.getHeight());
        Rect dst = new Rect((int)((this.surfaceWidth - bm.getWidth()) / 2),
                ((int)(this.surfaceHeight - bm.getHeight()) / 2),
                ((int)(this.surfaceWidth - bm.getWidth()) / 2 + bm.getWidth()),
                ((int)(this.surfaceHeight - bm.getHeight()) / 2 + bm.getHeight()));
        canvas.drawColor( 0, PorterDuff.Mode.CLEAR );
        canvas.drawBitmap(bm, src, dst, null);
        this.getHolder().unlockCanvasAndPost(canvas);
        bm.recycle();
    }
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    // TODO Auto-generated method stub
    Log.i("Surface", "Changed");
    Log.d("SurfaceHolder", String.valueOf(holder));
    Log.i("Surface Format", String.valueOf(format));
    Log.i("Surface Width", String.valueOf(width));
    Log.i("Surface Height", String.valueOf(height));
    this.surfaceWidth = width;
    this.surfaceHeight = height;
    if (holder.getSurface() == null) {
        Log.i("Surface", "Missing");
    }
    setWillNotDraw(false);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.i("Surface", "Created");
    Log.d("SurfaceHolder", String.valueOf(holder));
    this.surfaceAvailable = true;
    //this.setWillNotDraw(false);
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    // TODO Auto-generated method stub
    this.surfaceAvailable = false;
    Log.i("Surface", "Destroyed");
}

}

Thanks in advance for those who are able to provide suggestions/solutions

0 Answers
Related