Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)
BasicMediaDecoder / src / com.example.android.common.media /

MediaCodecWrapper.java

       
        1
       
       
        /*
       
       
        2
       
       
        * Copyright (C) 2013 The Android Open Source Project
       
       
        3
       
       
        *
       
       
        4
       
       
        * Licensed under the Apache License, Version 2.0 (the "License");
       
       
        5
       
       
        * you may not use this file except in compliance with the License.
       
       
        6
       
       
        * You may obtain a copy of the License at
       
       
        7
       
       
        *
       
       
        8
       
       
        *      http://www.apache.org/licenses/LICENSE-2.0
       
       
        9
       
       
        *
       
       
        10
       
       
        * Unless required by applicable law or agreed to in writing, software
       
       
        11
       
       
        * distributed under the License is distributed on an "AS IS" BASIS,
       
       
        12
       
       
        * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
       
        13
       
       
        * See the License for the specific language governing permissions and
       
       
        14
       
       
        * limitations under the License.
       
       
        15
       
       
        */
       
       
        16
       
       
       
       
        17
       
       
        package com.example.android.common.media;
       
       
        18
       
       
       
       
        19
       
       
        import android.media.*;
       
       
        20
       
       
        import android.os.Handler;
       
       
        21
       
       
        import android.os.Looper;
       
       
        22
       
       
        import android.view.Surface;
       
       
        23
       
       
       
       
        24
       
       
        import java.nio.ByteBuffer;
       
       
        25
       
       
        import java.util.ArrayDeque;
       
       
        26
       
       
        import java.util.Queue;
       
       
        27
       
       
       
       
        28
       
       
        /**
       
       
        29
       
       
        * Simplifies the MediaCodec interface by wrapping around the buffer processing operations.
       
       
        30
       
       
        */
       
       
        31
       
       
        public class MediaCodecWrapper {
       
       
        32
       
       
       
       
        33
       
       
        // Handler to use for {@code OutputSampleListener} and {code OutputFormatChangedListener}
       
       
        34
       
       
        // callbacks
       
       
        35
       
       
        private Handler mHandler;
       
       
        36
       
       
       
       
        37
       
       
       
       
        38
       
       
        // Callback when media output format changes.
       
       
        39
       
       
        public interface OutputFormatChangedListener {
       
       
        40
       
       
        void outputFormatChanged(MediaCodecWrapper sender, MediaFormat newFormat);
       
       
        41
       
       
        }
       
       
        42
       
       
       
       
        43
       
       
        private OutputFormatChangedListener mOutputFormatChangedListener = null;
       
       
        44
       
       
       
       
        45
       
       
        /**
       
       
        46
       
       
        * Callback for decodes frames. Observers can register a listener for optional stream
       
       
        47
       
       
        * of decoded data
       
       
        48
       
       
        */
       
       
        49
       
       
        public interface OutputSampleListener {
       
       
        50
       
       
        void outputSample(MediaCodecWrapper sender, MediaCodec.BufferInfo info, ByteBuffer buffer);
       
       
        51
       
       
        }
       
       
        52
       
       
       
       
        53
       
       
        /**
       
       
        54
       
       
        * The {@link MediaCodec} that is managed by this class.
       
       
        55
       
       
        */
       
       
        56
       
       
        private MediaCodec mDecoder;
       
       
        57
       
       
       
       
        58
       
       
        // References to the internal buffers managed by the codec. The codec
       
       
        59
       
       
        // refers to these buffers by index, never by reference so it's up to us
       
       
        60
       
       
        // to keep track of which buffer is which.
       
       
        61
       
       
        private ByteBuffer[] mInputBuffers;
       
       
        62
       
       
        private ByteBuffer[] mOutputBuffers;
       
       
        63
       
       
       
       
        64
       
       
        // Indices of the input buffers that are currently available for writing. We'll
       
       
        65
       
       
        // consume these in the order they were dequeued from the codec.
       
       
        66
       
       
        private Queue<Integer> mAvailableInputBuffers;
       
       
        67
       
       
       
       
        68
       
       
        // Indices of the output buffers that currently hold valid data, in the order
       
       
        69
       
       
        // they were produced by the codec.
       
       
        70
       
       
        private Queue<Integer> mAvailableOutputBuffers;
       
       
        71
       
       
       
       
        72
       
       
        // Information about each output buffer, by index. Each entry in this array
       
       
        73
       
       
        // is valid if and only if its index is currently contained in mAvailableOutputBuffers.
       
       
        74
       
       
        private MediaCodec.BufferInfo[] mOutputBufferInfo;
       
       
        75
       
       
       
       
        76
       
       
        // An (optional) stream that will receive decoded data.
       
       
        77
       
       
        private OutputSampleListener mOutputSampleListener;
       
       
        78
       
       
       
       
        79
       
       
        private MediaCodecWrapper(MediaCodec codec) {
       
       
        80
       
       
        mDecoder = codec;
       
       
        81
       
       
        codec.start();
       
       
        82
       
       
        mInputBuffers = codec.getInputBuffers();
       
       
        83
       
       
        mOutputBuffers = codec.getOutputBuffers();
       
       
        84
       
       
        mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length];
       
       
        85
       
       
        mAvailableInputBuffers = new ArrayDeque<Integer>(mOutputBuffers.length);
       
       
        86
       
       
        mAvailableOutputBuffers = new ArrayDeque<Integer>(mInputBuffers.length);
       
       
        87
       
       
        }
       
       
        88
       
       
       
       
        89
       
       
        /**
       
       
        90
       
       
        * Releases resources and ends the encoding/decoding session.
       
       
        91
       
       
        */
       
       
        92
       
       
        public void stopAndRelease() {
       
       
        93
       
       
        mDecoder.stop();
       
       
        94
       
       
        mDecoder.release();
       
       
        95
       
       
        mDecoder = null;
       
       
        96
       
       
        mHandler = null;
       
       
        97
       
       
        }
       
       
        98
       
       
       
       
        99
       
       
        /**
       
       
        100
       
       
        * Getter for the registered {@link OutputFormatChangedListener}
       
       
        101
       
       
        */
       
       
        102
       
       
        public OutputFormatChangedListener getOutputFormatChangedListener() {
       
       
        103
       
       
        return mOutputFormatChangedListener;
       
       
        104
       
       
        }
       
       
        105
       
       
       
       
        106
       
       
        /**
       
       
        107
       
       
        *
       
       
        108
       
       
        * @param outputFormatChangedListener the listener for callback.
       
       
        109
       
       
        * @param handler message handler for posting the callback.
       
       
        110
       
       
        */
       
       
        111
       
       
        public void setOutputFormatChangedListener(final OutputFormatChangedListener
       
       
        112
       
       
        outputFormatChangedListener, Handler handler) {
       
       
        113
       
       
        mOutputFormatChangedListener = outputFormatChangedListener;
       
       
        114
       
       
       
       
        115
       
       
        // Making sure we don't block ourselves due to a bad implementation of the callback by
       
       
        116
       
       
        // using a handler provided by client.
       
       
        117
       
       
        Looper looper;
       
       
        118
       
       
        mHandler = handler;
       
       
        119
       
       
        if (outputFormatChangedListener != null && mHandler == null) {
       
       
        120
       
       
        if ((looper = Looper.myLooper()) != null) {
       
       
        121
       
       
        mHandler = new Handler();
       
       
        122
       
       
        } else {
       
       
        123
       
       
        throw new IllegalArgumentException(
       
       
        124
       
       
        "Looper doesn't exist in the calling thread");
       
       
        125
       
       
        }
       
       
        126
       
       
        }
       
       
        127
       
       
        }
       
       
        128
       
       
       
       
        129
       
       
        /**
       
       
        130
       
       
        * Constructs the {@link MediaCodecWrapper} wrapper object around the video codec.
       
       
        131
       
       
        * The codec is created using the encapsulated information in the
       
       
        132
       
       
        * {@link MediaFormat} object.
       
       
        133
       
       
        *
       
       
        134
       
       
        * @param trackFormat The format of the media object to be decoded.
       
       
        135
       
       
        * @param surface Surface to render the decoded frames.
       
       
        136
       
       
        * @return
       
       
        137
       
       
        */
       
       
        138
       
       
        public static MediaCodecWrapper fromVideoFormat(final MediaFormat trackFormat,
       
       
        139
       
       
        Surface surface) {
       
       
        140
       
       
        MediaCodecWrapper result = null;
       
       
        141
       
       
        MediaCodec videoCodec = null;
       
       
        142
       
       
       
       
        144
       
       
        final String mimeType = trackFormat.getString(MediaFormat.KEY_MIME);
       
       
        145
       
       
       
       
        146
       
       
        // Check to see if this is actually a video mime type. If it is, then create
       
       
        147
       
       
        // a codec that can decode this mime type.
       
       
        148
       
       
        if (mimeType.contains("video/")) {
       
       
        149
       
       
        videoCodec = MediaCodec.createDecoderByType(mimeType);
       
       
        150
       
       
        videoCodec.configure(trackFormat, surface, null,  0);
       
       
        151
       
       
       
       
        152
       
       
        }
       
       
        153
       
       
       
       
        154
       
       
        // If codec creation was successful, then create a wrapper object around the
       
       
        155
       
       
        // newly created codec.
       
       
        156
       
       
        if (videoCodec != null) {
       
       
        157
       
       
        result = new MediaCodecWrapper(videoCodec);
       
       
        158
       
       
        }
       
       
        160
       
       
       
       
        161
       
       
        return result;
       
       
        162
       
       
        }
       
       
        163
       
       
       
       
        164
       
       
       
       
        165
       
       
        /**
       
       
        166
       
       
        * Write a media sample to the decoder.
       
       
        167
       
       
        *
       
       
        168
       
       
        * A "sample" here refers to a single atomic access unit in the media stream. The definition
       
       
        169
       
       
        * of "access unit" is dependent on the type of encoding used, but it typically refers to
       
       
        170
       
       
        * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor}
       
       
        171
       
       
        * extracts data from a stream one sample at a time.
       
       
        172
       
       
        *
       
       
        173
       
       
        * @param input A ByteBuffer containing the input data for one sample. The buffer must be set
       
       
        174
       
       
        * up for reading, with its position set to the beginning of the sample data and its limit
       
       
        175
       
       
        * set to the end of the sample data.
       
       
        176
       
       
        *
       
       
        177
       
       
        * @param presentationTimeUs  The time, relative to the beginning of the media stream,
       
       
        178
       
       
        * at which this buffer should be rendered.
       
       
        179
       
       
        *
       
       
        180
       
       
        * @param flags Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
       
       
        181
       
       
        * int, int, long, int)}
       
       
        182
       
       
        *
       
       
        183
       
       
        * @throws MediaCodec.CryptoException
       
       
        184
       
       
        */
       
       
        185
       
       
        public boolean writeSample(final ByteBuffer input,
       
       
        186
       
       
        final MediaCodec.CryptoInfo crypto,
       
       
        187
       
       
        final long presentationTimeUs,
       
       
        188
       
       
        final int flags) throws MediaCodec.CryptoException, WriteException {
       
       
        189
       
       
        boolean result = false;
       
       
        190
       
       
        int size = input.remaining();
       
       
        191
       
       
       
       
        192
       
       
        // check if we have dequed input buffers available from the codec
       
       
        193
       
       
        if (size > 0 &&  !mAvailableInputBuffers.isEmpty()) {
       
       
        194
       
       
        int index = mAvailableInputBuffers.remove();
       
       
        195
       
       
        ByteBuffer buffer = mInputBuffers[index];
       
       
        196
       
       
       
       
        197
       
       
        // we can't write our sample to a lesser capacity input buffer.
       
       
        198
       
       
        if (size > buffer.capacity()) {
       
       
        199
       
       
        throw new MediaCodecWrapper.WriteException(String.format(
       
       
        200
       
       
        "Insufficient capacity in MediaCodec buffer: "
       
       
        201
       
       
        + "tried to write %d, buffer capacity is %d.",
       
       
        202
       
       
        input.remaining(),
       
       
        203
       
       
        buffer.capacity()));
       
       
        204
       
       
        }
       
       
        205
       
       
       
       
        206
       
       
        buffer.clear();
       
       
        207
       
       
        buffer.put(input);
       
       
        208
       
       
       
       
        209
       
       
        // Submit the buffer to the codec for decoding. The presentationTimeUs
       
       
        210
       
       
        // indicates the position (play time) for the current sample.
       
       
        211
       
       
        if (crypto == null) {
       
       
        212
       
       
        mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
       
       
        213
       
       
        } else {
       
       
        214
       
       
        mDecoder.queueSecureInputBuffer(index, 0, crypto, presentationTimeUs, flags);
       
       
        215
       
       
        }
       
       
        216
       
       
        result = true;
       
       
        217
       
       
        }
       
       
        218
       
       
        return result;
       
       
        219
       
       
        }
       
       
        220
       
       
       
       
        221
       
       
        static MediaCodec.CryptoInfo cryptoInfo= new MediaCodec.CryptoInfo();
       
       
        222
       
       
       
       
        223
       
       
        /**
       
       
        224
       
       
        * Write a media sample to the decoder.
       
       
        225
       
       
        *
       
       
        226
       
       
        * A "sample" here refers to a single atomic access unit in the media stream. The definition
       
       
        227
       
       
        * of "access unit" is dependent on the type of encoding used, but it typically refers to
       
       
        228
       
       
        * a single frame of video or a few seconds of audio. {@link android.media.MediaExtractor}
       
       
        229
       
       
        * extracts data from a stream one sample at a time.
       
       
        230
       
       
        *
       
       
        231
       
       
        * @param extractor  Instance of {@link android.media.MediaExtractor} wrapping the media.
       
       
        232
       
       
        *
       
       
        233
       
       
        * @param presentationTimeUs The time, relative to the beginning of the media stream,
       
       
        234
       
       
        * at which this buffer should be rendered.
       
       
        235
       
       
        *
       
       
        236
       
       
        * @param flags  Flags to pass to the decoder. See {@link MediaCodec#queueInputBuffer(int,
       
       
        237
       
       
        * int, int, long, int)}
       
       
        238
       
       
        *
       
       
        239
       
       
        * @throws MediaCodec.CryptoException
       
       
        240
       
       
        */
       
       
        241
       
       
        public boolean writeSample(final MediaExtractor extractor,
       
       
        242
       
       
        final boolean isSecure,
       
       
        243
       
       
        final long presentationTimeUs,
       
       
        244
       
       
        int flags) {
       
       
        245
       
       
        boolean result = false;
       
       
        246
       
       
        boolean isEos = false;
       
       
        247
       
       
       
       
        248
       
       
        if (!mAvailableInputBuffers.isEmpty()) {
       
       
        249
       
       
        int index = mAvailableInputBuffers.remove();
       
       
        250
       
       
        ByteBuffer buffer = mInputBuffers[index];
       
       
        251
       
       
       
       
        252
       
       
        // reads the sample from the file using extractor into the buffer
       
       
        253
       
       
        int size = extractor.readSampleData(buffer, 0);
       
       
        254
       
       
        if (size <= 0) {
       
       
        255
       
       
        flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
       
       
        256
       
       
        }
       
       
        257
       
       
       
       
        258
       
       
        // Submit the buffer to the codec for decoding. The presentationTimeUs
       
       
        259
       
       
        // indicates the position (play time) for the current sample.
       
       
        260
       
       
        if (!isSecure) {
       
       
        261
       
       
        mDecoder.queueInputBuffer(index, 0, size, presentationTimeUs, flags);
       
       
        262
       
       
        } else {
       
       
        263
       
       
        extractor.getSampleCryptoInfo(cryptoInfo);
       
       
        264
       
       
        mDecoder.queueSecureInputBuffer(index, 0, cryptoInfo, presentationTimeUs, flags);
       
       
        265
       
       
        }
       
       
        266
       
       
       
       
        267
       
       
        result = true;
       
       
        268
       
       
        }
       
       
        269
       
       
        return result;
       
       
        270
       
       
        }
       
       
        271
       
       
       
       
        272
       
       
        /**
       
       
        273
       
       
        * Performs a peek() operation in the queue to extract media info for the buffer ready to be
       
       
        274
       
       
        * released i.e. the head element of the queue.
       
       
        275
       
       
        *
       
       
        276
       
       
        * @param out_bufferInfo An output var to hold the buffer info.
       
       
        277
       
       
        *
       
       
        278
       
       
        * @return True, if the peek was successful.
       
       
        279
       
       
        */
       
       
        280
       
       
        public boolean peekSample(MediaCodec.BufferInfo out_bufferInfo) {
       
       
        281
       
       
        // dequeue available buffers and synchronize our data structures with the codec.
       
       
        282
       
       
        update();
       
       
        283
       
       
        boolean result = false;
       
       
        284
       
       
        if (!mAvailableOutputBuffers.isEmpty()) {
       
       
        285
       
       
        int index = mAvailableOutputBuffers.peek();
       
       
        286
       
       
        MediaCodec.BufferInfo info = mOutputBufferInfo[index];
       
       
        287
       
       
        // metadata of the sample
       
       
        288
       
       
        out_bufferInfo.set(
       
       
        289
       
       
        info.offset,
       
       
        290
       
       
        info.size,
       
       
        291
       
       
        info.presentationTimeUs,
       
       
        292
       
       
        info.flags);
       
       
        293
       
       
        result = true;
       
       
        294
       
       
        }
       
       
        295
       
       
        return result;
       
       
        296
       
       
        }
       
       
        297
       
       
       
       
        298
       
       
        /**
       
       
        299
       
       
        * Processes, releases and optionally renders the output buffer available at the head of the
       
       
        300
       
       
        * queue. All observers are notified with a callback. See {@link
       
       
        301
       
       
        * OutputSampleListener#outputSample(MediaCodecWrapper, android.media.MediaCodec.BufferInfo,
       
       
        302
       
       
        * java.nio.ByteBuffer)}
       
       
        303
       
       
        *
       
       
        304
       
       
        * @param render True, if the buffer is to be rendered on the {@link Surface} configured
       
       
        305
       
       
        *
       
       
        306
       
       
        */
       
       
        307
       
       
        public void popSample(boolean render) {
       
       
        308
       
       
        // dequeue available buffers and synchronize our data structures with the codec.
       
       
        309
       
       
        update();
       
       
        310
       
       
        if (!mAvailableOutputBuffers.isEmpty()) {
       
       
        311
       
       
        int index = mAvailableOutputBuffers.remove();
       
       
        312
       
       
       
       
        313
       
       
        if (render && mOutputSampleListener != null) {
       
       
        314
       
       
        ByteBuffer buffer = mOutputBuffers[index];
       
       
        315
       
       
        MediaCodec.BufferInfo info = mOutputBufferInfo[index];
       
       
        316
       
       
        mOutputSampleListener.outputSample(this, info, buffer);
       
       
        317
       
       
        }
       
       
        318
       
       
       
       
        319
       
       
        // releases the buffer back to the codec
       
       
        320
       
       
        mDecoder.releaseOutputBuffer(index, render);
       
       
        321
       
       
        }
       
       
        322
       
       
        }
       
       
        323
       
       
       
       
        324
       
       
        /**
       
       
        325
       
       
        * Synchronize this object's state with the internal state of the wrapped
       
       
        326
       
       
        * MediaCodec.
       
       
        327
       
       
        */
       
       
        328
       
       
        private void update() {
       
       
        330
       
       
        int index;
       
       
        331
       
       
       
       
        332
       
       
        // Get valid input buffers from the codec to fill later in the same order they were
       
       
        333
       
       
        // made available by the codec.
       
       
        334
       
       
        while ((index = mDecoder.dequeueInputBuffer(0)) != MediaCodec.INFO_TRY_AGAIN_LATER) {
       
       
        335
       
       
        mAvailableInputBuffers.add(index);
       
       
        336
       
       
        }
       
       
        337
       
       
       
       
        338
       
       
       
       
        339
       
       
        // Likewise with output buffers. If the output buffers have changed, start using the
       
       
        340
       
       
        // new set of output buffers. If the output format has changed, notify listeners.
       
       
        341
       
       
        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
       
       
        342
       
       
        while ((index = mDecoder.dequeueOutputBuffer(info, 0)) !=  MediaCodec.INFO_TRY_AGAIN_LATER) {
       
       
        343
       
       
        switch (index) {
       
       
        344
       
       
        case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
       
       
        345
       
       
        mOutputBuffers = mDecoder.getOutputBuffers();
       
       
        346
       
       
        mOutputBufferInfo = new MediaCodec.BufferInfo[mOutputBuffers.length];
       
       
        347
       
       
        mAvailableOutputBuffers.clear();
       
       
        348
       
       
        break;
       
       
        349
       
       
        case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
       
       
        350
       
       
        if (mOutputFormatChangedListener != null) {
       
       
        351
       
       
        mHandler.post(new Runnable() {
       
       
        352
       
       
        @Override
       
       
        353
       
       
        public void run() {
       
       
        354
       
       
        mOutputFormatChangedListener
       
       
        355
       
       
        .outputFormatChanged(MediaCodecWrapper.this,
       
       
        356
       
       
        mDecoder.getOutputFormat());
       
       
        357
       
       
       
       
        358
       
       
        }
       
       
        359
       
       
        });
       
       
        360
       
       
        }
       
       
        361
       
       
        break;
       
       
        362
       
       
        default:
       
       
        363
       
       
        // Making sure the index is valid before adding to output buffers. We've already
       
       
        364
       
       
        // handled INFO_TRY_AGAIN_LATER, INFO_OUTPUT_FORMAT_CHANGED &
       
       
        365
       
       
        // INFO_OUTPUT_BUFFERS_CHANGED i.e all the other possible return codes but
       
       
        366
       
       
        // asserting index value anyways for future-proofing the code.
       
       
        367
       
       
        if(index >= 0) {
       
       
        368
       
       
        mOutputBufferInfo[index] = info;
       
       
        369
       
       
        mAvailableOutputBuffers.add(index);
       
       
        370
       
       
        } else {
       
       
        371
       
       
        throw new IllegalStateException("Unknown status from dequeueOutputBuffer");
       
       
        372
       
       
        }
       
       
        373
       
       
        break;
       
       
        374
       
       
        }
       
       
        375
       
       
       
       
        376
       
       
        }
       
       
        378
       
       
       
       
        379
       
       
        }
       
       
        380
       
       
       
       
        381
       
       
        private class WriteException extends Throwable {
       
       
        382
       
       
        private WriteException(final String detailMessage) {
       
       
        383
       
       
        super(detailMessage);
       
       
        384
       
       
        }
       
       
        385
       
       
        }
       
       
        386
       
       
        }