mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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
33 changed files with 2144 additions and 0 deletions
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 ':library'
|
||||||
include ':demo'
|
include ':demo'
|
||||||
include ':extension-opus'
|
include ':extension-opus'
|
||||||
|
include ':extension-vp9'
|
||||||
include ':extension-okhttp'
|
include ':extension-okhttp'
|
||||||
include ':extension-flac'
|
include ':extension-flac'
|
||||||
|
|
||||||
project(':extension-opus').projectDir = new File(settingsDir, 'extensions/opus')
|
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-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
|
||||||
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
|
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue