最近遇到一个异屏双显,一个屏幕上使用videoview播放视频,另外一个屏幕使用投屏,投屏的声音被抢占,导致投屏无声音,试了几个框架,不理想,预置自己改了一份源码

直接修改videoview源码,去掉了系统依赖,可以在任何项目中运行

/** Copyright (C) 2006 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.media.AudioManager;
import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.net.Uri;
import android.os.Build;
import android.os.Looper;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.MediaController.MediaPlayerControl;import com.google.android.exoplayer2.metadata.Metadata;import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Vector;/*** Displays a video file.  The VideoView class* can load images from various sources (such as resources or content* providers), takes care of computing its measurement from the video so that* it can be used in any layout manager, and provides various display options* such as scaling and tinting.<p>** <em>Note: VideoView does not retain its full state when going into the* background.</em>  In particular, it does not restore the current play state,* play position, selected tracks, or any subtitle tracks added via* {@link # addSubtitleSource()}.  Applications should* save and restore these on their own in* Also note that the audio session id (from {@link #getAudioSessionId}) may* change from its previously returned value when the VideoView is restored.*/
public class VideoView extends SurfaceView {private static final String TAG = "VideoView";// all possible internal statesprivate static final int STATE_ERROR = -1;private static final int STATE_IDLE = 0;private static final int STATE_PREPARING = 1;private static final int STATE_PREPARED = 2;private static final int STATE_PLAYING = 3;private static final int STATE_PAUSED = 4;private static final int STATE_PLAYBACK_COMPLETED = 5;private final Vector<Pair<InputStream, MediaFormat>> mPendingSubtitleTracks = new Vector<>();// settable by the clientprivate Uri mUri;private Map<String, String> mHeaders;// mCurrentState is a VideoView object's current state.// mTargetState is the state that a method caller intends to reach.// For instance, regardless the VideoView object's current state,// calling pause() intends to bring the object to a target state// of STATE_PAUSED.private int mCurrentState = STATE_IDLE;private int mTargetState = STATE_IDLE;// All the stuff we need for playing and showing a videoprivate SurfaceHolder mSurfaceHolder = null;private MediaPlayer mMediaPlayer = null;private int mAudioSession;private int mVideoWidth;private int mVideoHeight;private int mSurfaceWidth;private int mSurfaceHeight;private OnCompletionListener mOnCompletionListener;private MediaPlayer.OnPreparedListener mOnPreparedListener;private int mCurrentBufferPercentage;private OnErrorListener mOnErrorListener;private OnInfoListener mOnInfoListener;private int mSeekWhenPrepared;  // recording the seek position while preparingprivate boolean mCanPause;private boolean mCanSeekBack;private boolean mCanSeekForward;public VideoView(Context context) {this(context, null);}public VideoView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public VideoView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mVideoWidth = 0;mVideoHeight = 0;getHolder().addCallback(mSHCallback);getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);setFocusable(true);setFocusableInTouchMode(true);requestFocus();mCurrentState = STATE_IDLE;mTargetState = STATE_IDLE;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//Log.i("@@@@", "onMeasure(" + MeasureSpec.toString(widthMeasureSpec) + ", "//        + MeasureSpec.toString(heightMeasureSpec) + ")");int width = getDefaultSize(mVideoWidth, widthMeasureSpec);int height = getDefaultSize(mVideoHeight, heightMeasureSpec);if (mVideoWidth > 0 && mVideoHeight > 0) {int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);if (widthSpecMode == MeasureSpec.EXACTLY && heightSpecMode == MeasureSpec.EXACTLY) {// the size is fixedwidth = widthSpecSize;height = heightSpecSize;// for compatibility, we adjust size based on aspect ratioif (mVideoWidth * height < width * mVideoHeight) {//Log.i("@@@", "image too wide, correcting");width = height * mVideoWidth / mVideoHeight;} else if (mVideoWidth * height > width * mVideoHeight) {//Log.i("@@@", "image too tall, correcting");height = width * mVideoHeight / mVideoWidth;}} else if (widthSpecMode == MeasureSpec.EXACTLY) {// only the width is fixed, adjust the height to match aspect ratio if possiblewidth = widthSpecSize;height = width * mVideoHeight / mVideoWidth;if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {// couldn't match aspect ratio within the constraintsheight = heightSpecSize;}} else if (heightSpecMode == MeasureSpec.EXACTLY) {// only the height is fixed, adjust the width to match aspect ratio if possibleheight = heightSpecSize;width = height * mVideoWidth / mVideoHeight;if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {// couldn't match aspect ratio within the constraintswidth = widthSpecSize;}} else {// neither the width nor the height are fixed, try to use actual video sizewidth = mVideoWidth;height = mVideoHeight;if (heightSpecMode == MeasureSpec.AT_MOST && height > heightSpecSize) {// too tall, decrease both width and heightheight = heightSpecSize;width = height * mVideoWidth / mVideoHeight;}if (widthSpecMode == MeasureSpec.AT_MOST && width > widthSpecSize) {// too wide, decrease both width and heightwidth = widthSpecSize;height = width * mVideoHeight / mVideoWidth;}}} else {// no size yet, just adopt the given spec sizes}setMeasuredDimension(width, height);}@Overridepublic CharSequence getAccessibilityClassName() {return VideoView.class.getName();}public int resolveAdjustedSize(int desiredSize, int measureSpec) {return getDefaultSize(desiredSize, measureSpec);}/*** Sets video path.** @param path the path of the video.*/public void setVideoPath(String path) {setVideoURI(Uri.parse(path));}/*** Sets video URI.** @param uri the URI of the video.*/public void setVideoURI(Uri uri) {setVideoURI(uri, null);}/*** Sets video URI using specific headers.** @param uri     the URI of the video.* @param headers the headers for the URI request.*                Note that the cross domain redirection is allowed by default, but that can be*                changed with key/value pairs through the headers parameter with*                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value*                to disallow or allow cross domain redirection.*/public void setVideoURI(Uri uri, Map<String, String> headers) {mUri = uri;mHeaders = headers;mSeekWhenPrepared = 0;openVideo();requestLayout();invalidate();}public void stopPlayback() {if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();mMediaPlayer = null;mCurrentState = STATE_IDLE;mTargetState = STATE_IDLE;AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);am.abandonAudioFocus(null);}}private void openVideo() {if (mUri == null || mSurfaceHolder == null) {// not ready for playback just yet, will try again laterreturn;}// we shouldn't clear the target state, because somebody might have// called start() previouslyrelease(false);AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);try {mMediaPlayer = new MediaPlayer();// TODO: create SubtitleController in MediaPlayer, but we need// a context for the subtitle renderersfinal Context context = getContext();if (mAudioSession != 0) {mMediaPlayer.setAudioSessionId(mAudioSession);} else {mAudioSession = mMediaPlayer.getAudioSessionId();}mMediaPlayer.setOnPreparedListener(mPreparedListener);mMediaPlayer.setOnVideoSizeChangedListener(mSizeChangedListener);mMediaPlayer.setOnCompletionListener(mCompletionListener);mMediaPlayer.setOnErrorListener(mErrorListener);mMediaPlayer.setOnInfoListener(mInfoListener);mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener);mCurrentBufferPercentage = 0;mMediaPlayer.setDataSource(getContext(), mUri, mHeaders);mMediaPlayer.setDisplay(mSurfaceHolder);mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);mMediaPlayer.setScreenOnWhilePlaying(true);mMediaPlayer.prepareAsync();// we don't set the target state here either, but preserve the// target state that was there before.mCurrentState = STATE_PREPARING;} catch (IOException ex) {Log.w(TAG, "Unable to open content: " + mUri, ex);mCurrentState = STATE_ERROR;mTargetState = STATE_ERROR;mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);return;} catch (IllegalArgumentException ex) {Log.w(TAG, "Unable to open content: " + mUri, ex);mCurrentState = STATE_ERROR;mTargetState = STATE_ERROR;mErrorListener.onError(mMediaPlayer, MediaPlayer.MEDIA_ERROR_UNKNOWN, 0);return;} finally {mPendingSubtitleTracks.clear();}}MediaPlayer.OnVideoSizeChangedListener mSizeChangedListener =new MediaPlayer.OnVideoSizeChangedListener() {public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {mVideoWidth = mp.getVideoWidth();mVideoHeight = mp.getVideoHeight();if (mVideoWidth != 0 && mVideoHeight != 0) {getHolder().setFixedSize(mVideoWidth, mVideoHeight);requestLayout();}}};MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {public void onPrepared(MediaPlayer mp) {mCurrentState = STATE_PREPARED;if (mOnPreparedListener != null) {mOnPreparedListener.onPrepared(mMediaPlayer);}mVideoWidth = mp.getVideoWidth();mVideoHeight = mp.getVideoHeight();int seekToPosition = mSeekWhenPrepared;  // mSeekWhenPrepared may be changed after seekTo() callif (seekToPosition != 0) {seekTo(seekToPosition);}if (mVideoWidth != 0 && mVideoHeight != 0) {//Log.i("@@@@", "video size: " + mVideoWidth +"/"+ mVideoHeight);getHolder().setFixedSize(mVideoWidth, mVideoHeight);if (mSurfaceWidth == mVideoWidth && mSurfaceHeight == mVideoHeight) {// We didn't actually change the size (it was already at the size// we need), so we won't get a "surface changed" callback, so// start the video here instead of in the callback.if (mTargetState == STATE_PLAYING) {start();} else if (!isPlaying() &&(seekToPosition != 0 || getCurrentPosition() > 0)) {}}} else {// We don't know the video size yet, but should start anyway.// The video size might be reported to us later.if (mTargetState == STATE_PLAYING) {start();}}}};private MediaPlayer.OnCompletionListener mCompletionListener =new MediaPlayer.OnCompletionListener() {public void onCompletion(MediaPlayer mp) {mCurrentState = STATE_PLAYBACK_COMPLETED;mTargetState = STATE_PLAYBACK_COMPLETED;if (mOnCompletionListener != null) {mOnCompletionListener.onCompletion(mMediaPlayer);}}};private MediaPlayer.OnInfoListener mInfoListener =new MediaPlayer.OnInfoListener() {public boolean onInfo(MediaPlayer mp, int arg1, int arg2) {if (mOnInfoListener != null) {mOnInfoListener.onInfo(mp, arg1, arg2);}return true;}};private MediaPlayer.OnErrorListener mErrorListener =new MediaPlayer.OnErrorListener() {public boolean onError(MediaPlayer mp, int framework_err, int impl_err) {Log.d(TAG, "Error: " + framework_err + "," + impl_err);mCurrentState = STATE_ERROR;mTargetState = STATE_ERROR;/* If an error handler has been supplied, use it and finish. */if (mOnErrorListener != null) {if (mOnErrorListener.onError(mMediaPlayer, framework_err, impl_err)) {return true;}}return true;}};private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener =new MediaPlayer.OnBufferingUpdateListener() {public void onBufferingUpdate(MediaPlayer mp, int percent) {mCurrentBufferPercentage = percent;}};/*** Register a callback to be invoked when the media file* is loaded and ready to go.** @param l The callback that will be run*/public void setOnPreparedListener(MediaPlayer.OnPreparedListener l) {mOnPreparedListener = l;}/*** Register a callback to be invoked when the end of a media file* has been reached during playback.** @param l The callback that will be run*/public void setOnCompletionListener(OnCompletionListener l) {mOnCompletionListener = l;}/*** Register a callback to be invoked when an error occurs* during playback or setup.  If no listener is specified,* or if the listener returned false, VideoView will inform* the user of any errors.** @param l The callback that will be run*/public void setOnErrorListener(OnErrorListener l) {mOnErrorListener = l;}/*** Register a callback to be invoked when an informational event* occurs during playback or setup.** @param l The callback that will be run*/public void setOnInfoListener(OnInfoListener l) {mOnInfoListener = l;}SurfaceHolder.Callback mSHCallback = new SurfaceHolder.Callback() {public void surfaceChanged(SurfaceHolder holder, int format,int w, int h) {mSurfaceWidth = w;mSurfaceHeight = h;boolean isValidState = (mTargetState == STATE_PLAYING);boolean hasValidSize = (mVideoWidth == w && mVideoHeight == h);if (mMediaPlayer != null && isValidState && hasValidSize) {if (mSeekWhenPrepared != 0) {seekTo(mSeekWhenPrepared);}start();}}public void surfaceCreated(SurfaceHolder holder) {mSurfaceHolder = holder;openVideo();}public void surfaceDestroyed(SurfaceHolder holder) {// after we return from this we can't use the surface any moremSurfaceHolder = null;release(true);}};/** release the media player in any state*/private void release(boolean cleartargetstate) {if (mMediaPlayer != null) {mMediaPlayer.reset();mMediaPlayer.release();mMediaPlayer = null;mPendingSubtitleTracks.clear();mCurrentState = STATE_IDLE;if (cleartargetstate) {mTargetState = STATE_IDLE;}AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);am.abandonAudioFocus(null);}}@Overridepublic boolean onTouchEvent(MotionEvent ev) {return false;}@Overridepublic boolean onTrackballEvent(MotionEvent ev) {return false;}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {boolean isKeyCodeSupported = keyCode != KeyEvent.KEYCODE_BACK &&keyCode != KeyEvent.KEYCODE_VOLUME_UP &&keyCode != KeyEvent.KEYCODE_VOLUME_DOWN &&keyCode != KeyEvent.KEYCODE_VOLUME_MUTE &&keyCode != KeyEvent.KEYCODE_MENU &&keyCode != KeyEvent.KEYCODE_CALL &&keyCode != KeyEvent.KEYCODE_ENDCALL;if (isInPlaybackState() && isKeyCodeSupported) {if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK ||keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {if (mMediaPlayer.isPlaying()) {pause();} else {start();}return true;} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {if (!mMediaPlayer.isPlaying()) {start();}return true;} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP|| keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {if (mMediaPlayer.isPlaying()) {pause();}return true;} else {}}return super.onKeyDown(keyCode, event);}public void start() {if (isInPlaybackState()) {mMediaPlayer.start();mCurrentState = STATE_PLAYING;}mTargetState = STATE_PLAYING;}public void pause() {if (isInPlaybackState()) {if (mMediaPlayer.isPlaying()) {mMediaPlayer.pause();mCurrentState = STATE_PAUSED;}}mTargetState = STATE_PAUSED;}public void suspend() {release(false);}public void resume() {openVideo();}public int getDuration() {if (isInPlaybackState()) {return mMediaPlayer.getDuration();}return -1;}public int getCurrentPosition() {if (isInPlaybackState()) {return mMediaPlayer.getCurrentPosition();}return 0;}public void seekTo(int msec) {if (isInPlaybackState()) {mMediaPlayer.seekTo(msec);mSeekWhenPrepared = 0;} else {mSeekWhenPrepared = msec;}}public boolean isPlaying() {return isInPlaybackState() && mMediaPlayer.isPlaying();}public int getBufferPercentage() {if (mMediaPlayer != null) {return mCurrentBufferPercentage;}return 0;}private boolean isInPlaybackState() {return (mMediaPlayer != null &&mCurrentState != STATE_ERROR &&mCurrentState != STATE_IDLE &&mCurrentState != STATE_PREPARING);}public boolean canPause() {return mCanPause;}public boolean canSeekBackward() {return mCanSeekBack;}public boolean canSeekForward() {return mCanSeekForward;}public int getAudioSessionId() {if (mAudioSession == 0) {MediaPlayer foo = new MediaPlayer();mAudioSession = foo.getAudioSessionId();foo.release();}return mAudioSession;}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);}/*** Forces a measurement and layout pass for all overlaid views.*/private void measureAndLayoutSubtitleWidget() {final int width = getWidth() - getPaddingLeft() - getPaddingRight();final int height = getHeight() - getPaddingTop() - getPaddingBottom();}/*** @hide*/public Looper getSubtitleLooper() {return Looper.getMainLooper();}
}

videoview声音焦点被抢占相关推荐

  1. 车机开发问题: igo导航无audio focus机制导致声音焦点被抢走

    这段时间开发车机,由于机子是要给国外的客户用的,而且指定装igo导航,开发中遇到过一个问题,就是igo导航切到fm或者dvd或者aux时,fm或者dvd有声音而导航无声音,因为mcu开了fm通道或者d ...

  2. Android之使用VideoView组件播放一个简单的视频

    1.在Android开发中,提供了VideoView组件用来播放视频文件.首先,要使用这个组件来播放视频,必须在布局文件下添加VideoView组件,然后再到Activity里获取这个组件,然后调用这 ...

  3. Android 音频焦点处理

    刚开始的时候,认为在智能机中,每个 APP 都是各自管各自的,媒体播放也是这样子的:然而对比同类产品,发现同类产品可以放到播放自如,体验很好,通过对比研究,根源就在于音频焦点处理上. 一.引言 在功能 ...

  4. android 播放声音 停止其他,Android AudioManager处理两个播放器同时有声音,停止其中一个播放的问题,暂停其他的播放...

    尽管某个时刻只有一个 activity 可以运行, Android 却是一个多任务环境.这对使用音频的应用带来了特殊的挑战,因为只有一个音频输出而可能多个媒体都想用它.在 Android2.2 之前, ...

  5. Android使用VideoView播放视频

    1.流程 正常流程如下,但是一般使用MediaController类直接使用. 2.实例 https://www.bilibili.com/video/BV1jW411375J?p=150&s ...

  6. Android蓝牙开发音频焦点

    在车机开发中,蓝牙模块一般是定制的,而蓝牙的音频输出,包括蓝牙电话,蓝牙音乐,都要制定声音策略,进行音频焦点的管理. 音频焦点的管理,这一点类似于android多媒体开发时的音频焦点管理,也是通过Au ...

  7. 高德智慧景区随身听播放器框架设计与实现

    简介:我们开发选型并没有采用传统的TTS技术(由文本内容生成机器语音),而是采用了更加通用音频格式(比如mp3),作为讲解的音频输入源,方便讲解者进行二次创作.本文将简单回顾高德智慧景区随身听播放器的 ...

  8. 9.Android学习之动画与多媒体(二)

    目录 9.动画与多媒体(二) 2.播放音频与视频 2-1.使用MediaPlayer类播放音频 2-2.使用SoundPool类播放音频 2-3.使用 VideoView组件播放视频 4.难点解答 4 ...

  9. Android汽车服务篇(四) CarAudioService

    一.简介 本文将基于CarService中另一个重要的服务CarAudioService以及其对应的CarAudioManager介绍汽车音频的相关内容. 在车载上,音频设备的数量还是使用场景都和手机 ...

