10

In one screen I have a looping background video (upon which normal UI elements are shown), implemented with VideoView like this:

String resPath = "android.resource://" + getPackageName() + "/" + R.raw.bg_video;
videoView.setVideoURI(Uri.parse(resPath));

// Hide controls:
MediaController controller = new MediaController(getActivity());
controller.setVisibility(View.GONE);
videoView.setMediaController(controller);

// Use looping:
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.setLooping(true);
    }
});

videoView.start();

The video works great. The only problem is that when this view is shown (or even before if using e.g. ViewPager), music playback from other applications stops.

The video I'm using has no sound itself. Is there a way to set the VideoView, or the MediaPlayer it uses, in a "muted mode"? I.e., tell it not to do audio at all, and especially not to mess with other apps' audio.

Tried mp.setVolume(0, 0); in onPrepared() but it has no effect.

Jonik
  • 80,077
  • 70
  • 264
  • 372
  • I haven't tried it, but I believe your answer is going to involve going through the [`AudioManager`](http://developer.android.com/reference/android/media/AudioManager.html#setStreamSolo(int,%20boolean)) and playing with mute/solo settings for the stream. Because it is music from other apps you want to keep playing, you will need to set the audio stream for your [`MediaPlayer`](http://developer.android.com/reference/android/media/MediaPlayer.html#setAudioStreamType(int)) to a different value before calling `prepare()`. – Dave Mar 29 '15 at 14:32
  • @ab.helly: Yep, I'll post an answer when I have time. – Jonik Jul 26 '15 at 19:03
  • @Jonik, can you post the answer? – Ana Llera Apr 18 '16 at 08:25
  • @Ana: Actually what I ended up going was essentially the same as what [Łukasz posted](http://stackoverflow.com/a/31569930/56285). As ugly as it feels, copy-paste `VideoView` from Android sources and comment out `requestAudioFocus` call. – Jonik Apr 18 '16 at 09:00

4 Answers4

15

Copy paste VideoView from Android sources and comment out these lines:

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

You could name the new class MutedVideoView, for example.

gist here: https://gist.github.com/vishna/7e9d3466bced8502fcdd

Jonik
  • 80,077
  • 70
  • 264
  • 372
  • How do I compile the copied VideoView class. it complains 'cannot resolve symbol' on: import android.media.Metadata; import android.media.SubtitleController; import android.media.SubtitleTrack.RenderingWidget; import android.media.TtmlRenderer; import android.media.WebVttRenderer; – mik Jan 02 '16 at 18:57
  • The solution I went with (back in 2015) was essentially the same as this. "Ugly but it works." In the `VideoView` copy-pasted from Android sources, I also had to comment out some Subtitle and Metadata related code that wasn't compiling for some reason. – Jonik Apr 18 '16 at 09:01
  • 1
    Is there a way to accomplish this with reflection? – clocksmith Sep 11 '16 at 23:30
10

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;
    }
}
Cord Rehn
  • 1,119
  • 14
  • 22
5

This worked for me:

videoView.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE);

(before .setVideUri() call)

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
0

There's a solution for API level 26 and further. This is Kotlin way of keep video mute.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            binding.viewView2.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE)
        }