Copy vp9 extension v1 -> v2

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=119748182
This commit is contained in:
eguven 2016-04-13 08:21:52 -07:00 committed by Oliver Woodman
parent 51df2dce46
commit ba7b1b7bf1
33 changed files with 2144 additions and 0 deletions

81
extensions/vp9/README.md Normal file
View 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.

View 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')
}

View 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>

View 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>

View 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.

View File

@ -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();
}
}
}

View 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

View 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.

View 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>

View 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>

View 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>

View 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

View 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>

View File

@ -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);
}
});
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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();
}
}

View 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)

View 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

View 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."

View 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)

View 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));
}

View 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 {
*;
}

View 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

View 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.

View File

@ -14,9 +14,11 @@
include ':library'
include ':demo'
include ':extension-opus'
include ':extension-vp9'
include ':extension-okhttp'
include ':extension-flac'
project(':extension-opus').projectDir = new File(settingsDir, 'extensions/opus')
project(':extension-vp9').projectDir = new File(settingsDir, 'extensions/vp9')
project(':extension-okhttp').projectDir = new File(settingsDir, 'extensions/okhttp')
project(':extension-flac').projectDir = new File(settingsDir, 'extensions/flac')