diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index ce896b8..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-RemoteDecoderTest
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index 97626ba..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 508b3d9..3452509 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -9,14 +9,10 @@
+
-
+
diff --git a/.idea/modules.xml b/.idea/modules.xml
index ddfada6..13b02cb 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -2,8 +2,9 @@
-
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 7c669f2..dc923cd 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,4 +23,5 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
+ compile project(":library-release")
}
diff --git a/app/src/main/java/org/mozilla/remotedecoder/EventLogger.java b/app/src/main/java/org/mozilla/remotedecoder/EventLogger.java
new file mode 100644
index 0000000..c40447f
--- /dev/null
+++ b/app/src/main/java/org/mozilla/remotedecoder/EventLogger.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2016 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 org.mozilla.remotedecoder;
+
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.Surface;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.RendererCapabilities;
+import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.audio.AudioRendererEventListener;
+import com.google.android.exoplayer2.decoder.DecoderCounters;
+import com.google.android.exoplayer2.drm.StreamingDrmSessionManager;
+import com.google.android.exoplayer2.metadata.Metadata;
+import com.google.android.exoplayer2.metadata.MetadataRenderer;
+import com.google.android.exoplayer2.metadata.id3.ApicFrame;
+import com.google.android.exoplayer2.metadata.id3.CommentFrame;
+import com.google.android.exoplayer2.metadata.id3.GeobFrame;
+import com.google.android.exoplayer2.metadata.id3.Id3Frame;
+import com.google.android.exoplayer2.metadata.id3.PrivFrame;
+import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
+import com.google.android.exoplayer2.metadata.id3.TxxxFrame;
+import com.google.android.exoplayer2.source.AdaptiveMediaSourceEventListener;
+import com.google.android.exoplayer2.source.ExtractorMediaSource;
+import com.google.android.exoplayer2.source.TrackGroup;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.trackselection.MappingTrackSelector;
+import com.google.android.exoplayer2.trackselection.MappingTrackSelector.MappedTrackInfo;
+import com.google.android.exoplayer2.trackselection.TrackSelection;
+import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
+import com.google.android.exoplayer2.upstream.DataSpec;
+import com.google.android.exoplayer2.video.VideoRendererEventListener;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+/**
+ * Logs player events using {@link Log}.
+ */
+/* package */ final class EventLogger implements ExoPlayer.EventListener,
+ AudioRendererEventListener, VideoRendererEventListener, AdaptiveMediaSourceEventListener,
+ ExtractorMediaSource.EventListener, StreamingDrmSessionManager.EventListener,
+ MetadataRenderer.Output {
+
+ private static final String TAG = "EventLogger";
+ private static final int MAX_TIMELINE_ITEM_LINES = 3;
+ private static final NumberFormat TIME_FORMAT;
+ static {
+ TIME_FORMAT = NumberFormat.getInstance(Locale.US);
+ TIME_FORMAT.setMinimumFractionDigits(2);
+ TIME_FORMAT.setMaximumFractionDigits(2);
+ TIME_FORMAT.setGroupingUsed(false);
+ }
+
+ private final MappingTrackSelector trackSelector;
+ private final Timeline.Window window;
+ private final Timeline.Period period;
+ private final long startTimeMs;
+
+ public EventLogger(MappingTrackSelector trackSelector) {
+ this.trackSelector = trackSelector;
+ window = new Timeline.Window();
+ period = new Timeline.Period();
+ startTimeMs = SystemClock.elapsedRealtime();
+ }
+
+ // ExoPlayer.EventListener
+
+ @Override
+ public void onLoadingChanged(boolean isLoading) {
+ Log.d(TAG, "loading [" + isLoading + "]");
+ }
+
+ @Override
+ public void onPlayerStateChanged(boolean playWhenReady, int state) {
+ Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", "
+ + getStateString(state) + "]");
+ }
+
+ @Override
+ public void onPositionDiscontinuity() {
+ Log.d(TAG, "positionDiscontinuity");
+ }
+
+ @Override
+ public void onTimelineChanged(Timeline timeline, Object manifest) {
+ if (timeline == null) {
+ return;
+ }
+ int periodCount = timeline.getPeriodCount();
+ int windowCount = timeline.getWindowCount();
+ Log.d(TAG, "sourceInfo [periodCount=" + periodCount + ", windowCount=" + windowCount);
+ for (int i = 0; i < Math.min(periodCount, MAX_TIMELINE_ITEM_LINES); i++) {
+ timeline.getPeriod(i, period);
+ Log.d(TAG, " " + "period [" + getTimeString(period.getDurationMs()) + "]");
+ }
+ if (periodCount > MAX_TIMELINE_ITEM_LINES) {
+ Log.d(TAG, " ...");
+ }
+ for (int i = 0; i < Math.min(windowCount, MAX_TIMELINE_ITEM_LINES); i++) {
+ timeline.getWindow(i, window);
+ Log.d(TAG, " " + "window [" + getTimeString(window.getDurationMs()) + ", "
+ + window.isSeekable + ", " + window.isDynamic + "]");
+ }
+ if (windowCount > MAX_TIMELINE_ITEM_LINES) {
+ Log.d(TAG, " ...");
+ }
+ Log.d(TAG, "]");
+ }
+
+ @Override
+ public void onPlayerError(ExoPlaybackException e) {
+ Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
+ }
+
+ @Override
+ public void onTracksChanged(TrackGroupArray ignored, TrackSelectionArray trackSelections) {
+ MappedTrackInfo mappedTrackInfo = trackSelector.getCurrentMappedTrackInfo();
+ if (mappedTrackInfo == null) {
+ Log.d(TAG, "Tracks []");
+ return;
+ }
+ Log.d(TAG, "Tracks [");
+ // Log tracks associated to renderers.
+ for (int rendererIndex = 0; rendererIndex < mappedTrackInfo.length; rendererIndex++) {
+ TrackGroupArray rendererTrackGroups = mappedTrackInfo.getTrackGroups(rendererIndex);
+ TrackSelection trackSelection = trackSelections.get(rendererIndex);
+ if (rendererTrackGroups.length > 0) {
+ Log.d(TAG, " Renderer:" + rendererIndex + " [");
+ for (int groupIndex = 0; groupIndex < rendererTrackGroups.length; groupIndex++) {
+ TrackGroup trackGroup = rendererTrackGroups.get(groupIndex);
+ String adaptiveSupport = getAdaptiveSupportString(trackGroup.length,
+ mappedTrackInfo.getAdaptiveSupport(rendererIndex, groupIndex, false));
+ Log.d(TAG, " Group:" + groupIndex + ", adaptive_supported=" + adaptiveSupport + " [");
+ for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
+ String status = getTrackStatusString(trackSelection, trackGroup, trackIndex);
+ String formatSupport = getFormatSupportString(
+ mappedTrackInfo.getTrackFormatSupport(rendererIndex, groupIndex, trackIndex));
+ Log.d(TAG, " " + status + " Track:" + trackIndex + ", "
+ + getFormatString(trackGroup.getFormat(trackIndex))
+ + ", supported=" + formatSupport);
+ }
+ Log.d(TAG, " ]");
+ }
+ // Log metadata for at most one of the tracks selected for the renderer.
+ if (trackSelection != null) {
+ for (int selectionIndex = 0; selectionIndex < trackSelection.length(); selectionIndex++) {
+ Metadata metadata = trackSelection.getFormat(selectionIndex).metadata;
+ if (metadata != null) {
+ Log.d(TAG, " Metadata [");
+ printMetadata(metadata, " ");
+ Log.d(TAG, " ]");
+ break;
+ }
+ }
+ }
+ Log.d(TAG, " ]");
+ }
+ }
+ // Log tracks not associated with a renderer.
+ TrackGroupArray unassociatedTrackGroups = mappedTrackInfo.getUnassociatedTrackGroups();
+ if (unassociatedTrackGroups.length > 0) {
+ Log.d(TAG, " Renderer:None [");
+ for (int groupIndex = 0; groupIndex < unassociatedTrackGroups.length; groupIndex++) {
+ Log.d(TAG, " Group:" + groupIndex + " [");
+ TrackGroup trackGroup = unassociatedTrackGroups.get(groupIndex);
+ for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
+ String status = getTrackStatusString(false);
+ String formatSupport = getFormatSupportString(
+ RendererCapabilities.FORMAT_UNSUPPORTED_TYPE);
+ Log.d(TAG, " " + status + " Track:" + trackIndex + ", "
+ + getFormatString(trackGroup.getFormat(trackIndex))
+ + ", supported=" + formatSupport);
+ }
+ Log.d(TAG, " ]");
+ }
+ Log.d(TAG, " ]");
+ }
+ Log.d(TAG, "]");
+ }
+
+ // MetadataRenderer.Output
+
+ @Override
+ public void onMetadata(Metadata metadata) {
+ Log.d(TAG, "onMetadata [");
+ printMetadata(metadata, " ");
+ Log.d(TAG, "]");
+ }
+
+ // AudioRendererEventListener
+
+ @Override
+ public void onAudioEnabled(DecoderCounters counters) {
+ Log.d(TAG, "audioEnabled [" + getSessionTimeString() + "]");
+ }
+
+ @Override
+ public void onAudioSessionId(int audioSessionId) {
+ Log.d(TAG, "audioSessionId [" + audioSessionId + "]");
+ }
+
+ @Override
+ public void onAudioDecoderInitialized(String decoderName, long elapsedRealtimeMs,
+ long initializationDurationMs) {
+ Log.d(TAG, "audioDecoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]");
+ }
+
+ @Override
+ public void onAudioInputFormatChanged(Format format) {
+ Log.d(TAG, "audioFormatChanged [" + getSessionTimeString() + ", " + getFormatString(format)
+ + "]");
+ }
+
+ @Override
+ public void onAudioDisabled(DecoderCounters counters) {
+ Log.d(TAG, "audioDisabled [" + getSessionTimeString() + "]");
+ }
+
+ @Override
+ public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {
+ printInternalError("audioTrackUnderrun [" + bufferSize + ", " + bufferSizeMs + ", "
+ + elapsedSinceLastFeedMs + "]", null);
+ }
+
+ // VideoRendererEventListener
+
+ @Override
+ public void onVideoEnabled(DecoderCounters counters) {
+ Log.d(TAG, "videoEnabled [" + getSessionTimeString() + "]");
+ }
+
+ @Override
+ public void onVideoDecoderInitialized(String decoderName, long elapsedRealtimeMs,
+ long initializationDurationMs) {
+ Log.d(TAG, "videoDecoderInitialized [" + getSessionTimeString() + ", " + decoderName + "]");
+ }
+
+ @Override
+ public void onVideoInputFormatChanged(Format format) {
+ Log.d(TAG, "videoFormatChanged [" + getSessionTimeString() + ", " + getFormatString(format)
+ + "]");
+ }
+
+ @Override
+ public void onVideoDisabled(DecoderCounters counters) {
+ Log.d(TAG, "videoDisabled [" + getSessionTimeString() + "]");
+ }
+
+ @Override
+ public void onDroppedFrames(int count, long elapsed) {
+ Log.d(TAG, "droppedFrames [" + getSessionTimeString() + ", " + count + "]");
+ }
+
+ @Override
+ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees,
+ float pixelWidthHeightRatio) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onRenderedFirstFrame(Surface surface) {
+ // Do nothing.
+ }
+
+ // StreamingDrmSessionManager.EventListener
+
+ @Override
+ public void onDrmSessionManagerError(Exception e) {
+ printInternalError("drmSessionManagerError", e);
+ }
+
+ @Override
+ public void onDrmKeysLoaded() {
+ Log.d(TAG, "drmKeysLoaded [" + getSessionTimeString() + "]");
+ }
+
+ // ExtractorMediaSource.EventListener
+
+ @Override
+ public void onLoadError(IOException error) {
+ printInternalError("loadError", error);
+ }
+
+ // AdaptiveMediaSourceEventListener
+
+ @Override
+ public void onLoadStarted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
+ int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
+ long mediaEndTimeMs, long elapsedRealtimeMs) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onLoadError(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
+ int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
+ long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded,
+ IOException error, boolean wasCanceled) {
+ printInternalError("loadError", error);
+ }
+
+ @Override
+ public void onLoadCanceled(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
+ int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
+ long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onLoadCompleted(DataSpec dataSpec, int dataType, int trackType, Format trackFormat,
+ int trackSelectionReason, Object trackSelectionData, long mediaStartTimeMs,
+ long mediaEndTimeMs, long elapsedRealtimeMs, long loadDurationMs, long bytesLoaded) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onUpstreamDiscarded(int trackType, long mediaStartTimeMs, long mediaEndTimeMs) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onDownstreamFormatChanged(int trackType, Format trackFormat, int trackSelectionReason,
+ Object trackSelectionData, long mediaTimeMs) {
+ // Do nothing.
+ }
+
+ // Internal methods
+
+ private void printInternalError(String type, Exception e) {
+ Log.e(TAG, "internalError [" + getSessionTimeString() + ", " + type + "]", e);
+ }
+
+ private void printMetadata(Metadata metadata, String prefix) {
+ for (int i = 0; i < metadata.length(); i++) {
+ Metadata.Entry entry = metadata.get(i);
+ if (entry instanceof TxxxFrame) {
+ TxxxFrame txxxFrame = (TxxxFrame) entry;
+ Log.d(TAG, prefix + String.format("%s: description=%s, value=%s", txxxFrame.id,
+ txxxFrame.description, txxxFrame.value));
+ } else if (entry instanceof PrivFrame) {
+ PrivFrame privFrame = (PrivFrame) entry;
+ Log.d(TAG, prefix + String.format("%s: owner=%s", privFrame.id, privFrame.owner));
+ } else if (entry instanceof GeobFrame) {
+ GeobFrame geobFrame = (GeobFrame) entry;
+ Log.d(TAG, prefix + String.format("%s: mimeType=%s, filename=%s, description=%s",
+ geobFrame.id, geobFrame.mimeType, geobFrame.filename, geobFrame.description));
+ } else if (entry instanceof ApicFrame) {
+ ApicFrame apicFrame = (ApicFrame) entry;
+ Log.d(TAG, prefix + String.format("%s: mimeType=%s, description=%s",
+ apicFrame.id, apicFrame.mimeType, apicFrame.description));
+ } else if (entry instanceof TextInformationFrame) {
+ TextInformationFrame textInformationFrame = (TextInformationFrame) entry;
+ Log.d(TAG, prefix + String.format("%s: description=%s", textInformationFrame.id,
+ textInformationFrame.description));
+ } else if (entry instanceof CommentFrame) {
+ CommentFrame commentFrame = (CommentFrame) entry;
+ Log.d(TAG, prefix + String.format("%s: language=%s description=%s", commentFrame.id,
+ commentFrame.language, commentFrame.description));
+ } else if (entry instanceof Id3Frame) {
+ Id3Frame id3Frame = (Id3Frame) entry;
+ Log.d(TAG, prefix + String.format("%s", id3Frame.id));
+ }
+ }
+ }
+
+ private String getSessionTimeString() {
+ return getTimeString(SystemClock.elapsedRealtime() - startTimeMs);
+ }
+
+ private static String getTimeString(long timeMs) {
+ return timeMs == C.TIME_UNSET ? "?" : TIME_FORMAT.format((timeMs) / 1000f);
+ }
+
+ private static String getStateString(int state) {
+ switch (state) {
+ case ExoPlayer.STATE_BUFFERING:
+ return "B";
+ case ExoPlayer.STATE_ENDED:
+ return "E";
+ case ExoPlayer.STATE_IDLE:
+ return "I";
+ case ExoPlayer.STATE_READY:
+ return "R";
+ default:
+ return "?";
+ }
+ }
+
+ private static String getFormatSupportString(int formatSupport) {
+ switch (formatSupport) {
+ case RendererCapabilities.FORMAT_HANDLED:
+ return "YES";
+ case RendererCapabilities.FORMAT_EXCEEDS_CAPABILITIES:
+ return "NO_EXCEEDS_CAPABILITIES";
+ case RendererCapabilities.FORMAT_UNSUPPORTED_SUBTYPE:
+ return "NO_UNSUPPORTED_TYPE";
+ case RendererCapabilities.FORMAT_UNSUPPORTED_TYPE:
+ return "NO";
+ default:
+ return "?";
+ }
+ }
+
+ private static String getAdaptiveSupportString(int trackCount, int adaptiveSupport) {
+ if (trackCount < 2) {
+ return "N/A";
+ }
+ switch (adaptiveSupport) {
+ case RendererCapabilities.ADAPTIVE_SEAMLESS:
+ return "YES";
+ case RendererCapabilities.ADAPTIVE_NOT_SEAMLESS:
+ return "YES_NOT_SEAMLESS";
+ case RendererCapabilities.ADAPTIVE_NOT_SUPPORTED:
+ return "NO";
+ default:
+ return "?";
+ }
+ }
+
+ private static String getFormatString(Format format) {
+ if (format == null) {
+ return "null";
+ }
+ StringBuilder builder = new StringBuilder();
+ builder.append("id=").append(format.id).append(", mimeType=").append(format.sampleMimeType);
+ if (format.bitrate != Format.NO_VALUE) {
+ builder.append(", bitrate=").append(format.bitrate);
+ }
+ if (format.width != Format.NO_VALUE && format.height != Format.NO_VALUE) {
+ builder.append(", res=").append(format.width).append("x").append(format.height);
+ }
+ if (format.frameRate != Format.NO_VALUE) {
+ builder.append(", fps=").append(format.frameRate);
+ }
+ if (format.channelCount != Format.NO_VALUE) {
+ builder.append(", channels=").append(format.channelCount);
+ }
+ if (format.sampleRate != Format.NO_VALUE) {
+ builder.append(", sample_rate=").append(format.sampleRate);
+ }
+ if (format.language != null) {
+ builder.append(", language=").append(format.language);
+ }
+ return builder.toString();
+ }
+
+ private static String getTrackStatusString(TrackSelection selection, TrackGroup group,
+ int trackIndex) {
+ return getTrackStatusString(selection != null && selection.getTrackGroup() == group
+ && selection.indexOf(trackIndex) != C.INDEX_UNSET);
+ }
+
+ private static String getTrackStatusString(boolean enabled) {
+ return enabled ? "[X]" : "[ ]";
+ }
+
+}
diff --git a/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsAudioRender.java b/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsAudioRender.java
new file mode 100644
index 0000000..aeda57b
--- /dev/null
+++ b/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsAudioRender.java
@@ -0,0 +1,20 @@
+package org.mozilla.remotedecoder;
+
+import android.util.Log;
+
+import com.google.android.exoplayer2.audio.MediaCodecAudioRenderer;
+import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
+
+/**
+ * Created by kilikkuo on 2/8/17.
+ */
+
+public class GeckoHlsAudioRender extends MediaCodecAudioRenderer {
+ private static final String TAG = "GeckoHlsAudioRender";
+
+ public GeckoHlsAudioRender(MediaCodecSelector mediaCodecSelector) {
+ super(mediaCodecSelector);
+ }
+}
+
+
diff --git a/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsPlayer.java b/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsPlayer.java
new file mode 100644
index 0000000..5d49b12
--- /dev/null
+++ b/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsPlayer.java
@@ -0,0 +1,182 @@
+package org.mozilla.remotedecoder;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Surface;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.DefaultLoadControl;
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.ExoPlayerFactory;
+import com.google.android.exoplayer2.Renderer;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.source.hls.HlsMediaSource;
+import com.google.android.exoplayer2.trackselection.AdaptiveVideoTrackSelection;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.trackselection.TrackSelection;
+import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
+import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;
+import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
+import com.google.android.exoplayer2.upstream.HttpDataSource;
+import com.google.android.exoplayer2.util.Util;
+import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
+
+import java.util.ArrayList;
+
+public class GeckoHlsPlayer implements ExoPlayer.EventListener {
+
+ private static final String TAG = "GeckoHlsPlayer";
+ private static final String HLS_URL = "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8";
+ private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
+ private DataSource.Factory mediaDataSourceFactory;
+ private Timeline.Window window;
+ protected String userAgent;
+ private Handler mainHandler;
+ public final String extension = null;
+ public static final String EXTENSION_EXTRA = "extension";
+ private EventLogger eventLogger;
+
+ private ExoPlayer player;
+// private SimpleExoPlayer player;
+
+ private DefaultTrackSelector trackSelector;
+ private boolean isTimelineStatic = false;
+ private final Renderer[] renderers;
+ private MediaSource mediaSource;
+
+ private SimpleExoPlayerView simpleExoPlayerView;
+
+ public DataSource.Factory buildDataSourceFactory(VideoActivity va, DefaultBandwidthMeter bandwidthMeter) {
+ return new DefaultDataSourceFactory(va, bandwidthMeter,
+ buildHttpDataSourceFactory(bandwidthMeter));
+ }
+
+ public HttpDataSource.Factory buildHttpDataSourceFactory(DefaultBandwidthMeter bandwidthMeter) {
+ return new DefaultHttpDataSourceFactory(userAgent, bandwidthMeter);
+ }
+
+ private MediaSource buildMediaSource(Uri uri, String overrideExtension) {
+ int type = Util.inferContentType(!TextUtils.isEmpty(overrideExtension) ? "." + overrideExtension
+ : uri.getLastPathSegment());
+ switch (type) {
+ case C.TYPE_HLS:
+ return new HlsMediaSource(uri, mediaDataSourceFactory, mainHandler, null);
+ default: {
+ throw new IllegalStateException("Unsupported type: " + type);
+ }
+ }
+ }
+
+ public void setSurface(Surface surface) {
+ player.prepare(mediaSource);
+
+ ExoPlayer.ExoPlayerMessage[] messages = new ExoPlayer.ExoPlayerMessage[1];
+ int count = 0;
+ for (Renderer renderer : renderers) {
+ if (renderer.getTrackType() == C.TRACK_TYPE_VIDEO) {
+ messages[count++] = new ExoPlayer.ExoPlayerMessage(renderer, C.MSG_SET_SURFACE, surface);
+ }
+ }
+
+ if (surface != null) {
+ player.blockingSendMessages(messages);
+ }
+// if (this.surface != null && this.surface != surface) {
+// // If we created this surface, we are responsible for releasing it.
+// if (this.ownsSurface) {
+// this.surface.release();
+// }
+// // We're replacing a surface. Block to ensure that it's not accessed after the method returns.
+// player.blockingSendMessages(messages);
+// } else {
+// player.sendMessages(messages);
+// }
+// this.surface = surface;
+// this.ownsSurface = ownsSurface;
+
+ }
+
+ public GeckoHlsPlayer(VideoActivity va, Intent intent) {
+
+// simpleExoPlayerView = (SimpleExoPlayerView) va.findViewById(R.id.player_view);
+// simpleExoPlayerView.setControllerVisibilityListener(va);
+// simpleExoPlayerView.requestFocus();
+
+ window = new Timeline.Window();
+ TrackSelection.Factory videoTrackSelectionFactory =
+ new AdaptiveVideoTrackSelection.Factory(BANDWIDTH_METER);
+ trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
+
+ ArrayList renderersList = new ArrayList<>();
+ renderersList.add(new GeckoHlsVideoRender(va, MediaCodecSelector.DEFAULT));
+ renderersList.add(new GeckoHlsAudioRender(MediaCodecSelector.DEFAULT));
+ renderers = renderersList.toArray(new Renderer[renderersList.size()]);
+
+ player = ExoPlayerFactory.newInstance(renderers, trackSelector);
+// player = ExoPlayerFactory.newSimpleInstance(va, trackSelector, new DefaultLoadControl());
+ player.addListener(this);
+
+ eventLogger = new EventLogger(trackSelector);
+ player.addListener(eventLogger);
+
+ intent.setData(Uri.parse(HLS_URL));
+ intent.putExtra(EXTENSION_EXTRA, extension);
+
+ Uri[] uris = new Uri[]{intent.getData()};
+ String[] extensions = new String[]{intent.getStringExtra(EXTENSION_EXTRA)};
+ mainHandler = new Handler();
+ userAgent = Util.getUserAgent(va, "RemoteDecoder");
+ mediaDataSourceFactory = buildDataSourceFactory(va, null);
+
+ MediaSource[] mediaSources = new MediaSource[1];
+ mediaSources[0] = buildMediaSource(uris[0], extensions[0]);
+ mediaSource = mediaSources[0];
+// player.prepare(mediaSource);
+
+// simpleExoPlayerView.setPlayer(player);
+
+ }
+
+ @Override
+ public void onLoadingChanged(boolean isLoading) {
+ Log.d(TAG, "loading [" + isLoading + "]");
+ }
+
+ @Override
+ public void onPlayerStateChanged(boolean playWhenReady, int state) {
+// Log.d(TAG, "state [" + getSessionTimeString() + ", " + playWhenReady + ", "
+// + getStateString(state) + "]");
+ if (state == ExoPlayer.STATE_READY) {
+ player.setPlayWhenReady(true);
+ }
+ }
+
+ @Override
+ public void onPositionDiscontinuity() {
+ Log.d(TAG, "positionDiscontinuity");
+ }
+
+ @Override
+ public void onPlayerError(ExoPlaybackException e) {
+// Log.e(TAG, "playerFailed [" + getSessionTimeString() + "]", e);
+ }
+
+ @Override
+ public void onTracksChanged(TrackGroupArray ignored, TrackSelectionArray trackSelections) {
+ }
+
+ @Override
+ public void onTimelineChanged(Timeline timeline, Object manifest) {
+ isTimelineStatic = !timeline.isEmpty()
+ && !timeline.getWindow(timeline.getWindowCount() - 1, window).isDynamic;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsVideoRender.java b/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsVideoRender.java
new file mode 100644
index 0000000..a2aacb5
--- /dev/null
+++ b/app/src/main/java/org/mozilla/remotedecoder/GeckoHlsVideoRender.java
@@ -0,0 +1,21 @@
+package org.mozilla.remotedecoder;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
+import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
+
+/**
+ * Created by kilikkuo on 2/6/17.
+ */
+
+public class GeckoHlsVideoRender extends MediaCodecVideoRenderer {
+ private static final String TAG = "GeckoHlsVideoRender";
+
+ public GeckoHlsVideoRender(Context context, MediaCodecSelector mediaCodecSelector) {
+ super(context, mediaCodecSelector);
+ }
+}
diff --git a/app/src/main/java/org/mozilla/remotedecoder/VideoActivity.java b/app/src/main/java/org/mozilla/remotedecoder/VideoActivity.java
index c6ff2b7..4f32cd4 100644
--- a/app/src/main/java/org/mozilla/remotedecoder/VideoActivity.java
+++ b/app/src/main/java/org/mozilla/remotedecoder/VideoActivity.java
@@ -19,9 +19,15 @@
import android.view.SurfaceView;
import android.view.View;
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.Renderer;
+import com.google.android.exoplayer2.ui.SimpleExoPlayerView;
+
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.media.CodecProxy;
import org.mozilla.gecko.media.Sample;
+import org.mozilla.remotedecoder.GeckoHlsPlayer;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -50,6 +56,8 @@ public class VideoActivity extends AppCompatActivity implements SurfaceHolder.Ca
private CodecWorker mWorker;
+ private GeckoHlsPlayer geckoHlsPlayer;
+
class CodecWorker extends Handler {
public CodecWorker(Looper looper) { super(looper); }
@Override
@@ -129,11 +137,14 @@ protected void onCreate(Bundle savedInstanceState) {
mFrameView = findViewById(R.id.frameView);
mFrameDrawable = mFrameView.getBackground();
mFrameDrawableDeath = new ColorDrawable(getResources().getColor(android.R.color.holo_orange_light));
+
+ geckoHlsPlayer = new GeckoHlsPlayer(this, getIntent());
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mHolder = holder;
+ geckoHlsPlayer.setSurface(mHolder.getSurface());
}
@Override
@@ -146,7 +157,7 @@ public void surfaceChanged(SurfaceHolder holder, int format, int width, int heig
mWorker.post(new Runnable() {
public void run() {
- startDecoding();
+// startDecoding();
}
});
}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index d3314fe..364c2f0 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -15,7 +15,15 @@
android:layout_height="match_parent"
android:id="@+id/videoView"
android:layout_alignParentTop="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentStart="true"
- android:layout_alignParentBottom="true" />
+ android:layout_alignParentStart="true" />
+
+
+
+
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
index 03bced9..74b2ab0 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.0'
+ classpath 'com.android.tools.build:gradle:2.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 122a0dc..0c7949d 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Dec 28 10:00:20 PST 2015
+#Tue Feb 07 15:04:39 CST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
diff --git a/library-release/build.gradle b/library-release/build.gradle
new file mode 100644
index 0000000..df49809
--- /dev/null
+++ b/library-release/build.gradle
@@ -0,0 +1,2 @@
+configurations.maybeCreate("default")
+artifacts.add("default", file('library-release.aar'))
\ No newline at end of file
diff --git a/library-release/library-release.aar b/library-release/library-release.aar
new file mode 100644
index 0000000..a5db866
Binary files /dev/null and b/library-release/library-release.aar differ
diff --git a/settings.gradle b/settings.gradle
index e7b4def..34373c8 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':library-release'