我的面试准备Android音视频系列

视频开发(1)- 基础知识点整理

2019-01-04  本文已影响225人  前行的乌龟

话说音视频播放现在真是不搞不行啦,不光我们要从市面上 N 多的播放器中找出好用的,熟悉其 API ,甚至还需要我们能利用开源解码器来编写我们自己的视频播放器,模块,这点在越来越不一样,差异化的播放器中可以看得见,很多 app 不光播放器 UI 不一样,甚至很多逻辑都不同

所以大家努力吧,先不求能搞定 FFMG 编解码等底层技术,至少我们现在得能利用 ijkPlayer 编写自己的视频播放器出来,才能彻底搞得定 app 端的播放需求,要是打算自己上直播推流 sdk ,那么学的就更多了,都是得按年来计算了,音视频水太神了,至少要求我们得搞定 UI 部分才行,在视频这块用别人开源的播放器是走不远的,因为需求差异性太大

录制音视频 AudioRecord/MediaRecord

视频的基础知识点推荐看下面:


视频流媒体协议有哪些


官方原生播放器 MediaPlayer

MediaPlayer 是 Androd 多媒体框架中的一个重要组件,通过该类,我们可以解码和播放音视频,但是 MediaPlayer 本身只支持 音频 播放,需要传入专门的视频承载 view 才能播放视频

MediaPlayer 可以支持三种不同的媒体来源:

MediaPlayer支持两种流媒体协议,HTTP 和 RTSP,这两种协议最大的不同是,RTSP 协议支持实时流媒体的播放,而 HTTP 协议不支持

原生 MediaPalyer 支持的协议和封装格式实在太有限了,如果我们想播放那些它不支持的视频,这时候就需要第三方播放器了,很多第三方播放器的底层实现都是基于 FFmpeg

MediaPlayer 对于视频格式支持的也是非常少,值能支持: MP4,AVI,3DP 这3个早期的手机品是格式

开源的音视频解码器在 API 上都参考了 MediaPlayer ,所以学习 MediaPlayer API 就是我们的第一步

  1. 创建 MediaPlayer 对象,可以直接和本地资源绑定
MediaPlayer mp = new MediaPlayer();
MediaPlayer mp = MediaPlayer.create(this, R.raw.test); //无需再调用setDataSource
create(Context context, Uri uri, SurfaceHolder holder)
  1. 设置播放资源
MediaPlayer.create(this, R.raw.test); // raw下的资源
mp.setDataSource("/sdcard/test.mp3"); // 本地文件路径
mp.setDataSource("http://www.xxx.com/music/test.mp3");// 网络URL文件
  1. 播放资源
// uri 资源
Uri myUri = ....;   /**initialize Uri here*/
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

// 网络文件
String url = "http://........"; // your URL here 
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepareAsync()
mediaPlayer.setOnPreparedListener({
    // 加载完毕再开始播放
    mediaPlayer.start()
})
  1. MediaPlayer 主要 API,需要熟知
getCurrentPosition( ):得到当前的播放位置
getDuration() :得到文件的时间
getVideoHeight() :得到视频高度
getVideoWidth() :得到视频宽度
isLooping():是否循环播放
isPlaying():是否正在播放
pause():暂停
prepare():准备(同步)
prepareAsync():准备(异步)
release():释放MediaPlayer对象
reset():重置MediaPlayer对象
seekTo(int msec):指定播放的位置(以毫秒为单位的时间)
setAudioStreamType(int streamtype):指定流媒体的类型
setDisplay(SurfaceHolder sh):设置用SurfaceHolder来显示多媒体
setLooping(boolean looping):设置是否循环播放
setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener):网络流媒体的缓冲监听
setOnCompletionListener(MediaPlayer.OnCompletionListener listener):网络流媒体播放结束监听
setOnErrorListener(MediaPlayer.OnErrorListener listener):设置错误信息监听
setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener):视频尺寸监听
setScreenOnWhilePlaying(boolean screenOn):设置是否使用SurfaceHolder显示
setVolume(float leftVolume, float rightVolume):设置音量
start():开始播放
stop():停止播放

我们需要熟知下面这张 MediaPalyer 解码器生命周期图,所有的开源项目都是按这个思路来做的


播放器生命周期图

SurfaceView , TextureView

SurfaceView , TextureView 都是 android 中用于承载视频帧显示的 view ,熟悉这2个 view 的大家都知道,这 2个 view 都是异步的,都是再非 UI 线程中计算的,拥有独立 surface 显存的,这是因为视频播放任务太重,UI 线程 hold 不住


视频播放方式一:VideoView + MediaController

这是官方原生的实现,VideoView 继承 SurfaceView ,内部封装了 SurfaceView 的所有操作,MediaController 这样来显示视频控制相关的 view 部分

我个人是非常喜欢官方的 API 设计的,代码功能,层次分离,起名都非常规范,让人一看就懂,用起来也是很爽,奈何就是支持的流媒体格式太少,我们只能去使用开源组件

VideoView + MediaController 的简单使用

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    button = (Button) findViewById(R.id.play);
    videoview = (VideoView) findViewById(R.id.video);

    mMediaController = new MediaController(this);
    videoview.setMediaController(mMediaController);
    mMediaController.setAnchorView(videoview)

    button.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            loadView(url.getText().toString());
        }
    });
}

public void loadView(String path) {
    Uri uri = Uri.parse(path);
    videoview.setVideoURI(uri);
    videoview.start;
    videoview.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
 //         mp.setLooping(true);
            mp.start();// 播放
            Toast.makeText(MainActivity.this, "开始播放!", Toast.LENGTH_LONG).show();
        }
    });
    videoview.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            Toast.makeText(MainActivity.this, "播放完毕", Toast.LENGTH_SHORT).show();
        }
    });
}

我们简单看下 MediaController 是如何管理 视频控制 view 的

MediaController 继承 FrameLayout

public class MediaController extends FrameLayout

在 makeControllerView 中创建 控制布局 view ,并初始化 view

    protected View makeControllerView() {
        LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mRoot = inflate.inflate(com.android.internal.R.layout.media_controller, null);

        initControllerView(mRoot);

        return mRoot;
    }

media_controller 样式


media_controller

在 setAnchorView 关联视频播放器 VideoView 时把 控制 view 添加到自己身上

    public void setAnchorView(View view) {
        if (mAnchor != null) {
            mAnchor.removeOnLayoutChangeListener(mLayoutChangeListener);
        }
        mAnchor = view;
        if (mAnchor != null) {
            mAnchor.addOnLayoutChangeListener(mLayoutChangeListener);
        }

        FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
        );

        removeAllViews();
        View v = makeControllerView();
        addView(v, frameParams);
    }

视频播放方式二:开源解码器 + TextureView

我们注定是不会使用原生 VideoView 去远程视频的,没办法主流的流媒体格式 VideoView 都不支持,我们只能去使用 开源的视频解码器 + TextureView 去自己实现了

这就是我们后面需要自己的搞定的


参考:

上一篇下一篇

猜你喜欢

热点阅读