Commit cdae9ac5 by olly Committed by Oliver Woodman

ExoPlayer V2 Refactor - Steps 1/2.

GitHub note - Apologies for the cryptic change descriptions,
they relate to a design doc that's not externally visible.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=113043764
parent 61d581fe
Showing with 77 additions and 3831 deletions
<?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 combineaccessrules="false" kind="src" path="/ExoPlayerLib"/>
<classpathentry kind="src" path="java"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerDemo</name>
<comment></comment>
<projects>
</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>1363908154650</id>
<name></name>
<type>22</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-name-matches-false-false-BUILD</arguments>
</matcher>
</filter>
<filter>
<id>1363908154652</id>
<name></name>
<type>10</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-name-matches-true-false-build</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.source=1.7
...@@ -23,12 +23,12 @@ import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException; ...@@ -23,12 +23,12 @@ import com.google.android.exoplayer.MediaCodecUtil.DecoderQueryException;
import com.google.android.exoplayer.MediaFormat; import com.google.android.exoplayer.MediaFormat;
import com.google.android.exoplayer.audio.AudioCapabilities; import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver; import com.google.android.exoplayer.audio.AudioCapabilitiesReceiver;
import com.google.android.exoplayer.demo.player.DashRendererBuilder; import com.google.android.exoplayer.demo.player.DashSourceBuilder;
import com.google.android.exoplayer.demo.player.DemoPlayer; import com.google.android.exoplayer.demo.player.DemoPlayer;
import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.DemoPlayer.SourceBuilder;
import com.google.android.exoplayer.demo.player.ExtractorRendererBuilder; import com.google.android.exoplayer.demo.player.ExtractorSourceBuilder;
import com.google.android.exoplayer.demo.player.HlsRendererBuilder; import com.google.android.exoplayer.demo.player.HlsSourceBuilder;
import com.google.android.exoplayer.demo.player.SmoothStreamingRendererBuilder; import com.google.android.exoplayer.demo.player.SmoothStreamingSourceBuilder;
import com.google.android.exoplayer.drm.UnsupportedDrmException; import com.google.android.exoplayer.drm.UnsupportedDrmException;
import com.google.android.exoplayer.metadata.GeobMetadata; import com.google.android.exoplayer.metadata.GeobMetadata;
import com.google.android.exoplayer.metadata.PrivMetadata; import com.google.android.exoplayer.metadata.PrivMetadata;
...@@ -296,19 +296,19 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -296,19 +296,19 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
// Internal methods // Internal methods
private RendererBuilder getRendererBuilder() { private SourceBuilder getSourceBuilder() {
String userAgent = Util.getUserAgent(this, "ExoPlayerDemo"); String userAgent = Util.getUserAgent(this, "ExoPlayerDemo");
switch (contentType) { switch (contentType) {
case Util.TYPE_SS: case Util.TYPE_SS:
return new SmoothStreamingRendererBuilder(this, userAgent, contentUri.toString(), return new SmoothStreamingSourceBuilder(this, userAgent, contentUri.toString(),
new SmoothStreamingTestMediaDrmCallback()); new SmoothStreamingTestMediaDrmCallback());
case Util.TYPE_DASH: case Util.TYPE_DASH:
return new DashRendererBuilder(this, userAgent, contentUri.toString(), return new DashSourceBuilder(this, userAgent, contentUri.toString(),
new WidevineTestMediaDrmCallback(contentId, provider)); new WidevineTestMediaDrmCallback(contentId, provider));
case Util.TYPE_HLS: case Util.TYPE_HLS:
return new HlsRendererBuilder(this, userAgent, contentUri.toString()); return new HlsSourceBuilder(this, userAgent, contentUri.toString());
case Util.TYPE_OTHER: case Util.TYPE_OTHER:
return new ExtractorRendererBuilder(this, userAgent, contentUri); return new ExtractorSourceBuilder(this, userAgent, contentUri);
default: default:
throw new IllegalStateException("Unsupported type: " + contentType); throw new IllegalStateException("Unsupported type: " + contentType);
} }
...@@ -316,7 +316,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback, ...@@ -316,7 +316,7 @@ public class PlayerActivity extends Activity implements SurfaceHolder.Callback,
private void preparePlayer(boolean playWhenReady) { private void preparePlayer(boolean playWhenReady) {
if (player == null) { if (player == null) {
player = new DemoPlayer(getRendererBuilder()); player = new DemoPlayer(this, getSourceBuilder());
player.addListener(this); player.addListener(this);
player.setCaptionListener(this); player.setCaptionListener(this);
player.setMetadataListener(this); player.setMetadataListener(this);
......
...@@ -17,50 +17,38 @@ package com.google.android.exoplayer.demo.player; ...@@ -17,50 +17,38 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.DefaultLoadControl; import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MultiSampleSource;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.ChunkSource; import com.google.android.exoplayer.chunk.ChunkSource;
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
import com.google.android.exoplayer.dash.DashChunkSource; import com.google.android.exoplayer.dash.DashChunkSource;
import com.google.android.exoplayer.dash.DefaultDashTrackSelector; import com.google.android.exoplayer.dash.DefaultDashTrackSelector;
import com.google.android.exoplayer.dash.mpd.AdaptationSet;
import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription; import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription;
import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser; import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser;
import com.google.android.exoplayer.dash.mpd.Period;
import com.google.android.exoplayer.dash.mpd.UtcTimingElement; import com.google.android.exoplayer.dash.mpd.UtcTimingElement;
import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver; import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver;
import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver.UtcTimingCallback; import com.google.android.exoplayer.dash.mpd.UtcTimingElementResolver.UtcTimingCallback;
import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.DemoPlayer.SourceBuilder;
import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.drm.UnsupportedDrmException;
import com.google.android.exoplayer.text.TextTrackRenderer;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator; import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import com.google.android.exoplayer.upstream.UriDataSource; import com.google.android.exoplayer.upstream.UriDataSource;
import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.Util;
import android.content.Context; import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler; import android.os.Handler;
import android.util.Log; import android.util.Log;
import java.io.IOException; import java.io.IOException;
/** /**
* A {@link RendererBuilder} for DASH. * A {@link SourceBuilder} for DASH.
*/ */
public class DashRendererBuilder implements RendererBuilder { public class DashSourceBuilder implements SourceBuilder {
private static final String TAG = "DashRendererBuilder"; private static final String TAG = "DashSourceBuilder";
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int VIDEO_BUFFER_SEGMENTS = 200; private static final int VIDEO_BUFFER_SEGMENTS = 200;
...@@ -68,10 +56,6 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -68,10 +56,6 @@ public class DashRendererBuilder implements RendererBuilder {
private static final int TEXT_BUFFER_SEGMENTS = 2; private static final int TEXT_BUFFER_SEGMENTS = 2;
private static final int LIVE_EDGE_LATENCY_MS = 30000; private static final int LIVE_EDGE_LATENCY_MS = 30000;
private static final int SECURITY_LEVEL_UNKNOWN = -1;
private static final int SECURITY_LEVEL_1 = 1;
private static final int SECURITY_LEVEL_3 = 3;
private final Context context; private final Context context;
private final String userAgent; private final String userAgent;
private final String url; private final String url;
...@@ -79,7 +63,7 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -79,7 +63,7 @@ public class DashRendererBuilder implements RendererBuilder {
private AsyncRendererBuilder currentAsyncBuilder; private AsyncRendererBuilder currentAsyncBuilder;
public DashRendererBuilder(Context context, String userAgent, String url, public DashSourceBuilder(Context context, String userAgent, String url,
MediaDrmCallback drmCallback) { MediaDrmCallback drmCallback) {
this.context = context; this.context = context;
this.userAgent = userAgent; this.userAgent = userAgent;
...@@ -155,7 +139,7 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -155,7 +139,7 @@ public class DashRendererBuilder implements RendererBuilder {
return; return;
} }
player.onRenderersError(e); player.onSourceBuilderError(e);
} }
@Override @Override
...@@ -180,11 +164,9 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -180,11 +164,9 @@ public class DashRendererBuilder implements RendererBuilder {
} }
private void buildRenderers() { private void buildRenderers() {
// TODO[REFACTOR]: Bring back DRM support.
/*
Period period = manifest.getPeriod(0); Period period = manifest.getPeriod(0);
Handler mainHandler = player.getMainHandler();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
boolean hasContentProtection = false; boolean hasContentProtection = false;
for (int i = 0; i < period.adaptationSets.size(); i++) { for (int i = 0; i < period.adaptationSets.size(); i++) {
AdaptationSet adaptationSet = period.adaptationSets.get(i); AdaptationSet adaptationSet = period.adaptationSets.get(i);
...@@ -198,7 +180,7 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -198,7 +180,7 @@ public class DashRendererBuilder implements RendererBuilder {
StreamingDrmSessionManager drmSessionManager = null; StreamingDrmSessionManager drmSessionManager = null;
if (hasContentProtection) { if (hasContentProtection) {
if (Util.SDK_INT < 18) { if (Util.SDK_INT < 18) {
player.onRenderersError( player.onSourceBuilderError(
new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME));
return; return;
} }
...@@ -207,23 +189,25 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -207,23 +189,25 @@ public class DashRendererBuilder implements RendererBuilder {
player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player);
filterHdContent = getWidevineSecurityLevel(drmSessionManager) != SECURITY_LEVEL_1; filterHdContent = getWidevineSecurityLevel(drmSessionManager) != SECURITY_LEVEL_1;
} catch (UnsupportedDrmException e) { } catch (UnsupportedDrmException e) {
player.onRenderersError(e); player.onSourceBuilderError(e);
return; return;
} }
} }
*/
Handler mainHandler = player.getMainHandler();
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
// Build the video renderer. // Build the video renderer.
DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher, ChunkSource videoChunkSource = new DashChunkSource(manifestFetcher,
DefaultDashTrackSelector.newVideoInstance(context, true, filterHdContent), DefaultDashTrackSelector.newVideoInstance(context, true, false),
videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS, videoDataSource, new AdaptiveEvaluator(bandwidthMeter), LIVE_EDGE_LATENCY_MS,
elapsedRealtimeOffset, mainHandler, player, DemoPlayer.TYPE_VIDEO); elapsedRealtimeOffset, mainHandler, player, DemoPlayer.TYPE_VIDEO);
ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl,
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_VIDEO); DemoPlayer.TYPE_VIDEO);
TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource,
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
drmSessionManager, true, mainHandler, player, 50);
// Build the audio renderer. // Build the audio renderer.
DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
...@@ -233,9 +217,6 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -233,9 +217,6 @@ public class DashRendererBuilder implements RendererBuilder {
ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_AUDIO); DemoPlayer.TYPE_AUDIO);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource,
MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
// Build the text renderer. // Build the text renderer.
DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
...@@ -245,23 +226,19 @@ public class DashRendererBuilder implements RendererBuilder { ...@@ -245,23 +226,19 @@ public class DashRendererBuilder implements RendererBuilder {
ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl,
TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_TEXT); DemoPlayer.TYPE_TEXT);
TrackRenderer textRenderer = new TextTrackRenderer(textSampleSource, player,
mainHandler.getLooper());
// Invoke the callback. // Invoke the callback.
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; player.onSource(
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; new MultiSampleSource(videoSampleSource, audioSampleSource, textSampleSource));
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
} }
/*
private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) { private static int getWidevineSecurityLevel(StreamingDrmSessionManager sessionManager) {
String securityLevelProperty = sessionManager.getPropertyString("securityLevel"); String securityLevelProperty = sessionManager.getPropertyString("securityLevel");
return securityLevelProperty.equals("L1") ? SECURITY_LEVEL_1 : securityLevelProperty return securityLevelProperty.equals("L1") ? SECURITY_LEVEL_1 : securityLevelProperty
.equals("L3") ? SECURITY_LEVEL_3 : SECURITY_LEVEL_UNKNOWN; .equals("L3") ? SECURITY_LEVEL_3 : SECURITY_LEVEL_UNKNOWN;
} }
*/
} }
} }
...@@ -15,30 +15,21 @@ ...@@ -15,30 +15,21 @@
*/ */
package com.google.android.exoplayer.demo.player; package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.demo.player.DemoPlayer.SourceBuilder;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder;
import com.google.android.exoplayer.extractor.Extractor; import com.google.android.exoplayer.extractor.Extractor;
import com.google.android.exoplayer.extractor.ExtractorSampleSource; import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.text.TextTrackRenderer;
import com.google.android.exoplayer.upstream.Allocator; import com.google.android.exoplayer.upstream.Allocator;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator; import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import android.content.Context; import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.net.Uri; import android.net.Uri;
/** /**
* A {@link RendererBuilder} for streams that can be read using an {@link Extractor}. * A {@link SourceBuilder} for streams that can be read using an {@link Extractor}.
*/ */
public class ExtractorRendererBuilder implements RendererBuilder { public class ExtractorSourceBuilder implements SourceBuilder {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int BUFFER_SEGMENT_COUNT = 256; private static final int BUFFER_SEGMENT_COUNT = 256;
...@@ -47,7 +38,7 @@ public class ExtractorRendererBuilder implements RendererBuilder { ...@@ -47,7 +38,7 @@ public class ExtractorRendererBuilder implements RendererBuilder {
private final String userAgent; private final String userAgent;
private final Uri uri; private final Uri uri;
public ExtractorRendererBuilder(Context context, String userAgent, Uri uri) { public ExtractorSourceBuilder(Context context, String userAgent, Uri uri) {
this.context = context; this.context = context;
this.userAgent = userAgent; this.userAgent = userAgent;
this.uri = uri; this.uri = uri;
...@@ -56,28 +47,11 @@ public class ExtractorRendererBuilder implements RendererBuilder { ...@@ -56,28 +47,11 @@ public class ExtractorRendererBuilder implements RendererBuilder {
@Override @Override
public void buildRenderers(DemoPlayer player) { public void buildRenderers(DemoPlayer player) {
Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE); Allocator allocator = new DefaultAllocator(BUFFER_SEGMENT_SIZE);
DataSource dataSource = new DefaultUriDataSource(context, player.getBandwidthMeter(),
// Build the video and audio renderers. userAgent);
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(player.getMainHandler(), ExtractorSampleSource source = new ExtractorSampleSource(uri, dataSource, allocator,
null);
DataSource dataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(uri, dataSource, allocator,
BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE); BUFFER_SEGMENT_COUNT * BUFFER_SEGMENT_SIZE);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, player.onSource(source);
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
player.getMainHandler(), player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
TrackRenderer textRenderer = new TextTrackRenderer(sampleSource, player,
player.getMainHandler().getLooper());
// Invoke the callback.
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT];
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
} }
@Override @Override
......
...@@ -17,46 +17,32 @@ package com.google.android.exoplayer.demo.player; ...@@ -17,46 +17,32 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.DefaultLoadControl; import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.demo.player.DemoPlayer.SourceBuilder;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder;
import com.google.android.exoplayer.hls.DefaultHlsTrackSelector; import com.google.android.exoplayer.hls.DefaultHlsTrackSelector;
import com.google.android.exoplayer.hls.HlsChunkSource; import com.google.android.exoplayer.hls.HlsChunkSource;
import com.google.android.exoplayer.hls.HlsMasterPlaylist;
import com.google.android.exoplayer.hls.HlsPlaylist; import com.google.android.exoplayer.hls.HlsPlaylist;
import com.google.android.exoplayer.hls.HlsPlaylistParser; import com.google.android.exoplayer.hls.HlsPlaylistParser;
import com.google.android.exoplayer.hls.HlsSampleSource; import com.google.android.exoplayer.hls.HlsSampleSource;
import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider; import com.google.android.exoplayer.hls.PtsTimestampAdjusterProvider;
import com.google.android.exoplayer.metadata.Id3Parser; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.metadata.MetadataTrackRenderer;
import com.google.android.exoplayer.text.TextTrackRenderer;
import com.google.android.exoplayer.text.eia608.Eia608TrackRenderer;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator; import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback; import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback;
import android.content.Context; import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler; import android.os.Handler;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
/** /**
* A {@link RendererBuilder} for HLS. * A {@link SourceBuilder} for HLS.
*/ */
public class HlsRendererBuilder implements RendererBuilder { public class HlsSourceBuilder implements SourceBuilder {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int MAIN_BUFFER_SEGMENTS = 256; private static final int MAIN_BUFFER_SEGMENTS = 256;
private static final int TEXT_BUFFER_SEGMENTS = 2;
private final Context context; private final Context context;
private final String userAgent; private final String userAgent;
...@@ -64,7 +50,7 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -64,7 +50,7 @@ public class HlsRendererBuilder implements RendererBuilder {
private AsyncRendererBuilder currentAsyncBuilder; private AsyncRendererBuilder currentAsyncBuilder;
public HlsRendererBuilder(Context context, String userAgent, String url) { public HlsSourceBuilder(Context context, String userAgent, String url) {
this.context = context; this.context = context;
this.userAgent = userAgent; this.userAgent = userAgent;
this.url = url; this.url = url;
...@@ -118,7 +104,7 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -118,7 +104,7 @@ public class HlsRendererBuilder implements RendererBuilder {
return; return;
} }
player.onRenderersError(e); player.onSourceBuilderError(e);
} }
@Override @Override
...@@ -128,8 +114,8 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -128,8 +114,8 @@ public class HlsRendererBuilder implements RendererBuilder {
} }
Handler mainHandler = player.getMainHandler(); Handler mainHandler = player.getMainHandler();
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider(); PtsTimestampAdjusterProvider timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
// Build the video/audio/metadata renderers. // Build the video/audio/metadata renderers.
...@@ -139,16 +125,10 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -139,16 +125,10 @@ public class HlsRendererBuilder implements RendererBuilder {
timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE); timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl, HlsSampleSource sampleSource = new HlsSampleSource(chunkSource, loadControl,
MAIN_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO); MAIN_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, DemoPlayer.TYPE_VIDEO);
MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context,
sampleSource, MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
5000, mainHandler, player, 50);
MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource,
MediaCodecSelector.DEFAULT, null, true, player.getMainHandler(), player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
MetadataTrackRenderer<Map<String, Object>> id3Renderer = new MetadataTrackRenderer<>(
sampleSource, new Id3Parser(), player, mainHandler.getLooper());
// TODO[REFACTOR]: Bring back caption support.
// Build the text renderer, preferring Webvtt where available. // Build the text renderer, preferring Webvtt where available.
/*
boolean preferWebvtt = false; boolean preferWebvtt = false;
if (manifest instanceof HlsMasterPlaylist) { if (manifest instanceof HlsMasterPlaylist) {
preferWebvtt = !((HlsMasterPlaylist) manifest).subtitles.isEmpty(); preferWebvtt = !((HlsMasterPlaylist) manifest).subtitles.isEmpty();
...@@ -156,7 +136,7 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -156,7 +136,7 @@ public class HlsRendererBuilder implements RendererBuilder {
TrackRenderer textRenderer; TrackRenderer textRenderer;
if (preferWebvtt) { if (preferWebvtt) {
DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
HlsChunkSource textChunkSource = new HlsChunkSource(false /* isMaster */, textDataSource, HlsChunkSource textChunkSource = new HlsChunkSource(false, textDataSource,
url, manifest, DefaultHlsTrackSelector.newVttInstance(), bandwidthMeter, url, manifest, DefaultHlsTrackSelector.newVttInstance(), bandwidthMeter,
timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE); timestampAdjusterProvider, HlsChunkSource.ADAPTIVE_MODE_SPLICE);
HlsSampleSource textSampleSource = new HlsSampleSource(textChunkSource, loadControl, HlsSampleSource textSampleSource = new HlsSampleSource(textChunkSource, loadControl,
...@@ -165,13 +145,9 @@ public class HlsRendererBuilder implements RendererBuilder { ...@@ -165,13 +145,9 @@ public class HlsRendererBuilder implements RendererBuilder {
} else { } else {
textRenderer = new Eia608TrackRenderer(sampleSource, player, mainHandler.getLooper()); textRenderer = new Eia608TrackRenderer(sampleSource, player, mainHandler.getLooper());
} }
*/
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; player.onSource(sampleSource);
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer;
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_METADATA] = id3Renderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
} }
} }
......
...@@ -17,43 +17,33 @@ package com.google.android.exoplayer.demo.player; ...@@ -17,43 +17,33 @@ package com.google.android.exoplayer.demo.player;
import com.google.android.exoplayer.DefaultLoadControl; import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl; import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; import com.google.android.exoplayer.MultiSampleSource;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.audio.AudioCapabilities;
import com.google.android.exoplayer.chunk.ChunkSampleSource; import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.ChunkSource; import com.google.android.exoplayer.chunk.ChunkSource;
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
import com.google.android.exoplayer.demo.player.DemoPlayer.RendererBuilder; import com.google.android.exoplayer.demo.player.DemoPlayer.SourceBuilder;
import com.google.android.exoplayer.drm.DrmSessionManager;
import com.google.android.exoplayer.drm.MediaDrmCallback; import com.google.android.exoplayer.drm.MediaDrmCallback;
import com.google.android.exoplayer.drm.StreamingDrmSessionManager;
import com.google.android.exoplayer.drm.UnsupportedDrmException;
import com.google.android.exoplayer.smoothstreaming.DefaultSmoothStreamingTrackSelector; import com.google.android.exoplayer.smoothstreaming.DefaultSmoothStreamingTrackSelector;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingChunkSource; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingChunkSource;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifest;
import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser; import com.google.android.exoplayer.smoothstreaming.SmoothStreamingManifestParser;
import com.google.android.exoplayer.text.TextTrackRenderer; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator; import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultHttpDataSource; import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer.upstream.DefaultUriDataSource; import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import com.google.android.exoplayer.util.ManifestFetcher; import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.content.Context; import android.content.Context;
import android.media.AudioManager;
import android.media.MediaCodec;
import android.os.Handler; import android.os.Handler;
import java.io.IOException; import java.io.IOException;
/** /**
* A {@link RendererBuilder} for SmoothStreaming. * A {@link SourceBuilder} for SmoothStreaming.
*/ */
public class SmoothStreamingRendererBuilder implements RendererBuilder { public class SmoothStreamingSourceBuilder implements SourceBuilder {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024; private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int VIDEO_BUFFER_SEGMENTS = 200; private static final int VIDEO_BUFFER_SEGMENTS = 200;
...@@ -68,7 +58,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -68,7 +58,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
private AsyncRendererBuilder currentAsyncBuilder; private AsyncRendererBuilder currentAsyncBuilder;
public SmoothStreamingRendererBuilder(Context context, String userAgent, String url, public SmoothStreamingSourceBuilder(Context context, String userAgent, String url,
MediaDrmCallback drmCallback) { MediaDrmCallback drmCallback) {
this.context = context; this.context = context;
this.userAgent = userAgent; this.userAgent = userAgent;
...@@ -126,7 +116,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -126,7 +116,7 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
return; return;
} }
player.onRenderersError(exception); player.onSourceBuilderError(exception);
} }
@Override @Override
...@@ -135,15 +125,13 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -135,15 +125,13 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
return; return;
} }
Handler mainHandler = player.getMainHandler(); // TODO[REFACTOR]: Bring back DRM support.
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE)); /*
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(mainHandler, player);
// Check drm support if necessary. // Check drm support if necessary.
DrmSessionManager drmSessionManager = null; DrmSessionManager drmSessionManager = null;
if (manifest.protectionElement != null) { if (manifest.protectionElement != null) {
if (Util.SDK_INT < 18) { if (Util.SDK_INT < 18) {
player.onRenderersError( player.onSourceBuilderError(
new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME)); new UnsupportedDrmException(UnsupportedDrmException.REASON_UNSUPPORTED_SCHEME));
return; return;
} }
...@@ -151,10 +139,15 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -151,10 +139,15 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
drmSessionManager = new StreamingDrmSessionManager(manifest.protectionElement.uuid, drmSessionManager = new StreamingDrmSessionManager(manifest.protectionElement.uuid,
player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player); player.getPlaybackLooper(), drmCallback, null, player.getMainHandler(), player);
} catch (UnsupportedDrmException e) { } catch (UnsupportedDrmException e) {
player.onRenderersError(e); player.onSourceBuilderError(e);
return; return;
} }
} }
*/
Handler mainHandler = player.getMainHandler();
BandwidthMeter bandwidthMeter = player.getBandwidthMeter();
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
// Build the video renderer. // Build the video renderer.
DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource videoDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
...@@ -164,9 +157,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -164,9 +157,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl,
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_VIDEO); DemoPlayer.TYPE_VIDEO);
TrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(context, videoSampleSource,
MediaCodecSelector.DEFAULT, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 5000,
drmSessionManager, true, mainHandler, player, 50);
// Build the audio renderer. // Build the audio renderer.
DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource audioDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
...@@ -176,9 +166,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -176,9 +166,6 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, ChunkSampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_AUDIO); DemoPlayer.TYPE_AUDIO);
TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource,
MediaCodecSelector.DEFAULT, drmSessionManager, true, mainHandler, player,
AudioCapabilities.getCapabilities(context), AudioManager.STREAM_MUSIC);
// Build the text renderer. // Build the text renderer.
DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent); DataSource textDataSource = new DefaultUriDataSource(context, bandwidthMeter, userAgent);
...@@ -188,15 +175,10 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder { ...@@ -188,15 +175,10 @@ public class SmoothStreamingRendererBuilder implements RendererBuilder {
ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl, ChunkSampleSource textSampleSource = new ChunkSampleSource(textChunkSource, loadControl,
TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player, TEXT_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, mainHandler, player,
DemoPlayer.TYPE_TEXT); DemoPlayer.TYPE_TEXT);
TrackRenderer textRenderer = new TextTrackRenderer(textSampleSource, player,
mainHandler.getLooper());
// Invoke the callback. // Invoke the callback.
TrackRenderer[] renderers = new TrackRenderer[DemoPlayer.RENDERER_COUNT]; player.onSource(
renderers[DemoPlayer.TYPE_VIDEO] = videoRenderer; new MultiSampleSource(videoSampleSource, audioSampleSource, textSampleSource));
renderers[DemoPlayer.TYPE_AUDIO] = audioRenderer;
renderers[DemoPlayer.TYPE_TEXT] = textRenderer;
player.onRenderers(renderers, bandwidthMeter);
} }
} }
......
# Miscellaneous demos #
This folder contains miscellaneous demo applications. For example applications
that demonstrate use of optional extensions, or more advanced features.
A general purpose ExoPlayer demo application can be found in the [demo](../demo)
folder.
# WebM (VP9/Opus) Software Decoder Demo #
A demo app that shows how to use the ExoPlayer [VP9](../../extensions/vp9) and [Opus](../../extensions/opus) Extensions to enable VP9 and Opus playback in your app by bundling native libraries along with it.
The demo app depends on the VP9 and Opus Extensions being configured built correctly.
// 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.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
lintOptions {
abortOnError false
}
}
dependencies {
compile project(':library')
compile project(':extension-opus')
compile project(':extension-vp9')
}
<?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 kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerDemoMisc-Vp9OpusSw</name>
<comment></comment>
<projects>
</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>
</projectDescription>
<?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.demo.vp9opus"
android:versionCode="1503"
android:versionName="1.5.4"
android:theme="@style/RootTheme">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:glEsVersion="0x00020000"></uses-feature>
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
<application
tools:ignore="UnusedAttribute"
android:label="@string/app_name"
android:largeHeap="true"
android:allowBackup="false"
android:icon="@drawable/ic_launcher">
<activity android:name="com.google.android.exoplayer.demo.vp9opus.SampleChooserActivity"
android:label="@string/app_name"
android:configChanges="keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".PlayerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/app_name"
android:theme="@style/PlayerTheme"/>
</application>
</manifest>
/*
* 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.demo.vp9opus;
import com.google.android.exoplayer.DefaultLoadControl;
import com.google.android.exoplayer.LoadControl;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecSelector;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.chunk.ChunkSampleSource;
import com.google.android.exoplayer.chunk.ChunkSource;
import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator;
import com.google.android.exoplayer.dash.DashChunkSource;
import com.google.android.exoplayer.dash.DefaultDashTrackSelector;
import com.google.android.exoplayer.dash.mpd.AdaptationSet;
import com.google.android.exoplayer.dash.mpd.MediaPresentationDescription;
import com.google.android.exoplayer.dash.mpd.MediaPresentationDescriptionParser;
import com.google.android.exoplayer.dash.mpd.Period;
import com.google.android.exoplayer.dash.mpd.Representation;
import com.google.android.exoplayer.ext.opus.LibopusAudioTrackRenderer;
import com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer;
import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultBandwidthMeter;
import com.google.android.exoplayer.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
import com.google.android.exoplayer.util.ManifestFetcher;
import com.google.android.exoplayer.util.ManifestFetcher.ManifestCallback;
import android.text.TextUtils;
import java.io.IOException;
import java.util.ArrayList;
/**
* Helper class that parses the manifest and builds the track renderers.
*/
public class DashRendererBuilder implements ManifestCallback<MediaPresentationDescription> {
private static final int BUFFER_SEGMENT_SIZE = 64 * 1024;
private static final int VIDEO_BUFFER_SEGMENTS = 200;
private static final int AUDIO_BUFFER_SEGMENTS = 60;
private final String manifestUrl;
private final String userAgent;
private final PlayerActivity player;
public DashRendererBuilder(String manifestUrl, String userAgent, PlayerActivity player) {
this.manifestUrl = manifestUrl;
this.userAgent = userAgent;
this.player = player;
}
public void build() {
MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser();
ManifestFetcher<MediaPresentationDescription> manifestFetcher =
new ManifestFetcher<>(manifestUrl, new DefaultHttpDataSource(userAgent, null), parser);
manifestFetcher.singleLoad(player.getMainHandler().getLooper(), this);
}
@Override
public void onSingleManifestError(IOException e) {
// TODO: do something meaningful here.
e.printStackTrace();
}
@Override
public void onSingleManifest(MediaPresentationDescription manifest) {
LoadControl loadControl = new DefaultLoadControl(new DefaultAllocator(BUFFER_SEGMENT_SIZE));
DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(null, null);
// Obtain Representations for playback.
Representation audioRepresentation = null;
boolean audioRepresentationIsOpus = false;
ArrayList<Representation> videoRepresentationsList = new ArrayList<>();
Period period = manifest.getPeriod(0);
for (int i = 0; i < period.adaptationSets.size(); i++) {
AdaptationSet adaptationSet = period.adaptationSets.get(i);
int adaptationSetType = adaptationSet.type;
for (int j = 0; j < adaptationSet.representations.size(); j++) {
Representation representation = adaptationSet.representations.get(j);
String codecs = representation.format.codecs;
if (adaptationSetType == AdaptationSet.TYPE_AUDIO && audioRepresentation == null) {
audioRepresentation = representation;
audioRepresentationIsOpus = !TextUtils.isEmpty(codecs) && codecs.startsWith("opus");
} else if (adaptationSetType == AdaptationSet.TYPE_VIDEO && !TextUtils.isEmpty(codecs)
&& codecs.startsWith("vp9")) {
videoRepresentationsList.add(representation);
}
}
}
Representation[] videoRepresentations = new Representation[videoRepresentationsList.size()];
videoRepresentationsList.toArray(videoRepresentations);
// Build the video renderer.
LibvpxVideoTrackRenderer videoRenderer = null;
if (!videoRepresentationsList.isEmpty()) {
DataSource videoDataSource = new DefaultUriDataSource(player, bandwidthMeter, userAgent);
ChunkSource videoChunkSource = new DashChunkSource(
DefaultDashTrackSelector.newVideoInstance(null, false, false), videoDataSource,
new AdaptiveEvaluator(bandwidthMeter), manifest.getPeriodDuration(0),
AdaptationSet.TYPE_VIDEO, videoRepresentations);
ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl,
VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE);
videoRenderer = new LibvpxVideoTrackRenderer(videoSampleSource,
true, player.getMainHandler(), player, 50);
}
// Build the audio renderer.
TrackRenderer audioRenderer;
if (audioRepresentation == null) {
audioRenderer = null;
} else {
DataSource audioDataSource = new DefaultUriDataSource(player, bandwidthMeter, userAgent);
DashChunkSource audioChunkSource = new DashChunkSource(
DefaultDashTrackSelector.newAudioInstance(), audioDataSource, null,
manifest.getPeriodDuration(0), AdaptationSet.TYPE_AUDIO, audioRepresentation);
SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl,
AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE);
if (audioRepresentationIsOpus) {
audioRenderer = new LibopusAudioTrackRenderer(audioSampleSource);
} else {
audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource,
MediaCodecSelector.DEFAULT);
}
}
TrackRenderer[] renderers = new TrackRenderer[(audioRenderer == null) ? 1 : 2];
renderers[0] = videoRenderer;
if (audioRenderer != null) {
renderers[1] = audioRenderer;
}
player.onRenderersBuilt(renderers);
}
}
/*
* 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.demo.vp9opus;
import com.google.android.exoplayer.util.Util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
/**
* An activity for selecting from a number of samples.
*/
public class SampleChooserActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_chooser_activity);
ListView sampleList = (ListView) findViewById(R.id.sample_list);
final SampleAdapter sampleAdapter = new SampleAdapter(this);
sampleAdapter.add(new Header("DASH - VP9 Only"));
sampleAdapter.add(new Sample("Google Glass",
"http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9.mpd",
Util.TYPE_DASH));
sampleAdapter.add(new Header("DASH - VP9 and Opus"));
sampleAdapter.add(new Sample("Google Glass",
"http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9_opus.mpd",
Util.TYPE_DASH));
sampleAdapter.add(new Header("DASH - VP9 and Vorbis"));
sampleAdapter.add(new Sample("Google Glass",
"http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9_vorbis.mpd",
Util.TYPE_DASH));
sampleList.setAdapter(sampleAdapter);
sampleList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Object item = sampleAdapter.getItem(position);
if (item instanceof Sample) {
onSampleSelected((Sample) item);
}
}
});
}
private void onSampleSelected(Sample sample) {
Intent playerIntent = new Intent(this, PlayerActivity.class)
.setData(Uri.parse(sample.uri))
.putExtra(PlayerActivity.CONTENT_TYPE_EXTRA, sample.type);
startActivity(playerIntent);
}
private static class SampleAdapter extends ArrayAdapter<Object> {
public SampleAdapter(Context context) {
super(context, 0);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
int layoutId = getItemViewType(position) == 1 ? android.R.layout.simple_list_item_1
: R.layout.sample_chooser_inline_header;
view = LayoutInflater.from(getContext()).inflate(layoutId, null, false);
}
Object item = getItem(position);
String name = null;
if (item instanceof Sample) {
name = ((Sample) item).description;
} else if (item instanceof Header) {
name = ((Header) item).name;
}
((TextView) view).setText(name);
return view;
}
@Override
public int getItemViewType(int position) {
return (getItem(position) instanceof Sample) ? 1 : 0;
}
@Override
public int getViewTypeCount() {
return 2;
}
}
private static class Sample {
public final String description;
public final String uri;
public final int type;
public Sample(String description, String uri, int type) {
this.description = description;
this.uri = uri;
this.type = type;
}
}
private static class Header {
public final String name;
public Header(String name) {
this.name = name;
}
}
}
# 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.reference.1=../../../../library/src/main
android.library.reference.2=../../../../extensions/opus/src/main
android.library.reference.3=../../../../extensions/vp9/src/main
<?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.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:keepScreenOn="true">
<com.google.android.exoplayer.AspectRatioFrameLayout android:id="@+id/video_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center|center_vertical"/>
<com.google.android.exoplayer.ext.vp9.VpxVideoSurfaceView
android:id="@+id/vpx_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center|center_vertical"/>
</com.google.android.exoplayer.AspectRatioFrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#88000000">
<TextView
android:id="@+id/debug_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</FrameLayout>
<?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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rowtext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="25sp"/>
<?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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView android:id="@+id/sample_list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<?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.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textAllCaps="true"
android:textColor="@android:color/white"
android:textSize="14sp"
android:padding="8dp"
android:focusable="true"
android:background="#339999FF"/>
<?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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="RootTheme" parent="android:Theme.Holo"/>
</resources>
<?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.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">WebM ExoPlayer Demo</string>
<string name="choose_file">Choose File</string>
<string name="play">Play</string>
<string name="current_path">
Path: <xliff:g id="path" example="/sdcard/test.webm">%1$s</xliff:g>
</string>
<string name="libvpx_version">
Libvpx: <xliff:g id="path">%1$s</xliff:g>
</string>
<string name="libopus_version">
Libopus: <xliff:g id="path">%1$s</xliff:g>
</string>
<string name="storage_permission_denied">Permission to access storage was denied</string>
</resources>
<?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.
-->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="RootTheme" parent="android:Theme"/>
<style name="PlayerTheme" parent="@style/RootTheme">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/black</item>
</style>
</resources>
# Extensions #
This folder contains optional ExoPlayer extensions.
# ExoPlayer OkHttp Extension #
## Description ##
The OkHttp Extension is an [HttpDataSource][] implementation using Square's [OkHttp][].
[HttpDataSource]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/upstream/HttpDataSource.html
[OkHttp]: https://square.github.io/okhttp/
// 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
}
}
dependencies {
compile project(':library')
compile('com.squareup.okhttp3:okhttp:+') {
exclude group: 'org.json'
}
}
<?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 combineaccessrules="false" exported="true" kind="src" path="/ExoPlayerLib"/>
<classpathentry kind="src" path="java"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerExt-OkHttp</name>
<comment></comment>
<projects>
</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>
</projectDescription>
<?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.okhttp">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
</manifest>
# 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
# ExoPlayer Opus Extension #
## Description ##
The Opus Extension is a [TrackRenderer][] implementation that helps you bundle libopus (the Opus decoding library) into your app and use it along with ExoPlayer to play Opus audio on Android devices.
[TrackRenderer]: https://google.github.io/ExoPlayer/doc/reference/com/google/android/exoplayer/TrackRenderer.html
## Build Instructions (Android Studio and Eclipse) ##
Building the Opus Extension involves building libopus and JNI bindings using the Android NDK and linking it into your app. The following steps will tell you how to do that using Android Studio or Eclipse.
* Checkout ExoPlayer along with Extensions
```
git clone https://github.com/google/ExoPlayer.git
```
* Set the following environment variables:
```
cd "<path to exoplayer checkout>"
EXOPLAYER_ROOT="$(pwd)"
OPUS_EXT_PATH="${EXOPLAYER_ROOT}/extensions/opus/src/main"
```
* Download the [Android NDK][] and set its location in an environment variable:
```
NDK_PATH="<path to Android NDK>"
```
* Fetch libopus
```
cd "${OPUS_EXT_PATH}/jni" && \
git clone git://git.opus-codec.org/opus.git libopus
```
* Run the script to convert arm assembly to NDK compatible format
```
cd ${OPUS_EXT_PATH}/jni && ./convert_android_asm.sh
```
### Android Studio ###
For Android Studio, we build the native libraries from the command line and then Gradle will pick it up when building your app using Android Studio.
* Build the JNI native libraries
```
cd "${OPUS_EXT_PATH}"/jni && \
${NDK_PATH}/ndk-build APP_ABI=all -j4
```
* In your project, you can add a dependency to the Opus Extension by using a rule like this:
```
// in settings.gradle
include ':..:ExoPlayer:library'
include ':..:ExoPlayer:opus-extension'
// in build.gradle
dependencies {
compile project(':..:ExoPlayer:library')
compile project(':..:ExoPlayer:opus-extension')
}
```
* Now, when you build your app, the Opus extension will be built and the native libraries will be packaged along with the APK.
### Eclipse ###
* The following steps assume that you have installed Eclipse and configured it with the [Android SDK][] and [Android NDK ][]:
* Navigate to File->Import->General->Existing Projects into Workspace
* Select the root directory of the repository
* Import the following projects:
* ExoPlayerLib
* ExoPlayerExt-Opus
* If you are able to build ExoPlayerExt-Opus project, then you're all set.
* (Optional) To speed up the NDK build:
* Right click on ExoPlayerExt-Opus in the Project Explorer pane and choose Properties
* Click on C/C++ Build
* Uncheck `Use default build command`
* In `Build Command` enter: `ndk-build -j4` (adjust 4 to a reasonable number depending on the number of cores in your computer)
* Click Apply
You can now create your own Android App project and add ExoPlayerLib along with ExoPlayerExt-Opus as a dependencies to use ExoPlayer along with the Opus Extension.
[Android NDK]: https://developer.android.com/tools/sdk/ndk/index.html
<!---
Work around to point to two different links for the same text.
-->
[Android NDK ]: http://tools.android.com/recent/usingthendkplugin
[Android SDK]: http://developer.android.com/sdk/installing/index.html?pkg=tools
## Building for various Architectures ##
### Android Studio ###
The manual invocation of `ndk-build` will build the library for all architectures and the correct one will be picked up from the APK based on the device its running on.
### Eclipse ###
libopus can be built for the following architectures:
* armeabi (the default - does not include neon optimizations)
* armeabi-v7a (choose this to enable neon optimizations)
* mips
* x86
* all (will result in a larger binary but will cover all architectures)
You can build for a specific architecture in two ways:
* Method 1 (edit `Application.mk`)
* Edit `${OPUS_EXT_PATH}/jni/Application.mk` and add the following line `APP_ABI := <arch>` (where `<arch>` is one of the above 4 architectures)
* Method 2 (pass NDK build flag)
* Right click on ExoPlayerExt-Opus in the Project Explorer pane and choose Properties
* Click on C/C++ Build
* Uncheck `Use default build command`
* In `Build Command` enter: `ndk-build APP_ABI=<arch>` (where `<arch>` is one of the above 4 architectures)
* Click Apply
## Other Things to Note ##
* Every time there is a change to the libopus checkout:
* Arm assembly should be converted by running `convert_android_asm.sh`
* Clean and re-build the project.
* If you want to use your own version of libopus, place it in `${OPUS_EXT_PATH}/jni/libopus`.
// 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')
}
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
<classpathentry kind="src" path="gen"/>
<classpathentry kind="src" path="java"/>
<classpathentry kind="src" path="/ExoPlayerLib"/>
<classpathentry kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="com.android.toolchain.gcc.423224913">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="com.android.toolchain.gcc.423224913" moduleId="org.eclipse.cdt.core.settings" name="Default">
<externalSettings/>
<extensions>
<extension id="org.eclipse.cdt.core.VCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.MakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="com.android.toolchain.gcc.423224913" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="com.android.toolchain.gcc.423224913.1376674556" name="/" resourcePath="">
<toolChain id="com.android.toolchain.gcc.1798416430" name="Android GCC" superClass="com.android.toolchain.gcc">
<targetPlatform binaryParser="org.eclipse.cdt.core.ELF" id="com.android.targetPlatform.1132129264" isAbstract="false" superClass="com.android.targetPlatform"/>
<builder buildPath="${workspace_loc:/ExoPlayerExt-Opus}/jni" id="com.android.builder.532503968" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Android Builder" superClass="com.android.builder">
<outputEntries>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="obj"/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="libs"/>
</outputEntries>
</builder>
<tool id="com.android.gcc.compiler.906450637" name="Android GCC Compiler" superClass="com.android.gcc.compiler">
<inputType id="com.android.gcc.inputType.835889068" superClass="com.android.gcc.inputType"/>
</tool>
</toolChain>
</folderInfo>
<sourceEntries>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="jni"/>
</sourceEntries>
</configuration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="ExoPlayerExt-Opus.null.1840202624" name="ExoPlayerExt-Opus"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="com.android.toolchain.gcc.423224913;com.android.toolchain.gcc.423224913.1376674556;com.android.gcc.compiler.906450637;com.android.gcc.inputType.835889068">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="com.android.AndroidPerProjectProfile"/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="refreshScope" versionNumber="2">
<configuration configurationName="Default">
<resource resourceType="PROJECT" workspacePath="/ExoPlayerExt-Opus"/>
</configuration>
</storageModule>
</cproject>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerExt-Opus</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
<dictionary>
<key>?children?</key>
<value>?name?=outputEntries\|?children?=?name?=entry\\\\\\\|\\\|?name?=entry\\\\\\\|\\\|\||</value>
</dictionary>
<dictionary>
<key>?name?</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.append_environment</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildArguments</key>
<value></value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.buildCommand</key>
<value>ndk-build</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
<value>clean</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.contents</key>
<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
<value>false</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.stopOnError</key>
<value>true</value>
</dictionary>
<dictionary>
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
<value>true</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures>
</projectDescription>
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
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.exoplayer.ext.opus">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23"/>
</manifest>
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.opus;
import com.google.android.exoplayer.ext.opus.OpusDecoderWrapper.OpusHeader;
import java.nio.ByteBuffer;
/**
* JNI Wrapper for the libopus Opus decoder.
*/
/* package */ class OpusDecoder {
private static final boolean IS_AVAILABLE;
static {
boolean isAvailable;
try {
System.loadLibrary("opus");
System.loadLibrary("opusJNI");
isAvailable = true;
} catch (UnsatisfiedLinkError exception) {
isAvailable = false;
}
IS_AVAILABLE = isAvailable;
}
private final long nativeDecoderContext;
/**
* Creates the Opus Decoder.
*
* @param opusHeader OpusHeader used to initialize the decoder.
* @throws OpusDecoderException if the decoder initialization fails.
*/
public OpusDecoder(OpusHeader opusHeader) throws OpusDecoderException {
nativeDecoderContext = opusInit(
opusHeader.sampleRate, opusHeader.channelCount, opusHeader.numStreams,
opusHeader.numCoupled, opusHeader.gain, opusHeader.streamMap);
if (nativeDecoderContext == 0) {
throw new OpusDecoderException("Failed to initialize decoder");
}
}
/**
* Decodes an Opus Encoded Stream.
*
* @param inputBuffer buffer containing the encoded data. Must be allocated using allocateDirect.
* @param inputSize size of the input buffer.
* @param outputBuffer buffer to write the decoded data. Must be allocated using allocateDirect.
* @param outputSize Maximum capacity of the output buffer.
* @return number of decoded bytes.
* @throws OpusDecoderException if decode fails.
*/
public int decode(ByteBuffer inputBuffer, int inputSize, ByteBuffer outputBuffer,
int outputSize) throws OpusDecoderException {
int result = opusDecode(nativeDecoderContext, inputBuffer, inputSize, outputBuffer, outputSize);
if (result < 0) {
throw new OpusDecoderException("Decode error: " + opusGetErrorMessage(result));
}
return result;
}
/**
* Closes the native decoder.
*/
public void close() {
opusClose(nativeDecoderContext);
}
/**
* Resets the native decode on discontinuity (during seek for example).
*/
public void reset() {
opusReset(nativeDecoderContext);
}
/**
* Returns whether the underlying libopus library is available.
*/
public static boolean isLibopusAvailable() {
return IS_AVAILABLE;
}
/**
* Returns the version string of the underlying libopus decoder.
*/
public static native String getLibopusVersion();
private native long opusInit(int sampleRate, int channelCount, int numStreams, int numCoupled,
int gain, byte[] streamMap);
private native int opusDecode(long decoder, ByteBuffer inputBuffer, int inputSize,
ByteBuffer outputBuffer, int outputSize);
private native void opusClose(long decoder);
private native void opusReset(long decoder);
private native String opusGetErrorMessage(int errorCode);
}
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer.ext.opus;
/**
* Thrown when an Opus decoder error occurs.
*/
public class OpusDecoderException extends Exception {
public OpusDecoderException(String message) {
super(message);
}
}
#
# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
WORKING_DIR := $(call my-dir)
include $(CLEAR_VARS)
# build libopus.so
LOCAL_PATH := $(WORKING_DIR)
include libopus.mk
# build libopusJNI.so
include $(CLEAR_VARS)
LOCAL_PATH := $(WORKING_DIR)
LOCAL_MODULE := libopusJNI
LOCAL_ARM_MODE := arm
LOCAL_CPP_EXTENSION := .cc
LOCAL_SRC_FILES := opus_jni.cc
LOCAL_LDLIBS := -llog -lz -lm
LOCAL_SHARED_LIBRARIES := libopus
include $(BUILD_SHARED_LIBRARY)
#
# 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
#!/bin/bash
#
# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
set -e
ASM_CONVERTER="./libopus/celt/arm/arm2gnu.pl"
if [[ ! -x "${ASM_CONVERTER}" ]]; then
echo "Please make sure you have checked out libopus."
exit
fi
while read file; do
# This check is required because the ASM conversion script doesn't seem to be
# idempotent.
if [[ ! "${file}" =~ .*_gnu\.s$ ]]; then
gnu_file="${file%.s}_gnu.s"
${ASM_CONVERTER} "${file}" > "${gnu_file}"
# The ASM conversion script replaces includes with *_gnu.S. So, replace
# occurences of "*-gnu.S" with "*_gnu.s".
perl -pi -e "s/-gnu\.S/_gnu\.s/g" "${gnu_file}"
rm -f "${file}"
fi
done < <(find . -iname '*.s')
# Generate armopts.s from armopts.s.in
sed \
-e "s/@OPUS_ARM_MAY_HAVE_EDSP@/1/g" \
-e "s/@OPUS_ARM_MAY_HAVE_MEDIA@/1/g" \
-e "s/@OPUS_ARM_MAY_HAVE_NEON@/1/g" \
libopus/celt/arm/armopts.s.in > libopus/celt/arm/armopts.s.temp
${ASM_CONVERTER} "libopus/celt/arm/armopts.s.temp" > "libopus/celt/arm/armopts_gnu.s"
rm "libopus/celt/arm/armopts.s.temp"
echo "Converted all ASM files and generated armopts.s successfully."
#
# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)/libopus
include $(CLEAR_VARS)
include $(LOCAL_PATH)/celt_headers.mk
include $(LOCAL_PATH)/celt_sources.mk
include $(LOCAL_PATH)/opus_headers.mk
include $(LOCAL_PATH)/opus_sources.mk
include $(LOCAL_PATH)/silk_headers.mk
include $(LOCAL_PATH)/silk_sources.mk
LOCAL_MODULE := libopus
LOCAL_ARM_MODE := arm
LOCAL_CFLAGS := -DOPUS_BUILD -DFIXED_POINT -DUSE_ALLOCA -DHAVE_LRINT \
-DHAVE_LRINTF
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/src \
$(LOCAL_PATH)/silk $(LOCAL_PATH)/celt \
$(LOCAL_PATH)/silk/fixed
LOCAL_SRC_FILES := $(CELT_SOURCES) $(OPUS_SOURCES) $(OPUS_SOURCES_FLOAT) \
$(SILK_SOURCES) $(SILK_SOURCES_FIXED)
ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
LOCAL_SRC_FILES += $(CELT_SOURCES_ARM)
LOCAL_SRC_FILES += celt/arm/armopts_gnu.s.neon
LOCAL_SRC_FILES += $(subst .s,_gnu.s.neon,$(CELT_SOURCES_ARM_ASM))
LOCAL_CFLAGS += -DOPUS_ARM_ASM -DOPUS_ARM_INLINE_ASM -DOPUS_ARM_INLINE_EDSP \
-DOPUS_ARM_INLINE_MEDIA -DOPUS_ARM_INLINE_NEON \
-DOPUS_ARM_MAY_HAVE_NEON -DOPUS_ARM_MAY_HAVE_MEDIA \
-DOPUS_ARM_MAY_HAVE_EDSP
endif
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(BUILD_SHARED_LIBRARY)
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <jni.h>
#include <android/log.h>
#include <cstdlib>
#include "opus.h" // NOLINT
#include "opus_multistream.h" // NOLINT
#define LOG_TAG "libopus_native"
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
__VA_ARGS__))
#define FUNC(RETURN_TYPE, NAME, ...) \
extern "C" { \
JNIEXPORT RETURN_TYPE \
Java_com_google_android_exoplayer_ext_opus_OpusDecoder_ ## NAME \
(JNIEnv* env, jobject thiz, ##__VA_ARGS__);\
} \
JNIEXPORT RETURN_TYPE \
Java_com_google_android_exoplayer_ext_opus_OpusDecoder_ ## NAME \
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
return JNI_VERSION_1_6;
}
static int channelCount;
FUNC(jlong, opusInit, jint sampleRate, jint channelCount, jint numStreams,
jint numCoupled, jint gain, jbyteArray jStreamMap) {
int status = OPUS_INVALID_STATE;
::channelCount = channelCount;
jbyte* streamMapBytes = env->GetByteArrayElements(jStreamMap, 0);
uint8_t* streamMap = reinterpret_cast<uint8_t*>(streamMapBytes);
OpusMSDecoder* decoder = opus_multistream_decoder_create(
sampleRate, channelCount, numStreams, numCoupled, streamMap, &status);
env->ReleaseByteArrayElements(jStreamMap, streamMapBytes, 0);
if (!decoder || status != OPUS_OK) {
LOGE("Failed to create Opus Decoder; status=%s", opus_strerror(status));
return 0;
}
status = opus_multistream_decoder_ctl(decoder, OPUS_SET_GAIN(gain));
if (status != OPUS_OK) {
LOGE("Failed to set Opus header gain; status=%s", opus_strerror(status));
return 0;
}
return reinterpret_cast<intptr_t>(decoder);
}
FUNC(jint, opusDecode, jlong jDecoder, jobject jInputBuffer, jint inputSize,
jobject jOutputBuffer, jint outputSize) {
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
const uint8_t* inputBuffer =
reinterpret_cast<const uint8_t*>(
env->GetDirectBufferAddress(jInputBuffer));
int16_t* outputBuffer = reinterpret_cast<int16_t*>(
env->GetDirectBufferAddress(jOutputBuffer));
int numFrames = opus_multistream_decode(decoder, inputBuffer, inputSize,
outputBuffer, outputSize, 0);
return (numFrames < 0) ? numFrames : numFrames * 2 * channelCount;
}
FUNC(void, opusClose, jlong jDecoder) {
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
opus_multistream_decoder_destroy(decoder);
}
FUNC(void, opusReset, jlong jDecoder) {
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
}
FUNC(jstring, getLibopusVersion) {
return env->NewStringUTF(opus_get_version_string());
}
FUNC(jstring, opusGetErrorMessage, jint errorCode) {
return env->NewStringUTF(opus_strerror(errorCode));
}
# Proguard rules specific to the Opus extension.
# This prevents the names of native methods from being obfuscated.
-keepclasseswithmembernames class * {
native <methods>;
}
# 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
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.
# 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 (Android Studio and Eclipse) ##
Building the VP9 Extension involves building libvpx and JNI bindings using the Android NDK and linking it into your app. The following steps will tell you how to do that using Android Studio or Eclipse.
* 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}"
```
### Android Studio ###
For Android Studio, we build the native libraries from the command line and then Gradle will pick it up when building your app using Android Studio.
* Build the JNI native libraries
```
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:vp9-extension'
// in build.gradle
dependencies {
compile project(':..:ExoPlayer:library')
compile project(':..:ExoPlayer:vp9-extension')
}
```
* Now, when you build your app, the VP9 extension will be built and the native libraries will be packaged along with the APK.
### Eclipse ###
* The following steps assume that you have installed Eclipse and configured it with the [Android SDK][] and [Android NDK ][]:
* Navigate to File->Import->General->Existing Projects into Workspace
* Select the root directory of the repository
* Import the following projects:
* ExoPlayerLib
* ExoPlayerExt-VP9
* If you are able to build ExoPlayerExt-VP9 project, then you're all set.
* (Optional) To speed up the NDK build:
* Right click on ExoPlayerExt-VP9 in the Project Explorer pane and choose Properties
* Click on C/C++ Build
* Uncheck `Use default build command`
* In `Build Command` enter: `ndk-build -j4` (adjust 4 to a reasonable number depending on the number of cores in your computer)
* Click Apply
You can now create your own Android App project and add ExoPlayerLib along with ExoPlayerExt-VP9 as a dependencies to use ExoPlayer along with the VP9 Extension.
[Android NDK]: https://developer.android.com/tools/sdk/ndk/index.html
<!---
Work around to point to two different links for the same text.
-->
[Android NDK ]: http://tools.android.com/recent/usingthendkplugin
[Android SDK]: http://developer.android.com/sdk/installing/index.html?pkg=tools
## Building for various Architectures ##
### Android Studio ###
The manual invocation of `ndk-build` will build the library for all architectures and the correct one will be picked up from the APK based on the device its running on.
### Eclipse ###
libvpx is optimized for various architectures (like neon, x86, etc.). The `generate_libvpx_android_configs.sh` script generates Android configurations for the following architectures:
* armeabi (the default - does not include neon optimizations)
* armeabi-v7a (choose this to enable neon optimizations)
* mips
* x86
* arm64-v8a
* mips64
* x86_64
* all (will result in a larger binary but will cover all architectures)
You can build for a specific architecture in two ways:
* Method 1 (edit `Application.mk`)
* Edit `${VP9_EXT_PATH}/jni/Application.mk` and add the following line `APP_ABI := <arch>` (where `<arch>` is one of the above 7 architectures)
* Method 2 (pass NDK build flag)
* Right click on ExoPlayerExt-VP9 in the Project Explorer pane and choose Properties
* Click on C/C++ Build
* Uncheck `Use default build command`
* In `Build Command` enter: `ndk-build APP_ABI=<arch>` (where `<arch>` is one of the above 7 architectures)
* Click Apply
## Other Things to Note ##
* 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.
// 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')
}
<?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>
<?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>
<?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>
/*
* 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.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.extractor.ExtractorSampleSource;
import com.google.android.exoplayer.extractor.webm.WebmExtractor;
import com.google.android.exoplayer.upstream.DefaultAllocator;
import com.google.android.exoplayer.upstream.DefaultUriDataSource;
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();
player = ExoPlayer.Factory.newInstance(1);
player.addListener(this);
ExtractorSampleSource sampleSource = new ExtractorSampleSource(
uri, new DefaultUriDataSource(context, Util.getUserAgent(context, "ExoPlayerExtVP9Test")),
new DefaultAllocator(BUFFER_SEGMENT_SIZE), BUFFER_SEGMENT_SIZE * BUFFER_SEGMENT_COUNT,
new WebmExtractor());
LibvpxVideoTrackRenderer videoRenderer = new LibvpxVideoTrackRenderer(sampleSource, true);
player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_OUTPUT_BUFFER_RENDERER,
new VpxVideoSurfaceView(context));
player.prepare(videoRenderer);
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();
}
}
}
# 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
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.
<?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>
<?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>
<?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>
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
<?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>
/*
* 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 java.nio.ByteBuffer;
/**
* JNI Wrapper for the libvpx VP9 decoder.
*/
/* package */ class VpxDecoder {
private 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;
}
public static final int OUTPUT_MODE_UNKNOWN = -1;
public static final int OUTPUT_MODE_YUV = 0;
public static final int OUTPUT_MODE_RGB = 1;
private final long vpxDecContext;
/**
* Creates the VP9 Decoder.
*
* @throws VpxDecoderException if the decoder fails to initialize.
*/
public VpxDecoder() throws VpxDecoderException {
vpxDecContext = vpxInit();
if (vpxDecContext == 0) {
throw new VpxDecoderException("Failed to initialize decoder");
}
}
/**
* Decodes a vp9 encoded frame and converts it to RGB565.
*
* @param encoded The encoded buffer.
* @param size Size of the encoded buffer.
* @param outputBuffer The buffer into which the decoded frame should be written.
* @return 0 on success with a frame to render. 1 on success without a frame to render.
* @throws VpxDecoderException on decode failure.
*/
public int decode(ByteBuffer encoded, int size, VpxOutputBuffer outputBuffer)
throws VpxDecoderException {
if (vpxDecode(vpxDecContext, encoded, size) != 0) {
throw new VpxDecoderException("Decode error: " + vpxGetErrorMessage(vpxDecContext));
}
return vpxGetFrame(vpxDecContext, outputBuffer);
}
/**
* Destroys the decoder.
*/
public void close() {
vpxClose(vpxDecContext);
}
/**
* Returns whether the underlying libvpx library is available.
*/
public static boolean isLibvpxAvailable() {
return IS_AVAILABLE;
}
/**
* Returns the version string of the underlying libvpx decoder.
*/
public static native String getLibvpxVersion();
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);
}
/*
* 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 {
public VpxDecoderException(String message) {
super(message);
}
}
/*
* 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.SampleHolder;
import com.google.android.exoplayer.util.Assertions;
import java.nio.ByteBuffer;
import java.util.LinkedList;
/**
* Wraps {@link VpxDecoder}, exposing a higher level decoder interface.
*/
/* package */ final class VpxDecoderWrapper extends Thread {
public static final int FLAG_END_OF_STREAM = 1;
private static final int INPUT_BUFFER_SIZE = 768 * 1024; // Value based on cs/SoftVpx.cpp.
/**
* 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 final Object lock;
private final LinkedList<VpxInputBuffer> queuedInputBuffers;
private final LinkedList<VpxOutputBuffer> queuedOutputBuffers;
private final VpxInputBuffer[] availableInputBuffers;
private final VpxOutputBuffer[] availableOutputBuffers;
private int availableInputBufferCount;
private int availableOutputBufferCount;
private VpxInputBuffer dequeuedInputBuffer;
private boolean flushDecodedOutputBuffer;
private boolean released;
private int outputMode;
private VpxDecoderException decoderException;
/**
* @param outputMode One of OUTPUT_MODE_* constants from {@link VpxDecoderWrapper}
* depending on the desired output mode.
*/
public VpxDecoderWrapper(int outputMode) {
lock = new Object();
this.outputMode = outputMode;
queuedInputBuffers = new LinkedList<>();
queuedOutputBuffers = new LinkedList<>();
availableInputBuffers = new VpxInputBuffer[NUM_BUFFERS];
availableOutputBuffers = new VpxOutputBuffer[NUM_BUFFERS];
availableInputBufferCount = NUM_BUFFERS;
availableOutputBufferCount = NUM_BUFFERS;
for (int i = 0; i < NUM_BUFFERS; i++) {
availableInputBuffers[i] = new VpxInputBuffer();
availableOutputBuffers[i] = new VpxOutputBuffer(this);
}
}
public void setOutputMode(int outputMode) {
this.outputMode = outputMode;
}
public VpxInputBuffer dequeueInputBuffer() throws VpxDecoderException {
synchronized (lock) {
maybeThrowDecoderError();
Assertions.checkState(dequeuedInputBuffer == null);
if (availableInputBufferCount == 0) {
return null;
}
VpxInputBuffer inputBuffer = availableInputBuffers[--availableInputBufferCount];
inputBuffer.flags = 0;
inputBuffer.sampleHolder.clearData();
dequeuedInputBuffer = inputBuffer;
return inputBuffer;
}
}
public void queueInputBuffer(VpxInputBuffer inputBuffer) throws VpxDecoderException {
synchronized (lock) {
maybeThrowDecoderError();
Assertions.checkArgument(inputBuffer == dequeuedInputBuffer);
queuedInputBuffers.addLast(inputBuffer);
maybeNotifyDecodeLoop();
dequeuedInputBuffer = null;
}
}
public VpxOutputBuffer dequeueOutputBuffer() throws VpxDecoderException {
synchronized (lock) {
maybeThrowDecoderError();
if (queuedOutputBuffers.isEmpty()) {
return null;
}
return queuedOutputBuffers.removeFirst();
}
}
public void releaseOutputBuffer(VpxOutputBuffer outputBuffer) {
synchronized (lock) {
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
maybeNotifyDecodeLoop();
}
}
/**
* Flushes input/output buffers that have not been dequeued yet and returns ownership of any
* dequeued input buffer to the decoder. Flushes any pending output currently in the decoder. The
* caller is still responsible for releasing any dequeued output buffers.
*/
public void flush() {
synchronized (lock) {
flushDecodedOutputBuffer = true;
if (dequeuedInputBuffer != null) {
availableInputBuffers[availableInputBufferCount++] = dequeuedInputBuffer;
dequeuedInputBuffer = null;
}
while (!queuedInputBuffers.isEmpty()) {
availableInputBuffers[availableInputBufferCount++] = queuedInputBuffers.removeFirst();
}
while (!queuedOutputBuffers.isEmpty()) {
availableOutputBuffers[availableOutputBufferCount++] = queuedOutputBuffers.removeFirst();
}
}
}
public void release() {
synchronized (lock) {
released = true;
lock.notify();
}
try {
join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void maybeThrowDecoderError() throws VpxDecoderException {
if (decoderException != null) {
throw decoderException;
}
}
/**
* Notifies the decode loop if there exists a queued input buffer and an available output buffer
* to decode into.
* <p>
* Should only be called whilst synchronized on the lock object.
*/
private void maybeNotifyDecodeLoop() {
if (canDecodeBuffer()) {
lock.notify();
}
}
@Override
public void run() {
VpxDecoder decoder = null;
try {
decoder = new VpxDecoder();
while (decodeBuffer(decoder)) {
// Do nothing.
}
} catch (VpxDecoderException e) {
synchronized (lock) {
decoderException = e;
}
} catch (InterruptedException e) {
// Shouldn't ever happen.
} finally {
if (decoder != null) {
decoder.close();
}
}
}
private boolean decodeBuffer(VpxDecoder decoder) throws InterruptedException,
VpxDecoderException {
VpxInputBuffer inputBuffer;
VpxOutputBuffer outputBuffer;
// Wait until we have an input buffer to decode, and an output buffer to decode into.
synchronized (lock) {
while (!released && !canDecodeBuffer()) {
lock.wait();
}
if (released) {
return false;
}
inputBuffer = queuedInputBuffers.removeFirst();
outputBuffer = availableOutputBuffers[--availableOutputBufferCount];
flushDecodedOutputBuffer = false;
}
// Decode.
int decodeResult = -1;
if (inputBuffer.flags == FLAG_END_OF_STREAM) {
outputBuffer.flags = FLAG_END_OF_STREAM;
} else {
SampleHolder sampleHolder = inputBuffer.sampleHolder;
outputBuffer.timestampUs = sampleHolder.timeUs;
outputBuffer.flags = 0;
outputBuffer.mode = outputMode;
sampleHolder.data.position(sampleHolder.data.position() - sampleHolder.size);
decodeResult = decoder.decode(sampleHolder.data, sampleHolder.size, outputBuffer);
}
synchronized (lock) {
if (flushDecodedOutputBuffer
|| inputBuffer.sampleHolder.isDecodeOnly()
|| decodeResult == 1) {
// In the following cases, we make the output buffer available again rather than queuing it
// to be consumed:
// 1) A flush occured whilst we were decoding.
// 2) The input sample has decodeOnly flag set.
// 3) The decode succeeded, but we did not get any frame back for rendering (happens in case
// of an unpacked altref frame).
availableOutputBuffers[availableOutputBufferCount++] = outputBuffer;
} else {
// Queue the decoded output buffer to be consumed.
queuedOutputBuffers.addLast(outputBuffer);
}
// Make the input buffer available again.
availableInputBuffers[availableInputBufferCount++] = inputBuffer;
}
return true;
}
private boolean canDecodeBuffer() {
return !queuedInputBuffers.isEmpty() && availableOutputBufferCount > 0;
}
/* package */ static final class VpxInputBuffer {
public final SampleHolder sampleHolder;
public int width;
public int height;
public int flags;
public VpxInputBuffer() {
sampleHolder = new SampleHolder(SampleHolder.BUFFER_REPLACEMENT_MODE_DIRECT);
sampleHolder.data = ByteBuffer.allocateDirect(INPUT_BUFFER_SIZE);
}
}
}
/*
* 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 java.nio.ByteBuffer;
/**
* OutputBuffer for storing the video frame.
*/
public final class VpxOutputBuffer {
public static final int COLORSPACE_UNKNOWN = 0;
public static final int COLORSPACE_BT601 = 1;
public static final int COLORSPACE_BT709 = 2;
private final VpxDecoderWrapper decoder;
/* package */ VpxOutputBuffer(VpxDecoderWrapper decoder) {
this.decoder = decoder;
}
/**
* RGB buffer for RGB mode.
*/
public ByteBuffer data;
public long timestampUs;
public int width;
public int height;
public int flags;
/**
* YUV planes for YUV mode.
*/
public ByteBuffer[] yuvPlanes;
public int[] yuvStrides;
public int mode;
public int colorspace;
/**
* Releases the buffer back to the decoder, allowing it to be reused.
*/
public void release() {
decoder.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;
}
}
/*
* 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);
}
/*
* 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;
}
}
/*
* 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();
}
}
#
# 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)
#
# 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
#!/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."
#
# 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)
/*
* 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));
}
# 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.VpxDecoderWrapper$OutputBuffer {
*;
}
# 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
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.
<?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 kind="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerLibTests</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>
<linkedResources>
<link>
<name>libs/dexmaker-1.2.jar</name>
<type>1</type>
<locationURI>$%7BPARENT-3-PROJECT_LOC%7D/third_party/dexmaker/dexmaker-1.2.jar</locationURI>
</link>
<link>
<name>libs/dexmaker-mockito-1.2.jar</name>
<type>1</type>
<locationURI>$%7BPARENT-3-PROJECT_LOC%7D/third_party/dexmaker/dexmaker-mockito-1.2.jar</locationURI>
</link>
<link>
<name>libs/mockito-all-1.9.5.jar</name>
<type>1</type>
<locationURI>$%7BPARENT-3-PROJECT_LOC%7D/third_party/mockito/mockito-all-1.9.5.jar</locationURI>
</link>
</linkedResources>
<filteredResources>
<filter>
<id>1425657306619</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>
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.source=1.7
This file is needed to make sure the libs directory is present.
...@@ -13,4 +13,4 @@ ...@@ -13,4 +13,4 @@
# Project target. # Project target.
target=android-23 target=android-23
android.library=false android.library=false
android.library.reference.1=../main android.library.reference.1=../experimental
<?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="output" path="bin/classes"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ExoPlayerLib</name>
<comment></comment>
<projects>
</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>
</projectDescription>
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.source=1.7
...@@ -27,15 +27,14 @@ import android.media.MediaExtractor; ...@@ -27,15 +27,14 @@ import android.media.MediaExtractor;
public final class C { public final class C {
/** /**
* Represents an unknown microsecond time or duration. * Special microsecond constant representing an unknown time or duration.
*/ */
public static final long UNKNOWN_TIME_US = -1L; public static final long UNKNOWN_TIME_US = -1L;
/** /**
* Represents a microsecond duration whose exact value is unknown, but which should match the * Special microsecond constant representing the end of a source.
* longest of some other known durations.
*/ */
public static final long MATCH_LONGEST_US = -2L; public static final long END_OF_SOURCE_US = -2L;
/** /**
* The number of microseconds in one second. * The number of microseconds in one second.
......
...@@ -25,8 +25,8 @@ package com.google.android.exoplayer; ...@@ -25,8 +25,8 @@ package com.google.android.exoplayer;
public final class DummyTrackRenderer extends TrackRenderer { public final class DummyTrackRenderer extends TrackRenderer {
@Override @Override
protected boolean doPrepare(long positionUs) throws ExoPlaybackException { protected void doPrepare(SampleSource sampleSource) throws ExoPlaybackException {
return true; // Do nothing.
} }
@Override @Override
...@@ -50,11 +50,6 @@ public final class DummyTrackRenderer extends TrackRenderer { ...@@ -50,11 +50,6 @@ public final class DummyTrackRenderer extends TrackRenderer {
} }
@Override @Override
protected void seekTo(long positionUs) {
throw new IllegalStateException();
}
@Override
protected void doSomeWork(long positionUs, long elapsedRealtimeUs) { protected void doSomeWork(long positionUs, long elapsedRealtimeUs) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
...@@ -64,14 +59,4 @@ public final class DummyTrackRenderer extends TrackRenderer { ...@@ -64,14 +59,4 @@ public final class DummyTrackRenderer extends TrackRenderer {
throw new IllegalStateException(); throw new IllegalStateException();
} }
@Override
protected long getDurationUs() {
throw new IllegalStateException();
}
@Override
protected long getBufferedPositionUs() {
throw new IllegalStateException();
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment