Отключить Android VideoView requestAudioFocus при воспроизведении видео?

Я создаю приложение, которое записывает и воспроизводит видео. Я бы хотел сделать это без влияния на воспроизведение фоновой музыки, то есть, если я начну воспроизводить видео, я не хочу останавливать звук других приложений. Однако на Lollipop класс Android VideoView автоматически запрашивает фокус звука при вызове частного метода VideoView.openVideo():

AudioManager am = (AudioManager) super.getSystemService(name);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);

Любые предложения о том, как обойти это?

Ответ 1

Я обошел это с помощью глупого решения, скопировав весь исходный код android.widget.VideoView Lollipop и удалив указанную линию.

Создайте свой собственный класс VideoView. не используйте extends VideoView, так как вы не можете переопределить метод openVideo().

Я не рекомендую это, поскольку считаю это временным решением. VideoView Изменено много между 4.1-5.0, поэтому это может сделать RuntimeException в версии для Android, отличной от Lollipop

Edit

Я сделал подход к MediaPlayer + SurfaceView, как сказал нам pinxue; Он учитывает соотношение сторон в пределах viewWidth и viewHeight.

            final String finalFilePath = filePath;

            final SurfaceHolder surfaceHolder = sv.getHolder();
            final MediaPlayer mediaPlayer = new MediaPlayer();
            final LinearLayout.LayoutParams svLayoutParams = new LinearLayout.LayoutParams(viewWidth,viewHeight);
            surfaceHolder.addCallback(new SurfaceHolder.Callback(){

                @Override
                public void surfaceCreated(SurfaceHolder holder) {

                    try {
                        if(isDebug) {
                        System.out.println("setting VideoPath to VideoView: "+finalFilePath);
                        }
                        mediaPlayer.setDataSource(finalFilePath);
                    }catch (IOException ioe){
                        if(isDebug){
                            ioe.printStackTrace();
                        }
                        //mediaPlayer = null;
                    }
                    mediaPlayer.setDisplay(surfaceHolder);
                    mediaPlayer.prepareAsync();
                    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                        @Override
                        public void onPrepared(MediaPlayer mp) {
                            if(isDebug){
                                System.out.println("Video is starting...");
                            }

                            // for compatibility, we adjust size based on aspect ratio
                            if ( mp.getVideoWidth() * svLayoutParams.height  < svLayoutParams.width * mp.getVideoHeight() ) {
                                //Log.i("@@@", "image too wide, correcting");
                                svLayoutParams.width = svLayoutParams.height * mp.getVideoWidth() / mp.getVideoHeight();
                            } else if ( mp.getVideoWidth() * svLayoutParams.height  > svLayoutParams.width * mp.getVideoHeight() ) {
                                //Log.i("@@@", "image too tall, correcting");
                                svLayoutParams.height = svLayoutParams.width * mp.getVideoHeight() / mp.getVideoWidth();
                            }
                            sv.post(new Runnable(){
                                    @Override
                                    public void run() {
                                        sv.setLayoutParams(svLayoutParams);
                                    }
                                });


                            mp.start();
                        }
                    });
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    if(isDebug){
                        System.out.println("surfaceChanged(holder, "+format+", "+width+", "+height+")");
                    }
                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                    try {
                        mediaPlayer.setDataSource("");
                    }catch (IOException ioe){
                        if(isDebug){
                            ioe.printStackTrace();
                        }
                    }
                }
            });

            if(sv.post(new Runnable() {
                @Override
                public void run() {

                    sv.setLayoutParams(svLayoutParams);///
                    sv.setVisibility(View.VISIBLE);

                }})){

                if(isDebug) {
                    System.out.println("post Succeded");
                }
            }else{
                if(isDebug) {
                    System.out.println("post Failed");
                }
            }

Ответ 2

Вместо этого вы можете использовать MediaPlayer + SurfaceView.

Ответ 3

Принятое решение не гарантирует совместимость во всех версиях Android и является грязным взломом больше, чем истинное решение. Я пробовал все формы взлома, чтобы получить эту работу, но никто из них не работал на мое удовлетворение.

Я придумал гораздо лучшее решение - переключитесь с VideoView на TextureView и загрузите его с помощью MediaPlayer. Нет никакой разницы с точки зрения пользователя, просто нет остановки звука.

Здесь мой вариант использования для воспроизведения цикла MP4:

private TextureView _introVideoTextureView;
private MediaPlayer _introMediaPlayer;

...

@Override
public void onCreate(...) {
    _introVideoTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
           try {
               destoryIntroVideo();

               _introMediaPlayer = MediaPlayer.create(SignInActivity.this, R.raw.intro_video);
               _introMediaPlayer.setSurface(new Surface(surfaceTexture));
               _introMediaPlayer.setLooping(true);
               _introMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
               _introMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                    @Override
                    public void onPrepared(MediaPlayer mediaPlayer) {
                        mediaPlayer.start();
                    }
                });

            } catch (Exception e) {
                System.err.println("Error playing intro video: " + e.getMessage());
            }
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {}

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
            return false;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}
    });
}

@Override
public void onDestroy() {
    super.onDestroy();

    destoryIntroVideo();
}

private void destoryIntroVideo() {
    if (_introMediaPlayer != null) {
        _introMediaPlayer.stop();
        _introMediaPlayer.release();
        _introMediaPlayer = null;
    }
}

Ответ 4

использовать audioManager.abandonAudioFocus(null)

Если вы посмотрите на код VideoView, вы заметите, что он вызывает метод audioManager.requestAudioFocus с нулевым значением для OnAudioFocusChangeListener. Когда вы регистрируете прослушиватель с помощью AudioManager, он использует этот метод для создания идентификатора для слушателя

private String getIdForAudioFocusListener(OnAudioFocusChangeListener l) {
    if (l == null) {
        return new String(this.toString());
    } else {
        return new String(this.toString() + l.toString());
    }
}

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