编解码播放大数据量流问题

服务器收到解码的流数据后进行编码再播放,当数据量大的时候,长时间播放总是会导致内存问题,板子会自动重启,请问我代码哪里存在问题?


jni层服务器返回的解码后的数据

				/**
         * 根据资源id存放,jni回调的解码数据
         */
        public static HashMap<Integer, LinkedBlockingQueue<FrameData>> decodeMap = new HashMap<>();
        //对象池
        public static LinkedBlockingQueue<FrameData> frameDataPool = new LinkedBlockingQueue<>(50);


        /**
         * jni层返回解码后的数据
         * @param res     播放资源ID
         * @param codecid 解码器类型 (h264=27,h265=173,mpeg4=12,vp8=139,vp9=167)
         * @param w       视频的宽
         * @param h       视频的高
         * @param packet  解码数据
         */
        public int callback_videodecode(int isKeyframe, int res, int codecid, int w, int h, byte[] packet, long pts, byte[] codecdata) {
            if (packet != null) {
                FrameData frameData = frameDataPool.poll();
                if (frameData == null) {
                    //对象池中没有了就新建一个
                    frameData = new FrameData();
                }
                frameData.setIsKeyFrame(isKeyframe);
                frameData.setRes(res);
                frameData.setCodecid(codecid);
                frameData.setW(w);
                frameData.setH(h);
                frameData.setPts(pts);
                frameData.setBytes(packet);
                frameData.setCodecdata(codecdata);
                LinkedBlockingQueue<FrameData> decodeQueue = decodeMap.get(res);
                decodeQueue.offer(frameData);
            }
            return 0;
        }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.

自定义播放的view

public class PlayerView extends SurfaceView implements SurfaceHolder.Callback {
    private DecodeThread decodeThread;
    private Surface mSurface;
    private int resourceId = 0;
    private final AtomicBoolean mIsRunning = new AtomicBoolean(true);

    public PlayerView(Context context) {
        super(context);
        init();
    }

