سؤال

I'm trying to use Xuggler to transcode IP Camera rtsp streams to rtmp streams (all with h264 codecs). I currently have 2 IP cameras to test with, and wrote a basic Java program using Xuggler to do the transcoding of these streams.

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. 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. 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. Even in the while loop (currently commented out) the program never seems to get a key frame from the one camera.

I've also gotten this warning from 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. 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.

I've seen a lot of threads across the Xuggler google group that address problems with rtsp streams and the "no frame!" 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. Upon further investigation i found that this information is found in the SDP information sent from an RTSP DESCRIBE response. 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. 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. Again, this looked exactly the same as what Wireshark was showing me. I then found that Xuggler IContainers have a "createSDP()" method, which will print the SDP of the container. After using that, i saw that the SDP was in fact getting parsed incorrectly. 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. However, the "createSDP" method was showing the media payload type as just "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). 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.

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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top