2012-11-19 50 views
27

के साथ कैमरे से एन्कोडिंग एच .264 मैं इसे एंड्रॉइड 4.1 (एक अपग्रेड किए गए असस ट्रांसफार्मर टैबलेट का उपयोग करके) पर काम करने की कोशिश कर रहा हूं। Alex's response to my previous question के लिए धन्यवाद, मैं पहले से ही कुछ कच्चे H.264 डेटा को फ़ाइल में लिखने में सक्षम था, लेकिन यह फ़ाइल केवल ffplay -f h264 के साथ बजाने योग्य है, और ऐसा लगता है कि यह फ़्रेमेट (अत्यधिक तेज़ प्लेबैक) के बारे में सभी जानकारी खो गया है। रंग-स्थान भी गलत दिखता है (एन्कोडर की तरफ कैमरे के डिफ़ॉल्ट का उपयोग करके एटीएम)।एंड्रॉइड मीडियाकोडेक

public class AvcEncoder { 

private MediaCodec mediaCodec; 
private BufferedOutputStream outputStream; 

public AvcEncoder() { 
    File f = new File(Environment.getExternalStorageDirectory(), "Download/video_encoded.264"); 
    touch (f); 
    try { 
     outputStream = new BufferedOutputStream(new FileOutputStream(f)); 
     Log.i("AvcEncoder", "outputStream initialized"); 
    } catch (Exception e){ 
     e.printStackTrace(); 
    } 

    mediaCodec = MediaCodec.createEncoderByType("video/avc"); 
    MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 320, 240); 
    mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 125000); 
    mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15); 
    mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar); 
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); 
    mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
    mediaCodec.start(); 
} 

public void close() { 
    try { 
     mediaCodec.stop(); 
     mediaCodec.release(); 
     outputStream.flush(); 
     outputStream.close(); 
    } catch (Exception e){ 
     e.printStackTrace(); 
    } 
} 

// called from Camera.setPreviewCallbackWithBuffer(...) in other class 
public void offerEncoder(byte[] input) { 
    try { 
     ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers(); 
     ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers(); 
     int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1); 
     if (inputBufferIndex >= 0) { 
      ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 
      inputBuffer.clear(); 
      inputBuffer.put(input); 
      mediaCodec.queueInputBuffer(inputBufferIndex, 0, input.length, 0, 0); 
     } 

     MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); 
     int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,0); 
     while (outputBufferIndex >= 0) { 
      ByteBuffer outputBuffer = outputBuffers[outputBufferIndex]; 
      byte[] outData = new byte[bufferInfo.size]; 
      outputBuffer.get(outData); 
      outputStream.write(outData, 0, outData.length); 
      Log.i("AvcEncoder", outData.length + " bytes written"); 

      mediaCodec.releaseOutputBuffer(outputBufferIndex, false); 
      outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0); 

     } 
    } catch (Throwable t) { 
     t.printStackTrace(); 
    } 

} 

एनकोडर प्रकार बदलने के लिए "वीडियो/mp4" जाहिरा तौर पर फ़्रेमरेट-समस्या का हल है, लेकिन मुख्य लक्ष्य एक स्ट्रीमिंग सेवा करना है, यह एक अच्छा समाधान नहीं है।

मुझे पता है कि मैंने एसपीएस और पीपीएस न्यूल पर विचार करते हुए कुछ एलेक्स कोड को छोड़ दिया, लेकिन मुझे उम्मीद थी कि यह आवश्यक नहीं होगा क्योंकि यह जानकारी outData से भी आ रही थी और मुझे लगता है कि एन्कोडर इसे सही तरीके से प्रारूपित करेगा। यदि ऐसा नहीं है, तो मुझे अपनी फ़ाइल/स्ट्रीम में विभिन्न प्रकार के एनयूएल की व्यवस्था कैसे करनी चाहिए?

तो, वैध, काम कर रहे H.264 स्ट्रीम को बनाने के लिए मैं यहां क्या खो रहा हूं? और कैमरे के रंगस्थान और एन्कोडर के रंगस्थान के बीच एक मैच बनाने के लिए मुझे किन सेटिंग्स का उपयोग करना चाहिए?

