ExoPlayer RTMP支持H265扩展
RTMP是由Adobe公司提出的一种应用层的协议。RTMP和FLV格式友好的兼容性,主要体现在RTMP封装可播放的音视频流时,RTMP Packet中封装的音视频数据流时,其实和FLV/tag封装音频和视频数据的方式是相同的,FLV封装采用H264和AAC的方式,以下是对ExoPlayer FLV封装代码扩展H265编码。
ExoPlayer/library/core/src/main/java/com/google/android/exoplayer2/extractor/flv/VideoTagPayloadReader.java
package com.google.android.exoplayer2.extractor.flv;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.video.AvcConfig;
import com.google.android.exoplayer2.video.HevcConfig;
import java.util.Collections;
/**
* Parses video tags from an FLV stream and extracts H.264 nal units.
*/
/* package */ final class VideoTagPayloadReader extends TagPayloadReader {
// Video codec.
private static final int VIDEO_CODEC_AVC = 7;
// 添加 h265 hevc.
private static final int VIDEO_CODEC_HEVC = 12;
// Frame types.
private static final int VIDEO_FRAME_KEYFRAME = 1;
private static final int VIDEO_FRAME_VIDEO_INFO = 5;
// Packet types.
private static final int AVC_PACKET_TYPE_SEQUENCE_HEADER = 0;
private static final int AVC_PACKET_TYPE_AVC_NALU = 1;
// Temporary arrays.
private final ParsableByteArray nalStartCode;
private final ParsableByteArray nalLength;
private int nalUnitLengthFieldLength;
// State variables.
private boolean hasOutputFormat;
private int frameType;
//编码类型
private int lastType;
/**
* @param output A {@link TrackOutput} to which samples should be written.
*/
public VideoTagPayloadReader(TrackOutput output) {
super(output);
nalStartCode = new ParsableByteArray(NalUnitUtil.NAL_START_CODE);
nalLength = new ParsableByteArray(4);
}
@Override
public void seek() {
// Do nothing.
}
@Override
protected boolean parseHeader(ParsableByteArray data) throws UnsupportedFormatException {
int header = data.readUnsignedByte();
int frameType = (header >> 4) & 0x0F;
int videoCodec = (header & 0x0F);
// Support just H.264 encoded content.
// 编码类型判断
if(videoCodec == VIDEO_CODEC_AVC || videoCodec == VIDEO_CODEC_HEVC){
lastType = videoCodec;
}
if (videoCodec != VIDEO_CODEC_AVC && (videoCodec != VIDEO_CODEC_HEVC)) {
throw new UnsupportedFormatException("Video format not supported: " + videoCodec);
}
this.frameType = frameType;
return (frameType != VIDEO_FRAME_VIDEO_INFO);
}
@Override
protected void parsePayload(ParsableByteArray data, long timeUs) throws ParserException {
int packetType = data.readUnsignedByte();
int compositionTimeMs = data.readInt24();
timeUs += compositionTimeMs * 1000L;
Format format = null;
// Parse avc sequence header in case this was not done before.
if (packetType == AVC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) {
ParsableByteArray videoSequence = new ParsableByteArray(new byte[data.bytesLeft()]);
data.readBytes(videoSequence.data, 0, data.bytesLeft());
// H264编码格式
if (lastType == VIDEO_CODEC_AVC) {
AvcConfig avcConfig = AvcConfig.parse(videoSequence);
nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
// Construct and output the format.
format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null,
Format.NO_VALUE, Format.NO_VALUE, avcConfig.width, avcConfig.height, Format.NO_VALUE,
avcConfig.initializationData, Format.NO_VALUE, avcConfig.pixelWidthAspectRatio, null);
// H265编码格式
} else if(lastType == VIDEO_CODEC_HEVC) {
HevcConfig hevcConfig = HevcConfig.parse(videoSequence);
nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H265, null, Format.NO_VALUE,
Format.NO_VALUE, 0, 0, Format.NO_VALUE,
hevcConfig.initializationData, Format.NO_VALUE, 0, null);
}
output.format(format);
hasOutputFormat = true;
} else if (packetType == AVC_PACKET_TYPE_AVC_NALU && hasOutputFormat) {
// TODO: Deduplicate with Mp4Extractor.
// Zero the top three bytes of the array that we'll use to decode nal unit lengths, in case
// they're only 1 or 2 bytes long.
byte[] nalLengthData = nalLength.data;
nalLengthData[0] = 0;
nalLengthData[1] = 0;
nalLengthData[2] = 0;
int nalUnitLengthFieldLengthDiff = 4 - nalUnitLengthFieldLength;
// NAL units are length delimited, but the decoder requires start code delimited units.
// Loop until we've written the sample to the track output, replacing length delimiters with
// start codes as we encounter them.
int bytesWritten = 0;
int bytesToWrite;
while (data.bytesLeft() > 0) {
// Read the NAL length so that we know where we find the next one.
data.readBytes(nalLength.data, nalUnitLengthFieldLengthDiff, nalUnitLengthFieldLength);
nalLength.setPosition(0);
bytesToWrite = nalLength.readUnsignedIntToInt();
// Write a start code for the current NAL unit.
nalStartCode.setPosition(0);
output.sampleData(nalStartCode, 4);
bytesWritten += 4;
// Write the payload of the NAL unit.
output.sampleData(data, bytesToWrite);
bytesWritten += bytesToWrite;
}
output.sampleMetadata(timeUs, frameType == VIDEO_FRAME_KEYFRAME ? C.BUFFER_FLAG_KEY_FRAME : 0,
bytesWritten, 0, null);
}
}
}