2013-12-09 10 views
11

मैंने एंड्रॉइड के मीडियाकोडेक एपीआई का उपयोग करके एक H264 स्ट्रीम एन्कोडर लिखा है। मैंने विभिन्न प्रोसेसर के साथ लगभग दस अलग-अलग उपकरणों पर इसका परीक्षण किया और स्नैपड्रैगन 800 संचालित वाले (Google नेक्सस 5 और सोनी एक्सपीरिया जेड 1) को छोड़कर, उन सभी पर काम किया। उन उपकरणों पर मुझे एसपीएस और पीपीएस और पहला कीफ्रेम मिलता है, लेकिन उसके बाद mEncoder.dequeueOutputBuffer (mBufferInfo, 0) केवल MediaCodec.INFO_TRY_AGAIN_LATER लौटाता है। मैंने पहले से ही अलग-अलग टाइमआउट्स, बिट्रेट्स, रेज़ोल्यूशन और अन्य कॉन्फ़िगरेशन विकल्पों के साथ प्रयोग नहीं किया है, इसका कोई फायदा नहीं हुआ है। परिणाम हमेशा एक ही है।मीडियाकोडेक एच 264 एनकोडर स्नैपड्रैगन 800 डिवाइस पर काम नहीं कर रहा है

मैं निम्नलिखित कोड का उपयोग एनकोडर आरंभ करने के लिए:

 mBufferInfo = new MediaCodec.BufferInfo(); 
     encoder = MediaCodec.createEncoderByType("video/avc"); 
     MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 640, 480); 
     mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 768000); 
     mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30); 
     mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mEncoderColorFormat); 
     mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10); 
     encoder.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 

जहां चयनित रंग प्रारूप है:

MediaCodecInfo.CodecCapabilities capabilities = mCodecInfo.getCapabilitiesForType(MIME_TYPE); 
      for (int i = 0; i < capabilities.colorFormats.length && selectedColorFormat == 0; i++) 
      { 
       int format = capabilities.colorFormats[i]; 
       switch (format) { 
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar: 
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar: 
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar: 
        case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar: 
        case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar: 
        case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar: 
         selectedColorFormat = format; 
         break; 
        default: 
         LogHandler.e(LOG_TAG, "Unsupported color format " + format); 
         break; 
       } 
      } 

और मैं कर रहा

  ByteBuffer[] inputBuffers = mEncoder.getInputBuffers(); 
     ByteBuffer[] outputBuffers = mEncoder.getOutputBuffers(); 

     int inputBufferIndex = mEncoder.dequeueInputBuffer(-1); 
     if (inputBufferIndex >= 0) 
     { 
      // fill inputBuffers[inputBufferIndex] with valid data 
      ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
      inputBuffer.clear(); 
      inputBuffer.put(rawFrame); 
      mEncoder.queueInputBuffer(inputBufferIndex, 0, rawFrame.length, 0, 0); 
      LogHandler.e(LOG_TAG, "Queue Buffer in " + inputBufferIndex); 
     } 

     while(true) 
     { 
      int outputBufferIndex = mEncoder.dequeueOutputBuffer(mBufferInfo, 0); 
      if (outputBufferIndex >= 0) 
      { 
       Log.d(LOG_TAG, "Queue Buffer out " + outputBufferIndex); 
       ByteBuffer buffer = outputBuffers[outputBufferIndex]; 
       if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) 
       { 
        // Config Bytes means SPS and PPS 
        Log.d(LOG_TAG, "Got config bytes"); 
       } 

       if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0) 
       { 
        // Marks a Keyframe 
        Log.d(LOG_TAG, "Got Sync Frame"); 
       } 

       if (mBufferInfo.size != 0) 
       { 
        // adjust the ByteBuffer values to match BufferInfo (not needed?) 
        buffer.position(mBufferInfo.offset); 
        buffer.limit(mBufferInfo.offset + mBufferInfo.size); 

        int nalUnitLength = 0; 
        while((nalUnitLength = parseNextNalUnit(buffer)) != 0) 
        { 
         switch(mVideoData[0] & 0x0f) 
         { 
          // SPS 
          case 0x07: 
          { 
           Log.d(LOG_TAG, "Got SPS"); 
           break; 
          } 

          // PPS 
          case 0x08: 
          { 
           Log.d(LOG_TAG, "Got PPS"); 
           break; 
          } 

          // Key Frame 
          case 0x05: 
          { 
           Log.d(LOG_TAG, "Got Keyframe"); 
          } 

          //$FALL-THROUGH$ 
          default: 
          { 
           // Process Data 
           break; 
          } 
         } 
        } 
       } 

       mEncoder.releaseOutputBuffer(outputBufferIndex, false); 

       if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) 
       { 
        // Stream is marked as done, 
        // break out of while 
        Log.d(LOG_TAG, "Marked EOS"); 
        break; 
       } 
      } 
      else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) 
      { 
       outputBuffers = mEncoder.getOutputBuffers(); 
       Log.d(LOG_TAG, "Output Buffer changed " + outputBuffers); 
      } 
      else if(outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) 
      { 
       MediaFormat newFormat = mEncoder.getOutputFormat(); 
       Log.d(LOG_TAG, "Media Format Changed " + newFormat); 
      } 
      else if(outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) 
      { 
       // No Data, break out 
       break; 
      } 
      else 
      { 
       // Unexpected State, ignore it 
       Log.d(LOG_TAG, "Unexpected State " + outputBufferIndex); 
      } 
     } 

धन्यवाद द्वारा डेटा प्राप्त तुम्हारी मदद के लिए!

+1

आउटपुट स्टाल करते समय बिंदु पर कितने इनपुट फ्रेम कतारबद्ध होते हैं? (मैं यह सुनिश्चित करना चाहता हूं कि यह इनपुट के लिए भूख न हो।) क्या लॉगबैक में कुछ भी संदिग्ध दिख रहा है? (कोडेक्स Log.e स्प्रे करने के लिए प्रवृत्त होते हैं, जो इसे बताना मुश्किल हो सकता है।) किस रंग प्रारूप का चयन किया जा रहा है? (क्या यह क्यूकॉम प्रारूप है?) क्या आपके "कच्चे फ्रेम" का आकार बिल्कुल इनपुट बफर की क्षमता के समान है? (यदि नहीं ... क्यों नहीं?) – fadden

+0

@fadden इससे कोई फर्क नहीं पड़ता कि मैं इसे कितनी देर तक चलाने देता हूं लेकिन इनपुट इनपुट बफर में हमेशा 5 फ्रेम होते हैं। सृजन पर इसका उत्पादन है: 'I/OMXClient (11245): क्लाइंट-साइड ओएमएक्स मक्स का उपयोग करना। I/ACodec (11245): setupVideoEncoder सफल हुआ 'चयनित रंग प्रारूप दोनों मामलों में है' MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar' (यदि मैं सभी प्रारूपों से पूछता हूं कि इसमें केवल दो हैं, उपरोक्त और एक जिसमें 2130708361 है जो चयनित होने पर क्रैश हो ।) कच्चे फ्रेम और इनपुट बफर समान नहीं हैं (कच्चे फ्रेम का आकार हमेशा छोटा होता है और इनपुट बफर क्षमता हमेशा 282624 होती है) – lowtraxx

+0

पांच फ्रेम सामान्य होते हैं - लगता है जैसे यह इनपुट प्रसंस्करण नहीं कर रहा है, इसलिए कोई आउटपुट नहीं है। मुझे लगता है कि आप 'encoder.start()' को कॉल कर रहे हैं? YUV420SemiPlanar अच्छा है; 2130708361 केवल सतह इनपुट के लिए उपयोग किया जाता है। एक YUV420 बफर का आकार 'चौड़ाई * ऊंचाई * 1.5', या 460800 बाइट होना चाहिए, इसलिए मैं आपके बफर आकार के बारे में थोड़ा उलझन में हूं। क्या आप लॉग फ़ाइल में अपना "मीडिया प्रारूप बदल गया" संदेश देखते हैं, और यदि ऐसा है, तो यह क्या कहता है? – fadden

उत्तर

20

आपको queueInputBuffer पर अपनी कॉल में प्रस्तुति TimeUs पैरामीटर सेट करने की आवश्यकता है। अधिकांश एन्कोडर्स इसे अनदेखा करते हैं और आप बिना किसी समस्या के स्ट्रीमिंग के लिए एन्कोड कर सकते हैं। स्नैपड्रैगन 800 उपकरणों के लिए उपयोग किया जाने वाला एन्कोडर नहीं करता है।

यह पैरामीटर आपके फ्रेम के रिकॉर्डिंग समय का प्रतिनिधित्व करता है और इसलिए उस फ्रेम के बीच की संख्या को बढ़ाने के लिए आवश्यक है जिसे आप एन्कोड करना चाहते हैं और पिछला फ्रेम।

यदि पैरामीटर सेट पिछले फ्रेम के समान मूल्य है तो एन्कोडर इसे छोड़ देता है। यदि पैरामीटर बहुत कम मूल्य पर सेट किया गया है (उदाहरण के लिए 30 एफपीएस रिकॉर्डिंग पर 100000) एन्कोडेड फ्रेम की गुणवत्ता गिर जाती है।

+1

हू। प्रस्तुति समय टिकट एच 2264 प्राथमिक धारा का हिस्सा नहीं है, इसलिए मेरी अपेक्षा थी कि मूल्य को आसानी से पारित किया गया था। मैंने एक नई एफएक्यू एंट्री (http://bigflake.com/mediacodec/#q8) जोड़ा। – fadden

+0

क्या आप प्रेजेंटेशन समय को सेट करने का उदाहरण प्रदान कर सकते हैं? –

+0

स्नैपड्रैगन 800 डिवाइस – DreamCoder

0

एन्कोड कोड.क्यूयू इनपुटबफर (इनपुटबफर इंडेक्स, 0, इनपुट। लम्बाई, (System.currentTimeMillis() - startMs) * 1000, 0);

+4

के लिए queueInputBuffer मान पर आपके कॉल में टाइमयू पैरामीटर वर्तमान समय का उपयोग करना ठीक है यदि आप रीयल-टाइम इनपुट (जैसे कैमरे से) प्राप्त कर रहे हैं, लेकिन यदि आप अन्य स्रोतों के साथ काम कर रहे हैं तो खराब काम करेंगे (उदाहरण के लिए वास्तविक समय से तेज़ी से वीडियो ट्रांसकोडिंग)। मैं 'System.currentTimeMillis() 'के खिलाफ भी सिफारिश करता हूं, क्योंकि यह अचानक कूद (आगे और पीछे) के अधीन है। Monotonic 'System.nanoTime()' एक बेहतर स्रोत है। – fadden

+0

ट्रांसकोड के मामले में भी, स्रोत सामग्री के टाइमस्टैम्प लागू होंगे (स्रोत सामग्री के एफपीएस पर आधारित)। एन्कोडर को रेट-कंट्रोल को प्रबंधित करने में सक्षम होने के लिए टाइमस्टैम्प को जानना आवश्यक है। इसलिए, यदि आपने MediaFormat.setInteger (MediaFormat.KEY_FRAME_RATE, FPS) के साथ फ़्रेमेट को कॉन्फ़िगर किया है, तो गैर-रीयलटाइम एन्कोडिंग के लिए टाइमस्टैम्प (एन * 1000 * 1000/एफपीएस) उत्पन्न करने के लिए सलाह दी जाती है। – peasea

संबंधित मुद्दे