Commit e36ea3b7 by Ian Baker

Merge pull request #36 from ittiam-systems:rtp-h265

PiperOrigin-RevId: 429566102
parents c6805175 ca1c1c26
...@@ -152,6 +152,8 @@ ...@@ -152,6 +152,8 @@
* Remove deprecated call to `onStop(/* reset= */ true)` and provide an * Remove deprecated call to `onStop(/* reset= */ true)` and provide an
opt-out flag for apps that don't want to clear the playlist on stop. opt-out flag for apps that don't want to clear the playlist on stop.
* RTSP: * RTSP:
* Add RTP reader for HEVC
([#36](https://github.com/androidx/media/pull/36))
* Provide a client API to override the `SocketFactory` used for any server * Provide a client API to override the `SocketFactory` used for any server
connection ([#9606](https://github.com/google/ExoPlayer/pull/9606)). connection ([#9606](https://github.com/google/ExoPlayer/pull/9606)).
* Prefers DIGEST authentication method over BASIC if both are present * Prefers DIGEST authentication method over BASIC if both are present
......
...@@ -37,12 +37,14 @@ public final class RtpPayloadFormat { ...@@ -37,12 +37,14 @@ public final class RtpPayloadFormat {
private static final String RTP_MEDIA_AC3 = "AC3"; private static final String RTP_MEDIA_AC3 = "AC3";
private static final String RTP_MEDIA_MPEG4_GENERIC = "MPEG4-GENERIC"; private static final String RTP_MEDIA_MPEG4_GENERIC = "MPEG4-GENERIC";
private static final String RTP_MEDIA_H264 = "H264"; private static final String RTP_MEDIA_H264 = "H264";
private static final String RTP_MEDIA_H265 = "H265";
/** Returns whether the format of a {@link MediaDescription} is supported. */ /** Returns whether the format of a {@link MediaDescription} is supported. */
public static boolean isFormatSupported(MediaDescription mediaDescription) { public static boolean isFormatSupported(MediaDescription mediaDescription) {
switch (Ascii.toUpperCase(mediaDescription.rtpMapAttribute.mediaEncoding)) { switch (Ascii.toUpperCase(mediaDescription.rtpMapAttribute.mediaEncoding)) {
case RTP_MEDIA_AC3: case RTP_MEDIA_AC3:
case RTP_MEDIA_H264: case RTP_MEDIA_H264:
case RTP_MEDIA_H265:
case RTP_MEDIA_MPEG4_GENERIC: case RTP_MEDIA_MPEG4_GENERIC:
return true; return true;
default: default:
...@@ -63,6 +65,8 @@ public final class RtpPayloadFormat { ...@@ -63,6 +65,8 @@ public final class RtpPayloadFormat {
return MimeTypes.AUDIO_AC3; return MimeTypes.AUDIO_AC3;
case RTP_MEDIA_H264: case RTP_MEDIA_H264:
return MimeTypes.VIDEO_H264; return MimeTypes.VIDEO_H264;
case RTP_MEDIA_H265:
return MimeTypes.VIDEO_H265;
case RTP_MEDIA_MPEG4_GENERIC: case RTP_MEDIA_MPEG4_GENERIC:
return MimeTypes.AUDIO_AAC; return MimeTypes.AUDIO_AAC;
default: default:
......
...@@ -42,6 +42,11 @@ import com.google.common.collect.ImmutableMap; ...@@ -42,6 +42,11 @@ import com.google.common.collect.ImmutableMap;
// Format specific parameter names. // Format specific parameter names.
private static final String PARAMETER_PROFILE_LEVEL_ID = "profile-level-id"; private static final String PARAMETER_PROFILE_LEVEL_ID = "profile-level-id";
private static final String PARAMETER_SPROP_PARAMS = "sprop-parameter-sets"; private static final String PARAMETER_SPROP_PARAMS = "sprop-parameter-sets";
private static final String PARAMETER_H265_SPROP_SPS = "sprop-sps";
private static final String PARAMETER_H265_SPROP_PPS = "sprop-pps";
private static final String PARAMETER_H265_SPROP_VPS = "sprop-vps";
private static final String PARAMETER_H265_SPROP_MAX_DON_DIFF = "sprop-max-don-diff";
/** Prefix for the RFC6381 codecs string for AAC formats. */ /** Prefix for the RFC6381 codecs string for AAC formats. */
private static final String AAC_CODECS_PREFIX = "mp4a.40."; private static final String AAC_CODECS_PREFIX = "mp4a.40.";
/** Prefix for the RFC6381 codecs string for AVC formats. */ /** Prefix for the RFC6381 codecs string for AVC formats. */
...@@ -118,6 +123,10 @@ import com.google.common.collect.ImmutableMap; ...@@ -118,6 +123,10 @@ import com.google.common.collect.ImmutableMap;
checkArgument(!fmtpParameters.isEmpty()); checkArgument(!fmtpParameters.isEmpty());
processH264FmtpAttribute(formatBuilder, fmtpParameters); processH264FmtpAttribute(formatBuilder, fmtpParameters);
break; break;
case MimeTypes.VIDEO_H265:
checkArgument(!fmtpParameters.isEmpty());
processH265FmtpAttribute(formatBuilder, fmtpParameters);
break;
case MimeTypes.AUDIO_AC3: case MimeTypes.AUDIO_AC3:
// AC3 does not require a FMTP attribute. Fall through. // AC3 does not require a FMTP attribute. Fall through.
default: default:
...@@ -158,6 +167,26 @@ import com.google.common.collect.ImmutableMap; ...@@ -158,6 +167,26 @@ import com.google.common.collect.ImmutableMap;
AacUtil.buildAacLcAudioSpecificConfig(sampleRate, channelCount))); AacUtil.buildAacLcAudioSpecificConfig(sampleRate, channelCount)));
} }
/** Returns H264/H265 initialization data from the RTP parameter set. */
private static byte[] getInitializationDataFromParameterSet(String parameterSet) {
byte[] decodedParameterNalData = Base64.decode(parameterSet, Base64.DEFAULT);
byte[] decodedParameterNalUnit =
new byte[decodedParameterNalData.length + NAL_START_CODE.length];
System.arraycopy(
NAL_START_CODE,
/* srcPos= */ 0,
decodedParameterNalUnit,
/* destPos= */ 0,
NAL_START_CODE.length);
System.arraycopy(
decodedParameterNalData,
/* srcPos= */ 0,
decodedParameterNalUnit,
/* destPos= */ NAL_START_CODE.length,
decodedParameterNalData.length);
return decodedParameterNalUnit;
}
private static void processH264FmtpAttribute( private static void processH264FmtpAttribute(
Format.Builder formatBuilder, ImmutableMap<String, String> fmtpAttributes) { Format.Builder formatBuilder, ImmutableMap<String, String> fmtpAttributes) {
checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS)); checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS));
...@@ -166,8 +195,8 @@ import com.google.common.collect.ImmutableMap; ...@@ -166,8 +195,8 @@ import com.google.common.collect.ImmutableMap;
checkArgument(parameterSets.length == 2); checkArgument(parameterSets.length == 2);
ImmutableList<byte[]> initializationData = ImmutableList<byte[]> initializationData =
ImmutableList.of( ImmutableList.of(
getH264InitializationDataFromParameterSet(parameterSets[0]), getInitializationDataFromParameterSet(parameterSets[0]),
getH264InitializationDataFromParameterSet(parameterSets[1])); getInitializationDataFromParameterSet(parameterSets[1]));
formatBuilder.setInitializationData(initializationData); formatBuilder.setInitializationData(initializationData);
// Process SPS (Sequence Parameter Set). // Process SPS (Sequence Parameter Set).
...@@ -189,23 +218,44 @@ import com.google.common.collect.ImmutableMap; ...@@ -189,23 +218,44 @@ import com.google.common.collect.ImmutableMap;
} }
} }
private static byte[] getH264InitializationDataFromParameterSet(String parameterSet) { private static void processH265FmtpAttribute(
byte[] decodedParameterNalData = Base64.decode(parameterSet, Base64.DEFAULT); Format.Builder formatBuilder, ImmutableMap<String, String> fmtpAttributes) {
byte[] decodedParameterNalUnit = if (fmtpAttributes.containsKey(PARAMETER_H265_SPROP_MAX_DON_DIFF)) {
new byte[decodedParameterNalData.length + NAL_START_CODE.length]; int maxDonDiff =
System.arraycopy( Integer.parseInt(checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_MAX_DON_DIFF)));
NAL_START_CODE, checkArgument(
/* srcPos= */ 0, maxDonDiff == 0, "non-zero sprop-max-don-diff " + maxDonDiff + " is not supported");
decodedParameterNalUnit, }
/* destPos= */ 0,
NAL_START_CODE.length); checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_VPS));
System.arraycopy( String spropVPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_VPS));
decodedParameterNalData, checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_SPS));
/* srcPos= */ 0, String spropSPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_SPS));
decodedParameterNalUnit, checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_PPS));
/* destPos= */ NAL_START_CODE.length, String spropPPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_PPS));
decodedParameterNalData.length); ImmutableList<byte[]> initializationData =
return decodedParameterNalUnit; ImmutableList.of(
getInitializationDataFromParameterSet(spropVPS),
getInitializationDataFromParameterSet(spropSPS),
getInitializationDataFromParameterSet(spropPPS));
formatBuilder.setInitializationData(initializationData);
// Process the SPS (Sequence Parameter Set).
byte[] spsNalDataWithStartCode = initializationData.get(1);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnit(
spsNalDataWithStartCode, NAL_START_CODE.length, spsNalDataWithStartCode.length);
formatBuilder.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio);
formatBuilder.setHeight(spsData.height).setWidth(spsData.width);
formatBuilder.setCodecs(
CodecSpecificDataUtil.buildHevcCodecString(
spsData.generalProfileSpace,
spsData.generalTierFlag,
spsData.generalProfileIdc,
spsData.generalProfileCompatibilityFlags,
spsData.constraintBytes,
spsData.generalLevelIdc));
} }
/** /**
......
...@@ -36,6 +36,8 @@ import com.google.android.exoplayer2.util.MimeTypes; ...@@ -36,6 +36,8 @@ import com.google.android.exoplayer2.util.MimeTypes;
return new RtpAacReader(payloadFormat); return new RtpAacReader(payloadFormat);
case MimeTypes.VIDEO_H264: case MimeTypes.VIDEO_H264:
return new RtpH264Reader(payloadFormat); return new RtpH264Reader(payloadFormat);
case MimeTypes.VIDEO_H265:
return new RtpH265Reader(payloadFormat);
default: default:
// No supported reader, returning null. // No supported reader, returning null.
} }
......
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