播放视频时禁用Android的VideoView requestAudioFocus?

时间:2015-01-23 22:43:57

标签: android android-videoview audio-player

我正在构建一个记录和播放视频的应用。我想在不影响背景音乐播放的情况下这样做,即如果我开始播放视频,我不想暂停其他应用程序'音频。但是,在Lollipop上,当调用私有方法VideoView.openVideo()时,Android的VideoView类会自动请求音频焦点:

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

有关如何解决此问题的任何建议?

5 个答案:

答案 0 :(得分:2)

我通过复制Lollipop android.widget.VideoView的完整源代码并删除你提到的行来解决这个问题。

制作自己的VideoView课程。不要使用extends VideoView,因为您无法覆盖openVideo()方法。

我不建议这样做,因为我认为这是一个临时解决方案。 VideoView在4.1-5.0之间改变很多,所以这可以在除Lollipop之外的Android版本上进行RuntimeException

修改

当pinxue告诉我们时,我接近了MediaPlayer + SurfaceView; 它尊重viewWidthviewHeight中的宽高比。

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

答案 1 :(得分:2)

从Android SDK 26开始,您可能要使用VideoView

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

对于较旧的版本,这里描述了一种解决方法:https://stackoverflow.com/a/31569930/993439

基本上,复制VideoView的源代码,并在下面的行中取消注释

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

答案 2 :(得分:1)

您可以使用MediaPlayer + SurfaceView。

答案 3 :(得分:1)

已接受的解决方案并不能保证所有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 :(得分:0)

使用audioManager.abandonAudioFocus(null)

如果您查看VideoView代码,您会注意到它为OnAudioFocusChangeListener调用了带有null的方法audioManager.requestAudioFocus。当您使用AudioManager注册侦听器时,它使用此方法为侦听器创建ID

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

每次使用null时都会生成相同的ID。因此,如果您使用null调用abandonAudioFocus,它将删除任何添加了null的侦听器作为OnAudioFocusChangeListener的参数