Борьба с SurfaceView, камерой и OpenGL

Мы боролись с некоторыми проблемами, связанными с SurfaceViews, более недели, и не нашли подходящего решения для них. Мы читаем другие вопросы на форуме относительно похожих проблем (и даже исходного кода Mixare), но не можем найти ответ, поэтому мы надеемся, что вы каким-то образом поможете нам.

Сценарий: У нас есть

  • a SurfaceView для камеры
  • a SurfaceView для слоя OpenGL, который находится поверх камеры.
  • другой вид, который показывает некоторую информацию о том, что мы видим на экране. Это идет поверх обоих SurfaceViews.

Проблема:

Как бы мы ни старались, оба SurfaceViews, похоже, не ладят друг с другом. Если мы попытаемся:

setContentView(mCameraPreview); 
addContentView(mGLSurfaceView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 
addContentView(mInfoView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

(что кажется логичным), все идет так, как ожидалось, до тех пор, пока мы не заблокируем/не разблокируем телефон. После этого GLSurfaceView просто исчезает (а не InfoView, этот все еще отображается).

Если вместо этого мы попытаемся:

setContentView(mGLSurfaceView); 
addContentView(mCameraPreview, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); 
addContentView(mInfoView, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

Тогда проблема в том, что GLSurfaceView появляется только после блокировки/разблокировки, а перед этим на экране отображается камера и InfoView.

Мы обнаружили, что если мы спящем основной поток за 4,6 секунды (или больше) после выполнения onStart() в активности, которая отображает представления, поведение будет таким же, как и ожидалось (отображение как камеры, glsurface, так и информации, даже после блокировка/разблокировка).

Дело в том, что мы ищем более... элегантное решение.

Нам кажется, что проблема заключается в том, что камера занимает больше времени, чем ожидалось в Camera.open(), и поэтому добавляется представление камеры, добавляется GLSurfaceView, и когда камера действительно открывается, она открывается поверх GLSurfaceView, В связи с этим мы использовали bringToFront() в GLSurfaceView и получили его поверх информационного представления, но после блокировки/разблокировки камера все еще открывалась поверх него, оставляя нас с экраном только с предварительным просмотром камеры.

Любые идеи? Как мы можем показать как SurfaceViews, так и представление информации поверх них?

Ответ 1

попробуйте следующее:

mLayout.addView(mRenderView);
mLayout.addView(mCustomSurfaceView);

// without this line, the camera preview cannot be displayed 
// when running activity at first time.
mCustomSurfaceView.setZOrderMediaOverlay(true);

это сработало для меня:)

Ответ 2

У меня была та же проблема. Когда вы намекаете на себя: несколько SurfaceView не ладят друг с другом тем, что их порядок Z undefined.

На моем Samsung Galaxy S2 заказ такой же, как вы описываете (не знаю, как это делается на других телефонах). То, как я решил это, проверяет создание первого момента Activity в onCreate():

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

    //...

    if ( savedInstanceState == null )
    {
        initCamView();
        initOpenGL();
    }
    else
    {
        initOpenGL();
        initCamView();
    }

    //...
}

с:

private void initOpenGL()
{
    mGLSurfaceView = new GLSurfaceView(this);
    mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
    mGLSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);

    mOGLRenderer = new OGLRenderer(this);
    mGLSurfaceView.setRenderer(mOGLRenderer);
    mRL.addView(mGLSurfaceView); // mRL is the Relative Layout
}

и

private void initCamView()
{
    mCamView = new CustomCameraView( this.getApplicationContext(),
                                     this.getWindowManager() );
    mRL.addView(mCamView); // mRL is the Relative Layout
}

Не самое изящное решение, но это лучше, чем позволить потоку спать в течение 4.6 секунд.

Также возможно будет просто заблокировать экран в одной ориентации, но тогда вам нужно сделать много уродливого взлома, чтобы получить правильное расположение оверлей.

Или, если вы ориентируетесь только на Android 3.0 или выше (уровень API 8), вы можете просто показать камеру в OpenGL SurfaceTexture. http://developer.android.com/reference/android/hardware/Camera.html#setPreviewTexture(android.graphics.SurfaceTexture)

Ответ 3

У меня была та же проблема: мне нужен SurfaceView под GLSurfaceView, первый для воспроизведения видео и второй для запуска игры.

Что бы я ни делал, порядок z между этими двумя поверхностными видами кажется случайным.

Но я нашел решение, благодаря этому сообщению: fooobar.com/questions/471414/...

Хитрость заключается в том, чтобы удалить использование макета, но вместо этого использовать setContentView/addContentView.

Ответ 4

Каждая поверхность SurfaceView имеет глубину Z. Существуют три возможные глубины: по умолчанию (внизу), "наложение медиа" (над ним) и "верх" (что выше всего, включая пользовательский интерфейс на основе представления). Если у вас есть две перекрывающиеся поверхности на одной и той же глубине, одна победит, но вы не можете надежно определить, какой из них. Вы можете получить согласованное поведение на одном устройстве, только чтобы найти, что он работает другим способом на другом устройстве.

Композиция выполняется с помощью аппаратных накладок, когда это возможно. Многие популярные в настоящее время устройства имеют 4 плоскости наложения. Как только вы превысите это, композиция будет выполнена с графическим процессором, который будет дороже. Если у вас есть строка состояния, панель навигации и View UI, это три, поэтому вы получите только один SurfaceView для дешевых.

Для примера в вопросе было бы лучше объединить как можно больше вещей на поверхность, которая будет отображаться с помощью GLES. Это может включать выход камеры на API 11+. См. "Активность текстуры с камеры" в Grafika для примера. (Grafika также имеет демонстрационное приложение с тремя перекрывающимися SurfaceViews, см. "Активность с несколькими поверхностями".)

Если вы хотите узнать больше о том, почему SurfaceView ведет себя так, как описано в Android-уровневая графика,