• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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