《android多媒体api》之MediaPlayer自定义视频播放器

《android多媒体api》系列是整合梳理android开发中经常用到的媒体相关api;多媒体开发主要内容有音频、视频录制播放、摄像头操作、录制操作、流媒体、直播、推流、拉流等方面;最近几年移动直播和视频应用发展犹如雨后春笋一般直插云霄,呃。。好吧这段比喻可以不用看了!!,反正行业兴起肯定催生了很多多媒体相关应用开发程序员。那么怎样才能成为多媒体开发程序员,首先必须要熟练使用和了解android自带的多媒体api,并且还要掌握pcm、yuv、rgb、h264、aac、flv、mpegts、mp4、udp、rtp、rtmp等等众多文件格式和流媒体协议等等。所以这里整理android相关多媒体api,提供给想从事流媒体同学作为参照,同样还是要鸣谢网络上那些具有分享精神大神们!!

####基本概念:

  1. 视频播放:demuxer(解复用)->分离出音频流和视频流->decoder(解码)->播放原始数据(例如:pcm yuv)
  2. 视频录制:采集原始数据(例如:pcm yuv)->encoder(编码)->muxer(封装格式 例如:mp4 3gp)
  3. 流媒体协议:udp、rtp、rtmp、rtcp、rtsp等
  4. 音视频封装格式:mp4 、3gp、flv等
  5. 音视频编码格式:aac、amr、h264、h265等
  6. 原始音视频数据格式:pcm 、yuv、rgb等

流程图:
image

####文章目录:

  1. VideoView 视频播放控件
  2. camera配合surface预览相机画面和拍照
  3. MediaPlayer自定义视频播放器
  4. MediaRecorder音视频录制api
  5. AudioTrack原始音频pcm播放api
  6. AudioRecord原始音频pcm采集api

MediaPlayer是什么?
MediaPlayer类可用于控制音频/视频文件或流的播放。前面介绍了VideoView 跟MediaPlayer用法基本相同,不过MediaPlayer使用时候注意他有个缓存机制,如果没有缓存视频是不能够播放的;这里实现了一个基于MediaPlayer实现自定义视频播放器,具有功能播放、暂停、停止、进度条调整视频播放进度等。案例是通过surfaceview来显示画面,用seekbar来进度显示和控制,然后创建一个timer来一秒中更新一次进度条,同样进度条也可以通过手指抬起时来更新视频进度。

#####效果图:
这里写图片描述

#####MediaPlayer api方法:
方法:create(Context context, Uri uri)
解释:静态方法,通过Uri创建一个多媒体播放器。

方法:create(Context context, int resid)
解释:静态方法,通过资源ID创建一个多媒体播放器

方法:create(Context context, Uri uri, SurfaceHolder holder)
解释:静态方法,通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器

方法: getCurrentPosition()
解释:返回 Int, 得到当前播放位置

方法: getDuration()
解释:返回 Int,得到文件的时间

方法:getVideoHeight()
解释:返回 Int ,得到视频的高度

方法:getVideoWidth()
解释:返回 Int,得到视频的宽度

方法:isLooping()
解释:返回 boolean ,是否循环播放

方法:isPlaying()
解释:返回 boolean,是否正在播放

方法:pause()
解释:无返回值 ,暂停

方法:prepare()
解释:无返回值,准备同步

方法:prepareAsync()
解释:无返回值,准备异步

方法:release()
解释:无返回值,释放 MediaPlayer 对象

方法:reset()
解释:无返回值,重置 MediaPlayer 对象

方法:seekTo(int msec)
解释:无返回值,指定播放的位置(以毫秒为单位的时间)

方法:setAudioStreamType(int streamtype)
解释:无返回值,指定流媒体的类型

方法:setDataSource(String path)
解释:无返回值,设置多媒体数据来源【根据 路径】

方法:setDataSource(FileDescriptor fd, long offset, long length)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】

方法:setDataSource(FileDescriptor fd)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】

方法:setDataSource(Context context, Uri uri)
解释:无返回值,设置多媒体数据来源【根据 Uri】

方法: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()
解释:无返回值,停止播放

####基于MediaPlayer实现自定义播放器:

xml布局文件:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:orientation="horizontal">

            <Button
                android:id="@+id/btnPlayUrl"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="播放网络视频"/>

            <Button
                android:id="@+id/btnPause"
                android:layout_width="80dip"
                android:layout_height="wrap_content"
                android:text="暂停"/>

            <Button
                android:id="@+id/btnStop"
                android:layout_width="80dip"
                android:layout_height="wrap_content"
                android:text="停止"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <SeekBar
                android:id="@+id/skbProgress"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:layout_weight="1.0"
                android:max="100"
                android:progress="1"
                android:paddingLeft="10dip"
                android:paddingRight="10dip"/>
        </LinearLayout>
    </LinearLayout>
</FrameLayout>

java代码:

package com.jared.helloffmpeg;

import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.SeekBar;

import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

public class RecordMP4Push extends Activity implements SurfaceHolder.Callback, MediaPlayer.OnBufferingUpdateListener {
    private SurfaceView surfaceView;
    private Button btnPause, btnPlayUrl, btnStop;
    private SeekBar skbProgress;
    private MediaPlayer player;
    private boolean isLoad=false;
    private boolean isStop=false;
    private Timer mTimer = new Timer();
    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.record_aac_and_pcm);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

        surfaceView = (SurfaceView) this.findViewById(R.id.surfaceView1);
        btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl);
        btnPause = (Button) this.findViewById(R.id.btnPause);
        btnStop = (Button) this.findViewById(R.id.btnStop);
        skbProgress = (SeekBar) this.findViewById(R.id.skbProgress);

        btnPlayUrl.setOnClickListener(new ClickEvent());
        btnPause.setOnClickListener(new ClickEvent());
        btnStop.setOnClickListener(new ClickEvent());
        skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent());

        surfaceView.getHolder().addCallback(this);



    }

    TimerTask mTimerTask = new TimerTask() {
        @Override
        public void run() {
            if (player == null)
                return;
            if (player.isPlaying() && !skbProgress.isPressed()) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        int position = player.getCurrentPosition();
                        int duration = player.getDuration();

                        if (duration > 0) {
                            long pos = skbProgress.getMax() * position / duration;
                            skbProgress.setProgress((int) pos);
                        }
                    }
                });
            }
        }
    };

    private void initPlayer(SurfaceHolder holder){
        if (player!=null)
            return;
        player=new MediaPlayer();
        player.setDisplay(holder);
        player.setOnBufferingUpdateListener(this);
        player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mediaPlayer) {
                if (mediaPlayer.getVideoWidth()!=0)
                    isLoad=true;
            }
        });
        try {
            player.setDataSource("/storage/emulated/0/other/input.mp4");
            player.prepare();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void freePlayer(){
        if (player==null)
            return;
        if (player.isPlaying()) {
            player.stop();
        }
        //player.reset();//清空所有设置
        player.release();
        player=null;
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        initPlayer(surfaceHolder);
        mTimer.schedule(mTimerTask, 1000, 1000);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        freePlayer();
        mTimer.cancel();
    }

    @Override
    public void onBufferingUpdate(MediaPlayer mediaPlayer, int i) {
        Log.i(getClass().getSimpleName(), "buffer size=="+i);
        skbProgress.setSecondaryProgress(i);
        int currentProgress = skbProgress.getMax() * mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration();
        Log.e(currentProgress + "% play", i + "% buffer");
    }

    class ClickEvent implements OnClickListener {


        @Override
        public void onClick(View arg0) {
            if (!isLoad)
                return;
            if (arg0 == btnPause) {
                if (player.isPlaying())
                    player.pause();
            } else if (arg0 == btnPlayUrl) {
                if (!player.isPlaying())
                {
                    if (isStop)
                    {
                        player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                            @Override
                            public void onPrepared(MediaPlayer mediaPlayer) {
                                if (mediaPlayer.getVideoWidth()==0)
                                    return;
                                mediaPlayer.start();
                                isStop=false;
                            }
                        });
                        try {
                            player.prepare();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }else {
                        player.start();
                    }
                }
            } else if (arg0 == btnStop) {
                if (player.isPlaying()) {
                    player.stop();
                    skbProgress.setProgress(0);
                    isStop=true;
                }
            }

        }
    }

    class SeekBarChangeEvent implements SeekBar.OnSeekBarChangeListener {
        int progress;

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress,
                                      boolean fromUser) {
            // 原本是(progress/seekBar.getMax())*player.mediaPlayer.getDuration()
            this.progress = progress * player.getDuration() / seekBar.getMax();
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字
            player.seekTo(progress);
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值