Copy vp9 extension v1 -> v2
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=119748182
This commit is contained in:
parent
51df2dce46
commit
ba7b1b7bf1
81
extensions/vp9/README.md
Normal file
81
extensions/vp9/README.md
Normal file
@ -0,0 +1,81 @@
|
||||
# ExoPlayer VP9 Extension #
|
||||
|
||||
## Description ##
|
||||
|
||||
The VP9 Extension is a [TrackRenderer][] implementation that helps you bundle
|
||||
libvpx (the VP9 decoding library) into your app and use it along with ExoPlayer
|
||||
to play VP9 video on Android devices.
|
||||
|
||||
[TrackRenderer]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/TrackRenderer.html
|
||||
|
||||
## Build Instructions ##
|
||||
|
||||
* Checkout ExoPlayer along with Extensions:
|
||||
|
||||
```
|
||||
git clone https://github.com/google/ExoPlayer.git
|
||||
```
|
||||
|
||||
* Set the following environment variables:
|
||||
|
||||
```
|
||||
cd "<path to exoplayer checkout>"
|
||||
EXOPLAYER_ROOT="$(pwd)"
|
||||
VP9_EXT_PATH="${EXOPLAYER_ROOT}/extensions/vp9/src/main"
|
||||
```
|
||||
|
||||
* Download the [Android NDK][] and set its location in an environment variable:
|
||||
|
||||
```
|
||||
NDK_PATH="<path to Android NDK>"
|
||||
```
|
||||
|
||||
* Fetch libvpx and libyuv:
|
||||
|
||||
```
|
||||
cd "${VP9_EXT_PATH}/jni" && \
|
||||
git clone https://chromium.googlesource.com/webm/libvpx libvpx && \
|
||||
git clone https://chromium.googlesource.com/libyuv/libyuv libyuv
|
||||
```
|
||||
|
||||
* Run a script that generates necessary configuration files for libvpx:
|
||||
|
||||
```
|
||||
cd ${VP9_EXT_PATH}/jni && \
|
||||
./generate_libvpx_android_configs.sh "${NDK_PATH}"
|
||||
```
|
||||
|
||||
* Build the JNI native libraries from the command line:
|
||||
|
||||
```
|
||||
cd "${VP9_EXT_PATH}"/jni && \
|
||||
${NDK_PATH}/ndk-build APP_ABI=all -j4
|
||||
```
|
||||
|
||||
* In your project, you can add a dependency to the VP9 Extension by using a the
|
||||
following rule:
|
||||
|
||||
```
|
||||
// in settings.gradle
|
||||
include ':..:ExoPlayer:library'
|
||||
include ':..:ExoPlayer:extension-vp9'
|
||||
|
||||
// in build.gradle
|
||||
dependencies {
|
||||
compile project(':..:ExoPlayer:library')
|
||||
compile project(':..:ExoPlayer:extension-vp9')
|
||||
}
|
||||
```
|
||||
|
||||
* Now, when you build your app, the VP9 extension will be built and the native
|
||||
libraries will be packaged along with the APK.
|
||||
|
||||
## Notes ##
|
||||
|
||||
* Every time there is a change to the libvpx checkout:
|
||||
* Android config scripts should be re-generated by running
|
||||
`generate_libvpx_android_configs.sh`
|
||||
* Clean and re-build the project.
|
||||
* If you want to use your own version of libvpx or libyuv, place it in
|
||||
`${VP9_EXT_PATH}/jni/libvpx` or `${VP9_EXT_PATH}/jni/libyuv` respectively.
|
||||
|
45
extensions/vp9/build.gradle
Normal file
45
extensions/vp9/build.gradle
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2014 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.1"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 23
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
jniLibs.srcDir 'src/main/libs'
|
||||
jni.srcDirs = [] // Disable the automatic ndk-build call by Android Studio.
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':library')
|
||||
}
|
||||
|
10
extensions/vp9/src/androidTest/.classpath
Normal file
10
extensions/vp9/src/androidTest/.classpath
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="src" path="java"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/ExoPlayerExt-VP9"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
45
extensions/vp9/src/androidTest/.project
Normal file
45
extensions/vp9/src/androidTest/.project
Normal file
@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ExoPlayerExt-VP9Tests</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
<project>ExoPlayerLib</project>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
<filteredResources>
|
||||
<filter>
|
||||
<id>0</id>
|
||||
<name></name>
|
||||
<type>14</type>
|
||||
<matcher>
|
||||
<id>org.eclipse.ui.ide.multiFilter</id>
|
||||
<arguments>1.0-name-matches-true-false-BUILD</arguments>
|
||||
</matcher>
|
||||
</filter>
|
||||
</filteredResources>
|
||||
</projectDescription>
|
34
extensions/vp9/src/androidTest/AndroidManifest.xml
Normal file
34
extensions/vp9/src/androidTest/AndroidManifest.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.google.android.exoplayer.ext.vp9.test">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||
|
||||
<application android:debuggable="true"
|
||||
android:allowBackup="false"
|
||||
tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
|
||||
<uses-library android:name="android.test.runner"/>
|
||||
</application>
|
||||
|
||||
<instrumentation
|
||||
android:targetPackage="com.google.android.exoplayer.ext.vp9.test"
|
||||
android:name="android.test.InstrumentationTestRunner"
|
||||
tools:replace="android:targetPackage"/>
|
||||
|
||||
</manifest>
|
Binary file not shown.
BIN
extensions/vp9/src/androidTest/assets/bear-vp9.webm
Normal file
BIN
extensions/vp9/src/androidTest/assets/bear-vp9.webm
Normal file
Binary file not shown.
BIN
extensions/vp9/src/androidTest/assets/invalid-bitstream.webm
Normal file
BIN
extensions/vp9/src/androidTest/assets/invalid-bitstream.webm
Normal file
Binary file not shown.
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
|
||||
import com.google.android.exoplayer.extractor.mkv.MatroskaExtractor;
|
||||
import com.google.android.exoplayer.upstream.DefaultAllocator;
|
||||
import com.google.android.exoplayer.upstream.DefaultDataSource;
|
||||
import com.google.android.exoplayer.util.Util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Looper;
|
||||
import android.test.InstrumentationTestCase;
|
||||
|
||||
/**
|
||||
* Playback tests using {@link LibvpxVideoTrackRenderer}.
|
||||
*/
|
||||
public class VpxPlaybackTest extends InstrumentationTestCase {
|
||||
|
||||
private static final String BEAR_URI = "asset:///bear-vp9.webm";
|
||||
private static final String BEAR_ODD_DIMENSIONS_URI = "asset:///bear-vp9-odd-dimensions.webm";
|
||||
private static final String INVALID_BITSTREAM_URI = "asset:///invalid-bitstream.webm";
|
||||
|
||||
public void testBasicPlayback() throws ExoPlaybackException {
|
||||
playUri(BEAR_URI);
|
||||
}
|
||||
|
||||
public void testOddDimensionsPlayback() throws ExoPlaybackException {
|
||||
playUri(BEAR_ODD_DIMENSIONS_URI);
|
||||
}
|
||||
|
||||
public void testInvalidBitstream() {
|
||||
try {
|
||||
playUri(INVALID_BITSTREAM_URI);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertNotNull(e.getCause());
|
||||
assertTrue(e.getCause() instanceof VpxDecoderException);
|
||||
}
|
||||
}
|
||||
|
||||
private void playUri(String uri) throws ExoPlaybackException {
|
||||
TestPlaybackThread thread = new TestPlaybackThread(Uri.parse(uri),
|
||||
getInstrumentation().getContext());
|
||||
thread.start();
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
fail(); // Should never happen.
|
||||
}
|
||||
if (thread.playbackException != null) {
|
||||
throw thread.playbackException;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TestPlaybackThread extends Thread implements ExoPlayer.Listener {
|
||||
|
||||
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
|
||||
private static final int BUFFER_SEGMENT_COUNT = 16;
|
||||
|
||||
private final Context context;
|
||||
private final Uri uri;
|
||||
|
||||
private ExoPlayer player;
|
||||
private ExoPlaybackException playbackException;
|
||||
|
||||
public TestPlaybackThread(Uri uri, Context context) {
|
||||
this.uri = uri;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(true);
|
||||
DefaultTrackSelector trackSelector = new DefaultTrackSelector(null, null);
|
||||
player = ExoPlayer.Factory.newInstance(new TrackRenderer[] {videoRenderer}, trackSelector);
|
||||
player.addListener(this);
|
||||
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
|
||||
uri,
|
||||
new DefaultDataSource(context, null, Util.getUserAgent(context, "ExoPlayerExtVP9Test"),
|
||||
false),
|
||||
new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT,
|
||||
new MatroskaExtractor());
|
||||
player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER,
|
||||
new VpxVideoSurfaceView(context));
|
||||
player.prepare(sampleSource);
|
||||
player.setPlayWhenReady(true);
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayWhenReadyCommitted () {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(ExoPlaybackException error) {
|
||||
playbackException = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
|
||||
if (playbackState == ExoPlayer.STATE_ENDED
|
||||
|| (playbackState == ExoPlayer.STATE_IDLE && playbackException != null)) {
|
||||
releasePlayerAndQuitLooper();
|
||||
}
|
||||
}
|
||||
|
||||
private void releasePlayerAndQuitLooper() {
|
||||
player.release();
|
||||
Looper.myLooper().quit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
14
extensions/vp9/src/androidTest/project.properties
Normal file
14
extensions/vp9/src/androidTest/project.properties
Normal file
@ -0,0 +1,14 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-23
|
2
extensions/vp9/src/androidTest/res/.README.txt
Normal file
2
extensions/vp9/src/androidTest/res/.README.txt
Normal file
@ -0,0 +1,2 @@
|
||||
This file is needed to make sure the res directory is present.
|
||||
The file is ignored by the Android toolchain because its name starts with a dot.
|
10
extensions/vp9/src/main/.classpath
Normal file
10
extensions/vp9/src/main/.classpath
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="java"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="src" path="/ExoPlayerLib"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
57
extensions/vp9/src/main/.cproject
Normal file
57
extensions/vp9/src/main/.cproject
Normal file
@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
<cconfiguration id="com.android.toolchain.gcc.367693784">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.android.toolchain.gcc.367693784" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||
<externalSettings/>
|
||||
<extensions>
|
||||
<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactName="${ProjName}" buildProperties="" description="" id="com.android.toolchain.gcc.367693784" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||
<folderInfo id="com.android.toolchain.gcc.367693784.1582606005" name="/" resourcePath="">
|
||||
<toolChain id="com.android.toolchain.gcc.2090539093" name="Android GCC" superClass="com.android.toolchain.gcc">
|
||||
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="com.android.targetPlatform.1021581688" isAbstract="false" superClass="com.android.targetPlatform"/>
|
||||
<builder buildPath="${workspace_loc:/ExoPlayerExt-VP9}/jni" id="com.android.builder.1955717109" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Android Builder" superClass="com.android.builder">
|
||||
<outputEntries>
|
||||
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="obj"/>
|
||||
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="libs"/>
|
||||
</outputEntries>
|
||||
</builder>
|
||||
<tool id="com.android.gcc.compiler.162335776" name="Android GCC Compiler" superClass="com.android.gcc.compiler">
|
||||
<inputType id="com.android.gcc.inputType.78164988" superClass="com.android.gcc.inputType"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
<sourceEntries>
|
||||
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="jni"/>
|
||||
</sourceEntries>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<project id="ExoPlayerExt-VP9.null.410683598" name="ExoPlayerExt-VP9"/>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
<storageModule moduleId="scannerConfiguration">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
<scannerConfigBuildInfo instanceId="com.android.toolchain.gcc.367693784;com.android.toolchain.gcc.367693784.1582606005;com.android.gcc.compiler.162335776;com.android.gcc.inputType.78164988">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="com.android.AndroidPerProjectProfile"/>
|
||||
</scannerConfigBuildInfo>
|
||||
</storageModule>
|
||||
<storageModule moduleId="refreshScope" versionNumber="2">
|
||||
<configuration configurationName="Default">
|
||||
<resource resourceType="PROJECT" workspacePath="/ExoPlayerExt-VP9"/>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
</cproject>
|
97
extensions/vp9/src/main/.project
Normal file
97
extensions/vp9/src/main/.project
Normal file
@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>ExoPlayerExt-VP9</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>?children?</key>
|
||||
<value>?name?=outputEntries\|?children?=?name?=entry\\\\\\\|\\\|?name?=entry\\\\\\\|\\\|\||</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>?name?</key>
|
||||
<value></value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.append_environment</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.buildArguments</key>
|
||||
<value></value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.buildCommand</key>
|
||||
<value>ndk-build</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
|
||||
<value>clean</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.contents</key>
|
||||
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
|
||||
<value>false</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.stopOnError</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
12
extensions/vp9/src/main/.settings/org.eclipse.jdt.core.prefs
Normal file
12
extensions/vp9/src/main/.settings/org.eclipse.jdt.core.prefs
Normal file
@ -0,0 +1,12 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
23
extensions/vp9/src/main/AndroidManifest.xml
Normal file
23
extensions/vp9/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.google.android.exoplayer.ext.vp9">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
|
||||
<uses-feature android:glEsVersion="0x00020000"/>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,520 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer.CodecCounters;
|
||||
import com.google.android.exoplayer.ExoPlaybackException;
|
||||
import com.google.android.exoplayer.ExoPlayer;
|
||||
import com.google.android.exoplayer.Format;
|
||||
import com.google.android.exoplayer.FormatHolder;
|
||||
import com.google.android.exoplayer.TrackRenderer;
|
||||
import com.google.android.exoplayer.TrackStream;
|
||||
import com.google.android.exoplayer.util.MimeTypes;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.view.Surface;
|
||||
|
||||
/**
|
||||
* Decodes and renders video using the native VP9 decoder.
|
||||
*/
|
||||
public final class LibvpxVideoTrackRenderer extends TrackRenderer {
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be notified of {@link LibvpxVideoTrackRenderer} events.
|
||||
*/
|
||||
public interface EventListener {
|
||||
|
||||
/**
|
||||
* Invoked to report the number of frames dropped by the renderer. Dropped frames are reported
|
||||
* whenever the renderer is stopped having dropped frames, and optionally, whenever the count
|
||||
* reaches a specified threshold whilst the renderer is started.
|
||||
*
|
||||
* @param count The number of dropped frames.
|
||||
* @param elapsed The duration in milliseconds over which the frames were dropped. This
|
||||
* duration is timed from when the renderer was started or from when dropped frames were
|
||||
* last reported (whichever was more recent), and not from when the first of the reported
|
||||
* drops occurred.
|
||||
*/
|
||||
void onDroppedFrames(int count, long elapsed);
|
||||
|
||||
/**
|
||||
* Invoked each time there's a change in the size of the video being rendered.
|
||||
*
|
||||
* @param width The video width in pixels.
|
||||
* @param height The video height in pixels.
|
||||
*/
|
||||
void onVideoSizeChanged(int width, int height);
|
||||
|
||||
/**
|
||||
* Invoked when a frame is rendered to a surface for the first time following that surface
|
||||
* having been set as the target for the renderer.
|
||||
*
|
||||
* @param surface The surface to which a first frame has been rendered.
|
||||
*/
|
||||
void onDrawnToSurface(Surface surface);
|
||||
|
||||
/**
|
||||
* Invoked when one of the following happens: libvpx initialization failure, decoder error,
|
||||
* renderer error.
|
||||
*
|
||||
* @param e The corresponding exception.
|
||||
*/
|
||||
void onDecoderError(VpxDecoderException e);
|
||||
|
||||
/**
|
||||
* Invoked when a decoder is successfully created.
|
||||
*
|
||||
* @param decoderName The decoder that was configured and created.
|
||||
* @param elapsedRealtimeMs {@code elapsedRealtime} timestamp of when the initialization
|
||||
* finished.
|
||||
* @param initializationDurationMs Amount of time taken to initialize the decoder.
|
||||
*/
|
||||
void onDecoderInitialized(String decoderName, long elapsedRealtimeMs,
|
||||
long initializationDurationMs);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of a message that can be passed to an instance of this class via
|
||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||
* should be the target {@link Surface}, or null.
|
||||
*/
|
||||
public static final int MSG_SET_SURFACE = 1;
|
||||
/**
|
||||
* The type of a message that can be passed to an instance of this class via
|
||||
* {@link ExoPlayer#sendMessage} or {@link ExoPlayer#blockingSendMessage}. The message object
|
||||
* should be the target {@link VpxOutputBufferRenderer}, or null.
|
||||
*/
|
||||
public static final int MSG_SET_OUTPUT_BUFFER_RENDERER = 2;
|
||||
|
||||
/**
|
||||
* The number of input buffers and the number of output buffers. The track renderer may limit the
|
||||
* minimum possible value due to requiring multiple output buffers to be dequeued at a time for it
|
||||
* to make progress.
|
||||
*/
|
||||
private static final int NUM_BUFFERS = 16;
|
||||
private static final int INITIAL_INPUT_BUFFER_SIZE = 768 * 1024; // Value based on cs/SoftVpx.cpp.
|
||||
|
||||
public final CodecCounters codecCounters = new CodecCounters();
|
||||
|
||||
private final boolean scaleToFit;
|
||||
private final Handler eventHandler;
|
||||
private final EventListener eventListener;
|
||||
private final int maxDroppedFrameCountToNotify;
|
||||
private final FormatHolder formatHolder;
|
||||
|
||||
private Format format;
|
||||
private VpxDecoder decoder;
|
||||
private VpxInputBuffer inputBuffer;
|
||||
private VpxOutputBuffer outputBuffer;
|
||||
private VpxOutputBuffer nextOutputBuffer;
|
||||
|
||||
private Bitmap bitmap;
|
||||
private boolean drawnToSurface;
|
||||
private boolean renderedFirstFrame;
|
||||
private Surface surface;
|
||||
private VpxOutputBufferRenderer outputBufferRenderer;
|
||||
private int outputMode;
|
||||
|
||||
private boolean inputStreamEnded;
|
||||
private boolean outputStreamEnded;
|
||||
private int previousWidth;
|
||||
private int previousHeight;
|
||||
|
||||
private int droppedFrameCount;
|
||||
private long droppedFrameAccumulationStartTimeMs;
|
||||
|
||||
/**
|
||||
* @param scaleToFit Boolean that indicates if video frames should be scaled to fit when
|
||||
* rendering.
|
||||
*/
|
||||
public LibvpxVideoTrackRenderer(boolean scaleToFit) {
|
||||
this(scaleToFit, null, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param scaleToFit Boolean that indicates if video frames should be scaled to fit when
|
||||
* rendering.
|
||||
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
|
||||
* null if delivery of events is not required.
|
||||
* @param eventListener A listener of events. May be null if delivery of events is not required.
|
||||
* @param maxDroppedFrameCountToNotify The maximum number of frames that can be dropped between
|
||||
* invocations of {@link EventListener#onDroppedFrames(int, long)}.
|
||||
*/
|
||||
public LibvpxVideoTrackRenderer(boolean scaleToFit, Handler eventHandler,
|
||||
EventListener eventListener, int maxDroppedFrameCountToNotify) {
|
||||
this.scaleToFit = scaleToFit;
|
||||
this.eventHandler = eventHandler;
|
||||
this.eventListener = eventListener;
|
||||
this.maxDroppedFrameCountToNotify = maxDroppedFrameCountToNotify;
|
||||
previousWidth = -1;
|
||||
previousHeight = -1;
|
||||
formatHolder = new FormatHolder();
|
||||
outputMode = VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying libvpx library is available.
|
||||
*/
|
||||
public static boolean isLibvpxAvailable() {
|
||||
return VpxDecoder.IS_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the underlying libvpx library if available, otherwise {@code null}.
|
||||
*/
|
||||
public static String getLibvpxVersion() {
|
||||
return isLibvpxAvailable() ? VpxDecoder.getLibvpxVersion() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int supportsFormat(Format format) {
|
||||
return MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)
|
||||
? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void render(long positionUs, long elapsedRealtimeUs) throws ExoPlaybackException {
|
||||
if (outputStreamEnded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Try and read a format if we don't have one already.
|
||||
if (format == null && !readFormat()) {
|
||||
// We can't make progress without one.
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (decoder == null) {
|
||||
// If we don't have a decoder yet, we need to instantiate one.
|
||||
long startElapsedRealtimeMs = SystemClock.elapsedRealtime();
|
||||
decoder = new VpxDecoder(NUM_BUFFERS, NUM_BUFFERS, INITIAL_INPUT_BUFFER_SIZE);
|
||||
decoder.setOutputMode(outputMode);
|
||||
decoder.start();
|
||||
notifyDecoderInitialized(startElapsedRealtimeMs, SystemClock.elapsedRealtime());
|
||||
codecCounters.codecInitCount++;
|
||||
}
|
||||
while (processOutputBuffer(positionUs)) {}
|
||||
while (feedInputBuffer()) {}
|
||||
} catch (VpxDecoderException e) {
|
||||
notifyDecoderError(e);
|
||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||
}
|
||||
codecCounters.ensureUpdated();
|
||||
}
|
||||
|
||||
private boolean processOutputBuffer(long positionUs)
|
||||
throws VpxDecoderException {
|
||||
if (outputStreamEnded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Acquire outputBuffer either from nextOutputBuffer or from the decoder.
|
||||
if (outputBuffer == null) {
|
||||
if (nextOutputBuffer != null) {
|
||||
outputBuffer = nextOutputBuffer;
|
||||
nextOutputBuffer = null;
|
||||
} else {
|
||||
outputBuffer = decoder.dequeueOutputBuffer();
|
||||
}
|
||||
if (outputBuffer == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextOutputBuffer == null) {
|
||||
nextOutputBuffer = decoder.dequeueOutputBuffer();
|
||||
}
|
||||
|
||||
if (outputBuffer.isEndOfStream()) {
|
||||
outputStreamEnded = true;
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Drop frame only if we have the next frame and that's also late, otherwise render whatever we
|
||||
// have.
|
||||
if (nextOutputBuffer != null && nextOutputBuffer.timestampUs < positionUs) {
|
||||
// Drop frame if we are too late.
|
||||
codecCounters.droppedOutputBufferCount++;
|
||||
droppedFrameCount++;
|
||||
if (droppedFrameCount == maxDroppedFrameCountToNotify) {
|
||||
notifyAndResetDroppedFrameCount();
|
||||
}
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we have not rendered any frame so far (either initially or immediately following a seek),
|
||||
// render one frame irrespective of the state or current position.
|
||||
if (!renderedFirstFrame) {
|
||||
renderBuffer();
|
||||
renderedFirstFrame = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getState() == TrackRenderer.STATE_STARTED
|
||||
&& outputBuffer.timestampUs <= positionUs + 30000) {
|
||||
renderBuffer();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void renderBuffer() {
|
||||
codecCounters.renderedOutputBufferCount++;
|
||||
notifyIfVideoSizeChanged(outputBuffer);
|
||||
if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_RGB && surface != null) {
|
||||
renderRgbFrame(outputBuffer, scaleToFit);
|
||||
if (!drawnToSurface) {
|
||||
drawnToSurface = true;
|
||||
notifyDrawnToSurface(surface);
|
||||
}
|
||||
outputBuffer.release();
|
||||
} else if (outputBuffer.mode == VpxDecoder.OUTPUT_MODE_YUV && outputBufferRenderer != null) {
|
||||
// The renderer will release the buffer.
|
||||
outputBufferRenderer.setOutputBuffer(outputBuffer);
|
||||
} else {
|
||||
outputBuffer.release();
|
||||
}
|
||||
outputBuffer = null;
|
||||
}
|
||||
|
||||
private void renderRgbFrame(VpxOutputBuffer outputBuffer, boolean scale) {
|
||||
if (bitmap == null || bitmap.getWidth() != outputBuffer.width
|
||||
|| bitmap.getHeight() != outputBuffer.height) {
|
||||
bitmap = Bitmap.createBitmap(outputBuffer.width, outputBuffer.height, Bitmap.Config.RGB_565);
|
||||
}
|
||||
bitmap.copyPixelsFromBuffer(outputBuffer.data);
|
||||
Canvas canvas = surface.lockCanvas(null);
|
||||
if (scale) {
|
||||
canvas.scale(((float) canvas.getWidth()) / outputBuffer.width,
|
||||
((float) canvas.getHeight()) / outputBuffer.height);
|
||||
}
|
||||
canvas.drawBitmap(bitmap, 0, 0, null);
|
||||
surface.unlockCanvasAndPost(canvas);
|
||||
}
|
||||
|
||||
private boolean feedInputBuffer() throws VpxDecoderException {
|
||||
if (inputStreamEnded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (inputBuffer == null) {
|
||||
inputBuffer = decoder.dequeueInputBuffer();
|
||||
if (inputBuffer == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int result = readSource(formatHolder, inputBuffer);
|
||||
if (result == TrackStream.NOTHING_READ) {
|
||||
return false;
|
||||
}
|
||||
if (result == TrackStream.FORMAT_READ) {
|
||||
format = formatHolder.format;
|
||||
return true;
|
||||
}
|
||||
if (inputBuffer.isEndOfStream()) {
|
||||
inputStreamEnded = true;
|
||||
} else {
|
||||
inputBuffer.width = format.width;
|
||||
inputBuffer.height = format.height;
|
||||
}
|
||||
decoder.queueInputBuffer(inputBuffer);
|
||||
inputBuffer = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void flushDecoder() {
|
||||
inputBuffer = null;
|
||||
if (outputBuffer != null) {
|
||||
outputBuffer.release();
|
||||
outputBuffer = null;
|
||||
}
|
||||
if (nextOutputBuffer != null) {
|
||||
nextOutputBuffer.release();
|
||||
nextOutputBuffer = null;
|
||||
}
|
||||
decoder.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isEnded() {
|
||||
return outputStreamEnded;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isReady() {
|
||||
return format != null && (isSourceReady() || outputBuffer != null) && renderedFirstFrame;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reset(long positionUs) {
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
renderedFirstFrame = false;
|
||||
if (decoder != null) {
|
||||
flushDecoder();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStarted() {
|
||||
droppedFrameCount = 0;
|
||||
droppedFrameAccumulationStartTimeMs = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStopped() {
|
||||
notifyAndResetDroppedFrameCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDisabled() {
|
||||
inputBuffer = null;
|
||||
outputBuffer = null;
|
||||
format = null;
|
||||
try {
|
||||
if (decoder != null) {
|
||||
decoder.release();
|
||||
decoder = null;
|
||||
codecCounters.codecReleaseCount++;
|
||||
}
|
||||
} finally {
|
||||
super.onDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean readFormat() {
|
||||
int result = readSource(formatHolder, null);
|
||||
if (result == TrackStream.FORMAT_READ) {
|
||||
format = formatHolder.format;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(int messageType, Object message) throws ExoPlaybackException {
|
||||
if (messageType == MSG_SET_SURFACE) {
|
||||
setSurface((Surface) message);
|
||||
} else if (messageType == MSG_SET_OUTPUT_BUFFER_RENDERER) {
|
||||
setOutputBufferRenderer((VpxOutputBufferRenderer) message);
|
||||
} else {
|
||||
super.handleMessage(messageType, message);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSurface(Surface surface) {
|
||||
if (this.surface == surface) {
|
||||
return;
|
||||
}
|
||||
this.surface = surface;
|
||||
outputBufferRenderer = null;
|
||||
outputMode = (surface != null) ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
||||
if (decoder != null) {
|
||||
decoder.setOutputMode(outputMode);
|
||||
}
|
||||
drawnToSurface = false;
|
||||
}
|
||||
|
||||
private void setOutputBufferRenderer(VpxOutputBufferRenderer outputBufferRenderer) {
|
||||
if (this.outputBufferRenderer == outputBufferRenderer) {
|
||||
return;
|
||||
}
|
||||
this.outputBufferRenderer = outputBufferRenderer;
|
||||
surface = null;
|
||||
outputMode = (outputBufferRenderer != null)
|
||||
? VpxDecoder.OUTPUT_MODE_YUV : VpxDecoder.OUTPUT_MODE_UNKNOWN;
|
||||
if (decoder != null) {
|
||||
decoder.setOutputMode(outputMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyIfVideoSizeChanged(final VpxOutputBuffer outputBuffer) {
|
||||
if (previousWidth == -1 || previousHeight == -1
|
||||
|| previousWidth != outputBuffer.width || previousHeight != outputBuffer.height) {
|
||||
previousWidth = outputBuffer.width;
|
||||
previousHeight = outputBuffer.height;
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onVideoSizeChanged(outputBuffer.width, outputBuffer.height);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyAndResetDroppedFrameCount() {
|
||||
if (eventHandler != null && eventListener != null && droppedFrameCount > 0) {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
final int countToNotify = droppedFrameCount;
|
||||
final long elapsedToNotify = now - droppedFrameAccumulationStartTimeMs;
|
||||
droppedFrameCount = 0;
|
||||
droppedFrameAccumulationStartTimeMs = now;
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDroppedFrames(countToNotify, elapsedToNotify);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyDrawnToSurface(final Surface surface) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDrawnToSurface(surface);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyDecoderError(final VpxDecoderException e) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDecoderError(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyDecoderInitialized(
|
||||
final long startElapsedRealtimeMs, final long finishElapsedRealtimeMs) {
|
||||
if (eventHandler != null && eventListener != null) {
|
||||
eventHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
eventListener.onDecoderInitialized("libvpx" + getLibvpxVersion(),
|
||||
finishElapsedRealtimeMs, finishElapsedRealtimeMs - startElapsedRealtimeMs);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer.C;
|
||||
import com.google.android.exoplayer.util.extensions.SimpleDecoder;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* JNI wrapper for the libvpx VP9 decoder.
|
||||
*/
|
||||
/* package */ final class VpxDecoder extends
|
||||
SimpleDecoder<VpxInputBuffer, VpxOutputBuffer, VpxDecoderException> {
|
||||
|
||||
public static final int OUTPUT_MODE_UNKNOWN = -1;
|
||||
public static final int OUTPUT_MODE_YUV = 0;
|
||||
public static final int OUTPUT_MODE_RGB = 1;
|
||||
|
||||
/**
|
||||
* Whether the underlying libvpx library is available.
|
||||
*/
|
||||
public static final boolean IS_AVAILABLE;
|
||||
static {
|
||||
boolean isAvailable;
|
||||
try {
|
||||
System.loadLibrary("vpx");
|
||||
System.loadLibrary("vpxJNI");
|
||||
isAvailable = true;
|
||||
} catch (UnsatisfiedLinkError exception) {
|
||||
isAvailable = false;
|
||||
}
|
||||
IS_AVAILABLE = isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version string of the underlying libvpx decoder.
|
||||
*/
|
||||
public static native String getLibvpxVersion();
|
||||
|
||||
private final long vpxDecContext;
|
||||
|
||||
private volatile int outputMode;
|
||||
|
||||
/**
|
||||
* Creates a VP9 decoder.
|
||||
*
|
||||
* @param numInputBuffers The number of input buffers.
|
||||
* @param numOutputBuffers The number of output buffers.
|
||||
* @param initialInputBufferSize The initial size of each input buffer.
|
||||
* @throws VpxDecoderException Thrown if an exception occurs when initializing the decoder.
|
||||
*/
|
||||
public VpxDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize)
|
||||
throws VpxDecoderException {
|
||||
super(new VpxInputBuffer[numInputBuffers], new VpxOutputBuffer[numOutputBuffers]);
|
||||
vpxDecContext = vpxInit();
|
||||
if (vpxDecContext == 0) {
|
||||
throw new VpxDecoderException("Failed to initialize decoder");
|
||||
}
|
||||
setInitialInputBufferSize(initialInputBufferSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the output mode for frames rendered by the decoder.
|
||||
*
|
||||
* @param outputMode The output mode to use, which must be one of the {@code OUTPUT_MODE_*}
|
||||
* constants in {@link VpxDecoder}.
|
||||
*/
|
||||
public void setOutputMode(int outputMode) {
|
||||
this.outputMode = outputMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VpxInputBuffer createInputBuffer() {
|
||||
return new VpxInputBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VpxOutputBuffer createOutputBuffer() {
|
||||
return new VpxOutputBuffer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseOutputBuffer(VpxOutputBuffer buffer) {
|
||||
super.releaseOutputBuffer(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected VpxDecoderException decode(VpxInputBuffer inputBuffer, VpxOutputBuffer outputBuffer,
|
||||
boolean reset) {
|
||||
outputBuffer.timestampUs = inputBuffer.timeUs;
|
||||
inputBuffer.data.position(inputBuffer.data.position() - inputBuffer.size);
|
||||
if (vpxDecode(vpxDecContext, inputBuffer.data, inputBuffer.size) != 0) {
|
||||
return new VpxDecoderException("Decode error: " + vpxGetErrorMessage(vpxDecContext));
|
||||
}
|
||||
outputBuffer.mode = outputMode;
|
||||
if (vpxGetFrame(vpxDecContext, outputBuffer) != 0) {
|
||||
outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
super.release();
|
||||
vpxClose(vpxDecContext);
|
||||
}
|
||||
|
||||
private native long vpxInit();
|
||||
private native long vpxClose(long context);
|
||||
private native long vpxDecode(long context, ByteBuffer encoded, int length);
|
||||
private native int vpxGetFrame(long context, VpxOutputBuffer outputBuffer);
|
||||
private native String vpxGetErrorMessage(long context);
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
/**
|
||||
* Thrown when a libvpx decoder error occurs.
|
||||
*/
|
||||
public class VpxDecoderException extends Exception {
|
||||
|
||||
/* package */ VpxDecoderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer.DecoderInputBuffer;
|
||||
|
||||
/**
|
||||
* Input buffer to a {@link VpxDecoder}.
|
||||
*/
|
||||
/* package */ final class VpxInputBuffer extends DecoderInputBuffer {
|
||||
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
public VpxInputBuffer() {
|
||||
super(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import com.google.android.exoplayer.util.extensions.OutputBuffer;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Output buffer containing video frame data, populated by {@link VpxDecoder}.
|
||||
*/
|
||||
public final class VpxOutputBuffer extends OutputBuffer {
|
||||
|
||||
public static final int COLORSPACE_UNKNOWN = 0;
|
||||
public static final int COLORSPACE_BT601 = 1;
|
||||
public static final int COLORSPACE_BT709 = 2;
|
||||
|
||||
private final VpxDecoder owner;
|
||||
|
||||
public int mode;
|
||||
/**
|
||||
* RGB buffer for RGB mode.
|
||||
*/
|
||||
public ByteBuffer data;
|
||||
public int width;
|
||||
public int height;
|
||||
/**
|
||||
* YUV planes for YUV mode.
|
||||
*/
|
||||
public ByteBuffer[] yuvPlanes;
|
||||
public int[] yuvStrides;
|
||||
public int colorspace;
|
||||
|
||||
/* package */ VpxOutputBuffer(VpxDecoder owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
owner.releaseOutputBuffer(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the buffer based on the given dimensions. Called via JNI after decoding completes.
|
||||
*/
|
||||
/* package */ void initForRgbFrame(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
int minimumRgbSize = width * height * 2;
|
||||
if (data == null || data.capacity() < minimumRgbSize) {
|
||||
data = ByteBuffer.allocateDirect(minimumRgbSize);
|
||||
yuvPlanes = null;
|
||||
}
|
||||
data.position(0);
|
||||
data.limit(minimumRgbSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the buffer based on the given stride. Called via JNI after decoding completes.
|
||||
*/
|
||||
/* package */ void initForYuvFrame(int width, int height, int yStride, int uvStride,
|
||||
int colorspace) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.colorspace = colorspace;
|
||||
int yLength = yStride * height;
|
||||
int uvLength = uvStride * ((height + 1) / 2);
|
||||
int minimumYuvSize = yLength + (uvLength * 2);
|
||||
if (data == null || data.capacity() < minimumYuvSize) {
|
||||
data = ByteBuffer.allocateDirect(minimumYuvSize);
|
||||
}
|
||||
data.limit(minimumYuvSize);
|
||||
if (yuvPlanes == null) {
|
||||
yuvPlanes = new ByteBuffer[3];
|
||||
}
|
||||
// Rewrapping has to be done on every frame since the stride might have changed.
|
||||
data.position(0);
|
||||
yuvPlanes[0] = data.slice();
|
||||
yuvPlanes[0].limit(yLength);
|
||||
data.position(yLength);
|
||||
yuvPlanes[1] = data.slice();
|
||||
yuvPlanes[1].limit(uvLength);
|
||||
data.position(yLength + uvLength);
|
||||
yuvPlanes[2] = data.slice();
|
||||
yuvPlanes[2].limit(uvLength);
|
||||
if (yuvStrides == null) {
|
||||
yuvStrides = new int[3];
|
||||
}
|
||||
yuvStrides[0] = yStride;
|
||||
yuvStrides[1] = uvStride;
|
||||
yuvStrides[2] = uvStride;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
/**
|
||||
* Renders the {@link VpxOutputBuffer}.
|
||||
*/
|
||||
public interface VpxOutputBufferRenderer {
|
||||
|
||||
/**
|
||||
* Sets the output buffer to be rendered. The renderer is responsible for releasing the buffer.
|
||||
*/
|
||||
void setOutputBuffer(VpxOutputBuffer outputBuffer);
|
||||
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLSurfaceView;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
import javax.microedition.khronos.opengles.GL10;
|
||||
|
||||
/**
|
||||
* GLSurfaceView.Renderer implementation that can render YUV Frames returned by libvpx after
|
||||
* decoding. It does the YUV to RGB color conversion in the Fragment Shader.
|
||||
*/
|
||||
/* package */ class VpxRenderer implements GLSurfaceView.Renderer {
|
||||
|
||||
private static final float[] kColorConversion601 = {
|
||||
1.164f, 1.164f, 1.164f,
|
||||
0.0f, -0.392f, 2.017f,
|
||||
1.596f, -0.813f, 0.0f,
|
||||
};
|
||||
|
||||
private static final float[] kColorConversion709 = {
|
||||
1.164f, 1.164f, 1.164f,
|
||||
0.0f, -0.213f, 2.112f,
|
||||
1.793f, -0.533f, 0.0f,
|
||||
};
|
||||
|
||||
private static final String VERTEX_SHADER =
|
||||
"varying vec2 interp_tc;\n"
|
||||
+ "attribute vec4 in_pos;\n"
|
||||
+ "attribute vec2 in_tc;\n"
|
||||
+ "void main() {\n"
|
||||
+ " gl_Position = in_pos;\n"
|
||||
+ " interp_tc = in_tc;\n"
|
||||
+ "}\n";
|
||||
private static final String[] TEXTURE_UNIFORMS = {"y_tex", "u_tex", "v_tex"};
|
||||
private static final String FRAGMENT_SHADER =
|
||||
"precision mediump float;\n"
|
||||
+ "varying vec2 interp_tc;\n"
|
||||
+ "uniform sampler2D y_tex;\n"
|
||||
+ "uniform sampler2D u_tex;\n"
|
||||
+ "uniform sampler2D v_tex;\n"
|
||||
+ "uniform mat3 mColorConversion;\n"
|
||||
+ "void main() {\n"
|
||||
+ " vec3 yuv;"
|
||||
+ " yuv.x = texture2D(y_tex, interp_tc).r - 0.0625;\n"
|
||||
+ " yuv.y = texture2D(u_tex, interp_tc).r - 0.5;\n"
|
||||
+ " yuv.z = texture2D(v_tex, interp_tc).r - 0.5;\n"
|
||||
+ " gl_FragColor = vec4(mColorConversion * yuv, 1.0);"
|
||||
+ "}\n";
|
||||
private static final FloatBuffer TEXTURE_VERTICES = nativeFloatBuffer(
|
||||
-1.0f, 1.0f,
|
||||
-1.0f, -1.0f,
|
||||
1.0f, 1.0f,
|
||||
1.0f, -1.0f);
|
||||
private final int[] yuvTextures = new int[3];
|
||||
private final AtomicReference<VpxOutputBuffer> pendingOutputBufferReference;
|
||||
|
||||
private int program;
|
||||
private int texLocation;
|
||||
private int colorMatrixLocation;
|
||||
private FloatBuffer textureCoords;
|
||||
private int previousWidth;
|
||||
private int previousStride;
|
||||
|
||||
private VpxOutputBuffer renderedOutputBuffer; // Accessed only from the GL thread.
|
||||
|
||||
public VpxRenderer() {
|
||||
previousWidth = -1;
|
||||
previousStride = -1;
|
||||
pendingOutputBufferReference = new AtomicReference<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a frame to be rendered. This should be followed by a call to
|
||||
* VpxVideoSurfaceView.requestRender() to actually render the frame.
|
||||
*
|
||||
* @param outputBuffer OutputBuffer containing the YUV Frame to be rendered
|
||||
*/
|
||||
public void setFrame(VpxOutputBuffer outputBuffer) {
|
||||
VpxOutputBuffer oldPendingOutputBuffer = pendingOutputBufferReference.getAndSet(outputBuffer);
|
||||
if (oldPendingOutputBuffer != null) {
|
||||
// The old pending output buffer will never be used for rendering, so release it now.
|
||||
oldPendingOutputBuffer.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
|
||||
// Create the GL program.
|
||||
program = GLES20.glCreateProgram();
|
||||
|
||||
// Add the vertex and fragment shaders.
|
||||
addShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER, program);
|
||||
addShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER, program);
|
||||
|
||||
// Link the GL program.
|
||||
GLES20.glLinkProgram(program);
|
||||
int[] result = new int[] {
|
||||
GLES20.GL_FALSE
|
||||
};
|
||||
result[0] = GLES20.GL_FALSE;
|
||||
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, result, 0);
|
||||
abortUnless(result[0] == GLES20.GL_TRUE, GLES20.glGetProgramInfoLog(program));
|
||||
GLES20.glUseProgram(program);
|
||||
int posLocation = GLES20.glGetAttribLocation(program, "in_pos");
|
||||
GLES20.glEnableVertexAttribArray(posLocation);
|
||||
GLES20.glVertexAttribPointer(
|
||||
posLocation, 2, GLES20.GL_FLOAT, false, 0, TEXTURE_VERTICES);
|
||||
texLocation = GLES20.glGetAttribLocation(program, "in_tc");
|
||||
GLES20.glEnableVertexAttribArray(texLocation);
|
||||
checkNoGLES2Error();
|
||||
colorMatrixLocation = GLES20.glGetUniformLocation(program, "mColorConversion");
|
||||
checkNoGLES2Error();
|
||||
setupTextures();
|
||||
checkNoGLES2Error();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 unused, int width, int height) {
|
||||
GLES20.glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 unused) {
|
||||
VpxOutputBuffer pendingOutputBuffer = pendingOutputBufferReference.getAndSet(null);
|
||||
if (pendingOutputBuffer == null && renderedOutputBuffer == null) {
|
||||
// There is no output buffer to render at the moment.
|
||||
return;
|
||||
}
|
||||
if (pendingOutputBuffer != null) {
|
||||
if (renderedOutputBuffer != null) {
|
||||
renderedOutputBuffer.release();
|
||||
}
|
||||
renderedOutputBuffer = pendingOutputBuffer;
|
||||
}
|
||||
VpxOutputBuffer outputBuffer = renderedOutputBuffer;
|
||||
// Set color matrix. Assume BT709 if the color space is unknown.
|
||||
float[] colorConversion = outputBuffer.colorspace == VpxOutputBuffer.COLORSPACE_BT601
|
||||
? kColorConversion601 : kColorConversion709;
|
||||
GLES20.glUniformMatrix3fv(colorMatrixLocation, 1, false, colorConversion, 0);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int h = (i == 0) ? outputBuffer.height : (outputBuffer.height + 1) / 2;
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
||||
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
|
||||
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
|
||||
outputBuffer.yuvStrides[i], h, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE,
|
||||
outputBuffer.yuvPlanes[i]);
|
||||
}
|
||||
// Set cropping of stride if either width or stride has changed.
|
||||
if (previousWidth != outputBuffer.width || previousStride != outputBuffer.yuvStrides[0]) {
|
||||
float crop = (float) outputBuffer.width / outputBuffer.yuvStrides[0];
|
||||
textureCoords = nativeFloatBuffer(
|
||||
0.0f, 0.0f,
|
||||
0.0f, 1.0f,
|
||||
crop, 0.0f,
|
||||
crop, 1.0f);
|
||||
GLES20.glVertexAttribPointer(
|
||||
texLocation, 2, GLES20.GL_FLOAT, false, 0, textureCoords);
|
||||
previousWidth = outputBuffer.width;
|
||||
previousStride = outputBuffer.yuvStrides[0];
|
||||
}
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
||||
checkNoGLES2Error();
|
||||
}
|
||||
|
||||
private void addShader(int type, String source, int program) {
|
||||
int[] result = new int[] {
|
||||
GLES20.GL_FALSE
|
||||
};
|
||||
int shader = GLES20.glCreateShader(type);
|
||||
GLES20.glShaderSource(shader, source);
|
||||
GLES20.glCompileShader(shader);
|
||||
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, result, 0);
|
||||
abortUnless(result[0] == GLES20.GL_TRUE,
|
||||
GLES20.glGetShaderInfoLog(shader) + ", source: " + source);
|
||||
GLES20.glAttachShader(program, shader);
|
||||
GLES20.glDeleteShader(shader);
|
||||
|
||||
checkNoGLES2Error();
|
||||
}
|
||||
|
||||
private void setupTextures() {
|
||||
GLES20.glGenTextures(3, yuvTextures, 0);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
GLES20.glUniform1i(GLES20.glGetUniformLocation(program, TEXTURE_UNIFORMS[i]), i);
|
||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
||||
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
|
||||
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
|
||||
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
checkNoGLES2Error();
|
||||
}
|
||||
|
||||
private void abortUnless(boolean condition, String msg) {
|
||||
if (!condition) {
|
||||
throw new RuntimeException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkNoGLES2Error() {
|
||||
int error = GLES20.glGetError();
|
||||
if (error != GLES20.GL_NO_ERROR) {
|
||||
throw new RuntimeException("GLES20 error: " + error);
|
||||
}
|
||||
}
|
||||
|
||||
private static FloatBuffer nativeFloatBuffer(float... array) {
|
||||
FloatBuffer buffer = ByteBuffer.allocateDirect(array.length * 4).order(
|
||||
ByteOrder.nativeOrder()).asFloatBuffer();
|
||||
buffer.put(array);
|
||||
buffer.flip();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.google.android.exoplayer.ext.vp9;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
/**
|
||||
* A GLSurfaceView extension that scales itself to the given aspect ratio.
|
||||
*/
|
||||
@TargetApi(11)
|
||||
public class VpxVideoSurfaceView extends GLSurfaceView implements VpxOutputBufferRenderer {
|
||||
|
||||
private final VpxRenderer renderer;
|
||||
|
||||
public VpxVideoSurfaceView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public VpxVideoSurfaceView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
renderer = new VpxRenderer();
|
||||
setPreserveEGLContextOnPause(true);
|
||||
setEGLContextClientVersion(2);
|
||||
setRenderer(renderer);
|
||||
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOutputBuffer(VpxOutputBuffer outputBuffer) {
|
||||
renderer.setFrame(outputBuffer);
|
||||
requestRender();
|
||||
}
|
||||
|
||||
}
|
42
extensions/vp9/src/main/jni/Android.mk
Normal file
42
extensions/vp9/src/main/jni/Android.mk
Normal file
@ -0,0 +1,42 @@
|
||||
#
|
||||
# Copyright (C) 2014 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
WORKING_DIR := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
LIBVPX_ROOT := $(WORKING_DIR)/libvpx
|
||||
LIBYUV_ROOT := $(WORKING_DIR)/libyuv
|
||||
|
||||
# build libyuv_static.a
|
||||
LOCAL_PATH := $(WORKING_DIR)
|
||||
include $(LIBYUV_ROOT)/Android.mk
|
||||
|
||||
# build libvpx.so
|
||||
LOCAL_PATH := $(WORKING_DIR)
|
||||
include libvpx.mk
|
||||
|
||||
# build libvpxJNI.so
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_PATH := $(WORKING_DIR)
|
||||
LOCAL_MODULE := libvpxJNI
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_CPP_EXTENSION := .cc
|
||||
LOCAL_SRC_FILES := vpx_jni.cc
|
||||
LOCAL_LDLIBS := -llog -lz -lm
|
||||
LOCAL_SHARED_LIBRARIES := libvpx
|
||||
LOCAL_STATIC_LIBRARIES := libyuv_static cpufeatures
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,android/cpufeatures)
|
20
extensions/vp9/src/main/jni/Application.mk
Normal file
20
extensions/vp9/src/main/jni/Application.mk
Normal file
@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright (C) 2014 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
APP_OPTIM := release
|
||||
APP_STL := gnustl_static
|
||||
APP_CPPFLAGS := -frtti
|
||||
APP_PLATFORM := android-9
|
124
extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh
Executable file
124
extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh
Executable file
@ -0,0 +1,124 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright (C) 2014 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# a bash script that generates the necessary config files for libvpx android ndk
|
||||
# builds.
|
||||
|
||||
set -e
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: ${0} <path_to_android_ndk>"
|
||||
exit
|
||||
fi
|
||||
|
||||
ndk="${1}"
|
||||
shift 1
|
||||
|
||||
# configuration parameters common to all architectures
|
||||
common_params="--disable-examples --disable-docs --enable-realtime-only"
|
||||
common_params+=" --disable-vp8 --disable-vp9-encoder --disable-webm-io"
|
||||
common_params+=" --disable-vp10 --disable-libyuv --disable-runtime-cpu-detect"
|
||||
|
||||
# configuration parameters for various architectures
|
||||
arch[0]="armeabi-v7a"
|
||||
config[0]="--target=armv7-android-gcc --sdk-path=$ndk --enable-neon"
|
||||
config[0]+=" --enable-neon-asm"
|
||||
|
||||
arch[1]="armeabi"
|
||||
config[1]="--target=armv7-android-gcc --sdk-path=$ndk --disable-neon"
|
||||
config[1]+=" --disable-neon-asm --disable-media"
|
||||
|
||||
arch[2]="mips"
|
||||
config[2]="--force-target=mips32-android-gcc --sdk-path=$ndk"
|
||||
|
||||
arch[3]="x86"
|
||||
config[3]="--force-target=x86-android-gcc --sdk-path=$ndk --disable-sse2"
|
||||
config[3]+=" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
|
||||
config[3]+=" --disable-avx2 --enable-pic"
|
||||
|
||||
arch[4]="arm64-v8a"
|
||||
config[4]="--force-target=armv8-android-gcc --sdk-path=$ndk --disable-neon"
|
||||
config[4]+=" --disable-neon-asm"
|
||||
|
||||
arch[5]="x86_64"
|
||||
config[5]="--force-target=x86_64-android-gcc --sdk-path=$ndk --disable-sse2"
|
||||
config[5]+=" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
|
||||
config[5]+=" --disable-avx2 --enable-pic --disable-neon --disable-neon-asm"
|
||||
|
||||
arch[6]="mips64"
|
||||
config[6]="--force-target=mips64-android-gcc --sdk-path=$ndk"
|
||||
|
||||
limit=$((${#arch[@]} - 1))
|
||||
|
||||
# list of files allowed after running configure in each arch directory.
|
||||
# everything else will be removed.
|
||||
allowed_files="libvpx_srcs.txt vpx_config.c vpx_config.h vpx_scale_rtcd.h"
|
||||
allowed_files+=" vp8_rtcd.h vp9_rtcd.h vpx_version.h vpx_config.asm"
|
||||
allowed_files+=" vpx_dsp_rtcd.h"
|
||||
|
||||
remove_trailing_whitespace() {
|
||||
perl -pi -e 's/\s\+$//' "$@"
|
||||
}
|
||||
|
||||
convert_asm() {
|
||||
for i in $(seq 0 ${limit}); do
|
||||
while read file; do
|
||||
case "${file}" in
|
||||
*.asm.s)
|
||||
# Some files may already have been processed (there are duplicated
|
||||
# .asm.s files for vp8 in the armeabi/armeabi-v7a configurations).
|
||||
file="libvpx/${file}"
|
||||
if [[ ! -e "${file}" ]]; then
|
||||
asm_file="${file%.s}"
|
||||
cat "${asm_file}" | libvpx/build/make/ads2gas.pl > "${file}"
|
||||
remove_trailing_whitespace "${file}"
|
||||
rm "${asm_file}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < libvpx_android_configs/${arch[${i}]}/libvpx_srcs.txt
|
||||
done
|
||||
}
|
||||
|
||||
extglob_status="$(shopt extglob | cut -f2)"
|
||||
shopt -s extglob
|
||||
for i in $(seq 0 ${limit}); do
|
||||
mkdir -p "libvpx_android_configs/${arch[${i}]}"
|
||||
pushd "libvpx_android_configs/${arch[${i}]}"
|
||||
|
||||
# configure and make
|
||||
echo "build_android_configs: "
|
||||
echo "configure ${config[${i}]} ${common_params}"
|
||||
../../libvpx/configure ${config[${i}]} ${common_params}
|
||||
rm -f libvpx_srcs.txt
|
||||
make libvpx_srcs.txt
|
||||
|
||||
# remove files that aren't needed
|
||||
rm -rf !(${allowed_files// /|})
|
||||
remove_trailing_whitespace *
|
||||
|
||||
popd
|
||||
done
|
||||
|
||||
# restore extglob status as it was before
|
||||
if [[ "${extglob_status}" == "off" ]]; then
|
||||
shopt -u extglob
|
||||
fi
|
||||
|
||||
convert_asm
|
||||
|
||||
echo "Generated android config files."
|
51
extensions/vp9/src/main/jni/libvpx.mk
Normal file
51
extensions/vp9/src/main/jni/libvpx.mk
Normal file
@ -0,0 +1,51 @@
|
||||
#
|
||||
# Copyright (C) 2014 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
CONFIG_DIR := $(LOCAL_PATH)/libvpx_android_configs/$(TARGET_ARCH_ABI)
|
||||
libvpx_source_dir := $(LOCAL_PATH)/libvpx
|
||||
|
||||
LOCAL_MODULE := libvpx
|
||||
LOCAL_MODULE_CLASS := STATIC_LIBRARIES
|
||||
LOCAL_CFLAGS := -DHAVE_CONFIG_H=vpx_config.h
|
||||
LOCAL_ARM_MODE := arm
|
||||
LOCAL_CFLAGS += -O3
|
||||
|
||||
# config specific include should go first to pick up the config specific rtcd.
|
||||
LOCAL_C_INCLUDES := $(CONFIG_DIR) $(libvpx_source_dir)
|
||||
|
||||
# generate source file list
|
||||
libvpx_codec_srcs := $(sort $(shell cat $(CONFIG_DIR)/libvpx_srcs.txt))
|
||||
LOCAL_SRC_FILES := libvpx_android_configs/$(TARGET_ARCH_ABI)/vpx_config.c
|
||||
LOCAL_SRC_FILES += $(addprefix libvpx/, $(filter-out vpx_config.c, \
|
||||
$(filter %.c, $(libvpx_codec_srcs))))
|
||||
|
||||
# include assembly files if they exist
|
||||
# "%.asm.s" covers neon assembly and "%.asm" covers x86 assembly
|
||||
LOCAL_SRC_FILES += $(addprefix libvpx/, \
|
||||
$(filter %.asm.s %.asm, $(libvpx_codec_srcs)))
|
||||
|
||||
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
|
||||
# append .neon to *_neon.c and *.s
|
||||
LOCAL_SRC_FILES := $(subst _neon.c,_neon.c.neon,$(LOCAL_SRC_FILES))
|
||||
LOCAL_SRC_FILES := $(subst .s,.s.neon,$(LOCAL_SRC_FILES))
|
||||
endif
|
||||
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/libvpx \
|
||||
$(LOCAL_PATH)/libvpx/vpx
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
176
extensions/vp9/src/main/jni/vpx_jni.cc
Normal file
176
extensions/vp9/src/main/jni/vpx_jni.cc
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cpu-features.h>
|
||||
#include <jni.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <new>
|
||||
|
||||
#include "libyuv.h" // NOLINT
|
||||
|
||||
#define VPX_CODEC_DISABLE_COMPAT 1
|
||||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx/vp8dx.h"
|
||||
|
||||
#define LOG_TAG "LIBVPX_DEC"
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
|
||||
#define FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer_ext_vp9_VpxDecoder_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__);\
|
||||
} \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer_ext_vp9_VpxDecoder_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
// JNI references for VpxOutputBuffer class.
|
||||
static jmethodID initForRgbFrame;
|
||||
static jmethodID initForYuvFrame;
|
||||
static jfieldID dataField;
|
||||
static jfieldID outputModeField;
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||
return -1;
|
||||
}
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
FUNC(jlong, vpxInit) {
|
||||
vpx_codec_ctx_t* context = new vpx_codec_ctx_t();
|
||||
vpx_codec_dec_cfg_t cfg = {0};
|
||||
cfg.threads = android_getCpuCount();
|
||||
if (vpx_codec_dec_init(context, &vpx_codec_vp9_dx_algo, &cfg, 0)) {
|
||||
LOGE("ERROR: Fail to initialize libvpx decoder.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Populate JNI References.
|
||||
const jclass outputBufferClass = env->FindClass(
|
||||
"com/google/android/exoplayer/ext/vp9/VpxOutputBuffer");
|
||||
initForYuvFrame = env->GetMethodID(outputBufferClass, "initForYuvFrame",
|
||||
"(IIIII)V");
|
||||
initForRgbFrame = env->GetMethodID(outputBufferClass, "initForRgbFrame",
|
||||
"(II)V");
|
||||
dataField = env->GetFieldID(outputBufferClass, "data",
|
||||
"Ljava/nio/ByteBuffer;");
|
||||
outputModeField = env->GetFieldID(outputBufferClass, "mode", "I");
|
||||
|
||||
return reinterpret_cast<intptr_t>(context);
|
||||
}
|
||||
|
||||
FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
const uint8_t* const buffer =
|
||||
reinterpret_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded));
|
||||
const vpx_codec_err_t status =
|
||||
vpx_codec_decode(context, buffer, len, NULL, 0);
|
||||
if (status != VPX_CODEC_OK) {
|
||||
LOGE("ERROR: vpx_codec_decode() failed, status= %d", status);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FUNC(jlong, vpxClose, jlong jContext) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
vpx_codec_destroy(context);
|
||||
delete context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_image_t* const img = vpx_codec_get_frame(context, &iter);
|
||||
|
||||
if (img == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const int kOutputModeYuv = 0;
|
||||
const int kOutputModeRgb = 1;
|
||||
|
||||
int outputMode = env->GetIntField(jOutputBuffer, outputModeField);
|
||||
if (outputMode == kOutputModeRgb) {
|
||||
// resize buffer if required.
|
||||
env->CallVoidMethod(jOutputBuffer, initForRgbFrame, img->d_w, img->d_h);
|
||||
|
||||
// get pointer to the data buffer.
|
||||
const jobject dataObject = env->GetObjectField(jOutputBuffer, dataField);
|
||||
uint8_t* const dst =
|
||||
reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(dataObject));
|
||||
|
||||
libyuv::I420ToRGB565(img->planes[VPX_PLANE_Y], img->stride[VPX_PLANE_Y],
|
||||
img->planes[VPX_PLANE_U], img->stride[VPX_PLANE_U],
|
||||
img->planes[VPX_PLANE_V], img->stride[VPX_PLANE_V],
|
||||
dst, img->d_w * 2, img->d_w, img->d_h);
|
||||
} else if (outputMode == kOutputModeYuv) {
|
||||
const int kColorspaceUnknown = 0;
|
||||
const int kColorspaceBT601 = 1;
|
||||
const int kColorspaceBT709 = 2;
|
||||
|
||||
int colorspace = kColorspaceUnknown;
|
||||
switch (img->cs) {
|
||||
case VPX_CS_BT_601:
|
||||
colorspace = kColorspaceBT601;
|
||||
break;
|
||||
case VPX_CS_BT_709:
|
||||
colorspace = kColorspaceBT709;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// resize buffer if required.
|
||||
env->CallVoidMethod(jOutputBuffer, initForYuvFrame, img->d_w, img->d_h,
|
||||
img->stride[VPX_PLANE_Y], img->stride[VPX_PLANE_U],
|
||||
colorspace);
|
||||
|
||||
// get pointer to the data buffer.
|
||||
const jobject dataObject = env->GetObjectField(jOutputBuffer, dataField);
|
||||
jbyte* const data =
|
||||
reinterpret_cast<jbyte*>(env->GetDirectBufferAddress(dataObject));
|
||||
|
||||
// TODO: This copy can be eliminated by using external frame buffers. NOLINT
|
||||
// This is insignificant for smaller videos but takes ~1.5ms for 1080p
|
||||
// clips. So this should eventually be gotten rid of.
|
||||
const uint64_t y_length = img->stride[VPX_PLANE_Y] * img->d_h;
|
||||
const uint64_t uv_length = img->stride[VPX_PLANE_U] * ((img->d_h + 1) / 2);
|
||||
memcpy(data, img->planes[VPX_PLANE_Y], y_length);
|
||||
memcpy(data + y_length, img->planes[VPX_PLANE_U], uv_length);
|
||||
memcpy(data + y_length + uv_length, img->planes[VPX_PLANE_V], uv_length);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
FUNC(jstring, getLibvpxVersion) {
|
||||
return env->NewStringUTF(vpx_codec_version_str());
|
||||
}
|
||||
|
||||
FUNC(jstring, vpxGetErrorMessage, jlong jContext) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
return env->NewStringUTF(vpx_codec_error(context));
|
||||
}
|
11
extensions/vp9/src/main/proguard.cfg
Normal file
11
extensions/vp9/src/main/proguard.cfg
Normal file
@ -0,0 +1,11 @@
|
||||
# Proguard rules specific to the VP9 extension.
|
||||
|
||||
# This prevents the names of native methods from being obfuscated.
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# Some members of this class are being accessed from native methods. Keep them unobfuscated.
|
||||
-keep class com.google.android.exoplayer.ext.vp9.VpxOutputBuffer {
|
||||
*;
|
||||
}
|
16
extensions/vp9/src/main/project.properties
Normal file
16
extensions/vp9/src/main/project.properties
Normal file
@ -0,0 +1,16 @@
|
||||
# This file is automatically generated by Android Tools.
|
||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||
#
|
||||
# This file must be checked in Version Control Systems.
|
||||
#
|
||||
# To customize properties used by the Ant build system edit
|
||||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-23
|
||||
android.library=true
|
||||
android.library.reference.1=../../../../library/src/main
|
2
extensions/vp9/src/main/res/.README.txt
Normal file
2
extensions/vp9/src/main/res/.README.txt
Normal file
@ -0,0 +1,2 @@
|
||||
This file is needed to make sure the res directory is present.
|
||||
The file is ignored by the Android toolchain because its name starts with a dot.
|
@ -14,9 +14,11 @@
|
||||
include ':library'
|
||||
include ':demo'
|
||||
include ':extension-opus'
|
||||
include ':extension-vp9'
|
||||
include ':extension-okhttp'
|
||||
include ':extension-flac'
|
||||
|
||||
project(':extension-opus').projectDir = new File(settingsDir, 'extensions/opus')
|
||||
project(':extension-vp9').projectDir = new File(settingsDir, 'extensions/vp9')
|
||||
project(':extension-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
|
||||
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
|
||||
|
Loading…
x
Reference in New Issue
Block a user