8

मैं एक फ़ाइल से एक वीडियो को डिकोड और नए अतुल्यकालिक मोड एपीआई स्तर 21 में समर्थित में किसी भिन्न प्रारूप में यह सांकेतिक शब्दों में बदलना MediaCodec साथ और अप करने के लिए कोशिश कर रहा हूँ (Android ओएस 5.0 Lollipop) ।एंड्रॉयड MediaCodec एनकोड और डीकोड अतुल्यकालिक मोड में

वहाँ तुल्यकालिक मोड में इस तरह के Big Flake के रूप में साइटों पर ऐसा करने के लिए कई उदाहरण गूगल के Grafika, और StackOverflow पर जवाब के दर्जनों, कर रहे हैं, लेकिन उनमें से कोई अतुल्यकालिक विधा का समर्थन।

मुझे प्रक्रिया के दौरान वीडियो प्रदर्शित करने की आवश्यकता नहीं है।

मेरा मानना ​​है कि सामान्य प्रक्रिया है, एक MediaCodec (विकोडक) के लिए इनपुट के रूप में एक MediaExtractor साथ फ़ाइल को पढ़ने के लिए डिकोडर के उत्पादन में एक Surface एक MediaCodec (एनकोडर में भी साझा इनपुट है कि में प्रस्तुत करने के लिए अनुमति देते हैं कि), और फिर अंततः MediaMuxer के माध्यम से एन्कोडर आउटपुट फ़ाइल लिखने के लिए। Surface एन्कोडर के सेटअप के दौरान बनाया गया है और डीकोडर के साथ साझा किया गया है।

मैं वीडियो को TextureView में डीकोड कर सकता हूं, लेकिन स्क्रीन के बजाय एनकोडर के साथ Surface साझा करना सफल नहीं हुआ है।

मैं अपने दोनों कोडेक्स के लिए MediaCodec.Callback() एस सेटअप करता हूं। मेरा मानना ​​है कि एक मुद्दा यह है कि मुझे नहीं पता कि एनकोडर के कॉलबैक के onInputBufferAvailable() फ़ंक्शन में क्या करना है। मैं Surface से एन्कोडर में डेटा कॉपी करने के लिए (या कैसे जानता हूं) नहीं करता - जो स्वचालित रूप से होना चाहिए (जैसा कि codec.releaseOutputBuffer(outputBufferId, true); के साथ डीकोडर आउटपुट पर किया जाता है)। फिर भी, मेरा मानना ​​है कि onInputBufferAvailable को कार्य करने के लिए codec.queueInputBuffer पर कॉल की आवश्यकता है। मुझे नहीं पता कि डीकोड पक्ष पर उपयोग किए जाने वाले MediaExtractor जैसे किसी डेटा से डेटा प्राप्त किए बिना पैरामीटर कैसे सेट करें।

आप एक उदाहरण एक वीडियो फ़ाइल को खोलता है कि, यह डीकोड, एक अलग संकल्प या प्रारूप अतुल्यकालिक MediaCodec कॉलबैक का उपयोग करके करने के लिए वह कूटबद्ध, और फिर एक फ़ाइल के रूप में सहेजता है, तो अपने नमूना कोड साझा करें।

=== संपादित ===

यहाँ मैं क्या अतुल्यकालिक मोड में करने के लिए कोशिश कर रहा हूँ की तुल्यकालिक मोड में एक काम उदाहरण है: ExtractDecodeEditEncodeMuxTest.java: https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java यह उदाहरण अपने आवेदन में काम कर रहा है

Android MediaCodec

+0

पिंग, क्या आपने https://github.com/mstorsjo/android-decodeencodetest पर पूरा उदाहरण देखा था? क्या यह आपके लिए उपयोगी है, या क्या आपको अभी भी इसकी कमी है? – mstorsjo

+0

मैं @ mstorsjo प्रभावित हूँ! ऐसा प्रतीत होता है कि आपने उदाहरण को रूपांतरित किया है, जिसमें से मैंने एन्कोडर और डिकोडर दोनों के लिए गैर-वंचित एसिंक 'MediaCodec.Callback' विधियों का उपयोग करना शुरू किया था जैसा कि मैंने कामना की थी। ऐसा प्रतीत होता है कि आपके सभी परिवर्तन "ExtractDecodeEditEncodeMuxTest.java" तक ही सीमित हैं। लागू करने के बाद मैं रिपोर्ट करूंगा। धन्यवाद। –

+0

हां, अधिकांश परिवर्तन इस फ़ाइल तक ही सीमित हैं। मुझे InputSurface.java में कुछ मामूली परिवर्तन करना पड़ता था, हालांकि (https://github.com/mstorsjo/android-decodeencodetest/commit/0cad97666a278c88a751400a1fdfcdb054b52662 देखें) और आउटपुट Surface.java में एक परिवर्तन (https://github.com/ mstorsjo/android-decodeencodetest/प्रतिबद्ध/4053871ac53807b95c477403403cd2226b5b6a50) जो डीकोडर कॉलबैक को https://github.com/mstorsjo/android-decodeencodetest/commit/23a0621390404785e02a1ae7c24dfb67f9854129 में किसी भिन्न थ्रेड पर ले जाने के बाद आवश्यक नहीं था। – mstorsjo

उत्तर

9

मेरा मानना ​​है कि आप एनकोडर के onInputBufferAvailable() कॉलबैक में कुछ करने की ज़रूरत नहीं करना चाहिए - आप encoder.queueInputBuffer() नहीं बुलाना चाहिए। जैसे ही आप सिंक्रोनस मोड में भूतल इनपुट एन्कोडिंग करते समय encoder.dequeueInputBuffer() और encoder.queueInputBuffer() मैन्युअल रूप से कॉल नहीं करते हैं, आपको इसे एसिंक्रोनस मोड में भी नहीं करना चाहिए।

जब आप decoder.releaseOutputBuffer(outputBufferId, true); फोन (सिंक्रोनस और एसिंक्रोनस दोनों मोड में) इस आंतरिक रूप से (का उपयोग कर Surface आपके द्वारा दी गई), सतह से एक इनपुट बफर dequeues इसे में उत्पादन प्रस्तुत हुई है, और इसे वापस (करने के लिए enqueues सतह के लिए एनकोडर)। सिंक्रोनस और एसिंक्रोनस मोड के बीच एकमात्र अंतर यह है कि कैसे सार्वजनिक एपीआई में बफर घटनाओं का खुलासा किया जाता है, लेकिन सतह इनपुट का उपयोग करते समय, यह एक अलग (आंतरिक) एपीआई का उपयोग करने के लिए उपयोग करता है, इसलिए सिंक्रोनस बनाम एसिंक्रोनस मोड के लिए कोई फर्क नहीं पड़ता यह बिल्कुल

तो जहां तक ​​मुझे पता है (हालांकि मैंने इसे स्वयं नहीं किया है), आपको एन्कोडर के लिए onInputBufferAvailable() कॉलबैक खाली छोड़ना चाहिए।

संपादित करें: तो, मैंने इसे स्वयं करने की कोशिश की, और यह ऊपर वर्णित अनुसार (लगभग) सरल है।

यदि एन्कोडर इनपुट सतह को डीकोडर के आउटपुट के रूप में सीधे कॉन्फ़िगर किया गया है (बिना सतह सतह के बीच), चीजें बस एक सिंक्रोनस डीकोड-एनकोड लूप को एक असीमित में परिवर्तित कर देती हैं।

यदि आप भूतलटेक्चर का उपयोग करते हैं, तो भी, आप एक छोटे गोचा में भाग सकते हैं। कॉलिंग थ्रेड के संबंध में सतह के मिश्रण तक पहुंचने के लिए फ्रेम की प्रतीक्षा करने के साथ कोई समस्या है, इसके संदर्भ के लिए https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java#106 और https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java#104 और https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/OutputSurface.java#113 देखें।

समस्या, जहां तक ​​मैं इसे देखता हूं, awaitNewImage में https://android.googlesource.com/platform/cts/+/jb-mr2-release/tests/tests/media/src/android/media/cts/OutputSurface.java#240 में है। यदि onFrameAvailable कॉलबैक को मुख्य थ्रेड पर कॉल किया जाना है, तो हमारे पास कोई समस्या है यदि awaitNewImage कॉल मुख्य थ्रेड पर भी चलाया जाता है। यदि onOutputBufferAvailable कॉलबैक को मुख्य थ्रेड पर भी कॉल किया जाता है और आप वहां से awaitNewImage पर कॉल करते हैं, तो हमारे पास कोई समस्या है, क्योंकि आप कॉलबैक के लिए प्रतीक्षा कर सकते हैं (wait() के साथ जो पूरे थ्रेड को अवरुद्ध करता है) जिसे तब तक नहीं चलाया जा सकता है वर्तमान विधि रिटर्न।

इसलिए हमें यह सुनिश्चित करने की आवश्यकता है कि onFrameAvailable कॉलबैक awaitNewImage पर कॉल करने वाले किसी भिन्न थ्रेड पर आते हैं। ऐसा करने का एक बहुत ही आसान तरीका एक नया अलग धागा बनाना है, जो onFrameAvailable कॉलबैक सेवा के अलावा कुछ भी नहीं करता है। ऐसा करने के लिए, आप उदाहरण कर सकते हैं इस:

private HandlerThread mHandlerThread = new HandlerThread("CallbackThread"); 
    private Handler mHandler; 
... 
     mHandlerThread.start(); 
     mHandler = new Handler(mHandlerThread.getLooper()); 
... 
     mSurfaceTexture.setOnFrameAvailableListener(this, mHandler); 

मुझे आशा है कि यह पर्याप्त आपकी समस्या का हल करने के लिए, अगर तुम मुझे जरूरत सार्वजनिक उदाहरणों में से एक संपादित करने के लिए वहाँ अतुल्यकालिक कॉलबैक लागू करने के लिए मुझे पता है सक्षम होने के लिए के लिए है।

EDIT2: इसके अलावा, जीएल प्रतिपादन onOutputBufferAvailable कॉलबैक के भीतर से किया जा सकता है के बाद से, यह एक है कि EGL संदर्भ की स्थापना की तुलना में एक अलग धागा हो सकता है। प्रतिपादन से पहले अन्य सूत्र में

mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); 

और पुनः अनुलग्न यह:: तो उस स्थिति में, एक, धागा है कि इसे सेट अप में EGL संदर्भ जारी करने के लिए इस तरह की जरूरत है

mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext); 

EDIT3: इसके अतिरिक्त, यदि एन्कोडर और डिकोडर कॉलबैक एक ही थ्रेड पर प्राप्त होते हैं, तो डीकोडर onOutputBufferAvailable जो प्रतिपादन करता है, एन्कोडर कॉलबैक को वितरित होने से रोक सकता है। अगर उन्हें डिलीवर नहीं किया जाता है, तो प्रतिपादन असीमित रूप से अवरुद्ध किया जा सकता है क्योंकि एन्कोडर को आउटपुट बफर वापस नहीं मिलते हैं। यह सुनिश्चित करके तय किया जा सकता है कि वीडियो डिकोडर कॉलबैक इसके बजाय एक अलग थ्रेड पर प्राप्त किए जाते हैं, और यह इसके बजाय onFrameAvailable कॉलबैक के साथ समस्या से बचाता है।

मैंने इसे ExtractDecodeEditEncodeMuxTest के शीर्ष पर कार्यान्वित करने का प्रयास किया, और यह ठीक से काम कर रहा है, https://github.com/mstorsjo/android-decodeencodetest पर एक नज़र डालें। मैंने शुरुआत में अपरिवर्तित परीक्षण आयात किया, और प्रतिबद्ध लॉग में अलग-अलग फ़िक्सेस को देखना आसान बनाने के लिए, अलग-अलग मुश्किल विवरणों के लिए असीमित मोड और फ़िक्स को रूपांतरण किया।

+0

यह एन्कोडिंग के लिए मैंने देखा सबसे व्यापक उत्तर है, और इसमें ऐसे विवरण शामिल हैं जिन्हें बंदर-बानाना उदाहरणों में से कई में उपेक्षित किया गया है। बहुप्रचारित जीएल संदर्भ स्विचिंग विवरण विशेष रूप से उन्नत होते हैं - हमेशा सुनिश्चित करें कि जीएल संसाधन उसी थ्रेड पर जारी किए गए हैं जो उन्हें बनाए गए हैं। इनमें से किसी भी विवरण की उपेक्षा करने से एएनआर, ओओएम, एनपीई, लाश आदि हो सकते हैं। –

1

मीडियाइन्कोडर में हैंडलर भी सेट कर सकते हैं।

---> ऑडियोइन्कोडर कॉलबैक (aacSamplePreFrameSize), mHandler);


MyAudioCodecWrapper myMediaCodecWrapper;

public MyAudioEncoder(long startRecordWhenNs){ 
    super.startRecordWhenNs = startRecordWhenNs; 
} 

@RequiresApi(api = Build.VERSION_CODES.M) 
public MyAudioCodecWrapper prepareAudioEncoder(AudioRecord _audioRecord , int aacSamplePreFrameSize) throws Exception{ 
    if(_audioRecord==null || aacSamplePreFrameSize<=0) 
     throw new Exception(); 

    audioRecord = _audioRecord; 
    Log.d(TAG, "audioRecord:" + audioRecord.getAudioFormat() + ",aacSamplePreFrameSize:" + aacSamplePreFrameSize); 

    mHandlerThread.start(); 
    mHandler = new Handler(mHandlerThread.getLooper()); 

    MediaFormat audioFormat = new MediaFormat(); 
    audioFormat.setString(MediaFormat.KEY_MIME, MIMETYPE_AUDIO_AAC); 
    //audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); 
    audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC); 
    audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, audioRecord.getSampleRate());//44100 
    audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, audioRecord.getChannelCount());//1(單身道) 
    audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 128000); 
    audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 16384); 
    MediaCodec codec = MediaCodec.createEncoderByType(MIMETYPE_AUDIO_AAC); 
    codec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); 
    codec.setCallback(new AudioEncoderCallback(aacSamplePreFrameSize),mHandler); 
    //codec.start(); 

    MyAudioCodecWrapper myMediaCodecWrapper = new MyAudioCodecWrapper(); 
    myMediaCodecWrapper.mediaCodec = codec; 

    super.mediaCodec = codec; 

    return myMediaCodecWrapper; 

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