简体   繁体   English

Xuggler解码h264问题

[英]Xuggler decoding h264 issues

I'm trying to use Xuggler to transcode IP Camera rtsp streams to rtmp streams (all with h264 codecs). 我正在尝试使用Xuggler将IP Camera rtsp流转码为rtmp流(全部使用h264编解码器)。 I currently have 2 IP cameras to test with, and wrote a basic Java program using Xuggler to do the transcoding of these streams. 我目前有2个IP摄像头可供测试,并使用Xuggler编写了一个基本的Java程序来对这些流进行转码。

Here's snippet of code in question: 这是有问题的代码片段:

    // Setup the Input Container
    InContainer = IContainer.make();
    if(InContainer.open(InUrl, IContainer.Type.READ, null, false, false) < 0)
    {
        System.err.println("Could not open input container");
        return false;
    }
    System.out.println("Input cointainer opened...");

    // Loop until we find the key packet
    IPacket keyPacket = IPacket.make(); 
    InContainer.readNextPacket(keyPacket);
    //System.out.println("Waiting on key frame...");
    //while(InContainer.readNextPacket(keyPacket) >= 0 && !keyPacket.isKeyPacket()) {
        //System.out.println(keyPacket.toString());
    //}
    System.out.println(keyPacket.toString());
    System.out.println(bytesToHex(keyPacket.getData().getByteArray(0, keyPacket.getData().getSize())));

    videoStreamId = -1;
    int numStreams = InContainer.getNumStreams();
    System.out.println("Num. Streams in Container: " + numStreams);
    for(int i = 0; i < numStreams; i++){
        IStream stream = InContainer.getStream(i);
        IStreamCoder coder = stream.getStreamCoder();

        if(coder.getCodecType() == ICodec.Type.CODEC_TYPE_VIDEO)
        {
            VideoDecoder = coder;
            videoStreamId = i;

            if(VideoDecoder.open(null, null) < 0){
                System.err.println("Could not open video decoder for input container");
                return false;
            }
            System.out.println("Video decoder opened...");
            // Need to decode at least one key frame
            IVideoPicture keyPicture = IVideoPicture.make(VideoDecoder.getPixelType(), 0, 0);
            int bytesDecoded = VideoDecoder.decodeVideo(keyPicture, keyPacket, 0);
            if(bytesDecoded < 0)
            {
                throw new RuntimeException("Unable to decode key video packet");
            }

            System.out.println(DatatypeConverter.printBase64Binary(VideoDecoder.getExtraData().getByteArray(0, VideoDecoder.getExtraData().getSize())));
        }
        else // The stream has an unkown codec type, and no codec ID, we need to set the StreamCoder
        {
            coder.setCodec(ICodec.findDecodingCodec(ICodec.ID.CODEC_ID_H264));
            coder.setWidth(352);
            coder.setHeight(288);
            coder.setPixelType(IPixelFormat.Type.YUV420P);

            VideoDecoder = coder;
            videoStreamId = i;

            /*
            // Create the Extradata buffer
            byte[] start_sequence = new byte[]{0, 0, 1};
            byte[] extraData1 = DatatypeConverter.parseBase64Binary("Z0IAHtoFglMCKQI=");
            byte[] extraData2 = DatatypeConverter.parseBase64Binary("aN4Fcg==");
            int extraDataSize = extraData1.length + extraData2.length + start_sequence.length * 2;
            int destPos = 0;
            byte[] extraData = new byte[extraDataSize];
            System.arraycopy(start_sequence, 0, extraData, destPos, start_sequence.length);
            destPos += start_sequence.length;
            System.arraycopy(extraData1, 0, extraData, destPos, extraData1.length);
            destPos += extraData1.length;
            System.arraycopy(start_sequence, 0, extraData, destPos, start_sequence.length);
            destPos += start_sequence.length;
            System.arraycopy(extraData2, 0, extraData, destPos, extraData2.length);
            */
            if(VideoDecoder.open(null, null) < 0)
            {
                System.err.println("Could not open video decoder for input container");
                return false;
            }

            /*
            // Set the StreamCoder extradata
            IBuffer extraBuffer = IBuffer.make(null, extraData, 0, extraDataSize);
            int result = VideoDecoder.setExtraData(extraBuffer, 0, extraDataSize, true);
            if(result < 0)
            {
                System.err.println("Could not set the coder ExtraData");
            }
            else 
            {
                System.out.println("VideoDecoder ExtraData set!");
            }*/

            //System.out.println(DatatypeConverter.printBase64Binary(VideoDecoder.getExtraData().getByteArray(0, VideoDecoder.getExtraData().getSize())));

            IVideoPicture keyPicture = IVideoPicture.make(VideoDecoder.getPixelType(), VideoDecoder.getWidth(), VideoDecoder.getHeight());
            int bytesDecoded = VideoDecoder.decodeVideo(keyPicture, keyPacket, 0); //key/keyPacket
            if(bytesDecoded < 0)
            {
                throw new RuntimeException("Unable to decode key video packet");
            }
        }

    }

This program is able to successfully transcode one of the camera's streams without any problems. 该程序能够成功转码其中一个摄像机的流,没有任何问题。 The other, however, has been giving me constant headaches for several days now. 然而,另一个人现在已经持续了好几天了。 In the loop to look at the container's streams, I have an else statement because the problem stream has CODEC_TYPE_UNKOWN and CODEC_ID_NONE, so i thought i would need to set everything manually. 在循环中查看容器的流,我有一个else语句,因为问题流有CODEC_TYPE_UNKOWN和CODEC_ID_NONE,所以我想我需要手动设置所有内容。 I've gotten all kinds of errors such as: 我遇到了各种各样的错误,例如:

15:22:36.964 [main] ERROR org.ffmpeg - [h264 @ 0000000000423870] no frame!