मुझे एहसास है कि एंड्रॉइड/मीडियाकोडेक विषय की तुलना में यह H.264 से संबंधित प्रश्न है। या फिर भी मैं MediaCodec API का सही उपयोग नहीं कर रहा हूं?

अग्रिम धन्यवाद।

+0

मुझे एंड्रॉइड के मीडिया प्लेयर के साथ बहुत सारे मुद्दे हैं, और यह भी कि एंड्रॉइड फोन कितने अलग हैं अलग ढंग से। क्या आपके लिए रूपांतरण सर्वर पक्ष करना संभव है? –

+1

मैं इसी तरह की कार्यक्षमता पर काम कर रहा हूं लेकिन वर्तमान में कैमरा.एपियो.बफरबवरफ्लो एक्सेप्शन प्राप्त कर रहा हूं जब कैमरा.सेटप्रवाहवॉलबेलबैक विथबफर (...) से ऑफर एन्कोडर (...) को कॉल कर रहा है, तो क्या आप कैमरे में प्राप्त दृष्टिकोण को साझा करने में सक्षम होंगे .setPreviewCallbackWithBuffer (...)। बहुत धन्यवाद। – lucasweb

+0

कोड java.nio.BufferOverflowException को छोड़कर ठीक लगता है जो inputBuffer.put (इनपुट) पर होता है; बयान। बाइट को buffer.capacity() भागों में विभाजित करने का प्रयास किया, MediaCodec.queueInputBuffer कॉल पर एक IllegalStateException त्रुटि के साथ समाप्त हुआ। कोई विचार यह कैसे संशोधित किया जा सकता है? –

उत्तर

7

आपके तेज प्लेबैक के लिए - फ्रेम दर समस्या, आपको यहां कुछ भी नहीं करना है। चूंकि यह एक स्ट्रीमिंग समाधान है, दूसरी तरफ फ्रेम को प्रत्येक फ्रेम के साथ अग्रिम या टाइमस्टैम्प में बताया जाना चाहिए। ये दोनों प्राथमिक धारा का हिस्सा नहीं हैं। या तो पूर्व-निर्धारित फ़्रेमेट चुना जाता है या आप कुछ एसडीपी या उस तरह कुछ पास करते हैं या आप मौजूदा प्रोटोकॉल जैसे आरटीएसपी का उपयोग करते हैं। दूसरे मामले में टाइमस्टैम्प आरटीपी जैसे कुछ के रूप में भेजे गए धारा का हिस्सा हैं। फिर ग्राहक को आरटीपी स्ट्रीम को चुकाना पड़ता है और इसे बीएसीएल खेलना पड़ता है। इस तरह प्राथमिक स्ट्रीमिंग काम करता है। [या तो आपके फ्रेम दर को ठीक करें यदि आपके पास एक निश्चित दर एन्कोडर है या टाइमस्टैम्प दें]

स्थानीय पीसी प्लेबैक तेज़ होगा क्योंकि यह एफपीएस नहीं जान पाएगा। इनपुट से पहले एफपीएस पैरामीटर देकर

ffplay -fps 30 in.264 

आप पीसी पर प्लेबैक को नियंत्रित कर सकते हैं।

फ़ाइल के लिए बजाने योग्य नहीं है: क्या इसमें एक एसपीएस और पीपीएस है। इसके अलावा आपके पास एनएएल हेडर सक्षम होना चाहिए - अनुबंध बी प्रारूप। मुझे एंड्रॉइड के बारे में ज्यादा जानकारी नहीं है, लेकिन किसी भी कंटेनर में नहीं होने पर किसी भी एच .264 प्राथमिक धारा को खेलने योग्य होने की आवश्यकता है और इसे बाद में डालने और खेले जाने की आवश्यकता है। यदि एंड्रॉइड डिफ़ॉल्ट एमपी 4 है, लेकिन डिफ़ॉल्ट एनेक्सब हेडर बंद हो जाएंगे, तो शायद इसे सक्षम करने के लिए एक स्विच है। या यदि आप फ्रेम द्वारा डेटा फ्रेम प्राप्त कर रहे हैं, तो बस इसे स्वयं जोड़ें।

