Commit 1e2ed51f by andrewlewis Committed by kim-vde

Add support for H.263 and MPEG-4 Part 2 in TS

The new reader is named H263Reader as it handles H.263 streams, but
MPEG-4 Part 2 streams are also intended to be handled. The reader's
output format MIME type is video/mp4v as the H.263 streams can be
decoded by decoders supporting this MIME type.

The implementation is based on the framework implementation for
extracting MPEG-4 video in MPEG-TS
(https://cs.android.com/android/platform/superproject/+/master:frameworks/av/media/libstagefright/mpeg2ts/ESQueue.cpp;l=1825;drc=86e363c1fac27302ca4ae33e73296f7797672995)
and is similar to the existing H262Reader.

Issue: #1603
Issue: #5107
PiperOrigin-RevId: 320565337
parent a8f1cdcf
......@@ -212,6 +212,9 @@
headers mime type in `DefaultExtractorsFactory`.
* Add support for partially fragmented MP4s
([#7308](https://github.com/google/ExoPlayer/issues/7308)).
* Add support for MPEG-4 Part 2 and H.263 in MPEG-TS
([#1603](https://github.com/google/ExoPlayer/issues/1603),
[#5107](https://github.com/google/ExoPlayer/issues/5107)).
* Testing
* Add `TestExoPlayer`, a utility class with APIs to create
`SimpleExoPlayer` instances with fake components for testing.
......
......@@ -165,6 +165,8 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
return new PesReader(new DtsReader(esInfo.language));
case TsExtractor.TS_STREAM_TYPE_H262:
return new PesReader(new H262Reader(buildUserDataReader(esInfo)));
case TsExtractor.TS_STREAM_TYPE_H263:
return new PesReader(new H263Reader(buildUserDataReader(esInfo)));
case TsExtractor.TS_STREAM_TYPE_H264:
return isSet(FLAG_IGNORE_H264_STREAM) ? null
: new PesReader(new H264Reader(buildSeiReader(esInfo),
......
......@@ -15,6 +15,9 @@
*/
package com.google.android.exoplayer2.extractor.ts;
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
......@@ -22,7 +25,6 @@ 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.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
......@@ -118,7 +120,7 @@ public final class H262Reader implements ElementaryStreamReader {
@Override
public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
checkStateNotNull(output); // Asserts that createTracks has been called.
int offset = data.getPosition();
int limit = data.limit();
byte[] dataArray = data.data;
......@@ -156,7 +158,7 @@ public final class H262Reader implements ElementaryStreamReader {
int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0;
if (csdBuffer.onStartCode(startCodeValue, bytesAlreadyPassed)) {
// The csd data is complete, so we can decode and output the media format.
Pair<Format, Long> result = parseCsdBuffer(csdBuffer, formatId);
Pair<Format, Long> result = parseCsdBuffer(csdBuffer, checkNotNull(formatId));
output.format(result.first);
frameDurationUs = result.second;
hasOutputFormat = true;
......@@ -215,11 +217,11 @@ public final class H262Reader implements ElementaryStreamReader {
* Parses the {@link Format} and frame duration from a csd buffer.
*
* @param csdBuffer The csd buffer.
* @param formatId The id for the generated format. May be null.
* @param formatId The id for the generated format.
* @return A pair consisting of the {@link Format} and the frame duration in microseconds, or 0 if
* the duration could not be determined.
*/
private static Pair<Format, Long> parseCsdBuffer(CsdBuffer csdBuffer, @Nullable String formatId) {
private static Pair<Format, Long> parseCsdBuffer(CsdBuffer csdBuffer, String formatId) {
byte[] csdData = Arrays.copyOf(csdBuffer.data, csdBuffer.length);
int firstByte = csdData[4] & 0xFF;
......
......@@ -90,6 +90,7 @@ public final class TsExtractor implements Extractor {
public static final int TS_STREAM_TYPE_E_AC3 = 0x87;
public static final int TS_STREAM_TYPE_AC4 = 0xAC; // DVB/ATSC AC-4 Descriptor
public static final int TS_STREAM_TYPE_H262 = 0x02;
public static final int TS_STREAM_TYPE_H263 = 0x10; // MPEG-4 Part 2 and H.263
public static final int TS_STREAM_TYPE_H264 = 0x1B;
public static final int TS_STREAM_TYPE_H265 = 0x24;
public static final int TS_STREAM_TYPE_ID3 = 0x15;
......
......@@ -61,6 +61,11 @@ public final class TsExtractorTest {
}
@Test
public void sampleWithH263() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "ts/sample_h263.ts", simulationConfig);
}
@Test
public void sampleWithH264AndMpegAudio() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new, "ts/sample_h264_mpeg_audio.ts", simulationConfig);
......
No preview for this file type
seekMap:
isSeekable = true
duration = 960000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(480000) = [[timeUs=480000, position=21958]]
getPosition(960000) = [[timeUs=960000, position=44104]]
numberOfTracks = 2
track 256:
total output bytes = 39002
sample count = 24
format 0:
id = 1/256
sampleMimeType = video/mp4v-es
width = 640
height = 360
initializationData:
data = length 47, hash 7696BF67
sample 0:
time = 0
flags = 1
data = length 8408, hash 718A7985
sample 1:
time = 40000
flags = 0
data = length 2018, hash 7BC8193F
sample 2:
time = 80000
flags = 0
data = length 480, hash C244FFAF
sample 3:
time = 120000
flags = 0
data = length 256, hash 56D68D82
sample 4:
time = 160000
flags = 0
data = length 222, hash FADF6CA9
sample 5:
time = 200000
flags = 0
data = length 217, hash 161BB856
sample 6:
time = 240000
flags = 0
data = length 212, hash 835B0727
sample 7:
time = 280000
flags = 0
data = length 212, hash E9AF0AB7
sample 8:
time = 320000
flags = 0
data = length 212, hash E9517D06
sample 9:
time = 360000
flags = 0
data = length 212, hash 4FA58096
sample 10:
time = 400000
flags = 0
data = length 212, hash 4F47F2E5
sample 11:
time = 440000
flags = 0
data = length 212, hash B59BF675
sample 12:
time = 480000
flags = 1
data = length 11769, hash 3ED9DF06
sample 13:
time = 520000
flags = 0
data = length 230, hash 2AF3505D
sample 14:
time = 560000
flags = 0
data = length 222, hash F4E7436D
sample 15:
time = 600000
flags = 0
data = length 222, hash F0F812FD
sample 16:
time = 640000
flags = 0
data = length 222, hash 18472E8C
sample 17:
time = 680000
flags = 0
data = length 222, hash 1457FE1C
sample 18:
time = 720000
flags = 0
data = length 222, hash 3BA719AB
sample 19:
time = 760000
flags = 0
data = length 222, hash 37B7E93B
sample 20:
time = 800000
flags = 0
data = length 222, hash 5F0704CA
sample 21:
time = 840000
flags = 0
data = length 222, hash 5B17D45A
sample 22:
time = 880000
flags = 0
data = length 222, hash 8266EFE9
sample 23:
time = 920000
flags = 0
data = length 222, hash 7E77BF79
track 8448:
total output bytes = 0
sample count = 0
format 0:
id = 1/8448
sampleMimeType = application/cea-608
tracksEnded = true
seekMap:
isSeekable = true
duration = 960000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(480000) = [[timeUs=480000, position=21958]]
getPosition(960000) = [[timeUs=960000, position=44104]]
numberOfTracks = 2
track 256:
total output bytes = 27354
sample count = 18
format 0:
id = 1/256
sampleMimeType = video/mp4v-es
width = 640
height = 360
initializationData:
data = length 47, hash 7696BF67
sample 0:
time = 320000
flags = 0
data = length 212, hash 835B0727
sample 1:
time = 360000
flags = 0
data = length 212, hash E9AF0AB7
sample 2:
time = 400000
flags = 0
data = length 212, hash E9517D06
sample 3:
time = 440000
flags = 0
data = length 212, hash 4FA58096
sample 4:
time = 480000
flags = 0
data = length 212, hash 4F47F2E5
sample 5:
time = 520000
flags = 0
data = length 212, hash B59BF675
sample 6:
time = 560000
flags = 1
data = length 11769, hash 3ED9DF06
sample 7:
time = 600000
flags = 0
data = length 230, hash 2AF3505D
sample 8:
time = 640000
flags = 0
data = length 222, hash F4E7436D
sample 9:
time = 680000
flags = 0
data = length 222, hash F0F812FD
sample 10:
time = 720000
flags = 0
data = length 222, hash 18472E8C
sample 11:
time = 760000
flags = 0
data = length 222, hash 1457FE1C
sample 12:
time = 800000
flags = 0
data = length 222, hash 3BA719AB
sample 13:
time = 840000
flags = 0
data = length 222, hash 37B7E93B
sample 14:
time = 880000
flags = 0
data = length 222, hash 5F0704CA
sample 15:
time = 920000
flags = 0
data = length 222, hash 5B17D45A
sample 16:
time = 960000
flags = 0
data = length 222, hash 8266EFE9
sample 17:
time = 1000000
flags = 0
data = length 222, hash 7E77BF79
track 8448:
total output bytes = 0
sample count = 0
format 0:
id = 1/8448
sampleMimeType = application/cea-608
tracksEnded = true
seekMap:
isSeekable = true
duration = 960000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(480000) = [[timeUs=480000, position=21958]]
getPosition(960000) = [[timeUs=960000, position=44104]]
numberOfTracks = 2
track 256:
total output bytes = 13592
sample count = 8
format 0:
id = 1/256
sampleMimeType = video/mp4v-es
width = 640
height = 360
initializationData:
data = length 47, hash 7696BF67
sample 0:
time = 640000
flags = 0
data = length 222, hash 18472E8C
sample 1:
time = 680000
flags = 0
data = length 222, hash 1457FE1C
sample 2:
time = 720000
flags = 0
data = length 222, hash 3BA719AB
sample 3:
time = 760000
flags = 0
data = length 222, hash 37B7E93B
sample 4:
time = 800000
flags = 0
data = length 222, hash 5F0704CA
sample 5:
time = 840000
flags = 0
data = length 222, hash 5B17D45A
sample 6:
time = 880000
flags = 0
data = length 222, hash 8266EFE9
sample 7:
time = 920000
flags = 0
data = length 222, hash 7E77BF79
track 8448:
total output bytes = 0
sample count = 0
format 0:
id = 1/8448
sampleMimeType = application/cea-608
tracksEnded = true
seekMap:
isSeekable = true
duration = 960000
getPosition(0) = [[timeUs=0, position=0]]
getPosition(1) = [[timeUs=1, position=0]]
getPosition(480000) = [[timeUs=480000, position=21958]]
getPosition(960000) = [[timeUs=960000, position=44104]]
numberOfTracks = 2
track 256:
total output bytes = 0
sample count = 0
format 0:
id = 1/256
sampleMimeType = video/mp4v-es
width = 640
height = 360
initializationData:
data = length 47, hash 7696BF67
track 8448:
total output bytes = 0
sample count = 0
format 0:
id = 1/8448
sampleMimeType = application/cea-608
tracksEnded = true
seekMap:
isSeekable = false
duration = UNSET TIME
getPosition(0) = [[timeUs=0, position=0]]
numberOfTracks = 2
track 256:
total output bytes = 39002
sample count = 24
format 0:
id = 1/256
sampleMimeType = video/mp4v-es
width = 640
height = 360
initializationData:
data = length 47, hash 7696BF67
sample 0:
time = 0
flags = 1
data = length 8408, hash 718A7985
sample 1:
time = 40000
flags = 0
data = length 2018, hash 7BC8193F
sample 2:
time = 80000
flags = 0
data = length 480, hash C244FFAF
sample 3:
time = 120000
flags = 0
data = length 256, hash 56D68D82
sample 4:
time = 160000
flags = 0
data = length 222, hash FADF6CA9
sample 5:
time = 200000
flags = 0
data = length 217, hash 161BB856
sample 6:
time = 240000
flags = 0
data = length 212, hash 835B0727
sample 7:
time = 280000
flags = 0
data = length 212, hash E9AF0AB7
sample 8:
time = 320000
flags = 0
data = length 212, hash E9517D06
sample 9:
time = 360000
flags = 0
data = length 212, hash 4FA58096
sample 10:
time = 400000
flags = 0
data = length 212, hash 4F47F2E5
sample 11:
time = 440000
flags = 0
data = length 212, hash B59BF675
sample 12:
time = 480000
flags = 1
data = length 11769, hash 3ED9DF06
sample 13:
time = 520000
flags = 0
data = length 230, hash 2AF3505D
sample 14:
time = 560000
flags = 0
data = length 222, hash F4E7436D
sample 15:
time = 600000
flags = 0
data = length 222, hash F0F812FD
sample 16:
time = 640000
flags = 0
data = length 222, hash 18472E8C
sample 17:
time = 680000
flags = 0
data = length 222, hash 1457FE1C
sample 18:
time = 720000
flags = 0
data = length 222, hash 3BA719AB
sample 19:
time = 760000
flags = 0
data = length 222, hash 37B7E93B
sample 20:
time = 800000
flags = 0
data = length 222, hash 5F0704CA
sample 21:
time = 840000
flags = 0
data = length 222, hash 5B17D45A
sample 22:
time = 880000
flags = 0
data = length 222, hash 8266EFE9
sample 23:
time = 920000
flags = 0
data = length 222, hash 7E77BF79
track 8448:
total output bytes = 0
sample count = 0
format 0:
id = 1/8448
sampleMimeType = application/cea-608
tracksEnded = true
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment