2016-10-10 4 views
0

Привет Stack Overflow,Декодирование сырой h264 с результатами потока MediaCodec в черной поверхности

В настоящее время я пишу рамки для того чтобы достигнуть опыта Vr с помощью смартфона. Поэтому графический контент выводится на сервер (стереоскоп), кодируется и отправляется на смартфон. Я использую Nexus 5x от LG. Приложение, которое я пишу, первоначально состояло из двух видов текстур и логики для декодирования и отображения кадров. Тем не менее, класс Androids MediaCodec разбился в каждой попытке, поэтому я попытался создать минимальный рабочий пример с только одной поверхностью, основанный на рабочем коде, который я написал ранее. Но, несмотря на то, что MediaCodec больше не генерирует исключение CodecException, поверхность по-прежнему остается черной.

public class MainActivity extends Activity implements SurfaceHolder.Callback 
{ 
private DisplayThread displayThread = null; 

@Override 
protected void onCreate(Bundle savedInstanceState) 
{ 
    super.onCreate(savedInstanceState); 
    SurfaceView sv = new SurfaceView(this); 
    sv.getHolder().addCallback(this); 
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 
    setContentView(sv); 
} 

@Override 
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
{ 
    if (displayThread == null) 
    { 
     displayThread = new DisplayThread(holder.getSurface()); 
     displayThread.start(); 
    } 
} 

private class DisplayThread extends Thread 
{ 
    private MediaCodec codec; 
    private Surface surface; 
    private UdpReceiver m_renderSock; 


    public DisplayThread(Surface surface) 
    { 
     this.surface = surface; 
    } 

    @Override 
    public void run() 
    { 
     m_renderSock = new UdpReceiver(9091); 

     //Configuring Media Decoder 
     try { 
      codec = MediaCodec.createDecoderByType("video/avc"); 
     } catch (IOException e) { 
      throw new RuntimeException(e.getMessage()); 
     } 

     MediaFormat format = MediaFormat.createVideoFormat("video/avc", 1280,720); 

     codec.configure(format, surface, null, 0); 
     codec.start(); 


     while(!Thread.interrupted()) 
     { 
      int frameSize = 0; 
      byte[] frameData = m_renderSock.receive(); 

      if(frameData.length == 1) // Just for the moment, to cope with the first pakets get lost because of missing ARP, see http://stackoverflow.com/questions/11812731/first-udp-message-to-a-specific-remote-ip-gets-lost 
       continue; 

      /*Edit: This part may be left out*/ 
      int NAL_START = 1; 
      //103, 104 -> SPS, PPS | 101 -> Data 
      int id = 0; 
      int dataOffset = 0; 

      //Later on this will be serversided, but for now... 
      //Separate the SPSPPS from the Data 
      for(int i = 0; i < frameData.length - 4; i++) 
      { 
       id = frameData[i] << 24 |frameData[i+1] << 16 | frameData[i+2] << 8 
         | frameData[i+3]; 

       if(id == NAL_START) { 
        if(frameData[i+4] == 101) 
        { 
         dataOffset = i; 
        } 
       } 
      } 


      byte[] SPSPPS = Arrays.copyOfRange(frameData, 0, dataOffset); 
      byte[] data = Arrays.copyOfRange(frameData, dataOffset, frameData.length); 

      if(SPSPPS.length != 0) { 
       int inIndex = codec.dequeueInputBuffer(100000); 

       if(inIndex >= 0) 
       { 
        ByteBuffer input = codec.getInputBuffer(inIndex); 
        input.clear(); 
        input.put(SPSPPS); 
        codec.queueInputBuffer(inIndex, 0, SPSPPS.length, 16, MediaCodec.BUFFER_FLAG_CODEC_CONFIG); 
       } 
      } 
      /*Edit end*/ 

      int inIndex = codec.dequeueInputBuffer(10000); 
      if(inIndex >= 0) 
      { 
       ByteBuffer inputBuffer = codec.getInputBuffer(inIndex); 
       inputBuffer.clear(); 
       //inputBuffer.put(data); 
       inputBuffer.put(frameData); 
       //codec.queueInputBuffer(inIndex, 0, data.length, 16, 0); 
       codec.queueInputBuffer(inIndex, 0, frameData.length, 16, 0); 
      } 

      BufferInfo buffInfo = new MediaCodec.BufferInfo(); 
      int outIndex = codec.dequeueOutputBuffer(buffInfo, 10000); 

      switch(outIndex) 
      { 
       case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: 
        break; 
       case MediaCodec.INFO_TRY_AGAIN_LATER: 
        break; 
       case -3: //This solves it 
        break; 
       default: 
        ByteBuffer buffer = codec.getOutputBuffer(outIndex); 
        codec.releaseOutputBuffer(outIndex, true); 
      } 


     } 
    } 
} 

Так что в основном этот код работал в прошлом. Но в то время API-интерфейс кодеков мультимедиа ByteBuffer[] для входных и выходных буферов. Также не было необходимости отделять SPSPPS-данные от данных кадра (по крайней мере, я этого не делал, и это сработало, также из-за того, что Nvcuvenc разделил каждый NALU).

Я осмотрел содержимое двух буферов и это результат:

SPSPPS: 
0 0 0 1 103 100 0 32 -84 43 64 40 2 -35 -128 -120 0 0 31 64 0 14 -90 4 120 -31 -107 
0 0 1 104 -18 60 -80 

Data: 
0 0 0 1 101 -72 4 95 ... 

Для меня это выглядит правильно. Поток h264 создается с помощью API Nvidias NVenc и, если он сохраняется на диске, без проблем воспроизводится с VLC.

Прошу прощения за большой код. Спасибо за помощь!

ответ

1

Таким образом, единственная проблема заключалась в том, что dequeueOutputBuffers все еще может вернуться -3, aka MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED, который отмечен как устаревший. Очень хорошо. Не обрабатывая это возвращаемое значение или, если быть более конкретным, используйте значение констант в качестве ввода для getOutputBuffer(), кодек выдает ошибку -> черный экран.

Редактировать: О, и, видимо, весь материал NAL также не нужен. Даже если API заявляет, что перед запуском должны быть предоставлены SAL и PPS NALU. Я отметил ту часть, которая может быть оставлена ​​в моем Вопросе.

0

Я наблюдаю подобное поведение на новых устройствах Samsung и подозреваю, что кодеки могут иметь такую ​​же проблему. Попытайтесь исправить, спасибо.

Также материал SPS/PPS необходим только для боксерских контейнеров, таких как mp4. Сырые игроки в группе.

Смежные вопросы