我正在构建一个记录和播放视频的应用。我想在不影响背景音乐播放的情况下这样做,即如果我开始播放视频,我不想暂停其他应用程序'音频。但是,在Lollipop上,当调用私有方法VideoView.openVideo()
时,Android的VideoView类会自动请求音频焦点:
AudioManager am = (AudioManager) super.getSystemService(name);
am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
有关如何解决此问题的任何建议?
答案 0 :(得分:2)
我通过复制Lollipop android.widget.VideoView
的完整源代码并删除你提到的行来解决这个问题。
制作自己的VideoView
课程。不要使用extends VideoView
,因为您无法覆盖openVideo()
方法。
我不建议这样做,因为我认为这是一个临时解决方案。 VideoView
在4.1-5.0之间改变很多,所以这可以在除Lollipop之外的Android版本上进行RuntimeException
修改强>
当pinxue告诉我们时,我接近了MediaPlayer + SurfaceView;
它尊重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");
}
}
答案 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的参数