Android提供了MediaPlayer播放器播放媒体文件,其实MediaPlyer只是对Android Media包下的MediaCodec和MediaExtractor进行了包装,方便使用。但是最好理解下Android媒体文件的解码,编码和渲染流程。

Shape Of My Heart.mp4

使用android.media包下的MediaCodec和MediaExtractor实现一个简单的视频解码渲染。

使用到了:

  • MediaCodec:负责媒体文件的编码和解码工作,内部方法均为native
  • MediaExtractor:负责将指定类型的媒体文件从文件中找到轨道,并填充到MediaCodec的缓冲区中
  • AudioTrack:负责将解码之后的音频播放
  • SurfaceView:展示解码之后的视频

视频被播放主要分为以下步骤:

  1. 将资源加载到extractor
  2. 获取视频所在轨道
  3. 设置extractor选中视频所在轨道
  4. 创将解码视频的MediaCodec,decoder
  5. 开始循环,直到视频资源的末尾
  6. 将extractor中资源以一个单位填充进decoder的输入缓冲区
  7. decoder将解码之后的视频填充到输出缓冲区
  8. decoder释放输出缓冲区的同时,将缓冲区中数据渲染到surface

音频的播放类似,只多了AudioTrack部分,少了渲染到surface部分。

MediaCodec.releaseOutputBuffer(int outputBufferIndex, boolean render);

  • render为true就会渲染到surface

播放的控制,视频和音频各自拥有一个Thread。

    public void play() {        isPlaying = true;        if (videoThread == null) {            videoThread = new VideoThread();            videoThread.start();        }        if (audioThread == null) {            audioThread = new AudioThread();            audioThread.start();        }    }    public void stop() {        isPlaying = false;    }

VideoThread

private class VideoThread extends Thread {        @Override        public void run() {            MediaExtractor videoExtractor = new MediaExtractor();            MediaCodec videoCodec = null;            try {                videoExtractor.setDataSource(filePath);            } catch (IOException e) {                e.printStackTrace();            }            int videoTrackIndex;            //获取视频所在轨道            videoTrackIndex = getMediaTrackIndex(videoExtractor, "video/");            if (videoTrackIndex >= 0) {                MediaFormat mediaFormat = videoExtractor.getTrackFormat(videoTrackIndex);                int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);                int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);                //视频长度:秒                float time = mediaFormat.getLong(MediaFormat.KEY_DURATION) / 1000000;                callBack.videoAspect(width, height, time);                videoExtractor.selectTrack(videoTrackIndex);                try {                    videoCodec = MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));                    videoCodec.configure(mediaFormat, surface, null, 0);                } catch (IOException e) {                    e.printStackTrace();                }            }            if (videoCodec == null) {                Log.v(TAG, "MediaCodec null");                return;            }            videoCodec.start();            MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();            ByteBuffer[] inputBuffers = videoCodec.getInputBuffers();//            ByteBuffer[] outputBuffers = videoCodec.getOutputBuffers();            boolean isVideoEOS = false;            long startMs = System.currentTimeMillis();            while (!Thread.interrupted()) {                if (!isPlaying) {                    continue;                }                //将资源传递到解码器                if (!isVideoEOS) {                    isVideoEOS = putBufferToCoder(videoExtractor, videoCodec, inputBuffers);                }                int outputBufferIndex = videoCodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_US);                switch (outputBufferIndex) {                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:                        Log.v(TAG, "format changed");                        break;                    case MediaCodec.INFO_TRY_AGAIN_LATER:                        Log.v(TAG, "解码当前帧超时");                        break;                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:                        //outputBuffers = videoCodec.getOutputBuffers();                        Log.v(TAG, "output buffers changed");                        break;                    default:                        //直接渲染到Surface时使用不到outputBuffer                        //ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];                        //延时操作                        //如果缓冲区里的可展示时间>当前视频播放的进度,就休眠一下                        sleepRender(videoBufferInfo, startMs);                        //渲染                        videoCodec.releaseOutputBuffer(outputBufferIndex, true);                        break;                }                if ((videoBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {                    Log.v(TAG, "buffer stream end");                    break;                }            }//end while            videoCodec.stop();            videoCodec.release();            videoExtractor.release();        }    }

获取指定类型媒体文件所在轨道

    //获取指定类型媒体文件所在轨道    private int getMediaTrackIndex(MediaExtractor videoExtractor, String MEDIA_TYPE) {        int trackIndex = -1;        for (int i = 0; i < videoExtractor.getTrackCount(); i++) {            //获取视频所在轨道            MediaFormat mediaFormat = videoExtractor.getTrackFormat(i);            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);            if (mime.startsWith(MEDIA_TYPE)) {                trackIndex = i;                break;            }        }        return trackIndex;    }

将缓冲区传递至解码器

    //将缓冲区传递至解码器    private boolean putBufferToCoder(MediaExtractor extractor, MediaCodec decoder, ByteBuffer[] inputBuffers) {        boolean isMediaEOS = false;        int inputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_US);        if (inputBufferIndex >= 0) {            ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];            int sampleSize = extractor.readSampleData(inputBuffer, 0);            if (sampleSize < 0) {                decoder.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);                isMediaEOS = true;                Log.v(TAG, "media eos");            } else {                decoder.queueInputBuffer(inputBufferIndex, 0, sampleSize, extractor.getSampleTime(), 0);                extractor.advance();            }        }        return isMediaEOS;    }

音频的部分类似,完整源码请移步jiyangg/MediaPlaySimpleDemo

更多相关文章

  1. 第17天android:《android从零开始》视频(1-5)
  2. Android中OpenMax的适配层
  3. android下的多媒体开源项目
  4. 【Android】如何用MediaPlayer实现一个简单的音视频播放器
  5. android音频、视频、拍照基础操作
  6. android视频截图
  7. android获取本地视频路径
  8. android视频录制(调用系统视频录制),生成缩略图
  9. android 音视频录制

随机推荐

  1. android水平循环滚动控件
  2. 该学什么好呢
  3. Android关于桌面快捷方式工具类!
  4. android显示gif图片
  5. Surface Flinger boot flow in Android s
  6. android获得手机cpu型号
  7. Android(安卓)Questions (问题集锦)
  8. android sqlite lock
  9. android折叠展开列表动态修改显示测试
  10. Android实现手机定位的案例代码