Native startPreview failed on Nexus S? Check your preview size

I got several error reports for Nerdstalgia from people using Nexus S. They were something akin to this:

04-05 12:22:50.371: DEBUG/SEC_Overlay(102): overlay_createOverlay:IN w=176 h=144 format=99
04-05 12:22:50.375: DEBUG/SEC_Overlay(102): src width, height are changed [w= 176, h= 144]->[w=176, h= 144]
04-05 12:22:50.375: INFO/SEC_Overlay(102): Opened video1/fd=253/obj=006e2468/shm=251/size=4096
04-05 12:22:50.375: DEBUG/SEC_Overlay(102): dst width, height have changed [w= 16, h= 1] -> [w=16, h= 16]
04-05 12:22:50.375: INFO/SEC_Overlay(102): Postponing Stream Enable/1/0
04-05 12:22:50.375: ERROR/v4l2_utils(75): Error = Invalid argument from stream on
04-05 12:22:50.375: ERROR/SEC_Overlay(75): Stream Enable Failed!/-1
04-05 12:22:50.375: ERROR/CameraHardwareSec(75): ERR(virtual android::status_t android::CameraHardwareSec::setOverlay(const android::sp<android::Overlay>&))::(mOverlay->setCrop(0, 0, 176, 144) fail
04-05 12:22:50.375: ERROR/CameraService(75): mHardware->setOverlay() failed with status -2147483648
04-05 12:22:50.378: DEBUG/AndroidRuntime(3875): Shutting down VM
04-05 12:22:50.378: WARN/dalvikvm(3875): threadid=1: thread exiting with uncaught exception (group=0x40015560)
04-05 12:22:50.378: ERROR/AndroidRuntime(3875): FATAL EXCEPTION: main
        java.lang.RuntimeException: startPreview failed
        at android.hardware.Camera.startPreview(Native Method)
        (and here starts my code)

So it was failing well outside my domains--in a native method! There wasn't even a line number to have a look at. But was it because of something I did? I thought maybe it was because I wasn't synchronizing things properly--so I went to make sure everything was properly initialised, nothing was done before it was due... and still it crashed.

Then I paid even more close attention and I noticed that the difference between 'crash' and 'no crash' was that before it crashed it always output a mHardware->setOverlay() failed with status -2147483648 message

So since I couldn't find any solution or pointer anywhere, I decided to have a look at the native code--that way, I could at least find out what was that setOverlay() failed message about.

It took me a bit to navigate the code, since there are lots of messages and calls going back and forward between Samsung's specific code and Android's core, and I'm not sure I have totally understood how it works, but it seems that the firmware for the Nexus S (alias Samsung Crespo) requires that

  • the preview surface width is a multiple of 8 (and at least 8), and
  • the preview surface height is at least 16

That is defined in the check_fimc_dst_constraints function in mydroid/device/samsung/crespo/liboverlay/overlay.cpp

I changed the preview size to 8x16, and now the app doesn't crash on the Nexus S, although there's an unfortunate side effect: the preview is more visible than it was before --yes, it was visible. It was a 1x1 sized preview, and that's why it was failing on the Nexus S (and probably any other setup with that kind of surface size restriction). Now I need to find a way to hide it... oh wait, I found it! The answer, on the following post :P