It actually was something that intrigued me to no end. No tutorial that I’ve read explained how to do it; there were only vague (and incorrect) mentions to not setting PUSH_BUFFERS in the SurfaceHolder, but yet there were apps who were totally hiding the camera preview, such as the famous “spy camera” style apps (those that look like an innocently looking web page but actually are recording everything). So there should be a way, and I just had to find it.
Things that do not work:
- Placing the preview outside the screen, yet still in the current layout
- Placing another element on top of the preview –since the preview must be a SurfaceView type of View, its behaviour is to “crop” the area of screen that is in, and draw it self. So it would be visible anyway, through the “hole” it punched in the elements on top of it.
- Setting the preview to INVISIBLE or GONE
- Setting the preview size to a small size of 1×1 (here’s why it doesn’t work… sometimes)
Can you think of yet another way of hiding the preview? 😀
Anyway, the solution is to follow a series of steps–it’s not a static process. Ready?
This is the solution:
Before starting the camera preview…
- Set the camera’s preview display to the preview’s SurfaceHolder
- Set the preview surface to VISIBLE
And after starting the camera preview…
- Set the preview surface to GONE or INVISIBLE
This process must be repeated each time the camera needs to start previewing. So if you stop it for whatever the reason (e.g. in onPause), remember to repeat the “before” steps, or you won’t be able to re-start the preview again.
If you’re lucky, it will just do nothing, because since the surface is hidden it won’t trigger surfaceChanged or surfaceCreated events. In a bad case, it can even throw a huge exception in native code, and “force close” your app, which you know drives people crazy 😛
Warning, rants ahead!
I don’t know if this “show and hide” game must be played because of…
- some silly restriction such as the one that mandates that camera phones have to emit an annoying shutter sound,
- or if it’s just some technical restriction
The first hypothesis sounds so myopically short-sighted that it actually seems quite feasible: politicians would strongly agree on it, and programmers would just make the surface so small that no hypothetical subject would notice being spied upon. Job done.
The second hypothesis sounds equally silly: why, oh why, do we have to assign a preview surface for the camera driver to output its frames to it if we are also requesting the raw YUV420 frames too in order to decode and process them? It’s really idiotic that the camera driver has to decode the frames and scale them in order to fit into a minuscule preview surface.
But whatever the reason is–I don’t really care.
All I care about is making great stuff. And this kind of non-trivial issues prevent me from doing so.
Imagine you’re the camera driver, and you’re requested to start a preview, right? Then if you’re provided with an output surface, output your frames there, scaling as required. If you’re provided with a preview callback, then call that method and send your raw frames there as you get them. You’re only allowed to fail and crash if you don’t get ANYTHING (no surface, no callback).
It’s that simple.
It’s reasonable, and easier to program and use. And it probably uses less processor resources if only the preview callback is used, because there’s nothing to be blitted to the preview surface, no area to be invalidated and redrawn, nothing.
Now we simply need to get the Android Camera API changed, plus all the drivers in all the phones upgraded to reflect this extremely sensible and logical approach.
Oh well… sometimes dreaming is the only thing that is left ^^