Disable Android's VideoView requestAudioFocus when playing a video?

断了今生、忘了曾经 提交于 2019-12-21 03:36:07

问题


I'm building an app that records and plays back video. I would like to do so without affecting background music playback, i.e. if I begin playing a video, I do not want to pause other apps' audio. However, on Lollipop, Android's VideoView class automatically requests audio focus when the private method VideoView.openVideo() is called:

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

Any suggestions on how to get around this?


回答1:


Starting with Android SDK 26 you may want to use VideoView and

if(Build.VERSION.SDK_INT >= Build.Version_CODES.O){
     //set this BEFORE start playback
     videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE)
}

For older version, there's a workaround described here: https://stackoverflow.com/a/31569930/993439

Basically, copy source code of VideoView and uncomment following lines

AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);



回答2:


I got around this with a stupid solution by copying whole source code of android.widget.VideoView of Lollipop and removing line you mentioned.

Make your own VideoView class. don't use extends VideoView since you can't override openVideo() method.

I don't recommend this as I thinking it's a temporary solution. VideoView Changed a lot between 4.1-5.0 so this can make RuntimeException on Android version other than Lollipop

Edit

I made approach MediaPlayer + SurfaceView as pinxue told us; It respects aspect ratio within viewWidth and 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");
                }
            }



回答3:


You may use MediaPlayer + SurfaceView instead.




回答4:


The accepted solution does not guarantee compatibility across all Android versions and is a dirty hack more than a true solution. I've tried all forms of hacks to get this working, yet none have worked to my satisfaction.

I have come up with a much better solution though - switch from a VideoView to a TextureView and load it with a MediaPlayer. There is no difference from the user's perspective, just no more audio stoppage.

Here's my use case for playing an MP4 looping:

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;
    }
}



回答5:


use audioManager.abandonAudioFocus(null)

If you look at the VideoView code you will notice it calls the method audioManager.requestAudioFocus with null for the OnAudioFocusChangeListener. When you register a listener with the AudioManager it uses this method to make an ID for the listener

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

which generates the same ID every time you use null. So if you call abandonAudioFocus with null it will remove any listener that was added with null as the parameter for the OnAudioFocusChangeListener



来源:https://stackoverflow.com/questions/28119955/disable-androids-videoview-requestaudiofocus-when-playing-a-video

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!