Android alapú kamerakezelés – Netbeans alá

Az Android alapú programozás nem egy nehéz feladat – könnyű egyszerű, értelmes alkalmazásokat benne összerakni. Ha azonban alacsonyszintű megoldást kívánunk benne létrehozni, pl. kamera alapú megoldást, akkor bizony alaposan át kell böngésznünk a dokumentációkat, a fejlesztői fórumokat és akár még az operációs rendszer forráskódját is, hogy sikerüljön működésre bírni egy alkalmazást. Mivel nekünk sok idő ment el egy ilyen megoldás létrehozására, ezért most közkinccsé tesszük az eddigi sikeres próbálkozásainkat.

Megjegyezzük, hogy az általunk gyakran használt programozói környezet az Oracle Netbeans, így az Android fejlesztést is ez alá képzeltük el. Tudni kell azonban, hogy a Google elsődlegesen az Eclipse alapú fejlesztést támogatja. Általunk használt verziók:

  • Microsoft Windows 7
  • Netbeans 7 beta (6.9.1, illetve 6.8 esetében nem lesz érdemi változás)
  • Android SDK Revision 10
  • Kenai nbandroid 1.1 beta
1. lépés: Telepítés

Android SDK letöltése és telepítése rutinmunka lehet azok számára, akik már telepítettek legalább egy önálló alkalmazást számítógépre. Ezt nem részleteznénk, csak megemlítjük, hogy a C:\Android könyvtár alá telepítettük. A telepítés során arra kell csak figyelni, hogy a Google USB Driver package telepítésre kerüljön. Miért fontos ez? Azért, mert enélkül nehéz lesz a valódi eszközre telepíteni majd az alkalmazást. A telepítés után indítsuk el a Google SDK Manager alkalmazását. Miután telepítettünk vagy elhagytuk a frissítéseket, előbukkan az az ablak, amelyen az Android SDK legfontosabb beállításai találhatóak. Ellenőrizzük az “Installed Packages” rovatban, hogy az USB Driver elérhető-e – ha nem, akkor telepítsük. A “Virtual Devices” a virtuális Android telefonokat mutatja, ide kattintsunk. Jobboldalon a New… gomb lenyomásával létrehozhatjuk a kívánt virtuális gépünket. Mi az alábbi beállításokat tettük:

  • Name: Android-7
  • Target: Android 2.1-update 1 API Level 7
  • Skin: Default
  • Hardware: Abstracted LCD Density (240), Camera Support (yes), Touch-screen support (yes), Maximum horizontal camera pixels (640)

Ezek után célszerű telepíteni a Kendai nbandroid modult a Netbeans alá. A megfelelő elem letöltése és kicsomagolása után lépjünk be a Netbeans programba, majd nyissuk meg a “Tools > Plugins > Downloaded > Add Plugins…” menüket. Adjuk hozzá a kicsomagolt könyvtárban levő összes nbm kiterjesztésű állományt, és a licencfeltételek végigolvasása, illetve azok elfogadása után telepítjük beépülő alkalmazást. Sajnos, ekkor újra kell indítanunk a Netbeans-t, majd az újraindítás után a “Tools > Options > Miscellaneous > Android” nyomvonalon haladva beírjuk az Android SDK lokációját – a mi esetünkben ez C:\Android\android-sdk volt.

A valódi telefonra való alkalmazás-telepítéshez most csatlakoztassuk a bekapcsolt Android operációs rendszerrel rendelkező telefonunkat a számítógéphez (USB kábellel). A telepítéshez szükség van még a telefon beállítására. A telefonon nyomjuk meg a Menu gombot, majd a Settings (Beállítások) -> Applications (Alkalmazások) -> Development (Fejlesztés) nyomvonalon kapcsoljuk be az USB debugging opciót. Az esetek többségében nem sikerül a fejlesztői kapcsolat létrehozása (ADB hiányában) – a telepítés hibával leáll. A megoldás: a Windows Explorer (Intéző) segítségével a My Computer (Számítógép) sorra egy jobb egérkattintás után a Manage (Kezelés) menüpontot válasszuk ki – ebben pedig navigáljunk az Devices (Eszközkezelő) menüpontra. Az Other Devices (Egyéb eszközök) között egy figyelmeztető felirattal ellátott eszköz jelenik meg, nekünk ADB felirat jelenik meg. Az eszközre egy jobb egérkattintással előbukkanó Update driver… (Illesztőprogram frissítése…) segítségével, majd az illesztő program helyi meghajtón való keresése menü kiválasztásával megadhatjuk a Google USB driver telepítési helyét. Mi ide a C:\Android értéket írtuk be (valójában a C:\Android\android-sdk\extras\google\usb_driver\ tartalmazza a kívánt meghajtószoftvert). Ha sikeres volt a telepítés, és csatlakoztatva van az eszköz, akkodr a C:\Android\android-sdk\platform-tools\ könyvtárban futtatott

adb devices

parancs mutatja a számítógéphez csatlakozott eszközöket.

2. lépés: Fejlesztési háttér

Hozzunk létre új projektet a Netbeans segítségével a szokott módon azzal a kis különbséggel, hogy most a projektek az Android kategóriát, továbbá az Android Application projektet válasszuk. Ezek után kerül kiválasztásra, hogy milyen verzióra kívánunk fejleszteni (ez nálunk Android 2.1-update 1 volt). Nálunk a fő osztályt MainActivity névre keresztelték. A projekt létrehozása után készítsünk egy próba fordítást, amelynek során létrejön néhány hasznos állomány. Az Android projekten belül elérhető “Important files” lenyíló menü alatt (a projekt navigációs részében) található az Android manifest file, ami nem más, mint az Android programozásában nélkülözhetetlen AndroidManifest.xml. Ide az alábbi sorokat írjuk be:

...
 <uses-permission android:name="android.permission.CAMERA" />
 <uses-feature android:name="android.hardware.camera" />
 <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
    <application android:label="@string/app_name" android:icon="@drawable/icon" android:debuggable="true">
...
    </application>
3. lépés: A kód

A kód néhány trükköt tartalmaz, amely a kamera- és a telefongyártók a mobil alapú videófeldolgozáshoz fűződő sajátos viszonyát igyekszik kompenzálni. A legfontosabb ezek közül:

  • A kamerakép esetében a képernyő megváltozása általában sem támogatott – örüljünk, ha olyan gépünk van, ahol erre gondoltak. A másik jellemző problémaforrás, hogy a kijelző mérete és a megjeleníthető kép mérete jellemzően nem azonos – de csak az érvényes képméretek használhatóak. Ez azt is jelenti, hogy törődni kell a képernyő méretének beállításával, az alapértelmezett érték használata gyakran hibához vezet.
  • Csak a kamera által támogatott frissítési sebességen lehet az előnézeti módot használni – a legtöbb kamera esetében ez 15fps.
  • A képek kódolása 12 bites (per pixel – átlagban) NV21 (YCbCr) formátumban vannak rögzítve. Elvben van lehetőség ennek átállítására, de akkor az előnézeti kép sebessége nem lesz már érvényes – több mobiltelefon esetében is 1fps-re csökken. Törődjünk bele, hogy ez a kódolási formátum.
  • Android 2.2 (API 8)-tól kezdve több kamera is lehet a rendszerben – ebben az esetben a kamera azonosítójával tudjuk megadni, hogy mit jelenítsünk meg.

Ezek után jöjjön a kód vázlata:

public class MainActivity
                        extends Activity
                        implements SurfaceHolder.Callback, Camera.PreviewCallback {

    private SurfaceView surfaceView;
    private SurfaceHolder surfaceHolder;
    private Camera camera;
    private boolean isPreviewRunning = false;

    @Override
    public void onCreate( Bundle savedInstanceState ) {  // ---> Activity miatt
        super.onCreate( savedInstanceState );
        requestWindowFeature( Window.FEATURE_NO_TITLE );
        setContentView( R.layout.main ); // vagy amit a main.xml -ben beállítottál...
        setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE );  // !! lásd problémák
        surfaceView = (SurfaceView) findViewById( R.id.surface_camera ); // a main.xml nálunk tartalmaz egy ilyen azonosítót
        surfaceHolder = surfaceView.getHolder();
        surfaceHolder.addCallback( this );
        surfaceHolder.setType( SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS ); // szükségünk lesz rá...
    }

    public void surfaceChanged( SurfaceHolder holder, int format, int width, int height ) { // ---> SurfaceHolder.Callback miatt
        if ( isPreviewRunning ) {
            camera.stopPreview();
            camera.release();
            initCamera( holder ); // első futásra feleslegesen engedjük el a kamerát... nem szép.
        }
        Camera.Parameters p = camera.getParameters();
        p.setPreviewSize( 768, 432 ); // újabb HTC mobil esetében ez maximális méret, ami még jó - erősen hardverfüggő!
        p.setRotation( 0 ); // ne legyen forgatás, állandó méret
        camera.setParameters( p );
        camera.startPreview();
    }

    public void surfaceCreated( SurfaceHolder holder ) { // ---> SurfaceHolder.Callback miatt
         initCamera( holder );
    }

    public void surfaceDestroyed( SurfaceHolder holder ) { // ---> SurfaceHolder.Callback miatt
        camera.setPreviewCallback( null ); // először engedjük el, mert mindjárt megszűnik a képernyő - ami változás
        isPreviewRunning = false; // ennek sok jelentősége nincs, de így teljes a kód...
        camera.stopPreview();
        camera.release();
        camera = null;
    }

    public void onPreviewFrame( byte[] data, Camera camera ) { // ---> Camera.PreviewCallback miatt
        // saját feldolgozó kódod jön ide...
    }

    private void initCamera( SurfaceHolder holder ) {
        if ( isPreviewRunning ) {
            camera.stopPreview();
            camera.release();
        }
        try {
            camera = Camera.open();
            camera.setPreviewDisplay( holder );
            camera.setPreviewCallback( this );
        }  catch ( Exception e ) { // Egyszerűsítünk - több Exception lehet, ezeket célszerű külön üzenetekkel ellátni...
            Log.e( "Camera exception is caught: ", e.getMessage(), e );
        }
        isPreviewRunning = true;
    }