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