mirror of
https://github.com/androidx/media.git
synced 2025-05-04 22:20:47 +08:00
Introduce MetadataDecoderFactory
This is analogous to what we do for text/subtitles, and adds support for playlists where the type of metadata changes from one playlist item to the next. Issue: #2176 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=143948307
This commit is contained in:
parent
ae2e84df35
commit
59ab0fa9f1
@ -278,7 +278,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
|
|||||||
player.addListener(eventLogger);
|
player.addListener(eventLogger);
|
||||||
player.setAudioDebugListener(eventLogger);
|
player.setAudioDebugListener(eventLogger);
|
||||||
player.setVideoDebugListener(eventLogger);
|
player.setVideoDebugListener(eventLogger);
|
||||||
player.setId3Output(eventLogger);
|
player.setMetadataOutput(eventLogger);
|
||||||
|
|
||||||
simpleExoPlayerView.setPlayer(player);
|
simpleExoPlayerView.setPlayer(player);
|
||||||
if (isTimelineStatic) {
|
if (isTimelineStatic) {
|
||||||
|
@ -36,7 +36,6 @@ import com.google.android.exoplayer2.drm.FrameworkMediaCrypto;
|
|||||||
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
import com.google.android.exoplayer2.mediacodec.MediaCodecSelector;
|
||||||
import com.google.android.exoplayer2.metadata.Metadata;
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
import com.google.android.exoplayer2.metadata.MetadataRenderer;
|
||||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
import com.google.android.exoplayer2.text.Cue;
|
import com.google.android.exoplayer2.text.Cue;
|
||||||
@ -448,15 +447,6 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||||||
textOutput = output;
|
textOutput = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Use {@link #setMetadataOutput(MetadataRenderer.Output)} instead.
|
|
||||||
* @param output The output.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void setId3Output(MetadataRenderer.Output output) {
|
|
||||||
setMetadataOutput(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a listener to receive metadata events.
|
* Sets a listener to receive metadata events.
|
||||||
*
|
*
|
||||||
@ -771,7 +761,7 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||||||
protected void buildMetadataRenderers(Context context, Handler mainHandler,
|
protected void buildMetadataRenderers(Context context, Handler mainHandler,
|
||||||
@ExtensionRendererMode int extensionRendererMode, MetadataRenderer.Output output,
|
@ExtensionRendererMode int extensionRendererMode, MetadataRenderer.Output output,
|
||||||
ArrayList<Renderer> out) {
|
ArrayList<Renderer> out) {
|
||||||
out.add(new MetadataRenderer(output, mainHandler.getLooper(), new Id3Decoder()));
|
out.add(new MetadataRenderer(output, mainHandler.getLooper()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 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.exoplayer2.metadata;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.metadata.emsg.EventMessageDecoder;
|
||||||
|
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||||
|
import com.google.android.exoplayer2.metadata.scte35.SpliceInfoDecoder;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory for {@link MetadataDecoder} instances.
|
||||||
|
*/
|
||||||
|
public interface MetadataDecoderFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the factory is able to instantiate a {@link MetadataDecoder} for the given
|
||||||
|
* {@link Format}.
|
||||||
|
*
|
||||||
|
* @param format The {@link Format}.
|
||||||
|
* @return Whether the factory can instantiate a suitable {@link MetadataDecoder}.
|
||||||
|
*/
|
||||||
|
boolean supportsFormat(Format format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link MetadataDecoder} for the given {@link Format}.
|
||||||
|
*
|
||||||
|
* @param format The {@link Format}.
|
||||||
|
* @return A new {@link MetadataDecoder}.
|
||||||
|
* @throws IllegalArgumentException If the {@link Format} is not supported.
|
||||||
|
*/
|
||||||
|
MetadataDecoder createDecoder(Format format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default {@link MetadataDecoder} implementation.
|
||||||
|
* <p>
|
||||||
|
* The formats supported by this factory are:
|
||||||
|
* <ul>
|
||||||
|
* <li>ID3 ({@link Id3Decoder})</li>
|
||||||
|
* <li>EMSG ({@link EventMessageDecoder})</li>
|
||||||
|
* <li>SCTE-35 ({@link SpliceInfoDecoder})</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
MetadataDecoderFactory DEFAULT = new MetadataDecoderFactory() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsFormat(Format format) {
|
||||||
|
return getDecoderClass(format.sampleMimeType) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetadataDecoder createDecoder(Format format) {
|
||||||
|
try {
|
||||||
|
Class<?> clazz = getDecoderClass(format.sampleMimeType);
|
||||||
|
if (clazz == null) {
|
||||||
|
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
|
||||||
|
}
|
||||||
|
return clazz.asSubclass(MetadataDecoder.class).getConstructor().newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Unexpected error instantiating decoder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getDecoderClass(String mimeType) {
|
||||||
|
if (mimeType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
switch (mimeType) {
|
||||||
|
case MimeTypes.APPLICATION_ID3:
|
||||||
|
return Class.forName("com.google.android.exoplayer2.metadata.id3.Id3Decoder");
|
||||||
|
case MimeTypes.APPLICATION_EMSG:
|
||||||
|
return Class.forName("com.google.android.exoplayer2.metadata.emsg.EventMessageDecoder");
|
||||||
|
case MimeTypes.APPLICATION_SCTE35:
|
||||||
|
return Class.forName("com.google.android.exoplayer2.metadata.scte35.SpliceInfoDecoder");
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -49,12 +49,13 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
|
|||||||
|
|
||||||
private static final int MSG_INVOKE_RENDERER = 0;
|
private static final int MSG_INVOKE_RENDERER = 0;
|
||||||
|
|
||||||
private final MetadataDecoder metadataDecoder;
|
private final MetadataDecoderFactory decoderFactory;
|
||||||
private final Output output;
|
private final Output output;
|
||||||
private final Handler outputHandler;
|
private final Handler outputHandler;
|
||||||
private final FormatHolder formatHolder;
|
private final FormatHolder formatHolder;
|
||||||
private final DecoderInputBuffer buffer;
|
private final DecoderInputBuffer buffer;
|
||||||
|
|
||||||
|
private MetadataDecoder decoder;
|
||||||
private boolean inputStreamEnded;
|
private boolean inputStreamEnded;
|
||||||
private long pendingMetadataTimestamp;
|
private long pendingMetadataTimestamp;
|
||||||
private Metadata pendingMetadata;
|
private Metadata pendingMetadata;
|
||||||
@ -66,21 +67,38 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
|
|||||||
* looper associated with the application's main thread, which can be obtained using
|
* looper associated with the application's main thread, which can be obtained using
|
||||||
* {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be
|
* {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be
|
||||||
* called directly on the player's internal rendering thread.
|
* called directly on the player's internal rendering thread.
|
||||||
* @param metadataDecoder A decoder for the metadata.
|
|
||||||
*/
|
*/
|
||||||
public MetadataRenderer(Output output, Looper outputLooper, MetadataDecoder metadataDecoder) {
|
public MetadataRenderer(Output output, Looper outputLooper) {
|
||||||
|
this(output, outputLooper, MetadataDecoderFactory.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param output The output.
|
||||||
|
* @param outputLooper The looper associated with the thread on which the output should be called.
|
||||||
|
* If the output makes use of standard Android UI components, then this should normally be the
|
||||||
|
* looper associated with the application's main thread, which can be obtained using
|
||||||
|
* {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be
|
||||||
|
* called directly on the player's internal rendering thread.
|
||||||
|
* @param decoderFactory A factory from which to obtain {@link MetadataDecoder} instances.
|
||||||
|
*/
|
||||||
|
public MetadataRenderer(Output output, Looper outputLooper,
|
||||||
|
MetadataDecoderFactory decoderFactory) {
|
||||||
super(C.TRACK_TYPE_METADATA);
|
super(C.TRACK_TYPE_METADATA);
|
||||||
this.output = Assertions.checkNotNull(output);
|
this.output = Assertions.checkNotNull(output);
|
||||||
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
|
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
|
||||||
this.metadataDecoder = Assertions.checkNotNull(metadataDecoder);
|
this.decoderFactory = Assertions.checkNotNull(decoderFactory);
|
||||||
formatHolder = new FormatHolder();
|
formatHolder = new FormatHolder();
|
||||||
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int supportsFormat(Format format) {
|
public int supportsFormat(Format format) {
|
||||||
return metadataDecoder.canDecode(format.sampleMimeType) ? FORMAT_HANDLED
|
return decoderFactory.supportsFormat(format) ? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE;
|
||||||
: FORMAT_UNSUPPORTED_TYPE;
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStreamChanged(Format[] formats) throws ExoPlaybackException {
|
||||||
|
decoder = decoderFactory.createDecoder(formats[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -102,7 +120,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
|
|||||||
try {
|
try {
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
ByteBuffer bufferData = buffer.data;
|
ByteBuffer bufferData = buffer.data;
|
||||||
pendingMetadata = metadataDecoder.decode(bufferData.array(), bufferData.limit());
|
pendingMetadata = decoder.decode(bufferData.array(), bufferData.limit());
|
||||||
} catch (MetadataDecoderException e) {
|
} catch (MetadataDecoderException e) {
|
||||||
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
throw ExoPlaybackException.createForRenderer(e, getIndex());
|
||||||
}
|
}
|
||||||
@ -119,6 +137,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
|
|||||||
@Override
|
@Override
|
||||||
protected void onDisabled() {
|
protected void onDisabled() {
|
||||||
pendingMetadata = null;
|
pendingMetadata = null;
|
||||||
|
decoder = null;
|
||||||
super.onDisabled();
|
super.onDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user