diff --git a/.gitignore b/.gitignore index 16f7e8aec9..8ad7cdb8d0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Android generated bin gen +libs +obj lint.xml # IntelliJ IDEA @@ -16,6 +18,7 @@ gen-external-apklibs .classpath .settings .checkstyle +.cproject # Gradle .gradle @@ -37,3 +40,11 @@ proguard-project.txt .DS_Store dist tmp + +# VP9 Extension +extensions/vp9/src/main/jni/libvpx +extensions/vp9/src/main/jni/libvpx_android_configs +extensions/vp9/src/main/jni/libyuv + +# Opus Extension +extensions/opus/src/main/jni/libopus diff --git a/demo_misc/webm_sw_decoder/README.md b/demo_misc/webm_sw_decoder/README.md new file mode 100644 index 0000000000..4714d34084 --- /dev/null +++ b/demo_misc/webm_sw_decoder/README.md @@ -0,0 +1,5 @@ +# 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. diff --git a/demo_misc/webm_sw_decoder/build.gradle b/demo_misc/webm_sw_decoder/build.gradle new file mode 100644 index 0000000000..7c57b342a7 --- /dev/null +++ b/demo_misc/webm_sw_decoder/build.gradle @@ -0,0 +1,40 @@ +// 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 22 + buildToolsVersion "22.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 22 + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } + + lintOptions { + abortOnError false + } +} + +dependencies { + compile project(':library') + compile project(':opus-extension') + compile project(':vp9-extension') +} diff --git a/demo_misc/webm_sw_decoder/src/main/.classpath b/demo_misc/webm_sw_decoder/src/main/.classpath new file mode 100644 index 0000000000..be2dd156ff --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/demo_misc/webm_sw_decoder/src/main/.project b/demo_misc/webm_sw_decoder/src/main/.project new file mode 100644 index 0000000000..4a01439be8 --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/.project @@ -0,0 +1,33 @@ + + + ExoPlayerExt-WebMDemo + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/demo_misc/webm_sw_decoder/src/main/AndroidManifest.xml b/demo_misc/webm_sw_decoder/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..ab7229e990 --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/AndroidManifest.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/DashRendererBuilder.java b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/DashRendererBuilder.java new file mode 100644 index 0000000000..afafc1e639 --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/DashRendererBuilder.java @@ -0,0 +1,152 @@ +/* + * 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.webm; + +import com.google.android.exoplayer.DefaultLoadControl; +import com.google.android.exoplayer.LoadControl; +import com.google.android.exoplayer.MediaCodecAudioTrackRenderer; +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; +import com.google.android.exoplayer.chunk.FormatEvaluator.AdaptiveEvaluator; +import com.google.android.exoplayer.chunk.MultiTrackChunkSource; +import com.google.android.exoplayer.dash.DashChunkSource; +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 com.google.android.exoplayer.util.MimeTypes; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * Helper class that parses the manifest and builds the track renderers. + */ +public class DashRendererBuilder implements ManifestCallback { + + 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 VideoPlayer player; + + public DashRendererBuilder(String manifestUrl, String userAgent, VideoPlayer player) { + this.manifestUrl = manifestUrl; + this.userAgent = userAgent; + this.player = player; + } + + public void build() { + MediaPresentationDescriptionParser parser = new MediaPresentationDescriptionParser(); + ManifestFetcher 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. + ArrayList audioRepresentationsList = new ArrayList<>(); + ArrayList videoRepresentationsList = new ArrayList<>(); + Period period = manifest.periods.get(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); + if (adaptationSetType == AdaptationSet.TYPE_AUDIO) { + audioRepresentationsList.add(representation); + } else if (adaptationSetType == AdaptationSet.TYPE_VIDEO) { + 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; + String mimeType = videoRepresentations[0].format.mimeType; + if (mimeType.equals(MimeTypes.VIDEO_WEBM)) { + videoChunkSource = new DashChunkSource(videoDataSource, + new AdaptiveEvaluator(bandwidthMeter), videoRepresentations); + } else { + throw new IllegalStateException("Unexpected mime type: " + mimeType); + } + ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource, loadControl, + VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true); + videoRenderer = new LibvpxVideoTrackRenderer(videoSampleSource, + true, player.getMainHandler(), player, 50); + } + + // Build the audio renderer. + MultiTrackChunkSource audioChunkSource = null; + TrackRenderer audioRenderer = null; + if (!audioRepresentationsList.isEmpty()) { + DataSource audioDataSource = new DefaultUriDataSource(player, bandwidthMeter, userAgent); + ChunkSource[] audioChunkSources = new ChunkSource[audioRepresentationsList.size()]; + FormatEvaluator audioEvaluator = new FormatEvaluator.FixedEvaluator(); + for (int i = 0; i < audioRepresentationsList.size(); i++) { + Representation representation = audioRepresentationsList.get(i); + audioChunkSources[i] = new DashChunkSource(audioDataSource, + audioEvaluator, representation); + } + audioChunkSource = new MultiTrackChunkSource(audioChunkSources); + SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource, loadControl, + AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true); + if (manifestUrl.contains("opus")) { // TODO: Need a better logic here. + audioRenderer = new LibopusAudioTrackRenderer(audioSampleSource); + } else { + audioRenderer = new MediaCodecAudioTrackRenderer(audioSampleSource); + } + } + + TrackRenderer[] renderers = new TrackRenderer[(audioRenderer == null) ? 1 : 2]; + renderers[0] = videoRenderer; + if (audioRenderer != null) { + renderers[1] = audioRenderer; + } + player.onRenderersBuilt(renderers); + } + +} diff --git a/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/FilePickerActivity.java b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/FilePickerActivity.java new file mode 100644 index 0000000000..c7e5817d19 --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/FilePickerActivity.java @@ -0,0 +1,95 @@ +/* + * 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.webm; + +import android.app.Activity; +import android.app.ListActivity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * A simple file picker. + */ +public class FilePickerActivity extends ListActivity { + + public static final String FILENAME_EXTRA_ID = "filename"; + + private List listItems; + private List itemPaths; + private TextView currentPathView; + private File root; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.file_picker_activity); + setResult(Activity.RESULT_CANCELED); + currentPathView = (TextView) findViewById(R.id.path); + root = new File(Environment.getExternalStorageDirectory().getPath()); + setDirectory(root); + } + + private void setDirectory(File directory) { + currentPathView.setText(getString(R.string.current_path, directory.getAbsolutePath())); + listItems = new ArrayList<>(); + itemPaths = new ArrayList<>(); + File[] files = directory.listFiles(); + + if (!directory.getAbsolutePath().equals(root.getAbsolutePath())) { + listItems.add(root.getAbsolutePath()); + itemPaths.add(root); + listItems.add("../"); + itemPaths.add(new File(directory.getParent())); + } + + if (files != null) { + for (File file : files) { + if (!file.isHidden() && file.canRead()) { + itemPaths.add(file); + if (file.isDirectory()) { + listItems.add(file.getName() + File.separator); + } else { + listItems.add(file.getName()); + } + } + } + } + + setListAdapter(new ArrayAdapter<>(this, R.layout.rows, listItems)); + } + + @Override + protected void onListItemClick(ListView l, View v, int position, long id) { + File file = itemPaths.get(position); + if (file.isDirectory() && file.canRead()) { + setDirectory(itemPaths.get(position)); + } else { + Intent intent = new Intent(); + intent.putExtra(FILENAME_EXTRA_ID, file.getAbsolutePath()); + setResult(Activity.RESULT_OK, intent); + finish(); + } + } +} diff --git a/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/SampleChooserActivity.java b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/SampleChooserActivity.java new file mode 100644 index 0000000000..7f1fa98e5f --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/SampleChooserActivity.java @@ -0,0 +1,145 @@ +/* + * 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.webm; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +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("Local VP9 Video only")); + sampleAdapter.add(new Sample("S/W Color Conversion - upto 720p", false)); + sampleAdapter.add(new Sample("OpenGL", true)); + sampleAdapter.add(new Header("DASH - VP9 Only")); + sampleAdapter.add(new Sample("Google Glass", + "http://demos.webmproject.org/dash/201410/vp9_glass/manifest_vp9.mpd")); + 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")); + 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")); + + 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, VideoPlayer.class) + .putExtra(VideoPlayer.DASH_MANIFEST_URL_ID_EXTRA, sample.uri) + .putExtra(VideoPlayer.USE_OPENGL_ID_EXTRA, sample.useOpenGL); + startActivity(playerIntent); + } + + private static class SampleAdapter extends ArrayAdapter { + + 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 boolean useOpenGL; + + public Sample(String description, boolean useOpenGL) { + this(description, null, useOpenGL); + } + + public Sample(String description, String uri) { + this(description, uri, true); // always use OpenGL for DASH playbacks. + } + + public Sample(String description, String uri, boolean useOpenGL) { + this.description = description; + this.uri = uri; + this.useOpenGL = useOpenGL; + } + + } + + private static class Header { + + public final String name; + + public Header(String name) { + this.name = name; + } + + } + +} diff --git a/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/VideoPlayer.java b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/VideoPlayer.java new file mode 100644 index 0000000000..f6ea05e407 --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/java/com/google/android/exoplayer/demo/webm/VideoPlayer.java @@ -0,0 +1,283 @@ +/* + * 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.webm; + +import com.google.android.exoplayer.ExoPlaybackException; +import com.google.android.exoplayer.ExoPlayer; +import com.google.android.exoplayer.TrackRenderer; +import com.google.android.exoplayer.VideoSurfaceView; +import com.google.android.exoplayer.ext.opus.LibopusAudioTrackRenderer; +import com.google.android.exoplayer.ext.vp9.LibvpxVideoTrackRenderer; +import com.google.android.exoplayer.ext.vp9.VpxDecoderException; +import com.google.android.exoplayer.ext.vp9.VpxVideoSurfaceView; +import com.google.android.exoplayer.extractor.ExtractorSampleSource; +import com.google.android.exoplayer.extractor.webm.WebmExtractor; +import com.google.android.exoplayer.upstream.DefaultUriDataSource; +import com.google.android.exoplayer.util.PlayerControl; +import com.google.android.exoplayer.util.Util; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnTouchListener; +import android.widget.Button; +import android.widget.MediaController; +import android.widget.TextView; +import android.widget.Toast; + +import java.io.File; + +/** + * Sample player that shows how to use ExoPlayer Extensions to playback VP9 Video and Opus Audio. + */ +public class VideoPlayer extends Activity implements OnClickListener, + LibvpxVideoTrackRenderer.EventListener, ExoPlayer.Listener { + + public static final String DASH_MANIFEST_URL_ID_EXTRA = "manifest_url"; + public static final String USE_OPENGL_ID_EXTRA = "use_opengl"; + + private static final int FILE_PICKER_REQUEST = 1; + private static final int EXTRACTOR_BUFFER_SIZE = 10 * 1024 * 1024; + + private boolean isDash; + private String manifestUrl; + private boolean useOpenGL; + private String filename; + + private ExoPlayer player; + private Handler handler; + private MediaController mediaController; + private VideoSurfaceView videoSurfaceView; + private VpxVideoSurfaceView vpxVideoSurfaceView; + private TextView debugInfoView; + private TextView playerStateView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + manifestUrl = intent.getStringExtra(DASH_MANIFEST_URL_ID_EXTRA); + isDash = manifestUrl != null; + useOpenGL = intent.getBooleanExtra(USE_OPENGL_ID_EXTRA, true); + + handler = new Handler(); + + setContentView(R.layout.activity_video_player); + View root = findViewById(R.id.root); + root.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + toggleControlsVisibility(); + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + view.performClick(); + } + return true; + } + }); + + mediaController = new MediaController(this); + mediaController.setAnchorView(root); + videoSurfaceView = (VideoSurfaceView) findViewById(R.id.surface_view); + vpxVideoSurfaceView = (VpxVideoSurfaceView) findViewById(R.id.vpx_surface_view); + debugInfoView = (TextView) findViewById(R.id.debug_info); + playerStateView = (TextView) findViewById(R.id.player_state); + + // Set the buttons' onclick listeners. + ((Button) findViewById(R.id.choose_file)).setOnClickListener(this); + ((Button) findViewById(R.id.play)).setOnClickListener(this); + + // In case of DASH, start playback right away. + if (isDash) { + findViewById(R.id.buttons).setVisibility(View.GONE); + ((TextView) findViewById(R.id.filename)).setVisibility(View.GONE); + startDashPlayback(); + } + } + + @Override + public void onPause() { + super.onPause(); + stopPlayback(); + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.choose_file: + Intent intent = new Intent(); + intent.setClass(this, FilePickerActivity.class); + startActivityForResult(intent, FILE_PICKER_REQUEST); + break; + case R.id.play: + startBasicPlayback(); + break; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case FILE_PICKER_REQUEST: + if (resultCode == Activity.RESULT_OK) { + filename = data.getStringExtra(FilePickerActivity.FILENAME_EXTRA_ID); + ((TextView) findViewById(R.id.filename)).setText( + getString(R.string.current_path, filename)); + } + break; + } + } + + private void startBasicPlayback() { + if (filename == null) { + Toast.makeText(this, "Choose a file!", Toast.LENGTH_SHORT).show(); + return; + } + findViewById(R.id.buttons).setVisibility(View.GONE); + player = ExoPlayer.Factory.newInstance(2); + player.addListener(this); + mediaController.setMediaPlayer(new PlayerControl(player)); + mediaController.setEnabled(true); + ExtractorSampleSource sampleSource = new ExtractorSampleSource( + Uri.fromFile(new File(filename)), + new DefaultUriDataSource(this, Util.getUserAgent(this, "ExoPlayerExtWebMDemo")), + new WebmExtractor(), 2, EXTRACTOR_BUFFER_SIZE); + TrackRenderer videoRenderer = + new LibvpxVideoTrackRenderer(sampleSource, true, handler, this, 50); + if (useOpenGL) { + player.sendMessage(videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_VPX_SURFACE_VIEW, + vpxVideoSurfaceView); + videoSurfaceView.setVisibility(View.GONE); + } else { + player.sendMessage( + videoRenderer, LibvpxVideoTrackRenderer.MSG_SET_SURFACE, + videoSurfaceView.getHolder().getSurface()); + vpxVideoSurfaceView.setVisibility(View.GONE); + } + TrackRenderer audioRenderer = new LibopusAudioTrackRenderer(sampleSource); + player.prepare(videoRenderer, audioRenderer); + player.setPlayWhenReady(true); + } + + private void startDashPlayback() { + playerStateView.setText("Initializing"); + final String userAgent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like" + + " Gecko) Chrome/38.0.2125.104 Safari/537.36"; + DashRendererBuilder rendererBuilder = new DashRendererBuilder(manifestUrl, userAgent, this); + rendererBuilder.build(); + } + + public void onRenderersBuilt(TrackRenderer[] renderers) { + videoSurfaceView.setVisibility(View.GONE); + player = ExoPlayer.Factory.newInstance(renderers.length); + player.addListener(this); + mediaController.setMediaPlayer(new PlayerControl(player)); + mediaController.setEnabled(true); + player.sendMessage(renderers[0], LibvpxVideoTrackRenderer.MSG_SET_VPX_SURFACE_VIEW, + vpxVideoSurfaceView); + player.prepare(renderers); + player.setPlayWhenReady(true); + } + + @Override + public void onDroppedFrames(int count, long elapsed) { + // do nothing. + } + + @Override + public void onVideoSizeChanged(int width, int height) { + if (isDash || useOpenGL) { + vpxVideoSurfaceView.setVideoWidthHeightRatio(height == 0 ? 1 : (width * 1.0f) / height); + } else { + videoSurfaceView.setVideoWidthHeightRatio(height == 0 ? 1 : (width * 1.0f) / height); + } + debugInfoView.setText("Video: " + width + " x " + height); + } + + @Override + public void onDrawnToSurface(Surface surface) { + // do nothing. + } + + @Override + public void onDecoderError(VpxDecoderException e) { + debugInfoView.setText("Libvpx decode failure. Giving up."); + } + + @Override + public void onPlayerStateChanged(boolean playWhenReady, int state) { + String playerState = ""; + switch (player.getPlaybackState()) { + case ExoPlayer.STATE_BUFFERING: + playerState = "buffering"; + break; + case ExoPlayer.STATE_ENDED: + playerState = "ended"; + break; + case ExoPlayer.STATE_IDLE: + playerState = "idle"; + break; + case ExoPlayer.STATE_PREPARING: + playerState = "preparing"; + break; + case ExoPlayer.STATE_READY: + playerState = "ready"; + break; + } + playerStateView.setText("Player State: " + playerState); + } + + @Override + public void onPlayerError(ExoPlaybackException exception) { + debugInfoView.setText("Exoplayer Playback error. Giving up."); + // TODO: show a retry button here. + } + + @Override + public void onPlayWhenReadyCommitted() { + // Do nothing. + } + + public Handler getMainHandler() { + return handler; + } + + private void stopPlayback() { + if (player != null) { + player.stop(); + player.release(); + player = null; + } + } + + private void toggleControlsVisibility() { + if (mediaController != null) { + if (mediaController.isShowing()) { + mediaController.hide(); + } else { + mediaController.show(0); + } + } + } + +} diff --git a/demo_misc/webm_sw_decoder/src/main/project.properties b/demo_misc/webm_sw_decoder/src/main/project.properties new file mode 100644 index 0000000000..ca0c2d0867 --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/project.properties @@ -0,0 +1,17 @@ +# 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-22 +android.library.reference.1=../../../../library/src/main +android.library.reference.2=../../../../extensions/opus/src/main +android.library.reference.3=../../../../extensions/vp9/src/main diff --git a/demo_misc/webm_sw_decoder/src/main/res/drawable-hdpi/ic_launcher.png b/demo_misc/webm_sw_decoder/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000..3e5716b8ad Binary files /dev/null and b/demo_misc/webm_sw_decoder/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/demo_misc/webm_sw_decoder/src/main/res/drawable-mdpi/ic_launcher.png b/demo_misc/webm_sw_decoder/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000000..a5d2a53b13 Binary files /dev/null and b/demo_misc/webm_sw_decoder/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/demo_misc/webm_sw_decoder/src/main/res/drawable-xhdpi/ic_launcher.png b/demo_misc/webm_sw_decoder/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..1d00268635 Binary files /dev/null and b/demo_misc/webm_sw_decoder/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/demo_misc/webm_sw_decoder/src/main/res/drawable-xxhdpi/ic_launcher.png b/demo_misc/webm_sw_decoder/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ef2f312fd4 Binary files /dev/null and b/demo_misc/webm_sw_decoder/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/demo_misc/webm_sw_decoder/src/main/res/drawable-xxxhdpi/ic_launcher.png b/demo_misc/webm_sw_decoder/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0acebb43c1 Binary files /dev/null and b/demo_misc/webm_sw_decoder/src/main/res/drawable-xxxhdpi/ic_launcher.png differ diff --git a/demo_misc/webm_sw_decoder/src/main/res/layout/activity_video_player.xml b/demo_misc/webm_sw_decoder/src/main/res/layout/activity_video_player.xml new file mode 100644 index 0000000000..389e4b789e --- /dev/null +++ b/demo_misc/webm_sw_decoder/src/main/res/layout/activity_video_player.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + +