Получение периодических ошибок при приобретении камеры Google Glass

Я получаю периодические ошибки при получении камеры в приложении Glass GDK. Приложение представляет собой простое приложение, которое:

  1. отвечает на голосовой триггер («проверить камеру»)
  2. запускает действие, чтобы сделать снимок
  3. возвращается к вызывающей активности для отображения моментального снимка.

Проблема в том, что приложение работает, но время от времени не принимает камеру. Я завернул вызовы камеры в несколько блоков try/catch, чтобы обработать ее (приложение просто закрывается, если не может получить камеру), но мне интересно, почему это происходит в первую очередь.

Меня бы это не беспокоило, если бы не следующее:

  1. Я заметил, что эти ошибки часто возникают при запуске приложения сразу после того, как я сделал снимок с голосовой подсказкой «сделать снимок» (как если бы приложение «сделать снимок» не выпустило камеру).

  2. Я приложил много-много попыток/отловов, чтобы застраховаться от каждого плохого вызова камеры, но... до того, как я это сделал (т.е. когда у меня был код, который не был так внимателен к освобождению камеры), устройство сильно нагревалось , настолько, что мне пришлось выключить и снова включить его, чтобы убедиться, что я не повредил его.

Единственная странность, которую я вижу в логах, это следующие сообщения. Я понятия не имею, что может означать "Неизвестный тип сообщения 8192"

11-29 19:38:16.344: E/Camera(4551): Received CAMERA_MSG_RELEASE
11-29 19:38:16.493: D/Camera-JNI(4551): android_hardware_Camera_release - context->decStrong(thiz)
11-29 19:38:16.524: E/Camera(4551): Unknown message type 8192

Поскольку я понятия не имею, что может быть причиной этого, я собираюсь опубликовать весь проект, чтобы посмотреть, есть ли что-то в файле xml или другом неясном месте, что может быть причиной этого.

Вот Манифест:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.testcamera"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.example.testcamera.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
            <action
                android:name="com.google.android.glass.action.VOICE_TRIGGER" />
            </intent-filter>
            <meta-data android:name="com.google.android.glass.VoiceTrigger"
                       android:resource="@xml/voice_trigger" />
        </activity>
        <activity
            android:name="com.example.testcamera.CameraActivity"
            android:label="@string/app_name" />
    </application>

</manifest>

Вот голосовой триггер:

<?xml version="1.0" encoding="utf-8"?>
<trigger keyword="@string/glass_voice_trigger">
        <constraints
        camera="true"
        network="true" 
        microphone="true" />
</trigger>

Вот строки:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string name="app_name">TestCamera</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    <string name="glass_voice_trigger">test the camera</string>

    <!-- Menu item strings. -->
    <string name="stop">Done</string>
    <string name="tapforoptions">Tap for options</string>

</resources>

Вот макет для основного действия:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/black">

        <ImageView
        android:id="@+id/bgPhoto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/black_bg"
        android:alpha="0.5" />

        <LinearLayout     android:id="@+id/queryLinearLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:orientation="vertical">

       <TextView
        android:id="@+id/text1"
        android:layout_width="wrap_content"
        android:layout_marginLeft="2dp"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textColor="#FFFFFF"
        android:text="@string/hello_world"  />

       <TextView
        android:id="@+id/text2"
        android:layout_width="wrap_content"
        android:layout_marginLeft="2dp"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textColor="#FFFFFF"
        android:text="@string/hello_world"  />

</LinearLayout>

                <LinearLayout 
    android:id="@+id/resultLinearLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:orientation="vertical">

       <TextView
        android:id="@+id/titleOfWork"
        android:layout_width="wrap_content"
        android:layout_marginLeft="2dp"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textColor="#FFFFFF"
        android:textSize="30sp"
        android:text="@string/hello_world"  />

       <TextView
        android:id="@+id/Singer"
        android:layout_width="wrap_content"
        android:layout_marginLeft="2dp"
        android:layout_height="wrap_content"
        android:gravity="left"
        android:textColor="#FFFFFF"
        android:textSize="36sp"
                android:textStyle="bold"
        android:text="@string/hello_world"  />