रंग प्रारूप के लिए: मुझे लगता है कि डिफ़ॉल्ट काम करना चाहिए। तो इसे स्थापित करने की कोशिश मत करो। यदि 422 प्लानर या यूवीवायवी/वीयूयूवाई इंटरलीव किए गए प्रारूपों का प्रयास न करें। आमतौर पर कैमरे उनमें से एक हैं। (लेकिन जरूरी नहीं है, ये वे हो सकते हैं जिन्हें मैंने अक्सर सामना किया है)।

2

आप मीडियाकोडेक से इसके समर्थित बिटमैप प्रारूप के लिए पूछ सकते हैं और अपने पूर्वावलोकन से पूछ सकते हैं। समस्या यह है कि, कुछ मीडियाकोडेक्स केवल स्वामित्व वाले पैक किए गए वाईयूवी स्वरूपों का समर्थन करते हैं जिन्हें आप पूर्वावलोकन से प्राप्त नहीं कर सकते हैं। विशेष रूप से 2130706688 = 0x7F000100 = COLOR_TI_FormatYUV420PackedSemiPlanar।पूर्वावलोकन के लिए डिफ़ॉल्ट स्वरूप है 17 = NV21 = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV411Planar = YCbCr 420 अर्ध Planar

+0

इसके अतिरिक्त उनमें से कुछ एक प्रारूप का समर्थन करने का दावा करते हैं लेकिन एक और वितरित करते हैं (उदाहरण के लिए एनवी 12/एनवी 21 बहुत सारे सैमसंग उपकरणों पर गड़बड़ कर रहे हैं और कुछ सैमसंग डिवाइस जब आप एनवी प्रारूप मांगते हैं तो वाईवी में से एक को वितरित करते हैं)। यह परेशान करने वाला है। –

6

आप इसी प्रकार रंग रिक्त स्थान परिवर्तित कर सकते हैं आप YV12 के लिए पूर्वावलोकन रंग स्थान सेट किया है:

public static byte[] YV12toYUV420PackedSemiPlanar(final byte[] input, final byte[] output, final int width, final int height) { 
     /* 
     * COLOR_TI_FormatYUV420PackedSemiPlanar is NV12 
     * We convert by putting the corresponding U and V bytes together (interleaved). 
     */ 
     final int frameSize = width * height; 
     final int qFrameSize = frameSize/4; 

     System.arraycopy(input, 0, output, 0, frameSize); // Y 

     for (int i = 0; i < qFrameSize; i++) { 
      output[frameSize + i*2] = input[frameSize + i + qFrameSize]; // Cb (U) 
      output[frameSize + i*2 + 1] = input[frameSize + i]; // Cr (V) 
     } 
     return output; 
    } 

या

public static byte[] YV12toYUV420Planar(byte[] input, byte[] output, int width, int height) { 
     /* 
     * COLOR_FormatYUV420Planar is I420 which is like YV12, but with U and V reversed. 
     * So we just have to reverse U and V. 
     */ 
     final int frameSize = width * height; 
     final int qFrameSize = frameSize/4; 

     System.arraycopy(input, 0, output, 0, frameSize); // Y 
     System.arraycopy(input, frameSize, output, frameSize + qFrameSize, qFrameSize); // Cr (V) 
     System.arraycopy(input, frameSize + qFrameSize, output, frameSize, qFrameSize); // Cb (U) 

     return output; 
    } 
+0

बहुत उपयोगी। आपका दूसरा फ़ंक्शन बिल्कुल वही था जो मुझे चाहिए था। YV12 पूर्वावलोकन डेटा को पकड़ने में सक्षम था और इसे MediaCodec एन्कोडर में इनपुट के लिए YUV420Planar में परिवर्तित करने में सक्षम था। –

+0

कुछ इस जवाब में सही नहीं लगता है। पहले कोड में फिसल गया, विधि का नाम "YV12toYUV420PackedSemiPlanar" है। हालांकि, नीचे दी गई पंक्तियों में आप उल्लेख करते हैं कि "COLOR_TI_FormatYUV420PackedSemiPlanar एनवी 12 है"। –