    public PlayerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public PlayerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        getHolder().addCallback(this);
    }

    public int getResourceId() {
        return resourceId;
    }

    public void setResourceId(int resourceId) {
        this.resourceId = resourceId;
    }

    @Override
    public void surfaceCreated(@NonNull SurfaceHolder holder) {
        mSurface = holder.getSurface();
        startDecode();
    }

    @Override
    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
        stopDecode();
    }

    public void startDecode() {
        if (decodeThread == null) {
            mIsRunning.set(true);
            decodeThread = new DecodeThread("pure-" + resourceId);
            LogUtils.i("---startDecode---" + decodeThread);
            decodeThread.start();
        }
    }

    public void stopDecode() {
        if (decodeThread != null) {
            LogUtils.i("---stopDecode---" + decodeThread);
            mIsRunning.set(false);
            decodeThread.interrupt();
            decodeThread = null;
        }
    }

    class DecodeThread extends Thread {

        private MediaCodec mediaCodec;
        private final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
        private final LinkedBlockingQueue<FrameData> cacheQueue = new LinkedBlockingQueue<>(25);
        private String saveMimeType = "";
        private int initW;
        private int initH;

        public DecodeThread(@NonNull String name) {
            super(name);
        }

        @Override
        public void run() {
            try {
                LinkedBlockingQueue<FrameData> decodeQueue = CallValue.decodeMap.get(resourceId);
                byte[] packet;
                int packetSize;
                int width, height, codecid, isKeyFrame;
                long pts;
                String mimeType;
                FrameData bean;
                int cacheQueueSize, decodeQueueSize;
                while (mIsRunning.get()) {
                    cacheQueueSize = cacheQueue.size();
                    decodeQueueSize = decodeQueue.size();
                    if (mSurface == null) {
                        if (decodeQueueSize > 0) {
                            //回收对象
                            if (!CallValue.frameDataPool.offer(decodeQueue.poll())) {
                                LogUtils.i("---recycle object err---");
                            } else {
                                LogUtils.i("---recycle object---");
                            }
                        }
                        //跳过循环
                        continue;
                    }
                    if (decodeQueueSize > 0 || cacheQueueSize > 0) {
                        //先取缓存的数据
                        bean = cacheQueueSize > 0 ? cacheQueue.peek() : decodeQueue.poll();
                        packet = bean.getBytes();
                        packetSize = bean.getPacketSize();
                        width = bean.getW();
                        height = bean.getH();
                        isKeyFrame = bean.getIsKeyFrame();
                        if (initW != 0 && initW != width) {
                            LogUtils.i("宽度改变:" + width);
                        }
                        if (initH != 0 && initH != height) {
                            LogUtils.i("高度改变:" + height);
                        }
                        pts = bean.getPts();
                        codecid = bean.getCodecid();
                        mimeType = getMimeType(codecid);
                        //已经初始化过了,但是参数有变化的时候需要重新初始化
                        if (!saveMimeType.equals(mimeType) || initW != width || initH != height || mediaCodec == null) {
                            //进入初始化MediaCodec
                            if (mediaCodec != null) {
                                LogUtils.i("重新配置mediaCodec");
                                //调用stop方法使其进入 uninitialzed 状态,这样才可以重新配置MediaCodec
                                mediaCodec.stop();
                            }
                            saveMimeType = mimeType;
                            //初始化MediaCodec
                            initCodec(width, height, bean.getCodecdata());
                        }
                        if (mediaCodec == null) {//初始化失败
                            if (cacheQueueSize > 0) {//使用的是缓存中的数据
                                //把它删掉
                                FrameData poll = cacheQueue.poll();
                                //回收对象,已经满了再调用offer添加数据会返回false
                                CallValue.frameDataPool.offer(poll);
                            } else {//使用的是新数据
                                //放入缓存队列
                                if (!cacheQueue.offer(bean)) {
                                    //已经满了,把旧的删除掉
                                    cacheQueue.poll();
                                    //然后将这个新的放入
                                    cacheQueue.offer(bean);
                                }
                            }
                            return;
                        }
                        if (mediaCodecDecode(packet, pts, packetSize)) {
                            if (cacheQueueSize > 0) {
                                //使用缓存数据,编码成功就丢掉
                                cacheQueue.poll();
                            }
                            //回收对象,已经满了再调用offer添加数据会返回false
                            if (!CallValue.frameDataPool.offer(bean)) {
                                LogUtils.i("---recycle object---");
                                bean = null;
                            }
                        } else {//编码失败
                            if (cacheQueueSize == 0) {//使用的是新数据
                                LogUtils.i("after encoding fails, put it into the cache queue:" + bean.toString());
                                //放入缓存队列
                                if (!cacheQueue.offer(bean)) {
                                    //已经满了,把旧的删除掉
                                    cacheQueue.poll();
                                    //然后将这个新的放入
                                    cacheQueue.offer(bean);
                                }
                            }
                        }
                    } else {
                        if (mediaCodec != null) {
                            //外部没有数据了,还需要渲染已经放入解码器中的数据
                            mediaCodecDecode(null, 0, 0);
                        }
                    }
                }
                cacheQueue.clear();//清空缓存队列
                releaseMediaCodec();
                LogUtils.i("thread id=" + Thread.currentThread().getId() + ",end decoding decodeQueue=" + decodeQueue.size());
                CallValue.frameDataPool.clear();
                decodeQueue.clear();//清空接收队列,避免下次存放时留下脏数据
            } catch (Exception e) {
                LogUtils.e(e);
                e.printStackTrace();
            }
        }


        /**
         * 初始化解码器
         *
         * @param w         宽
         * @param h         高
         * @param codecdata pps/sps 编码配置数据
         */
        private void initCodec(int w, int h, byte[] codecdata) {
            try {
                LogUtils.i("initCodec : ---start--- ");
                //创建了一个解码器,此时编解码器处于未初始化状态(Uninitialized)
                mediaCodec = MediaCodec.createDecoderByType(saveMimeType);
                //宽高要判断是否是解码器所支持的范围
                MediaCodecInfo.CodecCapabilities capabilitiesForType = mediaCodec.getCodecInfo().getCapabilitiesForType(saveMimeType);
                MediaCodecInfo.VideoCapabilities videoCapabilities = capabilitiesForType.getVideoCapabilities();
                Range<Integer> supportedWidths = videoCapabilities.getSupportedWidths();
                Range<Integer> supportedHeights = videoCapabilities.getSupportedHeights();
                initW = w;
                initH = h;
                if (w < h) {
                    LogUtils.i("宽度比高度小:w=" + w + ",h=" + h);
                    int temp = w;
                    w = h;
                    h = temp;
                }
                w = supportedWidths.clamp(w);
                h = supportedHeights.clamp(h);
                LogUtils.d("initCodec 可支持的宽高:" + supportedWidths + "、" + supportedHeights + ",initW=" + initW + ",initH=" + initH);
                MediaFormat mediaFormat = MediaFormat.createVideoFormat(saveMimeType, w, h);
                mediaFormat.setInteger(MediaFormat.KEY_WIDTH, w);
                mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, h);
                if (codecdata != null && codecdata.length > 0) {
                    LogUtils.i("initCodec 设置 csd= " + Arrays.toString(codecdata));
                    mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(codecdata));
                    mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(codecdata));
                }
                boolean formatSupported = capabilitiesForType.isFormatSupported(mediaFormat);
                LogUtils.i("initCodec :  是否支持 --> " + formatSupported + ",mediaFormat=" + mediaFormat);
                //2.对编解码器进行配置,这将使编解码器转为配置状态(Configured)
                mediaCodec.configure(mediaFormat, mSurface, null, 0);
                LogUtils.i("initCodec : ---configure--- ");
                //3.调用start()方法使其转入执行状态(Executing)
                mediaCodec.start();
                LogUtils.i("initCodec : ---end--- ");
                //要保持屏幕纵横比:此方法必须在configure和start之后执行才有效
                //VIDEO_SCALING_MODE_SCALE_TO_FIT //默认铺满
                //VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING //保持比例,多余会被裁剪