</LinearLayout>

       <ProgressBar
        android:id="@+id/my_progressBar"
        android:layout_height="wrap_content"
        android:layout_width="match_parent" 
        style="?android:attr/progressBarStyleHorizontal" 
        android:layout_gravity="bottom"
        android:layout_margin="10dp"
    />

                     <TextView
        android:id="@+id/tap_instruction"
        android:layout_width="fill_parent"
        android:layout_marginLeft="2dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_gravity="bottom"
        android:textColor="#FFFFFF"
        android:textSize="16sp"
        android:textStyle="bold"
        android:layout_margin="20dp"
        android:text="@string/tapforoptions"  />

</FrameLayout>

Вот макет активности камеры:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <SurfaceView 
        android:id="@+id/surfaceView"
        android:layout_height="match_parent"
        android:layout_width="match_parent" 
         />    
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />

</LinearLayout>

Вот код основного действия:

package com.example.testcamera;

import java.io.File;
import com.google.android.glass.touchpad.Gesture;
import com.google.android.glass.touchpad.GestureDetector;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.Display;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

public class MainActivity extends Activity {

    // App responds to voice trigger "test the camera", takes a picture with CameraActivity and then returns.

    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int TAKE_PHOTO_CODE = 1;
    private static final int PROGRESS_TIMEOUT = 30000; // in ms --> 10s
    private static final String IMAGE_FILE_NAME = "/sdcard/ImageTest.jpg";

    private boolean picTaken = false; // flag to indicate if we just returned from the picture taking intent
    private String theImageFile = ""; // this holds the name of the image that was returned by the camera

    private TextView text1;
    private TextView text2;

    private ProgressBar myProgressBar;
    protected boolean mbActive;

    private String inputQueryString;
    private String queryCategory;

    final Handler myHandler = new Handler(); // handles looking for the returned image file
    private int numberOfImageFileAttempts = 0;

    private String responseBody = "";

    private TextToSpeech mSpeech;

    private boolean readyForMenu = false;
    private boolean gotImageMatch = false;

    private GestureDetector mGestureDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.v(TAG,"creating activity");

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);
        text1 = (TextView) findViewById(R.id.text1);
        text2 = (TextView) findViewById(R.id.text2);
        text1.setText("");
        text2.setText("");
        myProgressBar = (ProgressBar) findViewById(R.id.my_progressBar);
        LinearLayout llResult = (LinearLayout) findViewById(R.id.resultLinearLayout);
        TextView tvResult = (TextView) findViewById(R.id.tap_instruction);
        llResult.setVisibility(View.INVISIBLE);
        tvResult.setVisibility(View.INVISIBLE);
        myProgressBar.setVisibility(View.INVISIBLE);

        // Even though the text-to-speech engine is only used in response to a menu action, we
        // initialize it when the application starts so that we avoid delays that could occur
        // if we waited until it was needed to start it up
        mSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                // Do nothing.
            }
        });

        mGestureDetector = createGestureDetector(this);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (!picTaken) {
            Intent intent = new Intent(this, CameraActivity.class);
            intent.putExtra("imageFileName",IMAGE_FILE_NAME);
            startActivityForResult(intent,1);
        }
        else {
            // do nothing
        }
    }

    /*
     * Send generic motion events to the gesture detector
     */
    @Override
    public boolean onGenericMotionEvent(MotionEvent event) {
        if (mGestureDetector != null) {
            return mGestureDetector.onMotionEvent(event);
        }
        return false;
    }

    private GestureDetector createGestureDetector(Context context) {
        GestureDetector gestureDetector = new GestureDetector(context);
            //Create a base listener for generic gestures
            gestureDetector.setBaseListener( new GestureDetector.BaseListener() {
                @Override
                public boolean onGesture(Gesture gesture) {
                    if (gesture == Gesture.TAP) {
                        // do something on tap
                        Log.v(TAG,"tap");
                        //if (readyForMenu) {
                            openOptionsMenu();
                        //}
                        return true;
                    } else if (gesture == Gesture.TWO_TAP) {
                        // do something on two finger tap
                        return true;
                    } else if (gesture == Gesture.SWIPE_RIGHT) {
                        // do something on right (forward) swipe
                        return true;
                    } else if (gesture == Gesture.SWIPE_LEFT) {
                        // do something on left (backwards) swipe
                        return true;
                    }
                    return false;
                }
            });
            gestureDetector.setFingerListener(new GestureDetector.FingerListener() {
                @Override
                public void onFingerCountChanged(int previousCount, int currentCount) {
                  // do something on finger count changes
                }
            });
            gestureDetector.setScrollListener(new GestureDetector.ScrollListener() {
                @Override
                public boolean onScroll(float displacement, float delta, float velocity) {
                    // do something on scrolling
                    return false;
                }
            });
            return gestureDetector;
        }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.stop:
                finish();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      picTaken = true;
      switch(requestCode) {
        case (1) : {
          if (resultCode == Activity.RESULT_OK) {
            // TODO Extract the data returned from the child Activity.
              Log.v(TAG,"onActivityResult"); 

              File f = new File(IMAGE_FILE_NAME);
               if (f.exists()) {
                   Log.v(TAG,"image file from camera was found");

                   Bitmap b = BitmapFactory.decodeFile(IMAGE_FILE_NAME);
                   Log.v(TAG,"bmp width=" + b.getWidth() + " height=" + b.getHeight());
                   ImageView image = (ImageView) findViewById(R.id.bgPhoto);
                   image.setImageBitmap(b);

                   text1 = (TextView) findViewById(R.id.text1);
                   text2 = (TextView) findViewById(R.id.text2);
                   text1.setText("Got a picture.");
                   text2.setText("\nSaved successfully to " + IMAGE_FILE_NAME);

                   LinearLayout llResult = (LinearLayout) findViewById(R.id.resultLinearLayout);
                   llResult.setVisibility(View.VISIBLE);
                   TextView line1 = (TextView) findViewById(R.id.titleOfWork);
                   TextView line2 = (TextView) findViewById(R.id.Singer);
                   TextView tap = (TextView) findViewById(R.id.tap_instruction);
                   line1.setText("");
                   line2.setText("");
                   tap.setVisibility(View.VISIBLE);
               }
          }
          else {
              Log.v(TAG,"onActivityResult returned bad result code");
              finish();
          }
          break;
        } 
      }
    }

    @Override
    protected void onDestroy() {

        //Close the Text to Speech Library
        if(mSpeech != null) {
            mSpeech.stop();
            mSpeech.shutdown();
            mSpeech = null;
            Log.d(TAG, "TTS Destroyed");
        }
        super.onDestroy();
    }

}

Вот код активности камеры:

package com.example.testcamera;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ImageView;

public class CameraActivity extends Activity implements SurfaceHolder.Callback
{
    private static final String TAG = CameraActivity.class.getSimpleName();
    public static final int BUFFER_SIZE = 1024 * 8;
    String imageFileName = "";
    //a variable to store a reference to the Image View at the main.xml file.
    private ImageView iv_image;
    //a variable to store a reference to the Surface View at the main.xml file
    private SurfaceView sv;
    //a bitmap to display the captured image
    private Bitmap bmp;
    //Camera variables
    //a surface holder
    private SurfaceHolder sHolder; 
    //a variable to control the camera
    private Camera mCamera;
    //the camera parameters
    private Parameters parameters;

  @Override
  public void onCreate(Bundle savedInstanceState)
  {
      Log.v(TAG,"onCreate");
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_camera);
      //get the Image View at the main.xml file
      iv_image = (ImageView) findViewById(R.id.imageView);
      sv = (SurfaceView) findViewById(R.id.surfaceView);
      //Get a surface
      sHolder = sv.getHolder();
      sHolder.addCallback(this);
      Bundle extras = getIntent().getExtras();
      // get the image file name from the caller to save the 640x360 image
      imageFileName = extras.getString("imageFileName");
  }

    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3)
    {
      Log.v(TAG,"surfaceChanged");
           //get camera parameters
      try {
          parameters = mCamera.getParameters();
          Log.v(TAG,"got parms");
          //set camera parameters
          parameters.setPreviewSize(640,360);
          parameters.setPictureSize(1280,720);
          //Camera.Parameters params = mCamera.getParameters();
          parameters.setPreviewFpsRange(30000, 30000);
          Log.v(TAG,"parms were set");
          mCamera.setParameters(parameters);

          mCamera.startPreview();
          Log.v(TAG,"preview started");

          //sets what code should be executed after the picture is taken
          Camera.PictureCallback mCall = new Camera.PictureCallback()
          {
            public void onPictureTaken(byte[] data, Camera camera)
            {
                Log.v(TAG,"pictureTaken");
                Log.v(TAG,"data bytes=" + data.length);
                  //decode the data obtained by the camera into a Bitmap
                Bitmap bmp = decodeSampledBitmapFromData(data,640,360);
                Log.v(TAG,"bmp width=" + bmp.getWidth() + " height=" + bmp.getHeight());
                FileOutputStream outStream = null;
                try{
                    FileOutputStream fos = new FileOutputStream(imageFileName);
                    final BufferedOutputStream bos = new BufferedOutputStream(fos, BUFFER_SIZE);
                    bmp.compress(CompressFormat.JPEG, 100, bos);
                    bos.flush();
                    bos.close();
                    fos.close();
                } catch (FileNotFoundException e){
                    Log.v(TAG, e.getMessage());
                } catch (IOException e){
                    Log.v(TAG, e.getMessage());
                }
                Intent resultIntent = new Intent();
                // TODO Add extras or a data URI to this intent as appropriate.
                resultIntent.putExtra("testString","here is my test");
                setResult(Activity.RESULT_OK, resultIntent);
                finish();
            }
          };
          Log.v(TAG,"set callback");
          mCamera.takePicture(null, null, mCall);
      }       
      catch (Exception e) {
        try {
              mCamera.release();
              Log.e(TAG,"released the camera");
          }
          catch (Exception ee) {
              // do nothing
              Log.e(TAG,"error releasing camera");
              Log.e(TAG,"Exception encountered relerasing camera, exiting:" + ee.getLocalizedMessage());
          }
        Log.e(TAG,"Exception encountered, exiting:" + e.getLocalizedMessage());
         mCamera = null;  
        Intent resultIntent = new Intent();
       setResult(Activity.RESULT_CANCELED, resultIntent);
       finish();
      }
    }

    public static Bitmap decodeSampledBitmapFromData(byte[] data,
            int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(data, 0, data.length,options);
        options.inSampleSize = 2; // saved image will be one half the width and height of the original (image captured is double the resolution of the screen size)
        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeByteArray(data, 0, data.length,options);
    }


    public void surfaceCreated(SurfaceHolder holder)
    {
          Log.v(TAG,"surfaceCreated");
          // The Surface has been created, acquire the camera and tell it where
          // to draw the preview.
          try {
              mCamera = Camera.open();
              Log.v(TAG,"acquired the camera");
              mCamera.setPreviewDisplay(holder);
              Log.v(TAG,"set surface holder for preview");
            }
          catch (Exception e) {
              try {
                  mCamera.release();
                  Log.v(TAG,"released the camera");
              }
              catch (Exception ee) {
                  // do nothing
                  Log.e(TAG,"Exception encountered releasing camera, exiting:" + ee.getLocalizedMessage());
              }
              Log.e(TAG,"Exception encountered, exiting:" + e.getLocalizedMessage());
              mCamera = null;  
             Intent resultIntent = new Intent();
             setResult(Activity.RESULT_CANCELED, resultIntent);
             finish();
          }

     }

    public void surfaceDestroyed(SurfaceHolder holder)
    {
        Log.v(TAG,"surfaceDestroyed");
        if (mCamera != null) {
            mCamera.stopPreview();
            //release the camera
            mCamera.release();
            //unbind the camera from this object
            mCamera = null;
        }  
    }

    @Override
    public void onPause()
    {
        Log.v(TAG,"onPause");
        super.onPause();
        if (mCamera != null) {
            mCamera.stopPreview();
            //release the camera
            mCamera.release();
            //unbind the camera from this object
            mCamera = null;
        }
    }

    @Override
    public void onDestroy()
    {
        Log.v(TAG,"onDestroy");
        super.onDestroy();

        if (mCamera != null) {
            mCamera.stopPreview();
            //release the camera
            mCamera.release();
            //unbind the camera from this object
            mCamera = null;
        }
    }
}

Бьюсь об заклад, это что-то простое, чего я просто не вижу...


person Darren    schedule 30.11.2013    source источник


Ответы (1)


На самом деле, я только что проверил отчеты об ошибках API Google Glass и увидел точно такую ​​же проблему, с которой столкнулся кто-то другой: https://code.google.com/p/google-glass-api/issues/detail?id=259

Я заметил то же самое, что это было связано с голосовым триггером. Когда я выбрал элемент из меню смахивания, я не видел тех же ошибок.

Это, наверное, можно закрыть

* позже * Просто обновляю это. Я написал небольшой пример приложения, которое делает повторные попытки получить камеру. Проект находится здесь: https://github.com/dazza222/GlassCameraSnapshot

person Darren    schedule 30.11.2013