3

我正在申请一次播放 30 个视频。我正在使用Xuggler解码视频文件和Swing窗口进行显示。

但我面临以下问题:

  1. 视频显示不流畅
  2. 我发现使用分析器,大约 25% 的时间用于垃圾收集。

我应该如何调整垃圾收集器和其他性能参数?

Xuggler-Java 组合不好吗?

编辑

我的视频解码循环是:

private boolean decodeStreams() throws Exception {
    IPacket packet = IPacket.make();

    long firstTimestampInStream = Global.NO_PTS;
    long systemClockStartTime = 0;

    viewer.urlStatusUpdate(index, Globals.STATUS_PLAYING);

    while (container.readNextPacket(packet) >= 0) {
        if (stopPlaying) {
            if (isStopPlaying(2)) {
                return false;
            }
        }

        if (packet.getStreamIndex() == videoStreamID) {
            IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight());
            int offset = 0;
            while (offset < packet.getSize()) {
                int bytesDecoded = videoCoder.decodeVideo(picture, packet, offset);
                if (bytesDecoded < 0) {
                    throw new Exception("Got error on decoding video");
                }
                offset += bytesDecoded;
                if (picture.isComplete()) {
                    if (firstTimestampInStream == Global.NO_PTS) {
                        firstTimestampInStream = picture.getTimeStamp();
                        systemClockStartTime = System.currentTimeMillis();
                    } else {
                        long millisecondsToSleep = (
                                ((picture.getTimeStamp() - firstTimestampInStream) / 1000)
                                - (System.currentTimeMillis() - systemClockStartTime)
                                );
                        if (millisecondsToSleep > 50) {
                            try {
                                Thread.sleep(millisecondsToSleep - 50);
                            } catch (Exception e) {
                            }
                        }
                    }
                    viewer.videoImageUpdate(index, converter.toImage(picture));
                }
            }
        }
    }

    return true;
}

我改变了 IVideoPicture 声明的位置:

private boolean decodeStreams() throws Exception {
    IPacket packet = IPacket.make();

    long firstTimestampInStream = Global.NO_PTS;
    long systemClockStartTime = 0;

    viewer.urlStatusUpdate(index, Globals.STATUS_PLAYING);
    IVideoPicture picture = IVideoPicture.make(videoCoder.getPixelType(), videoCoder.getWidth(), videoCoder.getHeight());

    while (container.readNextPacket(packet) >= 0) {
        if (stopPlaying) {
            if (isStopPlaying(2)) {
                return false;
            }
        }

        if (packet.getStreamIndex() == videoStreamID) {
            int offset = 0;
            while (offset < packet.getSize()) {
                int bytesDecoded = videoCoder.decodeVideo(picture, packet, offset);
                if (bytesDecoded < 0) {
                    throw new Exception("Got error on decoding video");
                }
                offset += bytesDecoded;
                if (picture.isComplete()) {
                    if (firstTimestampInStream == Global.NO_PTS) {
                        firstTimestampInStream = picture.getTimeStamp();
                        systemClockStartTime = System.currentTimeMillis();
                    } else {
                        long millisecondsToSleep = (
                                ((picture.getTimeStamp() - firstTimestampInStream) / 1000)
                                - (System.currentTimeMillis() - systemClockStartTime)
                                );
                        if (millisecondsToSleep > 50) {
                            try {
                                Thread.sleep(millisecondsToSleep - 50);
                            } catch (Exception e) {
                            }
                        }
                    }
                    viewer.videoImageUpdate(index, converter.toImage(picture));
                }
            }
        }
    }

    return true;
}

现在 GC 花费的时间不到 10%,通常在 5% 到 8& 左右。我一次可以流畅地播放所有 30 个视频。

改变位置(将 IVideoPicture 声明放在外面,并且只分配一次内存)可能有问题吗?每次在分配的内存上解码新视频图片时,是否会设置图片时间戳?

谢谢

4

3 回答 3

3

您当前的 GC 可能不适合您的任务。要获得可预测的 GC 时间,您可以尝试使用 G1 垃圾收集器(我假设您使用的是 Java 7u4 或更高版本)。G1 计划作为并发标记扫描收集器 (CMS) 的长期替代品。将 G1 与 CMS 进行比较,G1 是一个更好的解决方案。G1 提供比 CMS 收集器更可预测的垃圾收集暂停,并允许用户指定所需的暂停目标。

使用以下选项为您的特定情况归档最佳性能:

-XX:+UseG1GC - 告诉 JVM 使用 G1 垃圾收集器。

-XX:MaxGCPauseMillis=500 - 设置最大 GC 暂停时间的目标。这是一个软目标,JVM 将尽最大努力实现它。因此,暂停时间目标有时无法实现。默认值为 200 毫秒。

-XX:InitiatingHeapOccupancyPercent=80 - 启动并发 GC 周期的堆占用百分比。G1 使用它来触发基于整个堆的占用情况的并发 GC 周期,而不仅仅是一代。值 0 表示“执行恒定的 GC 循环”。默认值为 45%。

此处提供更多详细信息

于 2013-07-28T09:49:00.913 回答
2

一种方法是更改​​堆大小和不同代的大小。Oracle的GC Papre解释了 gc 和调整的工作原理。

于 2013-07-28T09:42:22.247 回答
0

我有这个建议:

  1. Thread.sleep(long)当您需要非常精确的计时时,切勿使用;使用Thread.sleep(0,long). 这使用纳秒精度。

  2. 永远不要用于精确重复的任务Thread.sleep 使用 aScheduledExecutorService并以您需要的精确时间间隔安排任务。我亲眼目睹了这种方法比 Windows 上的 sleep 方法更精确。

  3. 浏览你的代码,注意内存分配发生的每个地方,并努力思考+研究是否可以通过替换一些现有内存缓冲区的内容来替换分配。如果你减少分配,GC 将很容易收集剩下的东西。

于 2013-07-28T11:45:11.767 回答