V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
CDCK
V2EX  ›  Android

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

  •  1
     
  •   CDCK · 2020-06-02 11:44:52 +08:00 · 7329 次点击
    这是一个创建于 1603 天前的主题,其中的信息可能已经有所发展或是发生改变。

    需求是一直接受后台返回的视频流数据,使用 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();
            }
        }
    
    目前尚无回复
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2983 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 13:34 · PVG 21:34 · LAX 06:34 · JFK 09:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.