Home | History | Annotate | Download | only in ffmpeg
      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. */
     50     public static boolean isAvailable() {
     51         return AVAILABLE;
     52     }
     53 
     54     /** Creates an Ffmpeg based software audio decoder. */
     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      */
     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. */
     81     public ByteBuffer getDecodedSample() {
     82         return mOutputBuffer.data;
     83     }
     84 
     85     /** Returns the presentation time for the decoded sample. */
     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      */
     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. */
    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