I get this error EVERY time i try to decode a frame. 每当我尝试解码一个帧时,我都会收到此错误。 I realize this usually means that no key frames have been read and that the decoder needs the SPS/PPS information for h264 decoding, which i've tried to manually set (you can see in one of the commented sections), but with no success. 我意识到这通常意味着没有读取关键帧并且解码器需要用于h264解码的SPS / PPS信息,我试图手动设置(你可以在其中一个评论部分看到),但没有成功。 I've even tried creating a packet, filling it with the SPS/PPS info, setting the key packet to true, etc... also with no success. 我甚至尝试过创建一个数据包,用SPS / PPS信息填充它,将密钥包设置为true等等......也没有成功。 Even in the while loop (currently commented out) the program never seems to get a key frame from the one camera. 即使在while循环中(目前被注释掉),程序也似乎从一个相机中得不到关键帧。

I've also gotten this warning from Xuggler: 我也从Xuggler那里得到了这个警告:

16:22:43.412 [main] WARN  com.xuggle.xuggler - Could not find streams in input container (../../../../../../../csrc/com/xuggle/xuggler/Container.cpp:898)

... which i've also looked into but none of the solutions i've seen have worked. ...我也调查过,但我见过的解决方案都没有奏效。

I've tried running both of these camera's streams through FFMPEG itself in the command line and both are transcoded with no errors. 我已经尝试在命令行中通过FFMPEG本身运行这两个摄像机的流,并且两者都被转码而没有错误。 I also thought that maybe Xuggler was built with too old of a version of FFMPEG to support rtsp streaming properly, but i went back and downloaded many of the older builds (0.10, 1.0.1 - 1.2, and current 2.2) and tried through the command line and all have succeeded. 我还认为,也许Xuggler是用过于旧版本的FFMPEG构建的,以便正确支持rtsp流,但我回去并下载了许多旧版本(0.10,1.0.1-1.2和当前2.2)并尝试通过命令行,所有都成功了。

I've seen a lot of threads across the Xuggler google group that address problems with rtsp streams and the "no frame!" 我在Xuggler google小组中看到过很多线程,解决了rtsp流和“无框架”的问题。 error, but none of them have had a solution (or at least one that worked for me). 错误,但他们都没有解决方案(或至少有一个解决方案)。 Does anyone have any idea what might be causing this? 有谁知道可能导致这种情况的原因是什么? I have absolutely no ideas left! 我绝对没有想法! (First time posting here as well, my apologies if I did anything incorrectly or left out information) Thanks. (第一次在这里发帖,如果我做错了什么或者遗漏了信息我很抱歉)谢谢。

After many long hours of searching and testing, I found a solution to my problem. 经过长时间的搜索和测试,我找到了解决问题的方法。

For starters, i learned that the "no frame!" 首先,我了解到“没有框架!” error is likely caused when no NAL Units/extradata have been set. 如果没有设置NAL单位/额外数据,可能会导致错误。 Upon further investigation i found that this information is found in the SDP information sent from an RTSP DESCRIBE response. 经过进一步调查,我发现此信息可在RTSP DESCRIBE响应发送的SDP信息中找到。 I then looked at what the camera was sending in the SDP using WireShark, and everything looked fine, so the problem must have been in the Xuggler program. 然后我使用WireShark查看了相机在SDP中发送的内容,一切看起来都很好,所以问题一定出现在Xuggler程序中。 I used the Xuggler function to set the captive FFMPEG logging level to debug, so that when the stream IContainer was opened, FFMPEG printed the SDP information it got. 我使用Xuggler函数将强制FFMPEG日志记录级别设置为debug,以便在打开流IContainer时,FFMPEG打印它获得的SDP信息。 Again, this looked exactly the same as what Wireshark was showing me. 再次,这看起来与Wireshark向我展示的完全一样。 I then found that Xuggler IContainers have a "createSDP()" method, which will print the SDP of the container. 然后我发现Xuggler IContainers有一个“createSDP()”方法,它将打印容器的SDP。 After using that, i saw that the SDP was in fact getting parsed incorrectly. 使用它之后,我看到SDP实际上被错误地解析了。 The media payload type was specified (in the original SDP) as 35 RTP/AVP, and the "rtpmap" line in the SDP was setting it to H264. 媒体有效载荷类型被指定(在原始SDP中)为35 RTP / AVP,并且SDP中的“rtpmap”行将其设置为H264。 However, the "createSDP" method was showing the media payload type as just "3". 但是,“createSDP”方法将媒体有效负载类型显示为“3”。

I dug around the captive FFMPEG code contained in Xuggler and found that the logic of parsing the media payload type was backwards, and that it checks for the dynamic types (96 +) first, and then the static payload types (1-34), which does not account for the unassigned payload types (such as 35 in my case). 我挖掘了Xuggler中包含的强制性FFMPEG代码,发现解析媒体有效负载类型的逻辑是向后的,它首先检查动态类型(96 +),然后检查静态有效负载类型(1-34),它没有考虑未分配的有效载荷类型(例如我的情况下为35)。 I adjusted the logic to that of the current FFMPEG code, and Xuggler then parsed the 35 as a dynamic payload type, and the stream is set up and transcoded successfully. 我将逻辑调整为当前FFMPEG代码的逻辑,然后Xuggler将35解析为动态有效负载类型,并成功设置流并进行转码。

I don't know if this is a proper fix to the problem, but it does work, and as far as I understand the payload types, it shouldn't affect any other parts of the program. 我不知道这是否是对问题的正确解决方法,但它确实有效,据我了解有效载荷类型,它不应该影响程序的任何其他部分。

You can see my full post on the Xuggler google groups here . 你可以在这里看到我在Xuggler谷歌小组上的完整帖子。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM