android सरक उच्च गुणवत्ता स्ट्रीम पर मीडियाकोडक दुर्घटना



सरकार की ओर से दुर्घटना में मृत्यु मुआवजा (1)

मेरे एंड्रॉइड एच 264 विकोडक के लिए मैं इसे आपके सेटअप से कुछ अलग करता हूं। मुझे लगता है कि आपका अधिक आधुनिक एपीआई स्तर का उपयोग करना लेकिन मेरे लिए यह इस तरह दिखता है:

public void startDecoder() {
    // Initilize codec
    mediaCodec = MediaCodec.createDecoderByType("video/avc");
    mediaFormat = MediaFormat.createVideoFormat("video/avc", 0, 0);
    bufferInfo = new MediaCodec.BufferInfo();

    // STOPS unit-tests from crashing here from mocked out android
    if (mediaCodec != null) {
        mediaCodec.configure(mediaFormat, targetSurface, null, 0);
        mediaCodec.start();
        decoderThread = new Thread(this);
        decoderThread.start();
    }
}

// डीकोडर थ्रेड इस वर्ग को संदर्भित करता है जो डिकोडर / रेंडर लूप करता है:

public void run() {
    //mediaCodec input + output dequeue timeouts
    long kInputBufferTimeoutMs = 50;
    long kOutputBufferTimeoutMs = 50;

    while (running && mediaCodec != null) {
        synchronized (mediaCodec) {
            // stop if not running.
            if (!running || mediaCodec == null)
                break;

            // Only push in new data if there is data available in the queue
            if (naluSegmentQueue.size() > 0) {
                int inputBufferIndex = mediaCodec.dequeueInputBuffer(kInputBufferTimeoutMs);
                if (inputBufferIndex >= 0) {
                    NaluSegment segment = naluSegmentQueue.poll();
                    codecInputBufferAvailable(segment, mediaCodec, inputBufferIndex);
                }
            }

            // always check if output is available.
            int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, kOutputBufferTimeoutMs);
            if (outputBufferIndex >= 0) {
                // Try and render first
                codecOuputBufferAvailable(mediaCodec, outputBufferIndex, bufferInfo);
            } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                // Subsequent data will conform to new format.
                // Can ignore if using getOutputFormat(outputBufferId)
                mediaFormat = mediaCodec.getOutputFormat();
            }
        }
    }
}

मापदंडों सहित डिकोडर में डेटा डालने के लिए मैं सीएसडी-0/1 नेटवर्क धाराओं का उपयोग करने की कोशिश करने से परेशान नहीं हूं, प्रारूप स्वरूप बदल सकता है और यह आसानी से इसे गतिशील रूप से उठाया जा सकता है

private void codecInputBufferAvailable(NaluSegment segment, MediaCodec codec, int index) {
    int flags = (segment.getType() == NaluType.SPS
            || segment.getType() == NaluType.PPS
            || segment.getType() == NaluType.SUPP_ENHANCEMENT) ?
            MediaCodec.BUFFER_FLAG_CODEC_CONFIG : MediaCodec.BUFFER_FLAG_SYNC_FRAME;

    ByteBuffer[] buffers = codec.getInputBuffers();
    ByteBuffer buffer = buffers[index];
    // Can throw buffer overflow exception when buffer sizes are too small.
    try {
        buffer.put(segment.getBuffer());
        codec.queueInputBuffer(index, 0, segment.getBufferSize(), 0, flags);
    } catch(Exception e) {
        Log.e(TAG, "Failed to push buffer to decoder");
    }
}

महत्वपूर्ण : बफ़र.पूट (सेगमेंट। बैटफर ()); getBuffer () यहां हमेशा एक 4 बाइट annexb बफर देता है एंड्रॉइड डिकोडर्स को 3 बाइट नल यूनिट्स नहीं समझते हैं। इसलिए यदि आपके पास 3 बाइट एनएएल इकाई है, तो इसे 4 बाइट्स के जादू अनुक्रम की लंबाई + 1 और 0x00, 0x00, 0x00, 0x01 के साथ शुरू की जाज अनुक्रम के रूप में बफर के बाकी बफर और [हेडर लैंप] होना चाहिए।

कोशिश-पकड़ यहाँ देखें कि यह संकलक चेतावनी नहीं देता है, लेकिन यह एक बफर अतिप्रवाह अपवाद फेंक सकता है यदि आपके पास बहुत बड़ा पेलोड है और बाइट-बफ़र बहुत छोटा है।

जब तक आपके एनएएल इकाइयों को सही तरीके से अपना पार्स ठीक से ही आपके लिए काम करना चाहिए। लेकिन मेरे मामले के लिए मैंने देखा कि एनएएल इकाइयां जादू हेडर के लिए 3 या 4 बाइट्स हो सकती हैं।

/**
 * H264 is comprised of NALU segments.
 *
 * XXXX Y ZZZZZZZZ -> XXXX Y ZZZZZZZZ -> XXXX Y ZZZZZZZZ
 *
 * Each segment is comprised of:
 *
 * XXXX   -> Magic byte header (0x00, 0x00, 0x00, 0x01) NOTE: this can be either 3 of 4 bytes
 * Y      -> The Nalu Type
 * ZZZ... -> The Payload
 *
 * Notice there is no nalu length specified. To parse an nalu, you must
 * read until the next magic-byte-sequence AKA the next segment to figure
 * out the full nalu length
 **/
public static List<NaluSegment> parseNaluSegments(byte[] buffer) throws NaluBufferException {
    List<NaluSegment> segmentList = new ArrayList<>();
    if (buffer.length < 6) {
        return segmentList;
    }

    int lastStartingOffset = -1;
    for (int i = 0; i < buffer.length - 10; ++i) {
        **if (buffer[i] == 0x00 && buffer[i+1] == 0x00 && buffer[i+2] == 0x01)** {
            int naluType = (buffer[i+3] & 0x1F);
            NaluSegment segment = new NaluSegment(naluType, 3, i);

            **if (i > 0 && buffer[i-1] == 0x00)** {
                // This is actually a 4 byte segment
                int currentSegmentOffset = segment.getOffset();
                segment.setHeaderSize(4);
                segment.setOffset(currentSegmentOffset - 1);
            }
...

अपनी खुद की न्यूलू सेगमेंट ऑब्जेक्ट्स बनाएँ और पीछे की ओर एनएएल को मत भूलें।

आशा है कि ये आपकी मदद करेगा।

मैं निम्नलिखित कोड (मूल गाइड) के साथ एक एच 264 वीडियो स्ट्रीम डीकोड कर रहा हूं:

public void configure(Surface surface, int width, int height, ByteBuffer csd0) {
        String VIDEO_FORMAT = "video/avc";
        if (mConfigured) {
            throw new IllegalStateException("Decoder is already configured");
        }
        MediaFormat format = MediaFormat.createVideoFormat(VIDEO_FORMAT, width, height);
        // little tricky here, csd-0 is required in order to configure the codec properly
        // it is basically the first sample from encoder with flag: BUFFER_FLAG_CODEC_CONFIG
        format.setByteBuffer("csd-0", csd0);
        try {
            mCodec = MediaCodec.createDecoderByType(VIDEO_FORMAT);
        } catch (IOException e) {
            throw new RuntimeException("Failed to create codec", e);
        }
        mCodec.configure(format, surface, null, 0);
        mCodec.start();
        mConfigured = true;
    }

    @SuppressWarnings("deprecation")
    public void decodeSample(byte[] data, int offset, int size, long presentationTimeUs, int flags) {
        if (mConfigured && mRunning) {
            int index = mCodec.dequeueInputBuffer(mTimeoutUs);
            if (index >= 0) {
                ByteBuffer buffer;
                // since API 21 we have new API to use
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                    buffer = mCodec.getInputBuffers()[index];
                    buffer.clear();
                } else {
                    buffer = mCodec.getInputBuffer(index);
                }
                if (buffer != null) {
                    buffer.put(data, offset, size);
                    mCodec.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
                }
            }
        }
    }

    @Override
    public void run() {
        try {
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            while (mRunning) {
                if (mConfigured) {
                    int index = mCodec.dequeueOutputBuffer(info, mTimeoutUs);
                    if (index >= 0) {
                        // setting true is telling system to render frame onto Surface
                        mCodec.releaseOutputBuffer(index, true);
                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                            break;
                        }
                    }
                } else {
                    // just waiting to be configured, then decode and render
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException ignore) {
                    }
                }
            }
        } finally {
            if (mConfigured) {
                mCodec.stop();
                mCodec.release();
            }
        }
    }

मैं इसे अपने नेक्सस 6 (एपीआई 22) और सैमसंग गैलेक्सी कोर (एपीआई 16) दोनों को कम और मध्यम गुणवत्ता पर चला सकते हैं। हालांकि, जब मैं उच्च गुणवत्ता (720p) पर स्विच करता हूं तो सैमसंग पर 30 फ्रेम के बाद क्रैश होता है (लेकिन स्क्रीन पर कुछ नहीं दिया जाता है)।

E/ACodec [OMX.qcom.video.decoder.avc] ERROR(0x8000100a)
E/MediaCodec Codec reported an error. (omx error 0x8000100a, internalError -2147483648)
[...]
W/System.err java.lang.IllegalStateException
W/System.err at android.media.MediaCodec.dequeueInputBuffer(Native Method)
W/System.err at com.test.stream.VideoDecoder$Worker.decodeSample(VideoDecoder.java:95)
W/System.err at com.test.stream.VideoDecoder.decodeSample(VideoDecoder.java:24)
W/System.err at com.test.stream.VideoThread.run(VideoThread.java:160)

उपरोक्त त्रुटि दिखाई देने वाली पहली त्रुटि है, IllegalStateException बाद में प्रत्येक फ्रेम पर फेंक दिया जाता है

मेरा प्रश्न यह है, क्या यह डिवाइस विशिष्ट समस्या है (क्योंकि: पुरानी एपीआई / डिवाइस, कम शक्तिशाली, आदि।) या वास्तव में कुछ गलत है? और मुझे इस से कैसे निपटना चाहिए?





decoder