Android 最简单的播放器之MediaExtractor封装(一)

一、概述

  案例:使用MediaExtractor+MediaFormat加载媒体文件信息。为后面使用MediaCodec硬件解码提供基础封装工具类

二、封装代码

  1.IExtractor接口(定义公共方法)

  

public interface IExtractor {
    /**
     * 获取媒体格式
     *
     * @return
     */
    MediaFormat getFormat();

    /**
     * 读取媒体数据
     *
     * @param byteBuffer 媒体数据(音频或视频、这里仅音频被使用)
     * @return 读取的有效字节数
     */
    int readBuffer(ByteBuffer byteBuffer);

    /**
     * 获取当前帧时间
     * BaseExtractor
     * @return
     */
    long getCurrentTimestamp();

    /**
     * 获取当前帧flag
     *
     * @return
     */
    long getSampleFlag();

    /**
     * Seek到指定位置,并返回实际帧的时间戳
     *
     * @param pos
     * @return
     */
    long seek(long pos);

    /**
     * 设置开始解码时间戳
     *
     * @param pos
     */
    void setStartPos(long pos);

    /**
     * 停止读取数据
     */
    void stop();

}

  

  2.BaseExtractor 解析器基类

public abstract class BaseExtractor implements IExtractor {
    private static final String TAG = "BaseExtractor";
    enum MIME_TYPE {
        MIME_VIDEO("video/"),
        MIME_AUDIO("audio/");

        MIME_TYPE(String video) {
        }
    }

    /**
     * 媒体提取器
     */
    private MediaExtractor mediaExtractor;
    /**
     * 音频轨道
     */
    private int mAudioTrackIndex = -1;
    /**
     * 视频轨道
     */
    private int mVideoTrackIndex = -1;
    /**
     * 当前帧时间戳
     */
    private long mCurSampleTime = 0;
    /**
     * 当前帧标志位
     */
    private int mCurSampleFlag = 0;
    /**
     * 开始解码时间点
     */
    private long mStartDecodeSampleTime = 0;


    public BaseExtractor(@NonNull String mediaPath) {
        //设置数据源
        try {
            mediaExtractor = new MediaExtractor();
            mediaExtractor.setDataSource(mediaPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取视频轨
     *
     * @return
     */
    public MediaFormat getVideoFormat() {
        //获取轨道总数量
        int trackCount = mediaExtractor.getTrackCount();
        MediaFormat mediaFormat = null;
        for (int i = 0; i < trackCount; i++) {
            //获取MediaFormat
            mediaFormat = mediaExtractor.getTrackFormat(i);
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            //如果
            if (mime.startsWith("video/")) {
                mVideoTrackIndex = i;
            }
        }
        if (mVideoTrackIndex >= 0) {
            return mediaFormat;
        }
        Log.e(TAG,"MediaFormat为空:mVideoTrackIndex-->"+mVideoTrackIndex);
        return null;
    }

    /**
     * 获取音频轨
     *
     * @return
     */
    public MediaFormat getAudioFormat() {
        int tackCount = mediaExtractor.getTrackCount();
        MediaFormat mediaFormat = null;
        for (int i = 0; i < tackCount; i++) {
            mediaFormat = mediaExtractor.getTrackFormat(i);
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio/")) {
                mAudioTrackIndex = i;
            }
        }
        if (mAudioTrackIndex >= 0) {
            return mediaFormat;
        }
        return null;
    }

    /**
     * 读取媒体数据
     *
     * @param byteBuffer
     * @return
     */
    public int readBuffer(ByteBuffer byteBuffer) {
        byteBuffer.clear();
        selectTrack();
        int readSampleCount = mediaExtractor.readSampleData(byteBuffer, 0);
        if (readSampleCount < 0) {
            return -1;
        }
        //记录当前帧时间戳
        mCurSampleTime = mediaExtractor.getSampleTime();
        //记录当前帧标志位
        mCurSampleFlag = mediaExtractor.getSampleFlags();
        //进入下一帧
        mediaExtractor.advance();
        return readSampleCount;

    }

    /**
     * 选择媒体轨道
     */
    private void selectTrack() {
        if (mVideoTrackIndex >= 0) {
            mediaExtractor.selectTrack(mVideoTrackIndex);
        } else if (mAudioTrackIndex >= 0) {
            mediaExtractor.selectTrack(mAudioTrackIndex);
        }
    }

    /**
     * seek到指定的位置,并返回实际时间戳
     *
     * @param pos seek到的位置
     * @return
     */
    public long seek(long pos) {
        mediaExtractor.seekTo(pos, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
        return mediaExtractor.getSampleTime();
    }

    /**
     * 停止读取数据
     */
    public void stop() {
        if (mediaExtractor != null) {
            mediaExtractor.release();
            mediaExtractor = null;
        }

    }

    /**
     * 获取视频轨索引
     *
     * @return
     */
    public int getVideoTrackIndex() {
        return mVideoTrackIndex;
    }

    /**
     * 获取音频轨索引
     *
     * @return
     */
    public int getAudioTrackIndex() {
        return mAudioTrackIndex;
    }

    /**
     * 设置开始解码时间戳
     *
     * @param pos
     */
    public void setStartPos(long pos) {
        mStartDecodeSampleTime = pos;
    }

    /**
     * 获取当前帧时间
     *
     * @return
     */
    public long getCurrentTimestamp() {
        return mCurSampleTime;
    }

    /**
     * 获取当前帧标志位
     *
     * @return
     */
    public long getCurrentSampleFlag() {
        return mCurSampleFlag;
    }
}

  

  3.VideoExtractor 视频格式解析器

public class VideoExtractor extends BaseExtractor{

    public VideoExtractor(@NonNull String mediaPath) {
        super(mediaPath);
    }

    @Override
    public MediaFormat getFormat() {
        return getVideoFormat();
    }

    @Override
    public long getSampleFlag() {
        return getCurrentSampleFlag();
    }
}

  

public class AudioExtractor extends BaseExtractor {
    public AudioExtractor(@NonNull String mediaPath) {
        super(mediaPath);
    }

    @Override
    public MediaFormat getFormat() {
        return getAudioFormat();
    }

    @Override
    public long getSampleFlag() {
        return getCurrentSampleFlag();
    }
}

  

  4.AudioExtractor 音频格式解析器

  5.MediaExtractorActivity 音视频格式测试类(主要打印基础信息,有写信息可以打印出啦,有些信息打印不出来)

  

posted on 2021-11-03 15:55  飘杨......  阅读(884)  评论(0)    收藏  举报