Skip to content

Commit

Permalink
Add support for Caption Format Descriptor
Browse files Browse the repository at this point in the history
This allows the TsExtractor to automatically determine the
closed caption tracks to expose by parsing available
descriptors.

Issue:#2161

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=148321380
  • Loading branch information
AquilesCanta authored and ojw28 committed Feb 23, 2017
1 parent 698e081 commit 3fc3349
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

Expand All @@ -35,39 +37,42 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {FLAG_ALLOW_NON_IDR_KEYFRAMES, FLAG_IGNORE_AAC_STREAM,
FLAG_IGNORE_H264_STREAM, FLAG_DETECT_ACCESS_UNITS, FLAG_IGNORE_SPLICE_INFO_STREAM})
FLAG_IGNORE_H264_STREAM, FLAG_DETECT_ACCESS_UNITS, FLAG_IGNORE_SPLICE_INFO_STREAM,
FLAG_OVERRIDE_CAPTION_DESCRIPTORS})
public @interface Flags {
}
public static final int FLAG_ALLOW_NON_IDR_KEYFRAMES = 1;
public static final int FLAG_IGNORE_AAC_STREAM = 1 << 1;
public static final int FLAG_IGNORE_H264_STREAM = 1 << 2;
public static final int FLAG_DETECT_ACCESS_UNITS = 1 << 3;
public static final int FLAG_IGNORE_SPLICE_INFO_STREAM = 1 << 4;
public static final int FLAG_OVERRIDE_CAPTION_DESCRIPTORS = 1 << 5;

private static final int DESCRIPTOR_TAG_CAPTION_SERVICE = 0x86;

@Flags private final int flags;
private final List<Format> closedCaptionFormats;

public DefaultTsPayloadReaderFactory() {
this(0);
}

/**
* @param flags A combination of {@code FLAG_*} values, which control the behavior of the created
* readers.
*/
public DefaultTsPayloadReaderFactory(@Flags int flags) {
this(flags, Collections.singletonList(Format.createTextSampleFormat(null,
MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null)));
this(0, Collections.<Format>emptyList());
}

/**
* @param flags A combination of {@code FLAG_*} values, which control the behavior of the created
* readers.
* @param closedCaptionFormats {@link Format}s to be exposed by elementary stream readers for
* streams with embedded closed captions.
* @param closedCaptionFormats {@link Format}s to be exposed by payload readers for streams with
* embedded closed captions when no caption service descriptors are provided. If
* {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, {@code closedCaptionFormats} overrides
* any descriptor information. If not set, and {@code closedCaptionFormats} is empty, a
* closed caption track with {@link Format#accessibilityChannel} {@link Format#NO_VALUE} will
* be exposed.
*/
public DefaultTsPayloadReaderFactory(@Flags int flags, List<Format> closedCaptionFormats) {
this.flags = flags;
if (!isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS) && closedCaptionFormats.isEmpty()) {
closedCaptionFormats = Collections.singletonList(Format.createTextSampleFormat(null,
MimeTypes.APPLICATION_CEA608, null, Format.NO_VALUE, 0, null, null));
}
this.closedCaptionFormats = closedCaptionFormats;
}

Expand Down Expand Up @@ -95,10 +100,10 @@ public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
return new PesReader(new H262Reader());
case TsExtractor.TS_STREAM_TYPE_H264:
return isSet(FLAG_IGNORE_H264_STREAM) ? null
: new PesReader(new H264Reader(buildSeiReader(), isSet(FLAG_ALLOW_NON_IDR_KEYFRAMES),
isSet(FLAG_DETECT_ACCESS_UNITS)));
: new PesReader(new H264Reader(buildSeiReader(esInfo),
isSet(FLAG_ALLOW_NON_IDR_KEYFRAMES), isSet(FLAG_DETECT_ACCESS_UNITS)));
case TsExtractor.TS_STREAM_TYPE_H265:
return new PesReader(new H265Reader(buildSeiReader()));
return new PesReader(new H265Reader(buildSeiReader(esInfo)));
case TsExtractor.TS_STREAM_TYPE_SPLICE_INFO:
return isSet(FLAG_IGNORE_SPLICE_INFO_STREAM)
? null : new SectionReader(new SpliceInfoSectionReader());
Expand All @@ -109,8 +114,52 @@ public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
}
}

private SeiReader buildSeiReader() {
// TODO: Add descriptor parsing to detect channels automatically.
/**
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link SeiReader} for
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
* {@link SeiReader} 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 SeiReader} for closed caption tracks.
*/
private SeiReader buildSeiReader(EsInfo esInfo) {
if (isSet(FLAG_OVERRIDE_CAPTION_DESCRIPTORS)) {
return new SeiReader(closedCaptionFormats);
}
ParsableByteArray scratchDescriptorData = new ParsableByteArray(esInfo.descriptorBytes);
List<Format> closedCaptionFormats = this.closedCaptionFormats;
while (scratchDescriptorData.bytesLeft() > 0) {
int descriptorTag = scratchDescriptorData.readUnsignedByte();
int descriptorLength = scratchDescriptorData.readUnsignedByte();
int nextDescriptorPosition = scratchDescriptorData.getPosition() + descriptorLength;
if (descriptorTag == DESCRIPTOR_TAG_CAPTION_SERVICE) {
// Note: see ATSC A/65 for detailed information about the caption service descriptor.
closedCaptionFormats = new ArrayList<>();
int numberOfServices = scratchDescriptorData.readUnsignedByte() & 0x1F;
for (int i = 0; i < numberOfServices; i++) {
String language = scratchDescriptorData.readString(3);
int captionTypeByte = scratchDescriptorData.readUnsignedByte();
boolean isDigital = (captionTypeByte & 0x80) != 0;
String mimeType;
int accessibilityChannel;
if (isDigital) {
mimeType = MimeTypes.APPLICATION_CEA708;
accessibilityChannel = captionTypeByte & 0x3F;
} else {
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);
}
} else {
// Unknown descriptor. Ignore.
}
scratchDescriptorData.setPosition(nextDescriptorPosition);
}
return new SeiReader(closedCaptionFormats);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,10 @@ private Extractor createExtractor() {
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultTsPayloadReaderFactory.Flags
int esReaderFactoryFlags = DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM;
if (!muxedCaptionFormats.isEmpty()) {
// The playlist declares closed caption renditions, we should ignore descriptors.
esReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_OVERRIDE_CAPTION_DESCRIPTORS;
}
String codecs = trackFormat.codecs;
if (!TextUtils.isEmpty(codecs)) {
// Sometimes AAC and H264 streams are declared in TS chunks even though they don't really
Expand Down

0 comments on commit 3fc3349

Please sign in to comment.