mirror of
https://github.com/samsonjs/media.git
synced 2026-04-16 13:05:46 +00:00
Merge branch 'dev-v2' of persistent-https://github.com/google/ExoPlayer into dev-v2
This commit is contained in:
commit
f903098721
18 changed files with 830 additions and 133 deletions
|
|
@ -262,11 +262,11 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||
// and that's also late. Else we'll render what we have.
|
||||
if ((joiningDeadlineMs != C.TIME_UNSET && outputBuffer.timeUs < positionUs - 30000)
|
||||
|| (nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||
&& nextOutputBuffer.timeUs < positionUs)) {
|
||||
final long nextOutputBufferTimeUs =
|
||||
nextOutputBuffer != null && !nextOutputBuffer.isEndOfStream()
|
||||
? nextOutputBuffer.timeUs : C.TIME_UNSET;
|
||||
if (shouldDropOutputBuffer(
|
||||
outputBuffer.timeUs, nextOutputBufferTimeUs, positionUs, joiningDeadlineMs)) {
|
||||
dropBuffer();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -280,6 +280,25 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the current frame should be dropped.
|
||||
*
|
||||
* @param outputBufferTimeUs The timestamp of the current output buffer.
|
||||
* @param nextOutputBufferTimeUs The timestamp of the next output buffer or
|
||||
* {@link TIME_UNSET} if the next output buffer is unavailable.
|
||||
* @param positionUs The current playback position.
|
||||
* @param joiningDeadlineMs The joining deadline.
|
||||
* @return Returns whether to drop the current output buffer.
|
||||
*/
|
||||
protected boolean shouldDropOutputBuffer(long outputBufferTimeUs, long nextOutputBufferTimeUs,
|
||||
long positionUs, long joiningDeadlineMs) {
|
||||
// Drop the frame if we're joining and are more than 30ms late, or if we have the next frame
|
||||
// and that's also late. Else we'll render what we have.
|
||||
return (joiningDeadlineMs != C.TIME_UNSET && outputBufferTimeUs < positionUs - 30000)
|
||||
|| (nextOutputBufferTimeUs != C.TIME_UNSET && nextOutputBufferTimeUs < positionUs);
|
||||
}
|
||||
|
||||
private void renderBuffer() {
|
||||
int bufferMode = outputBuffer.mode;
|
||||
boolean renderRgb = bufferMode == VpxDecoder.OUTPUT_MODE_RGB && surface != null;
|
||||
|
|
@ -445,13 +464,16 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
protected void onPositionReset(long positionUs, boolean joining) {
|
||||
inputStreamEnded = false;
|
||||
outputStreamEnded = false;
|
||||
renderedFirstFrame = false;
|
||||
clearRenderedFirstFrame();
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
if (decoder != null) {
|
||||
flushDecoder();
|
||||
}
|
||||
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
|
||||
if (joining) {
|
||||
setJoiningDeadlineMs();
|
||||
} else {
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -473,6 +495,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
format = null;
|
||||
waitingForKeys = false;
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
try {
|
||||
releaseDecoder();
|
||||
} finally {
|
||||
|
|
@ -549,28 +572,44 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
private void setOutput(Surface surface, VpxOutputBufferRenderer outputBufferRenderer) {
|
||||
// At most one output may be non-null. Both may be null if the output is being cleared.
|
||||
Assertions.checkState(surface == null || outputBufferRenderer == null);
|
||||
// We only need to update the decoder if the output has changed.
|
||||
if (this.surface != surface || this.outputBufferRenderer != outputBufferRenderer) {
|
||||
// The output has changed.
|
||||
this.surface = surface;
|
||||
this.outputBufferRenderer = outputBufferRenderer;
|
||||
outputMode = outputBufferRenderer != null ? VpxDecoder.OUTPUT_MODE_YUV
|
||||
: surface != null ? VpxDecoder.OUTPUT_MODE_RGB : VpxDecoder.OUTPUT_MODE_NONE;
|
||||
// If outputMode is OUTPUT_MODE_NONE we leave the mode of the underlying decoder unchanged in
|
||||
// anticipation that a subsequent output will likely be of the same type as the one that was
|
||||
// set previously.
|
||||
if (decoder != null && outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
decoder.setOutputMode(outputMode);
|
||||
if (outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
if (decoder != null) {
|
||||
decoder.setOutputMode(outputMode);
|
||||
}
|
||||
// If we know the video size, report it again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
// We haven't rendered to the new output yet.
|
||||
clearRenderedFirstFrame();
|
||||
if (getState() == STATE_STARTED) {
|
||||
setJoiningDeadlineMs();
|
||||
}
|
||||
} else {
|
||||
// The output has been removed. We leave the outputMode of the underlying decoder unchanged
|
||||
// in anticipation that a subsequent output will likely be of the same type.
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
}
|
||||
} else if (outputMode != VpxDecoder.OUTPUT_MODE_NONE) {
|
||||
// The output is unchanged and non-null. If we know the video size and/or have already
|
||||
// rendered to the output, report these again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
maybeRenotifyRenderedFirstFrame();
|
||||
}
|
||||
// Clear state so that we always call the event listener with the video size and when a frame
|
||||
// is rendered, even if the output hasn't changed.
|
||||
renderedFirstFrame = false;
|
||||
clearReportedVideoSize();
|
||||
}
|
||||
|
||||
private void clearReportedVideoSize() {
|
||||
reportedWidth = Format.NO_VALUE;
|
||||
reportedHeight = Format.NO_VALUE;
|
||||
private void setJoiningDeadlineMs() {
|
||||
joiningDeadlineMs = allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
private void clearRenderedFirstFrame() {
|
||||
renderedFirstFrame = false;
|
||||
}
|
||||
|
||||
private void maybeNotifyRenderedFirstFrame() {
|
||||
|
|
@ -580,6 +619,17 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private void maybeRenotifyRenderedFirstFrame() {
|
||||
if (renderedFirstFrame) {
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearReportedVideoSize() {
|
||||
reportedWidth = Format.NO_VALUE;
|
||||
reportedHeight = Format.NO_VALUE;
|
||||
}
|
||||
|
||||
private void maybeNotifyVideoSizeChanged(int width, int height) {
|
||||
if (reportedWidth != width || reportedHeight != height) {
|
||||
reportedWidth = width;
|
||||
|
|
@ -588,6 +638,12 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private void maybeRenotifyVideoSizeChanged() {
|
||||
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
|
||||
eventDispatcher.videoSizeChanged(reportedWidth, reportedHeight, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeNotifyDroppedFrames() {
|
||||
if (droppedFrames > 0) {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import com.google.android.exoplayer2.metadata.id3.TextInformationFrame;
|
|||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.video.ColorInfo;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -61,11 +62,13 @@ public final class FormatTest extends TestCase {
|
|||
Metadata metadata = new Metadata(
|
||||
new TextInformationFrame("id1", "description1", "value1"),
|
||||
new TextInformationFrame("id2", "description2", "value2"));
|
||||
ColorInfo colorInfo = new ColorInfo(C.COLOR_SPACE_BT709,
|
||||
C.COLOR_RANGE_LIMITED, C.COLOR_TRANSFER_SDR, new byte[] {1, 2, 3, 4, 5, 6, 7});
|
||||
|
||||
Format formatToParcel = new Format("id", MimeTypes.VIDEO_MP4, MimeTypes.VIDEO_H264, null,
|
||||
1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, 6, 44100,
|
||||
C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.NO_VALUE, Format.OFFSET_SAMPLE_RELATIVE,
|
||||
INIT_DATA, drmInitData, metadata);
|
||||
1024, 2048, 1920, 1080, 24, 90, 2, projectionData, C.STEREO_MODE_TOP_BOTTOM, colorInfo, 6,
|
||||
44100, C.ENCODING_PCM_24BIT, 1001, 1002, 0, "und", Format.NO_VALUE,
|
||||
Format.OFFSET_SAMPLE_RELATIVE, INIT_DATA, drmInitData, metadata);
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
formatToParcel.writeToParcel(parcel, 0);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import android.content.Context;
|
|||
import android.media.AudioFormat;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaFormat;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.view.Surface;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -554,6 +555,67 @@ public final class C {
|
|||
*/
|
||||
public static final int STEREO_MODE_STEREO_MESH = 3;
|
||||
|
||||
/**
|
||||
* Video colorspaces.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({Format.NO_VALUE, COLOR_SPACE_BT709, COLOR_SPACE_BT601, COLOR_SPACE_BT2020})
|
||||
public @interface ColorSpace {}
|
||||
/**
|
||||
* @see MediaFormat#COLOR_STANDARD_BT709
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_SPACE_BT709 = MediaFormat.COLOR_STANDARD_BT709;
|
||||
/**
|
||||
* @see MediaFormat#COLOR_STANDARD_BT601_PAL
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_SPACE_BT601 = MediaFormat.COLOR_STANDARD_BT601_PAL;
|
||||
/**
|
||||
* @see MediaFormat#COLOR_STANDARD_BT2020
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020;
|
||||
|
||||
/**
|
||||
* Video color transfer characteristics.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({Format.NO_VALUE, COLOR_TRANSFER_SDR, COLOR_TRANSFER_ST2084, COLOR_TRANSFER_HLG})
|
||||
public @interface ColorTransfer {}
|
||||
/**
|
||||
* @see MediaFormat#COLOR_TRANSFER_SDR_VIDEO
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_TRANSFER_SDR = MediaFormat.COLOR_TRANSFER_SDR_VIDEO;
|
||||
/**
|
||||
* @see MediaFormat#COLOR_TRANSFER_ST2084
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_TRANSFER_ST2084 = MediaFormat.COLOR_TRANSFER_ST2084;
|
||||
/**
|
||||
* @see MediaFormat#COLOR_TRANSFER_HLG
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG;
|
||||
|
||||
/**
|
||||
* Video color range.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({Format.NO_VALUE, COLOR_RANGE_LIMITED, COLOR_RANGE_FULL})
|
||||
public @interface ColorRange {}
|
||||
/**
|
||||
* @see MediaFormat#COLOR_RANGE_LIMITED
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_RANGE_LIMITED = MediaFormat.COLOR_RANGE_LIMITED;
|
||||
/**
|
||||
* @see MediaFormat#COLOR_RANGE_FULL
|
||||
*/
|
||||
@SuppressWarnings("InlinedApi")
|
||||
public static final int COLOR_RANGE_FULL = MediaFormat.COLOR_RANGE_FULL;
|
||||
|
||||
/**
|
||||
* Priority for media playback.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import com.google.android.exoplayer2.drm.DrmInitData;
|
|||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.video.ColorInfo;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -128,6 +129,10 @@ public final class Format implements Parcelable {
|
|||
* The projection data for 360/VR video, or null if not applicable.
|
||||
*/
|
||||
public final byte[] projectionData;
|
||||
/**
|
||||
* The color metadata associated with the video, helps with accurate color reproduction.
|
||||
*/
|
||||
public final ColorInfo colorInfo;
|
||||
|
||||
// Audio specific.
|
||||
|
||||
|
|
@ -192,7 +197,7 @@ public final class Format implements Parcelable {
|
|||
String sampleMimeType, String codecs, int bitrate, int width, int height,
|
||||
float frameRate, List<byte[]> initializationData, @C.SelectionFlags int selectionFlags) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, width,
|
||||
height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
height, frameRate, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, selectionFlags, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, null, null);
|
||||
}
|
||||
|
|
@ -210,17 +215,18 @@ public final class Format implements Parcelable {
|
|||
DrmInitData drmInitData) {
|
||||
return createVideoSampleFormat(id, sampleMimeType, codecs, bitrate, maxInputSize, width,
|
||||
height, frameRate, initializationData, rotationDegrees, pixelWidthHeightRatio, null,
|
||||
NO_VALUE, drmInitData);
|
||||
NO_VALUE, null, drmInitData);
|
||||
}
|
||||
|
||||
public static Format createVideoSampleFormat(String id, String sampleMimeType, String codecs,
|
||||
int bitrate, int maxInputSize, int width, int height, float frameRate,
|
||||
List<byte[]> initializationData, int rotationDegrees, float pixelWidthHeightRatio,
|
||||
byte[] projectionData, @C.StereoMode int stereoMode, DrmInitData drmInitData) {
|
||||
byte[] projectionData, @C.StereoMode int stereoMode, ColorInfo colorInfo,
|
||||
DrmInitData drmInitData) {
|
||||
return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, width, height,
|
||||
frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, drmInitData, null);
|
||||
frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
|
||||
colorInfo, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, 0, null, NO_VALUE,
|
||||
OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData, null);
|
||||
}
|
||||
|
||||
// Audio.
|
||||
|
|
@ -229,8 +235,8 @@ public final class Format implements Parcelable {
|
|||
String sampleMimeType, String codecs, int bitrate, int channelCount, int sampleRate,
|
||||
List<byte[]> initializationData, @C.SelectionFlags int selectionFlags, String language) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, null, null);
|
||||
}
|
||||
|
||||
|
|
@ -257,7 +263,7 @@ public final class Format implements Parcelable {
|
|||
List<byte[]> initializationData, DrmInitData drmInitData,
|
||||
@C.SelectionFlags int selectionFlags, String language, Metadata metadata) {
|
||||
return new Format(id, null, sampleMimeType, codecs, bitrate, maxInputSize, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, channelCount, sampleRate, pcmEncoding,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, channelCount, sampleRate, pcmEncoding,
|
||||
encoderDelay, encoderPadding, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
|
@ -275,8 +281,8 @@ public final class Format implements Parcelable {
|
|||
String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags,
|
||||
String language, int accessibilityChannel) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, selectionFlags, language, accessibilityChannel,
|
||||
OFFSET_SAMPLE_RELATIVE, null, null, null);
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +311,7 @@ public final class Format implements Parcelable {
|
|||
int accessibilityChannel, DrmInitData drmInitData, long subsampleOffsetUs,
|
||||
List<byte[]> initializationData) {
|
||||
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, null);
|
||||
}
|
||||
|
|
@ -315,7 +321,7 @@ public final class Format implements Parcelable {
|
|||
public static Format createImageSampleFormat(String id, String sampleMimeType, String codecs,
|
||||
int bitrate, List<byte[]> initializationData, String language, DrmInitData drmInitData) {
|
||||
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, 0, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, initializationData, drmInitData,
|
||||
null);
|
||||
}
|
||||
|
|
@ -326,7 +332,7 @@ public final class Format implements Parcelable {
|
|||
String sampleMimeType, String codecs, int bitrate, @C.SelectionFlags int selectionFlags,
|
||||
String language) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, selectionFlags, language, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, null,
|
||||
null);
|
||||
}
|
||||
|
|
@ -334,22 +340,22 @@ public final class Format implements Parcelable {
|
|||
public static Format createSampleFormat(String id, String sampleMimeType,
|
||||
long subsampleOffsetUs) {
|
||||
return new Format(id, null, sampleMimeType, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, 0, null, NO_VALUE, subsampleOffsetUs, null, null, null);
|
||||
}
|
||||
|
||||
public static Format createSampleFormat(String id, String sampleMimeType, String codecs,
|
||||
int bitrate, DrmInitData drmInitData) {
|
||||
return new Format(id, null, sampleMimeType, codecs, bitrate, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, NO_VALUE, NO_VALUE, null, NO_VALUE, null, NO_VALUE, NO_VALUE, NO_VALUE, NO_VALUE,
|
||||
NO_VALUE, 0, null, NO_VALUE, OFFSET_SAMPLE_RELATIVE, null, drmInitData, null);
|
||||
}
|
||||
|
||||
/* package */ Format(String id, String containerMimeType, String sampleMimeType, String codecs,
|
||||
int bitrate, int maxInputSize, int width, int height, float frameRate, int rotationDegrees,
|
||||
float pixelWidthHeightRatio, byte[] projectionData, @C.StereoMode int stereoMode,
|
||||
int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding, int encoderDelay,
|
||||
int encoderPadding, @C.SelectionFlags int selectionFlags, String language,
|
||||
ColorInfo colorInfo, int channelCount, int sampleRate, @C.PcmEncoding int pcmEncoding,
|
||||
int encoderDelay, int encoderPadding, @C.SelectionFlags int selectionFlags, String language,
|
||||
int accessibilityChannel, long subsampleOffsetUs, List<byte[]> initializationData,
|
||||
DrmInitData drmInitData, Metadata metadata) {
|
||||
this.id = id;
|
||||
|
|
@ -365,6 +371,7 @@ public final class Format implements Parcelable {
|
|||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||
this.projectionData = projectionData;
|
||||
this.stereoMode = stereoMode;
|
||||
this.colorInfo = colorInfo;
|
||||
this.channelCount = channelCount;
|
||||
this.sampleRate = sampleRate;
|
||||
this.pcmEncoding = pcmEncoding;
|
||||
|
|
@ -396,6 +403,7 @@ public final class Format implements Parcelable {
|
|||
boolean hasProjectionData = in.readInt() != 0;
|
||||
projectionData = hasProjectionData ? in.createByteArray() : null;
|
||||
stereoMode = in.readInt();
|
||||
colorInfo = in.readParcelable(ColorInfo.class.getClassLoader());
|
||||
channelCount = in.readInt();
|
||||
sampleRate = in.readInt();
|
||||
pcmEncoding = in.readInt();
|
||||
|
|
@ -417,26 +425,26 @@ public final class Format implements Parcelable {
|
|||
public Format copyWithMaxInputSize(int maxInputSize) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
|
||||
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
|
||||
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay,
|
||||
encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
||||
public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
|
||||
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
|
||||
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay,
|
||||
encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
||||
public Format copyWithContainerInfo(String id, String codecs, int bitrate, int width, int height,
|
||||
@C.SelectionFlags int selectionFlags, String language) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
|
||||
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
|
||||
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay,
|
||||
encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ReferenceEquality")
|
||||
|
|
@ -455,33 +463,33 @@ public final class Format implements Parcelable {
|
|||
: this.drmInitData;
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize, width,
|
||||
height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode,
|
||||
channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding, selectionFlags,
|
||||
language, accessibilityChannel, subsampleOffsetUs, initializationData, drmInitData,
|
||||
metadata);
|
||||
colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
}
|
||||
|
||||
public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
|
||||
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
|
||||
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay,
|
||||
encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
||||
public Format copyWithDrmInitData(DrmInitData drmInitData) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
|
||||
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
|
||||
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay,
|
||||
encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
||||
public Format copyWithMetadata(Metadata metadata) {
|
||||
return new Format(id, containerMimeType, sampleMimeType, codecs, bitrate, maxInputSize,
|
||||
width, height, frameRate, rotationDegrees, pixelWidthHeightRatio, projectionData,
|
||||
stereoMode, channelCount, sampleRate, pcmEncoding, encoderDelay, encoderPadding,
|
||||
selectionFlags, language, accessibilityChannel, subsampleOffsetUs, initializationData,
|
||||
drmInitData, metadata);
|
||||
stereoMode, colorInfo, channelCount, sampleRate, pcmEncoding, encoderDelay,
|
||||
encoderPadding, selectionFlags, language, accessibilityChannel, subsampleOffsetUs,
|
||||
initializationData, drmInitData, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -513,6 +521,7 @@ public final class Format implements Parcelable {
|
|||
for (int i = 0; i < initializationData.size(); i++) {
|
||||
format.setByteBuffer("csd-" + i, ByteBuffer.wrap(initializationData.get(i)));
|
||||
}
|
||||
maybeSetColorInfoV24(format, colorInfo);
|
||||
return format;
|
||||
}
|
||||
|
||||
|
|
@ -569,6 +578,7 @@ public final class Format implements Parcelable {
|
|||
|| !Util.areEqual(codecs, other.codecs)
|
||||
|| !Util.areEqual(drmInitData, other.drmInitData)
|
||||
|| !Util.areEqual(metadata, other.metadata)
|
||||
|| !Util.areEqual(colorInfo, other.colorInfo)
|
||||
|| !Arrays.equals(projectionData, other.projectionData)
|
||||
|| initializationData.size() != other.initializationData.size()) {
|
||||
return false;
|
||||
|
|
@ -581,6 +591,17 @@ public final class Format implements Parcelable {
|
|||
return true;
|
||||
}
|
||||
|
||||
@TargetApi(24)
|
||||
private static void maybeSetColorInfoV24(MediaFormat format, ColorInfo colorInfo) {
|
||||
if (colorInfo == null) {
|
||||
return;
|
||||
}
|
||||
maybeSetIntegerV16(format, MediaFormat.KEY_COLOR_TRANSFER, colorInfo.colorTransfer);
|
||||
maybeSetIntegerV16(format, MediaFormat.KEY_COLOR_STANDARD, colorInfo.colorSpace);
|
||||
maybeSetIntegerV16(format, MediaFormat.KEY_COLOR_RANGE, colorInfo.colorRange);
|
||||
maybeSetByteBufferV16(format, MediaFormat.KEY_HDR_STATIC_INFO, colorInfo.hdrStaticInfo);
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private static void maybeSetStringV16(MediaFormat format, String key, String value) {
|
||||
if (value != null) {
|
||||
|
|
@ -602,6 +623,13 @@ public final class Format implements Parcelable {
|
|||
}
|
||||
}
|
||||
|
||||
@TargetApi(16)
|
||||
private static void maybeSetByteBufferV16(MediaFormat format, String key, byte[] value) {
|
||||
if (value != null) {
|
||||
format.setByteBuffer(key, ByteBuffer.wrap(value));
|
||||
}
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
/**
|
||||
|
|
@ -659,6 +687,7 @@ public final class Format implements Parcelable {
|
|||
dest.writeByteArray(projectionData);
|
||||
}
|
||||
dest.writeInt(stereoMode);
|
||||
dest.writeParcelable(colorInfo, flags);
|
||||
dest.writeInt(channelCount);
|
||||
dest.writeInt(sampleRate);
|
||||
dest.writeInt(pcmEncoding);
|
||||
|
|
|
|||
|
|
@ -239,6 +239,18 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
setVideoSurfaceInternal(surface, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link Surface} onto which video is being rendered if it matches the one passed.
|
||||
* Else does nothing.
|
||||
*
|
||||
* @param surface The surface to clear.
|
||||
*/
|
||||
public void clearVideoSurface(Surface surface) {
|
||||
if (surface != null && surface == this.surface) {
|
||||
setVideoSurface(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SurfaceHolder} that holds the {@link Surface} onto which video will be
|
||||
* rendered. The player will track the lifecycle of the surface automatically.
|
||||
|
|
@ -256,6 +268,18 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link SurfaceHolder} that holds the {@link Surface} onto which video is being
|
||||
* rendered if it matches the one passed. Else does nothing.
|
||||
*
|
||||
* @param surfaceHolder The surface holder to clear.
|
||||
*/
|
||||
public void clearVideoSurfaceHolder(SurfaceHolder surfaceHolder) {
|
||||
if (surfaceHolder != null && surfaceHolder == this.surfaceHolder) {
|
||||
setVideoSurfaceHolder(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SurfaceView} onto which video will be rendered. The player will track the
|
||||
* lifecycle of the surface automatically.
|
||||
|
|
@ -263,7 +287,17 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
* @param surfaceView The surface view.
|
||||
*/
|
||||
public void setVideoSurfaceView(SurfaceView surfaceView) {
|
||||
setVideoSurfaceHolder(surfaceView.getHolder());
|
||||
setVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link SurfaceView} onto which video is being rendered if it matches the one passed.
|
||||
* Else does nothing.
|
||||
*
|
||||
* @param surfaceView The texture view to clear.
|
||||
*/
|
||||
public void clearVideoSurfaceView(SurfaceView surfaceView) {
|
||||
clearVideoSurfaceHolder(surfaceView == null ? null : surfaceView.getHolder());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -287,6 +321,18 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link TextureView} onto which video is being rendered if it matches the one passed.
|
||||
* Else does nothing.
|
||||
*
|
||||
* @param textureView The texture view to clear.
|
||||
*/
|
||||
public void clearVideoTextureView(TextureView textureView) {
|
||||
if (textureView != null && textureView == this.textureView) {
|
||||
setVideoTextureView(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stream type for audio playback (see {@link C.StreamType} and
|
||||
* {@link android.media.AudioTrack#AudioTrack(int, int, int, int, int, int)}). If the stream type
|
||||
|
|
@ -404,6 +450,57 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
videoListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the listener receiving video events if it matches the one passed. Else does nothing.
|
||||
*
|
||||
* @param listener The listener to clear.
|
||||
*/
|
||||
public void clearVideoListener(VideoListener listener) {
|
||||
if (videoListener == listener) {
|
||||
videoListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an output to receive text events.
|
||||
*
|
||||
* @param output The output.
|
||||
*/
|
||||
public void setTextOutput(TextRenderer.Output output) {
|
||||
textOutput = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the output receiving text events if it matches the one passed. Else does nothing.
|
||||
*
|
||||
* @param output The output to clear.
|
||||
*/
|
||||
public void clearTextOutput(TextRenderer.Output output) {
|
||||
if (textOutput == output) {
|
||||
textOutput = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive metadata events.
|
||||
*
|
||||
* @param output The output.
|
||||
*/
|
||||
public void setMetadataOutput(MetadataRenderer.Output output) {
|
||||
metadataOutput = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the output receiving metadata events if it matches the one passed. Else does nothing.
|
||||
*
|
||||
* @param output The output to clear.
|
||||
*/
|
||||
public void clearMetadataOutput(MetadataRenderer.Output output) {
|
||||
if (metadataOutput == output) {
|
||||
metadataOutput = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive debug events from the video renderer.
|
||||
*
|
||||
|
|
@ -422,24 +519,6 @@ public class SimpleExoPlayer implements ExoPlayer {
|
|||
audioDebugListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an output to receive text events.
|
||||
*
|
||||
* @param output The output.
|
||||
*/
|
||||
public void setTextOutput(TextRenderer.Output output) {
|
||||
textOutput = output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a listener to receive metadata events.
|
||||
*
|
||||
* @param output The output.
|
||||
*/
|
||||
public void setMetadataOutput(MetadataRenderer.Output output) {
|
||||
metadataOutput = output;
|
||||
}
|
||||
|
||||
// ExoPlayer implementation
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -64,10 +64,24 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
|||
FLAC_EXTRACTOR_CONSTRUCTOR = flacExtractorConstructor;
|
||||
}
|
||||
|
||||
private @MatroskaExtractor.Flags int matroskaFlags;
|
||||
private @FragmentedMp4Extractor.Flags int fragmentedMp4Flags;
|
||||
private @Mp3Extractor.Flags int mp3Flags;
|
||||
private @DefaultTsPayloadReaderFactory.Flags int tsFlags;
|
||||
|
||||
/**
|
||||
* Sets flags for {@link MatroskaExtractor} instances created by the factory.
|
||||
*
|
||||
* @see MatroskaExtractor#MatroskaExtractor(int)
|
||||
* @param flags The flags to use.
|
||||
* @return The factory, for convenience.
|
||||
*/
|
||||
public synchronized DefaultExtractorsFactory setMatroskaExtractorFlags(
|
||||
@MatroskaExtractor.Flags int flags) {
|
||||
this.matroskaFlags = flags;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags for {@link FragmentedMp4Extractor} instances created by the factory.
|
||||
*
|
||||
|
|
@ -110,7 +124,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
|||
@Override
|
||||
public synchronized Extractor[] createExtractors() {
|
||||
Extractor[] extractors = new Extractor[FLAC_EXTRACTOR_CONSTRUCTOR == null ? 11 : 12];
|
||||
extractors[0] = new MatroskaExtractor();
|
||||
extractors[0] = new MatroskaExtractor(matroskaFlags);
|
||||
extractors[1] = new FragmentedMp4Extractor(fragmentedMp4Flags);
|
||||
extractors[2] = new Mp4Extractor();
|
||||
extractors[3] = new Mp3Extractor(mp3Flags);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.extractor.mkv;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
import android.util.SparseArray;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
|
|
@ -36,8 +37,11 @@ import com.google.android.exoplayer2.util.NalUnitUtil;
|
|||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.android.exoplayer2.video.AvcConfig;
|
||||
import com.google.android.exoplayer2.video.ColorInfo;
|
||||
import com.google.android.exoplayer2.video.HevcConfig;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -64,6 +68,22 @@ public final class MatroskaExtractor implements Extractor {
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags controlling the behavior of the extractor.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(flag = true, value = {FLAG_DISABLE_SEEK_FOR_CUES})
|
||||
public @interface Flags {}
|
||||
/**
|
||||
* Flag to disable seeking for cues.
|
||||
* <p>
|
||||
* Normally (i.e. when this flag is not set) the extractor will seek to the cues element if its
|
||||
* position is specified in the seek head and if it's after the first cluster. Setting this flag
|
||||
* disables seeking to the cues element. If the cues element is after the first cluster then the
|
||||
* media is treated as being unseekable.
|
||||
*/
|
||||
public static final int FLAG_DISABLE_SEEK_FOR_CUES = 1;
|
||||
|
||||
private static final int UNSET_ENTRY_ID = -1;
|
||||
|
||||
private static final int BLOCK_STATE_START = 0;
|
||||
|
|
@ -168,6 +188,23 @@ public final class MatroskaExtractor implements Extractor {
|
|||
private static final int ID_PROJECTION = 0x7670;
|
||||
private static final int ID_PROJECTION_PRIVATE = 0x7672;
|
||||
private static final int ID_STEREO_MODE = 0x53B8;
|
||||
private static final int ID_COLOUR = 0x55B0;
|
||||
private static final int ID_COLOUR_RANGE = 0x55B9;
|
||||
private static final int ID_COLOUR_TRANSFER = 0x55BA;
|
||||
private static final int ID_COLOUR_PRIMARIES = 0x55BB;
|
||||
private static final int ID_MAX_CLL = 0x55BC;
|
||||
private static final int ID_MAX_FALL = 0x55BD;
|
||||
private static final int ID_MASTERING_METADATA = 0x55D0;
|
||||
private static final int ID_PRIMARY_R_CHROMATICITY_X = 0x55D1;
|
||||
private static final int ID_PRIMARY_R_CHROMATICITY_Y = 0x55D2;
|
||||
private static final int ID_PRIMARY_G_CHROMATICITY_X = 0x55D3;
|
||||
private static final int ID_PRIMARY_G_CHROMATICITY_Y = 0x55D4;
|
||||
private static final int ID_PRIMARY_B_CHROMATICITY_X = 0x55D5;
|
||||
private static final int ID_PRIMARY_B_CHROMATICITY_Y = 0x55D6;
|
||||
private static final int ID_WHITE_POINT_CHROMATICITY_X = 0x55D7;
|
||||
private static final int ID_WHITE_POINT_CHROMATICITY_Y = 0x55D8;
|
||||
private static final int ID_LUMNINANCE_MAX = 0x55D9;
|
||||
private static final int ID_LUMNINANCE_MIN = 0x55DA;
|
||||
|
||||
private static final int LACING_NONE = 0;
|
||||
private static final int LACING_XIPH = 1;
|
||||
|
|
@ -222,6 +259,7 @@ public final class MatroskaExtractor implements Extractor {
|
|||
private final EbmlReader reader;
|
||||
private final VarintReader varintReader;
|
||||
private final SparseArray<Track> tracks;
|
||||
private final boolean seekForCuesEnabled;
|
||||
|
||||
// Temporary arrays.
|
||||
private final ParsableByteArray nalStartCode;
|
||||
|
|
@ -289,12 +327,17 @@ public final class MatroskaExtractor implements Extractor {
|
|||
private ExtractorOutput extractorOutput;
|
||||
|
||||
public MatroskaExtractor() {
|
||||
this(new DefaultEbmlReader());
|
||||
this(0);
|
||||
}
|
||||
|
||||
/* package */ MatroskaExtractor(EbmlReader reader) {
|
||||
public MatroskaExtractor(@Flags int flags) {
|
||||
this(new DefaultEbmlReader(), flags);
|
||||
}
|
||||
|
||||
/* package */ MatroskaExtractor(EbmlReader reader, @Flags int flags) {
|
||||
this.reader = reader;
|
||||
this.reader.init(new InnerEbmlReaderOutput());
|
||||
seekForCuesEnabled = (flags & FLAG_DISABLE_SEEK_FOR_CUES) == 0;
|
||||
varintReader = new VarintReader();
|
||||
tracks = new SparseArray<>();
|
||||
scratch = new ParsableByteArray(4);
|
||||
|
|
@ -368,6 +411,8 @@ public final class MatroskaExtractor implements Extractor {
|
|||
case ID_CUE_TRACK_POSITIONS:
|
||||
case ID_BLOCK_GROUP:
|
||||
case ID_PROJECTION:
|
||||
case ID_COLOUR:
|
||||
case ID_MASTERING_METADATA:
|
||||
return EbmlReader.TYPE_MASTER;
|
||||
case ID_EBML_READ_VERSION:
|
||||
case ID_DOC_TYPE_READ_VERSION:
|
||||
|
|
@ -398,6 +443,11 @@ public final class MatroskaExtractor implements Extractor {
|
|||
case ID_CUE_CLUSTER_POSITION:
|
||||
case ID_REFERENCE_BLOCK:
|
||||
case ID_STEREO_MODE:
|
||||
case ID_COLOUR_RANGE:
|
||||
case ID_COLOUR_TRANSFER:
|
||||
case ID_COLOUR_PRIMARIES:
|
||||
case ID_MAX_CLL:
|
||||
case ID_MAX_FALL:
|
||||
return EbmlReader.TYPE_UNSIGNED_INT;
|
||||
case ID_DOC_TYPE:
|
||||
case ID_CODEC_ID:
|
||||
|
|
@ -413,6 +463,16 @@ public final class MatroskaExtractor implements Extractor {
|
|||
return EbmlReader.TYPE_BINARY;
|
||||
case ID_DURATION:
|
||||
case ID_SAMPLING_FREQUENCY:
|
||||
case ID_PRIMARY_R_CHROMATICITY_X:
|
||||
case ID_PRIMARY_R_CHROMATICITY_Y:
|
||||
case ID_PRIMARY_G_CHROMATICITY_X:
|
||||
case ID_PRIMARY_G_CHROMATICITY_Y:
|
||||
case ID_PRIMARY_B_CHROMATICITY_X:
|
||||
case ID_PRIMARY_B_CHROMATICITY_Y:
|
||||
case ID_WHITE_POINT_CHROMATICITY_X:
|
||||
case ID_WHITE_POINT_CHROMATICITY_Y:
|
||||
case ID_LUMNINANCE_MAX:
|
||||
case ID_LUMNINANCE_MIN:
|
||||
return EbmlReader.TYPE_FLOAT;
|
||||
default:
|
||||
return EbmlReader.TYPE_UNKNOWN;
|
||||
|
|
@ -448,7 +508,7 @@ public final class MatroskaExtractor implements Extractor {
|
|||
case ID_CLUSTER:
|
||||
if (!sentSeekMap) {
|
||||
// We need to build cues before parsing the cluster.
|
||||
if (cuesContentPosition != C.POSITION_UNSET) {
|
||||
if (seekForCuesEnabled && cuesContentPosition != C.POSITION_UNSET) {
|
||||
// We know where the Cues element is located. Seek to request it.
|
||||
seekForCues = true;
|
||||
} else {
|
||||
|
|
@ -471,6 +531,9 @@ public final class MatroskaExtractor implements Extractor {
|
|||
case ID_TRACK_ENTRY:
|
||||
currentTrack = new Track();
|
||||
break;
|
||||
case ID_MASTERING_METADATA:
|
||||
currentTrack.hasColorInfo = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -681,6 +744,60 @@ public final class MatroskaExtractor implements Extractor {
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case ID_COLOUR_PRIMARIES:
|
||||
currentTrack.hasColorInfo = true;
|
||||
switch ((int) value) {
|
||||
case 1:
|
||||
currentTrack.colorSpace = C.COLOR_SPACE_BT709;
|
||||
break;
|
||||
case 4: // BT.470M.
|
||||
case 5: // BT.470BG.
|
||||
case 6: // SMPTE 170M.
|
||||
case 7: // SMPTE 240M.
|
||||
currentTrack.colorSpace = C.COLOR_SPACE_BT601;
|
||||
break;
|
||||
case 9:
|
||||
currentTrack.colorSpace = C.COLOR_SPACE_BT2020;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ID_COLOUR_TRANSFER:
|
||||
switch ((int) value) {
|
||||
case 1: // BT.709.
|
||||
case 6: // SMPTE 170M.
|
||||
case 7: // SMPTE 240M.
|
||||
currentTrack.colorTransfer = C.COLOR_TRANSFER_SDR;
|
||||
break;
|
||||
case 16:
|
||||
currentTrack.colorTransfer = C.COLOR_TRANSFER_ST2084;
|
||||
break;
|
||||
case 18:
|
||||
currentTrack.colorTransfer = C.COLOR_TRANSFER_HLG;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ID_COLOUR_RANGE:
|
||||
switch((int) value) {
|
||||
case 1: // Broadcast range.
|
||||
currentTrack.colorRange = C.COLOR_RANGE_LIMITED;
|
||||
break;
|
||||
case 2:
|
||||
currentTrack.colorRange = C.COLOR_RANGE_FULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ID_MAX_CLL:
|
||||
currentTrack.maxContentLuminance = (int) value;
|
||||
break;
|
||||
case ID_MAX_FALL:
|
||||
currentTrack.maxFrameAverageLuminance = (int) value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -694,6 +811,36 @@ public final class MatroskaExtractor implements Extractor {
|
|||
case ID_SAMPLING_FREQUENCY:
|
||||
currentTrack.sampleRate = (int) value;
|
||||
break;
|
||||
case ID_PRIMARY_R_CHROMATICITY_X:
|
||||
currentTrack.primaryRChromaticityX = (float) value;
|
||||
break;
|
||||
case ID_PRIMARY_R_CHROMATICITY_Y:
|
||||
currentTrack.primaryRChromaticityY = (float) value;
|
||||
break;
|
||||
case ID_PRIMARY_G_CHROMATICITY_X:
|
||||
currentTrack.primaryGChromaticityX = (float) value;
|
||||
break;
|
||||
case ID_PRIMARY_G_CHROMATICITY_Y:
|
||||
currentTrack.primaryGChromaticityY = (float) value;
|
||||
break;
|
||||
case ID_PRIMARY_B_CHROMATICITY_X:
|
||||
currentTrack.primaryBChromaticityX = (float) value;
|
||||
break;
|
||||
case ID_PRIMARY_B_CHROMATICITY_Y:
|
||||
currentTrack.primaryBChromaticityY = (float) value;
|
||||
break;
|
||||
case ID_WHITE_POINT_CHROMATICITY_X:
|
||||
currentTrack.whitePointChromaticityX = (float) value;
|
||||
break;
|
||||
case ID_WHITE_POINT_CHROMATICITY_Y:
|
||||
currentTrack.whitePointChromaticityY = (float) value;
|
||||
break;
|
||||
case ID_LUMNINANCE_MAX:
|
||||
currentTrack.maxMasteringLuminance = (float) value;
|
||||
break;
|
||||
case ID_LUMNINANCE_MIN:
|
||||
currentTrack.minMasteringLuminance = (float) value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1305,6 +1452,16 @@ public final class MatroskaExtractor implements Extractor {
|
|||
private static final class Track {
|
||||
|
||||
private static final int DISPLAY_UNIT_PIXELS = 0;
|
||||
private static final int MAX_CHROMATICITY = 50000; // Defined in CTA-861.3.
|
||||
/**
|
||||
* Default max content light level (CLL) that should be encoded into hdrStaticInfo.
|
||||
*/
|
||||
private static final int DEFAULT_MAX_CLL = 1000; // nits.
|
||||
|
||||
/**
|
||||
* Default frame-average light level (FALL) that should be encoded into hdrStaticInfo.
|
||||
*/
|
||||
private static final int DEFAULT_MAX_FALL = 200; // nits.
|
||||
|
||||
// Common elements.
|
||||
public String codecId;
|
||||
|
|
@ -1326,6 +1483,25 @@ public final class MatroskaExtractor implements Extractor {
|
|||
public byte[] projectionData = null;
|
||||
@C.StereoMode
|
||||
public int stereoMode = Format.NO_VALUE;
|
||||
public boolean hasColorInfo = false;
|
||||
@C.ColorSpace
|
||||
public int colorSpace = Format.NO_VALUE;
|
||||
@C.ColorTransfer
|
||||
public int colorTransfer = Format.NO_VALUE;
|
||||
@C.ColorRange
|
||||
public int colorRange = Format.NO_VALUE;
|
||||
public int maxContentLuminance = DEFAULT_MAX_CLL;
|
||||
public int maxFrameAverageLuminance = DEFAULT_MAX_FALL;
|
||||
public float primaryRChromaticityX = Format.NO_VALUE;
|
||||
public float primaryRChromaticityY = Format.NO_VALUE;
|
||||
public float primaryGChromaticityX = Format.NO_VALUE;
|
||||
public float primaryGChromaticityY = Format.NO_VALUE;
|
||||
public float primaryBChromaticityX = Format.NO_VALUE;
|
||||
public float primaryBChromaticityY = Format.NO_VALUE;
|
||||
public float whitePointChromaticityX = Format.NO_VALUE;
|
||||
public float whitePointChromaticityY = Format.NO_VALUE;
|
||||
public float maxMasteringLuminance = Format.NO_VALUE;
|
||||
public float minMasteringLuminance = Format.NO_VALUE;
|
||||
|
||||
// Audio elements. Initially set to their default values.
|
||||
public int channelCount = 1;
|
||||
|
|
@ -1495,9 +1671,15 @@ public final class MatroskaExtractor implements Extractor {
|
|||
if (displayWidth != Format.NO_VALUE && displayHeight != Format.NO_VALUE) {
|
||||
pixelWidthHeightRatio = ((float) (height * displayWidth)) / (width * displayHeight);
|
||||
}
|
||||
ColorInfo colorInfo = null;
|
||||
if (hasColorInfo) {
|
||||
byte[] hdrStaticInfo = getHdrStaticInfo();
|
||||
colorInfo = new ColorInfo(colorSpace, colorRange, colorTransfer, hdrStaticInfo);
|
||||
}
|
||||
format = Format.createVideoSampleFormat(Integer.toString(trackId), mimeType, null,
|
||||
Format.NO_VALUE, maxInputSize, width, height, Format.NO_VALUE, initializationData,
|
||||
Format.NO_VALUE, pixelWidthHeightRatio, projectionData, stereoMode, drmInitData);
|
||||
Format.NO_VALUE, pixelWidthHeightRatio, projectionData, stereoMode, colorInfo,
|
||||
drmInitData);
|
||||
} else if (MimeTypes.APPLICATION_SUBRIP.equals(mimeType)) {
|
||||
type = C.TRACK_TYPE_TEXT;
|
||||
format = Format.createTextSampleFormat(Integer.toString(trackId), mimeType, null,
|
||||
|
|
@ -1516,6 +1698,38 @@ public final class MatroskaExtractor implements Extractor {
|
|||
this.output.format(format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the HDR Static Info as defined in CTA-861.3.
|
||||
*/
|
||||
private byte[] getHdrStaticInfo() {
|
||||
// Are all fields present.
|
||||
if (primaryRChromaticityX == Format.NO_VALUE || primaryRChromaticityY == Format.NO_VALUE
|
||||
|| primaryGChromaticityX == Format.NO_VALUE || primaryGChromaticityY == Format.NO_VALUE
|
||||
|| primaryBChromaticityX == Format.NO_VALUE || primaryBChromaticityY == Format.NO_VALUE
|
||||
|| whitePointChromaticityX == Format.NO_VALUE
|
||||
|| whitePointChromaticityY == Format.NO_VALUE || maxMasteringLuminance == Format.NO_VALUE
|
||||
|| minMasteringLuminance == Format.NO_VALUE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] hdrStaticInfoData = new byte[25];
|
||||
ByteBuffer hdrStaticInfo = ByteBuffer.wrap(hdrStaticInfoData);
|
||||
hdrStaticInfo.put((byte) 0); // Type.
|
||||
hdrStaticInfo.putShort((short) ((primaryRChromaticityX * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((primaryRChromaticityY * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((primaryGChromaticityX * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((primaryGChromaticityY * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((primaryBChromaticityX * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((primaryBChromaticityY * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((whitePointChromaticityX * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) ((whitePointChromaticityY * MAX_CHROMATICITY) + 0.5f));
|
||||
hdrStaticInfo.putShort((short) (maxMasteringLuminance + 0.5f));
|
||||
hdrStaticInfo.putShort((short) (minMasteringLuminance + 0.5f));
|
||||
hdrStaticInfo.putShort((short) maxContentLuminance);
|
||||
hdrStaticInfo.putShort((short) maxFrameAverageLuminance);
|
||||
return hdrStaticInfoData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds initialization data for a {@link Format} from FourCC codec private data.
|
||||
* <p>
|
||||
|
|
|
|||
|
|
@ -762,7 +762,7 @@ import java.util.List;
|
|||
|
||||
out.format = Format.createVideoSampleFormat(Integer.toString(trackId), mimeType, null,
|
||||
Format.NO_VALUE, Format.NO_VALUE, width, height, Format.NO_VALUE, initializationData,
|
||||
rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, drmInitData);
|
||||
rotationDegrees, pixelWidthHeightRatio, projectionData, stereoMode, null, drmInitData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* 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.video;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Stores color info.
|
||||
*/
|
||||
public final class ColorInfo implements Parcelable {
|
||||
|
||||
/**
|
||||
* The color space of the video. Valid values are {@link C#COLOR_SPACE_BT601}, {@link
|
||||
* C#COLOR_SPACE_BT709}, {@link C#COLOR_SPACE_BT2020} or {@link Format#NO_VALUE} if unknown.
|
||||
*/
|
||||
@C.ColorSpace
|
||||
public final int colorSpace;
|
||||
|
||||
/**
|
||||
* The color range of the video. Valid values are {@link C#COLOR_RANGE_LIMITED}, {@link
|
||||
* C#COLOR_RANGE_FULL} or {@link Format#NO_VALUE} if unknown.
|
||||
*/
|
||||
@C.ColorRange
|
||||
public final int colorRange;
|
||||
|
||||
/**
|
||||
* The color transfer characteristicks of the video. Valid values are {@link
|
||||
* C#COLOR_TRANSFER_HLG}, {@link C#COLOR_TRANSFER_ST2084}, {@link C#COLOR_TRANSFER_SDR} or {@link
|
||||
* Format#NO_VALUE} if unknown.
|
||||
*/
|
||||
@C.ColorTransfer
|
||||
public final int colorTransfer;
|
||||
|
||||
/**
|
||||
* HdrStaticInfo as defined in CTA-861.3.
|
||||
*/
|
||||
public final byte[] hdrStaticInfo;
|
||||
|
||||
// Lazily initialized hashcode.
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* Constructs the ColorInfo.
|
||||
*
|
||||
* @param colorSpace The color space of the video.
|
||||
* @param colorRange The color range of the video.
|
||||
* @param colorTransfer The color transfer characteristics of the video.
|
||||
* @param hdrStaticInfo HdrStaticInfo as defined in CTA-861.3.
|
||||
*/
|
||||
public ColorInfo(@C.ColorSpace int colorSpace, @C.ColorRange int colorRange,
|
||||
@C.ColorTransfer int colorTransfer, byte[] hdrStaticInfo) {
|
||||
this.colorSpace = colorSpace;
|
||||
this.colorRange = colorRange;
|
||||
this.colorTransfer = colorTransfer;
|
||||
this.hdrStaticInfo = hdrStaticInfo;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResourceType")
|
||||
/* package */ ColorInfo(Parcel in) {
|
||||
colorSpace = in.readInt();
|
||||
colorRange = in.readInt();
|
||||
colorTransfer = in.readInt();
|
||||
boolean hasHdrStaticInfo = in.readInt() != 0;
|
||||
hdrStaticInfo = hasHdrStaticInfo ? in.createByteArray() : null;
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ColorInfo other = (ColorInfo) obj;
|
||||
if (colorSpace != other.colorSpace || colorRange != other.colorRange
|
||||
|| colorTransfer != other.colorTransfer
|
||||
|| !Arrays.equals(hdrStaticInfo, other.hdrStaticInfo)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ColorInfo(" + colorSpace + ", " + colorRange + ", " + colorTransfer
|
||||
+ ", " + (hdrStaticInfo != null) + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
int result = 17;
|
||||
result = 31 * result + colorSpace;
|
||||
result = 31 * result + colorRange;
|
||||
result = 31 * result + colorTransfer;
|
||||
result = 31 * result + Arrays.hashCode(hdrStaticInfo);
|
||||
hashCode = result;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(colorSpace);
|
||||
dest.writeInt(colorRange);
|
||||
dest.writeInt(colorTransfer);
|
||||
dest.writeInt(hdrStaticInfo != null ? 1 : 0);
|
||||
if (hdrStaticInfo != null) {
|
||||
dest.writeByteArray(hdrStaticInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ColorInfo> CREATOR = new Parcelable.Creator<ColorInfo>() {
|
||||
@Override
|
||||
public ColorInfo createFromParcel(Parcel in) {
|
||||
return new ColorInfo(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorInfo[] newArray(int size) {
|
||||
return new ColorInfo[0];
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -166,7 +166,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
currentPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
pendingPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
scalingMode = C.VIDEO_SCALING_MODE_DEFAULT;
|
||||
clearLastReportedVideoSize();
|
||||
clearReportedVideoSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -229,8 +229,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
super.onPositionReset(positionUs, joining);
|
||||
clearRenderedFirstFrame();
|
||||
consecutiveDroppedFrameCount = 0;
|
||||
joiningDeadlineMs = joining && allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
|
||||
if (joining) {
|
||||
setJoiningDeadlineMs();
|
||||
} else {
|
||||
joiningDeadlineMs = C.TIME_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -272,7 +275,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
currentHeight = Format.NO_VALUE;
|
||||
currentPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
pendingPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
clearLastReportedVideoSize();
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
frameReleaseTimeHelper.disable();
|
||||
tunnelingOnFrameRenderedListener = null;
|
||||
try {
|
||||
|
|
@ -312,11 +316,25 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
maybeInitCodec();
|
||||
}
|
||||
}
|
||||
if (surface != null) {
|
||||
// If we know the video size, report it again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
// We haven't rendered to the new surface yet.
|
||||
clearRenderedFirstFrame();
|
||||
if (state == STATE_STARTED) {
|
||||
setJoiningDeadlineMs();
|
||||
}
|
||||
} else {
|
||||
// The surface has been removed.
|
||||
clearReportedVideoSize();
|
||||
clearRenderedFirstFrame();
|
||||
}
|
||||
} else if (surface != null) {
|
||||
// The surface is unchanged and non-null. If we know the video size and/or have already
|
||||
// rendered to the surface, report these again immediately.
|
||||
maybeRenotifyVideoSizeChanged();
|
||||
maybeRenotifyRenderedFirstFrame();
|
||||
}
|
||||
// Clear state so that we always call the event listener with the video size and when a frame
|
||||
// is rendered, even if the surface hasn't changed.
|
||||
clearRenderedFirstFrame();
|
||||
clearLastReportedVideoSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -521,6 +539,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
maybeNotifyRenderedFirstFrame();
|
||||
}
|
||||
|
||||
private void setJoiningDeadlineMs() {
|
||||
joiningDeadlineMs = allowedJoiningTimeMs > 0
|
||||
? (SystemClock.elapsedRealtime() + allowedJoiningTimeMs) : C.TIME_UNSET;
|
||||
}
|
||||
|
||||
private void clearRenderedFirstFrame() {
|
||||
renderedFirstFrame = false;
|
||||
// The first frame notification is triggered by renderOutputBuffer or renderOutputBufferV21 for
|
||||
|
|
@ -543,7 +566,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private void clearLastReportedVideoSize() {
|
||||
private void maybeRenotifyRenderedFirstFrame() {
|
||||
if (renderedFirstFrame) {
|
||||
eventDispatcher.renderedFirstFrame(surface);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearReportedVideoSize() {
|
||||
reportedWidth = Format.NO_VALUE;
|
||||
reportedHeight = Format.NO_VALUE;
|
||||
reportedPixelWidthHeightRatio = Format.NO_VALUE;
|
||||
|
|
@ -563,6 +592,13 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private void maybeRenotifyVideoSizeChanged() {
|
||||
if (reportedWidth != Format.NO_VALUE || reportedHeight != Format.NO_VALUE) {
|
||||
eventDispatcher.videoSizeChanged(currentWidth, currentHeight, currentUnappliedRotationDegrees,
|
||||
currentPixelWidthHeightRatio);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeNotifyDroppedFrames() {
|
||||
if (droppedFrames > 0) {
|
||||
long now = SystemClock.elapsedRealtime();
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
|
|||
if (MimeTypes.APPLICATION_RAWCC.equals(containerMimeType)) {
|
||||
extractor = new RawCcExtractor(representation.format);
|
||||
} else if (mimeTypeIsWebm(containerMimeType)) {
|
||||
extractor = new MatroskaExtractor();
|
||||
extractor = new MatroskaExtractor(MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES);
|
||||
} else {
|
||||
int flags = 0;
|
||||
if (enableEventMessageTrack) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ public class HlsMediaPlaylistParserTest extends TestCase {
|
|||
try {
|
||||
HlsPlaylist playlist = new HlsPlaylistParser().parse(playlistUri, inputStream);
|
||||
assertNotNull(playlist);
|
||||
assertEquals(HlsPlaylist.TYPE_MEDIA, playlist.type);
|
||||
|
||||
HlsMediaPlaylist mediaPlaylist = (HlsMediaPlaylist) playlist;
|
||||
assertEquals(HlsMediaPlaylist.PLAYLIST_TYPE_VOD, mediaPlaylist.playlistType);
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
|
|||
|
||||
public HlsMasterPlaylist(String baseUri, List<HlsUrl> variants, List<HlsUrl> audios,
|
||||
List<HlsUrl> subtitles, Format muxedAudioFormat, List<Format> muxedCaptionFormats) {
|
||||
super(baseUri, HlsPlaylist.TYPE_MASTER);
|
||||
super(baseUri);
|
||||
this.variants = Collections.unmodifiableList(variants);
|
||||
this.audios = Collections.unmodifiableList(audios);
|
||||
this.subtitles = Collections.unmodifiableList(subtitles);
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||
long startTimeUs, boolean hasDiscontinuitySequence, int discontinuitySequence,
|
||||
int mediaSequence, int version, long targetDurationUs, boolean hasEndTag,
|
||||
boolean hasProgramDateTime, Segment initializationSegment, List<Segment> segments) {
|
||||
super(baseUri, HlsPlaylist.TYPE_MEDIA);
|
||||
super(baseUri);
|
||||
this.playlistType = playlistType;
|
||||
this.startTimeUs = startTimeUs;
|
||||
this.hasDiscontinuitySequence = hasDiscontinuitySequence;
|
||||
|
|
|
|||
|
|
@ -15,30 +15,15 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.hls.playlist;
|
||||
|
||||
import android.support.annotation.IntDef;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Represents an HLS playlist.
|
||||
*/
|
||||
public abstract class HlsPlaylist {
|
||||
|
||||
/**
|
||||
* The type of playlist.
|
||||
*/
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef({TYPE_MASTER, TYPE_MEDIA})
|
||||
public @interface Type {}
|
||||
public static final int TYPE_MASTER = 0;
|
||||
public static final int TYPE_MEDIA = 1;
|
||||
|
||||
public final String baseUri;
|
||||
@Type public final int type;
|
||||
|
||||
protected HlsPlaylist(String baseUri, @Type int type) {
|
||||
protected HlsPlaylist(String baseUri) {
|
||||
this.baseUri = baseUri;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -477,9 +477,15 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
@Override
|
||||
public void onLoadCompleted(ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs,
|
||||
long loadDurationMs) {
|
||||
processLoadedPlaylist((HlsMediaPlaylist) loadable.getResult());
|
||||
eventDispatcher.loadCompleted(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs,
|
||||
loadDurationMs, loadable.bytesLoaded());
|
||||
HlsPlaylist result = loadable.getResult();
|
||||
if (result instanceof HlsMediaPlaylist) {
|
||||
processLoadedPlaylist((HlsMediaPlaylist) result);
|
||||
eventDispatcher.loadCompleted(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs,
|
||||
loadDurationMs, loadable.bytesLoaded());
|
||||
} else {
|
||||
onLoadError(loadable, elapsedRealtimeMs, loadDurationMs,
|
||||
new ParserException("Loaded playlist has unexpected type."));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ import android.content.res.Resources;
|
|||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -74,12 +76,18 @@ import java.util.List;
|
|||
* <li>Default: {@code null}</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>{@code use_controller}</b> - Whether playback controls are displayed.
|
||||
* <li><b>{@code use_controller}</b> - Whether the playback controls can be shown.
|
||||
* <ul>
|
||||
* <li>Corresponding method: {@link #setUseController(boolean)}</li>
|
||||
* <li>Default: {@code true}</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>{@code hide_on_touch}</b> - Whether the playback controls are hidden by touch events.
|
||||
* <ul>
|
||||
* <li>Corresponding method: {@link #setControllerHideOnTouch(boolean)}</li>
|
||||
* <li>Default: {@code true}</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* <li><b>{@code resize_mode}</b> - Controls how video and album art is resized within the view.
|
||||
* Valid values are {@code fit}, {@code fixed_width}, {@code fixed_height} and {@code fill}.
|
||||
* <ul>
|
||||
|
|
@ -190,6 +198,7 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
private boolean useArtwork;
|
||||
private Bitmap defaultArtwork;
|
||||
private int controllerShowTimeoutMs;
|
||||
private boolean controllerHideOnTouch;
|
||||
|
||||
public SimpleExoPlayerView(Context context) {
|
||||
this(context, null);
|
||||
|
|
@ -228,6 +237,7 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
int surfaceType = SURFACE_TYPE_SURFACE_VIEW;
|
||||
int resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
||||
int controllerShowTimeoutMs = PlaybackControlView.DEFAULT_SHOW_TIMEOUT_MS;
|
||||
boolean controllerHideOnTouch = true;
|
||||
if (attrs != null) {
|
||||
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
|
||||
R.styleable.SimpleExoPlayerView, 0, 0);
|
||||
|
|
@ -242,6 +252,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
resizeMode = a.getInt(R.styleable.SimpleExoPlayerView_resize_mode, resizeMode);
|
||||
controllerShowTimeoutMs = a.getInt(R.styleable.SimpleExoPlayerView_show_timeout,
|
||||
controllerShowTimeoutMs);
|
||||
controllerHideOnTouch = a.getBoolean(R.styleable.SimpleExoPlayerView_hide_on_touch,
|
||||
controllerHideOnTouch);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
|
|
@ -304,10 +316,35 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
this.controller = null;
|
||||
}
|
||||
this.controllerShowTimeoutMs = controller != null ? controllerShowTimeoutMs : 0;
|
||||
this.controllerHideOnTouch = controllerHideOnTouch;
|
||||
this.useController = useController && controller != null;
|
||||
hideController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the view targeted by a given {@link SimpleExoPlayer}.
|
||||
*
|
||||
* @param player The player whose target view is being switched.
|
||||
* @param oldPlayerView The old view to detach from the player.
|
||||
* @param newPlayerView The new view to attach to the player.
|
||||
*/
|
||||
public static void switchTargetView(@NonNull SimpleExoPlayer player,
|
||||
@Nullable SimpleExoPlayerView oldPlayerView, @Nullable SimpleExoPlayerView newPlayerView) {
|
||||
if (oldPlayerView == newPlayerView) {
|
||||
return;
|
||||
}
|
||||
// We attach the new view before detaching the old one because this ordering allows the player
|
||||
// to swap directly from one surface to another, without transitioning through a state where no
|
||||
// surface is attached. This is significantly more efficient and achieves a more seamless
|
||||
// transition when using platform provided video decoders.
|
||||
if (newPlayerView != null) {
|
||||
newPlayerView.setPlayer(player);
|
||||
}
|
||||
if (oldPlayerView != null) {
|
||||
oldPlayerView.setPlayer(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player currently set on this view, or null if no player is set.
|
||||
*/
|
||||
|
|
@ -319,6 +356,12 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
* Set the {@link SimpleExoPlayer} to use. The {@link SimpleExoPlayer#setTextOutput} and
|
||||
* {@link SimpleExoPlayer#setVideoListener} method of the player will be called and previous
|
||||
* assignments are overridden.
|
||||
* <p>
|
||||
* To transition a {@link SimpleExoPlayer} from targeting one view to another, it's recommended to
|
||||
* use {@link #switchTargetView(SimpleExoPlayer, SimpleExoPlayerView, SimpleExoPlayerView)} rather
|
||||
* than this method. If you do wish to use this method directly, be sure to attach the player to
|
||||
* the new view <em>before</em> calling {@code setPlayer(null)} to detach it from the old one.
|
||||
* This ordering is significantly more efficient and may allow for more seamless transitions.
|
||||
*
|
||||
* @param player The {@link SimpleExoPlayer} to use.
|
||||
*/
|
||||
|
|
@ -327,10 +370,14 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
return;
|
||||
}
|
||||
if (this.player != null) {
|
||||
this.player.setTextOutput(null);
|
||||
this.player.setVideoListener(null);
|
||||
this.player.removeListener(componentListener);
|
||||
this.player.setVideoSurface(null);
|
||||
this.player.clearTextOutput(componentListener);
|
||||
this.player.clearVideoListener(componentListener);
|
||||
if (surfaceView instanceof TextureView) {
|
||||
this.player.clearVideoTextureView((TextureView) surfaceView);
|
||||
} else if (surfaceView instanceof SurfaceView) {
|
||||
this.player.clearVideoSurfaceView((SurfaceView) surfaceView);
|
||||
}
|
||||
}
|
||||
this.player = player;
|
||||
if (useController) {
|
||||
|
|
@ -346,8 +393,8 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
player.setVideoSurfaceView((SurfaceView) surfaceView);
|
||||
}
|
||||
player.setVideoListener(componentListener);
|
||||
player.addListener(componentListener);
|
||||
player.setTextOutput(componentListener);
|
||||
player.addListener(componentListener);
|
||||
maybeShowController(false);
|
||||
updateForCurrentTrackSelections();
|
||||
} else {
|
||||
|
|
@ -407,17 +454,17 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns whether the playback controls are enabled.
|
||||
* Returns whether the playback controls can be shown.
|
||||
*/
|
||||
public boolean getUseController() {
|
||||
return useController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether playback controls are enabled. If set to {@code false} the playback controls are
|
||||
* never visible and are disconnected from the player.
|
||||
* Sets whether the playback controls can be shown. If set to {@code false} the playback controls
|
||||
* are never visible and are disconnected from the player.
|
||||
*
|
||||
* @param useController Whether playback controls should be enabled.
|
||||
* @param useController Whether the playback controls can be shown.
|
||||
*/
|
||||
public void setUseController(boolean useController) {
|
||||
Assertions.checkState(!useController || controller != null);
|
||||
|
|
@ -486,6 +533,23 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
this.controllerShowTimeoutMs = controllerShowTimeoutMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the playback controls are hidden by touch events.
|
||||
*/
|
||||
public boolean getControllerHideOnTouch() {
|
||||
return controllerHideOnTouch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the playback controls are hidden by touch events.
|
||||
*
|
||||
* @param controllerHideOnTouch Whether the playback controls are hidden by touch events.
|
||||
*/
|
||||
public void setControllerHideOnTouch(boolean controllerHideOnTouch) {
|
||||
Assertions.checkState(controller != null);
|
||||
this.controllerHideOnTouch = controllerHideOnTouch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link PlaybackControlView.VisibilityListener}.
|
||||
*
|
||||
|
|
@ -573,10 +637,10 @@ public final class SimpleExoPlayerView extends FrameLayout {
|
|||
if (!useController || player == null || ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
|
||||
return false;
|
||||
}
|
||||
if (controller.isVisible()) {
|
||||
controller.hide();
|
||||
} else {
|
||||
if (!controller.isVisible()) {
|
||||
maybeShowController(true);
|
||||
} else if (controllerHideOnTouch) {
|
||||
controller.hide();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,11 +39,12 @@
|
|||
<attr name="use_artwork" format="boolean"/>
|
||||
<attr name="default_artwork" format="reference"/>
|
||||
<attr name="use_controller" format="boolean"/>
|
||||
<attr name="hide_on_touch" format="boolean"/>
|
||||
<attr name="resize_mode"/>
|
||||
<attr name="surface_type"/>
|
||||
<attr name="show_timeout"/>
|
||||
<attr name="rewind_increment"/>
|
||||
<attr name="fastforward_increment"/>
|
||||
<attr name="resize_mode"/>
|
||||
<attr name="player_layout_id"/>
|
||||
<attr name="controller_layout_id"/>
|
||||
</declare-styleable>
|
||||
|
|
|
|||
Loading…
Reference in a new issue