+0

@UtkarshSinha http://www.fourcc.org/yuv.php एक अच्छा पढ़ा, एनवी 12 _is_ YUV420। मेरी समस्या बहुत कम फ्रेम बचा रही थी।अब मुझे रंग की जांच करने के लिए सूर्य 2moro देखने की जरूरत है। – John

2

आप स्पष्ट रूप से एक और पिक्सेल प्रारूप का अनुरोध नहीं किया है, तो कैमरा पूर्वावलोकन बफ़र्स एक YUV 420 NV21 रूप में जाना जाता प्रारूप में आ जाएगा, जिसके लिए COLOR_FormatYCrYCb है MediaCodec समकक्ष।

दुर्भाग्यवश, इस पृष्ठ के अन्य उत्तरों के अनुसार, इस बात की कोई गारंटी नहीं है कि आपके डिवाइस पर, एवीसी एन्कोडर इस प्रारूप का समर्थन करता है। ध्यान दें कि कुछ अजीब डिवाइस मौजूद हैं जो एनवी 21 का समर्थन नहीं करते हैं, लेकिन मुझे एपीआई 16 में अपग्रेड नहीं किया जा सकता है (इसलिए, मीडियाकोडेक है)।

गूगल प्रलेखन भी दावा है कि YV12 प्लानर YUV एपीआई के साथ सभी उपकरणों के लिए कैमरा पूर्वावलोकन प्रारूप के रूप में समर्थन किया जाना चाहिए> = 12. इसलिए, यह यह कोशिश करने के लिए उपयोगी हो सकता है (MediaCodec बराबर COLOR_FormatYUV420Planar जो आप अपने कोड स्निपेट में उपयोग है)।

अपडेट करें: क्योंकि एंड्रयू कॉटरेल ने मुझे याद दिलाया है, वाईवी 12 को अभी भी COLOR_FormatYUV420Planar बनने के लिए क्रोमा स्वैपिंग की आवश्यकता है।

+0

मेरे अनुभव में, वाईवी 12 पूर्वावलोकन डेटा और वाईयूवी 420 प्लानर प्रारूप बराबर नहीं हैं। मुझे इस पृष्ठ पर एक से दूसरे में कनवर्ट करने के लिए YV12toYUV420Planar() फ़ंक्शन का उपयोग करना था। –

+1

@AndrewCottrell: सुधार के लिए धन्यवाद! –

7

एंड्रॉइड 4.3 (एपीआई 18) एक आसान समाधान प्रदान करता है। MediaCodec कक्षा अब सर्फस से इनपुट स्वीकार करती है, जिसका अर्थ है कि आप कैमरे के भूतल पूर्वावलोकन को एन्कोडर से कनेक्ट कर सकते हैं और सभी अजीब वाईयूवी प्रारूप मुद्दों को बाईपास कर सकते हैं।

एक नया MediaMuxer class भी है जो आपके कच्चे H.264 स्ट्रीम को एक .mp4 फ़ाइल में परिवर्तित करेगा (वैकल्पिक रूप से ऑडियो स्ट्रीम में मिश्रण)।

इस तरह से करने के उदाहरण के लिए CameraToMpegTest source देखें। (यह वीडियो पर एक छोटे से संपादन को रिकॉर्ड करने के लिए ओपनजीएल ईएस फ्रैगमेंट शेडर का उपयोग भी प्रदर्शित करता है।)

+0

मैंने कैमरा टॉम्पेगटेस्ट कॉम्पटिबिलिटी टेस्ट सूट उदाहरण को नियमित गतिविधि में संशोधित किया है। https://github.com/OnlyInAmerica/HWEncoderExperiments – dbro

+1

और अब ग्राफिका (https://github.com/google/grafika) में "शो + कैप्चर कैमरा" उदाहरण है जो कैमरे के पूर्वावलोकन को .mp4 पर रिकॉर्ड करता है जबकि साथ ही इसे दिखा रहा है स्क्रीन पर। – fadden

+0

@fadden यह mp4 एक साथ स्ट्रीम किया जा सकता है? – ingsaurabh

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

  • कोई संबंधित समस्या नहीं^_^