Skip to content

Commit

Permalink
Implement MappingAudioSource on Android
Browse files Browse the repository at this point in the history
  • Loading branch information
hacker1024 committed Jul 23, 2022
1 parent 5e45b08 commit 15854ec
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 1 deletion.
Expand Up @@ -31,6 +31,7 @@
import com.google.android.exoplayer2.metadata.icy.IcyInfo;
import com.google.android.exoplayer2.source.ClippingMediaSource;
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
import com.google.android.exoplayer2.source.MaskingMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.ShuffleOrder;
Expand Down Expand Up @@ -62,7 +63,7 @@
import java.util.Map;
import java.util.Random;

public class AudioPlayer implements MethodCallHandler, Player.Listener, MetadataOutput {
public class AudioPlayer implements MethodCallHandler, Player.Listener, MetadataOutput, LazyMediaSourceProvider {

static final String TAG = "AudioPlayer";

Expand Down Expand Up @@ -611,6 +612,17 @@ private MediaSource decodeAudioSource(final Object json) {
.setUri(Uri.parse((String)map.get("uri")))
.setMimeType(MimeTypes.APPLICATION_M3U8)
.build());
case "mapping":
return new MaskingMediaSource(
new LazyMediaSource(
this,
id,
new MediaItem.Builder()
.setTag(id)
.build()
),
true
);
case "silence":
return new SilenceMediaSource.Factory()
.setDurationUs(getLong(map.get("duration")))
Expand Down Expand Up @@ -695,6 +707,29 @@ private DataSource.Factory buildDataSourceFactory() {
return new DefaultDataSource.Factory(context, httpDataSourceFactory);
}

@Override
public void createMediaSource(String id, LazyMediaSourceReceiver receiver) {
handler.post(() -> {
methodChannel.invokeMethod("createMappedAudioSourceSource", Collections.singletonMap("id", id), new Result() {
@Override
public void success(Object json) {
final MediaSource mediaSource = decodeAudioSource(json);
receiver.onMediaSourceCreated(mediaSource);
}

@Override
public void error(String errorCode, String errorMessage, Object errorDetails) {
throw new IllegalStateException("createMappedAudioSourceSource failed. Cannot proceed. (" + errorCode + ", " + errorMessage + ", " + errorDetails + ")");
}

@Override
public void notImplemented() {
throw new IllegalArgumentException("createMappedAudioSourceSource is not implemented by the platform.");
}
});
});
}

private void load(final MediaSource mediaSource, final long initialPosition, final Integer initialIndex, final Result result) {
this.initialPos = initialPosition;
this.initialIndex = initialIndex;
Expand Down
@@ -0,0 +1,160 @@
package com.ryanheise.just_audio;

import android.os.Handler;

import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.Timeline;
import com.google.android.exoplayer2.analytics.PlayerId;
import com.google.android.exoplayer2.drm.DrmSessionEventListener;
import com.google.android.exoplayer2.source.MediaPeriod;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.TransferListener;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
* A {@link MediaSource} that lazily defers to another {@link MediaSource} when it is required.
* <p>
* This {@link MediaSource} must be used with a {@link com.google.android.exoplayer2.source.MaskingMediaSource}.
*/
class LazyMediaSource implements MediaSource {
private final LazyMediaSourceProvider mediaSourceProvider;
public final String id;
public final MediaItem placeholderMediaItem;

private final Map<MediaSourceEventListener, Handler> pendingEventListeners = new HashMap<>();
private final Map<DrmSessionEventListener, Handler> pendingDrmEventListeners = new HashMap<>();

private MediaSource mediaSource;

LazyMediaSource(LazyMediaSourceProvider mediaSourceProvider, String id, MediaItem placeholderMediaItem) {
this.mediaSourceProvider = mediaSourceProvider;
this.id = id;
this.placeholderMediaItem = placeholderMediaItem;
}

@Override
public void addEventListener(Handler handler, MediaSourceEventListener eventListener) {
if (mediaSource == null) {
pendingEventListeners.put(eventListener, handler);
} else {
mediaSource.addEventListener(handler, eventListener);
}
}

@Override
public void removeEventListener(MediaSourceEventListener eventListener) {
if (mediaSource == null) {
pendingEventListeners.remove(eventListener);
} else {
mediaSource.removeEventListener(eventListener);
}
}

@Override
public void addDrmEventListener(Handler handler, DrmSessionEventListener eventListener) {
if (mediaSource == null) {
pendingDrmEventListeners.put(eventListener, handler);
} else {
mediaSource.addDrmEventListener(handler, eventListener);
}
}

@Override
public void removeDrmEventListener(DrmSessionEventListener eventListener) {
if (mediaSource == null) {
pendingDrmEventListeners.remove(eventListener);
} else {
mediaSource.removeDrmEventListener(eventListener);
}
}

@Override
public Timeline getInitialTimeline() {
if (mediaSource == null) return null;
return mediaSource.getInitialTimeline();
}

@Override
public boolean isSingleWindow() {
if (mediaSource == null) return false;
return mediaSource.isSingleWindow();
}

@Override
public MediaItem getMediaItem() {
if (mediaSource == null) {
return placeholderMediaItem;
} else {
return mediaSource.getMediaItem();
}
}

@Override
public void prepareSource(
MediaSourceCaller caller,
TransferListener mediaTransferListener,
PlayerId playerId
) {
mediaSourceProvider.createMediaSource(id, (mediaSource) -> {
this.mediaSource = mediaSource;
for (Map.Entry<MediaSourceEventListener, Handler> entry : pendingEventListeners.entrySet()) {
mediaSource.addEventListener(entry.getValue(), entry.getKey());
}
pendingEventListeners.clear();
for (Map.Entry<DrmSessionEventListener, Handler> entry : pendingDrmEventListeners.entrySet()) {
mediaSource.addDrmEventListener(entry.getValue(), entry.getKey());
}
pendingDrmEventListeners.clear();
mediaSource.prepareSource(caller, mediaTransferListener, playerId);
});
}

@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
if (mediaSource == null) return;
mediaSource.maybeThrowSourceInfoRefreshError();
}

@Override
public void enable(MediaSourceCaller caller) {
if (mediaSource == null) throw new IllegalStateException();
mediaSource.enable(caller);
}

@Override
public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) {
if (mediaSource == null) throw new IllegalStateException();
return mediaSource.createPeriod(id, allocator, startPositionUs);
}

@Override
public void releasePeriod(MediaPeriod mediaPeriod) {
if (mediaSource == null) throw new IllegalStateException();
mediaSource.releasePeriod(mediaPeriod);
}

@Override
public void disable(MediaSourceCaller caller) {
if (mediaSource == null) throw new IllegalStateException();
mediaSource.disable(caller);
}

@Override
public void releaseSource(MediaSourceCaller caller) {
if (mediaSource == null) return;
mediaSource.releaseSource(caller);
}
}

interface LazyMediaSourceReceiver {
void onMediaSourceCreated(MediaSource mediaSource);
}

interface LazyMediaSourceProvider {
void createMediaSource(String id, LazyMediaSourceReceiver receiver);
}

0 comments on commit 15854ec

Please sign in to comment.