Проблемы с TextureView на Android 4.2

Кто-нибудь сталкивался с проблемами с TextureView и MediaPlayer на Android 4.2.2?

РЕШЕНО: см. ответ ниже и как обслуживать файлы со встроенного HTTP-сервера.

ОБНОВЛЕНИЕ: видео, которые не отображаются, если они встроены в папку res/raw, воспроизводятся нормально, если они размещены в Интернете. См. ниже, например:

// This works
mMediaPlayer.setDataSource("http://someserver.com/test.mp4");
//This does not work. The file is the same as the one above but placed in res/raw.
String vidpath = "android.resource://" + getActivity().getPackageName() + "/" + R.raw.test;
mMediaPlayer.setDataSource(getActivity(), Uri.parse(vidpath));

Нам нужен вид текстуры, чтобы мы могли применить к нему некоторые эффекты. TextureView используется для отображения видео из MediaPlayer. Приложение работает на Android 4.1, 4.3 и 4.4 (на многих устройствах, включая старый Nexus S до Nexus 10 и Note 3), но на 4.2.2 TextureView становится черным. MediaPlayer не сообщает об ошибках или исключениях, и сообщаемые размеры видео являются правильными. Тестирование SurfaceView на этом конкретном устройстве отображает видео, но тогда мы не можем манипулировать представлением так, как нам нужно.

Интересным моментом является то, что TextureView работает на этом устройстве при воспроизведении потокового видео Nasa Live Streaming Video и некоторых других потоковых файлов m3u8 (http://www.nasa.gov/multimedia/nasatv/NTV-Public-IPS)..m3u8), но нам нужно воспроизводить встроенное видео из папки raw. Однако мы заметили, что в самом верху TextureView есть линия 4x1 пикселя, которая очень быстро мигает некоторыми цветами. Интересно, воспроизводит ли медиаплеер видео по этой тонкой линии, или, может быть, это проблема с кодировкой или проблема с оборудованием (это конкретное устройство 4.2.2 является имитацией iPad mini, называемого... haiPad >.‹ (какой из конечно это целевое устройство клиента - ненавижу тебя Мерфи)).

Вот информация, которую я смог собрать о видео, которое не воспроизводится:

MPEG-4 (Base Media / Version 2): 375 KiB, 5s 568ms
1 Video stream: AVC
1 Audio stream: AAC
Overall bit rate mode: Variable
Overall bit rate: 551 Kbps
Encoded date: UTC 2010-03-20 21:29:11
Tagged date: UTC 2010-03-20 21:29:12
Writing application: HandBrake 0.9.4 2009112300
Video: 466 Kbps, 560*320 (16:9), at 30.000 fps, AVC ([email protected]) (2 ref Frames)

У кого-нибудь есть указатели?


person AngraX    schedule 21.02.2014    source источник


Ответы (1)


Для тех, кто сталкивается с подобными проблемами, это то, что сработало для нас.

Мы до сих пор не знаем, почему воспроизведение встроенных видео в приложении не работает. Пробовали res/raw, ассеты и копирование во внутреннюю память и на SD-карту.

Поскольку он мог воспроизводить видео с HTTP-сервера, мы в итоге встроили в приложение легкий HTTP-сервер для обслуживания файлов непосредственно из каталога ресурсов. Здесь я использую https://github.com/NanoHttpd/nanohttpd/blob/master/core/src/main/java/fi/iki/elonen/NanoHTTPD.java в качестве встроенного сервера.

Чтобы это работало, мне просто нужно поместить видео в папку с ресурсами. Например, assets/animation/animation1.mp4. При ссылке на файл вы передаете «animation/animation1.mp4» в качестве пути, и сервер будет обслуживать файл из каталога ресурсов.

Класс приложения запускает http-сервер и регистрирует его как службу.

public class MyApplication extends Application {

    private NanoServer mNanoServer;

    @Override
    public void onCreate() {

        super.onCreate();
        mNanoServer = new NanoServer(0, getApplicationContext());

        try {

            mNanoServer.start();
        } catch (IOException e) {

            Log.e("Unable to start embeded video file server. Animations will be disabled.",
                    "MyApplication", e);
        }
    }

    @Override
    public void onTerminate() {

        mNanoServer.stop();
        super.onTerminate();
    }

    @Override
    public Object getSystemService(String name) {

        if (name.equals(NanoServer.SYSTEM_SERVICE_NAME)) {
            // TODO Maybe we should check if the server is alive and create a new one if it is not.
            // How often can the server crash?
            return mNanoServer;
        }

        return super.getSystemService(name);
    }
}

Класс NanoServer

/*
 * TODO Document this.
 */
public class NanoServer extends NanoHTTPD {

    public static final String SYSTEM_SERVICE_NAME = "NANO_EMBEDDED_SYSTEM_HTTP_SERVER";
    private Context mContext;

    public NanoServer(int port, Context context) {
        super(port);
        this.mContext = context;
    }

    public NanoServer(String hostname, int port) {
        super(hostname, port);
    }

    @Override
    public Response serve(IHTTPSession session) {

        try {

            Uri uri = Uri.parse(session.getUri());
            String fileToServe = normalizePath(uri.getPath());

            return new Response(Status.OK, "video/mp4", (InputStream) mContext.getAssets().open(fileToServe));
        } catch (IOException e) {

            return new Response(Status.INTERNAL_ERROR, "", "");
        }
    }

    private String normalizePath(String path) {

        return path.replaceAll("^/+", "");
    }

    public String getUrlFor(String filePath) {

        return String.format(Locale.ENGLISH, "http://localhost:%d/%s", getListeningPort(), filePath);
    }
}

Использование во фрагменте

if (fileToPlay != null) {

    mTextureView = (TextureView) inflatedView.findViewById(R.id.backgroundAnimation);
    mTextureView.setSurfaceTextureListener(new VideoTextureListener());
}

...

private final class VideoTextureListener implements SurfaceTextureListener {
    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {

        mMediaPlayer.release();
        mMediaPlayer = null;
        return true;
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {

        Surface s = new Surface(surface);

        try {

            NanoServer server =
                    (NanoServer) getActivity().getApplication().getSystemService(NanoServer.SYSTEM_SERVICE_NAME);
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setSurface(s);
            mMediaPlayer.setDataSource(server.getUrlFor(mHotspotTemplate.animation));
            mMediaPlayer.prepareAsync();
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            mMediaPlayer.setVolume(0, 0);

            mMediaPlayer.setOnErrorListener(new OnErrorListener() {

                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {

                    mMediaPlayer.release();
                    mTextureView.setVisibility(View.INVISIBLE);
                    return true;
                }
            });
            mMediaPlayer.setOnPreparedListener(new OnPreparedListener() {

                @Override
                public void onPrepared(MediaPlayer mp) {

                    mMediaPlayer.start();
                }
            });

            mMediaPlayer.setLooping(true);
            mMediaPlayer.setVolume(0, 0);
        } catch (Throwable e) {

            if (mMediaPlayer != null) {

                mMediaPlayer.release();
            }

            mTextureView.setVisibility(View.INVISIBLE);
        }
    }
}
person AngraX    schedule 21.02.2014