2016-04-26 247 views
1

我目前正試圖解析來自RTP流的H264數據,然後將其發送到MediaCodec以在Android的SurfaceView上呈現。在Android上使用H264數據解碼RTP流使用MediaCodec

不過,我不能確定如何:

  • 從RTP包正確地構建H264片
  • 發送H264片的媒體編解碼器一旦被組裝成片

我還沒有看到以清晰簡潔的方式實施的任何示例,我還沒有找到MediaCodec文檔對您有所幫助。

任何人都有這方面的經驗嗎?

void videoCodec(ByteBuffer input, int flags) { 

    bufferInfo.set(0, 0, 0, flags); 

    int inputBufferId = codec.dequeueInputBuffer(10000); 

    if (inputBufferId >= 0) { 

     //put data 
     ByteBuffer inputData = inputBuffers[inputBufferId]; 

     inputData.clear(); 
     inputData.put(input); 

     //queue it up 
     codec.queueInputBuffer(inputBufferId, 0, input.limit(), 0, flags); 
    } 

    int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, 10000); 

    if (outputBufferId >= 0) { 
     // outputBuffers[outputBufferId] is ready to be processed or rendered. 
     Timber.e("Rendering Data with Index of: %s", outputBufferId); 
     codec.releaseOutputBuffer(outputBufferId, true); 

    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { 
     outputBuffers = codec.getOutputBuffers(); 
    } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { 
     // Subsequent data will conform to new format. 
     //format = codec.getOutputFormat(); 
    } 
} 

MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080); 
        codec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); 
        codec.configure(format, surfaceVideo.getHolder().getSurface(), null, 0); 
        codec.start(); 


        inputBuffers = codec.getInputBuffers(); 
        outputBuffers = codec.getOutputBuffers(); 

     while (streaming) { 

      //receive RTP Packet 
      h264Parser(rtpPacket.getPayload()); 

     } 

而且在h264Parser看起來是這樣的:

void h264Parser(byte[] payload) { 

    int packetType = (byte) payload[0] & (byte) 0x1F; 
    boolean startBit = (payload[1] & 0x80) != 0; 
    boolean endBit = (payload[1] & 0x40) != 0; 
    int flags = 0; 

    switch (packetType) { 
     case 7: 
      pps = new ByteArrayOutputStream(); 
      pps.write(prefix); 
      pps.write(payload); 
      break; 
     case 8: 
      if (pps.size() > 0) { 
       pps.write(payload); 
       hasPps = true; 
       flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG; 
       payload = pps.toByteArray(); 
       //Send packet to decoder 
       videoCodec(ByteBuffer.wrap(payload), flags); 
      break; 
     case 28: 

      if (hasPps) { 
       if (startBit) { 
        baos = new ByteArrayOutputStream(); 
        baos.write(prefix); 
        baos.write(payload); 
       } else if (endBit) { 
         if(baos != null) { 
          baos.write(payload); 
          flags = MediaCodec.BUFFER_FLAG_KEY_FRAME; 
          payload = baos.toByteArray(); 
          //Send packet to decoder 
          videoCodec(ByteBuffer.wrap(payload), flags); 
          hasPps = false; 
       } else { 
         if(baos != null) { 
          baos.write(payload); 
         } 
       } 
      } 

      break; 
     case 1: 
      break; 
     default: 
    } 
+0

你可以使用一些庫來播放實況視頻。我曾與Vitamio合作過,其使用非常簡單:https://www.vitamio.org/en/ – WannaBeGeek

+0

我不是這個庫的作者,但是在做類似的研究時,我發現這個:[AndroidStreamingClient](https: //github.com/ekumenlabs/AndroidStreamingClient)請看這裏的代碼: https://github.com/ekumenlabs/AndroidStreamingClient/blob/master/android_streaming_client/src/main/java/com/c77/androidstreamingclient/lib/rtp /RtpMediaExtractor.java這似乎正在做你正在尋找。 –

回答

1

至於我記得MediaCodec採用全接取設備,不僅切片(有人糾正我,我錯了)

所以你必須建立一個完整的RTP接入單元並將其提供給解碼器(可惜我沒有RTP的經驗,並且無法幫助你構建一個)。

您發送的訪問單元到解碼器如下:

出隊INPUTBUFFER

int inputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC); ByteBuffer inputBuffer = videoDecoderInputBuffers[videoInputBufIndex];

與接入單位

inputBuffer.put(acessUnit); inputBuffer.flip();

隊列緩衝區用於填充它解碼

decoder.queueInputBuffer(inputBufferIndex,0,inputBuffer.limit(), 0, FLAGS); 

我希望這會有所幫助

+0

謝謝,克里斯!這很有幫助。我現在正在挖掘。 – androidDeving

+0

我一直得到-1的outputBufferId。我懷疑這意味着沒有可用的輸出緩衝區來排隊。有什麼想法嗎? – androidDeving

+0

的確,如果您提供無法處理的mediacodec輸入,情況也是如此。請記住,您必須將SPS和PPS送入解碼器。您可以通過傳遞給解碼器的MediaFormat來完成此操作('format.setByteBuffer(「csd-0」,ByteBuffer.wrap(header_sps)); format.setByteBuffer(「csd-1」,ByteBuffer.wrap(header_pps)) ;')或通過使用標誌BUFFER_FLAG_CODEC_CONFIG將它們作爲第一個緩衝區發送 – chris

相關問題