//                mediaCodec.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
            } catch (Exception e) {
                LogUtils.e(e);
                e.printStackTrace();
            }
        }

        /**
         * 解码数据
         *
         * @param packet     帧数据包
         * @param pts        此缓冲区的呈现时间戳(以微秒为单位)。这通常是应该呈现(渲染)此缓冲区的媒体时间。使用输出表面时,这将作为帧的时间戳传播(转换为纳秒后)
         * @param packetSize 帧数据大小
         * @return 成功将数据放入解码器则返回true
         */
        private boolean mediaCodecDecode(byte[] packet, long pts, int packetSize) {
            int inputBufferIndex = -1;
            //判断解码器是否初始化完成
            if (mediaCodec == null) {
                LogUtils.e("mediaCodec not ready");
                return false;
            }
            try {
                if (packet != null) {
                    //队列中有视频帧,检查解码队列中是否有空闲可用的buffer,有则取视频帧送进去解码
                    inputBufferIndex = mediaCodec.dequeueInputBuffer(0);
                    if (inputBufferIndex >= 0) {
                        //有空闲可用的解码buffer
                        ByteBuffer byteBuffer = mediaCodec.getInputBuffer(inputBufferIndex);
                        byteBuffer.clear();
                        byteBuffer.limit(packetSize);
                        byteBuffer.position(0);
                        //将视频队列中的头取出送到解码队列中
                        byteBuffer.put(packet, 0, packetSize);
                        mediaCodec.queueInputBuffer(inputBufferIndex, 0, packetSize, pts, 0);
                    }
                }
                int outputBufferIndex = mediaCodec.dequeueOutputBuffer(info, 0);
                if (outputBufferIndex >= 0) {
                    //如果配置编码器时指定了有效的surface,传true将此输出缓冲区显示在surface
                    mediaCodec.releaseOutputBuffer(outputBufferIndex, true);
                }
            } catch (Exception e) {
                e.printStackTrace();
                LogUtils.e(e);
            }
            return inputBufferIndex >= 0;
        }

        /**
         * 释放资源
         */
        private void releaseMediaCodec() {
            if (mediaCodec != null) {
                try {
                    LogUtils.e("---releaseMediaCodec---");
                    //使用完编解码器后,你必须调用release()方法释放其资源
                    mediaCodec.release();
                } catch (Exception e) {
                    LogUtils.e(e);
                    e.printStackTrace();
                }
                mediaCodec = null;
            }
        }
        
        /**
         * 获取指定的MimeType
         *
         * @param codecId 后台回调ID
         * @return MimeType
         */
        private String getMimeType(int codecId) {
            switch (codecId) {
                case 12:
                case 13:
                    return "video/mp4v-es";
                case 139:
                case 140:
                    return "video/x-vnd.on2.vp8";
                case 167:
                case 168:
                    return "video/x-vnd.on2.vp9";
                case 173:
                case 174:
                    return "video/hevc";
                default:
                    return "video/avc";
            }
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.
  • 218.
  • 219.
  • 220.
  • 221.
  • 222.
  • 223.
  • 224.
  • 225.
  • 226.
  • 227.
  • 228.
  • 229.
  • 230.
  • 231.
  • 232.
  • 233.
  • 234.
  • 235.
  • 236.
  • 237.
  • 238.
  • 239.
  • 240.
  • 241.
  • 242.
  • 243.
  • 244.
  • 245.
  • 246.
  • 247.
  • 248.
  • 249.
  • 250.
  • 251.
  • 252.
  • 253.
  • 254.
  • 255.
  • 256.
  • 257.
  • 258.
  • 259.
  • 260.
  • 261.
  • 262.
  • 263.
  • 264.
  • 265.
  • 266.
  • 267.
  • 268.
  • 269.
  • 270.
  • 271.
  • 272.
  • 273.
  • 274.
  • 275.
  • 276.
  • 277.
  • 278.
  • 279.
  • 280.
  • 281.
  • 282.
  • 283.
  • 284.
  • 285.
  • 286.
  • 287.
  • 288.
  • 289.
  • 290.
  • 291.
  • 292.
  • 293.
  • 294.
  • 295.
  • 296.
  • 297.
  • 298.
  • 299.
  • 300.
  • 301.
  • 302.
  • 303.
  • 304.
  • 305.
  • 306.
  • 307.
  • 308.
  • 309.
  • 310.
  • 311.
  • 312.
  • 313.
  • 314.
  • 315.
  • 316.
  • 317.
  • 318.
  • 319.
  • 320.
  • 321.
  • 322.
  • 323.
  • 324.
  • 325.
  • 326.
  • 327.
  • 328.
  • 329.
  • 330.
  • 331.
  • 332.
  • 333.
  • 334.
  • 335.
  • 336.
  • 337.


MediaCodec
编解码
Android
2023-02-20 14:42:41
浏览
收藏 0
回答 0
相关问题
HarmonyOS CBOR编解码问题
1194浏览 • 1回复 待解决
HarmonyOS 视频编解码问题
766浏览 • 1回复 待解决
HarmonyOS 音频编解码问题咨询
669浏览 • 1回复 待解决
sql数据库查询数据量太大查不出来?
4460浏览 • 1回复 待解决
HarmonyOS编解码接口标准
1189浏览 • 1回复 待解决
HarmonyOS 音频文件base64编解码问题
641浏览 • 1回复 待解决
如何实现字符串编解码
3561浏览 • 1回复 待解决
图片编解码能力支持哪些格式
2535浏览 • 1回复 待解决
HarmonyOS 视频编解码出现花屏
791浏览 • 1回复 待解决
HarmonyOS base64编解码
694浏览 • 0回复 待解决
ArkTS如何实现字符串编解码
3560浏览 • 1回复 待解决
HarmonyOS 音频解码器aac码格式问题
475浏览 • 1回复 待解决
图片编解码支持的格式有哪些
2470浏览 • 1回复 待解决