最新文章

  1. 初识Redux-Saga
  2. Windows编程-- 用户方式中线程的同步---关键代码段(临界区)
  3. IDEA好用的Servlet模板
  4. iOS之从N个数里面取M个数的组合算法
  5. 牛客练习赛25 因数个数和
  6. WebSocket 的通信机制
  7. Android Studio安装Flutter
  8. mybatis批量删除和插入
  9. 国产代码审计工具Pinpoint介绍
  10. 【案例】中国城市规划设计研究院:新型城镇化监控与评估平台
  11. linux sys文件的创建
  12. win7定时关机命令_只需9步教你轻松设置win7系统定时关机,无需任何工具
  13. 图解三代测序(Nanopore)
  14. 9.使用M4sh编程
  15. Redis中的布隆过滤器与布谷鸟过滤器,你了解多少?
  16. tink.js # pixi辅助插件 — 中文翻译教程
  17. 【迁移学习】特征空间相同、概率分布不同的概念
  18. Cluster04 - Ceph概述 部署Ceph集群 Ceph块存储
  19. 【Java】JRE与JDK
  20. 嘉为蓝鲸助力某通信集团实现多类型应用发布自动化

热门文章

  1. TLS/SSL 协议详解(6) SSL 数字证书的一些细节1
  2. 【转】 iOS开发之打包上传到App Store——(一)各种证书的理解
  3. delphi对应C里面连接类型代码
  4. 一个封装HTTP请求的函数(C++)
  5. 可信计算(Trusted Computing)
  6. 计算机专用单词缩写汇总
  7. CSS 块级元素和行内元素
  8. Java基础 - 面板组件JPanel
  9. Windows中修改Mendeley字体及大小
  10. 如果将公司的服务器托管到IDC机房会怎么样?