zoukankan      html  css  js  c++  java
  • Android 音视频深入 三 MP4解码播放视频 (附源码下载)

    本篇项目地址,名字是媒体解码MediaCodec,MediaExtractor,求star
    https://github.com/979451341/Audio-and-video-learning-materials

    这次要用到新的东西SurfaceView、MediaCodec、MediaExtractor、MediaFormat


    1.文字说明


    SurfaceView:一个View,用来显示视频的,使用的时候代码都比较简单就不多说了

    MediaCodec:访问底层媒体编码,能够完成媒体编码和解码
    编码是将源对象内容按照一种标准转换为一种标准格式内容。
    解码是和编码对应的,它使用和编码相同的标准将编码内容还原为最初的对象内容。

    MediaExtractor:负责将指定类型的媒体文件从文件中找到轨道,并填充到MediaCodec的缓冲区中

    MediaFormat:封装描述媒体数据格式的信息,无论是音频还是视频。媒体数据的格式被指定为字符串/值对。所有格式通用的键,所有未标记为可选的键都是必需的:
    名称     值类型     描述
    KEY_MIME     串     格式的类型。
    KEY_MAX_INPUT_SIZE     整数     可选,输入数据缓冲区的最大大小
    KEY_BIT_RATE     整数     仅编码器,所需比特率(以比特/秒为单位)

    2.视频播放顺序


    (1)开启两个线程分别处理MP4的音频和视频

    我先说一下视频的处理

    使用MediaExtractor提取资源,选择频道

                MediaExtractor videoExtractor = new MediaExtractor();
                MediaCodec videoCodec = null;
                try {
                    videoExtractor.setDataSource(filePath);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                int videoTrackIndex;
                //获取视频所在轨道
                videoTrackIndex = getMediaTrackIndex(videoExtractor, "video/");
    videoExtractor.selectTrack(videoTrackIndex);

    设置解码配置,并给MediaCodec配置,而且将SurfaceView于MediaCodec相关联,设置为在这个SurfaceView上显示

                    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();
                    }

    循环从MediaExtractor取数据放入MediaCodec,同时MediaCodec返回数据,表示视频播放状态,然后对应转台做不同的处理,

                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

    然后是音频,很相似
    MediaExtractor获取资源,选择频道,得到MediaFormat,获取一些参数来配置AudioTrack,然后就不说了,上一篇博客说了如何播放音频,

                MediaExtractor audioExtractor = new MediaExtractor();
                try {
                    audioExtractor.setDataSource(filePath);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                   MediaFormat mediaFormat = audioExtractor.getTrackFormat(i);
                    String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
                    if (mime.startsWith("audio/")) {
                        audioExtractor.selectTrack(i);
    
                        int audioChannels = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                        int audioSampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                        int minBufferSize = AudioTrack.getMinBufferSize(audioSampleRate,
                                (audioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO),
                                AudioFormat.ENCODING_PCM_16BIT);
                        int maxInputSize = mediaFormat.getInteger(MediaFormat.KEY_MAX_INPUT_SIZE);
    
    
                            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                                audioSampleRate,
                                (audioChannels == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO),
                                AudioFormat.ENCODING_PCM_16BIT,
                                audioInputBufferSize,
                                AudioTrack.MODE_STREAM);


    其实这做的不太好,因为音频和视频没有做协调,之前可能不同步

    完整代码下载地址在文章头部

    参考文章:
    http://blog.csdn.net/qq_36467463/article/details/77990089
    https://www.cnblogs.com/jiy-for-you/p/7282033.html
    https://www.jianshu.com/p/6df2ab17651a

  • 相关阅读:
    Echarts 实现中国地图并轮播指定的地区?
    Linux安装Zookeeper
    初遇携程apollo配置中心
    eclipse安装lombok
    依赖layui form模块 复选框tree插件(拓展可根据属性单选还是多选,数据反选)
    centos7环境下mysql5.7的安装与配置
    中文算数验证码(加减乘除)
    获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址;
    jdk自带的MD5进行数据的加密与解密
    MyBatis-plus 代码生成器
  • 原文地址:https://www.cnblogs.com/jianpanwuzhe/p/8409062.html
Copyright © 2011-2022 走看看