Copy opus extension v1->v2
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=119643009
This commit is contained in:
parent
767c7ab169
commit
192f566a1b
@ -74,7 +74,10 @@ import java.util.List;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FlacDecoderException decode(DecoderInputBuffer inputBuffer,
|
public FlacDecoderException decode(DecoderInputBuffer inputBuffer,
|
||||||
FlacOutputBuffer outputBuffer) {
|
FlacOutputBuffer outputBuffer, boolean reset) {
|
||||||
|
if (reset) {
|
||||||
|
decoder.flush();
|
||||||
|
}
|
||||||
ByteBuffer data = inputBuffer.data;
|
ByteBuffer data = inputBuffer.data;
|
||||||
outputBuffer.timestampUs = inputBuffer.timeUs;
|
outputBuffer.timestampUs = inputBuffer.timeUs;
|
||||||
data.limit(data.position());
|
data.limit(data.position());
|
||||||
|
77
extensions/opus/README.md
Normal file
77
extensions/opus/README.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# ExoPlayer Opus Extension #
|
||||||
|
|
||||||
|
## Description ##
|
||||||
|
|
||||||
|
The Opus Extension is a [TrackRenderer][] implementation that helps you bundle
|
||||||
|
libopus (the Opus decoding library) into your app and use it along with
|
||||||
|
ExoPlayer to play Opus 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)"
|
||||||
|
OPUS_EXT_PATH="${EXOPLAYER_ROOT}/extensions/opus/src/main"
|
||||||
|
```
|
||||||
|
|
||||||
|
* Download the [Android NDK][] and set its location in an environment variable:
|
||||||
|
|
||||||
|
```
|
||||||
|
NDK_PATH="<path to Android NDK>"
|
||||||
|
```
|
||||||
|
|
||||||
|
* Fetch libopus:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd "${OPUS_EXT_PATH}/jni" && \
|
||||||
|
git clone git://git.opus-codec.org/opus.git libopus
|
||||||
|
```
|
||||||
|
|
||||||
|
* Run the script to convert arm assembly to NDK compatible format:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd ${OPUS_EXT_PATH}/jni && ./convert_android_asm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
* Build the JNI native libraries from the command line:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd "${OPUS_EXT_PATH}"/jni && \
|
||||||
|
${NDK_PATH}/ndk-build APP_ABI=all -j4
|
||||||
|
```
|
||||||
|
|
||||||
|
* In your project, you can add a dependency to the Opus Extension by using a
|
||||||
|
rule like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
// in settings.gradle
|
||||||
|
include ':..:ExoPlayer:library'
|
||||||
|
include ':..:ExoPlayer:extension-opus'
|
||||||
|
|
||||||
|
// in build.gradle
|
||||||
|
dependencies {
|
||||||
|
compile project(':..:ExoPlayer:library')
|
||||||
|
compile project(':..:ExoPlayer:extension-opus')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now, when you build your app, the Opus extension will be built and the native
|
||||||
|
libraries will be packaged along with the APK.
|
||||||
|
|
||||||
|
## Notes ##
|
||||||
|
|
||||||
|
* Every time there is a change to the libopus checkout:
|
||||||
|
* Arm assembly should be converted by running `convert_android_asm.sh`
|
||||||
|
* Clean and re-build the project.
|
||||||
|
* If you want to use your own version of libopus, place it in
|
||||||
|
`${OPUS_EXT_PATH}/jni/libopus`.
|
45
extensions/opus/build.gradle
Normal file
45
extensions/opus/build.gradle
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (C) 2014 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')
|
||||||
|
}
|
||||||
|
|
10
extensions/opus/src/main/.classpath
Normal file
10
extensions/opus/src/main/.classpath
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?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>
|
57
extensions/opus/src/main/.cproject
Normal file
57
extensions/opus/src/main/.cproject
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||||
|
<cconfiguration id="com.android.toolchain.gcc.423224913">
|
||||||
|
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.android.toolchain.gcc.423224913" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||||
|
<externalSettings/>
|
||||||
|
<extensions>
|
||||||
|
<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||||
|
</extensions>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<configuration artifactName="${ProjName}" buildProperties="" description="" id="com.android.toolchain.gcc.423224913" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||||
|
<folderInfo id="com.android.toolchain.gcc.423224913.1376674556" name="/" resourcePath="">
|
||||||
|
<toolChain id="com.android.toolchain.gcc.1798416430" name="Android GCC" superClass="com.android.toolchain.gcc">
|
||||||
|
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="com.android.targetPlatform.1132129264" isAbstract="false" superClass="com.android.targetPlatform"/>
|
||||||
|
<builder buildPath="${workspace_loc:/ExoPlayerExt-Opus}/jni" id="com.android.builder.532503968" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Android Builder" superClass="com.android.builder">
|
||||||
|
<outputEntries>
|
||||||
|
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="obj"/>
|
||||||
|
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="libs"/>
|
||||||
|
</outputEntries>
|
||||||
|
</builder>
|
||||||
|
<tool id="com.android.gcc.compiler.906450637" name="Android GCC Compiler" superClass="com.android.gcc.compiler">
|
||||||
|
<inputType id="com.android.gcc.inputType.835889068" superClass="com.android.gcc.inputType"/>
|
||||||
|
</tool>
|
||||||
|
</toolChain>
|
||||||
|
</folderInfo>
|
||||||
|
<sourceEntries>
|
||||||
|
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="jni"/>
|
||||||
|
</sourceEntries>
|
||||||
|
</configuration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||||
|
</cconfiguration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<project id="ExoPlayerExt-Opus.null.1840202624" name="ExoPlayerExt-Opus"/>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||||
|
<storageModule moduleId="scannerConfiguration">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
<scannerConfigBuildInfo instanceId="com.android.toolchain.gcc.423224913;com.android.toolchain.gcc.423224913.1376674556;com.android.gcc.compiler.906450637;com.android.gcc.inputType.835889068">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="com.android.AndroidPerProjectProfile"/>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="refreshScope" versionNumber="2">
|
||||||
|
<configuration configurationName="Default">
|
||||||
|
<resource resourceType="PROJECT" workspacePath="/ExoPlayerExt-Opus"/>
|
||||||
|
</configuration>
|
||||||
|
</storageModule>
|
||||||
|
</cproject>
|
97
extensions/opus/src/main/.project
Normal file
97
extensions/opus/src/main/.project
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>ExoPlayerExt-Opus</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>
|
@ -0,0 +1,12 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.7
|
22
extensions/opus/src/main/AndroidManifest.xml
Normal file
22
extensions/opus/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2014 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.opus">
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||||
|
|
||||||
|
</manifest>
|
@ -0,0 +1,397 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.opus;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.CodecCounters;
|
||||||
|
import com.google.android.exoplayer.DecoderInputBuffer;
|
||||||
|
import com.google.android.exoplayer.ExoPlaybackException;
|
||||||
|
import com.google.android.exoplayer.ExoPlayer;
|
||||||
|
import com.google.android.exoplayer.Format;
|
||||||
|
import com.google.android.exoplayer.FormatHolder;
|
||||||
|
import com.google.android.exoplayer.MediaClock;
|
||||||
|
import com.google.android.exoplayer.SampleSourceTrackRenderer;
|
||||||
|
import com.google.android.exoplayer.TrackRenderer;
|
||||||
|
import com.google.android.exoplayer.TrackStream;
|
||||||
|
import com.google.android.exoplayer.audio.AudioTrack;
|
||||||
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes and renders audio using the native Opus decoder.
|
||||||
|
*/
|
||||||
|
public final class LibopusAudioTrackRenderer extends SampleSourceTrackRenderer
|
||||||
|
implements MediaClock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface definition for a callback to be notified of {@link LibopusAudioTrackRenderer} events.
|
||||||
|
*/
|
||||||
|
public interface EventListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when the {@link AudioTrack} fails to initialize.
|
||||||
|
*
|
||||||
|
* @param e The corresponding exception.
|
||||||
|
*/
|
||||||
|
void onAudioTrackInitializationError(AudioTrack.InitializationException e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when an {@link AudioTrack} write fails.
|
||||||
|
*
|
||||||
|
* @param e The corresponding exception.
|
||||||
|
*/
|
||||||
|
void onAudioTrackWriteError(AudioTrack.WriteException e);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when decoding fails.
|
||||||
|
*
|
||||||
|
* @param e The corresponding exception.
|
||||||
|
*/
|
||||||
|
void onDecoderError(OpusDecoderException e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of a message that can be passed to an instance of this class via
|
||||||
|
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||||
|
* should be a {@link Float} with 0 being silence and 1 being unity gain.
|
||||||
|
*/
|
||||||
|
public static final int MSG_SET_VOLUME = 1;
|
||||||
|
|
||||||
|
private static final int NUM_BUFFERS = 16;
|
||||||
|
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;
|
||||||
|
|
||||||
|
public final CodecCounters codecCounters = new CodecCounters();
|
||||||
|
|
||||||
|
private final Handler eventHandler;
|
||||||
|
private final EventListener eventListener;
|
||||||
|
private final AudioTrack audioTrack;
|
||||||
|
private final FormatHolder formatHolder;
|
||||||
|
|
||||||
|
private Format format;
|
||||||
|
private OpusDecoder decoder;
|
||||||
|
private DecoderInputBuffer inputBuffer;
|
||||||
|
private OpusOutputBuffer outputBuffer;
|
||||||
|
|
||||||
|
private long currentPositionUs;
|
||||||
|
private boolean allowPositionDiscontinuity;
|
||||||
|
private boolean inputStreamEnded;
|
||||||
|
private boolean outputStreamEnded;
|
||||||
|
private boolean sourceIsReady;
|
||||||
|
|
||||||
|
private int audioSessionId;
|
||||||
|
|
||||||
|
public LibopusAudioTrackRenderer() {
|
||||||
|
this(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||||
|
* null if delivery of events is not required.
|
||||||
|
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||||
|
*/
|
||||||
|
public LibopusAudioTrackRenderer(Handler eventHandler, EventListener eventListener) {
|
||||||
|
this.eventHandler = eventHandler;
|
||||||
|
this.eventListener = eventListener;
|
||||||
|
this.audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||||
|
audioTrack = new AudioTrack();
|
||||||
|
formatHolder = new FormatHolder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the underlying libopus library is available.
|
||||||
|
*/
|
||||||
|
public static boolean isLibopusAvailable() {
|
||||||
|
return OpusDecoder.IS_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the version of the underlying libopus library if available, otherwise {@code null}.
|
||||||
|
*/
|
||||||
|
public static String getLibopusVersion() {
|
||||||
|
return isLibopusAvailable() ? OpusDecoder.getLibopusVersion() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MediaClock getMediaClock() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int supportsFormat(Format format) {
|
||||||
|
return MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)
|
||||||
|
? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void render(long positionUs, long elapsedRealtimeUs, boolean sourceIsReady)
|
||||||
|
throws ExoPlaybackException {
|
||||||
|
if (outputStreamEnded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.sourceIsReady = sourceIsReady;
|
||||||
|
|
||||||
|
// Try and read a format if we don't have one already.
|
||||||
|
if (format == null && !readFormat()) {
|
||||||
|
// We can't make progress without one.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a decoder yet, we need to instantiate one.
|
||||||
|
if (decoder == null) {
|
||||||
|
// For opus, the format can contain upto 3 entries in initializationData in the following
|
||||||
|
// exact order:
|
||||||
|
// 1) Opus Header Information (required)
|
||||||
|
// 2) Codec Delay in nanoseconds (required if Seek Preroll is present)
|
||||||
|
// 3) Seek Preroll in nanoseconds (required if Codec Delay is present)
|
||||||
|
List<byte[]> initializationData = format.initializationData;
|
||||||
|
if (initializationData.size() < 1) {
|
||||||
|
throw ExoPlaybackException.createForRenderer(
|
||||||
|
new IllegalStateException("Missing initialization data"), getIndex());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
decoder = new OpusDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE,
|
||||||
|
initializationData);
|
||||||
|
} catch (OpusDecoderException e) {
|
||||||
|
notifyDecoderError(e);
|
||||||
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
|
}
|
||||||
|
decoder.start();
|
||||||
|
codecCounters.codecInitCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rendering loop.
|
||||||
|
try {
|
||||||
|
renderBuffer();
|
||||||
|
while (feedInputBuffer()) {}
|
||||||
|
} catch (AudioTrack.InitializationException e) {
|
||||||
|
notifyAudioTrackInitializationError(e);
|
||||||
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
|
} catch (AudioTrack.WriteException e) {
|
||||||
|
notifyAudioTrackWriteError(e);
|
||||||
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
|
} catch (OpusDecoderException e) {
|
||||||
|
notifyDecoderError(e);
|
||||||
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
|
}
|
||||||
|
codecCounters.ensureUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderBuffer() throws OpusDecoderException, AudioTrack.InitializationException,
|
||||||
|
AudioTrack.WriteException {
|
||||||
|
if (outputStreamEnded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputBuffer == null) {
|
||||||
|
outputBuffer = decoder.dequeueOutputBuffer();
|
||||||
|
if (outputBuffer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputBuffer.isEndOfStream()) {
|
||||||
|
outputStreamEnded = true;
|
||||||
|
audioTrack.handleEndOfStream();
|
||||||
|
outputBuffer.release();
|
||||||
|
outputBuffer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!audioTrack.isInitialized()) {
|
||||||
|
if (audioSessionId != AudioTrack.SESSION_ID_NOT_SET) {
|
||||||
|
audioTrack.initialize(audioSessionId);
|
||||||
|
} else {
|
||||||
|
audioSessionId = audioTrack.initialize();
|
||||||
|
}
|
||||||
|
if (getState() == TrackRenderer.STATE_STARTED) {
|
||||||
|
audioTrack.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handleBufferResult;
|
||||||
|
handleBufferResult = audioTrack.handleBuffer(outputBuffer.data, outputBuffer.data.position(),
|
||||||
|
outputBuffer.data.remaining(), outputBuffer.timestampUs);
|
||||||
|
|
||||||
|
// If we are out of sync, allow currentPositionUs to jump backwards.
|
||||||
|
if ((handleBufferResult & AudioTrack.RESULT_POSITION_DISCONTINUITY) != 0) {
|
||||||
|
allowPositionDiscontinuity = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the buffer if it was consumed.
|
||||||
|
if ((handleBufferResult & AudioTrack.RESULT_BUFFER_CONSUMED) != 0) {
|
||||||
|
codecCounters.renderedOutputBufferCount++;
|
||||||
|
outputBuffer.release();
|
||||||
|
outputBuffer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean feedInputBuffer() throws OpusDecoderException {
|
||||||
|
if (inputStreamEnded) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputBuffer == null) {
|
||||||
|
inputBuffer = decoder.dequeueInputBuffer();
|
||||||
|
if (inputBuffer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = readSource(formatHolder, inputBuffer);
|
||||||
|
if (result == TrackStream.NOTHING_READ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result == TrackStream.FORMAT_READ) {
|
||||||
|
format = formatHolder.format;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (inputBuffer.isEndOfStream()) {
|
||||||
|
inputStreamEnded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder.queueInputBuffer(inputBuffer);
|
||||||
|
inputBuffer = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void flushDecoder() {
|
||||||
|
inputBuffer = null;
|
||||||
|
if (outputBuffer != null) {
|
||||||
|
outputBuffer.release();
|
||||||
|
outputBuffer = null;
|
||||||
|
}
|
||||||
|
decoder.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isEnded() {
|
||||||
|
return outputStreamEnded && !audioTrack.hasPendingData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isReady() {
|
||||||
|
return audioTrack.hasPendingData()
|
||||||
|
|| (format != null && (sourceIsReady || outputBuffer != null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getPositionUs() {
|
||||||
|
long newCurrentPositionUs = audioTrack.getCurrentPositionUs(isEnded());
|
||||||
|
if (newCurrentPositionUs != AudioTrack.CURRENT_POSITION_NOT_SET) {
|
||||||
|
currentPositionUs = allowPositionDiscontinuity ? newCurrentPositionUs
|
||||||
|
: Math.max(currentPositionUs, newCurrentPositionUs);
|
||||||
|
allowPositionDiscontinuity = false;
|
||||||
|
}
|
||||||
|
return currentPositionUs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void reset(long positionUs) {
|
||||||
|
audioTrack.reset();
|
||||||
|
currentPositionUs = positionUs;
|
||||||
|
allowPositionDiscontinuity = true;
|
||||||
|
inputStreamEnded = false;
|
||||||
|
outputStreamEnded = false;
|
||||||
|
sourceIsReady = false;
|
||||||
|
if (decoder != null) {
|
||||||
|
flushDecoder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStarted() {
|
||||||
|
audioTrack.play();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStopped() {
|
||||||
|
audioTrack.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDisabled() {
|
||||||
|
inputBuffer = null;
|
||||||
|
outputBuffer = null;
|
||||||
|
format = null;
|
||||||
|
audioSessionId = AudioTrack.SESSION_ID_NOT_SET;
|
||||||
|
try {
|
||||||
|
if (decoder != null) {
|
||||||
|
decoder.release();
|
||||||
|
decoder = null;
|
||||||
|
codecCounters.codecReleaseCount++;
|
||||||
|
}
|
||||||
|
audioTrack.release();
|
||||||
|
} finally {
|
||||||
|
super.onDisabled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean readFormat() {
|
||||||
|
int result = readSource(formatHolder, null);
|
||||||
|
if (result == TrackStream.FORMAT_READ) {
|
||||||
|
format = formatHolder.format;
|
||||||
|
audioTrack.configure(format.getFrameworkMediaFormatV16(), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
||||||
|
if (messageType == MSG_SET_VOLUME) {
|
||||||
|
audioTrack.setVolume((Float) message);
|
||||||
|
} else {
|
||||||
|
super.handleMessage(messageType, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyAudioTrackInitializationError(final AudioTrack.InitializationException e) {
|
||||||
|
if (eventHandler != null && eventListener != null) {
|
||||||
|
eventHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
eventListener.onAudioTrackInitializationError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyAudioTrackWriteError(final AudioTrack.WriteException e) {
|
||||||
|
if (eventHandler != null && eventListener != null) {
|
||||||
|
eventHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
eventListener.onAudioTrackWriteError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyDecoderError(final OpusDecoderException e) {
|
||||||
|
if (eventHandler != null && eventListener != null) {
|
||||||
|
eventHandler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
eventListener.onDecoderError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.opus;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.C;
|
||||||
|
import com.google.android.exoplayer.DecoderInputBuffer;
|
||||||
|
import com.google.android.exoplayer.util.extensions.SimpleDecoder;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JNI wrapper for the libopus Opus decoder.
|
||||||
|
*/
|
||||||
|
/* package */ final class OpusDecoder extends
|
||||||
|
SimpleDecoder<DecoderInputBuffer, OpusOutputBuffer, OpusDecoderException> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the underlying libopus library is available.
|
||||||
|
*/
|
||||||
|
public static final boolean IS_AVAILABLE;
|
||||||
|
static {
|
||||||
|
boolean isAvailable;
|
||||||
|
try {
|
||||||
|
System.loadLibrary("opus");
|
||||||
|
System.loadLibrary("opusJNI");
|
||||||
|
isAvailable = true;
|
||||||
|
} catch (UnsatisfiedLinkError exception) {
|
||||||
|
isAvailable = false;
|
||||||
|
}
|
||||||
|
IS_AVAILABLE = isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the version string of the underlying libopus decoder.
|
||||||
|
*/
|
||||||
|
public static native String getLibopusVersion();
|
||||||
|
|
||||||
|
private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opus streams are always decoded at 48000 Hz.
|
||||||
|
*/
|
||||||
|
private static final int SAMPLE_RATE = 48000;
|
||||||
|
|
||||||
|
private final int channelCount;
|
||||||
|
private final int headerSkipSamples;
|
||||||
|
private final int headerSeekPreRollSamples;
|
||||||
|
private final long nativeDecoderContext;
|
||||||
|
|
||||||
|
private int skipSamples;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an Opus decoder.
|
||||||
|
*
|
||||||
|
* @param numInputBuffers The number of input buffers.
|
||||||
|
* @param numOutputBuffers The number of output buffers.
|
||||||
|
* @param initialInputBufferSize The initial size of each input buffer.
|
||||||
|
* @param initializationData Codec-specific initialization data. The first element must contain an
|
||||||
|
* opus header. Optionally, the list may contain two additional buffers, which must contain
|
||||||
|
* the encoder delay and seek pre roll values in nanoseconds, encoded as longs.
|
||||||
|
* @throws OpusDecoderException Thrown if an exception occurs when initializing the decoder.
|
||||||
|
*/
|
||||||
|
public OpusDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize,
|
||||||
|
List<byte[]> initializationData) throws OpusDecoderException {
|
||||||
|
super(new DecoderInputBuffer[numInputBuffers], new OpusOutputBuffer[numOutputBuffers]);
|
||||||
|
byte[] headerBytes = initializationData.get(0);
|
||||||
|
if (headerBytes.length < 19) {
|
||||||
|
throw new OpusDecoderException("Header size is too small.");
|
||||||
|
}
|
||||||
|
channelCount = headerBytes[9] & 0xFF;
|
||||||
|
if (channelCount > 8) {
|
||||||
|
throw new OpusDecoderException("Invalid channel count: " + channelCount);
|
||||||
|
}
|
||||||
|
int preskip = readLittleEndian16(headerBytes, 10);
|
||||||
|
int gain = readLittleEndian16(headerBytes, 16);
|
||||||
|
|
||||||
|
byte[] streamMap = new byte[8];
|
||||||
|
int numStreams, numCoupled;
|
||||||
|
if (headerBytes[18] == 0) { // Channel mapping
|
||||||
|
// If there is no channel mapping, use the defaults.
|
||||||
|
if (channelCount > 2) { // Maximum channel count with default layout.
|
||||||
|
throw new OpusDecoderException("Invalid Header, missing stream map.");
|
||||||
|
}
|
||||||
|
numStreams = 1;
|
||||||
|
numCoupled = (channelCount == 2) ? 1 : 0;
|
||||||
|
streamMap[0] = 0;
|
||||||
|
streamMap[1] = 1;
|
||||||
|
} else {
|
||||||
|
if (headerBytes.length < 21 + channelCount) {
|
||||||
|
throw new OpusDecoderException("Header size is too small.");
|
||||||
|
}
|
||||||
|
// Read the channel mapping.
|
||||||
|
numStreams = headerBytes[19] & 0xFF;
|
||||||
|
numCoupled = headerBytes[20] & 0xFF;
|
||||||
|
for (int i = 0; i < channelCount; i++) {
|
||||||
|
streamMap[i] = headerBytes[21 + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (initializationData.size() == 3) {
|
||||||
|
if (initializationData.get(1).length != 8 || initializationData.get(2).length != 8) {
|
||||||
|
throw new OpusDecoderException("Invalid Codec Delay or Seek Preroll");
|
||||||
|
}
|
||||||
|
long codecDelayNs =
|
||||||
|
ByteBuffer.wrap(initializationData.get(1)).order(ByteOrder.LITTLE_ENDIAN).getLong();
|
||||||
|
long seekPreRollNs =
|
||||||
|
ByteBuffer.wrap(initializationData.get(2)).order(ByteOrder.LITTLE_ENDIAN).getLong();
|
||||||
|
headerSkipSamples = nsToSamples(codecDelayNs);
|
||||||
|
headerSeekPreRollSamples = nsToSamples(seekPreRollNs);
|
||||||
|
} else {
|
||||||
|
headerSkipSamples = preskip;
|
||||||
|
headerSeekPreRollSamples = DEFAULT_SEEK_PRE_ROLL_SAMPLES;
|
||||||
|
}
|
||||||
|
nativeDecoderContext = opusInit(SAMPLE_RATE, channelCount, numStreams, numCoupled, gain,
|
||||||
|
streamMap);
|
||||||
|
if (nativeDecoderContext == 0) {
|
||||||
|
throw new OpusDecoderException("Failed to initialize decoder");
|
||||||
|
}
|
||||||
|
setInitialInputBufferSize(initialInputBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DecoderInputBuffer createInputBuffer() {
|
||||||
|
return new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpusOutputBuffer createOutputBuffer() {
|
||||||
|
return new OpusOutputBuffer(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void releaseOutputBuffer(OpusOutputBuffer buffer) {
|
||||||
|
super.releaseOutputBuffer(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OpusDecoderException decode(DecoderInputBuffer inputBuffer,
|
||||||
|
OpusOutputBuffer outputBuffer, boolean reset) {
|
||||||
|
if (reset) {
|
||||||
|
opusReset(nativeDecoderContext);
|
||||||
|
// When seeking to 0, skip number of samples as specified in opus header. When seeking to
|
||||||
|
// any other time, skip number of samples as specified by seek preroll.
|
||||||
|
skipSamples =
|
||||||
|
(inputBuffer.timeUs == 0) ? headerSkipSamples : headerSeekPreRollSamples;
|
||||||
|
}
|
||||||
|
outputBuffer.timestampUs = inputBuffer.timeUs;
|
||||||
|
inputBuffer.data.position(inputBuffer.data.position() - inputBuffer.size);
|
||||||
|
int requiredOutputBufferSize =
|
||||||
|
opusGetRequiredOutputBufferSize(inputBuffer.data, inputBuffer.size, SAMPLE_RATE);
|
||||||
|
if (requiredOutputBufferSize < 0) {
|
||||||
|
return new OpusDecoderException("Error when computing required output buffer size.");
|
||||||
|
}
|
||||||
|
outputBuffer.init(requiredOutputBufferSize);
|
||||||
|
int result = opusDecode(nativeDecoderContext, inputBuffer.data, inputBuffer.size,
|
||||||
|
outputBuffer.data, outputBuffer.data.capacity());
|
||||||
|
if (result < 0) {
|
||||||
|
return new OpusDecoderException("Decode error: " + opusGetErrorMessage(result));
|
||||||
|
}
|
||||||
|
outputBuffer.data.position(0);
|
||||||
|
outputBuffer.data.limit(result);
|
||||||
|
if (skipSamples > 0) {
|
||||||
|
int bytesPerSample = channelCount * 2;
|
||||||
|
int skipBytes = skipSamples * bytesPerSample;
|
||||||
|
if (result <= skipBytes) {
|
||||||
|
skipSamples -= result / bytesPerSample;
|
||||||
|
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
|
outputBuffer.data.position(result);
|
||||||
|
} else {
|
||||||
|
skipSamples = 0;
|
||||||
|
outputBuffer.data.position(skipBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void release() {
|
||||||
|
super.release();
|
||||||
|
opusClose(nativeDecoderContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private native long opusInit(int sampleRate, int channelCount, int numStreams, int numCoupled,
|
||||||
|
int gain, byte[] streamMap);
|
||||||
|
private native int opusDecode(long decoder, ByteBuffer inputBuffer, int inputSize,
|
||||||
|
ByteBuffer outputBuffer, int outputSize);
|
||||||
|
private native int opusGetRequiredOutputBufferSize(
|
||||||
|
ByteBuffer inputBuffer, int inputSize, int sampleRate);
|
||||||
|
private native void opusClose(long decoder);
|
||||||
|
private native void opusReset(long decoder);
|
||||||
|
private native String opusGetErrorMessage(int errorCode);
|
||||||
|
|
||||||
|
private static int nsToSamples(long ns) {
|
||||||
|
return (int) (ns * SAMPLE_RATE / 1000000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int readLittleEndian16(byte[] input, int offset) {
|
||||||
|
int value = input[offset] & 0xFF;
|
||||||
|
value |= (input[offset + 1] & 0xFF) << 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.opus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an Opus decoder error occurs.
|
||||||
|
*/
|
||||||
|
public final class OpusDecoderException extends Exception {
|
||||||
|
|
||||||
|
/* package */ OpusDecoderException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.opus;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer.util.extensions.OutputBuffer;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer for {@link OpusDecoder} output.
|
||||||
|
*/
|
||||||
|
public final class OpusOutputBuffer extends OutputBuffer {
|
||||||
|
|
||||||
|
private final OpusDecoder owner;
|
||||||
|
|
||||||
|
public ByteBuffer data;
|
||||||
|
|
||||||
|
/* package */ OpusOutputBuffer(OpusDecoder 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
33
extensions/opus/src/main/jni/Android.mk
Normal file
33
extensions/opus/src/main/jni/Android.mk
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2014 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)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
# build libopus.so
|
||||||
|
LOCAL_PATH := $(WORKING_DIR)
|
||||||
|
include libopus.mk
|
||||||
|
|
||||||
|
# build libopusJNI.so
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_PATH := $(WORKING_DIR)
|
||||||
|
LOCAL_MODULE := libopusJNI
|
||||||
|
LOCAL_ARM_MODE := arm
|
||||||
|
LOCAL_CPP_EXTENSION := .cc
|
||||||
|
LOCAL_SRC_FILES := opus_jni.cc
|
||||||
|
LOCAL_LDLIBS := -llog -lz -lm
|
||||||
|
LOCAL_SHARED_LIBRARIES := libopus
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
20
extensions/opus/src/main/jni/Application.mk
Normal file
20
extensions/opus/src/main/jni/Application.mk
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2014 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
|
47
extensions/opus/src/main/jni/convert_android_asm.sh
Executable file
47
extensions/opus/src/main/jni/convert_android_asm.sh
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Copyright (C) 2014 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
ASM_CONVERTER="./libopus/celt/arm/arm2gnu.pl"
|
||||||
|
|
||||||
|
if [[ ! -x "${ASM_CONVERTER}" ]]; then
|
||||||
|
echo "Please make sure you have checked out libopus."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
while read file; do
|
||||||
|
# This check is required because the ASM conversion script doesn't seem to be
|
||||||
|
# idempotent.
|
||||||
|
if [[ ! "${file}" =~ .*_gnu\.s$ ]]; then
|
||||||
|
gnu_file="${file%.s}_gnu.s"
|
||||||
|
${ASM_CONVERTER} "${file}" > "${gnu_file}"
|
||||||
|
# The ASM conversion script replaces includes with *_gnu.S. So, replace
|
||||||
|
# occurences of "*-gnu.S" with "*_gnu.s".
|
||||||
|
perl -pi -e "s/-gnu\.S/_gnu\.s/g" "${gnu_file}"
|
||||||
|
rm -f "${file}"
|
||||||
|
fi
|
||||||
|
done < <(find . -iname '*.s')
|
||||||
|
|
||||||
|
# Generate armopts.s from armopts.s.in
|
||||||
|
sed \
|
||||||
|
-e "s/@OPUS_ARM_MAY_HAVE_EDSP@/1/g" \
|
||||||
|
-e "s/@OPUS_ARM_MAY_HAVE_MEDIA@/1/g" \
|
||||||
|
-e "s/@OPUS_ARM_MAY_HAVE_NEON@/1/g" \
|
||||||
|
libopus/celt/arm/armopts.s.in > libopus/celt/arm/armopts.s.temp
|
||||||
|
${ASM_CONVERTER} "libopus/celt/arm/armopts.s.temp" > "libopus/celt/arm/armopts_gnu.s"
|
||||||
|
rm "libopus/celt/arm/armopts.s.temp"
|
||||||
|
echo "Converted all ASM files and generated armopts.s successfully."
|
50
extensions/opus/src/main/jni/libopus.mk
Normal file
50
extensions/opus/src/main/jni/libopus.mk
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2014 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
LOCAL_PATH := $(call my-dir)/libopus
|
||||||
|
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
include $(LOCAL_PATH)/celt_headers.mk
|
||||||
|
include $(LOCAL_PATH)/celt_sources.mk
|
||||||
|
include $(LOCAL_PATH)/opus_headers.mk
|
||||||
|
include $(LOCAL_PATH)/opus_sources.mk
|
||||||
|
include $(LOCAL_PATH)/silk_headers.mk
|
||||||
|
include $(LOCAL_PATH)/silk_sources.mk
|
||||||
|
|
||||||
|
LOCAL_MODULE := libopus
|
||||||
|
LOCAL_ARM_MODE := arm
|
||||||
|
LOCAL_CFLAGS := -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT \
|
||||||
|
-DHAVE_LRINTF
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src \
|
||||||
|
$(LOCAL_PATH)/silk $(LOCAL_PATH)/celt \
|
||||||
|
$(LOCAL_PATH)/silk/fixed
|
||||||
|
LOCAL_SRC_FILES := $(CELT_SOURCES) $(OPUS_SOURCES) $(OPUS_SOURCES_FLOAT) \
|
||||||
|
$(SILK_SOURCES) $(SILK_SOURCES_FIXED)
|
||||||
|
|
||||||
|
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
|
||||||
|
LOCAL_SRC_FILES += $(CELT_SOURCES_ARM)
|
||||||
|
LOCAL_SRC_FILES += celt/arm/armopts_gnu.s.neon
|
||||||
|
LOCAL_SRC_FILES += $(subst .s,_gnu.s.neon,$(CELT_SOURCES_ARM_ASM))
|
||||||
|
LOCAL_CFLAGS += -DOPUS_ARM_ASM -DOPUS_ARM_INLINE_ASM -DOPUS_ARM_INLINE_EDSP \
|
||||||
|
-DOPUS_ARM_INLINE_MEDIA -DOPUS_ARM_INLINE_NEON \
|
||||||
|
-DOPUS_ARM_MAY_HAVE_NEON -DOPUS_ARM_MAY_HAVE_MEDIA \
|
||||||
|
-DOPUS_ARM_MAY_HAVE_EDSP
|
||||||
|
endif
|
||||||
|
|
||||||
|
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
111
extensions/opus/src/main/jni/opus_jni.cc
Normal file
111
extensions/opus/src/main/jni/opus_jni.cc
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 "opus.h" // NOLINT
|
||||||
|
#include "opus_multistream.h" // NOLINT
|
||||||
|
|
||||||
|
#define LOG_TAG "libopus_native"
|
||||||
|
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
|
||||||
|
__VA_ARGS__))
|
||||||
|
|
||||||
|
#define FUNC(RETURN_TYPE, NAME, ...) \
|
||||||
|
extern "C" { \
|
||||||
|
JNIEXPORT RETURN_TYPE \
|
||||||
|
Java_com_google_android_exoplayer_ext_opus_OpusDecoder_ ## NAME \
|
||||||
|
(JNIEnv* env, jobject thiz, ##__VA_ARGS__);\
|
||||||
|
} \
|
||||||
|
JNIEXPORT RETURN_TYPE \
|
||||||
|
Java_com_google_android_exoplayer_ext_opus_OpusDecoder_ ## NAME \
|
||||||
|
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||||
|
|
||||||
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||||
|
JNIEnv* env;
|
||||||
|
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return JNI_VERSION_1_6;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kBytesPerSample = 2; // opus fixed point uses 16 bit samples.
|
||||||
|
static int channelCount;
|
||||||
|
|
||||||
|
FUNC(jlong, opusInit, jint sampleRate, jint channelCount, jint numStreams,
|
||||||
|
jint numCoupled, jint gain, jbyteArray jStreamMap) {
|
||||||
|
int status = OPUS_INVALID_STATE;
|
||||||
|
::channelCount = channelCount;
|
||||||
|
jbyte* streamMapBytes = env->GetByteArrayElements(jStreamMap, 0);
|
||||||
|
uint8_t* streamMap = reinterpret_cast<uint8_t*>(streamMapBytes);
|
||||||
|
OpusMSDecoder* decoder = opus_multistream_decoder_create(
|
||||||
|
sampleRate, channelCount, numStreams, numCoupled, streamMap, &status);
|
||||||
|
env->ReleaseByteArrayElements(jStreamMap, streamMapBytes, 0);
|
||||||
|
if (!decoder || status != OPUS_OK) {
|
||||||
|
LOGE("Failed to create Opus Decoder; status=%s", opus_strerror(status));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
status = opus_multistream_decoder_ctl(decoder, OPUS_SET_GAIN(gain));
|
||||||
|
if (status != OPUS_OK) {
|
||||||
|
LOGE("Failed to set Opus header gain; status=%s", opus_strerror(status));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<intptr_t>(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNC(jint, opusDecode, jlong jDecoder, jobject jInputBuffer, jint inputSize,
|
||||||
|
jobject jOutputBuffer, jint outputSize) {
|
||||||
|
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||||
|
const uint8_t* inputBuffer =
|
||||||
|
reinterpret_cast<const uint8_t*>(
|
||||||
|
env->GetDirectBufferAddress(jInputBuffer));
|
||||||
|
int16_t* outputBuffer = reinterpret_cast<int16_t*>(
|
||||||
|
env->GetDirectBufferAddress(jOutputBuffer));
|
||||||
|
int sampleCount = opus_multistream_decode(decoder, inputBuffer, inputSize,
|
||||||
|
outputBuffer, outputSize, 0);
|
||||||
|
return (sampleCount < 0) ? sampleCount
|
||||||
|
: sampleCount * kBytesPerSample * channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNC(jint, opusGetRequiredOutputBufferSize, jobject jInputBuffer,
|
||||||
|
jint inputSize, jint sampleRate) {
|
||||||
|
const uint8_t* inputBuffer = reinterpret_cast<const uint8_t*>(
|
||||||
|
env->GetDirectBufferAddress(jInputBuffer));
|
||||||
|
const int32_t sampleCount =
|
||||||
|
opus_packet_get_nb_samples(inputBuffer, inputSize, sampleRate);
|
||||||
|
return sampleCount * kBytesPerSample * channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNC(void, opusClose, jlong jDecoder) {
|
||||||
|
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||||
|
opus_multistream_decoder_destroy(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNC(void, opusReset, jlong jDecoder) {
|
||||||
|
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||||
|
opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNC(jstring, getLibopusVersion) {
|
||||||
|
return env->NewStringUTF(opus_get_version_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
FUNC(jstring, opusGetErrorMessage, jint errorCode) {
|
||||||
|
return env->NewStringUTF(opus_strerror(errorCode));
|
||||||
|
}
|
6
extensions/opus/src/main/proguard.cfg
Normal file
6
extensions/opus/src/main/proguard.cfg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Proguard rules specific to the Opus extension.
|
||||||
|
|
||||||
|
# This prevents the names of native methods from being obfuscated.
|
||||||
|
-keepclasseswithmembernames class * {
|
||||||
|
native <methods>;
|
||||||
|
}
|
16
extensions/opus/src/main/project.properties
Normal file
16
extensions/opus/src/main/project.properties
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# 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
|
2
extensions/opus/src/main/res/.README.txt
Normal file
2
extensions/opus/src/main/res/.README.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
This file is needed to make sure the res directory is present.
|
||||||
|
The file is ignored by the Android toolchain because its name starts with a dot.
|
@ -46,7 +46,7 @@ public abstract class SubtitleParser extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final ParserException decode(SubtitleInputBuffer inputBuffer,
|
protected final ParserException decode(SubtitleInputBuffer inputBuffer,
|
||||||
SubtitleOutputBuffer outputBuffer) {
|
SubtitleOutputBuffer outputBuffer, boolean reset) {
|
||||||
try {
|
try {
|
||||||
Subtitle subtitle = decode(inputBuffer.data.array(), inputBuffer.size);
|
Subtitle subtitle = decode(inputBuffer.data.array(), inputBuffer.size);
|
||||||
outputBuffer.setOutput(inputBuffer.timeUs, subtitle, inputBuffer.subsampleOffsetUs);
|
outputBuffer.setOutput(inputBuffer.timeUs, subtitle, inputBuffer.subsampleOffsetUs);
|
||||||
|
@ -52,7 +52,7 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
private I dequeuedInputBuffer;
|
private I dequeuedInputBuffer;
|
||||||
|
|
||||||
private E exception;
|
private E exception;
|
||||||
private boolean flushDecodedOutputBuffer;
|
private boolean flushed;
|
||||||
private boolean released;
|
private boolean released;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,7 +138,7 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
@Override
|
@Override
|
||||||
public final void flush() {
|
public final void flush() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
flushDecodedOutputBuffer = true;
|
flushed = true;
|
||||||
if (dequeuedInputBuffer != null) {
|
if (dequeuedInputBuffer != null) {
|
||||||
releaseInputBufferInternal(dequeuedInputBuffer);
|
releaseInputBufferInternal(dequeuedInputBuffer);
|
||||||
dequeuedInputBuffer = null;
|
dequeuedInputBuffer = null;
|
||||||
@ -203,6 +203,7 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
private boolean decode() throws InterruptedException {
|
private boolean decode() throws InterruptedException {
|
||||||
I inputBuffer;
|
I inputBuffer;
|
||||||
O outputBuffer;
|
O outputBuffer;
|
||||||
|
boolean resetDecoder;
|
||||||
|
|
||||||
// Wait until we have an input buffer to decode, and an output buffer to decode into.
|
// Wait until we have an input buffer to decode, and an output buffer to decode into.
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
@ -214,7 +215,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
}
|
}
|
||||||
inputBuffer = queuedInputBuffers.removeFirst();
|
inputBuffer = queuedInputBuffers.removeFirst();
|
||||||
outputBuffer = availableOutputBuffers[--availableOutputBufferCount];
|
outputBuffer = availableOutputBuffers[--availableOutputBufferCount];
|
||||||
flushDecodedOutputBuffer = false;
|
resetDecoder = flushed;
|
||||||
|
flushed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inputBuffer.isEndOfStream()) {
|
if (inputBuffer.isEndOfStream()) {
|
||||||
@ -223,7 +225,7 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
if (inputBuffer.isDecodeOnly()) {
|
if (inputBuffer.isDecodeOnly()) {
|
||||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||||
}
|
}
|
||||||
exception = decode(inputBuffer, outputBuffer);
|
exception = decode(inputBuffer, outputBuffer, resetDecoder);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
// Memory barrier to ensure that the decoder exception is visible from the playback thread.
|
// Memory barrier to ensure that the decoder exception is visible from the playback thread.
|
||||||
synchronized (lock) {}
|
synchronized (lock) {}
|
||||||
@ -232,7 +234,7 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
if (flushDecodedOutputBuffer || outputBuffer.isDecodeOnly()) {
|
if (flushed || outputBuffer.isDecodeOnly()) {
|
||||||
// If a flush occurred while decoding or the buffer was only for decoding (not presentation)
|
// If a flush occurred while decoding or the buffer was only for decoding (not presentation)
|
||||||
// then make the output buffer available again rather than queueing it to be consumed.
|
// then make the output buffer available again rather than queueing it to be consumed.
|
||||||
releaseOutputBufferInternal(outputBuffer);
|
releaseOutputBufferInternal(outputBuffer);
|
||||||
@ -279,8 +281,9 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
|
|||||||
* {@link C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on
|
* {@link C#BUFFER_FLAG_DECODE_ONLY} will be set if the same flag is set on
|
||||||
* {@code inputBuffer}, but the decoder may set/unset the flag if required. If the flag is set
|
* {@code inputBuffer}, but the decoder may set/unset the flag if required. If the flag is set
|
||||||
* after this method returns, any output will not be presented.
|
* after this method returns, any output will not be presented.
|
||||||
|
* @param reset True if the decoder must be reset before decoding.
|
||||||
* @return A decoder exception if an error occurred, or null if decoding was successful.
|
* @return A decoder exception if an error occurred, or null if decoding was successful.
|
||||||
*/
|
*/
|
||||||
protected abstract E decode(I inputBuffer, O outputBuffer);
|
protected abstract E decode(I inputBuffer, O outputBuffer, boolean reset);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,10 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
include ':library'
|
include ':library'
|
||||||
include ':demo'
|
include ':demo'
|
||||||
|
include ':extension-opus'
|
||||||
include ':extension-okhttp'
|
include ':extension-okhttp'
|
||||||
include ':extension-flac'
|
include ':extension-flac'
|
||||||
|
|
||||||
|
project(':extension-opus').projectDir = new File(settingsDir, 'extensions/opus')
|
||||||
project(':extension-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
|
project(':extension-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
|
||||||
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
|
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user