1 /* 2 * Copyright (C) 2017 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 package com.google.android.exoplayer2.ext.ffmpeg; 17 18 import android.content.Context; 19 import android.content.pm.PackageManager; 20 import com.google.android.exoplayer2.decoder.DecoderInputBuffer; 21 import com.google.android.exoplayer2.decoder.SimpleOutputBuffer; 22 import com.google.android.exoplayer2.util.MimeTypes; 23 import com.android.tv.common.SoftPreconditions; 24 25 import java.nio.ByteBuffer; 26 27 /** 28 * Audio decoder which uses ffmpeg extension of ExoPlayer2. Since {@link FfmpegDecoder} is package 29 * private, expose the decoder via this class. Supported formats are AC3 and MP2. 30 */ 31 public class FfmpegAudioDecoder { 32 private static final int NUM_DECODER_BUFFERS = 1; 33 34 // The largest AC3 sample size. This is bigger than the largest MP2 sample size (1729). 35 private static final int INITIAL_INPUT_BUFFER_SIZE = 2560; 36 private static boolean AVAILABLE; 37 38 static { 39 AVAILABLE = 40 FfmpegLibrary.supportsFormat(MimeTypes.AUDIO_AC3) 41 && FfmpegLibrary.supportsFormat(MimeTypes.AUDIO_MPEG_L2); 42 } 43 44 private FfmpegDecoder mDecoder; 45 private DecoderInputBuffer mInputBuffer; 46 private SimpleOutputBuffer mOutputBuffer; 47 private boolean mStarted; 48 49 /** Return whether Ffmpeg based software audio decoder is available. */ isAvailable()50 public static boolean isAvailable() { 51 return AVAILABLE; 52 } 53 54 /** Creates an Ffmpeg based software audio decoder. */ FfmpegAudioDecoder(Context context)55 public FfmpegAudioDecoder(Context context) { 56 if (context.checkSelfPermission("android.permission.INTERNET") 57 == PackageManager.PERMISSION_GRANTED) { 58 throw new IllegalStateException("This code should run in an isolated process"); 59 } 60 } 61 62 /** 63 * Decodes an audio sample. 64 * 65 * @param timeUs presentation timestamp of the sample 66 * @param sample data 67 */ decode(long timeUs, byte[] sample)68 public void decode(long timeUs, byte[] sample) { 69 SoftPreconditions.checkState(AVAILABLE); 70 mInputBuffer.data.clear(); 71 mInputBuffer.data.put(sample); 72 mInputBuffer.data.flip(); 73 mInputBuffer.timeUs = timeUs; 74 mDecoder.decode(mInputBuffer, mOutputBuffer, !mStarted); 75 if (!mStarted) { 76 mStarted = true; 77 } 78 } 79 80 /** Returns a decoded sample from decoder. */ getDecodedSample()81 public ByteBuffer getDecodedSample() { 82 return mOutputBuffer.data; 83 } 84 85 /** Returns the presentation time for the decoded sample. */ getDecodedTimeUs()86 public long getDecodedTimeUs() { 87 return mOutputBuffer.timeUs; 88 } 89 90 /** 91 * Clear previous decode state if any. Prepares to decode samples of the specified encoding. 92 * This method should be called before using decode. 93 * 94 * @param mime audio encoding 95 */ resetDecoderState(String mime)96 public void resetDecoderState(String mime) { 97 SoftPreconditions.checkState(AVAILABLE); 98 release(); 99 try { 100 mDecoder = 101 new FfmpegDecoder( 102 NUM_DECODER_BUFFERS, 103 NUM_DECODER_BUFFERS, 104 INITIAL_INPUT_BUFFER_SIZE, 105 mime, 106 null); 107 mStarted = false; 108 mInputBuffer = mDecoder.createInputBuffer(); 109 // Since native JNI requires direct buffer, we should allocate it by #allocateDirect. 110 mInputBuffer.data = ByteBuffer.allocateDirect(INITIAL_INPUT_BUFFER_SIZE); 111 mOutputBuffer = mDecoder.createOutputBuffer(); 112 } catch (FfmpegDecoderException e) { 113 // if AVAILABLE is {@code true}, this will not happen. 114 } 115 } 116 117 /** Releases all the resource. */ release()118 public void release() { 119 SoftPreconditions.checkState(AVAILABLE); 120 if (mDecoder != null) { 121 mDecoder.release(); 122 mInputBuffer = null; 123 mOutputBuffer = null; 124 mDecoder = null; 125 } 126 } 127 } 128