请教一下 MediaCodec 解码视频流问题

2020-06-02 11:44:52 +08:00
 CDCK

需求是一直接受后台返回的视频流数据,使用 MediaCodec 解码视频 SurfaceView 播放。后台数据只有宽、高、mimeType 、时间戳 pts 、数据包;我该怎么配置 MediaFormat ?当前遇到的问题是有一台定制平板什么视频都播放不了,dequeueOutputBuffer 一直返回的是-1,其它的设备都是能够正常播放;麻烦各位帮忙看看。

代码:

收到后台数据

                Object[] objects = message.getObjects();
                int res = (int) objects[0];
                int codecid = (int) objects[1];
                int width = (int) objects[2];
                int height = (int) objects[3];
                byte[] packet = (byte[]) objects[4];
                byte[] codecdata = (byte[]) objects[5];
                long pts = (long) objects[6];
                String mimeType = Macro.getMimeType(codecid);
                int length = 0;
                if (packet != null) {
                    lastPushTime = System.currentTimeMillis();
                    length = packet.length;
                    if (!saveMimeType.equals(mimeType) || initW != width || initH != height || mediaCodec == null) {
                        //有变更则重新初始化 MediaCodec
                        if (mediaCodec != null) {
                            LogUtil.e(TAG, "重新配置 MediaCodec");
                            mediaCodec.stop();
                        }
                        saveMimeType = mimeType;
                        initCodec(width, height, codecdata);
                    }
                }
                mediaCodecDecode(packet, length, pts);

初始化 MediaCodec

    private void initCodec(int w, int h, byte[] codecdata) {
        try {
            mediaCodec = MediaCodec.createDecoderByType(saveMimeType);
            MediaCodecInfo mediaCodecInfo = null;
            mediaCodecInfo = getSupportedMediaCodecInfo(saveMimeType);
            if (mediaCodecInfo == null) {
                mediaCodecInfo = mediaCodec.getCodecInfo();
            }
            /**  宽高要判断是否是解码器所支持的范围  */
            MediaCodecInfo.CodecCapabilities capabilitiesForType = mediaCodecInfo.getCapabilitiesForType(saveMimeType);
            MediaCodecInfo.VideoCapabilities videoCapabilities = capabilitiesForType.getVideoCapabilities();
            Range<Integer> supportedWidths = videoCapabilities.getSupportedWidths();
            Integer lower = supportedWidths.getLower();
            Integer upper = supportedWidths.getUpper();
            Range<Integer> supportedHeights = videoCapabilities.getSupportedHeights();
            Integer lower1 = supportedHeights.getLower();
            Integer upper1 = supportedHeights.getUpper();
            initW = w;
            initH = h;
            if (w > upper) {
                w = upper;
            } else if (w < lower) {
                w = lower;
            }
            if (h > upper1) {
                h = upper1;
            } else if (h < lower1) {
                h = lower1;
            }
            initMediaFormat(w, h, codecdata);
            info = new MediaCodec.BufferInfo();
            try {
                mediaCodec.configure(mediaFormat, surface, null, 0);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
            mediaCodec.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public MediaCodecInfo getSupportedMediaCodecInfo(String mimeType) {
        //=REGULAR_CODECS 常规,=ALL_CODECS 所有
        MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
        MediaCodecInfo[] codecInfos = mediaCodecList.getCodecInfos();
        for (MediaCodecInfo codecInfo : codecInfos) {
            if (!codecInfo.isEncoder()) {
                continue;
            }
            String[] types = codecInfo.getSupportedTypes();
            for (String type : types) {
                if (type.equalsIgnoreCase(mimeType)) {
                    return codecInfo;
                }
            }
        }
        return null;
    }

    private void initMediaFormat(int w, int h, byte[] csddata) {
        mediaFormat = MediaFormat.createVideoFormat(saveMimeType, w, h);
        mediaFormat.setInteger(MediaFormat.KEY_WIDTH, w);
        mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, h);
        mediaFormat.setLong(MediaFormat.KEY_MAX_INPUT_SIZE, w * h);
        if (csddata != null) {
            ByteBuffer csd_0 = ByteBuffer.wrap(csddata);
            ByteBuffer csd_1 = ByteBuffer.wrap(csddata);
            mediaFormat.setByteBuffer("csd-0", csd_0);
            mediaFormat.setByteBuffer("csd-1", csd_1);
        }
        LogUtil.d(TAG, "initMediaFormat :   --> " + mediaFormat);
    }

MediaCodecDecode 方法


    private void mediaCodecDecode(byte[] bytes, int size, long pts) {
        try {
            int inputBufferIndex = mediaCodec.dequeueInputBuffer(0);
            LogUtil.i(TAG, "mediaCodecDecode -->dequeueInputBuffer index= " + inputBufferIndex);
            if (inputBufferIndex >= 0) {
                ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
                byteBuffer.clear();
                byteBuffer.put(bytes);
                mediaCodec.queueInputBuffer(inputBufferIndex, 0, size, pts, 0);
                LogUtil.i(TAG, "mediaCodecDecode dequeueInputBuffer  pts= " + pts + ", index = " + inputBufferIndex + ", inputCount:" + (++inputCount));
            }
        } catch (IllegalStateException e) {
            LogUtil.e(TAG, "mediaCodecDecode dequeueInputBuffer 异常 -->"+e.getMessage());
            //如果解码出错,需要提示用户或者程序自动重新初始化解码
            mediaCodec = null;
            return;
        }
        try {
            //使输出缓冲区出队,最多阻塞“ timeoutUs”微秒。 返回已成功解码的输出缓冲区的索引
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, 0);
            switch (outputBufferIndex) {
                case MediaCodec.INFO_TRY_AGAIN_LATER:
                    LogUtil.d(TAG, "mediaCodecDecode -->dequeueOutputBuffer 无可用输出");
                    break;
                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    MediaFormat outputFormat = mediaCodec.getOutputFormat();
                    LogUtil.e(TAG, "The output format has changed, new format:" + outputFormat);
                    mediaFormat = outputFormat;
                    break;
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    LogUtil.e(TAG, "this event signals that the video scaling mode may have been reset to the default");
                    break;
                default:
                    ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex);
                    //position 和 limit 方法 解决输出混乱问题
                    outputBuffer.position(info.offset);
                    outputBuffer.limit(info.offset + info.size);
                    //如果配置编码器时指定了有效的 surface,传 true 将此输出缓冲区显示在 surface
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
                    break;
            }
        } catch (Exception e) {
            LogUtil.e(TAG, "mediaCodecDecode dequeueOutputBuffer 错误 -->");
            e.printStackTrace();
        }
    }
7331 次点击
所在节点    Android
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/677785

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX