mirror of
https://github.com/samsonjs/media.git
synced 2026-04-13 12:35:48 +00:00
Merge branch 'dev-v2' of https://github.com/peddisri/ExoPlayer into dev-v2
This commit is contained in:
commit
3c4b0aa11b
8 changed files with 292 additions and 10 deletions
|
|
@ -80,6 +80,12 @@ public final class C {
|
|||
/** The number of bits per byte. */
|
||||
public static final int BITS_PER_BYTE = 8;
|
||||
|
||||
/** non-Wide aspect ratio */
|
||||
public static final int NON_WIDE_ASPECT_RATIO_TYPE = 1;
|
||||
|
||||
/** Wide aspect ratio */
|
||||
public static final int WIDE_ASPECT_RATIO_TYPE = 2;
|
||||
|
||||
/**
|
||||
* The name of the ASCII charset.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -15,8 +15,11 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.extractor.ts;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.IntDef;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
|
@ -79,6 +82,13 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||
*/
|
||||
public DefaultTsPayloadReaderFactory(@Flags int flags, List<Format> closedCaptionFormats) {
|
||||
this.flags = flags;
|
||||
if (!isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS) && closedCaptionFormats.isEmpty()) {
|
||||
closedCaptionFormats = new ArrayList();
|
||||
closedCaptionFormats.add(Format.createTextSampleFormat(null,
|
||||
MimeTypes.APPLICATION_CEA608, 0, null));
|
||||
closedCaptionFormats.add(Format.createTextSampleFormat(null,
|
||||
MimeTypes.APPLICATION_CEA708, 0, null));
|
||||
}
|
||||
this.closedCaptionFormats = closedCaptionFormats;
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +116,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||
case TsExtractor.TS_STREAM_TYPE_HDMV_DTS:
|
||||
return new PesReader(new DtsReader(esInfo.language));
|
||||
case TsExtractor.TS_STREAM_TYPE_H262:
|
||||
return new PesReader(new H262Reader());
|
||||
return new PesReader(new H262Reader(buildUserDataReader(esInfo)));
|
||||
case TsExtractor.TS_STREAM_TYPE_H264:
|
||||
return isSet(FLAG_IGNORE_H264_STREAM) ? null
|
||||
: new PesReader(new H264Reader(buildSeiReader(esInfo),
|
||||
|
|
@ -136,8 +146,34 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||
* @return A {@link SeiReader} for closed caption tracks.
|
||||
*/
|
||||
private SeiReader buildSeiReader(EsInfo esInfo) {
|
||||
return new SeiReader(getCCformats(esInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link UserDataReader} for
|
||||
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
|
||||
* {@link UserDataReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
|
||||
* is not present.
|
||||
*
|
||||
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
|
||||
* @return A {@link UserDataReader} for closed caption tracks.
|
||||
*/
|
||||
private UserDataReader buildUserDataReader(EsInfo esInfo) {
|
||||
return new UserDataReader(getCCformats(esInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link List<Format>} of
|
||||
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
|
||||
* {@link List<Format>} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
|
||||
* is not present.
|
||||
*
|
||||
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
|
||||
* @return A {@link List<Format>} containing list of closed caption formats.
|
||||
*/
|
||||
private List<Format> getCCformats(EsInfo esInfo) {
|
||||
if (isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS)) {
|
||||
return new SeiReader(closedCaptionFormats);
|
||||
return closedCaptionFormats;
|
||||
}
|
||||
ParsableByteArray scratchDescriptorData = new ParsableByteArray(esInfo.descriptorBytes);
|
||||
List<Format> closedCaptionFormats = this.closedCaptionFormats;
|
||||
|
|
@ -162,17 +198,24 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||
mimeType = MimeTypes.APPLICATION_CEA608;
|
||||
accessibilityChannel = 1;
|
||||
}
|
||||
closedCaptionFormats.add(Format.createTextSampleFormat(null, mimeType, null,
|
||||
Format.NO_VALUE, 0, language, accessibilityChannel, null));
|
||||
// Skip easy_reader(1), wide_aspect_ratio(1), reserved(14).
|
||||
scratchDescriptorData.skipBytes(2);
|
||||
byte misc = (byte)scratchDescriptorData.readUnsignedByte();
|
||||
boolean isWideAspectRatio = ((misc & 0x60) == 0x60);
|
||||
Bundle params = new Bundle();
|
||||
params.putInt(Format.KEY_ASPECT_RATIO_TYPE,
|
||||
isWideAspectRatio ? C.WIDE_ASPECT_RATIO_TYPE: C.NON_WIDE_ASPECT_RATIO_TYPE);
|
||||
closedCaptionFormats.add(Format.createTextSampleFormat(null, mimeType, null,
|
||||
Format.NO_VALUE, 0, language, accessibilityChannel, null,
|
||||
params));
|
||||
scratchDescriptorData.skipBytes(1);
|
||||
}
|
||||
} else {
|
||||
// Unknown descriptor. Ignore.
|
||||
}
|
||||
scratchDescriptorData.setPosition(nextDescriptorPosition);
|
||||
}
|
||||
return new SeiReader(closedCaptionFormats);
|
||||
|
||||
return closedCaptionFormats;
|
||||
}
|
||||
|
||||
private boolean isSet(@Flags int flag) {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ public final class H262Reader implements ElementaryStreamReader {
|
|||
private static final int START_SEQUENCE_HEADER = 0xB3;
|
||||
private static final int START_EXTENSION = 0xB5;
|
||||
private static final int START_GROUP = 0xB8;
|
||||
private static final int START_USER_DATA = 0xB2;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
|
|
@ -62,16 +63,30 @@ public final class H262Reader implements ElementaryStreamReader {
|
|||
private long sampleTimeUs;
|
||||
private boolean sampleIsKeyframe;
|
||||
private boolean sampleHasPicture;
|
||||
|
||||
private NalUnitTargetBuffer userData = null;
|
||||
private UserDataReader userDataReader = null;
|
||||
// Scratch variables to avoid allocations.
|
||||
private ParsableByteArray userDataParsable = null;
|
||||
public H262Reader() {
|
||||
this(null);
|
||||
}
|
||||
public H262Reader(UserDataReader userDataReader) {
|
||||
this.userDataReader = userDataReader;
|
||||
prefixFlags = new boolean[4];
|
||||
csdBuffer = new CsdBuffer(128);
|
||||
if (userDataReader != null) {
|
||||
userData = new NalUnitTargetBuffer(START_USER_DATA, 128);
|
||||
userDataParsable = new ParsableByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek() {
|
||||
NalUnitUtil.clearPrefixFlags(prefixFlags);
|
||||
csdBuffer.reset();
|
||||
if (userData != null) {
|
||||
userData.reset();
|
||||
}
|
||||
totalBytesWritten = 0;
|
||||
startedFirstSample = false;
|
||||
}
|
||||
|
|
@ -81,6 +96,9 @@ public final class H262Reader implements ElementaryStreamReader {
|
|||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_VIDEO);
|
||||
if (userDataReader != null) {
|
||||
userDataReader.createTracks(extractorOutput, idGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -106,6 +124,9 @@ public final class H262Reader implements ElementaryStreamReader {
|
|||
if (!hasOutputFormat) {
|
||||
csdBuffer.onData(dataArray, offset, limit);
|
||||
}
|
||||
if (userData != null) {
|
||||
userData.appendToNalUnit(dataArray, offset, limit);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +151,25 @@ public final class H262Reader implements ElementaryStreamReader {
|
|||
hasOutputFormat = true;
|
||||
}
|
||||
}
|
||||
if (userDataReader != null && userData != null) {
|
||||
int lengthToStartCode = startCodeOffset - offset;
|
||||
int bytesAlreadyPassed = 0;
|
||||
if (lengthToStartCode > 0) {
|
||||
userData.appendToNalUnit(dataArray, offset, startCodeOffset);
|
||||
} else {
|
||||
bytesAlreadyPassed = -lengthToStartCode;
|
||||
}
|
||||
|
||||
if (userData.endNalUnit(bytesAlreadyPassed)) {
|
||||
int unescapedLength = NalUnitUtil.unescapeStream(userData.nalData, userData.nalLength);
|
||||
userDataParsable.reset(userData.nalData, unescapedLength);
|
||||
userDataReader.consume(sampleTimeUs, userDataParsable);
|
||||
}
|
||||
|
||||
if (startCodeValue == START_USER_DATA && data.data[startCodeOffset + 2] == 0x1) {
|
||||
userData.startNalUnit(startCodeValue);
|
||||
}
|
||||
}
|
||||
if (startCodeValue == START_PICTURE || startCodeValue == START_SEQUENCE_HEADER) {
|
||||
int bytesWrittenPastStartCode = limit - startCodeOffset;
|
||||
if (startedFirstSample && sampleHasPicture && hasOutputFormat) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.extractor.ts;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Consumes user data structure, outputting contained CEA-608/708 messages to a {@link TrackOutput}.
|
||||
*/
|
||||
/* package */ final class UserDataReader {
|
||||
private final List<Format> closedCaptionFormats;
|
||||
private final TrackOutput[] outputs;
|
||||
private final int USER_DATA_START_CODE = 0x0001B2;
|
||||
private final int USER_DATA_IDENTIFIER_GA94 = 0x47413934;
|
||||
private final int USER_DATA_TYPE_CODE_MPEG_CC = 0x03;
|
||||
public UserDataReader(List<Format> closedCaptionFormats) {
|
||||
this.closedCaptionFormats = closedCaptionFormats;
|
||||
outputs = new TrackOutput[closedCaptionFormats.size()];
|
||||
}
|
||||
|
||||
public void createTracks(ExtractorOutput extractorOutput,
|
||||
TsPayloadReader.TrackIdGenerator idGenerator) {
|
||||
for (int i = 0; i < outputs.length; i++) {
|
||||
idGenerator.generateNewId();
|
||||
TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT);
|
||||
Format channelFormat = closedCaptionFormats.get(i);
|
||||
String channelMimeType = channelFormat.sampleMimeType;
|
||||
Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType)
|
||||
|| MimeTypes.APPLICATION_CEA708.equals(channelMimeType),
|
||||
"Invalid closed caption mime type provided: " + channelMimeType);
|
||||
output.format(Format.createTextSampleFormat(idGenerator.getFormatId(), channelMimeType, null,
|
||||
Format.NO_VALUE, channelFormat.selectionFlags, channelFormat.language,
|
||||
channelFormat.accessibilityChannel, null, channelFormat.params));
|
||||
outputs[i] = output;
|
||||
}
|
||||
}
|
||||
|
||||
public void consume(long pesTimeUs, ParsableByteArray userDataPayload) {
|
||||
if (userDataPayload.bytesLeft() < 9) {
|
||||
return;
|
||||
}
|
||||
//check if payload is used_data_type (0x01B2)
|
||||
int userDataStartCode = userDataPayload.readInt();
|
||||
int userDataIdentifier = userDataPayload.readInt();
|
||||
int userDataTypeCode = userDataPayload.readUnsignedByte();
|
||||
|
||||
if (userDataStartCode == USER_DATA_START_CODE && userDataIdentifier == USER_DATA_IDENTIFIER_GA94
|
||||
&& userDataTypeCode == USER_DATA_TYPE_CODE_MPEG_CC) {
|
||||
if (userDataPayload.bytesLeft() < 2) {
|
||||
return;
|
||||
}
|
||||
// read cc_count and process_cc_data_flag byte.
|
||||
int ccByte = userDataPayload.readUnsignedByte();
|
||||
boolean processCCDataFlag = ((ccByte & 0x40) != 0);
|
||||
int ccCount = (ccByte & 0x1F);
|
||||
// skip reserved em_data byte of MPEG_CC structure
|
||||
userDataPayload.skipBytes(1);
|
||||
int payLoadSize = ccCount * 3;
|
||||
if (processCCDataFlag && payLoadSize != 0) {
|
||||
int ccPos = userDataPayload.getPosition();
|
||||
for (TrackOutput output : outputs) {
|
||||
output.sampleData(userDataPayload, payLoadSize);
|
||||
output.sampleMetadata(pesTimeUs, C.BUFFER_FLAG_KEY_FRAME, payLoadSize, 0, null);
|
||||
userDataPayload.setPosition(ccPos);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.text.cea;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Initialization data for CEA-708 decoders. */
|
||||
public final class Cea708InitializationData {
|
||||
|
||||
/**
|
||||
* Whether the closed caption service is formatted for displays with 16:9 aspect ratio. If false,
|
||||
* the closed caption service is formatted for 4:3 displays.
|
||||
*/
|
||||
public final boolean isWideAspectRatio;
|
||||
|
||||
private Cea708InitializationData(List<byte[]> initializationData) {
|
||||
isWideAspectRatio = initializationData.get(0)[0] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object representation of CEA-708 initialization data
|
||||
*
|
||||
* @param initializationData Binary CEA-708 initialization data.
|
||||
* @return The object representation.
|
||||
*/
|
||||
public static Cea708InitializationData fromData(List<byte[]> initializationData) {
|
||||
return new Cea708InitializationData(initializationData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds binary CEA-708 initialization data.
|
||||
*
|
||||
* @param isWideAspectRatio Whether the closed caption service is formatted for displays with 16:9
|
||||
* aspect ratio.
|
||||
* @return Binary CEA-708 initializaton data.
|
||||
*/
|
||||
public static List<byte[]> buildData(boolean isWideAspectRatio) {
|
||||
return Collections.singletonList(new byte[] {(byte) (isWideAspectRatio ? 1 : 0)});
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ seekMap:
|
|||
isSeekable = false
|
||||
duration = 66733
|
||||
getPosition(0) = [[timeUs=0, position=0]]
|
||||
numberOfTracks = 2
|
||||
numberOfTracks = 3
|
||||
track 256:
|
||||
format:
|
||||
bitrate = -1
|
||||
|
|
@ -76,4 +76,28 @@ track 257:
|
|||
time = 100822
|
||||
flags = 1
|
||||
data = length 1254, hash 73FB07B8
|
||||
track 8448:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = 1/8448
|
||||
containerMimeType = null
|
||||
sampleMimeType = application/cea-608
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
rotationDegrees = 0
|
||||
pixelWidthHeightRatio = 1.0
|
||||
channelCount = -1
|
||||
sampleRate = -1
|
||||
pcmEncoding = -1
|
||||
encoderDelay = 0
|
||||
encoderPadding = 0
|
||||
subsampleOffsetUs = 9223372036854775807
|
||||
selectionFlags = 0
|
||||
language = null
|
||||
drmInitData = -
|
||||
initializationData:
|
||||
total output bytes = 0
|
||||
sample count = 0
|
||||
tracksEnded = true
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ seekMap:
|
|||
isSeekable = false
|
||||
duration = UNSET TIME
|
||||
getPosition(0) = [[timeUs=0, position=0]]
|
||||
numberOfTracks = 2
|
||||
numberOfTracks = 3
|
||||
track 256:
|
||||
format:
|
||||
bitrate = -1
|
||||
|
|
@ -76,4 +76,28 @@ track 257:
|
|||
time = 100822
|
||||
flags = 1
|
||||
data = length 1254, hash 73FB07B8
|
||||
track 8448:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = 1/8448
|
||||
containerMimeType = null
|
||||
sampleMimeType = application/cea-608
|
||||
maxInputSize = -1
|
||||
width = -1
|
||||
height = -1
|
||||
frameRate = -1.0
|
||||
rotationDegrees = 0
|
||||
pixelWidthHeightRatio = 1.0
|
||||
channelCount = -1
|
||||
sampleRate = -1
|
||||
pcmEncoding = -1
|
||||
encoderDelay = 0
|
||||
encoderPadding = 0
|
||||
subsampleOffsetUs = 9223372036854775807
|
||||
selectionFlags = 0
|
||||
language = null
|
||||
drmInitData = -
|
||||
initializationData:
|
||||
total output bytes = 0
|
||||
sample count = 0
|
||||
tracksEnded = true
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ public class DefaultTrackNameProvider implements TrackNameProvider {
|
|||
} else {
|
||||
trackName = buildLabelString(format);
|
||||
}
|
||||
return trackName.length() == 0 ? resources.getString(R.string.exo_track_unknown) : trackName;
|
||||
return trackName.length() == 0 ? resources.getString(R.string.exo_track_unknown) : trackName + " - " + format.id;
|
||||
}
|
||||
|
||||
private String buildResolutionString(Format format) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue