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