Home | History | Annotate | Download | only in media
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 package org.chromium.media;
      6 
      7 import android.content.Context;
      8 import android.media.AudioFormat;
      9 import android.media.MediaCodec;
     10 import android.media.MediaCodec.BufferInfo;
     11 import android.media.MediaExtractor;
     12 import android.media.MediaFormat;
     13 import android.os.ParcelFileDescriptor;
     14 import android.util.Log;
     15 
     16 import java.io.File;
     17 import java.nio.ByteBuffer;
     18 
     19 import org.chromium.base.CalledByNative;
     20 import org.chromium.base.JNINamespace;
     21 
     22 @JNINamespace("media")
     23 class WebAudioMediaCodecBridge {
     24     private static final boolean DEBUG = true;
     25     static final String LOG_TAG = "WebAudioMediaCodec";
     26     // TODO(rtoy): What is the correct timeout value for reading
     27     // from a file in memory?
     28     static final long TIMEOUT_MICROSECONDS = 500;
     29     @CalledByNative
     30     private static String CreateTempFile(Context ctx) throws java.io.IOException {
     31         File outputDirectory = ctx.getCacheDir();
     32         File outputFile = File.createTempFile("webaudio", ".dat", outputDirectory);
     33         return outputFile.getAbsolutePath();
     34     }
     35 
     36     @CalledByNative
     37     private static boolean decodeAudioFile(Context ctx,
     38                                            int nativeMediaCodecBridge,
     39                                            int inputFD,
     40                                            long dataSize) {
     41 
     42         if (dataSize < 0 || dataSize > 0x7fffffff)
     43             return false;
     44 
     45         MediaExtractor extractor = new MediaExtractor();
     46 
     47         ParcelFileDescriptor encodedFD;
     48         encodedFD = ParcelFileDescriptor.adoptFd(inputFD);
     49         try {
     50             extractor.setDataSource(encodedFD.getFileDescriptor(), 0, dataSize);
     51         } catch (Exception e) {
     52             e.printStackTrace();
     53             encodedFD.detachFd();
     54             return false;
     55         }
     56 
     57         if (extractor.getTrackCount() <= 0) {
     58             encodedFD.detachFd();
     59             return false;
     60         }
     61 
     62         MediaFormat format = extractor.getTrackFormat(0);
     63 
     64         // Number of channels specified in the file
     65         int inputChannelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
     66 
     67         // Number of channels the decoder will provide. (Not
     68         // necessarily the same as inputChannelCount.  See
     69         // crbug.com/266006.)
     70         int outputChannelCount = inputChannelCount;
     71 
     72         int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
     73         String mime = format.getString(MediaFormat.KEY_MIME);
     74 
     75         long durationMicroseconds = 0;
     76         if (format.containsKey(MediaFormat.KEY_DURATION)) {
     77             try {
     78                 durationMicroseconds = format.getLong(MediaFormat.KEY_DURATION);
     79             } catch (Exception e) {
     80                 Log.d(LOG_TAG, "Cannot get duration");
     81             }
     82         }
     83 
     84         if (DEBUG) {
     85             Log.d(LOG_TAG, "Tracks: " + extractor.getTrackCount()
     86                   + " Rate: " + sampleRate
     87                   + " Channels: " + inputChannelCount
     88                   + " Mime: " + mime
     89                   + " Duration: " + durationMicroseconds + " microsec");
     90         }
     91 
     92         nativeInitializeDestination(nativeMediaCodecBridge,
     93                                     inputChannelCount,
     94                                     sampleRate,
     95                                     durationMicroseconds);
     96 
     97         // Create decoder
     98         MediaCodec codec = MediaCodec.createDecoderByType(mime);
     99         codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
    100         codec.start();
    101 
    102         ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
    103         ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
    104 
    105         // A track must be selected and will be used to read samples.
    106         extractor.selectTrack(0);
    107 
    108         boolean sawInputEOS = false;
    109         boolean sawOutputEOS = false;
    110 
    111         // Keep processing until the output is done.
    112         while (!sawOutputEOS) {
    113             if (!sawInputEOS) {
    114                 // Input side
    115                 int inputBufIndex = codec.dequeueInputBuffer(TIMEOUT_MICROSECONDS);
    116 
    117                 if (inputBufIndex >= 0) {
    118                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
    119                     int sampleSize = extractor.readSampleData(dstBuf, 0);
    120                     long presentationTimeMicroSec = 0;
    121 
    122                     if (sampleSize < 0) {
    123                         sawInputEOS = true;
    124                         sampleSize = 0;
    125                     } else {
    126                         presentationTimeMicroSec = extractor.getSampleTime();
    127                     }
    128 
    129                     codec.queueInputBuffer(inputBufIndex,
    130                                            0, /* offset */
    131                                            sampleSize,
    132                                            presentationTimeMicroSec,
    133                                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
    134 
    135                     if (!sawInputEOS) {
    136                         extractor.advance();
    137                     }
    138                 }
    139             }
    140 
    141             // Output side
    142             MediaCodec.BufferInfo info = new BufferInfo();
    143             final int outputBufIndex = codec.dequeueOutputBuffer(info, TIMEOUT_MICROSECONDS);
    144 
    145             if (outputBufIndex >= 0) {
    146                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
    147 
    148                 if (info.size > 0) {
    149                     nativeOnChunkDecoded(nativeMediaCodecBridge, buf, info.size,
    150                                          inputChannelCount, outputChannelCount);
    151                 }
    152 
    153                 buf.clear();
    154                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
    155 
    156                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    157                     sawOutputEOS = true;
    158                 }
    159             } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
    160                 codecOutputBuffers = codec.getOutputBuffers();
    161             } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
    162                 MediaFormat newFormat = codec.getOutputFormat();
    163                 outputChannelCount = newFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
    164                 Log.d(LOG_TAG, "output format changed to " + newFormat);
    165             }
    166         }
    167 
    168         encodedFD.detachFd();
    169 
    170         codec.stop();
    171         codec.release();
    172         codec = null;
    173 
    174         return true;
    175     }
    176 
    177     private static native void nativeOnChunkDecoded(
    178         int nativeWebAudioMediaCodecBridge, ByteBuffer buf, int size,
    179         int inputChannelCount, int outputChannelCount);
    180 
    181     private static native void nativeInitializeDestination(
    182         int nativeWebAudioMediaCodecBridge,
    183         int inputChannelCount,
    184         int sampleRate,
    185         long durationMicroseconds);
    186 }
    187