mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-19 19:13:03 +00:00
Merge remote-tracking branch 'origin/new-core'
This commit is contained in:
commit
ce7146175a
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,6 +30,7 @@ Thumbs.db
|
|||||||
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
|
#.idea/workspace.xml - remove # and delete .idea if it better suit your needs.
|
||||||
.gradle
|
.gradle
|
||||||
build/
|
build/
|
||||||
|
app/app.iml
|
||||||
|
|
||||||
# Compiled JNI libraries folder
|
# Compiled JNI libraries folder
|
||||||
**/jniLibs
|
**/jniLibs
|
||||||
|
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -1,3 +1,3 @@
|
|||||||
[submodule "app/src/main/jni/jnienet/enet"]
|
[submodule "moonlight-common"]
|
||||||
path = app/src/main/jni/jnienet/enet
|
path = moonlight-common
|
||||||
url = https://github.com/cgutman/enet.git
|
url = https://github.com/moonlight-stream/moonlight-common.git
|
||||||
|
153
app/app.iml
153
app/app.iml
@ -1,153 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module external.linked.project.id=":app" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="moonlight-android" external.system.module.version="unspecified" type="JAVA_MODULE" version="4">
|
|
||||||
<component name="FacetManager">
|
|
||||||
<facet type="android-gradle" name="Android-Gradle">
|
|
||||||
<configuration>
|
|
||||||
<option name="GRADLE_PROJECT_PATH" value=":app" />
|
|
||||||
</configuration>
|
|
||||||
</facet>
|
|
||||||
<facet type="android" name="Android">
|
|
||||||
<configuration>
|
|
||||||
<option name="SELECTED_BUILD_VARIANT" value="nonRootDebug" />
|
|
||||||
<option name="SELECTED_TEST_ARTIFACT" value="_android_test_" />
|
|
||||||
<option name="ASSEMBLE_TASK_NAME" value="assembleNonRootDebug" />
|
|
||||||
<option name="COMPILE_JAVA_TASK_NAME" value="compileNonRootDebugSources" />
|
|
||||||
<afterSyncTasks>
|
|
||||||
<task>generateNonRootDebugSources</task>
|
|
||||||
</afterSyncTasks>
|
|
||||||
<option name="ALLOW_USER_CONFIGURATION" value="false" />
|
|
||||||
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
|
|
||||||
<option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
|
|
||||||
<option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
|
|
||||||
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
|
|
||||||
</configuration>
|
|
||||||
</facet>
|
|
||||||
<facet type="native-android-gradle" name="Native-Android-Gradle">
|
|
||||||
<configuration>
|
|
||||||
<option name="SELECTED_BUILD_VARIANT" value="nonRootDebug" />
|
|
||||||
</configuration>
|
|
||||||
</facet>
|
|
||||||
</component>
|
|
||||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7" inherit-compiler-output="false">
|
|
||||||
<output url="file://$MODULE_DIR$/build/intermediates/classes/nonRoot/debug" />
|
|
||||||
<output-test url="file://$MODULE_DIR$/build/intermediates/classes/test/nonRoot/debug" />
|
|
||||||
<exclude-output />
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/jnienet/enet" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/jnienet" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/evdev_reader" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/jni/nv_opus_dec" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/nonRoot/debug" isTestSource="false" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/nonRoot/debug" isTestSource="false" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/nonRoot/debug" isTestSource="false" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/nonRoot/debug" isTestSource="false" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/nonRoot/debug" isTestSource="false" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/nonRoot/debug" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/nonRoot/debug" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/res" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/resources" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/assets" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/aidl" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/java" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/rs" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRootDebug/shaders" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/nonRoot/debug" isTestSource="true" generated="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/nonRoot/debug" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/nonRoot/debug" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/res" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/resources" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/assets" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/aidl" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/java" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/rs" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRootDebug/shaders" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/res" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/resources" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/assets" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/aidl" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/java" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/rs" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/nonRoot/shaders" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/res" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/resources" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/assets" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/aidl" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/java" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/rs" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testNonRoot/shaders" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/res" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/resources" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/assets" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/aidl" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/java" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/rs" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestNonRoot/shaders" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/blame" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental-safeguard" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/jniLibs" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndkBuild" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/shaders" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/transforms" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/outputs" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/build/tmp" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
<orderEntry type="library" exported="" name="bcprov-jdk15on-1.52" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="bcpkix-jdk15on-1.52" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="tinyrtsp" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="limelight-common" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="jmdns-3.4.2" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="okhttp-2.4.0" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="jcodec-0.1.9-patched" level="project" />
|
|
||||||
<orderEntry type="library" exported="" name="okio-1.5.0" level="project" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
@ -5,14 +5,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25
|
compileSdkVersion 25
|
||||||
buildToolsVersion '25.0.2'
|
buildToolsVersion '25.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 25
|
targetSdkVersion 25
|
||||||
|
|
||||||
versionName "4.8.5"
|
versionName "5.0.0"
|
||||||
versionCode = 117
|
versionCode = 121
|
||||||
}
|
}
|
||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
@ -38,17 +38,10 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled false
|
minifyEnabled false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
proguardFiles getDefaultProguardFile('proguard-android.txt')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// These lines are required to avoid dexing issues with the BouncyCastle library
|
|
||||||
// bundled with limelight-common.jar
|
|
||||||
packagingOptions {
|
|
||||||
exclude 'META-INF/BCKEY.SF'
|
|
||||||
exclude 'META-INF/BCKEY.DSA'
|
|
||||||
}
|
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
ndkBuild {
|
ndkBuild {
|
||||||
path "src/main/jni/Android.mk"
|
path "src/main/jni/Android.mk"
|
||||||
@ -59,10 +52,6 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile 'org.bouncycastle:bcprov-jdk15on:1.52'
|
compile 'org.bouncycastle:bcprov-jdk15on:1.52'
|
||||||
compile 'org.bouncycastle:bcpkix-jdk15on:1.52'
|
compile 'org.bouncycastle:bcpkix-jdk15on:1.52'
|
||||||
compile 'com.squareup.okhttp:okhttp:2.4.0'
|
compile project(':moonlight-common')
|
||||||
compile 'com.squareup.okio:okio:1.5.0'
|
|
||||||
compile files('libs/jmdns-3.4.2.jar')
|
|
||||||
compile files('libs/limelight-common.jar')
|
|
||||||
compile files('libs/tinyrtsp.jar')
|
|
||||||
compile files('libs/jcodec-0.1.9-patched.jar')
|
compile files('libs/jcodec-0.1.9-patched.jar')
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -81,6 +81,10 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
|
|
||||||
// Get the computer object
|
// Get the computer object
|
||||||
computer = managerBinder.getComputer(UUID.fromString(uuidString));
|
computer = managerBinder.getComputer(UUID.fromString(uuidString));
|
||||||
|
if (computer == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
appGridAdapter = new AppGridAdapter(AppView.this,
|
appGridAdapter = new AppGridAdapter(AppView.this,
|
||||||
|
@ -10,16 +10,15 @@ import com.limelight.binding.input.TouchContext;
|
|||||||
import com.limelight.binding.input.driver.UsbDriverService;
|
import com.limelight.binding.input.driver.UsbDriverService;
|
||||||
import com.limelight.binding.input.evdev.EvdevListener;
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
import com.limelight.binding.input.virtual_controller.VirtualController;
|
import com.limelight.binding.input.virtual_controller.VirtualController;
|
||||||
import com.limelight.binding.video.EnhancedDecoderRenderer;
|
|
||||||
import com.limelight.binding.video.MediaCodecDecoderRenderer;
|
import com.limelight.binding.video.MediaCodecDecoderRenderer;
|
||||||
import com.limelight.binding.video.MediaCodecHelper;
|
import com.limelight.binding.video.MediaCodecHelper;
|
||||||
import com.limelight.nvstream.NvConnection;
|
import com.limelight.nvstream.NvConnection;
|
||||||
import com.limelight.nvstream.NvConnectionListener;
|
import com.limelight.nvstream.NvConnectionListener;
|
||||||
import com.limelight.nvstream.StreamConfiguration;
|
import com.limelight.nvstream.StreamConfiguration;
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
|
||||||
import com.limelight.nvstream.http.NvApp;
|
import com.limelight.nvstream.http.NvApp;
|
||||||
import com.limelight.nvstream.input.KeyboardPacket;
|
import com.limelight.nvstream.input.KeyboardPacket;
|
||||||
import com.limelight.nvstream.input.MouseButtonPacket;
|
import com.limelight.nvstream.input.MouseButtonPacket;
|
||||||
|
import com.limelight.nvstream.jni.MoonBridge;
|
||||||
import com.limelight.preferences.PreferenceConfiguration;
|
import com.limelight.preferences.PreferenceConfiguration;
|
||||||
import com.limelight.ui.GameGestures;
|
import com.limelight.ui.GameGestures;
|
||||||
import com.limelight.ui.StreamView;
|
import com.limelight.ui.StreamView;
|
||||||
@ -35,7 +34,6 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.hardware.input.InputManager;
|
import android.hardware.input.InputManager;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
@ -61,8 +59,6 @@ import android.widget.FrameLayout;
|
|||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
|
|
||||||
public class Game extends Activity implements SurfaceHolder.Callback,
|
public class Game extends Activity implements SurfaceHolder.Callback,
|
||||||
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
|
OnGenericMotionListener, OnTouchListener, NvConnectionListener, EvdevListener,
|
||||||
@ -83,7 +79,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
private ControllerHandler controllerHandler;
|
private ControllerHandler controllerHandler;
|
||||||
private VirtualController virtualController;
|
private VirtualController virtualController;
|
||||||
private KeyboardTranslator keybTranslator;
|
|
||||||
|
|
||||||
private PreferenceConfiguration prefConfig;
|
private PreferenceConfiguration prefConfig;
|
||||||
|
|
||||||
@ -101,12 +96,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
private ShortcutHelper shortcutHelper;
|
private ShortcutHelper shortcutHelper;
|
||||||
|
|
||||||
private EnhancedDecoderRenderer decoderRenderer;
|
private MediaCodecDecoderRenderer decoderRenderer;
|
||||||
|
|
||||||
private WifiManager.WifiLock wifiLock;
|
private WifiManager.WifiLock wifiLock;
|
||||||
|
|
||||||
private int drFlags = 0;
|
|
||||||
|
|
||||||
private boolean connectedToUsbDriverService = false;
|
private boolean connectedToUsbDriverService = false;
|
||||||
private ServiceConnection usbDriverServiceConnection = new ServiceConnection() {
|
private ServiceConnection usbDriverServiceConnection = new ServiceConnection() {
|
||||||
@Override
|
@Override
|
||||||
@ -173,10 +166,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Read the stream preferences
|
// Read the stream preferences
|
||||||
prefConfig = PreferenceConfiguration.readPreferences(this);
|
prefConfig = PreferenceConfiguration.readPreferences(this);
|
||||||
|
|
||||||
if (prefConfig.stretchVideo) {
|
|
||||||
drFlags |= VideoDecoderRenderer.FLAG_FILL_SCREEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listen for events on the game surface
|
// Listen for events on the game surface
|
||||||
streamView = (StreamView) findViewById(R.id.surfaceView);
|
streamView = (StreamView) findViewById(R.id.surfaceView);
|
||||||
streamView.setOnGenericMotionListener(this);
|
streamView.setOnGenericMotionListener(this);
|
||||||
@ -236,20 +225,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
.setApp(new NvApp(appName, appId))
|
.setApp(new NvApp(appName, appId))
|
||||||
.setBitrate(prefConfig.bitrate * 1000)
|
.setBitrate(prefConfig.bitrate * 1000)
|
||||||
.setEnableSops(prefConfig.enableSops)
|
.setEnableSops(prefConfig.enableSops)
|
||||||
.enableAdaptiveResolution((decoderRenderer.getCapabilities() &
|
|
||||||
VideoDecoderRenderer.CAPABILITY_ADAPTIVE_RESOLUTION) != 0)
|
|
||||||
.enableLocalAudioPlayback(prefConfig.playHostAudio)
|
.enableLocalAudioPlayback(prefConfig.playHostAudio)
|
||||||
.setMaxPacketSize(remote ? 1024 : 1292)
|
.setMaxPacketSize(remote ? 1024 : 1292)
|
||||||
.setRemote(remote)
|
.setRemote(remote)
|
||||||
.setHevcSupported(decoderRenderer.isHevcSupported())
|
.setHevcSupported(decoderRenderer.isHevcSupported())
|
||||||
.setAudioConfiguration(prefConfig.enable51Surround ?
|
.setAudioConfiguration(prefConfig.enable51Surround ?
|
||||||
StreamConfiguration.AUDIO_CONFIGURATION_5_1 :
|
MoonBridge.AUDIO_CONFIGURATION_51_SURROUND :
|
||||||
StreamConfiguration.AUDIO_CONFIGURATION_STEREO)
|
MoonBridge.AUDIO_CONFIGURATION_STEREO)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Initialize the connection
|
// Initialize the connection
|
||||||
conn = new NvConnection(host, uniqueId, Game.this, config, PlatformBinding.getCryptoProvider(this));
|
conn = new NvConnection(host, uniqueId, config, PlatformBinding.getCryptoProvider(this));
|
||||||
keybTranslator = new KeyboardTranslator(conn);
|
|
||||||
controllerHandler = new ControllerHandler(this, conn, this, prefConfig.multiController, prefConfig.deadzonePercentage);
|
controllerHandler = new ControllerHandler(this, conn, this, prefConfig.multiController, prefConfig.deadzonePercentage);
|
||||||
|
|
||||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||||
@ -419,11 +405,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onDestroy() {
|
||||||
super.onStop();
|
super.onDestroy();
|
||||||
|
|
||||||
SpinnerDialog.closeDialogs(this);
|
|
||||||
Dialog.closeDialogs();
|
|
||||||
|
|
||||||
if (controllerHandler != null) {
|
if (controllerHandler != null) {
|
||||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||||
@ -438,7 +421,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
VideoDecoderRenderer.VideoFormat videoFormat = conn.getActiveVideoFormat();
|
int videoFormat = decoderRenderer.getActiveVideoFormat();
|
||||||
|
|
||||||
displayedFailureDialog = true;
|
displayedFailureDialog = true;
|
||||||
stopConnection();
|
stopConnection();
|
||||||
@ -457,11 +440,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the video codec to the post-stream toast
|
// Add the video codec to the post-stream toast
|
||||||
if (message != null && videoFormat != VideoDecoderRenderer.VideoFormat.Unknown) {
|
if (message != null) {
|
||||||
if (videoFormat == VideoDecoderRenderer.VideoFormat.H265) {
|
if (videoFormat == MoonBridge.VIDEO_FORMAT_H265) {
|
||||||
message += " [H.265]";
|
message += " [H.265]";
|
||||||
}
|
}
|
||||||
else {
|
else if (videoFormat == MoonBridge.VIDEO_FORMAT_H264) {
|
||||||
message += " [H.264]";
|
message += " [H.264]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,6 +453,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
SpinnerDialog.closeDialogs(this);
|
||||||
|
Dialog.closeDialogs();
|
||||||
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
@ -589,7 +580,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
// Try the keyboard handler
|
// Try the keyboard handler
|
||||||
short translated = keybTranslator.translate(event.getKeyCode());
|
short translated = KeyboardTranslator.translate(event.getKeyCode());
|
||||||
if (translated == 0) {
|
if (translated == 0) {
|
||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
@ -604,8 +595,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return super.onKeyDown(keyCode, event);
|
return super.onKeyDown(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
keybTranslator.sendKeyDown(translated,
|
conn.sendKeyboardInput(translated, KeyboardPacket.KEY_DOWN, getModifierState());
|
||||||
getModifierState(event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -632,7 +622,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
// Try the keyboard handler
|
// Try the keyboard handler
|
||||||
short translated = keybTranslator.translate(event.getKeyCode());
|
short translated = KeyboardTranslator.translate(event.getKeyCode());
|
||||||
if (translated == 0) {
|
if (translated == 0) {
|
||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
@ -646,8 +636,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return super.onKeyUp(keyCode, event);
|
return super.onKeyUp(keyCode, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
keybTranslator.sendKeyUp(translated,
|
conn.sendKeyboardInput(translated, KeyboardPacket.KEY_UP, getModifierState(event));
|
||||||
getModifierState(event));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -889,14 +878,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stageStarting(Stage stage) {
|
public void stageStarting(String stage) {
|
||||||
if (spinner != null) {
|
if (spinner != null) {
|
||||||
spinner.setMessage(getResources().getString(R.string.conn_starting)+" "+stage.getName());
|
spinner.setMessage(getResources().getString(R.string.conn_starting)+" "+stage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stageComplete(Stage stage) {
|
public void stageComplete(String stage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopConnection() {
|
private void stopConnection() {
|
||||||
@ -913,7 +902,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stageFailed(Stage stage) {
|
public void stageFailed(String stage, long errorCode) {
|
||||||
if (spinner != null) {
|
if (spinner != null) {
|
||||||
spinner.dismiss();
|
spinner.dismiss();
|
||||||
spinner = null;
|
spinner = null;
|
||||||
@ -921,17 +910,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
if (!displayedFailureDialog) {
|
if (!displayedFailureDialog) {
|
||||||
displayedFailureDialog = true;
|
displayedFailureDialog = true;
|
||||||
|
LimeLog.severe(stage+" failed: "+errorCode);
|
||||||
|
|
||||||
stopConnection();
|
stopConnection();
|
||||||
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title),
|
Dialog.displayDialog(this, getResources().getString(R.string.conn_error_title),
|
||||||
getResources().getString(R.string.conn_error_msg)+" "+stage.getName(), true);
|
getResources().getString(R.string.conn_error_msg)+" "+stage, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectionTerminated(Exception e) {
|
public void connectionTerminated(long errorCode) {
|
||||||
if (!displayedFailureDialog) {
|
if (!displayedFailureDialog) {
|
||||||
displayedFailureDialog = true;
|
displayedFailureDialog = true;
|
||||||
e.printStackTrace();
|
LimeLog.severe("Connection terminated: "+errorCode);
|
||||||
|
|
||||||
stopConnection();
|
stopConnection();
|
||||||
Dialog.displayDialog(this, getResources().getString(R.string.conn_terminated_title),
|
Dialog.displayDialog(this, getResources().getString(R.string.conn_terminated_title),
|
||||||
@ -993,8 +984,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
if (!connected && !connecting) {
|
if (!connected && !connecting) {
|
||||||
connecting = true;
|
connecting = true;
|
||||||
|
|
||||||
conn.start(PlatformBinding.getDeviceName(), holder, drFlags,
|
decoderRenderer.setRenderTarget(holder);
|
||||||
PlatformBinding.getAudioRenderer(), decoderRenderer);
|
conn.start(PlatformBinding.getAudioRenderer(), decoderRenderer, Game.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1053,17 +1044,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void keyboardEvent(boolean buttonDown, short keyCode) {
|
public void keyboardEvent(boolean buttonDown, short keyCode) {
|
||||||
short keyMap = keybTranslator.translate(keyCode);
|
short keyMap = KeyboardTranslator.translate(keyCode);
|
||||||
if (keyMap != 0) {
|
if (keyMap != 0) {
|
||||||
if (handleSpecialKeys(keyMap, buttonDown)) {
|
if (handleSpecialKeys(keyMap, buttonDown)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buttonDown) {
|
if (buttonDown) {
|
||||||
keybTranslator.sendKeyDown(keyMap, getModifierState());
|
conn.sendKeyboardInput(keyMap, KeyboardPacket.KEY_DOWN, getModifierState());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
keybTranslator.sendKeyUp(keyMap, getModifierState());
|
conn.sendKeyboardInput(keyMap, KeyboardPacket.KEY_UP, getModifierState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,34 +6,31 @@ import android.media.AudioTrack;
|
|||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.nvstream.av.audio.AudioRenderer;
|
import com.limelight.nvstream.av.audio.AudioRenderer;
|
||||||
|
import com.limelight.nvstream.jni.MoonBridge;
|
||||||
|
|
||||||
public class AndroidAudioRenderer implements AudioRenderer {
|
public class AndroidAudioRenderer implements AudioRenderer {
|
||||||
|
|
||||||
private AudioTrack track;
|
private AudioTrack track;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean streamInitialized(int channelCount, int channelMask, int samplesPerFrame, int sampleRate) {
|
public int setup(int audioConfiguration) {
|
||||||
int channelConfig;
|
int channelConfig;
|
||||||
int bufferSize;
|
int bufferSize;
|
||||||
int bytesPerFrame = (samplesPerFrame * 2);
|
int bytesPerFrame;
|
||||||
|
|
||||||
switch (channelCount)
|
switch (audioConfiguration)
|
||||||
{
|
{
|
||||||
case 1:
|
case MoonBridge.AUDIO_CONFIGURATION_STEREO:
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
break;
|
bytesPerFrame = 2 * 240 * 2;
|
||||||
case 2:
|
break;
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
case MoonBridge.AUDIO_CONFIGURATION_51_SURROUND:
|
||||||
break;
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
case 4:
|
bytesPerFrame = 6 * 240 * 2;
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
break;
|
||||||
break;
|
default:
|
||||||
case 6:
|
LimeLog.severe("Decoder returned unhandled channel count");
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
return -1;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LimeLog.severe("Decoder returned unhandled channel count");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're not supposed to request less than the minimum
|
// We're not supposed to request less than the minimum
|
||||||
@ -46,7 +43,7 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
|||||||
bufferSize = bytesPerFrame * 2;
|
bufferSize = bytesPerFrame * 2;
|
||||||
|
|
||||||
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||||
sampleRate,
|
48000,
|
||||||
channelConfig,
|
channelConfig,
|
||||||
AudioFormat.ENCODING_PCM_16BIT,
|
AudioFormat.ENCODING_PCM_16BIT,
|
||||||
bufferSize,
|
bufferSize,
|
||||||
@ -61,7 +58,7 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
|||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
// Now try the larger buffer size
|
// Now try the larger buffer size
|
||||||
bufferSize = Math.max(AudioTrack.getMinBufferSize(sampleRate,
|
bufferSize = Math.max(AudioTrack.getMinBufferSize(48000,
|
||||||
channelConfig,
|
channelConfig,
|
||||||
AudioFormat.ENCODING_PCM_16BIT),
|
AudioFormat.ENCODING_PCM_16BIT),
|
||||||
bytesPerFrame * 2);
|
bytesPerFrame * 2);
|
||||||
@ -70,7 +67,7 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
|||||||
bufferSize = (((bufferSize + (bytesPerFrame - 1)) / bytesPerFrame) * bytesPerFrame);
|
bufferSize = (((bufferSize + (bytesPerFrame - 1)) / bytesPerFrame) * bytesPerFrame);
|
||||||
|
|
||||||
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
track = new AudioTrack(AudioManager.STREAM_MUSIC,
|
||||||
sampleRate,
|
48000,
|
||||||
channelConfig,
|
channelConfig,
|
||||||
AudioFormat.ENCODING_PCM_16BIT,
|
AudioFormat.ENCODING_PCM_16BIT,
|
||||||
bufferSize,
|
bufferSize,
|
||||||
@ -79,24 +76,26 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LimeLog.info("Audio track buffer size: "+bufferSize);
|
LimeLog.info("Audio track buffer size: "+bufferSize);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void playDecodedAudio(byte[] audioData, int offset, int length) {
|
|
||||||
track.write(audioData, offset, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void streamClosing() {
|
|
||||||
if (track != null) {
|
|
||||||
track.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCapabilities() {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playDecodedAudio(byte[] audioData) {
|
||||||
|
track.write(audioData, 0, audioData.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
// Immediately drop all pending data
|
||||||
|
track.pause();
|
||||||
|
track.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanup() {
|
||||||
|
track.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1120,6 +1120,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
float rightStickX, float rightStickY,
|
float rightStickX, float rightStickY,
|
||||||
float leftTrigger, float rightTrigger) {
|
float leftTrigger, float rightTrigger) {
|
||||||
UsbDeviceContext context = usbDeviceContexts.get(controllerId);
|
UsbDeviceContext context = usbDeviceContexts.get(controllerId);
|
||||||
|
if (context == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Vector2d leftStickVector = populateCachedVector(leftStickX, leftStickY);
|
Vector2d leftStickVector = populateCachedVector(leftStickX, leftStickY);
|
||||||
|
|
||||||
|
@ -3,14 +3,13 @@ package com.limelight.binding.input;
|
|||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
|
|
||||||
import com.limelight.nvstream.NvConnection;
|
import com.limelight.nvstream.NvConnection;
|
||||||
import com.limelight.nvstream.input.KeycodeTranslator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to translate a Android key code into the codes GFE is expecting
|
* Class to translate a Android key code into the codes GFE is expecting
|
||||||
* @author Diego Waxemberg
|
* @author Diego Waxemberg
|
||||||
* @author Cameron Gutman
|
* @author Cameron Gutman
|
||||||
*/
|
*/
|
||||||
public class KeyboardTranslator extends KeycodeTranslator {
|
public class KeyboardTranslator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GFE's prefix for every key code
|
* GFE's prefix for every key code
|
||||||
@ -59,21 +58,12 @@ public class KeyboardTranslator extends KeycodeTranslator {
|
|||||||
public static final int VK_QUOTE = 222;
|
public static final int VK_QUOTE = 222;
|
||||||
public static final int VK_PAUSE = 19;
|
public static final int VK_PAUSE = 19;
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new translator for the specified connection
|
|
||||||
* @param conn the connection to which the translated codes are sent
|
|
||||||
*/
|
|
||||||
public KeyboardTranslator(NvConnection conn) {
|
|
||||||
super(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates the given keycode and returns the GFE keycode
|
* Translates the given keycode and returns the GFE keycode
|
||||||
* @param keycode the code to be translated
|
* @param keycode the code to be translated
|
||||||
* @return a GFE keycode for the given keycode
|
* @return a GFE keycode for the given keycode
|
||||||
*/
|
*/
|
||||||
@Override
|
public static short translate(int keycode) {
|
||||||
public short translate(int keycode) {
|
|
||||||
int translated;
|
int translated;
|
||||||
|
|
||||||
/* There seems to be no clean mapping between Android key codes
|
/* There seems to be no clean mapping between Android key codes
|
||||||
|
@ -2,6 +2,7 @@ package com.limelight.binding.input.evdev;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Looper;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
@ -198,6 +199,26 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void runInNetworkSafeContextSynchronously(Runnable runnable) {
|
||||||
|
// This function is used to avoid Android's strict NetworkOnMainThreadException.
|
||||||
|
// For our usage, it is highly unlikely to cause problems since we only do
|
||||||
|
// write operations and only to localhost sockets.
|
||||||
|
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
|
||||||
|
Thread t = new Thread(runnable);
|
||||||
|
t.start();
|
||||||
|
try {
|
||||||
|
t.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// The main thread should never be interrupted
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Run the runnable directly
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void enableCapture() {
|
||||||
if (!started) {
|
if (!started) {
|
||||||
@ -207,26 +228,38 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
started = true;
|
started = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Send a request to regrab if we're already capturing
|
// This may be called on the main thread
|
||||||
if (!shutdown && evdevOut != null) {
|
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||||
try {
|
@Override
|
||||||
evdevOut.write(REGRAB_REQUEST);
|
public void run() {
|
||||||
} catch (IOException e) {
|
// Send a request to regrab if we're already capturing
|
||||||
e.printStackTrace();
|
if (!shutdown && evdevOut != null) {
|
||||||
|
try {
|
||||||
|
evdevOut.write(REGRAB_REQUEST);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void disableCapture() {
|
||||||
if (started && !shutdown && evdevOut != null) {
|
// This may be called on the main thread
|
||||||
try {
|
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||||
evdevOut.write(UNGRAB_REQUEST);
|
@Override
|
||||||
} catch (IOException e) {
|
public void run() {
|
||||||
e.printStackTrace();
|
if (started && !shutdown && evdevOut != null) {
|
||||||
|
try {
|
||||||
|
evdevOut.write(UNGRAB_REQUEST);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -234,6 +267,8 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
// We need to stop the process in this context otherwise
|
// We need to stop the process in this context otherwise
|
||||||
// we could get stuck waiting on output from the process
|
// we could get stuck waiting on output from the process
|
||||||
// in order to terminate it.
|
// in order to terminate it.
|
||||||
|
//
|
||||||
|
// This may be called on the main thread.
|
||||||
|
|
||||||
if (!started) {
|
if (!started) {
|
||||||
return;
|
return;
|
||||||
@ -242,37 +277,42 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
|
|||||||
shutdown = true;
|
shutdown = true;
|
||||||
handlerThread.interrupt();
|
handlerThread.interrupt();
|
||||||
|
|
||||||
if (servSock != null) {
|
runInNetworkSafeContextSynchronously(new Runnable() {
|
||||||
try {
|
@Override
|
||||||
servSock.close();
|
public void run() {
|
||||||
} catch (IOException e) {
|
if (servSock != null) {
|
||||||
e.printStackTrace();
|
try {
|
||||||
}
|
servSock.close();
|
||||||
}
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (evdevSock != null) {
|
if (evdevSock != null) {
|
||||||
try {
|
try {
|
||||||
evdevSock.close();
|
evdevSock.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evdevIn != null) {
|
if (evdevIn != null) {
|
||||||
try {
|
try {
|
||||||
evdevIn.close();
|
evdevIn.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (evdevOut != null) {
|
if (evdevOut != null) {
|
||||||
try {
|
try {
|
||||||
evdevOut.close();
|
evdevOut.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
if (su != null) {
|
if (su != null) {
|
||||||
su.destroy();
|
su.destroy();
|
||||||
|
@ -190,8 +190,14 @@ public class DigitalButton extends VirtualControllerElement {
|
|||||||
for (DigitalButtonListener listener : listeners) {
|
for (DigitalButtonListener listener : listeners) {
|
||||||
listener.onRelease();
|
listener.onRelease();
|
||||||
}
|
}
|
||||||
timerLongClick.cancel();
|
|
||||||
longClickTimerTask.cancel();
|
// We may be called for a release without a prior click
|
||||||
|
if (timerLongClick != null) {
|
||||||
|
timerLongClick.cancel();
|
||||||
|
}
|
||||||
|
if (longClickTimerTask != null) {
|
||||||
|
longClickTimerTask.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
package com.limelight.binding.video;
|
|
||||||
|
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
|
||||||
|
|
||||||
public abstract class EnhancedDecoderRenderer extends VideoDecoderRenderer {
|
|
||||||
public abstract boolean isHevcSupported();
|
|
||||||
public abstract boolean isAvcSupported();
|
|
||||||
}
|
|
@ -2,17 +2,14 @@ package com.limelight.binding.video;
|
|||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.locks.LockSupport;
|
|
||||||
|
|
||||||
import org.jcodec.codecs.h264.H264Utils;
|
import org.jcodec.codecs.h264.H264Utils;
|
||||||
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
|
import org.jcodec.codecs.h264.io.model.SeqParameterSet;
|
||||||
import org.jcodec.codecs.h264.io.model.VUIParameters;
|
import org.jcodec.codecs.h264.io.model.VUIParameters;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
import com.limelight.LimeLog;
|
||||||
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
|
||||||
import com.limelight.nvstream.av.DecodeUnit;
|
|
||||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||||
import com.limelight.nvstream.av.video.VideoDepacketizer;
|
import com.limelight.nvstream.jni.MoonBridge;
|
||||||
import com.limelight.preferences.PreferenceConfiguration;
|
import com.limelight.preferences.PreferenceConfiguration;
|
||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
@ -23,7 +20,7 @@ import android.media.MediaCodec.CodecException;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
|
|
||||||
public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
||||||
|
|
||||||
private static final boolean USE_FRAME_RENDER_TIME = false;
|
private static final boolean USE_FRAME_RENDER_TIME = false;
|
||||||
|
|
||||||
@ -34,26 +31,28 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
private MediaCodecInfo hevcDecoder;
|
private MediaCodecInfo hevcDecoder;
|
||||||
|
|
||||||
private MediaCodec videoDecoder;
|
private MediaCodec videoDecoder;
|
||||||
private Thread rendererThread;
|
private Thread rendererThread, spinnerThread;
|
||||||
private boolean needsSpsBitstreamFixup, isExynos4;
|
private boolean needsSpsBitstreamFixup, isExynos4;
|
||||||
private VideoDepacketizer depacketizer;
|
|
||||||
private boolean adaptivePlayback, directSubmit;
|
private boolean adaptivePlayback, directSubmit;
|
||||||
private boolean constrainedHighProfile;
|
private boolean constrainedHighProfile;
|
||||||
|
private boolean refFrameInvalidationAvc, refFrameInvalidationHevc;
|
||||||
|
private boolean refFrameInvalidationActive;
|
||||||
private int initialWidth, initialHeight;
|
private int initialWidth, initialHeight;
|
||||||
private VideoFormat videoFormat;
|
private int videoFormat;
|
||||||
|
private Object renderTarget;
|
||||||
|
private boolean stopping;
|
||||||
|
|
||||||
private boolean needsBaselineSpsHack;
|
private boolean needsBaselineSpsHack;
|
||||||
private SeqParameterSet savedSps;
|
private SeqParameterSet savedSps;
|
||||||
|
|
||||||
private long lastTimestampUs;
|
private long lastTimestampUs;
|
||||||
private long totalTimeMs;
|
|
||||||
private long decoderTimeMs;
|
private long decoderTimeMs;
|
||||||
|
private long totalTimeMs;
|
||||||
private int totalFrames;
|
private int totalFrames;
|
||||||
|
|
||||||
private int numSpsIn;
|
private int numSpsIn;
|
||||||
private int numPpsIn;
|
private int numPpsIn;
|
||||||
private int numVpsIn;
|
private int numVpsIn;
|
||||||
private int numIframeIn;
|
|
||||||
|
|
||||||
private MediaCodecInfo findAvcDecoder() {
|
private MediaCodecInfo findAvcDecoder() {
|
||||||
MediaCodecInfo decoder = MediaCodecHelper.findProbableSafeDecoder("video/avc", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
|
MediaCodecInfo decoder = MediaCodecHelper.findProbableSafeDecoder("video/avc", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
|
||||||
@ -91,6 +90,10 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
return decoderInfo;
|
return decoderInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setRenderTarget(Object renderTarget) {
|
||||||
|
this.renderTarget = renderTarget;
|
||||||
|
}
|
||||||
|
|
||||||
public MediaCodecDecoderRenderer(int videoFormat) {
|
public MediaCodecDecoderRenderer(int videoFormat) {
|
||||||
//dumpDecoders();
|
//dumpDecoders();
|
||||||
|
|
||||||
@ -118,25 +121,35 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
if (avcDecoder != null) {
|
if (avcDecoder != null) {
|
||||||
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
||||||
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(avcDecoder.getName());
|
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(avcDecoder.getName());
|
||||||
|
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName());
|
||||||
|
refFrameInvalidationHevc = MediaCodecHelper.decoderSupportsRefFrameInvalidationHevc(avcDecoder.getName());
|
||||||
|
|
||||||
if (directSubmit) {
|
if (directSubmit) {
|
||||||
LimeLog.info("Decoder "+avcDecoder.getName()+" will use direct submit");
|
LimeLog.info("Decoder "+avcDecoder.getName()+" will use direct submit");
|
||||||
}
|
}
|
||||||
|
if (refFrameInvalidationAvc) {
|
||||||
|
LimeLog.info("Decoder "+avcDecoder.getName()+" will use reference frame invalidation for AVC");
|
||||||
|
}
|
||||||
|
if (refFrameInvalidationHevc) {
|
||||||
|
LimeLog.info("Decoder "+avcDecoder.getName()+" will use reference frame invalidation for HEVC");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isHevcSupported() {
|
public boolean isHevcSupported() {
|
||||||
return hevcDecoder != null;
|
return hevcDecoder != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvcSupported() {
|
public boolean isAvcSupported() {
|
||||||
return avcDecoder != null;
|
return avcDecoder != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getActiveVideoFormat() {
|
||||||
|
return this.videoFormat;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setup(VideoDecoderRenderer.VideoFormat format, int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
public int setup(int format, int width, int height, int redrawRate) {
|
||||||
this.initialWidth = width;
|
this.initialWidth = width;
|
||||||
this.initialHeight = height;
|
this.initialHeight = height;
|
||||||
this.videoFormat = format;
|
this.videoFormat = format;
|
||||||
@ -144,13 +157,13 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
String mimeType;
|
String mimeType;
|
||||||
String selectedDecoderName;
|
String selectedDecoderName;
|
||||||
|
|
||||||
if (videoFormat == VideoFormat.H264) {
|
if (videoFormat == MoonBridge.VIDEO_FORMAT_H264) {
|
||||||
mimeType = "video/avc";
|
mimeType = "video/avc";
|
||||||
selectedDecoderName = avcDecoder.getName();
|
selectedDecoderName = avcDecoder.getName();
|
||||||
|
|
||||||
if (avcDecoder == null) {
|
if (avcDecoder == null) {
|
||||||
LimeLog.severe("No available AVC decoder!");
|
LimeLog.severe("No available AVC decoder!");
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These fixups only apply to H264 decoders
|
// These fixups only apply to H264 decoders
|
||||||
@ -170,19 +183,24 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
if (isExynos4) {
|
if (isExynos4) {
|
||||||
LimeLog.info("Decoder "+selectedDecoderName+" is on Exynos 4");
|
LimeLog.info("Decoder "+selectedDecoderName+" is on Exynos 4");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refFrameInvalidationActive = refFrameInvalidationAvc;
|
||||||
}
|
}
|
||||||
else if (videoFormat == VideoFormat.H265) {
|
else if (videoFormat == MoonBridge.VIDEO_FORMAT_H265) {
|
||||||
mimeType = "video/hevc";
|
mimeType = "video/hevc";
|
||||||
selectedDecoderName = hevcDecoder.getName();
|
selectedDecoderName = hevcDecoder.getName();
|
||||||
|
|
||||||
if (hevcDecoder == null) {
|
if (hevcDecoder == null) {
|
||||||
LimeLog.severe("No available HEVC decoder!");
|
LimeLog.severe("No available HEVC decoder!");
|
||||||
return false;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refFrameInvalidationActive = refFrameInvalidationHevc;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Unknown format
|
// Unknown format
|
||||||
return false;
|
LimeLog.severe("Unknown format");
|
||||||
|
return -3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Codecs have been known to throw all sorts of crazy runtime exceptions
|
// Codecs have been known to throw all sorts of crazy runtime exceptions
|
||||||
@ -190,7 +208,8 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
try {
|
try {
|
||||||
videoDecoder = MediaCodec.createByCodecName(selectedDecoderName);
|
videoDecoder = MediaCodec.createByCodecName(selectedDecoderName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
e.printStackTrace();
|
||||||
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaFormat videoFormat = MediaFormat.createVideoFormat(mimeType, width, height);
|
MediaFormat videoFormat = MediaFormat.createVideoFormat(mimeType, width, height);
|
||||||
@ -210,26 +229,38 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
videoFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, Short.MAX_VALUE);
|
videoFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, Short.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0);
|
try {
|
||||||
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
videoDecoder.configure(videoFormat, ((SurfaceHolder)renderTarget).getSurface(), null, 0);
|
||||||
|
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
||||||
|
|
||||||
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
videoDecoder.setOnFrameRenderedListener(new MediaCodec.OnFrameRenderedListener() {
|
videoDecoder.setOnFrameRenderedListener(new MediaCodec.OnFrameRenderedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onFrameRendered(MediaCodec mediaCodec, long presentationTimeUs, long renderTimeNanos) {
|
public void onFrameRendered(MediaCodec mediaCodec, long presentationTimeUs, long renderTimeNanos) {
|
||||||
long delta = (renderTimeNanos / 1000000L) - (presentationTimeUs / 1000);
|
long delta = (renderTimeNanos / 1000000L) - (presentationTimeUs / 1000);
|
||||||
if (delta >= 0 && delta < 1000) {
|
if (delta >= 0 && delta < 1000) {
|
||||||
if (USE_FRAME_RENDER_TIME) {
|
if (USE_FRAME_RENDER_TIME) {
|
||||||
totalTimeMs += delta;
|
totalTimeMs += delta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}, null);
|
||||||
}, null);
|
}
|
||||||
|
|
||||||
|
LimeLog.info("Using codec "+selectedDecoderName+" for hardware decoding "+mimeType);
|
||||||
|
|
||||||
|
// Start the decoder
|
||||||
|
videoDecoder.start();
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
legacyInputBuffers = videoDecoder.getInputBuffers();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
LimeLog.info("Using codec "+selectedDecoderName+" for hardware decoding "+mimeType);
|
return 0;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags) {
|
private void handleDecoderException(Exception e, ByteBuffer buf, int codecFlags) {
|
||||||
@ -248,7 +279,8 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only throw if this happens at the beginning of a stream
|
// Only throw if this happens at the beginning of a stream
|
||||||
if (totalFrames < 60) {
|
// but not if we're stopping
|
||||||
|
if (totalFrames > 0 && totalFrames < 20 && !stopping) {
|
||||||
if (buf != null || codecFlags != 0) {
|
if (buf != null || codecFlags != 0) {
|
||||||
throw new RendererException(this, e, buf, codecFlags);
|
throw new RendererException(this, e, buf, codecFlags);
|
||||||
}
|
}
|
||||||
@ -258,7 +290,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDirectSubmitRendererThread()
|
private void startRendererThread()
|
||||||
{
|
{
|
||||||
rendererThread = new Thread() {
|
rendererThread = new Thread() {
|
||||||
@Override
|
@Override
|
||||||
@ -314,13 +346,36 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
rendererThread.start();
|
rendererThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int dequeueInputBuffer(boolean wait, boolean infiniteWait) {
|
private void startSpinnerThread() {
|
||||||
int index;
|
spinnerThread = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// This thread exists to keep the CPU at a higher DVFS state on devices
|
||||||
|
// where the governor scales clock speed sporadically, causing dropped frames.
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
Thread.yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
spinnerThread.setPriority(Thread.MIN_PRIORITY);
|
||||||
|
spinnerThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int dequeueInputBuffer() {
|
||||||
|
int index = -1;
|
||||||
long startTime, queueTime;
|
long startTime, queueTime;
|
||||||
|
|
||||||
startTime = MediaCodecHelper.getMonotonicMillis();
|
startTime = MediaCodecHelper.getMonotonicMillis();
|
||||||
|
|
||||||
index = videoDecoder.dequeueInputBuffer(wait ? (infiniteWait ? -1 : 3000) : 0);
|
try {
|
||||||
|
while (rendererThread.isAlive() && index < 0 && !stopping) {
|
||||||
|
index = videoDecoder.dequeueInputBuffer(10000);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
handleDecoderException(e, null, 0);
|
||||||
|
return MediaCodec.INFO_TRY_AGAIN_LATER;
|
||||||
|
}
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
@ -334,185 +389,35 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLegacyRendererThread()
|
|
||||||
{
|
|
||||||
rendererThread = new Thread() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
BufferInfo info = new BufferInfo();
|
|
||||||
DecodeUnit du = null;
|
|
||||||
int inputIndex = -1;
|
|
||||||
long lastDuDequeueTime = 0;
|
|
||||||
while (!isInterrupted())
|
|
||||||
{
|
|
||||||
// In order to get as much data to the decoder as early as possible,
|
|
||||||
// try to submit up to 5 decode units at once without blocking.
|
|
||||||
if (inputIndex == -1 && du == null) {
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < 5; i++) {
|
|
||||||
inputIndex = dequeueInputBuffer(false, false);
|
|
||||||
du = depacketizer.pollNextDecodeUnit();
|
|
||||||
if (du != null) {
|
|
||||||
lastDuDequeueTime = MediaCodecHelper.getMonotonicMillis();
|
|
||||||
notifyDuReceived(du);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop if we can't get a DU or input buffer
|
|
||||||
if (du == null || inputIndex == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
submitDecodeUnit(du, inputIndex);
|
|
||||||
|
|
||||||
du = null;
|
|
||||||
inputIndex = -1;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
inputIndex = -1;
|
|
||||||
handleDecoderException(e, null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab an input buffer if we don't have one already.
|
|
||||||
// This way we can have one ready hopefully by the time
|
|
||||||
// the depacketizer is done with this frame. It's important
|
|
||||||
// that this can timeout because it's possible that we could exhaust
|
|
||||||
// the decoder's input buffers and deadlocks because aren't pulling
|
|
||||||
// frames out of the other end.
|
|
||||||
if (inputIndex == -1) {
|
|
||||||
try {
|
|
||||||
// If we've got a DU waiting to be given to the decoder,
|
|
||||||
// wait a full 3 ms for an input buffer. Otherwise
|
|
||||||
// just see if we can get one immediately.
|
|
||||||
inputIndex = dequeueInputBuffer(du != null, false);
|
|
||||||
} catch (Exception e) {
|
|
||||||
inputIndex = -1;
|
|
||||||
handleDecoderException(e, null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab a decode unit if we don't have one already
|
|
||||||
if (du == null) {
|
|
||||||
du = depacketizer.pollNextDecodeUnit();
|
|
||||||
if (du != null) {
|
|
||||||
lastDuDequeueTime = MediaCodecHelper.getMonotonicMillis();
|
|
||||||
notifyDuReceived(du);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we've got both a decode unit and an input buffer, we'll
|
|
||||||
// submit now. Otherwise, we wait until we have one.
|
|
||||||
if (du != null && inputIndex >= 0) {
|
|
||||||
long submissionTime = MediaCodecHelper.getMonotonicMillis();
|
|
||||||
if (submissionTime - lastDuDequeueTime >= 20) {
|
|
||||||
LimeLog.warning("Receiving an input buffer took too long: "+(submissionTime - lastDuDequeueTime)+" ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
submitDecodeUnit(du, inputIndex);
|
|
||||||
|
|
||||||
// DU and input buffer have both been consumed
|
|
||||||
du = null;
|
|
||||||
inputIndex = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to output a frame
|
|
||||||
try {
|
|
||||||
int outIndex = videoDecoder.dequeueOutputBuffer(info, 0);
|
|
||||||
|
|
||||||
if (outIndex >= 0) {
|
|
||||||
long presentationTimeUs = info.presentationTimeUs;
|
|
||||||
int lastIndex = outIndex;
|
|
||||||
|
|
||||||
// Get the last output buffer in the queue
|
|
||||||
while ((outIndex = videoDecoder.dequeueOutputBuffer(info, 0)) >= 0) {
|
|
||||||
videoDecoder.releaseOutputBuffer(lastIndex, false);
|
|
||||||
lastIndex = outIndex;
|
|
||||||
presentationTimeUs = info.presentationTimeUs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render the last buffer
|
|
||||||
videoDecoder.releaseOutputBuffer(lastIndex, true);
|
|
||||||
|
|
||||||
// Add delta time to the totals (excluding probable outliers)
|
|
||||||
long delta = MediaCodecHelper.getMonotonicMillis()-(presentationTimeUs/1000);
|
|
||||||
if (delta >= 0 && delta < 1000) {
|
|
||||||
decoderTimeMs += delta;
|
|
||||||
if (!USE_FRAME_RENDER_TIME) {
|
|
||||||
totalTimeMs += delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (outIndex) {
|
|
||||||
case MediaCodec.INFO_TRY_AGAIN_LATER:
|
|
||||||
// Getting an input buffer may already block
|
|
||||||
// so don't park if we still need to do that
|
|
||||||
if (inputIndex >= 0) {
|
|
||||||
LockSupport.parkNanos(1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
|
||||||
LimeLog.info("Output format changed");
|
|
||||||
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleDecoderException(e, null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
rendererThread.setName("Video - Renderer (MediaCodec)");
|
|
||||||
rendererThread.setPriority(Thread.MAX_PRIORITY);
|
|
||||||
rendererThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
@Override
|
||||||
public boolean start(VideoDepacketizer depacketizer) {
|
public void start() {
|
||||||
this.depacketizer = depacketizer;
|
startRendererThread();
|
||||||
|
startSpinnerThread();
|
||||||
// Start the decoder
|
|
||||||
videoDecoder.start();
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
legacyInputBuffers = videoDecoder.getInputBuffers();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (directSubmit) {
|
|
||||||
startDirectSubmitRendererThread();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
startLegacyRendererThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (rendererThread != null) {
|
stopping = true;
|
||||||
// Halt the rendering thread
|
|
||||||
rendererThread.interrupt();
|
|
||||||
try {
|
|
||||||
rendererThread.join();
|
|
||||||
} catch (InterruptedException ignored) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
// We could stop the decoder here, but it seems to cause some problems
|
// Halt the rendering thread
|
||||||
// so we'll just let release take care of it.
|
rendererThread.interrupt();
|
||||||
|
try {
|
||||||
|
rendererThread.join();
|
||||||
|
} catch (InterruptedException ignored) { }
|
||||||
|
|
||||||
|
// Halt the spinner thread
|
||||||
|
spinnerThread.interrupt();
|
||||||
|
try {
|
||||||
|
spinnerThread.join();
|
||||||
|
} catch (InterruptedException ignored) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release() {
|
public void cleanup() {
|
||||||
if (videoDecoder != null) {
|
videoDecoder.release();
|
||||||
videoDecoder.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueInputBuffer(int inputBufferIndex, int offset, int length, long timestampUs, int codecFlags) {
|
private boolean queueInputBuffer(int inputBufferIndex, int offset, int length, long timestampUs, int codecFlags) {
|
||||||
// Try 25 times to submit the input buffer before throwing a real exception
|
// Try 25 times to submit the input buffer before throwing a real exception
|
||||||
int i;
|
int i;
|
||||||
Exception lastException = null;
|
Exception lastException = null;
|
||||||
@ -529,9 +434,17 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == 25) {
|
if (i == 25 && totalFrames > 0 && totalFrames < 20 && !stopping) {
|
||||||
throw new RendererException(this, lastException, null, codecFlags);
|
throw new RendererException(this, lastException, null, codecFlags);
|
||||||
}
|
}
|
||||||
|
else if (i != 25) {
|
||||||
|
// Queued input buffer
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Failed to queue
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using the new getInputBuffer() API on Lollipop allows
|
// Using the new getInputBuffer() API on Lollipop allows
|
||||||
@ -570,7 +483,10 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void submitDecodeUnit(DecodeUnit decodeUnit, int inputBufferIndex) {
|
@Override
|
||||||
|
public int submitDecodeUnit(byte[] frameData, int frameLength) {
|
||||||
|
totalFrames++;
|
||||||
|
|
||||||
long timestampUs = System.nanoTime() / 1000;
|
long timestampUs = System.nanoTime() / 1000;
|
||||||
if (timestampUs <= lastTimestampUs) {
|
if (timestampUs <= lastTimestampUs) {
|
||||||
// We can't submit multiple buffers with the same timestamp
|
// We can't submit multiple buffers with the same timestamp
|
||||||
@ -579,39 +495,36 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
}
|
}
|
||||||
lastTimestampUs = timestampUs;
|
lastTimestampUs = timestampUs;
|
||||||
|
|
||||||
|
int inputBufferIndex = dequeueInputBuffer();
|
||||||
|
if (inputBufferIndex < 0) {
|
||||||
|
// We're being torn down now
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
|
}
|
||||||
|
|
||||||
ByteBuffer buf = getEmptyInputBuffer(inputBufferIndex);
|
ByteBuffer buf = getEmptyInputBuffer(inputBufferIndex);
|
||||||
|
|
||||||
int codecFlags = 0;
|
int codecFlags = 0;
|
||||||
int decodeUnitFlags = decodeUnit.getFlags();
|
|
||||||
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
|
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
|
||||||
}
|
|
||||||
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_SYNC_FRAME) != 0) {
|
|
||||||
codecFlags |= MediaCodec.BUFFER_FLAG_SYNC_FRAME;
|
|
||||||
numIframeIn++;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean needsSpsReplay = false;
|
boolean needsSpsReplay = false;
|
||||||
|
|
||||||
if ((decodeUnitFlags & DecodeUnit.DU_FLAG_CODEC_CONFIG) != 0) {
|
// H264 SPS
|
||||||
ByteBufferDescriptor header = decodeUnit.getBufferHead();
|
if (frameData[4] == 0x67) {
|
||||||
|
numSpsIn++;
|
||||||
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
|
|
||||||
// H264 SPS
|
ByteBuffer spsBuf = ByteBuffer.wrap(frameData);
|
||||||
if (header.data[header.offset+4] == 0x67) {
|
|
||||||
numSpsIn++;
|
|
||||||
|
|
||||||
ByteBuffer spsBuf = ByteBuffer.wrap(header.data);
|
// Skip to the start of the NALU data
|
||||||
|
spsBuf.position(5);
|
||||||
|
|
||||||
// Skip to the start of the NALU data
|
// The H264Utils.readSPS function safely handles
|
||||||
spsBuf.position(header.offset+5);
|
// Annex B NALUs (including NALUs with escape sequences)
|
||||||
|
SeqParameterSet sps = H264Utils.readSPS(spsBuf);
|
||||||
|
|
||||||
// The H264Utils.readSPS function safely handles
|
// Some decoders rely on H264 level to decide how many buffers are needed
|
||||||
// Annex B NALUs (including NALUs with escape sequences)
|
// Since we only need one frame buffered, we'll set the level as low as we can
|
||||||
SeqParameterSet sps = H264Utils.readSPS(spsBuf);
|
// for known resolution combinations. Reference frame invalidation may need
|
||||||
|
// these, so leave them be for those decoders.
|
||||||
// Some decoders rely on H264 level to decide how many buffers are needed
|
if (!refFrameInvalidationActive) {
|
||||||
// Since we only need one frame buffered, we'll set the level as low as we can
|
|
||||||
// for known resolution combinations
|
|
||||||
if (initialWidth == 1280 && initialHeight == 720) {
|
if (initialWidth == 1280 && initialHeight == 720) {
|
||||||
// Max 5 buffered frames at 1280x720x60
|
// Max 5 buffered frames at 1280x720x60
|
||||||
LimeLog.info("Patching level_idc to 32");
|
LimeLog.info("Patching level_idc to 32");
|
||||||
@ -625,123 +538,142 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
else {
|
else {
|
||||||
// Leave the profile alone (currently 5.0)
|
// Leave the profile alone (currently 5.0)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TI OMAP4 requires a reference frame count of 1 to decode successfully. Exynos 4
|
// TI OMAP4 requires a reference frame count of 1 to decode successfully. Exynos 4
|
||||||
// also requires this fixup.
|
// also requires this fixup.
|
||||||
//
|
//
|
||||||
// I'm doing this fixup for all devices because I haven't seen any devices that
|
// I'm doing this fixup for all devices because I haven't seen any devices that
|
||||||
// this causes issues for. At worst, it seems to do nothing and at best it fixes
|
// this causes issues for. At worst, it seems to do nothing and at best it fixes
|
||||||
// issues with video lag, hangs, and crashes.
|
// issues with video lag, hangs, and crashes.
|
||||||
|
//
|
||||||
|
// It does break reference frame invalidation, so we will not do that for decoders
|
||||||
|
// where we've enabled reference frame invalidation.
|
||||||
|
if (!refFrameInvalidationActive) {
|
||||||
LimeLog.info("Patching num_ref_frames in SPS");
|
LimeLog.info("Patching num_ref_frames in SPS");
|
||||||
sps.num_ref_frames = 1;
|
sps.num_ref_frames = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// GFE 2.5.11 changed the SPS to add additional extensions
|
// GFE 2.5.11 changed the SPS to add additional extensions
|
||||||
// Some devices don't like these so we remove them here.
|
// Some devices don't like these so we remove them here.
|
||||||
sps.vuiParams.video_signal_type_present_flag = false;
|
sps.vuiParams.video_signal_type_present_flag = false;
|
||||||
sps.vuiParams.colour_description_present_flag = false;
|
sps.vuiParams.colour_description_present_flag = false;
|
||||||
sps.vuiParams.chroma_loc_info_present_flag = false;
|
sps.vuiParams.chroma_loc_info_present_flag = false;
|
||||||
|
|
||||||
if (needsSpsBitstreamFixup || isExynos4) {
|
if ((needsSpsBitstreamFixup || isExynos4) && !refFrameInvalidationActive) {
|
||||||
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
|
// The SPS that comes in the current H264 bytestream doesn't set bitstream_restriction_flag
|
||||||
// or max_dec_frame_buffering which increases decoding latency on Tegra.
|
// or max_dec_frame_buffering which increases decoding latency on Tegra.
|
||||||
|
|
||||||
// GFE 2.5.11 started sending bitstream restrictions
|
// GFE 2.5.11 started sending bitstream restrictions
|
||||||
if (sps.vuiParams.bitstreamRestriction == null) {
|
if (sps.vuiParams.bitstreamRestriction == null) {
|
||||||
LimeLog.info("Adding bitstream restrictions");
|
LimeLog.info("Adding bitstream restrictions");
|
||||||
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
|
sps.vuiParams.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
|
||||||
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true;
|
sps.vuiParams.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = true;
|
||||||
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_horizontal = 16;
|
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_horizontal = 16;
|
||||||
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_vertical = 16;
|
sps.vuiParams.bitstreamRestriction.log2_max_mv_length_vertical = 16;
|
||||||
sps.vuiParams.bitstreamRestriction.num_reorder_frames = 0;
|
sps.vuiParams.bitstreamRestriction.num_reorder_frames = 0;
|
||||||
}
|
|
||||||
else {
|
|
||||||
LimeLog.info("Patching bitstream restrictions");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some devices throw errors if max_dec_frame_buffering < num_ref_frames
|
|
||||||
sps.vuiParams.bitstreamRestriction.max_dec_frame_buffering = sps.num_ref_frames;
|
|
||||||
|
|
||||||
// These values are the defaults for the fields, but they are more aggressive
|
|
||||||
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
|
|
||||||
sps.vuiParams.bitstreamRestriction.max_bytes_per_pic_denom = 2;
|
|
||||||
sps.vuiParams.bitstreamRestriction.max_bits_per_mb_denom = 1;
|
|
||||||
|
|
||||||
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
|
|
||||||
// conservative values by GFE 2.5.11. We'll let those values stand.
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
|
LimeLog.info("Patching bitstream restrictions");
|
||||||
// will continue to not receive them now
|
|
||||||
sps.vuiParams.bitstreamRestriction = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we need to hack this SPS to say we're baseline, do so now
|
// Some devices throw errors if max_dec_frame_buffering < num_ref_frames
|
||||||
if (needsBaselineSpsHack) {
|
sps.vuiParams.bitstreamRestriction.max_dec_frame_buffering = sps.num_ref_frames;
|
||||||
LimeLog.info("Hacking SPS to baseline");
|
|
||||||
sps.profile_idc = 66;
|
|
||||||
savedSps = sps;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch the SPS constraint flags
|
// These values are the defaults for the fields, but they are more aggressive
|
||||||
doProfileSpecificSpsPatching(sps);
|
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
|
||||||
|
sps.vuiParams.bitstreamRestriction.max_bytes_per_pic_denom = 2;
|
||||||
|
sps.vuiParams.bitstreamRestriction.max_bits_per_mb_denom = 1;
|
||||||
|
|
||||||
// Write the annex B header
|
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
|
||||||
buf.put(header.data, header.offset, 5);
|
// conservative values by GFE 2.5.11. We'll let those values stand.
|
||||||
|
|
||||||
// The H264Utils.writeSPS function safely handles
|
|
||||||
// Annex B NALUs (including NALUs with escape sequences)
|
|
||||||
ByteBuffer escapedNalu = H264Utils.writeSPS(sps, header.length);
|
|
||||||
buf.put(escapedNalu);
|
|
||||||
|
|
||||||
queueInputBuffer(inputBufferIndex,
|
|
||||||
0, buf.position(),
|
|
||||||
timestampUs, codecFlags);
|
|
||||||
|
|
||||||
depacketizer.freeDecodeUnit(decodeUnit);
|
|
||||||
return;
|
|
||||||
|
|
||||||
// H264 PPS
|
|
||||||
} else if (header.data[header.offset+4] == 0x68) {
|
|
||||||
numPpsIn++;
|
|
||||||
|
|
||||||
if (needsBaselineSpsHack) {
|
|
||||||
LimeLog.info("Saw PPS; disabling SPS hack");
|
|
||||||
needsBaselineSpsHack = false;
|
|
||||||
|
|
||||||
// Give the decoder the SPS again with the proper profile now
|
|
||||||
needsSpsReplay = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (header.data[header.offset+4] == 0x40) {
|
else {
|
||||||
numVpsIn++;
|
// Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11
|
||||||
|
// will continue to not receive them now
|
||||||
|
sps.vuiParams.bitstreamRestriction = null;
|
||||||
}
|
}
|
||||||
else if (header.data[header.offset+4] == 0x42) {
|
|
||||||
numSpsIn++;
|
// If we need to hack this SPS to say we're baseline, do so now
|
||||||
|
if (needsBaselineSpsHack) {
|
||||||
|
LimeLog.info("Hacking SPS to baseline");
|
||||||
|
sps.profile_idc = 66;
|
||||||
|
savedSps = sps;
|
||||||
}
|
}
|
||||||
else if (header.data[header.offset+4] == 0x44) {
|
|
||||||
numPpsIn++;
|
// Patch the SPS constraint flags
|
||||||
|
doProfileSpecificSpsPatching(sps);
|
||||||
|
|
||||||
|
// Write the annex B header
|
||||||
|
buf.put(frameData, 0, 5);
|
||||||
|
|
||||||
|
// The H264Utils.writeSPS function safely handles
|
||||||
|
// Annex B NALUs (including NALUs with escape sequences)
|
||||||
|
ByteBuffer escapedNalu = H264Utils.writeSPS(sps, frameLength);
|
||||||
|
buf.put(escapedNalu);
|
||||||
|
|
||||||
|
if (queueInputBuffer(inputBufferIndex,
|
||||||
|
0, buf.position(),
|
||||||
|
timestampUs, codecFlags)) {
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return MoonBridge.DR_NEED_IDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// H264 PPS
|
||||||
|
} else if (frameData[4] == 0x68) {
|
||||||
|
numPpsIn++;
|
||||||
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
|
|
||||||
|
|
||||||
|
if (needsBaselineSpsHack) {
|
||||||
|
LimeLog.info("Saw PPS; disabling SPS hack");
|
||||||
|
needsBaselineSpsHack = false;
|
||||||
|
|
||||||
|
// Give the decoder the SPS again with the proper profile now
|
||||||
|
needsSpsReplay = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (frameData[4] == 0x40) {
|
||||||
|
numVpsIn++;
|
||||||
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
|
}
|
||||||
|
else if (frameData[4] == 0x42) {
|
||||||
|
numSpsIn++;
|
||||||
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
|
}
|
||||||
|
else if (frameData[4] == 0x44) {
|
||||||
|
numPpsIn++;
|
||||||
|
codecFlags |= MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy data from our buffer list into the input buffer
|
// Copy data from our buffer list into the input buffer
|
||||||
for (ByteBufferDescriptor desc = decodeUnit.getBufferHead();
|
buf.put(frameData, 0, frameLength);
|
||||||
desc != null; desc = desc.nextDescriptor) {
|
|
||||||
buf.put(desc.data, desc.offset, desc.length);
|
if (!queueInputBuffer(inputBufferIndex,
|
||||||
|
0, frameLength,
|
||||||
|
timestampUs, codecFlags)) {
|
||||||
|
return MoonBridge.DR_NEED_IDR;
|
||||||
}
|
}
|
||||||
|
|
||||||
queueInputBuffer(inputBufferIndex,
|
|
||||||
0, decodeUnit.getDataLength(),
|
|
||||||
timestampUs, codecFlags);
|
|
||||||
|
|
||||||
depacketizer.freeDecodeUnit(decodeUnit);
|
|
||||||
|
|
||||||
if (needsSpsReplay) {
|
if (needsSpsReplay) {
|
||||||
replaySps();
|
if (!replaySps()) {
|
||||||
|
return MoonBridge.DR_NEED_IDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
LimeLog.info("SPS replay complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return MoonBridge.DR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaySps() {
|
private boolean replaySps() {
|
||||||
int inputIndex = dequeueInputBuffer(true, true);
|
int inputIndex = dequeueInputBuffer();
|
||||||
|
if (inputIndex < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ByteBuffer inputBuffer = getEmptyInputBuffer(inputIndex);
|
ByteBuffer inputBuffer = getEmptyInputBuffer(inputIndex);
|
||||||
|
|
||||||
// Write the Annex B header
|
// Write the Annex B header
|
||||||
@ -762,36 +694,35 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
savedSps = null;
|
savedSps = null;
|
||||||
|
|
||||||
// Queue the new SPS
|
// Queue the new SPS
|
||||||
queueInputBuffer(inputIndex,
|
return queueInputBuffer(inputIndex,
|
||||||
0, inputBuffer.position(),
|
0, inputBuffer.position(),
|
||||||
System.nanoTime() / 1000,
|
System.nanoTime() / 1000,
|
||||||
MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
|
MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
|
||||||
|
|
||||||
LimeLog.info("SPS replay complete");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getCapabilities() {
|
public int getCapabilities() {
|
||||||
int caps = 0;
|
int capabilities = 0;
|
||||||
|
|
||||||
caps |= adaptivePlayback ?
|
// We always request 4 slices per frame to speed up decoding on some hardware
|
||||||
VideoDecoderRenderer.CAPABILITY_ADAPTIVE_RESOLUTION : 0;
|
capabilities |= MoonBridge.CAPABILITY_SLICES_PER_FRAME((byte) 4);
|
||||||
|
|
||||||
caps |= directSubmit ?
|
// Enable reference frame invalidation on supported hardware
|
||||||
VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT : 0;
|
if (refFrameInvalidationAvc) {
|
||||||
|
capabilities |= MoonBridge.CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC;
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAverageDecoderLatency() {
|
|
||||||
if (totalFrames == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return (int)(decoderTimeMs / totalFrames);
|
if (refFrameInvalidationHevc) {
|
||||||
|
capabilities |= MoonBridge.CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable direct submit on supported hardware
|
||||||
|
if (directSubmit) {
|
||||||
|
capabilities |= MoonBridge.CAPABILITY_DIRECT_SUBMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAverageEndToEndLatency() {
|
public int getAverageEndToEndLatency() {
|
||||||
if (totalFrames == 0) {
|
if (totalFrames == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -799,33 +730,11 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
return (int)(totalTimeMs / totalFrames);
|
return (int)(totalTimeMs / totalFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyDuReceived(DecodeUnit du) {
|
public int getAverageDecoderLatency() {
|
||||||
long currentTime = MediaCodecHelper.getMonotonicMillis();
|
if (totalFrames == 0) {
|
||||||
long delta = currentTime-du.getReceiveTimestamp();
|
return 0;
|
||||||
if (delta >= 0 && delta < 1000) {
|
|
||||||
totalTimeMs += currentTime-du.getReceiveTimestamp();
|
|
||||||
totalFrames++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void directSubmitDecodeUnit(DecodeUnit du) {
|
|
||||||
int inputIndex = -1;
|
|
||||||
|
|
||||||
notifyDuReceived(du);
|
|
||||||
|
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
|
||||||
try {
|
|
||||||
inputIndex = dequeueInputBuffer(true, true);
|
|
||||||
break;
|
|
||||||
} catch (Exception e) {
|
|
||||||
handleDecoderException(e, null, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputIndex >= 0) {
|
|
||||||
submitDecodeUnit(du, inputIndex);
|
|
||||||
}
|
}
|
||||||
|
return (int)(decoderTimeMs / totalFrames);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RendererException extends RuntimeException {
|
public class RendererException extends RuntimeException {
|
||||||
@ -855,7 +764,7 @@ public class MediaCodecDecoderRenderer extends EnhancedDecoderRenderer {
|
|||||||
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
|
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
|
||||||
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
||||||
str += "Initial video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
str += "Initial video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
||||||
str += "In stats: "+renderer.numVpsIn+", "+renderer.numSpsIn+", "+renderer.numPpsIn+", "+renderer.numIframeIn+"\n";
|
str += "In stats: "+renderer.numVpsIn+", "+renderer.numSpsIn+", "+renderer.numPpsIn+"\n";
|
||||||
str += "Total frames: "+renderer.totalFrames+"\n";
|
str += "Total frames: "+renderer.totalFrames+"\n";
|
||||||
str += "Average end-to-end client latency: "+getAverageEndToEndLatency()+"ms\n";
|
str += "Average end-to-end client latency: "+getAverageEndToEndLatency()+"ms\n";
|
||||||
str += "Average hardware decoder latency: "+getAverageDecoderLatency()+"ms\n";
|
str += "Average hardware decoder latency: "+getAverageDecoderLatency()+"ms\n";
|
||||||
|
@ -32,6 +32,8 @@ public class MediaCodecHelper {
|
|||||||
private static final List<String> directSubmitPrefixes;
|
private static final List<String> directSubmitPrefixes;
|
||||||
private static final List<String> constrainedHighProfilePrefixes;
|
private static final List<String> constrainedHighProfilePrefixes;
|
||||||
private static final List<String> whitelistedHevcDecoders;
|
private static final List<String> whitelistedHevcDecoders;
|
||||||
|
private static final List<String> refFrameInvalidationAvcPrefixes;
|
||||||
|
private static final List<String> refFrameInvalidationHevcPrefixes;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
directSubmitPrefixes = new LinkedList<>();
|
directSubmitPrefixes = new LinkedList<>();
|
||||||
@ -45,8 +47,16 @@ public class MediaCodecHelper {
|
|||||||
directSubmitPrefixes.add("omx.brcm");
|
directSubmitPrefixes.add("omx.brcm");
|
||||||
directSubmitPrefixes.add("omx.TI");
|
directSubmitPrefixes.add("omx.TI");
|
||||||
directSubmitPrefixes.add("omx.arc");
|
directSubmitPrefixes.add("omx.arc");
|
||||||
|
directSubmitPrefixes.add("omx.nvidia");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
refFrameInvalidationAvcPrefixes = new LinkedList<>();
|
||||||
|
refFrameInvalidationHevcPrefixes = new LinkedList<>();
|
||||||
|
|
||||||
|
// Qualcomm and NVIDIA may be added at runtime
|
||||||
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
preferredDecoders = new LinkedList<>();
|
preferredDecoders = new LinkedList<>();
|
||||||
}
|
}
|
||||||
@ -65,6 +75,8 @@ public class MediaCodecHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
// If a decoder qualifies for reference frame invalidation,
|
||||||
|
// these entries will be ignored for those decoders.
|
||||||
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<>();
|
spsFixupBitstreamFixupDecoderPrefixes = new LinkedList<>();
|
||||||
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
|
spsFixupBitstreamFixupDecoderPrefixes.add("omx.nvidia");
|
||||||
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
|
spsFixupBitstreamFixupDecoderPrefixes.add("omx.qcom");
|
||||||
@ -113,20 +125,36 @@ public class MediaCodecHelper {
|
|||||||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||||
ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo();
|
ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo();
|
||||||
if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
|
if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
|
||||||
// Qualcomm's early HEVC decoders break hard on our HEVC stream. The best check to
|
|
||||||
// tell the good from the bad decoders are the generation of Adreno GPU included:
|
|
||||||
// 3xx - bad
|
|
||||||
// 4xx - good
|
|
||||||
//
|
|
||||||
// Unfortunately, it's not that easy to get that information here, so I'll use an
|
|
||||||
// approximation by checking the GLES level (<= 3.0 is bad).
|
|
||||||
LimeLog.info("OpenGL ES version: "+configInfo.reqGlEsVersion);
|
LimeLog.info("OpenGL ES version: "+configInfo.reqGlEsVersion);
|
||||||
if (configInfo.reqGlEsVersion > 0x30000) {
|
|
||||||
LimeLog.info("Added omx.qcom to supported decoders based on GLES 3.1+ support");
|
// Tegra K1 and later can do reference frame invalidation properly
|
||||||
whitelistedHevcDecoders.add("omx.qcom");
|
if (configInfo.reqGlEsVersion >= 0x30000) {
|
||||||
|
LimeLog.info("Added omx.nvidia to AVC reference frame invalidation support list");
|
||||||
|
refFrameInvalidationAvcPrefixes.add("omx.nvidia");
|
||||||
|
|
||||||
|
LimeLog.info("Added omx.qcom to AVC reference frame invalidation support list");
|
||||||
|
refFrameInvalidationAvcPrefixes.add("omx.qcom");
|
||||||
|
|
||||||
|
LimeLog.info("Added omx.intel to AVC reference frame invalidation support list");
|
||||||
|
refFrameInvalidationAvcPrefixes.add("omx.intel");
|
||||||
|
|
||||||
|
// Qualcomm's early HEVC decoders break hard on our HEVC stream. The best check to
|
||||||
|
// tell the good from the bad decoders are the generation of Adreno GPU included:
|
||||||
|
// 3xx - bad
|
||||||
|
// 4xx - good
|
||||||
|
//
|
||||||
|
// Unfortunately, it's not that easy to get that information here, so I'll use an
|
||||||
|
// approximation by checking the GLES level (<= 3.0 is bad).
|
||||||
|
if (configInfo.reqGlEsVersion > 0x30000) {
|
||||||
|
// FIXME: We prefer reference frame invalidation support (which is only doable on AVC on
|
||||||
|
// older Qualcomm chips) vs. enabling HEVC by default. The user can override using the settings
|
||||||
|
// to force HEVC on.
|
||||||
|
//LimeLog.info("Added omx.qcom to supported HEVC decoders based on GLES 3.1+ support");
|
||||||
|
//whitelistedHevcDecoders.add("omx.qcom");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
|
private static boolean isDecoderInList(List<String> decoderList, String decoderName) {
|
||||||
for (String badPrefix : decoderList) {
|
for (String badPrefix : decoderList) {
|
||||||
@ -190,6 +218,14 @@ public class MediaCodecHelper {
|
|||||||
return isDecoderInList(baselineProfileHackPrefixes, decoderName);
|
return isDecoderInList(baselineProfileHackPrefixes, decoderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean decoderSupportsRefFrameInvalidationAvc(String decoderName) {
|
||||||
|
return isDecoderInList(refFrameInvalidationAvcPrefixes, decoderName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean decoderSupportsRefFrameInvalidationHevc(String decoderName) {
|
||||||
|
return isDecoderInList(refFrameInvalidationHevcPrefixes, decoderName);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean decoderIsWhitelistedForHevc(String decoderName) {
|
public static boolean decoderIsWhitelistedForHevc(String decoderName) {
|
||||||
// TODO: Shield Tablet K1/LTE?
|
// TODO: Shield Tablet K1/LTE?
|
||||||
//
|
//
|
||||||
@ -212,6 +248,16 @@ public class MediaCodecHelper {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Software decoders are terrible and we never want to use them.
|
||||||
|
// We want to catch decoders like:
|
||||||
|
// OMX.qcom.video.decoder.hevcswvdec
|
||||||
|
// OMX.SEC.hevc.sw.dec
|
||||||
|
//
|
||||||
|
if (decoderName.contains("sw")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return isDecoderInList(whitelistedHevcDecoders, decoderName);
|
return isDecoderInList(whitelistedHevcDecoders, decoderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
app/src/main/java/com/limelight/utils/Vector2d.java
Normal file
47
app/src/main/java/com/limelight/utils/Vector2d.java
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package com.limelight.utils;
|
||||||
|
|
||||||
|
public class Vector2d {
|
||||||
|
private float x;
|
||||||
|
private float y;
|
||||||
|
private double magnitude;
|
||||||
|
|
||||||
|
public static final Vector2d ZERO = new Vector2d();
|
||||||
|
|
||||||
|
public Vector2d() {
|
||||||
|
initialize(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(float x, float y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.magnitude = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getMagnitude() {
|
||||||
|
return magnitude;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getNormalized(Vector2d vector) {
|
||||||
|
vector.initialize((float)(x / magnitude), (float)(y / magnitude));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void scalarMultiply(double factor) {
|
||||||
|
initialize((float)(x * factor), (float)(y * factor));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(float x) {
|
||||||
|
initialize(x, this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(float y) {
|
||||||
|
initialize(this.x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
# Android.mk for Moonlight's ENet JNI binding
|
|
||||||
MY_LOCAL_PATH := $(call my-dir)
|
|
||||||
|
|
||||||
include $(call all-subdir-makefiles)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(MY_LOCAL_PATH)
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := jnienet
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES := jnienet.c \
|
|
||||||
enet/callbacks.c \
|
|
||||||
enet/compress.c \
|
|
||||||
enet/host.c \
|
|
||||||
enet/list.c \
|
|
||||||
enet/packet.c \
|
|
||||||
enet/peer.c \
|
|
||||||
enet/protocol.c \
|
|
||||||
enet/unix.c \
|
|
||||||
enet/win32.c \
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := -DHAS_SOCKLEN_T=1
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/enet/include
|
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
@ -1 +0,0 @@
|
|||||||
Subproject commit 7546b505c12b305ad0245320b46cb2b070bab216
|
|
@ -1,148 +0,0 @@
|
|||||||
#include "enet/enet.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
#define CLIENT_TO_LONG(x) ((intptr_t)(x))
|
|
||||||
#define LONG_TO_CLIENT(x) ((ENetHost*)(intptr_t)(x))
|
|
||||||
|
|
||||||
#define PEER_TO_LONG(x) ((intptr_t)(x))
|
|
||||||
#define LONG_TO_PEER(x) ((ENetPeer*)(intptr_t)(x))
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_initializeEnet(JNIEnv *env, jobject class) {
|
|
||||||
return enet_initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_createClient(JNIEnv *env, jobject class, jstring address) {
|
|
||||||
ENetAddress enetAddress;
|
|
||||||
const char *addrStr;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
// Perform a lookup on the address to determine the address family
|
|
||||||
addrStr = (*env)->GetStringUTFChars(env, address, 0);
|
|
||||||
err = enet_address_set_host(&enetAddress, addrStr);
|
|
||||||
(*env)->ReleaseStringUTFChars(env, address, addrStr);
|
|
||||||
if (err < 0) {
|
|
||||||
return CLIENT_TO_LONG(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a client that can use 1 outgoing connection and 1 channel
|
|
||||||
return CLIENT_TO_LONG(enet_host_create(enetAddress.address.ss_family, NULL, 1, 1, 0, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jlong JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_connectToPeer(JNIEnv *env, jobject class, jlong client, jstring address, jint port, jint timeout) {
|
|
||||||
ENetPeer* peer;
|
|
||||||
ENetAddress enetAddress;
|
|
||||||
ENetEvent event;
|
|
||||||
const char *addrStr;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
// Initialize the ENet address
|
|
||||||
addrStr = (*env)->GetStringUTFChars(env, address, 0);
|
|
||||||
err = enet_address_set_host(&enetAddress, addrStr);
|
|
||||||
enet_address_set_port(&enetAddress, port);
|
|
||||||
(*env)->ReleaseStringUTFChars(env, address, addrStr);
|
|
||||||
if (err < 0) {
|
|
||||||
return PEER_TO_LONG(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the connection
|
|
||||||
peer = enet_host_connect(LONG_TO_CLIENT(client), &enetAddress, 1, 0);
|
|
||||||
if (peer == NULL) {
|
|
||||||
return PEER_TO_LONG(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the connect to complete
|
|
||||||
if (enet_host_service(LONG_TO_CLIENT(client), &event, timeout) <= 0 || event.type != ENET_EVENT_TYPE_CONNECT) {
|
|
||||||
enet_peer_reset(peer);
|
|
||||||
return PEER_TO_LONG(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the connect verify ACK is sent immediately
|
|
||||||
enet_host_flush(LONG_TO_CLIENT(client));
|
|
||||||
|
|
||||||
// Set the max peer timeout to 10 seconds
|
|
||||||
enet_peer_timeout(peer, ENET_PEER_TIMEOUT_LIMIT, ENET_PEER_TIMEOUT_MINIMUM, 10000);
|
|
||||||
|
|
||||||
return PEER_TO_LONG(peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_readPacket(JNIEnv *env, jobject class, jlong client, jbyteArray data, jint length, jint timeout) {
|
|
||||||
jint err;
|
|
||||||
jbyte* dataPtr;
|
|
||||||
ENetEvent event;
|
|
||||||
|
|
||||||
// Wait for a receive event, timeout, or disconnect
|
|
||||||
err = enet_host_service(LONG_TO_CLIENT(client), &event, timeout);
|
|
||||||
if (err <= 0) {
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
else if (event.type != ENET_EVENT_TYPE_RECEIVE) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the packet isn't too large
|
|
||||||
if (event.packet->dataLength > length) {
|
|
||||||
enet_packet_destroy(event.packet);
|
|
||||||
return event.packet->dataLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the packet data into the caller's buffer
|
|
||||||
dataPtr = (*env)->GetByteArrayElements(env, data, 0);
|
|
||||||
memcpy(dataPtr, event.packet->data, event.packet->dataLength);
|
|
||||||
err = event.packet->dataLength;
|
|
||||||
(*env)->ReleaseByteArrayElements(env, data, dataPtr, 0);
|
|
||||||
|
|
||||||
// Free the packet
|
|
||||||
enet_packet_destroy(event.packet);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_writePacket(JNIEnv *env, jobject class, jlong client, jlong peer, jbyteArray data, jint length, jint packetFlags) {
|
|
||||||
ENetPacket* packet;
|
|
||||||
jboolean ret;
|
|
||||||
jbyte* dataPtr;
|
|
||||||
|
|
||||||
dataPtr = (*env)->GetByteArrayElements(env, data, 0);
|
|
||||||
|
|
||||||
// Create the reliable packet that describes our outgoing message
|
|
||||||
packet = enet_packet_create(dataPtr, length, packetFlags);
|
|
||||||
if (packet != NULL) {
|
|
||||||
// Send the message to the peer
|
|
||||||
if (enet_peer_send(LONG_TO_PEER(peer), 0, packet) < 0) {
|
|
||||||
// This can fail if the peer has been disconnected
|
|
||||||
enet_packet_destroy(packet);
|
|
||||||
ret = JNI_FALSE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Force the client to send the packet now
|
|
||||||
enet_host_flush(LONG_TO_CLIENT(client));
|
|
||||||
ret = JNI_TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ret = JNI_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->ReleaseByteArrayElements(env, data, dataPtr, JNI_ABORT);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_destroyClient(JNIEnv *env, jobject class, jlong client) {
|
|
||||||
enet_host_destroy(LONG_TO_CLIENT(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_com_limelight_nvstream_enet_EnetConnection_disconnectPeer(JNIEnv *env, jobject class, jlong peer) {
|
|
||||||
enet_peer_disconnect_now(LONG_TO_PEER(peer), 0);
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
# Android.mk for Limelight's Opus decoder
|
|
||||||
MY_LOCAL_PATH := $(call my-dir)
|
|
||||||
|
|
||||||
include $(call all-subdir-makefiles)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(MY_LOCAL_PATH)
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := nv_opus_dec
|
|
||||||
LOCAL_SRC_FILES := nv_opus_dec.c nv_opus_dec_jni.c
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/libopus/inc
|
|
||||||
|
|
||||||
# Link to libopus library
|
|
||||||
LOCAL_STATIC_LIBRARIES := libopus
|
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
@ -1,7 +0,0 @@
|
|||||||
LOCAL_PATH:= $(call my-dir)
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE:= libopus
|
|
||||||
LOCAL_SRC_FILES:= $(TARGET_ARCH_ABI)/libopus.a
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/inc
|
|
||||||
include $(PREBUILT_STATIC_LIBRARY)
|
|
Binary file not shown.
Binary file not shown.
@ -1,100 +0,0 @@
|
|||||||
ANDROID_API_TARGET=21
|
|
||||||
PARALLEL_JOBS=$(nproc)
|
|
||||||
|
|
||||||
rm -r ./android
|
|
||||||
mkdir android
|
|
||||||
|
|
||||||
function build_one
|
|
||||||
{
|
|
||||||
PREFIX=$(pwd)/android/$CPU
|
|
||||||
SYSROOT=$NDK/platforms/android-$ANDROID_API_TARGET/arch-$SYSROOT_CPU
|
|
||||||
TOOLCHAIN_PATH=$NDK/toolchains/$TOOLCHAIN_DIR/prebuilt/linux-x86_64
|
|
||||||
export PATH=$PATH:$TOOLCHAIN_PATH/bin
|
|
||||||
./configure \
|
|
||||||
--build=x86_64-unknown-linux-gnu \
|
|
||||||
--host=$TOOLCHAIN_BIN_PREFIX \
|
|
||||||
--target=$TOOLCHAIN_BIN_PREFIX \
|
|
||||||
CFLAGS="--sysroot=$SYSROOT -O2 $ADDI_CFLAGS" \
|
|
||||||
$ADDI_CONFIGURE_FLAGS
|
|
||||||
make clean
|
|
||||||
make -j$PARALLEL_JOBS
|
|
||||||
mkdir android/$CPU
|
|
||||||
cp .libs/libopus.a android/$CPU
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_mips
|
|
||||||
{
|
|
||||||
CPU=mips
|
|
||||||
SYSROOT_CPU=mips
|
|
||||||
TOOLCHAIN_BIN_PREFIX=mipsel-linux-android
|
|
||||||
TOOLCHAIN_DIR=mipsel-linux-android-4.9
|
|
||||||
ADDI_CFLAGS="-mips32 -mhard-float -EL -mno-dsp"
|
|
||||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point" # fixed point
|
|
||||||
build_one
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_mips64
|
|
||||||
{
|
|
||||||
CPU=mips64
|
|
||||||
SYSROOT_CPU=mips64
|
|
||||||
TOOLCHAIN_BIN_PREFIX=mips64el-linux-android
|
|
||||||
TOOLCHAIN_DIR=mips64el-linux-android-4.9
|
|
||||||
ADDI_CFLAGS="-mips64r6"
|
|
||||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point" # fixed point
|
|
||||||
build_one
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_x86
|
|
||||||
{
|
|
||||||
CPU=x86
|
|
||||||
SYSROOT_CPU=x86
|
|
||||||
TOOLCHAIN_BIN_PREFIX=i686-linux-android
|
|
||||||
TOOLCHAIN_DIR=x86-4.9
|
|
||||||
ADDI_CFLAGS="-march=i686 -mtune=atom -mstackrealign -msse -msse2 -msse3 -mssse3 -mfpmath=sse -m32"
|
|
||||||
ADDI_CONFIGURE_FLAGS="" # floating point for SSE optimizations
|
|
||||||
build_one
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_x86_64
|
|
||||||
{
|
|
||||||
CPU=x86_64
|
|
||||||
SYSROOT_CPU=x86_64
|
|
||||||
TOOLCHAIN_BIN_PREFIX=x86_64-linux-android
|
|
||||||
TOOLCHAIN_DIR=x86_64-4.9
|
|
||||||
ADDI_CFLAGS="-msse -msse2 -msse3 -mssse3 -msse4 -msse4.1 -msse4.2 -mpopcnt -m64"
|
|
||||||
ADDI_CONFIGURE_FLAGS="" # floating point for SSE optimizations
|
|
||||||
build_one
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_armv7
|
|
||||||
{
|
|
||||||
CPU=arm
|
|
||||||
SYSROOT_CPU=arm
|
|
||||||
TOOLCHAIN_BIN_PREFIX=arm-linux-androideabi
|
|
||||||
TOOLCHAIN_DIR=arm-linux-androideabi-4.9
|
|
||||||
ADDI_CFLAGS="-marm -mfpu=vfpv3-d16"
|
|
||||||
ADDI_LDFLAGS=""
|
|
||||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point" # fixed point for NEON, EDSP, Media
|
|
||||||
build_one
|
|
||||||
}
|
|
||||||
|
|
||||||
# ARMv8 doesn't currently have assembly in the opus project. We still use fixed point
|
|
||||||
# anyway in the hopes that it will be more performant even without assembly.
|
|
||||||
function build_armv8
|
|
||||||
{
|
|
||||||
CPU=aarch64
|
|
||||||
SYSROOT_CPU=arm64
|
|
||||||
TOOLCHAIN_BIN_PREFIX=aarch64-linux-android
|
|
||||||
TOOLCHAIN_DIR=aarch64-linux-android-4.9
|
|
||||||
ADDI_CFLAGS=""
|
|
||||||
ADDI_LDFLAGS=""
|
|
||||||
ADDI_CONFIGURE_FLAGS="--enable-fixed-point"
|
|
||||||
build_one
|
|
||||||
}
|
|
||||||
|
|
||||||
build_mips
|
|
||||||
build_mips64
|
|
||||||
build_x86
|
|
||||||
build_x86_64
|
|
||||||
build_armv7
|
|
||||||
build_armv8
|
|
@ -1,978 +0,0 @@
|
|||||||
/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
|
|
||||||
Written by Jean-Marc Valin and Koen Vos */
|
|
||||||
/*
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
- Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
- Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file opus.h
|
|
||||||
* @brief Opus reference implementation API
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPUS_H
|
|
||||||
#define OPUS_H
|
|
||||||
|
|
||||||
#include "opus_types.h"
|
|
||||||
#include "opus_defines.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mainpage Opus
|
|
||||||
*
|
|
||||||
* The Opus codec is designed for interactive speech and audio transmission over the Internet.
|
|
||||||
* It is designed by the IETF Codec Working Group and incorporates technology from
|
|
||||||
* Skype's SILK codec and Xiph.Org's CELT codec.
|
|
||||||
*
|
|
||||||
* The Opus codec is designed to handle a wide range of interactive audio applications,
|
|
||||||
* including Voice over IP, videoconferencing, in-game chat, and even remote live music
|
|
||||||
* performances. It can scale from low bit-rate narrowband speech to very high quality
|
|
||||||
* stereo music. Its main features are:
|
|
||||||
|
|
||||||
* @li Sampling rates from 8 to 48 kHz
|
|
||||||
* @li Bit-rates from 6 kb/s to 510 kb/s
|
|
||||||
* @li Support for both constant bit-rate (CBR) and variable bit-rate (VBR)
|
|
||||||
* @li Audio bandwidth from narrowband to full-band
|
|
||||||
* @li Support for speech and music
|
|
||||||
* @li Support for mono and stereo
|
|
||||||
* @li Support for multichannel (up to 255 channels)
|
|
||||||
* @li Frame sizes from 2.5 ms to 60 ms
|
|
||||||
* @li Good loss robustness and packet loss concealment (PLC)
|
|
||||||
* @li Floating point and fixed-point implementation
|
|
||||||
*
|
|
||||||
* Documentation sections:
|
|
||||||
* @li @ref opus_encoder
|
|
||||||
* @li @ref opus_decoder
|
|
||||||
* @li @ref opus_repacketizer
|
|
||||||
* @li @ref opus_multistream
|
|
||||||
* @li @ref opus_libinfo
|
|
||||||
* @li @ref opus_custom
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @defgroup opus_encoder Opus Encoder
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* @brief This page describes the process and functions used to encode Opus.
|
|
||||||
*
|
|
||||||
* Since Opus is a stateful codec, the encoding process starts with creating an encoder
|
|
||||||
* state. This can be done with:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* int error;
|
|
||||||
* OpusEncoder *enc;
|
|
||||||
* enc = opus_encoder_create(Fs, channels, application, &error);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* From this point, @c enc can be used for encoding an audio stream. An encoder state
|
|
||||||
* @b must @b not be used for more than one stream at the same time. Similarly, the encoder
|
|
||||||
* state @b must @b not be re-initialized for each frame.
|
|
||||||
*
|
|
||||||
* While opus_encoder_create() allocates memory for the state, it's also possible
|
|
||||||
* to initialize pre-allocated memory:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* int size;
|
|
||||||
* int error;
|
|
||||||
* OpusEncoder *enc;
|
|
||||||
* size = opus_encoder_get_size(channels);
|
|
||||||
* enc = malloc(size);
|
|
||||||
* error = opus_encoder_init(enc, Fs, channels, application);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* where opus_encoder_get_size() returns the required size for the encoder state. Note that
|
|
||||||
* future versions of this code may change the size, so no assuptions should be made about it.
|
|
||||||
*
|
|
||||||
* The encoder state is always continuous in memory and only a shallow copy is sufficient
|
|
||||||
* to copy it (e.g. memcpy())
|
|
||||||
*
|
|
||||||
* It is possible to change some of the encoder's settings using the opus_encoder_ctl()
|
|
||||||
* interface. All these settings already default to the recommended value, so they should
|
|
||||||
* only be changed when necessary. The most common settings one may want to change are:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate));
|
|
||||||
* opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
|
|
||||||
* opus_encoder_ctl(enc, OPUS_SET_SIGNAL(signal_type));
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* where
|
|
||||||
*
|
|
||||||
* @arg bitrate is in bits per second (b/s)
|
|
||||||
* @arg complexity is a value from 1 to 10, where 1 is the lowest complexity and 10 is the highest
|
|
||||||
* @arg signal_type is either OPUS_AUTO (default), OPUS_SIGNAL_VOICE, or OPUS_SIGNAL_MUSIC
|
|
||||||
*
|
|
||||||
* See @ref opus_encoderctls and @ref opus_genericctls for a complete list of parameters that can be set or queried. Most parameters can be set or changed at any time during a stream.
|
|
||||||
*
|
|
||||||
* To encode a frame, opus_encode() or opus_encode_float() must be called with exactly one frame (2.5, 5, 10, 20, 40 or 60 ms) of audio data:
|
|
||||||
* @code
|
|
||||||
* len = opus_encode(enc, audio_frame, frame_size, packet, max_packet);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* where
|
|
||||||
* <ul>
|
|
||||||
* <li>audio_frame is the audio data in opus_int16 (or float for opus_encode_float())</li>
|
|
||||||
* <li>frame_size is the duration of the frame in samples (per channel)</li>
|
|
||||||
* <li>packet is the byte array to which the compressed data is written</li>
|
|
||||||
* <li>max_packet is the maximum number of bytes that can be written in the packet (4000 bytes is recommended).
|
|
||||||
* Do not use max_packet to control VBR target bitrate, instead use the #OPUS_SET_BITRATE CTL.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* opus_encode() and opus_encode_float() return the number of bytes actually written to the packet.
|
|
||||||
* The return value <b>can be negative</b>, which indicates that an error has occurred. If the return value
|
|
||||||
* is 1 byte, then the packet does not need to be transmitted (DTX).
|
|
||||||
*
|
|
||||||
* Once the encoder state if no longer needed, it can be destroyed with
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* opus_encoder_destroy(enc);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* If the encoder was created with opus_encoder_init() rather than opus_encoder_create(),
|
|
||||||
* then no action is required aside from potentially freeing the memory that was manually
|
|
||||||
* allocated for it (calling free(enc) for the example above)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Opus encoder state.
|
|
||||||
* This contains the complete state of an Opus encoder.
|
|
||||||
* It is position independent and can be freely copied.
|
|
||||||
* @see opus_encoder_create,opus_encoder_init
|
|
||||||
*/
|
|
||||||
typedef struct OpusEncoder OpusEncoder;
|
|
||||||
|
|
||||||
/** Gets the size of an <code>OpusEncoder</code> structure.
|
|
||||||
* @param[in] channels <tt>int</tt>: Number of channels.
|
|
||||||
* This must be 1 or 2.
|
|
||||||
* @returns The size in bytes.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_encoder_get_size(int channels);
|
|
||||||
|
|
||||||
/**
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Allocates and initializes an encoder state.
|
|
||||||
* There are three coding modes:
|
|
||||||
*
|
|
||||||
* @ref OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice
|
|
||||||
* signals. It enhances the input signal by high-pass filtering and
|
|
||||||
* emphasizing formants and harmonics. Optionally it includes in-band
|
|
||||||
* forward error correction to protect against packet loss. Use this
|
|
||||||
* mode for typical VoIP applications. Because of the enhancement,
|
|
||||||
* even at high bitrates the output may sound different from the input.
|
|
||||||
*
|
|
||||||
* @ref OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most
|
|
||||||
* non-voice signals like music. Use this mode for music and mixed
|
|
||||||
* (music/voice) content, broadcast, and applications requiring less
|
|
||||||
* than 15 ms of coding delay.
|
|
||||||
*
|
|
||||||
* @ref OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that
|
|
||||||
* disables the speech-optimized mode in exchange for slightly reduced delay.
|
|
||||||
* This mode can only be set on an newly initialized or freshly reset encoder
|
|
||||||
* because it changes the codec delay.
|
|
||||||
*
|
|
||||||
* This is useful when the caller knows that the speech-optimized modes will not be needed (use with caution).
|
|
||||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate of input signal (Hz)
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
|
||||||
* @param [in] application <tt>int</tt>: Coding mode (@ref OPUS_APPLICATION_VOIP/@ref OPUS_APPLICATION_AUDIO/@ref OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
|
||||||
* @param [out] error <tt>int*</tt>: @ref opus_errorcodes
|
|
||||||
* @note Regardless of the sampling rate and number channels selected, the Opus encoder
|
|
||||||
* can switch to a lower audio bandwidth or number of channels if the bitrate
|
|
||||||
* selected is too low. This also means that it is safe to always use 48 kHz stereo input
|
|
||||||
* and let the encoder optimize the encoding.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusEncoder *opus_encoder_create(
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int application,
|
|
||||||
int *error
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Initializes a previously allocated encoder state
|
|
||||||
* The memory pointed to by st must be at least the size returned by opus_encoder_get_size().
|
|
||||||
* This is intended for applications which use their own allocator instead of malloc.
|
|
||||||
* @see opus_encoder_create(),opus_encoder_get_size()
|
|
||||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
|
||||||
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
|
||||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate of input signal (Hz)
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) in input signal
|
|
||||||
* @param [in] application <tt>int</tt>: Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO/OPUS_APPLICATION_RESTRICTED_LOWDELAY)
|
|
||||||
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_encoder_init(
|
|
||||||
OpusEncoder *st,
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int application
|
|
||||||
) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Encodes an Opus frame.
|
|
||||||
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
|
||||||
* @param [in] pcm <tt>opus_int16*</tt>: Input signal (interleaved if 2 channels). length is frame_size*channels*sizeof(opus_int16)
|
|
||||||
* @param [in] frame_size <tt>int</tt>: Number of samples per channel in the
|
|
||||||
* input signal.
|
|
||||||
* This must be an Opus frame size for
|
|
||||||
* the encoder's sampling rate.
|
|
||||||
* For example, at 48 kHz the permitted
|
|
||||||
* values are 120, 240, 480, 960, 1920,
|
|
||||||
* and 2880.
|
|
||||||
* Passing in a duration of less than
|
|
||||||
* 10 ms (480 samples at 48 kHz) will
|
|
||||||
* prevent the encoder from using the LPC
|
|
||||||
* or hybrid modes.
|
|
||||||
* @param [out] data <tt>unsigned char*</tt>: Output payload.
|
|
||||||
* This must contain storage for at
|
|
||||||
* least \a max_data_bytes.
|
|
||||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
|
||||||
* memory for the output
|
|
||||||
* payload. This may be
|
|
||||||
* used to impose an upper limit on
|
|
||||||
* the instant bitrate, but should
|
|
||||||
* not be used as the only bitrate
|
|
||||||
* control. Use #OPUS_SET_BITRATE to
|
|
||||||
* control the bitrate.
|
|
||||||
* @returns The length of the encoded packet (in bytes) on success or a
|
|
||||||
* negative error code (see @ref opus_errorcodes) on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode(
|
|
||||||
OpusEncoder *st,
|
|
||||||
const opus_int16 *pcm,
|
|
||||||
int frame_size,
|
|
||||||
unsigned char *data,
|
|
||||||
opus_int32 max_data_bytes
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Encodes an Opus frame from floating point input.
|
|
||||||
* @param [in] st <tt>OpusEncoder*</tt>: Encoder state
|
|
||||||
* @param [in] pcm <tt>float*</tt>: Input in float format (interleaved if 2 channels), with a normal range of +/-1.0.
|
|
||||||
* Samples with a range beyond +/-1.0 are supported but will
|
|
||||||
* be clipped by decoders using the integer API and should
|
|
||||||
* only be used if it is known that the far end supports
|
|
||||||
* extended dynamic range.
|
|
||||||
* length is frame_size*channels*sizeof(float)
|
|
||||||
* @param [in] frame_size <tt>int</tt>: Number of samples per channel in the
|
|
||||||
* input signal.
|
|
||||||
* This must be an Opus frame size for
|
|
||||||
* the encoder's sampling rate.
|
|
||||||
* For example, at 48 kHz the permitted
|
|
||||||
* values are 120, 240, 480, 960, 1920,
|
|
||||||
* and 2880.
|
|
||||||
* Passing in a duration of less than
|
|
||||||
* 10 ms (480 samples at 48 kHz) will
|
|
||||||
* prevent the encoder from using the LPC
|
|
||||||
* or hybrid modes.
|
|
||||||
* @param [out] data <tt>unsigned char*</tt>: Output payload.
|
|
||||||
* This must contain storage for at
|
|
||||||
* least \a max_data_bytes.
|
|
||||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
|
||||||
* memory for the output
|
|
||||||
* payload. This may be
|
|
||||||
* used to impose an upper limit on
|
|
||||||
* the instant bitrate, but should
|
|
||||||
* not be used as the only bitrate
|
|
||||||
* control. Use #OPUS_SET_BITRATE to
|
|
||||||
* control the bitrate.
|
|
||||||
* @returns The length of the encoded packet (in bytes) on success or a
|
|
||||||
* negative error code (see @ref opus_errorcodes) on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_encode_float(
|
|
||||||
OpusEncoder *st,
|
|
||||||
const float *pcm,
|
|
||||||
int frame_size,
|
|
||||||
unsigned char *data,
|
|
||||||
opus_int32 max_data_bytes
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Frees an <code>OpusEncoder</code> allocated by opus_encoder_create().
|
|
||||||
* @param[in] st <tt>OpusEncoder*</tt>: State to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT void opus_encoder_destroy(OpusEncoder *st);
|
|
||||||
|
|
||||||
/** Perform a CTL function on an Opus encoder.
|
|
||||||
*
|
|
||||||
* Generally the request and subsequent arguments are generated
|
|
||||||
* by a convenience macro.
|
|
||||||
* @param st <tt>OpusEncoder*</tt>: Encoder state.
|
|
||||||
* @param request This and all remaining parameters should be replaced by one
|
|
||||||
* of the convenience macros in @ref opus_genericctls or
|
|
||||||
* @ref opus_encoderctls.
|
|
||||||
* @see opus_genericctls
|
|
||||||
* @see opus_encoderctls
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @defgroup opus_decoder Opus Decoder
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* @brief This page describes the process and functions used to decode Opus.
|
|
||||||
*
|
|
||||||
* The decoding process also starts with creating a decoder
|
|
||||||
* state. This can be done with:
|
|
||||||
* @code
|
|
||||||
* int error;
|
|
||||||
* OpusDecoder *dec;
|
|
||||||
* dec = opus_decoder_create(Fs, channels, &error);
|
|
||||||
* @endcode
|
|
||||||
* where
|
|
||||||
* @li Fs is the sampling rate and must be 8000, 12000, 16000, 24000, or 48000
|
|
||||||
* @li channels is the number of channels (1 or 2)
|
|
||||||
* @li error will hold the error code in case of failure (or #OPUS_OK on success)
|
|
||||||
* @li the return value is a newly created decoder state to be used for decoding
|
|
||||||
*
|
|
||||||
* While opus_decoder_create() allocates memory for the state, it's also possible
|
|
||||||
* to initialize pre-allocated memory:
|
|
||||||
* @code
|
|
||||||
* int size;
|
|
||||||
* int error;
|
|
||||||
* OpusDecoder *dec;
|
|
||||||
* size = opus_decoder_get_size(channels);
|
|
||||||
* dec = malloc(size);
|
|
||||||
* error = opus_decoder_init(dec, Fs, channels);
|
|
||||||
* @endcode
|
|
||||||
* where opus_decoder_get_size() returns the required size for the decoder state. Note that
|
|
||||||
* future versions of this code may change the size, so no assuptions should be made about it.
|
|
||||||
*
|
|
||||||
* The decoder state is always continuous in memory and only a shallow copy is sufficient
|
|
||||||
* to copy it (e.g. memcpy())
|
|
||||||
*
|
|
||||||
* To decode a frame, opus_decode() or opus_decode_float() must be called with a packet of compressed audio data:
|
|
||||||
* @code
|
|
||||||
* frame_size = opus_decode(dec, packet, len, decoded, max_size, 0);
|
|
||||||
* @endcode
|
|
||||||
* where
|
|
||||||
*
|
|
||||||
* @li packet is the byte array containing the compressed data
|
|
||||||
* @li len is the exact number of bytes contained in the packet
|
|
||||||
* @li decoded is the decoded audio data in opus_int16 (or float for opus_decode_float())
|
|
||||||
* @li max_size is the max duration of the frame in samples (per channel) that can fit into the decoded_frame array
|
|
||||||
*
|
|
||||||
* opus_decode() and opus_decode_float() return the number of samples (per channel) decoded from the packet.
|
|
||||||
* If that value is negative, then an error has occurred. This can occur if the packet is corrupted or if the audio
|
|
||||||
* buffer is too small to hold the decoded audio.
|
|
||||||
*
|
|
||||||
* Opus is a stateful codec with overlapping blocks and as a result Opus
|
|
||||||
* packets are not coded independently of each other. Packets must be
|
|
||||||
* passed into the decoder serially and in the correct order for a correct
|
|
||||||
* decode. Lost packets can be replaced with loss concealment by calling
|
|
||||||
* the decoder with a null pointer and zero length for the missing packet.
|
|
||||||
*
|
|
||||||
* A single codec state may only be accessed from a single thread at
|
|
||||||
* a time and any required locking must be performed by the caller. Separate
|
|
||||||
* streams must be decoded with separate decoder states and can be decoded
|
|
||||||
* in parallel unless the library was compiled with NONTHREADSAFE_PSEUDOSTACK
|
|
||||||
* defined.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Opus decoder state.
|
|
||||||
* This contains the complete state of an Opus decoder.
|
|
||||||
* It is position independent and can be freely copied.
|
|
||||||
* @see opus_decoder_create,opus_decoder_init
|
|
||||||
*/
|
|
||||||
typedef struct OpusDecoder OpusDecoder;
|
|
||||||
|
|
||||||
/** Gets the size of an <code>OpusDecoder</code> structure.
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels.
|
|
||||||
* This must be 1 or 2.
|
|
||||||
* @returns The size in bytes.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_size(int channels);
|
|
||||||
|
|
||||||
/** Allocates and initializes a decoder state.
|
|
||||||
* @param [in] Fs <tt>opus_int32</tt>: Sample rate to decode at (Hz).
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
|
||||||
* @param [out] error <tt>int*</tt>: #OPUS_OK Success or @ref opus_errorcodes
|
|
||||||
*
|
|
||||||
* Internally Opus stores data at 48000 Hz, so that should be the default
|
|
||||||
* value for Fs. However, the decoder can efficiently decode to buffers
|
|
||||||
* at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use
|
|
||||||
* data at the full sample rate, or knows the compressed data doesn't
|
|
||||||
* use the full frequency range, it can request decoding at a reduced
|
|
||||||
* rate. Likewise, the decoder is capable of filling in either mono or
|
|
||||||
* interleaved stereo pcm buffers, at the caller's request.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusDecoder *opus_decoder_create(
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int *error
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Initializes a previously allocated decoder state.
|
|
||||||
* The state must be at least the size returned by opus_decoder_get_size().
|
|
||||||
* This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size
|
|
||||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
|
||||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state.
|
|
||||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate to decode to (Hz).
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels (1 or 2) to decode
|
|
||||||
* @retval #OPUS_OK Success or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_decoder_init(
|
|
||||||
OpusDecoder *st,
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels
|
|
||||||
) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Decode an Opus packet.
|
|
||||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
|
||||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
|
||||||
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload*
|
|
||||||
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
|
||||||
* is frame_size*channels*sizeof(opus_int16)
|
|
||||||
* @param [in] frame_size Number of samples per channel of available space in \a pcm.
|
|
||||||
* If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
|
|
||||||
* not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
|
|
||||||
* then frame_size needs to be exactly the duration of audio that is missing, otherwise the
|
|
||||||
* decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
|
|
||||||
* FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
|
|
||||||
* @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
|
|
||||||
* decoded. If no such data is available, the frame is decoded as if it were lost.
|
|
||||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode(
|
|
||||||
OpusDecoder *st,
|
|
||||||
const unsigned char *data,
|
|
||||||
opus_int32 len,
|
|
||||||
opus_int16 *pcm,
|
|
||||||
int frame_size,
|
|
||||||
int decode_fec
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Decode an Opus packet with floating point output.
|
|
||||||
* @param [in] st <tt>OpusDecoder*</tt>: Decoder state
|
|
||||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
|
||||||
* @param [in] len <tt>opus_int32</tt>: Number of bytes in payload
|
|
||||||
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
|
||||||
* is frame_size*channels*sizeof(float)
|
|
||||||
* @param [in] frame_size Number of samples per channel of available space in \a pcm.
|
|
||||||
* If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
|
|
||||||
* not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
|
|
||||||
* then frame_size needs to be exactly the duration of audio that is missing, otherwise the
|
|
||||||
* decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
|
|
||||||
* FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
|
|
||||||
* @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
|
|
||||||
* decoded. If no such data is available the frame is decoded as if it were lost.
|
|
||||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode_float(
|
|
||||||
OpusDecoder *st,
|
|
||||||
const unsigned char *data,
|
|
||||||
opus_int32 len,
|
|
||||||
float *pcm,
|
|
||||||
int frame_size,
|
|
||||||
int decode_fec
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Perform a CTL function on an Opus decoder.
|
|
||||||
*
|
|
||||||
* Generally the request and subsequent arguments are generated
|
|
||||||
* by a convenience macro.
|
|
||||||
* @param st <tt>OpusDecoder*</tt>: Decoder state.
|
|
||||||
* @param request This and all remaining parameters should be replaced by one
|
|
||||||
* of the convenience macros in @ref opus_genericctls or
|
|
||||||
* @ref opus_decoderctls.
|
|
||||||
* @see opus_genericctls
|
|
||||||
* @see opus_decoderctls
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_decoder_ctl(OpusDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Frees an <code>OpusDecoder</code> allocated by opus_decoder_create().
|
|
||||||
* @param[in] st <tt>OpusDecoder*</tt>: State to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT void opus_decoder_destroy(OpusDecoder *st);
|
|
||||||
|
|
||||||
/** Parse an opus packet into one or more frames.
|
|
||||||
* Opus_decode will perform this operation internally so most applications do
|
|
||||||
* not need to use this function.
|
|
||||||
* This function does not copy the frames, the returned pointers are pointers into
|
|
||||||
* the input packet.
|
|
||||||
* @param [in] data <tt>char*</tt>: Opus packet to be parsed
|
|
||||||
* @param [in] len <tt>opus_int32</tt>: size of data
|
|
||||||
* @param [out] out_toc <tt>char*</tt>: TOC pointer
|
|
||||||
* @param [out] frames <tt>char*[48]</tt> encapsulated frames
|
|
||||||
* @param [out] size <tt>opus_int16[48]</tt> sizes of the encapsulated frames
|
|
||||||
* @param [out] payload_offset <tt>int*</tt>: returns the position of the payload within the packet (in bytes)
|
|
||||||
* @returns number of frames
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_packet_parse(
|
|
||||||
const unsigned char *data,
|
|
||||||
opus_int32 len,
|
|
||||||
unsigned char *out_toc,
|
|
||||||
const unsigned char *frames[48],
|
|
||||||
opus_int16 size[48],
|
|
||||||
int *payload_offset
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Gets the bandwidth of an Opus packet.
|
|
||||||
* @param [in] data <tt>char*</tt>: Opus packet
|
|
||||||
* @retval OPUS_BANDWIDTH_NARROWBAND Narrowband (4kHz bandpass)
|
|
||||||
* @retval OPUS_BANDWIDTH_MEDIUMBAND Mediumband (6kHz bandpass)
|
|
||||||
* @retval OPUS_BANDWIDTH_WIDEBAND Wideband (8kHz bandpass)
|
|
||||||
* @retval OPUS_BANDWIDTH_SUPERWIDEBAND Superwideband (12kHz bandpass)
|
|
||||||
* @retval OPUS_BANDWIDTH_FULLBAND Fullband (20kHz bandpass)
|
|
||||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_bandwidth(const unsigned char *data) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Gets the number of samples per frame from an Opus packet.
|
|
||||||
* @param [in] data <tt>char*</tt>: Opus packet.
|
|
||||||
* This must contain at least one byte of
|
|
||||||
* data.
|
|
||||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
|
|
||||||
* This must be a multiple of 400, or
|
|
||||||
* inaccurate results will be returned.
|
|
||||||
* @returns Number of samples per frame.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_samples_per_frame(const unsigned char *data, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Gets the number of channels from an Opus packet.
|
|
||||||
* @param [in] data <tt>char*</tt>: Opus packet
|
|
||||||
* @returns Number of channels
|
|
||||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_channels(const unsigned char *data) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Gets the number of frames in an Opus packet.
|
|
||||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
|
||||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
|
||||||
* @returns Number of frames
|
|
||||||
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
|
||||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Gets the number of samples of an Opus packet.
|
|
||||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
|
||||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
|
||||||
* @param [in] Fs <tt>opus_int32</tt>: Sampling rate in Hz.
|
|
||||||
* This must be a multiple of 400, or
|
|
||||||
* inaccurate results will be returned.
|
|
||||||
* @returns Number of samples
|
|
||||||
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
|
||||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len, opus_int32 Fs) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Gets the number of samples of an Opus packet.
|
|
||||||
* @param [in] dec <tt>OpusDecoder*</tt>: Decoder state
|
|
||||||
* @param [in] packet <tt>char*</tt>: Opus packet
|
|
||||||
* @param [in] len <tt>opus_int32</tt>: Length of packet
|
|
||||||
* @returns Number of samples
|
|
||||||
* @retval OPUS_BAD_ARG Insufficient data was passed to the function
|
|
||||||
* @retval OPUS_INVALID_PACKET The compressed data passed is corrupted or of an unsupported type
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decoder_get_nb_samples(const OpusDecoder *dec, const unsigned char packet[], opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
|
||||||
|
|
||||||
/** Applies soft-clipping to bring a float signal within the [-1,1] range. If
|
|
||||||
* the signal is already in that range, nothing is done. If there are values
|
|
||||||
* outside of [-1,1], then the signal is clipped as smoothly as possible to
|
|
||||||
* both fit in the range and avoid creating excessive distortion in the
|
|
||||||
* process.
|
|
||||||
* @param [in,out] pcm <tt>float*</tt>: Input PCM and modified PCM
|
|
||||||
* @param [in] frame_size <tt>int</tt> Number of samples per channel to process
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @param [in,out] softclip_mem <tt>float*</tt>: State memory for the soft clipping process (one float per channel, initialized to zero)
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, float *softclip_mem);
|
|
||||||
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @defgroup opus_repacketizer Repacketizer
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* The repacketizer can be used to merge multiple Opus packets into a single
|
|
||||||
* packet or alternatively to split Opus packets that have previously been
|
|
||||||
* merged. Splitting valid Opus packets is always guaranteed to succeed,
|
|
||||||
* whereas merging valid packets only succeeds if all frames have the same
|
|
||||||
* mode, bandwidth, and frame size, and when the total duration of the merged
|
|
||||||
* packet is no more than 120 ms.
|
|
||||||
* The repacketizer currently only operates on elementary Opus
|
|
||||||
* streams. It will not manipualte multistream packets successfully, except in
|
|
||||||
* the degenerate case where they consist of data from a single stream.
|
|
||||||
*
|
|
||||||
* The repacketizing process starts with creating a repacketizer state, either
|
|
||||||
* by calling opus_repacketizer_create() or by allocating the memory yourself,
|
|
||||||
* e.g.,
|
|
||||||
* @code
|
|
||||||
* OpusRepacketizer *rp;
|
|
||||||
* rp = (OpusRepacketizer*)malloc(opus_repacketizer_get_size());
|
|
||||||
* if (rp != NULL)
|
|
||||||
* opus_repacketizer_init(rp);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* Then the application should submit packets with opus_repacketizer_cat(),
|
|
||||||
* extract new packets with opus_repacketizer_out() or
|
|
||||||
* opus_repacketizer_out_range(), and then reset the state for the next set of
|
|
||||||
* input packets via opus_repacketizer_init().
|
|
||||||
*
|
|
||||||
* For example, to split a sequence of packets into individual frames:
|
|
||||||
* @code
|
|
||||||
* unsigned char *data;
|
|
||||||
* int len;
|
|
||||||
* while (get_next_packet(&data, &len))
|
|
||||||
* {
|
|
||||||
* unsigned char out[1276];
|
|
||||||
* opus_int32 out_len;
|
|
||||||
* int nb_frames;
|
|
||||||
* int err;
|
|
||||||
* int i;
|
|
||||||
* err = opus_repacketizer_cat(rp, data, len);
|
|
||||||
* if (err != OPUS_OK)
|
|
||||||
* {
|
|
||||||
* release_packet(data);
|
|
||||||
* return err;
|
|
||||||
* }
|
|
||||||
* nb_frames = opus_repacketizer_get_nb_frames(rp);
|
|
||||||
* for (i = 0; i < nb_frames; i++)
|
|
||||||
* {
|
|
||||||
* out_len = opus_repacketizer_out_range(rp, i, i+1, out, sizeof(out));
|
|
||||||
* if (out_len < 0)
|
|
||||||
* {
|
|
||||||
* release_packet(data);
|
|
||||||
* return (int)out_len;
|
|
||||||
* }
|
|
||||||
* output_next_packet(out, out_len);
|
|
||||||
* }
|
|
||||||
* opus_repacketizer_init(rp);
|
|
||||||
* release_packet(data);
|
|
||||||
* }
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* Alternatively, to combine a sequence of frames into packets that each
|
|
||||||
* contain up to <code>TARGET_DURATION_MS</code> milliseconds of data:
|
|
||||||
* @code
|
|
||||||
* // The maximum number of packets with duration TARGET_DURATION_MS occurs
|
|
||||||
* // when the frame size is 2.5 ms, for a total of (TARGET_DURATION_MS*2/5)
|
|
||||||
* // packets.
|
|
||||||
* unsigned char *data[(TARGET_DURATION_MS*2/5)+1];
|
|
||||||
* opus_int32 len[(TARGET_DURATION_MS*2/5)+1];
|
|
||||||
* int nb_packets;
|
|
||||||
* unsigned char out[1277*(TARGET_DURATION_MS*2/2)];
|
|
||||||
* opus_int32 out_len;
|
|
||||||
* int prev_toc;
|
|
||||||
* nb_packets = 0;
|
|
||||||
* while (get_next_packet(data+nb_packets, len+nb_packets))
|
|
||||||
* {
|
|
||||||
* int nb_frames;
|
|
||||||
* int err;
|
|
||||||
* nb_frames = opus_packet_get_nb_frames(data[nb_packets], len[nb_packets]);
|
|
||||||
* if (nb_frames < 1)
|
|
||||||
* {
|
|
||||||
* release_packets(data, nb_packets+1);
|
|
||||||
* return nb_frames;
|
|
||||||
* }
|
|
||||||
* nb_frames += opus_repacketizer_get_nb_frames(rp);
|
|
||||||
* // If adding the next packet would exceed our target, or it has an
|
|
||||||
* // incompatible TOC sequence, output the packets we already have before
|
|
||||||
* // submitting it.
|
|
||||||
* // N.B., The nb_packets > 0 check ensures we've submitted at least one
|
|
||||||
* // packet since the last call to opus_repacketizer_init(). Otherwise a
|
|
||||||
* // single packet longer than TARGET_DURATION_MS would cause us to try to
|
|
||||||
* // output an (invalid) empty packet. It also ensures that prev_toc has
|
|
||||||
* // been set to a valid value. Additionally, len[nb_packets] > 0 is
|
|
||||||
* // guaranteed by the call to opus_packet_get_nb_frames() above, so the
|
|
||||||
* // reference to data[nb_packets][0] should be valid.
|
|
||||||
* if (nb_packets > 0 && (
|
|
||||||
* ((prev_toc & 0xFC) != (data[nb_packets][0] & 0xFC)) ||
|
|
||||||
* opus_packet_get_samples_per_frame(data[nb_packets], 48000)*nb_frames >
|
|
||||||
* TARGET_DURATION_MS*48))
|
|
||||||
* {
|
|
||||||
* out_len = opus_repacketizer_out(rp, out, sizeof(out));
|
|
||||||
* if (out_len < 0)
|
|
||||||
* {
|
|
||||||
* release_packets(data, nb_packets+1);
|
|
||||||
* return (int)out_len;
|
|
||||||
* }
|
|
||||||
* output_next_packet(out, out_len);
|
|
||||||
* opus_repacketizer_init(rp);
|
|
||||||
* release_packets(data, nb_packets);
|
|
||||||
* data[0] = data[nb_packets];
|
|
||||||
* len[0] = len[nb_packets];
|
|
||||||
* nb_packets = 0;
|
|
||||||
* }
|
|
||||||
* err = opus_repacketizer_cat(rp, data[nb_packets], len[nb_packets]);
|
|
||||||
* if (err != OPUS_OK)
|
|
||||||
* {
|
|
||||||
* release_packets(data, nb_packets+1);
|
|
||||||
* return err;
|
|
||||||
* }
|
|
||||||
* prev_toc = data[nb_packets][0];
|
|
||||||
* nb_packets++;
|
|
||||||
* }
|
|
||||||
* // Output the final, partial packet.
|
|
||||||
* if (nb_packets > 0)
|
|
||||||
* {
|
|
||||||
* out_len = opus_repacketizer_out(rp, out, sizeof(out));
|
|
||||||
* release_packets(data, nb_packets);
|
|
||||||
* if (out_len < 0)
|
|
||||||
* return (int)out_len;
|
|
||||||
* output_next_packet(out, out_len);
|
|
||||||
* }
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* An alternate way of merging packets is to simply call opus_repacketizer_cat()
|
|
||||||
* unconditionally until it fails. At that point, the merged packet can be
|
|
||||||
* obtained with opus_repacketizer_out() and the input packet for which
|
|
||||||
* opus_repacketizer_cat() needs to be re-added to a newly reinitialized
|
|
||||||
* repacketizer state.
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef struct OpusRepacketizer OpusRepacketizer;
|
|
||||||
|
|
||||||
/** Gets the size of an <code>OpusRepacketizer</code> structure.
|
|
||||||
* @returns The size in bytes.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_size(void);
|
|
||||||
|
|
||||||
/** (Re)initializes a previously allocated repacketizer state.
|
|
||||||
* The state must be at least the size returned by opus_repacketizer_get_size().
|
|
||||||
* This can be used for applications which use their own allocator instead of
|
|
||||||
* malloc().
|
|
||||||
* It must also be called to reset the queue of packets waiting to be
|
|
||||||
* repacketized, which is necessary if the maximum packet duration of 120 ms
|
|
||||||
* is reached or if you wish to submit packets with a different Opus
|
|
||||||
* configuration (coding mode, audio bandwidth, frame size, or channel count).
|
|
||||||
* Failure to do so will prevent a new packet from being added with
|
|
||||||
* opus_repacketizer_cat().
|
|
||||||
* @see opus_repacketizer_create
|
|
||||||
* @see opus_repacketizer_get_size
|
|
||||||
* @see opus_repacketizer_cat
|
|
||||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to
|
|
||||||
* (re)initialize.
|
|
||||||
* @returns A pointer to the same repacketizer state that was passed in.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OpusRepacketizer *opus_repacketizer_init(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Allocates memory and initializes the new repacketizer with
|
|
||||||
* opus_repacketizer_init().
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusRepacketizer *opus_repacketizer_create(void);
|
|
||||||
|
|
||||||
/** Frees an <code>OpusRepacketizer</code> allocated by
|
|
||||||
* opus_repacketizer_create().
|
|
||||||
* @param[in] rp <tt>OpusRepacketizer*</tt>: State to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT void opus_repacketizer_destroy(OpusRepacketizer *rp);
|
|
||||||
|
|
||||||
/** Add a packet to the current repacketizer state.
|
|
||||||
* This packet must match the configuration of any packets already submitted
|
|
||||||
* for repacketization since the last call to opus_repacketizer_init().
|
|
||||||
* This means that it must have the same coding mode, audio bandwidth, frame
|
|
||||||
* size, and channel count.
|
|
||||||
* This can be checked in advance by examining the top 6 bits of the first
|
|
||||||
* byte of the packet, and ensuring they match the top 6 bits of the first
|
|
||||||
* byte of any previously submitted packet.
|
|
||||||
* The total duration of audio in the repacketizer state also must not exceed
|
|
||||||
* 120 ms, the maximum duration of a single packet, after adding this packet.
|
|
||||||
*
|
|
||||||
* The contents of the current repacketizer state can be extracted into new
|
|
||||||
* packets using opus_repacketizer_out() or opus_repacketizer_out_range().
|
|
||||||
*
|
|
||||||
* In order to add a packet with a different configuration or to add more
|
|
||||||
* audio beyond 120 ms, you must clear the repacketizer state by calling
|
|
||||||
* opus_repacketizer_init().
|
|
||||||
* If a packet is too large to add to the current repacketizer state, no part
|
|
||||||
* of it is added, even if it contains multiple frames, some of which might
|
|
||||||
* fit.
|
|
||||||
* If you wish to be able to add parts of such packets, you should first use
|
|
||||||
* another repacketizer to split the packet into pieces and add them
|
|
||||||
* individually.
|
|
||||||
* @see opus_repacketizer_out_range
|
|
||||||
* @see opus_repacketizer_out
|
|
||||||
* @see opus_repacketizer_init
|
|
||||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state to which to
|
|
||||||
* add the packet.
|
|
||||||
* @param[in] data <tt>const unsigned char*</tt>: The packet data.
|
|
||||||
* The application must ensure
|
|
||||||
* this pointer remains valid
|
|
||||||
* until the next call to
|
|
||||||
* opus_repacketizer_init() or
|
|
||||||
* opus_repacketizer_destroy().
|
|
||||||
* @param len <tt>opus_int32</tt>: The number of bytes in the packet data.
|
|
||||||
* @returns An error code indicating whether or not the operation succeeded.
|
|
||||||
* @retval #OPUS_OK The packet's contents have been added to the repacketizer
|
|
||||||
* state.
|
|
||||||
* @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence,
|
|
||||||
* the packet's TOC sequence was not compatible
|
|
||||||
* with previously submitted packets (because
|
|
||||||
* the coding mode, audio bandwidth, frame size,
|
|
||||||
* or channel count did not match), or adding
|
|
||||||
* this packet would increase the total amount of
|
|
||||||
* audio stored in the repacketizer state to more
|
|
||||||
* than 120 ms.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_repacketizer_cat(OpusRepacketizer *rp, const unsigned char *data, opus_int32 len) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
|
||||||
|
|
||||||
|
|
||||||
/** Construct a new packet from data previously submitted to the repacketizer
|
|
||||||
* state via opus_repacketizer_cat().
|
|
||||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
|
||||||
* construct the new packet.
|
|
||||||
* @param begin <tt>int</tt>: The index of the first frame in the current
|
|
||||||
* repacketizer state to include in the output.
|
|
||||||
* @param end <tt>int</tt>: One past the index of the last frame in the
|
|
||||||
* current repacketizer state to include in the
|
|
||||||
* output.
|
|
||||||
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
|
||||||
* store the output packet.
|
|
||||||
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
|
||||||
* the output buffer. In order to guarantee
|
|
||||||
* success, this should be at least
|
|
||||||
* <code>1276</code> for a single frame,
|
|
||||||
* or for multiple frames,
|
|
||||||
* <code>1277*(end-begin)</code>.
|
|
||||||
* However, <code>1*(end-begin)</code> plus
|
|
||||||
* the size of all packet data submitted to
|
|
||||||
* the repacketizer since the last call to
|
|
||||||
* opus_repacketizer_init() or
|
|
||||||
* opus_repacketizer_create() is also
|
|
||||||
* sufficient, and possibly much smaller.
|
|
||||||
* @returns The total size of the output packet on success, or an error code
|
|
||||||
* on failure.
|
|
||||||
* @retval #OPUS_BAD_ARG <code>[begin,end)</code> was an invalid range of
|
|
||||||
* frames (begin < 0, begin >= end, or end >
|
|
||||||
* opus_repacketizer_get_nb_frames()).
|
|
||||||
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
|
|
||||||
* complete output packet.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out_range(OpusRepacketizer *rp, int begin, int end, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Return the total number of frames contained in packet data submitted to
|
|
||||||
* the repacketizer state so far via opus_repacketizer_cat() since the last
|
|
||||||
* call to opus_repacketizer_init() or opus_repacketizer_create().
|
|
||||||
* This defines the valid range of packets that can be extracted with
|
|
||||||
* opus_repacketizer_out_range() or opus_repacketizer_out().
|
|
||||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state containing the
|
|
||||||
* frames.
|
|
||||||
* @returns The total number of frames contained in the packet data submitted
|
|
||||||
* to the repacketizer state.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_repacketizer_get_nb_frames(OpusRepacketizer *rp) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Construct a new packet from data previously submitted to the repacketizer
|
|
||||||
* state via opus_repacketizer_cat().
|
|
||||||
* This is a convenience routine that returns all the data submitted so far
|
|
||||||
* in a single packet.
|
|
||||||
* It is equivalent to calling
|
|
||||||
* @code
|
|
||||||
* opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp),
|
|
||||||
* data, maxlen)
|
|
||||||
* @endcode
|
|
||||||
* @param rp <tt>OpusRepacketizer*</tt>: The repacketizer state from which to
|
|
||||||
* construct the new packet.
|
|
||||||
* @param[out] data <tt>const unsigned char*</tt>: The buffer in which to
|
|
||||||
* store the output packet.
|
|
||||||
* @param maxlen <tt>opus_int32</tt>: The maximum number of bytes to store in
|
|
||||||
* the output buffer. In order to guarantee
|
|
||||||
* success, this should be at least
|
|
||||||
* <code>1277*opus_repacketizer_get_nb_frames(rp)</code>.
|
|
||||||
* However,
|
|
||||||
* <code>1*opus_repacketizer_get_nb_frames(rp)</code>
|
|
||||||
* plus the size of all packet data
|
|
||||||
* submitted to the repacketizer since the
|
|
||||||
* last call to opus_repacketizer_init() or
|
|
||||||
* opus_repacketizer_create() is also
|
|
||||||
* sufficient, and possibly much smaller.
|
|
||||||
* @returns The total size of the output packet on success, or an error code
|
|
||||||
* on failure.
|
|
||||||
* @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the
|
|
||||||
* complete output packet.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_repacketizer_out(OpusRepacketizer *rp, unsigned char *data, opus_int32 maxlen) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Pads a given Opus packet to a larger size (possibly changing the TOC sequence).
|
|
||||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
|
||||||
* packet to pad.
|
|
||||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
|
||||||
* This must be at least 1.
|
|
||||||
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
|
||||||
* This must be at least as large as len.
|
|
||||||
* @returns an error code
|
|
||||||
* @retval #OPUS_OK \a on success.
|
|
||||||
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
|
|
||||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len);
|
|
||||||
|
|
||||||
/** Remove all padding from a given Opus packet and rewrite the TOC sequence to
|
|
||||||
* minimize space usage.
|
|
||||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
|
||||||
* packet to strip.
|
|
||||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
|
||||||
* This must be at least 1.
|
|
||||||
* @returns The new size of the output packet on success, or an error code
|
|
||||||
* on failure.
|
|
||||||
* @retval #OPUS_BAD_ARG \a len was less than 1.
|
|
||||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_packet_unpad(unsigned char *data, opus_int32 len);
|
|
||||||
|
|
||||||
/** Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence).
|
|
||||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
|
||||||
* packet to pad.
|
|
||||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
|
||||||
* This must be at least 1.
|
|
||||||
* @param new_len <tt>opus_int32</tt>: The desired size of the packet after padding.
|
|
||||||
* This must be at least 1.
|
|
||||||
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
|
|
||||||
* This must be at least as large as len.
|
|
||||||
* @returns an error code
|
|
||||||
* @retval #OPUS_OK \a on success.
|
|
||||||
* @retval #OPUS_BAD_ARG \a len was less than 1.
|
|
||||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_multistream_packet_pad(unsigned char *data, opus_int32 len, opus_int32 new_len, int nb_streams);
|
|
||||||
|
|
||||||
/** Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to
|
|
||||||
* minimize space usage.
|
|
||||||
* @param[in,out] data <tt>const unsigned char*</tt>: The buffer containing the
|
|
||||||
* packet to strip.
|
|
||||||
* @param len <tt>opus_int32</tt>: The size of the packet.
|
|
||||||
* This must be at least 1.
|
|
||||||
* @param nb_streams <tt>opus_int32</tt>: The number of streams (not channels) in the packet.
|
|
||||||
* This must be at least 1.
|
|
||||||
* @returns The new size of the output packet on success, or an error code
|
|
||||||
* on failure.
|
|
||||||
* @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len.
|
|
||||||
* @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_packet_unpad(unsigned char *data, opus_int32 len, int nb_streams);
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* OPUS_H */
|
|
@ -1,342 +0,0 @@
|
|||||||
/* Copyright (c) 2007-2008 CSIRO
|
|
||||||
Copyright (c) 2007-2009 Xiph.Org Foundation
|
|
||||||
Copyright (c) 2008-2012 Gregory Maxwell
|
|
||||||
Written by Jean-Marc Valin and Gregory Maxwell */
|
|
||||||
/*
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
- Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
- Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
@file opus_custom.h
|
|
||||||
@brief Opus-Custom reference implementation API
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPUS_CUSTOM_H
|
|
||||||
#define OPUS_CUSTOM_H
|
|
||||||
|
|
||||||
#include "opus_defines.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CUSTOM_MODES
|
|
||||||
# define OPUS_CUSTOM_EXPORT OPUS_EXPORT
|
|
||||||
# define OPUS_CUSTOM_EXPORT_STATIC OPUS_EXPORT
|
|
||||||
#else
|
|
||||||
# define OPUS_CUSTOM_EXPORT
|
|
||||||
# ifdef OPUS_BUILD
|
|
||||||
# define OPUS_CUSTOM_EXPORT_STATIC static OPUS_INLINE
|
|
||||||
# else
|
|
||||||
# define OPUS_CUSTOM_EXPORT_STATIC
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** @defgroup opus_custom Opus Custom
|
|
||||||
* @{
|
|
||||||
* Opus Custom is an optional part of the Opus specification and
|
|
||||||
* reference implementation which uses a distinct API from the regular
|
|
||||||
* API and supports frame sizes that are not normally supported.\ Use
|
|
||||||
* of Opus Custom is discouraged for all but very special applications
|
|
||||||
* for which a frame size different from 2.5, 5, 10, or 20 ms is needed
|
|
||||||
* (for either complexity or latency reasons) and where interoperability
|
|
||||||
* is less important.
|
|
||||||
*
|
|
||||||
* In addition to the interoperability limitations the use of Opus custom
|
|
||||||
* disables a substantial chunk of the codec and generally lowers the
|
|
||||||
* quality available at a given bitrate. Normally when an application needs
|
|
||||||
* a different frame size from the codec it should buffer to match the
|
|
||||||
* sizes but this adds a small amount of delay which may be important
|
|
||||||
* in some very low latency applications. Some transports (especially
|
|
||||||
* constant rate RF transports) may also work best with frames of
|
|
||||||
* particular durations.
|
|
||||||
*
|
|
||||||
* Libopus only supports custom modes if they are enabled at compile time.
|
|
||||||
*
|
|
||||||
* The Opus Custom API is similar to the regular API but the
|
|
||||||
* @ref opus_encoder_create and @ref opus_decoder_create calls take
|
|
||||||
* an additional mode parameter which is a structure produced by
|
|
||||||
* a call to @ref opus_custom_mode_create. Both the encoder and decoder
|
|
||||||
* must create a mode using the same sample rate (fs) and frame size
|
|
||||||
* (frame size) so these parameters must either be signaled out of band
|
|
||||||
* or fixed in a particular implementation.
|
|
||||||
*
|
|
||||||
* Similar to regular Opus the custom modes support on the fly frame size
|
|
||||||
* switching, but the sizes available depend on the particular frame size in
|
|
||||||
* use. For some initial frame sizes on a single on the fly size is available.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Contains the state of an encoder. One encoder state is needed
|
|
||||||
for each stream. It is initialized once at the beginning of the
|
|
||||||
stream. Do *not* re-initialize the state for every frame.
|
|
||||||
@brief Encoder state
|
|
||||||
*/
|
|
||||||
typedef struct OpusCustomEncoder OpusCustomEncoder;
|
|
||||||
|
|
||||||
/** State of the decoder. One decoder state is needed for each stream.
|
|
||||||
It is initialized once at the beginning of the stream. Do *not*
|
|
||||||
re-initialize the state for every frame.
|
|
||||||
@brief Decoder state
|
|
||||||
*/
|
|
||||||
typedef struct OpusCustomDecoder OpusCustomDecoder;
|
|
||||||
|
|
||||||
/** The mode contains all the information necessary to create an
|
|
||||||
encoder. Both the encoder and decoder need to be initialized
|
|
||||||
with exactly the same mode, otherwise the output will be
|
|
||||||
corrupted.
|
|
||||||
@brief Mode configuration
|
|
||||||
*/
|
|
||||||
typedef struct OpusCustomMode OpusCustomMode;
|
|
||||||
|
|
||||||
/** Creates a new mode struct. This will be passed to an encoder or
|
|
||||||
* decoder. The mode MUST NOT BE DESTROYED until the encoders and
|
|
||||||
* decoders that use it are destroyed as well.
|
|
||||||
* @param [in] Fs <tt>int</tt>: Sampling rate (8000 to 96000 Hz)
|
|
||||||
* @param [in] frame_size <tt>int</tt>: Number of samples (per channel) to encode in each
|
|
||||||
* packet (64 - 1024, prime factorization must contain zero or more 2s, 3s, or 5s and no other primes)
|
|
||||||
* @param [out] error <tt>int*</tt>: Returned error code (if NULL, no error will be returned)
|
|
||||||
* @return A newly created mode
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomMode *opus_custom_mode_create(opus_int32 Fs, int frame_size, int *error);
|
|
||||||
|
|
||||||
/** Destroys a mode struct. Only call this after all encoders and
|
|
||||||
* decoders using this mode are destroyed as well.
|
|
||||||
* @param [in] mode <tt>OpusCustomMode*</tt>: Mode to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT void opus_custom_mode_destroy(OpusCustomMode *mode);
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(OPUS_BUILD) || defined(CELT_ENCODER_C)
|
|
||||||
|
|
||||||
/* Encoder */
|
|
||||||
/** Gets the size of an OpusCustomEncoder structure.
|
|
||||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Mode configuration
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @returns size
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_encoder_get_size(
|
|
||||||
const OpusCustomMode *mode,
|
|
||||||
int channels
|
|
||||||
) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
# ifdef CUSTOM_MODES
|
|
||||||
/** Initializes a previously allocated encoder state
|
|
||||||
* The memory pointed to by st must be the size returned by opus_custom_encoder_get_size.
|
|
||||||
* This is intended for applications which use their own allocator instead of malloc.
|
|
||||||
* @see opus_custom_encoder_create(),opus_custom_encoder_get_size()
|
|
||||||
* To reset a previously initialized state use the OPUS_RESET_STATE CTL.
|
|
||||||
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
|
||||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Contains all the information about the characteristics of
|
|
||||||
* the stream (must be the same characteristics as used for the
|
|
||||||
* decoder)
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @return OPUS_OK Success or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT int opus_custom_encoder_init(
|
|
||||||
OpusCustomEncoder *st,
|
|
||||||
const OpusCustomMode *mode,
|
|
||||||
int channels
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/** Creates a new encoder state. Each stream needs its own encoder
|
|
||||||
* state (can't be shared across simultaneous streams).
|
|
||||||
* @param [in] mode <tt>OpusCustomMode*</tt>: Contains all the information about the characteristics of
|
|
||||||
* the stream (must be the same characteristics as used for the
|
|
||||||
* decoder)
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @param [out] error <tt>int*</tt>: Returns an error code
|
|
||||||
* @return Newly created encoder state.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomEncoder *opus_custom_encoder_create(
|
|
||||||
const OpusCustomMode *mode,
|
|
||||||
int channels,
|
|
||||||
int *error
|
|
||||||
) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
|
|
||||||
/** Destroys a an encoder state.
|
|
||||||
* @param[in] st <tt>OpusCustomEncoder*</tt>: State to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT void opus_custom_encoder_destroy(OpusCustomEncoder *st);
|
|
||||||
|
|
||||||
/** Encodes a frame of audio.
|
|
||||||
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
|
||||||
* @param [in] pcm <tt>float*</tt>: PCM audio in float format, with a normal range of +/-1.0.
|
|
||||||
* Samples with a range beyond +/-1.0 are supported but will
|
|
||||||
* be clipped by decoders using the integer API and should
|
|
||||||
* only be used if it is known that the far end supports
|
|
||||||
* extended dynamic range. There must be exactly
|
|
||||||
* frame_size samples per channel.
|
|
||||||
* @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
|
|
||||||
* @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
|
|
||||||
* @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
|
|
||||||
* (can change from one frame to another)
|
|
||||||
* @return Number of bytes written to "compressed".
|
|
||||||
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
|
||||||
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
|
||||||
* decoding is possible.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode_float(
|
|
||||||
OpusCustomEncoder *st,
|
|
||||||
const float *pcm,
|
|
||||||
int frame_size,
|
|
||||||
unsigned char *compressed,
|
|
||||||
int maxCompressedBytes
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Encodes a frame of audio.
|
|
||||||
* @param [in] st <tt>OpusCustomEncoder*</tt>: Encoder state
|
|
||||||
* @param [in] pcm <tt>opus_int16*</tt>: PCM audio in signed 16-bit format (native endian).
|
|
||||||
* There must be exactly frame_size samples per channel.
|
|
||||||
* @param [in] frame_size <tt>int</tt>: Number of samples per frame of input signal
|
|
||||||
* @param [out] compressed <tt>char *</tt>: The compressed data is written here. This may not alias pcm and must be at least maxCompressedBytes long.
|
|
||||||
* @param [in] maxCompressedBytes <tt>int</tt>: Maximum number of bytes to use for compressing the frame
|
|
||||||
* (can change from one frame to another)
|
|
||||||
* @return Number of bytes written to "compressed".
|
|
||||||
* If negative, an error has occurred (see error codes). It is IMPORTANT that
|
|
||||||
* the length returned be somehow transmitted to the decoder. Otherwise, no
|
|
||||||
* decoding is possible.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_encode(
|
|
||||||
OpusCustomEncoder *st,
|
|
||||||
const opus_int16 *pcm,
|
|
||||||
int frame_size,
|
|
||||||
unsigned char *compressed,
|
|
||||||
int maxCompressedBytes
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Perform a CTL function on an Opus custom encoder.
|
|
||||||
*
|
|
||||||
* Generally the request and subsequent arguments are generated
|
|
||||||
* by a convenience macro.
|
|
||||||
* @see opus_encoderctls
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT int opus_custom_encoder_ctl(OpusCustomEncoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(OPUS_BUILD) || defined(CELT_DECODER_C)
|
|
||||||
/* Decoder */
|
|
||||||
|
|
||||||
/** Gets the size of an OpusCustomDecoder structure.
|
|
||||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Mode configuration
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @returns size
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT_STATIC OPUS_WARN_UNUSED_RESULT int opus_custom_decoder_get_size(
|
|
||||||
const OpusCustomMode *mode,
|
|
||||||
int channels
|
|
||||||
) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Initializes a previously allocated decoder state
|
|
||||||
* The memory pointed to by st must be the size returned by opus_custom_decoder_get_size.
|
|
||||||
* This is intended for applications which use their own allocator instead of malloc.
|
|
||||||
* @see opus_custom_decoder_create(),opus_custom_decoder_get_size()
|
|
||||||
* To reset a previously initialized state use the OPUS_RESET_STATE CTL.
|
|
||||||
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
|
||||||
* @param [in] mode <tt>OpusCustomMode *</tt>: Contains all the information about the characteristics of
|
|
||||||
* the stream (must be the same characteristics as used for the
|
|
||||||
* encoder)
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @return OPUS_OK Success or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT_STATIC int opus_custom_decoder_init(
|
|
||||||
OpusCustomDecoder *st,
|
|
||||||
const OpusCustomMode *mode,
|
|
||||||
int channels
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/** Creates a new decoder state. Each stream needs its own decoder state (can't
|
|
||||||
* be shared across simultaneous streams).
|
|
||||||
* @param [in] mode <tt>OpusCustomMode</tt>: Contains all the information about the characteristics of the
|
|
||||||
* stream (must be the same characteristics as used for the encoder)
|
|
||||||
* @param [in] channels <tt>int</tt>: Number of channels
|
|
||||||
* @param [out] error <tt>int*</tt>: Returns an error code
|
|
||||||
* @return Newly created decoder state.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT OpusCustomDecoder *opus_custom_decoder_create(
|
|
||||||
const OpusCustomMode *mode,
|
|
||||||
int channels,
|
|
||||||
int *error
|
|
||||||
) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Destroys a an decoder state.
|
|
||||||
* @param[in] st <tt>OpusCustomDecoder*</tt>: State to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT void opus_custom_decoder_destroy(OpusCustomDecoder *st);
|
|
||||||
|
|
||||||
/** Decode an opus custom frame with floating point output
|
|
||||||
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
|
||||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
|
||||||
* @param [in] len <tt>int</tt>: Number of bytes in payload
|
|
||||||
* @param [out] pcm <tt>float*</tt>: Output signal (interleaved if 2 channels). length
|
|
||||||
* is frame_size*channels*sizeof(float)
|
|
||||||
* @param [in] frame_size Number of samples per channel of available space in *pcm.
|
|
||||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode_float(
|
|
||||||
OpusCustomDecoder *st,
|
|
||||||
const unsigned char *data,
|
|
||||||
int len,
|
|
||||||
float *pcm,
|
|
||||||
int frame_size
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Decode an opus custom frame
|
|
||||||
* @param [in] st <tt>OpusCustomDecoder*</tt>: Decoder state
|
|
||||||
* @param [in] data <tt>char*</tt>: Input payload. Use a NULL pointer to indicate packet loss
|
|
||||||
* @param [in] len <tt>int</tt>: Number of bytes in payload
|
|
||||||
* @param [out] pcm <tt>opus_int16*</tt>: Output signal (interleaved if 2 channels). length
|
|
||||||
* is frame_size*channels*sizeof(opus_int16)
|
|
||||||
* @param [in] frame_size Number of samples per channel of available space in *pcm.
|
|
||||||
* @returns Number of decoded samples or @ref opus_errorcodes
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT OPUS_WARN_UNUSED_RESULT int opus_custom_decode(
|
|
||||||
OpusCustomDecoder *st,
|
|
||||||
const unsigned char *data,
|
|
||||||
int len,
|
|
||||||
opus_int16 *pcm,
|
|
||||||
int frame_size
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Perform a CTL function on an Opus custom decoder.
|
|
||||||
*
|
|
||||||
* Generally the request and subsequent arguments are generated
|
|
||||||
* by a convenience macro.
|
|
||||||
* @see opus_genericctls
|
|
||||||
*/
|
|
||||||
OPUS_CUSTOM_EXPORT int opus_custom_decoder_ctl(OpusCustomDecoder * OPUS_RESTRICT st, int request, ...) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* OPUS_CUSTOM_H */
|
|
@ -1,726 +0,0 @@
|
|||||||
/* Copyright (c) 2010-2011 Xiph.Org Foundation, Skype Limited
|
|
||||||
Written by Jean-Marc Valin and Koen Vos */
|
|
||||||
/*
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
- Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
- Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file opus_defines.h
|
|
||||||
* @brief Opus reference implementation constants
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPUS_DEFINES_H
|
|
||||||
#define OPUS_DEFINES_H
|
|
||||||
|
|
||||||
#include "opus_types.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** @defgroup opus_errorcodes Error codes
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
/** No error @hideinitializer*/
|
|
||||||
#define OPUS_OK 0
|
|
||||||
/** One or more invalid/out of range arguments @hideinitializer*/
|
|
||||||
#define OPUS_BAD_ARG -1
|
|
||||||
/** Not enough bytes allocated in the buffer @hideinitializer*/
|
|
||||||
#define OPUS_BUFFER_TOO_SMALL -2
|
|
||||||
/** An internal error was detected @hideinitializer*/
|
|
||||||
#define OPUS_INTERNAL_ERROR -3
|
|
||||||
/** The compressed data passed is corrupted @hideinitializer*/
|
|
||||||
#define OPUS_INVALID_PACKET -4
|
|
||||||
/** Invalid/unsupported request number @hideinitializer*/
|
|
||||||
#define OPUS_UNIMPLEMENTED -5
|
|
||||||
/** An encoder or decoder structure is invalid or already freed @hideinitializer*/
|
|
||||||
#define OPUS_INVALID_STATE -6
|
|
||||||
/** Memory allocation has failed @hideinitializer*/
|
|
||||||
#define OPUS_ALLOC_FAIL -7
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @cond OPUS_INTERNAL_DOC */
|
|
||||||
/**Export control for opus functions */
|
|
||||||
|
|
||||||
#ifndef OPUS_EXPORT
|
|
||||||
# if defined(WIN32)
|
|
||||||
# ifdef OPUS_BUILD
|
|
||||||
# define OPUS_EXPORT __declspec(dllexport)
|
|
||||||
# else
|
|
||||||
# define OPUS_EXPORT
|
|
||||||
# endif
|
|
||||||
# elif defined(__GNUC__) && defined(OPUS_BUILD)
|
|
||||||
# define OPUS_EXPORT __attribute__ ((visibility ("default")))
|
|
||||||
# else
|
|
||||||
# define OPUS_EXPORT
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
# if !defined(OPUS_GNUC_PREREQ)
|
|
||||||
# if defined(__GNUC__)&&defined(__GNUC_MINOR__)
|
|
||||||
# define OPUS_GNUC_PREREQ(_maj,_min) \
|
|
||||||
((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min))
|
|
||||||
# else
|
|
||||||
# define OPUS_GNUC_PREREQ(_maj,_min) 0
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
|
||||||
# if OPUS_GNUC_PREREQ(3,0)
|
|
||||||
# define OPUS_RESTRICT __restrict__
|
|
||||||
# elif (defined(_MSC_VER) && _MSC_VER >= 1400)
|
|
||||||
# define OPUS_RESTRICT __restrict
|
|
||||||
# else
|
|
||||||
# define OPUS_RESTRICT
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
# define OPUS_RESTRICT restrict
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) )
|
|
||||||
# if OPUS_GNUC_PREREQ(2,7)
|
|
||||||
# define OPUS_INLINE __inline__
|
|
||||||
# elif (defined(_MSC_VER))
|
|
||||||
# define OPUS_INLINE __inline
|
|
||||||
# else
|
|
||||||
# define OPUS_INLINE
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
# define OPUS_INLINE inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**Warning attributes for opus functions
|
|
||||||
* NONNULL is not used in OPUS_BUILD to avoid the compiler optimizing out
|
|
||||||
* some paranoid null checks. */
|
|
||||||
#if defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4)
|
|
||||||
# define OPUS_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__))
|
|
||||||
#else
|
|
||||||
# define OPUS_WARN_UNUSED_RESULT
|
|
||||||
#endif
|
|
||||||
#if !defined(OPUS_BUILD) && defined(__GNUC__) && OPUS_GNUC_PREREQ(3, 4)
|
|
||||||
# define OPUS_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x)))
|
|
||||||
#else
|
|
||||||
# define OPUS_ARG_NONNULL(_x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** These are the actual Encoder CTL ID numbers.
|
|
||||||
* They should not be used directly by applications.
|
|
||||||
* In general, SETs should be even and GETs should be odd.*/
|
|
||||||
#define OPUS_SET_APPLICATION_REQUEST 4000
|
|
||||||
#define OPUS_GET_APPLICATION_REQUEST 4001
|
|
||||||
#define OPUS_SET_BITRATE_REQUEST 4002
|
|
||||||
#define OPUS_GET_BITRATE_REQUEST 4003
|
|
||||||
#define OPUS_SET_MAX_BANDWIDTH_REQUEST 4004
|
|
||||||
#define OPUS_GET_MAX_BANDWIDTH_REQUEST 4005
|
|
||||||
#define OPUS_SET_VBR_REQUEST 4006
|
|
||||||
#define OPUS_GET_VBR_REQUEST 4007
|
|
||||||
#define OPUS_SET_BANDWIDTH_REQUEST 4008
|
|
||||||
#define OPUS_GET_BANDWIDTH_REQUEST 4009
|
|
||||||
#define OPUS_SET_COMPLEXITY_REQUEST 4010
|
|
||||||
#define OPUS_GET_COMPLEXITY_REQUEST 4011
|
|
||||||
#define OPUS_SET_INBAND_FEC_REQUEST 4012
|
|
||||||
#define OPUS_GET_INBAND_FEC_REQUEST 4013
|
|
||||||
#define OPUS_SET_PACKET_LOSS_PERC_REQUEST 4014
|
|
||||||
#define OPUS_GET_PACKET_LOSS_PERC_REQUEST 4015
|
|
||||||
#define OPUS_SET_DTX_REQUEST 4016
|
|
||||||
#define OPUS_GET_DTX_REQUEST 4017
|
|
||||||
#define OPUS_SET_VBR_CONSTRAINT_REQUEST 4020
|
|
||||||
#define OPUS_GET_VBR_CONSTRAINT_REQUEST 4021
|
|
||||||
#define OPUS_SET_FORCE_CHANNELS_REQUEST 4022
|
|
||||||
#define OPUS_GET_FORCE_CHANNELS_REQUEST 4023
|
|
||||||
#define OPUS_SET_SIGNAL_REQUEST 4024
|
|
||||||
#define OPUS_GET_SIGNAL_REQUEST 4025
|
|
||||||
#define OPUS_GET_LOOKAHEAD_REQUEST 4027
|
|
||||||
/* #define OPUS_RESET_STATE 4028 */
|
|
||||||
#define OPUS_GET_SAMPLE_RATE_REQUEST 4029
|
|
||||||
#define OPUS_GET_FINAL_RANGE_REQUEST 4031
|
|
||||||
#define OPUS_GET_PITCH_REQUEST 4033
|
|
||||||
#define OPUS_SET_GAIN_REQUEST 4034
|
|
||||||
#define OPUS_GET_GAIN_REQUEST 4045 /* Should have been 4035 */
|
|
||||||
#define OPUS_SET_LSB_DEPTH_REQUEST 4036
|
|
||||||
#define OPUS_GET_LSB_DEPTH_REQUEST 4037
|
|
||||||
#define OPUS_GET_LAST_PACKET_DURATION_REQUEST 4039
|
|
||||||
#define OPUS_SET_EXPERT_FRAME_DURATION_REQUEST 4040
|
|
||||||
#define OPUS_GET_EXPERT_FRAME_DURATION_REQUEST 4041
|
|
||||||
#define OPUS_SET_PREDICTION_DISABLED_REQUEST 4042
|
|
||||||
#define OPUS_GET_PREDICTION_DISABLED_REQUEST 4043
|
|
||||||
|
|
||||||
/* Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST */
|
|
||||||
|
|
||||||
/* Macros to trigger compilation errors when the wrong types are provided to a CTL */
|
|
||||||
#define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x))
|
|
||||||
#define __opus_check_int_ptr(ptr) ((ptr) + ((ptr) - (opus_int32*)(ptr)))
|
|
||||||
#define __opus_check_uint_ptr(ptr) ((ptr) + ((ptr) - (opus_uint32*)(ptr)))
|
|
||||||
#define __opus_check_val16_ptr(ptr) ((ptr) + ((ptr) - (opus_val16*)(ptr)))
|
|
||||||
/** @endcond */
|
|
||||||
|
|
||||||
/** @defgroup opus_ctlvalues Pre-defined values for CTL interface
|
|
||||||
* @see opus_genericctls, opus_encoderctls
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
/* Values for the various encoder CTLs */
|
|
||||||
#define OPUS_AUTO -1000 /**<Auto/default setting @hideinitializer*/
|
|
||||||
#define OPUS_BITRATE_MAX -1 /**<Maximum bitrate @hideinitializer*/
|
|
||||||
|
|
||||||
/** Best for most VoIP/videoconference applications where listening quality and intelligibility matter most
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_APPLICATION_VOIP 2048
|
|
||||||
/** Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_APPLICATION_AUDIO 2049
|
|
||||||
/** Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_APPLICATION_RESTRICTED_LOWDELAY 2051
|
|
||||||
|
|
||||||
#define OPUS_SIGNAL_VOICE 3001 /**< Signal being encoded is voice */
|
|
||||||
#define OPUS_SIGNAL_MUSIC 3002 /**< Signal being encoded is music */
|
|
||||||
#define OPUS_BANDWIDTH_NARROWBAND 1101 /**< 4 kHz bandpass @hideinitializer*/
|
|
||||||
#define OPUS_BANDWIDTH_MEDIUMBAND 1102 /**< 6 kHz bandpass @hideinitializer*/
|
|
||||||
#define OPUS_BANDWIDTH_WIDEBAND 1103 /**< 8 kHz bandpass @hideinitializer*/
|
|
||||||
#define OPUS_BANDWIDTH_SUPERWIDEBAND 1104 /**<12 kHz bandpass @hideinitializer*/
|
|
||||||
#define OPUS_BANDWIDTH_FULLBAND 1105 /**<20 kHz bandpass @hideinitializer*/
|
|
||||||
|
|
||||||
#define OPUS_FRAMESIZE_ARG 5000 /**< Select frame size from the argument (default) */
|
|
||||||
#define OPUS_FRAMESIZE_2_5_MS 5001 /**< Use 2.5 ms frames */
|
|
||||||
#define OPUS_FRAMESIZE_5_MS 5002 /**< Use 5 ms frames */
|
|
||||||
#define OPUS_FRAMESIZE_10_MS 5003 /**< Use 10 ms frames */
|
|
||||||
#define OPUS_FRAMESIZE_20_MS 5004 /**< Use 20 ms frames */
|
|
||||||
#define OPUS_FRAMESIZE_40_MS 5005 /**< Use 40 ms frames */
|
|
||||||
#define OPUS_FRAMESIZE_60_MS 5006 /**< Use 60 ms frames */
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
|
|
||||||
/** @defgroup opus_encoderctls Encoder related CTLs
|
|
||||||
*
|
|
||||||
* These are convenience macros for use with the \c opus_encode_ctl
|
|
||||||
* interface. They are used to generate the appropriate series of
|
|
||||||
* arguments for that call, passing the correct type, size and so
|
|
||||||
* on as expected for each particular request.
|
|
||||||
*
|
|
||||||
* Some usage examples:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* int ret;
|
|
||||||
* ret = opus_encoder_ctl(enc_ctx, OPUS_SET_BANDWIDTH(OPUS_AUTO));
|
|
||||||
* if (ret != OPUS_OK) return ret;
|
|
||||||
*
|
|
||||||
* opus_int32 rate;
|
|
||||||
* opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&rate));
|
|
||||||
*
|
|
||||||
* opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE);
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* @see opus_genericctls, opus_encoder
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Configures the encoder's computational complexity.
|
|
||||||
* The supported range is 0-10 inclusive with 10 representing the highest complexity.
|
|
||||||
* @see OPUS_GET_COMPLEXITY
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values: 0-10, inclusive.
|
|
||||||
*
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_COMPLEXITY(x) OPUS_SET_COMPLEXITY_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's complexity configuration.
|
|
||||||
* @see OPUS_SET_COMPLEXITY
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns a value in the range 0-10,
|
|
||||||
* inclusive.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_COMPLEXITY(x) OPUS_GET_COMPLEXITY_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures the bitrate in the encoder.
|
|
||||||
* Rates from 500 to 512000 bits per second are meaningful, as well as the
|
|
||||||
* special values #OPUS_AUTO and #OPUS_BITRATE_MAX.
|
|
||||||
* The value #OPUS_BITRATE_MAX can be used to cause the codec to use as much
|
|
||||||
* rate as it can, which is useful for controlling the rate by adjusting the
|
|
||||||
* output buffer size.
|
|
||||||
* @see OPUS_GET_BITRATE
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Bitrate in bits per second. The default
|
|
||||||
* is determined based on the number of
|
|
||||||
* channels and the input sampling rate.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_BITRATE(x) OPUS_SET_BITRATE_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's bitrate configuration.
|
|
||||||
* @see OPUS_SET_BITRATE
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns the bitrate in bits per second.
|
|
||||||
* The default is determined based on the
|
|
||||||
* number of channels and the input
|
|
||||||
* sampling rate.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_BITRATE(x) OPUS_GET_BITRATE_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Enables or disables variable bitrate (VBR) in the encoder.
|
|
||||||
* The configured bitrate may not be met exactly because frames must
|
|
||||||
* be an integer number of bytes in length.
|
|
||||||
* @warning Only the MDCT mode of Opus can provide hard CBR behavior.
|
|
||||||
* @see OPUS_GET_VBR
|
|
||||||
* @see OPUS_SET_VBR_CONSTRAINT
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Hard CBR. For LPC/hybrid modes at very low bit-rate, this can
|
|
||||||
* cause noticeable quality degradation.</dd>
|
|
||||||
* <dt>1</dt><dd>VBR (default). The exact type of VBR is controlled by
|
|
||||||
* #OPUS_SET_VBR_CONSTRAINT.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_VBR(x) OPUS_SET_VBR_REQUEST, __opus_check_int(x)
|
|
||||||
/** Determine if variable bitrate (VBR) is enabled in the encoder.
|
|
||||||
* @see OPUS_SET_VBR
|
|
||||||
* @see OPUS_GET_VBR_CONSTRAINT
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Hard CBR.</dd>
|
|
||||||
* <dt>1</dt><dd>VBR (default). The exact type of VBR may be retrieved via
|
|
||||||
* #OPUS_GET_VBR_CONSTRAINT.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_VBR(x) OPUS_GET_VBR_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Enables or disables constrained VBR in the encoder.
|
|
||||||
* This setting is ignored when the encoder is in CBR mode.
|
|
||||||
* @warning Only the MDCT mode of Opus currently heeds the constraint.
|
|
||||||
* Speech mode ignores it completely, hybrid mode may fail to obey it
|
|
||||||
* if the LPC layer uses more bitrate than the constraint would have
|
|
||||||
* permitted.
|
|
||||||
* @see OPUS_GET_VBR_CONSTRAINT
|
|
||||||
* @see OPUS_SET_VBR
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Unconstrained VBR.</dd>
|
|
||||||
* <dt>1</dt><dd>Constrained VBR (default). This creates a maximum of one
|
|
||||||
* frame of buffering delay assuming a transport with a
|
|
||||||
* serialization speed of the nominal bitrate.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_VBR_CONSTRAINT(x) OPUS_SET_VBR_CONSTRAINT_REQUEST, __opus_check_int(x)
|
|
||||||
/** Determine if constrained VBR is enabled in the encoder.
|
|
||||||
* @see OPUS_SET_VBR_CONSTRAINT
|
|
||||||
* @see OPUS_GET_VBR
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Unconstrained VBR.</dd>
|
|
||||||
* <dt>1</dt><dd>Constrained VBR (default).</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_VBR_CONSTRAINT(x) OPUS_GET_VBR_CONSTRAINT_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures mono/stereo forcing in the encoder.
|
|
||||||
* This can force the encoder to produce packets encoded as either mono or
|
|
||||||
* stereo, regardless of the format of the input audio. This is useful when
|
|
||||||
* the caller knows that the input signal is currently a mono source embedded
|
|
||||||
* in a stereo stream.
|
|
||||||
* @see OPUS_GET_FORCE_CHANNELS
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_AUTO</dt><dd>Not forced (default)</dd>
|
|
||||||
* <dt>1</dt> <dd>Forced mono</dd>
|
|
||||||
* <dt>2</dt> <dd>Forced stereo</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_FORCE_CHANNELS(x) OPUS_SET_FORCE_CHANNELS_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's forced channel configuration.
|
|
||||||
* @see OPUS_SET_FORCE_CHANNELS
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_AUTO</dt><dd>Not forced (default)</dd>
|
|
||||||
* <dt>1</dt> <dd>Forced mono</dd>
|
|
||||||
* <dt>2</dt> <dd>Forced stereo</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_FORCE_CHANNELS(x) OPUS_GET_FORCE_CHANNELS_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures the maximum bandpass that the encoder will select automatically.
|
|
||||||
* Applications should normally use this instead of #OPUS_SET_BANDWIDTH
|
|
||||||
* (leaving that set to the default, #OPUS_AUTO). This allows the
|
|
||||||
* application to set an upper bound based on the type of input it is
|
|
||||||
* providing, but still gives the encoder the freedom to reduce the bandpass
|
|
||||||
* when the bitrate becomes too low, for better overall quality.
|
|
||||||
* @see OPUS_GET_MAX_BANDWIDTH
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
|
||||||
* <dt>OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
|
||||||
* <dt>OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
|
||||||
* <dt>OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
|
||||||
* <dt>OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband (default)</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_MAX_BANDWIDTH(x) OPUS_SET_MAX_BANDWIDTH_REQUEST, __opus_check_int(x)
|
|
||||||
|
|
||||||
/** Gets the encoder's configured maximum allowed bandpass.
|
|
||||||
* @see OPUS_SET_MAX_BANDWIDTH
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband (default)</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_MAX_BANDWIDTH(x) OPUS_GET_MAX_BANDWIDTH_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Sets the encoder's bandpass to a specific value.
|
|
||||||
* This prevents the encoder from automatically selecting the bandpass based
|
|
||||||
* on the available bitrate. If an application knows the bandpass of the input
|
|
||||||
* audio it is providing, it should normally use #OPUS_SET_MAX_BANDWIDTH
|
|
||||||
* instead, which still gives the encoder the freedom to reduce the bandpass
|
|
||||||
* when the bitrate becomes too low, for better overall quality.
|
|
||||||
* @see OPUS_GET_BANDWIDTH
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_BANDWIDTH(x) OPUS_SET_BANDWIDTH_REQUEST, __opus_check_int(x)
|
|
||||||
|
|
||||||
/** Configures the type of signal being encoded.
|
|
||||||
* This is a hint which helps the encoder's mode selection.
|
|
||||||
* @see OPUS_GET_SIGNAL
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
|
||||||
* <dt>#OPUS_SIGNAL_VOICE</dt><dd>Bias thresholds towards choosing LPC or Hybrid modes.</dd>
|
|
||||||
* <dt>#OPUS_SIGNAL_MUSIC</dt><dd>Bias thresholds towards choosing MDCT modes.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_SIGNAL(x) OPUS_SET_SIGNAL_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's configured signal type.
|
|
||||||
* @see OPUS_SET_SIGNAL
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
|
||||||
* <dt>#OPUS_SIGNAL_VOICE</dt><dd>Bias thresholds towards choosing LPC or Hybrid modes.</dd>
|
|
||||||
* <dt>#OPUS_SIGNAL_MUSIC</dt><dd>Bias thresholds towards choosing MDCT modes.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_SIGNAL(x) OPUS_GET_SIGNAL_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
|
|
||||||
/** Configures the encoder's intended application.
|
|
||||||
* The initial value is a mandatory argument to the encoder_create function.
|
|
||||||
* @see OPUS_GET_APPLICATION
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
|
||||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
|
||||||
* <dd>Favor faithfulness to the original input.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
|
||||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
|
||||||
* of operation.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_APPLICATION(x) OPUS_SET_APPLICATION_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's configured application.
|
|
||||||
* @see OPUS_SET_APPLICATION
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
|
||||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
|
||||||
* <dd>Favor faithfulness to the original input.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
|
||||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
|
||||||
* of operation.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_APPLICATION(x) OPUS_GET_APPLICATION_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Gets the total samples of delay added by the entire codec.
|
|
||||||
* This can be queried by the encoder and then the provided number of samples can be
|
|
||||||
* skipped on from the start of the decoder's output to provide time aligned input
|
|
||||||
* and output. From the perspective of a decoding application the real data begins this many
|
|
||||||
* samples late.
|
|
||||||
*
|
|
||||||
* The decoder contribution to this delay is identical for all decoders, but the
|
|
||||||
* encoder portion of the delay may vary from implementation to implementation,
|
|
||||||
* version to version, or even depend on the encoder's initial configuration.
|
|
||||||
* Applications needing delay compensation should call this CTL rather than
|
|
||||||
* hard-coding a value.
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Number of lookahead samples
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_LOOKAHEAD(x) OPUS_GET_LOOKAHEAD_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures the encoder's use of inband forward error correction (FEC).
|
|
||||||
* @note This is only applicable to the LPC layer
|
|
||||||
* @see OPUS_GET_INBAND_FEC
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Disable inband FEC (default).</dd>
|
|
||||||
* <dt>1</dt><dd>Enable inband FEC.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_INBAND_FEC(x) OPUS_SET_INBAND_FEC_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets encoder's configured use of inband forward error correction.
|
|
||||||
* @see OPUS_SET_INBAND_FEC
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Inband FEC disabled (default).</dd>
|
|
||||||
* <dt>1</dt><dd>Inband FEC enabled.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_INBAND_FEC(x) OPUS_GET_INBAND_FEC_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures the encoder's expected packet loss percentage.
|
|
||||||
* Higher values with trigger progressively more loss resistant behavior in the encoder
|
|
||||||
* at the expense of quality at a given bitrate in the lossless case, but greater quality
|
|
||||||
* under loss.
|
|
||||||
* @see OPUS_GET_PACKET_LOSS_PERC
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Loss percentage in the range 0-100, inclusive (default: 0).
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_PACKET_LOSS_PERC(x) OPUS_SET_PACKET_LOSS_PERC_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's configured packet loss percentage.
|
|
||||||
* @see OPUS_SET_PACKET_LOSS_PERC
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns the configured loss percentage
|
|
||||||
* in the range 0-100, inclusive (default: 0).
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_PACKET_LOSS_PERC(x) OPUS_GET_PACKET_LOSS_PERC_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures the encoder's use of discontinuous transmission (DTX).
|
|
||||||
* @note This is only applicable to the LPC layer
|
|
||||||
* @see OPUS_GET_DTX
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>Disable DTX (default).</dd>
|
|
||||||
* <dt>1</dt><dd>Enabled DTX.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_DTX(x) OPUS_SET_DTX_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets encoder's configured use of discontinuous transmission.
|
|
||||||
* @see OPUS_SET_DTX
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>0</dt><dd>DTX disabled (default).</dd>
|
|
||||||
* <dt>1</dt><dd>DTX enabled.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_DTX(x) OPUS_GET_DTX_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
/** Configures the depth of signal being encoded.
|
|
||||||
* This is a hint which helps the encoder identify silence and near-silence.
|
|
||||||
* @see OPUS_GET_LSB_DEPTH
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Input precision in bits, between 8 and 24
|
|
||||||
* (default: 24).
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_LSB_DEPTH(x) OPUS_SET_LSB_DEPTH_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's configured signal depth.
|
|
||||||
* @see OPUS_SET_LSB_DEPTH
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Input precision in bits, between 8 and
|
|
||||||
* 24 (default: 24).
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_LSB_DEPTH(x) OPUS_GET_LSB_DEPTH_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Configures the encoder's use of variable duration frames.
|
|
||||||
* When variable duration is enabled, the encoder is free to use a shorter frame
|
|
||||||
* size than the one requested in the opus_encode*() call.
|
|
||||||
* It is then the user's responsibility
|
|
||||||
* to verify how much audio was encoded by checking the ToC byte of the encoded
|
|
||||||
* packet. The part of the audio that was not encoded needs to be resent to the
|
|
||||||
* encoder for the next call. Do not use this option unless you <b>really</b>
|
|
||||||
* know what you are doing.
|
|
||||||
* @see OPUS_GET_EXPERT_VARIABLE_DURATION
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Allowed values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>OPUS_FRAMESIZE_ARG</dt><dd>Select frame size from the argument (default).</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_2_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_10_MS</dt><dd>Use 10 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_20_MS</dt><dd>Use 20 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_40_MS</dt><dd>Use 40 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_60_MS</dt><dd>Use 60 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_VARIABLE</dt><dd>Optimize the frame size dynamically.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_EXPERT_FRAME_DURATION(x) OPUS_SET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's configured use of variable duration frames.
|
|
||||||
* @see OPUS_SET_EXPERT_VARIABLE_DURATION
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>OPUS_FRAMESIZE_ARG</dt><dd>Select frame size from the argument (default).</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_2_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_5_MS</dt><dd>Use 2.5 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_10_MS</dt><dd>Use 10 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_20_MS</dt><dd>Use 20 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_40_MS</dt><dd>Use 40 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_60_MS</dt><dd>Use 60 ms frames.</dd>
|
|
||||||
* <dt>OPUS_FRAMESIZE_VARIABLE</dt><dd>Optimize the frame size dynamically.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_EXPERT_FRAME_DURATION(x) OPUS_GET_EXPERT_FRAME_DURATION_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** If set to 1, disables almost all use of prediction, making frames almost
|
|
||||||
completely independent. This reduces quality. (default : 0)
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_PREDICTION_DISABLED(x) OPUS_SET_PREDICTION_DISABLED_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the encoder's configured prediction status.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_PREDICTION_DISABLED(x) OPUS_GET_PREDICTION_DISABLED_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @defgroup opus_genericctls Generic CTLs
|
|
||||||
*
|
|
||||||
* These macros are used with the \c opus_decoder_ctl and
|
|
||||||
* \c opus_encoder_ctl calls to generate a particular
|
|
||||||
* request.
|
|
||||||
*
|
|
||||||
* When called on an \c OpusDecoder they apply to that
|
|
||||||
* particular decoder instance. When called on an
|
|
||||||
* \c OpusEncoder they apply to the corresponding setting
|
|
||||||
* on that encoder instance, if present.
|
|
||||||
*
|
|
||||||
* Some usage examples:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* int ret;
|
|
||||||
* opus_int32 pitch;
|
|
||||||
* ret = opus_decoder_ctl(dec_ctx, OPUS_GET_PITCH(&pitch));
|
|
||||||
* if (ret == OPUS_OK) return ret;
|
|
||||||
*
|
|
||||||
* opus_encoder_ctl(enc_ctx, OPUS_RESET_STATE);
|
|
||||||
* opus_decoder_ctl(dec_ctx, OPUS_RESET_STATE);
|
|
||||||
*
|
|
||||||
* opus_int32 enc_bw, dec_bw;
|
|
||||||
* opus_encoder_ctl(enc_ctx, OPUS_GET_BANDWIDTH(&enc_bw));
|
|
||||||
* opus_decoder_ctl(dec_ctx, OPUS_GET_BANDWIDTH(&dec_bw));
|
|
||||||
* if (enc_bw != dec_bw) {
|
|
||||||
* printf("packet bandwidth mismatch!\n");
|
|
||||||
* }
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* @see opus_encoder, opus_decoder_ctl, opus_encoder_ctl, opus_decoderctls, opus_encoderctls
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Resets the codec state to be equivalent to a freshly initialized state.
|
|
||||||
* This should be called when switching streams in order to prevent
|
|
||||||
* the back to back decoding from giving different results from
|
|
||||||
* one at a time decoding.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_RESET_STATE 4028
|
|
||||||
|
|
||||||
/** Gets the final state of the codec's entropy coder.
|
|
||||||
* This is used for testing purposes,
|
|
||||||
* The encoder and decoder state should be identical after coding a payload
|
|
||||||
* (assuming no data corruption or software bugs)
|
|
||||||
*
|
|
||||||
* @param[out] x <tt>opus_uint32 *</tt>: Entropy coder state
|
|
||||||
*
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_FINAL_RANGE(x) OPUS_GET_FINAL_RANGE_REQUEST, __opus_check_uint_ptr(x)
|
|
||||||
|
|
||||||
/** Gets the encoder's configured bandpass or the decoder's last bandpass.
|
|
||||||
* @see OPUS_SET_BANDWIDTH
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_AUTO</dt> <dd>(default)</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_NARROWBAND</dt> <dd>4 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_MEDIUMBAND</dt> <dd>6 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_WIDEBAND</dt> <dd>8 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_SUPERWIDEBAND</dt><dd>12 kHz passband</dd>
|
|
||||||
* <dt>#OPUS_BANDWIDTH_FULLBAND</dt> <dd>20 kHz passband</dd>
|
|
||||||
* </dl>
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_BANDWIDTH(x) OPUS_GET_BANDWIDTH_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Gets the sampling rate the encoder or decoder was initialized with.
|
|
||||||
* This simply returns the <code>Fs</code> value passed to opus_encoder_init()
|
|
||||||
* or opus_decoder_init().
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Sampling rate of encoder or decoder.
|
|
||||||
* @hideinitializer
|
|
||||||
*/
|
|
||||||
#define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @defgroup opus_decoderctls Decoder related CTLs
|
|
||||||
* @see opus_genericctls, opus_encoderctls, opus_decoder
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Configures decoder gain adjustment.
|
|
||||||
* Scales the decoded output by a factor specified in Q8 dB units.
|
|
||||||
* This has a maximum range of -32768 to 32767 inclusive, and returns
|
|
||||||
* OPUS_BAD_ARG otherwise. The default is zero indicating no adjustment.
|
|
||||||
* This setting survives decoder reset.
|
|
||||||
*
|
|
||||||
* gain = pow(10, x/(20.0*256))
|
|
||||||
*
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: Amount to scale PCM signal by in Q8 dB units.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_SET_GAIN(x) OPUS_SET_GAIN_REQUEST, __opus_check_int(x)
|
|
||||||
/** Gets the decoder's configured gain adjustment. @see OPUS_SET_GAIN
|
|
||||||
*
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Amount to scale PCM signal by in Q8 dB units.
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_GAIN(x) OPUS_GET_GAIN_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Gets the duration (in samples) of the last packet successfully decoded or concealed.
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: Number of samples (at current sampling rate).
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_LAST_PACKET_DURATION(x) OPUS_GET_LAST_PACKET_DURATION_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/** Gets the pitch of the last decoded frame, if available.
|
|
||||||
* This can be used for any post-processing algorithm requiring the use of pitch,
|
|
||||||
* e.g. time stretching/shortening. If the last frame was not voiced, or if the
|
|
||||||
* pitch was not coded in the frame, then zero is returned.
|
|
||||||
*
|
|
||||||
* This CTL is only implemented for decoder instances.
|
|
||||||
*
|
|
||||||
* @param[out] x <tt>opus_int32 *</tt>: pitch period at 48 kHz (or 0 if not available)
|
|
||||||
*
|
|
||||||
* @hideinitializer */
|
|
||||||
#define OPUS_GET_PITCH(x) OPUS_GET_PITCH_REQUEST, __opus_check_int_ptr(x)
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @defgroup opus_libinfo Opus library information functions
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Converts an opus error code into a human readable string.
|
|
||||||
*
|
|
||||||
* @param[in] error <tt>int</tt>: Error number
|
|
||||||
* @returns Error string
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT const char *opus_strerror(int error);
|
|
||||||
|
|
||||||
/** Gets the libopus version string.
|
|
||||||
*
|
|
||||||
* @returns Version string
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT const char *opus_get_version_string(void);
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* OPUS_DEFINES_H */
|
|
@ -1,660 +0,0 @@
|
|||||||
/* Copyright (c) 2011 Xiph.Org Foundation
|
|
||||||
Written by Jean-Marc Valin */
|
|
||||||
/*
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
- Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
- Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file opus_multistream.h
|
|
||||||
* @brief Opus reference implementation multistream API
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef OPUS_MULTISTREAM_H
|
|
||||||
#define OPUS_MULTISTREAM_H
|
|
||||||
|
|
||||||
#include "opus.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** @cond OPUS_INTERNAL_DOC */
|
|
||||||
|
|
||||||
/** Macros to trigger compilation errors when the wrong types are provided to a
|
|
||||||
* CTL. */
|
|
||||||
/**@{*/
|
|
||||||
#define __opus_check_encstate_ptr(ptr) ((ptr) + ((ptr) - (OpusEncoder**)(ptr)))
|
|
||||||
#define __opus_check_decstate_ptr(ptr) ((ptr) + ((ptr) - (OpusDecoder**)(ptr)))
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** These are the actual encoder and decoder CTL ID numbers.
|
|
||||||
* They should not be used directly by applications.
|
|
||||||
* In general, SETs should be even and GETs should be odd.*/
|
|
||||||
/**@{*/
|
|
||||||
#define OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST 5120
|
|
||||||
#define OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST 5122
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @endcond */
|
|
||||||
|
|
||||||
/** @defgroup opus_multistream_ctls Multistream specific encoder and decoder CTLs
|
|
||||||
*
|
|
||||||
* These are convenience macros that are specific to the
|
|
||||||
* opus_multistream_encoder_ctl() and opus_multistream_decoder_ctl()
|
|
||||||
* interface.
|
|
||||||
* The CTLs from @ref opus_genericctls, @ref opus_encoderctls, and
|
|
||||||
* @ref opus_decoderctls may be applied to a multistream encoder or decoder as
|
|
||||||
* well.
|
|
||||||
* In addition, you may retrieve the encoder or decoder state for an specific
|
|
||||||
* stream via #OPUS_MULTISTREAM_GET_ENCODER_STATE or
|
|
||||||
* #OPUS_MULTISTREAM_GET_DECODER_STATE and apply CTLs to it individually.
|
|
||||||
*/
|
|
||||||
/**@{*/
|
|
||||||
|
|
||||||
/** Gets the encoder state for an individual stream of a multistream encoder.
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: The index of the stream whose encoder you
|
|
||||||
* wish to retrieve.
|
|
||||||
* This must be non-negative and less than
|
|
||||||
* the <code>streams</code> parameter used
|
|
||||||
* to initialize the encoder.
|
|
||||||
* @param[out] y <tt>OpusEncoder**</tt>: Returns a pointer to the given
|
|
||||||
* encoder state.
|
|
||||||
* @retval OPUS_BAD_ARG The index of the requested stream was out of range.
|
|
||||||
* @hideinitializer
|
|
||||||
*/
|
|
||||||
#define OPUS_MULTISTREAM_GET_ENCODER_STATE(x,y) OPUS_MULTISTREAM_GET_ENCODER_STATE_REQUEST, __opus_check_int(x), __opus_check_encstate_ptr(y)
|
|
||||||
|
|
||||||
/** Gets the decoder state for an individual stream of a multistream decoder.
|
|
||||||
* @param[in] x <tt>opus_int32</tt>: The index of the stream whose decoder you
|
|
||||||
* wish to retrieve.
|
|
||||||
* This must be non-negative and less than
|
|
||||||
* the <code>streams</code> parameter used
|
|
||||||
* to initialize the decoder.
|
|
||||||
* @param[out] y <tt>OpusDecoder**</tt>: Returns a pointer to the given
|
|
||||||
* decoder state.
|
|
||||||
* @retval OPUS_BAD_ARG The index of the requested stream was out of range.
|
|
||||||
* @hideinitializer
|
|
||||||
*/
|
|
||||||
#define OPUS_MULTISTREAM_GET_DECODER_STATE(x,y) OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST, __opus_check_int(x), __opus_check_decstate_ptr(y)
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @defgroup opus_multistream Opus Multistream API
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* The multistream API allows individual Opus streams to be combined into a
|
|
||||||
* single packet, enabling support for up to 255 channels. Unlike an
|
|
||||||
* elementary Opus stream, the encoder and decoder must negotiate the channel
|
|
||||||
* configuration before the decoder can successfully interpret the data in the
|
|
||||||
* packets produced by the encoder. Some basic information, such as packet
|
|
||||||
* duration, can be computed without any special negotiation.
|
|
||||||
*
|
|
||||||
* The format for multistream Opus packets is defined in the
|
|
||||||
* <a href="http://tools.ietf.org/html/draft-terriberry-oggopus">Ogg
|
|
||||||
* encapsulation specification</a> and is based on the self-delimited Opus
|
|
||||||
* framing described in Appendix B of <a href="http://tools.ietf.org/html/rfc6716">RFC 6716</a>.
|
|
||||||
* Normal Opus packets are just a degenerate case of multistream Opus packets,
|
|
||||||
* and can be encoded or decoded with the multistream API by setting
|
|
||||||
* <code>streams</code> to <code>1</code> when initializing the encoder or
|
|
||||||
* decoder.
|
|
||||||
*
|
|
||||||
* Multistream Opus streams can contain up to 255 elementary Opus streams.
|
|
||||||
* These may be either "uncoupled" or "coupled", indicating that the decoder
|
|
||||||
* is configured to decode them to either 1 or 2 channels, respectively.
|
|
||||||
* The streams are ordered so that all coupled streams appear at the
|
|
||||||
* beginning.
|
|
||||||
*
|
|
||||||
* A <code>mapping</code> table defines which decoded channel <code>i</code>
|
|
||||||
* should be used for each input/output (I/O) channel <code>j</code>. This table is
|
|
||||||
* typically provided as an unsigned char array.
|
|
||||||
* Let <code>i = mapping[j]</code> be the index for I/O channel <code>j</code>.
|
|
||||||
* If <code>i < 2*coupled_streams</code>, then I/O channel <code>j</code> is
|
|
||||||
* encoded as the left channel of stream <code>(i/2)</code> if <code>i</code>
|
|
||||||
* is even, or as the right channel of stream <code>(i/2)</code> if
|
|
||||||
* <code>i</code> is odd. Otherwise, I/O channel <code>j</code> is encoded as
|
|
||||||
* mono in stream <code>(i - coupled_streams)</code>, unless it has the special
|
|
||||||
* value 255, in which case it is omitted from the encoding entirely (the
|
|
||||||
* decoder will reproduce it as silence). Each value <code>i</code> must either
|
|
||||||
* be the special value 255 or be less than <code>streams + coupled_streams</code>.
|
|
||||||
*
|
|
||||||
* The output channels specified by the encoder
|
|
||||||
* should use the
|
|
||||||
* <a href="http://www.xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9">Vorbis
|
|
||||||
* channel ordering</a>. A decoder may wish to apply an additional permutation
|
|
||||||
* to the mapping the encoder used to achieve a different output channel
|
|
||||||
* order (e.g. for outputing in WAV order).
|
|
||||||
*
|
|
||||||
* Each multistream packet contains an Opus packet for each stream, and all of
|
|
||||||
* the Opus packets in a single multistream packet must have the same
|
|
||||||
* duration. Therefore the duration of a multistream packet can be extracted
|
|
||||||
* from the TOC sequence of the first stream, which is located at the
|
|
||||||
* beginning of the packet, just like an elementary Opus stream:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* int nb_samples;
|
|
||||||
* int nb_frames;
|
|
||||||
* nb_frames = opus_packet_get_nb_frames(data, len);
|
|
||||||
* if (nb_frames < 1)
|
|
||||||
* return nb_frames;
|
|
||||||
* nb_samples = opus_packet_get_samples_per_frame(data, 48000) * nb_frames;
|
|
||||||
* @endcode
|
|
||||||
*
|
|
||||||
* The general encoding and decoding process proceeds exactly the same as in
|
|
||||||
* the normal @ref opus_encoder and @ref opus_decoder APIs.
|
|
||||||
* See their documentation for an overview of how to use the corresponding
|
|
||||||
* multistream functions.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** Opus multistream encoder state.
|
|
||||||
* This contains the complete state of a multistream Opus encoder.
|
|
||||||
* It is position independent and can be freely copied.
|
|
||||||
* @see opus_multistream_encoder_create
|
|
||||||
* @see opus_multistream_encoder_init
|
|
||||||
*/
|
|
||||||
typedef struct OpusMSEncoder OpusMSEncoder;
|
|
||||||
|
|
||||||
/** Opus multistream decoder state.
|
|
||||||
* This contains the complete state of a multistream Opus decoder.
|
|
||||||
* It is position independent and can be freely copied.
|
|
||||||
* @see opus_multistream_decoder_create
|
|
||||||
* @see opus_multistream_decoder_init
|
|
||||||
*/
|
|
||||||
typedef struct OpusMSDecoder OpusMSDecoder;
|
|
||||||
|
|
||||||
/**\name Multistream encoder functions */
|
|
||||||
/**@{*/
|
|
||||||
|
|
||||||
/** Gets the size of an OpusMSEncoder structure.
|
|
||||||
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
|
||||||
* input.
|
|
||||||
* This must be no more than 255.
|
|
||||||
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
|
||||||
* to encode.
|
|
||||||
* This must be no larger than the total
|
|
||||||
* number of streams.
|
|
||||||
* Additionally, The total number of
|
|
||||||
* encoded channels (<code>streams +
|
|
||||||
* coupled_streams</code>) must be no
|
|
||||||
* more than 255.
|
|
||||||
* @returns The size in bytes on success, or a negative error code
|
|
||||||
* (see @ref opus_errorcodes) on error.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_encoder_get_size(
|
|
||||||
int streams,
|
|
||||||
int coupled_streams
|
|
||||||
);
|
|
||||||
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_surround_encoder_get_size(
|
|
||||||
int channels,
|
|
||||||
int mapping_family
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
/** Allocates and initializes a multistream encoder state.
|
|
||||||
* Call opus_multistream_encoder_destroy() to release
|
|
||||||
* this object when finished.
|
|
||||||
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
|
||||||
* This must be at most 255.
|
|
||||||
* It may be greater than the number of
|
|
||||||
* coded channels (<code>streams +
|
|
||||||
* coupled_streams</code>).
|
|
||||||
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
|
||||||
* input.
|
|
||||||
* This must be no more than the number of channels.
|
|
||||||
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
|
||||||
* to encode.
|
|
||||||
* This must be no larger than the total
|
|
||||||
* number of streams.
|
|
||||||
* Additionally, The total number of
|
|
||||||
* encoded channels (<code>streams +
|
|
||||||
* coupled_streams</code>) must be no
|
|
||||||
* more than the number of input channels.
|
|
||||||
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
|
||||||
* encoded channels to input channels, as described in
|
|
||||||
* @ref opus_multistream. As an extra constraint, the
|
|
||||||
* multistream encoder does not allow encoding coupled
|
|
||||||
* streams for which one channel is unused since this
|
|
||||||
* is never a good idea.
|
|
||||||
* @param application <tt>int</tt>: The target encoder application.
|
|
||||||
* This must be one of the following:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
|
||||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
|
||||||
* <dd>Favor faithfulness to the original input.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
|
||||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
|
||||||
* of operation.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
|
||||||
* code (see @ref opus_errorcodes) on
|
|
||||||
* failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_encoder_create(
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int streams,
|
|
||||||
int coupled_streams,
|
|
||||||
const unsigned char *mapping,
|
|
||||||
int application,
|
|
||||||
int *error
|
|
||||||
) OPUS_ARG_NONNULL(5);
|
|
||||||
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSEncoder *opus_multistream_surround_encoder_create(
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int mapping_family,
|
|
||||||
int *streams,
|
|
||||||
int *coupled_streams,
|
|
||||||
unsigned char *mapping,
|
|
||||||
int application,
|
|
||||||
int *error
|
|
||||||
) OPUS_ARG_NONNULL(5);
|
|
||||||
|
|
||||||
/** Initialize a previously allocated multistream encoder state.
|
|
||||||
* The memory pointed to by \a st must be at least the size returned by
|
|
||||||
* opus_multistream_encoder_get_size().
|
|
||||||
* This is intended for applications which use their own allocator instead of
|
|
||||||
* malloc.
|
|
||||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
|
||||||
* @see opus_multistream_encoder_create
|
|
||||||
* @see opus_multistream_encoder_get_size
|
|
||||||
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state to initialize.
|
|
||||||
* @param Fs <tt>opus_int32</tt>: Sampling rate of the input signal (in Hz).
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param channels <tt>int</tt>: Number of channels in the input signal.
|
|
||||||
* This must be at most 255.
|
|
||||||
* It may be greater than the number of
|
|
||||||
* coded channels (<code>streams +
|
|
||||||
* coupled_streams</code>).
|
|
||||||
* @param streams <tt>int</tt>: The total number of streams to encode from the
|
|
||||||
* input.
|
|
||||||
* This must be no more than the number of channels.
|
|
||||||
* @param coupled_streams <tt>int</tt>: Number of coupled (2 channel) streams
|
|
||||||
* to encode.
|
|
||||||
* This must be no larger than the total
|
|
||||||
* number of streams.
|
|
||||||
* Additionally, The total number of
|
|
||||||
* encoded channels (<code>streams +
|
|
||||||
* coupled_streams</code>) must be no
|
|
||||||
* more than the number of input channels.
|
|
||||||
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
|
||||||
* encoded channels to input channels, as described in
|
|
||||||
* @ref opus_multistream. As an extra constraint, the
|
|
||||||
* multistream encoder does not allow encoding coupled
|
|
||||||
* streams for which one channel is unused since this
|
|
||||||
* is never a good idea.
|
|
||||||
* @param application <tt>int</tt>: The target encoder application.
|
|
||||||
* This must be one of the following:
|
|
||||||
* <dl>
|
|
||||||
* <dt>#OPUS_APPLICATION_VOIP</dt>
|
|
||||||
* <dd>Process signal for improved speech intelligibility.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_AUDIO</dt>
|
|
||||||
* <dd>Favor faithfulness to the original input.</dd>
|
|
||||||
* <dt>#OPUS_APPLICATION_RESTRICTED_LOWDELAY</dt>
|
|
||||||
* <dd>Configure the minimum possible coding delay by disabling certain modes
|
|
||||||
* of operation.</dd>
|
|
||||||
* </dl>
|
|
||||||
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
|
||||||
* on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_multistream_encoder_init(
|
|
||||||
OpusMSEncoder *st,
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int streams,
|
|
||||||
int coupled_streams,
|
|
||||||
const unsigned char *mapping,
|
|
||||||
int application
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
|
||||||
|
|
||||||
OPUS_EXPORT int opus_multistream_surround_encoder_init(
|
|
||||||
OpusMSEncoder *st,
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int mapping_family,
|
|
||||||
int *streams,
|
|
||||||
int *coupled_streams,
|
|
||||||
unsigned char *mapping,
|
|
||||||
int application
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
|
||||||
|
|
||||||
/** Encodes a multistream Opus frame.
|
|
||||||
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
|
|
||||||
* @param[in] pcm <tt>const opus_int16*</tt>: The input signal as interleaved
|
|
||||||
* samples.
|
|
||||||
* This must contain
|
|
||||||
* <code>frame_size*channels</code>
|
|
||||||
* samples.
|
|
||||||
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
|
||||||
* signal.
|
|
||||||
* This must be an Opus frame size for the
|
|
||||||
* encoder's sampling rate.
|
|
||||||
* For example, at 48 kHz the permitted values
|
|
||||||
* are 120, 240, 480, 960, 1920, and 2880.
|
|
||||||
* Passing in a duration of less than 10 ms
|
|
||||||
* (480 samples at 48 kHz) will prevent the
|
|
||||||
* encoder from using the LPC or hybrid modes.
|
|
||||||
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
|
||||||
* This must contain storage for at
|
|
||||||
* least \a max_data_bytes.
|
|
||||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
|
||||||
* memory for the output
|
|
||||||
* payload. This may be
|
|
||||||
* used to impose an upper limit on
|
|
||||||
* the instant bitrate, but should
|
|
||||||
* not be used as the only bitrate
|
|
||||||
* control. Use #OPUS_SET_BITRATE to
|
|
||||||
* control the bitrate.
|
|
||||||
* @returns The length of the encoded packet (in bytes) on success or a
|
|
||||||
* negative error code (see @ref opus_errorcodes) on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_encode(
|
|
||||||
OpusMSEncoder *st,
|
|
||||||
const opus_int16 *pcm,
|
|
||||||
int frame_size,
|
|
||||||
unsigned char *data,
|
|
||||||
opus_int32 max_data_bytes
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Encodes a multistream Opus frame from floating point input.
|
|
||||||
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
|
|
||||||
* @param[in] pcm <tt>const float*</tt>: The input signal as interleaved
|
|
||||||
* samples with a normal range of
|
|
||||||
* +/-1.0.
|
|
||||||
* Samples with a range beyond +/-1.0
|
|
||||||
* are supported but will be clipped by
|
|
||||||
* decoders using the integer API and
|
|
||||||
* should only be used if it is known
|
|
||||||
* that the far end supports extended
|
|
||||||
* dynamic range.
|
|
||||||
* This must contain
|
|
||||||
* <code>frame_size*channels</code>
|
|
||||||
* samples.
|
|
||||||
* @param frame_size <tt>int</tt>: Number of samples per channel in the input
|
|
||||||
* signal.
|
|
||||||
* This must be an Opus frame size for the
|
|
||||||
* encoder's sampling rate.
|
|
||||||
* For example, at 48 kHz the permitted values
|
|
||||||
* are 120, 240, 480, 960, 1920, and 2880.
|
|
||||||
* Passing in a duration of less than 10 ms
|
|
||||||
* (480 samples at 48 kHz) will prevent the
|
|
||||||
* encoder from using the LPC or hybrid modes.
|
|
||||||
* @param[out] data <tt>unsigned char*</tt>: Output payload.
|
|
||||||
* This must contain storage for at
|
|
||||||
* least \a max_data_bytes.
|
|
||||||
* @param [in] max_data_bytes <tt>opus_int32</tt>: Size of the allocated
|
|
||||||
* memory for the output
|
|
||||||
* payload. This may be
|
|
||||||
* used to impose an upper limit on
|
|
||||||
* the instant bitrate, but should
|
|
||||||
* not be used as the only bitrate
|
|
||||||
* control. Use #OPUS_SET_BITRATE to
|
|
||||||
* control the bitrate.
|
|
||||||
* @returns The length of the encoded packet (in bytes) on success or a
|
|
||||||
* negative error code (see @ref opus_errorcodes) on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_encode_float(
|
|
||||||
OpusMSEncoder *st,
|
|
||||||
const float *pcm,
|
|
||||||
int frame_size,
|
|
||||||
unsigned char *data,
|
|
||||||
opus_int32 max_data_bytes
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(2) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Frees an <code>OpusMSEncoder</code> allocated by
|
|
||||||
* opus_multistream_encoder_create().
|
|
||||||
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT void opus_multistream_encoder_destroy(OpusMSEncoder *st);
|
|
||||||
|
|
||||||
/** Perform a CTL function on a multistream Opus encoder.
|
|
||||||
*
|
|
||||||
* Generally the request and subsequent arguments are generated by a
|
|
||||||
* convenience macro.
|
|
||||||
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state.
|
|
||||||
* @param request This and all remaining parameters should be replaced by one
|
|
||||||
* of the convenience macros in @ref opus_genericctls,
|
|
||||||
* @ref opus_encoderctls, or @ref opus_multistream_ctls.
|
|
||||||
* @see opus_genericctls
|
|
||||||
* @see opus_encoderctls
|
|
||||||
* @see opus_multistream_ctls
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/**\name Multistream decoder functions */
|
|
||||||
/**@{*/
|
|
||||||
|
|
||||||
/** Gets the size of an <code>OpusMSDecoder</code> structure.
|
|
||||||
* @param streams <tt>int</tt>: The total number of streams coded in the
|
|
||||||
* input.
|
|
||||||
* This must be no more than 255.
|
|
||||||
* @param coupled_streams <tt>int</tt>: Number streams to decode as coupled
|
|
||||||
* (2 channel) streams.
|
|
||||||
* This must be no larger than the total
|
|
||||||
* number of streams.
|
|
||||||
* Additionally, The total number of
|
|
||||||
* coded channels (<code>streams +
|
|
||||||
* coupled_streams</code>) must be no
|
|
||||||
* more than 255.
|
|
||||||
* @returns The size in bytes on success, or a negative error code
|
|
||||||
* (see @ref opus_errorcodes) on error.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT opus_int32 opus_multistream_decoder_get_size(
|
|
||||||
int streams,
|
|
||||||
int coupled_streams
|
|
||||||
);
|
|
||||||
|
|
||||||
/** Allocates and initializes a multistream decoder state.
|
|
||||||
* Call opus_multistream_decoder_destroy() to release
|
|
||||||
* this object when finished.
|
|
||||||
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param channels <tt>int</tt>: Number of channels to output.
|
|
||||||
* This must be at most 255.
|
|
||||||
* It may be different from the number of coded
|
|
||||||
* channels (<code>streams +
|
|
||||||
* coupled_streams</code>).
|
|
||||||
* @param streams <tt>int</tt>: The total number of streams coded in the
|
|
||||||
* input.
|
|
||||||
* This must be no more than 255.
|
|
||||||
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
|
||||||
* (2 channel) streams.
|
|
||||||
* This must be no larger than the total
|
|
||||||
* number of streams.
|
|
||||||
* Additionally, The total number of
|
|
||||||
* coded channels (<code>streams +
|
|
||||||
* coupled_streams</code>) must be no
|
|
||||||
* more than 255.
|
|
||||||
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
|
||||||
* coded channels to output channels, as described in
|
|
||||||
* @ref opus_multistream.
|
|
||||||
* @param[out] error <tt>int *</tt>: Returns #OPUS_OK on success, or an error
|
|
||||||
* code (see @ref opus_errorcodes) on
|
|
||||||
* failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT OpusMSDecoder *opus_multistream_decoder_create(
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int streams,
|
|
||||||
int coupled_streams,
|
|
||||||
const unsigned char *mapping,
|
|
||||||
int *error
|
|
||||||
) OPUS_ARG_NONNULL(5);
|
|
||||||
|
|
||||||
/** Intialize a previously allocated decoder state object.
|
|
||||||
* The memory pointed to by \a st must be at least the size returned by
|
|
||||||
* opus_multistream_encoder_get_size().
|
|
||||||
* This is intended for applications which use their own allocator instead of
|
|
||||||
* malloc.
|
|
||||||
* To reset a previously initialized state, use the #OPUS_RESET_STATE CTL.
|
|
||||||
* @see opus_multistream_decoder_create
|
|
||||||
* @see opus_multistream_deocder_get_size
|
|
||||||
* @param st <tt>OpusMSEncoder*</tt>: Multistream encoder state to initialize.
|
|
||||||
* @param Fs <tt>opus_int32</tt>: Sampling rate to decode at (in Hz).
|
|
||||||
* This must be one of 8000, 12000, 16000,
|
|
||||||
* 24000, or 48000.
|
|
||||||
* @param channels <tt>int</tt>: Number of channels to output.
|
|
||||||
* This must be at most 255.
|
|
||||||
* It may be different from the number of coded
|
|
||||||
* channels (<code>streams +
|
|
||||||
* coupled_streams</code>).
|
|
||||||
* @param streams <tt>int</tt>: The total number of streams coded in the
|
|
||||||
* input.
|
|
||||||
* This must be no more than 255.
|
|
||||||
* @param coupled_streams <tt>int</tt>: Number of streams to decode as coupled
|
|
||||||
* (2 channel) streams.
|
|
||||||
* This must be no larger than the total
|
|
||||||
* number of streams.
|
|
||||||
* Additionally, The total number of
|
|
||||||
* coded channels (<code>streams +
|
|
||||||
* coupled_streams</code>) must be no
|
|
||||||
* more than 255.
|
|
||||||
* @param[in] mapping <code>const unsigned char[channels]</code>: Mapping from
|
|
||||||
* coded channels to output channels, as described in
|
|
||||||
* @ref opus_multistream.
|
|
||||||
* @returns #OPUS_OK on success, or an error code (see @ref opus_errorcodes)
|
|
||||||
* on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_multistream_decoder_init(
|
|
||||||
OpusMSDecoder *st,
|
|
||||||
opus_int32 Fs,
|
|
||||||
int channels,
|
|
||||||
int streams,
|
|
||||||
int coupled_streams,
|
|
||||||
const unsigned char *mapping
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(6);
|
|
||||||
|
|
||||||
/** Decode a multistream Opus packet.
|
|
||||||
* @param st <tt>OpusMSDecoder*</tt>: Multistream decoder state.
|
|
||||||
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
|
||||||
* Use a <code>NULL</code>
|
|
||||||
* pointer to indicate packet
|
|
||||||
* loss.
|
|
||||||
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
|
||||||
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
|
||||||
* samples.
|
|
||||||
* This must contain room for
|
|
||||||
* <code>frame_size*channels</code>
|
|
||||||
* samples.
|
|
||||||
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
|
||||||
* available space in \a pcm.
|
|
||||||
* If this is less than the maximum packet duration
|
|
||||||
* (120 ms; 5760 for 48kHz), this function will not be capable
|
|
||||||
* of decoding some packets. In the case of PLC (data==NULL)
|
|
||||||
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
|
||||||
* the duration of audio that is missing, otherwise the
|
|
||||||
* decoder will not be in the optimal state to decode the
|
|
||||||
* next incoming packet. For the PLC and FEC cases, frame_size
|
|
||||||
* <b>must</b> be a multiple of 2.5 ms.
|
|
||||||
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
|
||||||
* forward error correction data be decoded.
|
|
||||||
* If no such data is available, the frame is
|
|
||||||
* decoded as if it were lost.
|
|
||||||
* @returns Number of samples decoded on success or a negative error code
|
|
||||||
* (see @ref opus_errorcodes) on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode(
|
|
||||||
OpusMSDecoder *st,
|
|
||||||
const unsigned char *data,
|
|
||||||
opus_int32 len,
|
|
||||||
opus_int16 *pcm,
|
|
||||||
int frame_size,
|
|
||||||
int decode_fec
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Decode a multistream Opus packet with floating point output.
|
|
||||||
* @param st <tt>OpusMSDecoder*</tt>: Multistream decoder state.
|
|
||||||
* @param[in] data <tt>const unsigned char*</tt>: Input payload.
|
|
||||||
* Use a <code>NULL</code>
|
|
||||||
* pointer to indicate packet
|
|
||||||
* loss.
|
|
||||||
* @param len <tt>opus_int32</tt>: Number of bytes in payload.
|
|
||||||
* @param[out] pcm <tt>opus_int16*</tt>: Output signal, with interleaved
|
|
||||||
* samples.
|
|
||||||
* This must contain room for
|
|
||||||
* <code>frame_size*channels</code>
|
|
||||||
* samples.
|
|
||||||
* @param frame_size <tt>int</tt>: The number of samples per channel of
|
|
||||||
* available space in \a pcm.
|
|
||||||
* If this is less than the maximum packet duration
|
|
||||||
* (120 ms; 5760 for 48kHz), this function will not be capable
|
|
||||||
* of decoding some packets. In the case of PLC (data==NULL)
|
|
||||||
* or FEC (decode_fec=1), then frame_size needs to be exactly
|
|
||||||
* the duration of audio that is missing, otherwise the
|
|
||||||
* decoder will not be in the optimal state to decode the
|
|
||||||
* next incoming packet. For the PLC and FEC cases, frame_size
|
|
||||||
* <b>must</b> be a multiple of 2.5 ms.
|
|
||||||
* @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
|
|
||||||
* forward error correction data be decoded.
|
|
||||||
* If no such data is available, the frame is
|
|
||||||
* decoded as if it were lost.
|
|
||||||
* @returns Number of samples decoded on success or a negative error code
|
|
||||||
* (see @ref opus_errorcodes) on failure.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode_float(
|
|
||||||
OpusMSDecoder *st,
|
|
||||||
const unsigned char *data,
|
|
||||||
opus_int32 len,
|
|
||||||
float *pcm,
|
|
||||||
int frame_size,
|
|
||||||
int decode_fec
|
|
||||||
) OPUS_ARG_NONNULL(1) OPUS_ARG_NONNULL(4);
|
|
||||||
|
|
||||||
/** Perform a CTL function on a multistream Opus decoder.
|
|
||||||
*
|
|
||||||
* Generally the request and subsequent arguments are generated by a
|
|
||||||
* convenience macro.
|
|
||||||
* @param st <tt>OpusMSDecoder*</tt>: Multistream decoder state.
|
|
||||||
* @param request This and all remaining parameters should be replaced by one
|
|
||||||
* of the convenience macros in @ref opus_genericctls,
|
|
||||||
* @ref opus_decoderctls, or @ref opus_multistream_ctls.
|
|
||||||
* @see opus_genericctls
|
|
||||||
* @see opus_decoderctls
|
|
||||||
* @see opus_multistream_ctls
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...) OPUS_ARG_NONNULL(1);
|
|
||||||
|
|
||||||
/** Frees an <code>OpusMSDecoder</code> allocated by
|
|
||||||
* opus_multistream_decoder_create().
|
|
||||||
* @param st <tt>OpusMSDecoder</tt>: Multistream decoder state to be freed.
|
|
||||||
*/
|
|
||||||
OPUS_EXPORT void opus_multistream_decoder_destroy(OpusMSDecoder *st);
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* OPUS_MULTISTREAM_H */
|
|
@ -1,159 +0,0 @@
|
|||||||
/* (C) COPYRIGHT 1994-2002 Xiph.Org Foundation */
|
|
||||||
/* Modified by Jean-Marc Valin */
|
|
||||||
/*
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions
|
|
||||||
are met:
|
|
||||||
|
|
||||||
- Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
- Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
||||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
/* opus_types.h based on ogg_types.h from libogg */
|
|
||||||
|
|
||||||
/**
|
|
||||||
@file opus_types.h
|
|
||||||
@brief Opus reference implementation types
|
|
||||||
*/
|
|
||||||
#ifndef OPUS_TYPES_H
|
|
||||||
#define OPUS_TYPES_H
|
|
||||||
|
|
||||||
/* Use the real stdint.h if it's there (taken from Paul Hsieh's pstdint.h) */
|
|
||||||
#if (defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) || defined (HAVE_STDINT_H))
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef int16_t opus_int16;
|
|
||||||
typedef uint16_t opus_uint16;
|
|
||||||
typedef int32_t opus_int32;
|
|
||||||
typedef uint32_t opus_uint32;
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
|
|
||||||
# if defined(__CYGWIN__)
|
|
||||||
# include <_G_config.h>
|
|
||||||
typedef _G_int32_t opus_int32;
|
|
||||||
typedef _G_uint32_t opus_uint32;
|
|
||||||
typedef _G_int16 opus_int16;
|
|
||||||
typedef _G_uint16 opus_uint16;
|
|
||||||
# elif defined(__MINGW32__)
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
# elif defined(__MWERKS__)
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
# else
|
|
||||||
/* MSVC/Borland */
|
|
||||||
typedef __int32 opus_int32;
|
|
||||||
typedef unsigned __int32 opus_uint32;
|
|
||||||
typedef __int16 opus_int16;
|
|
||||||
typedef unsigned __int16 opus_uint16;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
#elif defined(__MACOS__)
|
|
||||||
|
|
||||||
# include <sys/types.h>
|
|
||||||
typedef SInt16 opus_int16;
|
|
||||||
typedef UInt16 opus_uint16;
|
|
||||||
typedef SInt32 opus_int32;
|
|
||||||
typedef UInt32 opus_uint32;
|
|
||||||
|
|
||||||
#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */
|
|
||||||
|
|
||||||
# include <sys/types.h>
|
|
||||||
typedef int16_t opus_int16;
|
|
||||||
typedef u_int16_t opus_uint16;
|
|
||||||
typedef int32_t opus_int32;
|
|
||||||
typedef u_int32_t opus_uint32;
|
|
||||||
|
|
||||||
#elif defined(__BEOS__)
|
|
||||||
|
|
||||||
/* Be */
|
|
||||||
# include <inttypes.h>
|
|
||||||
typedef int16 opus_int16;
|
|
||||||
typedef u_int16 opus_uint16;
|
|
||||||
typedef int32_t opus_int32;
|
|
||||||
typedef u_int32_t opus_uint32;
|
|
||||||
|
|
||||||
#elif defined (__EMX__)
|
|
||||||
|
|
||||||
/* OS/2 GCC */
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
|
|
||||||
#elif defined (DJGPP)
|
|
||||||
|
|
||||||
/* DJGPP */
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
|
|
||||||
#elif defined(R5900)
|
|
||||||
|
|
||||||
/* PS2 EE */
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned opus_uint32;
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
|
|
||||||
#elif defined(__SYMBIAN32__)
|
|
||||||
|
|
||||||
/* Symbian GCC */
|
|
||||||
typedef signed short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef signed int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
|
|
||||||
#elif defined(CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
|
|
||||||
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef long opus_int32;
|
|
||||||
typedef unsigned long opus_uint32;
|
|
||||||
|
|
||||||
#elif defined(CONFIG_TI_C6X)
|
|
||||||
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/* Give up, take a reasonable guess */
|
|
||||||
typedef short opus_int16;
|
|
||||||
typedef unsigned short opus_uint16;
|
|
||||||
typedef int opus_int32;
|
|
||||||
typedef unsigned int opus_uint32;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define opus_int int /* used for counters etc; at least 16 bits */
|
|
||||||
#define opus_int64 long long
|
|
||||||
#define opus_int8 signed char
|
|
||||||
|
|
||||||
#define opus_uint unsigned int /* used for counters etc; at least 16 bits */
|
|
||||||
#define opus_uint64 unsigned long long
|
|
||||||
#define opus_uint8 unsigned char
|
|
||||||
|
|
||||||
#endif /* OPUS_TYPES_H */
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,42 +0,0 @@
|
|||||||
#include <stdlib.h>
|
|
||||||
#include <opus_multistream.h>
|
|
||||||
#include "nv_opus_dec.h"
|
|
||||||
|
|
||||||
OpusMSDecoder* decoder;
|
|
||||||
|
|
||||||
// This function must be called before
|
|
||||||
// any other decoding functions
|
|
||||||
int nv_opus_init(int sampleRate, int channelCount, int streams,
|
|
||||||
int coupledStreams, const unsigned char *mapping) {
|
|
||||||
int err;
|
|
||||||
decoder = opus_multistream_decoder_create(
|
|
||||||
sampleRate,
|
|
||||||
channelCount,
|
|
||||||
streams,
|
|
||||||
coupledStreams,
|
|
||||||
mapping,
|
|
||||||
&err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function must be called after
|
|
||||||
// decoding is finished
|
|
||||||
void nv_opus_destroy(void) {
|
|
||||||
if (decoder != NULL) {
|
|
||||||
opus_multistream_decoder_destroy(decoder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// packets must be decoded in order
|
|
||||||
// a packet loss must call this function with NULL indata and 0 inlen
|
|
||||||
// returns the number of decoded samples
|
|
||||||
int nv_opus_decode(unsigned char* indata, int inlen, short* outpcmdata, int framesize) {
|
|
||||||
int err;
|
|
||||||
|
|
||||||
// Decoding to 16-bit PCM with FEC off
|
|
||||||
// Maximum length assuming 48KHz sample rate
|
|
||||||
err = opus_multistream_decode(decoder, indata, inlen,
|
|
||||||
outpcmdata, framesize, 0);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
int nv_opus_init(int sampleRate, int channelCount, int streams,
|
|
||||||
int coupledStreams, const unsigned char *mapping);
|
|
||||||
void nv_opus_destroy(void);
|
|
||||||
int nv_opus_decode(unsigned char* indata, int inlen, short* outpcmdata, int framesize);
|
|
@ -1,71 +0,0 @@
|
|||||||
#include "nv_opus_dec.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <jni.h>
|
|
||||||
|
|
||||||
static int SamplesPerChannel;
|
|
||||||
static int ChannelCount;
|
|
||||||
|
|
||||||
// This function must be called before
|
|
||||||
// any other decoding functions
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_com_limelight_nvstream_av_audio_OpusDecoder_init(JNIEnv *env, jobject this, int sampleRate,
|
|
||||||
int samplesPerChannel, int channelCount, int streams,
|
|
||||||
int coupledStreams, jbyteArray mapping) {
|
|
||||||
jbyte* jni_mapping_data;
|
|
||||||
jint ret;
|
|
||||||
|
|
||||||
SamplesPerChannel = samplesPerChannel;
|
|
||||||
ChannelCount = channelCount;
|
|
||||||
|
|
||||||
jni_mapping_data = (*env)->GetByteArrayElements(env, mapping, 0);
|
|
||||||
ret = nv_opus_init(sampleRate, channelCount, streams, coupledStreams,
|
|
||||||
(const unsigned char*)jni_mapping_data);
|
|
||||||
(*env)->ReleaseByteArrayElements(env, mapping, jni_mapping_data, JNI_ABORT);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function must be called after
|
|
||||||
// decoding is finished
|
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_com_limelight_nvstream_av_audio_OpusDecoder_destroy(JNIEnv *env, jobject this) {
|
|
||||||
nv_opus_destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// packets must be decoded in order
|
|
||||||
// a packet loss must call this function with NULL indata and 0 inlen
|
|
||||||
// returns the number of decoded bytes
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_com_limelight_nvstream_av_audio_OpusDecoder_decode(
|
|
||||||
JNIEnv *env, jobject this, // JNI parameters
|
|
||||||
jbyteArray indata, jint inoff, jint inlen, // Input parameters
|
|
||||||
jbyteArray outpcmdata) // Output parameter
|
|
||||||
{
|
|
||||||
jint ret;
|
|
||||||
jbyte* jni_input_data;
|
|
||||||
jbyte* jni_pcm_data;
|
|
||||||
|
|
||||||
jni_pcm_data = (*env)->GetByteArrayElements(env, outpcmdata, 0);
|
|
||||||
if (indata != NULL) {
|
|
||||||
jni_input_data = (*env)->GetByteArrayElements(env, indata, 0);
|
|
||||||
|
|
||||||
ret = nv_opus_decode((unsigned char*)&jni_input_data[inoff], inlen,
|
|
||||||
(jshort*)jni_pcm_data, SamplesPerChannel);
|
|
||||||
|
|
||||||
// The input data isn't changed so it can be safely aborted
|
|
||||||
(*env)->ReleaseByteArrayElements(env, indata, jni_input_data, JNI_ABORT);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ret = nv_opus_decode(NULL, 0, (jshort*)jni_pcm_data, SamplesPerChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert samples (2 bytes) per channel to total bytes returned
|
|
||||||
if (ret > 0) {
|
|
||||||
ret *= ChannelCount * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->ReleaseByteArrayElements(env, outpcmdata, jni_pcm_data, 0);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
@ -50,6 +50,7 @@
|
|||||||
android:layout_height="65dp"
|
android:layout_height="65dp"
|
||||||
android:cropToPadding="false"
|
android:cropToPadding="false"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
|
android:nextFocusForward="@id/helpButton"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
@ -62,6 +63,7 @@
|
|||||||
android:layout_height="65dp"
|
android:layout_height="65dp"
|
||||||
android:cropToPadding="false"
|
android:cropToPadding="false"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
|
android:nextFocusForward="@id/manuallyAddPc"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_below="@id/settingsButton"
|
android:layout_below="@id/settingsButton"
|
||||||
|
@ -10,5 +10,6 @@
|
|||||||
android:columnWidth="160dp"
|
android:columnWidth="160dp"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
|
android:nextFocusLeft="@id/settingsButton"
|
||||||
android:gravity="center"/>
|
android:gravity="center"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -10,5 +10,6 @@
|
|||||||
android:columnWidth="105dp"
|
android:columnWidth="105dp"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
|
android:nextFocusLeft="@id/settingsButton"
|
||||||
android:gravity="center"/>
|
android:gravity="center"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -4,7 +4,7 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.3.1'
|
classpath 'com.android.tools.build:gradle:2.3.2'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
moonlight-common
Submodule
1
moonlight-common
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 4c2dc16aefbb516da42399c0fb06bff19a2629e6
|
@ -1 +1 @@
|
|||||||
include ':app'
|
include ':app', ':moonlight-common'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user