Flickering while using surface view to draw frame by frame bitmap - android

I am using surface view to render frames after extracted from video, the problem is that there is a flickering effect while rendering, the origin video was smooth, I play the frames in ImageViewer and hold on press NEXT key to switch next and next it was smooth too, only flicker after render them in SurfaceView.
the problem is I have a period between frames drawing, because I want to control the playing frame rate, make it slower or faster via user's choice, once I give up the delay drawing the problem gone, but that's no my intention, I need to make them delay.
I understand that this is due to double/triple buffering problem, even though I went through many posts, including turn to use GLSurfaceView to render, also drawBitmap twice intent to keep front-buffer and back-buffer align, it doesn't help to fix this problem.
I found this Flickering while using surface view post, and try all the solution-like mention inside, but it's not work, the accepted answer mention about dirty rect, remind me to update every pixels if call lockCanvas() without rect specified, but I think I already draw the whole bitmap in the next, imply I updated every pixels, so I get not idea of this.
below are the code and the problem's gif, please take a look at my code and help me get this fixed.
class CustomView(
context: Context, attrs: AttributeSet?
) : SurfaceView(context, attrs), Runnable {
private var animationThread: Thread? = null
#Volatile private var running = false
private var frameList: List<BitmapFrame1>? = null
private var index = 0
fun start(frameList: List<BitmapFrame1>) {
if (running) return
running = true
index = 0
this.frameList = frameList
animationThread = Thread(this).apply {
start()
}
}
override fun run() {
val surHolder = holder
var nextFrameTimeMs = 0L
while (running) {
if (!surHolder.surface.isValid) continue
if (SystemClock.uptimeMillis() >= nextFrameTimeMs) {
val currentFrame = frameList!!.getOrNull(++index)
if (currentFrame == null) {
running = false
} else {
val canvas = surHolder.lockCanvas()
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, Paint())
val drawTimestamp = SystemClock.uptimeMillis()
surHolder.unlockCanvasAndPost(canvas)
nextFrameTimeMs = drawTimestamp + currentFrame.delayMs
}
} else {
// have tried to draw the current frame again before delay time's up,
// but not effect
val currentFrame = frameList!![index]
val canvas = surHolder.lockCanvas()
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, Paint())
surHolder.unlockCanvasAndPost(canvas)
}
}
}
}

You have to use Choreographer and not lock Canvas in "random" moment (this wrong moment could occurs when Surface is swapping from one buffer to another and the Bitmap/Texture is uploaded on wrong buffer) but only after a Choreographer callback event.
When this event occurs it's guaranteed that the next frame is uploaded on the right buffer and rendered well in next Drawing call.
Choreographer.getInstance().postFrameCallback(new Callback(){
#Override
public doFrame(final long frameTimeNanos) {
surHolder.lockCanvas();
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, paint);
surHolder.unlockCanvasAndPost(canvas);
}
});
So if you want to slow down rendering you need to "queue" requests using an Handler and send a delayed Message to trigger the drawing procedure (below it's pseudo code):
private Handler mHandler;
private final Choreographer.Callback mCallback = new Callback() {
#Override
public doFrame(final long frameTimeNanos) {
surHolder.lockCanvas();
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, paint);
surHolder.unlockCanvasAndPost(canvas);
}
}
public void prepareHandler() {
mHandler = new Handler(Looper.getMainLooper(), new method() {
#Override
private void handleMessage(#NonNull final Message message) {
switch (message.what) {
case 1234: {
Choreographer.getInstance().postFrameCallback(mCallback);
break;
}
}
}
});
}
private void postponeDraw(final long millis) {
mHandler.sendEmptyMessageDelayed(1234, 500/*milliseconds*/);
}

You may need to implement Choreographer.FrameCallback to synchronize the device's frame rate apart from playing frame rate.
Some good samples are Google's Grafika. Here is a comment from RecordFBOActivity.java:
We use Choreographer so our animation matches vsync, and a separate
rendering thread to keep the heavy lifting off of the UI thread.
Ideally we'd let the render thread receive the Choreographer events
directly, but that appears to be creating a permanent JNI global
reference to the render thread object, preventing it from being
garbage collected (which, in turn, causes the Activity to be
retained). So instead we receive the vsync on the UI thread and
forward it.
They made the RenderThread as a Looper thread. Also prepared a Handler for the thread. Implementing Choreographer.FrameCallback to the SurfaceView, they forward doFrame callback to RenderThread by posting a Handler message.

Related

Low frame rate when drawing full screen drawable on canvas

The app I'm developing is a Flappy Bird clone.
I'm using a surfaceView object in which I have a gameThread and inside of its run method I draw the various components of the game on the canvas.
Everything runs smoothly as long as I just draw Rects to represent the objects, but as soon as I added the first Drawables i noticed a little bit of a loss in smoothness. If I try to draw the background as a Drawable the game suffers very significant frame rate loss.
What I tried:
Using png and all different kinds of bitmap as assets
Resizing the asset to fit the canvas perfectly, thus avoiding a rescale
None of this had any tangible effect.
Basically:
If I only use drawRect: 60fps
If I draw the back with drawRect and the other components with drawable.draw(canvas): 57fps
If I draw everything (background included) with drawable.draw(canvas): 15fps
Somewhat relevant code:
public class CannonView extends SurfaceView
implements SurfaceHolder.Callback {
private CannonThread cannonThread; // controls the game loop
private Drawable background;
// constructor
public CannonView(Context context, AttributeSet attrs) {
super(context, attrs); // call superclass constructor
getHolder().addCallback(this);
background= ResourcesCompat.getDrawable(getResources(), R.drawable.background, null);
}
public void newGame() {
background.setBounds(0,0, getScreenWidth(),getScreenHeight());
}
public void drawGameElements(Canvas canvas) {
background.draw(canvas);
}
public void stopGame() {
if (cannonThread != null)
cannonThread.setRunning(false); // tell thread to terminate
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!dialogIsDisplayed) {
newGame(); // set up and start a new game
cannonThread = new CannonThread(holder); // create thread
cannonThread.setRunning(true); // start game running
cannonThread.start(); // start the game loop thread
}
}
private class CannonThread extends Thread {
private SurfaceHolder surfaceHolder; // for manipulating canvas
private boolean threadIsRunning = true; // running by default
// initializes the surface holder
public CannonThread(SurfaceHolder holder) {
surfaceHolder = holder;
setName("CannonThread");
}
// changes running state
public void setRunning(boolean running) {
threadIsRunning = running;
}
// controls the game loop
#Override
public void run() {
Canvas canvas = null; // used for drawing
while (threadIsRunning) {
try {
// get Canvas for exclusive drawing from this thread
canvas = surfaceHolder.lockCanvas(null);
synchronized(surfaceHolder) {
drawGameElements(canvas);
}
}
finally {
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
It seems apparent that the dominant cause of the low frame rate is background.draw(). Switching to a Bitmap improves this somewhat, probably since it cached the output of draw(), and because it can be used with Canvas functions that are guaranteed not to need scaling (e.g., drawBitmap( Bitmap, float, float, Paint))
You also found that switching to RGB_565 as an intermediate format improves performance quite a bit, presumably because it throws away the alpha. (Otherwise, I would've expected this to be somewhat slower, b/c the format has to be converted back to RGBA_8888 as it's blitted into the SurfaceView.)
It's also apparent that Android won't let you go over 60fps. This is almost certainly because lockCanvas() takes part in a triple buffering scheme that throttles the drawing rate, to prevent you from submitting frames that could never be displayed (due to your device's fixed screen refresh rate of 60Hz).
This leaves the question of why you don't get a full 60fps, but something close to it. If drawGameElements() takes the same amount of time to run each time, and it's less than 16ms, then lockCanvas() should be throttling you, and no frames should ever get dropped (60fps continuously). It seems likely that there is a burble in the thread scheduler or something, and every so often, the CannonThread does not execute quickly enough to provide the frame before the triple-buffering scheme needs to page-flip. In this event, the frame must be delayed until the next screen refresh. You might try increasing CannonThread's thread priority, removing any extra processing in drawGameElements() that doesn't absolutely need to happen on CannonThread, or closing other apps running on your device.
As mentioned, OpenGL is the standard way of getting max sprite performance for games like these, because it is able to offload many operations to hardware. You may be approaching the performance limit of a drawBitmap()-based game.

Android - Update Bitmap from timer thread

I got an Android project composed by a single Layout with an ImageView.
public class MainActivity extends AppCompatActivity {
/* original and stretched sized bitmaps */
private Bitmap bitmapOriginal;
private Bitmap bitmapStretched;
/* the only view */
private ImageView iv;
....
}
This ImageView is updated by this runnable function
runnable = new Runnable() {
#Override
public void run() {
iv.setImageBitmap(bitmapStretched);
}
};
and the runnable is ran by a temporized JNI function, running on a background thread, that call it 60 times per second.
public void jniTemporizedCallback(int buf[]) {
/* set data to original sized bitmap */
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
/* calculate the stretched one */
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);
/* tell the main thread to update the image view */
runOnUiThread(runnable);
}
After some frame is drawn, the app crashes with the following message.
A/OpenGLRenderer: Task is already in the queue!
I guess this is because the renderer didn't finish to fully render the previous frame of the ImageView and gets angry.
If i remove runOnUiThread(runnable); the problem disappear (obviously)
How can avoid this? How can i syncronize my application with the openGL renderer?
I also tried to extend ImageView and draw the bitmap on canvas into the onDraw function but i got the same result
I guess you're trying create bitmapOriginal ouside the thread. Therefore, when compiler is trying to call again after 60 seconds, it's getting same objects and couldn't identify the task. I would suggest better as below.
public void jniTemporizedCallback(int buf[]) {
// Initialize
bitmapOriginal = Bitmap.createBitmap(///)
/* set data to original sized bitmap */
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
/* calculate the stretched one */
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);
/* tell the main thread to update the image view */
runOnUiThread(runnable);
}
The proper way to synchronize your drawing logic with the device's frame rate is to use a SurfaceView instead of an ImageView. Instead of pushing frames to the View with your own timer, you should create a rendering Thread that tries to render frames as fast as possible. When you call surfaceHolder.lockCanvas(), the Android system will automatically block until it is time to render the frame. When you unlock the canvas using unlockCanvasAndPost(), the system will draw the buffer to the screen.
See https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview for more info. Hope this helps!
Problem was totally unrelated to the Bitmap itself....
It was the real time clock signal that messed with Android RenderThread.
Further explanation here:
Android and JNI real time clock
Provide here purposes of use such method for rendering? What you want to do?, there are great animation functionality in android engine, may be this task can be done with this animation.
One more if you will use codes like yours battery of phone will run to zero very fast coz this will load cpu/gpu to max.
in anyway - try to place blocks from running task, set bool taskRun = true on start and check if (!taskRun){ taskRun = true; //here start your task..} and on ui thread after updating ui you can switch to taskRun = false; Using this you can skip some frames, but should not crash.
The problem is that the Handler of the main thread is keeping a reference to your Runnable. When you want to run your Runnable for the second time, the old Runnable is already in the Message Queue, hence Task is already in the queue message. If you create a Runnable every time u want to execute the Runnable like in the code below, I think the problem will be solved.
public void jniTemporizedCallback(int buf[]) {
/* set data to original sized bitmap */
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
/* calculate the stretched one */
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);
/* tell the main thread to update the image view */
runOnUiThread(new Runnable() {
#Override
public void run() {
iv.setImageBitmap(bitmapStretched);
}
});
}
I think you are right with reason, because you cannot be sure, that Android render images in 60 FPS. And yeah, I think you need just synchronize Bitmap Native Callback with Android Render. So, lets start.
I prefer using Lock from concurrency stack Java. Because you see, when you lock object, and when you unlock. In case of using volatile (for example, sure there also reference restrictions) on Bitmap object, you need to check locking this object in very places, where you using Bitmap.
Also I think you should use Lock from THIS EXAMPLE (to unlock Lock object from any other Thread). So, here is example. Example below will work properly. Just don't forget about Context deleting and stopping task:
public class MainActivity extends AppCompatActivity {
/* Initialize lock (avoid lazy init, with your methods) */
private ReentrantLock lock = new ReentrantLock();
............
private runnableDrawImage = new Runnable() {
#Override
public void run() {
iv.setImageBitmap(bitmapStretched);
lock.unlock();
}
};
..........
public void jniTemporizedCallback(int buf[]) {
/* synchronize by locking state*/
lock.lock();
bitmapOriginal = Bitmap.createBitmap(///)
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);
MainActivity.this.runOnUiThread(runnableDrawImage);
}
}

AndEngine updating screen

I have a live wallpaper which I created using the android canvas. Upon testing, I felt it necessary to harness the power of OpenGL, and so am experimenting with AndEngine. I am wondering how I can achieve the following.
I have a background image that fills the whole screen, with many smaller bitmaps floating over the top (not animated movements)
So far I have this for the background image:
#Override
public void onLoadResources()
{
mtexture = new Texture(1024, 1024, TextureOptions.BILINEAR);
TextureRegionFactory.setAssetBasePath("gfx/");
mtextureRegion = TextureRegionFactory.createFromResource(mtexture , this, R.drawable.background1, 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mtexture );
}
#Override
public Scene onLoadScene(){
final Scene scene = new Scene(1);
Sprite background = new Sprite(0, 0, CAMERA_WIDTH*2, CAMERA_HEIGHT, mtextureRegion )
SpriteBackground sb = new SpriteBackground(background);
scene.setBackground(sb);
scene.setBackgroundEnabled(true);
return scene;
}
This works fine for the background, but I require moving sprites.
In my canvas code, I do the following to update the position & physics of the moving objects and draw the canvas every few ms
private final Runnable drawScreen = new Runnable() {
public void run() {
drawFrame();
}};
-
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
//draw
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
mHandler.removeCallbacks(drawScreen);
mHandler.postDelayed(drawScreen, 10);
}
What is the appropriate way to do this on AndEngine? do I use the same code and substitute openGL calls?
I had a look at GLEngine, am I supposed to send Runnables to the GlThread queue?
EDIT - I think I found the answer...an UpdateHandler. But how can I inform the handler of an update (i.e. to call the onUpdate method). If I make a timed Handler, what happens if I call too often, does a queue of requests build up?
First of all, don't use the constructor Scene(int), it's deprecated. Use Scene() instead.
Correct, you should use an update handler.
You can create an UpdateHandler, and then register it to your scene:
scene.registerUpdateHandler(mUpdateHandler);
This way, the code in mUpdateHandler.onUpdate method is executed each time the scene updates (Each frame.). You don't call it manually. If you want to stop it, call:
scene.unregisterUpdateHandler(mUpdateHandler);
So, the onUpdate method is always executed in the UpdateThread, so you can be sure you can do any change to entities you want there. So you can move around and sprite you want, etc...
By the way, why is the background's width CAMERA_WIDTH*2? It means that only the left half of your sprite is shown. If you don't plan moving the camera, then the right half won't ever show.

How can I draw an animated view in android?

I created a custom view from scratch. Extended View and overrided onDraw().
When comes down in animating the view i generate a custom animation using offsets.
eg.
while(!isOnTop){
mOffset++;
//draw the component a a it higher using the offset
if(position == 0)
isOnTop==true;
invalidate();
}
The thinking is that my frames come from invalidate it self. The problem is that invalidation of this view can come just by scrolling a listview at the same screen.
This "shared invalidation()" causes lag to my animation.So is there a way out of that lag?
Do you have any other suggestion of performing animations in that shared enviroment?
Creating an animation using a seperate thread that calculates the offset also needs forced invalidation() calls to display the animation (correct me if i'm wrong).
Is the only solution to perform the animation in eg 10 invalidation requests with a larger step? It will ease the lag out but i think i can use a different approach on that.
"What is best" of course depends greatly on exactly what you are trying to do. You haven't said what you are trying to accomplish, so we can only guess at what may be best for you.
Here are some simple things:
If you want to animate bitmap frames, use AnimationDrawable: http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
If you want to animate the movement of views within your hierarchy, use the view animation framework: http://developer.android.com/guide/topics/graphics/view-animation.html
The new more general animation framework can do a lot more stuff an is often easier to use: http://developer.android.com/guide/topics/graphics/animation.html. This is natively available in Android 3.0+ but can also be used in Android API level 7 with the support v7 library.
If you want to write a custom widget that is an integrated part of its view hierarchy and manually does its own animation drawing, you can use a Handler to time the updates (usually you'll want 60fps or 20ms between each invalidate()) and then in your onDraw() method draw your view's state based on SystemClock.uptimeMillis() as a delta from when the animation started.
Here's a simple repeated invalidate using Handler:
long mAnimStartTime;
Handler mHandler = new Handler();
Runnable mTick = new Runnable() {
public void run() {
invalidate();
mHandler.postDelayed(this, 20); // 20ms == 60fps
}
}
void startAnimation() {
mAnimStartTime = SystemClock.uptimeMillis();
mHandler.removeCallbacks(mTick);
mHandler.post(mTick);
}
void stopAnimation() {
mHandler.removeCallbacks(mTick);
}
Since this question has some interest I will reply.
The best way to to that is to have a separate canvas thread. A "separate" canvas can only be achieved with a SurfaceView. LunarLanding is an excelent example of that use. Each frame is calculated separately than the main view sharing only CPU time, not drawing time. Therefore is faster, even with the combination of for e.g a regular view at the top and an animating view at the bottom.
But you have to set an interval if you are in that shared environment. That interval is used for the FPS cap. If you don't set FPS cap then the CPU will running wild managing to get good animation to the SurfaceView if it was alone. Capping it at 60fps or even less will do the trick to draw all views efficiently with no CPU overload.
So see the drawing thread of the Lunar Landing from the API demos and set a FPS cap.
private long timeNow;
private long timeDelta;
private long timePrevFrame;
private void capFps(int fps) {
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
try {
//ps you can always set 16 instead of 1000/fps for 60FPS to avoid the calculation every time
Thread.sleep((1000 / fps) - timeDelta);
} catch (InterruptedException e) {
}
timePrevFrame = System.currentTimeMillis();
}
and then the drawing thread will look something like this:
#Override
public void run() {
Canvas c;
while (run) {
c = null;
sleepFps(60, false);
try {
synchronized (surfaceHolder) {
c = surfaceHolder.lockCanvas(null);
widgetView.doDraw(c);
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}

Draw high res animations with high frame rate on Android

I've got 30+ single bitmaps (320x240 pixels) that I would like to display one after another in full screen on Android devices resulting in an animation. Currently I implemented the animation using an ImageView and a Timer that sets the next frame and then sends a message that will apply the next frame. The resulting frame rate is very low: < 2 fps.
The timer:
animationTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Drawable frame = getNextFrame();
if (frame != null) {
Message message = animationFrameHandler.obtainMessage(1, frame);
animationFrameHandler.sendMessage(message);
}
}
}, 0, (int) (1000.0d / fps));
The handler:
final Handler animationFrameHandler = new Handler() {
#Override
public void handleMessage(Message message) {
setImageDrawable((Drawable) message.obj);
}
};
Since I want to achieve frame rates up to 30 fps I have to make use of another mechanism and heard of Canvas.drawBitmapMesh() and OpenGL.
If possible I would like to avoid using OpenGL.
Thank you very sharing your experiences!
My now working approach is the following:
Before starting the animation, load every frame into a List<Bitmap>. Important: Call System.gc() if you're getting OutOfMemoryErrors – that really helps loading more bitmaps into the memory. Then have a thread running that posts the next frame to a View instance that then update it's canvas.
Loading the frames and starting the animation
// Loading the frames before starting the animation
List<Bitmap> frames = new ArrayList<Bitmap>();
for (int i = 0; i < 30; i++) {
// Load next frame (e. g. from drawable or assets folder)
frames.add(...);
// Do garbage collection every 3rd frame; really helps loading all frames into memory
if (i %% 3 == 0) {
System.gc();
}
}
// Start animation
frameIndex = 0;
animationThread.start();
Thread that applies the next frame
private final class AnimationThread extends Thread {
#Override
public void run() {
while (!isInterrupted()) {
// Post next frame to be displayed
animationView.postFrame(frames.get(frameIndex));
// Apply next frame (restart if last frame has reached)
frameIndex++;
if (frameIndex >= frames.size()) {
frameIndex = 0;
}
try {
sleep(33); // delay between frames in msec (33 msec mean 30 fps)
} catch (InterruptedException e) {
break;
}
}
}
}
The animation view
class AnimationView extends View {
Bitmap frame = null;
public void postFrame(Bitmap frame) {
Message message = frameHandler.obtainMessage(0, frame);
frameHandler.sendMessage(message);
}
protected final Handler frameHandler = new Handler() {
#Override
public void handleMessage(Message message) {
if (message.obj != null) {
frame = (Bitmap) message.obj;
} else {
frame = null;
}
invalidate();
}
}
#Override
protected void onDraw(Canvas canvas) {
if (frame == null) return;
canvas.drawARGB(0, 0, 0, 0);
canvas.drawBitmap(frame, null, null, null);
}
}
You should look at the FrameAnimation class; http://developer.android.com/guide/topics/graphics/2d-graphics.html#frame-animation to do frame animation with Androids animation.
Though that might still be too slow.
The other alternative if you don't want to use OpenGL ES is to draw to the Canvas as you've mentioned. But just use .drawBitmap, not the drawBitmapMesh. Create a SurfaceView, which has a thread, that thread should draw on your Canvas at whatever interval you want.
It's pretty straightforward, just read the Android docs, the information is all there.
I'll let someone else go into the best way of doing this but one thing that immediately jumps to mind from your post that isn't helping is using TimerTask is a terrible way to do this and is not meant for animation.
Probably won't help with performance, but if those bitmaps are resources you might want to consider using an AnimationDrawable. If not, try to extend Drawable and implement the Animatable interface. Views already have built-in support for animating drawables, no need to use a handler for that.
One way to improve performance might be to match the bit-depth of the drawables to those of your current window. Romain Guy did a keynote on this and animations in general once: http://www.youtube.com/watch?v=duefsFTJXzc

Categories

Resources