Commit 087cf954 by eguven Committed by Oliver Woodman

Copy main/extensions/flac -> experimental/extensions/flac

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=119544516
parent eb877f0c
# ExoPlayer Flac Extension #
## Description ##
The Flac Extension is a [TrackRenderer][] implementation that helps you bundle
libFLAC (the Flac decoding library) into your app and use it along with
ExoPlayer to play Flac audio on Android devices.
[TrackRenderer]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/TrackRenderer.html
## Build Instructions ##
* Checkout ExoPlayer along with Extensions:
```
git clone https://github.com/google/ExoPlayer.git
```
* Set the following environment variables:
```
cd "<path to exoplayer checkout>"
EXOPLAYER_ROOT="$(pwd)"
FLAC_EXT_PATH="${EXOPLAYER_ROOT}/extensions/flac/src/main"
```
* Download the [Android NDK][] and set its location in an environment variable:
[Android NDK]: https://developer.android.com/tools/sdk/ndk/index.html
```
NDK_PATH="<path to Android NDK>"
```
* Download and extract flac-1.3.1 as "${FLAC_EXT_PATH}/jni/flac" folder:
```
curl http://downloads.xiph.org/releases/flac/flac-1.3.1.tar.xz | tar xJ && \
mv flac-1.3.1 flac
```
* Build the JNI native libraries from the command line:
```
cd "${FLAC_EXT_PATH}"/jni && \
${NDK_PATH}/ndk-build APP_ABI=all -j4
```
* In your project, you can add a dependency to the Flac Extension by using a
rule like this:
```
// in settings.gradle
include ':..:ExoPlayer:library'
include ':..:ExoPlayer:extension-flac'
// in build.gradle
dependencies {
compile project(':..:ExoPlayer:library')
compile project(':..:ExoPlayer:extension-flac')
}
```
* Now, when you build your app, the Flac extension will be built and the native
libraries will be packaged along with the APK.
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 9
targetSdkVersion 23
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
lintOptions {
abortOnError false
}
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] // Disable the automatic ndk-build call by Android Studio.
}
}
dependencies {
compile project(':library')
}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="java"/>
<classpathentry kind="src" path="/ExoPlayerLib"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerExt-Flac</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?children?</key>
<value>?name?=outputEntries\|?children?=?name?=entry\\\\\\\|\\\|?name?=entry\\\\\\\|\\\|\||</value>
</dictionary>
<dictionary>
<key>?name?</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.append_environment</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>
<value>ndk-build</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
<value>clean</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.contents</key>
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
<value>false</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.stopOnError</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.exoplayer.ext.flac">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
</manifest>
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.flac;
import com.google.android.exoplayer.DecoderInputBuffer;
import com.google.android.exoplayer.util.extensions.SimpleDecoder;
import java.nio.ByteBuffer;
import java.util.List;
/**
* Flac decoder.
*/
/* package */ final class FlacDecoder extends
SimpleDecoder<DecoderInputBuffer, FlacOutputBuffer, FlacDecoderException> {
private final int maxOutputBufferSize;
private final FlacJni decoder;
/**
* Creates a Flac decoder.
*
* @param numInputBuffers The number of input buffers.
* @param numOutputBuffers The number of output buffers.
* @param initializationData Codec-specific initialization data.
* @throws FlacDecoderException Thrown if an exception occurs when initializing the decoder.
*/
public FlacDecoder(int numInputBuffers, int numOutputBuffers, List<byte[]> initializationData)
throws FlacDecoderException {
super(new DecoderInputBuffer[numInputBuffers], new FlacOutputBuffer[numOutputBuffers]);
if (initializationData.size() != 1) {
throw new FlacDecoderException("Wrong number of initialization data");
}
decoder = new FlacJni();
ByteBuffer metadata = ByteBuffer.wrap(initializationData.get(0));
decoder.setData(metadata);
FlacStreamInfo streamInfo = decoder.decodeMetadata();
if (streamInfo == null) {
throw new FlacDecoderException("Metadata decoding failed");
}
setInitialInputBufferSize(streamInfo.maxFrameSize);
maxOutputBufferSize = streamInfo.maxDecodedFrameSize();
}
@Override
public DecoderInputBuffer createInputBuffer() {
return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
}
@Override
public FlacOutputBuffer createOutputBuffer() {
return new FlacOutputBuffer(this);
}
@Override
protected void releaseOutputBuffer(FlacOutputBuffer buffer) {
super.releaseOutputBuffer(buffer);
}
@Override
public FlacDecoderException decode(DecoderInputBuffer inputBuffer,
FlacOutputBuffer outputBuffer) {
ByteBuffer data = inputBuffer.data;
outputBuffer.timestampUs = inputBuffer.timeUs;
data.limit(data.position());
data.position(data.position() - inputBuffer.size);
outputBuffer.init(maxOutputBufferSize);
decoder.setData(data);
int result = decoder.decodeSample(outputBuffer.data);
if (result < 0) {
return new FlacDecoderException("Frame decoding failed");
}
outputBuffer.data.position(0);
outputBuffer.data.limit(result);
return null;
}
@Override
public void release() {
super.release();
decoder.release();
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.flac;
/**
* Thrown when an Flac decoder error occurs.
*/
public final class FlacDecoderException extends Exception {
/* package */ FlacDecoderException(String message) {
super(message);
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.flac;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorInput;
import com.google.android.exoplayer.extractor.ExtractorOutput;
import com.google.android.exoplayer.extractor.PositionHolder;
import com.google.android.exoplayer.extractor.SeekMap;
import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.util.MimeTypes;
import com.google.android.exoplayer.util.ParsableByteArray;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* Facilitates the extraction of data from the FLAC container format.
*/
public final class FlacExtractor implements Extractor {
/**
* FLAC signature: first 4 is the signature word, second 4 is the sizeof STREAMINFO. 0x22 is the
* mandatory STREAMINFO.
*/
private static final byte[] FLAC_SIGNATURE = {'f', 'L', 'a', 'C', 0, 0, 0, 0x22};
private ExtractorOutput output;
private TrackOutput trackOutput;
private FlacJni decoder;
private boolean metadataParsed;
private ParsableByteArray outputBuffer;
private ByteBuffer outputByteBuffer;
@Override
public void init(ExtractorOutput output) {
this.output = output;
this.trackOutput = output.track(0);
output.endTracks();
try {
decoder = new FlacJni();
} catch (FlacDecoderException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean sniff(ExtractorInput input) throws IOException, InterruptedException {
byte[] header = new byte[FLAC_SIGNATURE.length];
input.peekFully(header, 0, FLAC_SIGNATURE.length);
return Arrays.equals(header, FLAC_SIGNATURE);
}
@Override
public int read(final ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException {
decoder.setData(input);
if (!metadataParsed) {
final FlacStreamInfo streamInfo = decoder.decodeMetadata();
if (streamInfo == null) {
throw new IOException("Metadata decoding failed");
}
metadataParsed = true;
output.seekMap(new SeekMap() {
final boolean isSeekable = decoder.getSeekPosition(0) != -1;
final long durationUs = streamInfo.durationUs();
@Override
public boolean isSeekable() {
return isSeekable;
}
@Override
public long getPosition(long timeUs) {
return isSeekable ? decoder.getSeekPosition(timeUs) : 0;
}
@Override
public long getDurationUs() {
return durationUs;
}
});
Format mediaFormat = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_RAW,
Format.NO_VALUE, streamInfo.bitRate(),
streamInfo.channels, streamInfo.sampleRate, null, null);
trackOutput.format(mediaFormat);
outputBuffer = new ParsableByteArray(streamInfo.maxDecodedFrameSize());
outputByteBuffer = ByteBuffer.wrap(outputBuffer.data);
}
outputBuffer.reset();
int size = decoder.decodeSample(outputByteBuffer);
if (size <= 0) {
return RESULT_END_OF_INPUT;
}
trackOutput.sampleData(outputBuffer, size);
trackOutput
.sampleMetadata(decoder.getLastSampleTimestamp(), C.BUFFER_FLAG_KEY_FRAME, size, 0, null);
return decoder.isEndOfData() ? RESULT_END_OF_INPUT : RESULT_CONTINUE;
}
@Override
public void seek() {
decoder.flush();
}
@Override
public void release() {
decoder.release();
decoder = null;
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.flac;
import com.google.android.exoplayer.C;
import com.google.android.exoplayer.extractor.ExtractorInput;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* JNI wrapper for the libflac Flac decoder.
*/
/* package */ final class FlacJni {
/**
* Whether the underlying libflac library is available.
*/
public static final boolean IS_AVAILABLE;
static {
boolean isAvailable;
try {
System.loadLibrary("flacJNI");
isAvailable = true;
} catch (UnsatisfiedLinkError exception) {
isAvailable = false;
}
IS_AVAILABLE = isAvailable;
}
private static final int TEMP_BUFFER_SIZE = 8192; // The same buffer size which libflac has
private final long nativeDecoderContext;
private ByteBuffer byteBufferData;
private ExtractorInput extractorInput;
private boolean endOfExtractorInput;
private byte[] tempBuffer;
public FlacJni() throws FlacDecoderException {
nativeDecoderContext = flacInit();
if (nativeDecoderContext == 0) {
throw new FlacDecoderException("Failed to initialize decoder");
}
}
/**
* Sets data to be parsed by libflac.
* @param byteBufferData Source {@link ByteBuffer}
*/
public void setData(ByteBuffer byteBufferData) {
this.byteBufferData = byteBufferData;
this.extractorInput = null;
this.tempBuffer = null;
}
/**
* Sets data to be parsed by libflac.
* @param extractorInput Source {@link ExtractorInput}
*/
public void setData(ExtractorInput extractorInput) {
this.byteBufferData = null;
this.extractorInput = extractorInput;
if (tempBuffer == null) {
this.tempBuffer = new byte[TEMP_BUFFER_SIZE];
}
endOfExtractorInput = false;
}
public boolean isEndOfData() {
if (byteBufferData != null) {
return byteBufferData.remaining() == 0;
} else if (extractorInput != null) {
return endOfExtractorInput;
}
return true;
}
/**
* Reads up to {@code length} bytes from the data source.
* <p>
* This method blocks until at least one byte of data can be read, the end of the input is
* detected or an exception is thrown.
* <p>
* This method is called from the native code.
*
* @param target A target {@link ByteBuffer} into which data should be written.
* @return Returns the number of bytes read, or -1 on failure. It's not an error if this returns
* zero; it just means all the data read from the source.
*/
public int read(ByteBuffer target) throws IOException, InterruptedException {
int byteCount = target.remaining();
if (byteBufferData != null) {
byteCount = Math.min(byteCount, byteBufferData.remaining());
int originalLimit = byteBufferData.limit();
byteBufferData.limit(byteBufferData.position() + byteCount);
target.put(byteBufferData);
byteBufferData.limit(originalLimit);
} else if (extractorInput != null) {
byteCount = Math.min(byteCount, TEMP_BUFFER_SIZE);
int read = readFromExtractorInput(0, byteCount);
if (read < 4) {
// Reading less than 4 bytes, most of the time, happens because of getting the bytes left in
// the buffer of the input. Do another read to reduce the number of calls to this method
// from the native code.
read += readFromExtractorInput(read, byteCount - read);
}
byteCount = read;
target.put(tempBuffer, 0, byteCount);
} else {
return -1;
}
return byteCount;
}
public FlacStreamInfo decodeMetadata() {
return flacDecodeMetadata(nativeDecoderContext);
}
public int decodeSample(ByteBuffer output) {
return output.isDirect()
? flacDecodeToBuffer(nativeDecoderContext, output)
: flacDecodeToArray(nativeDecoderContext, output.array());
}
public long getLastSampleTimestamp() {
return flacGetLastTimestamp(nativeDecoderContext);
}
/**
* Maps a seek position in microseconds to a corresponding position (byte offset) in the flac
* stream.
*
* @param timeUs A seek position in microseconds.
* @return The corresponding position (byte offset) in the flac stream or -1 if the stream doesn't
* have a seek table.
*/
public long getSeekPosition(long timeUs) {
return flacGetSeekPosition(nativeDecoderContext, timeUs);
}
public void flush() {
flacFlush(nativeDecoderContext);
}
public void release() {
flacRelease(nativeDecoderContext);
}
private int readFromExtractorInput(int offset, int length)
throws IOException, InterruptedException {
int read = extractorInput.read(tempBuffer, offset, length);
if (read == C.RESULT_END_OF_INPUT) {
endOfExtractorInput = true;
read = 0;
}
return read;
}
private native long flacInit();
private native FlacStreamInfo flacDecodeMetadata(long context);
private native int flacDecodeToBuffer(long context, ByteBuffer outputBuffer);
private native int flacDecodeToArray(long context, byte[] outputArray);
private native long flacGetLastTimestamp(long context);
private native long flacGetSeekPosition(long context, long timeUs);
private native void flacFlush(long context);
private native void flacRelease(long context);
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.flac;
import com.google.android.exoplayer.util.extensions.OutputBuffer;
import java.nio.ByteBuffer;
/**
* Buffer for {@link FlacDecoder} output.
*/
public final class FlacOutputBuffer extends OutputBuffer {
private final FlacDecoder owner;
public ByteBuffer data;
/* package */ FlacOutputBuffer(FlacDecoder owner) {
this.owner = owner;
}
/* package */ void init(int size) {
if (data == null || data.capacity() < size) {
data = ByteBuffer.allocateDirect(size);
}
data.position(0);
data.limit(size);
}
@Override
public void clear() {
super.clear();
if (data != null) {
data.clear();
}
}
@Override
public void release() {
owner.releaseOutputBuffer(this);
}
}
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.flac;
/**
* Holder for flac stream info.
*/
/* package */ final class FlacStreamInfo {
public final int minBlockSize;
public final int maxBlockSize;
public final int minFrameSize;
public final int maxFrameSize;
public final int sampleRate;
public final int channels;
public final int bitsPerSample;
public final long totalSamples;
public FlacStreamInfo(int minBlockSize, int maxBlockSize, int minFrameSize, int maxFrameSize,
int sampleRate, int channels, int bitsPerSample, long totalSamples) {
this.minBlockSize = minBlockSize;
this.maxBlockSize = maxBlockSize;
this.minFrameSize = minFrameSize;
this.maxFrameSize = maxFrameSize;
this.sampleRate = sampleRate;
this.channels = channels;
this.bitsPerSample = bitsPerSample;
this.totalSamples = totalSamples;
}
public int maxDecodedFrameSize() {
return maxBlockSize * channels * 2;
}
public int bitRate() {
return bitsPerSample * sampleRate;
}
public long durationUs() {
return (totalSamples * 1000000L) / sampleRate;
}
}
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
WORKING_DIR := $(call my-dir)
# build libflacJNI.so
include $(CLEAR_VARS)
include $(WORKING_DIR)/flac_sources.mk
LOCAL_PATH := $(WORKING_DIR)
LOCAL_MODULE := libflacJNI
LOCAL_ARM_MODE := arm
LOCAL_CPP_EXTENSION := .cc
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/flac/include \
$(LOCAL_PATH)/flac/src/libFLAC/include
LOCAL_SRC_FILES := $(FLAC_SOURCES)
LOCAL_CFLAGS += '-DVERSION="1.3.1"' -DFLAC__NO_MD5 -DFLAC__INTEGER_ONLY_LIBRARY -DFLAC__NO_ASM
LOCAL_CFLAGS += -D_REENTRANT -DPIC -DU_COMMON_IMPLEMENTATION -fPIC
LOCAL_CFLAGS += -O3 -funroll-loops -finline-functions
LOCAL_LDLIBS := -llog -lz -lm
include $(BUILD_SHARED_LIBRARY)
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
APP_OPTIM := release
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti
APP_PLATFORM := android-9
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <android/log.h>
#include <cstdlib>
#include "include/flac_parser.h"
#define LOG_TAG "FlacJniJNI"
#define ALOGE(...) \
((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#define ALOGV(...) \
((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#define FUNC(RETURN_TYPE, NAME, ...) \
extern "C" { \
JNIEXPORT RETURN_TYPE \
Java_com_google_android_exoplayer_ext_flac_FlacJni_##NAME( \
JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
} \
JNIEXPORT RETURN_TYPE \
Java_com_google_android_exoplayer_ext_flac_FlacJni_##NAME( \
JNIEnv *env, jobject thiz, ##__VA_ARGS__)
class JavaDataSource : public DataSource {
public:
void setFlacJni(JNIEnv *env, jobject flacJni) {
this->env = env;
this->flacJni = flacJni;
if (mid == NULL) {
jclass cls = env->GetObjectClass(flacJni);
mid = env->GetMethodID(cls, "read", "(Ljava/nio/ByteBuffer;)I");
env->DeleteLocalRef(cls);
}
}
ssize_t readAt(off64_t offset, void *const data, size_t size) {
jobject byteBuffer = env->NewDirectByteBuffer(data, size);
int result = env->CallIntMethod(flacJni, mid, byteBuffer);
if (env->ExceptionOccurred()) {
result = -1;
}
env->DeleteLocalRef(byteBuffer);
return result;
}
private:
JNIEnv *env;
jobject flacJni;
jmethodID mid;
};
struct Context {
JavaDataSource *source;
FLACParser *parser;
};
FUNC(jlong, flacInit) {
Context *context = new Context;
context->source = new JavaDataSource();
context->parser = new FLACParser(context->source);
return reinterpret_cast<intptr_t>(context);
}
FUNC(jobject, flacDecodeMetadata, jlong jContext) {
Context *context = reinterpret_cast<Context *>(jContext);
context->source->setFlacJni(env, thiz);
if (!context->parser->init()) {
return NULL;
}
const FLAC__StreamMetadata_StreamInfo &streamInfo =
context->parser->getStreamInfo();
jclass cls = env->FindClass(
"com/google/android/exoplayer/ext/flac/"
"FlacStreamInfo");
jmethodID constructor = env->GetMethodID(cls, "<init>", "(IIIIIIIJ)V");
return env->NewObject(cls, constructor, streamInfo.min_blocksize,
streamInfo.max_blocksize, streamInfo.min_framesize,
streamInfo.max_framesize, streamInfo.sample_rate,
streamInfo.channels, streamInfo.bits_per_sample,
streamInfo.total_samples);
}
FUNC(jint, flacDecodeToBuffer, jlong jContext, jobject jOutputBuffer) {
Context *context = reinterpret_cast<Context *>(jContext);
context->source->setFlacJni(env, thiz);
void *outputBuffer = env->GetDirectBufferAddress(jOutputBuffer);
jint outputSize = env->GetDirectBufferCapacity(jOutputBuffer);
return context->parser->readBuffer(outputBuffer, outputSize);
}
FUNC(jint, flacDecodeToArray, jlong jContext, jbyteArray jOutputArray) {
Context *context = reinterpret_cast<Context *>(jContext);
context->source->setFlacJni(env, thiz);
jbyte *outputBuffer = env->GetByteArrayElements(jOutputArray, NULL);
jint outputSize = env->GetArrayLength(jOutputArray);
int count = context->parser->readBuffer(outputBuffer, outputSize);
env->ReleaseByteArrayElements(jOutputArray, outputBuffer, 0);
return count;
}
FUNC(jlong, flacGetLastTimestamp, jlong jContext) {
Context *context = reinterpret_cast<Context *>(jContext);
return context->parser->getLastTimestamp();
}
FUNC(jlong, flacGetSeekPosition, jlong jContext, jlong timeUs) {
Context *context = reinterpret_cast<Context *>(jContext);
return context->parser->getSeekPosition(timeUs);
}
FUNC(void, flacFlush, jlong jContext) {
Context *context = reinterpret_cast<Context *>(jContext);
context->parser->flush();
}
FUNC(void, flacRelease, jlong jContext) {
Context *context = reinterpret_cast<Context *>(jContext);
delete context->parser;
delete context->source;
delete context;
}
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
FLAC_SOURCES = \
flac_jni.cc \
flac_parser.cc \
flac/src/libFLAC/bitmath.c \
flac/src/libFLAC/bitreader.c \
flac/src/libFLAC/bitwriter.c \
flac/src/libFLAC/cpu.c \
flac/src/libFLAC/crc.c \
flac/src/libFLAC/fixed.c \
flac/src/libFLAC/fixed_intrin_sse2.c \
flac/src/libFLAC/fixed_intrin_ssse3.c \
flac/src/libFLAC/float.c \
flac/src/libFLAC/format.c \
flac/src/libFLAC/lpc.c \
flac/src/libFLAC/lpc_intrin_avx2.c \
flac/src/libFLAC/lpc_intrin_sse2.c \
flac/src/libFLAC/lpc_intrin_sse41.c \
flac/src/libFLAC/lpc_intrin_sse.c \
flac/src/libFLAC/md5.c \
flac/src/libFLAC/memory.c \
flac/src/libFLAC/metadata_iterators.c \
flac/src/libFLAC/metadata_object.c \
flac/src/libFLAC/stream_decoder.c \
flac/src/libFLAC/stream_encoder.c \
flac/src/libFLAC/stream_encoder_framing.c \
flac/src/libFLAC/stream_encoder_intrin_avx2.c \
flac/src/libFLAC/stream_encoder_intrin_sse2.c \
flac/src/libFLAC/stream_encoder_intrin_ssse3.c \
flac/src/libFLAC/window.c
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef INCLUDE_DATA_SOURCE_H_
#define INCLUDE_DATA_SOURCE_H_
#include <jni.h>
#include <sys/types.h>
class DataSource {
public:
// Returns the number of bytes read, or -1 on failure. It's not an error if
// this returns zero; it just means the given offset is equal to, or
// beyond, the end of the source.
virtual ssize_t readAt(off64_t offset, void* const data, size_t size) = 0;
};
#endif // INCLUDE_DATA_SOURCE_H_
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FLAC_PARSER_H_
#define FLAC_PARSER_H_
#include <stdint.h>
// libFLAC parser
#include "FLAC/stream_decoder.h"
#include "include/data_source.h"
typedef int status_t;
class FLACParser {
public:
FLACParser(DataSource *source);
~FLACParser();
bool init();
// stream properties
unsigned getMaxBlockSize() const { return mStreamInfo.max_blocksize; }
unsigned getSampleRate() const { return mStreamInfo.sample_rate; }
unsigned getChannels() const { return mStreamInfo.channels; }
unsigned getBitsPerSample() const { return mStreamInfo.bits_per_sample; }
FLAC__uint64 getTotalSamples() const { return mStreamInfo.total_samples; }
const FLAC__StreamMetadata_StreamInfo& getStreamInfo() const {
return mStreamInfo;
}
int64_t getLastTimestamp() const {
return (1000000LL * mWriteHeader.number.sample_number) / getSampleRate();
}
size_t readBuffer(void *output, size_t output_size);
int64_t getSeekPosition(int64_t timeUs);
void flush() {
if (mDecoder != NULL) {
FLAC__stream_decoder_flush(mDecoder);
}
}
private:
DataSource *mDataSource;
void (*mCopy)(int16_t *dst, const int *const *src, unsigned nSamples,
unsigned nChannels);
// handle to underlying libFLAC parser
FLAC__StreamDecoder *mDecoder;
// current position within the data source
off64_t mCurrentPos;
bool mEOF;
// cached when the STREAMINFO metadata is parsed by libFLAC
FLAC__StreamMetadata_StreamInfo mStreamInfo;
bool mStreamInfoValid;
const FLAC__StreamMetadata_SeekTable *mSeekTable;
uint64_t firstFrameOffset;
// cached when a decoded PCM block is "written" by libFLAC parser
bool mWriteRequested;
bool mWriteCompleted;
FLAC__FrameHeader mWriteHeader;
const FLAC__int32 *const *mWriteBuffer;
// most recent error reported by libFLAC parser
FLAC__StreamDecoderErrorStatus mErrorStatus;
// no copy constructor or assignment
FLACParser(const FLACParser &);
FLACParser &operator=(const FLACParser &);
// FLAC parser callbacks as C++ instance methods
FLAC__StreamDecoderReadStatus readCallback(FLAC__byte buffer[],
size_t *bytes);
FLAC__StreamDecoderSeekStatus seekCallback(FLAC__uint64 absolute_byte_offset);
FLAC__StreamDecoderTellStatus tellCallback(
FLAC__uint64 *absolute_byte_offset);
FLAC__StreamDecoderLengthStatus lengthCallback(FLAC__uint64 *stream_length);
FLAC__bool eofCallback();
FLAC__StreamDecoderWriteStatus writeCallback(
const FLAC__Frame *frame, const FLAC__int32 *const buffer[]);
void metadataCallback(const FLAC__StreamMetadata *metadata);
void errorCallback(FLAC__StreamDecoderErrorStatus status);
// FLAC parser callbacks as C-callable functions
static FLAC__StreamDecoderReadStatus read_callback(
const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes,
void *client_data);
static FLAC__StreamDecoderSeekStatus seek_callback(
const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset,
void *client_data);
static FLAC__StreamDecoderTellStatus tell_callback(
const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset,
void *client_data);
static FLAC__StreamDecoderLengthStatus length_callback(
const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length,
void *client_data);
static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder,
void *client_data);
static FLAC__StreamDecoderWriteStatus write_callback(
const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
const FLAC__int32 *const buffer[], void *client_data);
static void metadata_callback(const FLAC__StreamDecoder *decoder,
const FLAC__StreamMetadata *metadata,
void *client_data);
static void error_callback(const FLAC__StreamDecoder *decoder,
FLAC__StreamDecoderErrorStatus status,
void *client_data);
};
#endif // FLAC_PARSER_H_
# Proguard rules specific to the Flac extension.
# This prevents the names of native methods from being obfuscated.
-keepclasseswithmembernames class * {
native <methods>;
}
# Some members of this class are being accessed from native methods. Keep them unobfuscated.
-keep class com.google.android.exoplayer.ext.flac.FlacJni {
*;
}
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system edit
# "ant.properties", and override values to adapt the script to your
# project structure.
#
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
# Project target.
target=android-23
android.library=true
android.library.reference.1=../../../../library/src/main
......@@ -13,3 +13,8 @@
// limitations under the License.
include ':library'
include ':demo'
include ':extension-okhttp'
include ':extension-flac'
project(':extension-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment