2

I am trying to use a single thread to render three separate TextureView's off the UI thread using canvas lock/unlock. The problem observed is the consistent flickering of each of the Texture Views being drawn, with the flicker consisting of a DIFFERENT TextureView's frame. So for example, I draw frames A,B and C in that order. Frame C will appear fine, however frames A and B will flicker with occasional instances of Frame C, and frame A will occasionally flicker with instances of Frame B.

I have read through the documentation describing Android Core Graphics, and have a fairly good understanding of how android's frame buffering system works. In a nutshell, my suspicion is that I am performing lockCanvas/unlockCanvasAndPost call's too quickly in succession, and that the framebuffer is unable to complete my request and simply draws the previous (incorrect) frame instead.

I've tried several things to resolve this issue. Attempting to use Choreographer to wait for VSYNC before continuing to call canvas helped, but as expected lowered my frame rate however the problem was still there. I've also tried an alternative render loop sans sleep command, with no difference in the observed phenomenon.

Essentially I am out of ideas on how to tackle this problem, and was hoping to get some outside input on it. Thanks in advance, code snippet is below:

Main render loop:

 while (running) {

        long now = System.nanoTime();
        long updateLength = now - lastLoopTime;
        lastLoopTime = now;
        float delta = updateLength / ((float)OPTIMAL_TIME.get());

        lastFpsTime += updateLength;
        fps++;

        if (lastFpsTime >= 1000000000)
        {
            Log.e(TAG, "(FPS Target: " + TARGET_FPS+ " ,FPS Actual: " + fps + ")");
            lastFpsTime = 0;
            fps = 0;
        }


        for (MyTextureView mtv : mItemList){
            if (!mtv.isLocked()) {
                mtv.doUpdate(delta);
                mtv.doRender();
            }
        }

        long sleepTime = (lastLoopTime-System.nanoTime() + OPTIMAL_TIME.get())/1000000;
        sleepTime = sleepTime < 0 ? 0 : sleepTime;

        try{
            Thread.sleep( sleepTime );
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

MyTextureView doRender() method:

public void doRender() {
    doubleBufferCanvas.drawPaint(Colors.BLACK);
    doDraw(doubleBufferCanvas); //draws content onto secondary canvas

        Canvas c = lockCanvas();
        c.drawPaint(Colors.BLACK);
        c.save();
        c.drawBitmap(dbbmp, 0, 0, null);
        c.restore();
        unlockCanvasAndPost(c);
}
  • I have a sneaking suspicion that there's a bug in the driver or in TextureView that is preventing the correct texture from being selected. This may be the same problem as experienced here: http://stackoverflow.com/questions/31057293/android-textureview-drawing-painting-performance/ -- the OP managed to work around the issue with a bunch of calls to Invalidate(). I'd love to rule out app bugs first though... does the problem still happen if you run everything on a single thread? – fadden Jun 30 '15 at 00:02
  • Thanks for the advice. Unfortunately I was not able to resolve the issue utilizing that solution. I am a bit confused as to how calling invalidate() on the textureview (in the manner he did) would work, given that I am already utilizing a separate rendering thread and avoiding onDraw(Canvas c) altogether; to my knowledge onDraw() is called following invalidate? Also, I have been running the above script on a single thread, separate from the UI thread so the problem originated from a single thread solution. – Greg Shaulty Jun 30 '15 at 02:28
  • My best guess is that the View invalidate calls are having a side-effect that works around a bug. Like maybe the wrong EGL context is in use, but somehow exercising some of the View code causes it to be reset, or the TextureViews are inadvertently sharing a single texture. The other question also reported corruption issues when single-threaded (why do you have "multithreading" tagged?), which to me makes it less likely to be an app problem and more likely to be a framework issue. Unfortunately I'm not in a position to debug those. – fadden Jun 30 '15 at 04:06
  • FWIW, Grafika (https://github.com/google/grafika) plays a pair of videos side-by-side in two TextureViews in the "double decode" activity. The videos play at different frame rates and don't appear to interfere with each other. The code does some slightly strange things with the SurfaceTextures to demonstrate uninterrupted updates during screen rotation, but I wouldn't have expected that to matter. What version of Android are you running, and on what device? – fadden Jun 30 '15 at 04:10
  • Solved the problem. Fadden, you were correct in assuming that the view invalidate calls have a side-effect that works around some bug (I still don't know exactly what the bug is, so I am assuming it is framework-related). The solution to the buffer-flickering between multiple textureviews was to, in any case where one of the views was updated (lock/unlock cycle), to call postInvalidate() (I'm calling from outside UI thread) on the nearest common ancestor viewGroup, in my case a linearLayout that holds all textureViews. With this call, the problem disappeared with no loss in performance. – Greg Shaulty Jul 01 '15 at 01:14
  • I've filed http://b.android.com/178525 . – fadden Jul 01 '15 at 05:38
  • @GregShaulty I ended up calling Invalidate on all other TextureView objects, but you are saying simply calling Invalidate on the parent is sufficient? – jjxtra Jul 01 '15 at 16:12
  • @PsychoDad I believe so. In my case I only reDraw a view (lock/unlock) when necessary for performance reasons. However if one view or more is redrawn I issue a single call to invalidate on their common parent. I would give it a try and see if it works for you as well, it can't hurt only help. – Greg Shaulty Jul 01 '15 at 16:16
  • I am facing a similar issue..i have two textureviews rendering two different video streams(say A and B). Sometimes B video frames comes on A TextureView. But i am using android NDK to render the video frames into the surface (from the surfacetexture got in onsurfacetextureavailable). Does this solution work for native ndk based rendering as well ? – Shrish Aug 31 '15 at 11:26
  • I tried the common parent invalidation with video being rendered on both the textureViews. I dont see any improvement. Few observations: 1) it is observed only on some kitkat devices and 2) when the two views overlap each other, there is no issue. – Shrish Aug 31 '15 at 12:57
  • @Shrish I had the same problem as you. We can study this bug together! Have a look at my question:http://stackoverflow.com/questions/32285253/weird-frames-appear-when-using-textureview-to-play-video-in-listview Did you create any questions about this problem? – dragonfly Sep 08 '15 at 12:16
  • I have not created a new question. Does both my observations hold true for you too ? – Shrish Sep 09 '15 at 13:19

1 Answers1

0

I was able to find a workaround for this. (i dont observe this on android 5.0 and above).

Add a psuedo transparent view like below

            <View
                android:id="@+id/flicker_hack_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignParentBottom="true"
                android:layout_centerHorizontal="true"
                android:alpha="0.01"
                android:background="@color/black_12" />

which covers the entire activity wherever there is a textureView. the alpha should be set to 0.01 or a smaller value to make it seem invisible.

This workaround was based on my observation that when the two textureviews rendering videos overlap the flicker is not observed.

Shrish
  • 739
  • 5
  • 15
  • According to http://b.android.com/178525, the bug was fixed in 'L'. Details about the bug, including workarounds (extra invalidates, using a custom View), can be found in the bug tracker. – fadden Nov 10 '15 at 18:32