2014-05-14 5 views
1

Xuggler를 사용하여 IP 카메라 rtsp 스트림을 rtmp 스트림 (모두 h264 코덱 포함)으로 트랜스 코딩하려고합니다. 나는 현재 2 대의 IP 카메라를 테스트하고, Xuggler를 사용하여 이러한 스트림의 트랜스 코딩을 수행하는 기본 Java 프로그램을 작성했습니다.Xuggler 디코딩 h264 문제

여기에 문제의 코드 조각입니다 :

// 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"); 
      } 
     } 

    } 

이 프로그램이 성공적으로 아무 문제없이 카메라의 스트림 중 하나를 트랜스 코딩 할 수 있습니다. 그러나 다른 하나는 며칠 동안 계속 두통을 겪어 왔습니다. 문제는 스트림에 CODEC_TYPE_UNKOWN과 CODEC_ID_NONE이 있기 때문에 컨테이너의 스트림을 보는 루프에서 나는 else 문을 가지고 있으므로 모든 것을 수동으로 설정해야한다고 생각했습니다. 다음과 같은 모든 종류의 오류가 발생했습니다 :

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

나는 프레임을 디코딩하려고 할 때마다이 오류가 발생합니다. 나는 이것이 보통 어떤 키 프레임도 읽히지 않았고 디코더가 h264 디코딩을 위해 SPS/PPS 정보를 필요로한다는 것을 의미한다는 것을 알고 있습니다. 수동으로 설정하려고 시도했는데 (논평 된 섹션 중 하나에서 볼 수 있습니다), 성공하지 못했습니다 . 나는 패킷 생성, SPS/PPS 정보로 채우고, 키 패킷을 true로 설정하는 등의 시도도했습니다. 성공하지 못했습니다. 심지어 while 루프 (현재 주석 처리되어 있음)에서도 프로그램은 한 카메라에서 키 프레임을 얻지 못합니다. 나는 또한 Xuggler에서이 경고를받은 적이

: 나는 또한 살펴본하지만 내가 본 솔루션의 방법으로도 문제가 해결되지 않은

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

....

나는이 카메라의 스트림 모두를 FFMPEG 자체를 통해 명령 줄 에서 실행하려고 시도했지만 모두 오류없이 코드 변환됩니다. 또한 Xuggler가 rtsp 스트리밍을 지원하기 위해 너무 오래 된 FFMPEG 버전으로 빌드되었을 수도 있다고 생각했지만 이전 빌드 (0.10, 1.0.1 - 1.2 및 현재 2.2)를 다시 다운로드하여 명령 줄 및 모든 성공했습니다.

저는 Xuggler Google 그룹에서 rtsp 스트림 및 "no frame!"문제를 해결하는 많은 스레드를 보았습니다. 오류가 있지만 솔루션 중 하나 (또는 ​​적어도 나를 위해 일한 하나)가 없었습니다. 누군가이 문제를 일으킬 수있는 아이디어가 있습니까? 나는 절대적으로 떠난 아이디어가 없다! (처음으로 여기 게시하는 경우, 내가 잘못했거나 정보를 빠뜨린 경우 사과드립니다.) 감사합니다.

+0

처음으로 게시하는 것을 고려하면 -1에 대한 이유가 유용 할 것입니다. – Valenthorpe

답변

3

오랜 시간 동안 검색하고 테스트 한 결과, 내 문제에 대한 해결책을 찾았습니다.

처음에는 "프레임이 없습니다!"라는 것을 알게되었습니다. NAL Units/extradata가 설정되지 않은 경우 오류가 발생할 가능성이 높습니다. 추가 조사를 통해이 정보가 RTSP DESCRIBE 응답으로부터 전송 된 SDP 정보에서 발견된다는 것을 알았습니다. 그런 다음 카메라가 WireShark를 사용하여 SDP에서 보내는 것을 보았습니다. 모든 것이 잘 보였으므로 문제는 Xuggler 프로그램에 있었어야합니다. Xuggler 함수를 사용하여 캡 티브 FFMPEG 로깅 수준을 디버그로 설정하여 IContainer 스트림이 열릴 때 FFMPEG가 가져온 SDP 정보를 출력하도록했습니다. 다시 말하지만, Wireshark가 보여 주던 것과 똑같습니다. 그런 다음 Xuggler IContainers에 컨테이너의 SDP를 인쇄하는 "createSDP()"메서드가 있음을 발견했습니다. 그것을 사용한 후에, 나는 SDP가 실제로 잘못 파싱된다는 것을 알았다. 미디어 페이로드 유형이 (원래 SDP에서) 35 RTP/AVP로 지정되었으며 SDP의 "rtpmap"행이 H264로 설정되었습니다. 그러나 "createSDP"메서드는 미디어 페이로드 유형을 "3"으로 표시합니다.

Xuggler에 포함 된 캡 티브 FFMPEG 코드 주위를 파고 미디어 페이로드 유형을 구문 분석하는 논리가 거꾸로되어 있고 동적 유형 (96+)이 먼저 있는지 확인한 다음 정적 페이로드 유형 (1 -34), 할당되지 않은 페이로드 유형 (예 : 내 경우 35)을 설명하지 않습니다. 나는 현재의 FFMPEG 코드의 로직을 조정했고, Xuggler는 35를 동적 페이로드 유형으로 구문 분석했다. 스트림이 설정되고 성공적으로 코드 변환되었다.

이 문제에 대한 적절한 수정인지는 모르지만 작동하며 페이로드 유형을 이해하는 한 프로그램의 다른 부분에는 영향을 미치지 않습니다.

내 전체 게시물을 Xuggler Google 그룹 here에서 볼 수 있습니다.